|  | /* Copyright (c) 2015 Google, Inc. | 
|  | * Barret Rhoden <brho@cs.berkeley.edu> | 
|  | * See LICENSE for details. | 
|  | * | 
|  | * getaddrinfo/freeaddrinfo. | 
|  | * | 
|  | * Supports IPv4, TCP, UDP, and maybe a couple protocols/socktypes. | 
|  | * | 
|  | * Doesn't handle service resolution yet, and only returns one addrinfo. | 
|  | */ | 
|  |  | 
|  | #include <netdb.h> | 
|  | #include <malloc.h> | 
|  | #include <string.h> | 
|  | #include <stdlib.h> | 
|  | #include <arpa/inet.h> | 
|  | #include <netinet/in.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | /* Helper, given the serv string, figures out the port and protocol.  Returns 0 | 
|  | * on success or an EAI_ error. */ | 
|  | static int serv_get_portprotocol(const char *serv, unsigned long *port_ret, | 
|  | int *protocol_ret, struct addrinfo *hints) | 
|  | { | 
|  | char *strtoul_end = 0; | 
|  | unsigned long port = 0;	/* uninitialized, up to the main caller */ | 
|  | int protocol = 0;	/* any protocol, will assume TCP/UDP later */ | 
|  |  | 
|  | if (serv) { | 
|  | if (serv[0] == '\0') | 
|  | return EAI_NONAME; | 
|  | port = strtoul(serv, &strtoul_end, 0); | 
|  | /* Already checked that serv != \0 */ | 
|  | if (*strtoul_end != '\0') { | 
|  | if (hints->ai_flags & AI_NUMERICSERV) | 
|  | return EAI_NONAME; | 
|  | /* CS lookup */ | 
|  | /* TODO: get a port, maybe a protocol.  If we have a | 
|  | * restriction on the protocol from hints, check that | 
|  | * here.  Probably need to rework this a bit if we have | 
|  | * multiple protocols. */ | 
|  | return EAI_NONAME; | 
|  | } | 
|  | } | 
|  | *port_ret = port; | 
|  | *protocol_ret = protocol; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Helper: fills in all of the addr related info in ai for an ipv4 addr/port. | 
|  | * addr is in network order.  port is in host order. */ | 
|  | static void addrinfo_filladdr_ipv4(struct addrinfo *ai, in_addr_t addr, | 
|  | uint16_t port) | 
|  | { | 
|  | struct sockaddr_in *sa_in4 = (struct sockaddr_in*)ai->ai_addr; | 
|  | ai->ai_family = AF_INET; | 
|  | ai->ai_addrlen = sizeof(struct sockaddr_in); | 
|  | sa_in4->sin_family = AF_INET; | 
|  | sa_in4->sin_addr.s_addr = addr; | 
|  | sa_in4->sin_port = htons(port); | 
|  | } | 
|  |  | 
|  | static int resolve_name_to_ipv4(const char *name, struct in_addr *hostaddr) | 
|  | { | 
|  | struct hostent host, *result; | 
|  | char buf[1024]; | 
|  | int _herrno; | 
|  | int ret; | 
|  | ret = gethostbyname2_r(name, AF_INET, &host, buf, sizeof(buf), | 
|  | &result, &_herrno); | 
|  | /* TODO: deal with the herrno errors and errno errors. */ | 
|  | if (ret) | 
|  | return -1; | 
|  | assert(result == &host); | 
|  | memcpy(hostaddr, host.h_addr_list[0], host.h_length); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Given the node string, fills in the addr related info in ai: the ai_family, | 
|  | * ai_addr, and ai_addrlen.  The ai_addr needs the port too, though that's a big | 
|  | * ugly.  We only support ipv4, so there's just one of these.  Returns 0 on | 
|  | * success, EAI_ error o/w. */ | 
|  | static int node_fill_addrinfo(const char *node, struct addrinfo *ai, | 
|  | uint16_t port, struct addrinfo *hints) | 
|  | { | 
|  | struct in_addr local_addr; | 
|  | if (!node) { | 
|  | /* AI_PASSIVE only matters with no node name */ | 
|  | if (hints->ai_flags & AI_PASSIVE) { | 
|  | /* Caller wants to bind */ | 
|  | addrinfo_filladdr_ipv4(ai, INADDR_ANY, port); | 
|  | } else { | 
|  | /* Caller wants to send from localhost */ | 
|  | addrinfo_filladdr_ipv4(ai, INADDR_LOOPBACK, port); | 
|  | } | 
|  | } else { | 
|  | if (inet_pton(AF_INET, node, &local_addr) == 1) { | 
|  | addrinfo_filladdr_ipv4(ai, local_addr.s_addr, port); | 
|  | } else { | 
|  | if (hints->ai_flags & AI_NUMERICHOST) | 
|  | return EAI_NONAME; | 
|  | if (resolve_name_to_ipv4(node, &local_addr)) | 
|  | return EAI_FAIL; | 
|  | addrinfo_filladdr_ipv4(ai, local_addr.s_addr, port); | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int proto_to_socktype(int protocol) | 
|  | { | 
|  | switch (protocol) { | 
|  | case IPPROTO_IP: | 
|  | case IPPROTO_TCP: | 
|  | return SOCK_STREAM; | 
|  | case IPPROTO_ICMP: | 
|  | case IPPROTO_UDP: | 
|  | return SOCK_DGRAM; | 
|  | case IPPROTO_RAW: | 
|  | return SOCK_RAW; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static int socktype_to_proto(int socktype) | 
|  | { | 
|  | switch (socktype) { | 
|  | case SOCK_STREAM: | 
|  | return IPPROTO_TCP; | 
|  | case SOCK_DGRAM: | 
|  | return IPPROTO_UDP; | 
|  | case SOCK_RAW: | 
|  | return IPPROTO_RAW; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int getaddrinfo(const char *node, const char *serv, | 
|  | const struct addrinfo *hints, struct addrinfo **res) | 
|  | { | 
|  | struct addrinfo *ai; | 
|  | struct addrinfo local_hints; | 
|  |  | 
|  | local_hints.ai_socktype = 0; | 
|  | local_hints.ai_protocol = 0; | 
|  | local_hints.ai_family = AF_UNSPEC; | 
|  | local_hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG; | 
|  |  | 
|  | unsigned long port = 0;	/* AF agnostic, hope a long is enough */ | 
|  | int protocol = 0; | 
|  | int ret; | 
|  |  | 
|  | if (hints) | 
|  | local_hints = *hints; | 
|  |  | 
|  | if (!node && !serv) | 
|  | return EAI_NONAME; | 
|  |  | 
|  | /* Only support IPv4 for now */ | 
|  | if ((local_hints.ai_family != AF_UNSPEC) && | 
|  | (local_hints.ai_family != AF_INET)) | 
|  | return EAI_FAMILY; | 
|  |  | 
|  | ai = malloc(sizeof(struct addrinfo)); | 
|  | memset(ai, 0, sizeof(struct addrinfo)); | 
|  | ai->ai_addr = malloc(sizeof(struct sockaddr_storage)); | 
|  | memset(ai->ai_addr, 0, sizeof(struct sockaddr_storage)); | 
|  |  | 
|  | /* Only supporting TCP and UDP for now.  If protocol is 0, later we'll | 
|  | * make addrinfos for both (TODO).  Likewise, we only support DGRAM or | 
|  | * STREAM for socktype. */ | 
|  | if ((ret = serv_get_portprotocol(serv, &port, &protocol, &local_hints))) | 
|  | { | 
|  | freeaddrinfo(ai); | 
|  | return ret; | 
|  | } | 
|  | if ((ret = node_fill_addrinfo(node, ai, port, &local_hints))) { | 
|  | freeaddrinfo(ai); | 
|  | return ret; | 
|  | } | 
|  | /* We have a mostly full addrinfo.  Still missing ai_protocol, socktype, | 
|  | * flags (already 0) and canonname (already 0). | 
|  | * | 
|  | * We might have restrictions on our protocol from the hints or from | 
|  | * what serv specifies.  If we don't have a protocol yet (== 0), that | 
|  | * means we have no restrictions from serv. */ | 
|  | if (local_hints.ai_protocol) { | 
|  | if (protocol && protocol != local_hints.ai_protocol) { | 
|  | /* requested protocol wasn't available */ | 
|  | freeaddrinfo(ai); | 
|  | return EAI_SERVICE; | 
|  | } | 
|  | ai->ai_protocol = local_hints.ai_protocol; | 
|  | } else if (protocol) { | 
|  | ai->ai_protocol = protocol; | 
|  | } else if (local_hints.ai_socktype) { | 
|  | ai->ai_protocol = socktype_to_proto(local_hints.ai_socktype); | 
|  | } else { | 
|  | /* no serv restrictions, no preferences */ | 
|  | ai->ai_protocol = IPPROTO_TCP; | 
|  | /* TODO: consider building a second addrinfo for UDP.  while | 
|  | * you're at it, support IPv6 and a bunch of other options! */ | 
|  | } | 
|  | if (ai->ai_protocol == -1) { | 
|  | freeaddrinfo(ai); | 
|  | return EAI_SERVICE; | 
|  | } | 
|  | ai->ai_socktype = proto_to_socktype(ai->ai_protocol); | 
|  | if (local_hints.ai_socktype && | 
|  | (local_hints.ai_socktype != ai->ai_socktype)) { | 
|  | /* they requested a socktype, but we can't handle it */ | 
|  | freeaddrinfo(ai); | 
|  | return EAI_SOCKTYPE; | 
|  | } | 
|  | /* TODO: support AI_CANONNAME, only in the first ai */ | 
|  | *res = ai; | 
|  | return 0; | 
|  | } | 
|  | libc_hidden_def(getaddrinfo) | 
|  |  | 
|  | void freeaddrinfo(struct addrinfo *ai) | 
|  | { | 
|  | struct addrinfo *next; | 
|  |  | 
|  | if (!ai) | 
|  | return; | 
|  | free(ai->ai_addr); | 
|  | free(ai->ai_canonname); | 
|  | next = ai->ai_next; | 
|  | free(ai); | 
|  | freeaddrinfo(next); | 
|  | } | 
|  | libc_hidden_def(freeaddrinfo) |