webclient: use getaddrinfo to get IPv4 addresses on dual stack systems

Signed-off-by: Juha Niskanen <juha.niskanen@haltian.com>
This commit is contained in:
Juha Niskanen 2020-04-22 11:29:02 +03:00 committed by patacongo
parent 51cbd42227
commit f8f16975a9
4 changed files with 105 additions and 97 deletions

View File

@ -1,5 +1,5 @@
/**************************************************************************** /****************************************************************************
* apps/include/netutils/webclient.h * apps/include/netutils/webclient.h
* Header file for the HTTP client * Header file for the HTTP client
* *
* Copyright (C) 2007, 2009, 2011, 2015 Gregory Nutt. All rights reserved. * Copyright (C) 2007, 2009, 2011, 2015 Gregory Nutt. All rights reserved.
@ -46,9 +46,7 @@
* Included Files * Included Files
****************************************************************************/ ****************************************************************************/
#ifndef CONFIG_WEBCLIENT_HOST #include <nuttx/config.h>
# include <nuttx/config.h>
#endif
#include <sys/types.h> #include <sys/types.h>
/**************************************************************************** /****************************************************************************
@ -93,6 +91,7 @@
* of valid bytes is datend - offset. * of valid bytes is datend - offset.
* buflen - A pointer to the length of the buffer. If the callee wishes * buflen - A pointer to the length of the buffer. If the callee wishes
* to change the size of the buffer, it may write to buflen. * to change the size of the buffer, it may write to buflen.
* arg - User argument passed to callback.
*/ */
typedef void (*wget_callback_t)(FAR char **buffer, int offset, typedef void (*wget_callback_t)(FAR char **buffer, int offset,
@ -125,12 +124,6 @@ int web_posts_strlen(FAR char **name, FAR char **value, int len);
* Description: * Description:
* Obtain the requested file from an HTTP server using the GET method. * Obtain the requested file from an HTTP server using the GET method.
* *
* Note: If the function is passed a host name, it must already be in
* the resolver cache in order for the function to connect to the web
* server. It is therefore up to the calling module to implement the
* resolver calls and the signal handler used for reporting a resolve
* query answer.
*
* Input Parameters * Input Parameters
* url - A pointer to a string containing either the full URL to * url - A pointer to a string containing either the full URL to
* the file to get (e.g., http://www.nutt.org/index.html, or * the file to get (e.g., http://www.nutt.org/index.html, or

View File

@ -99,14 +99,14 @@ static inline uint16_t ping_newid(void)
* Name: ping_gethostip * Name: ping_gethostip
* *
* Description: * Description:
* Call gethostbyname() to get the IP address associated with a hostname. * Call getaddrinfo() to get the IP address associated with a hostname.
* *
* Input Parameters * Input Parameters
* hostname - The host name to use in the nslookup. * hostname - The host name to use in the nslookup.
* ipv4addr - The location to return the IPv4 address. * destr - The location to return the IPv4 address.
* *
* Returned Value: * Returned Value:
* Zero (OK) on success; a negated errno value on failure. * Zero (OK) on success; ERROR on failure.
* *
****************************************************************************/ ****************************************************************************/
@ -136,7 +136,7 @@ static int ping_gethostip(FAR const char *hostname, FAR struct in_addr *dest)
#else /* CONFIG_LIBC_NETDB */ #else /* CONFIG_LIBC_NETDB */
/* No host name support */ /* No host name support */
/* Convert strings to numeric IPv6 address */ /* Convert strings to numeric IPv4 address */
int ret = inet_pton(AF_INET, hostname, dest); int ret = inet_pton(AF_INET, hostname, dest);

View File

@ -98,14 +98,14 @@ static inline uint16_t ping6_newid(void)
* Name: ping6_gethostip * Name: ping6_gethostip
* *
* Description: * Description:
* Call gethostbyname() to get the IP address associated with a hostname. * Call getaddrinfo() to get the IP address associated with a hostname.
* *
* Input Parameters * Input Parameters
* hostname - The host name to use in the nslookup. * hostname - The host name to use in the nslookup.
* ipv4addr - The location to return the IPv4 address. * dest - The location to return the IPv6 address.
* *
* Returned Value: * Returned Value:
* Zero (OK) on success; a negated errno value on failure. * Zero (OK) on success; ERROR on failure.
* *
****************************************************************************/ ****************************************************************************/

View File

@ -48,11 +48,9 @@
* Included Files * Included Files
****************************************************************************/ ****************************************************************************/
#ifndef CONFIG_WEBCLIENT_HOST #include <nuttx/config.h>
# include <nuttx/config.h> #include <nuttx/compiler.h>
# include <nuttx/compiler.h> #include <debug.h>
# include <debug.h>
#endif
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/time.h> #include <sys/time.h>
@ -62,6 +60,7 @@
#include <unistd.h> #include <unistd.h>
#include <netdb.h> #include <netdb.h>
#include <strings.h> #include <strings.h>
#include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <arpa/inet.h> #include <arpa/inet.h>
@ -183,10 +182,6 @@ static const char g_httpcontsize[] = "Content-Length: ";
//static const char g_httpconn[] = "Connection: Keep-Alive"; //static const char g_httpconn[] = "Connection: Keep-Alive";
//static const char g_httpcache[] = "Cache-Control: no-cache"; //static const char g_httpcache[] = "Cache-Control: no-cache";
/****************************************************************************
* Private Functions
****************************************************************************/
/**************************************************************************** /****************************************************************************
* Private Functions * Private Functions
****************************************************************************/ ****************************************************************************/
@ -286,7 +281,7 @@ static inline int wget_parsestatus(struct wget_s *ws)
} }
/**************************************************************************** /****************************************************************************
* Name: wget_parsestatus * Name: wget_parseheaders
****************************************************************************/ ****************************************************************************/
static inline int wget_parseheaders(struct wget_s *ws) static inline int wget_parseheaders(struct wget_s *ws)
@ -378,36 +373,54 @@ exit:
* Name: wget_gethostip * Name: wget_gethostip
* *
* Description: * Description:
* Call gethostbyname() to get the IPv4 address associated with a hostname. * Call getaddrinfo() to get the IPv4 address associated with a hostname.
* *
* Input Parameters * Input Parameters
* hostname - The host name to use in the nslookup. * hostname - The host name to use in the nslookup.
* ipv4addr - The location to return the IPv4 address. *
* Output Parameters
* dest - The location to return the IPv4 address.
* *
* Returned Value: * Returned Value:
* Zero (OK) on success; a negated errno value on failure. * Zero (OK) on success; ERROR on failure.
* *
****************************************************************************/ ****************************************************************************/
static int wget_gethostip(FAR char *hostname, in_addr_t *ipv4addr) static int wget_gethostip(FAR char *hostname, FAR struct in_addr *dest)
{ {
FAR struct hostent *he; #ifdef CONFIG_LIBC_NETDB
FAR struct addrinfo hint;
FAR struct addrinfo *info;
FAR struct sockaddr_in *addr;
he = gethostbyname(hostname); memset(&hint, 0, sizeof(hint));
if (he == NULL) hint.ai_family = AF_INET;
if (getaddrinfo(hostname, NULL, &hint, &info) != OK)
{ {
nwarn("WARNING: gethostbyname failed: %d\n", h_errno); return ERROR;
return -ENOENT;
}
else if (he->h_addrtype != AF_INET)
{
nwarn("WARNING: gethostbyname returned an address of type: %d\n",
he->h_addrtype);
return -ENOEXEC;
} }
memcpy(ipv4addr, he->h_addr, sizeof(in_addr_t)); addr = (FAR struct sockaddr_in *)info->ai_addr;
memcpy(dest, &addr->sin_addr, sizeof(struct in_addr));
freeaddrinfo(info);
return OK; return OK;
#else
/* No host name support */
/* Convert strings to numeric IPv4 address */
int ret = inet_pton(AF_INET, hostname, dest);
/* The inet_pton() function returns 1 if the conversion succeeds. It will
* return 0 if the input is not a valid IPv4 dotted-decimal string or -1
* with errno set to EAFNOSUPPORT if the address family argument is
* unsupported.
*/
return (ret > 0) ? OK : ERROR;
#endif
} }
/**************************************************************************** /****************************************************************************
@ -416,12 +429,6 @@ static int wget_gethostip(FAR char *hostname, in_addr_t *ipv4addr)
* Description: * Description:
* Obtain the requested file from an HTTP server using the GET method. * Obtain the requested file from an HTTP server using the GET method.
* *
* Note: If the function is passed a host name, it must already be in
* the resolver cache in order for the function to connect to the web
* server. It is therefore up to the calling module to implement the
* resolver calls and the signal handler used for reporting a resolv
* query answer.
*
* Input Parameters * Input Parameters
* url - A pointer to a string containing either the full URL to * url - A pointer to a string containing either the full URL to
* the file to get (e.g., http://www.nutt.org/index.html, or * the file to get (e.g., http://www.nutt.org/index.html, or
@ -431,6 +438,7 @@ static int wget_gethostip(FAR char *hostname, in_addr_t *ipv4addr)
* buflen - The size of the user provided buffer * buflen - The size of the user provided buffer
* callback - As data is obtained from the host, this function is * callback - As data is obtained from the host, this function is
* to dispose of each block of file data as it is received. * to dispose of each block of file data as it is received.
* arg - User argument passed to callback.
* mode - Indicates GET or POST modes * mode - Indicates GET or POST modes
* *
* Returned Value: * Returned Value:
@ -444,34 +452,40 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
FAR const char *posts, uint8_t mode) FAR const char *posts, uint8_t mode)
{ {
struct sockaddr_in server; struct sockaddr_in server;
struct wget_s ws; struct wget_s *ws;
struct timeval tv; struct timeval tv;
bool redirected; bool redirected;
char *dest,post_size[8]; char *dest;
int sockfd; int sockfd;
int len,post_len; int len;
int ret; int ret;
/* Initialize the state structure */ /* Initialize the state structure */
memset(&ws, 0, sizeof(struct wget_s)); ws = calloc(1, sizeof(struct wget_s));
ws.buffer = buffer; if (!ws)
ws.buflen = buflen; {
ws.port = 80; return ERROR;
}
ws->buffer = buffer;
ws->buflen = buflen;
ws->port = 80;
/* Parse the hostname (with optional port number) and filename from the URL */ /* Parse the hostname (with optional port number) and filename from the URL */
ret = netlib_parsehttpurl(url, &ws.port, ret = netlib_parsehttpurl(url, &ws->port,
ws.hostname, CONFIG_WEBCLIENT_MAXHOSTNAME, ws->hostname, CONFIG_WEBCLIENT_MAXHOSTNAME,
ws.filename, CONFIG_WEBCLIENT_MAXFILENAME); ws->filename, CONFIG_WEBCLIENT_MAXFILENAME);
if (ret != 0) if (ret != 0)
{ {
nwarn("WARNING: Malformed HTTP URL: %s\n", url); nwarn("WARNING: Malformed HTTP URL: %s\n", url);
free(ws);
set_errno(-ret); set_errno(-ret);
return ERROR; return ERROR;
} }
ninfo("hostname='%s' filename='%s'\n", ws.hostname, ws.filename); ninfo("hostname='%s' filename='%s'\n", ws->hostname, ws->filename);
/* The following sequence may repeat indefinitely if we are redirected */ /* The following sequence may repeat indefinitely if we are redirected */
@ -482,10 +496,10 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
* persist with the new connection. * persist with the new connection.
*/ */
ws.httpstatus = HTTPSTATUS_NONE; ws->httpstatus = HTTPSTATUS_NONE;
ws.offset = 0; ws->offset = 0;
ws.datend = 0; ws->datend = 0;
ws.ndx = 0; ws->ndx = 0;
/* Create a socket */ /* Create a socket */
@ -495,6 +509,7 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
/* socket failed. It will set the errno appropriately */ /* socket failed. It will set the errno appropriately */
nerr("ERROR: socket failed: %d\n", errno); nerr("ERROR: socket failed: %d\n", errno);
free(ws);
return ERROR; return ERROR;
} }
@ -511,8 +526,8 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
/* Get the server address from the host name */ /* Get the server address from the host name */
server.sin_family = AF_INET; server.sin_family = AF_INET;
server.sin_port = htons(ws.port); server.sin_port = htons(ws->port);
ret = wget_gethostip(ws.hostname, &server.sin_addr.s_addr); ret = wget_gethostip(ws->hostname, &server.sin_addr);
if (ret < 0) if (ret < 0)
{ {
/* Could not resolve host (or malformed IP address) */ /* Could not resolve host (or malformed IP address) */
@ -536,7 +551,7 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
/* Send the GET request */ /* Send the GET request */
dest = ws.buffer; dest = ws->buffer;
if (mode == WGET_MODE_POST) if (mode == WGET_MODE_POST)
{ {
dest = wget_strcpy(dest, g_httppost); dest = wget_strcpy(dest, g_httppost);
@ -547,21 +562,24 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
} }
#ifndef WGET_USE_URLENCODE #ifndef WGET_USE_URLENCODE
dest = wget_strcpy(dest, ws.filename); dest = wget_strcpy(dest, ws->filename);
#else #else
//dest = wget_urlencode_strcpy(dest, ws.filename); //dest = wget_urlencode_strcpy(dest, ws->filename);
dest = wget_strcpy(dest, ws.filename); dest = wget_strcpy(dest, ws->filename);
#endif #endif
*dest++ = ISO_space; *dest++ = ISO_space;
dest = wget_strcpy(dest, g_http10); dest = wget_strcpy(dest, g_http10);
dest = wget_strcpy(dest, g_httpcrnl); dest = wget_strcpy(dest, g_httpcrnl);
dest = wget_strcpy(dest, g_httphost); dest = wget_strcpy(dest, g_httphost);
dest = wget_strcpy(dest, ws.hostname); dest = wget_strcpy(dest, ws->hostname);
dest = wget_strcpy(dest, g_httpcrnl); dest = wget_strcpy(dest, g_httpcrnl);
if (mode == WGET_MODE_POST) if (mode == WGET_MODE_POST)
{ {
int post_len;
char post_size[8];
dest = wget_strcpy(dest, g_httpform); dest = wget_strcpy(dest, g_httpform);
dest = wget_strcpy(dest, g_httpcrnl); dest = wget_strcpy(dest, g_httpcrnl);
dest = wget_strcpy(dest, g_httpcontsize); dest = wget_strcpy(dest, g_httpcontsize);
@ -569,7 +587,7 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
/* Post content size */ /* Post content size */
post_len = strlen((char *)posts); post_len = strlen((char *)posts);
sprintf(post_size,"%d", post_len); sprintf(post_size, "%d", post_len);
dest = wget_strcpy(dest, post_size); dest = wget_strcpy(dest, post_size);
dest = wget_strcpy(dest, g_httpcrnl); dest = wget_strcpy(dest, g_httpcrnl);
} }
@ -594,18 +612,18 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
* or until we detect that we have been redirected. * or until we detect that we have been redirected.
*/ */
ws.state = WEBCLIENT_STATE_STATUSLINE; ws->state = WEBCLIENT_STATE_STATUSLINE;
redirected = false; redirected = false;
for (;;) for (;;)
{ {
ws.datend = recv(sockfd, ws.buffer, ws.buflen, 0); ws->datend = recv(sockfd, ws->buffer, ws->buflen, 0);
if (ws.datend < 0) if (ws->datend < 0)
{ {
nerr("ERROR: recv failed: %d\n", errno); nerr("ERROR: recv failed: %d\n", errno);
ret = ws.datend; ret = ws->datend;
goto errout_with_errno; goto errout_with_errno;
} }
else if (ws.datend == 0) else if (ws->datend == 0)
{ {
ninfo("Connection lost\n"); ninfo("Connection lost\n");
close(sockfd); close(sockfd);
@ -614,10 +632,10 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
/* Handle initial parsing of the status line */ /* Handle initial parsing of the status line */
ws.offset = 0; ws->offset = 0;
if (ws.state == WEBCLIENT_STATE_STATUSLINE) if (ws->state == WEBCLIENT_STATE_STATUSLINE)
{ {
ret = wget_parsestatus(&ws); ret = wget_parsestatus(ws);
if (ret < 0) if (ret < 0)
{ {
goto errout_with_errno; goto errout_with_errno;
@ -626,9 +644,9 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
/* Parse the HTTP data */ /* Parse the HTTP data */
if (ws.state == WEBCLIENT_STATE_HEADERS) if (ws->state == WEBCLIENT_STATE_HEADERS)
{ {
ret = wget_parseheaders(&ws); ret = wget_parseheaders(ws);
if (ret < 0) if (ret < 0)
{ {
goto errout_with_errno; goto errout_with_errno;
@ -637,13 +655,13 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
/* Dispose of the data payload */ /* Dispose of the data payload */
if (ws.state == WEBCLIENT_STATE_DATA) if (ws->state == WEBCLIENT_STATE_DATA)
{ {
if (ws.httpstatus != HTTPSTATUS_MOVED) if (ws->httpstatus != HTTPSTATUS_MOVED)
{ {
/* Let the client decide what to do with the received file */ /* Let the client decide what to do with the received file */
callback(&ws.buffer, ws.offset, ws.datend, &buflen, arg); callback(&ws->buffer, ws->offset, ws->datend, &buflen, arg);
} }
else else
{ {
@ -656,12 +674,14 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
} }
while (redirected); while (redirected);
free(ws);
return OK; return OK;
errout_with_errno: errout_with_errno:
set_errno(-ret); set_errno(-ret);
errout: errout:
close(sockfd); close(sockfd);
free(ws);
return ERROR; return ERROR;
} }
@ -677,10 +697,10 @@ errout:
char *web_post_str(FAR char *buffer, int *size, FAR char *name, char *web_post_str(FAR char *buffer, int *size, FAR char *name,
FAR char *value) FAR char *value)
{ {
char *dst=buffer; char *dst = buffer;
buffer = wget_strcpy(buffer,name); buffer = wget_strcpy(buffer, name);
buffer = wget_strcpy(buffer, "="); buffer = wget_strcpy(buffer, "=");
buffer = wget_urlencode_strcpy(buffer,value); buffer = wget_urlencode_strcpy(buffer, value);
*size = buffer - dst; *size = buffer - dst;
return dst; return dst;
} }
@ -693,7 +713,7 @@ char *web_post_str(FAR char *buffer, int *size, FAR char *name,
#ifdef WGET_USE_URLENCODE #ifdef WGET_USE_URLENCODE
int web_post_strlen(FAR char *name, FAR char *value) int web_post_strlen(FAR char *name, FAR char *value)
{ {
return strlen(name) + urlencode_len(value,strlen(value)) + 1; return strlen(name) + urlencode_len(value, strlen(value)) + 1;
} }
#endif #endif
@ -705,7 +725,7 @@ int web_post_strlen(FAR char *name, FAR char *value)
char *web_posts_str(FAR char *buffer, int *size, FAR char **name, char *web_posts_str(FAR char *buffer, int *size, FAR char **name,
FAR char **value, int len) FAR char **value, int len)
{ {
char *dst=buffer; char *dst = buffer;
int wlen; int wlen;
int i; int i;
@ -721,7 +741,7 @@ char *web_posts_str(FAR char *buffer, int *size, FAR char **name,
buffer += wlen; buffer += wlen;
} }
*size=buffer-dst; *size = buffer - dst;
return dst; return dst;
} }
#endif #endif
@ -751,12 +771,6 @@ int web_posts_strlen(FAR char **name, FAR char **value, int len)
* Description: * Description:
* Obtain the requested file from an HTTP server using the GET method. * Obtain the requested file from an HTTP server using the GET method.
* *
* Note: If the function is passed a host name, it must already be in
* the resolver cache in order for the function to connect to the web
* server. It is therefore up to the calling module to implement the
* resolver calls and the signal handler used for reporting a resolv
* query answer.
*
* Input Parameters * Input Parameters
* url - A pointer to a string containing either the full URL to * url - A pointer to a string containing either the full URL to
* the file to get (e.g., http://www.nutt.org/index.html, or * the file to get (e.g., http://www.nutt.org/index.html, or
@ -766,6 +780,7 @@ int web_posts_strlen(FAR char **name, FAR char **value, int len)
* buflen - The size of the user provided buffer * buflen - The size of the user provided buffer
* callback - As data is obtained from the host, this function is * callback - As data is obtained from the host, this function is
* to dispose of each block of file data as it is received. * to dispose of each block of file data as it is received.
* arg - User argument passed to callback.
* *
* Returned Value: * Returned Value:
* 0: if the GET operation completed successfully; * 0: if the GET operation completed successfully;