代码拉取完成,页面将自动刷新
/*
Copyright (c) 2003-2006 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "polipo.h"
#ifndef NO_STANDARD_RESOLVER
#ifndef NO_FANCY_RESOLVER
int dnsUseGethostbyname = 1;
#else
const int dnsUseGethostbyname = 3;
#endif
#else
#ifndef NO_FANCY_RESOLVER
const int dnsUseGethostbyname = 0;
#else
#error use no resolver at all?
#endif
#endif
#ifndef NO_FANCY_RESOLVER
AtomPtr dnsNameServer = NULL;
int dnsMaxTimeout = 60;
int dnsNameServerPort = 53;
#endif
#ifndef NO_STANDARD_RESOLVER
int dnsGethostbynameTtl = 1200;
#endif
int dnsNegativeTtl = 120;
#ifdef HAVE_IPv6
int dnsQueryIPv6 = 2;
#else
const int dnsQueryIPv6 = 0;
#endif
typedef struct _DnsQuery {
unsigned id;
AtomPtr name;
ObjectPtr object;
AtomPtr inet4, inet6;
time_t ttl4, ttl6;
time_t time;
int timeout;
TimeEventHandlerPtr timeout_handler;
struct _DnsQuery *next;
} DnsQueryRec, *DnsQueryPtr;
union {
struct sockaddr sa;
struct sockaddr_in sin;
#ifdef HAVE_IPv6
struct sockaddr_in6 sin6;
#endif
} nameserverAddress_storage;
#ifndef NO_FANCY_RESOLVER
static AtomPtr atomLocalhost, atomLocalhostDot;
#define nameserverAddress nameserverAddress_storage.sa
static DnsQueryPtr inFlightDnsQueries;
static DnsQueryPtr inFlightDnsQueriesLast;
#endif
static int really_do_gethostbyname(AtomPtr name, ObjectPtr object);
static int really_do_dns(AtomPtr name, ObjectPtr object);
#ifndef NO_FANCY_RESOLVER
static int stringToLabels(char *buf, int offset, int n, char *string);
static int labelsToString(char *buf, int offset, int n, char *d,
int m, int *j_return);
static int dnsBuildQuery(int id, char *buf, int offset, int n,
AtomPtr name, int af);
static int dnsReplyHandler(int abort, FdEventHandlerPtr event);
static int dnsReplyId(char *buf, int offset, int n, int *id_return);
static int dnsDecodeReply(char *buf, int offset, int n,
int *id_return,
AtomPtr *name_return, AtomPtr *value_return,
int *af_return, unsigned *ttl_return);
static int dnsHandler(int status, ConditionHandlerPtr chandler);
static int dnsGethostbynameFallback(int id, AtomPtr message);
static int sendQuery(DnsQueryPtr query);
static int idSeed;
#endif
#if !defined(NO_FANCY_RESOLVER) && !defined(WIN32)
static int
parseResolvConf(char *filename)
{
FILE *f;
char buf[512];
char *p, *q;
int n;
AtomPtr nameserver = NULL;
f = fopen(filename, "r");
if(f == NULL) {
do_log_error(L_ERROR, errno, "DNS: couldn't open %s", filename);
return 0;
}
while(1) {
p = fgets(buf, 512, f);
if(p == NULL)
break;
n = strlen(buf);
if(buf[n - 1] != '\n') {
int c;
do_log(L_WARN, "DNS: overly long line in %s -- skipping.\n",
filename);
do {
c = fgetc(f);
if(c == EOF)
break;
} while(c != '\n');
if(c == EOF)
break;
}
while(*p == ' ' || *p == '\t')
p++;
if(strcasecmp_n("nameserver", p, 10) != 0)
continue;
p += 10;
while(*p == ' ' || *p == '\t')
p++;
q = p;
while(*q == '.' || *q == ':' || digit(*q) || letter(*q))
q++;
if(*q != ' ' && *q != '\t' && *q != '\r' && *q != '\n') {
do_log(L_WARN, "DNS: couldn't parse line in %s -- skipping.\n",
filename);
continue;
}
nameserver = internAtomLowerN(p, q - p);
break;
}
fclose(f);
if(nameserver) {
dnsNameServer = nameserver;
return 1;
} else {
return 0;
}
}
#endif
void
preinitDns()
{
#ifdef HAVE_IPv6
int fd;
#endif
assert(sizeof(struct in_addr) == 4);
#ifdef HAVE_IPv6
assert(sizeof(struct in6_addr) == 16);
#endif
#ifndef NO_STANDARD_RESOLVER
CONFIG_VARIABLE(dnsGethostbynameTtl, CONFIG_TIME,
"TTL for gethostbyname addresses.");
#endif
#ifdef HAVE_IPv6
fd = socket(PF_INET6, SOCK_STREAM, 0);
if(fd < 0) {
if(errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT) {
dnsQueryIPv6 = 0;
} else {
do_log_error(L_WARN, errno, "DNS: couldn't create socket");
}
} else {
close(fd);
}
#endif
#ifndef NO_FANCY_RESOLVER
#ifndef WIN32
parseResolvConf("/etc/resolv.conf");
#endif
if(dnsNameServer == NULL || dnsNameServer->string[0] == '\0')
dnsNameServer = internAtom("127.0.0.1");
CONFIG_VARIABLE(dnsMaxTimeout, CONFIG_TIME,
"Max timeout for DNS queries.");
CONFIG_VARIABLE(dnsNegativeTtl, CONFIG_TIME,
"TTL for negative DNS replies with no TTL.");
CONFIG_VARIABLE(dnsNameServer, CONFIG_ATOM_LOWER,
"The name server to use.");
CONFIG_VARIABLE(dnsNameServerPort, CONFIG_INT,
"The name server port to use.");
#ifndef NO_STANDARD_RESOLVER
CONFIG_VARIABLE(dnsUseGethostbyname, CONFIG_TETRASTATE,
"Use the system resolver.");
#endif
#endif
#ifdef HAVE_IPv6
CONFIG_VARIABLE(dnsQueryIPv6, CONFIG_TETRASTATE,
"Query for IPv6 addresses.");
#endif
}
void
initDns()
{
#ifndef NO_FANCY_RESOLVER
int rc;
struct timeval t;
struct sockaddr_in *sin = (struct sockaddr_in*)&nameserverAddress;
#ifdef HAVE_IPv6
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&nameserverAddress;
#endif
atomLocalhost = internAtom("localhost");
atomLocalhostDot = internAtom("localhost.");
inFlightDnsQueries = NULL;
inFlightDnsQueriesLast = NULL;
gettimeofday(&t, NULL);
idSeed = t.tv_usec & 0xFFFF;
sin->sin_family = AF_INET;
sin->sin_port = htons(dnsNameServerPort);
rc = inet_aton(dnsNameServer->string, &sin->sin_addr);
#ifdef HAVE_IPv6
if(rc != 1) {
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(dnsNameServerPort);
rc = inet_pton(AF_INET6, dnsNameServer->string, &sin6->sin6_addr);
}
#endif
if(rc != 1) {
do_log(L_ERROR, "DNS: couldn't parse name server %s.\n",
dnsNameServer->string);
exit(1);
}
#endif
}
int
do_gethostbyname(char *origname,
int count,
int (*handler)(int, GethostbynameRequestPtr),
void *data)
{
ObjectPtr object;
int n = strlen(origname);
AtomPtr name;
GethostbynameRequestRec request;
int done, rc;
memset(&request, 0, sizeof(request));
request.name = NULL;
request.addr = NULL;
request.error_message = NULL;
request.count = count;
request.handler = handler;
request.data = data;
if(n <= 0 || n > 131) {
if(n <= 0) {
request.error_message = internAtom("empty name");
do_log(L_ERROR, "Empty DNS name.\n");
done = handler(-EINVAL, &request);
} else {
request.error_message = internAtom("name too long");
do_log(L_ERROR, "DNS name too long.\n");
done = handler(-ENAMETOOLONG, &request);
}
assert(done);
releaseAtom(request.error_message);
return 1;
}
if(origname[n - 1] == '.')
n--;
name = internAtomLowerN(origname, n);
if(name == NULL) {
request.error_message = internAtom("couldn't allocate name");
do_log(L_ERROR, "Couldn't allocate DNS name.\n");
done = handler(-ENOMEM, &request);
assert(done);
releaseAtom(request.error_message);
return 1;
}
request.name = name;
request.addr = NULL;
request.error_message = NULL;
request.count = count;
request.object = NULL;
request.handler = handler;
request.data = data;
object = findObject(OBJECT_DNS, name->string, name->length);
if(object == NULL || objectMustRevalidate(object, NULL)) {
if(object) {
privatiseObject(object, 0);
releaseObject(object);
}
object = makeObject(OBJECT_DNS, name->string, name->length, 1, 0,
NULL, NULL);
if(object == NULL) {
request.error_message = internAtom("Couldn't allocate object");
do_log(L_ERROR, "Couldn't allocate DNS object.\n");
done = handler(-ENOMEM, &request);
assert(done);
releaseAtom(name);
releaseAtom(request.error_message);
return 1;
}
}
if((object->flags & (OBJECT_INITIAL | OBJECT_INPROGRESS)) ==
OBJECT_INITIAL) {
if(dnsUseGethostbyname >= 3)
rc = really_do_gethostbyname(name, object);
else
rc = really_do_dns(name, object);
if(rc < 0) {
assert(!(object->flags & (OBJECT_INITIAL | OBJECT_INPROGRESS)));
goto fail;
}
}
if(dnsUseGethostbyname >= 3)
assert(!(object->flags & OBJECT_INITIAL));
#ifndef NO_FANCY_RESOLVER
if(object->flags & OBJECT_INITIAL) {
ConditionHandlerPtr chandler;
assert(object->flags & OBJECT_INPROGRESS);
request.object = object;
chandler = conditionWait(&object->condition, dnsHandler,
sizeof(request), &request);
if(chandler == NULL)
goto fail;
return 1;
}
#endif
if(object->headers && object->headers->length > 0) {
if(object->headers->string[0] == DNS_A)
assert(((object->headers->length - 1) %
sizeof(HostAddressRec)) == 0);
else
assert(object->headers->string[0] == DNS_CNAME);
request.addr = retainAtom(object->headers);
} else if(object->message) {
request.error_message = retainAtom(object->message);
}
releaseObject(object);
if(request.addr && request.addr->length > 0)
done = handler(1, &request);
else
done = handler(-EDNS_HOST_NOT_FOUND, &request);
assert(done);
releaseAtom(request.addr); request.addr = NULL;
releaseAtom(request.name); request.name = NULL;
releaseAtom(request.error_message); request.error_message = NULL;
return 1;
fail:
releaseNotifyObject(object);
done = handler(-errno, &request);
assert(done);
releaseAtom(name);
return 1;
}
static int
dnsDelayedErrorNotifyHandler(TimeEventHandlerPtr event)
{
int done;
GethostbynameRequestRec request =
*(GethostbynameRequestPtr)event->data;
done = request.handler(-EDNS_HOST_NOT_FOUND, &request);
assert(done);
releaseAtom(request.name); request.name = NULL;
releaseAtom(request.addr); request.addr = NULL;
releaseAtom(request.error_message); request.error_message = NULL;
return 1;
}
static int
dnsDelayedDoneNotifyHandler(TimeEventHandlerPtr event)
{
int done;
GethostbynameRequestRec request = *(GethostbynameRequestPtr)event->data;
done = request.handler(1, &request);
assert(done);
releaseAtom(request.name); request.name = NULL;
releaseAtom(request.addr); request.addr = NULL;
releaseAtom(request.error_message); request.error_message = NULL;
return 1;
}
static int
dnsDelayedNotify(int error, GethostbynameRequestPtr request)
{
TimeEventHandlerPtr handler;
if(error)
handler = scheduleTimeEvent(0,
dnsDelayedErrorNotifyHandler,
sizeof(*request), request);
else
handler = scheduleTimeEvent(0,
dnsDelayedDoneNotifyHandler,
sizeof(*request), request);
if(handler == NULL) {
do_log(L_ERROR, "Couldn't schedule DNS notification.\n");
return -1;
}
return 1;
}
#ifdef HAVE_IPv6
AtomPtr
rfc2732(AtomPtr name)
{
char buf[40]; /* 8*4 (hexdigits) + 7 (colons) + 1 ('\0') */
int rc;
AtomPtr a = NULL;
if(name->length < 40+2 &&
name->string[0] == '[' && name->string[name->length - 1] == ']') {
struct in6_addr in6a;
memcpy(buf, name->string + 1, name->length - 2);
buf[name->length - 2] = '\0';
rc = inet_pton(AF_INET6, buf, &in6a);
if(rc == 1) {
char s[1 + sizeof(HostAddressRec)];
memset(s, 0, sizeof(s));
s[0] = DNS_A;
s[1] = 6;
memcpy(s + 2, &in6a, 16);
a = internAtomN(s, 1 + sizeof(HostAddressRec));
if(a == NULL)
return NULL;
}
}
return a;
}
/* Used for sorting host addresses depending on the value of dnsQueryIPv6 */
int
compare_hostaddr(const void *av, const void *bv)
{
const HostAddressRec *a = av, *b = bv;
int r;
if(a->af == 4) {
if(b->af == 4)
r = 0;
else
r = -1;
} else {
if(b->af == 6)
r = 0;
else
r = 1;
}
if(dnsQueryIPv6 >= 2)
return -r;
else
return r;
}
#ifndef NO_STANDARD_RESOLVER
static int
really_do_gethostbyname(AtomPtr name, ObjectPtr object)
{
struct addrinfo *ai, *entry, hints;
int rc;
int error, i;
char buf[1024];
AtomPtr a;
a = rfc2732(name);
if(a) {
object->headers = a;
object->age = current_time.tv_sec;
object->expires = current_time.tv_sec + 240;
object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
notifyObject(object);
return 0;
}
memset(&hints, 0, sizeof(hints));
hints.ai_protocol = IPPROTO_TCP;
if(dnsQueryIPv6 <= 0)
hints.ai_family = AF_INET;
else if(dnsQueryIPv6 >= 3)
hints.ai_family = AF_INET6;
rc = getaddrinfo(name->string, NULL, &hints, &ai);
switch(rc) {
case 0: error = 0; break;
case EAI_FAMILY:
#ifdef EAI_ADDRFAMILY
case EAI_ADDRFAMILY:
#endif
case EAI_SOCKTYPE:
error = EAFNOSUPPORT; break;
case EAI_BADFLAGS: error = EINVAL; break;
case EAI_SERVICE: error = EDNS_NO_RECOVERY; break;
#ifdef EAI_NONAME
case EAI_NONAME:
#endif
#ifdef EAI_NODATA
case EAI_NODATA:
#endif
error = EDNS_NO_ADDRESS; break;
case EAI_FAIL: error = EDNS_NO_RECOVERY; break;
case EAI_AGAIN: error = EDNS_TRY_AGAIN; break;
#ifdef EAI_MEMORY
case EAI_MEMORY: error = ENOMEM; break;
#endif
case EAI_SYSTEM: error = errno; break;
default: error = EUNKNOWN;
}
if(error == EDNS_NO_ADDRESS) {
object->headers = NULL;
object->age = current_time.tv_sec;
object->expires = current_time.tv_sec + dnsNegativeTtl;
object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
notifyObject(object);
return 0;
} else if(error) {
do_log_error(L_ERROR, error, "Getaddrinfo failed");
object->flags &= ~OBJECT_INPROGRESS;
abortObject(object, 404,
internAtomError(error, "Getaddrinfo failed"));
notifyObject(object);
return 0;
}
entry = ai;
buf[0] = DNS_A;
i = 0;
while(entry) {
HostAddressRec host;
int host_valid = 0;
if(entry->ai_family == AF_INET && entry->ai_protocol == IPPROTO_TCP) {
if(dnsQueryIPv6 < 3) {
host.af = 4;
memset(host.data, 0, sizeof(host.data));
memcpy(&host.data,
&((struct sockaddr_in*)entry->ai_addr)->sin_addr,
4);
host_valid = 1;
}
} else if(entry->ai_family == AF_INET6 &&
entry->ai_protocol == IPPROTO_TCP) {
if(dnsQueryIPv6 > 0) {
host.af = 6;
memset(&host.data, 0, sizeof(host.data));
memcpy(&host.data,
&((struct sockaddr_in6*)entry->ai_addr)->sin6_addr,
16);
host_valid = 1;
}
}
if(host_valid) {
if(i >= 1024 / sizeof(HostAddressRec) - 2) {
do_log(L_ERROR, "Too many addresses for host %s\n",
name->string);
break;
}
memcpy(buf + 1 + i * sizeof(HostAddressRec),
&host, sizeof(HostAddressRec));
i++;
}
entry = entry->ai_next;
}
freeaddrinfo(ai);
if(i == 0) {
do_log(L_ERROR, "Getaddrinfo returned no useful addresses\n");
object->flags &= ~OBJECT_INPROGRESS;
abortObject(object, 404,
internAtom("Getaddrinfo returned no useful addresses"));
notifyObject(object);
return 0;
}
if(1 <= dnsQueryIPv6 && dnsQueryIPv6 <= 2)
qsort(buf + 1, i, sizeof(HostAddressRec), compare_hostaddr);
a = internAtomN(buf, 1 + i * sizeof(HostAddressRec));
if(a == NULL) {
object->flags &= ~OBJECT_INPROGRESS;
abortObject(object, 501, internAtom("Couldn't allocate address"));
notifyObject(object);
return 0;
}
object->headers = a;
object->age = current_time.tv_sec;
object->expires = current_time.tv_sec + dnsGethostbynameTtl;
object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
notifyObject(object);
return 0;
}
#endif
#else
#ifndef NO_STANDARD_RESOLVER
static int
really_do_gethostbyname(AtomPtr name, ObjectPtr object)
{
struct hostent *host;
char *s;
AtomPtr a;
int i, j;
int error;
host = gethostbyname(name->string);
if(host == NULL) {
switch(h_errno) {
case HOST_NOT_FOUND: error = EDNS_HOST_NOT_FOUND; break;
#ifdef NO_ADDRESS
case NO_ADDRESS: error = EDNS_NO_ADDRESS; break;
#endif
case NO_RECOVERY: error = EDNS_NO_RECOVERY; break;
case TRY_AGAIN: error = EDNS_TRY_AGAIN; break;
default: error = EUNKNOWN; break;
}
if(error == EDNS_HOST_NOT_FOUND) {
object->headers = NULL;
object->age = current_time.tv_sec;
object->expires = current_time.tv_sec + dnsNegativeTtl;
object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
object->flags &= ~OBJECT_INPROGRESS;
notifyObject(object);
return 0;
} else {
do_log_error(L_ERROR, error, "Gethostbyname failed");
abortObject(object, 404,
internAtomError(error, "Gethostbyname failed"));
object->flags &= ~OBJECT_INPROGRESS;
notifyObject(object);
return 0;
}
}
if(host->h_addrtype != AF_INET) {
do_log(L_ERROR, "Address is not AF_INET.\n");
object->flags &= ~OBJECT_INPROGRESS;
abortObject(object, 404, internAtom("Address is not AF_INET"));
notifyObject(object);
return -1;
}
if(host->h_length != sizeof(struct in_addr)) {
do_log(L_ERROR, "Address size inconsistent.\n");
object->flags &= ~OBJECT_INPROGRESS;
abortObject(object, 404, internAtom("Address size inconsistent"));
notifyObject(object);
return 0;
}
i = 0;
while(host->h_addr_list[i] != NULL) i++;
s = malloc(1 + i * sizeof(HostAddressRec));
if(s == NULL) {
a = NULL;
} else {
memset(s, 0, 1 + i * sizeof(HostAddressRec));
s[0] = DNS_A;
for(j = 0; j < i; j++) {
s[j * sizeof(HostAddressRec) + 1] = 4;
memcpy(&s[j * sizeof(HostAddressRec) + 2], host->h_addr_list[j],
sizeof(struct in_addr));
}
a = internAtomN(s, i * sizeof(HostAddressRec) + 1);
free(s);
}
if(!a) {
object->flags &= ~OBJECT_INPROGRESS;
abortObject(object, 501, internAtom("Couldn't allocate address"));
notifyObject(object);
return 0;
}
object->headers = a;
object->age = current_time.tv_sec;
object->expires = current_time.tv_sec + dnsGethostbynameTtl;
object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
notifyObject(object);
return 0;
}
#endif
#endif
#ifdef NO_STANDARD_RESOLVER
static int
really_do_gethostbyname(AtomPtr name, ObjectPtr object)
{
abort();
}
#endif
#ifndef NO_FANCY_RESOLVER
static int dnsSocket = -1;
static FdEventHandlerPtr dnsSocketHandler = NULL;
static int
dnsHandler(int status, ConditionHandlerPtr chandler)
{
GethostbynameRequestRec request = *(GethostbynameRequestPtr)chandler->data;
ObjectPtr object = request.object;
assert(!(object->flags & OBJECT_INPROGRESS));
if(object->headers) {
request.addr = retainAtom(object->headers);
dnsDelayedNotify(0, &request);
} else {
if(object->message)
request.error_message = retainAtom(object->message);
dnsDelayedNotify(1, &request);
}
releaseObject(object);
return 1;
}
static int
queryInFlight(DnsQueryPtr query)
{
DnsQueryPtr other;
other = inFlightDnsQueries;
while(other) {
if(other == query)
return 1;
other = other->next;
}
return 0;
}
static void
removeQuery(DnsQueryPtr query)
{
DnsQueryPtr previous;
if(query == inFlightDnsQueries) {
inFlightDnsQueries = query->next;
if(inFlightDnsQueries == NULL)
inFlightDnsQueriesLast = NULL;
} else {
previous = inFlightDnsQueries;
while(previous->next) {
if(previous->next == query)
break;
previous = previous->next;
}
assert(previous->next != NULL);
previous->next = query->next;
if(previous->next == NULL)
inFlightDnsQueriesLast = previous;
}
}
static void
insertQuery(DnsQueryPtr query)
{
if(inFlightDnsQueriesLast)
inFlightDnsQueriesLast->next = query;
else
inFlightDnsQueries = query;
inFlightDnsQueriesLast = query;
}
static DnsQueryPtr
findQuery(int id, AtomPtr name)
{
DnsQueryPtr query;
query = inFlightDnsQueries;
while(query) {
if(query->id == id && (name == NULL || query->name == name))
return query;
query = query->next;
}
return NULL;
}
static int
dnsTimeoutHandler(TimeEventHandlerPtr event)
{
DnsQueryPtr query = *(DnsQueryPtr*)event->data;
ObjectPtr object = query->object;
int rc;
/* People are reporting that this does happen. And I have no idea why. */
if(!queryInFlight(query)) {
do_log(L_ERROR, "BUG: timing out martian query (%s, flags: 0x%x).\n",
scrub(query->name->string), (unsigned)object->flags);
return 1;
}
query->timeout = MAX(10, query->timeout * 2);
if(query->timeout > dnsMaxTimeout) {
abortObject(object, 501, internAtom("Timeout"));
goto fail;
} else {
rc = sendQuery(query);
if(rc < 0) {
if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) {
abortObject(object, 501,
internAtomError(-rc,
"Couldn't send DNS query"));
goto fail;
}
/* else let it timeout */
}
query->timeout_handler =
scheduleTimeEvent(query->timeout, dnsTimeoutHandler,
sizeof(query), &query);
if(query->timeout_handler == NULL) {
do_log(L_ERROR, "Couldn't schedule DNS timeout handler.\n");
abortObject(object, 501,
internAtom("Couldn't schedule DNS timeout handler"));
goto fail;
}
return 1;
}
fail:
removeQuery(query);
object->flags &= ~OBJECT_INPROGRESS;
if(query->inet4) releaseAtom(query->inet4);
if(query->inet6) releaseAtom(query->inet6);
free(query);
releaseNotifyObject(object);
return 1;
}
static int
establishDnsSocket()
{
int rc;
#ifdef HAVE_IPv6
int inet6 = (nameserverAddress.sa_family == AF_INET6);
int pf = inet6 ? PF_INET6 : PF_INET;
int sa_size =
inet6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
#else
int pf = PF_INET;
int sa_size = sizeof(struct sockaddr_in);
#endif
if(dnsSocket < 0) {
assert(!dnsSocketHandler);
dnsSocket = socket(pf, SOCK_DGRAM, 0);
if(dnsSocket < 0) {
do_log_error(L_ERROR, errno, "Couldn't create DNS socket");
return -errno;
}
rc = connect(dnsSocket, &nameserverAddress, sa_size);
if(rc < 0) {
CLOSE(dnsSocket);
dnsSocket = -1;
do_log_error(L_ERROR, errno, "Couldn't create DNS \"connection\"");
return -errno;
}
}
if(!dnsSocketHandler) {
dnsSocketHandler =
registerFdEvent(dnsSocket, POLLIN, dnsReplyHandler, 0, NULL);
if(dnsSocketHandler == NULL) {
do_log(L_ERROR, "Couldn't register DNS socket handler.\n");
CLOSE(dnsSocket);
dnsSocket = -1;
return -ENOMEM;
}
}
return 1;
}
static int
sendQuery(DnsQueryPtr query)
{
char buf[512];
int buflen;
int rc;
int af[2];
int i;
if(dnsSocket < 0)
return -1;
if(dnsQueryIPv6 <= 0) {
af[0] = 4; af[1] = 0;
} else if(dnsQueryIPv6 <= 2) {
af[0] = 4; af[1] = 6;
} else {
af[0] = 6; af[1] = 0;
}
for(i = 0; i < 2; i++) {
if(af[i] == 0)
continue;
if(af[i] == 4 && query->inet4)
continue;
else if(af[i] == 6 && query->inet6)
continue;
buflen = dnsBuildQuery(query->id, buf, 0, 512, query->name, af[i]);
if(buflen <= 0) {
do_log(L_ERROR, "Couldn't build DNS query.\n");
return buflen;
}
rc = send(dnsSocket, buf, buflen, 0);
if(rc < buflen) {
if(rc >= 0) {
do_log(L_ERROR, "Couldn't send DNS query: partial send.\n");
return -EAGAIN;
} else {
do_log_error(L_ERROR, errno, "Couldn't send DNS query");
return -errno;
}
}
}
return 1;
}
static int
really_do_dns(AtomPtr name, ObjectPtr object)
{
int rc;
DnsQueryPtr query;
AtomPtr message = NULL;
int id;
AtomPtr a = NULL;
if(a == NULL) {
if(name == atomLocalhost || name == atomLocalhostDot) {
char s[1 + sizeof(HostAddressRec)];
memset(s, 0, sizeof(s));
s[0] = DNS_A;
s[1] = 4;
s[2] = 127;
s[3] = 0;
s[4] = 0;
s[5] = 1;
a = internAtomN(s, 1 + sizeof(HostAddressRec));
if(a == NULL) {
abortObject(object, 501,
internAtom("Couldn't allocate address"));
notifyObject(object);
errno = ENOMEM;
return -1;
}
}
}
if(a == NULL) {
struct in_addr ina;
rc = inet_aton(name->string, &ina);
if(rc == 1) {
char s[1 + sizeof(HostAddressRec)];
memset(s, 0, sizeof(s));
s[0] = DNS_A;
s[1] = 4;
memcpy(s + 2, &ina, 4);
a = internAtomN(s, 1 + sizeof(HostAddressRec));
if(a == NULL) {
abortObject(object, 501,
internAtom("Couldn't allocate address"));
notifyObject(object);
errno = ENOMEM;
return -1;
}
}
}
#ifdef HAVE_IPv6
if(a == NULL)
a = rfc2732(name);
#endif
if(a) {
object->headers = a;
object->age = current_time.tv_sec;
object->expires = current_time.tv_sec + 240;
object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
notifyObject(object);
return 0;
}
rc = establishDnsSocket();
if(rc < 0) {
do_log_error(L_ERROR, -rc, "Couldn't establish DNS socket.\n");
message = internAtomError(-rc, "Couldn't establish DNS socket");
goto fallback;
}
/* The id is used to speed up detecting replies to queries that
are no longer current -- see dnsReplyHandler. */
id = (idSeed++) & 0xFFFF;
query = malloc(sizeof(DnsQueryRec));
if(query == NULL) {
do_log(L_ERROR, "Couldn't allocate DNS query.\n");
message = internAtom("Couldn't allocate DNS query");
goto fallback;
}
query->id = id;
query->inet4 = NULL;
query->inet6 = NULL;
query->name = name;
query->time = current_time.tv_sec;
query->object = retainObject(object);
query->timeout = 4;
query->timeout_handler = NULL;
query->next = NULL;
query->timeout_handler =
scheduleTimeEvent(query->timeout, dnsTimeoutHandler,
sizeof(query), &query);
if(query->timeout_handler == NULL) {
do_log(L_ERROR, "Couldn't schedule DNS timeout handler.\n");
message = internAtom("Couldn't schedule DNS timeout handler");
goto free_fallback;
}
insertQuery(query);
object->flags |= OBJECT_INPROGRESS;
rc = sendQuery(query);
if(rc < 0) {
if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) {
object->flags &= ~OBJECT_INPROGRESS;
message = internAtomError(-rc, "Couldn't send DNS query");
goto remove_fallback;
}
/* else let it timeout */
}
releaseAtom(message);
return 1;
remove_fallback:
removeQuery(query);
free_fallback:
releaseObject(query->object);
cancelTimeEvent(query->timeout_handler);
free(query);
fallback:
if(dnsUseGethostbyname >= 1) {
releaseAtom(message);
do_log(L_WARN, "Falling back on gethostbyname.\n");
return really_do_gethostbyname(name, object);
} else {
abortObject(object, 501, message);
notifyObject(object);
return 1;
}
}
static int
dnsReplyHandler(int abort, FdEventHandlerPtr event)
{
int fd = event->fd;
char buf[2048];
int len, rc;
ObjectPtr object;
unsigned ttl = 0;
AtomPtr name, value, message = NULL;
int id;
int af;
DnsQueryPtr query;
AtomPtr cname = NULL;
if(abort) {
dnsSocketHandler = NULL;
rc = establishDnsSocket();
if(rc < 0) {
do_log(L_ERROR, "Couldn't reestablish DNS socket.\n");
/* At this point, we should abort all in-flight
DNS requests. Oh, well, they'll timeout anyway. */
}
return 1;
}
len = recv(fd, buf, 2048, 0);
if(len <= 0) {
if(errno == EINTR || errno == EAGAIN) return 0;
/* This is where we get ECONNREFUSED for an ICMP port unreachable */
do_log_error(L_ERROR, errno, "DNS: recv failed");
dnsGethostbynameFallback(-1, message);
return 0;
}
/* This could be a late reply to a query that timed out and was
resent, a reply to a query that timed out, or a reply to an
AAAA query when we already got a CNAME reply to the associated
A. We filter such replies straight away, without trying to
parse them. */
rc = dnsReplyId(buf, 0, len, &id);
if(rc < 0) {
do_log(L_WARN, "Short DNS reply.\n");
return 0;
}
if(!findQuery(id, NULL)) {
return 0;
}
rc = dnsDecodeReply(buf, 0, len, &id, &name, &value, &af, &ttl);
if(rc < 0) {
assert(value == NULL);
/* We only want to fallback on gethostbyname if we received a
reply that we could not understand. What about truncated
replies? */
if(rc < 0) {
do_log_error(L_WARN, -rc, "DNS");
if(dnsUseGethostbyname >= 2 ||
(dnsUseGethostbyname &&
(rc != -EDNS_HOST_NOT_FOUND && rc != -EDNS_NO_RECOVERY &&
rc != -EDNS_FORMAT))) {
dnsGethostbynameFallback(id, message);
return 0;
} else {
message = internAtom(pstrerror(-rc));
}
} else {
assert(name != NULL && id >= 0 && af >= 0);
}
}
query = findQuery(id, name);
if(query == NULL) {
/* Duplicate id ? */
releaseAtom(value);
releaseAtom(name);
return 0;
}
/* We're going to use the information in this reply. If it was an
error, construct an empty atom to distinguish it from information
we're still waiting for. */
if(value == NULL)
value = internAtom("");
again:
if(af == 4) {
if(query->inet4 == NULL) {
query->inet4 = value;
query->ttl4 = current_time.tv_sec + ttl;
} else
releaseAtom(value);
} else if(af == 6) {
if(query->inet6 == NULL) {
query->inet6 = value;
query->ttl6 = current_time.tv_sec + ttl;
} else
releaseAtom(value);
} else if(af == 0) {
/* Ignore errors in this case. */
if(query->inet4 && query->inet4->length == 0) {
releaseAtom(query->inet4);
query->inet4 = NULL;
}
if(query->inet6 && query->inet6->length == 0) {
releaseAtom(query->inet6);
query->inet6 = NULL;
}
if(query->inet4 || query->inet6) {
do_log(L_WARN, "Host %s has both %s and CNAME -- "
"ignoring CNAME.\n", scrub(query->name->string),
query->inet4 ? "A" : "AAAA");
releaseAtom(value);
value = internAtom("");
af = query->inet4 ? 4 : 6;
goto again;
} else {
cname = value;
}
}
if(rc >= 0 && !cname &&
((dnsQueryIPv6 < 3 && query->inet4 == NULL) ||
(dnsQueryIPv6 > 0 && query->inet6 == NULL)))
return 0;
/* This query is complete */
cancelTimeEvent(query->timeout_handler);
object = query->object;
if(object->flags & OBJECT_INITIAL) {
assert(!object->headers);
if(cname) {
assert(query->inet4 == NULL && query->inet6 == NULL);
object->headers = cname;
object->expires = current_time.tv_sec + ttl;
} else if((!query->inet4 || query->inet4->length == 0) &&
(!query->inet6 || query->inet6->length == 0)) {
releaseAtom(query->inet4);
releaseAtom(query->inet6);
object->expires = current_time.tv_sec + dnsNegativeTtl;
abortObject(object, 500, retainAtom(message));
} else if(!query->inet4 || query->inet4->length == 0) {
object->headers = query->inet6;
object->expires = query->ttl6;
releaseAtom(query->inet4);
} else if(!query->inet6 || query->inet6->length == 0) {
object->headers = query->inet4;
object->expires = query->ttl4;
releaseAtom(query->inet6);
} else {
/* need to merge results */
char buf[1024];
if(query->inet4->length + query->inet6->length > 1024) {
releaseAtom(query->inet4);
releaseAtom(query->inet6);
abortObject(object, 500, internAtom("DNS reply too long"));
} else {
if(dnsQueryIPv6 <= 1) {
memcpy(buf, query->inet4->string, query->inet4->length);
memcpy(buf + query->inet4->length,
query->inet6->string + 1, query->inet6->length - 1);
} else {
memcpy(buf, query->inet6->string, query->inet6->length);
memcpy(buf + query->inet6->length,
query->inet4->string + 1, query->inet4->length - 1);
}
object->headers =
internAtomN(buf,
query->inet4->length +
query->inet6->length - 1);
if(object->headers == NULL)
abortObject(object, 500,
internAtom("Couldn't allocate DNS atom"));
}
object->expires = MIN(query->ttl4, query->ttl6);
}
object->age = current_time.tv_sec;
object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
} else {
do_log(L_WARN, "DNS object ex nihilo for %s.\n",
scrub(query->name->string));
}
removeQuery(query);
free(query);
releaseAtom(name);
releaseAtom(message);
releaseNotifyObject(object);
return 0;
}
static int
dnsGethostbynameFallback(int id, AtomPtr message)
{
DnsQueryPtr query, previous;
ObjectPtr object;
if(inFlightDnsQueries == NULL) {
releaseAtom(message);
return 1;
}
query = NULL;
if(id < 0 || inFlightDnsQueries->id == id) {
previous = NULL;
query = inFlightDnsQueries;
} else {
previous = inFlightDnsQueries;
while(previous->next) {
if(previous->next->id == id) {
query = previous->next;
break;
}
previous = previous->next;
}
if(!query) {
previous = NULL;
query = inFlightDnsQueries;
}
}
if(previous == NULL) {
inFlightDnsQueries = query->next;
if(inFlightDnsQueries == NULL)
inFlightDnsQueriesLast = NULL;
} else {
previous->next = query->next;
if(query->next == NULL)
inFlightDnsQueriesLast = NULL;
}
object = makeObject(OBJECT_DNS, query->name->string, query->name->length,
1, 0, NULL, NULL);
if(!object) {
do_log(L_ERROR, "Couldn't make DNS object.\n");
releaseAtom(query->name);
releaseAtom(message);
releaseObject(query->object);
cancelTimeEvent(query->timeout_handler);
free(query);
return -1;
}
if(dnsUseGethostbyname >= 1) {
releaseAtom(message);
do_log(L_WARN, "Falling back to using system resolver.\n");
really_do_gethostbyname(retainAtom(query->name), object);
} else {
releaseAtom(object->message);
object->message = message;
object->flags &= ~OBJECT_INPROGRESS;
releaseNotifyObject(object);
}
cancelTimeEvent(query->timeout_handler);
releaseAtom(query->name);
if(query->inet4) releaseAtom(query->inet4);
if(query->inet6) releaseAtom(query->inet6);
releaseObject(query->object);
free(query);
return 1;
}
static int
stringToLabels(char *buf, int offset, int n, char *string)
{
int i = offset;
int j = 0, k = 0;
while(1) {
while(string[k] != '.' && string[k] != '\0')
k++;
if(k >= j + 256) return -1;
buf[i] = (unsigned char)(k - j); i++; if(i >= n) return -1;
while(j < k) {
buf[i] = string[j]; i++; j++; if(i >= n) return -1;
}
if(string[j] == '\0') {
buf[i] = '\0';
i++; if(i >= n) return -1;
break;
}
j++; k++;
}
return i;
}
#ifdef UNALIGNED_ACCESS
#define DO_NTOHS(_d, _s) _d = ntohs(*(unsigned short*)(_s));
#define DO_NTOHL(_d, _s) _d = ntohl(*(unsigned*)(_s))
#define DO_HTONS(_d, _s) *(unsigned short*)(_d) = htons(_s);
#define DO_HTONL(_d, _s) *(unsigned*)(_d) = htonl(_s)
#else
#define DO_NTOHS(_d, _s) \
do { unsigned short _dd; \
memcpy(&(_dd), (_s), sizeof(unsigned short)); \
_d = ntohs(_dd); } while(0)
#define DO_NTOHL(_d, _s) \
do { unsigned _dd; \
memcpy(&(_dd), (_s), sizeof(unsigned)); \
_d = ntohl(_dd); } while(0)
#define DO_HTONS(_d, _s) \
do { unsigned short _dd; \
_dd = htons(_s); \
memcpy((_d), &(_dd), sizeof(unsigned short)); } while(0);
#define DO_HTONL(_d, _s) \
do { unsigned _dd; \
_dd = htonl(_s); \
memcpy((_d), &(_dd), sizeof(unsigned)); } while(0);
#endif
static int
labelsToString(char *buf, int offset, int n, char *d, int m, int *j_return)
{
int i = offset, j, k;
int ll, rc;
j = 0;
while(1) {
if(i >= n) return -1;
ll = *(unsigned char*)&buf[i]; i++;
if(ll == 0) {
break;
}
if((ll & (3 << 6)) == (3 << 6)) {
/* RFC 1035, 4.1.4 */
int o;
if(i >= n) return -1;
o = (ll & ~(3 << 6)) << 8 | *(unsigned char*)&buf[i];
i++;
rc = labelsToString(buf, o, n, &d[j], m - j, &k);
if(rc < 0)
return -1;
j += k;
break;
} else if((ll & (3 << 6)) == 0) {
for(k = 0; k < ll; k++) {
if(i >= n || j >= m) return -1;
d[j++] = buf[i++];
}
if(i >= n) return -1;
if(buf[i] != '\0') {
if(j >= m) return -1;
d[j++] = '.';
}
} else {
return -1;
}
}
*j_return = j;
return i;
}
static int
dnsBuildQuery(int id, char *buf, int offset, int n, AtomPtr name, int af)
{
int i = offset;
int type;
switch(af) {
case 4: type = 1; break;
case 6: type = 28; break;
default: return -EINVAL;
}
if(i + 12 >= n) return -1;
DO_HTONS(&buf[i], id); i += 2;
DO_HTONS(&buf[i], 1<<8); i += 2;
DO_HTONS(&buf[i], 1); i += 2;
DO_HTONS(&buf[i], 0); i += 2;
DO_HTONS(&buf[i], 0); i += 2;
DO_HTONS(&buf[i], 0); i += 2;
i = stringToLabels(buf, i, n, name->string);
if(i < 0) return -ENAMETOOLONG;
if(i + 4 >= n) return -ENAMETOOLONG;
DO_HTONS(&buf[i], type); i += 2;
DO_HTONS(&buf[i], 1); i += 2;
return i;
}
static int
dnsReplyId(char *buf, int offset, int n, int *id_return)
{
if(n - offset < 12)
return -1;
DO_NTOHS(*id_return, &buf[offset]);
return 1;
}
static int
dnsDecodeReply(char *buf, int offset, int n, int *id_return,
AtomPtr *name_return, AtomPtr *value_return,
int *af_return, unsigned *ttl_return)
{
int i = offset, j, m;
int id = -1, b23, qdcount, ancount, nscount, arcount, rdlength;
int class, type;
unsigned int ttl;
char b[2048];
int af = -1;
AtomPtr name = NULL, value;
char addresses[1024];
int addr_index = 0;
int error = EDNS_NO_ADDRESS;
unsigned final_ttl = 7 * 24 * 3600;
int dnserror;
if(n - i < 12) {
error = EDNS_INVALID;
goto fail;
}
DO_NTOHS(id, &buf[i]); i += 2;
DO_NTOHS(b23, &buf[i]); i += 2;
DO_NTOHS(qdcount, &buf[i]); i += 2;
DO_NTOHS(ancount, &buf[i]); i += 2;
DO_NTOHS(nscount, &buf[i]); i += 2;
DO_NTOHS(arcount, &buf[i]); i += 2;
do_log(D_DNS,
"DNS id %d, b23 0x%x, qdcount %d, ancount %d, "
"nscount %d, arcount %d\n",
id, b23, qdcount, ancount, nscount, arcount);
if((b23 & (0xF870)) != 0x8000) {
do_log(L_ERROR, "Incorrect DNS reply (b23 = 0x%x).\n", b23);
error = EDNS_INVALID;
goto fail;
}
dnserror = b23 & 0xF;
if(b23 & 0x200) {
do_log(L_WARN, "Truncated DNS reply (b23 = 0x%x).\n", b23);
}
if(dnserror || qdcount != 1) {
if(!dnserror)
do_log(L_ERROR,
"Unexpected number %d of DNS questions.\n", qdcount);
if(dnserror == 1)
error = EDNS_FORMAT;
else if(dnserror == 2)
error = EDNS_NO_RECOVERY;
else if(dnserror == 3)
error = EDNS_HOST_NOT_FOUND;
else if(dnserror == 4 || dnserror == 5)
error = EDNS_REFUSED;
else if(dnserror == 0)
error = EDNS_INVALID;
else
error = EUNKNOWN;
goto fail;
}
/* We do this early, so that we can return the address family to
the caller in case of error. */
i = labelsToString(buf, i, n, b, 2048, &m);
if(i < 0) {
error = EDNS_FORMAT;
goto fail;
}
DO_NTOHS(type, &buf[i]); i += 2;
DO_NTOHS(class, &buf[i]); i += 2;
if(type == 1)
af = 4;
else if(type == 28)
af = 6;
else {
error = EDNS_FORMAT;
goto fail;
}
do_log(D_DNS, "DNS q: ");
do_log_n(D_DNS, b, m);
do_log(D_DNS, " (%d, %d)\n", type, class);
name = internAtomLowerN(b, m);
if(name == NULL) {
error = ENOMEM;
goto fail;
}
if(class != 1) {
error = EDNS_FORMAT;
goto fail;
}
#define PARSE_ANSWER(kind, label) \
do { \
i = labelsToString(buf, i, 1024, b, 2048, &m); \
if(i < 0) goto label; \
DO_NTOHS(type, &buf[i]); i += 2; if(i > 1024) goto label; \
DO_NTOHS(class, &buf[i]); i += 2; if(i > 1024) goto label; \
DO_NTOHL(ttl, &buf[i]); i += 4; if(i > 1024) goto label; \
DO_NTOHS(rdlength, &buf[i]); i += 2; if(i > 1024) goto label; \
do_log(D_DNS, "DNS " kind ": "); \
do_log_n(D_DNS, b, m); \
do_log(D_DNS, " (%d, %d): %d bytes, ttl %u\n", \
type, class, rdlength, ttl); \
} while(0)
for(j = 0; j < ancount; j++) {
PARSE_ANSWER("an", fail);
if(strcasecmp_n(name->string, b, m) == 0) {
if(class != 1) {
do_log(D_DNS, "DNS: %s: unknown class %d.\n",
name->string, class);
error = EDNS_UNSUPPORTED;
goto cont;
}
if(type == 1 || type == 28) {
if((type == 1 && rdlength != 4) ||
(type == 28 && rdlength != 16)) {
do_log(L_ERROR,
"DNS: %s: unexpected length %d of %s record.\n",
scrub(name->string),
rdlength, type == 1 ? "A" : "AAAA");
error = EDNS_INVALID;
if(rdlength <= 0 || rdlength >= 32)
goto fail;
goto cont;
}
if(af == 0) {
do_log(L_WARN, "DNS: %s: host has both A and CNAME -- "
"ignoring CNAME.\n", scrub(name->string));
addr_index = 0;
af = -1;
}
if(type == 1) {
if(af < 0)
af = 4;
else if(af == 6) {
do_log(L_WARN, "Unexpected AAAA reply.\n");
goto cont;
}
} else {
if(af < 0)
af = 6;
else if(af == 4) {
do_log(L_WARN, "Unexpected A reply.\n");
goto cont;
}
}
if(addr_index == 0) {
addresses[0] = DNS_A;
addr_index++;
} else {
if(addr_index > 1000) {
error = EDNS_INVALID;
goto fail;
}
}
assert(addresses[0] == DNS_A);
if(final_ttl > ttl)
final_ttl = ttl;
memset(&addresses[addr_index], 0, sizeof(HostAddressRec));
if(type == 1) {
addresses[addr_index] = 4;
memcpy(addresses + addr_index + 1, buf + i, 4);
} else {
addresses[addr_index] = 6;
memcpy(addresses + addr_index + 1, buf + i, 16);
}
addr_index += sizeof(HostAddressRec);
} else if(type == 5) {
int j, k;
if(af != 0 && addr_index > 0) {
do_log(L_WARN, "DNS: host has both CNAME and A -- "
"ignoring CNAME.\n");
goto cont;
}
af = 0;
if(addr_index != 0) {
/* Only warn if the CNAMEs are not identical */
char tmp[512]; int jj, kk;
assert(addresses[0] == DNS_CNAME);
jj = labelsToString(buf, i, n,
tmp, 512, &kk);
if(jj < 0 ||
kk != strlen(addresses + 1) ||
memcmp(addresses + 1, tmp, kk) != 0) {
do_log(L_WARN, "DNS: "
"%s: host has multiple CNAMEs -- "
"ignoring subsequent.\n",
scrub(name->string));
}
goto cont;
}
addresses[0] = DNS_CNAME;
addr_index++;
j = labelsToString(buf, i, n,
addresses + 1, 1020, &k);
if(j < 0) {
addr_index = 0;
error = ENAMETOOLONG;
continue;
}
addr_index = k + 1;
} else {
error = EDNS_NO_ADDRESS;
i += rdlength;
continue;
}
}
cont:
i += rdlength;
}
#if (LOGGING_MAX & D_DNS)
for(j = 0; j < nscount; j++) {
PARSE_ANSWER("ns", nofail);
i += rdlength;
}
for(j = 0; j < arcount; j++) {
PARSE_ANSWER("ar", nofail);
i += rdlength;
}
nofail:
#endif
#undef PARSE_ANSWER
do_log(D_DNS, "DNS: %d bytes\n", addr_index);
if(af < 0)
goto fail;
value = internAtomN(addresses, addr_index);
if(value == NULL) {
error = ENOMEM;
goto fail;
}
assert(af >= 0);
*id_return = id;
*name_return = name;
*value_return = value;
*af_return = af;
*ttl_return = final_ttl;
return 1;
fail:
*id_return = id;
*name_return = name;
*value_return = NULL;
*af_return = af;
return -error;
}
#else
static int
really_do_dns(AtomPtr name, ObjectPtr object)
{
abort();
}
#endif
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。