Annotation of embedaddon/sudo/compat/getaddrinfo.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Replacement for a missing getaddrinfo.
! 3: *
! 4: * This is an implementation of getaddrinfo for systems that don't have one so
! 5: * that networking code can use a consistant interface without #ifdef. It is
! 6: * a fairly minimal implementation, with the following limitations:
! 7: *
! 8: * - IPv4 support only. IPv6 is not supported.
! 9: * - AI_ADDRCONFIG is ignored.
! 10: * - Not thread-safe due to gethostbyname and getservbyname.
! 11: * - SOCK_DGRAM and SOCK_STREAM only.
! 12: * - Multiple possible socket types only generate one addrinfo struct.
! 13: * - Protocol hints aren't used correctly.
! 14: *
! 15: * The last four issues could probably be easily remedied, but haven't been
! 16: * needed to date. Adding IPv6 support isn't worth it; systems with IPv6
! 17: * support should already support getaddrinfo natively.
! 18: *
! 19: * The canonical version of this file is maintained in the rra-c-util package,
! 20: * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
! 21: *
! 22: * Written by Russ Allbery <rra@stanford.edu>
! 23: *
! 24: * The authors hereby relinquish any claim to any copyright that they may have
! 25: * in this work, whether granted under contract or by operation of law or
! 26: * international treaty, and hereby commit to the public, at large, that they
! 27: * shall not, at any time in the future, seek to enforce any copyright in this
! 28: * work against any person or entity, or prevent any person or entity from
! 29: * copying, publishing, distributing or creating derivative works of this
! 30: * work.
! 31: */
! 32:
! 33: #include <config.h>
! 34:
! 35: #include <sys/types.h>
! 36: #include <sys/socket.h>
! 37:
! 38: #include <stdio.h>
! 39: #ifdef STDC_HEADERS
! 40: # include <stdlib.h>
! 41: # include <stddef.h>
! 42: #else
! 43: # ifdef HAVE_STDLIB_H
! 44: # include <stdlib.h>
! 45: # endif
! 46: #endif /* STDC_HEADERS */
! 47: #ifdef HAVE_STRING_H
! 48: # include <string.h>
! 49: #endif /* HAVE_STRING_H */
! 50: #ifdef HAVE_STRINGS_H
! 51: # include <strings.h>
! 52: #endif /* HAVE_STRINGS_H */
! 53: #include <netdb.h>
! 54: #include <errno.h>
! 55:
! 56: #include <netinet/in.h>
! 57:
! 58: #include "compat/getaddrinfo.h"
! 59: #include "missing.h"
! 60:
! 61: /* We need access to h_errno to map errors from gethostbyname. */
! 62: #if !HAVE_DECL_H_ERRNO
! 63: extern int h_errno;
! 64: #endif
! 65:
! 66: /*
! 67: * The netdb constants, which aren't always defined (particularly if h_errno
! 68: * isn't declared). We also make sure that a few of the less-used ones are
! 69: * defined so that we can deal with them in case statements.
! 70: */
! 71: #ifndef HOST_NOT_FOUND
! 72: # define HOST_NOT_FOUND 1
! 73: # define TRY_AGAIN 2
! 74: # define NO_RECOVERY 3
! 75: # define NO_DATA 4
! 76: #endif
! 77: #ifndef NETDB_INTERNAL
! 78: # define NETDB_INTERNAL -1
! 79: #endif
! 80:
! 81: /*
! 82: * If we're running the test suite, rename the functions to avoid conflicts
! 83: * with the system version. Note that we don't rename the structures and
! 84: * constants, but that should be okay (except possibly for gai_strerror).
! 85: */
! 86: #if TESTING
! 87: # define gai_strerror test_gai_strerror
! 88: # define freeaddrinfo test_freeaddrinfo
! 89: # define getaddrinfo test_getaddrinfo
! 90: const char *test_gai_strerror(int);
! 91: void test_freeaddrinfo(struct addrinfo *);
! 92: int test_getaddrinfo(const char *, const char *, const struct addrinfo *,
! 93: struct addrinfo **);
! 94: #endif
! 95:
! 96: /*
! 97: * If the native platform doesn't support AI_NUMERICSERV or AI_NUMERICHOST,
! 98: * pick some other values for them.
! 99: */
! 100: #if TESTING
! 101: # if AI_NUMERICSERV == 0
! 102: # undef AI_NUMERICSERV
! 103: # define AI_NUMERICSERV 0x0080
! 104: # endif
! 105: # if AI_NUMERICHOST == 0
! 106: # undef AI_NUMERICHOST
! 107: # define AI_NUMERICHOST 0x0100
! 108: # endif
! 109: #endif
! 110:
! 111: /*
! 112: * Value representing all of the hint flags set. Linux uses flags up to
! 113: * 0x0400, so be sure not to break when testing on that platform.
! 114: */
! 115: #if TESTING
! 116: # ifdef HAVE_GETADDRINFO
! 117: # define AI_INTERNAL_ALL 0x04ff
! 118: # else
! 119: # define AI_INTERNAL_ALL 0x01ff
! 120: # endif
! 121: #else
! 122: # define AI_INTERNAL_ALL 0x007f
! 123: #endif
! 124:
! 125: /* Table of strings corresponding to the EAI_* error codes. */
! 126: static const char * const gai_errors[] = {
! 127: "Host name lookup failure", /* 1 EAI_AGAIN */
! 128: "Invalid flag value", /* 2 EAI_BADFLAGS */
! 129: "Unknown server error", /* 3 EAI_FAIL */
! 130: "Unsupported address family", /* 4 EAI_FAMILY */
! 131: "Memory allocation failure", /* 5 EAI_MEMORY */
! 132: "Host unknown or not given", /* 6 EAI_NONAME */
! 133: "Service not supported for socket", /* 7 EAI_SERVICE */
! 134: "Unsupported socket type", /* 8 EAI_SOCKTYPE */
! 135: "System error", /* 9 EAI_SYSTEM */
! 136: "Supplied buffer too small", /* 10 EAI_OVERFLOW */
! 137: };
! 138:
! 139: /* Macro to set the len attribute of sockaddr_in. */
! 140: #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
! 141: # define sin_set_length(s) ((s)->sin_len = sizeof(struct sockaddr_in))
! 142: #else
! 143: # define sin_set_length(s) /* empty */
! 144: #endif
! 145:
! 146: /*
! 147: * Used for iterating through arrays. ARRAY_SIZE returns the number of
! 148: * elements in the array (useful for a < upper bound in a for loop).
! 149: */
! 150: #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
! 151:
! 152:
! 153: /*
! 154: * Return a constant string for a given EAI_* error code or a string
! 155: * indicating an unknown error.
! 156: */
! 157: const char *
! 158: gai_strerror(int ecode)
! 159: {
! 160: if (ecode < 1 || (size_t) ecode > ARRAY_SIZE(gai_errors))
! 161: return "Unknown error";
! 162: else
! 163: return gai_errors[ecode - 1];
! 164: }
! 165:
! 166:
! 167: /*
! 168: * Free a linked list of addrinfo structs.
! 169: */
! 170: void
! 171: freeaddrinfo(struct addrinfo *ai)
! 172: {
! 173: struct addrinfo *next;
! 174:
! 175: while (ai != NULL) {
! 176: next = ai->ai_next;
! 177: if (ai->ai_addr != NULL)
! 178: free(ai->ai_addr);
! 179: if (ai->ai_canonname != NULL)
! 180: free(ai->ai_canonname);
! 181: free(ai);
! 182: ai = next;
! 183: }
! 184: }
! 185:
! 186:
! 187: /*
! 188: * Convert a numeric service string to a number with error checking, returning
! 189: * true if the number was parsed correctly and false otherwise. Stores the
! 190: * converted number in the second argument. Equivalent to calling strtol, but
! 191: * with the base always fixed at 10, with checking of errno, ensuring that all
! 192: * of the string is consumed, and checking that the resulting number is
! 193: * positive.
! 194: */
! 195: static int
! 196: convert_service(const char *string, long *result)
! 197: {
! 198: char *end;
! 199:
! 200: if (*string == '\0')
! 201: return 0;
! 202: errno = 0;
! 203: *result = strtol(string, &end, 10);
! 204: if (errno != 0 || *end != '\0' || *result < 0)
! 205: return 0;
! 206: return 1;
! 207: }
! 208:
! 209:
! 210: /*
! 211: * Allocate a new addrinfo struct, setting some defaults given that this
! 212: * implementation is IPv4 only. Also allocates an attached sockaddr_in and
! 213: * zeroes it, per the requirement for getaddrinfo. Takes the socktype,
! 214: * canonical name (which is copied if not NULL), address, and port. Returns
! 215: * NULL on a memory allocation failure.
! 216: */
! 217: static struct addrinfo *
! 218: gai_addrinfo_new(int socktype, const char *canonical, struct in_addr addr,
! 219: unsigned short port)
! 220: {
! 221: struct addrinfo *ai;
! 222:
! 223: ai = malloc(sizeof(*ai));
! 224: if (ai == NULL)
! 225: return NULL;
! 226: ai->ai_addr = malloc(sizeof(struct sockaddr_in));
! 227: if (ai->ai_addr == NULL) {
! 228: free(ai);
! 229: return NULL;
! 230: }
! 231: ai->ai_next = NULL;
! 232: if (canonical == NULL)
! 233: ai->ai_canonname = NULL;
! 234: else {
! 235: ai->ai_canonname = strdup(canonical);
! 236: if (ai->ai_canonname == NULL) {
! 237: freeaddrinfo(ai);
! 238: return NULL;
! 239: }
! 240: }
! 241: memset(ai->ai_addr, 0, sizeof(struct sockaddr_in));
! 242: ai->ai_flags = 0;
! 243: ai->ai_family = AF_INET;
! 244: ai->ai_socktype = socktype;
! 245: ai->ai_protocol = (socktype == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP;
! 246: ai->ai_addrlen = sizeof(struct sockaddr_in);
! 247: ((struct sockaddr_in *) ai->ai_addr)->sin_family = AF_INET;
! 248: ((struct sockaddr_in *) ai->ai_addr)->sin_addr = addr;
! 249: ((struct sockaddr_in *) ai->ai_addr)->sin_port = htons(port);
! 250: sin_set_length((struct sockaddr_in *) ai->ai_addr);
! 251: return ai;
! 252: }
! 253:
! 254:
! 255: /*
! 256: * Look up a service. Takes the service name (which may be numeric), the hint
! 257: * flags, a pointer to the socket type (used to determine whether TCP or UDP
! 258: * services are of interest and, if 0, is filled in with the result of
! 259: * getservbyname if the service was not numeric), and a pointer to the
! 260: * addrinfo struct to fill in. Returns 0 on success or an EAI_* error on
! 261: * failure.
! 262: */
! 263: static int
! 264: gai_service(const char *servname, int flags, int *type, unsigned short *port)
! 265: {
! 266: struct servent *servent;
! 267: const char *protocol;
! 268: long value;
! 269:
! 270: if (convert_service(servname, &value)) {
! 271: if (value > (1L << 16) - 1)
! 272: return EAI_SERVICE;
! 273: *port = value;
! 274: } else {
! 275: if (flags & AI_NUMERICSERV)
! 276: return EAI_NONAME;
! 277: if (*type != 0)
! 278: protocol = (*type == SOCK_DGRAM) ? "udp" : "tcp";
! 279: else
! 280: protocol = NULL;
! 281:
! 282: /*
! 283: * We really technically should be generating an addrinfo struct for
! 284: * each possible protocol unless type is set, but this works well
! 285: * enough for what I need this for.
! 286: */
! 287: servent = getservbyname(servname, protocol);
! 288: if (servent == NULL)
! 289: return EAI_NONAME;
! 290: if (strcmp(servent->s_proto, "udp") == 0)
! 291: *type = SOCK_DGRAM;
! 292: else if (strcmp(servent->s_proto, "tcp") == 0)
! 293: *type = SOCK_STREAM;
! 294: else
! 295: return EAI_SERVICE;
! 296: *port = htons(servent->s_port);
! 297: }
! 298: return 0;
! 299: }
! 300:
! 301:
! 302: /*
! 303: * Look up a host and fill in a linked list of addrinfo structs with the
! 304: * results, one per IP address of the returned host. Takes the name or IP
! 305: * address of the host as a string, the lookup flags, the type of socket (to
! 306: * fill into the addrinfo structs), the port (likewise), and a pointer to
! 307: * where the head of the linked list should be put. Returns 0 on success or
! 308: * the appropriate EAI_* error.
! 309: */
! 310: static int
! 311: gai_lookup(const char *nodename, int flags, int socktype, unsigned short port,
! 312: struct addrinfo **res)
! 313: {
! 314: struct addrinfo *ai, *first, *prev;
! 315: struct in_addr addr;
! 316: struct hostent *host;
! 317: const char *canonical;
! 318: int i;
! 319:
! 320: if (inet_aton(nodename, &addr)) {
! 321: canonical = (flags & AI_CANONNAME) ? nodename : NULL;
! 322: ai = gai_addrinfo_new(socktype, canonical, addr, port);
! 323: if (ai == NULL)
! 324: return EAI_MEMORY;
! 325: *res = ai;
! 326: return 0;
! 327: } else {
! 328: if (flags & AI_NUMERICHOST)
! 329: return EAI_NONAME;
! 330: host = gethostbyname(nodename);
! 331: if (host == NULL)
! 332: switch (h_errno) {
! 333: case HOST_NOT_FOUND:
! 334: return EAI_NONAME;
! 335: case TRY_AGAIN:
! 336: case NO_DATA:
! 337: return EAI_AGAIN;
! 338: case NO_RECOVERY:
! 339: return EAI_FAIL;
! 340: case NETDB_INTERNAL:
! 341: default:
! 342: return EAI_SYSTEM;
! 343: }
! 344: if (host->h_addr_list[0] == NULL)
! 345: return EAI_FAIL;
! 346: canonical = (flags & AI_CANONNAME)
! 347: ? ((host->h_name != NULL) ? host->h_name : nodename)
! 348: : NULL;
! 349: first = NULL;
! 350: prev = NULL;
! 351: for (i = 0; host->h_addr_list[i] != NULL; i++) {
! 352: if (host->h_length != sizeof(addr)) {
! 353: freeaddrinfo(first);
! 354: return EAI_FAIL;
! 355: }
! 356: memcpy(&addr, host->h_addr_list[i], sizeof(addr));
! 357: ai = gai_addrinfo_new(socktype, canonical, addr, port);
! 358: if (ai == NULL) {
! 359: freeaddrinfo(first);
! 360: return EAI_MEMORY;
! 361: }
! 362: if (first == NULL) {
! 363: first = ai;
! 364: prev = ai;
! 365: } else {
! 366: prev->ai_next = ai;
! 367: prev = ai;
! 368: }
! 369: }
! 370: *res = first;
! 371: return 0;
! 372: }
! 373: }
! 374:
! 375:
! 376: /*
! 377: * The actual getaddrinfo implementation.
! 378: */
! 379: int
! 380: getaddrinfo(const char *nodename, const char *servname,
! 381: const struct addrinfo *hints, struct addrinfo **res)
! 382: {
! 383: struct addrinfo *ai;
! 384: struct in_addr addr;
! 385: int flags, socktype, status;
! 386: unsigned short port;
! 387:
! 388: /* Take the hints into account and check them for validity. */
! 389: if (hints != NULL) {
! 390: flags = hints->ai_flags;
! 391: socktype = hints->ai_socktype;
! 392: if ((flags & AI_INTERNAL_ALL) != flags)
! 393: return EAI_BADFLAGS;
! 394: if (hints->ai_family != AF_UNSPEC && hints->ai_family != AF_INET)
! 395: return EAI_FAMILY;
! 396: if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
! 397: return EAI_SOCKTYPE;
! 398:
! 399: /* EAI_SOCKTYPE isn't quite right, but there isn't anything better. */
! 400: if (hints->ai_protocol != 0) {
! 401: int protocol = hints->ai_protocol;
! 402: if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
! 403: return EAI_SOCKTYPE;
! 404: }
! 405: } else {
! 406: flags = 0;
! 407: socktype = 0;
! 408: }
! 409:
! 410: /*
! 411: * See what we're doing. If nodename is null, either AI_PASSIVE is set or
! 412: * we're getting information for connecting to a service on the loopback
! 413: * address. Otherwise, we're getting information for connecting to a
! 414: * remote system.
! 415: */
! 416: if (servname == NULL)
! 417: port = 0;
! 418: else {
! 419: status = gai_service(servname, flags, &socktype, &port);
! 420: if (status != 0)
! 421: return status;
! 422: }
! 423: if (nodename != NULL)
! 424: return gai_lookup(nodename, flags, socktype, port, res);
! 425: else {
! 426: if (servname == NULL)
! 427: return EAI_NONAME;
! 428: if ((flags & AI_PASSIVE) == AI_PASSIVE)
! 429: addr.s_addr = INADDR_ANY;
! 430: else
! 431: addr.s_addr = htonl(0x7f000001UL);
! 432: ai = gai_addrinfo_new(socktype, NULL, addr, port);
! 433: if (ai == NULL)
! 434: return EAI_MEMORY;
! 435: *res = ai;
! 436: return 0;
! 437: }
! 438: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>