check
[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 header_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  * @param 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 buffer buffer
352  * @param buf_size buffer size
353  * @param caller_cb user callback to trigger when done
354  * @param caller_cls closure to pass to caller_cb
355  */
356 void
357 UPNP_command_ (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 (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 (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_ (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_ (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_ (const char *control_url, const char *service_type,
670                         const char *ext_port,
671                         const char *in_port,
672                         const char *inClient,
673                         const char *desc,
674                         const char *proto, const char *remoteHost,
675                         UPNP_port_mapping_cb_ caller_cb, void *caller_cls)
676 {
677   struct UPNP_Arg_ args[9];
678   struct PortMapping_cls *cls;
679   char *buffer;
680
681   if (!in_port || !inClient || !proto || !ext_port)
682     {
683       caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
684                  ext_port, in_port, proto, remoteHost, caller_cls);
685       return;
686     }
687
688   args[0].elt = "NewRemoteHost";
689   args[0].val = remoteHost;
690   args[1].elt = "NewExternalPort";
691   args[1].val = ext_port;
692   args[2].elt = "NewProtocol";
693   args[2].val = proto;
694   args[3].elt = "NewInternalPort";
695   args[3].val = in_port;
696   args[4].elt = "NewInternalClient";
697   args[4].val = inClient;
698   args[5].elt = "NewEnabled";
699   args[5].val = "1";
700   args[6].elt = "NewPortMappingDescription";
701   args[6].val = desc ? desc : "GNUnet";
702   args[7].elt = "NewLeaseDuration";
703   args[7].val = "0";
704   args[8].elt = NULL;
705   args[8].val = NULL;
706
707   cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
708   cls->control_url = control_url;
709   cls->service_type = service_type;
710   cls->ext_port = ext_port;;
711   cls->in_port = in_port;
712   cls->proto = proto;
713   cls->remoteHost = remoteHost;
714   cls->caller_cb = caller_cb;
715   cls->caller_cls = caller_cls;
716
717   buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
718
719   UPNP_command_ (control_url, service_type, "AddPortMapping",
720                  args, buffer, UPNP_COMMAND_BUFSIZE,
721                  add_delete_port_mapping_receiver, cls);
722 }
723
724 void
725 UPNP_delete_port_mapping_ (const char *control_url, const char *service_type,
726                            const char *ext_port, const char *proto,
727                            const char *remoteHost,
728                            UPNP_port_mapping_cb_ caller_cb, void *caller_cls)
729 {
730   struct UPNP_Arg_ args[4];
731   struct PortMapping_cls *cls;
732   char *buffer;
733
734   if (!ext_port || !proto)
735     {
736       caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
737                  ext_port, NULL, proto, remoteHost, caller_cls);
738       return;
739     }
740
741   args[0].elt = "NewRemoteHost";
742   args[0].val = remoteHost;
743   args[1].elt = "NewExternalPort";
744   args[1].val = ext_port;
745   args[2].elt = "NewProtocol";
746   args[2].val = proto;
747   args[3].elt = NULL;
748   args[3].val = NULL;
749
750   cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
751   cls->control_url = control_url;
752   cls->service_type = service_type;
753   cls->ext_port = ext_port;
754   cls->in_port = "0";
755   cls->proto = proto;
756   cls->remoteHost = remoteHost;
757   cls->caller_cb = caller_cb;
758   cls->caller_cls = caller_cls;
759
760   buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
761
762   UPNP_command_ (control_url, service_type,
763                  "DeletePortMapping",
764                  args, buffer, UPNP_COMMAND_BUFSIZE,
765                  add_delete_port_mapping_receiver, cls);
766 }
767
768
769 struct get_specific_port_mapping_entry_cls
770 {
771   const char *control_url;
772   const char *service_type;
773   const char *ext_port;
774   const char *proto;
775   UPNP_port_mapping_cb_ caller_cb;
776   void *caller_cls;
777 };
778
779 static void
780 get_specific_port_mapping_entry_receiver (char *response, size_t received,
781                                           void *data)
782 {
783   struct PortMapping_cls *cls = data;
784   struct UPNP_REPLY_NameValueList_ pdata;
785   char *p;
786   char in_port[128];
787   char in_client[128];
788   int ret;
789
790   UPNP_REPLY_parse_ (response, received, &pdata);
791
792   p = UPNP_REPLY_get_value_ (&pdata, "NewInternalClient");
793   if (p)
794     {
795       strncpy (in_client, p, 128);
796       in_client[127] = '\0';
797     }
798   else
799     in_client[0] = '\0';
800
801   p = UPNP_REPLY_get_value_ (&pdata, "NewInternalPort");
802   if (p)
803     {
804       strncpy (in_port, p, 6);
805       in_port[5] = '\0';
806     }
807   else
808     in_port[0] = '\0';
809
810   p = UPNP_REPLY_get_value_ (&pdata, "errorCode");
811   if (p)
812     {
813       if (p)
814         {
815           ret = UPNP_COMMAND_UNKNOWN_ERROR;
816           sscanf (p, "%d", &ret);
817         }
818 #if DEBUG_UPNP
819       PRINT_UPNP_ERROR ("GetSpecificPortMappingEntry", p);
820 #endif
821     }
822
823   cls->caller_cb (ret, cls->control_url, cls->service_type,
824                   cls->ext_port, cls->proto, in_port, in_client,
825                   cls->caller_cls);
826
827   UPNP_REPLY_free_ (&pdata);
828   GNUNET_free (response);
829   GNUNET_free (cls);
830 }
831
832 /* UPNP_get_specific_port_mapping_entry _ retrieves an existing port mapping
833  * the result is returned in the in_client and in_port strings
834  * please provide 128 and 6 bytes of data */
835 void
836 UPNP_get_specific_port_mapping_entry_ (const char *control_url,
837                                        const char *service_type,
838                                        const char *ext_port,
839                                        const char *proto,
840                                        UPNP_get_specific_port_mapping_entry_cb_
841                                        caller_cb, void *caller_cls)
842 {
843   struct UPNP_Arg_ args[4];
844   struct get_specific_port_mapping_entry_cls *cls;
845   char *buffer;
846
847   if (!ext_port || !proto)
848     {
849       caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
850                  ext_port, proto, NULL, NULL, caller_cls);
851       return;
852     }
853
854   args[0].elt = "NewRemoteHost";
855   args[0].val = NULL;
856   args[1].elt = "NewExternalPort";
857   args[1].val = ext_port;
858   args[2].elt = "NewProtocol";
859   args[2].val = proto;
860   args[3].elt = NULL;
861   args[3].val = NULL;
862
863   cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
864   cls->control_url = control_url;
865   cls->service_type = service_type;
866   cls->ext_port = ext_port;
867   cls->proto = proto;
868   cls->caller_cb = caller_cb;
869   cls->caller_cls = caller_cls;
870
871   buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
872
873   UPNP_command_ (control_url, service_type,
874                  "GetSpecificPortMappingEntry",
875                  args, buffer, UPNP_COMMAND_BUFSIZE,
876                  get_specific_port_mapping_entry_receiver, cls);
877 }