1 /**************************************************************************
2 *
3 * Copyright (c) 2000-2003 Intel Corporation
4 * All rights reserved.
5 * Copyright (C) 2011-2012 France Telecom All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * - Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 * - Neither name of Intel Corporation nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 **************************************************************************/
32
33 #include "config.h"
34
35 #ifdef INCLUDE_DEVICE_APIS
36 #if EXCLUDE_SSDP == 0
37
38 #include "httputils.h"
39 #include "ssdplib.h"
40 #include "statcodes.h"
41 #include "ThreadPool.h"
42 #include "upnpapi.h"
43 #include "UpnpInet.h"
44 #include "upnpdebug.h"
45 #include "TimerThread.h"
46 #include "genut.h"
47 #include "inet_pton.h"
48 #include "netif.h"
49 #include "upnpapi.h"
50
51 #include <cassert>
52 #include <cstdio>
53 #include <cstring>
54 #include <iostream>
55 #include <sstream>
56 #include <string>
57 #include <thread>
58 #include <algorithm>
59 #include <chrono>
60
61 struct SsdpSearchReply {
62 int MaxAge;
63 UpnpDevice_Handle handle;
64 struct sockaddr_storage dest_addr;
65 SsdpEntity event;
66 };
67
68 struct SSDPPwrState {
69 int PowerState;
70 int SleepPeriod;
71 int RegistrationState;
72 };
73
74 // A bundle to simplify arg lists
75 struct SSDPCommonData {
76 SOCKET sock;
77 struct sockaddr *DestAddr;
78 const char *DevOrServType;
79 SSDPPwrState pwr;
80 std::string prodvers;
81 };
82
del_ssdpsearchreply(void * p)83 static void del_ssdpsearchreply(void *p)
84 {
85 delete static_cast<SsdpSearchReply *>(p);
86 }
87
thread_advertiseandreply(void * data)88 static void *thread_advertiseandreply(void *data)
89 {
90 auto arg = static_cast<SsdpSearchReply *>(data);
91
92 AdvertiseAndReply(arg->handle, MSGTYPE_REPLY,
93 arg->MaxAge,
94 reinterpret_cast<struct sockaddr *>(&arg->dest_addr),
95 arg->event);
96 return nullptr;
97 }
98
ssdp_handle_device_request(SSDPPacketParser & parser,struct sockaddr_storage * dest_addr)99 void ssdp_handle_device_request(SSDPPacketParser& parser,
100 struct sockaddr_storage *dest_addr)
101 {
102 int handle, start;
103 struct Handle_Info *dev_info = nullptr;
104 int mx;
105 SsdpEntity event;
106 int maxAge;
107
108 /* check man hdr. */
109 if (!parser.man || strcmp(parser.man, R"("ssdp:discover")") != 0) {
110 /* bad or missing hdr. */
111 UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
112 "ssdp_handle_device_req: no/bad MAN header\n");
113 return;
114 }
115 /* MX header. Must be >= 1*/
116 if (!parser.mx || (mx = atoi(parser.mx)) <= 0) {
117 UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
118 "ssdp_handle_device_req: no/bad MX header\n");
119 return;
120 }
121 /* ST header. */
122 if (!parser.st || ssdp_request_type(parser.st, &event) == -1) {
123 UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
124 "ssdp_handle_device_req: no/bad ST header\n");
125 return;
126 }
127
128 // Loop to dispatch the packet to each of our configured devices
129 // by starting the handle search at the last processed
130 // position. each device response is scheduled by the ThreadPool
131 // with a random delay based on the MX header of the search packet.
132 start = 0;
133 for (;;) {
134 HandleLock();
135 /* device info. */
136 switch (GetDeviceHandleInfo(start, &handle, &dev_info)) {
137 case HND_DEVICE:
138 break;
139 default:
140 HandleUnlock();
141 /* no info found. */
142 return;
143 }
144 maxAge = dev_info->MaxAge;
145 HandleUnlock();
146
147 UpnpPrintf(UPNP_DEBUG, API, __FILE__, __LINE__,
148 "MAX-AGE = %d\n", maxAge);
149 UpnpPrintf(UPNP_DEBUG, API, __FILE__, __LINE__,
150 "MX = %d\n", maxAge);
151 UpnpPrintf(UPNP_DEBUG, API, __FILE__, __LINE__,
152 "DeviceType = %s\n", event.DeviceType.c_str());
153 UpnpPrintf(UPNP_DEBUG, API, __FILE__, __LINE__,
154 "DeviceUuid = %s\n", event.UDN.c_str());
155 UpnpPrintf(UPNP_DEBUG, API, __FILE__, __LINE__,
156 "ServiceType = %s\n", event.ServiceType.c_str());
157
158 SsdpSearchReply *threadArg = new SsdpSearchReply;
159 if (threadArg == nullptr)
160 return;
161 threadArg->handle = handle;
162 memcpy(&threadArg->dest_addr, dest_addr, sizeof(threadArg->dest_addr));
163 threadArg->event = event;
164 threadArg->MaxAge = maxAge;
165
166 mx = std::max(1, mx);
167 /* Subtract a bit from the mx to allow for network/processing delays */
168 int delayms = rand() % (mx * 1000 - 100);
169 UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
170 "ssdp_handle_device_req: scheduling resp in %d ms\n",delayms);
171 gTimerThread->schedule(
172 TimerThread::SHORT_TERM, std::chrono::milliseconds(delayms),
173 nullptr, thread_advertiseandreply, threadArg, del_ssdpsearchreply);
174 start = handle;
175 }
176 }
177
178 // Create the reply socket and determine the appropriate host address
179 // for setting the LOCATION header
createMulticastSocket4(const struct sockaddr_in * srcaddr,std::string & lochost)180 static SOCKET createMulticastSocket4(
181 const struct sockaddr_in *srcaddr, std::string& lochost)
182 {
183 char ttl = 2;
184 #ifdef _WIN32
185 BOOL bcast = TRUE;
186 #else
187 int bcast = 1;
188 #endif
189 const NetIF::IPAddr
190 ipaddr(reinterpret_cast<const struct sockaddr *>(srcaddr));
191 lochost = ipaddr.straddr();
192 SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
193 if (sock == INVALID_SOCKET) {
194 return INVALID_SOCKET;
195 }
196 uint32_t srcAddr = srcaddr->sin_addr.s_addr;
197 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
198 reinterpret_cast<char *>(&srcAddr), sizeof(srcAddr)) < 0) {
199 goto error;
200 }
201 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
202 goto error;
203 }
204 if(setsockopt(sock, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char *>(&bcast), sizeof(bcast)) < 0) {
205 goto error;
206 }
207 if (bind(sock, reinterpret_cast<const struct sockaddr *>(srcaddr),
208 sizeof(struct sockaddr_in)) < 0) {
209 goto error;
210 }
211 return sock;
212 error:
213 UpnpCloseSocket(sock);
214 return INVALID_SOCKET;
215 }
216
createReplySocket4(struct sockaddr_in * destaddr,std::string & lochost)217 static SOCKET createReplySocket4(
218 struct sockaddr_in *destaddr, std::string& lochost)
219 {
220 SOCKET sock = socket(static_cast<int>(destaddr->sin_family), SOCK_DGRAM, 0);
221 if (sock == INVALID_SOCKET) {
222 return INVALID_SOCKET;
223 }
224 // Determine the proper interface and compute the location string
225 NetIF::IPAddr dipaddr(reinterpret_cast<struct sockaddr*>(destaddr));
226 NetIF::IPAddr hostaddr;
227 const auto netif =
228 NetIF::Interfaces::interfaceForAddress(dipaddr, g_netifs, hostaddr);
229 if (netif && hostaddr.ok()) {
230 lochost = hostaddr.straddr();
231 } else {
232 lochost = apiFirstIPV4Str();
233 }
234 return sock;
235 }
236
237
238 #ifdef UPNP_ENABLE_IPV6
strInBrackets(const std::string & straddr)239 static std::string strInBrackets(const std::string& straddr)
240 {
241 return std::string("[") + straddr + "]";
242 }
createMulticastSocket6(int index,std::string & lochost)243 static SOCKET createMulticastSocket6(int index, std::string& lochost)
244 {
245 int hops = 1;
246 SOCKET sock = socket(AF_INET6, SOCK_DGRAM, 0);
247 if (sock == INVALID_SOCKET) {
248 return INVALID_SOCKET;
249 }
250 if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
251 reinterpret_cast<char *>(&index), sizeof(index)) < 0) {
252 goto error;
253 }
254 if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
255 reinterpret_cast<char *>(&hops), sizeof(hops)) < 0) {
256 goto error;
257 }
258 lochost.clear();
259 for (const auto& netif : g_netifs) {
260 if (netif.getindex() == index) {
261 const auto ipaddr =
262 netif.firstipv6addr(NetIF::IPAddr::Scope::LINK);
263 if (ipaddr) {
264 lochost = strInBrackets(ipaddr->straddr());
265 break;
266 }
267 }
268 }
269 if (lochost.empty()) {
270 lochost = strInBrackets(apiFirstIPV6Str());
271 }
272 return sock;
273 error:
274 UpnpCloseSocket(sock);
275 return INVALID_SOCKET;
276 }
277
createReplySocket6(struct sockaddr_in6 * destaddr,std::string & lochost)278 static SOCKET createReplySocket6(
279 struct sockaddr_in6 *destaddr, std::string& lochost)
280 {
281 SOCKET sock = socket(AF_INET6, SOCK_DGRAM, 0);
282 if (sock == INVALID_SOCKET) {
283 return INVALID_SOCKET;
284 }
285 int index = destaddr->sin6_scope_id;
286 lochost.clear();
287 for (const auto& netif : g_netifs) {
288 if (netif.getindex() == index) {
289 const auto ipaddr =
290 netif.firstipv6addr(NetIF::IPAddr::Scope::LINK);
291 if (ipaddr) {
292 lochost = strInBrackets(ipaddr->straddr());
293 break;
294 }
295 }
296 }
297 if (lochost.empty()) {
298 lochost = strInBrackets(apiFirstIPV6Str());
299 }
300 return sock;
301 }
302 #else // ! ENABLE_IPV6 ->
createReplySocket6(struct sockaddr_in6 * destaddr,std::string & lochost)303 static SOCKET createReplySocket6(
304 struct sockaddr_in6 *destaddr, std::string& lochost)
305 {
306 return INVALID_SOCKET;
307 }
308 #endif // ! ENABLE_IPV6
309
310 // Set the UPnP predefined multicast destination addresses
ssdpMcastAddr(struct sockaddr_storage & ss,int AddressFamily)311 static bool ssdpMcastAddr(struct sockaddr_storage& ss, int AddressFamily)
312 {
313 ss = {};
314 switch (AddressFamily) {
315 case AF_INET:
316 {
317 auto DestAddr4 = reinterpret_cast<struct sockaddr_in *>(&ss);
318 DestAddr4->sin_family = static_cast<sa_family_t>(AF_INET);
319 inet_pton(AF_INET, SSDP_IP, &DestAddr4->sin_addr);
320 DestAddr4->sin_port = htons(SSDP_PORT);
321 }
322 break;
323 case AF_INET6:
324 {
325 auto DestAddr6 = reinterpret_cast<struct sockaddr_in6 *>(&ss);
326 DestAddr6->sin6_family = static_cast<sa_family_t>(AF_INET6);
327 inet_pton(AF_INET6, SSDP_IPV6_LINKLOCAL, &DestAddr6->sin6_addr);
328 DestAddr6->sin6_port = htons(SSDP_PORT);
329 }
330 break;
331 default:
332 return false;
333 }
334 return true;
335 }
336
sendPackets(SOCKET sock,struct sockaddr * daddr,int cnt,std::string * pckts)337 static int sendPackets(
338 SOCKET sock, struct sockaddr *daddr, int cnt, std::string *pckts)
339 {
340 NetIF::IPAddr destip(daddr);
341 int socklen = daddr->sa_family == AF_INET ? sizeof(struct sockaddr_in) :
342 sizeof(struct sockaddr_in6);
343
344 for (int i = 0; i < cnt; i++) {
345 ssize_t rc;
346 UpnpPrintf(UPNP_DEBUG, SSDP, __FILE__, __LINE__,
347 ">>> SSDP SEND to %s >>>\n%s\n",
348 destip.straddr().c_str(), pckts[i].c_str());
349 rc = sendto(sock, pckts[i].c_str(), pckts[i].size(), 0, daddr, socklen);
350
351 if (rc == -1) {
352 char errorBuffer[ERROR_BUFFER_LEN];
353 posix_strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
354 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
355 "sendPackets: sendto: %s\n", errorBuffer);
356 return UPNP_E_SOCKET_WRITE;
357
358 }
359 }
360 return UPNP_E_SUCCESS;
361 }
362
363
364 /* Creates a device notify or search reply packet. */
CreateServicePacket(SSDPDevMessageType msg_type,const char * nt,const char * usn,const std::string & location,int duration,std::string & packet,int AddressFamily,SSDPPwrState & pwr,const std::string & prodvers)365 static void CreateServicePacket(
366 SSDPDevMessageType msg_type, const char *nt, const char *usn,
367 const std::string& location, int duration, std::string &packet,
368 int AddressFamily, SSDPPwrState& pwr, const std::string& prodvers)
369 {
370 std::ostringstream str;
371 switch (msg_type) {
372 case MSGTYPE_REPLY:
373 str <<
374 "HTTP/1.1 " << HTTP_OK << " OK\r\n" <<
375 "CACHE-CONTROL: max-age=" << duration << "\r\n" <<
376 "DATE: " << make_date_string(0) << "\r\n" <<
377 "EXT:\r\n" <<
378 "LOCATION: " << location << "\r\n" <<
379 "SERVER: " << get_sdk_device_info(prodvers) << "\r\n" <<
380 #ifdef UPNP_HAVE_OPTSSDP
381 "OPT: " << R"("http://schemas.upnp.org/upnp/1/0/"; ns=01)" << "\r\n" <<
382 "01-NLS: " << gUpnpSdkNLSuuid << "\r\n" <<
383 "X-User-Agent: " << X_USER_AGENT << "\r\n" <<
384 #endif
385 "ST: " << nt << "\r\n" <<
386 "USN: " << usn << "\r\n";
387 break;
388 case MSGTYPE_ADVERTISEMENT:
389 case MSGTYPE_SHUTDOWN:
390 {
391 const char *nts = (msg_type == MSGTYPE_ADVERTISEMENT) ?
392 "ssdp:alive" : "ssdp:byebye";
393
394 /* NOTE: The CACHE-CONTROL and LOCATION headers are not present in
395 * a shutdown msg, but are present here for MS WinMe interop. */
396 const char *host;
397 switch (AddressFamily) {
398 case AF_INET: host = SSDP_IP; break;
399 default: host = "[" SSDP_IPV6_LINKLOCAL "]";
400 }
401
402 str <<
403 "NOTIFY * HTTP/1.1\r\n" <<
404 "HOST: " << host << ":" << SSDP_PORT << "\r\n" <<
405 "CACHE-CONTROL: max-age=" << duration << "\r\n" <<
406 "LOCATION: " << location << "\r\n" <<
407 "SERVER: " << get_sdk_device_info(prodvers) << "\r\n" <<
408 #ifdef UPNP_HAVE_OPTSSDP
409 "OPT: " << R"("http://schemas.upnp.org/upnp/1/0/"; ns=01)" << "\r\n" <<
410 "01-NLS: " << gUpnpSdkNLSuuid << "\r\n" <<
411 "X-User-Agent: " << X_USER_AGENT << "\r\n" <<
412 #endif
413 "NT: " << nt << "\r\n" <<
414 "NTS: " << nts << "\r\n" <<
415 "USN: " << usn << "\r\n";
416 }
417 break;
418 default:
419 std::cerr << "Unknown message type in CreateServicePacket\n";
420 abort();
421 }
422
423 if (pwr.PowerState > 0) {
424 str <<
425 "Powerstate: " << pwr.PowerState << "\r\n" <<
426 "SleepPeriod: " << pwr.SleepPeriod << "\r\n" <<
427 "RegistrationState: " << pwr.RegistrationState << "\r\n";
428 }
429
430 str << "\r\n";
431 packet = str.str();
432 }
433
DeviceAdvertisementOrShutdown(SSDPCommonData & sscd,SSDPDevMessageType msgtype,const char * DevType,int RootDev,const char * Udn,const std::string & Location,int Duration)434 static int DeviceAdvertisementOrShutdown(
435 SSDPCommonData& sscd, SSDPDevMessageType msgtype, const char *DevType,
436 int RootDev, const char *Udn, const std::string& Location, int Duration)
437 {
438 char Mil_Usn[LINE_SIZE];
439 std::string msgs[3];
440 int ret_code = UPNP_E_OUTOF_MEMORY;
441 int rc = 0;
442
443 /* If device is a root device, we need to send 3 messages */
444 if (RootDev) {
445 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::upnp:rootdevice", Udn);
446 if (rc < 0 || static_cast<unsigned int>(rc) >= sizeof(Mil_Usn))
447 goto error_handler;
448 CreateServicePacket(msgtype, "upnp:rootdevice",
449 Mil_Usn, Location, Duration, msgs[0],
450 sscd.DestAddr->sa_family, sscd.pwr, sscd.prodvers);
451 }
452 /* both root and sub-devices need to send these two messages */
453 CreateServicePacket(msgtype, Udn, Udn,
454 Location, Duration, msgs[1], sscd.DestAddr->sa_family,
455 sscd.pwr, sscd.prodvers);
456 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::%s", Udn, DevType);
457 if (rc < 0 || static_cast<unsigned int>(rc) >= sizeof(Mil_Usn))
458 goto error_handler;
459 CreateServicePacket(msgtype, DevType, Mil_Usn, Location, Duration, msgs[2],
460 sscd.DestAddr->sa_family, sscd.pwr, sscd.prodvers);
461 /* check error */
462 if ((RootDev && msgs[0].empty()) || msgs[1].empty() || msgs[2].empty()) {
463 goto error_handler;
464 }
465 /* send packets */
466 if (RootDev) {
467 /* send 3 msg types */
468 ret_code = sendPackets(sscd.sock, sscd.DestAddr, 3, &msgs[0]);
469 } else { /* sub-device */
470 /* send 2 msg types */
471 ret_code = sendPackets(sscd.sock, sscd.DestAddr, 2, &msgs[1]);
472 }
473
474 error_handler:
475 return ret_code;
476 }
477
SendReply(SSDPCommonData & sscd,const char * DevType,int RootDev,const char * Udn,const std::string & Location,int Duration,int ByType)478 static int SendReply(
479 SSDPCommonData& sscd, const char *DevType, int RootDev,
480 const char *Udn, const std::string& Location, int Duration, int ByType)
481 {
482 int ret_code = UPNP_E_OUTOF_MEMORY;
483 std::string msgs[2];
484 int num_msgs;
485 char Mil_Usn[LINE_SIZE];
486 int i;
487 int rc = 0;
488 int family = static_cast<int>(sscd.DestAddr->sa_family);
489
490 if (RootDev) {
491 /* one msg for root device */
492 num_msgs = 1;
493
494 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::upnp:rootdevice", Udn);
495 if (rc < 0 || static_cast<unsigned int>(rc) >= sizeof(Mil_Usn))
496 goto error_handler;
497 CreateServicePacket(MSGTYPE_REPLY, "upnp:rootdevice", Mil_Usn, Location,
498 Duration, msgs[0], family, sscd.pwr, sscd.prodvers);
499 } else {
500 /* two msgs for embedded devices */
501 num_msgs = 1;
502
503 /*NK: FIX for extra response when someone searches by udn */
504 if (!ByType) {
505 CreateServicePacket(MSGTYPE_REPLY, Udn, Udn, Location, Duration,
506 msgs[0], family, sscd.pwr, sscd.prodvers);
507 } else {
508 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::%s", Udn, DevType);
509 if (rc < 0 || static_cast<unsigned int>(rc) >= sizeof(Mil_Usn))
510 goto error_handler;
511 CreateServicePacket(MSGTYPE_REPLY, DevType, Mil_Usn, Location,
512 Duration, msgs[0],family,sscd.pwr,sscd.prodvers);
513 }
514 }
515 /* check error */
516 for (i = 0; i < num_msgs; i++) {
517 if (msgs[i].empty()) {
518 goto error_handler;
519 }
520 }
521
522 ret_code = sendPackets(sscd.sock, sscd.DestAddr, num_msgs, msgs);
523
524 error_handler:
525 return ret_code;
526 }
527
DeviceReply(SSDPCommonData & sscd,const char * DevType,int RootDev,const char * Udn,const std::string & Location,int Duration)528 static int DeviceReply(
529 SSDPCommonData& sscd, const char *DevType, int RootDev,
530 const char *Udn, const std::string& Location, int Duration)
531 {
532 std::string szReq[3];
533 char Mil_Nt[LINE_SIZE], Mil_Usn[LINE_SIZE];
534 int rc = 0;
535 int family = static_cast<int>(sscd.DestAddr->sa_family);
536
537 /* create 2 or 3 msgs */
538 if (RootDev) {
539 /* 3 replies for root device */
540 upnp_strlcpy(Mil_Nt, "upnp:rootdevice", sizeof(Mil_Nt));
541 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::upnp:rootdevice", Udn);
542 if (rc < 0 || static_cast<unsigned int>(rc) >= sizeof(Mil_Usn))
543 return UPNP_E_OUTOF_MEMORY;
544 CreateServicePacket(MSGTYPE_REPLY, Mil_Nt, Mil_Usn, Location,
545 Duration, szReq[0], family, sscd.pwr, sscd.prodvers);
546 }
547 rc = snprintf(Mil_Nt, sizeof(Mil_Nt), "%s", Udn);
548 if (rc < 0 || static_cast<unsigned int>(rc) >= sizeof(Mil_Nt))
549 return UPNP_E_OUTOF_MEMORY;
550 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s", Udn);
551 if (rc < 0 || static_cast<unsigned int>(rc) >= sizeof(Mil_Usn))
552 return UPNP_E_OUTOF_MEMORY;
553 CreateServicePacket(MSGTYPE_REPLY, Mil_Nt, Mil_Usn, Location, Duration,
554 szReq[1], family, sscd.pwr, sscd.prodvers);
555 rc = snprintf(Mil_Nt, sizeof(Mil_Nt), "%s", DevType);
556 if (rc < 0 || static_cast<unsigned int>(rc) >= sizeof(Mil_Nt))
557 return UPNP_E_OUTOF_MEMORY;
558 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::%s", Udn, DevType);
559 if (rc < 0 || static_cast<unsigned int>(rc) >= sizeof(Mil_Usn))
560 return UPNP_E_OUTOF_MEMORY;
561 CreateServicePacket(MSGTYPE_REPLY, Mil_Nt, Mil_Usn, Location, Duration,
562 szReq[2], family, sscd.pwr, sscd.prodvers);
563 /* check error */
564 if ((RootDev && szReq[0].empty()) || szReq[1].empty() || szReq[2].empty()) {
565 return UPNP_E_OUTOF_MEMORY;
566 }
567 /* send replies */
568 if (RootDev) {
569 return sendPackets(sscd.sock, sscd.DestAddr, 3, szReq);
570 }
571 return sendPackets(sscd.sock, sscd.DestAddr, 2, &szReq[1]);
572 }
573
ServiceSend(SSDPCommonData & sscd,SSDPDevMessageType tp,const char * ServType,const char * Udn,const std::string & Location,int Duration)574 static int ServiceSend(
575 SSDPCommonData& sscd, SSDPDevMessageType tp, const char *ServType,
576 const char *Udn, const std::string& Location, int Duration)
577 {
578 char Mil_Usn[LINE_SIZE];
579 std::string szReq[1];
580 int rc = 0;
581
582 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::%s", Udn, ServType);
583 if (rc < 0 || static_cast<unsigned int>(rc) >= sizeof(Mil_Usn))
584 return UPNP_E_OUTOF_MEMORY;
585 CreateServicePacket(tp, ServType, Mil_Usn, Location, Duration, szReq[0],
586 sscd.DestAddr->sa_family, sscd.pwr, sscd.prodvers);
587 if (szReq[0].empty()) {
588 return UPNP_E_OUTOF_MEMORY;
589
590 }
591
592 return sendPackets(sscd.sock, sscd.DestAddr, 1, szReq);
593 }
594
replaceLochost(std::string & location,const std::string & lochost)595 static void replaceLochost(std::string& location, const std::string& lochost)
596 {
597 std::string::size_type pos = location.find(g_HostForTemplate);
598 if (pos != std::string::npos) {
599 location.replace(pos, g_HostForTemplate.size(), lochost);
600 }
601 }
602
servOrDevVers(const char * in)603 static int servOrDevVers(const char *in)
604 {
605 const char *cp = std::strrchr(in, ':');
606 if (nullptr == cp)
607 return 0;
608 cp++;
609 if (*cp != 0) {
610 return std::atoi(cp);
611 }
612 return 0;
613 }
614
sameServOrDevNoVers(const char * his,const char * mine)615 static bool sameServOrDevNoVers(const char *his, const char *mine)
616 {
617 const char *cp = std::strrchr(mine, ':');
618 if (nullptr == cp) {
619 // ??
620 return !strcasecmp(his, mine);
621 }
622 return !strncasecmp(his, mine, cp - mine);
623 }
624
625 // Send SSDP messages for one root device, one destination address,
626 // which is the reply host or one of our source addresses. There may
627 // be subdevices
AdvertiseAndReplyOneDest(UpnpDevice_Handle Hnd,SSDPDevMessageType tp,int Exp,struct sockaddr * DestAddr,const SsdpEntity & sdata,SOCKET sock,const std::string & lochost)628 static int AdvertiseAndReplyOneDest(
629 UpnpDevice_Handle Hnd, SSDPDevMessageType tp, int Exp,
630 struct sockaddr *DestAddr, const SsdpEntity& sdata, SOCKET sock,
631 const std::string& lochost)
632 {
633 int retVal = UPNP_E_SUCCESS;
634 int defaultExp = DEFAULT_MAXAGE;
635 int NumCopy = 0;
636 std::vector<const UPnPDeviceDesc*> alldevices;
637 bool isNotify = (tp == MSGTYPE_ADVERTISEMENT || tp == MSGTYPE_SHUTDOWN);
638
639 /* Use a read lock */
640 HandleReadLock();
641 struct Handle_Info *SInfo = nullptr;
642 if (GetHandleInfo(Hnd, &SInfo) != HND_DEVICE) {
643 HandleUnlock();
644 return UPNP_E_INVALID_HANDLE;
645 }
646
647 defaultExp = SInfo->MaxAge;
648
649 struct SSDPCommonData sscd;
650 sscd.sock = sock;
651 sscd.DestAddr = DestAddr;
652 sscd.pwr = SSDPPwrState{
653 SInfo->PowerState, SInfo->SleepPeriod, SInfo->RegistrationState};
654 sscd.prodvers = SInfo->productversion;
655
656
657 std::string location{SInfo->DescURL};
658 replaceLochost(location, lochost);
659 std::string lowerloc{SInfo->LowerDescURL};
660 replaceLochost(lowerloc, lochost);
661
662 // Store pointers to the root and embedded devices in a single vector
663 // for later convenience of mostly identical processing.
664 alldevices.push_back(&SInfo->devdesc);
665 for (const auto& dev : SInfo->devdesc.embedded) {
666 alldevices.push_back(&dev);
667 }
668
669 /* send advertisements/replies */
670 while (NumCopy == 0 || (isNotify && NumCopy < NUM_SSDP_COPY)) {
671 if (NumCopy != 0)
672 std::this_thread::sleep_for(std::chrono::milliseconds(SSDP_PAUSE));
673 NumCopy++;
674
675 for (auto& devp : alldevices) {
676 bool isroot = &devp == &(*alldevices.begin());
677 const char *devType = devp->deviceType.c_str();
678 const char *UDNstr = devp->UDN.c_str();
679 if (isNotify) {
680 DeviceAdvertisementOrShutdown(
681 sscd, tp, devType, isroot, UDNstr, location, Exp);
682 } else {
683 switch (sdata.RequestType) {
684 case SSDP_ALL:
685 DeviceReply(sscd, devType, isroot, UDNstr,
686 location, defaultExp);
687 break;
688
689 case SSDP_ROOTDEVICE:
690 if (isroot) {
691 SendReply(sscd, devType, 1, UDNstr,
692 location, defaultExp, 0);
693 }
694 break;
695
696 case SSDP_DEVICEUDN:
697 if (!strcasecmp(sdata.UDN.c_str(), UDNstr)) {
698 UpnpPrintf(UPNP_DEBUG, SSDP, __FILE__, __LINE__,
699 "DeviceUDN=%s/search UDN=%s MATCH\n",
700 UDNstr, sdata.UDN.c_str());
701 SendReply(sscd, devType, 0, UDNstr, location,
702 defaultExp, 0);
703 } else {
704 UpnpPrintf(UPNP_DEBUG, SSDP, __FILE__, __LINE__,
705 "DeviceUDN=%s/search UDN=%s NOMATCH\n",
706 UDNstr, sdata.UDN.c_str());
707 }
708 break;
709
710 case SSDP_DEVICETYPE: {
711 const char *dt = sdata.DeviceType.c_str();
712 if (sameServOrDevNoVers(dt, devType)) {
713 int myvers = servOrDevVers(devType);
714 int hisvers = servOrDevVers(dt);
715 if (hisvers < myvers) {
716 /* the requested version is lower than the
717 device version must reply with the
718 lower version number and the lower
719 description URL */
720 UpnpPrintf(UPNP_DEBUG, SSDP, __FILE__, __LINE__,
721 "DeviceType=%s/srchdevType=%s MATCH\n",
722 devType, dt);
723 SendReply(sscd, dt, 0, UDNstr,
724 lowerloc, defaultExp, 1);
725 } else if (hisvers == myvers) {
726 UpnpPrintf(UPNP_DEBUG, SSDP, __FILE__, __LINE__,
727 "DeviceType=%s/srchDevType=%s MATCH\n",
728 devType, dt);
729 SendReply(sscd, dt, 0,
730 UDNstr, location, defaultExp, 1);
731 } else {
732 UpnpPrintf(UPNP_DEBUG, SSDP, __FILE__, __LINE__,
733 "DeviceType=%s/srchDevType=%s NOMATCH\n",
734 devType, dt);
735 }
736 } else {
737 UpnpPrintf(UPNP_DEBUG, SSDP, __FILE__, __LINE__,
738 "DeviceType=%s /srchdevType=%s NOMATCH\n",
739 devType, dt);
740 }
741 }
742 break;
743
744 default:
745 break;
746 }
747 }
748
749 /* send service advertisements for services corresponding
750 * to the same device */
751 UpnpPrintf(UPNP_DEBUG, SSDP, __FILE__, __LINE__,
752 "Sending service advertisements\n");
753 /* Correct service traversal such that each device's serviceList
754 * is directly traversed as a child of its parent device. This
755 * ensures that the service's alive message uses the UDN of
756 * the parent device. */
757 for (const auto& service : devp->services) {
758 const char *servType = service.serviceType.c_str();
759 UpnpPrintf(UPNP_DEBUG, SSDP, __FILE__, __LINE__,
760 "ServiceType = %s\n", servType);
761 if (isNotify) {
762 ServiceSend(sscd, tp, servType, UDNstr, location, Exp);
763 } else {
764 switch (sdata.RequestType) {
765 case SSDP_ALL:
766 ServiceSend(sscd, MSGTYPE_REPLY, servType,
767 UDNstr, location, defaultExp);
768 break;
769
770 case SSDP_SERVICE: {
771 const char *sst = sdata.ServiceType.c_str();
772 if (sameServOrDevNoVers(sst, servType)) {
773 int myvers = servOrDevVers(servType);
774 int hisvers = servOrDevVers(sst);
775 if (hisvers < myvers) {
776 /* the requested version is lower
777 than the service version must
778 reply with the lower version
779 number and the lower
780 description URL */
781 UpnpPrintf(
782 UPNP_DEBUG, SSDP, __FILE__, __LINE__,
783 "ServiceTp=%s/searchServTp=%s MATCH\n",
784 sst, servType);
785 SendReply(sscd, sst, 0, UDNstr, lowerloc,
786 defaultExp, 1);
787 } else if (hisvers == myvers) {
788 UpnpPrintf(
789 UPNP_DEBUG, SSDP, __FILE__, __LINE__,
790 "ServiceTp=%s/searchServTp=%s MATCH\n",
791 sst, servType);
792 SendReply(sscd, sst, 0, UDNstr,
793 location, defaultExp, 1);
794 } else {
795 UpnpPrintf(
796 UPNP_DEBUG, SSDP, __FILE__, __LINE__,
797 "ServiceTp=%s/srchServTp=%s NO MATCH\n",
798 sst, servType);
799 }
800 } else {
801 UpnpPrintf(
802 UPNP_DEBUG, SSDP, __FILE__, __LINE__,
803 "ServiceTp=%s/srchServTp=%s NO MATCH\n",
804 sst, servType);
805 }
806 }
807 break;
808
809 default:
810 break;
811 }
812 }
813 }
814 }
815 }
816
817 UpnpPrintf(UPNP_ALL, SSDP, __FILE__, __LINE__, "AdvertiseAndReply1 exit\n");
818 HandleUnlock();
819 return retVal;
820 }
821
822
823 // Process the advertisements or replies for one root device (which
824 // may have subdevices).
825 //
826 // This top routine calls AdvertiseAndReplyOneDest() to do the real
827 // work for each of the appropriate network addresses/interfaces.
828
AdvertiseAndReply(UpnpDevice_Handle Hnd,SSDPDevMessageType tp,int Exp,struct sockaddr * repDestAddr,const SsdpEntity & sdata)829 int AdvertiseAndReply(UpnpDevice_Handle Hnd, SSDPDevMessageType tp, int Exp,
830 struct sockaddr *repDestAddr, const SsdpEntity& sdata)
831 {
832 bool isNotify = (tp == MSGTYPE_ADVERTISEMENT || tp == MSGTYPE_SHUTDOWN);
833 int ret = UPNP_E_SUCCESS;
834 std::string lochost;
835 SOCKET sock = INVALID_SOCKET;
836
837 if (isNotify) {
838 // Loop on our interfaces and addresses
839 for (const auto& netif : g_netifs) {
840 UpnpPrintf(UPNP_ALL, SSDP, __FILE__, __LINE__,
841 "ssdp_device: mcast for %s\n", netif.getname().c_str());
842
843 struct sockaddr_storage dss;
844 auto destaddr = reinterpret_cast<struct sockaddr*>(&dss);
845
846 #ifdef UPNP_ENABLE_IPV6
847 if (using_ipv6()) {
848 ssdpMcastAddr(dss, AF_INET6);
849 sock = createMulticastSocket6(netif.getindex(), lochost);
850 if (sock == INVALID_SOCKET) {
851 goto exitfunc;
852 }
853
854 ret = AdvertiseAndReplyOneDest(
855 Hnd, tp, Exp, destaddr, sdata, sock, lochost);
856
857 if (ret != UPNP_E_SUCCESS) {
858 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
859 "SSDP dev: IPV6 SEND failed for %s\n",
860 netif.getname().c_str());
861 goto exitfunc;
862 }
863 UpnpCloseSocket(sock);
864 sock = INVALID_SOCKET;
865 }
866 #endif /* UPNP_ENABLE_IPV6 */
867
868 ssdpMcastAddr(dss, AF_INET);
869 auto addresses = netif.getaddresses();
870 for (const auto& ipaddr : addresses.first) {
871 if (ipaddr.family() == NetIF::IPAddr::Family::IPV4) {
872 const struct sockaddr_storage& fss{ipaddr.getaddr()};
873 sock = createMulticastSocket4(
874 reinterpret_cast<const struct sockaddr_in*>(&fss),
875 lochost);
876 } else {
877 continue;
878 }
879 if (sock == INVALID_SOCKET) {
880 goto exitfunc;
881 }
882 ret = AdvertiseAndReplyOneDest(
883 Hnd, tp, Exp, destaddr, sdata, sock, lochost);
884
885 UpnpCloseSocket(sock);
886 sock = INVALID_SOCKET;
887 }
888 }
889 } else {
890 sock = repDestAddr->sa_family == AF_INET ?
891 createReplySocket4(
892 reinterpret_cast<struct sockaddr_in*>(repDestAddr), lochost) :
893 createReplySocket6(
894 reinterpret_cast<struct sockaddr_in6*>(repDestAddr), lochost);
895 if (sock == INVALID_SOCKET) {
896 goto exitfunc;
897 }
898 ret = AdvertiseAndReplyOneDest(
899 Hnd, tp, Exp, repDestAddr, sdata, sock, lochost);
900 }
901
902 exitfunc:
903 if (ret != UPNP_E_SUCCESS) {
904 char errorBuffer[ERROR_BUFFER_LEN];
905 posix_strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
906 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
907 "sendPackets: %s\n", errorBuffer);
908 return UPNP_E_NETWORK_ERROR;
909 }
910 if (sock != INVALID_SOCKET)
911 UpnpCloseSocket(sock);
912 return ret;
913 }
914
915 #endif /* EXCLUDE_SSDP */
916 #endif /* INCLUDE_DEVICE_APIS */
917