From e631558f814bc8f2ffceb5ab8538fbfb85f4942f Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Thu, 8 Mar 2007 18:58:04 +0000 Subject: [PATCH] A really initial implementation on STUN server git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@1049 74dad513-b988-da41-8d7b-12977e46ad98 --- pjlib-util/build/pjstun_srv_test.dsp | 4 + pjlib-util/include/pjlib-util/stun_msg.h | 22 +- pjlib-util/src/pjlib-util/stun_msg.c | 2 + pjlib-util/src/pjlib-util/stun_msg_dump.c | 12 +- pjlib-util/src/pjlib-util/stun_session.c | 12 + pjlib-util/src/pjstun-srv-test/bind_usage.c | 56 +- pjlib-util/src/pjstun-srv-test/main.c | 17 +- pjlib-util/src/pjstun-srv-test/server.c | 55 ++ pjlib-util/src/pjstun-srv-test/server.h | 8 + pjlib-util/src/pjstun-srv-test/turn_usage.c | 662 ++++++++++++++++++++ pjlib-util/src/pjstun-srv-test/usage.c | 8 + 11 files changed, 824 insertions(+), 34 deletions(-) create mode 100644 pjlib-util/src/pjstun-srv-test/turn_usage.c diff --git a/pjlib-util/build/pjstun_srv_test.dsp b/pjlib-util/build/pjstun_srv_test.dsp index 1d8ca2531..fa1a5656f 100644 --- a/pjlib-util/build/pjstun_srv_test.dsp +++ b/pjlib-util/build/pjstun_srv_test.dsp @@ -99,6 +99,10 @@ SOURCE="..\src\pjstun-srv-test\server.c" # End Source File # Begin Source File +SOURCE="..\src\pjstun-srv-test\turn_usage.c" +# End Source File +# Begin Source File + SOURCE="..\src\pjstun-srv-test\usage.c" # End Source File # End Group diff --git a/pjlib-util/include/pjlib-util/stun_msg.h b/pjlib-util/include/pjlib-util/stun_msg.h index de7f73d2e..7a38c9d41 100644 --- a/pjlib-util/include/pjlib-util/stun_msg.h +++ b/pjlib-util/include/pjlib-util/stun_msg.h @@ -338,13 +338,13 @@ typedef enum pj_stun_attr_type PJ_STUN_ATTR_DATA = 0x0013,/**< DATA attribute. */ PJ_STUN_ATTR_REALM = 0x0014,/**< REALM attribute. */ PJ_STUN_ATTR_NONCE = 0x0015,/**< NONCE attribute. */ - PJ_STUN_ATTR_RELAY_ADDRESS = 0x0016,/**< RELAY-ADDRESS attribute. */ - PJ_STUN_ATTR_REQUESTED_ADDR_TYPE= 0x0017,/**< REQUESTED-ADDRESS-TYPE */ - PJ_STUN_ATTR_REQUESTED_PORT_PROPS=0x0018,/**< REQUESTED-PORT-PROPS */ - PJ_STUN_ATTR_REQUESTED_TRANSPORT= 0x0019,/**< REQUESTED-TRANSPORT */ - PJ_STUN_ATTR_XOR_MAPPED_ADDRESS = 0x0020,/**< XOR-MAPPED-ADDRESS */ + PJ_STUN_ATTR_RELAY_ADDR = 0x0016,/**< RELAY-ADDRESS attribute. */ + PJ_STUN_ATTR_REQ_ADDR_TYPE = 0x0017,/**< REQUESTED-ADDRESS-TYPE */ + PJ_STUN_ATTR_REQ_PORT_PROPS = 0x0018,/**< REQUESTED-PORT-PROPS */ + PJ_STUN_ATTR_REQ_TRANSPORT = 0x0019,/**< REQUESTED-TRANSPORT */ + PJ_STUN_ATTR_XOR_MAPPED_ADDR = 0x0020,/**< XOR-MAPPED-ADDRESS */ PJ_STUN_ATTR_TIMER_VAL = 0x0021,/**< TIMER-VAL attribute. */ - PJ_STUN_ATTR_REQUESTED_IP = 0x0022,/**< REQUESTED-IP attribute */ + PJ_STUN_ATTR_REQ_IP = 0x0022,/**< REQUESTED-IP attribute */ PJ_STUN_ATTR_XOR_REFLECTED_FROM = 0x0023,/**< XOR-REFLECTED-FROM */ PJ_STUN_ATTR_PRIORITY = 0x0024,/**< PRIORITY */ PJ_STUN_ATTR_USE_CANDIDATE = 0x0025,/**< USE-CANDIDATE */ @@ -390,7 +390,9 @@ typedef enum pj_stun_status PJ_STUN_STATUS_OPER_TCP_ONLY = 445, /**< Operation for TCP Only */ PJ_STUN_STATUS_CONNECTION_FAILURE = 446, /**< Connection Failure */ PJ_STUN_STATUS_CONNECTION_TIMEOUT = 447, /**< Connection Timeout */ + PJ_STUN_STATUS_ALLOCATION_QUOTA_REACHED = 486, /**< Allocation Quota Reached (TURN) */ PJ_STUN_STATUS_SERVER_ERROR = 500, /**< Server Error */ + PJ_STUN_STATUS_INSUFFICIENT_CAPACITY = 507, /**< Insufficient Capacity (TURN) */ PJ_STUN_STATUS_GLOBAL_FAILURE = 600 /**< Global Failure */ } pj_stun_status; @@ -933,7 +935,7 @@ typedef struct pj_stun_ip_addr_attr pj_stun_relay_addr_attr; \endverbatim */ -typedef struct pj_stun_uint_attr pj_stun_requested_addr_type; +typedef struct pj_stun_uint_attr pj_stun_req_addr_type; /** * This describes the STUN REQUESTED-PORT-PROPS attribute. @@ -953,7 +955,7 @@ typedef struct pj_stun_uint_attr pj_stun_requested_addr_type; \endverbatim */ -typedef struct pj_stun_uint_attr pj_stun_requested_port_props_attr; +typedef struct pj_stun_uint_attr pj_stun_req_port_props_attr; /** @@ -962,7 +964,7 @@ typedef struct pj_stun_uint_attr pj_stun_requested_port_props_attr; * protocol for the allocated transport address. It is a 32 bit * unsigned integer. Its values are: 0x0000 for UDP and 0x0000 for TCP. */ -typedef struct pj_stun_uint_attr pj_stun_requested_transport_attr; +typedef struct pj_stun_uint_attr pj_stun_req_transport_attr; /** @@ -970,7 +972,7 @@ typedef struct pj_stun_uint_attr pj_stun_requested_transport_attr; * The REQUESTED-IP attribute is used by the client to request that a * specific IP address be allocated to it. */ -typedef struct pj_stun_ip_addr_attr pj_stun_requested_ip_attr; +typedef struct pj_stun_ip_addr_attr pj_stun_req_ip_attr; /** * This describes the XOR-REFLECTED-FROM attribute, as described by diff --git a/pjlib-util/src/pjlib-util/stun_msg.c b/pjlib-util/src/pjlib-util/stun_msg.c index ffb5f61b1..00158ab4d 100644 --- a/pjlib-util/src/pjlib-util/stun_msg.c +++ b/pjlib-util/src/pjlib-util/stun_msg.c @@ -71,7 +71,9 @@ static struct { PJ_STUN_STATUS_OPER_TCP_ONLY, "Operation for TCP Only"}, { PJ_STUN_STATUS_CONNECTION_FAILURE, "Connection Failure"}, { PJ_STUN_STATUS_CONNECTION_TIMEOUT, "Connection Timeout"}, + { PJ_STUN_STATUS_ALLOCATION_QUOTA_REACHED, "Allocation Quota Reached"}, { PJ_STUN_STATUS_SERVER_ERROR, "Server Error"}, + { PJ_STUN_STATUS_INSUFFICIENT_CAPACITY, "Insufficient Capacity"}, { PJ_STUN_STATUS_GLOBAL_FAILURE, "Global Failure"} }; diff --git a/pjlib-util/src/pjlib-util/stun_msg_dump.c b/pjlib-util/src/pjlib-util/stun_msg_dump.c index 28e7a02c2..a886dd3f1 100644 --- a/pjlib-util/src/pjlib-util/stun_msg_dump.c +++ b/pjlib-util/src/pjlib-util/stun_msg_dump.c @@ -69,9 +69,9 @@ static int print_attr(char *buffer, unsigned length, 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_RELAY_ADDR: + case PJ_STUN_ATTR_XOR_MAPPED_ADDR: + case PJ_STUN_ATTR_REQ_IP: case PJ_STUN_ATTR_XOR_REFLECTED_FROM: case PJ_STUN_ATTR_XOR_INTERNAL_ADDR: case PJ_STUN_ATTR_ALTERNATE_SERVER: @@ -99,9 +99,9 @@ static int print_attr(char *buffer, unsigned length, 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_REQ_ADDR_TYPE: + case PJ_STUN_ATTR_REQ_PORT_PROPS: + case PJ_STUN_ATTR_REQ_TRANSPORT: case PJ_STUN_ATTR_TIMER_VAL: case PJ_STUN_ATTR_PRIORITY: case PJ_STUN_ATTR_FINGERPRINT: diff --git a/pjlib-util/src/pjlib-util/stun_session.c b/pjlib-util/src/pjlib-util/stun_session.c index 602cb2775..903be9e62 100644 --- a/pjlib-util/src/pjlib-util/stun_session.c +++ b/pjlib-util/src/pjlib-util/stun_session.c @@ -176,6 +176,7 @@ static void destroy_tdata(pj_stun_tx_data *tdata) pj_timer_heap_cancel(tdata->sess->endpt->timer_heap, &tdata->res_timer); tdata->res_timer.id = PJ_FALSE; + pj_list_erase(tdata); } pj_pool_release(tdata->pool); } @@ -348,6 +349,17 @@ PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess) { PJ_ASSERT_RETURN(sess, PJ_EINVAL); + pj_mutex_lock(sess->mutex); + while (!pj_list_empty(&sess->pending_request_list)) { + pj_stun_tx_data *tdata = sess->pending_request_list.next; + destroy_tdata(tdata); + } + while (!pj_list_empty(&sess->cached_response_list)) { + pj_stun_tx_data *tdata = sess->cached_response_list.next; + destroy_tdata(tdata); + } + pj_mutex_unlock(sess->mutex); + pj_mutex_destroy(sess->mutex); pj_pool_release(sess->pool); diff --git a/pjlib-util/src/pjstun-srv-test/bind_usage.c b/pjlib-util/src/pjstun-srv-test/bind_usage.c index d69d38c69..b49441c6c 100644 --- a/pjlib-util/src/pjstun-srv-test/bind_usage.c +++ b/pjlib-util/src/pjstun-srv-test/bind_usage.c @@ -25,6 +25,7 @@ static void usage_on_rx_data(pj_stun_usage *usage, pj_size_t pkt_size, const pj_sockaddr_t *src_addr, unsigned src_addr_len); +static void usage_on_destroy(pj_stun_usage *usage); static pj_status_t sess_on_send_msg(pj_stun_session *sess, const void *pkt, pj_size_t pkt_size, @@ -37,14 +38,22 @@ static pj_status_t sess_on_rx_request(pj_stun_session *sess, const pj_sockaddr_t *src_addr, unsigned src_addr_len); +struct bind_usage +{ + pj_pool_t *pool; + pj_stun_usage *usage; + pj_stun_session *session; +}; + + 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_pool_t *pool; + struct bind_usage *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; @@ -52,34 +61,42 @@ PJ_DEF(pj_status_t) pj_stun_bind_usage_create(pj_stun_server *srv, si = pj_stun_server_get_info(srv); + pool = pj_pool_create(si->pf, "bind%p", 128, 128, NULL); + bu = PJ_POOL_ZALLOC_T(pool, struct bind_usage); + bu->pool = pool; + 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; + usage_cb.on_destroy = &usage_on_destroy; 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) + &bu->usage); + if (status != PJ_SUCCESS) { + pj_pool_release(pool); 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); + &bu->session); if (status != PJ_SUCCESS) { - pj_stun_usage_destroy(usage); + pj_stun_usage_destroy(bu->usage); return status; } - pj_stun_usage_set_user_data(usage, session); - pj_stun_session_set_user_data(session, usage); + pj_stun_usage_set_user_data(bu->usage, bu); + pj_stun_session_set_user_data(bu->session, bu); - *p_bu = usage; + if (p_bu) + *p_bu = bu->usage; return PJ_SUCCESS; } @@ -91,10 +108,12 @@ static void usage_on_rx_data(pj_stun_usage *usage, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { + struct bind_usage *bu; pj_stun_session *session; pj_status_t status; - session = (pj_stun_session*) pj_stun_usage_get_user_data(usage); + bu = (struct bind_usage*) pj_stun_usage_get_user_data(usage); + session = bu->session; /* Handle packet to session */ status = pj_stun_session_on_rx_pkt(session, (pj_uint8_t*)pkt, pkt_size, @@ -113,9 +132,11 @@ static pj_status_t sess_on_send_msg(pj_stun_session *sess, const pj_sockaddr_t *dst_addr, unsigned addr_len) { + struct bind_usage *bu; pj_stun_usage *usage; - usage = pj_stun_session_get_user_data(sess); + bu = (struct bind_usage*) pj_stun_session_get_user_data(sess); + usage = bu->usage; return pj_stun_usage_sendto(usage, pkt, pkt_size, 0, dst_addr, addr_len); @@ -155,7 +176,7 @@ static pj_status_t sess_on_rx_request(pj_stun_session *sess, 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_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, src_addr, src_addr_len); if (status != PJ_SUCCESS) { @@ -172,3 +193,14 @@ static pj_status_t sess_on_rx_request(pj_stun_session *sess, } +static void usage_on_destroy(pj_stun_usage *usage) +{ + struct bind_usage *bu; + + bu = (struct bind_usage*) pj_stun_usage_get_user_data(usage); + if (bu==NULL) + return; + + pj_stun_session_destroy(bu->session); + pj_pool_release(bu->pool); +} diff --git a/pjlib-util/src/pjstun-srv-test/main.c b/pjlib-util/src/pjstun-srv-test/main.c index 6bc3382d3..13d64bcac 100644 --- a/pjlib-util/src/pjstun-srv-test/main.c +++ b/pjlib-util/src/pjstun-srv-test/main.c @@ -18,6 +18,8 @@ */ #include "server.h" +#define THIS_FILE "main.c" + struct options { char *realm; @@ -41,7 +43,7 @@ static void usage(void) } -static void server_main(void) +static void server_main(pj_stun_server *srv) { int quit = 0; @@ -49,12 +51,17 @@ static void server_main(void) char line[10]; printf("Menu:\n" + " d Dump status\n" " q Quit\n" "Choice:"); fgets(line, sizeof(line), stdin); - if (line[0] == 'q') + if (line[0] == 'q') { quit = 1; + } else if (line[0] == 'd') { + pj_stun_server_info *si = pj_stun_server_get_info(srv); + pj_pool_factory_dump(si->pf, PJ_TRUE); + } } } @@ -71,7 +78,6 @@ int main(int argc, char *argv[]) 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) { @@ -116,15 +122,14 @@ int main(int argc, char *argv[]) return 1; } - status = pj_stun_bind_usage_create(srv, NULL, 3478, &bu); + status = pj_stun_bind_usage_create(srv, NULL, 3478, NULL); if (status != PJ_SUCCESS) { pj_stun_perror(THIS_FILE, "Unable to create bind usage", status); return 1; } - server_main(); + server_main(srv); - pj_stun_usage_destroy(bu); pj_stun_server_destroy(srv); pj_pool_factory_dump(&cp.factory, PJ_TRUE); pj_shutdown(); diff --git a/pjlib-util/src/pjstun-srv-test/server.c b/pjlib-util/src/pjstun-srv-test/server.c index b95b349e7..5fdb233e0 100644 --- a/pjlib-util/src/pjstun-srv-test/server.c +++ b/pjlib-util/src/pjstun-srv-test/server.c @@ -29,6 +29,9 @@ struct pj_stun_server pj_bool_t thread_quit_flag; pj_thread_t **threads; + + unsigned usage_cnt; + pj_stun_usage *usage[32]; }; PJ_DEF(pj_status_t) pj_stun_perror( const char *sender, @@ -109,10 +112,62 @@ PJ_DEF(pj_stun_server_info*) pj_stun_server_get_info(pj_stun_server *srv) } +pj_status_t pj_stun_server_register_usage(pj_stun_server *srv, + pj_stun_usage *usage) +{ + unsigned i; + + for (i=0; iusage); ++i) { + if (srv->usage[i] == usage) + return PJ_SUCCESS; + } + + for (i=0; iusage); ++i) { + if (srv->usage[i] == NULL) + break; + } + + if (i == PJ_ARRAY_SIZE(srv->usage)) + return PJ_ETOOMANY; + + srv->usage[i] = usage; + ++srv->usage_cnt; + + return PJ_SUCCESS; +} + +pj_status_t pj_stun_server_unregister_usage(pj_stun_server *srv, + pj_stun_usage *usage) +{ + unsigned i; + + for (i=0; iusage); ++i) { + if (srv->usage[i] == usage) + break; + } + + if (i != PJ_ARRAY_SIZE(srv->usage)) { + srv->usage[i] = NULL; + --srv->usage_cnt; + return PJ_SUCCESS; + } + + return PJ_ENOTFOUND; +} + + PJ_DEF(pj_status_t) pj_stun_server_destroy(pj_stun_server *srv) { unsigned i; + for (i=0; iusage); ++i) { + if (!srv->usage[i]) + continue; + + pj_stun_usage_destroy(srv->usage[i]); + pj_stun_server_unregister_usage(srv, srv->usage[i]); + } + srv->thread_quit_flag = PJ_TRUE; for (i=0; isi.thread_cnt; ++i) { pj_thread_join(srv->threads[i]); diff --git a/pjlib-util/src/pjstun-srv-test/server.h b/pjlib-util/src/pjstun-srv-test/server.h index 612ea41af..491a1af38 100644 --- a/pjlib-util/src/pjstun-srv-test/server.h +++ b/pjlib-util/src/pjstun-srv-test/server.h @@ -47,6 +47,7 @@ typedef struct pj_stun_usage_cb pj_size_t pkt_size, const pj_sockaddr_t *src_addr, unsigned src_addr_len); + void (*on_destroy)(pj_stun_usage *usage); } pj_stun_usage_cb; @@ -117,6 +118,13 @@ PJ_DEF(pj_status_t) pj_stun_bind_usage_create(pj_stun_server *srv, pj_stun_usage **p_bu); + +pj_status_t pj_stun_server_register_usage(pj_stun_server *srv, + pj_stun_usage *usage); +pj_status_t pj_stun_server_unregister_usage(pj_stun_server *srv, + pj_stun_usage *usage); + + #endif /* __STUN_SERVER_H__ */ diff --git a/pjlib-util/src/pjstun-srv-test/turn_usage.c b/pjlib-util/src/pjstun-srv-test/turn_usage.c new file mode 100644 index 000000000..2351005a7 --- /dev/null +++ b/pjlib-util/src/pjstun-srv-test/turn_usage.c @@ -0,0 +1,662 @@ +/* $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 "turn_usage.c" + +#define MAX_CLIENTS 8000 +#define MAX_PEER_PER_CLIENTS 16 +#define START_PORT 2000 +#define END_PORT 65530 + +static void tu_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 void tu_on_destroy(pj_stun_usage *usage); + +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); + + +struct turn_usage +{ + pj_pool_factory *pf; + pj_stun_endpoint *endpt; + pj_ioqueue_t *ioqueue; + pj_pool_t *pool; + pj_stun_usage *usage; + int type; + pj_stun_session *default_session; + pj_hash_table_t *client_htable; + pj_hash_table_t *peer_htable; + + unsigned max_bw_kbps; + unsigned max_lifetime; + + unsigned next_port; +}; + +struct peer; + +struct turn_client +{ + struct turn_usage *tu; + pj_pool_t *pool; + pj_sockaddr_in addr; + pj_stun_session *session; + + unsigned bw_kbps; + unsigned lifetime; + + pj_sock_t sock; + pj_ioqueue_key_t *key; + char packet[4000]; + pj_sockaddr_in src_addr; + int src_addr_len; + pj_ioqueue_op_key_t read_key; + pj_ioqueue_op_key_t write_key; + + pj_bool_t has_ad; + pj_bool_t ad_is_pending; + pj_sockaddr_in ad; +}; + +struct peer +{ + struct turn_client *client; + pj_sockaddr_in addr; +}; + +struct session_data +{ + struct turn_usage *tu; + struct turn_client *client; +}; + + +PJ_DEF(pj_status_t) pj_stun_turn_usage_create(pj_stun_server *srv, + int type, + const pj_str_t *ip_addr, + unsigned port, + pj_stun_usage **p_bu) +{ + pj_pool_t *pool; + struct turn_usage *tu; + pj_stun_server_info *si; + pj_stun_usage_cb usage_cb; + pj_stun_session_cb sess_cb; + struct session_data *sd; + pj_sockaddr_in local_addr; + pj_status_t status; + + PJ_ASSERT_RETURN(srv && (type==PJ_SOCK_DGRAM||type==PJ_SOCK_STREAM) && + p_bu, PJ_EINVAL); + si = pj_stun_server_get_info(srv); + + pool = pj_pool_create(si->pf, "turn%p", 4000, 4000, NULL); + tu = PJ_POOL_ZALLOC_T(pool, struct turn_usage); + tu->pool = pool; + tu->type = type; + tu->pf = si->pf; + tu->endpt = si->endpt; + tu->next_port = START_PORT; + + status = pj_sockaddr_in_init(&local_addr, ip_addr, (pj_uint16_t)port); + if (status != PJ_SUCCESS) + return status; + + /* Create usage */ + pj_bzero(&usage_cb, sizeof(usage_cb)); + usage_cb.on_rx_data = &tu_on_rx_data; + usage_cb.on_destroy = &tu_on_destroy; + status = pj_stun_usage_create(srv, "turn%p", &usage_cb, + PJ_AF_INET, tu->type, 0, + &local_addr, sizeof(local_addr), + &tu->usage); + if (status != PJ_SUCCESS) { + pj_pool_release(pool); + return status; + } + pj_stun_usage_set_user_data(tu->usage, tu); + + /* Init hash tables */ + tu->client_htable = pj_hash_create(tu->pool, MAX_CLIENTS); + tu->peer_htable = pj_hash_create(tu->pool, MAX_CLIENTS); + + /* Create default session */ + 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, "turns%p", &sess_cb, PJ_FALSE, + &tu->default_session); + if (status != PJ_SUCCESS) { + pj_stun_usage_destroy(tu->usage); + return status; + } + + sd = PJ_POOL_ZALLOC_T(pool, struct session_data); + sd->tu = tu; + pj_stun_session_set_user_data(tu->default_session, sd); + + *p_bu = tu->usage; + + return PJ_SUCCESS; +} + + +static void tu_on_destroy(pj_stun_usage *usage) +{ + struct turn_usage *tu; + + tu = (struct turn_usage*) pj_stun_usage_get_user_data(usage); + PJ_TODO(DESTROY_ALL_CLIENTS); + pj_pool_release(tu->pool); +} + + +static void tu_on_rx_data(pj_stun_usage *usage, + void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ + struct turn_usage *tu; + pj_stun_session *session; + struct turn_client *client; + unsigned flags; + pj_status_t status; + + /* Which usage instance is this */ + tu = (struct turn_usage*) pj_stun_usage_get_user_data(usage); + + /* Lookup client structure based on source address */ + client = (struct turn_client*) pj_hash_get(tu->client_htable, src_addr, + src_addr_len, NULL); + + if (client == NULL) { + session = tu->default_session; + } else { + session = client->session; + } + + /* Handle packet to session */ + flags = PJ_STUN_CHECK_PACKET; + if (tu->type == PJ_SOCK_DGRAM) + flags |= PJ_STUN_IS_DATAGRAM; + + status = pj_stun_session_on_rx_pkt(session, (pj_uint8_t*)pkt, pkt_size, + flags, 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 tu_alloc_port(struct turn_usage *tu, + int type, + unsigned rpp_bits, + const pj_sockaddr_in *req_addr, + pj_sock_t *p_sock, + int *err_code) +{ + enum { RETRY = 100 }; + pj_sockaddr_in addr; + pj_sock_t sock = PJ_INVALID_SOCKET; + unsigned retry; + pj_status_t status; + + if (req_addr && req_addr->sin_port != 0) { + + *err_code = PJ_STUN_STATUS_INVALID_PORT; + + /* Allocate specific port */ + status = pj_sock_socket(PJ_AF_INET, type, 0, &sock); + if (status != PJ_SUCCESS) + return status; + + /* Bind */ + status = pj_sock_bind(sock, req_addr, sizeof(pj_sockaddr_in)); + if (status != PJ_SUCCESS) { + pj_sock_close(sock); + return status; + } + + /* Success */ + *p_sock = sock; + return PJ_SUCCESS; + + } else { + *err_code = PJ_STUN_STATUS_INSUFFICIENT_CAPACITY; + + if (req_addr && req_addr->sin_addr.s_addr) { + *err_code = PJ_STUN_STATUS_INVALID_IP_ADDR; + pj_memcpy(&addr, req_addr, sizeof(pj_sockaddr_in)); + } else { + pj_sockaddr_in_init(&addr, NULL, 0); + } + + for (retry=0; retrynext_port & 0x01)==0) + tu->next_port++; + break; + case 2: + case 3: + if ((tu->next_port & 0x01)==1) + tu->next_port++; + break; + } + + status = pj_sock_socket(PJ_AF_INET, type, 0, &sock); + if (status != PJ_SUCCESS) + return status; + + addr.sin_port = pj_htons((pj_uint16_t)tu->next_port); + + if (++tu->next_port > END_PORT) + tu->next_port = START_PORT; + + status = pj_sock_bind(sock, &addr, sizeof(addr)); + if (status != PJ_SUCCESS) { + pj_sock_close(sock); + sock = PJ_INVALID_SOCKET; + + /* If client requested specific IP address, assume that + * bind failed because the IP address is not valid. We + * don't want to retry that since it will exhaust our + * port space. + */ + if (req_addr && req_addr->sin_addr.s_addr) + break; + } + } + + if (sock == PJ_INVALID_SOCKET) { + return status; + } + + return PJ_SUCCESS; + } +} + +/****************************************************************************/ + +static pj_status_t client_create(struct turn_usage *tu, + const pj_stun_msg *msg, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len, + struct turn_client **p_client) +{ + pj_pool_t *pool; + struct turn_client *client; + pj_stun_session_cb sess_cb; + struct session_data *sd; + pj_status_t status; + + PJ_UNUSED_ARG(msg); + + pool = pj_pool_create(tu->pf, "turnc%p", 4000, 4000, NULL); + client = PJ_POOL_ZALLOC_T(pool, struct turn_client); + client->pool = pool; + client->tu = tu; + client->sock = PJ_INVALID_SOCKET; + + /* Create session */ + 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(tu->endpt, "turnc%p", &sess_cb, PJ_FALSE, + &client->session); + if (status != PJ_SUCCESS) { + pj_pool_release(pool); + return status; + } + + sd = PJ_POOL_ZALLOC_T(pool, struct session_data); + sd->tu = tu; + sd->client = client; + pj_stun_session_set_user_data(client->session, sd); + + /* Register to hash table */ + pj_hash_set(pool, tu->client_htable, src_addr, src_addr_len, 0, client); + + *p_client = client; + return PJ_SUCCESS; +} + +static pj_status_t client_destroy(struct turn_client *client) +{ +} + +static void client_on_read_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_read) +{ +} + +static void client_on_write_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_sent) +{ +} + +static pj_status_t client_create_relay(struct turn_client *client) +{ + pj_ioqueue_callback client_ioq_cb; + pj_status_t status; + + /* Register to ioqueue */ + pj_bzero(&client_ioq_cb, sizeof(client_ioq_cb)); + client_ioq_cb.on_read_complete = &client_on_read_complete; + client_ioq_cb.on_write_complete = &client_on_write_complete; + status = pj_ioqueue_register_sock(client->pool, client->tu->ioqueue, + client->sock, client, + &client_ioq_cb, &client->key); + if (status != PJ_SUCCESS) { + pj_sock_close(client->sock); + client->sock = PJ_INVALID_SOCKET; + return status; + } + + pj_ioqueue_op_key_init(&client->read_key, sizeof(client->read_key)); + pj_ioqueue_op_key_init(&client->write_key, sizeof(client->write_key)); + + /* Trigger the first read */ + client_on_read_complete(client->key, &client->read_key, 0); + + return PJ_SUCCESS; +} + +static pj_status_t client_handle_allocate_req(struct turn_client *client, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ + const pj_stun_bandwidth_attr *a_bw; + const pj_stun_lifetime_attr *a_lf; + const pj_stun_req_port_props_attr *a_rpp; + const pj_stun_req_transport_attr *a_rt; + const pj_stun_req_ip_attr *a_rip; + pj_stun_tx_data *response; + pj_sockaddr_in req_addr; + int addr_len; + unsigned type; + unsigned rpp_bits; + pj_status_t status; + + a_bw = (const pj_stun_bandwidth_attr *) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_BANDWIDTH, 0); + a_lf = (const pj_stun_lifetime_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0); + a_rpp = (const pj_stun_req_port_props_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REQ_PORT_PROPS, 0); + a_rt = (const pj_stun_req_transport_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REQ_TRANSPORT, 0); + a_rip = (const pj_stun_req_ip_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REQ_IP, 0); + + /* Init requested local address */ + pj_sockaddr_in_init(&req_addr, NULL, 0); + + /* Process BANDWIDTH attribute */ + if (a_bw && a_bw->value > client->tu->max_bw_kbps) { + status = pj_stun_session_create_response(client->session, msg, + PJ_STUN_STATUS_INSUFFICIENT_CAPACITY, + NULL, &response); + if (status == PJ_SUCCESS && response) { + pj_stun_session_send_msg(client->session, PJ_TRUE, + src_addr, src_addr_len, response); + } + return -1; + } else if (a_bw) { + client->bw_kbps = a_bw->value; + } else { + client->bw_kbps = client->tu->max_bw_kbps; + } + + /* Process REQUESTED-TRANSPORT attribute */ + if (a_rt && a_rt->value != 0) { + status = pj_stun_session_create_response(client->session, msg, + PJ_STUN_STATUS_UNSUPP_TRANSPORT_PROTO, + NULL, &response); + if (status == PJ_SUCCESS && response) { + pj_stun_session_send_msg(client->session, PJ_TRUE, + src_addr, src_addr_len, response); + } + return -1; + } else if (a_rt) { + type = a_rt->value ? PJ_SOCK_STREAM : PJ_SOCK_DGRAM; + } else { + type = client->tu->type;; + } + + /* Process REQUESTED-IP attribute */ + if (a_rip && a_rip->addr.addr.sa_family != PJ_AF_INET) { + status = pj_stun_session_create_response(client->session, msg, + PJ_STUN_STATUS_INVALID_IP_ADDR, + NULL, &response); + if (status == PJ_SUCCESS && response) { + pj_stun_session_send_msg(client->session, PJ_TRUE, + src_addr, src_addr_len, response); + } + return -1; + + } else if (a_rip) { + req_addr.sin_addr.s_addr = a_rip->addr.ipv4.sin_addr.s_addr; + } + + /* Process REQUESTED-PORT-PROPS attribute */ + if (a_rpp) { + unsigned port; + + rpp_bits = (a_rpp->value & 0x00030000) >> 16; + port = (a_rpp->value & 0xFFFF); + req_addr.sin_port = pj_htons((pj_uint8_t)port); + } else { + rpp_bits = 0; + } + + /* Process LIFETIME attribute */ + if (a_lf && a_lf->value > client->tu->max_lifetime) { + client->lifetime = client->tu->max_lifetime; + } else if (a_lf) { + client->lifetime = a_lf->value; + } else { + client->lifetime = client->tu->max_lifetime; + } + + /* Allocate socket if we don't have one */ + if (client->sock == PJ_INVALID_SOCKET) { + int err_code; + + status = tu_alloc_port(client->tu, type, rpp_bits, &req_addr, + &client->sock, &err_code); + if (status != PJ_SUCCESS) { + + status = pj_stun_session_create_response(client->session, msg, + err_code, NULL, + &response); + if (status == PJ_SUCCESS && response) { + pj_stun_session_send_msg(client->session, PJ_TRUE, + src_addr, src_addr_len, response); + } + return -1; + } + + status = client_create_relay(client); + if (status != PJ_SUCCESS) { + status = pj_stun_session_create_response(client->session, msg, + PJ_STUN_STATUS_SERVER_ERROR, + NULL, &response); + if (status == PJ_SUCCESS && response) { + pj_stun_session_send_msg(client->session, PJ_TRUE, + src_addr, src_addr_len, response); + } + return -1; + } + } else { + /* Otherwise check if the port parameter stays the same */ + /* TODO */ + } + + /* Done successfully, create and send success response */ + status = pj_stun_session_create_response(client->session, msg, + 0, NULL, &response); + if (status != PJ_SUCCESS) { + return -1; + } + + pj_stun_msg_add_uint_attr(response->pool, response->msg, + PJ_STUN_ATTR_BANDWIDTH, client->bw_kbps); + pj_stun_msg_add_uint_attr(response->pool, response->msg, + PJ_STUN_ATTR_LIFETIME, client->lifetime); + pj_stun_msg_add_ip_addr_attr(response->pool, response->msg, + PJ_STUN_ATTR_MAPPED_ADDR, PJ_FALSE, + src_addr, src_addr_len); + pj_stun_msg_add_ip_addr_attr(response->pool, response->msg, + PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, + src_addr, src_addr_len); + + addr_len = sizeof(req_addr); + pj_sock_getsockname(client->sock, &req_addr, &addr_len); + pj_stun_msg_add_ip_addr_attr(response->pool, response->msg, + PJ_STUN_ATTR_RELAY_ADDR, PJ_FALSE, + &req_addr, addr_len); + + return pj_stun_session_send_msg(client->session, PJ_TRUE, + src_addr, src_addr_len, response); +} + +static pj_status_t client_handle_send_ind(struct turn_client *client, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ +} + +static pj_status_t client_handle_unknown_msg(struct turn_client *client, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ +} + +static pj_status_t client_handle_stun_msg(struct turn_client *client, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ + pj_status_t status; + + switch (msg->hdr.type) { + case PJ_STUN_ALLOCATE_REQUEST: + return client_handle_allocate_req(client, pkt, pkt_len, msg, + src_addr, src_addr_len); + + case PJ_STUN_SEND_INDICATION: + return client_handle_send_ind(client, pkt, pkt_len, msg, + src_addr, src_addr_len); + + default: + return client_handle_unknown_msg(client, pkt, pkt_len, msg, + src_addr, src_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) +{ + struct session_data *sd; + struct turn_client *client; + pj_stun_tx_data *tdata; + pj_status_t status; + + sd = (struct session_data*) pj_stun_session_get_user_data(sess); + + if (sd->client == NULL) { + /* No client is associated with this source address. Create a new + * one if this is an Allocate request. + */ + if (msg->hdr.type != PJ_STUN_ALLOCATE_REQUEST) { + PJ_LOG(4,(THIS_FILE, "Received first packet not Allocate request")); + return PJ_SUCCESS; + } + + PJ_TODO(SUPPORT_MOVE); + + status = client_create(sd->tu, msg, src_addr, src_addr_len, &client); + if (status != PJ_SUCCESS) { + pj_stun_perror(THIS_FILE, "Error creating new TURN client", status); + return status; + } + + } else { + client = sd->client; + } + + return client_handle_stun_msg(client, pkt, pkt_len, msg, + src_addr, 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) +{ + struct session_data *sd; + + sd = (struct session_data*) pj_stun_session_get_user_data(sess); + + if (sd->tu->type == PJ_SOCK_DGRAM) { + return pj_stun_usage_sendto(sd->tu->usage, pkt, pkt_size, 0, + dst_addr, addr_len); + } else { + return PJ_ENOTSUP; + } +} + + + diff --git a/pjlib-util/src/pjstun-srv-test/usage.c b/pjlib-util/src/pjstun-srv-test/usage.c index 6afe3c3fd..198e0313b 100644 --- a/pjlib-util/src/pjstun-srv-test/usage.c +++ b/pjlib-util/src/pjstun-srv-test/usage.c @@ -30,6 +30,7 @@ struct worker struct pj_stun_usage { pj_pool_t *pool; + pj_stun_server *srv; pj_mutex_t *mutex; pj_stun_usage_cb cb; int type; @@ -75,6 +76,7 @@ PJ_DEF(pj_status_t) pj_stun_usage_create( pj_stun_server *srv, pool = pj_pool_create(si->pf, name, 4000, 4000, NULL); usage = PJ_POOL_ZALLOC_T(pool, pj_stun_usage); usage->pool = pool; + usage->srv = srv; status = pj_mutex_create_simple(pool, name, &usage->mutex); if (status != PJ_SUCCESS) @@ -129,6 +131,8 @@ PJ_DEF(pj_status_t) pj_stun_usage_create( pj_stun_server *srv, goto on_error; } + pj_stun_server_register_usage(srv, usage); + *p_usage = usage; return PJ_SUCCESS; @@ -143,6 +147,10 @@ on_error: */ PJ_DEF(pj_status_t) pj_stun_usage_destroy(pj_stun_usage *usage) { + pj_stun_server_unregister_usage(usage->srv, usage); + if (usage->cb.on_destroy) + (*usage->cb.on_destroy)(usage); + if (usage->key) { pj_ioqueue_unregister(usage->key); usage->key = NULL;