nuttx/libs/libc/netdb/lib_parsehostfile.c

470 lines
12 KiB
C
Raw Normal View History

/****************************************************************************
* libs/libc/netdb/lib_parsehostfile.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* 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 "lib_netdb.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];
int hi_addrtypes[1];
int hi_lengths[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 (; ; )
{
/* There there space to buffer one more character? */
if (nwritten >= buflen)
{
return -ERANGE;
}
/* 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)
{
/* Remember what terminated the string */
*terminator = ch;
/* Add NUL termination */
*ptr++ = '\0';
/* Return EOF if nothing has written */
return nwritten + 1;
}
/* Write the next string to the buffer */
*ptr++ = ch;
nwritten++;
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: 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 parse_hostfile(FAR FILE *stream, FAR struct hostent_s *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_s));
memset(info, 0, sizeof(struct hostent_info_s));
host->h_addrtypes = info->hi_addrtypes;
host->h_lengths = info->hi_lengths;
host->h_addr_list = info->hi_addrlist;
/* 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 ch == EOF ? -EPIPE : -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_addrtypes[0] = 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_addrtypes[0] = AF_INET;
}
host->h_addr_list[0] = ptr;
host->h_lengths[0] = 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;
}
else if (buflen == 0)
{
return -ERANGE;
}
/* 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++)
{
/* Skip over any leading whitespace */
ch = lib_skipspaces(stream, &nread);
if (ch == EOF || ch == '\n')
{
/* No further aliases on the line */
return nread;
}
else if (buflen == 0 || i >= CONFIG_NETDB_MAX_ALTNAMES)
{
return -ERANGE;
}
/* Parse the next alias */
start = ptr;
*ptr++ = ch;
buflen--;
nwritten = lib_copystring(stream, ptr, &nread, buflen, &ch);
if (nwritten < 0)
{
return nwritten;
}
/* 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;
}
}
#endif /* CONFIG_NETDB_HOSTFILE */