#!/usr/bin/perl
#
# Author: Geoff Huston, APNIC (c) 2008
#         gih@apnic.net
#
# Installation:
#  This uses the 'combined RIR stats file' A copy of this
#  file (updated daily) is at http://bgp.potaroo.net/stats/nro/delegated
#
# Usage:
#  command line tool: ipv6addr <v6address>
#  cgi-tool:          copy to local cgi-bin directory
#
#
use CGI qw(:cgi-lib);
use Math::BigInt;
$| = 1 ;

$nro_delegated = "/var/data/bgp/stats/nro/delegated" ;
if (!(-e $nro_delegated)) {
  $nro_delegated = "./delegated" ;
  }

&ReadParse();
if (!($prefix = $in{"pfx"})) {
  $addr = shift ;
  $html = 0  ;
  }
else {
  $html = 1 ;
  $addr = $prefix ;
  $addr =~ s/\-/:/g ;
  }

$addr =~ s/^\s+// ;
$addr =~ s/\s+$// ;

if ($html) {
  print("Content-type: text/html\n\n") ;
  print("<html><title>IPv6 Whois</title></head>\n") ;
  print("<body bgcolor=\"white\">\n<blockquote\n");
  print("<h2>IPv6 Address Report</h3>\n<ul>\n<p>\n") ;
  print("<table border=\"0\" cellpadding=\"0\" ");
  print(" cellspacing=\"0\">\n") ;
  print("<tr><td>IPv6Address:</td><td>&nbsp;</td><td>$addr</td><tr>\n") ;
  }

report($addr,$html) ;
if ($html) {
  print("</table>\n<pre>\n\n\n\n</pre></blockquote><hr><br>&nbsp;<br>");
  print_form($addr) ;
  print("</body>\n</html>\n") ;
  }
exit(0) ;




sub report
{
  local($addr,$html) = @_ ;
  $addr =~ s/\%.*$// ;
  $addr =~ tr/A-Z/a-z/ ;
  if ($addr =~ /^0x[0-9a-f]+$/) {
    $h = Math::BigInt->new($addr) ;
    for ($i = 0 ; $i <= 7 ; ++$i) { $dec[$i] = 0 ; } ;
    $i = 7 ;
    $hstr = $h->as_hex() ;
    while ((!($h->is_zero())) && ($i >= 0)) {
      $quad = $h->copy() ;
      $quad->band(0xFFFF) ;
      $dec[$i] = $quad->numify() ;
      $h->brsft(16) ;
      --$i ;
      }
    if (!($h->is_zero())) {
      if ($html) {
        print("<tr><td><b>ERROR:</b></td><td></td><td> $addr overflow (> 2**128)</td></tr>\n") ;
        }
      else {
        print("ERROR: $addr overflow (> 2**128)\n") ;
        }
      return;
      }
    $addr = sprintf("%X:%X:%X:%X:%X:%X:%X:%X",$dec[0],$dec[1],$dec[2],$dec[3],$dec[4],$dec[5],$dec[6],$dec[7]);
    }
  $addr =~ tr/a-z/A-Z/ ;
  if ($addr !~ /^[0-9A-F\.\:\/]+$/) {
    if ($html) {
      print("<tr><td><b>ERROR:</b></td><td></td><td> $addr contains unrecognised characters</td></tr>\n") ;
    } else {  print("ERROR: $addr contains unrecognised characters\n") ; }
    return ;
    }
  $mask = -1 ;
  if ($addr =~ /\/(\d+)$/) {
    $mask = $1 ;
    if ($mask > 128) {
      if ($html) {
        print("<tr><td><b>ERROR:</b></td><td></td><td> $addr has a prefix length >128</td></tr>\n") ;
      } else { print("ERROR: $addr has a prefix length >128\n") ; }
      return;
      }
    $addr =~ s/\/\d+$// ;
    }

  $addr =~ tr/a-z/A-Z/ ;

  # need to detect a.b.c.d points
  # first is ::FFFF:w.x.y.z
  if ($addr =~ /^\:\:FFFF\:(\d+).(\d+).(\d+).(\d+)$/) {
    $q[0] = $1 ;
    $q[1] = $2 ;
    $q[2] = $3 ;
    $q[3] = $4 ;
    $dec[6] = ($q[0] << 8) + $q[1] ;
    $dec[7] = ($q[2] << 8) + $q[3] ;
    $addr = sprintf("::FFFF:%X:%X",$dec[6],$dec[7]) ;
    }

  # second is ::a.b.c.d
  elsif ($addr =~ /^\:\:(\d+).(\d+).(\d+).(\d+)$/) {
    $q[0] = $1 ;
    $q[1] = $2 ;
    $q[2] = $3 ;
    $q[3] = $4 ;
    $dec[6] = ($q[0] << 8) + $q[1] ;
    $dec[7] = ($q[2] << 8) + $q[3] ;
    $addr = sprintf("::%X:%X",$dec[6],$dec[7]) ;
    }

  # third is 2002:a.b.c.d:<rest> (6to4)
  elsif ($addr =~ /^2002\:(\d+).(\d+).(\d+).(\d+)\:(.*)$/) {
    $q[0] = $1 ;
    $q[1] = $2 ;
    $q[2] = $3 ;
    $q[3] = $4 ;
    $rest = $5 ;
    $dec[1] = ($q[0] << 8) + $q[1] ;
    $dec[2] = ($q[2] << 8) + $q[3] ;
    $addr = sprintf("2002:%X:%X:%s",$dec[1],$dec[2],$rest) ;
    }

  if ($addr =~ /^([0-9A-F]+)\:([0-9A-F]+)\:([0-9A-F]+)\:([0-9A-F]+)\:([0-9A-F]+)\:([0-9A-F]+)\:([0-9A-F]+)\:([0-9A-F]+)$/) {
    $n[0] = $1 ;
    $n[1] = $2 ;
    $n[2] = $3 ;
    $n[3] = $4 ;
    $n[4] = $5 ;
    $n[5] = $6 ;
    $n[6] = $7 ;
    $n[7] = $8 ;
    }
  elsif ($addr =~ /\:\:.*\:\:/) {
    if ($html) {
      print("<tr><td><b>ERROR:</b></td><td></td><td> $addr - Cannot parse address</td></tr>\n") ;
    } else {  print("ERROR: $addr - Cannot parse address\n") ; }
    return;
    }
  elsif ($addr =~ /\:\:\:/) {
    if ($html) {
      print("<tr><td><b>ERROR:</b></td><td></td><td> $addr - Cannot parse address</td></tr>\n") ;
    } else { print("ERROR:  $addr - Cannot parse address\n") ; }
    return;
    }
  elsif ($addr =~ /^(.*)\:\:(.*)$/) {
    $fh = $1 ;
    $lh = $2 ;
    @fha = split(':',$fh) ;
    @lha = split(':',$lh) ;
    if ($#fha >= 7) {
      if ($html) {
        print("<tr><td><b>ERROR:</b></td><td></td><td> $addr - too many leading nibble fields in the address</td></tr>\n") ;
      } else { print("ERROR: $addr - too many leading nibble fields in the address\n") ; }
      return;
      }
    if ($#lha >= 7) {
            if ($html) {
        print("<tr><td><b>ERROR:</b></td><td></td><td> $addr - too many trailing nibble fields in the address</td></tr>\n") ;
      } else { print("ERROR: $addr - too many trailing nibble fields in the address\n") ; }
      return;
      }
    $fn = $#fha + 1 ;
    $ln = $#lha + 1 ;
    if ($ln + $fn >= 8) {
            if ($html) {
        print("<tr><td><b>ERROR:</b></td><td></td><td> $addr - too many nibble fields</td></tr>\n") ;
      } else { print("ERROR:  $addr - too many nibble fields\n") ; }
      return;
      }
    if ($fn > 0) {
      for ($i = 0 ; $i < $fn ; ++$i) {
        $n[$i] = $fha[$i] ;
        if ($fha[$i] !~ /^[0-9A-F]+$/) {
                if ($html) {
        print("<tr><td><b>ERROR:</b></td><td></td><td> $addr - non-hex nibble fields in leading part</td></tr>\n") ;
      } else { print("ERROR:  $addr - non-hex nibble fields in leading part\n") ; }
          return;
          }
        }
      }
    else {
      $n[0] = 0 ;
      $i = 0 ;
      }
    ++$i ;
    for (;$i < 8 ; ++$i) {
      $n[$i] = 0 ;
      }
    for ($i = 0 ; $i < $ln ; ++$i) {
      $n[8 - $ln + $i] = $lha[$i] ;
      if ($lha[$i] !~ /^[0-9A-F]+$/) {
              if ($html) {
        print("<tr><td><b>ERROR:</b></td><td></td><td> $addr - non-hex nibble fields in trailing part</td></tr>\n") ;
      } else { print("ERROR:  $addr - non-hex nibble fields in trailing part\n") ; }
        return;
        }
      }
    }
  else {
          if ($html) {
        print("<tr><td><b>ERROR:</b></td><td></td><td> $addr - Cannot parse IPv6 address</td></tr>\n") ;
      } else { print("ERROR:  $addr - Cannot parse IPv6 address\n") ; }
    return;
    }

  $addrv = Math::BigInt->bzero() ; ;
  for ($i = 0 ; $i < 8 ; ++$i) {
    $cv = "0x" . $n[$i] ;
    $dec[$i] = hex($cv) ;
#   printf("%x",$dec[$i]) ;
#   if ($i < 7) { print(":"); }
    $addrv->bmul(65536) ;
    $addrv->badd($dec[$i]) ;
    }
# print("\n") ;
# print("----\n") ;

  for ($i = 0 ; $i < 8 ; ++$i) {
    if ($dec[$i]) { $zb[$i] = 0 ; }
    elsif ($i) { $zb[$i] = $zb[$i -1] + 1 ; }
    else { $zb[$i] = 1 ; }
    }

  $maxv = 0 ;
  $maxi = -1 ;
  for ($i = 0 ; $i < 8 ; ++$i) {
    if ($zb[$i] > $maxv) {
      $maxi = $i ; 
      $maxv = $zb[$i] ;
      }
    }

# maxi - index of end of zero chain
# maxv - length of zero chain

  if (($maxi > 0) && ($maxv > 1)) {
    $ss = 0 ;
    $se = $maxi - $maxv ; 
    $es = $maxi + 1 ;
    $ee = 7 ;
    if ($se >= $ss) {
      $caddr = "" ;
      for ($i = $ss ; $i <= $se ; ++$i) {
        $ostr = sprintf("%X:",$dec[$i]) ;
        $caddr .= $ostr ;
        }
      }
    else {
      $caddr = ":" ;
      }
    if ($ee >= $es) {
      for ($i = $es ; $i <= $ee ; ++$i) {
        $ostr = sprintf(":%X",$dec[$i]) ;
        $caddr .= $ostr ;
        }
      }
    else {
      $caddr .= ":" ;
      }
    }
  else {
    $caddr = "" ;
    for ($i = 0 ; $i <= 7 ; ++$i) {
      $ostr = sprintf("%X",$dec[$i]) ;
      if ($i > 0) {$caddr .= ":" ; }
      $caddr .= $ostr ;
      }
    }

  $decstr = $addrv->bstr ;
  $hexstr = $addrv->as_hex ;

# print("$hexstr\n") ;
# print("$decstr\n") ;

  $cfaddr = "" ;
  for ($i = 0 ; $i < 8 ; ++$i) {
    $cfaddr .= sprintf("%X:",$dec[$i]) ;
    }
  chop($cfaddr) ;

  if ($addrv->is_zero()) {
    if ($mask >= 0) {
      if (!$mask) {
        if ($html) {
          print("<tr><td>AddressType:</td><td></td><td> ::/0 - Default Route</td></tr>\n");
          }
        else {
          print("AddressType: ::/0 - Default Route\n");
          }
        return;
        }
      elsif ($mask < 128) {
        if ($html) {
          print("<tr><td><b>ERROR:</b></td><td></td><td> ::/$mask Unspecified Mask</td></tr>\n") ;
          } 
        else { 
          print("ERROR:  ::/$mask Unspecified Mask\n") ; 
          }
        return;
        }
      }
    if ($html) {
      print("<tr><td>AddressType:</td><td></td><td> ::/128 - Unspecified Address</td></tr>\n");
      }
    else {
      print("AddressType: ::/128 - Unspecified Address\n");
      }
    return;
    }

  # check that masked out bits are all zero
  if (($mask > 0) && ($mask < 128)) {
    $maskv = Math::BigInt->bone() ;
    $shift = 128 - $mask ;
    $maskv->blsft($shift) ;
    $maskv->bsub(1) ;
    $maskv->band($addrv) ;
    if (!($maskv->is_zero)) {
      if ($html) {
        print("<tr><td><b>ERROR:</b></td><td></td><td> $caddr/$mask - Error in prefix specification: non-zero suffix</td></tr>\n") ;
        }
      else { 
        print("ERROR:  $caddr/$mask - Error in prefix specification: non-zero suffix\n") ; 
        }
      return;
      }
    }

  if ($mask == 0) {
    if ($html) {
      print("<tr><td><b>ERROR:</b></td><td></td><td> $caddr/$mask - Error in default route specification:  non-zero suffix</td></tr>\n") ;
      } 
    else { 
      print("ERROR:  $caddr/$mask - Error in default route specification:  non-zero suffix\n") ; 
      }
    return;
    }

  if ($addrv->is_one()) {
    if ($html) {
      print("<tr><td>AddressType:</td><td></td><td> ::1/128 - Local Loopback Address</td></tr>\n");
      } 
    else {
      print("AddressType: ::1/128 - Local Loopback Address\n");
      }
    return;
    }

  if ((!$dec[0]) && (!$dec[1]) && (!$dec[2]) && (!$dec[3]) && (!$dec[4]) && ($dec[5] == 0xffff)) {
    $q[0] = $dec[6] >> 8 ;
    $q[1] = $dec[6] & 255 ;
    $q[2] = $dec[7] >> 8 ;
    $q[3] = $dec[7] & 255 ;
    $faddr = sprintf("::FFFF:%d.%d.%d.%d",$q[0],$q[1],$q[2],$q[3]) ;
    if (($mask >= 0) && ($mask < 128)) {
      if ($html) {
        print("<tr><td><b>ERROR:</b></td><td></td><td> $faddr/$mask Error in prefix specification:  non-zero suffix</td></tr>\n") ;
        } 
      else { 
        print("ERROR:  $faddr/$mask Error in prefix specification:  non-zero suffix\n") ; 
        }
      return;
      }
    $faddr .= "/128" ;
    $caddr .= "/128" ;
    $v4addr = sprintf("%d.%d.%d.%d",$q[0],$q[1],$q[2],$q[3]);
    if ($html) {
      print("<tr><td>AddressType:</td><td></td><td> $caddr  - IPv4 mapped address ($v4addr)</td></tr>\n") ;
      } 
    else {
      print("AddressType: $caddr  - IPv4 mapped address ($v4addr)\n") ;
      }
    v4whois($v4addr) ;
    return;
    }
  if ((!$dec[0]) && (!$dec[1]) && (!$dec[2]) && (!$dec[3]) && (!$dec[4]) && (!$dec[5])) {
    $q[0] = $dec[6] >> 8 ;
    $q[1] = $dec[6] & 255 ;
    $q[2] = $dec[7] >> 8 ;
    $q[3] = $dec[7] & 255 ;
    if ($q[0]) {
      if ($html) {
        printf("<tr><td>AddressType:</td><td></td><td> $caddr - DEPRECATED IPv4 compatible address (%d.%d.%d.%d)</td></tr>\n",$q[0],$q[1],$q[2],$q[3]) ;
        }
      else {
        printf("AddressType:  $caddr - DEPRECATED IPv4 compatible address (%d.%d.%d.%d)\n",$q[0],$q[1],$q[2],$q[3]) ;
        }
      return;
      }
    if ($html) {
      print("<tr><td>AddressType:</td><td></td><td> $caddr - UNDEFINED Unknown address</td></tr>\n") ;
      }
    else {
      print("AddressType:  $caddr - UNDEFINED Unknown address\n") ;
      }
    return;
    }

  if (($dec[0] >> 8) == 0xfc) {
    if ($html) {
      print("<tr><td>AddressType:</td><td></td><td> $caddr - UNDEFINED Unique Local Address with Local flag cleared</td></tr>\n") ;
      }
    else {
      print("AddressType: $caddr - UNDEFINED Unique Local Address with Local flag cleared\n") ;
      }
    return;
    }

  if (($dec[0] >> 8) == 0xfd) {
    $globalid = (($dec[0] & 255) << 32) + ($dec[1] << 16) + ($dec[2]) ;
    $subnet = $dec[3];
    $iid = ($dec[4] << 48) + ($dec[5] << 32) + ($dec[6] << 16 + $dec[7]) ;
    if ($html) {
    printf("<tr><td>AddressType:</td><td></td><td> $caddr - ULA Locally Assigned Unique Local Address</td></tr>\n") ;
    printf("<tr><td>ULA:</td><td></td><td>         FC</td></tr>\n") ;
    printf("<tr><td>LocalFlag:</td><td></td><td>   1</td></tr>\n");
    printf("<tr><td>GlobalId:</td><td></td><td>    %X:%X:%X</td></tr>\n",($dec[0] & 255), $dec[1], $dec[2]);
    printf("<tr><td>SubnetId:</td><td></td><td>    %X</td></tr>\n",$subnet) ;
    printf("<tr><td>InterfaceId:</td><td></td><td> %X:%X:%X:%X</td></tr>\n",$dec[4],$dec[5],$dec[6],$dec[7]);  
    }
else {
    printf("AddressType: $caddr - ULA Locally Assigned Unique Local Address\n") ;
    printf("ULA:         FC\n") ;
    printf("LocalFlag:   1\n");
    printf("GlobalId:    %X:%X:%X\n",($dec[0] & 255), $dec[1], $dec[2]);
    printf("SubnetId:    %X\n",$subnet) ;
    printf("InterfaceId: %X:%X:%X:%X\n",$dec[4],$dec[5],$dec[6],$dec[7]);
  }
    return;
    }

  if (($dec[0] & 0xffa0) == 0xfe80) {
    if (($dec[0] != 0xfe80) || ($dec[1]) || ($dec[2]) || ($dec[3])) {
      if ($html) {
        printf("<tr><td>AddressType:</td><td></td><td> $addr - UNDEFINED Link Local Address</td></tr>\n") ;
        }
      else {
        printf("AddressType: $addr - UNDEFINED Link Local Address\n") ;
        }
      return;
      }
    $iid = ($dec[4] << 48) + ($dec[5] << 32) + ($dec[6] << 16 + $dec[7]) ;
    if ($html) {
      printf("<tr><td>AddressType:</td><td></td><td> $addr - Link Local Address</td></tr>\n") ;
      printf("<tr><td>LinkLocal:</td><td></td><td>   FE80</td></tr>\n") ;
      printf("<tr><td>InterfaceId:</td><td></td><td> %X:%X:%X:%X</td></tr>\n",$dec[4],$dec[5],$dec[6],$dec[7]);  
      }
    else {
      printf("AddressType: $addr - Link Local Address\n") ;
      printf("LinkLocal:   FE80\n") ;
      printf("InterfaceId: %X:%X:%X:%X\n",$dec[4],$dec[5],$dec[6],$dec[7]);  
      }
    return;
    }

  if (($dec[0] >> 8) == 0xff) {
    # multicast
    $flags = (($dec[0] >> 4) & 15);
    $scope = ($dec[0] & 15) ;
    if ($html) {
      printf("<tr><td>AddressType:</td><td></td><td> $addr - Multicast Address</td></tr>\n");
      printf("<tr><td>Flags:</td><td></td><td>     %X</td></tr>\n",$flags) ;
      printf("<tr><td>Scope:</td><td></td><td>     %X</td></tr>\n",$scope);
      printf("<tr><td>Group Identifier:</td><td></td><td>   %X:%X:%X:%X:%X:%X:%X</td></tr>\n",$dec[1],$dec[2],$dec[3],$dec[4],$dec[5],$dec[6],$dec[7]);
      } 
    else {
      printf("AddressType: $addr - Multicast Address\n");
      printf("Flags:     %X\n",$flags) ;
      printf("Scope:     %X\n",$scope);
      printf("Group Identifier:   %X:%X:%X:%X:%X:%X:%X\n",$dec[1],$dec[2],$dec[3],$dec[4],$dec[5],$dec[6],$dec[7]);
      }
    return;
    }

  # global unicast
  if (($dec[0] >> 13) == 1) {
    # 2000::/3
    if (($dec[0] == 0x2001) && (($dec[1] >> 9) == 0)) {
      # iana special
      if ($dec[1] == 0) {
        # teredo
        $t_server =( $dec[2] << 16) + $dec[3] ;
        $flags = $dec[4] ;
        $cone = $flags == 0x8000 ;
        $port = $dec[5] ;
        $port = $port ^ 0xffff ;
        $t_client = ($dec[6] << 16) + $dec[7] ;
        $t_client = $t_client ^ 0xffffffff ;

        $s[0] = ($t_server >> 24) & 255 ;
        $s[1] = ($t_server >> 16) & 255 ;
        $s[2] = ($t_server >> 8) & 255;
        $s[3] = ($t_server & 255) ;
        $t_s = sprintf("%d.%d.%d.%d",$s[0],$s[1],$s[2],$s[3]);

        $c[0] = ($t_client >> 24) & 255 ;
        $c[1] = ($t_client >> 16) & 255 ;
        $c[2] = ($t_client >> 8) & 255 ;
        $c[3] = ($t_client & 255) ;
        $t_c = sprintf("%d.%d.%d.%d",$c[0],$c[1],$c[2],$c[3]);
        if ($html) {
          print("<tr><td>AddressType:</td><td></td><td>   $caddr - TEREDO mapped address</td></tr>\n") ;
          printf("<tr><td>ConeFlag:</td><td></td><td>     %d</td></tr>\n",($cone ? 1 : 0)) ;
          printf("<tr><td>TeredoServer:</td><td></td><td> %d.%d.%d.%d</td></tr>\n",$s[0],$s[1],$s[2],$s[3]);
          printf("<tr><td>TeredoClient:</td><td></td><td> %d.%d.%d.%d:%d</td></tr>\n",$c[0],$c[1],$c[2],$c[3],$port);
          printf("<tr><td>ServerIPv4Address:</td><td></td><td> $t_s</td></tr>\n") ;
}
else {
        print("AddressType:   $caddr - TEREDO mapped address\n") ;
        printf("ConeFlag:     %d\n",($cone ? 1 : 0)) ;
        printf("TeredoServer: %d.%d.%d.%d\n",$s[0],$s[1],$s[2],$s[3]);
        printf("TeredoClient: %d.%d.%d.%d:%d\n",$c[0],$c[1],$c[2],$c[3],$port);
        printf("ServerIPv4Address: $t_s\n") ;

}
        v4whois($t_s) ;
        if ($html) {
          printf("<tr><td>ClientIPv4Address:</td><td></td><td> $t_c</td></tr>\n") ;
          }
        else {
          printf("ClientIPv4Address: $t_c\n") ; 
          }
        v4whois($t_c) ;
        return;
        }
      elsif (($dec[1] == 0x200) && ($dec[2] == 0)) {
        #bmwg = 5180
        if ($html) {
        print("<tr><td>AddressType:</td><td></td><td> $caddr - Unrouted IANA Special Use address - Benchmark prefix - see <a href=\"http://rfc5180.potaroo.net\">RFC5180</a></td></tr>\n") ;
} else {
        print("AddressType: $caddr - Unrouted IANA Special Use address - Benchmark prefix - see RFC5180\n") ;
      }
        return;
        }
      elsif (($dec[1] >> 4) == 1) {
        if ($html) {
        print("<tr><td>AddressType</td><td></td><td>: $caddr - Unrouted IANA Special Use address - ORCHID prefix - see <a href=\"http://rfc4843.potaroo.net\">RFC4843</a></td></tr>\n") ;
      } else {
        print("AddressType: $caddr - Unrouted IANA Special Use address - ORCHID prefix - see RFC4843\n") ;
      }
        return;
        }
            if ($html) {
        print("<tr><td>AddressType:</td><td></td><td> $caddr - UNDEFINED Unassigned IANA Special Use address</td></tr>\n") ;
      } else {
        print("<tr><td>AddressType:</td><td></td><td> $caddr - UNDEFINED Unassigned IANA Special Use address\n") ;
      }
      return;
      }

    if ($dec[0] == 0x2002) {
      $q[0] = $dec[1] >> 8 ;
      $q[1] = $dec[1] & 255 ;
      $q[2] = $dec[2] >> 8 ;
      $q[3] = $dec[2] & 255 ;
      $v4addr = sprintf("%d.%d.%d.%d",$q[0],$q[1],$q[2],$q[3]);
      $subnet = $dec[3];
      $iid = ($dec[4] << 48) + ($dec[5] << 32) + ($dec[6] << 16 + $dec[7]) ;
      if ($html) {
      printf("<tr><td>AddressType:</td><td></td><td> $caddr 6to4 address</td></tr>\n") ;
      printf("<tr><td>Subnet48Id:</td><td></td><td>  %X (/48)</td></tr>\n",$subnet) ;
      printf("<tr><td>InterfaceId:</td><td></td><td> %X:%X:%X:%X</td></tr>\n", $dec[4],$dec[5],$dec[6],$dec[7]);  
      printf("<tr><td>6to4IPv4:</td><td></td><td> $v4addr</td></tr>\n") ;
} else {
      printf("AddressType: $caddr 6to4 address\n") ;
      printf("Subnet48Id:  %X (/48)\n",$subnet) ;
      printf("InterfaceId: %X:%X:%X:%X\n", $dec[4],$dec[5],$dec[6],$dec[7]);  
      printf("6to4IPv4:    $v4addr\n") ;
    }
      v4whois($v4addr) ;
      return;
      }

    if ($dec[0] == 0x2001 && $dec[1] == 0xdb8) {
      #documentation
      if ($html) {
      print("<tr><td>AddressType:</td><td></td><td> $caddr - Documentation Prefix, Reserved address</td></tr>\n") ;
} else {
      print("AddressType: $caddr - Documentation Prefix, Reserved address\n") ;
    }
      return ;
      }

    $slash32 = ($dec[0] << 16) + ($dec[1]) ;
    @st = (0x20010200,0x20010400,0x20010600,0x20010C00,0x20011200,0x20011400,0x20014000,0x20018000,0x20030000,0x24000000,0x26000000,0x26100000,0x26200000,0x28000000,0x2A000000,0x2C000000);

    @en = (0x200103FF,0x200105FF,0x20010BFF,0x20010FFF,0x200113FF,0x20013BFF,0x20015FFF,0x2001BFFF,0x20033FFF,0x24FFFFFF,0x26FFFFFF,0x261001FF,0x262001FF,0x28FFFFFF,0x2AFFFFFF,0x2CFFFFFF);

    @regs = ("APNIC","ARIN","RIPE NCC","APNIC","LACNIC","RIPE NCC","RIPE NCC","APNIC","RIPE NCC","APNIC","ARIN","ARIN","ARIN","LACNIC","RIPE NCC","AFRINIC");

    $i = 0 ;
    $entries = $#st ;
    $found = $entries + 1 ;
    while ($i <= $entries) {
      if (($slash32 >= $st[$i]) && ($slash32 <= $en[$i])) {
        $found = $i ;
        $i = $entries + 1 ;
        }
      else { ++$i ; }
      }
    if ($found > $entries) {
      if ($html) {
        print("<tr><td>AddressType:</td><td></td><td> $addr - UNALLOCATED Global Unicast address - no registry details</td></tr>\n") ;
        }
      else {
        print("AddressType: $addr - UNALLOCATED Global Unicast address - no registry details\n") ;
        }
      return ;
      }
    $subnet = $dec[3];
    $iid = ($dec[4] << 48) + ($dec[5] << 32) + ($dec[6] << 16 + $dec[7]) ;
    if ($html) {
      print("<tr><td>AddressType:</td><td></td><td>       $caddr - RIR-Managed Global Unicast Address</td></tr>\n") ;
      printf("<tr><td>Subnet48Id:</td><td></td><td>        %X\ (/48)</td></tr>\n",$subnet) ;
      printf("<tr><td>Subnet56Id:</td><td></td><td>        %X\ (/56)</td></tr>\n",($subnet & 255)) ;
      printf("<tr><td>InterfaceId:</td><td></td><td>       %X:%X:%X:%X</td></tr>\n",$dec[4],$dec[5],$dec[6],$dec[7]);  
      print("<tr><td>AllocationRegistry:</td><td></td><td> $regs[$found]\n") ;
      }
    else {
      print("AddressType: $caddr - RIR-Managed Global Unicast Address\n") ;
      printf("Subnet48Id:  %X\ (/48)\n",$subnet) ;
      printf("Subnet56Id:  %X\ (/56)\n",($subnet & 255)) ;
      printf("InterfaceId: %X:%X:%X:%X\n",$dec[4],$dec[5],$dec[6],$dec[7]);  
      print("AllocationRegistry: $regs[$found]\n") ;
      }
    if (-e $nro_delegated) {
      delegation_info(@dec) ; 
      }
    v6whois($caddr) ;
    return;
    }

# Depecated formats
  #  0200::7 NSAP
  if (($dec[0] >> 9) == 1) {
    if ($html) {
    printf("<tr><td>AddressType:</td><td></td><td> $caddr - DEPRECATED mapped NSAP unicast address</td></tr>\n");
    } else {
    printf("AddressType: $caddr - DEPRECATED mapped NSAP unicast address\n");
  }
    return ;
    }

  # 3ffe::/16 - 6bone
  if (($dec[0] == 0x3ffe) && (!$dec[1])) {
    if ($html) {
    printf("<tr><td>AddressType:</td><td></td><td> $caddr - DEPRECATED 6bone unicast address</td></tr>\n");
    } else {
    printf("AddressType: $caddr - DEPRECATED 6bone unicast address\n");
  }
    return ;
    }

  # 5f00::/8 - ipv6 test network
  if (($dec[0]>> 8) == 0x5f) {
    if ($html) {
    printf("<tr><td>AddressType:</td><td></td><td> $caddr - DEPRECATED IPv6 test network unicast address</td></tr>\n");
    } else {
    printf("AddressType: $caddr - DEPRECATED IPv6 test network unicast address\n");
  }
    return ;
    }

  # fec0""/10 - old site local
  if (($dec[0] & 0xffc0) == 0xfec0) {
    if ($html) {
    printf("<tr><td>AddressType:</td><td></td><td> $caddr - DEPRECATED Site Local scoped address</td></tr>\n");
    } else {
    printf("AddressType: $caddr - DEPRECATED Site Local scoped address\n");
  }
    return ;
    }

  # then do the rest of the global unicast
  if ($mask == -1) {
    if ($html) {
    printf("<tr><td>AddressType:</td><td></td><td> $caddr UNDEFINED Global Unicast IPv6 address</td></tr>\n") ;
    } else {
    printf("AddressType: $caddr UNDEFINED Global Unicast IPv6 address\n") ;
  }
    return ;
    }
    if ($html) {
  printf("<tr><td>AddressType:</td><td></td><td> $caddr/$mask UNDEFINED Global Unicast IPv6 prefix</td></tr>\n") ;
    } else {
  printf("AddressType: $caddr/$mask UNDEFINED Global Unicast IPv6 prefix\n") ;
}
  return;
}



sub v4whois
{
  local($v4address) = @_ ;
  @servername = `dig +short -x $v4address` ;
  if ($#servername >= 0) {
    chop($servername[0]) ;
    if ($html) {
      print("<tr><td>DNSReverse:</td><td></td><td>  $servername[0]</td></tr>\n");
      }
    else {
      print("DNSReverse:  $servername[0]\n");
      }
    }
  if ($html) {
    print("<tr><td>Whois:</tyd><td></td><td>       $v4address</td></tr>\n") ;
    }
  else {
    print("Whois:       $v4address\n") ;
    }
  @whoisdata = `/usr/bin/whois -h jwhois.apnic.net $v4address` ;
  shift(@whoisdata) ;
  shift(@whoisdata) ;
  shift(@whoisdata) ;
  if ($html) {
    print("<tr><td> </td><td></td><td><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n") ;
    }
  foreach $line (@whoisdata) {
    if ($html) {
      ($kw,$rest) = split(/\:/,$line,2) ;
      print("<tr><td>$kw:</td><td>&nbsp;</td><td>$rest</td></tr>\n") ;
      }
    else {
      print("             $line") ;
      }
    }
  if ($html) {
    print("</table></td></tr>\n") ;
    }
}

sub v6whois
{
  local($v6address) = @_ ;
  @servername = `dig +short -x $v6address` ;
  if ($#servername >= 0) {
    chop($servername[0]) ;
    if ($html) {
      print("<tr><td>DNSReverse:</td><td></td><td>  $servername[0]</td></tr>\n");
      }
    else {
      print("DNSReverse:  $servername[0]\n");
      }
    }
  if ($html) {
    print("<tr><td>Whois:</tyd><td></td><td>       $v6address</td></tr>\n") ;
    }
  else {
    print("Whois:       $v6address\n") ;
    }
  @whoisdata = `/usr/bin/whois -h jwhois.apnic.net $v6address` ;
  shift(@whoisdata) ;
  shift(@whoisdata) ;
  shift(@whoisdata) ;
  if ($html) {
    print("<tr><td> </td><td></td><td><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n") ;
    }
  foreach $line (@whoisdata) {
    if ($html) {
      ($kw,$rest) = split(/\:/,$line,2) ;
      print("<tr><td>$kw:</td><td>&nbsp;</td><td>$rest</td></tr>\n") ;
      }
    else {
      print("             $line") ;
      }
    }
  if ($html) {
    print("</table></td></tr>\n") ;
    }
}

sub delegation_info
{
  local(@dec) = @_ ;

  $prf = (($dec[0] & 65535) << 48) + (($dec[1] & 65535) << 32) + (($dec[2] & 65535) << 16) + ($dec[3] & 65535) ;
  open(D,"$nro_delegated") || return ;
  while ($line = <D>) {
    chop($line) ;
    (@f) = split(/\|/,$line) ;
    next if ($f[2] ne "ipv6") ;
    next if ($f[1] eq "*") ;
    (@pra) = split(":",$f[3]) ;
    while ($#pra < 3) { push(@pra,"0") ; }
    for ($i = 0 ; $i <= 3 ; ++$i) {
      $prh[$i] = hex($pra[$i]) ;
      }
    $dlg_s = (($prh[0] & 65535) << 48) + (($prh[1] & 65535) << 32) + (($prh[2] & 65535) << 16) + ($prh[3] & 65535) ;
    $mask = $f[4] ;
    $shift = 64 - $mask ;
    $length = (1 << $shift) ;
    $dlg_e = $dlg_s + $length - 1 ;
    if (($prf >= $dlg_s) && ($prf <= $dlg_e)) {
      if ($html) {
        print("<tr><td>Delegation:</td><td></td><td>  $line</td></tr>\n") ;
        }
      else {
        print("Delegation:  $line\n") ;
        }
      close(D) ;
      return ;
      }
    }
  close(D) ;
}

sub print_form
{
  local($val) = @_ ;

  print("<FORM ACTION=\"/cgi-bin/ipv6addr\" METHOD=\"GET\">\n") ;
  print("<blockquote>\n") ;
  print("Enter an IPv6 address prefix here to generate a prefix report for the\n") ;
  print("address.</p>\n<p>\n") ;
  print("Enter prefix (e.g. \"2001:db8::1\") <INPUT TYPE=\"text\" NAME=\"pfx\" SIZE=20 Value=\"$val\">\n") ;
  print("</p><p>\n<INPUT TYPE=\"submit\" VALUE=\"IPv6 Address Report\">\n") ;
  print("</p>\n</blockquote>\n</FORM>\n<hr>\n</body>\n</html>\n") ;
}
