diff options
Diffstat (limited to 'pjlib-util/src/pjstun-client')
-rw-r--r-- | pjlib-util/src/pjstun-client/client_main.c | 136 | ||||
-rw-r--r-- | pjlib-util/src/pjstun-client/stun_session.c | 226 | ||||
-rw-r--r-- | pjlib-util/src/pjstun-client/stun_session.h | 341 |
3 files changed, 633 insertions, 70 deletions
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 <pjlib-util.h> #include <pjlib.h> +#include "stun_session.h" + +#include <conio.h> #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 <pj/list.h> +/** 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, |