UPnPsdk 0.1
Universal Plug and Play +, Software Development Kit
 
Loading...
Searching...
No Matches
ssdp_common.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-29
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 compare with ./pupnp source file on 2023-08-30, ver 1.14.18
43#include <ssdp_ctrlpt.hpp>
44#include <ssdp_device.hpp>
45#include <upnpapi.hpp>
46
47#ifndef COMPA_SSDP_COMMON_HPP
48#error "No or wrong ssdp_common.hpp header file included."
49#endif
50#ifndef COMPA_INTERNAL_CONFIG_HPP
51#error "No or wrong config.hpp header file included."
52#endif
53
54#include <umock/sys_socket.hpp>
55#include <umock/pupnp_sock.hpp>
56#ifdef _MSC_VER
57#include <umock/winsock2.hpp>
58#endif
59
60
61namespace {
62
64
74 void* the_data) {
75 ssdp_thread_data* data = (ssdp_thread_data*)the_data;
76
77 if (data != NULL) {
78 http_message_t* hmsg = &data->parser.msg;
79 /* free data */
80 httpmsg_destroy(hmsg);
81 free(data);
82 }
83}
84
90inline int valid_ssdp_msg(
93 http_message_t* hmsg) {
94 memptr hdr_value;
95
96 /* check for valid methods - NOTIFY or M-SEARCH */
97 if (hmsg->method != (http_method_t)HTTPMETHOD_NOTIFY &&
98 hmsg->method != (http_method_t)HTTPMETHOD_MSEARCH &&
99 hmsg->request_method != (http_method_t)HTTPMETHOD_MSEARCH) {
100 return 0;
101 }
102 if (hmsg->request_method != (http_method_t)HTTPMETHOD_MSEARCH) {
103 /* check PATH == "*" */
104 if (hmsg->uri.type != Relative ||
105 strncmp("*", hmsg->uri.pathquery.buff, hmsg->uri.pathquery.size) !=
106 0) {
107 return 0;
108 }
109 /* check HOST header */
110 if (httpmsg_find_hdr(hmsg, HDR_HOST, &hdr_value) == NULL ||
111 (memptr_cmp(&hdr_value, "239.255.255.250:1900") != 0 &&
112 memptr_cmp(&hdr_value, "[FF02::C]:1900") != 0 &&
113 memptr_cmp(&hdr_value, "[ff02::c]:1900") != 0 &&
114 memptr_cmp(&hdr_value, "[FF05::C]:1900") != 0 &&
115 memptr_cmp(&hdr_value, "[ff05::c]:1900") != 0)) {
116 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
117 "Invalid HOST header from SSDP message\n");
118
119 return 0;
120 }
121 }
122
123 /* passed quick check */
124 return 1;
125}
126
138 void* Data) {
139 http_parser_t* parser = NULL;
140 parse_status_t status;
141 ssdp_thread_data* data = (ssdp_thread_data*)Data;
142
143 parser = &data->parser;
144 status = parser_parse(parser);
145 if (status == (parse_status_t)PARSE_FAILURE) {
146 if (parser->msg.method != (http_method_t)HTTPMETHOD_NOTIFY ||
147 !parser->valid_ssdp_notify_hack) {
148 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
149 "SSDP recvd bad msg code = %u\n", status);
150 /* ignore bad msg, or not enuf mem */
151 goto error_handler;
152 }
153 /* valid notify msg */
154 } else if (status != (parse_status_t)PARSE_SUCCESS) {
155 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
156 "SSDP recvd bad msg code = %u\n", status);
157
158 goto error_handler;
159 }
160 /* check msg */
161 if (valid_ssdp_msg(&parser->msg) != 1) {
162 goto error_handler;
163 }
164 /* done; thread will free 'data' */
165 return 0;
166
167error_handler:
169 return -1;
170}
171
178 void* the_data) {
179 ssdp_thread_data* data = (ssdp_thread_data*)the_data;
180 http_message_t* hmsg = &data->parser.msg;
181
182 if (start_event_handler(the_data) != 0)
183 return;
184 /* send msg to device or ctrlpt */
185 if (hmsg->method == (http_method_t)HTTPMETHOD_NOTIFY ||
186 hmsg->request_method == (http_method_t)HTTPMETHOD_MSEARCH) {
188#ifdef COMPA_HAVE_CTRLPT_SSDP
189 ssdp_handle_ctrlpt_msg(hmsg, &data->dest_addr, 0);
190#endif
191 } else {
192#ifdef COMPA_HAVE_DEVICE_SSDP
194#endif
195 }
196
197 /* free data */
199}
200
214 SOCKET* ssdpSock) {
215 UPnPsdk_LOGINFO("MSG1075") "Executing...\n";
216 int onOff;
217 u_char ttl = (u_char)4;
218 ip_mreq ssdpMcastAddr;
219 sockaddr_storage __ss;
220 sockaddr_in* ssdpAddr4 = (struct sockaddr_in*)&__ss;
221 int ret = 0;
222 in_addr addr;
223 UPnPsdk::CSocketErr sockerrObj;
224
225 *ssdpSock = umock::sys_socket_h.socket(AF_INET, SOCK_DGRAM, 0);
226 if (*ssdpSock == INVALID_SOCKET) {
227 sockerrObj.catch_error();
228 UPnPsdk_LOGCRIT("MSG1076") "Error in socket(): "
229 << sockerrObj.error_str() << ".\n";
230 return UPNP_E_OUTOF_SOCKET;
231 }
232 onOff = 1;
233 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEADDR,
234 (char*)&onOff, sizeof(onOff));
235 if (ret == -1) {
236 sockerrObj.catch_error();
237 UPnPsdk_LOGCRIT("MSG1077") "Error in setsockopt() SO_REUSEADDR: "
238 << sockerrObj.error_str() << ".\n";
240 goto error_handler;
241 }
242#if (defined(BSD) && !defined(__GNU__)) || defined(__APPLE__)
243 onOff = 1;
244 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEPORT,
245 (char*)&onOff, sizeof(onOff));
246 if (ret == -1) {
247 sockerrObj.catch_error();
248 UPnPsdk_LOGCRIT("MSG1078") "Error in setsockopt() SO_REUSEP: "
249 << sockerrObj.error_str() << ".\n";
251 goto error_handler;
252 }
253#endif /* BSD, __APPLE__ */
254 memset(&__ss, 0, sizeof(__ss));
255 ssdpAddr4->sin_family = (sa_family_t)AF_INET;
256 ssdpAddr4->sin_addr.s_addr = htonl(INADDR_ANY);
257 ssdpAddr4->sin_port = htons(SSDP_PORT);
258 ret = umock::sys_socket_h.bind(*ssdpSock, (sockaddr*)ssdpAddr4,
259 sizeof(*ssdpAddr4));
260 if (ret == -1) {
261 sockerrObj.catch_error();
262 UPnPsdk_LOGCRIT("MSG1079") "Error in bind(), addr="
263 << INADDR_ANY << ", port=" << SSDP_PORT << ": "
264 << sockerrObj.error_str() << ".\n";
265 ret = UPNP_E_SOCKET_BIND;
266 goto error_handler;
267 }
268 /*
269 * See: https://man7.org/linux/man-pages/man7/ip.7.html
270 * Socket options, IP_ADD_MEMBERSHIP
271 *
272 * This memset actually sets imr_address to INADDR_ANY and
273 * imr_ifindex to zero, which make the system choose the appropriate
274 * interface.
275 *
276 * Still using "struct ip_mreq" instead of "struct ip_mreqn" because
277 * windows does not recognize the latter.
278 */
279 memset((void*)&ssdpMcastAddr, 0, sizeof ssdpMcastAddr);
280 inet_pton(AF_INET, gIF_IPV4, &ssdpMcastAddr.imr_interface);
281 inet_pton(AF_INET, SSDP_IP, &ssdpMcastAddr.imr_multiaddr);
282 ret = umock::sys_socket_h.setsockopt(
283 *ssdpSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&ssdpMcastAddr,
284 sizeof(struct ip_mreq));
285 if (ret == -1) {
286 sockerrObj.catch_error();
287 UPnPsdk_LOGCRIT("MSG1080") "Error in setsockopt() IP_ADD_MEMBERSHIP "
288 "(join multicast group): "
289 << sockerrObj.error_str() << ".\n";
291 goto error_handler;
292 }
293 /* Set multicast interface. */
294 memset((void*)&addr, 0, sizeof(struct in_addr));
295 inet_pton(AF_INET, gIF_IPV4, &addr);
296 ret = umock::sys_socket_h.setsockopt(*ssdpSock, IPPROTO_IP, IP_MULTICAST_IF,
297 (char*)&addr, sizeof addr);
298 if (ret == -1) {
299 sockerrObj.catch_error();
300 UPnPsdk_LOGINFO("MSG1081") "Error in setsockopt() IP_MULTICAST_IF (set "
301 "multicast interface): "
302 << sockerrObj.error_str() << ".\n";
303 /* This is probably not a critical error, so let's continue. */
304 }
305 /* result is not checked becuase it will fail in WinMe and Win9x. */
306 umock::sys_socket_h.setsockopt(*ssdpSock, IPPROTO_IP, IP_MULTICAST_TTL,
307 (const char*)&ttl, sizeof(ttl));
308 onOff = 1;
309 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_BROADCAST,
310 (char*)&onOff, sizeof(onOff));
311 if (ret == -1) {
312 sockerrObj.catch_error();
313 UPnPsdk_LOGCRIT(
314 "MSG1082") "Error in setsockopt() SO_BROADCAST (set broadcast): "
315 << sockerrObj.error_str() << ".\n";
317 goto error_handler;
318 }
319 ret = UPNP_E_SUCCESS;
320
321error_handler:
322 if (ret != UPNP_E_SUCCESS) {
323 umock::unistd_h.CLOSE_SOCKET_P(*ssdpSock);
324 }
325
326 return ret;
327}
328
329#if defined(COMPA_HAVE_CTRLPT_SSDP) || defined(DOXYGEN_RUN)
340 SOCKET* ssdpReqSock) {
341 UPnPsdk_LOGINFO("MSG1071") "Executing...\n";
342 u_char ttl = 4;
343
344 UPnPsdk::CSocketErr sockerrObj;
345 *ssdpReqSock = umock::sys_socket_h.socket(AF_INET, SOCK_DGRAM, 0);
346 if (*ssdpReqSock == INVALID_SOCKET) {
347 sockerrObj.catch_error();
348 UPnPsdk_LOGCRIT("MSG1072") "Error in socket(): "
349 << sockerrObj.error_str() << ".\n";
350 return UPNP_E_OUTOF_SOCKET;
351 }
352 umock::sys_socket_h.setsockopt(*ssdpReqSock, IPPROTO_IP, IP_MULTICAST_TTL,
353 (const char*)&ttl, sizeof(ttl));
354 /* just do it, regardless if fails or not. */
355 int ret = umock::pupnp_sock.sock_make_no_blocking(*ssdpReqSock);
356 if (ret == SOCKET_ERROR)
357 // But at least give a critical error message.
358 UPnPsdk_LOGCRIT("MSG1090") "SSDP Request Socket "
359 << *ssdpReqSock
360 << " failed to set \"no blocking\" but continue...\n";
361
362 /* Again, just do it, regardless if fails or not. */
363 return UPNP_E_SUCCESS;
364}
365
366#ifdef UPNP_ENABLE_IPV6
377 SOCKET* ssdpReqSock) {
378 UPnPsdk_LOGINFO("MSG1073") "Executing...\n";
379 char hops = 1;
380
381 UPnPsdk::CSocketErr sockerrObj;
382 *ssdpReqSock = umock::sys_socket_h.socket(AF_INET6, SOCK_DGRAM, 0);
383 if (*ssdpReqSock == INVALID_SOCKET) {
384 sockerrObj.catch_error();
385 UPnPsdk_LOGCRIT("MSG1074") "Error in socket(): "
386 << sockerrObj.error_str() << ".\n";
387 return UPNP_E_OUTOF_SOCKET;
388 }
389 /* MUST use scoping of IPv6 addresses to control the propagation os SSDP
390 * messages instead of relying on the Hop Limit (Equivalent to the TTL
391 * limit in IPv4). */
392 umock::sys_socket_h.setsockopt(*ssdpReqSock, IPPROTO_IPV6,
393 IPV6_MULTICAST_HOPS, &hops, sizeof(hops));
394 /* just do it, regardless if fails or not. */
395 umock::pupnp_sock.sock_make_no_blocking(*ssdpReqSock);
396
397 return UPNP_E_SUCCESS;
398}
399#endif /* IPv6 */
400#endif // COMPA_HAVE_CTRLPT_SSDP
401
402#ifdef UPNP_ENABLE_IPV6
416 SOCKET* ssdpSock) {
417 UPnPsdk_LOGINFO("MSG1083") "Executing...\n";
418 char errorBuffer[ERROR_BUFFER_LEN];
419 ipv6_mreq ssdpMcastAddr;
420 sockaddr_storage __ss;
421 sockaddr_in6* ssdpAddr6 = (struct sockaddr_in6*)&__ss;
422 int onOff;
423 int ret = 0;
424
425 *ssdpSock = umock::sys_socket_h.socket(AF_INET6, SOCK_DGRAM, 0);
426 if (*ssdpSock == INVALID_SOCKET) {
427 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
428 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
429 "Error in socket(): %s\n", errorBuffer);
430
431 return UPNP_E_OUTOF_SOCKET;
432 }
433 onOff = 1;
434 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEADDR,
435 (char*)&onOff, sizeof(onOff));
436 if (ret == -1) {
437 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
438 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
439 "Error in setsockopt() SO_REUSEADDR: %s\n", errorBuffer);
441 goto error_handler;
442 }
443#if (defined(BSD) && !defined(__GNU__)) || defined(__APPLE__)
444 onOff = 1;
445 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEPORT,
446 (char*)&onOff, sizeof(onOff));
447 if (ret == -1) {
448 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
449 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
450 "Error in setsockopt() SO_REUSEPORT: %s\n", errorBuffer);
452 goto error_handler;
453 }
454#endif /* BSD, __APPLE__ */
455 onOff = 1;
456 ret = umock::sys_socket_h.setsockopt(*ssdpSock, IPPROTO_IPV6, IPV6_V6ONLY,
457 (char*)&onOff, sizeof(onOff));
458 if (ret == -1) {
459 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
460 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
461 "Error in setsockopt() IPV6_V6ONLY: %s\n", errorBuffer);
463 goto error_handler;
464 }
465 memset(&__ss, 0, sizeof(__ss));
466 ssdpAddr6->sin6_family = (sa_family_t)AF_INET6;
467 ssdpAddr6->sin6_addr = in6addr_any;
468#ifndef _WIN32
469 ssdpAddr6->sin6_scope_id = gIF_INDEX;
470#endif
471 ssdpAddr6->sin6_port = htons(SSDP_PORT);
472 ret = umock::sys_socket_h.bind(*ssdpSock, (struct sockaddr*)ssdpAddr6,
473 sizeof(*ssdpAddr6));
474 if (ret == -1) {
475#ifndef _WIN32
476 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
477 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
478 "Error in bind(), addr=%s, index=%u, port=%d: %s\n",
479 gIF_IPV6, gIF_INDEX, SSDP_PORT, errorBuffer);
480 ret = UPNP_E_SOCKET_BIND;
481 goto error_handler;
482#else
483 int wsa_err = umock::winsock2_h.WSAGetLastError();
484 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
485 "Error in bind(), addr=%s, index=%u, port=%d: %d\n",
486 gIF_IPV6, gIF_INDEX, SSDP_PORT, wsa_err);
487 ret = UPNP_E_SOCKET_BIND;
488 goto error_handler;
489#endif
490 }
491 memset((void*)&ssdpMcastAddr, 0, sizeof(ssdpMcastAddr));
492 ssdpMcastAddr.ipv6mr_interface = gIF_INDEX;
493 inet_pton(AF_INET6, SSDP_IPV6_LINKLOCAL, &ssdpMcastAddr.ipv6mr_multiaddr);
494 ret = umock::sys_socket_h.setsockopt(*ssdpSock, IPPROTO_IPV6,
495 IPV6_JOIN_GROUP, (char*)&ssdpMcastAddr,
496 sizeof(ssdpMcastAddr));
497 if (ret == -1) {
498 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
499 uint32_t* p = (uint32_t*)&ssdpMcastAddr.ipv6mr_multiaddr;
500 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
501 "Error in setsockopt() IPV6_JOIN_GROUP (join multicast "
502 "group): %s.\n"
503 "SSDP_IPV6_LINKLOCAL = %s,\n"
504 "ipv6mr_interface = %u,\n"
505 "ipv6mr_multiaddr[0,1,2,3] = "
506 "0x%08X:0x%08X:0x%08X:0x%08X\n"
507 "gIF_NAME = %s\n",
508 errorBuffer, SSDP_IPV6_LINKLOCAL,
509 ssdpMcastAddr.ipv6mr_interface, p[0], p[1], p[2], p[3],
510 gIF_NAME);
512 goto error_handler;
513 }
514 onOff = 1;
515 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_BROADCAST,
516 (char*)&onOff, sizeof(onOff));
517 if (ret == -1) {
518 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
519 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
520 "Error in setsockopt() SO_BROADCAST (set broadcast): "
521 "%s\n",
522 errorBuffer);
524 goto error_handler;
525 }
526 ret = UPNP_E_SUCCESS;
527
528error_handler:
529 if (ret != UPNP_E_SUCCESS) {
530 umock::unistd_h.CLOSE_SOCKET_P(*ssdpSock);
531 }
532
533 return ret;
534}
535#endif /* IPv6 */
536
537#ifdef UPNP_ENABLE_IPV6
551 SOCKET* ssdpSock) {
552 UPnPsdk_LOGINFO("MSG1084") "Executing...\n";
553 char errorBuffer[ERROR_BUFFER_LEN];
554 ipv6_mreq ssdpMcastAddr;
555 int onOff;
556 int ret = 0;
557
558 *ssdpSock = umock::sys_socket_h.socket(AF_INET6, SOCK_DGRAM, 0);
559 if (*ssdpSock == INVALID_SOCKET) {
560 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
561 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
562 "Error in socket(): %s\n", errorBuffer);
563
564 return UPNP_E_OUTOF_SOCKET;
565 }
566 onOff = 1;
567 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEADDR,
568 (char*)&onOff, sizeof(onOff));
569 if (ret == -1) {
570 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
571 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
572 "Error in setsockopt() SO_REUSEADDR: %s\n", errorBuffer);
574 goto error_handler;
575 }
576#if (defined(BSD) && !defined(__GNU__)) || defined(__APPLE__)
577 onOff = 1;
578 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEPORT,
579 (char*)&onOff, sizeof(onOff));
580 if (ret == -1) {
581 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
582 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
583 "Error in setsockopt() SO_REUSEPORT: %s\n", errorBuffer);
585 goto error_handler;
586 }
587#endif /* BSD, __APPLE__ */
588 onOff = 1;
589 ret = umock::sys_socket_h.setsockopt(*ssdpSock, IPPROTO_IPV6, IPV6_V6ONLY,
590 (char*)&onOff, sizeof(onOff));
591 if (ret == -1) {
592 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
593 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
594 "Error in setsockopt() IPV6_V6ONLY: %s\n", errorBuffer);
596 goto error_handler;
597 }
598
599 { // Bind socket
600 UPnPsdk::sockaddr_t saddr{};
601 saddr.sin6.sin6_family = AF_INET6;
602 saddr.sin6.sin6_addr = in6addr_any;
603 // saddr.sin6.sin6_scope_id = gIF_INDEX; // Not valid on a GUA
604 saddr.sin6.sin6_port = htons(SSDP_PORT);
605 UPnPsdk::CSocketErr serrObj;
606 ret =
607 umock::sys_socket_h.bind(*ssdpSock, &saddr.sa, sizeof(saddr.sin6));
608 if (ret == -1) {
609 serrObj.catch_error();
610 UPnPsdk_LOGERR("MSG1134") "in ::bind() with addr=\"[::]:"
611 << SSDP_PORT << "\": (" << serrObj << ") "
612 << serrObj.error_str() << '\n';
613 ret = UPNP_E_SOCKET_BIND;
614 goto error_handler;
615 }
616 }
617 memset((void*)&ssdpMcastAddr, 0, sizeof(ssdpMcastAddr));
618 ssdpMcastAddr.ipv6mr_interface = gIF_INDEX;
619 /* SITE LOCAL */
620 inet_pton(AF_INET6, SSDP_IPV6_SITELOCAL, &ssdpMcastAddr.ipv6mr_multiaddr);
621 ret = umock::sys_socket_h.setsockopt(*ssdpSock, IPPROTO_IPV6,
622 IPV6_JOIN_GROUP, (char*)&ssdpMcastAddr,
623 sizeof(ssdpMcastAddr));
624 if (ret == -1) {
625 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
626 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
627 "Error in setsockopt() IPV6_JOIN_GROUP (join multicast "
628 "group): %s\n",
629 errorBuffer);
631 goto error_handler;
632 }
633 onOff = 1;
634 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_BROADCAST,
635 (char*)&onOff, sizeof(onOff));
636 if (ret == -1) {
637 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
638 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
639 "Error in setsockopt() SO_BROADCAST (set broadcast): "
640 "%s\n",
641 errorBuffer);
643 goto error_handler;
644 }
645 ret = UPNP_E_SUCCESS;
646
647error_handler:
648 if (ret != UPNP_E_SUCCESS) {
649 umock::unistd_h.CLOSE_SOCKET_P(*ssdpSock);
650 }
651
652 return ret;
653}
654#endif /* IPv6 */
655
657} // anonymous namespace
658
659
660int unique_service_name(char* cmd, SsdpEvent* Evt) {
661 char TempBuf[LINE_SIZE - 3];
662 char* TempPtr = NULL;
663 char* Ptr = NULL;
664 char* ptr1 = NULL;
665 char* ptr2 = NULL;
666 char* ptr3 = NULL;
667 int CommandFound = 0;
668 size_t n = (size_t)0;
669
670 if (strstr(cmd, "uuid:schemas") != NULL) {
671 ptr1 = strstr(cmd, ":device");
672 if (ptr1 != NULL)
673 ptr2 = strstr(ptr1 + 1, ":");
674 else
675 return -1;
676 if (ptr2 != NULL)
677 ptr3 = strstr(ptr2 + 1, ":");
678 else
679 return -1;
680 if (ptr3 != NULL) {
681 if (strlen("uuid:") + strlen(ptr3 + 1) >= sizeof Evt->UDN)
682 return -1;
683 snprintf(Evt->UDN, sizeof Evt->UDN, "uuid:%s", ptr3 + 1);
684 } else
685 return -1;
686 ptr1 = strstr(cmd, ":");
687 if (ptr1 != NULL) {
688 n = (size_t)ptr3 - (size_t)ptr1;
689 n = n >= sizeof TempBuf ? sizeof TempBuf - 1 : n;
690 strncpy(TempBuf, ptr1, n);
691 TempBuf[n] = '\0';
692 if (strlen("urn") + strlen(TempBuf) >= sizeof(Evt->DeviceType))
693 return -1;
694 snprintf(Evt->DeviceType, sizeof(Evt->DeviceType), "urn%s",
695 TempBuf);
696 } else
697 return -1;
698 return 0;
699 }
700 if ((TempPtr = strstr(cmd, "uuid")) != NULL) {
701 if ((Ptr = strstr(cmd, "::")) != NULL) {
702 n = (size_t)Ptr - (size_t)TempPtr;
703 n = n >= sizeof Evt->UDN ? sizeof Evt->UDN - 1 : n;
704 strncpy(Evt->UDN, TempPtr, n);
705 Evt->UDN[n] = '\0';
706 } else {
707 memset(Evt->UDN, 0, sizeof(Evt->UDN));
708 strncpy(Evt->UDN, TempPtr, sizeof Evt->UDN - 1);
709 }
710 CommandFound = 1;
711 }
712 if (strstr(cmd, "urn:") != NULL && strstr(cmd, ":service:") != NULL) {
713 if ((TempPtr = strstr(cmd, "urn")) != NULL) {
714 memset(Evt->ServiceType, 0, sizeof Evt->ServiceType);
715 strncpy(Evt->ServiceType, TempPtr, sizeof Evt->ServiceType - 1);
716 CommandFound = 1;
717 }
718 }
719 if (strstr(cmd, "urn:") != NULL && strstr(cmd, ":device:") != NULL) {
720 if ((TempPtr = strstr(cmd, "urn")) != NULL) {
721 memset(Evt->DeviceType, 0, sizeof Evt->DeviceType);
722 strncpy(Evt->DeviceType, TempPtr, sizeof Evt->DeviceType - 1);
723 CommandFound = 1;
724 }
725 }
726 if ((TempPtr = strstr(cmd, "::upnp:rootdevice")) != NULL) {
727 /* Everything before "::upnp::rootdevice" is the UDN. */
728 if (TempPtr != cmd) {
729 n = (size_t)TempPtr - (size_t)cmd;
730 n = n >= sizeof Evt->UDN ? sizeof Evt->UDN - 1 : n;
731 strncpy(Evt->UDN, cmd, n);
732 Evt->UDN[n] = 0;
733 CommandFound = 1;
734 }
735 }
736 if (CommandFound == 0)
737 return -1;
738
739 return 0;
740}
741
743 if (strstr(cmd, ":all"))
744 return SSDP_ALL;
745 if (strstr(cmd, ":rootdevice"))
746 return SSDP_ROOTDEVICE;
747 if (strstr(cmd, "uuid:"))
748 return SSDP_DEVICEUDN;
749 if (strstr(cmd, "urn:") && strstr(cmd, ":device:"))
750 return SSDP_DEVICETYPE;
751 if (strstr(cmd, "urn:") && strstr(cmd, ":service:"))
752 return SSDP_SERVICE;
753 return SSDP_SERROR;
754}
755
756int ssdp_request_type(char* cmd, SsdpEvent* Evt) {
757 /* clear event */
758 memset(Evt, 0, sizeof(SsdpEvent));
759 unique_service_name(cmd, Evt);
760 Evt->ErrCode = NO_ERROR_FOUND;
761 if ((Evt->RequestType = ssdp_request_type1(cmd)) == SSDP_SERROR) {
762 Evt->ErrCode = E_HTTP_SYNTEX;
763 return -1;
764 }
765 return 0;
766}
767
768
769int readFromSSDPSocket(SOCKET socket) {
770 TRACE("Executing readFromSSDPSocket()")
771
772 char staticBuf[BUFSIZE];
773 char* requestBuf = staticBuf;
774 /* in case memory can't be allocated, still drain the socket using a
775 * static buffer. */
776 ssdp_thread_data* data =
777 static_cast<ssdp_thread_data*>(malloc(sizeof(ssdp_thread_data)));
778 if (data) {
779 /* initialize parser */
780#ifdef COMPA_HAVE_CTRLPT_SSDP
781 if (socket == gSsdpReqSocket4 || socket == gSsdpReqSocket6)
782 parser_response_init(&data->parser, HTTPMETHOD_MSEARCH);
783 else
785#else /* COMPA_HAVE_CTRLPT_SSDP */
787#endif /* COMPA_HAVE_CTRLPT_SSDP */
788 /* set size of parser buffer */
789 if (membuffer_set_size(&data->parser.msg.msg, BUFSIZE) == 0)
790 /* use this as the buffer for recv */
791 requestBuf = data->parser.msg.msg.buf;
792 else {
793 free(data);
794 data = nullptr;
795 }
796 }
797 struct sockaddr_storage __ss;
798 socklen_t socklen = sizeof(__ss);
799 ssize_t byteReceived = umock::sys_socket_h.recvfrom(
800 socket, requestBuf, BUFSIZE - 1, 0, reinterpret_cast<sockaddr*>(&__ss),
801 &socklen);
802 if (byteReceived > 0) {
803 requestBuf[byteReceived] = '\0';
804 char ntop_buf[INET6_ADDRSTRLEN];
805
806 switch (__ss.ss_family) {
807 case AF_INET:
808 inet_ntop(AF_INET, &((struct sockaddr_in*)&__ss)->sin_addr,
809 ntop_buf, sizeof(ntop_buf));
810 break;
811 case AF_INET6:
812 inet_ntop(AF_INET6, &((struct sockaddr_in6*)&__ss)->sin6_addr,
813 ntop_buf, sizeof(ntop_buf));
814 break;
815 default:
816 memset(ntop_buf, 0, sizeof(ntop_buf));
817 strncpy(ntop_buf, "<Invalid address family>", sizeof(ntop_buf) - 1);
818 }
819 /* clang-format off */
820 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
821 "\nStart of received response ----------------------------------------------------\n"
822 "%s\n"
823 "End of received response ------------------------------------------------------\n"
824 "From host %s\n", requestBuf, ntop_buf);
825 /* clang-format on */
826
827 /* add thread pool job to handle request */
828 if (data != nullptr) {
829 // Cast is no problem; byteReceived is guarded above to be > 0.
830 data->parser.msg.msg.length += static_cast<size_t>(byteReceived);
831 /* null-terminate */
832 data->parser.msg.msg.buf[byteReceived] = '\0';
833 memcpy(&data->dest_addr, &__ss, sizeof(__ss));
834 ThreadPoolJob job{};
835 TPJobInit(&job, (UPnPsdk::start_routine)ssdp_event_handler_thread,
836 data);
838 TPJobSetPriority(&job, MED_PRIORITY);
839 if (ThreadPoolAdd(&gRecvThreadPool, &job, NULL) != 0) {
841 }
842 }
843 return 0;
844 }
846
847 return (byteReceived < 0) ? -1 : 0;
848}
849
850
852 int retVal;
853 UPnPsdk_LOGINFO("MSG1057") "Executing...\n";
854
855#if defined(COMPA_HAVE_CTRLPT_SSDP) || defined(DOXYGEN_RUN)
856 out->ssdpReqSock4 = INVALID_SOCKET;
857 out->ssdpReqSock6 = INVALID_SOCKET;
858 /* Create the IPv4 socket for SSDP REQUESTS */
859 if (strlen(gIF_IPV4) > (size_t)0) {
860 retVal = create_ssdp_sock_reqv4(&out->ssdpReqSock4);
861 if (retVal != UPNP_E_SUCCESS)
862 return retVal;
863 /* For use by ssdp control point. */
865 } else
866 out->ssdpReqSock4 = INVALID_SOCKET;
867 /* Create the IPv6 socket for SSDP REQUESTS */
868#ifdef UPNP_ENABLE_IPV6
869 if (strlen(gIF_IPV6) > (size_t)0) {
870 retVal = create_ssdp_sock_reqv6(&out->ssdpReqSock6);
871 if (retVal != UPNP_E_SUCCESS) {
872 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpReqSock4);
873 return retVal;
874 }
875 /* For use by ssdp control point. */
877 } else
878 out->ssdpReqSock6 = INVALID_SOCKET;
879#endif /* IPv6 */
880#endif /* COMPA_HAVE_CTRLPT_SSDP */
881 /* Create the IPv4 socket for SSDP */
882 if (strlen(gIF_IPV4) > (size_t)0) {
883 retVal = create_ssdp_sock_v4(&out->ssdpSock4);
884 if (retVal != UPNP_E_SUCCESS) {
885#ifdef COMPA_HAVE_CTRLPT_SSDP
886 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpReqSock4);
887 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpReqSock6);
888#endif
889 return retVal;
890 }
891 } else
892 out->ssdpSock4 = INVALID_SOCKET;
893#ifdef UPNP_ENABLE_IPV6
894 /* Create the IPv6 socket for SSDP */
895 if (strlen(gIF_IPV6) > (size_t)0) {
896 retVal = create_ssdp_sock_v6(&out->ssdpSock6);
897 if (retVal != UPNP_E_SUCCESS) {
898 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpSock4);
899#ifdef COMPA_HAVE_CTRLPT_SSDP
900 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpReqSock4);
901 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpReqSock6);
902#endif
903 return retVal;
904 }
905 } else
906 out->ssdpSock6 = INVALID_SOCKET;
907 if (strlen(gIF_IPV6_ULA_GUA) > (size_t)0) {
909 if (retVal != UPNP_E_SUCCESS) {
910 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpSock4);
911 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpSock6);
912#ifdef COMPA_HAVE_CTRLPT_SSDP
913 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpReqSock4);
914 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpReqSock6);
915#endif
916 return retVal;
917 }
918 } else
919 out->ssdpSock6UlaGua = INVALID_SOCKET;
920#endif /* UPNP_ENABLE_IPV6 */
921
922 return UPNP_E_SUCCESS;
923}
#define LINE_SIZE
Definition API.hpp:45
http_message_t msg
entire raw message
uri_type uri
Type of a uri, e.g. absolute, relative, etc.
http_method_t method
Http method of an outgoing request.
http_method_t
Method in a HTTP request.
membuffer msg
entire raw message.
#define HDR_HOST
Type of a HTTP header.
http_method_t request_method
Http method of an incoming response.
parse_status_t
Status of parsing.
@ PARSE_FAILURE
@ PARSE_SUCCESS
int valid_ssdp_notify_hack
read-only; this is set to 1 if a NOTIFY request has no content-length. used to read valid sspd notify...
Structure of an HTTP parser object.
Structure of an HTTP message.
int ThreadPoolAdd(ThreadPool *tp, ThreadPoolJob *job, int *jobId)
Adds a job to the thread pool.
int TPJobSetFreeFunction(ThreadPoolJob *job, free_routine func)
Sets the jobs free function.
int TPJobSetPriority(ThreadPoolJob *job, ThreadPriority priority)
Sets the priority of the threadpool job.
int TPJobInit(ThreadPoolJob *job, UPnPsdk::start_routine func, void *arg)
Initializes thread pool job.
Internal ThreadPool Job.
@ Relative
The URI is relative, means it hasn't a 'scheme' component.
size_t size
Size of the buffer.
Definition uri.hpp:50
const char * buff
Buffer.
Definition uri.hpp:49
token pathquery
Member variable.
Definition uri.hpp:70
uriType type
Member variable.
Definition uri.hpp:67
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
int unique_service_name(char *cmd, SsdpEvent *Evt)
Fills the fields of the event structure like DeviceType, Device UDN and Service Type.
void ssdp_handle_ctrlpt_msg(http_message_t *hmsg, sockaddr_storage *dest_addr, int timeout)
This function handles the ssdp messages from the devices.
Helpful union of the different socket address structures.
Definition sockaddr.hpp:34
parse_status_t parser_parse(http_parser_t *parser)
The parser function.
void parser_request_init(http_parser_t *parser)
Initializes parser object for a request.
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'.
void parser_response_init(http_parser_t *parser, http_method_t request_method)
Initializes parser object for a response.
void httpmsg_destroy(http_message_t *msg)
Free memory allocated for the http message.
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
int membuffer_set_size(membuffer *m, size_t new_length)
Increases or decreases buffer cap so that at least 'new_length' bytes can be stored.
size_t length
length of buffer without terminating null byte (read-only).
Definition membuffer.hpp:65
char * buf
mem buffer; must not write beyond buf[length-1] (read/write).
Definition membuffer.hpp:63
pointer to a chunk of memory.
Definition membuffer.hpp:50
#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_SOCKET_BIND
The SDK had a problem binding a socket to a network interface.
Definition messages.hpp:199
#define UPNP_E_SUCCESS
The operation completed successfully.
Definition messages.hpp:27
#define UPNP_E_OUTOF_SOCKET
The SDK cannot create any more sockets.
Definition messages.hpp:219
SOCKET ssdpSock4
IPv4 SSDP datagram Socket for incoming advertisments and search requests.
SOCKET ssdpReqSock6
IPv6 SSDP socket for sending search requests and receiving search replies.
SOCKET ssdpReqSock4
IPv4 SSDP socket for sending search requests and receiving search replies.
SOCKET ssdpSock6
IPv6 LLA SSDP Socket for incoming advertisments and search requests.
SOCKET ssdpSock6UlaGua
IPv6 ULA or GUA SSDP Socket for incoming advertisments and search requests.
Provides sockets for all network communications.
int create_ssdp_sock_v6(SOCKET *ssdpSock)
Create an SSDP IPv6 socket.
void free_ssdp_event_handler_data(void *the_data)
Frees the ssdp request.
int create_ssdp_sock_v4(SOCKET *ssdpSock)
Create an SSDP IPv4 socket.
int start_event_handler(void *Data)
Parses the message and dispatches it to a handler which handles the ssdp request msg.
void ssdp_event_handler_thread(void *the_data)
This function is a thread that handles SSDP requests.
int valid_ssdp_msg(http_message_t *hmsg)
Does some quick checking of the ssdp msg.
int create_ssdp_sock_v6_ula_gua(SOCKET *ssdpSock)
Create an SSDP IPv6 ULA_GUA socket.
int create_ssdp_sock_reqv6(SOCKET *ssdpReqSock)
Creates the SSDP IPv6 socket to be used by the control point.
int create_ssdp_sock_reqv4(SOCKET *ssdpReqSock)
Creates the SSDP IPv4 socket to be used by the control point.
int ssdp_request_type(char *cmd, SsdpEvent *Evt)
Starts filling the SSDP event structure based upon the request received.
int get_ssdp_sockets(MiniServerSockArray *out)
Creates the IPv4 and IPv6 ssdp sockets required by the control point and device operation.
SsdpSearchType ssdp_request_type1(char *cmd)
This function figures out the type of the SSDP search in the request.
int readFromSSDPSocket(SOCKET socket)
This function reads the data from the ssdp socket.
#define SSDP_PORT
constant
sockaddr_storage dest_addr
destination socket address
char UDN[LINE_SIZE]
Part of SSDP Event.
constexpr size_t ERROR_BUFFER_LEN
Size of the errorBuffer variable, passed to the strerror_r() function.
#define SSDP_IP
constant
#define SSDP_IPV6_SITELOCAL
constant
char ServiceType[LINE_SIZE]
Part of SSDP Event.
int ErrCode
Part of SSDP Event.
#define SSDP_IPV6_LINKLOCAL
constant
char DeviceType[LINE_SIZE]
Part of SSDP Event.
#define E_HTTP_SYNTEX
error code
http_parser_t parser
parser
SOCKET gSsdpReqSocket6
If control point API is compiled in, this is the global IPv6 socket for it.
enum SsdpSearchType RequestType
Part of SSDP Event.
#define NO_ERROR_FOUND
error code
SOCKET gSsdpReqSocket4
If control point API is compiled in, this is the global IPv4 socket for it.
#define BUFSIZE
constant
SsdpSearchType
Enumeration to define all different types of ssdp searches.
@ SSDP_SERROR
Unknown search command.
@ 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 thread data.
Manage "Step 1: Discovery" of the UPnP+™ specification for Control Points with SSDP.
Manage "Step 1: Discovery" of the UPnP+™ specification for UPnP Devices with SSDP.
void ssdp_handle_device_request(http_message_t *hmsg, struct sockaddr_storage *dest_addr)
Handles the search request.
char gIF_IPV6_ULA_GUA[INET6_ADDRSTRLEN]
IPv6 GUA buffer to contain interface IPv6 global-unicast address.
Definition upnpapi.cpp:145
ThreadPool gRecvThreadPool
Receive thread pool.
Definition upnpapi.cpp:114
char gIF_IPV4[INET_ADDRSTRLEN]
IPv4 buffer to contain interface address. (extern'ed in upnp.h)
Definition upnpapi.cpp:152
char gIF_NAME[LINE_SIZE]
Buffer to contain used network interface name. (extern'ed in upnp.h)
Definition upnpapi.cpp:133
unsigned gIF_INDEX
Index/scope-id from the used network interface.
Definition upnpapi.cpp:135
char gIF_IPV6[INET6_ADDRSTRLEN]
IPv6 LLA buffer to contain interface address. (extern'ed in upnp.h)
Definition upnpapi.cpp:138
Inititalize the compatible library before it can be used.
UPnPsdk_VIS void UpnpPrintf(Upnp_LogLevel DLevel, Dbg_Module Module, const char *DbgFileName, int DbgLineNo, const char *FmtStr,...)
Prints the debug statement.