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

Dependents:   X10Svr SSDP_Server

Committer:
WiredHome
Date:
Sun Nov 18 04:01:58 2018 +0000
Revision:
6:9df748509c3d
Parent:
5:199656d96c72
Child:
7:1e8c677e3d28
Minor code cleanup

Who changed what in which revision?

UserRevisionLine numberNew contents of line
WiredHome 0:f782e7bc66ad 1 //
WiredHome 0:f782e7bc66ad 2 // SSDP Server
WiredHome 0:f782e7bc66ad 3 //
WiredHome 0:f782e7bc66ad 4 // This is an SSDP server. It relies on a web server running on this same node.
WiredHome 0:f782e7bc66ad 5 //
WiredHome 0:f782e7bc66ad 6 //
WiredHome 0:f782e7bc66ad 7 #include "SSDP.h"
WiredHome 0:f782e7bc66ad 8 #include "EthernetInterface.h"
WiredHome 2:3d6d70556fca 9 #include "SW_String.h"
WiredHome 0:f782e7bc66ad 10
WiredHome 6:9df748509c3d 11 #define DEBUG "SSDP" //Debug is disabled by default
WiredHome 0:f782e7bc66ad 12
WiredHome 1:def15d0b2fae 13 #include <cstdio>
WiredHome 1:def15d0b2fae 14 #if (defined(DEBUG) && !defined(TARGET_LPC11U24))
WiredHome 6:9df748509c3d 15 #define DBG(x, ...) std::printf("[DBG %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 6:9df748509c3d 16 #define WARN(x, ...) std::printf("[WRN %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 6:9df748509c3d 17 #define ERR(x, ...) std::printf("[ERR %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 6:9df748509c3d 18 #define INFO(x, ...) std::printf("[INF %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 1:def15d0b2fae 19 #else
WiredHome 1:def15d0b2fae 20 #define DBG(x, ...)
WiredHome 1:def15d0b2fae 21 #define WARN(x, ...)
WiredHome 1:def15d0b2fae 22 #define ERR(x, ...)
WiredHome 1:def15d0b2fae 23 #define INFO(x, ...)
WiredHome 1:def15d0b2fae 24 #endif
WiredHome 6:9df748509c3d 25 extern void ShowSignOfLife(int which);
WiredHome 0:f782e7bc66ad 26 static const char* MCAST_GRP = "239.255.255.250";
WiredHome 0:f782e7bc66ad 27 static const int MCAST_PORT = 1900;
WiredHome 2:3d6d70556fca 28 static Thread * pThr;
WiredHome 0:f782e7bc66ad 29
WiredHome 0:f782e7bc66ad 30 // sprintf(buffer, SSDP_HTTP, "myIPString", myPort, "myIdentity", "myIdentity");
WiredHome 0:f782e7bc66ad 31 // Requires IP address as a string
WiredHome 0:f782e7bc66ad 32 static const char * SSDP_HTTP =
WiredHome 0:f782e7bc66ad 33 "HTTP/1.1 200 OK\r\n"
WiredHome 0:f782e7bc66ad 34 "CACHE-CONTROL: max-age=1800\r\n"
WiredHome 0:f782e7bc66ad 35 "DATE: Mon, 22 Jun 2015 17:24:01 GMT\r\n"
WiredHome 0:f782e7bc66ad 36 "EXT:\r\n"
WiredHome 0:f782e7bc66ad 37 "LOCATION: http://%s:%d/setup.xml\r\n" // "my.ip.string", portNum
WiredHome 0:f782e7bc66ad 38 "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
WiredHome 0:f782e7bc66ad 39 "01-NLS: %s\r\n" // "Unique Identity"
WiredHome 0:f782e7bc66ad 40 "SERVER: Smartware, UPnP/1.0, Smartware\r\n"
WiredHome 0:f782e7bc66ad 41 "ST: upnp:rootdevice\r\n"
WiredHome 0:f782e7bc66ad 42 "USN: uuid:Node-1_0-%s::upnp:rootdevice\r\n" // "Unique Identity"
WiredHome 0:f782e7bc66ad 43 "X-User-Agent: Smartware\r\n"
WiredHome 0:f782e7bc66ad 44 "\r\n";
WiredHome 0:f782e7bc66ad 45
WiredHome 0:f782e7bc66ad 46 // Addr: "###.###.###.###" [15]
WiredHome 0:f782e7bc66ad 47 // Port: 12345 [5]
WiredHome 0:f782e7bc66ad 48 // Ident: "#########0#########0#########0" [30] x 2
WiredHome 0:f782e7bc66ad 49 //
WiredHome 0:f782e7bc66ad 50 #define SSDP_HTTP_OVERHEAD 50 // Number of bytes to fill in the information
WiredHome 0:f782e7bc66ad 51
WiredHome 0:f782e7bc66ad 52
WiredHome 0:f782e7bc66ad 53 // sprintf(buffer, SSDP_NOTIFY, myIPasString, myPort);
WiredHome 0:f782e7bc66ad 54 // Requires IP address as a string
WiredHome 1:def15d0b2fae 55 static const char * SSDP_NOTIFY =
WiredHome 0:f782e7bc66ad 56 "NOTIFY * HTTP/1.1\r\n"
WiredHome 0:f782e7bc66ad 57 "HOST: 239.255.255.250:1900\r\n"
WiredHome 0:f782e7bc66ad 58 "CACHE-CONTROL: max-age=1800\r\n"
WiredHome 0:f782e7bc66ad 59 "LOCATION: http://%s:%u/setup.xml\r\n"
WiredHome 0:f782e7bc66ad 60 "NTS: ssdp:alive\r\n\r\n"
WiredHome 0:f782e7bc66ad 61 "";
WiredHome 0:f782e7bc66ad 62
WiredHome 0:f782e7bc66ad 63 // Addr: "###.###.###.###" [15]
WiredHome 0:f782e7bc66ad 64 // Port: 12345 [5]
WiredHome 0:f782e7bc66ad 65 //
WiredHome 5:199656d96c72 66 #define SSDP_NOTIFY_OVERHEAD 25 // Number of bytes to fill in the information (+5)
WiredHome 0:f782e7bc66ad 67
WiredHome 0:f782e7bc66ad 68
WiredHome 1:def15d0b2fae 69 // The SSDP listener thread
WiredHome 0:f782e7bc66ad 70 static void SSDPListener(void const * args) {
WiredHome 0:f782e7bc66ad 71 UDPSocket server;
WiredHome 0:f782e7bc66ad 72 SSDP_Config_T * cfg = (SSDP_Config_T *)args;
WiredHome 0:f782e7bc66ad 73
WiredHome 0:f782e7bc66ad 74 server.bind(MCAST_PORT);
WiredHome 0:f782e7bc66ad 75 if (server.join_multicast_group(MCAST_GRP) != 0) {
WiredHome 1:def15d0b2fae 76 ERR("Error joining the multicast group");
WiredHome 0:f782e7bc66ad 77 while (true) {}
WiredHome 0:f782e7bc66ad 78 }
WiredHome 6:9df748509c3d 79 server.set_blocking(false, 50); // non-blocking with 50ms timeout
WiredHome 0:f782e7bc66ad 80 Endpoint client;
WiredHome 0:f782e7bc66ad 81 char buffer[256];
WiredHome 0:f782e7bc66ad 82 while (true) {
WiredHome 2:3d6d70556fca 83 //INFO("Wait for packet...");
WiredHome 6:9df748509c3d 84 ShowSignOfLife(2);
WiredHome 0:f782e7bc66ad 85 int n = server.receiveFrom(client, buffer, sizeof(buffer)-1);
WiredHome 6:9df748509c3d 86 if (n > 0) {
WiredHome 6:9df748509c3d 87 buffer[n] = '\0';
WiredHome 6:9df748509c3d 88 }
WiredHome 0:f782e7bc66ad 89 if (n) {
WiredHome 0:f782e7bc66ad 90 char * p = buffer;
WiredHome 2:3d6d70556fca 91 volatile int delay = 0;
WiredHome 2:3d6d70556fca 92 uint8_t mask = 0x00; // fragments we found in the received packet
WiredHome 0:f782e7bc66ad 93
WiredHome 6:9df748509c3d 94 INFO("SSDP receiveFrom %d bytes from %s:%d", n,
WiredHome 6:9df748509c3d 95 client.get_address(), client.get_port());
WiredHome 6:9df748509c3d 96 INFO("SSDP\n%s", buffer);
WiredHome 0:f782e7bc66ad 97 while (*p) {
WiredHome 0:f782e7bc66ad 98 char * e = strstr(p, "\r\n");
WiredHome 0:f782e7bc66ad 99 if (e && (e - buffer) < n) {
WiredHome 0:f782e7bc66ad 100 *e = '\0';
WiredHome 2:3d6d70556fca 101 if (sw_stristr(p, "M-SEARCH * HTTP/1.1")) {
WiredHome 2:3d6d70556fca 102 mask |= 0x01; // M-SEARCH * HTTP/1.1
WiredHome 2:3d6d70556fca 103 } else if (sw_stristr(p, "MAN:") && sw_stristr(p,"\"ssdp:discover\"")) {
WiredHome 2:3d6d70556fca 104 mask |= 0x02; // MAN: "ssdp:discover"
WiredHome 2:3d6d70556fca 105 } else if (sw_stristr(p, "MX:")) {
WiredHome 2:3d6d70556fca 106 mask |= 0x04; // MX: \d
WiredHome 0:f782e7bc66ad 107 delay = atoi(p + 3);
WiredHome 2:3d6d70556fca 108 } else if (sw_stristr(p, "ST:") && sw_stristr(p, "upnp:rootdevice")) {
WiredHome 2:3d6d70556fca 109 mask |= 0x08;
WiredHome 2:3d6d70556fca 110 } else if (sw_stristr(p, "HOST: ")) {
WiredHome 2:3d6d70556fca 111 mask |= 0x10; // HOST: 239.255.255.250:49152
WiredHome 0:f782e7bc66ad 112 char * pColon = strchr(p+6, ':');
WiredHome 0:f782e7bc66ad 113 if (pColon) {
WiredHome 0:f782e7bc66ad 114 pColon = '\0';
WiredHome 0:f782e7bc66ad 115 }
WiredHome 0:f782e7bc66ad 116 }
WiredHome 0:f782e7bc66ad 117 p = e + 1;
WiredHome 0:f782e7bc66ad 118 }
WiredHome 0:f782e7bc66ad 119 p++;
WiredHome 0:f782e7bc66ad 120 }
WiredHome 6:9df748509c3d 121 INFO(" ***** %02X", mask);
WiredHome 2:3d6d70556fca 122 if ((mask & 0x1F) == 0x1F) {
WiredHome 2:3d6d70556fca 123 char * out_buffer = (char *)malloc(strlen(SSDP_HTTP) + SSDP_HTTP_OVERHEAD);
WiredHome 2:3d6d70556fca 124
WiredHome 2:3d6d70556fca 125 if (out_buffer) {
WiredHome 2:3d6d70556fca 126 sprintf(out_buffer, SSDP_HTTP, cfg->ipAddr, cfg->port, cfg->ident, cfg->ident);
WiredHome 2:3d6d70556fca 127 // It would be polite to delay, but the recommendation is from 1 to 2 seconds.
WiredHome 2:3d6d70556fca 128 // Send the response twice, to improve reliability of node discovery
WiredHome 6:9df748509c3d 129 for (int i=0; i<1; i++) {
WiredHome 6:9df748509c3d 130 INFO("SSDPListener: reply >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
WiredHome 6:9df748509c3d 131 int i = server.sendTo(client, out_buffer, strlen(out_buffer));
WiredHome 6:9df748509c3d 132 INFO(" sendTo %3d: reply <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", i);
WiredHome 2:3d6d70556fca 133 }
WiredHome 2:3d6d70556fca 134 free(out_buffer);
WiredHome 2:3d6d70556fca 135 INFO("SSDPListener: stack-used: %d, total: %d", pThr->max_stack(), pThr->stack_size());
WiredHome 2:3d6d70556fca 136 } else {
WiredHome 2:3d6d70556fca 137 ERR("Can't get memory for response");
WiredHome 2:3d6d70556fca 138 }
WiredHome 0:f782e7bc66ad 139 }
WiredHome 0:f782e7bc66ad 140 }
WiredHome 0:f782e7bc66ad 141 }
WiredHome 0:f782e7bc66ad 142 }
WiredHome 0:f782e7bc66ad 143
WiredHome 0:f782e7bc66ad 144 SSDP::SSDP(const char * name, const char * ident, const char * ipAddr, int port) {
WiredHome 0:f782e7bc66ad 145 pThr = NULL;
WiredHome 0:f782e7bc66ad 146 _config.name = NULL;
WiredHome 0:f782e7bc66ad 147 SetFriendlyName(name);
WiredHome 0:f782e7bc66ad 148 _config.ident = NULL;
WiredHome 0:f782e7bc66ad 149 SetFriendlyName(ident);
WiredHome 0:f782e7bc66ad 150 _config.ipAddr = NULL;
WiredHome 0:f782e7bc66ad 151 SetIPAddress(ipAddr);
WiredHome 0:f782e7bc66ad 152 _config.port = port;
WiredHome 0:f782e7bc66ad 153 StartListener();
WiredHome 1:def15d0b2fae 154 INFO("SSDP(......) constructor done. Listener Started.");
WiredHome 0:f782e7bc66ad 155 SendNotify();
WiredHome 0:f782e7bc66ad 156 }
WiredHome 0:f782e7bc66ad 157
WiredHome 0:f782e7bc66ad 158 SSDP::SSDP(const SSDP_Config_T * config) {
WiredHome 0:f782e7bc66ad 159 pThr = NULL;
WiredHome 0:f782e7bc66ad 160 memcpy(&_config, config, sizeof(SSDP_Config_T));
WiredHome 0:f782e7bc66ad 161 StartListener();
WiredHome 1:def15d0b2fae 162 INFO("SSDP(.) constructor done. Listener Started.");
WiredHome 0:f782e7bc66ad 163 SendNotify();
WiredHome 0:f782e7bc66ad 164 }
WiredHome 0:f782e7bc66ad 165
WiredHome 0:f782e7bc66ad 166 SSDP::~SSDP() {
WiredHome 0:f782e7bc66ad 167 if (pThr)
WiredHome 0:f782e7bc66ad 168 pThr->terminate();
WiredHome 0:f782e7bc66ad 169 pThr = NULL;
WiredHome 0:f782e7bc66ad 170 DelFriendlyName();
WiredHome 0:f782e7bc66ad 171 DelIdentity();
WiredHome 0:f782e7bc66ad 172 DelIPAddress();
WiredHome 0:f782e7bc66ad 173 }
WiredHome 0:f782e7bc66ad 174
WiredHome 0:f782e7bc66ad 175 void SSDP::SendNotify() {
WiredHome 0:f782e7bc66ad 176 char * out_buffer = (char *)malloc(strlen(SSDP_NOTIFY) + SSDP_NOTIFY_OVERHEAD);
WiredHome 0:f782e7bc66ad 177 if (out_buffer) {
WiredHome 0:f782e7bc66ad 178 UDPSocket sock;
WiredHome 1:def15d0b2fae 179 Endpoint broadcast;
WiredHome 5:199656d96c72 180 int i;
WiredHome 5:199656d96c72 181
WiredHome 5:199656d96c72 182 i = sock.init();
WiredHome 5:199656d96c72 183 printf(" %d = sock.init()\n", i);
WiredHome 5:199656d96c72 184 i = sock.set_broadcasting();
WiredHome 5:199656d96c72 185 printf(" %d = sock.set_broadcasting()\n", i);
WiredHome 5:199656d96c72 186 i = broadcast.set_address(MCAST_GRP, MCAST_PORT);
WiredHome 5:199656d96c72 187 printf(" %d = sock.set_address(%s,%d)\n", i, MCAST_GRP, MCAST_PORT);
WiredHome 0:f782e7bc66ad 188 sprintf(out_buffer, SSDP_NOTIFY, _config.ipAddr, _config.port);
WiredHome 5:199656d96c72 189 printf("SendNotify:\n%s\n", out_buffer);
WiredHome 5:199656d96c72 190 i = sock.sendTo(broadcast, out_buffer, strlen(out_buffer));
WiredHome 5:199656d96c72 191 printf(" %d = sendTo(%s, ..., %d)\n", i, broadcast.get_address(), strlen(out_buffer));
WiredHome 0:f782e7bc66ad 192 free(out_buffer);
WiredHome 0:f782e7bc66ad 193 }
WiredHome 0:f782e7bc66ad 194 }
WiredHome 0:f782e7bc66ad 195
WiredHome 0:f782e7bc66ad 196 bool SSDP::SetFriendlyName(const char * name) {
WiredHome 0:f782e7bc66ad 197 DelFriendlyName();
WiredHome 0:f782e7bc66ad 198 _config.name = (char *)malloc(strlen(name) + 1);
WiredHome 0:f782e7bc66ad 199 if (_config.name) {
WiredHome 0:f782e7bc66ad 200 strcpy(_config.name, name);
WiredHome 0:f782e7bc66ad 201 return true;
WiredHome 0:f782e7bc66ad 202 } else {
WiredHome 0:f782e7bc66ad 203 return false;
WiredHome 0:f782e7bc66ad 204 }
WiredHome 0:f782e7bc66ad 205 }
WiredHome 0:f782e7bc66ad 206
WiredHome 0:f782e7bc66ad 207 void SSDP::DelFriendlyName() {
WiredHome 0:f782e7bc66ad 208 if (_config.name)
WiredHome 0:f782e7bc66ad 209 free(_config.name);
WiredHome 0:f782e7bc66ad 210 _config.name = NULL;
WiredHome 0:f782e7bc66ad 211 }
WiredHome 0:f782e7bc66ad 212
WiredHome 0:f782e7bc66ad 213 bool SSDP::SetIdentity(const char * ident) {
WiredHome 0:f782e7bc66ad 214 DelIdentity();
WiredHome 0:f782e7bc66ad 215 _config.ident = (char *)malloc(strlen(ident) + 1);
WiredHome 0:f782e7bc66ad 216 if (_config.ident) {
WiredHome 0:f782e7bc66ad 217 strcpy(_config.ident, ident);
WiredHome 0:f782e7bc66ad 218 return true;
WiredHome 0:f782e7bc66ad 219 } else {
WiredHome 0:f782e7bc66ad 220 return false;
WiredHome 0:f782e7bc66ad 221 }
WiredHome 0:f782e7bc66ad 222 }
WiredHome 0:f782e7bc66ad 223
WiredHome 0:f782e7bc66ad 224 void SSDP::DelIdentity() {
WiredHome 0:f782e7bc66ad 225 if (_config.ident)
WiredHome 0:f782e7bc66ad 226 free(_config.ident);
WiredHome 0:f782e7bc66ad 227 _config.ident = NULL;
WiredHome 0:f782e7bc66ad 228 }
WiredHome 0:f782e7bc66ad 229
WiredHome 0:f782e7bc66ad 230 bool SSDP::SetIPAddress(const char * ipAddr) {
WiredHome 0:f782e7bc66ad 231 DelIPAddress();
WiredHome 0:f782e7bc66ad 232 _config.ipAddr = (char *)malloc(strlen(ipAddr) + 1);
WiredHome 0:f782e7bc66ad 233 if (_config.ipAddr) {
WiredHome 0:f782e7bc66ad 234 strcpy(_config.ipAddr, ipAddr);
WiredHome 0:f782e7bc66ad 235 return true;
WiredHome 0:f782e7bc66ad 236 } else {
WiredHome 0:f782e7bc66ad 237 return false;
WiredHome 0:f782e7bc66ad 238 }
WiredHome 0:f782e7bc66ad 239 }
WiredHome 0:f782e7bc66ad 240
WiredHome 0:f782e7bc66ad 241 void SSDP::DelIPAddress() {
WiredHome 0:f782e7bc66ad 242 if (_config.ipAddr)
WiredHome 0:f782e7bc66ad 243 free(_config.ipAddr);
WiredHome 0:f782e7bc66ad 244 _config.ipAddr = NULL;
WiredHome 0:f782e7bc66ad 245 }
WiredHome 0:f782e7bc66ad 246
WiredHome 0:f782e7bc66ad 247 bool SSDP::SetPort(int port) {
WiredHome 0:f782e7bc66ad 248 _config.port = port;
WiredHome 0:f782e7bc66ad 249 return true;
WiredHome 0:f782e7bc66ad 250 }
WiredHome 0:f782e7bc66ad 251
WiredHome 0:f782e7bc66ad 252 void SSDP::StartListener() {
WiredHome 2:3d6d70556fca 253 pThr = new Thread(SSDPListener, (void *)&_config, osPriorityLow, 768);
WiredHome 0:f782e7bc66ad 254 }