release
[hcoop/zz_old/debian/djbdns.git] / axfr-get.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include "uint32.h"
4 #include "uint16.h"
5 #include "stralloc.h"
6 #include "error.h"
7 #include "strerr.h"
8 #include "getln.h"
9 #include "buffer.h"
10 #include "exit.h"
11 #include "open.h"
12 #include "scan.h"
13 #include "byte.h"
14 #include "str.h"
15 #include "ip4.h"
16 #include "timeoutread.h"
17 #include "timeoutwrite.h"
18 #include "dns.h"
19
20 #define FATAL "axfr-get: fatal: "
21
22 void die_usage(void)
23 {
24 strerr_die1x(100,"axfr-get: usage: axfr-get zone fn fn.tmp");
25 }
26 void die_generate(void)
27 {
28 strerr_die2sys(111,FATAL,"unable to generate AXFR query: ");
29 }
30 void die_parse(void)
31 {
32 strerr_die2sys(111,FATAL,"unable to parse AXFR results: ");
33 }
34 unsigned int x_copy(char *buf,unsigned int len,unsigned int pos,char *out,unsigned int outlen)
35 {
36 pos = dns_packet_copy(buf,len,pos,out,outlen);
37 if (!pos) die_parse();
38 return pos;
39 }
40 unsigned int x_getname(char *buf,unsigned int len,unsigned int pos,char **out)
41 {
42 pos = dns_packet_getname(buf,len,pos,out);
43 if (!pos) die_parse();
44 return pos;
45 }
46 unsigned int x_skipname(char *buf,unsigned int len,unsigned int pos)
47 {
48 pos = dns_packet_skipname(buf,len,pos);
49 if (!pos) die_parse();
50 return pos;
51 }
52
53 static char *zone;
54 unsigned int zonelen;
55 char *fn;
56 char *fntmp;
57
58 void die_netread(void)
59 {
60 strerr_die2sys(111,FATAL,"unable to read from network: ");
61 }
62 void die_netwrite(void)
63 {
64 strerr_die2sys(111,FATAL,"unable to write to network: ");
65 }
66 void die_read(void)
67 {
68 strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
69 }
70 void die_write(void)
71 {
72 strerr_die4sys(111,FATAL,"unable to write ",fntmp,": ");
73 }
74
75 int saferead(int fd,char *buf,unsigned int len)
76 {
77 int r;
78 r = timeoutread(60,fd,buf,len);
79 if (r == 0) { errno = error_proto; die_parse(); }
80 if (r <= 0) die_netread();
81 return r;
82 }
83 int safewrite(int fd,char *buf,unsigned int len)
84 {
85 int r;
86 r = timeoutwrite(60,fd,buf,len);
87 if (r <= 0) die_netwrite();
88 return r;
89 }
90 char netreadspace[1024];
91 buffer netread = BUFFER_INIT(saferead,6,netreadspace,sizeof netreadspace);
92 char netwritespace[1024];
93 buffer netwrite = BUFFER_INIT(safewrite,7,netwritespace,sizeof netwritespace);
94
95 void netget(char *buf,unsigned int len)
96 {
97 int r;
98
99 while (len > 0) {
100 r = buffer_get(&netread,buf,len);
101 buf += r; len -= r;
102 }
103 }
104
105 int fd;
106 buffer b;
107 char bspace[1024];
108
109 void put(char *buf,unsigned int len)
110 {
111 if (buffer_put(&b,buf,len) == -1) die_write();
112 }
113
114 int printable(char ch)
115 {
116 if (ch == '.') return 1;
117 if ((ch >= 'a') && (ch <= 'z')) return 1;
118 if ((ch >= '0') && (ch <= '9')) return 1;
119 if ((ch >= 'A') && (ch <= 'Z')) return 1;
120 if (ch == '-') return 1;
121 return 0;
122 }
123
124 static char *d1;
125 static char *d2;
126 static char *d3;
127
128 stralloc line;
129 int match;
130
131 int numsoa;
132
133 unsigned int doit(char *buf,unsigned int len,unsigned int pos)
134 {
135 char data[20];
136 uint32 ttl;
137 uint16 dlen;
138 uint16 typenum;
139 uint32 u32;
140 int i;
141
142 pos = x_getname(buf,len,pos,&d1);
143 pos = x_copy(buf,len,pos,data,10);
144 uint16_unpack_big(data,&typenum);
145 uint32_unpack_big(data + 4,&ttl);
146 uint16_unpack_big(data + 8,&dlen);
147 if (len - pos < dlen) { errno = error_proto; return 0; }
148 len = pos + dlen;
149
150 if (!dns_domain_suffix(d1,zone)) return len;
151 if (byte_diff(data + 2,2,DNS_C_IN)) return len;
152
153 if (byte_equal(data,2,DNS_T_SOA)) {
154 if (++numsoa >= 2) return len;
155 pos = x_getname(buf,len,pos,&d2);
156 pos = x_getname(buf,len,pos,&d3);
157 x_copy(buf,len,pos,data,20);
158 uint32_unpack_big(data,&u32);
159 if (!stralloc_copys(&line,"#")) return 0;
160 if (!stralloc_catulong0(&line,u32,0)) return 0;
161 if (!stralloc_cats(&line," auto axfr-get\n")) return 0;
162 if (!stralloc_cats(&line,"Z")) return 0;
163 if (!dns_domain_todot_cat(&line,d1)) return 0;
164 if (!stralloc_cats(&line,":")) return 0;
165 if (!dns_domain_todot_cat(&line,d2)) return 0;
166 if (!stralloc_cats(&line,".:")) return 0;
167 if (!dns_domain_todot_cat(&line,d3)) return 0;
168 if (!stralloc_cats(&line,".")) return 0;
169 for (i = 0;i < 5;++i) {
170 uint32_unpack_big(data + 4 * i,&u32);
171 if (!stralloc_cats(&line,":")) return 0;
172 if (!stralloc_catulong0(&line,u32,0)) return 0;
173 }
174 }
175 else if (byte_equal(data,2,DNS_T_NS)) {
176 if (!stralloc_copys(&line,"&")) return 0;
177 if (byte_equal(d1,2,"\1*")) { errno = error_proto; return 0; }
178 if (!dns_domain_todot_cat(&line,d1)) return 0;
179 if (!stralloc_cats(&line,"::")) return 0;
180 x_getname(buf,len,pos,&d1);
181 if (!dns_domain_todot_cat(&line,d1)) return 0;
182 if (!stralloc_cats(&line,".")) return 0;
183 }
184 else if (byte_equal(data,2,DNS_T_CNAME)) {
185 if (!stralloc_copys(&line,"C")) return 0;
186 if (!dns_domain_todot_cat(&line,d1)) return 0;
187 if (!stralloc_cats(&line,":")) return 0;
188 x_getname(buf,len,pos,&d1);
189 if (!dns_domain_todot_cat(&line,d1)) return 0;
190 if (!stralloc_cats(&line,".")) return 0;
191 }
192 else if (byte_equal(data,2,DNS_T_PTR)) {
193 if (!stralloc_copys(&line,"^")) return 0;
194 if (!dns_domain_todot_cat(&line,d1)) return 0;
195 if (!stralloc_cats(&line,":")) return 0;
196 x_getname(buf,len,pos,&d1);
197 if (!dns_domain_todot_cat(&line,d1)) return 0;
198 if (!stralloc_cats(&line,".")) return 0;
199 }
200 else if (byte_equal(data,2,DNS_T_MX)) {
201 uint16 dist;
202 if (!stralloc_copys(&line,"@")) return 0;
203 if (!dns_domain_todot_cat(&line,d1)) return 0;
204 if (!stralloc_cats(&line,"::")) return 0;
205 pos = x_copy(buf,len,pos,data,2);
206 uint16_unpack_big(data,&dist);
207 x_getname(buf,len,pos,&d1);
208 if (!dns_domain_todot_cat(&line,d1)) return 0;
209 if (!stralloc_cats(&line,".:")) return 0;
210 if (!stralloc_catulong0(&line,dist,0)) return 0;
211 }
212 else if (byte_equal(data,2,DNS_T_A) && (dlen == 4)) {
213 char ipstr[IP4_FMT];
214 if (!stralloc_copys(&line,"+")) return 0;
215 if (!dns_domain_todot_cat(&line,d1)) return 0;
216 if (!stralloc_cats(&line,":")) return 0;
217 x_copy(buf,len,pos,data,4);
218 if (!stralloc_catb(&line,ipstr,ip4_fmt(ipstr,data))) return 0;
219 }
220 else {
221 unsigned char ch;
222 unsigned char ch2;
223 if (!stralloc_copys(&line,":")) return 0;
224 if (!dns_domain_todot_cat(&line,d1)) return 0;
225 if (!stralloc_cats(&line,":")) return 0;
226 if (!stralloc_catulong0(&line,typenum,0)) return 0;
227 if (!stralloc_cats(&line,":")) return 0;
228 for (i = 0;i < dlen;++i) {
229 pos = x_copy(buf,len,pos,data,1);
230 ch = data[0];
231 if (printable(ch)) {
232 if (!stralloc_catb(&line,&ch,1)) return 0;
233 }
234 else {
235 if (!stralloc_cats(&line,"\\")) return 0;
236 ch2 = '0' + ((ch >> 6) & 7);
237 if (!stralloc_catb(&line,&ch2,1)) return 0;
238 ch2 = '0' + ((ch >> 3) & 7);
239 if (!stralloc_catb(&line,&ch2,1)) return 0;
240 ch2 = '0' + (ch & 7);
241 if (!stralloc_catb(&line,&ch2,1)) return 0;
242 }
243 }
244 }
245 if (!stralloc_cats(&line,":")) return 0;
246 if (!stralloc_catulong0(&line,ttl,0)) return 0;
247 if (!stralloc_cats(&line,"\n")) return 0;
248 put(line.s,line.len);
249
250 return len;
251 }
252
253 stralloc packet;
254
255 int main(int argc,char **argv)
256 {
257 char out[20];
258 unsigned long u;
259 uint16 dlen;
260 unsigned int pos;
261 uint32 oldserial = 0;
262 uint32 newserial = 0;
263 uint16 numqueries;
264 uint16 numanswers;
265
266 if (!*argv) die_usage();
267
268 if (!*++argv) die_usage();
269 if (!dns_domain_fromdot(&zone,*argv,str_len(*argv))) die_generate();
270 zonelen = dns_domain_length(zone);
271
272 if (!*++argv) die_usage();
273 fn = *argv;
274 if (!*++argv) die_usage();
275 fntmp = *argv;
276
277 fd = open_read(fn);
278 if (fd == -1) {
279 if (errno != error_noent) die_read();
280 }
281 else {
282 buffer_init(&b,buffer_unixread,fd,bspace,sizeof bspace);
283 if (getln(&b,&line,&match,'\n') == -1) die_read();
284 if (!stralloc_0(&line)) die_read();
285 if (line.s[0] == '#') {
286 scan_ulong(line.s + 1,&u);
287 oldserial = u;
288 }
289 close(fd);
290 }
291
292 if (!stralloc_copyb(&packet,"\0\0\0\0\0\1\0\0\0\0\0\0",12)) die_generate();
293 if (!stralloc_catb(&packet,zone,zonelen)) die_generate();
294 if (!stralloc_catb(&packet,DNS_T_SOA DNS_C_IN,4)) die_generate();
295 uint16_pack_big(out,packet.len);
296 buffer_put(&netwrite,out,2);
297 buffer_put(&netwrite,packet.s,packet.len);
298 buffer_flush(&netwrite);
299
300 netget(out,2);
301 uint16_unpack_big(out,&dlen);
302 if (!stralloc_ready(&packet,dlen)) die_parse();
303 netget(packet.s,dlen);
304 packet.len = dlen;
305
306 pos = x_copy(packet.s,packet.len,0,out,12);
307 uint16_unpack_big(out + 4,&numqueries);
308 uint16_unpack_big(out + 6,&numanswers);
309
310 while (numqueries) {
311 --numqueries;
312 pos = x_skipname(packet.s,packet.len,pos);
313 pos += 4;
314 }
315
316 if (!numanswers) { errno = error_proto; die_parse(); }
317 pos = x_getname(packet.s,packet.len,pos,&d1);
318 if (!dns_domain_equal(zone,d1)) { errno = error_proto; die_parse(); }
319 pos = x_copy(packet.s,packet.len,pos,out,10);
320 if (byte_diff(out,4,DNS_T_SOA DNS_C_IN)) { errno = error_proto; die_parse(); }
321 pos = x_skipname(packet.s,packet.len,pos);
322 pos = x_skipname(packet.s,packet.len,pos);
323 pos = x_copy(packet.s,packet.len,pos,out,4);
324
325 uint32_unpack_big(out,&newserial);
326
327
328 if (oldserial && newserial) /* allow 0 for very recently modified zones */
329 if (oldserial == newserial) /* allow serial numbers to move backwards */
330 _exit(0);
331
332
333 fd = open_trunc(fntmp);
334 if (fd == -1) die_write();
335 buffer_init(&b,buffer_unixwrite,fd,bspace,sizeof bspace);
336
337 if (!stralloc_copyb(&packet,"\0\0\0\0\0\1\0\0\0\0\0\0",12)) die_generate();
338 if (!stralloc_catb(&packet,zone,zonelen)) die_generate();
339 if (!stralloc_catb(&packet,DNS_T_AXFR DNS_C_IN,4)) die_generate();
340 uint16_pack_big(out,packet.len);
341 buffer_put(&netwrite,out,2);
342 buffer_put(&netwrite,packet.s,packet.len);
343 buffer_flush(&netwrite);
344
345 numsoa = 0;
346 while (numsoa < 2) {
347 netget(out,2);
348 uint16_unpack_big(out,&dlen);
349 if (!stralloc_ready(&packet,dlen)) die_parse();
350 netget(packet.s,dlen);
351 packet.len = dlen;
352
353 pos = x_copy(packet.s,packet.len,0,out,12);
354 uint16_unpack_big(out + 4,&numqueries);
355
356 while (numqueries) {
357 --numqueries;
358 pos = x_skipname(packet.s,packet.len,pos);
359 pos += 4;
360 }
361 while (pos < packet.len) {
362 pos = doit(packet.s,packet.len,pos);
363 if (!pos) die_parse();
364 }
365 }
366
367 if (buffer_flush(&b) == -1) die_write();
368 if (fsync(fd) == -1) die_write();
369 if (close(fd) == -1) die_write(); /* NFS dorks */
370 if (rename(fntmp,fn) == -1)
371 strerr_die6sys(111,FATAL,"unable to move ",fntmp," to ",fn,": ");
372 _exit(0);
373 }