Refactoring gnunet time
[oweals/gnunet.git] / src / nat / upnp-commands.c
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       caller_cb (buffer, 0, caller_cls);
457       return;
458     }
459
460
461   /* Test IPv4 address, else use IPv6 */
462   memset (&dest, 0, sizeof (dest));
463   memset (&dest6, 0, sizeof (dest6));
464
465   if (inet_pton (AF_INET, hostname, &dest.sin_addr) == 1)
466     {
467       dest.sin_family = AF_INET;
468       dest.sin_port = htons (port);
469 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
470       dest.sin_len = sizeof (dest);
471 #endif
472
473       s = GNUNET_CONNECTION_create_from_sockaddr (sched, PF_INET,
474                                                   (struct sockaddr *) &dest,
475                                                   sizeof (dest));
476     }
477   else if (inet_pton (AF_INET6, hostname, &dest6.sin6_addr) == 1)
478     {
479       dest6.sin6_family = AF_INET6;
480       dest6.sin6_port = htons (port);
481 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
482       dest6.sin6_len = sizeof (dest6);
483 #endif
484
485       s = GNUNET_CONNECTION_create_from_sockaddr (sched, PF_INET6,
486                                                   (struct sockaddr *) &dest6,
487                                                   sizeof (dest6));
488     }
489   else
490     {
491       GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, _("%s failed at %s:%d\n"),
492                        "UPnP", "inet_pton", __FILE__, __LINE__);
493
494       caller_cb (buffer, 0, caller_cls);
495       return;
496     }
497
498   body_size = (int) strlen (soap_body);
499   content_buf = GNUNET_malloc (512 + body_size);
500
501   /* We are not using keep-alive HTTP connections.
502    * HTTP/1.1 needs the header Connection: close to do that.
503    * This is the default with HTTP/1.0 */
504   /* Connection: Close is normally there only in HTTP/1.1 but who knows */
505   port_str[0] = '\0';
506
507   if (port != 80)
508     snprintf (port_str, sizeof (port_str), ":%hu", port);
509
510   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"   /* ??? */
511                            "Pragma: no-cache\r\n"
512                            "\r\n", path, hostname, port_str, body_size,
513                            soap_act);
514   memcpy (content_buf + headers_size, soap_body, body_size);
515
516 #ifdef DEBUG_UPNP
517   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
518                    "Sending command '%s' to '%s' (service '%s')\n",
519                    action, url, service);
520 #endif
521
522   cls = GNUNET_malloc (sizeof (struct UPNP_command_cls));
523   cls->s = s;
524   cls->content = content_buf;
525   cls->buffer = buffer;
526   cls->buf_size = buf_size;
527   cls->caller_cb = caller_cb;
528   cls->caller_cls = caller_cls;
529
530   cls->th =
531     GNUNET_CONNECTION_notify_transmit_ready (s, body_size + headers_size,
532                                              GNUNET_TIME_relative_multiply
533                                              (GNUNET_TIME_UNIT_SECONDS, 15),
534                                              &UPNP_command_transmit, cls);
535
536
537   if (cls->th == NULL)
538     {
539 #ifdef DEBUG_UPNP
540       GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP",
541                        "Error sending SOAP request at %s:%d\n", __FILE__,
542                        __LINE__);
543 #endif
544
545       caller_cb (buffer, 0, caller_cls);
546
547       GNUNET_free (content_buf);
548       GNUNET_free (cls);
549       GNUNET_CONNECTION_destroy (s, GNUNET_NO);
550       return;
551     }
552 }
553
554 struct get_external_ip_address_cls
555 {
556   UPNP_get_external_ip_address_cb_ caller_cb;
557   void *caller_cls;
558 };
559
560 static void
561 get_external_ip_address_receiver (char *response, size_t received, void *data)
562 {
563   struct get_external_ip_address_cls *cls = data;
564   struct UPNP_REPLY_NameValueList_ pdata;
565   char extIpAdd[128];
566   char *p;
567   int ret = UPNP_COMMAND_UNKNOWN_ERROR;
568
569   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Response: %s", response);
570
571   UPNP_REPLY_parse_ (response, received, &pdata);
572   p = UPNP_REPLY_get_value_ (&pdata, "NewExternalIPAddress");
573   if (p)
574     {
575       strncpy (extIpAdd, p, 128);
576       extIpAdd[127] = '\0';
577       ret = UPNP_COMMAND_SUCCESS;
578     }
579   else
580     extIpAdd[0] = '\0';
581
582   p = UPNP_REPLY_get_value_ (&pdata, "errorCode");
583   if (p)
584     {
585       ret = UPNP_COMMAND_UNKNOWN_ERROR;
586       sscanf (p, "%d", &ret);
587     }
588   cls->caller_cb (ret, extIpAdd, cls->caller_cls);
589
590   UPNP_REPLY_free_ (&pdata);
591   GNUNET_free (response);
592   GNUNET_free (cls);
593 }
594
595 /* UPNP_get_external_ip_address_() call the corresponding UPNP method.
596  * 
597  * Return values :
598  * 0 : SUCCESS
599  * NON ZERO : ERROR Either an UPnP error code or an unknown error.
600  *
601  * 402 Invalid Args - See UPnP Device Architecture section on Control.
602  * 501 Action Failed - See UPnP Device Architecture section on Control.
603  */
604 void
605 UPNP_get_external_ip_address_ (struct GNUNET_SCHEDULER_Handle *sched,
606                                const char *control_url,
607                                const char *service_type,
608                                UPNP_get_external_ip_address_cb_ caller_cb,
609                                void *caller_cls)
610 {
611   struct get_external_ip_address_cls *cls;
612   char *buffer;
613
614   if (!control_url || !service_type)
615     caller_cb (UPNP_COMMAND_INVALID_ARGS, NULL, caller_cls);
616
617   cls = GNUNET_malloc (sizeof (struct get_external_ip_address_cls));
618   cls->caller_cb = caller_cb;
619   cls->caller_cls = caller_cls;
620
621   buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
622
623   UPNP_command_ (sched, control_url, service_type, "GetExternalIPAddress",
624                  NULL, buffer, UPNP_COMMAND_BUFSIZE,
625                  (UPNP_command_cb_) get_external_ip_address_receiver, cls);
626 }
627
628 struct PortMapping_cls
629 {
630   const char *control_url;
631   const char *service_type;
632   const char *ext_port;
633   const char *in_port;
634   const char *proto;
635   const char *remoteHost;
636   UPNP_port_mapping_cb_ caller_cb;
637   void *caller_cls;
638 };
639
640 static void
641 add_delete_port_mapping_receiver (char *response, size_t received, void *data)
642 {
643   struct PortMapping_cls *cls = data;
644   struct UPNP_REPLY_NameValueList_ pdata;
645   const char *resVal;
646   int ret;
647
648   UPNP_REPLY_parse_ (response, received, &pdata);
649   resVal = UPNP_REPLY_get_value_ (&pdata, "errorCode");
650   if (resVal)
651     {
652       ret = UPNP_COMMAND_UNKNOWN_ERROR;
653       sscanf (resVal, "%d", &ret);
654     }
655   else
656     {
657       ret = UPNP_COMMAND_SUCCESS;
658     }
659
660   cls->caller_cb (ret, cls->control_url, cls->service_type,
661                   cls->ext_port, cls->in_port, cls->proto,
662                   cls->remoteHost, cls->caller_cls);
663
664   UPNP_REPLY_free_ (&pdata);
665   GNUNET_free (response);
666   GNUNET_free (cls);
667 }
668
669 void
670 UPNP_add_port_mapping_ (struct GNUNET_SCHEDULER_Handle *sched,
671                         const char *control_url, const char *service_type,
672                         const char *ext_port,
673                         const char *in_port,
674                         const char *inClient,
675                         const char *desc,
676                         const char *proto, const char *remoteHost,
677                         UPNP_port_mapping_cb_ caller_cb, void *caller_cls)
678 {
679   struct UPNP_Arg_ args[9];
680   struct PortMapping_cls *cls;
681   char *buffer;
682
683   if (!in_port || !inClient || !proto || !ext_port)
684     {
685       caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
686                  ext_port, in_port, proto, remoteHost, caller_cls);
687       return;
688     }
689
690   args[0].elt = "NewRemoteHost";
691   args[0].val = remoteHost;
692   args[1].elt = "NewExternalPort";
693   args[1].val = ext_port;
694   args[2].elt = "NewProtocol";
695   args[2].val = proto;
696   args[3].elt = "NewInternalPort";
697   args[3].val = in_port;
698   args[4].elt = "NewInternalClient";
699   args[4].val = inClient;
700   args[5].elt = "NewEnabled";
701   args[5].val = "1";
702   args[6].elt = "NewPortMappingDescription";
703   args[6].val = desc ? desc : "GNUnet";
704   args[7].elt = "NewLeaseDuration";
705   args[7].val = "0";
706   args[8].elt = NULL;
707   args[8].val = NULL;
708
709   cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
710   cls->control_url = control_url;
711   cls->service_type = service_type;
712   cls->ext_port = ext_port;;
713   cls->in_port = in_port;
714   cls->proto = proto;
715   cls->remoteHost = remoteHost;
716   cls->caller_cb = caller_cb;
717   cls->caller_cls = caller_cls;
718
719   buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
720
721   UPNP_command_ (sched, control_url, service_type, "AddPortMapping",
722                  args, buffer, UPNP_COMMAND_BUFSIZE,
723                  add_delete_port_mapping_receiver, cls);
724 }
725
726 void
727 UPNP_delete_port_mapping_ (struct GNUNET_SCHEDULER_Handle *sched,
728                            const char *control_url, const char *service_type,
729                            const char *ext_port, const char *proto,
730                            const char *remoteHost,
731                            UPNP_port_mapping_cb_ caller_cb, void *caller_cls)
732 {
733   struct UPNP_Arg_ args[4];
734   struct PortMapping_cls *cls;
735   char *buffer;
736
737   if (!ext_port || !proto)
738     {
739       caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
740                  ext_port, NULL, proto, remoteHost, caller_cls);
741       return;
742     }
743
744   args[0].elt = "NewRemoteHost";
745   args[0].val = remoteHost;
746   args[1].elt = "NewExternalPort";
747   args[1].val = ext_port;
748   args[2].elt = "NewProtocol";
749   args[2].val = proto;
750   args[3].elt = NULL;
751   args[3].val = NULL;
752
753   cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
754   cls->control_url = control_url;
755   cls->service_type = service_type;
756   cls->ext_port = ext_port;
757   cls->in_port = "0";
758   cls->proto = proto;
759   cls->remoteHost = remoteHost;
760   cls->caller_cb = caller_cb;
761   cls->caller_cls = caller_cls;
762
763   buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
764
765   UPNP_command_ (sched, control_url, service_type,
766                  "DeletePortMapping",
767                  args, buffer, UPNP_COMMAND_BUFSIZE,
768                  add_delete_port_mapping_receiver, cls);
769 }
770
771
772 struct get_specific_port_mapping_entry_cls
773 {
774   const char *control_url;
775   const char *service_type;
776   const char *ext_port;
777   const char *proto;
778   UPNP_port_mapping_cb_ caller_cb;
779   void *caller_cls;
780 };
781
782 static void
783 get_specific_port_mapping_entry_receiver (char *response, size_t received,
784                                           void *data)
785 {
786   struct PortMapping_cls *cls = data;
787   struct UPNP_REPLY_NameValueList_ pdata;
788   char *p;
789   char in_port[128];
790   char in_client[128];
791   int ret;
792
793   UPNP_REPLY_parse_ (response, received, &pdata);
794
795   p = UPNP_REPLY_get_value_ (&pdata, "NewInternalClient");
796   if (p)
797     {
798       strncpy (in_client, p, 128);
799       in_client[127] = '\0';
800     }
801   else
802     in_client[0] = '\0';
803
804   p = UPNP_REPLY_get_value_ (&pdata, "NewInternalPort");
805   if (p)
806     {
807       strncpy (in_port, p, 6);
808       in_port[5] = '\0';
809     }
810   else
811     in_port[0] = '\0';
812
813   p = UPNP_REPLY_get_value_ (&pdata, "errorCode");
814   if (p)
815     {
816       if (p)
817         {
818           ret = UPNP_COMMAND_UNKNOWN_ERROR;
819           sscanf (p, "%d", &ret);
820         }
821 #if DEBUG_UPNP
822       PRINT_UPNP_ERROR ("GetSpecificPortMappingEntry", p);
823 #endif
824     }
825
826   cls->caller_cb (ret, cls->control_url, cls->service_type,
827                   cls->ext_port, cls->proto, in_port, in_client,
828                   cls->caller_cls);
829
830   UPNP_REPLY_free_ (&pdata);
831   GNUNET_free (response);
832   GNUNET_free (cls);
833 }
834
835 /* UPNP_get_specific_port_mapping_entry _ retrieves an existing port mapping
836  * the result is returned in the in_client and in_port strings
837  * please provide 128 and 6 bytes of data */
838 void
839 UPNP_get_specific_port_mapping_entry_ (struct GNUNET_SCHEDULER_Handle *sched,
840                                        const char *control_url,
841                                        const char *service_type,
842                                        const char *ext_port,
843                                        const char *proto,
844                                        UPNP_get_specific_port_mapping_entry_cb_
845                                        caller_cb, void *caller_cls)
846 {
847   struct UPNP_Arg_ args[4];
848   struct get_specific_port_mapping_entry_cls *cls;
849   char *buffer;
850
851   if (!ext_port || !proto)
852     {
853       caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
854                  ext_port, proto, NULL, NULL, caller_cls);
855       return;
856     }
857
858   args[0].elt = "NewRemoteHost";
859   args[0].val = NULL;
860   args[1].elt = "NewExternalPort";
861   args[1].val = ext_port;
862   args[2].elt = "NewProtocol";
863   args[2].val = proto;
864   args[3].elt = NULL;
865   args[3].val = NULL;
866
867   cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
868   cls->control_url = control_url;
869   cls->service_type = service_type;
870   cls->ext_port = ext_port;
871   cls->proto = proto;
872   cls->caller_cb = caller_cb;
873   cls->caller_cls = caller_cls;
874
875   buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
876
877   UPNP_command_ (sched, control_url, service_type,
878                  "GetSpecificPortMappingEntry",
879                  args, buffer, UPNP_COMMAND_BUFSIZE,
880                  get_specific_port_mapping_entry_receiver, cls);
881 }