Changeset 234
- Timestamp:
- 02/16/12 16:14:36 (13 years ago)
- Location:
- trunk
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/DNSDB.pm
r233 r234 205 205 return 0 if $parnet->addr =~ /:/ && $$val =~ /\./; 206 206 207 if ($$addr && $$val =~ /^ \d[\d:]+\d$/) {207 if ($$addr && $$val =~ /^[\da-fA-F][\da-fA-F:]+[\da-fA-F]$/) { 208 208 # the only case where NetAddr::IP's acceptance of legitimate IPs is "correct" is for a proper IPv6 address. 209 209 # the rest we have to restructure before fiddling. *sigh* … … 260 260 261 261 # Check IP is well-formed, and that it's a v4 address 262 # Fail on "compact" IPv4 variants, because they are not consistent and predictable. 263 return ('FAIL',"$typemap{${$args{rectype}}} record must be a valid IPv4 address") 264 unless ${$args{val}} =~ /^\d+\.\d+\.\d+\.\d+$/; 262 265 return ('FAIL',"$typemap{${$args{rectype}}} record must be a valid IPv4 address") 263 266 unless $args{addr} && !$args{addr}->{isv6}; 264 267 # coerce IP/value to normalized form for storage 265 268 ${$args{val}} = $args{addr}->addr; 266 267 # Add the necessary fields.268 ${$args{fields}} = 'domain_id,';269 push @{$args{vallist}}, $args{id};270 269 271 270 return ('OK','OK'); … … 337 336 ${$args{val}} = $args{addr}->addr; 338 337 } else { 339 ${$args{val}} =~ s/^([:.]*)/ZONE$1/; 338 if (${$args{val}} =~ /\./) { 339 # looks like a v4 or fragment 340 if (${$args{val}} =~ /^\d+\.\d+\.\d+\.\d+$/) { 341 # woo! a complete IP! validate it and normalize, or fail. 342 $args{addr} = NetAddr::IP->new(${$args{val}}) 343 or return ('FAIL', "IP/value looks like IPv4 but isn't valid"); 344 ${$args{val}} = $args{addr}->addr; 345 } else { 346 ${$args{val}} =~ s/^\.*/ZONE./; 347 } 348 } elsif (${$args{val}} =~ /[a-f:]/) { 349 # looks like a v6 or fragment 350 ${$args{val}} =~ s/^:*/ZONE::/ if !$args{addr}; 351 if ($args{addr}) { 352 if ($args{addr}->addr =~ /^0/) { 353 ${$args{val}} =~ s/^:*/ZONE::/; 354 } else { 355 ${$args{val}} = $args{addr}->addr; 356 } 357 } 358 } else { 359 # bare number (probably). These could be v4 or v6, so we'll 360 # expand on these on creation of a reverse zone. 361 ${$args{val}} = "ZONE,${$args{val}}"; 362 } 363 ${$args{host}} =~ s/\.*$/\.$config{domain}/ if ${$args{host}} !~ /$config{domain}$/; 340 364 } 341 342 ${$args{fields}} = _recparent($args{defrec},$args{revrec}).",";343 push @{$args{vallist}}, $args{id};344 365 345 366 # Multiple PTR records do NOT generally do what most people believe they do, … … 415 436 ${$args{val}} = $args{addr}->addr; 416 437 417 # Add the necessary fields.418 ${$args{fields}} = 'domain_id,';419 push @{$args{vallist}}, $args{id};420 421 438 return ('OK','OK'); 422 439 } # done AAAA record … … 482 499 return ('WARN', $msg); 483 500 } 484 ${$args{fields}} .= "domain_id,";485 push @{$args{vallist}}, ${$args{domid}};486 501 487 502 } else { … … 522 537 return ('FAIL',"A+PTR record must be a valid IPv4 address or fragment") 523 538 if ${$args{val}} =~ /:/; 524 ${$args{val}} =~ s/^ZONE,/ZONE./; 539 ${$args{val}} =~ s/^ZONE,/ZONE./; # Clean up after uncertain IP-fragment-type from _validate_12 525 540 } elsif (${$args{rectype}} == 65281) { 526 541 return ('FAIL',"AAAA+PTR record must be a valid IPv6 address or fragment") 527 542 if ${$args{val}} =~ /\./; 528 ${$args{val}} =~ s/^ZONE,/ZONE::/; 543 ${$args{val}} =~ s/^ZONE,/ZONE::/; # Clean up after uncertain IP-fragment-type from _validate_12 529 544 } 530 545 } else { … … 533 548 # sanely, and you'd end up with guaranteed over-replicated PTR records that would 534 549 # confuse the hell out of pretty much anything that uses them. 550 ##fixme: make this a config flag? 535 551 return ('FAIL', "$typemap{${$args{rectype}}} records not allowed in default domains"); 536 552 } … … 1822 1838 # and weight/port for SRV 1823 1839 # Returns a status code and detail message in case of error 1840 ##fixme: pass a hash with the record data, not a series of separate values 1824 1841 sub addRec { 1825 1842 $errstr = ''; … … 1831 1848 1832 1849 my $host = shift; 1833 my $rectype = shift; 1850 my $rectype = shift; # reference so we can coerce it if "+"-types can't find both zones 1834 1851 my $val = shift; 1835 1852 my $ttl = shift; … … 1848 1865 return ('FAIL', "TTL must be numeric") unless $ttl =~ /^\d+$/; 1849 1866 1850 1851 ## possible contents for record types 1852 # (A/AAAA+)PTR: IP + (FQDN or bare hostname to have ADMINDOMAIN appended?) 1853 # (A/AAAA+)PTR template: IP or netblock + (fully-qualified hostname pattern or bare hostname pattern to have 1854 # ADMINDOMAIN appended?) 1855 # A/AAAA: append parent domain if not included, validate IP 1856 # NS,MX,CNAME,SRV,TXT: append parent domain if not included 1857 1858 # ickypoo. can't see a handy way to really avoid hardcoding these here... otoh, these aren't 1859 # really mutable, it's just handy to have them in a DB table for reordering 1860 # 65280 | A+PTR 1861 # 65281 | AAAA+PTR 1862 # 65282 | PTR template 1863 # 65283 | A+PTR template 1864 # 65284 | AAAA+PTR template 1865 1866 # can only validate parenthood on IPs in live zones; group default records are likely to contain "ZONE" 1867 if ($revrec eq 'y' && $defrec eq 'n') { 1868 if ($rectype == $reverse_typemap{PTR} || $rectype == 65280 || $rectype == 65281) { 1869 return ('FAIL', "IP or IP fragment $val is not within ".revName($dbh, $id)) 1870 unless _ipparent($dbh, $defrec, $revrec, $val, $id, \$addr); 1871 $revid = $id; 1872 } 1873 if ($rectype == 65280 || $rectype == 65281) { 1874 # check host to see if it's managed here. coerce down to PTR if not. 1875 # Split $host and work our way up the hierarchy until we run out of parts to add, or we find a match 1876 # Note we do not do '$checkdom = shift @hostbits' right away, since we want to be able to support 1877 # private TLDs. 1878 my @hostbits = reverse(split(/\./, $host)); 1879 my $checkdom = ''; 1880 my $sth = $dbh->prepare("SELECT count(*),domain_id FROM domains WHERE domain = ? GROUP BY domain_id"); 1881 foreach (@hostbits) { 1882 $checkdom = "$_.$checkdom"; 1883 $checkdom =~ s/\.$//; 1884 $sth->execute($checkdom); 1885 my ($found, $parid) = $sth->fetchrow_array; 1886 if ($found) { 1887 $domid = $parid; 1888 last; 1889 } 1890 } 1891 if (!$domid) { 1892 # no domain found; set the return code and message, then coerce type down to PTR 1893 $retcode = 'WARN'; 1894 $retmsg = "Record added as PTR instead of $typemap{$rectype}; domain not found for $host"; 1895 $rectype = $reverse_typemap{PTR}; 1896 } 1897 } 1898 # types 65282, 65283, 65284 left 1899 } elsif ($revrec eq 'n' && $defrec eq 'n') { 1900 # Forward zone. Validate IPs where we know they *MUST* be correct, 1901 # check to see if we manage the reverse zone on A(AAA)+PTR, 1902 # append the domain on hostnames without it. 1903 if ($rectype == $reverse_typemap{A} || $rectype == 65280) { 1904 return ('FAIL',$typemap{$rectype}." record must be a valid IPv4 address") 1905 unless $addr && !$addr->{isv6}; 1906 $val = $addr->addr; 1907 } 1908 if ($rectype == $reverse_typemap{AAAA} || $rectype == 65281) { 1909 return ('FAIL',$typemap{$rectype}." record must be a valid IPv6 address") 1910 unless $addr && $addr->{isv6}; 1911 $val = $addr->addr; 1912 } 1913 if ($rectype == 65280 || $rectype == 65281) { 1914 # The ORDER BY here makes sure we pick the *smallest* revzone parent. Just In Case. 1915 ($revid) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >> ?". 1916 " ORDER BY masklen(revnet) DESC", undef, ($val)); 1917 if (!$revid) { 1918 $retcode = 'WARN'; 1919 $retmsg = "Record added as ".($rectype == 65280 ? 'A' : 'AAAA')." instead of $typemap{$rectype}; ". 1920 "reverse zone not found for $val"; 1921 $rectype = $reverse_typemap{A} if $rectype == 65280; 1922 $rectype = $reverse_typemap{AAAA} if $rectype == 65281; 1923 $revid = 0; # Just In Case 1924 } 1925 } 1926 my $parstr = domainName($dbh,$id); 1927 $host .= ".$parstr" if $host !~ /$parstr$/; 1928 } 1929 1930 # Validate IPs in MX, NS, SRV records? 1931 # hmm.. this might work. except possibly for something pointing to "deadbeef.ca". <g> 1932 # if ($type == $reverse_typemap{NS} || $type == $reverse_typemap{MX} || $type == $reverse_typemap{SRV}) { 1933 # if ($val =~ /^\s*[\da-f:.]+\s*$/) { 1934 # return ('FAIL',"$val is not a valid IP address") if !$addr; 1935 # } 1936 # } 1937 1938 # basic fields: immediate parent ID, host, type, val, ttl 1939 my $fields = _recparent($defrec,$revrec).",host,type,val,ttl"; 1940 my $vallen = "?,?,?,?,?"; 1941 my @vallist = ($id,$host,$rectype,$val,$ttl); 1942 1943 if ($defrec eq 'n' && ($rectype == 65280 || $rectype == 65281)) { 1944 $fields .= ",".($revrec eq 'n' ? 'rdns_id' : 'domain_id'); 1945 $vallen .= ",?"; 1946 push @vallist, ($revrec eq 'n' ? $revid : $domid); 1947 } 1948 1949 # MX and SRV specials 1950 my $dist; 1951 if ($rectype == $reverse_typemap{MX} or $rectype == $reverse_typemap{SRV}) { 1952 $dist = shift; 1953 return ('FAIL',"Distance is required for $typemap{$rectype} records") unless defined($dist); 1954 $dist =~ s/\s*//g; 1955 return ('FAIL',"Distance is required, and must be numeric") unless $dist =~ /^\d+$/; 1956 $fields .= ",distance"; 1957 $vallen .= ",?"; 1958 push @vallist, $dist; 1959 } 1960 my $weight; 1961 my $port; 1962 if ($rectype == $reverse_typemap{SRV}) { 1963 # check for _service._protocol. NB: RFC2782 does not say "MUST"... nor "SHOULD"... 1964 # it just says (paraphrased) "... is prepended with _ to prevent DNS collisions" 1965 return ('FAIL',"SRV records must begin with _service._protocol [$host]") 1966 unless $host =~ /^_[A-Za-z]+\._[A-Za-z]+\.[a-zA-Z0-9-]+/; 1967 $weight = shift; 1968 $port = shift; 1969 return ('FAIL',"Port and weight are required for SRV records") unless defined($weight) && defined($port); 1970 $weight =~ s/\s*//g; 1971 $port =~ s/\s*//g; 1972 return ('FAIL',"Port and weight are required, and must be numeric") 1973 unless $weight =~ /^\d+$/ && $port =~ /^\d+$/; 1974 $fields .= ",weight,port"; 1975 $vallen .= ",?,?"; 1976 push @vallist, ($weight,$port); 1977 } 1867 # Quick check on hostname parts. Note the regex is more forgiving than the error message; 1868 # domain names technically are case-insensitive, and we use printf-like % codes for a couple 1869 # of types. Other things may also be added to validate default records of several flavours. 1870 return ('FAIL', "Hostnames may not contain anything other than (0-9 a-z . _)") 1871 if $defrec eq 'n' && $host !~ /^[0-9a-z_%.]+$/i; 1872 1873 # Collect these even if we're only doing a simple A record so we can call *any* validation sub 1874 my $dist = shift; 1875 my $port = shift; 1876 my $weight = shift; 1877 1878 my $fields; 1879 my @vallist; 1880 1881 # Call the validation sub for the type requested. 1882 ($retcode,$retmsg) = $validators{$$rectype}($dbh, (defrec => $defrec, revrec => $revrec, id => $id, 1883 host => \$host, rectype => $rectype, val => \$val, addr => $addr, 1884 dist => \$dist, port => \$port, weight => \$weight, 1885 fields => \$fields, vallist => \@vallist) ); 1886 1887 return ($retcode,$retmsg) if $retcode eq 'FAIL'; 1888 1889 # Set up database fields and bind parameters 1890 $fields .= "host,type,val,ttl,"._recparent($defrec,$revrec); 1891 push @vallist, ($host,$$rectype,$val,$ttl,$id); 1892 my $vallen = '?'.(',?'x$#vallist); 1978 1893 1979 1894 # Allow transactions, and raise an exception on errors so we can catch it later. … … 1983 1898 1984 1899 eval { 1985 $dbh->do("INSERT INTO ". ($defrec eq 'y' ? 'default_' : '')."records ($fields) VALUES ($vallen)",1900 $dbh->do("INSERT INTO "._rectable($defrec, $revrec)."records ($fields) VALUES ($vallen)", 1986 1901 undef, @vallist); 1987 1902 $dbh->commit; -
trunk/dns.cgi
r227 r234 486 486 $session->clear('resultmsg'); 487 487 } 488 if ($session->param('warnmsg')) { 489 $page->param(warnmsg => $session->param('warnmsg')); 490 $session->clear('warnmsg'); 491 } 488 492 if ($session->param('errmsg')) { 489 493 $page->param(errmsg => $session->param('errmsg')); … … 531 535 unless ($permissions{admin} || $permissions{record_create}); 532 536 533 ##fixme: this should probably go in DNSDB::addRec(), need to ponder what to do about PTR and friends534 # prevent out-of-domain records from getting added by appending the domain, or DOMAIN for default records535 # my $pname = ($webvar{defrec} eq 'y' ? 'DOMAIN' : domainName($dbh,$webvar{parentid}));536 # $webvar{name} =~ s/\.*$/\.$pname/ if $webvar{name} !~ /$pname$/;537 538 537 my @recargs = ($dbh,$webvar{defrec},$webvar{revrec},$webvar{parentid}, 539 $webvar{name}, $webvar{type},$webvar{address},$webvar{ttl});538 $webvar{name},\$webvar{type},$webvar{address},$webvar{ttl}); 540 539 if ($webvar{type} == $reverse_typemap{MX} or $webvar{type} == $reverse_typemap{SRV}) { 541 540 push @recargs, $webvar{distance}; … … 548 547 my ($code,$msg) = addRec(@recargs); 549 548 550 if ($code eq 'OK') { 549 if ($code eq 'OK' || $code eq 'WARN') { 550 my $restr; 551 551 if ($webvar{defrec} eq 'y') { 552 my$restr = "Added default record '$webvar{name} $typemap{$webvar{type}}";552 $restr = "Added default record '$webvar{name} $typemap{$webvar{type}}"; 553 553 $restr .= " [distance $webvar{distance}]" if $typemap{$webvar{type}} eq 'MX'; 554 554 $restr .= " [priority $webvar{distance}] [weight $webvar{weight}] [port $webvar{port}]" … … 556 556 $restr .= " $webvar{address}', TTL $webvar{ttl}"; 557 557 logaction(0, $session->param("username"), $webvar{parentid}, $restr); 558 changepage(page => "reclist", id => $webvar{parentid}, defrec => $webvar{defrec}, resultmsg => $restr);559 558 } else { 560 my$restr = "Added record '$webvar{name} $typemap{$webvar{type}}";559 $restr = "Added record '$webvar{name} $typemap{$webvar{type}}"; 561 560 $restr .= " [distance $webvar{distance}]" if $typemap{$webvar{type}} eq 'MX'; 562 561 $restr .= " [priority $webvar{distance}] [weight $webvar{weight}] [port $webvar{port}]" … … 564 563 $restr .= " $webvar{address}', TTL $webvar{ttl}"; 565 564 logaction($webvar{parentid}, $session->param("username"), parentID($webvar{parentid}, 'dom', 'group'), $restr); 566 changepage(page => "reclist", id => $webvar{parentid}, defrec => $webvar{defrec}, resultmsg => $restr); 567 } 565 } 566 my %pageparams = (page => "reclist", id => $webvar{parentid}, 567 defrec => $webvar{defrec}, revrec => $webvar{revrec}); 568 $pageparams{warnmsg} = $msg."<br><br>\n".$restr if $code eq 'WARN'; 569 $pageparams{resultmsg} = $restr if $code eq 'OK'; 570 changepage(%pageparams); 568 571 } else { 569 572 $page->param(failed => 1); … … 574 577 $page->param(parentid => $webvar{parentid}); 575 578 $page->param(defrec => $webvar{defrec}); 579 $page->param(revrec => $webvar{revrec}); 576 580 $page->param(id => $webvar{id}); 581 $page->param(fwdzone => $webvar{revrec} eq 'n'); 577 582 fill_recdata(); # populate the form... er, mostly. 578 583 $page->param(name => $webvar{name}); -
trunk/templates/reclist.tmpl
r224 r234 8 8 <div class="result"><TMPL_VAR NAME=resultmsg></div> 9 9 </TMPL_IF> 10 <TMPL_IF warnmsg> 11 <div class="warn"><TMPL_VAR NAME=warnmsg></div> 12 </TMPL_IF> 10 13 <TMPL_IF errmsg> 11 <div class= 'errmsg'><TMPL_VAR NAME=errmsg></div>14 <div class="errmsg"><TMPL_VAR NAME=errmsg></div> 12 15 </TMPL_IF> 13 16 -
trunk/templates/record.tmpl
r226 r234 24 24 25 25 <table border="0" cellspacing="2" cellpadding="2" width="100%"> 26 <TMPL_IF failed> <tr><td class="errhead" colspan="2">Error <TMPL_VAR NAME=wastrying> record: <TMPL_VAR NAME=errmsg></td></tr></TMPL_IF>26 <TMPL_IF failed> <tr><td class="errhead" colspan="2">Error <TMPL_VAR NAME=wastrying> record: <TMPL_VAR NAME=errmsg></td></tr></TMPL_IF> 27 27 <tr class="tableheader"><td align="center" colspan="2"><TMPL_VAR NAME=todo>: <TMPL_VAR NAME=dohere></td></tr> 28 28 <tr class="datalinelight">
Note:
See TracChangeset
for help on using the changeset viewer.