148 lines
3.2 KiB
C
Executable file
148 lines
3.2 KiB
C
Executable file
#include <arpa/inet.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <netdb.h>
|
|
#include <unistd.h>
|
|
|
|
static size_t first_component_len(const char *s)
|
|
{
|
|
const char *first = s;
|
|
|
|
/* find the first dot or end of string */
|
|
while (*s && *s != '.')
|
|
s++;
|
|
|
|
/* return length */
|
|
return s - first;
|
|
}
|
|
|
|
static int getnameinfo_get_host(const struct sockaddr_in *sockaddr,
|
|
char *node, socklen_t nodelen, int flags)
|
|
{
|
|
struct hostent *hostent;
|
|
const char *ipaddr;
|
|
|
|
/* perform look-up */
|
|
if ((flags & NI_NUMERICHOST) != NI_NUMERICHOST)
|
|
{
|
|
hostent = gethostbyaddr(
|
|
(char *) &sockaddr->sin_addr,
|
|
sizeof(sockaddr->sin_addr),
|
|
AF_INET);
|
|
|
|
if (hostent && hostent->h_name)
|
|
{
|
|
/* return the hostname that was found */
|
|
if (nodelen <= strlen(hostent->h_name))
|
|
return EAI_OVERFLOW;
|
|
|
|
strcpy(node, hostent->h_name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if ((flags & NI_NAMEREQD) == NI_NAMEREQD)
|
|
return EAI_NONAME;
|
|
|
|
/* basic implementation to provide numeric values */
|
|
ipaddr = inet_ntoa(sockaddr->sin_addr);
|
|
if (nodelen <= strlen(ipaddr))
|
|
return EAI_OVERFLOW;
|
|
|
|
strcpy(node, ipaddr);
|
|
return 0;
|
|
}
|
|
|
|
static int getnameinfo_get_serv(const struct sockaddr_in *sockaddr,
|
|
char *service, socklen_t servicelen, int flags)
|
|
{
|
|
struct servent *servent;
|
|
unsigned short port;
|
|
|
|
/* perform look-up */
|
|
if ((flags & NI_NUMERICSERV) != NI_NUMERICSERV)
|
|
{
|
|
servent = getservbyport(sockaddr->sin_port,
|
|
((flags & NI_DGRAM) == NI_DGRAM) ? "udp" : "tcp");
|
|
if (servent && servent->s_name)
|
|
{
|
|
/* return the service name that was found */
|
|
if (strlen(servent->s_name) >= servicelen)
|
|
return EAI_OVERFLOW;
|
|
|
|
strcpy(service, servent->s_name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* return port number */
|
|
port = ntohs(sockaddr->sin_port);
|
|
if (snprintf(service, servicelen, "%u", port) >= servicelen)
|
|
return EAI_OVERFLOW;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* getnameinfo is based on
|
|
* http://www.opengroup.org/onlinepubs/009695399/functions/getnameinfo.html
|
|
*/
|
|
int getnameinfo(const struct sockaddr *sa, socklen_t salen,
|
|
char *node, socklen_t nodelen, char *service,
|
|
socklen_t servicelen, int flags)
|
|
{
|
|
int r;
|
|
const struct sockaddr_in *sockaddr;
|
|
|
|
/*
|
|
* The following flags are really supported:
|
|
* - NI_NUMERICHOST
|
|
* - NI_NAMEREQD
|
|
* - NI_NUMERICSERV
|
|
* - NI_DGRAM
|
|
*
|
|
* The following flag is not supported:
|
|
* - NI_NUMERICSCOPE
|
|
*
|
|
* The following flags could have been supported but is not implemented:
|
|
* - NI_NOFQDN
|
|
*/
|
|
|
|
/* check for invalid parameters; only support IPv4 */
|
|
if (sa == NULL)
|
|
{
|
|
errno = EINVAL;
|
|
return EAI_SYSTEM;
|
|
}
|
|
|
|
if (sa->sa_family != AF_INET || salen != sizeof(struct sockaddr_in))
|
|
return EAI_FAMILY;
|
|
|
|
if (flags & ~(NI_NUMERICHOST | NI_NAMEREQD | NI_NUMERICSERV | NI_DGRAM))
|
|
return EAI_BADFLAGS;
|
|
|
|
if ((!node || !nodelen) && (!service || !servicelen))
|
|
return EAI_NONAME;
|
|
|
|
/* look up host */
|
|
sockaddr = (const struct sockaddr_in *) sa;
|
|
if (node && nodelen > 0)
|
|
{
|
|
r = getnameinfo_get_host(sockaddr, node, nodelen, flags);
|
|
if (r)
|
|
return r;
|
|
}
|
|
|
|
/* look up service */
|
|
if (service && servicelen > 0)
|
|
{
|
|
r = getnameinfo_get_serv(sockaddr, service, servicelen, flags);
|
|
if (r)
|
|
return r;
|
|
}
|
|
|
|
return 0;
|
|
}
|