rework UPnP code to use GNUnet scheduler and network API
[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       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 }