blob: 9b0359924ec924038cfb89fdf354b5b410b14c43 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
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 <pjlib-util/stun_simple.h>
21#include <pjlib-util/errno.h>
22#include <pj/log.h>
23#include <pj/os.h>
24#include <pj/pool.h>
25#include <pj/rand.h>
26#include <pj/sock_select.h>
27#include <pj/string.h>
28
29
30enum { MAX_REQUEST = 4 };
31static int stun_timer[] = {500, 500, 500, 500 };
32#define STUN_MAGIC 0x2112A442
33
34#define THIS_FILE "stun_client.c"
35#define LOG_ADDR(addr) pj_inet_ntoa(addr.sin_addr), pj_ntohs(addr.sin_port)
36
37#define TRACE_(x) PJ_LOG(6,x)
38
39PJ_DEF(pj_status_t) pjstun_get_mapped_addr( pj_pool_factory *pf,
40 int sock_cnt, pj_sock_t sock[],
41 const pj_str_t *srv1, int port1,
42 const pj_str_t *srv2, int port2,
43 pj_sockaddr_in mapped_addr[])
44{
45 pjstun_setting opt;
46
47 pj_bzero(&opt, sizeof(opt));
48 opt.use_stun2 = PJ_FALSE;
49 opt.srv1 = *srv1;
50 opt.port1 = port1;
51 opt.srv2 = *srv2;
52 opt.port2 = port2;
53
54 return pjstun_get_mapped_addr2(pf, &opt, sock_cnt, sock, mapped_addr);
55}
56
57PJ_DEF(pj_status_t) pjstun_get_mapped_addr2(pj_pool_factory *pf,
58 const pjstun_setting *opt,
59 int sock_cnt,
60 pj_sock_t sock[],
61 pj_sockaddr_in mapped_addr[])
62{
63 unsigned srv_cnt;
64 const pj_str_t *srv1, *srv2;
65 int port1, port2;
66 pj_sockaddr_in srv_addr[2];
67 int i, send_cnt = 0, nfds;
68 pj_pool_t *pool;
69 struct query_rec {
70 struct {
71 pj_uint32_t mapped_addr;
72 pj_uint32_t mapped_port;
73 } srv[2];
74 } *rec;
75 void *out_msg;
76 pj_size_t out_msg_len;
77 int wait_resp = 0;
78 pj_status_t status;
79
80 PJ_CHECK_STACK();
81
82 srv1 = &opt->srv1;
83 port1 = opt->port1;
84 srv2 = &opt->srv1;
85 port2 = opt->port2;
86
87 TRACE_((THIS_FILE, "Entering pjstun_get_mapped_addr()"));
88
89 /* Create pool. */
90 pool = pj_pool_create(pf, "stun%p", 400, 400, NULL);
91 if (!pool)
92 return PJ_ENOMEM;
93
94
95 /* Allocate client records */
96 rec = (struct query_rec*) pj_pool_calloc(pool, sock_cnt, sizeof(*rec));
97 if (!rec) {
98 status = PJ_ENOMEM;
99 goto on_error;
100 }
101
102 TRACE_((THIS_FILE, " Memory allocated."));
103
104 /* Create the outgoing BIND REQUEST message template */
105 status = pjstun_create_bind_req( pool, &out_msg, &out_msg_len,
106 pj_rand(), pj_rand());
107 if (status != PJ_SUCCESS)
108 goto on_error;
109
110 /* Insert magic cookie (specified in RFC 5389) when requested to. */
111 if (opt->use_stun2) {
112 pjstun_msg_hdr *hdr = (pjstun_msg_hdr*)out_msg;
113 hdr->tsx[0] = pj_htonl(STUN_MAGIC);
114 }
115
116 TRACE_((THIS_FILE, " Binding request created."));
117
118 /* Resolve servers. */
119 status = pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1);
120 if (status != PJ_SUCCESS)
121 goto on_error;
122
123 srv_cnt = 1;
124
125 if (srv2 && port2) {
126 status = pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2);
127 if (status != PJ_SUCCESS)
128 goto on_error;
129
130 if (srv_addr[1].sin_addr.s_addr != srv_addr[0].sin_addr.s_addr &&
131 srv_addr[1].sin_port != srv_addr[0].sin_port)
132 {
133 srv_cnt++;
134 }
135 }
136
137 TRACE_((THIS_FILE, " Server initialized, using %d server(s)", srv_cnt));
138
139 /* Init mapped addresses to zero */
140 pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in));
141
142 /* We need these many responses */
143 wait_resp = sock_cnt * srv_cnt;
144
145 TRACE_((THIS_FILE, " Done initialization."));
146
147#if defined(PJ_SELECT_NEEDS_NFDS) && PJ_SELECT_NEEDS_NFDS!=0
148 nfds = -1;
149 for (i=0; i<sock_cnt; ++i) {
150 if (sock[i] > nfds) {
151 nfds = sock[i];
152 }
153 }
154#else
155 nfds = PJ_IOQUEUE_MAX_HANDLES-1;
156#endif
157
158 /* Main retransmission loop. */
159 for (send_cnt=0; send_cnt<MAX_REQUEST; ++send_cnt) {
160 pj_time_val next_tx, now;
161 pj_fd_set_t r;
162 int select_rc;
163
164 PJ_FD_ZERO(&r);
165
166 /* Send messages to servers that has not given us response. */
167 for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
168 unsigned j;
169 for (j=0; j<srv_cnt && status==PJ_SUCCESS; ++j) {
170 pjstun_msg_hdr *msg_hdr = (pjstun_msg_hdr*) out_msg;
171 pj_ssize_t sent_len;
172
173 if (rec[i].srv[j].mapped_port != 0)
174 continue;
175
176 /* Modify message so that we can distinguish response. */
177 msg_hdr->tsx[2] = pj_htonl(i);
178 msg_hdr->tsx[3] = pj_htonl(j);
179
180 /* Send! */
181 sent_len = out_msg_len;
182 status = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
183 (pj_sockaddr_t*)&srv_addr[j],
184 sizeof(pj_sockaddr_in));
185 }
186 }
187
188 /* All requests sent.
189 * The loop below will wait for responses until all responses have
190 * been received (i.e. wait_resp==0) or timeout occurs, which then
191 * we'll go to the next retransmission iteration.
192 */
193 TRACE_((THIS_FILE, " Request(s) sent, counter=%d", send_cnt));
194
195 /* Calculate time of next retransmission. */
196 pj_gettickcount(&next_tx);
197 next_tx.sec += (stun_timer[send_cnt]/1000);
198 next_tx.msec += (stun_timer[send_cnt]%1000);
199 pj_time_val_normalize(&next_tx);
200
201 for (pj_gettickcount(&now), select_rc=1;
202 status==PJ_SUCCESS && select_rc>=1 && wait_resp>0
203 && PJ_TIME_VAL_LT(now, next_tx);
204 pj_gettickcount(&now))
205 {
206 pj_time_val timeout;
207
208 timeout = next_tx;
209 PJ_TIME_VAL_SUB(timeout, now);
210
211 for (i=0; i<sock_cnt; ++i) {
212 PJ_FD_SET(sock[i], &r);
213 }
214
215 select_rc = pj_sock_select(nfds+1, &r, NULL, NULL, &timeout);
216 TRACE_((THIS_FILE, " select() rc=%d", select_rc));
217 if (select_rc < 1)
218 continue;
219
220 for (i=0; i<sock_cnt; ++i) {
221 int sock_idx, srv_idx;
222 pj_ssize_t len;
223 pjstun_msg msg;
224 pj_sockaddr_in addr;
225 int addrlen = sizeof(addr);
226 pjstun_mapped_addr_attr *attr;
227 char recv_buf[128];
228
229 if (!PJ_FD_ISSET(sock[i], &r))
230 continue;
231
232 len = sizeof(recv_buf);
233 status = pj_sock_recvfrom( sock[i], recv_buf,
234 &len, 0,
235 (pj_sockaddr_t*)&addr,
236 &addrlen);
237
238 if (status != PJ_SUCCESS) {
239 char errmsg[PJ_ERR_MSG_SIZE];
240
241 PJ_LOG(4,(THIS_FILE, "recvfrom() error ignored: %s",
242 pj_strerror(status, errmsg,sizeof(errmsg)).ptr));
243
244 /* Ignore non-PJ_SUCCESS status.
245 * It possible that other SIP entity is currently
246 * sending SIP request to us, and because SIP message
247 * is larger than STUN, we could get EMSGSIZE when
248 * we call recvfrom().
249 */
250 status = PJ_SUCCESS;
251 continue;
252 }
253
254 status = pjstun_parse_msg(recv_buf, len, &msg);
255 if (status != PJ_SUCCESS) {
256 char errmsg[PJ_ERR_MSG_SIZE];
257
258 PJ_LOG(4,(THIS_FILE, "STUN parsing error ignored: %s",
259 pj_strerror(status, errmsg,sizeof(errmsg)).ptr));
260
261 /* Also ignore non-successful parsing. This may not
262 * be STUN response at all. See the comment above.
263 */
264 status = PJ_SUCCESS;
265 continue;
266 }
267
268 sock_idx = pj_ntohl(msg.hdr->tsx[2]);
269 srv_idx = pj_ntohl(msg.hdr->tsx[3]);
270
271 if (sock_idx<0 || sock_idx>=sock_cnt || sock_idx!=i ||
272 srv_idx<0 || srv_idx>=2)
273 {
274 status = PJLIB_UTIL_ESTUNININDEX;
275 continue;
276 }
277
278 if (pj_ntohs(msg.hdr->type) != PJSTUN_BINDING_RESPONSE) {
279 status = PJLIB_UTIL_ESTUNNOBINDRES;
280 continue;
281 }
282
283 if (rec[sock_idx].srv[srv_idx].mapped_port != 0) {
284 /* Already got response */
285 continue;
286 }
287
288 /* From this part, we consider the packet as a valid STUN
289 * response for our request.
290 */
291 --wait_resp;
292
293 if (pjstun_msg_find_attr(&msg, PJSTUN_ATTR_ERROR_CODE) != NULL) {
294 status = PJLIB_UTIL_ESTUNRECVERRATTR;
295 continue;
296 }
297
298 attr = (pjstun_mapped_addr_attr*)
299 pjstun_msg_find_attr(&msg, PJSTUN_ATTR_MAPPED_ADDR);
300 if (!attr) {
301 attr = (pjstun_mapped_addr_attr*)
302 pjstun_msg_find_attr(&msg, PJSTUN_ATTR_XOR_MAPPED_ADDR);
303 if (!attr || attr->family != 1) {
304 status = PJLIB_UTIL_ESTUNNOMAP;
305 continue;
306 }
307 }
308
309 rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr;
310 rec[sock_idx].srv[srv_idx].mapped_port = attr->port;
311 if (pj_ntohs(attr->hdr.type) == PJSTUN_ATTR_XOR_MAPPED_ADDR) {
312 rec[sock_idx].srv[srv_idx].mapped_addr ^= pj_htonl(STUN_MAGIC);
313 rec[sock_idx].srv[srv_idx].mapped_port ^= pj_htons(STUN_MAGIC >> 16);
314 }
315 }
316 }
317
318 /* The best scenario is if all requests have been replied.
319 * Then we don't need to go to the next retransmission iteration.
320 */
321 if (wait_resp <= 0)
322 break;
323 }
324
325 TRACE_((THIS_FILE, " All responses received, calculating result.."));
326
327 for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
328 if (srv_cnt == 1) {
329 mapped_addr[i].sin_family = pj_AF_INET();
330 mapped_addr[i].sin_addr.s_addr = rec[i].srv[0].mapped_addr;
331 mapped_addr[i].sin_port = (pj_uint16_t)rec[i].srv[0].mapped_port;
332
333 if (rec[i].srv[0].mapped_addr == 0 || rec[i].srv[0].mapped_port == 0) {
334 status = PJLIB_UTIL_ESTUNNOTRESPOND;
335 break;
336 }
337 } else if (rec[i].srv[0].mapped_addr == rec[i].srv[1].mapped_addr &&
338 rec[i].srv[0].mapped_port == rec[i].srv[1].mapped_port)
339 {
340 mapped_addr[i].sin_family = pj_AF_INET();
341 mapped_addr[i].sin_addr.s_addr = rec[i].srv[0].mapped_addr;
342 mapped_addr[i].sin_port = (pj_uint16_t)rec[i].srv[0].mapped_port;
343
344 if (rec[i].srv[0].mapped_addr == 0 || rec[i].srv[0].mapped_port == 0) {
345 status = PJLIB_UTIL_ESTUNNOTRESPOND;
346 break;
347 }
348 } else {
349 status = PJLIB_UTIL_ESTUNSYMMETRIC;
350 break;
351 }
352 }
353
354 TRACE_((THIS_FILE, " Pool usage=%d of %d", pj_pool_get_used_size(pool),
355 pj_pool_get_capacity(pool)));
356
357 pj_pool_release(pool);
358
359 TRACE_((THIS_FILE, " Done."));
360 return status;
361
362on_error:
363 if (pool) pj_pool_release(pool);
364 return status;
365}
366