nuttx/libs/libc/netdb/lib_parsehostfile.c

484 lines
12 KiB
C
Raw Normal View History

/****************************************************************************
* libs/libc/netdb/lib_parsehostile.c
*
* Copyright (C) 2015 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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 <nuttx/config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <arpa/inet.h>
#include "libc.h"
#ifdef CONFIG_NETDB_HOSTFILE
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* 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];
};
/****************************************************************************
* Private functions
****************************************************************************/
/****************************************************************************
* Name: lib_skipspaces
*
* Description:
* Read from the 'stream' until a non-whitespace character is read or the
* end-of-line or end-of-file is encountered.
*
* Input Parameters:
* stream - The stream to read from
* nread - A count to the pointer of characters read. Will be
* incremented after each successful character read.
*
* Returned Value:
* The first non-whitespace character read. This will be the newline
* character of EROF if the end-of-line or end-of-file is encountered.
*
****************************************************************************/
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
*
* Description:
* Read from the 'stream' until the end-of-line or end-of-file is
* encountered.
*
* Input Parameters:
* stream - The stream to read from
* nread - A count to the pointer of characters read. Will be
* incremented after each successful character read.
*
* Returned Value:
* The character that terminated the line. This may be either the newline
* character or EOF.
*
****************************************************************************/
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
*
* Description:
* Read from the 'stream' And copy each byte to the buffer at 'ptr' until
* either a whitespace delimiter, the end-of-line, or the end-of-file is
* encountered.
*
* Input Parameters:
* stream - The stream to read from
* ptr - The pointer to the buffer to receive the string
* nread - A count to the pointer of characters read. Will be
* incremented after each successful character read.
* buflen - The size of the buffer in bytes
* terminator - The actual character the terminated the copy is returned
* to this location.
*
* 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 to hold the string.
*
****************************************************************************/
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;
socklen_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).
*/
2015-10-04 23:04:00 +02:00
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[1], &nread, 47, &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 */
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 */
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.
*/
lib_skipline(stream, &nread);
return nread;
}
#endif /* CONFIG_NETDB_HOSTFILE */