#!/usr/local/bin/perl
#
# Copyright 1994 Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission.  M.I.T. makes no representations about the
# suitability of this software for any purpose.  It is provided "as is"
# without express or implied warranty.
#
#

# The number of backup copies to keep of the DNS master files.
$backups = 9;

# The minimum host number within a subnet that is allowed to be
# automatically chosen.
$min_host = 11;

# Likely locations of named.boot.
@named_boot = ('/etc/named.boot',
	       '/etc/namedb/named.boot');

# Location of sendmail.
$sendmail = '/usr/lib/sendmail -t';

# Location to put dbm files
$dbm_dir = '/var/webdns';

###############################################################################

$LOCK_EX = 2;
$LOCK_UN = 8;

sub add_to_domains_to_store {
  local($key) = shift;
  local($domain);
  ($domain) = split(/;/,$key);
  if (!$domains{$domain}) {
    $domains{$domain} = 1;
    push(@domains_to_store,$domain);
  }
}

sub domains_to_store {
  if (!defined(@domains_to_store)) {
    local($key);
    @domains_to_store = ();
    while (($key) = each in_soa) { &add_to_domains_to_store($key); }
    while (($key) = each in_a) { &add_to_domains_to_store($key); }
    while (($key) = each in_cname) { &add_to_domains_to_store($key); }
    while (($key) = each in_hinfo) { &add_to_domains_to_store($key); }
    while (($key) = each in_ptr) { &add_to_domains_to_store($key); }
    while (($key) = each in_wks) { &add_to_domains_to_store($key); }
    while (($key) = each in_ns) { &add_to_domains_to_store($key); }
    while (($key) = each in_mx) { &add_to_domains_to_store($key); }
    while (($key) = each in_mb) { &add_to_domains_to_store($key); }
    while (($key) = each in_mg) { &add_to_domains_to_store($key); }
    while (($key) = each in_mr) { &add_to_domains_to_store($key); }
    while (($key) = each in_null) { &add_to_domains_to_store($key); }
    while (($key) = each in_minfo) { &add_to_domains_to_store($key); }
    while (($key) = each in_txt) { &add_to_domains_to_store($key); }
  }
  return @domains_to_store;
}

sub store {
  local($file,$origin) = @_;
  local($substr) = '.'.$origin;
  local($domain,$i);
  open($file,'>'.$file) 
    || die('Cannot open ',$file,': ',$!,"\n");
  eval {
    foreach $domain (&domains_to_store) {
      if ($domain eq $origin || (index($domain,$substr) > 0)) {
	for ($i = 0; $rdata=$in_soa{$domain.';'.$i}; $i++) {
	  print($file ($domain,' SOA ',$rdata,"\n"));
	}
	for ($i = 0; $rdata=$in_ns{$domain.';'.$i}; $i++) {
	  print($file ($domain,' NS ',$rdata,"\n"));
	}
	for ($i = 0; $rdata=$in_hinfo{$domain.';'.$i}; $i++) {
	  print($file ($domain,' HINFO ',$rdata,"\n"));
	}
	for ($i = 0; $rdata=$in_wks{$domain.';'.$i}; $i++) {
	  print($file ($domain,' WKS ',$rdata,"\n"));
	}
	for ($i = 0; $rdata=$in_a{$domain.';'.$i}; $i++) {
	  print($file ($domain,' A ',$rdata,"\n"));
	}
	for ($i = 0; $rdata=$in_cname{$domain.';'.$i}; $i++) {
	  print($file ($domain,' CNAME ',$rdata,"\n"));
	}
	for ($i = 0; $rdata=$in_ptr{$domain.';'.$i}; $i++) {
	  print($file ($domain,' PTR ',$rdata,"\n"));
	}
	for ($i = 0; $rdata=$in_mx{$domain.';'.$i}; $i++) {
	  print($file ($domain,' MX ',$rdata,"\n"));
	}
	for ($i = 0; $rdata=$in_mb{$domain.';'.$i}; $i++) {
	  print($file ($domain,' MB ',$rdata,"\n"));
	}
	for ($i = 0; $rdata=$in_mg{$domain.';'.$i}; $i++) {
	  print($file ($domain,' MG ',$rdata,"\n"));
	}
	for ($i = 0; $rdata=$in_mr{$domain.';'.$i}; $i++) {
	  print($file ($domain,' MR ',$rdata,"\n"));
	}
	for ($i = 0; $rdata=$in_minfo{$domain.';'.$i}; $i++) {
	  print($file ($domain,' MINFO ',$rdata,"\n"));
	}
	for ($i = 0; $rdata=$in_null{$domain.';'.$i}; $i++) {
	  print($file ($domain,' NULL ',"\n"));
	}
	for ($i = 0; $rdata=$in_txt{$domain.';'.$i}; $i++) {
	  print($file ($domain,' TXT ',$rdata,"\n"));
	}
      }
    }
  };
  close($file);
  if ($@) { die($@); }
}

sub increment_soa_serial {
  local($key,$rdata,@rdata);
  local($master,$mailbox,$serial,$paren,$refresh,$retry,$expire,$minimum);
  foreach $key (keys(%in_soa)) { 
    $rdata = $in_soa{$key};
    @rdata = split(/ /,$rdata);
    $master = shift(@rdata);
    $mailbox = shift(@rdata);
    $serial = shift(@rdata);
    $paren = ($serial eq '(');
    if ($paren) { $serial = shift(@rdata); }
    $refresh = shift(@rdata);
    $retry = shift(@rdata);
    $expire = shift(@rdata);
    $minimum = shift(@rdata);
    $serial += 1;
    if ($paren) {
      $in_soa{$key} =
	join(' ',($master,$mailbox,
		  '(',$serial,$refresh,$retry,$expire,$minimum,')'));
    } else {
      $in_soa{$key} =
	join(' ',($master,$mailbox,
		  $serial,$refresh,$retry,$expire,$minimum));
    }
  }
}

sub store_all {
  &increment_soa_serial;
  local($file_info,$directory,$domain,$file);
  foreach $file_info (@file_info) {
    ($directory,$domain,$file) = split(/ /,$file_info);
    chdir($directory);
    unlink($file.'.new');
    &store($file.'.new',$domain);
  }
  if ($backups > 0) {
    foreach $file_info (@file_info) {
      ($directory,$domain,$file) = split(/ /,$file_info);
      chdir($directory);
      unlink($file.'.'.$backups); 
    }
    for (local($i) = $backups; $i > 1; $i -= 1) {
      foreach $file_info (@file_info) {
	($directory,$domain,$file) = split(/ /,$file_info);
	chdir($directory);
	rename($file.'.'.($i-1),$file.'.'.$i);
      }
    }
    foreach $file_info (@file_info) {
      ($directory,$domain,$file) = split(/ /,$file_info);
      chdir($directory);
      rename($file,$file.'.1'); 
    }
  } else {
    foreach $file_info (@file_info) {
      ($directory,$domain,$file) = split(/ /,$file_info);
      chdir($directory);
      unlink($file); 
    }
  }
  foreach $file_info (@file_info) {
    ($directory,$domain,$file) = split(/ /,$file_info);
    chdir($directory);
    rename($file.'.new',$file); 
  }
  &update_dbm_time;
}

sub domain {
  local($_,$domain,$origin) = @_;
  y/A-Z/a-z/;
  if ($_ eq '@') { return $origin };
  if ($_ eq '') { return $domain };
  if ($_ eq '.') { return $domain };
  if ($_ eq '..') { return '' };
  if (/\.$/) { return $_; }
  return($_.'.'.$origin);
}

sub load_rr {
  local($domain,$origin,@_) = @_;
  local($i);
  $_ = shift;
  $domain = &domain($_,$domain,$origin);
  $_ = shift;
  if (/[0-9]+/) { $ttl = $_; $_ = shift; } else { $ttl = 0; }
  if (/^(IN)$/) { $class = $_; $_ = shift; } else { $class = 'IN'; }
  $type = $_; 
  if ($type eq 'SOA') {
    local($master) = &domain(shift,$domain,$origin); 
    local($mailbox) = &domain(shift,$domain,$origin); 
    local($serial) = shift; 
    local($paren) = ($serial eq '(');
    if ($paren) { $serial = shift; }
    local($refresh) = shift; 
    local($retry) = shift; 
    local($expire) = shift; 
    local($minimum) = shift; 
    if ($in_soa{$domain.';0'}) { 
      warn('Duplicate SOA for ',$domain,"\n"); 
    } elsif ($paren) {
      $in_soa{$domain.';0'} =
	join(' ',($master,$mailbox,
		  '(',$serial,$refresh,$retry,$expire,$minimum,')'));
    } else {
      $in_soa{$domain.';0'} =
	join(' ',($master,$mailbox,
		  $serial,$refresh,$retry,$expire,$minimum));
    }
  } elsif ($type eq 'CNAME') {
    local($name) = &domain(shift,$domain,$origin);
    if ($in_cname{$domain.';0'}) {
      warn('Duplicate CNAME for ',$domain,"\n");
    } else {
      $in_cname{$domain.';0'} = $name;
    }
  } elsif ($type eq 'A') {
    local($address) = shift;
    for ($i = 0; $in_a{$domain.';'.$i}; $i++) { };
    $in_a{$domain.';'.$i} = $address;
  } elsif ($type eq 'HINFO') {
    local($cpu_type) = shift; 
    local($os_type) = shift;
    $cpu_type =~ y/A-Z/a-z/; 
    $os_type =~ y/A-Z/a-z/;
    if ($in_hinfo{$domain.';0'}) {
      warn('Duplicate HINFO for ',$domain,"\n");
    } else {
      $in_hinfo{$domain.';0'} = join(' ',($cpu_type,$os_type));
    }
  } elsif ($type eq 'PTR') {
    local($pointer) = &domain(shift,$domain,$origin);
    for ($i = 0; $in_ptr{$domain.';'.$i}; $i++) { };
    $in_ptr{$domain.';'.$i} = $pointer;
  } elsif ($type eq 'WKS') {
    local($address) = shift; 
    local($medium) = shift; 
    local($services) = join(' ',@_);
    for ($i = 0; $in_wks{$domain.';'.$i}; $i++) { };
    $in_wks{$domain.';'.$i} = join(' ',($address,$medium,$services)); 
  } elsif ($type eq 'NS') {
    local($server) = &domain(shift,$domain,$origin);
    for ($i = 0; $in_ns{$domain.';'.$i}; $i++) { };
    $in_ns{$domain.';'.$i} = $server;
  } elsif ($type eq 'MX') {
    local($preference) = shift; 
    local($mailer) = &domain(shift,$domain,$origin);
    for ($i = 0; $in_mx{$domain.';'.$i}; $i++) { };
    $in_mx{$domain.';'.$i} = join(' ',($preference,$mailer)); 
  } elsif ($type eq 'MB') {
    local($mailbox) = &domain(shift,$domain,$origin);
    if ($in_mb{$domain.';0'}) {
      warn('Duplicate MB for ',$domain,"\n");
    } else {
      $in_mb{$domain.';0'} = $mailbox;
    }
  } elsif ($type eq 'MG') {
    local($mailgroup) = &domain(shift,$domain,$origin);
    for ($i = 0; $in_mg{$domain.';'.$i}; $i++) { };
    $in_mg{$domain.';',$i} = $mailgroup;
  } elsif ($type eq 'MR') {
    local($mailrename) = &domain(shift,$domain,$origin);
    if ($in_mr{$domain.';0'}) {
      warn('Duplicate MR for ',$domain,"\n");
    } else {
      $in_mr{$domain.';0'} = $mailrename;
    }
  } elsif ($type eq 'NULL') {
    if ($in_null{$domain.';0'}) {
      warn('Duplicate NULL for ',$domain,"\n");
    } else {
      $in_null{$domain.';0'} = $domain;
    }
  } elsif ($type eq 'MINFO') {
    local($request) = &domain(shift,$domain,$origin);
    local($error) = &domain(shift,$domain,$origin);
    if ($in_minfo{$domain.';0'}) { 
      warn('Duplicate MINFO for ',$domain,"\n");
    } else {
      $in_minfo{$domain.';0'} = join(' ',($request,$error));
    }
  } elsif ($type eq 'TXT') {
    local($txt) = @_;
    for ($i = 0; $in_txt{$domain.';'.$i}; $i++) { };
    $in_txt{$domain.';'.$i} = $txt;
  } else {
    warn('Unknown RR type ',$type,' for ',$domain,"\n"); 
  }
  return $domain;
}

sub load {
  local($file,$origin) = @_;
  local($domain) = $origin;
  open($file,$file) 
    || die('Cannot open ',$file,': ',$!,"\n");
  eval {
    local($line) = '';
    local($parcnt) = 0;
    while (<$file>) {
      chop; s/;.*//; $line .= $_;
      if (/\(.*$/) { $parcnt += 1; }
      if (/\).*$/) { $parcnt -= 1; }
      if ($parcnt > 0) { next; }
      if ($line eq '') { next; }
      split(/\s+/,$line);
      $line = '';
      if (scalar(@_) == 0) { next; }
      $_ = shift;
      if ($_ eq '$INCLUDE') {
	&load(shift,&domain(shift,$domain,$origin)); 
      } elsif ($_ eq '$ORIGIN') {
	$origin = &domain(shift,$domain,$origin); 
	$domain = origin; 
      } else {
	$domain = &load_rr($domain,$origin,$_,@_);
      }
    }
  };
  close($file);
  if ($@) { die($@); }
}

sub load_all {
  local($file_info,$directory,$domain,$file);
  foreach $file_info (@file_info) {
    ($directory,$domain,$file) = split(/ /,$file_info);
    chdir($directory);
    &load($file,$domain);
  }
}

sub file_time {
  local($file_info,$directory,$domain,$file,@stat);
  local($time) = 0;
  foreach $file_info (@file_info) {
    ($directory,$domain,$file) = split(/ /,$file_info);
    chdir($directory);
    @stat = stat($file);
    if ($time < $stat[9]) { $time = $stat[9]; };
  }
  return $time;
}

@dbm_arrays = 
  ('in_soa','in_cname','in_a','in_hinfo','in_ptr','in_wks',
   'in_ns','in_mx','in_mb','in_mg','in_mr','in_null','in_minfo', 'in_txt');

sub dbm_time {
  local($time) = 2147483647;
  if ($in_soa{';;TIME'} < $time) { $time = $in_soa{';;TIME'}; }
  if ($in_cname{';;TIME'} < $time) { $time = $in_cname{';;TIME'}; }
  if ($in_a{';;TIME'} < $time) { $time = $in_a{';;TIME'}; }
  if ($in_hinfo{';;TIME'} < $time) { $time = $in_hinfo{';;TIME'}; }
  if ($in_ptr{';;TIME'} < $time) { $time = $in_ptr{';;TIME'}; }
  if ($in_wks{';;TIME'} < $time) { $time = $in_wks{';;TIME'}; }
  if ($in_ns{';;TIME'} < $time) { $time = $in_ns{';;TIME'}; }
  if ($in_mx{';;TIME'} < $time) { $time = $in_mx{';;TIME'}; }
  if ($in_mb{';;TIME'} < $time) { $time = $in_mb{';;TIME'}; }
  if ($in_mg{';;TIME'} < $time) { $time = $in_mg{';;TIME'}; }
  if ($in_mr{';;TIME'} < $time) { $time = $in_mr{';;TIME'}; }
  if ($in_null{';;TIME'} < $time) { $time = $in_null{';;TIME'}; }
  if ($in_minfo{';;TIME'} < $time) { $time = $in_minfo{';;TIME'}; }
  if ($in_txt{';;TIME'} < $time) { $time = $in_txt{';;TIME'}; }
  return $time;
}

sub update_dbm_time {
  $time = &file_time;
  $in_soa{';;TIME'} = $time;
  $in_cname{';;TIME'} = $time;
  $in_a{';;TIME'} = $time;
  $in_hinfo{';;TIME'} = $time;
  $in_ptr{';;TIME'} = $time;
  $in_wks{';;TIME'} = $time;
  $in_ns{';;TIME'} = $time;
  $in_mx{';;TIME'} = $time;
  $in_mb{';;TIME'} = $time;
  $in_mg{';;TIME'} = $time;
  $in_mr{';;TIME'} = $time;
  $in_null{';;TIME'} = $time;
  $in_minfo{';;TIME'} = $time;
  $in_txt{';;TIME'} = $time;
}

sub dbm_open {
  dbmopen(%in_soa,$dbm_dir.'/in_soa',0666)
    || warn('Cannot dbmopen ',$dbm_dir,'/in_soa: ',$!,"\n");
  dbmopen(%in_cname,$dbm_dir.'/in_cname',0666)
    || warn('Cannot dbmopen ',$dbm_dir,'/in_cname: ',$!,"\n");
  dbmopen(%in_a,$dbm_dir.'/in_a',0666)
    || warn('Cannot dbmopen ',$dbm_dir,'/in_a: ',$!,"\n");
  dbmopen(%in_hinfo,$dbm_dir.'/in_hinfo',0666)
    || warn('Cannot dbmopen ',$dbm_dir,'/in_hinfo: ',$!,"\n");
  dbmopen(%in_ptr,$dbm_dir.'/in_ptr',0666)
    || warn('Cannot dbmopen ',$dbm_dir,'/in_ptr: ',$!,"\n");
  dbmopen(%in_wks,$dbm_dir.'/in_wks',0666)
    || warn('Cannot dbmopen ',$dbm_dir,'/in_wks: ',$!,"\n");
  dbmopen(%in_ns,$dbm_dir.'/in_ns',0666)
    || warn('Cannot dbmopen ',$dbm_dir,'/in_ns: ',$!,"\n");
  dbmopen(%in_mx,$dbm_dir.'/in_mx',0666)
    || warn('Cannot dbmopen ',$dbm_dir,'/in_mx: ',$!,"\n");
  dbmopen(%in_mb,$dbm_dir.'/in_mb',0666)
    || warn('Cannot dbmopen ',$dbm_dir,'/in_mb: ',$!,"\n");
  dbmopen(%in_mg,$dbm_dir.'/in_mg',0666)
    || warn('Cannot dbmopen ',$dbm_dir,'/in_mg: ',$!,"\n");
  dbmopen(%in_mr,$dbm_dir.'/in_mr',0666)
    || warn('Cannot dbmopen ',$dbm_dir,'/in_mr: ',$!,"\n");
  dbmopen(%in_null,$dbm_dir.'/in_null',0666)
    || warn('Cannot dbmopen ',$dbm_dir,'/in_null: ',$!,"\n");
  dbmopen(%in_minfo,$dbm_dir.'/in_minfo',0666)
    || warn('Cannot dbmopen ',$dbm_dir,'/in_minfo: ',$!,"\n");
  dbmopen(%in_txt,$dbm_dir.'/in_txt',0666)
    || warn('Cannot dbmopen ',$dbm_dir,'/in_txt: ',$!,"\n");
}

sub dbm_close {
  dbmclose(%in_soa);
  dbmclose(%in_cname);
  dbmclose(%in_a);
  dbmclose(%in_hinfo);
  dbmclose(%in_ptr);
  dbmclose(%in_wks);
  dbmclose(%in_ns);
  dbmclose(%in_mx);
  dbmclose(%in_mb);
  dbmclose(%in_mg);
  dbmclose(%in_mr);
  dbmclose(%in_null);
  dbmclose(%in_minfo);
  dbmclose(%in_txt);
}

sub dbm_delete {
  local($dbm_array);
  foreach $dbm_array (@dbm_arrays) {
    unlink($dbm_dir.'/'.$dbm_array.'.dir');
    unlink($dbm_dir.'/'.$dbm_array.'.pag');
  }
}

sub start_dbm_update {
  open(UPDATING,'>'.$dbm_dir.'/updating')
    || warn('Cannot create ',$dbm_dir,'/updating: ',$!,"\n");
  close(UPDATING);
}

sub end_dbm_update {
  unlink($dbm_dir.'/updating');
}

sub dbm_update_incomplete {
  return stat($dbm_dir.'/updating');
}

sub open_db {
  &dbm_open;
  local($rebuild) = 0;
  if (&file_time != &dbm_time) {
    warn("Rebuilt dbm files because DNS master files have changed.\n");
    $rebuild = 1;
  }
  if (&dbm_update_incomplete) {
    warn("Rebuilt dbm files because the last update had not completed.\n");
    $rebuild = 1;
  }
  if ($rebuild) {
    &dbm_close;
    &dbm_delete;
    mkdir($dbm_dir,0777);
    &dbm_open;
    &start_dbm_update;
    &load_all;
    &update_dbm_time;
    &dbm_close;
    &end_dbm_update;
    &dbm_open;
  }
}

sub close_db {
  &dbm_close;
  &end_dbm_update;
}

sub delete_rrs {
  local($domain) = shift;
  local($extent) = shift;
  local($i,$j,$k,$dom,@doms,$rdata,@rdata);
  if (($extent eq 'host') || ($extent eq 'all')) {
    local($os_type);
    for ($i = 0; $rdata=$in_hinfo{$domain.';'.$i}; $i++) {
      undef($in_hinfo{$domain.';'.$i});
    }
    for ($i = 0; $rdata=$in_wks{$domain.';'.$i}; $i++) {
      undef($in_wks{$domain.';'.$i});
    }
    for ($i = 0; $rdata=$in_a{$domain.';'.$i}; $i++) {
      undef($in_a{$domain.';'.$i});
    }
    for ($i = 0; $rdata=$in_cname{$domain.';'.$i}; $i++) {
      undef($in_cname{$domain.';'.$i});
    }
    for ($i = 0; $rdata=$in_ptr{$domain.';'.$i}; $i++) {
      undef($in_ptr{$domain.';'.$i});
    }
    for ($i = 0; $rdata=$in_mx{$domain.';'.$i}; $i++) {
      undef($in_mx{$domain.';'.$i});
    }
    @doms = ();
    while (($key,$rdata) = each in_cname) { 
      if ($rdata eq $domain) {
	($dom) = split(/;/,$key);
	push(@doms,$dom);
      }
    }
    foreach $dom (@doms) {
      for ($j = 0, $k = 0; $rdata=$in_cname{$dom.';'.$j}; $j++) {
	undef($in_cname{$dom.';'.$j});
	if ($rdata ne $domain) { $in_cname{$dom.';'.$k++} = $rdata; }
      }
    }
    @doms = ();
    while (($key,$rdata) = each in_ptr) { 
      if ($rdata eq $domain) {
	($dom) = split(/;/,$key);
	if (index($dom,'.in-addr.arpa.') > 0) {
	  push(@doms,$dom);
	}
      }
    }
    foreach $dom (@doms) {
      for ($j = 0, $k = 0; $rdata=$in_ptr{$dom.';'.$j}; $j++) {
	undef($in_ptr{$dom.';'.$j});
	if ($rdata ne $domain) { $in_ptr{$dom.';'.$k++} = $rdata; }
      }
    }
  }
  if ($extent eq 'all') {
    for ($i = 0; $rdata=$in_soa{$domain.';'.$i}; $i++) {
      undef($in_soa{$domain.';'.$i});
    }
    for ($i = 0; $rdata=$in_ns{$domain.';'.$i}; $i++) {
      undef($in_ns{$domain.';'.$i});
    }
    for ($i = 0; $rdata=$in_mb{$domain.';'.$i}; $i++) {
      undef($in_mb{$domain.';'.$i});
    }
    for ($i = 0; $rdata=$in_mg{$domain.';'.$i}; $i++) {
      undef($in_mg{$domain.';'.$i});
    }
    for ($i = 0; $rdata=$in_mr{$domain.';'.$i}; $i++) {
      undef($in_mr{$domain.';'.$i});
    }
    for ($i = 0; $rdata=$in_minfo{$domain.';'.$i}; $i++) {
      undef($in_minfo{$domain.';'.$i});
    }
    for ($i = 0; $rdata=$in_null{$domain.';'.$i}; $i++) {
      undef($in_null{$domain.';'.$i});
    }
    for ($i = 0; $rdata=$in_txt{$domain.';'.$i}; $i++) {
      undef($in_txt{$domain.';'.$i});
    }
    local($master,$mailbox);
    @doms = ();
    while (($key,$rdata) = each in_soa) { 
      @rdata = split(/ /,$rdata);
      $master = shift(@rdata);
      $mailbox = shift(@rdata);
      if (($master eq $domain) || ($mailbox eq $domain)) {
	($dom) = split(/;/,$key);
	push(@doms,$dom);
      }
    }
    foreach $dom (@doms) {
      for ($j = 0, $k = 0; $rdata=$in_soa{$dom.';'.$j}; $j++) {
	undef($in_soa{$dom.';'.$j});
	@rdata = split(/ /,$rdata);
	$master = shift(@rdata);
	$mailbox = shift(@rdata);
	if (($master ne $domain) && ($mailbox ne $domain)) { 
	  $in_soa{$dom.';'.$k++} = $rdata; 
	}
      }
    }
    @doms = ();
    while (($key,$rdata) = each in_ns) { 
      if ($rdata eq $domain) {
	($dom) = split(/;/,$key);
	push(@doms,$dom);
      }
    }
    foreach $dom (@doms) {
      for ($j = 0, $k = 0; $rdata=$in_ns{$dom.';'.$j}; $j++) {
	undef($in_ns{$dom.';'.$j});
	if ($rdata ne $domain) { 
	  $in_ns{$dom.';'.$k++} = $rdata; }
      }
    }
    @doms = ();
    while (($key,$rdata) = each in_ptr) { 
      if ($rdata eq $domain) {
	($dom) = split(/;/,$key);
	push(@doms,$dom);
      }
    }
    foreach $dom (@doms) {
      for ($j = 0, $k = 0; $rdata=$in_ptr{$dom.';'.$j}; $j++) {
	undef($in_ptr{$dom.';'.$j});
	if ($rdata ne $domain) { $in_ptr{$dom.';'.$k++} = $rdata; }
      }
    }
    local($preference,$mailer);
    @doms = ();
    while (($key,$rdata) = each in_mx) { 
      @rdata = split(/ /,$rdata);
      $preference = shift(@rdata); 
      $mailer = shift(@rdata);
      if ($mailer eq $domain) {
	($dom) = split(/;/,$key);
	push(@doms,$dom);
      }
    }
    foreach $dom (@doms) {
      for ($j = 0, $k = 0; $rdata=$in_mx{$dom.';'.$j}; $j++) {
	undef($in_mx{$dom.';'.$j});
	@rdata = split(/ /,$rdata);
	$preference = shift(@rdata); 
	$mailer = shift(@rdata);
	if ($mailer ne $domain) { $in_mx{$dom.';'.$k++} = $rdata; }
      }
    }
    @doms = ();
    while (($key,$rdata) = each in_mb) { 
      if ($rdata eq $domain) {
	($dom) = split(/;/,$key);
	push(@doms,$dom);
      }
    }
    foreach $dom (@doms) {
      for ($j = 0, $k = 0; $rdata=$in_mb{$dom.';'.$j}; $j++) {
	undef($in_mb{$dom.';'.$j});
	if ($rdata ne $domain) { $in_mb{$dom.';'.$k++} = $rdata; }
      }
    }
    @doms = ();
    while (($key,$rdata) = each in_mg) { 
      if ($rdata eq $domain) {
	($dom) = split(/;/,$key);
	push(@doms,$dom);
      }
    }
    foreach $dom (@doms) {
      for ($j = 0, $k = 0; $rdata=$in_mg{$dom.';'.$j}; $j++) {
	undef($in_mg{$dom.';'.$j});
	if ($rdata ne $domain) { $in_mg{$dom.';'.$k++} = $rdata; }
      }
    }
    @doms = ();
    while (($key,$rdata) = each in_mr) { 
      if ($rdata eq $domain) {
	($dom) = split(/;/,$key);
	push(@doms,$dom);
      }
    }
    foreach $dom (@doms) {
      for ($j = 0, $k = 0; $rdata=$in_mr{$dom.';'.$j}; $j++) {
	undef($in_mr{$dom.';'.$j});
	if ($rdata ne $domain) { $in_mr{$dom.';'.$k++} = $rdata; }
      }
    }
    local($request,$error);
    @doms = ();
    while (($key,$rdata) = each in_minfo) { 
      @rdata = split(/ /,$rdata);
      $request = shift(@rdata); 
      $error = shift(@rdata);
      if (($request eq $domain) || ($error eq $domain)) {
	($dom) = split(/;/,$key);
	push(@doms,$dom);
      }
    }
    foreach $dom (@doms) {
      for ($j = 0, $k = 0; $rdata=$in_minfo{$dom.';'.$j}; $j++) {
	undef($in_minfo{$rdata.';'.$j});
	@rdata = split(/ /,$rdata);
	$request = shift(@rdata); 
	$error = shift(@rdata);
	if ($request ne $domain) { $in_minfo{$dom.';'.$k++} = $rdata; }
      }
    }
  }
}

sub print_rrs {
  local($file) = shift;
  local($domain) = shift;
  local($i,$key,$dom,$rdata,@rdata);
  for ($i = 0; $rdata=$in_soa{$domain.';'.$i}; $i++) {
    print($file ($domain,' SOA ',$rdata,"\n"));
  }
  for ($i = 0; $rdata=$in_ns{$domain.';'.$i}; $i++) {
    print($file ($domain,' NS ',$rdata,"\n"));
  }
  for ($i = 0; $rdata=$in_hinfo{$domain.';'.$i}; $i++) {
    print($file ($domain,' HINFO ',$rdata,"\n"));
  }
  for ($i = 0; $rdata=$in_wks{$domain.';'.$i}; $i++) {
    print($file ($domain,' WKS ',$rdata,"\n"));
  }
  for ($i = 0; $rdata=$in_a{$domain.';'.$i}; $i++) {
    print($file ($domain,' A ',$rdata,"\n"));
  }
  for ($i = 0; $rdata=$in_cname{$domain.';'.$i}; $i++) {
    print($file ($domain,' CNAME ',$rdata,"\n"));
  }
  for ($i = 0; $rdata=$in_ptr{$domain.';'.$i}; $i++) {
    print($file ($domain,' PTR ',$rdata,"\n"));
  }
  for ($i = 0; $rdata=$in_mx{$domain.';'.$i}; $i++) {
    print($file ($domain,' MX ',$rdata,"\n"));
  }
  for ($i = 0; $rdata=$in_mb{$domain.';'.$i}; $i++) {
    print($file ($domain,' MB ',$rdata,"\n"));
  }
  for ($i = 0; $rdata=$in_mg{$domain.';'.$i}; $i++) {
    print($file ($domain,' MG ',$rdata,"\n"));
  }
  for ($i = 0; $rdata=$in_mr{$domain.';'.$i}; $i++) {
    print($file ($domain,' MR ',$rdata,"\n"));
  }
  for ($i = 0; $rdata=$in_minfo{$domain.';'.$i}; $i++) {
    print($file ($domain,' MINFO ',$rdata,"\n"));
  }
  for ($i = 0; $rdata=$in_null{$domain.';'.$i}; $i++) {
    print($file ($domain,' NULL',"\n"));
  }
  for ($i = 0; $rdata=$in_txt{$domain.';'.$i}; $i++) {
    print($file ($domain,' TXT ',$rdata,"\n"));
  }
  while (($key,$rdata) = each in_cname) { 
    if ($rdata eq $domain) {
      ($dom) = split(/;/,$key);
      if ($dom ne $domain) {
	print($file ($dom,' CNAME ',$rdata,"\n"));
      }
    }
  }
  while (($key,$rdata) = each in_ptr) { 
    if ($rdata eq $domain) {
      ($dom) = split(/;/,$key);
      if ($dom ne $domain) {
	print($file ($dom,' PTR ',$rdata,"\n"));
      }
    }
  }
  local($master,$mailbox);
  while (($key,$rdata) = each in_soa) { 
    @rdata = split(/ /,$rdata);
    $master = shift(@rdata);
    $mailbox = shift(@rdata);
    if (($master eq $domain) || ($mailbox eq $domain)) {
      ($dom) = split(/;/,$key);
      if ($dom ne $domain) {
	print($file ($dom,' SOA ',$rdata,"\n"));
      }
    }
  }
  while (($key,$rdata) = each in_ns) { 
    if ($rdata eq $domain) {
      ($dom) = split(/;/,$key);
      if ($dom ne $domain) {
	print($file ($dom,' NS ',$rdata,"\n"));
      }
    }
  }
  local($preference,$mailer);
  while (($key,$rdata) = each in_mx) { 
    @rdata = split(/ /,$rdata);
    $preference = shift(@rdata); 
    $mailer = shift(@rdata);
    if ($mailer eq $domain) {
      ($dom) = split(/;/,$key);
      if ($dom ne $domain) {
	print($file ($dom,' MX ',$rdata,"\n"));
      }
    }
  }
  while (($key,$rdata) = each in_mb) { 
    if ($rdata eq $domain) {
      ($dom) = split(/;/,$key);
      if ($dom ne $domain) {
	print($file ($dom,' MB ',$rdata,"\n"));
      }
    }
  }
  while (($key,$rdata) = each in_mg) { 
    if ($rdata eq $domain) {
      ($dom) = split(/;/,$key);
      if ($dom ne $domain) {
	print($file ($dom,' MG ',$rdata,"\n"));
      }
    }
  }
  while (($key,$rdata) = each in_mr) { 
    if ($rdata eq $domain) {
      ($dom) = split(/;/,$key);
      if ($dom ne $domain) {
	print($file ($dom,' MR ',$rdata,"\n"));
      }
    }
  }
  local($request,$error);
  while (($key,$rdata) = each in_minfo) { 
    @rdata = split(/ /,$rdata);
    $request = shift(@rdata); 
    $error = shift(@rdata);
    if (($request eq $domain) || ($error eq $domain)) {
      ($dom) = split(/;/,$key);
      if ($dom ne $domain) {
	print($file ($dom,' MINFO ',$rdata,"\n")); 
      }
    }
  }
}

sub tmpnam {
  if (!$tmpcnt) { $tmpcnt = 1; } else { $tmpcnt += 1; }
  local(@name) = split(/\//,$ENV{'SCRIPT_NAME'});
  return '/tmp/'.pop(@name).'.'.$$.'.'.$tmpcnt;
}

sub authorized {
  return $in_a{&domain($ENV{'REMOTE_HOST'}).';0'};
}

sub print_host {
  local($file) = shift;
  local($domain) = shift;
  local($dom,$i,$rdata,@rdata);
  local($a) = 0;
  local($cname) = 0;
  local($mx) = 0;
  local($hinfo) = 0;
  for ($i = 0; $rdata=$in_a{$domain.';'.$i}; $i++) {
    if (!$a++) { print($file "Network Addresses:\n"); }
    print($file '    ',$rdata,"\n");
  }
  while (($key,$rdata) = each in_cname) { 
    if ($rdata eq $domain) {
      ($dom) = split(/;/,$key);
      if (!$cname++) { print($file "Nicknames:\n"); }
      print($file '    ',$dom,"\n");
    }
  }
  local($preference,$mailer);
  for ($i = 0; $rdata=$in_mx{$domain.';'.$i}; $i++) {
    @rdata = split(/ /,$rdata);
    $preference = shift(@rdata); 
    $mailer = shift(@rdata);
    if (!$mx++) { print($file "Mail Exchangers:\n"); }
    print($file '    ',$mailer,"\n");
  }
  local($cpu_type,$os_type,$ct,$ot);
  for ($i = 0; $rdata=$in_hinfo{$domain.';'.$i}; $i++) {
    @rdata = split(/ /,$rdata);
    $cpu_type = shift(@rdata);
    $os_type = shift(@rdata);
    if (!$hinfo++) { 
      print($file "Type of Computer and Operating System:\n"); 
    }
    print($file '    ',$cpu_type,' ',$os_type,"\n");
  }
  local($address,$medium,$service,%tcp_services,%udp_services);
  for ($i = 0; $rdata=$in_wks{$domain.';'.$i}; $i++) {
    @rdata = split(/ /,$rdata);
    $address = shift(@rdata); 
    $medium = shift(@rdata); 
    if ($medium eq 'tcp' || $medium eq 'TCP') {
      foreach $service (@rdata) { $tcp_services{$service} = 1; }
    } elsif ($medium eq 'udp' || $medium eq 'UDP') {
      foreach $service (@rdata) { $udp_services{$service} = 1; }
    }
  }
  local(@tcp_services) = sort(keys(%tcp_services));
  if (@tcp_services) {
    print($file "Services over TCP:\n    ");
    foreach $service (@tcp_services) {
      print($file $service,' ');
    }
    print($file "\n");
  }
  local(@udp_services) = sort(keys(%udp_services));
  if (@udp_services) {
    print($file "Services over UDP:\n    ");
    foreach $service (@udp_services) {
      print($file $service,' ');
    }
    print($file "\n");
  }
}

sub dom_first {
  local($domain) = shift;
  local(@domain) = split(/\./,$domain);
  return shift(@domain);
}

sub dom_rest {
  local($domain) = shift;
  local(@domain) = split(/\./,$domain);
  shift(@domain);
  return join('.',@domain);
}

sub dom_nodot {
  local($domain) = shift;
  local(@domain) = split(/\./,$domain);
  return join('.',@domain);
}

sub mailaddr {
  local($domain) = &dom_nodot(shift);
  local($i,$j,$rdata,@rdata,$master,$mailbox,$mailer);
  while ($domain) {
    for ($i = 0; $rdata=$in_soa{$domain.';'.$i}; $i++) {
      @rdata = split(/ /,$rdata);
      $master = shift(@rdata);
      $mailbox = shift(@rdata);
      for ($j = 0; $rdata=$in_mb{$domain.';'.$j}; $j++) {
	$mailer = &dom_nodot($mailer);
	$mailbox = &dom_first($mailbox);
	return $mailbox.'@'.$mailer;
      }
      $mailer = &dom_rest($mailbox);
      $mailbox = &dom_first($mailbox);
      return $mailbox.'@'.$mailer;
    }
    $domain = &dom_rest($domain);
  }
  return 'webdns';
}

sub mail {
  local($domain) = shift;
  local($operation) = shift;
  local($mailaddr) = &mailaddr($domain);
  if (open(MAIL,'|'.$sendmail)) {
    eval {
      print(MAIL ('To: ',$mailaddr,"\n"));
      print(MAIL ('From: ',$ENV{'USER'},'@',$ENV{'SERVER_NAME'}));
      print(MAIL (" (The WEBDNS Program)\n"));
      print(MAIL ('Subject: ',&dom_nodot($domain),' ',$operation));
      print(MAIL (' by someone on ',$ENV{'REMOTE_HOST'},"\n"));
      print(MAIL ("\n"));
      if ($content{'remarks'}) {
	print(MAIL ($content{'remarks'},"\n\n"));
      }
      &print_rrs('MAIL',$domain);
    };
    close(MAIL);
    if ($@) { warn(("Error sending mail for change log:\n",$@)); }
  }
}

sub print_update_message {
  if ($content{'store'}) { 
    print("<H2>DNS Master Files Modified</H2>\n");
    print("The DNS master files have been modified\n");
    print("to reflect the changes you have made.\n");
  } else {
    print("<H2>Only dbm Cache Files Modified</H2>\n");
    print("Only the dbm files that are used as a cache\n");
    print("by WEBDNS have been modified\n");
    print("to reflect the changes you have made.\n");
    print("To also update the DNS master files, go back and\n");
    print("re-submit with the ``Update dbm files,\n");
    print("then regenerate DNS master files from dbm files''\n");
    print("submit option.\n");
  }
}

sub update_rrs_host {
  local($domain) = shift;
  local($errcnt) = 0;
  local(@rrs) = ();
  local($primary_addr) = '';
  local($dom,$addr,$i,$j,$key,$rdata);
  for ($i = 0; $rdata=$in_cname{$domain.';'.$i}; $i++) {
    if ($rdata ne $domain) {
      print('Nickname ',&dom_nodot($domain)," is already assigned to\n");
      print('<A HREF="',$ENV{'SCRIPT_NAME'},'/edit?domain=',$rdata,'">');
      print(&dom_nodot($rdata),"</A>.<BR>\n");
      $errcnt += 1;
    }
  }
  for ($i = 0; defined($content{'addr'.$i}); $i += 1) {
    ($addr) = split(' ',$content{'addr'.$i});
    if ($addr) { 
      while (($key,$rdata) = each in_a) { 
	if ($rdata eq $addr) {
	  ($dom) = split(/;/,$key);
	  if ($dom ne $domain) {
	    print('Network address ',$addr," is already assigned to\n");
	    print('<A HREF="',$ENV{'SCRIPT_NAME'},'/edit?domain=',$dom,'">');
	    print(&dom_nodot($dom),"</A>.<BR>\n");
	    $errcnt += 1;
	  }
	}
      }
      if ($primary_addr eq '') { $primary_addr = $addr; }
      push(@rrs,join(' ',($domain,'A',$addr))); 
      $dom = join('.',reverse(split(/\./,$addr))).'.in-addr.arpa.';
      push(@rrs,join(' ',($dom,'PTR',$domain)));
    }
  }
  local($name);
  for ($i = 0; defined($content{'name'.$i}); $i += 1) {
    ($name) = split(' ',$content{'name'.$i});
    if ($name) { 
      $name = &domain($name);
      for ($j = 0; $rdata=$in_cname{$name.';'.$j}; $j++) {
	if ($rdata ne $domain) {
	  print('Nickname ',&dom_nodot($name)," is already assigned to\n");
	  print('<A HREF="',$ENV{'SCRIPT_NAME'},'/edit?domain=',$rdata,'">');
	  print(&dom_nodot($rdata),"</A>.<BR>\n");
	  $errcnt += 1;
	}
      }
      push(@rrs,join(' ',($name,'CNAME',$domain))); 
    }
  }
  local($mailer);
  for ($i = 0; defined($content{'mx'.$i}); $i += 1) {
    ($mailer) = split(' ',$content{'mx'.$i});
    if ($mailer) { 
      $mailer = &domain($mailer);
      push(@rrs,join(' ',($domain,'MX',$i+1,$mailer))); 
    }
  }
  local($cpu_type) = $content{'cpu_type'};
  if ($cpu_type eq 'Other, Specified Below:') { 
    ($cpu_type) = split(' ',$content{'other_cpu_type'});
  }
  local($os_type) = $content{'os_type'};
  if ($os_type eq 'Other, Specified Below:') { 
    ($os_type) = split(' ',$content{'other_os_type'});
  }
  if (($cpu_type) && ($os_type)) {
    push(@rrs,join(' ',($domain,'HINFO',$cpu_type,$os_type)));
  }
  local($tcp_services) = $content{'tcp_services'};
  if (($tcp_services) && ($primary_addr)) {
    push(@rrs,join(' ',($domain,'WKS',$primary_addr,'tcp',
			split(/\;/,$tcp_services))));
  }
  local($udp_services) = $content{'udp_services'};
  if (($udp_services) && ($primary_addr)) {
    push(@rrs,join(' ',($domain,'WKS',$primary_addr,'udp',
			split(/\;/,$udp_services))));
  }
  if ($errcnt > 0) {
    print("<H2>DNS Files Not Modified</H2>\n");
    print("The DNS master files were not modified\n");
    print("because problems were detected.\n");
    print("If you want to force a change,\n");
    print("edit the raw DNS resource records for\n");
    print('<A HREF="',$ENV{'SCRIPT_NAME'},'/raw?domain=',$domain,'">');
    print(&dom_nodot($domain),"</A>.\n");
  } elsif (&authorized) {
    &start_dbm_update;
    &delete_rrs($domain,'host');
    foreach $rr (@rrs) { &load_rr('','',split(/ /,$rr)); }
    if ($content{'store'}) { &store_all; }
    &mail($domain,'updated'); 
    &print_update_message;
    print('<H2>DNS Data For Host ');
    print('<A HREF="',$ENV{'SCRIPT_NAME'},'/edit?domain=',$domain,'">');
    print(&dom_nodot($domain),"</A></H2>\n");
    print('<PRE>');
    &print_host('STDOUT',$domain);
    print('</PRE>');
  } else {
    print("<H2>DNS Files Not Modified</H2>\n");
    print("The DNS master files were not modified\n");
    print("because you are not authorized to change them.\n");
  }
}

sub parse_inaddr {
  local($addr) = shift; 
  local(@addr) = split(/\./,$addr);
  local($b0) = shift(@addr);
  local($b1) = shift(@addr);
  local($b2) = shift(@addr);
  local($b3) = shift(@addr);
  return (($b0*256+$b1)*256+$b2)*256+$b3;
}

sub unparse_inaddr {
  local($x) = shift; 
  local($b3) = $x&255; $x >>= 8;
  local($b2) = $x&255; $x >>= 8;
  local($b1) = $x&255; $x >>= 8;
  local($b0) = $x&255; $x >>= 8;
  return $b0.'.'.$b1.'.'.$b2.'.'.$b3;
}

sub subnet_mask {
  local($addr) = shift; 
  local(@addr) = split(/\./,$addr);
  local($b0) = shift(@addr);
  if ($b0 < 128) { return '255.255.0.0'; }
  else { return '255.255.255.0'; }
}

sub find_free_addr {
  local($addr) = shift;
  local($parsed_addr) = &parse_inaddr($addr);
  local($subnet_mask) = &subnet_mask($addr);
  local($parsed_subnet_mask) = &parse_inaddr($subnet_mask);
  local($max_host) = $parsed_subnet_mask^&parse_inaddr('255.255.255.255')-1;
  local($parsed_subnet) = $parsed_addr & $parsed_subnet_mask;
  local($host);
  local($parsed_host);
  local($unparsed_host);
  local($found);
  for ($host = $min_host; $host <= $max_host; $host += 1) {
    $parsed_host = $parsed_subnet + $host;
    $unparsed_host = &unparse_inaddr($parsed_host);
    $found = 0;
    while (($key,$rdata) = each in_a) { 
      if ($rdata eq $unparsed_host) { 
	$found = 1;
      }
    }
    if (!$found) { return $unparsed_host; }
  }
  return '';
}

sub get_all_cpu_and_os_types {
  local($key,$rdata,@rdata,$cpu_type,$os_type);
  while (($key,$rdata) = each in_hinfo) { 
    @rdata = split(/ /,$rdata);
    $cpu_type = shift(@rdata);
    $os_type = shift(@rdata);
    if ($cpu_type && $os_type) {
      if ($cpu_type) { $cpu_types{$cpu_type} = ''; }
      if ($os_type) { $os_types{$os_type} = ''; }
    }
  }
}

sub get_all_tcp_and_udp_services {
  setservent(1);
  local($s_name,$s_aliases,$s_port,$s_proto,$service);
  while (($s_name,$s_aliases,$s_port,$s_proto) = getservent) {
    if ($s_proto eq 'tcp') { 
      $tcp_services{$s_name} = 0; 
      $tcp_services_name{$s_port} = $s_name;
    }
    elsif ($s_proto eq 'udp') { 
      $udp_services{$s_name} = 0; 
      $udp_services_name{$s_port} = $s_name;
    }
  }
  endservent;
}

sub print_list {
  local($conjunction) = shift;
  local($count) = scalar(@_);
  if ($count > 1) { 
    if ($count > 2) {
      while (scalar(@_) > 1) { print(shift,', '); }
    } else {
      print(shift,' ');
    }
    print($conjunction,' ');
  }
  print(shift);
}

sub create_validate {
  local($domain,$proto) = @_;
  local($file_info,$directory,$origin,$file,@domains,$substr);
  foreach $file_info (@file_info) {
    ($directory,$origin,$file) = split(/ /,$file_info);
    if (index($origin,'.in-addr.arpa.') < 0) {
      $substr = '.'.$origin;
      if ($domain eq $origin || (index($domain,$substr) > 0)) {
	return 0;
      }
      push(@domains,&dom_nodot($origin));
      local($alt) = &dom_first($domain).$substr;
      push(@alternatives,'<A HREF="'.$ENV{'SCRIPT_NAME'}.'/create?domain='
	   .&dom_nodot($alt).'&proto='.&dom_nodot($proto).'">'
	   .&dom_nodot($alt).'</A>');
    }
  }
  print('<H2>Invalid Name: "',&dom_nodot($domain),'"</H2>',"\n");
  local($num_domains) = scalar(@domains);
  if ($num_domains) {
    print('Your input "',&dom_nodot($domain),'" is not a fully qualified ');
    print("domain name for any subdomain of\n");
    &print_list('or',@domains);
    print(".\n");
    print("Please try again with something like\n");
    &print_list('or',@alternatives);
    print(".\n");
  } else {
    print("There are no domains to be updated.\n");
  }
  return 1;
}

sub create {
  print("<TITLE>Create DNS Data for a New Host</TITLE>\n");
  print("<H1>Create DNS Data for a New Host</H1>\n");
  local($domain) = &domain($content{'domain'});
  local($proto) = &domain($content{'proto'});
  &open_db;
  if ($ENV{'REQUEST_METHOD'} eq 'POST') {
    &update_rrs_host($domain);
  } elsif ($domain) {
    if ($in_cname{$domain.';0'}) { $domain = $in_cname{$domain.';0'}; }
    if (&create_validate($domain,$proto)) { return; }
    local($dom,$i,$j,$rdata,@rdata);
    print('<H2>DNS Data For Host ');
    print('<A HREF="',$ENV{'SCRIPT_NAME'},'/create?');
    print('domain=',&dom_nodot($domain),'&proto=',&dom_nodot($proto));
    print('">',&dom_nodot($domain),"</A></H2>\n");
    print('<FORM ACTION="create" METHOD="POST">',"\n");
    print('<INPUT TYPE="hidden" NAME="domain" VALUE="',$domain,'">',"\n");
    print('<INPUT TYPE="hidden" NAME="proto" VALUE="',$proto,'">',"\n");
    print("Network Addresses:<BR>\n");
    $i = 0;
    for ($j = 0; $rdata=$in_a{$domain.';'.$j}; $j++) {
      print('<INPUT TYPE="text" SIZE=20 NAME="addr',$i++);
      print('" VALUE="',$rdata,'">',"\n");
    }
    if ($i == 0) {
      for ($j = 0; $rdata=$in_a{$proto.';'.$j}; $j++) {
	print('<INPUT TYPE="text" SIZE=20 NAME="addr',$i++);
	print('" VALUE="',&find_free_addr($rdata),'">',"\n");
      }
    }
    print('<INPUT TYPE="text" SIZE=20 NAME="addr',$i++,'" VALUE="">',"\n");
    print('<INPUT TYPE="text" SIZE=20 NAME="addr',$i++,'" VALUE="">',"\n");
    print("<BR>\n");
    print("Nicknames (must be fully-qualified):<BR>\n");
    local($key);
    $i = 0;
    while (($key,$rdata) = each in_cname) { 
      if ($rdata eq $domain) {
	($dom) = split(/;/,$key);
	print('<INPUT TYPE="text" SIZE=24 NAME="name',$i++);
	print('" VALUE="',&dom_nodot($dom),'">',"\n");
      }
    }
    print('<INPUT TYPE="text" SIZE=24 NAME="name',$i++,'" VALUE="">',"\n");
    print('<INPUT TYPE="text" SIZE=24 NAME="name',$i++,'" VALUE="">',"\n");
    print("<BR>\n");
    print("Mail Exchangers:<BR>\n");
    local($preference,$mailer);
    $i = 0;
    for ($j = 0; $rdata=$in_mx{$domain.';'.$j}; $j++) {
      @rdata = split(/ /,$rdata);
      $preference = shift(@rdata); 
      $mailer = shift(@rdata);
      print('<INPUT TYPE="text" SIZE=24 NAME="mx',$i++);
      print('" VALUE="',&dom_nodot($mailer),'">',"\n");
    }
    if ($i == 0) {
      for ($j = 0; $rdata=$in_mx{$proto.';'.$j}; $j++) {
	$rdata =~ s/$proto/$domain/;
	@rdata = split(/ /,$rdata);
	$preference = shift(@rdata); 
	$mailer = shift(@rdata);
	print('<INPUT TYPE="text" SIZE=24 NAME="mx',$i++);
	print('" VALUE="',&dom_nodot($mailer),'">',"\n");
      }
    }
    print('<INPUT TYPE="text" SIZE=24 NAME="mx',$i++,'" VALUE="">',"\n");
    print('<INPUT TYPE="text" SIZE=24 NAME="mx',$i++,'" VALUE="">',"\n");
    print("<BR>\n");
    &get_all_cpu_and_os_types;
    local($cpu_type,$os_type,$ct,$ot);
    for ($i = 0; $rdata=$in_hinfo{$proto.';'.$i}; $i++) {
      @rdata = split(/ /,$rdata);
      $cpu_type = shift(@rdata);
      $os_type = shift(@rdata);
    }
    for ($i = 0; $rdata=$in_hinfo{$domain.';'.$i}; $i++) {
      @rdata = split(/ /,$rdata);
      $cpu_type = shift(@rdata);
      $os_type = shift(@rdata);
    }
    print("Type of Computer and Operating System:<BR>\n");
    print('<SELECT NAME="cpu_type" SIZE=10>',"\n");
    foreach $ct (sort(keys(%cpu_types))) {
      if ($ct eq $cpu_type) { print('<OPTION SELECTED>',$ct,"\n"); }
      else { print('<OPTION>',$ct,"\n"); }
    }
    print("<OPTION>Other, Specified Below:\n");
    print("</SELECT>\n");
    print('<SELECT NAME="os_type" SIZE=10>',"\n");
    foreach $ot (sort(keys(%os_types))) {
      if ($ot eq $os_type) { print('<OPTION SELECTED>',$ot,"\n"); }
      else { print('<OPTION>',$ot,"\n"); }
    }
    print("<OPTION>Other, Specified Below:\n");
    print("</SELECT>\n");
    print("<BR>\n");
    print('<INPUT TYPE="text" SIZE=18 NAME="other_cpu_type" VALUE="">',"\n");
    print('<INPUT TYPE="text" SIZE=18 NAME="other_os_type" VALUE="">',"\n");
    print("<BR>\n");
    &get_all_tcp_and_udp_services;
    local($address,$medium);
    for ($i = 0; $rdata=$in_wks{$proto.';'.$i}; $i++) {
      @rdata = split(/ /,$rdata);
      $address = shift(@rdata); 
      $medium = shift(@rdata); 
      if ($medium eq 'tcp' || $medium eq 'TCP') {
	foreach $service (@rdata) {
	  if ($tcp_services_name{$service}) {
	    $service = $tcp_services_name{$service};
	  }
	  $tcp_services{$service} = 1;
	}
      } elsif ($medium eq 'udp' || $medium eq 'UDP') {
	foreach $service (@rdata) {
	  if ($udp_services_name{$service}) {
	    $service = $udp_services_name{$service};
	  }
	  $udp_services{$service} = 1;
	}
      }
    }
    for ($i = 0; $rdata=$in_wks{domain.';'.$i}; $i++) {
      @rdata = split(/ /,$rdata);
      $address = shift(@rdata); 
      $medium = shift(@rdata); 
      if ($medium eq 'tcp' || $medium eq 'TCP') {
	foreach $service (@rdata) {
	  if ($tcp_services_name{$service}) {
	    $service = $tcp_services_name{$service};
	  }
	  $tcp_services{$service} = 1;
	}
      } elsif ($medium eq 'udp' || $medium eq 'UDP') {
	foreach $service (@rdata) {
	  if ($udp_services_name{$service}) {
	    $service = $udp_services_name{$service};
	  }
	  $udp_services{$service} = 1;
	}
      }
    }
    print("Services over TCP and UDP:<BR>\n");
    print('<SELECT NAME="tcp_services" MULTIPLE SIZE=10>',"\n");
    foreach $service (sort(keys(%tcp_services))) {
      if ($tcp_services{$service}) { 
	print('<OPTION SELECTED>',$service,"\n"); 
      } else { print('<OPTION>',$service,"\n"); }
    }
    print("</SELECT>\n");
    print('<SELECT NAME="udp_services" MULTIPLE SIZE=10>',"\n");
    foreach $service (sort(keys(%udp_services))) {
      if ($udp_services{$service}) { 
	print('<OPTION SELECTED>',$service,"\n"); 
      } else { print('<OPTION>',$service,"\n"); }
    }
    print("</SELECT>\n");
    print("<BR>\n");
    print("Remarks for the change log:<BR>\n");
    print('<TEXTAREA NAME="remarks" COLS=60 ROWS=3>');
    print("</TEXTAREA><BR>\n");
    print('<INPUT TYPE="radio" NAME="store" VALUE="0">',"\n");
    print("Just update dbm files.<BR>\n");
    print('<INPUT TYPE="radio" NAME="store" VALUE="1" CHECKED>',"\n");
    print("Update dbm files,");
    print(" then regenerate DNS master files from dbm files.<BR>\n");
    print('<INPUT TYPE="submit" VALUE="Submit">',"\n");
    print('<INPUT TYPE="reset" VALUE="Reset">',"\n");
    print("</FORM>\n");
    print("It takes much longer to regenerate the\n");
    print("DNS master files than it does to load them.\n");
    print("Please be patient.\n");
  } else {
    print('<FORM ACTION="create" METHOD="GET">',"\n");
    print("Fully Qualified Domain Name:<BR>\n");
    print("<b>This means <em>machinename.appropriate.domain.edu</em></b><BR>\n");
    print("For example <em>dialup-XXXX.lcs.mit.edu</em><BR>\n");
    print('<INPUT TYPE="text" SIZE=24 NAME="domain" SIZE=24 ');
    print('VALUE="">',"\n");
    print("<BR>\n");
    print("Choose an Existing Host to Use as a Prototype:<BR>\n");
    print('<SELECT NAME="proto" SIZE=16>',"\n");
    local($i);
    foreach $key (sort(keys(%in_a))) {
      if ($in_a{$key} ne "") {
	($domain,$i) = split(/;/,$key);
	if ($i == 0 && $domain ne "") { 
	  $domain = &dom_nodot($domain);
	  if ($domain eq $ENV{'REMOTE_HOST'}) {
	    print('<OPTION SELECTED>',$domain,"\n"); 
	  } else {
	    print('<OPTION>',$domain,"\n"); 
	  }
	}
      }
    }
    print("</SELECT>\n");
    print("<BR>\n");
    print('<INPUT TYPE="submit" VALUE="Create DNS Data For Host">',"\n");
    print("</FORM>\n");
    print("It may take several seconds to load the DNS master files.\n");
    print("Please be patient.\n");
  }
}

sub edit {
  print("<TITLE>Edit DNS Data for an Existing Host</TITLE>\n");
  print("<H1>Edit DNS Data for an Existing Host</H1>\n");
  local($domain) = &domain($content{'domain'});
  &open_db;
  if ($ENV{'REQUEST_METHOD'} eq 'POST') {
    &update_rrs_host($domain);
  } elsif ($domain) {
    if ($in_cname{$domain.';0'}) { $domain = $in_cname{$domain.';0'}; }
    local($dom,$i,$rdata,@rdata);
    print('<H2>DNS Data For Host ');
    print('<A HREF="',$ENV{'SCRIPT_NAME'},'/edit?');
    print('domain=',&dom_nodot($domain));
    print('">',&dom_nodot($domain),"</A></H2>\n");
    print('<FORM ACTION="edit" METHOD="POST">',"\n");
    print('<INPUT TYPE="hidden" NAME="domain" VALUE="',$domain,'">',"\n");
    print("Network Addresses:<BR>\n");
    $i = 0;
    for ($j = 0; $rdata=$in_a{$domain.';'.$j}; $j++) {
      print('<INPUT TYPE="text" SIZE=20 NAME="addr',$i++);
      print('" VALUE="',$rdata,'">',"\n");
    }
    print('<INPUT TYPE="text" SIZE=20 NAME="addr',$i++,'" VALUE="">',"\n");
    print('<INPUT TYPE="text" SIZE=20 NAME="addr',$i++,'" VALUE="">',"\n");
    print("<BR>\n");
    print("Nicknames (must be fully-qualified):<BR>\n");
    $i = 0;
    while (($key,$rdata) = each in_cname) { 
      if ($rdata eq $domain) {
	($dom) = split(/;/,$key);
	print('<INPUT TYPE="text" SIZE=24 NAME="name',$i++);
	print('" VALUE="',&dom_nodot($dom),'">',"\n");
      }
    }
    print('<INPUT TYPE="text" SIZE=24 NAME="name',$i++,'" VALUE="">',"\n");
    print('<INPUT TYPE="text" SIZE=24 NAME="name',$i++,'" VALUE="">',"\n");
    print("<BR>\n");
    print("Mail Exchangers:<BR>\n");
    local($preference,$mailer);
    $i = 0;
    for ($j = 0; $rdata=$in_mx{$domain.';'.$j}; $j++) {
      @rdata = split(/ /,$rdata);
      $preference = shift(@rdata); 
      $mailer = shift(@rdata);
      print('<INPUT TYPE="text" SIZE=24 NAME="mx',$i++);
      print('" VALUE="',&dom_nodot($mailer),'">',"\n");
    }
    print('<INPUT TYPE="text" SIZE=24 NAME="mx',$i++,'" VALUE="">',"\n");
    print('<INPUT TYPE="text" SIZE=24 NAME="mx',$i++,'" VALUE="">',"\n");
    print("<BR>\n");
    &get_all_cpu_and_os_types;
    local($cpu_type,$os_type,$ct,$ot);
    for ($i = 0; $rdata=$in_hinfo{$domain.';'.$i}; $i++) {
      @rdata = split(/ /,$rdata);
      $cpu_type = shift(@rdata);
      $os_type = shift(@rdata);
    }
    print("Type of Computer and Operating System:<BR>\n");
    print('<SELECT NAME="cpu_type" SIZE=10>',"\n");
    foreach $ct (sort(keys(%cpu_types))) {
      if ($ct eq $cpu_type) { print('<OPTION SELECTED>',$ct,"\n"); }
      else { print('<OPTION>',$ct,"\n"); }
    }
    print("<OPTION>Other, Specified Below:\n");
    print("</SELECT>\n");
    print('<SELECT NAME="os_type" SIZE=10>',"\n");
    foreach $ot (sort(keys(%os_types))) {
      if ($ot eq $os_type) { print('<OPTION SELECTED>',$ot,"\n"); }
      else { print('<OPTION>',$ot,"\n"); }
    }
    print("<OPTION>Other, Specified Below:\n");
    print("</SELECT>\n");
    print("<BR>\n");
    print('<INPUT TYPE="text" SIZE=18 NAME="other_cpu_type" VALUE="">',"\n");
    print('<INPUT TYPE="text" SIZE=18 NAME="other_os_type" VALUE="">',"\n");
    print("<BR>\n");
    &get_all_tcp_and_udp_services;
    local($address,$medium);
    for ($i = 0; $rdata=$in_wks{$domain.';'.$i}; $i++) {
      @rdata = split(/ /,$rdata);
      $address = shift(@rdata); 
      $medium = shift(@rdata); 
      if ($medium eq 'tcp' || $medium eq 'TCP') {
	foreach $service (@rdata) {
	  if ($tcp_services_name{$service}) {
	    $service = $tcp_services_name{$service};
	  } 
	  $tcp_services{$service} = 1;
	}
      } elsif ($medium eq 'udp' || $medium eq 'UDP') {
	foreach $service (@rdata) {
	  if ($udp_services_name{$service}) {
	    $service = $udp_services_name{$service};
	  }
	  $udp_services{$service} = 1;
	}
      }
    }
    print("Services over TCP and UDP:<BR>\n");
    print('<SELECT NAME="tcp_services" MULTIPLE SIZE=10>',"\n");
    foreach $service (sort(keys(%tcp_services))) {
      if ($tcp_services{$service}) { 
	print('<OPTION SELECTED>',$service,"\n"); 
      } else { print('<OPTION>',$service,"\n"); }
    }
    print("</SELECT>\n");
    print('<SELECT NAME="udp_services" MULTIPLE SIZE=10>',"\n");
    foreach $service (sort(keys(%udp_services))) {
      if ($udp_services{$service}) { 
	print('<OPTION SELECTED>',$service,"\n"); 
      } else { print('<OPTION>',$service,"\n"); }
    }
    print("</SELECT>\n");
    print("<BR>\n");
    print("Remarks for the change log:<BR>\n");
    print('<TEXTAREA NAME="remarks" COLS=60 ROWS=3>');
    print("</TEXTAREA><BR>\n");
    print('<INPUT TYPE="radio" NAME="store" VALUE="0">',"\n");
    print("Just update dbm files.<BR>\n");
    print('<INPUT TYPE="radio" NAME="store" VALUE="1" CHECKED>',"\n");
    print("Update dbm files,");
    print(" then regenerate DNS master files from dbm files.<BR>\n");
    print('<INPUT TYPE="submit" VALUE="Submit">',"\n");
    print('<INPUT TYPE="reset" VALUE="Reset">',"\n");
    print("</FORM>\n");
    print("It takes much longer to regenerate the\n");
    print("DNS master files than it does to load them.\n");
    print("Please be patient.\n");
  } else {
    local($i);
    print('<FORM ACTION="edit" METHOD="GET">',"\n");
    print("Choose an Existing Host:<BR>\n");
    print('<SELECT NAME="domain" SIZE=20>',"\n");
    foreach $key (sort(keys(%in_a))) {
      if ($in_a{$key} ne "") {
	($domain,$i) = split(/;/,$key);
	if ($i == 0 && $domain ne "") { 
	  $domain = &dom_nodot($domain);
	  if ($domain eq $ENV{'REMOTE_HOST'}) {
	    print('<OPTION SELECTED>',$domain,"\n"); 
	  } else {
	    print('<OPTION>',$domain,"\n"); 
	  }
	}
      }
    }
    print("</SELECT>\n");
    print("<BR>\n");
    print('<INPUT TYPE="submit" VALUE="Edit DNS Data For Host">',"\n");
    print("</FORM>\n");
    print("It may take several seconds to load the DNS master files.\n");
    print("Please be patient.\n");
  }
}

sub delete {
  print("<TITLE>Delete All DNS Data for an Existing Host</TITLE>\n");
  print("<H1>Delete All DNS Data for an Existing Host</H1>\n");
  local($domain) = &domain($content{'domain'});
  &open_db;
  if ($ENV{'REQUEST_METHOD'} eq 'POST') {
    if (&authorized) {
      &print_update_message;
      print('All DNS resource records for host ',&dom_nodot($domain));
      print(" have been deleted.\n");
      print('<H2>Deleted DNS Resource Records For ',&dom_nodot($domain));
      print('<PRE>');
      &print_rrs('STDOUT',$domain);
      print('</PRE>');
      &mail($domain,'deleted'); 
      &start_dbm_update;
      &delete_rrs($domain,'all');
      if ($content{'store'}) { &store_all; }
    } else {
      print("<H2>DNS Files Not Modified</H2>\n");
      print("The DNS master files were not modified\n");
      print("because you are not authorized to change them.\n");
    }
  } elsif ($domain) {
    print('<H2>All DNS Resource Records For ');
    print('<A HREF="',$ENV{'SCRIPT_NAME'},'/raw?');
    print('domain=',&dom_nodot($domain));
    print('">',&dom_nodot($domain),"</A></H2>\n");
    print('<PRE>');
    &print_rrs('STDOUT',$domain);
    print('</PRE>');
    print('<FORM ACTION="delete" METHOD="POST">',"\n");
    print('<INPUT TYPE="hidden" NAME="domain" VALUE="',$domain,'">',"\n");
    print("Remarks for the change log:<BR>\n");
    print('<TEXTAREA NAME="remarks" COLS=60 ROWS=3>');
    print("</TEXTAREA><BR>\n");
    print('<INPUT TYPE="radio" NAME="store" VALUE="0">',"\n");
    print("Just update dbm files.<BR>\n");
    print('<INPUT TYPE="radio" NAME="store" VALUE="1" CHECKED>',"\n");
    print("Update dbm files,");
    print(" then regenerate DNS master files from dbm files.<BR>\n");
    print('<INPUT TYPE="submit" VALUE="Delete All DNS Data For Host">',"\n");
    print("</FORM>\n");
    print("It takes much longer to regenerate the\n");
    print("DNS master files than it does to load them.\n");
    print("Please be patient.\n");
  } else {
    print('<FORM ACTION="delete" METHOD="GET">',"\n");
    print("Choose an Existing Host:<BR>\n");
    print('<SELECT NAME="domain" SIZE=20>',"\n");
    local($i);
    foreach $key (sort(keys(%in_a))) {
      if ($in_a{$key} ne "") {
	($domain,$i) = split(/;/,$key);
	if ($i == 0 && $domain ne "") { 
	  $domain = &dom_nodot($domain);
	  if ($domain eq $ENV{'REMOTE_HOST'}) {
	    print('<OPTION SELECTED>',$domain,"\n"); 
	  } else {
	    print('<OPTION>',$domain,"\n"); 
	  }
	}
      }
    }
    print("</SELECT>\n");
    print("<BR>\n");
    print('<INPUT TYPE="submit" VALUE="Delete All DNS Data For Host">',"\n");
    print("</FORM>\n");
    print("It may take several seconds to load the DNS master files.\n");
    print("Please be patient.\n");
  }
}

sub update_rrs_raw {
  local($domain) = shift;
  local(@rrs) = @_;
  local($dom) = $domain;
  if (&authorized) {
    &start_dbm_update;
    &delete_rrs($domain,'all');
    foreach $_ (@rrs) {
      s/;.*//; split(/\s+/); 
      if (scalar(@_) == 0) { next; }
      $dom = &load_rr($dom,'',@_);
    }
    if ($content{'store'}) { &store_all; }
    &mail($domain,'updated'); 
    &print_update_message;
    print('<H2>Raw DNS Resource Records For ');
    print('<A HREF="',$ENV{'SCRIPT_NAME'},'/raw?');
    print('domain=',&dom_nodot($domain));
    print('">',&dom_nodot($domain),"</A></H2>\n");
    print('<PRE>');
    &print_rrs('STDOUT',$domain);
    print('</PRE>');
  } else {
    print("<H2>DNS Files Not Modified</H2>\n");
    print("The DNS master files were not modified\n");
    print("because you are not authorized to change them.\n");
  }
}

sub raw {
  print("<TITLE>Edit Raw DNS Resource Records</TITLE>\n");
  print("<H1>Edit Raw DNS Resource Records</H1>\n");
  local($domain) = &domain($content{'domain'});
  if ($ENV{'REQUEST_METHOD'} eq 'POST') {
    &open_db;
    &update_rrs_raw($domain,split(/\n/,$content{'rrs'}));
  } elsif ($domain) {
    &open_db;
    print('<H2>Raw DNS Records For ');
    print('<A HREF="',$ENV{'SCRIPT_NAME'},'/raw?');
    print('domain=',&dom_nodot($domain));
    print('">',&dom_nodot($domain),"</A></H2>\n");
    print('<FORM ACTION="raw" METHOD="POST">',"\n");
    print('<INPUT TYPE="hidden" NAME="domain" VALUE="',$domain,'">',"\n");
    print('<TEXTAREA NAME="rrs" COLS=60 ROWS=14>');
    &print_rrs('STDOUT', $domain,'all');
    print("</TEXTAREA><BR>\n");
    print("Remarks for the change log:<BR>\n");
    print('<TEXTAREA NAME="remarks" COLS=60 ROWS=3>');
    print("</TEXTAREA><BR>\n");
    print('<INPUT TYPE="radio" NAME="store" VALUE="0">',"\n");
    print("Just update dbm files.<BR>\n");
    print('<INPUT TYPE="radio" NAME="store" VALUE="1" CHECKED>',"\n");
    print("Update dbm files,");
    print(" then regenerate DNS master files from dbm files.<BR>\n");
    print('<INPUT TYPE="submit" VALUE="Submit">',"\n");
    print('<INPUT TYPE="reset" VALUE="Reset">',"\n");
    print("</FORM>\n");
    print("It takes much longer to regenerate the\n");
    print("DNS master files than it does to load them.\n");
    print("Please be patient.\n");
  } else {
    print('<FORM ACTION="raw" METHOD="GET">',"\n");
    print("Fully Qualified Domain Name:<BR>\n");
    print('<INPUT TYPE="text" SIZE=24 NAME="domain" SIZE=24 ');
    print('VALUE="">',"\n");
    print("<BR>\n");
    print('<INPUT TYPE="submit" VALUE="Edit Raw DNS Resource Records">',"\n");
    print("</FORM>\n");
    print("It may take several seconds to load the DNS master files.\n");
    print("Please be patient.\n");
  }
}

sub about {
  print("<TITLE>About WEBDNS</TITLE>\n");
  print("<H1>About WEBDNS</H1>\n");
  print("WEBDNS is a an World Wide Web facility for editing\n");
  print("Internet Domain Name System master files.\n");
  print("It is implemented as a perl script.\n");
  print("<H2>How It Works</H2>\n");
  print("Every time you make a request of \n");
  print("WEBDNS, it uses the named.boot file on the machine on\n");
  print("which it is running to find all the \n");
  print("DNS master files for which the machine is a primary server.\n");
  print("It then effectively reads in all the master files.\n");
  print("If the request \n");
  print("is to view some data or build a form,\n");
  print("WEBDNS extracts and presents the relevant data.\n");
  print("If the request is a post, WEBDNS checks the contents of the post\n");
  print("for consistency, updates its internal \n");
  print("data structures, and then dumps out new versions \n");
  print("of all the master files.\n");
  print("<H3>The dbm File Cache</H3>\n");
  print("Because it takes so long to read and write the master files,\n");
  print("WEBDNS maintains a cache of the data in random-access dbm files.\n");
  print("Whenever it is about to read in all the master files, \n");
  print("it really first checks if the master files have been modified\n");
  print("since WEBDNS last regenerated them, and if not, \n");
  print("it uses its dbm files instead.\n");
  print("This makes getting data a lot faster.<P>\n");
  print("By default, WEBDNS will always regenerate the DNS master files \n");
  print("every time an edit is made.  \n");
  print("This can take a long time.\n");
  print("You can disable the regeneration of DNS master files\n");
  print("by choosing the ``Just update dbm files'' option,\n");
  print("which appears above the ``Submit'' button on update forms.\n");
  print("If you are going to submit many changes in a row, \n");
  print("you will be done sooner if you choose the ``Just update dbm\n");
  print("files'' option in all but your last edit.\n");
  print("Then, when you submit your last change with the option ``Update\n");
  print("dbm files, then regenerate DNS master files from dbm files,''\n");
  print("all the changes you made to the dbm files will be reflected\n");
  print("in the new DNS master files.<P>\n");
  print("Be aware, however, that\n");
  print("if there ever is any ambiguity between changes to the DNS master\n");
  print("files and changes to dbm files, WEBDNS always defers to the DNS\n");
  print("master files.  For example, if Joe uses WEBDNS to make a change,\n");
  print("but tells WEBDNS to only update its dbm files, and Sue changes\n");
  print("a DNS master file directly by editing it with a text editor,\n");
  print("WEBDNS would not regenerate the DNS master files from its\n");
  print("dbm files, which would cause Sue's edit to be lost.\n");
  print("Instead, the next time WEBDNS runs, it would notice that the\n");
  print("DNS master files had been updated by Sue, and it\n");
  print("would rebuild its dbm files from the DNS master files, resulting\n");
  print("in the loss of Joe's change,\n");  
  print("even if he and Sue made changes to two different hosts.<P>\n");
  print("Due to the hazards of this ambiguity resolution policy,\n");
  print("if WEBDNS is used at all to make updates to the DNS master files,\n");
  print("the best DNS master file administration strategy is to use\n");
  print("WEBDNS as much as possible, with frequent regenerations\n");
  print("of the DNS master files, and to avoid changes to the\n");
  print("DNS master files by other means.");
  print("<H2>The Author</H2>\n");
  print("WEBDNS was written by Chris Lindblad,\n");
  print("a doctoral student at the\n");
  print('<A HREF="//www.lcs.mit.edu">');
  print("MIT Laboratory for Computer Science</A>.\n");
  print("He wrote it to learn perl and to avoid working on his thesis.\n");
  print("You can send comments to him at <I>cjl@lcs.mit.edu</I>.\n");
  print("<H2>WEBDNS Distribution</H2>\n");
  print("WEBDNS is distributed freely.\n");
  print("If you are interested in running it at your site\n");
  print("or you are just curious to see what it looks like, click\n");
  print('<A HREF="',$ENV{'SCRIPT_NAME'},'/listing">here</A> ');
  print("for a complete listing of the script.\n");
}

sub top {
  local($file_info,$directory,$domain,$file,@domains);
  foreach $file_info (@file_info) {
    ($directory,$domain,$file) = split(/ /,$file_info);
    if (index($domain,'.in-addr.arpa.') < 0) {
      push(@domains,&dom_nodot($domain));
    }
  }
  print("<TITLE>Edit DNS Master Files</TITLE>\n");
  print("<H1>Edit DNS Master Files</H1>\n");
  print("This is WEBDNS, a World Wide Web facility for editing\n");
  print("Internet Domain Name System master files.\n");
  if (scalar(@domains)) {
    print("Using this facility, you can edit the master files for\n");
    &print_list('and',@domains);
    print(".\n");
  }
  print("<H2>WEBDNS Pages</H2>\n");
  print("<UL>\n");
  print('<LI><A HREF="',$ENV{'SCRIPT_NAME'},'/edit">');
  print("Edit DNS Data for an Existing Host</A>\n");
  print('<LI><A HREF="',$ENV{'SCRIPT_NAME'},'/create">');
  print("Create DNS Data for a New Host</A>\n");
  print('<LI><A HREF="',$ENV{'SCRIPT_NAME'},'/delete">');
  print("Delete All DNS Data for an Existing Host</A>\n");
  print('<LI><A HREF="',$ENV{'SCRIPT_NAME'},'/raw">');
  print("Edit Raw DNS Resource Records</A>\n");
  print("</UL>\n");
  print("<P>\n");
  print("It may take several seconds to load the DNS master files.\n");
  print("Please be patient.\n");
}

sub open_named_boot {
  local($named_boot,$domain,@domains,$cmd);
  local($directory) = '/etc';
  foreach $named_boot (@named_boot) {
    if (open(NAMED_BOOT,$named_boot)) {
      flock(NAMED_BOOT,$LOCK_EX)
	|| die('Cannot lock ',$named_boot,': ',$!,"\n");
      while (<NAMED_BOOT>) {
	chop; s/;.*//; split; $cmd = shift;
	if ($cmd eq 'directory') {
	  $directory = shift;
	}
	if ($cmd eq 'primary') {
	  push(@file_info,join(' ',($directory,&domain(shift),shift)));
	}
      }
      return;
    }
  }
}

sub close_named_boot {
  flock(NAMED_BOOT,$LOCK_UN); 
  close(NAMED_BOOT);
}

sub content {
  local($buffer,@pairs,$key,$val);
  if ($ENV{'CONTENT_LENGTH'}) {
    if ($ENV{'CONTENT_TYPE'} ne 'application/x-www-form-urlencoded') {
      die('Unknown content type ',$ENV{'CONTENT_TYPE'},"\n");
    }
    read(STDIN,$buffer,$ENV{'CONTENT_LENGTH'});
  } elsif ($ENV{'QUERY_STRING'}) { $buffer = $ENV{'QUERY_STRING'}; } 
  else { $buffer = ''; }
  @pairs = split(/&/,$buffer);
  foreach $pair (@pairs) {
    ($key,$val) = split(/=/,$pair);
    $val =~ tr/+/ /;
    $val =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
    if (defined($content{$key})) { $content{$key} .= ';'.$val; }
    else { $content{$key} = $val; }
  }
}

sub dump {
  &open_named_boot;
  &open_db;
  &start_dbm_update;
  &store_all;
  &close_db;
  &close_named_boot;
}

sub main {
  if (scalar(@ARGV)) {
    local($cmd);
    foreach $cmd (@ARGV) {
      if ($cmd eq 'dump') { &dump; } 
      else { die('Unknown command: ',$cmd,"\n"); }
    }
  } else {
    if ($ENV{'PATH_INFO'} eq '/listing') {
      print("Content-type: text/plain\n\n");
      open($0,$0) 
	|| die('Cannot open ',$0,': ',$!,"\n");
      while (<$0>) { print; };
      close($0);
    } else { 
      print("Content-type: text/html\n\n");
      umask(022);
      local($errlog) = &tmpnam;
      open(SAVEERR,'>&STDERR')
	|| die('Cannot dup STDERR: ',$!,"\n");
      open(ERRLOG,'+>'.$errlog) 
	|| die('Cannot open ',$errlog,': ',$!,"\n");
      unlink($errlog)
	|| die('Cannot unlink ',$errlog,': ',$!,"\n");
      open(STDERR,'>&ERRLOG') 
	|| die('Cannot dup ERRLOG: ',$!,"\n");
      close(ERRLOG);
      select(STDERR); $| = 1;
      select(STDOUT);
      eval {
	eval {
	  &content;
	  &open_named_boot;
	  @_ = split(/\//,$ENV{'PATH_INFO'});
	  shift; $_ = shift;
	  if ($_ eq 'create') { &create; } 
	  elsif ($_ eq 'edit') { &edit; }
	  elsif ($_ eq 'delete') { &delete; }
	  elsif ($_ eq 'raw') { &raw; } 
	  elsif ($_ eq 'about') { &about; } 
	  elsif ($_ eq 'listing') { &listing; } 
	  else { &top; }
	};
	if ($@) { warn($@); }
	&close_db;
	&close_named_boot;
      };
      if ($@) { warn($@); }
      open(ERRLOG,'+>&STDERR');
      open(STDERR,'>&SAVEERR');
      close(SAVEERR);
      local(@stat) = stat(ERRLOG);
      local($size) = $stat[7];
      if ($size) {
	print("<HR>\n");
	print("<I>Error Output:</I>\n");
	print('<PRE>');
	seek(ERRLOG, 0, 0);
	while (<ERRLOG>) {
	  print;
	}
	print("</PRE>\n");
      }
      close(ERRLOG);
      print("<HR>\n");
      print('<A HREF="',$ENV{'SCRIPT_NAME'},'/about">');
      print('<I>About WEBDNS</I></A><BR>',"\n");
    }
  }
}

&main;
   
# Local Variables:
# mode: c
# End:
