Logo Search packages:      
Sourcecode: jabberd14 version File versions  Download package

char* srv_lookup ( pool  p,
const char *  service,
const char *  domain 
)

do a DNS lookup

This function implements a SRV DNS lookup and falls back to normal AAAA/A resolution if no service has been given by the caller.

Parameters:
p memory pool to be used by this function
service which service should be looked up (e.g. "_xmpp-server._tcp")
domain which domain should be looked up
Returns:
comma separated list of results containing IPv4 and IPv6 addresses with or without ports
Todo:
The function honors the priority values of a SRV record but not the weight values. Implement handling of weights!

Definition at line 226 of file srv_resolv.cc.

References __srv_list::host, j_strlen(), __srv_list::last, __srv_list::next, pmalloco(), __srv_list::port, __srv_list::priority, pstrdup(), srv_inet_ntoa(), srv_lookup_aaaa_a(), srv_port2str(), srv_xhash_join(), xhash_get(), and xhash_new().

Referenced by dnsrv_child_process_xstream_io().

{
     unsigned char    reply[1024];     /* Reply buffer */
     int              replylen = 0;
     char             host[1024];
     register HEADER* rheader;               /* Reply header*/
     unsigned char*   rrptr;           /* Current Resource record ptr */
     int              exprc;           /* dn_expand return code */
     int              rrtype;
     long             rrpayloadsz;
     srv_list       svrlist  = NULL;
     srv_list       tempnode = NULL;
     srv_list       iternode = NULL;
     xht          arr_table;     /* Hash of A records (name, ip) */
     spool            result;
     int          result_is_empty = 1;
     char*            ipname;
     char*            ipaddr;
#ifdef WITH_IPV6
     int          error_code;
     struct addrinfo  hints;
     struct addrinfo* addr_res;
#else
     struct hostent*  hp;
#endif

    /* If no service is specified, use a standard gethostbyname call */
    if (service == NULL) {
      result = spool_new(p);
      if (srv_lookup_aaaa_a(result, domain) == 0) {
          return spool_print(result);
      } else {
          return NULL;
      }
    }

    log_debug2(ZONE, LOGT_IO, "srv: SRV resolution of %s.%s", service, domain);

    /* Setup A record hash table */
    arr_table = xhash_new(11);

    /* Initialize lookup system if needed (check global _res structure) */
    if (((_res.options & RES_INIT) == 0) && (res_init() == -1)) {
      log_debug2(ZONE, LOGT_IO, "srv: initialization failed on res_init.");
      return NULL;
    }

    /* Run a SRV query against the specified domain */
    replylen = res_querydomain(service, domain,
          C_IN,               /* Class */
          T_SRV,              /* Type */
          (unsigned char*)&reply,   /* Answer buffer */
          sizeof(reply));           /* Answer buffer sz */

    /* Setup a pointer to the reply header */
    rheader = (HEADER*)reply;

    /* Process SRV response if all conditions are met per RFC 2052:
     * 1.) reply has some data available
     * 2.) no error occurred
     * 3.) there are 1 or more answers available */
    if ( (replylen > 0) && (ntohs(rheader->rcode) == NOERROR) && (ntohs(rheader->ancount) > 0) ) {
      /* Parse out the Question section, and get to the following 
       * RRs (see RFC 1035-4.1.2) */
      exprc = dn_expand(reply,      /* Msg ptr */
            reply + replylen, /* End of msg ptr */
            reply + sizeof(HEADER), /* Offset into msg */
            host, sizeof(host));    /* Dest buffer for expansion */
      if (exprc < 0) {
          log_debug2(ZONE, LOGT_IO, "srv: DN expansion failed for Question section.");
          return NULL;
      }

      /* Determine offset of the first RR */
      rrptr = reply + sizeof(HEADER) + exprc + 4;

      /* Walk the RRs, building a list of targets */
      while (rrptr < (reply + replylen)) {
          /* Expand the domain name */
          exprc = dn_expand(reply, reply + replylen, rrptr, host, sizeof(host));
          if (exprc < 0) {
            log_debug2(ZONE, LOGT_IO, "srv: Whoa nelly! DN expansion failed for RR.");
            return NULL;
          }

          /* Jump to RR info */
          rrptr += exprc;
          rrtype      = (rrptr[0] << 8 | rrptr[1]);  /* Extract RR type */
          rrpayloadsz = (rrptr[8] << 8 | rrptr[9]);  /* Extract RR payload size */
          rrptr += 10;

          /* Process the RR */
          switch(rrtype) {
#ifdef WITH_IPV6
            /* AAAA records should be hashed for the duration of this lookup */
            case T_AAAA:
                /* Allocate a new string to hold the IP address */
                ipaddr = srv_inet_ntop(p, rrptr, AF_INET6);
                /* Copy the domain name */
                ipname = pstrdup(p, host);

                /* Insert name/ip into hash table for future reference */
                srv_xhash_join(p, arr_table, ipname, ipaddr);

               break;
#endif
            /* A records should be hashed for the duration of this lookup */
            case T_A: 
                /* Allocate a new string to hold the IP address */
                ipaddr = srv_inet_ntoa(p, rrptr);
                /* Copy the domain name */
                ipname = pstrdup(p, host);

                /* Insert name/ip into hash table for future reference */
                srv_xhash_join(p, arr_table, ipname, ipaddr);
                
                break;

            /* SRV records should be stored in a sorted list */
            case T_SRV:
                /* Expand the target name */
                exprc = dn_expand(reply, reply + replylen, rrptr + 6, host, sizeof(host));
                if (exprc < 0) {
                  log_debug2(ZONE, LOGT_IO, "srv: DN expansion failed for SRV.");
                  return NULL;
                }

                /* Create a new node */
                tempnode = static_cast<srv_list>(pmalloco(p, sizeof(_srv_list)));
                tempnode->priority = (rrptr[0] << 8 | rrptr[1]);
                tempnode->port     = srv_port2str(p, (rrptr[4] << 8 | rrptr[5]));
                tempnode->host     = pstrdup(p, host);

                log_debug2(ZONE, LOGT_IO, "found SRV record pointing to %s", tempnode->host);

                /* Insert the node in the list */               
                if (svrlist == NULL) {
                  /* first result */
                  svrlist = tempnode;
                } else {
                  srv_list iternode_before = NULL;

                  /* insert result in ordered list */
                  iternode = svrlist;     /* HEAD of list (smallest priority value) */
                  
                  /* find element that stays in front of the new one */
                  /* XXX for elements with the same priority we should use the weight to order
                   * the elements in the list. We are now just ignoring the weight resulting
                   * in an equal distribution across results of the same priority */
                  while (iternode != NULL && iternode->priority < tempnode->priority) {
                      iternode_before = iternode; /* keep pointer to the element before */
                      iternode = iternode->next;      /* switch to next element */
                  }

                  /* iternode now either points to NULL (insert as last element)
                   * or it points to the element after the new one
                   *
                   * iternode_before now either point to NULL (insert as first element)
                   * or it points to the element in front of the new one */

                  /* insert the new element in the list */

                  /* update pointers in the new element */
                  tempnode->next = iternode;
                  tempnode->last = iternode_before;

                  /* update pointer in the previous element */
                  if (iternode_before != NULL) {
                      iternode_before->next = tempnode;
                  } else {
                      /* we are the first element */
                      svrlist = tempnode;
                  }

                  /* update pointer in the following element */
                  if (iternode != NULL) {
                      iternode->last = tempnode;
                  }
                }
          } /* end..switch */

          /* Increment to next RR */
          rrptr += rrpayloadsz;
      }

      /* Now, walk the nicely sorted list and resolve the target's A records, sticking the resolved name in
       * a spooler -- hopefully these have been pre-cached, and arrived along with the SRV reply */
      result = spool_new(p);

      iternode = svrlist;
      while (iternode != NULL) {
          log_debug2(ZONE, LOGT_IO, "processing SRV record pointing to %s", iternode->host);

          /* Check the AAAA/A record hash table first.. */
          ipaddr = (char*)xhash_get(arr_table, iternode->host);

          /* it hasn't been in the additional section, we have to lookup the IP address */
          if (ipaddr == NULL) {
            spool temp_result = spool_new(p);

            log_debug2(ZONE, LOGT_IO, "'%s' not in additional section of DNS reply, looking it up using AAAA/A query", iternode->host);
            srv_lookup_aaaa_a(temp_result, iternode->host);
            ipaddr = spool_print(temp_result);
          }
         
          if (j_strlen(ipaddr) > 0) {
            /* copy the ipaddr as we will modify it */
            char *ptrptr, *token, *ipaddr_copy = strdup(ipaddr);

            /* if there has been a result already, we have to separate by a "," */
            if (!result_is_empty) {
                spool_add(result, ",");
            } else {
                result_is_empty = 0;
            }

            /* add the port number for each address */
            token = strtok_r(ipaddr_copy, ",", &ptrptr);
            while (token != NULL) {
                if (strchr(token, ':')) {
                  /* IPv6 format */
                  spooler(result, "[", token, "]:", iternode->port, result);
                } else {
                  /* IPv4 format */
                  spooler(result, token, ":", iternode->port, result);
                }
                /* get next token */
                token = strtok_r(NULL, ",", &ptrptr);
                if (token) {
                  spool_add(result, ","); /* separate results by ',' */
                }
            }
            /* free our tokenized copy */
            free(ipaddr_copy);
          }
          iternode = iternode->next;
      }
      /* Finally, turn the fully resolved list into a string <ip>:<host>,... */
      return spool_print(result);
    }
    /* Otherwise, return NULL -- it's for the caller to finish up by using
     * standard A records */
    return NULL;  
}


Generated by  Doxygen 1.6.0   Back to index