diff options
author | Benny Prijono <bennylp@teluu.com> | 2007-02-22 02:09:23 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2007-02-22 02:09:23 +0000 |
commit | 20968030aa4e9d15c000cca74aa9a3b575c7f41e (patch) | |
tree | 972ffc087fc85636daf4caa5d4f2a36f729ac6e7 /pjlib-util/src | |
parent | 1a5b8c19989c4ab4e1eee7f712913f8f3e459e71 (diff) |
Continuing work on the new STUN framework, partly implemented the client session
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@993 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjlib-util/src')
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_msg_dump.c | 208 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_transaction.c | 44 | ||||
-rw-r--r-- | pjlib-util/src/pjstun-client/client_main.c | 25 | ||||
-rw-r--r-- | pjlib-util/src/pjstun-client/stun_session.c | 499 | ||||
-rw-r--r-- | pjlib-util/src/pjstun-client/stun_session.h | 114 |
5 files changed, 875 insertions, 15 deletions
diff --git a/pjlib-util/src/pjlib-util/stun_msg_dump.c b/pjlib-util/src/pjlib-util/stun_msg_dump.c new file mode 100644 index 00000000..2def62e4 --- /dev/null +++ b/pjlib-util/src/pjlib-util/stun_msg_dump.c @@ -0,0 +1,208 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjlib-util/stun_msg.h> +#include <pjlib-util/errno.h> +#include <pj/assert.h> +#include <pj/string.h> + + +#define APPLY() if (len < 1 || len >= (end-p)) \ + goto on_return; \ + p += len + +static int print_attr(char *buffer, unsigned length, + const pj_stun_attr_hdr *ahdr) +{ + char *p = buffer, *end = buffer + length; + int len; + + len = pj_ansi_snprintf(buffer, end-p, + " %s: length=%d", + pj_stun_get_attr_name(ahdr->type), + (int)ahdr->length); + APPLY(); + + + switch (ahdr->type) { + case PJ_STUN_ATTR_MAPPED_ADDR: + case PJ_STUN_ATTR_RESPONSE_ADDR: + case PJ_STUN_ATTR_SOURCE_ADDR: + case PJ_STUN_ATTR_CHANGED_ADDR: + case PJ_STUN_ATTR_REFLECTED_FROM: + case PJ_STUN_ATTR_REMOTE_ADDRESS: + case PJ_STUN_ATTR_RELAY_ADDRESS: + case PJ_STUN_ATTR_XOR_MAPPED_ADDRESS: + case PJ_STUN_ATTR_REQUESTED_IP: + case PJ_STUN_ATTR_XOR_REFLECTED_FROM: + case PJ_STUN_ATTR_XOR_INTERNAL_ADDR: + case PJ_STUN_ATTR_ALTERNATE_SERVER: + { + const pj_stun_generic_ip_addr_attr *attr; + + 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, + ", 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, + ", IPv6 addr present\n"); + } else { + len = pj_ansi_snprintf(buffer, end-p, + ", INVALID ADDRESS FAMILY!\n"); + } + } + break; + + case PJ_STUN_ATTR_CHANGE_REQUEST: + case PJ_STUN_ATTR_LIFETIME: + case PJ_STUN_ATTR_BANDWIDTH: + case PJ_STUN_ATTR_REQUESTED_ADDR_TYPE: + case PJ_STUN_ATTR_REQUESTED_PORT_PROPS: + case PJ_STUN_ATTR_REQUESTED_TRANSPORT: + case PJ_STUN_ATTR_TIMER_VAL: + case PJ_STUN_ATTR_PRIORITY: + case PJ_STUN_ATTR_FINGERPRINT: + case PJ_STUN_ATTR_REFRESH_INTERVAL: + { + 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", + (pj_uint32_t)attr->value, + (pj_uint32_t)attr->value); + } + break; + + case PJ_STUN_ATTR_USERNAME: + case PJ_STUN_ATTR_PASSWORD: + case PJ_STUN_ATTR_REALM: + case PJ_STUN_ATTR_NONCE: + case PJ_STUN_ATTR_SERVER: + { + const pj_stun_generic_string_attr *attr; + + attr = (pj_stun_generic_string_attr*)ahdr; + len = pj_ansi_snprintf(buffer, end-p, + ", value=\"%.*s\"\n", + (int)attr->value.slen, + attr->value.ptr); + } + break; + + case PJ_STUN_ATTR_ERROR_CODE: + { + const pj_stun_error_code_attr *attr; + + attr = (const pj_stun_error_code_attr*) ahdr; + len = pj_ansi_snprintf(buffer, end-p, + ", err_code=%d, reason=\"%.*s\"\n", + attr->err_class*100 + attr->number, + (int)attr->reason.slen, + attr->reason.ptr); + } + break; + + case PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES: + { + const pj_stun_unknown_attr *attr; + unsigned j; + + attr = (const pj_stun_unknown_attr*) ahdr; + + len = pj_ansi_snprintf(buffer, end-p, + ", unknown list:"); + APPLY(); + + for (j=0; j<attr->attr_count; ++j) { + len = pj_ansi_snprintf(buffer, end-p, + " %d", + (int)attr->attrs[j]); + APPLY(); + } + } + break; + + case PJ_STUN_ATTR_MESSAGE_INTEGRITY: + case PJ_STUN_ATTR_DATA: + case PJ_STUN_ATTR_USE_CANDIDATE: + default: + len = pj_ansi_snprintf(buffer, end-p, "\n"); + + break; + } + + APPLY(); + + return (p-buffer); + +on_return: + return len; +} + + +/* + * Dump STUN message to a printable string output. + */ +PJ_DEF(char*) pj_stun_msg_dump(const pj_stun_msg *msg, + char *buffer, + unsigned *length) +{ + char *p, *end; + int len; + unsigned i; + + PJ_ASSERT_RETURN(msg && buffer && length, NULL); + + p = buffer; + end = buffer + (*length); + + len = pj_ansi_snprintf(p, end-p, "STUN %s %s\n", + pj_stun_get_method_name(msg->hdr.type), + pj_stun_get_class_name(msg->hdr.type)); + APPLY(); + + len = pj_ansi_snprintf(p, end-p, + " Hdr: length=%d, magic=%x, tsx_id=%x %x %x\n" + " Attributes:\n", + msg->hdr.length, + msg->hdr.magic, + *(pj_uint32_t*)&msg->hdr.tsx_id[0], + *(pj_uint32_t*)&msg->hdr.tsx_id[4], + *(pj_uint32_t*)&msg->hdr.tsx_id[8]); + APPLY(); + + for (i=0; i<msg->attr_count; ++i) { + len = print_attr(p, end-p, msg->attr[i]); + APPLY(); + } + +on_return: + *p = '\0'; + *length = (p-buffer); + return buffer; + +} + + +#undef APPLY diff --git a/pjlib-util/src/pjlib-util/stun_transaction.c b/pjlib-util/src/pjlib-util/stun_transaction.c index 6720b8d7..701122fe 100644 --- a/pjlib-util/src/pjlib-util/stun_transaction.c +++ b/pjlib-util/src/pjlib-util/stun_transaction.c @@ -252,29 +252,16 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, } + /* * Notify the STUN transaction about the arrival of STUN response. */ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, - const void *packet, - pj_size_t pkt_size, - unsigned *parsed_len) + const pj_stun_msg *msg) { - pj_stun_msg *msg; pj_stun_error_code_attr *err_attr; 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; - } - /* Must be STUN response message */ if (!PJ_STUN_IS_RESPONSE(msg->hdr.type) && !PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) @@ -329,5 +316,32 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, } return PJ_SUCCESS; + +} + + +/* + * 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 new file mode 100644 index 00000000..7dc0540d --- /dev/null +++ b/pjlib-util/src/pjstun-client/client_main.c @@ -0,0 +1,25 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjlib-util.h> +#include <pjlib.h> + + +#define THIS_FILE "client_main.c" +#define MAX_THREADS 8 + diff --git a/pjlib-util/src/pjstun-client/stun_session.c b/pjlib-util/src/pjstun-client/stun_session.c new file mode 100644 index 00000000..571b723d --- /dev/null +++ b/pjlib-util/src/pjstun-client/stun_session.c @@ -0,0 +1,499 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "stun_session.h" +#include <pjlib.h> + +struct pj_stun_session +{ + pj_stun_endpoint *endpt; + pj_pool_t *pool; + pj_stun_session_cb cb; + void *user_data; + + pj_str_t realm; + pj_str_t username; + pj_str_t password; + + pj_bool_t fingerprint_enabled; + + pj_stun_tx_data pending_request_list; +}; + +#define SNAME(s_) ((s_)->pool->obj_name) + +#if PJ_LOG_MAX_LEVEL >= 5 +# define TRACE_(expr) PJ_LOG(5,expr) +#else +# define TRACE_(expr) +#endif + +#if PJ_LOG_MAX_LEVEL >= 4 +# define LOG_ERR_(sess, title, rc) +static void stun_perror(pj_stun_session *sess, const char *title, + pj_status_t status) +{ + char errmsg[PJ_ERR_MSG_SIZE]; + + pj_strerror(status, errmsg, sizeof(errmsg)); + + PJ_LOG(4,(SNAME(sess), "%s: %s", title, errmsg)); +} + +#else +# define ERR_(sess, title, rc) +#endif + +#define TDATA_POOL_SIZE 1024 +#define TDATA_POOL_INC 1024 + + +static void tsx_on_complete(pj_stun_client_tsx *tsx, + 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); + +static pj_stun_tsx_cb tsx_cb = +{ + &tsx_on_complete, + &tsx_on_send_msg +}; + + +static pj_status_t tsx_add(pj_stun_session *sess, + pj_stun_tx_data *tdata) +{ + pj_list_push_back(&sess->pending_request_list, tdata); + return PJ_SUCCESS; +} + +static pj_status_t tsx_erase(pj_stun_session *sess, + pj_stun_tx_data *tdata) +{ + pj_list_erase(tdata); + return PJ_SUCCESS; +} + +static pj_stun_tx_data* tsx_lookup(pj_stun_session *sess, + const pj_stun_msg *msg) +{ + pj_stun_tx_data *tdata; + + 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, + sizeof(msg->hdr.tsx_id))==0) + { + return tdata; + } + tdata = tdata->next; + } + + return NULL; +} + +static pj_status_t create_tdata(pj_stun_session *sess, + unsigned msg_type, + void *user_data, + pj_stun_tx_data **p_tdata) +{ + pj_pool_t *pool; + pj_status_t status; + pj_stun_tx_data *tdata; + + /* Create pool and initialize basic tdata attributes */ + pool = pj_pool_create(sess->endpt->pf, "tdata%p", + TDATA_POOL_SIZE, TDATA_POOL_INC, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + + tdata = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_tx_data); + tdata->pool = pool; + tdata->sess = sess; + tdata->user_data = tdata; + + /* Create STUN message */ + status = pj_stun_msg_create(pool, msg_type, PJ_STUN_MAGIC, + NULL, &tdata->msg); + if (status != PJ_SUCCESS) { + pj_pool_release(pool); + return status; + } + + /* If this is a request, then copy the request's transaction ID + * as the transaction key. + */ + if (PJ_STUN_IS_REQUEST(msg_type)) { + pj_assert(sizeof(tdata->client_key)==sizeof(tdata->msg->hdr.tsx_id)); + pj_memcpy(tdata->client_key, tdata->msg->hdr.tsx_id, + sizeof(tdata->msg->hdr.tsx_id)); + } + + *p_tdata = tdata; + + return PJ_SUCCESS; +} + +static void destroy_tdata(pj_stun_tx_data *tdata) +{ + if (tdata->client_tsx) { + tsx_erase(tdata->sess, tdata); + pj_stun_client_tsx_destroy(tdata->client_tsx); + tdata->client_tsx = NULL; + } + + pj_pool_release(tdata->pool); +} + +static pj_status_t session_apply_req(pj_stun_session *sess, + pj_pool_t *pool, + pj_stun_msg *msg) +{ + pj_status_t status; + + /* From draft-ietf-behave-rfc3489bis-05.txt + * Section 8.3.1. Formulating the Request Message + */ + if (sess->realm.slen || sess->username.slen) { + 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->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 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_INTEGRITY); + + } + + /* Add FINGERPRINT attribute if necessary */ + if (sess->fingerprint_enabled) { + pj_stun_fingerprint_attr *af; + + status = pj_stun_generic_uint_attr_create(sess->pool, + PJ_STUN_ATTR_FINGERPRINT, + 0, &af); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + + status = pj_stun_msg_add_attr(msg, &af->hdr); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + } + + return PJ_SUCCESS; +} + + + + + + +PJ_DEF(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) +{ + pj_pool_t *pool; + pj_stun_session *sess; + + PJ_ASSERT_RETURN(endpt && cb && p_sess, PJ_EINVAL); + + pool = pj_pool_create(endpt->pf, name, 4000, 4000, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + + sess = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_session); + sess->endpt = endpt; + sess->pool = pool; + pj_memcpy(&sess->cb, cb, sizeof(*cb)); + + pj_list_init(&sess->pending_request_list); + + *p_sess = sess; + + PJ_TODO(MUTEX_PROTECTION); + + return PJ_SUCCESS; +} + +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; +} + + +PJ_DEF(pj_status_t) pj_stun_session_set_user_data( pj_stun_session *sess, + void *user_data) +{ + PJ_ASSERT_RETURN(sess, PJ_EINVAL); + sess->user_data = user_data; + return PJ_SUCCESS; +} + +PJ_DEF(void*) pj_stun_session_get_user_data(pj_stun_session *sess) +{ + PJ_ASSERT_RETURN(sess, NULL); + 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_str_t empty = { 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); + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_stun_session_enable_fingerprint(pj_stun_session *sess, + pj_bool_t enabled) +{ + PJ_ASSERT_RETURN(sess, PJ_EINVAL); + sess->fingerprint_enabled = enabled; + return PJ_SUCCESS; +} + + +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; + + PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL); + + status = create_tdata(sess, PJ_STUN_BINDING_REQUEST, NULL, &tdata); + 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; +} + +PJ_DEF(pj_status_t) pj_stun_session_create_allocate_req(pj_stun_session *sess, + pj_stun_tx_data **p_tdata) +{ + PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); +} + + +PJ_DEF(pj_status_t) +pj_stun_session_create_set_active_destination_req(pj_stun_session *sess, + pj_stun_tx_data **p_tdata) +{ + PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); +} + +PJ_DEF(pj_status_t) pj_stun_session_create_connect_req( pj_stun_session *sess, + pj_stun_tx_data **p_tdata) +{ + PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); +} + +PJ_DEF(pj_status_t) +pj_stun_session_create_connection_status_ind(pj_stun_session *sess, + pj_stun_tx_data **p_tdata) +{ + PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); +} + +PJ_DEF(pj_status_t) pj_stun_session_create_send_ind( pj_stun_session *sess, + pj_stun_tx_data **p_tdata) +{ + PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); +} + +PJ_DEF(pj_status_t) pj_stun_session_create_data_ind( pj_stun_session *sess, + pj_stun_tx_data **p_tdata) +{ + PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); +} + +PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, + unsigned addr_len, + const pj_sockaddr_t *server, + pj_stun_tx_data *tdata) +{ + pj_status_t status; + + PJ_ASSERT_RETURN(sess && addr_len && server && tdata, PJ_EINVAL); + + if (PJ_LOG_MAX_LEVEL >= 5) { + char buf[512]; + unsigned buflen = sizeof(buf); + const char *dst_name; + int dst_port; + const pj_sockaddr *dst = (const pj_sockaddr*)server; + + if (dst->sa_family == PJ_AF_INET) { + const pj_sockaddr_in *dst4 = (const pj_sockaddr_in*)dst; + dst_name = pj_inet_ntoa(dst4->sin_addr); + dst_port = pj_ntohs(dst4->sin_port); + } else if (dst->sa_family == PJ_AF_INET6) { + const pj_sockaddr_in6 *dst6 = (const pj_sockaddr_in6*)dst; + dst_name = "IPv6"; + dst_port = pj_ntohs(dst6->sin6_port); + } else { + LOG_ERR_(sess, "Invalid address family", PJ_EINVAL); + return PJ_EINVAL; + } + + PJ_LOG(5,(SNAME(sess), + "Sending STUN message to %s:%d:\n" + "%s\n", + dst_name, dst_port, + pj_stun_msg_dump(tdata->msg, buf, &buflen))); + } + + + /* 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->endpt, &tsx_cb, + &tdata->client_tsx); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + pj_stun_client_tsx_set_data(tdata->client_tsx, (void*)tdata); + + /* Send the request! */ + status = pj_stun_client_tsx_send_msg(tdata->client_tsx, PJ_TRUE, + tdata->msg); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + LOG_ERR_(sess, "Error sending STUN request", status); + return status; + } + + /* Add to pending request list */ + tsx_add(sess, tdata); + + } else { + /* Otherwise for non-request message, send directly to transport. */ + status = sess->cb.on_send_msg(tdata, addr_len, server); + } + + + return status; +} + + +PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, + const void *packet, + pj_size_t pkt_size, + unsigned *parsed_len) +{ + pj_stun_msg *msg; + pj_status_t status; + + PJ_ASSERT_RETURN(sess && 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) { + LOG_ERR_(sess, "STUN msg_decode() error", status); + return status; + } + + if (PJ_STUN_IS_RESPONSE(msg->hdr.type) || + PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) + { + pj_stun_tx_data *tdata; + + /* Lookup pending client transaction */ + tdata = tsx_lookup(sess, msg); + if (tdata == NULL) { + LOG_ERR_(sess, "STUN error finding transaction", PJ_ENOTFOUND); + return PJ_ENOTFOUND; + } + + /* 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); + 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)) { + destroy_tdata(tdata); + tdata = NULL; + } + + return PJ_SUCCESS; + + } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) { + + + } else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) { + + + } else { + pj_assert(!"Unexpected!"); + return PJ_EBUG; + } + +} + diff --git a/pjlib-util/src/pjstun-client/stun_session.h b/pjlib-util/src/pjstun-client/stun_session.h new file mode 100644 index 00000000..f9759cb9 --- /dev/null +++ b/pjlib-util/src/pjstun-client/stun_session.h @@ -0,0 +1,114 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJLIB_UTIL_STUN_SESSION_H__ +#define __PJLIB_UTIL_STUN_SESSION_H__ + +#include <pjlib-util/stun_msg.h> +#include <pjlib-util/stun_endpoint.h> +#include <pjlib-util/stun_transaction.h> +#include <pj/list.h> + + +typedef struct pj_stun_tx_data pj_stun_tx_data; +typedef struct pj_stun_session pj_stun_session; + +typedef struct pj_stun_session_cb +{ + pj_status_t (*on_send_msg)(pj_stun_tx_data *tdata, + 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); +} pj_stun_session_cb; + + +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_stun_client_tsx *client_tsx; + pj_uint8_t client_key[12]; +}; + + +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); + +PJ_DECL(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess); + +PJ_DECL(pj_status_t) pj_stun_session_set_user_data(pj_stun_session *sess, + void *user_data); + +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); + +PJ_DECL(pj_status_t) pj_stun_session_enable_fingerprint(pj_stun_session *sess, + pj_bool_t enabled); + +PJ_DECL(pj_status_t) pj_stun_session_create_bind_req(pj_stun_session *sess, + pj_stun_tx_data **p_tdata); + +PJ_DECL(pj_status_t) pj_stun_session_create_allocate_req(pj_stun_session *sess, + pj_stun_tx_data **p_tdata); + +PJ_DECL(pj_status_t) +pj_stun_session_create_set_active_destination_req(pj_stun_session *sess, + pj_stun_tx_data **p_tdata); + +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) +pj_stun_session_create_connection_status_ind(pj_stun_session *sess, + pj_stun_tx_data **p_tdata); + +PJ_DECL(pj_status_t) pj_stun_session_create_send_ind(pj_stun_session *sess, + pj_stun_tx_data **p_tdata); + +PJ_DECL(pj_status_t) pj_stun_session_create_data_ind(pj_stun_session *sess, + pj_stun_tx_data **p_tdata); + +PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess, + unsigned addr_len, + const pj_sockaddr_t *server, + pj_stun_tx_data *tdata); + +PJ_DECL(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, + const void *packet, + pj_size_t pkt_size, + unsigned *parsed_len); + + + +#endif /* __PJLIB_UTIL_STUN_SESSION_H__ */ + |