#include #include #include #include #include #include #include #include #include #include #include #include #include "websocksy.h" #include "builtins.h" #include "network.h" #include "websocket.h" #include "plugin.h" #include "config.h" #define DEFAULT_HOST "::" #define DEFAULT_PORT "8001" /* TODO * - TLS * - continuation */ /* Main loop condition, to be set from signal handler */ static volatile sig_atomic_t shutdown_requested = 0; /* Core client registry */ static size_t socks = 0; static websocket* sock = NULL; /* Lowercase input string in-place */ char* xstr_lower(char* in){ size_t n; for(n = 0; n < strlen(in); n++){ in[n] = tolower(in[n]); } return in; } /* Daemon configuration */ static ws_config config = { .host = NULL, .port = NULL, .ping_interval = 30, .verbosity = 0, /* Assign the built-in defaultpeer backend by default */ .backend.init = backend_defaultpeer_init, .backend.config = backend_defaultpeer_configure, .backend.query = backend_defaultpeer_query, .backend.cleanup = backend_defaultpeer_cleanup }; /* 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; } /* Clean up and close all connections */ 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; } static peer_transport client_detect_transport(char* host){ if(!strncmp(host, "tcp://", 6)){ memmove(host, host + 6, strlen(host) - 5); return peer_tcp_client; } else if(!strncmp(host, "udp://", 6)){ memmove(host, host + 6, strlen(host) - 5); return peer_udp_client; } else if(!strncmp(host, "udp-listen://", 13)){ memmove(host, host + 13, strlen(host) - 12); return peer_udp_listen; } else if(!strncmp(host, "fifotx://", 9)){ memmove(host, host + 9, strlen(host) - 8); return peer_fifo_tx; } else if(!strncmp(host, "fiforx://", 9)){ memmove(host, host + 9, strlen(host) - 8); return peer_fifo_rx; } else if(!strncmp(host, "unix://", 7)){ memmove(host, host + 7, strlen(host) - 6); return peer_unix_stream; } else if(!strncmp(host, "unix-dgram://", 13)){ memmove(host, host + 13, strlen(host) - 12); return peer_unix_dgram; } fprintf(stderr, "Peer address %s does not include any known protocol identifier, guessing tcp_client\n", host); return peer_tcp_client; } static char* client_detect_port(char* host){ size_t u; for(u = 0; host[u]; u++){ if(host[u] == ':'){ host[u] = 0; return strdup(host + u + 1); } } return NULL; } /* Establish peer connection for negotiated websocket */ int client_connect(websocket* ws){ ws->peer = config.backend.query(ws->request_path, ws->protocols, ws->protocol, ws->headers, ws->header, ws); if(!ws->peer.host){ //no peer provided return 1; } //assign default framing function if none provided if(!ws->peer.framing){ ws->peer.framing = framing_auto; } //if required scan the hostname for a protocol if(ws->peer.transport == peer_transport_detect){ ws->peer.transport = client_detect_transport(ws->peer.host); } if((ws->peer.transport == peer_tcp_client || ws->peer.transport == peer_udp_client || ws->peer.transport == peer_udp_listen) && !ws->peer.port){ ws->peer.port = client_detect_port(ws->peer.host); if(!ws->peer.port){ //no port provided return 1; } } //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); break; case peer_udp_client: ws->peer_fd = network_socket(ws->peer.host, ws->peer.port, SOCK_DGRAM, 0); break; case peer_udp_listen: //FIXME this might create issues when multiple clients connect and expect to receive all incoming messages ws->peer_fd = network_socket(ws->peer.host, ws->peer.port, SOCK_DGRAM, 1); break; case peer_fifo_tx: case peer_fifo_rx: //TODO implement other peer modes fprintf(stderr, "Peer connection mode not yet implemented\n"); return 1; case peer_unix_stream: ws->peer_fd = network_socket_unix(ws->peer.host, SOCK_STREAM, 0); break; case peer_unix_dgram: ws->peer_fd = network_socket_unix(ws->peer.host, SOCK_DGRAM, 0); break; default: fprintf(stderr, "Invalid peer transport selected\n"); return 1; } return (ws->peer_fd == -1) ? 1 : 0; } ws_framing core_framing(char* name){ return plugin_framing(name); } int core_register_framing(char* name, ws_framing func){ return plugin_register_framing(name, func); } /* Signal handler, attached to SIGINT */ static void signal_handler(int signum){ shutdown_requested = 1; } /* Usage info */ 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 \n", fn); fprintf(stderr, "\t%s [-p ] [-l ] [-b ] [-c