2 # Networking functions for cygwin
5 # * detect when netsh isn't available
9 my $logfile = "/dev/null";
10 #my $logfile = "/tmp/debugwb";
12 #define variables that modify the behavior of the .cgi scripts
13 #that are different than any other OS.
14 $noos_support_add_ifcs = 1; #Windows doesn't supporting adding interfaces
15 $noos_support_delete_ifcs = 1; #Windows doesn't supporting deleting interfaces
16 $always_apply_ifcs = 1; #Changes made to interfaces are always applied
17 $routes_active_now = 1; #Changes made to routes are always applied
18 #Note: some changes Windows requires a reboot, some don't.
19 #TODO2: determine which changes require a reboot.
22 # Returns a list of currently ifconfig'd interfaces
23 # ifc keys: 'name','fullname','virtual','address','netmask','broadcast',
24 # 'ether','mtu','up','edit','index','dhcp'
27 local(@rv, @lines, $line);
28 &open_execute_command(IFC, "ipconfig /all", 1, 1);
34 #Need to get the list of boottime interfaces, because ipconfig /all
35 #doesn't return ipaddr if cable is disconnected
36 my @bootifs = boot_interfaces();
38 foreach $line (@lines) {
39 if ($line =~ /Ethernet adapter (.*):/) {
41 if (defined($ifc{'name'})) {
42 #save the previous one
43 $ifc{'index'} = scalar(@rv);
44 local ($a1, $a2, $a3, $a4) = ($1, $2, $3, $4);
45 if ($ifc{'address'}) {
46 $ifc{'netmask'} =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/;
47 $ifc{'broadcast'} = sprintf("%d.%d.%d.%d",
48 ($a1 | ~int($1))&0xff,
49 ($a2 | ~int($2))&0xff,
50 ($a3 | ~int($3))&0xff,
51 ($a4 | ~int($4))&0xff);
57 $ifc{'name'} = $ifc{'fullname'} = $name;
59 elsif ($line =~ /Media State.*: (.*)/) {
60 $ifc{'up'} = ($1 !~ /Disconnected/);
62 if ($_->{'name'} eq $ifc{'name'}) {
63 $ifc{'dhcp'} = $_->{'dhcp'};
64 $ifc{'address'} = $_->{'address'};
65 $ifc{'netmask'} = $_->{'netmask'};
69 elsif ($line =~ /Description.*: (.*) \#(\d+)\s*$/) {
73 elsif ($line =~ /Description.*: (.*)$/) {
78 elsif ($line =~ /Physical Address.*: (.+)$/) {
80 $ifc{'ether'} =~ s/-/:/g;
82 elsif ($line =~ /IP Address.*: (.+)$/) {
84 $ifc{'up'} = 1 if ! defined $ifc{'up'};
86 elsif ($line =~ /Subnet Mask.*: (.+)$/) {
89 elsif ($line =~ /Default Gateway.*: (.+)$/) {
90 #this is used for the router subroutines below
93 elsif ($line =~ /DHCP Enabled.*: (.+)$/) {
94 $ifc{'dhcp'} = ($1 =~ /Yes/);
97 if (defined($ifc{'name'})) {
99 $ifc{'index'} = scalar(@rv);
100 local ($a1, $a2, $a3, $a4) = ($1, $2, $3, $4);
101 if ($ifc{'address'}) {
102 $ifc{'netmask'} =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/;
103 $ifc{'broadcast'} = sprintf("%d.%d.%d.%d",
104 ($a1 | ~int($1))&0xff,
105 ($a2 | ~int($2))&0xff,
106 ($a3 | ~int($3))&0xff,
107 ($a4 | ~int($4))&0xff);
115 # activate_interface(&details)
116 # Create or modify an interface
117 sub activate_interface
120 #Windows doesn't support adding or removing interfaces
123 # apply_interface(&details)
124 # Save changes to an interface active now
130 # deactivate_interface(&details)
131 # Deactive an interface
132 sub deactivate_interface
134 #TODO2: determine how to deactivate an interface
138 # Returns a list of interfaces brought up at boot time
142 #It doesn't seem to really help to display the loopback since
143 #there's no mechanism in Windows to edit it.
144 # push(@rv, { 'name' => 'lo0',
145 # 'fullname' => 'lo0',
146 # 'address' => '127.0.0.1',
147 # 'netmask' => '255.0.0.0',
151 &open_execute_command(IFC, "netsh interface ip dump", 1);
158 foreach $l (@lines) {
159 #TODO2: handle this message:
160 #"Cannot access configuration.
161 # Connection UI or someone else is accessing it."
162 if ($l =~ /^set address name = "(.*)" source = dhcp/) {
164 $ifc{'fullname'} = $ifc{'name'} = $1;
165 $ifc{'index'} = scalar(@rv);
170 } elsif ($l =~ /^set address name = "(.*)" source = static addr = ([\d\.]+) mask = ([\d\.]+)/) {
172 $ifc{'fullname'} = $ifc{'name'} = $1;
173 $ifc{'address'} = $2;
174 $ifc{'netmask'} = $3;
175 $ifc{'index'} = scalar(@rv);
177 $ifc{'address'} =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/;
178 local ($a1, $a2, $a3, $a4) = ($1, $2, $3, $4);
179 $ifc{'netmask'} =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/;
180 $ifc{'broadcast'} = sprintf("%d.%d.%d.%d",
181 ($a1 | ~int($1))&0xff,
182 ($a2 | ~int($2))&0xff,
183 ($a3 | ~int($3))&0xff,
184 ($a4 | ~int($4))&0xff);
188 } elsif ($l =~ /^set address name = "(.*)" gateway = ([\d\.]+) gwmetric = (\d)/) {
190 if ($_->{'name'} eq $1) {
191 $_->{'gateway'} = $2;
192 $_->{'gwmetric'} = $3;
200 # save_interface(&details)
201 # Create or update a boot-time interface
205 my $cmd = "netsh interface ip set address name = \"" .
206 "$ifc->{'name'}\" source = ";
207 if ($ifc->{'dhcp'}) {
210 $cmd .= "static addr = $ifc->{'address'} mask = $ifc->{'netmask'}";
212 system_logged("$cmd >$logfile 2>&1");
215 # delete_interface(&details)
216 # Delete a boot-time interface
219 #Windows doesn't support adding or removing interfaces
223 # Returns a human-readable interface type name
227 #return "Fast Ethernet" if
228 #return "Token Ring" if
230 return "Loopback" if $_[0] =~ /^lo0$/;
234 # iface_hardware(name)
235 # Does some interface have an editable hardware address
239 return $_[0] !~ /^(lo\d)$/;
243 # Can some boot-time interface parameter be edited?
246 return $_[0] =~ /^(dhcp|netmask)$/;
249 # valid_boot_address(address)
250 # Is some address valid for a bootup interface
251 sub valid_boot_address
253 return &to_ipaddress($_[0]) ? 1 : 0;
257 # Returns a hashtable containing keys nameserver, domain, order
261 my $dns = {'domain' => []};
263 if (&open_execute_command(CMD, "ipconfig /all", 1)) {
264 my $doing_domain = 0;
268 if (/(Ethernet adapter|:)/) {
270 } elsif (/^\s*([^:]+\.[^:]+)$/) {
271 push(@{$dns->{"domain"}}, $1);
274 if (/Primary DNS Suffix.*: (.*)/) {
275 push(@{$dns->{"domain"}}, $1);
276 } elsif (/DNS Suffix Search List.*: (.*)/) {
278 push(@{$dns->{"domain"}}, $1);
279 } elsif (/^Ethernet adapter (.*):/) {
280 $dns->{"name"}[$i++] = $1;
285 if (&open_execute_command(CMD, "netsh interface ip show dns", 1)) {
286 my $doing_nameserver = 0;
288 my $key = "nameserver";
291 if ($doing_nameserver) {
292 if (/(Configuration for interface|:)/) {
293 $doing_nameserver = 0;
294 } elsif (/^\s*([\d\.]+)/) {
295 push(@{$dns->{$key}}, $1);
298 if (/Configuration for interface "(.*)"/) {
299 $dns->{'name'}[++$i] = $1;
301 $key .= $i if $i > 0;
302 } elsif (/Statically Configured DNS Servers:\s*([\d\.]+)/) {
303 push(@{$dns->{$key}}, $1);
304 $doing_nameserver = 1;
312 # save_dns_config(&config)
313 # Configures the DNS settings
317 for ($i=0; $i < @{$dns->{'name'}}; $i++) {
318 my $key = "nameserver";
319 $key .= $i if $i > 0;
320 if (@{$dns->{$key}}) {
321 my $cmd_fmt = "netsh interface ip %s dns name = \"" .
322 $dns->{'name'}[$i] . "\"%s addr = %s";
323 my $addr = pop(@{$dns->{$key}});
324 my $cmd = sprintf($cmd_fmt, "set", " source = static", $addr);
325 &system_logged("$cmd >$logfile 2>&1");
326 #add the new ones (any old list of adds was erased by the set cmd)
327 foreach (@{$dns->{$key}}) {
328 $cmd = sprintf($cmd_fmt, "add", "", $_);
329 &system_logged("$cmd >$logfile 2>&1");
332 #set it to be obtained automatically
333 my $cmd = "netsh interface ip set dns name = \"" .
334 $dns->{'name'}[$i] . "\" source = dhcp";
335 &system_logged("$cmd >$logfile 2>&1");
336 #any old list of adds was erased by the set cmd
339 #TODO: support saving the domain list
340 #if ($_[0]->{'domain'}) {
343 $max_dns_servers = 16; #more is possible, but this is realistic
346 # Returns HTML for selecting the name resolution order
353 # Parses the form created by order_input()
362 return &get_system_hostname();
365 # save_hostname(name)
368 &system_logged("hostname $_[0] >/dev/null 2>&1");
369 undef(@main::get_system_hostname); # clear cache
375 #TODO: determine how to get
379 # save_domainname(domain)
382 #TODO: determine how to set
385 sub routing_config_files
387 return map { $_->{'file'} } &boot_interfaces();
392 # show default router(s) input
393 my @if = boot_interfaces();
396 next if $_->{'address'} eq "127.0.0.1";
397 my $none_or_dhcp = defined($ifc{'gateway'}) ? 0 : 1;
398 my $desc = $_->{'name'} . ($_->{'dhcp'}? "" : " ($_->{'address'})");
399 print &ui_table_row("$desc $text{'routes_default'}",
400 &ui_radio("gateway${i}_def", $none_or_dhcp,
401 [ [ 1, $text{'routes_none'} ],
402 [ 0, $text{'routes_gateway'}." ".
403 &ui_textbox("gateway$i", $_->{'gateway'}, 15)." ".
404 $text{'routes_gwmetric'}." ".
405 &ui_textbox("gwmetric$i", $_->{'gwmetric'}, 4) ] ]).
406 &ui_hidden("ifname${i}", $_->{'name'}));
414 my @if = boot_interfaces();
415 while (defined($in{"gateway${i}_def"})) {
416 my $name = $in{"ifname$i"};
417 my $gateway = $in{"gateway$i"};
418 my $gwmetric = $in{"gwmetric$i"};
420 if ($_->{'name'} eq $name) {
421 if (! $in{"gateway${i}_def"}) {
422 if ($gateway != $_->{'gateway'} ||
423 $gwmetric != $_->{'gwmetric'}) {
424 &check_ipaddress($gateway) ||
425 &error(&text('routes_egateway', $gateway));
426 my $cmd = "netsh interface ip set address name = \"" .
427 $_->{'name'} . "\" gateway = $gateway " .
428 "gwmetric = $gwmetric";
429 system_logged("$cmd > $logfile 2>&1");
432 if (defined($_->{'gateway'})) {
433 my $cmd = "netsh interface ip delete address name = \""
434 . $_->{'name'} . "\" gateway = $_->{'gateway'}";
435 system_logged("$cmd > $logfile 2>&1");
444 # supports_address6([&iface])
445 # Returns 1 if managing IPv6 interfaces is supported
446 sub supports_address6