Find and document every message attribute/error code and from which RFC/Draft they...
[sipe-libnice.git] / stun / tools / stund.c
blobe36b64c372b11eec938401b2d7f08047463ea1d0
1 /*
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
15 * License.
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.
22 * Contributors:
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.
36 #ifdef HAVE_CONFIG_H
37 # include <config.h>
38 #endif
40 #ifndef _WIN32
42 #include <stdio.h>
43 #include <stdint.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <signal.h>
48 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <netdb.h>
53 #include <netinet/in.h>
55 #include <unistd.h>
56 #include <errno.h>
57 #include <limits.h>
59 #ifndef SOL_IP
60 # define SOL_IP IPPROTO_IP
61 # define SOL_IPV6 IPPROTO_IPV6
62 #endif
64 #ifndef IPV6_RECVPKTINFO
65 # define IPV6_RECVPKTINFO IPV6_PKTINFO
66 #endif
68 /** Default port for STUN binding discovery */
69 #define IPPORT_STUN 3478
71 #include "stun/stunagent.h"
72 #include "stund.h"
74 static const uint16_t known_attributes[] = {
78 /**
79 * Creates a listening socket
81 int listen_socket (int fam, int type, int proto, unsigned int port)
83 int yes = 1;
84 int fd = socket (fam, type, proto);
85 union {
86 struct sockaddr addr;
87 struct sockaddr_in in;
88 struct sockaddr_in6 in6;
89 struct sockaddr_storage storage;
90 } addr;
91 if (fd == -1)
93 perror ("Error opening IP port");
94 return -1;
96 if (fd < 3)
97 goto error;
99 memset (&addr, 0, sizeof (addr));
100 addr.storage.ss_family = fam;
101 #ifdef HAVE_SA_LEN
102 addr.storage.ss_len = sizeof (addr);
103 #endif
105 switch (fam)
107 case AF_INET:
108 addr.in.sin_port = htons (port);
109 break;
111 case AF_INET6:
112 #ifdef IPV6_V6ONLY
113 setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof (yes));
114 #endif
115 addr.in6.sin6_port = htons (port);
116 break;
119 if (bind (fd, (struct sockaddr *)&addr, sizeof (addr)))
121 perror ("Error opening IP port");
122 goto error;
125 if ((type == SOCK_DGRAM) || (type == SOCK_RAW))
127 switch (fam)
129 case AF_INET:
130 #ifdef IP_RECVERR
131 setsockopt (fd, SOL_IP, IP_RECVERR, &yes, sizeof (yes));
132 #endif
133 break;
135 case AF_INET6:
136 #ifdef IPV6_RECVERR
137 setsockopt (fd, SOL_IPV6, IPV6_RECVERR, &yes, sizeof (yes));
138 #endif
139 break;
142 else
144 if (listen (fd, INT_MAX))
146 perror ("Error opening IP port");
147 goto error;
151 return fd;
153 error:
154 close (fd);
155 return -1;
159 /** Dequeue error from a socket if applicable */
160 static int recv_err (int fd)
162 #ifdef MSG_ERRQUEUE
163 struct msghdr hdr;
164 memset (&hdr, 0, sizeof (hdr));
165 return recvmsg (fd, &hdr, MSG_ERRQUEUE) >= 0;
166 #endif
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);
174 if (len == -1)
175 recv_err (fd);
176 else
177 if (msg->msg_flags & MSG_TRUNC)
179 errno = EMSGSIZE;
180 return -1;
183 return len;
187 /** Sends a message through a socket */
188 ssize_t send_safe (int fd, const struct msghdr *msg)
190 ssize_t len;
193 len = sendmsg (fd, msg, 0);
194 while ((len == -1) && (recv_err (fd) == 0));
196 return len;
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) };
206 StunMessage request;
207 StunMessage response;
208 StunValidationStatus validation;
209 StunAgent *agent = NULL;
211 struct msghdr mh =
213 .msg_name = (struct sockaddr *)&addr,
214 .msg_namelen = sizeof (addr),
215 .msg_iov = &iov,
216 .msg_iovlen = 1,
217 .msg_control = ctlbuf,
218 .msg_controllen = sizeof (ctlbuf)
221 size_t len = recv_safe (sock, &mh);
222 if (len == (size_t)-1)
223 return -1;
225 validation = stun_agent_validate (newagent, &request, buf, len, NULL, 0);
227 if (validation == STUN_VALIDATION_SUCCESS) {
228 agent = newagent;
230 else {
231 validation = stun_agent_validate (oldagent, &request, buf, len, NULL, 0);
232 agent = oldagent;
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);
240 goto send_buf;
243 /* Mal-formatted packets */
244 if (validation != STUN_VALIDATION_SUCCESS ||
245 stun_message_get_class (&request) != STUN_REQUEST) {
246 return -1;
249 switch (stun_message_get_method (&request))
251 case STUN_BINDING:
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);
257 else
258 stun_message_append_addr (&response, STUN_ATTRIBUTE_MAPPED_ADDRESS,
259 mh.msg_name, mh.msg_namelen);
260 break;
262 default:
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);
268 send_buf:
270 len = send_safe (sock, &mh);
271 return (len < iov.iov_len) ? -1 : 0;
275 static int run (int family, int protocol, unsigned port)
277 StunAgent oldagent;
278 StunAgent newagent;
279 int sock = listen_socket (family, SOCK_DGRAM, protocol, port);
280 if (sock == -1)
281 return -1;
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);
288 for (;;)
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)
297 (void)signum;
298 exit (0);
302 int main (int argc, char *argv[])
304 int family = AF_INET;
305 unsigned port = IPPORT_STUN;
307 for (;;)
309 int c = getopt (argc, argv, "46");
310 if (c == EOF)
311 break;
313 switch (c)
315 case '4':
316 family = AF_INET;
317 break;
319 case '6':
320 family = AF_INET6;
321 break;
325 if (optind < argc)
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;
333 #else
334 int main () {
335 return 0;
337 #endif