UPnPsdk 0.1
Universal Plug and Play +, Software Development Kit
 
Loading...
Searching...
No Matches
ssdp_device.cpp
Go to the documentation of this file.
1/**************************************************************************
2 *
3 * Copyright (c) 2000-2003 Intel Corporation
4 * All rights reserved.
5 * Copyright (C) 2011-2012 France Telecom All rights reserved.
6 * Copyright (C) 2022+ GPL 3 and higher by Ingo Höft, <Ingo@Hoeft-online.de>
7 * Redistribution only with this Copyright remark. Last modified: 2026-03-19
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * - Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 * - Neither name of Intel Corporation nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
29 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 **************************************************************************/
34// Last update from ./pupnp source file on 2025-07-26, ver 1.14.23
43#include <ssdp_device.hpp>
44
45#include <httpreadwrite.hpp>
46#include <statcodes.hpp>
47#include <upnpapi.hpp>
48#include <webserver.hpp>
49
50#include <UPnPsdk/socket.hpp>
51#include <UPnPsdk/sockaddr.hpp>
52
53#include <umock/sys_socket.hpp>
54#include <umock/netdb.hpp>
55
56#ifndef COMPA_INTERNAL_CONFIG_HPP
57#error "No or wrong config.hpp header file included."
58#endif
59
61#include <cassert>
62#include <thread>
63#ifdef _MSC_VER
64#else
65#include <net/if.h>
66#endif
68
69
70namespace {
71
76constexpr int MSGTYPE_SHUTDOWN{0};
77constexpr int MSGTYPE_ADVERTISEMENT{1};
78constexpr int MSGTYPE_REPLY{2};
81
85int send_stateless(sockaddr* a_dest_saddr, int a_num_packet,
86 char** a_rq_packet) {
87 if (a_dest_saddr == nullptr || a_rq_packet == nullptr)
89
90 if (a_num_packet <= 0 || *a_rq_packet == nullptr) {
91 UPnPsdk_LOGINFO("MSG1163") "nothing to send.\n";
92 return UPNP_E_SUCCESS;
93 }
94
95 UPnPsdk::CSocket sockObj;
96 try {
97 sockObj.bind(SOCK_DGRAM, nullptr, AI_PASSIVE);
98 } catch (const std::exception& ex) {
99 UPnPsdk_LOGCATCH("MSG1166") "catched next line...\n" << ex.what();
100 return UPNP_E_SOCKET_ERROR;
101 }
102
103 if (UPnPsdk::g_dbug) {
104 UPnPsdk::SSockaddr saObj;
105 saObj = *reinterpret_cast<sockaddr_storage*>(a_dest_saddr);
106 UPnPsdk_LOGINFO("MSG1154") "syscall ::sendto() \""
107 << saObj.netaddrp() << "\", " << a_num_packet << " messages.\n";
108 }
109 UPnPsdk::CSocketErr serrObj;
110 for (int index{0}; index < a_num_packet; index++) {
111 // Ignore invalid or empty strings.
112 if ((*(a_rq_packet + index) == nullptr) ||
113 (**(a_rq_packet + index) == '\0'))
114 continue;
115
116 // Send data. The sent string is not zero terminated.
117 ssize_t bytes_sent = ::sendto(sockObj, *(a_rq_packet + index),
118 (SIZEP_T)strlen(*(a_rq_packet + index)),
119 0, a_dest_saddr, sizeof(sockaddr_in6));
120 if (bytes_sent == SOCKET_ERROR) {
121 serrObj.catch_error();
122 UPnPsdk_LOGERR("MSG1161") "syscall ::sendto() fails with errid="
123 << serrObj << " - " << serrObj.error_str() << '\n';
124 return UPNP_E_SOCKET_WRITE;
125 }
126 }
127
128 return UPNP_E_SUCCESS;
129}
130
145 struct sockaddr* a_dest_saddr,
147 int a_num_packet,
149 char** a_rq_packet) {
150 if (a_dest_saddr == nullptr)
152
153 switch (a_dest_saddr->sa_family) {
154 case AF_INET6:
155 return send_stateless(a_dest_saddr, a_num_packet, a_rq_packet);
156 // case AF_INET:
157 // return send_stateless_ip4(a_dest_saddr, a_num_packet, a_rq_packet,
158 // a_ttl);
159 case AF_UNSPEC:
160 UPnPsdk_LOGINFO(
161 "MSG1158") "Empty destination address specified. No stateless "
162 "message sent. Continue without error.\n";
163 return UPNP_E_SUCCESS;
164 default:
165 UPnPsdk_LOGCRIT(
166 "MSG1162") "Invalid destination internet address-family="
167 << a_dest_saddr->sa_family << " specified.\n";
168 } // switch
169
171}
172
178inline int extractIPv6address( //
179 char* url,
180 char* address
181) {
182 int i = 0;
183 int j = 0;
184 int ret = 0;
185
186 while (url[i] != '[' && url[i] != '\0') {
187 i++;
188 }
189 if (url[i] == '\0') {
190 goto exit_function;
191 }
192
193 /* bracket has been found, we deal with an IPv6 address */
194 i++;
195 while (url[i] != '\0' && url[i] != ']') {
196 address[j] = url[i];
197 i++;
198 j++;
199 }
200 if (url[i] == '\0') {
201 goto exit_function;
202 }
203
204 if (url[i] == ']') {
205 address[j] = '\0';
206 ret = 1;
207 }
208
209exit_function:
210 return ret;
211}
212
220 char* descdocUrl
221) {
222 char address[INET6_ADDRSTRLEN];
223 struct in6_addr v6_addr;
224
225 if (extractIPv6address(descdocUrl, address)) {
226 inet_pton(AF_INET6, address, &v6_addr);
227 return !IN6_IS_ADDR_LINKLOCAL(&v6_addr);
228 }
229
230 return 0;
231}
232
242 int msg_type,
244 const char* nt,
246 char* usn,
248 char* location,
250 int duration,
252 char** packet,
254 int AddressFamily,
256 int PowerState,
258 int SleepPeriod,
260 int RegistrationState) {
261 int ret_code;
262 const char* nts;
263 membuffer buf;
264
265 /* Notf == 0 means service shutdown,
266 * Notf == 1 means service advertisement,
267 * Notf == 2 means reply */
268 membuffer_init(&buf);
269 buf.size_inc = (size_t)30;
270 *packet = NULL;
271 if (msg_type == MSGTYPE_REPLY) {
272 if (PowerState > 0) {
273#ifdef COMPA_HAVE_OPTION_SSDP
274 ret_code = http_MakeMessage(
275 &buf, 1, 1,
276 "R"
277 "sdc"
278 "D"
279 "sc"
280 "ssc"
281 "ssc"
282 "ssc"
283 "S"
284 "Xc"
285 "ssc"
286 "ssc"
287 "sdc"
288 "sdc"
289 "sdcc",
290 HTTP_OK, "CACHE-CONTROL: max-age=", duration,
291 "EXT:", "LOCATION: ", location,
292 "OPT: ", "\"http://schemas.upnp.org/upnp/1/0/\"; ns=01",
293 "01-NLS: ", gUpnpSdkNLSuuid, X_USER_AGENT, "ST: ", nt,
294 "USN: ", usn, "Powerstate: ", PowerState,
295 "SleepPeriod: ", SleepPeriod,
296 "RegistrationState: ", RegistrationState);
297#else
298 ret_code = http_MakeMessage(
299 &buf, 1, 1,
300 "R"
301 "sdc"
302 "D"
303 "sc"
304 "ssc"
305 "S"
306 "ssc"
307 "ssc"
308 "sdc"
309 "sdc"
310 "sdcc",
311 HTTP_OK, "CACHE-CONTROL: max-age=", duration,
312 "EXT:", "LOCATION: ", location, "ST: ", nt, "USN: ", usn,
313 "Powerstate: ", PowerState, "SleepPeriod: ", SleepPeriod,
314 "RegistrationState: ", RegistrationState);
315#endif /* COMPA_HAVE_OPTION_SSDP */
316 } else {
317#ifdef COMPA_HAVE_OPTION_SSDP
318 ret_code = http_MakeMessage(
319 &buf, 1, 1,
320 "R"
321 "sdc"
322 "D"
323 "sc"
324 "ssc"
325 "ssc"
326 "ssc"
327 "S"
328 "Xc"
329 "ssc"
330 "sscc",
331 HTTP_OK, "CACHE-CONTROL: max-age=", duration,
332 "EXT:", "LOCATION: ", location,
333 "OPT: ", "\"http://schemas.upnp.org/upnp/1/0/\"; ns=01",
334 "01-NLS: ", gUpnpSdkNLSuuid, X_USER_AGENT, "ST: ", nt,
335 "USN: ", usn);
336#else
337 ret_code = http_MakeMessage(
338 &buf, 1, 1,
339 "R"
340 "sdc"
341 "D"
342 "sc"
343 "ssc"
344 "S"
345 "ssc"
346 "sscc",
347 HTTP_OK, "CACHE-CONTROL: max-age=", duration,
348 "EXT:", "LOCATION: ", location, "ST: ", nt, "USN: ", usn);
349#endif /* COMPA_HAVE_OPTION_SSDP */
350 }
351 if (ret_code != 0) {
352 return;
353 }
354 } else if (msg_type == MSGTYPE_ADVERTISEMENT ||
355 msg_type == MSGTYPE_SHUTDOWN) {
356 const char* host = NULL;
357
358 if (msg_type == MSGTYPE_ADVERTISEMENT)
359 nts = "ssdp:alive";
360 else
361 /* shutdown */
362 nts = "ssdp:byebye";
363 /* NOTE: The CACHE-CONTROL and LOCATION headers are not present in a
364 * shutdown msg, but are present here for MS WinMe interop. */
365 switch (AddressFamily) {
366 case AF_INET:
367 host = SSDP_IP;
368 break;
369 default:
370 if (isUrlV6UlaGua(location))
371 host = "[" SSDP_IPV6_SITELOCAL "]";
372 else
373 host = "[" SSDP_IPV6_LINKLOCAL "]";
374 }
375 if (PowerState > 0) {
376#ifdef COMPA_HAVE_OPTION_SSDP
377 ret_code = http_MakeMessage(
378 &buf, 1, 1,
379 "Q"
380 "sssdc"
381 "sdc"
382 "ssc"
383 "ssc"
384 "ssc"
385 "ssc"
386 "ssc"
387 "S"
388 "Xc"
389 "ssc"
390 "sdc"
391 "sdc"
392 "sdcc",
393 HTTPMETHOD_NOTIFY, "*", (size_t)1, "HOST: ", host, ":",
394 SSDP_PORT, "CACHE-CONTROL: max-age=", duration,
395 "LOCATION: ", location,
396 "OPT: ", "\"http://schemas.upnp.org/upnp/1/0/\"; ns=01",
397 "01-NLS: ", gUpnpSdkNLSuuid, "NT: ", nt, "NTS: ", nts,
398 X_USER_AGENT, "USN: ", usn, "Powerstate: ", PowerState,
399 "SleepPeriod: ", SleepPeriod,
400 "RegistrationState: ", RegistrationState);
401#else
402 ret_code = http_MakeMessage(
403 &buf, 1, 1,
404 "Q"
405 "sssdc"
406 "sdc"
407 "ssc"
408 "ssc"
409 "ssc"
410 "S"
411 "ssc"
412 "sdc"
413 "sdc"
414 "sdcc",
415 HTTPMETHOD_NOTIFY, "*", (size_t)1, "HOST: ", host, ":",
416 SSDP_PORT, "CACHE-CONTROL: max-age=", duration,
417 "LOCATION: ", location, "NT: ", nt, "NTS: ", nts, "USN: ", usn,
418 "Powerstate: ", PowerState, "SleepPeriod: ", SleepPeriod,
419 "RegistrationState: ", RegistrationState);
420#endif /* COMPA_HAVE_OPTION_SSDP */
421 } else {
422#ifdef COMPA_HAVE_OPTION_SSDP
423 ret_code = http_MakeMessage(
424 &buf, 1, 1,
425 "Q"
426 "sssdc"
427 "sdc"
428 "ssc"
429 "ssc"
430 "ssc"
431 "ssc"
432 "ssc"
433 "S"
434 "Xc"
435 "sscc",
436 HTTPMETHOD_NOTIFY, "*", (size_t)1, "HOST: ", host, ":",
437 SSDP_PORT, "CACHE-CONTROL: max-age=", duration,
438 "LOCATION: ", location,
439 "OPT: ", "\"http://schemas.upnp.org/upnp/1/0/\"; ns=01",
440 "01-NLS: ", gUpnpSdkNLSuuid, "NT: ", nt, "NTS: ", nts,
441 X_USER_AGENT, "USN: ", usn);
442#else
443 ret_code = http_MakeMessage(
444 &buf, 1, 1,
445 "Q"
446 "sssdc"
447 "sdc"
448 "ssc"
449 "ssc"
450 "ssc"
451 "S"
452 "sscc",
453 HTTPMETHOD_NOTIFY, "*", (size_t)1, "HOST: ", host, ":",
454 SSDP_PORT, "CACHE-CONTROL: max-age=", duration,
455 "LOCATION: ", location, "NT: ", nt, "NTS: ", nts, "USN: ", usn);
456#endif /* COMPA_HAVE_OPTION_SSDP */
457 }
458 if (ret_code)
459 return;
460 } else
461 /* unknown msg */
462 assert(0);
463 /* return msg */
464 *packet = membuffer_detach(&buf);
465 membuffer_destroy(&buf);
466
467 return;
468}
469
471} // anonymous namespace
472
473
475 struct sockaddr_storage* dest_addr) {
476 constexpr int MX_FUDGE_FACTOR{10};
477 int handle, start;
478 struct Handle_Info* dev_info = NULL;
479 memptr hdr_value;
480 int mx;
481 char save_char;
482 SsdpEvent event;
483 int ret_code;
484 SsdpSearchReply* threadArg = NULL;
485 ThreadPoolJob job;
486 int replyTime;
487 int maxAge;
488
489 memset(&job, 0, sizeof(job));
490
491 /* check man hdr. */
492 if (httpmsg_find_hdr(hmsg, HDR_MAN, &hdr_value) == NULL ||
493 memptr_cmp(&hdr_value, "\"ssdp:discover\"") != 0)
494 /* bad or missing hdr. */
495 return;
496 /* MX header. */
497 if (httpmsg_find_hdr(hmsg, HDR_MX, &hdr_value) == NULL ||
498 (mx = raw_to_int(&hdr_value, 10)) < 0)
499 return;
500 /* ST header. */
501 if (httpmsg_find_hdr(hmsg, HDR_ST, &hdr_value) == NULL)
502 return;
503 save_char = hdr_value.buf[hdr_value.length];
504 hdr_value.buf[hdr_value.length] = '\0';
505 ret_code = ssdp_request_type(hdr_value.buf, &event);
506 /* restore. */
507 hdr_value.buf[hdr_value.length] = save_char;
508 if (ret_code == -1)
509 /* bad ST header. */
510 return;
511
512 start = 0;
513 for (;;) {
514 HandleLock();
515 /* device info. */
516 switch (GetDeviceHandleInfo(start, (int)dest_addr->ss_family, &handle,
517 &dev_info)) {
518 case HND_DEVICE:
519 break;
520 default:
521 HandleUnlock();
522 /* no info found. */
523 return;
524 }
525 maxAge = dev_info->MaxAge;
526 HandleUnlock();
527
528 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, "MAX-AGE = %d\n",
529 maxAge);
530 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, "MX = %d\n",
531 event.Mx);
532 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, "DeviceType = %s\n",
533 event.DeviceType);
534 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, "DeviceUuid = %s\n",
535 event.UDN);
536 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, "ServiceType = %s\n",
537 event.ServiceType);
538 threadArg = (SsdpSearchReply*)malloc(sizeof(SsdpSearchReply));
539 if (threadArg == NULL)
540 return;
541 threadArg->handle = handle;
542 memcpy(&threadArg->dest_addr, dest_addr, sizeof(threadArg->dest_addr));
543 threadArg->event = event;
544 threadArg->MaxAge = maxAge;
545
546 TPJobInit(&job, advertiseAndReplyThread, threadArg);
548
549 /* Subtract a percentage from the mx to allow for network and processing
550 * delays (i.e. if search is for 30 seconds, respond
551 * within 0 - 27 seconds). */
552 if (mx >= 2)
553 mx -= std::max(1, mx / MX_FUDGE_FACTOR);
554 if (mx < 1)
555 mx = 1;
556 replyTime = rand() % mx;
557 TimerThreadSchedule(&gTimerThread, replyTime, REL_SEC, &job, SHORT_TERM,
558 NULL);
559 start = handle;
560 }
561}
562
563int DeviceAdvertisement(char* DevType, int RootDev, char* Udn, char* Location,
564 int Duration, int AddressFamily, int PowerState,
566 struct sockaddr_storage __ss;
567 struct sockaddr_in* DestAddr4 = (struct sockaddr_in*)&__ss;
568 struct sockaddr_in6* DestAddr6 = (struct sockaddr_in6*)&__ss;
569 /* char Mil_Nt[LINE_SIZE] */
570 char Mil_Usn[LINE_SIZE];
571 char* msgs[3];
572 int ret_code = UPNP_E_OUTOF_MEMORY;
573 int rc = 0;
574
575 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
576 "In function DeviceAdvertisement\n");
577 msgs[0] = NULL;
578 msgs[1] = NULL;
579 msgs[2] = NULL;
580 memset(&__ss, 0, sizeof(__ss));
581 switch (AddressFamily) {
582 case AF_INET:
583 DestAddr4->sin_family = (sa_family_t)AF_INET;
584 inet_pton(AF_INET, SSDP_IP, &DestAddr4->sin_addr);
585 DestAddr4->sin_port = htons(SSDP_PORT);
586 break;
587 case AF_INET6:
588 DestAddr6->sin6_family = (sa_family_t)AF_INET6;
589 inet_pton(AF_INET6,
592 &DestAddr6->sin6_addr);
593 DestAddr6->sin6_port = htons(SSDP_PORT);
594 DestAddr6->sin6_scope_id = gIF_INDEX;
595 break;
596 default:
597 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
598 "Invalid device address family.\n");
599 ret_code = UPNP_E_INVALID_PARAM;
600 goto error_handler;
601 }
602 // If device is a root device, here we need to send 3 advertisement or reply
603 if (RootDev) {
604 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::upnp:rootdevice", Udn);
605 if (rc < 0 || (unsigned int)rc >= sizeof(Mil_Usn))
606 goto error_handler;
607 CreateServicePacket(MSGTYPE_ADVERTISEMENT, "upnp:rootdevice", Mil_Usn,
608 Location, Duration, &msgs[0], AddressFamily,
609 PowerState, SleepPeriod, RegistrationState);
610 }
611 /* both root and sub-devices need to send these two messages */
613 &msgs[1], AddressFamily, PowerState, SleepPeriod,
614 RegistrationState);
615 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::%s", Udn, DevType);
616 if (rc < 0 || (unsigned int)rc >= sizeof(Mil_Usn))
617 goto error_handler;
618 CreateServicePacket(MSGTYPE_ADVERTISEMENT, DevType, Mil_Usn, Location,
619 Duration, &msgs[2], AddressFamily, PowerState,
620 SleepPeriod, RegistrationState);
621 /* check error */
622 if ((RootDev && msgs[0] == NULL) || msgs[1] == NULL || msgs[2] == NULL) {
623 goto error_handler;
624 }
625 /* send packets */
626 if (RootDev) {
627 /* send 3 msg types */
628 ret_code = NewRequestHandler((struct sockaddr*)&__ss, 3, &msgs[0]);
629 } else { /* sub-device */
630
631 /* send 2 msg types */
632 ret_code = NewRequestHandler((struct sockaddr*)&__ss, 2, &msgs[1]);
633 }
634
635error_handler:
636 /* free msgs */
637 free(msgs[0]);
638 free(msgs[1]);
639 free(msgs[2]);
640
641 return ret_code;
642}
643
644int SendReply(struct sockaddr* DestAddr, char* DevType, int RootDev, char* Udn,
645 char* Location, int Duration, int ByType, int PowerState,
646 int SleepPeriod, int RegistrationState) {
647 int ret_code = UPNP_E_OUTOF_MEMORY;
648 char* msgs[2];
649 int num_msgs;
650 char Mil_Usn[LINE_SIZE];
651 int i;
652 int rc = 0;
653
654 msgs[0] = NULL;
655 msgs[1] = NULL;
656 if (RootDev) {
657 /* one msg for root device */
658 num_msgs = 1;
659
660 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::upnp:rootdevice", Udn);
661 if (rc < 0 || (unsigned int)rc >= sizeof(Mil_Usn))
662 goto error_handler;
663 CreateServicePacket(MSGTYPE_REPLY, "upnp:rootdevice", Mil_Usn, Location,
664 Duration, &msgs[0], (int)DestAddr->sa_family,
665 PowerState, SleepPeriod, RegistrationState);
666 } else {
667 /* two msgs for embedded devices */
668 num_msgs = 1;
669
670 /*NK: FIX for extra response when someone searches by udn */
671 if (!ByType) {
672 CreateServicePacket(MSGTYPE_REPLY, Udn, Udn, Location, Duration,
673 &msgs[0], (int)DestAddr->sa_family, PowerState,
674 SleepPeriod, RegistrationState);
675 } else {
676 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::%s", Udn, DevType);
677 if (rc < 0 || (unsigned int)rc >= sizeof(Mil_Usn))
678 goto error_handler;
679 CreateServicePacket(MSGTYPE_REPLY, DevType, Mil_Usn, Location,
680 Duration, &msgs[0], (int)DestAddr->sa_family,
681 PowerState, SleepPeriod, RegistrationState);
682 }
683 }
684 /* check error */
685 for (i = 0; i < num_msgs; i++) {
686 if (msgs[i] == NULL) {
687 goto error_handler;
688 }
689 }
690 /* send msgs */
691 ret_code = NewRequestHandler(DestAddr, num_msgs, msgs);
692
693error_handler:
694 for (i = 0; i < num_msgs; i++) {
695 if (msgs[i] != NULL)
696 free(msgs[i]);
697 }
698
699 return ret_code;
700}
701
702int DeviceReply(struct sockaddr* DestAddr, char* DevType, int RootDev,
703 char* Udn, char* Location, int Duration, int PowerState,
704 int SleepPeriod, int RegistrationState) {
705 char *szReq[3], Mil_Nt[LINE_SIZE], Mil_Usn[LINE_SIZE];
706 int RetVal = UPNP_E_OUTOF_MEMORY;
707 int rc = 0;
708
709 szReq[0] = NULL;
710 szReq[1] = NULL;
711 szReq[2] = NULL;
712 /* create 2 or 3 msgs */
713 if (RootDev) {
714 /* 3 replies for root device */
715 memset(Mil_Nt, 0, sizeof(Mil_Nt));
716 strncpy(Mil_Nt, "upnp:rootdevice", sizeof(Mil_Nt) - 1);
717 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::upnp:rootdevice", Udn);
718 if (rc < 0 || (unsigned int)rc >= sizeof(Mil_Usn))
719 goto error_handler;
720 CreateServicePacket(MSGTYPE_REPLY, Mil_Nt, Mil_Usn, Location, Duration,
721 &szReq[0], (int)DestAddr->sa_family, PowerState,
722 SleepPeriod, RegistrationState);
723 }
724 rc = snprintf(Mil_Nt, sizeof(Mil_Nt), "%s", Udn);
725 if (rc < 0 || (unsigned int)rc >= sizeof(Mil_Nt))
726 goto error_handler;
727 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s", Udn);
728 if (rc < 0 || (unsigned int)rc >= sizeof(Mil_Usn))
729 goto error_handler;
730 CreateServicePacket(MSGTYPE_REPLY, Mil_Nt, Mil_Usn, Location, Duration,
731 &szReq[1], (int)DestAddr->sa_family, PowerState,
732 SleepPeriod, RegistrationState);
733 rc = snprintf(Mil_Nt, sizeof(Mil_Nt), "%s", DevType);
734 if (rc < 0 || (unsigned int)rc >= sizeof(Mil_Nt))
735 goto error_handler;
736 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::%s", Udn, DevType);
737 if (rc < 0 || (unsigned int)rc >= sizeof(Mil_Usn))
738 goto error_handler;
739 CreateServicePacket(MSGTYPE_REPLY, Mil_Nt, Mil_Usn, Location, Duration,
740 &szReq[2], (int)DestAddr->sa_family, PowerState,
741 SleepPeriod, RegistrationState);
742 /* check error */
743 if ((RootDev && szReq[0] == NULL) || szReq[1] == NULL || szReq[2] == NULL) {
744 goto error_handler;
745 }
746 /* send replies */
747 if (RootDev) {
748 RetVal = NewRequestHandler(DestAddr, 3, szReq);
749 } else {
750 RetVal = NewRequestHandler(DestAddr, 2, &szReq[1]);
751 }
752
753error_handler:
754 /* free */
755 free(szReq[0]);
756 free(szReq[1]);
757 free(szReq[2]);
758
759 return RetVal;
760}
761
762int ServiceAdvertisement(char* Udn, char* ServType, char* Location,
763 int Duration, int AddressFamily, int PowerState,
764 int SleepPeriod, int RegistrationState) {
765 char Mil_Usn[LINE_SIZE];
766 char* szReq[1];
767 int RetVal = UPNP_E_OUTOF_MEMORY;
768 struct sockaddr_storage __ss;
769 struct sockaddr_in* DestAddr4 = (struct sockaddr_in*)&__ss;
770 struct sockaddr_in6* DestAddr6 = (struct sockaddr_in6*)&__ss;
771 int rc = 0;
772
773 memset(&__ss, 0, sizeof(__ss));
774 szReq[0] = NULL;
775 switch (AddressFamily) {
776 case AF_INET:
777 DestAddr4->sin_family = (sa_family_t)AddressFamily;
778 inet_pton(AF_INET, SSDP_IP, &DestAddr4->sin_addr);
779 DestAddr4->sin_port = htons(SSDP_PORT);
780 break;
781 case AF_INET6:
782 DestAddr6->sin6_family = (sa_family_t)AddressFamily;
783 inet_pton(AF_INET6,
786 &DestAddr6->sin6_addr);
787 DestAddr6->sin6_port = htons(SSDP_PORT);
788 DestAddr6->sin6_scope_id = gIF_INDEX;
789 break;
790 default:
791 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
792 "Invalid device address family.\n");
793 }
794 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::%s", Udn, ServType);
795 if (rc < 0 || (unsigned int)rc >= sizeof(Mil_Usn))
796 goto error_handler;
797 /* CreateServiceRequestPacket(1,szReq[0],Mil_Nt,Mil_Usn,
798 * Server,Location,Duration); */
799 CreateServicePacket(MSGTYPE_ADVERTISEMENT, ServType, Mil_Usn, Location,
800 Duration, &szReq[0], AddressFamily, PowerState,
801 SleepPeriod, RegistrationState);
802 if (szReq[0] == NULL) {
803 goto error_handler;
804 }
805 RetVal = NewRequestHandler((struct sockaddr*)&__ss, 1, szReq);
806
807error_handler:
808 free(szReq[0]);
809
810 return RetVal;
811}
812
813int ServiceReply(struct sockaddr* DestAddr, char* ServType, char* Udn,
814 char* Location, int Duration, int PowerState, int SleepPeriod,
815 int RegistrationState) {
816 char Mil_Usn[LINE_SIZE];
817 char* szReq[1];
818 int RetVal = UPNP_E_OUTOF_MEMORY;
819 int rc = 0;
820
821 szReq[0] = NULL;
822 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::%s", Udn, ServType);
823 if (rc < 0 || (unsigned int)rc >= sizeof(Mil_Usn))
824 goto error_handler;
825 CreateServicePacket(MSGTYPE_REPLY, ServType, Mil_Usn, Location, Duration,
826 &szReq[0], (int)DestAddr->sa_family, PowerState,
827 SleepPeriod, RegistrationState);
828 if (szReq[0] == NULL)
829 goto error_handler;
830 RetVal = NewRequestHandler(DestAddr, 1, szReq);
831
832error_handler:
833 free(szReq[0]);
834
835 return RetVal;
836}
837
838int ServiceShutdown(char* Udn, char* ServType, char* Location, int Duration,
839 int AddressFamily, int PowerState, int SleepPeriod,
840 int RegistrationState) {
841 char Mil_Usn[LINE_SIZE];
842 char* szReq[1];
843 struct sockaddr_storage __ss;
844 struct sockaddr_in* DestAddr4 = (struct sockaddr_in*)&__ss;
845 struct sockaddr_in6* DestAddr6 = (struct sockaddr_in6*)&__ss;
846 int RetVal = UPNP_E_OUTOF_MEMORY;
847 int rc = 0;
848
849 memset(&__ss, 0, sizeof(__ss));
850 szReq[0] = NULL;
851 switch (AddressFamily) {
852 case AF_INET:
853 DestAddr4->sin_family = (sa_family_t)AddressFamily;
854 inet_pton(AF_INET, SSDP_IP, &DestAddr4->sin_addr);
855 DestAddr4->sin_port = htons(SSDP_PORT);
856 break;
857 case AF_INET6:
858 DestAddr6->sin6_family = (sa_family_t)AddressFamily;
859 inet_pton(AF_INET6,
862 &DestAddr6->sin6_addr);
863 DestAddr6->sin6_port = htons(SSDP_PORT);
864 DestAddr6->sin6_scope_id = gIF_INDEX;
865 break;
866 default:
867 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
868 "Invalid device address family.\n");
869 }
870 /* sprintf(Mil_Nt,"%s",ServType); */
871 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::%s", Udn, ServType);
872 if (rc < 0 || (unsigned int)rc >= sizeof(Mil_Usn))
873 goto error_handler;
874 /* CreateServiceRequestPacket(0,szReq[0],Mil_Nt,Mil_Usn,
875 * Server,Location,Duration); */
876 CreateServicePacket(MSGTYPE_SHUTDOWN, ServType, Mil_Usn, Location, Duration,
877 &szReq[0], AddressFamily, PowerState, SleepPeriod,
878 RegistrationState);
879 if (szReq[0] == NULL)
880 goto error_handler;
881 RetVal = NewRequestHandler((struct sockaddr*)&__ss, 1, szReq);
882
883error_handler:
884 free(szReq[0]);
885
886 return RetVal;
887}
888
889int DeviceShutdown(char* DevType, int RootDev, char* Udn, char* Location,
890 int Duration, int AddressFamily, int PowerState,
891 int SleepPeriod, int RegistrationState) {
892 struct sockaddr_storage __ss;
893 struct sockaddr_in* DestAddr4 = (struct sockaddr_in*)&__ss;
894 struct sockaddr_in6* DestAddr6 = (struct sockaddr_in6*)&__ss;
895 char* msgs[3];
896 char Mil_Usn[LINE_SIZE];
897 int ret_code = UPNP_E_OUTOF_MEMORY;
898 int rc = 0;
899
900 msgs[0] = NULL;
901 msgs[1] = NULL;
902 msgs[2] = NULL;
903 memset(&__ss, 0, sizeof(__ss));
904 switch (AddressFamily) {
905 case AF_INET:
906 DestAddr4->sin_family = (sa_family_t)AddressFamily;
907 inet_pton(AF_INET, SSDP_IP, &DestAddr4->sin_addr);
908 DestAddr4->sin_port = htons(SSDP_PORT);
909 break;
910 case AF_INET6:
911 DestAddr6->sin6_family = (sa_family_t)AddressFamily;
912 inet_pton(AF_INET6,
915 &DestAddr6->sin6_addr);
916 DestAddr6->sin6_port = htons(SSDP_PORT);
917 DestAddr6->sin6_scope_id = gIF_INDEX;
918 break;
919 default:
920 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
921 "Invalid device address family.\n");
922 }
923 /* root device has one extra msg */
924 if (RootDev) {
925 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::upnp:rootdevice", Udn);
926 if (rc < 0 || (unsigned int)rc >= sizeof(Mil_Usn))
927 goto error_handler;
928 CreateServicePacket(MSGTYPE_SHUTDOWN, "upnp:rootdevice", Mil_Usn,
929 Location, Duration, &msgs[0], AddressFamily,
930 PowerState, SleepPeriod, RegistrationState);
931 }
932 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
933 "In function DeviceShutdown\n");
934 /* both root and sub-devices need to send these two messages */
935 CreateServicePacket(MSGTYPE_SHUTDOWN, Udn, Udn, Location, Duration,
936 &msgs[1], AddressFamily, PowerState, SleepPeriod,
937 RegistrationState);
938 rc = snprintf(Mil_Usn, sizeof(Mil_Usn), "%s::%s", Udn, DevType);
939 if (rc < 0 || (unsigned int)rc >= sizeof(Mil_Usn))
940 goto error_handler;
941 CreateServicePacket(MSGTYPE_SHUTDOWN, DevType, Mil_Usn, Location, Duration,
942 &msgs[2], AddressFamily, PowerState, SleepPeriod,
943 RegistrationState);
944 /* check error */
945 if ((RootDev && msgs[0] == NULL) || msgs[1] == NULL || msgs[2] == NULL) {
946 goto error_handler;
947 }
948 /* send packets */
949 if (RootDev) {
950 /* send 3 msg types */
951 ret_code = NewRequestHandler((struct sockaddr*)&__ss, 3, &msgs[0]);
952 } else {
953 /* sub-device */
954 /* send 2 msg types */
955 ret_code = NewRequestHandler((struct sockaddr*)&__ss, 2, &msgs[1]);
956 }
957
958error_handler:
959 /* free msgs */
960 free(msgs[0]);
961 free(msgs[1]);
962 free(msgs[2]);
963
964 return ret_code;
965}
966
968 enum SsdpSearchType SearchType, struct sockaddr* DestAddr,
969 char* DeviceType, char* DeviceUDN, char* ServiceType,
970 int Exp) {
971 constexpr char SERVICELIST_STR[] = "serviceList";
972 int retVal = UPNP_E_SUCCESS;
973 long unsigned int i;
974 long unsigned int j;
975 int defaultExp = DEFAULT_MAXAGE;
976 struct Handle_Info* SInfo = NULL;
977 char UDNstr[100];
978 char devType[100];
979 char servType[100];
980 IXML_NodeList* nodeList = NULL;
981 IXML_NodeList* tmpNodeList = NULL;
982 IXML_Node* tmpNode = NULL;
983 IXML_Node* tmpNode2 = NULL;
984 IXML_Node* textNode = NULL;
985 const DOMString tmpStr;
986 const DOMString dbgStr;
987 int NumCopy = 0;
988
989 memset(UDNstr, 0, sizeof(UDNstr));
990 memset(devType, 0, sizeof(devType));
991 memset(servType, 0, sizeof(servType));
992
993 UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
994 "Inside AdvertiseAndReply with AdFlag = %d\n", AdFlag);
995
996 /* Use a read lock */
998 if (GetHandleInfo(Hnd, &SInfo) != HND_DEVICE) {
999 retVal = UPNP_E_INVALID_HANDLE;
1000 goto end_function;
1001 }
1002 defaultExp = SInfo->MaxAge;
1003 /* parse the device list and send advertisements/replies */
1004 while (NumCopy == 0 || (AdFlag && NumCopy < NUM_SSDP_COPY)) {
1005 if (NumCopy != 0)
1006 std::this_thread::sleep_for(std::chrono::milliseconds(SSDP_PAUSE));
1007 NumCopy++;
1008 for (i = 0lu;; i++) {
1009 UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
1010 "Entering new device list with i = %lu\n\n", i);
1011 tmpNode = ixmlNodeList_item(SInfo->DeviceList, i);
1012 if (!tmpNode) {
1013 UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
1014 "Exiting new device list with i = "
1015 "%lu\n\n",
1016 i);
1017 break;
1018 }
1019 dbgStr = ixmlNode_getNodeName(tmpNode);
1020 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1021 "Extracting device type once for %s\n", dbgStr);
1022 ixmlNodeList_free(nodeList);
1024 "deviceType");
1025 if (!nodeList)
1026 continue;
1027 UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
1028 "Extracting UDN for %s\n", dbgStr);
1029 UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
1030 "Extracting device type\n");
1031 tmpNode2 = ixmlNodeList_item(nodeList, 0lu);
1032 if (!tmpNode2)
1033 continue;
1034 textNode = ixmlNode_getFirstChild(tmpNode2);
1035 if (!textNode)
1036 continue;
1037 UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
1038 "Extracting device type \n");
1039 tmpStr = ixmlNode_getNodeValue(textNode);
1040 if (!tmpStr)
1041 continue;
1042 strncpy(devType, tmpStr, sizeof(devType) - 1);
1043 UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
1044 "Extracting device type = %s\n", devType);
1045 if (!tmpNode) {
1046 UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
1047 "TempNode is NULL\n");
1048 }
1049 dbgStr = ixmlNode_getNodeName(tmpNode);
1050 UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
1051 "Extracting UDN for %s\n", dbgStr);
1052 ixmlNodeList_free(nodeList);
1053 nodeList =
1055 if (!nodeList) {
1056 UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
1057 "UDN not found!\n");
1058 continue;
1059 }
1060 tmpNode2 = ixmlNodeList_item(nodeList, 0lu);
1061 if (!tmpNode2) {
1062 UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
1063 "UDN not found!\n");
1064 continue;
1065 }
1066 textNode = ixmlNode_getFirstChild(tmpNode2);
1067 if (!textNode) {
1068 UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
1069 "UDN not found!\n");
1070 continue;
1071 }
1072 tmpStr = ixmlNode_getNodeValue(textNode);
1073 if (!tmpStr) {
1074 UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
1075 "UDN not found!\n");
1076 continue;
1077 }
1078 strncpy(UDNstr, tmpStr, sizeof(UDNstr) - 1);
1079 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1080 "Sending UDNStr = %s \n", UDNstr);
1081 if (AdFlag) {
1082 /* send the device advertisement */
1083 if (AdFlag == 1) {
1084 DeviceAdvertisement(devType, i == 0lu, UDNstr,
1085 SInfo->DescURL, Exp, SInfo->DeviceAf,
1086 SInfo->PowerState, SInfo->SleepPeriod,
1087 SInfo->RegistrationState);
1088 } else {
1089 /* AdFlag == -1 */
1090 DeviceShutdown(devType, i == 0lu, UDNstr, SInfo->DescURL,
1091 Exp, SInfo->DeviceAf, SInfo->PowerState,
1092 SInfo->SleepPeriod,
1093 SInfo->RegistrationState);
1094 }
1095 } else {
1096 switch (SearchType) {
1097 case SSDP_ALL:
1098 DeviceReply(DestAddr, devType, i == 0lu, UDNstr,
1099 SInfo->DescURL, defaultExp, SInfo->PowerState,
1100 SInfo->SleepPeriod, SInfo->RegistrationState);
1101 break;
1102 case SSDP_ROOTDEVICE:
1103 if (i == 0lu) {
1104 SendReply(DestAddr, devType, 1, UDNstr, SInfo->DescURL,
1105 defaultExp, 0, SInfo->PowerState,
1106 SInfo->SleepPeriod, SInfo->RegistrationState);
1107 }
1108 break;
1109 case SSDP_DEVICEUDN: {
1110 /* clang-format off */
1111 if (DeviceUDN && strlen(DeviceUDN) != (size_t)0) {
1112 if (strcasecmp(DeviceUDN, UDNstr)) {
1113 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1114 "DeviceUDN=%s and search UDN=%s DID NOT match\n",
1115 UDNstr, DeviceUDN);
1116 } else {
1117 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1118 "DeviceUDN=%s and search UDN=%s MATCH\n",
1119 UDNstr, DeviceUDN);
1120 SendReply(DestAddr, devType, 0, UDNstr, SInfo->DescURL, defaultExp, 0,
1121 SInfo->PowerState,
1122 SInfo->SleepPeriod,
1123 SInfo->RegistrationState);
1124 }
1125 }
1126 /* clang-format on */
1127 break;
1128 }
1129 case SSDP_DEVICETYPE: {
1130 /* clang-format off */
1131 if (!strncasecmp(DeviceType, devType, strlen(DeviceType) - (size_t)2)) {
1132 if (atoi(strrchr(DeviceType, ':') + 1)
1133 < atoi(&devType[strlen(devType) - (size_t)1])) {
1134 /* the requested version is lower than the device version
1135 * must reply with the lower version number and the lower
1136 * description URL */
1137 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1138 "DeviceType=%s and search devType=%s MATCH\n",
1139 devType, DeviceType);
1140 SendReply(DestAddr, DeviceType, 0, UDNstr, SInfo->LowerDescURL,
1141 defaultExp, 1,
1142 SInfo->PowerState,
1143 SInfo->SleepPeriod,
1144 SInfo->RegistrationState);
1145 } else if (atoi(strrchr(DeviceType, ':') + 1)
1146 == atoi(&devType[strlen(devType) - (size_t)1])) {
1147 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1148 "DeviceType=%s and search devType=%s MATCH\n",
1149 devType, DeviceType);
1150 SendReply(DestAddr, DeviceType, 0, UDNstr, SInfo->DescURL,
1151 defaultExp, 1,
1152 SInfo->PowerState,
1153 SInfo->SleepPeriod,
1154 SInfo->RegistrationState);
1155 } else {
1156 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1157 "DeviceType=%s and search devType=%s DID NOT MATCH\n",
1158 devType, DeviceType);
1159 }
1160 } else {
1161 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1162 "DeviceType=%s and search devType=%s DID NOT MATCH\n",
1163 devType, DeviceType);
1164 }
1165 /* clang-format on */
1166 break;
1167 }
1168 default:
1169 break;
1170 }
1171 }
1172 /* send service advertisements for services
1173 * corresponding to the same device */
1174 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1175 "Sending service Advertisement\n");
1176 /* Correct service traversal such that each device's
1177 * serviceList is directly traversed as a child of its
1178 * parent device. This ensures that the service's alive
1179 * message uses the UDN of the parent device. */
1180 tmpNode = ixmlNode_getFirstChild(tmpNode);
1181 while (tmpNode) {
1182 dbgStr = ixmlNode_getNodeName(tmpNode);
1183 if (!strncmp(dbgStr, SERVICELIST_STR, sizeof SERVICELIST_STR)) {
1184 break;
1185 }
1186 tmpNode = ixmlNode_getNextSibling(tmpNode);
1187 }
1188 ixmlNodeList_free(nodeList);
1189 if (!tmpNode) {
1190 nodeList = NULL;
1191 continue;
1192 }
1194 "service");
1195 if (!nodeList) {
1196 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1197 "Service not found 3\n");
1198 continue;
1199 }
1200 for (j = 0lu;; j++) {
1201 tmpNode = ixmlNodeList_item(nodeList, j);
1202 if (!tmpNode) {
1203 break;
1204 }
1205 ixmlNodeList_free(tmpNodeList);
1207 (IXML_Element*)tmpNode, "serviceType");
1208 if (!tmpNodeList) {
1209 UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__,
1210 "ServiceType not found \n");
1211 continue;
1212 }
1213 tmpNode2 = ixmlNodeList_item(tmpNodeList, 0lu);
1214 if (!tmpNode2)
1215 continue;
1216 textNode = ixmlNode_getFirstChild(tmpNode2);
1217 if (!textNode)
1218 continue;
1219 /* servType is of format
1220 * Servicetype:ServiceVersion */
1221 tmpStr = ixmlNode_getNodeValue(textNode);
1222 if (!tmpStr)
1223 continue;
1224 strncpy(servType, tmpStr, sizeof(servType) - 1);
1225 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1226 "ServiceType = %s\n", servType);
1227 if (AdFlag) {
1228 if (AdFlag == 1) {
1230 UDNstr, servType, SInfo->DescURL, Exp,
1231 SInfo->DeviceAf, SInfo->PowerState,
1232 SInfo->SleepPeriod, SInfo->RegistrationState);
1233 } else {
1234 /* AdFlag == -1 */
1235 ServiceShutdown(UDNstr, servType, SInfo->DescURL, Exp,
1236 SInfo->DeviceAf, SInfo->PowerState,
1237 SInfo->SleepPeriod,
1238 SInfo->RegistrationState);
1239 }
1240 } else {
1241 switch (SearchType) {
1242 case SSDP_ALL:
1243 ServiceReply(DestAddr, servType, UDNstr, SInfo->DescURL,
1244 defaultExp, SInfo->PowerState,
1245 SInfo->SleepPeriod,
1246 SInfo->RegistrationState);
1247 break;
1248 case SSDP_SERVICE:
1249 /* clang-format off */
1250 if (ServiceType) {
1251 if (!strncasecmp(ServiceType, servType, strlen(ServiceType) - (size_t)2)) {
1252 if (atoi(strrchr(ServiceType, ':') + 1) <
1253 atoi(&servType[strlen(servType) - (size_t)1])) {
1254 /* the requested version is lower than the service version
1255 * must reply with the lower version number and the lower
1256 * description URL */
1257 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1258 "ServiceType=%s and search servType=%s MATCH\n",
1259 ServiceType, servType);
1260 SendReply(DestAddr, ServiceType, 0, UDNstr, SInfo->LowerDescURL,
1261 defaultExp, 1,
1262 SInfo->PowerState,
1263 SInfo->SleepPeriod,
1264 SInfo->RegistrationState);
1265 } else if (atoi(strrchr (ServiceType, ':') + 1) ==
1266 atoi(&servType[strlen(servType) - (size_t)1])) {
1267 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1268 "ServiceType=%s and search servType=%s MATCH\n",
1269 ServiceType, servType);
1270 SendReply(DestAddr, ServiceType, 0, UDNstr, SInfo->DescURL,
1271 defaultExp, 1,
1272 SInfo->PowerState,
1273 SInfo->SleepPeriod,
1274 SInfo->RegistrationState);
1275 } else {
1276 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1277 "ServiceType=%s and search servType=%s DID NOT MATCH\n",
1278 ServiceType, servType);
1279 }
1280 } else {
1281 UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
1282 "ServiceType=%s and search servType=%s DID NOT MATCH\n",
1283 ServiceType, servType);
1284 }
1285 }
1286 /* clang-format on */
1287 break;
1288 default:
1289 break;
1290 }
1291 }
1292 }
1293 ixmlNodeList_free(tmpNodeList);
1294 tmpNodeList = NULL;
1295 ixmlNodeList_free(nodeList);
1296 nodeList = NULL;
1297 }
1298 }
1299
1300end_function:
1301 ixmlNodeList_free(tmpNodeList);
1302 ixmlNodeList_free(nodeList);
1303 UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
1304 "Exiting AdvertiseAndReply.\n");
1305 HandleUnlock();
1306
1307 return retVal;
1308}
1309
1310void advertiseAndReplyThread(void* data) {
1311 SsdpSearchReply* arg = (SsdpSearchReply*)data;
1312
1314 (struct sockaddr*)&arg->dest_addr, arg->event.DeviceType,
1315 arg->event.UDN, arg->event.ServiceType, arg->MaxAge);
1316 free(arg);
1317}
int UpnpDevice_Handle
Returned when a device application registers with UpnpRegisterRootDevice(),UpnpRegisterRootDevice2(),...
Definition API.hpp:422
#define LINE_SIZE
Definition API.hpp:45
#define HDR_MX
Type of a HTTP header.
#define HDR_ST
Type of a HTTP header.
#define HDR_MAN
Type of a HTTP header.
Structure of an HTTP message.
#define X_USER_AGENT
Can be overwritten by configure CFLAGS argument.
Definition webserver.hpp:86
int TPJobSetFreeFunction(ThreadPoolJob *job, free_routine func)
Sets the jobs free function.
int TPJobInit(ThreadPoolJob *job, UPnPsdk::start_routine func, void *arg)
Initializes thread pool job.
Duration
Duration.
void(* free_routine)(void *arg)
Internal ThreadPool Job.
int TimerThreadSchedule(TimerThread *timer, time_t timeout, TimeoutType type, ThreadPoolJob *job, Duration duration, int *id)
Schedules an event to run at a specified time.
@ REL_SEC
seconds from current time.
Class for portable handling of network socket errors.
Definition socket.hpp:537
void catch_error()
Catch error for later use.
Definition socket.cpp:618
std::string error_str() const
Get human readable error description of the catched error.
Definition socket.cpp:627
Manage all aspects of a network socket.
Definition socket.hpp:320
void bind(const int a_socktype, const SSockaddr *const a_saddr=nullptr, const int a_flags=0)
Bind socket to an ip address of a local network adapter.
Definition socket.cpp:457
#define NUM_SSDP_COPY
This configuration parameter determines how many copies of each SSDP advertisement and search packets...
Definition config.hpp:142
#define SSDP_PAUSE
This configuration parameter determines the pause between identical SSDP advertisement and search pac...
Definition config.hpp:149
int ssdp_request_type(char *cmd, SsdpEvent *Evt)
Starts filling the SSDP event structure based upon the request received.
int AdvertiseAndReply(int AdFlag, UpnpDevice_Handle Hnd, enum SsdpSearchType SearchType, struct sockaddr *DestAddr, char *DeviceType, char *DeviceUDN, char *ServiceType, int Exp)
Sends SSDP advertisements, replies and shutdown messages.
Data structure common to all types of nodes.
Definition ixml.hpp:132
Data structure representing a list of nodes.
Definition ixml.hpp:193
Data structure representing an Element node.
Definition ixml.hpp:169
#define DOMString
The type of DOM strings.
Definition ixml.hpp:47
PUPNP_Api IXML_Node * ixmlNode_getNextSibling(IXML_Node *nodeptr)
Retrieves the sibling Node immediately following this Node.
Definition node.cpp:371
PUPNP_Api const DOMString ixmlNode_getNodeValue(IXML_Node *nodeptr)
Returns the value of the Node as a string.
Definition node.cpp:294
PUPNP_Api const DOMString ixmlNode_getNodeName(IXML_Node *nodeptr)
Returns the name of the Node, depending on what type of Node it is, in a read-only string.
Definition node.cpp:182
PUPNP_Api void ixmlNodeList_free(IXML_NodeList *nList)
Frees a NodeList object.
Definition nodeList.cpp:129
PUPNP_Api IXML_Node * ixmlNode_getFirstChild(IXML_Node *nodeptr)
Retrieves the first child Node of a Node.
Definition node.cpp:338
PUPNP_Api IXML_Node * ixmlNodeList_item(IXML_NodeList *nList, unsigned long index)
Retrieves a Node from a NodeList specified by a numerical index.
Definition nodeList.cpp:49
PUPNP_Api IXML_NodeList * ixmlElement_getElementsByTagName(IXML_Element *element, const DOMString tagName)
Returns a NodeList of all descendant Elements with a given tag name, in the order in which they are e...
Definition element.cpp:337
http_header_t * httpmsg_find_hdr(http_message_t *msg, int header_name_id, memptr *value)
Finds header from a list, with the given 'name_id'.
int raw_to_int(memptr *raw_value, int base)
Converts raw character data to integer value.
int http_MakeMessage(membuffer *buf, int http_major_version, int http_minor_version, const char *fmt,...)
Generate an HTTP message based on the format that is specified in the input parameters.
Performs HTTP read and write messages.
int memptr_cmp(memptr *m, const char *s)
Compares characters of strings passed for number of bytes. If equal for the number of bytes,...
Definition membuffer.cpp:70
void membuffer_destroy(membuffer *m)
Free's memory allocated for membuffer* m.
void membuffer_init(membuffer *m)
Wrapper to membuffer_initialize().
char * membuffer_detach(membuffer *m)
Detaches current buffer and returns it. The caller must free the returned buffer using free()....
size_t size_inc
used to increase size; MUST be > 0; (read/write).
Definition membuffer.hpp:69
size_t length
length of memory without terminating '\0' (read-only).
Definition membuffer.hpp:54
char * buf
start of memory (read/write).
Definition membuffer.hpp:52
pointer to a chunk of memory.
Definition membuffer.hpp:50
Maintains a block of dynamically allocated memory.
Definition membuffer.hpp:61
#define UPNP_E_SOCKET_ERROR
Generic socket error code for conditions not covered by other error codes.
Definition messages.hpp:243
#define UPNP_E_NETWORK_ERROR
A network error occurred.
Definition messages.hpp:169
#define UPNP_E_INVALID_HANDLE
The handle passed to a function is not a recognized as a valid handle.
Definition messages.hpp:32
#define UPNP_E_SUCCESS
The operation completed successfully.
Definition messages.hpp:27
#define UPNP_E_OUTOF_MEMORY
Not enough resources are currently available to complete the operation.
Definition messages.hpp:57
#define UPNP_E_SOCKET_WRITE
An error happened while writing to a socket.
Definition messages.hpp:179
#define UPNP_E_INVALID_PARAM
One or more of the parameters passed to the function is not valid.
Definition messages.hpp:40
UPnPsdk_EXTERN bool g_dbug
Switch to enable verbose (debug) output.
Definition global.hpp:26
int send_stateless(sockaddr *a_dest_saddr, int a_num_packet, char **a_rq_packet)
Works as a request handler which passes the HTTP request string to multicast channel.
int extractIPv6address(char *url, char *address)
Extract IPv6 address.
constexpr int MSGTYPE_REPLY
Message type.
int NewRequestHandler(struct sockaddr *a_dest_saddr, int a_num_packet, char **a_rq_packet)
Works as a request handler which passes the HTTP request string to multicast channel.
constexpr int MSGTYPE_SHUTDOWN
Message type.
void CreateServicePacket(int msg_type, const char *nt, char *usn, char *location, int duration, char **packet, int AddressFamily, int PowerState, int SleepPeriod, int RegistrationState)
Creates a HTTP request packet.
constexpr int MSGTYPE_ADVERTISEMENT
Message type.
int isUrlV6UlaGua(char *descdocUrl)
Test if a Url contains an ULA or GUA IPv6 address.
Declaration of the Sockaddr class and some free helper functions.
Socket Module: manage properties and methods but not connections of ONE network socket to handle IPv4...
#define SSDP_PORT
constant
struct sockaddr_storage dest_addr
part of search reply
char UDN[LINE_SIZE]
Part of SSDP Event.
int Mx
Part of SSDP Event.
#define SSDP_IP
constant
#define SSDP_IPV6_SITELOCAL
constant
UpnpDevice_Handle handle
part of search reply
char ServiceType[LINE_SIZE]
Part of SSDP Event.
#define SSDP_IPV6_LINKLOCAL
constant
char DeviceType[LINE_SIZE]
Part of SSDP Event.
int MaxAge
part of search reply
enum SsdpSearchType RequestType
Part of SSDP Event.
SsdpEvent event
part of search reply
SsdpSearchType
Enumeration to define all different types of ssdp searches.
@ SSDP_DEVICEUDN
Part of SType.
@ SSDP_DEVICETYPE
Part of SType.
@ SSDP_ROOTDEVICE
Part of SType.
@ SSDP_SERVICE
Part of SType.
@ SSDP_ALL
Part of SType.
Structure to store the SSDP information.
SSDP search reply.
int DeviceReply(struct sockaddr *DestAddr, char *DevType, int RootDev, char *Udn, char *Location, int Duration, int PowerState, int SleepPeriod, int RegistrationState)
Creates the reply packet and send it to the Control Point address.
int DeviceAdvertisement(char *DevType, int RootDev, char *Udn, char *Location, int Duration, int AddressFamily, int PowerState, int SleepPeriod, int RegistrationState)
Creates the device advertisement request.
int DeviceShutdown(char *DevType, int RootDev, char *Udn, char *Location, int Duration, int AddressFamily, int PowerState, int SleepPeriod, int RegistrationState)
Creates a HTTP device shutdown request packet and send it to the multicast channel through RequestHan...
void ssdp_handle_device_request(http_message_t *hmsg, struct sockaddr_storage *dest_addr)
Handles the search request.
void advertiseAndReplyThread(void *data)
Wrapper function to reply the search request coming from the control point.
int ServiceAdvertisement(char *Udn, char *ServType, char *Location, int Duration, int AddressFamily, int PowerState, int SleepPeriod, int RegistrationState)
Creates the advertisement packet and send it to the multicast channel.
int ServiceShutdown(char *Udn, char *ServType, char *Location, int Duration, int AddressFamily, int PowerState, int SleepPeriod, int RegistrationState)
Creates a HTTP service shutdown request packet and sends it to the multicast channel through RequestH...
int SendReply(struct sockaddr *DestAddr, char *DevType, int RootDev, char *Udn, char *Location, int Duration, int ByType, int PowerState, int SleepPeriod, int RegistrationState)
Creates the reply packet and send it to the Control Point addesss.
int ServiceReply(struct sockaddr *DestAddr, char *ServType, char *Udn, char *Location, int Duration, int PowerState, int SleepPeriod, int RegistrationState)
Creates the advertisement packet and send it to the multicast channel.
Manage "Step 1: Discovery" of the UPnP+™ specification for UPnP Devices with SSDP.
HTTP status codes.
Trivial ::sockaddr structures enhanced with methods.
Definition sockaddr.hpp:133
const std::string netaddrp() noexcept
Get the assosiated netaddress with port.
Definition sockaddr.cpp:484
Upnp_Handle_Type GetHandleInfo(UpnpClient_Handle Hnd, Handle_Info **HndInfo)
Get handle information.
Definition upnpapi.cpp:3339
Upnp_SID gUpnpSdkNLSuuid
Global variable used in discovery notifications.
Definition upnpapi.cpp:212
Upnp_Handle_Type GetDeviceHandleInfo(UpnpDevice_Handle start, int AddressFamily, UpnpDevice_Handle *device_handle_out, struct Handle_Info **HndInfo)
Retrieves the device handle and information of the first device of the address family specified....
Definition upnpapi.cpp:3265
TimerThread gTimerThread
Global timer thread.
Definition upnpapi.cpp:108
unsigned gIF_INDEX
Index/scope-id from the used network interface.
Definition upnpapi.cpp:135
Inititalize the compatible library before it can be used.
#define HandleLock()
HandleLock.
Definition upnpapi.hpp:140
#define DEFAULT_MAXAGE
DEFAULT_MAXAGE.
Definition upnpapi.hpp:58
int SleepPeriod
Sleep Period as defined by UPnP Low Power.
Definition upnpapi.hpp:102
char LowerDescURL[LINE_SIZE]
URL for the use of SSDP when answering to legacy CPs (CP searching for a v1 when the device is v2).
Definition upnpapi.hpp:96
int RegistrationState
Registration State as defined by UPnP Low Power.
Definition upnpapi.hpp:103
char DescURL[LINE_SIZE]
URL for the use of SSDP.
Definition upnpapi.hpp:95
#define HandleUnlock()
HandleUnlock.
Definition upnpapi.hpp:149
int PowerState
Power State as defined by UPnP Low Power.
Definition upnpapi.hpp:101
IXML_NodeList * DeviceList
List of devices in the description document.
Definition upnpapi.hpp:106
int MaxAge
Advertisement timeout.
Definition upnpapi.hpp:100
int DeviceAf
Address family: AF_INET6 or AF_INET.
Definition upnpapi.hpp:113
#define HandleReadLock()
HandleReadLock.
Definition upnpapi.hpp:146
Data to be stored in handle table for Handle Info.
Definition upnpapi.hpp:86
UPnPsdk_VIS void UpnpPrintf(Upnp_LogLevel DLevel, Dbg_Module Module, const char *DbgFileName, int DbgLineNo, const char *FmtStr,...)
Prints the debug statement.