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