Annotation of embedaddon/sudo/compat/getaddrinfo.c, revision 1.1.1.3

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>