From fab6d5bb10fef0a720f5e9094d54fc934c3892b5 Mon Sep 17 00:00:00 2001
From: dannybackx <dannybackx@users.noreply.github.com>
Date: Fri, 14 Apr 2017 18:28:09 +0200
Subject: [PATCH] Add documentation for the calls used, and yet another API.

---
 cmd/cmd.h       |   1 +
 cmd/handlers.c  |   2 +
 esp-link/upnp.c | 184 +++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 176 insertions(+), 11 deletions(-)

diff --git a/cmd/cmd.h b/cmd/cmd.h
index 9eae74a..738a8ad 100644
--- a/cmd/cmd.h
+++ b/cmd/cmd.h
@@ -72,6 +72,7 @@ typedef enum {
   CMD_UPNP_ADD_PORT,
   CMD_UPNP_REMOVE_PORT,
   CMD_UPNP_BEGIN,
+  CMD_UPNP_QUERY_EXTERNAL_ADDRESS
 
 } CmdName;
 
diff --git a/cmd/handlers.c b/cmd/handlers.c
index ca350b2..c5b7aa2 100644
--- a/cmd/handlers.c
+++ b/cmd/handlers.c
@@ -44,6 +44,7 @@ void cmdUPnPScan(CmdPacket *cmd);
 void cmdUPnPAddPort(CmdPacket *cmd);
 void cmdUPnPRemovePort(CmdPacket *cmd);
 void cmdUPnPBegin(CmdPacket *cmd);
+void cmdUPnPQueryExternalAddress(CmdPacket *cmd);
 
 // keep track of last status sent to uC so we can notify it when it changes
 static uint8_t lastWifiStatus = wifiIsDisconnected;
@@ -73,6 +74,7 @@ const CmdList commands[] = {
   {CMD_UPNP_ADD_PORT,		"UPNP_ADD_PORT",	cmdUPnPAddPort},
   {CMD_UPNP_REMOVE_PORT,	"UPNP_REMOVE_PORT",	cmdUPnPRemovePort},
   {CMD_UPNP_BEGIN,		"UPNP_BEGIN",		cmdUPnPBegin},
+  {CMD_UPNP_QUERY_EXTERNAL_ADDRESS, "UPNP_EXT_ADDR",	cmdUPnPQueryExternalAddress},
 
 #ifdef MQTT
   {CMD_MQTT_SETUP,      "MQTT_SETUP",     MQTTCMD_Setup},
diff --git a/esp-link/upnp.c b/esp-link/upnp.c
index 2c32c61..36e92c6 100644
--- a/esp-link/upnp.c
+++ b/esp-link/upnp.c
@@ -18,7 +18,10 @@ enum upnp_state_t {
 };
 static enum upnp_state_t upnp_state;
 
-
+/*
+ * Query #1 : SSDP protocol, this is a UDP multicast to discover a IGD device.
+ * (internet gateway device)
+ */
 static const char *upnp_ssdp_multicast = "239.255.255.250";
 static short upnp_server_port = 1900;
 static const char *ssdp_message = "M-SEARCH * HTTP/1.1\r\n"
@@ -31,6 +34,167 @@ static int ssdp_len;
 static char *control_url = 0;
 static int counter;
 
+/*
+ * Query #2 : query the IGD for its device & service list.
+ * We search this list for the control URL of the router.
+ * The protocol is now UPnP (universal plug and play), HTTP over TCP, usually with SOAP XML
+ * encoding. In this particular query, the query itself is a HTTP GET, no XML.
+ * But its reply is SOAP XML formatted.
+ */
+// BTW, use http/1.0 to avoid responses with transfer-encoding: chunked
+static const char *general_upnp_query = "GET %s HTTP/1.0\r\n"
+		   "Host: %s\r\n"
+                   "Connection: close\r\n"
+                   "User-Agent: esp-link\r\n\r\n";
+
+/*
+ * Subsequent queries aren't necessarily in order
+ *
+ * This one queries the IGD for its external IP address.
+ */
+static const char *upnp_query_external_address = "POST %s HTTP/1.0\r\n"	// control-url
+	"Host: %s\r\n"							// host ip+port
+	"User-Agent: esp-link\r\n"
+	"Content-Length : %d\r\n"					// XML length
+	"Content-Type: text/xml\r\n"
+	"SOAPAction: \"urn:schemas-upnp-org:service:WANPPPConnection:1#GetExternalIPAddress\"\r\n"
+	"Connection: Close\r\n"
+	"Cache-Control: no-cache\r\n"
+	"Pragma: no-cache\r\n"
+	"\r\n";
+
+static const char *upnp_query_external_address_xml =
+	"<?xml version=\"1.0\"?>\r\n"
+	"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+	"<s:Body>\r\n"
+	"<u:GetExternalIPAddress xmlns:u=\"urn:schemas-upnp-org:service:WANPPPConnection:1\">\r\n"
+	"</u:GetExternalIPAddress>\r\n"
+	"</s:Body>\r\n"
+	"</s:Envelope>\r\n";
+  /*
+    Sample query
+
+	POST /o8ee3npj36j/IGD/upnp/control/igd/wanpppc_1_1_1 HTTP/1.1
+	Host: 192.168.1.1:8000
+	User-Agent: Ubuntu/16.04, UPnP/1.1, MiniUPnPc/2.0
+	Content-Length: 286
+	Content-Type: text/xml
+	SOAPAction: "urn:schemas-upnp-org:service:WANPPPConnection:1#GetExternalIPAddress"
+	Connection: Close
+	Cache-Control: no-cache
+	Pragma: no-cache
+
+	<?xml version="1.0"?>
+	<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
+	    s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+	  <s:Body>
+	     <u:GetExternalIPAddress xmlns:u="urn:schemas-upnp-org:service:WANPPPConnection:1">
+	     </u:GetExternalIPAddress>
+	  </s:Body>
+	</s:Envelope>
+
+    and its reply
+	<?xml version="1.0"?>
+	<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
+	    s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+	  <s:Body>
+	    <m:GetExternalIPAddressResponse
+	        xmlns:m="urn:schemas-upnp-org:service:WANPPPConnection:1">
+	      <NewExternalIPAddress>213.49.166.224</NewExternalIPAddress>
+	    </m:GetExternalIPAddressResponse>
+	  </s:Body>
+	</s:Envelope>
+
+  */
+
+/*
+ * This query adds a port mapping
+
+	POST /o8ee3npj36j/IGD/upnp/control/igd/wanpppc_1_1_1 HTTP/1.1
+	Host: 192.168.1.1:8000
+	User-Agent: Ubuntu/16.04, UPnP/1.1, MiniUPnPc/2.0
+	Content-Length: 594
+	Content-Type: text/xml
+	SOAPAction: "urn:schemas-upnp-org:service:WANPPPConnection:1#AddPortMapping"
+	Connection: Close
+	Cache-Control: no-cache
+	Pragma: no-cache
+
+	<?xml version="1.0"?>
+	<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
+	    s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+	  <s:Body>
+	    <u:AddPortMapping xmlns:u="urn:schemas-upnp-org:service:WANPPPConnection:1">
+	      <NewRemoteHost></NewRemoteHost>
+	      <NewExternalPort>9876</NewExternalPort>
+	      <NewProtocol>TCP</NewProtocol>
+	      <NewInternalPort>80</NewInternalPort>
+	      <NewInternalClient>192.168.1.176</NewInternalClient>
+	      <NewEnabled>1</NewEnabled>
+	      <NewPortMappingDescription>libminiupnpc</NewPortMappingDescription>
+	      <NewLeaseDuration>0</NewLeaseDuration>
+	    </u:AddPortMapping>
+	  </s:Body>
+	</s:Envelope>
+
+    and the reply
+
+	HTTP/1.0 200 OK
+	Connection: close
+	Date: Sat, 25 Mar 2017 16:54:41 GMT
+	Server: MediaAccess TG 789Ovn Xtream 10.A.0.I UPnP/1.0 (9C-97-26-26-44-DE)
+	Content-Length: 300
+	Content-Type: text/xml; charset="utf-8"
+	EXT:
+
+	<?xml version="1.0"?>
+	<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
+	    s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+	  <s:Body>
+	<m:AddPortMappingResponse xmlns:m="urn:schemas-upnp-org:service:WANPPPConnection:1">
+	</m:AddPortMappingResponse>
+	</s:Body>
+	</s:Envelope>
+ */
+
+ /*
+  * Query to delete a port mapping
+
+	POST /o8ee3npj36j/IGD/upnp/control/igd/wanpppc_1_1_1 HTTP/1.1
+	Host: 192.168.1.1:8000
+	User-Agent: Ubuntu/16.04, UPnP/1.1, MiniUPnPc/2.0
+	Content-Length: 380
+	Content-Type: text/xml
+	SOAPAction: "urn:schemas-upnp-org:service:WANPPPConnection:1#DeletePortMapping"
+	Connection: Close
+	Cache-Control: no-cache
+	Pragma: no-cache
+
+	<?xml version="1.0"?>
+	<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
+	    s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+	  <s:Body>
+	    <u:DeletePortMapping xmlns:u="urn:schemas-upnp-org:service:WANPPPConnection:1">
+	    <NewRemoteHost>
+	    </NewRemoteHost>
+	    <NewExternalPort>9876</NewExternalPort>
+	    <NewProtocol>TCP</NewProtocol>
+	  </u:DeletePortMapping>
+	  </s:Body>
+	</s:Envelope>
+
+    and its reply :
+
+	<?xml version="1.0"?>
+	<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
+	    s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+	  <s:Body>
+	    <m:DeletePortMappingResponse xmlns:m="urn:schemas-upnp-org:service:WANPPPConnection:1">
+	    </m:DeletePortMappingResponse>
+	  </s:Body>
+	</s:Envelope>
+ */
+
 typedef struct {
   char			*host, *path, *location;
   uint32_t		port, remote_port;
@@ -105,12 +269,6 @@ ssdp_sent_cb(void *arg) {
 #endif
 }
 
-// BTW, use http/1.0 to avoid responses with transfer-encoding: chunked
-const char *http_tmpl1 = "GET %s HTTP/1.0\r\n"
-		   "Host: %s\r\n"
-                   "Connection: close\r\n"
-                   "User-Agent: esp-link\r\n\r\n";
-
 /*
  * This should be a generic function that performs a query via TCP.
  */
@@ -137,8 +295,8 @@ static void ICACHE_FLASH_ATTR upnp_query_igd(UPnPClient *client) {
      * os_sprintf(query, tmpl, "/o8ee3npj36j/IGD/upnp/IGD.xml", "http://192.168.1.1:8000");
      * upnp_query_igd(query);
      */
-    query = (char *)os_malloc(strlen(http_tmpl1) + strlen(client->path) + strlen(client->location));
-    os_sprintf(query, http_tmpl1, client->path, client->location);
+    query = (char *)os_malloc(strlen(general_upnp_query) + strlen(client->path) + strlen(client->location));
+    os_sprintf(query, general_upnp_query, client->path, client->location);
     break;
   default:
     os_printf("upnp_query_igd: untreated state %d\n", (int)upnp_state);
@@ -474,8 +632,8 @@ cmdUPnPAddPort(CmdPacket *cmd) {
   client->con->type = ESPCONN_TCP;
   client->con->state = ESPCONN_NONE;
 
-  char *query = (char *)os_malloc(strlen(http_tmpl1) + strlen(client->path) + strlen(client->location));
-  os_sprintf(query, http_tmpl1, client->path, client->location);
+  char *query = (char *)os_malloc(strlen(general_upnp_query) + strlen(client->path) + strlen(client->location));
+  os_sprintf(query, general_upnp_query, client->path, client->location);
   client->data = query;
   client->data_len = strlen(query);
 
@@ -581,3 +739,7 @@ upnp_analyze_location(UPnPClient *client, char *orig_loc, int len) {
   client->host = os_malloc(strlen(client->location + 7) + 1);
   strcpy(client->host, client->location+7);
 }
+
+void ICACHE_FLASH_ATTR
+cmdUPnPQueryExternalAddress(CmdPacket *cmd) {
+}