]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
New files added in merge from V3-RELEASE-BRANCH to HEAD as of V3-0-3-BETA-1.
authorDavid Hankins <dhankins@isc.org>
Thu, 17 Mar 2005 20:30:41 +0000 (20:30 +0000)
committerDavid Hankins <dhankins@isc.org>
Thu, 17 Mar 2005 20:30:41 +0000 (20:30 +0000)
LICENSE [new file with mode: 0644]
contrib/ms2isc/Registry.pm [new file with mode: 0644]
contrib/ms2isc/ms2isc.pl [new file with mode: 0644]
contrib/ms2isc/readme.txt [new file with mode: 0644]
doc/rfc1542.txt [new file with mode: 0644]
omapip/iscprint.c [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..b737de6
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+# Copyright (c) 2004-2005 by Internet Systems Consortium, Inc. ("ISC")
+# Copyright (c) 1995-2003 by Internet Software Consortium
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+#   Internet Systems Consortium, Inc.
+#   950 Charter Street
+#   Redwood City, CA 94063
+#   <info@isc.org>
+#   http://www.isc.org/
diff --git a/contrib/ms2isc/Registry.pm b/contrib/ms2isc/Registry.pm
new file mode 100644 (file)
index 0000000..69e2413
--- /dev/null
@@ -0,0 +1,361 @@
+# Registry.pm\r
+#   A perl module provided easy Windows Registry access\r
+#\r
+# Author: Shu-Min Chang\r
+#\r
+# Copyright(c) 2002 Intel Corporation.  All rights reserved\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions are met:\r
+#\r
+# 1. Redistributions of source code must retain the above copyright notice,\r
+#    this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright notice\r
+#    this list of conditions and the following disclaimer in the documentation\r
+#    and/or other materials provided with the distribution\r
+# 3. Neither the name of Intel Corporation nor the names of its contributors\r
+#    may be used to endorse or promote products derived from this software\r
+#    without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE INTEL CORPORATION AND CONTRIBUTORS "AS IS"\r
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE INTEL CORPORATION OR CONTRIBUTORS BE\r
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL EXEMPLARY, OR\r
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO PROCUREMENT OF SUBSTITUE\r
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\r
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVICED OF THE POSSIBILITY OF SUCH\r
+# DAMAGE.\r
+\r
+package Registry;\r
+use strict;\r
+use Win32API::Registry 0.21 qw( :ALL );\r
+\r
+\r
+###############################################################################\r
+\r
+#-----------------------------------------\r
+sub GetRegKeyVal($*) {\r
+       my ($FullRegPath, $value) = @_;\r
+#-----------------------------------------\r
+# Purpose: uses Win32API to get registry information from a given server\r
+#\r
+# WARNING: this procedure is VERY Win32 specific, you'll need a Win32 manual\r
+#          to figure out why something is done.\r
+# input: $FullRegPath: a MS specific way of fully qualifying a registry path\r
+#                     \\Server\RootKey\Path\ValueName\r
+# output: *value: the value of the registry key of $FullRegPath\r
+#\r
+\r
+       my ($RemoteMachine, $RootKey, $RegPath, $KeyName, $i);\r
+\r
+#print "in sub:GetRegKeyVal:Parameters:", @_, "\n";\r
+\r
+       # Check the for valid fully qualified registry path\r
+       return -1 if (! ($FullRegPath =~ /\\.+\\.+/)) && (!($FullRegPath =~ /\\\\.+\\.+\\.+/));\r
+\r
+\r
+       $RemoteMachine = (index($FullRegPath, "\\\\") == $[ ? substr($FullRegPath, $[+2, index($FullRegPath, "\\", $[+2)-2):0);\r
+\r
+#print "RemoteMachine = $RemoteMachine\n";\r
+\r
+       $i = $RemoteMachine ? $[+3+length($RemoteMachine) : $[+1;\r
+       $RootKey = substr ($FullRegPath, $i, index($FullRegPath, "\\", $i)-$i);\r
+\r
+       $KeyName = $FullRegPath;\r
+       $KeyName =~ s/.*\\(.+)/$1/;\r
+#print "KeyName = $KeyName\n";\r
+\r
+       $i = index($FullRegPath, $RootKey, $[+length($RemoteMachine)) + $[ + length($RootKey)+1;\r
+       $RegPath = substr ($FullRegPath, $i, length($FullRegPath) - length($KeyName) -$i - 1);\r
+#print "RegPath = $RegPath\n";\r
+\r
+       my ($RootKeyHandle, $handle, $key, $type);\r
+\r
+  if ($RemoteMachine) {\r
+               $RootKeyHandle = regConstant($RootKey);\r
+\r
+               if (!RegConnectRegistry ($RemoteMachine, $RootKeyHandle, $handle)) {\r
+                       $$value = regLastError();\r
+                       return -2;\r
+               }\r
+       } else { # not valid actually because I can't find the mapping table of default \r
+            # local handle mapping.  Should always pass in the Machine name to use for now\r
+               $handle = $RootKey;\r
+       }\r
+\r
+       if (!RegOpenKeyEx ($handle, $RegPath, 0, KEY_READ, $key)) {\r
+               $$value = regLastError();\r
+#print "regLastError = $$value\n";\r
+               return -3;\r
+       }\r
+       if (!RegQueryValueEx( $key, $KeyName, [], $type, $$value, [] )) {\r
+               $$value = regLastError();\r
+#print "regLastError = $$value\n";\r
+               return -4;\r
+       }\r
+\r
+#print "RegType=$type\n";      # Perl doesn't fetch type, at this in this \r
+                               # ActiveState 5.6.0 that I'm using\r
+#print "RegValue=$$value\n";\r
+       RegCloseKey ($key);\r
+       RegCloseKey ($handle);\r
+\r
+       return 0;\r
+}\r
+\r
+###############################################################################\r
+\r
+#-----------------------------------------\r
+sub GetRegSubkeyList($*) {\r
+       my ($FullKeyRegPath, $Subkeys) = @_;\r
+#-----------------------------------------\r
+# Purpose: uses Win32API to get registry subkey list from a given server\r
+#\r
+# WARNING: this procedure is VERY Win32 specific, you'll need a Win32 manual\r
+#          to figure out why something is done.\r
+# input: $FullKeyRegPath: a MS specific way of fully qualifying a registry path\r
+#                     \\Server\RootKey\Path\KeyName\r
+# output: *Subkeys: the list of subkeys in array of the registry key of \r
+#                   $FullKeyRegPath\r
+#\r
+\r
+       my ($RemoteMachine, $RootKey, $RegPath, $KeyName, $i);\r
+\r
+#print "in sub:GetRegSubkeyList:Parameters:", @_, "\n";\r
+\r
+       # Check the for valid registry key path\r
+       return -1 if (! ($FullKeyRegPath =~ /\\.+\\.+/)) && (!($FullKeyRegPath =~ /\\\\.+\\.+\\.+/));\r
+\r
+\r
+       $RemoteMachine = (index($FullKeyRegPath, "\\\\") == $[ ? substr($FullKeyRegPath, $[+2, index($FullKeyRegPath, "\\", $[+2)-2):0);\r
+\r
+#print "RemoteMachine = $RemoteMachine\n";\r
+\r
+       $i = $RemoteMachine ? $[+3+length($RemoteMachine) : $[+1;\r
+       $RootKey = substr ($FullKeyRegPath, $i, index($FullKeyRegPath, "\\", $i)-$i);\r
+\r
+       $i = index($FullKeyRegPath, $RootKey, $[+length($RemoteMachine)) + $[ + length($RootKey)+1;\r
+       $RegPath = substr ($FullKeyRegPath, $i);\r
+\r
+#print "RegPath = $RegPath\n";\r
+\r
+       my ($RootKeyHandle, $handle, $key, $type);\r
+\r
+       if ($RemoteMachine) {\r
+               $RootKeyHandle = regConstant($RootKey);\r
+\r
+               if (!RegConnectRegistry ($RemoteMachine, $RootKeyHandle, $handle)) {\r
+                       @$Subkeys[0]= regLastError();\r
+                       return -2;\r
+               }\r
+       } else { # not valid actually because I can't find the mapping table of default \r
+            # local handle mapping.  Should always pass in the Machine name to use for now\r
+               $handle = $RootKey;\r
+       }\r
+\r
+       if (!RegOpenKeyEx ($handle, $RegPath, 0, KEY_READ, $key)) {\r
+               @$Subkeys[0] = regLastError();\r
+#print "regLastError = @$Subkeys[0]\n";\r
+               return -3;\r
+       }\r
+\r
+       my $tmp;\r
+       # For some reason, the regLastError() stays at ERROR_NO_MORE_ITEMS\r
+       # in occasional call sequence, so I'm resetting the error code\r
+       # before entering the loop\r
+       regLastError(0);\r
+       for ($i=0; regLastError()==regConstant("ERROR_NO_MORE_ITEMS"); $i++) {\r
+#print "\nERROR: error enumumerating reg\n";\r
+               if (RegEnumKeyEx ($key, $i, $tmp, [], [], [], [], [])) {\r
+                       @$Subkeys[$i] = $tmp;\r
+               }\r
+       }\r
+       \r
+#print "RegType=$type\n";\r
+#print "RegValue=@$Subkeys\n";\r
+       RegCloseKey ($key);\r
+       RegCloseKey ($handle);\r
+\r
+       return 0;\r
+}\r
+\r
+#####################################################\r
+\r
+sub ExtractOptionIps ($) {\r
+       my ($MSDHCPOption6Value) = @_;\r
+       my @ip;\r
+# purpose: DHCP registry specific; to return the extracted IP addresses from \r
+#          the input variable\r
+# input:\r
+#   $MSDHCPOption6Value: Option 6 was used to develop, but it works for any\r
+#                        other options of the same datatype.\r
+# output: none\r
+# return: \r
+#   @ip: an arry of IP addresses in human readable format.\r
+\r
+\r
+       # First extract the size of the option\r
+       my ($byte, $size, $ind1, $ind2, @octet) = unpack("VVVV", $MSDHCPOption6Value);\r
+# print "byte = $byte\nsize=$size\nind1=$ind1\nind2=$ind2\n";\r
+\r
+       # Calculate total number of bytes that IP addresses occupy\r
+       my $number = $size * $ind1;\r
+       ($byte, $size, $ind1, $ind2, @octet) = unpack("VVVVC$number", $MSDHCPOption6Value);\r
+\r
+       for (my $i=0; $i<$#octet; $i=$i+4) {\r
+               $ip[$i/4] = "$octet[$i+3]\.$octet[$i+2]\.$octet[$i+1]\.$octet[$i]";\r
+       }\r
+\r
+       return @ip;\r
+}\r
+\r
+#####################################################\r
+\r
+sub ExtractOptionStrings ($) {\r
+       my ($MSDHCPOption15Value) = @_;\r
+       my @string;\r
+# purpose: DHCP registry specific; to return the extracted string from \r
+#          the input variable\r
+# input:\r
+#   $MSDHCPOption15Value: Option 15 was used to develop, but it works for any\r
+#                         other options of the same datatype.\r
+# output: none\r
+# return: \r
+#   @string: an arry of strings in human readable format.\r
+\r
+\r
+       # First extract the size of the option\r
+       my ($byte, $start, $ind1, $ind2, $size, @data) = unpack("VVVVV", $MSDHCPOption15Value);\r
+# print "byte = $byte\nstart=$start\nind1=$ind1\nind2=$ind2\nsize=$size\n";\r
+\r
+       # Calculate total number of bytes that IP addresses occupy\r
+       my $number = $size * $ind1;\r
+       ($byte, $start, $ind1, $ind2, $size, @data) = unpack("VVVVVC$number", $MSDHCPOption15Value);\r
+\r
+       for (my $i=0; $i<$ind1; $i++) {\r
+       # actually this is only programmed to do one string, until I see\r
+       # example of how the multiple strings are represented, I don't have a\r
+       # guess to how to program them properly.\r
+               for (my $j=0; $j<$#data & $data[$j]!=0; $j+=2) {\r
+                       $string[$i] = $string[$i].chr($data[$j]);\r
+               }\r
+       }\r
+\r
+       return @string;\r
+}\r
+\r
+#####################################################\r
+\r
+sub ExtractOptionHex ($) {\r
+       my ($MSDHCPOption46Value) = @_;\r
+       my @Hex;\r
+# purpose: DHCP registry specific; to return the extracted hex from the input\r
+#          variable\r
+# input:\r
+#   $MSDHCPOption46Value: Option 46 was used to develop, but it works for any\r
+#                         other options of the same datatype.\r
+# output: none\r
+# return: \r
+#   @Hex: an arry of hex strings in human readable format.\r
+       my $Temp;\r
+\r
+\r
+       # First extract the size of the option\r
+       my ($byte, $unknown, $ind1, $ind2, @data) = unpack("VVVV", $MSDHCPOption46Value);\r
+# print "byte=$byte\nunknown=$unknown\nind1=$ind1\nind2=$ind2\n";\r
+\r
+       # Calculate total number of bytes that IP addresses occupy\r
+       my $number = $byte - 15;\r
+       ($byte, $unknown, $ind1, $ind2, @data) = unpack("VVVVC$number", $MSDHCPOption46Value);\r
+\r
+# printf "data=%4x\n", $data[0];\r
+\r
+       for (my $i=0; $i<$ind1; $i++) {\r
+       # actually this is only programmed to do one Hex, until I see\r
+       # example of how the multiple Hexes are represented, I don't have a\r
+       # guess to how to program them properly.\r
+               for (my $j=3; $j>=0; $j--) {\r
+                       $Hex[$i] = $Hex[$i].sprintf ("%x", $data[$j+$i*4]);\r
+               }\r
+       }\r
+\r
+       return @Hex;\r
+}\r
+\r
+#####################################################\r
+\r
+sub ExtractExclusionRanges ($) {\r
+       my ($MSDHCPExclusionRanges) = @_;\r
+       my @RangeList;\r
+# purpose: DHCP registry specific; to return the extracted exclusion ranges \r
+#          from the input variable\r
+# input:\r
+#   $MSDHCPExclusionRanges: Exclusion range as DHCP server returns them\r
+# output: none\r
+# return: \r
+#   @RangeList: an arry of paird IP addresses strings in human readable format.\r
+\r
+\r
+       # First extract the size of the option\r
+       my ($paircount, @data) = unpack("V", $MSDHCPExclusionRanges);\r
+# print "paircount = $paircount\n";\r
+\r
+       # Calculate total number of bytes that IP addresses occupy\r
+#      my $number = $paircount * 4*2;\r
+#      ($paircount, @data) = unpack("VC$number", $MSDHCPExclusionRanges);\r
+#\r
+#      for (my $i=0; $i<$#data; $i=$i+4) {\r
+#              $ip[$i/4] = "$data[$i+3]\.$data[$i+2]\.$data[$i+1]\.$data[$i]";\r
+#      }\r
+#\r
+       my $number = $paircount * 2;\r
+       ($paircount, @data) = unpack("VL$number", $MSDHCPExclusionRanges);\r
+\r
+       for (my $i=0; $i<=$#data; $i++) {\r
+               $RangeList[$i] = pack ("L", $data[$i]);\r
+# print "extracted", ExtractIp ($RangeList[$i]), "\n";\r
+       }\r
+\r
+       return @RangeList;\r
+}\r
+#####################################################\r
+\r
+sub ExtractIp ($) {\r
+       my ($octet) = @_;\r
+# purpose: to return the registry saved IP address in a readable form\r
+# input:\r
+#   $octet: a 4 byte data storing the IP address as the registry save it as\r
+# output: none\r
+# return: anonymous variable of a string of IP address\r
+\r
+       my (@data) = unpack ("C4", $octet);\r
+\r
+       return "$data[3]\.$data[2]\.$data[1]\.$data[0]";\r
+\r
+}\r
+#####################################################\r
+\r
+sub ExtractHex ($) {\r
+       my ($HexVal) = @_;\r
+       my @Hex;\r
+# purpose: to return the registry saved hex number in a readable form\r
+# input:\r
+#   $octet: a 4 byte data storing the hex number as the registry save it as\r
+# output: none\r
+# return: \r
+#   $Hex: string of hex digit\r
+\r
+\r
+       # First extract the size of the option\r
+       my (@data) = unpack("C4", $HexVal);\r
+\r
+       for (my $i=3; $i>=0; $i--) {\r
+               $Hex[0] = $Hex[0] . sprintf ("%x", $data[$i]);\r
+       }\r
+\r
+       return @Hex;\r
+}\r
+1;\r
diff --git a/contrib/ms2isc/ms2isc.pl b/contrib/ms2isc/ms2isc.pl
new file mode 100644 (file)
index 0000000..da3e10f
--- /dev/null
@@ -0,0 +1,634 @@
+#set ts=3\r
+#\r
+# ms2isc.pl\r
+# MS NT4 DHCP to ISC DHCP Configuration Migration Tool\r
+#\r
+# Author: Shu-Min Chang\r
+#\r
+# Copyright(c) 2003 Intel Corporation.  All rights reserved\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions are met:\r
+#\r
+# 1. Redistributions of source code must retain the above copyright notice,\r
+#    this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright notice\r
+#    this list of conditions and the following disclaimer in the documentation\r
+#    and/or other materials provided with the distribution\r
+# 3. Neither the name of Intel Corporation nor the names of its contributors\r
+#    may be used to endorse or promote products derived from this software\r
+#    without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE INTEL CORPORATION AND CONTRIBUTORS "AS IS"\r
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE \r
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE INTEL CORPORATION OR CONTRIBUTORS BE\r
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL EXEMPLARY, OR \r
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO PROCUREMENT OF SUBSTITUE\r
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) \r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT \r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\r
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVICED OF THE POSSIBILITY OF SUCH \r
+# DAMAGE.\r
+\r
+use strict;\r
+use Socket;\r
+use Getopt::Std;\r
+use Filehandle;\r
+use Registry; # Custom Perl Module to make Registry access easier.\r
+\r
+my $usage = << 'ENDOFHELP';\r
+\r
+Purpose: A Perl Script converting MS NT4 DHCP configuration to ISC DHCP3 \r
+configuration file by reading NT4's registry.\r
+\r
+Requires: Registry.pm and ActiveState 5.6.0\r
+\r
+Usage: $ARGV -s <Srv> -o <Out> [-p <Pri> [-k <key>]] [-f <Fo>]\r
+\r
+  <Srv>  Server IP or name for NT4 DHCP server to fetch the configuration from.\r
+  <Out>  Output filename for the configuration file.\r
+  <Pri>  Primary DNS server name for sending the dynamic DNS update to.\r
+  <Key>  Key name for use in updating the dynamic DNS zone.\r
+  <Fo>   Failover peer name shared with the DHCP partner.\r
+\r
+Essentially the <Srv> needs to be an NT4 (3.x should work but not tested) which\r
+you should have registry read access to.  You must run this script from a \r
+Windows machine because of the requirement to access the registry.\r
+\r
+The <Pri> is optional parameter for desginating the dynamic DNS update if\r
+missing then the "zone" section of the declaration will be skipped.  The <Key>\r
+is needed if you've configured your DNS zone with a key, in addition, you'll\r
+need to define that key in this DHCP configuration file elsewhere manually,\r
+read the DHCP Handbook to figure out what you need to define.\r
+\r
+The <Fo> specifies the fail-over peer name in the pool section, you'll need to\r
+define additional detail elsewhere manually, again read the DHCP handbook.\r
+\r
+NOTE: the program only knows of the following global and subnet options:\r
+        3, 6, 15, 28, 44, and 46\r
+\r
+      If it runs into options other than the known ones, it will quit.  You\r
+      may fix this by modifying the following procedures:\r
+        GetGlobalOptions\r
+        GetScopes\r
+        PrintSubnetConfig\r
+\r
+      In addition, the resulting subnets configuration will have the "deny \r
+      dynamic bootp clients" you should take them out if that's not what you \r
+      want :).\r
+\r
+      Finally, as the parameter structures implied, it is assumed that you\r
+      want the same zone primary and update key for all zones and that the\r
+      same failover is to be applied to all the pools.  Furthermore the\r
+      subnet zones are all assumed to be class C delineated, but if you\r
+      happend to be delegated at the class B level, this will work fine too.\r
+\r
+Author: Shu-Min Chang <smchang@yahoo.com>\r
+\r
+Copyright: Please read the top of the source code\r
+\r
+Acknowledgement:\r
+  Brian L. King for coding help, Douglas A. Darrah for testing, and James E.\r
+Pressley for being the DHCP reference book :).\r
+\r
+Usage: $ARGV -s <Srv> -o <Out> [-p <Pri> [-k <key>]] [-f <Fo>]\r
+\r
+Version: 1.0.1\r
+\r
+ENDOFHELP\r
+\r
+###################### Begin Main Program ####################################\r
+\r
+       my (%opts, %GlobalOptions, %SuperScopes, %Scopes);\r
+\r
+       ### Get parameters and make sure that they meet the require/optoinal criteria\r
+       getopts('s:o:p:k:f:', \%opts) or die $usage;\r
+       ($opts{s} and $opts{o}) or die $usage;\r
+       if ($opts{k}) { $opts{p} or die $usage; }\r
+       \r
+       ### Read all the registry stuff into the memory\r
+       %GlobalOptions = GetGlobalOptions($opts{s});\r
+       %SuperScopes = GetSuperScope($opts{s});\r
+       %Scopes = GetScopes ($opts{s});\r
+\r
+       ### Process and print out to the output file\r
+       my ($outfile, $i, $j, @Domains);\r
+\r
+       $outfile = new FileHandle "> $opts{o}";\r
+       if (!defined $outfile) {\r
+               die "Can't open file: $opts{o}: $!";\r
+       }\r
+\r
+       for $i (keys %SuperScopes) {\r
+               print $outfile "\n##############################################################\n";\r
+               my ($Scopename) = $i;\r
+               $Scopename =~ s/ //g;\r
+               print $outfile "shared-network $Scopename {\n";\r
+               foreach $j (@{$SuperScopes{$i}}) {\r
+                       PrintSubnetConfig($outfile, \%GlobalOptions, \%{$Scopes{$j}}, $j, "\t", $opts{f});\r
+                       InsertIfUnique (\@Domains, $Scopes{$j}{domain}) if exists $Scopes{$j}{domain};\r
+                       delete $Scopes{$j};\r
+               }\r
+               print $outfile "}\n";\r
+               if ($opts{p} or $opts{k}) {\r
+                       foreach $j (@{$SuperScopes{$i}}) {\r
+                               PrintSubnetUpdate($outfile, $j, $opts{p}, $opts{k});\r
+                       }\r
+               }\r
+       }\r
+\r
+       for $i (keys %Scopes) {\r
+               print $outfile "\n##############################################################\n";\r
+               PrintSubnetConfig($outfile, \%GlobalOptions, \%{$Scopes{$i}}, $i, "", $opts{f});\r
+               if ($opts{p} or $opts{k}) { PrintSubnetUpdate($outfile, $i, $opts{p}, $opts{k}); }\r
+               InsertIfUnique (\@Domains, $Scopes{$i}{domain}) if exists $Scopes{$i}{domain};\r
+       }\r
+\r
+       if ($opts{p} or $opts{k}) {\r
+               InsertIfUnique (\@Domains, $GlobalOptions{domain}) if exists $GlobalOptions{domain};\r
+               for $i (@Domains) {\r
+                       PrintDomainUpdate($outfile, $i, $opts{p}, $opts{k});\r
+               }\r
+       }\r
+\r
+       undef ($outfile);\r
+       print "Done.\n";\r
+       exit();\r
+\r
+################################## End Main Program ###########################\r
+\r
+\r
+\r
+\r
+\r
+######################################################################\r
+sub InsertIfUnique ($$) {\r
+       my ($Array, $data) = @_;\r
+# purpose: insert $data into array @{$Array} iff the data is not in there yet\r
+# input:\r
+#   $data: scalar data to be added to the @{$Array} if unique\r
+#   $Array: reference of the Array to compare the uniqueness of the $data\r
+# output:\r
+#   $Array: reference of the array with the resulting array.\r
+# return: none\r
+\r
+       my ($i);\r
+\r
+       for ($i=0; $i<=$#{$Array} && ${$Array}[$i] ne $data; $i++) { }\r
+\r
+       if ($i > $#{$Array}) {\r
+               ${$Array}[$i] = $data;\r
+       }\r
+}\r
+######################################################################\r
+sub PrintDomainUpdate ($$$$) {\r
+       my ($outfile, $Domain, $DDNSServer, $key) = @_;\r
+# purpose: print out the foward domain zone update declaration\r
+# input:\r
+#   $outfile: filehandle of the file to write the output to\r
+#   $Domain: a string representing the forward domain\r
+#   $DDNSServer: a string of the DNS server accepting the DDNS update\r
+#   $key: a string representing the key used to update the zone\r
+# output: none\r
+# return: none\r
+#\r
+\r
+       print $outfile "zone $Domain {\n";\r
+       print $outfile "\tprimary $DDNSServer;\n";\r
+       !$key or print $outfile "\tkey $key;\n";\r
+       print $outfile "}\n";\r
+\r
+}\r
+######################################################################\r
+sub PrintSubnetUpdate ($$$$) {\r
+       my ($outfile, $Subnet, $DDNSServer, $key) = @_;\r
+# purpose: print out the reverse domain zone update declaration\r
+# input:\r
+#   $outfile: filehandle of the file to write the output to\r
+#   $Subnet: a string representing the subnet in the form 1.2.3.4\r
+#   $DDNSServer: a string of the DNS server accepting the DDNS update\r
+#   $key: a string representing the key used to update the zone\r
+# output: none\r
+# return: none\r
+#\r
+\r
+       my ($Reverse);\r
+\r
+       $_ = join (".", reverse(split(/\./, $Subnet)));\r
+       m/\d*\.(.*)/;\r
+       $Reverse = $1;\r
+       print $outfile "zone $Reverse.in-addr.arpa. {\n";\r
+       print $outfile "\tprimary $DDNSServer;\n";\r
+       !$key or print $outfile "\tkey $key;\n";\r
+       print $outfile "}\n";\r
+\r
+}\r
+######################################################################\r
+sub PrintSubnetConfig ($$$$$$) {\r
+       my ($outfile, $GlobalOptions, $Scope, $Subnet, $prefix, $failover) = @_;\r
+# purpose: print out the effective scope configuration for one subnet as\r
+#          derived from the global and scope options.\r
+# input:\r
+#   $outfile: filehandle of the file to write the output to\r
+#   $GlobalOptions: refernce to the hashed variable from GetGlobalOptions\r
+#   $Scopes: reference to the hashed variable of the subnet in interest\r
+#   $Subnet: string variable of the subnet being processed\r
+#   $prefix: string to be printed before each line (designed for tab)\r
+#   $failover: string to be used for the "failover peer" line\r
+# output: none\r
+# return: none\r
+#\r
+       my ($pound) = ( ${$Scope}{disable}? "#".$prefix : $prefix);\r
+       print $outfile $pound, "subnet $Subnet netmask ${$Scope}{mask} {\n";\r
+       print $outfile "$prefix# Name: ${$Scope}{name}\n";\r
+       print $outfile "$prefix# Comment: ${$Scope}{comment}\n";\r
+       if (exists ${$Scope}{routers}) {\r
+               print $outfile $pound, "\toption routers @{${$Scope}{routers}};\n";\r
+       } elsif (exists ${$GlobalOptions}{routers}) {\r
+               print $outfile $pound, "\toption routers @{${$GlobalOptions}{routers}};\t# NOTE: obtained from global option, bad practice detected\n";\r
+       } else {\r
+               print $outfile "### WARNING: No router was found for this subnet!!! ##########\n";\r
+       }\r
+       \r
+       if (exists ${$Scope}{dnses}) {\r
+               print $outfile $pound, "\toption domain-name-servers ", join(",", @{${$Scope}{dnses}}), ";\n";\r
+       } elsif (exists ${$GlobalOptions}{dnses}) {\r
+               print $outfile $pound, "\toption domain-name-servers ", join(",", @{${$GlobalOptions}{dnses}}), ";\n";\r
+       }\r
+\r
+       if (exists ${$Scope}{domain}) {\r
+               print $outfile $pound, "\toption domain-name \"${$Scope}{domain}\";\n";\r
+       } elsif (exists ${$GlobalOptions}{domain}) {\r
+               print $outfile $pound, "\toption domain-name \"${$GlobalOptions}{domain}\";\n";\r
+       }\r
+\r
+       if (exists ${$Scope}{broadcast}) {\r
+               print $outfile $pound, "\toption broadcast-address ${$Scope}{broadcast};\n";\r
+       } elsif (exists ${$GlobalOptions}{broadcast}) {\r
+               print $outfile $pound, "\toption broadcast-address ${$GlobalOptions}{broadcast};\n";\r
+       }\r
+\r
+       if (exists ${$Scope}{winses}) {\r
+               print $outfile $pound, "\toption netbios-name-servers ", join(",", @{${$Scope}{winses}}), ";\n";\r
+       } elsif (exists ${$GlobalOptions}{winses}) {\r
+               print $outfile $pound, "\toption netbios-name-servers ", join(",", @{${$GlobalOptions}{winses}}), ";\n";\r
+       }\r
+\r
+       if (exists ${$Scope}{winstype}) {\r
+               print $outfile $pound, "\toption netbios-node-type ${$Scope}{winstype};\n";\r
+       } elsif (exists ${$GlobalOptions}{winstype}) {\r
+               print $outfile $pound, "\toption netbios-node-type ${$GlobalOptions}{winstype};\n"\r
+       }\r
+\r
+       print $outfile $pound, "\tdefault-lease-time ${$Scope}{leaseduration};\n";\r
+       print $outfile $pound, "\tpool {\n";\r
+       for (my $r=0; $r<=$#{${$Scope}{ranges}}; $r+=2) {\r
+               print $outfile $pound, "\t\trange ${$Scope}{ranges}[$r] ${$Scope}{ranges}[$r+1];\n";\r
+       }\r
+       !$failover or print $outfile $pound, "\t\tfailover peer \"$failover\";\n";\r
+       print $outfile $pound, "\t\tdeny dynamic bootp clients;\n";\r
+       print $outfile $pound, "\t}\n";\r
+       print $outfile $pound, "}\n";\r
+}\r
+\r
+######################################################################\r
+sub GetScopes ($) {\r
+       my ($Server) = @_;\r
+       my (%Scopes);\r
+# purpose: to return NT4 server's scope configuration\r
+# input:\r
+#   $Server: string of the valid IP or name of the NT4 server\r
+# output: none\r
+# return:\r
+#   %Scope: hash of hash of hash of various data types to be returned of the \r
+#           following data structure\r
+#     $Scope{<subnet>}{disable} => boolean\r
+#     $Scope{<subnet>}{mask} => string (e.g. "1.2.3.255")\r
+#     $Scope{<subnet>}{name} => string (e.g "Office Subnet #1")\r
+#     $Scope{<subnet>}{comment} => string (e.g. "This is a funny subnet")\r
+#     $Scope{<subnet>}{ranges} => array of paired inclusion IP addresses\r
+#                                 (e.g. "1.2.3.1 1.2.3.10 1.2.3.100 10.2.3.200\r
+#                                  says that we have 2 inclusion ranges of\r
+#                                  1-10 and 100-200)\r
+#     $Scopes{<subnet>}{routers} => array of IP address strings\r
+#     $Scopes{<subnet>}{dnses} => array of IP address/name string\r
+#     $Scopes{<subnet>}{domain} > string\r
+#     $Scopes{<subnet>}{broadcast} => string\r
+#     $Scopes{<subnet>}{winses} => array of IP addresses/name string\r
+#     $Scopes{<subnet>}{winstype} => integer\r
+#     $Scopes{<subnet>}{leaseduration} => integer\r
+\r
+       my ($RegVal, @Subnets, @Router, $SubnetName, $SubnetComment, @SubnetOptions, @SRouter, @SDNSServers, @SDomainname, @SWINSservers, @SNetBIOS, @SLeaseDuration, @SSubnetState, @SExclusionRanges, @SSubnetAddress, @SSubnetMask, @SFirstAddress, $SStartAddress, $SEndAddress, @InclusionRanges, @SBroadcastAddress);\r
+\r
+       print "Getting list of subnets\n";\r
+       if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets", \@Subnets)) {\r
+               die "Unable to obtain a list of subnets from the server!\n";\r
+       }\r
+\r
+       for (my $i=0; $i<=$#Subnets; $i++) {\r
+               print "\t Fetching Subnet $Subnets[$i] (",$i+1, "/", $#Subnets+1, "): ";\r
+\r
+               print ".";\r
+               if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges", \@SFirstAddress)) {\r
+                       # Don't know why MS has a tree for this, but as far\r
+                       # as I can tell, only one subtree will ever come out of\r
+                       # this, so I'm skipping the 'for' loop\r
+               \r
+                       print ".";\r
+                       if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges\\$SFirstAddress[0]\\StartAddress", \$RegVal)) {\r
+                               $SStartAddress = $RegVal;\r
+                       }\r
+                       print ".";\r
+                       if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges\\$SFirstAddress[0]\\EndAddress", \$RegVal)) {\r
+                               $SEndAddress = $RegVal;\r
+                       }\r
+# print "\n\tInclusion Range: ", Registry::ExtractIp($SStartAddress), " - ", Registry::ExtractIp($SEndAddress),"\n";\r
+       \r
+               } else {\r
+                       die "\n\n# Error Getting Inclusion Range FirstAddress!!!\n\n";\r
+               }\r
+\r
+               if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\ExcludedIpRanges", \$RegVal)) {\r
+                       @SExclusionRanges = Registry::ExtractExclusionRanges($RegVal);\r
+\r
+#                      for (my $j=2; $j<=$#SExclusionRanges; $j+=2) {\r
+#                              if (unpack("L",$SExclusionRanges[$j]) < unpack("L",$SExclusionRanges[$j-2])) {\r
+#                                      print ("\n******** Subnet exclusion ranges out of order ********\n");\r
+#                              }\r
+#                      }\r
+\r
+                       @SExclusionRanges = sort(@SExclusionRanges);\r
+\r
+#              print "\n\tExclusion Ranges: ";\r
+#              for (my $j=0; $j<=$#SExclusionRanges; $j+=2) {\r
+#                      print "\n\t\t",Registry::ExtractIp($SExclusionRanges[$j])," - ",Registry::ExtractIp($SExclusionRanges[$j+1]);\r
+#              }\r
+\r
+               }\r
+               @InclusionRanges = FindInclusionRanges ($SStartAddress, $SEndAddress, @SExclusionRanges);\r
+\r
+               print ".";\r
+               if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetName", \$RegVal)) {\r
+                       $SubnetName = $RegVal;\r
+#              print "\n\tSubnetName: $SubnetName";\r
+               }\r
+\r
+               print ".";\r
+               if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetComment", \$RegVal)) {\r
+                       $SubnetComment = $RegVal;\r
+#              print "\n\tSubnetComment: $SubnetComment";\r
+               }\r
+               print ".";\r
+               if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetAddress", \$RegVal)) {\r
+                       @SSubnetAddress = Registry::ExtractIp($RegVal);\r
+#              print "\n\tSubnetAddress: $SSubnetAddress[0]";\r
+               }\r
+               print ".";\r
+               if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetMask", \$RegVal)) {\r
+                       @SSubnetMask = Registry::ExtractIp($RegVal);\r
+#              print "\n\tSubnetMask: $SSubnetMask[0]";\r
+               }\r
+\r
+               print ".";\r
+               if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetState", \$RegVal)) {\r
+                       @SSubnetState = Registry::ExtractHex ($RegVal);\r
+#              print "\n\tSubnetState = $SSubnetState[0]";\r
+               }\r
+\r
+               $Scopes{$Subnets[$i]}{disable} = hex($SSubnetState[0]) ? 1 : 0;\r
+               $Scopes{$Subnets[$i]}{mask} = $SSubnetMask[0];\r
+               $Scopes{$Subnets[$i]}{name} = $SubnetName;\r
+               $Scopes{$Subnets[$i]}{comment} = $SubnetComment;\r
+               for (my $r=0; $r<=$#InclusionRanges; $r++) {\r
+                       $Scopes{$Subnets[$i]}{ranges}[$r] = Registry::ExtractIp($InclusionRanges[$r]);\r
+               }\r
+\r
+################## Get scope options\r
+\r
+               my (@SubnetOptionsList);\r
+\r
+               print "\n\t\tOptions:";\r
+               if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetOptions", \@SubnetOptionsList)) {\r
+                       die "Unable to get subnet options list for $Subnets[$i]!\n";\r
+               }\r
+\r
+               for (my $j=0; $j<=$#SubnetOptionsList; $j++) {\r
+                       print ".";\r
+                       if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetOptions\\$SubnetOptionsList[$j]\\OptionValue", \$RegVal)) {\r
+                               for ($SubnetOptionsList[$j]) {\r
+                                       /003/ and do {\r
+#                                              @SRouter = Registry::ExtractOptionIps($RegVal);\r
+                                               $Scopes{$Subnets[$i]}{routers} = [Registry::ExtractOptionIps($RegVal)];\r
+                                               last;\r
+                                       };\r
+                                       /006/ and do {\r
+                                               @SDNSServers = Registry::ExtractOptionIps($RegVal);\r
+                                               for (my $d=0; $d<=$#SDNSServers; $d++) {\r
+                                                       my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $SDNSServers[$d])), &AF_INET);\r
+                                                       $Scopes{$Subnets[$i]}{dnses}[$d] = $ipname ? $ipname : $SDNSServers[$d];\r
+               }\r
+                                               last;\r
+                                       };\r
+                                       /015/ and do { \r
+                                               @SDomainname = Registry::ExtractOptionStrings($RegVal);\r
+                                               $Scopes{$Subnets[$i]}{domain} = $SDomainname[0];\r
+                                               last;\r
+                                       };\r
+                                       /028/ and do {\r
+                                               @SBroadcastAddress = Registry::ExtractOptionIps($RegVal);\r
+                                               $Scopes{$Subnets[$i]}{broadcast} = $SBroadcastAddress[0];\r
+                                               last;\r
+                                       };\r
+                                       /044/ and do {\r
+                                               @SWINSservers = Registry::ExtractOptionIps($RegVal);\r
+                                               for (my $w=0; $w<=$#SWINSservers; $w++) {\r
+                                                       my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $SWINSservers[$w])), &AF_INET);\r
+                                                       $Scopes{$Subnets[$i]}{winses}[$w] = $ipname ? $ipname : $SWINSservers[$w];\r
+                                               }\r
+                                               last;\r
+                                       };\r
+                                       /046/ and do {\r
+                                               @SNetBIOS = Registry::ExtractOptionHex($RegVal);\r
+                                               $Scopes{$Subnets[$i]}{winstype} = hex($SNetBIOS[0]);\r
+                                               last;\r
+                                       };\r
+                                       /051/ and do {\r
+                                               @SLeaseDuration = Registry::ExtractOptionHex($RegVal);\r
+                                               $Scopes{$Subnets[$i]}{leaseduration} = hex($SLeaseDuration[0]);\r
+                                               last;\r
+                                       };\r
+                                       die "This program does not recognize subnet option \#$SubnetOptionsList[$j] yet!\n"\r
+                               }\r
+                       } else {\r
+                                       die "Unable to obtain option SubnetOptionsList[$j] from $Subnets[$i], most likely a registry problem!\n"\r
+                       }\r
+               }\r
+               print "\n";\r
+       }\r
+\r
+       return %Scopes;\r
+}\r
+\r
+######################################################################\r
+sub FindInclusionRanges ($$@) {\r
+       my ($StartAddress, $EndAddress, @ExclusionRanges) = @_;\r
+# Purpose: to calculate and return the DHCP inclusion ranges out of\r
+#          data provided by the NT4 DHCP server\r
+# input:       $StartAddress:\r
+#        $EndAddress:  \r
+#        @ExclusionRanges\r
+# output: none\r
+# return: An arry of IP address pair representing the inclusion ranges\r
+#         in the native registry format.\r
+#\r
+\r
+       my ($SA, $EA, @ER);\r
+       $SA = unpack("L", $StartAddress);\r
+       $EA = unpack("L", $EndAddress);\r
+       @ER = @ExclusionRanges;\r
+       for (my $i=0; $i<=$#ER; $i++) {\r
+               $ER[$i] = unpack ("L", $ER[$i]);\r
+       }\r
+\r
+       my @InclusionRanges;\r
+\r
+\r
+       $InclusionRanges[0] = $SA;\r
+       $InclusionRanges[1] = $EA;\r
+\r
+       for (my $i=0; $i<=$#ER; $i+=2) {\r
+               if ($ER[$i] == $InclusionRanges[$#InclusionRanges-1]) {\r
+                       $InclusionRanges[$#InclusionRanges-1] = $ER[$i+1] + 1;\r
+               }\r
+               if ($ER[$i] > $InclusionRanges[$#InclusionRanges-1]) {\r
+                       $InclusionRanges[$#InclusionRanges] = $ER[$i]-1;\r
+               }\r
+               if (($ER[$i+1] > $InclusionRanges[$#InclusionRanges]) && \r
+                   ($ER[$i+1] != $EA)) {\r
+                       $InclusionRanges[$#InclusionRanges+1] = $ER[$i+1] + 1;\r
+                       $InclusionRanges[$#InclusionRanges+1] = $EA;\r
+               }\r
+               if ($InclusionRanges[$#InclusionRanges] < $InclusionRanges[$#InclusionRanges-1]) {\r
+                       $#InclusionRanges -= 2;\r
+               }\r
+       }\r
+\r
+       for (my $i=0; $i<=$#InclusionRanges; $i++) {\r
+               $InclusionRanges[$i] = pack("L", $InclusionRanges[$i]);\r
+       #       print "Inclusion: ", Registry::ExtractIp($InclusionRanges[$i]), "\n";\r
+       }\r
+       return @InclusionRanges;\r
+}\r
+\r
+####################################################################\r
+sub GetSuperScope ($) {\r
+       my ($Server) = @_;\r
+       my (%SuperScopes);\r
+#\r
+# purpose: gets the Superscope list from the given server\r
+# input:\r
+#   $Server:  string of the valid IP address or name of the NT4 server\r
+# ouput: none\r
+# return:\r
+#   %SuperScopes: hash of array subnets with the following data structure\r
+#          $SuperScopes{<SuperscopeName>} => array of sunbets\r
+#\r
+       my (@SuperScopeNames, @SCSubnetList);\r
+\r
+       print "Getting Superscope list: ";\r
+       if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\SuperScope", \@SuperScopeNames)) {\r
+               for (my $i=0; $i<=$#SuperScopeNames; $i++) {\r
+                       print ".";\r
+                       if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\SuperScope\\$SuperScopeNames[$i]", \@SCSubnetList)) {\r
+                               $SuperScopes{$SuperScopeNames[$i]} = [@SCSubnetList];\r
+                       }\r
+               }\r
+               print "\n";\r
+       }\r
+\r
+       return %SuperScopes;\r
+}\r
+\r
+####################################################################\r
+sub GetGlobalOptions($) {\r
+       my ($Server) = @_;\r
+       my (%GlobalOptions);\r
+# purpose: to return NT4 server's global scope configuration\r
+# input:\r
+#   $Server: string of the valid IP or name of the NT4 server\r
+# output: none\r
+# return:\r
+#   %GlobalOptions: hash of hash of various data types to be returned of the \r
+#           following data structure\r
+#     $GlobalOptions{routers} => array of IP address strings\r
+#     $GlobalOptions{dnses} => array of IP address/name string\r
+#     $GlobalOptions{domain} > string\r
+#     $GlobalOptions{broadcast} => string\r
+#     $GlobalOptions{winses} => array of IP addresses/name string\r
+#     $GlobalOptions{winstype} => integer\r
+\r
+       my ($RegVal, @temp, @GlobalOptionValues);\r
+\r
+       print "Getting Global Options: ";\r
+       if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\GlobalOptionValues", \@GlobalOptionValues)) { \r
+               die "Unable to obtain GlobalOptionValues"; \r
+       }\r
+       \r
+       for (my $i=0; $i<=$#GlobalOptionValues; $i++) {\r
+               print ".";\r
+               if (Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\configuration\\globaloptionvalues\\$GlobalOptionValues[$i]\\optionvalue", \$RegVal)) { \r
+                       die "Unable to retrive global option $GlobalOptionValues[$i]\n";\r
+               }\r
+       \r
+       \r
+               for ($GlobalOptionValues[$i]) {\r
+                       /003/ and do {\r
+                               @temp=Registry::ExtractOptionIps($RegVal);\r
+                               $GlobalOptions{routers} = [@temp];\r
+                               last;\r
+                       };\r
+                       /006/ and do {\r
+                               # DNS Servers\r
+                               @temp = Registry::ExtractOptionIps($RegVal);\r
+                               for (my $d=0; $d<=$#temp; $d++) {\r
+                                       my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $temp[$d])), &AF_INET);\r
+                                       $GlobalOptions{dnses}[$d] = $ipname ? $ipname : $temp[$d];\r
+                               }\r
+                               last;\r
+                       };\r
+                       /015/ and do { \r
+                               # Domain Name\r
+                               @temp = Registry::ExtractOptionStrings($RegVal);\r
+                               $GlobalOptions{domain} = $temp[0];\r
+                               last;\r
+                       };\r
+                       /028/ and do { \r
+                               # broadcast address\r
+                               @temp = Registry::ExtractOptionIps($RegVal);\r
+                               $GlobalOptions{broadcast} = $temp[0];\r
+                               last;\r
+                       };\r
+                       /044/ and do {\r
+                               # WINS Servers\r
+                               @temp = Registry::ExtractOptionIps ($RegVal);\r
+                               $GlobalOptions{winses} = [@temp];\r
+                               for (my $w=0; $w<=$#temp; $w++) {\r
+                                       my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $temp[$w])), &AF_INET);\r
+                                       $GlobalOptions{winses}[$w] = $ipname ? $ipname : $temp[$w];\r
+                               }\r
+                               last;\r
+                       };\r
+                       /046/ and do {\r
+                               # NETBIOS node type\r
+                               @temp = Registry::ExtractOptionHex($RegVal);\r
+                               $GlobalOptions{winstype} = hex($temp[0]);\r
+                               last;\r
+                       };\r
+                       die "This program does not recgonize global option \#$GlobalOptionValues[$i] yet!\n"\r
+               }\r
+       }\r
+       print "\n";\r
+\r
+       return %GlobalOptions;\r
+}\r
diff --git a/contrib/ms2isc/readme.txt b/contrib/ms2isc/readme.txt
new file mode 100644 (file)
index 0000000..862d010
--- /dev/null
@@ -0,0 +1,15 @@
+Copyright: please read the top of the source code.\r
+\r
+Usage:\r
+Objective: please read the help screen by executing the program without any \r
+           parameter.\r
+\r
+Revision:\r
+SMC: Shu-Min Chang\r
+\r
+Who When   What\r
+--- ------ --------------------------------------------------------------------\r
+SMC 021107 Initial release Version 1.0 to ISC DHCP repository\r
+SMC 030129 Fixed inclusion range calculation by sorting exclusion before\r
+           passing to FindInclusionRanges\r
+SMC 030228 release 1.0.1 to ISC DHCP repository\r
diff --git a/doc/rfc1542.txt b/doc/rfc1542.txt
new file mode 100644 (file)
index 0000000..cc03e66
--- /dev/null
@@ -0,0 +1,1291 @@
+
+
+
+
+
+
+Network Working Group                                           W. Wimer
+Request for Comments: 1542                    Carnegie Mellon University
+Updates: 951                                                October 1993
+Obsoletes: 1532
+Category: Standards Track
+
+
+        Clarifications and Extensions for the Bootstrap Protocol
+
+Status of this Memo
+
+   This RFC specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" for the standardization state and status
+   of this protocol.  Distribution of this memo is unlimited.
+
+Abstract
+
+   Some aspects of the BOOTP protocol were rather loosely defined in its
+   original specification.  In particular, only a general description
+   was provided for the behavior of "BOOTP relay agents" (originally
+   called BOOTP forwarding agents").  The client behavior description
+   also suffered in certain ways.  This memo attempts to clarify and
+   strengthen the specification in these areas.  Due to some errors
+   introduced into RFC 1532 in the editorial process, this memo is
+   reissued as RFC 1542.
+
+   In addition, new issues have arisen since the original specification
+   was written.  This memo also attempts to address some of these.
+
+Table of Contents
+
+   1. Introduction.................................................  2
+   1.1 Requirements................................................  3
+   1.2 Terminology.................................................  3
+   1.3 Data Transmission Order.....................................  4
+   2. General Issues...............................................  5
+   2.1 General BOOTP Processing....................................  5
+   2.2 Definition of the 'flags' Field.............................  5
+   2.3 Bit Ordering of Hardware Addresses..........................  7
+   2.4 BOOTP Over IEEE 802.5 Token Ring Networks...................  8
+   3. BOOTP Client Behavior........................................  9
+   3.1 Client use of the 'flags' field.............................  9
+   3.1.1 The BROADCAST flag........................................  9
+   3.1.2 The remainder of the 'flags' field........................  9
+   3.2 Definition of the 'secs' field.............................. 10
+   3.3 Use of the 'ciaddr' and 'yiaddr' fields..................... 10
+
+
+
+Wimer                                                           [Page 1]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+   3.4 Interpretation of the 'giaddr' field........................ 11
+   3.5 Vendor information "magic cookie"........................... 12
+   4. BOOTP Relay Agents........................................... 13
+   4.1 General BOOTP Processing for Relay Agents................... 14
+   4.1.1 BOOTREQUEST Messages...................................... 14
+   4.1.2 BOOTREPLY Messages........................................ 17
+   5. BOOTP Server Behavior........................................ 18
+   5.1 Reception of BOOTREQUEST Messages........................... 18
+   5.2 Use of the 'secs' field..................................... 19
+   5.3 Use of the 'ciaddr' field................................... 19
+   5.4 Strategy for Delivery of BOOTREPLY Messages................. 20
+   Acknowledgements................................................ 21
+   References...................................................... 22
+   Security Considerations......................................... 23
+   Author's Address................................................ 23
+
+1. Introduction
+
+   The Bootstrap Protocol (BOOTP) is a UDP/IP-based protocol which
+   allows a booting host to configure itself dynamically and without
+   user supervision.  BOOTP provides a means to notify a host of its
+   assigned IP address, the IP address of a boot server host, and the
+   name of a file to be loaded into memory and executed [1].  Other
+   configuration information such as the local subnet mask, the local
+   time offset, the addresses of default routers, and the addresses of
+   various Internet servers can also be communicated to a host using
+   BOOTP [2].
+
+   Unfortunately, the original BOOTP specification [1] left some issues
+   of the protocol open to question.  The exact behavior of BOOTP relay
+   agents formerly called "BOOTP forwarding agents") was not clearly
+   specified.  Some parts of the overall protocol specification actually
+   conflict, while other parts have been subject to misinterpretation,
+   indicating that clarification is needed.  This memo addresses these
+   problems.
+
+   Since the introduction of BOOTP, the IEEE 802.5 Token Ring Network
+   has been developed which presents a unique problem for BOOTP's
+   particular message-transfer paradigm.  This memo also suggests a
+   solution for this problem.
+
+   NOTE: Unless otherwise specified in this document or a later
+   document, the information and requirements specified througout this
+   document also apply to extensions to BOOTP such as the Dynamic Host
+   Configuration Protocol (DHCP) [3].
+
+
+
+
+
+
+Wimer                                                           [Page 2]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+1.1 Requirements
+
+   In this memo, the words that are used to define the significance of
+   particular requirements are capitalized.  These words are:
+
+      o "MUST"
+
+        This word or the adjective "REQUIRED" means that the item
+        is an absolute requirement of the specification.
+
+      o "MUST NOT"
+
+        This phrase means that the item is an absolute prohibition
+        of the specification.
+
+      o "SHOULD"
+
+        This word or the adjective "RECOMMENDED" means that there
+        may exist valid reasons in particular circumstances to
+        ignore this item, but the full implications should be
+        understood and the case carefully weighed before choosing a
+        different course.
+
+      o "SHOULD NOT"
+
+        This phrase means that there may exist valid reasons in
+        particular circumstances when the listed behavior is
+        acceptable or even useful, but the full implications should
+        be understood and the case carefully weighed before
+        implementing any behavior described with this label.
+
+      o "MAY"
+
+        This word or the adjective "OPTIONAL" means that this item
+        is truly optional.  One vendor may choose to include the
+        item because a particular marketplace requires it or
+        because it enhances the product, for example; another
+        vendor may omit the same item.
+
+1.2 Terminology
+
+   This memo uses the following terms:
+
+      BOOTREQUEST
+
+         A BOOTREQUEST message is a BOOTP message sent from a BOOTP
+         client to a BOOTP server, requesting configuration information.
+
+
+
+
+Wimer                                                           [Page 3]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+      BOOTREPLY
+
+         A BOOTREPLY message is a BOOTP message sent from a BOOTP server
+         to a BOOTP client, providing configuration information.
+
+      Silently discard
+
+         This memo specifies several cases where a BOOTP entity is to
+         "silently discard" a received BOOTP message.  This means that
+         the entity is to discard the message without further
+         processing, and that the entity will not send any ICMP error
+         message as a result.  However, for diagnosis of problems, the
+         entity SHOULD provide the capability of logging the error,
+         including the contents of the silently-discarded message, and
+         SHOULD record the event in a statistics counter.
+
+1.3 Data Transmission Order
+
+   The order of transmission of the header and data described in this
+   document is resolved to the octet level.  Whenever a diagram shows a
+   group of octets, the order of transmission of those octets is the
+   normal order in which they are read in English.  For example, in the
+   following diagram, the octets are transmitted in the order they are
+   numbered.
+
+                     0                   1
+                     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+                     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                     |       1       |       2       |
+                     +-------------------------------+
+                     |       3       |       4       |
+                     +-------------------------------+
+                     |       5       |       6       |
+                     +-------------------------------+
+
+   Whenever an octet represents a numeric quantity, the leftmost bit in
+   the diagram is the high order or most significant bit.  That is, the
+   bit labeled 0 is the most significant bit.  For example, the
+   following diagram represents the value 170 (decimal).
+
+                               0 1 2 3 4 5 6 7
+                              +-+-+-+-+-+-+-+-+
+                              |1 0 1 0 1 0 1 0|
+                              +---------------+
+
+   Similarly, whenever a multi-octet field represents a numeric quantity
+   the leftmost bit of the whole field is the most significant bit.
+   When a multi-octet quantity is transmitted the most significant octet
+
+
+
+Wimer                                                           [Page 4]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+   is transmitted first.
+
+2. General Issues
+
+   This section covers issues of general relevance to all BOOTP entities
+   (clients, servers, and relay agents).
+
+2.1 General BOOTP Processing
+
+   The following consistency checks SHOULD be performed on BOOTP
+   messages:
+
+      o The IP Total Length and UDP Length must be large enough to
+        contain the minimal BOOTP header of 300 octets (in the UDP
+        data field) specified in [1].
+
+   NOTE: Future extensions to the BOOTP protocol may increase the size
+   of BOOTP messages.  Therefore, BOOTP messages which, according to the
+   IP Total Length and UDP Length fields, are larger than the minimum
+   size specified by [1] MUST also be accepted.
+
+      o The 'op' (opcode) field of the message must contain either the
+        code for a BOOTREQUEST (1) or the code for a BOOTREPLY (2).
+
+   BOOTP messages not meeting these consistency checks MUST be silently
+   discarded.
+
+2.2 Definition of the 'flags' Field
+
+   The standard BOOTP message format defined in [1] includes a two-octet
+   field located between the 'secs' field and the 'ciaddr' field.  This
+   field is merely designated as "unused" and its contents left
+   unspecified, although Section 7.1 of [1] does offer the following
+   suggestion:
+
+      "Before setting up the packet for the first time, it is a good
+      idea to clear the entire packet buffer to all zeros; this will
+      place all fields in their default state."
+
+      This memo hereby designates this two-octet field as the 'flags'
+      field.
+
+      This memo hereby defines the most significant bit of the 'flags'
+      field as the BROADCAST (B) flag.  The semantics of this flag are
+      discussed in Sections 3.1.1 and 4.1.2 of this memo.
+
+      The remaining bits of the 'flags' field are reserved for future
+      use.  They MUST be set to zero by clients and ignored by servers
+
+
+
+Wimer                                                           [Page 5]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+      and relay agents.
+
+      The 'flags' field, then, appears as follows:
+
+                     0                   1
+                     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+                     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                     |B|             MBZ             |
+                     +-+-----------------------------+
+
+   where:
+
+      B    BROADCAST flag (discussed in Sections 3.1.1 and 4.1.2)
+
+      MBZ  MUST BE ZERO (reserved for future use)
+
+   The format of a BOOTP message is shown below.  The numbers in
+   parentheses indicate the size of each field in octets.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Wimer                                                           [Page 6]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+   0                   1                   2                   3
+   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     op (1)    |   htype (1)   |   hlen (1)    |   hops (1)    |
+   +---------------+---------------+---------------+---------------+
+   |                            xid (4)                            |
+   +-------------------------------+-------------------------------+
+   |           secs (2)            |           flags (2)           |
+   +-------------------------------+-------------------------------+
+   |                           ciaddr (4)                          |
+   +---------------------------------------------------------------+
+   |                           yiaddr (4)                          |
+   +---------------------------------------------------------------+
+   |                           siaddr (4)                          |
+   +---------------------------------------------------------------+
+   |                           giaddr (4)                          |
+   +---------------------------------------------------------------+
+   |                                                               |
+   |                           chaddr (16)                         |
+   |                                                               |
+   |                                                               |
+   +---------------------------------------------------------------+
+   |                                                               |
+   |                           sname  (64)                         |
+   +---------------------------------------------------------------+
+   |                                                               |
+   |                           file   (128)                        |
+   +---------------------------------------------------------------+
+   |                                                               |
+   |                           vend   (64)                         |
+   +---------------------------------------------------------------+
+
+2.3 Bit Ordering of Hardware Addresses
+
+   The bit ordering used for link-level hardware addresses in the
+   'chaddr' field SHOULD be the same as the ordering used for the ARP
+   protocol [4] on the client's link-level network (assuming ARP is
+   defined for that network).
+
+   The 'chaddr' field MUST be preserved as it was specified by the BOOTP
+   client.  A relay agent MUST NOT reverse the bit ordering of the
+   'chaddr' field even if it happens to be relaying a BOOTREQUEST
+   between two networks which use different bit orderings.
+
+      DISCUSSION:
+
+         One of the primary reasons the 'chaddr' field exists is to
+         enable BOOTP servers and relay agents to communicate directly
+
+
+
+Wimer                                                           [Page 7]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+         with clients without the use of broadcasts.  In practice, the
+         contents of the 'chaddr' field is often used to create an ARP-
+         cache entry in exactly the same way the normal ARP protocol
+         would have.  Clearly, interoperability can only be achieved if
+         a consistent interpretation of the 'chaddr' field is used.
+
+         As a practical example, this means that the bit ordering used
+         for the 'chaddr' field by a BOOTP client on an IEEE 802.5 Token
+         Ring network is the opposite of the bit ordering used by a
+         BOOTP client on a DIX ethernet network.
+
+2.4 BOOTP Over IEEE 802.5 Token Ring Networks
+
+   Special consideration of the client/server and client/relay agent
+   interactions must be given to IEEE 802.5 networks because of non-
+   transparent bridging.
+
+   The client SHOULD send its broadcast BOOTREQUEST with an All Routes
+   Explorer RIF.  This will enable servers/relay agents to cache the
+   return route if they choose to do so.  For those server/relay agents
+   which cannot cache the return route (because they are stateless, for
+   example), the BOOTREPLY message SHOULD be sent to the client's
+   hardware address, as taken from the BOOTP message, with a Spanning
+   Tree Rooted RIF.  The actual bridge route will be recorded by the
+   client and server/relay agent by normal ARP processing code.
+
+      DISCUSSION:
+
+         In the simplest case, an unbridged, single ring network, the
+         broadcast behavior of the BOOTP protocol is identical to that
+         of Ethernet networks.  However, a BOOTP client cannot know, a
+         priori, that an 802.5 network is not bridged.  In fact, the
+         likelihood is that the server, or relay agent, will not know
+         either.
+
+         Of the four possible scenerios, only two are interesting: where
+         the assumption is that the 802.5 network is not bridged and it
+         is, and the assumption that the network is bridged and it is
+         not.  In the former case, the Routing Information Field (RIF)
+         will not be used; therefore, if the server/relay agent are on
+         another segment of the ring, the client cannot reach it.  In
+         the latter case, the RIF field will be used, resulting in a few
+         extraneous bytes on the ring.  It is obvious that an almost
+         immeasurable inefficiency is to be preferred over a complete
+         failure to communicate.
+
+
+
+
+
+
+Wimer                                                           [Page 8]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+         Given that the assumption is that RIF fields will be needed, it
+         is necesary to determine the optimum method for the client to
+         reach the server/relay agent, and the optimum method for the
+         response to be returned.
+
+3. BOOTP Client Behavior
+
+   This section clarifies various issues regarding BOOTP client
+   behavior.
+
+3.1 Client use of the 'flags' field
+
+3.1.1 The BROADCAST flag
+
+   Normally, BOOTP servers and relay agents attempt to deliver BOOTREPLY
+   messages directly to a client using unicast delivery.  The IP
+   destination address (in the IP header) is set to the BOOTP 'yiaddr'
+   address and the link-layer destination address is set to the BOOTP
+   'chaddr' address.  Unfortunately, some client implementations are
+   unable to receive such unicast IP datagrams until they know their own
+   IP address (thus we have a "chicken and egg" issue).  Often, however,
+   they can receive broadcast IP datagrams (those with a valid IP
+   broadcast address as the IP destination and the link-layer broadcast
+   address as the link-layer destination).
+
+   If a client falls into this category, it SHOULD set (to 1) the
+   newly-defined BROADCAST flag in the 'flags' field of BOOTREPLY
+   messages it generates.  This will provide a hint to BOOTP servers and
+   relay agents that they should attempt to broadcast their BOOTREPLY
+   messages to the client.
+
+   If a client does not have this limitation (i.e., it is perfectly able
+   to receive unicast BOOTREPLY messages), it SHOULD NOT set the
+   BROADCAST flag (i.e., it SHOULD clear the BROADCAST flag to 0).
+
+      DISCUSSION:
+
+         This addition to the protocol is a workaround for old host
+         implementations.  Such implementations SHOULD be modified so
+         that they may receive unicast BOOTREPLY messages, thus making
+         use of this workaround unnecessary.  In general, the use of
+         this mechanism is discouraged.
+
+3.1.2 The remainder of the 'flags' field
+
+   The remaining bits of the 'flags' field are reserved for future use.
+   A client MUST set these bits to zero in all BOOTREQUEST messages it
+   generates.  A client MUST ignore these bits in all BOOTREPLY messages
+
+
+
+Wimer                                                           [Page 9]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+   it receives.
+
+3.2 Definition of the 'secs' field
+
+   The 'secs' field of a BOOTREQUEST message SHOULD represent the
+   elapsed time, in seconds, since the client sent its first BOOTREQUEST
+   message.  Note that this implies that the 'secs' field of the first
+   BOOTREQUEST message SHOULD be set to zero.
+
+   Clients SHOULD NOT set the 'secs' field to a value which is constant
+   for all BOOTREQUEST messages.
+
+      DISCUSSION:
+
+         The original definition of the 'secs' field was vague.  It was
+         not clear whether it represented the time since the first
+         BOOTREQUEST message was sent or some other time period such as
+         the time since the client machine was powered-up.  This has
+         limited its usefulness as a policy control mechanism for BOOTP
+         servers and relay agents.  Furthermore, certain client
+         implementations have been known to simply set this field to a
+         constant value or use incorrect byte-ordering.  Incorrect
+         byte-ordering usually makes it appear as if a client has been
+         waiting much longer than it really has, so a relay agent will
+         relay the BOOTREQUEST sooner than desired (usually
+         immediately).  These implementation errors have further
+         undermined the usefulness of the 'secs' field.  These incorrect
+         implementations SHOULD be corrected.
+
+3.3 Use of the 'ciaddr' and 'yiaddr' fields
+
+   If a BOOTP client does not know what IP address it should be using,
+   the client SHOULD set the 'ciaddr' field to 0.0.0.0.  If the client
+   has the ability to remember the last IP address it was assigned, or
+   it has been preconfigured with an IP address via some alternate
+   mechanism, the client MAY fill the 'ciaddr' field with that IP
+   address.  If the client does place a non-zero IP address in the
+   'ciaddr' field, the client MUST be prepared to accept incoming
+   unicast datagrams addressed to that IP address and also answer ARP
+   requests for that IP address (if ARP is used on that network).
+
+   The BOOTP server is free to assign a different IP address (in the
+   'yiaddr' field) than the client expressed in 'ciaddr'.  The client
+   SHOULD adopt the IP address specified in 'yiaddr' and begin using it
+   as soon as possible.
+
+
+
+
+
+
+Wimer                                                          [Page 10]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+      DISCUSSION:
+
+         There are various interpretations about the purpose of the
+         'ciaddr' field and, unfortunately, no agreement on a single
+         correct interpretation.  One interpretation is that if a client
+         is willing to accept whatever IP address the BOOTP server
+         assigns to it, the client should always place 0.0.0.0 in the
+         'ciaddr' field, regardless of whether it knows its previously-
+         assigned address.  Conversely, if the client wishes to assert
+         that it must have a particular IP address (e.g., the IP address
+         was hand-configured by the host administrator and BOOTP is only
+         being used to obtain a boot file and/or information from the
+         'vend' field), the client will then fill the 'ciaddr' field
+         with the desired IP address and ignore the IP address assigned
+         by the BOOTP server as indicated in the 'yiaddr' field.  An
+         alternate interpretation holds that the client always fills the
+         'ciaddr' field with its most recently-assigned IP address (if
+         known) even if that address may be incorrect.  Such a client
+         will still accept and use the address assigned by the BOOTP
+         server as indicated in the 'yiaddr' field.  The motivation for
+         this interpretation is to aid the server in identifying the
+         client and/or in delivering the BOOTREPLY to the client.  Yet a
+         third (mis)interpretation allows the client to use 'ciaddr' to
+         express the client's desired IP address, even if the client has
+         never used that address before or is not currently using that
+         address.
+
+         The last interpretation is incorrect as it may prevent the
+         BOOTREPLY from reaching the client.  The server will usually
+         unicast the reply to the address given in 'ciaddr' but the
+         client may not be listening on that address yet, or the client
+         may be connected to an incorrect subnet such that normal IP
+         routing (correctly) routes the reply to a different subnet.
+
+         The second interpretation also suffers from the "incorrect
+         subnet" problem.
+
+         The first interpretation seems to be the safest and most likely
+         to promote interoperability.
+
+3.4 Interpretation of the 'giaddr' field
+
+   The 'giaddr' field is rather poorly named.  It exists to facilitate
+   the transfer of BOOTREQUEST messages from a client, through BOOTP
+   relay agents, to servers on different networks than the client.
+   Similarly, it facilitates the delivery of BOOTREPLY messages from the
+   servers, through BOOTP relay agents, back to the client.  In no case
+   does it represent a general IP router to be used by the client.  A
+
+
+
+Wimer                                                          [Page 11]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+   BOOTP client MUST set the 'giaddr' field to zero (0.0.0.0) in all
+   BOOTREQUEST messages it generates.
+
+   A BOOTP client MUST NOT interpret the 'giaddr' field of a BOOTREPLY
+   message to be the IP address of an IP router.  A BOOTP client SHOULD
+   completely ignore the contents of the 'giaddr' field in BOOTREPLY
+   messages.
+
+      DISCUSSION:
+
+         The semantics of the 'giaddr' field were poorly defined.
+         Section 7.5 of [1] states:
+
+           "If 'giaddr' (gateway address) is nonzero, then the packets
+           should be forwarded there first, in order to get to the
+           server."
+
+   In that sentence, "get to" refers to communication from the client to
+   the server subsequent to the BOOTP exchange, such as a TFTP session.
+   Unfortunately, the 'giaddr' field may contain the address of a BOOTP
+   relay agent that is not itself an IP router (according to [1],
+   Section 8, fifth paragraph), in which case, it will be useless as a
+   first-hop for TFTP packets sent to the server (since, by definition,
+   non-routers don't forward datagrams at the IP layer).
+
+   Although now prohibited by Section 4.1.1 of this memo, the 'giaddr'
+   field might contain a broadcast address according to Section 8, sixth
+   paragraph of [1].  Not only would such an address be useless as a
+   router address, it might also cause the client to ARP for the
+   broadcast address (since, if the client didn't receive a subnet mask
+   in the BOOTREPLY message, it would be unable to recognize a subnet
+   broadcast address).  This is clearly undesirable.
+
+   To reach a non-local server, clients can obtain a first-hop router
+   address from the "Gateway" subfield of the "Vendor Information
+   Extensions" [2] (if present), or via the ICMP router discovery
+   protocol [5] or other similar mechanism.
+
+3.5 Vendor information "magic cookie"
+
+   It is RECOMMENDED that a BOOTP client always fill the first four
+   octets of the 'vend' (vendor information) field of a BOOTREQUEST with
+   a four-octet identifier called a "magic cookie."  A BOOTP client
+   SHOULD do this even if it has no special information to communicate
+   to the BOOTP server using the 'vend' field.  This aids the BOOTP
+   server in determining what vendor information format it should use in
+   its BOOTREPLY messages.
+
+
+
+
+Wimer                                                          [Page 12]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+   If a special vendor-specific magic cookie is not being used, a BOOTP
+   client SHOULD use the dotted decimal value 99.130.83.99 as specified
+   in [2].  In this case, if the client has no information to
+   communicate to the server, the octet immediately following the magic
+   cookie SHOULD be set to the "End" tag (255) and the remaining octets
+   of the 'vend' field SHOULD be set to zero.
+
+      DISCUSSION:
+
+         Sometimes different operating systems or networking packages
+         are run on the same machine at different times (or even at the
+         same time!).  Since the hardware address placed in the 'chaddr'
+         field will likely be the same, BOOTREQUESTs from completely
+         different BOOTP clients on the same machine will likely be
+         difficult for a BOOTP server to differentiate.  If the client
+         includes a magic cookie in its BOOTREQUESTs, the server will at
+         least know what format the client expects and can understand in
+         corresponding BOOTREPLY messages.
+
+4. BOOTP Relay Agents
+
+         In many cases, BOOTP clients and their associated BOOTP
+         server(s) do not reside on the same IP network or subnet.  In
+         such cases, some kind of third-party agent is required to
+         transfer BOOTP messages between clients and servers.  Such an
+         agent was originally referred to as a "BOOTP forwarding agent."
+         However, in order to avoid confusion with the IP forwarding
+         function of an IP router, the name "BOOTP relay agent" is
+         hereby adopted instead.
+
+      DISCUSSION:
+
+         A BOOTP relay agent performs a task which is distinct from an
+         IP router's normal IP forwarding function.  While a router
+         normally switches IP datagrams between networks more-or-less
+         transparently, a BOOTP relay agent may more properly be thought
+         to receive BOOTP messages as a final destination and then
+         generate new BOOTP messages as a result.  It is incorrect for a
+         relay agent implementation to simply forward a BOOTP message
+         "straight through like a regular packet."
+
+         This relay-agent functionality is most conveniently located in
+         the routers which interconnect the clients and servers, but may
+         alternatively be located in a host which is directly connected
+         to the client subnet.
+
+         Any Internet host or router which provides BOOTP relay-agent
+         capability MUST conform to the specifications in this memo.
+
+
+
+Wimer                                                          [Page 13]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+4.1 General BOOTP Processing for Relay Agents
+
+   All locally delivered UDP messages whose UDP destination port number
+   is BOOTPS (67) are considered for special processing by the host or
+   router's logical BOOTP relay agent.
+
+   In the case of a host, locally delivered datagrams are simply all
+   datagrams normally received by that host, i.e., broadcast and
+   multicast datagrams as well as unicast datagrams addressed to IP
+   addresses of that host.
+
+   In the case of a router, locally delivered datagrams are broadcast
+   and multicast datagrams as well as unicast datagrams addressed to IP
+   addresses of that router.  These are datagrams for which the router
+   should be considered an end destination as opposed to an intermediate
+   switching node.  Thus a unicast datagram with an IP destination not
+   matching any of the router's IP addresses is not considered for
+   processing by the router's logical BOOTP relay agent.
+
+   Hosts and routers are usually required to silently discard incoming
+   datagrams containing illegal IP source addresses.  This is generally
+   known as "Martian address filtering."  One of these illegal addresses
+   is 0.0.0.0 (or actually anything on network 0).  However, hosts or
+   routers which support a BOOTP relay agent MUST accept for local
+   delivery to the relay agent BOOTREQUEST messages whose IP source
+   address is 0.0.0.0.  BOOTREQUEST messages from legal IP source
+   addresses MUST also be accepted.
+
+   A relay agent MUST silently discard any received UDP messages whose
+   UDP destination port number is BOOTPC (68).
+
+      DISCUSSION:
+
+         There should be no need for a relay agent to process messages
+         addressed to the BOOTPC port.  Careful reading of the original
+         BOOTP specification [1] will show this.  Nevertheless, some
+         relay agent implementations incorrectly relay such messages.
+
+   The consistency checks specified in Section 2.1 SHOULD be performed
+   by the relay agent.  BOOTP messages not meeting these consistency
+   checks MUST be silently discarded.
+
+4.1.1 BOOTREQUEST Messages
+
+   Some configuration mechanism MUST exist to enable or disable the
+   relaying of BOOTREQUEST messages.  Relaying MUST be disabled by
+   default.
+
+
+
+
+Wimer                                                          [Page 14]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+   When the BOOTP relay agent receives a BOOTREQUEST message, it MAY use
+   the value of the 'secs' (seconds since client began booting) field of
+   the request as a factor in deciding whether to relay the request.  If
+   such a policy mechanism is implemented, its threshold SHOULD be
+   configurable.
+
+      DISCUSSION:
+
+         To date, this feature of the BOOTP protocol has not necessarily
+         been shown to be useful.  See Section 3.2 for a discussion.
+
+   The relay agent MUST silently discard BOOTREQUEST messages whose
+   'hops' field exceeds the value 16.  A configuration option SHOULD be
+   provided to set this threshold to a smaller value if desired by the
+   network manager.  The default setting for a configurable threshold
+   SHOULD be 4.
+
+   If the relay agent does decide to relay the request, it MUST examine
+   the 'giaddr' ("gateway" IP address) field.  If this field is zero,
+   the relay agent MUST fill this field with the IP address of the
+   interface on which the request was received.  If the interface has
+   more than one IP address logically associated with it, the relay
+   agent SHOULD choose one IP address associated with that interface and
+   use it consistently for all BOOTP messages it relays.  If the
+   'giaddr' field contains some non-zero value, the 'giaddr' field MUST
+   NOT be modified.  The relay agent MUST NOT, under any circumstances,
+   fill the 'giaddr' field with a broadcast address as is suggested in
+   [1] (Section 8, sixth paragraph).
+
+   The value of the 'hops' field MUST be incremented.
+
+   All other BOOTP fields MUST be preserved intact.
+
+   At this point, the request is relayed to its new destination (or
+   destinations).  This destination MUST be configurable.  Further, this
+   destination configuration SHOULD be independent of the destination
+   configuration for any other so-called "broadcast forwarders" (e.g.,
+   for the UDP-based TFTP, DNS, Time, etc.  protocols).
+
+      DISCUSSION:
+
+         The network manager may wish the relaying destination to be an
+         IP unicast, multicast, broadcast, or some combination.  A
+         configurable list of destination IP addresses provides good
+         flexibility.  More flexible configuration schemes are
+         encouraged.  For example, it may be desirable to send to the
+         limited broadcast address (255.255.255.255) on specific
+         physical interfaces.  However, if the BOOTREQUEST message was
+
+
+
+Wimer                                                          [Page 15]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+         received as a broadcast, the relay agent MUST NOT rebroadcast
+         the BOOTREQUEST on the physical interface from whence it came.
+
+         A relay agent MUST use the same destination (or set of
+         destinations) for all BOOTREQUEST messages it relays from a
+         given client.
+
+      DISCUSSION:
+
+         At least one known relay agent implementation uses a round-
+         robin scheme to provide load balancing across multiple BOOTP
+         servers.  Each time it receives a new BOOTREQUEST message, it
+         relays the message to the next BOOTP server in a list of
+         servers.  Thus, with this relay agent, multiple consecutive
+         BOOTREQUEST messages from a given client will be delivered to
+         different servers.
+
+         Unfortunately, this well-intentioned scheme reacts badly with
+         DHCP [3] and perhaps other variations of the BOOTP protocol
+         which depend on multiple exchanges of BOOTREQUEST and BOOTREPLY
+         messages between clients and servers.  Therefore, all
+         BOOTREQUEST messages from a given client MUST be relayed to the
+         same destination (or set of destinations).
+
+         One way to meet this requirement while providing some load-
+         balancing benefit is to hash the client's link-layer address
+         (or some other reliable client-identifying information) and use
+         the resulting hash value to select the appropriate relay
+         destination (or set of destinations).  The simplest solution,
+         of course, is to not use a load-balancing scheme and just relay
+         ALL received BOOTREQUEST messages to the same destination (or
+         set of destinations).
+
+         When transmitting the request to its next destination, the
+         relay agent may set the IP Time-To-Live field to either the
+         default value for new datagrams originated by the relay agent,
+         or to the TTL of the original BOOTREQUEST decremented by (at
+         least) one.
+
+      DISCUSSION:
+
+         As an extra precaution against BOOTREQUEST loops, it is
+         preferable to use the decremented TTL from the original
+         BOOTREQUEST.  Unfortunately, this may be difficult to do in
+         some implementations.
+
+         If the BOOTREQUEST has a UDP checksum (i.e., the UDP checksum
+         is non-zero), the checksum must be recalculated before
+
+
+
+Wimer                                                          [Page 16]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+         transmitting the request.
+
+4.1.2 BOOTREPLY Messages
+
+   BOOTP relay agents relay BOOTREPLY messages only to BOOTP clients.
+   It is the responsibility of BOOTP servers to send BOOTREPLY messages
+   directly to the relay agent identified in the 'giaddr' field.
+   Therefore, a relay agent may assume that all BOOTREPLY messages it
+   receives are intended for BOOTP clients on its directly-connected
+   networks.
+
+   When a relay agent receives a BOOTREPLY message, it should examine
+   the BOOTP 'giaddr', 'yiaddr', 'chaddr', 'htype', and 'hlen' fields.
+   These fields should provide adequate information for the relay agent
+   to deliver the BOOTREPLY message to the client.
+
+   The 'giaddr' field can be used to identify the logical interface from
+   which the reply must be sent (i.e., the host or router interface
+   connected to the same network as the BOOTP client).  If the content
+   of the 'giaddr' field does not match one of the relay agent's
+   directly-connected logical interfaces, the BOOTREPLY messsage MUST be
+   silently discarded.
+
+   The 'htype', 'hlen', and 'chaddr' fields supply the link-layer
+   hardware type, hardware address length, and hardware address of the
+   client as defined in the ARP protocol [4] and the Assigned Numbers
+   document [6].  The 'yiaddr' field is the IP address of the client, as
+   assigned by the BOOTP server.
+
+   The relay agent SHOULD examine the newly-defined BROADCAST flag (see
+   Sections 2.2 and 3.1.1 for more information).  If this flag is set to
+   1, the reply SHOULD be sent as an IP broadcast using the IP limited
+   broadcast address 255.255.255.255 as the IP destination address and
+   the link-layer broadcast address as the link-layer destination
+   address.  If the BROADCAST flag is cleared (0), the reply SHOULD be
+   sent as an IP unicast to the IP address specified by the 'yiaddr'
+   field and the link-layer address specified in the 'chaddr' field.  If
+   unicasting is not possible, the reply MAY be sent as a broadcast, in
+   which case it SHOULD be sent to the link-layer broadcast address
+   using the IP limited broadcast address 255.255.255.255 as the IP
+   destination address.
+
+      DISCUSSION:
+
+         The addition of the BROADCAST flag to the protocol is a
+         workaround to help promote interoperability with certain client
+         implementations.
+
+
+
+
+Wimer                                                          [Page 17]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+         Note that since the 'flags' field was previously defined in [1]
+         simply as an "unused" field, it is possible that old client or
+         server implementations may accidentally and unknowingly set the
+         new BROADCAST flag.  It is actually expected that such
+         implementations will be rare (most implementations seem to
+         zero-out this field), but interactions with such
+         implementations must nevertheless be considered.  If an old
+         client or server does set the BROADCAST flag to 1 incorrectly,
+         conforming relay agents will generate broadcast BOOTREPLY
+         messages to the corresponding client.  The BOOTREPLY messages
+         should still properly reach the client, at the cost of one
+         (otherwise unnecessary) additional broadcast.  This, however,
+         is no worse than a server or relay agent which always
+         broadcasts its BOOTREPLY messages.
+
+         Older client or server implementations which accidentally set
+         the BROADCAST flag SHOULD be corrected to properly comply with
+         this newer specification.
+
+         All BOOTP fields MUST be preserved intact.  The relay agent
+         MUST NOT modify any BOOTP field of the BOOTREPLY message when
+         relaying it to the client.
+
+         The reply MUST have its UDP destination port set to BOOTPC
+         (68).
+
+         If the BOOTREPLY has a UDP checksum (i.e., the UDP checksum is
+         non-zero), the checksum must be recalculated before
+         transmitting the reply.
+
+5. BOOTP Server Behavior
+
+   This section provides clarifications on the behavior of BOOTP
+   servers.
+
+5.1 Reception of BOOTREQUEST Messages
+
+   All received UDP messages whose UDP destination port number is BOOTPS
+   (67) are considered for processing by the BOOTP server.
+
+   Hosts and routers are usually required to silently discard incoming
+   datagrams containing illegal IP source addresses.  This is generally
+   known as "Martian address filtering."  One of these illegal addresses
+   is 0.0.0.0 (or actually anything on network 0).  However, hosts or
+   routers which support a BOOTP server MUST accept for local delivery
+   to the server BOOTREQUEST messages whose IP source address is
+   0.0.0.0.  BOOTREQUEST messages from legal IP source addresses MUST
+   also be accepted.
+
+
+
+Wimer                                                          [Page 18]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+   A BOOTP server MUST silently discard any received UDP messages whose
+   UDP destination port number is BOOTPC (68).
+
+      DISCUSSION:
+
+         There should be no need for a BOOTP server to process messages
+         addressed to the BOOTPC port.  Careful reading of the original
+         BOOTP specification [1] will show this.
+
+         The consistency checks specified in Section 2.1 SHOULD be
+         performed by the BOOTP server.  BOOTP messages not meeting
+         these consistency checks MUST be silently discarded.
+
+5.2 Use of the 'secs' field
+
+   When the BOOTP server receives a BOOTREQUEST message, it MAY use the
+   value of the 'secs' (seconds since client began booting) field of the
+   request as a factor in deciding whether and/or how to reply to the
+   request.
+
+      DISCUSSION:
+
+         To date, this feature of the BOOTP protocol has not necessarily
+         been shown to be useful.  See Section 3.2 for a discussion.
+
+5.3 Use of the 'ciaddr' field
+
+   There have been various client interpretations of the 'ciaddr' field
+   for which Section 3.3 should be consulted.  A BOOTP server SHOULD be
+   prepared to deal with these varying interpretations.  In general, the
+   'ciaddr' field SHOULD NOT be trusted as a sole key in identifying a
+   client; the contents of the 'ciaddr', 'chaddr', 'htype', and 'hlen'
+   fields, and probably other information (perhaps in the 'file' and
+   'vend' fields) SHOULD all be considered together in deciding how to
+   respond to a given client.
+
+   BOOTP servers SHOULD preserve the contents of the 'ciaddr' field in
+   BOOTREPLY messages; the contents of 'ciaddr' in a BOOTREPLY message
+   SHOULD exactly match the contents of 'ciaddr' in the corresponding
+   BOOTREQUEST message.
+
+      DISCUSSION:
+
+         It has been suggested that a client may wish to use the
+         contents of 'ciaddr' to further verify that a particular
+         BOOTREPLY message was indeed intended for it.
+
+
+
+
+
+Wimer                                                          [Page 19]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+5.4 Strategy for Delivery of BOOTREPLY Messages
+
+   Once the BOOTP server has created an appropriate BOOTREPLY message,
+   that BOOTREPLY message must be properly delivered to the client.
+
+   The server SHOULD first check the 'ciaddr' field.  If the 'ciaddr'
+   field is non-zero, the BOOTREPLY message SHOULD be sent as an IP
+   unicast to the IP address identified in the 'ciaddr' field.  The UDP
+   destination port MUST be set to BOOTPC (68).  However, the server
+   MUST be aware of the problems identified in Section 3.3.  The server
+   MAY choose to ignore the 'ciaddr' field and act as if the 'ciaddr'
+   field contains 0.0.0.0 (and thus continue with the rest of the
+   delivery algorithm below).
+
+   The server SHOULD next check the 'giaddr' field.  If this field is
+   non-zero, the server SHOULD send the BOOTREPLY as an IP unicast to
+   the IP address identified in the 'giaddr' field.  The UDP destination
+   port MUST be set to BOOTPS (67).  This action will deliver the
+   BOOTREPLY message directly to the BOOTP relay agent closest to the
+   client; the relay agent will then perform the final delivery to the
+   client.  If the BOOTP server has prior knowledge that a particular
+   client cannot receive unicast BOOTREPLY messages (e.g., the network
+   manager has explicitly configured the server with such knowledge),
+   the server MAY set the newly-defined BROADCAST flag to indicate that
+   relay agents SHOULD broadcast the BOOTREPLY message to the client.
+   Otherwise, the server MUST preserve the state of the BROADCAST flag
+   so that the relay agent can correctly act upon it.
+
+   If the 'giaddr' field is set to 0.0.0.0, then the client resides on
+   one of the same networks as the BOOTP server.  The server SHOULD
+   examine the newly-defined BROADCAST flag (see Sections 2.2, 3.1.1 and
+   4.1.2 for more information).  If this flag is set to 1 or the server
+   has prior knowledge that the client is unable to receive unicast
+   BOOTREPLY messages, the reply SHOULD be sent as an IP broadcast using
+   the IP limited broadcast address 255.255.255.255 as the IP
+   destination address and the link-layer broadcast address as the
+   link-layer destination address.  If the BROADCAST flag is cleared
+   (0), the reply SHOULD be sent as an IP unicast to the IP address
+   specified by the 'yiaddr' field and the link-layer address specified
+   in the 'chaddr' field.  If unicasting is not possible, the reply MAY
+   be sent as a broadcast in which case it SHOULD be sent to the link-
+   layer broadcast address using the IP limited broadcast address
+   255.255.255.255 as the IP destination address.  In any case, the UDP
+   destination port MUST be set to BOOTPC (68).
+
+
+
+
+
+
+
+Wimer                                                          [Page 20]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+      DISCUSSION:
+
+         The addition of the BROADCAST flag to the protocol is a
+         workaround to help promote interoperability with certain client
+         implementations.
+
+         The following table summarizes server delivery decisions for
+         BOOTREPLY messages based upon information in BOOTREQUEST
+         messages:
+
+      BOOTREQUEST fields     BOOTREPLY values for UDP, IP, link-layer
+   +-----------------------+-----------------------------------------+
+   | 'ciaddr'  'giaddr'  B | UDP dest     IP destination   link dest |
+   +-----------------------+-----------------------------------------+
+   | non-zero     X      X | BOOTPC (68)  'ciaddr'         normal    |
+   | 0.0.0.0   non-zero  X | BOOTPS (67)  'giaddr'         normal    |
+   | 0.0.0.0   0.0.0.0   0 | BOOTPC (68)  'yiaddr'         'chaddr'  |
+   | 0.0.0.0   0.0.0.0   1 | BOOTPC (68)  255.255.255.255  broadcast |
+   +-----------------------+-----------------------------------------+
+
+        B = BROADCAST flag
+
+        X = Don't care
+
+   normal = determine from the given IP destination using normal
+            IP routing mechanisms and/or ARP as for any other
+            normal datagram
+
+Acknowledgements
+
+   The author would like to thank Gary Malkin for his contribution of
+   the "BOOTP over IEEE 802.5 Token Ring Networks" section, and Steve
+   Deering for his observations on the problems associated with the
+   'giaddr' field.
+
+   Ralph Droms and the many members of the IETF Dynamic Host
+   Configuration and Router Requirements working groups provided ideas
+   for this memo as well as encouragement to write it.
+
+   Philip Almquist and David Piscitello offered many helpful suggestions
+   for improving the clarity, accuracy, and organization of this memo.
+   These contributions are graciously acknowledged.
+
+
+
+
+
+
+
+
+
+Wimer                                                          [Page 21]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+References
+
+   [1] Croft, B., and J. Gilmore, "Bootstrap Protocol (BOOTP)", RFC 951,
+       Stanford University and Sun Microsystems, September 1985.
+
+   [2] Reynolds, J., "BOOTP Vendor Information Extensions", RFC 1497,
+       USC/Information Sciences Institute, August 1993.  This RFC is
+       occasionally reissued with a new number.  Please be sure to
+       consult the latest version.
+
+   [3] Droms, R., "Dynamic Host Configuration Protocol", RFC 1541,
+       Bucknell University, October 1993.
+
+   [4] Plummer, D., "An Ethernet Address Resolution Protocol", STD 37,
+       RFC 826, MIT, November 1982.
+
+   [5] Deering, S., "ICMP Router Discovery Messages", RFC 1256, Xerox
+       PARC, September 1991.
+
+   [6] Reynolds, J., and J. Postel, "Assigned Numbers", STD 2, RFC 1340,
+       USC/Information Sciences Institute, July, 1992.  This RFC is
+       periodically reissued with a new number.  Please be sure to
+       consult the latest version.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Wimer                                                          [Page 22]
+\f
+RFC 1542        Clarifications and Extensions for BOOTP     October 1993
+
+
+Security Considerations
+
+   There are many factors which make BOOTP in its current form quite
+   insecure.  BOOTP is built directly upon UDP and IP which are as yet
+   inherently insecure themselves.  Furthermore, BOOTP is generally
+   intended to make maintenance of remote and/or diskless hosts easier.
+   While perhaps not impossible, configuring such hosts with passwords or
+   keys may be difficult and inconvenient.  This makes it difficult to
+   provide any form of reasonable authentication between servers and
+   clients.
+
+   Unauthorized BOOTP servers may easily be set up.  Such servers can
+   then send false and potentially disruptive information to clients such
+   as incorrect or duplicate IP addresses, incorrect routing information
+   (including spoof routers, etc.), incorrect domain nameserver addresses
+   (such as spoof nameservers), and so on.  Clearly, once this "seed"
+   mis-information is planted, an attacker can further compromise the
+   affected systems.
+
+   Unauthorized BOOTP relay agents may present some of the same problems
+   as unauthorized BOOTP servers.
+
+   Malicious BOOTP clients could masquerade as legitimate clients and
+   retrieve information intended for those legitimate clients.  Where
+   dynamic allocation of resources is used, a malicious client could
+   claim all resources for itself, thereby denying resources to
+   legitimate clients.
+
+Author's Address
+
+   Walt Wimer
+   Network Development
+   Carnegie Mellon University
+   5000 Forbes Avenue
+   Pittsburgh, PA  15213-3890
+
+   Phone: (412) 268-6252
+   EMail:  Walter.Wimer@CMU.EDU
+
+
+
+
+
+
+
+
+
+
+
+
+
+Wimer                                                          [Page 23]
+\f
\ No newline at end of file
diff --git a/omapip/iscprint.c b/omapip/iscprint.c
new file mode 100644 (file)
index 0000000..dd75e9f
--- /dev/null
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: iscprint.c,v 1.2 2005/03/17 20:30:41 dhankins Exp $ */
+
+#include "dhcpd.h"
+
+#ifdef NO_SNPRINTF
+
+#ifndef LINT
+static char copyright[] =
+"$Id: iscprint.c,v 1.2 2005/03/17 20:30:41 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium, Inc.  All rights reserved.";
+#endif
+
+#define INSIST(cond)   REQUIRE(cond)
+#define REQUIRE(cond)  if (!(cond)) { return 0; }
+
+/*
+ * Return length of string that would have been written if not truncated.
+ */
+
+int
+isc_print_snprintf(char *str, size_t size, const char *format, ...) {
+       va_list ap;
+       int ret;
+
+       va_start(ap, format);
+       ret = vsnprintf(str, size, format, ap);
+       va_end(ap);
+       return (ret);
+}
+
+/*
+ * Return length of string that would have been written if not truncated.
+ */
+
+int
+isc_print_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
+       int h;
+       int l;
+       int q;
+       int alt;
+       int zero;
+       int left;
+       int plus;
+       int space;
+       int neg;
+       isc_int64_t tmpi;
+       isc_uint64_t tmpui;
+       unsigned long width;
+       unsigned long precision;
+       unsigned int length;
+       char buf[1024];
+       char c;
+       void *v;
+       char *save = str;
+       const char *cp;
+       const char *head;
+       int count = 0;
+       int pad;
+       int zeropad;
+       int dot;
+       double dbl;
+#ifdef HAVE_LONG_DOUBLE
+       long double ldbl;
+#endif
+       char fmt[32];
+
+       INSIST(str != NULL);
+       INSIST(format != NULL);
+
+       while (*format != '\0') {
+               if (*format != '%') {
+                       if (size > 1) {
+                               *str++ = *format;
+                               size--;
+                       }
+                       count++;
+                       format++;
+                       continue;
+               }
+               format++;
+
+               /*
+                * Reset flags.
+                */
+               dot = neg = space = plus = left = zero = alt = h = l = q = 0;
+               width = precision = 0;
+               head = "";
+               length = pad = zeropad = 0;
+
+               do {
+                       if (*format == '#') {
+                               alt = 1;
+                               format++;
+                       } else if (*format == '-') {
+                               left = 1;
+                               zero = 0;
+                               format++;
+                       } else if (*format == ' ') {
+                               if (!plus)
+                                       space = 1;
+                               format++;
+                       } else if (*format == '+') {
+                               plus = 1;
+                               space = 0;
+                               format++;
+                       } else if (*format == '0') {
+                               if (!left)
+                                       zero = 1;
+                               format++;
+                       } else
+                               break;
+               } while (1);
+
+               /*
+                * Width.
+                */
+               if (*format == '*') {
+                       width = va_arg(ap, int);
+                       format++;
+               } else if (isdigit((unsigned char)*format)) {
+                       char *e;
+                       width = strtoul(format, &e, 10);
+                       format = e;
+               }
+
+               /*
+                * Precision.
+                */
+               if (*format == '.') {
+                       format++;
+                       dot = 1;
+                       if (*format == '*') {
+                               precision = va_arg(ap, int);
+                               format++;
+                       } else if (isdigit((unsigned char)*format)) {
+                               char *e;
+                               precision = strtoul(format, &e, 10);
+                               format = e;
+                       }
+               }
+
+               switch (*format) {
+               case '\0':
+                       continue;
+               case '%':
+                       if (size > 1) {
+                               *str++ = *format;
+                               size--;
+                       }
+                       count++;
+                       break;
+               case 'q':
+                       q = 1;
+                       format++;
+                       goto doint;
+               case 'h':
+                       h = 1;
+                       format++;
+                       goto doint;
+               case 'l':
+                       l = 1;
+                       format++;
+                       if (*format == 'l') {
+                               q = 1;
+                               format++;
+                       }
+                       goto doint;
+               case 'n':
+               case 'i':
+               case 'd':
+               case 'o':
+               case 'u':
+               case 'x':
+               case 'X':
+               doint:
+                       if (precision != 0)
+                               zero = 0;
+                       switch (*format) {
+                       case 'n':
+                               if (h) {
+                                       short int *p;
+                                       p = va_arg(ap, short *);
+                                       REQUIRE(p != NULL);
+                                       *p = str - save;
+                               } else if (l) {
+                                       long int *p;
+                                       p = va_arg(ap, long *);
+                                       REQUIRE(p != NULL);
+                                       *p = str - save;
+                               } else {
+                                       int *p;
+                                       p = va_arg(ap, int *);
+                                       REQUIRE(p != NULL);
+                                       *p = str - save;
+                               }
+                               break;
+                       case 'i':
+                       case 'd':
+                               if (q)
+                                       tmpi = va_arg(ap, isc_int64_t);
+                               else if (l)
+                                       tmpi = va_arg(ap, long int);
+                               else
+                                       tmpi = va_arg(ap, int);
+                               if (tmpi < 0) {
+                                       head = "-";
+                                       tmpui = -tmpi;
+                               } else {
+                                       if (plus)
+                                               head = "+";
+                                       else if (space)
+                                               head = " ";
+                                       else
+                                               head = "";
+                                       tmpui = tmpi;
+                               }
+                               sprintf(buf, "%u", tmpui);
+                               goto printint;
+                       case 'o':
+                               if (q)
+                                       tmpui = va_arg(ap, isc_uint64_t);
+                               else if (l)
+                                       tmpui = va_arg(ap, long int);
+                               else
+                                       tmpui = va_arg(ap, int);
+                               sprintf(buf, alt ? "%#o"
+                                                : "%o", tmpui);
+                               goto printint;
+                       case 'u':
+                               if (q)
+                                       tmpui = va_arg(ap, isc_uint64_t);
+                               else if (l)
+                                       tmpui = va_arg(ap, unsigned long int);
+                               else
+                                       tmpui = va_arg(ap, unsigned int);
+                               sprintf(buf, "%u", tmpui);
+                               goto printint;
+                       case 'x':
+                               if (q)
+                                       tmpui = va_arg(ap, isc_uint64_t);
+                               else if (l)
+                                       tmpui = va_arg(ap, unsigned long int);
+                               else
+                                       tmpui = va_arg(ap, unsigned int);
+                               if (alt) {
+                                       head = "0x";
+                                       if (precision > 2)
+                                               precision -= 2;
+                               }
+                               sprintf(buf, "%x", tmpui);
+                               goto printint;
+                       case 'X':
+                               if (q)
+                                       tmpui = va_arg(ap, isc_uint64_t);
+                               else if (l)
+                                       tmpui = va_arg(ap, unsigned long int);
+                               else
+                                       tmpui = va_arg(ap, unsigned int);
+                               if (alt) {
+                                       head = "0X";
+                                       if (precision > 2)
+                                               precision -= 2;
+                               }
+                               sprintf(buf, "%X", tmpui);
+                               goto printint;
+                       printint:
+                               if (precision != 0 || width != 0) {
+                                       length = strlen(buf);
+                                       if (length < precision)
+                                               zeropad = precision - length;
+                                       else if (length < width && zero)
+                                               zeropad = width - length;
+                                       if (width != 0) {
+                                               pad = width - length -
+                                                     zeropad - strlen(head);
+                                               if (pad < 0)
+                                                       pad = 0;
+                                       }
+                               }
+                               count += strlen(head) + strlen(buf) + pad +
+                                        zeropad;
+                               if (!left) {
+                                       while (pad > 0 && size > 1) {
+                                               *str++ = ' ';
+                                               size--;
+                                               pad--;
+                                       }
+                               }
+                               cp = head;
+                               while (*cp != '\0' && size > 1) {
+                                       *str++ = *cp++;
+                                       size--;
+                               }
+                               while (zeropad > 0 && size > 1) {
+                                       *str++ = '0';
+                                       size--;
+                                       zeropad--;
+                               }
+                               cp = buf;
+                               while (*cp != '\0' && size > 1) {
+                                       *str++ = *cp++;
+                                       size--;
+                               }
+                               while (pad > 0 && size > 1) {
+                                       *str++ = ' ';
+                                       size--;
+                                       pad--;
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               case 's':
+                       cp = va_arg(ap, char *);
+                       REQUIRE(cp != NULL);
+
+                       if (precision != 0) {
+                               /*
+                                * cp need not be NULL terminated.
+                                */
+                               const char *tp;
+                               unsigned long n;
+
+                               n = precision;
+                               tp = cp;
+                               while (n != 0 && *tp != '\0')
+                                       n--, tp++;
+                               length = precision - n;
+                       } else {
+                               length = strlen(cp);
+                       }
+                       if (width != 0) {
+                               pad = width - length;
+                               if (pad < 0)
+                                       pad = 0;
+                       }
+                       count += pad + length;
+                       if (!left)
+                               while (pad > 0 && size > 1) {
+                                       *str++ = ' ';
+                                       size--;
+                                       pad--;
+                               }
+                       if (precision != 0)
+                               while (precision > 0 && *cp != '\0' &&
+                                      size > 1) {
+                                       *str++ = *cp++;
+                                       size--;
+                                       precision--;
+                               }
+                       else
+                               while (*cp != '\0' && size > 1) {
+                                       *str++ = *cp++;
+                                       size--;
+                               }
+                       while (pad > 0 && size > 1) {
+                               *str++ = ' ';
+                               size--;
+                               pad--;
+                       }
+                       break;
+               case 'c':
+                       c = va_arg(ap, int);
+                       if (width > 0) {
+                               count += width;
+                               width--;
+                               if (left) {
+                                       *str++ = c;
+                                       size--;
+                               }
+                               while (width-- > 0 && size > 1) {
+                                       *str++ = ' ';
+                                       size--;
+                               }
+                               if (!left && size > 1) {
+                                       *str++ = c;
+                                       size--;
+                               }
+                       } else {
+                               count++;
+                               if (size > 1) {
+                                       *str++ = c;
+                                       size--;
+                               }
+                       }
+                       break;
+               case 'p':
+                       v = va_arg(ap, void *);
+                       sprintf(buf, "%p", v);
+                       length = strlen(buf);
+                       if (precision > length)
+                               zeropad = precision - length;
+                       if (width > 0) {
+                               pad = width - length - zeropad;
+                               if (pad < 0)
+                                       pad = 0;
+                       }
+                       count += length + pad + zeropad;
+                       if (!left)
+                               while (pad > 0 && size > 1) {
+                                       *str++ = ' ';
+                                       size--;
+                                       pad--;
+                               }
+                       cp = buf;
+                       if (zeropad > 0 && buf[0] == '0' &&
+                           (buf[1] == 'x' || buf[1] == 'X')) {
+                               if (size > 1) {
+                                       *str++ = *cp++;
+                                       size--;
+                               }
+                               if (size > 1) {
+                                       *str++ = *cp++;
+                                       size--;
+                               }
+                               while (zeropad > 0 && size > 1) {
+                                       *str++ = '0';
+                                       size--;
+                                       zeropad--;
+                               }
+                       }
+                       while (*cp != '\0' && size > 1) {
+                               *str++ = *cp++;
+                               size--;
+                       }
+                       while (pad > 0 && size > 1) {
+                               *str++ = ' ';
+                               size--;
+                               pad--;
+                       }
+                       break;
+               case 'D':       /*deprecated*/
+                       INSIST("use %ld instead of %D" == NULL);
+               case 'O':       /*deprecated*/
+                       INSIST("use %lo instead of %O" == NULL);
+               case 'U':       /*deprecated*/
+                       INSIST("use %lu instead of %U" == NULL);
+
+               case 'L':
+#ifdef HAVE_LONG_DOUBLE
+                       l = 1;
+#else
+                       INSIST("long doubles are not supported" == NULL);
+#endif
+                       /*FALLTHROUGH*/
+               case 'e':
+               case 'E':
+               case 'f':
+               case 'g':
+               case 'G':
+                       if (!dot)
+                               precision = 6;
+                       /*
+                        * IEEE floating point.
+                        * MIN 2.2250738585072014E-308
+                        * MAX 1.7976931348623157E+308
+                        * VAX floating point has a smaller range than IEEE.
+                        *
+                        * precisions > 324 don't make much sense.
+                        * if we cap the precision at 512 we will not
+                        * overflow buf.
+                        */
+                       if (precision > 512)
+                               precision = 512;
+                       sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "",
+                               plus ? "+" : space ? " " : "",
+                               precision, l ? "L" : "", *format);
+                       switch (*format) {
+                       case 'e':
+                       case 'E':
+                       case 'f':
+                       case 'g':
+                       case 'G':
+#ifdef HAVE_LONG_DOUBLE
+                               if (l) {
+                                       ldbl = va_arg(ap, long double);
+                                       sprintf(buf, fmt, ldbl);
+                               } else
+#endif
+                               {
+                                       dbl = va_arg(ap, double);
+                                       sprintf(buf, fmt, dbl);
+                               }
+                               length = strlen(buf);
+                               if (width > 0) {
+                                       pad = width - length;
+                                       if (pad < 0)
+                                               pad = 0;
+                               }
+                               count += length + pad;
+                               if (!left)
+                                       while (pad > 0 && size > 1) {
+                                               *str++ = ' ';
+                                               size--;
+                                               pad--;
+                                       }
+                               cp = buf;
+                               while (*cp != ' ' && size > 1) {
+                                       *str++ = *cp++;
+                                       size--;
+                               }
+                               while (pad > 0 && size > 1) {
+                                       *str++ = ' ';
+                                       size--;
+                                       pad--;
+                               }
+                               break;
+                       default:
+                               continue;
+                       }
+                       break;
+               default:
+                       continue;
+               }
+               format++;
+       }
+       if (size > 0)
+               *str = '\0';
+       return (count);
+}
+
+#endif