-/* $Id: evdns.c 6979 2006-08-04 18:31:13Z nickm $ */
-
-/* The original version of this module was written by Adam Langley; for
- * a history of modifications, check out the subversion logs.
+/* Copyright 2006-2007 Niels Provos
+ * Copyright 2007-2012 Nick Mathewson and Niels Provos
*
- * When editing this module, try to keep it re-mergeable by Adam. Don't
- * reformat the whitespace, add Tor dependencies, or so on.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
*
- * TODO:
- * - Support IPv6 and PTR records.
- * - Replace all externally visible magic numbers with #defined constants.
- * - Write documentation for APIs of all external functions.
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-/* Async DNS Library
+/* Based on software by Adam Langly. Adam's original message:
+ *
+ * Async DNS Library
* Adam Langley <agl@imperialviolet.org>
* http://www.imperialviolet.org/eventdns.html
* Public Domain code
#define TYPE_A EVDNS_TYPE_A
#define TYPE_CNAME 5
#define TYPE_PTR EVDNS_TYPE_PTR
+#define TYPE_SOA EVDNS_TYPE_SOA
#define TYPE_AAAA EVDNS_TYPE_AAAA
#define CLASS_INET EVDNS_CLASS_INET
struct nameserver *ns; /* the server which we last sent it */
/* these objects are kept in a circular list */
+ /* XXX We could turn this into a CIRCLEQ. */
struct request *next, *prev;
struct event timeout_event;
} else {
base->global_requests_waiting--;
}
+ /* it was initialized during request_new / evtimer_assign */
+ event_debug_unassign(&req->timeout_event);
if (!req->request_appended) {
/* need to free the request data on it's own */
cb->reply.data.a.addresses,
user_pointer);
else
- cb->user_callback(cb->err, 0, 0, 0, NULL, user_pointer);
+ cb->user_callback(cb->err, 0, 0, cb->ttl, NULL, user_pointer);
break;
case TYPE_PTR:
if (cb->have_reply) {
cb->user_callback(DNS_ERR_NONE, DNS_PTR, 1, cb->ttl,
&name, user_pointer);
} else {
- cb->user_callback(cb->err, 0, 0, 0, NULL, user_pointer);
+ cb->user_callback(cb->err, 0, 0, cb->ttl, NULL, user_pointer);
}
break;
case TYPE_AAAA:
cb->reply.data.aaaa.addresses,
user_pointer);
else
- cb->user_callback(cb->err, 0, 0, 0, NULL, user_pointer);
+ cb->user_callback(cb->err, 0, 0, cb->ttl, NULL, user_pointer);
break;
default:
EVUTIL_ASSERT(0);
/* there was an error */
if (flags & 0x0200) {
error = DNS_ERR_TRUNCATED;
- } else {
+ } else if (flags & 0x000f) {
u16 error_code = (flags & 0x000f) - 1;
if (error_code > 4) {
error = DNS_ERR_UNKNOWN;
} else {
error = error_codes[error_code];
}
+ } else if (reply && !reply->have_answer) {
+ error = DNS_ERR_NODATA;
+ } else {
+ error = DNS_ERR_UNKNOWN;
}
switch (error) {
addrbuf, sizeof(addrbuf)));
break;
default:
- /* we got a good reply from the nameserver */
+ /* we got a good reply from the nameserver: it is up. */
+ if (req->handle == req->ns->probe_request) {
+ /* Avoid double-free */
+ req->ns->probe_request = NULL;
+ }
+
nameserver_up(req->ns);
}
}
/* all else failed. Pass the failure up */
- reply_schedule_callback(req, 0, error, NULL);
+ reply_schedule_callback(req, ttl, error, NULL);
request_finished(req, &REQ_HEAD(req->base, req->trans_id), 1);
} else {
/* all ok, tell the user */
/* If it's not an answer, it doesn't correspond to any request. */
if (!(flags & 0x8000)) return -1; /* must be an answer */
- if (flags & 0x020f) {
- /* there was an error */
+ if ((flags & 0x020f) && (flags & 0x020f) != DNS_ERR_NOTEXIST) {
+ /* there was an error and it's not NXDOMAIN */
goto err;
}
/* if (!answers) return; */ /* must have an answer of some form */
*/
TEST_NAME;
j += 4;
- if (j >= length) goto err;
+ if (j > length) goto err;
}
if (!name_matches)
}
}
+ if (!reply.have_answer) {
+ for (i = 0; i < authority; ++i) {
+ u16 type, class;
+ SKIP_NAME;
+ GET16(type);
+ GET16(class);
+ GET32(ttl);
+ GET16(datalength);
+ if (type == TYPE_SOA && class == CLASS_INET) {
+ u32 serial, refresh, retry, expire, minimum;
+ SKIP_NAME;
+ SKIP_NAME;
+ GET32(serial);
+ GET32(refresh);
+ GET32(retry);
+ GET32(expire);
+ GET32(minimum);
+ (void)expire;
+ (void)retry;
+ (void)refresh;
+ (void)serial;
+ ttl_r = MIN(ttl_r, ttl);
+ ttl_r = MIN(ttl_r, minimum);
+ } else {
+ /* skip over any other type of resource */
+ j += datalength;
+ }
+ }
+ }
+
+ if (ttl_r == 0xffffffff)
+ ttl_r = 0;
+
reply_handle(req, flags, ttl_r, &reply);
return 0;
err:
ns->next = base->server_head->next;
ns->prev = base->server_head;
base->server_head->next = ns;
- if (base->server_head->prev == base->server_head) {
- base->server_head->prev = ns;
- }
+ ns->next->prev = ns;
}
base->global_good_nameservers++;
case DNS_ERR_TIMEOUT: return "request timed out";
case DNS_ERR_SHUTDOWN: return "dns subsystem shut down";
case DNS_ERR_CANCEL: return "dns request canceled";
+ case DNS_ERR_NODATA: return "no records in the reply";
default: return "[Unknown error code]";
}
}