From 90e1aaa20dfa9b6b15c1808e4f8deb304744328f Mon Sep 17 00:00:00 2001 From: Philippe PITTOLI Date: Sun, 13 Oct 2019 01:21:40 +0200 Subject: [PATCH] dns, WIP autoconfiguration, context... --- src/main.cr | 171 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 108 insertions(+), 63 deletions(-) diff --git a/src/main.cr b/src/main.cr index 5ccc88e..544478d 100644 --- a/src/main.cr +++ b/src/main.cr @@ -3,51 +3,57 @@ require "option_parser" require "ipaddress" require "./colors" -simulation = false -file = nil +class Context + class_property root : String = "/" + + class_property simulation = false + class_property verbosity = 1 -prefered_network_configuration_program = nil -prefered_wireless_configuration_program = nil -prefered_dhcp_client = nil + class_property prefered_network_configuration_program : String? = nil + class_property prefered_wireless_configuration_program : String? = nil + class_property prefered_dhcp_client : String? = nil -root = "/" -print_autodetect = false + class_property root = "/" + class_property print_autodetect = false -command = "list" -args = Array(String).new + class_property command = "list" + class_property args = Array(String).new +end + +file_option : String? = nil OptionParser.parse! do |parser| parser.on "-s", "--simulation", "Export the network configuration." do - simulation = true + Context.simulation = true end parser.on "-a", "--print-autodetect", "Print autodetection of the installed programs." do - print_autodetect = true + Context.print_autodetect = true end parser.on "-w wireless-configuration-program", "--wireless wireless-configuration-program", "iw" do |prog| - prefered_wireless_configuration_program = prog + Context.prefered_wireless_configuration_program = prog end parser.on "-n network-configuration-program", "--net-conf network-configuration-program", "ifconfig | ip" do |prog| - prefered_network_configuration_program = prog + Context.prefered_network_configuration_program = prog end parser.on "-d dhcp-client-program", "--dhcp-client dhcp-client-program", "udhcpc | dhclient" do |prog| - prefered_dhcp_client = prog + Context.prefered_dhcp_client = prog end parser.on "-r root", "--root root", "Root where to search for /etc/hostname.* files." do |optsn| - root = optsn + Context.root = optsn end parser.on "-f file", "--file file", "Parse a configuration file." do |optsn| - file = optsn + file_option = optsn end # 0: nothing is printed, 1: only events, 2: events and messages parser.on "-v verbosity", "--verbosity verbosity", "Verbosity (0-2). Default: 1" do |optsn| - verbosity = optsn.to_i + Context.verbosity = optsn.to_i end parser.missing_option do |opt| @@ -62,16 +68,16 @@ OptionParser.parse! do |parser| end parser.unknown_args do |arg| - command = arg.shift - args = arg + Context.command = arg.shift + Context.args = arg - case command + case Context.command when /^(list)/ when /^(up)/ when /^(down)/ when /^(scan)/ else - STDERR.puts "Command #{command} not understood" + STDERR.puts "Command #{Context.command} not understood" exit 1 end @@ -115,6 +121,12 @@ class NotSetup end end +class Autoconfiguration + def to_s(io : IO) + io << "autoconfiguration" + end +end + class DHCP def to_s(io : IO) io << "dhcp" @@ -127,6 +139,21 @@ class NetworkCommands class_property cmd_wireless_configuration : IfconfigCommand.class | IWCommand.class | NotSetup.class = NotSetup class_property cmd_dhcp_client : UDHCPCCommand.class | DHClientCommand.class | NotSetup.class = NotSetup + class DNS + def initialize(@addresses : Array(String), @search : Array(String)) + end + + def execute + File.open("#{Context.root}/etc/resolv.conf", "w") do |file| + @addresses.each do |address| + file.puts "nameserver #{address}\n" + end + + file.puts "search #{@search.join(" ")}" + end + end + end + class IWCommand # get the available SSID def self.scan(ifname : String) : Array(String) @@ -338,6 +365,10 @@ class NetworkCommands end end + def self.autoconfiguration(ifname : String) + puts "TODO: IPv6 autoconfiguration setup" + end + def self.wireless_connect_wpa_psk(ifname : String, ssid : String, passwd : String) cmd = @@cmd_wireless_configuration case cmd @@ -358,7 +389,7 @@ class WirelessAPSetup property description : String? property mtu : Int32? property main_ip_v4 : IPAddress | DHCP | NotSetup - property main_ip_v6 : IPAddress | DHCP | NotSetup + property main_ip_v6 : IPAddress | DHCP | Autoconfiguration | NotSetup property aliasses_v4 : Array(IPAddress) property aliasses_v6 : Array(IPAddress) property dns : Array(IPAddress) @@ -397,29 +428,23 @@ class WirelessAPSetup # ipv4 unless main_ip_v4.is_a?(NotSetup) str << "\t\tinet #{main_ip_v4}\n" - end - aliasses_v4.each do |a| - str << "\t\talias #{a}\n" + aliasses_v4.each do |a| + str << "\t\talias #{a}\n" + end end # ipv6 unless main_ip_v6.is_a?(NotSetup) str << "\t\tinet6 #{main_ip_v6}\n" - end - unless @aliasses_v6.empty? @aliasses_v6.each do |a| str << "\t\talias6 #{a}\n" end end - if dns.empty? - str << "\t\tno dns configured\n" - else - dns.each do |ip| - str << "\t\tdns: #{ip}\n" - end + dns.each do |ip| + str << "\t\tdns: #{ip}\n" end # to improve readability @@ -441,7 +466,7 @@ class InterfaceConfiguration property mtu : Int32? property wireless : Bool property main_ip_v4 : IPAddress | DHCP | NotSetup - property main_ip_v6 : IPAddress | DHCP | NotSetup + property main_ip_v6 : IPAddress | Autoconfiguration | DHCP | NotSetup property aliasses_v4 : Array(IPAddress) property aliasses_v6 : Array(IPAddress) property wireless_networks : Hash(String, WirelessAPSetup) @@ -490,25 +515,33 @@ class InterfaceConfiguration # ipv4 unless main_ip_v4.is_a?(NotSetup) str << "\tinet #{main_ip_v4}\n" - end - unless aliasses_v4.empty? aliasses_v4.each do |a| str << "\talias #{a}\n" end end + # warning: alias but no main ip address + if main_ip_v4.is_a?(NotSetup) && ! aliasses_v4.empty? + str << "\t#{CRED}alias configured but no main ipv4 configuration.#{CRESET}\n" + str << "\t#{CRED}Should main ipv4 be obtained from DHCP? Static configuration?#{CRESET}\n" + end + # ipv6 unless main_ip_v6.is_a?(NotSetup) str << "\tinet6 #{main_ip_v6}\n" - end - unless aliasses_v6.empty? aliasses_v6.each do |a| str << "\talias6 #{a}\n" end end + # warning: alias but no main ip address + if main_ip_v6.is_a?(NotSetup) && ! aliasses_v6.empty? + str << "\t#{CRED}alias6 configured but no main ipv6 configuration.#{CRESET}\n" + str << "\t#{CRED}Should main ipv6 be obtained from autoconfiguration? DHCP? Static configuration?#{CRESET}\n" + end + unless dns.empty? dns.each do |ip| str << "\tdns: #{ip}\n" @@ -531,6 +564,17 @@ class InterfaceConfiguration raise "The interface #{@name} doesn't exists, yet." end + # TODO: treat differently wireless and non-wireless interfaces + if @wireless + puts "interface #{name} is wireless" + puts "TODO:" + puts "1. scan for SSID" + puts "2. select configured SSID then try to connect" + puts "3. connectivity check with the gateway" + else + puts "interface #{name} is not wireless" + end + if up NetworkCommands.up name else @@ -569,19 +613,22 @@ class InterfaceConfiguration # ipv6 configuration main_ip_v6.tap do |ip| + puts "CONFIGURATION IPV6" case ip when IPAddress NetworkCommands.set_ip name, ip # TODO - #when Autoconfiguration - # NetworkCommands.autoconfiguration name + when Autoconfiguration + puts "CONFIGURATION IPV6 : autoconfiguration !!!" + NetworkCommands.autoconfiguration name #when DHCP # NetworkCommands.dhcp6 name when NotSetup # do nothing + puts "CONFIGURATION IPV6 : not setup !!!" else - raise "ipv4 configuration: neither static nor dynamic" + raise "ipv6 configuration: neither static nor dynamic" end # We wont setup aliasses unless there is an actual IP address @@ -661,7 +708,7 @@ class NetworkConfigurationParser end when /^inet6 autoconf/ # IP address is autoconfigured - puts "TODO: IPv6 autoconfiguration" + main_ip_v6 = Autoconfiguration.new when /^inet6? .*/ ipstr = /^inet6? ([a-f0-9:.\/]+)/.match(line).try &.[1] @@ -710,8 +757,9 @@ class NetworkConfigurationParser next end - # TODO access_point = wireless_networks[ssid].not_nil! + access_point.main_ip_v6 = Autoconfiguration.new + puts "for SSID: #{ssid} ipv6 configuration = autoconf" when /^network [^ \t]+ inet6? .*/ ssid = nil @@ -841,8 +889,8 @@ class Autodetect end -Do.simulation = simulation -Autodetect.print_autodetect = print_autodetect +Do.simulation = Context.simulation +Autodetect.print_autodetect = Context.print_autodetect # # discover available configuration commands @@ -867,17 +915,17 @@ possible_wireless_configuration_cmds = { "ifconfig" => NetworkCommands::IfconfigCommand } -key = prefered_network_configuration_program +key = Context.prefered_network_configuration_program key = possible_network_configuration_cmds.keys.find { |key| Autodetect.which(key) } if key.nil? # should crash if there is no network command installed NetworkCommands.cmd_network_configuration = possible_network_configuration_cmds[key.not_nil!] -key = prefered_dhcp_client +key = Context.prefered_dhcp_client key = possible_dhcp_clients.keys.find { |key| Autodetect.which(key) } if key.nil? # should not crash if there is no NetworkCommands.cmd_dhcp_client = possible_dhcp_clients[key] unless key.nil? -key = prefered_wireless_configuration_program +key = Context.prefered_wireless_configuration_program key = possible_wireless_configuration_cmds.keys.find { |key| Autodetect.which(key) } if key.nil? # should crash if there is no wireless command installed NetworkCommands.cmd_wireless_configuration = possible_wireless_configuration_cmds[key.not_nil!] @@ -885,7 +933,7 @@ NetworkCommands.cmd_wireless_configuration = possible_wireless_configuration_cmd files = Array(String).new -Dir.children("#{root}/etc/").each do |f| +Dir.children("#{Context.root}/etc/").each do |f| if /^hostname\./.match(f) files << f end @@ -893,46 +941,43 @@ end interface_files = Array(String).new -if ! file.nil? +if ! file_option.nil? # file passed via the '-f' option # TODO: why having to force "not_nil!" ? Seems like a compiler bug - interface_files << file.not_nil! -elsif args.empty? + interface_files << file_option.not_nil! +elsif Context.args.empty? # every configured interface files.each do |f| - interface_files << "#{root}/etc/#{f}" + interface_files << "#{Context.root}/etc/#{f}" end else # only interfaces in arguments - args.each do |interface| - interface_files << "#{root}/etc/hostname.#{interface}" + Context.args.each do |interface| + interface_files << "#{Context.root}/etc/hostname.#{interface}" end end begin - case command + case Context.command when "list" interface_files.each do |f| - puts NetworkConfigurationParser.parse_file(f.not_nil!) + puts NetworkConfigurationParser.parse_file(f) end when "up" - # TODO: why having to force "not_nil!" ? Seems like a compiler bug interface_files.each do |f| - network_configuration = NetworkConfigurationParser.parse_file(f.not_nil!) + network_configuration = NetworkConfigurationParser.parse_file(f) network_configuration.execute end when "down" - # TODO: why having to force "not_nil!" ? Seems like a compiler bug interface_files.each do |f| - network_configuration = NetworkConfigurationParser.parse_file(f.not_nil!) + network_configuration = NetworkConfigurationParser.parse_file(f) network_configuration.down end when "scan" - # TODO: why having to force "not_nil!" ? Seems like a compiler bug interface_files.each do |f| - network_configuration = NetworkConfigurationParser.parse_file(f.not_nil!) + network_configuration = NetworkConfigurationParser.parse_file(f) network_configuration.scan end end