static int doit(void) { unsigned int pos; char header[12]; char qtype[2]; char qclass[2]; if (len >= sizeof buf) goto NOQ; pos = dns_packet_copy(buf,len,0,header,12); if (!pos) goto NOQ; if (header[2] & 128) goto NOQ; if (header[4]) goto NOQ; if (header[5] != 1) goto NOQ; pos = dns_packet_getname(buf,len,pos,&q); if (!pos) goto NOQ; pos = dns_packet_copy(buf,len,pos,qtype,2); if (!pos) goto NOQ; pos = dns_packet_copy(buf,len,pos,qclass,2); if (!pos) goto NOQ; if (!response_query(q,qtype)) goto NOQ; response_id(header); if (byte_equal(qclass,2,DNS_C_IN)) response[2] |= 4; else if (byte_diff(qclass,2,DNS_C_ANY)) goto NOQ; response[3] &= ~128; if (!(header[2] & 1)) response[2] &= ~1; if (header[2] & 126) goto NOTIMP; if (byte_equal(qtype,2,DNS_T_AXFR)) goto NOTIMP; case_lowerb(q,dns_domain_length(q)); if (!respond(q,qtype,ip)) { qlog(ip,port,header,q,qtype," - "); return 0; } qlog(ip,port,header,q,qtype," + "); return 1; NOTIMP: response[3] &= ~15; response[3] |= 4; qlog(ip,port,header,q,qtype," I "); return 1; NOQ: qlog(ip,port,"\0\0","","\0\0"," / "); return 0; }
static int dobytes(unsigned int len) { char buf[20]; if (len > 20) return 0; dpos = dns_packet_copy(data,dlen,dpos,buf,len); if (!dpos) return 0; return response_addbytes(buf,len); }
static int serverwantstcp(const char *buf,unsigned int len) { char out[12]; if (!dns_packet_copy(buf,len,0,out,12)) return 1; if (out[2] & 2) return 1; return 0; }
static int find(char *d,int flagwild) { int r; char ch; struct tai cutoff; char ttd[8]; char ttlstr[4]; char recordloc[2]; double newttl; for (;;) { r = cdb_findnext(&c,d,dns_domain_length(d)); if (r <= 0) return r; dlen = cdb_datalen(&c); if (dlen > sizeof data) return -1; if (cdb_read(&c,data,dlen,cdb_datapos(&c)) == -1) return -1; dpos = dns_packet_copy(data,dlen,0,type,2); if (!dpos) return -1; dpos = dns_packet_copy(data,dlen,dpos,&ch,1); if (!dpos) return -1; if ((ch == '=' + 1) || (ch == '*' + 1)) { --ch; dpos = dns_packet_copy(data,dlen,dpos,recordloc,2); if (!dpos) return -1; if (byte_diff(recordloc,2,clientloc)) continue; } if (flagwild != (ch == '*')) continue; dpos = dns_packet_copy(data,dlen,dpos,ttlstr,4); if (!dpos) return -1; uint32_unpack_big(ttlstr,&ttl); dpos = dns_packet_copy(data,dlen,dpos,ttd,8); if (!dpos) return -1; if (byte_diff(ttd,8,"\0\0\0\0\0\0\0\0")) { tai_unpack(ttd,&cutoff); if (ttl == 0) { if (tai_less(&cutoff,&now)) continue; tai_sub(&cutoff,&cutoff,&now); newttl = tai_approx(&cutoff); if (newttl <= 2.0) newttl = 2.0; if (newttl >= 3600.0) newttl = 3600.0; ttl = newttl; } else if (!tai_less(&cutoff,&now)) continue; } return 1; } }
static int getit(struct dns_result* out, unsigned int i, unsigned int offset, const char* buf, unsigned int len, unsigned int pos, uint16 datalen) { unsigned char header[4]; if (datalen != 4) { errno = EPROTO; return -1; } if (!dns_packet_copy(buf,len,pos,header,4)) { errno = EPROTO; return -1; } memcpy(&out->rr.ip4[i], header, 4); return 0; (void)offset; }
int dns_txt_packet(stralloc *out,const char *buf,unsigned int len) { unsigned int pos; char header[12]; uint16 numanswers; uint16 datalen; char ch; unsigned int txtlen; int i; if (!stralloc_copys(out,"")) return -1; pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return -1; uint16_unpack_big(header + 6,&numanswers); pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1; pos += 4; while (numanswers--) { pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1; pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return -1; uint16_unpack_big(header + 8,&datalen); if (byte_equal(header,2,DNS_T_TXT)) if (byte_equal(header + 2,2,DNS_C_IN)) { if (pos + datalen > len) return -1; txtlen = 0; for (i = 0;i < datalen;++i) { ch = buf[pos + i]; if (!txtlen) txtlen = (unsigned char) ch; else { --txtlen; if (ch < 32) ch = '?'; if (ch > 126) ch = '?'; if (!stralloc_append(out,&ch)) return -1; } } } pos += datalen; } return 0; }
static int serverfailed(const char *buf,unsigned int len) { char out[12]; unsigned int rcode; if (!dns_packet_copy(buf,len,0,out,12)) return 1; rcode = out[3]; rcode &= 15; if (rcode && (rcode != 3)) { errno = error_again; return 1; } return 0; }
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 int irrelevant(const struct dns_transmit *d,const char *buf,unsigned int len) { char out[12]; char *dn; unsigned int pos; pos = dns_packet_copy(buf,len,0,out,12); if (!pos) return 1; if (byte_diff(out,2,d->query + 2)) return 1; if (out[4] != 0) return 1; if (out[5] != 1) return 1; dn = 0; pos = dns_packet_getname(buf,len,pos,&dn); if (!pos) return 1; if (!dns_domain_equal(dn,d->query + 14)) { alloc_free(dn); return 1; } alloc_free(dn); pos = dns_packet_copy(buf,len,pos,out,4); if (!pos) return 1; if (byte_diff(out,2,d->qtype)) return 1; if (byte_diff(out + 2,2,DNS_C_IN)) return 1; return 0; }
static int want(const char *owner,const char type[2]) { unsigned int pos; static char *d; char x[10]; uint16 datalen; pos = dns_packet_skipname(response,response_len,12); if (!pos) return 0; pos += 4; while (pos < response_len) { pos = dns_packet_getname(response,response_len,pos,&d); if (!pos) return 0; pos = dns_packet_copy(response,response_len,pos,x,10); if (!pos) return 0; if (dns_domain_equal(d,owner)) if (byte_equal(type,2,x)) return 0; uint16_unpack_big(x + 8,&datalen); pos += datalen; } return 1; }
static int doit(char *q,char qtype[2]) { unsigned int bpos; unsigned int anpos; unsigned int aupos; unsigned int arpos; char *control; char *wild; int flaggavesoa; int flagfound; int r; int flagns; int flagauthoritative; char x[20]; uint16 u16; char addr[8][4]; int addrnum; uint32 addrttl; int i; anpos = response_len; control = q; for (;;) { flagns = 0; flagauthoritative = 0; cdb_findstart(&c); while (r = find(control,0)) { if (r == -1) return 0; if (byte_equal(type,2,DNS_T_SOA)) flagauthoritative = 1; if (byte_equal(type,2,DNS_T_NS)) flagns = 1; } if (flagns) break; if (!*control) return 0; /* q is not within our bailiwick */ control += *control; control += 1; } if (!flagauthoritative) { response[2] &= ~4; goto AUTHORITY; /* q is in a child zone */ } flaggavesoa = 0; flagfound = 0; wild = q; for (;;) { addrnum = 0; addrttl = 0; cdb_findstart(&c); while (r = find(wild,wild != q)) { if (r == -1) return 0; flagfound = 1; if (flaggavesoa && byte_equal(type,2,DNS_T_SOA)) continue; if (byte_diff(type,2,qtype) && byte_diff(qtype,2,DNS_T_ANY) && byte_diff(type,2,DNS_T_CNAME)) continue; if (byte_equal(type,2,DNS_T_A) && (dlen - dpos == 4)) { addrttl = ttl; i = dns_random(addrnum + 1); if (i < 8) { if ((i < addrnum) && (addrnum < 8)) byte_copy(addr[addrnum],4,addr[i]); byte_copy(addr[i],4,data + dpos); } if (addrnum < 1000000) ++addrnum; continue; } if (!response_rstart(q,type,ttl)) return 0; if (byte_equal(type,2,DNS_T_NS) || byte_equal(type,2,DNS_T_CNAME) || byte_equal(type,2,DNS_T_PTR)) { if (!doname()) return 0; } else if (byte_equal(type,2,DNS_T_MX)) { if (!dobytes(2)) return 0; if (!doname()) return 0; } else if (byte_equal(type,2,DNS_T_SOA)) { if (!doname()) return 0; if (!doname()) return 0; if (!dobytes(20)) return 0; flaggavesoa = 1; } else if (!response_addbytes(data + dpos,dlen - dpos)) return 0; response_rfinish(RESPONSE_ANSWER); } for (i = 0;i < addrnum;++i) if (i < 8) { if (!response_rstart(q,DNS_T_A,addrttl)) return 0; if (!response_addbytes(addr[i],4)) return 0; response_rfinish(RESPONSE_ANSWER); } if (flagfound) break; if (wild == control) break; if (!*wild) break; /* impossible */ wild += *wild; wild += 1; } if (!flagfound) response_nxdomain(); AUTHORITY: aupos = response_len; if (flagauthoritative && (aupos == anpos)) { cdb_findstart(&c); while (r = find(control,0)) { if (r == -1) return 0; if (byte_equal(type,2,DNS_T_SOA)) { if (!response_rstart(control,DNS_T_SOA,ttl)) return 0; if (!doname()) return 0; if (!doname()) return 0; if (!dobytes(20)) return 0; response_rfinish(RESPONSE_AUTHORITY); break; } } } else if (want(control,DNS_T_NS)) { cdb_findstart(&c); while (r = find(control,0)) { if (r == -1) return 0; if (byte_equal(type,2,DNS_T_NS)) { if (!response_rstart(control,DNS_T_NS,ttl)) return 0; if (!doname()) return 0; response_rfinish(RESPONSE_AUTHORITY); } } } arpos = response_len; bpos = anpos; while (bpos < arpos) { bpos = dns_packet_skipname(response,arpos,bpos); if (!bpos) return 0; bpos = dns_packet_copy(response,arpos,bpos,x,10); if (!bpos) return 0; if (byte_equal(x,2,DNS_T_NS) || byte_equal(x,2,DNS_T_MX)) { if (byte_equal(x,2,DNS_T_NS)) { if (!dns_packet_getname(response,arpos,bpos,&d1)) return 0; } else if (!dns_packet_getname(response,arpos,bpos + 2,&d1)) return 0; case_lowerb(d1,dns_domain_length(d1)); if (want(d1,DNS_T_A)) { cdb_findstart(&c); while (r = find(d1,0)) { if (r == -1) return 0; if (byte_equal(type,2,DNS_T_A)) { if (!response_rstart(d1,DNS_T_A,ttl)) return 0; if (!dobytes(4)) return 0; response_rfinish(RESPONSE_ADDITIONAL); } } } } uint16_unpack_big(x + 8,&u16); bpos += u16; } if (flagauthoritative && (response_len > 512)) { byte_zero(response + RESPONSE_ADDITIONAL,2); response_len = arpos; if (response_len > 512) { byte_zero(response + RESPONSE_AUTHORITY,2); response_len = aupos; } } return 1; }
static int doit (void) { char qtype[2]; char qclass[2]; char header[12]; unsigned int pos = 0; if ((unsigned)len >= sizeof buf) goto NOQ; if (!(pos = dns_packet_copy (buf, len, 0, header, 12))) goto NOQ; if (header[2] & 128) goto NOQ; if (header[4]) goto NOQ; if (header[5] != 1) goto NOQ; if (!(pos = dns_packet_getname (buf, len, pos, &q))) goto NOQ; if (!(pos = dns_packet_copy (buf, len, pos, qtype, 2))) goto NOQ; if (!(pos = dns_packet_copy (buf, len, pos, qclass, 2))) goto NOQ; if (!response_query (q, qtype, qclass)) goto NOQ; response_id (header); qnum++; if (byte_equal (qclass, 2, DNS_C_IN)) response[2] |= 4; else if (byte_diff (qclass, 2, DNS_C_ANY)) goto WEIRDCLASS; response[3] &= ~128; if (!(header[2] & 1)) response[2] &= ~1; if (header[2] & 126) goto NOTIMP; if (byte_equal (qtype, 2, DNS_T_AXFR)) goto NOTIMP; case_lowerb (q, dns_domain_length (q)); if (!respond (q, qtype, ip)) { log_query (qnum, ip, port, header, q, qtype); return 0; } log_query (qnum, ip, port, header, q, qtype); return 1; NOTIMP: response[3] &= ~15; response[3] |= 4; log_query (qnum, ip, port, header, q, qtype); return 1; WEIRDCLASS: response[3] &= ~15; response[3] |= 1; log_query (qnum, ip, port, header, q, qtype); return 1; NOQ: log_query (qnum, ip, port, "\0\0", "", "\0\0"); return 0; }
int main (int argc, char *argv[]) { int n = 0; time_t t = 0; struct sigaction sa; char qtype[2]; char qclass[2]; char header[12]; const char *x = NULL; unsigned int pos = 0; unsigned long long qnum = 0; sa.sa_handler = handle_term; sigaction (SIGINT, &sa, NULL); sigaction (SIGTERM, &sa, NULL); sa.sa_handler = SIG_IGN; sigaction (SIGPIPE, &sa, NULL); prog = strdup ((x = strrchr (argv[0], '/')) != NULL ? x + 1 : argv[0]); n = check_option (argc, argv); argc -= n; argv += n; if (mode & DAEMON) /* redirect stderr to a log file */ redirect_to_log (logfile, STDERR_FILENO); time (&t); memset (seed, 0, sizeof (seed)); strftime (seed, sizeof (seed), "%b-%d %Y %T %Z", localtime (&t)); warnx ("version %s: starting %s\n", VERSION, seed); set_timezone (); if (debug_level) warnx ("TIMEZONE: %s", env_get ("TZ")); read_conf (cfgfile); if (!debug_level) if ((x = env_get ("DEBUG_LEVEL"))) debug_level = atol (x); warnx ("DEBUG_LEVEL set to `%d'", debug_level); dns_random_init (seed); axfr = env_get ("AXFR"); if (debug_level) warnx ("AXFR set to `%s'", axfr); x = env_get ("TCPREMOTEIP"); if (debug_level) warnx ("TCPREMOTEIP set to `%s'", x); if (x) ip4_scan (x, ip); else byte_zero (ip, 4); x = env_get ("TCPREMOTEPORT"); if (debug_level) warnx ("TCPREMOTEPORT set to `%s'", x); if (!x) x = "0"; scan_ulong (x, &port); droproot (); for (;;) { netread (tcpheader, 2); uint16_unpack_big (tcpheader, &len); if (len > 512) errx (-1, "excessively large request"); netread (buf, len); pos = dns_packet_copy (buf, len, 0, header, 12); if (!pos) errx (-1, "truncated request"); if (header[2] & 254) errx (-1, "bogus query"); if (header[4] || (header[5] != 1)) errx (-1, "bogus query"); pos = dns_packet_getname (buf, len, pos, &zone); if (!pos) errx (-1, "truncated request"); zonelen = dns_domain_length (zone); pos = dns_packet_copy (buf, len, pos, qtype, 2); if (!pos) errx (-1, "truncated request"); pos = dns_packet_copy (buf, len, pos, qclass, 2); if (!pos) errx (-1, "truncated request"); if (byte_diff(qclass, 2, DNS_C_IN) && byte_diff(qclass, 2, DNS_C_ANY)) errx (-1, "bogus query: bad class"); log_query (++qnum, ip, port, header, zone, qtype); if (byte_equal(qtype,2,DNS_T_AXFR)) { case_lowerb (zone, zonelen); fdcdb = open_read ("data.cdb"); if (fdcdb == -1) errx (-1, "could not read from file `data.cdb'"); doaxfr (header); close (fdcdb); } else { if (!response_query (zone, qtype, qclass)) err (-1, "could not allocate enough memory"); response[2] |= 4; case_lowerb (zone, zonelen); response_id (header); response[3] &= ~128; if (!(header[2] & 1)) response[2] &= ~1; if (!respond (zone, qtype, ip)) errx (-1, "could not find information in file `data.cdb'"); print (response, response_len); } } }
unsigned int printrecord_cat(stralloc *out,char *buf,unsigned int len,unsigned int pos,char *q,char qtype[2]) { char *x; char misc[20]; uint16 datalen; uint16 u16; uint32 u32; unsigned int newpos; int i; unsigned char ch; pos = dns_packet_getname(buf,len,pos,&d); if (!pos) return 0; pos = dns_packet_copy(buf,len,pos,misc,10); if (!pos) return 0; uint16_unpack_big(misc + 8,&datalen); newpos = pos + datalen; if (q) { if (!dns_domain_equal(d,q)) return newpos; if (byte_diff(qtype,2,misc) && byte_diff(qtype,2,DNS_T_ANY)) return newpos; } if (!dns_domain_todot_cat(out,d)) return 0; if (!stralloc_cats(out," ")) return 0; uint32_unpack_big(misc + 4,&u32); if (!stralloc_catulong0(out,u32,0)) return 0; if (byte_diff(misc + 2,2,DNS_C_IN)) { if (!stralloc_cats(out," weird class\n")) return 0; return newpos; } x = 0; if (byte_equal(misc,2,DNS_T_NS)) x = " NS "; if (byte_equal(misc,2,DNS_T_PTR)) x = " PTR "; if (byte_equal(misc,2,DNS_T_CNAME)) x = " CNAME "; if (x) { pos = dns_packet_getname(buf,len,pos,&d); if (!pos) return 0; if (!stralloc_cats(out,x)) return 0; if (!dns_domain_todot_cat(out,d)) return 0; } else if (byte_equal(misc,2,DNS_T_MX)) { if (!stralloc_cats(out," MX ")) return 0; pos = dns_packet_copy(buf,len,pos,misc,2); if (!pos) return 0; pos = dns_packet_getname(buf,len,pos,&d); if (!pos) return 0; uint16_unpack_big(misc,&u16); if (!stralloc_catulong0(out,u16,0)) return 0; if (!stralloc_cats(out," ")) return 0; if (!dns_domain_todot_cat(out,d)) return 0; } else if (byte_equal(misc,2,DNS_T_SOA)) { if (!stralloc_cats(out," SOA ")) return 0; pos = dns_packet_getname(buf,len,pos,&d); if (!pos) return 0; if (!dns_domain_todot_cat(out,d)) return 0; if (!stralloc_cats(out," ")) return 0; pos = dns_packet_getname(buf,len,pos,&d); if (!pos) return 0; if (!dns_domain_todot_cat(out,d)) return 0; pos = dns_packet_copy(buf,len,pos,misc,20); if (!pos) return 0; for (i = 0;i < 5;++i) { if (!stralloc_cats(out," ")) return 0; uint32_unpack_big(misc + 4 * i,&u32); if (!stralloc_catulong0(out,u32,0)) return 0; } } else if (byte_equal(misc,2,DNS_T_A)) { if (datalen != 4) { errno = error_proto; return 0; } if (!stralloc_cats(out," A ")) return 0; pos = dns_packet_copy(buf,len,pos,misc,4); if (!pos) return 0; for (i = 0;i < 4;++i) { ch = misc[i]; if (i) if (!stralloc_cats(out,".")) return 0; if (!stralloc_catulong0(out,ch,0)) return 0; } } else if (byte_equal(misc,2,DNS_T_AAAA)) { char ip6str[IP6_FMT]; int stringlen; if (datalen != 16) { errno = error_proto; return 0; } if (!stralloc_cats(out," AAAA ")) return 0; pos = dns_packet_copy(buf,len,pos,misc,16); if (!pos) return 0; stringlen=ip6_fmt(ip6str,misc); if (!stralloc_cats(out,ip6str)) return 0; } else { if (!stralloc_cats(out," ")) return 0; uint16_unpack_big(misc,&u16); if (!stralloc_catulong0(out,u16,0)) return 0; if (!stralloc_cats(out," ")) return 0; while (datalen--) { pos = dns_packet_copy(buf,len,pos,misc,1); if (!pos) return 0; if ((misc[0] >= 33) && (misc[0] <= 126) && (misc[0] != '\\')) { if (!stralloc_catb(out,misc,1)) return 0; } else { ch = misc[0]; misc[3] = '0' + (7 & ch); ch >>= 3; misc[2] = '0' + (7 & ch); ch >>= 3; misc[1] = '0' + (7 & ch); misc[0] = '\\'; if (!stralloc_catb(out,misc,4)) return 0; } } } if (!stralloc_cats(out,"\n")) return 0; if (pos != newpos) { errno = error_proto; return 0; } return newpos; }
int main() { unsigned int pos; char header[12]; char qtype[2]; char qclass[2]; const char *x; droproot(FATAL); dns_random_init(seed); axfr = env_get("AXFR"); x = env_get("TCPREMOTEIP"); if (x && ip6_scan(x,ip)) ; else byte_zero(ip,16); x = env_get("TCPREMOTEPORT"); if (!x) x = "0"; scan_ulong(x,&port); for (;;) { netread(tcpheader,2); uint16_unpack_big(tcpheader,&len); if (len > 512) strerr_die2x(111,FATAL,"excessively large request"); netread(buf,len); pos = dns_packet_copy(buf,len,0,header,12); if (!pos) die_truncated(); if (header[2] & 254) strerr_die2x(111,FATAL,"bogus query"); if (header[4] || (header[5] != 1)) strerr_die2x(111,FATAL,"bogus query"); pos = dns_packet_getname(buf,len,pos,&zone); if (!pos) die_truncated(); zonelen = dns_domain_length(zone); pos = dns_packet_copy(buf,len,pos,qtype,2); if (!pos) die_truncated(); pos = dns_packet_copy(buf,len,pos,qclass,2); if (!pos) die_truncated(); if (byte_diff(qclass,2,DNS_C_IN) && byte_diff(qclass,2,DNS_C_ANY)) strerr_die2x(111,FATAL,"bogus query: bad class"); pos = check_edns0(header, buf, len, pos); if (!pos) die_truncated(); qlog(ip,port,header,zone,qtype," "); if (byte_equal(qtype,2,DNS_T_AXFR)) { case_lowerb(zone,zonelen); fdcdb = open_read("data.cdb"); if (fdcdb == -1) die_cdbread(); doaxfr(header); close(fdcdb); } else { if (!response_query(zone,qtype,qclass)) nomem(); response[2] |= 4; case_lowerb(zone,zonelen); response_id(header); response[3] &= ~128; if (!(header[2] & 1)) response[2] &= ~1; if (!respond(zone,qtype,ip)) die_outside(); print(response,response_len); } } }
void copy(char *buf,unsigned int len) { dpos = dns_packet_copy(data,dlen,dpos,buf,len); if (!dpos) die_cdbread(); }