149 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			149 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;
 | |
| }
 | 
