summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pjlib-util/include/pjlib-util/stun_endpoint.h34
-rw-r--r--pjlib-util/include/pjlib-util/stun_session.h22
-rw-r--r--pjlib-util/src/pjlib-util/stun_endpoint.c1
-rw-r--r--pjlib-util/src/pjlib-util/stun_session.c109
-rw-r--r--pjlib-util/src/pjstun-srv-test/server_main.c3
5 files changed, 151 insertions, 18 deletions
diff --git a/pjlib-util/include/pjlib-util/stun_endpoint.h b/pjlib-util/include/pjlib-util/stun_endpoint.h
index fadde315..f6e12115 100644
--- a/pjlib-util/include/pjlib-util/stun_endpoint.h
+++ b/pjlib-util/include/pjlib-util/stun_endpoint.h
@@ -45,14 +45,46 @@ PJ_BEGIN_DECL
*/
typedef struct pj_stun_endpoint
{
+ /**
+ * Pool factory to be used by the STUN endpoint and all objects created
+ * that use this STUN endpoint.
+ */
pj_pool_factory *pf;
+
+ /**
+ * Ioqueue used by this endpoint.
+ */
pj_ioqueue_t *ioqueue;
+
+ /**
+ * Timer heap instance used by this endpoint.
+ */
pj_timer_heap_t *timer_heap;
+
+ /**
+ * Internal pool used by this endpoint. This shouldn't be used by
+ * application.
+ */
+ pj_pool_t *pool;
+
+ /**
+ * Options.
+ */
unsigned options;
+ /**
+ * The default initial STUN round-trip time estimation in msecs.
+ * The value normally is PJ_STUN_RTO_VALUE.
+ */
unsigned rto_msec;
- pj_pool_t *pool;
+ /**
+ * The interval to cache outgoing STUN response in the STUN session,
+ * in miliseconds.
+ *
+ * Default 10000 (10 seconds).
+ */
+ unsigned res_cache_msec;
} pj_stun_endpoint;
diff --git a/pjlib-util/include/pjlib-util/stun_session.h b/pjlib-util/include/pjlib-util/stun_session.h
index 94ea9ff6..610302fa 100644
--- a/pjlib-util/include/pjlib-util/stun_session.h
+++ b/pjlib-util/include/pjlib-util/stun_session.h
@@ -23,6 +23,7 @@
#include <pjlib-util/stun_endpoint.h>
#include <pjlib-util/stun_transaction.h>
#include <pj/list.h>
+#include <pj/timer.h>
PJ_BEGIN_DECL
@@ -139,14 +140,18 @@ struct pj_stun_tx_data
void *user_data; /**< Arbitrary user data. */
pj_stun_client_tsx *client_tsx; /**< Client STUN transaction. */
- pj_uint8_t client_key[12];/**< Client transaction key. */
+ pj_uint32_t msg_magic; /**< Message magic. */
+ pj_uint8_t msg_key[12]; /**< Message/transaction key. */
void *pkt; /**< The STUN packet. */
unsigned max_len; /**< Length of packet buffer. */
unsigned pkt_size; /**< The actual length of STUN pkt. */
+ unsigned options; /**< Options specified when sending */
unsigned addr_len; /**< Length of destination address. */
const pj_sockaddr_t *dst_addr; /**< Destination address. */
+
+ pj_timer_entry res_timer; /**< Response cache timer. */
};
@@ -154,7 +159,7 @@ struct pj_stun_tx_data
* Options that can be specified when creating or sending outgoing STUN
* messages. These options may be specified as bitmask.
*/
-enum pj_stun_session_option
+enum pj_stun_session_send_option
{
/**
* Add short term credential to the message. This option may not be used
@@ -171,7 +176,15 @@ enum pj_stun_session_option
/**
* Add STUN fingerprint to the message.
*/
- PJ_STUN_USE_FINGERPRINT = 4
+ PJ_STUN_USE_FINGERPRINT = 4,
+
+ /**
+ * Instruct the session to cache outgoing response. This can only be
+ * used when sending outgoing response message, and when it's specified,
+ * the session will use \a res_cache_msec settings in pj_stun_endpoint
+ * as the duration of the cache.
+ */
+ PJ_STUN_CACHE_RESPONSE = 8
};
@@ -258,6 +271,7 @@ pj_stun_session_set_short_term_credential(pj_stun_session *sess,
const pj_str_t *user,
const pj_str_t *passwd);
+
/**
* Create a STUN Bind request message. After the message has been
* successfully created, application can send the message by calling
@@ -395,7 +409,7 @@ PJ_DECL(pj_status_t) pj_stun_session_create_response(pj_stun_session *sess,
* to actually send the message to the wire.
*
* @param sess The STUN session instance.
- * @param options Optional flags, from pj_stun_session_option.
+ * @param options Optional flags, from pj_stun_session_send_option.
* @param dst_addr The destination socket address.
* @param addr_len Length of destination address.
* @param tdata The STUN transmit data containing the STUN message to
diff --git a/pjlib-util/src/pjlib-util/stun_endpoint.c b/pjlib-util/src/pjlib-util/stun_endpoint.c
index 033a2839..2ca106a7 100644
--- a/pjlib-util/src/pjlib-util/stun_endpoint.c
+++ b/pjlib-util/src/pjlib-util/stun_endpoint.c
@@ -47,6 +47,7 @@ PJ_DEF(pj_status_t) pj_stun_endpoint_create( pj_pool_factory *factory,
endpt->ioqueue = ioqueue;
endpt->timer_heap = timer_heap;
endpt->rto_msec = PJ_STUN_RTO_VALUE;
+ endpt->res_cache_msec = 10000;
*p_endpt = endpt;
diff --git a/pjlib-util/src/pjlib-util/stun_session.c b/pjlib-util/src/pjlib-util/stun_session.c
index eea1c2ec..11146047 100644
--- a/pjlib-util/src/pjlib-util/stun_session.c
+++ b/pjlib-util/src/pjlib-util/stun_session.c
@@ -37,6 +37,7 @@ struct pj_stun_session
pj_str_t s_password;
pj_stun_tx_data pending_request_list;
+ pj_stun_tx_data cached_response_list;
};
#define SNAME(s_) ((s_)->pool->obj_name)
@@ -103,8 +104,9 @@ static pj_stun_tx_data* tsx_lookup(pj_stun_session *sess,
tdata = sess->pending_request_list.next;
while (tdata != &sess->pending_request_list) {
- pj_assert(sizeof(tdata->client_key)==sizeof(msg->hdr.tsx_id));
- if (pj_memcmp(tdata->client_key, msg->hdr.tsx_id,
+ pj_assert(sizeof(tdata->msg_key)==sizeof(msg->hdr.tsx_id));
+ if (tdata->msg_magic == msg->hdr.magic &&
+ pj_memcmp(tdata->msg_key, msg->hdr.tsx_id,
sizeof(msg->hdr.tsx_id))==0)
{
return tdata;
@@ -158,8 +160,9 @@ static pj_status_t create_request_tdata(pj_stun_session *sess,
}
/* copy the request's transaction ID as the transaction key. */
- pj_assert(sizeof(tdata->client_key)==sizeof(tdata->msg->hdr.tsx_id));
- pj_memcpy(tdata->client_key, tdata->msg->hdr.tsx_id,
+ pj_assert(sizeof(tdata->msg_key)==sizeof(tdata->msg->hdr.tsx_id));
+ tdata->msg_magic = tdata->msg->hdr.magic;
+ pj_memcpy(tdata->msg_key, tdata->msg->hdr.tsx_id,
sizeof(tdata->msg->hdr.tsx_id));
*p_tdata = tdata;
@@ -174,7 +177,11 @@ static void destroy_tdata(pj_stun_tx_data *tdata)
pj_stun_client_tsx_destroy(tdata->client_tsx);
tdata->client_tsx = NULL;
}
-
+ if (tdata->res_timer.id != PJ_FALSE) {
+ pj_timer_heap_cancel(tdata->sess->endpt->timer_heap,
+ &tdata->res_timer);
+ tdata->res_timer.id = PJ_FALSE;
+ }
pj_pool_release(tdata->pool);
}
@@ -188,6 +195,24 @@ PJ_DEF(void) pj_stun_msg_destroy_tdata( pj_stun_session *sess,
destroy_tdata(tdata);
}
+
+/* Timer callback to be called when it's time to destroy response cache */
+static void on_cache_timeout(pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry)
+{
+ pj_stun_tx_data *tdata;
+
+ PJ_UNUSED_ARG(timer_heap);
+
+ entry->id = PJ_FALSE;
+ tdata = (pj_stun_tx_data*) entry->user_data;
+
+ PJ_LOG(5,(SNAME(tdata->sess), "Response cache deleted"));
+
+ pj_list_erase(tdata);
+ pj_stun_msg_destroy_tdata(tdata->sess, tdata);
+}
+
static pj_status_t apply_msg_options(pj_stun_session *sess,
pj_pool_t *pool,
unsigned options,
@@ -328,6 +353,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_endpoint *endpt,
pj_memcpy(&sess->cb, cb, sizeof(*cb));
pj_list_init(&sess->pending_request_list);
+ pj_list_init(&sess->cached_response_list);
status = pj_mutex_create_recursive(pool, name, &sess->mutex);
if (status != PJ_SUCCESS) {
@@ -499,8 +525,9 @@ PJ_DEF(pj_status_t) pj_stun_session_create_response( pj_stun_session *sess,
}
/* copy the request's transaction ID as the transaction key. */
- pj_assert(sizeof(tdata->client_key)==sizeof(req->hdr.tsx_id));
- pj_memcpy(tdata->client_key, req->hdr.tsx_id, sizeof(req->hdr.tsx_id));
+ pj_assert(sizeof(tdata->msg_key)==sizeof(req->hdr.tsx_id));
+ tdata->msg_magic = req->hdr.magic;
+ pj_memcpy(tdata->msg_key, req->hdr.tsx_id, sizeof(req->hdr.tsx_id));
*p_tdata = tdata;
@@ -552,6 +579,8 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
PJ_ASSERT_RETURN(sess && addr_len && server && tdata, PJ_EINVAL);
+ tdata->options = options;
+
/* Allocate packet */
tdata->max_len = PJ_STUN_MAX_PKT_LEN;
tdata->pkt = pj_pool_alloc(tdata->pool, tdata->max_len);
@@ -611,6 +640,33 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
tsx_add(sess, tdata);
} else {
+ if ((options & PJ_STUN_CACHE_RESPONSE) &&
+ (PJ_STUN_IS_RESPONSE(tdata->msg->hdr.type) ||
+ PJ_STUN_IS_ERROR_RESPONSE(tdata->msg->hdr.type)))
+ {
+ /* Requested to keep the response in the cache */
+ pj_time_val timeout;
+
+ pj_memset(&tdata->res_timer, 0, sizeof(tdata->res_timer));
+ pj_timer_entry_init(&tdata->res_timer, PJ_TRUE, tdata,
+ &on_cache_timeout);
+
+ timeout.sec = sess->endpt->res_cache_msec / 1000;
+ timeout.msec = sess->endpt->res_cache_msec % 1000;
+
+ status = pj_timer_heap_schedule(sess->endpt->timer_heap,
+ &tdata->res_timer,
+ &timeout);
+ if (status != PJ_SUCCESS) {
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ pj_mutex_unlock(sess->mutex);
+ LOG_ERR_(sess, "Error scheduling response timer", status);
+ return status;
+ }
+
+ pj_list_push_back(&sess->cached_response_list, tdata);
+ }
+
/* Otherwise for non-request message, send directly to transport. */
status = sess->cb.on_send_msg(sess, tdata->pkt, tdata->pkt_size,
server, addr_len);
@@ -619,8 +675,10 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
LOG_ERR_(sess, "Error sending STUN request", status);
}
- /* Destroy */
- pj_stun_msg_destroy_tdata(sess, tdata);
+ /* Destroy only when response is not cached*/
+ if (tdata->res_timer.id == 0) {
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ }
}
@@ -639,8 +697,9 @@ static pj_status_t on_incoming_response(pj_stun_session *sess,
/* Lookup pending client transaction */
tdata = tsx_lookup(sess, msg);
if (tdata == NULL) {
- LOG_ERR_(sess, "STUN error finding transaction", PJ_ENOTFOUND);
- return PJ_ENOTFOUND;
+ PJ_LOG(4,(SNAME(sess),
+ "Transaction not found, response silently discarded"));
+ return PJ_SUCCESS;
}
/* Pass the response to the transaction.
@@ -707,8 +766,34 @@ static pj_status_t on_incoming_request(pj_stun_session *sess,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
+ pj_stun_tx_data *t;
pj_status_t status;
+ /* First lookup response in response cache */
+ t = sess->cached_response_list.next;
+ while (t != &sess->cached_response_list) {
+ if (t->msg_magic == msg->hdr.magic &&
+ pj_memcmp(t->msg_key, msg->hdr.tsx_id,
+ sizeof(msg->hdr.tsx_id))==0)
+ {
+ break;
+ }
+ t = t->next;
+ }
+
+ if (t != &sess->cached_response_list) {
+ /* Found response in the cache */
+ unsigned options;
+
+ PJ_LOG(5,(SNAME(sess),
+ "Request retransmission, sending cached response"));
+
+ options = t->options;
+ options &= ~PJ_STUN_CACHE_RESPONSE;
+ pj_stun_session_send_msg(sess, options, src_addr, src_addr_len, t);
+ return PJ_SUCCESS;
+ }
+
/* Distribute to handler, or respond with Bad Request */
if (sess->cb.on_rx_request) {
status = (*sess->cb.on_rx_request)(sess, in_pkt, in_pkt_len, msg,
@@ -725,7 +810,7 @@ static pj_status_t on_incoming_request(pj_stun_session *sess,
}
}
- return status;
+ return status;
}
diff --git a/pjlib-util/src/pjstun-srv-test/server_main.c b/pjlib-util/src/pjstun-srv-test/server_main.c
index a731c0a2..4825cf8e 100644
--- a/pjlib-util/src/pjstun-srv-test/server_main.c
+++ b/pjlib-util/src/pjstun-srv-test/server_main.c
@@ -156,7 +156,8 @@ static pj_status_t on_rx_binding_request(pj_stun_session *sess,
}
/* Send */
- status = pj_stun_session_send_msg(sess, 0, src_addr, src_addr_len, tdata);
+ status = pj_stun_session_send_msg(sess, PJ_STUN_CACHE_RESPONSE,
+ src_addr, src_addr_len, tdata);
return status;
}