/******************************************************************************* * * Copyright (c) 2000-2003 Intel Corporation * 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. * ******************************************************************************/ /************************************************************************ * 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 "webserver.h" #include #include #ifndef UPNP_USE_BCBPP #ifndef UPNP_USE_MSVCPP #include #include #endif #endif #ifdef WIN32 #include #define fseeko fseek #else #include #include #include #include #include #include #include #include #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 10 #define CHUNK_TAIL_SIZE 10 #ifndef UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS /* in seconds */ #define DEFAULT_TCP_CONNECT_TIMEOUT 5 /************************************************************************ * Function : Make_Socket_NoBlocking * * Parameters: * IN int sock: socket * * Description: * This function makes socket non-blocking. * * Returns: int * 0 if successful else -1 ***************************************************************************/ static int Make_Socket_NoBlocking(SOCKET sock) { #ifdef WIN32 u_long val = 1; return ioctlsocket(sock, FIONBIO, &val); #else int val; val = fcntl(sock, F_GETFL, 0); if (fcntl(sock, F_SETFL, val | O_NONBLOCK) == -1) { return -1; } #endif return 0; } /************************************************************************ * Function : Make_Socket_Blocking * * Parameters: * IN int sock: socket * * Description: * This function makes socket blocking. * * Returns: int * 0 if successful else -1 ***************************************************************************/ static int Make_Socket_Blocking(int sock) { #ifdef WIN32 u_long val = 0; return ioctlsocket(sock, FIONBIO, &val); #else int val; val = fcntl(sock, F_GETFL, 0); if (fcntl(sock, F_SETFL, val & ~O_NONBLOCK) == -1) { return -1; } #endif return 0; } /************************************************************************ * Function : Check_Connect_And_Wait_Connection * * Parameters: * IN int sock: socket * IN int connect_res: result of connect * * Description: * This function checks socket connection and wait if it is not connected * It should be called just after connect * * Returns: int * 0 if successful else -1 ***************************************************************************/ static int Check_Connect_And_Wait_Connection(int sock, 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 = 0; 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 = Make_Socket_NoBlocking(sockfd); if (ret != - 1) { ret = connect(sockfd, serv_addr, addrlen); ret = Check_Connect_And_Wait_Connection(sockfd, ret); if (ret != - 1) { ret = Make_Socket_Blocking(sockfd); } } return ret; #else return connect(sockfd, serv_addr, addrlen); #endif /* UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS */ } /************************************************************************ * Function: http_FixUrl * * Parameters: * IN uri_type* url; URL to be validated and fixed * OUT uri_type* fixed_url; URL after being fixed. * * Description: * Validates URL * * Returns: * UPNP_E_INVALID_URL * UPNP_E_SUCCESS ************************************************************************/ int http_FixUrl(IN uri_type *url, OUT uri_type *fixed_url) { 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 == 0 ) { return UPNP_E_INVALID_URL; } /* set pathquery to "/" if it is empty */ if (fixed_url->pathquery.size == 0) { fixed_url->pathquery.buff = temp_path; fixed_url->pathquery.size = 1; } return UPNP_E_SUCCESS; } /************************************************************************ * Function: http_FixStrUrl * * Parameters: * IN const char* urlstr; Character string as a URL * IN int urlstrlen; Length of the character string * OUT uri_type* fixed_url; Fixed and corrected URL * * Description: * Parses URL and then validates URL * * Returns: * UPNP_E_INVALID_URL * UPNP_E_SUCCESS ************************************************************************/ int http_FixStrUrl( IN const char *urlstr, IN int 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; int sockaddr_len; int ret_connect; http_FixUrl(destination_url, url); connfd = socket(url->hostport.IPaddress.ss_family, SOCK_STREAM, 0); if (connfd == -1) { return UPNP_E_OUTOF_SOCKET; } sockaddr_len = 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 shutdown(connfd, SD_BOTH); UpnpCloseSocket(connfd); return 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; xboolean ok_on_close = FALSE; char buf[2 * 1024]; if (request_method == 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, num_read); if (status == 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 > 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; } else if (status == PARSE_FAILURE) { *http_error_code = parser->http_error_code; line = __LINE__; ret = UPNP_E_BAD_HTTPMSG; goto ExitFunction; } else if (status == PARSE_INCOMPLETE_ENTITY) { /* read until close */ ok_on_close = TRUE; } else if (status == PARSE_CONTINUE_1) { /* Web post request. */ line = __LINE__; ret = PARSE_SUCCESS; goto ExitFunction; } } 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; } /************************************************************************ * Function: http_SendMessage * * Parameters: * IN SOCKINFO *info ; Socket information object * IN OUT int * TimeOut ; time out value * IN const char* fmt, ... Pattern format to take actions upon * * Description: * Sends a message to the destination based on the * IN const char* fmt parameter * fmt types: * 'f': arg = const char * file name * 'm': arg1 = const char * mem_buffer; arg2= size_t buf_length * E.g.: * char *buf = "POST /xyz.cgi http/1.1\r\n\r\n"; * char *filename = "foo.dat"; * int status = http_SendMessage( tcpsock, "mf", * buf, strlen(buf), * filename ); * * Returns: * UPNP_E_OUTOF_MEMORY * UPNP_E_FILE_READ_ERROR * UPNP_E_SUCCESS ************************************************************************/ int http_SendMessage( IN SOCKINFO * info, IN OUT int *TimeOut, IN const char *fmt, ... ) { char c; char *buf = NULL; size_t buf_length; char *filename = NULL; FILE *Fp; int num_read; int num_written; off_t amount_to_be_read = 0; va_list argp; char *file_buf = NULL; char *ChunkBuf = NULL; struct SendInstruction *Instr = NULL; char Chunk_Header[CHUNK_HEADER_SIZE]; int RetVal = 0; /* 10 byte allocated for chunk header. */ int Data_Buf_Size = WEB_SERVER_BUF_SIZE; va_start( argp, fmt ); while( ( c = *fmt++ ) != 0 ) { if( c == 'I' ) { Instr = va_arg(argp, struct SendInstruction *); assert( Instr ); if( Instr->ReadSendSize >= 0 ) { amount_to_be_read = 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 = (char *)malloc( Data_Buf_Size + CHUNK_HEADER_SIZE + CHUNK_TAIL_SIZE); if( !ChunkBuf ) { return UPNP_E_OUTOF_MEMORY; } 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 ) { free( ChunkBuf ); return UPNP_E_FILE_READ_ERROR; } if( Instr && Instr->IsRangeActive && Instr->IsVirtualFile ) { if( virtualDirCallback.seek( Fp, Instr->RangeOffset, SEEK_CUR ) != 0 ) { free( ChunkBuf ); return UPNP_E_FILE_READ_ERROR; } } else if( Instr && Instr->IsRangeActive ) { if( fseeko( Fp, Instr->RangeOffset, SEEK_CUR ) != 0 ) { free( ChunkBuf ); return UPNP_E_FILE_READ_ERROR; } } while( amount_to_be_read ) { if( Instr ) { int n = (amount_to_be_read >= Data_Buf_Size) ? Data_Buf_Size : amount_to_be_read; if( Instr->IsVirtualFile ) { num_read = virtualDirCallback.read( Fp, file_buf, n ); } else { num_read = fread( file_buf, 1, n, Fp ); } amount_to_be_read = 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, 1, Data_Buf_Size, Fp ); } if( num_read == 0 ) { /* EOF so no more to send. */ if( Instr && Instr->IsChunkActive ) { char *str = "0\r\n\r\n"; num_written = 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 ) { /* Copy CRLF at the end of the chunk */ memcpy( file_buf + num_read, "\r\n", 2 ); /* Hex length for the chunk size. */ sprintf( Chunk_Header, "%x", num_read ); /*itoa(num_read,Chunk_Header,16); */ strcat( Chunk_Header, "\r\n" ); /* 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)); */ num_written = sock_write( info, file_buf - strlen( Chunk_Header ), num_read + strlen( Chunk_Header ) + 2, TimeOut ); if( num_written != num_read + ( int )strlen( Chunk_Header ) + 2 ) { /* Send error nothing we can do. */ goto Cleanup_File; } } else { /* write data */ num_written = sock_write( info, file_buf, num_read, TimeOut ); UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, ">>> (SENT) >>>\n%.*s\n------------\n", ( int )num_written, file_buf ); /* Send error nothing we can do */ if( num_written != num_read ) { goto Cleanup_File; } } } /* while */ Cleanup_File: va_end( argp ); if( Instr && Instr->IsVirtualFile ) { virtualDirCallback.close( Fp ); } else { fclose( Fp ); } free( ChunkBuf ); return RetVal; } else if( c == 'b' ) { /* memory buffer */ buf = va_arg(argp, char *); buf_length = va_arg(argp, size_t); if( buf_length > 0 ) { num_written = sock_write( info, buf, buf_length, TimeOut ); UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, ">>> (SENT) >>>\n" "%.*s\nbuf_length=%d, num_written=%d\n" "------------\n", (int)buf_length, buf, (int)buf_length, num_written ); if( (size_t)num_written != buf_length ) { goto end; } } } } end: va_end( argp ); free( ChunkBuf ); return 0; } /************************************************************************ * 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; int sockaddr_len; int http_error_code; SOCKINFO info; tcp_connection = socket( destination->hostport.IPaddress.ss_family, SOCK_STREAM, 0); if (tcp_connection == -1) { 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), 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 int *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 ) + 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 ); strcpy( urlPath, 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 ); 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 - 1 ? ctype.length : LINE_SIZE - 1; memcpy( content_type, ctype.buf, copy_len ); content_type[copy_len] = '\0'; } } /* */ /* extract doc from msg */ /* */ if( ( *doc_length = ( int )response.msg.entity.length ) == 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 + 1 ); /* save mem for body only */ *document = realloc( msg_start, *doc_length + 1 ); /*LEAK_FIX_MK */ /* *document = Realloc( msg_start,msg_length, *doc_length + 1 ); LEAK_FIX_MK */ /* shrink can't fail */ assert( ( int )msg_length > *doc_length ); assert( *document != NULL ); } 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 ) + 1 ); size_t hostlen = 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 ); strcpy( urlPath, 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 unsigned int *size, IN int timeout ) { http_post_handle_t *handle = ( http_post_handle_t * ) Handle; char *tempbuf = NULL; int tempbufSize = 0; int freeTempbuf = 0; int numWritten = 0; if( ( !handle ) || ( !size ) || ( ( ( *size ) > 0 ) && !buf ) || ( ( *size ) < 0 ) ) { if(size) ( *size ) = 0; return UPNP_E_INVALID_PARAM; } if( handle->contentLength == UPNP_USING_CHUNKED ) { if( ( *size ) ) { int tempSize = 0; tempbuf = ( char * )malloc( *size + CHUNK_HEADER_SIZE + CHUNK_TAIL_SIZE ); if ( tempbuf == NULL) { return UPNP_E_OUTOF_MEMORY; } /* begin chunk */ sprintf( tempbuf, "%x\r\n", ( *size ) ); tempSize = strlen( tempbuf ); memcpy( tempbuf + tempSize, buf, ( *size ) ); memcpy( tempbuf + tempSize + ( *size ), "\r\n", 2 ); /* end of chunk */ tempbufSize = tempSize + ( *size ) + 2; freeTempbuf = 1; } } else { tempbuf = buf; tempbufSize = ( *size ); } numWritten = sock_write( &handle->sock_info, tempbuf, tempbufSize, &timeout ); /*(*size) = sock_write(&handle->sock_info,tempbuf,tempbufSize,&timeout); */ if( freeTempbuf ) { free( tempbuf ); } if( numWritten < 0 ) { ( *size ) = 0; return numWritten; } else { ( *size ) = 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; http_post_handle_t *handle = Handle; if( ( !handle ) || ( !httpStatus ) ) { return UPNP_E_INVALID_PARAM; } if( handle->contentLength == UPNP_USING_CHUNKED ) { retc = sock_write( &handle->sock_info, "0\r\n\r\n", strlen( "0\r\n\r\n" ), &timeout ); /*send last chunk */ } /*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; sock_destroy( &handle->sock_info, SD_BOTH ); /*should shutdown completely */ 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; int 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 = (http_post_handle_t *) malloc(sizeof(http_post_handle_t)); if (!handle) { return UPNP_E_OUTOF_MEMORY; } handle->contentLength = contentLength; tcp_connection = socket(url.hostport.IPaddress.ss_family, SOCK_STREAM, 0); if (tcp_connection == -1) { 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), 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 ) + 1 ); size_t querylen = 0; const char *querystr; size_t hostlen = 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 ); strcpy( urlPath, 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 ); if( status == PARSE_OK ) { done = 1; } else if( status == PARSE_INCOMPLETE ) { done = 0; } else { /*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, 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 ); if( status == PARSE_OK ) { done = 1; } else if( status == 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; } } done = 0; status = parser_parse_headers( parser ); if( ( status == PARSE_OK ) && ( parser->position == POS_ENTITY ) ) { done = 1; } else if( status == 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, 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_OK ) && ( parser->position == POS_ENTITY ) ) { done = 1; } else if( status == 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; xboolean ok_on_close = FALSE; char tempbuf[2 * 1024]; int ret_code = 0; if( !handle || !size || (*size > 0 && !buf) || *size < 0) { if(size) { *size = 0; } return UPNP_E_INVALID_PARAM; } /*first parse what has already been gotten */ if( handle->response.position != POS_COMPLETE ) { status = parser_parse_entity( &handle->response ); } else { status = PARSE_SUCCESS; } if( status == PARSE_INCOMPLETE_ENTITY ) { /* read until close */ ok_on_close = TRUE; } else if( ( status != PARSE_SUCCESS ) && ( status != PARSE_CONTINUE_1 ) && ( status != PARSE_INCOMPLETE ) ) { /*error */ *size = 0; return UPNP_E_BAD_RESPONSE; } /*read more if necessary entity */ while( ( ( handle->response.msg.entity_offset + *size ) > handle->response.msg.entity.length ) && ( ! handle->cancel ) && ( handle->response.position != 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, num_read ); if( ret_code != 0 ) { /* set failure status */ handle->response.http_error_code = HTTP_INTERNAL_SERVER_ERROR; *size = 0; return PARSE_FAILURE; } status = parser_parse_entity( &handle->response ); if( status == PARSE_INCOMPLETE_ENTITY ) { /* read until close */ ok_on_close = TRUE; } else if( ( status != PARSE_SUCCESS ) && ( status != PARSE_CONTINUE_1 ) && ( status != PARSE_INCOMPLETE ) ) { /*error */ *size = 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 = 0; handle->response.http_error_code = HTTP_BAD_REQUEST; /* or response */ return UPNP_E_BAD_HTTPMSG; } } else { *size = 0; return num_read; } } if ((handle->response.msg.entity_offset + *size) > handle->response.msg.entity.length) { *size = handle->response.msg.entity.length - handle->response.msg.entity_offset; } memcpy(buf, &handle->response.msg.msg.buf[handle->response.entity_start_position], *size); if (*size > 0) { membuffer_delete(&handle->response.msg.msg, handle->response.entity_start_position, *size); } handle->response.msg.entity_offset += *size; if (handle->cancel) { return UPNP_E_CANCELED; } 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; } sock_destroy( &handle->sock_info, SD_BOTH ); /*should shutdown completely */ httpmsg_destroy( &handle->response.msg ); free( handle ); return UPNP_E_SUCCESS; } /************************************************************************ * Function: http_OpenHttpGet * * 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_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); } /************************************************************************ * Function: http_OpenHttpGetProxy * * Parameters: * IN const char *url_str; String as a URL * IN const char *proxy_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. * If a proxy URL is defined then the connection is made there. * * 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_OpenHttpGetProxy( IN const char *url_str, IN const char *proxy_str, IN OUT void **Handle, IN OUT char **contentType, OUT int *contentLength, OUT int *httpStatus, IN int timeout) { int ret_code; int 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 = (http_get_handle_t *)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(peer->hostport.IPaddress.ss_family, SOCK_STREAM, 0); if (tcp_connection == -1) { 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), 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; } status = ReadResponseLineAndHeaders(&handle->sock_info, &handle->response, &timeout, &http_error_code); if (status != PARSE_OK) { ret_code = UPNP_E_BAD_RESPONSE; goto errorHandler; } status = parser_get_entity_read_method(&handle->response); if (status != PARSE_CONTINUE_1 && status != PARSE_SUCCESS) { 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 == 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 = 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 = 70; ret = http_MakeMessage( &membuf, response_major, response_minor, "RSCB", http_status_code, /* response start line */ http_status_code ); /* body */ if( ret == 0 ) { timeout = HTTP_DEFAULT_TIMEOUT; ret = http_SendMessage( info, &timeout, "b", membuf.buf, membuf.length ); } membuffer_destroy( &membuf ); return ret; } /************************************************************************ * Function: http_MakeMessage * * Parameters: * INOUT membuffer* buf; buffer with the contents of the * message * IN int http_major_version; HTTP major version * IN int http_minor_version; HTTP minor version * IN const char* fmt; Pattern format * ...; * * Description: * Generate an HTTP message based on the format that is specified * in the input parameters. * * fmt types: * 'B': arg = int status_code * appends content-length, content-type and HTML body * for given code * 'b': arg1 = const char* buf; * arg2 = size_t buf_length memory ptr * 'C': (no args) appends a HTTP CONNECTION: close header * depending on major,minor version * 'c': (no args) appends CRLF "\r\n" * 'D': (no args) appends HTTP DATE: header * 'd': arg = int number -- appends decimal number * 'G': arg = range information -- add range header * 'h': arg = off_t number -- appends off_t number * 'K': (no args) -- add chunky header * 'L': arg = language information -- add Content-Language header if * Accept-Language header is not empty and if * WEB_SERVER_CONTENT_LANGUAGE is not empty * 'N': arg1 = off_t content_length -- content-length header * 'q': arg1 = http_method_t -- request start line and HOST header * arg2 = (uri_type *) * 'Q': arg1 = http_method_t; -- start line of request * arg2 = char* url; * arg3 = size_t url_length * 'R': arg = int status_code -- adds a response start line * 'S': (no args) appends HTTP SERVER: header * 's': arg = const char* C_string * 'T': arg = char * content_type; format * e.g: "text/html"; content-type header * 't': arg = time_t * gmt_time -- appends time in RFC 1123 fmt * 'U': (no args) appends HTTP USER-AGENT: header * 'X': arg = const char useragent; "redsonic" HTTP X-User-Agent: useragent * * Return: int * 0 - On Success * UPNP_E_OUTOF_MEMORY * UPNP_E_INVALID_URL ************************************************************************/ int http_MakeMessage( INOUT membuffer * buf, IN int http_major_version, IN int http_minor_version, IN 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; char *start_str, *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 = UPNP_E_OUTOF_MEMORY; 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"; va_start( argp, fmt ); while( ( c = *fmt++ ) != 0 ) { 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 ) ) != 0 ) { 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" ) ) != 0 ) { 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 ) ) != 0 ) { 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", s[0]); assert( s ); length = ( size_t ) va_arg( argp, size_t ); if( membuffer_append( buf, s, length ) != 0 ) { goto error_handler; } } else if( c == 'c' ) { /* crlf */ if( membuffer_append( buf, "\r\n", 2 ) != 0 ) { goto error_handler; } } else if( c == 'd' ) { /* integer */ num = ( int )va_arg( argp, int ); sprintf( tempbuf, "%"PRIzu, num ); if( membuffer_append( buf, tempbuf, strlen( tempbuf ) ) != 0 ) { goto error_handler; } } else if( c == 'h' ) { /* off_t */ bignum = ( off_t )va_arg( argp, off_t ); sprintf( tempbuf, "%"PRId64, (int64_t)bignum ); if( membuffer_append( buf, tempbuf, strlen( tempbuf ) ) != 0 ) { 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 ); date = gmtime( &curr_time ); } else { /* date value only */ start_str = end_str = ""; loc_time = ( time_t * ) va_arg( argp, time_t * ); assert( loc_time ); date = gmtime( loc_time ); } sprintf( 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( membuffer_append( buf, tempbuf, strlen( tempbuf ) ) != 0 ) { 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" ) != 0 ) { 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 ); 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 ); sprintf( tempbuf, "HTTP/%d.%d %d ", http_major_version, http_minor_version, status_code ); /* str */ status_msg = http_get_code_text( status_code ); if (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 ); sprintf( tempbuf, "%s%d %s%s", "

", status_code, http_get_code_text( status_code ), "

" ); bignum = 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 ); } } return 0; error_handler: va_end( argp ); membuffer_destroy( buf ); 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 = 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 ) + 1 ); if( !urlPath ) { errCode = UPNP_E_OUTOF_MEMORY; break; } memset( urlPath, 0, strlen( url_str ) + 1 ); strcpy( urlPath, 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; int 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; 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)); sprintf(rangeBuf.RangeHeader, "Range: bytes=%d-%d\r\n", lowRange, highRange); 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(url.hostport.IPaddress.ss_family, SOCK_STREAM, 0); if (tcp_connection == -1) { 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), 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; } status = ReadResponseLineAndHeaders(&handle->sock_info, &handle->response, &timeout, &http_error_code); if (status != PARSE_OK) { errCode = UPNP_E_BAD_RESPONSE; free(handle); break; } status = parser_get_entity_read_method(&handle->response); if (status != PARSE_CONTINUE_1 && status != 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 == 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 = 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 * * 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 ) { #ifdef WIN32 OSVERSIONINFO versioninfo; versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (GetVersionEx(&versioninfo)!=0) { sprintf( info, "%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'; } sprintf( info, "%s/%s, UPnP/1.0, Portable SDK for UPnP devices/"PACKAGE_VERSION "\r\n", sys_info.sysname, sys_info.release ); #endif }