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 
die_usage(void)22 void die_usage(void)
23 {
24   strerr_die1x(100,"axfr-get: usage: axfr-get zone fn fn.tmp");
25 }
die_generate(void)26 void die_generate(void)
27 {
28   strerr_die2sys(111,FATAL,"unable to generate AXFR query: ");
29 }
die_parse(void)30 void die_parse(void)
31 {
32   strerr_die2sys(111,FATAL,"unable to parse AXFR results: ");
33 }
x_copy(char * buf,unsigned int len,unsigned int pos,char * out,unsigned int outlen)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 }
x_getname(char * buf,unsigned int len,unsigned int pos,char ** out)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 }
x_skipname(char * buf,unsigned int len,unsigned int pos)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 
die_netread(void)58 void die_netread(void)
59 {
60   strerr_die2sys(111,FATAL,"unable to read from network: ");
61 }
die_netwrite(void)62 void die_netwrite(void)
63 {
64   strerr_die2sys(111,FATAL,"unable to write to network: ");
65 }
die_read(void)66 void die_read(void)
67 {
68   strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
69 }
die_write(void)70 void die_write(void)
71 {
72   strerr_die4sys(111,FATAL,"unable to write ",fntmp,": ");
73 }
74 
saferead(int fd,char * buf,unsigned int len)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 }
safewrite(int fd,char * buf,unsigned int len)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 
netget(char * buf,unsigned int len)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 
put(char * buf,unsigned int len)109 void put(char *buf,unsigned int len)
110 {
111   if (buffer_put(&b,buf,len) == -1) die_write();
112 }
113 
printable(char ch)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 
doit(char * buf,unsigned int len,unsigned int pos)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 
main(int argc,char ** argv)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 }
374