diff --git a/libsofia-sip-ua/tport/ws.c b/libsofia-sip-ua/tport/ws.c index 8306cce7c..b38fd7a9f 100644 --- a/libsofia-sip-ua/tport/ws.c +++ b/libsofia-sip-ua/tport/ws.c @@ -344,7 +344,9 @@ int ws_handshake(wsh_t *wsh) ws_raw_write(wsh, respond, strlen(respond)); } - ws_close(wsh, WS_NONE); + if (wsh->sock != ws_sock_invalid) { + ws_close(wsh, WS_NONE); + } } return -1; @@ -365,6 +367,10 @@ ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes, int block) if (wsh->ssl) { do { //ERR_clear_error(); + if ((!SSL_has_pending(wsh->ssl)) && wsh->down) { + r = -1; + goto end; + } r = SSL_read(wsh->ssl, data, bytes); if (r < 0) { @@ -456,12 +462,13 @@ int wss_error(wsh_t *wsh, int ssl_err, char const *who) case SSL_ERROR_ZERO_RETURN: return 0; + case SSL_ERROR_SSL: case SSL_ERROR_SYSCALL: + wss_log_errors(1, who, ssl_err); ERR_clear_error(); if (SSL_get_shutdown(wsh->ssl) & SSL_RECEIVED_SHUTDOWN) return 0; /* EOS */ - if (errno == 0) - return 0; /* EOS */ + ws_close(wsh, WS_SSLERR); errno = EIO; return -1; @@ -479,6 +486,11 @@ int wss_error(wsh_t *wsh, int ssl_err, char const *who) static ssize_t ws_raw_read_blocking(wsh_t *wsh, char *data, size_t max_bytes, int max_retries) { ssize_t total_bytes_read = 0; + + if (wsh->sock == ws_sock_invalid) { + return -3; + } + while (total_bytes_read < max_bytes && max_retries-- > 0) { ssize_t bytes_read = ws_raw_read(wsh, data + total_bytes_read, max_bytes - total_bytes_read, WS_BLOCK); if (bytes_read < 0) { @@ -545,7 +557,7 @@ ssize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes) if (!sanity) ssl_err = -56; - if (ssl_err) { + if (ssl_err <= 0) { r = ssl_err; } @@ -694,11 +706,10 @@ int establish_logical_layer(wsh_t *wsh) } - while (!wsh->down && !wsh->handshake) { + while (!wsh->down && !wsh->handshake && wsh->sock != ws_sock_invalid) { int r = ws_handshake(wsh); if (r < 0) { - wsh->down = 1; return -1; } @@ -768,7 +779,11 @@ void ws_destroy(wsh_t *wsh) return; } - if (!wsh->down) { + if ((wsh->sock == ws_sock_invalid) && (!wsh->down)){ + return; + } + + if (!wsh->down && wsh->ssl) { ws_close(wsh, WS_NONE); } @@ -805,7 +820,7 @@ ssize_t ws_close(wsh_t *wsh, int16_t reason) wsh->uri = NULL; } - if (reason && wsh->sock != ws_sock_invalid) { + if (reason && reason != WS_SSLERR && wsh->sock != ws_sock_invalid) { uint16_t *u16; uint8_t fr[4] = {WSOC_CLOSE | 0x80, 2, 0}; @@ -816,12 +831,29 @@ ssize_t ws_close(wsh_t *wsh, int16_t reason) restore_socket(wsh->sock); + if (reason == WS_SSLERR) { + SSL_free(wsh->ssl); + wsh->ssl = NULL; + } + if (wsh->ssl) { int code = 0; int ssl_error = 0; const char* buf = "0"; + struct timeval tout = {5,0}; /* check if no fatal error occurs on connection */ + if (SSL_get_shutdown(wsh->ssl)) { + goto ssl_finish_it; + } + + // we're closing down, do not wait > 5 seoncds + if (setsockopt(SSL_get_wfd(wsh->ssl), SOL_SOCKET, SO_SNDTIMEO, &tout, sizeof(tout)) < 0) { + goto ssl_finish_it; + } + else if (setsockopt(SSL_get_wfd(wsh->ssl), SOL_SOCKET, SO_RCVTIMEO, &tout, sizeof(tout)) < 0) { + goto ssl_finish_it; + } code = SSL_write(wsh->ssl, buf, 1); ssl_error = SSL_get_error(wsh->ssl, code); @@ -840,7 +872,7 @@ ssize_t ws_close(wsh_t *wsh, int16_t reason) wsh->ssl = NULL; } - if (wsh->close_sock && wsh->sock != ws_sock_invalid) { + if (wsh->close_sock && wsh->sock != ws_sock_invalid && reason != WS_SSLERR) { #ifndef WIN32 close(wsh->sock); #else @@ -887,6 +919,10 @@ ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data) ll = establish_logical_layer(wsh); + if (ll == -1) { + ws_close(wsh, WS_SSLERR); + } + if (ll < 0) { return ll; } @@ -922,7 +958,7 @@ ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data) { wsh->plen = wsh->buffer[1] & 0x7f; *data = (uint8_t *) &wsh->buffer[2]; - return ws_close(wsh, 1000); + return ws_close(wsh, WS_NORMAL); } break; case WSOC_CONTINUATION: diff --git a/libsofia-sip-ua/tport/ws.h b/libsofia-sip-ua/tport/ws.h index 837e1f70a..63af730cd 100644 --- a/libsofia-sip-ua/tport/ws.h +++ b/libsofia-sip-ua/tport/ws.h @@ -78,7 +78,8 @@ typedef enum { WS_NONE = 0, WS_NORMAL = 1000, WS_PROTO_ERR = 1002, - WS_DATA_TOO_BIG = 1009 + WS_DATA_TOO_BIG = 1009, + WS_SSLERR = 1010 } ws_cause_t; typedef enum {