SSDP Server - working version provides SSDP based network discovery, and with a companion web server, may provide other functionalities.

Dependents:   X10Svr SSDP_Server

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