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

Dependents:   X10Svr SSDP_Server

Files at this revision

API Documentation at this revision

Comitter:
WiredHome
Date:
Tue Jul 03 00:16:45 2018 +0000
Child:
1:def15d0b2fae
Commit message:
Initial working version that seems to run well (only tested with a near zero-load main( ), which hosted only the web server.

Changed in this revision

SSDP.cpp Show annotated file Show diff for this revision Revisions of this file
SSDP.h Show annotated file Show diff for this revision Revisions of this file
--- /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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SSDP.h	Tue Jul 03 00:16:45 2018 +0000
@@ -0,0 +1,126 @@
+//
+//
+//
+
+#include "mbed.h"
+#include "rtos.h"
+
+
+/// Configuration data for the SSDP server
+///
+typedef struct {
+    char * name;        ///< pointer to the friendly name, storage managed here
+    char * ident;       ///< pointer to a unique identity number, like a mac address
+    char * ipAddr;      ///< pointer to the node IP address
+    int port;           ///< port number of the local server
+} SSDP_Config_T;
+
+
+///
+/// SSDP Server
+/// 
+/// This file documents the SSDP Server, which can be implanted into a node
+/// so that it is discoverable on Ethernet, and in Windows Network Explorer
+/// view.
+///
+/// Many basics are defined in order to satisfy the SSDP discovery process.
+/// - the hosting node has a web server running, which will serve various responses.
+/// - the hosting node shall provide a /setup.xml which satisfies the SSDP requirements.
+/// - depending on the application, many additional files may need to be supported.
+/// 
+/// @code
+///    HTTPServer svr(Server_Port, Server_Root, 15, 30, 20, 50, &pc);
+///    svr.RegisterHandler("/", RootPage);
+///    svr.RegisterHandler("/setup.xml", Setup_xml);
+///    SSDP ssdp("Friendly Node", eth.getMACAddress(), eth.getIPAddress(), Server_Port);
+///    
+///    while (1) {
+///        led4 = !led4;
+///        svr.Poll();     // non-blocking, but also not deterministic
+///        Thread::yield();
+///        if (criteria)
+///           ssdp.SetFriendlyName("New Node Name");
+///    }
+/// @endcode
+///
+class SSDP {
+public:
+    /// Constructor for the SSDP server
+    ///
+    /// @param[in] name is a pointer to a string containing the name.
+    /// @param[in] ident is a pointer to an identity string of this node, e.g. a mac address.
+    /// @param[in] ipAddr is a pointer to an IP Address string of this node.
+    /// @param[in] port is an integer port number for the local web server.
+    ///
+    SSDP(const char * name, const char * ident, const char * ipAddr, int port);
+    
+    /// Constructor for the SSDP server, as an alternate to the parameter version.
+    ///
+    /// @param[in] config is a pointer to configuration structure which contains
+    ///             the individual elements;
+    ///             - pointer to the const char * friendly name 
+    ///             - pointer to the const char * identity
+    ///             - pointer to the const char * IP Address of this node
+    ///             - port number for the web server 
+    ///
+    SSDP(const SSDP_Config_T * config);
+    
+    /// Destructor
+    ///
+    ~SSDP();
+    
+    /// Set the friendly name for this node
+    ///
+    /// @param[in] name is a pointer to a string containing the name.
+    /// @returns true if the name was set (memory was available).
+    ///
+    bool SetFriendlyName(const char * name);
+
+    /// Delete the friendly name that was assigned.
+    ///
+    /// This frees the memory that was previously allocated.
+    ///
+    void DelFriendlyName();
+
+    /// Set the identity for this node
+    ///
+    /// @param[in] ident is a pointer to a string containing the identity.
+    /// @returns true if the name was set (memory was available).
+    ///
+    bool SetIdentity(const char * ident);
+
+    /// Delete the identity that was assigned.
+    ///
+    /// This frees the memory that was previously allocated.
+    ///
+    void DelIdentity();
+
+    /// Set the IP Address for this node
+    ///
+    /// @param[in] ipAddr is a pointer to a string containing the ipAddress.
+    /// @returns true if the IP Address was set (memory was available).
+    ///
+    bool SetIPAddress(const char * ipAddr);
+
+    /// Delete the friendly IP Address that was assigned.
+    ///
+    /// This frees the memory that was previously allocated.
+    ///
+    void DelIPAddress();
+
+    /// Set the Port
+    ///
+    /// @param[in] port is the port number of the local web server.
+    /// @returns true if the port number was set.
+    ///
+    bool SetPort(int port);
+    
+private:
+
+    void StartListener();
+    void SendNotify();
+    
+    SSDP_Config_T _config;       ///< the configuration
+
+    Thread * pThr;
+};
\ No newline at end of file