--- embedaddon/miniupnpd/upnpsoap.c 2012/02/21 23:16:02 1.1.1.1
+++ embedaddon/miniupnpd/upnpsoap.c 2012/05/29 12:55:57 1.1.1.2
@@ -1,4 +1,4 @@
-/* $Id: upnpsoap.c,v 1.1.1.1 2012/02/21 23:16:02 misho Exp $ */
+/* $Id: upnpsoap.c,v 1.1.1.2 2012/05/29 12:55:57 misho Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
@@ -24,6 +24,8 @@
#include "upnpredirect.h"
#include "getifaddr.h"
#include "getifstats.h"
+#include "getconnstatus.h"
+#include "upnpurns.h"
static void
BuildSendAndCloseSoapResp(struct upnphttp * h,
@@ -60,7 +62,7 @@ GetConnectionTypeInfo(struct upnphttp * h, const char
{
static const char resp[] =
""
+ "xmlns:u=\"" SERVICE_TYPE_WANIPC "\">"
"IP_Routed"
"IP_Routed"
"";
@@ -209,18 +211,15 @@ GetStatusInfo(struct upnphttp * h, const char * action
char body[512];
int bodylen;
time_t uptime;
- const char * status = "Connected";
+ const char * status;
/* ConnectionStatus possible values :
* Unconfigured, Connecting, Connected, PendingDisconnect,
* Disconnecting, Disconnected */
- char ext_ip_addr[INET_ADDRSTRLEN];
- if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0) {
- status = "Disconnected";
- }
+ status = get_wan_connection_status_str(ext_if_name);
uptime = (time(NULL) - startup_time);
bodylen = snprintf(body, sizeof(body), resp,
- action, "urn:schemas-upnp-org:service:WANIPConnection:1",
+ action, SERVICE_TYPE_WANIPC,
status, (long)uptime, action);
BuildSendAndCloseSoapResp(h, body, bodylen);
}
@@ -230,7 +229,7 @@ GetNATRSIPStatus(struct upnphttp * h, const char * act
{
static const char resp[] =
""
+ "xmlns:u=\"" SERVICE_TYPE_WANIPC "\">"
"0"
"1"
"";
@@ -257,6 +256,8 @@ GetExternalIPAddress(struct upnphttp * h, const char *
char body[512];
int bodylen;
char ext_ip_addr[INET_ADDRSTRLEN];
+ /* Does that method need to work with IPv6 ?
+ * There is usually no NAT with IPv6 */
#ifndef MULTIPLE_EXTERNAL_IP
if(use_ext_ip_addr)
@@ -270,24 +271,26 @@ GetExternalIPAddress(struct upnphttp * h, const char *
strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
}
#else
- int i;
+ struct lan_addr_s * lan_addr;
strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
- for(i = 0; ilist.le_next)
{
- if( (h->clientaddr.s_addr & lan_addr[i].mask.s_addr)
- == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
+ if( (h->clientaddr.s_addr & lan_addr->mask.s_addr)
+ == (lan_addr->addr.s_addr & lan_addr->mask.s_addr))
{
- strncpy(ext_ip_addr, lan_addr[i].ext_ip_str, INET_ADDRSTRLEN);
+ strncpy(ext_ip_addr, lan_addr->ext_ip_str, INET_ADDRSTRLEN);
break;
}
}
#endif
bodylen = snprintf(body, sizeof(body), resp,
- action, "urn:schemas-upnp-org:service:WANIPConnection:1",
+ action, SERVICE_TYPE_WANIPC,
ext_ip_addr, action);
BuildSendAndCloseSoapResp(h, body, bodylen);
}
+/* AddPortMapping method of WANIPConnection Service
+ * Ignored argument : NewEnabled */
static void
AddPortMapping(struct upnphttp * h, const char * action)
{
@@ -295,11 +298,13 @@ AddPortMapping(struct upnphttp * h, const char * actio
static const char resp[] =
"";
+ "xmlns:u=\"" SERVICE_TYPE_WANIPC "\"/>";
struct NameValueParserData data;
char * int_ip, * int_port, * ext_port, * protocol, * desc;
- char * leaseduration;
+ char * leaseduration_str;
+ unsigned int leaseduration;
+ char * r_host;
unsigned short iport, eport;
struct hostent *hp; /* getbyhostname() */
@@ -308,7 +313,6 @@ AddPortMapping(struct upnphttp * h, const char * actio
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
int_ip = GetValueFromNameValueList(&data, "NewInternalClient");
-
if (!int_ip)
{
ClearNameValueList(&data);
@@ -316,6 +320,20 @@ AddPortMapping(struct upnphttp * h, const char * actio
return;
}
+ /* IGD 2 MUST support both wildcard and specific IP address values
+ * for RemoteHost (only the wildcard value was REQUIRED in release 1.0) */
+ r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
+#ifndef SUPPORT_REMOTEHOST
+#ifdef UPNP_STRICT
+ if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*")))
+ {
+ ClearNameValueList(&data);
+ SoapError(h, 726, "RemoteHostOnlySupportsWildcard");
+ return;
+ }
+#endif
+#endif
+
/* if ip not valid assume hostname and convert */
if (inet_pton(AF_INET, int_ip, &result_ip) <= 0)
{
@@ -356,7 +374,7 @@ AddPortMapping(struct upnphttp * h, const char * actio
ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
protocol = GetValueFromNameValueList(&data, "NewProtocol");
desc = GetValueFromNameValueList(&data, "NewPortMappingDescription");
- leaseduration = GetValueFromNameValueList(&data, "NewLeaseDuration");
+ leaseduration_str = GetValueFromNameValueList(&data, "NewLeaseDuration");
if (!int_port || !ext_port || !protocol)
{
@@ -368,15 +386,24 @@ AddPortMapping(struct upnphttp * h, const char * actio
eport = (unsigned short)atoi(ext_port);
iport = (unsigned short)atoi(int_port);
- if(leaseduration && atoi(leaseduration)) {
- /* at the moment, lease duration is always infinite */
- syslog(LOG_WARNING, "NewLeaseDuration=%s not supported, ignored. (ip=%s, desc='%s')", leaseduration, int_ip, desc);
- }
+ leaseduration = leaseduration_str ? atoi(leaseduration_str) : 0;
+#ifdef IGD_V2
+ /* PortMappingLeaseDuration can be either a value between 1 and
+ * 604800 seconds or the zero value (for infinite lease time).
+ * Note that an infinite lease time can be only set by out-of-band
+ * mechanisms like WWW-administration, remote management or local
+ * management.
+ * If a control point uses the value 0 to indicate an infinite lease
+ * time mapping, it is REQUIRED that gateway uses the maximum value
+ * instead (e.g. 604800 seconds) */
+ if(leaseduration == 0 || leaseduration > 604800)
+ leaseduration = 604800;
+#endif
- syslog(LOG_INFO, "%s: ext port %hu to %s:%hu protocol %s for: %s",
- action, eport, int_ip, iport, protocol, desc);
+ syslog(LOG_INFO, "%s: ext port %hu to %s:%hu protocol %s for: %s leaseduration=%u rhost=%s",
+ action, eport, int_ip, iport, protocol, desc, leaseduration, r_host);
- r = upnp_redirect(eport, int_ip, iport, protocol, desc);
+ r = upnp_redirect(r_host, eport, int_ip, iport, protocol, desc, leaseduration);
ClearNameValueList(&data);
@@ -386,19 +413,20 @@ AddPortMapping(struct upnphttp * h, const char * actio
* 715 - Wildcard not permited in SrcAddr
* 716 - Wildcard not permited in ExtPort
* 718 - ConflictInMappingEntry
- * 724 - SamePortValuesRequired
+ * 724 - SamePortValuesRequired (deprecated in IGD v2)
* 725 - OnlyPermanentLeasesSupported
The NAT implementation only supports permanent lease times on
- port mappings
+ port mappings (deprecated in IGD v2)
* 726 - RemoteHostOnlySupportsWildcard
RemoteHost must be a wildcard and cannot be a specific IP
- address or DNS name
+ address or DNS name (deprecated in IGD v2)
* 727 - ExternalPortOnlySupportsWildcard
ExternalPort must be a wildcard and cannot be a specific port
- value
+ value (deprecated in IGD v2)
* 728 - NoPortMapsAvailable
There are not enough free prots available to complete the mapping
- (added in IGD v2) */
+ (added in IGD v2)
+ * 729 - ConflictWithOtherMechanisms (added in IGD v2) */
switch(r)
{
case 0: /* success */
@@ -431,6 +459,7 @@ AddAnyPortMapping(struct upnphttp * h, const char * ac
const char * int_ip, * int_port, * ext_port, * protocol, * desc;
const char * r_host;
unsigned short iport, eport;
+ const char * leaseduration_str;
unsigned int leaseduration;
struct hostent *hp; /* getbyhostname() */
@@ -445,7 +474,9 @@ AddAnyPortMapping(struct upnphttp * h, const char * ac
int_ip = GetValueFromNameValueList(&data, "NewInternalClient");
/* NewEnabled */
desc = GetValueFromNameValueList(&data, "NewPortMappingDescription");
- leaseduration = atoi(GetValueFromNameValueList(&data, "NewLeaseDuration"));
+ leaseduration_str = GetValueFromNameValueList(&data, "NewLeaseDuration");
+
+ leaseduration = leaseduration_str ? atoi(leaseduration_str) : 0;
if(leaseduration == 0)
leaseduration = 604800;
@@ -458,6 +489,16 @@ AddAnyPortMapping(struct upnphttp * h, const char * ac
SoapError(h, 402, "Invalid Args");
return;
}
+#ifndef SUPPORT_REMOTEHOST
+#ifdef UPNP_STRICT
+ if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*")))
+ {
+ ClearNameValueList(&data);
+ SoapError(h, 726, "RemoteHostOnlySupportsWildcard");
+ return;
+ }
+#endif
+#endif
/* if ip not valid assume hostname and convert */
if (inet_pton(AF_INET, int_ip, &result_ip) <= 0)
@@ -495,8 +536,16 @@ AddAnyPortMapping(struct upnphttp * h, const char * ac
}
}
- /* TODO : accept a different external port */
- r = upnp_redirect(eport, int_ip, iport, protocol, desc);
+ /* TODO : accept a different external port
+ * have some smart strategy to choose the port */
+ for(;;) {
+ r = upnp_redirect(r_host, eport, int_ip, iport, protocol, desc, leaseduration);
+ if(r==-2 && eport < 65535) {
+ eport++;
+ } else {
+ break;
+ }
+ }
ClearNameValueList(&data);
@@ -504,7 +553,7 @@ AddAnyPortMapping(struct upnphttp * h, const char * ac
{
case 0: /* success */
bodylen = snprintf(body, sizeof(body), resp,
- action, "urn:schemas-upnp-org:service:WANIPConnection:2",
+ action, SERVICE_TYPE_WANIPC,
eport, action);
BuildSendAndCloseSoapResp(h, body, bodylen);
break;
@@ -531,7 +580,7 @@ GetSpecificPortMappingEntry(struct upnphttp * h, const
"%s"
"1"
"%s"
- "0"
+ "%u"
"";
char body[1024];
@@ -541,6 +590,7 @@ GetSpecificPortMappingEntry(struct upnphttp * h, const
unsigned short eport, iport;
char int_ip[32];
char desc[64];
+ unsigned int leaseduration = 0;
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
@@ -553,12 +603,28 @@ GetSpecificPortMappingEntry(struct upnphttp * h, const
SoapError(h, 402, "Invalid Args");
return;
}
+#ifndef SUPPORT_REMOTEHOST
+#ifdef UPNP_STRICT
+ if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*")))
+ {
+ ClearNameValueList(&data);
+ SoapError(h, 726, "RemoteHostOnlySupportsWildcard");
+ return;
+ }
+#endif
+#endif
eport = (unsigned short)atoi(ext_port);
+ /* TODO : add r_host as an input parameter ...
+ * We prevent several Port Mapping with same external port
+ * but different remoteHost to be set up, so that is not
+ * a priority. */
r = upnp_get_redirection_infos(eport, protocol, &iport,
int_ip, sizeof(int_ip),
- desc, sizeof(desc));
+ desc, sizeof(desc),
+ NULL, 0,
+ &leaseduration);
if(r < 0)
{
@@ -570,8 +636,8 @@ GetSpecificPortMappingEntry(struct upnphttp * h, const
action,
r_host, ext_port, protocol, int_ip, (unsigned int)iport, desc);
bodylen = snprintf(body, sizeof(body), resp,
- action, "urn:schemas-upnp-org:service:WANIPConnection:1",
- (unsigned int)iport, int_ip, desc,
+ action, SERVICE_TYPE_WANIPC,
+ (unsigned int)iport, int_ip, desc, leaseduration,
action);
BuildSendAndCloseSoapResp(h, body, bodylen);
}
@@ -586,7 +652,7 @@ DeletePortMapping(struct upnphttp * h, const char * ac
static const char resp[] =
""
+ "xmlns:u=\"" SERVICE_TYPE_WANIPC "\">"
"";
struct NameValueParserData data;
@@ -604,10 +670,23 @@ DeletePortMapping(struct upnphttp * h, const char * ac
SoapError(h, 402, "Invalid Args");
return;
}
+#ifndef SUPPORT_REMOTEHOST
+#ifdef UPNP_STRICT
+ if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*")))
+ {
+ ClearNameValueList(&data);
+ SoapError(h, 726, "RemoteHostOnlySupportsWildcard");
+ return;
+ }
+#endif
+#endif
eport = (unsigned short)atoi(ext_port);
- /* TODO : if in secure mode, check the IP */
+ /* TODO : if in secure mode, check the IP
+ * Removing a redirection is not a security threat,
+ * just an annoyance for the user using it. So this is not
+ * a priority. */
syslog(LOG_INFO, "%s: external port: %hu, protocol: %s",
action, eport, protocol);
@@ -630,14 +709,17 @@ DeletePortMapping(struct upnphttp * h, const char * ac
static void
DeletePortMappingRange(struct upnphttp * h, const char * action)
{
+ int r = -1;
static const char resp[] =
""
+ "xmlns:u=\"" SERVICE_TYPE_WANIPC "\">"
"";
struct NameValueParserData data;
const char * protocol;
unsigned short startport, endport;
int manage;
+ unsigned short * port_list;
+ unsigned int i, number = 0;
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
startport = (unsigned short)atoi(GetValueFromNameValueList(&data, "NewStartPort"));
@@ -645,13 +727,26 @@ DeletePortMappingRange(struct upnphttp * h, const char
protocol = GetValueFromNameValueList(&data, "NewProtocol");
manage = atoi(GetValueFromNameValueList(&data, "NewManage"));
- /* TODO : implement the method ! */
-
/* possible errors :
606 - Action not authorized
730 - PortMappingNotFound
733 - InconsistentParameter
*/
+ if(startport > endport)
+ {
+ SoapError(h, 733, "InconsistentParameter");
+ ClearNameValueList(&data);
+ return;
+ }
+
+ port_list = upnp_get_portmappings_in_range(startport, endport,
+ protocol, &number);
+ for(i = 0; i < number; i++)
+ {
+ r = upnp_delete_redirection(port_list[i], protocol);
+ /* TODO : check return value for errors */
+ }
+ free(port_list);
BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
ClearNameValueList(&data);
@@ -665,14 +760,14 @@ GetGenericPortMappingEntry(struct upnphttp * h, const
static const char resp[] =
""
- ""
+ "%s"
"%u"
"%s"
"%u"
"%s"
"1"
"%s"
- "0"
+ "%u"
"";
int index = 0;
@@ -680,6 +775,8 @@ GetGenericPortMappingEntry(struct upnphttp * h, const
const char * m_index;
char protocol[4], iaddr[32];
char desc[64];
+ char rhost[40];
+ unsigned int leaseduration = 0;
struct NameValueParserData data;
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
@@ -696,9 +793,12 @@ GetGenericPortMappingEntry(struct upnphttp * h, const
syslog(LOG_INFO, "%s: index=%d", action, index);
+ rhost[0] = '\0';
r = upnp_get_redirection_infos_by_index(index, &eport, protocol, &iport,
iaddr, sizeof(iaddr),
- desc, sizeof(desc));
+ desc, sizeof(desc),
+ rhost, sizeof(rhost),
+ &leaseduration);
if(r < 0)
{
@@ -709,9 +809,9 @@ GetGenericPortMappingEntry(struct upnphttp * h, const
int bodylen;
char body[2048];
bodylen = snprintf(body, sizeof(body), resp,
- action, "urn:schemas-upnp-org:service:WANIPConnection:1",
+ action, SERVICE_TYPE_WANIPC, rhost,
(unsigned int)eport, protocol, (unsigned int)iport, iaddr, desc,
- action);
+ leaseduration, action);
BuildSendAndCloseSoapResp(h, body, bodylen);
}
@@ -722,20 +822,52 @@ GetGenericPortMappingEntry(struct upnphttp * h, const
static void
GetListOfPortMappings(struct upnphttp * h, const char * action)
{
- static const char resp[] =
+ static const char resp_start[] =
""
- ""
+ ""
"";
- char body[512];
+ static const char list_start[] =
+ "";
+ static const char list_end[] =
+ "";
+
+ static const char entry[] =
+ ""
+ "%s"
+ "%hu"
+ "%s"
+ "%hu"
+ "%s"
+ "1"
+ "%s"
+ "%u"
+ "";
+
+ char * body;
+ size_t bodyalloc;
int bodylen;
+ int r = -1;
+ unsigned short iport;
+ char int_ip[32];
+ char desc[64];
+ char rhost[64];
+ unsigned int leaseduration = 0;
+
struct NameValueParserData data;
unsigned short startport, endport;
const char * protocol;
int manage;
int number;
+ unsigned short * port_list;
+ unsigned int i, list_size = 0;
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
startport = (unsigned short)atoi(GetValueFromNameValueList(&data, "NewStartPort"));
@@ -743,9 +875,16 @@ GetListOfPortMappings(struct upnphttp * h, const char
protocol = GetValueFromNameValueList(&data, "NewProtocol");
manage = atoi(GetValueFromNameValueList(&data, "NewManage"));
number = atoi(GetValueFromNameValueList(&data, "NewNumberOfPorts"));
+ if(number == 0) number = 1000; /* return up to 1000 mappings by default */
+ if(startport > endport)
+ {
+ SoapError(h, 733, "InconsistentParameter");
+ ClearNameValueList(&data);
+ return;
+ }
/*
-TODO : build the PortMappingList xml document :
+build the PortMappingList xml document :
*/
- bodylen = snprintf(body, sizeof(body), resp,
- action, "urn:schemas-upnp-org:service:WANIPConnection:2",
- "", action);
+ bodyalloc = 4096;
+ body = malloc(bodyalloc);
+ if(!body)
+ {
+ ClearNameValueList(&data);
+ SoapError(h, 501, "ActionFailed");
+ return;
+ }
+ bodylen = snprintf(body, bodyalloc, resp_start,
+ action, SERVICE_TYPE_WANIPC);
+ memcpy(body+bodylen, list_start, sizeof(list_start));
+ bodylen += (sizeof(list_start) - 1);
+
+ port_list = upnp_get_portmappings_in_range(startport, endport,
+ protocol, &list_size);
+ /* loop through port mappings */
+ for(i = 0; number > 0 && i < list_size; i++)
+ {
+ /* have a margin of 1024 bytes to store the new entry */
+ if(bodylen + 1024 > bodyalloc)
+ {
+ bodyalloc += 4096;
+ body = realloc(body, bodyalloc);
+ if(!body)
+ {
+ ClearNameValueList(&data);
+ SoapError(h, 501, "ActionFailed");
+ free(port_list);
+ return;
+ }
+ }
+ rhost[0] = '\0';
+ r = upnp_get_redirection_infos(port_list[i], protocol, &iport,
+ int_ip, sizeof(int_ip),
+ desc, sizeof(desc),
+ rhost, sizeof(rhost),
+ &leaseduration);
+ if(r == 0)
+ {
+ bodylen += snprintf(body+bodylen, bodyalloc-bodylen, entry,
+ rhost, port_list[i], protocol,
+ iport, int_ip, desc, leaseduration);
+ number--;
+ }
+ }
+ free(port_list);
+ port_list = NULL;
+
+ memcpy(body+bodylen, list_end, sizeof(list_end));
+ bodylen += (sizeof(list_end) - 1);
+ bodylen += snprintf(body+bodylen, bodyalloc-bodylen, resp_end,
+ action);
BuildSendAndCloseSoapResp(h, body, bodylen);
+ free(body);
ClearNameValueList(&data);
}
@@ -797,7 +986,7 @@ GetDefaultConnectionService(struct upnphttp * h, const
""
"%s:WANConnectionDevice:1,"
- "urn:upnp-org:serviceId:WANIPConn1"
+ SERVICE_ID_WANIPC ""
"";
/* example from UPnP_IGD_Layer3Forwarding 1.0.pdf :
* uuid:44f5824f-c57d-418c-a131-f22b34e14111:WANConnectionDevice:1,
@@ -875,11 +1064,9 @@ QueryStateVariable(struct upnphttp * h, const char * a
}
else if(strcmp(var_name, "ConnectionStatus") == 0)
{
- const char * status = "Connected";
- char ext_ip_addr[INET_ADDRSTRLEN];
- if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0) {
- status = "Disconnected";
- }
+ const char * status;
+
+ status = get_wan_connection_status_str(ext_if_name);
bodylen = snprintf(body, sizeof(body), resp,
action, "urn:schemas-upnp-org:control-1-0",
status, action);
@@ -917,6 +1104,625 @@ QueryStateVariable(struct upnphttp * h, const char * a
ClearNameValueList(&data);
}
+#ifdef ENABLE_6FC_SERVICE
+#ifndef ENABLE_IPV6
+#error "ENABLE_6FC_SERVICE needs ENABLE_IPV6"
+#endif
+/* WANIPv6FirewallControl actions */
+static void
+GetFirewallStatus(struct upnphttp * h, const char * action)
+{
+ static const char resp[] =
+ ""
+ "%d"
+ "%d"
+ "";
+
+ char body[512];
+ int bodylen;
+
+ bodylen = snprintf(body, sizeof(body), resp,
+ action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1",
+ ipv6fc_firewall_enabled, ipv6fc_inbound_pinhole_allowed, action);
+ BuildSendAndCloseSoapResp(h, body, bodylen);
+}
+
+static int
+CheckStatus(struct upnphttp * h)
+{
+ if (!ipv6fc_firewall_enabled)
+ {
+ SoapError(h, 702, "FirewallDisabed");
+ return 0;
+ }
+ else if(!ipv6fc_inbound_pinhole_allowed)
+ {
+ SoapError(h, 703, "InboundPinholeNotAllowed");
+ return 0;
+ }
+ else
+ return 1;
+}
+
+static int
+DataVerification(struct upnphttp * h, char * int_ip, unsigned short * int_port, const char * protocol, char * leaseTime)
+{
+ //int n;
+ // ** Internal IP can't be wildcarded
+ if (!int_ip)
+ {
+ SoapError(h, 708, "WildCardNotPermittedInSrcIP");
+ return 0;
+ }
+
+ if (!strchr(int_ip, ':'))
+ {
+ SoapError(h, 402, "Invalid Args");
+ return 0;
+ }
+
+ // ** Internal port can't be wilcarded.
+// printf("\tint_port: *%d*\n", *int_port);
+ if (*int_port == 0)
+ {
+ SoapError(h, 706, "InternalPortWilcardingNotAllowed");
+ return 0;
+ }
+
+ // ** Protocol can't be wilcarded and can't be an unknown port (here deal with only UDP, TCP, UDPLITE)
+// printf("\tprotocol: *%s*\n", protocol);
+ if (atoi(protocol) == 65535)
+ {
+ SoapError(h, 707, "ProtocolWilcardingNotAllowed");
+ return 0;
+ }
+ else if (atoi(protocol) != IPPROTO_UDP
+ && atoi(protocol) != IPPROTO_TCP
+#ifdef IPPROTO_UDPITE
+ && atoi(protocol) != IPPROTO_UDPLITE
+#endif
+ )
+ {
+ SoapError(h, 705, "ProtocolNotSupported");
+ return 0;
+ }
+
+ // ** Lease Time can't be wilcarded nor >86400.
+// printf("\tlease time: %s\n", leaseTime);
+ if(!leaseTime || !atoi(leaseTime) || atoi(leaseTime)>86400)
+ {
+ /* lease duration is never infinite, nor wilcarded. In this case, use default value */
+ syslog(LOG_WARNING, "LeaseTime=%s not supported, (ip=%s)", leaseTime, int_ip);
+ SoapError(h, 402, "Invalid Args");
+ return 0;
+ }
+
+ return 1;
+}
+
+#if 0
+static int connecthostport(const char * host, unsigned short port, char * result)
+{
+ int s, n;
+ char hostname[INET6_ADDRSTRLEN];
+ char port_str[8], ifname[8], tmp[4];
+ struct addrinfo *ai, *p;
+ struct addrinfo hints;
+
+ memset(&hints, 0, sizeof(hints));
+ /* hints.ai_flags = AI_ADDRCONFIG; */
+#ifdef AI_NUMERICSERV
+ hints.ai_flags = AI_NUMERICSERV;
+#endif
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */
+ /* hints.ai_protocol = IPPROTO_TCP; */
+ snprintf(port_str, sizeof(port_str), "%hu", port);
+ strcpy(hostname, host);
+ if(!strncmp(host, "fe80", 4))
+ {
+ printf("Using an linklocal address\n");
+ strcpy(ifname, "%");
+ snprintf(tmp, sizeof(tmp), "%d", linklocal_index);
+ strcat(ifname, tmp);
+ strcat(hostname, ifname);
+ printf("host: %s\n", hostname);
+ }
+ n = getaddrinfo(hostname, port_str, &hints, &ai);
+ if(n != 0)
+ {
+ fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
+ return -1;
+ }
+ s = -1;
+ for(p = ai; p; p = p->ai_next)
+ {
+#ifdef DEBUG
+ char tmp_host[256];
+ char tmp_service[256];
+ printf("ai_family=%d ai_socktype=%d ai_protocol=%d ai_addrlen=%d\n ",
+ p->ai_family, p->ai_socktype, p->ai_protocol, p->ai_addrlen);
+ getnameinfo(p->ai_addr, p->ai_addrlen, tmp_host, sizeof(tmp_host),
+ tmp_service, sizeof(tmp_service),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ printf(" host=%s service=%s\n", tmp_host, tmp_service);
+#endif
+ inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)p->ai_addr)->sin6_addr), result, INET6_ADDRSTRLEN);
+ return 0;
+ }
+ freeaddrinfo(ai);
+}
+#endif
+
+/* Check the security policy right */
+static int
+PinholeVerification(struct upnphttp * h, char * int_ip, unsigned short * int_port)
+{
+ int n;
+ /* Pinhole InternalClient address must correspond to the action sender */
+ syslog(LOG_INFO, "Checking internal IP@ and port (Security policy purpose)");
+ char senderAddr[INET6_ADDRSTRLEN]="";
+ //char str[INET6_ADDRSTRLEN]="";
+ //connecthostport(int_ip, *int_port, str);
+ //printf("int_ip: %s / str: %s\n", int_ip, str);
+
+ struct addrinfo hints, *ai, *p;
+ struct in6_addr result_ip;/*unsigned char result_ip[16];*/ /* inet_pton() */ //IPv6 Modification
+
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_UNSPEC;
+
+ /* if ip not valid assume hostname and convert */
+ if (inet_pton(AF_INET6, int_ip, &result_ip) <= 0) //IPv6 Modification
+ {
+
+ n = getaddrinfo(int_ip, NULL, &hints, &ai);//hp = gethostbyname(int_ip);
+ if(!n && ai->ai_family == AF_INET6) //IPv6 Modification
+ {
+ for(p = ai; p; p = p->ai_next)//ptr = hp->h_addr_list; ptr && *ptr; ptr++)
+ {
+ inet_ntop(AF_INET6, (struct in6_addr *) p, int_ip, sizeof(struct in6_addr)); ///IPv6 Modification
+ result_ip = *((struct in6_addr *) p);
+ fprintf(stderr, "upnpsoap / AddPinhole: assuming int addr = %s", int_ip);
+ /* TODO : deal with more than one ip per hostname */
+ break;
+ }
+ }
+ else
+ {
+ syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip);
+ SoapError(h, 402, "Invalid Args");
+ return -1;
+ }
+ freeaddrinfo(p);
+ }
+
+ if(inet_ntop(AF_INET6, &(h->clientaddr_v6), senderAddr, INET6_ADDRSTRLEN)<=0)
+ {
+ //printf("Failed to inet_ntop\n");
+ syslog(LOG_ERR, "inet_ntop: %m");
+ }
+#ifdef DEBUG
+ printf("\tPinholeVerification:\n\t\tCompare sender @: %s\n\t\t to intClient @: %s\n", senderAddr, int_ip);
+#endif
+ if(strcmp(senderAddr, int_ip) != 0)
+ if(h->clientaddr_v6.s6_addr != result_ip.s6_addr)
+ {
+ syslog(LOG_INFO, "Client %s tried to access pinhole for internal %s and is not authorized to do it",
+ senderAddr, int_ip);
+ SoapError(h, 606, "Action not authorized");
+ return 0;
+ }
+
+ /* Pinhole InternalPort must be greater than or equal to 1024 */
+ if (*int_port < 1024)
+ {
+ syslog(LOG_INFO, "Client %s tried to access pinhole with port < 1024 and is not authorized to do it",
+ senderAddr);
+ SoapError(h, 606, "Action not authorized");
+ return 0;
+ }
+ return 1;
+}
+
+static void
+AddPinhole(struct upnphttp * h, const char * action)
+{
+ int r;
+ static const char resp[] =
+ ""
+ "%d"
+ "";
+ char body[512];
+ int bodylen;
+ struct NameValueParserData data;
+ char * rem_host, * rem_port, * int_ip, * int_port, * protocol, * leaseTime;
+ int uid = 0;
+ unsigned short iport, rport;
+
+ if(CheckStatus(h)==0)
+ return;
+
+ ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
+ rem_host = GetValueFromNameValueList(&data, "RemoteHost");
+ rem_port = GetValueFromNameValueList(&data, "RemotePort");
+ int_ip = GetValueFromNameValueList(&data, "InternalClient");
+ int_port = GetValueFromNameValueList(&data, "InternalPort");
+ protocol = GetValueFromNameValueList(&data, "Protocol");
+ leaseTime = GetValueFromNameValueList(&data, "LeaseTime");
+
+ rport = (unsigned short)atoi(rem_port);
+ iport = (unsigned short)atoi(int_port);
+
+ // ** As there is no security policy, InternalClient must be equal to the CP's IP address.
+ if(DataVerification(h, int_ip, &iport, protocol, leaseTime) == 0
+ || PinholeVerification(h, int_ip, &iport) <= 0)
+ {
+ ClearNameValueList(&data);
+ return ;
+ }
+
+ // ** RemoteHost can be wilcarded or an IDN.
+ /*printf("\trem_host: %s\n", rem_host);*/
+ if (rem_host!=NULL && !strchr(rem_host, ':'))
+ {
+ ClearNameValueList(&data);
+ SoapError(h, 402, "Invalid Args");
+ return;
+ }
+ /*printf("\tAddr check passed.\n");*/
+
+ syslog(LOG_INFO, "%s: (inbound) from [%s]:%hu to [%s]:%hu with protocol %s during %ssec", action, rem_host?rem_host:"anywhere", rport, int_ip, iport, protocol, leaseTime);
+
+ r = upnp_add_inboundpinhole(rem_host, rport, int_ip, iport, protocol, leaseTime, &uid);
+
+ switch(r)
+ {
+ case 1: /* success */
+ bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1", uid, action);
+ BuildSendAndCloseSoapResp(h, body, bodylen);
+ break;
+ case -1: /* not permitted */
+ SoapError(h, 701, "PinholeSpaceExhausted");
+ break;
+ default:
+ SoapError(h, 501, "ActionFailed");
+ break;
+ }
+ ClearNameValueList(&data);
+}
+
+static void
+UpdatePinhole(struct upnphttp * h, const char * action)
+{
+ int r, n;
+ static const char resp[] =
+ ""
+ "";
+ struct NameValueParserData data;
+ const char * uid, * leaseTime;
+ char iaddr[40], proto[6], lt[12];
+ unsigned short iport;
+
+ if(CheckStatus(h)==0)
+ return;
+
+ ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
+ uid = GetValueFromNameValueList(&data, "UniqueID");
+ leaseTime = GetValueFromNameValueList(&data, "NewLeaseTime");
+
+ if(!uid || !leaseTime || !atoi(leaseTime) || atoi(leaseTime) > 86400)
+ {
+ ClearNameValueList(&data);
+ SoapError(h, 402, "Invalid Args");
+ return;
+ }
+
+ // Check that client is not deleting an pinhole he doesn't have access to, because of its public access
+ n = upnp_get_pinhole_info(0, 0, iaddr, &iport, proto, uid, lt);
+ if (n > 0)
+ {
+ if(PinholeVerification(h, iaddr, &iport)==0)
+ {
+ ClearNameValueList(&data);
+ return ;
+ }
+ }
+
+ syslog(LOG_INFO, "%s: (inbound) updating lease duration to %s for pinhole with ID: %s", action, leaseTime, uid);
+
+ r = upnp_update_inboundpinhole(uid, leaseTime);
+
+ if(r < 0)
+ {
+ if(r == -4 || r == -1)
+ SoapError(h, 704, "NoSuchEntry");
+ else
+ SoapError(h, 501, "ActionFailed");
+ }
+ else
+ {
+ BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
+ }
+ ClearNameValueList(&data);
+}
+
+static void
+GetOutboundPinholeTimeout(struct upnphttp * h, const char * action)
+{
+ if (!ipv6fc_firewall_enabled)
+ {
+ SoapError(h, 702, "FirewallDisabed");
+ return;
+ }
+ int r;
+
+ static const char resp[] =
+ ""
+ "%d"
+ "";
+
+ char body[512];
+ int bodylen;
+ struct NameValueParserData data;
+ char * int_ip, * int_port, * rem_host, * rem_port, * protocol;
+ int opt=0, proto=0;
+ unsigned short iport, rport;
+
+ ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
+ int_ip = GetValueFromNameValueList(&data, "InternalClient");
+ int_port = GetValueFromNameValueList(&data, "InternalPort");
+ rem_host = GetValueFromNameValueList(&data, "RemoteHost");
+ rem_port = GetValueFromNameValueList(&data, "RemotePort");
+ protocol = GetValueFromNameValueList(&data, "Protocol");
+
+ rport = (unsigned short)atoi(rem_port);
+ iport = (unsigned short)atoi(int_port);
+ proto = atoi(protocol);
+
+ syslog(LOG_INFO, "%s: retrieving timeout for outbound pinhole from [%s]:%hu to [%s]:%hu protocol %s", action, int_ip, iport,rem_host, rport, protocol);
+
+ r = upnp_check_outbound_pinhole(proto, &opt);
+
+ switch(r)
+ {
+ case 1: /* success */
+ bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1", opt, action);
+ BuildSendAndCloseSoapResp(h, body, bodylen);
+ break;
+ case -5: /* Protocol not supported */
+ SoapError(h, 705, "ProtocolNotSupported");
+ break;
+ default:
+ SoapError(h, 501, "ActionFailed");
+ }
+ ClearNameValueList(&data);
+}
+
+static void
+DeletePinhole(struct upnphttp * h, const char * action)
+{
+ if(CheckStatus(h)==0)
+ return;
+ int r, n;
+
+ static const char resp[] =
+ ""
+ "";
+
+ struct NameValueParserData data;
+ const char * uid;
+ char iaddr[40], proto[6], lt[12];
+ unsigned short iport;
+
+ ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
+ uid = GetValueFromNameValueList(&data, "UniqueID");
+
+ if(!uid)
+ {
+ ClearNameValueList(&data);
+ SoapError(h, 402, "Invalid Args");
+ return;
+ }
+
+ // Check that client is not deleting an pinhole he doesn't have access to, because of its public access
+ n = upnp_get_pinhole_info(0, 0, iaddr, &iport, proto, uid, lt);
+ if (n > 0)
+ {
+ if(PinholeVerification(h, iaddr, &iport)==0)
+ {
+ ClearNameValueList(&data);
+ return ;
+ }
+ }
+
+ syslog(LOG_INFO, "%s: (inbound) delete pinhole with ID: %s", action, uid);
+
+ r = upnp_delete_inboundpinhole(uid);
+
+ if(r <= 0)
+ {
+ syslog(LOG_INFO, "%s: (inbound) failed to remove pinhole with ID: %s", action, uid);
+ if(r==-4)
+ SoapError(h, 704, "NoSuchEntry");
+ else
+ SoapError(h, 501, "ActionFailed");
+ }
+ else
+ {
+ syslog(LOG_INFO, "%s: (inbound) pinhole successfully removed", action);
+ BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
+ }
+ ClearNameValueList(&data);
+}
+
+static void
+CheckPinholeWorking(struct upnphttp * h, const char * action)
+{
+ if(CheckStatus(h)==0)
+ return;
+ int r, d;
+
+ static const char resp[] =
+ ""
+ "%d"
+ "";
+
+ char body[512];
+ int bodylen;
+ struct NameValueParserData data;
+ const char * uid;
+ char eaddr[40], iaddr[40], proto[6], lt[12];
+ unsigned short eport, iport;
+ int isWorking = 0;
+
+ ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
+ uid = GetValueFromNameValueList(&data, "UniqueID");
+
+ if(!uid)
+ {
+ ClearNameValueList(&data);
+ SoapError(h, 402, "Invalid Args");
+ return;
+ }
+
+ // Check that client is not checking a pinhole he doesn't have access to, because of its public access
+ r = upnp_get_pinhole_info(eaddr, eport, iaddr, &iport, proto, uid, lt);
+ if (r > 0)
+ {
+ if(PinholeVerification(h, iaddr, &iport)==0)
+ {
+ ClearNameValueList(&data);
+ return ;
+ }
+ else
+ {
+ int rulenum_used, rulenum = 0;
+ d = upnp_check_pinhole_working(uid, eaddr, iaddr, &eport, &iport, proto, &rulenum_used);
+ if(d < 0)
+ {
+ if(d == -4)
+ {
+ syslog(LOG_INFO, "%s: rule for ID=%s, no trace found for this pinhole", action, uid);
+ SoapError(h, 709, "NoPacketSent");
+ ClearNameValueList(&data);
+ return ;
+ }
+ else
+ {
+ // d==-5 not same table // d==-6 not same chain // d==-7 not found a rule but policy traced
+ isWorking=0;
+ syslog(LOG_INFO, "%s: rule for ID=%s is not working, packet going through %s", action, uid, (d==-5)?"the wrong table":((d==-6)?"the wrong chain":"a chain policy"));
+ bodylen = snprintf(body, sizeof(body), resp,
+ action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1",
+ isWorking, action);
+ BuildSendAndCloseSoapResp(h, body, bodylen);
+ }
+ }
+ else
+ {
+ /*check_rule_from_file(uid, &rulenum);*/
+ if(rulenum_used == rulenum)
+ {
+ isWorking=1;
+ syslog(LOG_INFO, "%s: rule for ID=%s is working properly", action, uid);
+ }
+ else
+ {
+ isWorking=0;
+ syslog(LOG_INFO, "%s: rule for ID=%s is not working", action, uid);
+ }
+ bodylen = snprintf(body, sizeof(body), resp,
+ action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1",
+ isWorking, action);
+ BuildSendAndCloseSoapResp(h, body, bodylen);
+ }
+ }
+ }
+ else if(r == -4 || r == -1)
+ {
+ SoapError(h, 704, "NoSuchEntry");
+ }
+ else
+ {
+ SoapError(h, 501, "ActionFailed");
+ ClearNameValueList(&data);
+ return ;
+ }
+ ClearNameValueList(&data);
+}
+
+static void
+GetPinholePackets(struct upnphttp * h, const char * action)
+{
+ if(CheckStatus(h)==0)
+ return;
+ int r, n;
+
+ static const char resp[] =
+ ""
+ "%d"
+ "";
+
+ char body[512];
+ int bodylen;
+ struct NameValueParserData data;
+ const char * uid;
+ char iaddr[40], proto[6], lt[12];
+ unsigned short iport;
+ int pinholePackets = 0;
+
+ ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
+ uid = GetValueFromNameValueList(&data, "UniqueID");
+
+ if(!uid)
+ {
+ ClearNameValueList(&data);
+ SoapError(h, 402, "Invalid Args");
+ return;
+ }
+
+ // Check that client is not getting infos of a pinhole he doesn't have access to, because of its public access
+ r = upnp_get_pinhole_info(0, 0, iaddr, &iport, proto, uid, lt);
+ if (r > 0)
+ {
+ if(PinholeVerification(h, iaddr, &iport)==0)
+ {
+ ClearNameValueList(&data);
+ return ;
+ }
+ }
+
+ n = upnp_get_pinhole_packets(uid, &pinholePackets);
+ if(n > 0)
+ {
+ bodylen = snprintf(body, sizeof(body), resp,
+ action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1",
+ pinholePackets, action);
+ BuildSendAndCloseSoapResp(h, body, bodylen);
+ }
+ else if(r == -4 || r == -1)
+ {
+ SoapError(h, 704, "NoSuchEntry");
+ }
+ else
+ {
+ SoapError(h, 501, "ActionFailed");
+ ClearNameValueList(&data);
+ return ;
+ }
+ ClearNameValueList(&data);
+}
+#endif
+
+
/* Windows XP as client send the following requests :
* GetConnectionTypeInfo
* GetNATRSIPStatus
@@ -936,13 +1742,7 @@ static const struct
}
soapMethods[] =
{
- { "GetConnectionTypeInfo", GetConnectionTypeInfo },
- { "GetNATRSIPStatus", GetNATRSIPStatus},
- { "GetExternalIPAddress", GetExternalIPAddress},
- { "AddPortMapping", AddPortMapping},
- { "DeletePortMapping", DeletePortMapping},
- { "GetGenericPortMappingEntry", GetGenericPortMappingEntry},
- { "GetSpecificPortMappingEntry", GetSpecificPortMappingEntry},
+ /* WANCommonInterfaceConfig */
{ "QueryStateVariable", QueryStateVariable},
{ "GetTotalBytesSent", GetTotalBytesSent},
{ "GetTotalBytesReceived", GetTotalBytesReceived},
@@ -950,6 +1750,14 @@ soapMethods[] =
{ "GetTotalPacketsReceived", GetTotalPacketsReceived},
{ "GetCommonLinkProperties", GetCommonLinkProperties},
{ "GetStatusInfo", GetStatusInfo},
+ /* WANIPConnection */
+ { "GetConnectionTypeInfo", GetConnectionTypeInfo },
+ { "GetNATRSIPStatus", GetNATRSIPStatus},
+ { "GetExternalIPAddress", GetExternalIPAddress},
+ { "AddPortMapping", AddPortMapping},
+ { "DeletePortMapping", DeletePortMapping},
+ { "GetGenericPortMappingEntry", GetGenericPortMappingEntry},
+ { "GetSpecificPortMappingEntry", GetSpecificPortMappingEntry},
/* Required in WANIPConnection:2 */
{ "SetConnectionType", SetConnectionType},
{ "RequestConnection", RequestConnection},
@@ -958,8 +1766,19 @@ soapMethods[] =
{ "DeletePortMappingRange", DeletePortMappingRange},
{ "GetListOfPortMappings", GetListOfPortMappings},
#ifdef ENABLE_L3F_SERVICE
+ /* Layer3Forwarding */
{ "SetDefaultConnectionService", SetDefaultConnectionService},
{ "GetDefaultConnectionService", GetDefaultConnectionService},
+#endif
+#ifdef ENABLE_6FC_SERVICE
+ /* WANIPv6FirewallControl */
+ { "GetFirewallStatus", GetFirewallStatus},
+ { "AddPinhole", AddPinhole},
+ { "UpdatePinhole", UpdatePinhole},
+ { "GetOutboundPinholeTimeout", GetOutboundPinholeTimeout},
+ { "DeletePinhole", DeletePinhole},
+ { "CheckPinholeWorking", CheckPinholeWorking},
+ { "GetPinholePackets", GetPinholePackets},
#endif
{ 0, 0 }
};