Index: /branches/stable/DNSDB.pm
===================================================================
--- /branches/stable/DNSDB.pm	(revision 581)
+++ /branches/stable/DNSDB.pm	(revision 582)
@@ -213,4 +213,5 @@
 		force_refresh	=> 1,
 		lowercase	=> 0,	# mangle as little as possible by default
+		showrec_arpa_ns	=> 0,	# show formal .arpa zone name instead of logical CIDR on reverse NS records
 	);
 
@@ -242,5 +243,5 @@
 
   # Several settings are booleans.  Handle multiple possible ways of setting them.
-  for my $boolopt ('log_failures', 'force_refresh', 'lowercase', 'usecache') {
+  for my $boolopt ('log_failures', 'force_refresh', 'lowercase', 'usecache', 'showrec_arpa_ns') {
     if ($self->{$boolopt} ne '1' && $self->{$boolopt} ne '0') {
       # true/false, on/off, yes/no all valid.
@@ -1136,5 +1137,5 @@
     my $tmpzone = $zone;
     $tmpzone =~ s/\.in-addr\.arpa\.?//;
-    return ('FAIL', "Non-numerics in apparent IPv4 reverse zone name") if $tmpzone !~ /^(?:\d+-)?[\d\.]+$/;
+    return ('FAIL', "Non-numerics in apparent IPv4 reverse zone name [$tmpzone]") if $tmpzone !~ m{^(?:\d+[/-])?[\d\.]+$};
 
     # Snag the octet pieces
@@ -1153,5 +1154,5 @@
     # Alternate form:  The second number is actually the real netmask, not the end of the range.
     my $masklen = 0;
-    if ($octs[0] =~ /^((\d+)-(\d+))$/) {	# take the range...
+    if ($octs[0] =~ m{^((\d+)[/-](\d+))$}) {	# take the range...
       if (24 < $3 && $3 < 31) {
 	# we have a real netmask
@@ -1342,4 +1343,5 @@
       $cfg->{force_refresh}	= $1 if /^force_refresh\s*=\s*([a-z01]+)/i;
       $cfg->{lowercase}		= $1 if /^lowercase\s*=\s*([a-z01]+)/i;
+      $cfg->{showrec_arpa_ns}	= $1 if /^showrec_arpa_ns\s*=\s*([a-z01]+)/i;
 # not supported in dns.cgi yet
 #      $cfg->{templatedir}	= $1 if m{^templatedir\s*=\s*([a-z0-9/_.-]+)}i;
@@ -2307,5 +2309,5 @@
   if ($args{revrec} eq 'n') {
     $args{sortby} = 'domain' if !$args{sortby} || !grep /^$args{sortby}$/, ('domain','group','status');
-    $sql = "SELECT domain_id,domain,status,groups.group_name AS group FROM domains".
+    $sql = "SELECT domain_id AS zoneid,domain AS zone,status,groups.group_name AS group FROM domains".
 	" INNER JOIN groups ON domains.group_id=groups.group_id".
 	" WHERE domains.group_id IN ($args{curgroup}".($args{childlist} ? ",$args{childlist}" : '').")".
@@ -2315,5 +2317,5 @@
 ##fixme:  arguably startwith here is irrelevant.  depends on the UI though.
     $args{sortby} = 'revnet' if !$args{sortby} || !grep /^$args{sortby}$/, ('revnet','group','status');
-    $sql = "SELECT rdns_id,revnet,status,groups.group_name AS group FROM revzones".
+    $sql = "SELECT rdns_id AS zoneid,revnet AS zone,status,groups.group_name AS group FROM revzones".
 	" INNER JOIN groups ON revzones.group_id=groups.group_id".
 	" WHERE revzones.group_id IN ($args{curgroup}".($args{childlist} ? ",$args{childlist}" : '').")".
@@ -2325,18 +2327,7 @@
 	($args{offset} eq 'all' ? '' : " LIMIT $self->{perpage}".
 	" OFFSET ".$args{offset}*$self->{perpage});
-  my $sth = $dbh->prepare($sql);
-  $sth->execute(@filterargs);
-  my $rownum = 0;
-
-  while (my @data = $sth->fetchrow_array) {
-    my %row;
-    $row{domain_id} = $data[0];
-    $row{domain} = $data[1];
-    $row{status} = $data[2];
-    $row{group} = $data[3];
-    push @zonelist, \%row;
-  }
-
-  return \@zonelist;
+
+  my $ret = $dbh->selectall_arrayref($sql, { Slice => {} }, @filterargs);
+  return $ret;
 } # end getZoneList()
 
@@ -4712,9 +4703,9 @@
       } elsif ($type eq 'NS') {
 # hmm.  should we warn here if subdomain NS'es are left alone?
-	next if ($args{rwns} && ($rr->name eq $zone));
 	if ($rev eq 'y') {
 	  # revzones have records more or less reversed from forward zones.
 	  my ($tmpcode,$tmpmsg) = _zone2cidr($host);
 	  die "Error converting NS record: $tmpmsg\n" if $tmpcode eq 'FAIL';	# hmm.  may not make sense...
+	  next if ($args{rwns} && ($tmpmsg eq "$cidr"));
 	  $val = "$tmpmsg";
 	  $host = $rr->nsdname;
@@ -4726,4 +4717,5 @@
 #}
 	} else {
+	  next if ($args{rwns} && ($rr->name eq $zone));
 	  $val = $rr->nsdname;
 	}
@@ -4879,23 +4871,34 @@
     if ($args{rwsoa}) {
       $soaflag = 1;
-      my $sthgetsoa = $dbh->prepare("SELECT host,val,ttl FROM default_records WHERE group_id=? AND type=?");
-      my $sthputsoa = $dbh->prepare("INSERT INTO records (domain_id,host,type,val,ttl) VALUES (?,?,?,?,?)");
+      my $sthgetsoa = $dbh->prepare("SELECT host,val,ttl FROM "._rectable('y', $rev)." WHERE group_id=? AND type=?");
+      my $sthputsoa = $dbh->prepare("INSERT INTO records (".
+	($rev eq 'n' ? 'domain_id' : 'rdns_id').",host,type,val,ttl) VALUES (?,?,?,?,?)");
       $sthgetsoa->execute($group,$reverse_typemap{SOA});
       while (my ($host,$val,$ttl) = $sthgetsoa->fetchrow_array()) {
-	$host =~ s/DOMAIN/$zone/g;
-	$val =~ s/DOMAIN/$zone/g;
+	if ($rev eq 'n') {
+	  $host =~ s/DOMAIN/$zone/g;
+	  $val =~ s/DOMAIN/$zone/g;	# arguably useless
+	} else {
+	  $host =~ s/ADMINDOMAIN/$self->{domain}/g;
+	}
 	$sthputsoa->execute($zone_id,$host,$reverse_typemap{SOA},$val,$ttl);
       }
     }
 
-    # Overwrite NS records
+    # Add standard NS records.  The old one(s) should have been skipped by this point.
     if ($args{rwns}) {
       $nsflag = 1;
-      my $sthgetns = $dbh->prepare("SELECT host,val,ttl FROM default_records WHERE group_id=? AND type=?");
-      my $sthputns = $dbh->prepare("INSERT INTO records (domain_id,host,type,val,ttl) VALUES (?,?,?,?,?)");
+      my $sthgetns = $dbh->prepare("SELECT host,val,ttl FROM "._rectable('y',$rev)." WHERE group_id=? AND type=?");
+      my $sthputns = $dbh->prepare("INSERT INTO records (".
+	($rev eq 'n' ? 'domain_id' : 'rdns_id').",host,type,val,ttl) VALUES (?,?,?,?,?)");
       $sthgetns->execute($group,$reverse_typemap{NS});
       while (my ($host,$val,$ttl) = $sthgetns->fetchrow_array()) {
-	$host =~ s/DOMAIN/$zone/g;
-	$val =~ s/DOMAIN/$zone/g;
+	if ($rev eq 'n') {
+	  $host =~ s/DOMAIN/$zone/g;
+	  $val =~ s/DOMAIN/$zone/g;	#hmm.
+	} else {
+	  $host =~ s/ADMINDOMAIN/$self->{domain}/g;	#hmm.
+	  $val =~ s/ZONE/$cidr/g;
+	}
 	$sthputns->execute($zone_id,$host,$reverse_typemap{NS},$val,$ttl);
       }
@@ -5053,5 +5056,5 @@
 	"FROM records WHERE rdns_id=? AND type=6");
   my $recsth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl,record_id,location,extract(epoch from stamp),expires,stampactive ".
-	"FROM records WHERE rdns_id=? AND not type=6 ".
+	"FROM records WHERE rdns_id=? AND NOT type=6 ".
 	"ORDER BY masklen(CAST(val AS inet)) DESC, CAST(val AS inet)");
   my $revsth = $dbh->prepare("SELECT rdns_id,revnet,status,changed FROM revzones WHERE status=1 ".
@@ -5089,9 +5092,10 @@
         $soasth->execute($revid);
         my (@zsoa) = $soasth->fetchrow_array();
-        _printrec_tiny($zonefilehandle,'y',\%recflags,$revzone,
+        _printrec_tiny($zonefilehandle, $zsoa[7], 'y',\%recflags,$revzone,
           $zsoa[0],$zsoa[1],$zsoa[2],$zsoa[3],$zsoa[4],$zsoa[5],$zsoa[6],$zsoa[8],'');
 
         $recsth->execute($revid);
-        while (my ($host,$type,$val,$dist,$weight,$port,$ttl,$recid,$loc,$stamp,$expires,$stampactive) = $recsth->fetchrow_array) {
+        while (my ($host, $type, $val, $dist, $weight, $port, $ttl, $recid, $loc, $stamp, $expires, $stampactive)
+		= $recsth->fetchrow_array) {
           next if $recflags{$recid};
 
@@ -5106,5 +5110,5 @@
 #	  }
 
-          _printrec_tiny($zonefilehandle, 'y', \%recflags, $revzone,
+          _printrec_tiny($zonefilehandle, $recid, 'y', \%recflags, $revzone,
             $host, $type, $val, $dist, $weight, $port, $ttl, $loc, $stamp, $expires, $stampactive);
 
@@ -5148,8 +5152,10 @@
   } # while ($revsth)
 
+  $soasth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl,record_id,location ".
+	"FROM records WHERE domain_id=? AND type=6");
+  $recsth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl,record_id,location,extract(epoch from stamp),expires,stampactive ".
+	"FROM records WHERE domain_id=? AND NOT type=6");	# Just exclude all types relating to rDNS
+#	"FROM records WHERE domain_id=? AND type < 65280");	# Just exclude all types relating to rDNS
   my $domsth = $dbh->prepare("SELECT domain_id,domain,status,changed FROM domains WHERE status=1 ORDER BY domain_id");
-  $recsth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl,record_id,location,extract(epoch from stamp),expires,stampactive ".
-	"FROM records WHERE domain_id=?");	# Just exclude all types relating to rDNS
-#	"FROM records WHERE domain_id=? AND type < 65280");	# Just exclude all types relating to rDNS
   $zonesth = $dbh->prepare("UPDATE domains SET changed='n' WHERE domain_id=?");
   $domsth->execute();
@@ -5177,4 +5183,11 @@
         }
 
+        # need to fetch this separately so the SOA comes first in the flatfile....
+        # Just In Case we need/want to reimport from the flatfile later on.
+        $soasth->execute($domid);
+        my (@zsoa) = $soasth->fetchrow_array();
+        _printrec_tiny($zonefilehandle, $zsoa[7], 'n',\%recflags,$dom,
+          $zsoa[0],$zsoa[1],$zsoa[2],$zsoa[3],$zsoa[4],$zsoa[5],$zsoa[6],$zsoa[8],'');
+
         $recsth->execute($domid);
         while (my ($host,$type,$val,$dist,$weight,$port,$ttl,$recid,$loc,$stamp,$expires,$stampactive) = $recsth->fetchrow_array) {
@@ -5190,5 +5203,5 @@
 	  }
 
-	  _printrec_tiny($zonefilehandle, 'n', \%recflags,
+	  _printrec_tiny($zonefilehandle, $recid, 'n', \%recflags,
 		$dom, $host, $type, $val, $dist, $weight, $port, $ttl, $loc, $stamp, $expires, $stampactive);
 
@@ -5239,5 +5252,6 @@
 # Utility sub for __export_tiny above
 sub _printrec_tiny {
-  my ($datafile,$revrec,$recflags,$zone,$host,$type,$val,$dist,$weight,$port,$ttl,$loc,$stamp,$expires,$stampactive) = @_;
+  my ($datafile, $recid, $revrec, $recflags, $zone, $host, $type, $val, $dist, $weight, $port, $ttl,
+	$loc, $stamp, $expires, $stampactive) = @_;
 
   $loc = '' if !$loc;	# de-nullify - just in case
Index: /branches/stable/dns.cgi
===================================================================
--- /branches/stable/dns.cgi	(revision 581)
+++ /branches/stable/dns.cgi	(revision 582)
@@ -275,5 +275,5 @@
       # and reconstruct the URI argument list.
       my %target = (page => "domlist");
-      if ($webvar{target} && $webvar{target} =~ /\?/) {
+      if ($webvar{target} && $webvar{target} =~ /\?/ && $webvar{target} !~ /page=login/) {
         my $tmp = (split /\?/, $webvar{target})[1];
         $tmp =~ s/^\&//;
@@ -2004,4 +2004,9 @@
 
   foreach my $rec (@$foo2) {
+    # NS records.  Need to do this first before we convert the type-value to the text representation
+    if ($rev eq 'y' && $dnsdb->{showrec_arpa_ns} && $rec->{type} == $reverse_typemap{NS}) {
+      my $tmp = new NetAddr::IP $rec->{val};
+      $rec->{val} = DNSDB::_ZONE($tmp, 'ZONE', 'r', '.').($tmp->{isv6} ? '.ip6.arpa' : '.in-addr.arpa');
+    }
     $rec->{type} = $typemap{$rec->{type}};
     $rec->{fwdzone} = $rev eq 'n';
Index: /branches/stable/dnsdb.conf
===================================================================
--- /branches/stable/dnsdb.conf	(revision 581)
+++ /branches/stable/dnsdb.conf	(revision 582)
@@ -38,4 +38,7 @@
 #lowercase = 0
 
+# Show formal .arpa zone name instead of usual CIDR for reverse zone NS records?
+#showrec_arpa_ns = 0
+
 ## General RPC options
 # may already be obsolete.  how do we want to run RPC requests?
Index: /branches/stable/export.pl
===================================================================
--- /branches/stable/export.pl	(revision 581)
+++ /branches/stable/export.pl	(revision 582)
@@ -29,5 +29,6 @@
 my $dnsdb = new DNSDB;
 
+#open TINYDATA, ">small/tinydata";
 open TINYDATA, ">tinydata";
 
-$dnsdb->export('tiny', *TINYDATA);
+$dnsdb->export('tiny', *TINYDATA) or die "fatal: ".$dnsdb->errstr."\n";
Index: /branches/stable/templates/bulkdomain.tmpl
===================================================================
--- /branches/stable/templates/bulkdomain.tmpl	(revision 581)
+++ /branches/stable/templates/bulkdomain.tmpl	(revision 582)
@@ -38,5 +38,5 @@
 <table>
 <tr>
-<TMPL_LOOP NAME=domtable><td><input type="checkbox" name="dom_<TMPL_VAR NAME=domain_id>" value="<TMPL_VAR NAME=domain_id>" /> <TMPL_VAR NAME=domain></td>
+<TMPL_LOOP NAME=domtable><td><input type="checkbox" name="dom_<TMPL_VAR NAME=zoneid>" value="<TMPL_VAR NAME=zoneid>" /> <TMPL_VAR NAME=zone></td>
 <TMPL_IF newrow></tr>
 <tr>
Index: /branches/stable/templates/domlist.tmpl
===================================================================
--- /branches/stable/templates/domlist.tmpl	(revision 581)
+++ /branches/stable/templates/domlist.tmpl	(revision 582)
@@ -39,9 +39,9 @@
 <TMPL_LOOP name=domtable>
 <tr class="row<TMPL_IF __odd__>0<TMPL_ELSE>1</TMPL_IF>">
-	<td align="left"><a href="<TMPL_VAR NAME=script_self>&amp;page=reclist&amp;id=<TMPL_VAR NAME=domain_id>&amp;defrec=n<TMPL_UNLESS domlist>&amp;revrec=y</TMPL_UNLESS>"><TMPL_VAR NAME=domain></a></td>
+ 	<td align="left"><a href="<TMPL_VAR NAME=script_self>&amp;page=reclist&amp;id=<TMPL_VAR NAME=zoneid>&amp;defrec=n<TMPL_UNLESS domlist>&amp;revrec=y</TMPL_UNLESS>"><TMPL_VAR NAME=zone></a></td>
 	<td><TMPL_IF status>Active<TMPL_ELSE>Inactive</TMPL_IF></td>
 	<td><TMPL_VAR name=group></td>
-<TMPL_IF domain_edit>	<td align="center"><a href="<TMPL_VAR NAME=script_self>&amp;page=<TMPL_VAR NAME=curpage><TMPL_IF NAME=offset>&amp;offset=<TMPL_VAR NAME=offset></TMPL_IF>&amp;id=<TMPL_VAR NAME=domainid>&amp;zonestatus=<TMPL_IF status>domoff<TMPL_ELSE>domon</TMPL_IF>"><TMPL_IF status>deactivate<TMPL_ELSE>activate</TMPL_IF></a></td></TMPL_IF>
-<TMPL_IF domain_delete>	<td align="center"><a href="<TMPL_VAR NAME=script_self>&amp;page=<TMPL_IF domlist>deldom<TMPL_ELSE>delrevzone</TMPL_IF>&amp;id=<TMPL_VAR NAME=domain_id>"><img src="images/trash2.png" alt="[ Delete ]" /></a></td></TMPL_IF>
+<TMPL_IF domain_edit>	<td align="center"><a href="<TMPL_VAR NAME=script_self>&amp;page=<TMPL_VAR NAME=curpage><TMPL_IF NAME=offset>&amp;offset=<TMPL_VAR NAME=offset></TMPL_IF>&amp;id=<TMPL_VAR NAME=zoneid>&amp;zonestatus=<TMPL_IF status>domoff<TMPL_ELSE>domon</TMPL_IF>"><TMPL_IF status>deactivate<TMPL_ELSE>activate</TMPL_IF></a></td></TMPL_IF>
+<TMPL_IF domain_delete>	<td align="center"><a href="<TMPL_VAR NAME=script_self>&amp;page=<TMPL_IF domlist>deldom<TMPL_ELSE>delrevzone</TMPL_IF>&amp;id=<TMPL_VAR NAME=zoneid>"><img src="images/trash2.png" alt="[ Delete ]" /></a></td></TMPL_IF>
 </tr>
 </TMPL_LOOP>
Index: /branches/stable/textrecs.cgi
===================================================================
--- /branches/stable/textrecs.cgi	(revision 581)
+++ /branches/stable/textrecs.cgi	(revision 582)
@@ -75,5 +75,5 @@
 
 my $reclist = $dnsdb->getRecList(defrec => $webvar{defrec}, revrec => $webvar{revrec}, id => $webvar{id},
-	sortby => ($webvar{revrec} eq 'n' ? 'type,host' : 'type,val'), sortorder => 'ASC');
+	sortby => ($webvar{revrec} eq 'n' ? 'type,host' : 'type,val'), sortorder => 'ASC', offset => 'all');
 foreach my $rec (@$reclist) {
   $rec->{type} = $typemap{$rec->{type}};
@@ -83,4 +83,8 @@
   $rec->{val} = "$rec->{distance}  $rec->{weight}  $rec->{port}  $rec->{val}" if $rec->{type} eq 'SRV';
   if ($webvar{revrec} eq 'y') {
+    if ($dnsdb->{showrec_arpa_ns} && $rec->{type} eq 'NS') {
+      my $tmp = new NetAddr::IP $rec->{val};
+      $rec->{val} = DNSDB::_ZONE($tmp, 'ZONE', 'r', '.').($tmp->{isv6} ? '.ip6.arpa' : '.in-addr.arpa');
+    }
     printf "%-16s\t%d\t%s\t%s\n", $rec->{val}, $rec->{ttl}, $rec->{type}, $rec->{host};
   } else {
Index: /branches/stable/tiny-import.pl
===================================================================
--- /branches/stable/tiny-import.pl	(revision 581)
+++ /branches/stable/tiny-import.pl	(revision 582)
@@ -39,27 +39,55 @@
 	conv	=> 0,
 	trial	=> 0,
-	legacy  => 0,
+	legacy	=> 0,
+	merge	=> 0,
+	group	=> 1,
 	);
+my $gnum = '';
 # Handle some command-line arguments
 while ($ARGV[0] =~ /^-/) {
   my $arg = shift @ARGV;
-  usage() if $arg !~ /^-[rclt]+$/;
+  usage() if $arg !~ /^-(?:[rclmt]+|g\d*)$/;
   # -r  rewrite imported files to comment imported records
   # -c  coerce/downconvert A+PTR = records to PTR
   # -l  swallow A+PTR as-is
+  # -m  merge PTR and A/AAAA as possible
   # -t  trial mode;  don't commit to DB or actually rewrite flatfile (disables -r)
+  # -g  import to specified group (name or ID) instead of group 1
   $arg =~ s/^-//;
-  my @tmp = split //, $arg;
-  foreach (@tmp) {
-    $importcfg{rw} = 1 if $_ eq 'r';
-    $importcfg{conv} = 1 if $_ eq 'c';
-    $importcfg{legacy} = 1 if $_ eq 'l';
-    $importcfg{trial} = 1 if $_ eq 't';
-  }
+# for Reasons (none clear), $arg is undefined yet defined, but only when number characters are involved.  Ebbeh?
+no warnings qw(uninitialized);
+  if ($arg =~ /^g/) {
+    if ($arg eq 'g') {
+      $importcfg{group} = shift @ARGV;
+    } else {
+      $arg =~ s/^g//;
+      $importcfg{group} = $arg;
+    }
+  } else {
+    my @tmp = split //, $arg;
+    foreach (@tmp) {
+      $importcfg{rw} = 1 if $_ eq 'r';
+      $importcfg{conv} = 1 if $_ eq 'c';
+      $importcfg{legacy} = 1 if $_ eq 'l';
+      $importcfg{merge} = 1 if $_ eq 'm';
+      $importcfg{trial} = 1 if $_ eq 't';
+    }
+  }
+  use warnings qw(uninitialized);
 }
 $importcfg{rw} = 0 if $importcfg{trial};
 
+# allow group names
+if ($importcfg{group} =~ /^\d+$/) {
+  $importcfg{groupname} = $dnsdb->groupName($importcfg{group});
+} else {
+  $importcfg{groupname} = $importcfg{group};
+  $importcfg{group} = $dnsdb->groupID($importcfg{groupname});
+}
+
+die usage() if $importcfg{group} !~ /^\d+$/;
+
 sub usage {
-  die q(usage:  tiny-import.pl [-r] [-c] datafile1 datafile2 ... datafileN ...
+  die q(usage:  tiny-import.pl [-rclt] [-gnn] [-g name] datafile1 datafile2 ... datafileN ...
 	-r  Rewrite all specified data files with a warning header indicating the
 	    records are now managed by web, and commenting out all imported records.
@@ -72,4 +100,8 @@
 	-l  (for "legacy")  Force import of A+PTR records as-is.  Mutually exclusive
             with -c.  -l takes precedence as -c is lossy.
+	-m  Merge PTR and A or AAAA records to A+PTR or AAAA+PTR records where possible
+	-gnnn or -g nnn or -g name
+	    Import new zones into this group (group name or ID accepted) instead of
+	    the root/default group 1
 	-t  Trial run mode;  spits out records that would be left unimported.
 	    Disables -r if set.
@@ -83,4 +115,10 @@
 my $code;
 my $dbh = $dnsdb->{dbh};
+
+# collect some things for logging
+($dnsdb->{logusername}, undef, undef, undef, undef, undef, $dnsdb->{logfullname}) = getpwuid($<);
+$dnsdb->{loguserid} = 0;        # not worth setting up a pseudouser the way the RPC system does
+$dnsdb->{logusername} = $dnsdb->{logusername}."/tiny-import.pl";
+$dnsdb->{logfullname} = $dnsdb->{logusername} if !$dnsdb->{logfullname};
 
 $dbh->{AutoCommit} = 0;
@@ -129,4 +167,11 @@
   our $recsth = $dbh->prepare("INSERT INTO records (domain_id,rdns_id,host,type,val,distance,weight,port,ttl,location,stamp,expires,stampactive) ".
 	" VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)");
+
+  # for A/AAAA records
+  our $revcheck = $dbh->prepare("SELECT rdns_id,record_id,ttl FROM records WHERE host=? AND val=? AND type=12");
+  our $mergefwd = $dbh->prepare("UPDATE records SET type=?,domain_id=?,ttl=? WHERE record_id=?");
+  # for PTR records
+  our $fwdcheck = $dbh->prepare("SELECT domain_id,record_id,ttl FROM records WHERE host=? AND val=? AND (type=1 OR type=28)");
+  our $mergerev = $dbh->prepare("UPDATE records SET type=?,rdns_id=?,ttl=? WHERE record_id=?");
 
   my %deleg;
@@ -451,7 +496,22 @@
 	($rparent) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >> ?", undef, ("$msg"));
       }
+
       if ($rparent) {
-	($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
-	$recsth->execute(0, $rparent, $host, 12, $msg->addr, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
+##fixme:  really want to pull this DB call inside an if $importcfg{merge},
+# but then we need to duplicate the insert for the case where the matching
+# reverse doesn't exist.
+        $host =~ s/\.$//g;   # pure sytactic sugar, we don't store this trailing dot.
+        $fwdcheck->execute($host, $msg->addr);
+        my ($domid, $recid, $rttl) = $fwdcheck->fetchrow_array;
+        if ($importcfg{merge} && $domid) {
+          $ttl = ($rttl < $ttl ? $rttl : $ttl);        # Take the shorter TTL
+          $mergerev->execute(($msg->{isv6} ? 65281 : 65280), $rparent, $ttl, $recid);
+          $dnsdb->_log(rdns_id => $rparent, domain_id => $domid, group_id => $importcfg{group},
+            entry => "[ import ] PTR ".$msg->addr." -> $host merged with matching ".
+                  ($msg->{isv6} ? 'AAAA' : 'A')." record");
+        } else {
+	  ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
+	  $recsth->execute(0, $rparent, $host, 12, $msg->addr, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
+        }
       } else {
 	push @deferred, $rec unless $nodefer;
@@ -476,6 +536,19 @@
       my $domid = $dnsdb->_hostparent($host);
       if ($domid) {
-	($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
-	$recsth->execute($domid, 0, $host, 1, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
+##fixme:  really want to pull this DB call inside an if $importcfg{merge},
+# but then we need to duplicate the insert for the case where the matching
+# reverse doesn't exist.
+        $revcheck->execute($host, $ip);
+        my ($revid, $recid, $rttl) = $revcheck->fetchrow_array;
+        if ($importcfg{merge} && $revid) {
+          $ttl = ($rttl < $ttl ? $rttl : $ttl);	# Take the shorter TTL
+          $mergefwd->execute(65280, $domid, $ttl, $recid);
+          $dnsdb->_log(rdns_id => $revid, domain_id => $domid, group_id => $importcfg{group},
+            entry => "[ import ] ".($msg->{isv6} ? 'AAAA' : 'A')." record $host -> $ip".
+                  " merged with matching PTR record");
+        } else {
+	  ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
+	  $recsth->execute($domid, 0, $host, 1, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
+        }
       } else {
 	push @deferred, $rec unless $nodefer;
@@ -514,6 +587,6 @@
       if ($zone =~ /\.arpa$/) {
 	($code,$msg) = DNSDB::_zone2cidr($zone);
-	$dbh->do("INSERT INTO revzones (revnet,group_id,status,default_location) VALUES (?,1,1,?)",
-		undef, ($msg, $loc));
+	$dbh->do("INSERT INTO revzones (revnet,group_id,status,default_location) VALUES (?,?,1,?)",
+		undef, ($msg, $importcfg{group}, $loc));
 	my ($rdns) = $dbh->selectrow_array("SELECT currval('revzones_rdns_id_seq')");
 	my $newttl;
@@ -523,6 +596,6 @@
 		$loc, $stamp, $expires, $stampactive);
       } else {
-	$dbh->do("INSERT INTO domains (domain,group_id,status,default_location) VALUES (?,1,1,?)",
-		undef, ($zone, $loc));
+	$dbh->do("INSERT INTO domains (domain,group_id,status,default_location) VALUES (?,?,1,?)",
+		undef, ($zone, $importcfg{group}, $loc));
 	my ($domid) = $dbh->selectrow_array("SELECT currval('domains_domain_id_seq')");
 	my $newttl;
@@ -748,11 +821,28 @@
 	my $fparent = $dnsdb->_hostparent($fqdn);
 
-	if ($fparent) {
-	  ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $fparent, 'n');
-	  $recsth->execute($fparent, 0, $fqdn, 28, $val->addr, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
-	} else {
-	  push @deferred, $rec unless $nodefer;
-	  $impok = 0;
-	}
+##fixme:  really want to pull this DB call inside an if $importcfg{merge},
+# but then we need to duplicate the insert for the case where the matching
+# reverse doesn't exist.
+        $revcheck->execute($fqdn, $val);
+        my ($revid, $recid, $rttl) = $revcheck->fetchrow_array;
+
+        # If we have a revzone and merging is enabled, update the existing
+        # record with a reverse ID, set the type to one of the internal
+        # pseudotypes, and set the TTL to the lower of the two.
+        if ($importcfg{merge} && $revid) {
+          $ttl = ($rttl < $ttl ? $rttl : $ttl);	# Take the shorter TTL
+          $mergefwd->execute(65281, $fparent, $ttl, $recid);
+          $dnsdb->_log(rdns_id => $revid, domain_id => $fparent, group_id => $importcfg{group},
+            entry => "[ import ] ".($msg->{isv6} ? 'AAAA' : 'A')." record $fqdn -> $val".
+                  " merged with matching PTR record");
+        } else {
+	  if ($fparent) {
+	    ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $fparent, 'n');
+	    $recsth->execute($fparent, 0, $fqdn, 28, $val->addr, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
+	  } else {
+	    push @deferred, $rec unless $nodefer;
+	    $impok = 0;
+	  }
+        }
 
       } elsif ($type == 16) {
