UPnPsdk 0.1
Universal Plug and Play +, Software Development Kit
 
Loading...
Searching...
No Matches
miniserver.cpp
Go to the documentation of this file.
1/**************************************************************************
2 *
3 * Copyright (c) 2000-2003 Intel Corporation
4 * All rights reserved.
5 * Copyright (C) 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-31
8 * Cloned from pupnp ver 1.14.15.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither name of Intel Corporation nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
26 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
30 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 *
34 **************************************************************************/
35// Last compare with ./pupnp source file on 2023-08-25, ver 1.14.18
48#include <miniserver.hpp> // Needed for one of the compile options
49
50#include <httpreadwrite.hpp>
51#include <ssdp_common.hpp>
52#include <statcodes.hpp>
53#include <upnpapi.hpp>
54
55#include <umock/sys_socket.hpp>
56#include <umock/stdlib.hpp>
57#include <umock/pupnp_miniserver.hpp>
58#include <umock/pupnp_ssdp.hpp>
59
61#include <thread>
63
64namespace {
65
72 SOCKET sock;
74 sockaddr_storage foreign_sockaddr;
75};
76
83
90
93
94#ifdef COMPA_HAVE_WEBSERVER
95
100
106static pthread_mutex_t gActiveConnectionsMutex;
108
111 SOCKET socket;
112 time_t connect_time;
113};
114
118static int active_connection_cmp(void* first, void* second) {
119 TRACE("Executing active_connection_cmp()");
120 struct active_connection_t* conn1 = (struct active_connection_t*)first;
121 struct active_connection_t* conn2 = (struct active_connection_t*)second;
122
123 /* Return non-zero (true) if sockets match */
124 return (conn1->socket == conn2->socket);
125}
126
130static void add_active_connection(SOCKET sock) {
131 TRACE("Executing add_active_connection()");
132 struct active_connection_t* conn;
133
136 pthread_mutex_init(&gActiveConnectionsMutex, NULL);
138 }
139
140 conn =
141 (struct active_connection_t*)malloc(sizeof(struct active_connection_t));
142 if (conn) {
143 conn->socket = sock;
144 conn->connect_time = time(NULL);
145
146 pthread_mutex_lock(&gActiveConnectionsMutex);
148 pthread_mutex_unlock(&gActiveConnectionsMutex);
149
150 UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
151 "Added active connection: socket %d\n", sock);
152 }
153}
154
158static void remove_active_connection(SOCKET sock) {
159 TRACE("Executing remove_active_connection()");
160 struct active_connection_t search_conn;
161 ListNode* node;
162
164 return;
165 }
166 /* Create a temporary connection structure for searching */
167 search_conn.socket = sock;
168 search_conn.connect_time = 0; /* Not used for comparison */
169
170 pthread_mutex_lock(&gActiveConnectionsMutex);
171 node = ListFind(&gActiveConnections, NULL, &search_conn);
172 if (node) {
174 UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
175 "Removed active connection: socket %d\n", sock);
176 }
177 pthread_mutex_unlock(&gActiveConnectionsMutex);
178}
179
184 TRACE("Executing shutdown_all_active_connections()");
185 ListNode* node;
186 int count;
187
189 return;
190 }
191 UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
192 "Shutting down all active socket connections\n");
193
194 pthread_mutex_lock(&gActiveConnectionsMutex);
196 count = 0;
197 while (node) {
198 struct active_connection_t* conn =
199 (struct active_connection_t*)node->item;
200 if (conn && conn->socket != INVALID_SOCKET) {
201 UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
202 "Force shutting down socket %d\n", conn->socket);
203 shutdown(conn->socket, SD_BOTH);
204 count++;
205 }
206 node = ListNext(&gActiveConnections, node);
207 }
208
209 /* Destroy the list and reset state */
212 pthread_mutex_unlock(&gActiveConnectionsMutex);
213 pthread_mutex_destroy(&gActiveConnectionsMutex);
214
215 UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
216 "Shutdown %d active socket connections and clean\n", count);
217}
218
225 char* a_host_port,
226 size_t a_host_port_len
227) {
228 TRACE("Executing host_header_is_numeric()");
229 if (a_host_port_len == 0 || strncmp(a_host_port, "[::]", 4) == 0 ||
230 strncmp(a_host_port, "0.0.0.0", 7) == 0)
231 return 0;
232
233 UPnPsdk::SSockaddr saddrObj;
234 try {
235 saddrObj = std::string(a_host_port, a_host_port_len);
236 } catch (const std::exception& e) {
237 UPnPsdk_LOGCATCH("MSG1049") << e.what() << "\n";
238 return 0;
239 }
240 return 1;
241}
242
252 SOCKET a_socket,
253 char* a_host_port,
254 size_t a_hp_size
255) {
256 TRACE("Executing getNumericHostRedirection()")
257 UPnPsdk::CSocket_basic socketObj(a_socket);
258 try {
259 socketObj.load(); // UPnPsdk::CSocket_basic
261 socketObj.local_saddr(&sa);
262 memcpy(a_host_port, sa.netaddrp().c_str(), a_hp_size);
263 return true;
264
265 } catch (const std::exception& ex) {
266 UPnPsdk_LOGCATCH("MSG1093") "catched next line...\n" << ex.what();
267 }
268 return false;
269}
270
280 SOCKINFO* a_info,
282 http_parser_t* a_hparser) {
283 memptr header;
284 size_t min_size;
285 http_message_t* request;
286 MiniServerCallback callback;
287 WebCallback_HostValidate host_validate_callback = 0;
288 void* cookie{};
289 int rc = UPNP_E_SUCCESS;
290 /* If it does not fit in here, it is likely invalid anyway. */
291 char host_port[NAME_SIZE];
292
293 switch (a_hparser->msg.method) {
294 /* Soap Call */
295 case SOAPMETHOD_POST:
296 case HTTPMETHOD_MPOST:
297 callback = gSoapCallback;
298 UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
299 "miniserver %d: got SOAP msg\n", a_info->socket);
300 break;
301 /* Gena Call */
302 case HTTPMETHOD_NOTIFY:
303 case HTTPMETHOD_SUBSCRIBE:
304 case HTTPMETHOD_UNSUBSCRIBE:
305 callback = gGenaCallback;
306 UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
307 "miniserver %d: got GENA msg\n", a_info->socket);
308 break;
309 /* HTTP server call */
310 case HTTPMETHOD_GET:
311 case HTTPMETHOD_POST:
312 case HTTPMETHOD_HEAD:
313 case HTTPMETHOD_SIMPLEGET:
314 callback = gGetCallback;
315 host_validate_callback = gWebCallback_HostValidate;
317 UPnPsdk_LOGINFO("MSG1107") "miniserver socket="
318 << a_info->socket << ": got WEB server msg.\n";
319 break;
320 default:
321 callback = nullptr;
322 }
323 if (!callback) {
324 rc = HTTP_INTERNAL_SERVER_ERROR;
325 goto ExitFunction;
326 }
327 request = &a_hparser->msg;
328 if (UPnPsdk::g_dbug) {
329 getNumericHostRedirection(a_info->socket, host_port, sizeof host_port);
330 UPnPsdk_LOGINFO("MSG1113") "Redirect host_port=\"" << host_port
331 << "\"\n";
332 }
333 /* check HOST header for an IP number -- prevents DNS rebinding. */
334 if (!httpmsg_find_hdr(request, HDR_HOST, &header)) {
336 goto ExitFunction;
337 }
338 min_size = header.length < ((sizeof host_port) - 1)
339 ? header.length
340 : (sizeof host_port) - 1;
341 memcpy(host_port, header.buf, min_size);
342 host_port[min_size] = 0;
343 if (host_validate_callback) {
344 rc = host_validate_callback(host_port, cookie);
345 if (rc == UPNP_E_BAD_HTTPMSG) {
346 goto ExitFunction;
347 }
348 } else if (!host_header_is_numeric(host_port, min_size)) {
351 UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
352 "Possible DNS Rebind attack prevented.\n");
353 goto ExitFunction;
354 } else {
355 membuffer redir_buf;
356 static const char* redir_fmt = "HTTP/1.1 307 Temporary Redirect\r\n"
357 "Location: http://%s\r\n\r\n";
358 char redir_str[NAME_SIZE];
359 int timeout = HTTP_DEFAULT_TIMEOUT;
360
361 getNumericHostRedirection(a_info->socket, host_port,
362 sizeof host_port);
363 membuffer_init(&redir_buf);
364 snprintf(redir_str, NAME_SIZE, redir_fmt, host_port);
365 membuffer_append_str(&redir_buf, redir_str);
366 rc = http_SendMessage(a_info, &timeout, "b", redir_buf.buf,
367 redir_buf.length);
368 membuffer_destroy(&redir_buf);
369 goto ExitFunction;
370 }
371 }
372 callback(a_hparser, request, a_info);
373
374ExitFunction:
375 return rc;
376}
377
384 void* args) {
385 TRACE("Executing free_handle_request_arg()")
386 if (args == nullptr)
387 return;
388
389 mserv_request_t* request = static_cast<mserv_request_t*>(args);
391 sock_close(request->sock);
392 free(request);
393}
394
401 void* args) {
402 TRACE("Executing handle_request()")
403 int http_major_version{1};
404 int http_minor_version{1};
405 int ret_code;
406 mserv_request_t* request_in = (mserv_request_t*)args;
407 SOCKET sock = request_in->sock;
408
409 if (UPnPsdk::g_dbug) {
410 UPnPsdk::CSocket_basic sockObj(sock);
411 sockObj.load();
412 UPnPsdk::SSockaddr local_saObj;
413 sockObj.local_saddr(&local_saObj);
414 UPnPsdk::SSockaddr remote_saObj;
415 remote_saObj = request_in->foreign_sockaddr;
416 UPnPsdk_LOGINFO("MSG1027") "UDevice socket="
417 << sock << ": READING request on local=\"" << local_saObj.netaddrp()
418 << "\" from control point remote=\"" << remote_saObj.netaddrp()
419 << "\".\n";
420 }
421
422 /* parser_request_init( &parser ); */ /* LEAK_FIX_MK */
423 http_parser_t parser;
424 http_message_t* hmsg = &parser.msg;
425 SOCKINFO info;
426 ret_code = sock_init_with_ip(
427 &info, sock,
428 reinterpret_cast<sockaddr*>(&request_in->foreign_sockaddr));
429 if (ret_code != UPNP_E_SUCCESS) {
430 free(request_in);
431 httpmsg_destroy(hmsg);
432 return;
433 }
434
435 /* read */
436 int timeout{HTTP_DEFAULT_TIMEOUT};
437 int http_error_code; // Either negative UPNP_E_XXX error code,
438 // or positve HTTP error code (4XX or 5XX).
439 // Will be initialized by next function.
440 ret_code = http_RecvMessage(&info, &parser, HTTPMETHOD_UNKNOWN, &timeout,
441 &http_error_code);
442 if (ret_code == 0) {
443 UPnPsdk_LOGINFO("MSG1106") "miniserver socket=" << sock
444 << ": PROCESSING...\n";
445 /* dispatch */
446 http_error_code = dispatch_request(&info, &parser);
447 }
448
449 if (http_error_code > 0) { // only positive HTTP error codes (4XX or 5XX).
450 if (hmsg) {
451 http_major_version = hmsg->major_version;
452 http_minor_version = hmsg->minor_version;
453 }
454 http_SendStatusResponse(&info, http_error_code, http_major_version,
455 http_minor_version);
456 }
457 sock_destroy(&info, SD_BOTH);
458 httpmsg_destroy(hmsg);
459 free(request_in);
460
461 UPnPsdk_LOGINFO("MSG1058") "miniserver socket(" << sock << "); COMPLETE.\n";
462}
463
470 SOCKET a_sock,
472 UPnPsdk::SSockaddr& clientAddr) {
473 TRACE("Executing schedule_request_job()")
474 if (UPnPsdk::g_dbug) {
475 UPnPsdk::CSocket_basic sockObj(a_sock);
476 sockObj.load();
477 UPnPsdk::SSockaddr local_saObj;
478 sockObj.local_saddr(&local_saObj);
479 UPnPsdk_LOGINFO("MSG1042") "Schedule UDevice to accept incomming "
480 "request with socket("
481 << a_sock << ") local=\"" << local_saObj.netaddrp()
482 << "\" remote=\"" << clientAddr.netaddrp() << "\".\n";
483 }
484
485 ThreadPoolJob job{};
486 mserv_request_t* request{
487 static_cast<mserv_request_t*>(std::malloc(sizeof(mserv_request_t)))};
488 if (request == nullptr) {
489 UPnPsdk_LOGCRIT("MSG1024") "Socket(" << a_sock << "): out of memory.\n";
490 sock_close(a_sock);
491 return;
492 }
493
494 request->sock = a_sock;
495 memcpy(&request->foreign_sockaddr, &clientAddr.ss,
496 sizeof(request->foreign_sockaddr));
497 TPJobInit(&job, (UPnPsdk::start_routine)handle_request, request);
499 TPJobSetPriority(&job, MED_PRIORITY);
500
501 /* Add the connection to active connections list */
502 add_active_connection(a_sock);
503
504 if (ThreadPoolAdd(&gMiniServerThreadPool, &job, NULL) != 0) {
505 UPnPsdk_LOGERR("MSG1025") "Socket("
506 << a_sock << "): failed to add job to miniserver threadpool.\n";
508 free(request);
509 sock_close(a_sock);
510 }
511}
512#endif // COMPA_HAVE_WEBSERVER
513
532 SOCKET a_sock,
533 fd_set* a_set
536) {
537 TRACE("Executing fdset_if_valid(): check sockfd=" + std::to_string(a_sock))
538 if (a_sock == INVALID_SOCKET)
539 // This is a defined state and we return silently.
540 return;
541
542 if (a_sock < 3 || a_sock >= FD_SETSIZE) {
543 UPnPsdk_LOGERR("MSG1005")
544 << (a_sock < 0 ? "Invalid" : "Prohibited") << " socket " << a_sock
545 << " not set to be monitored by ::select()"
546 << (a_sock >= 3 ? " because it violates FD_SETSIZE.\n" : ".\n");
547 return;
548 }
549 // Check if socket is valid and bound
550 UPnPsdk::CSocket_basic sockObj(a_sock);
551 try {
552 sockObj.load(); // UPnPsdk::CSocket_basic
553 if (sockObj.local_saddr())
554
555 FD_SET(a_sock, a_set);
556
557 else
558 UPnPsdk_LOGINFO("MSG1002") "Unbound socket "
559 << a_sock << " not set to be monitored by ::select().\n";
560
561 } catch (const std::exception& e) {
562 if (UPnPsdk::g_dbug)
563 std::cerr << e.what();
564 UPnPsdk_LOGCATCH("MSG1009") "Invalid socket "
565 << a_sock << " not set to be monitored by ::select().\n";
566 }
567}
568
582 [[maybe_unused]] SOCKET listen_sock,
584 [[maybe_unused]] fd_set& set) {
585#ifndef COMPA_HAVE_WEBSERVER
587#else
588 TRACE("Executing web_server_accept()")
589 if (listen_sock == INVALID_SOCKET || !FD_ISSET(listen_sock, &set)) {
590 UPnPsdk_LOGINFO("MSG1012") "Socket("
591 << listen_sock << ") invalid or not in file descriptor set.\n";
592 return UPNP_E_SOCKET_ERROR;
593 }
594
595 UPnPsdk::SSockaddr ctrlpnt_saObj;
596 socklen_t ctrlpntLen = sizeof(ctrlpnt_saObj.ss);
597
598 // accept a network request connection
599 SOCKET conn_sock =
600 umock::sys_socket_h.accept(listen_sock, &ctrlpnt_saObj.sa, &ctrlpntLen);
601 if (conn_sock == INVALID_SOCKET) {
602 UPnPsdk_LOGERR("MSG1022") "Error in ::accept(): "
603 << std::strerror(errno) << ".\n";
605 }
606
607 if (UPnPsdk::g_dbug) {
608 // Some helpful status information.
609 UPnPsdk::CSocket_basic listen_sockObj(listen_sock);
610 listen_sockObj.load();
611 UPnPsdk::SSockaddr listen_saObj;
612 listen_sockObj.local_saddr(&listen_saObj);
613
614 UPnPsdk::CSocket_basic conn_sockObj(conn_sock);
615 conn_sockObj.load();
616 UPnPsdk::SSockaddr conn_saObj;
617 conn_sockObj.local_saddr(&conn_saObj);
618
619 UPnPsdk_LOGINFO("MSG1023") "Listening socket("
620 << listen_sock << ") on \"" << listen_saObj.netaddrp()
621 << "\" accept connection socket(" << conn_sock << ") local=\""
622 << conn_saObj.netaddrp() << "\" to remote=\""
623 << ctrlpnt_saObj.netaddrp() << "\".\n";
624 }
625
626 // Schedule a job to manage the UPnP request from the remote control point.
627 schedule_request_job(conn_sock, ctrlpnt_saObj);
628
629 return UPNP_E_SUCCESS;
630#endif /* COMPA_HAVE_WEBSERVER */
631}
632
636void ssdp_read( //
637 SOCKET* rsock,
638 fd_set* set ) {
640 TRACE("Executing ssdp_read()")
641 if (*rsock == INVALID_SOCKET || !FD_ISSET(*rsock, set))
642 return;
643
644#if defined(COMPA_HAVE_CTRLPT_SSDP) || defined(COMPA_HAVE_DEVICE_SSDP)
645 if (readFromSSDPSocket(*rsock) != 0) {
646 UpnpPrintf(UPNP_ERROR, MSERV, __FILE__, __LINE__,
647 "miniserver: Error in readFromSSDPSocket(%d): "
648 "closing socket\n",
649 *rsock);
650 sock_close(*rsock);
651 *rsock = INVALID_SOCKET;
652 }
653#else
654 sock_close(*rsock);
655 *rsock = INVALID_SOCKET;
656#endif
657}
658
673 SOCKET ssock,
674 fd_set* set
676) {
677 TRACE("Executing receive_from_stopSock()")
678 constexpr char shutdown_str[]{"ShutDown"};
679
680 if (!FD_ISSET(ssock, set))
681 return 0; // Nothing to do for this socket
682
683 UPnPsdk::sockaddr_t clientAddr{};
684 socklen_t clientLen{sizeof(clientAddr)}; // May be modified
685
686 // The receive buffer is one byte greater with '\0' than the max receiving
687 // bytes so the received message will always be terminated.
688 char receiveBuf[sizeof(shutdown_str) + 1]{};
689 char buf_ntop[INET6_ADDRSTRLEN];
690
691 // receive from
692 SSIZEP_T byteReceived = umock::sys_socket_h.recvfrom(
693 ssock, receiveBuf, sizeof(shutdown_str), 0, &clientAddr.sa, &clientLen);
694 if (byteReceived == SOCKET_ERROR ||
695 inet_ntop(AF_INET, &clientAddr.sin.sin_addr, buf_ntop,
696 sizeof(buf_ntop)) == nullptr) {
697 UPnPsdk_LOGCRIT("MSG1038") "Failed to receive data from socket "
698 << ssock << ". Stop miniserver.\n";
699 return 1;
700 }
701
702 // integer of "127.0.0.1" is 2130706433.
703 if (clientAddr.sin.sin_addr.s_addr != htonl(2130706433) ||
704 strcmp(receiveBuf, shutdown_str) != 0) //
705 {
706 char nullstr[]{"\\0"};
707 if (byteReceived == 0 || receiveBuf[byteReceived - 1] != '\0')
708 nullstr[0] = '\0';
709 UPnPsdk_LOGERR("MSG1039") "Received \""
710 << receiveBuf << nullstr << "\" from " << buf_ntop << ":"
711 << ntohs(clientAddr.sin.sin_port)
712 << ", must be \"ShutDown\\0\" from 127.0.0.1:*. Don't "
713 "stopping miniserver.\n";
714 return 0;
715 }
716
717 UPnPsdk_LOGINFO("MSG1040") "On socket "
718 << ssock << " received ordinary datagram \"" << receiveBuf
719 << "\\0\" from " << buf_ntop << ":" << ntohs(clientAddr.sin.sin_port)
720 << ". Stop miniserver.\n";
721 return 1;
722}
723
739 MiniServerSockArray* miniSock) {
740 UPnPsdk_LOGINFO("MSG1085") "Executing...\n";
741
742#ifdef COMPA_HAVE_WEBSERVER
743 // Move the current valid socket objects to this scope. They are owner of
744 // the raw socket file descriptor and manage/close it when leaving this
745 // scope.
746 UPnPsdk::CSocket sockLlaObj;
747 if (miniSock->pSockLlaObj != nullptr) {
748 sockLlaObj = std::move(*miniSock->pSockLlaObj);
749 miniSock->pSockLlaObj = &sockLlaObj;
750 }
751 UPnPsdk::CSocket sockGuaObj;
752 if (miniSock->pSockGuaObj != nullptr) {
753 sockGuaObj = std::move(*miniSock->pSockGuaObj);
754 miniSock->pSockGuaObj = &sockGuaObj;
755 }
756 UPnPsdk::CSocket sockIp4Obj;
757 if (miniSock->pSockIp4Obj != nullptr) {
758 sockIp4Obj = std::move(*miniSock->pSockIp4Obj);
759 miniSock->pSockIp4Obj = &sockIp4Obj;
760 }
761#endif
762
763 fd_set expSet;
764 fd_set rdSet;
765 int stopSock = 0;
766
767 // On MS Windows INVALID_SOCKET is unsigned -1 = 18446744073709551615 so we
768 // get maxMiniSock with this big number even if there is only one
769 // INVALID_SOCKET. Incrementing it at the end results in 0. To be portable
770 // we must not assume INVALID_SOCKET to be -1. --Ingo
771 SOCKET maxMiniSock = 0;
772 maxMiniSock = //
773 std::max(maxMiniSock, miniSock->miniServerSock4 == INVALID_SOCKET
774 ? 0
775 : miniSock->miniServerSock4);
776 maxMiniSock = //
777 std::max(maxMiniSock, miniSock->miniServerSock6 == INVALID_SOCKET
778 ? 0
779 : miniSock->miniServerSock6);
780 maxMiniSock = //
781 std::max(maxMiniSock, miniSock->miniServerSock6UlaGua == INVALID_SOCKET
782 ? 0
783 : miniSock->miniServerSock6UlaGua);
784 maxMiniSock = //
785 std::max(maxMiniSock, miniSock->miniServerStopSock == INVALID_SOCKET
786 ? 0
787 : miniSock->miniServerStopSock);
788 maxMiniSock = //
789 std::max(maxMiniSock, miniSock->ssdpSock4 == INVALID_SOCKET
790 ? 0
791 : miniSock->ssdpSock4);
792 maxMiniSock = //
793 std::max(maxMiniSock, miniSock->ssdpSock6 == INVALID_SOCKET
794 ? 0
795 : miniSock->ssdpSock6);
796 maxMiniSock = //
797 std::max(maxMiniSock, miniSock->ssdpSock6UlaGua == INVALID_SOCKET
798 ? 0
799 : miniSock->ssdpSock6UlaGua);
800#ifdef COMPA_HAVE_CTRLPT_SSDP
801 maxMiniSock = //
802 std::max(maxMiniSock, miniSock->ssdpReqSock4 == INVALID_SOCKET
803 ? 0
804 : miniSock->ssdpReqSock4);
805 maxMiniSock = //
806 std::max(maxMiniSock, miniSock->ssdpReqSock6 == INVALID_SOCKET
807 ? 0
808 : miniSock->ssdpReqSock6);
809#endif
810 ++maxMiniSock;
811
813 while (!stopSock) {
814 FD_ZERO(&rdSet);
815 FD_ZERO(&expSet);
816 /* FD_SET()'s */
817 FD_SET(miniSock->miniServerStopSock, &expSet);
818 FD_SET(miniSock->miniServerStopSock, &rdSet);
819 fdset_if_valid(miniSock->miniServerSock4, &rdSet);
820 fdset_if_valid(miniSock->miniServerSock6, &rdSet);
821 fdset_if_valid(miniSock->miniServerSock6UlaGua, &rdSet);
822 fdset_if_valid(miniSock->ssdpSock4, &rdSet);
823 fdset_if_valid(miniSock->ssdpSock6, &rdSet);
824 fdset_if_valid(miniSock->ssdpSock6UlaGua, &rdSet);
825#ifdef COMPA_HAVE_CTRLPT_SSDP
826 fdset_if_valid(miniSock->ssdpReqSock4, &rdSet);
827 fdset_if_valid(miniSock->ssdpReqSock6, &rdSet);
828#endif
829
830 /* select() */
831 int ret = umock::sys_socket_h.select(static_cast<int>(maxMiniSock),
832 &rdSet, NULL, &expSet, NULL);
833
834 if (ret == SOCKET_ERROR) {
835 if (errno == EINTR) {
836 // A signal was caught, not for us. We ignore it and
837 continue;
838 }
839 if (errno == EBADF) {
840 // A closed socket file descriptor was given in one of the
841 // sets. For details look at
842 // REF:_[Should_I_assert_fail_on_select()_EBADF?](https://stackoverflow.com/q/28015859/5014688)
843 // It is difficult to determine here what file descriptor
844 // in rdSet or expSet is invalid. So I ensure that only valid
845 // socket fds are given by checking them with fdset_if_valid()
846 // before calling select(). Doing this I have to
847 continue;
848 }
849 // All other errors EINVAL and ENOMEM are critical and cannot
850 // continue run mininserver.
851 UPnPsdk_LOGCRIT("MSG1021") "Error in ::select(): "
852 << std::strerror(errno) << ".\n";
853 break;
854 }
855
856 // Accept requested connection from a remote control point and run the
857 // connection in a new thread. Due to side effects with threading we
858 // need to avoid lazy evaluation with chained || because all
859 // web_server_accept() must be called.
860 // if (ret1 == UPNP_E_SUCCESS || ret2 == UPNP_E_SUCCESS ||
861 // ret3 == UPNP_E_SUCCESS) {
862 [[maybe_unused]] int ret1 =
863 web_server_accept(miniSock->miniServerSock4, rdSet);
864 [[maybe_unused]] int ret2 =
865 web_server_accept(miniSock->miniServerSock6, rdSet);
866 [[maybe_unused]] int ret3 =
868#ifdef COMPA_HAVE_CTRLPT_SSDP
869 ssdp_read(&miniSock->ssdpReqSock4, &rdSet);
870 ssdp_read(&miniSock->ssdpReqSock6, &rdSet);
871#endif
872 ssdp_read(&miniSock->ssdpSock4, &rdSet);
873 ssdp_read(&miniSock->ssdpSock6, &rdSet);
874 ssdp_read(&miniSock->ssdpSock6UlaGua, &rdSet);
875 // }
876
877 // Check if we have received a packet from
878 // localhost(127.0.0.1) that will stop the miniserver.
879 stopSock = receive_from_stopSock(miniSock->miniServerStopSock, &rdSet);
880 } // while (!stopsock)
881
882#ifdef COMPA_HAVE_WEBSERVER
883 /* shutdown connections */
885#endif
886
887 /* Close all sockets. */
888 sock_close(miniSock->miniServerSock4);
889 sock_close(miniSock->miniServerSock6);
892 sock_close(miniSock->ssdpSock4);
893 sock_close(miniSock->ssdpSock6);
894 sock_close(miniSock->ssdpSock6UlaGua);
895#ifdef COMPA_HAVE_CTRLPT_SSDP
896 sock_close(miniSock->ssdpReqSock4);
897 sock_close(miniSock->ssdpReqSock6);
898#endif
899 /* Free minisock. */
900 umock::stdlib_h.free(miniSock);
902
903 UPnPsdk_LOGINFO("MSG1060") "Finished.\n";
904 return;
905}
906
908 umock::pupnp_miniserver.RunMiniServer(miniSock);
909}
910
911
921 SOCKET sockfd,
923 uint16_t* port) {
924 TRACE("Executing get_port(), calls system ::getsockname()")
925 UPnPsdk::sockaddr_t sockinfo{};
926 socklen_t len(sizeof sockinfo); // May be modified by getsockname()
927
928 if (umock::sys_socket_h.getsockname(sockfd, &sockinfo.sa, &len) == -1)
929 // system error (errno etc.) is expected to be unmodified on return.
930 return -1;
931
932 switch (sockinfo.ss.ss_family) {
933 case AF_INET:
934 case AF_INET6:
935 *port = ntohs(sockinfo.sin6.sin6_port);
936 break;
937 default:
938 // system error (errno etc.) is expected to be unmodified on return.
939 return -1;
940 }
941 UPnPsdk_LOGINFO("MSG1063") "sockfd=" << sockfd << ", port=" << *port
942 << ".\n";
943
944 return 0;
945}
946
947#ifdef COMPA_HAVE_WEBSERVER
975 in_port_t listen_port4,
979 in_port_t listen_port6,
983 in_port_t listen_port6UlaGua) {
984 UPnPsdk_LOGINFO("MSG1109") "Executing with listen_port4="
985 << listen_port4 << ", listen_port6=" << listen_port6
986 << ", listen_port6UlaGua=" << listen_port6UlaGua << ".\n";
987
988 int retval{UPNP_E_OUTOF_SOCKET};
989
990 if (out->pSockLlaObj != nullptr && gIF_IPV6[0] != '\0') {
991 try {
992 UPnPsdk::SSockaddr saObj;
993 if (::strncmp(gIF_IPV6, "::1", 3) == 0)
994 // The loopback address belongs to a lla but 'bind()' on win32
995 // does not accept it with scope id.
996 saObj = '[' + std::string(gIF_IPV6) +
997 "]:" + std::to_string(listen_port6);
998 else
999 saObj = '[' + std::string(gIF_IPV6) + '%' +
1000 std::to_string(gIF_INDEX) +
1001 "]:" + std::to_string(listen_port6);
1002 out->pSockLlaObj->bind(SOCK_STREAM, &saObj, AI_PASSIVE);
1003 out->pSockLlaObj->listen();
1004 out->miniServerSock6 = *out->pSockLlaObj;
1005 out->pSockLlaObj->local_saddr(&saObj);
1006 out->miniServerPort6 = saObj.port();
1007 retval = UPNP_E_SUCCESS;
1008 } catch (const std::exception& ex) {
1009 UPnPsdk_LOGCATCH("MSG1110") "gIF_IPV6=\""
1010 << gIF_IPV6 << "\", catched next line...\n"
1011 << ex.what();
1012 }
1013 }
1014
1015 // ss6.serverAddr6->sin6_scope_id = gIF_INDEX;
1016 // I could not find where sin6_scope_id is read elsewhere to use it.
1017 // It is never copied to 'out'. --Ingo
1018
1019 if (out->pSockGuaObj != nullptr && gIF_IPV6_ULA_GUA[0] != '\0') {
1020 try {
1021 UPnPsdk::SSockaddr saObj;
1022 saObj = '[' + std::string(gIF_IPV6_ULA_GUA) +
1023 "]:" + std::to_string(listen_port6UlaGua);
1024 out->pSockGuaObj->bind(SOCK_STREAM, &saObj, AI_PASSIVE);
1025 out->pSockGuaObj->listen();
1026 out->miniServerSock6UlaGua = *out->pSockGuaObj;
1027 out->pSockGuaObj->local_saddr(&saObj);
1028 out->miniServerPort6UlaGua = saObj.port();
1029 retval = UPNP_E_SUCCESS;
1030 } catch (const std::exception& ex) {
1031 UPnPsdk_LOGCATCH("MSG1117") "gIF_IPV6_ULA_GUA=\""
1032 << gIF_IPV6_ULA_GUA << "\", catched next line...\n"
1033 << ex.what();
1034 }
1035 }
1036
1037 if (out->pSockIp4Obj != nullptr && gIF_IPV4[0] != '\0') {
1038 try {
1039 UPnPsdk::SSockaddr saObj;
1040 saObj = std::string(gIF_IPV4) + ':' + std::to_string(listen_port4);
1041 out->pSockIp4Obj->bind(SOCK_STREAM, &saObj, AI_PASSIVE);
1042 out->pSockIp4Obj->listen();
1043 out->miniServerSock4 = *out->pSockIp4Obj;
1044 out->pSockIp4Obj->local_saddr(&saObj);
1045 out->miniServerPort4 = saObj.port();
1046 retval = UPNP_E_SUCCESS;
1047 } catch (const std::exception& ex) {
1048 UPnPsdk_LOGCATCH("MSG1114") "gIF_IPV4=\""
1049 << gIF_IPV4 << "\", catched next line...\n"
1050 << ex.what();
1051 }
1052 }
1053
1054 if (retval != UPNP_E_SUCCESS)
1055 UPnPsdk_LOGERR("MSG1065") "No valid IP address on a local network "
1056 "adapter found for listening.\n";
1057 return retval;
1058}
1059#endif /* COMPA_HAVE_WEBSERVER */
1060
1079 MiniServerSockArray* out) {
1080 TRACE("Executing get_miniserver_stopsock()");
1081 sockaddr_in stop_sockaddr;
1082
1083 UPnPsdk::CSocketErr sockerrObj;
1084 SOCKET miniServerStopSock =
1085 umock::sys_socket_h.socket(AF_INET, SOCK_DGRAM, 0);
1086 if (miniServerStopSock == INVALID_SOCKET) {
1087 sockerrObj.catch_error();
1088 UPnPsdk_LOGCRIT("MSG1094") "Error in socket(): "
1089 << sockerrObj.error_str() << "\n";
1090 return UPNP_E_OUTOF_SOCKET;
1091 }
1092 /* Bind to local socket. */
1093 memset(&stop_sockaddr, 0, sizeof(stop_sockaddr));
1094 stop_sockaddr.sin_family = AF_INET;
1095 inet_pton(AF_INET, "127.0.0.1", &stop_sockaddr.sin_addr);
1096 int ret = umock::sys_socket_h.bind(
1097 miniServerStopSock, reinterpret_cast<sockaddr*>(&stop_sockaddr),
1098 sizeof(stop_sockaddr));
1099 if (ret == SOCKET_ERROR) {
1100 sockerrObj.catch_error();
1101 UPnPsdk_LOGCRIT("MSG1095") "Error in binding localhost: "
1102 << sockerrObj.error_str() << "\n";
1103 sock_close(miniServerStopSock);
1104 return UPNP_E_SOCKET_BIND;
1105 }
1106 ret = get_port(miniServerStopSock, &miniStopSockPort);
1107 if (ret < 0) {
1108 sock_close(miniServerStopSock);
1109 return UPNP_E_INTERNAL_ERROR;
1110 }
1111 out->miniServerStopSock = miniServerStopSock;
1113
1114 UPnPsdk_LOGINFO("MSG1053") "Bound stop socket="
1115 << miniServerStopSock << " to \"127.0.0.1:" << miniStopSockPort
1116 << "\".\n";
1117
1118 return UPNP_E_SUCCESS;
1119}
1120
1125 MiniServerSockArray* miniSocket
1126) {
1127 TRACE("Executing InitMiniServerSockArray()");
1128 miniSocket->miniServerSock4 = INVALID_SOCKET;
1129 miniSocket->miniServerSock6 = INVALID_SOCKET;
1130 miniSocket->miniServerSock6UlaGua = INVALID_SOCKET;
1131 miniSocket->miniServerStopSock = INVALID_SOCKET;
1132 miniSocket->ssdpSock4 = INVALID_SOCKET;
1133 miniSocket->ssdpSock6 = INVALID_SOCKET;
1134 miniSocket->ssdpSock6UlaGua = INVALID_SOCKET;
1135 miniSocket->stopPort = 0u;
1136 miniSocket->miniServerPort4 = 0u;
1137 miniSocket->miniServerPort6 = 0u;
1138 miniSocket->miniServerPort6UlaGua = 0u;
1139#ifdef COMPA_HAVE_CTRLPT_SSDP
1140 miniSocket->ssdpReqSock4 = INVALID_SOCKET;
1141 miniSocket->ssdpReqSock6 = INVALID_SOCKET;
1142#endif
1143#ifdef COMPA_HAVE_WEBSERVER
1144 miniSocket->pSockLlaObj = nullptr;
1145 miniSocket->pSockGuaObj = nullptr;
1146 miniSocket->pSockIp4Obj = nullptr;
1147#endif
1148}
1149
1151} // anonymous namespace
1152
1153
1154#ifdef COMPA_HAVE_DEVICE_SOAP
1156 TRACE("Executing SetSoapCallback()");
1157 gSoapCallback = callback;
1158}
1159#endif
1160
1161#ifdef COMPA_HAVE_DEVICE_GENA
1163 TRACE("Executing SetGenaCallback()");
1164 gGenaCallback = callback;
1165}
1166#endif
1167
1168int StartMiniServer([[maybe_unused]] in_port_t* listen_port4,
1169 [[maybe_unused]] in_port_t* listen_port6,
1170 [[maybe_unused]] in_port_t* listen_port6UlaGua) {
1171 UPnPsdk_LOGINFO("MSG1068") "Executing...\n";
1172 constexpr int max_count{10000};
1173 MiniServerSockArray* miniSocket;
1174 ThreadPoolJob job;
1175 int ret_code{UPNP_E_INTERNAL_ERROR};
1176
1177 memset(&job, 0, sizeof(job));
1178
1179 if (gMServState != MSERV_IDLE) {
1180 /* miniserver running. */
1181 UPnPsdk_LOGERR("MSG1087") "Cannot start. Miniserver is running.\n";
1182 return UPNP_E_INTERNAL_ERROR;
1183 }
1184
1185 miniSocket = (MiniServerSockArray*)malloc(sizeof(MiniServerSockArray));
1186 if (!miniSocket) {
1187 return UPNP_E_OUTOF_MEMORY;
1188 }
1189 InitMiniServerSockArray(miniSocket);
1190
1191#ifdef COMPA_HAVE_WEBSERVER
1192 // These socket objects must be valid until the miniserver has successful
1193 // started. They will be moved to the running miniserver thread.
1194 UPnPsdk::CSocket sockLlaObj;
1195 miniSocket->pSockLlaObj = &sockLlaObj;
1196 UPnPsdk::CSocket sockGuaObj;
1197 miniSocket->pSockGuaObj = &sockGuaObj;
1198 UPnPsdk::CSocket sockIp4Obj;
1199 miniSocket->pSockIp4Obj = &sockIp4Obj;
1200
1201 /* V4 and V6 http listeners. */
1202 ret_code = get_miniserver_sockets(miniSocket, *listen_port4, *listen_port6,
1203 *listen_port6UlaGua);
1204 if (ret_code != UPNP_E_SUCCESS) {
1205 free(miniSocket);
1206 return ret_code;
1207 }
1208#endif
1209
1210 /* Stop socket (To end miniserver processing). */
1211 ret_code = get_miniserver_stopsock(miniSocket);
1212 if (ret_code != UPNP_E_SUCCESS) {
1213 sock_close(miniSocket->miniServerSock4);
1214 sock_close(miniSocket->miniServerSock6);
1215 sock_close(miniSocket->miniServerSock6UlaGua);
1216 free(miniSocket);
1217 return ret_code;
1218 }
1219#if defined(COMPA_HAVE_CTRLPT_SSDP) || defined(COMPA_HAVE_DEVICE_SSDP)
1220 /* SSDP socket for discovery/advertising. */
1221 ret_code = umock::pupnp_ssdp.get_ssdp_sockets(miniSocket);
1222 if (ret_code != UPNP_E_SUCCESS) {
1223 sock_close(miniSocket->miniServerSock4);
1224 sock_close(miniSocket->miniServerSock6);
1225 sock_close(miniSocket->miniServerSock6UlaGua);
1226 sock_close(miniSocket->miniServerStopSock);
1227 free(miniSocket);
1228 return ret_code;
1229 }
1230#endif
1231 // Run miniserver in a new thread.
1232 TPJobInit(&job, (UPnPsdk::start_routine)RunMiniServer_f, (void*)miniSocket);
1233 TPJobSetPriority(&job, MED_PRIORITY);
1235 ret_code = ThreadPoolAddPersistent(&gMiniServerThreadPool, &job, NULL);
1236 if (ret_code != 0) {
1237 sock_close(miniSocket->miniServerSock4);
1238 sock_close(miniSocket->miniServerSock6);
1239 sock_close(miniSocket->miniServerSock6UlaGua);
1240 sock_close(miniSocket->miniServerStopSock);
1241 sock_close(miniSocket->ssdpSock4);
1242 sock_close(miniSocket->ssdpSock6);
1243 sock_close(miniSocket->ssdpSock6UlaGua);
1244#ifdef COMPA_HAVE_CTRLPT_SSDP
1245 sock_close(miniSocket->ssdpReqSock4);
1246 sock_close(miniSocket->ssdpReqSock6);
1247#endif
1248 free(miniSocket);
1249 return UPNP_E_OUTOF_MEMORY;
1250 }
1251 /* Wait for miniserver to start. */
1252 int count{0};
1253 while (gMServState != (MiniServerState)MSERV_RUNNING && count < max_count) {
1254 /* 0.05s */
1255 std::this_thread::sleep_for(std::chrono::milliseconds(50));
1256 count++;
1257 }
1258 if (count >= max_count) {
1259 /* Took it too long to start that thread. */
1260 sock_close(miniSocket->miniServerSock4);
1261 sock_close(miniSocket->miniServerSock6);
1262 sock_close(miniSocket->miniServerSock6UlaGua);
1263 sock_close(miniSocket->miniServerStopSock);
1264 sock_close(miniSocket->ssdpSock4);
1265 sock_close(miniSocket->ssdpSock6);
1266 sock_close(miniSocket->ssdpSock6UlaGua);
1267#ifdef COMPA_HAVE_CTRLPT_SSDP
1268 sock_close(miniSocket->ssdpReqSock4);
1269 sock_close(miniSocket->ssdpReqSock6);
1270#endif
1271 return UPNP_E_INTERNAL_ERROR;
1272 }
1273#ifdef COMPA_HAVE_WEBSERVER
1274 *listen_port4 = miniSocket->miniServerPort4;
1275 *listen_port6 = miniSocket->miniServerPort6;
1276 *listen_port6UlaGua = miniSocket->miniServerPort6UlaGua;
1277#endif
1278
1279 return UPNP_E_SUCCESS;
1280}
1281
1283 UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
1284 "Inside StopMiniServer()\n");
1285
1286 socklen_t socklen = sizeof(struct sockaddr_in);
1287 SOCKET sock;
1288 sockaddr_in ssdpAddr;
1289 char buf[256] = "ShutDown";
1290 // due to required type cast for 'sendto' on WIN32 bufLen must fit to an int
1291 size_t bufLen = strlen(buf);
1292
1293 switch (gMServState) {
1294 case MSERV_RUNNING:
1296 break;
1297 default:
1298 return 0;
1299 }
1300 sock = umock::sys_socket_h.socket(AF_INET, SOCK_DGRAM, 0);
1301 if (sock == INVALID_SOCKET) {
1302 UpnpPrintf(UPNP_ERROR, SSDP, __FILE__, __LINE__,
1303 "SSDP_SERVER: StopSSDPServer: Error in socket() %s\n",
1304 std::strerror(errno));
1305 return 1;
1306 }
1308 ssdpAddr.sin_family = (sa_family_t)AF_INET;
1309 inet_pton(AF_INET, "127.0.0.1", &ssdpAddr.sin_addr);
1310 ssdpAddr.sin_port = htons(miniStopSockPort);
1311 umock::sys_socket_h.sendto(sock, buf, (SIZEP_T)bufLen, 0,
1312 reinterpret_cast<sockaddr*>(&ssdpAddr),
1313 socklen);
1314 std::this_thread::sleep_for(std::chrono::milliseconds(1));
1316 break;
1317 }
1318 std::this_thread::sleep_for(std::chrono::seconds(1));
1319 }
1320
1321 sock_close(sock);
1322 return 0;
1323}
#define UPNP_E_NO_WEB_SERVER
The operation completed successfully.
Definition API.hpp:354
#define NAME_SIZE
Definition API.hpp:46
http_message_t msg
entire raw message
http_method_t method
Http method of an outgoing request.
int major_version
Http major version.
#define HDR_HOST
Type of a HTTP header.
int minor_version
Http minor version.
Structure of an HTTP parser object.
Structure of an HTTP message.
int ListDestroy(LinkedList *list, int freeItem)
Removes all memory associated with list nodes. Does not free LinkedList *list.
void * ListDelNode(LinkedList *list, ListNode *dnode, int freeItem)
Removes a node from the list. The memory for the node is freed.
ListNode * ListNext(LinkedList *list, ListNode *node)
Returns the next item in the list.
ListNode * ListHead(LinkedList *list)
Returns the head of the list.
ListNode * ListAddTail(LinkedList *list, void *item)
Adds a node to the tail of the list. Node gets added immediately before list.tail.
ListNode * ListFind(LinkedList *list, ListNode *start, void *item)
Finds the specified item in the list.
int ListInit(LinkedList *list, cmp_routine cmp_func, free_function free_func)
Initializes LinkedList. Must be called first and only once for List.
Linked list node. Stores generic item and pointers to next and prev.
Linked list (no protection).
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.
int ThreadPoolAddPersistent(ThreadPool *tp, ThreadPoolJob *job, int *jobId)
Adds a persistent job to the thread pool.
void(* free_routine)(void *arg)
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:618
std::string error_str() const
Get human readable error description of the catched error.
Definition socket.cpp:627
Get information from a raw network socket file descriptor.
Definition socket.hpp:129
bool local_saddr(SSockaddr *a_saddr=nullptr) const
Get the local socket address the socket is bound to.
Definition socket.cpp:224
void load()
Load the raw socket file descriptor specified with the constructor into the object.
Definition socket.cpp:189
Manage all aspects of a network socket.
Definition socket.hpp:320
void listen()
Set socket to listen.
Definition socket.cpp:561
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
int readFromSSDPSocket(SOCKET socket)
This function reads the data from the ssdp socket.
int(* WebCallback_HostValidate)(const char *hostname, void *cookie)
Callback for validating HTTP requests HOST header values.
Definition API.hpp:2782
Helpful union of the different socket address structures.
Definition sockaddr.hpp:34
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 httpmsg_destroy(http_message_t *msg)
Free memory allocated for the http message.
int http_SendMessage(SOCKINFO *info, int *TimeOut, const char *fmt,...)
Sends a message to the destination based on the format parameter.
int http_SendStatusResponse(SOCKINFO *info, int http_status_code, int request_major_version, int request_minor_version)
Generate a response message for the status query and send the status response.
int http_RecvMessage(SOCKINFO *info, http_parser_t *parser, http_method_t request_method, int *timeout_secs, int *http_error_code)
Get the data on the socket and take actions based on the read data to modify the parser objects buffe...
Performs HTTP read and write messages.
#define HTTP_DEFAULT_TIMEOUT
void membuffer_destroy(membuffer *m)
Free's memory allocated for membuffer* m.
void membuffer_init(membuffer *m)
Wrapper to membuffer_initialize().
int membuffer_append_str(membuffer *m, const char *c_str)
Invokes function to appends data from a constant string to the buffer.
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
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_SOCKET_BIND
The SDK had a problem binding a socket to a network interface.
Definition messages.hpp:199
#define UPNP_E_BAD_HTTPMSG
The HTTP message contains invalid message headers.
Definition messages.hpp:146
#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
#define UPNP_E_OUTOF_MEMORY
Not enough resources are currently available to complete the operation.
Definition messages.hpp:57
#define UPNP_E_SOCKET_ACCEPT
The SDK had a problem accepting a network connection.
Definition messages.hpp:262
#define UPNP_E_INTERNAL_ERROR
Generic error code for internal conditions not covered by other error codes.
Definition messages.hpp:319
void SetSoapCallback(MiniServerCallback callback)
Set SOAP Callback.
int StartMiniServer(in_port_t *listen_port4, in_port_t *listen_port6, in_port_t *listen_port6UlaGua)
Initialize the sockets functionality for the Miniserver.
int StopMiniServer()
Stop and Shutdown the MiniServer and free socket resources.
void SetGenaCallback(MiniServerCallback callback)
Set GENA Callback.
Manage "Step 0: Addressing" of the UPnP+™ specification.
in_port_t miniServerPort6
Corresponding port to miniServerSock6.
in_port_t miniServerPort6UlaGua
Corresponding port to miniServerSock6UlaGua.
SOCKET ssdpSock4
IPv4 SSDP datagram Socket for incoming advertisments and search requests.
void(* MiniServerCallback)(http_parser_t *parser, http_message_t *request, SOCKINFO *info)
For a miniserver callback function.
SOCKET miniServerSock6UlaGua
IPv6 ULA or GUA Socket for listening for miniserver requests.
in_port_t stopPort
Corresponding port to miniServerStopSock. This is set with miniStopSockPort but never used.
SOCKET miniServerSock4
IPv4 socket for listening for miniserver requests.
in_port_t miniServerPort4
Corresponding port to miniServerSock4.
MiniServerCallback gGetCallback
HTTP server callback.
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 miniServerStopSock
Datagram Socket for stopping miniserver.
SOCKET ssdpSock6
IPv6 LLA SSDP Socket for incoming advertisments and search requests.
SOCKET miniServerSock6
IPv6 LLA Socket for listening for miniserver requests.
SOCKET ssdpSock6UlaGua
IPv6 ULA or GUA SSDP Socket for incoming advertisments and search requests.
Provides sockets for all network communications.
UPnPsdk_EXTERN bool g_dbug
Switch to enable verbose (debug) output.
Definition global.hpp:26
void ssdp_read(SOCKET *rsock, fd_set *set)
Read data from the SSDP socket.
int host_header_is_numeric(char *a_host_port, size_t a_host_port_len)
Check if a network address is numeric.
void schedule_request_job(SOCKET a_sock, UPnPsdk::SSockaddr &clientAddr)
Initilize the thread pool to handle an incomming request, sets priority for the job and adds the job ...
static void remove_active_connection(SOCKET sock)
Remove a socket from the active connections list.
int get_miniserver_stopsock(MiniServerSockArray *out)
Creates the miniserver STOP socket, usable to listen for stopping the miniserver.
static pthread_mutex_t gActiveConnectionsMutex
void shutdown_all_active_connections(void)
Shutdown all active socket connections.
void RunMiniServer(MiniServerSockArray *miniSock)
Run the miniserver.
MiniServerCallback gSoapCallback
SOAP callback.
int web_server_accept(SOCKET listen_sock, fd_set &set)
Accept requested connection from a remote control point and run it in a new thread.
void InitMiniServerSockArray(MiniServerSockArray *miniSocket)
Initialize a miniserver Socket Array.
void free_handle_request_arg(void *args)
Free memory assigned for handling request and unitialize socket functionality.
in_port_t miniStopSockPort
Port of the stop socket.
int getNumericHostRedirection(SOCKET a_socket, char *a_host_port, size_t a_hp_size)
Returns the ip address with port as text that is bound to a socket.
MiniServerCallback gGenaCallback
GENA callback.
int receive_from_stopSock(SOCKET ssock, fd_set *set)
Check if we have received a packet that shall stop the miniserver.
int get_miniserver_sockets(MiniServerSockArray *out, in_port_t listen_port4, in_port_t listen_port6, in_port_t listen_port6UlaGua)
Create STREAM sockets, binds to local network interfaces and listens for incoming connections.
void RunMiniServer_f(MiniServerSockArray *miniSock)
void fdset_if_valid(SOCKET a_sock, fd_set *a_set)
Add a socket file descriptor to an 'fd_set' structure as needed for ::select().
sockaddr_storage foreign_sockaddr
Socket address of the remote control point.
@ MSERV_RUNNING
miniserver is running.
@ MSERV_STOPPING
miniserver is running to stop.
void handle_request(void *args)
Receive the request and dispatch it for handling.
static void add_active_connection(SOCKET sock)
Add a socket to the active connections list.
SOCKET sock
Connection socket file descriptor.
int dispatch_request(SOCKINFO *a_info, http_parser_t *a_hparser)
Based on the type of message, appropriate callback is issued.
MiniServerState gMServState
miniserver state
static int active_connection_cmp(void *first, void *second)
Compare function for active connections, return true if equal (found)
int get_port(SOCKET sockfd, uint16_t *port)
Returns port to which socket, sockfd, is bound.
miniserver received request message.
int sock_destroy(SOCKINFO *info, int ShutdownMethod)
Shutsdown the socket using the ShutdownMethod to indicate whether sends and receives on the socket wi...
Definition sock.cpp:489
int sock_init_with_ip(SOCKINFO *info, SOCKET sockfd, sockaddr *foreign_sockaddr)
Calls the sock_init function and assigns the passed in IP address and port to the IP address and port...
Definition sock.cpp:451
SOCKET socket
Handle/descriptor to a socket.
Definition sock.hpp:67
int sock_close(SOCKET sock)
Closes the socket if it is different from -1.
Definition sock.hpp:85
Additional socket information for connections and ssl.
Definition sock.hpp:65
Manage "Step 1: Discovery" of the UPnP+™ specification 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
sockaddr & sa
Reference to sockaddr struct.
Definition sockaddr.hpp:143
in_port_t port() const
Get the numeric port.
Definition sockaddr.cpp:510
sockaddr_storage & ss
Reference to sockaddr_storage struct.
Definition sockaddr.hpp:135
WebCallback_HostValidate gWebCallback_HostValidate
webCallback for HOST validation.
Definition upnpapi.cpp:123
char gIF_IPV6_ULA_GUA[INET6_ADDRSTRLEN]
IPv6 GUA buffer to contain interface IPv6 global-unicast address.
Definition upnpapi.cpp:145
int gAllowLiteralHostRedirection
Allow literal host names redirection to numeric host names.
Definition upnpapi.cpp:129
void * gWebCallback_HostValidateCookie
Cookie to the webCallback for HOST validation.
Definition upnpapi.cpp:126
char gIF_IPV4[INET_ADDRSTRLEN]
IPv4 buffer to contain interface address. (extern'ed in upnp.h)
Definition upnpapi.cpp:152
unsigned gIF_INDEX
Index/scope-id from the used network interface.
Definition upnpapi.cpp:135
ThreadPool gMiniServerThreadPool
Mini server thread pool.
Definition upnpapi.cpp:117
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.