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
34use strict;
35use Socket;
36use Getopt::Std;
37use Filehandle;
38use Registry; # Custom Perl Module to make Registry access easier.
39
40my $usage = << 'ENDOFHELP';
41
42Purpose: A Perl Script converting MS NT4 DHCP configuration to ISC DHCP3
43configuration file by reading NT4's registry.
44
45Requires: Registry.pm and ActiveState 5.6.0
46
47Usage: $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
55Essentially the <Srv> needs to be an NT4 (3.x should work but not tested) which
56you should have registry read access to.  You must run this script from a
57Windows machine because of the requirement to access the registry.
58
59The <Pri> is optional parameter for desginating the dynamic DNS update if
60missing then the "zone" section of the declaration will be skipped.  The <Key>
61is needed if you've configured your DNS zone with a key, in addition, you'll
62need to define that key in this DHCP configuration file elsewhere manually,
63read the DHCP Handbook to figure out what you need to define.
64
65The <Fo> specifies the fail-over peer name in the pool section, you'll need to
66define additional detail elsewhere manually, again read the DHCP handbook.
67
68NOTE: 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
87Author: Shu-Min Chang <smchang@yahoo.com>
88
89Copyright: Please read the top of the source code
90
91Acknowledgement:
92  Brian L. King for coding help, Douglas A. Darrah for testing, and James E.
93Pressley for being the DHCP reference book :).
94
95Usage: $ARGV -s <Srv> -o <Out> [-p <Pri> [-k <key>]] [-f <Fo>]
96
97Version: 1.0.1
98
99ENDOFHELP
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######################################################################
166sub 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######################################################################
185sub 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######################################################################
204sub 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######################################################################
228sub 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######################################################################
296sub 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######################################################################
474sub 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####################################################################
525sub 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####################################################################
554sub 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