Skip to content
ares_init.c 41 KiB
Newer Older
    channel->servers[0].addr.s_addr = htonl(INADDR_LOOPBACK);
    channel->nservers = 1;
  }
#ifdef ENAMETOOLONG
#define toolong(x) (x == -1) && ((ENAMETOOLONG == errno) || (EINVAL == errno))
#else
#define toolong(x) (x == -1) && (EINVAL == errno)
#endif
  if (channel->ndomains == -1) {
    /* Derive a default domain search list from the kernel hostname,
     * or set it to empty if the hostname isn't helpful.
     */
    size_t len = 64;
    int res;
    channel->ndomains = 0; /* default to none */
    hostname = malloc(len);
    do {
      res = gethostname(hostname, len);

      if(toolong(res)) {
        char *p;
        len *= 2;
        p = realloc(hostname, len);
        if(!p) {
          rc = ARES_ENOMEM;
          goto error;
        }
        hostname = p;
        continue;
      }
      else if(res) {
        rc = ARES_EBADNAME;
        goto error;
      }

    } while(0);

      /* a dot was found */
      channel->domains = malloc(sizeof(char *));
      if (!channel->domains) {
        rc = ARES_ENOMEM;
        goto error;
      }
      channel->domains[0] = strdup(dot + 1);
      if (!channel->domains[0]) {
        rc = ARES_ENOMEM;
        goto error;
      }
      channel->ndomains = 1;
  if (channel->nsort == -1) {
    channel->sortlist = NULL;
    channel->nsort = 0;
  }

  if (!channel->lookups) {
    channel->lookups = strdup("fb");
    if (!channel->lookups)
      rc = ARES_ENOMEM;
  }

  error:
  if(rc) {
    if(channel->servers)
      free(channel->servers);

    if(channel->domains && channel->domains[0])
      free(channel->domains[0]);
    if(channel->domains)
      free(channel->domains);
    if(channel->lookups)
      free(channel->lookups);
  }

  if(hostname)
    free(hostname);

  return rc;
#if !defined(WIN32) && !defined(WATT32)
static int config_domain(ares_channel channel, char *str)
{
  char *q;

  /* Set a single search domain. */
  q = str;
  while (*q && !ISSPACE(*q))
  return set_search(channel, str);
}

static int config_lookup(ares_channel channel, const char *str,
                         const char *bindch, const char *filech)
{
  char lookups[3], *l;
  const char *p;

  /* Set the lookup order.  Only the first letter of each work
   * is relevant, and it has to be "b" for DNS or "f" for the
   * host file.  Ignore everything else.
   */
  l = lookups;
  p = str;
  while (*p)
    {
      if ((*p == *bindch || *p == *filech) && l < lookups + 2) {
Daniel Stenberg's avatar
Daniel Stenberg committed
        if (*p == *bindch) *l++ = 'b';
      while (*p && !ISSPACE(*p) && (*p != ','))
Daniel Stenberg's avatar
Daniel Stenberg committed
        p++;
      while (*p && (ISSPACE(*p) || (*p == ',')))
Daniel Stenberg's avatar
Daniel Stenberg committed
        p++;
  channel->lookups = strdup(lookups);
  return (channel->lookups) ? ARES_SUCCESS : ARES_ENOMEM;
}
static int config_nameserver(struct server_state **servers, int *nservers,
Daniel Stenberg's avatar
Daniel Stenberg committed
                             char *str)
{
  struct in_addr addr;
  struct server_state *newserv;
  /* On Windows, there may be more than one nameserver specified in the same
   * registry key, so we parse it as a space or comma seperated list.
   */
#ifdef WIN32
  char *p = str;
  char *begin = str;
  int more = 1;
  while (more)
  {
    more = 0;
    while (*p && !ISSPACE(*p) && *p != ',')
      more = 1;
    }

    /* Skip multiple spaces or trailing spaces */
    if (!*begin)
    {
      begin = ++p;
      continue;
    }

    /* This is the part that actually sets the nameserver */
    addr.s_addr = inet_addr(begin);
    if (addr.s_addr == INADDR_NONE)
      continue;
    newserv = realloc(*servers, (*nservers + 1) * sizeof(struct server_state));
    if (!newserv)
      return ARES_ENOMEM;
    newserv[*nservers].addr = addr;
    *servers = newserv;
    (*nservers)++;
  /* Add a nameserver entry, if this is a valid address. */
  addr.s_addr = inet_addr(str);
  if (addr.s_addr == INADDR_NONE)
    return ARES_SUCCESS;
  newserv = realloc(*servers, (*nservers + 1) * sizeof(struct server_state));
  if (!newserv)
    return ARES_ENOMEM;
  newserv[*nservers].addr = addr;
  *servers = newserv;
  (*nservers)++;
static int config_sortlist(struct apattern **sortlist, int *nsort,
Daniel Stenberg's avatar
Daniel Stenberg committed
                           const char *str)
  const char *q;

  /* Add sortlist entries. */
  while (*str && *str != ';')
    {
      int bits;
      char ipbuf[16], ipbufpfx[32];
      /* Find just the IP */
      while (*q && *q != '/' && *q != ';' && !ISSPACE(*q))
Daniel Stenberg's avatar
Daniel Stenberg committed
        q++;
      memcpy(ipbuf, str, (int)(q-str));
      ipbuf[(int)(q-str)] = '\0';
      /* Find the prefix */
      if (*q == '/')
        {
          const char *str2 = q+1;
          while (*q && *q != ';' && !ISSPACE(*q))
            q++;
          memcpy(ipbufpfx, str, (int)(q-str));
          ipbufpfx[(int)(q-str)] = '\0';
      /* Lets see if it is CIDR */
      if ((bits = ares_inet_net_pton(AF_INET6, ipbufpfx[0] ? ipbufpfx : ipbuf,
                                     &pat.addrV6,
                                     sizeof(pat.addrV6))) > 0)
Yang Tse's avatar
Yang Tse committed
          pat.mask.bits = (unsigned short)bits;
          pat.family = AF_INET6;
          if (!sortlist_alloc(sortlist, nsort, &pat))
            return ARES_ENOMEM;
        }
          (bits = ares_inet_net_pton(AF_INET, ipbufpfx, &pat.addrV4,
                                     sizeof(pat.addrV4))) > 0)
        {
          pat.type = PATTERN_CIDR;
Yang Tse's avatar
Yang Tse committed
          pat.mask.bits = (unsigned short)bits;
          if (!sortlist_alloc(sortlist, nsort, &pat))
            return ARES_ENOMEM;
        }
      /* See if it is just a regular IP */
      else if (ip_addr(ipbuf, (int)(q-str), &pat.addrV4) == 0)
Daniel Stenberg's avatar
Daniel Stenberg committed
        {
Daniel Stenberg's avatar
Daniel Stenberg committed
            {
              memcpy(ipbuf, str, (int)(q-str));
              ipbuf[(int)(q-str)] = '\0';
              if (ip_addr(ipbuf, (int)(q - str), &pat.mask.addr4) != 0)
Daniel Stenberg's avatar
Daniel Stenberg committed
                natural_mask(&pat);
            }
          else
            natural_mask(&pat);
          if (!sortlist_alloc(sortlist, nsort, &pat))
Daniel Stenberg's avatar
Daniel Stenberg committed
            return ARES_ENOMEM;
        }
Daniel Stenberg's avatar
Daniel Stenberg committed
        {
          while (*q && *q != ';' && !ISSPACE(*q))
Daniel Stenberg's avatar
Daniel Stenberg committed
            q++;
        }
      while (ISSPACE(*str))
Daniel Stenberg's avatar
Daniel Stenberg committed
        str++;
#endif  /* !WIN32 */
#endif  /* !WATT32 */

static int set_search(ares_channel channel, const char *str)
{
  int n;
  const char *p, *q;

  if(channel->ndomains != -1) {
    /* if we already have some domains present, free them first */
    for(n=0; n < channel->ndomains; n++)
      free(channel->domains[n]);
    free(channel->domains);
    channel->domains = NULL;
  /* Count the domains given. */
  n = 0;
  p = str;
  while (*p)
    {
      while (*p && !ISSPACE(*p))
Daniel Stenberg's avatar
Daniel Stenberg committed
        p++;
      while (ISSPACE(*p))
Daniel Stenberg's avatar
Daniel Stenberg committed
        p++;
  if (!n)
    {
      channel->ndomains = 0;
      return ARES_SUCCESS;
    }

  channel->domains = malloc(n * sizeof(char *));
  if (!channel->domains)
    return ARES_ENOMEM;

  /* Now copy the domains. */
  n = 0;
  p = str;
  while (*p)
    {
      channel->ndomains = n;
      q = p;
      while (*q && !ISSPACE(*q))
Daniel Stenberg's avatar
Daniel Stenberg committed
        q++;
      channel->domains[n] = malloc(q - p + 1);
      if (!channel->domains[n])
Daniel Stenberg's avatar
Daniel Stenberg committed
        return ARES_ENOMEM;
      memcpy(channel->domains[n], p, q - p);
      channel->domains[n][q - p] = 0;
      p = q;
      while (ISSPACE(*p))
Daniel Stenberg's avatar
Daniel Stenberg committed
        p++;
      n++;
    }
  channel->ndomains = n;

  return ARES_SUCCESS;
}

static int set_options(ares_channel channel, const char *str)
{
  const char *p, *q, *val;

  p = str;
  while (*p)
    {
      q = p;
      while (*q && !ISSPACE(*q))
Daniel Stenberg's avatar
Daniel Stenberg committed
        q++;
      val = try_option(p, q, "ndots:");
      if (val && channel->ndots == -1)
Daniel Stenberg's avatar
Daniel Stenberg committed
        channel->ndots = atoi(val);
      val = try_option(p, q, "retrans:");
      if (val && channel->timeout == -1)
Daniel Stenberg's avatar
Daniel Stenberg committed
        channel->timeout = atoi(val);
      val = try_option(p, q, "retry:");
      if (val && channel->tries == -1)
Daniel Stenberg's avatar
Daniel Stenberg committed
        channel->tries = atoi(val);
      val = try_option(p, q, "rotate");
      if (val && channel->rotate == -1)
        channel->rotate = 1;
      while (ISSPACE(*p))
Daniel Stenberg's avatar
Daniel Stenberg committed
        p++;
static const char *try_option(const char *p, const char *q, const char *opt)
{
  size_t len = strlen(opt);
  return ((size_t)(q - p) >= len && !strncmp(p, opt, len)) ? &p[len] : NULL;
}

#if !defined(WIN32) && !defined(WATT32)
static char *try_config(char *s, const char *opt)
  ssize_t i;
  ssize_t j;
  char *p;
  if (!s || !opt)
    /* no line or no option */
    return NULL;

  /* trim line comment */
  for (i = 0; s[i] && s[i] != '#'; ++i);
  s[i] = '\0';

  /* trim trailing whitespace */
  for (j = i-1; j >= 0 && ISSPACE(s[j]); --j);
  s[++j] = '\0';

  /* skip leading whitespace */
  for (i = 0; s[i] && ISSPACE(s[i]); ++i);
  p = &s[i];

  if (!*p)
    /* empty line */
    return NULL;

  if ((len = strlen(opt)) == 0)
    /* empty option */
    return NULL;

  if (strncmp(p, opt, len) != 0)
    /* line and option do not match */
  /* skip over given option name */
  p += len;

  if (!*p)
    /* no option value */
    return NULL;

  if ((opt[len-1] != ':') && (opt[len-1] != '=') && !ISSPACE(*p))
    /* whitespace between option name and value is mandatory
       for given option names which do not end with ':' or '=' */
    return NULL;

  /* skip over whitespace */
  while (*p && ISSPACE(*p))
    p++;

  if (!*p)
    /* no option value */
    return NULL;

  /* return pointer to option value */
  return p;
}
static int sortlist_alloc(struct apattern **sortlist, int *nsort,
                          struct apattern *pat)
{
  struct apattern *newsort;
  newsort = realloc(*sortlist, (*nsort + 1) * sizeof(struct apattern));
  if (!newsort)
    return 0;
  newsort[*nsort] = *pat;
  *sortlist = newsort;
  (*nsort)++;
  return 1;
}

static int ip_addr(const char *ipbuf, int len, struct in_addr *addr)
{

  /* Four octets and three periods yields at most 15 characters. */
  if (len > 15)
    return -1;

  addr->s_addr = inet_addr(ipbuf);
  if (addr->s_addr == INADDR_NONE && strcmp(ipbuf, "255.255.255.255") != 0)
    return -1;
  return 0;
}

static void natural_mask(struct apattern *pat)
{
  struct in_addr addr;

  /* Store a host-byte-order copy of pat in a struct in_addr.  Icky,
   * but portable.
   */
  addr.s_addr = ntohl(pat->addrV4.s_addr);

  /* This is out of date in the CIDR world, but some people might
   * still rely on it.
   */
  if (IN_CLASSA(addr.s_addr))
    pat->mask.addr4.s_addr = htonl(IN_CLASSA_NET);
  else if (IN_CLASSB(addr.s_addr))
    pat->mask.addr4.s_addr = htonl(IN_CLASSB_NET);
    pat->mask.addr4.s_addr = htonl(IN_CLASSC_NET);
/* initialize an rc4 key. If possible a cryptographically secure random key
   is generated using a suitable function (for example win32's RtlGenRandom as
   described in
   http://blogs.msdn.com/michael_howard/archive/2005/01/14/353379.aspx
   otherwise the code defaults to cross-platform albeit less secure mechanism
   using rand
*/
static void randomize_key(unsigned char* key,int key_data_len)
{
  int randomized = 0;
  BOOLEAN res;
  if (fpSystemFunction036)
    {
      res = (*fpSystemFunction036) (key, key_data_len);
      if (res)
        randomized = 1;
    }
#else /* !WIN32 */
#ifdef RANDOM_FILE
  FILE *f = fopen(RANDOM_FILE, "rb");
  if(f) {
    counter = fread(key, 1, key_data_len, f);
      key[counter]=(unsigned char)(rand() % 256);
static int init_id_key(rc4_key* key,int key_data_len)
{
  unsigned char index1;
  unsigned char index2;
  unsigned char* state;
  short counter;
  unsigned char *key_data_ptr = 0;

  key_data_ptr = calloc(1,key_data_len);
  state = &key->state[0];
  for(counter = 0; counter < 256; counter++)
    /* unnecessary AND but it keeps some compilers happier */
    state[counter] = (unsigned char)(counter & 0xff);
Daniel Stenberg's avatar
Daniel Stenberg committed
  randomize_key(key->state,key_data_len);
  key->x = 0;
  key->y = 0;
  index1 = 0;
  index2 = 0;
  for(counter = 0; counter < 256; counter++)
  {
    index2 = (unsigned char)((key_data_ptr[index1] + state[counter] +
                              index2) % 256);
    ARES_SWAP_BYTE(&state[counter], &state[index2]);

    index1 = (unsigned char)((index1 + 1) % key_data_len);
unsigned short ares__generate_new_id(rc4_key* key)
  ares__rc4(key, (unsigned char *)&r, sizeof(r));
  return r;
}

void ares_set_socket_callback(ares_channel channel,
                              ares_sock_create_callback cb,
                              void *data)
{
  channel->sock_create_cb = cb;
  channel->sock_create_cb_data = data;
}