
commit 0edaf3361db01425cae0daee7dc3f6039f381a17 replaced several malloc()+strcat() sequences with strncat() using strlen() on the *source* string. This is still vulnerable to overwrite the *target* buffer. While reviewing this commit change the code to directly use snprintf() for concatenating strings and check the length of the target buffer. Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2171 lines
62 KiB
C
2171 lines
62 KiB
C
/*******************************************************************************
|
|
*
|
|
* Copyright (c) 2000-2003 Intel Corporation
|
|
* All rights reserved.
|
|
* Copyright (c) 2012 France Telecom All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* - Neither name of Intel Corporation nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
******************************************************************************/
|
|
|
|
/*!
|
|
* \file
|
|
*
|
|
* Purpose: This file defines the functionality making use of the http.
|
|
* It defines functions to receive messages, process messages, send messages.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "httpreadwrite.h"
|
|
|
|
#include "unixutil.h"
|
|
#include "upnp.h"
|
|
#include "upnpapi.h"
|
|
#include "membuffer.h"
|
|
#include "uri.h"
|
|
#include "statcodes.h"
|
|
#include "sock.h"
|
|
#include "UpnpInet.h"
|
|
#include "UpnpIntTypes.h"
|
|
#include "UpnpStdInt.h"
|
|
#include "webserver.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
|
|
#ifdef WIN32
|
|
#include <malloc.h>
|
|
#define fseeko fseek
|
|
#define snprintf _snprintf
|
|
#else
|
|
#include <arpa/inet.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/utsname.h>
|
|
#endif
|
|
|
|
/*
|
|
* Please, do not change these to const int while MSVC cannot understand
|
|
* const int in array dimensions.
|
|
*/
|
|
/*
|
|
const int CHUNK_HEADER_SIZE = 10;
|
|
const int CHUNK_TAIL_SIZE = 10;
|
|
*/
|
|
#define CHUNK_HEADER_SIZE (size_t)10
|
|
#define CHUNK_TAIL_SIZE (size_t)10
|
|
|
|
#ifndef UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS
|
|
|
|
/* in seconds */
|
|
#define DEFAULT_TCP_CONNECT_TIMEOUT 5
|
|
|
|
/*!
|
|
* \brief Checks socket connection and wait if it is not connected.
|
|
* It should be called just after connect.
|
|
*
|
|
* \return 0 if successful, else -1.
|
|
*/
|
|
static int Check_Connect_And_Wait_Connection(
|
|
/*! [in] socket. */
|
|
SOCKET sock,
|
|
/*! [in] result of connect. */
|
|
int connect_res)
|
|
{
|
|
struct timeval tmvTimeout = {DEFAULT_TCP_CONNECT_TIMEOUT, 0};
|
|
int result;
|
|
#ifdef WIN32
|
|
struct fd_set fdSet;
|
|
#else
|
|
fd_set fdSet;
|
|
#endif
|
|
FD_ZERO(&fdSet);
|
|
FD_SET(sock, &fdSet);
|
|
|
|
if (connect_res < 0) {
|
|
#ifdef WIN32
|
|
if (WSAEWOULDBLOCK == WSAGetLastError() ) {
|
|
#else
|
|
if (EINPROGRESS == errno ) {
|
|
#endif
|
|
result = select(sock + 1, NULL, &fdSet, NULL, &tmvTimeout);
|
|
if (result < 0) {
|
|
#ifdef WIN32
|
|
/* WSAGetLastError(); */
|
|
#else
|
|
/* errno */
|
|
#endif
|
|
return -1;
|
|
} else if (result == 0) {
|
|
/* timeout */
|
|
return -1;
|
|
#ifndef WIN32
|
|
} else {
|
|
int valopt = 0;
|
|
socklen_t len = sizeof(valopt);
|
|
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void *) &valopt, &len) < 0) {
|
|
/* failed to read delayed error */
|
|
return -1;
|
|
} else if (valopt) {
|
|
/* delayed error = valopt */
|
|
return -1;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS */
|
|
|
|
static int private_connect(
|
|
SOCKET sockfd,
|
|
const struct sockaddr *serv_addr,
|
|
socklen_t addrlen)
|
|
{
|
|
#ifndef UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS
|
|
int ret = sock_make_no_blocking(sockfd);
|
|
if (ret != - 1) {
|
|
ret = connect(sockfd, serv_addr, addrlen);
|
|
ret = Check_Connect_And_Wait_Connection(sockfd, ret);
|
|
if (ret != - 1) {
|
|
ret = sock_make_blocking(sockfd);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
#else
|
|
return connect(sockfd, serv_addr, addrlen);
|
|
#endif /* UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS */
|
|
}
|
|
|
|
#ifdef WIN32
|
|
struct tm *http_gmtime_r(const time_t *clock, struct tm *result)
|
|
{
|
|
if (clock == NULL || *clock < 0 || result == NULL)
|
|
return NULL;
|
|
|
|
/* gmtime in VC runtime is thread safe. */
|
|
*result = *gmtime(clock);
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
int http_FixUrl(IN uri_type *url, OUT uri_type *fixed_url)
|
|
{
|
|
const char *temp_path = "/";
|
|
|
|
*fixed_url = *url;
|
|
if (token_string_casecmp(&fixed_url->scheme, "http") != 0) {
|
|
return UPNP_E_INVALID_URL;
|
|
}
|
|
if( fixed_url->hostport.text.size == ( size_t ) 0 ) {
|
|
return UPNP_E_INVALID_URL;
|
|
}
|
|
/* set pathquery to "/" if it is empty */
|
|
if (fixed_url->pathquery.size == (size_t)0) {
|
|
fixed_url->pathquery.buff = temp_path;
|
|
fixed_url->pathquery.size = (size_t)1;
|
|
}
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
int http_FixStrUrl(
|
|
IN const char *urlstr,
|
|
IN size_t urlstrlen,
|
|
OUT uri_type *fixed_url)
|
|
{
|
|
uri_type url;
|
|
|
|
if (parse_uri(urlstr, urlstrlen, &url) != HTTP_SUCCESS) {
|
|
return UPNP_E_INVALID_URL;
|
|
}
|
|
|
|
return http_FixUrl(&url, fixed_url);
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: http_Connect
|
|
*
|
|
* Parameters:
|
|
* IN uri_type* destination_url; URL containing destination information
|
|
* OUT uri_type *url; Fixed and corrected URL
|
|
*
|
|
* Description:
|
|
* Gets destination address from URL and then connects to the remote end
|
|
*
|
|
* Returns:
|
|
* socket descriptor on success
|
|
* UPNP_E_OUTOF_SOCKET
|
|
* UPNP_E_SOCKET_CONNECT on error
|
|
************************************************************************/
|
|
SOCKET http_Connect(
|
|
IN uri_type *destination_url,
|
|
OUT uri_type *url)
|
|
{
|
|
SOCKET connfd;
|
|
socklen_t sockaddr_len;
|
|
int ret_connect;
|
|
char errorBuffer[ERROR_BUFFER_LEN];
|
|
|
|
http_FixUrl(destination_url, url);
|
|
|
|
connfd = socket((int)url->hostport.IPaddress.ss_family,
|
|
SOCK_STREAM, 0);
|
|
if (connfd == INVALID_SOCKET) {
|
|
return (SOCKET)(UPNP_E_OUTOF_SOCKET);
|
|
}
|
|
sockaddr_len = (socklen_t)(url->hostport.IPaddress.ss_family == AF_INET6 ?
|
|
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
|
|
ret_connect = private_connect(connfd,
|
|
(struct sockaddr *)&url->hostport.IPaddress, sockaddr_len);
|
|
if (ret_connect == -1) {
|
|
#ifdef WIN32
|
|
UpnpPrintf(UPNP_CRITICAL, HTTP, __FILE__, __LINE__,
|
|
"connect error: %d\n", WSAGetLastError());
|
|
#endif
|
|
if (shutdown(connfd, SD_BOTH) == -1) {
|
|
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"Error in shutdown: %s\n", errorBuffer);
|
|
}
|
|
UpnpCloseSocket(connfd);
|
|
return (SOCKET)(UPNP_E_SOCKET_CONNECT);
|
|
}
|
|
|
|
return connfd;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Get the data on the socket and take actions based on the read data to
|
|
* modify the parser objects buffer.
|
|
*
|
|
* If an error is reported while parsing the data, the error code is passed in
|
|
* the http_errr_code parameter.
|
|
*
|
|
* Parameters:
|
|
* IN SOCKINFO *info; Socket information object
|
|
* OUT http_parser_t* parser; HTTP parser object
|
|
* IN http_method_t request_method; HTTP request method
|
|
* IN OUT int* timeout_secs; time out
|
|
* OUT int* http_error_code; HTTP error code returned
|
|
*
|
|
* \return
|
|
* UPNP_E_SUCCESS
|
|
* UPNP_E_BAD_HTTPMSG
|
|
*/
|
|
int http_RecvMessage(
|
|
IN SOCKINFO *info,
|
|
OUT http_parser_t *parser,
|
|
IN http_method_t request_method,
|
|
IN OUT int *timeout_secs,
|
|
OUT int *http_error_code)
|
|
{
|
|
int ret = UPNP_E_SUCCESS;
|
|
int line = 0;
|
|
parse_status_t status;
|
|
int num_read;
|
|
int ok_on_close = FALSE;
|
|
char buf[2 * 1024];
|
|
|
|
if (request_method == (http_method_t)HTTPMETHOD_UNKNOWN) {
|
|
parser_request_init(parser);
|
|
} else {
|
|
parser_response_init(parser, request_method);
|
|
}
|
|
|
|
while (TRUE) {
|
|
num_read = sock_read(info, buf, sizeof buf, timeout_secs);
|
|
if (num_read > 0) {
|
|
/* got data */
|
|
status = parser_append(parser, buf, (size_t)num_read);
|
|
switch (status) {
|
|
case PARSE_SUCCESS:
|
|
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"<<< (RECVD) <<<\n%s\n-----------------\n",
|
|
parser->msg.msg.buf );
|
|
print_http_headers( &parser->msg );
|
|
if (g_maxContentLength > (size_t)0 && parser->content_length > (unsigned int)g_maxContentLength) {
|
|
*http_error_code = HTTP_REQ_ENTITY_TOO_LARGE;
|
|
line = __LINE__;
|
|
ret = UPNP_E_OUTOF_BOUNDS;
|
|
goto ExitFunction;
|
|
}
|
|
line = __LINE__;
|
|
ret = 0;
|
|
goto ExitFunction;
|
|
case PARSE_FAILURE:
|
|
case PARSE_NO_MATCH:
|
|
*http_error_code = parser->http_error_code;
|
|
line = __LINE__;
|
|
ret = UPNP_E_BAD_HTTPMSG;
|
|
goto ExitFunction;
|
|
case PARSE_INCOMPLETE_ENTITY:
|
|
/* read until close */
|
|
ok_on_close = TRUE;
|
|
break;
|
|
case PARSE_CONTINUE_1:
|
|
/* Web post request. */
|
|
line = __LINE__;
|
|
ret = PARSE_SUCCESS;
|
|
goto ExitFunction;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (num_read == 0) {
|
|
if (ok_on_close) {
|
|
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"<<< (RECVD) <<<\n%s\n-----------------\n",
|
|
parser->msg.msg.buf );
|
|
print_http_headers(&parser->msg);
|
|
line = __LINE__;
|
|
ret = 0;
|
|
goto ExitFunction;
|
|
} else {
|
|
/* partial msg */
|
|
*http_error_code = HTTP_BAD_REQUEST; /* or response */
|
|
line = __LINE__;
|
|
ret = UPNP_E_BAD_HTTPMSG;
|
|
goto ExitFunction;
|
|
}
|
|
} else {
|
|
*http_error_code = parser->http_error_code;
|
|
line = __LINE__;
|
|
ret = num_read;
|
|
goto ExitFunction;
|
|
}
|
|
}
|
|
|
|
ExitFunction:
|
|
if (ret != UPNP_E_SUCCESS) {
|
|
UpnpPrintf(UPNP_ALL, HTTP, __FILE__, line,
|
|
"(http_RecvMessage): Error %d, http_error_code = %d.\n",
|
|
ret,
|
|
*http_error_code);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int http_SendMessage(SOCKINFO *info, int *TimeOut, const char *fmt, ...)
|
|
{
|
|
#if EXCLUDE_WEB_SERVER == 0
|
|
FILE *Fp;
|
|
struct SendInstruction *Instr = NULL;
|
|
char *filename = NULL;
|
|
char *file_buf = NULL;
|
|
char *ChunkBuf = NULL;
|
|
/* 10 byte allocated for chunk header. */
|
|
char Chunk_Header[CHUNK_HEADER_SIZE];
|
|
size_t num_read;
|
|
size_t amount_to_be_read = (size_t)0;
|
|
size_t Data_Buf_Size = WEB_SERVER_BUF_SIZE;
|
|
#endif /* EXCLUDE_WEB_SERVER */
|
|
va_list argp;
|
|
char *buf = NULL;
|
|
char c;
|
|
int nw;
|
|
int RetVal = 0;
|
|
size_t buf_length;
|
|
size_t num_written;
|
|
|
|
#if EXCLUDE_WEB_SERVER == 0
|
|
memset(Chunk_Header, 0, sizeof(Chunk_Header));
|
|
#endif /* EXCLUDE_WEB_SERVER */
|
|
va_start(argp, fmt);
|
|
while ((c = *fmt++)) {
|
|
#if EXCLUDE_WEB_SERVER == 0
|
|
if (c == 'I') {
|
|
Instr = va_arg(argp, struct SendInstruction *);
|
|
if (Instr->ReadSendSize >= 0)
|
|
amount_to_be_read = (size_t)Instr->ReadSendSize;
|
|
else
|
|
amount_to_be_read = Data_Buf_Size;
|
|
if (amount_to_be_read < WEB_SERVER_BUF_SIZE)
|
|
Data_Buf_Size = amount_to_be_read;
|
|
ChunkBuf = malloc((size_t)
|
|
(Data_Buf_Size + CHUNK_HEADER_SIZE +
|
|
CHUNK_TAIL_SIZE));
|
|
if (!ChunkBuf) {
|
|
RetVal = UPNP_E_OUTOF_MEMORY;
|
|
goto ExitFunction;
|
|
}
|
|
file_buf = ChunkBuf + CHUNK_HEADER_SIZE;
|
|
} else if (c == 'f') {
|
|
/* file name */
|
|
filename = va_arg(argp, char *);
|
|
if (Instr && Instr->IsVirtualFile)
|
|
Fp = (virtualDirCallback.open)(filename, UPNP_READ);
|
|
else
|
|
Fp = fopen(filename, "rb");
|
|
if (Fp == NULL) {
|
|
RetVal = UPNP_E_FILE_READ_ERROR;
|
|
goto ExitFunction;
|
|
}
|
|
if (Instr && Instr->IsRangeActive && Instr->IsVirtualFile) {
|
|
if (virtualDirCallback.seek(Fp, Instr->RangeOffset,
|
|
SEEK_CUR) != 0) {
|
|
RetVal = UPNP_E_FILE_READ_ERROR;
|
|
goto Cleanup_File;
|
|
}
|
|
} else if (Instr && Instr->IsRangeActive) {
|
|
if (fseeko(Fp, Instr->RangeOffset, SEEK_CUR) != 0) {
|
|
RetVal = UPNP_E_FILE_READ_ERROR;
|
|
goto Cleanup_File;
|
|
}
|
|
}
|
|
while (amount_to_be_read) {
|
|
if (Instr) {
|
|
int nr;
|
|
size_t n = amount_to_be_read >= Data_Buf_Size ?
|
|
Data_Buf_Size : amount_to_be_read;
|
|
if (Instr->IsVirtualFile) {
|
|
nr = virtualDirCallback.read(Fp, file_buf, n);
|
|
num_read = (size_t)nr;
|
|
} else {
|
|
num_read = fread(file_buf, (size_t)1, n, Fp);
|
|
}
|
|
amount_to_be_read -= num_read;
|
|
if (Instr->ReadSendSize < 0) {
|
|
/* read until close */
|
|
amount_to_be_read = Data_Buf_Size;
|
|
}
|
|
} else {
|
|
num_read = fread(file_buf, (size_t)1, Data_Buf_Size, Fp);
|
|
}
|
|
if (num_read == (size_t)0) {
|
|
/* EOF so no more to send. */
|
|
if (Instr && Instr->IsChunkActive) {
|
|
const char *str = "0\r\n\r\n";
|
|
nw = sock_write(info, str,
|
|
strlen(str),
|
|
TimeOut);
|
|
} else {
|
|
RetVal = UPNP_E_FILE_READ_ERROR;
|
|
}
|
|
goto Cleanup_File;
|
|
}
|
|
/* Create chunk for the current buffer. */
|
|
if (Instr && Instr->IsChunkActive) {
|
|
int rc;
|
|
/* Copy CRLF at the end of the chunk */
|
|
memcpy(file_buf + num_read, "\r\n", (size_t)2);
|
|
/* Hex length for the chunk size. */
|
|
memset(Chunk_Header, 0,
|
|
sizeof(Chunk_Header));
|
|
rc = snprintf(Chunk_Header,
|
|
sizeof(Chunk_Header),
|
|
"%" PRIzx "\r\n", num_read);
|
|
if (rc < 0 || (unsigned int) rc >= sizeof(Chunk_Header)) {
|
|
RetVal = UPNP_E_INTERNAL_ERROR;
|
|
goto Cleanup_File;
|
|
}
|
|
/* Copy the chunk size header */
|
|
memcpy(file_buf - strlen(Chunk_Header),
|
|
Chunk_Header,
|
|
strlen(Chunk_Header));
|
|
/* on the top of the buffer. */
|
|
/*file_buf[num_read+strlen(Chunk_Header)] = NULL; */
|
|
/*printf("Sending %s\n",file_buf-strlen(Chunk_Header)); */
|
|
nw = sock_write(info,
|
|
file_buf - strlen(Chunk_Header),
|
|
num_read + strlen(Chunk_Header) + (size_t)2,
|
|
TimeOut);
|
|
num_written = (size_t)nw;
|
|
if (nw <= 0 || num_written != num_read + strlen(Chunk_Header) + (size_t)2)
|
|
/* Send error nothing we can do. */
|
|
goto Cleanup_File;
|
|
} else {
|
|
/* write data */
|
|
nw = sock_write(info, file_buf, num_read, TimeOut);
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
">>> (SENT) >>>\n%.*s\n------------\n",
|
|
nw, file_buf);
|
|
/* Send error nothing we can do */
|
|
num_written = (size_t)nw;
|
|
if (nw <= 0 || num_written != num_read) {
|
|
goto Cleanup_File;
|
|
}
|
|
}
|
|
} /* while */
|
|
Cleanup_File:
|
|
if (Instr && Instr->IsVirtualFile) {
|
|
virtualDirCallback.close(Fp);
|
|
} else {
|
|
fclose(Fp);
|
|
}
|
|
goto ExitFunction;
|
|
} else
|
|
#endif /* EXCLUDE_WEB_SERVER */
|
|
if (c == 'b') {
|
|
/* memory buffer */
|
|
buf = va_arg(argp, char *);
|
|
buf_length = va_arg(argp, size_t);
|
|
if (buf_length > (size_t)0) {
|
|
nw = sock_write(info, buf, buf_length, TimeOut);
|
|
num_written = (size_t)nw;
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
">>> (SENT) >>>\n"
|
|
"%.*s\nbuf_length=%" PRIzd ", num_written=%" PRIzd "\n"
|
|
"------------\n",
|
|
(int)buf_length, buf, buf_length, num_written);
|
|
if (num_written != buf_length) {
|
|
RetVal = 0;
|
|
goto ExitFunction;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ExitFunction:
|
|
va_end(argp);
|
|
#if EXCLUDE_WEB_SERVER == 0
|
|
free(ChunkBuf);
|
|
#endif /* EXCLUDE_WEB_SERVER */
|
|
return RetVal;
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
* Function: http_RequestAndResponse
|
|
*
|
|
* Parameters:
|
|
* IN uri_type* destination; Destination URI object which contains
|
|
* remote IP address among other elements
|
|
* IN const char* request; Request to be sent
|
|
* IN size_t request_length; Length of the request
|
|
* IN http_method_t req_method; HTTP Request method
|
|
* IN int timeout_secs; time out value
|
|
* OUT http_parser_t* response; Parser object to receive the repsonse
|
|
*
|
|
* Description:
|
|
* Initiates socket, connects to the destination, sends a
|
|
* request and waits for the response from the remote end
|
|
*
|
|
* Returns:
|
|
* UPNP_E_SOCKET_ERROR
|
|
* UPNP_E_SOCKET_CONNECT
|
|
* Error Codes returned by http_SendMessage
|
|
* Error Codes returned by http_RecvMessage
|
|
************************************************************************/
|
|
int http_RequestAndResponse(
|
|
IN uri_type *destination,
|
|
IN const char *request,
|
|
IN size_t request_length,
|
|
IN http_method_t req_method,
|
|
IN int timeout_secs,
|
|
OUT http_parser_t *response)
|
|
{
|
|
SOCKET tcp_connection;
|
|
int ret_code;
|
|
size_t sockaddr_len;
|
|
int http_error_code;
|
|
SOCKINFO info;
|
|
|
|
tcp_connection = socket(
|
|
(int)destination->hostport.IPaddress.ss_family, SOCK_STREAM, 0);
|
|
if (tcp_connection == INVALID_SOCKET) {
|
|
parser_response_init(response, req_method);
|
|
return UPNP_E_SOCKET_ERROR;
|
|
}
|
|
if (sock_init(&info, tcp_connection) != UPNP_E_SUCCESS) {
|
|
parser_response_init(response, req_method);
|
|
ret_code = UPNP_E_SOCKET_ERROR;
|
|
goto end_function;
|
|
}
|
|
/* connect */
|
|
sockaddr_len = destination->hostport.IPaddress.ss_family == AF_INET6 ?
|
|
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
|
|
ret_code = private_connect(info.socket,
|
|
(struct sockaddr *)&(destination->hostport.IPaddress),
|
|
(socklen_t)sockaddr_len);
|
|
if (ret_code == -1) {
|
|
parser_response_init(response, req_method);
|
|
ret_code = UPNP_E_SOCKET_CONNECT;
|
|
goto end_function;
|
|
}
|
|
/* send request */
|
|
ret_code = http_SendMessage(&info, &timeout_secs, "b",
|
|
request, request_length);
|
|
if (ret_code != 0) {
|
|
parser_response_init(response, req_method);
|
|
goto end_function;
|
|
}
|
|
/* recv response */
|
|
ret_code = http_RecvMessage(&info, response, req_method,
|
|
&timeout_secs, &http_error_code);
|
|
|
|
end_function:
|
|
/* should shutdown completely */
|
|
sock_destroy(&info, SD_BOTH);
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
* Function: http_Download
|
|
*
|
|
* Parameters:
|
|
* IN const char* url_str; String as a URL
|
|
* IN int timeout_secs; time out value
|
|
* OUT char** document; buffer to store the document extracted
|
|
* from the donloaded message.
|
|
* OUT int* doc_length; length of the extracted document
|
|
* OUT char* content_type; Type of content
|
|
*
|
|
* Description:
|
|
* Download the document message and extract the document
|
|
* from the message.
|
|
*
|
|
* Return: int
|
|
* UPNP_E_SUCCESS
|
|
* UPNP_E_INVALID_URL
|
|
************************************************************************/
|
|
int http_Download( IN const char *url_str,
|
|
IN int timeout_secs,
|
|
OUT char **document,
|
|
OUT size_t *doc_length,
|
|
OUT char *content_type )
|
|
{
|
|
int ret_code;
|
|
uri_type url;
|
|
char *msg_start;
|
|
char *entity_start;
|
|
char *hoststr;
|
|
char *temp;
|
|
http_parser_t response;
|
|
size_t msg_length;
|
|
size_t hostlen;
|
|
memptr ctype;
|
|
size_t copy_len;
|
|
membuffer request;
|
|
char *urlPath = alloca(strlen(url_str) + (size_t)1);
|
|
|
|
/*ret_code = parse_uri( (char*)url_str, strlen(url_str), &url ); */
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"DOWNLOAD URL : %s\n", url_str);
|
|
ret_code = http_FixStrUrl((char *)url_str, strlen(url_str), &url);
|
|
if (ret_code != UPNP_E_SUCCESS)
|
|
return ret_code;
|
|
/* make msg */
|
|
membuffer_init(&request);
|
|
memset(urlPath, 0, strlen(url_str) + (size_t)1);
|
|
strncpy(urlPath, url_str, strlen(url_str));
|
|
hoststr = strstr(urlPath, "//");
|
|
if (hoststr == NULL)
|
|
return UPNP_E_INVALID_URL;
|
|
hoststr += 2;
|
|
temp = strchr(hoststr, '/');
|
|
if (temp) {
|
|
*temp = '\0';
|
|
hostlen = strlen(hoststr);
|
|
*temp = '/';
|
|
} else {
|
|
hostlen = strlen(hoststr);
|
|
}
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"HOSTNAME : %s Length : %" PRIzu "\n", hoststr, hostlen);
|
|
ret_code = http_MakeMessage(&request, 1, 1,
|
|
"Q" "s" "bcDCUc",
|
|
HTTPMETHOD_GET, url.pathquery.buff,
|
|
url.pathquery.size, "HOST: ", hoststr,
|
|
hostlen);
|
|
if (ret_code != 0) {
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"HTTP Makemessage failed\n");
|
|
membuffer_destroy(&request);
|
|
return ret_code;
|
|
}
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"HTTP Buffer:\n%s\n" "----------END--------\n", request.buf);
|
|
/* get doc msg */
|
|
ret_code =
|
|
http_RequestAndResponse(&url, request.buf, request.length,
|
|
HTTPMETHOD_GET, timeout_secs, &response);
|
|
|
|
if (ret_code != 0) {
|
|
httpmsg_destroy(&response.msg);
|
|
membuffer_destroy(&request);
|
|
return ret_code;
|
|
}
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__, "Response\n");
|
|
print_http_headers(&response.msg);
|
|
/* optional content-type */
|
|
if (content_type) {
|
|
if (httpmsg_find_hdr(&response.msg, HDR_CONTENT_TYPE, &ctype) ==
|
|
NULL) {
|
|
*content_type = '\0'; /* no content-type */
|
|
} else {
|
|
/* safety */
|
|
copy_len = ctype.length < LINE_SIZE - (size_t)1 ?
|
|
ctype.length : LINE_SIZE - (size_t)1;
|
|
|
|
memcpy(content_type, ctype.buf, copy_len);
|
|
content_type[copy_len] = '\0';
|
|
}
|
|
}
|
|
/* extract doc from msg */
|
|
if ((*doc_length = response.msg.entity.length) == (size_t)0) {
|
|
/* 0-length msg */
|
|
*document = NULL;
|
|
} else if (response.msg.status_code == HTTP_OK) {
|
|
/*LEAK_FIX_MK */
|
|
/* copy entity */
|
|
entity_start = response.msg.entity.buf; /* what we want */
|
|
msg_length = response.msg.msg.length; /* save for posterity */
|
|
msg_start = membuffer_detach(&response.msg.msg); /* whole msg */
|
|
/* move entity to the start; copy null-terminator too */
|
|
memmove(msg_start, entity_start, *doc_length + (size_t)1);
|
|
/* save mem for body only */
|
|
*document = realloc(msg_start, *doc_length + (size_t)1); /*LEAK_FIX_MK */
|
|
/* *document = Realloc( msg_start,msg_length, *doc_length + 1 ); LEAK_FIX_MK */
|
|
/* shrink can't fail */
|
|
assert(msg_length > *doc_length);
|
|
assert(*document != NULL);
|
|
if (msg_length <= *doc_length || *document == NULL)
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"msg_length(%" PRIzu ") <= *doc_length(%"
|
|
PRIzu ") or document is NULL",
|
|
msg_length, *doc_length);
|
|
}
|
|
if (response.msg.status_code == HTTP_OK) {
|
|
ret_code = 0; /* success */
|
|
} else {
|
|
/* server sent error msg (not requested doc) */
|
|
ret_code = response.msg.status_code;
|
|
}
|
|
httpmsg_destroy(&response.msg);
|
|
membuffer_destroy(&request);
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
typedef struct HTTPPOSTHANDLE {
|
|
SOCKINFO sock_info;
|
|
int contentLength;
|
|
} http_post_handle_t;
|
|
|
|
/************************************************************************
|
|
* Function: MakePostMessage
|
|
*
|
|
* Parameters:
|
|
* const char *url_str; String as a URL
|
|
* membuffer *request; Buffer containing the request
|
|
* uri_type *url; URI object containing the scheme,
|
|
* path query token, etc.
|
|
* int contentLength; length of content
|
|
* const char *contentType; Type of content
|
|
*
|
|
* Description:
|
|
* Makes the message for the HTTP POST message
|
|
*
|
|
* Returns:
|
|
* UPNP_E_INVALID_URL
|
|
* UPNP_E_INVALID_PARAM
|
|
* UPNP_E_SUCCESS
|
|
************************************************************************/
|
|
int MakePostMessage(const char *url_str, membuffer *request,
|
|
uri_type *url, int contentLength, const char *contentType)
|
|
{
|
|
int ret_code = 0;
|
|
char *urlPath = alloca(strlen(url_str) + (size_t)1);
|
|
size_t hostlen = (size_t)0;
|
|
char *hoststr;
|
|
char *temp;
|
|
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"DOWNLOAD URL : %s\n", url_str);
|
|
ret_code = http_FixStrUrl((char *)url_str, strlen(url_str), url);
|
|
if (ret_code != UPNP_E_SUCCESS)
|
|
return ret_code;
|
|
/* make msg */
|
|
membuffer_init(request);
|
|
memset(urlPath, 0, strlen(url_str) + (size_t)1);
|
|
strncpy(urlPath, url_str, strlen(url_str));
|
|
hoststr = strstr(urlPath, "//");
|
|
if (hoststr == NULL)
|
|
return UPNP_E_INVALID_URL;
|
|
hoststr += 2;
|
|
temp = strchr(hoststr, '/');
|
|
if (temp == NULL)
|
|
return UPNP_E_INVALID_URL;
|
|
*temp = '\0';
|
|
hostlen = strlen(hoststr);
|
|
*temp = '/';
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"HOSTNAME : %s Length : %" PRIzu "\n", hoststr, hostlen);
|
|
if (contentLength >= 0)
|
|
ret_code = http_MakeMessage(request, 1, 1,
|
|
"Q" "s" "bcDCU" "T" "Nc",
|
|
HTTPMETHOD_POST,
|
|
url->pathquery.buff,
|
|
url->pathquery.size, "HOST: ",
|
|
hoststr, hostlen, contentType,
|
|
(off_t) contentLength);
|
|
else if (contentLength == UPNP_USING_CHUNKED)
|
|
ret_code = http_MakeMessage(request, 1, 1,
|
|
"Q" "s" "bcDCU" "TKc",
|
|
HTTPMETHOD_POST,
|
|
url->pathquery.buff,
|
|
url->pathquery.size, "HOST: ",
|
|
hoststr, hostlen, contentType);
|
|
else if (contentLength == UPNP_UNTIL_CLOSE)
|
|
ret_code = http_MakeMessage(request, 1, 1,
|
|
"Q" "s" "bcDCU" "Tc",
|
|
HTTPMETHOD_POST,
|
|
url->pathquery.buff,
|
|
url->pathquery.size, "HOST: ",
|
|
hoststr, hostlen, contentType);
|
|
else
|
|
ret_code = UPNP_E_INVALID_PARAM;
|
|
if (ret_code != 0) {
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"HTTP Makemessage failed\n");
|
|
membuffer_destroy(request);
|
|
return ret_code;
|
|
}
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"HTTP Buffer:\n%s\n" "----------END--------\n",
|
|
request->buf);
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: http_WriteHttpPost
|
|
*
|
|
* Parameters:
|
|
* IN void *Handle: Handle to the http post object
|
|
* IN char *buf: Buffer to send to peer, if format used
|
|
* is not UPNP_USING_CHUNKED,
|
|
* IN unsigned int *size: Size of the data to be sent.
|
|
* IN int timeout: time out value
|
|
*
|
|
* Description:
|
|
* Formats data if format used is UPNP_USING_CHUNKED.
|
|
* Writes data on the socket connected to the peer.
|
|
*
|
|
* Return: int
|
|
* UPNP_E_SUCCESS - On Success
|
|
* UPNP_E_INVALID_PARAM - Invalid Parameter
|
|
* -1 - On Socket Error.
|
|
************************************************************************/
|
|
int http_WriteHttpPost( IN void *Handle,
|
|
IN char *buf,
|
|
IN size_t *size,
|
|
IN int timeout )
|
|
{
|
|
http_post_handle_t *handle = (http_post_handle_t *)Handle;
|
|
char *tempbuf = NULL;
|
|
size_t tempbufSize = (size_t)0;
|
|
int freeTempbuf = 0;
|
|
int numWritten = 0;
|
|
|
|
if (!handle || !size || !buf) {
|
|
if (size)
|
|
*size = (size_t)0;
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
if (handle->contentLength == UPNP_USING_CHUNKED) {
|
|
if (*size) {
|
|
size_t tempSize = *size +
|
|
CHUNK_HEADER_SIZE + CHUNK_TAIL_SIZE;
|
|
tempbuf = malloc(tempSize);
|
|
if (!tempbuf)
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
/* begin chunk */
|
|
snprintf(tempbuf, tempSize, "%" PRIzx "\r\n", *size);
|
|
tempSize = strlen(tempbuf);
|
|
memcpy(tempbuf + tempSize, buf, *size);
|
|
memcpy(tempbuf + tempSize + *size, "\r\n", (size_t)2);
|
|
/* end of chunk */
|
|
tempbufSize = tempSize + *size + (size_t)2;
|
|
freeTempbuf = 1;
|
|
}
|
|
} else {
|
|
tempbuf = buf;
|
|
tempbufSize = *size;
|
|
}
|
|
numWritten =
|
|
sock_write(&handle->sock_info, tempbuf, tempbufSize, &timeout);
|
|
if (freeTempbuf)
|
|
free(tempbuf);
|
|
if (numWritten < 0) {
|
|
*size = (size_t)0;
|
|
return numWritten;
|
|
} else {
|
|
*size = (size_t)numWritten;
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: http_CloseHttpPost
|
|
*
|
|
* Parameters:
|
|
* IN void *Handle; Handle to the http post object
|
|
* IN OUT int *httpStatus; HTTP status returned on receiving a
|
|
* response message
|
|
* IN int timeout; time out value
|
|
*
|
|
* Description:
|
|
* Sends remaining data if using UPNP_USING_CHUNKED
|
|
* format. Receives any more messages. Destroys socket and any socket
|
|
* associated memory. Frees handle associated with the HTTP POST msg.
|
|
*
|
|
* Return: int
|
|
* UPNP_E_SUCCESS - On success
|
|
* UPNP_E_INVALID_PARAM - Invalid Parameter
|
|
************************************************************************/
|
|
int http_CloseHttpPost(IN void *Handle, IN OUT int *httpStatus, IN int timeout)
|
|
{
|
|
int retc = 0;
|
|
http_parser_t response;
|
|
int http_error_code;
|
|
const char *zcrlf = "0\r\n\r\n";
|
|
|
|
http_post_handle_t *handle = Handle;
|
|
if ((!handle) || (!httpStatus))
|
|
return UPNP_E_INVALID_PARAM;
|
|
if (handle->contentLength == UPNP_USING_CHUNKED)
|
|
/*send last chunk */
|
|
retc = sock_write(&handle->sock_info, zcrlf, strlen(zcrlf), &timeout);
|
|
/*read response */
|
|
parser_response_init(&response, HTTPMETHOD_POST);
|
|
retc = http_RecvMessage(&handle->sock_info, &response,
|
|
HTTPMETHOD_POST, &timeout, &http_error_code);
|
|
*httpStatus = http_error_code;
|
|
/*should shutdown completely */
|
|
sock_destroy(&handle->sock_info, SD_BOTH);
|
|
httpmsg_destroy(&response.msg);
|
|
free(handle);
|
|
|
|
return retc;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: http_OpenHttpPost
|
|
*
|
|
* Parameters:
|
|
* IN const char *url_str; String as a URL
|
|
* IN OUT void **Handle; Pointer to buffer to store HTTP
|
|
* post handle
|
|
* IN const char *contentType; Type of content
|
|
* IN int contentLength; length of content
|
|
* IN int timeout; time out value
|
|
*
|
|
* Description:
|
|
* Makes the HTTP POST message, connects to the peer,
|
|
* sends the HTTP POST request. Adds the post handle to buffer of
|
|
* such handles
|
|
*
|
|
* Return : int;
|
|
* UPNP_E_SUCCESS - On success
|
|
* UPNP_E_INVALID_PARAM - Invalid Parameter
|
|
* UPNP_E_OUTOF_MEMORY
|
|
* UPNP_E_SOCKET_ERROR
|
|
* UPNP_E_SOCKET_CONNECT
|
|
************************************************************************/
|
|
int http_OpenHttpPost(
|
|
IN const char *url_str,
|
|
IN OUT void **Handle,
|
|
IN const char *contentType,
|
|
IN int contentLength,
|
|
IN int timeout)
|
|
{
|
|
int ret_code;
|
|
size_t sockaddr_len;
|
|
SOCKET tcp_connection;
|
|
membuffer request;
|
|
http_post_handle_t *handle = NULL;
|
|
uri_type url;
|
|
|
|
if (!url_str || !Handle || !contentType)
|
|
return UPNP_E_INVALID_PARAM;
|
|
*Handle = handle;
|
|
ret_code = MakePostMessage(url_str, &request, &url,
|
|
contentLength, contentType);
|
|
if (ret_code != UPNP_E_SUCCESS)
|
|
return ret_code;
|
|
handle = malloc(sizeof(http_post_handle_t));
|
|
if (!handle)
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
handle->contentLength = contentLength;
|
|
tcp_connection = socket((int)url.hostport.IPaddress.ss_family,
|
|
SOCK_STREAM, 0);
|
|
if (tcp_connection == INVALID_SOCKET) {
|
|
ret_code = UPNP_E_SOCKET_ERROR;
|
|
goto errorHandler;
|
|
}
|
|
if (sock_init(&handle->sock_info, tcp_connection) != UPNP_E_SUCCESS) {
|
|
sock_destroy(&handle->sock_info, SD_BOTH);
|
|
ret_code = UPNP_E_SOCKET_ERROR;
|
|
goto errorHandler;
|
|
}
|
|
sockaddr_len = url.hostport.IPaddress.ss_family == AF_INET6 ?
|
|
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
|
|
ret_code = private_connect(handle->sock_info.socket,
|
|
(struct sockaddr *)&(url.hostport.IPaddress),
|
|
(socklen_t)sockaddr_len);
|
|
if (ret_code == -1) {
|
|
sock_destroy(&handle->sock_info, SD_BOTH);
|
|
ret_code = UPNP_E_SOCKET_CONNECT;
|
|
goto errorHandler;
|
|
}
|
|
/* send request */
|
|
ret_code = http_SendMessage(&handle->sock_info, &timeout, "b",
|
|
request.buf, request.length);
|
|
if (ret_code != 0)
|
|
sock_destroy(&handle->sock_info, SD_BOTH);
|
|
|
|
errorHandler:
|
|
membuffer_destroy(&request);
|
|
*Handle = handle;
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
typedef struct HTTPGETHANDLE {
|
|
http_parser_t response;
|
|
SOCKINFO sock_info;
|
|
int entity_offset;
|
|
int cancel;
|
|
} http_get_handle_t;
|
|
|
|
/************************************************************************
|
|
* Function: MakeGetMessage
|
|
*
|
|
* Parameters:
|
|
* const char *url_str ; String as a URL
|
|
* const char *proxy_str ; String as a URL of proxy to use
|
|
* membuffer *request ; Buffer containing the request
|
|
* uri_type *url ; URI object containing the scheme, path
|
|
* query token, etc.
|
|
*
|
|
* Description:
|
|
* Makes the message for the HTTP GET method
|
|
*
|
|
* Returns:
|
|
* UPNP_E_INVALID_URL
|
|
* Error Codes returned by http_MakeMessage
|
|
* UPNP_E_SUCCESS
|
|
************************************************************************/
|
|
int MakeGetMessage(const char *url_str, const char *proxy_str,
|
|
membuffer *request, uri_type *url)
|
|
{
|
|
int ret_code;
|
|
char *urlPath = alloca(strlen(url_str) + (size_t)1);
|
|
size_t querylen = (size_t)0;
|
|
const char *querystr;
|
|
size_t hostlen = (size_t)0;
|
|
char *hoststr, *temp;
|
|
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"DOWNLOAD URL : %s\n", url_str);
|
|
ret_code = http_FixStrUrl((char *)url_str, strlen(url_str), url);
|
|
if (ret_code != UPNP_E_SUCCESS)
|
|
return ret_code;
|
|
/* make msg */
|
|
membuffer_init(request);
|
|
memset(urlPath, 0, strlen(url_str) + (size_t)1);
|
|
strncpy(urlPath, url_str, strlen(url_str));
|
|
hoststr = strstr(urlPath, "//");
|
|
if (hoststr == NULL)
|
|
return UPNP_E_INVALID_URL;
|
|
hoststr += 2;
|
|
temp = strchr(hoststr, '/');
|
|
if (temp == NULL)
|
|
return UPNP_E_INVALID_URL;
|
|
*temp = '\0';
|
|
hostlen = strlen(hoststr);
|
|
*temp = '/';
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"HOSTNAME : %s Length : %" PRIzu "\n", hoststr, hostlen);
|
|
if (proxy_str) {
|
|
querystr = url_str;
|
|
querylen = strlen(querystr);
|
|
} else {
|
|
querystr = url->pathquery.buff;
|
|
querylen = url->pathquery.size;
|
|
}
|
|
ret_code = http_MakeMessage(request, 1, 1,
|
|
"Q" "s" "bcDCUc",
|
|
HTTPMETHOD_GET, querystr, querylen,
|
|
"HOST: ", hoststr, hostlen);
|
|
if (ret_code != 0) {
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"HTTP Makemessage failed\n");
|
|
membuffer_destroy(request);
|
|
|
|
return ret_code;
|
|
}
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"HTTP Buffer:\n%s\n" "----------END--------\n",
|
|
request->buf);
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
/*!
|
|
* \brief Parses already exiting data. If not complete reads more
|
|
* data on the connected socket. The read data is then parsed. The
|
|
* same methid is carried out for headers.
|
|
*
|
|
* \return integer:
|
|
* \li \c PARSE_OK - On Success
|
|
* \li \c PARSE_FAILURE - Failure to parse data correctly
|
|
* \li \c UPNP_E_BAD_HTTPMSG - Socker read() returns an error
|
|
*/
|
|
static int ReadResponseLineAndHeaders(
|
|
/*! Socket information object. */
|
|
IN SOCKINFO *info,
|
|
/*! HTTP Parser object. */
|
|
IN OUT http_parser_t *parser,
|
|
/*! Time out value. */
|
|
IN OUT int *timeout_secs,
|
|
/*! HTTP errror code returned. */
|
|
IN OUT int *http_error_code)
|
|
{
|
|
parse_status_t status;
|
|
int num_read;
|
|
char buf[2 * 1024];
|
|
int done = 0;
|
|
int ret_code = 0;
|
|
|
|
/*read response line */
|
|
status = parser_parse_responseline(parser);
|
|
switch (status) {
|
|
case PARSE_OK:
|
|
done = 1;
|
|
break;
|
|
case PARSE_INCOMPLETE:
|
|
done = 0;
|
|
break;
|
|
default:
|
|
/*error */
|
|
return status;
|
|
}
|
|
while (!done) {
|
|
num_read = sock_read(info, buf, sizeof(buf), timeout_secs);
|
|
if (num_read > 0) {
|
|
/* append data to buffer */
|
|
ret_code =
|
|
membuffer_append(&parser->msg.msg, buf, (size_t)num_read);
|
|
if (ret_code != 0) {
|
|
/* set failure status */
|
|
parser->http_error_code =
|
|
HTTP_INTERNAL_SERVER_ERROR;
|
|
return PARSE_FAILURE;
|
|
}
|
|
status = parser_parse_responseline(parser);
|
|
switch (status) {
|
|
case PARSE_OK:
|
|
done = 1;
|
|
break;
|
|
case PARSE_INCOMPLETE:
|
|
done = 0;
|
|
break;
|
|
default:
|
|
/*error */
|
|
return status;
|
|
}
|
|
} else if (num_read == 0) {
|
|
/* partial msg */
|
|
*http_error_code = HTTP_BAD_REQUEST; /* or response */
|
|
return UPNP_E_BAD_HTTPMSG;
|
|
} else {
|
|
*http_error_code = parser->http_error_code;
|
|
return num_read;
|
|
}
|
|
}
|
|
status = parser_parse_headers(parser);
|
|
if ((status == (parse_status_t)PARSE_OK) &&
|
|
(parser->position == (parser_pos_t)POS_ENTITY))
|
|
done = 1;
|
|
else if (status == (parse_status_t)PARSE_INCOMPLETE)
|
|
done = 0;
|
|
else
|
|
/*error */
|
|
return status;
|
|
/*read headers */
|
|
while (!done) {
|
|
num_read = sock_read(info, buf, sizeof(buf), timeout_secs);
|
|
if (num_read > 0) {
|
|
/* append data to buffer */
|
|
ret_code =
|
|
membuffer_append(&parser->msg.msg, buf, (size_t)num_read);
|
|
if (ret_code != 0) {
|
|
/* set failure status */
|
|
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
|
|
return PARSE_FAILURE;
|
|
}
|
|
status = parser_parse_headers(parser);
|
|
if (status == (parse_status_t)PARSE_OK &&
|
|
parser->position == (parser_pos_t)POS_ENTITY)
|
|
done = 1;
|
|
else if (status == (parse_status_t)PARSE_INCOMPLETE)
|
|
done = 0;
|
|
else
|
|
/*error */
|
|
return status;
|
|
} else if (num_read == 0) {
|
|
/* partial msg */
|
|
*http_error_code = HTTP_BAD_REQUEST; /* or response */
|
|
return UPNP_E_BAD_HTTPMSG;
|
|
} else {
|
|
*http_error_code = parser->http_error_code;
|
|
return num_read;
|
|
}
|
|
}
|
|
|
|
return PARSE_OK;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: http_ReadHttpGet
|
|
*
|
|
* Parameters:
|
|
* IN void *Handle; Handle to the HTTP get object
|
|
* IN OUT char *buf; Buffer to get the read and parsed data
|
|
* IN OUT size_t *size; Size of the buffer passed
|
|
* IN int timeout; time out value
|
|
*
|
|
* Description:
|
|
* Parses already existing data, then gets new data.
|
|
* Parses and extracts information from the new data.
|
|
*
|
|
* Return: int
|
|
* UPNP_E_SUCCESS - On success
|
|
* UPNP_E_INVALID_PARAM - Invalid Parameter
|
|
* UPNP_E_BAD_RESPONSE
|
|
* UPNP_E_BAD_HTTPMSG
|
|
* UPNP_E_CANCELED
|
|
************************************************************************/
|
|
int http_ReadHttpGet(
|
|
IN void *Handle,
|
|
IN OUT char *buf,
|
|
IN OUT size_t *size,
|
|
IN int timeout)
|
|
{
|
|
http_get_handle_t *handle = Handle;
|
|
parse_status_t status;
|
|
int num_read;
|
|
int ok_on_close = FALSE;
|
|
char tempbuf[2 * 1024];
|
|
int ret_code = 0;
|
|
|
|
if (!handle || !size || (*size > (size_t)0 && !buf)) {
|
|
if (size)
|
|
*size = (size_t)0;
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
/* first parse what has already been gotten */
|
|
if (handle->response.position != (parser_pos_t)POS_COMPLETE)
|
|
status = parser_parse_entity(&handle->response);
|
|
else
|
|
status = PARSE_SUCCESS;
|
|
switch (status) {
|
|
case PARSE_INCOMPLETE_ENTITY:
|
|
/* read until close */
|
|
ok_on_close = TRUE;
|
|
break;
|
|
case PARSE_SUCCESS:
|
|
case PARSE_CONTINUE_1:
|
|
case PARSE_INCOMPLETE:
|
|
break;
|
|
default:
|
|
/*error */
|
|
*size = (size_t)0;
|
|
return UPNP_E_BAD_RESPONSE;
|
|
}
|
|
/* read more if necessary entity */
|
|
while (handle->response.msg.amount_discarded + *size >
|
|
handle->response.msg.entity.length &&
|
|
!handle->cancel &&
|
|
handle->response.position != (parser_pos_t)POS_COMPLETE) {
|
|
num_read = sock_read(&handle->sock_info, tempbuf,
|
|
sizeof(tempbuf), &timeout);
|
|
if (num_read > 0) {
|
|
/* append data to buffer */
|
|
ret_code = membuffer_append(&handle->response.msg.msg,
|
|
tempbuf, (size_t)num_read);
|
|
if (ret_code != 0) {
|
|
/* set failure status */
|
|
handle->response.http_error_code =
|
|
HTTP_INTERNAL_SERVER_ERROR;
|
|
*size = (size_t)0;
|
|
return PARSE_FAILURE;
|
|
}
|
|
status = parser_parse_entity(&handle->response);
|
|
switch (status) {
|
|
case PARSE_INCOMPLETE_ENTITY:
|
|
/* read until close */
|
|
ok_on_close = TRUE;
|
|
break;
|
|
case PARSE_SUCCESS:
|
|
case PARSE_CONTINUE_1:
|
|
case PARSE_INCOMPLETE:
|
|
break;
|
|
default:
|
|
/*error */
|
|
*size = (size_t)0;
|
|
return UPNP_E_BAD_RESPONSE;
|
|
}
|
|
} else if (num_read == 0) {
|
|
if (ok_on_close) {
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"<<< (RECVD) <<<\n%s\n-----------------\n",
|
|
handle->response.msg.msg.buf);
|
|
handle->response.position = POS_COMPLETE;
|
|
} else {
|
|
/* partial msg */
|
|
*size = (size_t)0;
|
|
handle->response.http_error_code = HTTP_BAD_REQUEST; /* or response */
|
|
return UPNP_E_BAD_HTTPMSG;
|
|
}
|
|
} else {
|
|
*size = (size_t)0;
|
|
return num_read;
|
|
}
|
|
}
|
|
if (handle->cancel) {
|
|
return UPNP_E_CANCELED;
|
|
}
|
|
/* truncate size to fall within available data */
|
|
if (handle->response.msg.amount_discarded + *size >
|
|
handle->response.msg.entity.length)
|
|
*size = handle->response.msg.entity.length -
|
|
handle->response.msg.amount_discarded;
|
|
/* copy data to user buffer. delete copied data */
|
|
if (*size > (size_t)0) {
|
|
memcpy(buf, &handle->response.msg.msg.buf[handle->response.entity_start_position],
|
|
*size);
|
|
membuffer_delete(&handle->response.msg.msg,
|
|
handle->response.entity_start_position, *size);
|
|
/* update scanner position. needed for chunked transfers */
|
|
handle->response.scanner.cursor -= *size;
|
|
/* update amount discarded */
|
|
handle->response.msg.amount_discarded += *size;
|
|
}
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: http_HttpGetProgress
|
|
*
|
|
* Parameters:
|
|
* IN void *Handle; Handle to the HTTP get object
|
|
* OUT size_t *length; Buffer to get the read and parsed data
|
|
* OUT size_t *total; Size of tge buffer passed
|
|
*
|
|
* Description:
|
|
* Extracts information from the Handle to the HTTP get object.
|
|
*
|
|
* Return: int
|
|
* UPNP_E_SUCCESS - On Sucess
|
|
* UPNP_E_INVALID_PARAM - Invalid Parameter
|
|
************************************************************************/
|
|
int http_HttpGetProgress(
|
|
IN void *Handle,
|
|
OUT size_t *length,
|
|
OUT size_t *total)
|
|
{
|
|
http_get_handle_t *handle = Handle;
|
|
|
|
if (!handle || !length || !total) {
|
|
return UPNP_E_INVALID_PARAM;
|
|
}
|
|
*length = handle->response.msg.entity.length;
|
|
*total = handle->response.content_length;
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: http_CancelHttpGet
|
|
*
|
|
* Parameters:
|
|
* IN void *Handle; Handle to HTTP get object
|
|
*
|
|
* Description:
|
|
* Set the cancel flag of the HttpGet handle
|
|
*
|
|
* Return: int
|
|
* UPNP_E_SUCCESS - On Success
|
|
* UPNP_E_INVALID_PARAM - Invalid Parameter
|
|
************************************************************************/
|
|
int http_CancelHttpGet(IN void *Handle)
|
|
{
|
|
http_get_handle_t *handle = Handle;
|
|
|
|
if (!handle)
|
|
return UPNP_E_INVALID_PARAM;
|
|
handle->cancel = 1;
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
* Function: http_CloseHttpGet
|
|
*
|
|
* Parameters:
|
|
* IN void *Handle; Handle to HTTP get object
|
|
*
|
|
* Description:
|
|
* Clears the handle allocated for the HTTP GET operation
|
|
* Clears socket states and memory allocated for socket operations.
|
|
*
|
|
* Return: int
|
|
* UPNP_E_SUCCESS - On Success
|
|
* UPNP_E_INVALID_PARAM - Invalid Parameter
|
|
************************************************************************/
|
|
int http_CloseHttpGet(IN void *Handle)
|
|
{
|
|
http_get_handle_t *handle = Handle;
|
|
|
|
if (!handle)
|
|
return UPNP_E_INVALID_PARAM;
|
|
/*should shutdown completely */
|
|
sock_destroy(&handle->sock_info, SD_BOTH);
|
|
httpmsg_destroy(&handle->response.msg);
|
|
free(handle);
|
|
|
|
return UPNP_E_SUCCESS;
|
|
}
|
|
|
|
int http_OpenHttpGet( IN const char *url_str,
|
|
IN OUT void **Handle,
|
|
IN OUT char **contentType,
|
|
OUT int *contentLength,
|
|
OUT int *httpStatus,
|
|
IN int timeout)
|
|
{
|
|
return http_OpenHttpGetProxy(
|
|
url_str, NULL, Handle, contentType, contentLength, httpStatus,
|
|
timeout);
|
|
}
|
|
|
|
int http_OpenHttpGetProxy(const char *url_str, const char *proxy_str,
|
|
void **Handle, char **contentType, int *contentLength,
|
|
int *httpStatus, int timeout)
|
|
{
|
|
int ret_code;
|
|
size_t sockaddr_len;
|
|
int http_error_code;
|
|
memptr ctype;
|
|
SOCKET tcp_connection;
|
|
membuffer request;
|
|
http_get_handle_t *handle = NULL;
|
|
uri_type url;
|
|
uri_type proxy;
|
|
uri_type *peer;
|
|
parse_status_t status;
|
|
|
|
if (!url_str || !Handle || !contentType || !httpStatus)
|
|
return UPNP_E_INVALID_PARAM;
|
|
*httpStatus = 0;
|
|
*Handle = handle;
|
|
*contentType = NULL;
|
|
*contentLength = 0;
|
|
ret_code = MakeGetMessage(url_str, proxy_str, &request, &url);
|
|
if (ret_code != UPNP_E_SUCCESS)
|
|
return ret_code;
|
|
if (proxy_str) {
|
|
ret_code =
|
|
http_FixStrUrl((char *)proxy_str, strlen(proxy_str),
|
|
&proxy);
|
|
peer = &proxy;
|
|
} else {
|
|
peer = &url;
|
|
}
|
|
handle = malloc(sizeof(http_get_handle_t));
|
|
if (!handle)
|
|
return UPNP_E_OUTOF_MEMORY;
|
|
handle->cancel = 0;
|
|
parser_response_init(&handle->response, HTTPMETHOD_GET);
|
|
tcp_connection =
|
|
socket((int)peer->hostport.IPaddress.ss_family, SOCK_STREAM, 0);
|
|
if (tcp_connection == INVALID_SOCKET) {
|
|
ret_code = UPNP_E_SOCKET_ERROR;
|
|
goto errorHandler;
|
|
}
|
|
if (sock_init(&handle->sock_info, tcp_connection) != UPNP_E_SUCCESS) {
|
|
sock_destroy(&handle->sock_info, SD_BOTH);
|
|
ret_code = UPNP_E_SOCKET_ERROR;
|
|
goto errorHandler;
|
|
}
|
|
sockaddr_len = peer->hostport.IPaddress.ss_family == AF_INET6 ?
|
|
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
|
|
ret_code = private_connect(handle->sock_info.socket,
|
|
(struct sockaddr *)&(peer->hostport.IPaddress),
|
|
(socklen_t) sockaddr_len);
|
|
if (ret_code == -1) {
|
|
sock_destroy(&handle->sock_info, SD_BOTH);
|
|
ret_code = UPNP_E_SOCKET_CONNECT;
|
|
goto errorHandler;
|
|
}
|
|
/* send request */
|
|
ret_code = http_SendMessage(&handle->sock_info, &timeout, "b",
|
|
request.buf, request.length);
|
|
if (ret_code) {
|
|
sock_destroy(&handle->sock_info, SD_BOTH);
|
|
goto errorHandler;
|
|
}
|
|
if (ReadResponseLineAndHeaders(&handle->sock_info,
|
|
&handle->response, &timeout,
|
|
&http_error_code) != (int)PARSE_OK) {
|
|
ret_code = UPNP_E_BAD_RESPONSE;
|
|
goto errorHandler;
|
|
}
|
|
status = parser_get_entity_read_method(&handle->response);
|
|
switch (status) {
|
|
case PARSE_CONTINUE_1:
|
|
case PARSE_SUCCESS:
|
|
break;
|
|
default:
|
|
ret_code = UPNP_E_BAD_RESPONSE;
|
|
goto errorHandler;
|
|
}
|
|
*httpStatus = handle->response.msg.status_code;
|
|
ret_code = UPNP_E_SUCCESS;
|
|
if (!httpmsg_find_hdr(&handle->response.msg, HDR_CONTENT_TYPE, &ctype))
|
|
/* no content-type */
|
|
*contentType = NULL;
|
|
else
|
|
*contentType = ctype.buf;
|
|
if (handle->response.position == (parser_pos_t)POS_COMPLETE)
|
|
*contentLength = 0;
|
|
else if (handle->response.ent_position == ENTREAD_USING_CHUNKED)
|
|
*contentLength = UPNP_USING_CHUNKED;
|
|
else if (handle->response.ent_position == ENTREAD_USING_CLEN)
|
|
*contentLength = (int)handle->response.content_length;
|
|
else if (handle->response.ent_position == ENTREAD_UNTIL_CLOSE)
|
|
*contentLength = UPNP_UNTIL_CLOSE;
|
|
|
|
errorHandler:
|
|
*Handle = handle;
|
|
membuffer_destroy(&request);
|
|
if (ret_code != UPNP_E_SUCCESS)
|
|
httpmsg_destroy(&handle->response.msg);
|
|
return ret_code;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: http_SendStatusResponse
|
|
*
|
|
* Parameters:
|
|
* IN SOCKINFO *info; Socket information object
|
|
* IN int http_status_code; error code returned while making
|
|
* or sending the response message
|
|
* IN int request_major_version; request major version
|
|
* IN int request_minor_version; request minor version
|
|
*
|
|
* Description:
|
|
* Generate a response message for the status query and send the
|
|
* status response.
|
|
*
|
|
* Return: int
|
|
* 0 -- success
|
|
* UPNP_E_OUTOF_MEMORY
|
|
* UPNP_E_SOCKET_WRITE
|
|
* UPNP_E_TIMEDOUT
|
|
************************************************************************/
|
|
int http_SendStatusResponse(IN SOCKINFO *info, IN int http_status_code,
|
|
IN int request_major_version, IN int request_minor_version)
|
|
{
|
|
int response_major, response_minor;
|
|
membuffer membuf;
|
|
int ret;
|
|
int timeout;
|
|
|
|
http_CalcResponseVersion(request_major_version, request_minor_version,
|
|
&response_major, &response_minor);
|
|
membuffer_init(&membuf);
|
|
membuf.size_inc = (size_t)70;
|
|
/* response start line */
|
|
ret = http_MakeMessage(&membuf, response_major, response_minor, "RSCB",
|
|
http_status_code, http_status_code);
|
|
if (ret == 0) {
|
|
timeout = HTTP_DEFAULT_TIMEOUT;
|
|
ret = http_SendMessage(info, &timeout, "b",
|
|
membuf.buf, membuf.length);
|
|
}
|
|
membuffer_destroy(&membuf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int http_MakeMessage(membuffer *buf, int http_major_version,
|
|
int http_minor_version, const char *fmt, ...)
|
|
{
|
|
char c;
|
|
char *s = NULL;
|
|
size_t num;
|
|
off_t bignum;
|
|
size_t length;
|
|
time_t *loc_time;
|
|
time_t curr_time;
|
|
struct tm date_storage;
|
|
struct tm *date;
|
|
const char *start_str;
|
|
const char *end_str;
|
|
int status_code;
|
|
const char *status_msg;
|
|
http_method_t method;
|
|
const char *method_str;
|
|
const char *url_str;
|
|
const char *temp_str;
|
|
uri_type url;
|
|
uri_type *uri_ptr;
|
|
int error_code = 0;
|
|
va_list argp;
|
|
char tempbuf[200];
|
|
const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
|
|
const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
|
|
"Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
|
|
int rc = 0;
|
|
|
|
memset(tempbuf, 0, sizeof(tempbuf));
|
|
va_start(argp, fmt);
|
|
while ((c = *fmt++)) {
|
|
if (c == 's') {
|
|
/* C string */
|
|
s = (char *)va_arg(argp, char *);
|
|
assert(s);
|
|
UpnpPrintf(UPNP_ALL, HTTP, __FILE__, __LINE__,
|
|
"Adding a string : %s\n", s);
|
|
if (membuffer_append(buf, s, strlen(s)))
|
|
goto error_handler;
|
|
} else if (c == 'K') {
|
|
/* Add Chunky header */
|
|
if (membuffer_append(buf, "TRANSFER-ENCODING: chunked\r\n",
|
|
strlen("Transfer-Encoding: chunked\r\n")))
|
|
goto error_handler;
|
|
} else if (c == 'G') {
|
|
/* Add Range header */
|
|
struct SendInstruction *RespInstr;
|
|
RespInstr = (struct SendInstruction *)
|
|
va_arg(argp, struct SendInstruction *);
|
|
assert(RespInstr);
|
|
/* connection header */
|
|
if (membuffer_append(buf, RespInstr->RangeHeader,
|
|
strlen(RespInstr->RangeHeader)))
|
|
goto error_handler;
|
|
} else if (c == 'b') {
|
|
/* mem buffer */
|
|
s = (char *)va_arg(argp, char *);
|
|
UpnpPrintf(UPNP_ALL, HTTP, __FILE__, __LINE__,
|
|
"Adding a char Buffer starting with: %c\n", (int)s[0]);
|
|
assert(s);
|
|
length = (size_t) va_arg(argp, size_t);
|
|
if (membuffer_append(buf, s, length))
|
|
goto error_handler;
|
|
} else if (c == 'c') {
|
|
/* crlf */
|
|
if (membuffer_append(buf, "\r\n", (size_t)2))
|
|
goto error_handler;
|
|
} else if (c == 'd') {
|
|
/* integer */
|
|
num = (size_t)va_arg(argp, int);
|
|
rc = snprintf(tempbuf, sizeof(tempbuf), "%" PRIzu, num);
|
|
if (rc < 0 || (unsigned int) rc >= sizeof(tempbuf) ||
|
|
membuffer_append(buf, tempbuf, strlen(tempbuf)))
|
|
goto error_handler;
|
|
} else if (c == 'h') {
|
|
/* off_t */
|
|
bignum = (off_t) va_arg(argp, off_t);
|
|
rc = snprintf(tempbuf, sizeof(tempbuf), "%" PRId64,
|
|
(int64_t) bignum);
|
|
if (rc < 0 || (unsigned int) rc >= sizeof(tempbuf) ||
|
|
membuffer_append(buf, tempbuf, strlen(tempbuf)))
|
|
goto error_handler;
|
|
} else if (c == 't' || c == 'D') {
|
|
/* date */
|
|
if (c == 'D') {
|
|
/* header */
|
|
start_str = "DATE: ";
|
|
end_str = "\r\n";
|
|
curr_time = time(NULL);
|
|
loc_time = &curr_time;
|
|
} else {
|
|
/* date value only */
|
|
start_str = end_str = "";
|
|
loc_time = (time_t *)va_arg(argp, time_t *);
|
|
}
|
|
assert(loc_time);
|
|
date = http_gmtime_r(loc_time, &date_storage);
|
|
if (date == NULL)
|
|
goto error_handler;
|
|
rc = snprintf(tempbuf, sizeof(tempbuf),
|
|
"%s%s, %02d %s %d %02d:%02d:%02d GMT%s",
|
|
start_str, &weekday_str[date->tm_wday * 4],
|
|
date->tm_mday, &month_str[date->tm_mon * 4],
|
|
date->tm_year + 1900, date->tm_hour,
|
|
date->tm_min, date->tm_sec, end_str);
|
|
if (rc < 0 || (unsigned int) rc >= sizeof(tempbuf) ||
|
|
membuffer_append(buf, tempbuf, strlen(tempbuf)))
|
|
goto error_handler;
|
|
} else if (c == 'L') {
|
|
/* Add CONTENT-LANGUAGE header only if WEB_SERVER_CONTENT_LANGUAGE */
|
|
/* is not empty and if Accept-Language header is not empty */
|
|
struct SendInstruction *RespInstr;
|
|
RespInstr = (struct SendInstruction *)
|
|
va_arg(argp, struct SendInstruction *);
|
|
assert(RespInstr);
|
|
if (strcmp(RespInstr->AcceptLanguageHeader, "") &&
|
|
strcmp(WEB_SERVER_CONTENT_LANGUAGE, "") &&
|
|
http_MakeMessage(buf, http_major_version,
|
|
http_minor_version, "ssc",
|
|
"CONTENT-LANGUAGE: ",
|
|
WEB_SERVER_CONTENT_LANGUAGE) != 0)
|
|
goto error_handler;
|
|
} else if (c == 'C') {
|
|
if ((http_major_version > 1) ||
|
|
(http_major_version == 1 && http_minor_version == 1)
|
|
) {
|
|
/* connection header */
|
|
if (membuffer_append_str(buf, "CONNECTION: close\r\n"))
|
|
goto error_handler;
|
|
}
|
|
} else if (c == 'N') {
|
|
/* content-length header */
|
|
bignum = (off_t) va_arg(argp, off_t);
|
|
assert(bignum >= 0);
|
|
if (http_MakeMessage(buf, http_major_version, http_minor_version,
|
|
"shc", "CONTENT-LENGTH: ", bignum) != 0)
|
|
goto error_handler;
|
|
} else if (c == 'S' || c == 'U') {
|
|
/* SERVER or USER-AGENT header */
|
|
temp_str = (c == 'S') ? "SERVER: " : "USER-AGENT: ";
|
|
get_sdk_info(tempbuf, sizeof(tempbuf));
|
|
if (http_MakeMessage(buf, http_major_version, http_minor_version,
|
|
"ss", temp_str, tempbuf) != 0)
|
|
goto error_handler;
|
|
} else if (c == 'X') {
|
|
/* C string */
|
|
s = (char *)va_arg(argp, char *);
|
|
assert(s);
|
|
if (membuffer_append_str(buf, "X-User-Agent: ") != 0)
|
|
goto error_handler;
|
|
if (membuffer_append(buf, s, strlen(s)) != 0)
|
|
goto error_handler;
|
|
} else if (c == 'R') {
|
|
/* response start line */
|
|
/* e.g.: 'HTTP/1.1 200 OK' code */
|
|
status_code = (int)va_arg(argp, int);
|
|
assert(status_code > 0);
|
|
rc = snprintf(tempbuf, sizeof(tempbuf), "HTTP/%d.%d %d ",
|
|
http_major_version, http_minor_version,
|
|
status_code);
|
|
/* str */
|
|
status_msg = http_get_code_text(status_code);
|
|
if (rc < 0 || (unsigned int) rc >= sizeof(tempbuf) ||
|
|
http_MakeMessage(buf, http_major_version, http_minor_version,
|
|
"ssc", tempbuf, status_msg) != 0)
|
|
goto error_handler;
|
|
} else if (c == 'B') {
|
|
/* body of a simple reply */
|
|
status_code = (int)va_arg(argp, int);
|
|
rc = snprintf(tempbuf, sizeof(tempbuf), "%s%d %s%s",
|
|
"<html><body><h1>",
|
|
status_code, http_get_code_text(status_code),
|
|
"</h1></body></html>");
|
|
if (rc < 0 || (unsigned int) rc >= sizeof(tempbuf))
|
|
goto error_handler;
|
|
bignum = (off_t)strlen(tempbuf);
|
|
if (http_MakeMessage(buf, http_major_version, http_minor_version,
|
|
"NTcs", bignum, /* content-length */
|
|
"text/html", /* content-type */
|
|
tempbuf) != 0 /* body */
|
|
)
|
|
goto error_handler;
|
|
} else if (c == 'Q') {
|
|
/* request start line */
|
|
/* GET /foo/bar.html HTTP/1.1\r\n */
|
|
method = (http_method_t) va_arg(argp, http_method_t);
|
|
method_str = method_to_str(method);
|
|
url_str = (const char *)va_arg(argp, const char *);
|
|
num = (size_t) va_arg(argp, size_t); /* length of url_str */
|
|
if (http_MakeMessage(buf, http_major_version, http_minor_version,
|
|
"ssbsdsdc", method_str, /* method */
|
|
" ", url_str, num, /* url */
|
|
" HTTP/", http_major_version, ".",
|
|
http_minor_version) != 0)
|
|
goto error_handler;
|
|
} else if (c == 'q') {
|
|
/* request start line and HOST header */
|
|
method = (http_method_t) va_arg(argp, http_method_t);
|
|
uri_ptr = (uri_type *) va_arg(argp, uri_type *);
|
|
assert(uri_ptr);
|
|
if (http_FixUrl(uri_ptr, &url) != 0) {
|
|
error_code = UPNP_E_INVALID_URL;
|
|
goto error_handler;
|
|
}
|
|
if (http_MakeMessage(buf, http_major_version, http_minor_version,
|
|
"Q" "sbc", method, url.pathquery.buff,
|
|
url.pathquery.size, "HOST: ",
|
|
url.hostport.text.buff,
|
|
url.hostport.text.size) != 0)
|
|
goto error_handler;
|
|
} else if (c == 'T') {
|
|
/* content type header */
|
|
temp_str = (const char *)va_arg(argp, const char *); /* type/subtype format */
|
|
if (http_MakeMessage(buf, http_major_version, http_minor_version,
|
|
"ssc", "CONTENT-TYPE: ", temp_str) != 0)
|
|
goto error_handler;
|
|
} else {
|
|
assert(0);
|
|
}
|
|
}
|
|
goto ExitFunction;
|
|
|
|
error_handler:
|
|
/* Default is out of memory error. */
|
|
if (!error_code)
|
|
error_code = UPNP_E_OUTOF_MEMORY;
|
|
membuffer_destroy(buf);
|
|
|
|
ExitFunction:
|
|
va_end(argp);
|
|
return error_code;
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
* Function: http_CalcResponseVersion
|
|
*
|
|
* Parameters:
|
|
* IN int request_major_vers; Request major version
|
|
* IN int request_minor_vers; Request minor version
|
|
* OUT int* response_major_vers; Response mojor version
|
|
* OUT int* response_minor_vers; Response minor version
|
|
*
|
|
* Description:
|
|
* Calculate HTTP response versions based on the request versions.
|
|
*
|
|
* Return: void
|
|
************************************************************************/
|
|
void http_CalcResponseVersion( IN int request_major_vers,
|
|
IN int request_minor_vers,
|
|
OUT int *response_major_vers,
|
|
OUT int *response_minor_vers)
|
|
{
|
|
if ((request_major_vers > 1) ||
|
|
(request_major_vers == 1 && request_minor_vers >= 1)) {
|
|
*response_major_vers = 1;
|
|
*response_minor_vers = 1;
|
|
} else {
|
|
*response_major_vers = request_major_vers;
|
|
*response_minor_vers = request_minor_vers;
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: MakeGetMessageEx
|
|
*
|
|
* Parameters:
|
|
* const char *url_str; String as a URL
|
|
* membuffer *request; Buffer containing the request
|
|
* uri_type *url; URI object containing the scheme, path
|
|
* query token, etc.
|
|
*
|
|
* Description:
|
|
* Makes the message for the HTTP GET method
|
|
*
|
|
* Returns:
|
|
* UPNP_E_INVALID_URL
|
|
* Error Codes returned by http_MakeMessage
|
|
* UPNP_E_SUCCESS
|
|
************************************************************************/
|
|
int MakeGetMessageEx( const char *url_str,
|
|
membuffer * request,
|
|
uri_type * url,
|
|
struct SendInstruction *pRangeSpecifier)
|
|
{
|
|
int errCode = UPNP_E_SUCCESS;
|
|
char *urlPath = NULL;
|
|
size_t hostlen = (size_t)0;
|
|
char *hoststr, *temp;
|
|
|
|
do {
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"DOWNLOAD URL : %s\n", url_str);
|
|
if ((errCode = http_FixStrUrl((char *)url_str,
|
|
strlen(url_str),
|
|
url)) != UPNP_E_SUCCESS) {
|
|
break;
|
|
}
|
|
/* make msg */
|
|
membuffer_init(request);
|
|
urlPath = alloca(strlen(url_str) + (size_t)1);
|
|
if (!urlPath) {
|
|
errCode = UPNP_E_OUTOF_MEMORY;
|
|
break;
|
|
}
|
|
memset(urlPath, 0, strlen(url_str) + (size_t)1);
|
|
strncpy(urlPath, url_str, strlen(url_str));
|
|
hoststr = strstr(urlPath, "//");
|
|
if (hoststr == NULL) {
|
|
errCode = UPNP_E_INVALID_URL;
|
|
break;
|
|
}
|
|
hoststr += 2;
|
|
temp = strchr(hoststr, '/');
|
|
if (temp == NULL) {
|
|
errCode = UPNP_E_INVALID_URL;
|
|
break;
|
|
}
|
|
*temp = '\0';
|
|
hostlen = strlen(hoststr);
|
|
*temp = '/';
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"HOSTNAME : %s Length : %" PRIzu "\n",
|
|
hoststr, hostlen);
|
|
errCode = http_MakeMessage(request, 1, 1,
|
|
"Q" "s" "bc" "GDCUc",
|
|
HTTPMETHOD_GET, url->pathquery.buff,
|
|
url->pathquery.size, "HOST: ",
|
|
hoststr, hostlen, pRangeSpecifier);
|
|
if (errCode != 0) {
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"HTTP Makemessage failed\n");
|
|
membuffer_destroy(request);
|
|
return errCode;
|
|
}
|
|
} while (0);
|
|
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"HTTP Buffer:\n%s\n" "----------END--------\n",
|
|
request->buf);
|
|
|
|
return errCode;
|
|
}
|
|
|
|
#define SIZE_RANGE_BUFFER 50
|
|
|
|
/************************************************************************
|
|
* Function: http_OpenHttpGetEx
|
|
*
|
|
* Parameters:
|
|
* IN const char *url_str; String as a URL
|
|
* IN OUT void **Handle; Pointer to buffer to store HTTP
|
|
* post handle
|
|
* IN OUT char **contentType; Type of content
|
|
* OUT int *contentLength; length of content
|
|
* OUT int *httpStatus; HTTP status returned on receiving a
|
|
* response message
|
|
* IN int timeout; time out value
|
|
*
|
|
* Description:
|
|
* Makes the HTTP GET message, connects to the peer,
|
|
* sends the HTTP GET request, gets the response and parses the
|
|
* response.
|
|
*
|
|
* Return: int
|
|
* UPNP_E_SUCCESS - On Success
|
|
* UPNP_E_INVALID_PARAM - Invalid Paramters
|
|
* UPNP_E_OUTOF_MEMORY
|
|
* UPNP_E_SOCKET_ERROR
|
|
* UPNP_E_BAD_RESPONSE
|
|
************************************************************************/
|
|
int http_OpenHttpGetEx(
|
|
IN const char *url_str,
|
|
IN OUT void **Handle,
|
|
IN OUT char **contentType,
|
|
OUT int *contentLength,
|
|
OUT int *httpStatus,
|
|
IN int lowRange,
|
|
IN int highRange,
|
|
IN int timeout)
|
|
{
|
|
int http_error_code;
|
|
memptr ctype;
|
|
SOCKET tcp_connection;
|
|
size_t sockaddr_len;
|
|
membuffer request;
|
|
http_get_handle_t *handle = NULL;
|
|
uri_type url;
|
|
parse_status_t status;
|
|
int errCode = UPNP_E_SUCCESS;
|
|
/* char rangeBuf[SIZE_RANGE_BUFFER]; */
|
|
struct SendInstruction rangeBuf;
|
|
int rc = 0;
|
|
|
|
membuffer_init(&request);
|
|
|
|
do {
|
|
/* Checking Input parameters */
|
|
if (!url_str || !Handle || !contentType || !httpStatus ) {
|
|
errCode = UPNP_E_INVALID_PARAM;
|
|
break;
|
|
}
|
|
/* Initialize output parameters */
|
|
*httpStatus = 0;
|
|
*Handle = handle;
|
|
*contentType = NULL;
|
|
*contentLength = 0;
|
|
if (lowRange > highRange) {
|
|
errCode = UPNP_E_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
memset(&rangeBuf, 0, sizeof(rangeBuf));
|
|
rc = snprintf(rangeBuf.RangeHeader, sizeof(rangeBuf.RangeHeader),
|
|
"Range: bytes=%d-%d\r\n", lowRange, highRange);
|
|
if (rc < 0 || (unsigned int) rc >= sizeof(rangeBuf.RangeHeader))
|
|
break;
|
|
membuffer_init(&request);
|
|
errCode = MakeGetMessageEx(url_str, &request, &url, &rangeBuf);
|
|
if (errCode != UPNP_E_SUCCESS)
|
|
break;
|
|
handle = (http_get_handle_t *)malloc(sizeof(http_get_handle_t));
|
|
if (!handle) {
|
|
errCode = UPNP_E_OUTOF_MEMORY;
|
|
break;
|
|
}
|
|
memset(handle, 0, sizeof(*handle));
|
|
parser_response_init(&handle->response, HTTPMETHOD_GET);
|
|
tcp_connection = socket((int)url.hostport.IPaddress.ss_family,
|
|
SOCK_STREAM, 0);
|
|
if (tcp_connection == INVALID_SOCKET) {
|
|
errCode = UPNP_E_SOCKET_ERROR;
|
|
free(handle);
|
|
break;
|
|
}
|
|
if (sock_init(&handle->sock_info, tcp_connection) != UPNP_E_SUCCESS) {
|
|
sock_destroy(&handle->sock_info, SD_BOTH);
|
|
errCode = UPNP_E_SOCKET_ERROR;
|
|
free(handle);
|
|
break;
|
|
}
|
|
sockaddr_len = url.hostport.IPaddress.ss_family == AF_INET6 ?
|
|
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
|
|
errCode = private_connect(handle->sock_info.socket,
|
|
(struct sockaddr *)&(url.hostport.IPaddress),
|
|
(socklen_t)sockaddr_len);
|
|
if (errCode == -1) {
|
|
sock_destroy(&handle->sock_info, SD_BOTH);
|
|
errCode = UPNP_E_SOCKET_CONNECT;
|
|
free(handle);
|
|
break;
|
|
}
|
|
/* send request */
|
|
errCode = http_SendMessage(&handle->sock_info, &timeout,
|
|
"b", request.buf, request.length);
|
|
if (errCode != UPNP_E_SUCCESS) {
|
|
sock_destroy(&handle->sock_info, SD_BOTH);
|
|
free(handle);
|
|
break;
|
|
}
|
|
if (ReadResponseLineAndHeaders(&handle->sock_info,
|
|
&handle->response, &timeout, &http_error_code) != (int)PARSE_OK) {
|
|
errCode = UPNP_E_BAD_RESPONSE;
|
|
free(handle);
|
|
break;
|
|
}
|
|
status = parser_get_entity_read_method(&handle->response);
|
|
if (status != (parse_status_t)PARSE_CONTINUE_1 &&
|
|
status != (parse_status_t)PARSE_SUCCESS) {
|
|
errCode = UPNP_E_BAD_RESPONSE;
|
|
free(handle);
|
|
break;
|
|
}
|
|
*httpStatus = handle->response.msg.status_code;
|
|
errCode = UPNP_E_SUCCESS;
|
|
|
|
if (!httpmsg_find_hdr(&handle->response.msg, HDR_CONTENT_TYPE, &ctype))
|
|
/* no content-type */
|
|
*contentType = NULL;
|
|
else
|
|
*contentType = ctype.buf;
|
|
if (handle->response.position == (parser_pos_t)POS_COMPLETE)
|
|
*contentLength = 0;
|
|
else if(handle->response.ent_position == ENTREAD_USING_CHUNKED)
|
|
*contentLength = UPNP_USING_CHUNKED;
|
|
else if(handle->response.ent_position == ENTREAD_USING_CLEN)
|
|
*contentLength = (int)handle->response.content_length;
|
|
else if(handle->response.ent_position == ENTREAD_UNTIL_CLOSE)
|
|
*contentLength = UPNP_UNTIL_CLOSE;
|
|
*Handle = handle;
|
|
} while (0);
|
|
|
|
membuffer_destroy(&request);
|
|
|
|
return errCode;
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
* Function: get_sdk_info
|
|
*
|
|
* Parameters:
|
|
* OUT char *info; buffer to store the operating system information
|
|
* IN size_t infoSize; size of buffer
|
|
*
|
|
* Description:
|
|
* Returns the server information for the operating system
|
|
*
|
|
* Return:
|
|
* UPNP_INLINE void
|
|
************************************************************************/
|
|
/* 'info' should have a size of at least 100 bytes */
|
|
void get_sdk_info(OUT char *info, IN size_t infoSize)
|
|
{
|
|
#ifdef UPNP_ENABLE_UNSPECIFIED_SERVER
|
|
snprintf(info, infoSize, "Unspecified, UPnP/1.0, Unspecified\r\n");
|
|
#else /* UPNP_ENABLE_UNSPECIFIED_SERVER */
|
|
#ifdef WIN32
|
|
OSVERSIONINFO versioninfo;
|
|
versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
|
|
if (GetVersionEx(&versioninfo) != 0)
|
|
snprintf(info, infoSize,
|
|
"%d.%d.%d %d/%s, UPnP/1.0, Portable SDK for UPnP devices/"
|
|
PACKAGE_VERSION "\r\n", versioninfo.dwMajorVersion,
|
|
versioninfo.dwMinorVersion, versioninfo.dwBuildNumber,
|
|
versioninfo.dwPlatformId, versioninfo.szCSDVersion);
|
|
else
|
|
*info = '\0';
|
|
#else
|
|
int ret_code;
|
|
struct utsname sys_info;
|
|
|
|
ret_code = uname(&sys_info);
|
|
if (ret_code == -1)
|
|
*info = '\0';
|
|
snprintf(info, infoSize,
|
|
"%s/%s, UPnP/1.0, Portable SDK for UPnP devices/"
|
|
PACKAGE_VERSION "\r\n", sys_info.sysname, sys_info.release);
|
|
#endif
|
|
#endif /* UPNP_ENABLE_UNSPECIFIED_SERVER */
|
|
}
|
|
|