--- 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 } };