diff options
author | Bruno Cabral <bcabral@uw.edu> | 2015-06-25 02:21:23 +0000 |
---|---|---|
committer | Bruno Cabral <bcabral@uw.edu> | 2015-06-25 02:21:23 +0000 |
commit | 26ad92551ed4afd96f3d7742179509b850a5cc7a (patch) | |
tree | ddcf6699873afc2431df036193b1e598c15e142c /src/nat/nat_stun.c | |
parent | 20d8a41eaa19064d16e9a004dea2d9abcdbd9731 (diff) | |
download | gnunet-26ad92551ed4afd96f3d7742179509b850a5cc7a.tar.gz gnunet-26ad92551ed4afd96f3d7742179509b850a5cc7a.zip |
Polish and simplyfy STUN code, move STUN code to GNUNET_NAT_
Diffstat (limited to 'src/nat/nat_stun.c')
-rw-r--r-- | src/nat/nat_stun.c | 464 |
1 files changed, 463 insertions, 1 deletions
diff --git a/src/nat/nat_stun.c b/src/nat/nat_stun.c index 06ed9261f..490a11257 100644 --- a/src/nat/nat_stun.c +++ b/src/nat/nat_stun.c | |||
@@ -34,6 +34,468 @@ | |||
34 | * | 34 | * |
35 | * | 35 | * |
36 | * @file nat/nat_stun.c | 36 | * @file nat/nat_stun.c |
37 | * @brief Testcase for STUN library | 37 | * @brief Functions for STUN functionality |
38 | * @author Bruno Souza Cabral | 38 | * @author Bruno Souza Cabral |
39 | */ | 39 | */ |
40 | |||
41 | #include "platform.h" | ||
42 | #include "gnunet_util_lib.h" | ||
43 | #include "gnunet_program_lib.h" | ||
44 | #include "gnunet_scheduler_lib.h" | ||
45 | #include "gnunet_nat_lib.h" | ||
46 | |||
47 | |||
48 | #include "nat_stun.h" | ||
49 | |||
50 | #define LOG(kind,...) GNUNET_log_from (kind, "stun", __VA_ARGS__) | ||
51 | |||
52 | |||
53 | |||
54 | /** | ||
55 | * Handle to a request given to the resolver. Can be used to cancel | ||
56 | * the request prior to the timeout or successful execution. Also | ||
57 | * used to track our internal state for the request. | ||
58 | */ | ||
59 | struct GNUNET_NAT_StunRequestHandle { | ||
60 | |||
61 | /** | ||
62 | * Handle to a pending DNS lookup request. | ||
63 | */ | ||
64 | struct GNUNET_RESOLVER_RequestHandle *dns_active; | ||
65 | |||
66 | |||
67 | /** | ||
68 | * Handle to the listen socket | ||
69 | */ | ||
70 | struct GNUNET_NETWORK_Handle * sock; | ||
71 | |||
72 | /** | ||
73 | * Stun server address | ||
74 | */ | ||
75 | char *stun_server ; | ||
76 | |||
77 | /** | ||
78 | * STUN port | ||
79 | */ | ||
80 | int stun_port; | ||
81 | |||
82 | }; | ||
83 | |||
84 | |||
85 | |||
86 | /* here we store credentials extracted from a message */ | ||
87 | struct StunState { | ||
88 | uint16_t attr; | ||
89 | }; | ||
90 | |||
91 | |||
92 | /** | ||
93 | * Convert a message to a StunClass | ||
94 | * | ||
95 | * @param msg the received message | ||
96 | * @return the converted StunClass | ||
97 | */ | ||
98 | static int decode_class(int msg) | ||
99 | { | ||
100 | return ((msg & 0x0010) >> 4) | ((msg & 0x0100) >> 7); | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * Convert a message to a StunMethod | ||
105 | * | ||
106 | * @param msg the received message | ||
107 | * @return the converted StunMethod | ||
108 | */ | ||
109 | static int decode_method(int msg) | ||
110 | { | ||
111 | return (msg & 0x000f) | ((msg & 0x00e0) >> 1) | ((msg & 0x3e00) >> 2); | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * Encode a class and method to a compatible STUN format | ||
116 | * | ||
117 | * @param msg_class class to be converted | ||
118 | * @param method method to be converted | ||
119 | * @return message in a STUN compatible format | ||
120 | */ | ||
121 | static int encode_message(StunClasses msg_class, StunMethods method) | ||
122 | { | ||
123 | return ((msg_class & 1) << 4) | ((msg_class & 2) << 7) | | ||
124 | (method & 0x000f) | ((method & 0x0070) << 1) | ((method & 0x0f800) << 2); | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * Print a class and method from a STUN message | ||
129 | * | ||
130 | * @param msg | ||
131 | * @return string with the message class and method | ||
132 | */ | ||
133 | static const char *stun_msg2str(int msg) | ||
134 | { | ||
135 | |||
136 | const struct { enum StunClasses value; const char *name; } classes[] = { | ||
137 | { STUN_REQUEST, "Request" }, | ||
138 | { STUN_INDICATION, "Indication" }, | ||
139 | { STUN_RESPONSE, "Response" }, | ||
140 | { STUN_ERROR_RESPONSE, "Error Response" }, | ||
141 | { 0, NULL } | ||
142 | }; | ||
143 | |||
144 | const struct { enum StunMethods value; const char *name; } methods[] = { | ||
145 | { STUN_BINDING, "Binding" }, | ||
146 | { 0, NULL } | ||
147 | }; | ||
148 | |||
149 | static char result[32]; | ||
150 | const char *msg_class = NULL; | ||
151 | const char *method = NULL; | ||
152 | int i; | ||
153 | int value; | ||
154 | |||
155 | value = decode_class(msg); | ||
156 | for (i = 0; classes[i].name; i++) { | ||
157 | msg_class = classes[i].name; | ||
158 | if (classes[i].value == value) | ||
159 | break; | ||
160 | } | ||
161 | value = decode_method(msg); | ||
162 | for (i = 0; methods[i].name; i++) { | ||
163 | method = methods[i].name; | ||
164 | if (methods[i].value == value) | ||
165 | break; | ||
166 | } | ||
167 | snprintf(result, sizeof(result), "%s %s", | ||
168 | method ? : "Unknown Method", | ||
169 | msg_class ? : "Unknown Class Message"); | ||
170 | return result; | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * Print attribute name | ||
175 | * | ||
176 | * @param msg with a attribute type | ||
177 | * @return string with the attribute name | ||
178 | */ | ||
179 | static const char *stun_attr2str(int msg) | ||
180 | { | ||
181 | const struct { enum StunAttributes value; const char *name; } attrs[] = { | ||
182 | { STUN_MAPPED_ADDRESS, "Mapped Address" }, | ||
183 | { STUN_RESPONSE_ADDRESS, "Response Address" }, | ||
184 | { STUN_CHANGE_ADDRESS, "Change Address" }, | ||
185 | { STUN_SOURCE_ADDRESS, "Source Address" }, | ||
186 | { STUN_CHANGED_ADDRESS, "Changed Address" }, | ||
187 | { STUN_USERNAME, "Username" }, | ||
188 | { STUN_PASSWORD, "Password" }, | ||
189 | { STUN_MESSAGE_INTEGRITY, "Message Integrity" }, | ||
190 | { STUN_ERROR_CODE, "Error Code" }, | ||
191 | { STUN_UNKNOWN_ATTRIBUTES, "Unknown Attributes" }, | ||
192 | { STUN_REFLECTED_FROM, "Reflected From" }, | ||
193 | { STUN_REALM, "Realm" }, | ||
194 | { STUN_NONCE, "Nonce" }, | ||
195 | { STUN_XOR_MAPPED_ADDRESS, "XOR Mapped Address" }, | ||
196 | { STUN_MS_VERSION, "MS Version" }, | ||
197 | { STUN_MS_XOR_MAPPED_ADDRESS, "MS XOR Mapped Address" }, | ||
198 | { STUN_SOFTWARE, "Software" }, | ||
199 | { STUN_ALTERNATE_SERVER, "Alternate Server" }, | ||
200 | { STUN_FINGERPRINT, "Fingerprint" }, | ||
201 | { 0, NULL } | ||
202 | }; | ||
203 | int i; | ||
204 | |||
205 | for (i = 0; attrs[i].name; i++) { | ||
206 | if (attrs[i].value == msg) | ||
207 | return attrs[i].name; | ||
208 | } | ||
209 | return "Unknown Attribute"; | ||
210 | } | ||
211 | |||
212 | |||
213 | /** | ||
214 | * Fill the stun_header with a random request_id | ||
215 | * | ||
216 | * @param state, STUN attribute type | ||
217 | * @param attr , the actual attribute | ||
218 | * | ||
219 | * @param req, stun header to be filled | ||
220 | */ | ||
221 | static int stun_process_attr(struct StunState *state, struct stun_attr *attr) | ||
222 | { | ||
223 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
224 | "Found STUN Attribute %s (%04x), length %d\n", | ||
225 | stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len)); | ||
226 | |||
227 | switch (ntohs(attr->attr)) { | ||
228 | case STUN_MAPPED_ADDRESS: | ||
229 | case STUN_XOR_MAPPED_ADDRESS: | ||
230 | case STUN_MS_XOR_MAPPED_ADDRESS: | ||
231 | break; | ||
232 | default: | ||
233 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
234 | "Ignoring STUN Attribute %s (%04x), length %d\n", | ||
235 | stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len)); | ||
236 | |||
237 | } | ||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | |||
242 | |||
243 | /** | ||
244 | * Fill the stun_header with a random request_id | ||
245 | * | ||
246 | * @param req, stun header to be filled | ||
247 | */ | ||
248 | static void | ||
249 | generate_request_id(struct stun_header *req) | ||
250 | { | ||
251 | int x; | ||
252 | req->magic = htonl(STUN_MAGIC_COOKIE); | ||
253 | for (x = 0; x < 3; x++) | ||
254 | req->id.id[x] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, | ||
255 | UINT32_MAX); | ||
256 | } | ||
257 | |||
258 | |||
259 | /** | ||
260 | * Extract the STUN_MAPPED_ADDRESS from the stun response. | ||
261 | * This is used as a callback for stun_handle_response | ||
262 | * when called from stun_request. | ||
263 | * | ||
264 | * @param st, pointer where we will set the type | ||
265 | * @param attr , received stun attribute | ||
266 | * @param arg , pointer to a sockaddr_in where we will set the reported IP and port | ||
267 | * @param magic , Magic cookie | ||
268 | * | ||
269 | * @return 0 on sucess, other value otherwise | ||
270 | */ | ||
271 | static int | ||
272 | stun_get_mapped(struct StunState *st, struct stun_attr *attr,struct sockaddr_in *arg, unsigned int magic) | ||
273 | { | ||
274 | struct stun_addr *returned_addr = (struct stun_addr *)(attr + 1); | ||
275 | struct sockaddr_in *sa = (struct sockaddr_in *)arg; | ||
276 | unsigned short type = ntohs(attr->attr); | ||
277 | |||
278 | switch (type) { | ||
279 | case STUN_MAPPED_ADDRESS: | ||
280 | if (st->attr == STUN_XOR_MAPPED_ADDRESS || | ||
281 | st->attr == STUN_MS_XOR_MAPPED_ADDRESS) | ||
282 | return 1; | ||
283 | magic = 0; | ||
284 | break; | ||
285 | case STUN_MS_XOR_MAPPED_ADDRESS: | ||
286 | if (st->attr == STUN_XOR_MAPPED_ADDRESS) | ||
287 | return 1; | ||
288 | break; | ||
289 | case STUN_XOR_MAPPED_ADDRESS: | ||
290 | break; | ||
291 | default: | ||
292 | return 1; | ||
293 | } | ||
294 | if (ntohs(attr->len) < 8 && returned_addr->family != 1) { | ||
295 | return 1; | ||
296 | } | ||
297 | |||
298 | st->attr = type; | ||
299 | sa->sin_port = returned_addr->port ^ htons(ntohl(magic) >> 16); | ||
300 | sa->sin_addr.s_addr = returned_addr->addr ^ magic; | ||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | |||
305 | /** | ||
306 | * Handle an incoming STUN message, Do some basic sanity checks on packet size and content, | ||
307 | * try to extract a bit of information, and possibly reply. | ||
308 | * At the moment this only processes BIND requests, and returns | ||
309 | * the externally visible address of the request. | ||
310 | * If a callback is specified, invoke it with the attribute. | ||
311 | * | ||
312 | * @param data, the packet | ||
313 | * @param len, the length of the packet | ||
314 | * @param arg, sockaddr_in where we will set our discovered packet | ||
315 | * | ||
316 | * @return, 0 on OK, -1 if the packet is invalid ( not a stun packet) | ||
317 | */ | ||
318 | int | ||
319 | GNUNET_NAT_stun_handle_packet(const uint8_t *data, size_t len,struct sockaddr_in *arg) | ||
320 | { | ||
321 | struct stun_header *hdr = (struct stun_header *)data; | ||
322 | struct stun_attr *attr; | ||
323 | struct StunState st; | ||
324 | int ret = STUN_IGNORE; | ||
325 | |||
326 | uint32_t advertised_message_size; | ||
327 | uint32_t message_magic_cookie; | ||
328 | |||
329 | |||
330 | /* On entry, 'len' is the length of the udp payload. After the | ||
331 | * initial checks it becomes the size of unprocessed options, | ||
332 | * while 'data' is advanced accordingly. | ||
333 | */ | ||
334 | if (len < sizeof(struct stun_header)) { | ||
335 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
336 | "STUN packet too short (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header)); | ||
337 | GNUNET_break_op (0); | ||
338 | return -1; | ||
339 | } | ||
340 | /* Skip header as it is already in hdr */ | ||
341 | len -= sizeof(struct stun_header); | ||
342 | data += sizeof(struct stun_header); | ||
343 | |||
344 | /* len as advertised in the message */ | ||
345 | advertised_message_size = ntohs(hdr->msglen); | ||
346 | |||
347 | message_magic_cookie = ntohl(hdr->magic); | ||
348 | /* Compare if the cookie match */ | ||
349 | if(STUN_MAGIC_COOKIE != message_magic_cookie){ | ||
350 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
351 | "Invalid magic cookie \n"); | ||
352 | GNUNET_break_op (0); | ||
353 | return -1; | ||
354 | } | ||
355 | |||
356 | |||
357 | LOG (GNUNET_ERROR_TYPE_INFO, "STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), advertised_message_size); | ||
358 | |||
359 | |||
360 | if (advertised_message_size > len) { | ||
361 | LOG (GNUNET_ERROR_TYPE_INFO, "Scrambled STUN packet length (got %d, expecting %d)\n", advertised_message_size, (int)len); | ||
362 | GNUNET_break_op (0); | ||
363 | return -1; | ||
364 | } else { | ||
365 | len = advertised_message_size; | ||
366 | } | ||
367 | /* Zero the struct */ | ||
368 | memset(&st,0, sizeof(st)); | ||
369 | |||
370 | while (len > 0) { | ||
371 | if (len < sizeof(struct stun_attr)) { | ||
372 | LOG (GNUNET_ERROR_TYPE_INFO, "Attribute too short (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr)); | ||
373 | GNUNET_break_op (0); | ||
374 | break; | ||
375 | } | ||
376 | attr = (struct stun_attr *)data; | ||
377 | |||
378 | /* compute total attribute length */ | ||
379 | advertised_message_size = ntohs(attr->len) + sizeof(struct stun_attr); | ||
380 | |||
381 | /* Check if we still have space in our buffer */ | ||
382 | if (advertised_message_size > len ) { | ||
383 | LOG (GNUNET_ERROR_TYPE_INFO, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", advertised_message_size, (int)len); | ||
384 | GNUNET_break_op (0); | ||
385 | break; | ||
386 | } | ||
387 | |||
388 | |||
389 | stun_get_mapped(&st, attr, arg, hdr->magic); | ||
390 | |||
391 | if (stun_process_attr(&st, attr)) { | ||
392 | LOG (GNUNET_ERROR_TYPE_INFO, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr)); | ||
393 | break; | ||
394 | } | ||
395 | /* Clear attribute id: in case previous entry was a string, | ||
396 | * this will act as the terminator for the string. | ||
397 | */ | ||
398 | attr->attr = 0; | ||
399 | data += advertised_message_size; | ||
400 | len -= advertised_message_size; | ||
401 | } | ||
402 | |||
403 | return ret; | ||
404 | } | ||
405 | |||
406 | |||
407 | |||
408 | /** | ||
409 | * Try to establish a connection given the specified address. | ||
410 | * | ||
411 | * @param cls our `struct GNUNET_NAT_StunRequestHandle *` | ||
412 | * @param addr address to try, NULL for "last call" | ||
413 | * @param addrlen length of @a addr | ||
414 | */ | ||
415 | static void | ||
416 | stun_dns_callback (void *cls, | ||
417 | const struct sockaddr *addr, | ||
418 | socklen_t addrlen) { | ||
419 | |||
420 | |||
421 | struct GNUNET_NAT_StunRequestHandle *request = cls; | ||
422 | |||
423 | struct stun_header *req; | ||
424 | uint8_t reqdata[1024]; | ||
425 | int reqlen; | ||
426 | struct sockaddr_in server; | ||
427 | |||
428 | if(NULL == request) { | ||
429 | LOG (GNUNET_ERROR_TYPE_INFO, "Empty request\n"); | ||
430 | return; | ||
431 | } | ||
432 | |||
433 | if (NULL == addr) { | ||
434 | request->dns_active = NULL; | ||
435 | LOG (GNUNET_ERROR_TYPE_INFO, "Error resolving host %s\n", request->stun_server); | ||
436 | return; | ||
437 | } | ||
438 | |||
439 | |||
440 | memset(&server,0, sizeof(server)); | ||
441 | server.sin_family = AF_INET; | ||
442 | server.sin_addr = ((struct sockaddr_in *)addr)->sin_addr; | ||
443 | server.sin_port = htons(request->stun_port); | ||
444 | |||
445 | |||
446 | /*Craft the simplest possible STUN packet. A request binding*/ | ||
447 | req = (struct stun_header *)reqdata; | ||
448 | generate_request_id(req); | ||
449 | reqlen = 0; | ||
450 | req->msgtype = 0; | ||
451 | req->msglen = 0; | ||
452 | req->msglen = htons(reqlen); | ||
453 | req->msgtype = htons(encode_message(STUN_REQUEST, STUN_BINDING)); | ||
454 | |||
455 | /* Send the packet */ | ||
456 | if (-1 == GNUNET_NETWORK_socket_sendto (request->sock, req, ntohs(req->msglen) + sizeof(*req), | ||
457 | (const struct sockaddr *) &server, sizeof (server))) | ||
458 | { | ||
459 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "Fail to sendto"); | ||
460 | } | ||
461 | |||
462 | } | ||
463 | |||
464 | |||
465 | /** | ||
466 | * Make Generic STUN request and | ||
467 | * Send a generic stun request to the server specified using the specified socket. | ||
468 | * possibly waiting for a reply and filling the 'reply' field with | ||
469 | * the externally visible address. | ||
470 | *c | ||
471 | * @param server, the address of the stun server | ||
472 | * @param port, port of the stun server | ||
473 | * @param sock the socket used to send the request | ||
474 | * @return GNUNET_NAT_StunRequestHandle on success, NULL on error. | ||
475 | */ | ||
476 | struct GNUNET_NAT_StunRequestHandle * | ||
477 | GNUNET_NAT_stun_make_request(char * server, int port, struct GNUNET_NETWORK_Handle * sock) | ||
478 | { | ||
479 | |||
480 | struct GNUNET_NAT_StunRequestHandle *rh; | ||
481 | |||
482 | rh = GNUNET_malloc (sizeof (struct GNUNET_NAT_StunRequestHandle)); | ||
483 | rh->sock = sock; | ||
484 | |||
485 | char * server_copy = GNUNET_malloc (1 + strlen (server)); | ||
486 | if (server_copy) { | ||
487 | strcpy (server_copy, server); | ||
488 | }else{ | ||
489 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "Failed to allocate string"); | ||
490 | return NULL; | ||
491 | } | ||
492 | |||
493 | rh->stun_server = server_copy; | ||
494 | rh->stun_port = port; | ||
495 | rh->dns_active = GNUNET_RESOLVER_ip_get (rh->stun_server, AF_INET, | ||
496 | GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT, | ||
497 | &stun_dns_callback, rh); | ||
498 | |||
499 | |||
500 | return rh; | ||
501 | } \ No newline at end of file | ||