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@5:199656d96c72, 2018-11-17 (annotated)
- Committer:
- WiredHome
- Date:
- Sat Nov 17 01:30:19 2018 +0000
- Revision:
- 5:199656d96c72
- Parent:
- 3:85fa421bbcc2
- Child:
- 6:9df748509c3d
Minor changes to SSDP Notify reporting and memory allocation
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 | 3:85fa421bbcc2 | 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 | 1:def15d0b2fae | 15 | #define DBG(x, ...) std::printf("[DBG %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 1:def15d0b2fae | 16 | #define WARN(x, ...) std::printf("[WRN %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 1:def15d0b2fae | 17 | #define ERR(x, ...) std::printf("[ERR %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 1:def15d0b2fae | 18 | #define INFO(x, ...) std::printf("[INF %s %3d] "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 | |
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 | 0:f782e7bc66ad | 79 | |
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 | 0:f782e7bc66ad | 84 | int n = server.receiveFrom(client, buffer, sizeof(buffer)-1); |
WiredHome | 0:f782e7bc66ad | 85 | buffer[n] = '\0'; |
WiredHome | 0:f782e7bc66ad | 86 | if (n) { |
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 | 2:3d6d70556fca | 91 | //INFO("SSDP\n%s", buffer); |
WiredHome | 0:f782e7bc66ad | 92 | while (*p) { |
WiredHome | 0:f782e7bc66ad | 93 | char * e = strstr(p, "\r\n"); |
WiredHome | 0:f782e7bc66ad | 94 | if (e && (e - buffer) < n) { |
WiredHome | 0:f782e7bc66ad | 95 | *e = '\0'; |
WiredHome | 2:3d6d70556fca | 96 | if (sw_stristr(p, "M-SEARCH * HTTP/1.1")) { |
WiredHome | 2:3d6d70556fca | 97 | mask |= 0x01; // M-SEARCH * HTTP/1.1 |
WiredHome | 2:3d6d70556fca | 98 | } else if (sw_stristr(p, "MAN:") && sw_stristr(p,"\"ssdp:discover\"")) { |
WiredHome | 2:3d6d70556fca | 99 | mask |= 0x02; // MAN: "ssdp:discover" |
WiredHome | 2:3d6d70556fca | 100 | } else if (sw_stristr(p, "MX:")) { |
WiredHome | 2:3d6d70556fca | 101 | mask |= 0x04; // MX: \d |
WiredHome | 0:f782e7bc66ad | 102 | delay = atoi(p + 3); |
WiredHome | 2:3d6d70556fca | 103 | } else if (sw_stristr(p, "ST:") && sw_stristr(p, "upnp:rootdevice")) { |
WiredHome | 2:3d6d70556fca | 104 | mask |= 0x08; |
WiredHome | 2:3d6d70556fca | 105 | } else if (sw_stristr(p, "HOST: ")) { |
WiredHome | 2:3d6d70556fca | 106 | mask |= 0x10; // HOST: 239.255.255.250:49152 |
WiredHome | 0:f782e7bc66ad | 107 | char * pColon = strchr(p+6, ':'); |
WiredHome | 0:f782e7bc66ad | 108 | if (pColon) { |
WiredHome | 0:f782e7bc66ad | 109 | pColon = '\0'; |
WiredHome | 0:f782e7bc66ad | 110 | } |
WiredHome | 0:f782e7bc66ad | 111 | } |
WiredHome | 0:f782e7bc66ad | 112 | p = e + 1; |
WiredHome | 0:f782e7bc66ad | 113 | } |
WiredHome | 0:f782e7bc66ad | 114 | p++; |
WiredHome | 0:f782e7bc66ad | 115 | } |
WiredHome | 2:3d6d70556fca | 116 | //INFO("*********** %02X", mask); |
WiredHome | 2:3d6d70556fca | 117 | if ((mask & 0x1F) == 0x1F) { |
WiredHome | 2:3d6d70556fca | 118 | char * out_buffer = (char *)malloc(strlen(SSDP_HTTP) + SSDP_HTTP_OVERHEAD); |
WiredHome | 2:3d6d70556fca | 119 | |
WiredHome | 2:3d6d70556fca | 120 | if (out_buffer) { |
WiredHome | 2:3d6d70556fca | 121 | sprintf(out_buffer, SSDP_HTTP, cfg->ipAddr, cfg->port, cfg->ident, cfg->ident); |
WiredHome | 2:3d6d70556fca | 122 | // It would be polite to delay, but the recommendation is from 1 to 2 seconds. |
WiredHome | 2:3d6d70556fca | 123 | // Send the response twice, to improve reliability of node discovery |
WiredHome | 2:3d6d70556fca | 124 | for (int i=0; i<2; i++) { |
WiredHome | 2:3d6d70556fca | 125 | server.sendTo(client, out_buffer, strlen(out_buffer)); |
WiredHome | 2:3d6d70556fca | 126 | //Thread::wait(150); |
WiredHome | 2:3d6d70556fca | 127 | } |
WiredHome | 2:3d6d70556fca | 128 | free(out_buffer); |
WiredHome | 2:3d6d70556fca | 129 | INFO("SSDPListener: stack-used: %d, total: %d", pThr->max_stack(), pThr->stack_size()); |
WiredHome | 2:3d6d70556fca | 130 | } else { |
WiredHome | 2:3d6d70556fca | 131 | ERR("Can't get memory for response"); |
WiredHome | 2:3d6d70556fca | 132 | } |
WiredHome | 0:f782e7bc66ad | 133 | } |
WiredHome | 0:f782e7bc66ad | 134 | } |
WiredHome | 0:f782e7bc66ad | 135 | } |
WiredHome | 0:f782e7bc66ad | 136 | } |
WiredHome | 0:f782e7bc66ad | 137 | |
WiredHome | 0:f782e7bc66ad | 138 | SSDP::SSDP(const char * name, const char * ident, const char * ipAddr, int port) { |
WiredHome | 0:f782e7bc66ad | 139 | pThr = NULL; |
WiredHome | 0:f782e7bc66ad | 140 | _config.name = NULL; |
WiredHome | 0:f782e7bc66ad | 141 | SetFriendlyName(name); |
WiredHome | 0:f782e7bc66ad | 142 | _config.ident = NULL; |
WiredHome | 0:f782e7bc66ad | 143 | SetFriendlyName(ident); |
WiredHome | 0:f782e7bc66ad | 144 | _config.ipAddr = NULL; |
WiredHome | 0:f782e7bc66ad | 145 | SetIPAddress(ipAddr); |
WiredHome | 0:f782e7bc66ad | 146 | _config.port = port; |
WiredHome | 0:f782e7bc66ad | 147 | StartListener(); |
WiredHome | 1:def15d0b2fae | 148 | INFO("SSDP(......) constructor done. Listener Started."); |
WiredHome | 0:f782e7bc66ad | 149 | SendNotify(); |
WiredHome | 0:f782e7bc66ad | 150 | } |
WiredHome | 0:f782e7bc66ad | 151 | |
WiredHome | 0:f782e7bc66ad | 152 | SSDP::SSDP(const SSDP_Config_T * config) { |
WiredHome | 0:f782e7bc66ad | 153 | pThr = NULL; |
WiredHome | 0:f782e7bc66ad | 154 | memcpy(&_config, config, sizeof(SSDP_Config_T)); |
WiredHome | 0:f782e7bc66ad | 155 | StartListener(); |
WiredHome | 1:def15d0b2fae | 156 | INFO("SSDP(.) constructor done. Listener Started."); |
WiredHome | 0:f782e7bc66ad | 157 | SendNotify(); |
WiredHome | 0:f782e7bc66ad | 158 | } |
WiredHome | 0:f782e7bc66ad | 159 | |
WiredHome | 0:f782e7bc66ad | 160 | SSDP::~SSDP() { |
WiredHome | 0:f782e7bc66ad | 161 | if (pThr) |
WiredHome | 0:f782e7bc66ad | 162 | pThr->terminate(); |
WiredHome | 0:f782e7bc66ad | 163 | pThr = NULL; |
WiredHome | 0:f782e7bc66ad | 164 | DelFriendlyName(); |
WiredHome | 0:f782e7bc66ad | 165 | DelIdentity(); |
WiredHome | 0:f782e7bc66ad | 166 | DelIPAddress(); |
WiredHome | 0:f782e7bc66ad | 167 | } |
WiredHome | 0:f782e7bc66ad | 168 | |
WiredHome | 0:f782e7bc66ad | 169 | void SSDP::SendNotify() { |
WiredHome | 0:f782e7bc66ad | 170 | char * out_buffer = (char *)malloc(strlen(SSDP_NOTIFY) + SSDP_NOTIFY_OVERHEAD); |
WiredHome | 0:f782e7bc66ad | 171 | if (out_buffer) { |
WiredHome | 0:f782e7bc66ad | 172 | UDPSocket sock; |
WiredHome | 1:def15d0b2fae | 173 | Endpoint broadcast; |
WiredHome | 5:199656d96c72 | 174 | int i; |
WiredHome | 5:199656d96c72 | 175 | |
WiredHome | 5:199656d96c72 | 176 | i = sock.init(); |
WiredHome | 5:199656d96c72 | 177 | printf(" %d = sock.init()\n", i); |
WiredHome | 5:199656d96c72 | 178 | i = sock.set_broadcasting(); |
WiredHome | 5:199656d96c72 | 179 | printf(" %d = sock.set_broadcasting()\n", i); |
WiredHome | 5:199656d96c72 | 180 | i = broadcast.set_address(MCAST_GRP, MCAST_PORT); |
WiredHome | 5:199656d96c72 | 181 | printf(" %d = sock.set_address(%s,%d)\n", i, MCAST_GRP, MCAST_PORT); |
WiredHome | 0:f782e7bc66ad | 182 | sprintf(out_buffer, SSDP_NOTIFY, _config.ipAddr, _config.port); |
WiredHome | 5:199656d96c72 | 183 | printf("SendNotify:\n%s\n", out_buffer); |
WiredHome | 5:199656d96c72 | 184 | i = sock.sendTo(broadcast, out_buffer, strlen(out_buffer)); |
WiredHome | 5:199656d96c72 | 185 | printf(" %d = sendTo(%s, ..., %d)\n", i, broadcast.get_address(), strlen(out_buffer)); |
WiredHome | 0:f782e7bc66ad | 186 | free(out_buffer); |
WiredHome | 0:f782e7bc66ad | 187 | } |
WiredHome | 0:f782e7bc66ad | 188 | } |
WiredHome | 0:f782e7bc66ad | 189 | |
WiredHome | 0:f782e7bc66ad | 190 | bool SSDP::SetFriendlyName(const char * name) { |
WiredHome | 0:f782e7bc66ad | 191 | DelFriendlyName(); |
WiredHome | 0:f782e7bc66ad | 192 | _config.name = (char *)malloc(strlen(name) + 1); |
WiredHome | 0:f782e7bc66ad | 193 | if (_config.name) { |
WiredHome | 0:f782e7bc66ad | 194 | strcpy(_config.name, name); |
WiredHome | 0:f782e7bc66ad | 195 | return true; |
WiredHome | 0:f782e7bc66ad | 196 | } else { |
WiredHome | 0:f782e7bc66ad | 197 | return false; |
WiredHome | 0:f782e7bc66ad | 198 | } |
WiredHome | 0:f782e7bc66ad | 199 | } |
WiredHome | 0:f782e7bc66ad | 200 | |
WiredHome | 0:f782e7bc66ad | 201 | void SSDP::DelFriendlyName() { |
WiredHome | 0:f782e7bc66ad | 202 | if (_config.name) |
WiredHome | 0:f782e7bc66ad | 203 | free(_config.name); |
WiredHome | 0:f782e7bc66ad | 204 | _config.name = NULL; |
WiredHome | 0:f782e7bc66ad | 205 | } |
WiredHome | 0:f782e7bc66ad | 206 | |
WiredHome | 0:f782e7bc66ad | 207 | bool SSDP::SetIdentity(const char * ident) { |
WiredHome | 0:f782e7bc66ad | 208 | DelIdentity(); |
WiredHome | 0:f782e7bc66ad | 209 | _config.ident = (char *)malloc(strlen(ident) + 1); |
WiredHome | 0:f782e7bc66ad | 210 | if (_config.ident) { |
WiredHome | 0:f782e7bc66ad | 211 | strcpy(_config.ident, ident); |
WiredHome | 0:f782e7bc66ad | 212 | return true; |
WiredHome | 0:f782e7bc66ad | 213 | } else { |
WiredHome | 0:f782e7bc66ad | 214 | return false; |
WiredHome | 0:f782e7bc66ad | 215 | } |
WiredHome | 0:f782e7bc66ad | 216 | } |
WiredHome | 0:f782e7bc66ad | 217 | |
WiredHome | 0:f782e7bc66ad | 218 | void SSDP::DelIdentity() { |
WiredHome | 0:f782e7bc66ad | 219 | if (_config.ident) |
WiredHome | 0:f782e7bc66ad | 220 | free(_config.ident); |
WiredHome | 0:f782e7bc66ad | 221 | _config.ident = NULL; |
WiredHome | 0:f782e7bc66ad | 222 | } |
WiredHome | 0:f782e7bc66ad | 223 | |
WiredHome | 0:f782e7bc66ad | 224 | bool SSDP::SetIPAddress(const char * ipAddr) { |
WiredHome | 0:f782e7bc66ad | 225 | DelIPAddress(); |
WiredHome | 0:f782e7bc66ad | 226 | _config.ipAddr = (char *)malloc(strlen(ipAddr) + 1); |
WiredHome | 0:f782e7bc66ad | 227 | if (_config.ipAddr) { |
WiredHome | 0:f782e7bc66ad | 228 | strcpy(_config.ipAddr, ipAddr); |
WiredHome | 0:f782e7bc66ad | 229 | return true; |
WiredHome | 0:f782e7bc66ad | 230 | } else { |
WiredHome | 0:f782e7bc66ad | 231 | return false; |
WiredHome | 0:f782e7bc66ad | 232 | } |
WiredHome | 0:f782e7bc66ad | 233 | } |
WiredHome | 0:f782e7bc66ad | 234 | |
WiredHome | 0:f782e7bc66ad | 235 | void SSDP::DelIPAddress() { |
WiredHome | 0:f782e7bc66ad | 236 | if (_config.ipAddr) |
WiredHome | 0:f782e7bc66ad | 237 | free(_config.ipAddr); |
WiredHome | 0:f782e7bc66ad | 238 | _config.ipAddr = NULL; |
WiredHome | 0:f782e7bc66ad | 239 | } |
WiredHome | 0:f782e7bc66ad | 240 | |
WiredHome | 0:f782e7bc66ad | 241 | bool SSDP::SetPort(int port) { |
WiredHome | 0:f782e7bc66ad | 242 | _config.port = port; |
WiredHome | 0:f782e7bc66ad | 243 | return true; |
WiredHome | 0:f782e7bc66ad | 244 | } |
WiredHome | 0:f782e7bc66ad | 245 | |
WiredHome | 0:f782e7bc66ad | 246 | void SSDP::StartListener() { |
WiredHome | 2:3d6d70556fca | 247 | pThr = new Thread(SSDPListener, (void *)&_config, osPriorityLow, 768); |
WiredHome | 0:f782e7bc66ad | 248 | } |