| 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 |
| |