Blame ares_search.c

Packit 514978
Packit 514978
/* Copyright 1998 by the Massachusetts Institute of Technology.
Packit 514978
 *
Packit 514978
 * Permission to use, copy, modify, and distribute this
Packit 514978
 * software and its documentation for any purpose and without
Packit 514978
 * fee is hereby granted, provided that the above copyright
Packit 514978
 * notice appear in all copies and that both that copyright
Packit 514978
 * notice and this permission notice appear in supporting
Packit 514978
 * documentation, and that the name of M.I.T. not be used in
Packit 514978
 * advertising or publicity pertaining to distribution of the
Packit 514978
 * software without specific, written prior permission.
Packit 514978
 * M.I.T. makes no representations about the suitability of
Packit 514978
 * this software for any purpose.  It is provided "as is"
Packit 514978
 * without express or implied warranty.
Packit 514978
 */
Packit 514978
Packit 514978
#include "ares_setup.h"
Packit 514978
Packit 514978
#ifdef HAVE_STRINGS_H
Packit 514978
#  include <strings.h>
Packit 514978
#endif
Packit 514978
Packit 514978
#include "ares.h"
Packit 514978
#include "ares_private.h"
Packit 514978
Packit 514978
struct search_query {
Packit 514978
  /* Arguments passed to ares_search */
Packit 514978
  ares_channel channel;
Packit 514978
  char *name;                   /* copied into an allocated buffer */
Packit 514978
  int dnsclass;
Packit 514978
  int type;
Packit 514978
  ares_callback callback;
Packit 514978
  void *arg;
Packit 514978
Packit 514978
  int status_as_is;             /* error status from trying as-is */
Packit 514978
  int next_domain;              /* next search domain to try */
Packit 514978
  int trying_as_is;             /* current query is for name as-is */
Packit 514978
  int timeouts;                 /* number of timeouts we saw for this request */
Packit 514978
  int ever_got_nodata;          /* did we ever get ARES_ENODATA along the way? */
Packit 514978
};
Packit 514978
Packit 514978
static void search_callback(void *arg, int status, int timeouts,
Packit 514978
                            unsigned char *abuf, int alen);
Packit 514978
static void end_squery(struct search_query *squery, int status,
Packit 514978
                       unsigned char *abuf, int alen);
Packit 514978
static int cat_domain(const char *name, const char *domain, char **s);
Packit 514978
STATIC_TESTABLE int single_domain(ares_channel channel, const char *name, char **s);
Packit 514978
Packit 514978
void ares_search(ares_channel channel, const char *name, int dnsclass,
Packit 514978
                 int type, ares_callback callback, void *arg)
Packit 514978
{
Packit 514978
  struct search_query *squery;
Packit 514978
  char *s;
Packit 514978
  const char *p;
Packit 514978
  int status, ndots;
Packit 514978
Packit 514978
  /* If name only yields one domain to search, then we don't have
Packit 514978
   * to keep extra state, so just do an ares_query().
Packit 514978
   */
Packit 514978
  status = single_domain(channel, name, &s);
Packit 514978
  if (status != ARES_SUCCESS)
Packit 514978
    {
Packit 514978
      callback(arg, status, 0, NULL, 0);
Packit 514978
      return;
Packit 514978
    }
Packit 514978
  if (s)
Packit 514978
    {
Packit 514978
      ares_query(channel, s, dnsclass, type, callback, arg);
Packit 514978
      ares_free(s);
Packit 514978
      return;
Packit 514978
    }
Packit 514978
Packit 514978
  /* Allocate a search_query structure to hold the state necessary for
Packit 514978
   * doing multiple lookups.
Packit 514978
   */
Packit 514978
  squery = ares_malloc(sizeof(struct search_query));
Packit 514978
  if (!squery)
Packit 514978
    {
Packit 514978
      callback(arg, ARES_ENOMEM, 0, NULL, 0);
Packit 514978
      return;
Packit 514978
    }
Packit 514978
  squery->channel = channel;
Packit 514978
  squery->name = ares_strdup(name);
Packit 514978
  if (!squery->name)
Packit 514978
    {
Packit 514978
      ares_free(squery);
Packit 514978
      callback(arg, ARES_ENOMEM, 0, NULL, 0);
Packit 514978
      return;
Packit 514978
    }
Packit 514978
  squery->dnsclass = dnsclass;
Packit 514978
  squery->type = type;
Packit 514978
  squery->status_as_is = -1;
Packit 514978
  squery->callback = callback;
Packit 514978
  squery->arg = arg;
Packit 514978
  squery->timeouts = 0;
Packit 514978
  squery->ever_got_nodata = 0;
Packit 514978
Packit 514978
  /* Count the number of dots in name. */
Packit 514978
  ndots = 0;
Packit 514978
  for (p = name; *p; p++)
Packit 514978
    {
Packit 514978
      if (*p == '.')
Packit 514978
        ndots++;
Packit 514978
    }
Packit 514978
Packit 514978
  /* If ndots is at least the channel ndots threshold (usually 1),
Packit 514978
   * then we try the name as-is first.  Otherwise, we try the name
Packit 514978
   * as-is last.
Packit 514978
   */
Packit 514978
  if (ndots >= channel->ndots)
Packit 514978
    {
Packit 514978
      /* Try the name as-is first. */
Packit 514978
      squery->next_domain = 0;
Packit 514978
      squery->trying_as_is = 1;
Packit 514978
      ares_query(channel, name, dnsclass, type, search_callback, squery);
Packit 514978
    }
Packit 514978
  else
Packit 514978
    {
Packit 514978
      /* Try the name as-is last; start with the first search domain. */
Packit 514978
      squery->next_domain = 1;
Packit 514978
      squery->trying_as_is = 0;
Packit 514978
      status = cat_domain(name, channel->domains[0], &s);
Packit 514978
      if (status == ARES_SUCCESS)
Packit 514978
        {
Packit 514978
          ares_query(channel, s, dnsclass, type, search_callback, squery);
Packit 514978
          ares_free(s);
Packit 514978
        }
Packit 514978
      else
Packit 514978
      {
Packit 514978
        /* failed, free the malloc()ed memory */
Packit 514978
        ares_free(squery->name);
Packit 514978
        ares_free(squery);
Packit 514978
        callback(arg, status, 0, NULL, 0);
Packit 514978
      }
Packit 514978
    }
Packit 514978
}
Packit 514978
Packit 514978
static void search_callback(void *arg, int status, int timeouts,
Packit 514978
                            unsigned char *abuf, int alen)
Packit 514978
{
Packit 514978
  struct search_query *squery = (struct search_query *) arg;
Packit 514978
  ares_channel channel = squery->channel;
Packit 514978
  char *s;
Packit 514978
Packit 514978
  squery->timeouts += timeouts;
Packit 514978
Packit 514978
  /* Stop searching unless we got a non-fatal error. */
Packit 514978
  if (status != ARES_ENODATA && status != ARES_ESERVFAIL
Packit 514978
      && status != ARES_ENOTFOUND)
Packit 514978
    end_squery(squery, status, abuf, alen);
Packit 514978
  else
Packit 514978
    {
Packit 514978
      /* Save the status if we were trying as-is. */
Packit 514978
      if (squery->trying_as_is)
Packit 514978
        squery->status_as_is = status;
Packit 514978
Packit 514978
      /*
Packit 514978
       * If we ever get ARES_ENODATA along the way, record that; if the search
Packit 514978
       * should run to the very end and we got at least one ARES_ENODATA,
Packit 514978
       * then callers like ares_gethostbyname() may want to try a T_A search
Packit 514978
       * even if the last domain we queried for T_AAAA resource records
Packit 514978
       * returned ARES_ENOTFOUND.
Packit 514978
       */
Packit 514978
      if (status == ARES_ENODATA)
Packit 514978
        squery->ever_got_nodata = 1;
Packit 514978
Packit 514978
      if (squery->next_domain < channel->ndomains)
Packit 514978
        {
Packit 514978
          /* Try the next domain. */
Packit 514978
          status = cat_domain(squery->name,
Packit 514978
                              channel->domains[squery->next_domain], &s);
Packit 514978
          if (status != ARES_SUCCESS)
Packit 514978
            end_squery(squery, status, NULL, 0);
Packit 514978
          else
Packit 514978
            {
Packit 514978
              squery->trying_as_is = 0;
Packit 514978
              squery->next_domain++;
Packit 514978
              ares_query(channel, s, squery->dnsclass, squery->type,
Packit 514978
                         search_callback, squery);
Packit 514978
              ares_free(s);
Packit 514978
            }
Packit 514978
        }
Packit 514978
      else if (squery->status_as_is == -1)
Packit 514978
        {
Packit 514978
          /* Try the name as-is at the end. */
Packit 514978
          squery->trying_as_is = 1;
Packit 514978
          ares_query(channel, squery->name, squery->dnsclass, squery->type,
Packit 514978
                     search_callback, squery);
Packit 514978
        }
Packit 514978
      else {
Packit 514978
        if (squery->status_as_is == ARES_ENOTFOUND && squery->ever_got_nodata) {
Packit 514978
          end_squery(squery, ARES_ENODATA, NULL, 0);
Packit 514978
        }
Packit 514978
        else
Packit 514978
          end_squery(squery, squery->status_as_is, NULL, 0);
Packit 514978
      }
Packit 514978
    }
Packit 514978
}
Packit 514978
Packit 514978
static void end_squery(struct search_query *squery, int status,
Packit 514978
                       unsigned char *abuf, int alen)
Packit 514978
{
Packit 514978
  squery->callback(squery->arg, status, squery->timeouts, abuf, alen);
Packit 514978
  ares_free(squery->name);
Packit 514978
  ares_free(squery);
Packit 514978
}
Packit 514978
Packit 514978
/* Concatenate two domains. */
Packit 514978
static int cat_domain(const char *name, const char *domain, char **s)
Packit 514978
{
Packit 514978
  size_t nlen = strlen(name);
Packit 514978
  size_t dlen = strlen(domain);
Packit 514978
Packit 514978
  *s = ares_malloc(nlen + 1 + dlen + 1);
Packit 514978
  if (!*s)
Packit 514978
    return ARES_ENOMEM;
Packit 514978
  memcpy(*s, name, nlen);
Packit 514978
  (*s)[nlen] = '.';
Packit 514978
  memcpy(*s + nlen + 1, domain, dlen);
Packit 514978
  (*s)[nlen + 1 + dlen] = 0;
Packit 514978
  return ARES_SUCCESS;
Packit 514978
}
Packit 514978
Packit 514978
/* Determine if this name only yields one query.  If it does, set *s to
Packit 514978
 * the string we should query, in an allocated buffer.  If not, set *s
Packit 514978
 * to NULL.
Packit 514978
 */
Packit 514978
STATIC_TESTABLE int single_domain(ares_channel channel, const char *name, char **s)
Packit 514978
{
Packit 514978
  size_t len = strlen(name);
Packit 514978
  const char *hostaliases;
Packit 514978
  FILE *fp;
Packit 514978
  char *line = NULL;
Packit 514978
  int status;
Packit 514978
  size_t linesize;
Packit 514978
  const char *p, *q;
Packit 514978
  int error;
Packit 514978
Packit 514978
  /* If the name contains a trailing dot, then the single query is the name
Packit 514978
   * sans the trailing dot.
Packit 514978
   */
Packit 514978
  if ((len > 0) && (name[len - 1] == '.'))
Packit 514978
    {
Packit 514978
      *s = ares_strdup(name);
Packit 514978
      return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
Packit 514978
    }
Packit 514978
Packit 514978
  if (!(channel->flags & ARES_FLAG_NOALIASES) && !strchr(name, '.'))
Packit 514978
    {
Packit 514978
      /* The name might be a host alias. */
Packit 514978
      hostaliases = getenv("HOSTALIASES");
Packit 514978
      if (hostaliases)
Packit 514978
        {
Packit 514978
          fp = fopen(hostaliases, "r");
Packit 514978
          if (fp)
Packit 514978
            {
Packit 514978
              while ((status = ares__read_line(fp, &line, &linesize))
Packit 514978
                     == ARES_SUCCESS)
Packit 514978
                {
Packit 514978
                  if (strncasecmp(line, name, len) != 0 ||
Packit 514978
                      !ISSPACE(line[len]))
Packit 514978
                    continue;
Packit 514978
                  p = line + len;
Packit 514978
                  while (ISSPACE(*p))
Packit 514978
                    p++;
Packit 514978
                  if (*p)
Packit 514978
                    {
Packit 514978
                      q = p + 1;
Packit 514978
                      while (*q && !ISSPACE(*q))
Packit 514978
                        q++;
Packit 514978
                      *s = ares_malloc(q - p + 1);
Packit 514978
                      if (*s)
Packit 514978
                        {
Packit 514978
                          memcpy(*s, p, q - p);
Packit 514978
                          (*s)[q - p] = 0;
Packit 514978
                        }
Packit 514978
                      ares_free(line);
Packit 514978
                      fclose(fp);
Packit 514978
                      return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
Packit 514978
                    }
Packit 514978
                }
Packit 514978
              ares_free(line);
Packit 514978
              fclose(fp);
Packit 514978
              if (status != ARES_SUCCESS && status != ARES_EOF)
Packit 514978
                return status;
Packit 514978
            }
Packit 514978
          else
Packit 514978
            {
Packit 514978
              error = ERRNO;
Packit 514978
              switch(error)
Packit 514978
                {
Packit 514978
                case ENOENT:
Packit 514978
                case ESRCH:
Packit 514978
                  break;
Packit 514978
                default:
Packit 514978
                  DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n",
Packit 514978
                                 error, strerror(error)));
Packit 514978
                  DEBUGF(fprintf(stderr, "Error opening file: %s\n",
Packit 514978
                                 hostaliases));
Packit 514978
                  *s = NULL;
Packit 514978
                  return ARES_EFILE;
Packit 514978
                }
Packit 514978
            }
Packit 514978
        }
Packit 514978
    }
Packit 514978
Packit 514978
  if (channel->flags & ARES_FLAG_NOSEARCH || channel->ndomains == 0)
Packit 514978
    {
Packit 514978
      /* No domain search to do; just try the name as-is. */
Packit 514978
      *s = ares_strdup(name);
Packit 514978
      return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
Packit 514978
    }
Packit 514978
Packit 514978
  *s = NULL;
Packit 514978
  return ARES_SUCCESS;
Packit 514978
}