SSDP Server - working version provides SSDP based network discovery, and with a companion web server, may provide other functionalities.
Dependents: X10Svr SSDP_Server
SSDP.cpp@7:1e8c677e3d28, 2018-11-19 (annotated)
- Committer:
- WiredHome
- Date:
- Mon Nov 19 01:16:59 2018 +0000
- Revision:
- 7:1e8c677e3d28
- Parent:
- 6:9df748509c3d
- Child:
- 8:e8f0dc2b78c4
Add ST: ssdp:all as a trigger to reply to the SSDP query.
Who changed what in which revision?
User | Revision | Line number | New 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 | 0:f782e7bc66ad | 87 | char * p = buffer; |
WiredHome | 2:3d6d70556fca | 88 | volatile int delay = 0; |
WiredHome | 2:3d6d70556fca | 89 | uint8_t mask = 0x00; // fragments we found in the received packet |
WiredHome | 0:f782e7bc66ad | 90 | |
WiredHome | 7:1e8c677e3d28 | 91 | buffer[n] = '\0'; |
WiredHome | 6:9df748509c3d | 92 | INFO("SSDP receiveFrom %d bytes from %s:%d", n, |
WiredHome | 6:9df748509c3d | 93 | client.get_address(), client.get_port()); |
WiredHome | 6:9df748509c3d | 94 | INFO("SSDP\n%s", buffer); |
WiredHome | 0:f782e7bc66ad | 95 | while (*p) { |
WiredHome | 0:f782e7bc66ad | 96 | char * e = strstr(p, "\r\n"); |
WiredHome | 0:f782e7bc66ad | 97 | if (e && (e - buffer) < n) { |
WiredHome | 0:f782e7bc66ad | 98 | *e = '\0'; |
WiredHome | 2:3d6d70556fca | 99 | if (sw_stristr(p, "M-SEARCH * HTTP/1.1")) { |
WiredHome | 2:3d6d70556fca | 100 | mask |= 0x01; // M-SEARCH * HTTP/1.1 |
WiredHome | 2:3d6d70556fca | 101 | } else if (sw_stristr(p, "MAN:") && sw_stristr(p,"\"ssdp:discover\"")) { |
WiredHome | 2:3d6d70556fca | 102 | mask |= 0x02; // MAN: "ssdp:discover" |
WiredHome | 2:3d6d70556fca | 103 | } else if (sw_stristr(p, "MX:")) { |
WiredHome | 2:3d6d70556fca | 104 | mask |= 0x04; // MX: \d |
WiredHome | 0:f782e7bc66ad | 105 | delay = atoi(p + 3); |
WiredHome | 2:3d6d70556fca | 106 | } else if (sw_stristr(p, "ST:") && sw_stristr(p, "upnp:rootdevice")) { |
WiredHome | 2:3d6d70556fca | 107 | mask |= 0x08; |
WiredHome | 7:1e8c677e3d28 | 108 | } else if (sw_stristr(p, "ST:") && sw_stristr(p, "ssdp:all")) { |
WiredHome | 7:1e8c677e3d28 | 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 | } |