From 75d3c1c4773b7b4e35db24ccf695788d871aba2e Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Fri, 23 Feb 2007 01:07:54 +0000 Subject: Ticket #121 and #122: Initial implementation of generic STUN transaction, with Binding request as an example git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@996 74dad513-b988-da41-8d7b-12977e46ad98 --- pjlib-util/src/pjlib-util/stun_endpoint.c | 12 +- pjlib-util/src/pjlib-util/stun_msg.c | 6 +- pjlib-util/src/pjlib-util/stun_msg_dump.c | 34 +-- pjlib-util/src/pjlib-util/stun_transaction.c | 89 +++---- pjlib-util/src/pjstun-client/client_main.c | 136 ++++++++++- pjlib-util/src/pjstun-client/stun_session.c | 226 ++++++++++++++---- pjlib-util/src/pjstun-client/stun_session.h | 341 +++++++++++++++++++++++++-- pjlib-util/src/pjstun-srv/server_main.c | 16 +- 8 files changed, 703 insertions(+), 157 deletions(-) (limited to 'pjlib-util/src') diff --git a/pjlib-util/src/pjlib-util/stun_endpoint.c b/pjlib-util/src/pjlib-util/stun_endpoint.c index 6a1de943..033a2839 100644 --- a/pjlib-util/src/pjlib-util/stun_endpoint.c +++ b/pjlib-util/src/pjlib-util/stun_endpoint.c @@ -25,11 +25,11 @@ /* * Create a STUN endpoint instance. */ -PJ_DEF(pj_status_t) pj_stun_endpt_create( pj_pool_factory *factory, - unsigned options, - pj_ioqueue_t *ioqueue, - pj_timer_heap_t *timer_heap, - pj_stun_endpoint **p_endpt) +PJ_DEF(pj_status_t) pj_stun_endpoint_create( pj_pool_factory *factory, + unsigned options, + pj_ioqueue_t *ioqueue, + pj_timer_heap_t *timer_heap, + pj_stun_endpoint **p_endpt) { pj_pool_t *pool; pj_stun_endpoint *endpt; @@ -57,7 +57,7 @@ PJ_DEF(pj_status_t) pj_stun_endpt_create( pj_pool_factory *factory, /* * Destroy STUN endpoint instance. */ -PJ_DEF(pj_status_t) pj_stun_endpt_destroy(pj_stun_endpoint *endpt) +PJ_DEF(pj_status_t) pj_stun_endpoint_destroy(pj_stun_endpoint *endpt) { PJ_ASSERT_RETURN(endpt, PJ_EINVAL); diff --git a/pjlib-util/src/pjlib-util/stun_msg.c b/pjlib-util/src/pjlib-util/stun_msg.c index 9abb2a4c..ec92f6b6 100644 --- a/pjlib-util/src/pjlib-util/stun_msg.c +++ b/pjlib-util/src/pjlib-util/stun_msg.c @@ -1332,8 +1332,10 @@ PJ_DEF(pj_status_t) pj_stun_msg_check(const void *pdu, unsigned pdu_len, { pj_stun_msg_hdr *hdr; - PJ_ASSERT_RETURN(pdu && pdu_len > sizeof(pj_stun_msg_hdr), - PJLIB_UTIL_ESTUNINMSGLEN); + PJ_ASSERT_RETURN(pdu, PJ_EINVAL); + + if (pdu_len < sizeof(pj_stun_msg_hdr)) + return PJLIB_UTIL_ESTUNINMSGLEN; PJ_UNUSED_ARG(options); diff --git a/pjlib-util/src/pjlib-util/stun_msg_dump.c b/pjlib-util/src/pjlib-util/stun_msg_dump.c index 2def62e4..21d569da 100644 --- a/pjlib-util/src/pjlib-util/stun_msg_dump.c +++ b/pjlib-util/src/pjlib-util/stun_msg_dump.c @@ -32,7 +32,7 @@ static int print_attr(char *buffer, unsigned length, char *p = buffer, *end = buffer + length; int len; - len = pj_ansi_snprintf(buffer, end-p, + len = pj_ansi_snprintf(p, end-p, " %s: length=%d", pj_stun_get_attr_name(ahdr->type), (int)ahdr->length); @@ -58,16 +58,16 @@ static int print_attr(char *buffer, unsigned length, attr = (const pj_stun_generic_ip_addr_attr*)ahdr; if (attr->addr.addr.sa_family == PJ_AF_INET) { - len = pj_ansi_snprintf(buffer, end-p, + len = pj_ansi_snprintf(p, end-p, ", IPv4 addr=%s:%d\n", pj_inet_ntoa(attr->addr.ipv4.sin_addr), pj_ntohs(attr->addr.ipv4.sin_port)); } else if (attr->addr.addr.sa_family == PJ_AF_INET6) { - len = pj_ansi_snprintf(buffer, end-p, + len = pj_ansi_snprintf(p, end-p, ", IPv6 addr present\n"); } else { - len = pj_ansi_snprintf(buffer, end-p, + len = pj_ansi_snprintf(p, end-p, ", INVALID ADDRESS FAMILY!\n"); } } @@ -87,8 +87,8 @@ static int print_attr(char *buffer, unsigned length, const pj_stun_generic_uint_attr *attr; attr = (const pj_stun_generic_uint_attr*)ahdr; - len = pj_ansi_snprintf(buffer, end-p, - ", value=%d (%x)\n", + len = pj_ansi_snprintf(p, end-p, + ", value=%d (0x%x)\n", (pj_uint32_t)attr->value, (pj_uint32_t)attr->value); } @@ -103,7 +103,7 @@ static int print_attr(char *buffer, unsigned length, const pj_stun_generic_string_attr *attr; attr = (pj_stun_generic_string_attr*)ahdr; - len = pj_ansi_snprintf(buffer, end-p, + len = pj_ansi_snprintf(p, end-p, ", value=\"%.*s\"\n", (int)attr->value.slen, attr->value.ptr); @@ -115,7 +115,7 @@ static int print_attr(char *buffer, unsigned length, const pj_stun_error_code_attr *attr; attr = (const pj_stun_error_code_attr*) ahdr; - len = pj_ansi_snprintf(buffer, end-p, + len = pj_ansi_snprintf(p, end-p, ", err_code=%d, reason=\"%.*s\"\n", attr->err_class*100 + attr->number, (int)attr->reason.slen, @@ -130,12 +130,12 @@ static int print_attr(char *buffer, unsigned length, attr = (const pj_stun_unknown_attr*) ahdr; - len = pj_ansi_snprintf(buffer, end-p, + len = pj_ansi_snprintf(p, end-p, ", unknown list:"); APPLY(); for (j=0; jattr_count; ++j) { - len = pj_ansi_snprintf(buffer, end-p, + len = pj_ansi_snprintf(p, end-p, " %d", (int)attr->attrs[j]); APPLY(); @@ -147,7 +147,7 @@ static int print_attr(char *buffer, unsigned length, case PJ_STUN_ATTR_DATA: case PJ_STUN_ATTR_USE_CANDIDATE: default: - len = pj_ansi_snprintf(buffer, end-p, "\n"); + len = pj_ansi_snprintf(p, end-p, "\n"); break; } @@ -166,7 +166,8 @@ on_return: */ PJ_DEF(char*) pj_stun_msg_dump(const pj_stun_msg *msg, char *buffer, - unsigned *length) + unsigned length, + unsigned *printed_len) { char *p, *end; int len; @@ -175,7 +176,7 @@ PJ_DEF(char*) pj_stun_msg_dump(const pj_stun_msg *msg, PJ_ASSERT_RETURN(msg && buffer && length, NULL); p = buffer; - end = buffer + (*length); + end = buffer + length; len = pj_ansi_snprintf(p, end-p, "STUN %s %s\n", pj_stun_get_method_name(msg->hdr.type), @@ -183,8 +184,8 @@ PJ_DEF(char*) pj_stun_msg_dump(const pj_stun_msg *msg, APPLY(); len = pj_ansi_snprintf(p, end-p, - " Hdr: length=%d, magic=%x, tsx_id=%x %x %x\n" - " Attributes:\n", + " Hdr: length=%d, magic=%08x, tsx_id=%08x %08x %08x\n" + " Attributes:\n", msg->hdr.length, msg->hdr.magic, *(pj_uint32_t*)&msg->hdr.tsx_id[0], @@ -199,7 +200,8 @@ PJ_DEF(char*) pj_stun_msg_dump(const pj_stun_msg *msg, on_return: *p = '\0'; - *length = (p-buffer); + if (printed_len) + *printed_len = (p-buffer); return buffer; } diff --git a/pjlib-util/src/pjlib-util/stun_transaction.c b/pjlib-util/src/pjlib-util/stun_transaction.c index 701122fe..02f33177 100644 --- a/pjlib-util/src/pjlib-util/stun_transaction.c +++ b/pjlib-util/src/pjlib-util/stun_transaction.c @@ -31,19 +31,18 @@ struct pj_stun_client_tsx { char obj_name[PJ_MAX_OBJ_NAME]; - pj_pool_t *pool; pj_stun_endpoint *endpt; pj_stun_tsx_cb cb; void *user_data; - pj_uint32_t tsx_id[4]; - + pj_bool_t complete; + \ pj_bool_t require_retransmit; pj_timer_entry timer; unsigned transmit_count; pj_time_val retransmit_time; - pj_uint8_t last_pkt[PJ_STUN_MAX_PKT_LEN]; + void *last_pkt; unsigned last_pkt_size; }; @@ -65,25 +64,23 @@ static void stun_perror(pj_stun_client_tsx *tsx, const char *title, * Create a STUN client transaction. */ PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_endpoint *endpt, + pj_pool_t *pool, const pj_stun_tsx_cb *cb, pj_stun_client_tsx **p_tsx) { - pj_pool_t *pool; pj_stun_client_tsx *tsx; PJ_ASSERT_RETURN(endpt && cb && p_tsx, PJ_EINVAL); PJ_ASSERT_RETURN(cb->on_send_msg, PJ_EINVAL); - pool = pj_pool_create(endpt->pf, "tsx", 1000, 1000, NULL); tsx = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_client_tsx); - tsx->pool = pool; tsx->endpt = endpt; pj_memcpy(&tsx->cb, cb, sizeof(*cb)); tsx->timer.cb = &retransmit_timer_callback; tsx->timer.user_data = tsx; - pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "stuntsx%p", pool); + pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "stuntsx%p", tsx); *p_tsx = tsx; @@ -103,11 +100,20 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx) pj_timer_heap_cancel(tsx->endpt->timer_heap, &tsx->timer); tsx->timer.id = 0; } - pj_pool_release(tsx->pool); return PJ_SUCCESS; } +/* + * Check if transaction has completed. + */ +PJ_DEF(pj_bool_t) pj_stun_client_tsx_is_complete(pj_stun_client_tsx *tsx) +{ + PJ_ASSERT_RETURN(tsx, PJ_FALSE); + return tsx->complete; +} + + /* * Set user data. */ @@ -145,13 +151,13 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx) tsx->retransmit_time.sec = 0; tsx->retransmit_time.msec = tsx->endpt->rto_msec; - } else if (tsx->transmit_count < PJ_STUN_MAX_RETRANSMIT_COUNT) { + } else if (tsx->transmit_count < PJ_STUN_MAX_RETRANSMIT_COUNT-1) { unsigned msec; msec = PJ_TIME_VAL_MSEC(tsx->retransmit_time); - msec = (msec >> 1) + 100; + msec = (msec << 1) + 100; tsx->retransmit_time.sec = msec / 1000; - tsx->retransmit_time.msec = msec % 100; + tsx->retransmit_time.msec = msec % 1000; } else { tsx->retransmit_time.sec = PJ_STUN_TIMEOUT_VALUE / 1000; @@ -161,13 +167,14 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx) /* Schedule timer first because when send_msg() failed we can * cancel it (as opposed to when schedule_timer() failed we cannot * cancel transmission). - */ + */; status = pj_timer_heap_schedule(tsx->endpt->timer_heap, &tsx->timer, &tsx->retransmit_time); if (status != PJ_SUCCESS) { tsx->timer.id = 0; return status; } + tsx->timer.id = TIMER_ACTIVE; } @@ -195,24 +202,15 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx) */ PJ_DEF(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx, pj_bool_t retransmit, - const pj_stun_msg *msg) + void *pkt, + unsigned pkt_len) { - pj_status_t status; - - PJ_ASSERT_RETURN(tsx && msg, PJ_EINVAL); - PJ_ASSERT_RETURN(tsx->timer.id != 0, PJ_EBUSY); + PJ_ASSERT_RETURN(tsx && pkt && pkt_len, PJ_EINVAL); + PJ_ASSERT_RETURN(tsx->timer.id == 0, PJ_EBUSY); /* Encode message */ - status = pj_stun_msg_encode(msg, tsx->last_pkt, sizeof(tsx->last_pkt), - 0, &tsx->last_pkt_size); - if (status != PJ_SUCCESS) { - stun_perror(tsx, "STUN msg_encode() failed", status); - return status; - } - - /* Update STUN transaction ID */ - tsx->tsx_id[0] = msg->hdr.magic; - pj_memcpy(&tsx->tsx_id[1], msg->hdr.tsx_id, 12); + tsx->last_pkt = pkt; + tsx->last_pkt_size = pkt_len; /* Update STUN retransmit flag */ tsx->require_retransmit = retransmit; @@ -235,6 +233,7 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, /* Retransmission count exceeded. Transaction has failed */ tsx->timer.id = 0; PJ_LOG(4,(tsx->obj_name, "STUN timeout waiting for response")); + tsx->complete = PJ_TRUE; if (tsx->cb.on_complete) { tsx->cb.on_complete(tsx, PJLIB_UTIL_ESTUNNOTRESPOND, NULL); } @@ -245,6 +244,7 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, status = tsx_transmit_msg(tsx); if (status != PJ_SUCCESS) { tsx->timer.id = 0; + tsx->complete = PJ_TRUE; if (tsx->cb.on_complete) { tsx->cb.on_complete(tsx, status, NULL); } @@ -271,12 +271,6 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, return PJLIB_UTIL_ESTUNNOTRESPONSE; } - /* Compare response's transaction ID */ - if (msg->hdr.magic != tsx->tsx_id[0] || - pj_memcmp(msg->hdr.tsx_id, &tsx->tsx_id[1], 12) != 0) - { - return PJLIB_UTIL_ESTUNINVALIDID; - } /* We have a response with matching transaction ID. * We can cancel retransmit timer now. @@ -311,6 +305,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, } /* Call callback */ + tsx->complete = PJ_TRUE; if (tsx->cb.on_complete) { tsx->cb.on_complete(tsx, status, msg); } @@ -319,29 +314,3 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, } - -/* - * Notify the STUN transaction about the arrival of STUN response. - */ -PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_pkt(pj_stun_client_tsx *tsx, - const void *packet, - pj_size_t pkt_size, - unsigned *parsed_len) -{ - pj_stun_msg *msg; - pj_status_t status; - - PJ_ASSERT_RETURN(tsx && packet && pkt_size, PJ_EINVAL); - - /* Try to parse the message */ - status = pj_stun_msg_decode(tsx->pool, (const pj_uint8_t*)packet, - pkt_size, 0, &msg, parsed_len, - NULL, NULL, NULL); - if (status != PJ_SUCCESS) { - stun_perror(tsx, "STUN msg_decode() error", status); - return status; - } - - return pj_stun_client_tsx_on_rx_msg(tsx, msg); -} - diff --git a/pjlib-util/src/pjstun-client/client_main.c b/pjlib-util/src/pjstun-client/client_main.c index 7dc0540d..5f9d7d4c 100644 --- a/pjlib-util/src/pjstun-client/client_main.c +++ b/pjlib-util/src/pjstun-client/client_main.c @@ -18,8 +18,142 @@ */ #include #include +#include "stun_session.h" + +#include #define THIS_FILE "client_main.c" -#define MAX_THREADS 8 + + +static my_perror(const char *title, pj_status_t status) +{ + char errmsg[PJ_ERR_MSG_SIZE]; + pj_strerror(status, errmsg, sizeof(errmsg)); + + PJ_LOG(3,(THIS_FILE, "%s: %s", title, errmsg)); +} + +static pj_status_t on_send_msg(pj_stun_tx_data *tdata, + const void *pkt, + pj_size_t pkt_size, + unsigned addr_len, + const pj_sockaddr_t *dst_addr) +{ + pj_sock_t sock; + pj_ssize_t len; + pj_status_t status; + + sock = (pj_sock_t) pj_stun_session_get_user_data(tdata->sess); + + len = pkt_size; + status = pj_sock_sendto(sock, pkt, &len, 0, dst_addr, addr_len); + + if (status != PJ_SUCCESS) + my_perror("Error sending packet", status); + + return status; +} + +static void on_bind_response(pj_stun_session *sess, + pj_status_t status, + pj_stun_tx_data *request, + const pj_stun_msg *response) +{ + my_perror("on_bind_response()", status); +} + +int main() +{ + pj_stun_endpoint *endpt = NULL; + pj_pool_t *pool = NULL; + pj_caching_pool cp; + pj_timer_heap_t *th = NULL; + pj_stun_session *sess; + pj_sock_t sock = PJ_INVALID_SOCKET; + pj_sockaddr_in addr; + pj_stun_session_cb stun_cb; + pj_stun_tx_data *tdata; + pj_str_t s; + pj_status_t status; + + status = pj_init(); + status = pjlib_util_init(); + + pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); + + pool = pj_pool_create(&cp.factory, NULL, 1000, 1000, NULL); + + status = pj_timer_heap_create(pool, 1000, &th); + pj_assert(status == PJ_SUCCESS); + + status = pj_stun_endpoint_create(&cp.factory, 0, NULL, th, &endpt); + pj_assert(status == PJ_SUCCESS); + + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock); + pj_assert(status == PJ_SUCCESS); + + status = pj_sockaddr_in_init(&addr, pj_cstr(&s, "127.0.0.1"), PJ_STUN_PORT); + pj_assert(status == PJ_SUCCESS); + + pj_memset(&stun_cb, 0, sizeof(stun_cb)); + stun_cb.on_send_msg = &on_send_msg; + stun_cb.on_bind_response = &on_bind_response; + + status = pj_stun_session_create(endpt, NULL, &stun_cb, &sess); + pj_assert(status == PJ_SUCCESS); + + pj_stun_session_set_user_data(sess, (void*)sock); + + status = pj_stun_session_create_bind_req(sess, &tdata); + pj_assert(status == PJ_SUCCESS); + + status = pj_stun_session_send_msg(sess, 0, sizeof(addr), &addr, tdata); + pj_assert(status == PJ_SUCCESS); + + while (1) { + pj_fd_set_t rset; + int n; + pj_time_val timeout; + + if (kbhit()) { + if (_getch()==27) + break; + } + + PJ_FD_ZERO(&rset); + PJ_FD_SET(sock, &rset); + + timeout.sec = 0; timeout.msec = 100; + + n = pj_sock_select(FD_SETSIZE, &rset, NULL, NULL, &timeout); + + if (PJ_FD_ISSET(sock, &rset)) { + char pkt[512]; + pj_ssize_t len; + + len = sizeof(pkt); + status = pj_sock_recv(sock, pkt, &len, 0); + if (status == PJ_SUCCESS) { + pj_stun_session_on_rx_pkt(sess, pkt, len, NULL); + } + } + + pj_timer_heap_poll(th, NULL); + } + +on_return: + if (sock != PJ_INVALID_SOCKET) + pj_sock_close(sock); + if (endpt) + pj_stun_endpoint_destroy(endpt); + if (th) + pj_timer_heap_destroy(th); + if (pool) + pj_pool_release(pool); + pj_caching_pool_destroy(&cp); + + return 0; +} + diff --git a/pjlib-util/src/pjstun-client/stun_session.c b/pjlib-util/src/pjstun-client/stun_session.c index 571b723d..101bb0b4 100644 --- a/pjlib-util/src/pjstun-client/stun_session.c +++ b/pjlib-util/src/pjstun-client/stun_session.c @@ -26,11 +26,14 @@ struct pj_stun_session pj_stun_session_cb cb; void *user_data; - pj_str_t realm; - pj_str_t username; - pj_str_t password; + /* Long term credential */ + pj_str_t l_realm; + pj_str_t l_username; + pj_str_t l_password; - pj_bool_t fingerprint_enabled; + /* Short term credential */ + pj_str_t s_username; + pj_str_t s_password; pj_stun_tx_data pending_request_list; }; @@ -64,8 +67,8 @@ static void stun_perror(pj_stun_session *sess, const char *title, static void tsx_on_complete(pj_stun_client_tsx *tsx, - pj_status_t status, - const pj_stun_msg *response); + pj_status_t status, + const pj_stun_msg *response); static pj_status_t tsx_on_send_msg(pj_stun_client_tsx *tsx, const void *stun_pkt, pj_size_t pkt_size); @@ -164,6 +167,7 @@ static void destroy_tdata(pj_stun_tx_data *tdata) static pj_status_t session_apply_req(pj_stun_session *sess, pj_pool_t *pool, + unsigned options, pj_stun_msg *msg) { pj_status_t status; @@ -171,32 +175,30 @@ static pj_status_t session_apply_req(pj_stun_session *sess, /* From draft-ietf-behave-rfc3489bis-05.txt * Section 8.3.1. Formulating the Request Message */ - if (sess->realm.slen || sess->username.slen) { + if (options & PJ_STUN_USE_LONG_TERM_CRED) { pj_stun_generic_string_attr *auname; pj_stun_msg_integrity_attr *amsgi; + pj_stun_generic_string_attr *arealm; /* Create and add USERNAME attribute */ status = pj_stun_generic_string_attr_create(sess->pool, PJ_STUN_ATTR_USERNAME, - &sess->username, + &sess->l_username, &auname); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); status = pj_stun_msg_add_attr(msg, &auname->hdr); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); - if (sess->realm.slen) { - /* Add REALM only when long term credential is used */ - pj_stun_generic_string_attr *arealm; - status = pj_stun_generic_string_attr_create(sess->pool, - PJ_STUN_ATTR_REALM, - &sess->realm, - &arealm); - PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); - - status = pj_stun_msg_add_attr(msg, &arealm->hdr); - PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); - } + /* Add REALM only when long term credential is used */ + status = pj_stun_generic_string_attr_create(sess->pool, + PJ_STUN_ATTR_REALM, + &sess->l_realm, + &arealm); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + + status = pj_stun_msg_add_attr(msg, &arealm->hdr); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); /* Add MESSAGE-INTEGRITY attribute */ status = pj_stun_msg_integrity_attr_create(sess->pool, &amsgi); @@ -205,12 +207,34 @@ static pj_status_t session_apply_req(pj_stun_session *sess, status = pj_stun_msg_add_attr(msg, &amsgi->hdr); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); - PJ_TODO(COMPUTE_MESSAGE_INTEGRITY); + PJ_TODO(COMPUTE_MESSAGE_INTEGRITY1); + + } else if (options & PJ_STUN_USE_SHORT_TERM_CRED) { + pj_stun_generic_string_attr *auname; + pj_stun_msg_integrity_attr *amsgi; + + /* Create and add USERNAME attribute */ + status = pj_stun_generic_string_attr_create(sess->pool, + PJ_STUN_ATTR_USERNAME, + &sess->s_username, + &auname); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + + status = pj_stun_msg_add_attr(msg, &auname->hdr); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + + /* Add MESSAGE-INTEGRITY attribute */ + status = pj_stun_msg_integrity_attr_create(sess->pool, &amsgi); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + status = pj_stun_msg_add_attr(msg, &amsgi->hdr); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + + PJ_TODO(COMPUTE_MESSAGE_INTEGRITY2); } /* Add FINGERPRINT attribute if necessary */ - if (sess->fingerprint_enabled) { + if (options & PJ_STUN_USE_FINGERPRINT) { pj_stun_fingerprint_attr *af; status = pj_stun_generic_uint_attr_create(sess->pool, @@ -226,9 +250,49 @@ static pj_status_t session_apply_req(pj_stun_session *sess, } +static void tsx_on_complete(pj_stun_client_tsx *tsx, + pj_status_t status, + const pj_stun_msg *response) +{ + pj_stun_tx_data *tdata; + + tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx); + + switch (PJ_STUN_GET_METHOD(tdata->msg->hdr.type)) { + case PJ_STUN_BINDING_METHOD: + tdata->sess->cb.on_bind_response(tdata->sess, status, tdata, response); + break; + case PJ_STUN_ALLOCATE_METHOD: + tdata->sess->cb.on_allocate_response(tdata->sess, status, + tdata, response); + break; + case PJ_STUN_SET_ACTIVE_DESTINATION_METHOD: + tdata->sess->cb.on_set_active_destination_response(tdata->sess, status, + tdata, response); + break; + case PJ_STUN_CONNECT_METHOD: + tdata->sess->cb.on_connect_response(tdata->sess, status, tdata, + response); + break; + default: + pj_assert(!"Unknown method"); + break; + } +} + +static pj_status_t tsx_on_send_msg(pj_stun_client_tsx *tsx, + const void *stun_pkt, + pj_size_t pkt_size) +{ + pj_stun_tx_data *tdata; + tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx); + return tdata->sess->cb.on_send_msg(tdata, stun_pkt, pkt_size, + tdata->addr_len, tdata->dst_addr); +} +/* **************************************************************************/ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_endpoint *endpt, const char *name, @@ -260,6 +324,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_endpoint *endpt, PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess) { PJ_ASSERT_RETURN(sess, PJ_EINVAL); + pj_pool_release(sess->pool); return PJ_SUCCESS; @@ -280,26 +345,34 @@ PJ_DEF(void*) pj_stun_session_get_user_data(pj_stun_session *sess) return sess->user_data; } -PJ_DEF(pj_status_t) pj_stun_session_set_credential( pj_stun_session *sess, - const pj_str_t *realm, - const pj_str_t *user, - const pj_str_t *passwd) +PJ_DEF(pj_status_t) +pj_stun_session_set_long_term_credential(pj_stun_session *sess, + const pj_str_t *realm, + const pj_str_t *user, + const pj_str_t *passwd) { - pj_str_t empty = { NULL, 0 }; + pj_str_t nil = { NULL, 0 }; PJ_ASSERT_RETURN(sess, PJ_EINVAL); - pj_strdup_with_null(sess->pool, &sess->realm, realm ? realm : &empty); - pj_strdup_with_null(sess->pool, &sess->username, user ? user : &empty); - pj_strdup_with_null(sess->pool, &sess->password, passwd ? passwd : &empty); + pj_strdup_with_null(sess->pool, &sess->l_realm, realm ? realm : &nil); + pj_strdup_with_null(sess->pool, &sess->l_username, user ? user : &nil); + pj_strdup_with_null(sess->pool, &sess->l_password, passwd ? passwd : &nil); return PJ_SUCCESS; } -PJ_DEF(pj_status_t) pj_stun_session_enable_fingerprint(pj_stun_session *sess, - pj_bool_t enabled) + +PJ_DEF(pj_status_t) +pj_stun_session_set_short_term_credential(pj_stun_session *sess, + const pj_str_t *user, + const pj_str_t *passwd) { + pj_str_t nil = { NULL, 0 }; + PJ_ASSERT_RETURN(sess, PJ_EINVAL); - sess->fingerprint_enabled = enabled; + pj_strdup_with_null(sess->pool, &sess->s_username, user ? user : &nil); + pj_strdup_with_null(sess->pool, &sess->s_password, passwd ? passwd : &nil); + return PJ_SUCCESS; } @@ -307,7 +380,6 @@ PJ_DEF(pj_status_t) pj_stun_session_enable_fingerprint(pj_stun_session *sess, PJ_DEF(pj_status_t) pj_stun_session_create_bind_req(pj_stun_session *sess, pj_stun_tx_data **p_tdata) { - pj_pool_t *pool; pj_stun_tx_data *tdata; pj_status_t status; @@ -317,12 +389,6 @@ PJ_DEF(pj_status_t) pj_stun_session_create_bind_req(pj_stun_session *sess, if (status != PJ_SUCCESS) return status; - status = session_apply_req(sess, pool, tdata->msg); - if (status != PJ_SUCCESS) { - destroy_tdata(tdata); - return status; - } - *p_tdata = tdata; return PJ_SUCCESS; } @@ -367,6 +433,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_data_ind( pj_stun_session *sess, } PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, + unsigned options, unsigned addr_len, const pj_sockaddr_t *server, pj_stun_tx_data *tdata) @@ -375,9 +442,12 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, 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); + if (PJ_LOG_MAX_LEVEL >= 5) { - char buf[512]; - unsigned buflen = sizeof(buf); + char *buf = (char*) tdata->pkt; const char *dst_name; int dst_port; const pj_sockaddr *dst = (const pj_sockaddr*)server; @@ -397,11 +467,29 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, PJ_LOG(5,(SNAME(sess), "Sending STUN message to %s:%d:\n" - "%s\n", + "--- begin STUN message ---\n" + "%s" + "--- end of STUN message ---\n", dst_name, dst_port, - pj_stun_msg_dump(tdata->msg, buf, &buflen))); + pj_stun_msg_dump(tdata->msg, buf, tdata->max_len, NULL))); } + /* Apply options */ + status = session_apply_req(sess, tdata->pool, options, tdata->msg); + if (status != PJ_SUCCESS) { + LOG_ERR_(sess, "Error applying options", status); + destroy_tdata(tdata); + return status; + } + + /* Encode message */ + status = pj_stun_msg_encode(tdata->msg, tdata->pkt, tdata->max_len, + 0, &tdata->pkt_size); + if (status != PJ_SUCCESS) { + LOG_ERR_(sess, "STUN encode() error", status); + destroy_tdata(tdata); + return status; + } /* If this is a STUN request message, then send the request with * a new STUN client transaction. @@ -409,16 +497,21 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, if (PJ_STUN_IS_REQUEST(tdata->msg->hdr.type)) { /* Create STUN client transaction */ - status = pj_stun_client_tsx_create(sess->endpt, &tsx_cb, - &tdata->client_tsx); + status = pj_stun_client_tsx_create(sess->endpt, 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->msg); + tdata->pkt, tdata->pkt_size); if (status != PJ_SUCCESS && status != PJ_EPENDING) { LOG_ERR_(sess, "Error sending STUN request", status); + destroy_tdata(tdata); return status; } @@ -427,7 +520,14 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, } else { /* Otherwise for non-request message, send directly to transport. */ - status = sess->cb.on_send_msg(tdata, addr_len, server); + status = sess->cb.on_send_msg(tdata, tdata->pkt, tdata->pkt_size, + addr_len, server); + + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + LOG_ERR_(sess, "Error sending STUN request", status); + destroy_tdata(tdata); + return status; + } } @@ -441,19 +541,36 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, unsigned *parsed_len) { pj_stun_msg *msg; + 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->endpt->pf, "tmpstun", 1024, 1024, NULL); + if (!tmp_pool) + return PJ_ENOMEM; + /* Try to parse the message */ - status = pj_stun_msg_decode(tsx->pool, (const pj_uint8_t*)packet, + status = pj_stun_msg_decode(tmp_pool, (const pj_uint8_t*)packet, pkt_size, 0, &msg, parsed_len, NULL, NULL, NULL); if (status != PJ_SUCCESS) { LOG_ERR_(sess, "STUN msg_decode() error", status); + pj_pool_release(tmp_pool); return status; } + dump = pj_pool_alloc(tmp_pool, PJ_STUN_MAX_PKT_LEN); + + PJ_LOG(4,(SNAME(sess), + "RX STUN message:\n" + "--- begin STUN message ---" + "%s" + "--- end of STUN message ---\n", + pj_stun_msg_dump(msg, dump, PJ_STUN_MAX_PKT_LEN, NULL))); + + if (PJ_STUN_IS_RESPONSE(msg->hdr.type) || PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) { @@ -463,6 +580,7 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, tdata = tsx_lookup(sess, msg); if (tdata == NULL) { LOG_ERR_(sess, "STUN error finding transaction", PJ_ENOTFOUND); + pj_pool_release(tmp_pool); return PJ_ENOTFOUND; } @@ -471,8 +589,10 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, * and this will call the session callback too. */ status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg); - if (status != PJ_SUCCESS) + if (status != PJ_SUCCESS) { + pj_pool_release(tmp_pool); return status; + } /* If transaction has completed, destroy the transmit data. * This will remove the transaction from the pending list too. @@ -482,18 +602,22 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, tdata = NULL; } + pj_pool_release(tmp_pool); return PJ_SUCCESS; } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) { + PJ_TODO(HANDLE_INCOMING_STUN_REQUEST); } else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) { + PJ_TODO(HANDLE_INCOMING_STUN_INDICATION); } else { pj_assert(!"Unexpected!"); - return PJ_EBUG; } + pj_pool_release(tmp_pool); + return PJ_ENOTSUP; } diff --git a/pjlib-util/src/pjstun-client/stun_session.h b/pjlib-util/src/pjstun-client/stun_session.h index f9759cb9..057b35aa 100644 --- a/pjlib-util/src/pjstun-client/stun_session.h +++ b/pjlib-util/src/pjstun-client/stun_session.h @@ -25,84 +25,389 @@ #include +/** Forward declaration for pj_stun_tx_data */ typedef struct pj_stun_tx_data pj_stun_tx_data; + +/** Forward declaration for pj_stun_session */ typedef struct pj_stun_session pj_stun_session; +/** + * This is the callback to be registered to pj_stun_session, to send + * outgoing message and to receive various notifications from the STUN + * session. + */ typedef struct pj_stun_session_cb { + /** + * Callback to be called by the STUN session to send outgoing message. + * + * @param tdata The STUN transmit data containing the original + * STUN message + * @param pkt Packet to be sent. + * @param pkt_size Size of the packet to be sent. + * @param addr_len Length of destination address. + * @param dst_addr The destination address. + * + * @return The callback should return the status of the + * packet sending. + */ pj_status_t (*on_send_msg)(pj_stun_tx_data *tdata, + const void *pkt, + pj_size_t pkt_size, unsigned addr_len, const pj_sockaddr_t *dst_addr); - void (*on_bind_response)(void *user_data, pj_status_t status, pj_stun_msg *response); - void (*on_allocate_response)(void *user_data, pj_status_t status, pj_stun_msg *response); - void (*on_set_active_destination_response)(void *user_data, pj_status_t status, pj_stun_msg *response); - void (*on_connect_response)(void *user_data, pj_status_t status, pj_stun_msg *response); + /** + * Callback to be called when Binding response is received or the + * transaction has timed out. + * + * @param sess The STUN session. + * @param status Status of the request. If the value if not + * PJ_SUCCESS, the transaction has timed-out + * or other error has occurred, and the response + * argument may be NULL. + * @param request The original STUN request. + * @param response The response message, on successful transaction. + */ + void (*on_bind_response)(pj_stun_session *sess, + pj_status_t status, + pj_stun_tx_data *request, + const pj_stun_msg *response); + + /** + * Callback to be called when Allocate response is received or the + * transaction has timed out. + * + * @param sess The STUN session. + * @param status Status of the request. If the value if not + * PJ_SUCCESS, the transaction has timed-out + * or other error has occurred, and the response + * argument may be NULL. + * @param request The original STUN request. + * @param response The response message, on successful transaction. + */ + void (*on_allocate_response)(pj_stun_session *sess, + pj_status_t status, + pj_stun_tx_data *request, + const pj_stun_msg *response); + + /** + * Callback to be called when Set Active Destination response is received + * or the transaction has timed out. + * + * @param sess The STUN session. + * @param status Status of the request. If the value if not + * PJ_SUCCESS, the transaction has timed-out + * or other error has occurred, and the response + * argument may be NULL. + * @param request The original STUN request. + * @param response The response message, on successful transaction. + */ + void (*on_set_active_destination_response)(pj_stun_session *sess, + pj_status_t status, + pj_stun_tx_data *request, + const pj_stun_msg *response); + + /** + * Callback to be called when Connect response is received or the + * transaction has timed out. + * + * @param sess The STUN session. + * @param status Status of the request. If the value if not + * PJ_SUCCESS, the transaction has timed-out + * or other error has occurred, and the response + * argument may be NULL. + * @param request The original STUN request. + * @param response The response message, on successful transaction. + */ + void (*on_connect_response)( pj_stun_session *sess, + pj_status_t status, + pj_stun_tx_data *request, + const pj_stun_msg *response); } pj_stun_session_cb; +/** + * This structure describe the outgoing STUN transmit data to carry the + * message to be sent. + */ struct pj_stun_tx_data { PJ_DECL_LIST_MEMBER(struct pj_stun_tx_data); - pj_pool_t *pool; - pj_stun_session *sess; - pj_stun_msg *msg; - void *user_data; + pj_pool_t *pool; /**< Pool. */ + pj_stun_session *sess; /**< The STUN session. */ + pj_stun_msg *msg; /**< The STUN message. */ + 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_stun_client_tsx *client_tsx; - pj_uint8_t client_key[12]; + void *pkt; /**< The STUN packet. */ + unsigned max_len; /**< Length of packet buffer. */ + unsigned pkt_size; /**< The actual length of STUN pkt. */ + + unsigned addr_len; /**< Length of destination address. */ + const pj_sockaddr_t *dst_addr; /**< Destination address. */ }; +/** + * Options that can be specified when creating or sending outgoing STUN + * messages. These options may be specified as bitmask. + */ +enum pj_stun_session_option +{ + /** + * Add short term credential to the message. This option may not be used + * together with PJ_STUN_USE_LONG_TERM_CRED option. + */ + PJ_STUN_USE_SHORT_TERM_CRED = 1, + + /** + * Add long term credential to the message. This option may not be used + * together with PJ_STUN_USE_SHORT_TERM_CRED option. + */ + PJ_STUN_USE_LONG_TERM_CRED = 2, + + /** + * Add STUN fingerprint to the message. + */ + PJ_STUN_USE_FINGERPRINT = 4 +}; + + +/** + * Create a STUN session. + * + * @param endpt The STUN endpoint, to be used to register timers etc. + * @param name Optional name to be associated with this instance. The + * name will be used for example for logging purpose. + * @param cb Session callback. + * @param p_sess Pointer to receive STUN session instance. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ PJ_DECL(pj_status_t) pj_stun_session_create(pj_stun_endpoint *endpt, const char *name, const pj_stun_session_cb *cb, pj_stun_session **p_sess); +/** + * Destroy the STUN session. + * + * @param sess The STUN session instance. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ PJ_DECL(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess); +/** + * Associated an arbitrary data with this STUN session. The user data may + * be retrieved later with pj_stun_session_get_user_data() function. + * + * @param sess The STUN session instance. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ PJ_DECL(pj_status_t) pj_stun_session_set_user_data(pj_stun_session *sess, void *user_data); +/** + * Retrieve the user data previously associated to this STUN session with + * pj_stun_session_set_user_data(). + * + * @param sess The STUN session instance. + * + * @return The user data associated with this STUN session. + */ PJ_DECL(void*) pj_stun_session_get_user_data(pj_stun_session *sess); -PJ_DECL(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess, - const pj_str_t *realm, - const pj_str_t *user, - const pj_str_t *passwd); +/** + * Save a long term credential to be used by this STUN session when sending + * outgoing messages. After long term credential is configured, application + * may specify PJ_STUN_USE_LONG_TERM_CRED option when sending outgoing STUN + * message to send the long term credential in the message. + * + * @param sess The STUN session instance. + * @param realm Realm of the long term credential. + * @param user The user name. + * @param passwd The pain-text password. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_DECL(pj_status_t) +pj_stun_session_set_long_term_credential(pj_stun_session *sess, + const pj_str_t *realm, + const pj_str_t *user, + const pj_str_t *passwd); + -PJ_DECL(pj_status_t) pj_stun_session_enable_fingerprint(pj_stun_session *sess, - pj_bool_t enabled); +/** + * Save a short term credential to be used by this STUN session when sending + * outgoing messages. After short term credential is configured, application + * may specify PJ_STUN_USE_SHORT_TERM_CRED option when sending outgoing STUN + * message to send the short term credential in the message. + * + * @param sess The STUN session instance. + * @param user The user name. + * @param passwd The pain-text password. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_DECL(pj_status_t) +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 + * pj_stun_session_send_msg(). + * + * @param sess The STUN session instance. + * @param p_tdata Pointer to receive STUN transmit data instance containing + * the request. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ PJ_DECL(pj_status_t) pj_stun_session_create_bind_req(pj_stun_session *sess, pj_stun_tx_data **p_tdata); +/** + * Create a STUN Allocate request message. After the message has been + * successfully created, application can send the message by calling + * pj_stun_session_send_msg(). + * + * @param sess The STUN session instance. + * @param p_tdata Pointer to receive STUN transmit data instance containing + * the request. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ PJ_DECL(pj_status_t) pj_stun_session_create_allocate_req(pj_stun_session *sess, pj_stun_tx_data **p_tdata); +/** + * Create a STUN Set Active Destination request message. After the message + * has been successfully created, application can send the message by calling + * pj_stun_session_send_msg(). + * + * @param sess The STUN session instance. + * @param p_tdata Pointer to receive STUN transmit data instance containing + * the request. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ PJ_DECL(pj_status_t) pj_stun_session_create_set_active_destination_req(pj_stun_session *sess, pj_stun_tx_data **p_tdata); +/** + * Create a STUN Connect request message. After the message has been + * successfully created, application can send the message by calling + * pj_stun_session_send_msg(). + * + * @param sess The STUN session instance. + * @param p_tdata Pointer to receive STUN transmit data instance containing + * the request. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ PJ_DECL(pj_status_t) pj_stun_session_create_connect_req(pj_stun_session *sess, pj_stun_tx_data **p_tdata); -PJ_DECL(pj_status_t) +/** + * Create a STUN Connection Status Indication message. After the message + * has been successfully created, application can send the message by calling + * pj_stun_session_send_msg(). + * + * @param sess The STUN session instance. + * @param p_tdata Pointer to receive STUN transmit data instance containing + * the message. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_stun_session_create_connection_status_ind(pj_stun_session *sess, pj_stun_tx_data **p_tdata); +/** + * Create a STUN Send Indication message. After the message has been + * successfully created, application can send the message by calling + * pj_stun_session_send_msg(). + * + * @param sess The STUN session instance. + * @param p_tdata Pointer to receive STUN transmit data instance containing + * the message. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ PJ_DECL(pj_status_t) pj_stun_session_create_send_ind(pj_stun_session *sess, pj_stun_tx_data **p_tdata); +/** + * Create a STUN Data Indication message. After the message has been + * successfully created, application can send the message by calling + * pj_stun_session_send_msg(). + * + * @param sess The STUN session instance. + * @param p_tdata Pointer to receive STUN transmit data instance containing + * the message. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ PJ_DECL(pj_status_t) pj_stun_session_create_data_ind(pj_stun_session *sess, pj_stun_tx_data **p_tdata); +/** + * Send STUN message to the specified destination. This function will encode + * the pj_stun_msg instance to a packet buffer, and add credential or + * fingerprint if necessary. If the message is a request, the session will + * also create and manage a STUN client transaction to be used to manage the + * retransmission of the request. After the message has been encoded and + * transaction is setup, the \a on_send_msg() callback of pj_stun_session_cb + * (which is registered when the STUN session is created) will be called + * to actually send the message to the wire. + * + * @param sess The STUN session instance. + * @param options Optional flags, from pj_stun_session_option. + * @param addr_len Length of destination address. + * @param dst_addr The destination socket address. + * @param tdata The STUN transmit data containing the STUN message to + * be sent. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess, + unsigned options, unsigned addr_len, - const pj_sockaddr_t *server, + const pj_sockaddr_t *dst_addr, pj_stun_tx_data *tdata); +/** + * Application must call this function to notify the STUN session about + * the arrival of STUN packet. The STUN packet MUST have been checked + * first with #pj_stun_msg_check() to verify that this is indeed a valid + * STUN packet. + * + * The STUN session will decode the packet into pj_stun_msg, and process + * the message accordingly. If the message is a response, it will search + * through the outstanding STUN client transactions for a matching + * transaction ID and hand over the response to the transaction. + * + * On successful message processing, application will be notified about + * the message via one of the pj_stun_session_cb callback. + * + * @param sess The STUN session instance. + * @param packet The packet containing STUN message. + * @param pkt_size Size of the packet. + * @param parsed_len Optional pointer to receive the size of the parsed + * STUN message (useful if packet is received via a + * stream oriented protocol). + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ PJ_DECL(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, const void *packet, pj_size_t pkt_size, diff --git a/pjlib-util/src/pjstun-srv/server_main.c b/pjlib-util/src/pjstun-srv/server_main.c index 56a1bf85..5fd58ef9 100644 --- a/pjlib-util/src/pjstun-srv/server_main.c +++ b/pjlib-util/src/pjstun-srv/server_main.c @@ -100,6 +100,13 @@ static pj_status_t send_msg(struct service *svc, const pj_stun_msg *msg) pj_ssize_t length; pj_status_t status; + /* Print to log */ + PJ_LOG(4,(THIS_FILE, "TX STUN message: \n" + "--- begin STUN message ---\n" + "%s" + "--- end of STUN message ---\n", + pj_stun_msg_dump(msg, svc->tx_pkt, sizeof(svc->tx_pkt), NULL))); + /* Encode packet */ tx_pkt_len = sizeof(svc->tx_pkt); status = pj_stun_msg_encode(msg, svc->tx_pkt, tx_pkt_len, 0, @@ -220,6 +227,7 @@ static void on_read_complete(pj_ioqueue_key_t *key, unsigned err_code; unsigned uattr_cnt; pj_uint16_t uattr_types[16]; + char dump[512]; pj_status_t status; if (bytes_read <= 0) @@ -241,9 +249,11 @@ static void on_read_complete(pj_ioqueue_key_t *key, goto next_read; } - PJ_LOG(4,(THIS_FILE, "RX STUN %s %s message", - pj_stun_get_method_name(rx_msg->hdr.type), - pj_stun_get_class_name(rx_msg->hdr.type))); + PJ_LOG(4,(THIS_FILE, "RX STUN message: \n" + "--- begin STUN message ---\n" + "%s" + "--- end of STUN message ---\n", + pj_stun_msg_dump(rx_msg, dump, sizeof(dump), NULL))); if (PJ_STUN_IS_REQUEST(rx_msg->hdr.type)) { switch (rx_msg->hdr.type) { -- cgit v1.2.3