diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/ws.c b/libs/sofia-sip/libsofia-sip-ua/tport/ws.c index cdc211e86b..98c00032cc 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/ws.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/ws.c @@ -318,17 +318,26 @@ int ws_handshake(wsh_t *wsh) ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes) { ssize_t r; - int x = 0; + int err = 0; if (wsh->ssl) { do { r = SSL_read(wsh->ssl, data, bytes); #ifndef _MSC_VER - if (x++) usleep(10000); + if (wsh->x++) usleep(10000); #else - if (x++) Sleep(10); + if (wsh->x++) Sleep(10); #endif - } while (r == -1 && SSL_get_error(wsh->ssl, r) == SSL_ERROR_WANT_READ && x < 100); + if (r == -1) { + err = SSL_get_error(wsh->ssl, r); + + if (wsh->handshake && err == SSL_ERROR_WANT_READ) { + r = -2; + goto end; + } + } + + } while (r == -1 && err == SSL_ERROR_WANT_READ && wsh->x < 100); goto end; } @@ -336,13 +345,13 @@ ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes) do { r = recv(wsh->sock, data, bytes, 0); #ifndef _MSC_VER - if (x++) usleep(10000); + if (wsh->x++) usleep(10000); #else - if (x++) Sleep(10); + if (wsh->x++) Sleep(10); #endif - } while (r == -1 && xp_is_blocking(xp_errno()) && x < 100); + } while (r == -1 && xp_is_blocking(xp_errno()) && wsh->x < 100); - if (x >= 100) { + if (wsh->x >= 100) { r = -1; } @@ -352,6 +361,10 @@ ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes) *((char *)data + r) = '\0'; } + if (r >= 0) { + wsh->x = 0; + } + return r; } @@ -621,6 +634,9 @@ ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data) } if ((wsh->datalen = ws_raw_read(wsh, wsh->buffer, 9)) < 0) { + if (wsh->datalen == -2) { + return -2; + } return ws_close(wsh, WS_PROTO_ERR); } diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/ws.h b/libs/sofia-sip/libsofia-sip-ua/tport/ws.h index b048df730d..b9621b3824 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/ws.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/ws.h @@ -88,6 +88,7 @@ typedef struct wsh_s { int sanity; int secure_established; int logical_established; + int x; } wsh_t; ssize_t ws_send_buf(wsh_t *wsh, ws_opcode_t oc); diff --git a/src/mod/xml_int/mod_xml_rpc/ws.c b/src/mod/xml_int/mod_xml_rpc/ws.c index 7247ddb4ba..98c00032cc 100644 --- a/src/mod/xml_int/mod_xml_rpc/ws.c +++ b/src/mod/xml_int/mod_xml_rpc/ws.c @@ -1,11 +1,18 @@ #include "ws.h" +#include #ifndef _MSC_VER #include #endif +#ifndef _MSC_VER +#define ms_sleep(x) usleep( x * 1000); +#else +#define ms_sleep(x) Sleep( x ); +#endif + #define SHA1_HASH_SIZE 20 -struct globals_s globals; +struct ws_globals_s ws_globals; #ifndef WSS_STANDALONE @@ -82,20 +89,20 @@ void init_ssl(void) { OpenSSL_add_all_algorithms(); /* load & register cryptos */ SSL_load_error_strings(); /* load all error messages */ - globals.ssl_method = TLSv1_server_method(); /* create server instance */ - globals.ssl_ctx = SSL_CTX_new(globals.ssl_method); /* create context */ - assert(globals.ssl_ctx); + ws_globals.ssl_method = TLSv1_server_method(); /* create server instance */ + ws_globals.ssl_ctx = SSL_CTX_new(ws_globals.ssl_method); /* create context */ + assert(ws_globals.ssl_ctx); /* set the local certificate from CertFile */ - SSL_CTX_use_certificate_file(globals.ssl_ctx, globals.cert, SSL_FILETYPE_PEM); + SSL_CTX_use_certificate_file(ws_globals.ssl_ctx, ws_globals.cert, SSL_FILETYPE_PEM); /* set the private key from KeyFile */ - SSL_CTX_use_PrivateKey_file(globals.ssl_ctx, globals.key, SSL_FILETYPE_PEM); + SSL_CTX_use_PrivateKey_file(ws_globals.ssl_ctx, ws_globals.key, SSL_FILETYPE_PEM); /* verify private key */ - if ( !SSL_CTX_check_private_key(globals.ssl_ctx) ) { + if ( !SSL_CTX_check_private_key(ws_globals.ssl_ctx) ) { abort(); - } + } - SSL_CTX_set_cipher_list(globals.ssl_ctx, "HIGH:!DSS:!aNULL@STRENGTH"); + SSL_CTX_set_cipher_list(ws_globals.ssl_ctx, "HIGH:!DSS:!aNULL@STRENGTH"); thread_setup(); } @@ -119,28 +126,28 @@ static int cheezy_get_var(char *data, char *name, char *buf, size_t buflen) */ do { - if(!strncmp(p,name,strlen(name)) && *(p+strlen(name))==':') break; + if(!strncmp(p,name,strlen(name)) && *(p+strlen(name))==':') break; } while((p = (strstr(p,"\n")+1))!=(char *)1); if (p != (char *)1 && *p!='\0') { - char *v, *e = 0; + char *v, *e = 0; - v = strchr(p, ':'); - if (v) { - v++; - while(v && *v == ' ') { + v = strchr(p, ':'); + if (v) { + v++; + while(v && *v == ' ') { v++; - } - if (v) { + } + if (v) { e = strchr(v, '\r'); if (!e) { e = strchr(v, '\n'); } - } + } - if (v && e) { - size_t cplen; + if (v && e) { + int cplen; size_t len = e - v; if (len > buflen - 1) { @@ -152,9 +159,9 @@ static int cheezy_get_var(char *data, char *name, char *buf, size_t buflen) strncpy(buf, v, cplen); *(buf+cplen) = '\0'; return 1; - } - - } + } + + } } return 0; } @@ -217,44 +224,89 @@ static void sha1_digest(unsigned char *digest, char *in) #endif -int ws_handshake_kvp(wsh_t *wsh, char *key, char *version, char *proto) +int ws_handshake(wsh_t *wsh) { + char key[256] = ""; + char version[5] = ""; + char proto[256] = ""; + char proto_buf[384] = ""; + char uri[256] = ""; char input[256] = ""; unsigned char output[SHA1_HASH_SIZE] = ""; char b64[256] = ""; char respond[512] = ""; + ssize_t bytes; + char *p, *e = 0; - if (!wsh->tsession) { + if (wsh->sock == ws_sock_invalid) { return -3; } - if (!*key || !*version || !*proto) { + while((bytes = ws_raw_read(wsh, wsh->buffer + wsh->datalen, wsh->buflen - wsh->datalen)) > 0) { + wsh->datalen += bytes; + if (strstr(wsh->buffer, "\r\n\r\n") || strstr(wsh->buffer, "\n\n")) { + break; + } + } + + if (bytes > sizeof(wsh->buffer) -1) { goto err; } + *(wsh->buffer+bytes) = '\0'; + + if (strncasecmp(wsh->buffer, "GET ", 4)) { + goto err; + } + + p = wsh->buffer + 4; + + e = strchr(p, ' '); + if (!e) { + goto err; + } + + strncpy(uri, p, e-p); + + cheezy_get_var(wsh->buffer, "Sec-WebSocket-Key", key, sizeof(key)); + cheezy_get_var(wsh->buffer, "Sec-WebSocket-Version", version, sizeof(version)); + cheezy_get_var(wsh->buffer, "Sec-WebSocket-Protocol", proto, sizeof(proto)); + + if (!*key) { + goto err; + } + snprintf(input, sizeof(input), "%s%s", key, WEBSOCKET_GUID); sha1_digest(output, input); b64encode((unsigned char *)output, SHA1_HASH_SIZE, (unsigned char *)b64, sizeof(b64)); + if (*proto) { + snprintf(proto_buf, sizeof(proto_buf), "Sec-WebSocket-Protocol: %s\r\n", proto); + } + snprintf(respond, sizeof(respond), "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Accept: %s\r\n" - "Sec-WebSocket-Protocol: %s\r\n\r\n", + "%s\r\n", b64, - proto); + proto_buf); - if (ws_raw_write(wsh, respond, strlen(respond))) { - wsh->handshake = 1; - return 0; - } + + ws_raw_write(wsh, respond, strlen(respond)); + wsh->handshake = 1; + + return 0; err: snprintf(respond, sizeof(respond), "HTTP/1.1 400 Bad Request\r\n" "Sec-WebSocket-Version: 13\r\n\r\n"); + //printf("ERR:\n%s\n", respond); + + ws_raw_write(wsh, respond, strlen(respond)); ws_close(wsh, WS_NONE); @@ -263,85 +315,238 @@ int ws_handshake_kvp(wsh_t *wsh, char *key, char *version, char *proto) } -issize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes) +ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes) { - issize_t r; - TConn *conn = wsh->tsession->connP; + ssize_t r; + int err = 0; - if (!wsh->handshake) { - r = wsh->tsession->connP->buffersize; - memcpy(data, conn->buffer.b, r); - printf("%s\n", conn->buffer.t); - ConnReadInit(conn); - return r; - } else { - const char *readError = NULL; + if (wsh->ssl) { + do { + r = SSL_read(wsh->ssl, data, bytes); +#ifndef _MSC_VER + if (wsh->x++) usleep(10000); +#else + if (wsh->x++) Sleep(10); +#endif + if (r == -1) { + err = SSL_get_error(wsh->ssl, r); - // printf(" pos=%d size=%d need=%d\n", conn->bufferpos, conn->buffersize, bytes); - - r = conn->buffersize - conn->bufferpos; - - if (r < 0) { - printf("286 Read Error %d!\n", r); - return 0; - } else if (r == 0) { - ConnRead(conn, 2, NULL, NULL, &readError); - - if (readError) { - // printf("292 Read Error %s\n", readError); - free((void *)readError); - return 0; + if (wsh->handshake && err == SSL_ERROR_WANT_READ) { + r = -2; + goto end; + } } - r = conn->buffersize - conn->bufferpos; - } - - if (r <= (issize_t)bytes) { - memcpy(data, conn->buffer.b + conn->bufferpos, r); - // ConnReadInit(conn); - conn->bufferpos = conn->buffersize; - ConnReadInit(conn); - return r; - } else { - memcpy(data, conn->buffer.b + conn->bufferpos, bytes); - conn->bufferpos += (uint32_t)bytes; - return (issize_t)bytes; - } + } while (r == -1 && err == SSL_ERROR_WANT_READ && wsh->x < 100); + goto end; } + do { + r = recv(wsh->sock, data, bytes, 0); +#ifndef _MSC_VER + if (wsh->x++) usleep(10000); +#else + if (wsh->x++) Sleep(10); +#endif + } while (r == -1 && xp_is_blocking(xp_errno()) && wsh->x < 100); + + if (wsh->x >= 100) { + r = -1; + } + + end: + + if (r > 0) { + *((char *)data + r) = '\0'; + } + + if (r >= 0) { + wsh->x = 0; + } + + return r; } -issize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes) +ssize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes) { size_t r; if (wsh->ssl) { do { - r = SSL_write(wsh->ssl, data, (int)bytes); - } while (r == -1 && SSL_get_error(wsh->ssl, (int)r) == SSL_ERROR_WANT_WRITE); + r = SSL_write(wsh->ssl, data, bytes); + } while (r == -1 && SSL_get_error(wsh->ssl, r) == SSL_ERROR_WANT_WRITE); - return (issize_t)r; + return r; } - if (ConnWrite(wsh->tsession->connP, data, (uint32_t)bytes)) { - return (issize_t)bytes; - } else { - return 0; - } + do { + r = send(wsh->sock, data, bytes, 0); + } while (r == -1 && xp_is_blocking(xp_errno())); + + //if (r<0) { + //printf("wRITE FAIL: %s\n", strerror(errno)); + //} + + return r; } -wsh_t * ws_init(ws_tsession_t *tsession) +#ifdef _MSC_VER +static int setup_socket(ws_socket_t sock) { - wsh_t *wsh = (wsh_t *)malloc(sizeof(*wsh)); + unsigned long v = 1; - if (!wsh) return NULL; + if (ioctlsocket(sock, FIONBIO, &v) == SOCKET_ERROR) { + return -1; + } + return 0; + +} + +static int restore_socket(ws_socket_t sock) +{ + unsigned long v = 0; + + if (ioctlsocket(sock, FIONBIO, &v) == SOCKET_ERROR) { + return -1; + } + + return 0; + +} + +#else + +static int setup_socket(ws_socket_t sock) +{ + int flags = fcntl(sock, F_GETFL, 0); + return fcntl(sock, F_SETFL, flags | O_NONBLOCK); +} + +static int restore_socket(ws_socket_t sock) +{ + int flags = fcntl(sock, F_GETFL, 0); + + flags &= ~O_NONBLOCK; + + return fcntl(sock, F_SETFL, flags); + +} + +#endif + + +static int establish_logical_layer(wsh_t *wsh) +{ + + if (!wsh->sanity) { + return -1; + } + + if (wsh->logical_established) { + return 0; + } + + if (wsh->secure && !wsh->secure_established) { + int code; + + if (!wsh->ssl) { + wsh->ssl = SSL_new(wsh->ssl_ctx); + assert(wsh->ssl); + + SSL_set_fd(wsh->ssl, wsh->sock); + } + + do { + code = SSL_accept(wsh->ssl); + + if (code == 1) { + wsh->secure_established = 1; + break; + } + + if (code == 0) { + return -1; + } + + if (code < 0) { + if (code == -1 && SSL_get_error(wsh->ssl, code) != SSL_ERROR_WANT_READ) { + return -1; + } + } + + if (wsh->block) { + ms_sleep(10); + } else { + ms_sleep(1); + } + + wsh->sanity--; + + if (!wsh->block) { + return -2; + } + + } while (wsh->sanity > 0); + + if (!wsh->sanity) { + return -1; + } + + } + + while (!wsh->down && !wsh->handshake) { + int r = ws_handshake(wsh); + + if (r < 0) { + wsh->down = 1; + return -1; + } + + if (!wsh->handshake && !wsh->block) { + return -2; + } + + } + + wsh->logical_established = 1; + + return 0; +} + + +int ws_init(wsh_t *wsh, ws_socket_t sock, SSL_CTX *ssl_ctx, int close_sock, int block) +{ memset(wsh, 0, sizeof(*wsh)); - wsh->tsession = tsession; - wsh->buflen = sizeof(wsh->buffer); - return wsh; + wsh->sock = sock; + wsh->block = block; + wsh->sanity = 5000; + wsh->ssl_ctx = ssl_ctx; + + if (!ssl_ctx) { + ssl_ctx = ws_globals.ssl_ctx; + } + + if (close_sock) { + wsh->close_sock = 1; + } + + wsh->buflen = sizeof(wsh->buffer); + wsh->secure = ssl_ctx ? 1 : 0; + + setup_socket(sock); + + if (establish_logical_layer(wsh) == -1) { + return -1; + } + + if (wsh->down) { + return -1; + } + + return 0; } void ws_destroy(wsh_t *wsh) @@ -372,7 +577,7 @@ void ws_destroy(wsh_t *wsh) } } -issize_t ws_close(wsh_t *wsh, int16_t reason) +ssize_t ws_close(wsh_t *wsh, int16_t reason) { if (wsh->down) { @@ -380,21 +585,46 @@ issize_t ws_close(wsh_t *wsh, int16_t reason) } wsh->down = 1; + + if (reason && wsh->sock != ws_sock_invalid) { + uint16_t *u16; + uint8_t fr[4] = {WSOC_CLOSE | 0x80, 2, 0}; + + u16 = (uint16_t *) &fr[2]; + *u16 = htons((int16_t)reason); + ws_raw_write(wsh, fr, 4); + } + + restore_socket(wsh->sock); + + if (wsh->close_sock && wsh->sock != ws_sock_invalid) { + close(wsh->sock); + } + + wsh->sock = ws_sock_invalid; return reason * -1; + } -issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data) +ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data) { - issize_t need = 2; + ssize_t need = 2; char *maskp; + int ll = 0; again: need = 2; maskp = NULL; *data = NULL; + ll = establish_logical_layer(wsh); + + if (ll < 0) { + return ll; + } + if (wsh->down) { return -1; } @@ -403,11 +633,16 @@ issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data) return ws_close(wsh, WS_PROTO_ERR); } - if ((wsh->datalen = ws_raw_read(wsh, wsh->buffer, 14)) < need) { - while (!wsh->down && (wsh->datalen += ws_raw_read(wsh, wsh->buffer + wsh->datalen, 14 - wsh->datalen)) < need) ; - - if (0 && (wsh->datalen += ws_raw_read(wsh, wsh->buffer + wsh->datalen, 14 - wsh->datalen)) < need) { - /* too small - protocol err */ + if ((wsh->datalen = ws_raw_read(wsh, wsh->buffer, 9)) < 0) { + if (wsh->datalen == -2) { + return -2; + } + return ws_close(wsh, WS_PROTO_ERR); + } + + if (wsh->datalen < need) { + if ((wsh->datalen += ws_raw_read(wsh, wsh->buffer + wsh->datalen, 9 - wsh->datalen)) < need) { + /* too small - protocol err */ return ws_close(wsh, WS_PROTO_ERR); } } @@ -443,7 +678,7 @@ issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data) wsh->plen = wsh->buffer[1] & 0x7f; wsh->payload = &wsh->buffer[2]; - + if (wsh->plen == 127) { uint64_t *u64; @@ -483,7 +718,13 @@ issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data) need = (wsh->plen - (wsh->datalen - need)); - if ((need + wsh->datalen) > (issize_t)wsh->buflen) { + if (need < 0) { + /* invalid read - protocol err .. */ + *oc = WSOC_CLOSE; + return ws_close(wsh, WS_PROTO_ERR); + } + + if ((need + wsh->datalen) > (ssize_t)wsh->buflen) { /* too big - Ain't nobody got time fo' dat */ *oc = WSOC_CLOSE; return ws_close(wsh, WS_DATA_TOO_BIG); @@ -492,7 +733,7 @@ issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data) wsh->rplen = wsh->plen - need; while(need) { - issize_t r = ws_raw_read(wsh, wsh->payload + wsh->rplen, need); + ssize_t r = ws_raw_read(wsh, wsh->payload + wsh->rplen, need); if (r < 1) { /* invalid read - protocol err .. */ @@ -506,7 +747,7 @@ issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data) } if (mask && maskp) { - issize_t i; + ssize_t i; for (i = 0; i < wsh->datalen; i++) { wsh->payload[i] ^= maskp[i % 4]; @@ -539,7 +780,7 @@ issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data) } } -issize_t ws_feed_buf(wsh_t *wsh, void *data, size_t bytes) +ssize_t ws_feed_buf(wsh_t *wsh, void *data, size_t bytes) { if (bytes + wsh->wdatalen > wsh->buflen) { @@ -548,14 +789,14 @@ issize_t ws_feed_buf(wsh_t *wsh, void *data, size_t bytes) memcpy(wsh->wbuffer + wsh->wdatalen, data, bytes); - wsh->wdatalen += (issize_t)bytes; + wsh->wdatalen += bytes; - return (issize_t)bytes; + return bytes; } -issize_t ws_send_buf(wsh_t *wsh, ws_opcode_t oc) +ssize_t ws_send_buf(wsh_t *wsh, ws_opcode_t oc) { - issize_t r = 0; + ssize_t r = 0; if (!wsh->wdatalen) { return -1; @@ -569,7 +810,7 @@ issize_t ws_send_buf(wsh_t *wsh, ws_opcode_t oc) } -issize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes) +ssize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes) { uint8_t hdr[14] = { 0 }; size_t hlen = 2; @@ -603,15 +844,39 @@ issize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes) *u64 = htonl(bytes); } - if (ws_raw_write(wsh, (void *) &hdr[0], hlen) != (issize_t)hlen) { + if (ws_raw_write(wsh, (void *) &hdr[0], hlen) != (ssize_t)hlen) { return -1; } - if (ws_raw_write(wsh, data, bytes) != (issize_t)bytes) { + if (ws_raw_write(wsh, data, bytes) != (ssize_t)bytes) { return -2; } - return (issize_t)bytes; + return bytes; } +#ifdef _MSC_VER +int xp_errno(void) +{ + return WSAGetLastError(); +} + +int xp_is_blocking(int errcode) +{ + return errcode == WSAEWOULDBLOCK || errcode == WSAEINPROGRESS; +} + +#else + +int xp_errno(void) +{ + return errno; +} + +int xp_is_blocking(int errcode) +{ + return errcode == EAGAIN || errcode == EWOULDBLOCK || errcode == EINPROGRESS || errcode == EINTR || errcode == ETIMEDOUT; +} + +#endif diff --git a/src/mod/xml_int/mod_xml_rpc/ws.h b/src/mod/xml_int/mod_xml_rpc/ws.h index 8b34844f15..b9621b3824 100644 --- a/src/mod/xml_int/mod_xml_rpc/ws.h +++ b/src/mod/xml_int/mod_xml_rpc/ws.h @@ -13,30 +13,40 @@ #include #else #pragma warning(disable:4996) -#define snprintf _snprintf #endif #include +#include #include #include #include #include #include #include +//#include "sha1.h" #include -#include <../lib/abyss/src/session.h> -#include <../lib/abyss/src/conn.h> -typedef TSession ws_tsession_t; -typedef int issize_t; +#ifdef _MSC_VER +#define strncasecmp _strnicmp +#define snprintf _snprintf +#ifdef _WIN64 +#define WS_SSIZE_T __int64 +#elif _MSC_VER >= 1400 +#define WS_SSIZE_T __int32 __w64 +#else +#define WS_SSIZE_T __int32 +#endif +typedef WS_SSIZE_T ssize_t; +#endif -struct globals_s { + +struct ws_globals_s { const SSL_METHOD *ssl_method; SSL_CTX *ssl_ctx; char cert[512]; char key[512]; }; -// extern struct globals_s globals; +extern struct ws_globals_s ws_globals; typedef int ws_socket_t; #define ws_sock_invalid -1 @@ -59,36 +69,44 @@ typedef enum { } ws_opcode_t; typedef struct wsh_s { - ws_tsession_t *tsession; + ws_socket_t sock; char buffer[65536]; char wbuffer[65536]; size_t buflen; - issize_t datalen; - issize_t wdatalen; + ssize_t datalen; + ssize_t wdatalen; char *payload; - issize_t plen; - issize_t rplen; + ssize_t plen; + ssize_t rplen; SSL *ssl; int handshake; uint8_t down; int secure; uint8_t close_sock; + SSL_CTX *ssl_ctx; + int block; + int sanity; + int secure_established; + int logical_established; + int x; } wsh_t; -issize_t ws_send_buf(wsh_t *wsh, ws_opcode_t oc); -issize_t ws_feed_buf(wsh_t *wsh, void *data, size_t bytes); +ssize_t ws_send_buf(wsh_t *wsh, ws_opcode_t oc); +ssize_t ws_feed_buf(wsh_t *wsh, void *data, size_t bytes); -issize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes); -issize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes); -issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data); -issize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes); -wsh_t * ws_init(ws_tsession_t *tsession); -issize_t ws_close(wsh_t *wsh, int16_t reason); +ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes); +ssize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes); +ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data); +ssize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes); +int ws_init(wsh_t *wsh, ws_socket_t sock, SSL_CTX *ssl_ctx, int close_sock, int block); +ssize_t ws_close(wsh_t *wsh, int16_t reason); void ws_destroy(wsh_t *wsh); void init_ssl(void); void deinit_ssl(void); -int ws_handshake_kvp(wsh_t *wsh, char *key, char *version, char *proto); +int xp_errno(void); +int xp_is_blocking(int errcode); + #ifndef _MSC_VER