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