blob: 6fb606aaa9769760eac35e8f868635b6d87c2e31 [file] [log] [blame]
Benny Prijonob05aafc2008-03-08 00:54:04 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijonob05aafc2008-03-08 00:54:04 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include "turn.h"
Benny Prijono708725a2008-03-09 12:55:00 +000021#include "auth.h"
22
Benny Prijonob05aafc2008-03-08 00:54:04 +000023
24#define THIS_FILE "allocation.c"
25
26
27enum {
28 TIMER_ID_NONE,
29 TIMER_ID_TIMEOUT,
30 TIMER_ID_DESTROY
31};
32
33#define DESTROY_DELAY {0, 500}
34#define PEER_TABLE_SIZE 32
35
Benny Prijono708725a2008-03-09 12:55:00 +000036#define MAX_CLIENT_BANDWIDTH 128 /* In Kbps */
37#define DEFA_CLIENT_BANDWIDTH 64
38
39#define MIN_LIFETIME 30
40#define MAX_LIFETIME 600
41#define DEF_LIFETIME 300
42
43
Benny Prijono708725a2008-03-09 12:55:00 +000044/* Parsed Allocation request. */
45typedef struct alloc_request
46{
47 unsigned tp_type; /* Requested transport */
48 char addr[PJ_INET6_ADDRSTRLEN]; /* Requested IP */
49 unsigned bandwidth; /* Requested bandwidth */
50 unsigned lifetime; /* Lifetime. */
51 unsigned rpp_bits; /* A bits */
52 unsigned rpp_port; /* Requested port */
53} alloc_request;
54
55
56
Benny Prijonob05aafc2008-03-08 00:54:04 +000057/* Prototypes */
Benny Prijono708725a2008-03-09 12:55:00 +000058static void destroy_allocation(pj_turn_allocation *alloc);
59static pj_status_t create_relay(pj_turn_srv *srv,
60 pj_turn_allocation *alloc,
61 const pj_stun_msg *msg,
62 const alloc_request *req,
63 pj_turn_relay_res *relay);
64static void destroy_relay(pj_turn_relay_res *relay);
Benny Prijonob05aafc2008-03-08 00:54:04 +000065static void on_rx_from_peer(pj_ioqueue_key_t *key,
66 pj_ioqueue_op_key_t *op_key,
67 pj_ssize_t bytes_read);
Benny Prijonob05aafc2008-03-08 00:54:04 +000068static pj_status_t stun_on_send_msg(pj_stun_session *sess,
Benny Prijono879ad1a2008-04-09 09:38:12 +000069 void *token,
Benny Prijonob05aafc2008-03-08 00:54:04 +000070 const void *pkt,
71 pj_size_t pkt_size,
72 const pj_sockaddr_t *dst_addr,
73 unsigned addr_len);
74static pj_status_t stun_on_rx_request(pj_stun_session *sess,
75 const pj_uint8_t *pkt,
76 unsigned pkt_len,
Benny Prijonoa5d214f2008-03-19 23:00:30 +000077 const pj_stun_rx_data *rdata,
Benny Prijono879ad1a2008-04-09 09:38:12 +000078 void *token,
Benny Prijonob05aafc2008-03-08 00:54:04 +000079 const pj_sockaddr_t *src_addr,
80 unsigned src_addr_len);
81static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
82 const pj_uint8_t *pkt,
83 unsigned pkt_len,
84 const pj_stun_msg *msg,
Benny Prijono879ad1a2008-04-09 09:38:12 +000085 void *token,
Benny Prijonob05aafc2008-03-08 00:54:04 +000086 const pj_sockaddr_t *src_addr,
87 unsigned src_addr_len);
88
89/* Log allocation error */
Benny Prijono708725a2008-03-09 12:55:00 +000090static void alloc_err(pj_turn_allocation *alloc, const char *title,
Benny Prijonob05aafc2008-03-08 00:54:04 +000091 pj_status_t status)
92{
93 char errmsg[PJ_ERR_MSG_SIZE];
94
95 pj_strerror(status, errmsg, sizeof(errmsg));
96 PJ_LOG(4,(alloc->obj_name, "%s for client %s: %s",
97 title, alloc->info, errmsg));
98}
99
Benny Prijono708725a2008-03-09 12:55:00 +0000100
101/* Parse ALLOCATE request */
102static pj_status_t parse_allocate_req(alloc_request *cfg,
103 pj_stun_session *sess,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000104 const pj_stun_rx_data *rdata,
Benny Prijono708725a2008-03-09 12:55:00 +0000105 const pj_sockaddr_t *src_addr,
106 unsigned src_addr_len)
107{
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000108 const pj_stun_msg *req = rdata->msg;
Benny Prijono708725a2008-03-09 12:55:00 +0000109 pj_stun_bandwidth_attr *attr_bw;
110 pj_stun_req_transport_attr *attr_req_tp;
111 pj_stun_res_token_attr *attr_res_token;
112 pj_stun_req_props_attr *attr_rpp;
113 pj_stun_lifetime_attr *attr_lifetime;
114
115 pj_bzero(cfg, sizeof(*cfg));
116
117 /* Get BANDWIDTH attribute, if any. */
118 attr_bw = (pj_stun_uint_attr*)
119 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_BANDWIDTH, 0);
120 if (attr_bw) {
121 cfg->bandwidth = attr_bw->value;
122 } else {
123 cfg->bandwidth = DEFA_CLIENT_BANDWIDTH;
124 }
125
126 /* Check if we can satisfy the bandwidth */
127 if (cfg->bandwidth > MAX_CLIENT_BANDWIDTH) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000128 pj_stun_session_respond(sess, rdata,
129 PJ_STUN_SC_ALLOCATION_QUOTA_REACHED,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000130 "Invalid bandwidth", NULL, PJ_TRUE,
Benny Prijono708725a2008-03-09 12:55:00 +0000131 src_addr, src_addr_len);
132 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_ALLOCATION_QUOTA_REACHED);
133 }
134
135 /* MUST have REQUESTED-TRANSPORT attribute */
136 attr_req_tp = (pj_stun_uint_attr*)
137 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_TRANSPORT, 0);
138 if (attr_req_tp == NULL) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000139 pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST,
Benny Prijono708725a2008-03-09 12:55:00 +0000140 "Missing REQUESTED-TRANSPORT attribute",
Benny Prijono879ad1a2008-04-09 09:38:12 +0000141 NULL, PJ_TRUE, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000142 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
143 }
144
145 cfg->tp_type = PJ_STUN_GET_RT_PROTO(attr_req_tp->value);
146
147 /* Can only support UDP for now */
148 if (cfg->tp_type != PJ_TURN_TP_UDP) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000149 pj_stun_session_respond(sess, rdata, PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000150 NULL, NULL, PJ_TRUE, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000151 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO);
152 }
153
154 /* Get RESERVATION-TOKEN attribute, if any */
155 attr_res_token = (pj_stun_res_token_attr*)
156 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_RESERVATION_TOKEN,
157 0);
158 if (attr_res_token) {
159 /* We don't support RESERVATION-TOKEN for now */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000160 pj_stun_session_respond(sess, rdata,
Benny Prijono708725a2008-03-09 12:55:00 +0000161 PJ_STUN_SC_BAD_REQUEST,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000162 "RESERVATION-TOKEN is not supported", NULL,
163 PJ_TRUE, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000164 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
165 }
166
167 /* Get REQUESTED-PROPS attribute, if any */
168 attr_rpp = (pj_stun_req_props_attr*)
169 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_PROPS, 0);
170 if (attr_rpp) {
171 /* We don't support REQUESTED-PROPS for now */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000172 pj_stun_session_respond(sess, rdata,
Benny Prijono708725a2008-03-09 12:55:00 +0000173 PJ_STUN_SC_BAD_REQUEST,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000174 "REQUESTED-PROPS is not supported",
175 NULL, PJ_TRUE, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000176 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
177 }
178
179 /* Get LIFETIME attribute */
180 attr_lifetime = (pj_stun_uint_attr*)
181 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0);
182 if (attr_lifetime) {
183 cfg->lifetime = attr_lifetime->value;
184 if (cfg->lifetime < MIN_LIFETIME) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000185 pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000186 "LIFETIME too short", NULL,
187 PJ_TRUE, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000188 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
189 }
190 if (cfg->lifetime > MAX_LIFETIME)
191 cfg->lifetime = MAX_LIFETIME;
192 } else {
193 cfg->lifetime = DEF_LIFETIME;
194 }
195
196 return PJ_SUCCESS;
197}
198
199
200/* Respond to ALLOCATE request */
201static pj_status_t send_allocate_response(pj_turn_allocation *alloc,
202 pj_stun_session *srv_sess,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000203 pj_turn_transport *transport,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000204 const pj_stun_rx_data *rdata)
Benny Prijono708725a2008-03-09 12:55:00 +0000205{
206 pj_stun_tx_data *tdata;
207 pj_status_t status;
208
209 /* Respond the original ALLOCATE request */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000210 status = pj_stun_session_create_res(srv_sess, rdata, 0, NULL, &tdata);
Benny Prijono708725a2008-03-09 12:55:00 +0000211 if (status != PJ_SUCCESS)
212 return status;
213
214 /* Add RELAYED-ADDRESS attribute */
215 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
Benny Prijonoefa014b2008-08-22 17:46:33 +0000216 PJ_STUN_ATTR_RELAYED_ADDR, PJ_TRUE,
Benny Prijono708725a2008-03-09 12:55:00 +0000217 &alloc->relay.hkey.addr,
218 pj_sockaddr_get_len(&alloc->relay.hkey.addr));
219
220 /* Add LIFETIME. */
221 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
222 PJ_STUN_ATTR_LIFETIME,
223 (unsigned)alloc->relay.lifetime);
224
225 /* Add BANDWIDTH */
226 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
227 PJ_STUN_ATTR_BANDWIDTH,
228 alloc->bandwidth);
229
230 /* Add RESERVATION-TOKEN */
231 PJ_TODO(ADD_RESERVATION_TOKEN);
232
233 /* Add XOR-MAPPED-ADDRESS */
234 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
235 PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE,
236 &alloc->hkey.clt_addr,
237 pj_sockaddr_get_len(&alloc->hkey.clt_addr));
238
239 /* Send the response */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000240 return pj_stun_session_send_msg(srv_sess, transport, PJ_TRUE,
241 PJ_FALSE, &alloc->hkey.clt_addr,
Benny Prijono708725a2008-03-09 12:55:00 +0000242 pj_sockaddr_get_len(&alloc->hkey.clt_addr),
243 tdata);
244}
245
246
247/*
248 * Init credential for the allocation. We use static credential, meaning that
249 * the user's password must not change during allocation.
250 */
251static pj_status_t init_cred(pj_turn_allocation *alloc, const pj_stun_msg *req)
252{
253 const pj_stun_username_attr *user;
254 const pj_stun_realm_attr *realm;
255 const pj_stun_nonce_attr *nonce;
256 pj_status_t status;
257
258 realm = (const pj_stun_realm_attr*)
259 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REALM, 0);
260 PJ_ASSERT_RETURN(realm != NULL, PJ_EBUG);
261
262 user = (const pj_stun_username_attr*)
263 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0);
264 PJ_ASSERT_RETURN(user != NULL, PJ_EBUG);
265
266 nonce = (const pj_stun_nonce_attr*)
267 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_NONCE, 0);
268 PJ_ASSERT_RETURN(nonce != NULL, PJ_EBUG);
269
270 /* Lookup the password */
271 status = pj_turn_get_password(NULL, NULL, &realm->value,
272 &user->value, alloc->pool,
273 &alloc->cred.data.static_cred.data_type,
274 &alloc->cred.data.static_cred.data);
275 if (status != PJ_SUCCESS)
276 return status;
277
278 /* Save credential */
279 alloc->cred.type = PJ_STUN_AUTH_CRED_STATIC;
280 pj_strdup(alloc->pool, &alloc->cred.data.static_cred.realm, &realm->value);
281 pj_strdup(alloc->pool, &alloc->cred.data.static_cred.username, &user->value);
282 pj_strdup(alloc->pool, &alloc->cred.data.static_cred.nonce, &nonce->value);
283
284 return PJ_SUCCESS;
285}
286
287
Benny Prijonob05aafc2008-03-08 00:54:04 +0000288/*
289 * Create new allocation.
290 */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000291PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport,
Benny Prijono708725a2008-03-09 12:55:00 +0000292 const pj_sockaddr_t *src_addr,
293 unsigned src_addr_len,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000294 const pj_stun_rx_data *rdata,
Benny Prijono708725a2008-03-09 12:55:00 +0000295 pj_stun_session *srv_sess,
296 pj_turn_allocation **p_alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000297{
Benny Prijono879ad1a2008-04-09 09:38:12 +0000298 pj_turn_srv *srv = transport->listener->server;
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000299 const pj_stun_msg *msg = rdata->msg;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000300 pj_pool_t *pool;
Benny Prijono708725a2008-03-09 12:55:00 +0000301 alloc_request req;
302 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000303 pj_stun_session_cb sess_cb;
Benny Prijono708725a2008-03-09 12:55:00 +0000304 char str_tmp[80];
Benny Prijonob05aafc2008-03-08 00:54:04 +0000305 pj_status_t status;
306
Benny Prijono708725a2008-03-09 12:55:00 +0000307 /* Parse ALLOCATE request */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000308 status = parse_allocate_req(&req, srv_sess, rdata, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000309 if (status != PJ_SUCCESS)
310 return status;
311
Benny Prijonob05aafc2008-03-08 00:54:04 +0000312 pool = pj_pool_create(srv->core.pf, "alloc%p", 1000, 1000, NULL);
313
314 /* Init allocation structure */
Benny Prijono708725a2008-03-09 12:55:00 +0000315 alloc = PJ_POOL_ZALLOC_T(pool, pj_turn_allocation);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000316 alloc->pool = pool;
317 alloc->obj_name = pool->obj_name;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000318 alloc->relay.tp.sock = PJ_INVALID_SOCKET;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000319 alloc->server = transport->listener->server;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000320
Benny Prijono708725a2008-03-09 12:55:00 +0000321 alloc->bandwidth = req.bandwidth;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000322
Benny Prijono879ad1a2008-04-09 09:38:12 +0000323 /* Set transport */
324 alloc->transport = transport;
325 pj_turn_transport_add_ref(transport, alloc);
326
327 alloc->hkey.tp_type = transport->listener->tp_type;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000328 pj_memcpy(&alloc->hkey.clt_addr, src_addr, src_addr_len);
329
330 status = pj_lock_create_recursive_mutex(pool, alloc->obj_name,
331 &alloc->lock);
332 if (status != PJ_SUCCESS) {
Benny Prijono708725a2008-03-09 12:55:00 +0000333 goto on_error;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000334 }
335
336 /* Create peer hash table */
337 alloc->peer_table = pj_hash_create(pool, PEER_TABLE_SIZE);
338
339 /* Create channel hash table */
340 alloc->ch_table = pj_hash_create(pool, PEER_TABLE_SIZE);
341
342 /* Print info */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000343 pj_ansi_strcpy(alloc->info,
344 pj_turn_tp_type_name(transport->listener->tp_type));
Benny Prijonob05aafc2008-03-08 00:54:04 +0000345 alloc->info[3] = ':';
346 pj_sockaddr_print(src_addr, alloc->info+4, sizeof(alloc->info)-4, 3);
347
348 /* Create STUN session to handle STUN communication with client */
349 pj_bzero(&sess_cb, sizeof(sess_cb));
350 sess_cb.on_send_msg = &stun_on_send_msg;
351 sess_cb.on_rx_request = &stun_on_rx_request;
352 sess_cb.on_rx_indication = &stun_on_rx_indication;
353 status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name,
354 &sess_cb, PJ_FALSE, &alloc->sess);
355 if (status != PJ_SUCCESS) {
Benny Prijono708725a2008-03-09 12:55:00 +0000356 goto on_error;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000357 }
358
359 /* Attach to STUN session */
360 pj_stun_session_set_user_data(alloc->sess, alloc);
361
Benny Prijono708725a2008-03-09 12:55:00 +0000362 /* Init authentication credential */
363 status = init_cred(alloc, msg);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000364 if (status != PJ_SUCCESS) {
Benny Prijono708725a2008-03-09 12:55:00 +0000365 goto on_error;
366 }
367
368 /* Attach authentication credential to STUN session */
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000369 pj_stun_session_set_credential(alloc->sess, PJ_STUN_AUTH_LONG_TERM,
370 &alloc->cred);
Benny Prijono708725a2008-03-09 12:55:00 +0000371
372 /* Create the relay resource */
373 status = create_relay(srv, alloc, msg, &req, &alloc->relay);
374 if (status != PJ_SUCCESS) {
375 goto on_error;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000376 }
377
378 /* Register this allocation */
Benny Prijono708725a2008-03-09 12:55:00 +0000379 pj_turn_srv_register_allocation(srv, alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000380
Benny Prijono708725a2008-03-09 12:55:00 +0000381 /* Respond to ALLOCATE request */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000382 status = send_allocate_response(alloc, srv_sess, transport, rdata);
Benny Prijono708725a2008-03-09 12:55:00 +0000383 if (status != PJ_SUCCESS)
384 goto on_error;
385
386 /* Done */
387 pj_sockaddr_print(&alloc->relay.hkey.addr, str_tmp,
388 sizeof(str_tmp), 3);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000389 PJ_LOG(4,(alloc->obj_name, "Client %s created, relay addr=%s:%s",
Benny Prijono708725a2008-03-09 12:55:00 +0000390 alloc->info, pj_turn_tp_type_name(req.tp_type), str_tmp));
Benny Prijonob05aafc2008-03-08 00:54:04 +0000391
392 /* Success */
393 *p_alloc = alloc;
394 return PJ_SUCCESS;
Benny Prijono708725a2008-03-09 12:55:00 +0000395
396on_error:
397 /* Send reply to the ALLOCATE request */
398 pj_strerror(status, str_tmp, sizeof(str_tmp));
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000399 pj_stun_session_respond(srv_sess, rdata, PJ_STUN_SC_BAD_REQUEST, str_tmp,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000400 transport, PJ_TRUE, src_addr, src_addr_len);
Benny Prijono708725a2008-03-09 12:55:00 +0000401
402 /* Cleanup */
403 destroy_allocation(alloc);
404 return status;
405}
406
407
408/* Destroy relay resource */
409static void destroy_relay(pj_turn_relay_res *relay)
410{
411 if (relay->timer.id) {
Benny Prijono879ad1a2008-04-09 09:38:12 +0000412 pj_timer_heap_cancel(relay->allocation->server->core.timer_heap,
Benny Prijono708725a2008-03-09 12:55:00 +0000413 &relay->timer);
414 relay->timer.id = PJ_FALSE;
415 }
416
417 if (relay->tp.key) {
418 pj_ioqueue_unregister(relay->tp.key);
419 relay->tp.key = NULL;
420 relay->tp.sock = PJ_INVALID_SOCKET;
421 } else if (relay->tp.sock != PJ_INVALID_SOCKET) {
422 pj_sock_close(relay->tp.sock);
423 relay->tp.sock = PJ_INVALID_SOCKET;
424 }
425
426 /* Mark as shutdown */
427 relay->lifetime = 0;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000428}
429
430
431/*
Benny Prijono708725a2008-03-09 12:55:00 +0000432 * Really destroy allocation.
Benny Prijonob05aafc2008-03-08 00:54:04 +0000433 */
Benny Prijono708725a2008-03-09 12:55:00 +0000434static void destroy_allocation(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000435{
436 pj_pool_t *pool;
437
438 /* Unregister this allocation */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000439 pj_turn_srv_unregister_allocation(alloc->server, alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000440
441 /* Destroy relay */
442 destroy_relay(&alloc->relay);
443
444 /* Must lock only after destroying relay otherwise deadlock */
445 if (alloc->lock) {
446 pj_lock_acquire(alloc->lock);
447 }
448
Benny Prijono879ad1a2008-04-09 09:38:12 +0000449 /* Unreference transport */
450 if (alloc->transport) {
451 pj_turn_transport_dec_ref(alloc->transport, alloc);
452 alloc->transport = NULL;
453 }
454
Benny Prijonob05aafc2008-03-08 00:54:04 +0000455 /* Destroy STUN session */
456 if (alloc->sess) {
457 pj_stun_session_destroy(alloc->sess);
458 alloc->sess = NULL;
459 }
460
461 /* Destroy lock */
462 if (alloc->lock) {
463 pj_lock_release(alloc->lock);
464 pj_lock_destroy(alloc->lock);
465 alloc->lock = NULL;
466 }
467
468 /* Destroy pool */
469 pool = alloc->pool;
470 if (pool) {
471 alloc->pool = NULL;
472 pj_pool_release(pool);
473 }
474}
475
476
Benny Prijono708725a2008-03-09 12:55:00 +0000477PJ_DECL(void) pj_turn_allocation_destroy(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000478{
Benny Prijono708725a2008-03-09 12:55:00 +0000479 destroy_allocation(alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000480}
481
Benny Prijono708725a2008-03-09 12:55:00 +0000482
Benny Prijono879ad1a2008-04-09 09:38:12 +0000483/*
484 * Handle transport closure.
485 */
486PJ_DEF(void) pj_turn_allocation_on_transport_closed( pj_turn_allocation *alloc,
487 pj_turn_transport *tp)
488{
489 PJ_LOG(5,(alloc->obj_name, "Transport %s unexpectedly closed, destroying "
490 "allocation %s", tp->info, alloc->info));
491 pj_turn_transport_dec_ref(tp, alloc);
492 alloc->transport = NULL;
493 destroy_allocation(alloc);
494}
495
496
Benny Prijono708725a2008-03-09 12:55:00 +0000497/* Initiate shutdown sequence for this allocation and start destroy timer.
498 * Once allocation is marked as shutting down, any packets will be
499 * rejected/discarded
500 */
501static void alloc_shutdown(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000502{
503 pj_time_val destroy_delay = DESTROY_DELAY;
504
505 /* Work with existing schedule */
506 if (alloc->relay.timer.id == TIMER_ID_TIMEOUT) {
Benny Prijono708725a2008-03-09 12:55:00 +0000507 /* Cancel existing shutdown timer */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000508 pj_timer_heap_cancel(alloc->server->core.timer_heap,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000509 &alloc->relay.timer);
510 alloc->relay.timer.id = TIMER_ID_NONE;
511
512 } else if (alloc->relay.timer.id == TIMER_ID_DESTROY) {
513 /* We've been scheduled to be destroyed, ignore this
514 * shutdown request.
515 */
516 return;
517 }
518
519 pj_assert(alloc->relay.timer.id == TIMER_ID_NONE);
520
521 /* Shutdown relay socket */
522 destroy_relay(&alloc->relay);
523
524 /* Don't unregister from hash table because we still need to
525 * handle REFRESH retransmission.
526 */
527
528 /* Schedule destroy timer */
529 alloc->relay.timer.id = TIMER_ID_DESTROY;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000530 pj_timer_heap_schedule(alloc->server->core.timer_heap,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000531 &alloc->relay.timer, &destroy_delay);
532}
533
Benny Prijono708725a2008-03-09 12:55:00 +0000534
Benny Prijonob05aafc2008-03-08 00:54:04 +0000535/* Reschedule timeout using current lifetime setting */
Benny Prijono708725a2008-03-09 12:55:00 +0000536static pj_status_t resched_timeout(pj_turn_allocation *alloc)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000537{
538 pj_time_val delay;
539 pj_status_t status;
540
541 pj_gettimeofday(&alloc->relay.expiry);
542 alloc->relay.expiry.sec += alloc->relay.lifetime;
543
544 pj_assert(alloc->relay.timer.id != TIMER_ID_DESTROY);
545 if (alloc->relay.timer.id != 0) {
Benny Prijono879ad1a2008-04-09 09:38:12 +0000546 pj_timer_heap_cancel(alloc->server->core.timer_heap,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000547 &alloc->relay.timer);
548 alloc->relay.timer.id = TIMER_ID_NONE;
549 }
550
551 delay.sec = alloc->relay.lifetime;
552 delay.msec = 0;
553
554 alloc->relay.timer.id = TIMER_ID_TIMEOUT;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000555 status = pj_timer_heap_schedule(alloc->server->core.timer_heap,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000556 &alloc->relay.timer, &delay);
557 if (status != PJ_SUCCESS) {
558 alloc->relay.timer.id = TIMER_ID_NONE;
559 return status;
560 }
561
562 return PJ_SUCCESS;
563}
564
565
566/* Timer timeout callback */
567static void relay_timeout_cb(pj_timer_heap_t *heap, pj_timer_entry *e)
568{
Benny Prijono708725a2008-03-09 12:55:00 +0000569 pj_turn_relay_res *rel;
570 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000571
Benny Prijono708725a2008-03-09 12:55:00 +0000572 PJ_UNUSED_ARG(heap);
573
574 rel = (pj_turn_relay_res*) e->user_data;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000575 alloc = rel->allocation;
576
577 if (e->id == TIMER_ID_TIMEOUT) {
578
579 e->id = TIMER_ID_NONE;
580
581 PJ_LOG(4,(alloc->obj_name,
582 "Client %s refresh timed-out, shutting down..",
583 alloc->info));
584
585 alloc_shutdown(alloc);
586
587 } else if (e->id == TIMER_ID_DESTROY) {
588 e->id = TIMER_ID_NONE;
589
590 PJ_LOG(4,(alloc->obj_name, "Client %s destroying..",
591 alloc->info));
592
Benny Prijono708725a2008-03-09 12:55:00 +0000593 destroy_allocation(alloc);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000594 }
595}
596
597
598/*
599 * Create relay.
600 */
Benny Prijono708725a2008-03-09 12:55:00 +0000601static pj_status_t create_relay(pj_turn_srv *srv,
602 pj_turn_allocation *alloc,
603 const pj_stun_msg *msg,
604 const alloc_request *req,
605 pj_turn_relay_res *relay)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000606{
607 enum { RETRY = 40 };
608 pj_pool_t *pool = alloc->pool;
609 int retry, retry_max, sock_type;
610 pj_ioqueue_callback icb;
611 int af, namelen;
612 pj_stun_string_attr *sa;
613 pj_status_t status;
614
615 pj_bzero(relay, sizeof(*relay));
616
617 relay->allocation = alloc;
618 relay->tp.sock = PJ_INVALID_SOCKET;
619
620 /* TODO: get the requested address family from somewhere */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000621 af = alloc->transport->listener->addr.addr.sa_family;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000622
623 /* Save realm */
624 sa = (pj_stun_string_attr*)
625 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0);
626 PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP);
627 pj_strdup(pool, &relay->realm, &sa->value);
628
629 /* Save username */
630 sa = (pj_stun_string_attr*)
631 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0);
632 PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP);
633 pj_strdup(pool, &relay->user, &sa->value);
634
635 /* Lifetime and timeout */
636 relay->lifetime = req->lifetime;
637 pj_timer_entry_init(&relay->timer, TIMER_ID_NONE, relay,
638 &relay_timeout_cb);
639 resched_timeout(alloc);
640
641 /* Transport type */
642 relay->hkey.tp_type = req->tp_type;
643
644 /* Create the socket */
Benny Prijono708725a2008-03-09 12:55:00 +0000645 if (req->tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000646 sock_type = pj_SOCK_DGRAM();
Benny Prijono708725a2008-03-09 12:55:00 +0000647 } else if (req->tp_type == PJ_TURN_TP_TCP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000648 sock_type = pj_SOCK_STREAM();
649 } else {
650 pj_assert(!"Unknown transport");
651 return PJ_EINVALIDOP;
652 }
653
654 status = pj_sock_socket(af, sock_type, 0, &relay->tp.sock);
655 if (status != PJ_SUCCESS) {
656 pj_bzero(relay, sizeof(*relay));
657 return status;
658 }
659
660 /* Find suitable port for this allocation */
661 if (req->rpp_port) {
662 retry_max = 1;
663 } else {
664 retry_max = RETRY;
665 }
666
667 for (retry=0; retry<retry_max; ++retry) {
668 pj_uint16_t port;
669 pj_sockaddr bound_addr;
670
671 pj_lock_acquire(srv->core.lock);
672
673 if (req->rpp_port) {
674 port = (pj_uint16_t) req->rpp_port;
Benny Prijono708725a2008-03-09 12:55:00 +0000675 } else if (req->tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000676 port = (pj_uint16_t) srv->ports.next_udp++;
677 if (srv->ports.next_udp > srv->ports.max_udp)
678 srv->ports.next_udp = srv->ports.min_udp;
Benny Prijono708725a2008-03-09 12:55:00 +0000679 } else if (req->tp_type == PJ_TURN_TP_TCP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000680 port = (pj_uint16_t) srv->ports.next_tcp++;
681 if (srv->ports.next_tcp > srv->ports.max_tcp)
682 srv->ports.next_tcp = srv->ports.min_tcp;
683 } else {
684 pj_assert(!"Invalid transport");
Benny Prijono708725a2008-03-09 12:55:00 +0000685 port = 0;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000686 }
687
688 pj_lock_release(srv->core.lock);
689
690 pj_sockaddr_init(af, &bound_addr, NULL, port);
691
692 status = pj_sock_bind(relay->tp.sock, &bound_addr,
693 pj_sockaddr_get_len(&bound_addr));
694 if (status == PJ_SUCCESS)
695 break;
696 }
697
698 if (status != PJ_SUCCESS) {
699 /* Unable to allocate port */
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000700 PJ_LOG(4,(THIS_FILE, "Unable to allocate relay, giving up: err %d",
Benny Prijonob05aafc2008-03-08 00:54:04 +0000701 status));
702 pj_sock_close(relay->tp.sock);
703 relay->tp.sock = PJ_INVALID_SOCKET;
704 return status;
705 }
706
707 /* Init relay key */
708 namelen = sizeof(relay->hkey.addr);
709 status = pj_sock_getsockname(relay->tp.sock, &relay->hkey.addr, &namelen);
710 if (status != PJ_SUCCESS) {
711 PJ_LOG(4,(THIS_FILE, "pj_sock_getsockname() failed: err %d",
712 status));
713 pj_sock_close(relay->tp.sock);
714 relay->tp.sock = PJ_INVALID_SOCKET;
715 return status;
716 }
717 if (!pj_sockaddr_has_addr(&relay->hkey.addr)) {
Benny Prijono879ad1a2008-04-09 09:38:12 +0000718 pj_sockaddr_copy_addr(&relay->hkey.addr,
719 &alloc->transport->listener->addr);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000720 }
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000721 if (!pj_sockaddr_has_addr(&relay->hkey.addr)) {
722 pj_sockaddr tmp_addr;
723 pj_gethostip(af, &tmp_addr);
724 pj_sockaddr_copy_addr(&relay->hkey.addr, &tmp_addr);
725 }
Benny Prijonob05aafc2008-03-08 00:54:04 +0000726
727 /* Init ioqueue */
728 pj_bzero(&icb, sizeof(icb));
729 icb.on_read_complete = &on_rx_from_peer;
730
731 status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, relay->tp.sock,
732 relay, &icb, &relay->tp.key);
733 if (status != PJ_SUCCESS) {
734 PJ_LOG(4,(THIS_FILE, "pj_ioqueue_register_sock() failed: err %d",
735 status));
736 pj_sock_close(relay->tp.sock);
737 relay->tp.sock = PJ_INVALID_SOCKET;
738 return status;
739 }
740
741 /* Kick off pending read operation */
742 pj_ioqueue_op_key_init(&relay->tp.read_key, sizeof(relay->tp.read_key));
743 on_rx_from_peer(relay->tp.key, &relay->tp.read_key, 0);
744
745 /* Done */
746 return PJ_SUCCESS;
747}
748
749/* Create and send error response */
Benny Prijono708725a2008-03-09 12:55:00 +0000750static void send_reply_err(pj_turn_allocation *alloc,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000751 const pj_stun_rx_data *rdata,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000752 pj_bool_t cache,
753 int code, const char *errmsg)
754{
755 pj_status_t status;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000756
Benny Prijono879ad1a2008-04-09 09:38:12 +0000757 status = pj_stun_session_respond(alloc->sess, rdata, code, errmsg, NULL,
758 cache, &alloc->hkey.clt_addr,
Benny Prijono708725a2008-03-09 12:55:00 +0000759 pj_sockaddr_get_len(&alloc->hkey.clt_addr.addr));
Benny Prijonob05aafc2008-03-08 00:54:04 +0000760 if (status != PJ_SUCCESS) {
761 alloc_err(alloc, "Error sending STUN error response", status);
762 return;
763 }
764}
765
766/* Create and send successful response */
Benny Prijono708725a2008-03-09 12:55:00 +0000767static void send_reply_ok(pj_turn_allocation *alloc,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000768 const pj_stun_rx_data *rdata)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000769{
770 pj_status_t status;
771 unsigned interval;
772 pj_stun_tx_data *tdata;
773
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000774 status = pj_stun_session_create_res(alloc->sess, rdata, 0, NULL, &tdata);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000775 if (status != PJ_SUCCESS) {
776 alloc_err(alloc, "Error creating STUN success response", status);
777 return;
778 }
779
780 /* Calculate time to expiration */
781 if (alloc->relay.lifetime != 0) {
782 pj_time_val now;
783 pj_gettimeofday(&now);
784 interval = alloc->relay.expiry.sec - now.sec;
785 } else {
786 interval = 0;
787 }
788
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000789 /* Add LIFETIME if this is not ChannelBind. */
790 if (PJ_STUN_GET_METHOD(tdata->msg->hdr.type)!=PJ_STUN_CHANNEL_BIND_METHOD){
791 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
792 PJ_STUN_ATTR_LIFETIME, interval);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000793
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000794 /* Add BANDWIDTH if lifetime is not zero */
795 if (interval != 0) {
796 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
797 PJ_STUN_ATTR_BANDWIDTH,
798 alloc->bandwidth);
799 }
800 }
Benny Prijonob05aafc2008-03-08 00:54:04 +0000801
Benny Prijono879ad1a2008-04-09 09:38:12 +0000802 status = pj_stun_session_send_msg(alloc->sess, NULL, PJ_TRUE,
803 PJ_FALSE, &alloc->hkey.clt_addr,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000804 pj_sockaddr_get_len(&alloc->hkey.clt_addr),
805 tdata);
806 if (status != PJ_SUCCESS) {
807 alloc_err(alloc, "Error sending STUN success response", status);
808 return;
809 }
810}
811
812
813/* Create new permission */
Benny Prijono708725a2008-03-09 12:55:00 +0000814static pj_turn_permission *create_permission(pj_turn_allocation *alloc,
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000815 const pj_sockaddr_t *peer_addr,
816 unsigned addr_len)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000817{
Benny Prijono708725a2008-03-09 12:55:00 +0000818 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000819
Benny Prijono708725a2008-03-09 12:55:00 +0000820 perm = PJ_POOL_ZALLOC_T(alloc->pool, pj_turn_permission);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000821 pj_memcpy(&perm->hkey.peer_addr, peer_addr, addr_len);
822
Benny Prijonob05aafc2008-03-08 00:54:04 +0000823 perm->allocation = alloc;
Benny Prijono708725a2008-03-09 12:55:00 +0000824 perm->channel = PJ_TURN_INVALID_CHANNEL;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000825
826 pj_gettimeofday(&perm->expiry);
Benny Prijono708725a2008-03-09 12:55:00 +0000827 perm->expiry.sec += PJ_TURN_PERM_TIMEOUT;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000828
Benny Prijonoe7226852008-04-13 21:48:44 +0000829 /* Register to hash table (only the address part!) */
830 pj_hash_set(alloc->pool, alloc->peer_table,
831 pj_sockaddr_get_addr(&perm->hkey.peer_addr),
832 pj_sockaddr_get_addr_len(&perm->hkey.peer_addr), 0, perm);
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000833
Benny Prijonob05aafc2008-03-08 00:54:04 +0000834 return perm;
835}
836
837/* Check if a permission isn't expired. Return NULL if expired. */
Benny Prijono708725a2008-03-09 12:55:00 +0000838static pj_turn_permission *check_permission_expiry(pj_turn_permission *perm)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000839{
Benny Prijono708725a2008-03-09 12:55:00 +0000840 pj_turn_allocation *alloc = perm->allocation;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000841 pj_time_val now;
842
843 pj_gettimeofday(&now);
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000844 if (PJ_TIME_VAL_GT(perm->expiry, now)) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000845 /* Permission has not expired */
846 return perm;
847 }
848
849 /* Remove from permission hash table */
Benny Prijonoe7226852008-04-13 21:48:44 +0000850 pj_hash_set(NULL, alloc->peer_table,
851 pj_sockaddr_get_addr(&perm->hkey.peer_addr),
852 pj_sockaddr_get_addr_len(&perm->hkey.peer_addr), 0, NULL);
Benny Prijonob05aafc2008-03-08 00:54:04 +0000853
854 /* Remove from channel hash table, if assigned a channel number */
Benny Prijono708725a2008-03-09 12:55:00 +0000855 if (perm->channel != PJ_TURN_INVALID_CHANNEL) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000856 pj_hash_set(NULL, alloc->ch_table, &perm->channel,
857 sizeof(perm->channel), 0, NULL);
858 }
859
860 return NULL;
861}
862
863/* Lookup permission in hash table by the peer address */
Benny Prijono708725a2008-03-09 12:55:00 +0000864static pj_turn_permission*
865lookup_permission_by_addr(pj_turn_allocation *alloc,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000866 const pj_sockaddr_t *peer_addr,
867 unsigned addr_len)
868{
Benny Prijono708725a2008-03-09 12:55:00 +0000869 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000870
Benny Prijonoe7226852008-04-13 21:48:44 +0000871 PJ_UNUSED_ARG(addr_len);
872
Benny Prijonob05aafc2008-03-08 00:54:04 +0000873 /* Lookup in peer hash table */
Benny Prijonoe7226852008-04-13 21:48:44 +0000874 perm = (pj_turn_permission*)
875 pj_hash_get(alloc->peer_table,
876 pj_sockaddr_get_addr(peer_addr),
877 pj_sockaddr_get_addr_len(peer_addr),
878 NULL);
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000879 return perm ? check_permission_expiry(perm) : NULL;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000880}
881
882/* Lookup permission in hash table by the channel number */
Benny Prijono708725a2008-03-09 12:55:00 +0000883static pj_turn_permission*
884lookup_permission_by_chnum(pj_turn_allocation *alloc,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000885 unsigned chnum)
886{
887 pj_uint16_t chnum16 = (pj_uint16_t)chnum;
Benny Prijono708725a2008-03-09 12:55:00 +0000888 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000889
890 /* Lookup in peer hash table */
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000891 perm = (pj_turn_permission*) pj_hash_get(alloc->ch_table, &chnum16,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000892 sizeof(chnum16), NULL);
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000893 return perm ? check_permission_expiry(perm) : NULL;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000894}
895
896/* Update permission because of data from client to peer.
897 * Return PJ_TRUE is permission is found.
898 */
Benny Prijono708725a2008-03-09 12:55:00 +0000899static pj_bool_t refresh_permission(pj_turn_permission *perm)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000900{
901 pj_gettimeofday(&perm->expiry);
Benny Prijono708725a2008-03-09 12:55:00 +0000902 if (perm->channel == PJ_TURN_INVALID_CHANNEL)
903 perm->expiry.sec += PJ_TURN_PERM_TIMEOUT;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000904 else
Benny Prijono708725a2008-03-09 12:55:00 +0000905 perm->expiry.sec += PJ_TURN_CHANNEL_TIMEOUT;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000906 return PJ_TRUE;
907}
908
909/*
Benny Prijono708725a2008-03-09 12:55:00 +0000910 * Handle incoming packet from client. This would have been called by
911 * server upon receiving packet from a listener.
Benny Prijonob05aafc2008-03-08 00:54:04 +0000912 */
Benny Prijono708725a2008-03-09 12:55:00 +0000913PJ_DEF(void) pj_turn_allocation_on_rx_client_pkt(pj_turn_allocation *alloc,
914 pj_turn_pkt *pkt)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000915{
916 pj_bool_t is_stun;
917 pj_status_t status;
918
Benny Prijono708725a2008-03-09 12:55:00 +0000919 /* Lock this allocation */
920 pj_lock_acquire(alloc->lock);
921
Benny Prijonob05aafc2008-03-08 00:54:04 +0000922 /* Quickly check if this is STUN message */
923 is_stun = ((*((pj_uint8_t*)pkt->pkt) & 0xC0) == 0);
924
925 if (is_stun) {
926 /*
927 * This could be an incoming STUN requests or indications.
928 * Pass this through to the STUN session, which will call
929 * our stun_on_rx_request() or stun_on_rx_indication()
930 * callbacks.
Benny Prijonoe7226852008-04-13 21:48:44 +0000931 *
932 * Note: currently it is necessary to specify the
933 * PJ_STUN_NO_FINGERPRINT_CHECK otherwise the FINGERPRINT
934 * attribute inside STUN Send Indication message will mess up
935 * with fingerprint checking.
Benny Prijonob05aafc2008-03-08 00:54:04 +0000936 */
Benny Prijonoe7226852008-04-13 21:48:44 +0000937 unsigned options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK;
Nanang Izzuddin838cb322008-12-18 17:52:57 +0000938 pj_size_t parsed_len = 0;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000939
940 if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP)
Benny Prijonob05aafc2008-03-08 00:54:04 +0000941 options |= PJ_STUN_IS_DATAGRAM;
942
943 status = pj_stun_session_on_rx_pkt(alloc->sess, pkt->pkt, pkt->len,
Benny Prijono879ad1a2008-04-09 09:38:12 +0000944 options, NULL, &parsed_len,
Benny Prijonob05aafc2008-03-08 00:54:04 +0000945 &pkt->src.clt_addr,
946 pkt->src_addr_len);
Benny Prijono879ad1a2008-04-09 09:38:12 +0000947
948 if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) {
949 pkt->len = 0;
950 } else if (parsed_len > 0) {
951 if (parsed_len == pkt->len) {
952 pkt->len = 0;
953 } else {
954 pj_memmove(pkt->pkt, pkt->pkt+parsed_len,
955 pkt->len - parsed_len);
956 pkt->len -= parsed_len;
957 }
958 }
959
Benny Prijonob05aafc2008-03-08 00:54:04 +0000960 if (status != PJ_SUCCESS) {
961 alloc_err(alloc, "Error handling STUN packet", status);
Benny Prijono708725a2008-03-09 12:55:00 +0000962 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000963 }
964
965 } else {
966 /*
967 * This is not a STUN packet, must be ChannelData packet.
968 */
Benny Prijono3fd9fc52008-03-09 23:52:48 +0000969 pj_turn_channel_data *cd = (pj_turn_channel_data*)pkt->pkt;
Benny Prijono708725a2008-03-09 12:55:00 +0000970 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000971 pj_ssize_t len;
972
Benny Prijono3fd9fc52008-03-09 23:52:48 +0000973 pj_assert(sizeof(*cd)==4);
974
Benny Prijonob05aafc2008-03-08 00:54:04 +0000975 /* For UDP check the packet length */
Benny Prijono879ad1a2008-04-09 09:38:12 +0000976 if (alloc->transport->listener->tp_type == PJ_TURN_TP_UDP) {
Benny Prijonob05aafc2008-03-08 00:54:04 +0000977 if (pkt->len < pj_ntohs(cd->length)+sizeof(*cd)) {
978 PJ_LOG(4,(alloc->obj_name,
979 "ChannelData from %s discarded: UDP size error",
980 alloc->info));
Benny Prijono708725a2008-03-09 12:55:00 +0000981 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000982 }
983 } else {
984 pj_assert(!"Unsupported transport");
Benny Prijono708725a2008-03-09 12:55:00 +0000985 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000986 }
987
988 perm = lookup_permission_by_chnum(alloc, pj_ntohs(cd->ch_number));
989 if (!perm) {
990 /* Discard */
991 PJ_LOG(4,(alloc->obj_name,
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000992 "ChannelData from %s discarded: ch#0x%x not found",
993 alloc->info, pj_ntohs(cd->ch_number)));
Benny Prijono708725a2008-03-09 12:55:00 +0000994 goto on_return;
Benny Prijonob05aafc2008-03-08 00:54:04 +0000995 }
996
997 /* Relay the data */
998 len = pj_ntohs(cd->length);
999 pj_sock_sendto(alloc->relay.tp.sock, cd+1, &len, 0,
1000 &perm->hkey.peer_addr,
1001 pj_sockaddr_get_len(&perm->hkey.peer_addr));
1002
1003 /* Refresh permission */
1004 refresh_permission(perm);
1005 }
Benny Prijono708725a2008-03-09 12:55:00 +00001006
1007on_return:
1008 /* Release lock */
1009 pj_lock_release(alloc->lock);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001010}
1011
Benny Prijono708725a2008-03-09 12:55:00 +00001012
Benny Prijonob05aafc2008-03-08 00:54:04 +00001013/*
1014 * Handle incoming packet from peer. This function is called by
1015 * on_rx_from_peer().
1016 */
Benny Prijono708725a2008-03-09 12:55:00 +00001017static void handle_peer_pkt(pj_turn_allocation *alloc,
1018 pj_turn_relay_res *rel,
1019 char *pkt, pj_size_t len,
1020 const pj_sockaddr *src_addr)
Benny Prijonob05aafc2008-03-08 00:54:04 +00001021{
Benny Prijono708725a2008-03-09 12:55:00 +00001022 pj_turn_permission *perm;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001023
1024 /* Lookup permission */
1025 perm = lookup_permission_by_addr(alloc, src_addr,
1026 pj_sockaddr_get_len(src_addr));
1027 if (perm == NULL) {
1028 /* No permission, discard data */
1029 return;
1030 }
1031
1032 /* Send Data Indication or ChannelData, depends on whether
1033 * this permission is attached to a channel number.
1034 */
Benny Prijono708725a2008-03-09 12:55:00 +00001035 if (perm->channel != PJ_TURN_INVALID_CHANNEL) {
Benny Prijonob05aafc2008-03-08 00:54:04 +00001036 /* Send ChannelData */
Benny Prijono3fd9fc52008-03-09 23:52:48 +00001037 pj_turn_channel_data *cd = (pj_turn_channel_data*)rel->tp.tx_pkt;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001038
Benny Prijono708725a2008-03-09 12:55:00 +00001039 if (len > PJ_TURN_MAX_PKT_LEN) {
Benny Prijonob05aafc2008-03-08 00:54:04 +00001040 char peer_addr[80];
1041 pj_sockaddr_print(src_addr, peer_addr, sizeof(peer_addr), 3);
Benny Prijono708725a2008-03-09 12:55:00 +00001042 PJ_LOG(4,(alloc->obj_name, "Client %s: discarded data from %s "
Benny Prijonob05aafc2008-03-08 00:54:04 +00001043 "because it's too long (%d bytes)",
1044 alloc->info, peer_addr, len));
1045 return;
1046 }
1047
1048 /* Init header */
1049 cd->ch_number = pj_htons(perm->channel);
1050 cd->length = pj_htons((pj_uint16_t)len);
1051
1052 /* Copy data */
Benny Prijono9e6d60a2008-03-20 16:32:06 +00001053 pj_memcpy(rel->tp.tx_pkt+sizeof(pj_turn_channel_data), pkt, len);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001054
1055 /* Send to client */
Benny Prijono879ad1a2008-04-09 09:38:12 +00001056 alloc->transport->sendto(alloc->transport, rel->tp.tx_pkt,
1057 len+sizeof(pj_turn_channel_data), 0,
1058 &alloc->hkey.clt_addr,
1059 pj_sockaddr_get_len(&alloc->hkey.clt_addr));
Benny Prijonob05aafc2008-03-08 00:54:04 +00001060 } else {
1061 /* Send Data Indication */
1062 pj_stun_tx_data *tdata;
1063 pj_status_t status;
1064
1065 status = pj_stun_session_create_ind(alloc->sess,
1066 PJ_STUN_DATA_INDICATION, &tdata);
1067 if (status != PJ_SUCCESS) {
1068 alloc_err(alloc, "Error creating Data indication", status);
1069 return;
1070 }
Benny Prijono9e6d60a2008-03-20 16:32:06 +00001071
1072 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
1073 PJ_STUN_ATTR_PEER_ADDR, PJ_TRUE,
1074 src_addr, pj_sockaddr_get_len(src_addr));
1075 pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg,
1076 PJ_STUN_ATTR_DATA,
1077 (const pj_uint8_t*)pkt, len);
1078
Benny Prijono879ad1a2008-04-09 09:38:12 +00001079 pj_stun_session_send_msg(alloc->sess, NULL, PJ_FALSE,
1080 PJ_FALSE, &alloc->hkey.clt_addr,
Benny Prijono9e6d60a2008-03-20 16:32:06 +00001081 pj_sockaddr_get_len(&alloc->hkey.clt_addr),
1082 tdata);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001083 }
1084}
1085
1086/*
1087 * ioqueue notification on RX packets from the relay socket.
1088 */
1089static void on_rx_from_peer(pj_ioqueue_key_t *key,
1090 pj_ioqueue_op_key_t *op_key,
1091 pj_ssize_t bytes_read)
1092{
Benny Prijono708725a2008-03-09 12:55:00 +00001093 pj_turn_relay_res *rel;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001094 pj_status_t status;
1095
Benny Prijono708725a2008-03-09 12:55:00 +00001096 rel = (pj_turn_relay_res*) pj_ioqueue_get_user_data(key);
1097
1098 /* Lock the allocation */
1099 pj_lock_acquire(rel->allocation->lock);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001100
1101 do {
1102 if (bytes_read > 0) {
Benny Prijono708725a2008-03-09 12:55:00 +00001103 handle_peer_pkt(rel->allocation, rel, rel->tp.rx_pkt,
1104 bytes_read, &rel->tp.src_addr);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001105 }
1106
1107 /* Read next packet */
1108 bytes_read = sizeof(rel->tp.rx_pkt);
1109 rel->tp.src_addr_len = sizeof(rel->tp.src_addr);
1110 status = pj_ioqueue_recvfrom(key, op_key,
1111 rel->tp.rx_pkt, &bytes_read, 0,
1112 &rel->tp.src_addr,
1113 &rel->tp.src_addr_len);
1114
1115 if (status != PJ_EPENDING && status != PJ_SUCCESS)
1116 bytes_read = -status;
1117
1118 } while (status != PJ_EPENDING && status != PJ_ECANCELLED);
1119
Benny Prijono708725a2008-03-09 12:55:00 +00001120 /* Release allocation lock */
1121 pj_lock_release(rel->allocation->lock);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001122}
1123
1124/*
1125 * Callback notification from STUN session when it wants to send
1126 * a STUN message towards the client.
1127 */
1128static pj_status_t stun_on_send_msg(pj_stun_session *sess,
Benny Prijono879ad1a2008-04-09 09:38:12 +00001129 void *token,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001130 const void *pkt,
1131 pj_size_t pkt_size,
1132 const pj_sockaddr_t *dst_addr,
1133 unsigned addr_len)
1134{
Benny Prijono708725a2008-03-09 12:55:00 +00001135 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001136
Benny Prijono879ad1a2008-04-09 09:38:12 +00001137 PJ_UNUSED_ARG(token);
1138
Benny Prijono708725a2008-03-09 12:55:00 +00001139 alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001140
Benny Prijono879ad1a2008-04-09 09:38:12 +00001141 return alloc->transport->sendto(alloc->transport, pkt, pkt_size, 0,
1142 dst_addr, addr_len);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001143}
1144
1145/*
1146 * Callback notification from STUN session when it receives STUN
1147 * requests. This callback was trigger by STUN incoming message
Benny Prijono708725a2008-03-09 12:55:00 +00001148 * processing in pj_turn_allocation_on_rx_client_pkt().
Benny Prijonob05aafc2008-03-08 00:54:04 +00001149 */
1150static pj_status_t stun_on_rx_request(pj_stun_session *sess,
1151 const pj_uint8_t *pkt,
1152 unsigned pkt_len,
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001153 const pj_stun_rx_data *rdata,
Benny Prijono879ad1a2008-04-09 09:38:12 +00001154 void *token,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001155 const pj_sockaddr_t *src_addr,
1156 unsigned src_addr_len)
1157{
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001158 const pj_stun_msg *msg = rdata->msg;
Benny Prijono708725a2008-03-09 12:55:00 +00001159 pj_turn_allocation *alloc;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001160
Benny Prijono708725a2008-03-09 12:55:00 +00001161 PJ_UNUSED_ARG(pkt);
1162 PJ_UNUSED_ARG(pkt_len);
Benny Prijono879ad1a2008-04-09 09:38:12 +00001163 PJ_UNUSED_ARG(token);
Benny Prijono708725a2008-03-09 12:55:00 +00001164 PJ_UNUSED_ARG(src_addr);
1165 PJ_UNUSED_ARG(src_addr_len);
1166
1167 alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001168
1169 /* Refuse to serve any request if we've been shutdown */
1170 if (alloc->relay.lifetime == 0) {
Benny Prijono708725a2008-03-09 12:55:00 +00001171 /* Reject with 437 if we're shutting down */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001172 send_reply_err(alloc, rdata, PJ_TRUE,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001173 PJ_STUN_SC_ALLOCATION_MISMATCH, NULL);
1174 return PJ_SUCCESS;
1175 }
1176
1177 if (msg->hdr.type == PJ_STUN_REFRESH_REQUEST) {
1178 /*
1179 * Handle REFRESH request
1180 */
1181 pj_stun_lifetime_attr *lifetime;
1182 pj_stun_bandwidth_attr *bandwidth;
1183
1184 /* Get LIFETIME attribute */
1185 lifetime = (pj_stun_lifetime_attr*)
1186 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0);
1187
1188 /* Get BANDWIDTH attribute */
1189 bandwidth = (pj_stun_bandwidth_attr*)
1190 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_BANDWIDTH, 0);
1191
1192 if (lifetime && lifetime->value==0) {
1193 /*
1194 * This is deallocation request.
1195 */
1196 alloc->relay.lifetime = 0;
1197
1198 /* Respond first */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001199 send_reply_ok(alloc, rdata);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001200
1201 /* Shutdown allocation */
1202 PJ_LOG(4,(alloc->obj_name,
1203 "Client %s request to dealloc, shutting down",
1204 alloc->info));
1205
1206 alloc_shutdown(alloc);
1207
1208 } else {
1209 /*
1210 * This is a refresh request.
1211 */
1212
1213 /* Update lifetime */
1214 if (lifetime) {
1215 alloc->relay.lifetime = lifetime->value;
1216 }
1217
1218 /* Update bandwidth */
1219 // TODO:
1220
1221 /* Update expiration timer */
1222 resched_timeout(alloc);
1223
1224 /* Send reply */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001225 send_reply_ok(alloc, rdata);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001226 }
1227
1228 } else if (msg->hdr.type == PJ_STUN_CHANNEL_BIND_REQUEST) {
1229 /*
1230 * ChannelBind request.
1231 */
1232 pj_stun_channel_number_attr *ch_attr;
1233 pj_stun_peer_addr_attr *peer_attr;
Benny Prijono708725a2008-03-09 12:55:00 +00001234 pj_turn_permission *p1, *p2;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001235
1236 ch_attr = (pj_stun_channel_number_attr*)
1237 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CHANNEL_NUMBER, 0);
1238 peer_attr = (pj_stun_peer_addr_attr*)
1239 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
1240
1241 if (!ch_attr || !peer_attr) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001242 send_reply_err(alloc, rdata, PJ_TRUE,
1243 PJ_STUN_SC_BAD_REQUEST, NULL);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001244 return PJ_SUCCESS;
1245 }
1246
1247 /* Find permission with the channel number */
1248 p1 = lookup_permission_by_chnum(alloc, PJ_STUN_GET_CH_NB(ch_attr->value));
1249
1250 /* If permission is found, this is supposed to be a channel bind
1251 * refresh. Make sure it's for the same peer.
1252 */
1253 if (p1) {
1254 if (pj_sockaddr_cmp(&p1->hkey.peer_addr, &peer_attr->sockaddr)) {
1255 /* Address mismatch. Send 400 */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001256 send_reply_err(alloc, rdata, PJ_TRUE,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001257 PJ_STUN_SC_BAD_REQUEST,
1258 "Peer address mismatch");
1259 return PJ_SUCCESS;
1260 }
1261
1262 /* Refresh permission */
1263 refresh_permission(p1);
1264
Benny Prijono9e6d60a2008-03-20 16:32:06 +00001265 /* Send response */
1266 send_reply_ok(alloc, rdata);
1267
Benny Prijonob05aafc2008-03-08 00:54:04 +00001268 /* Done */
1269 return PJ_SUCCESS;
1270 }
1271
1272 /* If permission is not found, create a new one. Make sure the peer
1273 * has not alreadyy assigned with a channel number.
1274 */
1275 p2 = lookup_permission_by_addr(alloc, &peer_attr->sockaddr,
1276 pj_sockaddr_get_len(&peer_attr->sockaddr));
Benny Prijono708725a2008-03-09 12:55:00 +00001277 if (p2 && p2->channel != PJ_TURN_INVALID_CHANNEL) {
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001278 send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001279 "Peer address already assigned a channel number");
1280 return PJ_SUCCESS;
1281 }
1282
1283 /* Create permission if it doesn't exist */
1284 if (!p2) {
1285 p2 = create_permission(alloc, &peer_attr->sockaddr,
1286 pj_sockaddr_get_len(&peer_attr->sockaddr));
1287 if (!p2)
1288 return PJ_SUCCESS;
1289 }
1290
1291 /* Assign channel number to permission */
1292 p2->channel = PJ_STUN_GET_CH_NB(ch_attr->value);
1293
Benny Prijono9e6d60a2008-03-20 16:32:06 +00001294 /* Register to hash table */
1295 pj_assert(sizeof(p2->channel==2));
1296 pj_hash_set(alloc->pool, alloc->ch_table, &p2->channel,
1297 sizeof(p2->channel), 0, p2);
1298
Benny Prijonob05aafc2008-03-08 00:54:04 +00001299 /* Update */
1300 refresh_permission(p2);
1301
1302 /* Reply */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001303 send_reply_ok(alloc, rdata);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001304
1305 return PJ_SUCCESS;
1306
1307 } else if (msg->hdr.type == PJ_STUN_ALLOCATE_REQUEST) {
1308
1309 /* Respond with 437 (section 6.3 turn-07) */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001310 send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH,
1311 NULL);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001312
1313 } else {
1314
1315 /* Respond with Bad Request? */
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001316 send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001317
1318 }
1319
1320 return PJ_SUCCESS;
1321}
1322
1323/*
1324 * Callback notification from STUN session when it receives STUN
1325 * indications. This callback was trigger by STUN incoming message
Benny Prijono708725a2008-03-09 12:55:00 +00001326 * processing in pj_turn_allocation_on_rx_client_pkt().
Benny Prijonob05aafc2008-03-08 00:54:04 +00001327 */
1328static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
1329 const pj_uint8_t *pkt,
1330 unsigned pkt_len,
1331 const pj_stun_msg *msg,
Benny Prijono879ad1a2008-04-09 09:38:12 +00001332 void *token,
Benny Prijonob05aafc2008-03-08 00:54:04 +00001333 const pj_sockaddr_t *src_addr,
1334 unsigned src_addr_len)
1335{
1336 pj_stun_peer_addr_attr *peer_attr;
1337 pj_stun_data_attr *data_attr;
Benny Prijono708725a2008-03-09 12:55:00 +00001338 pj_turn_allocation *alloc;
1339 pj_turn_permission *perm;
Benny Prijono879ad1a2008-04-09 09:38:12 +00001340 pj_ssize_t len;
Benny Prijonob05aafc2008-03-08 00:54:04 +00001341
Benny Prijono708725a2008-03-09 12:55:00 +00001342 PJ_UNUSED_ARG(pkt);
1343 PJ_UNUSED_ARG(pkt_len);
Benny Prijono879ad1a2008-04-09 09:38:12 +00001344 PJ_UNUSED_ARG(token);
Benny Prijono708725a2008-03-09 12:55:00 +00001345 PJ_UNUSED_ARG(src_addr);
1346 PJ_UNUSED_ARG(src_addr_len);
1347
1348 alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
Benny Prijonob05aafc2008-03-08 00:54:04 +00001349
1350 /* Only expect Send Indication */
1351 if (msg->hdr.type != PJ_STUN_SEND_INDICATION) {
1352 /* Ignore */
1353 return PJ_SUCCESS;
1354 }
1355
1356 /* Get PEER-ADDRESS attribute */
1357 peer_attr = (pj_stun_peer_addr_attr*)
1358 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
1359
1360 /* MUST have PEER-ADDRESS attribute */
1361 if (!peer_attr)
1362 return PJ_SUCCESS;
1363
1364 /* Get DATA attribute */
1365 data_attr = (pj_stun_data_attr*)
1366 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_DATA, 0);
1367
1368 /* Create/update/refresh the permission */
1369 perm = lookup_permission_by_addr(alloc, &peer_attr->sockaddr,
1370 pj_sockaddr_get_len(&peer_attr->sockaddr));
1371 if (perm == NULL) {
1372 perm = create_permission(alloc, &peer_attr->sockaddr,
1373 pj_sockaddr_get_len(&peer_attr->sockaddr));
1374 }
1375 refresh_permission(perm);
1376
1377 /* Return if we don't have data */
1378 if (data_attr == NULL)
1379 return PJ_SUCCESS;
1380
Benny Prijono879ad1a2008-04-09 09:38:12 +00001381 /* Relay the data to peer */
1382 len = data_attr->length;
1383 pj_sock_sendto(alloc->relay.tp.sock, data_attr->data,
1384 &len, 0, &peer_attr->sockaddr,
1385 pj_sockaddr_get_len(&peer_attr->sockaddr));
Benny Prijonob05aafc2008-03-08 00:54:04 +00001386
1387 return PJ_SUCCESS;
1388}
1389
1390