release
[hcoop/zz_old/debian/djbdns.git] / axfrdns.c
1 #include <unistd.h>
2 #include "droproot.h"
3 #include "exit.h"
4 #include "env.h"
5 #include "uint32.h"
6 #include "uint16.h"
7 #include "ip4.h"
8 #include "tai.h"
9 #include "buffer.h"
10 #include "timeoutread.h"
11 #include "timeoutwrite.h"
12 #include "open.h"
13 #include "seek.h"
14 #include "cdb.h"
15 #include "stralloc.h"
16 #include "strerr.h"
17 #include "str.h"
18 #include "byte.h"
19 #include "case.h"
20 #include "dns.h"
21 #include "scan.h"
22 #include "qlog.h"
23 #include "response.h"
24
25 extern int respond(char *,char *,char *);
26
27 #define FATAL "axfrdns: fatal: "
28
29 void nomem()
30 {
31 strerr_die2x(111,FATAL,"out of memory");
32 }
33 void die_truncated()
34 {
35 strerr_die2x(111,FATAL,"truncated request");
36 }
37 void die_netwrite()
38 {
39 strerr_die2sys(111,FATAL,"unable to write to network: ");
40 }
41 void die_netread()
42 {
43 strerr_die2sys(111,FATAL,"unable to read from network: ");
44 }
45 void die_outside()
46 {
47 strerr_die2x(111,FATAL,"unable to locate information in data.cdb");
48 }
49 void die_cdbread()
50 {
51 strerr_die2sys(111,FATAL,"unable to read data.cdb: ");
52 }
53 void die_cdbformat()
54 {
55 strerr_die3x(111,FATAL,"unable to read data.cdb: ","format error");
56 }
57
58 int safewrite(int fd,char *buf,unsigned int len)
59 {
60 int w;
61
62 w = timeoutwrite(60,fd,buf,len);
63 if (w <= 0) die_netwrite();
64 return w;
65 }
66
67 char netwritespace[1024];
68 buffer netwrite = BUFFER_INIT(safewrite,1,netwritespace,sizeof netwritespace);
69
70 void print(char *buf,unsigned int len)
71 {
72 char tcpheader[2];
73 uint16_pack_big(tcpheader,len);
74 buffer_put(&netwrite,tcpheader,2);
75 buffer_put(&netwrite,buf,len);
76 buffer_flush(&netwrite);
77 }
78
79 char *axfr;
80 static char *axfrok;
81
82 void axfrcheck(char *q)
83 {
84 int i;
85 int j;
86
87 if (!axfr) return;
88
89 i = j = 0;
90 for (;;) {
91 if (!axfr[i] || (axfr[i] == '/')) {
92 if (i > j) {
93 if (!dns_domain_fromdot(&axfrok,axfr + j,i - j)) nomem();
94 if (dns_domain_equal(q,axfrok)) return;
95 }
96 j = i + 1;
97 }
98 if (!axfr[i]) break;
99 ++i;
100 }
101
102 strerr_die2x(111,FATAL,"disallowed zone transfer request");
103 }
104
105 static char *zone;
106 unsigned int zonelen;
107 char typeclass[4];
108
109 int fdcdb;
110 buffer bcdb;
111 char bcdbspace[1024];
112
113 void get(char *buf,unsigned int len)
114 {
115 int r;
116
117 while (len > 0) {
118 r = buffer_get(&bcdb,buf,len);
119 if (r < 0) die_cdbread();
120 if (!r) die_cdbformat();
121 buf += r;
122 len -= r;
123 }
124 }
125
126 char ip[4];
127 unsigned long port;
128 char clientloc[2];
129
130 struct tai now;
131 char data[32767];
132 uint32 dlen;
133 uint32 dpos;
134
135 void copy(char *buf,unsigned int len)
136 {
137 dpos = dns_packet_copy(data,dlen,dpos,buf,len);
138 if (!dpos) die_cdbread();
139 }
140
141 void doname(stralloc *sa)
142 {
143 static char *d;
144 dpos = dns_packet_getname(data,dlen,dpos,&d);
145 if (!dpos) die_cdbread();
146 if (!stralloc_catb(sa,d,dns_domain_length(d))) nomem();
147 }
148
149 int build(stralloc *sa,char *q,int flagsoa,char id[2])
150 {
151 unsigned int rdatapos;
152 char misc[20];
153 char type[2];
154 char recordloc[2];
155 char ttl[4];
156 char ttd[8];
157 struct tai cutoff;
158
159 dpos = 0;
160 copy(type,2);
161 if (flagsoa) if (byte_diff(type,2,DNS_T_SOA)) return 0;
162 if (!flagsoa) if (byte_equal(type,2,DNS_T_SOA)) return 0;
163
164 if (!stralloc_copyb(sa,id,2)) nomem();
165 if (!stralloc_catb(sa,"\204\000\0\0\0\1\0\0\0\0",10)) nomem();
166 copy(misc,1);
167 if ((misc[0] == '=' + 1) || (misc[0] == '*' + 1)) {
168 --misc[0];
169 copy(recordloc,2);
170 if (byte_diff(recordloc,2,clientloc)) return 0;
171 }
172 if (misc[0] == '*') {
173 if (flagsoa) return 0;
174 if (!stralloc_catb(sa,"\1*",2)) nomem();
175 }
176 if (!stralloc_catb(sa,q,dns_domain_length(q))) nomem();
177 if (!stralloc_catb(sa,type,2)) nomem();
178
179 copy(ttl,4);
180 copy(ttd,8);
181 if (byte_diff(ttd,8,"\0\0\0\0\0\0\0\0")) {
182 tai_unpack(ttd,&cutoff);
183 if (byte_equal(ttl,4,"\0\0\0\0")) {
184 if (tai_less(&cutoff,&now)) return 0;
185 uint32_pack_big(ttl,2);
186 }
187 else
188 if (!tai_less(&cutoff,&now)) return 0;
189 }
190
191 if (!stralloc_catb(sa,DNS_C_IN,2)) nomem();
192 if (!stralloc_catb(sa,ttl,4)) nomem();
193 if (!stralloc_catb(sa,"\0\0",2)) nomem();
194 rdatapos = sa->len;
195
196 if (byte_equal(type,2,DNS_T_SOA)) {
197 doname(sa);
198 doname(sa);
199 copy(misc,20);
200 if (!stralloc_catb(sa,misc,20)) nomem();
201 }
202 else if (byte_equal(type,2,DNS_T_NS) || byte_equal(type,2,DNS_T_PTR) || byte_equal(type,2,DNS_T_CNAME)) {
203 doname(sa);
204 }
205 else if (byte_equal(type,2,DNS_T_MX)) {
206 copy(misc,2);
207 if (!stralloc_catb(sa,misc,2)) nomem();
208 doname(sa);
209 }
210 else
211 if (!stralloc_catb(sa,data + dpos,dlen - dpos)) nomem();
212
213 if (sa->len > 65535) die_cdbformat();
214 uint16_pack_big(sa->s + rdatapos - 2,sa->len - rdatapos);
215 return 1;
216 }
217
218 static struct cdb c;
219 static char *q;
220 static stralloc soa;
221 static stralloc message;
222
223 void doaxfr(char id[2])
224 {
225 char key[512];
226 uint32 klen;
227 char num[4];
228 uint32 eod;
229 uint32 pos;
230 int r;
231
232 axfrcheck(zone);
233
234 tai_now(&now);
235 cdb_init(&c,fdcdb);
236
237 byte_zero(clientloc,2);
238 key[0] = 0;
239 key[1] = '%';
240 byte_copy(key + 2,4,ip);
241 r = cdb_find(&c,key,6);
242 if (!r) r = cdb_find(&c,key,5);
243 if (!r) r = cdb_find(&c,key,4);
244 if (!r) r = cdb_find(&c,key,3);
245 if (!r) r = cdb_find(&c,key,2);
246 if (r == -1) die_cdbread();
247 if (r && (cdb_datalen(&c) == 2))
248 if (cdb_read(&c,clientloc,2,cdb_datapos(&c)) == -1) die_cdbread();
249
250 cdb_findstart(&c);
251 for (;;) {
252 r = cdb_findnext(&c,zone,zonelen);
253 if (r == -1) die_cdbread();
254 if (!r) die_outside();
255 dlen = cdb_datalen(&c);
256 if (dlen > sizeof data) die_cdbformat();
257 if (cdb_read(&c,data,dlen,cdb_datapos(&c)) == -1) die_cdbformat();
258 if (build(&soa,zone,1,id)) break;
259 }
260
261 cdb_free(&c);
262 print(soa.s,soa.len);
263
264 seek_begin(fdcdb);
265 buffer_init(&bcdb,buffer_unixread,fdcdb,bcdbspace,sizeof bcdbspace);
266
267 pos = 0;
268 get(num,4); pos += 4;
269 uint32_unpack(num,&eod);
270 while (pos < 2048) { get(num,4); pos += 4; }
271
272 while (pos < eod) {
273 if (eod - pos < 8) die_cdbformat();
274 get(num,4); pos += 4;
275 uint32_unpack(num,&klen);
276 get(num,4); pos += 4;
277 uint32_unpack(num,&dlen);
278 if (eod - pos < klen) die_cdbformat();
279 pos += klen;
280 if (eod - pos < dlen) die_cdbformat();
281 pos += dlen;
282
283 if (klen > sizeof key) die_cdbformat();
284 get(key,klen);
285 if (dlen > sizeof data) die_cdbformat();
286 get(data,dlen);
287
288 if ((klen > 1) && (key[0] == 0)) continue; /* location */
289 if (klen < 1) die_cdbformat();
290 if (dns_packet_getname(key,klen,0,&q) != klen) die_cdbformat();
291 if (!dns_domain_suffix(q,zone)) continue;
292 if (!build(&message,q,0,id)) continue;
293 print(message.s,message.len);
294 }
295
296 print(soa.s,soa.len);
297 }
298
299 void netread(char *buf,unsigned int len)
300 {
301 int r;
302
303 while (len > 0) {
304 r = timeoutread(60,0,buf,len);
305 if (r == 0) _exit(0);
306 if (r < 0) die_netread();
307 buf += r; len -= r;
308 }
309 }
310
311 char tcpheader[2];
312 char buf[512];
313 uint16 len;
314
315 static char seed[128];
316
317 int main()
318 {
319 unsigned int pos;
320 char header[12];
321 char qtype[2];
322 char qclass[2];
323 const char *x;
324
325 droproot(FATAL);
326 dns_random_init(seed);
327
328 axfr = env_get("AXFR");
329
330 x = env_get("TCPREMOTEIP");
331 if (x && ip4_scan(x,ip))
332 ;
333 else
334 byte_zero(ip,4);
335
336 x = env_get("TCPREMOTEPORT");
337 if (!x) x = "0";
338 scan_ulong(x,&port);
339
340 for (;;) {
341 netread(tcpheader,2);
342 uint16_unpack_big(tcpheader,&len);
343 if (len > 512) strerr_die2x(111,FATAL,"excessively large request");
344 netread(buf,len);
345
346 pos = dns_packet_copy(buf,len,0,header,12); if (!pos) die_truncated();
347 if (header[2] & 254) strerr_die2x(111,FATAL,"bogus query");
348 if (header[4] || (header[5] != 1)) strerr_die2x(111,FATAL,"bogus query");
349
350 pos = dns_packet_getname(buf,len,pos,&zone); if (!pos) die_truncated();
351 zonelen = dns_domain_length(zone);
352 pos = dns_packet_copy(buf,len,pos,qtype,2); if (!pos) die_truncated();
353 pos = dns_packet_copy(buf,len,pos,qclass,2); if (!pos) die_truncated();
354
355 if (byte_diff(qclass,2,DNS_C_IN) && byte_diff(qclass,2,DNS_C_ANY))
356 strerr_die2x(111,FATAL,"bogus query: bad class");
357
358 qlog(ip,port,header,zone,qtype," ");
359
360 if (byte_equal(qtype,2,DNS_T_AXFR)) {
361 case_lowerb(zone,zonelen);
362 fdcdb = open_read("data.cdb");
363 if (fdcdb == -1) die_cdbread();
364 doaxfr(header);
365 close(fdcdb);
366 }
367 else {
368 if (!response_query(zone,qtype,qclass)) nomem();
369 response[2] |= 4;
370 case_lowerb(zone,zonelen);
371 response_id(header);
372 response[3] &= ~128;
373 if (!(header[2] & 1)) response[2] &= ~1;
374 if (!respond(zone,qtype,ip)) die_outside();
375 print(response,response_len);
376 }
377 }
378 }