/* $Id: upnpdescgen.c,v 1.1.1.1 2012/02/21 23:16:02 misho Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2009 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include "config.h" #ifdef ENABLE_EVENTS #include "getifaddr.h" #include "upnpredirect.h" #endif #include "upnpdescgen.h" #include "miniupnpdpath.h" #include "upnpglobalvars.h" #include "upnpdescstrings.h" static const char * const upnptypes[] = { "string", "boolean", "ui2", "ui4" }; static const char * const upnpdefaultvalues[] = { 0, "Unconfigured" }; static const char * const upnpallowedvalues[] = { 0, /* 0 */ "DSL", /* 1 */ "POTS", "Cable", "Ethernet", 0, "Up", /* 6 */ "Down", "Initializing", "Unavailable", 0, "TCP", /* 11 */ "UDP", 0, "Unconfigured", /* 14 */ "IP_Routed", "IP_Bridged", 0, "Unconfigured", /* 18 */ "Connecting", "Connected", "PendingDisconnect", "Disconnecting", "Disconnected", 0, "ERROR_NONE", /* 25 */ 0, "", /* 27 */ 0 }; static const char xmlver[] = "\r\n"; static const char root_service[] = "scpd xmlns=\"urn:schemas-upnp-org:service-1-0\""; static const char root_device[] = "root xmlns=\"urn:schemas-upnp-org:device-1-0\""; /* root Description of the UPnP Device * fixed to match UPnP_IGD_InternetGatewayDevice 1.0.pdf * presentationURL is only "recommended" but the router doesn't appears * in "Network connections" in Windows XP if it is not present. */ static const struct XMLElt rootDesc[] = { /* 0 */ {root_device, INITHELPER(1,2)}, {"specVersion", INITHELPER(3,2)}, #if defined(ENABLE_L3F_SERVICE) || defined(HAS_DUMMY_SERVICE) {"device", INITHELPER(5,13)}, #else {"device", INITHELPER(5,12)}, #endif {"/major", "1"}, {"/minor", "0"}, /* 5 */ {"/deviceType", "urn:schemas-upnp-org:device:InternetGatewayDevice:1"}, {"/friendlyName", ROOTDEV_FRIENDLYNAME}, /* required */ {"/manufacturer", ROOTDEV_MANUFACTURER}, /* required */ /* 8 */ {"/manufacturerURL", ROOTDEV_MANUFACTURERURL}, /* optional */ {"/modelDescription", ROOTDEV_MODELDESCRIPTION}, /* recommended */ {"/modelName", ROOTDEV_MODELNAME}, /* required */ {"/modelNumber", modelnumber}, {"/modelURL", ROOTDEV_MODELURL}, {"/serialNumber", serialnumber}, {"/UDN", uuidvalue}, /* required */ #if defined(ENABLE_L3F_SERVICE) || defined(HAS_DUMMY_SERVICE) {"serviceList", INITHELPER(57,1)}, {"deviceList", INITHELPER(18,1)}, {"/presentationURL", presentationurl}, /* recommended */ #else {"deviceList", INITHELPER(18,1)}, {"/presentationURL", presentationurl}, /* recommended */ {0,0}, #endif /* 18 */ {"device", INITHELPER(19,13)}, /* 19 */ {"/deviceType", "urn:schemas-upnp-org:device:WANDevice:1"}, /* required */ {"/friendlyName", WANDEV_FRIENDLYNAME}, {"/manufacturer", WANDEV_MANUFACTURER}, {"/manufacturerURL", WANDEV_MANUFACTURERURL}, {"/modelDescription" , WANDEV_MODELDESCRIPTION}, {"/modelName", WANDEV_MODELNAME}, {"/modelNumber", WANDEV_MODELNUMBER}, {"/modelURL", WANDEV_MODELURL}, {"/serialNumber", serialnumber}, {"/UDN", uuidvalue}, {"/UPC", WANDEV_UPC}, /* 30 */ {"serviceList", INITHELPER(32,1)}, {"deviceList", INITHELPER(38,1)}, /* 32 */ {"service", INITHELPER(33,5)}, /* 33 */ {"/serviceType", "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"}, /*{"/serviceId", "urn:upnp-org:serviceId:WANCommonInterfaceConfig"}, */ {"/serviceId", "urn:upnp-org:serviceId:WANCommonIFC1"}, /* required */ {"/controlURL", WANCFG_CONTROLURL}, {"/eventSubURL", WANCFG_EVENTURL}, {"/SCPDURL", WANCFG_PATH}, /* 38 */ {"device", INITHELPER(39,12)}, /* 39 */ {"/deviceType", "urn:schemas-upnp-org:device:WANConnectionDevice:1"}, {"/friendlyName", WANCDEV_FRIENDLYNAME}, {"/manufacturer", WANCDEV_MANUFACTURER}, {"/manufacturerURL", WANCDEV_MANUFACTURERURL}, {"/modelDescription", WANCDEV_MODELDESCRIPTION}, {"/modelName", WANCDEV_MODELNAME}, {"/modelNumber", WANCDEV_MODELNUMBER}, {"/modelURL", WANCDEV_MODELURL}, {"/serialNumber", serialnumber}, {"/UDN", uuidvalue}, {"/UPC", WANCDEV_UPC}, {"serviceList", INITHELPER(51,1)}, /* 51 */ {"service", INITHELPER(52,5)}, /* 52 */ {"/serviceType", "urn:schemas-upnp-org:service:WANIPConnection:1"}, /* {"/serviceId", "urn:upnp-org:serviceId:WANIPConnection"}, */ {"/serviceId", "urn:upnp-org:serviceId:WANIPConn1"}, {"/controlURL", WANIPC_CONTROLURL}, {"/eventSubURL", WANIPC_EVENTURL}, {"/SCPDURL", WANIPC_PATH}, /* 57 */ #ifdef HAS_DUMMY_SERVICE {"service", INITHELPER(58,5)}, /* 58 */ {"/serviceType", "urn:schemas-dummy-com:service:Dummy:1"}, {"/serviceId", "urn:dummy-com:serviceId:dummy1"}, {"/controlURL", "/dummy"}, {"/eventSubURL", "/dummy"}, {"/SCPDURL", DUMMY_PATH}, #endif #ifdef ENABLE_L3F_SERVICE {"service", INITHELPER(58,5)}, /* 58 */ {"/serviceType", "urn:schemas-upnp-org:service:Layer3Forwarding:1"}, {"/serviceId", "urn:upnp-org:serviceId:Layer3Forwarding1"}, {"/controlURL", L3F_CONTROLURL}, /* The Layer3Forwarding service is only */ {"/eventSubURL", L3F_EVENTURL}, /* recommended, not mandatory */ {"/SCPDURL", L3F_PATH}, #endif {0, 0} }; /* WANIPCn.xml */ /* see UPnP_IGD_WANIPConnection 1.0.pdf static struct XMLElt scpdWANIPCn[] = { {root_service, {INITHELPER(1,2)}}, {0, {0}} }; */ static const struct argument AddPortMappingArgs[] = { {1, 11}, {1, 12}, {1, 14}, {1, 13}, {1, 15}, {1, 9}, {1, 16}, {1, 10}, {0, 0} }; static const struct argument GetExternalIPAddressArgs[] = { {2, 7}, {0, 0} }; static const struct argument DeletePortMappingArgs[] = { {1, 11}, {1, 12}, {1, 14}, {0, 0} }; static const struct argument SetConnectionTypeArgs[] = { {1, 0}, {0, 0} }; static const struct argument GetConnectionTypeInfoArgs[] = { {2, 0}, {2, 1}, {0, 0} }; static const struct argument GetStatusInfoArgs[] = { {2, 2}, {2, 4}, {2, 3}, {0, 0} }; static const struct argument GetNATRSIPStatusArgs[] = { {2, 5}, {2, 6}, {0, 0} }; static const struct argument GetGenericPortMappingEntryArgs[] = { {1, 8}, {2, 11}, {2, 12}, {2, 14}, {2, 13}, {2, 15}, {2, 9}, {2, 16}, {2, 10}, {0, 0} }; static const struct argument GetSpecificPortMappingEntryArgs[] = { {1, 11}, {1, 12}, {1, 14}, {2, 13}, {2, 15}, {2, 9}, {2, 16}, {2, 10}, {0, 0} }; static const struct action WANIPCnActions[] = { {"AddPortMapping", AddPortMappingArgs}, /* R */ {"GetExternalIPAddress", GetExternalIPAddressArgs}, /* R */ {"DeletePortMapping", DeletePortMappingArgs}, /* R */ {"SetConnectionType", SetConnectionTypeArgs}, /* R */ {"GetConnectionTypeInfo", GetConnectionTypeInfoArgs}, /* R */ {"RequestConnection", 0}, /* R */ {"ForceTermination", 0}, /* R */ {"GetStatusInfo", GetStatusInfoArgs}, /* R */ {"GetNATRSIPStatus", GetNATRSIPStatusArgs}, /* R */ {"GetGenericPortMappingEntry", GetGenericPortMappingEntryArgs}, /* R */ {"GetSpecificPortMappingEntry", GetSpecificPortMappingEntryArgs}, /* R */ {0, 0} }; /* R=Required, O=Optional */ static const struct stateVar WANIPCnVars[] = { /* 0 */ {"ConnectionType", 0, 0/*1*/}, /* required */ {"PossibleConnectionTypes", 0|0x80, 0, 14, 15}, /* Required * Allowed values : Unconfigured / IP_Routed / IP_Bridged */ {"ConnectionStatus", 0|0x80, 0/*1*/, 18, 20}, /* required */ /* Allowed Values : Unconfigured / Connecting(opt) / Connected * PendingDisconnect(opt) / Disconnecting (opt) * Disconnected */ {"Uptime", 3, 0}, /* Required */ {"LastConnectionError", 0, 0, 25}, /* required : */ /* Allowed Values : ERROR_NONE(req) / ERROR_COMMAND_ABORTED(opt) * ERROR_NOT_ENABLED_FOR_INTERNET(opt) * ERROR_USER_DISCONNECT(opt) * ERROR_ISP_DISCONNECT(opt) * ERROR_IDLE_DISCONNECT(opt) * ERROR_FORCED_DISCONNECT(opt) * ERROR_NO_CARRIER(opt) * ERROR_IP_CONFIGURATION(opt) * ERROR_UNKNOWN(opt) */ {"RSIPAvailable", 1, 0}, /* required */ {"NATEnabled", 1, 0}, /* required */ {"ExternalIPAddress", 0|0x80, 0, 0, 254}, /* required. Default : empty string */ {"PortMappingNumberOfEntries", 2|0x80, 0, 0, 253}, /* required >= 0 */ {"PortMappingEnabled", 1, 0}, /* Required */ {"PortMappingLeaseDuration", 3, 0}, /* required */ {"RemoteHost", 0, 0}, /* required. Default : empty string */ {"ExternalPort", 2, 0}, /* required */ {"InternalPort", 2, 0}, /* required */ {"PortMappingProtocol", 0, 0, 11}, /* required allowedValues: TCP/UDP */ {"InternalClient", 0, 0}, /* required */ {"PortMappingDescription", 0, 0}, /* required default: empty string */ {0, 0} }; static const struct serviceDesc scpdWANIPCn = { WANIPCnActions, WANIPCnVars }; /* WANCfg.xml */ /* See UPnP_IGD_WANCommonInterfaceConfig 1.0.pdf */ static const struct argument GetCommonLinkPropertiesArgs[] = { {2, 0}, {2, 1}, {2, 2}, {2, 3}, {0, 0} }; static const struct argument GetTotalBytesSentArgs[] = { {2, 4}, {0, 0} }; static const struct argument GetTotalBytesReceivedArgs[] = { {2, 5}, {0, 0} }; static const struct argument GetTotalPacketsSentArgs[] = { {2, 6}, {0, 0} }; static const struct argument GetTotalPacketsReceivedArgs[] = { {2, 7}, {0, 0} }; static const struct action WANCfgActions[] = { {"GetCommonLinkProperties", GetCommonLinkPropertiesArgs}, /* Required */ {"GetTotalBytesSent", GetTotalBytesSentArgs}, /* optional */ {"GetTotalBytesReceived", GetTotalBytesReceivedArgs}, /* optional */ {"GetTotalPacketsSent", GetTotalPacketsSentArgs}, /* optional */ {"GetTotalPacketsReceived", GetTotalPacketsReceivedArgs}, /* optional */ {0, 0} }; /* See UPnP_IGD_WANCommonInterfaceConfig 1.0.pdf */ static const struct stateVar WANCfgVars[] = { {"WANAccessType", 0, 0, 1}, /* Allowed Values : DSL / POTS / Cable / Ethernet * Default value : empty string */ {"Layer1UpstreamMaxBitRate", 3, 0}, {"Layer1DownstreamMaxBitRate", 3, 0}, {"PhysicalLinkStatus", 0|0x80, 0, 6, 6}, /* allowed values : * Up / Down / Initializing (optional) / Unavailable (optionnal) * no Default value * Evented */ {"TotalBytesSent", 3, 0}, /* Optional */ {"TotalBytesReceived", 3, 0}, /* Optional */ {"TotalPacketsSent", 3, 0}, /* Optional */ {"TotalPacketsReceived", 3, 0},/* Optional */ /*{"MaximumActiveConnections", 2, 0}, // allowed Range value // OPTIONAL */ {0, 0} }; static const struct serviceDesc scpdWANCfg = { WANCfgActions, WANCfgVars }; #ifdef ENABLE_L3F_SERVICE /* Read UPnP_IGD_Layer3Forwarding_1.0.pdf */ static const struct argument SetDefaultConnectionServiceArgs[] = { {1, 0}, /* in */ {0, 0} }; static const struct argument GetDefaultConnectionServiceArgs[] = { {2, 0}, /* out */ {0, 0} }; static const struct action L3FActions[] = { {"SetDefaultConnectionService", SetDefaultConnectionServiceArgs}, /* Req */ {"GetDefaultConnectionService", GetDefaultConnectionServiceArgs}, /* Req */ {0, 0} }; static const struct stateVar L3FVars[] = { {"DefaultConnectionService", 0|0x80, 0, 0, 255}, /* Required */ {0, 0} }; static const struct serviceDesc scpdL3F = { L3FActions, L3FVars }; #endif /* strcat_str() * concatenate the string and use realloc to increase the * memory buffer if needed. */ static char * strcat_str(char * str, int * len, int * tmplen, const char * s2) { int s2len; s2len = (int)strlen(s2); if(*tmplen <= (*len + s2len)) { if(s2len < 256) *tmplen += 256; else *tmplen += s2len + 1; str = (char *)realloc(str, *tmplen); } /*strcpy(str + *len, s2); */ memcpy(str + *len, s2, s2len + 1); *len += s2len; return str; } /* strcat_char() : * concatenate a character and use realloc to increase the * size of the memory buffer if needed */ static char * strcat_char(char * str, int * len, int * tmplen, char c) { if(*tmplen <= (*len + 1)) { *tmplen += 256; str = (char *)realloc(str, *tmplen); } str[*len] = c; (*len)++; return str; } /* iterative subroutine using a small stack * This way, the progam stack usage is kept low */ static char * genXML(char * str, int * len, int * tmplen, const struct XMLElt * p) { unsigned short i, j; unsigned long k; int top; const char * eltname, *s; char c; struct { unsigned short i; unsigned short j; const char * eltname; } pile[16]; /* stack */ top = -1; i = 0; /* current node */ j = 1; /* i + number of nodes*/ for(;;) { eltname = p[i].eltname; if(!eltname) return str; if(eltname[0] == '/') { if(p[i].data && p[i].data[0]) { /*printf("<%s>%s<%s>\n", eltname+1, p[i].data, eltname); */ str = strcat_char(str, len, tmplen, '<'); str = strcat_str(str, len, tmplen, eltname+1); str = strcat_char(str, len, tmplen, '>'); str = strcat_str(str, len, tmplen, p[i].data); str = strcat_char(str, len, tmplen, '<'); str = strcat_str(str, len, tmplen, eltname); str = strcat_char(str, len, tmplen, '>'); } for(;;) { if(top < 0) return str; i = ++(pile[top].i); j = pile[top].j; /*printf(" pile[%d]\t%d %d\n", top, i, j); */ if(i==j) { /*printf("\n", pile[top].eltname); */ str = strcat_char(str, len, tmplen, '<'); str = strcat_char(str, len, tmplen, '/'); s = pile[top].eltname; for(c = *s; c > ' '; c = *(++s)) str = strcat_char(str, len, tmplen, c); str = strcat_char(str, len, tmplen, '>'); top--; } else break; } } else { /*printf("<%s>\n", eltname); */ str = strcat_char(str, len, tmplen, '<'); str = strcat_str(str, len, tmplen, eltname); str = strcat_char(str, len, tmplen, '>'); k = (unsigned long)p[i].data; i = k & 0xffff; j = i + (k >> 16); top++; /*printf(" +pile[%d]\t%d %d\n", top, i, j); */ pile[top].i = i; pile[top].j = j; pile[top].eltname = eltname; } } } /* genRootDesc() : * - Generate the root description of the UPnP device. * - the len argument is used to return the length of * the returned string. * - tmp_uuid argument is used to build the uuid string */ char * genRootDesc(int * len) { char * str; int tmplen; tmplen = 2048; str = (char *)malloc(tmplen); if(str == NULL) return NULL; * len = strlen(xmlver); /*strcpy(str, xmlver); */ memcpy(str, xmlver, *len + 1); str = genXML(str, len, &tmplen, rootDesc); str[*len] = '\0'; return str; } /* genServiceDesc() : * Generate service description with allowed methods and * related variables. */ static char * genServiceDesc(int * len, const struct serviceDesc * s) { int i, j; const struct action * acts; const struct stateVar * vars; const struct argument * args; const char * p; char * str; int tmplen; tmplen = 2048; str = (char *)malloc(tmplen); if(str == NULL) return NULL; /*strcpy(str, xmlver); */ *len = strlen(xmlver); memcpy(str, xmlver, *len + 1); acts = s->actionList; vars = s->serviceStateTable; str = strcat_char(str, len, &tmplen, '<'); str = strcat_str(str, len, &tmplen, root_service); str = strcat_char(str, len, &tmplen, '>'); str = strcat_str(str, len, &tmplen, "10"); i = 0; str = strcat_str(str, len, &tmplen, ""); while(acts[i].name) { str = strcat_str(str, len, &tmplen, ""); str = strcat_str(str, len, &tmplen, acts[i].name); str = strcat_str(str, len, &tmplen, ""); /* argument List */ args = acts[i].args; if(args) { str = strcat_str(str, len, &tmplen, ""); j = 0; while(args[j].dir) { str = strcat_str(str, len, &tmplen, "New"); p = vars[args[j].relatedVar].name; if(0 == memcmp(p, "PortMapping", 11) && 0 != memcmp(p + 11, "Description", 11)) { if(0 == memcmp(p + 11, "NumberOfEntries", 15)) str = strcat_str(str, len, &tmplen, "PortMappingIndex"); else str = strcat_str(str, len, &tmplen, p + 11); } else { str = strcat_str(str, len, &tmplen, p); } str = strcat_str(str, len, &tmplen, ""); str = strcat_str(str, len, &tmplen, args[j].dir==1?"in":"out"); str = strcat_str(str, len, &tmplen, ""); str = strcat_str(str, len, &tmplen, p); str = strcat_str(str, len, &tmplen, ""); j++; } str = strcat_str(str, len, &tmplen,""); } str = strcat_str(str, len, &tmplen, ""); /*str = strcat_char(str, len, &tmplen, '\n'); // TEMP ! */ i++; } str = strcat_str(str, len, &tmplen, ""); i = 0; while(vars[i].name) { str = strcat_str(str, len, &tmplen, ""); str = strcat_str(str, len, &tmplen, vars[i].name); str = strcat_str(str, len, &tmplen, ""); str = strcat_str(str, len, &tmplen, upnptypes[vars[i].itype & 0x0f]); str = strcat_str(str, len, &tmplen, ""); if(vars[i].iallowedlist) { str = strcat_str(str, len, &tmplen, ""); for(j=vars[i].iallowedlist; upnpallowedvalues[j]; j++) { str = strcat_str(str, len, &tmplen, ""); str = strcat_str(str, len, &tmplen, upnpallowedvalues[j]); str = strcat_str(str, len, &tmplen, ""); } str = strcat_str(str, len, &tmplen, ""); } /*if(vars[i].defaultValue) */ if(vars[i].idefault) { str = strcat_str(str, len, &tmplen, ""); /*str = strcat_str(str, len, &tmplen, vars[i].defaultValue); */ str = strcat_str(str, len, &tmplen, upnpdefaultvalues[vars[i].idefault]); str = strcat_str(str, len, &tmplen, ""); } str = strcat_str(str, len, &tmplen, ""); /*str = strcat_char(str, len, &tmplen, '\n'); // TEMP ! */ i++; } str = strcat_str(str, len, &tmplen, ""); str[*len] = '\0'; return str; } /* genWANIPCn() : * Generate the WANIPConnection xml description */ char * genWANIPCn(int * len) { return genServiceDesc(len, &scpdWANIPCn); } /* genWANCfg() : * Generate the WANInterfaceConfig xml description. */ char * genWANCfg(int * len) { return genServiceDesc(len, &scpdWANCfg); } #ifdef ENABLE_L3F_SERVICE char * genL3F(int * len) { return genServiceDesc(len, &scpdL3F); } #endif #ifdef ENABLE_EVENTS static char * genEventVars(int * len, const struct serviceDesc * s, const char * servns) { char tmp[16]; const struct stateVar * v; char * str; int tmplen; tmplen = 512; str = (char *)malloc(tmplen); if(str == NULL) return NULL; *len = 0; v = s->serviceStateTable; str = strcat_str(str, len, &tmplen, ""); while(v->name) { if(v->itype & 0x80) { str = strcat_str(str, len, &tmplen, "name); str = strcat_str(str, len, &tmplen, ">"); //printf("", v->name); switch(v->ieventvalue) { case 0: break; case 253: /* Port mapping number of entries magical value */ snprintf(tmp, sizeof(tmp), "%d", upnp_get_portmapping_number_of_entries()); str = strcat_str(str, len, &tmplen, tmp); break; case 254: /* External ip address magical value */ if(use_ext_ip_addr) str = strcat_str(str, len, &tmplen, use_ext_ip_addr); else { char ext_ip_addr[INET_ADDRSTRLEN]; if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0) { str = strcat_str(str, len, &tmplen, "0.0.0.0"); } else { str = strcat_str(str, len, &tmplen, ext_ip_addr); } } /*str = strcat_str(str, len, &tmplen, "0.0.0.0");*/ break; case 255: /* DefaultConnectionService magical value */ str = strcat_str(str, len, &tmplen, uuidvalue); str = strcat_str(str, len, &tmplen, ":WANConnectionDevice:1,urn:upnp-org:serviceId:WANIPConn1"); //printf("%s:WANConnectionDevice:1,urn:upnp-org:serviceId:WANIPConn1", uuidvalue); break; default: str = strcat_str(str, len, &tmplen, upnpallowedvalues[v->ieventvalue]); //printf("%s", upnpallowedvalues[v->ieventvalue]); } str = strcat_str(str, len, &tmplen, "name); str = strcat_str(str, len, &tmplen, ">"); //printf("\n", v->name); } v++; } str = strcat_str(str, len, &tmplen, ""); //printf("\n"); //printf("\n"); //printf("%d\n", tmplen); str[*len] = '\0'; return str; } char * getVarsWANIPCn(int * l) { return genEventVars(l, &scpdWANIPCn, "urn:schemas-upnp-org:service:WANIPConnection:1"); } char * getVarsWANCfg(int * l) { return genEventVars(l, &scpdWANCfg, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"); } #ifdef ENABLE_L3F_SERVICE char * getVarsL3F(int * l) { return genEventVars(l, &scpdL3F, "urn:schemas-upnp-org:service:Layer3Forwarding:1"); } #endif #endif