diff --git a/lib/context.c b/lib/context.c index c6f4b51f..0b504896 100644 --- a/lib/context.c +++ b/lib/context.c @@ -1578,7 +1578,8 @@ lws_check_deferred_free(struct lws_context *context, int force) lws_start_foreach_llp(struct lws_deferred_free **, pdf, context->deferred_free_list) { - if (now > (*pdf)->deadline || force) { + if (force || + lws_compare_time_t(context, now, (*pdf)->deadline) > 5) { df = *pdf; *pdf = df->next; /* finalize vh destruction */ @@ -1605,7 +1606,7 @@ lws_vhost_destroy(struct lws_vhost *vh) /* part 2 is deferred to allow all the handle closes to complete */ df->next = vh->context->deferred_free_list; - df->deadline = lws_now_secs() + 5; + df->deadline = lws_now_secs(); df->payload = vh; vh->context->deferred_free_list = df; } diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index b8a49947..90120eea 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -182,7 +182,8 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) } lwsl_debug("%s: %p: %d secs\n", __func__, wsi, secs); - wsi->pending_timeout_limit = now + secs; + wsi->pending_timeout_limit = secs; + wsi->pending_timeout_set = now; wsi->pending_timeout = reason; lws_pt_unlock(pt); @@ -1295,6 +1296,18 @@ lws_now_secs(void) return tv.tv_sec; } +LWS_VISIBLE LWS_EXTERN int +lws_compare_time_t(struct lws_context *context, time_t t1, time_t t2) +{ + if (t1 < context->time_discontiguity) + t1 += context->time_fixup; + + if (t2 < context->time_discontiguity) + t2 += context->time_fixup; + + return (int)(t1 - t2); +} + #if LWS_POSIX @@ -2423,8 +2436,7 @@ lws_restart_ws_ping_pong_timer(struct lws *wsi) if (!lws_state_is_ws(wsi->state)) return; - wsi->ws->time_next_ping_check = (time_t)lws_now_secs() + - wsi->context->ws_ping_pong_interval; + wsi->ws->time_next_ping_check = (time_t)lws_now_secs(); } static const char *hex = "0123456789ABCDEF"; diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 3753a470..a695b66f 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -5286,6 +5286,26 @@ lws_parse_uri(char *p, const char **prot, const char **ads, int *port, LWS_VISIBLE LWS_EXTERN unsigned long lws_now_secs(void); +/** + * lws_compare_time_t(): return relationship between two time_t + * + * \param context: struct lws_context + * \param t1: time_t 1 + * \param t2: time_t 2 + * + * returns <0 if t2 > t1; >0 if t1 > t2; or == 0 if t1 == t2. + * + * This is aware of clock discontiguities that may have affected either t1 or + * t2 and adapts the comparison for them. + * + * For the discontiguity detection to work, you must avoid any arithmetic on + * the times being compared. For example to have a timeout that triggers + * 15s from when it was set, store the time it was set and compare like + * `if (lws_compare_time_t(context, now, set_time) > 15)` + */ +LWS_VISIBLE LWS_EXTERN int +lws_compare_time_t(struct lws_context *context, time_t t1, time_t t2); + /** * lws_get_context - Allow getting lws_context from a Websocket connection * instance diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index ed2085ab..f96688f2 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -1130,6 +1130,8 @@ struct lws_context { time_t last_ws_ping_pong_check_s; time_t last_cert_check_s; time_t time_up; + time_t time_discontiguity; + time_t time_fixup; const struct lws_plat_file_ops *fops; struct lws_plat_file_ops fops_platform; #if defined(LWS_WITH_HTTP2) @@ -1974,7 +1976,7 @@ struct lws { uint64_t accept_start_us; #endif #endif - time_t pending_timeout_limit; + time_t pending_timeout_set; /* ints */ int position_in_fds_table; @@ -2049,6 +2051,8 @@ struct lws { #ifndef LWS_NO_CLIENT unsigned short c_port; #endif + unsigned short pending_timeout_limit; + uint8_t state; /* enum lws_connection_states */ uint8_t mode; /* enum connection_mode */ @@ -2167,7 +2171,7 @@ lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len); LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_service_timeout_check(struct lws *wsi, unsigned int sec); +lws_service_timeout_check(struct lws *wsi, time_t sec); LWS_EXTERN void lws_remove_from_timeout_list(struct lws *wsi); diff --git a/lib/service.c b/lib/service.c index 666c9af7..7a0196c5 100644 --- a/lib/service.c +++ b/lib/service.c @@ -565,7 +565,7 @@ bail_die: } int -lws_service_timeout_check(struct lws *wsi, unsigned int sec) +lws_service_timeout_check(struct lws *wsi, time_t sec) { struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; int n = 0; @@ -586,7 +586,8 @@ lws_service_timeout_check(struct lws *wsi, unsigned int sec) * if we went beyond the allowed time, kill the * connection */ - if ((time_t)sec > wsi->pending_timeout_limit) { + if (lws_compare_time_t(wsi->context, sec, wsi->pending_timeout_set) > + wsi->pending_timeout_limit) { if (wsi->desc.sockfd != LWS_SOCK_INVALID && wsi->position_in_fds_table >= 0) @@ -993,8 +994,31 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, if (context->time_up < 1464083026 && now > 1464083026) context->time_up = now; - /* TODO: if using libev, we should probably use timeout watchers... */ - if (context->last_timeout_check_s != now) { + if (context->last_timeout_check_s && + now - context->last_timeout_check_s > 100) { + /* + * There has been a discontiguity. Any stored time that is + * less than context->time_discontiguity should have context-> + * time_fixup added to it. + * + * Some platforms with no RTC will experience this as a normal + * event when ntp sets their clock, but we can have started + * long before that with a 0-based unix time. + */ + + context->time_discontiguity = now; + context->time_fixup = now - context->last_timeout_check_s; + + lwsl_notice("time discontiguity: at old time %llus, " + "new time %llus: +%llus\n", + (unsigned long long)context->last_timeout_check_s, + (unsigned long long)context->time_discontiguity, + (unsigned long long)context->time_fixup); + + context->last_timeout_check_s = now - 1; + } + + if (lws_compare_time_t(context, context->last_timeout_check_s, now)) { context->last_timeout_check_s = now; #if defined(LWS_WITH_STATS) diff --git a/lib/tls/tls.c b/lib/tls/tls.c index 2572434b..09ce7810 100644 --- a/lib/tls/tls.c +++ b/lib/tls/tls.c @@ -235,7 +235,7 @@ lws_tls_check_cert_lifetime(struct lws_vhost *v) n = lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_TO, &ir, 0); if (n) - return -1; + return 1; life = (ir.time - now) / (24 * 3600); lwsl_notice(" vhost %s: cert expiry: %dd\n", v->name, (int)life); @@ -254,7 +254,8 @@ lws_tls_check_all_cert_lifetimes(struct lws_context *context) struct lws_vhost *v = context->vhost_list; while (v) { - lws_tls_check_cert_lifetime(v); + if (lws_tls_check_cert_lifetime(v) < 0) + return -1; v = v->vhost_next; }