1 /*
2 * This file is part of the Nice GLib ICE library.
3 *
4 * (C) 2008-2009 Collabora Ltd.
5 * Contact: Youness Alaoui
6 * (C) 2007-2009 Nokia Corporation. All rights reserved.
7 * Contact: Rémi Denis-Courmont
8 *
9 * The contents of this file are subject to the Mozilla Public License Version
10 * 1.1 (the "License"); you may not use this file except in compliance with
11 * the License. You may obtain a copy of the License at
12 * http://www.mozilla.org/MPL/
13 *
14 * Software distributed under the License is distributed on an "AS IS" basis,
15 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16 * for the specific language governing rights and limitations under the
17 * License.
18 *
19 * The Original Code is the Nice GLib ICE library.
20 *
21 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
22 * Corporation. All Rights Reserved.
23 *
24 * Contributors:
25 * Youness Alaoui, Collabora Ltd.
26 * Rémi Denis-Courmont, Nokia
27 *
28 * Alternatively, the contents of this file may be used under the terms of the
29 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
30 * case the provisions of LGPL are applicable instead of those above. If you
31 * wish to allow use of your version of this file only under the terms of the
32 * LGPL and not to allow others to use your version of this file under the
33 * MPL, indicate your decision by deleting the provisions above and replace
34 * them with the notice and other provisions required by the LGPL. If you do
35 * not delete the provisions above, a recipient may use your version of this
36 * file under either the MPL or the LGPL.
37 */
38
39 #ifdef HAVE_CONFIG_H
40 # include <config.h>
41 #endif
42
43 #ifdef _WIN32
44 #include <winsock2.h>
45 #include <ws2tcpip.h>
46 #include "win32_common.h"
47 #define close closesocket
48 #else
49 #include <sys/types.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <stdint.h>
53 #include <stdbool.h>
54 #include <unistd.h>
55 #include <errno.h>
56 #include <sys/time.h>
57 #include <fcntl.h>
58 #endif
59
60
61 #ifdef HAVE_POLL
62 # include <poll.h>
63 #endif
64
65
66 #include "bind.h"
67 #include "stun/stunagent.h"
68
69 #include <assert.h>
70 #include <string.h>
71 #include <stdlib.h>
72 #include <time.h>
73 #include <errno.h>
74 #include "timer.h"
75
76
77 #ifndef SOL_IP
78 # define SOL_IP IPPROTO_IP
79 #endif
80
81 #ifndef SOL_IPV6
82 # define SOL_IPV6 IPPROTO_IPV6
83 #endif
84
85
86 /** Non-blocking mode STUN binding discovery */
87
stun_usage_bind_create(StunAgent * agent,StunMessage * msg,uint8_t * buffer,size_t buffer_len)88 size_t stun_usage_bind_create (StunAgent *agent, StunMessage *msg,
89 uint8_t *buffer, size_t buffer_len)
90 {
91 stun_agent_init_request (agent, msg, buffer, buffer_len, STUN_BINDING);
92
93 return stun_agent_finish_message (agent, msg, NULL, 0);
94 }
95
stun_usage_bind_process(StunMessage * msg,struct sockaddr * addr,socklen_t * addrlen,struct sockaddr * alternate_server,socklen_t * alternate_server_len)96 StunUsageBindReturn stun_usage_bind_process (StunMessage *msg,
97 struct sockaddr *addr, socklen_t *addrlen,
98 struct sockaddr *alternate_server, socklen_t *alternate_server_len)
99 {
100 int code = -1;
101 StunMessageReturn val;
102 union {
103 struct sockaddr *sa;
104 struct sockaddr_storage *sas;
105 } sa;
106
107 sa.sa = addr;
108
109 if (stun_message_get_method (msg) != STUN_BINDING)
110 return STUN_USAGE_BIND_RETURN_INVALID;
111
112 switch (stun_message_get_class (msg))
113 {
114 case STUN_REQUEST:
115 case STUN_INDICATION:
116 return STUN_USAGE_BIND_RETURN_INVALID;
117
118 case STUN_RESPONSE:
119 break;
120
121 case STUN_ERROR:
122 if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) {
123 /* missing ERROR-CODE: ignore message */
124 return STUN_USAGE_BIND_RETURN_INVALID;
125 }
126
127 /* NOTE: currently we ignore unauthenticated messages if the context
128 * is authenticated, for security reasons. */
129 stun_debug (" STUN error message received (code: %d)", code);
130
131 /* ALTERNATE-SERVER mechanism */
132 if ((code / 100) == 3) {
133 union {
134 struct sockaddr *sa;
135 struct sockaddr_storage *sas;
136 } alternate_sa;
137
138 alternate_sa.sa = alternate_server;
139 if (alternate_server && alternate_server_len) {
140 if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER,
141 alternate_sa.sas,
142 alternate_server_len) != STUN_MESSAGE_RETURN_SUCCESS) {
143 stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute");
144 return STUN_USAGE_BIND_RETURN_ERROR;
145 }
146 } else {
147 if (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER)) {
148 stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute");
149 return STUN_USAGE_BIND_RETURN_ERROR;
150 }
151 }
152
153 stun_debug ("Found alternate server");
154 return STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER;
155
156 }
157 return STUN_USAGE_BIND_RETURN_ERROR;
158
159 default:
160 /* Fall through. */
161 break;
162 }
163
164 stun_debug ("Received %u-bytes STUN message", stun_message_length (msg));
165
166 val = stun_message_find_xor_addr (msg,
167 STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, sa.sas,
168 addrlen);
169 if (val != STUN_MESSAGE_RETURN_SUCCESS)
170 {
171 stun_debug (" No XOR-MAPPED-ADDRESS: %d", val);
172 val = stun_message_find_addr (msg,
173 STUN_ATTRIBUTE_MAPPED_ADDRESS, sa.sas,
174 addrlen);
175 if (val != STUN_MESSAGE_RETURN_SUCCESS)
176 {
177 stun_debug (" No MAPPED-ADDRESS: %d", val);
178 return STUN_USAGE_BIND_RETURN_ERROR;
179 }
180 }
181
182 stun_debug (" Mapped address found!");
183 return STUN_USAGE_BIND_RETURN_SUCCESS;
184
185 }
186
187
188 /** Binding keep-alive (Binding discovery indication!) */
189
190 size_t
stun_usage_bind_keepalive(StunAgent * agent,StunMessage * msg,uint8_t * buf,size_t len)191 stun_usage_bind_keepalive (StunAgent *agent, StunMessage *msg,
192 uint8_t *buf, size_t len)
193 {
194
195 stun_agent_init_indication (agent, msg,
196 buf, len, STUN_BINDING);
197 return stun_agent_finish_message (agent, msg, NULL, 0);
198 }
199
200
201
202 typedef struct stun_trans_s
203 {
204
205 int fd;
206 int own_fd;
207 socklen_t dstlen;
208 struct sockaddr_storage dst;
209 } StunTransport;
210
211
212 typedef enum {
213 STUN_USAGE_TRANS_RETURN_SUCCESS,
214 STUN_USAGE_TRANS_RETURN_ERROR,
215 STUN_USAGE_TRANS_RETURN_RETRY,
216 STUN_USAGE_TRANS_RETURN_INVALID_ADDRESS,
217 STUN_USAGE_TRANS_RETURN_UNSUPPORTED,
218 } StunUsageTransReturn;
219
220
221
222
223 static StunUsageTransReturn
stun_trans_init(StunTransport * tr,int fd,const struct sockaddr * srv,socklen_t srvlen)224 stun_trans_init (StunTransport *tr, int fd,
225 const struct sockaddr *srv, socklen_t srvlen)
226 {
227 assert (fd != -1);
228
229 if ((size_t) srvlen > sizeof (tr->dst))
230 return STUN_USAGE_TRANS_RETURN_INVALID_ADDRESS;
231
232 tr->own_fd = -1;
233 tr->fd = fd;
234
235 tr->dstlen = srvlen;
236 memcpy (&tr->dst, srv, srvlen);
237
238 return STUN_USAGE_TRANS_RETURN_SUCCESS;
239 }
240
241
242 /*
243 * Creates and connects a socket. This is useful when a socket is to be used
244 * for multiple consecutive transactions (e.g. TURN).
245 */
stun_socket(int family,int type,int proto)246 static int stun_socket (int family, int type, int proto)
247 {
248 #ifdef _WIN32
249 unsigned long set_nonblock=1;
250 #endif
251
252 int fd = socket (family, type, proto);
253 if (fd == -1)
254 return -1;
255
256 #ifdef FD_CLOEXEC
257 fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC);
258 #endif
259 #ifdef O_NONBLOCK
260 fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK);
261 #elif defined _WIN32
262 ioctlsocket(fd, FIONBIO, &set_nonblock);
263 #endif
264
265 #ifdef MSG_ERRQUEUE
266 if (type == SOCK_DGRAM)
267 {
268 /* Linux specifics for ICMP errors on non-connected sockets */
269 int yes = 1;
270 switch (family)
271 {
272 case AF_INET:
273 setsockopt (fd, SOL_IP, IP_RECVERR, &yes, sizeof (yes));
274 break;
275 case AF_INET6:
276 setsockopt (fd, SOL_IPV6, IPV6_RECVERR, &yes, sizeof (yes));
277 break;
278 default:
279 /* Nothing to do. */
280 break;
281 }
282 }
283 #endif
284
285 return fd;
286 }
287
288
289 static StunUsageTransReturn
stun_trans_create(StunTransport * tr,int type,int proto,const struct sockaddr * srv,socklen_t srvlen)290 stun_trans_create (StunTransport *tr, int type, int proto,
291 const struct sockaddr *srv, socklen_t srvlen)
292 {
293 StunUsageTransReturn val = STUN_USAGE_TRANS_RETURN_ERROR;
294 int fd;
295
296 if ((size_t) srvlen < sizeof(*srv))
297 return STUN_USAGE_TRANS_RETURN_INVALID_ADDRESS;
298
299 fd = stun_socket (srv->sa_family, type, proto);
300 if (fd == -1)
301 return STUN_USAGE_TRANS_RETURN_ERROR;
302
303 if (type != SOCK_DGRAM) {
304 if (connect (fd, srv, srvlen) &&
305 #ifdef _WIN32
306 (WSAGetLastError () != WSAEINPROGRESS)) {
307 #else
308 (errno != EINPROGRESS)) {
309 #endif
310 goto error;
311 }
312 val = stun_trans_init (tr, fd, NULL, 0);
313 } else {
314 val = stun_trans_init (tr, fd, srv, srvlen);
315 }
316
317 if (val)
318 goto error;
319
320 tr->own_fd = tr->fd;
321 return STUN_USAGE_TRANS_RETURN_SUCCESS;
322
323 error:
324 close (fd);
325 return val;
326 }
327
328
329 static void stun_trans_deinit (StunTransport *tr)
330 {
331 int saved = errno;
332
333 assert (tr->fd != -1);
334
335 if (tr->own_fd != -1)
336 close (tr->own_fd);
337
338 tr->own_fd = -1;
339 tr->fd = -1;
340
341 errno = saved;
342 }
343
344
345 #ifndef MSG_DONTWAIT
346 # define MSG_DONTWAIT 0
347 #endif
348 #ifndef MSG_NOSIGNAL
349 # define MSG_NOSIGNAL 0
350 #endif
351
352
353 static int stun_err_dequeue (int fd)
354 {
355 #ifdef MSG_ERRQUEUE
356 struct msghdr hdr;
357 int saved_errno = errno, ret;
358
359 memset (&hdr, 0, sizeof (hdr));
360 ret = (recvmsg (fd, &hdr, MSG_ERRQUEUE) >= 0);
361 errno = saved_errno;
362 return ret;
363 #else
364 (void) fd;
365 return 0;
366 #endif
367 }
368
369
370 static ssize_t
371 stun_trans_sendto (StunTransport *tr, const uint8_t *buf, size_t len,
372 const struct sockaddr *dst, socklen_t dstlen)
373 {
374 static const int flags = MSG_DONTWAIT | MSG_NOSIGNAL;
375 ssize_t val;
376
377 do
378 {
379 if (dstlen > 0)
380 val = sendto (tr->fd, (void *)buf, len, flags, dst, dstlen);
381 else
382 val = send (tr->fd, (void *)buf, len, flags);
383 }
384 while ((val == -1) && stun_err_dequeue (tr->fd));
385
386 return val;
387 }
388
389
390 static ssize_t
391 stun_trans_recvfrom (StunTransport *tr, uint8_t *buf, size_t maxlen,
392 struct sockaddr_storage * dst,
393 socklen_t * dstlen)
394 {
395 static const int flags = MSG_DONTWAIT | MSG_NOSIGNAL;
396 ssize_t val;
397
398 if (dstlen != NULL)
399 val = recvfrom (tr->fd, (void *)buf, maxlen, flags, (struct sockaddr *) dst,
400 dstlen);
401 else
402 val = recv (tr->fd, (void *)buf, maxlen, flags);
403
404 if (val == -1)
405 stun_err_dequeue (tr->fd);
406
407 return val;
408 }
409
410
411 static ssize_t
412 stun_trans_send (StunTransport *tr, const uint8_t *buf, size_t len)
413 {
414 struct sockaddr *conv;
415
416 conv = (struct sockaddr *) &tr->dst;
417
418 return stun_trans_sendto (tr, buf, len, conv, tr->dstlen);
419 }
420
421 static ssize_t
422 stun_trans_recv (StunTransport *tr, uint8_t *buf, size_t maxlen)
423 {
424 return stun_trans_recvfrom (tr, buf, maxlen, NULL, NULL);
425 }
426
427
428 #ifdef HAVE_POLL
429 static int stun_trans_fd (const StunTransport *tr)
430 {
431 assert (tr != NULL);
432 return tr->fd;
433 }
434 #endif
435
436
437 /*
438 * Waits for a response or timeout to occur.
439 *
440 * @return ETIMEDOUT if the transaction has timed out, or 0 if an incoming
441 * message needs to be processed.
442 */
443 static StunUsageTransReturn
444 stun_trans_poll (StunTransport *tr, unsigned int delay)
445 {
446 #ifdef HAVE_POLL
447 struct pollfd ufd;
448
449 memset (&ufd, 0, sizeof (ufd));
450 ufd.fd = stun_trans_fd (tr);
451
452 ufd.events |= POLLIN;
453
454 if (poll (&ufd, 1, delay) <= 0) {
455 return STUN_USAGE_TRANS_RETURN_RETRY;
456 }
457
458 return STUN_USAGE_TRANS_RETURN_SUCCESS;
459 #else
460 (void)tr;
461 return STUN_USAGE_TRANS_RETURN_UNSUPPORTED;
462 #endif
463 }
464
465
466
467 /** Blocking mode STUN binding discovery */
468 StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv,
469 socklen_t srvlen, struct sockaddr_storage *addr, socklen_t *addrlen)
470 {
471 StunTimer timer;
472 StunTransport trans;
473 StunAgent agent;
474 StunMessage req;
475 uint8_t req_buf[STUN_MAX_MESSAGE_SIZE];
476 StunMessage msg;
477 uint8_t buf[STUN_MAX_MESSAGE_SIZE];
478 StunValidationStatus valid;
479 size_t len;
480 StunUsageTransReturn ret;
481 int val;
482 struct sockaddr_storage alternate_server = { AF_UNSPEC } ;
483 socklen_t alternate_server_len = sizeof (alternate_server);
484 StunUsageBindReturn bind_ret;
485
486 trans.fd = -1;
487
488 stun_agent_init (&agent, STUN_ALL_KNOWN_ATTRIBUTES,
489 STUN_COMPATIBILITY_RFC3489, 0);
490
491 len = stun_usage_bind_create (&agent, &req, req_buf, sizeof(req_buf));
492
493 ret = stun_trans_create (&trans, SOCK_DGRAM, 0, srv, srvlen);
494 if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) {
495 stun_debug ("STUN transaction failed: couldn't create transport.");
496 bind_ret = STUN_USAGE_BIND_RETURN_ERROR;
497 goto done;
498 }
499
500 val = stun_trans_send (&trans, req_buf, len);
501 if (val < -1) {
502 stun_debug ("STUN transaction failed: couldn't send request.");
503 bind_ret = STUN_USAGE_BIND_RETURN_ERROR;
504 goto done;
505 }
506
507 stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT,
508 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);
509 stun_debug ("STUN transaction started (timeout %dms).",
510 stun_timer_remainder (&timer));
511
512 do
513 {
514 for (;;) {
515 unsigned delay = stun_timer_remainder (&timer);
516 ret = stun_trans_poll (&trans, delay);
517 if (ret == STUN_USAGE_TRANS_RETURN_RETRY) {
518 switch (stun_timer_refresh (&timer)) {
519 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
520 stun_debug ("STUN transaction failed: time out.");
521 bind_ret = STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error!
522 goto done;
523 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
524 stun_debug ("STUN transaction retransmitted (timeout %dms).",
525 stun_timer_remainder (&timer));
526 val = stun_trans_send (&trans, req_buf, len);
527 if (val < -1) {
528 stun_debug ("STUN transaction failed: couldn't resend request.");
529 bind_ret = STUN_USAGE_BIND_RETURN_ERROR;
530 goto done;
531 }
532 continue;
533 case STUN_USAGE_TIMER_RETURN_SUCCESS:
534 default:
535 /* Fall through. */
536 break;
537 }
538 }
539 val = stun_trans_recv (&trans, buf, sizeof (buf));
540 if (val >= 0) {
541 break;
542 }
543 }
544
545 valid = stun_agent_validate (&agent, &msg, buf, val, NULL, NULL);
546 if (valid == STUN_VALIDATION_UNKNOWN_ATTRIBUTE)
547 {
548 bind_ret = STUN_USAGE_BIND_RETURN_ERROR;
549 goto done;
550 }
551
552 if (valid != STUN_VALIDATION_SUCCESS) {
553 ret = STUN_USAGE_TRANS_RETURN_RETRY;
554 } else {
555 bind_ret = stun_usage_bind_process (&msg, (struct sockaddr *) addr,
556 addrlen, (struct sockaddr *) &alternate_server, &alternate_server_len);
557 if (bind_ret == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) {
558 stun_trans_deinit (&trans);
559
560 assert (alternate_server.ss_family != AF_UNSPEC);
561
562 ret = stun_trans_create (&trans, SOCK_DGRAM, 0,
563 (struct sockaddr *) &alternate_server, alternate_server_len);
564
565 if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) {
566 bind_ret = STUN_USAGE_BIND_RETURN_ERROR;
567 goto done;
568 }
569
570 val = stun_trans_send (&trans, req_buf, len);
571 if (val < -1)
572 {
573 bind_ret = STUN_USAGE_BIND_RETURN_ERROR;
574 goto done;
575 }
576
577 stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT,
578 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);
579 ret = STUN_USAGE_TRANS_RETURN_RETRY;
580 } else if (bind_ret == STUN_USAGE_BIND_RETURN_INVALID) {
581 ret = STUN_USAGE_TRANS_RETURN_RETRY;
582 } else {
583 break;
584 }
585 }
586 }
587 while (ret == STUN_USAGE_TRANS_RETURN_RETRY);
588
589 done:
590 if (trans.fd != -1)
591 stun_trans_deinit (&trans);
592
593 return bind_ret;
594 }
595