diff options
author | Moon <moon@140774ce-b5e7-0310-ab8b-a85725594a96> | 2010-10-09 13:53:47 +0000 |
---|---|---|
committer | Moon <moon@140774ce-b5e7-0310-ab8b-a85725594a96> | 2010-10-09 13:53:47 +0000 |
commit | 922a0672749ba9d496d1dd8f6596bb4f8035e71d (patch) | |
tree | ccd4b351bd5acd5cc3b8b1cc358c712d6c0c116d /src/nat/upnp-commands.c | |
parent | dc24b5bf44bf8d9460b2571fe529403637aa3e16 (diff) | |
download | gnunet-922a0672749ba9d496d1dd8f6596bb4f8035e71d.tar.gz gnunet-922a0672749ba9d496d1dd8f6596bb4f8035e71d.zip |
rework UPnP code to use GNUnet scheduler and network API
disable NAT-PMP support for now
Diffstat (limited to 'src/nat/upnp-commands.c')
-rw-r--r-- | src/nat/upnp-commands.c | 880 |
1 files changed, 880 insertions, 0 deletions
diff --git a/src/nat/upnp-commands.c b/src/nat/upnp-commands.c new file mode 100644 index 000000000..ddc5d9473 --- /dev/null +++ b/src/nat/upnp-commands.c | |||
@@ -0,0 +1,880 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2009, 2010 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 3, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /* | ||
22 | * Code in this file is originally based on the miniupnp library. | ||
23 | * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved. | ||
24 | * | ||
25 | * Original licence: | ||
26 | * | ||
27 | * Redistribution and use in source and binary forms, with or without | ||
28 | * modification, are permitted provided that the following conditions are met: | ||
29 | * | ||
30 | * * Redistributions of source code must retain the above copyright notice, | ||
31 | * this list of conditions and the following disclaimer. | ||
32 | * * Redistributions in binary form must reproduce the above copyright notice, | ||
33 | * this list of conditions and the following disclaimer in the documentation | ||
34 | * and/or other materials provided with the distribution. | ||
35 | * * The name of the author may not be used to endorse or promote products | ||
36 | * derived from this software without specific prior written permission. | ||
37 | * | ||
38 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
39 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
40 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
41 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||
42 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
43 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
44 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
45 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
46 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
47 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
48 | * POSSIBILITY OF SUCH DAMAGE. | ||
49 | */ | ||
50 | |||
51 | /** | ||
52 | * @file nat/upnp-commands.c | ||
53 | * @brief Implementation of a basic set of UPnP commands | ||
54 | * | ||
55 | * @author Milan Bouchet-Valat | ||
56 | */ | ||
57 | |||
58 | #include "platform.h" | ||
59 | #include "gnunet_util_lib.h" | ||
60 | |||
61 | #include <stdlib.h> | ||
62 | #include <stdio.h> | ||
63 | #include <string.h> | ||
64 | |||
65 | #include "upnp-reply-parse.h" | ||
66 | #include "upnp-igd-parse.h" | ||
67 | #include "upnp-discover.h" | ||
68 | #include "upnp-commands.h" | ||
69 | |||
70 | #define SOAP_PREFIX "s" | ||
71 | #define SERVICE_PREFIX "u" | ||
72 | #define SERVICE_PREFIX2 'u' | ||
73 | #define MAX_HOSTNAME_LEN 64 | ||
74 | |||
75 | #define PRINT_UPNP_ERROR(a, b) GNUNET_log_from(GNUNET_ERROR_TYPE_WARNING, "UPnP", _("%s failed at %s:%d: %s\n"), a, __FILE__, __LINE__, b); | ||
76 | |||
77 | |||
78 | /** | ||
79 | * Private closure used by UPNP_command() and its callbacks. | ||
80 | */ | ||
81 | struct UPNP_command_cls | ||
82 | { | ||
83 | /** | ||
84 | * Connection handle used for sending and receiving. | ||
85 | */ | ||
86 | struct GNUNET_CONNECTION_Handle *s; | ||
87 | |||
88 | /** | ||
89 | * Transmission handle used for sending command. | ||
90 | */ | ||
91 | struct GNUNET_CONNECTION_TransmitHandle *th; | ||
92 | |||
93 | /** | ||
94 | * HTML content to send to run command. | ||
95 | */ | ||
96 | char *content; | ||
97 | |||
98 | /** | ||
99 | * Buffer where to copy received data to pass to caller. | ||
100 | */ | ||
101 | char *buffer; | ||
102 | |||
103 | /** | ||
104 | * Size of buffer. | ||
105 | */ | ||
106 | size_t buf_size; | ||
107 | |||
108 | /** | ||
109 | * User callback to trigger when done. | ||
110 | */ | ||
111 | UPNP_command_cb_ caller_cb; | ||
112 | |||
113 | /** | ||
114 | * User closure to pass to caller_cb. | ||
115 | */ | ||
116 | void *caller_cls; | ||
117 | }; | ||
118 | |||
119 | /** | ||
120 | * Get the length of content included in an HTML line. | ||
121 | * | ||
122 | * @param p line to parse | ||
123 | * @param n size of p | ||
124 | * @return the length of the content | ||
125 | */ | ||
126 | static ssize_t | ||
127 | get_content_len_from_line (const char *p, int n) | ||
128 | { | ||
129 | static const char cont_len_str[] = "content-length"; | ||
130 | const char *p2 = cont_len_str; | ||
131 | int a = 0; | ||
132 | |||
133 | while (*p2) | ||
134 | { | ||
135 | if (n == 0) | ||
136 | return -1; | ||
137 | |||
138 | if (*p2 != *p && *p2 != (*p + 32)) | ||
139 | return -1; | ||
140 | |||
141 | p++; | ||
142 | p2++; | ||
143 | n--; | ||
144 | } | ||
145 | |||
146 | if (n == 0) | ||
147 | return -1; | ||
148 | |||
149 | if (*p != ':') | ||
150 | return -1; | ||
151 | |||
152 | p++; | ||
153 | n--; | ||
154 | |||
155 | while (*p == ' ') | ||
156 | { | ||
157 | if (n == 0) | ||
158 | return -1; | ||
159 | |||
160 | p++; | ||
161 | n--; | ||
162 | } | ||
163 | |||
164 | while (*p >= '0' && *p <= '9') | ||
165 | { | ||
166 | if (n == 0) | ||
167 | return -1; | ||
168 | |||
169 | a = (a * 10) + (*p - '0'); | ||
170 | p++; | ||
171 | n--; | ||
172 | } | ||
173 | |||
174 | return a; | ||
175 | } | ||
176 | |||
177 | /** | ||
178 | * Get the respective lengths of content and header from an HTML reply. | ||
179 | * | ||
180 | * @param p HTML to parse | ||
181 | * @param n size of p | ||
182 | * @param content_len pointer to store content length to | ||
183 | * @param content_len pointer to store header length to | ||
184 | */ | ||
185 | static void | ||
186 | get_content_and_header_len (const char *p, int n, | ||
187 | int *content_len, int *header_len) | ||
188 | { | ||
189 | const char *line; | ||
190 | int line_len; | ||
191 | int r; | ||
192 | |||
193 | line = p; | ||
194 | |||
195 | while (line < p + n) | ||
196 | { | ||
197 | line_len = 0; | ||
198 | |||
199 | while (line[line_len] != '\r' && line[line_len] != '\r') | ||
200 | { | ||
201 | if (line + line_len >= p + n) | ||
202 | return; | ||
203 | |||
204 | line_len++; | ||
205 | } | ||
206 | |||
207 | r = get_content_len_from_line (line, line_len); | ||
208 | |||
209 | if (r > 0) | ||
210 | *content_len = r; | ||
211 | |||
212 | line = line + line_len + 2; | ||
213 | |||
214 | if (line[0] == '\r' && line[1] == '\n') | ||
215 | { | ||
216 | *header_len = (line - p) + 2; | ||
217 | return; | ||
218 | } | ||
219 | } | ||
220 | } | ||
221 | |||
222 | /** | ||
223 | * Receive reply of the device to our UPnP command. | ||
224 | * | ||
225 | * @param data closure from UPNP_command() | ||
226 | * @param buf struct UPNP_command_cls *cls | ||
227 | * @param available number of bytes in buf | ||
228 | * @param addr address of the sender | ||
229 | * @param addrlen size of addr | ||
230 | * @errCode value of errno | ||
231 | */ | ||
232 | static void | ||
233 | UPNP_command_receiver (void *data, | ||
234 | const void *buf, | ||
235 | size_t available, | ||
236 | const struct sockaddr *addr, | ||
237 | socklen_t addrlen, int errCode) | ||
238 | { | ||
239 | struct UPNP_command_cls *cls = data; | ||
240 | int content_len; | ||
241 | int header_len; | ||
242 | |||
243 | if (available > 0) | ||
244 | { | ||
245 | content_len = -1; | ||
246 | header_len = -1; | ||
247 | get_content_and_header_len (buf, available, &content_len, &header_len); | ||
248 | |||
249 | strncpy (cls->buffer, (char *) buf, cls->buf_size - 2); | ||
250 | cls->buffer[cls->buf_size - 2] = '\0'; | ||
251 | } | ||
252 | else | ||
253 | { | ||
254 | cls->buffer[0] = '\0'; | ||
255 | } | ||
256 | |||
257 | GNUNET_CONNECTION_destroy (cls->s, GNUNET_NO); | ||
258 | |||
259 | (*cls->caller_cb) (cls->buffer, cls->buf_size, cls->caller_cls); | ||
260 | |||
261 | GNUNET_free (cls->content); | ||
262 | GNUNET_free (cls); | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * Send UPnP command to device. | ||
267 | */ | ||
268 | static size_t | ||
269 | UPNP_command_transmit (void *data, size_t size, void *buf) | ||
270 | { | ||
271 | struct UPNP_command_cls *cls = data; | ||
272 | int n; | ||
273 | char *content = cls->content; | ||
274 | |||
275 | n = strlen (content); | ||
276 | memcpy (buf, content, size); | ||
277 | |||
278 | GNUNET_CONNECTION_receive (cls->s, cls->buf_size, GNUNET_TIME_UNIT_MINUTES, | ||
279 | UPNP_command_receiver, cls); | ||
280 | |||
281 | return n; | ||
282 | } | ||
283 | |||
284 | /** | ||
285 | * Parse a HTTP URL string to extract hostname, port and path it points to. | ||
286 | * | ||
287 | * @param url source string corresponding to URL | ||
288 | * @param hostname pointer where to store hostname (size of MAX_HOSTNAME_LEN+1) | ||
289 | * @param port pointer where to store port | ||
290 | * @param path pointer where to store path | ||
291 | * | ||
292 | * @return GNUNET_OK on success, GNUNET_SYSERR on failure | ||
293 | */ | ||
294 | int | ||
295 | parse_url (const char *url, char *hostname, unsigned short *port, char **path) | ||
296 | { | ||
297 | char *p1, *p2, *p3; | ||
298 | |||
299 | if (!url) | ||
300 | return GNUNET_SYSERR; | ||
301 | |||
302 | p1 = strstr (url, "://"); | ||
303 | |||
304 | if (!p1) | ||
305 | return GNUNET_SYSERR; | ||
306 | |||
307 | p1 += 3; | ||
308 | |||
309 | if ((url[0] != 'h') || (url[1] != 't') | ||
310 | || (url[2] != 't') || (url[3] != 'p')) | ||
311 | return GNUNET_SYSERR; | ||
312 | |||
313 | p2 = strchr (p1, ':'); | ||
314 | p3 = strchr (p1, '/'); | ||
315 | |||
316 | if (!p3) | ||
317 | return GNUNET_SYSERR; | ||
318 | |||
319 | memset (hostname, 0, MAX_HOSTNAME_LEN + 1); | ||
320 | |||
321 | if (!p2 || (p2 > p3)) | ||
322 | { | ||
323 | strncpy (hostname, p1, MIN (MAX_HOSTNAME_LEN, (int) (p3 - p1))); | ||
324 | *port = 80; | ||
325 | } | ||
326 | else | ||
327 | { | ||
328 | strncpy (hostname, p1, MIN (MAX_HOSTNAME_LEN, (int) (p2 - p1))); | ||
329 | *port = 0; | ||
330 | p2++; | ||
331 | |||
332 | while ((*p2 >= '0') && (*p2 <= '9')) | ||
333 | { | ||
334 | *port *= 10; | ||
335 | *port += (unsigned short) (*p2 - '0'); | ||
336 | p2++; | ||
337 | } | ||
338 | } | ||
339 | |||
340 | *path = p3; | ||
341 | return GNUNET_OK; | ||
342 | } | ||
343 | |||
344 | /** | ||
345 | * Send UPnP command to the device identified by url and service. | ||
346 | * | ||
347 | * @param sched scheduler to use for network tasks | ||
348 | * @param url control URL of the device | ||
349 | * @param service type of the service corresponding to the command | ||
350 | * @param action action to send | ||
351 | * @param args arguments for action | ||
352 | * @param caller_cb user callback to trigger when done | ||
353 | * @param caller_cls closure to pass to caller_cb | ||
354 | */ | ||
355 | void | ||
356 | UPNP_command_ (struct GNUNET_SCHEDULER_Handle *sched, | ||
357 | const char *url, const char *service, | ||
358 | const char *action, struct UPNP_Arg_ *args, | ||
359 | char *buffer, size_t buf_size, | ||
360 | UPNP_command_cb_ caller_cb, void *caller_cls) | ||
361 | { | ||
362 | struct GNUNET_CONNECTION_Handle *s; | ||
363 | struct UPNP_command_cls *cls; | ||
364 | struct sockaddr_in dest; | ||
365 | struct sockaddr_in6 dest6; | ||
366 | char hostname[MAX_HOSTNAME_LEN + 1]; | ||
367 | unsigned short port = 0; | ||
368 | char *path; | ||
369 | char soap_act[128]; | ||
370 | char soap_body[2048]; | ||
371 | int body_size; | ||
372 | char *content_buf; | ||
373 | int headers_size; | ||
374 | char port_str[8]; | ||
375 | |||
376 | snprintf (soap_act, sizeof (soap_act), "%s#%s", service, action); | ||
377 | |||
378 | if (args == NULL) | ||
379 | { | ||
380 | snprintf (soap_body, sizeof (soap_body), | ||
381 | "<?xml version=\"1.0\"?>\r\n" | ||
382 | "<" SOAP_PREFIX ":Envelope " | ||
383 | "xmlns:" SOAP_PREFIX | ||
384 | "=\"http://schemas.xmlsoap.org/soap/envelope/\" " | ||
385 | SOAP_PREFIX | ||
386 | ":encodingStyle=\"http://schema GNUNET_free (content_buf);s.xmlsoap.org/soap/encoding/\">" | ||
387 | "<" SOAP_PREFIX ":Body>" "<" SERVICE_PREFIX | ||
388 | ":%s xmlns:" SERVICE_PREFIX "=\"%s\">" "</" | ||
389 | SERVICE_PREFIX ":%s>" "</" SOAP_PREFIX | ||
390 | ":Body></" SOAP_PREFIX ":Envelope>" "\r\n", | ||
391 | action, service, action); | ||
392 | } | ||
393 | else | ||
394 | { | ||
395 | char *p; | ||
396 | const char *pe, *pv; | ||
397 | int soap_body_len; | ||
398 | |||
399 | soap_body_len = snprintf (soap_body, sizeof (soap_body), | ||
400 | "<?xml version=\"1.0\"?>\r\n" | ||
401 | "<" SOAP_PREFIX ":Envelope " | ||
402 | "xmlns:" SOAP_PREFIX | ||
403 | "=\"http://schemas.xmlsoap.org/soap/envelope/\" " | ||
404 | SOAP_PREFIX | ||
405 | ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" | ||
406 | "<" SOAP_PREFIX ":Body>" "<" SERVICE_PREFIX | ||
407 | ":%s xmlns:" SERVICE_PREFIX "=\"%s\">", | ||
408 | action, service); | ||
409 | |||
410 | p = soap_body + soap_body_len; | ||
411 | |||
412 | while (args->elt) | ||
413 | { | ||
414 | /* check that we are never overflowing the string... */ | ||
415 | if (soap_body + sizeof (soap_body) <= p + 100) | ||
416 | { | ||
417 | GNUNET_assert (GNUNET_NO); | ||
418 | (*caller_cb) (buffer, 0, caller_cls); | ||
419 | return; | ||
420 | } | ||
421 | *(p++) = '<'; | ||
422 | pe = args->elt; | ||
423 | while (*pe) | ||
424 | *(p++) = *(pe++); | ||
425 | *(p++) = '>'; | ||
426 | if ((pv = args->val)) | ||
427 | { | ||
428 | while (*pv) | ||
429 | *(p++) = *(pv++); | ||
430 | } | ||
431 | *(p++) = '<'; | ||
432 | *(p++) = '/'; | ||
433 | pe = args->elt; | ||
434 | while (*pe) | ||
435 | *(p++) = *(pe++); | ||
436 | *(p++) = '>'; | ||
437 | args++; | ||
438 | } | ||
439 | *(p++) = '<'; | ||
440 | *(p++) = '/'; | ||
441 | *(p++) = SERVICE_PREFIX2; | ||
442 | *(p++) = ':'; | ||
443 | pe = action; | ||
444 | |||
445 | while (*pe) | ||
446 | *(p++) = *(pe++); | ||
447 | |||
448 | strncpy (p, "></" SOAP_PREFIX ":Body></" SOAP_PREFIX ":Envelope>\r\n", | ||
449 | soap_body + sizeof (soap_body) - p); | ||
450 | } | ||
451 | |||
452 | if (GNUNET_OK != parse_url (url, hostname, &port, &path)) | ||
453 | { | ||
454 | GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP", | ||
455 | "Invalid URL passed to UPNP_command(): %s\n", url); | ||
456 | return; | ||
457 | } | ||
458 | |||
459 | |||
460 | /* Test IPv4 address, else use IPv6 */ | ||
461 | memset (&dest, 0, sizeof (dest)); | ||
462 | memset (&dest6, 0, sizeof (dest6)); | ||
463 | |||
464 | if (inet_pton (AF_INET, hostname, &dest.sin_addr) == 1) | ||
465 | { | ||
466 | dest.sin_family = AF_INET; | ||
467 | dest.sin_port = htons (port); | ||
468 | #ifdef HAVE_SOCKADDR_IN_SIN_LEN | ||
469 | dest.sin_len = sizeof (dest); | ||
470 | #endif | ||
471 | |||
472 | s = GNUNET_CONNECTION_create_from_sockaddr (sched, PF_INET, | ||
473 | (struct sockaddr *) &dest, | ||
474 | sizeof (dest)); | ||
475 | } | ||
476 | else if (inet_pton (AF_INET6, hostname, &dest6.sin6_addr) == 1) | ||
477 | { | ||
478 | dest6.sin6_family = AF_INET6; | ||
479 | dest6.sin6_port = htons (port); | ||
480 | #ifdef HAVE_SOCKADDR_IN_SIN_LEN | ||
481 | dest6.sin6_len = sizeof (dest6); | ||
482 | #endif | ||
483 | |||
484 | s = GNUNET_CONNECTION_create_from_sockaddr (sched, PF_INET6, | ||
485 | (struct sockaddr *) &dest6, | ||
486 | sizeof (dest6)); | ||
487 | } | ||
488 | else | ||
489 | { | ||
490 | GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, _("%s failed at %s:%d\n"), | ||
491 | "UPnP", "inet_pton", __FILE__, __LINE__); | ||
492 | |||
493 | (*caller_cb) (buffer, 0, caller_cls); | ||
494 | return; | ||
495 | } | ||
496 | |||
497 | body_size = (int) strlen (soap_body); | ||
498 | content_buf = GNUNET_malloc (512 + body_size); | ||
499 | |||
500 | /* We are not using keep-alive HTTP connections. | ||
501 | * HTTP/1.1 needs the header Connection: close to do that. | ||
502 | * This is the default with HTTP/1.0 */ | ||
503 | /* Connection: Close is normally there only in HTTP/1.1 but who knows */ | ||
504 | port_str[0] = '\0'; | ||
505 | |||
506 | if (port != 80) | ||
507 | snprintf (port_str, sizeof (port_str), ":%hu", port); | ||
508 | |||
509 | headers_size = snprintf (content_buf, 512, "POST %s HTTP/1.1\r\n" "Host: %s%s\r\n" "User-Agent: GNU, UPnP/1.0, GNUnet/" PACKAGE_VERSION "\r\n" "Content-Length: %d\r\n" "Content-Type: text/xml\r\n" "SOAPAction: \"%s\"\r\n" "Connection: Close\r\n" "Cache-Control: no-cache\r\n" /* ??? */ | ||
510 | "Pragma: no-cache\r\n" | ||
511 | "\r\n", path, hostname, port_str, body_size, | ||
512 | soap_act); | ||
513 | memcpy (content_buf + headers_size, soap_body, body_size); | ||
514 | |||
515 | #ifdef DEBUG_UPNP | ||
516 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", | ||
517 | "Sending command '%s' to '%s' (service '%s')\n", | ||
518 | action, url, service); | ||
519 | #endif | ||
520 | |||
521 | cls = GNUNET_malloc (sizeof (struct UPNP_command_cls)); | ||
522 | cls->s = s; | ||
523 | cls->content = content_buf; | ||
524 | cls->buffer = buffer; | ||
525 | cls->buf_size = buf_size; | ||
526 | cls->caller_cb = caller_cb; | ||
527 | cls->caller_cls = caller_cls; | ||
528 | |||
529 | cls->th = | ||
530 | GNUNET_CONNECTION_notify_transmit_ready (s, body_size + headers_size, | ||
531 | GNUNET_TIME_relative_multiply | ||
532 | (GNUNET_TIME_UNIT_SECONDS, 15), | ||
533 | &UPNP_command_transmit, cls); | ||
534 | |||
535 | |||
536 | if (cls->th == NULL) | ||
537 | { | ||
538 | #ifdef DEBUG_UPNP | ||
539 | GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP", | ||
540 | "Error sending SOAP request at %s:%d\n", __FILE__, | ||
541 | __LINE__); | ||
542 | #endif | ||
543 | |||
544 | (*caller_cb) (buffer, 0, caller_cls); | ||
545 | |||
546 | GNUNET_free (content_buf); | ||
547 | GNUNET_free (cls); | ||
548 | GNUNET_CONNECTION_destroy (s, GNUNET_NO); | ||
549 | return; | ||
550 | } | ||
551 | } | ||
552 | |||
553 | struct get_external_ip_address_cls | ||
554 | { | ||
555 | UPNP_get_external_ip_address_cb_ caller_cb; | ||
556 | void *caller_cls; | ||
557 | }; | ||
558 | |||
559 | static void | ||
560 | get_external_ip_address_receiver (char *response, size_t received, void *data) | ||
561 | { | ||
562 | struct get_external_ip_address_cls *cls = data; | ||
563 | struct UPNP_REPLY_NameValueList_ pdata; | ||
564 | char extIpAdd[128]; | ||
565 | char *p; | ||
566 | int ret = UPNP_COMMAND_UNKNOWN_ERROR; | ||
567 | |||
568 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Response: %s", response); | ||
569 | |||
570 | UPNP_REPLY_parse_ (response, received, &pdata); | ||
571 | p = UPNP_REPLY_get_value_ (&pdata, "NewExternalIPAddress"); | ||
572 | if (p) | ||
573 | { | ||
574 | strncpy (extIpAdd, p, 128); | ||
575 | extIpAdd[127] = '\0'; | ||
576 | ret = UPNP_COMMAND_SUCCESS; | ||
577 | } | ||
578 | else | ||
579 | extIpAdd[0] = '\0'; | ||
580 | |||
581 | p = UPNP_REPLY_get_value_ (&pdata, "errorCode"); | ||
582 | if (p) | ||
583 | { | ||
584 | ret = UPNP_COMMAND_UNKNOWN_ERROR; | ||
585 | sscanf (p, "%d", &ret); | ||
586 | } | ||
587 | cls->caller_cb (ret, extIpAdd, cls->caller_cls); | ||
588 | |||
589 | UPNP_REPLY_free_ (&pdata); | ||
590 | GNUNET_free (response); | ||
591 | GNUNET_free (cls); | ||
592 | } | ||
593 | |||
594 | /* UPNP_get_external_ip_address_() call the corresponding UPNP method. | ||
595 | * | ||
596 | * Return values : | ||
597 | * 0 : SUCCESS | ||
598 | * NON ZERO : ERROR Either an UPnP error code or an unknown error. | ||
599 | * | ||
600 | * 402 Invalid Args - See UPnP Device Architecture section on Control. | ||
601 | * 501 Action Failed - See UPnP Device Architecture section on Control. | ||
602 | */ | ||
603 | void | ||
604 | UPNP_get_external_ip_address_ (struct GNUNET_SCHEDULER_Handle *sched, | ||
605 | const char *control_url, | ||
606 | const char *service_type, | ||
607 | UPNP_get_external_ip_address_cb_ caller_cb, | ||
608 | void *caller_cls) | ||
609 | { | ||
610 | struct get_external_ip_address_cls *cls; | ||
611 | char *buffer; | ||
612 | |||
613 | if (!control_url || !service_type) | ||
614 | caller_cb (UPNP_COMMAND_INVALID_ARGS, NULL, caller_cls); | ||
615 | |||
616 | cls = GNUNET_malloc (sizeof (struct get_external_ip_address_cls)); | ||
617 | cls->caller_cb = caller_cb; | ||
618 | cls->caller_cls = caller_cls; | ||
619 | |||
620 | buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE); | ||
621 | |||
622 | UPNP_command_ (sched, control_url, service_type, "GetExternalIPAddress", | ||
623 | NULL, buffer, UPNP_COMMAND_BUFSIZE, | ||
624 | (UPNP_command_cb_) get_external_ip_address_receiver, cls); | ||
625 | } | ||
626 | |||
627 | struct PortMapping_cls | ||
628 | { | ||
629 | const char *control_url; | ||
630 | const char *service_type; | ||
631 | const char *ext_port; | ||
632 | const char *in_port; | ||
633 | const char *proto; | ||
634 | const char *remoteHost; | ||
635 | UPNP_port_mapping_cb_ caller_cb; | ||
636 | void *caller_cls; | ||
637 | }; | ||
638 | |||
639 | static void | ||
640 | add_delete_port_mapping_receiver (char *response, size_t received, void *data) | ||
641 | { | ||
642 | struct PortMapping_cls *cls = data; | ||
643 | struct UPNP_REPLY_NameValueList_ pdata; | ||
644 | const char *resVal; | ||
645 | int ret; | ||
646 | |||
647 | UPNP_REPLY_parse_ (response, received, &pdata); | ||
648 | resVal = UPNP_REPLY_get_value_ (&pdata, "errorCode"); | ||
649 | if (resVal) | ||
650 | { | ||
651 | ret = UPNP_COMMAND_UNKNOWN_ERROR; | ||
652 | sscanf (resVal, "%d", &ret); | ||
653 | } | ||
654 | else | ||
655 | { | ||
656 | ret = UPNP_COMMAND_SUCCESS; | ||
657 | } | ||
658 | |||
659 | cls->caller_cb (ret, cls->control_url, cls->service_type, | ||
660 | cls->ext_port, cls->in_port, cls->proto, | ||
661 | cls->remoteHost, cls->caller_cls); | ||
662 | |||
663 | UPNP_REPLY_free_ (&pdata); | ||
664 | GNUNET_free (response); | ||
665 | GNUNET_free (cls); | ||
666 | } | ||
667 | |||
668 | void | ||
669 | UPNP_add_port_mapping_ (struct GNUNET_SCHEDULER_Handle *sched, | ||
670 | const char *control_url, const char *service_type, | ||
671 | const char *ext_port, | ||
672 | const char *in_port, | ||
673 | const char *inClient, | ||
674 | const char *desc, | ||
675 | const char *proto, const char *remoteHost, | ||
676 | UPNP_port_mapping_cb_ caller_cb, void *caller_cls) | ||
677 | { | ||
678 | struct UPNP_Arg_ args[9]; | ||
679 | struct PortMapping_cls *cls; | ||
680 | char *buffer; | ||
681 | |||
682 | if (!in_port || !inClient || !proto || !ext_port) | ||
683 | { | ||
684 | caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type, | ||
685 | ext_port, in_port, proto, remoteHost, caller_cls); | ||
686 | return; | ||
687 | } | ||
688 | |||
689 | args[0].elt = "NewRemoteHost"; | ||
690 | args[0].val = remoteHost; | ||
691 | args[1].elt = "NewExternalPort"; | ||
692 | args[1].val = ext_port; | ||
693 | args[2].elt = "NewProtocol"; | ||
694 | args[2].val = proto; | ||
695 | args[3].elt = "NewInternalPort"; | ||
696 | args[3].val = in_port; | ||
697 | args[4].elt = "NewInternalClient"; | ||
698 | args[4].val = inClient; | ||
699 | args[5].elt = "NewEnabled"; | ||
700 | args[5].val = "1"; | ||
701 | args[6].elt = "NewPortMappingDescription"; | ||
702 | args[6].val = desc ? desc : "GNUnet"; | ||
703 | args[7].elt = "NewLeaseDuration"; | ||
704 | args[7].val = "0"; | ||
705 | args[8].elt = NULL; | ||
706 | args[8].val = NULL; | ||
707 | |||
708 | cls = GNUNET_malloc (sizeof (struct PortMapping_cls)); | ||
709 | cls->control_url = control_url; | ||
710 | cls->service_type = service_type; | ||
711 | cls->ext_port = ext_port;; | ||
712 | cls->in_port = in_port; | ||
713 | cls->proto = proto; | ||
714 | cls->remoteHost = remoteHost; | ||
715 | cls->caller_cb = caller_cb; | ||
716 | cls->caller_cls = caller_cls; | ||
717 | |||
718 | buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE); | ||
719 | |||
720 | UPNP_command_ (sched, control_url, service_type, "AddPortMapping", | ||
721 | args, buffer, UPNP_COMMAND_BUFSIZE, | ||
722 | add_delete_port_mapping_receiver, cls); | ||
723 | } | ||
724 | |||
725 | void | ||
726 | UPNP_delete_port_mapping_ (struct GNUNET_SCHEDULER_Handle *sched, | ||
727 | const char *control_url, const char *service_type, | ||
728 | const char *ext_port, const char *proto, | ||
729 | const char *remoteHost, | ||
730 | UPNP_port_mapping_cb_ caller_cb, void *caller_cls) | ||
731 | { | ||
732 | struct UPNP_Arg_ args[4]; | ||
733 | struct PortMapping_cls *cls; | ||
734 | char *buffer; | ||
735 | |||
736 | if (!ext_port || !proto) | ||
737 | { | ||
738 | caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type, | ||
739 | ext_port, NULL, proto, remoteHost, caller_cls); | ||
740 | return; | ||
741 | } | ||
742 | |||
743 | args[0].elt = "NewRemoteHost"; | ||
744 | args[0].val = remoteHost; | ||
745 | args[1].elt = "NewExternalPort"; | ||
746 | args[1].val = ext_port; | ||
747 | args[2].elt = "NewProtocol"; | ||
748 | args[2].val = proto; | ||
749 | args[3].elt = NULL; | ||
750 | args[3].val = NULL; | ||
751 | |||
752 | cls = GNUNET_malloc (sizeof (struct PortMapping_cls)); | ||
753 | cls->control_url = control_url; | ||
754 | cls->service_type = service_type; | ||
755 | cls->ext_port = ext_port; | ||
756 | cls->in_port = "0"; | ||
757 | cls->proto = proto; | ||
758 | cls->remoteHost = remoteHost; | ||
759 | cls->caller_cb = caller_cb; | ||
760 | cls->caller_cls = caller_cls; | ||
761 | |||
762 | buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE); | ||
763 | |||
764 | UPNP_command_ (sched, control_url, service_type, | ||
765 | "DeletePortMapping", | ||
766 | args, buffer, UPNP_COMMAND_BUFSIZE, | ||
767 | add_delete_port_mapping_receiver, cls); | ||
768 | } | ||
769 | |||
770 | |||
771 | struct get_specific_port_mapping_entry_cls | ||
772 | { | ||
773 | const char *control_url; | ||
774 | const char *service_type; | ||
775 | const char *ext_port; | ||
776 | const char *proto; | ||
777 | UPNP_port_mapping_cb_ caller_cb; | ||
778 | void *caller_cls; | ||
779 | }; | ||
780 | |||
781 | static void | ||
782 | get_specific_port_mapping_entry_receiver (char *response, size_t received, | ||
783 | void *data) | ||
784 | { | ||
785 | struct PortMapping_cls *cls = data; | ||
786 | struct UPNP_REPLY_NameValueList_ pdata; | ||
787 | char *p; | ||
788 | char in_port[128]; | ||
789 | char in_client[128]; | ||
790 | int ret; | ||
791 | |||
792 | UPNP_REPLY_parse_ (response, received, &pdata); | ||
793 | |||
794 | p = UPNP_REPLY_get_value_ (&pdata, "NewInternalClient"); | ||
795 | if (p) | ||
796 | { | ||
797 | strncpy (in_client, p, 128); | ||
798 | in_client[127] = '\0'; | ||
799 | } | ||
800 | else | ||
801 | in_client[0] = '\0'; | ||
802 | |||
803 | p = UPNP_REPLY_get_value_ (&pdata, "NewInternalPort"); | ||
804 | if (p) | ||
805 | { | ||
806 | strncpy (in_port, p, 6); | ||
807 | in_port[5] = '\0'; | ||
808 | } | ||
809 | else | ||
810 | in_port[0] = '\0'; | ||
811 | |||
812 | p = UPNP_REPLY_get_value_ (&pdata, "errorCode"); | ||
813 | if (p) | ||
814 | { | ||
815 | if (p) | ||
816 | { | ||
817 | ret = UPNP_COMMAND_UNKNOWN_ERROR; | ||
818 | sscanf (p, "%d", &ret); | ||
819 | } | ||
820 | #if DEBUG_UPNP | ||
821 | PRINT_UPNP_ERROR ("GetSpecificPortMappingEntry", p); | ||
822 | #endif | ||
823 | } | ||
824 | |||
825 | cls->caller_cb (ret, cls->control_url, cls->service_type, | ||
826 | cls->ext_port, cls->proto, in_port, in_client, | ||
827 | cls->caller_cls); | ||
828 | |||
829 | UPNP_REPLY_free_ (&pdata); | ||
830 | GNUNET_free (response); | ||
831 | GNUNET_free (cls); | ||
832 | } | ||
833 | |||
834 | /* UPNP_get_specific_port_mapping_entry _ retrieves an existing port mapping | ||
835 | * the result is returned in the in_client and in_port strings | ||
836 | * please provide 128 and 6 bytes of data */ | ||
837 | void | ||
838 | UPNP_get_specific_port_mapping_entry_ (struct GNUNET_SCHEDULER_Handle *sched, | ||
839 | const char *control_url, | ||
840 | const char *service_type, | ||
841 | const char *ext_port, | ||
842 | const char *proto, | ||
843 | UPNP_get_specific_port_mapping_entry_cb_ | ||
844 | caller_cb, void *caller_cls) | ||
845 | { | ||
846 | struct UPNP_Arg_ args[4]; | ||
847 | struct get_specific_port_mapping_entry_cls *cls; | ||
848 | char *buffer; | ||
849 | |||
850 | if (!ext_port || !proto) | ||
851 | { | ||
852 | caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type, | ||
853 | ext_port, proto, NULL, NULL, caller_cls); | ||
854 | return; | ||
855 | } | ||
856 | |||
857 | args[0].elt = "NewRemoteHost"; | ||
858 | args[0].val = NULL; | ||
859 | args[1].elt = "NewExternalPort"; | ||
860 | args[1].val = ext_port; | ||
861 | args[2].elt = "NewProtocol"; | ||
862 | args[2].val = proto; | ||
863 | args[3].elt = NULL; | ||
864 | args[3].val = NULL; | ||
865 | |||
866 | cls = GNUNET_malloc (sizeof (struct PortMapping_cls)); | ||
867 | cls->control_url = control_url; | ||
868 | cls->service_type = service_type; | ||
869 | cls->ext_port = ext_port; | ||
870 | cls->proto = proto; | ||
871 | cls->caller_cb = caller_cb; | ||
872 | cls->caller_cls = caller_cls; | ||
873 | |||
874 | buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE); | ||
875 | |||
876 | UPNP_command_ (sched, control_url, service_type, | ||
877 | "GetSpecificPortMappingEntry", | ||
878 | args, buffer, UPNP_COMMAND_BUFSIZE, | ||
879 | get_specific_port_mapping_entry_receiver, cls); | ||
880 | } | ||