@@ -411,8 +411,10 @@ static inline int php_openssl_enable_crypto(php_stream *stream,
411
411
int n , retry = 1 ;
412
412
413
413
if (cparam -> inputs .activate && !sslsock -> ssl_active ) {
414
- float timeout = sslsock -> connect_timeout .tv_sec + sslsock -> connect_timeout .tv_usec / 1000000 ;
415
- int blocked = sslsock -> s .is_blocked ;
414
+ struct timeval start_time ,
415
+ * timeout ;
416
+ int blocked = sslsock -> s .is_blocked ,
417
+ has_timeout = 0 ;
416
418
417
419
#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT )
418
420
if (sslsock -> is_client && sslsock -> sni ) {
@@ -429,36 +431,70 @@ static inline int php_openssl_enable_crypto(php_stream *stream,
429
431
sslsock -> state_set = 1 ;
430
432
}
431
433
432
- if (sslsock -> is_client && SUCCESS == php_set_sock_blocking (sslsock -> s .socket , 0 TSRMLS_CC )) {
433
- sslsock -> s .is_blocked = 0 ;
434
+ if (SUCCESS == php_set_sock_blocking (sslsock -> s .socket , 0 TSRMLS_CC )) {
435
+ sslsock -> s .is_blocked = 0 ;
434
436
}
437
+
438
+ timeout = sslsock -> is_client ? & sslsock -> connect_timeout : & sslsock -> s .timeout ;
439
+ has_timeout = !sslsock -> s .is_blocked && (timeout -> tv_sec || timeout -> tv_usec );
440
+ /* gettimeofday is not monotonic; using it here is not strictly correct */
441
+ if (has_timeout ) {
442
+ gettimeofday (& start_time , NULL );
443
+ }
444
+
435
445
do {
446
+ struct timeval cur_time ,
447
+ elapsed_time ;
448
+
436
449
if (sslsock -> is_client ) {
437
- struct timeval tvs , tve ;
438
- struct timezone tz ;
439
-
440
- gettimeofday (& tvs , & tz );
441
450
n = SSL_connect (sslsock -> ssl_handle );
442
- gettimeofday (& tve , & tz );
451
+ } else {
452
+ n = SSL_accept (sslsock -> ssl_handle );
453
+ }
443
454
444
- timeout -= (tve .tv_sec + (float ) tve .tv_usec / 1000000 ) - (tvs .tv_sec + (float ) tvs .tv_usec / 1000000 );
445
- if (timeout < 0 ) {
446
- php_error_docref (NULL TSRMLS_CC , E_WARNING , "SSL: connection timeout" );
455
+ if (has_timeout ) {
456
+ gettimeofday (& cur_time , NULL );
457
+ elapsed_time .tv_sec = cur_time .tv_sec - start_time .tv_sec ;
458
+ elapsed_time .tv_usec = cur_time .tv_usec - start_time .tv_usec ;
459
+ if (cur_time .tv_usec < start_time .tv_usec ) {
460
+ elapsed_time .tv_sec -= 1L ;
461
+ elapsed_time .tv_usec += 1000000L ;
462
+ }
463
+
464
+ if (elapsed_time .tv_sec > timeout -> tv_sec ||
465
+ (elapsed_time .tv_sec == timeout -> tv_sec &&
466
+ elapsed_time .tv_usec > timeout -> tv_usec )) {
467
+ php_error_docref (NULL TSRMLS_CC , E_WARNING , "SSL: crypto enabling timeout" );
447
468
return -1 ;
448
469
}
449
- } else {
450
- n = SSL_accept (sslsock -> ssl_handle );
451
470
}
452
471
453
472
if (n <= 0 ) {
454
- retry = handle_ssl_error (stream , n , sslsock -> is_client || sslsock -> s .is_blocked TSRMLS_CC );
455
-
473
+ /* in case of SSL_ERROR_WANT_READ/WRITE, do not retry in non-blocking mode */
474
+ retry = handle_ssl_error (stream , n , blocked TSRMLS_CC );
475
+ if (retry ) {
476
+ /* wait until something interesting happens in the socket. It may be a
477
+ * timeout. Also consider the unlikely of possibility of a write block */
478
+ int err = SSL_get_error (sslsock -> ssl_handle , n );
479
+ struct timeval left_time ;
480
+
481
+ if (has_timeout ) {
482
+ left_time .tv_sec = timeout -> tv_sec - elapsed_time .tv_sec ;
483
+ left_time .tv_usec = timeout -> tv_usec - elapsed_time .tv_usec ;
484
+ if (timeout -> tv_usec < elapsed_time .tv_usec ) {
485
+ left_time .tv_sec -= 1L ;
486
+ left_time .tv_usec += 1000000L ;
487
+ }
488
+ }
489
+ php_pollfd_for (sslsock -> s .socket , (err == SSL_ERROR_WANT_READ ) ?
490
+ (POLLIN |POLLPRI ) : POLLOUT , has_timeout ? & left_time : NULL );
491
+ }
456
492
} else {
457
- break ;
493
+ retry = 0 ;
458
494
}
459
495
} while (retry );
460
496
461
- if (sslsock -> is_client && sslsock -> s .is_blocked != blocked && SUCCESS == php_set_sock_blocking (sslsock -> s .socket , blocked TSRMLS_CC )) {
497
+ if (sslsock -> s .is_blocked != blocked && SUCCESS == php_set_sock_blocking (sslsock -> s .socket , blocked TSRMLS_CC )) {
462
498
sslsock -> s .is_blocked = blocked ;
463
499
}
464
500
0 commit comments