1 /*******************************************************************************
2 *
3 * Copyright (c) 2000-2003 Intel Corporation
4 * All rights reserved.
5 * Copyright (c) 2012 France Telecom All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * - Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 * - Neither name of Intel Corporation nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 ******************************************************************************/
32
33 /*!
34 * \file
35 *
36 * Purpose: This file defines the functionality making use of the http.
37 * It defines functions to receive messages, process messages, send messages.
38 */
39
40 #include "config.h"
41
42 #include "httpreadwrite.h"
43
44 #include "UpnpExtraHeaders.h"
45 #include "UpnpFileInfo.h"
46 #include "UpnpInet.h"
47 #include "UpnpIntTypes.h"
48 #include "UpnpStdInt.h"
49 #include "membuffer.h"
50 #include "sock.h"
51 #include "statcodes.h"
52 #include "unixutil.h"
53 #include "upnp.h"
54 #include "upnpapi.h"
55 #include "uri.h"
56 #include "webserver.h"
57
58 #include <assert.h>
59 #include <stdarg.h>
60 #include <string.h>
61
62 #ifdef _WIN32
63 #include <malloc.h>
64 #define fseeko fseek
65 #if defined(_MSC_VER) && _MSC_VER < 1900
66 #define snprintf _snprintf
67 #endif
68 #else /* _WIN32 */
69 #include <arpa/inet.h>
70 #include <sys/time.h>
71 #include <sys/types.h>
72 #include <sys/utsname.h>
73 #include <sys/wait.h>
74 #endif /* _WIN32 */
75
76 /*
77 * Please, do not change these to const int while MSVC cannot understand
78 * const int in array dimensions.
79 */
80 /*
81 const int CHUNK_HEADER_SIZE = 10;
82 const int CHUNK_TAIL_SIZE = 10;
83 */
84 #define CHUNK_HEADER_SIZE (size_t)10
85 #define CHUNK_TAIL_SIZE (size_t)10
86
87 #ifndef UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS
88
89 /* in seconds */
90 #define DEFAULT_TCP_CONNECT_TIMEOUT 5
91
92 /*!
93 * \brief Checks socket connection and wait if it is not connected.
94 * It should be called just after connect.
95 *
96 * \return 0 if successful, else -1.
97 */
Check_Connect_And_Wait_Connection(SOCKET sock,int connect_res)98 static int Check_Connect_And_Wait_Connection(
99 /*! [in] socket. */
100 SOCKET sock,
101 /*! [in] result of connect. */
102 int connect_res)
103 {
104 struct timeval tmvTimeout = {DEFAULT_TCP_CONNECT_TIMEOUT, 0};
105 int result;
106 #ifdef _WIN32
107 struct fd_set fdSet;
108 #else
109 fd_set fdSet;
110 #endif
111 FD_ZERO(&fdSet);
112 FD_SET(sock, &fdSet);
113
114 if (connect_res < 0) {
115 #ifdef _WIN32
116 if (WSAEWOULDBLOCK == WSAGetLastError()) {
117 #else
118 if (EINPROGRESS == errno) {
119 #endif
120 result = select(
121 sock + 1, NULL, &fdSet, NULL, &tmvTimeout);
122 if (result < 0) {
123 #ifdef _WIN32
124 /* WSAGetLastError(); */
125 #else
126 /* errno */
127 #endif
128 return -1;
129 } else if (result == 0) {
130 /* timeout */
131 return -1;
132 #ifndef _WIN32
133 } else {
134 int valopt = 0;
135 socklen_t len = sizeof(valopt);
136 if (getsockopt(sock,
137 SOL_SOCKET,
138 SO_ERROR,
139 (void *)&valopt,
140 &len) < 0) {
141 /* failed to read delayed error */
142 return -1;
143 } else if (valopt) {
144 /* delayed error = valopt */
145 return -1;
146 }
147 #endif
148 }
149 }
150 }
151
152 return 0;
153 }
154 #endif /* UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS */
155
156 static int private_connect(
157 SOCKET sockfd, const struct sockaddr *serv_addr, socklen_t addrlen)
158 {
159 #ifndef UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS
160 int ret = sock_make_no_blocking(sockfd);
161 if (ret != -1) {
162 ret = connect(sockfd, serv_addr, addrlen);
163 ret = Check_Connect_And_Wait_Connection(sockfd, ret);
164 if (ret != -1) {
165 ret = sock_make_blocking(sockfd);
166 }
167 }
168
169 return ret;
170 #else
171 return connect(sockfd, serv_addr, addrlen);
172 #endif /* UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS */
173 }
174
175 #ifdef _WIN32
176 struct tm *http_gmtime_r(const time_t *clock, struct tm *result)
177 {
178 if (clock == NULL || *clock < 0 || result == NULL)
179 return NULL;
180
181 /* gmtime in VC runtime is thread safe. */
182 *result = *gmtime(clock);
183 return result;
184 }
185 #endif
186
187 static int get_hoststr(
188 const char *url_str, const char **hoststr, size_t *hostlen)
189 {
190 const char *start;
191 const char *finish;
192 int ret_code = UPNP_E_INVALID_URL;
193
194 start = strstr(url_str, "//");
195 if (!start) {
196 goto end_function;
197 }
198 start += 2;
199 finish = strchr(start, '/');
200 if (finish) {
201 *hostlen = (size_t)(finish - start);
202 } else {
203 *hostlen = strlen(start);
204 }
205 *hoststr = start;
206
207 ret_code = UPNP_E_SUCCESS;
208
209 end_function:
210 return ret_code;
211 }
212
213 static void copy_msg_headers(LinkedList *msgHeaders, UpnpString *headers)
214 {
215 (void)msgHeaders;
216 (void)headers;
217 return;
218 /* TODO: */
219 #if 0
220 ListNode *node;
221 UpnpHttpHeader *header;
222 http_header_t *msgHeader;
223 if (headers) {
224 ListInit(headers, NULL, (free_function) UpnpHttpHeader_delete);
225 node = ListHead(msgHeaders);
226 while(node) {
227 msgHeader = (http_header_t*) node->item;
228 header = UpnpHttpHeader_new();
229 UpnpHttpHeader_strncpy_Name(
230 header,
231 msgHeader->name.buf,
232 msgHeader->name.length);
233 UpnpHttpHeader_strncpy_Value(
234 header,
235 msgHeader->value.buf,
236 msgHeader->value.length);
237 node = ListNext(msgHeaders, node);
238 }
239 }
240 #endif
241 }
242
243 int http_FixUrl(uri_type *url, uri_type *fixed_url)
244 {
245 const char *temp_path = "/";
246
247 *fixed_url = *url;
248 #ifdef UPNP_ENABLE_OPEN_SSL
249 if (token_string_casecmp(&fixed_url->scheme, "http") != 0 &&
250 token_string_casecmp(&fixed_url->scheme, "https") != 0) {
251 return UPNP_E_INVALID_URL;
252 }
253 #else
254 if (token_string_casecmp(&fixed_url->scheme, "http") != 0) {
255 return UPNP_E_INVALID_URL;
256 }
257 #endif
258 if (fixed_url->hostport.text.size == (size_t)0) {
259 return UPNP_E_INVALID_URL;
260 }
261 /* set pathquery to "/" if it is empty */
262 if (fixed_url->pathquery.size == (size_t)0) {
263 fixed_url->pathquery.buff = temp_path;
264 fixed_url->pathquery.size = (size_t)1;
265 }
266
267 return UPNP_E_SUCCESS;
268 }
269
270 int http_FixStrUrl(const char *urlstr, size_t urlstrlen, uri_type *fixed_url)
271 {
272 uri_type url;
273
274 if (parse_uri(urlstr, urlstrlen, &url) != HTTP_SUCCESS) {
275 return UPNP_E_INVALID_URL;
276 }
277
278 return http_FixUrl(&url, fixed_url);
279 }
280
281 /************************************************************************
282 * Function: http_Connect
283 *
284 * Parameters:
285 * IN uri_type* destination_url; URL containing destination information
286 * OUT uri_type *url; Fixed and corrected URL
287 *
288 * Description:
289 * Gets destination address from URL and then connects to the remote end
290 *
291 * Returns:
292 * socket descriptor on success
293 * UPNP_E_OUTOF_SOCKET
294 * UPNP_E_SOCKET_CONNECT on error
295 ************************************************************************/
296 SOCKET http_Connect(uri_type *destination_url, uri_type *url)
297 {
298 SOCKET connfd;
299 socklen_t sockaddr_len;
300 int ret_connect;
301 char errorBuffer[ERROR_BUFFER_LEN];
302
303 http_FixUrl(destination_url, url);
304
305 connfd = socket((int)url->hostport.IPaddress.ss_family, SOCK_STREAM, 0);
306 if (connfd == INVALID_SOCKET) {
307 return (SOCKET)(UPNP_E_OUTOF_SOCKET);
308 }
309 sockaddr_len = (socklen_t)(url->hostport.IPaddress.ss_family == AF_INET6
310 ? sizeof(struct sockaddr_in6)
311 : sizeof(struct sockaddr_in));
312 ret_connect = private_connect(connfd,
313 (struct sockaddr *)&url->hostport.IPaddress,
314 sockaddr_len);
315 if (ret_connect == -1) {
316 #ifdef _WIN32
317 UpnpPrintf(UPNP_CRITICAL,
318 HTTP,
319 __FILE__,
320 __LINE__,
321 "connect error: %d\n",
322 WSAGetLastError());
323 #endif
324 if (shutdown(connfd, SD_BOTH) == -1) {
325 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
326 UpnpPrintf(UPNP_INFO,
327 HTTP,
328 __FILE__,
329 __LINE__,
330 "Error in shutdown: %s\n",
331 errorBuffer);
332 }
333 UpnpCloseSocket(connfd);
334 return (SOCKET)(UPNP_E_SOCKET_CONNECT);
335 }
336
337 return connfd;
338 }
339
340 /*!
341 * \brief Get the data on the socket and take actions based on the read data to
342 * modify the parser objects buffer.
343 *
344 * If an error is reported while parsing the data, the error code is passed in
345 * the http_errr_code parameter.
346 *
347 * Parameters:
348 * IN SOCKINFO *info; Socket information object
349 * OUT http_parser_t* parser; HTTP parser object
350 * IN http_method_t request_method; HTTP request method
351 * IN OUT int* timeout_secs; time out
352 * OUT int* http_error_code; HTTP error code returned
353 *
354 * \return
355 * UPNP_E_SUCCESS
356 * UPNP_E_BAD_HTTPMSG
357 */
358 int http_RecvMessage(SOCKINFO *info,
359 http_parser_t *parser,
360 http_method_t request_method,
361 int *timeout_secs,
362 int *http_error_code)
363 {
364 int ret = UPNP_E_SUCCESS;
365 int line = 0;
366 parse_status_t status;
367 int num_read;
368 int ok_on_close = 0;
369 char *buf;
370 size_t buf_len = 1024;
371
372 *http_error_code = HTTP_INTERNAL_SERVER_ERROR;
373 buf = malloc(buf_len);
374 if (!buf) {
375 ret = UPNP_E_OUTOF_MEMORY;
376 goto ExitFunction;
377 }
378 if (request_method == (http_method_t)HTTPMETHOD_UNKNOWN) {
379 parser_request_init(parser);
380 } else {
381 parser_response_init(parser, request_method);
382 }
383
384 while (1) {
385 /* Double the bet */
386 free(buf);
387 buf_len = 2 * buf_len;
388 buf = malloc(buf_len);
389 if (!buf) {
390 ret = UPNP_E_OUTOF_MEMORY;
391 goto ExitFunction;
392 }
393 num_read = sock_read(info, buf, buf_len, timeout_secs);
394 if (num_read > 0) {
395 /* got data */
396 status = parser_append(parser, buf, (size_t)num_read);
397 switch (status) {
398 case PARSE_SUCCESS:
399 UpnpPrintf(UPNP_INFO,
400 HTTP,
401 __FILE__,
402 __LINE__,
403 "<<< (RECVD) "
404 "<<<\n%s\n-----------------\n",
405 parser->msg.msg.buf);
406 print_http_headers(&parser->msg);
407 if (g_maxContentLength > 0 &&
408 parser->content_length >
409 (unsigned int)
410 g_maxContentLength) {
411 *http_error_code =
412 HTTP_REQ_ENTITY_TOO_LARGE;
413 line = __LINE__;
414 ret = UPNP_E_OUTOF_BOUNDS;
415 goto ExitFunction;
416 }
417 line = __LINE__;
418 ret = 0;
419 goto ExitFunction;
420 case PARSE_FAILURE:
421 case PARSE_NO_MATCH:
422 *http_error_code = parser->http_error_code;
423 line = __LINE__;
424 ret = UPNP_E_BAD_HTTPMSG;
425 goto ExitFunction;
426 case PARSE_INCOMPLETE_ENTITY:
427 /* read until close */
428 ok_on_close = 1;
429 break;
430 case PARSE_CONTINUE_1:
431 /* Web post request. */
432 line = __LINE__;
433 ret = PARSE_SUCCESS;
434 goto ExitFunction;
435 default:
436 break;
437 }
438 } else if (num_read == 0) {
439 if (ok_on_close) {
440 UpnpPrintf(UPNP_INFO,
441 HTTP,
442 __FILE__,
443 __LINE__,
444 "<<< (RECVD) "
445 "<<<\n%s\n-----------------\n",
446 parser->msg.msg.buf);
447 print_http_headers(&parser->msg);
448 line = __LINE__;
449 ret = 0;
450 goto ExitFunction;
451 } else {
452 /* partial msg */
453 *http_error_code =
454 HTTP_BAD_REQUEST; /* or response */
455 line = __LINE__;
456 ret = UPNP_E_BAD_HTTPMSG;
457 goto ExitFunction;
458 }
459 } else {
460 *http_error_code = parser->http_error_code;
461 line = __LINE__;
462 ret = num_read;
463 goto ExitFunction;
464 }
465 }
466
467 ExitFunction:
468 free(buf);
469 if (ret != UPNP_E_SUCCESS) {
470 UpnpPrintf(UPNP_ALL,
471 HTTP,
472 __FILE__,
473 line,
474 "(http_RecvMessage): Error %d, http_error_code = %d.\n",
475 ret,
476 *http_error_code);
477 }
478
479 return ret;
480 }
481
482 int http_SendMessage(SOCKINFO *info, int *TimeOut, const char *fmt, ...)
483 {
484 #if EXCLUDE_WEB_SERVER == 0
485 FILE *Fp;
486 struct SendInstruction *Instr = NULL;
487 char *filename = NULL;
488 char *file_buf = NULL;
489 char *ChunkBuf = NULL;
490 /* 10 byte allocated for chunk header. */
491 char Chunk_Header[CHUNK_HEADER_SIZE];
492 size_t num_read;
493 off_t amount_to_be_read = 0;
494 size_t Data_Buf_Size = WEB_SERVER_BUF_SIZE;
495 #endif /* EXCLUDE_WEB_SERVER */
496 va_list argp;
497 char *buf = NULL;
498 char c;
499 int nw;
500 int RetVal = 0;
501 size_t buf_length;
502 size_t num_written;
503 int I_fmt_processed = 0;
504
505 #if EXCLUDE_WEB_SERVER == 0
506 memset(Chunk_Header, 0, sizeof(Chunk_Header));
507 #endif /* EXCLUDE_WEB_SERVER */
508 va_start(argp, fmt);
509 while ((c = *fmt++)) {
510 #if EXCLUDE_WEB_SERVER == 0
511 if (c == 'I' && !I_fmt_processed) {
512 I_fmt_processed = 1;
513 Instr = va_arg(argp, struct SendInstruction *);
514 if (Instr->ReadSendSize >= 0)
515 amount_to_be_read = Instr->ReadSendSize;
516 else
517 amount_to_be_read = (off_t)Data_Buf_Size;
518 if (amount_to_be_read < (off_t)WEB_SERVER_BUF_SIZE)
519 Data_Buf_Size = (size_t)amount_to_be_read;
520 ChunkBuf = malloc(
521 (size_t)(Data_Buf_Size + CHUNK_HEADER_SIZE +
522 CHUNK_TAIL_SIZE));
523 if (!ChunkBuf) {
524 RetVal = UPNP_E_OUTOF_MEMORY;
525 goto ExitFunction;
526 }
527 file_buf = ChunkBuf + CHUNK_HEADER_SIZE;
528 } else if (c == 'f') {
529 /* file name */
530 filename = va_arg(argp, char *);
531 if (Instr && Instr->IsVirtualFile)
532 Fp = (virtualDirCallback.open)(filename,
533 UPNP_READ,
534 Instr->Cookie,
535 Instr->RequestCookie);
536 else
537 Fp = fopen(filename, "rb");
538 if (Fp == NULL) {
539 RetVal = UPNP_E_FILE_READ_ERROR;
540 goto ExitFunction;
541 }
542 if (Instr && Instr->IsRangeActive &&
543 Instr->IsVirtualFile) {
544 if (virtualDirCallback.seek(Fp,
545 Instr->RangeOffset,
546 SEEK_CUR,
547 Instr->Cookie,
548 Instr->RequestCookie) != 0) {
549 RetVal = UPNP_E_FILE_READ_ERROR;
550 goto Cleanup_File;
551 }
552 } else if (Instr && Instr->IsRangeActive) {
553 if (fseeko(Fp, Instr->RangeOffset, SEEK_CUR) !=
554 0) {
555 RetVal = UPNP_E_FILE_READ_ERROR;
556 goto Cleanup_File;
557 }
558 }
559 while (amount_to_be_read) {
560 if (Instr) {
561 int nr;
562 size_t n =
563 amount_to_be_read >=
564 (off_t)Data_Buf_Size
565 ? Data_Buf_Size
566 : (size_t)amount_to_be_read;
567 if (Instr->IsVirtualFile) {
568 nr = virtualDirCallback.read(Fp,
569 file_buf,
570 n,
571 Instr->Cookie,
572 Instr->RequestCookie);
573 num_read = (size_t)nr;
574 } else {
575 num_read = fread(file_buf,
576 (size_t)1,
577 n,
578 Fp);
579 }
580 amount_to_be_read -= (off_t)num_read;
581 if (Instr->ReadSendSize < 0) {
582 /* read until close */
583 amount_to_be_read =
584 (off_t)Data_Buf_Size;
585 }
586 } else {
587 num_read = fread(file_buf,
588 (size_t)1,
589 Data_Buf_Size,
590 Fp);
591 }
592 if (num_read == (size_t)0) {
593 /* EOF so no more to send. */
594 if (Instr && Instr->IsChunkActive) {
595 const char *str = "0\r\n\r\n";
596 nw = sock_write(info,
597 str,
598 strlen(str),
599 TimeOut);
600 } else {
601 RetVal = UPNP_E_FILE_READ_ERROR;
602 }
603 goto Cleanup_File;
604 }
605 /* Create chunk for the current buffer. */
606 if (Instr && Instr->IsChunkActive) {
607 int rc;
608 /* Copy CRLF at the end of the chunk */
609 memcpy(file_buf + num_read,
610 "\r\n",
611 (size_t)2);
612 /* Hex length for the chunk size. */
613 memset(Chunk_Header,
614 0,
615 sizeof(Chunk_Header));
616 rc = snprintf(Chunk_Header,
617 sizeof(Chunk_Header),
618 "%" PRIzx "\r\n",
619 num_read);
620 if (rc < 0 ||
621 (unsigned int)rc >=
622 sizeof(Chunk_Header)) {
623 RetVal = UPNP_E_INTERNAL_ERROR;
624 goto Cleanup_File;
625 }
626 /* Copy the chunk size header */
627 memcpy(file_buf - strlen(Chunk_Header),
628 Chunk_Header,
629 strlen(Chunk_Header));
630 /* on the top of the buffer. */
631 /*file_buf[num_read+strlen(Chunk_Header)]
632 * = NULL; */
633 /*upnpprintf("Sending
634 * %s\n",file_buf-strlen(Chunk_Header));*/
635 nw = sock_write(info,
636 file_buf - strlen(Chunk_Header),
637 num_read +
638 strlen(Chunk_Header) +
639 (size_t)2,
640 TimeOut);
641 num_written = (size_t)nw;
642 if (nw <= 0 ||
643 num_written !=
644 num_read +
645 strlen(Chunk_Header) +
646 (size_t)2)
647 /* Send error nothing we can do.
648 */
649 goto Cleanup_File;
650 } else {
651 /* write data */
652 nw = sock_write(info,
653 file_buf,
654 num_read,
655 TimeOut);
656 UpnpPrintf(UPNP_INFO,
657 HTTP,
658 __FILE__,
659 __LINE__,
660 ">>> (SENT) "
661 ">>>\n%.*s\n------------\n",
662 nw,
663 file_buf);
664 /* Send error nothing we can do */
665 num_written = (size_t)nw;
666 if (nw <= 0 ||
667 num_written != num_read) {
668 goto Cleanup_File;
669 }
670 }
671 } /* while */
672 Cleanup_File:
673 if (Instr && Instr->IsVirtualFile) {
674 virtualDirCallback.close(Fp,
675 Instr->Cookie,
676 Instr->RequestCookie);
677 } else {
678 fclose(Fp);
679 }
680 goto ExitFunction;
681 } else
682 #endif /* EXCLUDE_WEB_SERVER */
683 if (c == 'b') {
684 /* memory buffer */
685 buf = va_arg(argp, char *);
686 buf_length = va_arg(argp, size_t);
687 if (buf_length > (size_t)0) {
688 nw = sock_write(info, buf, buf_length, TimeOut);
689 num_written = (size_t)nw;
690 UpnpPrintf(UPNP_INFO,
691 HTTP,
692 __FILE__,
693 __LINE__,
694 ">>> (SENT) >>>\n"
695 "%.*s\nbuf_length=%" PRIzd
696 ", num_written=%" PRIzd "\n"
697 "------------\n",
698 (int)buf_length,
699 buf,
700 buf_length,
701 num_written);
702 if (num_written != buf_length) {
703 RetVal = 0;
704 goto ExitFunction;
705 }
706 }
707 }
708 }
709
710 ExitFunction:
711 va_end(argp);
712 #if EXCLUDE_WEB_SERVER == 0
713 free(ChunkBuf);
714 #endif /* EXCLUDE_WEB_SERVER */
715 return RetVal;
716 }
717
718 /************************************************************************
719 * Function: http_RequestAndResponse
720 *
721 * Parameters:
722 * IN uri_type* destination; Destination URI object which contains
723 * remote IP address among other elements
724 * IN const char* request; Request to be sent
725 * IN size_t request_length; Length of the request
726 * IN http_method_t req_method; HTTP Request method
727 * IN int timeout_secs; time out value
728 * OUT http_parser_t* response; Parser object to receive the repsonse
729 *
730 * Description:
731 * Initiates socket, connects to the destination, sends a
732 * request and waits for the response from the remote end
733 *
734 * Returns:
735 * UPNP_E_SOCKET_ERROR
736 * UPNP_E_SOCKET_CONNECT
737 * Error Codes returned by http_SendMessage
738 * Error Codes returned by http_RecvMessage
739 ************************************************************************/
740 int http_RequestAndResponse(uri_type *destination,
741 const char *request,
742 size_t request_length,
743 http_method_t req_method,
744 int timeout_secs,
745 http_parser_t *response)
746 {
747 SOCKET tcp_connection;
748 int ret_code;
749 size_t sockaddr_len;
750 int http_error_code;
751 SOCKINFO info;
752
753 tcp_connection = socket(
754 (int)destination->hostport.IPaddress.ss_family, SOCK_STREAM, 0);
755 if (tcp_connection == INVALID_SOCKET) {
756 parser_response_init(response, req_method);
757 return UPNP_E_SOCKET_ERROR;
758 }
759 if (sock_init(&info, tcp_connection) != UPNP_E_SUCCESS) {
760 parser_response_init(response, req_method);
761 ret_code = UPNP_E_SOCKET_ERROR;
762 goto end_function;
763 }
764 /* connect */
765 sockaddr_len = destination->hostport.IPaddress.ss_family == AF_INET6
766 ? sizeof(struct sockaddr_in6)
767 : sizeof(struct sockaddr_in);
768 ret_code = private_connect(info.socket,
769 (struct sockaddr *)&(destination->hostport.IPaddress),
770 (socklen_t)sockaddr_len);
771 if (ret_code == -1) {
772 parser_response_init(response, req_method);
773 ret_code = UPNP_E_SOCKET_CONNECT;
774 goto end_function;
775 }
776 /* send request */
777 ret_code = http_SendMessage(
778 &info, &timeout_secs, "b", request, request_length);
779 if (ret_code != 0) {
780 parser_response_init(response, req_method);
781 goto end_function;
782 }
783 /* recv response */
784 ret_code = http_RecvMessage(
785 &info, response, req_method, &timeout_secs, &http_error_code);
786
787 end_function:
788 /* should shutdown completely */
789 sock_destroy(&info, SD_BOTH);
790
791 return ret_code;
792 }
793
794 /************************************************************************
795 * Function: http_Download
796 *
797 * Parameters:
798 * IN const char* url_str; String as a URL
799 * IN int timeout_secs; time out value
800 * OUT char** document; buffer to store the document extracted
801 * from the donloaded message.
802 * OUT int* doc_length; length of the extracted document
803 * OUT char* content_type; Type of content
804 *
805 * Description:
806 * Download the document message and extract the document
807 * from the message.
808 *
809 * Return: int
810 * UPNP_E_SUCCESS
811 * UPNP_E_INVALID_URL
812 ************************************************************************/
813 int http_Download(const char *url_str,
814 int timeout_secs,
815 char **document,
816 size_t *doc_length,
817 char *content_type)
818 {
819 int ret_code;
820 uri_type url;
821 char *msg_start;
822 char *entity_start;
823 const char *hoststr;
824 http_parser_t response;
825 size_t msg_length;
826 size_t hostlen;
827 memptr ctype;
828 size_t copy_len;
829 membuffer request;
830 size_t url_str_len;
831
832 url_str_len = strlen(url_str);
833 /*ret_code = parse_uri( (char*)url_str, url_str_len, &url ); */
834 UpnpPrintf(UPNP_INFO,
835 HTTP,
836 __FILE__,
837 __LINE__,
838 "DOWNLOAD URL : %s\n",
839 url_str);
840 ret_code = http_FixStrUrl((char *)url_str, url_str_len, &url);
841 if (ret_code != UPNP_E_SUCCESS) {
842 return ret_code;
843 }
844 /* make msg */
845 membuffer_init(&request);
846 ret_code = get_hoststr(url_str, &hoststr, &hostlen);
847 if (ret_code != UPNP_E_SUCCESS) {
848 return ret_code;
849 }
850 UpnpPrintf(UPNP_INFO,
851 HTTP,
852 __FILE__,
853 __LINE__,
854 "HOSTNAME : %s Length : %" PRIzu "\n",
855 hoststr,
856 hostlen);
857 ret_code = http_MakeMessage(&request,
858 1,
859 1,
860 "Q"
861 "s"
862 "bcDCUc",
863 HTTPMETHOD_GET,
864 url.pathquery.buff,
865 url.pathquery.size,
866 "HOST: ",
867 hoststr,
868 hostlen);
869 if (ret_code != 0) {
870 UpnpPrintf(UPNP_INFO,
871 HTTP,
872 __FILE__,
873 __LINE__,
874 "HTTP Makemessage failed\n");
875 membuffer_destroy(&request);
876 return ret_code;
877 }
878 UpnpPrintf(UPNP_INFO,
879 HTTP,
880 __FILE__,
881 __LINE__,
882 "HTTP Buffer:\n%s\n"
883 "----------END--------\n",
884 request.buf);
885 /* get doc msg */
886 ret_code = http_RequestAndResponse(&url,
887 request.buf,
888 request.length,
889 HTTPMETHOD_GET,
890 timeout_secs,
891 &response);
892
893 if (ret_code != 0) {
894 httpmsg_destroy(&response.msg);
895 membuffer_destroy(&request);
896 return ret_code;
897 }
898 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__, "Response\n");
899 print_http_headers(&response.msg);
900 /* optional content-type */
901 if (content_type) {
902 if (httpmsg_find_hdr(&response.msg, HDR_CONTENT_TYPE, &ctype) ==
903 NULL) {
904 *content_type = '\0'; /* no content-type */
905 } else {
906 /* safety */
907 copy_len = ctype.length < LINE_SIZE - (size_t)1
908 ? ctype.length
909 : LINE_SIZE - (size_t)1;
910
911 memcpy(content_type, ctype.buf, copy_len);
912 content_type[copy_len] = '\0';
913 }
914 }
915 /* extract doc from msg */
916 if ((*doc_length = response.msg.entity.length) == (size_t)0) {
917 /* 0-length msg */
918 *document = NULL;
919 } else if (response.msg.status_code == HTTP_OK) {
920 /*LEAK_FIX_MK */
921 /* copy entity */
922 entity_start = response.msg.entity.buf; /* what we want */
923 msg_length = response.msg.msg.length; /* save for posterity */
924 msg_start = membuffer_detach(&response.msg.msg); /* whole msg */
925 /* move entity to the start; copy null-terminator too */
926 memmove(msg_start, entity_start, *doc_length + (size_t)1);
927 /* save mem for body only */
928 *document = realloc(
929 msg_start, *doc_length + (size_t)1); /*LEAK_FIX_MK */
930 /* *document = Realloc( msg_start,msg_length, *doc_length + 1 );
931 * LEAK_FIX_MK */
932 /* shrink can't fail */
933 assert(msg_length > *doc_length);
934 assert(*document != NULL);
935 if (msg_length <= *doc_length || *document == NULL)
936 UpnpPrintf(UPNP_INFO,
937 HTTP,
938 __FILE__,
939 __LINE__,
940 "msg_length(%" PRIzu ") <= *doc_length(%" PRIzu
941 ") or document is NULL",
942 msg_length,
943 *doc_length);
944 }
945 if (response.msg.status_code == HTTP_OK) {
946 ret_code = 0; /* success */
947 } else {
948 /* server sent error msg (not requested doc) */
949 ret_code = response.msg.status_code;
950 }
951 httpmsg_destroy(&response.msg);
952 membuffer_destroy(&request);
953
954 return ret_code;
955 }
956
957 /************************************************************************
958 * Function: MakeGenericMessage
959 *
960 * Parameters:
961 * http_method_t method; The type of HTTP method.
962 * const char *url_str; String as a URL
963 * membuffer *request; Buffer containing the request
964 * uri_type *url; URI object containing the scheme,
965 * path query token, etc.
966 * int contentLength; length of content
967 * const char *contentType; Type of content
968 *
969 * Description:
970 * Makes the message for the HTTP POST message
971 *
972 * Returns:
973 * UPNP_E_INVALID_URL
974 * UPNP_E_INVALID_PARAM
975 * UPNP_E_SUCCESS
976 ************************************************************************/
977 int MakeGenericMessage(http_method_t method,
978 const char *url_str,
979 membuffer *request,
980 uri_type *url,
981 int contentLength,
982 const char *contentType,
983 const UpnpString *headers)
984 {
985 int ret_code = 0;
986 size_t hostlen = 0;
987 const char *hoststr;
988
989 UpnpPrintf(UPNP_INFO,
990 HTTP,
991 __FILE__,
992 __LINE__,
993 "URL: %s method: %d\n",
994 url_str,
995 method);
996 ret_code = http_FixStrUrl(url_str, strlen(url_str), url);
997 if (ret_code != UPNP_E_SUCCESS)
998 return ret_code;
999 /* make msg */
1000 membuffer_init(request);
1001 ret_code = http_MakeMessage(request,
1002 1,
1003 1,
1004 "Q",
1005 method,
1006 url->pathquery.buff,
1007 url->pathquery.size);
1008 /* add request headers if specified, otherwise use default headers */
1009 if (ret_code == 0) {
1010 if (headers) {
1011 ret_code = http_MakeMessage(request,
1012 1,
1013 1,
1014 "s",
1015 UpnpString_get_String(headers));
1016 } else {
1017 ret_code = get_hoststr(url_str, &hoststr, &hostlen);
1018 if (ret_code != UPNP_E_SUCCESS)
1019 return ret_code;
1020 UpnpPrintf(UPNP_INFO,
1021 HTTP,
1022 __FILE__,
1023 __LINE__,
1024 "HOSTNAME : %s Length : %" PRIzu "\n",
1025 hoststr,
1026 hostlen);
1027 ret_code = http_MakeMessage(request,
1028 1,
1029 1,
1030 "s"
1031 "bcDCU",
1032 "HOST: ",
1033 hoststr,
1034 hostlen);
1035 }
1036 }
1037
1038 /* add the content-type header */
1039 if (ret_code == 0 && contentType) {
1040 ret_code = http_MakeMessage(request, 1, 1, "T", contentType);
1041 }
1042 /* add content-length header. */
1043 if (ret_code == 0) {
1044 if (contentLength >= 0)
1045 ret_code = http_MakeMessage(
1046 request, 1, 1, "Nc", (off_t)contentLength);
1047 else if (contentLength == UPNP_USING_CHUNKED)
1048 ret_code = http_MakeMessage(request, 1, 1, "Kc");
1049 else if (contentLength == UPNP_UNTIL_CLOSE)
1050 ret_code = http_MakeMessage(request, 1, 1, "c");
1051 else
1052 ret_code = UPNP_E_INVALID_PARAM;
1053 }
1054 if (ret_code != 0) {
1055 UpnpPrintf(UPNP_INFO,
1056 HTTP,
1057 __FILE__,
1058 __LINE__,
1059 "HTTP Makemessage failed\n");
1060 membuffer_destroy(request);
1061 return ret_code;
1062 }
1063 UpnpPrintf(UPNP_INFO,
1064 HTTP,
1065 __FILE__,
1066 __LINE__,
1067 "HTTP Buffer:\n%s\n"
1068 "----------END--------\n",
1069 request->buf);
1070
1071 return UPNP_E_SUCCESS;
1072 }
1073
1074 typedef struct HTTPCONNECTIONHANDLE
1075 {
1076 SOCKINFO sock_info;
1077 int contentLength;
1078 http_parser_t response;
1079 int requestStarted;
1080 int cancel;
1081 } http_connection_handle_t;
1082
1083 /*!
1084 * \brief Parses already exiting data. If not complete reads more
1085 * data on the connected socket. The read data is then parsed. The
1086 * same methid is carried out for headers.
1087 *
1088 * \return integer:
1089 * \li \c PARSE_OK - On Success
1090 * \li \c PARSE_FAILURE - Failure to parse data correctly
1091 * \li \c UPNP_E_BAD_HTTPMSG - Socker read() returns an error
1092 */
1093 static int ReadResponseLineAndHeaders(
1094 /*! Socket information object. */
1095 SOCKINFO *info,
1096 /*! HTTP Parser object. */
1097 http_parser_t *parser,
1098 /*! Time out value. */
1099 int *timeout_secs,
1100 /*! HTTP errror code returned. */
1101 int *http_error_code)
1102 {
1103 parse_status_t status;
1104 int num_read;
1105 char buf[2 * 1024];
1106 int done = 0;
1107 int ret_code = 0;
1108
1109 /*read response line */
1110 status = parser_parse_responseline(parser);
1111 switch (status) {
1112 case PARSE_OK:
1113 done = 1;
1114 break;
1115 case PARSE_INCOMPLETE:
1116 done = 0;
1117 break;
1118 default:
1119 /*error */
1120 return status;
1121 }
1122 while (!done) {
1123 num_read = sock_read(info, buf, sizeof(buf), timeout_secs);
1124 if (num_read > 0) {
1125 /* append data to buffer */
1126 ret_code = membuffer_append(
1127 &parser->msg.msg, buf, (size_t)num_read);
1128 if (ret_code != 0) {
1129 /* set failure status */
1130 parser->http_error_code =
1131 HTTP_INTERNAL_SERVER_ERROR;
1132 return PARSE_FAILURE;
1133 }
1134 status = parser_parse_responseline(parser);
1135 switch (status) {
1136 case PARSE_OK:
1137 done = 1;
1138 break;
1139 case PARSE_INCOMPLETE:
1140 done = 0;
1141 break;
1142 default:
1143 /*error */
1144 return status;
1145 }
1146 } else if (num_read == 0) {
1147 /* partial msg */
1148 *http_error_code = HTTP_BAD_REQUEST; /* or response */
1149 return UPNP_E_BAD_HTTPMSG;
1150 } else {
1151 *http_error_code = parser->http_error_code;
1152 return num_read;
1153 }
1154 }
1155 status = parser_parse_headers(parser);
1156 if ((status == (parse_status_t)PARSE_OK) &&
1157 (parser->position == (parser_pos_t)POS_ENTITY))
1158 done = 1;
1159 else if (status == (parse_status_t)PARSE_INCOMPLETE)
1160 done = 0;
1161 else
1162 /*error */
1163 return status;
1164 /*read headers */
1165 while (!done) {
1166 num_read = sock_read(info, buf, sizeof(buf), timeout_secs);
1167 if (num_read > 0) {
1168 /* append data to buffer */
1169 ret_code = membuffer_append(
1170 &parser->msg.msg, buf, (size_t)num_read);
1171 if (ret_code != 0) {
1172 /* set failure status */
1173 parser->http_error_code =
1174 HTTP_INTERNAL_SERVER_ERROR;
1175 return PARSE_FAILURE;
1176 }
1177 status = parser_parse_headers(parser);
1178 if (status == (parse_status_t)PARSE_OK &&
1179 parser->position == (parser_pos_t)POS_ENTITY)
1180 done = 1;
1181 else if (status == (parse_status_t)PARSE_INCOMPLETE)
1182 done = 0;
1183 else
1184 /*error */
1185 return status;
1186 } else if (num_read == 0) {
1187 /* partial msg */
1188 *http_error_code = HTTP_BAD_REQUEST; /* or response */
1189 return UPNP_E_BAD_HTTPMSG;
1190 } else {
1191 *http_error_code = parser->http_error_code;
1192 return num_read;
1193 }
1194 }
1195
1196 return PARSE_OK;
1197 }
1198
1199 /************************************************************************
1200 * Function: http_HttpGetProgress
1201 *
1202 * Parameters:
1203 * IN void *Handle; Handle to the HTTP get object
1204 * OUT size_t *length; Buffer to get the read and parsed data
1205 * OUT size_t *total; Size of tge buffer passed
1206 *
1207 * Description:
1208 * Extracts information from the Handle to the HTTP get object.
1209 *
1210 * Return: int
1211 * UPNP_E_SUCCESS - On Sucess
1212 * UPNP_E_INVALID_PARAM - Invalid Parameter
1213 ************************************************************************/
1214 int http_HttpGetProgress(void *Handle, size_t *length, size_t *total)
1215 {
1216 http_connection_handle_t *handle = Handle;
1217
1218 if (!handle || !length || !total) {
1219 return UPNP_E_INVALID_PARAM;
1220 }
1221 *length = handle->response.msg.entity.length;
1222 *total = handle->response.content_length;
1223
1224 return UPNP_E_SUCCESS;
1225 }
1226
1227 /************************************************************************
1228 * Function: http_CancelHttpGet
1229 *
1230 * Parameters:
1231 * IN void *Handle; Handle to HTTP get object
1232 *
1233 * Description:
1234 * Set the cancel flag of the HttpGet handle
1235 *
1236 * Return: int
1237 * UPNP_E_SUCCESS - On Success
1238 * UPNP_E_INVALID_PARAM - Invalid Parameter
1239 ************************************************************************/
1240 int http_CancelHttpGet(void *Handle)
1241 {
1242 http_connection_handle_t *handle = Handle;
1243
1244 if (!handle)
1245 return UPNP_E_INVALID_PARAM;
1246 handle->cancel = 1;
1247
1248 return UPNP_E_SUCCESS;
1249 }
1250
1251 int http_OpenHttpConnection(const char *url_str, void **Handle, int timeout)
1252 {
1253 int ret_code;
1254 size_t sockaddr_len;
1255 SOCKET tcp_connection;
1256 http_connection_handle_t *handle = NULL;
1257 uri_type url;
1258 (void)timeout; /* Unused parameter */
1259 if (!url_str || !Handle)
1260 return UPNP_E_INVALID_PARAM;
1261 *Handle = handle;
1262 /* parse url_str */
1263 ret_code = http_FixStrUrl(url_str, strlen(url_str), &url);
1264 if (ret_code != UPNP_E_SUCCESS)
1265 return ret_code;
1266 /* create the handle */
1267 handle = malloc(sizeof(http_connection_handle_t));
1268 if (!handle) {
1269 return UPNP_E_OUTOF_MEMORY;
1270 }
1271 handle->requestStarted = 0;
1272 memset(&handle->response, 0, sizeof(handle->response));
1273 /* connect to the server */
1274 tcp_connection =
1275 socket(url.hostport.IPaddress.ss_family, SOCK_STREAM, 0);
1276 if (tcp_connection == INVALID_SOCKET) {
1277 ret_code = UPNP_E_SOCKET_ERROR;
1278 goto errorHandler;
1279 }
1280 if (sock_init(&handle->sock_info, tcp_connection) != UPNP_E_SUCCESS) {
1281 sock_destroy(&handle->sock_info, SD_BOTH);
1282 ret_code = UPNP_E_SOCKET_ERROR;
1283 goto errorHandler;
1284 }
1285 sockaddr_len = url.hostport.IPaddress.ss_family == AF_INET6
1286 ? sizeof(struct sockaddr_in6)
1287 : sizeof(struct sockaddr_in);
1288 ret_code = private_connect(handle->sock_info.socket,
1289 (struct sockaddr *)&(url.hostport.IPaddress),
1290 (socklen_t)sockaddr_len);
1291 if (ret_code == -1) {
1292 sock_destroy(&handle->sock_info, SD_BOTH);
1293 ret_code = UPNP_E_SOCKET_CONNECT;
1294 goto errorHandler;
1295 }
1296 #ifdef UPNP_ENABLE_OPEN_SSL
1297 /* For HTTPS connections start the TLS/SSL handshake. */
1298 if (token_string_casecmp(&url.scheme, "https") == 0) {
1299 ret_code = sock_ssl_connect(&handle->sock_info);
1300 if (ret_code != UPNP_E_SUCCESS) {
1301 sock_destroy(&handle->sock_info, SD_BOTH);
1302 goto errorHandler;
1303 }
1304 }
1305 #endif
1306 errorHandler:
1307 *Handle = handle;
1308 return ret_code;
1309 }
1310
1311 int http_MakeHttpRequest(Upnp_HttpMethod method,
1312 const char *url_str,
1313 void *Handle,
1314 UpnpString *headers,
1315 const char *contentType,
1316 int contentLength,
1317 int timeout)
1318 {
1319 int ret_code;
1320 membuffer request;
1321 http_connection_handle_t *handle = Handle;
1322 uri_type url;
1323 if (!url_str || !Handle)
1324 return UPNP_E_INVALID_PARAM;
1325 if (handle->requestStarted) {
1326 /* TODO: Log an error that a previous request is already in
1327 * progress. */
1328 }
1329 handle->requestStarted = 1;
1330 handle->cancel = 0;
1331 ret_code = MakeGenericMessage((http_method_t)method,
1332 url_str,
1333 &request,
1334 &url,
1335 contentLength,
1336 contentType,
1337 headers);
1338 if (ret_code != UPNP_E_SUCCESS)
1339 return ret_code;
1340 /* send request */
1341 ret_code = http_SendMessage(
1342 &handle->sock_info, &timeout, "b", request.buf, request.length);
1343 membuffer_destroy(&request);
1344 httpmsg_destroy(&handle->response.msg);
1345 parser_response_init(&handle->response, (http_method_t)method);
1346 return ret_code;
1347 }
1348
1349 int http_WriteHttpRequest(void *Handle, char *buf, size_t *size, int timeout)
1350 {
1351 http_connection_handle_t *handle = (http_connection_handle_t *)Handle;
1352 char *tempbuf = NULL;
1353 size_t tempbufSize = 0;
1354 int freeTempbuf = 0;
1355 int numWritten = 0;
1356
1357 if (!handle || !size || !buf) {
1358 if (size)
1359 *size = 0;
1360 return UPNP_E_INVALID_PARAM;
1361 }
1362 if (handle->contentLength == UPNP_USING_CHUNKED) {
1363 if (*size) {
1364 size_t tempSize = 0;
1365 tempbuf = malloc(
1366 *size + CHUNK_HEADER_SIZE + CHUNK_TAIL_SIZE);
1367 if (!tempbuf)
1368 return UPNP_E_OUTOF_MEMORY;
1369 /* begin chunk */
1370 sprintf(tempbuf, "%zx\r\n", *size);
1371 tempSize = strlen(tempbuf);
1372 memcpy(tempbuf + tempSize, buf, *size);
1373 memcpy(tempbuf + tempSize + *size, "\r\n", 2);
1374 /* end of chunk */
1375 tempbufSize = tempSize + *size + 2;
1376 freeTempbuf = 1;
1377 }
1378 } else {
1379 tempbuf = buf;
1380 tempbufSize = *size;
1381 }
1382 numWritten =
1383 sock_write(&handle->sock_info, tempbuf, tempbufSize, &timeout);
1384 if (freeTempbuf)
1385 free(tempbuf);
1386 if (numWritten < 0) {
1387 *size = 0;
1388 return numWritten;
1389 } else {
1390 *size = (size_t)numWritten;
1391 return UPNP_E_SUCCESS;
1392 }
1393 }
1394
1395 int http_EndHttpRequest(void *Handle, int timeout)
1396 {
1397 int retc = 0;
1398 const char *zcrlf = "0\r\n\r\n";
1399 http_connection_handle_t *handle = Handle;
1400 if (!handle)
1401 return UPNP_E_INVALID_PARAM;
1402 if (!handle->requestStarted) {
1403 return UPNP_E_SUCCESS;
1404 }
1405 handle->requestStarted = 0;
1406 if (handle->contentLength == UPNP_USING_CHUNKED)
1407 /*send last chunk */
1408 retc = sock_write(
1409 &handle->sock_info, zcrlf, strlen(zcrlf), &timeout);
1410
1411 return retc >= 0 ? UPNP_E_SUCCESS : UPNP_E_SOCKET_WRITE;
1412 }
1413
1414 int http_GetHttpResponse(void *Handle,
1415 UpnpString *headers,
1416 char **contentType,
1417 int *contentLength,
1418 int *httpStatus,
1419 int timeout)
1420 {
1421 int ret_code;
1422 int http_error_code;
1423 memptr ctype;
1424 http_connection_handle_t *handle = Handle;
1425 parse_status_t status;
1426
1427 status = ReadResponseLineAndHeaders(&handle->sock_info,
1428 &handle->response,
1429 &timeout,
1430 &http_error_code);
1431 if (status != (parse_status_t)PARSE_OK) {
1432 ret_code = UPNP_E_BAD_RESPONSE;
1433 goto errorHandler;
1434 }
1435 status = parser_get_entity_read_method(&handle->response);
1436 switch (status) {
1437 case PARSE_CONTINUE_1:
1438 case PARSE_SUCCESS:
1439 break;
1440 default:
1441 ret_code = UPNP_E_BAD_RESPONSE;
1442 goto errorHandler;
1443 }
1444 ret_code = UPNP_E_SUCCESS;
1445 if (httpStatus) {
1446 *httpStatus = handle->response.msg.status_code;
1447 }
1448 if (contentType) {
1449 if (!httpmsg_find_hdr(
1450 &handle->response.msg, HDR_CONTENT_TYPE, &ctype))
1451 /* no content-type */
1452 *contentType = NULL;
1453 else
1454 *contentType = ctype.buf;
1455 }
1456 if (contentLength) {
1457 if (handle->response.position == (parser_pos_t)POS_COMPLETE)
1458 *contentLength = 0;
1459 else if (handle->response.ent_position == ENTREAD_USING_CHUNKED)
1460 *contentLength = UPNP_USING_CHUNKED;
1461 else if (handle->response.ent_position == ENTREAD_USING_CLEN)
1462 *contentLength = (int)handle->response.content_length;
1463 else if (handle->response.ent_position == ENTREAD_UNTIL_CLOSE)
1464 *contentLength = UPNP_UNTIL_CLOSE;
1465 }
1466
1467 if (headers) {
1468 copy_msg_headers(&handle->response.msg.headers, headers);
1469 }
1470
1471 errorHandler:
1472 if (ret_code != UPNP_E_SUCCESS)
1473 httpmsg_destroy(&handle->response.msg);
1474 return ret_code;
1475 }
1476
1477 int http_ReadHttpResponse(void *Handle, char *buf, size_t *size, int timeout)
1478 {
1479 http_connection_handle_t *handle = Handle;
1480 parse_status_t status;
1481 int num_read;
1482 int ok_on_close = 0;
1483 char tempbuf[2 * 1024];
1484 int ret_code = 0;
1485
1486 if (!handle || !size || (*size > 0 && !buf)) {
1487 if (size)
1488 *size = 0;
1489 return UPNP_E_INVALID_PARAM;
1490 }
1491 /* first parse what has already been gotten */
1492 if (handle->response.position != POS_COMPLETE)
1493 status = parser_parse_entity(&handle->response);
1494 else
1495 status = PARSE_SUCCESS;
1496 if (status == PARSE_INCOMPLETE_ENTITY)
1497 /* read until close */
1498 ok_on_close = 1;
1499 else if ((status != PARSE_SUCCESS) && (status != PARSE_CONTINUE_1) &&
1500 (status != PARSE_INCOMPLETE)) {
1501 /*error */
1502 *size = 0;
1503 return UPNP_E_BAD_RESPONSE;
1504 }
1505 /* read more if necessary entity */
1506 while (handle->response.msg.amount_discarded + *size >
1507 handle->response.msg.entity.length &&
1508 !handle->cancel && handle->response.position != POS_COMPLETE) {
1509 num_read = sock_read(
1510 &handle->sock_info, tempbuf, sizeof(tempbuf), &timeout);
1511 if (num_read > 0) {
1512 /* append data to buffer */
1513 ret_code = membuffer_append(&handle->response.msg.msg,
1514 tempbuf,
1515 (size_t)num_read);
1516 if (ret_code != 0) {
1517 /* set failure status */
1518 handle->response.http_error_code =
1519 HTTP_INTERNAL_SERVER_ERROR;
1520 *size = 0;
1521 return PARSE_FAILURE;
1522 }
1523 status = parser_parse_entity(&handle->response);
1524 if (status == PARSE_INCOMPLETE_ENTITY) {
1525 /* read until close */
1526 ok_on_close = 1;
1527 } else if ((status != PARSE_SUCCESS) &&
1528 (status != PARSE_CONTINUE_1) &&
1529 (status != PARSE_INCOMPLETE)) {
1530 /*error */
1531 *size = 0;
1532 return UPNP_E_BAD_RESPONSE;
1533 }
1534 } else if (num_read == 0) {
1535 if (ok_on_close) {
1536 UpnpPrintf(UPNP_INFO,
1537 HTTP,
1538 __FILE__,
1539 __LINE__,
1540 "<<< (RECVD) "
1541 "<<<\n%s\n-----------------\n",
1542 handle->response.msg.msg.buf);
1543 handle->response.position = POS_COMPLETE;
1544 } else {
1545 /* partial msg */
1546 *size = 0;
1547 handle->response.http_error_code =
1548 HTTP_BAD_REQUEST; /* or response */
1549 return UPNP_E_BAD_HTTPMSG;
1550 }
1551 } else {
1552 *size = 0;
1553 return num_read;
1554 }
1555 }
1556 if (handle->cancel) {
1557 return UPNP_E_CANCELED;
1558 }
1559 /* truncate size to fall within available data */
1560 if (handle->response.msg.amount_discarded + *size >
1561 handle->response.msg.entity.length)
1562 *size = handle->response.msg.entity.length -
1563 handle->response.msg.amount_discarded;
1564 /* copy data to user buffer. delete copied data */
1565 if (*size > 0) {
1566 memcpy(buf,
1567 &handle->response.msg.msg
1568 .buf[handle->response.entity_start_position],
1569 *size);
1570 membuffer_delete(&handle->response.msg.msg,
1571 handle->response.entity_start_position,
1572 *size);
1573 /* update scanner position. needed for chunked transfers */
1574 handle->response.scanner.cursor -= *size;
1575 /* update amount discarded */
1576 handle->response.msg.amount_discarded += *size;
1577 }
1578
1579 return UPNP_E_SUCCESS;
1580 }
1581
1582 int http_CloseHttpConnection(void *Handle)
1583 {
1584 http_connection_handle_t *handle = Handle;
1585 if (!handle)
1586 return UPNP_E_INVALID_PARAM;
1587 /*should shutdown completely */
1588 sock_destroy(&handle->sock_info, SD_BOTH);
1589 httpmsg_destroy(&handle->response.msg);
1590 free(handle);
1591 return UPNP_E_SUCCESS;
1592 }
1593
1594 /************************************************************************
1595 * Function: http_SendStatusResponse
1596 *
1597 * Parameters:
1598 * IN SOCKINFO *info; Socket information object
1599 * IN int http_status_code; error code returned while making
1600 * or sending the response message
1601 * IN int request_major_version; request major version
1602 * IN int request_minor_version; request minor version
1603 *
1604 * Description:
1605 * Generate a response message for the status query and send the
1606 * status response.
1607 *
1608 * Return: int
1609 * 0 -- success
1610 * UPNP_E_OUTOF_MEMORY
1611 * UPNP_E_SOCKET_WRITE
1612 * UPNP_E_TIMEDOUT
1613 ************************************************************************/
1614 int http_SendStatusResponse(SOCKINFO *info,
1615 int http_status_code,
1616 int request_major_version,
1617 int request_minor_version)
1618 {
1619 int response_major, response_minor;
1620 membuffer membuf;
1621 int ret;
1622 int timeout;
1623
1624 http_CalcResponseVersion(request_major_version,
1625 request_minor_version,
1626 &response_major,
1627 &response_minor);
1628 membuffer_init(&membuf);
1629 membuf.size_inc = (size_t)70;
1630 /* response start line */
1631 ret = http_MakeMessage(&membuf,
1632 response_major,
1633 response_minor,
1634 "RSCB",
1635 http_status_code,
1636 http_status_code);
1637 if (ret == 0) {
1638 timeout = HTTP_DEFAULT_TIMEOUT;
1639 ret = http_SendMessage(
1640 info, &timeout, "b", membuf.buf, membuf.length);
1641 }
1642 membuffer_destroy(&membuf);
1643
1644 return ret;
1645 }
1646
1647 int http_MakeMessage(membuffer *buf,
1648 int http_major_version,
1649 int http_minor_version,
1650 const char *fmt,
1651 ...)
1652 {
1653 char c;
1654 char *s = NULL;
1655 size_t num;
1656 off_t bignum;
1657 size_t length;
1658 time_t *loc_time;
1659 time_t curr_time;
1660 struct tm date_storage;
1661 struct tm *date;
1662 const char *start_str;
1663 const char *end_str;
1664 int status_code;
1665 const char *status_msg;
1666 http_method_t method;
1667 const char *method_str;
1668 const char *url_str;
1669 const char *temp_str;
1670 uri_type url;
1671 uri_type *uri_ptr;
1672 int error_code = 0;
1673 va_list argp;
1674 char tempbuf[200];
1675 const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
1676 const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
1677 "Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
1678 int rc = 0;
1679
1680 memset(tempbuf, 0, sizeof(tempbuf));
1681 va_start(argp, fmt);
1682 while ((c = *fmt++)) {
1683 if (c == 'E') {
1684 /* list of extra headers */
1685 UpnpListIter pos;
1686 UpnpListHead *head;
1687 UpnpExtraHeaders *extra;
1688 const DOMString resp;
1689 head = (UpnpListHead *)va_arg(argp, UpnpListHead *);
1690 if (head) {
1691 for (pos = UpnpListBegin(head);
1692 pos != UpnpListEnd(head);
1693 pos = UpnpListNext(head, pos)) {
1694 extra = (UpnpExtraHeaders *)pos;
1695 resp = UpnpExtraHeaders_get_resp(extra);
1696 if (resp) {
1697 if (membuffer_append(buf,
1698 resp,
1699 strlen(resp)))
1700 goto error_handler;
1701 if (membuffer_append(buf,
1702 "\r\n",
1703 (size_t)2))
1704 goto error_handler;
1705 }
1706 }
1707 }
1708 } else if (c == 's') {
1709 /* C string */
1710 s = (char *)va_arg(argp, char *);
1711 assert(s);
1712 UpnpPrintf(UPNP_ALL,
1713 HTTP,
1714 __FILE__,
1715 __LINE__,
1716 "Adding a string : %s\n",
1717 s);
1718 if (membuffer_append(buf, s, strlen(s)))
1719 goto error_handler;
1720 } else if (c == 'K') {
1721 /* Add Chunky header */
1722 if (membuffer_append(buf,
1723 "TRANSFER-ENCODING: chunked\r\n",
1724 strlen("Transfer-Encoding: chunked\r\n")))
1725 goto error_handler;
1726 } else if (c == 'G') {
1727 /* Add Range header */
1728 struct SendInstruction *RespInstr;
1729 RespInstr = (struct SendInstruction *)va_arg(
1730 argp, struct SendInstruction *);
1731 assert(RespInstr);
1732 /* connection header */
1733 if (membuffer_append(buf,
1734 RespInstr->RangeHeader,
1735 strlen(RespInstr->RangeHeader)))
1736 goto error_handler;
1737 } else if (c == 'b') {
1738 /* mem buffer */
1739 s = (char *)va_arg(argp, char *);
1740 UpnpPrintf(UPNP_ALL,
1741 HTTP,
1742 __FILE__,
1743 __LINE__,
1744 "Adding a char Buffer starting with: %c\n",
1745 (int)s[0]);
1746 assert(s);
1747 length = (size_t)va_arg(argp, size_t);
1748 if (membuffer_append(buf, s, length))
1749 goto error_handler;
1750 } else if (c == 'c') {
1751 /* crlf */
1752 if (membuffer_append(buf, "\r\n", (size_t)2))
1753 goto error_handler;
1754 } else if (c == 'd') {
1755 /* integer */
1756 num = (size_t)va_arg(argp, int);
1757 rc = snprintf(tempbuf, sizeof(tempbuf), "%" PRIzu, num);
1758 if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf) ||
1759 membuffer_append(buf, tempbuf, strlen(tempbuf)))
1760 goto error_handler;
1761 } else if (c == 'h') {
1762 /* off_t */
1763 bignum = (off_t)va_arg(argp, off_t);
1764 rc = snprintf(tempbuf,
1765 sizeof(tempbuf),
1766 "%" PRId64,
1767 (int64_t)bignum);
1768 if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf) ||
1769 membuffer_append(buf, tempbuf, strlen(tempbuf)))
1770 goto error_handler;
1771 } else if (c == 't' || c == 'D') {
1772 /* date */
1773 if (c == 'D') {
1774 /* header */
1775 start_str = "DATE: ";
1776 end_str = "\r\n";
1777 curr_time = time(NULL);
1778 loc_time = &curr_time;
1779 } else {
1780 /* date value only */
1781 start_str = end_str = "";
1782 loc_time = (time_t *)va_arg(argp, time_t *);
1783 }
1784 assert(loc_time);
1785 date = http_gmtime_r(loc_time, &date_storage);
1786 if (date == NULL)
1787 goto error_handler;
1788 rc = snprintf(tempbuf,
1789 sizeof(tempbuf),
1790 "%s%s, %02d %s %d %02d:%02d:%02d GMT%s",
1791 start_str,
1792 &weekday_str[date->tm_wday * 4],
1793 date->tm_mday,
1794 &month_str[date->tm_mon * 4],
1795 date->tm_year + 1900,
1796 date->tm_hour,
1797 date->tm_min,
1798 date->tm_sec,
1799 end_str);
1800 if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf) ||
1801 membuffer_append(buf, tempbuf, strlen(tempbuf)))
1802 goto error_handler;
1803 } else if (c == 'L') {
1804 /* Add CONTENT-LANGUAGE header only if
1805 * WEB_SERVER_CONTENT_LANGUAGE */
1806 /* is not empty and if Accept-Language header is not
1807 * empty */
1808 struct SendInstruction *RespInstr;
1809 RespInstr = (struct SendInstruction *)va_arg(
1810 argp, struct SendInstruction *);
1811 assert(RespInstr);
1812 if (strcmp(RespInstr->AcceptLanguageHeader, "") &&
1813 strcmp(WEB_SERVER_CONTENT_LANGUAGE, "") &&
1814 http_MakeMessage(buf,
1815 http_major_version,
1816 http_minor_version,
1817 "ssc",
1818 "CONTENT-LANGUAGE: ",
1819 WEB_SERVER_CONTENT_LANGUAGE) != 0)
1820 goto error_handler;
1821 } else if (c == 'C') {
1822 if ((http_major_version > 1) ||
1823 (http_major_version == 1 &&
1824 http_minor_version == 1)) {
1825 /* connection header */
1826 if (membuffer_append_str(
1827 buf, "CONNECTION: close\r\n"))
1828 goto error_handler;
1829 }
1830 } else if (c == 'N') {
1831 /* content-length header */
1832 bignum = (off_t)va_arg(argp, off_t);
1833 assert(bignum >= 0);
1834 if (http_MakeMessage(buf,
1835 http_major_version,
1836 http_minor_version,
1837 "shc",
1838 "CONTENT-LENGTH: ",
1839 bignum) != 0)
1840 goto error_handler;
1841 /* Add accept ranges */
1842 if (http_MakeMessage(buf,
1843 http_major_version,
1844 http_minor_version,
1845 "sc",
1846 "Accept-Ranges: bytes") != 0)
1847 goto error_handler;
1848 } else if (c == 'S' || c == 'U') {
1849 /* SERVER or USER-AGENT header */
1850 temp_str = (c == 'S') ? "SERVER: " : "USER-AGENT: ";
1851 get_sdk_info(tempbuf, sizeof(tempbuf));
1852 if (http_MakeMessage(buf,
1853 http_major_version,
1854 http_minor_version,
1855 "ss",
1856 temp_str,
1857 tempbuf) != 0)
1858 goto error_handler;
1859 } else if (c == 'X') {
1860 /* C string */
1861 s = (char *)va_arg(argp, char *);
1862 assert(s);
1863 if (membuffer_append_str(buf, "X-User-Agent: ") != 0)
1864 goto error_handler;
1865 if (membuffer_append(buf, s, strlen(s)) != 0)
1866 goto error_handler;
1867 } else if (c == 'R') {
1868 /* response start line */
1869 /* e.g.: 'HTTP/1.1 200 OK' code */
1870 status_code = (int)va_arg(argp, int);
1871 assert(status_code > 0);
1872 rc = snprintf(tempbuf,
1873 sizeof(tempbuf),
1874 "HTTP/%d.%d %d ",
1875 http_major_version,
1876 http_minor_version,
1877 status_code);
1878 /* str */
1879 status_msg = http_get_code_text(status_code);
1880 if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf) ||
1881 http_MakeMessage(buf,
1882 http_major_version,
1883 http_minor_version,
1884 "ssc",
1885 tempbuf,
1886 status_msg) != 0)
1887 goto error_handler;
1888 } else if (c == 'B') {
1889 /* body of a simple reply */
1890 status_code = (int)va_arg(argp, int);
1891 rc = snprintf(tempbuf,
1892 sizeof(tempbuf),
1893 "%s%d %s%s",
1894 "<html><body><h1>",
1895 status_code,
1896 http_get_code_text(status_code),
1897 "</h1></body></html>");
1898 if (rc < 0 || (unsigned int)rc >= sizeof(tempbuf))
1899 goto error_handler;
1900 bignum = (off_t)strlen(tempbuf);
1901 if (http_MakeMessage(buf,
1902 http_major_version,
1903 http_minor_version,
1904 "NTcs",
1905 bignum, /* content-length */
1906 "text/html", /* content-type */
1907 tempbuf) != 0 /* body */
1908 )
1909 goto error_handler;
1910 } else if (c == 'Q') {
1911 /* request start line */
1912 /* GET /foo/bar.html HTTP/1.1\r\n */
1913 method = (http_method_t)va_arg(argp, http_method_t);
1914 method_str = method_to_str(method);
1915 url_str = (const char *)va_arg(argp, const char *);
1916 num = (size_t)va_arg(
1917 argp, size_t); /* length of url_str */
1918 if (http_MakeMessage(buf,
1919 http_major_version,
1920 http_minor_version,
1921 "ssbsdsdc",
1922 method_str, /* method */
1923 " ",
1924 url_str,
1925 num, /* url */
1926 " HTTP/",
1927 http_major_version,
1928 ".",
1929 http_minor_version) != 0)
1930 goto error_handler;
1931 } else if (c == 'q') {
1932 /* request start line and HOST header */
1933 method = (http_method_t)va_arg(argp, http_method_t);
1934 uri_ptr = (uri_type *)va_arg(argp, uri_type *);
1935 assert(uri_ptr);
1936 if (http_FixUrl(uri_ptr, &url) != 0) {
1937 error_code = UPNP_E_INVALID_URL;
1938 goto error_handler;
1939 }
1940 if (http_MakeMessage(buf,
1941 http_major_version,
1942 http_minor_version,
1943 "Q"
1944 "sbc",
1945 method,
1946 url.pathquery.buff,
1947 url.pathquery.size,
1948 "HOST: ",
1949 url.hostport.text.buff,
1950 url.hostport.text.size) != 0)
1951 goto error_handler;
1952 } else if (c == 'T') {
1953 /* content type header */
1954 temp_str = (const char *)va_arg(
1955 argp, const char *); /* type/subtype format */
1956 if (http_MakeMessage(buf,
1957 http_major_version,
1958 http_minor_version,
1959 "ssc",
1960 "CONTENT-TYPE: ",
1961 temp_str) != 0)
1962 goto error_handler;
1963 } else {
1964 assert(0);
1965 }
1966 }
1967 goto ExitFunction;
1968
1969 error_handler:
1970 /* Default is out of memory error. */
1971 if (!error_code)
1972 error_code = UPNP_E_OUTOF_MEMORY;
1973 membuffer_destroy(buf);
1974
1975 ExitFunction:
1976 va_end(argp);
1977 return error_code;
1978 }
1979
1980 /************************************************************************
1981 * Function: http_CalcResponseVersion
1982 *
1983 * Parameters:
1984 * IN int request_major_vers; Request major version
1985 * IN int request_minor_vers; Request minor version
1986 * OUT int* response_major_vers; Response mojor version
1987 * OUT int* response_minor_vers; Response minor version
1988 *
1989 * Description:
1990 * Calculate HTTP response versions based on the request versions.
1991 *
1992 * Return: void
1993 ************************************************************************/
1994 void http_CalcResponseVersion(int request_major_vers,
1995 int request_minor_vers,
1996 int *response_major_vers,
1997 int *response_minor_vers)
1998 {
1999 if ((request_major_vers > 1) ||
2000 (request_major_vers == 1 && request_minor_vers >= 1)) {
2001 *response_major_vers = 1;
2002 *response_minor_vers = 1;
2003 } else {
2004 *response_major_vers = request_major_vers;
2005 *response_minor_vers = request_minor_vers;
2006 }
2007 }
2008
2009 /************************************************************************
2010 * Function: MakeGetMessageEx
2011 *
2012 * Parameters:
2013 * const char *url_str; String as a URL
2014 * membuffer *request; Buffer containing the request
2015 * uri_type *url; URI object containing the scheme, path
2016 * query token, etc.
2017 *
2018 * Description:
2019 * Makes the message for the HTTP GET method
2020 *
2021 * Returns:
2022 * UPNP_E_INVALID_URL
2023 * Error Codes returned by http_MakeMessage
2024 * UPNP_E_SUCCESS
2025 ************************************************************************/
2026 int MakeGetMessageEx(const char *url_str,
2027 membuffer *request,
2028 uri_type *url,
2029 struct SendInstruction *pRangeSpecifier)
2030 {
2031 size_t url_str_len;
2032 int ret_code = UPNP_E_SUCCESS;
2033 size_t hostlen = 0;
2034 const char *hoststr;
2035
2036 url_str_len = strlen(url_str);
2037 do {
2038 UpnpPrintf(UPNP_INFO,
2039 HTTP,
2040 __FILE__,
2041 __LINE__,
2042 "DOWNLOAD URL : %s\n",
2043 url_str);
2044 ret_code = http_FixStrUrl(url_str, url_str_len, url);
2045 if (ret_code != UPNP_E_SUCCESS) {
2046 break;
2047 }
2048 /* make msg */
2049 membuffer_init(request);
2050 ret_code = get_hoststr(url_str, &hoststr, &hostlen);
2051 if (ret_code != UPNP_E_SUCCESS) {
2052 break;
2053 }
2054 UpnpPrintf(UPNP_INFO,
2055 HTTP,
2056 __FILE__,
2057 __LINE__,
2058 "HOSTNAME : %s Length : %" PRIzu "\n",
2059 hoststr,
2060 hostlen);
2061 ret_code = http_MakeMessage(request,
2062 1,
2063 1,
2064 "Q"
2065 "s"
2066 "bc"
2067 "GDCUc",
2068 HTTPMETHOD_GET,
2069 url->pathquery.buff,
2070 url->pathquery.size,
2071 "HOST: ",
2072 hoststr,
2073 hostlen,
2074 pRangeSpecifier);
2075 if (ret_code != 0) {
2076 UpnpPrintf(UPNP_INFO,
2077 HTTP,
2078 __FILE__,
2079 __LINE__,
2080 "HTTP Makemessage failed\n");
2081 membuffer_destroy(request);
2082 return ret_code;
2083 }
2084 } while (0);
2085 UpnpPrintf(UPNP_INFO,
2086 HTTP,
2087 __FILE__,
2088 __LINE__,
2089 "HTTP Buffer:\n%s\n"
2090 "----------END--------\n",
2091 request->buf);
2092
2093 return ret_code;
2094 }
2095
2096 #define SIZE_RANGE_BUFFER 50
2097
2098 /************************************************************************
2099 * Function: http_OpenHttpGetEx
2100 *
2101 * Parameters:
2102 * IN const char *url_str; String as a URL
2103 * IN OUT void **Handle; Pointer to buffer to store HTTP
2104 * post handle
2105 * IN OUT char **contentType; Type of content
2106 * OUT int *contentLength; length of content
2107 * OUT int *httpStatus; HTTP status returned on receiving a
2108 * response message
2109 * IN int timeout; time out value
2110 *
2111 * Description:
2112 * Makes the HTTP GET message, connects to the peer,
2113 * sends the HTTP GET request, gets the response and parses the
2114 * response.
2115 *
2116 * Return: int
2117 * UPNP_E_SUCCESS - On Success
2118 * UPNP_E_INVALID_PARAM - Invalid Paramters
2119 * UPNP_E_OUTOF_MEMORY
2120 * UPNP_E_SOCKET_ERROR
2121 * UPNP_E_BAD_RESPONSE
2122 ************************************************************************/
2123 int http_OpenHttpGetEx(const char *url_str,
2124 void **Handle,
2125 char **contentType,
2126 int *contentLength,
2127 int *httpStatus,
2128 int lowRange,
2129 int highRange,
2130 int timeout)
2131 {
2132 int http_error_code;
2133 memptr ctype;
2134 SOCKET tcp_connection;
2135 size_t sockaddr_len;
2136 membuffer request;
2137 http_connection_handle_t *handle = NULL;
2138 uri_type url;
2139 parse_status_t status;
2140 int errCode = UPNP_E_SUCCESS;
2141 /* char rangeBuf[SIZE_RANGE_BUFFER]; */
2142 struct SendInstruction rangeBuf;
2143 int rc = 0;
2144
2145 membuffer_init(&request);
2146
2147 do {
2148 /* Checking Input parameters */
2149 if (!url_str || !Handle || !contentType || !httpStatus) {
2150 errCode = UPNP_E_INVALID_PARAM;
2151 break;
2152 }
2153 /* Initialize output parameters */
2154 *httpStatus = 0;
2155 *Handle = handle;
2156 *contentType = NULL;
2157 *contentLength = 0;
2158 if (lowRange > highRange) {
2159 errCode = UPNP_E_INTERNAL_ERROR;
2160 break;
2161 }
2162 memset(&rangeBuf, 0, sizeof(rangeBuf));
2163 rc = snprintf(rangeBuf.RangeHeader,
2164 sizeof(rangeBuf.RangeHeader),
2165 "Range: bytes=%d-%d\r\n",
2166 lowRange,
2167 highRange);
2168 if (rc < 0 || (unsigned int)rc >= sizeof(rangeBuf.RangeHeader))
2169 break;
2170 membuffer_init(&request);
2171 errCode = MakeGetMessageEx(url_str, &request, &url, &rangeBuf);
2172 if (errCode != UPNP_E_SUCCESS)
2173 break;
2174 handle = (http_connection_handle_t *)malloc(
2175 sizeof(http_connection_handle_t));
2176 if (!handle) {
2177 errCode = UPNP_E_OUTOF_MEMORY;
2178 break;
2179 }
2180 memset(handle, 0, sizeof(*handle));
2181 parser_response_init(&handle->response, HTTPMETHOD_GET);
2182 tcp_connection = socket(
2183 (int)url.hostport.IPaddress.ss_family, SOCK_STREAM, 0);
2184 if (tcp_connection == INVALID_SOCKET) {
2185 errCode = UPNP_E_SOCKET_ERROR;
2186 free(handle);
2187 break;
2188 }
2189 if (sock_init(&handle->sock_info, tcp_connection) !=
2190 UPNP_E_SUCCESS) {
2191 sock_destroy(&handle->sock_info, SD_BOTH);
2192 errCode = UPNP_E_SOCKET_ERROR;
2193 free(handle);
2194 break;
2195 }
2196 sockaddr_len = url.hostport.IPaddress.ss_family == AF_INET6
2197 ? sizeof(struct sockaddr_in6)
2198 : sizeof(struct sockaddr_in);
2199 errCode = private_connect(handle->sock_info.socket,
2200 (struct sockaddr *)&(url.hostport.IPaddress),
2201 (socklen_t)sockaddr_len);
2202 if (errCode == -1) {
2203 sock_destroy(&handle->sock_info, SD_BOTH);
2204 errCode = UPNP_E_SOCKET_CONNECT;
2205 free(handle);
2206 break;
2207 }
2208 /* send request */
2209 errCode = http_SendMessage(&handle->sock_info,
2210 &timeout,
2211 "b",
2212 request.buf,
2213 request.length);
2214 if (errCode != UPNP_E_SUCCESS) {
2215 sock_destroy(&handle->sock_info, SD_BOTH);
2216 free(handle);
2217 break;
2218 }
2219 if (ReadResponseLineAndHeaders(&handle->sock_info,
2220 &handle->response,
2221 &timeout,
2222 &http_error_code) != (int)PARSE_OK) {
2223 errCode = UPNP_E_BAD_RESPONSE;
2224 free(handle);
2225 break;
2226 }
2227 status = parser_get_entity_read_method(&handle->response);
2228 if (status != (parse_status_t)PARSE_CONTINUE_1 &&
2229 status != (parse_status_t)PARSE_SUCCESS) {
2230 errCode = UPNP_E_BAD_RESPONSE;
2231 free(handle);
2232 break;
2233 }
2234 *httpStatus = handle->response.msg.status_code;
2235 errCode = UPNP_E_SUCCESS;
2236
2237 if (!httpmsg_find_hdr(
2238 &handle->response.msg, HDR_CONTENT_TYPE, &ctype))
2239 /* no content-type */
2240 *contentType = NULL;
2241 else
2242 *contentType = ctype.buf;
2243 if (handle->response.position == (parser_pos_t)POS_COMPLETE)
2244 *contentLength = 0;
2245 else if (handle->response.ent_position == ENTREAD_USING_CHUNKED)
2246 *contentLength = UPNP_USING_CHUNKED;
2247 else if (handle->response.ent_position == ENTREAD_USING_CLEN)
2248 *contentLength = (int)handle->response.content_length;
2249 else if (handle->response.ent_position == ENTREAD_UNTIL_CLOSE)
2250 *contentLength = UPNP_UNTIL_CLOSE;
2251 *Handle = handle;
2252 } while (0);
2253
2254 membuffer_destroy(&request);
2255
2256 return errCode;
2257 }
2258
2259 /************************************************************************
2260 * Function: get_sdk_info
2261 *
2262 * Parameters:
2263 * OUT char *info; buffer to store the operating system information
2264 * IN size_t infoSize; size of buffer
2265 *
2266 * Description:
2267 * Returns the server information for the operating system
2268 *
2269 * Return:
2270 * UPNP_INLINE void
2271 ************************************************************************/
2272 /* 'info' should have a size of at least 100 bytes */
2273 void get_sdk_info(char *info, size_t infoSize)
2274 {
2275 #ifdef UPNP_ENABLE_UNSPECIFIED_SERVER
2276 snprintf(info, infoSize, "Unspecified, UPnP/1.0, Unspecified\r\n");
2277 #else /* UPNP_ENABLE_UNSPECIFIED_SERVER */
2278 #ifdef _WIN32
2279 OSVERSIONINFO versioninfo;
2280 versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
2281
2282 if (GetVersionEx(&versioninfo) != 0)
2283 snprintf(info,
2284 infoSize,
2285 "%d.%d.%d %d/%s, UPnP/1.0, Portable SDK for UPnP "
2286 "devices/" UPNP_VERSION_STRING "\r\n",
2287 versioninfo.dwMajorVersion,
2288 versioninfo.dwMinorVersion,
2289 versioninfo.dwBuildNumber,
2290 versioninfo.dwPlatformId,
2291 versioninfo.szCSDVersion);
2292 else
2293 *info = '\0';
2294 #else
2295 int ret_code;
2296 struct utsname sys_info;
2297
2298 ret_code = uname(&sys_info);
2299 if (ret_code == -1)
2300 *info = '\0';
2301 snprintf(info,
2302 infoSize,
2303 "%s/%s, UPnP/1.0, Portable SDK for UPnP "
2304 "devices/" UPNP_VERSION_STRING "\r\n",
2305 sys_info.sysname,
2306 sys_info.release);
2307 #endif
2308 #endif /* UPNP_ENABLE_UNSPECIFIED_SERVER */
2309 }
2310