0b55209aff
High performance websocket client/server The goal of cwebsocket is to provide a portable, high performance websocket client/server, especially on low power embedded systems. cwebsocket is currently in a development state. You may encounter bugs. Report them for a timely fix. Successful tests have been conducted on the following architectures: x86 x86_64 ARM cwebsocket is compliant with the following standards: ANSI C POSIX RFC 6455 Signed-off-by: Alin Jerpelea <alin.jerpelea@sony.com>
1738 lines
61 KiB
Diff
1738 lines
61 KiB
Diff
From 3898d4b6601f951f630f615098f5546f7184fe90 Mon Sep 17 00:00:00 2001
|
|
From: SPRESENSE <41312067+SPRESENSE@users.noreply.github.com>
|
|
Date: Fri, 19 Apr 2019 17:40:58 +0900
|
|
Subject: [PATCH 1/2] Porting the code for NuttX
|
|
|
|
---
|
|
cwebsocket/src/cwebsocket/client.c | 801 +++++++++++++++++++----------
|
|
cwebsocket/src/cwebsocket/client.h | 177 ++++++-
|
|
cwebsocket/src/cwebsocket/common.c | 29 +-
|
|
cwebsocket/src/cwebsocket/common.h | 68 ++-
|
|
4 files changed, 752 insertions(+), 323 deletions(-)
|
|
|
|
diff --git a/cwebsocket/src/cwebsocket/client.c b/cwebsocket/src/cwebsocket/client.c
|
|
index 09f7a06..50747f0 100755
|
|
--- a/cwebsocket/src/cwebsocket/client.c
|
|
+++ b/cwebsocket/src/cwebsocket/client.c
|
|
@@ -24,116 +24,198 @@
|
|
|
|
#include "client.h"
|
|
|
|
-void cwebsocket_client_init(cwebsocket_client *websocket, cwebsocket_subprotocol *subprotocols[], int subprotocol_len) {
|
|
+/* for NUTTX */
|
|
+extern int getaddrinfo(const char *nodename, const char *servname,
|
|
+ const struct addrinfo *hints, struct addrinfo **res);
|
|
+extern void freeaddrinfo(struct addrinfo *res);
|
|
+extern int ws_sscanf(FAR const char *buf, FAR const char *fmt, ...);
|
|
+
|
|
+int cwebsocket_client_init(cwebsocket_client *websocket, cwebsocket_subprotocol **subprotocols, int subprotocol_len) {
|
|
+ if (subprotocol_len > WEBSOCKET_SUBPROTOCOL_MAX)
|
|
+ return -1;
|
|
websocket->fd = 0;
|
|
websocket->retry = 0;
|
|
websocket->uri = '\0';
|
|
websocket->flags = 0;
|
|
websocket->state = WEBSOCKET_STATE_CLOSED;
|
|
+ websocket->proxy_addr = NULL;
|
|
+ websocket->proxy_port = NULL;
|
|
+ websocket->headers = NULL;
|
|
+ websocket->num_headers = 0;
|
|
websocket->subprotocol_len = subprotocol_len;
|
|
int i;
|
|
for(i=0; i<subprotocol_len; i++) {
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_init: loading subprotocol %s", subprotocols[i]->name);
|
|
+ WS_DEBUG("client_init: loading subprotocol %s\n", subprotocols[i]->name);
|
|
+
|
|
websocket->subprotocols[i] = subprotocols[i];
|
|
}
|
|
- const rlim_t kStackSize = CWS_STACK_SIZE_MIN * 1024 * 1024;
|
|
- struct rlimit rl;
|
|
- int result;
|
|
- result = getrlimit(RLIMIT_STACK, &rl);
|
|
- if (result == 0) {
|
|
- if (rl.rlim_cur < kStackSize) {
|
|
- rl.rlim_cur = kStackSize;
|
|
- result = setrlimit(RLIMIT_STACK, &rl);
|
|
- if(result != 0) {
|
|
- syslog(LOG_CRIT, "cwebsocket_client_init: unable to set stack space");
|
|
- exit(1);
|
|
- }
|
|
- }
|
|
+ websocket->subprotocol = websocket->subprotocols[0];
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void cwebsocket_client_set_proxy(cwebsocket_client *websocket, char *proxy_addr, char *proxy_port)
|
|
+{
|
|
+ if (strlen(proxy_addr) > 0 && strlen(proxy_port) > 0){
|
|
+ websocket->flags |= WEBSOCKET_FLAG_PROXY;
|
|
+ websocket->proxy_addr = proxy_addr;
|
|
+ websocket->proxy_port = proxy_port;
|
|
+ }else {
|
|
+ WS_DEBUG("client_set_proxy: invalid proxy_addr\n");
|
|
+ }
|
|
+}
|
|
+void cwebsocket_client_unset_proxy(cwebsocket_client *websocket)
|
|
+{
|
|
+ if (websocket->flags & WEBSOCKET_FLAG_PROXY){
|
|
+ websocket->proxy_addr = NULL;
|
|
+ websocket->proxy_port = NULL;
|
|
+ websocket->flags |= ~WEBSOCKET_FLAG_PROXY;
|
|
}
|
|
- getrlimit(RLIMIT_STACK, &rl);
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_init: stack limit min=%ld, max=%ld\n", rl.rlim_cur, rl.rlim_max);
|
|
}
|
|
|
|
void cwebsocket_client_parse_uri(cwebsocket_client *websocket, const char *uri,
|
|
char *hostname, char *port, char *resource, char *querystring) {
|
|
|
|
- if(sscanf(uri, "ws://%[^:]:%[^/]%[^?]%s", hostname, port, resource, querystring) == 4) {
|
|
+ if(ws_sscanf(uri, "ws://%[^:]:%[^/]%[^?]%s", hostname, port, resource, querystring) == 4) {
|
|
return;
|
|
}
|
|
- else if(sscanf(uri, "ws://%[^:]:%[^/]%s", hostname, port, resource) == 3) {
|
|
+ else if(ws_sscanf(uri, "ws://%[^:]:%[^/]%s", hostname, port, resource) == 3) {
|
|
strcpy(querystring, "");
|
|
return;
|
|
}
|
|
- else if(sscanf(uri, "ws://%[^:]:%[^/]%s", hostname, port, resource) == 2) {
|
|
- strcpy(resource, "/");
|
|
+ else if(ws_sscanf(uri, "ws://%[^:]:%[^/]%s", hostname, port, resource) == 2) {
|
|
+ strncpy(resource, "/", strlen("/"));
|
|
strcpy(querystring, "");
|
|
return;
|
|
}
|
|
- else if(sscanf(uri, "ws://%[^/]%s", hostname, resource) == 2) {
|
|
- strcpy(port, "80");
|
|
+ else if(ws_sscanf(uri, "ws://%[^/]%s", hostname, resource) == 2) {
|
|
+ strncpy(port, "80", strlen("80"));
|
|
strcpy(querystring, "");
|
|
return;
|
|
}
|
|
- else if(sscanf(uri, "ws://%[^/]", hostname) == 1) {
|
|
- strcpy(port, "80");
|
|
- strcpy(resource, "/");
|
|
+ else if(ws_sscanf(uri, "ws://%[^/]", hostname) == 1) {
|
|
+ strncpy(port, "80", strlen("80"));
|
|
+ strncpy(resource, "/", strlen("/"));
|
|
strcpy(querystring, "");
|
|
return;
|
|
}
|
|
#ifdef ENABLE_SSL
|
|
- else if(sscanf(uri, "wss://%[^:]:%[^/]%[^?]%s", hostname, port, resource, querystring) == 4) {
|
|
+ else if(ws_sscanf(uri, "wss://%[^:]:%[^/]%[^?]%s", hostname, port, resource, querystring) == 4) {
|
|
websocket->flags |= WEBSOCKET_FLAG_SSL;
|
|
return;
|
|
}
|
|
- else if(sscanf(uri, "wss://%[^:]:%[^/]%s", hostname, port, resource) == 3) {
|
|
+ else if(ws_sscanf(uri, "wss://%[^:]:%[^/]%s", hostname, port, resource) == 3) {
|
|
strcpy(querystring, "");
|
|
websocket->flags |= WEBSOCKET_FLAG_SSL;
|
|
return;
|
|
}
|
|
- else if(sscanf(uri, "wss://%[^:]:%[^/]%s", hostname, port, resource) == 2) {
|
|
- strcpy(resource, "/");
|
|
+ else if(ws_sscanf(uri, "wss://%[^:]:%[^/]%s", hostname, port, resource) == 2) {
|
|
+ strncpy(resource, "/", strlen("/"));
|
|
strcpy(querystring, "");
|
|
websocket->flags |= WEBSOCKET_FLAG_SSL;
|
|
return;
|
|
}
|
|
- else if(sscanf(uri, "wss://%[^/]%s", hostname, resource) == 2) {
|
|
- strcpy(port, "443");
|
|
+ else if(ws_sscanf(uri, "wss://%[^/]%s", hostname, resource) == 2) {
|
|
+ strncpy(port, "443", strlen("443"));
|
|
strcpy(querystring, "");
|
|
websocket->flags |= WEBSOCKET_FLAG_SSL;
|
|
return;
|
|
}
|
|
- else if(sscanf(uri, "wss://%[^/]", hostname) == 1) {
|
|
- strcpy(port, "443");
|
|
- strcpy(resource, "/");
|
|
+ else if(ws_sscanf(uri, "wss://%[^/]", hostname) == 1) {
|
|
+ strncpy(port, "443", strlen("443"));
|
|
+ strncpy(resource, "/", strlen("/"));
|
|
strcpy(querystring, "");
|
|
websocket->flags |= WEBSOCKET_FLAG_SSL;
|
|
return;
|
|
}
|
|
#endif
|
|
else if(strstr(uri, "wss://") > 0) {
|
|
- syslog(LOG_CRIT, "cwebsocket_client_parse_uri: recompile with SSL support to use a secure connection");
|
|
+ WS_DEBUG("client_parse_uri: recompile with SSL support to use a secure connection");
|
|
exit(1);
|
|
}
|
|
else {
|
|
- syslog(LOG_CRIT, "cwebsocket_client_parse_uri: invalid websocket URL\n");
|
|
+ WS_DEBUG("client_parse_uri: invalid websocket URL : %s\n", uri);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
+
|
|
+int cwebsocket_client_ssl_init(cwebsocket_client *websocket, char *cert_name, char *cli_cert, char *cli_key, char *passphrase) {
|
|
+#ifdef ENABLE_SSL
|
|
+ int ret= 0;
|
|
+
|
|
+ WS_DEBUG("client_connect: using secure (SSL) connection\n");
|
|
+
|
|
+ mbedtls_net_init( &websocket->ssl_net_ctx );
|
|
+ mbedtls_ssl_init( &websocket->ssl );
|
|
+ mbedtls_ssl_config_init( &websocket->conf );
|
|
+ mbedtls_x509_crt_init( &websocket->cacert );
|
|
+ mbedtls_ctr_drbg_init( &websocket->ctr_drbg );
|
|
+ mbedtls_entropy_init( &websocket->entropy );
|
|
+
|
|
+ if( ( ret = mbedtls_ctr_drbg_seed( &websocket->ctr_drbg, NULL, &websocket->entropy,
|
|
+ (const unsigned char *) "ssl_client1",
|
|
+ strlen( "ssl_client1" ) ) ) != 0 )
|
|
+ {
|
|
+ WS_DEBUG(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret );
|
|
+ return -1;
|
|
+ }
|
|
+ ret = mbedtls_x509_crt_parse_file( &websocket->cacert, cert_name );
|
|
+ if( ret != 0 )
|
|
+ {
|
|
+ WS_DEBUG("nothing file = -0x%x\n", -ret);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if( ( ret = mbedtls_ssl_config_defaults( &websocket->conf,
|
|
+ MBEDTLS_SSL_IS_CLIENT,
|
|
+ MBEDTLS_SSL_TRANSPORT_STREAM,
|
|
+ MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 )
|
|
+ {
|
|
+ WS_DEBUG(" failed\n ! mbedtls_ssl_config_defaults returned -0x%x\n\n", ret );
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* OPTIONAL is not optimal for security,
|
|
+ * but makes interop easier in this simplified example */
|
|
+ mbedtls_ssl_conf_authmode( &websocket->conf, MBEDTLS_SSL_VERIFY_OPTIONAL );
|
|
+ mbedtls_ssl_conf_ca_chain( &websocket->conf, &websocket->cacert, NULL );
|
|
+ mbedtls_ssl_conf_rng( &websocket->conf, NULL, &websocket->ctr_drbg );
|
|
+
|
|
+ if (cli_cert != NULL && cli_key != NULL){
|
|
+ ret = mbedtls_x509_crt_parse_file(&websocket->clicert, cli_cert);
|
|
+ if (ret == 0) {
|
|
+ ret = mbedtls_pk_parse_keyfile(&websocket->pkey, cli_key, passphrase);
|
|
+ if (ret != 0) {
|
|
+ WS_DEBUG("Private Key not found\n");
|
|
+ return -1;
|
|
+ }
|
|
+ } else {
|
|
+ WS_DEBUG("Client certificate not found\n");
|
|
+ return -1;
|
|
+ }
|
|
+ mbedtls_ssl_conf_own_cert(&websocket->conf, &websocket->clicert, &websocket->pkey);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+#else
|
|
+ return -1;
|
|
+#endif
|
|
+}
|
|
+
|
|
int cwebsocket_client_connect(cwebsocket_client *websocket) {
|
|
|
|
if(websocket->state & WEBSOCKET_STATE_CONNECTED) {
|
|
- syslog(LOG_CRIT, "cwebsocket_client_connect: socket already connected");
|
|
+ WS_DEBUG("client_connect: socket already connected");
|
|
return -1;
|
|
}
|
|
|
|
if(websocket->state & WEBSOCKET_STATE_CONNECTING) {
|
|
- syslog(LOG_CRIT, "cwebsocket_client_connect: socket already connecting");
|
|
+ WS_DEBUG("client_connect: socket already connecting");
|
|
return -1;
|
|
}
|
|
|
|
if(websocket->state & WEBSOCKET_STATE_OPEN) {
|
|
- syslog(LOG_CRIT, "cwebsocket_client_connect: socket already open");
|
|
+ WS_DEBUG("client_connect: socket already open");
|
|
return -1;
|
|
}
|
|
|
|
@@ -154,24 +236,35 @@ int cwebsocket_client_connect(cwebsocket_client *websocket) {
|
|
#else
|
|
websocket->state = WEBSOCKET_STATE_CONNECTING;
|
|
#endif
|
|
-
|
|
+#ifdef ENABLE_SSL
|
|
+ uint32_t flags;
|
|
+ int ret;
|
|
+#endif
|
|
char hostname[100];
|
|
char port[6];
|
|
char resource[256];
|
|
char querystring[256];
|
|
+
|
|
+ memset(hostname, 0, 100);
|
|
+ memset(port, 0, 6);
|
|
+ memset(resource, 0, 256);
|
|
+ memset(querystring, 0, 100);
|
|
+
|
|
cwebsocket_client_parse_uri(websocket, websocket->uri, hostname, port, resource, querystring);
|
|
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_connect: hostname=%s, port=%s, resource=%s, querystring=%s, secure=%i\n",
|
|
+ WS_DEBUG ("client_connect: hostname=%s, port=%s, resource=%s, querystring=%s, secure=%i\n",
|
|
hostname, port, resource, querystring, (websocket->flags & WEBSOCKET_FLAG_SSL));
|
|
|
|
- char handshake[1024];
|
|
- struct addrinfo hints, *servinfo;
|
|
+ char handshake[CWS_HANDSHAKE_BUFFER_MAX];
|
|
+ struct addrinfo hints, *servinfo;
|
|
+ time_t Tick0 = 0;
|
|
+ time(&Tick0);
|
|
|
|
memset(&hints, 0, sizeof hints);
|
|
hints.ai_family = AF_INET;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
- srand(time(NULL));
|
|
+ srand(Tick0);
|
|
char nonce[16];
|
|
static const char alphanum[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz";
|
|
int i;
|
|
@@ -180,100 +273,178 @@ int cwebsocket_client_connect(cwebsocket_client *websocket) {
|
|
}
|
|
char *seckey = cwebsocket_base64_encode((const unsigned char *)nonce, sizeof(nonce));
|
|
|
|
- snprintf(handshake, 1024,
|
|
- "GET %s%s HTTP/1.1\r\n"
|
|
- "Host: %s:%s\r\n"
|
|
- "Upgrade: WebSocket\r\n"
|
|
- "Connection: Upgrade\r\n"
|
|
- "Sec-WebSocket-Key: %s\r\n"
|
|
- "Sec-WebSocket-Version: 13\r\n"
|
|
+ snprintf(handshake, CWS_HANDSHAKE_BUFFER_MAX,
|
|
+ "GET %s%s HTTP/1.1\r\n"
|
|
+ "Host: %s:%s\r\n"
|
|
+ "Upgrade: WebSocket\r\n"
|
|
+ "Connection: Upgrade\r\n"
|
|
+ "Sec-WebSocket-Key: %s\r\n"
|
|
+ "Sec-WebSocket-Version: 13\r\n"
|
|
,resource, querystring, hostname, port, seckey);
|
|
|
|
+ if((websocket->headers != NULL) && (websocket->num_headers > 0)) {
|
|
+ for(i = 0; i < websocket->num_headers; i++) {
|
|
+ strncat(handshake, websocket->headers[i], (CWS_HANDSHAKE_BUFFER_MAX - strlen(handshake)));
|
|
+ strncat(handshake, "\r\n", (CWS_HANDSHAKE_BUFFER_MAX - strlen(handshake)));
|
|
+ }
|
|
+ }
|
|
+
|
|
if(websocket->subprotocol_len > 0) {
|
|
- strcat(handshake, "Sec-WebSocket-Protocol: ");
|
|
+ strncat(handshake, "Sec-WebSocket-Protocol: ", (CWS_HANDSHAKE_BUFFER_MAX - strlen(handshake)));
|
|
for(i=0; i<websocket->subprotocol_len; i++) {
|
|
- strcat(handshake, websocket->subprotocols[i]->name);
|
|
+ strncat(handshake, websocket->subprotocols[i]->name, (CWS_HANDSHAKE_BUFFER_MAX - strlen(handshake)));
|
|
if(i<websocket->subprotocol_len) {
|
|
- strcat(handshake, " ");
|
|
+ strncat(handshake, " ", (CWS_HANDSHAKE_BUFFER_MAX - strlen(handshake)));
|
|
}
|
|
else {
|
|
- strcat(handshake, "\r\n");
|
|
+ strncat(handshake, "\r\n", (CWS_HANDSHAKE_BUFFER_MAX - strlen(handshake)));
|
|
}
|
|
}
|
|
+ strncat(handshake, "\r\n", (CWS_HANDSHAKE_BUFFER_MAX - strlen(handshake)));
|
|
}
|
|
|
|
- strcat(handshake, "\r\n");
|
|
+ strncat(handshake, "\r\n", (CWS_HANDSHAKE_BUFFER_MAX - strlen(handshake)));
|
|
|
|
- if(getaddrinfo(hostname, port, &hints, &servinfo) != 0 ) {
|
|
- freeaddrinfo(servinfo);
|
|
- const char *errmsg = "invalid hostname or IP";
|
|
- syslog(LOG_ERR, "cwebsocket_client_connect: %s", errmsg);
|
|
- cwebsocket_client_onerror(websocket, errmsg);
|
|
- return -1;
|
|
- }
|
|
+ if(websocket->flags & WEBSOCKET_FLAG_SSL) {
|
|
+#ifdef ENABLE_SSL
|
|
+ if( ( ret = mbedtls_ssl_setup( &websocket->ssl, &websocket->conf ) ) != 0 )
|
|
+ {
|
|
+ WS_DEBUG( " failed\n ! mbedtls_ssl_setup returned -0x%x\n\n", -ret );
|
|
+ goto fail;
|
|
+ }
|
|
+ if(websocket->flags & WEBSOCKET_FLAG_PROXY) {
|
|
+ /* SSL On / Proxy On
|
|
+ Execute CONNECT command of non-secure message after connecting to proxy server.
|
|
+ */
|
|
+ if( ( ret = mbedtls_net_connect( &websocket->ssl_net_ctx, websocket->proxy_addr,
|
|
+ websocket->proxy_port, MBEDTLS_NET_PROTO_TCP ) ) != 0 )
|
|
+ {
|
|
+ WS_DEBUG(" failed\n ! mbedtls_net_connect proxy returned %d\n\n", ret );
|
|
+ goto fail;
|
|
+ }
|
|
+ if(websocket->flags & WEBSOCKET_FLAG_PROXY) {
|
|
+ char proxy_connect[CWS_HANDSHAKE_BUFFER_MAX];
|
|
+
|
|
+ snprintf(proxy_connect, CWS_HANDSHAKE_BUFFER_MAX,
|
|
+ "CONNECT %s:%s HTTP/1.1\r\n"
|
|
+ "Host: %s\r\n"
|
|
+ , hostname, port, hostname);
|
|
+ strncat(proxy_connect, "\r\n", (CWS_HANDSHAKE_BUFFER_MAX - strlen(proxy_connect)));
|
|
+ if(write(websocket->ssl_net_ctx.fd, proxy_connect, strlen(proxy_connect)) == -1) {
|
|
+ WS_DEBUG("proxy_client_write: NG A\n");
|
|
+ goto fail;
|
|
+ }
|
|
+ if(cwebsocket_client_read_handshake(websocket, seckey, websocket->flags) == -1) {
|
|
+ WS_DEBUG("proxy_client_read_handshake for SSL: %s\n", seckey);
|
|
+ goto fail;
|
|
+ }
|
|
+ }
|
|
+ }else{
|
|
+ /* SSL On / Proxy OFF
|
|
+ Connecting to websocket server.
|
|
+ */
|
|
+ if( ( ret = mbedtls_net_connect( &websocket->ssl_net_ctx, hostname,
|
|
+ port, MBEDTLS_NET_PROTO_TCP ) ) != 0 )
|
|
+ {
|
|
+ WS_DEBUG(" failed\n ! mbedtls_net_connect returned %d\n\n", ret );
|
|
+ goto fail;
|
|
+ }
|
|
+ }
|
|
|
|
- websocket->fd = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
|
|
- if(websocket->fd < 0) {
|
|
- freeaddrinfo(servinfo);
|
|
- syslog(LOG_ERR, "cwebsocket_client_connect: %s", strerror(errno));
|
|
- cwebsocket_client_onerror(websocket, strerror(errno));
|
|
- return -1;
|
|
- }
|
|
+ mbedtls_ssl_set_bio( &websocket->ssl, &websocket->ssl_net_ctx, NULL, NULL, NULL);
|
|
|
|
- if(connect(websocket->fd, servinfo->ai_addr, servinfo->ai_addrlen) != 0 ) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_connect: %s", strerror(errno));
|
|
- cwebsocket_client_onerror(websocket, strerror(errno));
|
|
- websocket->state = WEBSOCKET_STATE_CLOSED;
|
|
- if(websocket->retry > 0) {
|
|
- sleep(websocket->retry);
|
|
- cwebsocket_client_connect(websocket);
|
|
+ while( ( ret = mbedtls_ssl_handshake( &websocket->ssl ) ) != 0 )
|
|
+ {
|
|
+ if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE )
|
|
+ {
|
|
+ WS_DEBUG(" failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n", -ret );
|
|
+ goto fail;
|
|
+ }
|
|
}
|
|
- return -1;
|
|
- }
|
|
- freeaddrinfo(servinfo);
|
|
+ websocket->fd = websocket->ssl_net_ctx.fd;
|
|
|
|
- int optval = 1;
|
|
- if(setsockopt(websocket->fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof optval) == -1) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_connect: %s", strerror(errno));
|
|
- cwebsocket_client_onerror(websocket, strerror(errno));
|
|
- return -1;
|
|
- }
|
|
+ /* In real life, we probably want to bail out when ret != 0 */
|
|
+ if( ( flags = mbedtls_ssl_get_verify_result( &websocket->ssl ) ) != 0 )
|
|
+ {
|
|
+ char vrfy_buf[512];
|
|
|
|
-#ifdef ENABLE_SSL
|
|
+ mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), " ! ", flags );
|
|
|
|
- websocket->ssl = NULL;
|
|
- websocket->sslctx = NULL;
|
|
+ WS_DEBUG("%s\n", vrfy_buf );
|
|
|
|
- if(websocket->flags & WEBSOCKET_FLAG_SSL) {
|
|
-
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_connect: using secure (SSL) connection");
|
|
+ }
|
|
+ else
|
|
+ WS_DEBUG(" certificate ok\n");
|
|
+#endif
|
|
+ }else {
|
|
+ if(websocket->flags & WEBSOCKET_FLAG_PROXY) {
|
|
+ if(getaddrinfo(websocket->proxy_addr, websocket->proxy_port, &hints, &servinfo) != 0 ) {
|
|
+ freeaddrinfo(servinfo);
|
|
+ const char *errmsg = "invalid hostname or IP";
|
|
+ WS_DEBUG("client_proxy_connect: %s", websocket->proxy_addr);
|
|
+ cwebsocket_client_onerror(websocket, errmsg);
|
|
+ goto fail;
|
|
+ }
|
|
+ }else {
|
|
+ if(getaddrinfo(hostname, port, &hints, &servinfo) != 0 ) {
|
|
+ freeaddrinfo(servinfo);
|
|
+ const char *errmsg = "invalid hostname or IP";
|
|
+ WS_DEBUG("client_non_proxy_connect: %s", errmsg);
|
|
+ cwebsocket_client_onerror(websocket, errmsg);
|
|
+ goto fail;
|
|
+ }
|
|
+ }
|
|
|
|
- SSL_load_error_strings();
|
|
- SSL_library_init();
|
|
+ websocket->fd = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
|
|
+ if(websocket->fd < 0) {
|
|
+ freeaddrinfo(servinfo);
|
|
+ goto fail;
|
|
+ }
|
|
|
|
- websocket->sslctx = SSL_CTX_new(SSLv23_client_method());
|
|
- if(websocket->sslctx == NULL) {
|
|
- ERR_print_errors_fp(stderr);
|
|
- return -1;
|
|
- }
|
|
+ if(connect(websocket->fd, servinfo->ai_addr, servinfo->ai_addrlen) != 0 ) {
|
|
+ freeaddrinfo(servinfo);
|
|
+ websocket->state = WEBSOCKET_STATE_CLOSED;
|
|
+ if(websocket->retry > 0) {
|
|
+ usleep(websocket->retry * 1000);
|
|
+ cwebsocket_client_connect(websocket);
|
|
+ }
|
|
+ goto fail;
|
|
+ }
|
|
+ freeaddrinfo(servinfo);
|
|
|
|
- websocket->ssl = SSL_new(websocket->sslctx);
|
|
- if(websocket->ssl == NULL) {
|
|
- ERR_print_errors_fp(stderr);
|
|
- return -1;
|
|
- }
|
|
+ int optval = 1;
|
|
+ if(setsockopt(websocket->fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof optval) == -1) {
|
|
+ goto fail;
|
|
+ }
|
|
+ struct timeval tv;
|
|
+ tv.tv_sec = 1; /* 1 second Timeout */
|
|
+ tv.tv_usec = 0;
|
|
|
|
- if(!SSL_set_fd(websocket->ssl, websocket->fd)) {
|
|
- ERR_print_errors_fp(stderr);
|
|
- return -1;
|
|
- }
|
|
+ if(setsockopt(websocket->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)) == -1) {
|
|
+ goto fail;
|
|
+ }
|
|
|
|
- if(SSL_connect(websocket->ssl) != 1) {
|
|
- ERR_print_errors_fp(stderr);
|
|
- return -1;
|
|
- }
|
|
+ if(websocket->flags & WEBSOCKET_FLAG_PROXY) {
|
|
+ /* SSL Off / Proxy On
|
|
+ Execute CONNECT command after connecting to proxy server.
|
|
+ */
|
|
+ char proxy_connect[CWS_HANDSHAKE_BUFFER_MAX];
|
|
+
|
|
+ snprintf(proxy_connect, CWS_HANDSHAKE_BUFFER_MAX,
|
|
+ "CONNECT %s:%s HTTP/1.1\r\n"
|
|
+ "Host: %s\r\n"
|
|
+ , hostname, port, hostname);
|
|
+ strncat(proxy_connect, "\r\n", (CWS_HANDSHAKE_BUFFER_MAX - strlen(proxy_connect)));
|
|
+ if(write(websocket->fd, proxy_connect, strlen(proxy_connect)) == -1) {
|
|
+ WS_DEBUG("proxy_client_write: NG\n");
|
|
+ goto fail;
|
|
+ }
|
|
+ if(cwebsocket_client_read_handshake(websocket, seckey, websocket->flags) == -1) {
|
|
+ WS_DEBUG("proxy_client_read_handshake: %s\n", seckey);
|
|
+ goto fail;
|
|
+ }
|
|
+ }
|
|
}
|
|
-#endif
|
|
|
|
#ifdef ENABLE_THREADS
|
|
pthread_mutex_lock(&websocket->lock);
|
|
@@ -284,17 +455,16 @@ int cwebsocket_client_connect(cwebsocket_client *websocket) {
|
|
#endif
|
|
|
|
if(cwebsocket_client_write(websocket, handshake, strlen(handshake)) == -1) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_connect: %s", strerror(errno));
|
|
- cwebsocket_client_onerror(websocket, strerror(errno));
|
|
- return -1;
|
|
+ WS_DEBUG("client_connect: NG\n");
|
|
+ goto fail;
|
|
}
|
|
|
|
- if(cwebsocket_client_read_handshake(websocket, seckey) == -1) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_connect: %s", strerror(errno));
|
|
- cwebsocket_client_onerror(websocket, strerror(errno));
|
|
- return -1;
|
|
+ if(cwebsocket_client_read_handshake(websocket, seckey, 0) == -1) {
|
|
+ WS_DEBUG("client_connect: %s\n", seckey);
|
|
+ goto fail;
|
|
}
|
|
|
|
+ free(seckey);
|
|
#ifdef ENABLE_THREADS
|
|
pthread_mutex_lock(&websocket->lock);
|
|
websocket->state = WEBSOCKET_STATE_OPEN;
|
|
@@ -306,11 +476,15 @@ int cwebsocket_client_connect(cwebsocket_client *websocket) {
|
|
cwebsocket_client_onopen(websocket);
|
|
|
|
return 0;
|
|
+fail:
|
|
+ free(seckey);
|
|
+ cwebsocket_client_close(websocket, 1009, "connect failed");
|
|
+ return -1;
|
|
}
|
|
|
|
int cwebsocket_client_handshake_handler(cwebsocket_client *websocket, const char *handshake_response, char *seckey) {
|
|
uint8_t flags = 0;
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_handshake_handler: handshake response: \n%s\n", handshake_response);
|
|
+ WS_DEBUG("client_handshake_handler: handshake response: \n%s\n", handshake_response);
|
|
char *ptr = NULL, *token = NULL;
|
|
for(token = strtok((char *)handshake_response, "\r\n"); token != NULL; token = strtok(NULL, "\r\n")) {
|
|
if(*token == 'H' && *(token+1) == 'T' && *(token+2) == 'T' && *(token+3) == 'P') {
|
|
@@ -324,20 +498,21 @@ int cwebsocket_client_handshake_handler(cwebsocket_client *websocket, const char
|
|
} else {
|
|
ptr = strchr(token, ' ');
|
|
if(ptr == NULL) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_handshake_handler: invalid HTTP header sent: %s", token);
|
|
+ WS_DEBUG("client_handshake_handler: invalid HTTP header sent: %s\n", token);
|
|
cwebsocket_client_onerror(websocket, "invalid HTTP header sent");
|
|
return -1;
|
|
}
|
|
*ptr = '\0';
|
|
if(strcasecmp(token, "Upgrade:") == 0) {
|
|
- if(strcasecmp(ptr+1, "websocket") != 0) {
|
|
+
|
|
+ if(strncasecmp(ptr+1, "websocket", strlen("websocket") -1) != 0) {
|
|
cwebsocket_client_onerror(websocket, "cwebsocket_client_handshake_handler: invalid HTTP upgrade header");
|
|
return -1;
|
|
}
|
|
flags |= CWS_HANDSHAKE_HAS_UPGRADE;
|
|
}
|
|
if(strcasecmp(token, "Connection:") == 0) {
|
|
- if(strcasecmp(ptr+1, "upgrade") != 0) {
|
|
+ if(strncasecmp(ptr+1, "upgrade", strlen("upgrade") -1) != 0) {
|
|
cwebsocket_client_onerror(websocket, "cwebsocket_client_handshake_handler: invalid HTTP connection header");
|
|
return -1;
|
|
}
|
|
@@ -346,27 +521,24 @@ int cwebsocket_client_handshake_handler(cwebsocket_client *websocket, const char
|
|
if(strcasecmp(token, "Sec-WebSocket-Protocol:") == 0) {
|
|
int i;
|
|
for(i=0; i<websocket->subprotocol_len; i++) {
|
|
- if(strcasecmp(ptr+1, websocket->subprotocols[i]->name) == 0) {
|
|
+ if(strncasecmp(ptr+1, websocket->subprotocols[i]->name, strlen(websocket->subprotocols[i]->name) -1) == 0) {
|
|
websocket->subprotocol = websocket->subprotocols[i];
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_handshake_handler: setting subprotocol to %s", websocket->subprotocol->name);
|
|
+ WS_DEBUG("client_handshake_handler: setting subprotocol to %s\n", websocket->subprotocol->name);
|
|
}
|
|
}
|
|
}
|
|
if(strcasecmp(token, "Sec-WebSocket-Accept:") == 0) {
|
|
char* response = cwebsocket_create_key_challenge_response(seckey);
|
|
- if(strcmp(ptr+1, response) != 0) {
|
|
- free(seckey);
|
|
+ if(strncmp(ptr+1, response, strlen(response) -1) != 0) {
|
|
if(websocket->subprotocol->onerror != NULL) {
|
|
char errmsg[255];
|
|
sprintf(errmsg, "cwebsocket_client_handshake_handler: Sec-WebSocket-Accept header does not match computed sha1/base64 response. expected=%s, actual=%s", response, ptr+1);
|
|
cwebsocket_client_onerror(websocket, errmsg);
|
|
- free(response);
|
|
- return -1;
|
|
}
|
|
+ free(response);
|
|
return -1;
|
|
}
|
|
free(response);
|
|
- free(seckey);
|
|
flags |= CWS_HANDSHAKE_HAS_ACCEPT;
|
|
}
|
|
}
|
|
@@ -377,11 +549,11 @@ int cwebsocket_client_handshake_handler(cwebsocket_client *websocket, const char
|
|
cwebsocket_client_close(websocket, 1002, "invalid websocket HTTP headers");
|
|
return -1;
|
|
}
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_handshake_handler: handshake successful");
|
|
+ WS_DEBUG("client_handshake_handler: handshake successful\n");
|
|
return 0;
|
|
}
|
|
|
|
-int cwebsocket_client_read_handshake(cwebsocket_client *websocket, char *seckey) {
|
|
+int cwebsocket_client_read_handshake(cwebsocket_client *websocket, char *seckey, int flags) {
|
|
|
|
int byte, tmplen = 0;
|
|
uint32_t bytes_read = 0;
|
|
@@ -394,12 +566,11 @@ int cwebsocket_client_read_handshake(cwebsocket_client *websocket, char *seckey)
|
|
|
|
if(byte == 0) return -1;
|
|
if(byte == -1) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_read_handshake: %s", strerror(errno));
|
|
- cwebsocket_client_onerror(websocket, strerror(errno));
|
|
+ WS_DEBUG("client_read_handshake: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
if(bytes_read == CWS_HANDSHAKE_BUFFER_MAX) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_read_handshake: handshake response too large. CWS_HANDSHAKE_BUFFER_MAX = %i bytes.", CWS_HANDSHAKE_BUFFER_MAX);
|
|
+ WS_DEBUG("client_read_handshake: handshake response too large. CWS_HANDSHAKE_BUFFER_MAX = %i bytes.", CWS_HANDSHAKE_BUFFER_MAX);
|
|
cwebsocket_client_onerror(websocket, "handshake response too large");
|
|
return -1;
|
|
}
|
|
@@ -414,22 +585,62 @@ int cwebsocket_client_read_handshake(cwebsocket_client *websocket, char *seckey)
|
|
memcpy(buf, data, tmplen);
|
|
buf[tmplen+1] = '\0';
|
|
|
|
- return cwebsocket_client_handshake_handler(websocket, buf, seckey);
|
|
+ if(flags & WEBSOCKET_FLAG_PROXY) {
|
|
+ return cwebsocket_proxy_client_handshake_handler(websocket, buf);
|
|
+ } else {
|
|
+ return cwebsocket_client_handshake_handler(websocket, buf, seckey);
|
|
+ }
|
|
+}
|
|
+
|
|
+int cwebsocket_proxy_client_handshake_handler(cwebsocket_client *websocket, const char *handshake_response) {
|
|
+ uint8_t flags = 0;
|
|
+ WS_DEBUG("proxy_client_handshake_handler: handshake response: \n%s\n", handshake_response);
|
|
+ char *ptr = NULL, *token = NULL;
|
|
+
|
|
+ /*
|
|
+ The response code from the proxy server allows only 200.
|
|
+ */
|
|
+ for(token = strtok((char *)handshake_response, "\r\n"); token != NULL; token = strtok(NULL, "\r\n")) {
|
|
+ if(*token == 'H' && *(token+1) == 'T' && *(token+2) == 'T' && *(token+3) == 'P') {
|
|
+ ptr = strchr(token, ' ');
|
|
+ ptr = strchr(ptr+1, ' ');
|
|
+ *ptr = '\0';
|
|
+ if((websocket->flags & WEBSOCKET_FLAG_PROXY) && (strcmp(token, "HTTP/1.1 200") == 0 || strcmp(token, "HTTP/1.0 200") == 0)) {
|
|
+ WS_DEBUG("client_handshake_handler: proxy handshake successful\n");
|
|
+ return 0;
|
|
+ }
|
|
+ } else {
|
|
+ ptr = strchr(token, ' ');
|
|
+ if(ptr == NULL) {
|
|
+ WS_DEBUG("client_handshake_handler: invalid HTTP header sent: %s\n", token);
|
|
+ cwebsocket_client_onerror(websocket, "invalid HTTP header sent");
|
|
+ return -1;
|
|
+ }
|
|
+ *ptr = '\0';
|
|
+ }
|
|
+ }
|
|
+ if(((flags & CWS_HANDSHAKE_HAS_UPGRADE) == 0) || ((flags & CWS_HANDSHAKE_HAS_CONNECTION) == 0) ||
|
|
+ ((flags & CWS_HANDSHAKE_HAS_ACCEPT) == 0)) {
|
|
+ // TODO send http error code (500?)
|
|
+ cwebsocket_client_close(websocket, 1002, "invalid websocket HTTP headers");
|
|
+ return -1;
|
|
+ }
|
|
+ WS_DEBUG("client_handshake_handler: handshake successful\n");
|
|
+ return 0;
|
|
}
|
|
|
|
void cwebsocket_client_listen(cwebsocket_client *websocket) {
|
|
while(websocket->state & WEBSOCKET_STATE_OPEN) {
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_listen: calling cwebsocket_client_read_data");
|
|
+ WS_DEBUG("client_listen: calling cwebsocket_client_read_data");
|
|
cwebsocket_client_read_data(websocket);
|
|
}
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_listen: shutting down");
|
|
+ WS_DEBUG("client_listen: shutting down");
|
|
}
|
|
|
|
#ifdef ENABLE_THREADS
|
|
void *cwebsocket_client_onmessage_thread(void *ptr) {
|
|
cwebsocket_client_thread_args *args = (cwebsocket_client_thread_args *)ptr;
|
|
cwebsocket_client_onmessage(args->socket, args->message);
|
|
- //free(args->message->payload);
|
|
free(args->message);
|
|
free(ptr);
|
|
return NULL;
|
|
@@ -452,28 +663,23 @@ int cwebsocket_client_send_control_frame(cwebsocket_client *websocket, opcode co
|
|
control_frame[4] = masking_key[2];
|
|
control_frame[5] = masking_key[3];
|
|
if(code & CLOSE) {
|
|
- uint16_t close_code = 1000;
|
|
if(payload_len >= 2) {
|
|
if(payload_len > 2) {
|
|
- char parsed_payload[payload_len];
|
|
- memcpy(parsed_payload, &payload[0], payload_len);
|
|
- parsed_payload[payload_len] = '\0';
|
|
- close_code = (control_frame[6] << 8) + control_frame[7];
|
|
int i;
|
|
for(i=0; i<payload_len; i++) {
|
|
- control_frame[6+i] = (parsed_payload[i] ^ masking_key[i % 4]) & 0xff;
|
|
+ control_frame[header_len+i] = (payload[i] ^ masking_key[i % 4]) & 0xff;
|
|
}
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_send_control_frame: opcode=%#04x, frame_type=%s, payload_len=%i, code=%i, payload=%s",
|
|
- code, frame_type, payload_len, close_code, parsed_payload);
|
|
+ WS_DEBUG("client_send_control_frame: opcode=%#04x, frame_type=%s, payload_len=%i, payload=%s\n",
|
|
+ code, frame_type, payload_len, payload + 2);
|
|
}
|
|
else {
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_send_control_frame: opcode=%#04x, frame_type=%s, payload_len=%i, code=%i, payload=(null)",
|
|
- code, frame_type, payload_len, close_code);
|
|
+ WS_DEBUG("client_send_control_frame: opcode=%#04x, frame_type=%s, payload_len=%i, payload=(null)\n",
|
|
+ code, frame_type, payload_len);
|
|
}
|
|
}
|
|
else {
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_send_control_frame: opcode=%#04x, frame_type=%s, payload_len=%i, code=%i, payload=(null)",
|
|
- code, frame_type, payload_len, close_code);
|
|
+ WS_DEBUG("client_send_control_frame: opcode=%#04x, frame_type=%s, payload_len=%i, payload=(null)\n",
|
|
+ code, frame_type, payload_len);
|
|
}
|
|
}
|
|
else {
|
|
@@ -484,16 +690,15 @@ int cwebsocket_client_send_control_frame(cwebsocket_client *websocket, opcode co
|
|
}
|
|
bytes_written = cwebsocket_client_write(websocket, control_frame, frame_len);
|
|
if(bytes_written == 0) {
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_send_control_frame: remote host closed the connection");
|
|
+ WS_DEBUG("client_send_control_frame: remote host closed the connection\n");
|
|
return 0;
|
|
}
|
|
else if(bytes_written == -1) {
|
|
- syslog(LOG_CRIT, "cwebsocket_client_send_control_frame: error sending %s control frame. %s", frame_type, strerror(errno));
|
|
- cwebsocket_client_onerror(websocket, strerror(errno));
|
|
+ WS_DEBUG("client_send_control_frame: error sending control frame\n");
|
|
return -1;
|
|
}
|
|
else {
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_send_control_frame: wrote %zd byte %s frame", bytes_written, frame_type);
|
|
+ WS_DEBUG("client_send_control_frame: wrote %zd byte\n", bytes_written);
|
|
}
|
|
return bytes_written;
|
|
}
|
|
@@ -508,8 +713,9 @@ int cwebsocket_client_read_data(cwebsocket_client *websocket) {
|
|
|
|
uint8_t *data = malloc(CWS_DATA_BUFFER_MAX);
|
|
if(data == NULL) {
|
|
- perror("out of memory");
|
|
- exit(-1);
|
|
+ WS_DEBUG("client_read_data: data out of memory");
|
|
+ cwebsocket_client_close(websocket, 1009, "out of memory");
|
|
+ return -1;
|
|
}
|
|
memset(data, 0, CWS_DATA_BUFFER_MAX);
|
|
|
|
@@ -520,9 +726,10 @@ int cwebsocket_client_read_data(cwebsocket_client *websocket) {
|
|
while(bytes_read < frame_size && (websocket->state & WEBSOCKET_STATE_OPEN)) {
|
|
|
|
if(bytes_read >= CWS_DATA_BUFFER_MAX) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_read_data: frame too large. RECEIVE_BUFFER_MAX = %i bytes. bytes_read=%i, header_length=%i",
|
|
+ WS_DEBUG("client_read_data: frame too large. RECEIVE_BUFFER_MAX = %i bytes. bytes_read=%i, header_length=%i",
|
|
CWS_DATA_BUFFER_MAX, bytes_read, header_length);
|
|
cwebsocket_client_close(websocket, 1009, "frame too large");
|
|
+ free(data);
|
|
return -1;
|
|
}
|
|
|
|
@@ -532,11 +739,11 @@ int cwebsocket_client_read_data(cwebsocket_client *websocket) {
|
|
char *errmsg = "server closed the connection";
|
|
cwebsocket_client_onerror(websocket, errmsg);
|
|
cwebsocket_client_close(websocket, 1006, errmsg);
|
|
+ free(data);
|
|
return -1;
|
|
}
|
|
if(byte == -1) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_read_data: error reading frame: %s", strerror(errno));
|
|
- cwebsocket_client_onerror(websocket, strerror(errno));
|
|
+ free(data);
|
|
return -1;
|
|
}
|
|
bytes_read++;
|
|
@@ -553,8 +760,9 @@ int cwebsocket_client_read_data(cwebsocket_client *websocket) {
|
|
|
|
if(frame.mask == 1) {
|
|
const char *errmsg = "received masked frame from server";
|
|
- syslog(LOG_CRIT, "cwebsocket_client_read_data: %s", errmsg);
|
|
+ WS_DEBUG("client_read_data: %s", errmsg);
|
|
cwebsocket_client_onerror(websocket, errmsg);
|
|
+ free(data);
|
|
return -1;
|
|
}
|
|
|
|
@@ -596,113 +804,137 @@ int cwebsocket_client_read_data(cwebsocket_client *websocket) {
|
|
|
|
char *payload = malloc(sizeof(char) * payload_length+1);
|
|
if(payload == NULL) {
|
|
- perror("out of memory");
|
|
- exit(-1);
|
|
+ WS_DEBUG("client_read_data: payload out of memory");
|
|
+ cwebsocket_client_close(websocket, 1009, "out of memory");
|
|
+ free(data);
|
|
+ return -1;
|
|
}
|
|
memcpy(payload, &data[header_length], payload_length);
|
|
payload[payload_length] = '\0';
|
|
free(data);
|
|
+ data = NULL;
|
|
|
|
size_t utf8_code_points = 0;
|
|
if(utf8_count_code_points((uint8_t *)payload, &utf8_code_points)) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_read_data: received %lld byte malformed utf8 text payload: %s", payload_length, payload);
|
|
+ WS_DEBUG("client_read_data: received %lld byte malformed utf8 text payload: %s", payload_length, payload);
|
|
cwebsocket_client_onerror(websocket, "received malformed utf8 payload");
|
|
+ free(payload);
|
|
return -1;
|
|
}
|
|
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_read_data: received %lld byte text payload: %s", payload_length, payload);
|
|
+ WS_DEBUG("client_read_data: received %lld byte text payload: %s", payload_length, payload);
|
|
|
|
if(websocket->subprotocol != NULL && websocket->subprotocol->onmessage != NULL) {
|
|
|
|
#ifdef ENABLE_THREADS
|
|
- cwebsocket_message *message = malloc(sizeof(cwebsocket_message));
|
|
+ cwebsocket_dsp_message *message = malloc(sizeof(cwebsocket_dsp_message));
|
|
if(message == NULL) {
|
|
- perror("out of memory");
|
|
- exit(-1);
|
|
+ WS_DEBUG("client_read_data: text message out of memory");
|
|
+ cwebsocket_client_close(websocket, 1009, "out of memory");
|
|
+ free(payload);
|
|
+ return -1;
|
|
}
|
|
- memset(message, 0, sizeof(cwebsocket_message));
|
|
+ memset(message, 0, sizeof(cwebsocket_dsp_message));
|
|
message->opcode = frame.opcode;
|
|
- message->payload_len = frame.payload_len;
|
|
+ message->payload_len = payload_length;
|
|
message->payload = payload;
|
|
|
|
cwebsocket_client_thread_args *args = malloc(sizeof(cwebsocket_client_thread_args));
|
|
if(args == NULL) {
|
|
- perror("out of memory");
|
|
- exit(-1);
|
|
+ WS_DEBUG ("client_read_data: text args out of memory");
|
|
+ cwebsocket_client_close(websocket, 1009, "out of memory");
|
|
+ free(payload);
|
|
+ return -1;
|
|
}
|
|
memset(args, 0, sizeof(cwebsocket_client_thread_args));
|
|
args->socket = websocket;
|
|
args->message = message;
|
|
|
|
if(pthread_create(&websocket->thread, NULL, cwebsocket_client_onmessage_thread, (void *)args) == -1) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_read_data: %s", strerror(errno));
|
|
- cwebsocket_client_onerror(websocket, strerror(errno));
|
|
- return -1;
|
|
+ free(payload);
|
|
+ free(message);
|
|
+ free(args);
|
|
+ return -1;
|
|
}
|
|
+ free(payload);
|
|
return bytes_read;
|
|
#else
|
|
- cwebsocket_message message = {0};
|
|
+ cwebsocket_dsp_message message = {0};
|
|
message.opcode = frame.opcode;
|
|
- message.payload_len = frame.payload_len;
|
|
+ message.payload_len = payload_length;
|
|
message.payload = payload;
|
|
- cwebsocket_client_onmessage(websocket, &message);
|
|
- //free(payload);
|
|
- return bytes_read;
|
|
+ cwebsocket_client_onmessage(websocket, &message);
|
|
+ free(payload);
|
|
+ return bytes_read;
|
|
#endif
|
|
}
|
|
|
|
- syslog(LOG_WARNING, "cwebsocket_client_read_data: onmessage callback undefined");
|
|
+ WS_DEBUG("client_read_data: onmessage callback undefined\n");
|
|
+ free(payload);
|
|
return bytes_read;
|
|
}
|
|
else if(frame.fin && frame.opcode == BINARY_FRAME) {
|
|
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_read_data: received BINARY payload. bytes=%lld", payload_length);
|
|
+ WS_DEBUG("client_read_data: received BINARY payload. bytes=%lld\n", payload_length);
|
|
|
|
char *payload = malloc(sizeof(char) * payload_length);
|
|
if(payload == NULL) {
|
|
perror("out of memory");
|
|
+ free(data);
|
|
exit(-1);
|
|
}
|
|
memcpy(payload, &data[header_length], payload_length);
|
|
free(data);
|
|
+ data = NULL;
|
|
|
|
if(websocket->subprotocol->onmessage != NULL) {
|
|
|
|
#ifdef ENABLE_THREADS
|
|
- cwebsocket_message *message = malloc(sizeof(cwebsocket_message));
|
|
+ cwebsocket_dsp_message *message = malloc(sizeof(cwebsocket_dsp_message));
|
|
if(message == NULL) {
|
|
- perror("out of memory");
|
|
- exit(-1);
|
|
+ WS_DEBUG("client_read_data: binary message out of memory");
|
|
+ cwebsocket_client_close(websocket, 1009, "out of memory");
|
|
+ free(payload);
|
|
+ return -1;
|
|
}
|
|
message->opcode = frame.opcode;
|
|
- message->payload_len = frame.payload_len;
|
|
+ message->payload_len = payload_length;
|
|
message->payload = payload;
|
|
|
|
cwebsocket_client_thread_args *args = malloc(sizeof(cwebsocket_client_thread_args));
|
|
+ if(args == NULL) {
|
|
+ WS_DEBUG("client_read_data: binary args out of memory");
|
|
+ cwebsocket_client_close(websocket, 1009, "out of memory");
|
|
+ return -1;
|
|
+ }
|
|
args->socket = websocket;
|
|
args->message = message;
|
|
|
|
if(pthread_create(&websocket->thread, NULL, cwebsocket_client_onmessage_thread, (void *)args) == -1) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_read_data: %s", strerror(errno));
|
|
- cwebsocket_client_onerror(websocket, strerror(errno));
|
|
+ free(payload);
|
|
+ free(message);
|
|
+ free(args);
|
|
return -1;
|
|
}
|
|
+ free(payload);
|
|
return bytes_read;
|
|
#else
|
|
- cwebsocket_message message;
|
|
+ cwebsocket_dsp_message message;
|
|
message.opcode = frame.opcode;
|
|
- message.payload_len = frame.payload_len;
|
|
+ message.payload_len = payload_length;
|
|
message.payload = payload;
|
|
- websocket->subprotocol->onmessage(websocket, &message);
|
|
+ cwebsocket_client_onmessage(websocket, &message);
|
|
free(payload);
|
|
return bytes_read;
|
|
#endif
|
|
}
|
|
- syslog(LOG_WARNING, "cwebsocket_client_read_data: onmessage callback undefined");
|
|
+ WS_DEBUG("client_read_data: onmessage callback undefined");
|
|
+ free(payload);
|
|
return bytes_read;
|
|
}
|
|
else if(frame.opcode == CONTINUATION) {
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_read_data: received CONTINUATION opcode");
|
|
+ WS_DEBUG("client_read_data: onmessage callback undefined");
|
|
+ free(data);
|
|
return 0;
|
|
}
|
|
else if(frame.opcode == PING) {
|
|
@@ -713,20 +945,22 @@ int cwebsocket_client_read_data(cwebsocket_client *websocket) {
|
|
cwebsocket_client_close(websocket, 1002, "control frames must not exceed 125 bytes");
|
|
return -1;
|
|
}
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_read_data: received PING control frame");
|
|
+ WS_DEBUG("client_read_data: received PING control frame");
|
|
uint8_t payload[payload_length];
|
|
memcpy(payload, &data[header_length], payload_length);
|
|
payload[payload_length] = '\0';
|
|
free(data);
|
|
- return cwebsocket_client_send_control_frame(websocket, 0x0A, "PONG", payload, payload_length);
|
|
+ return cwebsocket_client_send_control_frame(websocket, PONG, "PONG", payload, payload_length);
|
|
}
|
|
else if(frame.opcode == PONG) {
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_read_data: received PONG control frame");
|
|
+ WS_DEBUG("client_read_data: received PONG control frame");
|
|
+ free(data);
|
|
return 0;
|
|
}
|
|
else if(frame.opcode == CLOSE) {
|
|
if(frame.payload_len > 125) {
|
|
cwebsocket_client_close(websocket, 1002, "control frames must not exceed 125 bytes");
|
|
+ free(data);
|
|
return -1;
|
|
}
|
|
int code = 0;
|
|
@@ -739,7 +973,7 @@ int cwebsocket_client_read_data(cwebsocket_client *websocket) {
|
|
memcpy(payload, &data[header_length], (payload_length) * sizeof(uint8_t));
|
|
payload[payload_length] = '\0';
|
|
free(data);
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_read_data: received CLOSE control frame. payload_length=%lld, code=%i, reason=%s", payload_length, code, payload);
|
|
+ WS_DEBUG("client_read_data: received CLOSE control frame. payload_length=%lld, code=%i, reason=%s", payload_length, code, payload);
|
|
cwebsocket_client_close(websocket, code, NULL);
|
|
return 0;
|
|
}
|
|
@@ -747,7 +981,7 @@ int cwebsocket_client_read_data(cwebsocket_client *websocket) {
|
|
free(data);
|
|
char closemsg[50];
|
|
sprintf(closemsg, "received unsupported opcode: %#04x", frame.opcode);
|
|
- syslog(LOG_ERR, "cwebsocket_client_read_data: %s", closemsg);
|
|
+ WS_DEBUG("client_read_data: %s", closemsg);
|
|
cwebsocket_print_frame(&frame);
|
|
cwebsocket_client_onerror(websocket, closemsg);
|
|
cwebsocket_client_close(websocket, 1002, closemsg);
|
|
@@ -756,25 +990,26 @@ int cwebsocket_client_read_data(cwebsocket_client *websocket) {
|
|
|
|
void cwebsocket_client_create_masking_key(uint8_t *masking_key) {
|
|
uint8_t mask_bit;
|
|
- struct timeval tv;
|
|
- gettimeofday(&tv, NULL);
|
|
- srand(tv.tv_usec * tv.tv_sec);
|
|
+ time_t Tick0 = 0;
|
|
+
|
|
+ time(&Tick0);
|
|
+ srand(Tick0);
|
|
mask_bit = rand();
|
|
memcpy(masking_key, &mask_bit, 4);
|
|
}
|
|
|
|
-ssize_t cwebsocket_client_write_data(cwebsocket_client *websocket, const char *data, uint64_t payload_len, opcode code) {
|
|
+int cwebsocket_client_write_data(cwebsocket_client *websocket, const char *data, uint64_t payload_len, opcode code) {
|
|
|
|
if((websocket->state & WEBSOCKET_STATE_OPEN) == 0) {
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_write_data: websocket closed");
|
|
+ WS_DEBUG("client_write_data: websocket closed");
|
|
cwebsocket_client_onerror(websocket, "websocket closed");
|
|
return -1;
|
|
}
|
|
|
|
- uint32_t header_length = 6 + (payload_len > 125 ? 2 : 0) + (payload_len > 0xffff ? 8 : 0);
|
|
+ uint32_t header_length = 6 + (payload_len > 125 ? 2 : 0) + (payload_len > 0xffff ? 6 : 0);
|
|
uint8_t masking_key[4];
|
|
uint8_t header[header_length];
|
|
- ssize_t bytes_written;
|
|
+ int bytes_written;
|
|
|
|
cwebsocket_client_create_masking_key(masking_key);
|
|
header[0] = (code | 0x80);
|
|
@@ -808,7 +1043,7 @@ ssize_t cwebsocket_client_write_data(cwebsocket_client *websocket, const char *d
|
|
header[13] = masking_key[3];
|
|
}
|
|
else {
|
|
- syslog(LOG_CRIT, "cwebsocket_client_write_data: frame too large");
|
|
+ WS_DEBUG("client_write_data: frame too large");
|
|
cwebsocket_client_close(websocket, 1009, "frame too large");
|
|
return -1;
|
|
}
|
|
@@ -827,74 +1062,86 @@ ssize_t cwebsocket_client_write_data(cwebsocket_client *websocket, const char *d
|
|
bytes_written = cwebsocket_client_write(websocket, framebuf, frame_length);
|
|
|
|
if(bytes_written == -1) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_write_data: error: %s", strerror(errno));
|
|
- cwebsocket_client_onerror(websocket, strerror(errno));
|
|
+ WS_DEBUG("client_write_data: error: %d", bytes_written);
|
|
return -1;
|
|
}
|
|
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_write_data: bytes_written=%zu, frame_length=%i, payload_len=%lld, payload=%s\n",
|
|
+ WS_DEBUG("client_write_data: bytes_written=%zu, frame_length=%i, payload_len=%lld, payload=%s\n",
|
|
bytes_written, frame_length, (long long)payload_len, data);
|
|
|
|
return bytes_written;
|
|
}
|
|
|
|
void cwebsocket_client_close(cwebsocket_client *websocket, uint16_t code, const char *message) {
|
|
+ int code32 = 0;
|
|
|
|
- if((websocket->state & WEBSOCKET_STATE_OPEN) == 0 || websocket->fd < 1) {
|
|
+ if(websocket->state == 0) {
|
|
return;
|
|
}
|
|
|
|
-#ifdef ENABLE_THREADS
|
|
- pthread_mutex_lock(&websocket->lock);
|
|
- websocket->state = WEBSOCKET_STATE_CLOSING;
|
|
- pthread_mutex_unlock(&websocket->lock);
|
|
+#ifdef ENABLE_SSL
|
|
+ if((websocket->state & WEBSOCKET_STATE_OPEN) != 0) {
|
|
#else
|
|
- websocket->state = WEBSOCKET_STATE_CLOSING;
|
|
+ if((websocket->state & WEBSOCKET_STATE_OPEN) != 0 && websocket->fd > 0) {
|
|
#endif
|
|
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_close: code=%i, message=%s\n", code, message);
|
|
+#ifdef ENABLE_THREADS
|
|
+ pthread_mutex_lock(&websocket->lock);
|
|
+ websocket->state = WEBSOCKET_STATE_CLOSING;
|
|
+ pthread_mutex_unlock(&websocket->lock);
|
|
+#else
|
|
+ websocket->state = WEBSOCKET_STATE_CLOSING;
|
|
+#endif
|
|
|
|
- int code32 = 0;
|
|
- if(code > 0) {
|
|
- code = code ? htons(code) : htons(1005);
|
|
- int message_len = (message == NULL) ? 0 : strlen(message) + 2;
|
|
- uint8_t close_frame[message_len];
|
|
- close_frame[0] = code & 0xFF;
|
|
- close_frame[1] = (code >> 8);
|
|
- code32 = (close_frame[0] << 8) + (close_frame[1]);
|
|
- int i;
|
|
- for(i=0; i<message_len; i++) {
|
|
- close_frame[i+2] = message[i];
|
|
+ WS_DEBUG("client_close: code=%i, message=%s\n", code, message);
|
|
+
|
|
+ if(code > 0) {
|
|
+ code = code ? htons(code) : htons(1005);
|
|
+ int message_len = (message == NULL) ? 2 : strlen(message) + 2;
|
|
+ uint8_t close_frame[message_len];
|
|
+ close_frame[0] = code & 0xFF;
|
|
+ close_frame[1] = (code >> 8);
|
|
+ code32 = (close_frame[0] << 8) + (close_frame[1]);
|
|
+ int i;
|
|
+ for(i=0; i<message_len; i++) {
|
|
+ close_frame[i+2] = message[i];
|
|
+ }
|
|
+ cwebsocket_client_send_control_frame(websocket, CLOSE, "CLOSE", close_frame, message_len);
|
|
+ }
|
|
+ else {
|
|
+ cwebsocket_client_send_control_frame(websocket, CLOSE, "CLOSE", NULL, 0);
|
|
}
|
|
- cwebsocket_client_send_control_frame(websocket, CLOSE, "CLOSE", close_frame, message_len);
|
|
- }
|
|
- else {
|
|
- cwebsocket_client_send_control_frame(websocket, CLOSE, "CLOSE", NULL, 0);
|
|
}
|
|
|
|
#ifdef ENABLE_SSL
|
|
- if(websocket->ssl != NULL) {
|
|
- SSL_shutdown(websocket->ssl);
|
|
- SSL_free(websocket->ssl);
|
|
- }
|
|
- if(websocket->sslctx != NULL) {
|
|
- SSL_CTX_free(websocket->sslctx);
|
|
- }
|
|
-#else
|
|
- if(shutdown(websocket->fd, SHUT_WR) == -1) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_close: unable to shutdown websocket: %s", strerror(errno));
|
|
- }
|
|
- char buf[1];
|
|
- while(read(websocket->fd, buf, 1) > 0) { buf[0] = '\0'; }
|
|
- if(close(websocket->fd) == -1) {
|
|
- syslog(LOG_ERR, "cwebsocket_client_close: error closing websocket: %s\n", strerror(errno));
|
|
- cwebsocket_client_onclose(websocket, 1011, strerror(errno));
|
|
+ if(websocket->flags & WEBSOCKET_FLAG_SSL) {
|
|
+ WS_DEBUG("ssl_client_close: code=%i, message=%s\n", code, message);
|
|
+
|
|
+ mbedtls_ssl_close_notify( &websocket->ssl );
|
|
+
|
|
+ mbedtls_net_free( &websocket->ssl_net_ctx );
|
|
+ mbedtls_x509_crt_free( &websocket->cacert );
|
|
+ mbedtls_ssl_free( &websocket->ssl );
|
|
+ mbedtls_ssl_config_free( &websocket->conf );
|
|
+ mbedtls_ctr_drbg_free( &websocket->ctr_drbg );
|
|
+ mbedtls_entropy_free( &websocket->entropy );
|
|
+ }else {
|
|
+#endif
|
|
+ if(shutdown(websocket->fd, SHUT_WR) == -1) {
|
|
+ WS_DEBUG("cwebsocket_client_close: unable to shutdown websocket: %s", strerror(errno));
|
|
+ }
|
|
+ char buf[1];
|
|
+ while(read(websocket->fd, buf, 1) > 0) { buf[0] = '\0'; }
|
|
+ if(close(websocket->fd) == -1) {
|
|
+ WS_DEBUG("cwebsocket_client_close: error closing websocket: %s\n", strerror(errno));
|
|
+ }
|
|
+#ifdef ENABLE_SSL
|
|
}
|
|
- websocket->fd = 0;
|
|
#endif
|
|
-
|
|
cwebsocket_client_onclose(websocket, code32, message);
|
|
|
|
+ websocket->fd = 0;
|
|
+
|
|
#ifdef ENABLE_THREADS
|
|
pthread_mutex_lock(&websocket->lock);
|
|
websocket->state = WEBSOCKET_STATE_CLOSED;
|
|
@@ -904,7 +1151,6 @@ void cwebsocket_client_close(cwebsocket_client *websocket, uint16_t code, const
|
|
websocket->state = WEBSOCKET_STATE_CLOSED;
|
|
#endif
|
|
|
|
- syslog(LOG_DEBUG, "cwebsocket_client_close: websocket closed\n");
|
|
websocket->state = 0;
|
|
|
|
if(websocket->flags & WEBSOCKET_FLAG_AUTORECONNECT) {
|
|
@@ -912,33 +1158,33 @@ void cwebsocket_client_close(cwebsocket_client *websocket, uint16_t code, const
|
|
}
|
|
}
|
|
|
|
-ssize_t inline cwebsocket_client_read(cwebsocket_client *websocket, void *buf, int len) {
|
|
+ssize_t cwebsocket_client_read(cwebsocket_client *websocket, void *buf, int len) {
|
|
#ifdef ENABLE_SSL
|
|
return (websocket->flags & WEBSOCKET_FLAG_SSL) ?
|
|
- SSL_read(websocket->ssl, buf, len) :
|
|
+ mbedtls_ssl_read(&websocket->ssl, buf, len ) :
|
|
read(websocket->fd, buf, len);
|
|
#else
|
|
return read(websocket->fd, buf, len);
|
|
#endif
|
|
}
|
|
|
|
-ssize_t inline cwebsocket_client_write(cwebsocket_client *websocket, void *buf, int len) {
|
|
+ssize_t cwebsocket_client_write(cwebsocket_client *websocket, void *buf, int len) {
|
|
#ifdef ENABLE_THREADS
|
|
ssize_t bytes_written;
|
|
pthread_mutex_lock(&websocket->write_lock);
|
|
- #ifdef USESSL
|
|
- bytes_written = (websocket->flags & WEBSOCKET_FLAG_SSL) ?
|
|
- SSL_write(websocket->ssl, buf, len) :
|
|
- write(websocket->fd, buf, len);
|
|
- #else
|
|
- bytes_written = write(websocket->fd, buf, len);
|
|
- #endif
|
|
+#ifdef ENABLE_SSL
|
|
+ bytes_written = (websocket->flags & WEBSOCKET_FLAG_SSL) ?
|
|
+ mbedtls_ssl_write(&websocket->ssl, buf, len ) :
|
|
+ write(websocket->fd, buf, len);
|
|
+#else
|
|
+ bytes_written = write(websocket->fd, buf, len);
|
|
+#endif
|
|
pthread_mutex_unlock(&websocket->write_lock);
|
|
return bytes_written;
|
|
#else
|
|
#ifdef ENABLE_SSL
|
|
return (websocket->flags & WEBSOCKET_FLAG_SSL) ?
|
|
- SSL_write(websocket->ssl, buf, len) :
|
|
+ mbedtls_ssl_write(&websocket->ssl, buf, len) :
|
|
write(websocket->fd, buf, len);
|
|
#else
|
|
return write(websocket->fd, buf, len);
|
|
@@ -952,20 +1198,41 @@ void cwebsocket_client_onopen(cwebsocket_client *websocket) {
|
|
}
|
|
}
|
|
|
|
-void cwebsocket_client_onmessage(cwebsocket_client *websocket, cwebsocket_message *message) {
|
|
+void cwebsocket_client_onmessage(cwebsocket_client *websocket, cwebsocket_dsp_message *message) {
|
|
if(websocket->subprotocol != NULL && websocket->subprotocol->onmessage != NULL) {
|
|
- websocket->subprotocol->onmessage(websocket, message);
|
|
+ uint64_t len;
|
|
+ websocket->message.opcode = message->opcode;
|
|
+
|
|
+ for (len = 0; len < message->payload_len; len += MAX_CHUNK_SIZE){
|
|
+ if (len + MAX_CHUNK_SIZE > message->payload_len){
|
|
+ websocket->message.chunk_len = (message->payload_len - len);
|
|
+ memcpy(websocket->message.payload, &message->payload[len], (message->payload_len - len));
|
|
+ websocket->message.payload[(message->payload_len - len)] = '\0';
|
|
+ } else {
|
|
+ websocket->message.chunk_len = MAX_CHUNK_SIZE;
|
|
+ memcpy(websocket->message.payload, &message->payload[len], MAX_CHUNK_SIZE);
|
|
+ websocket->message.payload[MAX_CHUNK_SIZE] = '\0';
|
|
+ }
|
|
+ websocket->message.chunk_pos = len;
|
|
+ websocket->message.payload_len = message->payload_len;
|
|
+ websocket->subprotocol->onmessage(websocket);
|
|
+ }
|
|
}
|
|
}
|
|
|
|
void cwebsocket_client_onclose(cwebsocket_client *websocket, int code, const char *message) {
|
|
if(websocket->subprotocol != NULL && websocket->subprotocol->onclose != NULL) {
|
|
- websocket->subprotocol->onclose(websocket, code, message);
|
|
+ strncpy(websocket->message.payload, message, MAX_CHUNK_SIZE);
|
|
+ websocket->code = code;
|
|
+ websocket->message.payload_len = strlen(message);
|
|
+ websocket->subprotocol->onclose(websocket);
|
|
}
|
|
}
|
|
|
|
void cwebsocket_client_onerror(cwebsocket_client *websocket, const char *error) {
|
|
if(websocket->subprotocol != NULL && websocket->subprotocol->onerror != NULL) {
|
|
- websocket->subprotocol->onerror(websocket, error);
|
|
+ strncpy(websocket->message.payload, error, MAX_CHUNK_SIZE);
|
|
+ websocket->message.payload_len = strlen(error);
|
|
+ websocket->subprotocol->onerror(websocket);
|
|
}
|
|
}
|
|
diff --git a/cwebsocket/src/cwebsocket/client.h b/cwebsocket/src/cwebsocket/client.h
|
|
index 0c4d57c..d11dc0b 100755
|
|
--- a/cwebsocket/src/cwebsocket/client.h
|
|
+++ b/cwebsocket/src/cwebsocket/client.h
|
|
@@ -26,65 +26,212 @@
|
|
#define CWEBSOCKET_CLIENT_H
|
|
|
|
#include <time.h>
|
|
+#include <sys/time.h>
|
|
+#include <strings.h>
|
|
#include <ctype.h>
|
|
-#include <netdb.h>
|
|
-#include <netinet/in.h>
|
|
-#include <sys/resource.h>
|
|
+#include <errno.h>
|
|
+#include "sys/socket.h"
|
|
+#include "netdb.h"
|
|
+#include "arpa/inet.h"
|
|
#include "common.h"
|
|
|
|
+#ifdef ENABLE_THREADS
|
|
+#include "pthread.h"
|
|
+#endif
|
|
+
|
|
+/**
|
|
+ * @file client.h
|
|
+ * @brief WebSocket API
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * @ingroup lteiftop
|
|
+ * @defgroup netwebsocket WebSocket
|
|
+ * @brief WebSocket interface
|
|
+ * @{
|
|
+ */
|
|
+
|
|
#define WEBSOCKET_FLAG_AUTORECONNECT (1 << 1)
|
|
+#define WEBSOCKET_FLAG_PROXY (1 << 5)
|
|
+
|
|
+#define WEBSOCKET_SUBPROTOCOL_MAX 4
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
+/**
|
|
+ * @defgroup websockcomstr Structure for common interface
|
|
+ * Data structure for common interface
|
|
+ * @{
|
|
+ */
|
|
+
|
|
typedef struct _cwebsocket {
|
|
int fd;
|
|
int retry;
|
|
char *uri;
|
|
uint8_t flags;
|
|
uint8_t state;
|
|
+ char *proxy_addr;
|
|
+ char *proxy_port;
|
|
+ char **headers;
|
|
+ int num_headers;
|
|
#ifdef ENABLE_SSL
|
|
- SSL_CTX *sslctx;
|
|
- SSL *ssl;
|
|
+ mbedtls_ssl_context ssl;
|
|
+ mbedtls_net_context ssl_net_ctx;
|
|
+ mbedtls_ssl_config conf;
|
|
+ mbedtls_x509_crt cacert;
|
|
+ mbedtls_x509_crt clicert;
|
|
+ mbedtls_pk_context pkey;
|
|
+ mbedtls_entropy_context entropy;
|
|
+ mbedtls_ctr_drbg_context ctr_drbg;
|
|
#endif
|
|
#ifdef ENABLE_THREADS
|
|
pthread_t thread;
|
|
pthread_mutex_t lock;
|
|
pthread_mutex_t write_lock;
|
|
#endif
|
|
+ cwebsocket_app_message message;
|
|
+ int code;
|
|
size_t subprotocol_len;
|
|
cwebsocket_subprotocol *subprotocol;
|
|
- cwebsocket_subprotocol *subprotocols[];
|
|
+ cwebsocket_subprotocol *subprotocols[WEBSOCKET_SUBPROTOCOL_MAX];
|
|
} cwebsocket_client;
|
|
|
|
typedef struct {
|
|
cwebsocket_client *socket;
|
|
- cwebsocket_message *message;
|
|
+ cwebsocket_dsp_message *message;
|
|
} cwebsocket_client_thread_args;
|
|
|
|
// "public"
|
|
-void cwebsocket_client_init(cwebsocket_client *websocket, cwebsocket_subprotocol *subprotocols[], int subprotocol_len);
|
|
+
|
|
+/**
|
|
+ * @brief Initialized the client connection on a websocket
|
|
+ *
|
|
+ * @param[in] websocket : Data structure for common interface
|
|
+ * @param[in] subprotocols : The array information for sub protocol
|
|
+ * @param[in] subprotocol_len : Actual size of the subprotocol.
|
|
+ *
|
|
+ * @retval On success, 0 is returned. On error, -1 is returned.
|
|
+ *
|
|
+ * @detail Initialized the client connection on a websocket
|
|
+ *
|
|
+ */
|
|
+int cwebsocket_client_init(cwebsocket_client *websocket, cwebsocket_subprotocol **subprotocols, int subprotocol_len);
|
|
+
|
|
+/**
|
|
+ * @brief Initialized the ssl information on a websocket
|
|
+ *
|
|
+ * @param[in] websocket : Data structure for common interface
|
|
+ * @param[in] cert_name : Certificate name in file system
|
|
+ * @param[in] pers : Personalization data (Device specific identifiers)
|
|
+ * (Can be NULL)
|
|
+ *
|
|
+ * @retval On success, 0 is returned. On error, -1 is returned.
|
|
+ *
|
|
+ * @detail Initialized the ssl information on a websocket
|
|
+ *
|
|
+ */
|
|
+int cwebsocket_client_ssl_init(cwebsocket_client *websocket, char *cert_name, char *cli_cert, char *cli_key, char *passphrase);
|
|
+
|
|
+/**
|
|
+ * @brief Conneted to the websocket server
|
|
+ *
|
|
+ * @param[in] websocket : Data structure for common interface.(Set the server name to the uri)
|
|
+ *
|
|
+ * @retval On success, 0 is returned. On error, -1 is returned.
|
|
+ *
|
|
+ * @detail Conneted to the websocket server
|
|
+ *
|
|
+ */
|
|
int cwebsocket_client_connect(cwebsocket_client *websocket);
|
|
+/**
|
|
+ * @brief Read the packet data from the websocket server
|
|
+ *
|
|
+ * @param[in] websocket : Data structure for common interface.
|
|
+ *
|
|
+ * @retval On success, Read() shall return the length of the message in bytes. On error, -1 is returned.
|
|
+ * On finish, 0 is returne.
|
|
+ *
|
|
+ * @detail Read the packet data from the websocket server
|
|
+ *
|
|
+ */
|
|
int cwebsocket_client_read_data(cwebsocket_client *websocket);
|
|
-ssize_t cwebsocket_client_write_data(cwebsocket_client *websocket, const char *data, uint64_t len, opcode code);
|
|
-void cwebsocket_client_run(cwebsocket_client *websocket);
|
|
+
|
|
+/**
|
|
+ * @brief Wrote the packet data to the websocket server
|
|
+ *
|
|
+ * @param[in] websocket : Data structure for common interface.
|
|
+ *
|
|
+ * @retval On success, Write() shall return the length of the message in bytes. On error, -1 is returned.
|
|
+ *
|
|
+ * @detail Wrote the packet data to the websocket server
|
|
+ *
|
|
+ */
|
|
+int cwebsocket_client_write_data(cwebsocket_client *websocket, const char *data, uint64_t len, opcode code);
|
|
+
|
|
+/**
|
|
+ * @brief Closed the websocket session to server
|
|
+ *
|
|
+ * @param[in] websocket : Data structure for common interface.
|
|
+ * @param[in] code : Closed code. (Refer to chapter 7.4 of RFC6455.)
|
|
+ * @param[in] reason : Closed reason.
|
|
+ *
|
|
+ * @retval Nothing
|
|
+ *
|
|
+ * @detail Closed the websocket session to server
|
|
+ *
|
|
+ */
|
|
+
|
|
void cwebsocket_client_close(cwebsocket_client *websocket, uint16_t code, const char *reason);
|
|
-void cwebsocket_client_listen(cwebsocket_client *websocket);
|
|
+
|
|
+/**
|
|
+ * @brief Set the proxy address for websocket
|
|
+ *
|
|
+ * @param[in] websocket : Data structure for common interface.
|
|
+ * @param[in] proxy_addr : Proxy address.
|
|
+ * @param[in] proxy_port : Proxy port.
|
|
+ *
|
|
+ * @retval Nothing
|
|
+ *
|
|
+ * @detail Set the proxy address for websocket
|
|
+ *
|
|
+ */
|
|
+void cwebsocket_client_set_proxy(cwebsocket_client *websocket, char *proxy_addr, char *proxy_port);
|
|
+
|
|
+/**
|
|
+ * @brief Released the proxy features for websocket
|
|
+ *
|
|
+ * @param[in] websocket : Data structure for common interface.
|
|
+ *
|
|
+ * @retval Nothing
|
|
+ *
|
|
+ * @detail Released the proxy address for websocket
|
|
+ *
|
|
+ */
|
|
+void cwebsocket_client_unset_proxy(cwebsocket_client *websocket);
|
|
|
|
// "private"
|
|
+void cwebsocket_client_run(cwebsocket_client *websocket);
|
|
+void cwebsocket_client_listen(cwebsocket_client *websocket);
|
|
void cwebsocket_client_parse_uri(cwebsocket_client *websocket, const char *uri, char *hostname, char *port, char *resource, char *querystring);
|
|
int cwebsocket_client_handshake_handler(cwebsocket_client *websocket, const char *handshake_response, char *seckey);
|
|
-int cwebsocket_client_read_handshake(cwebsocket_client *websocket, char *seckey);
|
|
+int cwebsocket_client_read_handshake(cwebsocket_client *websocket, char *seckey, int flags);
|
|
int cwebsocket_client_send_control_frame(cwebsocket_client *websocket, opcode opcode, const char *frame_type, uint8_t *payload, int payload_len);
|
|
void cwebsocket_client_create_masking_key(uint8_t *masking_key);
|
|
-ssize_t inline cwebsocket_client_read(cwebsocket_client *websocket, void *buf, int len);
|
|
-ssize_t inline cwebsocket_client_write(cwebsocket_client *websocket, void *buf, int len);
|
|
+int cwebsocket_client_read(cwebsocket_client *websocket, void *buf, int len);
|
|
+int cwebsocket_client_write(cwebsocket_client *websocket, void *buf, int len);
|
|
+
|
|
void cwebsocket_client_onopen(cwebsocket_client *websocket);
|
|
-void cwebsocket_client_onmessage(cwebsocket_client *websocket, cwebsocket_message *message);
|
|
+void cwebsocket_client_onmessage(cwebsocket_client *websocket, cwebsocket_dsp_message *message);
|
|
void cwebsocket_client_onclose(cwebsocket_client *websocket, int code, const char *message);
|
|
void cwebsocket_client_onerror(cwebsocket_client *websocket, const char *error);
|
|
|
|
+ssize_t cwebsocket_proxy_client_write(int fd, void *buf, int len);
|
|
+int cwebsocket_proxy_client_handshake_handler(cwebsocket_client *websocket, const char *handshake_response);
|
|
+int cwebsocket_proxy_client_read_handshake(cwebsocket_client *websocket);
|
|
+/**@}*/
|
|
+
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
diff --git a/cwebsocket/src/cwebsocket/common.c b/cwebsocket/src/cwebsocket/common.c
|
|
index 931bb17..e271ae1 100644
|
|
--- a/cwebsocket/src/cwebsocket/common.c
|
|
+++ b/cwebsocket/src/cwebsocket/common.c
|
|
@@ -23,25 +23,26 @@
|
|
*/
|
|
|
|
#include "common.h"
|
|
+#include "mbedtls/base64.h"
|
|
+#include "mbedtls/sha1.h"
|
|
+
|
|
|
|
char* cwebsocket_base64_encode(const unsigned char *input, int length) {
|
|
- BIO *bmem, *b64;
|
|
- BUF_MEM *bptr;
|
|
- b64 = BIO_new(BIO_f_base64());
|
|
- bmem = BIO_new(BIO_s_mem());
|
|
- b64 = BIO_push(b64, bmem);
|
|
- BIO_write(b64, input, length);
|
|
- BIO_flush(b64);
|
|
- BIO_get_mem_ptr(b64, &bptr);
|
|
- char *buff = (char *)malloc(bptr->length);
|
|
- memcpy(buff, bptr->data, bptr->length-1);
|
|
- buff[bptr->length-1] = '\0';
|
|
- BIO_free_all(b64);
|
|
+
|
|
+ unsigned char buffer[128];
|
|
+ size_t olen;
|
|
+
|
|
+ mbedtls_base64_encode(buffer, sizeof( buffer ), &olen, input, length );
|
|
+
|
|
+ char *buff = (char *)malloc(olen);
|
|
+ memcpy(buff, buffer, olen-1);
|
|
+ buff[olen-1] = '\0';
|
|
+
|
|
return buff;
|
|
}
|
|
|
|
void cwebsocket_print_frame(cwebsocket_frame *frame) {
|
|
- syslog(LOG_DEBUG, "cwebsocket_print_frame: fin=%i, rsv1=%i, rsv2=%i, rsv3=%i, opcode=%#04x, mask=%i, payload_len=%lld\n",
|
|
+ WS_DEBUG("print_frame: fin=%i, rsv1=%i, rsv2=%i, rsv3=%i, opcode=%#04x, mask=%i, payload_len=%lld\n",
|
|
frame->fin, frame->rsv1, frame->rsv2, frame->rsv3, frame->opcode, frame->mask, frame->payload_len);
|
|
}
|
|
|
|
@@ -53,6 +54,6 @@ char* cwebsocket_create_key_challenge_response(const char *seckey) {
|
|
memcpy(sha1buf, seckey, seckey_len);
|
|
memcpy(&sha1buf[seckey_len], GUID, 36);
|
|
unsigned char sha1_bytes[20];
|
|
- SHA1((const unsigned char *)sha1buf, total_len, sha1_bytes);
|
|
+ mbedtls_sha1((const unsigned char *)sha1buf, total_len, sha1_bytes);
|
|
return cwebsocket_base64_encode((const unsigned char *)sha1_bytes, sizeof(sha1_bytes));
|
|
}
|
|
diff --git a/cwebsocket/src/cwebsocket/common.h b/cwebsocket/src/cwebsocket/common.h
|
|
index d6d28c3..faeb8ff 100644
|
|
--- a/cwebsocket/src/cwebsocket/common.h
|
|
+++ b/cwebsocket/src/cwebsocket/common.h
|
|
@@ -28,31 +28,36 @@
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
-#include <syslog.h>
|
|
#include <string.h>
|
|
-#include <errno.h>
|
|
-#include <pthread.h>
|
|
-#include <unistd.h>
|
|
-#include <fcntl.h>
|
|
-#include <sys/time.h>
|
|
-#include <sys/types.h>
|
|
-#include <sys/socket.h>
|
|
-#include <arpa/inet.h>
|
|
-#include <openssl/sha.h>
|
|
-#include <openssl/hmac.h>
|
|
-#include <openssl/evp.h>
|
|
-#include <openssl/bio.h>
|
|
-#include <openssl/buffer.h>
|
|
+
|
|
#include "utf8.h"
|
|
|
|
+#include "sys/socket.h"
|
|
+
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "../../config.h"
|
|
#endif
|
|
|
|
+//#define ENABLE_WEBSOCKET_DEBUG
|
|
+
|
|
+#ifdef ENABLE_WEBSOCKET_DEBUG
|
|
+#define WS_DEBUG(...) printf(__VA_ARGS__)
|
|
+#else
|
|
+#define WS_DEBUG(...)
|
|
+#endif
|
|
+
|
|
+//#define ENABLE_SSL
|
|
+
|
|
#ifdef ENABLE_SSL
|
|
- #include <openssl/rand.h>
|
|
- #include <openssl/ssl.h>
|
|
- #include <openssl/err.h>
|
|
+#include "mbedtls/config.h"
|
|
+#include "mbedtls/ssl.h"
|
|
+#include "mbedtls/net.h"
|
|
+#include "mbedtls/entropy.h"
|
|
+#include "mbedtls/ctr_drbg.h"
|
|
+#include "mbedtls/error.h"
|
|
+#if defined(MBEDTLS_SSL_CACHE_C)
|
|
+#include "mbedtls/ssl_cache.h"
|
|
+#endif
|
|
#endif
|
|
|
|
#if defined(__linux__)
|
|
@@ -72,11 +77,11 @@
|
|
(char)(((p & ((uint64_t)0xff << 48)) >> 48) & 0xff), (char)(((p & ((uint64_t)0xff << 56)) >> 56) & 0xff) }
|
|
|
|
#ifndef CWS_HANDSHAKE_BUFFER_MAX
|
|
- #define CWS_HANDSHAKE_BUFFER_MAX 256 // bytes
|
|
+ #define CWS_HANDSHAKE_BUFFER_MAX 1024 // bytes
|
|
#endif
|
|
|
|
#ifndef CWS_DATA_BUFFER_MAX
|
|
- #define CWS_DATA_BUFFER_MAX 65543 // bytes
|
|
+ #define CWS_DATA_BUFFER_MAX 4096 // bytes
|
|
#endif
|
|
|
|
#ifndef CWS_STACK_SIZE_MIN
|
|
@@ -103,10 +108,9 @@
|
|
extern "C" {
|
|
#endif
|
|
|
|
-typedef enum {
|
|
- TRUE,
|
|
- FALSE
|
|
-} bool;
|
|
+#define MAX_CHUNK_SIZE 256
|
|
+
|
|
+typedef int ssize_t;
|
|
|
|
typedef enum {
|
|
CONTINUATION = 0x00,
|
|
@@ -132,20 +136,30 @@ typedef struct {
|
|
uint32_t opcode;
|
|
uint64_t payload_len;
|
|
char *payload;
|
|
-} cwebsocket_message;
|
|
+} cwebsocket_dsp_message;
|
|
+
|
|
+typedef struct {
|
|
+ uint32_t opcode;
|
|
+ uint64_t payload_len;
|
|
+ uint64_t chunk_len;
|
|
+ uint64_t chunk_pos;
|
|
+ char payload[MAX_CHUNK_SIZE + 1];
|
|
+} cwebsocket_app_message;
|
|
|
|
typedef struct {
|
|
char *name;
|
|
void (*onopen)(void *arg);
|
|
- void (*onmessage)(void *arg, cwebsocket_message *message);
|
|
- void (*onclose)(void *arg, int code, const char *message);
|
|
- void (*onerror)(void *arg, const char *error);
|
|
+ void (*onmessage)(void *arg);
|
|
+ void (*onclose)(void *arg);
|
|
+ void (*onerror)(void *arg);
|
|
} cwebsocket_subprotocol;
|
|
|
|
char* cwebsocket_create_key_challenge_response(const char *seckey);
|
|
char* cwebsocket_base64_encode(const unsigned char *input, int length);
|
|
void cwebsocket_print_frame(cwebsocket_frame *frame);
|
|
|
|
+void ws_thread_new( const char *pcName, void( *pxThread )( void *pvParameters ), void *pvArg, int iStackSize, int iPriority);
|
|
+
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
--
|
|
2.37.1
|
|
|