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@1:def15d0b2fae, 2018-07-03 (annotated)
- Committer:
- WiredHome
- Date:
- Tue Jul 03 00:46:34 2018 +0000
- Revision:
- 1:def15d0b2fae
- Parent:
- 0:f782e7bc66ad
- Child:
- 2:3d6d70556fca
Documentation update.
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 | 0:f782e7bc66ad | 9 | |
WiredHome | 1:def15d0b2fae | 10 | //#define DEBUG "SSDP" //Debug is disabled by default |
WiredHome | 0:f782e7bc66ad | 11 | |
WiredHome | 1:def15d0b2fae | 12 | #include <cstdio> |
WiredHome | 1:def15d0b2fae | 13 | #if (defined(DEBUG) && !defined(TARGET_LPC11U24)) |
WiredHome | 1:def15d0b2fae | 14 | #define DBG(x, ...) std::printf("[DBG %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 1:def15d0b2fae | 15 | #define WARN(x, ...) std::printf("[WRN %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 1:def15d0b2fae | 16 | #define ERR(x, ...) std::printf("[ERR %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 1:def15d0b2fae | 17 | #define INFO(x, ...) std::printf("[INF %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 1:def15d0b2fae | 18 | #else |
WiredHome | 1:def15d0b2fae | 19 | #define DBG(x, ...) |
WiredHome | 1:def15d0b2fae | 20 | #define WARN(x, ...) |
WiredHome | 1:def15d0b2fae | 21 | #define ERR(x, ...) |
WiredHome | 1:def15d0b2fae | 22 | #define INFO(x, ...) |
WiredHome | 1:def15d0b2fae | 23 | #endif |
WiredHome | 0:f782e7bc66ad | 24 | |
WiredHome | 0:f782e7bc66ad | 25 | static const char* MCAST_GRP = "239.255.255.250"; |
WiredHome | 0:f782e7bc66ad | 26 | static const int MCAST_PORT = 1900; |
WiredHome | 0:f782e7bc66ad | 27 | |
WiredHome | 0:f782e7bc66ad | 28 | // sprintf(buffer, SSDP_HTTP, "myIPString", myPort, "myIdentity", "myIdentity"); |
WiredHome | 0:f782e7bc66ad | 29 | // Requires IP address as a string |
WiredHome | 0:f782e7bc66ad | 30 | static const char * SSDP_HTTP = |
WiredHome | 0:f782e7bc66ad | 31 | "HTTP/1.1 200 OK\r\n" |
WiredHome | 0:f782e7bc66ad | 32 | "CACHE-CONTROL: max-age=1800\r\n" |
WiredHome | 0:f782e7bc66ad | 33 | "DATE: Mon, 22 Jun 2015 17:24:01 GMT\r\n" |
WiredHome | 0:f782e7bc66ad | 34 | "EXT:\r\n" |
WiredHome | 0:f782e7bc66ad | 35 | "LOCATION: http://%s:%d/setup.xml\r\n" // "my.ip.string", portNum |
WiredHome | 0:f782e7bc66ad | 36 | "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" |
WiredHome | 0:f782e7bc66ad | 37 | "01-NLS: %s\r\n" // "Unique Identity" |
WiredHome | 0:f782e7bc66ad | 38 | "SERVER: Smartware, UPnP/1.0, Smartware\r\n" |
WiredHome | 0:f782e7bc66ad | 39 | "ST: upnp:rootdevice\r\n" |
WiredHome | 0:f782e7bc66ad | 40 | "USN: uuid:Node-1_0-%s::upnp:rootdevice\r\n" // "Unique Identity" |
WiredHome | 0:f782e7bc66ad | 41 | "X-User-Agent: Smartware\r\n" |
WiredHome | 0:f782e7bc66ad | 42 | "\r\n"; |
WiredHome | 0:f782e7bc66ad | 43 | |
WiredHome | 0:f782e7bc66ad | 44 | // Addr: "###.###.###.###" [15] |
WiredHome | 0:f782e7bc66ad | 45 | // Port: 12345 [5] |
WiredHome | 0:f782e7bc66ad | 46 | // Ident: "#########0#########0#########0" [30] x 2 |
WiredHome | 0:f782e7bc66ad | 47 | // |
WiredHome | 0:f782e7bc66ad | 48 | #define SSDP_HTTP_OVERHEAD 50 // Number of bytes to fill in the information |
WiredHome | 0:f782e7bc66ad | 49 | |
WiredHome | 0:f782e7bc66ad | 50 | |
WiredHome | 0:f782e7bc66ad | 51 | // sprintf(buffer, SSDP_NOTIFY, myIPasString, myPort); |
WiredHome | 0:f782e7bc66ad | 52 | // Requires IP address as a string |
WiredHome | 1:def15d0b2fae | 53 | static const char * SSDP_NOTIFY = |
WiredHome | 0:f782e7bc66ad | 54 | "NOTIFY * HTTP/1.1\r\n" |
WiredHome | 0:f782e7bc66ad | 55 | "HOST: 239.255.255.250:1900\r\n" |
WiredHome | 0:f782e7bc66ad | 56 | "CACHE-CONTROL: max-age=1800\r\n" |
WiredHome | 0:f782e7bc66ad | 57 | "LOCATION: http://%s:%u/setup.xml\r\n" |
WiredHome | 0:f782e7bc66ad | 58 | "NTS: ssdp:alive\r\n\r\n" |
WiredHome | 0:f782e7bc66ad | 59 | ""; |
WiredHome | 0:f782e7bc66ad | 60 | |
WiredHome | 0:f782e7bc66ad | 61 | // Addr: "###.###.###.###" [15] |
WiredHome | 0:f782e7bc66ad | 62 | // Port: 12345 [5] |
WiredHome | 0:f782e7bc66ad | 63 | // |
WiredHome | 0:f782e7bc66ad | 64 | #define SSDP_NOTIFY_OVERHEAD 20 // Number of bytes to fill in the information |
WiredHome | 0:f782e7bc66ad | 65 | |
WiredHome | 0:f782e7bc66ad | 66 | |
WiredHome | 1:def15d0b2fae | 67 | // The SSDP listener thread |
WiredHome | 0:f782e7bc66ad | 68 | static void SSDPListener(void const * args) { |
WiredHome | 0:f782e7bc66ad | 69 | UDPSocket server; |
WiredHome | 0:f782e7bc66ad | 70 | SSDP_Config_T * cfg = (SSDP_Config_T *)args; |
WiredHome | 0:f782e7bc66ad | 71 | |
WiredHome | 0:f782e7bc66ad | 72 | server.bind(MCAST_PORT); |
WiredHome | 0:f782e7bc66ad | 73 | if (server.join_multicast_group(MCAST_GRP) != 0) { |
WiredHome | 1:def15d0b2fae | 74 | ERR("Error joining the multicast group"); |
WiredHome | 0:f782e7bc66ad | 75 | while (true) {} |
WiredHome | 0:f782e7bc66ad | 76 | } |
WiredHome | 0:f782e7bc66ad | 77 | |
WiredHome | 0:f782e7bc66ad | 78 | Endpoint client; |
WiredHome | 0:f782e7bc66ad | 79 | char buffer[256]; |
WiredHome | 0:f782e7bc66ad | 80 | while (true) { |
WiredHome | 1:def15d0b2fae | 81 | INFO("Wait for packet..."); |
WiredHome | 0:f782e7bc66ad | 82 | int n = server.receiveFrom(client, buffer, sizeof(buffer)-1); |
WiredHome | 0:f782e7bc66ad | 83 | buffer[n] = '\0'; |
WiredHome | 0:f782e7bc66ad | 84 | if (n) { |
WiredHome | 0:f782e7bc66ad | 85 | char * p = buffer; |
WiredHome | 0:f782e7bc66ad | 86 | volatile int delay = 1; |
WiredHome | 0:f782e7bc66ad | 87 | |
WiredHome | 0:f782e7bc66ad | 88 | while (*p) { |
WiredHome | 0:f782e7bc66ad | 89 | char * e = strstr(p, "\r\n"); |
WiredHome | 0:f782e7bc66ad | 90 | if (e && (e - buffer) < n) { |
WiredHome | 0:f782e7bc66ad | 91 | *e = '\0'; |
WiredHome | 0:f782e7bc66ad | 92 | if (strstr(p, "MX: ")) { |
WiredHome | 0:f782e7bc66ad | 93 | delay = atoi(p + 3); |
WiredHome | 0:f782e7bc66ad | 94 | } else if (strstr(p, "HOST: ")) { |
WiredHome | 0:f782e7bc66ad | 95 | char * pColon = strchr(p+6, ':'); |
WiredHome | 0:f782e7bc66ad | 96 | if (pColon) { |
WiredHome | 0:f782e7bc66ad | 97 | pColon = '\0'; |
WiredHome | 0:f782e7bc66ad | 98 | } |
WiredHome | 0:f782e7bc66ad | 99 | } |
WiredHome | 0:f782e7bc66ad | 100 | p = e + 1; |
WiredHome | 0:f782e7bc66ad | 101 | } |
WiredHome | 0:f782e7bc66ad | 102 | p++; |
WiredHome | 0:f782e7bc66ad | 103 | } |
WiredHome | 0:f782e7bc66ad | 104 | char * out_buffer = (char *)malloc(strlen(SSDP_HTTP) + SSDP_HTTP_OVERHEAD); |
WiredHome | 0:f782e7bc66ad | 105 | if (out_buffer) { |
WiredHome | 0:f782e7bc66ad | 106 | sprintf(out_buffer, SSDP_HTTP, cfg->ipAddr, cfg->port, cfg->ident, cfg->ident); |
WiredHome | 0:f782e7bc66ad | 107 | // It would be polite to delay, but the recommendation is from 1 to 2 seconds |
WiredHome | 0:f782e7bc66ad | 108 | // Thread::wait(delay * 1000); |
WiredHome | 0:f782e7bc66ad | 109 | server.sendTo(client, out_buffer, strlen(out_buffer)); |
WiredHome | 0:f782e7bc66ad | 110 | free(out_buffer); |
WiredHome | 0:f782e7bc66ad | 111 | } |
WiredHome | 0:f782e7bc66ad | 112 | } |
WiredHome | 0:f782e7bc66ad | 113 | } |
WiredHome | 0:f782e7bc66ad | 114 | } |
WiredHome | 0:f782e7bc66ad | 115 | |
WiredHome | 0:f782e7bc66ad | 116 | SSDP::SSDP(const char * name, const char * ident, const char * ipAddr, int port) { |
WiredHome | 0:f782e7bc66ad | 117 | pThr = NULL; |
WiredHome | 0:f782e7bc66ad | 118 | _config.name = NULL; |
WiredHome | 0:f782e7bc66ad | 119 | SetFriendlyName(name); |
WiredHome | 0:f782e7bc66ad | 120 | _config.ident = NULL; |
WiredHome | 0:f782e7bc66ad | 121 | SetFriendlyName(ident); |
WiredHome | 0:f782e7bc66ad | 122 | _config.ipAddr = NULL; |
WiredHome | 0:f782e7bc66ad | 123 | SetIPAddress(ipAddr); |
WiredHome | 0:f782e7bc66ad | 124 | _config.port = port; |
WiredHome | 0:f782e7bc66ad | 125 | StartListener(); |
WiredHome | 1:def15d0b2fae | 126 | INFO("SSDP(......) constructor done. Listener Started."); |
WiredHome | 0:f782e7bc66ad | 127 | SendNotify(); |
WiredHome | 0:f782e7bc66ad | 128 | } |
WiredHome | 0:f782e7bc66ad | 129 | |
WiredHome | 0:f782e7bc66ad | 130 | SSDP::SSDP(const SSDP_Config_T * config) { |
WiredHome | 0:f782e7bc66ad | 131 | pThr = NULL; |
WiredHome | 0:f782e7bc66ad | 132 | memcpy(&_config, config, sizeof(SSDP_Config_T)); |
WiredHome | 0:f782e7bc66ad | 133 | StartListener(); |
WiredHome | 1:def15d0b2fae | 134 | INFO("SSDP(.) constructor done. Listener Started."); |
WiredHome | 0:f782e7bc66ad | 135 | SendNotify(); |
WiredHome | 0:f782e7bc66ad | 136 | } |
WiredHome | 0:f782e7bc66ad | 137 | |
WiredHome | 0:f782e7bc66ad | 138 | SSDP::~SSDP() { |
WiredHome | 0:f782e7bc66ad | 139 | if (pThr) |
WiredHome | 0:f782e7bc66ad | 140 | pThr->terminate(); |
WiredHome | 0:f782e7bc66ad | 141 | pThr = NULL; |
WiredHome | 0:f782e7bc66ad | 142 | DelFriendlyName(); |
WiredHome | 0:f782e7bc66ad | 143 | DelIdentity(); |
WiredHome | 0:f782e7bc66ad | 144 | DelIPAddress(); |
WiredHome | 0:f782e7bc66ad | 145 | } |
WiredHome | 0:f782e7bc66ad | 146 | |
WiredHome | 0:f782e7bc66ad | 147 | void SSDP::SendNotify() { |
WiredHome | 0:f782e7bc66ad | 148 | char * out_buffer = (char *)malloc(strlen(SSDP_NOTIFY) + SSDP_NOTIFY_OVERHEAD); |
WiredHome | 0:f782e7bc66ad | 149 | if (out_buffer) { |
WiredHome | 0:f782e7bc66ad | 150 | UDPSocket sock; |
WiredHome | 1:def15d0b2fae | 151 | Endpoint broadcast; |
WiredHome | 0:f782e7bc66ad | 152 | sock.init(); |
WiredHome | 0:f782e7bc66ad | 153 | sock.set_broadcasting(); |
WiredHome | 0:f782e7bc66ad | 154 | broadcast.set_address(MCAST_GRP, MCAST_PORT); |
WiredHome | 0:f782e7bc66ad | 155 | sprintf(out_buffer, SSDP_NOTIFY, _config.ipAddr, _config.port); |
WiredHome | 0:f782e7bc66ad | 156 | sock.sendTo(broadcast, out_buffer, strlen(out_buffer)); |
WiredHome | 0:f782e7bc66ad | 157 | free(out_buffer); |
WiredHome | 0:f782e7bc66ad | 158 | } |
WiredHome | 0:f782e7bc66ad | 159 | } |
WiredHome | 0:f782e7bc66ad | 160 | |
WiredHome | 0:f782e7bc66ad | 161 | bool SSDP::SetFriendlyName(const char * name) { |
WiredHome | 0:f782e7bc66ad | 162 | DelFriendlyName(); |
WiredHome | 0:f782e7bc66ad | 163 | _config.name = (char *)malloc(strlen(name) + 1); |
WiredHome | 0:f782e7bc66ad | 164 | if (_config.name) { |
WiredHome | 0:f782e7bc66ad | 165 | strcpy(_config.name, name); |
WiredHome | 0:f782e7bc66ad | 166 | return true; |
WiredHome | 0:f782e7bc66ad | 167 | } else { |
WiredHome | 0:f782e7bc66ad | 168 | return false; |
WiredHome | 0:f782e7bc66ad | 169 | } |
WiredHome | 0:f782e7bc66ad | 170 | } |
WiredHome | 0:f782e7bc66ad | 171 | |
WiredHome | 0:f782e7bc66ad | 172 | void SSDP::DelFriendlyName() { |
WiredHome | 0:f782e7bc66ad | 173 | if (_config.name) |
WiredHome | 0:f782e7bc66ad | 174 | free(_config.name); |
WiredHome | 0:f782e7bc66ad | 175 | _config.name = NULL; |
WiredHome | 0:f782e7bc66ad | 176 | } |
WiredHome | 0:f782e7bc66ad | 177 | |
WiredHome | 0:f782e7bc66ad | 178 | bool SSDP::SetIdentity(const char * ident) { |
WiredHome | 0:f782e7bc66ad | 179 | DelIdentity(); |
WiredHome | 0:f782e7bc66ad | 180 | _config.ident = (char *)malloc(strlen(ident) + 1); |
WiredHome | 0:f782e7bc66ad | 181 | if (_config.ident) { |
WiredHome | 0:f782e7bc66ad | 182 | strcpy(_config.ident, ident); |
WiredHome | 0:f782e7bc66ad | 183 | return true; |
WiredHome | 0:f782e7bc66ad | 184 | } else { |
WiredHome | 0:f782e7bc66ad | 185 | return false; |
WiredHome | 0:f782e7bc66ad | 186 | } |
WiredHome | 0:f782e7bc66ad | 187 | } |
WiredHome | 0:f782e7bc66ad | 188 | |
WiredHome | 0:f782e7bc66ad | 189 | void SSDP::DelIdentity() { |
WiredHome | 0:f782e7bc66ad | 190 | if (_config.ident) |
WiredHome | 0:f782e7bc66ad | 191 | free(_config.ident); |
WiredHome | 0:f782e7bc66ad | 192 | _config.ident = NULL; |
WiredHome | 0:f782e7bc66ad | 193 | } |
WiredHome | 0:f782e7bc66ad | 194 | |
WiredHome | 0:f782e7bc66ad | 195 | bool SSDP::SetIPAddress(const char * ipAddr) { |
WiredHome | 0:f782e7bc66ad | 196 | DelIPAddress(); |
WiredHome | 0:f782e7bc66ad | 197 | _config.ipAddr = (char *)malloc(strlen(ipAddr) + 1); |
WiredHome | 0:f782e7bc66ad | 198 | if (_config.ipAddr) { |
WiredHome | 0:f782e7bc66ad | 199 | strcpy(_config.ipAddr, ipAddr); |
WiredHome | 0:f782e7bc66ad | 200 | return true; |
WiredHome | 0:f782e7bc66ad | 201 | } else { |
WiredHome | 0:f782e7bc66ad | 202 | return false; |
WiredHome | 0:f782e7bc66ad | 203 | } |
WiredHome | 0:f782e7bc66ad | 204 | } |
WiredHome | 0:f782e7bc66ad | 205 | |
WiredHome | 0:f782e7bc66ad | 206 | void SSDP::DelIPAddress() { |
WiredHome | 0:f782e7bc66ad | 207 | if (_config.ipAddr) |
WiredHome | 0:f782e7bc66ad | 208 | free(_config.ipAddr); |
WiredHome | 0:f782e7bc66ad | 209 | _config.ipAddr = NULL; |
WiredHome | 0:f782e7bc66ad | 210 | } |
WiredHome | 0:f782e7bc66ad | 211 | |
WiredHome | 0:f782e7bc66ad | 212 | bool SSDP::SetPort(int port) { |
WiredHome | 0:f782e7bc66ad | 213 | _config.port = port; |
WiredHome | 0:f782e7bc66ad | 214 | return true; |
WiredHome | 0:f782e7bc66ad | 215 | } |
WiredHome | 0:f782e7bc66ad | 216 | |
WiredHome | 0:f782e7bc66ad | 217 | void SSDP::StartListener() { |
WiredHome | 0:f782e7bc66ad | 218 | pThr = new Thread(SSDPListener, (void *)&_config, osPriorityLow); |
WiredHome | 0:f782e7bc66ad | 219 | } |