Add TLS initial receive timeout for server connection (#3744)

This commit is contained in:
Riza Sulistyo 2023-10-30 10:08:01 +07:00 committed by GitHub
parent f2da44b720
commit 33f64ba933
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 120 additions and 55 deletions

View File

@ -676,6 +676,25 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void)
#endif
/**
* The initial timeout interval for incoming TCP/TLS transports
* (i.e. server side) in the event that no valid SIP message is received
* following a successful connection. The value is in seconds.
* Disable the timeout by setting it to 0.
*
* Note that even if this is disabled, the connection might still get closed
* when it is idle or not referred anymore. Have a look at \a
* PJSIP_TRANSPORT_SERVER_IDLE_TIME.
*
* Notes:
* - keep-alive packet is not considered as a valid message.
*
* Default: 0
*/
#ifndef PJSIP_TRANSPORT_SERVER_IDLE_TIME_FIRST
# define PJSIP_TRANSPORT_SERVER_IDLE_TIME_FIRST 0
#endif
/**
* Maximum number of usages for a transport before a new transport is
* created. This only applies for ephemeral transports such as TCP.
@ -786,14 +805,20 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void)
/**
* Initial timeout interval to be applied to incoming transports (i.e. server
* side) when no data received after a successful connection. Value is in
* seconds. Disable the timeout by setting it to 0.
* The initial timeout interval for incoming TCP transports
* (i.e. server side) in the event that no valid SIP message is received
* following a successful connection. The value is in seconds.
* Disable the timeout by setting it to 0.
*
* Note that even when this is disable, the connection might still get closed
* Note that even if this is disabled, the connection might still get closed
* when it is idle or not referred anymore. Have a look at \a
* PJSIP_TRANSPORT_SERVER_IDLE_TIME
* PJSIP_TRANSPORT_SERVER_IDLE_TIME.
*
* Notes:
* - keep-alive packet is not considered as a valid message.
* - This macro is specific to TCP usage and takes precedence over
* a\ PJSIP_TRANSPORT_SERVER_IDLE_TIME_FIRST when both are set.
*
* Default: 0 (disabled)
*/
#ifndef PJSIP_TCP_INITIAL_TIMEOUT

View File

@ -870,6 +870,11 @@ struct pjsip_transport
pj_size_t last_recv_len; /**< Last received data length. */
void *data; /**< Internal transport data. */
unsigned initial_timeout;/**< Initial timeout interval
to be applied to incoming
TCP/TLS transports when no
valid data received after
a successful connection. */
/**
* Function to be called by transport manager to send SIP message.

View File

@ -115,7 +115,8 @@ typedef struct pjsip_tcp_transport_cfg
/**
* Intial timeout interval to be applied to incoming transports
* (i.e. server side) when no data received after a successful connection.
* (i.e. server side) when no valid data received after a successful
* connection.
*
* Default: PJSIP_TCP_INITIAL_TIMEOUT
*/

View File

@ -324,6 +324,15 @@ typedef struct pjsip_tls_setting
*/
pj_time_val timeout;
/**
* Intial timeout interval to be applied to incoming transports
* (i.e. server side) when no valid data received after a successful
* connection.
*
* Default: PJSIP_TRANSPORT_SERVER_IDLE_TIME_FIRST
*/
unsigned initial_timeout;
/**
* Should SO_REUSEADDR be used for the listener socket.
* Default value is PJSIP_TLS_TRANSPORT_REUSEADDR.
@ -437,6 +446,7 @@ PJ_INLINE(void) pjsip_tls_setting_default(pjsip_tls_setting *tls_opt)
tls_opt->sockopt_ignore_error = PJ_TRUE;
tls_opt->proto = PJSIP_SSL_DEFAULT_PROTO;
tls_opt->enable_renegotiation = PJ_TRUE;
tls_opt->initial_timeout = PJSIP_TRANSPORT_SERVER_IDLE_TIME_FIRST;
}

View File

@ -102,8 +102,10 @@ PJ_DEF(void) pjsip_dump_config(void)
PJSIP_MAX_TIMED_OUT_ENTRIES));
PJ_LOG(3, (id, " PJSIP_TRANSPORT_IDLE_TIME : %d",
PJSIP_TRANSPORT_IDLE_TIME));
PJ_LOG(3, (id, " PJSIP_TRANSPORT_SERVER_IDLE_TIME : %d",
PJ_LOG(3, (id, " PJSIP_TRANSPORT_SERVER_IDLE_TIME : %d",
PJSIP_TRANSPORT_SERVER_IDLE_TIME));
PJ_LOG(3, (id, " PJSIP_TRANSPORT_SERVER_IDLE_TIME_FIRST : %d",
PJSIP_TRANSPORT_SERVER_IDLE_TIME_FIRST));
PJ_LOG(3, (id, " PJSIP_MAX_TRANSPORT_USAGE : %d",
PJSIP_MAX_TRANSPORT_USAGE));
PJ_LOG(3, (id, " PJSIP_TCP_TRANSPORT_BACKLOG : %d",

View File

@ -90,6 +90,12 @@ static const char* print_tpsel_info(const pjsip_tpselector *sel)
# define PJSIP_TRANSPORT_ENTRY_ALLOC_CNT 16
#endif
/* Enum for id idle_timer. */
enum timer_id {
IDLE_TIMER_ID = 1,
INITIAL_IDLE_TIMER_ID
};
/* Prototype. */
static pj_status_t mod_on_tx_msg(pjsip_tx_data *tdata);
@ -1066,6 +1072,8 @@ static void transport_idle_callback(pj_timer_heap_t *timer_heap,
struct pj_timer_entry *entry)
{
pjsip_transport *tp = (pjsip_transport*) entry->user_data;
int entry_id = entry->id;
pj_assert(tp != NULL);
PJ_UNUSED_ARG(timer_heap);
@ -1079,8 +1087,23 @@ static void transport_idle_callback(pj_timer_heap_t *timer_heap,
* race condition with pjsip_tpmgr_acquire_transport2().
*/
pj_lock_acquire(tp->tpmgr->lock);
if (pj_atomic_get(tp->ref_cnt) == 0) {
tp->is_destroying = PJ_TRUE;
PJ_LOG(4, (THIS_FILE, "Transport %s is being destroyed "
"due to timeout in %s timer", tp->obj_name,
(entry_id == IDLE_TIMER_ID)?"idle":"initial"));
if (entry_id == INITIAL_IDLE_TIMER_ID) {
if (tp->last_recv_len > 0 && tp->tpmgr->tp_drop_data_cb) {
pjsip_tp_dropped_data dd;
pj_bzero(&dd, sizeof(dd));
dd.tp = tp;
dd.data = NULL;
dd.len = tp->last_recv_len;
dd.status = PJ_ESOCKETSTOP;
(*tp->tpmgr->tp_drop_data_cb)(&dd);
}
}
} else {
pj_lock_release(tp->tpmgr->lock);
return;
@ -1179,6 +1202,8 @@ PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp )
{
pj_time_val delay;
int timer_id = IDLE_TIMER_ID;
/* If transport is in graceful shutdown, then this is the
* last user who uses the transport. Schedule to destroy the
* transport immediately. Otherwise schedule idle timer.
@ -1186,9 +1211,18 @@ PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp )
if (tp->is_shutdown) {
delay.sec = delay.msec = 0;
} else {
delay.sec = (tp->dir==PJSIP_TP_DIR_OUTGOING) ?
PJSIP_TRANSPORT_IDLE_TIME :
PJSIP_TRANSPORT_SERVER_IDLE_TIME;
if (tp->dir == PJSIP_TP_DIR_OUTGOING) {
delay.sec = PJSIP_TRANSPORT_IDLE_TIME;
} else {
delay.sec = PJSIP_TRANSPORT_SERVER_IDLE_TIME;
if (tp->last_recv_ts.u64 == 0 && tp->initial_timeout) {
PJ_LOG(4, (THIS_FILE,
"Starting transport %s initial timer",
tp->obj_name));
timer_id = INITIAL_IDLE_TIMER_ID;
delay.sec = tp->initial_timeout;
}
}
delay.msec = 0;
}
@ -1199,7 +1233,7 @@ PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp )
pjsip_endpt_schedule_timer_w_grp_lock(tp->tpmgr->endpt,
&tp->idle_timer,
&delay,
PJ_TRUE,
timer_id,
tp->grp_lock);
}
pj_lock_release(tpmgr->lock);
@ -2048,6 +2082,20 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr,
dd.status = PJSIP_ERXOVERFLOW;
(*mgr->tp_drop_data_cb)(&dd);
}
if (rdata->tp_info.transport->idle_timer.id ==
INITIAL_IDLE_TIMER_ID)
{
/* We are not getting the first valid SIP message
* as expected, close the transport.
*/
PJ_LOG(4, (THIS_FILE, "Unexpected data was received "\
"while waiting for a valid initial SIP messages. "\
"Shutting down transport %s",
rdata->tp_info.transport->obj_name));
pjsip_transport_shutdown(rdata->tp_info.transport);
}
/* Exhaust all data. */
return rdata->pkt_info.len;
@ -2211,6 +2259,17 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr,
}
*/
/* We have a valid message, cancel the initial timer. */
if (rdata->tp_info.transport->idle_timer.id == INITIAL_IDLE_TIMER_ID) {
PJ_LOG(4, (THIS_FILE, "Receive initial valid message from %s, "\
"cancelling the initial timer",
rdata->tp_info.transport->obj_name));
rdata->tp_info.transport->idle_timer.id = PJ_FALSE;
pjsip_endpt_cancel_timer(mgr->endpt,
&rdata->tp_info.transport->idle_timer);
}
/* Call the transport manager's upstream message callback.
*/
mgr->on_rx_msg(mgr->endpt, PJ_SUCCESS, rdata);

View File

@ -124,8 +124,6 @@ struct tcp_transport
/* Group lock to be used by TCP transport and ioqueue key */
pj_grp_lock_t *grp_lock;
/* Initial timer. */
pj_timer_entry initial_timer;
};
@ -235,7 +233,8 @@ PJ_DEF(void) pjsip_tcp_transport_cfg_default(pjsip_tcp_transport_cfg *cfg,
pj_sockaddr_init(cfg->af, &cfg->bind_addr, NULL, 0);
cfg->async_cnt = 1;
cfg->reuse_addr = PJSIP_TCP_TRANSPORT_REUSEADDR;
cfg->initial_timeout = PJSIP_TCP_INITIAL_TIMEOUT;
cfg->initial_timeout = (PJSIP_TCP_INITIAL_TIMEOUT!=0)?
PJSIP_TCP_INITIAL_TIMEOUT:PJSIP_TRANSPORT_SERVER_IDLE_TIME_FIRST;
}
@ -605,9 +604,6 @@ static pj_bool_t on_connect_complete(pj_activesock_t *asock,
/* TCP keep-alive timer callback */
static void tcp_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e);
/* TCP initial timer callback */
static void tcp_initial_timer(pj_timer_heap_t *th, pj_timer_entry *e);
/* Clean up TCP resources */
static void tcp_on_destroy(void *arg);
@ -688,6 +684,7 @@ static pj_status_t tcp_create( struct tcp_listener *listener,
tcp->base.do_shutdown = &tcp_shutdown;
tcp->base.destroy = &tcp_destroy_transport;
tcp->base.factory = &listener->factory;
tcp->base.initial_timeout = listener->initial_timeout;
/* Create group lock */
status = pj_grp_lock_create_w_handler(pool, NULL, tcp, &tcp_on_destroy,
@ -730,18 +727,10 @@ static pj_status_t tcp_create( struct tcp_listener *listener,
pj_ioqueue_op_key_init(&tcp->ka_op_key.key, sizeof(pj_ioqueue_op_key_t));
pj_strdup(tcp->base.pool, &tcp->ka_pkt, &ka_pkt);
/* Initialize initial timer. */
if (is_server && listener->initial_timeout) {
pj_time_val delay = { 0 };
tcp->initial_timer.user_data = (void*)tcp;
tcp->initial_timer.cb = &tcp_initial_timer;
delay.sec = listener->initial_timeout;
pjsip_endpt_schedule_timer(listener->endpt,
&tcp->initial_timer,
&delay);
tcp->initial_timer.id = PJ_TRUE;
/* Initialize initial timer. */
pjsip_transport_add_ref(&tcp->base);
pjsip_transport_dec_ref(&tcp->base);
}
/* Done setting up basic transport. */
@ -848,12 +837,6 @@ static pj_status_t tcp_destroy(pjsip_transport *transport,
tcp->ka_timer.id = PJ_FALSE;
}
/* Stop initial timer. */
if (tcp->initial_timer.id) {
pjsip_endpt_cancel_timer(tcp->base.endpt, &tcp->initial_timer);
tcp->initial_timer.id = PJ_FALSE;
}
/* Cancel all delayed transmits */
while (!pj_list_empty(&tcp->delayed_list)) {
struct delayed_tdata *pending_tx;
@ -1394,12 +1377,6 @@ static pj_status_t tcp_shutdown(pjsip_transport *transport)
tcp->ka_timer.id = PJ_FALSE;
}
/* Stop initial timer. */
if (tcp->initial_timer.id) {
pjsip_endpt_cancel_timer(tcp->base.endpt, &tcp->initial_timer);
tcp->initial_timer.id = PJ_FALSE;
}
return PJ_SUCCESS;
}
@ -1428,11 +1405,6 @@ static pj_bool_t on_data_read(pj_activesock_t *asock,
return PJ_FALSE;
}
if (tcp->initial_timer.id) {
pjsip_endpt_cancel_timer(tcp->base.endpt, &tcp->initial_timer);
tcp->initial_timer.id = PJ_FALSE;
}
/* Houston, we have packet! Report the packet to transport manager
* to be parsed.
*/
@ -1650,16 +1622,6 @@ static void tcp_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e)
tcp->ka_timer.id = PJ_TRUE;
}
/* Transport keep-alive timer callback */
static void tcp_initial_timer(pj_timer_heap_t *th, pj_timer_entry *e)
{
pj_status_t status = PJ_ETIMEDOUT;
struct tcp_transport *tcp = (struct tcp_transport*) e->user_data;
PJ_UNUSED_ARG(th);
tcp_init_shutdown(tcp, status);
}
PJ_DEF(pj_sock_t) pjsip_tcp_transport_get_socket(pjsip_transport *transport)
{

View File

@ -907,6 +907,7 @@ static pj_status_t tls_create( struct tls_listener *listener,
tls->base.do_shutdown = &tls_shutdown;
tls->base.destroy = &tls_destroy_transport;
tls->base.factory = &listener->factory;
tls->base.initial_timeout = listener->tls_setting.initial_timeout;
tls->ssock = ssock;
tls->on_verify_cb = listener->tls_setting.on_verify_cb;