about summary refs log tree commit diff stats
path: root/091socket.cc
diff options
context:
space:
mode:
authorStephen Malina <stephenmalina@gmail.com>2016-09-11 16:26:44 -0400
committerStephen Malina <stephenmalina@gmail.com>2016-09-11 17:16:47 -0400
commit68578a7828ce8300fa10b28b5f57e56723303e93 (patch)
tree6d8683701f44ccabada39644c8054c07b9790249 /091socket.cc
parentfa20bd3143ffb637e438b44dcb10b56d9abf9888 (diff)
downloadmu-68578a7828ce8300fa10b28b5f57e56723303e93.tar.gz
3323 - Add simple network primitives
Includes four Mu functions:
- $socket: Creates the C structure for a socket and tries to bind and
  listen on a user-provided port.
- $accept: Returns a number pointer to a new socket session. Should
    be called with the result of $socket.
- $read-from-socket: Read one character from the socket, passed in
    as a Mu number. Should only be called after calling $socket and
    $accept.
- $close-socket: Takes two parameters, one for the result of $socket
    and one for the result of $accept, closing both sockets
    and releasing bound ports.
Diffstat (limited to '091socket.cc')
-rw-r--r--091socket.cc166
1 files changed, 166 insertions, 0 deletions
diff --git a/091socket.cc b/091socket.cc
new file mode 100644
index 00000000..cb8966d5
--- /dev/null
+++ b/091socket.cc
@@ -0,0 +1,166 @@
+:(before "End Includes")
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+:(before "End Types")
+struct socket_t {
+  int fd;
+  sockaddr_in addr;
+  socket_t() {
+    fd = 0;
+    bzero(&addr, sizeof(addr));
+  }
+};
+
+:(code)
+void server_socket(int portno, socket_t* server) {
+  server->fd = socket(AF_INET, SOCK_STREAM, 0);
+  int dummy;
+  setsockopt(server->fd, SOL_SOCKET, SO_REUSEADDR, &dummy, sizeof(dummy));
+  server->addr.sin_family = AF_INET;
+  server->addr.sin_addr.s_addr = INADDR_ANY;
+  server->addr.sin_port = htons(portno);
+  if (bind(server->fd, (struct sockaddr*)&server->addr, sizeof(server->addr)) < 0) {
+    server->fd = -1;
+    raise << "Failed to bind server socket to port " << portno << ". Something's already using that port." << "\n";
+    return;
+  }
+  listen(server->fd, 5);
+}
+
+:(before "End Primitive Recipe Declarations")
+_SOCKET,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "$socket", _SOCKET);
+:(before "End Primitive Recipe Checks")
+case _SOCKET: {
+  if (SIZE(inst.ingredients) != 1) {
+    raise << maybe(get(Recipe, r).name) << "'$socket' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
+  if (!is_mu_number(inst.ingredients.at(0))) {
+    raise << maybe(get(Recipe, r).name) << "first ingredient of '$socket' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end();
+    break;
+  }
+  if (SIZE(inst.products) != 1) {
+    raise << maybe(get(Recipe, r).name) << "'$socket' requires exactly one product, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
+  if (!is_mu_number(inst.products.at(0))) {
+    raise << maybe(get(Recipe, r).name) << "first product of '$socket' should be a number (file handle), but got '" << to_string(inst.products.at(0)) << "'\n" << end();
+    break;
+  }
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case _SOCKET: {
+  int port = ingredients.at(0).at(0);
+  socket_t server;
+  server_socket(port, &server);
+  if (server.fd < 0) {
+    break;
+  }
+  products.resize(1);
+  products.at(0).push_back(server.fd);
+  break;
+}
+
+:(code)
+void session_socket(int serverfd, socket_t* session) {
+  socklen_t dummy = sizeof(session->addr);
+  session->fd = accept(serverfd, (struct sockaddr*)&session->addr, &dummy);
+}
+:(before "End Primitive Recipe Declarations")
+_ACCEPT,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "$accept", _ACCEPT);
+:(before "End Primitive Recipe Checks")
+case _ACCEPT: {
+  if (SIZE(inst.ingredients) != 1) {
+    raise << maybe(get(Recipe, r).name) << "'$accept' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
+  if (!is_mu_number(inst.ingredients.at(0))) {
+    raise << maybe(get(Recipe, r).name) << "first ingredient of '$accept' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end();
+    break;
+  }
+  if (SIZE(inst.products) != 1) {
+    raise << maybe(get(Recipe, r).name) << "'$accept' requires exactly one product, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
+  if (!is_mu_number(inst.products.at(0))) {
+    raise << maybe(get(Recipe, r).name) << "first product of '$accept' should be a number (file handle), but got '" << to_string(inst.products.at(0)) << "'\n" << end();
+    break;
+  }
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case _ACCEPT: {
+  double socket_fd = ingredients.at(0).at(0);
+  socket_t session;
+  session_socket(socket_fd, &session);
+  products.resize(1);
+  products.at(0).push_back(session.fd);
+  break;
+}
+
+:(before "End Primitive Recipe Declarations")
+_READ_FROM_SOCKET,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "$read-from-socket", _READ_FROM_SOCKET);
+:(before "End Primitive Recipe Checks")
+case _READ_FROM_SOCKET: {
+  if (SIZE(inst.ingredients) != 1) {
+    raise << maybe(get(Recipe, r).name) << "'$read-from-socket' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
+  if (!is_mu_number(inst.ingredients.at(0))) {
+    raise << maybe(get(Recipe, r).name) << "first ingredient of '$read-from-socket' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end();
+    break;
+  }
+  if (SIZE(inst.products) != 1) {
+    raise << maybe(get(Recipe, r).name) << "'$read-from-socket' requires exactly one product, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
+  if (!is_mu_character(inst.products.at(0))) {
+    raise << maybe(get(Recipe, r).name) << "first product of '$read-from-socket' should be a character, but got '" << to_string(inst.products.at(0)) << "'\n" << end();
+    break;
+  }
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case _READ_FROM_SOCKET: {
+  double socket_fd = ingredients.at(0).at(0);
+  char single_char[2];
+  bzero(single_char, 2);
+  read(socket_fd, single_char, 1);
+  products.resize(1);
+  products.at(0).push_back(single_char[0]);
+  break;
+}
+
+:(before "End Primitive Recipe Declarations")
+_CLOSE_SOCKET,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "$close-socket", _CLOSE_SOCKET);
+:(before "End Primitive Recipe Checks")
+case _CLOSE_SOCKET: {
+  if (SIZE(inst.ingredients) != 2) {
+    raise << maybe(get(Recipe, r).name) << "'$close-socket' requires exactly two ingredient, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
+  if (!is_mu_number(inst.ingredients.at(0)) || !is_mu_number(inst.ingredients.at(0))) {
+    raise << maybe(get(Recipe, r).name) << "first ingredient of '$close-socket' should be a character, but got '" << to_string(inst.ingredients.at(0)) << "t\n" << end();
+    break;
+  }
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case _CLOSE_SOCKET: {
+  double socket_fd = ingredients.at(0).at(0);
+  double session_fd = ingredients.at(1).at(0);
+  close(socket_fd);
+  close(session_fd);
+  break;
+}