Keep your time right
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

403 lines
11 KiB

/*
* Copyright (c) 2001, 02 Motoyuki Kasahara
* Copyright (c) 2017-2021 Carsten Sonne Larsen <cs@innolan.net>
*
* 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 of the project 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 PROJECT 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 PROJECT 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.
*/
/*
* This program provides getaddrinfo() and getnameinfo() described in
* RFC2133, 2553 and 3493. These functions are mainly used for IPv6
* application to resolve hostname or address.
*
* This program is designed to be working on traditional IPv4 systems
* which don't have those functions. Therefore, this implementation
* supports IPv4 only.
*
* This program is useful for application which should support both IPv6
* and traditional IPv4 systems. Use genuine getaddrinfo() and getnameinfo()
* provided by system if the system supports IPv6. Otherwise, use this
* implementation.
*
* This program also provides freeaddrinfo() and gai_strerror().
*
* Restriction:
* getaddrinfo() and getnameinfo() of this program are NOT thread safe.
*/
#include "config.h"
#include "net_getaddrinfo.h"
#include "mem.h"
#include "logmod.h"
#define MODULENAME "Network"
#define gettext(string) (string)
#define _(string) (string)
#define N_(string) (string)
extern int gai_h_errno;
#ifdef AOS3
extern int h_errno;
#endif
/*
* Error messages for gai_strerror().
*/
static char *eai_errlist[] = {
N_("Success"),
/* EAI_ADDRFAMILY */
N_("Address family for hostname not supported"),
/* EAI_AGAIN */
N_("Temporary failure in name resolution"),
/* EAI_BADFLAGS */
N_("Invalid value for ai_flags"),
/* EAI_FAIL */
N_("Non-recoverable failure in name resolution"),
/* EAI_FAMILY */
N_("ai_family not supported"),
/* EAI_MEMORY */
N_("Memory allocation failure"),
/* EAI_NONAME */
N_("Hostname nor servname provided, or not known"),
/* EAI_OVERFLOW */
N_("An argument buffer overflowed"),
/* EAI_SERVICE */
N_("servname not supported for ai_socktype"),
/* EAI_SOCKTYPE */
N_("ai_socktype not supported"),
/* EAI_SYSTEM */
N_("System error returned in errno")};
/*
* Default hints for getaddrinfo().
*/
static struct addrinfo default_hints = {
0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL};
/*
* gai_strerror().
*/
#if defined(__CLIB2__)
STRPTR gai_strerror(LONG ecode)
#else
const char *gai_strerror(int ecode)
#endif
{
if (ecode < 0 || ecode > EAI_SYSTEM)
return _("Unknown error");
return gettext(eai_errlist[ecode]);
}
/*
* freeaddrinfo().
*/
void freeaddrinfo(struct addrinfo *ai)
{
struct addrinfo *next_ai;
while (ai != NULL)
{
if (ai->ai_canonname != NULL)
FreeMemSafe(ai->ai_canonname);
if (ai->ai_addr != NULL)
FreeMemSafe(ai->ai_addr);
next_ai = ai->ai_next;
FreeMemSafe(ai);
ai = next_ai;
}
}
/*
* Return 1 if the string `s' represents an integer.
*/
static int is_integer(const char *s)
{
if (*s == '-' || *s == '+')
s++;
if (*s < '0' || '9' < *s)
return 0;
s++;
while ('0' <= *s && *s <= '9')
s++;
return (*s == '\0');
}
/*
* Return 1 if the string `s' represents an IPv4 address.
* Unlike inet_addr(), it doesn't permit malformed notation such
* as "192.168".
*/
static int
is_address(const char *s)
{
const static char delimiters[] = {'.', '.', '.', '\0'};
int i, j;
int octet;
for (i = 0; i < 4; i++)
{
if (*s == '0' && *(s + 1) != delimiters[i])
return 0;
for (j = 0, octet = 0; '0' <= *s && *s <= '9' && j < 3; s++, j++)
octet = octet * 10 + (*s - '0');
if (j == 0 || octet > 255 || *s != delimiters[i])
return 0;
s++;
}
return 1;
}
/*
* getaddrinfo().
*/
#if defined(__CLIB2__)
LONG getaddrinfo(
STRPTR nodename,
STRPTR servname,
struct addrinfo *hints,
struct addrinfo **res)
#else
int getaddrinfo(
const char *nodename,
const char *servname,
const struct addrinfo *hints,
struct addrinfo **res)
#endif
{
struct addrinfo *head_res = NULL;
struct addrinfo *tail_res = NULL;
struct addrinfo *new_res;
struct sockaddr_in *sa_in;
struct in_addr **addr_list;
struct in_addr *addr_list_buf[2];
struct in_addr addr_buf;
struct in_addr **ap;
struct servent *servent;
struct hostent *hostent;
const char *canonname = NULL;
in_port_t port;
LONG port_parse;
int saved_h_errno;
int result = 0;
saved_h_errno = h_errno;
if (nodename == NULL && servname == NULL)
{
result = EAI_NONAME;
goto end;
}
if (hints != NULL)
{
if (hints->ai_family != PF_INET && hints->ai_family != PF_UNSPEC)
{
result = EAI_FAMILY;
goto end;
}
if (hints->ai_socktype != SOCK_DGRAM && hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != 0)
{
result = EAI_SOCKTYPE;
goto end;
}
}
else
{
hints = &default_hints;
}
if (servname != NULL)
{
if (is_integer(servname))
{
StrToLong((STRPTR)servname, &port_parse);
port = htons(port_parse);
}
else
{
if (hints->ai_flags & AI_NUMERICSERV)
{
result = EAI_NONAME;
goto end;
}
if (hints->ai_socktype == SOCK_DGRAM)
servent = getservbyname((char *)servname, (char *)"udp");
else if (hints->ai_socktype == SOCK_STREAM)
servent = getservbyname((char *)servname, (char *)"tcp");
else if (hints->ai_socktype == 0)
servent = getservbyname((char *)servname, (char *)"tcp");
else
{
result = EAI_SOCKTYPE;
goto end;
}
if (servent == NULL)
{
result = EAI_SERVICE;
goto end;
}
port = servent->s_port;
}
}
else
{
port = htons(0);
}
if (nodename != NULL)
{
if (is_address(nodename))
{
addr_buf.s_addr = inet_addr(nodename);
addr_list_buf[0] = &addr_buf;
addr_list_buf[1] = NULL;
addr_list = addr_list_buf;
if (hints->ai_flags & AI_CANONNAME && !(hints->ai_flags & AI_NUMERICHOST))
{
hostent = gethostbyaddr((const char *)&addr_buf, sizeof(struct in_addr), AF_INET);
if (hostent != NULL)
canonname = (const char *)hostent->h_name;
else
canonname = (const char *)nodename;
}
}
else
{
if (hints->ai_flags & AI_NUMERICHOST)
{
result = EAI_NONAME;
goto end;
}
hostent = gethostbyname(nodename);
if (hostent == NULL)
{
switch (h_errno)
{
case HOST_NOT_FOUND:
case NO_DATA:
result = EAI_NONAME;
goto end;
case TRY_AGAIN:
result = EAI_AGAIN;
goto end;
default:
result = EAI_FAIL;
goto end;
}
}
addr_list = (struct in_addr **)hostent->h_addr_list;
if (hints->ai_flags & AI_CANONNAME)
canonname = hostent->h_name;
}
}
else
{
if (hints->ai_flags & AI_PASSIVE)
addr_buf.s_addr = htonl(INADDR_ANY);
else
addr_buf.s_addr = htonl(0x7F000001);
addr_list_buf[0] = &addr_buf;
addr_list_buf[1] = NULL;
addr_list = addr_list_buf;
}
for (ap = addr_list; *ap != NULL; ap++)
{
new_res = AllocStructSafe(struct addrinfo);
if (new_res == NULL)
{
if (head_res != NULL)
freeaddrinfo(head_res);
result = EAI_MEMORY;
goto end;
}
new_res->ai_family = PF_INET;
new_res->ai_socktype = hints->ai_socktype;
new_res->ai_protocol = hints->ai_protocol;
new_res->ai_addr = NULL;
new_res->ai_addrlen = sizeof(struct sockaddr_in);
new_res->ai_canonname = NULL;
new_res->ai_next = NULL;
new_res->ai_addr = (struct sockaddr *)AllocStructSafe(struct sockaddr_in);
if (new_res->ai_addr == NULL)
{
FreeMemSafe(new_res);
if (head_res != NULL)
freeaddrinfo(head_res);
result = EAI_MEMORY;
goto end;
}
sa_in = (struct sockaddr_in *)new_res->ai_addr;
memset(sa_in, 0, sizeof(struct sockaddr_in));
sa_in->sin_family = PF_INET;
sa_in->sin_port = port;
memcpy(&sa_in->sin_addr, *ap, sizeof(struct in_addr));
if (head_res == NULL)
head_res = new_res;
else
tail_res->ai_next = new_res;
tail_res = new_res;
}
if (canonname != NULL && head_res != NULL)
{
head_res->ai_canonname = AllocStringSafe(StrLen(canonname) + 1);
if (head_res->ai_canonname != NULL)
StrCopy(head_res->ai_canonname, canonname);
}
*res = head_res;
end:
h_errno = saved_h_errno;
gai_h_errno = result;
return result;
}