summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2007-10-04 09:50:36 +0000
committerBenny Prijono <bennylp@teluu.com>2007-10-04 09:50:36 +0000
commite27b8a021b6768b20189ba7c9e3d8ba303172010 (patch)
tree4ce4a9f1fee4cddfcbd8de2251507a762e39505d /pjsip
parent5a17c9a249ae2804b3b0ae558a0a63275ef14259 (diff)
Ticket #95: Keep-alive mechanism for TCP and TLS transports
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1473 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip')
-rw-r--r--pjsip/build/pjsip_core.dsp2
-rw-r--r--pjsip/include/pjsip/sip_config.h50
-rw-r--r--pjsip/src/pjsip/sip_transport_tcp.c109
-rw-r--r--pjsip/src/pjsip/sip_transport_tls_ossl.c117
4 files changed, 263 insertions, 15 deletions
diff --git a/pjsip/build/pjsip_core.dsp b/pjsip/build/pjsip_core.dsp
index 38f1ab03..2cd4fc06 100644
--- a/pjsip/build/pjsip_core.dsp
+++ b/pjsip/build/pjsip_core.dsp
@@ -40,6 +40,7 @@ RSC=rc.exe
# PROP Output_Dir ".\output\pjsip-core-i386-win32-vc6-release"
# PROP Intermediate_Dir ".\output\pjsip-core-i386-win32-vc6-release"
# PROP Target_Dir ""
+F90=df.exe
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /MD /W4 /Zi /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /c
# SUBTRACT CPP /YX
@@ -64,6 +65,7 @@ LIB32=link.exe -lib
# PROP Output_Dir ".\output\pjsip-core-i386-win32-vc6-debug"
# PROP Intermediate_Dir ".\output\pjsip-core-i386-win32-vc6-debug"
# PROP Target_Dir ""
+F90=df.exe
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
# SUBTRACT CPP /YX
diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
index 846c2a57..3d386097 100644
--- a/pjsip/include/pjsip/sip_config.h
+++ b/pjsip/include/pjsip/sip_config.h
@@ -265,10 +265,10 @@
* Idle timeout interval to be applied to transports with no usage
* before the transport is destroyed. Value is in seconds.
*
- * Default: 60
+ * Default: 600
*/
#ifndef PJSIP_TRANSPORT_IDLE_TIME
-# define PJSIP_TRANSPORT_IDLE_TIME 60
+# define PJSIP_TRANSPORT_IDLE_TIME 600
#endif
@@ -298,6 +298,52 @@
/**
+ * Set the interval to send keep-alive packet for TCP transports.
+ * If the value is zero, keep-alive will be disabled for TCP.
+ *
+ * Default: 90 (seconds)
+ *
+ * @see PJSIP_TCP_KEEP_ALIVE_DATA
+ */
+#ifndef PJSIP_TCP_KEEP_ALIVE_INTERVAL
+# define PJSIP_TCP_KEEP_ALIVE_INTERVAL 90
+#endif
+
+
+/**
+ * Set the payload of the TCP keep-alive packet.
+ *
+ * Default: CRLF
+ */
+#ifndef PJSIP_TCP_KEEP_ALIVE_DATA
+# define PJSIP_TCP_KEEP_ALIVE_DATA { "\r\n", 2 }
+#endif
+
+
+/**
+ * Set the interval to send keep-alive packet for TLS transports.
+ * If the value is zero, keep-alive will be disabled for TLS.
+ *
+ * Default: 90 (seconds)
+ *
+ * @see PJSIP_TLS_KEEP_ALIVE_DATA
+ */
+#ifndef PJSIP_TLS_KEEP_ALIVE_INTERVAL
+# define PJSIP_TLS_KEEP_ALIVE_INTERVAL 90
+#endif
+
+
+/**
+ * Set the payload of the TLS keep-alive packet.
+ *
+ * Default: CRLF
+ */
+#ifndef PJSIP_TLS_KEEP_ALIVE_DATA
+# define PJSIP_TLS_KEEP_ALIVE_DATA { "\r\n", 2 }
+#endif
+
+
+/**
* This macro specifies whether full DNS resolution should be used.
* When enabled, #pjsip_resolve() will perform asynchronous DNS SRV and
* A (or AAAA, when IPv6 is supported) resolution to resolve the SIP
diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c
index 585dad1f..1bf0a030 100644
--- a/pjsip/src/pjsip/sip_transport_tcp.c
+++ b/pjsip/src/pjsip/sip_transport_tcp.c
@@ -41,7 +41,6 @@
#define POOL_TP_INIT 4000
#define POOL_TP_INC 4002
-
struct tcp_listener;
struct tcp_transport;
@@ -112,6 +111,11 @@ struct tcp_transport
pj_ioqueue_key_t *key;
pj_bool_t has_pending_connect;
+ /* Keep-alive timer. */
+ pj_timer_entry ka_timer;
+ pj_time_val last_activity;
+ pjsip_tx_data_op_key ka_op_key;
+ pj_str_t ka_pkt;
/* TCP transport can only have one rdata!
* Otherwise chunks of incoming PDU may be received on different
@@ -476,6 +480,8 @@ static void on_write_complete(pj_ioqueue_key_t *key,
static void on_connect_complete(pj_ioqueue_key_t *key,
pj_status_t status);
+/* TCP keep-alive timer callback */
+static void tcp_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e);
/*
* Common function to create TCP transport, called when pending accept() and
@@ -491,6 +497,7 @@ static pj_status_t tcp_create( struct tcp_listener *listener,
struct tcp_transport *tcp;
pj_ioqueue_t *ioqueue;
pj_ioqueue_callback tcp_callback;
+ const pj_str_t ka_pkt = PJSIP_TCP_KEEP_ALIVE_DATA;
pj_status_t status;
@@ -569,6 +576,12 @@ static pj_status_t tcp_create( struct tcp_listener *listener,
tcp->is_registered = PJ_TRUE;
+ /* Initialize keep-alive timer */
+ tcp->ka_timer.user_data = (void*)tcp;
+ tcp->ka_timer.cb = &tcp_keep_alive_timer;
+ 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);
+
/* Done setting up basic transport. */
*p_tcp = tcp;
@@ -966,6 +979,16 @@ static void on_accept_complete( pj_ioqueue_key_t *key,
if (status != PJ_SUCCESS) {
PJ_LOG(3,(tcp->base.obj_name, "New transport cancelled"));
tcp_destroy(&tcp->base, status);
+ } else {
+ /* Start keep-alive timer */
+ if (PJSIP_TCP_KEEP_ALIVE_INTERVAL) {
+ pj_time_val delay = {PJSIP_TCP_KEEP_ALIVE_INTERVAL, 0};
+ pjsip_endpt_schedule_timer(listener->endpt,
+ &tcp->ka_timer,
+ &delay);
+ tcp->ka_timer.id = PJ_TRUE;
+ pj_gettimeofday(&tcp->last_activity);
+ }
}
}
@@ -1005,6 +1028,10 @@ static void on_write_complete(pj_ioqueue_key_t *key,
pj_ioqueue_get_user_data(key);
pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key;
+ /* Note that op_key may be the op_key from keep-alive, thus
+ * it will not have tdata etc.
+ */
+
tdata_op_key->tdata = NULL;
/* Check for error/closure */
@@ -1025,6 +1052,9 @@ static void on_write_complete(pj_ioqueue_key_t *key,
* Notify sip_transport.c that packet has been sent.
*/
tdata_op_key->callback(&tcp->base, tdata_op_key->token, bytes_sent);
+
+ /* Mark last activity time */
+ pj_gettimeofday(&tcp->last_activity);
}
}
@@ -1127,14 +1157,17 @@ static pj_status_t tcp_send_msg(pjsip_transport *transport,
/*
* This callback is called by transport manager to shutdown transport.
- * This normally is only used by UDP transport.
*/
static pj_status_t tcp_shutdown(pjsip_transport *transport)
{
+ struct tcp_transport *tcp = (struct tcp_transport*)transport;
+
+ /* Stop keep-alive timer. */
+ if (tcp->ka_timer.id) {
+ pjsip_endpt_cancel_timer(tcp->listener->endpt, &tcp->ka_timer);
+ tcp->ka_timer.id = PJ_FALSE;
+ }
- PJ_UNUSED_ARG(transport);
-
- /* Nothing to do for TCP */
return PJ_SUCCESS;
}
@@ -1175,6 +1208,9 @@ static void on_read_complete(pj_ioqueue_key_t *key,
if (bytes_read > 0) {
pj_size_t size_eaten;
+ /* Mark this as an activity */
+ pj_gettimeofday(&tcp->last_activity);
+
/* Init pkt_info part. */
rdata->pkt_info.len += bytes_read;
rdata->pkt_info.zero = 0;
@@ -1364,7 +1400,70 @@ static void on_connect_complete(pj_ioqueue_key_t *key,
/* Flush all pending send operations */
tcp_flush_pending_tx(tcp);
+
+ /* Start keep-alive timer */
+ if (PJSIP_TCP_KEEP_ALIVE_INTERVAL) {
+ pj_time_val delay = { PJSIP_TCP_KEEP_ALIVE_INTERVAL, 0 };
+ pjsip_endpt_schedule_timer(tcp->listener->endpt, &tcp->ka_timer,
+ &delay);
+ tcp->ka_timer.id = PJ_TRUE;
+ pj_gettimeofday(&tcp->last_activity);
+ }
+}
+
+/* Transport keep-alive timer callback */
+static void tcp_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e)
+{
+ struct tcp_transport *tcp = (struct tcp_transport*) e->user_data;
+ pj_time_val delay;
+ pj_time_val now;
+ pj_ssize_t size;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(th);
+
+ tcp->ka_timer.id = PJ_TRUE;
+
+ pj_gettimeofday(&now);
+ PJ_TIME_VAL_SUB(now, tcp->last_activity);
+
+ if (now.sec > 0 && now.sec < PJSIP_TCP_KEEP_ALIVE_INTERVAL) {
+ /* There has been activity, so don't send keep-alive */
+ delay.sec = PJSIP_TCP_KEEP_ALIVE_INTERVAL - now.sec;
+ delay.msec = 0;
+
+ pjsip_endpt_schedule_timer(tcp->listener->endpt, &tcp->ka_timer,
+ &delay);
+ tcp->ka_timer.id = PJ_TRUE;
+ return;
+ }
+
+ PJ_LOG(5,(tcp->base.obj_name, "Sending %d byte(s) keep-alive to %.*s:%d",
+ (int)tcp->ka_pkt.slen, (int)tcp->base.remote_name.host.slen,
+ tcp->base.remote_name.host.ptr,
+ tcp->base.remote_name.port));
+
+ /* Send the data */
+ size = tcp->ka_pkt.slen;
+ status = pj_ioqueue_send(tcp->key, &tcp->ka_op_key.key,
+ tcp->ka_pkt.ptr, &size, 0);
+
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ tcp_perror(tcp->base.obj_name,
+ "Error sending keep-alive packet", status);
+ pjsip_transport_shutdown(&tcp->base);
+ return;
+ }
+
+ /* Register next keep-alive */
+ delay.sec = PJSIP_TCP_KEEP_ALIVE_INTERVAL;
+ delay.msec = 0;
+
+ pjsip_endpt_schedule_timer(tcp->listener->endpt, &tcp->ka_timer,
+ &delay);
+ tcp->ka_timer.id = PJ_TRUE;
}
+
#endif /* PJ_HAS_TCP */
diff --git a/pjsip/src/pjsip/sip_transport_tls_ossl.c b/pjsip/src/pjsip/sip_transport_tls_ossl.c
index 235a32e1..3998b6d8 100644
--- a/pjsip/src/pjsip/sip_transport_tls_ossl.c
+++ b/pjsip/src/pjsip/sip_transport_tls_ossl.c
@@ -167,6 +167,10 @@ struct tls_transport
SSL *ssl;
pj_bool_t ssl_shutdown_called;
+ /* Keep alive */
+ pj_timer_entry ka_timer;
+ pj_time_val last_activity;
+
/* TLS transport can only have one rdata!
* Otherwise chunks of incoming PDU may be received on different
* buffer.
@@ -741,15 +745,16 @@ static pj_status_t ssl_accept(struct tls_transport *tls)
/* Send outgoing data with SSL connection */
-static pj_status_t ssl_write(struct tls_transport *tls,
- pjsip_tx_data *tdata)
+static pj_status_t ssl_write_bytes(struct tls_transport *tls,
+ const void *data,
+ int size,
+ const char *data_name)
{
- int size = tdata->buf.cur - tdata->buf.start;
int sent = 0;
do {
const int fragment_sent = SSL_write(tls->ssl,
- tdata->buf.start + sent,
+ ((pj_uint8_t*)data) + sent,
size - sent);
switch( SSL_get_error(tls->ssl, fragment_sent)) {
@@ -798,7 +803,7 @@ static pj_status_t ssl_write(struct tls_transport *tls,
default:
ssl_report_error(tls->base.obj_name, 4, PJ_SUCCESS,
"Error sending %s with SSL_write()",
- pjsip_tx_data_get_info(tdata));
+ data_name);
return pj_get_netos_error() ? pj_get_netos_error()
: PJSIP_TLS_ESEND;
}
@@ -806,6 +811,16 @@ static pj_status_t ssl_write(struct tls_transport *tls,
} while (sent < size);
return PJ_SUCCESS;
+
+}
+
+/* Send outgoing tdata with SSL connection */
+static pj_status_t ssl_write(struct tls_transport *tls,
+ pjsip_tx_data *tdata)
+{
+ return ssl_write_bytes(tls, tdata->buf.start,
+ tdata->buf.cur - tdata->buf.start,
+ pjsip_tx_data_get_info(tdata));
}
@@ -1161,6 +1176,8 @@ static void on_write_complete(pj_ioqueue_key_t *key,
static void on_connect_complete(pj_ioqueue_key_t *key,
pj_status_t status);
+/* TLS keep-alive timer callback */
+static void tls_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e);
/*
* Common function to create TLS transport, called when pending accept() and
@@ -1203,7 +1220,7 @@ static pj_status_t tls_create( struct tls_listener *listener,
(is_server ? "tlss%p" :"tlsc%p"), tls);
/* Initialize transport reference counter to 1 */
- status = pj_atomic_create(pool, 1, &tls->base.ref_cnt);
+ status = pj_atomic_create(pool, 0, &tls->base.ref_cnt);
if (status != PJ_SUCCESS) {
goto on_error;
}
@@ -1273,6 +1290,11 @@ static pj_status_t tls_create( struct tls_listener *listener,
tls->is_registered = PJ_TRUE;
+ /* Initialize keep-alive timer */
+ tls->ka_timer.user_data = (void*) tls;
+ tls->ka_timer.cb = &tls_keep_alive_timer;
+
+
/* Done setting up basic transport. */
*p_tls = tls;
@@ -1723,6 +1745,17 @@ static void on_accept_complete( pj_ioqueue_key_t *key,
ssl_report_error(tls->base.obj_name, 4, status,
"Error creating incoming TLS transport");
pjsip_transport_shutdown(&tls->base);
+
+ } else {
+ /* Start keep-alive timer */
+ if (PJSIP_TLS_KEEP_ALIVE_INTERVAL) {
+ pj_time_val delay = {PJSIP_TLS_KEEP_ALIVE_INTERVAL, 0};
+ pjsip_endpt_schedule_timer(listener->endpt,
+ &tls->ka_timer,
+ &delay);
+ tls->ka_timer.id = PJ_TRUE;
+ pj_gettimeofday(&tls->last_activity);
+ }
}
accept_op = new_op;
@@ -1774,6 +1807,9 @@ static void on_write_complete(pj_ioqueue_key_t *key,
-bytes_sent;
if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status;
pjsip_transport_shutdown(&tls->base);
+ } else {
+ /* Mark last activity */
+ pj_gettimeofday(&tls->last_activity);
}
if (tdata_op_key->callback) {
@@ -1918,8 +1954,7 @@ static pj_status_t tls_shutdown(pjsip_transport *transport)
/* Shutdown SSL */
if (!tls->ssl_shutdown_called) {
- /* Release our reference counter and shutdown SSL */
- pjsip_transport_dec_ref(transport);
+ /* shutdown SSL */
SSL_shutdown(tls->ssl);
tls->ssl_shutdown_called = PJ_TRUE;
@@ -1969,6 +2004,9 @@ static void on_read_complete(pj_ioqueue_key_t *key,
*/
pj_size_t size_eaten;
+ /* Mark last activity */
+ pj_gettimeofday(&tls->last_activity);
+
/* Init pkt_info part. */
rdata->pkt_info.zero = 0;
pj_gettimeofday(&rdata->pkt_info.timestamp);
@@ -2216,6 +2254,69 @@ static void on_connect_complete(pj_ioqueue_key_t *key,
/* Flush all pending send operations */
tls_flush_pending_tx(tls);
+
+ /* Start keep-alive timer */
+ if (PJSIP_TLS_KEEP_ALIVE_INTERVAL) {
+ pj_time_val delay = { PJSIP_TLS_KEEP_ALIVE_INTERVAL, 0 };
+ pjsip_endpt_schedule_timer(tls->listener->endpt, &tls->ka_timer,
+ &delay);
+ tls->ka_timer.id = PJ_TRUE;
+ pj_gettimeofday(&tls->last_activity);
+ }
+
+}
+
+
+/* Transport keep-alive timer callback */
+static void tls_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e)
+{
+ struct tls_transport *tls = (struct tls_transport*) e->user_data;
+ const pj_str_t ka_data = PJSIP_TLS_KEEP_ALIVE_DATA;
+ pj_time_val delay;
+ pj_time_val now;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(th);
+
+ tls->ka_timer.id = PJ_TRUE;
+
+ pj_gettimeofday(&now);
+ PJ_TIME_VAL_SUB(now, tls->last_activity);
+
+ if (now.sec > 0 && now.sec < PJSIP_TLS_KEEP_ALIVE_INTERVAL) {
+ /* There has been activity, so don't send keep-alive */
+ delay.sec = PJSIP_TLS_KEEP_ALIVE_INTERVAL - now.sec;
+ delay.msec = 0;
+
+ pjsip_endpt_schedule_timer(tls->listener->endpt, &tls->ka_timer,
+ &delay);
+ tls->ka_timer.id = PJ_TRUE;
+ return;
+ }
+
+ PJ_LOG(5,(tls->base.obj_name, "Sending %d byte(s) keep-alive to %.*s:%d",
+ (int)ka_data.slen, (int)tls->base.remote_name.host.slen,
+ tls->base.remote_name.host.ptr,
+ tls->base.remote_name.port));
+
+ /* Send the data */
+ status = ssl_write_bytes(tls, ka_data.ptr, (int)ka_data.slen,
+ "keep-alive");
+ if (status != PJ_SUCCESS &&
+ status != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK))
+ {
+ ssl_report_error(tls->base.obj_name, 1, status,
+ "Error sending keep-alive packet");
+ return;
+ }
+
+ /* Register next keep-alive */
+ delay.sec = PJSIP_TLS_KEEP_ALIVE_INTERVAL;
+ delay.msec = 0;
+
+ pjsip_endpt_schedule_timer(tls->listener->endpt, &tls->ka_timer,
+ &delay);
+ tls->ka_timer.id = PJ_TRUE;
}
#endif /* PJSIP_HAS_TLS_TRANSPORT */