Skip to content

Commit 8e5f83c

Browse files
authored
Merge pull request #233 from freeswitch/fix_tport_secondary_and_ssl
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.
2 parents 563fa31 + d5ef673 commit 8e5f83c

File tree

4 files changed

+103
-43
lines changed

4 files changed

+103
-43
lines changed

libsofia-sip-ua/tport/tport_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ struct tport_s {
178178

179179
su_time_t tp_ktime; /**< Keepalive timer updated */
180180
su_time_t tp_ptime; /**< Ping sent */
181+
su_time_t tp_ltime; /**< Logical Layer timer updated */
181182

182183
tp_name_t tp_name[1]; /**< Transport name.
183184
*

libsofia-sip-ua/tport/tport_type_ws.c

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -613,11 +613,30 @@ int tport_ws_pong(tport_t *self)
613613
return send(self->tp_socket, "\r\n", 2, 0);
614614
}
615615

616+
/** Calculate next timeout for logical layer */
617+
int tport_next_logical_layer_establish(tport_t *self,
618+
su_time_t *return_target,
619+
char const **return_why, unsigned timeout)
620+
{
621+
if (timeout != 0 && timeout != UINT_MAX) {
622+
if (!tport_has_queued(self)) {
623+
su_time_t ntime = su_time_add(self->tp_ltime, timeout);
624+
if (su_time_cmp(ntime, *return_target) < 0)
625+
*return_target = ntime, *return_why = "logicallayer";
626+
}
627+
}
628+
629+
return 0;
630+
}
631+
632+
616633
/** Calculate next timer for WS. */
617634
int tport_ws_next_timer(tport_t *self,
618635
su_time_t *return_target,
619636
char const **return_why)
620637
{
638+
unsigned timeout = self->tp_params->tpp_keepalive;
639+
621640
tport_ws_t *wstp = (tport_ws_t *)self;
622641
int ll = establish_logical_layer(&wstp->ws);
623642
int punt = 0;
@@ -626,14 +645,18 @@ int tport_ws_next_timer(tport_t *self,
626645
punt = 1;
627646
} else if (ll < 0) {
628647
time_t now = time(NULL);
648+
su_time_t sunow = su_now();
629649
if (now - wstp->connected > 5) {
630650
punt = 2;
631651
}
652+
653+
self->tp_ltime = sunow;
632654
} else {
633-
self->tp_params->tpp_keepalive = 0;
655+
timeout = 0;
634656
}
635657

636658
if (punt) {
659+
ws_destroy(&wstp->ws);
637660
tport_close(self);
638661

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

647670
return
648671
tport_next_recv_timeout(self, return_target, return_why) |
649-
tport_next_keepalive(self, return_target, return_why);
672+
tport_next_logical_layer_establish(self, return_target, return_why, timeout);
650673
}
651674

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

657680
if (!strcmp("wss", self->tp_protoname) && !wstp->ws.secure_established) {
681+
ws_destroy(&wstp->ws);
658682
tport_close(self);
659683
} else {
660684
tport_recv_timeout_timer(self, now);
661-
tport_keepalive_timer(self, now);
662685
}
663686
tport_base_timer(self, now);
664687
}

libsofia-sip-ua/tport/ws.c

Lines changed: 75 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
#pragma warning(disable: 4706)
2222
#endif
2323

24-
#define WS_BLOCK 1
24+
#define WS_BLOCK 10000 /* ms, blocks read operation for 10 seconds */
25+
#define WS_SOFT_BLOCK 1000 /* ms, blocks read operation for 1 second */
2526
#define WS_NOBLOCK 0
2627

2728
#define WS_INIT_SANITY 5000
@@ -267,7 +268,7 @@ int ws_handshake(wsh_t *wsh)
267268
return -3;
268269
}
269270

270-
while((bytes = ws_raw_read(wsh, wsh->buffer + wsh->datalen, wsh->buflen - wsh->datalen, WS_BLOCK)) > 0) {
271+
while((bytes = ws_raw_read(wsh, wsh->buffer + wsh->datalen, wsh->buflen - wsh->datalen, WS_NOBLOCK)) > 0) {
271272
wsh->datalen += bytes;
272273
if (strstr(wsh->buffer, "\r\n\r\n") || strstr(wsh->buffer, "\n\n")) {
273274
break;
@@ -344,20 +345,26 @@ int ws_handshake(wsh_t *wsh)
344345
ws_raw_write(wsh, respond, strlen(respond));
345346
}
346347

348+
if (bytes == -2) {
349+
return 0;
350+
}
351+
347352
ws_close(wsh, WS_NONE);
348353
}
349354

350355
return -1;
351356

352357
}
353358

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

357363
ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes, int block)
358364
{
359365
ssize_t r;
360366
int ssl_err = 0;
367+
int block_n = block / 10;
361368

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

370-
if (r < 0) {
377+
if (r <= 0) {
371378
ssl_err = SSL_get_error(wsh->ssl, r);
372379

373380
if (SSL_WANT_READ_WRITE(ssl_err)) {
@@ -379,12 +386,16 @@ ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes, int block)
379386
ms_sleep(10);
380387
} else {
381388
wss_error(wsh, ssl_err, "ws_raw_read: SSL_read");
389+
if (SSL_IO_ERROR(ssl_err)) {
390+
wsh->ssl_io_error = 1;
391+
}
392+
382393
r = -1;
383394
goto end;
384395
}
385396
}
386397

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

389400
goto end;
390401
}
@@ -404,11 +415,11 @@ ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes, int block)
404415
ms_sleep(10);
405416
}
406417
}
407-
} while (r == -1 && xp_is_blocking(xp_errno()) && wsh->x < 1000);
418+
} while (r == -1 && xp_is_blocking(xp_errno()) && wsh->x < block_n);
408419

409420
end:
410421

411-
if (wsh->x >= 10000 || (block && wsh->x >= 1000)) {
422+
if (wsh->x >= 10000 || (block && wsh->x >= block_n)) {
412423
r = -1;
413424
}
414425

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

512523
if (r == 0) {
524+
ssl_err = SSL_get_error(wsh->ssl, r);
525+
if (SSL_IO_ERROR(ssl_err)) {
526+
wsh->ssl_io_error = 1;
527+
}
528+
513529
ssl_err = -42;
514530
break;
515531
}
@@ -528,16 +544,22 @@ ssize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes)
528544
ms = 50;
529545
}
530546
}
547+
531548
ms_sleep(ms);
532549
}
533550

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

537554
if (!SSL_WANT_READ_WRITE(ssl_err)) {
555+
if (SSL_IO_ERROR(ssl_err)) {
556+
wsh->ssl_io_error = 1;
557+
}
558+
538559
ssl_err = wss_error(wsh, ssl_err, "ws_raw_write: SSL_write");
539560
break;
540561
}
562+
541563
ssl_err = 0;
542564
}
543565

@@ -600,18 +622,6 @@ static int setup_socket(ws_socket_t sock)
600622

601623
}
602624

603-
static int restore_socket(ws_socket_t sock)
604-
{
605-
unsigned long v = 0;
606-
607-
if (ioctlsocket(sock, FIONBIO, &v) == SOCKET_ERROR) {
608-
return -1;
609-
}
610-
611-
return 0;
612-
613-
}
614-
615625
#else
616626

617627
static int setup_socket(ws_socket_t sock)
@@ -620,16 +630,6 @@ static int setup_socket(ws_socket_t sock)
620630
return fcntl(sock, F_SETFL, flags | O_NONBLOCK);
621631
}
622632

623-
static int restore_socket(ws_socket_t sock)
624-
{
625-
int flags = fcntl(sock, F_GETFL, 0);
626-
627-
flags &= ~O_NONBLOCK;
628-
629-
return fcntl(sock, F_SETFL, flags);
630-
631-
}
632-
633633
#endif
634634

635635

@@ -814,25 +814,60 @@ ssize_t ws_close(wsh_t *wsh, int16_t reason)
814814
ws_raw_write(wsh, fr, 4);
815815
}
816816

817-
restore_socket(wsh->sock);
818-
819817
if (wsh->ssl) {
820-
int code = 0;
818+
int code = 0, rcode = 0;
821819
int ssl_error = 0;
822-
const char* buf = "0";
820+
int n = 0, block_n = WS_SOFT_BLOCK / 10;
823821

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

828-
if (ssl_error == SSL_ERROR_SYSCALL || ssl_error == SSL_ERROR_SSL) {
827+
/* connection has been already closed */
828+
if (SSL_get_shutdown(wsh->ssl) & SSL_SENT_SHUTDOWN) {
829+
goto ssl_finish_it;
830+
}
831+
832+
/* peer closes the connection */
833+
if (SSL_get_shutdown(wsh->ssl) & SSL_RECEIVED_SHUTDOWN) {
834+
SSL_shutdown(wsh->ssl);
829835
goto ssl_finish_it;
830836
}
831837

832-
code = SSL_shutdown(wsh->ssl);
833-
if (code == 0) {
834-
/* need to make sure there is no more data to read */
835-
ws_raw_read(wsh, wsh->buffer, 9, WS_BLOCK);
838+
/* us closes the connection. We do bidirection shutdown handshake */
839+
for(;;) {
840+
code = SSL_shutdown(wsh->ssl);
841+
ssl_error = SSL_get_error(wsh->ssl, code);
842+
if (code <= 0 && ssl_error == SSL_ERROR_WANT_READ) {
843+
/* need to make sure there are no more data to read */
844+
for(;;) {
845+
if ((rcode = SSL_read(wsh->ssl, wsh->buffer, 9)) <= 0) {
846+
ssl_error = SSL_get_error(wsh->ssl, rcode);
847+
if (ssl_error == SSL_ERROR_ZERO_RETURN) {
848+
break;
849+
} else if (SSL_IO_ERROR(ssl_error)) {
850+
goto ssl_finish_it;
851+
} else if (ssl_error == SSL_ERROR_WANT_READ) {
852+
if (++n == block_n) {
853+
goto ssl_finish_it;
854+
}
855+
856+
ms_sleep(10);
857+
} else {
858+
goto ssl_finish_it;
859+
}
860+
}
861+
}
862+
} else if (code == 0 || (code < 0 && ssl_error == SSL_ERROR_WANT_WRITE)) {
863+
if (++n == block_n) {
864+
goto ssl_finish_it;
865+
}
866+
867+
ms_sleep(10);
868+
} else { /* code != 0 */
869+
goto ssl_finish_it;
870+
}
836871
}
837872

838873
ssl_finish_it:

libsofia-sip-ua/tport/ws.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ typedef struct wsh_s {
115115
int logical_established;
116116
int stay_open;
117117
int x;
118+
int ssl_io_error;
118119
void *write_buffer;
119120
size_t write_buffer_len;
120121

0 commit comments

Comments
 (0)