blob: 14ed228b529007a1d75e6cd498ef7fb8b7a4676b [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,
Benny Prijonoa5d214f2008-03-19 23:00:30 +000075 const pj_stun_rx_data *rdata,
Benny Prijonob05aafc2008-03-08 00:54:04 +000076 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,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000100 const pj_stun_rx_data *rdata,
Benny Prijono708725a2008-03-09 12:55:00 +0000101 const pj_sockaddr_t *src_addr,
102 unsigned src_addr_len)
103{
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000104 const pj_stun_msg *req = rdata->msg;
Benny Prijono708725a2008-03-09 12:55:00 +0000105 pj_stun_bandwidth_attr *attr_bw;
106 pj_stun_req_transport_attr *attr_req_tp;
107 pj_stun_res_token_attr *attr_res_token;
108 pj_stun_req_props_attr *attr_rpp;
109 pj_stun_lifetime_attr *attr_lifetime;
110
111 pj_bzero(cfg, sizeof(*cfg));
112
113 /* Get BANDWIDTH attribute, if any. */
114 attr_bw = (pj_stun_uint_attr*)
115 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_BANDWIDTH, 0);
116 if (attr_bw) {
117 cfg->bandwidth = attr_bw->value;
118 } else {
119 cfg->bandwidth = DEFA_CLIENT_BANDWIDTH;
120 }
121
122 /* Check if we can satisfy the bandwidth */
123 if (cfg->bandwidth > MAX_CLIENT_BANDWIDTH) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000124 pj_stun_session_respond(sess, rdata,
125 PJ_STUN_SC_ALLOCATION_QUOTA_REACHED,
Benny Prijono708725a2008-03-09 12:55:00 +0000126 "Invalid bandwidth", PJ_TRUE,
127 src_addr, src_addr_len);
128 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_ALLOCATION_QUOTA_REACHED);
129 }
130
131 /* MUST have REQUESTED-TRANSPORT attribute */
132 attr_req_tp = (pj_stun_uint_attr*)
133 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_TRANSPORT, 0);
134 if (attr_req_tp == NULL) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000135 pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST,
Benny Prijono708725a2008-03-09 12:55:00 +0000136 "Missing REQUESTED-TRANSPORT attribute",
137 PJ_TRUE, src_addr, src_addr_len);
138 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
139 }
140
141 cfg->tp_type = PJ_STUN_GET_RT_PROTO(attr_req_tp->value);
142
143 /* Can only support UDP for now */
144 if (cfg->tp_type != PJ_TURN_TP_UDP) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000145 pj_stun_session_respond(sess, rdata, PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO,
Benny Prijono708725a2008-03-09 12:55:00 +0000146 NULL, PJ_TRUE, src_addr, src_addr_len);
147 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO);
148 }
149
150 /* Get RESERVATION-TOKEN attribute, if any */
151 attr_res_token = (pj_stun_res_token_attr*)
152 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_RESERVATION_TOKEN,
153 0);
154 if (attr_res_token) {
155 /* We don't support RESERVATION-TOKEN for now */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000156 pj_stun_session_respond(sess, rdata,
Benny Prijono708725a2008-03-09 12:55:00 +0000157 PJ_STUN_SC_BAD_REQUEST,
158 "RESERVATION-TOKEN is not supported", PJ_TRUE,
159 src_addr, src_addr_len);
160 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
161 }
162
163 /* Get REQUESTED-PROPS attribute, if any */
164 attr_rpp = (pj_stun_req_props_attr*)
165 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_PROPS, 0);
166 if (attr_rpp) {
167 /* We don't support REQUESTED-PROPS for now */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000168 pj_stun_session_respond(sess, rdata,
Benny Prijono708725a2008-03-09 12:55:00 +0000169 PJ_STUN_SC_BAD_REQUEST,
170 "REQUESTED-PROPS is not supported", PJ_TRUE,
171 src_addr, src_addr_len);
172 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
173 }
174
175 /* Get LIFETIME attribute */
176 attr_lifetime = (pj_stun_uint_attr*)
177 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0);
178 if (attr_lifetime) {
179 cfg->lifetime = attr_lifetime->value;
180 if (cfg->lifetime < MIN_LIFETIME) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000181 pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST,
Benny Prijono708725a2008-03-09 12:55:00 +0000182 "LIFETIME too short", PJ_TRUE,
183 src_addr, src_addr_len);
184 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
185 }
186 if (cfg->lifetime > MAX_LIFETIME)
187 cfg->lifetime = MAX_LIFETIME;
188 } else {
189 cfg->lifetime = DEF_LIFETIME;
190 }
191
192 return PJ_SUCCESS;
193}
194
195
196/* Respond to ALLOCATE request */
197static pj_status_t send_allocate_response(pj_turn_allocation *alloc,
198 pj_stun_session *srv_sess,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000199 const pj_stun_rx_data *rdata)
Benny Prijono708725a2008-03-09 12:55:00 +0000200{
201 pj_stun_tx_data *tdata;
202 pj_status_t status;
203
204 /* Respond the original ALLOCATE request */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000205 status = pj_stun_session_create_res(srv_sess, rdata, 0, NULL, &tdata);
Benny Prijono708725a2008-03-09 12:55:00 +0000206 if (status != PJ_SUCCESS)
207 return status;
208
209 /* Add RELAYED-ADDRESS attribute */
210 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
211 PJ_STUN_ATTR_RELAY_ADDR, PJ_TRUE,
212 &alloc->relay.hkey.addr,
213 pj_sockaddr_get_len(&alloc->relay.hkey.addr));
214
215 /* Add LIFETIME. */
216 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
217 PJ_STUN_ATTR_LIFETIME,
218 (unsigned)alloc->relay.lifetime);
219
220 /* Add BANDWIDTH */
221 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
222 PJ_STUN_ATTR_BANDWIDTH,
223 alloc->bandwidth);
224
225 /* Add RESERVATION-TOKEN */
226 PJ_TODO(ADD_RESERVATION_TOKEN);
227
228 /* Add XOR-MAPPED-ADDRESS */
229 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
230 PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE,
231 &alloc->hkey.clt_addr,
232 pj_sockaddr_get_len(&alloc->hkey.clt_addr));
233
234 /* Send the response */
235 return pj_stun_session_send_msg(srv_sess, PJ_TRUE,
236 &alloc->hkey.clt_addr,
237 pj_sockaddr_get_len(&alloc->hkey.clt_addr),
238 tdata);
239}
240
241
242/*
243 * Init credential for the allocation. We use static credential, meaning that
244 * the user's password must not change during allocation.
245 */
246static pj_status_t init_cred(pj_turn_allocation *alloc, const pj_stun_msg *req)
247{
248 const pj_stun_username_attr *user;
249 const pj_stun_realm_attr *realm;
250 const pj_stun_nonce_attr *nonce;
251 pj_status_t status;
252
253 realm = (const pj_stun_realm_attr*)
254 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REALM, 0);
255 PJ_ASSERT_RETURN(realm != NULL, PJ_EBUG);
256
257 user = (const pj_stun_username_attr*)
258 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0);
259 PJ_ASSERT_RETURN(user != NULL, PJ_EBUG);
260
261 nonce = (const pj_stun_nonce_attr*)
262 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_NONCE, 0);
263 PJ_ASSERT_RETURN(nonce != NULL, PJ_EBUG);
264
265 /* Lookup the password */
266 status = pj_turn_get_password(NULL, NULL, &realm->value,
267 &user->value, alloc->pool,
268 &alloc->cred.data.static_cred.data_type,
269 &alloc->cred.data.static_cred.data);
270 if (status != PJ_SUCCESS)
271 return status;
272
273 /* Save credential */
274 alloc->cred.type = PJ_STUN_AUTH_CRED_STATIC;
275 pj_strdup(alloc->pool, &alloc->cred.data.static_cred.realm, &realm->value);
276 pj_strdup(alloc->pool, &alloc->cred.data.static_cred.username, &user->value);
277 pj_strdup(alloc->pool, &alloc->cred.data.static_cred.nonce, &nonce->value);
278
279 return PJ_SUCCESS;
280}
281
282
Benny Prijonob05aafc2008-03-08 00:54:04 +0000283/*
284 * Create new allocation.
285 */
Benny Prijono708725a2008-03-09 12:55:00 +0000286PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener,
287 const pj_sockaddr_t *src_addr,
288 unsigned src_addr_len,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000289 const pj_stun_rx_data *rdata,
Benny Prijono708725a2008-03-09 12:55:00 +0000290 pj_stun_session *srv_sess,
291 pj_turn_allocation **p_alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000292{
Benny Prijono708725a2008-03-09 12:55:00 +0000293 pj_turn_srv *srv = listener->server;
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000294 const pj_stun_msg *msg = rdata->msg;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000295 pj_pool_t *pool;
Benny Prijono708725a2008-03-09 12:55:00 +0000296 alloc_request req;
297 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000298 pj_stun_session_cb sess_cb;
Benny Prijono708725a2008-03-09 12:55:00 +0000299 char str_tmp[80];
Benny Prijonob05aafc2008-03-08 00:54:04 +0000300 pj_status_t status;
301
Benny Prijono708725a2008-03-09 12:55:00 +0000302 /* Parse ALLOCATE request */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000303 status = parse_allocate_req(&req, srv_sess, rdata, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000304 if (status != PJ_SUCCESS)
305 return status;
306
Benny Prijonob05aafc2008-03-08 00:54:04 +0000307 pool = pj_pool_create(srv->core.pf, "alloc%p", 1000, 1000, NULL);
308
309 /* Init allocation structure */
Benny Prijono708725a2008-03-09 12:55:00 +0000310 alloc = PJ_POOL_ZALLOC_T(pool, pj_turn_allocation);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000311 alloc->pool = pool;
312 alloc->obj_name = pool->obj_name;
313 alloc->listener = listener;
314 alloc->clt_sock = PJ_INVALID_SOCKET;
315 alloc->relay.tp.sock = PJ_INVALID_SOCKET;
316
Benny Prijono708725a2008-03-09 12:55:00 +0000317 alloc->bandwidth = req.bandwidth;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000318
319 alloc->hkey.tp_type = listener->tp_type;
320 pj_memcpy(&alloc->hkey.clt_addr, src_addr, src_addr_len);
321
322 status = pj_lock_create_recursive_mutex(pool, alloc->obj_name,
323 &alloc->lock);
324 if (status != PJ_SUCCESS) {
Benny Prijono708725a2008-03-09 12:55:00 +0000325 goto on_error;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000326 }
327
328 /* Create peer hash table */
329 alloc->peer_table = pj_hash_create(pool, PEER_TABLE_SIZE);
330
331 /* Create channel hash table */
332 alloc->ch_table = pj_hash_create(pool, PEER_TABLE_SIZE);
333
334 /* Print info */
Benny Prijono708725a2008-03-09 12:55:00 +0000335 pj_ansi_strcpy(alloc->info, pj_turn_tp_type_name(listener->tp_type));
Benny Prijonob05aafc2008-03-08 00:54:04 +0000336 alloc->info[3] = ':';
337 pj_sockaddr_print(src_addr, alloc->info+4, sizeof(alloc->info)-4, 3);
338
339 /* Create STUN session to handle STUN communication with client */
340 pj_bzero(&sess_cb, sizeof(sess_cb));
341 sess_cb.on_send_msg = &stun_on_send_msg;
342 sess_cb.on_rx_request = &stun_on_rx_request;
343 sess_cb.on_rx_indication = &stun_on_rx_indication;
344 status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name,
345 &sess_cb, PJ_FALSE, &alloc->sess);
346 if (status != PJ_SUCCESS) {
Benny Prijono708725a2008-03-09 12:55:00 +0000347 goto on_error;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000348 }
349
350 /* Attach to STUN session */
351 pj_stun_session_set_user_data(alloc->sess, alloc);
352
Benny Prijono708725a2008-03-09 12:55:00 +0000353 /* Init authentication credential */
354 status = init_cred(alloc, msg);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000355 if (status != PJ_SUCCESS) {
Benny Prijono708725a2008-03-09 12:55:00 +0000356 goto on_error;
357 }
358
359 /* Attach authentication credential to STUN session */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000360 pj_stun_session_set_credential(alloc->sess, PJ_STUN_AUTH_LONG_TERM,
361 &alloc->cred);
Benny Prijono708725a2008-03-09 12:55:00 +0000362
363 /* Create the relay resource */
364 status = create_relay(srv, alloc, msg, &req, &alloc->relay);
365 if (status != PJ_SUCCESS) {
366 goto on_error;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000367 }
368
369 /* Register this allocation */
Benny Prijono708725a2008-03-09 12:55:00 +0000370 pj_turn_srv_register_allocation(srv, alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000371
Benny Prijono708725a2008-03-09 12:55:00 +0000372 /* Respond to ALLOCATE request */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000373 status = send_allocate_response(alloc, srv_sess, rdata);
Benny Prijono708725a2008-03-09 12:55:00 +0000374 if (status != PJ_SUCCESS)
375 goto on_error;
376
377 /* Done */
378 pj_sockaddr_print(&alloc->relay.hkey.addr, str_tmp,
379 sizeof(str_tmp), 3);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000380 PJ_LOG(4,(alloc->obj_name, "Client %s created, relay addr=%s:%s",
Benny Prijono708725a2008-03-09 12:55:00 +0000381 alloc->info, pj_turn_tp_type_name(req.tp_type), str_tmp));
Benny Prijonob05aafc2008-03-08 00:54:04 +0000382
383 /* Success */
384 *p_alloc = alloc;
385 return PJ_SUCCESS;
Benny Prijono708725a2008-03-09 12:55:00 +0000386
387on_error:
388 /* Send reply to the ALLOCATE request */
389 pj_strerror(status, str_tmp, sizeof(str_tmp));
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000390 pj_stun_session_respond(srv_sess, rdata, PJ_STUN_SC_BAD_REQUEST, str_tmp,
Benny Prijono708725a2008-03-09 12:55:00 +0000391 PJ_TRUE, src_addr, src_addr_len);
392
393 /* Cleanup */
394 destroy_allocation(alloc);
395 return status;
396}
397
398
399/* Destroy relay resource */
400static void destroy_relay(pj_turn_relay_res *relay)
401{
402 if (relay->timer.id) {
403 pj_timer_heap_cancel(relay->allocation->listener->server->core.timer_heap,
404 &relay->timer);
405 relay->timer.id = PJ_FALSE;
406 }
407
408 if (relay->tp.key) {
409 pj_ioqueue_unregister(relay->tp.key);
410 relay->tp.key = NULL;
411 relay->tp.sock = PJ_INVALID_SOCKET;
412 } else if (relay->tp.sock != PJ_INVALID_SOCKET) {
413 pj_sock_close(relay->tp.sock);
414 relay->tp.sock = PJ_INVALID_SOCKET;
415 }
416
417 /* Mark as shutdown */
418 relay->lifetime = 0;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000419}
420
421
422/*
Benny Prijono708725a2008-03-09 12:55:00 +0000423 * Really destroy allocation.
Benny Prijonob05aafc2008-03-08 00:54:04 +0000424 */
Benny Prijono708725a2008-03-09 12:55:00 +0000425static void destroy_allocation(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000426{
427 pj_pool_t *pool;
428
429 /* Unregister this allocation */
Benny Prijono708725a2008-03-09 12:55:00 +0000430 pj_turn_srv_unregister_allocation(alloc->listener->server, alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000431
432 /* Destroy relay */
433 destroy_relay(&alloc->relay);
434
435 /* Must lock only after destroying relay otherwise deadlock */
436 if (alloc->lock) {
437 pj_lock_acquire(alloc->lock);
438 }
439
440 /* Destroy STUN session */
441 if (alloc->sess) {
442 pj_stun_session_destroy(alloc->sess);
443 alloc->sess = NULL;
444 }
445
446 /* Destroy lock */
447 if (alloc->lock) {
448 pj_lock_release(alloc->lock);
449 pj_lock_destroy(alloc->lock);
450 alloc->lock = NULL;
451 }
452
453 /* Destroy pool */
454 pool = alloc->pool;
455 if (pool) {
456 alloc->pool = NULL;
457 pj_pool_release(pool);
458 }
459}
460
461
Benny Prijono708725a2008-03-09 12:55:00 +0000462PJ_DECL(void) pj_turn_allocation_destroy(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000463{
Benny Prijono708725a2008-03-09 12:55:00 +0000464 destroy_allocation(alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000465}
466
Benny Prijono708725a2008-03-09 12:55:00 +0000467
468/* Initiate shutdown sequence for this allocation and start destroy timer.
469 * Once allocation is marked as shutting down, any packets will be
470 * rejected/discarded
471 */
472static void alloc_shutdown(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000473{
474 pj_time_val destroy_delay = DESTROY_DELAY;
475
476 /* Work with existing schedule */
477 if (alloc->relay.timer.id == TIMER_ID_TIMEOUT) {
Benny Prijono708725a2008-03-09 12:55:00 +0000478 /* Cancel existing shutdown timer */
Benny Prijonob05aafc2008-03-08 00:54:04 +0000479 pj_timer_heap_cancel(alloc->listener->server->core.timer_heap,
480 &alloc->relay.timer);
481 alloc->relay.timer.id = TIMER_ID_NONE;
482
483 } else if (alloc->relay.timer.id == TIMER_ID_DESTROY) {
484 /* We've been scheduled to be destroyed, ignore this
485 * shutdown request.
486 */
487 return;
488 }
489
490 pj_assert(alloc->relay.timer.id == TIMER_ID_NONE);
491
492 /* Shutdown relay socket */
493 destroy_relay(&alloc->relay);
494
495 /* Don't unregister from hash table because we still need to
496 * handle REFRESH retransmission.
497 */
498
499 /* Schedule destroy timer */
500 alloc->relay.timer.id = TIMER_ID_DESTROY;
501 pj_timer_heap_schedule(alloc->listener->server->core.timer_heap,
502 &alloc->relay.timer, &destroy_delay);
503}
504
Benny Prijono708725a2008-03-09 12:55:00 +0000505
Benny Prijonob05aafc2008-03-08 00:54:04 +0000506/* Reschedule timeout using current lifetime setting */
Benny Prijono708725a2008-03-09 12:55:00 +0000507static pj_status_t resched_timeout(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000508{
509 pj_time_val delay;
510 pj_status_t status;
511
512 pj_gettimeofday(&alloc->relay.expiry);
513 alloc->relay.expiry.sec += alloc->relay.lifetime;
514
515 pj_assert(alloc->relay.timer.id != TIMER_ID_DESTROY);
516 if (alloc->relay.timer.id != 0) {
517 pj_timer_heap_cancel(alloc->listener->server->core.timer_heap,
518 &alloc->relay.timer);
519 alloc->relay.timer.id = TIMER_ID_NONE;
520 }
521
522 delay.sec = alloc->relay.lifetime;
523 delay.msec = 0;
524
525 alloc->relay.timer.id = TIMER_ID_TIMEOUT;
526 status = pj_timer_heap_schedule(alloc->listener->server->core.timer_heap,
527 &alloc->relay.timer, &delay);
528 if (status != PJ_SUCCESS) {
529 alloc->relay.timer.id = TIMER_ID_NONE;
530 return status;
531 }
532
533 return PJ_SUCCESS;
534}
535
536
537/* Timer timeout callback */
538static void relay_timeout_cb(pj_timer_heap_t *heap, pj_timer_entry *e)
539{
Benny Prijono708725a2008-03-09 12:55:00 +0000540 pj_turn_relay_res *rel;
541 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000542
Benny Prijono708725a2008-03-09 12:55:00 +0000543 PJ_UNUSED_ARG(heap);
544
545 rel = (pj_turn_relay_res*) e->user_data;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000546 alloc = rel->allocation;
547
548 if (e->id == TIMER_ID_TIMEOUT) {
549
550 e->id = TIMER_ID_NONE;
551
552 PJ_LOG(4,(alloc->obj_name,
553 "Client %s refresh timed-out, shutting down..",
554 alloc->info));
555
556 alloc_shutdown(alloc);
557
558 } else if (e->id == TIMER_ID_DESTROY) {
559 e->id = TIMER_ID_NONE;
560
561 PJ_LOG(4,(alloc->obj_name, "Client %s destroying..",
562 alloc->info));
563
Benny Prijono708725a2008-03-09 12:55:00 +0000564 destroy_allocation(alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000565 }
566}
567
568
569/*
570 * Create relay.
571 */
Benny Prijono708725a2008-03-09 12:55:00 +0000572static pj_status_t create_relay(pj_turn_srv *srv,
573 pj_turn_allocation *alloc,
574 const pj_stun_msg *msg,
575 const alloc_request *req,
576 pj_turn_relay_res *relay)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000577{
578 enum { RETRY = 40 };
579 pj_pool_t *pool = alloc->pool;
580 int retry, retry_max, sock_type;
581 pj_ioqueue_callback icb;
582 int af, namelen;
583 pj_stun_string_attr *sa;
584 pj_status_t status;
585
586 pj_bzero(relay, sizeof(*relay));
587
588 relay->allocation = alloc;
589 relay->tp.sock = PJ_INVALID_SOCKET;
590
591 /* TODO: get the requested address family from somewhere */
592 af = alloc->listener->addr.addr.sa_family;
593
594 /* Save realm */
595 sa = (pj_stun_string_attr*)
596 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0);
597 PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP);
598 pj_strdup(pool, &relay->realm, &sa->value);
599
600 /* Save username */
601 sa = (pj_stun_string_attr*)
602 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0);
603 PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP);
604 pj_strdup(pool, &relay->user, &sa->value);
605
606 /* Lifetime and timeout */
607 relay->lifetime = req->lifetime;
608 pj_timer_entry_init(&relay->timer, TIMER_ID_NONE, relay,
609 &relay_timeout_cb);
610 resched_timeout(alloc);
611
612 /* Transport type */
613 relay->hkey.tp_type = req->tp_type;
614
615 /* Create the socket */
Benny Prijono708725a2008-03-09 12:55:00 +0000616 if (req->tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000617 sock_type = pj_SOCK_DGRAM();
Benny Prijono708725a2008-03-09 12:55:00 +0000618 } else if (req->tp_type == PJ_TURN_TP_TCP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000619 sock_type = pj_SOCK_STREAM();
620 } else {
621 pj_assert(!"Unknown transport");
622 return PJ_EINVALIDOP;
623 }
624
625 status = pj_sock_socket(af, sock_type, 0, &relay->tp.sock);
626 if (status != PJ_SUCCESS) {
627 pj_bzero(relay, sizeof(*relay));
628 return status;
629 }
630
631 /* Find suitable port for this allocation */
632 if (req->rpp_port) {
633 retry_max = 1;
634 } else {
635 retry_max = RETRY;
636 }
637
638 for (retry=0; retry<retry_max; ++retry) {
639 pj_uint16_t port;
640 pj_sockaddr bound_addr;
641
642 pj_lock_acquire(srv->core.lock);
643
644 if (req->rpp_port) {
645 port = (pj_uint16_t) req->rpp_port;
Benny Prijono708725a2008-03-09 12:55:00 +0000646 } else if (req->tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000647 port = (pj_uint16_t) srv->ports.next_udp++;
648 if (srv->ports.next_udp > srv->ports.max_udp)
649 srv->ports.next_udp = srv->ports.min_udp;
Benny Prijono708725a2008-03-09 12:55:00 +0000650 } else if (req->tp_type == PJ_TURN_TP_TCP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000651 port = (pj_uint16_t) srv->ports.next_tcp++;
652 if (srv->ports.next_tcp > srv->ports.max_tcp)
653 srv->ports.next_tcp = srv->ports.min_tcp;
654 } else {
655 pj_assert(!"Invalid transport");
Benny Prijono708725a2008-03-09 12:55:00 +0000656 port = 0;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000657 }
658
659 pj_lock_release(srv->core.lock);
660
661 pj_sockaddr_init(af, &bound_addr, NULL, port);
662
663 status = pj_sock_bind(relay->tp.sock, &bound_addr,
664 pj_sockaddr_get_len(&bound_addr));
665 if (status == PJ_SUCCESS)
666 break;
667 }
668
669 if (status != PJ_SUCCESS) {
670 /* Unable to allocate port */
671 PJ_LOG(4,(THIS_FILE, "bind() failed: err %d",
672 status));
673 pj_sock_close(relay->tp.sock);
674 relay->tp.sock = PJ_INVALID_SOCKET;
675 return status;
676 }
677
678 /* Init relay key */
679 namelen = sizeof(relay->hkey.addr);
680 status = pj_sock_getsockname(relay->tp.sock, &relay->hkey.addr, &namelen);
681 if (status != PJ_SUCCESS) {
682 PJ_LOG(4,(THIS_FILE, "pj_sock_getsockname() failed: err %d",
683 status));
684 pj_sock_close(relay->tp.sock);
685 relay->tp.sock = PJ_INVALID_SOCKET;
686 return status;
687 }
688 if (!pj_sockaddr_has_addr(&relay->hkey.addr)) {
689 pj_sockaddr_copy_addr(&relay->hkey.addr, &alloc->listener->addr);
690 }
691
692 /* Init ioqueue */
693 pj_bzero(&icb, sizeof(icb));
694 icb.on_read_complete = &on_rx_from_peer;
695
696 status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, relay->tp.sock,
697 relay, &icb, &relay->tp.key);
698 if (status != PJ_SUCCESS) {
699 PJ_LOG(4,(THIS_FILE, "pj_ioqueue_register_sock() failed: err %d",
700 status));
701 pj_sock_close(relay->tp.sock);
702 relay->tp.sock = PJ_INVALID_SOCKET;
703 return status;
704 }
705
706 /* Kick off pending read operation */
707 pj_ioqueue_op_key_init(&relay->tp.read_key, sizeof(relay->tp.read_key));
708 on_rx_from_peer(relay->tp.key, &relay->tp.read_key, 0);
709
710 /* Done */
711 return PJ_SUCCESS;
712}
713
714/* Create and send error response */
Benny Prijono708725a2008-03-09 12:55:00 +0000715static void send_reply_err(pj_turn_allocation *alloc,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000716 const pj_stun_rx_data *rdata,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000717 pj_bool_t cache,
718 int code, const char *errmsg)
719{
720 pj_status_t status;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000721
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000722 status = pj_stun_session_respond(alloc->sess, rdata, code, errmsg, cache,
Benny Prijono708725a2008-03-09 12:55:00 +0000723 &alloc->hkey.clt_addr,
724 pj_sockaddr_get_len(&alloc->hkey.clt_addr.addr));
Benny Prijonob05aafc2008-03-08 00:54:04 +0000725 if (status != PJ_SUCCESS) {
726 alloc_err(alloc, "Error sending STUN error response", status);
727 return;
728 }
729}
730
731/* Create and send successful response */
Benny Prijono708725a2008-03-09 12:55:00 +0000732static void send_reply_ok(pj_turn_allocation *alloc,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000733 const pj_stun_rx_data *rdata)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000734{
735 pj_status_t status;
736 unsigned interval;
737 pj_stun_tx_data *tdata;
738
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000739 status = pj_stun_session_create_res(alloc->sess, rdata, 0, NULL, &tdata);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000740 if (status != PJ_SUCCESS) {
741 alloc_err(alloc, "Error creating STUN success response", status);
742 return;
743 }
744
745 /* Calculate time to expiration */
746 if (alloc->relay.lifetime != 0) {
747 pj_time_val now;
748 pj_gettimeofday(&now);
749 interval = alloc->relay.expiry.sec - now.sec;
750 } else {
751 interval = 0;
752 }
753
754 /* Add LIFETIME. */
755 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
756 PJ_STUN_ATTR_LIFETIME, interval);
757
758 /* Add BANDWIDTH */
759 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
760 PJ_STUN_ATTR_BANDWIDTH,
761 alloc->bandwidth);
762
763 status = pj_stun_session_send_msg(alloc->sess, PJ_TRUE,
764 &alloc->hkey.clt_addr,
765 pj_sockaddr_get_len(&alloc->hkey.clt_addr),
766 tdata);
767 if (status != PJ_SUCCESS) {
768 alloc_err(alloc, "Error sending STUN success response", status);
769 return;
770 }
771}
772
773
774/* Create new permission */
Benny Prijono708725a2008-03-09 12:55:00 +0000775static pj_turn_permission *create_permission(pj_turn_allocation *alloc,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000776 const pj_sockaddr_t *peer_addr,
777 unsigned addr_len)
778{
Benny Prijono708725a2008-03-09 12:55:00 +0000779 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000780
Benny Prijono708725a2008-03-09 12:55:00 +0000781 perm = PJ_POOL_ZALLOC_T(alloc->pool, pj_turn_permission);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000782 pj_memcpy(&perm->hkey.peer_addr, peer_addr, addr_len);
783
Benny Prijono708725a2008-03-09 12:55:00 +0000784 if (alloc->listener->tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000785 perm->sock = alloc->listener->sock;
786 } else {
787 pj_assert(!"TCP is not supported yet");
788 return NULL;
789 }
790
791 perm->allocation = alloc;
Benny Prijono708725a2008-03-09 12:55:00 +0000792 perm->channel = PJ_TURN_INVALID_CHANNEL;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000793
794 pj_gettimeofday(&perm->expiry);
Benny Prijono708725a2008-03-09 12:55:00 +0000795 perm->expiry.sec += PJ_TURN_PERM_TIMEOUT;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000796
797 return perm;
798}
799
800/* Check if a permission isn't expired. Return NULL if expired. */
Benny Prijono708725a2008-03-09 12:55:00 +0000801static pj_turn_permission *check_permission_expiry(pj_turn_permission *perm)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000802{
Benny Prijono708725a2008-03-09 12:55:00 +0000803 pj_turn_allocation *alloc = perm->allocation;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000804 pj_time_val now;
805
806 pj_gettimeofday(&now);
807 if (PJ_TIME_VAL_LT(perm->expiry, now)) {
808 /* Permission has not expired */
809 return perm;
810 }
811
812 /* Remove from permission hash table */
813 pj_hash_set(NULL, alloc->peer_table, &perm->hkey, sizeof(perm->hkey),
814 0, NULL);
815
816 /* Remove from channel hash table, if assigned a channel number */
Benny Prijono708725a2008-03-09 12:55:00 +0000817 if (perm->channel != PJ_TURN_INVALID_CHANNEL) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000818 pj_hash_set(NULL, alloc->ch_table, &perm->channel,
819 sizeof(perm->channel), 0, NULL);
820 }
821
822 return NULL;
823}
824
825/* Lookup permission in hash table by the peer address */
Benny Prijono708725a2008-03-09 12:55:00 +0000826static pj_turn_permission*
827lookup_permission_by_addr(pj_turn_allocation *alloc,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000828 const pj_sockaddr_t *peer_addr,
829 unsigned addr_len)
830{
Benny Prijono708725a2008-03-09 12:55:00 +0000831 pj_turn_permission_key key;
832 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000833
834 pj_bzero(&key, sizeof(key));
835 pj_memcpy(&key, peer_addr, addr_len);
836
837 /* Lookup in peer hash table */
Benny Prijono708725a2008-03-09 12:55:00 +0000838 perm = (pj_turn_permission*) pj_hash_get(alloc->peer_table, &key,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000839 sizeof(key), NULL);
840 return check_permission_expiry(perm);
841}
842
843/* Lookup permission in hash table by the channel number */
Benny Prijono708725a2008-03-09 12:55:00 +0000844static pj_turn_permission*
845lookup_permission_by_chnum(pj_turn_allocation *alloc,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000846 unsigned chnum)
847{
848 pj_uint16_t chnum16 = (pj_uint16_t)chnum;
Benny Prijono708725a2008-03-09 12:55:00 +0000849 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000850
851 /* Lookup in peer hash table */
Benny Prijono708725a2008-03-09 12:55:00 +0000852 perm = (pj_turn_permission*) pj_hash_get(alloc->peer_table, &chnum16,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000853 sizeof(chnum16), NULL);
854 return check_permission_expiry(perm);
855}
856
857/* Update permission because of data from client to peer.
858 * Return PJ_TRUE is permission is found.
859 */
Benny Prijono708725a2008-03-09 12:55:00 +0000860static pj_bool_t refresh_permission(pj_turn_permission *perm)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000861{
862 pj_gettimeofday(&perm->expiry);
Benny Prijono708725a2008-03-09 12:55:00 +0000863 if (perm->channel == PJ_TURN_INVALID_CHANNEL)
864 perm->expiry.sec += PJ_TURN_PERM_TIMEOUT;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000865 else
Benny Prijono708725a2008-03-09 12:55:00 +0000866 perm->expiry.sec += PJ_TURN_CHANNEL_TIMEOUT;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000867 return PJ_TRUE;
868}
869
870/*
Benny Prijono708725a2008-03-09 12:55:00 +0000871 * Handle incoming packet from client. This would have been called by
872 * server upon receiving packet from a listener.
Benny Prijonob05aafc2008-03-08 00:54:04 +0000873 */
Benny Prijono708725a2008-03-09 12:55:00 +0000874PJ_DEF(void) pj_turn_allocation_on_rx_client_pkt(pj_turn_allocation *alloc,
875 pj_turn_pkt *pkt)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000876{
877 pj_bool_t is_stun;
878 pj_status_t status;
879
Benny Prijono708725a2008-03-09 12:55:00 +0000880 /* Lock this allocation */
881 pj_lock_acquire(alloc->lock);
882
Benny Prijonob05aafc2008-03-08 00:54:04 +0000883 /* Quickly check if this is STUN message */
884 is_stun = ((*((pj_uint8_t*)pkt->pkt) & 0xC0) == 0);
885
886 if (is_stun) {
887 /*
888 * This could be an incoming STUN requests or indications.
889 * Pass this through to the STUN session, which will call
890 * our stun_on_rx_request() or stun_on_rx_indication()
891 * callbacks.
892 */
893 unsigned options = PJ_STUN_CHECK_PACKET;
Benny Prijono708725a2008-03-09 12:55:00 +0000894 if (pkt->listener->tp_type == PJ_TURN_TP_UDP)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000895 options |= PJ_STUN_IS_DATAGRAM;
896
897 status = pj_stun_session_on_rx_pkt(alloc->sess, pkt->pkt, pkt->len,
898 options, NULL,
899 &pkt->src.clt_addr,
900 pkt->src_addr_len);
901 if (status != PJ_SUCCESS) {
902 alloc_err(alloc, "Error handling STUN packet", status);
Benny Prijono708725a2008-03-09 12:55:00 +0000903 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000904 }
905
906 } else {
907 /*
908 * This is not a STUN packet, must be ChannelData packet.
909 */
Benny Prijono3fd9fc52008-03-09 23:52:48 +0000910 pj_turn_channel_data *cd = (pj_turn_channel_data*)pkt->pkt;
Benny Prijono708725a2008-03-09 12:55:00 +0000911 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000912 pj_ssize_t len;
913
Benny Prijono3fd9fc52008-03-09 23:52:48 +0000914 pj_assert(sizeof(*cd)==4);
915
Benny Prijonob05aafc2008-03-08 00:54:04 +0000916 /* For UDP check the packet length */
Benny Prijono708725a2008-03-09 12:55:00 +0000917 if (alloc->listener->tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000918 if (pkt->len < pj_ntohs(cd->length)+sizeof(*cd)) {
919 PJ_LOG(4,(alloc->obj_name,
920 "ChannelData from %s discarded: UDP size error",
921 alloc->info));
Benny Prijono708725a2008-03-09 12:55:00 +0000922 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000923 }
924 } else {
925 pj_assert(!"Unsupported transport");
Benny Prijono708725a2008-03-09 12:55:00 +0000926 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000927 }
928
929 perm = lookup_permission_by_chnum(alloc, pj_ntohs(cd->ch_number));
930 if (!perm) {
931 /* Discard */
932 PJ_LOG(4,(alloc->obj_name,
933 "ChannelData from %s discarded: not found",
934 alloc->info));
Benny Prijono708725a2008-03-09 12:55:00 +0000935 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000936 }
937
938 /* Relay the data */
939 len = pj_ntohs(cd->length);
940 pj_sock_sendto(alloc->relay.tp.sock, cd+1, &len, 0,
941 &perm->hkey.peer_addr,
942 pj_sockaddr_get_len(&perm->hkey.peer_addr));
943
944 /* Refresh permission */
945 refresh_permission(perm);
946 }
Benny Prijono708725a2008-03-09 12:55:00 +0000947
948on_return:
949 /* Release lock */
950 pj_lock_release(alloc->lock);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000951}
952
Benny Prijono708725a2008-03-09 12:55:00 +0000953
Benny Prijonob05aafc2008-03-08 00:54:04 +0000954/*
955 * Handle incoming packet from peer. This function is called by
956 * on_rx_from_peer().
957 */
Benny Prijono708725a2008-03-09 12:55:00 +0000958static void handle_peer_pkt(pj_turn_allocation *alloc,
959 pj_turn_relay_res *rel,
960 char *pkt, pj_size_t len,
961 const pj_sockaddr *src_addr)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000962{
Benny Prijono708725a2008-03-09 12:55:00 +0000963 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000964
965 /* Lookup permission */
966 perm = lookup_permission_by_addr(alloc, src_addr,
967 pj_sockaddr_get_len(src_addr));
968 if (perm == NULL) {
969 /* No permission, discard data */
970 return;
971 }
972
973 /* Send Data Indication or ChannelData, depends on whether
974 * this permission is attached to a channel number.
975 */
Benny Prijono708725a2008-03-09 12:55:00 +0000976 if (perm->channel != PJ_TURN_INVALID_CHANNEL) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000977 /* Send ChannelData */
Benny Prijono3fd9fc52008-03-09 23:52:48 +0000978 pj_turn_channel_data *cd = (pj_turn_channel_data*)rel->tp.tx_pkt;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000979
Benny Prijono708725a2008-03-09 12:55:00 +0000980 if (len > PJ_TURN_MAX_PKT_LEN) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000981 char peer_addr[80];
982 pj_sockaddr_print(src_addr, peer_addr, sizeof(peer_addr), 3);
Benny Prijono708725a2008-03-09 12:55:00 +0000983 PJ_LOG(4,(alloc->obj_name, "Client %s: discarded data from %s "
Benny Prijonob05aafc2008-03-08 00:54:04 +0000984 "because it's too long (%d bytes)",
985 alloc->info, peer_addr, len));
986 return;
987 }
988
989 /* Init header */
990 cd->ch_number = pj_htons(perm->channel);
991 cd->length = pj_htons((pj_uint16_t)len);
992
993 /* Copy data */
Benny Prijono3fd9fc52008-03-09 23:52:48 +0000994 pj_memcpy(rel->tp.rx_pkt+sizeof(pj_turn_channel_data), pkt, len);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000995
996 /* Send to client */
Benny Prijono708725a2008-03-09 12:55:00 +0000997 pj_turn_listener_sendto(alloc->listener, rel->tp.tx_pkt,
Benny Prijono3fd9fc52008-03-09 23:52:48 +0000998 len+sizeof(pj_turn_channel_data), 0,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000999 &alloc->hkey.clt_addr,
1000 pj_sockaddr_get_len(&alloc->hkey.clt_addr));
1001 } else {
1002 /* Send Data Indication */
1003 pj_stun_tx_data *tdata;
1004 pj_status_t status;
1005
1006 status = pj_stun_session_create_ind(alloc->sess,
1007 PJ_STUN_DATA_INDICATION, &tdata);
1008 if (status != PJ_SUCCESS) {
1009 alloc_err(alloc, "Error creating Data indication", status);
1010 return;
1011 }
1012 }
1013}
1014
1015/*
1016 * ioqueue notification on RX packets from the relay socket.
1017 */
1018static void on_rx_from_peer(pj_ioqueue_key_t *key,
1019 pj_ioqueue_op_key_t *op_key,
1020 pj_ssize_t bytes_read)
1021{
Benny Prijono708725a2008-03-09 12:55:00 +00001022 pj_turn_relay_res *rel;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001023 pj_status_t status;
1024
Benny Prijono708725a2008-03-09 12:55:00 +00001025 rel = (pj_turn_relay_res*) pj_ioqueue_get_user_data(key);
1026
1027 /* Lock the allocation */
1028 pj_lock_acquire(rel->allocation->lock);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001029
1030 do {
1031 if (bytes_read > 0) {
Benny Prijono708725a2008-03-09 12:55:00 +00001032 handle_peer_pkt(rel->allocation, rel, rel->tp.rx_pkt,
1033 bytes_read, &rel->tp.src_addr);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001034 }
1035
1036 /* Read next packet */
1037 bytes_read = sizeof(rel->tp.rx_pkt);
1038 rel->tp.src_addr_len = sizeof(rel->tp.src_addr);
1039 status = pj_ioqueue_recvfrom(key, op_key,
1040 rel->tp.rx_pkt, &bytes_read, 0,
1041 &rel->tp.src_addr,
1042 &rel->tp.src_addr_len);
1043
1044 if (status != PJ_EPENDING && status != PJ_SUCCESS)
1045 bytes_read = -status;
1046
1047 } while (status != PJ_EPENDING && status != PJ_ECANCELLED);
1048
Benny Prijono708725a2008-03-09 12:55:00 +00001049 /* Release allocation lock */
1050 pj_lock_release(rel->allocation->lock);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001051}
1052
1053/*
1054 * Callback notification from STUN session when it wants to send
1055 * a STUN message towards the client.
1056 */
1057static pj_status_t stun_on_send_msg(pj_stun_session *sess,
1058 const void *pkt,
1059 pj_size_t pkt_size,
1060 const pj_sockaddr_t *dst_addr,
1061 unsigned addr_len)
1062{
Benny Prijono708725a2008-03-09 12:55:00 +00001063 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001064
Benny Prijono708725a2008-03-09 12:55:00 +00001065 alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001066
Benny Prijono708725a2008-03-09 12:55:00 +00001067 return pj_turn_listener_sendto(alloc->listener, pkt, pkt_size, 0,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001068 dst_addr, addr_len);
1069}
1070
1071/*
1072 * Callback notification from STUN session when it receives STUN
1073 * requests. This callback was trigger by STUN incoming message
Benny Prijono708725a2008-03-09 12:55:00 +00001074 * processing in pj_turn_allocation_on_rx_client_pkt().
Benny Prijonob05aafc2008-03-08 00:54:04 +00001075 */
1076static pj_status_t stun_on_rx_request(pj_stun_session *sess,
1077 const pj_uint8_t *pkt,
1078 unsigned pkt_len,
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001079 const pj_stun_rx_data *rdata,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001080 const pj_sockaddr_t *src_addr,
1081 unsigned src_addr_len)
1082{
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001083 const pj_stun_msg *msg = rdata->msg;
Benny Prijono708725a2008-03-09 12:55:00 +00001084 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001085
Benny Prijono708725a2008-03-09 12:55:00 +00001086 PJ_UNUSED_ARG(pkt);
1087 PJ_UNUSED_ARG(pkt_len);
1088 PJ_UNUSED_ARG(src_addr);
1089 PJ_UNUSED_ARG(src_addr_len);
1090
1091 alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001092
1093 /* Refuse to serve any request if we've been shutdown */
1094 if (alloc->relay.lifetime == 0) {
Benny Prijono708725a2008-03-09 12:55:00 +00001095 /* Reject with 437 if we're shutting down */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001096 send_reply_err(alloc, rdata, PJ_TRUE,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001097 PJ_STUN_SC_ALLOCATION_MISMATCH, NULL);
1098 return PJ_SUCCESS;
1099 }
1100
1101 if (msg->hdr.type == PJ_STUN_REFRESH_REQUEST) {
1102 /*
1103 * Handle REFRESH request
1104 */
1105 pj_stun_lifetime_attr *lifetime;
1106 pj_stun_bandwidth_attr *bandwidth;
1107
1108 /* Get LIFETIME attribute */
1109 lifetime = (pj_stun_lifetime_attr*)
1110 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0);
1111
1112 /* Get BANDWIDTH attribute */
1113 bandwidth = (pj_stun_bandwidth_attr*)
1114 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_BANDWIDTH, 0);
1115
1116 if (lifetime && lifetime->value==0) {
1117 /*
1118 * This is deallocation request.
1119 */
1120 alloc->relay.lifetime = 0;
1121
1122 /* Respond first */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001123 send_reply_ok(alloc, rdata);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001124
1125 /* Shutdown allocation */
1126 PJ_LOG(4,(alloc->obj_name,
1127 "Client %s request to dealloc, shutting down",
1128 alloc->info));
1129
1130 alloc_shutdown(alloc);
1131
1132 } else {
1133 /*
1134 * This is a refresh request.
1135 */
1136
1137 /* Update lifetime */
1138 if (lifetime) {
1139 alloc->relay.lifetime = lifetime->value;
1140 }
1141
1142 /* Update bandwidth */
1143 // TODO:
1144
1145 /* Update expiration timer */
1146 resched_timeout(alloc);
1147
1148 /* Send reply */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001149 send_reply_ok(alloc, rdata);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001150 }
1151
1152 } else if (msg->hdr.type == PJ_STUN_CHANNEL_BIND_REQUEST) {
1153 /*
1154 * ChannelBind request.
1155 */
1156 pj_stun_channel_number_attr *ch_attr;
1157 pj_stun_peer_addr_attr *peer_attr;
Benny Prijono708725a2008-03-09 12:55:00 +00001158 pj_turn_permission *p1, *p2;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001159
1160 ch_attr = (pj_stun_channel_number_attr*)
1161 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CHANNEL_NUMBER, 0);
1162 peer_attr = (pj_stun_peer_addr_attr*)
1163 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
1164
1165 if (!ch_attr || !peer_attr) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001166 send_reply_err(alloc, rdata, PJ_TRUE,
1167 PJ_STUN_SC_BAD_REQUEST, NULL);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001168 return PJ_SUCCESS;
1169 }
1170
1171 /* Find permission with the channel number */
1172 p1 = lookup_permission_by_chnum(alloc, PJ_STUN_GET_CH_NB(ch_attr->value));
1173
1174 /* If permission is found, this is supposed to be a channel bind
1175 * refresh. Make sure it's for the same peer.
1176 */
1177 if (p1) {
1178 if (pj_sockaddr_cmp(&p1->hkey.peer_addr, &peer_attr->sockaddr)) {
1179 /* Address mismatch. Send 400 */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001180 send_reply_err(alloc, rdata, PJ_TRUE,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001181 PJ_STUN_SC_BAD_REQUEST,
1182 "Peer address mismatch");
1183 return PJ_SUCCESS;
1184 }
1185
1186 /* Refresh permission */
1187 refresh_permission(p1);
1188
1189 /* Done */
1190 return PJ_SUCCESS;
1191 }
1192
1193 /* If permission is not found, create a new one. Make sure the peer
1194 * has not alreadyy assigned with a channel number.
1195 */
1196 p2 = lookup_permission_by_addr(alloc, &peer_attr->sockaddr,
1197 pj_sockaddr_get_len(&peer_attr->sockaddr));
Benny Prijono708725a2008-03-09 12:55:00 +00001198 if (p2 && p2->channel != PJ_TURN_INVALID_CHANNEL) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001199 send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001200 "Peer address already assigned a channel number");
1201 return PJ_SUCCESS;
1202 }
1203
1204 /* Create permission if it doesn't exist */
1205 if (!p2) {
1206 p2 = create_permission(alloc, &peer_attr->sockaddr,
1207 pj_sockaddr_get_len(&peer_attr->sockaddr));
1208 if (!p2)
1209 return PJ_SUCCESS;
1210 }
1211
1212 /* Assign channel number to permission */
1213 p2->channel = PJ_STUN_GET_CH_NB(ch_attr->value);
1214
1215 /* Update */
1216 refresh_permission(p2);
1217
1218 /* Reply */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001219 send_reply_ok(alloc, rdata);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001220
1221 return PJ_SUCCESS;
1222
1223 } else if (msg->hdr.type == PJ_STUN_ALLOCATE_REQUEST) {
1224
1225 /* Respond with 437 (section 6.3 turn-07) */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001226 send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH,
1227 NULL);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001228
1229 } else {
1230
1231 /* Respond with Bad Request? */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001232 send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001233
1234 }
1235
1236 return PJ_SUCCESS;
1237}
1238
1239/*
1240 * Callback notification from STUN session when it receives STUN
1241 * indications. This callback was trigger by STUN incoming message
Benny Prijono708725a2008-03-09 12:55:00 +00001242 * processing in pj_turn_allocation_on_rx_client_pkt().
Benny Prijonob05aafc2008-03-08 00:54:04 +00001243 */
1244static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
1245 const pj_uint8_t *pkt,
1246 unsigned pkt_len,
1247 const pj_stun_msg *msg,
1248 const pj_sockaddr_t *src_addr,
1249 unsigned src_addr_len)
1250{
1251 pj_stun_peer_addr_attr *peer_attr;
1252 pj_stun_data_attr *data_attr;
Benny Prijono708725a2008-03-09 12:55:00 +00001253 pj_turn_allocation *alloc;
1254 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001255
Benny Prijono708725a2008-03-09 12:55:00 +00001256 PJ_UNUSED_ARG(pkt);
1257 PJ_UNUSED_ARG(pkt_len);
1258 PJ_UNUSED_ARG(src_addr);
1259 PJ_UNUSED_ARG(src_addr_len);
1260
1261 alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001262
1263 /* Only expect Send Indication */
1264 if (msg->hdr.type != PJ_STUN_SEND_INDICATION) {
1265 /* Ignore */
1266 return PJ_SUCCESS;
1267 }
1268
1269 /* Get PEER-ADDRESS attribute */
1270 peer_attr = (pj_stun_peer_addr_attr*)
1271 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
1272
1273 /* MUST have PEER-ADDRESS attribute */
1274 if (!peer_attr)
1275 return PJ_SUCCESS;
1276
1277 /* Get DATA attribute */
1278 data_attr = (pj_stun_data_attr*)
1279 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_DATA, 0);
1280
1281 /* Create/update/refresh the permission */
1282 perm = lookup_permission_by_addr(alloc, &peer_attr->sockaddr,
1283 pj_sockaddr_get_len(&peer_attr->sockaddr));
1284 if (perm == NULL) {
1285 perm = create_permission(alloc, &peer_attr->sockaddr,
1286 pj_sockaddr_get_len(&peer_attr->sockaddr));
1287 }
1288 refresh_permission(perm);
1289
1290 /* Return if we don't have data */
1291 if (data_attr == NULL)
1292 return PJ_SUCCESS;
1293
1294 /* Relay the data to client */
Benny Prijono708725a2008-03-09 12:55:00 +00001295 if (alloc->hkey.tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +00001296 pj_ssize_t len = data_attr->length;
1297 pj_sock_sendto(alloc->listener->sock, data_attr->data,
1298 &len, 0, &peer_attr->sockaddr,
1299 pj_sockaddr_get_len(&peer_attr->sockaddr));
1300 } else {
1301 pj_assert(!"TCP is not supported");
1302 }
1303
1304 return PJ_SUCCESS;
1305}
1306
1307