diff options
-rw-r--r-- | index.html | 2 | ||||
-rw-r--r-- | makefile | 3 | ||||
-rw-r--r-- | websocksy.c | 142 | ||||
-rw-r--r-- | websocksy.h | 15 | ||||
-rw-r--r-- | ws_proto.c | 213 |
5 files changed, 236 insertions, 139 deletions
@@ -2,7 +2,7 @@ <head> <script type="text/javascript"> function init(){ - var webSocket = new WebSocket("ws://localhost:8001/"); + var webSocket = new WebSocket("ws://localhost:8001/foo/bar"); } </script> </head> @@ -2,7 +2,8 @@ CFLAGS=-g -Wall -Wpedantic all: websocksy -websocksy: websocksy.c websocksy.h +websocksy: websocksy.c websocksy.h ws_proto.c + $(CC) $(CFLAGS) $< -o $@ clean: rm websocksy diff --git a/websocksy.c b/websocksy.c index cc5b9e0..f193ae6 100644 --- a/websocksy.c +++ b/websocksy.c @@ -10,6 +10,7 @@ #include <fcntl.h> #include "websocksy.h" +#include "ws_proto.c" /* TODO * - TLS @@ -17,9 +18,6 @@ * - Prevent http overrun */ -static size_t socks = 0; -static websocket* sock = NULL; - static volatile sig_atomic_t shutdown_requested = 0; void signal_handler(int signum){ @@ -121,122 +119,14 @@ int network_socket(char* host, char* port, int socktype, int listener){ return fd; } -int ws_close(websocket* ws){ - size_t p; - - if(ws->fd >= 0){ - close(ws->fd); - ws->fd = -1; - } - - for(p = 0; p < ws->protocols; p++){ - if(ws->protocol[p].fd >= 0){ - close(ws->protocol[p].fd); - ws->protocol[p].fd = -1; - free(ws->protocol[p].name); - ws->protocol[p].name = NULL; - } - } - - ws->read_buffer_offset = 0; - - return 0; -} - -int ws_accept(int listen_fd){ - size_t n = 0; - - websocket ws = { - .fd = accept(listen_fd, NULL, NULL) - }; - - //try to find a slot to occupy - for(n = 0; n < socks; n++){ - if(sock[n].fd == -1){ - break; - } - } - - //none found, need to extend - if(n == socks){ - sock = realloc(sock, (socks + 1) * sizeof(websocket)); - if(!sock){ - close(ws.fd); - fprintf(stderr, "Failed to allocate memory\n"); - shutdown_requested = 1; - return 1; - } - socks++; - } - - sock[n] = ws; - - return 0; -} - -int ws_data(websocket* ws){ - ssize_t bytes_read, u, bytes_left = sizeof(ws->read_buffer) - ws->read_buffer_offset; - - bytes_read = recv(ws->fd, ws->read_buffer + ws->read_buffer_offset, bytes_left - 1, 0); - if(bytes_read < 0){ - fprintf(stderr, "Failed to receive from websocket: %s\n", strerror(errno)); - ws_close(ws); - return 0; - } - else if(bytes_read == 0){ - //client closed connection - ws_close(ws); - return 0; - } - - //terminate new data - ws->read_buffer[ws->read_buffer_offset + bytes_read] = 0; - - switch(ws->state){ - case ws_new: - case ws_http: - //scan for newline, handle line - for(u = 0; u < bytes_read - 1; u++){ - if(!strncmp((char*) ws->read_buffer + ws->read_buffer_offset + u, "\r\n", 2)){ - //terminate line - ws->read_buffer[ws->read_buffer_offset + u] = 0; - - fprintf(stderr, "Line: %s\n", ws->read_buffer); - - //remove from buffer - bytes_read -= (u + 2); - memmove(ws->read_buffer, ws->read_buffer + ws->read_buffer_offset + u + 2, bytes_read); - ws->read_buffer_offset = 0; - - //restart from the beginning - u = -1; - } - } - break; - //case ws_rfc6455: - //TODO parse websocket encap, forward to peer - } - - //update read buffer offset - ws->read_buffer_offset = bytes_read; - - //disconnect spammy clients - if(sizeof(ws->read_buffer) - ws->read_buffer_offset < 2){ - fprintf(stderr, "Disconnecting misbehaving client\n"); - ws_close(ws); - return 0; - } - return 0; -} - -int ws_peer_data(websocket* ws, size_t proto){ +int ws_peer_data(websocket* ws){ //TODO return -1; } int main(int argc, char** argv){ fd_set read_fds; - size_t n, p; + size_t n; int listen_fd = -1, status, max_fd; if(args_parse(argc - 1, argv + 1)){ @@ -263,13 +153,11 @@ int main(int argc, char** argv){ if(max_fd < sock[n].fd){ max_fd = sock[n].fd; } - - for(p = 0; p < sock[n].protocols; p++){ - if(sock[n].protocol[p].fd >= 0){ - FD_SET(sock[n].protocol[p].fd, &read_fds); - if(max_fd < sock[n].protocol[p].fd){ - max_fd = sock[n].protocol[p].fd; - } + + if(sock[n].peer >= 0){ + FD_SET(sock[n].peer, &read_fds); + if(max_fd < sock[n].peer){ + max_fd = sock[n].peer; } } } @@ -301,11 +189,9 @@ int main(int argc, char** argv){ } } - for(p = 0; p < sock[n].protocols; p++){ - if(sock[n].protocol[p].fd >= 0 && FD_ISSET(sock[n].protocol[p].fd, &read_fds)){ - if(ws_peer_data(sock + n, p)){ - break; - } + if(sock[n].peer >= 0 && FD_ISSET(sock[n].peer, &read_fds)){ + if(ws_peer_data(sock + n)){ + break; } } } @@ -314,11 +200,7 @@ int main(int argc, char** argv){ } //cleanup - for(n = 0; n < socks; n++){ - ws_close(sock + n); - } - free(sock); - socks = 0; + ws_cleanup(); close(listen_fd); return 0; } diff --git a/websocksy.h b/websocksy.h index 0fc9538..794b4ed 100644 --- a/websocksy.h +++ b/websocksy.h @@ -21,17 +21,18 @@ struct { .backend.name = "internal" }; -typedef struct { - char* name; - int fd; -} ws_proto; - typedef struct /*_web_socket*/ { + int fd; + int peer; uint8_t read_buffer[WS_MAX_LINE]; size_t read_buffer_offset; ws_state state; - int fd; + char* request_path; + unsigned websocket_version; + char* socket_key; + unsigned want_upgrade; + size_t protocols; - ws_proto* protocol; + char** protocol; } websocket; diff --git a/ws_proto.c b/ws_proto.c new file mode 100644 index 0000000..b9f92e9 --- /dev/null +++ b/ws_proto.c @@ -0,0 +1,213 @@ +#include <ctype.h> + +static size_t socks = 0; +static websocket* sock = NULL; + +int ws_close(websocket* ws){ + size_t p; + + if(ws->fd >= 0){ + close(ws->fd); + ws->fd = -1; + } + + if(ws->peer >= 0){ + close(ws->peer); + ws->peer = -1; + } + + for(p = 0; p < ws->protocols; p++){ + free(ws->protocol[p]); + } + ws->protocols = 0; + ws->protocol = NULL; + + ws->read_buffer_offset = 0; + + free(ws->request_path); + ws->request_path = NULL; + + free(ws->socket_key); + ws->socket_key = NULL; + + ws->websocket_version = 0; + ws->want_upgrade = 0; + + return 0; +} + +int ws_accept(int listen_fd){ + size_t n = 0; + + websocket ws = { + .fd = accept(listen_fd, NULL, NULL), + .peer = -1 + }; + + //try to find a slot to occupy + for(n = 0; n < socks; n++){ + if(sock[n].fd == -1){ + break; + } + } + + //none found, need to extend + if(n == socks){ + sock = realloc(sock, (socks + 1) * sizeof(websocket)); + if(!sock){ + close(ws.fd); + fprintf(stderr, "Failed to allocate memory\n"); + return 1; + } + socks++; + } + + sock[n] = ws; + + return 0; +} + +int ws_handle_new(websocket* ws){ + size_t u; + char* path, *proto; + + if(!strncmp((char*) ws->read_buffer, "GET ", 4)){ + path = (char*) ws->read_buffer + 4; + for(u = 0; path[u] && !isspace(path[u]); u++){ + } + path[u] = 0; + proto = path + u + 1; + } + //TODO handle other methods + else{ + fprintf(stderr, "Unknown HTTP method in request\n"); + return 1; + } + + if(strncmp(proto, "HTTP/", 5)){ + fprintf(stderr, "Malformed HTTP initiation\n"); + return 1; + } + + ws->state = ws_http; + ws->request_path = strdup(path); + return 0; +} + +int ws_upgrade_http(websocket* ws){ + if(ws->websocket_version == 13 + && ws->want_upgrade){ + + return 0; + } + return 1; +} + +int ws_handle_http(websocket* ws){ + char* header, *value; + if(!ws->read_buffer[0]){ + return ws_upgrade_http(ws); + } + else if(isspace(ws->read_buffer[0])){ + //i hate header folding + //TODO disconnect client + return 1; + } + else{ + header = (char*) ws->read_buffer; + value = strchr(header, ':'); + if(!value){ + //TODO disconnect + return 1; + } + *value = 0; + value++; + for(; isspace(*value); value++){ + } + } + + if(!strcmp(header, "Sec-WebSocket-Version")){ + ws->websocket_version = strtoul(value, NULL, 10); + } + else if(!strcmp(header, "Sec-WebSocket-Key")){ + ws->socket_key = strdup(value); + } + else if(!strcmp(header, "Upgrade") && !strcmp(value, "websocket")){ + ws->want_upgrade = 1; + } + //TODO parse websocket protocol offers + return 0; +} + +int ws_data(websocket* ws){ + ssize_t bytes_read, u, bytes_left = sizeof(ws->read_buffer) - ws->read_buffer_offset; + int rv = 0; + + bytes_read = recv(ws->fd, ws->read_buffer + ws->read_buffer_offset, bytes_left - 1, 0); + if(bytes_read < 0){ + fprintf(stderr, "Failed to receive from websocket: %s\n", strerror(errno)); + ws_close(ws); + return 0; + } + else if(bytes_read == 0){ + //client closed connection + ws_close(ws); + return 0; + } + + //terminate new data + ws->read_buffer[ws->read_buffer_offset + bytes_read] = 0; + + switch(ws->state){ + case ws_new: + case ws_http: + //scan for newline, handle line + for(u = 0; u < bytes_read - 1; u++){ + if(!strncmp((char*) ws->read_buffer + ws->read_buffer_offset + u, "\r\n", 2)){ + //terminate line + ws->read_buffer[ws->read_buffer_offset + u] = 0; + + if(ws->state == ws_new){ + rv |= ws_handle_new(ws); + } + else{ + rv |= ws_handle_http(ws); + } + //TODO handle rv + + //remove from buffer + bytes_read -= (u + 2); + memmove(ws->read_buffer, ws->read_buffer + ws->read_buffer_offset + u + 2, bytes_read); + ws->read_buffer_offset = 0; + + //restart from the beginning + u = -1; + } + } + break; + //case ws_rfc6455: + //TODO parse websocket encap, forward to peer + } + + //update read buffer offset + ws->read_buffer_offset = bytes_read; + + //disconnect spammy clients + if(sizeof(ws->read_buffer) - ws->read_buffer_offset < 2){ + fprintf(stderr, "Disconnecting misbehaving client\n"); + ws_close(ws); + return 0; + } + return rv; +} + +void ws_cleanup(){ + size_t n; + for(n = 0; n < socks; n++){ + ws_close(sock + n); + } + + free(sock); + sock = NULL; + socks = 0; +} |