Commit | Line | Data |
---|---|---|
dc0d77d7 CE |
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 | } |