aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2019-05-18 13:12:46 +0200
committercbdev <cb@cbcdn.com>2019-05-18 13:12:46 +0200
commitff94e4518d417d88f2fabdfca7025e68ce21849b (patch)
tree22232e7187cf5e7e9b03be84691a2b4f12de0645
parentd28b5eb2960e80d80e967a3f01ce70e1fa457808 (diff)
downloadwebsocksy-ff94e4518d417d88f2fabdfca7025e68ce21849b.tar.gz
websocksy-ff94e4518d417d88f2fabdfca7025e68ce21849b.tar.bz2
websocksy-ff94e4518d417d88f2fabdfca7025e68ce21849b.zip
Complete WS connection establishment
-rw-r--r--index.html10
-rw-r--r--makefile3
-rw-r--r--network.c99
-rw-r--r--websocksy.c86
-rw-r--r--websocksy.h4
-rw-r--r--ws_proto.c70
6 files changed, 176 insertions, 96 deletions
diff --git a/index.html b/index.html
index 233ee69..8a9ae9c 100644
--- a/index.html
+++ b/index.html
@@ -1,8 +1,16 @@
<html>
<head>
<script type="text/javascript">
+ var webSocket = null;
+
+ function openSocket(){
+ webSocket = new WebSocket("ws://localhost:8001/foo/bar");
+ webSocket.onclose = openSocket;
+ }
+
function init(){
- var webSocket = new WebSocket("ws://localhost:8001/foo/bar");
+ openSocket();
+ setInterval(function(){webSocket.send("foo");}, 1000);
}
</script>
</head>
diff --git a/makefile b/makefile
index 90716a0..2d23909 100644
--- a/makefile
+++ b/makefile
@@ -1,9 +1,10 @@
CFLAGS=-g -Wall -Wpedantic
+LDLIBS=-lnettle
all: websocksy
websocksy: websocksy.c websocksy.h ws_proto.c
- $(CC) $(CFLAGS) $< -o $@
+ $(CC) $(CFLAGS) $(LDLIBS) $< -o $@
clean:
rm websocksy
diff --git a/network.c b/network.c
new file mode 100644
index 0000000..9719b27
--- /dev/null
+++ b/network.c
@@ -0,0 +1,99 @@
+int network_socket(char* host, char* port, int socktype, int listener){
+ int fd = -1, status, yes = 1, flags;
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = socktype,
+ .ai_flags = (listener ? AI_PASSIVE : 0)
+ };
+ struct addrinfo *info, *addr_it;
+
+ status = getaddrinfo(host, port, &hints, &info);
+ if(status){
+ fprintf(stderr, "Failed to parse address %s port %s: %s\n", host, port, gai_strerror(status));
+ return -1;
+ }
+
+ //traverse the result list
+ for(addr_it = info; addr_it; addr_it = addr_it->ai_next){
+ fd = socket(addr_it->ai_family, addr_it->ai_socktype, addr_it->ai_protocol);
+ if(fd < 0){
+ continue;
+ }
+
+ //set required socket options
+ yes = 1;
+ if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(yes)) < 0){
+ fprintf(stderr, "Failed to enable SO_REUSEADDR on socket: %s\n", strerror(errno));
+ }
+
+ yes = 0;
+ if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&yes, sizeof(yes)) < 0){
+ fprintf(stderr, "Failed to unset IPV6_V6ONLY on socket: %s\n", strerror(errno));
+ }
+
+ //TODO loop, bcast for udp
+
+ if(listener){
+ status = bind(fd, addr_it->ai_addr, addr_it->ai_addrlen);
+ if(status < 0){
+ close(fd);
+ continue;
+ }
+ }
+ else{
+ status = connect(fd, addr_it->ai_addr, addr_it->ai_addrlen);
+ if(status < 0){
+ close(fd);
+ continue;
+ }
+ }
+
+ break;
+ }
+ freeaddrinfo(info);
+
+ if(!addr_it){
+ fprintf(stderr, "Failed to create socket for %s port %s\n", host, port);
+ return -1;
+ }
+
+ //set nonblocking
+ flags = fcntl(fd, F_GETFL, 0);
+ if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0){
+ fprintf(stderr, "Failed to set socket nonblocking: %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if(!listener){
+ return fd;
+ }
+
+ if(socktype == SOCK_STREAM){
+ status = listen(fd, SOMAXCONN);
+ if(status < 0){
+ fprintf(stderr, "Failed to listen on socket: %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ }
+
+ return fd;
+}
+
+int network_send(int fd, uint8_t* data, size_t length){
+ ssize_t total = 0, sent;
+ while(total < length){
+ sent = send(fd, data + total, length - total, 0);
+ if(sent < 0){
+ fprintf(stderr, "Failed to send: %s\n", strerror(errno));
+ return 1;
+ }
+ total += sent;
+ }
+ return 0;
+}
+
+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/websocksy.c b/websocksy.c
index f193ae6..ca7193b 100644
--- a/websocksy.c
+++ b/websocksy.c
@@ -10,6 +10,7 @@
#include <fcntl.h>
#include "websocksy.h"
+#include "network.c"
#include "ws_proto.c"
/* TODO
@@ -36,89 +37,6 @@ int args_parse(int argc, char** argv){
return 0;
}
-int network_socket(char* host, char* port, int socktype, int listener){
- int fd = -1, status, yes = 1, flags;
- struct addrinfo hints = {
- .ai_family = AF_UNSPEC,
- .ai_socktype = socktype,
- .ai_flags = (listener ? AI_PASSIVE : 0)
- };
- struct addrinfo *info, *addr_it;
-
- status = getaddrinfo(host, port, &hints, &info);
- if(status){
- fprintf(stderr, "Failed to parse address %s port %s: %s\n", host, port, gai_strerror(status));
- return -1;
- }
-
- //traverse the result list
- for(addr_it = info; addr_it; addr_it = addr_it->ai_next){
- fd = socket(addr_it->ai_family, addr_it->ai_socktype, addr_it->ai_protocol);
- if(fd < 0){
- continue;
- }
-
- //set required socket options
- yes = 1;
- if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(yes)) < 0){
- fprintf(stderr, "Failed to enable SO_REUSEADDR on socket: %s\n", strerror(errno));
- }
-
- yes = 0;
- if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&yes, sizeof(yes)) < 0){
- fprintf(stderr, "Failed to unset IPV6_V6ONLY on socket: %s\n", strerror(errno));
- }
-
- //TODO loop, bcast for udp
-
- if(listener){
- status = bind(fd, addr_it->ai_addr, addr_it->ai_addrlen);
- if(status < 0){
- close(fd);
- continue;
- }
- }
- else{
- status = connect(fd, addr_it->ai_addr, addr_it->ai_addrlen);
- if(status < 0){
- close(fd);
- continue;
- }
- }
-
- break;
- }
- freeaddrinfo(info);
-
- if(!addr_it){
- fprintf(stderr, "Failed to create socket for %s port %s\n", host, port);
- return -1;
- }
-
- //set nonblocking
- flags = fcntl(fd, F_GETFL, 0);
- if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0){
- fprintf(stderr, "Failed to set socket nonblocking: %s\n", strerror(errno));
- close(fd);
- return -1;
- }
-
- if(!listener){
- return fd;
- }
-
- if(socktype == SOCK_STREAM){
- status = listen(fd, SOMAXCONN);
- if(status < 0){
- fprintf(stderr, "Failed to listen on socket: %s\n", strerror(errno));
- close(fd);
- return -1;
- }
- }
-
- return fd;
-}
-
int ws_peer_data(websocket* ws){
//TODO
return -1;
@@ -139,6 +57,8 @@ int main(int argc, char** argv){
exit(usage(argv[0]));
}
+ signal(SIGINT, signal_handler);
+
//core loop
while(!shutdown_requested){
FD_ZERO(&read_fds);
diff --git a/websocksy.h b/websocksy.h
index 794b4ed..275b9ab 100644
--- a/websocksy.h
+++ b/websocksy.h
@@ -1,12 +1,14 @@
#include <stdint.h>
#include <stdlib.h>
+#include <nettle/sha1.h>
+#include <nettle/base64.h>
#define WS_MAX_LINE 16384
typedef enum {
ws_new = 0,
ws_http,
- ws_rfc6455
+ ws_open
} ws_state;
struct {
diff --git a/ws_proto.c b/ws_proto.c
index b9f92e9..7da6698 100644
--- a/ws_proto.c
+++ b/ws_proto.c
@@ -1,5 +1,7 @@
#include <ctype.h>
+#define RFC6455_MAGIC_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
static size_t socks = 0;
static websocket* sock = NULL;
@@ -96,15 +98,52 @@ int ws_handle_new(websocket* ws){
int ws_upgrade_http(websocket* ws){
if(ws->websocket_version == 13
+ && ws->socket_key
&& ws->want_upgrade){
+ if(network_send_str(ws->fd, "HTTP/1.1 101 Upgrading\r\n")
+ || network_send_str(ws->fd, "Upgrade: websocket\r\n")
+ || network_send_str(ws->fd, "Connection: Upgrade\r\n")){
+ //TODO might want to disconnect here
+ return 1;
+ }
- return 0;
+ //calculate the websocket key which for some reason is defined as
+ //base64(sha1(concat(trim(client-key), "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")))
+ //which requires not one but 2 unnecessarily complex operations
+ size_t encode_offset = 0;
+ struct sha1_ctx ws_accept_ctx;
+ struct base64_encode_ctx ws_accept_encode;
+ uint8_t ws_accept_digest[SHA1_DIGEST_SIZE];
+ char ws_accept_key[BASE64_ENCODE_LENGTH(SHA1_DIGEST_SIZE) + 3] = "";
+ sha1_init(&ws_accept_ctx);
+ sha1_update(&ws_accept_ctx, strlen(ws->socket_key), (uint8_t*) ws->socket_key);
+ sha1_update(&ws_accept_ctx, strlen(RFC6455_MAGIC_KEY), (uint8_t*) RFC6455_MAGIC_KEY);
+ sha1_digest(&ws_accept_ctx, sizeof(ws_accept_digest), (uint8_t*) &ws_accept_digest);
+ base64_encode_init(&ws_accept_encode);
+ encode_offset = base64_encode_update(&ws_accept_encode, (uint8_t*) ws_accept_key, SHA1_DIGEST_SIZE, ws_accept_digest);
+ encode_offset += base64_encode_final(&ws_accept_encode, (uint8_t*) ws_accept_key + encode_offset);
+ memcpy(ws_accept_key + encode_offset, "\r\n\0", 3);
+
+ //send websocket accept key
+ if(network_send_str(ws->fd, "Sec-WebSocket-Accept: ")
+ || network_send_str(ws->fd, ws_accept_key)){
+ return 1;
+ }
+
+ //TODO Sec-Websocket-Protocol
+ //TODO find/connect peer
+
+ ws->state = ws_open;
+ return network_send_str(ws->fd, "\r\n") ? 1 : 0;
}
+ //TODO RFC 4.2.2.4: An unsupported version must be answered with HTTP 426
return 1;
}
int ws_handle_http(websocket* ws){
char* header, *value;
+ ssize_t p;
+
if(!ws->read_buffer[0]){
return ws_upgrade_http(ws);
}
@@ -130,6 +169,10 @@ int ws_handle_http(websocket* ws){
ws->websocket_version = strtoul(value, NULL, 10);
}
else if(!strcmp(header, "Sec-WebSocket-Key")){
+ //right-trim the key
+ for(p = strlen(value) - 1; p >= 0 && isspace(value[p]); p--){
+ value[p] = 0;
+ }
ws->socket_key = strdup(value);
}
else if(!strcmp(header, "Upgrade") && !strcmp(value, "websocket")){
@@ -139,8 +182,15 @@ int ws_handle_http(websocket* ws){
return 0;
}
+int ws_frame(websocket* ws){
+ //TODO check for complete WS frame
+ //read/handle/forward data
+ fprintf(stderr, "Incoming websocket data\n");
+ return 0;
+}
+
int ws_data(websocket* ws){
- ssize_t bytes_read, u, bytes_left = sizeof(ws->read_buffer) - ws->read_buffer_offset;
+ ssize_t bytes_read, n, 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);
@@ -162,10 +212,10 @@ int ws_data(websocket* ws){
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)){
+ for(n = 0; n < bytes_read - 1; n++){
+ if(!strncmp((char*) ws->read_buffer + ws->read_buffer_offset + n, "\r\n", 2)){
//terminate line
- ws->read_buffer[ws->read_buffer_offset + u] = 0;
+ ws->read_buffer[ws->read_buffer_offset + n] = 0;
if(ws->state == ws_new){
rv |= ws_handle_new(ws);
@@ -176,17 +226,17 @@ int ws_data(websocket* 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);
+ bytes_read -= (n + 2);
+ memmove(ws->read_buffer, ws->read_buffer + ws->read_buffer_offset + n + 2, bytes_read);
ws->read_buffer_offset = 0;
//restart from the beginning
- u = -1;
+ n = -1;
}
}
break;
- //case ws_rfc6455:
- //TODO parse websocket encap, forward to peer
+ case ws_open:
+ rv |= ws_frame(ws);
}
//update read buffer offset