| 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 | } |