Recognize a matching check if it's an INVALID message (error with no error-code)
[sipe-libnice.git] / stun / usages / bind.c
blob01f0fecef0aeaeb2f248756afc211ed27db74f8a
1 /*
2 * This file is part of the Nice GLib ICE library.
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
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/
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.
19 * The Original Code is the Nice GLib ICE library.
21 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
22 * Corporation. All Rights Reserved.
24 * Contributors:
25 * Youness Alaoui, Collabora Ltd.
26 * Rémi Denis-Courmont, Nokia
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.
39 #ifdef HAVE_CONFIG_H
40 # include <config.h>
41 #endif
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
61 #ifdef HAVE_POLL
62 # include <poll.h>
63 #endif
66 #include "bind.h"
67 #include "stun/stunagent.h"
69 #include <assert.h>
70 #include <string.h>
71 #include <stdlib.h>
72 #include <time.h>
73 #include <errno.h>
74 #include "timer.h"
77 #ifndef SOL_IP
78 # define SOL_IP IPPROTO_IP
79 #endif
81 #ifndef SOL_IPV6
82 # define SOL_IPV6 IPPROTO_IPV6
83 #endif
86 /** Non-blocking mode STUN binding discovery */
88 size_t stun_usage_bind_create (StunAgent *agent, StunMessage *msg,
89 uint8_t *buffer, size_t buffer_len)
91 stun_agent_init_request (agent, msg, buffer, buffer_len, STUN_BINDING);
93 return stun_agent_finish_message (agent, msg, NULL, 0);
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)
100 int code = -1;
101 StunMessageReturn val;
103 if (stun_message_get_method (msg) != STUN_BINDING)
104 return STUN_USAGE_BIND_RETURN_INVALID;
106 switch (stun_message_get_class (msg))
108 case STUN_REQUEST:
109 case STUN_INDICATION:
110 return STUN_USAGE_BIND_RETURN_INVALID;
112 case STUN_RESPONSE:
113 break;
115 case STUN_ERROR:
116 if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) {
117 /* missing ERROR-CODE: ignore message */
118 return STUN_USAGE_BIND_RETURN_INVALID;
121 /* NOTE: currently we ignore unauthenticated messages if the context
122 * is authenticated, for security reasons. */
123 stun_debug (" STUN error message received (code: %d)\n", code);
125 /* ALTERNATE-SERVER mechanism */
126 if ((code / 100) == 3) {
127 if (alternate_server && alternate_server_len) {
128 if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER,
129 alternate_server,
130 alternate_server_len) != STUN_MESSAGE_RETURN_SUCCESS) {
131 stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute\n");
132 return STUN_USAGE_BIND_RETURN_ERROR;
134 } else {
135 if (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER)) {
136 stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute\n");
137 return STUN_USAGE_BIND_RETURN_ERROR;
141 stun_debug ("Found alternate server\n");
142 return STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER;
145 return STUN_USAGE_BIND_RETURN_ERROR;
148 stun_debug ("Received %u-bytes STUN message\n", stun_message_length (msg));
150 val = stun_message_find_xor_addr (msg,
151 STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, addr, addrlen);
152 if (val != STUN_MESSAGE_RETURN_SUCCESS)
154 stun_debug (" No XOR-MAPPED-ADDRESS: %d\n", val);
155 val = stun_message_find_addr (msg,
156 STUN_ATTRIBUTE_MAPPED_ADDRESS, addr, addrlen);
157 if (val != STUN_MESSAGE_RETURN_SUCCESS)
159 stun_debug (" No MAPPED-ADDRESS: %d\n", val);
160 return STUN_USAGE_BIND_RETURN_ERROR;
164 stun_debug (" Mapped address found!\n");
165 return STUN_USAGE_BIND_RETURN_SUCCESS;
170 /** Binding keep-alive (Binding discovery indication!) */
172 size_t
173 stun_usage_bind_keepalive (StunAgent *agent, StunMessage *msg,
174 uint8_t *buf, size_t len)
177 stun_agent_init_indication (agent, msg,
178 buf, len, STUN_BINDING);
179 return stun_agent_finish_message (agent, msg, NULL, 0);
184 typedef struct stun_trans_s
187 int fd;
188 int own_fd;
189 socklen_t dstlen;
190 struct sockaddr_storage dst;
191 } StunTransport;
194 typedef enum {
195 STUN_USAGE_TRANS_RETURN_SUCCESS,
196 STUN_USAGE_TRANS_RETURN_ERROR,
197 STUN_USAGE_TRANS_RETURN_RETRY,
198 STUN_USAGE_TRANS_RETURN_INVALID_ADDRESS,
199 STUN_USAGE_TRANS_RETURN_UNSUPPORTED,
200 } StunUsageTransReturn;
205 static StunUsageTransReturn
206 stun_trans_init (StunTransport *tr, int fd,
207 const struct sockaddr *srv, socklen_t srvlen)
209 assert (fd != -1);
211 if ((size_t) srvlen > sizeof (tr->dst))
212 return STUN_USAGE_TRANS_RETURN_INVALID_ADDRESS;
214 tr->own_fd = -1;
215 tr->fd = fd;
217 tr->dstlen = srvlen;
218 memcpy (&tr->dst, srv, srvlen);
220 return STUN_USAGE_TRANS_RETURN_SUCCESS;
225 * Creates and connects a socket. This is useful when a socket is to be used
226 * for multiple consecutive transactions (e.g. TURN).
228 static int stun_socket (int family, int type, int proto)
230 #ifdef _WIN32
231 unsigned long set_nonblock=1;
232 #endif
234 int fd = socket (family, type, proto);
235 if (fd == -1)
236 return -1;
238 #ifdef FD_CLOEXEC
239 fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC);
240 #endif
241 #ifdef O_NONBLOCK
242 fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK);
243 #elif defined _WIN32
244 ioctlsocket(fd, FIONBIO, &set_nonblock);
245 #endif
247 #ifdef MSG_ERRQUEUE
248 if (type == SOCK_DGRAM)
250 /* Linux specifics for ICMP errors on non-connected sockets */
251 int yes = 1;
252 switch (family)
254 case AF_INET:
255 setsockopt (fd, SOL_IP, IP_RECVERR, &yes, sizeof (yes));
256 break;
257 case AF_INET6:
258 setsockopt (fd, SOL_IPV6, IPV6_RECVERR, &yes, sizeof (yes));
259 break;
262 #endif
264 return fd;
268 static StunUsageTransReturn
269 stun_trans_create (StunTransport *tr, int type, int proto,
270 const struct sockaddr *srv, socklen_t srvlen)
272 StunUsageTransReturn val = STUN_USAGE_TRANS_RETURN_ERROR;
273 int fd;
275 if ((size_t) srvlen < sizeof(*srv))
276 return STUN_USAGE_TRANS_RETURN_INVALID_ADDRESS;
278 fd = stun_socket (srv->sa_family, type, proto);
279 if (fd == -1)
280 return STUN_USAGE_TRANS_RETURN_ERROR;
282 if (type != SOCK_DGRAM) {
283 if (connect (fd, srv, srvlen) &&
284 #ifdef _WIN32
285 (WSAGetLastError () != WSAEINPROGRESS)) {
286 #else
287 (errno != EINPROGRESS)) {
288 #endif
289 goto error;
291 val = stun_trans_init (tr, fd, NULL, 0);
292 } else {
293 val = stun_trans_init (tr, fd, srv, srvlen);
296 if (val)
297 goto error;
299 tr->own_fd = tr->fd;
300 return STUN_USAGE_TRANS_RETURN_SUCCESS;
302 error:
303 close (fd);
304 return val;
308 static void stun_trans_deinit (StunTransport *tr)
310 int saved = errno;
312 assert (tr->fd != -1);
314 if (tr->own_fd != -1)
315 close (tr->own_fd);
317 tr->own_fd = -1;
318 tr->fd = -1;
320 errno = saved;
324 #ifndef MSG_DONTWAIT
325 # define MSG_DONTWAIT 0
326 #endif
327 #ifndef MSG_NOSIGNAL
328 # define MSG_NOSIGNAL 0
329 #endif
332 static int stun_err_dequeue (int fd)
334 #ifdef MSG_ERRQUEUE
335 struct msghdr hdr;
336 int saved_errno = errno, ret;
338 memset (&hdr, 0, sizeof (hdr));
339 ret = (recvmsg (fd, &hdr, MSG_ERRQUEUE) >= 0);
340 errno = saved_errno;
341 return ret;
342 #else
343 (void) fd;
344 return 0;
345 #endif
349 static ssize_t
350 stun_trans_sendto (StunTransport *tr, const uint8_t *buf, size_t len,
351 const struct sockaddr *dst, socklen_t dstlen)
353 static const int flags = MSG_DONTWAIT | MSG_NOSIGNAL;
354 ssize_t val;
358 if (dstlen > 0)
359 val = sendto (tr->fd, (void *)buf, len, flags, dst, dstlen);
360 else
361 val = send (tr->fd, (void *)buf, len, flags);
363 while ((val == -1) && stun_err_dequeue (tr->fd));
365 return val;
369 static ssize_t
370 stun_trans_recvfrom (StunTransport *tr, uint8_t *buf, size_t maxlen,
371 struct sockaddr * dst,
372 socklen_t * dstlen)
374 static const int flags = MSG_DONTWAIT | MSG_NOSIGNAL;
375 ssize_t val;
377 if (dstlen != NULL)
378 val = recvfrom (tr->fd, (void *)buf, maxlen, flags, dst, dstlen);
379 else
380 val = recv (tr->fd, (void *)buf, maxlen, flags);
382 if (val == -1)
383 stun_err_dequeue (tr->fd);
385 return val;
389 static ssize_t
390 stun_trans_send (StunTransport *tr, const uint8_t *buf, size_t len)
392 return stun_trans_sendto (tr, buf, len,
393 (struct sockaddr *)&tr->dst, tr->dstlen);
396 static ssize_t
397 stun_trans_recv (StunTransport *tr, uint8_t *buf, size_t maxlen)
399 return stun_trans_recvfrom (tr, buf, maxlen, NULL, NULL);
403 #ifdef HAVE_POLL
404 static int stun_trans_fd (const StunTransport *tr)
406 assert (tr != NULL);
407 return tr->fd;
409 #endif
413 * Waits for a response or timeout to occur.
415 * @return ETIMEDOUT if the transaction has timed out, or 0 if an incoming
416 * message needs to be processed.
418 static StunUsageTransReturn
419 stun_trans_poll (StunTransport *tr, unsigned int delay)
421 #ifdef HAVE_POLL
422 struct pollfd ufd;
424 memset (&ufd, 0, sizeof (ufd));
425 ufd.fd = stun_trans_fd (tr);
427 ufd.events |= POLLIN;
429 if (poll (&ufd, 1, delay) <= 0) {
430 return STUN_USAGE_TRANS_RETURN_RETRY;
433 return STUN_USAGE_TRANS_RETURN_SUCCESS;
434 #else
435 (void)tr;
436 return STUN_USAGE_TRANS_RETURN_UNSUPPORTED;
437 #endif
442 /** Blocking mode STUN binding discovery */
443 StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv,
444 socklen_t srvlen, struct sockaddr *addr, socklen_t *addrlen)
446 StunTimer timer;
447 StunTransport trans;
448 StunAgent agent;
449 StunMessage req;
450 uint8_t req_buf[STUN_MAX_MESSAGE_SIZE];
451 StunMessage msg;
452 uint8_t buf[STUN_MAX_MESSAGE_SIZE];
453 StunValidationStatus valid;
454 size_t len;
455 StunUsageTransReturn ret;
456 int val;
457 struct sockaddr_storage alternate_server;
458 socklen_t alternate_server_len = sizeof (alternate_server);
459 StunUsageBindReturn bind_ret;
461 stun_agent_init (&agent, STUN_ALL_KNOWN_ATTRIBUTES,
462 STUN_COMPATIBILITY_RFC3489, 0);
464 len = stun_usage_bind_create (&agent, &req, req_buf, sizeof(req_buf));
466 ret = stun_trans_create (&trans, SOCK_DGRAM, 0, srv, srvlen);
467 if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) {
468 stun_debug ("STUN transaction failed: couldn't create transport.\n");
469 return STUN_USAGE_BIND_RETURN_ERROR;
472 val = stun_trans_send (&trans, req_buf, len);
473 if (val < -1) {
474 stun_debug ("STUN transaction failed: couldn't send request.\n");
475 return STUN_USAGE_BIND_RETURN_ERROR;
478 stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT,
479 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);
480 stun_debug ("STUN transaction started (timeout %dms).\n",
481 stun_timer_remainder (&timer));
485 for (;;) {
486 unsigned delay = stun_timer_remainder (&timer);
487 ret = stun_trans_poll (&trans, delay);
488 if (ret == STUN_USAGE_TRANS_RETURN_RETRY) {
489 switch (stun_timer_refresh (&timer)) {
490 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
491 stun_debug ("STUN transaction failed: time out.\n");
492 return STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error!
493 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
494 stun_debug ("STUN transaction retransmitted (timeout %dms).\n",
495 stun_timer_remainder (&timer));
496 val = stun_trans_send (&trans, req_buf, len);
497 if (val < -1) {
498 stun_debug ("STUN transaction failed: couldn't resend request.\n");
499 return STUN_USAGE_BIND_RETURN_ERROR;
501 continue;
502 case STUN_USAGE_TIMER_RETURN_SUCCESS:
503 break;
506 val = stun_trans_recv (&trans, buf, sizeof (buf));
507 if (val >= 0) {
508 break;
512 valid = stun_agent_validate (&agent, &msg, buf, val, NULL, NULL);
513 if (valid == STUN_VALIDATION_UNKNOWN_ATTRIBUTE)
514 return STUN_USAGE_BIND_RETURN_ERROR;
516 if (valid != STUN_VALIDATION_SUCCESS) {
517 ret = STUN_USAGE_TRANS_RETURN_RETRY;
518 } else {
519 bind_ret = stun_usage_bind_process (&msg, addr, addrlen,
520 (struct sockaddr *) &alternate_server, &alternate_server_len);
521 if (bind_ret == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) {
522 stun_trans_deinit (&trans);
524 ret = stun_trans_create (&trans, SOCK_DGRAM, 0,
525 (struct sockaddr *) &alternate_server, alternate_server_len);
527 if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) {
528 return STUN_USAGE_BIND_RETURN_ERROR;
531 val = stun_trans_send (&trans, req_buf, len);
532 if (val < -1)
533 return STUN_USAGE_BIND_RETURN_ERROR;
535 stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT,
536 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);
537 ret = STUN_USAGE_TRANS_RETURN_RETRY;
538 } else if (bind_ret == STUN_USAGE_BIND_RETURN_INVALID) {
539 ret = STUN_USAGE_TRANS_RETURN_RETRY;
540 } else {
541 break;
545 while (ret == STUN_USAGE_TRANS_RETURN_RETRY);
547 return bind_ret;