From d5b52a1302ef4409424ec0633ccf9bd72359ea32 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sat, 14 May 2016 22:03:43 -0400 Subject: refactor/rewrite the client/server protocol handling also pass the environment and current directory over as well --- Makefile | 8 +- src/daemon.c | 153 ++++--------------------- src/daemon.h | 1 - src/protocol.c | 351 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/protocol.h | 23 ++++ src/runesc.c | 75 ++++-------- src/socket.c | 117 +++++++++++++++++++ src/socket.h | 10 ++ src/util.c | 14 +++ src/util.h | 1 + 10 files changed, 566 insertions(+), 187 deletions(-) create mode 100644 src/protocol.c create mode 100644 src/protocol.h create mode 100644 src/socket.c create mode 100644 src/socket.h diff --git a/Makefile b/Makefile index 001c0f7..b0e4eaa 100644 --- a/Makefile +++ b/Makefile @@ -21,9 +21,13 @@ DOBJ = $(BUILD)runesd.o \ $(BUILD)pty-unix.o \ $(BUILD)loop.o \ $(BUILD)util.o \ - $(BUILD)daemon.o + $(BUILD)daemon.o \ + $(BUILD)socket.o \ + $(BUILD)protocol.o COBJ = $(BUILD)runesc.o \ - $(BUILD)util.o + $(BUILD)util.o \ + $(BUILD)socket.o \ + $(BUILD)protocol.o LIBS = cairo cairo-xlib libevent pangocairo OPT ?= -g CFLAGS ?= $(OPT) -Wall -Wextra -Werror diff --git a/src/daemon.c b/src/daemon.c index 8c1269e..44c3c11 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -11,10 +11,10 @@ #include "daemon.h" #include "loop.h" +#include "protocol.h" +#include "socket.h" #include "term.h" -static int runes_daemon_open_socket(char *sock_name); -static void runes_daemon_close_socket(RunesDaemon *sock); static int runes_daemon_handle_request(void *t); RunesDaemon *runes_daemon_new(RunesLoop *loop, RunesWindowBackend *wb) @@ -25,7 +25,7 @@ RunesDaemon *runes_daemon_new(RunesLoop *loop, RunesWindowBackend *wb) daemon->loop = loop; daemon->wb = wb; daemon->sock_name = runes_get_daemon_socket_name(); - daemon->sock = runes_daemon_open_socket(daemon->sock_name); + daemon->sock = runes_socket_server_open(daemon->sock_name); runes_daemon_init_loop(daemon, loop); return daemon; @@ -39,151 +39,42 @@ void runes_daemon_init_loop(RunesDaemon *daemon, RunesLoop *loop) void runes_socket_delete(RunesDaemon *daemon) { - runes_daemon_close_socket(daemon); + runes_socket_server_close(daemon->sock, daemon->sock_name); free(daemon->sock_name); free(daemon); } -static int runes_daemon_open_socket(char *sock_name) -{ - 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", sock_name); - } - - dir = strdup(sock_name); - slash = strrchr(dir, '/'); - if (slash == NULL) { - runes_die("socket path %s must be an absolute path", sock_name); - } - *slash = '\0'; - runes_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", 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", sock_name, - strerror(errno)); - } - - if (chmod(sock_name, S_IRUSR|S_IWUSR)) { - runes_die("couldn't chmod socket %s: %s", sock_name, - strerror(errno)); - } - - if (listen(s, 5)) { - runes_die("couldn't listen on socket %s: %s", sock_name, - strerror(errno)); - } - - return s; -} - -static void runes_daemon_close_socket(RunesDaemon *daemon) -{ - close(daemon->sock); - unlink(daemon->sock_name); -} - static int runes_daemon_handle_request(void *t) { RunesDaemon *daemon = (RunesDaemon *)t; - struct sockaddr_un client; - socklen_t len = sizeof(client); - ssize_t bytes; - uint32_t argc, argv_len; - char **argv, *argv_buf; - - daemon->client_sock = accept( - daemon->sock, (struct sockaddr*)(&client), &len); - if (daemon->client_sock < 0) { - runes_die("couldn't accept connection: %s", strerror(errno)); - } + int client_sock; + char *buf; + size_t len; + struct runes_protocol_message msg; + RunesTerm *new_term; - bytes = recv(daemon->client_sock, (void*)(&argc), sizeof(argc), 0); - if (bytes < (int)sizeof(argc)) { - runes_warn("invalid message received: got %d bytes, expected 4", - bytes); - close(daemon->client_sock); - return 1; - } + client_sock = runes_socket_server_accept(daemon->sock); - argc = ntohl(argc); - if (argc > 1024) { - runes_warn("invalid message received: argc = %d, must be < 1024", - argc); - close(daemon->client_sock); - return 1; - } - argv = malloc(argc * sizeof(char*)); - - bytes = recv(daemon->client_sock, (void*)(&argv_len), sizeof(argv_len), 0); - if (bytes < (int)sizeof(argc)) { - runes_warn("invalid message received: got %d bytes, expected 4", - bytes); - close(daemon->client_sock); - free(argv); + if (!runes_protocol_read_packet(client_sock, &buf, &len)) { + runes_warn("invalid packet received"); return 1; } - argv_len = ntohl(argv_len); - if (argv_len > 131072) { - runes_warn("invalid message received: argv_len = %d, must be < %d", - argv_len, 131072); - close(daemon->client_sock); - free(argv); - return 1; - } - argv_buf = malloc(argv_len + 1); - - bytes = recv(daemon->client_sock, argv_buf, argv_len, 0); - if (bytes < argv_len) { - runes_warn("invalid message received: got %d bytes, expected %d", - bytes, argv_len); - close(daemon->client_sock); - free(argv); - free(argv_buf); + runes_socket_client_close(client_sock); + + if (!runes_protocol_parse_message(buf, len, &msg)) { + runes_warn("couldn't parse message from client"); + free(buf); return 1; } - close(daemon->client_sock); - - if (argc > 0) { - size_t offset = 0; - int i; - - 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", 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", argc); - } - offset = next_null - argv_buf + 1; - } - - runes_term_register_with_loop( - runes_term_new(argc, argv, NULL, NULL, daemon->wb), daemon->loop); - } + new_term = runes_term_new( + msg.argc, msg.argv, msg.envp, msg.cwd, daemon->wb); + runes_term_register_with_loop(new_term, daemon->loop); - free(argv); - free(argv_buf); + free(buf); + runes_protocol_free_message(&msg); return 1; } diff --git a/src/daemon.h b/src/daemon.h index 733e940..daf010e 100644 --- a/src/daemon.h +++ b/src/daemon.h @@ -8,7 +8,6 @@ struct runes_daemon { RunesWindowBackend *wb; char *sock_name; int sock; - int client_sock; }; RunesDaemon *runes_daemon_new(RunesLoop *loop, RunesWindowBackend *wb); diff --git a/src/protocol.c b/src/protocol.c new file mode 100644 index 0000000..6451f63 --- /dev/null +++ b/src/protocol.c @@ -0,0 +1,351 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "runes.h" +#include "protocol.h" + +#define RUNES_MAX_PACKET_SIZE (1024*1024) + +static int runes_protocol_store_u32(GArray *buf, uint32_t i); +static int runes_protocol_store_strvec(GArray *buf, char *vec[]); +static int runes_protocol_store_str(GArray *buf, char *str); +static ssize_t runes_protocol_load_u32(char *buf, size_t len, uint32_t *i); +static ssize_t runes_protocol_load_strvec(char *buf, size_t len, char ***vec); +static ssize_t runes_protocol_load_str(char *buf, size_t len, char **str); +static int runes_protocol_read_bytes(int sock, size_t len, char *outbuf); +static int runes_protocol_write_bytes(int sock, char *buf, size_t len); + +int runes_protocol_parse_message( + char *buf, size_t len, struct runes_protocol_message *outmsg) +{ + uint32_t version; + size_t offset = 0; + +#define LOAD(type, var) \ + do { \ + ssize_t incr; \ + incr = runes_protocol_load_##type(buf + offset, len - offset, &var); \ + if (incr < 0) { \ + free(outmsg); \ + return 0; \ + } \ + offset += incr; \ + } while (0) + + LOAD(u32, version); + if (version != RUNES_PROTOCOL_MESSAGE_VERSION) { + /* backcompat code can go here eventually */ + runes_warn("unknown protocol version %d", version); + free(outmsg); + return 0; + } + + LOAD(u32, outmsg->argc); + LOAD(strvec, outmsg->argv); + LOAD(strvec, outmsg->envp); + LOAD(str, outmsg->cwd); + +#undef LOAD + + return 1; +} + +int runes_protocol_create_message( + struct runes_protocol_message *msg, char **outbuf, size_t *outlen) +{ + GArray *buf; + + buf = g_array_new(FALSE, FALSE, 1); + +#define STORE(type, val) \ + do { \ + if (!runes_protocol_store_##type(buf, val)) { \ + g_array_unref(buf); \ + return 0; \ + } \ + } while (0) + + STORE(u32, RUNES_PROTOCOL_MESSAGE_VERSION); + + STORE(u32, msg->argc); + STORE(strvec, msg->argv); + STORE(strvec, msg->envp); + STORE(str, msg->cwd); + +#undef STORE + + *outbuf = malloc(buf->len); + memcpy(*outbuf, buf->data, buf->len); + *outlen = buf->len; + + g_array_unref(buf); + + return 1; +} + +void runes_protocol_free_message(struct runes_protocol_message *msg) +{ + char **p; + + p = msg->argv; + while (*p) { + free(*p); + p++; + } + free(msg->argv); + + p = msg->envp; + while (*p) { + free(*p); + p++; + } + free(msg->envp); + + free(msg->cwd); +} + +int runes_protocol_read_packet(int sock, char **outbuf, size_t *outlen) +{ + uint32_t len; + char *buf; + + if (!runes_protocol_read_bytes(sock, sizeof(len), (char *)&len)) { + return 0; + } + + len = ntohl(len); + if (len > RUNES_MAX_PACKET_SIZE) { + runes_warn("packet of size %d is too big", len); + return 0; + } + buf = malloc(len); + + if (!runes_protocol_read_bytes(sock, len, buf)) { + free(buf); + return 0; + } + + *outlen = len; + *outbuf = buf; + + return 1; +} + +int runes_protocol_send_packet(int sock, char *buf, size_t len) +{ + uint32_t len32 = len; + + if (len > RUNES_MAX_PACKET_SIZE) { + runes_warn("packet of size %d is too big", len); + return 0; + } + + len32 = htonl(len32); + + if (!runes_protocol_write_bytes(sock, (char *)&len32, sizeof(len32))) { + runes_warn("failed to write packet to socket"); + return 0; + } + + if (!runes_protocol_write_bytes(sock, buf, len)) { + runes_warn("failed to write packet to socket"); + return 0; + } + + return 1; +} + +static int runes_protocol_store_u32(GArray *buf, uint32_t i) +{ + uint32_t ni = htonl(i); + + g_array_append_vals(buf, (void *)&ni, sizeof(ni)); + + return 1; +} + +static int runes_protocol_store_strvec(GArray *buf, char *vec[]) +{ + size_t len, i; + + for (len = 0; vec[len]; ++len); + if (len > RUNES_MAX_PACKET_SIZE) { + runes_warn("string vector of size %d is too big", len); + return 0; + } + +#define STORE(type, val) \ + do { \ + if (!runes_protocol_store_##type(buf, val)) { \ + return 0; \ + } \ + } while (0) + + STORE(u32, len); + for (i = 0; i < len; ++i) { + STORE(str, vec[i]); + } + +#undef STORE + + return 1; +} + +static int runes_protocol_store_str(GArray *buf, char *str) +{ + size_t len = strlen(str); + + if (len > RUNES_MAX_PACKET_SIZE) { + runes_warn("string of size %d is too big", len); + return 0; + } + +#define STORE(type, val) \ + do { \ + if (!runes_protocol_store_##type(buf, val)) { \ + return 0; \ + } \ + } while (0) + + STORE(u32, len); + + g_array_append_vals(buf, str, len); + +#undef STORE + + return 1; +} + +static ssize_t runes_protocol_load_u32(char *buf, size_t len, uint32_t *i) +{ + uint32_t hi; + + if (len < sizeof(uint32_t)) { + return -1; + } + + hi = ((uint32_t *)buf)[0]; + *i = ntohl(hi); + + return sizeof(uint32_t); +} + +static ssize_t runes_protocol_load_strvec(char *buf, size_t len, char ***vec) +{ + ssize_t offset = 0; + uint32_t veclen = 0; + size_t i; + + *vec = NULL; + +#define LOAD(type, var) \ + do { \ + ssize_t incr; \ + incr = runes_protocol_load_##type(buf + offset, len - offset, &var); \ + if (incr < 0) { \ + free(*vec); \ + for (i = 0; i < veclen; ++i) { \ + free(*vec[i]); \ + } \ + return -1; \ + } \ + offset += incr; \ + } while (0) + + LOAD(u32, veclen); + if (veclen > len) { + runes_warn("string of size %d is too big", strlen); + return -1; + } + + *vec = calloc(veclen + 1, sizeof(char *)); + + for (i = 0; i < veclen; ++i) { + LOAD(str, (*vec)[i]); + } + (*vec)[veclen] = NULL; + +#undef LOAD + + return offset; +} + +static ssize_t runes_protocol_load_str(char *buf, size_t len, char **str) +{ + ssize_t offset = 0; + uint32_t strlen; + + *str = NULL; + +#define LOAD(type, var) \ + do { \ + ssize_t incr; \ + incr = runes_protocol_load_##type(buf + offset, len - offset, &var); \ + if (incr < 0) { \ + free(*str); \ + return -1; \ + } \ + offset += incr; \ + } while (0) + + LOAD(u32, strlen); + if (strlen > len) { + runes_warn("string of size %d is too big", strlen); + return -1; + } + + *str = malloc(strlen + 1); + + memcpy(*str, buf + offset, strlen); + (*str)[strlen] = '\0'; + offset += strlen; + +#undef LOAD + + return offset; +} + +static int runes_protocol_write_bytes(int sock, char *buf, size_t len) +{ + size_t bytes = 0; + + while (bytes < len) { + ssize_t sent; + + sent = send(sock, buf + bytes, len - bytes, 0); + if (sent <= 0 && errno != EINTR) { + runes_warn( + "couldn't write %d bytes to socket: %s", + len, strerror(errno)); + return 0; + } + bytes += sent; + } + + return 1; +} + +static int runes_protocol_read_bytes(int sock, size_t len, char *outbuf) +{ + size_t bytes = 0; + + while (bytes < len) { + ssize_t got; + + got = recv(sock, outbuf + bytes, len - bytes, 0); + if (got <= 0 && errno != EINTR) { + runes_warn( + "couldn't read %d bytes from socket: %s", + len, strerror(errno)); + return 0; + } + bytes += got; + } + + return 1; +} diff --git a/src/protocol.h b/src/protocol.h new file mode 100644 index 0000000..fb3af96 --- /dev/null +++ b/src/protocol.h @@ -0,0 +1,23 @@ +#ifndef _RUNES_PROTOCOL_H +#define _RUNES_PROTOCOL_H + +#include + +#define RUNES_PROTOCOL_MESSAGE_VERSION 1 + +struct runes_protocol_message { + uint32_t argc; + char **argv; + char **envp; + char *cwd; +}; + +int runes_protocol_parse_message( + char *buf, size_t len, struct runes_protocol_message *outmsg); +int runes_protocol_create_message( + struct runes_protocol_message *msg, char **outbuf, size_t *outlen); +void runes_protocol_free_message(struct runes_protocol_message *msg); +int runes_protocol_read_packet(int sock, char **outbuf, size_t *outlen); +int runes_protocol_send_packet(int s, char *buf, size_t len); + +#endif diff --git a/src/runesc.c b/src/runesc.c index 5046b98..88adec3 100644 --- a/src/runesc.c +++ b/src/runesc.c @@ -1,71 +1,40 @@ -#include -#include #include #include -#include -#include -#include -#include #include "runes.h" -#define MAX_SOCKET_PATH_LEN \ - (sizeof(struct sockaddr_un) - offsetof(struct sockaddr_un, sun_path)) +#include "protocol.h" +#include "socket.h" -static int runes_socket_open_client(char *name); +extern char **environ; int main (int argc, char *argv[]) { char *name, *buf; - int s, i; - size_t len, offset; - uint32_t argc32 = argc, argv_len; + int s; + size_t len; + struct runes_protocol_message msg; - name = runes_get_daemon_socket_name(); - s = runes_socket_open_client(name); + msg.argc = argc; + msg.argv = argv; + msg.envp = environ; + msg.cwd = runes_getcwd(); - len = sizeof(argc32) + sizeof(argv_len); - for (i = 0; i < argc; ++i) { - len += strlen(argv[i]) + 1; + name = runes_get_daemon_socket_name(); + s = runes_socket_client_open(name); + if (!runes_protocol_create_message(&msg, &buf, &len)) { + runes_warn("couldn't create message"); } - - buf = malloc(len); - ((uint32_t*)buf)[0] = htonl(argc32); - offset = sizeof(argc32) + sizeof(argv_len); - ((uint32_t*)buf)[1] = htonl(len - offset); - - for (i = 0; i < argc; ++i) { - size_t arg_len = strlen(argv[i]) + 1; - memcpy(buf + offset, argv[i], arg_len); - offset += arg_len; + else { + if (!runes_protocol_send_packet(s, buf, len)) { + runes_warn("couldn't send packet"); + free(buf); + } } - send(s, buf, offset, 0); free(buf); - shutdown(s, SHUT_RDWR); - return 0; -} + free(msg.cwd); + runes_socket_client_close(s); -static int runes_socket_open_client(char *name) -{ - int s; - struct sockaddr_un client; - - if (strlen(name) + 1 > MAX_SOCKET_PATH_LEN) { - runes_die("socket path %s is too long", name); - } - - s = socket(AF_UNIX, SOCK_STREAM, 0); - if (s < 0) { - runes_die("couldn't create socket: %s", strerror(errno)); - } - - client.sun_family = AF_UNIX; - strcpy(client.sun_path, name); - if (connect(s, (struct sockaddr*)(&client), sizeof(struct sockaddr_un))) { - runes_die( - "couldn't connect to socket at %s: %s", name, strerror(errno)); - } - - return s; + return 0; } diff --git a/src/socket.c b/src/socket.c new file mode 100644 index 0000000..3d0a3ae --- /dev/null +++ b/src/socket.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "runes.h" +#include "socket.h" + +#define MAX_SOCKET_PATH_LEN \ + (sizeof(struct sockaddr_un) - offsetof(struct sockaddr_un, sun_path)) + +static void runes_socket_populate_sockaddr( + char *path, struct sockaddr_un *addr); + +int runes_socket_client_open(char *path) +{ + int s; + struct sockaddr_un client; + + runes_socket_populate_sockaddr(path, &client); + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) { + runes_die("couldn't create socket: %s", strerror(errno)); + } + + if (connect(s, (struct sockaddr*)(&client), sizeof(struct sockaddr_un))) { + runes_die( + "couldn't connect to socket at %s: %s", path, strerror(errno)); + } + + return s; +} + +int runes_socket_server_open(char *path) +{ + char *dir, *slash; + int s; + struct sockaddr_un server; + + runes_socket_populate_sockaddr(path, &server); + + dir = strdup(path); + slash = strrchr(dir, '/'); + if (slash == NULL) { + runes_die("socket path %s must be an absolute path", path); + } + *slash = '\0'; + runes_mkdir_p(dir); + free(dir); + + unlink(path); + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) { + runes_die("couldn't create socket: %s", strerror(errno)); + } + + if (bind(s, (struct sockaddr*)(&server), sizeof(struct sockaddr_un))) { + runes_die( + "couldn't bind to socket %s: %s", path, strerror(errno)); + } + + if (chmod(path, S_IRUSR|S_IWUSR)) { + runes_die( + "couldn't chmod socket %s: %s", path, strerror(errno)); + } + + if (listen(s, 5)) { + runes_die( + "couldn't listen on socket %s: %s", path, strerror(errno)); + } + + return s; +} + +int runes_socket_server_accept(int ss) +{ + struct sockaddr_un client; + socklen_t len = sizeof(client); + int cs; + + cs = accept(ss, (struct sockaddr*)(&client), &len); + if (cs < 0) { + runes_die("couldn't accept connection: %s", strerror(errno)); + } + + return cs; +} + +void runes_socket_client_close(int s) +{ + close(s); +} + +void runes_socket_server_close(int s, char *path) +{ + close(s); + unlink(path); +} + +static void runes_socket_populate_sockaddr( + char *path, struct sockaddr_un *addr) +{ + size_t name_len = strlen(path) + 1; // including the nul byte + + if (name_len > MAX_SOCKET_PATH_LEN) { + runes_die("socket path %s is too long", path); + } + + addr->sun_family = AF_UNIX; + strncpy(addr->sun_path, path, name_len); +} diff --git a/src/socket.h b/src/socket.h new file mode 100644 index 0000000..85387c4 --- /dev/null +++ b/src/socket.h @@ -0,0 +1,10 @@ +#ifndef _RUNES_SOCKET_H +#define _RUNES_SOCKET_H + +int runes_socket_client_open(char *path); +int runes_socket_server_open(char *path); +int runes_socket_server_accept(int s); +void runes_socket_client_close(int s); +void runes_socket_server_close(int s, char *path); + +#endif diff --git a/src/util.c b/src/util.c index 8737f11..c663710 100644 --- a/src/util.c +++ b/src/util.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "runes.h" @@ -103,6 +104,19 @@ void runes_mkdir_p(char *dir) free(dir); } +char *runes_getcwd(void) +{ + char buf[4096]; + + if (getcwd(buf, 4096)) { + return strdup(buf); + } + else { + runes_die("getcwd: %s", strerror(errno)); + return NULL; + } +} + static void runes_vwarn(const char *fmt, va_list ap) { size_t fmt_len; diff --git a/src/util.h b/src/util.h index e4b9dcb..931cc0d 100644 --- a/src/util.h +++ b/src/util.h @@ -8,5 +8,6 @@ void runes_die(const char *fmt, ...); char *runes_get_daemon_socket_name(void); int runes_sprintf_dup(char **out, const char *fmt, ...); void runes_mkdir_p(char *dir); +char *runes_getcwd(void); #endif -- cgit v1.2.3-54-g00ecf