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

Dependents:   X10Svr SSDP_Server

Revision:
0:f782e7bc66ad
Child:
1:def15d0b2fae
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SSDP.cpp	Tue Jul 03 00:16:45 2018 +0000
@@ -0,0 +1,210 @@
+//
+// SSDP Server 
+//
+// This is an SSDP server. It relies on a web server running on this same node.
+//
+// 
+#include "SSDP.h"
+#include "EthernetInterface.h"
+
+extern DigitalOut led1;
+
+
+static const char* MCAST_GRP = "239.255.255.250";
+static const int MCAST_PORT = 1900;
+
+
+// sprintf(buffer, SSDP_HTTP, "myIPString", myPort, "myIdentity", "myIdentity");
+// Requires IP address as a string
+static const char * SSDP_HTTP = 
+    "HTTP/1.1 200 OK\r\n"
+    "CACHE-CONTROL: max-age=1800\r\n"
+    "DATE: Mon, 22 Jun 2015 17:24:01 GMT\r\n"
+    "EXT:\r\n"
+    "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"
+    "ST: upnp:rootdevice\r\n"
+    "USN: uuid:Node-1_0-%s::upnp:rootdevice\r\n"                // "Unique Identity"
+    "X-User-Agent: Smartware\r\n"
+    "\r\n";
+
+// Addr:    "###.###.###.###"                   [15]
+// Port:    12345                               [5]
+// Ident:   "#########0#########0#########0"    [30] x 2
+//
+#define SSDP_HTTP_OVERHEAD 50   // Number of bytes to fill in the information
+
+
+// sprintf(buffer, SSDP_NOTIFY, myIPasString, myPort);
+// Requires IP address as a string
+const char * SSDP_NOTIFY = 
+    "NOTIFY * HTTP/1.1\r\n"
+    "HOST: 239.255.255.250:1900\r\n"
+    "CACHE-CONTROL: max-age=1800\r\n"
+    "LOCATION: http://%s:%u/setup.xml\r\n"
+    "NTS: ssdp:alive\r\n\r\n"
+    "";
+
+// Addr:    "###.###.###.###"                   [15]
+// Port:    12345                               [5]
+//
+#define SSDP_NOTIFY_OVERHEAD 20   // Number of bytes to fill in the information
+
+
+static void SSDPListener(void const * args) {
+    UDPSocket server;
+    SSDP_Config_T * cfg = (SSDP_Config_T *)args;
+    
+    server.bind(MCAST_PORT);
+    if (server.join_multicast_group(MCAST_GRP) != 0) {
+        printf("Error joining the multicast group\n");
+        while (true) {}
+    }
+
+    Endpoint client;
+    char buffer[256];
+    while (true) {
+        printf("Wait for packet...\n");
+        int n = server.receiveFrom(client, buffer, sizeof(buffer)-1);
+        buffer[n] = '\0';
+        led1 = true;
+        if (n) {
+            char * p = buffer;
+            volatile int delay = 1;
+            
+            while (*p) {
+                char * e = strstr(p, "\r\n");
+                if (e && (e - buffer) < n) {
+                    *e = '\0';
+                    if (strstr(p, "MX: ")) {
+                        delay = atoi(p + 3);
+                    } else if (strstr(p, "HOST: ")) {
+                        char * pColon = strchr(p+6, ':');
+                        if (pColon) {
+                            pColon = '\0';
+                        }
+                    }
+                    p = e + 1;
+                }
+                p++;
+            }
+            char * out_buffer = (char *)malloc(strlen(SSDP_HTTP) + SSDP_HTTP_OVERHEAD);
+            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
+                // Thread::wait(delay * 1000);
+                server.sendTo(client, out_buffer, strlen(out_buffer));
+                free(out_buffer);
+            }
+        }
+        led1 = false;
+    }
+}
+
+SSDP::SSDP(const char * name, const char * ident, const char * ipAddr, int port) {
+    pThr = NULL;
+    _config.name = NULL;
+    SetFriendlyName(name);
+    _config.ident = NULL;
+    SetFriendlyName(ident);
+    _config.ipAddr = NULL;
+    SetIPAddress(ipAddr);
+    _config.port = port;
+    StartListener();
+    printf("SSDP(......) constructor done. Listener Started.\n");
+    SendNotify();
+}
+
+SSDP::SSDP(const SSDP_Config_T * config) {
+    pThr = NULL;
+    memcpy(&_config, config, sizeof(SSDP_Config_T));
+    StartListener();
+    printf("SSDP() constructor done. Listener Started.\n");
+    SendNotify();
+}
+
+SSDP::~SSDP() {
+    if (pThr)
+        pThr->terminate();
+    pThr = NULL;
+    DelFriendlyName();
+    DelIdentity();
+    DelIPAddress();
+}
+
+void SSDP::SendNotify() {
+    char * out_buffer = (char *)malloc(strlen(SSDP_NOTIFY) + SSDP_NOTIFY_OVERHEAD);
+    if (out_buffer) {
+        UDPSocket sock;
+        sock.init();
+        sock.set_broadcasting();
+        
+        Endpoint broadcast;
+        broadcast.set_address(MCAST_GRP, MCAST_PORT);
+        sprintf(out_buffer, SSDP_NOTIFY, _config.ipAddr, _config.port);
+        sock.sendTo(broadcast, out_buffer, strlen(out_buffer));
+        free(out_buffer);
+    }
+}
+
+bool SSDP::SetFriendlyName(const char * name) {
+    DelFriendlyName();
+    _config.name = (char *)malloc(strlen(name) + 1);
+    if (_config.name) {
+        strcpy(_config.name, name);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void SSDP::DelFriendlyName() {
+    if (_config.name)
+        free(_config.name);
+    _config.name = NULL;
+}
+
+bool SSDP::SetIdentity(const char * ident) {
+    DelIdentity();
+    _config.ident = (char *)malloc(strlen(ident) + 1);
+    if (_config.ident) {
+        strcpy(_config.ident, ident);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void SSDP::DelIdentity() {
+    if (_config.ident)
+        free(_config.ident);
+    _config.ident = NULL;
+}
+
+bool SSDP::SetIPAddress(const char * ipAddr) {
+    DelIPAddress();
+    _config.ipAddr = (char *)malloc(strlen(ipAddr) + 1);
+    if (_config.ipAddr) {
+        strcpy(_config.ipAddr, ipAddr);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void SSDP::DelIPAddress() {
+    if (_config.ipAddr)
+        free(_config.ipAddr);
+    _config.ipAddr = NULL;
+}
+
+bool SSDP::SetPort(int port) {
+    _config.port = port;
+    return true;
+}
+
+void SSDP::StartListener() {
+    pThr = new Thread(SSDPListener, (void *)&_config, osPriorityLow);
+}