2 * This file is part of the Nice GLib ICE library.
4 * (C) 2007 Nokia Corporation. All rights reserved.
5 * Contact: Rémi Denis-Courmont
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is the Nice GLib ICE library.
19 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
20 * Corporation. All Rights Reserved.
23 * Rémi Denis-Courmont, Nokia
25 * Alternatively, the contents of this file may be used under the terms of the
26 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
27 * case the provisions of LGPL are applicable instead of those above. If you
28 * wish to allow use of your version of this file only under the terms of the
29 * LGPL and not to allow others to use your version of this file under the
30 * MPL, indicate your decision by deleting the provisions above and replace
31 * them with the notice and other provisions required by the LGPL. If you do
32 * not delete the provisions above, a recipient may use your version of this
33 * file under either the MPL or the LGPL.
48 #include <sys/types.h>
51 #include <sys/socket.h>
53 #include <netinet/in.h>
60 # define SOL_IP IPPROTO_IP
61 # define SOL_IPV6 IPPROTO_IPV6
64 #ifndef IPV6_RECVPKTINFO
65 # define IPV6_RECVPKTINFO IPV6_PKTINFO
68 /** Default port for STUN binding discovery */
69 #define IPPORT_STUN 3478
71 #include "stun/stunagent.h"
74 static const uint16_t known_attributes
[] = {
79 * Creates a listening socket
81 int listen_socket (int fam
, int type
, int proto
, unsigned int port
)
84 int fd
= socket (fam
, type
, proto
);
87 struct sockaddr_in in
;
88 struct sockaddr_in6 in6
;
89 struct sockaddr_storage storage
;
93 perror ("Error opening IP port");
99 memset (&addr
, 0, sizeof (addr
));
100 addr
.storage
.ss_family
= fam
;
102 addr
.storage
.ss_len
= sizeof (addr
);
108 addr
.in
.sin_port
= htons (port
);
113 setsockopt (fd
, SOL_IPV6
, IPV6_V6ONLY
, &yes
, sizeof (yes
));
115 addr
.in6
.sin6_port
= htons (port
);
119 if (bind (fd
, (struct sockaddr
*)&addr
, sizeof (addr
)))
121 perror ("Error opening IP port");
125 if ((type
== SOCK_DGRAM
) || (type
== SOCK_RAW
))
131 setsockopt (fd
, SOL_IP
, IP_RECVERR
, &yes
, sizeof (yes
));
137 setsockopt (fd
, SOL_IPV6
, IPV6_RECVERR
, &yes
, sizeof (yes
));
144 if (listen (fd
, INT_MAX
))
146 perror ("Error opening IP port");
159 /** Dequeue error from a socket if applicable */
160 static int recv_err (int fd
)
164 memset (&hdr
, 0, sizeof (hdr
));
165 return recvmsg (fd
, &hdr
, MSG_ERRQUEUE
) >= 0;
170 /** Receives a message or dequeues an error from a socket */
171 ssize_t
recv_safe (int fd
, struct msghdr
*msg
)
173 ssize_t len
= recvmsg (fd
, msg
, 0);
177 if (msg
->msg_flags
& MSG_TRUNC
)
187 /** Sends a message through a socket */
188 ssize_t
send_safe (int fd
, const struct msghdr
*msg
)
193 len
= sendmsg (fd
, msg
, 0);
194 while ((len
== -1) && (recv_err (fd
) == 0));
200 static int dgram_process (int sock
, StunAgent
*oldagent
, StunAgent
*newagent
)
202 struct sockaddr_storage addr
;
203 uint8_t buf
[STUN_MAX_MESSAGE_SIZE
];
204 char ctlbuf
[CMSG_SPACE (sizeof (struct in6_pktinfo
))];
205 struct iovec iov
= { buf
, sizeof (buf
) };
207 StunMessage response
;
208 StunValidationStatus validation
;
209 StunAgent
*agent
= NULL
;
213 .msg_name
= (struct sockaddr
*)&addr
,
214 .msg_namelen
= sizeof (addr
),
217 .msg_control
= ctlbuf
,
218 .msg_controllen
= sizeof (ctlbuf
)
221 size_t len
= recv_safe (sock
, &mh
);
222 if (len
== (size_t)-1)
225 validation
= stun_agent_validate (newagent
, &request
, buf
, len
, NULL
, 0);
227 if (validation
== STUN_VALIDATION_SUCCESS
) {
231 validation
= stun_agent_validate (oldagent
, &request
, buf
, len
, NULL
, 0);
235 /* Unknown attributes */
236 if (validation
== STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE
)
238 stun_agent_build_unknown_attributes_error (agent
, &response
, buf
,
239 sizeof (buf
), &request
);
243 /* Mal-formatted packets */
244 if (validation
!= STUN_VALIDATION_SUCCESS
||
245 stun_message_get_class (&request
) != STUN_REQUEST
) {
249 switch (stun_message_get_method (&request
))
252 stun_agent_init_response (agent
, &response
, buf
, sizeof (buf
), &request
);
253 if (stun_has_cookie (&request
))
254 stun_message_append_xor_addr (&response
,
255 STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS
,
256 mh
.msg_name
, mh
.msg_namelen
);
258 stun_message_append_addr (&response
, STUN_ATTRIBUTE_MAPPED_ADDRESS
,
259 mh
.msg_name
, mh
.msg_namelen
);
263 stun_agent_init_error (agent
, &response
, buf
, sizeof (buf
),
264 &request
, STUN_ERROR_BAD_REQUEST
);
267 iov
.iov_len
= stun_agent_finish_message (agent
, &response
, NULL
, 0);
270 len
= send_safe (sock
, &mh
);
271 return (len
< iov
.iov_len
) ? -1 : 0;
275 static int run (int family
, int protocol
, unsigned port
)
279 int sock
= listen_socket (family
, SOCK_DGRAM
, protocol
, port
);
283 stun_agent_init (&oldagent
, known_attributes
,
284 STUN_COMPATIBILITY_RFC3489
, 0);
285 stun_agent_init (&newagent
, known_attributes
,
286 STUN_COMPATIBILITY_RFC5389
, STUN_AGENT_USAGE_USE_FINGERPRINT
);
289 dgram_process (sock
, &oldagent
, &newagent
);
293 /* Pretty useless dummy signal handler...
294 * But calling exit() is needed for gcov to work properly. */
295 static void exit_handler (int signum
)
302 int main (int argc
, char *argv
[])
304 int family
= AF_INET
;
305 unsigned port
= IPPORT_STUN
;
309 int c
= getopt (argc
, argv
, "46");
326 port
= atoi (argv
[optind
++]);
328 signal (SIGINT
, exit_handler
);
329 signal (SIGTERM
, exit_handler
);
330 return run (family
, IPPROTO_UDP
, port
) ? EXIT_FAILURE
: EXIT_SUCCESS
;