summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Hankins <dhankins@isc.org>2005-03-17 20:30:41 +0000
committerDavid Hankins <dhankins@isc.org>2005-03-17 20:30:41 +0000
commit5d0ff7ea7cc183d004752c08e002e965921d4d48 (patch)
tree2590e81d18bc19c75f751f0b074de447f423adb1
parent98311e4ba99c21b71079481caccdd5a977f22c78 (diff)
downloadisc-dhcp-5d0ff7ea7cc183d004752c08e002e965921d4d48.tar.gz
New files added in merge from V3-RELEASE-BRANCH to HEAD as of V3-0-3-BETA-1.
-rw-r--r--LICENSE20
-rw-r--r--contrib/ms2isc/Registry.pm361
-rw-r--r--contrib/ms2isc/ms2isc.pl634
-rw-r--r--contrib/ms2isc/readme.txt15
-rw-r--r--doc/rfc1542.txt1291
-rw-r--r--omapip/iscprint.c539
6 files changed, 2860 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..b737de68
--- /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
index 00000000..69e24136
--- /dev/null
+++ b/contrib/ms2isc/Registry.pm
@@ -0,0 +1,361 @@
+# Registry.pm
+# A perl module provided easy Windows Registry access
+#
+# Author: Shu-Min Chang
+#
+# Copyright(c) 2002 Intel Corporation. All rights reserved
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution
+# 3. Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INTEL CORPORATION AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL CORPORATION OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO PROCUREMENT OF SUBSTITUE
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVICED OF THE POSSIBILITY OF SUCH
+# DAMAGE.
+
+package Registry;
+use strict;
+use Win32API::Registry 0.21 qw( :ALL );
+
+
+###############################################################################
+
+#-----------------------------------------
+sub GetRegKeyVal($*) {
+ my ($FullRegPath, $value) = @_;
+#-----------------------------------------
+# Purpose: uses Win32API to get registry information from a given server
+#
+# WARNING: this procedure is VERY Win32 specific, you'll need a Win32 manual
+# to figure out why something is done.
+# input: $FullRegPath: a MS specific way of fully qualifying a registry path
+# \\Server\RootKey\Path\ValueName
+# output: *value: the value of the registry key of $FullRegPath
+#
+
+ my ($RemoteMachine, $RootKey, $RegPath, $KeyName, $i);
+
+#print "in sub:GetRegKeyVal:Parameters:", @_, "\n";
+
+ # Check the for valid fully qualified registry path
+ return -1 if (! ($FullRegPath =~ /\\.+\\.+/)) && (!($FullRegPath =~ /\\\\.+\\.+\\.+/));
+
+
+ $RemoteMachine = (index($FullRegPath, "\\\\") == $[ ? substr($FullRegPath, $[+2, index($FullRegPath, "\\", $[+2)-2):0);
+
+#print "RemoteMachine = $RemoteMachine\n";
+
+ $i = $RemoteMachine ? $[+3+length($RemoteMachine) : $[+1;
+ $RootKey = substr ($FullRegPath, $i, index($FullRegPath, "\\", $i)-$i);
+
+ $KeyName = $FullRegPath;
+ $KeyName =~ s/.*\\(.+)/$1/;
+#print "KeyName = $KeyName\n";
+
+ $i = index($FullRegPath, $RootKey, $[+length($RemoteMachine)) + $[ + length($RootKey)+1;
+ $RegPath = substr ($FullRegPath, $i, length($FullRegPath) - length($KeyName) -$i - 1);
+#print "RegPath = $RegPath\n";
+
+ my ($RootKeyHandle, $handle, $key, $type);
+
+ if ($RemoteMachine) {
+ $RootKeyHandle = regConstant($RootKey);
+
+ if (!RegConnectRegistry ($RemoteMachine, $RootKeyHandle, $handle)) {
+ $$value = regLastError();
+ return -2;
+ }
+ } else { # not valid actually because I can't find the mapping table of default
+ # local handle mapping. Should always pass in the Machine name to use for now
+ $handle = $RootKey;
+ }
+
+ if (!RegOpenKeyEx ($handle, $RegPath, 0, KEY_READ, $key)) {
+ $$value = regLastError();
+#print "regLastError = $$value\n";
+ return -3;
+ }
+ if (!RegQueryValueEx( $key, $KeyName, [], $type, $$value, [] )) {
+ $$value = regLastError();
+#print "regLastError = $$value\n";
+ return -4;
+ }
+
+#print "RegType=$type\n"; # Perl doesn't fetch type, at this in this
+ # ActiveState 5.6.0 that I'm using
+#print "RegValue=$$value\n";
+ RegCloseKey ($key);
+ RegCloseKey ($handle);
+
+ return 0;
+}
+
+###############################################################################
+
+#-----------------------------------------
+sub GetRegSubkeyList($*) {
+ my ($FullKeyRegPath, $Subkeys) = @_;
+#-----------------------------------------
+# Purpose: uses Win32API to get registry subkey list from a given server
+#
+# WARNING: this procedure is VERY Win32 specific, you'll need a Win32 manual
+# to figure out why something is done.
+# input: $FullKeyRegPath: a MS specific way of fully qualifying a registry path
+# \\Server\RootKey\Path\KeyName
+# output: *Subkeys: the list of subkeys in array of the registry key of
+# $FullKeyRegPath
+#
+
+ my ($RemoteMachine, $RootKey, $RegPath, $KeyName, $i);
+
+#print "in sub:GetRegSubkeyList:Parameters:", @_, "\n";
+
+ # Check the for valid registry key path
+ return -1 if (! ($FullKeyRegPath =~ /\\.+\\.+/)) && (!($FullKeyRegPath =~ /\\\\.+\\.+\\.+/));
+
+
+ $RemoteMachine = (index($FullKeyRegPath, "\\\\") == $[ ? substr($FullKeyRegPath, $[+2, index($FullKeyRegPath, "\\", $[+2)-2):0);
+
+#print "RemoteMachine = $RemoteMachine\n";
+
+ $i = $RemoteMachine ? $[+3+length($RemoteMachine) : $[+1;
+ $RootKey = substr ($FullKeyRegPath, $i, index($FullKeyRegPath, "\\", $i)-$i);
+
+ $i = index($FullKeyRegPath, $RootKey, $[+length($RemoteMachine)) + $[ + length($RootKey)+1;
+ $RegPath = substr ($FullKeyRegPath, $i);
+
+#print "RegPath = $RegPath\n";
+
+ my ($RootKeyHandle, $handle, $key, $type);
+
+ if ($RemoteMachine) {
+ $RootKeyHandle = regConstant($RootKey);
+
+ if (!RegConnectRegistry ($RemoteMachine, $RootKeyHandle, $handle)) {
+ @$Subkeys[0]= regLastError();
+ return -2;
+ }
+ } else { # not valid actually because I can't find the mapping table of default
+ # local handle mapping. Should always pass in the Machine name to use for now
+ $handle = $RootKey;
+ }
+
+ if (!RegOpenKeyEx ($handle, $RegPath, 0, KEY_READ, $key)) {
+ @$Subkeys[0] = regLastError();
+#print "regLastError = @$Subkeys[0]\n";
+ return -3;
+ }
+
+ my $tmp;
+ # For some reason, the regLastError() stays at ERROR_NO_MORE_ITEMS
+ # in occasional call sequence, so I'm resetting the error code
+ # before entering the loop
+ regLastError(0);
+ for ($i=0; regLastError()==regConstant("ERROR_NO_MORE_ITEMS"); $i++) {
+#print "\nERROR: error enumumerating reg\n";
+ if (RegEnumKeyEx ($key, $i, $tmp, [], [], [], [], [])) {
+ @$Subkeys[$i] = $tmp;
+ }
+ }
+
+#print "RegType=$type\n";
+#print "RegValue=@$Subkeys\n";
+ RegCloseKey ($key);
+ RegCloseKey ($handle);
+
+ return 0;
+}
+
+#####################################################
+
+sub ExtractOptionIps ($) {
+ my ($MSDHCPOption6Value) = @_;
+ my @ip;
+# purpose: DHCP registry specific; to return the extracted IP addresses from
+# the input variable
+# input:
+# $MSDHCPOption6Value: Option 6 was used to develop, but it works for any
+# other options of the same datatype.
+# output: none
+# return:
+# @ip: an arry of IP addresses in human readable format.
+
+
+ # First extract the size of the option
+ my ($byte, $size, $ind1, $ind2, @octet) = unpack("VVVV", $MSDHCPOption6Value);
+# print "byte = $byte\nsize=$size\nind1=$ind1\nind2=$ind2\n";
+
+ # Calculate total number of bytes that IP addresses occupy
+ my $number = $size * $ind1;
+ ($byte, $size, $ind1, $ind2, @octet) = unpack("VVVVC$number", $MSDHCPOption6Value);
+
+ for (my $i=0; $i<$#octet; $i=$i+4) {
+ $ip[$i/4] = "$octet[$i+3]\.$octet[$i+2]\.$octet[$i+1]\.$octet[$i]";
+ }
+
+ return @ip;
+}
+
+#####################################################
+
+sub ExtractOptionStrings ($) {
+ my ($MSDHCPOption15Value) = @_;
+ my @string;
+# purpose: DHCP registry specific; to return the extracted string from
+# the input variable
+# input:
+# $MSDHCPOption15Value: Option 15 was used to develop, but it works for any
+# other options of the same datatype.
+# output: none
+# return:
+# @string: an arry of strings in human readable format.
+
+
+ # First extract the size of the option
+ my ($byte, $start, $ind1, $ind2, $size, @data) = unpack("VVVVV", $MSDHCPOption15Value);
+# print "byte = $byte\nstart=$start\nind1=$ind1\nind2=$ind2\nsize=$size\n";
+
+ # Calculate total number of bytes that IP addresses occupy
+ my $number = $size * $ind1;
+ ($byte, $start, $ind1, $ind2, $size, @data) = unpack("VVVVVC$number", $MSDHCPOption15Value);
+
+ for (my $i=0; $i<$ind1; $i++) {
+ # actually this is only programmed to do one string, until I see
+ # example of how the multiple strings are represented, I don't have a
+ # guess to how to program them properly.
+ for (my $j=0; $j<$#data & $data[$j]!=0; $j+=2) {
+ $string[$i] = $string[$i].chr($data[$j]);
+ }
+ }
+
+ return @string;
+}
+
+#####################################################
+
+sub ExtractOptionHex ($) {
+ my ($MSDHCPOption46Value) = @_;
+ my @Hex;
+# purpose: DHCP registry specific; to return the extracted hex from the input
+# variable
+# input:
+# $MSDHCPOption46Value: Option 46 was used to develop, but it works for any
+# other options of the same datatype.
+# output: none
+# return:
+# @Hex: an arry of hex strings in human readable format.
+ my $Temp;
+
+
+ # First extract the size of the option
+ my ($byte, $unknown, $ind1, $ind2, @data) = unpack("VVVV", $MSDHCPOption46Value);
+# print "byte=$byte\nunknown=$unknown\nind1=$ind1\nind2=$ind2\n";
+
+ # Calculate total number of bytes that IP addresses occupy
+ my $number = $byte - 15;
+ ($byte, $unknown, $ind1, $ind2, @data) = unpack("VVVVC$number", $MSDHCPOption46Value);
+
+# printf "data=%4x\n", $data[0];
+
+ for (my $i=0; $i<$ind1; $i++) {
+ # actually this is only programmed to do one Hex, until I see
+ # example of how the multiple Hexes are represented, I don't have a
+ # guess to how to program them properly.
+ for (my $j=3; $j>=0; $j--) {
+ $Hex[$i] = $Hex[$i].sprintf ("%x", $data[$j+$i*4]);
+ }
+ }
+
+ return @Hex;
+}
+
+#####################################################
+
+sub ExtractExclusionRanges ($) {
+ my ($MSDHCPExclusionRanges) = @_;
+ my @RangeList;
+# purpose: DHCP registry specific; to return the extracted exclusion ranges
+# from the input variable
+# input:
+# $MSDHCPExclusionRanges: Exclusion range as DHCP server returns them
+# output: none
+# return:
+# @RangeList: an arry of paird IP addresses strings in human readable format.
+
+
+ # First extract the size of the option
+ my ($paircount, @data) = unpack("V", $MSDHCPExclusionRanges);
+# print "paircount = $paircount\n";
+
+ # Calculate total number of bytes that IP addresses occupy
+# my $number = $paircount * 4*2;
+# ($paircount, @data) = unpack("VC$number", $MSDHCPExclusionRanges);
+#
+# for (my $i=0; $i<$#data; $i=$i+4) {
+# $ip[$i/4] = "$data[$i+3]\.$data[$i+2]\.$data[$i+1]\.$data[$i]";
+# }
+#
+ my $number = $paircount * 2;
+ ($paircount, @data) = unpack("VL$number", $MSDHCPExclusionRanges);
+
+ for (my $i=0; $i<=$#data; $i++) {
+ $RangeList[$i] = pack ("L", $data[$i]);
+# print "extracted", ExtractIp ($RangeList[$i]), "\n";
+ }
+
+ return @RangeList;
+}
+#####################################################
+
+sub ExtractIp ($) {
+ my ($octet) = @_;
+# purpose: to return the registry saved IP address in a readable form
+# input:
+# $octet: a 4 byte data storing the IP address as the registry save it as
+# output: none
+# return: anonymous variable of a string of IP address
+
+ my (@data) = unpack ("C4", $octet);
+
+ return "$data[3]\.$data[2]\.$data[1]\.$data[0]";
+
+}
+#####################################################
+
+sub ExtractHex ($) {
+ my ($HexVal) = @_;
+ my @Hex;
+# purpose: to return the registry saved hex number in a readable form
+# input:
+# $octet: a 4 byte data storing the hex number as the registry save it as
+# output: none
+# return:
+# $Hex: string of hex digit
+
+
+ # First extract the size of the option
+ my (@data) = unpack("C4", $HexVal);
+
+ for (my $i=3; $i>=0; $i--) {
+ $Hex[0] = $Hex[0] . sprintf ("%x", $data[$i]);
+ }
+
+ return @Hex;
+}
+1;
diff --git a/contrib/ms2isc/ms2isc.pl b/contrib/ms2isc/ms2isc.pl
new file mode 100644
index 00000000..da3e10f7
--- /dev/null
+++ b/contrib/ms2isc/ms2isc.pl
@@ -0,0 +1,634 @@
+#set ts=3
+#
+# ms2isc.pl
+# MS NT4 DHCP to ISC DHCP Configuration Migration Tool
+#
+# Author: Shu-Min Chang
+#
+# Copyright(c) 2003 Intel Corporation. All rights reserved
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution
+# 3. Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INTEL CORPORATION AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL CORPORATION OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO PROCUREMENT OF SUBSTITUE
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVICED OF THE POSSIBILITY OF SUCH
+# DAMAGE.
+
+use strict;
+use Socket;
+use Getopt::Std;
+use Filehandle;
+use Registry; # Custom Perl Module to make Registry access easier.
+
+my $usage = << 'ENDOFHELP';
+
+Purpose: A Perl Script converting MS NT4 DHCP configuration to ISC DHCP3
+configuration file by reading NT4's registry.
+
+Requires: Registry.pm and ActiveState 5.6.0
+
+Usage: $ARGV -s <Srv> -o <Out> [-p <Pri> [-k <key>]] [-f <Fo>]
+
+ <Srv> Server IP or name for NT4 DHCP server to fetch the configuration from.
+ <Out> Output filename for the configuration file.
+ <Pri> Primary DNS server name for sending the dynamic DNS update to.
+ <Key> Key name for use in updating the dynamic DNS zone.
+ <Fo> Failover peer name shared with the DHCP partner.
+
+Essentially the <Srv> needs to be an NT4 (3.x should work but not tested) which
+you should have registry read access to. You must run this script from a
+Windows machine because of the requirement to access the registry.
+
+The <Pri> is optional parameter for desginating the dynamic DNS update if
+missing then the "zone" section of the declaration will be skipped. The <Key>
+is needed if you've configured your DNS zone with a key, in addition, you'll
+need to define that key in this DHCP configuration file elsewhere manually,
+read the DHCP Handbook to figure out what you need to define.
+
+The <Fo> specifies the fail-over peer name in the pool section, you'll need to
+define additional detail elsewhere manually, again read the DHCP handbook.
+
+NOTE: the program only knows of the following global and subnet options:
+ 3, 6, 15, 28, 44, and 46
+
+ If it runs into options other than the known ones, it will quit. You
+ may fix this by modifying the following procedures:
+ GetGlobalOptions
+ GetScopes
+ PrintSubnetConfig
+
+ In addition, the resulting subnets configuration will have the "deny
+ dynamic bootp clients" you should take them out if that's not what you
+ want :).
+
+ Finally, as the parameter structures implied, it is assumed that you
+ want the same zone primary and update key for all zones and that the
+ same failover is to be applied to all the pools. Furthermore the
+ subnet zones are all assumed to be class C delineated, but if you
+ happend to be delegated at the class B level, this will work fine too.
+
+Author: Shu-Min Chang <smchang@yahoo.com>
+
+Copyright: Please read the top of the source code
+
+Acknowledgement:
+ Brian L. King for coding help, Douglas A. Darrah for testing, and James E.
+Pressley for being the DHCP reference book :).
+
+Usage: $ARGV -s <Srv> -o <Out> [-p <Pri> [-k <key>]] [-f <Fo>]
+
+Version: 1.0.1
+
+ENDOFHELP
+
+###################### Begin Main Program ####################################
+
+ my (%opts, %GlobalOptions, %SuperScopes, %Scopes);
+
+ ### Get parameters and make sure that they meet the require/optoinal criteria
+ getopts('s:o:p:k:f:', \%opts) or die $usage;
+ ($opts{s} and $opts{o}) or die $usage;
+ if ($opts{k}) { $opts{p} or die $usage; }
+
+ ### Read all the registry stuff into the memory
+ %GlobalOptions = GetGlobalOptions($opts{s});
+ %SuperScopes = GetSuperScope($opts{s});
+ %Scopes = GetScopes ($opts{s});
+
+ ### Process and print out to the output file
+ my ($outfile, $i, $j, @Domains);
+
+ $outfile = new FileHandle "> $opts{o}";
+ if (!defined $outfile) {
+ die "Can't open file: $opts{o}: $!";
+ }
+
+ for $i (keys %SuperScopes) {
+ print $outfile "\n##############################################################\n";
+ my ($Scopename) = $i;
+ $Scopename =~ s/ //g;
+ print $outfile "shared-network $Scopename {\n";
+ foreach $j (@{$SuperScopes{$i}}) {
+ PrintSubnetConfig($outfile, \%GlobalOptions, \%{$Scopes{$j}}, $j, "\t", $opts{f});
+ InsertIfUnique (\@Domains, $Scopes{$j}{domain}) if exists $Scopes{$j}{domain};
+ delete $Scopes{$j};
+ }
+ print $outfile "}\n";
+ if ($opts{p} or $opts{k}) {
+ foreach $j (@{$SuperScopes{$i}}) {
+ PrintSubnetUpdate($outfile, $j, $opts{p}, $opts{k});
+ }
+ }
+ }
+
+ for $i (keys %Scopes) {
+ print $outfile "\n##############################################################\n";
+ PrintSubnetConfig($outfile, \%GlobalOptions, \%{$Scopes{$i}}, $i, "", $opts{f});
+ if ($opts{p} or $opts{k}) { PrintSubnetUpdate($outfile, $i, $opts{p}, $opts{k}); }
+ InsertIfUnique (\@Domains, $Scopes{$i}{domain}) if exists $Scopes{$i}{domain};
+ }
+
+ if ($opts{p} or $opts{k}) {
+ InsertIfUnique (\@Domains, $GlobalOptions{domain}) if exists $GlobalOptions{domain};
+ for $i (@Domains) {
+ PrintDomainUpdate($outfile, $i, $opts{p}, $opts{k});
+ }
+ }
+
+ undef ($outfile);
+ print "Done.\n";
+ exit();
+
+################################## End Main Program ###########################
+
+
+
+
+
+######################################################################
+sub InsertIfUnique ($$) {
+ my ($Array, $data) = @_;
+# purpose: insert $data into array @{$Array} iff the data is not in there yet
+# input:
+# $data: scalar data to be added to the @{$Array} if unique
+# $Array: reference of the Array to compare the uniqueness of the $data
+# output:
+# $Array: reference of the array with the resulting array.
+# return: none
+
+ my ($i);
+
+ for ($i=0; $i<=$#{$Array} && ${$Array}[$i] ne $data; $i++) { }
+
+ if ($i > $#{$Array}) {
+ ${$Array}[$i] = $data;
+ }
+}
+######################################################################
+sub PrintDomainUpdate ($$$$) {
+ my ($outfile, $Domain, $DDNSServer, $key) = @_;
+# purpose: print out the foward domain zone update declaration
+# input:
+# $outfile: filehandle of the file to write the output to
+# $Domain: a string representing the forward domain
+# $DDNSServer: a string of the DNS server accepting the DDNS update
+# $key: a string representing the key used to update the zone
+# output: none
+# return: none
+#
+
+ print $outfile "zone $Domain {\n";
+ print $outfile "\tprimary $DDNSServer;\n";
+ !$key or print $outfile "\tkey $key;\n";
+ print $outfile "}\n";
+
+}
+######################################################################
+sub PrintSubnetUpdate ($$$$) {
+ my ($outfile, $Subnet, $DDNSServer, $key) = @_;
+# purpose: print out the reverse domain zone update declaration
+# input:
+# $outfile: filehandle of the file to write the output to
+# $Subnet: a string representing the subnet in the form 1.2.3.4
+# $DDNSServer: a string of the DNS server accepting the DDNS update
+# $key: a string representing the key used to update the zone
+# output: none
+# return: none
+#
+
+ my ($Reverse);
+
+ $_ = join (".", reverse(split(/\./, $Subnet)));
+ m/\d*\.(.*)/;
+ $Reverse = $1;
+ print $outfile "zone $Reverse.in-addr.arpa. {\n";
+ print $outfile "\tprimary $DDNSServer;\n";
+ !$key or print $outfile "\tkey $key;\n";
+ print $outfile "}\n";
+
+}
+######################################################################
+sub PrintSubnetConfig ($$$$$$) {
+ my ($outfile, $GlobalOptions, $Scope, $Subnet, $prefix, $failover) = @_;
+# purpose: print out the effective scope configuration for one subnet as
+# derived from the global and scope options.
+# input:
+# $outfile: filehandle of the file to write the output to
+# $GlobalOptions: refernce to the hashed variable from GetGlobalOptions
+# $Scopes: reference to the hashed variable of the subnet in interest
+# $Subnet: string variable of the subnet being processed
+# $prefix: string to be printed before each line (designed for tab)
+# $failover: string to be used for the "failover peer" line
+# output: none
+# return: none
+#
+ my ($pound) = ( ${$Scope}{disable}? "#".$prefix : $prefix);
+ print $outfile $pound, "subnet $Subnet netmask ${$Scope}{mask} {\n";
+ print $outfile "$prefix# Name: ${$Scope}{name}\n";
+ print $outfile "$prefix# Comment: ${$Scope}{comment}\n";
+ if (exists ${$Scope}{routers}) {
+ print $outfile $pound, "\toption routers @{${$Scope}{routers}};\n";
+ } elsif (exists ${$GlobalOptions}{routers}) {
+ print $outfile $pound, "\toption routers @{${$GlobalOptions}{routers}};\t# NOTE: obtained from global option, bad practice detected\n";
+ } else {
+ print $outfile "### WARNING: No router was found for this subnet!!! ##########\n";
+ }
+
+ if (exists ${$Scope}{dnses}) {
+ print $outfile $pound, "\toption domain-name-servers ", join(",", @{${$Scope}{dnses}}), ";\n";
+ } elsif (exists ${$GlobalOptions}{dnses}) {
+ print $outfile $pound, "\toption domain-name-servers ", join(",", @{${$GlobalOptions}{dnses}}), ";\n";
+ }
+
+ if (exists ${$Scope}{domain}) {
+ print $outfile $pound, "\toption domain-name \"${$Scope}{domain}\";\n";
+ } elsif (exists ${$GlobalOptions}{domain}) {
+ print $outfile $pound, "\toption domain-name \"${$GlobalOptions}{domain}\";\n";
+ }
+
+ if (exists ${$Scope}{broadcast}) {
+ print $outfile $pound, "\toption broadcast-address ${$Scope}{broadcast};\n";
+ } elsif (exists ${$GlobalOptions}{broadcast}) {
+ print $outfile $pound, "\toption broadcast-address ${$GlobalOptions}{broadcast};\n";
+ }
+
+ if (exists ${$Scope}{winses}) {
+ print $outfile $pound, "\toption netbios-name-servers ", join(",", @{${$Scope}{winses}}), ";\n";
+ } elsif (exists ${$GlobalOptions}{winses}) {
+ print $outfile $pound, "\toption netbios-name-servers ", join(",", @{${$GlobalOptions}{winses}}), ";\n";
+ }
+
+ if (exists ${$Scope}{winstype}) {
+ print $outfile $pound, "\toption netbios-node-type ${$Scope}{winstype};\n";
+ } elsif (exists ${$GlobalOptions}{winstype}) {
+ print $outfile $pound, "\toption netbios-node-type ${$GlobalOptions}{winstype};\n"
+ }
+
+ print $outfile $pound, "\tdefault-lease-time ${$Scope}{leaseduration};\n";
+ print $outfile $pound, "\tpool {\n";
+ for (my $r=0; $r<=$#{${$Scope}{ranges}}; $r+=2) {
+ print $outfile $pound, "\t\trange ${$Scope}{ranges}[$r] ${$Scope}{ranges}[$r+1];\n";
+ }
+ !$failover or print $outfile $pound, "\t\tfailover peer \"$failover\";\n";
+ print $outfile $pound, "\t\tdeny dynamic bootp clients;\n";
+ print $outfile $pound, "\t}\n";
+ print $outfile $pound, "}\n";
+}
+
+######################################################################
+sub GetScopes ($) {
+ my ($Server) = @_;
+ my (%Scopes);
+# purpose: to return NT4 server's scope configuration
+# input:
+# $Server: string of the valid IP or name of the NT4 server
+# output: none
+# return:
+# %Scope: hash of hash of hash of various data types to be returned of the
+# following data structure
+# $Scope{<subnet>}{disable} => boolean
+# $Scope{<subnet>}{mask} => string (e.g. "1.2.3.255")
+# $Scope{<subnet>}{name} => string (e.g "Office Subnet #1")
+# $Scope{<subnet>}{comment} => string (e.g. "This is a funny subnet")
+# $Scope{<subnet>}{ranges} => array of paired inclusion IP addresses
+# (e.g. "1.2.3.1 1.2.3.10 1.2.3.100 10.2.3.200
+# says that we have 2 inclusion ranges of
+# 1-10 and 100-200)
+# $Scopes{<subnet>}{routers} => array of IP address strings
+# $Scopes{<subnet>}{dnses} => array of IP address/name string
+# $Scopes{<subnet>}{domain} > string
+# $Scopes{<subnet>}{broadcast} => string
+# $Scopes{<subnet>}{winses} => array of IP addresses/name string
+# $Scopes{<subnet>}{winstype} => integer
+# $Scopes{<subnet>}{leaseduration} => integer
+
+ my ($RegVal, @Subnets, @Router, $SubnetName, $SubnetComment, @SubnetOptions, @SRouter, @SDNSServers, @SDomainname, @SWINSservers, @SNetBIOS, @SLeaseDuration, @SSubnetState, @SExclusionRanges, @SSubnetAddress, @SSubnetMask, @SFirstAddress, $SStartAddress, $SEndAddress, @InclusionRanges, @SBroadcastAddress);
+
+ print "Getting list of subnets\n";
+ if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets", \@Subnets)) {
+ die "Unable to obtain a list of subnets from the server!\n";
+ }
+
+ for (my $i=0; $i<=$#Subnets; $i++) {
+ print "\t Fetching Subnet $Subnets[$i] (",$i+1, "/", $#Subnets+1, "): ";
+
+ print ".";
+ if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges", \@SFirstAddress)) {
+ # Don't know why MS has a tree for this, but as far
+ # as I can tell, only one subtree will ever come out of
+ # this, so I'm skipping the 'for' loop
+
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges\\$SFirstAddress[0]\\StartAddress", \$RegVal)) {
+ $SStartAddress = $RegVal;
+ }
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges\\$SFirstAddress[0]\\EndAddress", \$RegVal)) {
+ $SEndAddress = $RegVal;
+ }
+# print "\n\tInclusion Range: ", Registry::ExtractIp($SStartAddress), " - ", Registry::ExtractIp($SEndAddress),"\n";
+
+ } else {
+ die "\n\n# Error Getting Inclusion Range FirstAddress!!!\n\n";
+ }
+
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\ExcludedIpRanges", \$RegVal)) {
+ @SExclusionRanges = Registry::ExtractExclusionRanges($RegVal);
+
+# for (my $j=2; $j<=$#SExclusionRanges; $j+=2) {
+# if (unpack("L",$SExclusionRanges[$j]) < unpack("L",$SExclusionRanges[$j-2])) {
+# print ("\n******** Subnet exclusion ranges out of order ********\n");
+# }
+# }
+
+ @SExclusionRanges = sort(@SExclusionRanges);
+
+# print "\n\tExclusion Ranges: ";
+# for (my $j=0; $j<=$#SExclusionRanges; $j+=2) {
+# print "\n\t\t",Registry::ExtractIp($SExclusionRanges[$j])," - ",Registry::ExtractIp($SExclusionRanges[$j+1]);
+# }
+
+ }
+ @InclusionRanges = FindInclusionRanges ($SStartAddress, $SEndAddress, @SExclusionRanges);
+
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetName", \$RegVal)) {
+ $SubnetName = $RegVal;
+# print "\n\tSubnetName: $SubnetName";
+ }
+
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetComment", \$RegVal)) {
+ $SubnetComment = $RegVal;
+# print "\n\tSubnetComment: $SubnetComment";
+ }
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetAddress", \$RegVal)) {
+ @SSubnetAddress = Registry::ExtractIp($RegVal);
+# print "\n\tSubnetAddress: $SSubnetAddress[0]";
+ }
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetMask", \$RegVal)) {
+ @SSubnetMask = Registry::ExtractIp($RegVal);
+# print "\n\tSubnetMask: $SSubnetMask[0]";
+ }
+
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetState", \$RegVal)) {
+ @SSubnetState = Registry::ExtractHex ($RegVal);
+# print "\n\tSubnetState = $SSubnetState[0]";
+ }
+
+ $Scopes{$Subnets[$i]}{disable} = hex($SSubnetState[0]) ? 1 : 0;
+ $Scopes{$Subnets[$i]}{mask} = $SSubnetMask[0];
+ $Scopes{$Subnets[$i]}{name} = $SubnetName;
+ $Scopes{$Subnets[$i]}{comment} = $SubnetComment;
+ for (my $r=0; $r<=$#InclusionRanges; $r++) {
+ $Scopes{$Subnets[$i]}{ranges}[$r] = Registry::ExtractIp($InclusionRanges[$r]);
+ }
+
+################## Get scope options
+
+ my (@SubnetOptionsList);
+
+ print "\n\t\tOptions:";
+ if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetOptions", \@SubnetOptionsList)) {
+ die "Unable to get subnet options list for $Subnets[$i]!\n";
+ }
+
+ for (my $j=0; $j<=$#SubnetOptionsList; $j++) {
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetOptions\\$SubnetOptionsList[$j]\\OptionValue", \$RegVal)) {
+ for ($SubnetOptionsList[$j]) {
+ /003/ and do {
+# @SRouter = Registry::ExtractOptionIps($RegVal);
+ $Scopes{$Subnets[$i]}{routers} = [Registry::ExtractOptionIps($RegVal)];
+ last;
+ };
+ /006/ and do {
+ @SDNSServers = Registry::ExtractOptionIps($RegVal);
+ for (my $d=0; $d<=$#SDNSServers; $d++) {
+ my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $SDNSServers[$d])), &AF_INET);
+ $Scopes{$Subnets[$i]}{dnses}[$d] = $ipname ? $ipname : $SDNSServers[$d];
+ }
+ last;
+ };
+ /015/ and do {
+ @SDomainname = Registry::ExtractOptionStrings($RegVal);
+ $Scopes{$Subnets[$i]}{domain} = $SDomainname[0];
+ last;
+ };
+ /028/ and do {
+ @SBroadcastAddress = Registry::ExtractOptionIps($RegVal);
+ $Scopes{$Subnets[$i]}{broadcast} = $SBroadcastAddress[0];
+ last;
+ };
+ /044/ and do {
+ @SWINSservers = Registry::ExtractOptionIps($RegVal);
+ for (my $w=0; $w<=$#SWINSservers; $w++) {
+ my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $SWINSservers[$w])), &AF_INET);
+ $Scopes{$Subnets[$i]}{winses}[$w] = $ipname ? $ipname : $SWINSservers[$w];
+ }
+ last;
+ };
+ /046/ and do {
+ @SNetBIOS = Registry::ExtractOptionHex($RegVal);
+ $Scopes{$Subnets[$i]}{winstype} = hex($SNetBIOS[0]);
+ last;
+ };
+ /051/ and do {
+ @SLeaseDuration = Registry::ExtractOptionHex($RegVal);
+ $Scopes{$Subnets[$i]}{leaseduration} = hex($SLeaseDuration[0]);
+ last;
+ };
+ die "This program does not recognize subnet option \#$SubnetOptionsList[$j] yet!\n"
+ }
+ } else {
+ die "Unable to obtain option SubnetOptionsList[$j] from $Subnets[$i], most likely a registry problem!\n"
+ }
+ }
+ print "\n";
+ }
+
+ return %Scopes;
+}
+
+######################################################################
+sub FindInclusionRanges ($$@) {
+ my ($StartAddress, $EndAddress, @ExclusionRanges) = @_;
+# Purpose: to calculate and return the DHCP inclusion ranges out of
+# data provided by the NT4 DHCP server
+# input: $StartAddress:
+# $EndAddress:
+# @ExclusionRanges
+# output: none
+# return: An arry of IP address pair representing the inclusion ranges
+# in the native registry format.
+#
+
+ my ($SA, $EA, @ER);
+ $SA = unpack("L", $StartAddress);
+ $EA = unpack("L", $EndAddress);
+ @ER = @ExclusionRanges;
+ for (my $i=0; $i<=$#ER; $i++) {
+ $ER[$i] = unpack ("L", $ER[$i]);
+ }
+
+ my @InclusionRanges;
+
+
+ $InclusionRanges[0] = $SA;
+ $InclusionRanges[1] = $EA;
+
+ for (my $i=0; $i<=$#ER; $i+=2) {
+ if ($ER[$i] == $InclusionRanges[$#InclusionRanges-1]) {
+ $InclusionRanges[$#InclusionRanges-1] = $ER[$i+1] + 1;
+ }
+ if ($ER[$i] > $InclusionRanges[$#InclusionRanges-1]) {
+ $InclusionRanges[$#InclusionRanges] = $ER[$i]-1;
+ }
+ if (($ER[$i+1] > $InclusionRanges[$#InclusionRanges]) &&
+ ($ER[$i+1] != $EA)) {
+ $InclusionRanges[$#InclusionRanges+1] = $ER[$i+1] + 1;
+ $InclusionRanges[$#InclusionRanges+1] = $EA;
+ }
+ if ($InclusionRanges[$#InclusionRanges] < $InclusionRanges[$#InclusionRanges-1]) {
+ $#InclusionRanges -= 2;
+ }
+ }
+
+ for (my $i=0; $i<=$#InclusionRanges; $i++) {
+ $InclusionRanges[$i] = pack("L", $InclusionRanges[$i]);
+ # print "Inclusion: ", Registry::ExtractIp($InclusionRanges[$i]), "\n";
+ }
+ return @InclusionRanges;
+}
+
+####################################################################
+sub GetSuperScope ($) {
+ my ($Server) = @_;
+ my (%SuperScopes);
+#
+# purpose: gets the Superscope list from the given server
+# input:
+# $Server: string of the valid IP address or name of the NT4 server
+# ouput: none
+# return:
+# %SuperScopes: hash of array subnets with the following data structure
+# $SuperScopes{<SuperscopeName>} => array of sunbets
+#
+ my (@SuperScopeNames, @SCSubnetList);
+
+ print "Getting Superscope list: ";
+ if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\SuperScope", \@SuperScopeNames)) {
+ for (my $i=0; $i<=$#SuperScopeNames; $i++) {
+ print ".";
+ if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\SuperScope\\$SuperScopeNames[$i]", \@SCSubnetList)) {
+ $SuperScopes{$SuperScopeNames[$i]} = [@SCSubnetList];
+ }
+ }
+ print "\n";
+ }
+
+ return %SuperScopes;
+}
+
+####################################################################
+sub GetGlobalOptions($) {
+ my ($Server) = @_;
+ my (%GlobalOptions);
+# purpose: to return NT4 server's global scope configuration
+# input:
+# $Server: string of the valid IP or name of the NT4 server
+# output: none
+# return:
+# %GlobalOptions: hash of hash of various data types to be returned of the
+# following data structure
+# $GlobalOptions{routers} => array of IP address strings
+# $GlobalOptions{dnses} => array of IP address/name string
+# $GlobalOptions{domain} > string
+# $GlobalOptions{broadcast} => string
+# $GlobalOptions{winses} => array of IP addresses/name string
+# $GlobalOptions{winstype} => integer
+
+ my ($RegVal, @temp, @GlobalOptionValues);
+
+ print "Getting Global Options: ";
+ if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\GlobalOptionValues", \@GlobalOptionValues)) {
+ die "Unable to obtain GlobalOptionValues";
+ }
+
+ for (my $i=0; $i<=$#GlobalOptionValues; $i++) {
+ print ".";
+ if (Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\configuration\\globaloptionvalues\\$GlobalOptionValues[$i]\\optionvalue", \$RegVal)) {
+ die "Unable to retrive global option $GlobalOptionValues[$i]\n";
+ }
+
+
+ for ($GlobalOptionValues[$i]) {
+ /003/ and do {
+ @temp=Registry::ExtractOptionIps($RegVal);
+ $GlobalOptions{routers} = [@temp];
+ last;
+ };
+ /006/ and do {
+ # DNS Servers
+ @temp = Registry::ExtractOptionIps($RegVal);
+ for (my $d=0; $d<=$#temp; $d++) {
+ my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $temp[$d])), &AF_INET);
+ $GlobalOptions{dnses}[$d] = $ipname ? $ipname : $temp[$d];
+ }
+ last;
+ };
+ /015/ and do {
+ # Domain Name
+ @temp = Registry::ExtractOptionStrings($RegVal);
+ $GlobalOptions{domain} = $temp[0];
+ last;
+ };
+ /028/ and do {
+ # broadcast address
+ @temp = Registry::ExtractOptionIps($RegVal);
+ $GlobalOptions{broadcast} = $temp[0];
+ last;
+ };
+ /044/ and do {
+ # WINS Servers
+ @temp = Registry::ExtractOptionIps ($RegVal);
+ $GlobalOptions{winses} = [@temp];
+ for (my $w=0; $w<=$#temp; $w++) {
+ my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $temp[$w])), &AF_INET);
+ $GlobalOptions{winses}[$w] = $ipname ? $ipname : $temp[$w];
+ }
+ last;
+ };
+ /046/ and do {
+ # NETBIOS node type
+ @temp = Registry::ExtractOptionHex($RegVal);
+ $GlobalOptions{winstype} = hex($temp[0]);
+ last;
+ };
+ die "This program does not recgonize global option \#$GlobalOptionValues[$i] yet!\n"
+ }
+ }
+ print "\n";
+
+ return %GlobalOptions;
+}
diff --git a/contrib/ms2isc/readme.txt b/contrib/ms2isc/readme.txt
new file mode 100644
index 00000000..862d0103
--- /dev/null
+++ b/contrib/ms2isc/readme.txt
@@ -0,0 +1,15 @@
+Copyright: please read the top of the source code.
+
+Usage:
+Objective: please read the help screen by executing the program without any
+ parameter.
+
+Revision:
+SMC: Shu-Min Chang
+
+Who When What
+--- ------ --------------------------------------------------------------------
+SMC 021107 Initial release Version 1.0 to ISC DHCP repository
+SMC 030129 Fixed inclusion range calculation by sorting exclusion before
+ passing to FindInclusionRanges
+SMC 030228 release 1.0.1 to ISC DHCP repository
diff --git a/doc/rfc1542.txt b/doc/rfc1542.txt
new file mode 100644
index 00000000..cc03e669
--- /dev/null
+++ b/doc/rfc1542.txt
@@ -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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+
+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]
+ \ No newline at end of file
diff --git a/omapip/iscprint.c b/omapip/iscprint.c
new file mode 100644
index 00000000..dd75e9f5
--- /dev/null
+++ b/omapip/iscprint.c
@@ -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