From 1b1e34e1d57ac1d690fa476e893440f30ebf5f61 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 8 Jul 2015 13:40:52 -0600 Subject: [PATCH] libc: Add gethostbyname(). Untested on initial commit --- include/netdb.h | 96 ++++--- libc/Kconfig | 21 ++ libc/lib_internal.h | 7 + libc/net/Make.defs | 6 + libc/net/lib_gethostbyname.c | 106 ++++++++ libc/net/lib_gethostbynamer.c | 345 +++++++++++++++++++++++++ libc/net/lib_parsehostfile.c | 457 ++++++++++++++++++++++++++++++++++ 7 files changed, 1007 insertions(+), 31 deletions(-) create mode 100644 libc/net/lib_gethostbyname.c create mode 100644 libc/net/lib_gethostbynamer.c create mode 100644 libc/net/lib_parsehostfile.c diff --git a/include/netdb.h b/include/netdb.h index 28669c54fe..0274281e40 100644 --- a/include/netdb.h +++ b/include/netdb.h @@ -156,6 +156,27 @@ #define EAI_SYSTEM 8 #define EAI_OVERFLOW 9 +/* h_errno values that may be returned by gethosbyname(), gethostbyname_r(), + * gethostbyaddr(), or gethostbyaddr_r() + * + * HOST_NOT_FOUND - No such host is known. + * + * NO_DATA - The server recognized the request and the name, but no + * address is available. Another type of request to the name server + * for the domain might return an answer. + * + * NO_RECOVERY - An unexpected server failure occurred which cannot be + * recovered. + * + * TRY_AGAIN - A temporary and possibly transient error occurred, such as + * a failure of a server to respond. + */ + +#define HOST_NOT_FOUND 1 +#define NO_DATA 2 +#define NO_RECOVERY 3 +#define TRY_AGAIN 4 + /**************************************************************************** * Public Types ****************************************************************************/ @@ -215,7 +236,7 @@ struct addrinfo FAR struct sockaddr *ai_addr; /* Socket address of socket. */ FAR char *ai_canonname; /* Canonical name of service location. */ - sFAR truct addrinfo *ai_next; /* Pointer to next in list. */ + FAR struct addrinfo *ai_next; /* Pointer to next in list. */ }; /**************************************************************************** @@ -235,46 +256,59 @@ extern "C" * macro or an identifier declared with external linkage. */ -/* To be provided */ +/* REVISIT: This should at least be per-task? */ +EXTERN int h_errno; /**************************************************************************** * Public Function Prototypes ****************************************************************************/ +#ifdef CONFIG_LIB_NETDB #if 0 /* None of these are yet supported */ -void endhostent(void); -void endnetent(void); -void endprotoent(void); -void endservent(void); -void freeaddrinfo(FAR struct addrinfo *); -const char *gai_strerror(int); -int getaddrinfo(FAR const char *restrict, - FAR const char *restrict, - FAR const struct addrinfo *restrict, - FAR struct addrinfo **restrict); -struct hostent *gethostbyaddr(FAR const void *, socklen_t, int); -struct hostent *gethostbyname(FAR const char *); -struct hostent *gethostent(void); -int getnameinfo(FAR const struct sockaddr *restrict, socklen_t, - FAR char *restrict, socklen_t, FAR char *restrict, - socklen_t, int); -struct netent *getnetbyaddr(uint32_t, int); -struct netent *getnetbyname(FAR const char *); -struct netent *getnetent(void); -struct protoent *getprotobyname(FAR const char *); -struct protoent *getprotobynumber(int); -struct protoent *getprotoent(void); -struct servent *getservbyname(FAR const char *, FAR const char *); -struct servent *getservbyport(int, FAR const char *); -struct servent *getservent(void); -void sethostent(int); -void setnetent(int); -void setprotoent(int); -void setservent(int); +void endhostent(void); +void endnetent(void); +void endprotoent(void); +void endservent(void); +void freeaddrinfo(FAR struct addrinfo *); +FAR const char *gai_strerror(int); +int getaddrinfo(FAR const char *restrict, + FAR const char *restrict, + FAR const struct addrinfo *restrict, + FAR struct addrinfo **restrict); +FAR struct hostent *gethostbyaddr(FAR const void *, socklen_t, int); +#endif + +FAR struct hostent *gethostbyname(FAR const char *); + +#if 0 /* None of these are yet supported */ +FAR struct hostent *gethostent(void); +int getnameinfo(FAR const struct sockaddr *restrict, socklen_t, + FAR char *restrict, socklen_t, FAR char *restrict, + socklen_t, int); +FAR struct netent *getnetbyaddr(uint32_t, int); +FAR struct netent *getnetbyname(FAR const char *); +FAR struct netent *getnetent(void); +FAR struct protoent *getprotobyname(FAR const char *); +FAR struct protoent *getprotobynumber(int); +FAR struct protoent *getprotoent(void); +FAR struct servent *getservbyname(FAR const char *, FAR const char *); +FAR struct servent *getservbyport(int, FAR const char *); +FAR struct servent *getservent(void); +void sethostent(int); +void setnetent(int); +void setprotoent(int); +void setservent(int); #endif /* None of these are yet supported */ +/* Non-standard interfaces similar to Glibc 2 interfaces */ + +int gethostbyname_r(FAR const char *name, FAR struct hostent *host, + FAR char *buf, size_t buflen, int *h_errnop); + +#endif /* CONFIG_LIB_NETDB */ + #undef EXTERN #ifdef __cplusplus } diff --git a/libc/Kconfig b/libc/Kconfig index 013c28eb63..e9614d29f0 100644 --- a/libc/Kconfig +++ b/libc/Kconfig @@ -504,6 +504,27 @@ config ARCH_BZERO endif # ARCH_OPTIMIZED_FUNCTIONS +menuconfig LIB_NETDB + bool "Network database support" + default n + depends on FS_READABLE + +if LIB_NETDB + +config NETDB_HOSTCONF_PATH + string "Path to host configuration file" + default "/etc/hosts" + +config NETDB_MAX_ALTNAMES + int "Max number of alternate host names" + default 4 + +config NETDB_BUFSIZE + int "gethostname() buffer size" + default 128 + +endif # LIB_NETDB + comment "Non-standard Library Support" if BUILD_PROTECTED || BUILD_KERNEL diff --git a/libc/lib_internal.h b/libc/lib_internal.h index a9f8a3fbce..1aae22c494 100644 --- a/libc/lib_internal.h +++ b/libc/lib_internal.h @@ -216,6 +216,13 @@ double lib_expi(size_t n); float lib_sqrtapprox(float x); #endif +/* Defined in lib_parsehostfile.c */ + +#ifdef CONFIG_LIB_NETDB +ssize_t lib_parse_hostfile(FAR FILE *stream, FAR struct hostent *host, + FAR char *buf, size_t buflen); +#endif + #undef EXTERN #if defined(__cplusplus) } diff --git a/libc/net/Make.defs b/libc/net/Make.defs index 6fe3cce9c2..a32799a91c 100644 --- a/libc/net/Make.defs +++ b/libc/net/Make.defs @@ -44,6 +44,12 @@ ifeq ($(CONFIG_NET_ROUTE),y) CSRCS += lib_addroute.c lib_delroute.c endif +# netdb support + +ifeq ($(CONFIG_LIB_NETDB),y) +CSRCS += lib_gethostbyname.c lib_gethostbynamer.c lib_parsehostfile.c +endif + # Add the net directory to the build DEPPATH += --dep-path net diff --git a/libc/net/lib_gethostbyname.c b/libc/net/lib_gethostbyname.c new file mode 100644 index 0000000000..f5d15942aa --- /dev/null +++ b/libc/net/lib_gethostbyname.c @@ -0,0 +1,106 @@ +/**************************************************************************** + * net/netdb/lib_gethostbyname.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name NuttX 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 THE + * COPYRIGHT OWNER 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "lib_internal.h" + +#ifdef CONFIG_LIB_NETDB + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_NETDB_BUFSIZE +# define CONFIG_NETDB_BUFSIZE 128 +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct hostent g_hostent; +static char g_hostbuffer[CONFIG_NETDB_BUFSIZE]; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: gethostname + * + * Description: + * The gethostbyname() function returns a structure of type hostent for + * the given host name. Here name is either a hostname, or an IPv4 address + * in standard dot notation (as for inet_addr(3)), or an IPv6 address in + * colon (and possibly dot) notation. + * + * If name is an IPv4 or IPv6 address, no lookup is performed and + * gethostbyname_r() simply copies name into the h_name field + * and its struct in_addr equivalent into the h_addr_list[0] field of the + * returned hostent structure. + * + * Input Parameters: + * name - The name of the host to find. + * + * Returned Value: + * Upon successful completion, this function will return a pointer to a + * hostent structure if the requested entry was found, and a null pointer + * if the end of the database was reached or the requested entry was not + * found. + * + * Upon unsuccessful completion, gethostbyname() will set h_errno to + * indicate the error + * + ****************************************************************************/ + +FAR struct hostent *gethostbyname(FAR const char *name) +{ + int ret; + + DEBUGASSERT(name != NULL); + ret = gethostbyname_r(name, &g_hostent, g_hostbuffer, CONFIG_NETDB_BUFSIZE, + &h_errno); + return ret == 0 ? &g_hostent : NULL; +} + +#endif /* CONFIG_LIB_NETDB */ diff --git a/libc/net/lib_gethostbynamer.c b/libc/net/lib_gethostbynamer.c new file mode 100644 index 0000000000..3e8a5aeef3 --- /dev/null +++ b/libc/net/lib_gethostbynamer.c @@ -0,0 +1,345 @@ +/**************************************************************************** + * net/netdb/lib_gethostbynamer.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name NuttX 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 THE + * COPYRIGHT OWNER 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "lib_internal.h" + +#ifdef CONFIG_LIB_NETDB + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Type Definitions + ****************************************************************************/ + +/* This is the maximum number of alternate host names supported by this + * implementation: + */ + +#ifndef CONFIG_NETDB_MAX_ALTNAMES +# define CONFIG_NETDB_MAX_ALTNAMES 4 +#endif + +#ifndef CONFIG_NETDB_HOSTCONF_PATH +# define CONFIG_NETDB_HOSTCONF_PATH "/etc/hosts" +#endif + +/* This is the layout of the caller provided memory area */ + +struct hostent_info_s +{ + FAR char *hi_addrlist[2]; + char hi_data[1]; +}; + +/**************************************************************************** + * Private functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lib_numeric_address + * + * Description: + * Check if the name is a numeric IP address. In this case, simply copy + * name into the h_name field and its struct in_addr equivalent into the + * h_addr_list[0] field of the returned hostent structure. + * + * Input paramters: + * stream - File stream of the opened hosts file with the file pointer + * positioned at the beginning of the next host entry. + * host - Caller provided location to return the host data. + * buf - Caller provided buffer to hold string data associated with the + * host data. + * buflen - The size of the caller-provided buffer + * + * Returned Value: + * Zero (0) is returned if the name is an numeric IP address. + * + ****************************************************************************/ + +static int lib_numeric_address(FAR const char *name, FAR struct hostent *host, + FAR char *buf, size_t buflen) +{ + FAR struct hostent_info_s *info; + FAR char *ptr; + size_t addrlen; + int namelen; + int ret; + + /* Verify that we have a buffer big enough to get started (it still may not + * be big enough). + */ + + if (buflen <= sizeof(struct hostent_info_s)) + { + return -ERANGE; + } + + info = (FAR struct hostent_info_s *)buf; + ptr = info->hi_data; + buflen -= (sizeof(struct hostent_info_s) - 1); + + memset(host, 0, sizeof(struct hostent)); + memset(info, 0, sizeof(struct hostent_info_s)); + + /* If the address contains a colon, then it might be a numeric IPv6 */ + + if (strchr(name, ':') != NULL) + { + /* Make sure that space remains to hold the IPv6 address */ + + addrlen = sizeof(struct in6_addr); + if (buflen < addrlen) + { + return -ERANGE; + } + + ret = inet_pton(AF_INET6, name, ptr); + if (ret < 0) + { + /* Conversion failed. Must not be a IPv6 address */ + + return 1; + } + + host->h_addrtype = AF_INET6; + } + /* If the address contains a colon, then it might be a numeric IPv6 */ + + else if (strchr(name, '.') != NULL) + { + /* Make sure that space remains to hold the IPv4 address */ + + addrlen = sizeof(struct in_addr); + if (buflen < addrlen) + { + return -ERANGE; + } + + ret = inet_pton(AF_INET, name, ptr); + if (ret < 0) + { + /* Conversion failed. Must not be an IPv4 address */ + + return 1; + } + + host->h_addrtype = AF_INET; + } + + /* No colon? No period? Can't be a numeric address */ + + else + { + return 1; + } + + info->hi_addrlist[0] = ptr; + host->h_addr_list = info->hi_addrlist; + host->h_length = addrlen; + + ptr += addrlen; + buflen -= addrlen; + + /* And copy name */ + + namelen = strlen(name); + if (addrlen + namelen + 1 > buflen) + { + return -ERANGE; + } + + strncpy(ptr, name, buflen); + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: gethostname_r + * + * Description: + * The gethostbyname_r() function returns a structure of type hostent for + * the given host name. Here name is either a hostname, or an IPv4 address + * in standard dot notation (as for inet_addr(3)), or an IPv6 address in + * colon (and possibly dot) notation. + * + * If name is an IPv4 or IPv6 address, no lookup is performed and + * gethostbyname_r() simply copies name into the h_name field + * and its struct in_addr equivalent into the h_addr_list[0] field of the + * returned hostent structure. + * + * gethostname_r() is *not* POSIX but is similar to a Glibc extension and is + * used internally by NuttX to implement the POSIX gethostname(). + * + * Input Parameters: + * name - The name of the host to find. + * host - Caller provided location to return the host data. + * buf - Caller provided buffer to hold string data associated with the + * host data. + * buflen - The size of the caller-provided buffer + * h_errnop - There h_errno value returned in the event of a failure. + * + * Returned Value: + * Zero (OK) is returned on success, -1 (ERROR) is returned on a failure + * with the returned h_errno value provided the reason for the failure. + * + ****************************************************************************/ + +int gethostbyname_r(FAR const char *name, FAR struct hostent *host, + FAR char *buf, size_t buflen, int *h_errnop) +{ + FAR FILE *stream; + int herrnocode; + int nread; + + DEBUGASSERT(name != NULL && host != NULL && buf != NULL); + + /* Make sure that the h_errno has a non-error code */ + + if (h_errnop) + { + *h_errnop = 0; + } + + /* Check for a numeric hostname */ + + if (lib_numeric_address(name, host, buf, buflen) == 0) + { + /* Yes.. we are done */ + + return OK; + } + + /* Try to find the name in the HOSTALIASES environment variable */ + /* REVISIT: Not implemented */ + + /* Try to get the host address using the DNS name server */ + /* REVISIT: Not implemented */ + + /* Search the hosts file for a match */ + + stream = fopen(CONFIG_NETDB_HOSTCONF_PATH, "r"); + if (stream == NULL) + { + int errcode = errno; + + ndbg("ERROR: Failed to open the hosts file %s: %d\n", + CONFIG_NETDB_HOSTCONF_PATH, errcode); + UNUSED(errcode); + + herrnocode = NO_RECOVERY; + goto errorout_with_herrnocode; + } + + /* Loop reading entries from the hosts file until a match is found or + * until we hit the end-of-file. + */ + + do + { + /* Read the next entry from the hosts file */ + + nread = lib_parse_hostfile(stream, host, buf, buflen); + if (nread < 0) + { + /* Possible errors: + * ERANGE - Buffer not big enough + * ESPIPE - End of file (or possibly a read error). + * EAGAIN - Error parsing the line (E.g., missing hostname) + */ + + if (ESPIPE) + { + nread = 0; + } + else if (ERANGE) + { + herrnocode = NO_RECOVERY; + goto errorout_with_stream; + } + } + else if (nread > 0) + { + /* We successfully read the entry */ + + nvdbg("Comparing %s to %s\n", name, host->h_name); + if (strcmp(name, host->h_name) == 0) + { + /* We have a match */ + + fclose(stream); + return OK; + } + } + } + while (nread != 0); + + /* We get here when the end of the hosts file is encountered without + * finding the hostname. + */ + + herrnocode = HOST_NOT_FOUND; + +errorout_with_stream: + fclose(stream); + +errorout_with_herrnocode: + if (h_errnop) + { + *h_errnop = herrnocode; + } + + return ERROR; +} + +#endif /* CONFIG_LIB_NETDB */ diff --git a/libc/net/lib_parsehostfile.c b/libc/net/lib_parsehostfile.c new file mode 100644 index 0000000000..9ee743f6ee --- /dev/null +++ b/libc/net/lib_parsehostfile.c @@ -0,0 +1,457 @@ +/**************************************************************************** + * net/netdb/lib_parsehostile.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name NuttX 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 THE + * COPYRIGHT OWNER 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "lib_internal.h" + +#ifdef CONFIG_LIB_NETDB + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* This is the maximum number of alternate host names supported by this + * implementation: + */ + +#ifndef CONFIG_NETDB_MAX_ALTNAMES +# define CONFIG_NETDB_MAX_ALTNAMES 4 +#endif + +/* Check if character is any kind of white space (except for newline) */ + +#define lib_isspace(c) \ + ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\f' || c== '\v') + +/**************************************************************************** + * Private Type Definitions + ****************************************************************************/ + +/* This is the layout of the caller provided memory area */ + +struct hostent_info_s +{ + FAR char *hi_aliases[CONFIG_NETDB_MAX_ALTNAMES + 1]; + FAR char *hi_addrlist[2]; + char hi_data[1]; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* When the header is included, h_errno shall be available as a + * modifiable lvalue of type int. It is unspecified whether h_errno is a + * macro or an identifier declared with external linkage. + */ + +/* REVISIT: This should at least be per-task? */ +int h_errno; + +/**************************************************************************** + * Private functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lib_skipspaces + ****************************************************************************/ + +static int lib_skipspaces(FAR FILE *stream, FAR size_t *nread) +{ + int ch; + + /* Skip over most white space (but not newline) */ + + do + { + ch = fgetc(stream); + if (ch != EOF) + { + (*nread)++; + } + } + while (lib_isspace(ch)); + + return ch; +} + +/**************************************************************************** + * Name: lib_skipline + ****************************************************************************/ + +static int lib_skipline(FAR FILE *stream, FAR size_t *nread) +{ + int ch; + + /* Skip over all characters until we encounter a newline or end-of-file */ + + do + { + ch = fgetc(stream); + if (ch != EOF) + { + (*nread)++; + } + } + while (ch != EOF && ch != '\n'); + + return ch; +} + +/**************************************************************************** + * Name: lib_copystring + * + * Returned Value: + * Number of bytes written to the buffer on success. 0 if the end of + * file is encountered (or a read error occurs). A negated errno value on + * any failure: + * + * -ERANGE - Insufficient buffer space + * + ****************************************************************************/ + +static ssize_t lib_copystring(FAR FILE *stream, FAR char *ptr, FAR size_t *nread, + size_t buflen, FAR int *terminator) +{ + size_t nwritten = 0; + int ch; + + /* Copy the string from the file until any whitepace delimiter is + * encountered + */ + + for(;;) + { + /* Read the next character from the file */ + + ch = fgetc(stream); + if (ch != EOF) + { + (*nread)++; + } + + /* Check for whitepace (including \n') or EOF terminating the string */ + + if (isspace(ch) || ch == EOF) + { + /* Remeber what terminated the string */ + + *terminator = ch; + + /* Add NUL termination */ + + *ptr++ = '\0'; + + /* Return EOF if nothing has written */ + return nwritten == 0 ? 0 : nwritten + 1; + } + + /* Write the next string to the buffer */ + + *ptr++ = ch; + nwritten++; + + /* There there space to buffer one more character? */ + + if (nwritten >= buflen) + { + return -ERANGE; + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lib_parse_hostfile + * + * Description: + * Parse the next line from the hosts file. + * + * Input Parameters: + * stream - File stream of the opened hosts file with the file pointer + * positioned at the beginning of the next host entry. + * host - Caller provided location to return the host data. + * buf - Caller provided buffer to hold string data associated with the + * host data. + * buflen - The size of the caller-provided buffer + * + * Returned Value: + * The non-zero number of bytes read from the hosts file is returned if + * the host entry was successfully read. Zero is returned if the end + * of the host file has been reached. A negated errno value is return + * in the event a failure: + * + * ERANGE - Buffer not big enough + * ESPIPE - End of file (or possibly a read error). + * EAGAIN - Error parsing the line (E.g., missing hostname) + * + ****************************************************************************/ + +ssize_t lib_parse_hostfile(FAR FILE *stream, FAR struct hostent *host, + FAR char *buf, size_t buflen) +{ + FAR struct hostent_info_s *info; + FAR char addrstring[48]; + FAR char *ptr; + FAR char *start; + size_t addrlen; + size_t nread = 0; + ssize_t nwritten; + int ret; + int ch; + int i; + + /* Verify that we have a buffer big enough to get started (it still may not + * be big enough). + */ + + if (buflen <= sizeof(struct hostent_info_s)) + { + return -ERANGE; + } + + info = (FAR struct hostent_info_s *)buf; + ptr = info->hi_data; + buflen -= (sizeof(struct hostent_info_s) - 1); + + memset(host, 0, sizeof(struct hostent)); + memset(info, 0, sizeof(struct hostent_info_s)); + + /* Skip over any leading spaces */ + + do + { + ch = lib_skipspaces(stream, &nread); + if (ch == EOF) + { + return -EPIPE; + } + + /* Skip comment lines beginning with '#' */ + + if (ch == '#') + { + /* Skip to the end of line. */ + + ch = lib_skipline(stream, &nread); + if (ch == EOF) + { + return -EPIPE; + } + } + } + while (ch == '\n'); + + /* Parse the IP address */ + + addrstring[0] = ch; + + nwritten = lib_copystring(stream, addrstring, &nread, 48, &ch); + if (nwritten <= 0) + { + return nwritten; + } + + if (!lib_isspace(ch)) + { + /* The string was terminated with a newline of EOF */ + + return -EAGAIN; + } + + /* If the address contains a colon, say it is IPv6 */ + + if (strchr(addrstring, ':') != NULL) + { + /* Make sure that space remains to hold the IPv6 address */ + + addrlen = sizeof(struct in6_addr); + if (buflen < addrlen) + { + return -ERANGE; + } + + ret = inet_pton(AF_INET6, addrstring, ptr); + if (ret < 0) + { + /* Conversion failed. Entry is corrupted */ + + (void)lib_skipline(stream, &nread); + return -EAGAIN; + } + + host->h_addrtype = AF_INET6; + } + else + { + /* Make sure that space remains to hold the IPv4 address */ + + addrlen = sizeof(struct in_addr); + if (buflen < addrlen) + { + return -ERANGE; + } + + ret = inet_pton(AF_INET, addrstring, ptr); + if (ret < 0) + { + /* Conversion failed. Entry is corrupted */ + + (void)lib_skipline(stream, &nread); + return -EAGAIN; + } + + host->h_addrtype = AF_INET; + } + + info->hi_addrlist[0] = ptr; + host->h_addr_list = info->hi_addrlist; + host->h_length = addrlen; + + ptr += addrlen; + buflen -= addrlen; + + /* Skip over any additional whitespace */ + + ch = lib_skipspaces(stream, &nread); + if (ch == EOF) + { + return -EPIPE; + } + else if (ch == '\n') + { + return -EAGAIN; + } + + /* Parse the host name */ + + start = ptr; + *ptr++ = ch; + buflen--; + + nwritten = lib_copystring(stream, ptr, &nread, buflen, &ch); + if (nwritten <= 0) + { + return nwritten; + } + + host->h_name = start; + + if (!lib_isspace(ch)) + { + /* The string was terminated with a newline or EOF */ + + return nread; + } + + ptr += nwritten; + buflen -= nwritten; + + /* Parse any host name aliases */ + + for (i = 0; i < CONFIG_NETDB_MAX_ALTNAMES; i++) + { + /* Skip over any leading whitespace */ + + ch = lib_skipspaces(stream, &nread); + if (ch == EOF || ch == '\n') + { + /* No further aliases on the line */ + + return nread; + } + + /* Parse the next alias */ + + start = ptr; + *ptr++ = ch; + buflen--; + + nwritten = lib_copystring(stream, ptr, &nread, buflen, &ch); + if (nwritten < 0) + { + return nwritten; + } + else if (nwritten == 0) + { + return nread; + } + + /* Save the pointer to the beginning of the next alias */ + + info->hi_aliases[i] = start; + if (host->h_aliases == NULL) + { + host->h_aliases = info->hi_aliases; + } + + if (!lib_isspace(ch)) + { + /* The string was terminated with a newline of EOF */ + + return nread; + } + + ptr += nwritten; + buflen -= nwritten; + } + + /* We get here only if there are more than CONFIG_NETDB_MAX_ALTNAMES + * aliases on the line. Skip to the endof the line, ignoring any + * additional aliases. + */ + + (void)lib_skipline(stream, &nread); + return nread; +} + +#endif /* CONFIG_LIB_NETDB */