Changeset 301
- Timestamp:
- 04/11/12 22:58:17 (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/DNSDB.pm
r299 r301 853 853 854 854 my $cidr; 855 my $warnmsg; 855 my $tmpcidr; 856 my $warnmsg = ''; 856 857 857 858 if ($zone =~ /\.in-addr\.arpa\.?$/) { 858 859 # v4 revzone, formal zone name type 859 my $tmpcidr;860 860 my $tmpzone = $zone; 861 861 $tmpzone =~ s/\.in-addr\.arpa\.?//; 862 return ('FAIL', "Non-numerics in apparent IPv4 reverse zone name") if $tmpzone !~ /^(?:\d+-)?[\d\.]+$/;862 return ('FAIL', "Non-numerics in apparent IPv4 reverse zone name") if $tmpzone !~ /^(?:\d+-)?[\d\.]+$/; 863 863 864 864 # Snag the octet pieces … … 893 893 } 894 894 895 # Just to be sure, use NetAddr::IP to validate. Saves a lot of nasty regex watching for valid octet values.896 return ('FAIL', "Invalid zone $zone (apparent netblock $cidr)")897 unless $cidr = NetAddr::IP->new($tmpcidr);898 899 895 } elsif ($zone =~ /\.ip6\.arpa$/) { 900 896 # v6 revzone, formal zone name type 901 897 my $tmpzone = $zone; 902 898 $tmpzone =~ s/\.ip6\.arpa\.?//; 903 return ('FAIL',"Non-hexadecimals in apparent IPv6 reverse zone name") if $tmpzone !~ /^[a-fA-F\d\.]+$/; 899 ##fixme: if-n-when we decide we can support sub-nibble v6 zone names, we'll need to change this segment 900 return ('FAIL', "Non-hexadecimals in apparent IPv6 reverse zone name") if $tmpzone !~ /^[a-fA-F\d\.]+$/; 904 901 my @quads = reverse(split(/\./, $tmpzone)); 905 902 $warnmsg .= "Apparent sub-/64 IPv6 reverse zone\n" if $#quads > 15; 906 903 my $nc; 907 904 foreach (@quads) { 908 $ cidr .= $_;909 $ cidr .= ":" if ++$nc % 4 == 0;905 $tmpcidr .= $_; 906 $tmpcidr .= ":" if ++$nc % 4 == 0; 910 907 } 911 908 my $nq = 1 if $nc % 4 != 0; 912 909 my $mask = $nc * 4; # need to do this here because we probably increment it below 913 910 while ($nc++ % 4 != 0) { 914 $cidr .= "0"; 915 } 916 $cidr .= ($nq ? '::' : ':')."/$mask"; 917 } 911 $tmpcidr .= "0"; 912 } 913 $tmpcidr .= ($nq ? '::' : ':')."/$mask"; 914 } 915 916 # Just to be sure, use NetAddr::IP to validate. Saves a lot of nasty regex watching for valid octet values. 917 return ('FAIL', "Invalid zone $zone (apparent netblock $tmpcidr)") 918 unless $cidr = NetAddr::IP->new($tmpcidr); 919 920 if ($warnmsg) { 921 $errstr = $warnmsg; 922 return ('WARN', $cidr); 923 } 924 return ('OK', $cidr); 918 925 } # done _zone2cidr() 919 926 … … 3213 3220 my $dbh = shift; 3214 3221 my $ifrom_in = shift; 3215 my $ domain= shift;3222 my $zone = shift; 3216 3223 my $group = shift; 3217 3224 my $status = shift || 1; 3218 3225 my $rwsoa = shift || 0; 3219 3226 my $rwns = shift || 0; 3220 3227 my $merge = shift || 0; # do we attempt to merge A/AAAA and PTR records whenever possible? 3228 # do we overload this with the fixme below? 3221 3229 ##fixme: add mode to delete&replace, merge+overwrite, merge new? 3222 3230 … … 3227 3235 my $ifrom; 3228 3236 3237 my $rev = 'n'; 3238 my $code = 'OK'; 3239 my $msg = 'foobar?'; 3240 3229 3241 # choke on possible bad setting in ifrom 3230 3242 # IPv4 and v6, and valid hostnames! … … 3233 3245 unless ($ifrom) = ($ifrom_in =~ /^([0-9a-f\:.]+|[0-9a-z_.-]+)$/i); 3234 3246 3247 my $errmsg; 3248 3249 my $zone_id; 3250 my $domain_id = 0; 3251 my $rdns_id = 0; 3252 my $cidr; 3253 3254 # magic happens! detect if we're importing a domain or a reverse zone 3255 # while we're at it, figure out what the CIDR netblock is (if we got a .arpa) 3256 # or what the formal .arpa zone is (if we got a CIDR netblock) 3257 # Handles sub-octet v4 zones in the format specified in the Cricket Book, 2nd Ed, p217-218 3258 3259 if ($zone =~ m{(?:\.arpa\.?|/\d+)$}) { 3260 # we seem to have a reverse zone 3261 $rev = 'y'; 3262 3263 if ($zone =~ /\.arpa\.?$/) { 3264 # we have a formal reverse zone. call _zone2cidr and get the CIDR block. 3265 ($code,$msg) = _zone2cidr($zone); 3266 return ($code, $msg) if $code eq 'FAIL'; 3267 $cidr = $msg; 3268 } elsif ($zone =~ m|^[\d.]+/\d+$|) { 3269 # v4 revzone, CIDR netblock 3270 $cidr = NetAddr::IP->new($zone) or return ('FAIL',"$zone is not a valid CIDR block"); 3271 $zone = _ZONE($cidr, 'ZONE.in-addr.arpa', 'r', '.'); 3272 } elsif ($zone =~ m|^[a-fA-F\d:]+/\d+$|) { 3273 # v6 revzone, CIDR netblock 3274 $cidr = NetAddr::IP->new($zone) or return ('FAIL',"$zone is not a valid CIDR block"); 3275 return ('FAIL', "$zone is not a nibble-aligned block") if $cidr->masklen % 4 != 0; 3276 $zone = _ZONE($cidr, 'ZONE.ip6.arpa', 'r', '.'); 3277 } else { 3278 # there is. no. else! 3279 return ('FAIL', "Unknown zone name format"); 3280 } 3281 3282 # quick check to start to see if we've already got one 3283 3284 ($zone_id) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet=?", 3285 undef, ("$cidr")); 3286 $rdns_id = $zone_id; 3287 } else { 3288 # default to domain 3289 ($zone_id) = $dbh->selectrow_array("SELECT domain_id FROM domains WHERE domain=?", 3290 undef, ($zone)); 3291 $domain_id = $zone_id; 3292 } 3293 3294 return ('FAIL', ($rev eq 'n' ? 'Domain' : 'Reverse zone')." already exists") if $zone_id; 3295 3235 3296 # Allow transactions, and raise an exception on errors so we can catch it later. 3236 3297 # Use local to make sure these get "reset" properly on exiting this block … … 3238 3299 local $dbh->{RaiseError} = 1; 3239 3300 3240 my $sth = $dbh->prepare("SELECT domain_id FROM domains WHERE domain=?"); 3241 my $dom_id; 3242 3243 # quick check to start to see if we've already got one 3244 $sth->execute($domain); 3245 ($dom_id) = $sth->fetchrow_array; 3246 3247 return ('FAIL', "Domain already exists") if $dom_id; 3248 3301 my $sth; 3249 3302 eval { 3250 # can't do this, can't nest transactions. sigh. 3251 #my ($dcode, $dmsg) = addDomain(dbh, domain, group, status);3303 3304 if ($rev eq 'n') { 3252 3305 3253 3306 ##fixme: serial 3254 my $sth = $dbh->prepare("INSERT INTO domains (domain,group_id,status) VALUES (?,?,?)"); 3255 $sth->execute($domain,$group,$status); 3307 $dbh->do("INSERT INTO domains (domain,group_id,status) VALUES (?,?,?)", undef, ($zone,$group,$status) ); 3308 # get domain id so we can do the records 3309 ($zone_id) = $dbh->selectrow_array("SELECT currval('domains_domain_id_seq')"); 3310 $domain_id = $zone_id; 3311 _log($dbh, (group_id => $group, domain_id => $domain_id, 3312 entry => "[Added ".($status ? 'active' : 'inactive')." domain $zone via AXFR]") ); 3313 } else { 3314 ##fixme: serial 3315 $dbh->do("INSERT INTO revzones (revnet,group_id,status) VALUES (?,?,?)", undef, ($cidr,$group,$status) ); 3316 # get revzone id so we can do the records 3317 ($zone_id) = $dbh->selectrow_array("SELECT currval('revzones_rdns_id_seq')"); 3318 $rdns_id = $zone_id; 3319 _log($dbh, (group_id => $group, rdns_id => $rdns_id, 3320 entry => "[Added ".($status ? 'active' : 'inactive')." reverse zone $zone via AXFR]") ); 3321 } 3256 3322 3257 3323 ## bizarre DBI<->Net::DNS interaction bug: … … 3260 3326 ## caused a commit instead of barfing 3261 3327 3262 # get domain id so we can do the records3263 $sth = $dbh->prepare("SELECT domain_id FROM domains WHERE domain=?");3264 $sth->execute($domain);3265 ($dom_id) = $sth->fetchrow_array();3266 3267 3328 my $res = Net::DNS::Resolver->new; 3268 3329 $res->nameservers($ifrom); 3269 $res->axfr_start($ domain)3330 $res->axfr_start($zone) 3270 3331 or die "Couldn't begin AXFR\n"; 3271 3332 3333 $sth = $dbh->prepare("INSERT INTO records (domain_id,rdns_id,host,type,val,distance,weight,port,ttl)". 3334 " VALUES (?,?,?,?,?,?,?,?,?)"); 3335 3272 3336 while (my $rr = $res->axfr_next()) { 3337 3338 my $val; 3339 my $distance = 0; 3340 my $weight = 0; 3341 my $port = 0; 3342 3273 3343 my $type = $rr->type; 3274 3275 my $sql = "INSERT INTO records (domain_id,host,type,ttl,val"; 3276 my $vallen = "?,?,?,?,?"; 3344 my $host = $rr->name; 3345 my $ttl = $rr->ttl; 3277 3346 3278 3347 $soaflag = 1 if $type eq 'SOA'; 3279 3348 $nsflag = 1 if $type eq 'NS'; 3280 3349 3281 my @vallist = ($dom_id, $rr->name, $reverse_typemap{$type}, $rr->ttl);3350 # my @vallist = ($zone_id, $rr->name, $reverse_typemap{$type}, $rr->ttl); 3282 3351 3283 3352 # "Primary" types: … … 3291 3360 3292 3361 if ($type eq 'A') { 3293 push @vallist,$rr->address;3362 $val = $rr->address; 3294 3363 } elsif ($type eq 'NS') { 3295 3364 # hmm. should we warn here if subdomain NS'es are left alone? 3296 next if ($rwns && ($rr->name eq $ domain));3297 push @vallist,$rr->nsdname;3365 next if ($rwns && ($rr->name eq $zone)); 3366 $val = $rr->nsdname; 3298 3367 $nsflag = 1; 3299 3368 } elsif ($type eq 'CNAME') { 3300 push @vallist,$rr->cname;3369 $val = $rr->cname; 3301 3370 } elsif ($type eq 'SOA') { 3302 3371 next if $rwsoa; 3303 $ vallist[1]= $rr->mname.":".$rr->rname;3304 push @vallist, ($rr->refresh.":".$rr->retry.":".$rr->expire.":".$rr->minimum);3372 $host = $rr->mname.":".$rr->rname; 3373 $val = $rr->refresh.":".$rr->retry.":".$rr->expire.":".$rr->minimum; 3305 3374 $soaflag = 1; 3306 3375 } elsif ($type eq 'PTR') { 3307 push @vallist, $rr->ptrdname; 3376 $val = $host; 3377 $host = $rr->ptrdname; 3378 if ($val =~ /\.in-addr\.arpa\.?$/) { 3379 $val =~ s/\.in-addr\.arpa\.?$//; 3380 $val = join '.', reverse split /\./, $val; 3381 } else { 3382 $val =~ s/\.ip6\.arpa\.?$//; 3383 my @nibs = reverse split /\./, $val; 3384 $val = ''; 3385 my $nc; 3386 foreach (@nibs) { 3387 $val .= $_; 3388 $val .= ":" if ++$nc % 4 == 0 && $nc < 32; 3389 } 3390 # canonicalize with NetAddr::IP 3391 $val = NetAddr::IP->new($val)->addr unless $val =~ /\*$/; 3392 } 3308 3393 # hmm. PTR records should not be in forward zones. 3309 3394 } elsif ($type eq 'MX') { 3310 $sql .= ",distance"; 3311 $vallen .= ",?"; 3312 push @vallist, $rr->exchange; 3313 push @vallist, $rr->preference; 3395 $val = $rr->exchange; 3396 $distance = $rr->preference; 3314 3397 } elsif ($type eq 'TXT') { 3315 3398 ##fixme: Net::DNS docs say this should be deprecated for rdatastr() or char_str_list(), 3316 3399 ## but don't really seem enthusiastic about it. 3317 my $rrdata = $rr->txtdata; 3318 push @vallist, $rrdata; 3400 $val = $rr->txtdata; 3319 3401 } elsif ($type eq 'SPF') { 3320 3402 ##fixme: and the same caveat here, since it is apparently a clone of ::TXT 3321 my $rrdata = $rr->txtdata; 3322 push @vallist, $rrdata; 3403 $val = $rr->txtdata; 3323 3404 } elsif ($type eq 'AAAA') { 3324 push @vallist,$rr->address;3405 $val = $rr->address; 3325 3406 } elsif ($type eq 'SRV') { 3326 $sql .= ",distance,weight,port" if $type eq 'SRV'; 3327 $vallen .= ",?,?,?" if $type eq 'SRV'; 3328 push @vallist, $rr->target; 3329 push @vallist, $rr->priority; 3330 push @vallist, $rr->weight; 3331 push @vallist, $rr->port; 3407 $val = $rr->target; 3408 $distance = $rr->priority; 3409 $weight = $rr->weight; 3410 $port = $rr->port; 3332 3411 } elsif ($type eq 'KEY') { 3333 3412 # we don't actually know what to do with these... 3334 push @vallist, ($rr->flags.":".$rr->protocol.":".$rr->algorithm.":".$rr->key.":".$rr->keytag.":".$rr->privatekeyname);3413 $val = $rr->flags.":".$rr->protocol.":".$rr->algorithm.":".$rr->key.":".$rr->keytag.":".$rr->privatekeyname; 3335 3414 } else { 3336 my $rrdata = $rr->rdatastr; 3337 push @vallist, $rrdata; 3415 $val = $rr->rdatastr; 3338 3416 # Finding a different record type is not fatal.... just problematic. 3339 3417 # We may not be able to export it correctly. … … 3342 3420 3343 3421 # BIND supports: 3344 # A CNAME HINFO MB(ex) MD(ob) MF(ob) MG(ex) MINFO(ex) MR(ex) MX NS NULL 3345 # PTR SOA TXT WKS AFSDB(ex) ISDN(ex) RP(ex) RT(ex) X25(ex) PX 3422 # [standard] 3423 # A AAAA CNAME MX NS PTR SOA TXT 3424 # [variously experimental, obsolete, or obscure] 3425 # HINFO MB(ex) MD(ob) MF(ob) MG(ex) MINFO(ex) MR(ex) NULL WKS AFSDB(ex) ISDN(ex) RP(ex) RT(ex) X25(ex) PX 3346 3426 # ... if one can ever find the right magic to format them correctly 3347 3427 … … 3351 3431 # DNSKEY DLV X25 TXT TSIG TKEY SSHFP SRV SPF SOA RT RP PX PTR NULL APL::AplItem 3352 3432 3353 $sth = $dbh->prepare($sql.") VALUES (".$vallen.")") or die "problem preparing record insert SQL\n"; 3354 $sth->execute(@vallist) or die "failed to insert ".$rr->string.": ".$sth->errstr."\n"; 3433 # $sth = $dbh->prepare($sql.") VALUES (".$vallen.")") 3434 # or die "problem preparing record insert SQL: ".$dbh->errstr."\n"; 3435 # $sth->execute(@vallist) or die "failed to insert ".$rr->string.": ".$sth->errstr."\n"; 3436 3437 $sth->execute($domain_id, $rdns_id, $host, $reverse_typemap{$type}, $val, 3438 $distance, $weight, $port, $ttl); 3355 3439 3356 3440 $nrecs++; 3441 3442 my $logentry = "[AXFR $zone] "; 3443 if ($type eq 'SOA') { 3444 # also !$rwsoa, but if that's set, it should be impossible to get here. 3445 my @tmp1 = split /:/, $host; 3446 my @tmp2 = split /:/, $val; 3447 $logentry .= "Added SOA record [contact $tmp1[0]] [master $tmp1[1]] ". 3448 "[refresh $tmp2[0]] [retry $tmp2[1]] [expire $tmp2[2]] [minttl $tmp2[3]], TTL $ttl"; 3449 } else { 3450 $logentry .= "Added record '$host $type"; 3451 $logentry .= " [distance $distance]" if $type eq 'MX'; 3452 $logentry .= " [priority $distance] [weight $weight] [port $port]" if $type eq 'SRV'; 3453 $logentry .= " $val', TTL $ttl"; 3454 } 3455 _log($dbh, (group_id => $group, domain_id => $domain_id, rdns_id => $rdns_id, entry => $logentry) ); 3357 3456 3358 3457 } # while axfr_next … … 3365 3464 $sthgetsoa->execute($group,$reverse_typemap{SOA}); 3366 3465 while (my ($host,$val,$ttl) = $sthgetsoa->fetchrow_array()) { 3367 $host =~ s/DOMAIN/$ domain/g;3368 $val =~ s/DOMAIN/$ domain/g;3369 $sthputsoa->execute($ dom_id,$host,$reverse_typemap{SOA},$val,$ttl);3466 $host =~ s/DOMAIN/$zone/g; 3467 $val =~ s/DOMAIN/$zone/g; 3468 $sthputsoa->execute($zone_id,$host,$reverse_typemap{SOA},$val,$ttl); 3370 3469 } 3371 3470 } … … 3378 3477 $sthgetns->execute($group,$reverse_typemap{NS}); 3379 3478 while (my ($host,$val,$ttl) = $sthgetns->fetchrow_array()) { 3380 $host =~ s/DOMAIN/$ domain/g;3381 $val =~ s/DOMAIN/$ domain/g;3382 $sthputns->execute($ dom_id,$host,$reverse_typemap{NS},$val,$ttl);3479 $host =~ s/DOMAIN/$zone/g; 3480 $val =~ s/DOMAIN/$zone/g; 3481 $sthputns->execute($zone_id,$host,$reverse_typemap{NS},$val,$ttl); 3383 3482 } 3384 3483 }
Note:
See TracChangeset
for help on using the changeset viewer.