blob: 17ded7b05dc1378e854955c349e31c802c29d494 [file] [log] [blame]
Benny Prijonod1e862f2008-02-21 15:54:27 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include "turn.h"
Benny Prijono708725a2008-03-09 12:55:00 +000020#include "auth.h"
Benny Prijonod1e862f2008-02-21 15:54:27 +000021
22#define MAX_CLIENTS 32
23#define MAX_PEERS_PER_CLIENT 8
Benny Prijono708725a2008-03-09 12:55:00 +000024//#define MAX_HANDLES (MAX_CLIENTS*MAX_PEERS_PER_CLIENT+MAX_LISTENERS)
25#define MAX_HANDLES PJ_IOQUEUE_MAX_HANDLES
Benny Prijonod1e862f2008-02-21 15:54:27 +000026#define MAX_TIMER (MAX_HANDLES * 2)
27#define MIN_PORT 49152
28#define MAX_PORT 65535
29#define MAX_LISTENERS 16
30#define MAX_THREADS 2
Benny Prijono879ad1a2008-04-09 09:38:12 +000031#define MAX_NET_EVENTS 1000
Benny Prijonod1e862f2008-02-21 15:54:27 +000032
Benny Prijonod1e862f2008-02-21 15:54:27 +000033/* Prototypes */
Benny Prijono708725a2008-03-09 12:55:00 +000034static int server_thread_proc(void *arg);
Benny Prijonod1e862f2008-02-21 15:54:27 +000035static pj_status_t on_tx_stun_msg( pj_stun_session *sess,
Benny Prijono879ad1a2008-04-09 09:38:12 +000036 void *token,
Benny Prijonod1e862f2008-02-21 15:54:27 +000037 const void *pkt,
38 pj_size_t pkt_size,
39 const pj_sockaddr_t *dst_addr,
40 unsigned addr_len);
41static pj_status_t on_rx_stun_request(pj_stun_session *sess,
42 const pj_uint8_t *pkt,
43 unsigned pkt_len,
Benny Prijonoa5d214f2008-03-19 23:00:30 +000044 const pj_stun_rx_data *rdata,
Benny Prijono879ad1a2008-04-09 09:38:12 +000045 void *user_data,
Benny Prijonod1e862f2008-02-21 15:54:27 +000046 const pj_sockaddr_t *src_addr,
47 unsigned src_addr_len);
48
Benny Prijono708725a2008-03-09 12:55:00 +000049struct saved_cred
50{
51 pj_str_t realm;
52 pj_str_t username;
53 pj_str_t nonce;
54 int data_type;
55 pj_str_t data;
56};
57
58
Benny Prijonob05aafc2008-03-08 00:54:04 +000059/*
Benny Prijono708725a2008-03-09 12:55:00 +000060 * Get transport type name, normally for logging purpose only.
Benny Prijonob05aafc2008-03-08 00:54:04 +000061 */
Benny Prijono708725a2008-03-09 12:55:00 +000062PJ_DEF(const char*) pj_turn_tp_type_name(int tp_type)
Benny Prijonob05aafc2008-03-08 00:54:04 +000063{
64 /* Must be 3 characters long! */
Benny Prijono708725a2008-03-09 12:55:00 +000065 if (tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +000066 return "UDP";
Benny Prijono708725a2008-03-09 12:55:00 +000067 } else if (tp_type == PJ_TURN_TP_TCP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +000068 return "TCP";
Benny Prijono708725a2008-03-09 12:55:00 +000069 } else {
70 pj_assert(!"Unsupported transport");
Benny Prijonob05aafc2008-03-08 00:54:04 +000071 return "???";
Benny Prijono708725a2008-03-09 12:55:00 +000072 }
Benny Prijonob05aafc2008-03-08 00:54:04 +000073}
Benny Prijonod1e862f2008-02-21 15:54:27 +000074
75/*
76 * Create server.
77 */
Benny Prijono708725a2008-03-09 12:55:00 +000078PJ_DEF(pj_status_t) pj_turn_srv_create(pj_pool_factory *pf,
79 pj_turn_srv **p_srv)
Benny Prijonod1e862f2008-02-21 15:54:27 +000080{
81 pj_pool_t *pool;
Benny Prijono879ad1a2008-04-09 09:38:12 +000082 pj_stun_session_cb sess_cb;
Benny Prijono708725a2008-03-09 12:55:00 +000083 pj_turn_srv *srv;
84 unsigned i;
Benny Prijonod1e862f2008-02-21 15:54:27 +000085 pj_status_t status;
86
87 PJ_ASSERT_RETURN(pf && p_srv, PJ_EINVAL);
88
89 /* Create server and init core settings */
90 pool = pj_pool_create(pf, "srv%p", 1000, 1000, NULL);
Benny Prijono708725a2008-03-09 12:55:00 +000091 srv = PJ_POOL_ZALLOC_T(pool, pj_turn_srv);
92 srv->obj_name = pool->obj_name;
Benny Prijonod1e862f2008-02-21 15:54:27 +000093 srv->core.pf = pf;
94 srv->core.pool = pool;
Benny Prijono708725a2008-03-09 12:55:00 +000095 srv->core.tls_key = srv->core.tls_data = -1;
Benny Prijonod1e862f2008-02-21 15:54:27 +000096
Benny Prijono708725a2008-03-09 12:55:00 +000097 /* Create ioqueue */
Benny Prijonod1e862f2008-02-21 15:54:27 +000098 status = pj_ioqueue_create(pool, MAX_HANDLES, &srv->core.ioqueue);
99 if (status != PJ_SUCCESS)
100 goto on_error;
101
Benny Prijono708725a2008-03-09 12:55:00 +0000102 /* Server mutex */
103 status = pj_lock_create_recursive_mutex(pool, srv->obj_name,
104 &srv->core.lock);
105 if (status != PJ_SUCCESS)
106 goto on_error;
107
108 /* Allocate TLS */
109 status = pj_thread_local_alloc(&srv->core.tls_key);
110 if (status != PJ_SUCCESS)
111 goto on_error;
112
113 status = pj_thread_local_alloc(&srv->core.tls_data);
114 if (status != PJ_SUCCESS)
115 goto on_error;
116
117 /* Create timer heap */
Benny Prijonod1e862f2008-02-21 15:54:27 +0000118 status = pj_timer_heap_create(pool, MAX_TIMER, &srv->core.timer_heap);
119 if (status != PJ_SUCCESS)
120 goto on_error;
121
Benny Prijono708725a2008-03-09 12:55:00 +0000122 /* Configure lock for the timer heap */
123 pj_timer_heap_set_lock(srv->core.timer_heap, srv->core.lock, PJ_FALSE);
124
125 /* Array of listeners */
126 srv->core.listener = (pj_turn_listener**)
127 pj_pool_calloc(pool, MAX_LISTENERS,
Benny Prijonod1e862f2008-02-21 15:54:27 +0000128 sizeof(srv->core.listener[0]));
Benny Prijono708725a2008-03-09 12:55:00 +0000129
Benny Prijonod1e862f2008-02-21 15:54:27 +0000130 /* Create hash tables */
131 srv->tables.alloc = pj_hash_create(pool, MAX_CLIENTS);
132 srv->tables.res = pj_hash_create(pool, MAX_CLIENTS);
Benny Prijonod1e862f2008-02-21 15:54:27 +0000133
134 /* Init ports settings */
135 srv->ports.min_udp = srv->ports.next_udp = MIN_PORT;
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000136 srv->ports.max_udp = MAX_PORT;
Benny Prijonod1e862f2008-02-21 15:54:27 +0000137 srv->ports.min_tcp = srv->ports.next_tcp = MIN_PORT;
138 srv->ports.max_tcp = MAX_PORT;
139
140 /* Init STUN config */
141 pj_stun_config_init(&srv->core.stun_cfg, pf, 0, srv->core.ioqueue,
142 srv->core.timer_heap);
143
Benny Prijono708725a2008-03-09 12:55:00 +0000144 /* Init STUN credential */
145 srv->core.cred.type = PJ_STUN_AUTH_CRED_DYNAMIC;
146 srv->core.cred.data.dyn_cred.user_data = srv;
147 srv->core.cred.data.dyn_cred.get_auth = &pj_turn_get_auth;
Benny Prijono708725a2008-03-09 12:55:00 +0000148 srv->core.cred.data.dyn_cred.get_password = &pj_turn_get_password;
149 srv->core.cred.data.dyn_cred.verify_nonce = &pj_turn_verify_nonce;
150
Benny Prijono879ad1a2008-04-09 09:38:12 +0000151 /* Create STUN session to handle new allocation */
152 pj_bzero(&sess_cb, sizeof(sess_cb));
153 sess_cb.on_rx_request = &on_rx_stun_request;
154 sess_cb.on_send_msg = &on_tx_stun_msg;
155
156 status = pj_stun_session_create(&srv->core.stun_cfg, srv->obj_name,
157 &sess_cb, PJ_FALSE, &srv->core.stun_sess);
158 if (status != PJ_SUCCESS) {
159 goto on_error;
160 }
161
162 pj_stun_session_set_user_data(srv->core.stun_sess, srv);
163 pj_stun_session_set_credential(srv->core.stun_sess, PJ_STUN_AUTH_LONG_TERM,
164 &srv->core.cred);
165
166
Benny Prijono708725a2008-03-09 12:55:00 +0000167 /* Array of worker threads */
168 srv->core.thread_cnt = MAX_THREADS;
169 srv->core.thread = (pj_thread_t**)
170 pj_pool_calloc(pool, srv->core.thread_cnt,
171 sizeof(pj_thread_t*));
172
173 /* Start the worker threads */
174 for (i=0; i<srv->core.thread_cnt; ++i) {
175 status = pj_thread_create(pool, srv->obj_name, &server_thread_proc,
176 srv, 0, 0, &srv->core.thread[i]);
177 if (status != PJ_SUCCESS)
178 goto on_error;
179 }
180
181 /* We're done. Application should add listeners now */
182 PJ_LOG(4,(srv->obj_name, "TURN server v%s is running",
183 pj_get_version()));
184
Benny Prijonod1e862f2008-02-21 15:54:27 +0000185 *p_srv = srv;
186 return PJ_SUCCESS;
187
188on_error:
Benny Prijono708725a2008-03-09 12:55:00 +0000189 pj_turn_srv_destroy(srv);
Benny Prijonod1e862f2008-02-21 15:54:27 +0000190 return status;
191}
192
Benny Prijono708725a2008-03-09 12:55:00 +0000193
194/*
195 * Handle timer and network events
Benny Prijonod1e862f2008-02-21 15:54:27 +0000196 */
Benny Prijono708725a2008-03-09 12:55:00 +0000197static void srv_handle_events(pj_turn_srv *srv, const pj_time_val *max_timeout)
Benny Prijonod1e862f2008-02-21 15:54:27 +0000198{
Benny Prijono708725a2008-03-09 12:55:00 +0000199 /* timeout is 'out' var. This just to make compiler happy. */
200 pj_time_val timeout = { 0, 0};
201 unsigned net_event_count = 0;
202 int c;
203
204 /* Poll the timer. The timer heap has its own mutex for better
205 * granularity, so we don't need to lock the server.
206 */
207 timeout.sec = timeout.msec = 0;
208 c = pj_timer_heap_poll( srv->core.timer_heap, &timeout );
209
210 /* timer_heap_poll should never ever returns negative value, or otherwise
211 * ioqueue_poll() will block forever!
212 */
213 pj_assert(timeout.sec >= 0 && timeout.msec >= 0);
214 if (timeout.msec >= 1000) timeout.msec = 999;
215
216 /* If caller specifies maximum time to wait, then compare the value with
217 * the timeout to wait from timer, and use the minimum value.
218 */
219 if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) {
220 timeout = *max_timeout;
221 }
222
223 /* Poll ioqueue.
224 * Repeat polling the ioqueue while we have immediate events, because
225 * timer heap may process more than one events, so if we only process
226 * one network events at a time (such as when IOCP backend is used),
227 * the ioqueue may have trouble keeping up with the request rate.
228 *
229 * For example, for each send() request, one network event will be
230 * reported by ioqueue for the send() completion. If we don't poll
231 * the ioqueue often enough, the send() completion will not be
232 * reported in timely manner.
233 */
234 do {
235 c = pj_ioqueue_poll( srv->core.ioqueue, &timeout);
236 if (c < 0) {
237 pj_thread_sleep(PJ_TIME_VAL_MSEC(timeout));
238 return;
239 } else if (c == 0) {
240 break;
241 } else {
242 net_event_count += c;
243 timeout.sec = timeout.msec = 0;
244 }
245 } while (c > 0 && net_event_count < MAX_NET_EVENTS);
246
247}
248
249/*
250 * Server worker thread proc.
251 */
252static int server_thread_proc(void *arg)
253{
254 pj_turn_srv *srv = (pj_turn_srv*)arg;
255
256 while (!srv->core.quit) {
Benny Prijono879ad1a2008-04-09 09:38:12 +0000257 pj_time_val timeout_max = {0, 100};
Benny Prijono708725a2008-03-09 12:55:00 +0000258 srv_handle_events(srv, &timeout_max);
259 }
260
261 return 0;
262}
263
264/*
265 * Destroy the server.
266 */
267PJ_DEF(pj_status_t) pj_turn_srv_destroy(pj_turn_srv *srv)
268{
269 pj_hash_iterator_t itbuf, *it;
270 unsigned i;
271
272 /* Stop all worker threads */
273 srv->core.quit = PJ_TRUE;
274 for (i=0; i<srv->core.thread_cnt; ++i) {
275 if (srv->core.thread[i]) {
276 pj_thread_join(srv->core.thread[i]);
277 pj_thread_destroy(srv->core.thread[i]);
278 srv->core.thread[i] = NULL;
279 }
280 }
281
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000282 /* Destroy all allocations FIRST */
283 if (srv->tables.alloc) {
284 it = pj_hash_first(srv->tables.alloc, &itbuf);
285 while (it != NULL) {
286 pj_turn_allocation *alloc = (pj_turn_allocation*)
287 pj_hash_this(srv->tables.alloc, it);
288 pj_hash_iterator_t *next = pj_hash_next(srv->tables.alloc, it);
289 pj_turn_allocation_destroy(alloc);
290 it = next;
291 }
292 }
293
Benny Prijono879ad1a2008-04-09 09:38:12 +0000294 /* Destroy all listeners. */
Benny Prijono708725a2008-03-09 12:55:00 +0000295 for (i=0; i<srv->core.lis_cnt; ++i) {
296 if (srv->core.listener[i]) {
297 pj_turn_listener_destroy(srv->core.listener[i]);
298 srv->core.listener[i] = NULL;
299 }
Benny Prijono879ad1a2008-04-09 09:38:12 +0000300 }
301
302 /* Destroy STUN session */
303 if (srv->core.stun_sess) {
304 pj_stun_session_destroy(srv->core.stun_sess);
305 srv->core.stun_sess = NULL;
Benny Prijono708725a2008-03-09 12:55:00 +0000306 }
307
Benny Prijono708725a2008-03-09 12:55:00 +0000308 /* Destroy hash tables (well, sort of) */
309 if (srv->tables.alloc) {
310 srv->tables.alloc = NULL;
311 srv->tables.res = NULL;
312 }
313
314 /* Destroy timer heap */
315 if (srv->core.timer_heap) {
316 pj_timer_heap_destroy(srv->core.timer_heap);
317 srv->core.timer_heap = NULL;
318 }
319
320 /* Destroy ioqueue */
321 if (srv->core.ioqueue) {
322 pj_ioqueue_destroy(srv->core.ioqueue);
323 srv->core.ioqueue = NULL;
324 }
325
326 /* Destroy thread local IDs */
327 if (srv->core.tls_key != -1) {
328 pj_thread_local_free(srv->core.tls_key);
329 srv->core.tls_key = -1;
330 }
331 if (srv->core.tls_data != -1) {
332 pj_thread_local_free(srv->core.tls_data);
333 srv->core.tls_data = -1;
334 }
335
336 /* Destroy server lock */
337 if (srv->core.lock) {
338 pj_lock_destroy(srv->core.lock);
339 srv->core.lock = NULL;
340 }
341
342 /* Release pool */
343 if (srv->core.pool) {
344 pj_pool_t *pool = srv->core.pool;
345 srv->core.pool = NULL;
346 pj_pool_release(pool);
347 }
348
349 /* Done */
Benny Prijonod1e862f2008-02-21 15:54:27 +0000350 return PJ_SUCCESS;
351}
352
Benny Prijono708725a2008-03-09 12:55:00 +0000353
354/*
Benny Prijonod1e862f2008-02-21 15:54:27 +0000355 * Add listener.
356 */
Benny Prijono708725a2008-03-09 12:55:00 +0000357PJ_DEF(pj_status_t) pj_turn_srv_add_listener(pj_turn_srv *srv,
358 pj_turn_listener *lis)
Benny Prijonod1e862f2008-02-21 15:54:27 +0000359{
Benny Prijonod1e862f2008-02-21 15:54:27 +0000360 unsigned index;
Benny Prijonod1e862f2008-02-21 15:54:27 +0000361
362 PJ_ASSERT_RETURN(srv && lis, PJ_EINVAL);
363 PJ_ASSERT_RETURN(srv->core.lis_cnt < MAX_LISTENERS, PJ_ETOOMANY);
364
365 /* Add to array */
366 index = srv->core.lis_cnt;
367 srv->core.listener[index] = lis;
368 lis->server = srv;
Benny Prijonod1e862f2008-02-21 15:54:27 +0000369 lis->id = index;
370 srv->core.lis_cnt++;
371
Benny Prijono708725a2008-03-09 12:55:00 +0000372 PJ_LOG(4,(srv->obj_name, "Listener %s/%s added at index %d",
373 lis->obj_name, lis->info, lis->id));
374
Benny Prijonod1e862f2008-02-21 15:54:27 +0000375 return PJ_SUCCESS;
376}
377
Benny Prijono708725a2008-03-09 12:55:00 +0000378
379/*
Benny Prijono708725a2008-03-09 12:55:00 +0000380 * Destroy listener.
381 */
382PJ_DEF(pj_status_t) pj_turn_listener_destroy(pj_turn_listener *listener)
383{
384 pj_turn_srv *srv = listener->server;
385 unsigned i;
386
387 /* Remove from our listener list */
388 pj_lock_acquire(srv->core.lock);
389 for (i=0; i<srv->core.lis_cnt; ++i) {
390 if (srv->core.listener[i] == listener) {
391 srv->core.listener[i] = NULL;
392 srv->core.lis_cnt--;
393 listener->id = PJ_TURN_INVALID_LIS_ID;
Benny Prijono708725a2008-03-09 12:55:00 +0000394 break;
395 }
396 }
397 pj_lock_release(srv->core.lock);
398
399 /* Destroy */
400 return listener->destroy(listener);
401}
402
403
Benny Prijono879ad1a2008-04-09 09:38:12 +0000404/**
405 * Add a reference to a transport.
406 */
407PJ_DEF(void) pj_turn_transport_add_ref( pj_turn_transport *transport,
408 pj_turn_allocation *alloc)
409{
410 transport->add_ref(transport, alloc);
411}
412
413
414/**
415 * Decrement transport reference counter.
416 */
417PJ_DEF(void) pj_turn_transport_dec_ref( pj_turn_transport *transport,
418 pj_turn_allocation *alloc)
419{
420 transport->dec_ref(transport, alloc);
421}
422
423
Benny Prijono708725a2008-03-09 12:55:00 +0000424/*
425 * Register an allocation to the hash tables.
426 */
427PJ_DEF(pj_status_t) pj_turn_srv_register_allocation(pj_turn_srv *srv,
428 pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000429{
430 /* Add to hash tables */
431 pj_lock_acquire(srv->core.lock);
432 pj_hash_set(alloc->pool, srv->tables.alloc,
433 &alloc->hkey, sizeof(alloc->hkey), 0, alloc);
434 pj_hash_set(alloc->pool, srv->tables.res,
435 &alloc->relay.hkey, sizeof(alloc->relay.hkey), 0,
436 &alloc->relay);
437 pj_lock_release(srv->core.lock);
438
439 return PJ_SUCCESS;
440}
441
Benny Prijono708725a2008-03-09 12:55:00 +0000442
443/*
444 * Unregister an allocation from the hash tables.
Benny Prijonob05aafc2008-03-08 00:54:04 +0000445 */
Benny Prijono708725a2008-03-09 12:55:00 +0000446PJ_DEF(pj_status_t) pj_turn_srv_unregister_allocation(pj_turn_srv *srv,
447 pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000448{
449 /* Unregister from hash tables */
450 pj_lock_acquire(srv->core.lock);
451 pj_hash_set(alloc->pool, srv->tables.alloc,
452 &alloc->hkey, sizeof(alloc->hkey), 0, NULL);
453 pj_hash_set(alloc->pool, srv->tables.res,
454 &alloc->relay.hkey, sizeof(alloc->relay.hkey), 0, NULL);
455 pj_lock_release(srv->core.lock);
456
457 return PJ_SUCCESS;
458}
459
Benny Prijonod1e862f2008-02-21 15:54:27 +0000460
Benny Prijono708725a2008-03-09 12:55:00 +0000461/* Callback from our own STUN session whenever it needs to send
462 * outgoing STUN packet.
463 */
Benny Prijonod1e862f2008-02-21 15:54:27 +0000464static pj_status_t on_tx_stun_msg( pj_stun_session *sess,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000465 void *token,
466 const void *pdu,
467 pj_size_t pdu_size,
Benny Prijonod1e862f2008-02-21 15:54:27 +0000468 const pj_sockaddr_t *dst_addr,
469 unsigned addr_len)
470{
Benny Prijono879ad1a2008-04-09 09:38:12 +0000471 pj_turn_transport *transport = (pj_turn_transport*) token;
Benny Prijonod1e862f2008-02-21 15:54:27 +0000472
Benny Prijono879ad1a2008-04-09 09:38:12 +0000473 PJ_ASSERT_RETURN(transport!=NULL, PJ_EINVALIDOP);
Benny Prijonod1e862f2008-02-21 15:54:27 +0000474
Benny Prijono879ad1a2008-04-09 09:38:12 +0000475 PJ_UNUSED_ARG(sess);
Benny Prijonod1e862f2008-02-21 15:54:27 +0000476
Benny Prijono879ad1a2008-04-09 09:38:12 +0000477 return transport->sendto(transport, pdu, pdu_size, 0,
478 dst_addr, addr_len);
Benny Prijonod1e862f2008-02-21 15:54:27 +0000479}
480
Benny Prijono708725a2008-03-09 12:55:00 +0000481
482/* Respond to STUN request */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000483static pj_status_t stun_respond(pj_stun_session *sess,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000484 pj_turn_transport *transport,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000485 const pj_stun_rx_data *rdata,
Benny Prijono708725a2008-03-09 12:55:00 +0000486 unsigned code,
487 const char *errmsg,
488 pj_bool_t cache,
489 const pj_sockaddr_t *dst_addr,
490 unsigned addr_len)
Benny Prijonod1e862f2008-02-21 15:54:27 +0000491{
492 pj_status_t status;
493 pj_str_t reason;
494 pj_stun_tx_data *tdata;
495
Benny Prijono708725a2008-03-09 12:55:00 +0000496 /* Create response */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000497 status = pj_stun_session_create_res(sess, rdata, code,
Benny Prijono708725a2008-03-09 12:55:00 +0000498 (errmsg?pj_cstr(&reason,errmsg):NULL),
Benny Prijonod1e862f2008-02-21 15:54:27 +0000499 &tdata);
500 if (status != PJ_SUCCESS)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000501 return status;
Benny Prijonod1e862f2008-02-21 15:54:27 +0000502
Benny Prijono708725a2008-03-09 12:55:00 +0000503 /* Send the response */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000504 return pj_stun_session_send_msg(sess, transport, cache, PJ_FALSE,
505 dst_addr, addr_len, tdata);
Benny Prijonod1e862f2008-02-21 15:54:27 +0000506}
507
Benny Prijono708725a2008-03-09 12:55:00 +0000508
Benny Prijono708725a2008-03-09 12:55:00 +0000509/* Callback from our own STUN session when incoming request arrives.
510 * This function is triggered by pj_stun_session_on_rx_pkt() call in
Benny Prijono879ad1a2008-04-09 09:38:12 +0000511 * pj_turn_srv_on_rx_pkt() function below.
Benny Prijono708725a2008-03-09 12:55:00 +0000512 */
Benny Prijonod1e862f2008-02-21 15:54:27 +0000513static pj_status_t on_rx_stun_request(pj_stun_session *sess,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000514 const pj_uint8_t *pdu,
515 unsigned pdu_len,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000516 const pj_stun_rx_data *rdata,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000517 void *token,
Benny Prijonod1e862f2008-02-21 15:54:27 +0000518 const pj_sockaddr_t *src_addr,
519 unsigned src_addr_len)
520{
Benny Prijono879ad1a2008-04-09 09:38:12 +0000521 pj_turn_transport *transport;
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000522 const pj_stun_msg *msg = rdata->msg;
Benny Prijono708725a2008-03-09 12:55:00 +0000523 pj_turn_srv *srv;
524 pj_turn_allocation *alloc;
Benny Prijonod1e862f2008-02-21 15:54:27 +0000525 pj_status_t status;
526
Benny Prijono879ad1a2008-04-09 09:38:12 +0000527 PJ_UNUSED_ARG(pdu);
528 PJ_UNUSED_ARG(pdu_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000529
Benny Prijono879ad1a2008-04-09 09:38:12 +0000530 transport = (pj_turn_transport*) token;
531 srv = transport->listener->server;
Benny Prijonod1e862f2008-02-21 15:54:27 +0000532
Benny Prijono708725a2008-03-09 12:55:00 +0000533 /* Respond any requests other than ALLOCATE with 437 response */
Benny Prijonod1e862f2008-02-21 15:54:27 +0000534 if (msg->hdr.type != PJ_STUN_ALLOCATE_REQUEST) {
Benny Prijono879ad1a2008-04-09 09:38:12 +0000535 stun_respond(sess, transport, rdata, PJ_STUN_SC_ALLOCATION_MISMATCH,
Benny Prijono708725a2008-03-09 12:55:00 +0000536 NULL, PJ_FALSE, src_addr, src_addr_len);
537 return PJ_SUCCESS;
Benny Prijonod1e862f2008-02-21 15:54:27 +0000538 }
539
Benny Prijonob05aafc2008-03-08 00:54:04 +0000540 /* Create new allocation. The relay resource will be allocated
541 * in this function.
542 */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000543 status = pj_turn_allocation_create(transport, src_addr, src_addr_len,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000544 rdata, sess, &alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000545 if (status != PJ_SUCCESS) {
Benny Prijono708725a2008-03-09 12:55:00 +0000546 /* STUN response has been sent, no need to reply here */
547 return PJ_SUCCESS;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000548 }
549
Benny Prijonob05aafc2008-03-08 00:54:04 +0000550 /* Done. */
551 return PJ_SUCCESS;
Benny Prijonod1e862f2008-02-21 15:54:27 +0000552}
553
554
Benny Prijonod1e862f2008-02-21 15:54:27 +0000555/*
Benny Prijono708725a2008-03-09 12:55:00 +0000556 * This callback is called by UDP listener on incoming packet. This is
557 * the first entry for incoming packet (from client) to the server. From
558 * here, the packet may be handed over to an allocation if an allocation
559 * is found for the client address, or handed over to owned STUN session
560 * if an allocation is not found.
Benny Prijonod1e862f2008-02-21 15:54:27 +0000561 */
Benny Prijono708725a2008-03-09 12:55:00 +0000562PJ_DEF(void) pj_turn_srv_on_rx_pkt(pj_turn_srv *srv,
563 pj_turn_pkt *pkt)
Benny Prijonod1e862f2008-02-21 15:54:27 +0000564{
Benny Prijono708725a2008-03-09 12:55:00 +0000565 pj_turn_allocation *alloc;
Benny Prijonod1e862f2008-02-21 15:54:27 +0000566
567 /* Get TURN allocation from the source address */
568 pj_lock_acquire(srv->core.lock);
569 alloc = pj_hash_get(srv->tables.alloc, &pkt->src, sizeof(pkt->src), NULL);
570 pj_lock_release(srv->core.lock);
571
572 /* If allocation is found, just hand over the packet to the
573 * allocation.
574 */
575 if (alloc) {
Benny Prijono708725a2008-03-09 12:55:00 +0000576 pj_turn_allocation_on_rx_client_pkt(alloc, pkt);
Benny Prijonod1e862f2008-02-21 15:54:27 +0000577 } else {
578 /* Otherwise this is a new client */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000579 unsigned options;
580 unsigned parsed_len;
Benny Prijono708725a2008-03-09 12:55:00 +0000581 pj_status_t status;
582
583 /* Check that this is a STUN message */
584 options = PJ_STUN_CHECK_PACKET;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000585 if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP)
Benny Prijono708725a2008-03-09 12:55:00 +0000586 options |= PJ_STUN_IS_DATAGRAM;
587
588 status = pj_stun_msg_check(pkt->pkt, pkt->len, options);
589 if (status != PJ_SUCCESS) {
Benny Prijono879ad1a2008-04-09 09:38:12 +0000590 /* If the first byte are not STUN, drop the packet. First byte
591 * of STUN message is always 0x00 or 0x01. Otherwise wait for
592 * more data as the data might have come from TCP.
593 *
594 * Also drop packet if it's unreasonably too big, as this might
595 * indicate invalid data that's building up in the buffer.
596 *
597 * Or if packet is a datagram.
598 */
599 if ((*pkt->pkt != 0x00 && *pkt->pkt != 0x01) ||
600 pkt->len > 1600 ||
601 (options & PJ_STUN_IS_DATAGRAM))
602 {
603 char errmsg[PJ_ERR_MSG_SIZE];
604 char ip[PJ_INET6_ADDRSTRLEN+10];
Benny Prijono708725a2008-03-09 12:55:00 +0000605
Benny Prijono879ad1a2008-04-09 09:38:12 +0000606 pkt->len = 0;
607
608 pj_strerror(status, errmsg, sizeof(errmsg));
609 PJ_LOG(5,(srv->obj_name,
610 "Non-STUN packet from %s is dropped: %s",
611 pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3),
612 errmsg));
613 }
Benny Prijono708725a2008-03-09 12:55:00 +0000614 return;
615 }
616
Benny Prijono708725a2008-03-09 12:55:00 +0000617 /* Hand over processing to STUN session. This will trigger
618 * on_rx_stun_request() callback to be called if the STUN
619 * message is a request.
620 */
621 options &= ~PJ_STUN_CHECK_PACKET;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000622 parsed_len = 0;
623 status = pj_stun_session_on_rx_pkt(srv->core.stun_sess, pkt->pkt,
624 pkt->len, options, pkt->transport,
625 &parsed_len, &pkt->src.clt_addr,
Benny Prijono708725a2008-03-09 12:55:00 +0000626 pkt->src_addr_len);
627 if (status != PJ_SUCCESS) {
628 char errmsg[PJ_ERR_MSG_SIZE];
629 char ip[PJ_INET6_ADDRSTRLEN+10];
630
631 pj_strerror(status, errmsg, sizeof(errmsg));
632 PJ_LOG(5,(srv->obj_name,
633 "Error processing STUN packet from %s: %s",
634 pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3),
635 errmsg));
Benny Prijono879ad1a2008-04-09 09:38:12 +0000636 }
637
638 if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) {
639 pkt->len = 0;
640 } else if (parsed_len > 0) {
641 if (parsed_len == pkt->len) {
642 pkt->len = 0;
643 } else {
644 pj_memmove(pkt->pkt, pkt->pkt+parsed_len,
645 pkt->len - parsed_len);
646 pkt->len -= parsed_len;
647 }
Benny Prijono708725a2008-03-09 12:55:00 +0000648 }
Benny Prijonod1e862f2008-02-21 15:54:27 +0000649 }
650}
651
652