SSDP Server - working version provides SSDP based network discovery, and with a companion web server, may provide other functionalities.
Dependents: X10Svr SSDP_Server
Diff: SSDP.cpp
- Revision:
- 10:26f5a66f05a4
- Parent:
- 9:9b46a499de53
- Child:
- 11:b7f8070014d8
--- a/SSDP.cpp Tue Feb 26 23:07:15 2019 +0000 +++ b/SSDP.cpp Sun Mar 03 20:25:10 2019 +0000 @@ -8,6 +8,14 @@ #include "EthernetInterface.h" #include "SW_String.h" +// Normal discovery +// ST: upnp:rootdevice +// ST: ssdp:all +// Some devices query directly for a Belkin device (like Amazon dot) +// ST: urn:Belkin:device:** +// define the following to support that discovery +#define ST_DISCOVER_BELKIN_DEVICE + //#define DEBUG "SSDP" //Debug is disabled by default #include <cstdio> @@ -36,7 +44,7 @@ "LOCATION: http://%s:%d/setup.xml\r\n" // "my.ip.string", portNum "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" "01-NLS: %s\r\n" // "Unique Identity" - "SERVER: Smartware, UPnP/1.0, Smartware\r\n" + "SERVER: Smartware, UPnP/2.0, Smartware\r\n" "ST: upnp:rootdevice\r\n" "USN: uuid:Node-1_0-%s::upnp:rootdevice\r\n" // "Unique Identity" "X-User-Agent: Smartware\r\n" @@ -49,20 +57,22 @@ #define SSDP_HTTP_OVERHEAD 50 // Number of bytes to fill in the information -// sprintf(buffer, SSDP_NOTIFY, myIPasString, myPort); +// sprintf(buffer, SSDP_NOTIFY_ALIVE, myIPasString, myPort); // Requires IP address as a string -static const char * SSDP_NOTIFY = +static const char * SSDP_NOTIFY_ALIVE = "NOTIFY * HTTP/1.1\r\n" "HOST: 239.255.255.250:1900\r\n" - "CACHE-CONTROL: max-age=1800\r\n" + "CACHE-CONTROL: max-age=86400\r\n" "LOCATION: http://%s:%u/setup.xml\r\n" + "NT: upnp:rootdevice\r\n" "NTS: ssdp:alive\r\n\r\n" + "SERVER: Smartware, UPnP/2.0, Smartware\r\n" ""; // Addr: "###.###.###.###" [15] // Port: 12345 [5] // -#define SSDP_NOTIFY_OVERHEAD 25 // Number of bytes to fill in the information (+5) +#define SSDP_NOTIFY_ALIVE_OVERHEAD 25 // Number of bytes to fill in the information (+5) // The SSDP listener thread @@ -106,6 +116,10 @@ mask |= 0x08; } else if (sw_stristr(p, "ST:") && sw_stristr(p, "ssdp:all")) { mask |= 0x08; + #ifdef ST_DISCOVER_BELKIN_DEVICE + } else if (sw_stristr(p, "ST:") && sw_stristr(p, "urn:Belkin:device:**")) { + mask |= 0x08; + #endif } else if (sw_stristr(p, "HOST: ")) { mask |= 0x10; // HOST: 239.255.255.250:49152 char * pColon = strchr(p+6, ':'); @@ -118,20 +132,19 @@ } p++; } - INFO(" ***** %02X", mask); if ((mask & 0x1F) == 0x1F) { char * out_buffer = (char *)malloc(strlen(SSDP_HTTP) + SSDP_HTTP_OVERHEAD); + INFO("\r\n\r\n ***** %02X", mask); if (out_buffer) { sprintf(out_buffer, SSDP_HTTP, cfg->ipAddr, cfg->port, cfg->ident, cfg->ident); - // It would be polite to delay, but the recommendation is from 1 to 2 seconds. - // Send the response twice, to improve reliability of node discovery - //for (int i=0; i<1; i++) { - INFO("SSDPListener: reply >>>>>>> %s:%d >>>>>>>>>>>", client.get_address(), - client.get_port()); - int i = server.sendTo(client, out_buffer, strlen(out_buffer)); - INFO(" sendTo %3d: reply <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", i); - //} + // It would be polite to delay, but the recommendation is from 1 to 5 seconds, + // and that will stall this thread. + INFO("SSDPListener: reply >>>>>>> %s:%d >>>>>>>>>>>", client.get_address(), + client.get_port()); + INFO("\r\n%s", out_buffer); + int i = server.sendTo(client, out_buffer, strlen(out_buffer)); + INFO(" sendTo %3d: reply <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", i); free(out_buffer); INFO("SSDPListener: stack-used: %d, total: %d", pThr->max_stack(), pThr->stack_size()); } else { @@ -173,12 +186,14 @@ DelIPAddress(); } -void SSDP::SendNotify() { - char * out_buffer = (char *)malloc(strlen(SSDP_NOTIFY) + SSDP_NOTIFY_OVERHEAD); +void SSDP::SendNotify(NotifyType_t nt) { + char * out_buffer = (char *)malloc(strlen(SSDP_NOTIFY_ALIVE) + SSDP_NOTIFY_ALIVE_OVERHEAD); + (void)nt; + if (out_buffer) { UDPSocket sock; Endpoint broadcast; - int i; + volatile int i; i = sock.init(); INFO(" %d = sock.init()", i); @@ -186,8 +201,8 @@ INFO(" %d = sock.set_broadcasting()", i); i = broadcast.set_address(MCAST_GRP, MCAST_PORT); INFO(" %d = sock.set_address(%s,%d)", i, MCAST_GRP, MCAST_PORT); - sprintf(out_buffer, SSDP_NOTIFY, _config.ipAddr, _config.port); - INFO("SendNotify:\n%s", out_buffer); + sprintf(out_buffer, SSDP_NOTIFY_ALIVE, _config.ipAddr, _config.port); + printf("SendNotify:\n%s", out_buffer); i = sock.sendTo(broadcast, out_buffer, strlen(out_buffer)); INFO(" %d = sendTo(%s, ..., %d)", i, broadcast.get_address(), strlen(out_buffer)); free(out_buffer); @@ -253,3 +268,48 @@ void SSDP::StartListener() { pThr = new Thread(SSDPListener, (void *)&_config, osPriorityLow, 768); } + + +#if 0 // simple UUID generator is needed +char GUID[40]; + +// srand (clock()); +// UUIDGenerator(GUID, sizeof(GUID)); +// printf ("%s\r\n", GUID); + +/// Simple UUID Generator. +/// +/// This is not claimed to be particularly good, but it does generally meet +/// the minimum requirements for a valid UUID. +/// +/// @param UUID is a pointer to a buffer into which the UUID will be written. +/// @param UUID_size is that size of that buffer, and is used to ensure the +/// buffer is large enough. +/// @returns true if a UUID was successfully generated into the provided buffer. +/// +bool UUIDGenerator(char * UUID, size_t UUID_size) { + int t = 0; + char *szTemp = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"; + char *szHex = "0123456789ABCDEF-"; + int nLen = strlen (szTemp); + + if (UUID_size < 40) + return false; + + for (t=0; t<nLen+1; t++) + { + int r = rand () % 16; + char c = ' '; + + switch (szTemp[t]) + { + case 'x' : { c = szHex [r]; } break; + case 'y' : { c = szHex [r & 0x03 | 0x08]; } break; + case '-' : { c = '-'; } break; + case '4' : { c = '4'; } break; + } + UUID[t] = ( t < nLen ) ? c : 0x00; + } + return true; +} +#endif