"Fossies" - the Fresh Open Source Software Archive

Member "coda-6.9.5/lib-src/base/coda_getaddrinfo.c" (20 Jul 2007, 11370 Bytes) of package /linux/misc/old/coda-6.9.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "coda_getaddrinfo.c" see the Fossies "Dox" file reference documentation.

    1 /* BLURB lgpl
    2 
    3                            Coda File System
    4                               Release 6
    5 
    6           Copyright (c) 2002-2003 Carnegie Mellon University
    7                   Additional copyrights listed below
    8 
    9 This  code  is  distributed "AS IS" without warranty of any kind under
   10 the  terms of the  GNU  Library General Public Licence  Version 2,  as
   11 shown in the file LICENSE. The technical and financial contributors to
   12 Coda are listed in the file CREDITS.
   13 
   14                         Additional copyrights
   15                            none currently
   16 #*/
   17 
   18 /*
   19  * This code handles DNS SRV record (RFC2782), and IN A lookups for a
   20  * specified service within a domain. In a perfect world 'getaddrinfo'
   21  * would do all of this for us ;)
   22  */
   23 
   24 /*
   25  * Special extended version of getaddrinfo for Coda,
   26  *   coda_getaddrinfo      - adds (ai_flags & CODA_AI_RES_SRV), which performs
   27  *               SRV record lookups, additional information such as
   28  *               priority and weights are stored in the returned
   29  *               addrinfo list.
   30  *   coda_reorder_addrinfo - re-shuffles the addrinfo list according to the
   31  *               RFC's recommendations for load-balancing among
   32  *               multiple servers with the same priority.
   33  */
   34 
   35 #ifdef HAVE_CONFIG_H
   36 #include "config.h"
   37 #endif
   38 
   39 #include <sys/types.h>
   40 #include <stdlib.h>
   41 #include <stdio.h>
   42 #include <string.h>
   43 
   44 #ifdef HAVE_NETINET_IN_H
   45 #include <netinet/in.h>
   46 #endif
   47 #ifdef HAVE_ARPA_INET_H
   48 #include <arpa/inet.h>
   49 #endif
   50 #ifdef HAVE_ARPA_NAMESER_H
   51 #include <arpa/nameser.h>
   52 #endif
   53 #ifdef HAVE_RESOLV_H
   54 #include <resolv.h>
   55 #endif
   56 
   57 #include "coda_getservbyname.h"
   58 
   59 #ifndef NS_INT32SZ /* NetBSD and RedHat5 */
   60 #define NS_INT32SZ INT32SZ
   61 #define NS_INT16SZ INT16SZ
   62 #define NS_HFIXEDSZ HFIXEDSZ
   63 #define ns_t_srv T_SRV
   64 #define ns_c_in  C_IN
   65 
   66 #ifndef T_SRV /* MacOS X */
   67 #define T_SRV 33
   68 #endif /* !T_SRV */
   69 #endif /* !NS_INT32SZ */
   70 
   71 #ifdef PF_INET6
   72 #ifndef HAVE_STRUCT_IN6_ADDR
   73 struct in6_addr {
   74     u_int8_t u6_addr[16];
   75 };
   76 #endif
   77 #ifndef HAVE_STRUCT_SOCKADDR_IN6
   78 struct sockaddr_in6 {
   79     u_int16_t sin6_family;
   80     u_int16_t sin6_port;
   81     u_int32_t sin6_flowinfo;
   82     struct in6_addr sin6_addr;
   83 };
   84 #endif
   85 #endif
   86 
   87 #include "coda_getaddrinfo.h"
   88 
   89 #ifdef HAVE_RES_SEARCH
   90 static char *srvdomainname(const char *node, const char *service,
   91                const char *proto)
   92 {
   93     int len = strlen(service) + strlen(proto) + strlen(node) + 6;
   94     char *domain = malloc(len);
   95 
   96     if (domain)
   97     sprintf(domain, "_%s._%s.%s.", service, proto, node);
   98 
   99     return domain;
  100 }
  101 
  102 static int DN_HOST(unsigned char *msg, int mlen, unsigned char **ptr,
  103            char *dest)
  104 {
  105     int len = dn_expand(msg, msg + mlen, *ptr, dest, MAXHOSTNAMELEN);
  106     if (len < 0 || len > MAXHOSTNAMELEN) return -1;
  107     *ptr += len;
  108     return 0;
  109 }
  110 
  111 static int DN_SHORT(unsigned char *msg, int mlen, unsigned char **ptr, int *dest)
  112 {
  113     if (*ptr + NS_INT16SZ > msg + mlen)
  114     return -1;
  115     *dest = ntohs(*(u_int16_t *)*ptr);
  116     *ptr += NS_INT16SZ;
  117     return 0;
  118 }
  119 
  120 static int DN_INT(unsigned char *msg, int mlen, unsigned char **ptr, int *dest)
  121 {
  122     if (*ptr + NS_INT32SZ > msg + mlen)
  123     return -1;
  124     *dest = ntohl(*(u_int32_t *)*ptr);
  125     *ptr += NS_INT32SZ;
  126     return 0;
  127 }
  128 
  129 static int parse_res_reply(unsigned char *answer, int alen,
  130                const struct RPC2_addrinfo *hints,
  131                struct RPC2_addrinfo **res)
  132 {
  133     unsigned char *p = answer;
  134     char name[MAXHOSTNAMELEN];
  135     int priority, weight, port, dummy;
  136     int err = RPC2_EAI_AGAIN, tmperr;
  137 
  138     /* arghhhhh, I don't like digging through libresolv output */
  139     p += NS_HFIXEDSZ; /* what is in the header? probably nothing interesting */
  140 
  141     /* skip original query + type + class */
  142     if (DN_HOST(answer, alen, &p, name) ||
  143     DN_SHORT(answer, alen, &p, &dummy) ||
  144         DN_SHORT(answer, alen, &p, &dummy))
  145     return err; /* corrupted packet, retryable? */
  146 
  147     while (p < answer + alen)
  148     {
  149     int type, size;
  150     struct RPC2_addrinfo *cur = NULL;
  151 
  152     if (DN_HOST(answer, alen, &p, name) ||
  153         DN_SHORT(answer, alen, &p, &type) ||
  154         DN_SHORT(answer, alen, &p, &dummy) || // class
  155         DN_INT(answer, alen, &p, &dummy) || // ttl
  156         DN_SHORT(answer, alen, &p, &size))
  157     {
  158         break; /* corrupted packet? */
  159     }
  160 
  161     if (type != ns_t_srv) {
  162         p += size; 
  163         continue;
  164     }
  165 
  166     if (DN_SHORT(answer, alen, &p, &priority) ||
  167         DN_SHORT(answer, alen, &p, &weight) ||
  168         DN_SHORT(answer, alen, &p, &port) ||
  169         DN_HOST(answer, alen, &p, name))
  170     {
  171         break; /* corrupted packet? */
  172     }
  173 
  174     /* according to the RFC, if there is a SRV record that has '.' as the
  175      * target, the service is decidedly not available at this domain */
  176     if (name[0] == '.' && name[1] == '\0') {
  177         err = RPC2_EAI_NONAME;
  178         break;
  179     }
  180 
  181     tmperr = RPC2_getaddrinfo(name, NULL, hints, &cur);
  182     if (!tmperr) {
  183         struct RPC2_addrinfo *p;
  184         int i;
  185 
  186         /* adjust weight depending on how many results we got */
  187         if (weight) {
  188         for (p = cur, i = 0; p; p = p->ai_next, i++) /**/;
  189         weight /= i;
  190         }
  191 
  192         for (p = cur; p; p = p->ai_next) {
  193         switch(p->ai_family) {
  194         case PF_INET:
  195             ((struct sockaddr_in *)p->ai_addr)->sin_port = htons(port);
  196             break;
  197 #ifdef PF_INET6
  198         case PF_INET6:
  199             ((struct sockaddr_in6 *)p->ai_addr)->sin6_port = htons(port);
  200             break;
  201 #endif
  202         }
  203         p->ai_priority = priority;
  204         p->ai_weight = weight;
  205         }
  206         cur->ai_next = *res;
  207         *res = cur;
  208     }
  209     if (!err || err == RPC2_EAI_AGAIN)
  210         err = tmperr;
  211     }
  212     return err;
  213 }
  214 
  215 static int do_srv_lookup(const char *node, const char *service,
  216              const struct RPC2_addrinfo *hints,
  217              struct RPC2_addrinfo **res)
  218 {
  219 #ifdef TESTING
  220     fprintf(stderr, "Doing SRV record lookup for %s %s\n", node, service);
  221 #endif
  222 
  223     unsigned char answer[1024];
  224     char *srvdomain;
  225     int len;
  226     const char *proto = (hints && hints->ai_protocol == IPPROTO_UDP) ?
  227     "udp" : "tcp";
  228 
  229     srvdomain = srvdomainname(node, service, proto);
  230     if (!srvdomain)
  231     return RPC2_EAI_MEMORY;
  232 
  233     len = res_search(srvdomain, ns_c_in, ns_t_srv, answer, sizeof(answer));
  234 
  235     free(srvdomain);
  236     
  237     /* make sure we actually got a usable answer */
  238     if (len < NS_HFIXEDSZ || len > sizeof(answer))
  239     return RPC2_EAI_FAIL;
  240 
  241     return parse_res_reply(answer, len, hints, res);
  242 }
  243 #endif
  244 
  245 void coda_reorder_addrinfo(struct RPC2_addrinfo **srvs)
  246 {
  247     struct RPC2_addrinfo **tmp, *res, **tail;
  248 
  249     /* sort by priority, lowest first */
  250 start:
  251     /* very simple sort, should be efficient for already sorted list */
  252     for (tmp = srvs; *tmp && (*tmp)->ai_next; tmp = &(*tmp)->ai_next) {
  253     struct RPC2_addrinfo *next = (*tmp)->ai_next;
  254 
  255 #ifdef PF_INET6
  256     /* Should we prefer ipv6 addresses? */
  257     if ((*tmp)->ai_family == PF_INET6 && next->ai_family == PF_INET)
  258         continue;
  259 #endif
  260 
  261     if ((*tmp)->ai_priority < next->ai_priority)
  262         continue;
  263 
  264     /* move 0-weight items to the beginning of their priority */
  265     if ((*tmp)->ai_priority == next->ai_priority &&
  266         ((*tmp)->ai_weight == 0 || next->ai_weight != 0))
  267         continue;
  268 
  269     /* swap current and next */
  270     (*tmp)->ai_next = next->ai_next;
  271     next->ai_next = *tmp;
  272     *tmp = next;
  273 
  274     /* and start again from the beginning */
  275     goto start;
  276     }
  277 
  278     /* then order within each priority by weight */
  279     res = NULL; tail = &res;
  280     while(*srvs)
  281     {
  282     int total_weight = 0;
  283 
  284     /* calculate the sum of all weights of the lowest priority */
  285     for (tmp = srvs; *tmp; tmp = &(*tmp)->ai_next) {
  286         if ((*tmp)->ai_priority != (*srvs)->ai_priority)
  287         break;
  288         total_weight += (*tmp)->ai_weight + 1; 
  289     }
  290 
  291     while (total_weight > 0)
  292     {
  293         int selector = (rand() % total_weight) + 1;
  294 
  295         for (tmp = srvs; *tmp; tmp = &(*tmp)->ai_next) {
  296         selector -= (*tmp)->ai_weight + 1;
  297         if (selector <= 0)
  298             break;
  299         }
  300 
  301         /* selected an entry, pull it off the list and append it to a
  302          * temporary list */
  303         total_weight -= (*tmp)->ai_weight + 1;
  304 
  305         *tail = *tmp;
  306         *tmp = (*tail)->ai_next;
  307         tail = &(*tail)->ai_next;
  308         *tail = NULL;
  309     }
  310     }
  311     *srvs = res;
  312 }
  313 
  314 int coda_getaddrinfo(const char *node, const char *service,
  315              const struct RPC2_addrinfo *hints,
  316              struct RPC2_addrinfo **res)
  317 {
  318     struct RPC2_addrinfo Hints, *srvs = NULL;
  319     static int initialized = 0;
  320     int err = RPC2_EAI_FAIL;
  321 
  322     if (!initialized) {
  323 #ifdef HAVE_RES_SEARCH
  324     /* assuming that if we have res_search, we'll have res_init and
  325      * _res.options and so on */
  326     res_init();
  327 
  328     /* turn off default domain substitutions */
  329     _res.options &= ~(RES_DEFNAMES | RES_DNSRCH);
  330 #endif
  331     initialized = 1;
  332     }
  333 
  334     err = RPC2_EAI_NONAME;
  335     if (hints && (hints->ai_flags & CODA_AI_RES_SRV))
  336     {
  337 #ifdef PF_INET6
  338     char tmp[sizeof(struct in6_addr)];
  339 #endif
  340     char *end;
  341 
  342     /* We want to clear the CODA_AI_RES_SRV, but hints is const
  343      * so we make a copy */
  344     Hints = *hints;
  345     Hints.ai_flags &= ~CODA_AI_RES_SRV;
  346     hints = &Hints;
  347 
  348     /* we can only do an IN SRV record lookup if both node and
  349      * service are specified and not numerical */
  350     if (!node || !service)
  351         return RPC2_EAI_NONAME;
  352 
  353     if (hints->ai_flags & RPC2_AI_NUMERICHOST)
  354         return RPC2_EAI_BADFLAGS;
  355 
  356     if (strtol(service, &end, 10) && *end == '\0')
  357         return RPC2_EAI_BADFLAGS;
  358 
  359 #ifdef PF_INET6
  360     /* check whether we were given an IP address in a format that doesn't
  361      * match the hinted address family */
  362     if (hints->ai_family == PF_INET6 && inet_pton(PF_INET, node, &tmp) > 0)
  363         return RPC2_EAI_BADFLAGS;
  364 
  365     if (hints->ai_family == PF_INET && inet_pton(PF_INET6, node, &tmp) > 0)
  366         return RPC2_EAI_BADFLAGS;
  367 #endif
  368 
  369 #ifdef HAVE_RES_SEARCH
  370     /* try to find SRV records */
  371     err = do_srv_lookup(node, service, hints, &srvs);
  372     if (err == RPC2_EAI_NONAME) /* found a SRV record with a '.' target? */
  373         goto Exit;
  374 
  375     if (!err) {
  376         coda_reorder_addrinfo(&srvs);
  377         goto Exit;
  378     }
  379 #endif
  380     }
  381 
  382     /* when not doing SRV record lookup or when SRV lookup failed,
  383      * we fall back to a normal lookup */
  384     err = RPC2_getaddrinfo(node, service, hints, &srvs);
  385     if (err == RPC2_EAI_SERVICE) {
  386     /* try to map service to port number */
  387     const char *proto = NULL;
  388     struct servent *s;
  389 
  390     if (hints && hints->ai_socktype == SOCK_STREAM) proto = "tcp";
  391     if (hints && hints->ai_socktype == SOCK_DGRAM)  proto = "udp";
  392     s = coda_getservbyname(service, proto);
  393 
  394     if (s) {
  395         char buf[6];
  396         sprintf(buf, "%d", ntohs(s->s_port));
  397         err = RPC2_getaddrinfo(node, buf, hints, &srvs);
  398     }
  399     }
  400 
  401 Exit:
  402     /* append new addresses to the end of **res? */
  403     while (*res) res = &(*res)->ai_next;
  404 
  405     *res = srvs;
  406     return err;
  407 }
  408 
  409 #ifdef TESTING
  410 int main(int argc, char **argv)
  411 {
  412     struct RPC2_addrinfo *res = NULL, *p;
  413     struct sockaddr_in *sin;
  414     int err;
  415 
  416     struct RPC2_addrinfo hints;
  417     memset(&hints, 0, sizeof(hints));
  418     hints.ai_family   = PF_INET;
  419     hints.ai_socktype = SOCK_STREAM;
  420     hints.ai_protocol = IPPROTO_TCP;
  421 
  422     err = coda_getaddrinfo(argv[1], argv[2], &hints, &res);
  423     printf("err: %d\n", err);
  424 
  425     coda_reorder_addrinfo(&res);
  426 
  427     for (p = res; p; p = p->ai_next) {
  428     char buf[RPC2_ADDRSTRLEN];
  429     printf("ai_flags %d ai_family %d ai_socktype %d ai_protocol %d\n",
  430            p->ai_flags, p->ai_family, p->ai_socktype, p->ai_protocol);
  431 
  432     RPC2_formataddrinfo(p, buf, sizeof(*buf));
  433     printf("addrlen %d sin_family %d addr %s\n",
  434            p->ai_addrlen, sin->sin_family, buf);
  435     printf("ai_canonname %s\n", p->ai_canonname);
  436     printf("ai_priority %d ai_weight %d\n", p->ai_priority, p->ai_weight);
  437     }
  438     RPC2_freeaddrinfo(res);
  439 
  440     exit(0);
  441 }
  442 #endif
  443