File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / compat / getaddrinfo.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 10:46:11 2013 UTC (10 years, 11 months ago) by misho
Branches: sudo, MAIN
CVS tags: v1_8_8p0, v1_8_8, v1_8_7p0, v1_8_7, HEAD
1.8.7

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

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