libupnp/upnp/src/genlib/net/http/httpreadwrite.c
Philipp Matthias Hahn 848d66e69d Fix broken strncat(..., strlen())
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>
2014-09-22 11:19:25 -03:00

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 */
}