From 998f05896130fb3cfbfdd03a145164d2067cd042 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Mon, 5 Mar 2007 00:58:24 +0000 Subject: Implemented new STUN server framework git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1040 74dad513-b988-da41-8d7b-12977e46ad98 --- pjlib-util/src/pjlib-util/stun_endpoint.c | 2 +- pjlib-util/src/pjlib-util/stun_msg.c | 36 +- pjlib-util/src/pjlib-util/stun_session.c | 4 +- pjlib-util/src/pjlib-util/stun_transaction.c | 2 +- pjlib-util/src/pjstun-srv-test/bind_usage.c | 174 +++++++++ pjlib-util/src/pjstun-srv-test/main.c | 132 +++++++ pjlib-util/src/pjstun-srv-test/server.c | 511 +++------------------------ pjlib-util/src/pjstun-srv-test/server.h | 122 +++++++ pjlib-util/src/pjstun-srv-test/usage.c | 259 ++++++++++++++ 9 files changed, 761 insertions(+), 481 deletions(-) create mode 100644 pjlib-util/src/pjstun-srv-test/bind_usage.c create mode 100644 pjlib-util/src/pjstun-srv-test/main.c create mode 100644 pjlib-util/src/pjstun-srv-test/server.h create mode 100644 pjlib-util/src/pjstun-srv-test/usage.c (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 2ca106a7..277e385e 100644 --- a/pjlib-util/src/pjlib-util/stun_endpoint.c +++ b/pjlib-util/src/pjlib-util/stun_endpoint.c @@ -40,7 +40,7 @@ PJ_DEF(pj_status_t) pj_stun_endpoint_create( pj_pool_factory *factory, if (!pool) return PJ_ENOMEM; - endpt = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_endpoint); + endpt = PJ_POOL_ZALLOC_T(pool, pj_stun_endpoint); endpt->pool = pool; endpt->pf = factory; endpt->options = options; diff --git a/pjlib-util/src/pjlib-util/stun_msg.c b/pjlib-util/src/pjlib-util/stun_msg.c index 19828234..ffb5f61b 100644 --- a/pjlib-util/src/pjlib-util/stun_msg.c +++ b/pjlib-util/src/pjlib-util/stun_msg.c @@ -526,7 +526,7 @@ pj_stun_ip_addr_attr_create(pj_pool_t *pool, PJ_ASSERT_RETURN(addr_len == sizeof(pj_sockaddr_in) || addr_len == sizeof(pj_sockaddr_in6), PJ_EINVAL); - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_ip_addr_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_ip_addr_attr); INIT_ATTR(attr, attr_type, STUN_GENERIC_IP_ADDR_LEN); if (!xor_ed) { @@ -580,7 +580,7 @@ static pj_status_t decode_ip_addr_attr(pj_pool_t *pool, pj_uint32_t val; /* Create the attribute */ - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_ip_addr_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_ip_addr_attr); pj_memcpy(attr, buf, ATTR_HDR_LEN); /* Convert to host byte order */ @@ -673,7 +673,7 @@ pj_stun_string_attr_create(pj_pool_t *pool, PJ_ASSERT_RETURN(pool && value && p_attr, PJ_EINVAL); - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_string_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_string_attr); INIT_ATTR(attr, attr_type, value->slen); pj_strdup(pool, &attr->value, value); @@ -712,7 +712,7 @@ static pj_status_t decode_string_attr(pj_pool_t *pool, pj_str_t value; /* Create the attribute */ - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_string_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_string_attr); /* Copy the header */ pj_memcpy(attr, buf, ATTR_HDR_LEN); @@ -786,7 +786,7 @@ pj_stun_empty_attr_create(pj_pool_t *pool, PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL); - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_empty_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_empty_attr); INIT_ATTR(attr, attr_type, sizeof(pj_stun_empty_attr)); *p_attr = attr; @@ -805,7 +805,7 @@ static pj_status_t decode_empty_attr(pj_pool_t *pool, pj_assert(sizeof(pj_stun_empty_attr) == ATTR_HDR_LEN); /* Create the attribute */ - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_empty_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_empty_attr); pj_memcpy(attr, buf, ATTR_HDR_LEN); /* Convert to host byte order */ @@ -864,7 +864,7 @@ pj_stun_uint_attr_create(pj_pool_t *pool, PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL); - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_uint_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_uint_attr); INIT_ATTR(attr, attr_type, STUN_UINT_LEN); attr->value = value; @@ -904,7 +904,7 @@ static pj_status_t decode_uint_attr(pj_pool_t *pool, pj_assert(sizeof(pj_stun_uint_attr) == ATTR_LEN); /* Create the attribute */ - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_uint_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_uint_attr); pj_memcpy(attr, buf, ATTR_LEN); /* Convert to host byte order */ @@ -967,7 +967,7 @@ pj_stun_msgint_attr_create(pj_pool_t *pool, PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL); - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msgint_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_msgint_attr); INIT_ATTR(attr, PJ_STUN_ATTR_MESSAGE_INTEGRITY, STUN_MSG_INTEGRITY_LEN); *p_attr = attr; @@ -1003,7 +1003,7 @@ static pj_status_t decode_msgint_attr(pj_pool_t *pool, pj_assert(sizeof(pj_stun_msgint_attr)==ATTR_LEN); /* Create attribute */ - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msgint_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_msgint_attr); pj_memcpy(attr, buf, sizeof(pj_stun_msgint_attr)); attr->hdr.type = pj_ntohs(attr->hdr.type); attr->hdr.length = pj_ntohs(attr->hdr.length); @@ -1073,7 +1073,7 @@ pj_stun_errcode_attr_create(pj_pool_t *pool, err_reason = &str; } - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_errcode_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_errcode_attr); INIT_ATTR(attr, PJ_STUN_ATTR_ERROR_CODE, 4+err_reason->slen); attr->err_class = (pj_uint8_t)(err_code / 100); attr->number = (pj_uint8_t) (err_code % 100); @@ -1109,7 +1109,7 @@ static pj_status_t decode_errcode_attr(pj_pool_t *pool, pj_str_t value; /* Create the attribute */ - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_errcode_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_errcode_attr); /* Copy the header */ pj_memcpy(attr, buf, ATTR_HDR_LEN + 4); @@ -1186,7 +1186,7 @@ pj_stun_unknown_attr_create(pj_pool_t *pool, PJ_ASSERT_RETURN(pool && attr_cnt < PJ_STUN_MAX_ATTR && p_attr, PJ_EINVAL); - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_unknown_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_unknown_attr); INIT_ATTR(attr, PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES, attr_cnt * 2); attr->attr_count = attr_cnt; @@ -1232,7 +1232,7 @@ static pj_status_t decode_unknown_attr(pj_pool_t *pool, const pj_uint16_t *punk_attr; unsigned i; - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_unknown_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_unknown_attr); pj_memcpy(attr, buf, ATTR_HDR_LEN); attr->hdr.type = pj_ntohs(attr->hdr.type); @@ -1309,7 +1309,7 @@ pj_stun_binary_attr_create(pj_pool_t *pool, PJ_ASSERT_RETURN(pool && attr_type && p_attr, PJ_EINVAL); - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_binary_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_binary_attr); INIT_ATTR(attr, attr_type, sizeof(pj_stun_binary_attr)); if (data && length) { @@ -1351,7 +1351,7 @@ static pj_status_t decode_binary_attr(pj_pool_t *pool, pj_stun_binary_attr *attr; /* Create the attribute */ - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_binary_attr); + attr = PJ_POOL_ZALLOC_T(pool, pj_stun_binary_attr); /* Copy the header */ pj_memcpy(attr, buf, ATTR_HDR_LEN); @@ -1418,7 +1418,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_create( pj_pool_t *pool, PJ_ASSERT_RETURN(pool && msg_type && p_msg, PJ_EINVAL); - msg = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msg); + msg = PJ_POOL_ZALLOC_T(pool, pj_stun_msg); msg->hdr.type = (pj_uint16_t) msg_type; msg->hdr.magic = magic; @@ -1605,7 +1605,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, } /* Create the message, copy the header, and convert to host byte order */ - msg = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msg); + msg = PJ_POOL_ZALLOC_T(pool, pj_stun_msg); pj_memcpy(&msg->hdr, pdu, sizeof(pj_stun_msg_hdr)); msg->hdr.type = pj_ntohs(msg->hdr.type); msg->hdr.length = pj_ntohs(msg->hdr.length); diff --git a/pjlib-util/src/pjlib-util/stun_session.c b/pjlib-util/src/pjlib-util/stun_session.c index 49ae6eae..602cb277 100644 --- a/pjlib-util/src/pjlib-util/stun_session.c +++ b/pjlib-util/src/pjlib-util/stun_session.c @@ -124,7 +124,7 @@ static pj_status_t create_tdata(pj_stun_session *sess, TDATA_POOL_SIZE, TDATA_POOL_INC, NULL); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); - tdata = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_tx_data); + tdata = PJ_POOL_ZALLOC_T(pool, pj_stun_tx_data); tdata->pool = pool; tdata->sess = sess; tdata->user_data = user_data; @@ -320,7 +320,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_endpoint *endpt, 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 = PJ_POOL_ZALLOC_T(pool, pj_stun_session); sess->endpt = endpt; sess->pool = pool; pj_memcpy(&sess->cb, cb, sizeof(*cb)); diff --git a/pjlib-util/src/pjlib-util/stun_transaction.c b/pjlib-util/src/pjlib-util/stun_transaction.c index 636d56d1..0000e3a6 100644 --- a/pjlib-util/src/pjlib-util/stun_transaction.c +++ b/pjlib-util/src/pjlib-util/stun_transaction.c @@ -73,7 +73,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_endpoint *endpt, PJ_ASSERT_RETURN(endpt && cb && p_tsx, PJ_EINVAL); PJ_ASSERT_RETURN(cb->on_send_msg, PJ_EINVAL); - tsx = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_client_tsx); + tsx = PJ_POOL_ZALLOC_T(pool, pj_stun_client_tsx); tsx->endpt = endpt; pj_memcpy(&tsx->cb, cb, sizeof(*cb)); diff --git a/pjlib-util/src/pjstun-srv-test/bind_usage.c b/pjlib-util/src/pjstun-srv-test/bind_usage.c new file mode 100644 index 00000000..d69d38c6 --- /dev/null +++ b/pjlib-util/src/pjstun-srv-test/bind_usage.c @@ -0,0 +1,174 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2005 Benny Prijono + * + * 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 "server.h" + +#define THIS_FILE "bind_usage.c" + +static void usage_on_rx_data(pj_stun_usage *usage, + void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); +static pj_status_t sess_on_send_msg(pj_stun_session *sess, + const void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *dst_addr, + unsigned addr_len); +static pj_status_t sess_on_rx_request(pj_stun_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); + +PJ_DEF(pj_status_t) pj_stun_bind_usage_create(pj_stun_server *srv, + const pj_str_t *ip_addr, + unsigned port, + pj_stun_usage **p_bu) +{ + pj_stun_server_info *si; + pj_stun_session *session; + pj_stun_usage *usage; + pj_stun_usage_cb usage_cb; + pj_stun_session_cb sess_cb; + pj_sockaddr_in local_addr; + pj_status_t status; + + si = pj_stun_server_get_info(srv); + + status = pj_sockaddr_in_init(&local_addr, ip_addr, (pj_uint16_t)port); + if (status != PJ_SUCCESS) + return status; + + pj_bzero(&usage_cb, sizeof(usage_cb)); + usage_cb.on_rx_data = &usage_on_rx_data; + + status = pj_stun_usage_create(srv, "bind%p", &usage_cb, + PJ_AF_INET, PJ_SOCK_DGRAM, 0, + &local_addr, sizeof(local_addr), + &usage); + if (status != PJ_SUCCESS) + return status; + + pj_bzero(&sess_cb, sizeof(sess_cb)); + sess_cb.on_send_msg = &sess_on_send_msg; + sess_cb.on_rx_request = &sess_on_rx_request; + status = pj_stun_session_create(si->endpt, "bind%p", &sess_cb, PJ_FALSE, + &session); + if (status != PJ_SUCCESS) { + pj_stun_usage_destroy(usage); + return status; + } + + pj_stun_usage_set_user_data(usage, session); + pj_stun_session_set_user_data(session, usage); + + *p_bu = usage; + + return PJ_SUCCESS; +} + + +static void usage_on_rx_data(pj_stun_usage *usage, + void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ + pj_stun_session *session; + pj_status_t status; + + session = (pj_stun_session*) pj_stun_usage_get_user_data(usage); + + /* Handle packet to session */ + status = pj_stun_session_on_rx_pkt(session, (pj_uint8_t*)pkt, pkt_size, + PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, + NULL, src_addr, src_addr_len); + if (status != PJ_SUCCESS) { + pj_stun_perror(THIS_FILE, "Error handling incoming packet", status); + return; + } +} + + +static pj_status_t sess_on_send_msg(pj_stun_session *sess, + const void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *dst_addr, + unsigned addr_len) +{ + pj_stun_usage *usage; + + usage = pj_stun_session_get_user_data(sess); + + return pj_stun_usage_sendto(usage, pkt, pkt_size, 0, + dst_addr, addr_len); +} + + +static pj_status_t sess_on_rx_request(pj_stun_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ + pj_stun_tx_data *tdata; + pj_status_t status; + + PJ_UNUSED_ARG(pkt); + PJ_UNUSED_ARG(pkt_len); + + /* Create response */ + status = pj_stun_session_create_response(sess, msg, 0, NULL, &tdata); + if (status != PJ_SUCCESS) + return status; + + /* Create MAPPED-ADDRESS attribute */ + status = pj_stun_msg_add_ip_addr_attr(tdata->pool, tdata->msg, + PJ_STUN_ATTR_MAPPED_ADDR, + PJ_FALSE, + src_addr, src_addr_len); + if (status != PJ_SUCCESS) { + pj_stun_perror(THIS_FILE, "Error creating response", status); + pj_stun_msg_destroy_tdata(sess, tdata); + return status; + } + + /* On the presence of magic, create XOR-MAPPED-ADDRESS attribute */ + if (msg->hdr.magic == PJ_STUN_MAGIC) { + status = + pj_stun_msg_add_ip_addr_attr(tdata->pool, tdata->msg, + PJ_STUN_ATTR_XOR_MAPPED_ADDRESS, + PJ_TRUE, + src_addr, src_addr_len); + if (status != PJ_SUCCESS) { + pj_stun_perror(THIS_FILE, "Error creating response", status); + pj_stun_msg_destroy_tdata(sess, tdata); + return status; + } + } + + /* Send */ + status = pj_stun_session_send_msg(sess, PJ_TRUE, + src_addr, src_addr_len, tdata); + return status; + +} + diff --git a/pjlib-util/src/pjstun-srv-test/main.c b/pjlib-util/src/pjstun-srv-test/main.c new file mode 100644 index 00000000..6bc3382d --- /dev/null +++ b/pjlib-util/src/pjstun-srv-test/main.c @@ -0,0 +1,132 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2005 Benny Prijono + * + * 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 "server.h" + +struct options +{ + char *realm; + char *user_name; + char *password; + char *nonce; + pj_bool_t use_fingerprint; +} o; + +static void usage(void) +{ + puts("Usage: pjstun_srv_test [OPTIONS]"); + puts(""); + puts("where OPTIONS:"); + puts(" --realm, -r Set realm of the credential"); + puts(" --username, -u Set username of the credential"); + puts(" --password, -p Set password of the credential"); + puts(" --nonce, -N Set NONCE"); + puts(" --fingerprint, -F Use fingerprint for outgoing requests"); + puts(" --help, -h"); +} + + +static void server_main(void) +{ + int quit = 0; + + while (!quit) { + char line[10]; + + printf("Menu:\n" + " q Quit\n" + "Choice:"); + + fgets(line, sizeof(line), stdin); + if (line[0] == 'q') + quit = 1; + } +} + +int main(int argc, char *argv[]) +{ + struct pj_getopt_option long_options[] = { + { "realm", 1, 0, 'r'}, + { "username", 1, 0, 'u'}, + { "password", 1, 0, 'p'}, + { "nonce", 1, 0, 'N'}, + { "fingerprint",0, 0, 'F'}, + { "help", 0, 0, 'h'} + }; + int c, opt_id; + pj_caching_pool cp; + pj_stun_server *srv; + pj_stun_usage *bu; + pj_status_t status; + + while((c=pj_getopt_long(argc,argv, "r:u:p:hF", long_options, &opt_id))!=-1) { + switch (c) { + case 'r': + o.realm = pj_optarg; + break; + case 'u': + o.user_name = pj_optarg; + break; + case 'p': + o.password = pj_optarg; + break; + case 'N': + o.nonce = pj_optarg; + break; + case 'h': + usage(); + return 0; + case 'F': + o.use_fingerprint = PJ_TRUE; + break; + default: + printf("Argument \"%s\" is not valid. Use -h to see help", + argv[pj_optind]); + return 1; + } + } + + if (pj_optind != argc) { + puts("Error: invalid arguments"); + return 1; + } + + pj_init(); + pjlib_util_init(); + pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); + + status = pj_stun_server_create(&cp.factory, 1, &srv); + if (status != PJ_SUCCESS) { + pj_stun_perror(THIS_FILE, "Unable to create server", status); + return 1; + } + + status = pj_stun_bind_usage_create(srv, NULL, 3478, &bu); + if (status != PJ_SUCCESS) { + pj_stun_perror(THIS_FILE, "Unable to create bind usage", status); + return 1; + } + + server_main(); + + pj_stun_usage_destroy(bu); + pj_stun_server_destroy(srv); + pj_pool_factory_dump(&cp.factory, PJ_TRUE); + pj_shutdown(); + return 0; +} diff --git a/pjlib-util/src/pjstun-srv-test/server.c b/pjlib-util/src/pjstun-srv-test/server.c index e4fc6d5b..b95b349e 100644 --- a/pjlib-util/src/pjstun-srv-test/server.c +++ b/pjlib-util/src/pjstun-srv-test/server.c @@ -16,522 +16,115 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include -#include -#include -#include +#include "server.h" +#define THIS_FILE "server.c" -#define THIS_FILE "server_main.c" -#define MAX_THREADS 8 -#define MAX_SERVICE 16 -#define MAX_PKT_LEN 512 - -struct service +struct pj_stun_server { - unsigned index; - pj_uint16_t port; - pj_bool_t is_stream; - pj_sock_t sock; - pj_ioqueue_key_t *key; - pj_ioqueue_op_key_t recv_opkey, - send_opkey; - - pj_stun_session *sess; + pj_stun_server_info si; - int src_addr_len; - pj_sockaddr_in src_addr; - pj_ssize_t rx_pkt_len; - pj_uint8_t rx_pkt[MAX_PKT_LEN]; - pj_uint8_t tx_pkt[MAX_PKT_LEN]; -}; - -static struct stun_server -{ - pj_caching_pool cp; pj_pool_t *pool; - pj_stun_endpoint *endpt; - pj_ioqueue_t *ioqueue; - pj_timer_heap_t *timer_heap; - unsigned service_cnt; - struct service services[MAX_SERVICE]; pj_bool_t thread_quit_flag; - unsigned thread_cnt; - pj_thread_t *threads[16]; - - -} server; - -static struct options -{ - char *realm; - char *user_name; - char *password; - char *nonce; - pj_bool_t use_fingerprint; -} o; + pj_thread_t **threads; +}; -static pj_status_t server_perror(const char *sender, const char *title, - pj_status_t status) +PJ_DEF(pj_status_t) pj_stun_perror( const char *sender, + const char *title, + pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(3,(sender, "%s: %s", title, errmsg)); - return status; } - -/* Callback to be called to send outgoing message */ -static pj_status_t on_send_msg(pj_stun_session *sess, - const void *pkt, - pj_size_t pkt_size, - const pj_sockaddr_t *dst_addr, - unsigned addr_len) -{ - struct service *svc; - pj_ssize_t length; - pj_status_t status; - - svc = (struct service*) pj_stun_session_get_user_data(sess); - - /* Send packet */ - length = pkt_size; - if (svc->is_stream) { - status = pj_ioqueue_send(svc->key, &svc->send_opkey, pkt, &length, 0); - } else { -#if 0 - pj_pool_t *pool; - char *buf; - pj_stun_msg *msg; - - pool = pj_pool_create(&server.cp.factory, "", 4000, 4000, NULL); - status = pj_stun_msg_decode(pool, pkt, pkt_size, PJ_STUN_CHECK_PACKET, &msg, NULL, NULL); - buf = pj_pool_alloc(pool, 512); - PJ_LOG(3,("", "%s", pj_stun_msg_dump(msg, buf, 512, NULL))); -#endif - status = pj_ioqueue_sendto(svc->key, &svc->send_opkey, pkt, &length, - 0, dst_addr, addr_len); - } - - return (status == PJ_SUCCESS || status == PJ_EPENDING) ? - PJ_SUCCESS : status; -} - - -/* Handle STUN binding request */ -static pj_status_t on_rx_binding_request(pj_stun_session *sess, - const pj_uint8_t *pkt, - unsigned pkt_len, - const pj_stun_msg *msg, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len) +static int worker_thread(void *p) { - struct service *svc = (struct service *) pj_stun_session_get_user_data(sess); - pj_stun_tx_data *tdata; - pj_status_t status; - - /* Create response */ - status = pj_stun_session_create_response(sess, msg, 0, NULL, &tdata); - if (status != PJ_SUCCESS) - return status; + pj_stun_server *srv = (pj_stun_server*)p; - /* Create MAPPED-ADDRESS attribute */ - status = pj_stun_msg_add_ip_addr_attr(tdata->pool, tdata->msg, - PJ_STUN_ATTR_MAPPED_ADDR, - PJ_FALSE, - src_addr, src_addr_len); - if (status != PJ_SUCCESS) { - server_perror(THIS_FILE, "Error creating response", status); - pj_stun_msg_destroy_tdata(sess, tdata); - return status; - } - - /* On the presence of magic, create XOR-MAPPED-ADDRESS attribute */ - if (msg->hdr.magic == PJ_STUN_MAGIC) { - status = - pj_stun_msg_add_ip_addr_attr(tdata->pool, tdata->msg, - PJ_STUN_ATTR_XOR_MAPPED_ADDRESS, - PJ_TRUE, - src_addr, src_addr_len); - if (status != PJ_SUCCESS) { - server_perror(THIS_FILE, "Error creating response", status); - pj_stun_msg_destroy_tdata(sess, tdata); - return status; - } + while (!srv->thread_quit_flag) { + pj_time_val timeout = { 0, 50 }; + pj_timer_heap_poll(srv->si.timer_heap, NULL); + pj_ioqueue_poll(srv->si.ioqueue, &timeout); } - /* Send */ - status = pj_stun_session_send_msg(sess, PJ_TRUE, - src_addr, src_addr_len, tdata); - return status; -} - - -/* Handle unknown request */ -static pj_status_t on_rx_unknown_request(pj_stun_session *sess, - const pj_uint8_t *pkt, - unsigned pkt_len, - const pj_stun_msg *msg, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len) -{ - pj_stun_tx_data *tdata; - pj_status_t status; - - /* Create response */ - status = pj_stun_session_create_response(sess, msg, - PJ_STUN_STATUS_BAD_REQUEST, - NULL, &tdata); - if (status != PJ_SUCCESS) - return status; - - /* Send */ - status = pj_stun_session_send_msg(sess, 0, src_addr, src_addr_len, tdata); - return status; -} - -/* Callback to be called by STUN session on incoming STUN requests */ -static pj_status_t on_rx_request(pj_stun_session *sess, - const pj_uint8_t *pkt, - unsigned pkt_len, - const pj_stun_msg *msg, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len) -{ - switch (PJ_STUN_GET_METHOD(msg->hdr.type)) { - case PJ_STUN_BINDING_METHOD: - return on_rx_binding_request(sess, pkt, pkt_len, msg, - src_addr, src_addr_len); - default: - return on_rx_unknown_request(sess, pkt, pkt_len, msg, - src_addr, src_addr_len); - } + return 0; } -/* Callback on ioqueue read completion */ -static void on_read_complete(pj_ioqueue_key_t *key, - pj_ioqueue_op_key_t *op_key, - pj_ssize_t bytes_read) +PJ_DEF(pj_status_t) pj_stun_server_create(pj_pool_factory *pf, + unsigned thread_cnt, + pj_stun_server **p_srv) { - struct service *svc = (struct service *) pj_ioqueue_get_user_data(key); + pj_pool_t *pool; + pj_stun_server *srv; + unsigned i; pj_status_t status; - if (bytes_read <= 0) - goto next_read; - - /* Handle packet to session */ - status = pj_stun_session_on_rx_pkt(svc->sess, svc->rx_pkt, bytes_read, - PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, - NULL, &svc->src_addr, svc->src_addr_len); - if (status != PJ_SUCCESS) { - server_perror(THIS_FILE, "Error processing incoming packet", status); - } - -next_read: - if (bytes_read < 0) { - server_perror(THIS_FILE, "on_read_complete()", -bytes_read); - } - - svc->rx_pkt_len = sizeof(svc->rx_pkt); - svc->src_addr_len = sizeof(svc->src_addr); - - status = pj_ioqueue_recvfrom(svc->key, &svc->recv_opkey, - svc->rx_pkt, &svc->rx_pkt_len, - PJ_IOQUEUE_ALWAYS_ASYNC, - &svc->src_addr, &svc->src_addr_len); - if (status != PJ_EPENDING) - server_perror(THIS_FILE, "error starting async read", status); -} - + pool = pj_pool_create(pf, "server%p", 4000, 4000, NULL); -static pj_status_t init_service(struct service *svc) -{ - pj_status_t status; - pj_ioqueue_callback service_callback; - pj_stun_session_cb sess_cb; - pj_sockaddr_in addr; - - status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &svc->sock); - if (status != PJ_SUCCESS) - return status; + srv = PJ_POOL_ZALLOC_T(pool, pj_stun_server); + srv->pool = pool; + srv->si.pf = pf; - status = pj_sockaddr_in_init(&addr, NULL, svc->port); - if (status != PJ_SUCCESS) - goto on_error; - - status = pj_sock_bind(svc->sock, &addr, sizeof(addr)); + status = pj_ioqueue_create(srv->pool, PJ_IOQUEUE_MAX_HANDLES, + &srv->si.ioqueue); if (status != PJ_SUCCESS) goto on_error; - pj_bzero(&sess_cb, sizeof(sess_cb)); - sess_cb.on_send_msg = &on_send_msg; - sess_cb.on_rx_request = &on_rx_request; - status = pj_stun_session_create(server.endpt, "session", - &sess_cb, - o.use_fingerprint!=0, - &svc->sess); + status = pj_timer_heap_create(srv->pool, 1024, &srv->si.timer_heap); if (status != PJ_SUCCESS) goto on_error; - pj_stun_session_set_user_data(svc->sess, (void*)svc); - - if (o.user_name) { - pj_stun_auth_cred cred; - - pj_bzero(&cred, sizeof(cred)); - - cred.type = PJ_STUN_AUTH_CRED_STATIC; - cred.data.static_cred.realm = pj_str(o.realm); - cred.data.static_cred.username = pj_str(o.user_name); - cred.data.static_cred.data_type = 0; - cred.data.static_cred.data = pj_str(o.password); - cred.data.static_cred.nonce = pj_str(o.nonce); - - pj_stun_session_set_credential(svc->sess, &cred); - puts("Session credential set"); - } else { - puts("Credential not set"); - } - - pj_bzero(&service_callback, sizeof(service_callback)); - service_callback.on_read_complete = &on_read_complete; - - status = pj_ioqueue_register_sock(server.pool, server.ioqueue, svc->sock, - svc, &service_callback, &svc->key); + status = pj_stun_endpoint_create(srv->si.pf, 0, srv->si.ioqueue, + srv->si.timer_heap, &srv->si.endpt); if (status != PJ_SUCCESS) goto on_error; + srv->si.thread_cnt = thread_cnt; + srv->threads = pj_pool_calloc(pool, thread_cnt, sizeof(pj_thread_t*)); + for (i=0; ithreads[i]); + if (status != PJ_SUCCESS) + goto on_error; + } - pj_ioqueue_op_key_init(&svc->recv_opkey, sizeof(svc->recv_opkey)); - pj_ioqueue_op_key_init(&svc->send_opkey, sizeof(svc->send_opkey)); - - on_read_complete(svc->key, &svc->recv_opkey, 0); - - PJ_LOG(4,(THIS_FILE, "Service started on port %d", svc->port)); + *p_srv = srv; return PJ_SUCCESS; on_error: - if (svc->key != NULL) { - pj_ioqueue_unregister(svc->key); - svc->key = NULL; - svc->sock = PJ_INVALID_SOCKET; - } else if (svc->sock != 0 && svc->sock != PJ_INVALID_SOCKET) { - pj_sock_close(svc->sock); - svc->sock = PJ_INVALID_SOCKET; - } - + pj_stun_server_destroy(srv); return status; } -static int worker_thread(void *p) +PJ_DEF(pj_stun_server_info*) pj_stun_server_get_info(pj_stun_server *srv) { - PJ_UNUSED_ARG(p); - - while (!server.thread_quit_flag) { - pj_time_val timeout = { 0, 50 }; - pj_timer_heap_poll(server.timer_heap, NULL); - pj_ioqueue_poll(server.ioqueue, &timeout); - } - - return 0; + return &srv->si; } -pj_status_t server_init(void) -{ - pj_status_t status; - - status = pj_init(); - if (status != PJ_SUCCESS) - return server_perror(THIS_FILE, "pj_init() error", status); - - status = pjlib_util_init(); - if (status != PJ_SUCCESS) - return server_perror(THIS_FILE, "pjlib_util_init() error", status); - - pj_caching_pool_init(&server.cp, - &pj_pool_factory_default_policy, 0); - - - server.pool = pj_pool_create(&server.cp.factory, "server", 4000, 4000, - NULL); - - status = pj_ioqueue_create(server.pool, PJ_IOQUEUE_MAX_HANDLES, - &server.ioqueue); - if (status != PJ_SUCCESS) - return server_perror(THIS_FILE, "pj_ioqueue_create()", status); - - status = pj_timer_heap_create(server.pool, 1024, &server.timer_heap); - if (status != PJ_SUCCESS) - return server_perror(THIS_FILE, "Error creating timer heap", status); - - status = pj_stun_endpoint_create(&server.cp.factory, 0, - server.ioqueue, server.timer_heap, - &server.endpt); - if (status != PJ_SUCCESS) - return server_perror(THIS_FILE, "Error creating endpoint", status); - - server.service_cnt = 1; - server.services[0].index = 0; - server.services[0].port = PJ_STUN_PORT; - - status = init_service(&server.services[0]); - if (status != PJ_SUCCESS) - return server_perror(THIS_FILE, "init_service() error", status); - - return PJ_SUCCESS; -} - - -pj_status_t server_main(void) -{ -#if 0 - for (;;) { - pj_time_val timeout = { 0, 50 }; - pj_timer_heap_poll(server.timer_heap, NULL); - pj_ioqueue_poll(server.ioqueue, &timeout); - - if (kbhit() && _getch()==27) - break; - } -#else - pj_status_t status; - - status = pj_thread_create(server.pool, "stun_server", &worker_thread, NULL, - 0, 0, &server.threads[0]); - if (status != PJ_SUCCESS) - return server_perror(THIS_FILE, "create_thread() error", status); - - while (!server.thread_quit_flag) { - char line[10]; - - printf("Menu:\n" - " q Quit\n" - "Choice:"); - - fgets(line, sizeof(line), stdin); - if (line[0] == 'q') - server.thread_quit_flag = 1; - } - -#endif - - return PJ_SUCCESS; -} - - -pj_status_t server_destroy(void) +PJ_DEF(pj_status_t) pj_stun_server_destroy(pj_stun_server *srv) { unsigned i; - for (i=0; ikey != NULL) { - pj_ioqueue_unregister(svc->key); - svc->key = NULL; - svc->sock = PJ_INVALID_SOCKET; - } else if (svc->sock != 0 && svc->sock != PJ_INVALID_SOCKET) { - pj_sock_close(svc->sock); - svc->sock = PJ_INVALID_SOCKET; - } - } - - server.thread_quit_flag = PJ_TRUE; - for (i=0; ithread_quit_flag = PJ_TRUE; + for (i=0; isi.thread_cnt; ++i) { + pj_thread_join(srv->threads[i]); + srv->threads[i] = NULL; } - pj_stun_session_destroy(server.services[0].sess); - pj_stun_endpoint_destroy(server.endpt); - pj_ioqueue_destroy(server.ioqueue); - pj_pool_release(server.pool); - - pj_pool_factory_dump(&server.cp.factory, PJ_TRUE); - pj_caching_pool_destroy(&server.cp); - - pj_shutdown(); + pj_stun_endpoint_destroy(srv->si.endpt); + pj_timer_heap_destroy(srv->si.timer_heap); + pj_ioqueue_destroy(srv->si.ioqueue); + pj_pool_release(srv->pool); return PJ_SUCCESS; } -static void usage(void) -{ - puts("Usage: pjstun_srv_test [OPTIONS]"); - puts(""); - puts("where OPTIONS:"); - puts(" --realm, -r Set realm of the credential"); - puts(" --username, -u Set username of the credential"); - puts(" --password, -p Set password of the credential"); - puts(" --nonce, -N Set NONCE"); - puts(" --fingerprint, -F Use fingerprint for outgoing requests"); - puts(" --help, -h"); -} - - -int main(int argc, char *argv[]) -{ - struct pj_getopt_option long_options[] = { - { "realm", 1, 0, 'r'}, - { "username", 1, 0, 'u'}, - { "password", 1, 0, 'p'}, - { "nonce", 1, 0, 'N'}, - { "fingerprint",0, 0, 'F'}, - { "help", 0, 0, 'h'} - }; - int c, opt_id; - - while((c=pj_getopt_long(argc,argv, "r:u:p:hF", long_options, &opt_id))!=-1) { - switch (c) { - case 'r': - o.realm = pj_optarg; - break; - case 'u': - o.user_name = pj_optarg; - break; - case 'p': - o.password = pj_optarg; - break; - case 'N': - o.nonce = pj_optarg; - break; - case 'h': - usage(); - return 0; - case 'F': - o.use_fingerprint = PJ_TRUE; - break; - default: - printf("Argument \"%s\" is not valid. Use -h to see help", - argv[pj_optind]); - return 1; - } - } - - if (pj_optind != argc) { - puts("Error: invalid arguments"); - return 1; - } - - - if (server_init()) { - server_destroy(); - return 1; - } - - server_main(); - server_destroy(); - - return 0; -} diff --git a/pjlib-util/src/pjstun-srv-test/server.h b/pjlib-util/src/pjstun-srv-test/server.h new file mode 100644 index 00000000..612ea41a --- /dev/null +++ b/pjlib-util/src/pjstun-srv-test/server.h @@ -0,0 +1,122 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2005 Benny Prijono + * + * 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 __STUN_SERVER_H__ +#define __STUN_SERVER_H__ + +#include +#include + + +/** Opaque declaration for STUN server instance */ +typedef struct pj_stun_server pj_stun_server; + +/** STUN server info */ +typedef struct pj_stun_server_info +{ + pj_pool_factory *pf; + pj_stun_endpoint *endpt; + pj_ioqueue_t *ioqueue; + pj_timer_heap_t *timer_heap; + unsigned thread_cnt; +} pj_stun_server_info; + +/** STUN usage */ +typedef struct pj_stun_usage pj_stun_usage; + +/** STUN usage callback */ +typedef struct pj_stun_usage_cb +{ + void (*on_rx_data)(pj_stun_usage *usage, + void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); +} pj_stun_usage_cb; + + +PJ_DECL(pj_status_t) pj_stun_perror(const char *sender, + const char *title, + pj_status_t status); + +/** + * Create instance of STUN server. + */ +PJ_DECL(pj_status_t) pj_stun_server_create(pj_pool_factory *pf, + unsigned thread_cnt, + pj_stun_server **p_srv); + +/** + * Get STUN server info. + */ +PJ_DECL(pj_stun_server_info*) pj_stun_server_get_info(pj_stun_server *srv); + + +/** + * Destroy STUN server. + */ +PJ_DECL(pj_status_t) pj_stun_server_destroy(pj_stun_server *srv); + + +/** + * Create STUN usage. + */ +PJ_DECL(pj_status_t) pj_stun_usage_create(pj_stun_server *srv, + const char *name, + const pj_stun_usage_cb *cb, + int family, + int type, + int protocol, + const pj_sockaddr_t *local_addr, + int addr_len, + pj_stun_usage **p_usage); + +/** + * Destroy usage. + */ +PJ_DECL(pj_status_t) pj_stun_usage_destroy(pj_stun_usage *usage); + +/** + * Set user data. + */ +PJ_DECL(pj_status_t) pj_stun_usage_set_user_data(pj_stun_usage *usage, + void *user_data); +/** + * Get user data. + */ +PJ_DECL(void*) pj_stun_usage_get_user_data(pj_stun_usage *usage); + +/** + * Send with the usage. + */ +PJ_DECL(pj_status_t) pj_stun_usage_sendto(pj_stun_usage *usage, + const void *pkt, + pj_size_t pkt_size, + unsigned flags, + const pj_sockaddr_t *dst_addr, + unsigned addr_len); + +PJ_DEF(pj_status_t) pj_stun_bind_usage_create(pj_stun_server *srv, + const pj_str_t *ip_addr, + unsigned port, + pj_stun_usage **p_bu); + + +#endif /* __STUN_SERVER_H__ */ + + diff --git a/pjlib-util/src/pjstun-srv-test/usage.c b/pjlib-util/src/pjstun-srv-test/usage.c new file mode 100644 index 00000000..6afe3c3f --- /dev/null +++ b/pjlib-util/src/pjstun-srv-test/usage.c @@ -0,0 +1,259 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2005 Benny Prijono + * + * 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 "server.h" + +struct worker +{ + pj_ioqueue_op_key_t read_key; + unsigned index; + pj_uint8_t readbuf[4000]; + pj_sockaddr src_addr; + int src_addr_len; +}; + +struct pj_stun_usage +{ + pj_pool_t *pool; + pj_mutex_t *mutex; + pj_stun_usage_cb cb; + int type; + pj_sock_t sock; + pj_ioqueue_key_t *key; + unsigned worker_cnt; + struct worker *worker; + + pj_ioqueue_op_key_t *send_key; + unsigned send_count, send_index; + + pj_bool_t quitting; + void *user_data; +}; + + +static void on_read_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_read); + +/* + * Create STUN usage. + */ +PJ_DEF(pj_status_t) pj_stun_usage_create( pj_stun_server *srv, + const char *name, + const pj_stun_usage_cb *cb, + int family, + int type, + int protocol, + const pj_sockaddr_t *local_addr, + int addr_len, + pj_stun_usage **p_usage) +{ + pj_stun_server_info *si; + pj_pool_t *pool; + pj_stun_usage *usage; + pj_ioqueue_callback ioqueue_cb; + unsigned i; + pj_status_t status; + + si = pj_stun_server_get_info(srv); + + pool = pj_pool_create(si->pf, name, 4000, 4000, NULL); + usage = PJ_POOL_ZALLOC_T(pool, pj_stun_usage); + usage->pool = pool; + + status = pj_mutex_create_simple(pool, name, &usage->mutex); + if (status != PJ_SUCCESS) + goto on_error; + + pj_memcpy(&usage->cb, cb, sizeof(*cb)); + + usage->type = type; + status = pj_sock_socket(family, type, protocol, &usage->sock); + if (status != PJ_SUCCESS) + goto on_error; + + status = pj_sock_bind(usage->sock, local_addr, addr_len); + if (status != PJ_SUCCESS) + goto on_error; + + pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); + ioqueue_cb.on_read_complete = &on_read_complete; + status = pj_ioqueue_register_sock(usage->pool, si->ioqueue, usage->sock, + usage, &ioqueue_cb, &usage->key); + if (status != PJ_SUCCESS) + goto on_error; + + usage->worker_cnt = si->thread_cnt; + usage->worker = pj_pool_calloc(pool, si->thread_cnt, + sizeof(struct worker)); + for (i=0; ithread_cnt; ++i) { + pj_ioqueue_op_key_init(&usage->worker[i].read_key, + sizeof(usage->worker[i].read_key)); + usage->worker[i].index = i; + } + + usage->send_count = usage->worker_cnt * 2; + usage->send_key = pj_pool_calloc(pool, usage->send_count, + sizeof(pj_ioqueue_op_key_t)); + for (i=0; isend_count; ++i) { + pj_ioqueue_op_key_init(&usage->send_key[i], + sizeof(usage->send_key[i])); + } + + for (i=0; ithread_cnt; ++i) { + pj_ssize_t size; + + size = sizeof(usage->worker[i].readbuf); + usage->worker[i].src_addr_len = sizeof(usage->worker[i].src_addr); + status = pj_ioqueue_recvfrom(usage->key, &usage->worker[i].read_key, + usage->worker[i].readbuf, &size, + PJ_IOQUEUE_ALWAYS_ASYNC, + &usage->worker[i].src_addr, + &usage->worker[i].src_addr_len); + if (status != PJ_EPENDING) + goto on_error; + } + + *p_usage = usage; + return PJ_SUCCESS; + +on_error: + pj_stun_usage_destroy(usage); + return status; +} + + +/** + * Destroy usage. + */ +PJ_DEF(pj_status_t) pj_stun_usage_destroy(pj_stun_usage *usage) +{ + if (usage->key) { + pj_ioqueue_unregister(usage->key); + usage->key = NULL; + usage->sock = PJ_INVALID_SOCKET; + } else if (usage->sock != 0 && usage->sock != PJ_INVALID_SOCKET) { + pj_sock_close(usage->sock); + usage->sock = PJ_INVALID_SOCKET; + } + + if (usage->mutex) { + pj_mutex_destroy(usage->mutex); + usage->mutex = NULL; + } + + if (usage->pool) { + pj_pool_t *pool = usage->pool; + usage->pool = NULL; + pj_pool_release(pool); + } + + return PJ_SUCCESS; +} + + +/** + * Set user data. + */ +PJ_DEF(pj_status_t) pj_stun_usage_set_user_data( pj_stun_usage *usage, + void *user_data) +{ + usage->user_data = user_data; + return PJ_SUCCESS; +} + +/** + * Get user data. + */ +PJ_DEF(void*) pj_stun_usage_get_user_data(pj_stun_usage *usage) +{ + return usage->user_data; +} + + +/** + * Send with the usage. + */ +PJ_DEF(pj_status_t) pj_stun_usage_sendto( pj_stun_usage *usage, + const void *pkt, + pj_size_t pkt_size, + unsigned flags, + const pj_sockaddr_t *dst_addr, + unsigned addr_len) +{ + pj_ssize_t size = pkt_size; + unsigned i, count = usage->send_count, index; + + pj_mutex_lock(usage->mutex); + for (i=0, ++usage->send_index; isend_index) { + if (usage->send_index >= usage->send_count) + usage->send_index = 0; + + if (pj_ioqueue_is_pending(usage->key, &usage->send_key[usage->send_index])==0) { + break; + } + } + + if (i==count) { + pj_mutex_unlock(usage->mutex); + return PJ_EBUSY; + } + + index = usage->send_index; + pj_mutex_unlock(usage->mutex); + + return pj_ioqueue_sendto(usage->key, &usage->send_key[index], + pkt, &size, flags, + dst_addr, addr_len); +} + + +static void on_read_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_read) +{ + enum { MAX_LOOP = 10 }; + pj_stun_usage *usage = pj_ioqueue_get_user_data(key); + struct worker *worker = (struct worker*) op_key; + unsigned count; + pj_status_t status; + + for (count=0; !usage->quitting; ++count) { + unsigned flags; + + if (bytes_read > 0) { + (*usage->cb.on_rx_data)(usage, worker->readbuf, bytes_read, + &worker->src_addr, worker->src_addr_len); + } else if (bytes_read < 0) { + pj_stun_perror(usage->pool->obj_name, "recv() error", -bytes_read); + } + + if (usage->quitting) + break; + + bytes_read = sizeof(worker->readbuf); + flags = (count >= MAX_LOOP) ? PJ_IOQUEUE_ALWAYS_ASYNC : 0; + worker->src_addr_len = sizeof(worker->src_addr); + status = pj_ioqueue_recvfrom(usage->key, &worker->read_key, + worker->readbuf, &bytes_read, flags, + &worker->src_addr, &worker->src_addr_len); + if (status == PJ_EPENDING) + break; + } +} + -- cgit v1.2.3