Example #1
0
/* ares_dup() duplicates a channel handle with all its options and returns a
   new channel handle */
int ares_dup(ares_channel *dest, ares_channel src)
{
  struct ares_options opts;
  struct ares_addr_node *servers;
  int ipv6_nservers = 0;
  int i, rc;
  int optmask;

  *dest = NULL; /* in case of failure return NULL explicitly */

  /* First get the options supported by the old ares_save_options() function,
     which is most of them */
  rc = ares_save_options(src, &opts, &optmask);
  if(rc)
    return rc;

  /* Then create the new channel with those options */
  rc = ares_init_options(dest, &opts, optmask);

  /* destroy the options copy to not leak any memory */
  ares_destroy_options(&opts);

  if(rc)
    return rc;

  /* Now clone the options that ares_save_options() doesn't support. */
  (*dest)->sock_create_cb      = src->sock_create_cb;
  (*dest)->sock_create_cb_data = src->sock_create_cb_data;

  strncpy((*dest)->local_dev_name, src->local_dev_name, sizeof(src->local_dev_name));
  (*dest)->local_ip4 = src->local_ip4;
  memcpy((*dest)->local_ip6, src->local_ip6, sizeof(src->local_ip6));

  /* Full name server cloning required when not all are IPv4 */
  for (i = 0; i < src->nservers; i++)
    {
      if (src->servers[i].addr.family != AF_INET) {
        ipv6_nservers++;
        break;
      }
    }
  if (ipv6_nservers) {
    rc = ares_get_servers(src, &servers);
    if (rc != ARES_SUCCESS)
      return rc;
    rc = ares_set_servers(*dest, servers);
    ares_free_data(servers);
    if (rc != ARES_SUCCESS)
      return rc;
  }

  return ARES_SUCCESS; /* everything went fine */
}
Example #2
0
/* Incomming string format: host[:port][,host[:port]]... */
int ares_set_servers_csv(ares_channel channel,
                         const char* _csv)
{
  int i;
  char* csv = NULL;
  char* ptr;
  char* start_host;
  long port;
  bool found_port;
  int rv = ARES_SUCCESS;
  struct ares_addr_node *servers = NULL;
  struct ares_addr_node *last = NULL;

  if (ares_library_initialized() != ARES_SUCCESS)
    return ARES_ENOTINITIALIZED;

  if (!channel)
    return ARES_ENODATA;

  ares__destroy_servers_state(channel);

  i = strlen(_csv);
  if (i == 0)
     return ARES_SUCCESS; /* blank all servers */

  csv = malloc(i + 2);
  strcpy(csv, _csv);
  if (csv[i-1] != ',') { /* make parsing easier by ensuring ending ',' */
    csv[i] = ',';
    csv[i+1] = 0;
  }

  start_host = csv;
  found_port = false;
  for (ptr = csv; *ptr; ptr++) {
    if (*ptr == ',') {
      char* pp = ptr - 1;
      struct in_addr in4;
      struct ares_in6_addr in6;
      struct ares_addr_node *s = NULL;

      *ptr = 0; /* null terminate host:port string */
      /* Got an entry..see if port was specified. */
      while (pp > start_host) {
        if (*pp == ':')
          break; /* yes */
        if (!ISDIGIT(*pp)) {
          /* Found end of digits before we found :, so wasn't a port */
          pp = ptr;
          break;
        }
        pp--;
      }
      if ((pp != start_host) && ((pp + 1) < ptr)) {
        /* Found it. */
        found_port = true;
        port = strtol(pp + 1, NULL, 10);
        *pp = 0; /* null terminate host */
      }
      /* resolve host, try ipv4 first, rslt is in network byte order */
      rv = ares_inet_pton(AF_INET, start_host, &in4);
      if (!rv) {
        /* Ok, try IPv6 then */
        rv = ares_inet_pton(AF_INET6, start_host, &in6);
        if (!rv) {
          rv = ARES_EBADSTR;
          goto out;
        }
        /* was ipv6, add new server */
        s = malloc(sizeof(*s));
        if (!s) {
          rv = ARES_ENOMEM;
          goto out;
        }
        s->family = AF_INET6;
        memcpy(&s->addr, &in6, sizeof(struct ares_in6_addr));
      }
      else {
        /* was ipv4, add new server */
        s = malloc(sizeof(*s));
        if (!s) {
          rv = ARES_ENOMEM;
          goto out;
        }
        s->family = AF_INET;
        memcpy(&s->addr, &in4, sizeof(struct in_addr));
      }
      if (s) {
        /* TODO:  Add port to ares_addr_node and assign it here. */

        s->next = NULL;
        if (last) {
          last->next = s;
        }
        else {
          servers = s;
          last = s;
        }
      }

      /* Set up for next one */
      found_port = false;
      start_host = ptr + 1;
    }
  }

  rv = ares_set_servers(channel, servers);

  out:
  if (csv)
    free(csv);
  while (servers) {
    struct ares_addr_node *s = servers;
    servers = servers->next;
    free(s);
  }

  return rv;
}
static int
set_nameservers(Channel *self, PyObject *value)
{
    char *server;
    int i, r, length, ret;
    struct ares_addr_node *servers;
    Bool is_buffer;
    Py_buffer pbuf;
    PyObject *server_list, *item, *data_fast;

    is_buffer = False;
    servers = NULL;
    server_list = value;
    ret = 0;

    if ((data_fast = PySequence_Fast(server_list, "argument 1 must be an iterable")) == NULL) {
        return -1;
    }

    length = PySequence_Fast_GET_SIZE(data_fast);
    if (length > INT_MAX) {
        PyErr_SetString(PyExc_ValueError, "argument 1 is too long");
        Py_DECREF(data_fast);
        return -1;
    }

    if (length == 0) {
        /* c-ares doesn't do anything */
        return 0;
    }

    servers = PyMem_Malloc(sizeof(struct ares_addr_node) * length);
    if (!servers) {
        PyErr_NoMemory();
        ret = -1;
        goto end;
    }

    for (i = 0; i < length; i++) {
        item = PySequence_Fast_GET_ITEM(data_fast, i);
        if (!item || !PyArg_Parse(item, "s*;args contains a non-string value", &pbuf)) {
            Py_XDECREF(item);
            goto end;
        }
        Py_DECREF(item);
        server = pbuf.buf;

        if (ares_inet_pton(AF_INET, server, &servers[i].addr.addr4) == 1) {
            servers[i].family = AF_INET;
        } else if (ares_inet_pton(AF_INET6, server, &servers[i].addr.addr6) == 1) {
            servers[i].family = AF_INET6;
        } else {
            PyErr_SetString(PyExc_ValueError, "invalid IP address");
            PyBuffer_Release(&pbuf);
            ret = -1;
            goto end;
        }

        PyBuffer_Release(&pbuf);

        if (i > 0) {
            servers[i-1].next = &servers[i];
        }

    }

    if (servers) {
        servers[length-1].next = NULL;
    }

    r = ares_set_servers(self->channel, servers);
    if (r != ARES_SUCCESS) {
        RAISE_ARES_EXCEPTION(r);
        ret = -1;
    }

end:
    PyMem_Free(servers);
    return ret;
}
Example #4
0
/* IPv6 addresses with ports require square brackets [fe80::1%lo0]:53 */
int ares_set_servers_csv(ares_channel channel,
                         const char* _csv)
{
  size_t i;
  char* csv = NULL;
  char* ptr;
  char* start_host;
  int cc = 0;
  int rv = ARES_SUCCESS;
  struct ares_addr_node *servers = NULL;
  struct ares_addr_node *last = NULL;

  if (ares_library_initialized() != ARES_SUCCESS)
    return ARES_ENOTINITIALIZED;

  if (!channel)
    return ARES_ENODATA;

  ares__destroy_servers_state(channel);

  i = strlen(_csv);
  if (i == 0)
     return ARES_SUCCESS; /* blank all servers */

  csv = malloc(i + 2);
  strcpy(csv, _csv);
  if (csv[i-1] != ',') { /* make parsing easier by ensuring ending ',' */
    csv[i] = ',';
    csv[i+1] = 0;
  }

  start_host = csv;
  for (ptr = csv; *ptr; ptr++) {
    if (*ptr == ':') {
      /* count colons to determin if we have an IPv6 number or IPv4 with port */
      cc++;
    }
    if (*ptr == '[') {
      /* move start_host if an open square bracket is found wrapping an IPv6 address */
      start_host = ptr + 1;
    }
    if (*ptr == ',') {
      char* pp = ptr - 1;
      char* p = ptr;
      struct in_addr in4;
      struct ares_in6_addr in6;
      struct ares_addr_node *s = NULL;

      *ptr = 0; /* null terminate host:port string */
      /* Got an entry..see if the port was specified. */
      if (cc > 0) {
        while (pp > start_host) {
          /* a single close square bracket followed by a colon, ']:' indicates an IPv6 address with port */
          if ((*pp == ']') && (*p == ':'))
            break; /* found port */
          /* a single colon, ':' indicates an IPv4 address with port */
          if ((*pp == ':') && (cc == 1))
            break; /* found port */
          if (!(ISDIGIT(*pp) || (*pp == ':'))) {
            /* Found end of digits before we found :, so wasn't a port */
            /* must allow ':' for IPv6 case of ']:' indicates we found a port */
            pp = p = ptr;
            break;
          }
          pp--;
          p--;
        }
        if ((pp != start_host) && ((pp + 1) < ptr)) {
          /* Found it. Parse over the port number */
          /* when an IPv6 address is wrapped with square brackets the port starts at pp + 2 */
          if (*pp == ']')
            p++; /* move p before ':' */
          /* p will point to the start of the port */
          (void)strtol(p, NULL, 10);
          *pp = 0; /* null terminate host */
        }
      }
      /* resolve host, try ipv4 first, rslt is in network byte order */
      rv = ares_inet_pton(AF_INET, start_host, &in4);
      if (!rv) {
        /* Ok, try IPv6 then */
        rv = ares_inet_pton(AF_INET6, start_host, &in6);
        if (!rv) {
          rv = ARES_EBADSTR;
          goto out;
        }
        /* was ipv6, add new server */
        s = malloc(sizeof(*s));
        if (!s) {
          rv = ARES_ENOMEM;
          goto out;
        }
        s->family = AF_INET6;
        memcpy(&s->addr, &in6, sizeof(struct ares_in6_addr));
      }
      else {
        /* was ipv4, add new server */
        s = malloc(sizeof(*s));
        if (!s) {
          rv = ARES_ENOMEM;
          goto out;
        }
        s->family = AF_INET;
        memcpy(&s->addr, &in4, sizeof(struct in_addr));
      }
      if (s) {
        /* TODO:  Add port to ares_addr_node and assign it here. */

        s->next = NULL;
        if (last) {
          last->next = s;
          /* need to move last to maintain the linked list */
          last = last->next;
        }
        else {
          servers = s;
          last = s;
        }
      }

      /* Set up for next one */
      start_host = ptr + 1;
      cc = 0;
    }
  }

  rv = ares_set_servers(channel, servers);

  out:
  if (csv)
    free(csv);
  while (servers) {
    struct ares_addr_node *s = servers;
    servers = servers->next;
    free(s);
  }

  return rv;
}
Example #5
0
int main(int argc, char **argv)
{
  ares_channel channel;
  int c, i, optmask = ARES_OPT_FLAGS, dnsclass = C_IN, type = T_A;
  int status, nfds, count;
  struct ares_options options;
  struct hostent *hostent;
  fd_set read_fds, write_fds;
  struct timeval *tvp, tv;
  struct ares_addr_node *srvr, *servers = NULL;

#ifdef USE_WINSOCK
  WORD wVersionRequested = MAKEWORD(USE_WINSOCK,USE_WINSOCK);
  WSADATA wsaData;
  WSAStartup(wVersionRequested, &wsaData);
#endif

  status = ares_library_init(ARES_LIB_INIT_ALL);
  if (status != ARES_SUCCESS)
    {
      fprintf(stderr, "ares_library_init: %s\n", ares_strerror(status));
      return 1;
    }

  options.flags = ARES_FLAG_NOCHECKRESP;
  options.servers = NULL;
  options.nservers = 0;
  while ((c = ares_getopt(argc, argv, "df:s:c:t:T:U:")) != -1)
    {
      switch (c)
        {
        case 'd':
#ifdef WATT32
          dbug_init();
#endif
          break;

        case 'f':
          /* Add a flag. */
          for (i = 0; i < nflags; i++)
            {
              if (strcmp(flags[i].name, optarg) == 0)
                break;
            }
          if (i < nflags)
            options.flags |= flags[i].value;
          else
            usage();
          break;

        case 's':
          /* User-specified name servers override default ones. */
          srvr = malloc(sizeof(struct ares_addr_node));
          if (!srvr)
            {
              fprintf(stderr, "Out of memory!\n");
              destroy_addr_list(servers);
              return 1;
            }
          append_addr_list(&servers, srvr);
          if (ares_inet_pton(AF_INET, optarg, &srvr->addr.addr4) > 0)
            srvr->family = AF_INET;
          else if (ares_inet_pton(AF_INET6, optarg, &srvr->addr.addr6) > 0)
            srvr->family = AF_INET6;
          else
            {
              hostent = gethostbyname(optarg);
              if (!hostent)
                {
                  fprintf(stderr, "adig: server %s not found.\n", optarg);
                  destroy_addr_list(servers);
                  return 1;
                }
              switch (hostent->h_addrtype)
                {
                  case AF_INET:
                    srvr->family = AF_INET;
                    memcpy(&srvr->addr.addr4, hostent->h_addr,
                           sizeof(srvr->addr.addr4));
                    break;
                  case AF_INET6:
                    srvr->family = AF_INET6;
                    memcpy(&srvr->addr.addr6, hostent->h_addr,
                           sizeof(srvr->addr.addr6));
                    break;
                  default:
                    fprintf(stderr,
                      "adig: server %s unsupported address family.\n", optarg);
                    destroy_addr_list(servers);
                    return 1;
                }
            }
          /* Notice that calling ares_init_options() without servers in the
           * options struct and with ARES_OPT_SERVERS set simultaneously in
           * the options mask, results in an initialization with no servers.
           * When alternative name servers have been specified these are set
           * later calling ares_set_servers() overriding any existing server
           * configuration. To prevent initial configuration with default
           * servers that will be discarded later, ARES_OPT_SERVERS is set.
           * If this flag is not set here the result shall be the same but
           * ares_init_options() will do needless work. */
          optmask |= ARES_OPT_SERVERS;
          break;

        case 'c':
          /* Set the query class. */
          for (i = 0; i < nclasses; i++)
            {
              if (strcasecmp(classes[i].name, optarg) == 0)
                break;
            }
          if (i < nclasses)
            dnsclass = classes[i].value;
          else
            usage();
          break;

        case 't':
          /* Set the query type. */
          for (i = 0; i < ntypes; i++)
            {
              if (strcasecmp(types[i].name, optarg) == 0)
                break;
            }
          if (i < ntypes)
            type = types[i].value;
          else
            usage();
          break;

        case 'T':
          /* Set the TCP port number. */
          if (!ISDIGIT(*optarg))
            usage();
          options.tcp_port = (unsigned short)strtol(optarg, NULL, 0);
          optmask |= ARES_OPT_TCP_PORT;
          break;

        case 'U':
          /* Set the UDP port number. */
          if (!ISDIGIT(*optarg))
            usage();
          options.udp_port = (unsigned short)strtol(optarg, NULL, 0);
          optmask |= ARES_OPT_UDP_PORT;
          break;
        }
    }
  argc -= optind;
  argv += optind;
  if (argc == 0)
    usage();

  status = ares_init_options(&channel, &options, optmask);

  if (status != ARES_SUCCESS)
    {
      fprintf(stderr, "ares_init_options: %s\n",
              ares_strerror(status));
      return 1;
    }

  if(servers)
    {
      status = ares_set_servers(channel, servers);
      destroy_addr_list(servers);
      if (status != ARES_SUCCESS)
        {
          fprintf(stderr, "ares_init_options: %s\n",
                  ares_strerror(status));
          return 1;
        }
    }

  /* Initiate the queries, one per command-line argument.  If there is
   * only one query to do, supply NULL as the callback argument;
   * otherwise, supply the query name as an argument so we can
   * distinguish responses for the user when printing them out.
   */
  if (argc == 1)
    ares_query(channel, *argv, dnsclass, type, callback, (char *) NULL);
  else
    {
      for (; *argv; argv++)
        ares_query(channel, *argv, dnsclass, type, callback, *argv);
    }

  /* Wait for all queries to complete. */
  for (;;)
    {
      FD_ZERO(&read_fds);
      FD_ZERO(&write_fds);
      nfds = ares_fds(channel, &read_fds, &write_fds);
      if (nfds == 0)
        break;
      tvp = ares_timeout(channel, NULL, &tv);
      count = select(nfds, &read_fds, &write_fds, NULL, tvp);
      if (count < 0 && SOCKERRNO != EINVAL)
        {
          perror("select");
          return 1;
        }
      ares_process(channel, &read_fds, &write_fds);
    }

  ares_destroy(channel);

  ares_library_cleanup();

#ifdef USE_WINSOCK
  WSACleanup();
#endif

  return 0;
}
Example #6
0
int dns_start_query(myconn_t *rec, char *targetserver)
{
	struct ares_addr_node *srvr, *servers = NULL;
	struct ares_options options;
	int status, optmask;
	char *tdup, *tst;

	/* See what IP family we must use to communicate with the target DNS server */
	srvr = malloc(sizeof(struct ares_addr_node));
	append_addr_list(&servers, srvr);
	srvr->family = -1;
#ifdef IPV4_SUPPORT
	if ((srvr->family == -1) && (inet_pton(AF_INET, targetserver, &srvr->addr.addr4) > 0))
		srvr->family = AF_INET;
#endif

#ifdef IPV6_SUPPORT
	if ((srvr->family == -1) && (inet_pton(AF_INET6, targetserver, &srvr->addr.addr6) > 0))
		srvr->family = AF_INET6;
#endif

	if (srvr->family == -1) {
		errprintf("Unsupported IP family for DNS target IP %s, test %s\n", targetserver, rec->testspec);
		return 0;
	}

	/* Create a new ARES request channel */
	rec->dnschannel = malloc(sizeof(ares_channel));

	/* 
	 * The C-ARES timeout handling is a bit complicated. The timeout setting
	 * here in the options only determines the timeout for the first query;
	 * subsequent queries (up to the "tries" count) use a progressively
	 * higher timeout setting.
	 * So we cannot easily determine what combination of timeout/tries will
	 * result in the full query timing out after the desired number of seconds.
	 * Therefore, use a fixed set of values - the 2000 ms / 4 tries combination
	 * results in a timeout after 23-24 seconds.
	 */
	optmask = ARES_OPT_FLAGS | ARES_OPT_SERVERS | ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES;
	options.flags = ARES_FLAG_NOCHECKRESP;
	options.servers = NULL;
	options.nservers = 0;
	options.timeout = 2000;
	options.tries = 4;
	status = ares_init_options(rec->dnschannel, &options, optmask);
	if (status != ARES_SUCCESS) {
		errprintf("Cannot create ARES channel for DNS target %s, test %s\n", targetserver, rec->testspec);
		rec->dnsstatus = DNS_FINISHED;
		return 0;
	}

	/* Point the channel at the target server */
	status = ares_set_servers(*((ares_channel *)rec->dnschannel), servers);
	destroy_addr_list(servers);
	if (status != ARES_SUCCESS)
	{
		errprintf("Cannot select ARES target DNS server %s, test %s\n", targetserver, rec->testspec);
		rec->dnsstatus = DNS_QUERY_COMPLETED;	/* To reap the ARES channel later */
		return 0;
	}

	/* Post the queries we want to perform */
	tdup = strdup(rec->testspec);

	tst = strtok(tdup, ",");
	do {
		char *p, *tlookup;
		int atype = dns_atype;

		p = strchr(tst, ':');
		tlookup = (p ? p+1 : tst);
		if (p) { 
			*p = '\0'; 
			atype = dns_name_type(tst);
			*p = ':';
		}

		/* Use ares_query() here, since we dont want to get results from hosts file or other odd stuff. */
		ares_query(*((ares_channel *)rec->dnschannel), tlookup, dns_aclass, atype, dns_query_callback, rec);
		tst = strtok(NULL, ",");
	} while (tst);

	xfree(tdup);

	rec->textlog = newstrbuffer(0);
	getntimer(&rec->dnsstarttime);
	rec->dnsstatus = DNS_QUERY_ACTIVE;

	return 1;
}
Example #7
0
static int
set_dns_servers(DNSResolver *self, PyObject *value)
{
    char *server;
    int i, r, length, ret;
    struct ares_addr_node *servers;
    PyObject *server_list = value;
    PyObject *item;

    ret = 0;

    if (!PyList_Check(server_list)) {
        PyErr_SetString(PyExc_TypeError, "servers argument must be a list");
	return -1;
    }

    length = PyList_Size(server_list);
    servers = PyMem_Malloc(sizeof(struct ares_addr_node) * length);

    for (i = 0; i < length; i++) {
        item = PyList_GetItem(server_list, i);
        if (!item) {
            ret = -1;
            goto servers_set_end;
        }

        server = PyString_AsString(item);
        if (!server) {
            ret = -1;
            goto servers_set_end;
        }

        if (uv_inet_pton(AF_INET, server, &servers[i].addr.addr4) == 1) {
            servers[i].family = AF_INET;
        } else if (uv_inet_pton(AF_INET6, server, &servers[i].addr.addr6) == 1) {
            servers[i].family = AF_INET6;
        } else {
            PyErr_SetString(PyExc_ValueError, "invalid IP address");
            ret = -1;
            goto servers_set_end;
        }

        if (i > 0) {
            servers[i-1].next = &servers[i];
        }
    }

    if (length > 0) {
        servers[length-1].next = NULL;
    } else {
        servers = NULL;
    }

    r = ares_set_servers(self->channel, servers);
    if (r != 0) {
        PyErr_SetString(PyExc_DNSError, "error c-ares library options");
        ret = -1;
    }

servers_set_end:

    PyMem_Free(servers);
    return ret;
}