/* 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 */ }
/* 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; }
/* 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; }
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; }
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; }
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; }