aboutsummaryrefslogtreecommitdiff
path: root/src/nat/upnp-commands.c
diff options
context:
space:
mode:
authorMoon <moon@140774ce-b5e7-0310-ab8b-a85725594a96>2010-10-09 13:53:47 +0000
committerMoon <moon@140774ce-b5e7-0310-ab8b-a85725594a96>2010-10-09 13:53:47 +0000
commit922a0672749ba9d496d1dd8f6596bb4f8035e71d (patch)
treeccd4b351bd5acd5cc3b8b1cc358c712d6c0c116d /src/nat/upnp-commands.c
parentdc24b5bf44bf8d9460b2571fe529403637aa3e16 (diff)
downloadgnunet-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.c880
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 */
81struct 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 */
126static ssize_t
127get_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 */
185static void
186get_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 */
232static void
233UPNP_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 */
268static size_t
269UPNP_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 */
294int
295parse_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 */
355void
356UPNP_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
553struct get_external_ip_address_cls
554{
555 UPNP_get_external_ip_address_cb_ caller_cb;
556 void *caller_cls;
557};
558
559static void
560get_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 */
603void
604UPNP_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
627struct 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
639static void
640add_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
668void
669UPNP_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
725void
726UPNP_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
771struct 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
781static void
782get_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 */
837void
838UPNP_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}