From 0e5b2871ca6456b01d4bf037a6e68badf1ff1a41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henryk=20Pl=C3=B6tz?= Date: Fri, 3 Oct 2014 19:58:52 +0200 Subject: Initial commit of djbdns-1.05.tar.gz Source was http://cr.yp.to/djbdns/djbdns-1.05.tar.gz, SHA1 2efdb3a039d0c548f40936aa9cb30829e0ce8c3d --- dnscache.c | 447 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 447 insertions(+) create mode 100644 dnscache.c (limited to 'dnscache.c') diff --git a/dnscache.c b/dnscache.c new file mode 100644 index 0000000..8c899a3 --- /dev/null +++ b/dnscache.c @@ -0,0 +1,447 @@ +#include +#include "env.h" +#include "exit.h" +#include "scan.h" +#include "strerr.h" +#include "error.h" +#include "ip4.h" +#include "uint16.h" +#include "uint64.h" +#include "socket.h" +#include "dns.h" +#include "taia.h" +#include "byte.h" +#include "roots.h" +#include "fmt.h" +#include "iopause.h" +#include "query.h" +#include "alloc.h" +#include "response.h" +#include "cache.h" +#include "ndelay.h" +#include "log.h" +#include "okclient.h" +#include "droproot.h" + +static int packetquery(char *buf,unsigned int len,char **q,char qtype[2],char qclass[2],char id[2]) +{ + unsigned int pos; + char header[12]; + + errno = error_proto; + pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return 0; + if (header[2] & 128) return 0; /* must not respond to responses */ + if (!(header[2] & 1)) return 0; /* do not respond to non-recursive queries */ + if (header[2] & 120) return 0; + if (header[2] & 2) return 0; + if (byte_diff(header + 4,2,"\0\1")) return 0; + + pos = dns_packet_getname(buf,len,pos,q); if (!pos) return 0; + pos = dns_packet_copy(buf,len,pos,qtype,2); if (!pos) return 0; + pos = dns_packet_copy(buf,len,pos,qclass,2); if (!pos) return 0; + if (byte_diff(qclass,2,DNS_C_IN) && byte_diff(qclass,2,DNS_C_ANY)) return 0; + + byte_copy(id,2,header); + return 1; +} + + +static char myipoutgoing[4]; +static char myipincoming[4]; +static char buf[1024]; +uint64 numqueries = 0; + + +static int udp53; + +#define MAXUDP 200 +static struct udpclient { + struct query q; + struct taia start; + uint64 active; /* query number, if active; otherwise 0 */ + iopause_fd *io; + char ip[4]; + uint16 port; + char id[2]; +} u[MAXUDP]; +int uactive = 0; + +void u_drop(int j) +{ + if (!u[j].active) return; + log_querydrop(&u[j].active); + u[j].active = 0; --uactive; +} + +void u_respond(int j) +{ + if (!u[j].active) return; + response_id(u[j].id); + if (response_len > 512) response_tc(); + socket_send4(udp53,response,response_len,u[j].ip,u[j].port); + log_querydone(&u[j].active,response_len); + u[j].active = 0; --uactive; +} + +void u_new(void) +{ + int j; + int i; + struct udpclient *x; + int len; + static char *q = 0; + char qtype[2]; + char qclass[2]; + + for (j = 0;j < MAXUDP;++j) + if (!u[j].active) + break; + + if (j >= MAXUDP) { + j = 0; + for (i = 1;i < MAXUDP;++i) + if (taia_less(&u[i].start,&u[j].start)) + j = i; + errno = error_timeout; + u_drop(j); + } + + x = u + j; + taia_now(&x->start); + + len = socket_recv4(udp53,buf,sizeof buf,x->ip,&x->port); + if (len == -1) return; + if (len >= sizeof buf) return; + if (x->port < 1024) if (x->port != 53) return; + if (!okclient(x->ip)) return; + + if (!packetquery(buf,len,&q,qtype,qclass,x->id)) return; + + x->active = ++numqueries; ++uactive; + log_query(&x->active,x->ip,x->port,x->id,q,qtype); + switch(query_start(&x->q,q,qtype,qclass,myipoutgoing)) { + case -1: + u_drop(j); + return; + case 1: + u_respond(j); + } +} + + +static int tcp53; + +#define MAXTCP 20 +struct tcpclient { + struct query q; + struct taia start; + struct taia timeout; + uint64 active; /* query number or 1, if active; otherwise 0 */ + iopause_fd *io; + char ip[4]; /* send response to this address */ + uint16 port; /* send response to this port */ + char id[2]; + int tcp; /* open TCP socket, if active */ + int state; + char *buf; /* 0, or dynamically allocated of length len */ + unsigned int len; + unsigned int pos; +} t[MAXTCP]; +int tactive = 0; + +/* +state 1: buf 0; normal state at beginning of TCP connection +state 2: buf 0; have read 1 byte of query packet length into len +state 3: buf allocated; have read pos bytes of buf +state 0: buf 0; handling query in q +state -1: buf allocated; have written pos bytes +*/ + +void t_free(int j) +{ + if (!t[j].buf) return; + alloc_free(t[j].buf); + t[j].buf = 0; +} + +void t_timeout(int j) +{ + struct taia now; + if (!t[j].active) return; + taia_now(&now); + taia_uint(&t[j].timeout,10); + taia_add(&t[j].timeout,&t[j].timeout,&now); +} + +void t_close(int j) +{ + if (!t[j].active) return; + t_free(j); + log_tcpclose(t[j].ip,t[j].port); + close(t[j].tcp); + t[j].active = 0; --tactive; +} + +void t_drop(int j) +{ + log_querydrop(&t[j].active); + errno = error_pipe; + t_close(j); +} + +void t_respond(int j) +{ + if (!t[j].active) return; + log_querydone(&t[j].active,response_len); + response_id(t[j].id); + t[j].len = response_len + 2; + t_free(j); + t[j].buf = alloc(response_len + 2); + if (!t[j].buf) { t_close(j); return; } + uint16_pack_big(t[j].buf,response_len); + byte_copy(t[j].buf + 2,response_len,response); + t[j].pos = 0; + t[j].state = -1; +} + +void t_rw(int j) +{ + struct tcpclient *x; + char ch; + static char *q = 0; + char qtype[2]; + char qclass[2]; + int r; + + x = t + j; + if (x->state == -1) { + r = write(x->tcp,x->buf + x->pos,x->len - x->pos); + if (r <= 0) { t_close(j); return; } + x->pos += r; + if (x->pos == x->len) { + t_free(j); + x->state = 1; /* could drop connection immediately */ + } + return; + } + + r = read(x->tcp,&ch,1); + if (r == 0) { errno = error_pipe; t_close(j); return; } + if (r < 0) { t_close(j); return; } + + if (x->state == 1) { + x->len = (unsigned char) ch; + x->len <<= 8; + x->state = 2; + return; + } + if (x->state == 2) { + x->len += (unsigned char) ch; + if (!x->len) { errno = error_proto; t_close(j); return; } + x->buf = alloc(x->len); + if (!x->buf) { t_close(j); return; } + x->pos = 0; + x->state = 3; + return; + } + + if (x->state != 3) return; /* impossible */ + + x->buf[x->pos++] = ch; + if (x->pos < x->len) return; + + if (!packetquery(x->buf,x->len,&q,qtype,qclass,x->id)) { t_close(j); return; } + + x->active = ++numqueries; + log_query(&x->active,x->ip,x->port,x->id,q,qtype); + switch(query_start(&x->q,q,qtype,qclass,myipoutgoing)) { + case -1: + t_drop(j); + return; + case 1: + t_respond(j); + return; + } + t_free(j); + x->state = 0; +} + +void t_new(void) +{ + int i; + int j; + struct tcpclient *x; + + for (j = 0;j < MAXTCP;++j) + if (!t[j].active) + break; + + if (j >= MAXTCP) { + j = 0; + for (i = 1;i < MAXTCP;++i) + if (taia_less(&t[i].start,&t[j].start)) + j = i; + errno = error_timeout; + if (t[j].state == 0) + t_drop(j); + else + t_close(j); + } + + x = t + j; + taia_now(&x->start); + + x->tcp = socket_accept4(tcp53,x->ip,&x->port); + if (x->tcp == -1) return; + if (x->port < 1024) if (x->port != 53) { close(x->tcp); return; } + if (!okclient(x->ip)) { close(x->tcp); return; } + if (ndelay_on(x->tcp) == -1) { close(x->tcp); return; } /* Linux bug */ + + x->active = 1; ++tactive; + x->state = 1; + t_timeout(j); + + log_tcpopen(x->ip,x->port); +} + + +iopause_fd io[3 + MAXUDP + MAXTCP]; +iopause_fd *udp53io; +iopause_fd *tcp53io; + +static void doit(void) +{ + int j; + struct taia deadline; + struct taia stamp; + int iolen; + int r; + + for (;;) { + taia_now(&stamp); + taia_uint(&deadline,120); + taia_add(&deadline,&deadline,&stamp); + + iolen = 0; + + udp53io = io + iolen++; + udp53io->fd = udp53; + udp53io->events = IOPAUSE_READ; + + tcp53io = io + iolen++; + tcp53io->fd = tcp53; + tcp53io->events = IOPAUSE_READ; + + for (j = 0;j < MAXUDP;++j) + if (u[j].active) { + u[j].io = io + iolen++; + query_io(&u[j].q,u[j].io,&deadline); + } + for (j = 0;j < MAXTCP;++j) + if (t[j].active) { + t[j].io = io + iolen++; + if (t[j].state == 0) + query_io(&t[j].q,t[j].io,&deadline); + else { + if (taia_less(&t[j].timeout,&deadline)) deadline = t[j].timeout; + t[j].io->fd = t[j].tcp; + t[j].io->events = (t[j].state > 0) ? IOPAUSE_READ : IOPAUSE_WRITE; + } + } + + iopause(io,iolen,&deadline,&stamp); + + for (j = 0;j < MAXUDP;++j) + if (u[j].active) { + r = query_get(&u[j].q,u[j].io,&stamp); + if (r == -1) u_drop(j); + if (r == 1) u_respond(j); + } + + for (j = 0;j < MAXTCP;++j) + if (t[j].active) { + if (t[j].io->revents) + t_timeout(j); + if (t[j].state == 0) { + r = query_get(&t[j].q,t[j].io,&stamp); + if (r == -1) t_drop(j); + if (r == 1) t_respond(j); + } + else + if (t[j].io->revents || taia_less(&t[j].timeout,&stamp)) + t_rw(j); + } + + if (udp53io) + if (udp53io->revents) + u_new(); + + if (tcp53io) + if (tcp53io->revents) + t_new(); + } +} + +#define FATAL "dnscache: fatal: " + +char seed[128]; + +int main() +{ + char *x; + unsigned long cachesize; + + x = env_get("IP"); + if (!x) + strerr_die2x(111,FATAL,"$IP not set"); + if (!ip4_scan(x,myipincoming)) + strerr_die3x(111,FATAL,"unable to parse IP address ",x); + + udp53 = socket_udp(); + if (udp53 == -1) + strerr_die2sys(111,FATAL,"unable to create UDP socket: "); + if (socket_bind4_reuse(udp53,myipincoming,53) == -1) + strerr_die2sys(111,FATAL,"unable to bind UDP socket: "); + + tcp53 = socket_tcp(); + if (tcp53 == -1) + strerr_die2sys(111,FATAL,"unable to create TCP socket: "); + if (socket_bind4_reuse(tcp53,myipincoming,53) == -1) + strerr_die2sys(111,FATAL,"unable to bind TCP socket: "); + + droproot(FATAL); + + socket_tryreservein(udp53,131072); + + byte_zero(seed,sizeof seed); + read(0,seed,sizeof seed); + dns_random_init(seed); + close(0); + + x = env_get("IPSEND"); + if (!x) + strerr_die2x(111,FATAL,"$IPSEND not set"); + if (!ip4_scan(x,myipoutgoing)) + strerr_die3x(111,FATAL,"unable to parse IP address ",x); + + x = env_get("CACHESIZE"); + if (!x) + strerr_die2x(111,FATAL,"$CACHESIZE not set"); + scan_ulong(x,&cachesize); + if (!cache_init(cachesize)) + strerr_die3x(111,FATAL,"not enough memory for cache of size ",x); + + if (env_get("HIDETTL")) + response_hidettl(); + if (env_get("FORWARDONLY")) + query_forwardonly(); + + if (!roots_init()) + strerr_die2sys(111,FATAL,"unable to read servers: "); + + if (socket_listen(tcp53,20) == -1) + strerr_die2sys(111,FATAL,"unable to listen on TCP socket: "); + + log_startup(); + doit(); +} -- cgit v1.2.3