mirror of
https://github.com/3proxy/3proxy.git
synced 2026-10-28 22:05:48 +08:00
dnspr: name servers asked from the loop, nothing waits in it
dnspr answered an A or AAAA query missing from the name cache with udpresolve(), which waited for the name servers in place, and a name server over TCP was waited for in place too, holding up every query of the service. Now only the name cache is looked at in place; anything it does not have goes to the name servers from the loop: over UDP the query is sent on a socket the loop waits on, over TCP the socket is connected without waiting, the query written once the loop sees it writable, and the answer read in as many pieces as it comes. A name server silent for DNS_TO is followed by the next one, as udpresolve() did; the last ends the query with 821. An address in an answer is kept in the name cache, unless -s says not to use it. The UDP loop lets a session say what it waits for on its socket (events) and a service handle a session's deadline itself (timeout). Tests: an A query not in the cache answered by the name server and then from the cache with the server gone, an A query waiting for a silent name server while another is answered, a name server over TCP whose answer comes in pieces, and the second name server asked when the first is silent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
f28552fc6f
commit
58334cb51c
260
src/dnspr.c
260
src/dnspr.c
@ -15,22 +15,30 @@
|
||||
#define BUFSIZE 16384
|
||||
|
||||
/* dnspr: a query is a session of its own in the service's thread (see
|
||||
udploop() in proxymain.c). It is answered there and then, from the name
|
||||
cache, the resolver or a fake address, or sent on to the first name
|
||||
server, whose answer, or its timeout, ends it. A name server over TCP is
|
||||
still waited for in the loop. */
|
||||
udploop() in mainudp.c). It is answered there and then from a fake
|
||||
address, the name cache, or the service's own address for a PTR;
|
||||
anything else goes to the name servers, one after another, over UDP or
|
||||
TCP, the loop waiting for their sockets: nothing here blocks. An address
|
||||
in an answer is kept in the name cache. */
|
||||
|
||||
struct dnsquery {
|
||||
unsigned type;
|
||||
uint32_t ip, ttl;
|
||||
unsigned char addr[16];
|
||||
char *host;
|
||||
unsigned char *query; /* as the client sent it, for a name server */
|
||||
/* the query as the client sent it, behind 2 bytes for its length over
|
||||
TCP */
|
||||
unsigned char *query;
|
||||
int len;
|
||||
/* the name server asked, and over TCP what of its answer came */
|
||||
int server;
|
||||
int tcp, connecting;
|
||||
unsigned char *answer;
|
||||
int got;
|
||||
};
|
||||
|
||||
/* The answer to the client, the query in buf made one: ip set, an address
|
||||
for it; a PTR for the service's own address; else the name not found. */
|
||||
for it; else the name not found. */
|
||||
static int dnsanswer(struct udploop *u, struct clientparam *param, struct dnsquery *q, unsigned char *buf, int len){
|
||||
uint32_t ttl;
|
||||
|
||||
@ -59,6 +67,113 @@ static int dnsanswer(struct udploop *u, struct clientparam *param, struct dnsque
|
||||
return q->ip? 0 : 888;
|
||||
}
|
||||
|
||||
/* An address of the type asked for in a name server's answer: noted for the
|
||||
log and kept in the name cache for its TTL, up to 12 hours. */
|
||||
static void dnscache(struct clientparam *param, struct dnsquery *q, unsigned char *buf, int len){
|
||||
int k = 12, n, j, rlen;
|
||||
unsigned type;
|
||||
uint32_t ttl;
|
||||
|
||||
if((q->type != 0x01 && q->type != 0x1c) || !q->host) return;
|
||||
while(k < len && buf[k]) k += buf[k] + 1;
|
||||
k += 5;
|
||||
n = buf[7] + (((unsigned)buf[6])<<8);
|
||||
for(j = 0; j < n && k < len; j++){
|
||||
/* the name: a pointer, or labels to the end */
|
||||
if((buf[k] & 0xc0) == 0xc0) k += 2;
|
||||
else {
|
||||
while(k < len && buf[k]) k += buf[k] + 1;
|
||||
k++;
|
||||
}
|
||||
if(k + 10 > len) return;
|
||||
type = buf[k+1] + (((unsigned)buf[k])<<8);
|
||||
rlen = buf[k+9] + (((unsigned)buf[k+8])<<8);
|
||||
if(k + 10 + rlen > len) return;
|
||||
if(type == q->type && rlen == (q->type == 0x01? 4 : 16)){
|
||||
ttl = ntohl(*(uint32_t *)(buf + k + 4));
|
||||
if(ttl > 3600*12) ttl = 3600*12;
|
||||
if(!ttl) ttl = 1;
|
||||
memcpy(q->addr, buf + k + 10, rlen);
|
||||
q->ip = 1;
|
||||
if(!param->srv->s_option)
|
||||
hashadd(q->type == 0x1c? &dns6_table : &dns_table, (unsigned char *)q->host, q->addr, curtime + ttl);
|
||||
return;
|
||||
}
|
||||
k += 10 + rlen;
|
||||
}
|
||||
}
|
||||
|
||||
/* An answer from a name server: to the client as it is if it has answers,
|
||||
else as the name not found. 1: the query is done. */
|
||||
static int dnsreply(struct udploop *u, struct udpsession *ss, unsigned char *buf, int len){
|
||||
struct clientparam *param = ss->param;
|
||||
struct dnsquery *q = ss->data;
|
||||
|
||||
param->statssrv64 += len;
|
||||
param->nreads++;
|
||||
if(buf[6] || buf[7]){
|
||||
dnscache(param, q, buf, len);
|
||||
param->res = (udpanswer(u, param, buf, len) == len)? 0 : 822;
|
||||
return 1;
|
||||
}
|
||||
q->ip = 0;
|
||||
param->res = dnsanswer(u, param, q, buf, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The query to name server q->server or a later one: 0 once it is on its
|
||||
way, the loop to wait for the answer; nonzero if none takes it. */
|
||||
static int dnsforward(struct udploop *u, struct udpsession *ss){
|
||||
struct clientparam *param = ss->param;
|
||||
struct dnsquery *q = ss->data;
|
||||
struct extparam *c = param->srv->config;
|
||||
#ifdef _WIN32
|
||||
unsigned long ul = 1;
|
||||
#endif
|
||||
|
||||
for(; q->server < c->numservers; q->server++){
|
||||
if(param->remsock != INVALID_SOCKET){
|
||||
param->srv->so._closesocket(param->sostate, param->remsock);
|
||||
param->remsock = INVALID_SOCKET;
|
||||
}
|
||||
q->tcp = c->nservers[q->server].usetcp;
|
||||
q->got = 0;
|
||||
if((param->remsock = param->srv->so._socket(param->sostate, SASOCK(&c->nservers[q->server].addr),
|
||||
q->tcp? SOCK_STREAM : SOCK_DGRAM, q->tcp? IPPROTO_TCP : IPPROTO_UDP)) == INVALID_SOCKET) continue;
|
||||
#ifdef _WIN32
|
||||
ioctlsocket(param->remsock, FIONBIO, &ul);
|
||||
#else
|
||||
fcntl(param->remsock, F_SETFL, O_NONBLOCK | fcntl(param->remsock, F_GETFL));
|
||||
#endif
|
||||
memset(¶m->sinsl, 0, sizeof(param->sinsl));
|
||||
*SAFAMILY(¶m->sinsl) = *SAFAMILY(&c->nservers[q->server].addr);
|
||||
if(bindwithrange(param, param->remsock, ¶m->sinsl, param->extport)) continue;
|
||||
param->sinsr = c->nservers[q->server].addr;
|
||||
ss->idle = 0;
|
||||
ss->deadline = time(NULL) + param->srv->timeouts[DNS_TO];
|
||||
if(q->tcp){
|
||||
/* connected, then written to, as the loop sees it writable */
|
||||
if(param->srv->so._connect(param->sostate, param->remsock, (struct sockaddr *)¶m->sinsr, SASIZE(¶m->sinsr))
|
||||
#ifdef _WIN32
|
||||
&& WSAGetLastError() != WSAEWOULDBLOCK
|
||||
#else
|
||||
&& errno != EINPROGRESS
|
||||
#endif
|
||||
) continue;
|
||||
q->connecting = 1;
|
||||
ss->events = POLLOUT;
|
||||
return 0;
|
||||
}
|
||||
ss->events = POLLIN;
|
||||
if(param->srv->so._sendto(param->sostate, param->remsock, (char *)q->query + 2, q->len, 0,
|
||||
(struct sockaddr *)¶m->sinsr, SASIZE(¶m->sinsr)) != q->len) continue;
|
||||
param->statscli64 += q->len;
|
||||
param->nwrites++;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dnsprfirst(struct udploop *u, struct udpsession *ss, unsigned char *query, int i){
|
||||
struct clientparam *param = ss->param;
|
||||
struct dnsquery *q;
|
||||
@ -66,9 +181,6 @@ static int dnsprfirst(struct udploop *u, struct udpsession *ss, unsigned char *q
|
||||
unsigned char c;
|
||||
int res, len;
|
||||
uint32_t ttl, ip = 0;
|
||||
#ifdef _WIN32
|
||||
unsigned long ul = 1;
|
||||
#endif
|
||||
|
||||
if(!(q = calloc(1, sizeof(*q))) || !(q->query = malloc(BUFSIZE + 2))){
|
||||
free(q);
|
||||
@ -78,21 +190,23 @@ static int dnsprfirst(struct udploop *u, struct udpsession *ss, unsigned char *q
|
||||
if(i > BUFSIZE) i = BUFSIZE;
|
||||
memcpy(q->query + 2, query, i);
|
||||
q->len = i;
|
||||
buf = q->query + 2;
|
||||
param->sincl = param->srv->intsa;
|
||||
if(i<=13 || i>1000) return (param->res = 814);
|
||||
/* the query as it came is what goes to a name server; buf is worked on */
|
||||
if(!(buf = malloc(BUFSIZE))) return (param->res = 21);
|
||||
memcpy(buf, query, i);
|
||||
buf[BUFSIZE - 1] = 0;
|
||||
param->sincl = param->srv->intsa;
|
||||
param->operation = DNSRESOLVE;
|
||||
if((res = (*param->srv->authfunc)(param))) return (param->res = res);
|
||||
if((res = (*param->srv->authfunc)(param))) { free(buf); return (param->res = res); }
|
||||
|
||||
if(buf[4]!=0 || buf[5]!=1) return (param->res = 816);
|
||||
if(buf[4]!=0 || buf[5]!=1) { free(buf); return (param->res = 816); }
|
||||
for(len = 12; len<i; len+=(c+1)){
|
||||
c = buf[len];
|
||||
if(!c)break;
|
||||
buf[len] = '.';
|
||||
}
|
||||
if(len > (i-4)) return (param->res = 817);
|
||||
if(!(q->host = strdup((char *)buf+13))) return (param->res = 21);
|
||||
if(len > (i-4)) { free(buf); return (param->res = 817); }
|
||||
if(!(q->host = strdup((char *)buf+13))) { free(buf); return (param->res = 21); }
|
||||
for(s2 = buf + 12; (s1 = (unsigned char *)strchr((char *)s2 + 1, '.')); s2 = s1)*s2 = (unsigned char)((s1 - s2) - 1);
|
||||
*s2 = (len - (int)(s2 - buf)) - 1;
|
||||
|
||||
@ -108,8 +222,10 @@ static int dnsprfirst(struct udploop *u, struct udpsession *ss, unsigned char *q
|
||||
memcpy(q->addr, param->srv->fakeip6, 16);
|
||||
}
|
||||
#endif
|
||||
/* the name cache only: what it does not have the name servers are
|
||||
asked, without waiting here */
|
||||
else if((q->type==0x01 || q->type==0x1c) && !param->srv->s_option){
|
||||
ip = udpresolve((q->type==0x1c)?AF_INET6:AF_INET, (unsigned char *)q->host, q->addr, &q->ttl, param, 0);
|
||||
ip = hashresolv(q->type==0x1c? &dns6_table : &dns_table, (unsigned char *)q->host, q->addr, &q->ttl);
|
||||
}
|
||||
len += 5;
|
||||
|
||||
@ -137,85 +253,73 @@ static int dnsprfirst(struct udploop *u, struct udpsession *ss, unsigned char *q
|
||||
memcpy(buf+len+13,(void *)"3proxy",6);
|
||||
len += 20;
|
||||
q->ip = 1;
|
||||
if(udpanswer(u, param, buf, len) != len) return (param->res = 819);
|
||||
param->res = (udpanswer(u, param, buf, len) == len)? 0 : 819;
|
||||
free(buf);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
q->ip = ip;
|
||||
if(ip || !param->srv->config->numservers){
|
||||
param->res = dnsanswer(u, param, q, buf, len);
|
||||
free(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* on to the first name server */
|
||||
if((param->remsock=param->srv->so._socket(param->sostate, SASOCK(¶m->srv->config->nservers[0].addr), param->srv->config->nservers[0].usetcp? SOCK_STREAM:SOCK_DGRAM, param->srv->config->nservers[0].usetcp?IPPROTO_TCP:IPPROTO_UDP)) == INVALID_SOCKET)
|
||||
return (param->res = 818);
|
||||
memset(¶m->sinsl, 0, sizeof(param->sinsl));
|
||||
*SAFAMILY(¶m->sinsl) = *SAFAMILY(¶m->srv->config->nservers[0].addr);
|
||||
if(bindwithrange(param, param->remsock, ¶m->sinsl, param->extport)) return (param->res = 819);
|
||||
param->sinsr = param->srv->config->nservers[0].addr;
|
||||
if(param->srv->config->nservers[0].usetcp) {
|
||||
unsigned short us;
|
||||
|
||||
if(connectwithpoll(param, param->remsock,(struct sockaddr *)¶m->sinsr,SASIZE(¶m->sinsr),param->srv->timeouts[CONNECT_TO])) return (param->res = 830);
|
||||
buf -= 2;
|
||||
*(unsigned short*)buf = htons(i);
|
||||
i += 2;
|
||||
if(socksend(param, param->remsock, buf, i, param->srv->timeouts[SINGLEBYTE_L]) != i) return (param->res = 820);
|
||||
param->statscli64 += i;
|
||||
param->nwrites++;
|
||||
len = sockrecvfrom(param, param->remsock, NULL, buf, BUFSIZE, param->srv->timeouts[DNS_TO]*1000);
|
||||
if(len <= 13) return (param->res = 821);
|
||||
param->statssrv64 += len;
|
||||
param->nreads++;
|
||||
us = ntohs(*(unsigned short *)buf);
|
||||
if(us > 4096) return (param->res = 833);
|
||||
buf += 2;
|
||||
len -= 2;
|
||||
if(len < us) len += sockgetlinebuf(param, SERVER, buf+len, us - len, 0, param->srv->timeouts[SINGLEBYTE_L]);
|
||||
if(len != us) return (param->res = 832);
|
||||
if(buf[6] || buf[7]){
|
||||
if(udpanswer(u, param, buf, len) != len) return (param->res = 822);
|
||||
return 1;
|
||||
}
|
||||
param->res = dnsanswer(u, param, q, buf, len);
|
||||
return 1;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
ioctlsocket(param->remsock, FIONBIO, &ul);
|
||||
#else
|
||||
fcntl(param->remsock, F_SETFL, O_NONBLOCK | fcntl(param->remsock, F_GETFL));
|
||||
#endif
|
||||
if(param->srv->so._sendto(param->sostate, param->remsock, (char *)buf, i, 0, (struct sockaddr *)¶m->sinsr, SASIZE(¶m->sinsr)) != i)
|
||||
return (param->res = 820);
|
||||
param->statscli64 += i;
|
||||
param->nwrites++;
|
||||
/* its answer is waited for in the loop, as long as DNS_TO */
|
||||
ss->idle = 0;
|
||||
ss->deadline = time(NULL) + param->srv->timeouts[DNS_TO];
|
||||
free(buf);
|
||||
if(dnsforward(u, ss)) return (param->res = 818);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The name server's answer: to the client as it is if it has answers,
|
||||
else as the name not found. */
|
||||
/* The name server's socket: connected, over TCP, so the query is written;
|
||||
an answer, or over TCP some of it. */
|
||||
static int dnsprserver(struct udploop *u, struct udpsession *ss){
|
||||
struct clientparam *param = ss->param;
|
||||
struct dnsquery *q = ss->data;
|
||||
int len;
|
||||
|
||||
if(q->tcp && q->connecting){
|
||||
q->connecting = 0;
|
||||
q->query[0] = (unsigned char)(q->len >> 8);
|
||||
q->query[1] = (unsigned char)(q->len & 0xff);
|
||||
if(param->srv->so._send(param->sostate, param->remsock, (char *)q->query, q->len + 2, 0) != q->len + 2){
|
||||
q->server++;
|
||||
return dnsforward(u, ss)? (param->res = 820) : 0;
|
||||
}
|
||||
param->statscli64 += q->len + 2;
|
||||
param->nwrites++;
|
||||
ss->events = POLLIN;
|
||||
return 0;
|
||||
}
|
||||
if(q->tcp){
|
||||
unsigned short us;
|
||||
|
||||
if(!q->answer && !(q->answer = malloc(4096 + 2))) return (param->res = 21);
|
||||
len = param->srv->so._recv(param->sostate, param->remsock, (char *)q->answer + q->got, 4096 + 2 - q->got, 0);
|
||||
if(len < 0 && (errno == EAGAIN || errno == EINTR)) return 0;
|
||||
if(len <= 0){
|
||||
q->server++;
|
||||
return dnsforward(u, ss)? (param->res = 821) : 0;
|
||||
}
|
||||
q->got += len;
|
||||
if(q->got < 2) return 0;
|
||||
us = (unsigned short)((q->answer[0] << 8) | q->answer[1]);
|
||||
if(us > 4096 || us <= 13) return (param->res = 833);
|
||||
if(q->got < us + 2) return 0;
|
||||
return dnsreply(u, ss, q->answer + 2, us);
|
||||
}
|
||||
len = param->srv->so._recvfrom(param->sostate, param->remsock, (char *)u->buf, UDPBUFSIZE, 0, NULL, NULL);
|
||||
if(len < 0 && (errno == EAGAIN || errno == EINTR)) return 0;
|
||||
if(len <= 13) return (param->res = 821);
|
||||
param->statssrv64 += len;
|
||||
param->nreads++;
|
||||
if(u->buf[6] || u->buf[7]){
|
||||
if(udpanswer(u, param, u->buf, len) != len) return (param->res = 822);
|
||||
param->res = 0;
|
||||
return 1;
|
||||
/* an answer to another query, or none at all */
|
||||
if(len <= 13 || u->buf[0] != q->query[2] || u->buf[1] != q->query[3]) return 0;
|
||||
return dnsreply(u, ss, u->buf, len);
|
||||
}
|
||||
q->ip = 0;
|
||||
param->res = dnsanswer(u, param, q, u->buf, len);
|
||||
return 1;
|
||||
|
||||
/* No answer in DNS_TO: the next name server, if there is one. */
|
||||
static int dnsprtimeout(struct udploop *u, struct udpsession *ss){
|
||||
struct dnsquery *q = ss->data;
|
||||
|
||||
q->server++;
|
||||
if(dnsforward(u, ss)) return (ss->param->res = 821);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dnsprend(struct udploop *u, struct udpsession *ss){
|
||||
@ -233,6 +337,7 @@ static void dnsprend(struct udploop *u, struct udpsession *ss){
|
||||
if(q){
|
||||
free(q->host);
|
||||
free(q->query);
|
||||
free(q->answer);
|
||||
free(q);
|
||||
}
|
||||
ss->data = NULL;
|
||||
@ -244,5 +349,6 @@ const struct udpservice dnsprservice = {
|
||||
NULL,
|
||||
dnsprserver,
|
||||
dnsprend,
|
||||
821
|
||||
821,
|
||||
dnsprtimeout
|
||||
};
|
||||
|
||||
@ -344,7 +344,7 @@ static int udploop(struct srvstart *s){
|
||||
timeo = ss->deadline > now? (int)(ss->deadline - now) * 1000 : 0;
|
||||
if(ss->param->remsock != INVALID_SOCKET){
|
||||
st.fds[nfds].fd = ss->param->remsock;
|
||||
st.fds[nfds].events = POLLIN;
|
||||
st.fds[nfds].events = ss->events? ss->events : POLLIN;
|
||||
st.fds[nfds].revents = 0;
|
||||
st.of[nfds++] = ss;
|
||||
}
|
||||
@ -375,9 +375,12 @@ static int udploop(struct srvstart *s){
|
||||
for(ss = st.head; ss; ss = next){
|
||||
next = ss->next;
|
||||
if(!ss->done && ss->deadline && ss->deadline <= now){
|
||||
if(st.svc->timeout) ss->done = (*st.svc->timeout)(&st.u, ss);
|
||||
else {
|
||||
ss->done = 1;
|
||||
ss->param->res = st.svc->timeoutres;
|
||||
}
|
||||
}
|
||||
if(ss->done){
|
||||
udpremove(&st, ss);
|
||||
udpend(ss);
|
||||
|
||||
@ -252,6 +252,7 @@ struct udpsession {
|
||||
int idle; /* seconds of silence it lasts, 0: deadline fixed */
|
||||
int keyed; /* found by its client's address */
|
||||
int done; /* to be ended, param->res saying how */
|
||||
short events; /* polled for on remsock, 0: POLLIN */
|
||||
void *data; /* the service's own, freed by it in end() */
|
||||
};
|
||||
struct udpservice {
|
||||
@ -269,6 +270,9 @@ struct udpservice {
|
||||
void (*end)(struct udploop *u, struct udpsession *ss);
|
||||
/* param->res of a session whose deadline passed */
|
||||
int timeoutres;
|
||||
/* or, if set, its deadline passed: 0 if it goes on, a new deadline
|
||||
set, nonzero to end it, param->res saying how */
|
||||
int (*timeout)(struct udploop *u, struct udpsession *ss);
|
||||
};
|
||||
struct udploop {
|
||||
struct srvparam *srv;
|
||||
|
||||
@ -109,7 +109,8 @@ const struct udpservice udppmservice = {
|
||||
udppmclient,
|
||||
udppmserver,
|
||||
udppmend,
|
||||
92
|
||||
92,
|
||||
NULL
|
||||
};
|
||||
|
||||
#ifdef WITHMAIN
|
||||
|
||||
@ -24,28 +24,64 @@ def dns_raw(name, qtype, qid=0x3B3B):
|
||||
return q + b"\x00" + struct.pack("!HH", qtype, 1)
|
||||
|
||||
|
||||
def fake_name_server(answer):
|
||||
"""A name server answering every query with one TXT record, the text
|
||||
given; and one which answers nothing."""
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.bind(("127.0.0.1", 0))
|
||||
|
||||
def serve():
|
||||
while True:
|
||||
try:
|
||||
data, peer = sock.recvfrom(65536)
|
||||
except OSError:
|
||||
return
|
||||
if answer is None:
|
||||
continue
|
||||
def dns_reply(data, answer):
|
||||
"""The answer to a query: an A record 10.9.8.7 for an A query, else a TXT
|
||||
record with the text given."""
|
||||
reply = bytearray(data)
|
||||
reply[2] = 0x81
|
||||
reply[3] = 0x80
|
||||
reply[6:8] = struct.pack("!H", 1)
|
||||
pos = 12
|
||||
while data[pos]:
|
||||
pos += data[pos] + 1
|
||||
qtype = struct.unpack("!H", data[pos + 1:pos + 3])[0]
|
||||
if qtype == 1:
|
||||
reply += b"\xc0\x0c" + struct.pack("!HHIH", 1, 1, 60, 4) + bytes([10, 9, 8, 7])
|
||||
else:
|
||||
txt = bytes([len(answer)]) + answer
|
||||
reply += b"\xc0\x0c" + struct.pack("!HHIH", 16, 1, 60, len(txt)) + txt
|
||||
sock.sendto(bytes(reply), peer)
|
||||
return bytes(reply)
|
||||
|
||||
|
||||
def fake_name_server(answer, tcp=False):
|
||||
"""A name server answering every query (see dns_reply), over UDP or
|
||||
TCP; with answer None, one which answers nothing."""
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM if tcp else socket.SOCK_DGRAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.bind(("127.0.0.1", 0))
|
||||
|
||||
def serve_tcp(c):
|
||||
try:
|
||||
c.settimeout(10)
|
||||
head = c.recv(2)
|
||||
n = struct.unpack("!H", head)[0]
|
||||
data = b""
|
||||
while len(data) < n:
|
||||
data += c.recv(n - len(data))
|
||||
reply = dns_reply(data, answer)
|
||||
# in two pieces, as a stream may bring it
|
||||
c.sendall(struct.pack("!H", len(reply)) + reply[:5])
|
||||
time.sleep(0.05)
|
||||
c.sendall(reply[5:])
|
||||
except OSError:
|
||||
pass
|
||||
c.close()
|
||||
|
||||
def serve():
|
||||
while True:
|
||||
try:
|
||||
if tcp:
|
||||
c, _ = sock.accept()
|
||||
threading.Thread(target=serve_tcp, args=(c,), daemon=True).start()
|
||||
continue
|
||||
data, peer = sock.recvfrom(65536)
|
||||
except OSError:
|
||||
return
|
||||
if answer is not None:
|
||||
sock.sendto(dns_reply(data, answer), peer)
|
||||
|
||||
if tcp:
|
||||
sock.listen(8)
|
||||
threading.Thread(target=serve, daemon=True).start()
|
||||
return sock, sock.getsockname()[1]
|
||||
|
||||
@ -201,9 +237,22 @@ def run(t):
|
||||
if got and b"from-the-server" in got else \
|
||||
t.fail("a TXT query is sent on to the name server and its answer relayed", "the TXT", got)
|
||||
|
||||
# an address not in the cache is asked of the name server, and kept
|
||||
got = exchange(s, fwd, dns_raw("a.test", 1))
|
||||
t.ok("an A query not in the cache is answered by the name server") \
|
||||
if got and bytes([10, 9, 8, 7]) in got else \
|
||||
t.fail("an A query not in the cache is answered by the name server", "10.9.8.7", got)
|
||||
good.close()
|
||||
time.sleep(0.2)
|
||||
got = exchange(s, fwd, dns_raw("a.test", 1))
|
||||
t.ok("and then from the name cache, the name server gone") \
|
||||
if got and bytes([10, 9, 8, 7]) in got else \
|
||||
t.fail("and then from the name cache, the name server gone", "10.9.8.7", got)
|
||||
|
||||
t.wait_udp(slow, payload=dns_raw("local.test", 1))
|
||||
pending = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
pending.sendto(dns_raw("mute.test", 16), ("127.0.0.1", slow))
|
||||
# an A query, which the resolver used to wait for in place
|
||||
pending.sendto(dns_raw("mute.test", 1), ("127.0.0.1", slow))
|
||||
start = time.time()
|
||||
other = exchange(s, slow, dns_raw("local.test", 1), timeout=2)
|
||||
t.ok(f"while one query waits for a silent name server another is answered ({time.time() - start:.2f} s)") \
|
||||
@ -213,5 +262,41 @@ def run(t):
|
||||
while time.time() < deadline and " 00821 " not in dns2.output():
|
||||
time.sleep(0.2)
|
||||
t.contains(dns2.output(), " 00821 ", "the waiting one ends at DNS_TO (821)")
|
||||
for x in (s, pending, good, mute):
|
||||
|
||||
# a name server over TCP; and a second one asked when the first is silent
|
||||
tcpns, tport = fake_name_server(b"over-tcp", tcp=True)
|
||||
second, sport = fake_name_server(b"the-second")
|
||||
viatcp, fallback = t.free_port(), t.free_port()
|
||||
t.start("udploop_tcpns", f"""
|
||||
log
|
||||
timeouts 1 5 30 60 180 1800 2 60 15 5 5
|
||||
nserver 127.0.0.1:{tport}/tcp
|
||||
nscache 256
|
||||
nsrecord local.test 10.1.2.3
|
||||
auth iponly
|
||||
allow *
|
||||
dnspr -p{viatcp}
|
||||
""")
|
||||
t.start("udploop_fallback", f"""
|
||||
log
|
||||
timeouts 1 5 30 60 180 1800 2 60 15 5 5
|
||||
nserver 127.0.0.1:{mport}
|
||||
nserver 127.0.0.1:{sport}
|
||||
nscache 256
|
||||
nsrecord local.test 10.1.2.3
|
||||
auth iponly
|
||||
allow *
|
||||
dnspr -p{fallback}
|
||||
""")
|
||||
t.wait_udp(viatcp, payload=dns_raw("local.test", 1))
|
||||
got = exchange(s, viatcp, dns_raw("tcp.test", 16), timeout=4)
|
||||
t.ok("a name server over TCP is asked, its answer in pieces put together") \
|
||||
if got and b"over-tcp" in got else \
|
||||
t.fail("a name server over TCP is asked, its answer in pieces put together", "the TXT", got)
|
||||
t.wait_udp(fallback, payload=dns_raw("local.test", 1))
|
||||
got = exchange(s, fallback, dns_raw("second.test", 16), timeout=6)
|
||||
t.ok("the first name server silent, the second is asked after DNS_TO") \
|
||||
if got and b"the-second" in got else \
|
||||
t.fail("the first name server silent, the second is asked after DNS_TO", "the TXT", got)
|
||||
for x in (s, pending, mute, tcpns, second):
|
||||
x.close()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user