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) { +}