diff options
-rw-r--r-- | pjlib-util/include/pjlib-util/stun_endpoint.h | 34 | ||||
-rw-r--r-- | pjlib-util/include/pjlib-util/stun_session.h | 22 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_endpoint.c | 1 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_session.c | 109 | ||||
-rw-r--r-- | pjlib-util/src/pjstun-srv-test/server_main.c | 3 |
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; } |