Imported Upstream version 0.1.17
[platform/upstream/libnice.git] / stun / tools / stund.c
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 __sun
44 #define _XPG4_2 1
45 #endif
46
47 #include <assert.h>
48 #include <stdio.h>
49 #include <stdint.h>
50 #include <string.h>
51 #include <stdlib.h>
52 #include <signal.h>
53
54 #include <sys/types.h>
55
56 #ifdef _WIN32
57 #include <WinSock2.h>
58 #else
59 #include <sys/socket.h>
60 #include <netdb.h>
61 #include <netinet/in.h>
62 #endif
63
64 #ifdef HAVE_UNISTD_H
65 # include <unistd.h>
66 #else
67 # define close(fd) _close(fd)
68 #endif
69
70 #include <errno.h>
71 #include <limits.h>
72 #include <stdio.h>
73
74 #ifndef SOL_IP
75 # define SOL_IP IPPROTO_IP
76 #endif
77
78 #ifndef SOL_IPV6
79 # define SOL_IPV6 IPPROTO_IPV6
80 #endif
81
82
83 #ifndef IPV6_RECVPKTINFO
84 # define IPV6_RECVPKTINFO IPV6_PKTINFO
85 #endif
86
87 /** Default port for STUN binding discovery */
88 #define IPPORT_STUN  3478
89
90 #include "stun/stunagent.h"
91 #include "stund.h"
92
93 static const uint16_t known_attributes[] =  {
94   0
95 };
96
97 /*
98  * Creates a listening socket
99  */
100 int listen_socket (int fam, int type, int proto, unsigned int port)
101 {
102   int yes = 1;
103   int fd = socket (fam, type, proto);
104   union {
105     struct sockaddr addr;
106     struct sockaddr_in in;
107     struct sockaddr_in6 in6;
108     struct sockaddr_storage storage;
109   } addr;
110
111   if (fd == -1)
112   {
113     perror ("Error opening IP port");
114     return -1;
115   }
116
117   memset (&addr, 0, sizeof (addr));
118   addr.storage.ss_family = fam;
119 #ifdef HAVE_SA_LEN
120   addr.storage.ss_len = sizeof (addr);
121 #endif
122
123   switch (fam)
124   {
125     case AF_INET:
126       addr.in.sin_port = htons (port);
127       break;
128
129     case AF_INET6:
130 #ifdef IPV6_V6ONLY
131       setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, (const char *) &yes, sizeof (yes));
132 #endif
133       addr.in6.sin6_port = htons (port);
134       break;
135
136     default:
137       assert (0);  /* should never be reached */
138   }
139
140   if (bind (fd, &addr.addr, sizeof (struct sockaddr_storage)))
141   {
142     perror ("Error opening IP port");
143     goto error;
144   }
145
146   if ((type == SOCK_DGRAM) || (type == SOCK_RAW))
147   {
148     switch (fam)
149     {
150       case AF_INET:
151 #ifdef IP_RECVERR
152         setsockopt (fd, SOL_IP, IP_RECVERR, (const char*) &yes, sizeof (yes));
153 #endif
154         break;
155
156       case AF_INET6:
157 #ifdef IPV6_RECVERR
158         setsockopt (fd, SOL_IPV6, IPV6_RECVERR, (const char*) &yes, sizeof (yes));
159 #endif
160         break;
161
162       default:
163         assert (0);  /* should never be reached */
164     }
165   }
166   else
167   {
168     if (listen (fd, INT_MAX))
169     {
170       perror ("Error opening IP port");
171       goto error;
172     }
173   }
174
175   return fd;
176
177 error:
178   close (fd);
179   return -1;
180 }
181
182 static int dgram_process (int sock, StunAgent *oldagent, StunAgent *newagent)
183 {
184   union {
185     struct sockaddr_storage storage;
186     struct sockaddr addr;
187   } addr;
188   socklen_t addr_len;
189   uint8_t buf[STUN_MAX_MESSAGE_SIZE];
190   size_t buf_len = 0;
191   size_t len = 0;
192   StunMessage request;
193   StunMessage response;
194   StunValidationStatus validation;
195   StunAgent *agent = NULL;
196
197   addr_len = sizeof (struct sockaddr_storage);
198   len = recvfrom (sock, buf, sizeof(buf), 0, &addr.addr, &addr_len);
199   if (len == (size_t)-1)
200     return -1;
201
202   validation = stun_agent_validate (newagent, &request, buf, len, NULL, 0);
203
204   if (validation == STUN_VALIDATION_SUCCESS) {
205     agent = newagent;
206   }
207   else {
208     validation = stun_agent_validate (oldagent, &request, buf, len, NULL, 0);
209     agent = oldagent;
210   }
211
212   /* Unknown attributes */
213   if (validation == STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE)
214   {
215     buf_len = stun_agent_build_unknown_attributes_error (agent, &response, buf,
216         sizeof (buf), &request);
217     goto send_buf;
218   }
219
220   /* Mal-formatted packets */
221   if (validation != STUN_VALIDATION_SUCCESS ||
222       stun_message_get_class (&request) != STUN_REQUEST) {
223     return -1;
224   }
225
226   switch (stun_message_get_method (&request))
227   {
228     case STUN_BINDING:
229       stun_agent_init_response (agent, &response, buf, sizeof (buf), &request);
230       if (stun_message_has_cookie (&request))
231         stun_message_append_xor_addr (&response,
232             STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr.storage, addr_len);
233       else
234          stun_message_append_addr (&response, STUN_ATTRIBUTE_MAPPED_ADDRESS,
235              &addr.addr, addr_len);
236       break;
237
238     case STUN_SHARED_SECRET:
239     case STUN_ALLOCATE:
240     case STUN_SEND:
241     case STUN_CONNECT:
242     case STUN_IND_SEND:
243     case STUN_IND_DATA:
244     case STUN_CREATEPERMISSION:
245     case STUN_CHANNELBIND:
246     default:
247       if (!stun_agent_init_error (agent, &response, buf, sizeof (buf),
248               &request, STUN_ERROR_BAD_REQUEST))
249         return -1;
250   }
251
252   buf_len = stun_agent_finish_message (agent, &response, NULL, 0);
253 send_buf:
254   len = sendto (sock, buf, buf_len, 0, &addr.addr, addr_len);
255   return (len < buf_len) ? -1 : 0;
256 }
257
258
259 static int run (int family, int protocol, unsigned port)
260 {
261   StunAgent oldagent;
262   StunAgent newagent;
263   int sock = listen_socket (family, SOCK_DGRAM, protocol, port);
264   if (sock == -1)
265     return -1;
266
267   stun_agent_init (&oldagent, known_attributes,
268       STUN_COMPATIBILITY_RFC3489, 0);
269   stun_agent_init (&newagent, known_attributes,
270       STUN_COMPATIBILITY_RFC5389, STUN_AGENT_USAGE_USE_FINGERPRINT);
271
272   for (;;)
273     dgram_process (sock, &oldagent, &newagent);
274 }
275
276
277 /* Pretty useless dummy signal handler...
278  * But calling exit() is needed for gcov to work properly. */
279 static void exit_handler (int signum)
280 {
281   (void)signum;
282   exit (0);
283 }
284
285
286 int main (int argc, char *argv[])
287 {
288   int family = AF_INET;
289   unsigned port = IPPORT_STUN;
290   int i;
291
292
293 #ifdef _WIN32
294   WSADATA wsadata;
295
296   if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) {
297           fprintf(stderr, "Could not start Winsock2");
298           return 1;
299   }
300
301 #endif
302
303
304   for (i = 1; i < argc; ++i)
305   {
306     const char *arg = argv[i];
307
308     if (strcmp (arg, "-4") == 0)
309     {
310       family = AF_INET;
311     }
312     else if (strcmp (arg, "-6") == 0)
313     {
314       family = AF_INET6;
315     }
316     else if (arg[0] < '0' || arg[0] > '9')
317     {
318       fprintf (stderr, "Unexpected command line argument '%s'", arg);
319     }
320     else
321     {
322       port = atoi (arg);
323       break;
324     }
325   }
326
327   signal (SIGINT, exit_handler);
328   signal (SIGTERM, exit_handler);
329   return run (family, IPPROTO_UDP, port) ? EXIT_FAILURE : EXIT_SUCCESS;
330 }
331