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
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.
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.
46 #include "win32_common.h"
47 #define close closesocket
49 #include <sys/types.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
67 #include "stun/stunagent.h"
78 # define SOL_IP IPPROTO_IP
82 # define SOL_IPV6 IPPROTO_IPV6
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
)
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
))
109 case STUN_INDICATION
:
110 return STUN_USAGE_BIND_RETURN_INVALID
;
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
,
130 alternate_server_len
) != STUN_MESSAGE_RETURN_SUCCESS
) {
131 stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute\n");
132 return STUN_USAGE_BIND_RETURN_ERROR
;
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!) */
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
190 struct sockaddr_storage dst
;
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
)
211 if ((size_t) srvlen
> sizeof (tr
->dst
))
212 return STUN_USAGE_TRANS_RETURN_INVALID_ADDRESS
;
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
)
231 unsigned long set_nonblock
=1;
234 int fd
= socket (family
, type
, proto
);
239 fcntl (fd
, F_SETFD
, fcntl (fd
, F_GETFD
) | FD_CLOEXEC
);
242 fcntl (fd
, F_SETFL
, fcntl (fd
, F_GETFL
) | O_NONBLOCK
);
244 ioctlsocket(fd
, FIONBIO
, &set_nonblock
);
248 if (type
== SOCK_DGRAM
)
250 /* Linux specifics for ICMP errors on non-connected sockets */
255 setsockopt (fd
, SOL_IP
, IP_RECVERR
, &yes
, sizeof (yes
));
258 setsockopt (fd
, SOL_IPV6
, IPV6_RECVERR
, &yes
, sizeof (yes
));
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
;
275 if ((size_t) srvlen
< sizeof(*srv
))
276 return STUN_USAGE_TRANS_RETURN_INVALID_ADDRESS
;
278 fd
= stun_socket (srv
->sa_family
, type
, proto
);
280 return STUN_USAGE_TRANS_RETURN_ERROR
;
282 if (type
!= SOCK_DGRAM
) {
283 if (connect (fd
, srv
, srvlen
) &&
285 (WSAGetLastError () != WSAEINPROGRESS
)) {
287 (errno
!= EINPROGRESS
)) {
291 val
= stun_trans_init (tr
, fd
, NULL
, 0);
293 val
= stun_trans_init (tr
, fd
, srv
, srvlen
);
300 return STUN_USAGE_TRANS_RETURN_SUCCESS
;
308 static void stun_trans_deinit (StunTransport
*tr
)
312 assert (tr
->fd
!= -1);
314 if (tr
->own_fd
!= -1)
325 # define MSG_DONTWAIT 0
328 # define MSG_NOSIGNAL 0
332 static int stun_err_dequeue (int fd
)
336 int saved_errno
= errno
, ret
;
338 memset (&hdr
, 0, sizeof (hdr
));
339 ret
= (recvmsg (fd
, &hdr
, MSG_ERRQUEUE
) >= 0);
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
;
359 val
= sendto (tr
->fd
, (void *)buf
, len
, flags
, dst
, dstlen
);
361 val
= send (tr
->fd
, (void *)buf
, len
, flags
);
363 while ((val
== -1) && stun_err_dequeue (tr
->fd
));
370 stun_trans_recvfrom (StunTransport
*tr
, uint8_t *buf
, size_t maxlen
,
371 struct sockaddr
* dst
,
374 static const int flags
= MSG_DONTWAIT
| MSG_NOSIGNAL
;
378 val
= recvfrom (tr
->fd
, (void *)buf
, maxlen
, flags
, dst
, dstlen
);
380 val
= recv (tr
->fd
, (void *)buf
, maxlen
, flags
);
383 stun_err_dequeue (tr
->fd
);
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
);
397 stun_trans_recv (StunTransport
*tr
, uint8_t *buf
, size_t maxlen
)
399 return stun_trans_recvfrom (tr
, buf
, maxlen
, NULL
, NULL
);
404 static int stun_trans_fd (const StunTransport
*tr
)
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
)
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
;
436 return STUN_USAGE_TRANS_RETURN_UNSUPPORTED
;
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
)
450 uint8_t req_buf
[STUN_MAX_MESSAGE_SIZE
];
452 uint8_t buf
[STUN_MAX_MESSAGE_SIZE
];
453 StunValidationStatus valid
;
455 StunUsageTransReturn ret
;
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
);
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
));
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
);
498 stun_debug ("STUN transaction failed: couldn't resend request.\n");
499 return STUN_USAGE_BIND_RETURN_ERROR
;
502 case STUN_USAGE_TIMER_RETURN_SUCCESS
:
506 val
= stun_trans_recv (&trans
, buf
, sizeof (buf
));
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
;
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
);
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
;
545 while (ret
== STUN_USAGE_TRANS_RETURN_RETRY
);