diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | builtins.c | 5 | ||||
-rw-r--r-- | builtins.h | 11 | ||||
-rw-r--r-- | makefile | 9 | ||||
-rw-r--r-- | network.c | 27 | ||||
-rw-r--r-- | network.h | 7 | ||||
-rw-r--r-- | websocket.c (renamed from ws_proto.c) | 64 | ||||
-rw-r--r-- | websocket.h | 6 | ||||
-rw-r--r-- | websocksy.c | 76 | ||||
-rw-r--r-- | websocksy.h | 8 |
10 files changed, 147 insertions, 67 deletions
@@ -1,2 +1,3 @@ *.swp websocksy +*.o @@ -1,3 +1,8 @@ +#include <string.h> + +#include "websocksy.h" +#include "builtins.h" + #define UTF8_BYTE(a) ((a & 0xC0) == 0x80) /* diff --git a/builtins.h b/builtins.h new file mode 100644 index 0000000..926d5bb --- /dev/null +++ b/builtins.h @@ -0,0 +1,11 @@ +/* The builtin `defaultpeer` backend */ +uint64_t backend_defaultpeer_init(); +uint64_t backend_defaultpeer_configure(char* key, char* value); +ws_peer_info backend_defaultpeer_query(char* endpoint, size_t protocols, char** protocol, size_t headers, ws_http_header* header, websocket* ws); +void backend_defaultpeer_cleanup(); + +/* Built-in framing functions */ +int64_t framing_auto(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config); +int64_t framing_binary(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config); +int64_t framing_separator(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config); +int64_t framing_newline(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config); @@ -1,10 +1,13 @@ CFLAGS=-g -Wall -Wpedantic LDLIBS=-lnettle +OBJECTS=builtins.o network.o websocket.o + all: websocksy -websocksy: websocksy.c websocksy.h ws_proto.c builtins.c - $(CC) $(CFLAGS) $(LDLIBS) $< -o $@ +websocksy: websocksy.c websocksy.h $(OBJECTS) + $(CC) $(CFLAGS) $(LDLIBS) $< -o $@ $(OBJECTS) clean: - rm websocksy + $(RM) $(OBJECTS) + $(RM) websocksy @@ -1,3 +1,19 @@ +#include "network.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> + +/* + * Create a file descriptor connected to a socket peer. + * Client sockets will be connected, listening sockets will be bound. + * Returns -1 in case of failure, a valid fd otherwise. + */ int network_socket(char* host, char* port, int socktype, int listener){ int fd = -1, status, yes = 1, flags; struct addrinfo hints = { @@ -81,7 +97,12 @@ int network_socket(char* host, char* port, int socktype, int listener){ return fd; } +/* + * Send arbitrary data over multiple writes if necessary. + * Returns 0 on success + */ int network_send(int fd, uint8_t* data, size_t length){ + //TODO probably should introduce send buffering at some point ssize_t total = 0, sent; while(total < length){ sent = send(fd, data + total, length - total, 0); @@ -94,6 +115,10 @@ int network_send(int fd, uint8_t* data, size_t length){ return 0; } +/* + * Send string data over multiple writes if necessary. + * Returns 0 on success + */ int network_send_str(int fd, char* data){ return network_send(fd, (uint8_t*) data, strlen(data)); -}
\ No newline at end of file +} diff --git a/network.h b/network.h new file mode 100644 index 0000000..50c8e18 --- /dev/null +++ b/network.h @@ -0,0 +1,7 @@ +#include <stdint.h> +#include <stdlib.h> + +/* Socket interface convenience functions */ +int network_socket(char* host, char* port, int socktype, int listener); +int network_send(int fd, uint8_t* data, size_t length); +int network_send_str(int fd, char* data); @@ -1,3 +1,14 @@ +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <ctype.h> +#include <errno.h> + +#include "websocket.h" +#include "network.h" + #define RFC6455_MAGIC_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" #define WS_FRAME_HEADER_LEN 16 @@ -8,9 +19,6 @@ #define WS_GET_MASK(a) ((a & 0x80) >> 7) #define WS_GET_LEN(a) ((a & 0x7F)) -static size_t socks = 0; -static websocket* sock = NULL; - int ws_close(websocket* ws, ws_close_reason code, char* reason){ size_t p; ws_peer_info empty_peer = { @@ -72,37 +80,15 @@ int ws_close(websocket* ws, ws_close_reason code, char* reason){ } int ws_accept(int listen_fd){ - size_t n = 0; - websocket ws = { .ws_fd = accept(listen_fd, NULL, NULL), .peer_fd = -1 }; - //try to find a slot to occupy - for(n = 0; n < socks; n++){ - if(sock[n].ws_fd == -1){ - break; - } - } - - //none found, need to extend - if(n == socks){ - sock = realloc(sock, (socks + 1) * sizeof(websocket)); - if(!sock){ - close(ws.ws_fd); - fprintf(stderr, "Failed to allocate memory\n"); - return 1; - } - socks++; - } - - sock[n] = ws; - - return 0; + return client_register(&ws); } -int ws_handle_new(websocket* ws){ +static int ws_handle_new(websocket* ws){ size_t u; char* path, *proto; @@ -129,13 +115,13 @@ int ws_handle_new(websocket* ws){ return 0; } -int ws_upgrade_http(websocket* ws){ +static int ws_upgrade_http(websocket* ws){ if(ws->websocket_version == 13 && ws->socket_key && ws->want_upgrade == 3){ //find and connect peer - if(connect_peer(ws)){ + if(client_connect(ws)){ ws_close(ws, ws_close_http, "500 Peer connection failed"); return 0; } @@ -196,7 +182,7 @@ int ws_upgrade_http(websocket* ws){ return 1; } -int ws_handle_http(websocket* ws){ +static int ws_handle_http(websocket* ws){ char* header, *value; ssize_t p; @@ -275,13 +261,13 @@ int ws_handle_http(websocket* ws){ } else{ //limit the number of stored headers to prevent abuse - ws_close(ws, ws_close_http, "500 Header limit"); + //ws_close(ws, ws_close_http, "500 Header limit"); } return 0; } //returns bytes handled -size_t ws_frame(websocket* ws){ +static size_t ws_frame(websocket* ws){ size_t u; uint64_t payload_length = 0; uint16_t* payload_len16 = (uint16_t*) (ws->read_buffer + 2); @@ -389,6 +375,9 @@ size_t ws_frame(websocket* ws){ case ws_frame_ping: break; case ws_frame_pong: + if(ws_send_frame(ws, ws_frame_pong, payload, payload_length)){ + ws_close(ws, ws_close_unexpected, "Failed to send ping"); + } break; default: //unknown frame type received @@ -500,14 +489,3 @@ int ws_data(websocket* ws){ } return rv; } - -void ws_cleanup(){ - size_t n; - for(n = 0; n < socks; n++){ - ws_close(sock + n, ws_close_shutdown, "Shutting down"); - } - - free(sock); - sock = NULL; - socks = 0; -} diff --git a/websocket.h b/websocket.h new file mode 100644 index 0000000..f2a4500 --- /dev/null +++ b/websocket.h @@ -0,0 +1,6 @@ +#include "websocksy.h" + +int ws_close(websocket* ws, ws_close_reason code, char* reason); +int ws_accept(int listen_fd); +int ws_send_frame(websocket* ws, ws_operation opcode, uint8_t* data, size_t len); +int ws_data(websocket* ws); diff --git a/websocksy.c b/websocksy.c index 6ab0f81..aa0cf77 100644 --- a/websocksy.c +++ b/websocksy.c @@ -10,6 +10,11 @@ #include <fcntl.h> #include <ctype.h> +#include "websocksy.h" +#include "builtins.h" +#include "network.h" +#include "websocket.h" + /* TODO * - TLS * - config parsing @@ -19,16 +24,14 @@ * - framing function discovery / registry */ -/* - * Main loop condition, to be set from signal handler - */ +/* Main loop condition, to be set from signal handler */ static volatile sig_atomic_t shutdown_requested = 0; -#include "websocksy.h" +/* Core client registry */ +static size_t socks = 0; +static websocket* sock = NULL; -/* - * Lowercase input string in-place - */ +/* Lowercase input string in-place */ char* xstr_lower(char* in){ size_t n; for(n = 0; n < strlen(in); n++){ @@ -37,12 +40,7 @@ char* xstr_lower(char* in){ return in; } -#include "network.c" -#include "builtins.c" - -/* - * WebSocket interface & peer discovery configuration - */ +/* Daemon configuration */ static struct { char* host; char* port; @@ -57,7 +55,46 @@ static struct { .backend.cleanup = backend_defaultpeer_cleanup }; -int connect_peer(websocket* ws){ +/* Push a new client to the registry */ +int client_register(websocket* ws){ + size_t n = 0; + + //try to find a slot to occupy + for(n = 0; n < socks; n++){ + if(sock[n].ws_fd == -1){ + break; + } + } + + //none found, need to extend + if(n == socks){ + sock = realloc(sock, (socks + 1) * sizeof(websocket)); + if(!sock){ + close(ws->ws_fd); + fprintf(stderr, "Failed to allocate memory\n"); + return 1; + } + socks++; + } + + sock[n] = *ws; + + return 0; +} + +void client_cleanup(){ + size_t n; + for(n = 0; n < socks; n++){ + ws_close(sock + n, ws_close_shutdown, "Shutting down"); + } + + free(sock); + sock = NULL; + socks = 0; +} + +/* Establish peer connection for negotiated websocket */ +int client_connect(websocket* ws){ int rv = 1; ws->peer = config.backend.query(ws->request_path, ws->protocols, ws->protocol, ws->headers, ws->header, ws); @@ -71,6 +108,7 @@ int connect_peer(websocket* ws){ ws->peer.framing = framing_auto; } + //TODO connection establishment should be async in the future switch(ws->peer.transport){ case peer_tcp_client: ws->peer_fd = network_socket(ws->peer.host, ws->peer.port, SOCK_STREAM, 0); @@ -100,19 +138,17 @@ int connect_peer(websocket* ws){ return rv; } -#include "ws_proto.c" - /* * Signal handler, attached to SIGINT */ -void signal_handler(int signum){ +static void signal_handler(int signum){ shutdown_requested = 1; } /* * Usage info */ -int usage(char* fn){ +static int usage(char* fn){ fprintf(stderr, "\nwebsocksy v%s - Proxy between websockets and 'real' sockets\n", WEBSOCKSY_VERSION); fprintf(stderr, "Usage:\n"); fprintf(stderr, "\t%s [-p <port>] [-l <listen address>] [-b <targeting backend>]\n", fn); @@ -124,7 +160,7 @@ int args_parse(int argc, char** argv){ return 0; } -int ws_peer_data(websocket* ws){ +static int ws_peer_data(websocket* ws){ ssize_t bytes_read, bytes_left = sizeof(ws->peer_buffer) - ws->peer_buffer_offset; int64_t bytes_framed; //default to a binary frame @@ -268,7 +304,7 @@ int main(int argc, char** argv){ if(config.backend.cleanup){ config.backend.cleanup(); } - ws_cleanup(); + client_cleanup(); close(listen_fd); return 0; } diff --git a/websocksy.h b/websocksy.h index 59ec5b2..f50bb46 100644 --- a/websocksy.h +++ b/websocksy.h @@ -1,3 +1,5 @@ +#ifndef WEBSOCKSY_HEADER_INCLUDED +#define WEBSOCKSY_HEADER_INCLUDED #include <stdint.h> #include <stdlib.h> #include <nettle/sha1.h> @@ -199,3 +201,9 @@ typedef struct /*_ws_backend*/ { ws_backend_query query; ws_backend_cleanup cleanup; } ws_backend; + +/* Internal helper functions */ +char* xstr_lower(char* in); +int client_register(websocket* ws); +int client_connect(websocket* ws); +#endif |