blob: da35266cb660e9796654c2c1d76f240fedd033d9 [file] [log] [blame]
Benny Prijonob05aafc2008-03-08 00:54:04 +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"
21
Benny Prijonob05aafc2008-03-08 00:54:04 +000022
23#define THIS_FILE "allocation.c"
24
25
26enum {
27 TIMER_ID_NONE,
28 TIMER_ID_TIMEOUT,
29 TIMER_ID_DESTROY
30};
31
32#define DESTROY_DELAY {0, 500}
33#define PEER_TABLE_SIZE 32
34
Benny Prijono708725a2008-03-09 12:55:00 +000035#define MAX_CLIENT_BANDWIDTH 128 /* In Kbps */
36#define DEFA_CLIENT_BANDWIDTH 64
37
38#define MIN_LIFETIME 30
39#define MAX_LIFETIME 600
40#define DEF_LIFETIME 300
41
42
Benny Prijono708725a2008-03-09 12:55:00 +000043/* Parsed Allocation request. */
44typedef struct alloc_request
45{
46 unsigned tp_type; /* Requested transport */
47 char addr[PJ_INET6_ADDRSTRLEN]; /* Requested IP */
48 unsigned bandwidth; /* Requested bandwidth */
49 unsigned lifetime; /* Lifetime. */
50 unsigned rpp_bits; /* A bits */
51 unsigned rpp_port; /* Requested port */
52} alloc_request;
53
54
55
Benny Prijonob05aafc2008-03-08 00:54:04 +000056/* Prototypes */
Benny Prijono708725a2008-03-09 12:55:00 +000057static void destroy_allocation(pj_turn_allocation *alloc);
58static pj_status_t create_relay(pj_turn_srv *srv,
59 pj_turn_allocation *alloc,
60 const pj_stun_msg *msg,
61 const alloc_request *req,
62 pj_turn_relay_res *relay);
63static void destroy_relay(pj_turn_relay_res *relay);
Benny Prijonob05aafc2008-03-08 00:54:04 +000064static void on_rx_from_peer(pj_ioqueue_key_t *key,
65 pj_ioqueue_op_key_t *op_key,
66 pj_ssize_t bytes_read);
Benny Prijonob05aafc2008-03-08 00:54:04 +000067static pj_status_t stun_on_send_msg(pj_stun_session *sess,
68 const void *pkt,
69 pj_size_t pkt_size,
70 const pj_sockaddr_t *dst_addr,
71 unsigned addr_len);
72static pj_status_t stun_on_rx_request(pj_stun_session *sess,
73 const pj_uint8_t *pkt,
74 unsigned pkt_len,
75 const pj_stun_msg *msg,
76 const pj_sockaddr_t *src_addr,
77 unsigned src_addr_len);
78static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
79 const pj_uint8_t *pkt,
80 unsigned pkt_len,
81 const pj_stun_msg *msg,
82 const pj_sockaddr_t *src_addr,
83 unsigned src_addr_len);
84
85/* Log allocation error */
Benny Prijono708725a2008-03-09 12:55:00 +000086static void alloc_err(pj_turn_allocation *alloc, const char *title,
Benny Prijonob05aafc2008-03-08 00:54:04 +000087 pj_status_t status)
88{
89 char errmsg[PJ_ERR_MSG_SIZE];
90
91 pj_strerror(status, errmsg, sizeof(errmsg));
92 PJ_LOG(4,(alloc->obj_name, "%s for client %s: %s",
93 title, alloc->info, errmsg));
94}
95
Benny Prijono708725a2008-03-09 12:55:00 +000096
97/* Parse ALLOCATE request */
98static pj_status_t parse_allocate_req(alloc_request *cfg,
99 pj_stun_session *sess,
100 const pj_stun_msg *req,
101 const pj_sockaddr_t *src_addr,
102 unsigned src_addr_len)
103{
104 pj_stun_bandwidth_attr *attr_bw;
105 pj_stun_req_transport_attr *attr_req_tp;
106 pj_stun_res_token_attr *attr_res_token;
107 pj_stun_req_props_attr *attr_rpp;
108 pj_stun_lifetime_attr *attr_lifetime;
109
110 pj_bzero(cfg, sizeof(*cfg));
111
112 /* Get BANDWIDTH attribute, if any. */
113 attr_bw = (pj_stun_uint_attr*)
114 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_BANDWIDTH, 0);
115 if (attr_bw) {
116 cfg->bandwidth = attr_bw->value;
117 } else {
118 cfg->bandwidth = DEFA_CLIENT_BANDWIDTH;
119 }
120
121 /* Check if we can satisfy the bandwidth */
122 if (cfg->bandwidth > MAX_CLIENT_BANDWIDTH) {
123 pj_stun_session_respond(sess, req, PJ_STUN_SC_ALLOCATION_QUOTA_REACHED,
124 "Invalid bandwidth", PJ_TRUE,
125 src_addr, src_addr_len);
126 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_ALLOCATION_QUOTA_REACHED);
127 }
128
129 /* MUST have REQUESTED-TRANSPORT attribute */
130 attr_req_tp = (pj_stun_uint_attr*)
131 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_TRANSPORT, 0);
132 if (attr_req_tp == NULL) {
133 pj_stun_session_respond(sess, req, PJ_STUN_SC_BAD_REQUEST,
134 "Missing REQUESTED-TRANSPORT attribute",
135 PJ_TRUE, src_addr, src_addr_len);
136 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
137 }
138
139 cfg->tp_type = PJ_STUN_GET_RT_PROTO(attr_req_tp->value);
140
141 /* Can only support UDP for now */
142 if (cfg->tp_type != PJ_TURN_TP_UDP) {
143 pj_stun_session_respond(sess, req, PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO,
144 NULL, PJ_TRUE, src_addr, src_addr_len);
145 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO);
146 }
147
148 /* Get RESERVATION-TOKEN attribute, if any */
149 attr_res_token = (pj_stun_res_token_attr*)
150 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_RESERVATION_TOKEN,
151 0);
152 if (attr_res_token) {
153 /* We don't support RESERVATION-TOKEN for now */
154 pj_stun_session_respond(sess, req,
155 PJ_STUN_SC_BAD_REQUEST,
156 "RESERVATION-TOKEN is not supported", PJ_TRUE,
157 src_addr, src_addr_len);
158 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
159 }
160
161 /* Get REQUESTED-PROPS attribute, if any */
162 attr_rpp = (pj_stun_req_props_attr*)
163 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_PROPS, 0);
164 if (attr_rpp) {
165 /* We don't support REQUESTED-PROPS for now */
166 pj_stun_session_respond(sess, req,
167 PJ_STUN_SC_BAD_REQUEST,
168 "REQUESTED-PROPS is not supported", PJ_TRUE,
169 src_addr, src_addr_len);
170 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
171 }
172
173 /* Get LIFETIME attribute */
174 attr_lifetime = (pj_stun_uint_attr*)
175 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0);
176 if (attr_lifetime) {
177 cfg->lifetime = attr_lifetime->value;
178 if (cfg->lifetime < MIN_LIFETIME) {
179 pj_stun_session_respond(sess, req, PJ_STUN_SC_BAD_REQUEST,
180 "LIFETIME too short", PJ_TRUE,
181 src_addr, src_addr_len);
182 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
183 }
184 if (cfg->lifetime > MAX_LIFETIME)
185 cfg->lifetime = MAX_LIFETIME;
186 } else {
187 cfg->lifetime = DEF_LIFETIME;
188 }
189
190 return PJ_SUCCESS;
191}
192
193
194/* Respond to ALLOCATE request */
195static pj_status_t send_allocate_response(pj_turn_allocation *alloc,
196 pj_stun_session *srv_sess,
197 const pj_stun_msg *msg)
198{
199 pj_stun_tx_data *tdata;
200 pj_status_t status;
201
202 /* Respond the original ALLOCATE request */
203 status = pj_stun_session_create_res(srv_sess, msg, 0, NULL, &tdata);
204 if (status != PJ_SUCCESS)
205 return status;
206
207 /* Add RELAYED-ADDRESS attribute */
208 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
209 PJ_STUN_ATTR_RELAY_ADDR, PJ_TRUE,
210 &alloc->relay.hkey.addr,
211 pj_sockaddr_get_len(&alloc->relay.hkey.addr));
212
213 /* Add LIFETIME. */
214 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
215 PJ_STUN_ATTR_LIFETIME,
216 (unsigned)alloc->relay.lifetime);
217
218 /* Add BANDWIDTH */
219 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
220 PJ_STUN_ATTR_BANDWIDTH,
221 alloc->bandwidth);
222
223 /* Add RESERVATION-TOKEN */
224 PJ_TODO(ADD_RESERVATION_TOKEN);
225
226 /* Add XOR-MAPPED-ADDRESS */
227 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
228 PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE,
229 &alloc->hkey.clt_addr,
230 pj_sockaddr_get_len(&alloc->hkey.clt_addr));
231
232 /* Send the response */
233 return pj_stun_session_send_msg(srv_sess, PJ_TRUE,
234 &alloc->hkey.clt_addr,
235 pj_sockaddr_get_len(&alloc->hkey.clt_addr),
236 tdata);
237}
238
239
240/*
241 * Init credential for the allocation. We use static credential, meaning that
242 * the user's password must not change during allocation.
243 */
244static pj_status_t init_cred(pj_turn_allocation *alloc, const pj_stun_msg *req)
245{
246 const pj_stun_username_attr *user;
247 const pj_stun_realm_attr *realm;
248 const pj_stun_nonce_attr *nonce;
249 pj_status_t status;
250
251 realm = (const pj_stun_realm_attr*)
252 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REALM, 0);
253 PJ_ASSERT_RETURN(realm != NULL, PJ_EBUG);
254
255 user = (const pj_stun_username_attr*)
256 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0);
257 PJ_ASSERT_RETURN(user != NULL, PJ_EBUG);
258
259 nonce = (const pj_stun_nonce_attr*)
260 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_NONCE, 0);
261 PJ_ASSERT_RETURN(nonce != NULL, PJ_EBUG);
262
263 /* Lookup the password */
264 status = pj_turn_get_password(NULL, NULL, &realm->value,
265 &user->value, alloc->pool,
266 &alloc->cred.data.static_cred.data_type,
267 &alloc->cred.data.static_cred.data);
268 if (status != PJ_SUCCESS)
269 return status;
270
271 /* Save credential */
272 alloc->cred.type = PJ_STUN_AUTH_CRED_STATIC;
273 pj_strdup(alloc->pool, &alloc->cred.data.static_cred.realm, &realm->value);
274 pj_strdup(alloc->pool, &alloc->cred.data.static_cred.username, &user->value);
275 pj_strdup(alloc->pool, &alloc->cred.data.static_cred.nonce, &nonce->value);
276
277 return PJ_SUCCESS;
278}
279
280
Benny Prijonob05aafc2008-03-08 00:54:04 +0000281/*
282 * Create new allocation.
283 */
Benny Prijono708725a2008-03-09 12:55:00 +0000284PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener,
285 const pj_sockaddr_t *src_addr,
286 unsigned src_addr_len,
287 const pj_stun_msg *msg,
288 pj_stun_session *srv_sess,
289 pj_turn_allocation **p_alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000290{
Benny Prijono708725a2008-03-09 12:55:00 +0000291 pj_turn_srv *srv = listener->server;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000292 pj_pool_t *pool;
Benny Prijono708725a2008-03-09 12:55:00 +0000293 alloc_request req;
294 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000295 pj_stun_session_cb sess_cb;
Benny Prijono708725a2008-03-09 12:55:00 +0000296 char str_tmp[80];
Benny Prijonob05aafc2008-03-08 00:54:04 +0000297 pj_status_t status;
298
Benny Prijono708725a2008-03-09 12:55:00 +0000299 /* Parse ALLOCATE request */
300 status = parse_allocate_req(&req, srv_sess, msg, src_addr, src_addr_len);
301 if (status != PJ_SUCCESS)
302 return status;
303
Benny Prijonob05aafc2008-03-08 00:54:04 +0000304 pool = pj_pool_create(srv->core.pf, "alloc%p", 1000, 1000, NULL);
305
306 /* Init allocation structure */
Benny Prijono708725a2008-03-09 12:55:00 +0000307 alloc = PJ_POOL_ZALLOC_T(pool, pj_turn_allocation);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000308 alloc->pool = pool;
309 alloc->obj_name = pool->obj_name;
310 alloc->listener = listener;
311 alloc->clt_sock = PJ_INVALID_SOCKET;
312 alloc->relay.tp.sock = PJ_INVALID_SOCKET;
313
Benny Prijono708725a2008-03-09 12:55:00 +0000314 alloc->bandwidth = req.bandwidth;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000315
316 alloc->hkey.tp_type = listener->tp_type;
317 pj_memcpy(&alloc->hkey.clt_addr, src_addr, src_addr_len);
318
319 status = pj_lock_create_recursive_mutex(pool, alloc->obj_name,
320 &alloc->lock);
321 if (status != PJ_SUCCESS) {
Benny Prijono708725a2008-03-09 12:55:00 +0000322 goto on_error;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000323 }
324
325 /* Create peer hash table */
326 alloc->peer_table = pj_hash_create(pool, PEER_TABLE_SIZE);
327
328 /* Create channel hash table */
329 alloc->ch_table = pj_hash_create(pool, PEER_TABLE_SIZE);
330
331 /* Print info */
Benny Prijono708725a2008-03-09 12:55:00 +0000332 pj_ansi_strcpy(alloc->info, pj_turn_tp_type_name(listener->tp_type));
Benny Prijonob05aafc2008-03-08 00:54:04 +0000333 alloc->info[3] = ':';
334 pj_sockaddr_print(src_addr, alloc->info+4, sizeof(alloc->info)-4, 3);
335
336 /* Create STUN session to handle STUN communication with client */
337 pj_bzero(&sess_cb, sizeof(sess_cb));
338 sess_cb.on_send_msg = &stun_on_send_msg;
339 sess_cb.on_rx_request = &stun_on_rx_request;
340 sess_cb.on_rx_indication = &stun_on_rx_indication;
341 status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name,
342 &sess_cb, PJ_FALSE, &alloc->sess);
343 if (status != PJ_SUCCESS) {
Benny Prijono708725a2008-03-09 12:55:00 +0000344 goto on_error;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000345 }
346
347 /* Attach to STUN session */
348 pj_stun_session_set_user_data(alloc->sess, alloc);
349
Benny Prijono708725a2008-03-09 12:55:00 +0000350 /* Init authentication credential */
351 status = init_cred(alloc, msg);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000352 if (status != PJ_SUCCESS) {
Benny Prijono708725a2008-03-09 12:55:00 +0000353 goto on_error;
354 }
355
356 /* Attach authentication credential to STUN session */
357 pj_stun_session_set_credential(alloc->sess, &alloc->cred);
358
359 /* Create the relay resource */
360 status = create_relay(srv, alloc, msg, &req, &alloc->relay);
361 if (status != PJ_SUCCESS) {
362 goto on_error;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000363 }
364
365 /* Register this allocation */
Benny Prijono708725a2008-03-09 12:55:00 +0000366 pj_turn_srv_register_allocation(srv, alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000367
Benny Prijono708725a2008-03-09 12:55:00 +0000368 /* Respond to ALLOCATE request */
369 status = send_allocate_response(alloc, srv_sess, msg);
370 if (status != PJ_SUCCESS)
371 goto on_error;
372
373 /* Done */
374 pj_sockaddr_print(&alloc->relay.hkey.addr, str_tmp,
375 sizeof(str_tmp), 3);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000376 PJ_LOG(4,(alloc->obj_name, "Client %s created, relay addr=%s:%s",
Benny Prijono708725a2008-03-09 12:55:00 +0000377 alloc->info, pj_turn_tp_type_name(req.tp_type), str_tmp));
Benny Prijonob05aafc2008-03-08 00:54:04 +0000378
379 /* Success */
380 *p_alloc = alloc;
381 return PJ_SUCCESS;
Benny Prijono708725a2008-03-09 12:55:00 +0000382
383on_error:
384 /* Send reply to the ALLOCATE request */
385 pj_strerror(status, str_tmp, sizeof(str_tmp));
386 pj_stun_session_respond(srv_sess, msg, PJ_STUN_SC_BAD_REQUEST, str_tmp,
387 PJ_TRUE, src_addr, src_addr_len);
388
389 /* Cleanup */
390 destroy_allocation(alloc);
391 return status;
392}
393
394
395/* Destroy relay resource */
396static void destroy_relay(pj_turn_relay_res *relay)
397{
398 if (relay->timer.id) {
399 pj_timer_heap_cancel(relay->allocation->listener->server->core.timer_heap,
400 &relay->timer);
401 relay->timer.id = PJ_FALSE;
402 }
403
404 if (relay->tp.key) {
405 pj_ioqueue_unregister(relay->tp.key);
406 relay->tp.key = NULL;
407 relay->tp.sock = PJ_INVALID_SOCKET;
408 } else if (relay->tp.sock != PJ_INVALID_SOCKET) {
409 pj_sock_close(relay->tp.sock);
410 relay->tp.sock = PJ_INVALID_SOCKET;
411 }
412
413 /* Mark as shutdown */
414 relay->lifetime = 0;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000415}
416
417
418/*
Benny Prijono708725a2008-03-09 12:55:00 +0000419 * Really destroy allocation.
Benny Prijonob05aafc2008-03-08 00:54:04 +0000420 */
Benny Prijono708725a2008-03-09 12:55:00 +0000421static void destroy_allocation(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000422{
423 pj_pool_t *pool;
424
425 /* Unregister this allocation */
Benny Prijono708725a2008-03-09 12:55:00 +0000426 pj_turn_srv_unregister_allocation(alloc->listener->server, alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000427
428 /* Destroy relay */
429 destroy_relay(&alloc->relay);
430
431 /* Must lock only after destroying relay otherwise deadlock */
432 if (alloc->lock) {
433 pj_lock_acquire(alloc->lock);
434 }
435
436 /* Destroy STUN session */
437 if (alloc->sess) {
438 pj_stun_session_destroy(alloc->sess);
439 alloc->sess = NULL;
440 }
441
442 /* Destroy lock */
443 if (alloc->lock) {
444 pj_lock_release(alloc->lock);
445 pj_lock_destroy(alloc->lock);
446 alloc->lock = NULL;
447 }
448
449 /* Destroy pool */
450 pool = alloc->pool;
451 if (pool) {
452 alloc->pool = NULL;
453 pj_pool_release(pool);
454 }
455}
456
457
Benny Prijono708725a2008-03-09 12:55:00 +0000458PJ_DECL(void) pj_turn_allocation_destroy(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000459{
Benny Prijono708725a2008-03-09 12:55:00 +0000460 destroy_allocation(alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000461}
462
Benny Prijono708725a2008-03-09 12:55:00 +0000463
464/* Initiate shutdown sequence for this allocation and start destroy timer.
465 * Once allocation is marked as shutting down, any packets will be
466 * rejected/discarded
467 */
468static void alloc_shutdown(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000469{
470 pj_time_val destroy_delay = DESTROY_DELAY;
471
472 /* Work with existing schedule */
473 if (alloc->relay.timer.id == TIMER_ID_TIMEOUT) {
Benny Prijono708725a2008-03-09 12:55:00 +0000474 /* Cancel existing shutdown timer */
Benny Prijonob05aafc2008-03-08 00:54:04 +0000475 pj_timer_heap_cancel(alloc->listener->server->core.timer_heap,
476 &alloc->relay.timer);
477 alloc->relay.timer.id = TIMER_ID_NONE;
478
479 } else if (alloc->relay.timer.id == TIMER_ID_DESTROY) {
480 /* We've been scheduled to be destroyed, ignore this
481 * shutdown request.
482 */
483 return;
484 }
485
486 pj_assert(alloc->relay.timer.id == TIMER_ID_NONE);
487
488 /* Shutdown relay socket */
489 destroy_relay(&alloc->relay);
490
491 /* Don't unregister from hash table because we still need to
492 * handle REFRESH retransmission.
493 */
494
495 /* Schedule destroy timer */
496 alloc->relay.timer.id = TIMER_ID_DESTROY;
497 pj_timer_heap_schedule(alloc->listener->server->core.timer_heap,
498 &alloc->relay.timer, &destroy_delay);
499}
500
Benny Prijono708725a2008-03-09 12:55:00 +0000501
Benny Prijonob05aafc2008-03-08 00:54:04 +0000502/* Reschedule timeout using current lifetime setting */
Benny Prijono708725a2008-03-09 12:55:00 +0000503static pj_status_t resched_timeout(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000504{
505 pj_time_val delay;
506 pj_status_t status;
507
508 pj_gettimeofday(&alloc->relay.expiry);
509 alloc->relay.expiry.sec += alloc->relay.lifetime;
510
511 pj_assert(alloc->relay.timer.id != TIMER_ID_DESTROY);
512 if (alloc->relay.timer.id != 0) {
513 pj_timer_heap_cancel(alloc->listener->server->core.timer_heap,
514 &alloc->relay.timer);
515 alloc->relay.timer.id = TIMER_ID_NONE;
516 }
517
518 delay.sec = alloc->relay.lifetime;
519 delay.msec = 0;
520
521 alloc->relay.timer.id = TIMER_ID_TIMEOUT;
522 status = pj_timer_heap_schedule(alloc->listener->server->core.timer_heap,
523 &alloc->relay.timer, &delay);
524 if (status != PJ_SUCCESS) {
525 alloc->relay.timer.id = TIMER_ID_NONE;
526 return status;
527 }
528
529 return PJ_SUCCESS;
530}
531
532
533/* Timer timeout callback */
534static void relay_timeout_cb(pj_timer_heap_t *heap, pj_timer_entry *e)
535{
Benny Prijono708725a2008-03-09 12:55:00 +0000536 pj_turn_relay_res *rel;
537 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000538
Benny Prijono708725a2008-03-09 12:55:00 +0000539 PJ_UNUSED_ARG(heap);
540
541 rel = (pj_turn_relay_res*) e->user_data;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000542 alloc = rel->allocation;
543
544 if (e->id == TIMER_ID_TIMEOUT) {
545
546 e->id = TIMER_ID_NONE;
547
548 PJ_LOG(4,(alloc->obj_name,
549 "Client %s refresh timed-out, shutting down..",
550 alloc->info));
551
552 alloc_shutdown(alloc);
553
554 } else if (e->id == TIMER_ID_DESTROY) {
555 e->id = TIMER_ID_NONE;
556
557 PJ_LOG(4,(alloc->obj_name, "Client %s destroying..",
558 alloc->info));
559
Benny Prijono708725a2008-03-09 12:55:00 +0000560 destroy_allocation(alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000561 }
562}
563
564
565/*
566 * Create relay.
567 */
Benny Prijono708725a2008-03-09 12:55:00 +0000568static pj_status_t create_relay(pj_turn_srv *srv,
569 pj_turn_allocation *alloc,
570 const pj_stun_msg *msg,
571 const alloc_request *req,
572 pj_turn_relay_res *relay)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000573{
574 enum { RETRY = 40 };
575 pj_pool_t *pool = alloc->pool;
576 int retry, retry_max, sock_type;
577 pj_ioqueue_callback icb;
578 int af, namelen;
579 pj_stun_string_attr *sa;
580 pj_status_t status;
581
582 pj_bzero(relay, sizeof(*relay));
583
584 relay->allocation = alloc;
585 relay->tp.sock = PJ_INVALID_SOCKET;
586
587 /* TODO: get the requested address family from somewhere */
588 af = alloc->listener->addr.addr.sa_family;
589
590 /* Save realm */
591 sa = (pj_stun_string_attr*)
592 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0);
593 PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP);
594 pj_strdup(pool, &relay->realm, &sa->value);
595
596 /* Save username */
597 sa = (pj_stun_string_attr*)
598 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0);
599 PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP);
600 pj_strdup(pool, &relay->user, &sa->value);
601
602 /* Lifetime and timeout */
603 relay->lifetime = req->lifetime;
604 pj_timer_entry_init(&relay->timer, TIMER_ID_NONE, relay,
605 &relay_timeout_cb);
606 resched_timeout(alloc);
607
608 /* Transport type */
609 relay->hkey.tp_type = req->tp_type;
610
611 /* Create the socket */
Benny Prijono708725a2008-03-09 12:55:00 +0000612 if (req->tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000613 sock_type = pj_SOCK_DGRAM();
Benny Prijono708725a2008-03-09 12:55:00 +0000614 } else if (req->tp_type == PJ_TURN_TP_TCP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000615 sock_type = pj_SOCK_STREAM();
616 } else {
617 pj_assert(!"Unknown transport");
618 return PJ_EINVALIDOP;
619 }
620
621 status = pj_sock_socket(af, sock_type, 0, &relay->tp.sock);
622 if (status != PJ_SUCCESS) {
623 pj_bzero(relay, sizeof(*relay));
624 return status;
625 }
626
627 /* Find suitable port for this allocation */
628 if (req->rpp_port) {
629 retry_max = 1;
630 } else {
631 retry_max = RETRY;
632 }
633
634 for (retry=0; retry<retry_max; ++retry) {
635 pj_uint16_t port;
636 pj_sockaddr bound_addr;
637
638 pj_lock_acquire(srv->core.lock);
639
640 if (req->rpp_port) {
641 port = (pj_uint16_t) req->rpp_port;
Benny Prijono708725a2008-03-09 12:55:00 +0000642 } else if (req->tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000643 port = (pj_uint16_t) srv->ports.next_udp++;
644 if (srv->ports.next_udp > srv->ports.max_udp)
645 srv->ports.next_udp = srv->ports.min_udp;
Benny Prijono708725a2008-03-09 12:55:00 +0000646 } else if (req->tp_type == PJ_TURN_TP_TCP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000647 port = (pj_uint16_t) srv->ports.next_tcp++;
648 if (srv->ports.next_tcp > srv->ports.max_tcp)
649 srv->ports.next_tcp = srv->ports.min_tcp;
650 } else {
651 pj_assert(!"Invalid transport");
Benny Prijono708725a2008-03-09 12:55:00 +0000652 port = 0;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000653 }
654
655 pj_lock_release(srv->core.lock);
656
657 pj_sockaddr_init(af, &bound_addr, NULL, port);
658
659 status = pj_sock_bind(relay->tp.sock, &bound_addr,
660 pj_sockaddr_get_len(&bound_addr));
661 if (status == PJ_SUCCESS)
662 break;
663 }
664
665 if (status != PJ_SUCCESS) {
666 /* Unable to allocate port */
667 PJ_LOG(4,(THIS_FILE, "bind() failed: err %d",
668 status));
669 pj_sock_close(relay->tp.sock);
670 relay->tp.sock = PJ_INVALID_SOCKET;
671 return status;
672 }
673
674 /* Init relay key */
675 namelen = sizeof(relay->hkey.addr);
676 status = pj_sock_getsockname(relay->tp.sock, &relay->hkey.addr, &namelen);
677 if (status != PJ_SUCCESS) {
678 PJ_LOG(4,(THIS_FILE, "pj_sock_getsockname() failed: err %d",
679 status));
680 pj_sock_close(relay->tp.sock);
681 relay->tp.sock = PJ_INVALID_SOCKET;
682 return status;
683 }
684 if (!pj_sockaddr_has_addr(&relay->hkey.addr)) {
685 pj_sockaddr_copy_addr(&relay->hkey.addr, &alloc->listener->addr);
686 }
687
688 /* Init ioqueue */
689 pj_bzero(&icb, sizeof(icb));
690 icb.on_read_complete = &on_rx_from_peer;
691
692 status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, relay->tp.sock,
693 relay, &icb, &relay->tp.key);
694 if (status != PJ_SUCCESS) {
695 PJ_LOG(4,(THIS_FILE, "pj_ioqueue_register_sock() failed: err %d",
696 status));
697 pj_sock_close(relay->tp.sock);
698 relay->tp.sock = PJ_INVALID_SOCKET;
699 return status;
700 }
701
702 /* Kick off pending read operation */
703 pj_ioqueue_op_key_init(&relay->tp.read_key, sizeof(relay->tp.read_key));
704 on_rx_from_peer(relay->tp.key, &relay->tp.read_key, 0);
705
706 /* Done */
707 return PJ_SUCCESS;
708}
709
710/* Create and send error response */
Benny Prijono708725a2008-03-09 12:55:00 +0000711static void send_reply_err(pj_turn_allocation *alloc,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000712 const pj_stun_msg *req,
713 pj_bool_t cache,
714 int code, const char *errmsg)
715{
716 pj_status_t status;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000717
Benny Prijono708725a2008-03-09 12:55:00 +0000718 status = pj_stun_session_respond(alloc->sess, req, code, errmsg, cache,
719 &alloc->hkey.clt_addr,
720 pj_sockaddr_get_len(&alloc->hkey.clt_addr.addr));
Benny Prijonob05aafc2008-03-08 00:54:04 +0000721 if (status != PJ_SUCCESS) {
722 alloc_err(alloc, "Error sending STUN error response", status);
723 return;
724 }
725}
726
727/* Create and send successful response */
Benny Prijono708725a2008-03-09 12:55:00 +0000728static void send_reply_ok(pj_turn_allocation *alloc,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000729 const pj_stun_msg *req)
730{
731 pj_status_t status;
732 unsigned interval;
733 pj_stun_tx_data *tdata;
734
735 status = pj_stun_session_create_res(alloc->sess, req, 0, NULL, &tdata);
736 if (status != PJ_SUCCESS) {
737 alloc_err(alloc, "Error creating STUN success response", status);
738 return;
739 }
740
741 /* Calculate time to expiration */
742 if (alloc->relay.lifetime != 0) {
743 pj_time_val now;
744 pj_gettimeofday(&now);
745 interval = alloc->relay.expiry.sec - now.sec;
746 } else {
747 interval = 0;
748 }
749
750 /* Add LIFETIME. */
751 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
752 PJ_STUN_ATTR_LIFETIME, interval);
753
754 /* Add BANDWIDTH */
755 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
756 PJ_STUN_ATTR_BANDWIDTH,
757 alloc->bandwidth);
758
759 status = pj_stun_session_send_msg(alloc->sess, PJ_TRUE,
760 &alloc->hkey.clt_addr,
761 pj_sockaddr_get_len(&alloc->hkey.clt_addr),
762 tdata);
763 if (status != PJ_SUCCESS) {
764 alloc_err(alloc, "Error sending STUN success response", status);
765 return;
766 }
767}
768
769
770/* Create new permission */
Benny Prijono708725a2008-03-09 12:55:00 +0000771static pj_turn_permission *create_permission(pj_turn_allocation *alloc,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000772 const pj_sockaddr_t *peer_addr,
773 unsigned addr_len)
774{
Benny Prijono708725a2008-03-09 12:55:00 +0000775 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000776
Benny Prijono708725a2008-03-09 12:55:00 +0000777 perm = PJ_POOL_ZALLOC_T(alloc->pool, pj_turn_permission);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000778 pj_memcpy(&perm->hkey.peer_addr, peer_addr, addr_len);
779
Benny Prijono708725a2008-03-09 12:55:00 +0000780 if (alloc->listener->tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000781 perm->sock = alloc->listener->sock;
782 } else {
783 pj_assert(!"TCP is not supported yet");
784 return NULL;
785 }
786
787 perm->allocation = alloc;
Benny Prijono708725a2008-03-09 12:55:00 +0000788 perm->channel = PJ_TURN_INVALID_CHANNEL;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000789
790 pj_gettimeofday(&perm->expiry);
Benny Prijono708725a2008-03-09 12:55:00 +0000791 perm->expiry.sec += PJ_TURN_PERM_TIMEOUT;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000792
793 return perm;
794}
795
796/* Check if a permission isn't expired. Return NULL if expired. */
Benny Prijono708725a2008-03-09 12:55:00 +0000797static pj_turn_permission *check_permission_expiry(pj_turn_permission *perm)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000798{
Benny Prijono708725a2008-03-09 12:55:00 +0000799 pj_turn_allocation *alloc = perm->allocation;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000800 pj_time_val now;
801
802 pj_gettimeofday(&now);
803 if (PJ_TIME_VAL_LT(perm->expiry, now)) {
804 /* Permission has not expired */
805 return perm;
806 }
807
808 /* Remove from permission hash table */
809 pj_hash_set(NULL, alloc->peer_table, &perm->hkey, sizeof(perm->hkey),
810 0, NULL);
811
812 /* Remove from channel hash table, if assigned a channel number */
Benny Prijono708725a2008-03-09 12:55:00 +0000813 if (perm->channel != PJ_TURN_INVALID_CHANNEL) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000814 pj_hash_set(NULL, alloc->ch_table, &perm->channel,
815 sizeof(perm->channel), 0, NULL);
816 }
817
818 return NULL;
819}
820
821/* Lookup permission in hash table by the peer address */
Benny Prijono708725a2008-03-09 12:55:00 +0000822static pj_turn_permission*
823lookup_permission_by_addr(pj_turn_allocation *alloc,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000824 const pj_sockaddr_t *peer_addr,
825 unsigned addr_len)
826{
Benny Prijono708725a2008-03-09 12:55:00 +0000827 pj_turn_permission_key key;
828 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000829
830 pj_bzero(&key, sizeof(key));
831 pj_memcpy(&key, peer_addr, addr_len);
832
833 /* Lookup in peer hash table */
Benny Prijono708725a2008-03-09 12:55:00 +0000834 perm = (pj_turn_permission*) pj_hash_get(alloc->peer_table, &key,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000835 sizeof(key), NULL);
836 return check_permission_expiry(perm);
837}
838
839/* Lookup permission in hash table by the channel number */
Benny Prijono708725a2008-03-09 12:55:00 +0000840static pj_turn_permission*
841lookup_permission_by_chnum(pj_turn_allocation *alloc,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000842 unsigned chnum)
843{
844 pj_uint16_t chnum16 = (pj_uint16_t)chnum;
Benny Prijono708725a2008-03-09 12:55:00 +0000845 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000846
847 /* Lookup in peer hash table */
Benny Prijono708725a2008-03-09 12:55:00 +0000848 perm = (pj_turn_permission*) pj_hash_get(alloc->peer_table, &chnum16,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000849 sizeof(chnum16), NULL);
850 return check_permission_expiry(perm);
851}
852
853/* Update permission because of data from client to peer.
854 * Return PJ_TRUE is permission is found.
855 */
Benny Prijono708725a2008-03-09 12:55:00 +0000856static pj_bool_t refresh_permission(pj_turn_permission *perm)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000857{
858 pj_gettimeofday(&perm->expiry);
Benny Prijono708725a2008-03-09 12:55:00 +0000859 if (perm->channel == PJ_TURN_INVALID_CHANNEL)
860 perm->expiry.sec += PJ_TURN_PERM_TIMEOUT;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000861 else
Benny Prijono708725a2008-03-09 12:55:00 +0000862 perm->expiry.sec += PJ_TURN_CHANNEL_TIMEOUT;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000863 return PJ_TRUE;
864}
865
866/*
Benny Prijono708725a2008-03-09 12:55:00 +0000867 * Handle incoming packet from client. This would have been called by
868 * server upon receiving packet from a listener.
Benny Prijonob05aafc2008-03-08 00:54:04 +0000869 */
Benny Prijono708725a2008-03-09 12:55:00 +0000870PJ_DEF(void) pj_turn_allocation_on_rx_client_pkt(pj_turn_allocation *alloc,
871 pj_turn_pkt *pkt)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000872{
873 pj_bool_t is_stun;
874 pj_status_t status;
875
Benny Prijono708725a2008-03-09 12:55:00 +0000876 /* Lock this allocation */
877 pj_lock_acquire(alloc->lock);
878
Benny Prijonob05aafc2008-03-08 00:54:04 +0000879 /* Quickly check if this is STUN message */
880 is_stun = ((*((pj_uint8_t*)pkt->pkt) & 0xC0) == 0);
881
882 if (is_stun) {
883 /*
884 * This could be an incoming STUN requests or indications.
885 * Pass this through to the STUN session, which will call
886 * our stun_on_rx_request() or stun_on_rx_indication()
887 * callbacks.
888 */
889 unsigned options = PJ_STUN_CHECK_PACKET;
Benny Prijono708725a2008-03-09 12:55:00 +0000890 if (pkt->listener->tp_type == PJ_TURN_TP_UDP)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000891 options |= PJ_STUN_IS_DATAGRAM;
892
893 status = pj_stun_session_on_rx_pkt(alloc->sess, pkt->pkt, pkt->len,
894 options, NULL,
895 &pkt->src.clt_addr,
896 pkt->src_addr_len);
897 if (status != PJ_SUCCESS) {
898 alloc_err(alloc, "Error handling STUN packet", status);
Benny Prijono708725a2008-03-09 12:55:00 +0000899 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000900 }
901
902 } else {
903 /*
904 * This is not a STUN packet, must be ChannelData packet.
905 */
Benny Prijono3fd9fc52008-03-09 23:52:48 +0000906 pj_turn_channel_data *cd = (pj_turn_channel_data*)pkt->pkt;
Benny Prijono708725a2008-03-09 12:55:00 +0000907 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000908 pj_ssize_t len;
909
Benny Prijono3fd9fc52008-03-09 23:52:48 +0000910 pj_assert(sizeof(*cd)==4);
911
Benny Prijonob05aafc2008-03-08 00:54:04 +0000912 /* For UDP check the packet length */
Benny Prijono708725a2008-03-09 12:55:00 +0000913 if (alloc->listener->tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000914 if (pkt->len < pj_ntohs(cd->length)+sizeof(*cd)) {
915 PJ_LOG(4,(alloc->obj_name,
916 "ChannelData from %s discarded: UDP size error",
917 alloc->info));
Benny Prijono708725a2008-03-09 12:55:00 +0000918 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000919 }
920 } else {
921 pj_assert(!"Unsupported transport");
Benny Prijono708725a2008-03-09 12:55:00 +0000922 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000923 }
924
925 perm = lookup_permission_by_chnum(alloc, pj_ntohs(cd->ch_number));
926 if (!perm) {
927 /* Discard */
928 PJ_LOG(4,(alloc->obj_name,
929 "ChannelData from %s discarded: not found",
930 alloc->info));
Benny Prijono708725a2008-03-09 12:55:00 +0000931 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000932 }
933
934 /* Relay the data */
935 len = pj_ntohs(cd->length);
936 pj_sock_sendto(alloc->relay.tp.sock, cd+1, &len, 0,
937 &perm->hkey.peer_addr,
938 pj_sockaddr_get_len(&perm->hkey.peer_addr));
939
940 /* Refresh permission */
941 refresh_permission(perm);
942 }
Benny Prijono708725a2008-03-09 12:55:00 +0000943
944on_return:
945 /* Release lock */
946 pj_lock_release(alloc->lock);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000947}
948
Benny Prijono708725a2008-03-09 12:55:00 +0000949
Benny Prijonob05aafc2008-03-08 00:54:04 +0000950/*
951 * Handle incoming packet from peer. This function is called by
952 * on_rx_from_peer().
953 */
Benny Prijono708725a2008-03-09 12:55:00 +0000954static void handle_peer_pkt(pj_turn_allocation *alloc,
955 pj_turn_relay_res *rel,
956 char *pkt, pj_size_t len,
957 const pj_sockaddr *src_addr)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000958{
Benny Prijono708725a2008-03-09 12:55:00 +0000959 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000960
961 /* Lookup permission */
962 perm = lookup_permission_by_addr(alloc, src_addr,
963 pj_sockaddr_get_len(src_addr));
964 if (perm == NULL) {
965 /* No permission, discard data */
966 return;
967 }
968
969 /* Send Data Indication or ChannelData, depends on whether
970 * this permission is attached to a channel number.
971 */
Benny Prijono708725a2008-03-09 12:55:00 +0000972 if (perm->channel != PJ_TURN_INVALID_CHANNEL) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000973 /* Send ChannelData */
Benny Prijono3fd9fc52008-03-09 23:52:48 +0000974 pj_turn_channel_data *cd = (pj_turn_channel_data*)rel->tp.tx_pkt;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000975
Benny Prijono708725a2008-03-09 12:55:00 +0000976 if (len > PJ_TURN_MAX_PKT_LEN) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000977 char peer_addr[80];
978 pj_sockaddr_print(src_addr, peer_addr, sizeof(peer_addr), 3);
Benny Prijono708725a2008-03-09 12:55:00 +0000979 PJ_LOG(4,(alloc->obj_name, "Client %s: discarded data from %s "
Benny Prijonob05aafc2008-03-08 00:54:04 +0000980 "because it's too long (%d bytes)",
981 alloc->info, peer_addr, len));
982 return;
983 }
984
985 /* Init header */
986 cd->ch_number = pj_htons(perm->channel);
987 cd->length = pj_htons((pj_uint16_t)len);
988
989 /* Copy data */
Benny Prijono3fd9fc52008-03-09 23:52:48 +0000990 pj_memcpy(rel->tp.rx_pkt+sizeof(pj_turn_channel_data), pkt, len);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000991
992 /* Send to client */
Benny Prijono708725a2008-03-09 12:55:00 +0000993 pj_turn_listener_sendto(alloc->listener, rel->tp.tx_pkt,
Benny Prijono3fd9fc52008-03-09 23:52:48 +0000994 len+sizeof(pj_turn_channel_data), 0,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000995 &alloc->hkey.clt_addr,
996 pj_sockaddr_get_len(&alloc->hkey.clt_addr));
997 } else {
998 /* Send Data Indication */
999 pj_stun_tx_data *tdata;
1000 pj_status_t status;
1001
1002 status = pj_stun_session_create_ind(alloc->sess,
1003 PJ_STUN_DATA_INDICATION, &tdata);
1004 if (status != PJ_SUCCESS) {
1005 alloc_err(alloc, "Error creating Data indication", status);
1006 return;
1007 }
1008 }
1009}
1010
1011/*
1012 * ioqueue notification on RX packets from the relay socket.
1013 */
1014static void on_rx_from_peer(pj_ioqueue_key_t *key,
1015 pj_ioqueue_op_key_t *op_key,
1016 pj_ssize_t bytes_read)
1017{
Benny Prijono708725a2008-03-09 12:55:00 +00001018 pj_turn_relay_res *rel;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001019 pj_status_t status;
1020
Benny Prijono708725a2008-03-09 12:55:00 +00001021 rel = (pj_turn_relay_res*) pj_ioqueue_get_user_data(key);
1022
1023 /* Lock the allocation */
1024 pj_lock_acquire(rel->allocation->lock);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001025
1026 do {
1027 if (bytes_read > 0) {
Benny Prijono708725a2008-03-09 12:55:00 +00001028 handle_peer_pkt(rel->allocation, rel, rel->tp.rx_pkt,
1029 bytes_read, &rel->tp.src_addr);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001030 }
1031
1032 /* Read next packet */
1033 bytes_read = sizeof(rel->tp.rx_pkt);
1034 rel->tp.src_addr_len = sizeof(rel->tp.src_addr);
1035 status = pj_ioqueue_recvfrom(key, op_key,
1036 rel->tp.rx_pkt, &bytes_read, 0,
1037 &rel->tp.src_addr,
1038 &rel->tp.src_addr_len);
1039
1040 if (status != PJ_EPENDING && status != PJ_SUCCESS)
1041 bytes_read = -status;
1042
1043 } while (status != PJ_EPENDING && status != PJ_ECANCELLED);
1044
Benny Prijono708725a2008-03-09 12:55:00 +00001045 /* Release allocation lock */
1046 pj_lock_release(rel->allocation->lock);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001047}
1048
1049/*
1050 * Callback notification from STUN session when it wants to send
1051 * a STUN message towards the client.
1052 */
1053static pj_status_t stun_on_send_msg(pj_stun_session *sess,
1054 const void *pkt,
1055 pj_size_t pkt_size,
1056 const pj_sockaddr_t *dst_addr,
1057 unsigned addr_len)
1058{
Benny Prijono708725a2008-03-09 12:55:00 +00001059 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001060
Benny Prijono708725a2008-03-09 12:55:00 +00001061 alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001062
Benny Prijono708725a2008-03-09 12:55:00 +00001063 return pj_turn_listener_sendto(alloc->listener, pkt, pkt_size, 0,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001064 dst_addr, addr_len);
1065}
1066
1067/*
1068 * Callback notification from STUN session when it receives STUN
1069 * requests. This callback was trigger by STUN incoming message
Benny Prijono708725a2008-03-09 12:55:00 +00001070 * processing in pj_turn_allocation_on_rx_client_pkt().
Benny Prijonob05aafc2008-03-08 00:54:04 +00001071 */
1072static pj_status_t stun_on_rx_request(pj_stun_session *sess,
1073 const pj_uint8_t *pkt,
1074 unsigned pkt_len,
1075 const pj_stun_msg *msg,
1076 const pj_sockaddr_t *src_addr,
1077 unsigned src_addr_len)
1078{
Benny Prijono708725a2008-03-09 12:55:00 +00001079 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001080
Benny Prijono708725a2008-03-09 12:55:00 +00001081 PJ_UNUSED_ARG(pkt);
1082 PJ_UNUSED_ARG(pkt_len);
1083 PJ_UNUSED_ARG(src_addr);
1084 PJ_UNUSED_ARG(src_addr_len);
1085
1086 alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001087
1088 /* Refuse to serve any request if we've been shutdown */
1089 if (alloc->relay.lifetime == 0) {
Benny Prijono708725a2008-03-09 12:55:00 +00001090 /* Reject with 437 if we're shutting down */
Benny Prijonob05aafc2008-03-08 00:54:04 +00001091 send_reply_err(alloc, msg, PJ_TRUE,
1092 PJ_STUN_SC_ALLOCATION_MISMATCH, NULL);
1093 return PJ_SUCCESS;
1094 }
1095
1096 if (msg->hdr.type == PJ_STUN_REFRESH_REQUEST) {
1097 /*
1098 * Handle REFRESH request
1099 */
1100 pj_stun_lifetime_attr *lifetime;
1101 pj_stun_bandwidth_attr *bandwidth;
1102
1103 /* Get LIFETIME attribute */
1104 lifetime = (pj_stun_lifetime_attr*)
1105 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0);
1106
1107 /* Get BANDWIDTH attribute */
1108 bandwidth = (pj_stun_bandwidth_attr*)
1109 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_BANDWIDTH, 0);
1110
1111 if (lifetime && lifetime->value==0) {
1112 /*
1113 * This is deallocation request.
1114 */
1115 alloc->relay.lifetime = 0;
1116
1117 /* Respond first */
1118 send_reply_ok(alloc, msg);
1119
1120 /* Shutdown allocation */
1121 PJ_LOG(4,(alloc->obj_name,
1122 "Client %s request to dealloc, shutting down",
1123 alloc->info));
1124
1125 alloc_shutdown(alloc);
1126
1127 } else {
1128 /*
1129 * This is a refresh request.
1130 */
1131
1132 /* Update lifetime */
1133 if (lifetime) {
1134 alloc->relay.lifetime = lifetime->value;
1135 }
1136
1137 /* Update bandwidth */
1138 // TODO:
1139
1140 /* Update expiration timer */
1141 resched_timeout(alloc);
1142
1143 /* Send reply */
1144 send_reply_ok(alloc, msg);
1145 }
1146
1147 } else if (msg->hdr.type == PJ_STUN_CHANNEL_BIND_REQUEST) {
1148 /*
1149 * ChannelBind request.
1150 */
1151 pj_stun_channel_number_attr *ch_attr;
1152 pj_stun_peer_addr_attr *peer_attr;
Benny Prijono708725a2008-03-09 12:55:00 +00001153 pj_turn_permission *p1, *p2;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001154
1155 ch_attr = (pj_stun_channel_number_attr*)
1156 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CHANNEL_NUMBER, 0);
1157 peer_attr = (pj_stun_peer_addr_attr*)
1158 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
1159
1160 if (!ch_attr || !peer_attr) {
1161 send_reply_err(alloc, msg, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL);
1162 return PJ_SUCCESS;
1163 }
1164
1165 /* Find permission with the channel number */
1166 p1 = lookup_permission_by_chnum(alloc, PJ_STUN_GET_CH_NB(ch_attr->value));
1167
1168 /* If permission is found, this is supposed to be a channel bind
1169 * refresh. Make sure it's for the same peer.
1170 */
1171 if (p1) {
1172 if (pj_sockaddr_cmp(&p1->hkey.peer_addr, &peer_attr->sockaddr)) {
1173 /* Address mismatch. Send 400 */
1174 send_reply_err(alloc, msg, PJ_TRUE,
1175 PJ_STUN_SC_BAD_REQUEST,
1176 "Peer address mismatch");
1177 return PJ_SUCCESS;
1178 }
1179
1180 /* Refresh permission */
1181 refresh_permission(p1);
1182
1183 /* Done */
1184 return PJ_SUCCESS;
1185 }
1186
1187 /* If permission is not found, create a new one. Make sure the peer
1188 * has not alreadyy assigned with a channel number.
1189 */
1190 p2 = lookup_permission_by_addr(alloc, &peer_attr->sockaddr,
1191 pj_sockaddr_get_len(&peer_attr->sockaddr));
Benny Prijono708725a2008-03-09 12:55:00 +00001192 if (p2 && p2->channel != PJ_TURN_INVALID_CHANNEL) {
Benny Prijonob05aafc2008-03-08 00:54:04 +00001193 send_reply_err(alloc, msg, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST,
1194 "Peer address already assigned a channel number");
1195 return PJ_SUCCESS;
1196 }
1197
1198 /* Create permission if it doesn't exist */
1199 if (!p2) {
1200 p2 = create_permission(alloc, &peer_attr->sockaddr,
1201 pj_sockaddr_get_len(&peer_attr->sockaddr));
1202 if (!p2)
1203 return PJ_SUCCESS;
1204 }
1205
1206 /* Assign channel number to permission */
1207 p2->channel = PJ_STUN_GET_CH_NB(ch_attr->value);
1208
1209 /* Update */
1210 refresh_permission(p2);
1211
1212 /* Reply */
1213 send_reply_ok(alloc, msg);
1214
1215 return PJ_SUCCESS;
1216
1217 } else if (msg->hdr.type == PJ_STUN_ALLOCATE_REQUEST) {
1218
1219 /* Respond with 437 (section 6.3 turn-07) */
1220 send_reply_err(alloc, msg, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH, NULL);
1221
1222 } else {
1223
1224 /* Respond with Bad Request? */
1225 send_reply_err(alloc, msg, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL);
1226
1227 }
1228
1229 return PJ_SUCCESS;
1230}
1231
1232/*
1233 * Callback notification from STUN session when it receives STUN
1234 * indications. This callback was trigger by STUN incoming message
Benny Prijono708725a2008-03-09 12:55:00 +00001235 * processing in pj_turn_allocation_on_rx_client_pkt().
Benny Prijonob05aafc2008-03-08 00:54:04 +00001236 */
1237static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
1238 const pj_uint8_t *pkt,
1239 unsigned pkt_len,
1240 const pj_stun_msg *msg,
1241 const pj_sockaddr_t *src_addr,
1242 unsigned src_addr_len)
1243{
1244 pj_stun_peer_addr_attr *peer_attr;
1245 pj_stun_data_attr *data_attr;
Benny Prijono708725a2008-03-09 12:55:00 +00001246 pj_turn_allocation *alloc;
1247 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001248
Benny Prijono708725a2008-03-09 12:55:00 +00001249 PJ_UNUSED_ARG(pkt);
1250 PJ_UNUSED_ARG(pkt_len);
1251 PJ_UNUSED_ARG(src_addr);
1252 PJ_UNUSED_ARG(src_addr_len);
1253
1254 alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001255
1256 /* Only expect Send Indication */
1257 if (msg->hdr.type != PJ_STUN_SEND_INDICATION) {
1258 /* Ignore */
1259 return PJ_SUCCESS;
1260 }
1261
1262 /* Get PEER-ADDRESS attribute */
1263 peer_attr = (pj_stun_peer_addr_attr*)
1264 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
1265
1266 /* MUST have PEER-ADDRESS attribute */
1267 if (!peer_attr)
1268 return PJ_SUCCESS;
1269
1270 /* Get DATA attribute */
1271 data_attr = (pj_stun_data_attr*)
1272 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_DATA, 0);
1273
1274 /* Create/update/refresh the permission */
1275 perm = lookup_permission_by_addr(alloc, &peer_attr->sockaddr,
1276 pj_sockaddr_get_len(&peer_attr->sockaddr));
1277 if (perm == NULL) {
1278 perm = create_permission(alloc, &peer_attr->sockaddr,
1279 pj_sockaddr_get_len(&peer_attr->sockaddr));
1280 }
1281 refresh_permission(perm);
1282
1283 /* Return if we don't have data */
1284 if (data_attr == NULL)
1285 return PJ_SUCCESS;
1286
1287 /* Relay the data to client */
Benny Prijono708725a2008-03-09 12:55:00 +00001288 if (alloc->hkey.tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +00001289 pj_ssize_t len = data_attr->length;
1290 pj_sock_sendto(alloc->listener->sock, data_attr->data,
1291 &len, 0, &peer_attr->sockaddr,
1292 pj_sockaddr_get_len(&peer_attr->sockaddr));
1293 } else {
1294 pj_assert(!"TCP is not supported");
1295 }
1296
1297 return PJ_SUCCESS;
1298}
1299
1300