Initial import of STUN support
[oweals/gnunet.git] / src / nat / test_stun.c
1 /*\r
2      This file is part of GNUnet.\r
3      Copyright (C) 2009, 2015 Christian Grothoff (and other contributing authors)\r
4 \r
5      GNUnet is free software; you can redistribute it and/or modify\r
6      it under the terms of the GNU General Public License as published\r
7      by the Free Software Foundation; either version 3, or (at your\r
8      option) any later version.\r
9 \r
10      GNUnet is distributed in the hope that it will be useful, but\r
11      WITHOUT ANY WARRANTY; without even the implied warranty of\r
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
13      General Public License for more details.\r
14 \r
15      You should have received a copy of the GNU General Public License\r
16      along with GNUnet; see the file COPYING.  If not, write to the\r
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,\r
18      Boston, MA 02111-1307, USA.\r
19 */\r
20 \r
21 /**\r
22  * Testcase for STUN server resolution\r
23  *\r
24  * @file nat/test_stun.c\r
25  * @brief Testcase for STUN library\r
26  * @author Bruno Souza Cabral\r
27  * @autor Mark Spencer (Original code borrowed from Asterisk)\r
28  *\r
29  */\r
30 \r
31 \r
32 #include "platform.h"\r
33 #include "gnunet_util_lib.h"\r
34 #include "gnunet_program_lib.h"\r
35 #include "gnunet_scheduler_lib.h"\r
36 #include "gnunet_nat_lib.h"\r
37 \r
38 \r
39 #include "test_stun.h"\r
40 \r
41 \r
42 /**\r
43  * The port the test service is running on (default 7895)\r
44  */\r
45 static unsigned long port = 7895;\r
46 \r
47 static int ret = 1;\r
48 \r
49 /**\r
50  * The listen socket of the service for IPv4\r
51  */\r
52 static struct GNUNET_NETWORK_Handle *lsock4;\r
53 \r
54 \r
55 /**\r
56  * The listen task ID for IPv4\r
57  */\r
58 static struct GNUNET_SCHEDULER_Task * ltask4;\r
59 \r
60 \r
61 static char *stun_server = STUN_SERVER;\r
62 static int stun_port = STUN_PORT;\r
63 \r
64 static int stun_debug = 1;\r
65 \r
66 \r
67 struct stun_strings {\r
68     const int value;\r
69     const char *name;\r
70 };\r
71 \r
72 \r
73 static inline int stun_msg2class(int msg)\r
74 {\r
75     return ((msg & 0x0010) >> 4) | ((msg & 0x0100) >> 7);\r
76 }\r
77 \r
78 static inline int stun_msg2method(int msg)\r
79 {\r
80     return (msg & 0x000f) | ((msg & 0x00e0) >> 1) | ((msg & 0x3e00) >> 2);\r
81 }\r
82 \r
83 static inline int stun_msg2type(int class, int method)\r
84 {\r
85     return ((class & 1) << 4) | ((class & 2) << 7) |\r
86             (method & 0x000f) | ((method & 0x0070) << 1) | ((method & 0x0f800) << 2);\r
87 }\r
88 \r
89 /* helper function to print message names */\r
90 static const char *stun_msg2str(int msg)\r
91 {\r
92     static const struct stun_strings classes[] = {\r
93     { STUN_REQUEST, "Request" },\r
94     { STUN_INDICATION, "Indication" },\r
95     { STUN_RESPONSE, "Response" },\r
96     { STUN_ERROR_RESPONSE, "Error Response" },\r
97     { 0, NULL }\r
98 };\r
99     static const struct stun_strings methods[] = {\r
100     { STUN_BINDING, "Binding" },\r
101     { 0, NULL }\r
102 };\r
103     static char result[32];\r
104     const char *class = NULL, *method = NULL;\r
105     int i, value;\r
106 \r
107     value = stun_msg2class(msg);\r
108     for (i = 0; classes[i].name; i++) {\r
109         class = classes[i].name;\r
110         if (classes[i].value == value)\r
111             break;\r
112     }\r
113     value = stun_msg2method(msg);\r
114     for (i = 0; methods[i].name; i++) {\r
115         method = methods[i].name;\r
116         if (methods[i].value == value)\r
117             break;\r
118     }\r
119     snprintf(result, sizeof(result), "%s %s",\r
120              method ? : "Unknown Method",\r
121              class ? : "Unknown Class Message");\r
122     return result;\r
123 }\r
124 \r
125 /* helper function to print attribute names */\r
126 static const char *stun_attr2str(int msg)\r
127 {\r
128     static const struct stun_strings attrs[] = {\r
129     { STUN_MAPPED_ADDRESS, "Mapped Address" },\r
130     { STUN_RESPONSE_ADDRESS, "Response Address" },\r
131     { STUN_CHANGE_ADDRESS, "Change Address" },\r
132     { STUN_SOURCE_ADDRESS, "Source Address" },\r
133     { STUN_CHANGED_ADDRESS, "Changed Address" },\r
134     { STUN_USERNAME, "Username" },\r
135     { STUN_PASSWORD, "Password" },\r
136     { STUN_MESSAGE_INTEGRITY, "Message Integrity" },\r
137     { STUN_ERROR_CODE, "Error Code" },\r
138     { STUN_UNKNOWN_ATTRIBUTES, "Unknown Attributes" },\r
139     { STUN_REFLECTED_FROM, "Reflected From" },\r
140     { STUN_REALM, "Realm" },\r
141     { STUN_NONCE, "Nonce" },\r
142     { STUN_XOR_MAPPED_ADDRESS, "XOR Mapped Address" },\r
143     { STUN_MS_VERSION, "MS Version" },\r
144     { STUN_MS_XOR_MAPPED_ADDRESS, "MS XOR Mapped Address" },\r
145     { STUN_SOFTWARE, "Software" },\r
146     { STUN_ALTERNATE_SERVER, "Alternate Server" },\r
147     { STUN_FINGERPRINT, "Fingerprint" },\r
148     { 0, NULL }\r
149 };\r
150     int i;\r
151 \r
152     for (i = 0; attrs[i].name; i++) {\r
153         if (attrs[i].value == msg)\r
154             return attrs[i].name;\r
155     }\r
156     return "Unknown Attribute";\r
157 }\r
158 \r
159 /* here we store credentials extracted from a message */\r
160 struct stun_state {\r
161     unsigned short attr;\r
162 };\r
163 \r
164 static int stun_process_attr(struct stun_state *state, struct stun_attr *attr)\r
165 {\r
166     if (stun_debug)\r
167         fprintf(stderr, "Found STUN Attribute %s (%04x), length %d\n",\r
168                 stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len));\r
169     switch (ntohs(attr->attr)) {\r
170     case STUN_MAPPED_ADDRESS:\r
171     case STUN_XOR_MAPPED_ADDRESS:\r
172     case STUN_MS_XOR_MAPPED_ADDRESS:\r
173         break;\r
174     default:\r
175         if (stun_debug)\r
176             fprintf(stderr, "Ignoring STUN Attribute %s (%04x), length %d\n",\r
177                     stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len));\r
178     }\r
179     return 0;\r
180 }\r
181 \r
182 /* append a string to an STUN message */\r
183 static void append_attr_string(struct stun_attr **attr, int attrval, const char *s, int *len, int *left)\r
184 {\r
185     int str_length = strlen(s);\r
186     int attr_length = str_length + ((~(str_length - 1)) & 0x3);\r
187     int size = sizeof(**attr) + attr_length;\r
188     if (*left > size) {\r
189         (*attr)->attr = htons(attrval);\r
190         (*attr)->len = htons(attr_length);\r
191         memcpy((*attr)->value, s, str_length);\r
192         memset((*attr)->value + str_length, 0, attr_length - str_length);\r
193         (*attr) = (struct stun_attr *)((*attr)->value + attr_length);\r
194         *len += size;\r
195         *left -= size;\r
196     }\r
197 }\r
198 \r
199 \r
200 /* helper function to generate a random request id */\r
201 static void stun_req_id(struct stun_header *req)\r
202 {\r
203     int x;\r
204     srand(time(0));\r
205     req->magic = htonl(STUN_MAGIC_COOKIE);\r
206     for (x = 0; x < 3; x++)\r
207         req->id.id[x] = rand();\r
208 }\r
209 \r
210 /* callback type to be invoked on stun responses. */\r
211 typedef int (stun_cb_f)(struct stun_state *st, struct stun_attr *attr, void *arg, unsigned int magic);\r
212 \r
213 /* handle an incoming STUN message.\r
214  *\r
215  * Do some basic sanity checks on packet size and content,\r
216  * try to extract a bit of information, and possibly reply.\r
217  * At the moment this only processes BIND requests, and returns\r
218  * the externally visible address of the request.\r
219  * If a callback is specified, invoke it with the attribute.\r
220  */\r
221 static int stun_handle_packet(unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg)\r
222 {\r
223     struct stun_header *hdr = (struct stun_header *)data;\r
224     struct stun_attr *attr;\r
225     struct stun_state st;\r
226     int ret = STUN_IGNORE;\r
227     int x;\r
228 \r
229     /* On entry, 'len' is the length of the udp payload. After the\r
230          * initial checks it becomes the size of unprocessed options,\r
231          * while 'data' is advanced accordingly.\r
232          */\r
233     if (len < sizeof(struct stun_header)) {\r
234         fprintf(stderr, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header));\r
235         return -1;\r
236     }\r
237     len -= sizeof(struct stun_header);\r
238     data += sizeof(struct stun_header);\r
239     x = ntohs(hdr->msglen);     /* len as advertised in the message */\r
240     if (stun_debug)\r
241         fprintf(stderr, "STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), x);\r
242     if (x > len) {\r
243         fprintf(stderr, "Scrambled STUN packet length (got %d, expecting %d)\n", x, (int)len);\r
244     } else\r
245         len = x;\r
246     memset(&st,0, sizeof(st));\r
247 \r
248     while (len) {\r
249         if (len < sizeof(struct stun_attr)) {\r
250             fprintf(stderr, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr));\r
251             break;\r
252         }\r
253         attr = (struct stun_attr *)data;\r
254 \r
255         /* compute total attribute length */\r
256         x = ntohs(attr->len) + sizeof(struct stun_attr);\r
257         if (x > len) {\r
258             fprintf(stderr, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len);\r
259             break;\r
260         }\r
261         if (stun_cb)\r
262             stun_cb(&st, attr, arg, hdr->magic);\r
263         if (stun_process_attr(&st, attr)) {\r
264             fprintf(stderr, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr));\r
265             break;\r
266         }\r
267         /* Clear attribute id: in case previous entry was a string,\r
268                  * this will act as the terminator for the string.\r
269                  */\r
270         attr->attr = 0;\r
271         data += x;\r
272         len -= x;\r
273     }\r
274     /* Null terminate any string.\r
275          * XXX NOTE, we write past the size of the buffer passed by the\r
276          * caller, so this is potentially dangerous. The only thing that\r
277          * saves us is that usually we read the incoming message in a\r
278          * much larger buffer\r
279          */\r
280     *data = '\0';\r
281 \r
282     return ret;\r
283 }\r
284 \r
285 /* Extract the STUN_MAPPED_ADDRESS from the stun response.\r
286  * This is used as a callback for stun_handle_response\r
287  * when called from stun_request.\r
288  */\r
289 static int stun_get_mapped(struct stun_state *st, struct stun_attr *attr, void *arg, unsigned int magic)\r
290 {\r
291     struct stun_addr *returned_addr = (struct stun_addr *)(attr + 1);\r
292     struct sockaddr_in *sa = (struct sockaddr_in *)arg;\r
293     unsigned short type = ntohs(attr->attr);\r
294 \r
295     switch (type) {\r
296     case STUN_MAPPED_ADDRESS:\r
297         if (st->attr == STUN_XOR_MAPPED_ADDRESS ||\r
298                 st->attr == STUN_MS_XOR_MAPPED_ADDRESS)\r
299             return 1;\r
300         magic = 0;\r
301         break;\r
302     case STUN_MS_XOR_MAPPED_ADDRESS:\r
303         if (st->attr == STUN_XOR_MAPPED_ADDRESS)\r
304             return 1;\r
305         break;\r
306     case STUN_XOR_MAPPED_ADDRESS:\r
307         break;\r
308     default:\r
309         return 1;\r
310     }\r
311     if (ntohs(attr->len) < 8 && returned_addr->family != 1)\r
312         return 1;\r
313 \r
314     st->attr = type;\r
315     sa->sin_port = returned_addr->port ^ htons(ntohl(magic) >> 16);\r
316     sa->sin_addr.s_addr = returned_addr->addr ^ magic;\r
317     return 0;\r
318 }\r
319 \r
320 /* Generic STUN request\r
321  * Send a generic stun request to the server specified,\r
322  * possibly waiting for a reply and filling the 'reply' field with\r
323  * the externally visible address. \r
324  \r
325  * \param s the socket used to send the request\r
326  * \return 0 on success, other values on error.\r
327  */\r
328 int stun_request(struct GNUNET_NETWORK_Handle * sock)\r
329 {\r
330     struct stun_header *req;\r
331     unsigned char reqdata[1024];\r
332     int reqlen, reqleft;\r
333     struct stun_attr *attr;\r
334 \r
335 \r
336         \r
337         \r
338         struct sockaddr_in server;\r
339         struct hostent *hostinfo = gethostbyname(stun_server);\r
340         if (!hostinfo) {\r
341                 fprintf(stderr, "Error resolving host %s\n", stun_server);\r
342                 return -1;\r
343         }\r
344         memset(&server,0, sizeof(server));\r
345         server.sin_family = AF_INET;\r
346         server.sin_addr = *(struct in_addr*) hostinfo->h_addr;\r
347         server.sin_port = htons(stun_port);\r
348         \r
349         \r
350 \r
351     req = (struct stun_header *)reqdata;\r
352     stun_req_id(req);\r
353     reqlen = 0;\r
354     reqleft = sizeof(reqdata) - sizeof(struct stun_header);\r
355     req->msgtype = 0;\r
356     req->msglen = 0;\r
357     attr = (struct stun_attr *)req->ies;\r
358 \r
359     append_attr_string(&attr, STUN_SOFTWARE, PACKAGE " v" VERSION_PACKAGE, &reqlen, &reqleft);\r
360     req->msglen = htons(reqlen);\r
361     req->msgtype = htons(stun_msg2type(STUN_REQUEST, STUN_BINDING));\r
362 \r
363 \r
364         if (-1 == GNUNET_NETWORK_socket_sendto (sock, req, ntohs(req->msglen) + sizeof(*req),\r
365                                     (const struct sockaddr *) &server, sizeof (server)))\r
366         {\r
367                 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "sendto");\r
368         }\r
369 \r
370     return -1;\r
371 }\r
372 \r
373 static void print_answer(struct sockaddr_in* answer)\r
374 {\r
375         printf("External IP is: %s , with port %d\n", inet_ntoa(answer->sin_addr), ntohs(answer->sin_port));\r
376 }\r
377 \r
378 \r
379 /**\r
380  * Activity on our incoming socket.  Read data from the\r
381  * incoming connection.\r
382  *\r
383  * @param cls \r
384  * @param tc scheduler context\r
385  */\r
386 static void\r
387 do_udp_read (void *cls,\r
388              const struct GNUNET_SCHEDULER_TaskContext *tc)\r
389 {\r
390     //struct GNUNET_NAT_Test *tst = cls;\r
391         unsigned char reply_buf[1024];\r
392         ssize_t rlen;\r
393         struct sockaddr_in answer;\r
394 \r
395 \r
396     if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&\r
397       (GNUNET_NETWORK_fdset_isset (tc->read_ready,\r
398                                    lsock4)))\r
399         {\r
400                 rlen = GNUNET_NETWORK_socket_recv (lsock4, reply_buf, sizeof (reply_buf));\r
401                 printf("Recivied something of size %d", rlen);\r
402                 \r
403                 //Lets handle the packet\r
404                 memset(&answer, 0, sizeof(struct sockaddr_in));\r
405         stun_handle_packet(reply_buf, rlen, stun_get_mapped, &answer);\r
406                 //Print the anser\r
407                 //TODO: Delete the object\r
408                 ret = 0;\r
409                 print_answer(&answer);\r
410                 \r
411                 \r
412         }\r
413 }\r
414 \r
415 \r
416 /**\r
417  * Create an IPv4 listen socket bound to our port.\r
418  *\r
419  * @return NULL on error\r
420  */\r
421 static struct GNUNET_NETWORK_Handle *\r
422         bind_v4 ()\r
423 {\r
424     struct GNUNET_NETWORK_Handle *ls;\r
425     struct sockaddr_in sa4;\r
426     int eno;\r
427 \r
428     memset (&sa4, 0, sizeof (sa4));\r
429     sa4.sin_family = AF_INET;\r
430     sa4.sin_port = htons (port);\r
431 #if HAVE_SOCKADDR_IN_SIN_LEN\r
432     sa4.sin_len = sizeof (sa4);\r
433 #endif \r
434     ls = GNUNET_NETWORK_socket_create (AF_INET,\r
435                                        SOCK_DGRAM,\r
436                                        0);\r
437     if (NULL == ls)\r
438         return NULL;\r
439     if (GNUNET_OK !=\r
440             GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa4,\r
441                                         sizeof (sa4)))\r
442     {\r
443         eno = errno;\r
444         GNUNET_NETWORK_socket_close (ls);\r
445         errno = eno;\r
446         return NULL;\r
447     }\r
448     return ls;\r
449 }\r
450 \r
451 \r
452 \r
453 /**\r
454  * Main function run with scheduler.\r
455  */\r
456 \r
457 \r
458 static void\r
459 run (void *cls, char *const *args, const char *cfgfile,\r
460      const struct GNUNET_CONFIGURATION_Handle *cfg)\r
461 {\r
462 \r
463 \r
464     //Lets create the socket\r
465     lsock4 = bind_v4 ();\r
466     if (NULL == lsock4)\r
467     {\r
468         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");\r
469     }\r
470     else\r
471     {\r
472                 printf("Binded, now will call add_read\n");\r
473         //Lets call our function now when it accepts\r
474         ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,\r
475                                                 lsock4, &do_udp_read, NULL);\r
476 \r
477     }\r
478     if(NULL == lsock4 )\r
479     {\r
480         GNUNET_SCHEDULER_shutdown ();\r
481         return;\r
482     }\r
483     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,\r
484                 "Service listens on port %u\n",\r
485                 port);\r
486         printf("Start main event\n");\r
487         stun_request(lsock4);\r
488     //Main event\r
489     //main_task = GNUNET_SCHEDULER_add_delayed (timeout, &do_timeout, nh);\r
490 \r
491 }\r
492 \r
493 \r
494 int\r
495 main (int argc, char *const argv[])\r
496 {\r
497     struct GNUNET_GETOPT_CommandLineOption options[] = {\r
498         GNUNET_GETOPT_OPTION_END\r
499     };\r
500 \r
501     char *const argv_prog[] = {\r
502         "test-stun",\r
503         NULL\r
504     };\r
505     GNUNET_log_setup ("test-stun",\r
506                       "WARNING",\r
507                       NULL);\r
508 \r
509     GNUNET_PROGRAM_run (1, argv_prog, "test-stun", "nohelp", options, &run, NULL);\r
510     \r
511         return ret;\r
512 }\r
513 \r
514 /* end of test_nat.c */\r