package Win32::IPConfig; use 5.006; use strict; use warnings; our $VERSION = '0.09'; use Carp; use Win32::TieRegistry qw/:KEY_/; use Win32::IPConfig::Adapter; sub new { my $class = shift; my $host = shift || ""; my $hklm = $Registry->Connect($host, "HKEY_LOCAL_MACHINE", {Access => KEY_READ | KEY_WRITE}) #{Access => KEY_READ}) or return undef; $hklm->SplitMultis(1); # return REG_MULTI_SZ as arrays my $osversion = $hklm->{"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\\\CurrentVersion"} or return undef; my $self = {}; $self->{"osversion"} = $osversion; # Remember the necessary registry keys my $services_key = $hklm->{"SYSTEM\\CurrentControlSet\\Services\\"} or return undef; $self->{"netbt_params_key"} = $services_key->{"Netbt\\Parameters\\"} or return undef; $self->{"tcpip_params_key"} = $services_key->{"Tcpip\\Parameters\\"} or return undef; # Retrieve each network card's config my $networkcards_key = $hklm->{"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards\\"} or return undef; for my $nic ($networkcards_key->SubKeyNames) { if (my $adapter = Win32::IPConfig::Adapter->new($hklm, $nic)) { push @{$self->{"adapters"}}, $adapter; } } bless $self, $class; return $self; } sub get_adapters { return wantarray ? @{$_[0]->{"adapters"}} : $_[0]->{"adapters"}; } sub get_configured_adapters { my @adapters = (); for my $adapter (@{$_[0]->{"adapters"}}) { if (my @ipaddresses = $adapter->get_ipaddresses) { push @adapters, $adapter unless $ipaddresses[0] eq "0.0.0.0"; } } return wantarray ? @adapters : \@adapters; } sub get_osversion { return $_[0]->{"osversion"}; } # Value: Hostname (REG_SZ) # NT: Tcpip\Parameters # 2000+: Tcpip\Parameters # Value: NV Hostname (REG_SZ) # 2000+: Tcpip\Parameters sub get_hostname { my $self = shift; return $self->{"tcpip_params_key"}{"\\Hostname"}; } # Value: Domain (REG_SZ) # NT: Tcpip\Parameters # 2000+: Tcpip\Parameters (primary) # 2000+: Tcpip\Parameters\Interfaces\ (connection-specific) # Value: NV Domain (REG_SZ) # 2000+: Tcpip\Parameters # Value: DhcpDomain (REG_SZ) # NT: Tcpip\Parameters # 2000+: Tcpip\Parameters # 2000+: Tcpip\Parameters\Interfaces\ (connection-specific) # How do you know when to read the Domain value and when to read the DhcpDomain # value? The Domain and DhcpDomain values are attributes of a host, but # the EnableDHCP value is an attribute of an adapter. # On Windows NT 4.0, when I set the adapter to static, the DhcpDomain # setting disappears from the registry to leave only the empty Domain setting. # This also appears to be the case on Windows XP. # What happens when Domain setting is set statically and an adapter card adds a # DhcpDomain setting as well - i.e. when both Domain and DhcpDomain exist? # On Windows NT 4.0, when I set the adapter to dynamic and set a static domain # setting, the static domain setting is returned by ipconfig /all while both # settings exist in the registry. # This suggests: return the Domain setting if it is non-empty. Otherwise # return the DhcpDomain if it is present. Otherwise, return an empty string. sub get_domain { my $self = shift; my $domain = $self->{"tcpip_params_key"}{"\\Domain"}; if (! $domain) { $domain = $self->{"tcpip_params_key"}{"\\DhcpDomain"} || ""; } return $domain; } # Value: SearchList (REG_SZ) (space delimited on NT, comma delimited on 2000+) # NT: Tcpip\Parameters # 2000+: Tcpip\Parameters # The Windows 2000 Advanced TCP/IP Settings dialog gives you the choice of # resolving unqualified names by: # 1. appending primary and connection specific DNS suffixes # 2. appending a user-specified list of DNS suffixes # It appears to choose between each method simply by seeing if # SearchList is set or not: if it is set, use it, otherwise append # the primary and connection specific DNS suffixes. # The Windows NT Microsoft TCP/IP Properties dialog simply allows you # the option of specifying a domain suffix search order. sub get_searchlist { my $self = shift; my @searchlist; if ($self->{"osversion"} >= 5.0) { @searchlist = split /,/, $self->{"tcpip_params_key"}{"\\SearchList"}; } else { @searchlist = split / /, $self->{"tcpip_params_key"}{"\\SearchList"}; } return wantarray ? @searchlist : \@searchlist; } # Value: NodeType (REG_DWORD) # NT: Netbt\Parameters # 2000+: Netbt\Parameters # Value: DhcpNodeType (REG_DWORD) (overidden by NodeType) # NT: Netbt\Parameters # 2000+: Netbt\Parameters # On Windows NT 4.0, if the adapter receives a DhcpNodeType setting from the # DHCP server, the DhcpNodeType setting is present. Otherwise neither the # NodeType nor the DhcpNodeType setting is present. When neither setting is # present, Windows NT 4.0 reports "Node Type = Broadcast". # According to the Q120642 and Q314053 the NodeType setting will override the # DhcpNodeType setting. # This suggests: use the NodeType value if set. Otherwise check for a # DhcpNodeType setting. If there is no DhcpNodeType make a stab at getting the # default NodeType. Check all the adapters present for WINS settings. If there # are any set, then return H-node, else return B-node. sub get_nodetype { my $self = shift; my %nodetypes = (1=>"B-node", 2=>"P-node", 4=>"M-node", 8=>"H-node"); # Windows NT 4.0's ipconfig reports these node types as # Broadcast, Peer-Peer, Mixed, Hybrid my $nodetype; if (my $type = $self->{"netbt_params_key"}{"\\NodeType"}) { $nodetype = hex($type); } elsif ($type = $self->{"netbt_params_key"}{"\\DhcpNodeType"}) { $nodetype = hex($type) } else { my $wins_count = 0; for my $adapter ($self->get_adapters) { my @wins = $adapter->get_wins; $wins_count += @wins; } $nodetype = $wins_count ? 8 : 1; } return $nodetypes{$nodetype}; } # Value: IPEnableRouter (REG_DWORD) # NT: Tcpip\Parameters # 2000+: Tcpip\Parameters sub is_router { my $self = shift; if (my $router = $self->{"tcpip_params_key"}{"\\IPEnableRouter"}) { return hex($router); } else { return 0; # defaults to 0 } } # Value: EnableProxy (REG_DWORD) # NT: Netbt\Parameters # 2000+: Netbt\Parameters sub is_wins_proxy { my $self = shift; if (my $proxy = $self->{"netbt_params_key"}{"\\EnableProxy"}) { return hex($proxy); } else { return 0; # defaults to 0 } } # Value: EnableLMHOSTS (REG_DWORD) # NT: Netbt\Parameters # 2000+: Netbt\Parameters sub is_lmhosts_enabled { my $self = shift; if (my $lmhosts_enabled = $self->{"netbt_params_key"}{"\\EnableLMHOSTS"}) { return hex($lmhosts_enabled); } else { return 1; # defaults to 1 } } # Value: EnableDns (REG_DWORD) # NT: Netbt\Parameters # 2000+: Netbt\Parameters sub is_dns_enabled_for_netbt { my $self = shift; if (my $dns_enabled_for_netbt = $self->{"netbt_params_key"}{"\\EnableDns"}) { return hex($dns_enabled_for_netbt); } else { return 0; # defaults to 0 } } sub get_adapter { my $self = shift; my $adapter_name_or_num = shift; if ($adapter_name_or_num =~ m/^\d+$/) { my $adapter = $self->{"adapters"}[$adapter_name_or_num]; return $adapter; } else { for my $adapter ($self->get_adapters) { if (uc $adapter->get_name eq uc $adapter_name_or_num) { return $adapter; } } return undef; # couldn't find a matching adapter. } } sub dump { my $self = shift; print "hostname=", $self->get_hostname, "\n"; print "domain=", $self->get_domain, "\n"; my @searchlist = $self->get_searchlist; print "searchlist=@searchlist (", scalar @searchlist, ")\n"; print "nodetype=", $self->get_nodetype, "\n"; print "ip router enabled=", $self->is_router ? "Yes":"No", "\n"; print "wins proxy enabled=", $self->is_wins_proxy ? "Yes":"No", "\n"; print "LMHOSTS enabled=", $self->is_lmhosts_enabled ? "Yes":"No", "\n"; print "dns enabled for netbt=", $self->is_dns_enabled_for_netbt ? "Yes":"No", "\n"; my $i = 0; for ($self->get_adapters) { print "\nAdapter ", $i++, ":\n"; $_->dump; } } 1; __END__ =head1 NAME Win32::IPConfig - IP Configuration Settings for Windows NT/2000/XP/2003 =head1 SYNOPSIS use Win32::IPConfig; $host = shift || ""; if ($ipconfig = Win32::IPConfig->new($host)) { print "hostname=", $ipconfig->get_hostname, "\n"; print "domain=", $ipconfig->get_domain, "\n"; my @searchlist = $ipconfig->get_searchlist; print "searchlist=@searchlist (", scalar @searchlist, ")\n"; print "nodetype=", $ipconfig->get_nodetype, "\n"; print "IP routing enabled=", $ipconfig->is_router ? "Yes" : "No", "\n"; print "WINS proxy enabled=", $ipconfig->is_wins_proxy ? "Yes" : "No", "\n"; print "LMHOSTS enabled=", $ipconfig->is_lmhosts_enabled ? "Yes" : "No", "\n"; print "DNS enabled for netbt=", $ipconfig->is_dns_enabled_for_netbt ? "Yes" : "No", "\n"; foreach $adapter ($ipconfig->get_adapters) { print "\nAdapter '", $adapter->get_name, "':\n"; print "Description=", $adapter->get_description, "\n"; print "DHCP enabled=", $adapter->is_dhcp_enabled ? "Yes" : "No", "\n"; @ipaddresses = $adapter->get_ipaddresses; print "IP addresses=@ipaddresses (", scalar @ipaddresses, ")\n"; @subnet_masks = $adapter->get_subnet_masks; print "subnet masks=@subnet_masks (", scalar @subnet_masks, ")\n"; @gateways = $adapter->get_gateways; print "gateways=@gateways (", scalar @gateways, ")\n"; print "domain=", $adapter->get_domain, "\n"; @dns = $adapter->get_dns; print "dns=@dns (", scalar @dns, ")\n"; @wins = $adapter->get_wins; print "wins=@wins (", scalar @wins, ")\n"; } } =head1 DESCRIPTION Win32::IPConfig is a module for retrieving TCP/IP network settings from a Windows NT/2000/XP/2003 host machine. Specify the host and the module will retrieve and collate all the information from the specified machine's registry (using Win32::TieRegistry). For this module to retrieve information from a host machine, you must have read and write access to the registry on that machine. =head1 METHODS =over 4 =item $ipconfig = Win32::IPConfig->new($host); Creates a new Win32::IPConfig object. $host is passed directly to Win32::TieRegistry, and can be a computer name or an IP address. =item $ipconfig->get_hostname Returns a string containing the DNS hostname of the machine. =item $ipconfig->get_domain Returns a string containing the domain name suffix of the machine. For machines running Windows 2000 or later (which can have connection-specific domain name suffixes for each adapter) this is the primary domain name for the machine. =item $ipconfig->get_searchlist Returns a list of domain name suffixes added during DNS name resolution. They are only used if the initial DNS name lookup fails. (Returns a reference to a list in a scalar context.) =item $ipconfig->get_nodetype Returns the NetBIOS over TCP/IP node type of the machine. The four possible node types are: B-node - resolve NetBIOS names by broadcast P-node - resolve NetBIOS names using a WINS server M-node - resolve NetBIOS names by broadcast, then using a WINS server H-node - resolve NetBIOS names using a WINS server, then by broadcast Windows defaults to B-node if no WINS servers are configured, and defaults to H-node if there are. =item $ipconfig->is_router Returns 1 if the host is configured to route packets between its network adapters; returns 0 if it is not. Only applicable to machines with more than one adapter. By default, this is set to 0 (no routing). =item $ipconfig->is_wins_proxy Returns 1 if the host is configured to be a WINS proxy for NetBIOS over TCP/IP name resolution; returns 0 if it is not. By default, this is set to 0 (do not act as a WINS proxy). A WINS proxy will answer broadcast name queries it detects on its subnet. It is only required in networks where there are hosts that cannot be configured to use WINS servers. =item $ipconfig->is_lmhosts_enabled Returns 1 if the host is configured to use the LMHOSTS file for NetBIOS over TCP/IP name resolution; returns 0 if it is not. By default, this is set to 1 (use LMHOSTS). The LMHOSTS file does not exist unless created by an administrator at %SystemRoot%\system32\drivers\etc\LMHOSTS. =item $ipconfig->is_dns_enabled_for_netbt Returns 1 if the host is configured to use DNS for NetBIOS over TCP/IP name resolution; returns 0 if it is not. By default, this is set to 0 (do not use DNS). DNS will only be used for NetBIOS over TCP/IP name resolution after all standard methods have failed (cache, WINS, broadcast, LMHOSTS). This setting has little relevance to Windows 2000 and later, as these operating systems were designed to work without WINS servers and already use DNS (through "direct hosting") to resolve Windows computer names. =item $ipconfig->get_adapters The real business of the module. Returns a list of Win32::IPConfig::Adapter objects. (Returns a reference to a list in a scalar context.) Each adapter object contains the TCP/IP network settings for an individual adapter. See the Win32::IPConfig::Adapter documentation for more information. =item $ipconfig->get_configured_adapters Returns a list of Win32::IPConfig::Adapter objects that have IP addresses configured, whether manually or through DHCP. (Returns a reference to a list in a scalar context.) =item $ipconfig->get_adapter($name_or_num) Returns the Win32::IPConfig::Adapter specified by $name_or_num. If you specify a string (e.g. "Local Area Connection") it will look for an adapter with that name, otherwise it will take the adapter from the list of Win32::IPConfig::Adapter objects with the index value of $name_or_num. Use get_adapter(0) to retrieve the first adapter. You could use the following code to retrieve the adapter named "Local Area Connection" on Windows 2000 (or later) or the first adapter on Windows NT. my $adapter = $ipconfig->get_adapter("Local Area Connection") || $ipconfig->get_adapter(0); =back =head1 EXAMPLES =head2 Displaying the IP Settings for a PC This example is a variation on the code given in the synopsis; it prints the output in a style closer to the ipconfig command. Specify the target computer's name as the first command line parameter. It also uses the get_configured_adapters method to filter out adapters that do not have IP addresses. use Win32::IPConfig; my $host = shift || Win32::NodeName; my $ipconfig = Win32::IPConfig->new($host) or die "Unable to connect to $host\n"; print "Host Name. . . . . . . . . . . . : ", $ipconfig->get_hostname, "\n"; print "Domain Name (Primary). . . . . . : ", $ipconfig->get_domain, "\n"; my @searchlist = $ipconfig->get_searchlist; print "Search List. . . . . . . . . . . : $searchlist[0]\n"; print " $searchlist[$_]\n" for (1..@searchlist-1); print "Node Type . . . . . . . . . . . : ", $ipconfig->get_nodetype, "\n"; print "IP Routing Enabled . . . . . . . : ", $ipconfig->is_router ? "Yes" : "No", "\n"; print "WINS Proxy Enabled . . . . . . . : ", $ipconfig->is_wins_proxy ? "Yes" : "No", "\n"; print "LMHOSTS Enabled. . . . . . . . . : ", $ipconfig->is_lmhosts_enabled ? "Yes" : "No", "\n"; print "DNS Enabled for NetBT. . . . . . : ", $ipconfig->is_dns_enabled_for_netbt ? "Yes" : "No", "\n"; for my $adapter ($ipconfig->get_configured_adapters) { print "\nAdapter '", $adapter->get_name, "':\n\n"; print "DHCP Enabled . . . . . . . . . . : ", $adapter->is_dhcp_enabled ? "Yes" : "No", "\n"; print "Domain Name. . . . . . . . . . . : ", $adapter->get_domain, "\n"; my @ipaddresses = $adapter->get_ipaddresses; my @subnet_masks = $adapter->get_subnet_masks; for (0..@ipaddresses-1) { print "IP Address . . . . . . . . . . . : $ipaddresses[$_]\n"; print "Subnet Mask. . . . . . . . . . . : $subnet_masks[$_]\n"; } my @gateways = $adapter->get_gateways; print "Default Gateway. . . . . . . . . : $gateways[0]\n"; print " $gateways[$_]\n" for (1..@gateways-1); my @dns = $adapter->get_dns; print "DNS Servers. . . . . . . . . . . : $dns[0]\n"; print " $dns[$_]\n" for (1..@dns-1); my @wins = $adapter->get_wins; print "WINS Servers . . . . . . . . . . : $wins[0]\n"; print " $wins[$_]\n" for (1..@wins-1); } =head2 Collecting IP Settings for a list of PCs This example outputs data in CSV format with the hostname and domain followed by the IP configuration settings for the first adapter. (Additional adapters are ignored.) use strict; use Win32::IPConfig; print "hostname,domain,"; print "dhcp?,ip addresses,subnet masks,gateways,dns servers,wins servers\n"; while () { chomp; if (my $ipconfig = Win32::IPConfig->new($_)) { print $ipconfig->get_hostname, ","; print $ipconfig->get_domain, ","; if (my $adapter = $ipconfig->get_adapter(0)) { print $adapter->is_dhcp_enabled ? "Y," : "N,"; my @ipaddresses = $adapter->get_ipaddresses; print "@ipaddresses,"; my @subnet_masks = $adapter->get_subnet_masks; print "@subnet_masks,"; my @gateways = $adapter->get_gateways; print "@gateways,"; my @dns = $adapter->get_dns; print "@dns,"; my @wins = $adapter->get_wins; print "@wins"; } print "\n"; } } __DATA__ HOST1 HOST2 HOST3 =head2 Setting a PC's DNS servers This example demonstrates how you can change the DNS servers set on a remote host. It reads the target machine name and the IP addresses for the DNS servers from the command line. use strict; use Win32::IPConfig; my $host = shift or die "You must specify a host\n"; if (my $ipconfig = Win32::IPConfig->new($host)) { if (my $adapter = $ipconfig->get_adapter("Local Area Connection") || $ipconfig->get_adapter(0)) { if (! $adapter->is_dhcp_enabled) { $adapter->set_dns(@ARGV); my @dns = $adapter->get_dns; if (@dns) { print "Set to @dns\n"; } else { print "Cleared\n"; } } else { warn "Adapter is configured for DHCP\n"; } } else { warn "Could not find an adapter to configure\n"; } } else { warn "Host '$host' does not appear to be up\n"; } =head1 REGISTRY KEYS USED IP configuration information is stored in a number of registry keys under HKLM\SYSTEM\CurrentControlSet\Services. To find adapter-specific configuration information, you need the adapter id, which can be found by examining the list of installed network cards at HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards. Note that on Windows NT the adapter id will look like a service or driver, while on Windows 2000 and later it will be a GUID. There are some variations in where the TCP/IP configuration data is stored. For all operating systems, the main keys are: Tcpip\Parameters Netbt\Parameters Adapter-specific TCP/IP settings are stored in: \Parameters\Tcpip (Windows NT) Tcpip\Parameters\Interfaces\ (Windows 2000 and later) NetBIOS over TCP/IP stores adapter-specific settings in: Netbt\Adapters\ (Windows NT) Netbt\Parameters\Interfaces\Tcpip_ (Windows 2000 and later) =head1 NOTES Windows 2000 and later will use DNS and WINS to resolve Windows computer names, whereas Windows NT will use WINS and only use DNS if configured to do so (see the is_dns_enabled_for_netbt method). For Windows 2000 and later, both the primary and connection-specific domain settings are significant and will be used in this initial name resolution process. The DHCP Server options correspond to the following registry values: 003 Router -> DhcpDefaultGateway 006 DNS Servers -> DhcpNameServer 015 DNS Domain Name -> DhcpDomain 044 WINS/NBNS Servers -> DhcpNameServer/DhcpNameServerList 046 WINS/NBT Node Type -> DhcpNodeType =head1 AUTHOR James Macfarlane, Ejmacfarla@cpan.orgE =head1 SEE ALSO Win32::IPConfig::Adapter Win32::TieRegistry The following Microsoft support articles were helpful: =over 4 =item * Q120642 TCP/IP and NBT Configuration Parameters for Windows 2000 or Windows NT =item * Q314053 TCP/IP and NBT Configuration Parameters for Windows XP =back =cut