Fix W32 interface listing, and correctly copy addr in test-nat (patch by LRN)
[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, GNUNET_MIN (MAX_HOSTNAME_LEN, (int) (p3 - p1)));
324       *port = 80;
325     }
326   else
327     {
328       strncpy (hostname, p1, GNUNET_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 url control URL of the device
348  * @param service type of the service corresponding to the command
349  * @param action action to send
350  * @param args arguments for action
351  * @param caller_cb user callback to trigger when done
352  * @param caller_cls closure to pass to caller_cb
353  */
354 void
355 UPNP_command_ (const char *url, const char *service,
356                const char *action, struct UPNP_Arg_ *args,
357                char *buffer, size_t buf_size,
358                UPNP_command_cb_ caller_cb, void *caller_cls)
359 {
360   struct GNUNET_CONNECTION_Handle *s;
361   struct UPNP_command_cls *cls;
362   struct sockaddr_in dest;
363   struct sockaddr_in6 dest6;
364   char hostname[MAX_HOSTNAME_LEN + 1];
365   unsigned short port = 0;
366   char *path;
367   char soap_act[128];
368   char soap_body[2048];
369   int body_size;
370   char *content_buf;
371   int headers_size;
372   char port_str[8];
373
374   snprintf (soap_act, sizeof (soap_act), "%s#%s", service, action);
375
376   if (args == NULL)
377     {
378       snprintf (soap_body, sizeof (soap_body),
379                 "<?xml version=\"1.0\"?>\r\n"
380                 "<" SOAP_PREFIX ":Envelope "
381                 "xmlns:" SOAP_PREFIX
382                 "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
383                 SOAP_PREFIX
384                 ":encodingStyle=\"http://schema      GNUNET_free (content_buf);s.xmlsoap.org/soap/encoding/\">"
385                 "<" SOAP_PREFIX ":Body>" "<" SERVICE_PREFIX
386                 ":%s xmlns:" SERVICE_PREFIX "=\"%s\">" "</"
387                 SERVICE_PREFIX ":%s>" "</" SOAP_PREFIX
388                 ":Body></" SOAP_PREFIX ":Envelope>" "\r\n",
389                 action, service, action);
390     }
391   else
392     {
393       char *p;
394       const char *pe, *pv;
395       int soap_body_len;
396
397       soap_body_len = snprintf (soap_body, sizeof (soap_body),
398                                 "<?xml version=\"1.0\"?>\r\n"
399                                 "<" SOAP_PREFIX ":Envelope "
400                                 "xmlns:" SOAP_PREFIX
401                                 "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
402                                 SOAP_PREFIX
403                                 ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
404                                 "<" SOAP_PREFIX ":Body>" "<" SERVICE_PREFIX
405                                 ":%s xmlns:" SERVICE_PREFIX "=\"%s\">",
406                                 action, service);
407
408       p = soap_body + soap_body_len;
409
410       while (args->elt)
411         {
412           /* check that we are never overflowing the string... */
413           if (soap_body + sizeof (soap_body) <= p + 100)
414             {
415               GNUNET_assert (GNUNET_NO);
416               caller_cb (buffer, 0, caller_cls);
417               return;
418             }
419           *(p++) = '<';
420           pe = args->elt;
421           while (*pe)
422             *(p++) = *(pe++);
423           *(p++) = '>';
424           if ((pv = args->val))
425             {
426               while (*pv)
427                 *(p++) = *(pv++);
428             }
429           *(p++) = '<';
430           *(p++) = '/';
431           pe = args->elt;
432           while (*pe)
433             *(p++) = *(pe++);
434           *(p++) = '>';
435           args++;
436         }
437       *(p++) = '<';
438       *(p++) = '/';
439       *(p++) = SERVICE_PREFIX2;
440       *(p++) = ':';
441       pe = action;
442
443       while (*pe)
444         *(p++) = *(pe++);
445
446       strncpy (p, "></" SOAP_PREFIX ":Body></" SOAP_PREFIX ":Envelope>\r\n",
447                soap_body + sizeof (soap_body) - p);
448     }
449
450   if (GNUNET_OK != parse_url (url, hostname, &port, &path))
451     {
452       GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP",
453                        "Invalid URL passed to UPNP_command(): %s\n", url);
454       caller_cb (buffer, 0, caller_cls);
455       return;
456     }
457
458
459   /* Test IPv4 address, else use IPv6 */
460   memset (&dest, 0, sizeof (dest));
461   memset (&dest6, 0, sizeof (dest6));
462
463   if (inet_pton (AF_INET, hostname, &dest.sin_addr) == 1)
464     {
465       dest.sin_family = AF_INET;
466       dest.sin_port = htons (port);
467 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
468       dest.sin_len = sizeof (dest);
469 #endif
470
471       s = GNUNET_CONNECTION_create_from_sockaddr (PF_INET,
472                                                   (struct sockaddr *) &dest,
473                                                   sizeof (dest));
474     }
475   else if (inet_pton (AF_INET6, hostname, &dest6.sin6_addr) == 1)
476     {
477       dest6.sin6_family = AF_INET6;
478       dest6.sin6_port = htons (port);
479 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
480       dest6.sin6_len = sizeof (dest6);
481 #endif
482
483       s = GNUNET_CONNECTION_create_from_sockaddr (PF_INET6,
484                                                   (struct sockaddr *) &dest6,
485                                                   sizeof (dest6));
486     }
487   else
488     {
489       GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, _("%s failed at %s:%d\n"),
490                        "UPnP", "inet_pton", __FILE__, __LINE__);
491
492       caller_cb (buffer, 0, caller_cls);
493       return;
494     }
495
496   body_size = (int) strlen (soap_body);
497   content_buf = GNUNET_malloc (512 + body_size);
498
499   /* We are not using keep-alive HTTP connections.
500    * HTTP/1.1 needs the header Connection: close to do that.
501    * This is the default with HTTP/1.0 */
502   /* Connection: Close is normally there only in HTTP/1.1 but who knows */
503   port_str[0] = '\0';
504
505   if (port != 80)
506     snprintf (port_str, sizeof (port_str), ":%hu", port);
507
508   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"   /* ??? */
509                            "Pragma: no-cache\r\n"
510                            "\r\n", path, hostname, port_str, body_size,
511                            soap_act);
512   memcpy (content_buf + headers_size, soap_body, body_size);
513
514 #ifdef DEBUG_UPNP
515   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
516                    "Sending command '%s' to '%s' (service '%s')\n",
517                    action, url, service);
518 #endif
519
520   cls = GNUNET_malloc (sizeof (struct UPNP_command_cls));
521   cls->s = s;
522   cls->content = content_buf;
523   cls->buffer = buffer;
524   cls->buf_size = buf_size;
525   cls->caller_cb = caller_cb;
526   cls->caller_cls = caller_cls;
527
528   cls->th =
529     GNUNET_CONNECTION_notify_transmit_ready (s, body_size + headers_size,
530                                              GNUNET_TIME_relative_multiply
531                                              (GNUNET_TIME_UNIT_SECONDS, 15),
532                                              &UPNP_command_transmit, cls);
533
534
535   if (cls->th == NULL)
536     {
537 #ifdef DEBUG_UPNP
538       GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP",
539                        "Error sending SOAP request at %s:%d\n", __FILE__,
540                        __LINE__);
541 #endif
542
543       caller_cb (buffer, 0, caller_cls);
544
545       GNUNET_free (content_buf);
546       GNUNET_free (cls);
547       GNUNET_CONNECTION_destroy (s, GNUNET_NO);
548       return;
549     }
550 }
551
552 struct get_external_ip_address_cls
553 {
554   UPNP_get_external_ip_address_cb_ caller_cb;
555   void *caller_cls;
556 };
557
558 static void
559 get_external_ip_address_receiver (char *response, size_t received, void *data)
560 {
561   struct get_external_ip_address_cls *cls = data;
562   struct UPNP_REPLY_NameValueList_ pdata;
563   char extIpAdd[128];
564   char *p;
565   int ret = UPNP_COMMAND_UNKNOWN_ERROR;
566
567   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Response: %s", response);
568
569   UPNP_REPLY_parse_ (response, received, &pdata);
570   p = UPNP_REPLY_get_value_ (&pdata, "NewExternalIPAddress");
571   if (p)
572     {
573       strncpy (extIpAdd, p, 128);
574       extIpAdd[127] = '\0';
575       ret = UPNP_COMMAND_SUCCESS;
576     }
577   else
578     extIpAdd[0] = '\0';
579
580   p = UPNP_REPLY_get_value_ (&pdata, "errorCode");
581   if (p)
582     {
583       ret = UPNP_COMMAND_UNKNOWN_ERROR;
584       sscanf (p, "%d", &ret);
585     }
586   cls->caller_cb (ret, extIpAdd, cls->caller_cls);
587
588   UPNP_REPLY_free_ (&pdata);
589   GNUNET_free (response);
590   GNUNET_free (cls);
591 }
592
593 /* UPNP_get_external_ip_address_() call the corresponding UPNP method.
594  * 
595  * Return values :
596  * 0 : SUCCESS
597  * NON ZERO : ERROR Either an UPnP error code or an unknown error.
598  *
599  * 402 Invalid Args - See UPnP Device Architecture section on Control.
600  * 501 Action Failed - See UPnP Device Architecture section on Control.
601  */
602 void
603 UPNP_get_external_ip_address_ (const char *control_url,
604                                const char *service_type,
605                                UPNP_get_external_ip_address_cb_ caller_cb,
606                                void *caller_cls)
607 {
608   struct get_external_ip_address_cls *cls;
609   char *buffer;
610
611   if (!control_url || !service_type)
612     caller_cb (UPNP_COMMAND_INVALID_ARGS, NULL, caller_cls);
613
614   cls = GNUNET_malloc (sizeof (struct get_external_ip_address_cls));
615   cls->caller_cb = caller_cb;
616   cls->caller_cls = caller_cls;
617
618   buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
619
620   UPNP_command_ (control_url, service_type, "GetExternalIPAddress",
621                  NULL, buffer, UPNP_COMMAND_BUFSIZE,
622                  (UPNP_command_cb_) get_external_ip_address_receiver, cls);
623 }
624
625 struct PortMapping_cls
626 {
627   const char *control_url;
628   const char *service_type;
629   const char *ext_port;
630   const char *in_port;
631   const char *proto;
632   const char *remoteHost;
633   UPNP_port_mapping_cb_ caller_cb;
634   void *caller_cls;
635 };
636
637 static void
638 add_delete_port_mapping_receiver (char *response, size_t received, void *data)
639 {
640   struct PortMapping_cls *cls = data;
641   struct UPNP_REPLY_NameValueList_ pdata;
642   const char *resVal;
643   int ret;
644
645   UPNP_REPLY_parse_ (response, received, &pdata);
646   resVal = UPNP_REPLY_get_value_ (&pdata, "errorCode");
647   if (resVal)
648     {
649       ret = UPNP_COMMAND_UNKNOWN_ERROR;
650       sscanf (resVal, "%d", &ret);
651     }
652   else
653     {
654       ret = UPNP_COMMAND_SUCCESS;
655     }
656
657   cls->caller_cb (ret, cls->control_url, cls->service_type,
658                   cls->ext_port, cls->in_port, cls->proto,
659                   cls->remoteHost, cls->caller_cls);
660
661   UPNP_REPLY_free_ (&pdata);
662   GNUNET_free (response);
663   GNUNET_free (cls);
664 }
665
666 void
667 UPNP_add_port_mapping_ (const char *control_url, const char *service_type,
668                         const char *ext_port,
669                         const char *in_port,
670                         const char *inClient,
671                         const char *desc,
672                         const char *proto, const char *remoteHost,
673                         UPNP_port_mapping_cb_ caller_cb, void *caller_cls)
674 {
675   struct UPNP_Arg_ args[9];
676   struct PortMapping_cls *cls;
677   char *buffer;
678
679   if (!in_port || !inClient || !proto || !ext_port)
680     {
681       caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
682                  ext_port, in_port, proto, remoteHost, caller_cls);
683       return;
684     }
685
686   args[0].elt = "NewRemoteHost";
687   args[0].val = remoteHost;
688   args[1].elt = "NewExternalPort";
689   args[1].val = ext_port;
690   args[2].elt = "NewProtocol";
691   args[2].val = proto;
692   args[3].elt = "NewInternalPort";
693   args[3].val = in_port;
694   args[4].elt = "NewInternalClient";
695   args[4].val = inClient;
696   args[5].elt = "NewEnabled";
697   args[5].val = "1";
698   args[6].elt = "NewPortMappingDescription";
699   args[6].val = desc ? desc : "GNUnet";
700   args[7].elt = "NewLeaseDuration";
701   args[7].val = "0";
702   args[8].elt = NULL;
703   args[8].val = NULL;
704
705   cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
706   cls->control_url = control_url;
707   cls->service_type = service_type;
708   cls->ext_port = ext_port;;
709   cls->in_port = in_port;
710   cls->proto = proto;
711   cls->remoteHost = remoteHost;
712   cls->caller_cb = caller_cb;
713   cls->caller_cls = caller_cls;
714
715   buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
716
717   UPNP_command_ (control_url, service_type, "AddPortMapping",
718                  args, buffer, UPNP_COMMAND_BUFSIZE,
719                  add_delete_port_mapping_receiver, cls);
720 }
721
722 void
723 UPNP_delete_port_mapping_ (const char *control_url, const char *service_type,
724                            const char *ext_port, const char *proto,
725                            const char *remoteHost,
726                            UPNP_port_mapping_cb_ caller_cb, void *caller_cls)
727 {
728   struct UPNP_Arg_ args[4];
729   struct PortMapping_cls *cls;
730   char *buffer;
731
732   if (!ext_port || !proto)
733     {
734       caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
735                  ext_port, NULL, proto, remoteHost, caller_cls);
736       return;
737     }
738
739   args[0].elt = "NewRemoteHost";
740   args[0].val = remoteHost;
741   args[1].elt = "NewExternalPort";
742   args[1].val = ext_port;
743   args[2].elt = "NewProtocol";
744   args[2].val = proto;
745   args[3].elt = NULL;
746   args[3].val = NULL;
747
748   cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
749   cls->control_url = control_url;
750   cls->service_type = service_type;
751   cls->ext_port = ext_port;
752   cls->in_port = "0";
753   cls->proto = proto;
754   cls->remoteHost = remoteHost;
755   cls->caller_cb = caller_cb;
756   cls->caller_cls = caller_cls;
757
758   buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
759
760   UPNP_command_ (control_url, service_type,
761                  "DeletePortMapping",
762                  args, buffer, UPNP_COMMAND_BUFSIZE,
763                  add_delete_port_mapping_receiver, cls);
764 }
765
766
767 struct get_specific_port_mapping_entry_cls
768 {
769   const char *control_url;
770   const char *service_type;
771   const char *ext_port;
772   const char *proto;
773   UPNP_port_mapping_cb_ caller_cb;
774   void *caller_cls;
775 };
776
777 static void
778 get_specific_port_mapping_entry_receiver (char *response, size_t received,
779                                           void *data)
780 {
781   struct PortMapping_cls *cls = data;
782   struct UPNP_REPLY_NameValueList_ pdata;
783   char *p;
784   char in_port[128];
785   char in_client[128];
786   int ret;
787
788   UPNP_REPLY_parse_ (response, received, &pdata);
789
790   p = UPNP_REPLY_get_value_ (&pdata, "NewInternalClient");
791   if (p)
792     {
793       strncpy (in_client, p, 128);
794       in_client[127] = '\0';
795     }
796   else
797     in_client[0] = '\0';
798
799   p = UPNP_REPLY_get_value_ (&pdata, "NewInternalPort");
800   if (p)
801     {
802       strncpy (in_port, p, 6);
803       in_port[5] = '\0';
804     }
805   else
806     in_port[0] = '\0';
807
808   p = UPNP_REPLY_get_value_ (&pdata, "errorCode");
809   if (p)
810     {
811       if (p)
812         {
813           ret = UPNP_COMMAND_UNKNOWN_ERROR;
814           sscanf (p, "%d", &ret);
815         }
816 #if DEBUG_UPNP
817       PRINT_UPNP_ERROR ("GetSpecificPortMappingEntry", p);
818 #endif
819     }
820
821   cls->caller_cb (ret, cls->control_url, cls->service_type,
822                   cls->ext_port, cls->proto, in_port, in_client,
823                   cls->caller_cls);
824
825   UPNP_REPLY_free_ (&pdata);
826   GNUNET_free (response);
827   GNUNET_free (cls);
828 }
829
830 /* UPNP_get_specific_port_mapping_entry _ retrieves an existing port mapping
831  * the result is returned in the in_client and in_port strings
832  * please provide 128 and 6 bytes of data */
833 void
834 UPNP_get_specific_port_mapping_entry_ (const char *control_url,
835                                        const char *service_type,
836                                        const char *ext_port,
837                                        const char *proto,
838                                        UPNP_get_specific_port_mapping_entry_cb_
839                                        caller_cb, void *caller_cls)
840 {
841   struct UPNP_Arg_ args[4];
842   struct get_specific_port_mapping_entry_cls *cls;
843   char *buffer;
844
845   if (!ext_port || !proto)
846     {
847       caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
848                  ext_port, proto, NULL, NULL, caller_cls);
849       return;
850     }
851
852   args[0].elt = "NewRemoteHost";
853   args[0].val = NULL;
854   args[1].elt = "NewExternalPort";
855   args[1].val = ext_port;
856   args[2].elt = "NewProtocol";
857   args[2].val = proto;
858   args[3].elt = NULL;
859   args[3].val = NULL;
860
861   cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
862   cls->control_url = control_url;
863   cls->service_type = service_type;
864   cls->ext_port = ext_port;
865   cls->proto = proto;
866   cls->caller_cb = caller_cb;
867   cls->caller_cls = caller_cls;
868
869   buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
870
871   UPNP_command_ (control_url, service_type,
872                  "GetSpecificPortMappingEntry",
873                  args, buffer, UPNP_COMMAND_BUFSIZE,
874                  get_specific_port_mapping_entry_receiver, cls);
875 }