Skip to content

Instantly share code, notes, and snippets.

@Darkshadow2
Created August 3, 2021 22:05
Show Gist options
  • Save Darkshadow2/a676844c6036a7beaaad343f56ab0116 to your computer and use it in GitHub Desktop.
Save Darkshadow2/a676844c6036a7beaaad343f56ab0116 to your computer and use it in GitHub Desktop.
miniupnp - patch to get MS clients to see a V2 as a V1 UPNP server
diff --git a/miniupnpd/upnpdescgen.c b/miniupnpd/upnpdescgen.c
index 46110f2..4020a09 100644
--- a/miniupnpd/upnpdescgen.c
+++ b/miniupnpd/upnpdescgen.c
@@ -317,6 +317,147 @@ static const struct XMLElt rootDesc[] =
{0, 0}
};
+#ifdef IGD_V2
+static const struct XMLElt ms_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", UPNP_VERSION_MAJOR_STR},
+ {"/minor", UPNP_VERSION_MINOR_STR},
+/* 5 */
+ {"/deviceType", DEVICE_TYPE_IGD},
+ /* urn:schemas-upnp-org:device:InternetGatewayDevice:1 or 2 */
+#ifdef ENABLE_MANUFACTURER_INFO_CONFIGURATION
+ {"/friendlyName", friendly_name/*ROOTDEV_FRIENDLYNAME*/}, /* required */
+ {"/manufacturer", manufacturer_name/*ROOTDEV_MANUFACTURER*/}, /* required */
+/* 8 */
+ {"/manufacturerURL", manufacturer_url/*ROOTDEV_MANUFACTURERURL*/}, /* optional */
+ {"/modelDescription", model_description/*ROOTDEV_MODELDESCRIPTION*/}, /* recommended */
+ {"/modelName", model_name/*ROOTDEV_MODELNAME*/}, /* required */
+ {"/modelNumber", modelnumber},
+ {"/modelURL", model_url/*ROOTDEV_MODELURL*/},
+#else
+ {"/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},
+#endif
+ {"/serialNumber", serialnumber},
+ {"/UDN", uuidvalue_igd}, /* required */
+ /* see if /UPC is needed. */
+#define MS_SERVICES_OFFSET 58
+#if defined(ENABLE_L3F_SERVICE) || defined(HAS_DUMMY_SERVICE)
+ /* here we defining Services for the root device :
+ * L3F and DUMMY */
+#ifdef ENABLE_L3F_SERVICE
+#define MS_NSERVICES1 1
+#else
+#define MS_NSERVICES1 0
+#endif
+#ifdef HAS_DUMMY_SERVICE
+#define MS_NSERVICES2 1
+#else
+#define MS_NSERVICES2 0
+#endif
+#define MS_NSERVICES (MS_NSERVICES1+MS_NSERVICES2)
+ {"serviceList", INITHELPER(MS_SERVICES_OFFSET,MS_NSERVICES)},
+ {"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", DEVICE_TYPE_WAN}, /* required */
+ /* urn:schemas-upnp-org:device:WANDevice:1 or 2 */
+ {"/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_wan},
+ {"/UPC", WANDEV_UPC}, /* UPC (=12 digit barcode) is optional */
+/* 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 */
+ {"/SCPDURL", WANCFG_PATH},
+ {"/controlURL", WANCFG_CONTROLURL},
+ {"/eventSubURL", WANCFG_EVENTURL},
+/* 38 */
+ {"device", INITHELPER(39,12)},
+/* 39 */
+ {"/deviceType", DEVICE_TYPE_WANC},
+ /* urn:schemas-upnp-org:device:WANConnectionDevice:1 or 2 */
+ {"/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_wcd},
+ {"/UPC", WANCDEV_UPC}, /* UPC (=12 digit Barcode) is optional */
+ {"serviceList", INITHELPER(51,1)},
+/* 51 */
+ {"service", INITHELPER(53,5)},
+ {"service", INITHELPER(58,5)},
+/* 53 */
+ {"/serviceType", SERVICE_TYPE_WANIPC},
+ /* urn:schemas-upnp-org:service:WANIPConnection:2 for v2 */
+ {"/serviceId", SERVICE_ID_WANIPC},
+ /* urn:upnp-org:serviceId:WANIPConn1 or 2 */
+ {"/SCPDURL", WANIPC_PATH},
+ {"/controlURL", WANIPC_CONTROLURL},
+ {"/eventSubURL", WANIPC_EVENTURL},
+/* 58 / 63 = SERVICES_OFFSET*/
+#if defined(HAS_DUMMY_SERVICE) || defined(ENABLE_L3F_SERVICE)
+ {"service", INITHELPER(MS_SERVICES_OFFSET+2,5)},
+ {"service", INITHELPER(MS_SERVICES_OFFSET+7,5)},
+#endif
+#ifdef HAS_DUMMY_SERVICE
+/* 60 / 65 = SERVICES_OFFSET+2 */
+ {"/serviceType", "urn:schemas-dummy-com:service:Dummy:1"},
+ {"/serviceId", "urn:dummy-com:serviceId:dummy1"},
+ {"/SCPDURL", DUMMY_PATH},
+ {"/controlURL", "/dummy"},
+ {"/eventSubURL", "/dummy"},
+#endif
+#ifdef ENABLE_L3F_SERVICE
+/* 60 / 65 = SERVICES_OFFSET+2 */
+ {"/serviceType", "urn:schemas-upnp-org:service:Layer3Forwarding:1"},
+ {"/serviceId", "urn:upnp-org:serviceId:L3Forwarding1"},
+ {"/SCPDURL", L3F_PATH},
+ {"/controlURL", L3F_CONTROLURL}, /* The Layer3Forwarding service is only */
+ {"/eventSubURL", L3F_EVENTURL}, /* recommended, not mandatory */
+#endif
+ {0, 0}
+};
+#endif
+
/* WANIPCn.xml */
/* see UPnP_IGD_WANIPConnection 1.0.pdf
static struct XMLElt scpdWANIPCn[] =
@@ -491,6 +632,31 @@ static const struct action WANIPCnActions[] =
};
/* R=Required, O=Optional */
+#ifdef IGD_V2
+static const struct action ms_WANIPCnActions[] =
+{
+ {"SetConnectionType", SetConnectionTypeArgs}, /* R */
+ {"GetConnectionTypeInfo", GetConnectionTypeInfoArgs}, /* R */
+ {"RequestConnection", 0}, /* R */
+ /*{"RequestTermination", 0},*/ /* O */
+ {"ForceTermination", 0}, /* R */
+ /*{"SetAutoDisconnectTime", 0},*/ /* O */
+ /*{"SetIdleDisconnectTime", 0},*/ /* O */
+ /*{"SetWarnDisconnectDelay", 0}, */ /* O */
+ {"GetStatusInfo", GetStatusInfoArgs}, /* R */
+ /*GetAutoDisconnectTime*/ /* O */
+ /*GetIdleDisconnectTime*/ /* O */
+ /*GetWarnDisconnectDelay*/ /* O */
+ {"GetNATRSIPStatus", GetNATRSIPStatusArgs}, /* R */
+ {"GetGenericPortMappingEntry", GetGenericPortMappingEntryArgs}, /* R */
+ {"GetSpecificPortMappingEntry", GetSpecificPortMappingEntryArgs}, /* R */
+ {"AddPortMapping", AddPortMappingArgs}, /* R */
+ {"DeletePortMapping", DeletePortMappingArgs}, /* R */
+ {"GetExternalIPAddress", GetExternalIPAddressArgs}, /* R */
+ {0, 0}
+};
+#endif
+
/* ignore "warning: missing initializer" */
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
@@ -551,9 +717,56 @@ static const struct stateVar WANIPCnVars[] =
{0, 0}
};
+#ifdef IGD_V2
+static const struct stateVar ms_WANIPCnVars[] =
+{
+ {"ConnectionType", 0, 1, 0, 15}, /* required */
+ {"PossibleConnectionTypes", 0|0x80, 0, 14, 15},
+ /* Required
+ * Allowed values : Unconfigured / IP_Routed / IP_Bridged */
+ {"ConnectionStatus", 0|0x80, 3, 18,
+ CONNECTIONSTATUS_MAGICALVALUE }, /* required */
+ /* Allowed Values : Unconfigured / Connecting(opt) / Connected
+ * PendingDisconnect(opt) / Disconnecting (opt)
+ * Disconnected */
+ {"Uptime", 3, 0}, /* Required */
+ {"LastConnectionError", 0, 6, 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, 4}, /* required */
+ {"NATEnabled", 1, 5}, /* required */
+ {"ExternalIPAddress", 0|0x80, 0, 0,
+ EXTERNALIPADDRESS_MAGICALVALUE}, /* required. Default : empty string */
+ {"PortMappingNumberOfEntries", 2|0x80, 0, 0,
+ PORTMAPPINGNUMBEROFENTRIES_MAGICALVALUE}, /* required >= 0 */
+ {"PortMappingEnabled", 1, 0}, /* Required */
+ /* 10 */
+ {"PortMappingLeaseDuration", 3, 2, 1}, /* required */
+ {"RemoteHost", 0, 0}, /* required. Default : empty string */
+ {"ExternalPort", 2, 0}, /* required */
+ {"InternalPort", 2, 0, 3}, /* required */
+ {"PortMappingProtocol", 0, 0, 11}, /* required allowedValues: TCP/UDP */
+ {"InternalClient", 0, 0}, /* required */
+ {"PortMappingDescription", 0, 0}, /* required default: empty string */
+ {0, 0}
+};
+#endif
+
static const struct serviceDesc scpdWANIPCn =
{ WANIPCnActions, WANIPCnVars };
+#ifdef IGD_V2
+static const struct serviceDesc ms_scpdWANIPCn =
+{ ms_WANIPCnActions, ms_WANIPCnVars };
+#endif
+
/* WANCfg.xml */
/* See UPnP_IGD_WANCommonInterfaceConfig 1.0.pdf */
@@ -887,7 +1100,7 @@ strcat_int(char * str, int * len, int * tmplen, int i)
* This way, the progam stack usage is kept low */
static char *
genXML(char * str, int * len, int * tmplen,
- const struct XMLElt * p)
+ const struct XMLElt * p, int is_ms)
{
#define GENXML_STACK_SIZE 16
unsigned short i, j;
@@ -928,7 +1141,7 @@ genXML(char * str, int * len, int * tmplen,
str = strcat_str(str, len, tmplen, p[i].data);
#ifdef IGD_V2
/* checking a single 'u' saves us 4 strcmp() calls most of the time */
- if (GETFLAG(FORCEIGDDESCV1MASK) && (p[i].data[0] == 'u'))
+ if ((GETFLAG(FORCEIGDDESCV1MASK) || is_ms) && (p[i].data[0] == 'u'))
{
if ((strcmp(p[i].data, DEVICE_TYPE_IGD) == 0) ||
(strcmp(p[i].data, DEVICE_TYPE_WAN) == 0) ||
@@ -936,6 +1149,10 @@ genXML(char * str, int * len, int * tmplen,
(strcmp(p[i].data, SERVICE_TYPE_WANIPC) == 0) )
{
str[*len - 1] = '1'; /* Change the version number to 1 */
+#ifdef DEBUG
+ if ( is_ms )
+ syslog(LOG_DEBUG, "downgraded IGD version for MS client");
+#endif
}
}
#endif
@@ -1007,6 +1224,9 @@ genRootDesc(int * len)
{
char * str;
int tmplen;
+#ifdef IGD_V2
+ int is_ms = *len;
+#endif
tmplen = 2048;
str = (char *)malloc(tmplen);
if(str == NULL)
@@ -1014,7 +1234,12 @@ genRootDesc(int * len)
* len = strlen(xmlver);
/*strcpy(str, xmlver); */
memcpy(str, xmlver, *len + 1);
- str = genXML(str, len, &tmplen, rootDesc);
+#ifdef IGD_V2
+ if ( is_ms )
+ str = genXML(str, len, &tmplen, ms_rootDesc, 1);
+ else
+#endif
+ str = genXML(str, len, &tmplen, rootDesc, 0);
str[*len] = '\0';
return str;
}
@@ -1081,11 +1306,15 @@ genServiceDesc(int * len, const struct serviceDesc * s)
if(plen >= (11+15) && 0 == memcmp(p + 11, "NumberOfEntries", 15)) {
/* PortMappingNumberOfEntries */
#ifdef IGD_V2
+ if(s != &ms_scpdWANIPCn ){
if(0 == memcmp(acts[i].name, "GetListOfPortMappings", 22)) {
str = strcat_str(str, len, &tmplen, "NumberOfPorts");
} else {
str = strcat_str(str, len, &tmplen, "PortMappingIndex");
}
+ } else {
+ str = strcat_str(str, len, &tmplen, "PortMappingIndex");
+ }
#else
str = strcat_str(str, len, &tmplen, "PortMappingIndex");
#endif
@@ -1096,9 +1325,9 @@ genServiceDesc(int * len, const struct serviceDesc * s)
str = strcat_str(str, len, &tmplen, p + 11);
}
#ifdef IGD_V2
- } else if(plen >= 11 && 0 == memcmp(p, "A_ARG_TYPE_", 11)) {
+ } else if((s != &ms_scpdWANIPCn) && plen >= 11 && 0 == memcmp(p, "A_ARG_TYPE_", 11)) {
str = strcat_str(str, len, &tmplen, p + 11);
- } else if(plen >= 13 && 0 == memcmp(p, "ExternalPort", 13)
+ } else if((s != &ms_scpdWANIPCn) && plen >= 13 && 0 == memcmp(p, "ExternalPort", 13)
&& args[j].dir == 2
&& 0 == memcmp(acts[i].name, "AddAnyPortMapping", 18)) {
str = strcat_str(str, len, &tmplen, "ReservedPort");
@@ -1183,6 +1412,10 @@ genServiceDesc(int * len, const struct serviceDesc * s)
char *
genWANIPCn(int * len)
{
+#ifdef IGD_V2
+ if ( *len == 1 )
+ return genServiceDesc(len, &ms_scpdWANIPCn);
+#endif
return genServiceDesc(len, &scpdWANIPCn);
}
diff --git a/miniupnpd/upnphttp.c b/miniupnpd/upnphttp.c
index e40bb7b..39e44d8 100644
--- a/miniupnpd/upnphttp.c
+++ b/miniupnpd/upnphttp.c
@@ -221,6 +221,9 @@ ParseHttpHeaders(struct upnphttp * h)
int n;
if((h->req_buf == NULL) || (h->req_contentoff <= 0))
return;
+#ifdef IGD_V2
+ h->req_is_ms = 0;
+#endif
line = h->req_buf;
while(line < (h->req_buf + h->req_contentoff))
{
@@ -308,6 +311,15 @@ ParseHttpHeaders(struct upnphttp * h)
syslog(LOG_DEBUG, "\"Expect: 100-Continue\" header detected");
}
}
+#ifdef IGD_V2
+ else if(strncasecmp(line, "user-agent:", 11) == 0)
+ {
+ if(strcasestr(line, "microsoft") != NULL) {
+ h->req_is_ms = 1;
+ syslog(LOG_DEBUG, "MS client detected");
+ }
+ }
+#endif
#ifdef ENABLE_EVENTS
else if(strncasecmp(line, "Callback:", 9)==0)
{
@@ -461,7 +473,10 @@ static void
sendXMLdesc(struct upnphttp * h, char * (f)(int *))
{
char * desc;
- int len;
+ int len = 0;
+#ifdef IGD_V2
+ if (h->req_is_ms == 1) len = 1;
+#endif
desc = f(&len);
if(!desc)
{
diff --git a/miniupnpd/upnphttp.h b/miniupnpd/upnphttp.h
index 9d9cf48..881f643 100644
--- a/miniupnpd/upnphttp.h
+++ b/miniupnpd/upnphttp.h
@@ -88,6 +88,9 @@ struct upnphttp {
int res_buflen;
int res_sent;
int res_buf_alloclen;
+#ifdef IGD_V2
+ int req_is_ms;
+#endif
LIST_ENTRY(upnphttp) entries;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment