Files
m241Neelima/components/ip_net2-6.9/ipike/src/ipike_http_fetcher.c
T
2026-07-30 11:19:44 +05:30

848 lines
25 KiB
C

/*
******************************************************************************
* SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipike_http_fetcher.c,v $ $Revision: 1.4.10.1 $
* $Source: /home/interpeak/CVSRoot/ipike/src/ipike_http_fetcher.c,v $
* $Author: ulf $ $Date: 2011-11-16 10:33:33 $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Erik Dahlberg <erik.dahlberg@windriver.com>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/* Provides functionality to fetch files (certificates) remotely using HTTP */
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include "ipike_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#include "ipike_h.h"
#include <ipcom_type.h>
#include <ipcom_cstyle.h>
#include <ipcom_strlib.h>
#include <ipcom_clib.h>
#include <ipcom_sock2.h>
#include <ipcom_os.h>
#include <ipcom_syslog.h>
#include <ipcom_tmo2.h>
#include "ipike_http_fetcher.h"
/*
****************************************************************************
* 4 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 5 DEFINES
****************************************************************************
*/
#define IPIKE_HTTP_REQUEST_FMT_STRING "GET %s HTTP/1.1\r\nhost: %s\r\n\r\n"
#define IPIKE_HTTP_REQUEST_BUF_SIZE 256
#define IPIKE_HTTP_RESPONSE_BUF_SIZE_DEFAULT 4096
#define IPIKE_HTTP_RESPONSE_STATUS_OK "HTTP/1.1 200"
#define IPIKE_HTTP_RESPONSE_STATUS_OK_LEN 12
#define IPIKE_HTTP_PORT 80
#define IPIKE_HTTP_PREFIX "http://"
#define IPIKE_HTTP_PREFIX_LEN 7
/* HTTP socket state */
#define IPIKE_HTTP_SOCK_STATE_CREATED (1 << 0)
#define IPIKE_HTTP_SOCK_STATE_CONNECTED (1 << 1)
/*
****************************************************************************
* 6 TYPES
****************************************************************************
*/
/* URL */
typedef struct Ipike_http_fetcher_url_st
{
char *address;
char *path;
} Ipike_http_fetcher_url;
/* HTTP fetcher context */
typedef struct Ipike_http_fetcher_ctx_st
{
Ipcom_list list; /* Context list */
Ipcom_tmo2 tmo; /* Timeout entry */
Ipike_http_fetcher_callback callback; /* User callback */
void *callback_ctx; /* User callback context */
Ip_fd fd; /* Socket filedescriptor */
Ipike_http_fetcher_url *url; /* URL */
Ip_u32 state; /* Socket state */
} Ipike_http_fetcher_ctx;
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
IP_STATIC Ipcom_list ctx_list_head =
{
.next = &ctx_list_head,
.prev = &ctx_list_head,
.head = &ctx_list_head,
.size = 0
};
/*
****************************************************************************
* 9 LOCAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* Ipike_http_fetcher_url_alloc
*===========================================================================
*/
IP_STATIC Ipike_http_fetcher_url *
ipike_http_fetcher_url_alloc()
{
Ipike_http_fetcher_url *url;
url = ipcom_malloc(sizeof(Ipike_http_fetcher_url));
if ( url )
ipcom_memset(url, 0, sizeof(Ipike_http_fetcher_url));
return url;
}
/*
*===========================================================================
* Ipike_http_fetcher_url_destroy
*===========================================================================
*/
IP_STATIC void
ipike_http_fetcher_url_destroy(Ipike_http_fetcher_url *url)
{
ip_assert(url);
if ( url->address )
ipcom_free(url->address);
if ( url->path )
ipcom_free(url->path);
ipcom_free(url);
}
/*
*===========================================================================
* Ipike_http_fetcher_ctx_alloc
*===========================================================================
*/
IP_STATIC Ipike_http_fetcher_ctx *
ipike_http_fetcher_ctx_alloc()
{
Ipike_http_fetcher_ctx *ctx;
ctx = ipcom_malloc(sizeof(Ipike_http_fetcher_ctx));
if ( ctx )
{
ipcom_memset(ctx, 0, sizeof(Ipike_http_fetcher_ctx));
ctx->fd = IP_INVALID_SOCKET;
}
return ctx;
}
/*
*===========================================================================
* ipike_http_fetcher_ctx_destroy
*===========================================================================
*/
IP_STATIC void
ipike_http_fetcher_ctx_destroy(Ipike_http_fetcher_ctx *ctx)
{
ip_assert(ctx);
ipike_tmo2_cancel(&ctx->tmo);
if ( ctx->fd != IP_INVALID_SOCKET )
ipcom_socketclose(ctx->fd);
if ( ctx->url )
ipike_http_fetcher_url_destroy(ctx->url);
ipcom_free(ctx);
}
/*
*===========================================================================
* ipike_http_fetcher_tmo_cb
*===========================================================================
*/
IP_STATIC Ip_bool
ipike_http_fetcher_tmo_cb(Ipike_http_fetcher_ctx *ctx)
{
if ( ctx->state == IPIKE_HTTP_SOCK_STATE_CREATED )
{
IPCOM_LOG1(WARNING, "ipike_http_fetcher_tmo_cb() :: could not connect"\
" to %s",
ctx->url->address);
}
else
{
IPCOM_LOG1(WARNING, "ipike_http_fetcher_tmo_cb() :: server %s did not"\
" respond",
ctx->url->address);
}
/* Notify failure to user callback */
ctx->callback(IPIKE_FAILED, IP_NULL, 0, ctx->callback_ctx);
ipcom_list_remove(&ctx->list);
ipike_http_fetcher_ctx_destroy(ctx);
return IP_FALSE;
}
/*
*===========================================================================
* ipike_http_fetcher_url_parse_string
*===========================================================================
*/
IP_STATIC Ipike_http_fetcher_url *
ipike_http_fetcher_url_parse_string(const char *url_string)
{
Ipike_http_fetcher_url *url = IP_NULL;
const char *addr_ptr = IP_NULL;
const char *path_ptr = IP_NULL;
Ip_size_t addr_len = 0;
if ( ipcom_strncmp(url_string, IPIKE_HTTP_PREFIX, IPIKE_HTTP_PREFIX_LEN) != 0 )
goto error_out;
url = ipike_http_fetcher_url_alloc();
if ( !url )
goto error_out;
/* Make a local copy of the string without the url prefix */
addr_ptr = url_string + IPIKE_HTTP_PREFIX_LEN;
/* Get the start of the path */
path_ptr = ipcom_strchr(addr_ptr, '/');
/* Make sure we have ha '/' and something after it */
if ( !path_ptr || *path_ptr == '\0' )
goto error_out;
url->path = ipcom_strdup( path_ptr );
/* Get length of the address */
addr_len = (Ip_size_t) (path_ptr - addr_ptr);
url->address = ipcom_malloc( addr_len + 1 );
if ( !url->address )
goto error_out;
ipcom_strncpy( url->address, addr_ptr, addr_len );
url->address[addr_len] = '\0';
return url;
error_out:
if ( url )
ipike_http_fetcher_url_destroy( url );
return IP_NULL;
}
/*
*===========================================================================
* Ipike_http_fetcher_get_content
*===========================================================================
*/
IP_STATIC const char *
ipike_http_fetcher_get_content(
const char *http_response,
Ip_size_t http_response_len,
Ip_size_t *content_len)
{
const char *needle = "\r\n\r\n";
const char *ptr;
const char *end_ptr;
const char *needle_ptr;
end_ptr = http_response + http_response_len;
needle_ptr = needle;
/* Search for the "needle*" */
for (ptr = http_response; ptr < end_ptr; ptr++)
{
if ( *ptr == *needle_ptr )
needle_ptr++;
else
needle_ptr = needle;
if ( *needle_ptr == '\0' )
{
/* Match */
ptr++;
*content_len = (Ip_size_t) (end_ptr - ptr);
if ( *content_len == 0 )
return IP_NULL;
return ptr;
}
}
*content_len = 0;
return IP_NULL;
}
/*
*===========================================================================
* ipike_http_fetcher_process_response
*===========================================================================
*/
IP_STATIC void
ipike_http_fetcher_process_response( Ipike_http_fetcher_ctx *ctx )
{
char *resp_buf = IP_NULL;
const char *content = IP_NULL;
Ip_ssize_t recv_len = 0;
Ip_size_t content_len = 0;
Ip_size_t resp_buf_size = 0;
Ipike_error status = IPIKE_FAILED;;
ip_assert(ctx);
/* Remove the context from the list */
ipcom_list_remove(&ctx->list);
resp_buf_size = (Ip_size_t) ipcom_sysvar_get_as_int("ipike.http_response_buf_size",
IPIKE_HTTP_RESPONSE_BUF_SIZE_DEFAULT);
/* Allocate response buffer */
resp_buf = ipcom_malloc(resp_buf_size);
if ( !resp_buf )
{
IPCOM_LOG1(CRIT, "ipike_http_fetcher_process_response() :: "\
"failed to allocate response buffer (%lu)",
resp_buf_size);
goto done;
}
/* Receive data */
recv_len = ipcom_recv(ctx->fd, resp_buf, resp_buf_size, 0);
if ( recv_len < 0 )
{
IPCOM_LOG1(ERR, "ipike_http_fetcher_process_response :: receive error \"%s\"",
ipcom_strerror(ipcom_errno));
goto done;
}
/* Were we able to read all data */
if ( (Ip_size_t) recv_len == resp_buf_size )
{
IPCOM_LOG1(ERR, "ipike_http_fetcher_process_response() :: "\
"response buffer to small (%lu)\n",
resp_buf_size);
goto done;
}
/* The received data must at least contain a HTTP OK code */
if ( recv_len <= IPIKE_HTTP_RESPONSE_STATUS_OK_LEN )
{
IPCOM_LOG1(WARNING, "ipike_http_fetcher_process_response() :: "\
"not enough data in http response (%ld)",
recv_len);
goto done;
}
IPCOM_LOG1(DEBUG2, "ipike_http_fetcher_process_response() :: "\
"received %ld bytes of http data",
recv_len);
/* Check for HTTP OK */
if ( ipcom_strncmp( resp_buf, IPIKE_HTTP_RESPONSE_STATUS_OK,
IPIKE_HTTP_RESPONSE_STATUS_OK_LEN) != 0 )
{
IPCOM_LOG0(WARNING, "ipike_http_fetcher_process_response() :: "\
"no ok code in http response");
goto done;
}
/* Ignore the rest of the HTTP Header and just get the content */
content = ipike_http_fetcher_get_content( resp_buf,
(Ip_size_t) recv_len,
&content_len);
if ( !content )
{
IPCOM_LOG0(WARNING, "ipike_http_fetcher_process_response() :: "\
"no http content in response data");
goto done;
}
/* All success */
status = IPIKE_SUCCESS;;
done:
/* Call user callback */
ctx->callback(status, content, content_len, ctx->callback_ctx);
/* Free response buffer */
if ( resp_buf )
ipcom_free(resp_buf);
/* Context can now be destroyed */
ipike_http_fetcher_ctx_destroy(ctx);
}
/*
*===========================================================================
* ipike_http_fetcher_send_request
*===========================================================================
*/
IP_STATIC void
ipike_http_fetcher_send_request(Ipike_http_fetcher_ctx *ctx)
{
char http_request[IPIKE_HTTP_REQUEST_BUF_SIZE];
Ip_ssize_t len = 0;
Ip_socklen_t err_len = 0;
Ip_err err = 0;
ip_assert(ctx);
/* Remove the context from the list */
ipcom_list_remove(&ctx->list);
/* Cancel the connect timeout */
ipike_tmo2_cancel(&ctx->tmo);
/* In the normal case the socket comes from a created state, i.e.
* through an asynchronous (non-blocking) connect */
if ( ctx->state == IPIKE_HTTP_SOCK_STATE_CREATED )
{
/* Get any error on the socket */
err_len = sizeof(err);
if ( ipcom_getsockopt(ctx->fd, IP_SOL_SOCKET, IP_SO_ERROR, &err, &err_len) < 0 )
{
IPCOM_LOG1(ERR, "ipike_http_fetcher_send_request() :: getsockopt failed (%s)",
ipcom_strerror(ipcom_errno));
goto error_out;
}
/* Check if the connect operation was successful */
if ( err != 0 )
{
IPCOM_LOG1(ERR, "ipike_http_fetcher_send_request() :: connect failed (%s)",
ipcom_strerror(err));
goto error_out;
}
/* Connect operation was successful */
ctx->state = IPIKE_HTTP_SOCK_STATE_CONNECTED;
}
/* We should now have a connected socket */
ip_assert( ctx->state == IPIKE_HTTP_SOCK_STATE_CONNECTED );
/* Create HTTP Request */
len = ipcom_snprintf(http_request, sizeof(http_request),
IPIKE_HTTP_REQUEST_FMT_STRING,
ctx->url->path, ctx->url->address);
if ( len < 0 )
{
IPCOM_LOG0(ERR, "ipike_http_fetcher_send_request() :: "\
"failed to create http request");
goto error_out;
}
IPCOM_LOG1(DEBUG2, "ipike_http_fetcher_send_request() :: "\
"sending http request \n%s",
http_request);
/* Send HTTP Request */
if ( ipcom_send(ctx->fd, http_request, (Ip_size_t) len, 0) != len )
{
IPCOM_LOG1(ERR, "ipike_http_fetcher_send_request() :: "\
"failed to send http request (%s)",
ipcom_strerror(ipcom_errno));
goto error_out;
}
/* Add a timeout handler for the send operation */
if (ipcom_tmo2_request(ipike.tmo_head,
&ctx->tmo,
(Ipcom_tmo2_func) ipike_http_fetcher_tmo_cb,
ctx,
ipike.http_request_timeout * 1000u) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipike_http_fetcher_send_request() :: "\
"failed to register timeout");
goto error_out;
}
/* Insert into list */
ipcom_list_insert_last(&ctx_list_head, &ctx->list);
/* All success, lets wait for the response */
return;
error_out:
/* Notify failure to user */
ctx->callback(IPIKE_FAILED, IP_NULL, 0, ctx->callback_ctx);
/* Destroy context */
ipike_http_fetcher_ctx_destroy(ctx);
}
/*
****************************************************************************
* 10 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipike_http_fetcher_add_fds
*===========================================================================
*/
IP_GLOBAL int
ipike_http_fetcher_add_fds(Ip_fd_set *read_fds, Ip_fd_set *write_fds)
{
Ipike_http_fetcher_ctx *ctx;
int max = -1;
for ( ctx = (Ipike_http_fetcher_ctx *) ipcom_list_first(&ctx_list_head);
ctx != IP_NULL;
ctx = (Ipike_http_fetcher_ctx *) ipcom_list_next(&ctx->list) )
{
switch (ctx->state)
{
case IPIKE_HTTP_SOCK_STATE_CREATED:
/* Waiting for connect to finish */
IP_FD_SET(ctx->fd, write_fds);
break;
case IPIKE_HTTP_SOCK_STATE_CONNECTED:
/* Waiting for HTTP response */
IP_FD_SET(ctx->fd, read_fds);
break;
default:
IPCOM_LOG1(ERR, "ipike_http_fetcher_add_fds() :: "\
"invalid socket state (%u)",
ctx->state);
break;
}
max = IP_MAX(ctx->fd, max);
}
return max;
}
/*
*===========================================================================
* ipike_http_fetcher_get
*===========================================================================
*/
IP_GLOBAL Ipike_error
ipike_http_fetcher_get(
const char *url_string,
Ipike_http_fetcher_callback callback,
void *callback_ctx)
{
union Ip_sockaddr_union server_addr;
struct Ip_addrinfo *result = IP_NULL;;
Ipike_http_fetcher_ctx *ctx = IP_NULL;
int i = 0;
IPCOM_LOG1(DEBUG2, "ipike_http_fetcher_get() :: fetching \"%s\"", url_string);
/* Allocate context for this request */
ctx = ipike_http_fetcher_ctx_alloc();
if ( !ctx )
{
IP_PANIC();
IPCOM_LOG0(CRIT, "ipike_http_fetcher_get() :: failed to allocate memory");
goto error_out;
}
/* Set user callback */
ctx->callback = callback;
ctx->callback_ctx = callback_ctx;
/* Parse URL */
ctx->url = ipike_http_fetcher_url_parse_string(url_string);
if ( !ctx->url )
{
IPCOM_LOG1(ERR, "ipike_http_fetcher_get() :: invalid URL \"%s\"", url_string);
goto error_out;
}
memset(&server_addr, 0, sizeof(server_addr));
/* Do we have a DNS name */
if ( isalpha(*ctx->url->address) )
{
/* Resolve */
if ( ipcom_getaddrinfo(ctx->url->address, IP_NULL, IP_NULL, &result) != 0 )
{
IPCOM_LOG1(ERR, "ipike_http_fetcher() :: failed to resolve \"%s\"",
ctx->url->address);
goto error_out;
}
/* Use the first proposed address */
memcpy(&server_addr, result->ai_addr, result->ai_addrlen);
ipcom_freeaddrinfo(result);
}
else
{
#ifdef IPCOM_USE_INET
if ( ipcom_inet_pton(IP_AF_INET, ctx->url->address, &server_addr.sin.sin_addr) == 1 )
{
server_addr.sa.sa_family = IP_AF_INET;
server_addr.sin.sin_port = ip_htons(IPIKE_HTTP_PORT);
IPCOM_SA_LEN_SET(&server_addr.sa, sizeof(struct Ip_sockaddr_in));
}
#endif
#ifdef IPCOM_USE_INET6
if ( server_addr.sa.sa_family == 0 &&
ipcom_inet_pton(IP_AF_INET6, ctx->url->address, &server_addr.sin6.sin6_addr) == 1 )
{
server_addr.sa.sa_family = IP_AF_INET6;
IPCOM_SA_LEN_SET(&server_addr.sa, sizeof(struct Ip_sockaddr_in6));
}
#endif
}
if ( server_addr.sa.sa_family == 0 )
{
IPCOM_LOG1(ERR, "ipike_http_fetcher() :: bad address \"%s\"", ctx->url->address);
goto error_out;
}
#ifdef IPCOM_USE_INET
if ( server_addr.sa.sa_family == IP_AF_INET )
server_addr.sin.sin_port = ip_htons(IPIKE_HTTP_PORT);
#endif
#ifdef IPCOM_USE_INET6
if ( server_addr.sa.sa_family == IP_AF_INET6 )
server_addr.sin6.sin6_port = ip_htons(IPIKE_HTTP_PORT);
#endif
/* Create socket */
if ( (ctx->fd = ipcom_socket(server_addr.sa.sa_family, IP_SOCK_STREAM, IP_IPPROTO_TCP)) < 0 )
{
IPCOM_LOG1(ERR, "ipike_http_fetcher() :: failed to create socket (%s)",
ipcom_strerror(ipcom_errno));
goto error_out;
}
/* Set state to created */
ctx->state = IPIKE_HTTP_SOCK_STATE_CREATED;
i = 1;
/* Set socket to non-blocking */
if (ipcom_socketioctl(ctx->fd, IP_FIONBIO, (char *)&i) != 0)
{
IPCOM_LOG1(WARNING, "ipike_http_fetcher() :: failed to set socket to"\
" non blocking (%s)",
ipcom_strerror(ipcom_errno));
goto error_out;
}
/* Connect */
if ( ipcom_connect(ctx->fd, (struct Ip_sockaddr *) &server_addr,
IPCOM_SA_LEN_GET(&server_addr.sa)) < 0 )
{
/* We expect IP_ERRNO_EINPROGRESS in the normal case, fail otherwise */
if ( ipcom_errno != IP_ERRNO_EINPROGRESS )
{
IPCOM_LOG1(ERR, "ipike_http_fetcher() :: failed to connect socket (%s)",
ipcom_strerror(ipcom_errno));
goto error_out;
}
}
else
{
/* Socket successfully connected, can only occur if server address is a
* local address */
IPCOM_LOG0(DEBUG2, "ipike_http_fetcher() :: socket immediately connected");
ctx->state = IPIKE_HTTP_SOCK_STATE_CONNECTED;
}
i = 0;
/* Set socket to blocking */
if (ipcom_socketioctl(ctx->fd, IP_FIONBIO, (char *)&i) != 0)
{
IPCOM_LOG1(WARNING, "ipike_http_fetcher() :: failed to set socket to"\
" blocking (%s)",
ipcom_strerror(ipcom_errno));
goto error_out;
}
/* Add a timeout handler for connect operation */
if (ipcom_tmo2_request(ipike.tmo_head,
&ctx->tmo,
(Ipcom_tmo2_func) ipike_http_fetcher_tmo_cb,
ctx,
ipike.http_request_timeout * 1000u) != IPCOM_SUCCESS)
goto error_out;
/* Insert into list */
ipcom_list_insert_last(&ctx_list_head, &ctx->list);
/* It's unlikely but the socket can be in a connected state, in that case
* we can send the request immediately */
if ( ctx->state == IPIKE_HTTP_SOCK_STATE_CONNECTED )
ipike_http_fetcher_send_request(ctx);
/* return */
return IPIKE_SUCCESS;
error_out:
if ( ctx )
ipike_http_fetcher_ctx_destroy(ctx);
return IPIKE_FAILED;
}
/*
*===========================================================================
* ipike_http_fetcher_process
*===========================================================================
*/
IP_GLOBAL int
ipike_http_fetcher_process( Ip_fd_set *read_fds, Ip_fd_set *write_fds )
{
Ipike_http_fetcher_ctx *ctx = IP_NULL;
Ipike_http_fetcher_ctx *next = IP_NULL;
int num_processed = 0;
ctx = (Ipike_http_fetcher_ctx *) ipcom_list_first(&ctx_list_head);
while ( ctx )
{
next = (Ipike_http_fetcher_ctx *) ipcom_list_next(&ctx->list);
/* Socket ready for write? (connect done?)*/
if ( IP_FD_ISSET(ctx->fd, write_fds) )
{
/* Try to send the request */
ipike_http_fetcher_send_request(ctx);
num_processed++;
}
/* Socket ready for read? */
else if ( IP_FD_ISSET(ctx->fd, read_fds) )
{
/* Process response */
ipike_http_fetcher_process_response(ctx);
num_processed++;
}
ctx = next;
}
return num_processed;
}
/*
*===========================================================================
* ipike_http_fetcher_cleanup
*===========================================================================
*/
IP_GLOBAL void
ipike_http_fetcher_cleanup(void)
{
Ipike_http_fetcher_ctx *ctx;
/* Close any pending http requests */
for ( ctx = (Ipike_http_fetcher_ctx *) ipcom_list_first(&ctx_list_head);
ctx != IP_NULL;
ctx = (Ipike_http_fetcher_ctx *) ipcom_list_first(&ctx_list_head) )
{
/* Notify failures to user callback */
ctx->callback(IPIKE_FAILED, IP_NULL, 0, ctx->callback_ctx);
/* Remove from list */
ipcom_list_remove(&ctx->list);
/* Destroy context */
ipike_http_fetcher_ctx_destroy(ctx);
}
}
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/