diff options
Diffstat (limited to 'src/socket.c')
-rw-r--r-- | src/socket.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/src/socket.c b/src/socket.c new file mode 100644 index 0000000..24f0260 --- /dev/null +++ b/src/socket.c @@ -0,0 +1,183 @@ +#include <arpa/inet.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> +#include <unistd.h> + +#include "runes.h" +#include "socket.h" + +static int runes_socket_open(RunesSocket *sock); +static void runes_socket_close(RunesSocket *sock); +static void runes_socket_accept(RunesTerm *t); +static int runes_socket_handle_request(RunesTerm *t); + +void runes_socket_init(RunesSocket *sock, RunesLoop *loop) +{ + sock->loop = loop; + sock->name = runes_get_socket_name(); + sock->sock = runes_socket_open(sock); + runes_socket_init_loop(sock, loop); +} + +void runes_socket_init_loop(RunesSocket *sock, RunesLoop *loop) +{ + runes_loop_start_work(loop, (RunesTerm *)sock, runes_socket_accept, + runes_socket_handle_request); +} + +void runes_socket_cleanup(RunesSocket *sock) +{ + runes_socket_close(sock); + free(sock->name); +} + +static int runes_socket_open(RunesSocket *sock) +{ + char *dir, *slash; + int s; + struct sockaddr_un server; + + if (strlen(sock->name) + 1 > MAX_SOCKET_PATH_LEN) { + runes_die("socket path %s is too long\n", sock->name); + } + + dir = strdup(sock->name); + slash = strrchr(dir, '/'); + if (slash == NULL) { + runes_die("socket path %s must be an absolute path\n", sock->name); + } + *slash = '\0'; + mkdir_p(dir); + free(dir); + + unlink(sock->name); + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) { + runes_die("couldn't create socket: %s\n", strerror(errno)); + } + + server.sun_family = AF_UNIX; + strcpy(server.sun_path, sock->name); + if (bind(s, (struct sockaddr*)(&server), sizeof(struct sockaddr_un))) { + runes_die("couldn't bind to socket %s: %s\n", sock->name, + strerror(errno)); + } + + if (chmod(sock->name, S_IRUSR|S_IWUSR)) { + runes_die("couldn't chmod socket %s: %s\n", sock->name, + strerror(errno)); + } + + if (listen(s, 5)) { + runes_die("couldn't listen on socket %s: %s\n", sock->name, + strerror(errno)); + } + + return s; +} + +static void runes_socket_close(RunesSocket *sock) +{ + close(sock->sock); + unlink(sock->name); +} + +static void runes_socket_accept(RunesTerm *t) +{ + RunesSocket *sock = (RunesSocket *)t; + struct sockaddr_un client; + socklen_t len = sizeof(client); + + sock->client_sock = accept(sock->sock, (struct sockaddr*)(&client), &len); +} + +static int runes_socket_handle_request(RunesTerm *t) +{ + RunesSocket *sock = (RunesSocket *)t; + ssize_t bytes; + uint32_t argc, argv_len; + char **argv, *argv_buf; + + if (sock->client_sock < 0) { + runes_die("couldn't accept connection: %s", strerror(errno)); + } + + bytes = recv(sock->client_sock, (void*)(&argc), sizeof(argc), 0); + if (bytes < (int)sizeof(argc)) { + runes_warn("invalid message received: got %d bytes, expected 4\n", + bytes); + close(sock->client_sock); + return 1; + } + + argc = ntohl(argc); + if (argc > 1024) { + runes_warn("invalid message received: argc = %d, must be < 1024\n", + argc); + close(sock->client_sock); + return 1; + } + argv = malloc(argc * sizeof(char*)); + + bytes = recv(sock->client_sock, (void*)(&argv_len), sizeof(argv_len), 0); + if (bytes < (int)sizeof(argc)) { + runes_warn("invalid message received: got %d bytes, expected 4\n", + bytes); + close(sock->client_sock); + free(argv); + return 1; + } + + argv_len = ntohl(argv_len); + if (argv_len > 131072) { + runes_warn("invalid message received: argv_len = %d, must be < %d\n", + argv_len, 131072); + close(sock->client_sock); + free(argv); + return 1; + } + argv_buf = malloc(argv_len + 1); + + bytes = recv(sock->client_sock, argv_buf, argv_len, 0); + if (bytes < argv_len) { + runes_warn("invalid message received: got %d bytes, expected %d\n", + bytes, argv_len); + close(sock->client_sock); + free(argv); + free(argv_buf); + return 1; + } + + close(sock->client_sock); + + if (argc > 0) { + size_t offset = 0; + int i; + RunesTerm *t; + + for (i = 0; i < (int)argc; ++i) { + char *next_null; + + if (offset >= argv_len) { + runes_die("args in argv_buf don't match argc of %d\n", argc); + } + argv[i] = argv_buf + offset; + next_null = memchr(argv_buf + offset, '\0', argv_len - offset); + if (!next_null) { + runes_die("args in argv_buf don't match argc of %d\n", argc); + } + offset = next_null - argv_buf + 1; + } + + t = calloc(1, sizeof(RunesTerm)); + runes_term_init(t, sock->loop, argc, argv); + } + + free(argv); + free(argv_buf); + + return 1; +} |