Changeset 690 for branches/stable/DNSDB.pm
- Timestamp:
- 10/14/15 17:54:51 (9 years ago)
- Location:
- branches/stable
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable
-
branches/stable/DNSDB.pm
r652 r690 517 517 # Fail on "compact" IPv4 variants, because they are not consistent and predictable. 518 518 return ('FAIL',"A record must be a valid IPv4 address") 519 unless ${$args{host}} =~ /^\d+\.\d+\.\d+\.\d+$/;519 unless ${$args{host}} =~ m{^\d+\.\d+\.\d+\.\d+(/\d+)?$}; 520 520 $args{addr} = new NetAddr::IP ${$args{host}}; 521 521 return ('FAIL',"A record must be a valid IPv4 address") … … 533 533 # or the intended parent domain for live records. 534 534 my $pname = ($args{defrec} eq 'y' ? 'DOMAIN' : $self->domainName($args{id})); 535 ${$args{host}} =~ s/\.*$/\.$pname/ if (${$args{host}} ne '@' && ${$args{host}} !~ /$pname$/ );535 ${$args{host}} =~ s/\.*$/\.$pname/ if (${$args{host}} ne '@' && ${$args{host}} !~ /$pname$/i); 536 536 537 537 # Check if it's a proper formal .arpa name for an IP, and renormalize it to the IP … … 597 597 # Forcibly append the domain name if the hostname being added does not end with the current domain name 598 598 my $pname = $self->domainName($args{id}); 599 ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/;599 ${$args{host}} =~ s/\.*$/\.$pname/ if (${$args{host}} ne '@' && ${$args{host}} !~ /$pname$/i); 600 600 } 601 601 } else { … … 638 638 my $tmpz = _ZONE($revzone, 'ZONE', 'r', '.'); 639 639 return ('FAIL', "The bare zone may not be a CNAME") if $tmphn eq $tmpz; 640 ##enhance: look up the target name and publish that instead on export 640 641 } 641 642 } … … 653 654 if ${$args{val}} =~ /^(?:[\d.]+|[0-9a-fA-F:]+)$/; 654 655 656 # Make sure target is a well-formed hostname 657 return ('FAIL', $errstr) if ! _check_hostname_form(${$args{val}}, ${$args{rectype}}, $args{defrec}, $args{revrec}); 658 655 659 # Forcibly append the domain name if the hostname being added does not end with the current domain name 656 660 my $pname = ($args{defrec} eq 'y' ? 'DOMAIN' : $self->domainName($args{id})); 657 ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/ ;661 ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/i; 658 662 659 663 # CNAMEs can not be used for parent nodes; just leaf nodes with no other record types 660 664 # Enforce this for the zone name 661 return ('FAIL', "The bare zone name may not be a CNAME") if ${$args{host}} eq $pname ;665 return ('FAIL', "The bare zone name may not be a CNAME") if ${$args{host}} eq $pname || ${$args{host}} =~ /^\@/; 662 666 663 667 ##enhance: Look up the passed value to see if it exists. Ooo, fancy. … … 738 742 } 739 743 # Validate PTR target for form. 740 return ('FAIL', $errstr) if ! _check_hostname_form(${$args{host}}, ${$args{rectype}}, $args{defrec}, $args{revrec}); 744 # %blank% skips the IP when expanding a template record 745 return ('FAIL', $errstr) 746 unless _check_hostname_form(${$args{host}}, ${$args{rectype}}, $args{defrec}, $args{revrec}) || 747 lc(${$args{host}}) eq '%blank%'; 741 748 } else { # revrec ne 'y' 742 749 # Fetch the domain and append if the passed hostname isn't within it. 743 750 my $pname = ($args{defrec} eq 'y' ? 'DOMAIN' : $self->domainName($args{id})); 744 ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/;751 ${$args{host}} =~ s/\.*$/\.$pname/ if (${$args{host}} ne '@' && ${$args{host}} !~ /$pname$/i); 745 752 # Validate hostname and target for form 746 753 return ('FAIL', $errstr) if ! _check_hostname_form(${$args{host}}, ${$args{rectype}}, $args{defrec}, $args{revrec}); … … 780 787 } else { 781 788 # New record. Always warn if a PTR exists 789 # Don't warn when a matching A record exists tho 782 790 my ($ptrcount) = $dbh->selectrow_array("SELECT count(*) FROM "._rectable($args{defrec},$args{revrec}). 783 " WHERE $hostcol = ? ", undef, ($checkme));791 " WHERE $hostcol = ? AND (type=12 OR type=65280 OR type=65281)", undef, ($checkme)); 784 792 $warnflag .= "PTR record for $checkme already exists; adding another will probably not do what you want" 785 793 if $ptrcount; … … 817 825 # or the intended parent domain for live records. 818 826 my $pname = ($args{defrec} eq 'y' ? 'DOMAIN' : $self->domainName($args{id})); 819 ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/; 827 ${$args{host}} =~ s/\.*$/\.$pname/ if (${$args{host}} ne '@' && ${$args{host}} !~ /$pname$/i); 828 return ('FAIL', $errstr) if ! _check_hostname_form(${$args{host}}, ${$args{rectype}}, $args{defrec}, $args{revrec}); 820 829 } else { 821 830 # MX target check - IP addresses not allowed. Must be a more or less well-formed hostname. … … 855 864 # or the intended parent domain for live records. 856 865 my $pname = ($args{defrec} eq 'y' ? 'DOMAIN' : $self->domainName($args{id})); 857 ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/;866 ${$args{host}} =~ s/\.*$/\.$pname/ if (${$args{host}} ne '@' && ${$args{host}} !~ /$pname$/i); 858 867 return ('FAIL', $errstr) if ! _check_hostname_form(${$args{host}}, ${$args{rectype}}, $args{defrec}, $args{revrec}); 859 868 } else { … … 924 933 # or the intended parent domain for live records. 925 934 my $pname = ($args{defrec} eq 'y' ? 'DOMAIN' : $self->domainName($args{id})); 926 ${$args{host}} =~ s/\.*$/\.$pname/ if (${$args{host}} ne '@' && ${$args{host}} !~ /$pname$/ );935 ${$args{host}} =~ s/\.*$/\.$pname/ if (${$args{host}} ne '@' && ${$args{host}} !~ /$pname$/i); 927 936 928 937 # Check if it's a proper formal .arpa name for an IP, and renormalize it to the IP … … 956 965 957 966 # Not absolutely true but WTF use is an SRV record for a reverse zone? 958 return ('FAIL', 'Reverse zones cannot contain SRV records') if $args{revrec} eq 'y';967 # return ('FAIL', 'Reverse zones cannot contain SRV records') if $args{revrec} eq 'y'; 959 968 960 969 # Key additional record parts. Always required. … … 974 983 # or the intended parent domain for live records. 975 984 my $pname = ($args{defrec} eq 'y' ? 'DOMAIN' : $self->domainName($args{id})); 976 ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/ ;985 ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/i; 977 986 978 987 ##enhance: Rejig so that we can pass back a WARN red flag, instead of … … 1042 1051 # but that gets stupid in forward zones, since these records are shared. 1043 1052 return ('FAIL', "$typemap{${$args{rectype}}} record must be a valid IPv4 address") 1044 if ${$args{rectype}} == 65280 && ${$args{val}} !~ /^\d+\.\d+\.\d+\.\d+$/;1053 if ${$args{rectype}} == 65280 && ${$args{val}} !~ m{^\d+\.\d+\.\d+\.\d+(?:/\d+)?$}; 1045 1054 return ('FAIL', "$typemap{${$args{rectype}}} record must be a valid IPv6 address") 1046 if ${$args{rectype}} == 65281 && ${$args{val}} !~ /^[a-fA-F0-9:]+$/;1055 if ${$args{rectype}} == 65281 && ${$args{val}} !~ m{^[a-fA-F0-9:]+(?:/\d+)?$}; 1047 1056 # If things are not OK, this should prevent Stupid in the error log. 1048 1057 $args{addr} = new NetAddr::IP ${$args{val}} … … 1115 1124 ${$args{fields}} .= "rdns_id,"; 1116 1125 push @{$args{vallist}}, $revid; 1117 } 1126 1127 # Coerce the hostname back to the domain; this is so it displays and manipulates 1128 # sanely in the reverse zone. 1129 if (${$args{host}} eq '@') { 1130 ${$args{host}} = $self->domainName($args{id}); # errors? What errors? 1131 } 1132 } # revrec ne 'y' 1118 1133 1119 1134 } else { # defrec eq 'y' … … 1243 1258 if ($args{defrec} eq 'n') { 1244 1259 if ($args{revrec} eq 'n') { 1245 ($code,$msg) = $self->_validate_1(%args) if ${$args{rectype}} == 65280; 1246 ($code,$msg) = $self->_validate_28(%args) if ${$args{rectype}} == 65281; 1247 return ($code,$msg) if $code eq 'FAIL'; 1260 1261 # Coerce all hostnames to end in ".DOMAIN" for group/default records, 1262 # or the intended parent domain for live records. 1263 my $pname = $self->domainName($args{id}); 1264 ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/i; 1265 1266 # check for form; note this checks both normal and "other" hostnames. 1267 return ('FAIL', $errstr) 1268 if !_check_hostname_form(${$args{host}}, ${$args{rectype}}, $args{defrec}, $args{revrec}); 1248 1269 1249 1270 # Check if the requested reverse zone exists - note, an IP fragment won't … … 1295 1316 # AAAA+PTR template record 1296 1317 # Not sure this can be handled sanely due to the size of IPv6 address space 1318 # _validate_65283 above should handle v6 template records fine. It's on export we've got trouble. 1297 1319 sub _validate_65284 { 1298 return ('OK','OK'); 1320 my $self = shift; 1321 my %args = @_; 1322 1323 # do a quick check on the form of the hostname part; this is effectively a 1324 # "*.0.0.f.ip6.arpa" hostname, not an actual expandable IP template pattern 1325 # like with 65283. 1326 return ('FAIL', $errstr) 1327 if !_check_hostname_form(${$args{host}}, ${$args{rectype}}, $args{defrec}, $args{revrec}); 1328 return $self->_validate_65283(%args); 1299 1329 } # done AAAA+PTR template record 1300 1330 … … 1323 1353 } else { 1324 1354 my $pname = $self->domainName($args{id}); 1325 ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/ ;1355 ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/i; 1326 1356 } 1327 1357 } else { … … 1573 1603 # really have a sane way to handle this type of expansion at the moment 1574 1604 # due to the size of the address space. 1575 # Takes a reference to a template string to be expanded, and an IP to use in the replacement. 1605 # Takes a reference to a template string to be expanded, an IP to use in the replacement, 1606 # an optional netblock for the %ngb (net, gw, bcast) expansion, and an optional index 1607 # number for %c "n'th usable IP in block/range" patterns. 1608 # Alters the template string referred. 1576 1609 sub _template4_expand { 1610 # ugh pthui 1611 my $self; 1612 $self = shift if ref($_[0]) eq 'DNSDB'; 1613 1577 1614 my $tmpl = shift; 1578 1615 my $ip = shift; 1616 my $subnet = shift; # for %ngb and %c 1617 my $ipindex = shift; # for %c 1618 1619 # blank $tmpl on config template_skip_0 or template_skip_255, unless we have a %ngb 1620 if ($$tmpl !~ /\%-?n-?g-?b\%/) { 1621 if ( ($ip =~ /\.0$/ && $self->{template_skip_0}) || 1622 ($ip =~ /\.255$/ && $self->{template_skip_255}) ) { 1623 $$tmpl = ''; 1624 return; 1625 } 1626 } 1579 1627 1580 1628 my @ipparts = split /\./, $ip; … … 1585 1633 push @ippad, sprintf("%0.3u", $_); 1586 1634 } 1635 1636 # Two or three consecutive separator characters (_ or -) should be rare - users that use them 1637 # anywhere other than punycoded internationalized domains get to keep the pieces when it breaks. 1638 # We clean up the ones that we may inadvertently generate after replacing %c and %ngb% 1639 my ($thrsep) = ($$tmpl =~ /[_-]{3}/); 1640 my ($twosep) = ($$tmpl =~ /[_-]{2}/); 1641 1642 # Take the simplest path to pattern substitution; replace only exactly the %c or %ngb% 1643 # patterns as-is. Then check after to see if we've caused doubled separator characters (- or _) 1644 # and eliminate them, but only if the original template didn't have them already. Also 1645 # unconditionally drop separator characters immediately before a dot; these do not always 1646 # strictly make the label invalid but almost always, and any exceptions should never show up 1647 # in a template record that expands to "many" real records anyway. 1648 1649 # %ngb and %c require a netblock 1650 if ($subnet) { 1651 # extract the fragments 1652 my ($ngb,$n,$g,$b) = ($$tmpl =~ /(\%(-?n)(-?g)(-?b)\%)/); 1653 my ($c) = ($$tmpl =~ /(\%-?c)/); my $nld = ''; my $cld = ''; 1654 $c = '' if !$c; 1655 my $skipgw = ($c =~ /\%-c/ ? 0 : 1); 1656 my $ipkill = 0; 1657 1658 ##fixme: still have one edge case not handled well: 1659 # %c%n-gb% 1660 # do we drop the record as per -g, or publish the record with an index of 1 as per %c? 1661 # arguably this is a "that's a STUPID question!" case 1662 1663 if ($c) { 1664 # "n'th usable IP in the block" pattern. We need the caller to provide an index 1665 # number otherwise we see exponential time growth because we have to iterate over 1666 # the whole block to map the IP back to an index. :/ 1667 # NetAddr::IP does not have a method for asking "what index is IP <foo> at?" 1668 1669 # no index, or index == 0, (AKA network address), or IP == broadcast, blank the index fragment 1670 if (!$ipindex || ($$subnet->broadcast->addr eq $ip)) { 1671 $$tmpl =~ s/$c//; 1672 } else { 1673 # if we have %c, AKA "skip the gateway", and we're on the nominal gateway IP, blank the index fragment 1674 if ($skipgw && $$subnet->first->addr eq $ip) { 1675 $$tmpl =~ s/$c//; 1676 } 1677 # else replace the index fragment with the passed index minus $skipgw, so that we can start the 1678 # resulting index at 1 on net+2 1679 else { 1680 $$tmpl =~ s/$c/($ipindex-$skipgw)/e; 1681 } 1682 } 1683 } # if ($c) 1684 1685 if ($ngb) { 1686 # individually check the network, standard gateway (net+1) IP, and broadcast IP 1687 # blank $$tmpl if n, g, or b was prefixed with - (this allows "hiding" net/gw/bcast entries) 1688 1689 if ($$subnet->network->addr eq $ip) { 1690 if ($n eq '-n') { 1691 $$tmpl = ''; 1692 } else { 1693 $$tmpl =~ s/$ngb/net/; 1694 $ipkill = 1; 1695 } 1696 } elsif ($$subnet->first->addr eq $ip) { 1697 if ($g eq '-g') { 1698 $$tmpl = ''; 1699 } else { 1700 $$tmpl =~ s/$ngb/gw/; 1701 $ipkill = 1; 1702 } 1703 } elsif ($$subnet->broadcast->addr eq $ip) { 1704 if ($b eq '-b') { 1705 $$tmpl = ''; 1706 } else { 1707 $$tmpl =~ s/$ngb/bcast/; 1708 $ipkill = 1; 1709 } 1710 } else { 1711 $$tmpl =~ s/$ngb//; 1712 } 1713 } 1714 1715 # We don't (usually) want to expand the IP-related patterns on the -net, -gw, or -bcast IPs. 1716 # Arguably this is another place for another config knob, or possibly further extension of 1717 # the template pattern to control it on a per-subnet basis. 1718 if ($ipkill) { 1719 # kill common IP patterns 1720 $$tmpl =~ s/\%[_.-]?[irdh]//; 1721 # kill IP octet patterns 1722 $$tmpl =~ s/\%[1234][dh0](?:[_.-]\%[1234][dh0]){0,3}//; 1723 } 1724 1725 # and now clean up to make sure we leave a valid DNS label... mostly. Should arguably 1726 # split on /\./ and process each label separately. 1727 $$tmpl =~ s/([_-]){3}/$1/ if !$thrsep; 1728 $$tmpl =~ s/([_-]){2}/$1/ if !$twosep; 1729 $$tmpl =~ s/[_-]\././; 1730 1731 } # if ($subnet) 1587 1732 1588 1733 # IP substitutions in template records: … … 1620 1765 $$tmpl =~ s/\%([1234])h/$iphex[$1-1]/g; 1621 1766 $$tmpl =~ s/\%([1234])0/$ippad[$1-1]/g; 1767 1622 1768 } # _template4_expand() 1623 1769 … … 1631 1777 my $tmphost = $hname; 1632 1778 # we don't actually need to test with the real IP passed; that saves a bit of fiddling. 1633 _template4_expand(\$tmphost, '10.10.10.10');1634 if ($tmphost =~ /\%/ ) {1779 DNSDB::_template4_expand(\$tmphost, '10.10.10.10'); 1780 if ($tmphost =~ /\%/ || lc($tmphost) !~ /^(?:\*\.)?(?:[0-9a-z_.-]+)$/) { 1635 1781 $errstr = "Invalid template $hname"; 1636 1782 return; 1637 1783 } 1638 } elsif ($rectype == $reverse_typemap{CNAME} ) {1784 } elsif ($rectype == $reverse_typemap{CNAME} && $revrec eq 'y') { 1639 1785 # Allow / in reverse CNAME hostnames for sub-/24 delegation 1640 1786 if (lc($hname) !~ m|^[0-9a-z_./-]+$|) { … … 2280 2426 local $dbh->{RaiseError} = 1; 2281 2427 2428 return ('FAIL', 'Need a zone identifier to look up') if !$zoneid; 2429 2282 2430 my $msg = ''; 2283 2431 my $failmsg = ''; … … 3989 4137 my %args = @_; 3990 4138 4139 $args{revrec} = 'n' if !$args{revrec}; 4140 $args{defrec} = 'n' if !$args{defrec}; 4141 3991 4142 # protection against bad or missing arguments 3992 4143 $args{sortorder} = 'ASC' if !$args{sortorder} || !grep /^$args{sortorder}$/, ('ASC','DESC'); … … 4000 4151 my $perpage = ($args{nrecs} ? $args{nrecs} : $self->{perpage}); 4001 4152 4002 # sort reverse zones on IP, correctly4003 # do other fiddling with $args{sortby} while we're at it.4004 # whee! multisort means just passing comma-separated fields in sortby!4005 my $newsort = '';4006 foreach my $sf (split /,/, $args{sortby}) {4007 $sf = "r.$sf";4008 $sf =~ s/r\.val/inetlazy(r.val)/4009 if $args{revrec} eq 'y' && $args{defrec} eq 'n';4010 $sf =~ s/r\.type/t.alphaorder/;4011 $newsort .= ",$sf";4012 }4013 $newsort =~ s/^,//;4014 4015 my @bindvars = ($args{id});4016 push @bindvars, ($args{filter},$args{filter}) if $args{filter};4017 4153 4018 4154 ##fixme: do we need a knob to twist to switch from unix epoch to postgres time string? 4019 my $sql = "SELECT r.record_id,r.host,r.type,r.val,r.ttl"; 4155 my @bindvars; 4156 my $sql = "SELECT r.record_id,"; 4157 # only include the parent info if we don't already know which parent we're in 4158 $sql .= "r.domain_id,r.rdns_id," unless $args{id}; 4159 $sql .= "r.host,r.type,r.val,r.ttl"; 4020 4160 $sql .= ",l.description AS locname,stamp,r.stamp < now() AS ispast,r.expires,r.stampactive" 4021 4161 if $args{defrec} eq 'n'; … … 4024 4164 $sql .= "INNER JOIN rectypes t ON r.type=t.val "; # for sorting by type alphabetically 4025 4165 $sql .= "LEFT JOIN locations l ON r.location=l.location " if $args{defrec} eq 'n'; 4026 $sql .= "WHERE "._recparent($args{defrec},$args{revrec})." = ?"; 4027 $sql .= " AND NOT r.type=$reverse_typemap{SOA}"; 4166 $sql .= "WHERE NOT r.type=$reverse_typemap{SOA}"; 4167 4168 # "normal" record list 4169 if ($args{id}) { 4170 $sql .= " AND "._recparent($args{defrec},$args{revrec})." = ?"; 4171 push @bindvars, $args{id}; 4172 } 4173 4174 # Filtering on host/val (mainly normal record list) 4028 4175 if ($args{filter}) { 4029 4176 $sql .= " AND (r.host ~* ? OR r.val ~* ? OR r.host ~* ? OR r.val ~* ?)"; 4030 4177 my $tmp = join('.',reverse(split(/\./,$args{filter}))); 4178 push @bindvars, ($args{filter},$args{filter}); 4031 4179 push @bindvars, ($tmp, $tmp); 4032 4180 } 4181 4182 # Filtering on other fields 4183 foreach (qw(type distance weight port ttl description)) { 4184 if ($args{$_}) { 4185 $sql .= " AND $_ ~* ?"; 4186 push @bindvars, $args{$_}; 4187 } 4188 } 4189 4190 # whee! multisort means just passing comma-separated fields in sortby! 4191 my $newsort = ''; 4192 foreach my $sf (split /,/, $args{sortby}) { 4193 $sf = "r.$sf"; 4194 # sort on IP, correctly 4195 $sf =~ s/r\.val/inetlazy(r.val)/; 4196 # hmm. do we really need to limit this? 4197 # if $args{revrec} eq 'y' && $args{defrec} eq 'n'; 4198 $sf =~ s/r\.type/t.alphaorder/; # subtly different from sorting on rectypes.name 4199 $newsort .= ",$sf"; 4200 } 4201 $newsort =~ s/^,//; 4202 ##enhance: pass in ascending/descending sort per-field 4033 4203 $sql .= " ORDER BY $newsort $args{sortorder}"; 4034 4204 # ensure consistent ordering by sorting on record_id too 4035 4205 $sql .= ", record_id $args{sortorder}"; 4206 4207 # Offset/pagination 4036 4208 $sql .= ($args{offset} eq 'all' ? '' : " LIMIT $perpage OFFSET ".$args{offset}*$perpage); 4037 4209 … … 4062 4234 my $self = shift; 4063 4235 my $dbh = $self->{dbh}; 4064 my $defrec = shift; 4065 my $revrec = shift; 4066 my $id = shift; 4067 my $filter = shift || ''; 4068 4069 # keep the nasties down, since we can't ?-sub this bit. :/ 4070 # note this is chars allowed in DNS hostnames 4071 $filter =~ s/[^a-zA-Z0-9_.:-]//g; 4072 4073 my @bindvars = ($id); 4074 push @bindvars, ($filter,$filter) if $filter; 4236 4237 my %args = @_; 4238 4239 $args{defrec} = 'n' if !$args{defrec}; 4240 $args{revrec} = 'n' if !$args{revrec}; 4241 4242 my @bindvars; 4075 4243 my $sql = "SELECT count(*) FROM ". 4076 _rectable($defrec,$revrec). 4077 " WHERE "._recparent($defrec,$revrec)."=? ". 4078 "AND NOT type=$reverse_typemap{SOA}"; 4079 if ($filter) { 4244 _rectable($args{defrec},$args{revrec}). 4245 " r WHERE NOT type=$reverse_typemap{SOA}"; 4246 if ($args{id}) { 4247 $sql .= " AND "._recparent($args{defrec},$args{revrec})." = ?"; 4248 push @bindvars, $args{id}; 4249 } 4250 4251 # Filtering on host/val (mainly normal record list) 4252 if ($args{filter}) { 4080 4253 $sql .= " AND (r.host ~* ? OR r.val ~* ? OR r.host ~* ? OR r.val ~* ?)"; 4081 my $tmp = join('.',reverse(split(/\./,$filter))); 4254 my $tmp = join('.',reverse(split(/\./,$args{filter}))); 4255 push @bindvars, ($args{filter},$args{filter}); 4082 4256 push @bindvars, ($tmp, $tmp); 4083 4257 } 4084 $sql .= " AND (host ~* ? OR val ~* ? OR host ~* ? OR val ~* ?)" if $filter; 4085 my $tmp = join('.',reverse(split(/\./,$filter))); 4086 push @bindvars, ($tmp, $tmp) if $filter; 4258 4259 # Filtering on other fields 4260 foreach (qw(type distance weight port ttl description)) { 4261 if ($args{$_}) { 4262 $sql .= " AND $_ ~* ?"; 4263 push @bindvars, $args{$_}; 4264 } 4265 } 4087 4266 4088 4267 my ($count) = $dbh->selectrow_array($sql, undef, (@bindvars) ); … … 4117 4296 $location = '' if !$location; 4118 4297 4119 my $expires = shift ;4298 my $expires = shift || ''; 4120 4299 $expires = 1 if $expires eq 'until'; # Turn some special values into the appropriate booleans. 4121 4300 $expires = 0 if $expires eq 'after'; 4122 4301 my $stamp = shift; 4123 4302 $stamp = '' if !$stamp; # Timestamp should be a string at this point. 4303 4304 # extra safety net - apparently RPC can squeak this by. O_o 4305 return ('FAIL', "host must contain a value") if !$$host; 4306 return ('FAIL', "val must contain a value") if !$$val; 4124 4307 4125 4308 # Spaces are evil. … … 4249 4432 my $self = shift; 4250 4433 my $dbh = $self->{dbh}; 4434 4251 4435 my $defrec = shift; 4252 4436 my $revrec = shift; … … 4263 4447 $location = '' if !$location; 4264 4448 4265 my $expires = shift ;4449 my $expires = shift || ''; 4266 4450 $expires = 1 if $expires eq 'until'; # Turn some special values into the appropriate booleans. 4267 4451 $expires = 0 if $expires eq 'after'; … … 4481 4665 eval { 4482 4666 $dbh->do("UPDATE records SET $delpar = ?, type = ? WHERE record_id = ?", undef, @sqlargs); 4667 $self->_log(domain_id => $rec->{domain_id}, rdns_id => $rec->{rdns_id}, 4668 group_id => $self->parentID(id => $rec->{rdns_id}, type => 'revzone', revrec => 'y'), 4669 entry => "'$rec->{host} $typemap{$rec->{type}} $rec->{val}' downconverted to ". 4670 "'$rec->{host} $typemap{$newtype} $rec->{val}'"); 4483 4671 $dbh->commit; 4484 4672 }; … … 4494 4682 ## DNSDB::delRec() 4495 4683 # Delete a record. 4684 # Takes a default/live flag, forward/reverse flag, and the ID of the record to delete. 4496 4685 sub delRec { 4497 4686 $errstr = ''; … … 4514 4703 $logdata{rdns_id} = $oldrec->{rdns_id}; 4515 4704 $logdata{group_id} = $oldrec->{group_id} if $defrec eq 'y'; 4516 $logdata{group_id} = $self->parentID(id => $oldrec->{domain_id}, type => ($revrec eq 'n' ? 'domain' : 'revzone'),4517 revrec => $revrec)4705 $logdata{group_id} = $self->parentID(id => ($revrec eq 'n' ? $oldrec->{domain_id} : $oldrec->{rdns_id}), 4706 type => 'domain', revrec => $revrec) 4518 4707 if $defrec eq 'n'; 4519 4708 $logdata{entry} = "Deleted ".($defrec eq 'y' ? 'default record ' : 'record '). … … 4623 4812 4624 4813 4625 ## IPDB::getRevPattern()4814 ## DNSDB::getRevPattern() 4626 4815 # Get the narrowest template pattern applicable to a passed CIDR address (may be a netblock or an IP) 4627 4816 sub getRevPattern { … … 4632 4821 4633 4822 # for speed! Casting and comparing even ~7K records takes ~2.5s, so narrow it down to one revzone first. 4634 my ($revid) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >>= ? AND group_id = ?",4635 undef, ($cidr , $group) );4823 my ($revid) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >>= ?", 4824 undef, ($cidr) ); 4636 4825 4637 4826 ##fixme? may need to narrow things down more by octet-chopping and doing text comparisons before casting. … … 4641 4830 return $revpatt; 4642 4831 } # end getRevPattern() 4832 4833 4834 ## DNSDB::getRevSet() 4835 # Return the unique per-IP reverse hostnames, if any, for the passed 4836 # CIDR address (may be a netblock or an IP) 4837 sub getRevSet { 4838 my $self = shift; 4839 my $dbh = $self->{dbh}; 4840 my $cidr = shift; 4841 my $group = shift || 1; # just in case 4842 4843 # for speed! Casting and comparing even ~7K records takes ~2.5s, so narrow it down to one revzone first. 4844 my ($revid) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >>= ?", 4845 undef, ($cidr) ); 4846 4847 $cidr = new NetAddr::IP $cidr; 4848 if ($cidr->num > 256) { # should also catch v6! 4849 # Even reverse entries for a v4 /24 of IPs is a bit much. I don't expect 4850 # there to be a sane reason to retrive more than a /27 at once, really. 4851 # v6 is going to be hairy no matter how you slice it. 4852 $errstr = "Reverse hostname detail range too large"; 4853 return; 4854 } 4855 4856 my $sth = $dbh->prepare("SELECT val, host FROM records ". 4857 "WHERE (type in (12,65280,65281,65282,65283,65284)) AND rdns_id = ? AND inetlazy(val) = ?"); 4858 4859 my @ret; 4860 foreach my $ip (@{$cidr->splitref()}) { 4861 $sth->execute($revid, $ip); 4862 my @data = $sth->fetchrow_array(); 4863 my %row; 4864 if (@data) { 4865 $row{r_ip} = $data[0]; 4866 $row{iphost} = $data[1]; 4867 } else { 4868 $row{r_ip} = $ip->addr; 4869 $row{iphost} = ''; 4870 } 4871 push @ret, \%row; 4872 } 4873 4874 return \@ret; 4875 } # end getRevSet() 4643 4876 4644 4877 … … 4715 4948 my %args = @_; 4716 4949 4717 # clean up the parent-type. Set it to group if not set; coerce revzone to domain for simpler logic4718 $args{partype} = 'group' if !$args{partype};4719 $args{partype} = 'domain' if $args{partype} eq 'revzone';4720 4721 4950 # clean up defrec and revrec. default to live record, forward zone 4722 4951 $args{defrec} = 'n' if !$args{defrec}; 4723 4952 $args{revrec} = 'n' if !$args{revrec}; 4724 4953 4725 if ($par_type{$args{partype}} eq 'domain') { 4954 # clean up the parent-type. Set it to group if not set 4955 $args{partype} = 'group' if !$args{partype}; 4956 4957 # allow callers to be lazy with type 4958 $args{type} = 'revzone' if $args{type} eq 'domain' && $args{revrec} eq 'y'; 4959 4960 if ($par_type{$args{partype}} eq 'domain' || $par_type{$args{partype}} eq 'revzone') { 4726 4961 # only live records can have a domain/zone parent 4727 4962 return unless ($args{type} eq 'record' && $args{defrec} eq 'n'); … … 5679 5914 5680 5915 # Check for out-of-zone data 5681 if ($host !~ /$dom$/) { 5916 $host = $dom if $host eq '@'; 5917 if ($host !~ /$dom$/i) { 5682 5918 warn "Not exporting out-of-zone record $host $type $val, $ttl (zone $dom)\n"; 5683 5919 next; … … 5825 6061 $tmp = sprintf "%0.4x", $tmp if $srctype eq 'd'; # 0-pad decimal to 4 hex digits 5826 6062 my @o = ($tmp =~ /^(..)(..)$/); # split into octets 5827 return sprintf "\\%0.3o\\%0.3o", hex($o[0]), hex($o[1]); ;6063 return sprintf "\\%0.3o\\%0.3o", hex($o[0]), hex($o[1]); 5828 6064 } 5829 6065 … … 5847 6083 ## forked process 5848 6084 sub __publish_subnet { 5849 my $ obj= shift; # *sigh* need to pass in the DNSDB object so we can read a couple of options6085 my $self = shift; # *sigh* need to pass in the DNSDB object so we can read a couple of options 5850 6086 my $sub = shift; 5851 6087 my $recflags = shift; … … 5862 6098 5863 6099 my $iplist = $sub->splitref(32); 6100 my $ipindex = -1; 5864 6101 foreach (@$iplist) { 5865 6102 my $ip = $_->addr; 6103 $ipindex++; 5866 6104 # make as if we split the non-octet-aligned block into octet-aligned blocks as with SOA 5867 6105 my $lastoct = (split /\./, $ip)[3]; 5868 next if $ip =~ /\.0$/ && $obj->{template_skip_0};5869 next if $ip =~ /\.255$/ && $obj->{template_skip_255};5870 6106 next if $$recflags{$ip}; # && $self->{skip_bcast_255} 5871 6107 $$recflags{$ip}++; … … 5873 6109 my $rec = $hpat; # start fresh with the template for each IP 5874 6110 ##fixme: there really isn't a good way to handle sub-/24 zones here. This way at least 5875 # seems less bad than some alternatives. 5876 _template4_expand(\$rec, $ip); 6111 # seems less bad than some alternatives. 6112 $self->_template4_expand(\$rec, $ip, \$sub, $ipindex); 6113 # _template4_expand may blank $rec; if so, don't publish a record 6114 next if !$rec; 5877 6115 if ($ptronly || $zone->masklen > 24) { 5878 6116 print $fh "^$lastoct.$arpabase:$rec:$ttl:$stamp:$loc\n" or die $!; … … 5885 6123 } 5886 6124 } 5887 } 6125 } # __publish_subnet 5888 6126 5889 6127 ## And now the meat. … … 6085 6323 # print both; a dangling record is harmless, and impossible via web 6086 6324 # UI anyway 6087 $self->_printrec_tiny($datafile,$revrec,$recflags,$zone,$host,28,$val,$dist,$weight,$port,$ttl,$loc,$stamp); 6088 $self->_printrec_tiny($datafile,$revrec,$recflags,$zone,$host,12,$val,$dist,$weight,$port,$ttl,$loc,$stamp); 6325 $self->_printrec_tiny($datafile,$recid,'n',$recflags, $self->domainName($self->_hostparent($host)), 6326 $host, 28, $val, $dist, $weight, $port, $ttl, $loc, $stamp); 6327 $self->_printrec_tiny($datafile, $recid, 'y', $recflags, $zone, 6328 $host, 12, $val, $dist, $weight, $port, $ttl, $loc, $stamp); 6329 6089 6330 ##fixme: add a config flag to indicate use of the patch from http://www.fefe.de/dns/ 6090 6331 # type 6 is for AAAA+PTR, type 3 is for AAAA … … 6111 6352 return if $val->{isv6}; 6112 6353 6113 if ($val->masklen < =16) {6354 if ($val->masklen < 16) { 6114 6355 foreach my $sub ($val->split(16)) { 6115 6356 $self->__publish_subnet($sub, $recflags, $host, $datafile, $ttl, $stamp, $loc, $zone, 0);
Note:
See TracChangeset
for help on using the changeset viewer.