Home | History | Annotate | Line # | Download | only in ms2isc
ms2isc.pl revision 1.1
      1 #set ts=3

      2 #

      3 # ms2isc.pl

      4 # MS NT4 DHCP to ISC DHCP Configuration Migration Tool

      5 #

      6 # Author: Shu-Min Chang

      7 #

      8 # Copyright(c) 2003 Intel Corporation.  All rights reserved

      9 #

     10 # Redistribution and use in source and binary forms, with or without

     11 # modification, are permitted provided that the following conditions are met:

     12 #

     13 # 1. Redistributions of source code must retain the above copyright notice,

     14 #    this list of conditions and the following disclaimer.

     15 # 2. Redistributions in binary form must reproduce the above copyright notice

     16 #    this list of conditions and the following disclaimer in the documentation

     17 #    and/or other materials provided with the distribution

     18 # 3. Neither the name of Intel Corporation nor the names of its contributors

     19 #    may be used to endorse or promote products derived from this software

     20 #    without specific prior written permission.

     21 #

     22 # THIS SOFTWARE IS PROVIDED BY THE INTEL CORPORATION AND CONTRIBUTORS "AS IS"

     23 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE

     24 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 

     25 # ARE DISCLAIMED.  IN NO EVENT SHALL THE INTEL CORPORATION OR CONTRIBUTORS BE

     26 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL EXEMPLARY, OR 

     27 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO PROCUREMENT OF SUBSTITUE

     28 # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 

     29 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 

     30 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT

     31 # OF THE USE OF THIS SOFTWARE, EVEN IF ADVICED OF THE POSSIBILITY OF SUCH 

     32 # DAMAGE.

     33 
     34 use strict;
     35 use Socket;
     36 use Getopt::Std;
     37 use Filehandle;
     38 use Registry; # Custom Perl Module to make Registry access easier.

     39 
     40 my $usage = << 'ENDOFHELP';
     41 
     42 Purpose: A Perl Script converting MS NT4 DHCP configuration to ISC DHCP3 
     43 configuration file by reading NT4's registry.
     44 
     45 Requires: Registry.pm and ActiveState 5.6.0
     46 
     47 Usage: $ARGV -s <Srv> -o <Out> [-p <Pri> [-k <key>]] [-f <Fo>]
     48 
     49   <Srv>  Server IP or name for NT4 DHCP server to fetch the configuration from.
     50   <Out>  Output filename for the configuration file.
     51   <Pri>  Primary DNS server name for sending the dynamic DNS update to.
     52   <Key>  Key name for use in updating the dynamic DNS zone.
     53   <Fo>   Failover peer name shared with the DHCP partner.
     54 
     55 Essentially the <Srv> needs to be an NT4 (3.x should work but not tested) which
     56 you should have registry read access to.  You must run this script from a 
     57 Windows machine because of the requirement to access the registry.
     58 
     59 The <Pri> is optional parameter for desginating the dynamic DNS update if
     60 missing then the "zone" section of the declaration will be skipped.  The <Key>
     61 is needed if you've configured your DNS zone with a key, in addition, you'll
     62 need to define that key in this DHCP configuration file elsewhere manually,
     63 read the DHCP Handbook to figure out what you need to define.
     64 
     65 The <Fo> specifies the fail-over peer name in the pool section, you'll need to
     66 define additional detail elsewhere manually, again read the DHCP handbook.
     67 
     68 NOTE: the program only knows of the following global and subnet options:
     69         3, 6, 15, 28, 44, and 46
     70 
     71       If it runs into options other than the known ones, it will quit.  You
     72       may fix this by modifying the following procedures:
     73         GetGlobalOptions
     74         GetScopes
     75         PrintSubnetConfig
     76 
     77       In addition, the resulting subnets configuration will have the "deny 
     78       dynamic bootp clients" you should take them out if that's not what you 
     79       want :).
     80 
     81       Finally, as the parameter structures implied, it is assumed that you
     82       want the same zone primary and update key for all zones and that the
     83       same failover is to be applied to all the pools.  Furthermore the
     84       subnet zones are all assumed to be class C delineated, but if you
     85       happend to be delegated at the class B level, this will work fine too.
     86 
     87 Author: Shu-Min Chang <smchang (at] yahoo.com>
     88 
     89 Copyright: Please read the top of the source code
     90 
     91 Acknowledgement:
     92   Brian L. King for coding help, Douglas A. Darrah for testing, and James E.
     93 Pressley for being the DHCP reference book :).
     94 
     95 Usage: $ARGV -s <Srv> -o <Out> [-p <Pri> [-k <key>]] [-f <Fo>]
     96 
     97 Version: 1.0.1
     98 
     99 ENDOFHELP
    100 
    101 ###################### Begin Main Program ####################################
    102 
    103 	my (%opts, %GlobalOptions, %SuperScopes, %Scopes);
    104 
    105 	### Get parameters and make sure that they meet the require/optoinal criteria
    106 	getopts('s:o:p:k:f:', \%opts) or die $usage;
    107 	($opts{s} and $opts{o}) or die $usage;
    108 	if ($opts{k}) { $opts{p} or die $usage; }
    109 	
    110 	### Read all the registry stuff into the memory
    111 	%GlobalOptions = GetGlobalOptions($opts{s});
    112 	%SuperScopes = GetSuperScope($opts{s});
    113 	%Scopes = GetScopes ($opts{s});
    114 
    115 	### Process and print out to the output file
    116 	my ($outfile, $i, $j, @Domains);
    117 
    118 	$outfile = new FileHandle "> $opts{o}";
    119 	if (!defined $outfile) {
    120 		die "Can't open file: $opts{o}: $!";
    121 	}
    122 
    123 	for $i (keys %SuperScopes) {
    124 		print $outfile "\n##############################################################\n";

    125 		my ($Scopename) = $i;
    126 		$Scopename =~ s/ //g;
    127 		print $outfile "shared-network $Scopename {\n";
    128 		foreach $j (@{$SuperScopes{$i}}) {
    129 			PrintSubnetConfig($outfile, \%GlobalOptions, \%{$Scopes{$j}}, $j, "\t", $opts{f});
    130 			InsertIfUnique (\@Domains, $Scopes{$j}{domain}) if exists $Scopes{$j}{domain};
    131 			delete $Scopes{$j};
    132 		}
    133 		print $outfile "}\n";
    134 		if ($opts{p} or $opts{k}) {
    135 			foreach $j (@{$SuperScopes{$i}}) {
    136 				PrintSubnetUpdate($outfile, $j, $opts{p}, $opts{k});
    137 			}
    138 		}
    139 	}
    140 
    141 	for $i (keys %Scopes) {
    142 		print $outfile "\n##############################################################\n";
    143 		PrintSubnetConfig($outfile, \%GlobalOptions, \%{$Scopes{$i}}, $i, "", $opts{f});
    144 		if ($opts{p} or $opts{k}) { PrintSubnetUpdate($outfile, $i, $opts{p}, $opts{k}); }
    145 		InsertIfUnique (\@Domains, $Scopes{$i}{domain}) if exists $Scopes{$i}{domain};
    146 	}
    147 
    148 	if ($opts{p} or $opts{k}) {
    149 		InsertIfUnique (\@Domains, $GlobalOptions{domain}) if exists $GlobalOptions{domain};
    150 		for $i (@Domains) {
    151 			PrintDomainUpdate($outfile, $i, $opts{p}, $opts{k});
    152 		}
    153 	}
    154 
    155 	undef ($outfile);
    156 	print "Done.\n";
    157 	exit();
    158 
    159 ################################## End Main Program ###########################

    160 
    161 
    162 
    163 
    164 
    165 ######################################################################

    166 sub InsertIfUnique ($$) {
    167 	my ($Array, $data) = @_;
    168 # purpose: insert $data into array @{$Array} iff the data is not in there yet

    169 # input:

    170 #   $data: scalar data to be added to the @{$Array} if unique

    171 #   $Array: reference of the Array to compare the uniqueness of the $data

    172 # output:

    173 #   $Array: reference of the array with the resulting array.

    174 # return: none

    175 
    176 	my ($i);
    177 
    178 	for ($i=0; $i<=$#{$Array} && ${$Array}[$i] ne $data; $i++) { }

    179 
    180 	if ($i > $#{$Array}) {

    181 		${$Array}[$i] = $data;
    182 	}
    183 }
    184 ######################################################################

    185 sub PrintDomainUpdate ($$$$) {
    186 	my ($outfile, $Domain, $DDNSServer, $key) = @_;
    187 # purpose: print out the foward domain zone update declaration

    188 # input:

    189 #   $outfile: filehandle of the file to write the output to

    190 #   $Domain: a string representing the forward domain

    191 #   $DDNSServer: a string of the DNS server accepting the DDNS update

    192 #   $key: a string representing the key used to update the zone

    193 # output: none

    194 # return: none

    195 #

    196 
    197 	print $outfile "zone $Domain {\n";
    198 	print $outfile "\tprimary $DDNSServer;\n";
    199 	!$key or print $outfile "\tkey $key;\n";
    200 	print $outfile "}\n";
    201 
    202 }
    203 ######################################################################

    204 sub PrintSubnetUpdate ($$$$) {
    205 	my ($outfile, $Subnet, $DDNSServer, $key) = @_;
    206 # purpose: print out the reverse domain zone update declaration

    207 # input:

    208 #   $outfile: filehandle of the file to write the output to

    209 #   $Subnet: a string representing the subnet in the form 1.2.3.4

    210 #   $DDNSServer: a string of the DNS server accepting the DDNS update

    211 #   $key: a string representing the key used to update the zone

    212 # output: none

    213 # return: none

    214 #

    215 
    216 	my ($Reverse);
    217 
    218 	$_ = join (".", reverse(split(/\./, $Subnet)));
    219 	m/\d*\.(.*)/;
    220 	$Reverse = $1;
    221 	print $outfile "zone $Reverse.in-addr.arpa. {\n";
    222 	print $outfile "\tprimary $DDNSServer;\n";
    223 	!$key or print $outfile "\tkey $key;\n";
    224 	print $outfile "}\n";
    225 
    226 }
    227 ######################################################################

    228 sub PrintSubnetConfig ($$$$$$) {
    229 	my ($outfile, $GlobalOptions, $Scope, $Subnet, $prefix, $failover) = @_;
    230 # purpose: print out the effective scope configuration for one subnet as

    231 #          derived from the global and scope options.

    232 # input:

    233 #   $outfile: filehandle of the file to write the output to

    234 #   $GlobalOptions: refernce to the hashed variable from GetGlobalOptions

    235 #   $Scopes: reference to the hashed variable of the subnet in interest

    236 #   $Subnet: string variable of the subnet being processed

    237 #   $prefix: string to be printed before each line (designed for tab)

    238 #   $failover: string to be used for the "failover peer" line

    239 # output: none

    240 # return: none

    241 #

    242 	my ($pound) = ( ${$Scope}{disable}? "#".$prefix : $prefix);
    243 	print $outfile $pound, "subnet $Subnet netmask ${$Scope}{mask} {\n";
    244 	print $outfile "$prefix# Name: ${$Scope}{name}\n";
    245 	print $outfile "$prefix# Comment: ${$Scope}{comment}\n";
    246 	if (exists ${$Scope}{routers}) {
    247 		print $outfile $pound, "\toption routers @{${$Scope}{routers}};\n";
    248 	} elsif (exists ${$GlobalOptions}{routers}) {
    249 		print $outfile $pound, "\toption routers @{${$GlobalOptions}{routers}};\t# NOTE: obtained from global option, bad practice detected\n";
    250 	} else {
    251 		print $outfile "### WARNING: No router was found for this subnet!!! ##########\n";
    252 	}
    253 	
    254 	if (exists ${$Scope}{dnses}) {
    255 		print $outfile $pound, "\toption domain-name-servers ", join(",", @{${$Scope}{dnses}}), ";\n";
    256 	} elsif (exists ${$GlobalOptions}{dnses}) {
    257 		print $outfile $pound, "\toption domain-name-servers ", join(",", @{${$GlobalOptions}{dnses}}), ";\n";
    258 	}
    259 
    260 	if (exists ${$Scope}{domain}) {
    261 		print $outfile $pound, "\toption domain-name \"${$Scope}{domain}\";\n";
    262 	} elsif (exists ${$GlobalOptions}{domain}) {
    263 		print $outfile $pound, "\toption domain-name \"${$GlobalOptions}{domain}\";\n";
    264 	}
    265 
    266 	if (exists ${$Scope}{broadcast}) {
    267 		print $outfile $pound, "\toption broadcast-address ${$Scope}{broadcast};\n";
    268 	} elsif (exists ${$GlobalOptions}{broadcast}) {
    269 		print $outfile $pound, "\toption broadcast-address ${$GlobalOptions}{broadcast};\n";
    270 	}
    271 
    272 	if (exists ${$Scope}{winses}) {
    273 		print $outfile $pound, "\toption netbios-name-servers ", join(",", @{${$Scope}{winses}}), ";\n";
    274 	} elsif (exists ${$GlobalOptions}{winses}) {
    275 		print $outfile $pound, "\toption netbios-name-servers ", join(",", @{${$GlobalOptions}{winses}}), ";\n";
    276 	}
    277 
    278 	if (exists ${$Scope}{winstype}) {
    279 		print $outfile $pound, "\toption netbios-node-type ${$Scope}{winstype};\n";
    280 	} elsif (exists ${$GlobalOptions}{winstype}) {
    281 		print $outfile $pound, "\toption netbios-node-type ${$GlobalOptions}{winstype};\n"
    282 	}
    283 
    284 	print $outfile $pound, "\tdefault-lease-time ${$Scope}{leaseduration};\n";
    285 	print $outfile $pound, "\tpool {\n";
    286 	for (my $r=0; $r<=$#{${$Scope}{ranges}}; $r+=2) {

    287 		print $outfile $pound, "\t\trange ${$Scope}{ranges}[$r] ${$Scope}{ranges}[$r+1];\n";
    288 	}
    289 	!$failover or print $outfile $pound, "\t\tfailover peer \"$failover\";\n";
    290 	print $outfile $pound, "\t\tdeny dynamic bootp clients;\n";
    291 	print $outfile $pound, "\t}\n";
    292 	print $outfile $pound, "}\n";
    293 }
    294 
    295 ######################################################################

    296 sub GetScopes ($) {
    297 	my ($Server) = @_;
    298 	my (%Scopes);
    299 # purpose: to return NT4 server's scope configuration

    300 # input:

    301 #   $Server: string of the valid IP or name of the NT4 server

    302 # output: none

    303 # return:

    304 #   %Scope: hash of hash of hash of various data types to be returned of the 

    305 #           following data structure

    306 #     $Scope{<subnet>}{disable} => boolean

    307 #     $Scope{<subnet>}{mask} => string (e.g. "1.2.3.255")

    308 #     $Scope{<subnet>}{name} => string (e.g "Office Subnet #1")

    309 #     $Scope{<subnet>}{comment} => string (e.g. "This is a funny subnet")

    310 #     $Scope{<subnet>}{ranges} => array of paired inclusion IP addresses

    311 #                                 (e.g. "1.2.3.1 1.2.3.10 1.2.3.100 10.2.3.200

    312 #                                  says that we have 2 inclusion ranges of

    313 #                                  1-10 and 100-200)

    314 #     $Scopes{<subnet>}{routers} => array of IP address strings

    315 #     $Scopes{<subnet>}{dnses} => array of IP address/name string

    316 #     $Scopes{<subnet>}{domain} > string

    317 #     $Scopes{<subnet>}{broadcast} => string

    318 #     $Scopes{<subnet>}{winses} => array of IP addresses/name string

    319 #     $Scopes{<subnet>}{winstype} => integer

    320 #     $Scopes{<subnet>}{leaseduration} => integer

    321 
    322 	my ($RegVal, @Subnets, @Router, $SubnetName, $SubnetComment, @SubnetOptions, @SRouter, @SDNSServers, @SDomainname, @SWINSservers, @SNetBIOS, @SLeaseDuration, @SSubnetState, @SExclusionRanges, @SSubnetAddress, @SSubnetMask, @SFirstAddress, $SStartAddress, $SEndAddress, @InclusionRanges, @SBroadcastAddress);
    323 
    324 	print "Getting list of subnets\n";
    325 	if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets", \@Subnets)) {
    326 		die "Unable to obtain a list of subnets from the server!\n";
    327 	}
    328 
    329 	for (my $i=0; $i<=$#Subnets; $i++) {

    330 		print "\t Fetching Subnet $Subnets[$i] (",$i+1, "/", $#Subnets+1, "): ";

    331 
    332 		print ".";
    333 		if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges", \@SFirstAddress)) {
    334 			# Don't know why MS has a tree for this, but as far

    335 			# as I can tell, only one subtree will ever come out of

    336 			# this, so I'm skipping the 'for' loop

    337 		
    338 			print ".";
    339 			if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges\\$SFirstAddress[0]\\StartAddress", \$RegVal)) {
    340 				$SStartAddress = $RegVal;
    341 			}
    342 			print ".";
    343 			if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges\\$SFirstAddress[0]\\EndAddress", \$RegVal)) {
    344 				$SEndAddress = $RegVal;
    345 			}
    346 # print "\n\tInclusion Range: ", Registry::ExtractIp($SStartAddress), " - ", Registry::ExtractIp($SEndAddress),"\n";

    347 	
    348 		} else {
    349 			die "\n\n# Error Getting Inclusion Range FirstAddress!!!\n\n";
    350 		}
    351 
    352 		if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\ExcludedIpRanges", \$RegVal)) {
    353 			@SExclusionRanges = Registry::ExtractExclusionRanges($RegVal);
    354 
    355 #			for (my $j=2; $j<=$#SExclusionRanges; $j+=2) {

    356 #				if (unpack("L",$SExclusionRanges[$j]) < unpack("L",$SExclusionRanges[$j-2])) {

    357 #					print ("\n******** Subnet exclusion ranges out of order ********\n");

    358 #				}

    359 #			}

    360 
    361 			@SExclusionRanges = sort(@SExclusionRanges);
    362 
    363 #		print "\n\tExclusion Ranges: ";

    364 #		for (my $j=0; $j<=$#SExclusionRanges; $j+=2) {

    365 #			print "\n\t\t",Registry::ExtractIp($SExclusionRanges[$j])," - ",Registry::ExtractIp($SExclusionRanges[$j+1]);

    366 #		}

    367 
    368 		}
    369 		@InclusionRanges = FindInclusionRanges ($SStartAddress, $SEndAddress, @SExclusionRanges);
    370 
    371 		print ".";
    372 		if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetName", \$RegVal)) {
    373 			$SubnetName = $RegVal;
    374 #		print "\n\tSubnetName: $SubnetName";

    375 		}
    376 
    377 		print ".";
    378 		if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetComment", \$RegVal)) {
    379 			$SubnetComment = $RegVal;
    380 #		print "\n\tSubnetComment: $SubnetComment";

    381 		}
    382 		print ".";
    383 		if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetAddress", \$RegVal)) {
    384 			@SSubnetAddress = Registry::ExtractIp($RegVal);
    385 #		print "\n\tSubnetAddress: $SSubnetAddress[0]";

    386 		}
    387 		print ".";
    388 		if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetMask", \$RegVal)) {
    389 			@SSubnetMask = Registry::ExtractIp($RegVal);
    390 #		print "\n\tSubnetMask: $SSubnetMask[0]";

    391 		}
    392 
    393 		print ".";
    394 		if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetState", \$RegVal)) {
    395 			@SSubnetState = Registry::ExtractHex ($RegVal);
    396 #		print "\n\tSubnetState = $SSubnetState[0]";

    397 		}
    398 
    399 		$Scopes{$Subnets[$i]}{disable} = hex($SSubnetState[0]) ? 1 : 0;
    400 		$Scopes{$Subnets[$i]}{mask} = $SSubnetMask[0];
    401 		$Scopes{$Subnets[$i]}{name} = $SubnetName;
    402 		$Scopes{$Subnets[$i]}{comment} = $SubnetComment;
    403 		for (my $r=0; $r<=$#InclusionRanges; $r++) {

    404 			$Scopes{$Subnets[$i]}{ranges}[$r] = Registry::ExtractIp($InclusionRanges[$r]);
    405 		}
    406 
    407 ################## Get scope options

    408 
    409 		my (@SubnetOptionsList);
    410 
    411 		print "\n\t\tOptions:";
    412 		if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetOptions", \@SubnetOptionsList)) {
    413 			die "Unable to get subnet options list for $Subnets[$i]!\n";
    414 		}
    415 
    416 		for (my $j=0; $j<=$#SubnetOptionsList; $j++) {

    417 			print ".";
    418 			if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetOptions\\$SubnetOptionsList[$j]\\OptionValue", \$RegVal)) {
    419 				for ($SubnetOptionsList[$j]) {
    420 					/003/ and do {
    421 #						@SRouter = Registry::ExtractOptionIps($RegVal);

    422 						$Scopes{$Subnets[$i]}{routers} = [Registry::ExtractOptionIps($RegVal)];
    423 						last;
    424 					};
    425 					/006/ and do {
    426 						@SDNSServers = Registry::ExtractOptionIps($RegVal);
    427 						for (my $d=0; $d<=$#SDNSServers; $d++) {

    428 							my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $SDNSServers[$d])), &AF_INET);
    429 							$Scopes{$Subnets[$i]}{dnses}[$d] = $ipname ? $ipname : $SDNSServers[$d];
    430 		}
    431 						last;
    432 					};
    433 					/015/ and do { 
    434 						@SDomainname = Registry::ExtractOptionStrings($RegVal);
    435 						$Scopes{$Subnets[$i]}{domain} = $SDomainname[0];
    436 						last;
    437 					};
    438 					/028/ and do {
    439 						@SBroadcastAddress = Registry::ExtractOptionIps($RegVal);
    440 						$Scopes{$Subnets[$i]}{broadcast} = $SBroadcastAddress[0];
    441 						last;
    442 					};
    443 					/044/ and do {
    444 						@SWINSservers = Registry::ExtractOptionIps($RegVal);
    445 						for (my $w=0; $w<=$#SWINSservers; $w++) {

    446 							my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $SWINSservers[$w])), &AF_INET);
    447 							$Scopes{$Subnets[$i]}{winses}[$w] = $ipname ? $ipname : $SWINSservers[$w];
    448 						}
    449 						last;
    450 					};
    451 					/046/ and do {
    452 						@SNetBIOS = Registry::ExtractOptionHex($RegVal);
    453 						$Scopes{$Subnets[$i]}{winstype} = hex($SNetBIOS[0]);
    454 						last;
    455 					};
    456 					/051/ and do {
    457 						@SLeaseDuration = Registry::ExtractOptionHex($RegVal);
    458 						$Scopes{$Subnets[$i]}{leaseduration} = hex($SLeaseDuration[0]);
    459 						last;
    460 					};
    461 					die "This program does not recognize subnet option \#$SubnetOptionsList[$j] yet!\n"
    462 				}
    463 			} else {
    464 					die "Unable to obtain option SubnetOptionsList[$j] from $Subnets[$i], most likely a registry problem!\n"
    465 			}
    466 		}
    467 		print "\n";
    468 	}
    469 
    470 	return %Scopes;
    471 }
    472 
    473 ######################################################################

    474 sub FindInclusionRanges ($$@) {
    475 	my ($StartAddress, $EndAddress, @ExclusionRanges) = @_;
    476 # Purpose: to calculate and return the DHCP inclusion ranges out of

    477 #          data provided by the NT4 DHCP server

    478 # input:	$StartAddress:

    479 #        $EndAddress:	

    480 #        @ExclusionRanges

    481 # output: none

    482 # return: An arry of IP address pair representing the inclusion ranges

    483 #         in the native registry format.

    484 #

    485 
    486 	my ($SA, $EA, @ER);
    487 	$SA = unpack("L", $StartAddress);
    488 	$EA = unpack("L", $EndAddress);
    489 	@ER = @ExclusionRanges;
    490 	for (my $i=0; $i<=$#ER; $i++) {

    491 		$ER[$i] = unpack ("L", $ER[$i]);
    492 	}
    493 
    494 	my @InclusionRanges;
    495 
    496 
    497 	$InclusionRanges[0] = $SA;
    498 	$InclusionRanges[1] = $EA;
    499 
    500 	for (my $i=0; $i<=$#ER; $i+=2) {

    501 		if ($ER[$i] == $InclusionRanges[$#InclusionRanges-1]) {

    502 			$InclusionRanges[$#InclusionRanges-1] = $ER[$i+1] + 1;

    503 		}
    504 		if ($ER[$i] > $InclusionRanges[$#InclusionRanges-1]) {

    505 			$InclusionRanges[$#InclusionRanges] = $ER[$i]-1;

    506 		}
    507 		if (($ER[$i+1] > $InclusionRanges[$#InclusionRanges]) && 

    508 		    ($ER[$i+1] != $EA)) {
    509 			$InclusionRanges[$#InclusionRanges+1] = $ER[$i+1] + 1;

    510 			$InclusionRanges[$#InclusionRanges+1] = $EA;

    511 		}
    512 		if ($InclusionRanges[$#InclusionRanges] < $InclusionRanges[$#InclusionRanges-1]) {

    513 			$#InclusionRanges -= 2;

    514 		}
    515 	}
    516 
    517 	for (my $i=0; $i<=$#InclusionRanges; $i++) {

    518 		$InclusionRanges[$i] = pack("L", $InclusionRanges[$i]);
    519 	#	print "Inclusion: ", Registry::ExtractIp($InclusionRanges[$i]), "\n";

    520 	}
    521 	return @InclusionRanges;
    522 }
    523 
    524 ####################################################################

    525 sub GetSuperScope ($) {
    526 	my ($Server) = @_;
    527 	my (%SuperScopes);
    528 #

    529 # purpose: gets the Superscope list from the given server

    530 # input:

    531 #   $Server:  string of the valid IP address or name of the NT4 server

    532 # ouput: none

    533 # return:

    534 #   %SuperScopes: hash of array subnets with the following data structure

    535 #          $SuperScopes{<SuperscopeName>} => array of sunbets

    536 #

    537 	my (@SuperScopeNames, @SCSubnetList);
    538 
    539 	print "Getting Superscope list: ";
    540 	if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\SuperScope", \@SuperScopeNames)) {
    541 		for (my $i=0; $i<=$#SuperScopeNames; $i++) {

    542 			print ".";
    543 			if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\SuperScope\\$SuperScopeNames[$i]", \@SCSubnetList)) {
    544 				$SuperScopes{$SuperScopeNames[$i]} = [@SCSubnetList];
    545 			}
    546 		}
    547 		print "\n";
    548 	}
    549 
    550 	return %SuperScopes;
    551 }
    552 
    553 ####################################################################

    554 sub GetGlobalOptions($) {
    555 	my ($Server) = @_;
    556 	my (%GlobalOptions);
    557 # purpose: to return NT4 server's global scope configuration

    558 # input:

    559 #   $Server: string of the valid IP or name of the NT4 server

    560 # output: none

    561 # return:

    562 #   %GlobalOptions: hash of hash of various data types to be returned of the 

    563 #           following data structure

    564 #     $GlobalOptions{routers} => array of IP address strings

    565 #     $GlobalOptions{dnses} => array of IP address/name string

    566 #     $GlobalOptions{domain} > string

    567 #     $GlobalOptions{broadcast} => string

    568 #     $GlobalOptions{winses} => array of IP addresses/name string

    569 #     $GlobalOptions{winstype} => integer

    570 
    571 	my ($RegVal, @temp, @GlobalOptionValues);
    572 
    573 	print "Getting Global Options: ";
    574 	if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\GlobalOptionValues", \@GlobalOptionValues)) { 
    575 		die "Unable to obtain GlobalOptionValues"; 
    576 	}
    577 	
    578 	for (my $i=0; $i<=$#GlobalOptionValues; $i++) {

    579 		print ".";
    580 		if (Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\configuration\\globaloptionvalues\\$GlobalOptionValues[$i]\\optionvalue", \$RegVal)) { 
    581 			die "Unable to retrive global option $GlobalOptionValues[$i]\n";
    582 		}
    583 	
    584 	
    585 		for ($GlobalOptionValues[$i]) {
    586 			/003/ and do {
    587 				@temp=Registry::ExtractOptionIps($RegVal);
    588 				$GlobalOptions{routers} = [@temp];
    589 				last;
    590 			};
    591 			/006/ and do {
    592 				# DNS Servers

    593 				@temp = Registry::ExtractOptionIps($RegVal);
    594 				for (my $d=0; $d<=$#temp; $d++) {

    595 					my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $temp[$d])), &AF_INET);
    596 					$GlobalOptions{dnses}[$d] = $ipname ? $ipname : $temp[$d];
    597 				}
    598 				last;
    599 			};
    600 			/015/ and do { 
    601 				# Domain Name

    602 				@temp = Registry::ExtractOptionStrings($RegVal);
    603 				$GlobalOptions{domain} = $temp[0];
    604 				last;
    605 			};
    606 			/028/ and do { 
    607 				# broadcast address

    608 				@temp = Registry::ExtractOptionIps($RegVal);
    609 				$GlobalOptions{broadcast} = $temp[0];
    610 				last;
    611 			};
    612 			/044/ and do {
    613 				# WINS Servers

    614 				@temp = Registry::ExtractOptionIps ($RegVal);
    615 				$GlobalOptions{winses} = [@temp];
    616 				for (my $w=0; $w<=$#temp; $w++) {

    617 					my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $temp[$w])), &AF_INET);
    618 					$GlobalOptions{winses}[$w] = $ipname ? $ipname : $temp[$w];
    619 				}
    620 				last;
    621 			};
    622 			/046/ and do {
    623 				# NETBIOS node type

    624 				@temp = Registry::ExtractOptionHex($RegVal);
    625 				$GlobalOptions{winstype} = hex($temp[0]);
    626 				last;
    627 			};
    628 			die "This program does not recgonize global option \#$GlobalOptionValues[$i] yet!\n"
    629 		}
    630 	}
    631 	print "\n";
    632 
    633 	return %GlobalOptions;
    634 }
    635