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
*
* Copyright (C) 2007, 2009, 2011, 2015 Gregory Nutt. All rights reserved.
@ -46,9 +46,7 @@
* Included Files
****************************************************************************/
#ifndef CONFIG_WEBCLIENT_HOST
# include <nuttx/config.h>
#endif
#include <nuttx/config.h>
#include <sys/types.h>
/****************************************************************************
@ -93,6 +91,7 @@
* of valid bytes is datend - offset.
* 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.
* arg - User argument passed to callback.
*/
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:
* 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
* 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

View File

@ -99,14 +99,14 @@ static inline uint16_t ping_newid(void)
* Name: ping_gethostip
*
* 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
* 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:
* 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 */
/* No host name support */
/* Convert strings to numeric IPv6 address */
/* Convert strings to numeric IPv4 address */
int ret = inet_pton(AF_INET, hostname, dest);

View File

@ -98,14 +98,14 @@ static inline uint16_t ping6_newid(void)
* Name: ping6_gethostip
*
* 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
* 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:
* 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
****************************************************************************/
#ifndef CONFIG_WEBCLIENT_HOST
# include <nuttx/config.h>
# include <nuttx/compiler.h>
# include <debug.h>
#endif
#include <nuttx/config.h>
#include <nuttx/compiler.h>
#include <debug.h>
#include <sys/socket.h>
#include <sys/time.h>
@ -62,6 +60,7 @@
#include <unistd.h>
#include <netdb.h>
#include <strings.h>
#include <stdlib.h>
#include <errno.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_httpcache[] = "Cache-Control: no-cache";
/****************************************************************************
* 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)
@ -378,36 +373,54 @@ exit:
* Name: wget_gethostip
*
* 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
* 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:
* 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);
if (he == NULL)
memset(&hint, 0, sizeof(hint));
hint.ai_family = AF_INET;
if (getaddrinfo(hostname, NULL, &hint, &info) != OK)
{
nwarn("WARNING: gethostbyname failed: %d\n", h_errno);
return -ENOENT;
}
else if (he->h_addrtype != AF_INET)
{
nwarn("WARNING: gethostbyname returned an address of type: %d\n",
he->h_addrtype);
return -ENOEXEC;
return ERROR;
}
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;
#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:
* 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
* 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
@ -431,6 +438,7 @@ static int wget_gethostip(FAR char *hostname, in_addr_t *ipv4addr)
* buflen - The size of the user provided buffer
* callback - As data is obtained from the host, this function is
* to dispose of each block of file data as it is received.
* arg - User argument passed to callback.
* mode - Indicates GET or POST modes
*
* 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)
{
struct sockaddr_in server;
struct wget_s ws;
struct wget_s *ws;
struct timeval tv;
bool redirected;
char *dest,post_size[8];
char *dest;
int sockfd;
int len,post_len;
int len;
int ret;
/* Initialize the state structure */
memset(&ws, 0, sizeof(struct wget_s));
ws.buffer = buffer;
ws.buflen = buflen;
ws.port = 80;
ws = calloc(1, sizeof(struct wget_s));
if (!ws)
{
return ERROR;
}
ws->buffer = buffer;
ws->buflen = buflen;
ws->port = 80;
/* Parse the hostname (with optional port number) and filename from the URL */
ret = netlib_parsehttpurl(url, &ws.port,
ws.hostname, CONFIG_WEBCLIENT_MAXHOSTNAME,
ws.filename, CONFIG_WEBCLIENT_MAXFILENAME);
ret = netlib_parsehttpurl(url, &ws->port,
ws->hostname, CONFIG_WEBCLIENT_MAXHOSTNAME,
ws->filename, CONFIG_WEBCLIENT_MAXFILENAME);
if (ret != 0)
{
nwarn("WARNING: Malformed HTTP URL: %s\n", url);
free(ws);
set_errno(-ret);
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 */
@ -482,10 +496,10 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
* persist with the new connection.
*/
ws.httpstatus = HTTPSTATUS_NONE;
ws.offset = 0;
ws.datend = 0;
ws.ndx = 0;
ws->httpstatus = HTTPSTATUS_NONE;
ws->offset = 0;
ws->datend = 0;
ws->ndx = 0;
/* 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 */
nerr("ERROR: socket failed: %d\n", errno);
free(ws);
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 */
server.sin_family = AF_INET;
server.sin_port = htons(ws.port);
ret = wget_gethostip(ws.hostname, &server.sin_addr.s_addr);
server.sin_port = htons(ws->port);
ret = wget_gethostip(ws->hostname, &server.sin_addr);
if (ret < 0)
{
/* 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 */
dest = ws.buffer;
dest = ws->buffer;
if (mode == WGET_MODE_POST)
{
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
dest = wget_strcpy(dest, ws.filename);
dest = wget_strcpy(dest, ws->filename);
#else
//dest = wget_urlencode_strcpy(dest, ws.filename);
dest = wget_strcpy(dest, ws.filename);
//dest = wget_urlencode_strcpy(dest, ws->filename);
dest = wget_strcpy(dest, ws->filename);
#endif
*dest++ = ISO_space;
dest = wget_strcpy(dest, g_http10);
dest = wget_strcpy(dest, g_httpcrnl);
dest = wget_strcpy(dest, g_httphost);
dest = wget_strcpy(dest, ws.hostname);
dest = wget_strcpy(dest, ws->hostname);
dest = wget_strcpy(dest, g_httpcrnl);
if (mode == WGET_MODE_POST)
{
int post_len;
char post_size[8];
dest = wget_strcpy(dest, g_httpform);
dest = wget_strcpy(dest, g_httpcrnl);
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_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, 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.
*/
ws.state = WEBCLIENT_STATE_STATUSLINE;
ws->state = WEBCLIENT_STATE_STATUSLINE;
redirected = false;
for (;;)
{
ws.datend = recv(sockfd, ws.buffer, ws.buflen, 0);
if (ws.datend < 0)
ws->datend = recv(sockfd, ws->buffer, ws->buflen, 0);
if (ws->datend < 0)
{
nerr("ERROR: recv failed: %d\n", errno);
ret = ws.datend;
ret = ws->datend;
goto errout_with_errno;
}
else if (ws.datend == 0)
else if (ws->datend == 0)
{
ninfo("Connection lost\n");
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 */
ws.offset = 0;
if (ws.state == WEBCLIENT_STATE_STATUSLINE)
ws->offset = 0;
if (ws->state == WEBCLIENT_STATE_STATUSLINE)
{
ret = wget_parsestatus(&ws);
ret = wget_parsestatus(ws);
if (ret < 0)
{
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 */
if (ws.state == WEBCLIENT_STATE_HEADERS)
if (ws->state == WEBCLIENT_STATE_HEADERS)
{
ret = wget_parseheaders(&ws);
ret = wget_parseheaders(ws);
if (ret < 0)
{
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 */
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 */
callback(&ws.buffer, ws.offset, ws.datend, &buflen, arg);
callback(&ws->buffer, ws->offset, ws->datend, &buflen, arg);
}
else
{
@ -656,12 +674,14 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
}
while (redirected);
free(ws);
return OK;
errout_with_errno:
set_errno(-ret);
errout:
close(sockfd);
free(ws);
return ERROR;
}
@ -677,10 +697,10 @@ errout:
char *web_post_str(FAR char *buffer, int *size, FAR char *name,
FAR char *value)
{
char *dst=buffer;
buffer = wget_strcpy(buffer,name);
char *dst = buffer;
buffer = wget_strcpy(buffer, name);
buffer = wget_strcpy(buffer, "=");
buffer = wget_urlencode_strcpy(buffer,value);
buffer = wget_urlencode_strcpy(buffer, value);
*size = buffer - dst;
return dst;
}
@ -693,7 +713,7 @@ char *web_post_str(FAR char *buffer, int *size, FAR char *name,
#ifdef WGET_USE_URLENCODE
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
@ -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,
FAR char **value, int len)
{
char *dst=buffer;
char *dst = buffer;
int wlen;
int i;
@ -721,7 +741,7 @@ char *web_posts_str(FAR char *buffer, int *size, FAR char **name,
buffer += wlen;
}
*size=buffer-dst;
*size = buffer - dst;
return dst;
}
#endif
@ -751,12 +771,6 @@ int web_posts_strlen(FAR char **name, FAR char **value, int len)
* Description:
* 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
* 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
@ -766,6 +780,7 @@ int web_posts_strlen(FAR char **name, FAR char **value, int len)
* buflen - The size of the user provided buffer
* callback - As data is obtained from the host, this function is
* to dispose of each block of file data as it is received.
* arg - User argument passed to callback.
*
* Returned Value:
* 0: if the GET operation completed successfully;