6a3c2aded6
* Simplify EINTR/ECANCEL error handling 1. Add semaphore uninterruptible wait function 2 .Replace semaphore wait loop with a single uninterruptible wait 3. Replace all sem_xxx to nxsem_xxx * Unify the void cast usage 1. Remove void cast for function because many place ignore the returned value witout cast 2. Replace void cast for variable with UNUSED macro
484 lines
12 KiB
C
484 lines
12 KiB
C
/****************************************************************************
|
|
* 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).
|
|
*/
|
|
|
|
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 */
|