Skip to content

Commit

Permalink
Merge pull request #233 from freeswitch/fix_tport_secondary_and_ssl
Browse files Browse the repository at this point in the history
Fix WSS secondary tport destroy. Make SSL shutdown procedure strict to openssl documentation and make sure the entire procedure is performed on non-blocking BIO. Fix WS next timer. Perform WS handshake non-blocking.
  • Loading branch information
andywolk committed Nov 6, 2023
2 parents 563fa31 + d5ef673 commit 8e5f83c
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 43 deletions.
1 change: 1 addition & 0 deletions libsofia-sip-ua/tport/tport_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ struct tport_s {

su_time_t tp_ktime; /**< Keepalive timer updated */
su_time_t tp_ptime; /**< Ping sent */
su_time_t tp_ltime; /**< Logical Layer timer updated */

tp_name_t tp_name[1]; /**< Transport name.
*
Expand Down
29 changes: 26 additions & 3 deletions libsofia-sip-ua/tport/tport_type_ws.c
Original file line number Diff line number Diff line change
Expand Up @@ -613,11 +613,30 @@ int tport_ws_pong(tport_t *self)
return send(self->tp_socket, "\r\n", 2, 0);
}

/** Calculate next timeout for logical layer */
int tport_next_logical_layer_establish(tport_t *self,
su_time_t *return_target,
char const **return_why, unsigned timeout)
{
if (timeout != 0 && timeout != UINT_MAX) {
if (!tport_has_queued(self)) {
su_time_t ntime = su_time_add(self->tp_ltime, timeout);
if (su_time_cmp(ntime, *return_target) < 0)
*return_target = ntime, *return_why = "logicallayer";
}
}

return 0;
}


/** Calculate next timer for WS. */
int tport_ws_next_timer(tport_t *self,
su_time_t *return_target,
char const **return_why)
{
unsigned timeout = self->tp_params->tpp_keepalive;

tport_ws_t *wstp = (tport_ws_t *)self;
int ll = establish_logical_layer(&wstp->ws);
int punt = 0;
Expand All @@ -626,14 +645,18 @@ int tport_ws_next_timer(tport_t *self,
punt = 1;
} else if (ll < 0) {
time_t now = time(NULL);
su_time_t sunow = su_now();
if (now - wstp->connected > 5) {
punt = 2;
}

self->tp_ltime = sunow;
} else {
self->tp_params->tpp_keepalive = 0;
timeout = 0;
}

if (punt) {
ws_destroy(&wstp->ws);
tport_close(self);

SU_DEBUG_7(("%s(%p): %s to " TPN_FORMAT "%s\n",
Expand All @@ -646,7 +669,7 @@ int tport_ws_next_timer(tport_t *self,

return
tport_next_recv_timeout(self, return_target, return_why) |
tport_next_keepalive(self, return_target, return_why);
tport_next_logical_layer_establish(self, return_target, return_why, timeout);
}

/** WS timer. */
Expand All @@ -655,10 +678,10 @@ void tport_ws_timer(tport_t *self, su_time_t now)
tport_ws_t *wstp = (tport_ws_t *)self;

if (!strcmp("wss", self->tp_protoname) && !wstp->ws.secure_established) {
ws_destroy(&wstp->ws);
tport_close(self);
} else {
tport_recv_timeout_timer(self, now);
tport_keepalive_timer(self, now);
}
tport_base_timer(self, now);
}
115 changes: 75 additions & 40 deletions libsofia-sip-ua/tport/ws.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
#pragma warning(disable: 4706)
#endif

#define WS_BLOCK 1
#define WS_BLOCK 10000 /* ms, blocks read operation for 10 seconds */
#define WS_SOFT_BLOCK 1000 /* ms, blocks read operation for 1 second */
#define WS_NOBLOCK 0

#define WS_INIT_SANITY 5000
Expand Down Expand Up @@ -267,7 +268,7 @@ int ws_handshake(wsh_t *wsh)
return -3;
}

while((bytes = ws_raw_read(wsh, wsh->buffer + wsh->datalen, wsh->buflen - wsh->datalen, WS_BLOCK)) > 0) {
while((bytes = ws_raw_read(wsh, wsh->buffer + wsh->datalen, wsh->buflen - wsh->datalen, WS_NOBLOCK)) > 0) {
wsh->datalen += bytes;
if (strstr(wsh->buffer, "\r\n\r\n") || strstr(wsh->buffer, "\n\n")) {
break;
Expand Down Expand Up @@ -344,20 +345,26 @@ int ws_handshake(wsh_t *wsh)
ws_raw_write(wsh, respond, strlen(respond));
}

if (bytes == -2) {
return 0;
}

ws_close(wsh, WS_NONE);
}

return -1;

}

#define SSL_IO_ERROR(err) (err == SSL_ERROR_SYSCALL || err == SSL_ERROR_SSL)
#define SSL_WANT_READ_WRITE(err) (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
int wss_error(wsh_t *wsh, int ssl_err, char const *who);

ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes, int block)
{
ssize_t r;
int ssl_err = 0;
int block_n = block / 10;

wsh->x++;
if (wsh->x > 250) ms_sleep(1);
Expand All @@ -367,7 +374,7 @@ ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes, int block)
//ERR_clear_error();
r = SSL_read(wsh->ssl, data, bytes);

if (r < 0) {
if (r <= 0) {
ssl_err = SSL_get_error(wsh->ssl, r);

if (SSL_WANT_READ_WRITE(ssl_err)) {
Expand All @@ -379,12 +386,16 @@ ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes, int block)
ms_sleep(10);
} else {
wss_error(wsh, ssl_err, "ws_raw_read: SSL_read");
if (SSL_IO_ERROR(ssl_err)) {
wsh->ssl_io_error = 1;
}

r = -1;
goto end;
}
}

} while (r < 0 && SSL_WANT_READ_WRITE(ssl_err) && wsh->x < 1000);
} while (r < 0 && SSL_WANT_READ_WRITE(ssl_err) && wsh->x < block_n);

goto end;
}
Expand All @@ -404,11 +415,11 @@ ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes, int block)
ms_sleep(10);
}
}
} while (r == -1 && xp_is_blocking(xp_errno()) && wsh->x < 1000);
} while (r == -1 && xp_is_blocking(xp_errno()) && wsh->x < block_n);

end:

if (wsh->x >= 10000 || (block && wsh->x >= 1000)) {
if (wsh->x >= 10000 || (block && wsh->x >= block_n)) {
r = -1;
}

Expand Down Expand Up @@ -510,6 +521,11 @@ ssize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes)
r = SSL_write(wsh->ssl, buf, size);

if (r == 0) {
ssl_err = SSL_get_error(wsh->ssl, r);
if (SSL_IO_ERROR(ssl_err)) {
wsh->ssl_io_error = 1;
}

ssl_err = -42;
break;
}
Expand All @@ -528,16 +544,22 @@ ssize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes)
ms = 50;
}
}

ms_sleep(ms);
}

if (r < 0) {
ssl_err = SSL_get_error(wsh->ssl, r);

if (!SSL_WANT_READ_WRITE(ssl_err)) {
if (SSL_IO_ERROR(ssl_err)) {
wsh->ssl_io_error = 1;
}

ssl_err = wss_error(wsh, ssl_err, "ws_raw_write: SSL_write");
break;
}

ssl_err = 0;
}

Expand Down Expand Up @@ -600,18 +622,6 @@ static int setup_socket(ws_socket_t sock)

}

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)
Expand All @@ -620,16 +630,6 @@ static int setup_socket(ws_socket_t sock)
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


Expand Down Expand Up @@ -814,25 +814,60 @@ ssize_t ws_close(wsh_t *wsh, int16_t reason)
ws_raw_write(wsh, fr, 4);
}

restore_socket(wsh->sock);

if (wsh->ssl) {
int code = 0;
int code = 0, rcode = 0;
int ssl_error = 0;
const char* buf = "0";
int n = 0, block_n = WS_SOFT_BLOCK / 10;

/* check if no fatal error occurs on connection */
code = SSL_write(wsh->ssl, buf, 1);
ssl_error = SSL_get_error(wsh->ssl, code);
/* SSL layer was never established or underlying IO error occured */
if (!wsh->secure_established || wsh->ssl_io_error) {
goto ssl_finish_it;
}

if (ssl_error == SSL_ERROR_SYSCALL || ssl_error == SSL_ERROR_SSL) {
/* connection has been already closed */
if (SSL_get_shutdown(wsh->ssl) & SSL_SENT_SHUTDOWN) {
goto ssl_finish_it;
}

/* peer closes the connection */
if (SSL_get_shutdown(wsh->ssl) & SSL_RECEIVED_SHUTDOWN) {
SSL_shutdown(wsh->ssl);
goto ssl_finish_it;
}

code = SSL_shutdown(wsh->ssl);
if (code == 0) {
/* need to make sure there is no more data to read */
ws_raw_read(wsh, wsh->buffer, 9, WS_BLOCK);
/* us closes the connection. We do bidirection shutdown handshake */
for(;;) {
code = SSL_shutdown(wsh->ssl);
ssl_error = SSL_get_error(wsh->ssl, code);
if (code <= 0 && ssl_error == SSL_ERROR_WANT_READ) {
/* need to make sure there are no more data to read */
for(;;) {
if ((rcode = SSL_read(wsh->ssl, wsh->buffer, 9)) <= 0) {
ssl_error = SSL_get_error(wsh->ssl, rcode);
if (ssl_error == SSL_ERROR_ZERO_RETURN) {
break;
} else if (SSL_IO_ERROR(ssl_error)) {
goto ssl_finish_it;
} else if (ssl_error == SSL_ERROR_WANT_READ) {
if (++n == block_n) {
goto ssl_finish_it;
}

ms_sleep(10);
} else {
goto ssl_finish_it;
}
}
}
} else if (code == 0 || (code < 0 && ssl_error == SSL_ERROR_WANT_WRITE)) {
if (++n == block_n) {
goto ssl_finish_it;
}

ms_sleep(10);
} else { /* code != 0 */
goto ssl_finish_it;
}
}

ssl_finish_it:
Expand Down
1 change: 1 addition & 0 deletions libsofia-sip-ua/tport/ws.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ typedef struct wsh_s {
int logical_established;
int stay_open;
int x;
int ssl_io_error;
void *write_buffer;
size_t write_buffer_len;

Expand Down

0 comments on commit 8e5f83c

Please sign in to comment.