blob: 3e4b77f6eb641e00b49b268387eb0bb0717c9a10 [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,
Benny Prijono879ad1a2008-04-09 09:38:12 +000068 void *token,
Benny Prijonob05aafc2008-03-08 00:54:04 +000069 const void *pkt,
70 pj_size_t pkt_size,
71 const pj_sockaddr_t *dst_addr,
72 unsigned addr_len);
73static pj_status_t stun_on_rx_request(pj_stun_session *sess,
74 const pj_uint8_t *pkt,
75 unsigned pkt_len,
Benny Prijonoa5d214f2008-03-19 23:00:30 +000076 const pj_stun_rx_data *rdata,
Benny Prijono879ad1a2008-04-09 09:38:12 +000077 void *token,
Benny Prijonob05aafc2008-03-08 00:54:04 +000078 const pj_sockaddr_t *src_addr,
79 unsigned src_addr_len);
80static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
81 const pj_uint8_t *pkt,
82 unsigned pkt_len,
83 const pj_stun_msg *msg,
Benny Prijono879ad1a2008-04-09 09:38:12 +000084 void *token,
Benny Prijonob05aafc2008-03-08 00:54:04 +000085 const pj_sockaddr_t *src_addr,
86 unsigned src_addr_len);
87
88/* Log allocation error */
Benny Prijono708725a2008-03-09 12:55:00 +000089static void alloc_err(pj_turn_allocation *alloc, const char *title,
Benny Prijonob05aafc2008-03-08 00:54:04 +000090 pj_status_t status)
91{
92 char errmsg[PJ_ERR_MSG_SIZE];
93
94 pj_strerror(status, errmsg, sizeof(errmsg));
95 PJ_LOG(4,(alloc->obj_name, "%s for client %s: %s",
96 title, alloc->info, errmsg));
97}
98
Benny Prijono708725a2008-03-09 12:55:00 +000099
100/* Parse ALLOCATE request */
101static pj_status_t parse_allocate_req(alloc_request *cfg,
102 pj_stun_session *sess,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000103 const pj_stun_rx_data *rdata,
Benny Prijono708725a2008-03-09 12:55:00 +0000104 const pj_sockaddr_t *src_addr,
105 unsigned src_addr_len)
106{
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000107 const pj_stun_msg *req = rdata->msg;
Benny Prijono708725a2008-03-09 12:55:00 +0000108 pj_stun_bandwidth_attr *attr_bw;
109 pj_stun_req_transport_attr *attr_req_tp;
110 pj_stun_res_token_attr *attr_res_token;
111 pj_stun_req_props_attr *attr_rpp;
112 pj_stun_lifetime_attr *attr_lifetime;
113
114 pj_bzero(cfg, sizeof(*cfg));
115
116 /* Get BANDWIDTH attribute, if any. */
117 attr_bw = (pj_stun_uint_attr*)
118 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_BANDWIDTH, 0);
119 if (attr_bw) {
120 cfg->bandwidth = attr_bw->value;
121 } else {
122 cfg->bandwidth = DEFA_CLIENT_BANDWIDTH;
123 }
124
125 /* Check if we can satisfy the bandwidth */
126 if (cfg->bandwidth > MAX_CLIENT_BANDWIDTH) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000127 pj_stun_session_respond(sess, rdata,
128 PJ_STUN_SC_ALLOCATION_QUOTA_REACHED,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000129 "Invalid bandwidth", NULL, PJ_TRUE,
Benny Prijono708725a2008-03-09 12:55:00 +0000130 src_addr, src_addr_len);
131 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_ALLOCATION_QUOTA_REACHED);
132 }
133
134 /* MUST have REQUESTED-TRANSPORT attribute */
135 attr_req_tp = (pj_stun_uint_attr*)
136 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_TRANSPORT, 0);
137 if (attr_req_tp == NULL) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000138 pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST,
Benny Prijono708725a2008-03-09 12:55:00 +0000139 "Missing REQUESTED-TRANSPORT attribute",
Benny Prijono879ad1a2008-04-09 09:38:12 +0000140 NULL, PJ_TRUE, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000141 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
142 }
143
144 cfg->tp_type = PJ_STUN_GET_RT_PROTO(attr_req_tp->value);
145
146 /* Can only support UDP for now */
147 if (cfg->tp_type != PJ_TURN_TP_UDP) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000148 pj_stun_session_respond(sess, rdata, PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000149 NULL, NULL, PJ_TRUE, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000150 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO);
151 }
152
153 /* Get RESERVATION-TOKEN attribute, if any */
154 attr_res_token = (pj_stun_res_token_attr*)
155 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_RESERVATION_TOKEN,
156 0);
157 if (attr_res_token) {
158 /* We don't support RESERVATION-TOKEN for now */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000159 pj_stun_session_respond(sess, rdata,
Benny Prijono708725a2008-03-09 12:55:00 +0000160 PJ_STUN_SC_BAD_REQUEST,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000161 "RESERVATION-TOKEN is not supported", NULL,
162 PJ_TRUE, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000163 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
164 }
165
166 /* Get REQUESTED-PROPS attribute, if any */
167 attr_rpp = (pj_stun_req_props_attr*)
168 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_PROPS, 0);
169 if (attr_rpp) {
170 /* We don't support REQUESTED-PROPS for now */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000171 pj_stun_session_respond(sess, rdata,
Benny Prijono708725a2008-03-09 12:55:00 +0000172 PJ_STUN_SC_BAD_REQUEST,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000173 "REQUESTED-PROPS is not supported",
174 NULL, PJ_TRUE, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000175 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
176 }
177
178 /* Get LIFETIME attribute */
179 attr_lifetime = (pj_stun_uint_attr*)
180 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0);
181 if (attr_lifetime) {
182 cfg->lifetime = attr_lifetime->value;
183 if (cfg->lifetime < MIN_LIFETIME) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000184 pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000185 "LIFETIME too short", NULL,
186 PJ_TRUE, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000187 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
188 }
189 if (cfg->lifetime > MAX_LIFETIME)
190 cfg->lifetime = MAX_LIFETIME;
191 } else {
192 cfg->lifetime = DEF_LIFETIME;
193 }
194
195 return PJ_SUCCESS;
196}
197
198
199/* Respond to ALLOCATE request */
200static pj_status_t send_allocate_response(pj_turn_allocation *alloc,
201 pj_stun_session *srv_sess,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000202 pj_turn_transport *transport,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000203 const pj_stun_rx_data *rdata)
Benny Prijono708725a2008-03-09 12:55:00 +0000204{
205 pj_stun_tx_data *tdata;
206 pj_status_t status;
207
208 /* Respond the original ALLOCATE request */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000209 status = pj_stun_session_create_res(srv_sess, rdata, 0, NULL, &tdata);
Benny Prijono708725a2008-03-09 12:55:00 +0000210 if (status != PJ_SUCCESS)
211 return status;
212
213 /* Add RELAYED-ADDRESS attribute */
214 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
Benny Prijonoefa014b2008-08-22 17:46:33 +0000215 PJ_STUN_ATTR_RELAYED_ADDR, PJ_TRUE,
Benny Prijono708725a2008-03-09 12:55:00 +0000216 &alloc->relay.hkey.addr,
217 pj_sockaddr_get_len(&alloc->relay.hkey.addr));
218
219 /* Add LIFETIME. */
220 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
221 PJ_STUN_ATTR_LIFETIME,
222 (unsigned)alloc->relay.lifetime);
223
224 /* Add BANDWIDTH */
225 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
226 PJ_STUN_ATTR_BANDWIDTH,
227 alloc->bandwidth);
228
229 /* Add RESERVATION-TOKEN */
230 PJ_TODO(ADD_RESERVATION_TOKEN);
231
232 /* Add XOR-MAPPED-ADDRESS */
233 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
234 PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE,
235 &alloc->hkey.clt_addr,
236 pj_sockaddr_get_len(&alloc->hkey.clt_addr));
237
238 /* Send the response */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000239 return pj_stun_session_send_msg(srv_sess, transport, PJ_TRUE,
240 PJ_FALSE, &alloc->hkey.clt_addr,
Benny Prijono708725a2008-03-09 12:55:00 +0000241 pj_sockaddr_get_len(&alloc->hkey.clt_addr),
242 tdata);
243}
244
245
246/*
247 * Init credential for the allocation. We use static credential, meaning that
248 * the user's password must not change during allocation.
249 */
250static pj_status_t init_cred(pj_turn_allocation *alloc, const pj_stun_msg *req)
251{
252 const pj_stun_username_attr *user;
253 const pj_stun_realm_attr *realm;
254 const pj_stun_nonce_attr *nonce;
255 pj_status_t status;
256
257 realm = (const pj_stun_realm_attr*)
258 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REALM, 0);
259 PJ_ASSERT_RETURN(realm != NULL, PJ_EBUG);
260
261 user = (const pj_stun_username_attr*)
262 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0);
263 PJ_ASSERT_RETURN(user != NULL, PJ_EBUG);
264
265 nonce = (const pj_stun_nonce_attr*)
266 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_NONCE, 0);
267 PJ_ASSERT_RETURN(nonce != NULL, PJ_EBUG);
268
269 /* Lookup the password */
270 status = pj_turn_get_password(NULL, NULL, &realm->value,
271 &user->value, alloc->pool,
272 &alloc->cred.data.static_cred.data_type,
273 &alloc->cred.data.static_cred.data);
274 if (status != PJ_SUCCESS)
275 return status;
276
277 /* Save credential */
278 alloc->cred.type = PJ_STUN_AUTH_CRED_STATIC;
279 pj_strdup(alloc->pool, &alloc->cred.data.static_cred.realm, &realm->value);
280 pj_strdup(alloc->pool, &alloc->cred.data.static_cred.username, &user->value);
281 pj_strdup(alloc->pool, &alloc->cred.data.static_cred.nonce, &nonce->value);
282
283 return PJ_SUCCESS;
284}
285
286
Benny Prijonob05aafc2008-03-08 00:54:04 +0000287/*
288 * Create new allocation.
289 */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000290PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport,
Benny Prijono708725a2008-03-09 12:55:00 +0000291 const pj_sockaddr_t *src_addr,
292 unsigned src_addr_len,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000293 const pj_stun_rx_data *rdata,
Benny Prijono708725a2008-03-09 12:55:00 +0000294 pj_stun_session *srv_sess,
295 pj_turn_allocation **p_alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000296{
Benny Prijono879ad1a2008-04-09 09:38:12 +0000297 pj_turn_srv *srv = transport->listener->server;
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000298 const pj_stun_msg *msg = rdata->msg;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000299 pj_pool_t *pool;
Benny Prijono708725a2008-03-09 12:55:00 +0000300 alloc_request req;
301 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000302 pj_stun_session_cb sess_cb;
Benny Prijono708725a2008-03-09 12:55:00 +0000303 char str_tmp[80];
Benny Prijonob05aafc2008-03-08 00:54:04 +0000304 pj_status_t status;
305
Benny Prijono708725a2008-03-09 12:55:00 +0000306 /* Parse ALLOCATE request */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000307 status = parse_allocate_req(&req, srv_sess, rdata, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000308 if (status != PJ_SUCCESS)
309 return status;
310
Benny Prijonob05aafc2008-03-08 00:54:04 +0000311 pool = pj_pool_create(srv->core.pf, "alloc%p", 1000, 1000, NULL);
312
313 /* Init allocation structure */
Benny Prijono708725a2008-03-09 12:55:00 +0000314 alloc = PJ_POOL_ZALLOC_T(pool, pj_turn_allocation);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000315 alloc->pool = pool;
316 alloc->obj_name = pool->obj_name;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000317 alloc->relay.tp.sock = PJ_INVALID_SOCKET;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000318 alloc->server = transport->listener->server;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000319
Benny Prijono708725a2008-03-09 12:55:00 +0000320 alloc->bandwidth = req.bandwidth;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000321
Benny Prijono879ad1a2008-04-09 09:38:12 +0000322 /* Set transport */
323 alloc->transport = transport;
324 pj_turn_transport_add_ref(transport, alloc);
325
326 alloc->hkey.tp_type = transport->listener->tp_type;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000327 pj_memcpy(&alloc->hkey.clt_addr, src_addr, src_addr_len);
328
329 status = pj_lock_create_recursive_mutex(pool, alloc->obj_name,
330 &alloc->lock);
331 if (status != PJ_SUCCESS) {
Benny Prijono708725a2008-03-09 12:55:00 +0000332 goto on_error;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000333 }
334
335 /* Create peer hash table */
336 alloc->peer_table = pj_hash_create(pool, PEER_TABLE_SIZE);
337
338 /* Create channel hash table */
339 alloc->ch_table = pj_hash_create(pool, PEER_TABLE_SIZE);
340
341 /* Print info */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000342 pj_ansi_strcpy(alloc->info,
343 pj_turn_tp_type_name(transport->listener->tp_type));
Benny Prijonob05aafc2008-03-08 00:54:04 +0000344 alloc->info[3] = ':';
345 pj_sockaddr_print(src_addr, alloc->info+4, sizeof(alloc->info)-4, 3);
346
347 /* Create STUN session to handle STUN communication with client */
348 pj_bzero(&sess_cb, sizeof(sess_cb));
349 sess_cb.on_send_msg = &stun_on_send_msg;
350 sess_cb.on_rx_request = &stun_on_rx_request;
351 sess_cb.on_rx_indication = &stun_on_rx_indication;
352 status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name,
353 &sess_cb, PJ_FALSE, &alloc->sess);
354 if (status != PJ_SUCCESS) {
Benny Prijono708725a2008-03-09 12:55:00 +0000355 goto on_error;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000356 }
357
358 /* Attach to STUN session */
359 pj_stun_session_set_user_data(alloc->sess, alloc);
360
Benny Prijono708725a2008-03-09 12:55:00 +0000361 /* Init authentication credential */
362 status = init_cred(alloc, msg);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000363 if (status != PJ_SUCCESS) {
Benny Prijono708725a2008-03-09 12:55:00 +0000364 goto on_error;
365 }
366
367 /* Attach authentication credential to STUN session */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000368 pj_stun_session_set_credential(alloc->sess, PJ_STUN_AUTH_LONG_TERM,
369 &alloc->cred);
Benny Prijono708725a2008-03-09 12:55:00 +0000370
371 /* Create the relay resource */
372 status = create_relay(srv, alloc, msg, &req, &alloc->relay);
373 if (status != PJ_SUCCESS) {
374 goto on_error;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000375 }
376
377 /* Register this allocation */
Benny Prijono708725a2008-03-09 12:55:00 +0000378 pj_turn_srv_register_allocation(srv, alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000379
Benny Prijono708725a2008-03-09 12:55:00 +0000380 /* Respond to ALLOCATE request */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000381 status = send_allocate_response(alloc, srv_sess, transport, rdata);
Benny Prijono708725a2008-03-09 12:55:00 +0000382 if (status != PJ_SUCCESS)
383 goto on_error;
384
385 /* Done */
386 pj_sockaddr_print(&alloc->relay.hkey.addr, str_tmp,
387 sizeof(str_tmp), 3);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000388 PJ_LOG(4,(alloc->obj_name, "Client %s created, relay addr=%s:%s",
Benny Prijono708725a2008-03-09 12:55:00 +0000389 alloc->info, pj_turn_tp_type_name(req.tp_type), str_tmp));
Benny Prijonob05aafc2008-03-08 00:54:04 +0000390
391 /* Success */
392 *p_alloc = alloc;
393 return PJ_SUCCESS;
Benny Prijono708725a2008-03-09 12:55:00 +0000394
395on_error:
396 /* Send reply to the ALLOCATE request */
397 pj_strerror(status, str_tmp, sizeof(str_tmp));
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000398 pj_stun_session_respond(srv_sess, rdata, PJ_STUN_SC_BAD_REQUEST, str_tmp,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000399 transport, PJ_TRUE, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000400
401 /* Cleanup */
402 destroy_allocation(alloc);
403 return status;
404}
405
406
407/* Destroy relay resource */
408static void destroy_relay(pj_turn_relay_res *relay)
409{
410 if (relay->timer.id) {
Benny Prijono879ad1a2008-04-09 09:38:12 +0000411 pj_timer_heap_cancel(relay->allocation->server->core.timer_heap,
Benny Prijono708725a2008-03-09 12:55:00 +0000412 &relay->timer);
413 relay->timer.id = PJ_FALSE;
414 }
415
416 if (relay->tp.key) {
417 pj_ioqueue_unregister(relay->tp.key);
418 relay->tp.key = NULL;
419 relay->tp.sock = PJ_INVALID_SOCKET;
420 } else if (relay->tp.sock != PJ_INVALID_SOCKET) {
421 pj_sock_close(relay->tp.sock);
422 relay->tp.sock = PJ_INVALID_SOCKET;
423 }
424
425 /* Mark as shutdown */
426 relay->lifetime = 0;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000427}
428
429
430/*
Benny Prijono708725a2008-03-09 12:55:00 +0000431 * Really destroy allocation.
Benny Prijonob05aafc2008-03-08 00:54:04 +0000432 */
Benny Prijono708725a2008-03-09 12:55:00 +0000433static void destroy_allocation(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000434{
435 pj_pool_t *pool;
436
437 /* Unregister this allocation */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000438 pj_turn_srv_unregister_allocation(alloc->server, alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000439
440 /* Destroy relay */
441 destroy_relay(&alloc->relay);
442
443 /* Must lock only after destroying relay otherwise deadlock */
444 if (alloc->lock) {
445 pj_lock_acquire(alloc->lock);
446 }
447
Benny Prijono879ad1a2008-04-09 09:38:12 +0000448 /* Unreference transport */
449 if (alloc->transport) {
450 pj_turn_transport_dec_ref(alloc->transport, alloc);
451 alloc->transport = NULL;
452 }
453
Benny Prijonob05aafc2008-03-08 00:54:04 +0000454 /* Destroy STUN session */
455 if (alloc->sess) {
456 pj_stun_session_destroy(alloc->sess);
457 alloc->sess = NULL;
458 }
459
460 /* Destroy lock */
461 if (alloc->lock) {
462 pj_lock_release(alloc->lock);
463 pj_lock_destroy(alloc->lock);
464 alloc->lock = NULL;
465 }
466
467 /* Destroy pool */
468 pool = alloc->pool;
469 if (pool) {
470 alloc->pool = NULL;
471 pj_pool_release(pool);
472 }
473}
474
475
Benny Prijono708725a2008-03-09 12:55:00 +0000476PJ_DECL(void) pj_turn_allocation_destroy(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000477{
Benny Prijono708725a2008-03-09 12:55:00 +0000478 destroy_allocation(alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000479}
480
Benny Prijono708725a2008-03-09 12:55:00 +0000481
Benny Prijono879ad1a2008-04-09 09:38:12 +0000482/*
483 * Handle transport closure.
484 */
485PJ_DEF(void) pj_turn_allocation_on_transport_closed( pj_turn_allocation *alloc,
486 pj_turn_transport *tp)
487{
488 PJ_LOG(5,(alloc->obj_name, "Transport %s unexpectedly closed, destroying "
489 "allocation %s", tp->info, alloc->info));
490 pj_turn_transport_dec_ref(tp, alloc);
491 alloc->transport = NULL;
492 destroy_allocation(alloc);
493}
494
495
Benny Prijono708725a2008-03-09 12:55:00 +0000496/* Initiate shutdown sequence for this allocation and start destroy timer.
497 * Once allocation is marked as shutting down, any packets will be
498 * rejected/discarded
499 */
500static void alloc_shutdown(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000501{
502 pj_time_val destroy_delay = DESTROY_DELAY;
503
504 /* Work with existing schedule */
505 if (alloc->relay.timer.id == TIMER_ID_TIMEOUT) {
Benny Prijono708725a2008-03-09 12:55:00 +0000506 /* Cancel existing shutdown timer */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000507 pj_timer_heap_cancel(alloc->server->core.timer_heap,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000508 &alloc->relay.timer);
509 alloc->relay.timer.id = TIMER_ID_NONE;
510
511 } else if (alloc->relay.timer.id == TIMER_ID_DESTROY) {
512 /* We've been scheduled to be destroyed, ignore this
513 * shutdown request.
514 */
515 return;
516 }
517
518 pj_assert(alloc->relay.timer.id == TIMER_ID_NONE);
519
520 /* Shutdown relay socket */
521 destroy_relay(&alloc->relay);
522
523 /* Don't unregister from hash table because we still need to
524 * handle REFRESH retransmission.
525 */
526
527 /* Schedule destroy timer */
528 alloc->relay.timer.id = TIMER_ID_DESTROY;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000529 pj_timer_heap_schedule(alloc->server->core.timer_heap,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000530 &alloc->relay.timer, &destroy_delay);
531}
532
Benny Prijono708725a2008-03-09 12:55:00 +0000533
Benny Prijonob05aafc2008-03-08 00:54:04 +0000534/* Reschedule timeout using current lifetime setting */
Benny Prijono708725a2008-03-09 12:55:00 +0000535static pj_status_t resched_timeout(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000536{
537 pj_time_val delay;
538 pj_status_t status;
539
540 pj_gettimeofday(&alloc->relay.expiry);
541 alloc->relay.expiry.sec += alloc->relay.lifetime;
542
543 pj_assert(alloc->relay.timer.id != TIMER_ID_DESTROY);
544 if (alloc->relay.timer.id != 0) {
Benny Prijono879ad1a2008-04-09 09:38:12 +0000545 pj_timer_heap_cancel(alloc->server->core.timer_heap,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000546 &alloc->relay.timer);
547 alloc->relay.timer.id = TIMER_ID_NONE;
548 }
549
550 delay.sec = alloc->relay.lifetime;
551 delay.msec = 0;
552
553 alloc->relay.timer.id = TIMER_ID_TIMEOUT;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000554 status = pj_timer_heap_schedule(alloc->server->core.timer_heap,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000555 &alloc->relay.timer, &delay);
556 if (status != PJ_SUCCESS) {
557 alloc->relay.timer.id = TIMER_ID_NONE;
558 return status;
559 }
560
561 return PJ_SUCCESS;
562}
563
564
565/* Timer timeout callback */
566static void relay_timeout_cb(pj_timer_heap_t *heap, pj_timer_entry *e)
567{
Benny Prijono708725a2008-03-09 12:55:00 +0000568 pj_turn_relay_res *rel;
569 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000570
Benny Prijono708725a2008-03-09 12:55:00 +0000571 PJ_UNUSED_ARG(heap);
572
573 rel = (pj_turn_relay_res*) e->user_data;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000574 alloc = rel->allocation;
575
576 if (e->id == TIMER_ID_TIMEOUT) {
577
578 e->id = TIMER_ID_NONE;
579
580 PJ_LOG(4,(alloc->obj_name,
581 "Client %s refresh timed-out, shutting down..",
582 alloc->info));
583
584 alloc_shutdown(alloc);
585
586 } else if (e->id == TIMER_ID_DESTROY) {
587 e->id = TIMER_ID_NONE;
588
589 PJ_LOG(4,(alloc->obj_name, "Client %s destroying..",
590 alloc->info));
591
Benny Prijono708725a2008-03-09 12:55:00 +0000592 destroy_allocation(alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000593 }
594}
595
596
597/*
598 * Create relay.
599 */
Benny Prijono708725a2008-03-09 12:55:00 +0000600static pj_status_t create_relay(pj_turn_srv *srv,
601 pj_turn_allocation *alloc,
602 const pj_stun_msg *msg,
603 const alloc_request *req,
604 pj_turn_relay_res *relay)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000605{
606 enum { RETRY = 40 };
607 pj_pool_t *pool = alloc->pool;
608 int retry, retry_max, sock_type;
609 pj_ioqueue_callback icb;
610 int af, namelen;
611 pj_stun_string_attr *sa;
612 pj_status_t status;
613
614 pj_bzero(relay, sizeof(*relay));
615
616 relay->allocation = alloc;
617 relay->tp.sock = PJ_INVALID_SOCKET;
618
619 /* TODO: get the requested address family from somewhere */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000620 af = alloc->transport->listener->addr.addr.sa_family;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000621
622 /* Save realm */
623 sa = (pj_stun_string_attr*)
624 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0);
625 PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP);
626 pj_strdup(pool, &relay->realm, &sa->value);
627
628 /* Save username */
629 sa = (pj_stun_string_attr*)
630 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0);
631 PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP);
632 pj_strdup(pool, &relay->user, &sa->value);
633
634 /* Lifetime and timeout */
635 relay->lifetime = req->lifetime;
636 pj_timer_entry_init(&relay->timer, TIMER_ID_NONE, relay,
637 &relay_timeout_cb);
638 resched_timeout(alloc);
639
640 /* Transport type */
641 relay->hkey.tp_type = req->tp_type;
642
643 /* Create the socket */
Benny Prijono708725a2008-03-09 12:55:00 +0000644 if (req->tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000645 sock_type = pj_SOCK_DGRAM();
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 sock_type = pj_SOCK_STREAM();
648 } else {
649 pj_assert(!"Unknown transport");
650 return PJ_EINVALIDOP;
651 }
652
653 status = pj_sock_socket(af, sock_type, 0, &relay->tp.sock);
654 if (status != PJ_SUCCESS) {
655 pj_bzero(relay, sizeof(*relay));
656 return status;
657 }
658
659 /* Find suitable port for this allocation */
660 if (req->rpp_port) {
661 retry_max = 1;
662 } else {
663 retry_max = RETRY;
664 }
665
666 for (retry=0; retry<retry_max; ++retry) {
667 pj_uint16_t port;
668 pj_sockaddr bound_addr;
669
670 pj_lock_acquire(srv->core.lock);
671
672 if (req->rpp_port) {
673 port = (pj_uint16_t) req->rpp_port;
Benny Prijono708725a2008-03-09 12:55:00 +0000674 } else if (req->tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000675 port = (pj_uint16_t) srv->ports.next_udp++;
676 if (srv->ports.next_udp > srv->ports.max_udp)
677 srv->ports.next_udp = srv->ports.min_udp;
Benny Prijono708725a2008-03-09 12:55:00 +0000678 } else if (req->tp_type == PJ_TURN_TP_TCP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000679 port = (pj_uint16_t) srv->ports.next_tcp++;
680 if (srv->ports.next_tcp > srv->ports.max_tcp)
681 srv->ports.next_tcp = srv->ports.min_tcp;
682 } else {
683 pj_assert(!"Invalid transport");
Benny Prijono708725a2008-03-09 12:55:00 +0000684 port = 0;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000685 }
686
687 pj_lock_release(srv->core.lock);
688
689 pj_sockaddr_init(af, &bound_addr, NULL, port);
690
691 status = pj_sock_bind(relay->tp.sock, &bound_addr,
692 pj_sockaddr_get_len(&bound_addr));
693 if (status == PJ_SUCCESS)
694 break;
695 }
696
697 if (status != PJ_SUCCESS) {
698 /* Unable to allocate port */
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000699 PJ_LOG(4,(THIS_FILE, "Unable to allocate relay, giving up: err %d",
Benny Prijonob05aafc2008-03-08 00:54:04 +0000700 status));
701 pj_sock_close(relay->tp.sock);
702 relay->tp.sock = PJ_INVALID_SOCKET;
703 return status;
704 }
705
706 /* Init relay key */
707 namelen = sizeof(relay->hkey.addr);
708 status = pj_sock_getsockname(relay->tp.sock, &relay->hkey.addr, &namelen);
709 if (status != PJ_SUCCESS) {
710 PJ_LOG(4,(THIS_FILE, "pj_sock_getsockname() failed: err %d",
711 status));
712 pj_sock_close(relay->tp.sock);
713 relay->tp.sock = PJ_INVALID_SOCKET;
714 return status;
715 }
716 if (!pj_sockaddr_has_addr(&relay->hkey.addr)) {
Benny Prijono879ad1a2008-04-09 09:38:12 +0000717 pj_sockaddr_copy_addr(&relay->hkey.addr,
718 &alloc->transport->listener->addr);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000719 }
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000720 if (!pj_sockaddr_has_addr(&relay->hkey.addr)) {
721 pj_sockaddr tmp_addr;
722 pj_gethostip(af, &tmp_addr);
723 pj_sockaddr_copy_addr(&relay->hkey.addr, &tmp_addr);
724 }
Benny Prijonob05aafc2008-03-08 00:54:04 +0000725
726 /* Init ioqueue */
727 pj_bzero(&icb, sizeof(icb));
728 icb.on_read_complete = &on_rx_from_peer;
729
730 status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, relay->tp.sock,
731 relay, &icb, &relay->tp.key);
732 if (status != PJ_SUCCESS) {
733 PJ_LOG(4,(THIS_FILE, "pj_ioqueue_register_sock() failed: err %d",
734 status));
735 pj_sock_close(relay->tp.sock);
736 relay->tp.sock = PJ_INVALID_SOCKET;
737 return status;
738 }
739
740 /* Kick off pending read operation */
741 pj_ioqueue_op_key_init(&relay->tp.read_key, sizeof(relay->tp.read_key));
742 on_rx_from_peer(relay->tp.key, &relay->tp.read_key, 0);
743
744 /* Done */
745 return PJ_SUCCESS;
746}
747
748/* Create and send error response */
Benny Prijono708725a2008-03-09 12:55:00 +0000749static void send_reply_err(pj_turn_allocation *alloc,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000750 const pj_stun_rx_data *rdata,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000751 pj_bool_t cache,
752 int code, const char *errmsg)
753{
754 pj_status_t status;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000755
Benny Prijono879ad1a2008-04-09 09:38:12 +0000756 status = pj_stun_session_respond(alloc->sess, rdata, code, errmsg, NULL,
757 cache, &alloc->hkey.clt_addr,
Benny Prijono708725a2008-03-09 12:55:00 +0000758 pj_sockaddr_get_len(&alloc->hkey.clt_addr.addr));
Benny Prijonob05aafc2008-03-08 00:54:04 +0000759 if (status != PJ_SUCCESS) {
760 alloc_err(alloc, "Error sending STUN error response", status);
761 return;
762 }
763}
764
765/* Create and send successful response */
Benny Prijono708725a2008-03-09 12:55:00 +0000766static void send_reply_ok(pj_turn_allocation *alloc,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000767 const pj_stun_rx_data *rdata)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000768{
769 pj_status_t status;
770 unsigned interval;
771 pj_stun_tx_data *tdata;
772
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000773 status = pj_stun_session_create_res(alloc->sess, rdata, 0, NULL, &tdata);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000774 if (status != PJ_SUCCESS) {
775 alloc_err(alloc, "Error creating STUN success response", status);
776 return;
777 }
778
779 /* Calculate time to expiration */
780 if (alloc->relay.lifetime != 0) {
781 pj_time_val now;
782 pj_gettimeofday(&now);
783 interval = alloc->relay.expiry.sec - now.sec;
784 } else {
785 interval = 0;
786 }
787
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000788 /* Add LIFETIME if this is not ChannelBind. */
789 if (PJ_STUN_GET_METHOD(tdata->msg->hdr.type)!=PJ_STUN_CHANNEL_BIND_METHOD){
790 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
791 PJ_STUN_ATTR_LIFETIME, interval);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000792
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000793 /* Add BANDWIDTH if lifetime is not zero */
794 if (interval != 0) {
795 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
796 PJ_STUN_ATTR_BANDWIDTH,
797 alloc->bandwidth);
798 }
799 }
Benny Prijonob05aafc2008-03-08 00:54:04 +0000800
Benny Prijono879ad1a2008-04-09 09:38:12 +0000801 status = pj_stun_session_send_msg(alloc->sess, NULL, PJ_TRUE,
802 PJ_FALSE, &alloc->hkey.clt_addr,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000803 pj_sockaddr_get_len(&alloc->hkey.clt_addr),
804 tdata);
805 if (status != PJ_SUCCESS) {
806 alloc_err(alloc, "Error sending STUN success response", status);
807 return;
808 }
809}
810
811
812/* Create new permission */
Benny Prijono708725a2008-03-09 12:55:00 +0000813static pj_turn_permission *create_permission(pj_turn_allocation *alloc,
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000814 const pj_sockaddr_t *peer_addr,
815 unsigned addr_len)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000816{
Benny Prijono708725a2008-03-09 12:55:00 +0000817 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000818
Benny Prijono708725a2008-03-09 12:55:00 +0000819 perm = PJ_POOL_ZALLOC_T(alloc->pool, pj_turn_permission);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000820 pj_memcpy(&perm->hkey.peer_addr, peer_addr, addr_len);
821
Benny Prijonob05aafc2008-03-08 00:54:04 +0000822 perm->allocation = alloc;
Benny Prijono708725a2008-03-09 12:55:00 +0000823 perm->channel = PJ_TURN_INVALID_CHANNEL;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000824
825 pj_gettimeofday(&perm->expiry);
Benny Prijono708725a2008-03-09 12:55:00 +0000826 perm->expiry.sec += PJ_TURN_PERM_TIMEOUT;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000827
Benny Prijonoe7226852008-04-13 21:48:44 +0000828 /* Register to hash table (only the address part!) */
829 pj_hash_set(alloc->pool, alloc->peer_table,
830 pj_sockaddr_get_addr(&perm->hkey.peer_addr),
831 pj_sockaddr_get_addr_len(&perm->hkey.peer_addr), 0, perm);
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000832
Benny Prijonob05aafc2008-03-08 00:54:04 +0000833 return perm;
834}
835
836/* Check if a permission isn't expired. Return NULL if expired. */
Benny Prijono708725a2008-03-09 12:55:00 +0000837static pj_turn_permission *check_permission_expiry(pj_turn_permission *perm)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000838{
Benny Prijono708725a2008-03-09 12:55:00 +0000839 pj_turn_allocation *alloc = perm->allocation;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000840 pj_time_val now;
841
842 pj_gettimeofday(&now);
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000843 if (PJ_TIME_VAL_GT(perm->expiry, now)) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000844 /* Permission has not expired */
845 return perm;
846 }
847
848 /* Remove from permission hash table */
Benny Prijonoe7226852008-04-13 21:48:44 +0000849 pj_hash_set(NULL, alloc->peer_table,
850 pj_sockaddr_get_addr(&perm->hkey.peer_addr),
851 pj_sockaddr_get_addr_len(&perm->hkey.peer_addr), 0, NULL);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000852
853 /* Remove from channel hash table, if assigned a channel number */
Benny Prijono708725a2008-03-09 12:55:00 +0000854 if (perm->channel != PJ_TURN_INVALID_CHANNEL) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000855 pj_hash_set(NULL, alloc->ch_table, &perm->channel,
856 sizeof(perm->channel), 0, NULL);
857 }
858
859 return NULL;
860}
861
862/* Lookup permission in hash table by the peer address */
Benny Prijono708725a2008-03-09 12:55:00 +0000863static pj_turn_permission*
864lookup_permission_by_addr(pj_turn_allocation *alloc,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000865 const pj_sockaddr_t *peer_addr,
866 unsigned addr_len)
867{
Benny Prijono708725a2008-03-09 12:55:00 +0000868 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000869
Benny Prijonoe7226852008-04-13 21:48:44 +0000870 PJ_UNUSED_ARG(addr_len);
871
Benny Prijonob05aafc2008-03-08 00:54:04 +0000872 /* Lookup in peer hash table */
Benny Prijonoe7226852008-04-13 21:48:44 +0000873 perm = (pj_turn_permission*)
874 pj_hash_get(alloc->peer_table,
875 pj_sockaddr_get_addr(peer_addr),
876 pj_sockaddr_get_addr_len(peer_addr),
877 NULL);
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000878 return perm ? check_permission_expiry(perm) : NULL;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000879}
880
881/* Lookup permission in hash table by the channel number */
Benny Prijono708725a2008-03-09 12:55:00 +0000882static pj_turn_permission*
883lookup_permission_by_chnum(pj_turn_allocation *alloc,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000884 unsigned chnum)
885{
886 pj_uint16_t chnum16 = (pj_uint16_t)chnum;
Benny Prijono708725a2008-03-09 12:55:00 +0000887 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000888
889 /* Lookup in peer hash table */
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000890 perm = (pj_turn_permission*) pj_hash_get(alloc->ch_table, &chnum16,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000891 sizeof(chnum16), NULL);
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000892 return perm ? check_permission_expiry(perm) : NULL;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000893}
894
895/* Update permission because of data from client to peer.
896 * Return PJ_TRUE is permission is found.
897 */
Benny Prijono708725a2008-03-09 12:55:00 +0000898static pj_bool_t refresh_permission(pj_turn_permission *perm)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000899{
900 pj_gettimeofday(&perm->expiry);
Benny Prijono708725a2008-03-09 12:55:00 +0000901 if (perm->channel == PJ_TURN_INVALID_CHANNEL)
902 perm->expiry.sec += PJ_TURN_PERM_TIMEOUT;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000903 else
Benny Prijono708725a2008-03-09 12:55:00 +0000904 perm->expiry.sec += PJ_TURN_CHANNEL_TIMEOUT;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000905 return PJ_TRUE;
906}
907
908/*
Benny Prijono708725a2008-03-09 12:55:00 +0000909 * Handle incoming packet from client. This would have been called by
910 * server upon receiving packet from a listener.
Benny Prijonob05aafc2008-03-08 00:54:04 +0000911 */
Benny Prijono708725a2008-03-09 12:55:00 +0000912PJ_DEF(void) pj_turn_allocation_on_rx_client_pkt(pj_turn_allocation *alloc,
913 pj_turn_pkt *pkt)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000914{
915 pj_bool_t is_stun;
916 pj_status_t status;
917
Benny Prijono708725a2008-03-09 12:55:00 +0000918 /* Lock this allocation */
919 pj_lock_acquire(alloc->lock);
920
Benny Prijonob05aafc2008-03-08 00:54:04 +0000921 /* Quickly check if this is STUN message */
922 is_stun = ((*((pj_uint8_t*)pkt->pkt) & 0xC0) == 0);
923
924 if (is_stun) {
925 /*
926 * This could be an incoming STUN requests or indications.
927 * Pass this through to the STUN session, which will call
928 * our stun_on_rx_request() or stun_on_rx_indication()
929 * callbacks.
Benny Prijonoe7226852008-04-13 21:48:44 +0000930 *
931 * Note: currently it is necessary to specify the
932 * PJ_STUN_NO_FINGERPRINT_CHECK otherwise the FINGERPRINT
933 * attribute inside STUN Send Indication message will mess up
934 * with fingerprint checking.
Benny Prijonob05aafc2008-03-08 00:54:04 +0000935 */
Benny Prijonoe7226852008-04-13 21:48:44 +0000936 unsigned options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000937 unsigned parsed_len = 0;
938
939 if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000940 options |= PJ_STUN_IS_DATAGRAM;
941
942 status = pj_stun_session_on_rx_pkt(alloc->sess, pkt->pkt, pkt->len,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000943 options, NULL, &parsed_len,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000944 &pkt->src.clt_addr,
945 pkt->src_addr_len);
Benny Prijono879ad1a2008-04-09 09:38:12 +0000946
947 if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) {
948 pkt->len = 0;
949 } else if (parsed_len > 0) {
950 if (parsed_len == pkt->len) {
951 pkt->len = 0;
952 } else {
953 pj_memmove(pkt->pkt, pkt->pkt+parsed_len,
954 pkt->len - parsed_len);
955 pkt->len -= parsed_len;
956 }
957 }
958
Benny Prijonob05aafc2008-03-08 00:54:04 +0000959 if (status != PJ_SUCCESS) {
960 alloc_err(alloc, "Error handling STUN packet", status);
Benny Prijono708725a2008-03-09 12:55:00 +0000961 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000962 }
963
964 } else {
965 /*
966 * This is not a STUN packet, must be ChannelData packet.
967 */
Benny Prijono3fd9fc52008-03-09 23:52:48 +0000968 pj_turn_channel_data *cd = (pj_turn_channel_data*)pkt->pkt;
Benny Prijono708725a2008-03-09 12:55:00 +0000969 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000970 pj_ssize_t len;
971
Benny Prijono3fd9fc52008-03-09 23:52:48 +0000972 pj_assert(sizeof(*cd)==4);
973
Benny Prijonob05aafc2008-03-08 00:54:04 +0000974 /* For UDP check the packet length */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000975 if (alloc->transport->listener->tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000976 if (pkt->len < pj_ntohs(cd->length)+sizeof(*cd)) {
977 PJ_LOG(4,(alloc->obj_name,
978 "ChannelData from %s discarded: UDP size error",
979 alloc->info));
Benny Prijono708725a2008-03-09 12:55:00 +0000980 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000981 }
982 } else {
983 pj_assert(!"Unsupported transport");
Benny Prijono708725a2008-03-09 12:55:00 +0000984 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000985 }
986
987 perm = lookup_permission_by_chnum(alloc, pj_ntohs(cd->ch_number));
988 if (!perm) {
989 /* Discard */
990 PJ_LOG(4,(alloc->obj_name,
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000991 "ChannelData from %s discarded: ch#0x%x not found",
992 alloc->info, pj_ntohs(cd->ch_number)));
Benny Prijono708725a2008-03-09 12:55:00 +0000993 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000994 }
995
996 /* Relay the data */
997 len = pj_ntohs(cd->length);
998 pj_sock_sendto(alloc->relay.tp.sock, cd+1, &len, 0,
999 &perm->hkey.peer_addr,
1000 pj_sockaddr_get_len(&perm->hkey.peer_addr));
1001
1002 /* Refresh permission */
1003 refresh_permission(perm);
1004 }
Benny Prijono708725a2008-03-09 12:55:00 +00001005
1006on_return:
1007 /* Release lock */
1008 pj_lock_release(alloc->lock);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001009}
1010
Benny Prijono708725a2008-03-09 12:55:00 +00001011
Benny Prijonob05aafc2008-03-08 00:54:04 +00001012/*
1013 * Handle incoming packet from peer. This function is called by
1014 * on_rx_from_peer().
1015 */
Benny Prijono708725a2008-03-09 12:55:00 +00001016static void handle_peer_pkt(pj_turn_allocation *alloc,
1017 pj_turn_relay_res *rel,
1018 char *pkt, pj_size_t len,
1019 const pj_sockaddr *src_addr)
Benny Prijonob05aafc2008-03-08 00:54:04 +00001020{
Benny Prijono708725a2008-03-09 12:55:00 +00001021 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001022
1023 /* Lookup permission */
1024 perm = lookup_permission_by_addr(alloc, src_addr,
1025 pj_sockaddr_get_len(src_addr));
1026 if (perm == NULL) {
1027 /* No permission, discard data */
1028 return;
1029 }
1030
1031 /* Send Data Indication or ChannelData, depends on whether
1032 * this permission is attached to a channel number.
1033 */
Benny Prijono708725a2008-03-09 12:55:00 +00001034 if (perm->channel != PJ_TURN_INVALID_CHANNEL) {
Benny Prijonob05aafc2008-03-08 00:54:04 +00001035 /* Send ChannelData */
Benny Prijono3fd9fc52008-03-09 23:52:48 +00001036 pj_turn_channel_data *cd = (pj_turn_channel_data*)rel->tp.tx_pkt;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001037
Benny Prijono708725a2008-03-09 12:55:00 +00001038 if (len > PJ_TURN_MAX_PKT_LEN) {
Benny Prijonob05aafc2008-03-08 00:54:04 +00001039 char peer_addr[80];
1040 pj_sockaddr_print(src_addr, peer_addr, sizeof(peer_addr), 3);
Benny Prijono708725a2008-03-09 12:55:00 +00001041 PJ_LOG(4,(alloc->obj_name, "Client %s: discarded data from %s "
Benny Prijonob05aafc2008-03-08 00:54:04 +00001042 "because it's too long (%d bytes)",
1043 alloc->info, peer_addr, len));
1044 return;
1045 }
1046
1047 /* Init header */
1048 cd->ch_number = pj_htons(perm->channel);
1049 cd->length = pj_htons((pj_uint16_t)len);
1050
1051 /* Copy data */
Benny Prijono9e6d60a2008-03-20 16:32:06 +00001052 pj_memcpy(rel->tp.tx_pkt+sizeof(pj_turn_channel_data), pkt, len);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001053
1054 /* Send to client */
Benny Prijono879ad1a2008-04-09 09:38:12 +00001055 alloc->transport->sendto(alloc->transport, rel->tp.tx_pkt,
1056 len+sizeof(pj_turn_channel_data), 0,
1057 &alloc->hkey.clt_addr,
1058 pj_sockaddr_get_len(&alloc->hkey.clt_addr));
Benny Prijonob05aafc2008-03-08 00:54:04 +00001059 } else {
1060 /* Send Data Indication */
1061 pj_stun_tx_data *tdata;
1062 pj_status_t status;
1063
1064 status = pj_stun_session_create_ind(alloc->sess,
1065 PJ_STUN_DATA_INDICATION, &tdata);
1066 if (status != PJ_SUCCESS) {
1067 alloc_err(alloc, "Error creating Data indication", status);
1068 return;
1069 }
Benny Prijono9e6d60a2008-03-20 16:32:06 +00001070
1071 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
1072 PJ_STUN_ATTR_PEER_ADDR, PJ_TRUE,
1073 src_addr, pj_sockaddr_get_len(src_addr));
1074 pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg,
1075 PJ_STUN_ATTR_DATA,
1076 (const pj_uint8_t*)pkt, len);
1077
Benny Prijono879ad1a2008-04-09 09:38:12 +00001078 pj_stun_session_send_msg(alloc->sess, NULL, PJ_FALSE,
1079 PJ_FALSE, &alloc->hkey.clt_addr,
Benny Prijono9e6d60a2008-03-20 16:32:06 +00001080 pj_sockaddr_get_len(&alloc->hkey.clt_addr),
1081 tdata);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001082 }
1083}
1084
1085/*
1086 * ioqueue notification on RX packets from the relay socket.
1087 */
1088static void on_rx_from_peer(pj_ioqueue_key_t *key,
1089 pj_ioqueue_op_key_t *op_key,
1090 pj_ssize_t bytes_read)
1091{
Benny Prijono708725a2008-03-09 12:55:00 +00001092 pj_turn_relay_res *rel;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001093 pj_status_t status;
1094
Benny Prijono708725a2008-03-09 12:55:00 +00001095 rel = (pj_turn_relay_res*) pj_ioqueue_get_user_data(key);
1096
1097 /* Lock the allocation */
1098 pj_lock_acquire(rel->allocation->lock);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001099
1100 do {
1101 if (bytes_read > 0) {
Benny Prijono708725a2008-03-09 12:55:00 +00001102 handle_peer_pkt(rel->allocation, rel, rel->tp.rx_pkt,
1103 bytes_read, &rel->tp.src_addr);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001104 }
1105
1106 /* Read next packet */
1107 bytes_read = sizeof(rel->tp.rx_pkt);
1108 rel->tp.src_addr_len = sizeof(rel->tp.src_addr);
1109 status = pj_ioqueue_recvfrom(key, op_key,
1110 rel->tp.rx_pkt, &bytes_read, 0,
1111 &rel->tp.src_addr,
1112 &rel->tp.src_addr_len);
1113
1114 if (status != PJ_EPENDING && status != PJ_SUCCESS)
1115 bytes_read = -status;
1116
1117 } while (status != PJ_EPENDING && status != PJ_ECANCELLED);
1118
Benny Prijono708725a2008-03-09 12:55:00 +00001119 /* Release allocation lock */
1120 pj_lock_release(rel->allocation->lock);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001121}
1122
1123/*
1124 * Callback notification from STUN session when it wants to send
1125 * a STUN message towards the client.
1126 */
1127static pj_status_t stun_on_send_msg(pj_stun_session *sess,
Benny Prijono879ad1a2008-04-09 09:38:12 +00001128 void *token,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001129 const void *pkt,
1130 pj_size_t pkt_size,
1131 const pj_sockaddr_t *dst_addr,
1132 unsigned addr_len)
1133{
Benny Prijono708725a2008-03-09 12:55:00 +00001134 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001135
Benny Prijono879ad1a2008-04-09 09:38:12 +00001136 PJ_UNUSED_ARG(token);
1137
Benny Prijono708725a2008-03-09 12:55:00 +00001138 alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001139
Benny Prijono879ad1a2008-04-09 09:38:12 +00001140 return alloc->transport->sendto(alloc->transport, pkt, pkt_size, 0,
1141 dst_addr, addr_len);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001142}
1143
1144/*
1145 * Callback notification from STUN session when it receives STUN
1146 * requests. This callback was trigger by STUN incoming message
Benny Prijono708725a2008-03-09 12:55:00 +00001147 * processing in pj_turn_allocation_on_rx_client_pkt().
Benny Prijonob05aafc2008-03-08 00:54:04 +00001148 */
1149static pj_status_t stun_on_rx_request(pj_stun_session *sess,
1150 const pj_uint8_t *pkt,
1151 unsigned pkt_len,
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001152 const pj_stun_rx_data *rdata,
Benny Prijono879ad1a2008-04-09 09:38:12 +00001153 void *token,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001154 const pj_sockaddr_t *src_addr,
1155 unsigned src_addr_len)
1156{
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001157 const pj_stun_msg *msg = rdata->msg;
Benny Prijono708725a2008-03-09 12:55:00 +00001158 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001159
Benny Prijono708725a2008-03-09 12:55:00 +00001160 PJ_UNUSED_ARG(pkt);
1161 PJ_UNUSED_ARG(pkt_len);
Benny Prijono879ad1a2008-04-09 09:38:12 +00001162 PJ_UNUSED_ARG(token);
Benny Prijono708725a2008-03-09 12:55:00 +00001163 PJ_UNUSED_ARG(src_addr);
1164 PJ_UNUSED_ARG(src_addr_len);
1165
1166 alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001167
1168 /* Refuse to serve any request if we've been shutdown */
1169 if (alloc->relay.lifetime == 0) {
Benny Prijono708725a2008-03-09 12:55:00 +00001170 /* Reject with 437 if we're shutting down */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001171 send_reply_err(alloc, rdata, PJ_TRUE,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001172 PJ_STUN_SC_ALLOCATION_MISMATCH, NULL);
1173 return PJ_SUCCESS;
1174 }
1175
1176 if (msg->hdr.type == PJ_STUN_REFRESH_REQUEST) {
1177 /*
1178 * Handle REFRESH request
1179 */
1180 pj_stun_lifetime_attr *lifetime;
1181 pj_stun_bandwidth_attr *bandwidth;
1182
1183 /* Get LIFETIME attribute */
1184 lifetime = (pj_stun_lifetime_attr*)
1185 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0);
1186
1187 /* Get BANDWIDTH attribute */
1188 bandwidth = (pj_stun_bandwidth_attr*)
1189 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_BANDWIDTH, 0);
1190
1191 if (lifetime && lifetime->value==0) {
1192 /*
1193 * This is deallocation request.
1194 */
1195 alloc->relay.lifetime = 0;
1196
1197 /* Respond first */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001198 send_reply_ok(alloc, rdata);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001199
1200 /* Shutdown allocation */
1201 PJ_LOG(4,(alloc->obj_name,
1202 "Client %s request to dealloc, shutting down",
1203 alloc->info));
1204
1205 alloc_shutdown(alloc);
1206
1207 } else {
1208 /*
1209 * This is a refresh request.
1210 */
1211
1212 /* Update lifetime */
1213 if (lifetime) {
1214 alloc->relay.lifetime = lifetime->value;
1215 }
1216
1217 /* Update bandwidth */
1218 // TODO:
1219
1220 /* Update expiration timer */
1221 resched_timeout(alloc);
1222
1223 /* Send reply */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001224 send_reply_ok(alloc, rdata);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001225 }
1226
1227 } else if (msg->hdr.type == PJ_STUN_CHANNEL_BIND_REQUEST) {
1228 /*
1229 * ChannelBind request.
1230 */
1231 pj_stun_channel_number_attr *ch_attr;
1232 pj_stun_peer_addr_attr *peer_attr;
Benny Prijono708725a2008-03-09 12:55:00 +00001233 pj_turn_permission *p1, *p2;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001234
1235 ch_attr = (pj_stun_channel_number_attr*)
1236 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CHANNEL_NUMBER, 0);
1237 peer_attr = (pj_stun_peer_addr_attr*)
1238 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
1239
1240 if (!ch_attr || !peer_attr) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001241 send_reply_err(alloc, rdata, PJ_TRUE,
1242 PJ_STUN_SC_BAD_REQUEST, NULL);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001243 return PJ_SUCCESS;
1244 }
1245
1246 /* Find permission with the channel number */
1247 p1 = lookup_permission_by_chnum(alloc, PJ_STUN_GET_CH_NB(ch_attr->value));
1248
1249 /* If permission is found, this is supposed to be a channel bind
1250 * refresh. Make sure it's for the same peer.
1251 */
1252 if (p1) {
1253 if (pj_sockaddr_cmp(&p1->hkey.peer_addr, &peer_attr->sockaddr)) {
1254 /* Address mismatch. Send 400 */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001255 send_reply_err(alloc, rdata, PJ_TRUE,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001256 PJ_STUN_SC_BAD_REQUEST,
1257 "Peer address mismatch");
1258 return PJ_SUCCESS;
1259 }
1260
1261 /* Refresh permission */
1262 refresh_permission(p1);
1263
Benny Prijono9e6d60a2008-03-20 16:32:06 +00001264 /* Send response */
1265 send_reply_ok(alloc, rdata);
1266
Benny Prijonob05aafc2008-03-08 00:54:04 +00001267 /* Done */
1268 return PJ_SUCCESS;
1269 }
1270
1271 /* If permission is not found, create a new one. Make sure the peer
1272 * has not alreadyy assigned with a channel number.
1273 */
1274 p2 = lookup_permission_by_addr(alloc, &peer_attr->sockaddr,
1275 pj_sockaddr_get_len(&peer_attr->sockaddr));
Benny Prijono708725a2008-03-09 12:55:00 +00001276 if (p2 && p2->channel != PJ_TURN_INVALID_CHANNEL) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001277 send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001278 "Peer address already assigned a channel number");
1279 return PJ_SUCCESS;
1280 }
1281
1282 /* Create permission if it doesn't exist */
1283 if (!p2) {
1284 p2 = create_permission(alloc, &peer_attr->sockaddr,
1285 pj_sockaddr_get_len(&peer_attr->sockaddr));
1286 if (!p2)
1287 return PJ_SUCCESS;
1288 }
1289
1290 /* Assign channel number to permission */
1291 p2->channel = PJ_STUN_GET_CH_NB(ch_attr->value);
1292
Benny Prijono9e6d60a2008-03-20 16:32:06 +00001293 /* Register to hash table */
1294 pj_assert(sizeof(p2->channel==2));
1295 pj_hash_set(alloc->pool, alloc->ch_table, &p2->channel,
1296 sizeof(p2->channel), 0, p2);
1297
Benny Prijonob05aafc2008-03-08 00:54:04 +00001298 /* Update */
1299 refresh_permission(p2);
1300
1301 /* Reply */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001302 send_reply_ok(alloc, rdata);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001303
1304 return PJ_SUCCESS;
1305
1306 } else if (msg->hdr.type == PJ_STUN_ALLOCATE_REQUEST) {
1307
1308 /* Respond with 437 (section 6.3 turn-07) */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001309 send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH,
1310 NULL);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001311
1312 } else {
1313
1314 /* Respond with Bad Request? */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001315 send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001316
1317 }
1318
1319 return PJ_SUCCESS;
1320}
1321
1322/*
1323 * Callback notification from STUN session when it receives STUN
1324 * indications. This callback was trigger by STUN incoming message
Benny Prijono708725a2008-03-09 12:55:00 +00001325 * processing in pj_turn_allocation_on_rx_client_pkt().
Benny Prijonob05aafc2008-03-08 00:54:04 +00001326 */
1327static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
1328 const pj_uint8_t *pkt,
1329 unsigned pkt_len,
1330 const pj_stun_msg *msg,
Benny Prijono879ad1a2008-04-09 09:38:12 +00001331 void *token,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001332 const pj_sockaddr_t *src_addr,
1333 unsigned src_addr_len)
1334{
1335 pj_stun_peer_addr_attr *peer_attr;
1336 pj_stun_data_attr *data_attr;
Benny Prijono708725a2008-03-09 12:55:00 +00001337 pj_turn_allocation *alloc;
1338 pj_turn_permission *perm;
Benny Prijono879ad1a2008-04-09 09:38:12 +00001339 pj_ssize_t len;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001340
Benny Prijono708725a2008-03-09 12:55:00 +00001341 PJ_UNUSED_ARG(pkt);
1342 PJ_UNUSED_ARG(pkt_len);
Benny Prijono879ad1a2008-04-09 09:38:12 +00001343 PJ_UNUSED_ARG(token);
Benny Prijono708725a2008-03-09 12:55:00 +00001344 PJ_UNUSED_ARG(src_addr);
1345 PJ_UNUSED_ARG(src_addr_len);
1346
1347 alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001348
1349 /* Only expect Send Indication */
1350 if (msg->hdr.type != PJ_STUN_SEND_INDICATION) {
1351 /* Ignore */
1352 return PJ_SUCCESS;
1353 }
1354
1355 /* Get PEER-ADDRESS attribute */
1356 peer_attr = (pj_stun_peer_addr_attr*)
1357 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
1358
1359 /* MUST have PEER-ADDRESS attribute */
1360 if (!peer_attr)
1361 return PJ_SUCCESS;
1362
1363 /* Get DATA attribute */
1364 data_attr = (pj_stun_data_attr*)
1365 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_DATA, 0);
1366
1367 /* Create/update/refresh the permission */
1368 perm = lookup_permission_by_addr(alloc, &peer_attr->sockaddr,
1369 pj_sockaddr_get_len(&peer_attr->sockaddr));
1370 if (perm == NULL) {
1371 perm = create_permission(alloc, &peer_attr->sockaddr,
1372 pj_sockaddr_get_len(&peer_attr->sockaddr));
1373 }
1374 refresh_permission(perm);
1375
1376 /* Return if we don't have data */
1377 if (data_attr == NULL)
1378 return PJ_SUCCESS;
1379
Benny Prijono879ad1a2008-04-09 09:38:12 +00001380 /* Relay the data to peer */
1381 len = data_attr->length;
1382 pj_sock_sendto(alloc->relay.tp.sock, data_attr->data,
1383 &len, 0, &peer_attr->sockaddr,
1384 pj_sockaddr_get_len(&peer_attr->sockaddr));
Benny Prijonob05aafc2008-03-08 00:54:04 +00001385
1386 return PJ_SUCCESS;
1387}
1388
1389