Support for outgoing IPv6 connections
authorJamie Cameron <jcameron@webmin.com>
Sat, 30 Oct 2010 23:59:20 +0000 (16:59 -0700)
committerJamie Cameron <jcameron@webmin.com>
Sat, 30 Oct 2010 23:59:20 +0000 (16:59 -0700)
WebminCore.pm
hpuxexports/exports-lib.pl
miniserv.pl
web-lib-funcs.pl
webmin/webmin-lib.pl

index 56d58c5..cde1607 100644 (file)
@@ -20,7 +20,7 @@ require Exporter;
 # Add functions in web-lib-funcs.pl
 # Generated with :
 # grep -h "^sub " web-lib-funcs.pl ui-lib.pl | sed -e 's/sub /\&/' | xargs echo
-@EXPORT = qw(&read_file &read_file_cached &write_file &html_escape &quote_escape &tempname &transname &trunc &indexof &indexoflc &sysprint &check_ipaddress &check_ip6address &generate_icon &urlize &un_urlize &include &copydata &ReadParseMime &ReadParse &read_fully &read_parse_mime_callback &read_parse_mime_javascript &PrintHeader &header &get_html_title &get_html_framed_title &get_html_status_line &popup_header &footer &popup_footer &load_theme_library &redirect &kill_byname &kill_byname_logged &find_byname &error &popup_error &error_setup &wait_for &fast_wait_for &has_command &make_date &file_chooser_button &popup_window_button &read_acl &acl_filename &acl_check &get_miniserv_config &put_miniserv_config &restart_miniserv &reload_miniserv &check_os_support &http_download &complete_http_download &ftp_download &ftp_upload &no_proxy &open_socket &download_timeout &ftp_command &to_ipaddress &icons_table &replace_file_line &read_file_lines &flush_file_lines &unflush_file_lines &unix_user_input &unix_group_input &hlink &user_chooser_button &group_chooser_button &foreign_check &foreign_exists &foreign_available &foreign_require &foreign_call &foreign_config &foreign_installed &foreign_defined &get_system_hostname &get_webmin_version &get_module_acl &get_group_module_acl &save_module_acl &save_group_module_acl &init_config &load_language &text_subs &text &encode_base64 &decode_base64 &get_module_info &get_all_module_infos &get_theme_info &list_languages &read_env_file &write_env_file &lock_file &unlock_file &test_lock &unlock_all_files &can_lock_file &webmin_log &additional_log &webmin_debug_log &system_logged &backquote_logged &backquote_with_timeout &backquote_command &kill_logged &rename_logged &rename_file &symlink_logged &symlink_file &link_file &make_dir &set_ownership_permissions &unlink_logged &unlink_file &copy_source_dest &remote_session_name &remote_foreign_require &remote_foreign_call &remote_foreign_check &remote_foreign_config &remote_eval &remote_write &remote_read &remote_finished &remote_error_setup &remote_rpc_call &remote_multi_callback &remote_multi_callback_error &serialise_variable &unserialise_variable &other_groups &date_chooser_button &help_file &seed_random &disk_usage_kb &recursive_disk_usage &help_search_link &make_http_connection &read_http_connection &write_http_connection &close_http_connection &clean_environment &reset_environment &progress_callback &switch_to_remote_user &switch_to_unix_user &create_user_config_dirs &create_missing_homedir &filter_javascript &resolve_links &simplify_path &same_file &flush_webmin_caches &list_usermods &available_usermods &get_available_module_infos &get_visible_module_infos &get_visible_modules_categories &is_under_directory &parse_http_url &check_clicks_function &load_entities_map &entities_to_ascii &get_product_name &get_charset &get_display_hostname &save_module_config &save_user_module_config &nice_size &get_perl_path &get_goto_module &select_all_link &select_invert_link &select_rows_link &check_pid_file &get_mod_lib &module_root_directory &list_mime_types &guess_mime_type &open_tempfile &close_tempfile &print_tempfile &is_selinux_enabled &get_clear_file_attributes &reset_file_attributes &cleanup_tempnames &open_lock_tempfile &END &month_to_number &number_to_month &get_rbac_module_acl &supports_rbac &use_rbac_module_acl &execute_command &open_readfile &open_execute_command &translate_filename &translate_command &register_filename_callback &register_command_callback &capture_function_output &capture_function_output_tempfile &modules_chooser_button &substitute_template &running_in_zone &running_in_vserver &running_in_xen &running_in_openvz &list_categories &is_readonly_mode &command_as_user &list_osdn_mirrors &convert_osdn_url &get_current_dir &supports_users &supports_symlinks &quote_path &get_windows_root &read_file_contents &unix_crypt &split_quoted_string &write_to_http_cache &check_in_http_cache &supports_javascript &ui_table_start &ui_table_end &ui_table_row &ui_table_hr &ui_table_span &ui_columns_start &ui_columns_row &ui_columns_header &ui_checked_columns_row &ui_radio_columns_row &ui_columns_end &ui_columns_table &ui_form_columns_table &ui_form_start &ui_form_end &ui_textbox &ui_filebox &ui_bytesbox &ui_upload &ui_password &ui_hidden &ui_select &ui_multi_select &ui_multi_select_javascript &ui_radio &ui_yesno_radio &ui_checkbox &ui_oneradio &ui_textarea &ui_user_textbox &ui_group_textbox &ui_opt_textbox &ui_submit &ui_reset &ui_button &ui_date_input &ui_buttons_start &ui_buttons_end &ui_buttons_row &ui_buttons_hr &ui_post_header &ui_pre_footer &ui_print_header &ui_print_unbuffered_header &ui_print_footer &ui_config_link &ui_print_endpage &ui_subheading &ui_links_row &ui_hidden_javascript &ui_hidden_start &ui_hidden_end &ui_hidden_table_row_start &ui_hidden_table_row_end &ui_hidden_table_start &ui_hidden_table_end &ui_tabs_start &ui_tabs_end &ui_tabs_start_tab &ui_tabs_start_tabletab &ui_tabs_end_tab &ui_tabs_end_tabletab &ui_max_text_width &ui_radio_selector &ui_radio_selector_javascript &ui_grid_table &ui_radio_table &ui_up_down_arrows &ui_hr &ui_nav_link &ui_confirmation_form &js_disable_inputs &ui_page_flipper &js_checkbox_disable &js_redirect &get_module_name clear_time_locale reset_time_locale eval_as_unix_user get_userdb_string connect_userdb disconnect_userdb split_userdb_string);
+@EXPORT = qw(&read_file &read_file_cached &write_file &html_escape &quote_escape &tempname &transname &trunc &indexof &indexoflc &sysprint &check_ipaddress &check_ip6address &generate_icon &urlize &un_urlize &include &copydata &ReadParseMime &ReadParse &read_fully &read_parse_mime_callback &read_parse_mime_javascript &PrintHeader &header &get_html_title &get_html_framed_title &get_html_status_line &popup_header &footer &popup_footer &load_theme_library &redirect &kill_byname &kill_byname_logged &find_byname &error &popup_error &error_setup &wait_for &fast_wait_for &has_command &make_date &file_chooser_button &popup_window_button &read_acl &acl_filename &acl_check &get_miniserv_config &put_miniserv_config &restart_miniserv &reload_miniserv &check_os_support &http_download &complete_http_download &ftp_download &ftp_upload &no_proxy &open_socket &download_timeout &ftp_command &to_ipaddress &to_ip6address &to_hostname &icons_table &replace_file_line &read_file_lines &flush_file_lines &unflush_file_lines &unix_user_input &unix_group_input &hlink &user_chooser_button &group_chooser_button &foreign_check &foreign_exists &foreign_available &foreign_require &foreign_call &foreign_config &foreign_installed &foreign_defined &get_system_hostname &get_webmin_version &get_module_acl &get_group_module_acl &save_module_acl &save_group_module_acl &init_config &load_language &text_subs &text &encode_base64 &decode_base64 &get_module_info &get_all_module_infos &get_theme_info &list_languages &read_env_file &write_env_file &lock_file &unlock_file &test_lock &unlock_all_files &can_lock_file &webmin_log &additional_log &webmin_debug_log &system_logged &backquote_logged &backquote_with_timeout &backquote_command &kill_logged &rename_logged &rename_file &symlink_logged &symlink_file &link_file &make_dir &set_ownership_permissions &unlink_logged &unlink_file &copy_source_dest &remote_session_name &remote_foreign_require &remote_foreign_call &remote_foreign_check &remote_foreign_config &remote_eval &remote_write &remote_read &remote_finished &remote_error_setup &remote_rpc_call &remote_multi_callback &remote_multi_callback_error &serialise_variable &unserialise_variable &other_groups &date_chooser_button &help_file &seed_random &disk_usage_kb &recursive_disk_usage &help_search_link &make_http_connection &read_http_connection &write_http_connection &close_http_connection &clean_environment &reset_environment &progress_callback &switch_to_remote_user &switch_to_unix_user &create_user_config_dirs &create_missing_homedir &filter_javascript &resolve_links &simplify_path &same_file &flush_webmin_caches &list_usermods &available_usermods &get_available_module_infos &get_visible_module_infos &get_visible_modules_categories &is_under_directory &parse_http_url &check_clicks_function &load_entities_map &entities_to_ascii &get_product_name &get_charset &get_display_hostname &save_module_config &save_user_module_config &nice_size &get_perl_path &get_goto_module &select_all_link &select_invert_link &select_rows_link &check_pid_file &get_mod_lib &module_root_directory &list_mime_types &guess_mime_type &open_tempfile &close_tempfile &print_tempfile &is_selinux_enabled &get_clear_file_attributes &reset_file_attributes &cleanup_tempnames &open_lock_tempfile &END &month_to_number &number_to_month &get_rbac_module_acl &supports_rbac &supports_ipv6 &use_rbac_module_acl &execute_command &open_readfile &open_execute_command &translate_filename &translate_command &register_filename_callback &register_command_callback &capture_function_output &capture_function_output_tempfile &modules_chooser_button &substitute_template &running_in_zone &running_in_vserver &running_in_xen &running_in_openvz &list_categories &is_readonly_mode &command_as_user &list_osdn_mirrors &convert_osdn_url &get_current_dir &supports_users &supports_symlinks &quote_path &get_windows_root &read_file_contents &unix_crypt &split_quoted_string &write_to_http_cache &check_in_http_cache &supports_javascript &ui_table_start &ui_table_end &ui_table_row &ui_table_hr &ui_table_span &ui_columns_start &ui_columns_row &ui_columns_header &ui_checked_columns_row &ui_radio_columns_row &ui_columns_end &ui_columns_table &ui_form_columns_table &ui_form_start &ui_form_end &ui_textbox &ui_filebox &ui_bytesbox &ui_upload &ui_password &ui_hidden &ui_select &ui_multi_select &ui_multi_select_javascript &ui_radio &ui_yesno_radio &ui_checkbox &ui_oneradio &ui_textarea &ui_user_textbox &ui_group_textbox &ui_opt_textbox &ui_submit &ui_reset &ui_button &ui_date_input &ui_buttons_start &ui_buttons_end &ui_buttons_row &ui_buttons_hr &ui_post_header &ui_pre_footer &ui_print_header &ui_print_unbuffered_header &ui_print_footer &ui_config_link &ui_print_endpage &ui_subheading &ui_links_row &ui_hidden_javascript &ui_hidden_start &ui_hidden_end &ui_hidden_table_row_start &ui_hidden_table_row_end &ui_hidden_table_start &ui_hidden_table_end &ui_tabs_start &ui_tabs_end &ui_tabs_start_tab &ui_tabs_start_tabletab &ui_tabs_end_tab &ui_tabs_end_tabletab &ui_max_text_width &ui_radio_selector &ui_radio_selector_javascript &ui_grid_table &ui_radio_table &ui_up_down_arrows &ui_hr &ui_nav_link &ui_confirmation_form &js_disable_inputs &ui_page_flipper &js_checkbox_disable &js_redirect &get_module_name clear_time_locale reset_time_locale eval_as_unix_user get_userdb_string connect_userdb disconnect_userdb split_userdb_string);
 
 # Add global variables in web-lib.pl
 push(@EXPORT, qw(&unique));
index e56dbae..b298313 100755 (executable)
@@ -111,7 +111,7 @@ if (!@h) {
         }
 
 foreach (@h) {
-        if (!inet_aton($_)) { &error(&text('save_ehost', $_, $_[0])); }
+        if (!&to_ipaddress($_)) { &error(&text('save_ehost', $_, $_[0])); }
         }
 }
 
index fe31010..d042d86 100755 (executable)
@@ -506,7 +506,6 @@ $proto = getprotobyname('tcp');
 @sockerrs = ( );
 $tried_inaddr_any = 0;
 for($i=0; $i<@sockets; $i++) {
-       print STDERR "socket=",join(" ", @{$sockets[$i]}),"\n";
        $fh = "MAIN$i";
        socket($fh, $sockets[$i]->[2], SOCK_STREAM, $proto) ||
                die "Failed to open socket family $sockets[$i]->[2] : $!";
index bc35157..5cc6715 100755 (executable)
@@ -15,6 +15,8 @@ Example code:
 #use warnings;
 use Socket;
 use POSIX;
+eval "use Socket6";
+$ipv6_module_error = $@;
 
 use vars qw($user_risk_level $loaded_theme_library $wait_for_input
            $done_webmin_header $trust_unknown_referers $unsafe_index_cgi
@@ -25,7 +27,7 @@ use vars qw($module_index_name $number_to_month_map $month_to_number_map
            $umask_already $default_charset $licence_status $os_type
            $licence_message $script_name $loaded_theme_oo_library
            $done_web_lib_funcs $os_version $module_index_link
-           $called_from_webmin_core);
+           $called_from_webmin_core $ipv6_module_error);
 
 =head2 read_file(file, &hash, [&order], [lowercase], [split-char])
 
@@ -1880,16 +1882,15 @@ else {
        }
 
 if (!$nowait) {
-       # wait for miniserv to come back up
-       $addr = inet_aton($miniserv{'bind'} ? $miniserv{'bind'} : "127.0.0.1");
+       # Wait for miniserv to come back up
+       my $addr = $miniserv{'bind'} || "127.0.0.1";
        my $ok = 0;
        for($i=0; $i<20; $i++) {
+               my $err;
                sleep(1);
-               socket(STEST, PF_INET, SOCK_STREAM, getprotobyname("tcp"));
-               my $rv = connect(STEST,
-                                pack_sockaddr_in($miniserv{'port'}, $addr));
+               &open_socket($addr, $miniserv{'port'}, STEST, \$err);
                close(STEST);
-               last if ($rv && ++$ok >= 2);
+               last if (!$err && ++$ok >= 2);
                }
        $i < 20 || &error("Failed to restart Webmin server!");
        }
@@ -2530,26 +2531,57 @@ $fh = &callers_package($fh);
 if ($gconfig{'debug_what_net'}) {
        &webmin_debug_log('TCP', "host=$host port=$port");
        }
-if (!socket($fh, PF_INET, SOCK_STREAM, getprotobyname("tcp"))) {
-       if ($err) { $$err = "Failed to create socket : $!"; return 0; }
-       else { &error("Failed to create socket : $!"); }
-       }
-my $addr;
-if (!($addr = inet_aton($host))) {
-       if ($err) { $$err = "Failed to lookup IP address for $host"; return 0; }
-       else { &error("Failed to lookup IP address for $host"); }
+
+# Lookup IP address for the host. Try v4 first, and failing that v6
+my $ip;
+my $proto = getprotobyname("tcp");
+if ($ip = &to_ipaddress($host)) {
+       # Create IPv4 socket and connection
+       if (!socket($fh, PF_INET, SOCK_STREAM, $proto)) {
+               my $msg = "Failed to create socket : $!";
+               if ($err) { $$err = $msg; return 0; }
+               else { &error($msg); }
+               }
+       my $addr = inet_aton($ip);
+       if ($gconfig{'bind_proxy'}) {
+               # BIND to outgoing IP
+               if (!bind($fh,pack_sockaddr_in(0, inet_aton($gconfig{'bind_proxy'})))) {
+                       my $msg = "Failed to bind to source address : $!";
+                       if ($err) { $$err = $msg; return 0; }
+                       else { &error($msg); }
+                       }
+               }
+       if (!connect($fh, pack_sockaddr_in($port, $addr))) {
+               my $msg = "Failed to connect to $host:$port : $!";
+               if ($err) { $$err = $msg; return 0; }
+               else { &error($msg); }
+               }
        }
-if ($gconfig{'bind_proxy'}) {
-       if (!bind($fh,pack_sockaddr_in(0, inet_aton($gconfig{'bind_proxy'})))) {
-               if ($err) { $$err = "Failed to bind to source address : $!"; return 0; }
-               else { &error("Failed to bind to source address : $!"); }
+elsif ($ip = &to_ip6address($host)) {
+       # Create IPv6 socket and connection
+       if (!socket($fh, Socket6::PF_INET6(), SOCK_STREAM, $proto)) {
+               my $msg = "Failed to create IPv6 socket : $!";
+               if ($err) { $$err = $msg; return 0; }
+               else { &error($msg); }
+               }
+       my $addr = inet_pton(Socket6::AF_INET6(), $ip);
+       if (!connect($fh, pack_sockaddr_in6($port, $addr))) {
+               my $msg = "Failed to IPv6 connect to $host:$port : $!";
+               if ($err) { $$err = $msg; return 0; }
+               else { &error($msg); }
                }
        }
-if (!connect($fh, pack_sockaddr_in($port, $addr))) {
-       if ($err) { $$err = "Failed to connect to $host:$port : $!"; return 0; }
-       else { &error("Failed to connect to $host:$port : $!"); }
+else {
+       # Resolution failed
+       my $msg = "Failed to lookup IP address for $host";
+       if ($err) { $$err = $msg; return 0; }
+       else { &error($msg); }
        }
-my $old = select($fh); $| =1; select($old);
+
+# Disable buffering
+my $old = select($fh);
+$| = 1;
+select($old);
 return 1;
 }
 
@@ -2629,7 +2661,10 @@ it cannot be resolved.
 sub to_ipaddress
 {
 if (&check_ipaddress($_[0])) {
-       return $_[0];
+       return $_[0];   # Already in v4 format
+       }
+elsif (&check_ip6address($_[0])) {
+       return undef;   # A v6 address cannot be converted to v4
        }
 else {
        my $hn = gethostbyname($_[0]);
@@ -2639,6 +2674,51 @@ else {
        }
 }
 
+=head2 to_ip6address(hostname)
+
+Converts a hostname to IPv6 address, or returns undef if it cannot be resolved.
+
+=cut
+sub to_ip6address
+{
+if (&check_ip6address($_[0])) {
+       return $_[0];   # Already in v6 format
+       }
+elsif (&check_ipaddress($_[0])) {
+       return undef;   # A v4 address cannot be v6
+       }
+elsif (!&supports_ipv6()) {
+       return undef;   # Cannot lookup
+       }
+else {
+       # Perform IPv6 DNS lookup
+       my $inaddr;
+       (undef, undef, undef, $inaddr) =
+           getaddrinfo($_[0], undef, Socket6::AF_INET6(), SOCK_STREAM);
+       return undef if (!$inaddr);
+       my $addr;
+       (undef, $addr) = unpack_sockaddr_in6($inaddr);
+       return inet_ntop(Socket6::AF_INET6(), $addr);
+       }
+}
+
+=head2 to_hostname(ipv4|ipv6-address)
+
+Reverse-resolves an IPv4 or 6 address to a hostname
+
+=cut
+sub to_hostname
+{
+my ($addr) = @_;
+if (&check_ip6address($addr) && &supports_ipv6()) {
+       return gethostbyaddr(inet_pton(Socket6::AF_INET6(), $addr),
+                            Socket6::AF_INET6());
+       }
+else {
+       return gethostbyaddr(inet_aton($addr), AF_INET);
+       }
+}
+
 =head2 icons_table(&links, &titles, &icons, [columns], [href], [width], [height], &befores, &afters)
 
 Renders a 4-column table of icons. The useful parameters are :
@@ -8197,6 +8277,16 @@ if ($_[0]) {
 return 1;
 }
 
+=head2 supports_ipv6()
+
+Returns 1 if outgoing IPv6 connections can be made
+
+=cut
+sub supports_ipv6
+{
+return $ipv6_module_error ? 0 : 1;
+}
+
 =head2 use_rbac_module_acl(user, module)
 
 Returns 1 if some user should use RBAC to get permissions for a module
index 940a287..0db25cd 100755 (executable)
@@ -1387,9 +1387,18 @@ returns 1 if a match is found.
 sub ip_match
 {
 local(@io, @mo, @ms, $i, $j);
-@io = split(/\./, $_[0]);
-local $hn = gethostbyaddr(inet_aton($_[0]), AF_INET);
-undef($hn) if ((&to_ipaddress($hn))[0] ne $_[0]);
+@io = &check_ip6address($_[0]) ? split(/:/, $_[0])
+                              : split(/\./, $_[0]);
+
+# Resolve to hostname and check that it forward resolves again
+$hn = &to_hostname($_[0]);
+if (&check_ip6address($_[0])) {
+       $hn = "" if (&to_ip6address($hn) ne $_[0]);
+       }
+else {
+       $hn = "" if (&to_ipaddress($hn) ne $_[0]);
+       }
+
 for($i=1; $i<@_; $i++) {
        local $mismatch = 0;
        local $ip = $_[$i];
@@ -1399,6 +1408,7 @@ for($i=1; $i<@_; $i++) {
                 }
        if ($ip =~ /^(\S+)\/(\S+)$/) {
                # Compare with network/mask
+               # XXX IPv6 support
                @mo = split(/\./, $1); @ms = split(/\./, $2);
                for($j=0; $j<4; $j++) {
                        if ((int($io[$j]) & int($ms[$j])) != int($mo[$j])) {
@@ -1413,13 +1423,19 @@ for($i=1; $i<@_; $i++) {
        elsif ($ip eq 'LOCAL') {
                # Just assume OK for now
                }
-       elsif ($ip !~ /^[0-9\.]+$/) {
-               # Compare with hostname
-               $mismatch = 1 if ($_[0] ne &to_ipaddress($ip));
+       elsif ($_[$i] =~ /^[0-9\.]+$/) {
+               # Compare with IPv4 address or network
+               @mo = split(/\./, $_[$i]);
+               while(@mo && !$mo[$#mo]) { pop(@mo); }
+               for($j=0; $j<@mo; $j++) {
+                       if ($mo[$j] != $io[$j]) {
+                               $mismatch = 1;
+                               }
+                       }
                }
-       else {
-               # Compare with IP or network
-               @mo = split(/\./, $ip);
+       elsif ($_[$i] =~ /^[a-f0-9:]+$/) {
+               # Compare with IPv6 address or network
+               @mo = split(/:/, $_[$i]);
                while(@mo && !$mo[$#mo]) { pop(@mo); }
                for($j=0; $j<@mo; $j++) {
                        if ($mo[$j] != $io[$j]) {
@@ -1427,6 +1443,10 @@ for($i=1; $i<@_; $i++) {
                                }
                        }
                }
+       elsif ($_[$i] !~ /^[0-9\.]+$/) {
+               # Compare with hostname
+               $mismatch = 1 if ($_[0] ne &to_ipaddress($_[$i]));
+               }
        return 1 if (!$mismatch);
        }
 return 0;