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 =
+ "\r\n"
+ "\r\n"
+ "\r\n"
+ "\r\n"
+ "\r\n"
+ "\r\n"
+ "\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
+
+
+
+
+
+
+
+
+
+ and its reply
+
+
+
+
+ 213.49.166.224
+
+
+
+
+ */
+
+/*
+ * 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
+
+
+
+
+
+
+ 9876
+ TCP
+ 80
+ 192.168.1.176
+ 1
+ libminiupnpc
+ 0
+
+
+
+
+ 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:
+
+
+
+
+
+
+
+
+ */
+
+ /*
+ * 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
+
+
+
+
+
+
+
+ 9876
+ TCP
+
+
+
+
+ and its reply :
+
+
+
+
+
+
+
+
+ */
+
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) {
+}