From 922a0672749ba9d496d1dd8f6596bb4f8035e71d Mon Sep 17 00:00:00 2001 From: Moon Date: Sat, 9 Oct 2010 13:53:47 +0000 Subject: rework UPnP code to use GNUnet scheduler and network API disable NAT-PMP support for now --- src/nat/upnp-commands.c | 880 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 880 insertions(+) create mode 100644 src/nat/upnp-commands.c (limited to 'src/nat/upnp-commands.c') diff --git a/src/nat/upnp-commands.c b/src/nat/upnp-commands.c new file mode 100644 index 000000000..ddc5d9473 --- /dev/null +++ b/src/nat/upnp-commands.c @@ -0,0 +1,880 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Code in this file is originally based on the miniupnp library. + * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved. + * + * Original licence: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file nat/upnp-commands.c + * @brief Implementation of a basic set of UPnP commands + * + * @author Milan Bouchet-Valat + */ + +#include "platform.h" +#include "gnunet_util_lib.h" + +#include +#include +#include + +#include "upnp-reply-parse.h" +#include "upnp-igd-parse.h" +#include "upnp-discover.h" +#include "upnp-commands.h" + +#define SOAP_PREFIX "s" +#define SERVICE_PREFIX "u" +#define SERVICE_PREFIX2 'u' +#define MAX_HOSTNAME_LEN 64 + +#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); + + +/** + * Private closure used by UPNP_command() and its callbacks. + */ +struct UPNP_command_cls +{ + /** + * Connection handle used for sending and receiving. + */ + struct GNUNET_CONNECTION_Handle *s; + + /** + * Transmission handle used for sending command. + */ + struct GNUNET_CONNECTION_TransmitHandle *th; + + /** + * HTML content to send to run command. + */ + char *content; + + /** + * Buffer where to copy received data to pass to caller. + */ + char *buffer; + + /** + * Size of buffer. + */ + size_t buf_size; + + /** + * User callback to trigger when done. + */ + UPNP_command_cb_ caller_cb; + + /** + * User closure to pass to caller_cb. + */ + void *caller_cls; +}; + +/** + * Get the length of content included in an HTML line. + * + * @param p line to parse + * @param n size of p + * @return the length of the content + */ +static ssize_t +get_content_len_from_line (const char *p, int n) +{ + static const char cont_len_str[] = "content-length"; + const char *p2 = cont_len_str; + int a = 0; + + while (*p2) + { + if (n == 0) + return -1; + + if (*p2 != *p && *p2 != (*p + 32)) + return -1; + + p++; + p2++; + n--; + } + + if (n == 0) + return -1; + + if (*p != ':') + return -1; + + p++; + n--; + + while (*p == ' ') + { + if (n == 0) + return -1; + + p++; + n--; + } + + while (*p >= '0' && *p <= '9') + { + if (n == 0) + return -1; + + a = (a * 10) + (*p - '0'); + p++; + n--; + } + + return a; +} + +/** + * Get the respective lengths of content and header from an HTML reply. + * + * @param p HTML to parse + * @param n size of p + * @param content_len pointer to store content length to + * @param content_len pointer to store header length to + */ +static void +get_content_and_header_len (const char *p, int n, + int *content_len, int *header_len) +{ + const char *line; + int line_len; + int r; + + line = p; + + while (line < p + n) + { + line_len = 0; + + while (line[line_len] != '\r' && line[line_len] != '\r') + { + if (line + line_len >= p + n) + return; + + line_len++; + } + + r = get_content_len_from_line (line, line_len); + + if (r > 0) + *content_len = r; + + line = line + line_len + 2; + + if (line[0] == '\r' && line[1] == '\n') + { + *header_len = (line - p) + 2; + return; + } + } +} + +/** + * Receive reply of the device to our UPnP command. + * + * @param data closure from UPNP_command() + * @param buf struct UPNP_command_cls *cls + * @param available number of bytes in buf + * @param addr address of the sender + * @param addrlen size of addr + * @errCode value of errno + */ +static void +UPNP_command_receiver (void *data, + const void *buf, + size_t available, + const struct sockaddr *addr, + socklen_t addrlen, int errCode) +{ + struct UPNP_command_cls *cls = data; + int content_len; + int header_len; + + if (available > 0) + { + content_len = -1; + header_len = -1; + get_content_and_header_len (buf, available, &content_len, &header_len); + + strncpy (cls->buffer, (char *) buf, cls->buf_size - 2); + cls->buffer[cls->buf_size - 2] = '\0'; + } + else + { + cls->buffer[0] = '\0'; + } + + GNUNET_CONNECTION_destroy (cls->s, GNUNET_NO); + + (*cls->caller_cb) (cls->buffer, cls->buf_size, cls->caller_cls); + + GNUNET_free (cls->content); + GNUNET_free (cls); +} + +/** + * Send UPnP command to device. + */ +static size_t +UPNP_command_transmit (void *data, size_t size, void *buf) +{ + struct UPNP_command_cls *cls = data; + int n; + char *content = cls->content; + + n = strlen (content); + memcpy (buf, content, size); + + GNUNET_CONNECTION_receive (cls->s, cls->buf_size, GNUNET_TIME_UNIT_MINUTES, + UPNP_command_receiver, cls); + + return n; +} + +/** + * Parse a HTTP URL string to extract hostname, port and path it points to. + * + * @param url source string corresponding to URL + * @param hostname pointer where to store hostname (size of MAX_HOSTNAME_LEN+1) + * @param port pointer where to store port + * @param path pointer where to store path + * + * @return GNUNET_OK on success, GNUNET_SYSERR on failure + */ +int +parse_url (const char *url, char *hostname, unsigned short *port, char **path) +{ + char *p1, *p2, *p3; + + if (!url) + return GNUNET_SYSERR; + + p1 = strstr (url, "://"); + + if (!p1) + return GNUNET_SYSERR; + + p1 += 3; + + if ((url[0] != 'h') || (url[1] != 't') + || (url[2] != 't') || (url[3] != 'p')) + return GNUNET_SYSERR; + + p2 = strchr (p1, ':'); + p3 = strchr (p1, '/'); + + if (!p3) + return GNUNET_SYSERR; + + memset (hostname, 0, MAX_HOSTNAME_LEN + 1); + + if (!p2 || (p2 > p3)) + { + strncpy (hostname, p1, MIN (MAX_HOSTNAME_LEN, (int) (p3 - p1))); + *port = 80; + } + else + { + strncpy (hostname, p1, MIN (MAX_HOSTNAME_LEN, (int) (p2 - p1))); + *port = 0; + p2++; + + while ((*p2 >= '0') && (*p2 <= '9')) + { + *port *= 10; + *port += (unsigned short) (*p2 - '0'); + p2++; + } + } + + *path = p3; + return GNUNET_OK; +} + +/** + * Send UPnP command to the device identified by url and service. + * + * @param sched scheduler to use for network tasks + * @param url control URL of the device + * @param service type of the service corresponding to the command + * @param action action to send + * @param args arguments for action + * @param caller_cb user callback to trigger when done + * @param caller_cls closure to pass to caller_cb + */ +void +UPNP_command_ (struct GNUNET_SCHEDULER_Handle *sched, + const char *url, const char *service, + const char *action, struct UPNP_Arg_ *args, + char *buffer, size_t buf_size, + UPNP_command_cb_ caller_cb, void *caller_cls) +{ + struct GNUNET_CONNECTION_Handle *s; + struct UPNP_command_cls *cls; + struct sockaddr_in dest; + struct sockaddr_in6 dest6; + char hostname[MAX_HOSTNAME_LEN + 1]; + unsigned short port = 0; + char *path; + char soap_act[128]; + char soap_body[2048]; + int body_size; + char *content_buf; + int headers_size; + char port_str[8]; + + snprintf (soap_act, sizeof (soap_act), "%s#%s", service, action); + + if (args == NULL) + { + snprintf (soap_body, sizeof (soap_body), + "\r\n" + "<" SOAP_PREFIX ":Envelope " + "xmlns:" SOAP_PREFIX + "=\"http://schemas.xmlsoap.org/soap/envelope/\" " + SOAP_PREFIX + ":encodingStyle=\"http://schema GNUNET_free (content_buf);s.xmlsoap.org/soap/encoding/\">" + "<" SOAP_PREFIX ":Body>" "<" SERVICE_PREFIX + ":%s xmlns:" SERVICE_PREFIX "=\"%s\">" "" "" "\r\n", + action, service, action); + } + else + { + char *p; + const char *pe, *pv; + int soap_body_len; + + soap_body_len = snprintf (soap_body, sizeof (soap_body), + "\r\n" + "<" SOAP_PREFIX ":Envelope " + "xmlns:" SOAP_PREFIX + "=\"http://schemas.xmlsoap.org/soap/envelope/\" " + SOAP_PREFIX + ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "<" SOAP_PREFIX ":Body>" "<" SERVICE_PREFIX + ":%s xmlns:" SERVICE_PREFIX "=\"%s\">", + action, service); + + p = soap_body + soap_body_len; + + while (args->elt) + { + /* check that we are never overflowing the string... */ + if (soap_body + sizeof (soap_body) <= p + 100) + { + GNUNET_assert (GNUNET_NO); + (*caller_cb) (buffer, 0, caller_cls); + return; + } + *(p++) = '<'; + pe = args->elt; + while (*pe) + *(p++) = *(pe++); + *(p++) = '>'; + if ((pv = args->val)) + { + while (*pv) + *(p++) = *(pv++); + } + *(p++) = '<'; + *(p++) = '/'; + pe = args->elt; + while (*pe) + *(p++) = *(pe++); + *(p++) = '>'; + args++; + } + *(p++) = '<'; + *(p++) = '/'; + *(p++) = SERVICE_PREFIX2; + *(p++) = ':'; + pe = action; + + while (*pe) + *(p++) = *(pe++); + + strncpy (p, ">\r\n", + soap_body + sizeof (soap_body) - p); + } + + if (GNUNET_OK != parse_url (url, hostname, &port, &path)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP", + "Invalid URL passed to UPNP_command(): %s\n", url); + return; + } + + + /* Test IPv4 address, else use IPv6 */ + memset (&dest, 0, sizeof (dest)); + memset (&dest6, 0, sizeof (dest6)); + + if (inet_pton (AF_INET, hostname, &dest.sin_addr) == 1) + { + dest.sin_family = AF_INET; + dest.sin_port = htons (port); +#ifdef HAVE_SOCKADDR_IN_SIN_LEN + dest.sin_len = sizeof (dest); +#endif + + s = GNUNET_CONNECTION_create_from_sockaddr (sched, PF_INET, + (struct sockaddr *) &dest, + sizeof (dest)); + } + else if (inet_pton (AF_INET6, hostname, &dest6.sin6_addr) == 1) + { + dest6.sin6_family = AF_INET6; + dest6.sin6_port = htons (port); +#ifdef HAVE_SOCKADDR_IN_SIN_LEN + dest6.sin6_len = sizeof (dest6); +#endif + + s = GNUNET_CONNECTION_create_from_sockaddr (sched, PF_INET6, + (struct sockaddr *) &dest6, + sizeof (dest6)); + } + else + { + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, _("%s failed at %s:%d\n"), + "UPnP", "inet_pton", __FILE__, __LINE__); + + (*caller_cb) (buffer, 0, caller_cls); + return; + } + + body_size = (int) strlen (soap_body); + content_buf = GNUNET_malloc (512 + body_size); + + /* We are not using keep-alive HTTP connections. + * HTTP/1.1 needs the header Connection: close to do that. + * This is the default with HTTP/1.0 */ + /* Connection: Close is normally there only in HTTP/1.1 but who knows */ + port_str[0] = '\0'; + + if (port != 80) + snprintf (port_str, sizeof (port_str), ":%hu", port); + + 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" /* ??? */ + "Pragma: no-cache\r\n" + "\r\n", path, hostname, port_str, body_size, + soap_act); + memcpy (content_buf + headers_size, soap_body, body_size); + +#ifdef DEBUG_UPNP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP", + "Sending command '%s' to '%s' (service '%s')\n", + action, url, service); +#endif + + cls = GNUNET_malloc (sizeof (struct UPNP_command_cls)); + cls->s = s; + cls->content = content_buf; + cls->buffer = buffer; + cls->buf_size = buf_size; + cls->caller_cb = caller_cb; + cls->caller_cls = caller_cls; + + cls->th = + GNUNET_CONNECTION_notify_transmit_ready (s, body_size + headers_size, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 15), + &UPNP_command_transmit, cls); + + + if (cls->th == NULL) + { +#ifdef DEBUG_UPNP + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP", + "Error sending SOAP request at %s:%d\n", __FILE__, + __LINE__); +#endif + + (*caller_cb) (buffer, 0, caller_cls); + + GNUNET_free (content_buf); + GNUNET_free (cls); + GNUNET_CONNECTION_destroy (s, GNUNET_NO); + return; + } +} + +struct get_external_ip_address_cls +{ + UPNP_get_external_ip_address_cb_ caller_cb; + void *caller_cls; +}; + +static void +get_external_ip_address_receiver (char *response, size_t received, void *data) +{ + struct get_external_ip_address_cls *cls = data; + struct UPNP_REPLY_NameValueList_ pdata; + char extIpAdd[128]; + char *p; + int ret = UPNP_COMMAND_UNKNOWN_ERROR; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Response: %s", response); + + UPNP_REPLY_parse_ (response, received, &pdata); + p = UPNP_REPLY_get_value_ (&pdata, "NewExternalIPAddress"); + if (p) + { + strncpy (extIpAdd, p, 128); + extIpAdd[127] = '\0'; + ret = UPNP_COMMAND_SUCCESS; + } + else + extIpAdd[0] = '\0'; + + p = UPNP_REPLY_get_value_ (&pdata, "errorCode"); + if (p) + { + ret = UPNP_COMMAND_UNKNOWN_ERROR; + sscanf (p, "%d", &ret); + } + cls->caller_cb (ret, extIpAdd, cls->caller_cls); + + UPNP_REPLY_free_ (&pdata); + GNUNET_free (response); + GNUNET_free (cls); +} + +/* UPNP_get_external_ip_address_() call the corresponding UPNP method. + * + * Return values : + * 0 : SUCCESS + * NON ZERO : ERROR Either an UPnP error code or an unknown error. + * + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 501 Action Failed - See UPnP Device Architecture section on Control. + */ +void +UPNP_get_external_ip_address_ (struct GNUNET_SCHEDULER_Handle *sched, + const char *control_url, + const char *service_type, + UPNP_get_external_ip_address_cb_ caller_cb, + void *caller_cls) +{ + struct get_external_ip_address_cls *cls; + char *buffer; + + if (!control_url || !service_type) + caller_cb (UPNP_COMMAND_INVALID_ARGS, NULL, caller_cls); + + cls = GNUNET_malloc (sizeof (struct get_external_ip_address_cls)); + cls->caller_cb = caller_cb; + cls->caller_cls = caller_cls; + + buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE); + + UPNP_command_ (sched, control_url, service_type, "GetExternalIPAddress", + NULL, buffer, UPNP_COMMAND_BUFSIZE, + (UPNP_command_cb_) get_external_ip_address_receiver, cls); +} + +struct PortMapping_cls +{ + const char *control_url; + const char *service_type; + const char *ext_port; + const char *in_port; + const char *proto; + const char *remoteHost; + UPNP_port_mapping_cb_ caller_cb; + void *caller_cls; +}; + +static void +add_delete_port_mapping_receiver (char *response, size_t received, void *data) +{ + struct PortMapping_cls *cls = data; + struct UPNP_REPLY_NameValueList_ pdata; + const char *resVal; + int ret; + + UPNP_REPLY_parse_ (response, received, &pdata); + resVal = UPNP_REPLY_get_value_ (&pdata, "errorCode"); + if (resVal) + { + ret = UPNP_COMMAND_UNKNOWN_ERROR; + sscanf (resVal, "%d", &ret); + } + else + { + ret = UPNP_COMMAND_SUCCESS; + } + + cls->caller_cb (ret, cls->control_url, cls->service_type, + cls->ext_port, cls->in_port, cls->proto, + cls->remoteHost, cls->caller_cls); + + UPNP_REPLY_free_ (&pdata); + GNUNET_free (response); + GNUNET_free (cls); +} + +void +UPNP_add_port_mapping_ (struct GNUNET_SCHEDULER_Handle *sched, + const char *control_url, const char *service_type, + const char *ext_port, + const char *in_port, + const char *inClient, + const char *desc, + const char *proto, const char *remoteHost, + UPNP_port_mapping_cb_ caller_cb, void *caller_cls) +{ + struct UPNP_Arg_ args[9]; + struct PortMapping_cls *cls; + char *buffer; + + if (!in_port || !inClient || !proto || !ext_port) + { + caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type, + ext_port, in_port, proto, remoteHost, caller_cls); + return; + } + + args[0].elt = "NewRemoteHost"; + args[0].val = remoteHost; + args[1].elt = "NewExternalPort"; + args[1].val = ext_port; + args[2].elt = "NewProtocol"; + args[2].val = proto; + args[3].elt = "NewInternalPort"; + args[3].val = in_port; + args[4].elt = "NewInternalClient"; + args[4].val = inClient; + args[5].elt = "NewEnabled"; + args[5].val = "1"; + args[6].elt = "NewPortMappingDescription"; + args[6].val = desc ? desc : "GNUnet"; + args[7].elt = "NewLeaseDuration"; + args[7].val = "0"; + args[8].elt = NULL; + args[8].val = NULL; + + cls = GNUNET_malloc (sizeof (struct PortMapping_cls)); + cls->control_url = control_url; + cls->service_type = service_type; + cls->ext_port = ext_port;; + cls->in_port = in_port; + cls->proto = proto; + cls->remoteHost = remoteHost; + cls->caller_cb = caller_cb; + cls->caller_cls = caller_cls; + + buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE); + + UPNP_command_ (sched, control_url, service_type, "AddPortMapping", + args, buffer, UPNP_COMMAND_BUFSIZE, + add_delete_port_mapping_receiver, cls); +} + +void +UPNP_delete_port_mapping_ (struct GNUNET_SCHEDULER_Handle *sched, + const char *control_url, const char *service_type, + const char *ext_port, const char *proto, + const char *remoteHost, + UPNP_port_mapping_cb_ caller_cb, void *caller_cls) +{ + struct UPNP_Arg_ args[4]; + struct PortMapping_cls *cls; + char *buffer; + + if (!ext_port || !proto) + { + caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type, + ext_port, NULL, proto, remoteHost, caller_cls); + return; + } + + args[0].elt = "NewRemoteHost"; + args[0].val = remoteHost; + args[1].elt = "NewExternalPort"; + args[1].val = ext_port; + args[2].elt = "NewProtocol"; + args[2].val = proto; + args[3].elt = NULL; + args[3].val = NULL; + + cls = GNUNET_malloc (sizeof (struct PortMapping_cls)); + cls->control_url = control_url; + cls->service_type = service_type; + cls->ext_port = ext_port; + cls->in_port = "0"; + cls->proto = proto; + cls->remoteHost = remoteHost; + cls->caller_cb = caller_cb; + cls->caller_cls = caller_cls; + + buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE); + + UPNP_command_ (sched, control_url, service_type, + "DeletePortMapping", + args, buffer, UPNP_COMMAND_BUFSIZE, + add_delete_port_mapping_receiver, cls); +} + + +struct get_specific_port_mapping_entry_cls +{ + const char *control_url; + const char *service_type; + const char *ext_port; + const char *proto; + UPNP_port_mapping_cb_ caller_cb; + void *caller_cls; +}; + +static void +get_specific_port_mapping_entry_receiver (char *response, size_t received, + void *data) +{ + struct PortMapping_cls *cls = data; + struct UPNP_REPLY_NameValueList_ pdata; + char *p; + char in_port[128]; + char in_client[128]; + int ret; + + UPNP_REPLY_parse_ (response, received, &pdata); + + p = UPNP_REPLY_get_value_ (&pdata, "NewInternalClient"); + if (p) + { + strncpy (in_client, p, 128); + in_client[127] = '\0'; + } + else + in_client[0] = '\0'; + + p = UPNP_REPLY_get_value_ (&pdata, "NewInternalPort"); + if (p) + { + strncpy (in_port, p, 6); + in_port[5] = '\0'; + } + else + in_port[0] = '\0'; + + p = UPNP_REPLY_get_value_ (&pdata, "errorCode"); + if (p) + { + if (p) + { + ret = UPNP_COMMAND_UNKNOWN_ERROR; + sscanf (p, "%d", &ret); + } +#if DEBUG_UPNP + PRINT_UPNP_ERROR ("GetSpecificPortMappingEntry", p); +#endif + } + + cls->caller_cb (ret, cls->control_url, cls->service_type, + cls->ext_port, cls->proto, in_port, in_client, + cls->caller_cls); + + UPNP_REPLY_free_ (&pdata); + GNUNET_free (response); + GNUNET_free (cls); +} + +/* UPNP_get_specific_port_mapping_entry _ retrieves an existing port mapping + * the result is returned in the in_client and in_port strings + * please provide 128 and 6 bytes of data */ +void +UPNP_get_specific_port_mapping_entry_ (struct GNUNET_SCHEDULER_Handle *sched, + const char *control_url, + const char *service_type, + const char *ext_port, + const char *proto, + UPNP_get_specific_port_mapping_entry_cb_ + caller_cb, void *caller_cls) +{ + struct UPNP_Arg_ args[4]; + struct get_specific_port_mapping_entry_cls *cls; + char *buffer; + + if (!ext_port || !proto) + { + caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type, + ext_port, proto, NULL, NULL, caller_cls); + return; + } + + args[0].elt = "NewRemoteHost"; + args[0].val = NULL; + args[1].elt = "NewExternalPort"; + args[1].val = ext_port; + args[2].elt = "NewProtocol"; + args[2].val = proto; + args[3].elt = NULL; + args[3].val = NULL; + + cls = GNUNET_malloc (sizeof (struct PortMapping_cls)); + cls->control_url = control_url; + cls->service_type = service_type; + cls->ext_port = ext_port; + cls->proto = proto; + cls->caller_cb = caller_cb; + cls->caller_cls = caller_cls; + + buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE); + + UPNP_command_ (sched, control_url, service_type, + "GetSpecificPortMappingEntry", + args, buffer, UPNP_COMMAND_BUFSIZE, + get_specific_port_mapping_entry_receiver, cls); +} -- cgit v1.2.3