UPnPsdk 0.1
Universal Plug and Play +, Software Development Kit
 
Loading...
Searching...
No Matches
httpreadwrite.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: 2025-05-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 adjustment with ./pupnp source file on 2024-10-26, pUPnP ver 1.14.20
35
43#include <httpreadwrite.hpp>
44#include <UPnPsdk/synclog.hpp>
45#include <UPnPsdk/sockaddr.hpp>
46
47#include <UpnpExtraHeaders.hpp>
48#include <UpnpIntTypes.hpp>
49#include <statcodes.hpp>
50#include <upnpapi.hpp>
51#include <webserver.hpp>
52
54#include <cassert>
55#include <cstdarg>
56#include <cstring>
57
58#ifdef _WIN32
59#include <malloc.h>
60#define fseeko fseek
61#else /* _WIN32 */
62#include <sys/utsname.h>
63// fseeko is not supported on 32-bit Android older than API 24:
64// https://android.googlesource.com/platform/bionic/+/main/docs/32-bit-abi.md
65#if defined(__ANDROID__) && \
66 (!defined(__USE_FILE_OFFSET64) || __ANDROID_API__ < 24)
67#define fseeko fseek
68#endif
69#endif /* _WIN32 */
71
72#include <umock/pupnp_sock.hpp>
73#include <umock/pupnp_httprw.hpp>
74#include <umock/stdio.hpp>
75#include <umock/sys_socket.hpp>
76#include <umock/winsock2.hpp>
77#include <umock/sysinfo.hpp>
78
79
80namespace {
81
83constexpr size_t CHUNK_HEADER_SIZE{10};
85constexpr size_t CHUNK_TAIL_SIZE{10};
86
87/* in seconds */
90constexpr time_t DEFAULT_TCP_CONNECT_TIMEOUT{5};
91
93// #define PRIzd "zd"
94#define PRIzu "zu"
95#define PRIzx "zx"
97
109 const SOCKET a_sock,
110 const int connect_res
112) {
113 TRACE("Executing Check_Connect_And_Wait_Connection()")
114
115 if (connect_res == 0)
116 return 0;
117#ifdef _WIN32
118 if (umock::winsock2_h.WSAGetLastError() != WSAEWOULDBLOCK)
119 return -1;
120#else
121 if (errno != EINPROGRESS)
122 return -1;
123#endif
124
125 fd_set fdSet;
126 FD_ZERO(&fdSet);
127 FD_SET(a_sock, &fdSet);
128 timeval tmvTimeout = {DEFAULT_TCP_CONNECT_TIMEOUT, 0};
129 int result{SOCKET_ERROR};
130
131 result =
132 umock::sys_socket_h.select(a_sock + 1, NULL, &fdSet, NULL, &tmvTimeout);
133 switch (result) {
134 case SOCKET_ERROR:
135 return -1;
136 case 0:
137 /* timeout */
138 return -1;
139 }
140 int valopt{};
141 socklen_t len{sizeof(valopt)};
142 if (umock::sys_socket_h.getsockopt(a_sock, SOL_SOCKET, SO_ERROR,
143 (void*)&valopt, &len) < 0)
144 /* failed to read delayed error */
145 return -1;
146 if (valopt)
147 /* delayed error = valopt */
148 // TODO: Return more detailed error codes, e.g. valopt == 111:
149 // ECONNREFUSED "Connection refused" if there is a remote host but no
150 // server service listening.
151 return -1;
152
153 return 0;
154}
155
157// Using this variable to be able to set it by unit tests to test
158// blocking vs. unblocking at runtime, no need to compile it.
159#ifdef COMPA_ENABLE_BLOCKING_TCP_CONNECTIONS
160bool unblock_tcp_connections{false};
161#else
162bool unblock_tcp_connections{true};
163#endif
165
176 const char* url_str,
177 const char**
178 hoststr,
180 size_t* hostlen
182) {
183 TRACE("Executing get_hoststr()")
184 const char* start;
185 const char* finish;
186 int ret_code = UPNP_E_INVALID_URL;
187
188 // strlen() must be at least 2 char otherwise compiler complains boundary
189 // error with next strstr() statement.
190 if (strlen(url_str) < 2)
191 goto end_function;
192
193 start = strstr(url_str, "//");
194 if (!start)
195 goto end_function;
196
197 start += 2;
198 finish = strchr(start, '/');
199 if (finish) {
200 *hostlen = static_cast<size_t>(finish - start);
201 } else {
202 *hostlen = strlen(start);
203 }
204 *hoststr = start;
205
206 ret_code = UPNP_E_SUCCESS;
207
208end_function:
209 return ret_code;
210}
211
217void copy_msg_headers([[maybe_unused]] LinkedList* msgHeaders,
218 [[maybe_unused]] UpnpString* headers) {
219 return;
220/* TODO: */
221#if 0
222 ListNode *node;
223 UpnpHttpHeader *header;
224 http_header_t *msgHeader;
225 if (headers) {
226 ListInit(headers, NULL, (free_function) UpnpHttpHeader_delete);
227 node = ListHead(msgHeaders);
228 while(node) {
229 msgHeader = (http_header_t*) node->item;
230 header = UpnpHttpHeader_new();
231 UpnpHttpHeader_strncpy_Name(
232 header,
233 msgHeader->name.buf,
234 msgHeader->name.length);
235 UpnpHttpHeader_strncpy_Value(
236 header,
237 msgHeader->value.buf,
238 msgHeader->value.length);
239 node = ListNext(msgHeaders, node);
240 }
241 }
242#endif
243}
244
250int MakeGenericMessage(http_method_t method, const char* url_str,
251 membuffer* request, uri_type* url, int contentLength,
252 const char* contentType, const UpnpString* headers) {
253 int ret_code = 0;
254 size_t hostlen{};
255 const char* hoststr;
256
257 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__, "URL: %s method: %d\n",
258 url_str, method);
259 ret_code = http_FixStrUrl(url_str, strlen(url_str), url);
260 if (ret_code != UPNP_E_SUCCESS)
261 return ret_code;
262 /* make msg */
263 membuffer_init(request);
264 ret_code = http_MakeMessage(request, 1, 1, "Q", method, url->pathquery.buff,
265 url->pathquery.size);
266 /* add request headers if specified, otherwise use default headers */
267 if (ret_code == 0) {
268 if (headers) {
269 ret_code = http_MakeMessage(request, 1, 1, "s",
270 UpnpString_get_String(headers));
271 } else {
272 ret_code = get_hoststr(url_str, &hoststr, &hostlen);
273 if (ret_code != UPNP_E_SUCCESS)
274 return ret_code;
275 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
276 "HOSTNAME : %s Length : %" PRIzu "\n", hoststr, hostlen);
277 ret_code = http_MakeMessage(request, 1, 1,
278 "s"
279 "bcDCU",
280 "HOST: ", hoststr, hostlen);
281 }
282 }
283
284 /* add the content-type header */
285 if (ret_code == 0 && contentType) {
286 ret_code = http_MakeMessage(request, 1, 1, "T", contentType);
287 }
288 /* add content-length header. */
289 if (ret_code == 0) {
290 if (contentLength >= 0)
291 ret_code =
292 http_MakeMessage(request, 1, 1, "Nc", (off_t)contentLength);
293 else if (contentLength == UPNP_USING_CHUNKED)
294 ret_code = http_MakeMessage(request, 1, 1, "Kc");
295 else if (contentLength == UPNP_UNTIL_CLOSE)
296 ret_code = http_MakeMessage(request, 1, 1, "c");
297 else
298 ret_code = UPNP_E_INVALID_PARAM;
299 }
300 if (ret_code != 0) {
301 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
302 "HTTP Makemessage failed\n");
303 membuffer_destroy(request);
304 return ret_code;
305 }
306 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
307 "HTTP Buffer:\n%s\n"
308 "----------END--------\n",
309 request->buf);
310
311 return UPNP_E_SUCCESS;
312}
313
318 SOCKINFO sock_info;
319 int contentLength;
320 http_parser_t response;
321 int requestStarted;
322 int cancel;
323};
324
339 SOCKINFO* info,
341 http_parser_t* parser,
343 int* timeout_secs,
345 int* http_error_code) {
346 parse_status_t status;
347 int num_read;
348 char buf[2 * 1024];
349 int done = 0;
350 int ret_code = 0;
351
352 /*read response line */
353 status = parser_parse_responseline(parser);
354 switch (status) {
355 case PARSE_OK:
356 done = 1;
357 break;
358 case PARSE_INCOMPLETE:
359 done = 0;
360 break;
361 default:
362 /*error */
363 return status;
364 }
365 while (!done) {
366 num_read = sock_read(info, buf, sizeof(buf), timeout_secs);
367 if (num_read > 0) {
368 /* append data to buffer */
369 ret_code =
370 membuffer_append(&parser->msg.msg, buf, (size_t)num_read);
371 if (ret_code != 0) {
372 /* set failure status */
373 parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
374 return PARSE_FAILURE;
375 }
376 status = parser_parse_responseline(parser);
377 switch (status) {
378 case PARSE_OK:
379 done = 1;
380 break;
381 case PARSE_INCOMPLETE:
382 done = 0;
383 break;
384 default:
385 /*error */
386 return status;
387 }
388 } else if (num_read == 0) {
389 /* partial msg */
390 *http_error_code = HTTP_BAD_REQUEST; /* or response */
391 return UPNP_E_BAD_HTTPMSG;
392 } else {
393 *http_error_code = parser->http_error_code;
394 return num_read;
395 }
396 }
397 status = parser_parse_headers(parser);
398 if ((status == (parse_status_t)PARSE_OK) &&
399 (parser->position == (parser_pos_t)POS_ENTITY))
400 done = 1;
401 else if (status == (parse_status_t)PARSE_INCOMPLETE)
402 done = 0;
403 else
404 /*error */
405 return status;
406 /*read headers */
407 while (!done) {
408 num_read = sock_read(info, buf, sizeof(buf), timeout_secs);
409 if (num_read > 0) {
410 /* append data to buffer */
411 ret_code =
412 membuffer_append(&parser->msg.msg, buf, (size_t)num_read);
413 if (ret_code != 0) {
414 /* set failure status */
415 parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
416 return PARSE_FAILURE;
417 }
418 status = parser_parse_headers(parser);
419 if (status == (parse_status_t)PARSE_OK &&
420 parser->position == (parser_pos_t)POS_ENTITY)
421 done = 1;
422 else if (status == (parse_status_t)PARSE_INCOMPLETE)
423 done = 0;
424 else
425 /*error */
426 return status;
427 } else if (num_read == 0) {
428 /* partial msg */
429 *http_error_code = HTTP_BAD_REQUEST; /* or response */
430 return UPNP_E_BAD_HTTPMSG;
431 } else {
432 *http_error_code = parser->http_error_code;
433 return num_read;
434 }
435 }
436
437 return PARSE_OK;
438}
439
444int MakeGetMessageEx(const char* url_str, membuffer* request, uri_type* url,
445 SendInstruction* pRangeSpecifier) {
446 size_t url_str_len;
447 int ret_code = UPNP_E_SUCCESS;
448 size_t hostlen = 0;
449 const char* hoststr;
450
451 url_str_len = strlen(url_str);
452 do {
453 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__, "DOWNLOAD URL : %s\n",
454 url_str);
455 ret_code = http_FixStrUrl(url_str, url_str_len, url);
456 if (ret_code != UPNP_E_SUCCESS) {
457 break;
458 }
459 /* make msg */
460 membuffer_init(request);
461 ret_code = get_hoststr(url_str, &hoststr, &hostlen);
462 if (ret_code != UPNP_E_SUCCESS) {
463 break;
464 }
465 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
466 "HOSTNAME : %s Length : %" PRIzu "\n", hoststr, hostlen);
467 ret_code = http_MakeMessage(request, 1, 1,
468 "Q"
469 "s"
470 "bc"
471 "GDCUc",
472 HTTPMETHOD_GET, url->pathquery.buff,
473 url->pathquery.size, "HOST: ", hoststr,
474 hostlen, pRangeSpecifier);
475 if (ret_code != 0) {
476 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
477 "HTTP Makemessage failed\n");
478 membuffer_destroy(request);
479 return ret_code;
480 }
481 } while (0);
482 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
483 "HTTP Buffer:\n%s\n"
484 "----------END--------\n",
485 request->buf);
486
487 return ret_code;
488}
489
491} // anonymous namespace
492
494
505// Due to compatibility with Umock for Pupnp this has to be defined static.
507 const SOCKET sockfd,
508 const sockaddr* const
509 serv_addr,
510 const socklen_t addrlen
511) {
512 TRACE("Executing private_connect(), blocking " +
513 std::string(unblock_tcp_connections ? "false" : "true"))
514
515 if (unblock_tcp_connections) {
516 // This is never used due to SDK specification. It is only presevered
517 // for historical reasons.
518
519 int ret{SOCKET_ERROR};
520 // returns 0 if successful, else SOCKET_ERROR.
521 ret = umock::pupnp_sock.sock_make_no_blocking(sockfd);
522 if (ret == 0) {
523 // ret is needed for Check_Connect_And_Wait_Connection(),
524 // returns 0 if successful, else -1.
525 ret = umock::sys_socket_h.connect(sockfd, serv_addr, addrlen);
526 // returns 0 if successful, else -1.
527 ret = Check_Connect_And_Wait_Connection(sockfd, ret);
528
529 // Always make_blocking() to revert make_no_blocking() above.
530 // returns 0 if successful, else SOCKET_ERROR.
531 ret = ret | umock::pupnp_sock.sock_make_blocking(sockfd);
532 }
533
534 return ret == 0 ? 0 : SOCKET_ERROR;
535
536 } else { // tcp_connection is blocking
537
538 UPnPsdk::CSocketErr serrObj;
539 if (umock::sys_socket_h.connect(sockfd, serv_addr, addrlen) != 0) {
540 serrObj.catch_error();
541 UPnPsdk_LOGERR("MSG1020") "failed to connect() socket("
542 << sockfd << "): " << serrObj.error_str() << "\n";
543
544 return SOCKET_ERROR;
545 }
546
547 return 0;
548 }
549}
551
552#if defined(_WIN32) || defined(DOXYGEN_RUN)
553tm* http_gmtime_r(const time_t* clock, tm* result) {
554 if (clock == NULL || *clock < 0 || result == NULL)
555 return NULL;
556
557 /* gmtime in VC runtime is thread safe. */
558 gmtime_s(result, clock);
559 return result;
560}
561#endif
562
563int http_FixUrl(uri_type* url, uri_type* fixed_url) {
564 const char* temp_path = "/";
565
566 *fixed_url = *url;
567#ifdef UPnPsdk_HAVE_OPENSSL
568 if (token_string_casecmp(&fixed_url->scheme, "http") != 0 &&
569 token_string_casecmp(&fixed_url->scheme, "https") != 0) {
570 return UPNP_E_INVALID_URL;
571 }
572#else
573 if (token_string_casecmp(&fixed_url->scheme, "http") != 0) {
574 return UPNP_E_INVALID_URL;
575 }
576#endif
577 if (fixed_url->hostport.text.size == (size_t)0) {
578 return UPNP_E_INVALID_URL;
579 }
580 /* set pathquery to "/" if it is empty */
581 if (fixed_url->pathquery.size == (size_t)0) {
582 fixed_url->pathquery.buff = temp_path;
583 fixed_url->pathquery.size = (size_t)1;
584 }
585
586 return UPNP_E_SUCCESS;
587}
588
589int http_FixStrUrl(const char* urlstr, size_t urlstrlen, uri_type* fixed_url) {
590 uri_type url;
591
592 if (parse_uri(urlstr, urlstrlen, &url) != HTTP_SUCCESS) {
593 return UPNP_E_INVALID_URL;
594 }
595
596 return http_FixUrl(&url, fixed_url);
597}
598
599SOCKET http_Connect(uri_type* destination_url, uri_type* url) {
600 SOCKET connfd;
601 socklen_t sockaddr_len;
602 int ret_connect;
603
604 // BUG! Must check return value. --Ingo
605 http_FixUrl(destination_url, url);
606
607 connfd = umock::sys_socket_h.socket((int)url->hostport.IPaddress.ss_family,
608 SOCK_STREAM, 0);
609 if (connfd == INVALID_SOCKET) {
610 return (SOCKET)(UPNP_E_OUTOF_SOCKET);
611 }
612 sockaddr_len = (socklen_t)(url->hostport.IPaddress.ss_family == AF_INET6
613 ? sizeof(struct sockaddr_in6)
614 : sizeof(struct sockaddr_in));
615 ret_connect = umock::pupnp_httprw.private_connect(
616 connfd, (struct sockaddr*)&url->hostport.IPaddress, sockaddr_len);
617 if (ret_connect == -1) {
618#ifdef _WIN32
619 UpnpPrintf(UPNP_CRITICAL, HTTP, __FILE__, __LINE__,
620 "connect error: %d\n", umock::winsock2_h.WSAGetLastError());
621#endif
622 if (umock::sys_socket_h.shutdown(connfd, SD_BOTH) == -1) {
623 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
624 "Error in shutdown: %s\n", std::strerror(errno));
625 }
626 umock::unistd_h.CLOSE_SOCKET_P(connfd);
627 return (SOCKET)(UPNP_E_SOCKET_CONNECT);
628 }
629
630 return connfd;
631}
632
634 http_method_t request_method, int* timeout_secs,
635 int* http_error_code) {
636 TRACE("Executing http_RecvMessage()")
637 int ret{UPNP_E_SUCCESS};
638 int line{};
639 parse_status_t status;
640 int num_read{};
641 int ok_on_close{};
642 char* buf;
643 size_t buf_len{1024};
644
645 *http_error_code = HTTP_INTERNAL_SERVER_ERROR;
646 buf = (char*)malloc(buf_len);
647 if (!buf) {
649 goto ExitFunction;
650 }
651 if (request_method == (http_method_t)HTTPMETHOD_UNKNOWN) {
652 parser_request_init(parser);
653 } else {
654 parser_response_init(parser, request_method);
655 }
656
657 while (1) {
658 /* Double the bet if needed */
659 /* We should have already exited the loop if the value was <= 0
660 * so the cast is safe */
661 if ((size_t)num_read >= buf_len) {
662 free(buf);
663 buf_len = 2 * buf_len;
664 buf = (char*)malloc(buf_len);
665 if (!buf) {
667 goto ExitFunction;
668 }
669 }
670 num_read = sock_read(info, buf, buf_len, timeout_secs);
671 if (num_read > 0) {
672 /* got data */
673 status = parser_append(parser, buf, (size_t)num_read);
674 switch (status) {
675 case PARSE_SUCCESS:
676 UPnPsdk_LOGINFO("MSG1031") "<<< (RECVD) <<<\n"
677 << parser->msg.msg.buf << "UPnPsdk -----------------\n";
678 if (g_maxContentLength > (size_t)0 &&
679 parser->content_length > (unsigned int)g_maxContentLength) {
680 *http_error_code = HTTP_REQ_ENTITY_TOO_LARGE;
681 line = __LINE__;
683 goto ExitFunction;
684 }
685 line = __LINE__;
686 ret = 0;
687 goto ExitFunction;
688 case PARSE_FAILURE:
689 case PARSE_NO_MATCH:
690 *http_error_code = parser->http_error_code;
691 line = __LINE__;
692 ret = UPNP_E_BAD_HTTPMSG;
693 goto ExitFunction;
695 /* read until close */
696 ok_on_close = 1;
697 break;
698 case PARSE_CONTINUE_1:
699 /* Web post request. */
700 line = __LINE__;
701 ret = PARSE_SUCCESS;
702 goto ExitFunction;
703 default:
704 break;
705 }
706 } else if (num_read == 0) {
707 UPnPsdk_LOGINFO("MSG1047") "<<< (RECVD) <<<\n"
708 << parser->msg.msg.buf << "\n-----------------\n";
709 if (ok_on_close) {
710 line = __LINE__;
711 ret = 0;
712 goto ExitFunction;
713 } else {
714 *http_error_code = HTTP_BAD_REQUEST; /* or response */
715 line = __LINE__;
716 UPnPsdk_LOGERR("MSG1005") "Line " << line
717 << ": partial message.\n";
718 ret = UPNP_E_BAD_HTTPMSG;
719 goto ExitFunction;
720 }
721 } else {
722 *http_error_code = parser->http_error_code;
723 line = __LINE__;
724 ret = num_read;
725 goto ExitFunction;
726 }
727 }
728
729ExitFunction:
730 free(buf);
731 if (ret != UPNP_E_SUCCESS) {
732 UPnPsdk_LOGERR("MSG1048") "error "
733 << ret << " on line " << line
734 << ", http_error_code=" << *http_error_code << ".\n";
735 }
736
737 return ret;
738}
739
740int http_SendMessage(SOCKINFO* info, int* TimeOut, const char* fmt, ...) {
741 TRACE("Executing http_SendMessage()")
742#ifdef COMPA_HAVE_WEBSERVER
743 SendInstruction* Instr{nullptr};
744 char* file_buf{nullptr};
745 char* ChunkBuf{nullptr};
746 /* 10 byte allocated for chunk header. */
747 size_t num_read;
748 off_t amount_to_be_read{};
749 size_t Data_Buf_Size{WEB_SERVER_BUF_SIZE};
750 int I_fmt_processed = 0;
751#endif /* COMPA_HAVE_WEBSERVER */
752 va_list argp;
753 char c;
754 int RetVal = UPNP_E_SUCCESS;
755
756 UPnPsdk::SSockaddr local_saObj;
757 UPnPsdk::CSocket_basic sockObj(info->socket);
758 try {
759 sockObj.load();
760 } catch (const std::exception& ex) {
761 UPnPsdk_LOGCATCH("MSG1086") "catched next line...\n" << ex.what();
762 RetVal = UPNP_E_SOCKET_WRITE;
763 goto ExitFunction;
764 }
765 if (UPnPsdk::g_dbug) {
766 // Get the local netaddress the socket is bound to.
767 sockObj.local_saddr(&local_saObj);
768 }
769
770 va_start(argp, fmt);
771 while ((c = *fmt++) != '\0') {
772#ifdef COMPA_HAVE_WEBSERVER
773 if (c == 'I' && !I_fmt_processed) {
774 I_fmt_processed = 1;
775 Instr = va_arg(argp, struct SendInstruction*);
776 if (Instr->ReadSendSize >= 0) {
777 amount_to_be_read = Instr->ReadSendSize;
778 } else {
779 amount_to_be_read = (off_t)Data_Buf_Size;
780 }
781 if (amount_to_be_read < (off_t)WEB_SERVER_BUF_SIZE)
782 Data_Buf_Size = (size_t)amount_to_be_read;
783 ChunkBuf = (char*)malloc(
784 (size_t)(Data_Buf_Size + CHUNK_HEADER_SIZE + CHUNK_TAIL_SIZE));
785 if (!ChunkBuf) {
786 RetVal = UPNP_E_OUTOF_MEMORY;
787 goto ExitFunction;
788 }
789 file_buf = ChunkBuf + CHUNK_HEADER_SIZE;
790
791 } else if (c == 'f') {
792 /* file name */
793 char* filename = va_arg(argp, char*);
794 FILE* Fp{nullptr};
795
796 if (Instr && Instr->IsVirtualFile)
797 Fp = (FILE*)(virtualDirCallback.open)(
798 filename, UPNP_READ, Instr->Cookie, Instr->RequestCookie);
799 else
800#ifdef _WIN32
801 // returns error code direct
802 if (umock::stdio_h.fopen_s(&Fp, filename, "rb") != 0)
803 Fp = nullptr;
804#else
805 Fp = umock::stdio_h.fopen(filename, "rb");
806#endif
807 if (Fp == nullptr) {
808 RetVal = UPNP_E_FILE_READ_ERROR;
809 goto ExitFunction;
810 }
811 if (Instr && Instr->IsRangeActive && Instr->IsVirtualFile) {
812 if (virtualDirCallback.seek(Fp, Instr->RangeOffset, SEEK_CUR,
813 Instr->Cookie,
814 Instr->RequestCookie) != 0) {
815 RetVal = UPNP_E_FILE_READ_ERROR;
816 goto Cleanup_File;
817 }
818 } else if (Instr && Instr->IsRangeActive) {
819 if (fseeko(Fp, Instr->RangeOffset, SEEK_CUR) != 0) {
820 RetVal = UPNP_E_FILE_READ_ERROR;
821 goto Cleanup_File;
822 }
823 }
824 while (amount_to_be_read) {
825 if (Instr) {
826 size_t n = amount_to_be_read >= (off_t)Data_Buf_Size
827 ? Data_Buf_Size
828 : (size_t)amount_to_be_read;
829 if (Instr->IsVirtualFile) {
830 int nr = virtualDirCallback.read(Fp, file_buf, n,
831 Instr->Cookie,
832 Instr->RequestCookie);
833 if (nr < 0) {
834 RetVal = UPNP_E_FILE_READ_ERROR;
835 goto Cleanup_File;
836 }
837 // Type cast is save, 'nr' cannot be negative.
838 num_read = static_cast<size_t>(nr);
839 } else {
840 num_read = umock::stdio_h.fread(file_buf, 1u, n, Fp);
841 if (umock::stdio_h.ferror(Fp)) {
842 RetVal = UPNP_E_FILE_READ_ERROR;
843 umock::stdio_h.clearerr(Fp);
844 goto Cleanup_File;
845 }
846 }
847 amount_to_be_read -= (off_t)num_read;
848 if (Instr->ReadSendSize < 0) {
849 /* read until close */
850 amount_to_be_read = (off_t)Data_Buf_Size;
851 }
852 } else {
853 num_read = umock::stdio_h.fread(file_buf, (size_t)1,
854 Data_Buf_Size, Fp);
855 if (umock::stdio_h.ferror(Fp)) {
856 RetVal = UPNP_E_FILE_READ_ERROR;
857 umock::stdio_h.clearerr(Fp);
858 goto Cleanup_File;
859 }
860 }
861 int num_write;
862 if (num_read == 0u) {
863 /* EOF so no more to send. */
864 if (Instr && Instr->IsChunkActive) {
865 const char* str = "0\r\n\r\n";
866 num_write = sock_write(info, str, strlen(str), TimeOut);
867 } else {
868 RetVal = UPNP_E_FILE_READ_ERROR;
869 }
870 goto Cleanup_File;
871 }
872 /* Create chunk for the current buffer. */
873 if (Instr && Instr->IsChunkActive) {
874 /* Copy CRLF at the end of the chunk */
875 memcpy(file_buf + num_read, "\r\n", (size_t)2);
876 /* Hex length for the chunk size. */
877 char Chunk_Header[CHUNK_HEADER_SIZE]{};
878 int rc = snprintf(Chunk_Header, sizeof(Chunk_Header),
879 "%" PRIzx "\r\n", num_read);
880 if (rc < 0 ||
881 static_cast<unsigned int>(rc) >= sizeof(Chunk_Header)) {
882 RetVal = UPNP_E_INTERNAL_ERROR;
883 goto Cleanup_File;
884 }
885 /* Copy the chunk size header */
886 memcpy(file_buf - strlen(Chunk_Header), Chunk_Header,
887 strlen(Chunk_Header));
888 /* on the top of the buffer. */
889 /*file_buf[num_read+strlen(Chunk_Header)]
890 * = NULL; */
891 /*upnpprintf("Sending
892 * %s\n",file_buf-strlen(Chunk_Header));*/
893 num_write = sock_write(
894 info, file_buf - strlen(Chunk_Header),
895 num_read + strlen(Chunk_Header) + 2u, TimeOut);
896 size_t num_written = static_cast<size_t>(num_write);
897 if (num_write <= 0 ||
898 num_written != num_read + strlen(Chunk_Header) + 2u)
899 /* Send error nothing we can do. */
900 goto Cleanup_File;
901 } else {
902 /* write data */
903 num_write = sock_write(info, file_buf, num_read, TimeOut);
904
905 UPnPsdk_LOGINFO(
906 "MSG1104") ">>> (SENT) >>> UDevice response_out "
907 "from local \""
908 << local_saObj.netaddrp()
909 << "\" to (\"HOST:\" in following message) ...\n"
910 << std::string(file_buf, static_cast<size_t>(num_write))
911 << "\nUPnPsdk num_written=" << num_write
912 << ".\nUPnPsdk ------------\n";
913
914 /* Send error nothing we can do */
915 size_t num_written = static_cast<size_t>(num_write);
916 if (num_write <= 0 || num_written != num_read) {
917 goto Cleanup_File;
918 }
919 }
920 } /* while */
921
922 Cleanup_File:
923 if (Instr && Instr->IsVirtualFile) {
924 virtualDirCallback.close(Fp, Instr->Cookie,
925 Instr->RequestCookie);
926 } else {
927 umock::stdio_h.fclose(Fp);
928 }
929 goto ExitFunction;
930 } else
931#endif /* COMPA_HAVE_WEBSERVER */
932
933 if (c == 'b') {
934 /* Message to send is given in a memory buffer */
935 char* buf = va_arg(argp, char*);
936 size_t buf_length = va_arg(argp, size_t);
937 if (buf_length > 0u) {
938 // Write data.
939 int num_write = sock_write(info, buf, buf_length, TimeOut);
940
941 UPnPsdk_LOGINFO(
942 "MSG1105") ">>> (SENT) >>> UDevice response_out "
943 "from local \""
944 << local_saObj.netaddrp()
945 << "\" to (\"HOST:\" in following message) ...\n"
946 << std::string(buf, buf_length)
947 << "\nUPnPsdk buf_length=" << buf_length
948 << ", num_written=" << num_write
949 << ".\nUPnPsdk ------------\n";
950
951 if (num_write < 0) {
952 RetVal = num_write;
953 goto ExitFunction;
954 }
955 // 'num_write' may be negive in case of error. This will be
956 // casted to very big positive numbers that also never
957 // equals to buf_length, means the cast is save.
958 if (static_cast<size_t>(num_write) != buf_length) {
959 RetVal = UPNP_E_SOCKET_WRITE;
960 goto ExitFunction;
961 }
962 }
963 }
964 } // while
965
966ExitFunction:
967 va_end(argp);
968#ifdef COMPA_HAVE_WEBSERVER
969 free(ChunkBuf);
970#endif
971 return RetVal;
972}
973
974
975int http_RequestAndResponse(uri_type* destination, const char* request,
976 size_t request_length, http_method_t req_method,
977 int timeout_secs, http_parser_t* response) {
978 TRACE("Executing http_RequestAndResponse()")
979 SOCKET tcp_connection;
980 int ret_code;
981 socklen_t sockaddr_len;
982 SOCKINFO info;
983
984 tcp_connection = umock::sys_socket_h.socket(
985 (int)destination->hostport.IPaddress.ss_family, SOCK_STREAM, 0);
986 if (tcp_connection == INVALID_SOCKET) {
987 parser_response_init(response, req_method);
988 return UPNP_E_SOCKET_ERROR;
989 }
990 if (sock_init(&info, tcp_connection) != UPNP_E_SUCCESS) {
991 parser_response_init(response, req_method);
992 ret_code = UPNP_E_SOCKET_ERROR;
993 goto end_function;
994 }
995 /* connect */
996 sockaddr_len = destination->hostport.IPaddress.ss_family == AF_INET6
997 ? sizeof(sockaddr_in6)
998 : sizeof(sockaddr_in);
999 ret_code = umock::pupnp_httprw.private_connect(
1000 info.socket, (sockaddr*)&(destination->hostport.IPaddress),
1001 sockaddr_len);
1002 if (ret_code == -1) {
1003 parser_response_init(response, req_method);
1004 ret_code = UPNP_E_SOCKET_CONNECT;
1005 goto end_function;
1006 }
1007 /* send request */
1008 ret_code =
1009 http_SendMessage(&info, &timeout_secs, "b", request, request_length);
1010 if (ret_code != 0) {
1011 parser_response_init(response, req_method);
1012 goto end_function;
1013 }
1014 /* recv response */
1015 int http_error_code;
1016 ret_code = http_RecvMessage(&info, response, req_method, &timeout_secs,
1017 &http_error_code);
1018
1019end_function:
1020 /* should shutdown completely */
1021 sock_destroy(&info, SD_BOTH);
1022
1023 return ret_code;
1024}
1025
1026
1027int http_Download(const char* url_str, int timeout_secs, char** document,
1028 size_t* doc_length, char* content_type) {
1029 TRACE("Executing http_Download()")
1030 int ret_code;
1031 char* msg_start;
1032 char* entity_start;
1033 size_t msg_length;
1034 memptr ctype;
1035 size_t copy_len;
1036
1037 // Normalize URL-string into
1038 uri_type url;
1039 {
1040 size_t url_str_len = strlen(url_str);
1041 /*ret_code = parse_uri( (char*)url_str, url_str_len, &url ); */
1042 UPnPsdk_LOGINFO("MSG1098") "UDevice DOWNLOAD URL=\"" << url_str
1043 << "\".\n";
1044 ret_code = http_FixStrUrl(url_str, url_str_len, &url);
1045 if (ret_code != UPNP_E_SUCCESS) {
1046 return ret_code;
1047 }
1048 }
1049
1050 // make msg into
1051 http_parser_t response_in;
1052 {
1053 const char* hoststr;
1054 size_t hostlen;
1055 ret_code = get_hoststr(url_str, &hoststr, &hostlen);
1056 if (ret_code != UPNP_E_SUCCESS) {
1057 return ret_code;
1058 }
1059
1060 membuffer request_out;
1061 membuffer_init(&request_out); // Does not allocate memory
1062 ret_code =
1063 http_MakeMessage(&request_out, 1, 1,
1064 "Q"
1065 "s"
1066 "bcDCUc",
1067 HTTPMETHOD_GET, url.pathquery.buff,
1068 url.pathquery.size, "HOST: ", hoststr, hostlen);
1069 if (ret_code != 0) {
1070 UPnPsdk_LOGERR("MSG1100") "HTTP MakeMessage failed.\n";
1071 membuffer_destroy(&request_out);
1072 return ret_code;
1073 }
1074 UPnPsdk_LOGINFO("MSG1101") "Ctrlpnt send request_out HTTP Buffer...\n"
1075 << request_out.buf << "UPnPsdk ----------END--------\n";
1076
1077 /* get doc msg */
1078 ret_code =
1079 http_RequestAndResponse(&url, request_out.buf, request_out.length,
1080 HTTPMETHOD_GET, timeout_secs, &response_in);
1081
1082 membuffer_destroy(&request_out);
1083 if (ret_code != 0) {
1084 httpmsg_destroy(&response_in.msg);
1085 return ret_code;
1086 }
1087 }
1088 UPnPsdk_LOGINFO("MSG1102") "Response_in...\n";
1089
1090 /* optional content-type */
1091 if (content_type) {
1092 if (httpmsg_find_hdr(&response_in.msg, HDR_CONTENT_TYPE, &ctype) ==
1093 NULL) {
1094 *content_type = '\0'; /* no content-type */
1095 } else {
1096 /* safety */
1097 copy_len = ctype.length < LINE_SIZE - (size_t)1
1098 ? ctype.length
1099 : LINE_SIZE - (size_t)1;
1100
1101 memcpy(content_type, ctype.buf, copy_len);
1102 content_type[copy_len] = '\0';
1103 }
1104 }
1105
1106 /* extract doc from msg */
1107 if ((*doc_length = response_in.msg.entity.length) == (size_t)0) {
1108 /* 0-length msg */
1109 *document = NULL;
1110 } else if (response_in.msg.status_code == HTTP_OK) {
1111 /*LEAK_FIX_MK */
1112 /* copy entity */
1113 entity_start = response_in.msg.entity.buf; /* what we want */
1114 msg_length = response_in.msg.msg.length; /* save for posterity */
1115 msg_start = membuffer_detach(&response_in.msg.msg); /* whole msg */
1116 /* move entity to the start; copy null-terminator too */
1117 memmove(msg_start, entity_start, *doc_length + (size_t)1);
1118 /* save mem for body only */
1119 *document =
1120 (char*)realloc(msg_start, *doc_length + (size_t)1); /*LEAK_FIX_MK */
1121 /* *document = Realloc( msg_start,msg_length, *doc_length + 1 );
1122 * LEAK_FIX_MK */
1123 /* shrink can't fail */
1124 assert(msg_length > *doc_length);
1125 assert(*document != NULL);
1126 if (msg_length <= *doc_length || *document == NULL)
1127 UPnPsdk_LOGINFO("MSG1103") "msg_length("
1128 << msg_length << ") <= *doc_length(" << *doc_length
1129 << ") or document is NULL.\n";
1130 }
1131 if (response_in.msg.status_code == HTTP_OK) {
1132 ret_code = 0; /* success */
1133 } else {
1134 /* server sent error msg (not requested doc) */
1135 ret_code = response_in.msg.status_code;
1136 }
1137 httpmsg_destroy(&response_in.msg);
1138
1139 return ret_code;
1140}
1141
1142
1143int http_HttpGetProgress(void* Handle, size_t* length, size_t* total) {
1144 http_connection_handle_t* handle = (http_connection_handle_t*)Handle;
1145
1146 if (!handle || !length || !total) {
1147 return UPNP_E_INVALID_PARAM;
1148 }
1149 *length = handle->response.msg.entity.length;
1150 *total = handle->response.content_length;
1151
1152 return UPNP_E_SUCCESS;
1153}
1154
1155int http_CancelHttpGet(void* Handle) {
1156 http_connection_handle_t* handle = (http_connection_handle_t*)Handle;
1157
1158 if (!handle)
1159 return UPNP_E_INVALID_PARAM;
1160 handle->cancel = 1;
1161
1162 return UPNP_E_SUCCESS;
1163}
1164
1165int http_OpenHttpConnection(const char* url_str, void** Handle,
1166 [[maybe_unused]] int timeout) {
1167 TRACE("Executing http_OpenHttpConnection()")
1168 int ret_code;
1169 size_t sockaddr_len;
1170 SOCKET tcp_connection;
1171 http_connection_handle_t* handle = nullptr;
1172 uri_type url;
1173 // BUG! *Handle should be initialized here? Otherwise possible segfault.
1174 // *Handle = handle;
1175 if (!url_str || !Handle)
1176 return UPNP_E_INVALID_PARAM;
1177 *Handle = handle;
1178 /* parse url_str */
1179 ret_code = http_FixStrUrl(url_str, strlen(url_str), &url);
1180 if (ret_code != UPNP_E_SUCCESS)
1181 return ret_code;
1182 /* create the connection handle */
1183 handle =
1184 (http_connection_handle_t*)malloc(sizeof(http_connection_handle_t));
1185 if (!handle) {
1186 return UPNP_E_OUTOF_MEMORY;
1187 }
1188 handle->requestStarted = 0;
1189 memset(&handle->response, 0, sizeof(handle->response));
1190 /* connect to the server */
1191 tcp_connection = umock::sys_socket_h.socket(
1192 url.hostport.IPaddress.ss_family, SOCK_STREAM, 0);
1193 if (tcp_connection == INVALID_SOCKET) {
1194 handle->sock_info.socket = INVALID_SOCKET;
1195 ret_code = UPNP_E_SOCKET_ERROR;
1196 goto errorHandler;
1197 }
1198 if (sock_init(&handle->sock_info, tcp_connection) != UPNP_E_SUCCESS) {
1199 sock_destroy(&handle->sock_info, SD_BOTH);
1200 ret_code = UPNP_E_SOCKET_ERROR;
1201 goto errorHandler;
1202 }
1203 sockaddr_len = url.hostport.IPaddress.ss_family == AF_INET6
1204 ? sizeof(struct sockaddr_in6)
1205 : sizeof(struct sockaddr_in);
1206 ret_code = umock::pupnp_httprw.private_connect(
1207 handle->sock_info.socket, (struct sockaddr*)&(url.hostport.IPaddress),
1208 (socklen_t)sockaddr_len);
1209 if (ret_code == -1) {
1210 sock_destroy(&handle->sock_info, SD_BOTH);
1211 ret_code = UPNP_E_SOCKET_CONNECT;
1212 goto errorHandler;
1213 }
1214#ifdef UPnPsdk_HAVE_OPENSSL
1215 /* For HTTPS connections start the TLS/SSL handshake. */
1216 if (token_string_casecmp(&url.scheme, "https") == 0) {
1217 ret_code = sock_ssl_connect(&handle->sock_info);
1218 if (ret_code != UPNP_E_SUCCESS) {
1219 sock_destroy(&handle->sock_info, SD_BOTH);
1220 goto errorHandler;
1221 }
1222 }
1223#endif
1224errorHandler:
1225 *Handle = handle;
1226 return ret_code;
1227}
1228
1229int http_MakeHttpRequest(Upnp_HttpMethod method, const char* url_str,
1230 void* Handle, UpnpString* headers,
1231 const char* contentType, int contentLength,
1232 int timeout) {
1233 int ret_code;
1234 membuffer request;
1235 http_connection_handle_t* handle = (http_connection_handle_t*)Handle;
1236 uri_type url;
1237 if (!url_str || !Handle)
1238 return UPNP_E_INVALID_PARAM;
1239 if (handle->requestStarted) {
1240 /* TODO: Log an error that a previous request is already in
1241 * progress. */
1242 }
1243 handle->requestStarted = 1;
1244 handle->cancel = 0;
1245 ret_code = MakeGenericMessage((http_method_t)method, url_str, &request,
1246 &url, contentLength, contentType, headers);
1247 if (ret_code != UPNP_E_SUCCESS)
1248 return ret_code;
1249 /* send request */
1250 ret_code = http_SendMessage(&handle->sock_info, &timeout, "b", request.buf,
1251 request.length);
1252 membuffer_destroy(&request);
1253 httpmsg_destroy(&handle->response.msg);
1254 parser_response_init(&handle->response, (http_method_t)method);
1255 return ret_code;
1256}
1257
1258int http_WriteHttpRequest(void* Handle, char* buf, size_t* size, int timeout) {
1259 http_connection_handle_t* handle = (http_connection_handle_t*)Handle;
1260 char* tempbuf = NULL;
1261 size_t tempbufSize = 0;
1262 int freeTempbuf = 0;
1263 int numWritten = 0;
1264
1265 if (!handle || !size || !buf) {
1266 if (size)
1267 *size = 0;
1268 return UPNP_E_INVALID_PARAM;
1269 }
1270 if (handle->contentLength == UPNP_USING_CHUNKED) {
1271 if (*size) {
1272 size_t tempSize = 0;
1273 size_t tempbuf_len{*size + CHUNK_HEADER_SIZE + CHUNK_TAIL_SIZE};
1274 tempbuf = (char*)malloc(tempbuf_len);
1275 if (!tempbuf)
1276 return UPNP_E_OUTOF_MEMORY;
1277 /* begin chunk */
1278 snprintf(tempbuf, tempbuf_len, "%zx\r\n", *size);
1279 tempSize = strlen(tempbuf);
1280 memcpy(tempbuf + tempSize, buf, *size);
1281 memcpy(tempbuf + tempSize + *size, "\r\n", 2);
1282 /* end of chunk */
1283 tempbufSize = tempSize + *size + 2;
1284 freeTempbuf = 1;
1285 }
1286 } else {
1287 tempbuf = buf;
1288 tempbufSize = *size;
1289 }
1290 numWritten = sock_write(&handle->sock_info, tempbuf, tempbufSize, &timeout);
1291 if (freeTempbuf)
1292 free(tempbuf);
1293 if (numWritten < 0) {
1294 *size = 0;
1295 return numWritten;
1296 } else {
1297 *size = (size_t)numWritten;
1298 return UPNP_E_SUCCESS;
1299 }
1300}
1301
1302int http_EndHttpRequest(void* Handle, int timeout) {
1303 int retc = 0;
1304 const char* zcrlf = "0\r\n\r\n";
1305 http_connection_handle_t* handle = (http_connection_handle_t*)Handle;
1306 if (!handle)
1307 return UPNP_E_INVALID_PARAM;
1308 if (!handle->requestStarted) {
1309 return UPNP_E_SUCCESS;
1310 }
1311 handle->requestStarted = 0;
1312 if (handle->contentLength == UPNP_USING_CHUNKED)
1313 /*send last chunk */
1314 retc = sock_write(&handle->sock_info, zcrlf, strlen(zcrlf), &timeout);
1315
1316 return retc >= 0 ? UPNP_E_SUCCESS : UPNP_E_SOCKET_WRITE;
1317}
1318
1319int http_GetHttpResponse(void* Handle, UpnpString* headers, char** contentType,
1320 int* contentLength, int* httpStatus, int timeout) {
1321 int ret_code;
1322 int http_error_code;
1323 memptr ctype;
1324 http_connection_handle_t* handle = (http_connection_handle_t*)Handle;
1325 parse_status_t status;
1326
1328 &handle->sock_info, &handle->response, &timeout, &http_error_code);
1329 if (status != (parse_status_t)PARSE_OK) {
1330 ret_code = UPNP_E_BAD_RESPONSE;
1331 goto errorHandler;
1332 }
1333 status = parser_get_entity_read_method(&handle->response);
1334 switch (status) {
1335 case PARSE_CONTINUE_1:
1336 case PARSE_SUCCESS:
1337 break;
1338 default:
1339 ret_code = UPNP_E_BAD_RESPONSE;
1340 goto errorHandler;
1341 }
1342 ret_code = UPNP_E_SUCCESS;
1343 if (httpStatus) {
1344 *httpStatus = handle->response.msg.status_code;
1345 }
1346 if (contentType) {
1347 if (!httpmsg_find_hdr(&handle->response.msg, HDR_CONTENT_TYPE, &ctype))
1348 /* no content-type */
1349 *contentType = NULL;
1350 else
1351 *contentType = ctype.buf;
1352 }
1353 if (contentLength) {
1354 if (handle->response.position == (parser_pos_t)POS_COMPLETE)
1355 *contentLength = 0;
1356 else if (handle->response.ent_position == ENTREAD_USING_CHUNKED)
1357 *contentLength = UPNP_USING_CHUNKED;
1358 else if (handle->response.ent_position == ENTREAD_USING_CLEN)
1359 *contentLength = (int)handle->response.content_length;
1360 else if (handle->response.ent_position == ENTREAD_UNTIL_CLOSE)
1361 *contentLength = UPNP_UNTIL_CLOSE;
1362 }
1363
1364 if (headers) {
1365 copy_msg_headers(&handle->response.msg.headers, headers);
1366 }
1367
1368errorHandler:
1369 if (ret_code != UPNP_E_SUCCESS)
1370 httpmsg_destroy(&handle->response.msg);
1371 return ret_code;
1372}
1373
1374int http_ReadHttpResponse(void* Handle, char* buf, size_t* size, int timeout) {
1375 http_connection_handle_t* handle = (http_connection_handle_t*)Handle;
1376 parse_status_t status;
1377 int num_read;
1378 int ok_on_close = 0;
1379 char tempbuf[2 * 1024];
1380 int ret_code = 0;
1381
1382 if (!handle || !size || (*size > 0 && !buf)) {
1383 if (size)
1384 *size = 0;
1385 return UPNP_E_INVALID_PARAM;
1386 }
1387 /* first parse what has already been gotten */
1388 if (handle->response.position != POS_COMPLETE)
1389 status = parser_parse_entity(&handle->response);
1390 else
1391 status = PARSE_SUCCESS;
1392 if (status == PARSE_INCOMPLETE_ENTITY)
1393 /* read until close */
1394 ok_on_close = 1;
1395 else if ((status != PARSE_SUCCESS) && (status != PARSE_CONTINUE_1) &&
1396 (status != PARSE_INCOMPLETE)) {
1397 /*error */
1398 *size = 0;
1399 return UPNP_E_BAD_RESPONSE;
1400 }
1401 /* read more if necessary entity */
1402 while (handle->response.msg.amount_discarded + *size >
1403 handle->response.msg.entity.length &&
1404 !handle->cancel && handle->response.position != POS_COMPLETE) {
1405 num_read =
1406 sock_read(&handle->sock_info, tempbuf, sizeof(tempbuf), &timeout);
1407 if (num_read > 0) {
1408 /* append data to buffer */
1409 ret_code = membuffer_append(&handle->response.msg.msg, tempbuf,
1410 (size_t)num_read);
1411 if (ret_code != 0) {
1412 /* set failure status */
1413 handle->response.http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1414 *size = 0;
1415 return PARSE_FAILURE;
1416 }
1417 status = parser_parse_entity(&handle->response);
1418 if (status == PARSE_INCOMPLETE_ENTITY) {
1419 /* read until close */
1420 ok_on_close = 1;
1421 } else if ((status != PARSE_SUCCESS) &&
1422 (status != PARSE_CONTINUE_1) &&
1423 (status != PARSE_INCOMPLETE)) {
1424 /*error */
1425 *size = 0;
1426 return UPNP_E_BAD_RESPONSE;
1427 }
1428 } else if (num_read == 0) {
1429 if (ok_on_close) {
1430 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
1431 "<<< (RECVD) "
1432 "<<<\n%s\n-----------------\n",
1433 handle->response.msg.msg.buf);
1434 handle->response.position = POS_COMPLETE;
1435 } else {
1436 /* partial msg */
1437 *size = 0;
1438 handle->response.http_error_code =
1439 HTTP_BAD_REQUEST; /* or response */
1440 return UPNP_E_BAD_HTTPMSG;
1441 }
1442 } else {
1443 *size = 0;
1444 return num_read;
1445 }
1446 }
1447 if (handle->cancel) {
1448 return UPNP_E_CANCELED;
1449 }
1450 /* truncate size to fall within available data */
1451 if (handle->response.msg.amount_discarded + *size >
1452 handle->response.msg.entity.length)
1453 *size = handle->response.msg.entity.length -
1454 handle->response.msg.amount_discarded;
1455 /* copy data to user buffer. delete copied data */
1456 if (*size > 0) {
1457 memcpy(buf,
1458 &handle->response.msg.msg
1459 .buf[handle->response.entity_start_position],
1460 *size);
1461 membuffer_delete(&handle->response.msg.msg,
1462 handle->response.entity_start_position, *size);
1463 /* update scanner position. needed for chunked transfers */
1464 handle->response.scanner.cursor -= *size;
1465 /* update amount discarded */
1466 handle->response.msg.amount_discarded += *size;
1467 }
1468
1469 return UPNP_E_SUCCESS;
1470}
1471
1472int http_CloseHttpConnection(void* Handle) {
1473 http_connection_handle_t* handle = (http_connection_handle_t*)Handle;
1474 if (!handle)
1475 return UPNP_E_INVALID_PARAM;
1476 /*should shutdown completely */
1477 sock_destroy(&handle->sock_info, SD_BOTH);
1478 httpmsg_destroy(&handle->response.msg);
1479 free(handle);
1480 return UPNP_E_SUCCESS;
1481}
1482
1483int http_SendStatusResponse(SOCKINFO* info, int http_status_code,
1484 int request_major_version,
1485 int request_minor_version) {
1486 int response_major, response_minor;
1487 membuffer membuf;
1488 int ret;
1489 int timeout;
1490
1491 http_CalcResponseVersion(request_major_version, request_minor_version,
1492 &response_major, &response_minor);
1493 membuffer_init(&membuf);
1494 membuf.size_inc = (size_t)70;
1495 /* response start line */
1496 ret = http_MakeMessage(&membuf, response_major, response_minor, "RSCB",
1497 http_status_code, http_status_code);
1498 if (ret == 0) {
1499 timeout = HTTP_DEFAULT_TIMEOUT;
1500 ret = http_SendMessage(info, &timeout, "b", membuf.buf, membuf.length);
1501 }
1502 membuffer_destroy(&membuf);
1503
1504 return ret;
1505}
1506
1508// Using this variable to be able to modify it by gtests without recompile.
1509std::string web_server_content_language{WEB_SERVER_CONTENT_LANGUAGE};
1511
1512int http_MakeMessage(membuffer* buf, int http_major_version,
1513 int http_minor_version, const char* fmt, ...) {
1514 // For format types look at the declaration of http_MakeMessage() in the
1515 // header file httpreadwrite.hpp.
1516 TRACE("Executing http_MakeMessage()")
1517 char c;
1518 char* s = NULL;
1519 size_t num;
1520 off_t bignum;
1521 size_t length;
1522 time_t* loc_time;
1523 time_t curr_time;
1524 struct tm date_storage;
1525 struct tm* date;
1526 const char* start_str;
1527 const char* end_str;
1528 int status_code;
1529 const char* status_msg;
1530 http_method_t method;
1531 const char* method_str;
1532 const char* url_str;
1533 const char* temp_str;
1534 uri_type url;
1535 uri_type* uri_ptr;
1536 int error_code = 0;
1537 va_list argp;
1538 char tempbuf[200];
1539 const char* weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
1540 const char* month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
1541 "Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
1542 int rc = 0;
1543
1544 memset(tempbuf, 0, sizeof(tempbuf));
1545 va_start(argp, fmt);
1546 while ((c = *fmt++) != 0) {
1547 if (c == 'E') {
1548 /* list of extra headers */
1549 UpnpListIter pos;
1550 UpnpListHead* head;
1551 UpnpExtraHeaders* extra;
1552 const DOMString resp;
1553 head = (UpnpListHead*)va_arg(argp, UpnpListHead*);
1554 if (head) {
1555 for (pos = UpnpListBegin(head); pos != UpnpListEnd(head);
1556 pos = UpnpListNext(head, pos)) {
1557 extra = (UpnpExtraHeaders*)pos;
1558 resp = UpnpExtraHeaders_get_resp(extra);
1559 if (resp) {
1560 if (membuffer_append(buf, resp, strlen(resp)))
1561 goto error_handler;
1562 if (membuffer_append(buf, "\r\n", (size_t)2))
1563 goto error_handler;
1564 }
1565 }
1566 }
1567 } else if (c == 's') {
1568 /* C string */
1569 s = (char*)va_arg(argp, char*);
1570 assert(s);
1571 UpnpPrintf(UPNP_ALL, HTTP, __FILE__, __LINE__,
1572 "Adding a string : %s\n", s);
1573 if (membuffer_append(buf, s, strlen(s)))
1574 goto error_handler;
1575 } else if (c == 'K') {
1576 /* Add Chunky header */
1577 if (membuffer_append_str(buf, "TRANSFER-ENCODING: chunked\r\n"))
1578 goto error_handler;
1579 } else if (c == 'A') {
1580 /* Add Access-Control-Allow-Origin header only if
1581 * set by UpnpSetWebServerCorsString */
1582 struct SendInstruction* RespInstr;
1583 RespInstr =
1584 (struct SendInstruction*)va_arg(argp, struct SendInstruction*);
1585 assert(RespInstr);
1586 if (RespInstr->CorsHeader &&
1587 strcmp(static_cast<const char*>(RespInstr->CorsHeader), "") &&
1588 http_MakeMessage(buf, http_major_version, http_minor_version,
1589 "ssc", "Access-Control-Allow-Origin: ",
1590 RespInstr->CorsHeader) != 0)
1591 goto error_handler;
1592 } else if (c == 'G') {
1593 /* Add Range header */
1594 struct SendInstruction* RespInstr;
1595 RespInstr =
1596 (struct SendInstruction*)va_arg(argp, struct SendInstruction*);
1597 assert(RespInstr);
1598 /* connection header */
1599 if (membuffer_append(buf, RespInstr->RangeHeader,
1600 strlen(RespInstr->RangeHeader)))
1601 goto error_handler;
1602 } else if (c == 'b') {
1603 /* mem buffer */
1604 s = (char*)va_arg(argp, char*);
1605 UpnpPrintf(UPNP_ALL, HTTP, __FILE__, __LINE__,
1606 "Adding a char Buffer starting with: %c\n", (int)s[0]);
1607 assert(s);
1608 length = (size_t)va_arg(argp, size_t);
1609 if (membuffer_append(buf, s, length))
1610 goto error_handler;
1611 } else if (c == 'c') {
1612 /* crlf */
1613 if (membuffer_append(buf, "\r\n", (size_t)2))
1614 goto error_handler;
1615 } else if (c == 'd') {
1616 /* integer */
1617 num = (size_t)va_arg(argp, int);
1618 rc = snprintf(tempbuf, sizeof(tempbuf), "%" PRIzu, num);
1619 if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf) ||
1620 membuffer_append(buf, tempbuf, strlen(tempbuf)))
1621 goto error_handler;
1622 } else if (c == 'h') {
1623 /* off_t */
1624 bignum = (off_t)va_arg(argp, off_t);
1625 rc =
1626 snprintf(tempbuf, sizeof(tempbuf), "%" PRId64, (int64_t)bignum);
1627 if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf) ||
1628 membuffer_append(buf, tempbuf, strlen(tempbuf)))
1629 goto error_handler;
1630 } else if (c == 't' || c == 'D') {
1631 /* date */
1632 if (c == 'D') {
1633 /* header */
1634 start_str = "DATE: ";
1635 end_str = "\r\n";
1636 curr_time = umock::sysinfo.time(NULL);
1637 loc_time = &curr_time;
1638 } else {
1639 /* date value only */
1640 start_str = end_str = "";
1641 loc_time = (time_t*)va_arg(argp, time_t*);
1642 }
1643 assert(loc_time);
1644 date = http_gmtime_r(loc_time, &date_storage);
1645 if (date == NULL)
1646 goto error_handler;
1647 rc = snprintf(tempbuf, sizeof(tempbuf),
1648 "%s%s, %02d %s %d %02d:%02d:%02d GMT%s", start_str,
1649 &weekday_str[date->tm_wday * 4], date->tm_mday,
1650 &month_str[date->tm_mon * 4], date->tm_year + 1900,
1651 date->tm_hour, date->tm_min, date->tm_sec, end_str);
1652 if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf) ||
1653 membuffer_append(buf, tempbuf, strlen(tempbuf)))
1654 goto error_handler;
1655 } else if (c == 'L') {
1656 // Add CONTENT-LANGUAGE header only
1657 // if Accept-Language header is not empty
1658 // and
1659 // if web_server_content_language (aka WEB_SERVER_CONTENT_LANGUAGE)
1660 // is not empty
1661 SendInstruction* RespInstr;
1662 RespInstr = (SendInstruction*)va_arg(argp, SendInstruction*);
1663 assert(RespInstr);
1664 if ((bool)strcmp(RespInstr->AcceptLanguageHeader, "") &&
1665 !web_server_content_language.empty() &&
1666 (bool)http_MakeMessage(
1667 buf, http_major_version, http_minor_version, "ssc",
1668 "CONTENT-LANGUAGE: ",
1669 web_server_content_language.c_str()) != 0)
1670 goto error_handler;
1671 } else if (c == 'C') {
1672 if ((http_major_version > 1) ||
1673 (http_major_version == 1 && http_minor_version == 1)) {
1674 /* connection header */
1675 if (membuffer_append_str(buf, "CONNECTION: close\r\n"))
1676 goto error_handler;
1677 }
1678 } else if (c == 'N') {
1679 /* content-length header */
1680 bignum = (off_t)va_arg(argp, off_t);
1681 assert(bignum >= 0);
1682 if (http_MakeMessage(buf, http_major_version, http_minor_version,
1683 "shc", "CONTENT-LENGTH: ", bignum) != 0)
1684 goto error_handler;
1685 /* Add accept ranges */
1686 if (http_MakeMessage(buf, http_major_version, http_minor_version,
1687 "sc", "Accept-Ranges: bytes") != 0)
1688 goto error_handler;
1689 } else if (c == 'S' || c == 'U') {
1690 /* SERVER or USER-AGENT header */
1691 temp_str = (c == 'S') ? "SERVER: " : "USER-AGENT: ";
1692 get_sdk_info(tempbuf, sizeof(tempbuf));
1693 if (http_MakeMessage(buf, http_major_version, http_minor_version,
1694 "ss", temp_str, tempbuf) != 0)
1695 goto error_handler;
1696 } else if (c == 'X') {
1697 /* C string */
1698 s = (char*)va_arg(argp, char*);
1699 assert(s);
1700 if (membuffer_append_str(buf, "X-User-Agent: ") != 0)
1701 goto error_handler;
1702 if (membuffer_append(buf, s, strlen(s)) != 0)
1703 goto error_handler;
1704 } else if (c == 'R') {
1705 /* response start line */
1706 /* e.g.: 'HTTP/1.1 200 OK' code */
1707 status_code = (int)va_arg(argp, int);
1708 assert(status_code > 0);
1709 rc = snprintf(tempbuf, sizeof(tempbuf), "HTTP/%d.%d %d ",
1710 http_major_version, http_minor_version, status_code);
1711 /* str */
1712 status_msg = http_get_code_text(status_code);
1713 if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf) ||
1714 http_MakeMessage(buf, http_major_version, http_minor_version,
1715 "ssc", tempbuf, status_msg) != 0)
1716 goto error_handler;
1717 } else if (c == 'B') {
1718 /* body of a simple reply */
1719 status_code = (int)va_arg(argp, int);
1720 rc = snprintf(tempbuf, sizeof(tempbuf), "%s%d %s%s",
1721 "<html><body><h1>", status_code,
1722 http_get_code_text(status_code),
1723 "</h1></body></html>");
1724 if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf))
1725 goto error_handler;
1726 bignum = (off_t)strlen(tempbuf);
1727 if (http_MakeMessage(buf, http_major_version, http_minor_version,
1728 "NTcs", bignum, /* content-length */
1729 "text/html", /* content-type */
1730 tempbuf) != 0 /* body */
1731 )
1732 goto error_handler;
1733 } else if (c == 'Q') {
1734 /* request start line */
1735 /* GET /foo/bar.html HTTP/1.1\r\n */
1736 method = (http_method_t)va_arg(argp, int);
1737 method_str = method_to_str(method);
1738 url_str = (const char*)va_arg(argp, const char*);
1739 num = (size_t)va_arg(argp, size_t); /* length of url_str */
1740 if (http_MakeMessage(buf, http_major_version, http_minor_version,
1741 "ssbsdsdc", method_str, /* method */
1742 " ", url_str, num, /* url */
1743 " HTTP/", http_major_version, ".",
1744 http_minor_version) != 0)
1745 goto error_handler;
1746 } else if (c == 'q') {
1747 /* request start line and HOST header */
1748 method = (http_method_t)va_arg(argp, int);
1749 uri_ptr = (uri_type*)va_arg(argp, uri_type*);
1750 assert(uri_ptr);
1751 if (http_FixUrl(uri_ptr, &url) != 0) {
1752 error_code = UPNP_E_INVALID_URL;
1753 goto error_handler;
1754 }
1755 if (http_MakeMessage(buf, http_major_version, http_minor_version,
1756 "Q"
1757 "sbc",
1758 method, url.pathquery.buff, url.pathquery.size,
1759 "HOST: ", url.hostport.text.buff,
1760 url.hostport.text.size) != 0)
1761 goto error_handler;
1762 } else if (c == 'T') {
1763 /* content type header */
1764 temp_str = (const char*)va_arg(
1765 argp, const char*); /* type/subtype format */
1766 if (http_MakeMessage(buf, http_major_version, http_minor_version,
1767 "ssc", "CONTENT-TYPE: ", temp_str) != 0)
1768 goto error_handler;
1769 } else {
1770 assert(0);
1771 }
1772 }
1773 goto ExitFunction;
1774
1775error_handler:
1776 /* Default is out of memory error. */
1777 if (!error_code)
1778 error_code = UPNP_E_OUTOF_MEMORY;
1779 membuffer_destroy(buf);
1780
1781ExitFunction:
1782 va_end(argp);
1783 return error_code;
1784}
1785
1786void http_CalcResponseVersion(int request_major_vers, int request_minor_vers,
1787 int* response_major_vers,
1788 int* response_minor_vers) {
1789 if ((request_major_vers > 1) ||
1790 (request_major_vers == 1 && request_minor_vers >= 1)) {
1791 *response_major_vers = 1;
1792 *response_minor_vers = 1;
1793 } else {
1794 *response_major_vers = request_major_vers;
1795 *response_minor_vers = request_minor_vers;
1796 }
1797}
1798
1799
1800int http_OpenHttpGetEx(const char* url_str, void** Handle, char** contentType,
1801 int* contentLength, int* httpStatus, int lowRange,
1802 int highRange, int timeout) {
1803 int http_error_code;
1804 memptr ctype;
1805 SOCKET tcp_connection;
1806 size_t sockaddr_len;
1807 membuffer request;
1808 http_connection_handle_t* handle = NULL;
1809 uri_type url;
1810 parse_status_t status;
1811 int errCode = UPNP_E_SUCCESS;
1812 struct SendInstruction rangeBuf;
1813 int rc = 0;
1814
1815 membuffer_init(&request);
1816
1817 do {
1818 /* Checking Input parameters */
1819 if (!url_str || !Handle || !contentType || !httpStatus) {
1820 errCode = UPNP_E_INVALID_PARAM;
1821 break;
1822 }
1823 /* Initialize output parameters */
1824 *httpStatus = 0;
1825 *Handle = handle;
1826 *contentType = NULL;
1827 *contentLength = 0;
1828 if (lowRange > highRange) {
1829 errCode = UPNP_E_INTERNAL_ERROR;
1830 break;
1831 }
1832 memset(&rangeBuf, 0, sizeof(rangeBuf));
1833 rc = snprintf(rangeBuf.RangeHeader, sizeof(rangeBuf.RangeHeader),
1834 "Range: bytes=%d-%d\r\n", lowRange, highRange);
1835 if (rc < 0 || (unsigned int)rc >= sizeof(rangeBuf.RangeHeader))
1836 break;
1837 membuffer_init(&request);
1838 errCode = MakeGetMessageEx(url_str, &request, &url, &rangeBuf);
1839 if (errCode != UPNP_E_SUCCESS)
1840 break;
1841 handle =
1842 (http_connection_handle_t*)malloc(sizeof(http_connection_handle_t));
1843 if (!handle) {
1844 errCode = UPNP_E_OUTOF_MEMORY;
1845 break;
1846 }
1847 memset(handle, 0, sizeof(*handle));
1848 parser_response_init(&handle->response, HTTPMETHOD_GET);
1849 tcp_connection = umock::sys_socket_h.socket(
1850 (int)url.hostport.IPaddress.ss_family, SOCK_STREAM, 0);
1851 if (tcp_connection == INVALID_SOCKET) {
1852 errCode = UPNP_E_SOCKET_ERROR;
1853 free(handle);
1854 break;
1855 }
1856 if (sock_init(&handle->sock_info, tcp_connection) != UPNP_E_SUCCESS) {
1857 sock_destroy(&handle->sock_info, SD_BOTH);
1858 errCode = UPNP_E_SOCKET_ERROR;
1859 free(handle);
1860 break;
1861 }
1862 sockaddr_len = url.hostport.IPaddress.ss_family == AF_INET6
1863 ? sizeof(struct sockaddr_in6)
1864 : sizeof(struct sockaddr_in);
1865 errCode = umock::pupnp_httprw.private_connect(
1866 handle->sock_info.socket,
1867 (struct sockaddr*)&(url.hostport.IPaddress),
1868 (socklen_t)sockaddr_len);
1869 if (errCode == -1) {
1870 sock_destroy(&handle->sock_info, SD_BOTH);
1871 errCode = UPNP_E_SOCKET_CONNECT;
1872 free(handle);
1873 break;
1874 }
1875 /* send request */
1876 errCode = http_SendMessage(&handle->sock_info, &timeout, "b",
1877 request.buf, request.length);
1878 if (errCode != UPNP_E_SUCCESS) {
1879 sock_destroy(&handle->sock_info, SD_BOTH);
1880 free(handle);
1881 break;
1882 }
1883 if (ReadResponseLineAndHeaders(&handle->sock_info, &handle->response,
1884 &timeout,
1885 &http_error_code) != (int)PARSE_OK) {
1886 errCode = UPNP_E_BAD_RESPONSE;
1887 free(handle);
1888 break;
1889 }
1890 status = parser_get_entity_read_method(&handle->response);
1891 if (status != (parse_status_t)PARSE_CONTINUE_1 &&
1892 status != (parse_status_t)PARSE_SUCCESS) {
1893 errCode = UPNP_E_BAD_RESPONSE;
1894 free(handle);
1895 break;
1896 }
1897 *httpStatus = handle->response.msg.status_code;
1898 errCode = UPNP_E_SUCCESS;
1899
1900 if (!httpmsg_find_hdr(&handle->response.msg, HDR_CONTENT_TYPE, &ctype))
1901 /* no content-type */
1902 *contentType = NULL;
1903 else
1904 *contentType = ctype.buf;
1905 if (handle->response.position == (parser_pos_t)POS_COMPLETE)
1906 *contentLength = 0;
1907 else if (handle->response.ent_position == ENTREAD_USING_CHUNKED)
1908 *contentLength = UPNP_USING_CHUNKED;
1909 else if (handle->response.ent_position == ENTREAD_USING_CLEN)
1910 *contentLength = (int)handle->response.content_length;
1911 else if (handle->response.ent_position == ENTREAD_UNTIL_CLOSE)
1912 *contentLength = UPNP_UNTIL_CLOSE;
1913 *Handle = handle;
1914 } while (0);
1915
1916 membuffer_destroy(&request);
1917
1918 return errCode;
1919}
1920
1921/* 'info' should have a size of at least 100 bytes */
1922void get_sdk_info(char* info, size_t infoSize) {
1923 TRACE("Executing get_sdk_info().")
1924#ifdef UPNP_ENABLE_UNSPECIFIED_SERVER
1925 snprintf(info, infoSize, "Unspecified, UPnP/1.0, Unspecified\r\n");
1926#else /* UPNP_ENABLE_UNSPECIFIED_SERVER */
1927#ifdef _WIN32
1928 snprintf(info, infoSize,
1929 "UPnP/1.0, Portable SDK for UPnP devices/" PUPNP_VERSION
1930 " on windows\r\n");
1931#else
1932 struct utsname sys_info;
1933
1934 int ret_code = umock::sysinfo.uname(&sys_info);
1935 if (ret_code == -1)
1936 snprintf(info, infoSize,
1937 "Unspecified, UPnP/1.0, Portable SDK for UPnP "
1938 "devices/" PUPNP_VERSION "\r\n");
1939 else
1940 snprintf(info, infoSize,
1941 "%s/%s, UPnP/1.0, Portable SDK for UPnP "
1942 "devices/" PUPNP_VERSION "\r\n",
1943 sys_info.sysname, sys_info.release);
1944#endif
1945#endif /* UPNP_ENABLE_UNSPECIFIED_SERVER */
1946}
#define UPNP_UNTIL_CLOSE
Definition API.hpp:53
#define UPNP_E_OUTOF_BOUNDS
The operation completed successfully.
Definition API.hpp:355
#define LINE_SIZE
Definition API.hpp:45
#define UPNP_USING_CHUNKED
Definition API.hpp:52
int http_ReadHttpResponse(void *Handle, char *buf, size_t *size, int timeout)
Reads the content of a response using a connection previously created by UpnpOpenHttpConnection().
int http_OpenHttpConnection(const char *url_str, void **Handle, int timeout)
Opens a connection to the server.
int http_RequestAndResponse(uri_type *destination, const char *request, size_t request_length, http_method_t req_method, int timeout_secs, http_parser_t *response)
Initiates socket, connects to the remote host, sends a request and waits for the response from the re...
int http_CloseHttpConnection(void *Handle)
Closes the connection created with UpnpOpenHttpConnection() and frees any memory associated with the ...
int http_FixStrUrl(const char *urlstr, size_t urlstrlen, uri_type *fixed_url)
Parses URL and then validates URL.
int http_SendMessage(SOCKINFO *info, int *TimeOut, const char *fmt,...)
Sends a message to the destination based on the format parameter.
SOCKET http_Connect(uri_type *destination_url, uri_type *url)
Gets destination address from URL and then connects to the remote end.
void get_sdk_info(char *info, size_t infoSize)
Returns the server information for the operating system.
int http_Download(const char *url_str, int timeout_secs, char **document, size_t *doc_length, char *content_type)
Download the document message and extract the document from the message.
int http_FixUrl(uri_type *url, uri_type *fixed_url)
Validates URL.
int http_CancelHttpGet(void *Handle)
Set the cancel flag of the HttpGet handle.
int http_WriteHttpRequest(void *Handle, char *buf, size_t *size, int timeout)
Writes the content of a HTTP request initiated by a UpnpMakeHttpRequest() call. The end of the conten...
void http_CalcResponseVersion(int request_major_vers, int request_minor_vers, int *response_major_vers, int *response_minor_vers)
Calculate HTTP response versions based on the request versions.
tm * http_gmtime_r(const time_t *clock, tm *result)
Portable gmtime_r for Microsoft Windows.
static int private_connect(const SOCKET sockfd, const sockaddr *const serv_addr, const socklen_t addrlen)
Initiate a connection on a socket.
int http_EndHttpRequest(void *Handle, int timeout)
Indicates the end of a HTTP request previously made by UpnpMakeHttpRequest().
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_OpenHttpGetEx(const char *url_str, void **Handle, char **contentType, int *contentLength, int *httpStatus, int lowRange, int highRange, int timeout)
Makes the HTTP GET message, connects to the peer, sends the HTTP GET request, gets the response and p...
int http_GetHttpResponse(void *Handle, UpnpString *headers, char **contentType, int *contentLength, int *httpStatus, int timeout)
Gets the response from the server using a connection previously created by UpnpOpenHttpConnection().
int http_MakeHttpRequest(Upnp_HttpMethod method, const char *url_str, void *Handle, UpnpString *headers, const char *contentType, int contentLength, int timeout)
Makes a HTTP request using a connection previously created by UpnpOpenHttpConnection().
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...
int http_HttpGetProgress(void *Handle, size_t *length, size_t *total)
Extracts information from the Handle to the HTTP get object.
int http_MakeMessage(membuffer *buf, int http_major_version, int http_minor_version, const char *fmt,...)
Generate an HTTP message based on the format that is specified in the input parameters.
http_message_t msg
entire raw message
http_method_t
Method in a HTTP request.
memptr name
Header name as a string.
memptr entity
message body(entity).
membuffer value
Raw-value; could be multi-lined; min-length = 0.
membuffer msg
entire raw message.
int http_error_code
read-only; in case of parse error, this contains the HTTP error code (4XX or 5XX).
parser_pos_t position
Private data – don't touch.
parser_pos_t
Type of a parser position.
@ POS_COMPLETE
Position complete.
@ POS_ENTITY
Position entity.
parse_status_t
Status of parsing.
@ PARSE_INCOMPLETE
@ PARSE_NO_MATCH
@ PARSE_FAILURE
@ PARSE_INCOMPLETE_ENTITY
@ PARSE_SUCCESS
@ PARSE_OK
@ PARSE_CONTINUE_1
unsigned int content_length
Private data – don't touch.
#define ENTREAD_USING_CHUNKED
Defines read method.
#define HDR_CONTENT_TYPE
Type of a HTTP header.
#define ENTREAD_USING_CLEN
Defines read method.
#define ENTREAD_UNTIL_CLOSE
Defines read method.
Structure of an HTTP parser object.
Structure of an HTTP header object.
#define HTTP_DEFAULT_TIMEOUT
token scheme
Member variable.
Definition uri.hpp:96
hostport_type hostport
Member variable.
Definition uri.hpp:100
size_t size
Size of the buffer.
Definition uri.hpp:78
const char * buff
Buffer.
Definition uri.hpp:77
token pathquery
Member variable.
Definition uri.hpp:98
sockaddr_storage IPaddress
Network socket address.
Definition uri.hpp:86
constexpr int HTTP_SUCCESS
Yet another success code.
Definition uri.hpp:70
token text
Pointing to the full host:port string representation.
Definition uri.hpp:85
Represents a URI used in parse_uri and elsewhere.
Definition uri.hpp:92
const void * CorsHeader
Definition webserver.hpp:68
char AcceptLanguageHeader[200]
member variable
Definition webserver.hpp:60
char RangeHeader[200]
member variable
Definition webserver.hpp:59
Send instruction.
Definition webserver.hpp:50
ListNode * ListNext(LinkedList *list, ListNode *node)
Returns the next item in the list.
ListNode * ListHead(LinkedList *list)
Returns the head of 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.
void(* free_function)(void *arg)
Function for freeing list items.
Linked list node. Stores generic item and pointers to next and prev.
Linked list (no protection).
s_UpnpExtraHeaders
Header file for UpnpExtraHeaders methods.
PUPNP_Api const DOMString UpnpExtraHeaders_get_resp(const UpnpExtraHeaders *p)
Printf format for integers.
VDCallback_Read read
VDCallback_Open open
VDCallback_Close close
VDCallback_Seek seek
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
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:231
void load()
Load the raw socket file descriptor specified with the constructor into the object.
Definition socket.cpp:196
#define WEB_SERVER_CONTENT_LANGUAGE
This configuration parameter sets the value of the Content-Language header for the webserver....
Definition config.hpp:164
#define WEB_SERVER_BUF_SIZE
This configuration parameter sets the maximum buffer size for the webserver. The default value is 1MB...
Definition config.hpp:155
Internal implementation of the class UpnpString.
PUPNP_Api const char * UpnpString_get_String(const UpnpString *p)
Returns the pointer to char.
#define DOMString
The type of DOM strings.
Definition ixml.hpp:47
Upnp_HttpMethod
Different HTTP methods.
Definition API.hpp:1945
parse_status_t parser_parse_headers(http_parser_t *parser)
Get HTTP Method, URL location and version information.
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'.
parse_status_t parser_parse_entity(http_parser_t *parser)
Determines method to read entity.
void parser_response_init(http_parser_t *parser, http_method_t request_method)
Initializes parser object for a response.
parse_status_t parser_parse_responseline(http_parser_t *parser)
Get HTTP Method, URL location and version information.
void httpmsg_destroy(http_message_t *msg)
Free memory allocated for the http message.
parse_status_t parser_get_entity_read_method(http_parser_t *parser)
Determines method to read entity.
parse_status_t parser_append(http_parser_t *parser, const char *buf, size_t buf_length)
Append date to HTTP parser, and do the parsing.
const char * method_to_str(http_method_t method)
A wrapper function that maps a method id to a method.
PUPNP_Api UpnpListIter UpnpListEnd(UpnpListHead *list)
Return end of list sentinel iterator (not an element)
Definition list.cpp:58
PUPNP_Api UpnpListIter UpnpListBegin(UpnpListHead *list)
Return iterator pointing to the first list element, or UpnpListEnd(list) if the list is empty.
Definition list.cpp:51
PUPNP_Api UpnpListIter UpnpListNext(UpnpListHead *list, UpnpListIter pos)
Return iterator pointing to element after pos, or end()
Definition list.cpp:60
List anchor structure.
Definition list.hpp:56
void membuffer_destroy(membuffer *m)
Free's memory allocated for membuffer* m.
void membuffer_init(membuffer *m)
Wrapper to membuffer_initialize().
int membuffer_append(membuffer *m, const void *buf, size_t buf_len)
Invokes function to appends data from a constant buffer to the buffer.
char * membuffer_detach(membuffer *m)
Detaches current buffer and returns it. The caller must free the returned buffer using free()....
int membuffer_append_str(membuffer *m, const char *c_str)
Invokes function to appends data from a constant string to the buffer.
void membuffer_delete(membuffer *m, size_t index, size_t num_bytes)
Shrink the size of the buffer depending on the current size of the buffer and the input parameters....
size_t size_inc
used to increase size; MUST be > 0; (read/write).
Definition membuffer.hpp:69
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 (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_CONNECT
The SDK had a problem connecting to a remote host.
Definition messages.hpp:209
#define UPNP_E_SOCKET_ERROR
Generic socket error code for conditions not covered by other error codes.
Definition messages.hpp:243
#define UPNP_E_FILE_READ_ERROR
An error happened while reading a file.
Definition messages.hpp:298
#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_INVALID_URL
An URL passed into the function is invalid.
Definition messages.hpp:83
#define UPNP_E_SOCKET_WRITE
An error happened while writing to a socket.
Definition messages.hpp:179
#define UPNP_E_CANCELED
The operation was canceled.
Definition messages.hpp:252
#define UPNP_E_INVALID_PARAM
One or more of the parameters passed to the function is not valid.
Definition messages.hpp:40
#define UPNP_E_INTERNAL_ERROR
Generic error code for internal conditions not covered by other error codes.
Definition messages.hpp:319
#define UPNP_E_BAD_RESPONSE
The response received from the remote side of a connection is not correct for the protocol.
Definition messages.hpp:103
UPnPsdk_EXTERN bool g_dbug
Switch to enable verbose (debug) output.
Definition global.hpp:26
int MakeGetMessageEx(const char *url_str, membuffer *request, uri_type *url, SendInstruction *pRangeSpecifier)
Make extended GetMessage.
constexpr time_t DEFAULT_TCP_CONNECT_TIMEOUT
Default TCP connection timeout.
int Check_Connect_And_Wait_Connection(const SOCKET a_sock, const int connect_res)
Checks socket connection and wait if it is not connected.
void copy_msg_headers(LinkedList *msgHeaders, UpnpString *headers)
Dummy function. It do nothing since years. Do we need it?
int MakeGenericMessage(http_method_t method, const char *url_str, membuffer *request, uri_type *url, int contentLength, const char *contentType, const UpnpString *headers)
Make a generic message, what ever this mean.
int get_hoststr(const char *url_str, const char **hoststr, size_t *hostlen)
Point to the hostname within a URL C-string.
int ReadResponseLineAndHeaders(SOCKINFO *info, http_parser_t *parser, int *timeout_secs, int *http_error_code)
Parses already exiting data.
int sock_ssl_connect(SOCKINFO *info)
Associates an SSL object with the socket and begins the client-side SSL/TLS handshake.
Definition sock.cpp:469
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_read(SOCKINFO *info, char *buffer, size_t bufsize, int *timeoutSecs)
Reads data on socket in sockinfo.
Definition sock.cpp:525
int sock_write(SOCKINFO *info, const char *buffer, size_t bufsize, int *timeoutSecs)
Writes data on the socket in sockinfo.
Definition sock.cpp:541
int sock_init(SOCKINFO *info, SOCKET sockfd)
Assign the passed in socket descriptor to socket descriptor in the SOCKINFO structure.
Definition sock.cpp:440
SOCKET socket
Handle/descriptor to a socket.
Definition sock.hpp:67
Additional socket information for connections and ssl.
Definition sock.hpp:65
Declaration of the Sockaddr class and some free helper functions.
const char * http_get_code_text(int statusCode)
Return the right status message based on the passed in int statusCode input parameter.
HTTP status codes.
Trivial ::sockaddr structures enhanced with methods.
Definition sockaddr.hpp:94
const std::string netaddrp() noexcept
Get the assosiated netaddress with port.
Definition sockaddr.cpp:519
Define macro for synced logging to the console for detailed info and debug.
struct VirtualDirCallbacks virtualDirCallback
This structure is for virtual directory callbacks.
Definition upnpapi.cpp:247
size_t g_maxContentLength
Maximum content-length (in bytes) that the SDK will process on an incoming packet.
Definition upnpapi.cpp:327
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.
int token_string_casecmp(token *in1, const char *in2)
Compares buffer in the token object with the buffer in in2 case insensitive.
Definition uri.cpp:510
int parse_uri(const char *in, size_t max, uri_type *out)
Parses a uri as defined in RFC 2396 (explaining URIs).
Definition uri.cpp:733