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: 2025-05-16
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 <UPnPsdk/socket.hpp>
55#include <umock/sys_socket.hpp>
56#include <umock/winsock2.hpp>
57
58
59namespace {
60
61using compa::uriType::Relative;
62
72 void* the_data) {
73 ssdp_thread_data* data = (ssdp_thread_data*)the_data;
74
75 if (data != NULL) {
76 http_message_t* hmsg = &data->parser.msg;
77 /* free data */
78 httpmsg_destroy(hmsg);
79 free(data);
80 }
81}
82
88inline int valid_ssdp_msg(
91 http_message_t* hmsg) {
92 memptr hdr_value;
93
94 /* check for valid methods - NOTIFY or M-SEARCH */
95 if (hmsg->method != (http_method_t)HTTPMETHOD_NOTIFY &&
96 hmsg->method != (http_method_t)HTTPMETHOD_MSEARCH &&
97 hmsg->request_method != (http_method_t)HTTPMETHOD_MSEARCH) {
98 return 0;
99 }
100 if (hmsg->request_method != (http_method_t)HTTPMETHOD_MSEARCH) {
101 /* check PATH == "*" */
102 if (hmsg->uri.type != Relative ||
103 strncmp("*", hmsg->uri.pathquery.buff, hmsg->uri.pathquery.size) !=
104 0) {
105 return 0;
106 }
107 /* check HOST header */
108 if (httpmsg_find_hdr(hmsg, HDR_HOST, &hdr_value) == NULL ||
109 (memptr_cmp(&hdr_value, "239.255.255.250:1900") != 0 &&
110 memptr_cmp(&hdr_value, "[FF02::C]:1900") != 0 &&
111 memptr_cmp(&hdr_value, "[ff02::c]:1900") != 0 &&
112 memptr_cmp(&hdr_value, "[FF05::C]:1900") != 0 &&
113 memptr_cmp(&hdr_value, "[ff05::c]:1900") != 0)) {
114 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
115 "Invalid HOST header from SSDP message\n");
116
117 return 0;
118 }
119 }
120
121 /* passed quick check */
122 return 1;
123}
124
136 void* Data) {
137 http_parser_t* parser = NULL;
138 parse_status_t status;
139 ssdp_thread_data* data = (ssdp_thread_data*)Data;
140
141 parser = &data->parser;
142 status = parser_parse(parser);
143 if (status == (parse_status_t)PARSE_FAILURE) {
144 if (parser->msg.method != (http_method_t)HTTPMETHOD_NOTIFY ||
145 !parser->valid_ssdp_notify_hack) {
146 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
147 "SSDP recvd bad msg code = %d\n", status);
148 /* ignore bad msg, or not enuf mem */
149 goto error_handler;
150 }
151 /* valid notify msg */
152 } else if (status != (parse_status_t)PARSE_SUCCESS) {
153 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
154 "SSDP recvd bad msg code = %d\n", status);
155
156 goto error_handler;
157 }
158 /* check msg */
159 if (valid_ssdp_msg(&parser->msg) != 1) {
160 goto error_handler;
161 }
162 /* done; thread will free 'data' */
163 return 0;
164
165error_handler:
167 return -1;
168}
169
176 void* the_data) {
177 ssdp_thread_data* data = (ssdp_thread_data*)the_data;
178 http_message_t* hmsg = &data->parser.msg;
179
180 if (start_event_handler(the_data) != 0)
181 return;
182 /* send msg to device or ctrlpt */
183 if (hmsg->method == (http_method_t)HTTPMETHOD_NOTIFY ||
184 hmsg->request_method == (http_method_t)HTTPMETHOD_MSEARCH) {
186#ifdef COMPA_HAVE_CTRLPT_SSDP
187 ssdp_handle_ctrlpt_msg(hmsg, &data->dest_addr, 0);
188#endif
189 } else {
190#ifdef COMPA_HAVE_DEVICE_SSDP
192#endif
193 }
194
195 /* free data */
197}
198
212 SOCKET* ssdpSock) {
213 UPnPsdk_LOGINFO("MSG1075") "Executing...\n";
214 int onOff;
215 u_char ttl = (u_char)4;
216 ip_mreq ssdpMcastAddr;
217 sockaddr_storage __ss;
218 sockaddr_in* ssdpAddr4 = (struct sockaddr_in*)&__ss;
219 int ret = 0;
220 in_addr addr;
221 UPnPsdk::CSocketErr sockerrObj;
222
223 *ssdpSock = umock::sys_socket_h.socket(AF_INET, SOCK_DGRAM, 0);
224 if (*ssdpSock == INVALID_SOCKET) {
225 sockerrObj.catch_error();
226 UPnPsdk_LOGCRIT("MSG1076") "Error in socket(): "
227 << sockerrObj.error_str() << ".\n";
228 return UPNP_E_OUTOF_SOCKET;
229 }
230 onOff = 1;
231 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEADDR,
232 (char*)&onOff, sizeof(onOff));
233 if (ret == -1) {
234 sockerrObj.catch_error();
235 UPnPsdk_LOGCRIT("MSG1077") "Error in setsockopt() SO_REUSEADDR: "
236 << sockerrObj.error_str() << ".\n";
238 goto error_handler;
239 }
240#if (defined(BSD) && !defined(__GNU__)) || defined(__APPLE__)
241 onOff = 1;
242 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEPORT,
243 (char*)&onOff, sizeof(onOff));
244 if (ret == -1) {
245 sockerrObj.catch_error();
246 UPnPsdk_LOGCRIT("MSG1078") "Error in setsockopt() SO_REUSEP: "
247 << sockerrObj.error_str() << ".\n";
249 goto error_handler;
250 }
251#endif /* BSD, __APPLE__ */
252 memset(&__ss, 0, sizeof(__ss));
253 ssdpAddr4->sin_family = (sa_family_t)AF_INET;
254 ssdpAddr4->sin_addr.s_addr = htonl(INADDR_ANY);
255 ssdpAddr4->sin_port = htons(SSDP_PORT);
256 ret = umock::sys_socket_h.bind(*ssdpSock, (sockaddr*)ssdpAddr4,
257 sizeof(*ssdpAddr4));
258 if (ret == -1) {
259 sockerrObj.catch_error();
260 UPnPsdk_LOGCRIT("MSG1079") "Error in bind(), addr="
261 << INADDR_ANY << ", port=" << SSDP_PORT << ": "
262 << sockerrObj.error_str() << ".\n";
263 ret = UPNP_E_SOCKET_BIND;
264 goto error_handler;
265 }
266 /*
267 * See: https://man7.org/linux/man-pages/man7/ip.7.html
268 * Socket options, IP_ADD_MEMBERSHIP
269 *
270 * This memset actually sets imr_address to INADDR_ANY and
271 * imr_ifindex to zero, which make the system choose the appropriate
272 * interface.
273 *
274 * Still using "struct ip_mreq" instead of "struct ip_mreqn" because
275 * windows does not recognize the latter.
276 */
277 memset((void*)&ssdpMcastAddr, 0, sizeof ssdpMcastAddr);
278 inet_pton(AF_INET, gIF_IPV4, &ssdpMcastAddr.imr_interface);
279 inet_pton(AF_INET, SSDP_IP, &ssdpMcastAddr.imr_multiaddr);
280 ret = umock::sys_socket_h.setsockopt(
281 *ssdpSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&ssdpMcastAddr,
282 sizeof(struct ip_mreq));
283 if (ret == -1) {
284 sockerrObj.catch_error();
285 UPnPsdk_LOGCRIT("MSG1080") "Error in setsockopt() IP_ADD_MEMBERSHIP "
286 "(join multicast group): "
287 << sockerrObj.error_str() << ".\n";
289 goto error_handler;
290 }
291 /* Set multicast interface. */
292 memset((void*)&addr, 0, sizeof(struct in_addr));
293 inet_pton(AF_INET, gIF_IPV4, &addr);
294 ret = umock::sys_socket_h.setsockopt(*ssdpSock, IPPROTO_IP, IP_MULTICAST_IF,
295 (char*)&addr, sizeof addr);
296 if (ret == -1) {
297 sockerrObj.catch_error();
298 UPnPsdk_LOGINFO("MSG1081") "Error in setsockopt() IP_MULTICAST_IF (set "
299 "multicast interface): "
300 << sockerrObj.error_str() << ".\n";
301 /* This is probably not a critical error, so let's continue. */
302 }
303 /* result is not checked becuase it will fail in WinMe and Win9x. */
304 umock::sys_socket_h.setsockopt(*ssdpSock, IPPROTO_IP, IP_MULTICAST_TTL,
305 (const char*)&ttl, sizeof(ttl));
306 onOff = 1;
307 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_BROADCAST,
308 (char*)&onOff, sizeof(onOff));
309 if (ret == -1) {
310 sockerrObj.catch_error();
311 UPnPsdk_LOGCRIT(
312 "MSG1082") "Error in setsockopt() SO_BROADCAST (set broadcast): "
313 << sockerrObj.error_str() << ".\n";
315 goto error_handler;
316 }
317 ret = UPNP_E_SUCCESS;
318
319error_handler:
320 if (ret != UPNP_E_SUCCESS) {
321 umock::unistd_h.CLOSE_SOCKET_P(*ssdpSock);
322 }
323
324 return ret;
325}
326
327#ifdef UPNP_ENABLE_IPV6
341 SOCKET* ssdpSock) {
342 UPnPsdk_LOGINFO("MSG1083") "Executing...\n";
343 char errorBuffer[ERROR_BUFFER_LEN];
344 ipv6_mreq ssdpMcastAddr;
345 sockaddr_storage __ss;
346 sockaddr_in6* ssdpAddr6 = (struct sockaddr_in6*)&__ss;
347 int onOff;
348 int ret = 0;
349
350 *ssdpSock = umock::sys_socket_h.socket(AF_INET6, SOCK_DGRAM, 0);
351 if (*ssdpSock == INVALID_SOCKET) {
352 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
353 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
354 "Error in socket(): %s\n", errorBuffer);
355
356 return UPNP_E_OUTOF_SOCKET;
357 }
358 onOff = 1;
359 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEADDR,
360 (char*)&onOff, sizeof(onOff));
361 if (ret == -1) {
362 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
363 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
364 "Error in setsockopt() SO_REUSEADDR: %s\n", errorBuffer);
366 goto error_handler;
367 }
368#if (defined(BSD) && !defined(__GNU__)) || defined(__APPLE__)
369 onOff = 1;
370 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEPORT,
371 (char*)&onOff, sizeof(onOff));
372 if (ret == -1) {
373 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
374 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
375 "Error in setsockopt() SO_REUSEPORT: %s\n", errorBuffer);
377 goto error_handler;
378 }
379#endif /* BSD, __APPLE__ */
380 onOff = 1;
381 ret = umock::sys_socket_h.setsockopt(*ssdpSock, IPPROTO_IPV6, IPV6_V6ONLY,
382 (char*)&onOff, sizeof(onOff));
383 if (ret == -1) {
384 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
385 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
386 "Error in setsockopt() IPV6_V6ONLY: %s\n", errorBuffer);
388 goto error_handler;
389 }
390 memset(&__ss, 0, sizeof(__ss));
391 ssdpAddr6->sin6_family = (sa_family_t)AF_INET6;
392 ssdpAddr6->sin6_addr = in6addr_any;
393#ifndef _WIN32
394 ssdpAddr6->sin6_scope_id = gIF_INDEX;
395#endif
396 ssdpAddr6->sin6_port = htons(SSDP_PORT);
397 ret = umock::sys_socket_h.bind(*ssdpSock, (struct sockaddr*)ssdpAddr6,
398 sizeof(*ssdpAddr6));
399 if (ret == -1) {
400#ifndef _WIN32
401 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
402 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
403 "Error in bind(), addr=%s, index=%d, port=%d: %s\n",
404 gIF_IPV6, gIF_INDEX, SSDP_PORT, errorBuffer);
405 ret = UPNP_E_SOCKET_BIND;
406 goto error_handler;
407#else
408 int wsa_err = umock::winsock2_h.WSAGetLastError();
409 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
410 "Error in bind(), addr=%s, index=%d, port=%d: %d\n",
411 gIF_IPV6, gIF_INDEX, SSDP_PORT, wsa_err);
412 ret = UPNP_E_SOCKET_BIND;
413 goto error_handler;
414#endif
415 }
416 memset((void*)&ssdpMcastAddr, 0, sizeof(ssdpMcastAddr));
417 ssdpMcastAddr.ipv6mr_interface = gIF_INDEX;
418 inet_pton(AF_INET6, SSDP_IPV6_LINKLOCAL, &ssdpMcastAddr.ipv6mr_multiaddr);
419 ret = umock::sys_socket_h.setsockopt(*ssdpSock, IPPROTO_IPV6,
420 IPV6_JOIN_GROUP, (char*)&ssdpMcastAddr,
421 sizeof(ssdpMcastAddr));
422 if (ret == -1) {
423 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
424 uint32_t* p = (uint32_t*)&ssdpMcastAddr.ipv6mr_multiaddr;
425 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
426 "Error in setsockopt() IPV6_JOIN_GROUP (join multicast "
427 "group): %s.\n"
428 "SSDP_IPV6_LINKLOCAL = %s,\n"
429 "ipv6mr_interface = %u,\n"
430 "ipv6mr_multiaddr[0,1,2,3] = "
431 "0x%08X:0x%08X:0x%08X:0x%08X\n"
432 "gIF_NAME = %s\n",
433 errorBuffer, SSDP_IPV6_LINKLOCAL,
434 ssdpMcastAddr.ipv6mr_interface, p[0], p[1], p[2], p[3],
435 gIF_NAME);
437 goto error_handler;
438 }
439 onOff = 1;
440 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_BROADCAST,
441 (char*)&onOff, sizeof(onOff));
442 if (ret == -1) {
443 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
444 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
445 "Error in setsockopt() SO_BROADCAST (set broadcast): "
446 "%s\n",
447 errorBuffer);
449 goto error_handler;
450 }
451 ret = UPNP_E_SUCCESS;
452
453error_handler:
454 if (ret != UPNP_E_SUCCESS) {
455 umock::unistd_h.CLOSE_SOCKET_P(*ssdpSock);
456 }
457
458 return ret;
459}
460#endif /* IPv6 */
461
462#ifdef UPNP_ENABLE_IPV6
476 SOCKET* ssdpSock) {
477 UPnPsdk_LOGINFO("MSG1084") "Executing...\n";
478 char errorBuffer[ERROR_BUFFER_LEN];
479 ipv6_mreq ssdpMcastAddr;
480 int onOff;
481 int ret = 0;
482
483 *ssdpSock = umock::sys_socket_h.socket(AF_INET6, SOCK_DGRAM, 0);
484 if (*ssdpSock == INVALID_SOCKET) {
485 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
486 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
487 "Error in socket(): %s\n", errorBuffer);
488
489 return UPNP_E_OUTOF_SOCKET;
490 }
491 onOff = 1;
492 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEADDR,
493 (char*)&onOff, sizeof(onOff));
494 if (ret == -1) {
495 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
496 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
497 "Error in setsockopt() SO_REUSEADDR: %s\n", errorBuffer);
499 goto error_handler;
500 }
501#if (defined(BSD) && !defined(__GNU__)) || defined(__APPLE__)
502 onOff = 1;
503 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEPORT,
504 (char*)&onOff, sizeof(onOff));
505 if (ret == -1) {
506 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
507 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
508 "Error in setsockopt() SO_REUSEPORT: %s\n", errorBuffer);
510 goto error_handler;
511 }
512#endif /* BSD, __APPLE__ */
513 onOff = 1;
514 ret = umock::sys_socket_h.setsockopt(*ssdpSock, IPPROTO_IPV6, IPV6_V6ONLY,
515 (char*)&onOff, sizeof(onOff));
516 if (ret == -1) {
517 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
518 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
519 "Error in setsockopt() IPV6_V6ONLY: %s\n", errorBuffer);
521 goto error_handler;
522 }
523
524 { // Bind socket
525 UPnPsdk::sockaddr_t saddr{};
526 saddr.sin6.sin6_family = AF_INET6;
527 saddr.sin6.sin6_addr = in6addr_any;
528 // saddr.sin6.sin6_scope_id = gIF_INDEX; // Not valid on a GUA
529 saddr.sin6.sin6_port = htons(SSDP_PORT);
530 UPnPsdk::CSocketErr serrObj;
531 ret =
532 umock::sys_socket_h.bind(*ssdpSock, &saddr.sa, sizeof(saddr.sin6));
533 if (ret == -1) {
534 serrObj.catch_error();
535 UPnPsdk_LOGERR("MSG1134") "in ::bind() with addr=\"[::]:"
536 << SSDP_PORT << "\": (" << serrObj << ") "
537 << serrObj.error_str() << '\n';
538 ret = UPNP_E_SOCKET_BIND;
539 goto error_handler;
540 }
541 }
542 memset((void*)&ssdpMcastAddr, 0, sizeof(ssdpMcastAddr));
543 ssdpMcastAddr.ipv6mr_interface = gIF_INDEX;
544 /* SITE LOCAL */
545 inet_pton(AF_INET6, SSDP_IPV6_SITELOCAL, &ssdpMcastAddr.ipv6mr_multiaddr);
546 ret = umock::sys_socket_h.setsockopt(*ssdpSock, IPPROTO_IPV6,
547 IPV6_JOIN_GROUP, (char*)&ssdpMcastAddr,
548 sizeof(ssdpMcastAddr));
549 if (ret == -1) {
550 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
551 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
552 "Error in setsockopt() IPV6_JOIN_GROUP (join multicast "
553 "group): %s\n",
554 errorBuffer);
556 goto error_handler;
557 }
558 onOff = 1;
559 ret = umock::sys_socket_h.setsockopt(*ssdpSock, SOL_SOCKET, SO_BROADCAST,
560 (char*)&onOff, sizeof(onOff));
561 if (ret == -1) {
562 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
563 UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
564 "Error in setsockopt() SO_BROADCAST (set broadcast): "
565 "%s\n",
566 errorBuffer);
568 goto error_handler;
569 }
570 ret = UPNP_E_SUCCESS;
571
572error_handler:
573 if (ret != UPNP_E_SUCCESS) {
574 umock::unistd_h.CLOSE_SOCKET_P(*ssdpSock);
575 }
576
577 return ret;
578}
579#endif /* IPv6 */
580
582} // anonymous namespace
583
584
585int unique_service_name(char* cmd, SsdpEvent* Evt) {
586 char TempBuf[LINE_SIZE - 3];
587 char* TempPtr = NULL;
588 char* Ptr = NULL;
589 char* ptr1 = NULL;
590 char* ptr2 = NULL;
591 char* ptr3 = NULL;
592 int CommandFound = 0;
593 size_t n = (size_t)0;
594
595 if (strstr(cmd, "uuid:schemas") != NULL) {
596 ptr1 = strstr(cmd, ":device");
597 if (ptr1 != NULL)
598 ptr2 = strstr(ptr1 + 1, ":");
599 else
600 return -1;
601 if (ptr2 != NULL)
602 ptr3 = strstr(ptr2 + 1, ":");
603 else
604 return -1;
605 if (ptr3 != NULL) {
606 if (strlen("uuid:") + strlen(ptr3 + 1) >= sizeof Evt->UDN)
607 return -1;
608 snprintf(Evt->UDN, sizeof Evt->UDN, "uuid:%s", ptr3 + 1);
609 } else
610 return -1;
611 ptr1 = strstr(cmd, ":");
612 if (ptr1 != NULL) {
613 n = (size_t)ptr3 - (size_t)ptr1;
614 n = n >= sizeof TempBuf ? sizeof TempBuf - 1 : n;
615 strncpy(TempBuf, ptr1, n);
616 TempBuf[n] = '\0';
617 if (strlen("urn") + strlen(TempBuf) >= sizeof(Evt->DeviceType))
618 return -1;
619 snprintf(Evt->DeviceType, sizeof(Evt->DeviceType), "urn%s",
620 TempBuf);
621 } else
622 return -1;
623 return 0;
624 }
625 if ((TempPtr = strstr(cmd, "uuid")) != NULL) {
626 if ((Ptr = strstr(cmd, "::")) != NULL) {
627 n = (size_t)Ptr - (size_t)TempPtr;
628 n = n >= sizeof Evt->UDN ? sizeof Evt->UDN - 1 : n;
629 strncpy(Evt->UDN, TempPtr, n);
630 Evt->UDN[n] = '\0';
631 } else {
632 memset(Evt->UDN, 0, sizeof(Evt->UDN));
633 strncpy(Evt->UDN, TempPtr, sizeof Evt->UDN - 1);
634 }
635 CommandFound = 1;
636 }
637 if (strstr(cmd, "urn:") != NULL && strstr(cmd, ":service:") != NULL) {
638 if ((TempPtr = strstr(cmd, "urn")) != NULL) {
639 memset(Evt->ServiceType, 0, sizeof Evt->ServiceType);
640 strncpy(Evt->ServiceType, TempPtr, sizeof Evt->ServiceType - 1);
641 CommandFound = 1;
642 }
643 }
644 if (strstr(cmd, "urn:") != NULL && strstr(cmd, ":device:") != NULL) {
645 if ((TempPtr = strstr(cmd, "urn")) != NULL) {
646 memset(Evt->DeviceType, 0, sizeof Evt->DeviceType);
647 strncpy(Evt->DeviceType, TempPtr, sizeof Evt->DeviceType - 1);
648 CommandFound = 1;
649 }
650 }
651 if ((TempPtr = strstr(cmd, "::upnp:rootdevice")) != NULL) {
652 /* Everything before "::upnp::rootdevice" is the UDN. */
653 if (TempPtr != cmd) {
654 n = (size_t)TempPtr - (size_t)cmd;
655 n = n >= sizeof Evt->UDN ? sizeof Evt->UDN - 1 : n;
656 strncpy(Evt->UDN, cmd, n);
657 Evt->UDN[n] = 0;
658 CommandFound = 1;
659 }
660 }
661 if (CommandFound == 0)
662 return -1;
663
664 return 0;
665}
666
668 if (strstr(cmd, ":all"))
669 return SSDP_ALL;
670 if (strstr(cmd, ":rootdevice"))
671 return SSDP_ROOTDEVICE;
672 if (strstr(cmd, "uuid:"))
673 return SSDP_DEVICEUDN;
674 if (strstr(cmd, "urn:") && strstr(cmd, ":device:"))
675 return SSDP_DEVICETYPE;
676 if (strstr(cmd, "urn:") && strstr(cmd, ":service:"))
677 return SSDP_SERVICE;
678 return SSDP_SERROR;
679}
680
681int ssdp_request_type(char* cmd, SsdpEvent* Evt) {
682 /* clear event */
683 memset(Evt, 0, sizeof(SsdpEvent));
684 unique_service_name(cmd, Evt);
685 Evt->ErrCode = NO_ERROR_FOUND;
686 if ((Evt->RequestType = ssdp_request_type1(cmd)) == SSDP_SERROR) {
687 Evt->ErrCode = E_HTTP_SYNTEX;
688 return -1;
689 }
690 return 0;
691}
692
693
694int readFromSSDPSocket(SOCKET socket) {
695 TRACE("Executing readFromSSDPSocket()")
696 char* requestBuf = NULL;
697 char staticBuf[BUFSIZE];
698 struct sockaddr_storage __ss;
699 ThreadPoolJob job;
700 ssdp_thread_data* data = NULL;
701 socklen_t socklen = sizeof(__ss);
702 ssize_t byteReceived = 0;
703 char ntop_buf[INET6_ADDRSTRLEN];
704
705 memset(&job, 0, sizeof(job));
706
707 requestBuf = staticBuf;
708 /* in case memory can't be allocated, still drain the socket using a
709 * static buffer. */
710 data = (ssdp_thread_data*)malloc(sizeof(ssdp_thread_data));
711 if (data) {
712 /* initialize parser */
713#ifdef COMPA_HAVE_CTRLPT_SSDP
714 if (socket == gSsdpReqSocket4
715#ifdef UPNP_ENABLE_IPV6
716 || socket == gSsdpReqSocket6
717#endif /* UPNP_ENABLE_IPV6 */
718 )
719 parser_response_init(&data->parser, HTTPMETHOD_MSEARCH);
720 else
722#else /* COMPA_HAVE_CTRLPT_SSDP */
724#endif /* COMPA_HAVE_CTRLPT_SSDP */
725 /* set size of parser buffer */
726 if (membuffer_set_size(&data->parser.msg.msg, BUFSIZE) == 0)
727 /* use this as the buffer for recv */
728 requestBuf = data->parser.msg.msg.buf;
729 else {
730 free(data);
731 data = NULL;
732 }
733 }
734 byteReceived =
735 umock::sys_socket_h.recvfrom(socket, requestBuf, BUFSIZE - (size_t)1, 0,
736 (struct sockaddr*)&__ss, &socklen);
737 if (byteReceived > 0) {
738 requestBuf[byteReceived] = '\0';
739 switch (__ss.ss_family) {
740 case AF_INET:
741 inet_ntop(AF_INET, &((struct sockaddr_in*)&__ss)->sin_addr,
742 ntop_buf, sizeof(ntop_buf));
743 break;
744#ifdef UPNP_ENABLE_IPV6
745 case AF_INET6:
746 inet_ntop(AF_INET6, &((struct sockaddr_in6*)&__ss)->sin6_addr,
747 ntop_buf, sizeof(ntop_buf));
748 break;
749#endif /* UPNP_ENABLE_IPV6 */
750 default:
751 memset(ntop_buf, 0, sizeof(ntop_buf));
752 strncpy(ntop_buf, "<Invalid address family>", sizeof(ntop_buf) - 1);
753 }
754 /* clang-format off */
755 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
756 "Start of received response ----------------------------------------------------\n"
757 "%s\n"
758 "End of received response ------------------------------------------------------\n"
759 "From host %s\n", requestBuf, ntop_buf);
760 /* clang-format on */
761 /* add thread pool job to handle request */
762 if (data != NULL) {
763 data->parser.msg.msg.length += (size_t)byteReceived;
764 /* null-terminate */
765 data->parser.msg.msg.buf[byteReceived] = 0;
766 memcpy(&data->dest_addr, &__ss, sizeof(__ss));
767 TPJobInit(&job, (UPnPsdk::start_routine)ssdp_event_handler_thread,
768 data);
770 TPJobSetPriority(&job, MED_PRIORITY);
771 if (ThreadPoolAdd(&gRecvThreadPool, &job, NULL) != 0)
773 }
774 return 0;
775 } else {
777 return -1;
778 }
779}
780
781
783 int retVal;
784 UPnPsdk_LOGINFO("MSG1057") "Executing...\n";
785
786#ifdef COMPA_HAVE_CTRLPT_SSDP
787 out->ssdpReqSock4 = INVALID_SOCKET;
788 out->ssdpReqSock6 = INVALID_SOCKET;
789 /* Create the IPv4 socket for SSDP REQUESTS */
790 if (strlen(gIF_IPV4) > (size_t)0) {
791 retVal = create_ssdp_sock_reqv4(&out->ssdpReqSock4);
792 if (retVal != UPNP_E_SUCCESS)
793 return retVal;
794 /* For use by ssdp control point. */
796 } else
797 out->ssdpReqSock4 = INVALID_SOCKET;
798 /* Create the IPv6 socket for SSDP REQUESTS */
799#ifdef UPNP_ENABLE_IPV6
800 if (strlen(gIF_IPV6) > (size_t)0) {
801 retVal = create_ssdp_sock_reqv6(&out->ssdpReqSock6);
802 if (retVal != UPNP_E_SUCCESS) {
803 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpReqSock4);
804 return retVal;
805 }
806 /* For use by ssdp control point. */
808 } else
809 out->ssdpReqSock6 = INVALID_SOCKET;
810#endif /* IPv6 */
811#endif /* COMPA_HAVE_CTRLPT_SSDP */
812 /* Create the IPv4 socket for SSDP */
813 if (strlen(gIF_IPV4) > (size_t)0) {
814 retVal = create_ssdp_sock_v4(&out->ssdpSock4);
815 if (retVal != UPNP_E_SUCCESS) {
816#ifdef COMPA_HAVE_CTRLPT_SSDP
817 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpReqSock4);
818 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpReqSock6);
819#endif
820 return retVal;
821 }
822 } else
823 out->ssdpSock4 = INVALID_SOCKET;
824#ifdef UPNP_ENABLE_IPV6
825 /* Create the IPv6 socket for SSDP */
826 if (strlen(gIF_IPV6) > (size_t)0) {
827 retVal = create_ssdp_sock_v6(&out->ssdpSock6);
828 if (retVal != UPNP_E_SUCCESS) {
829 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpSock4);
830#ifdef COMPA_HAVE_CTRLPT_SSDP
831 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpReqSock4);
832 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpReqSock6);
833#endif
834 return retVal;
835 }
836 } else
837 out->ssdpSock6 = INVALID_SOCKET;
838 if (strlen(gIF_IPV6_ULA_GUA) > (size_t)0) {
840 if (retVal != UPNP_E_SUCCESS) {
841 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpSock4);
842 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpSock6);
843#ifdef COMPA_HAVE_CTRLPT_SSDP
844 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpReqSock4);
845 umock::unistd_h.CLOSE_SOCKET_P(out->ssdpReqSock6);
846#endif
847 return retVal;
848 }
849 } else
850 out->ssdpSock6UlaGua = INVALID_SOCKET;
851#endif /* UPNP_ENABLE_IPV6 */
852
853 return UPNP_E_SUCCESS;
854}
#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.
size_t size
Size of the buffer.
Definition uri.hpp:78
compa::uriType type
Member variable.
Definition uri.hpp:95
const char * buff
Buffer.
Definition uri.hpp:77
token pathquery
Member variable.
Definition uri.hpp:98
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.
Class for portable handling of network socket errors.
Definition socket.hpp:537
void catch_error()
Catch error for later use.
Definition socket.cpp:609
std::string error_str() const
Get human readable error description of the catched error.
Definition socket.cpp:618
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.
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.
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.
Socket Module: manage properties and methods but not connections of ONE network socket to handle IPv4...
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]
Static buffer to contain interface IPv6 unique-local or globally-unique address (ULA or GUA)....
Definition upnpapi.cpp:305
ThreadPool gRecvThreadPool
Receive thread pool.
Definition upnpapi.cpp:262
char gIF_IPV4[INET_ADDRSTRLEN]
Static buffer to contain interface IPv4 address. (extern'ed in upnp.h)
Definition upnpapi.cpp:284
char gIF_NAME[LINE_SIZE]
Static buffer to contain interface name. (extern'ed in upnp.h)
Definition upnpapi.cpp:280
unsigned gIF_INDEX
Contains network interface index of the link local address gIF_IPV6 that is used as its scope_id.
Definition upnpapi.cpp:299
char gIF_IPV6[INET6_ADDRSTRLEN]
Static buffer to contain interface IPv6 link-local address (LLA). (extern'ed in upnp....
Definition upnpapi.cpp:292
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.