📄 stun_session.c
字号:
if (status != PJ_SUCCESS)
return status;
/* Create STUN response message */
status = pj_stun_msg_create_response(tdata->pool, req, err_code, err_msg,
&tdata->msg);
if (status != PJ_SUCCESS) {
pj_pool_release(tdata->pool);
return status;
}
/* copy the request's transaction ID as the transaction key. */
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;
return PJ_SUCCESS;
}
/* Print outgoing message to log */
static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg,
unsigned pkt_size, const pj_sockaddr_t *addr)
{
const char *dst_name;
int dst_port;
const pj_sockaddr *dst = (const pj_sockaddr*)addr;
char buf[512];
if (dst->addr.sa_family == PJ_AF_INET) {
dst_name = pj_inet_ntoa(dst->ipv4.sin_addr);
dst_port = pj_ntohs(dst->ipv4.sin_port);
} else if (dst->addr.sa_family == PJ_AF_INET6) {
dst_name = "IPv6";
dst_port = pj_ntohs(dst->ipv6.sin6_port);
} else {
LOG_ERR_(sess, "Invalid address family", PJ_EINVAL);
return;
}
PJ_LOG(5,(SNAME(sess),
"TX %d bytes STUN message to %s:%d:\n"
"--- begin STUN message ---\n"
"%s"
"--- end of STUN message ---\n",
pkt_size, dst_name, dst_port,
pj_stun_msg_dump(msg, buf, sizeof(buf), NULL)));
}
PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
pj_bool_t cache_res,
const pj_sockaddr_t *server,
unsigned addr_len,
pj_stun_tx_data *tdata)
{
pj_status_t status;
PJ_ASSERT_RETURN(sess && addr_len && server && tdata, PJ_EINVAL);
/* Allocate packet */
tdata->max_len = PJ_STUN_MAX_PKT_LEN;
tdata->pkt = pj_pool_alloc(tdata->pool, tdata->max_len);
/* Start locking the session now */
pj_mutex_lock(sess->mutex);
/* Apply options */
status = apply_msg_options(sess, tdata->pool, tdata->msg);
if (status != PJ_SUCCESS) {
pj_stun_msg_destroy_tdata(sess, tdata);
pj_mutex_unlock(sess->mutex);
LOG_ERR_(sess, "Error applying options", status);
return status;
}
status = get_key(sess, tdata->pool, tdata->msg, &tdata->auth_key);
if (status != PJ_SUCCESS) {
pj_stun_msg_destroy_tdata(sess, tdata);
pj_mutex_unlock(sess->mutex);
LOG_ERR_(sess, "Error getting creadential's key", status);
return status;
}
/* Encode message */
status = pj_stun_msg_encode(tdata->msg, (pj_uint8_t*)tdata->pkt,
tdata->max_len, 0,
&tdata->auth_key,
&tdata->pkt_size);
if (status != PJ_SUCCESS) {
pj_stun_msg_destroy_tdata(sess, tdata);
pj_mutex_unlock(sess->mutex);
LOG_ERR_(sess, "STUN encode() error", status);
return status;
}
/* Dump packet */
dump_tx_msg(sess, tdata->msg, tdata->pkt_size, server);
/* If this is a STUN request message, then send the request with
* a new STUN client transaction.
*/
if (PJ_STUN_IS_REQUEST(tdata->msg->hdr.type)) {
/* Create STUN client transaction */
status = pj_stun_client_tsx_create(sess->cfg, tdata->pool,
&tsx_cb, &tdata->client_tsx);
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
pj_stun_client_tsx_set_data(tdata->client_tsx, (void*)tdata);
/* Save the remote address */
tdata->addr_len = addr_len;
tdata->dst_addr = server;
/* Send the request! */
status = pj_stun_client_tsx_send_msg(tdata->client_tsx, PJ_TRUE,
tdata->pkt, tdata->pkt_size);
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
pj_stun_msg_destroy_tdata(sess, tdata);
pj_mutex_unlock(sess->mutex);
LOG_ERR_(sess, "Error sending STUN request", status);
return status;
}
/* Add to pending request list */
tsx_add(sess, tdata);
} else {
if (cache_res &&
(PJ_STUN_IS_SUCCESS_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->cfg->res_cache_msec / 1000;
timeout.msec = sess->cfg->res_cache_msec % 1000;
status = pj_timer_heap_schedule(sess->cfg->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);
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
LOG_ERR_(sess, "Error sending STUN request", status);
}
/* Destroy only when response is not cached*/
if (tdata->res_timer.id == 0) {
pj_stun_msg_destroy_tdata(sess, tdata);
}
}
pj_mutex_unlock(sess->mutex);
return status;
}
/*
* Cancel outgoing STUN transaction.
*/
PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess,
pj_stun_tx_data *tdata,
pj_bool_t notify,
pj_status_t notify_status)
{
PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL);
PJ_ASSERT_RETURN(!notify || notify_status!=PJ_SUCCESS, PJ_EINVAL);
PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL);
pj_mutex_lock(sess->mutex);
if (notify) {
(sess->cb.on_request_complete)(sess, notify_status, tdata, NULL,
NULL, 0);
}
/* Just destroy tdata. This will destroy the transaction as well */
pj_stun_msg_destroy_tdata(sess, tdata);
pj_mutex_unlock(sess->mutex);
return PJ_SUCCESS;
}
/*
* Explicitly request retransmission of the request.
*/
PJ_DEF(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess,
pj_stun_tx_data *tdata)
{
pj_status_t status;
PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL);
PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL);
pj_mutex_lock(sess->mutex);
status = pj_stun_client_tsx_retransmit(tdata->client_tsx);
pj_mutex_unlock(sess->mutex);
return status;
}
/* Send response */
static pj_status_t send_response(pj_stun_session *sess,
pj_pool_t *pool, pj_stun_msg *response,
const pj_str_t *auth_key,
pj_bool_t retransmission,
const pj_sockaddr_t *addr, unsigned addr_len)
{
pj_uint8_t *out_pkt;
unsigned out_max_len, out_len;
pj_status_t status;
/* Apply options */
if (!retransmission) {
status = apply_msg_options(sess, pool, response);
if (status != PJ_SUCCESS)
return status;
}
/* Alloc packet buffer */
out_max_len = PJ_STUN_MAX_PKT_LEN;
out_pkt = (pj_uint8_t*) pj_pool_alloc(pool, out_max_len);
/* Encode */
status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0,
auth_key, &out_len);
if (status != PJ_SUCCESS) {
LOG_ERR_(sess, "Error encoding message", status);
return status;
}
/* Print log */
dump_tx_msg(sess, response, out_len, addr);
/* Send packet */
status = sess->cb.on_send_msg(sess, out_pkt, out_len, addr, addr_len);
return status;
}
/* Authenticate incoming message */
static pj_status_t authenticate_req(pj_stun_session *sess,
const pj_uint8_t *pkt,
unsigned pkt_len,
const pj_stun_msg *msg,
pj_pool_t *tmp_pool,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
pj_stun_msg *response;
pj_status_t status;
if (PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type) || sess->cred == NULL)
return PJ_SUCCESS;
status = pj_stun_authenticate_request(pkt, pkt_len, msg, sess->cred,
tmp_pool, &response);
if (status != PJ_SUCCESS && response != NULL) {
PJ_LOG(5,(SNAME(sess), "Message authentication failed"));
send_response(sess, tmp_pool, response, NULL, PJ_FALSE,
src_addr, src_addr_len);
}
return status;
}
/* Handle incoming response */
static pj_status_t on_incoming_response(pj_stun_session *sess,
unsigned options,
const pj_uint8_t *pkt,
unsigned pkt_len,
pj_stun_msg *msg,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
pj_stun_tx_data *tdata;
pj_status_t status;
/* Lookup pending client transaction */
tdata = tsx_lookup(sess, msg);
if (tdata == NULL) {
PJ_LOG(5,(SNAME(sess),
"Transaction not found, response silently discarded"));
return PJ_SUCCESS;
}
/* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
* is specified in the option.
*/
if ((options & PJ_STUN_NO_AUTHENTICATE) == 0 && tdata->auth_key.slen != 0
&& pj_stun_auth_valid_for_msg(msg))
{
status = pj_stun_authenticate_response(pkt, pkt_len, msg,
&tdata->auth_key);
if (status != PJ_SUCCESS) {
PJ_LOG(5,(SNAME(sess),
"Response authentication failed"));
return status;
}
}
/* Pass the response to the transaction.
* If the message is accepted, transaction callback will be called,
* and this will call the session callback too.
*/
status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg,
src_addr, src_addr_len);
if (status != PJ_SUCCESS) {
return status;
}
/* If transaction has completed, destroy the transmit data.
* This will remove the transaction from the pending list too.
*/
if (pj_stun_client_tsx_is_complete(tdata->client_tsx)) {
pj_stun_msg_destroy_tdata(sess, tdata);
tdata = NULL;
}
return PJ_SUCCESS;
}
/* For requests, check if we cache the response */
static pj_status_t check_cached_response(pj_stun_session *sess,
pj_pool_t *tmp_pool,
const pj_stun_msg *msg,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
pj_stun_tx_data *t;
/* 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 */
PJ_LOG(5,(SNAME(sess),
"Request retransmission, sending cached response"));
send_response(sess, tmp_pool, t->msg, &t->auth_key, PJ_TRUE,
src_addr, src_addr_len);
return PJ_SUCCESS;
}
return PJ_ENOTFOUND;
}
/* Handle incoming request */
static pj_status_t on_incoming_request(pj_stun_session *sess,
unsigned options,
pj_pool_t *tmp_pool,
const pj_uint8_t *in_pkt,
unsigned in_pkt_len,
const pj_stun_msg *msg,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
pj_status_t status;
/* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
* is specified in the option.
*/
if ((options & PJ_STUN_NO_AUTHENTICATE) == 0) {
status = authenticate_req(sess, (const pj_uint8_t*) in_pkt, in_pkt_len,
msg, tmp_pool, src_addr, src_addr_len);
if (status != PJ_SUCCESS) {
return status;
}
}
/* 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,
src_addr, src_addr_len);
} else {
pj_stun_msg *response;
status = pj_stun_msg_create_response(tmp_pool, msg,
PJ_STUN_SC_BAD_REQUEST, NULL,
&response);
if (status == PJ_SUCCESS && response) {
status = send_response(sess, tmp_pool, response,
NULL, PJ_FALSE, src_addr, src_addr_len);
}
}
return status;
}
/* Handle incoming indication */
static pj_status_t on_incoming_indication(pj_stun_session *sess,
pj_pool_t *tmp_pool,
const pj_uint8_t *in_pkt,
unsigned in_pkt_len,
const pj_stun_msg *msg,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
PJ_UNUSED_ARG(tmp_pool);
/* Distribute to handler */
if (sess->cb.on_rx_indication) {
return (*sess->cb.on_rx_indication)(sess, in_pkt, in_pkt_len, msg,
src_addr, src_addr_len);
} else {
return PJ_SUCCESS;
}
}
PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
const void *packet,
pj_size_t pkt_size,
unsigned options,
unsigned *parsed_len,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
pj_stun_msg *msg, *response;
pj_pool_t *tmp_pool;
char *dump;
pj_status_t status;
PJ_ASSERT_RETURN(sess && packet && pkt_size, PJ_EINVAL);
tmp_pool = pj_pool_create(sess->cfg->pf, "tmpstun", 1024, 1024, NULL);
if (!tmp_pool)
return PJ_ENOMEM;
/* Try to parse the message */
status = pj_stun_msg_decode(tmp_pool, (const pj_uint8_t*)packet,
pkt_size, options,
&msg, parsed_len, &response);
if (status != PJ_SUCCESS) {
LOG_ERR_(sess, "STUN msg_decode() error", status);
if (response) {
send_response(sess, tmp_pool, response, NULL,
PJ_FALSE, src_addr, src_addr_len);
}
pj_pool_release(tmp_pool);
return status;
}
dump = (char*) pj_pool_alloc(tmp_pool, PJ_STUN_MAX_PKT_LEN);
PJ_LOG(5,(SNAME(sess),
"RX STUN message:\n"
"--- begin STUN message ---\n"
"%s"
"--- end of STUN message ---\n",
pj_stun_msg_dump(msg, dump, PJ_STUN_MAX_PKT_LEN, NULL)));
pj_mutex_lock(sess->mutex);
/* For requests, check if we have cached response */
status = check_cached_response(sess, tmp_pool, msg,
src_addr, src_addr_len);
if (status == PJ_SUCCESS) {
goto on_return;
}
/* Handle message */
if (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) ||
PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
{
status = on_incoming_response(sess, options,
(const pj_uint8_t*) packet, pkt_size,
msg, src_addr, src_addr_len);
} else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
status = on_incoming_request(sess, options, tmp_pool,
(const pj_uint8_t*) packet, pkt_size,
msg, src_addr, src_addr_len);
} else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) {
status = on_incoming_indication(sess, tmp_pool,
(const pj_uint8_t*) packet, pkt_size,
msg, src_addr, src_addr_len);
} else {
pj_assert(!"Unexpected!");
status = PJ_EBUG;
}
on_return:
pj_mutex_unlock(sess->mutex);
pj_pool_release(tmp_pool);
return status;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -