diff --git a/src/main.cr b/src/main.cr index 6e9403b..4b0fa0c 100644 --- a/src/main.cr +++ b/src/main.cr @@ -10,11 +10,19 @@ prefered_network_configuration_program = nil prefered_wireless_configuration_program = nil prefered_dhcp_client = nil +print_autodetect = false + +command = "list" + OptionParser.parse! do |parser| parser.on "-s", "--simulation", "Export the network configuration." do simulation = true end + parser.on "-a", "--print-autodetect", "Print autodetection of the installed programs." do + print_autodetect = true + end + parser.on "-w wireless-configuration-program", "--wireless wireless-configuration-program", "iw" do |prog| prefered_wireless_configuration_program = prog end @@ -36,6 +44,35 @@ OptionParser.parse! do |parser| verbosity = optsn.to_i end + parser.missing_option do |opt| + STDERR.puts "You missed the argument for option #{opt}" + # TODO: explain the different arguments + exit 1 + end + + parser.invalid_option do |flag| + STDERR.puts "Error: #{flag} not a valid option" + exit 1 + end + + parser.unknown_args do |arg| + command = arg.shift + + case command + when /^(list)/ + STDERR.puts "TODO: list" unless arg.empty? + when /^(connect)/ + STDERR.puts "TODO: connect" unless arg.empty? + else + STDERR.puts "Command #{command} not understood" + end + + unless arg.empty? + STDERR.puts "unknown arg: #{arg}" + exit 1 + end + end + parser.on "-h", "--help", "Show this help" do puts parser exit 0 @@ -61,12 +98,17 @@ class NotSetup end end +class DHCP + def to_s(io : IO) + io << "dhcp" + end +end class NetworkCommands class_property cmd_network_configuration : IfconfigCommand.class | IPCommand.class = IfconfigCommand class_property cmd_wireless_configuration : IWCommand.class | NotSetup.class = NotSetup - class_property cmd_dhcp_client : UDHCPCCommand.class | NotSetup.class = NotSetup + class_property cmd_dhcp_client : UDHCPCCommand.class | DHClientCommand.class | NotSetup.class = NotSetup class IWCommand def self.get_ssid(ifname : String, ssid) @@ -86,6 +128,16 @@ class NetworkCommands end end + class DHClientCommand + def self.run(ifname : String) + # TODO: verify which dhcp client is installed on the system + cmd = "udhcpc" + unless Do.run(cmd, [ name ]).success? + raise "(#{cmd}) dhcp failed on #{ifname}" + end + end + end + class IfconfigCommand def self.interface_exists(name : String) Do.run("ifconfig", [ name ]).success? @@ -236,6 +288,15 @@ end class WirelessAPSetup property ssid : String + # This is a list of parameters that should be unique to each AP + property up : Bool + property description : String? + property mtu : Int32? + property main_ip_v4 : IPAddress | DHCP | NotSetup + property main_ip_v6 : IPAddress | DHCP | NotSetup + property aliasses_v4 : Array(IPAddress) + property aliasses_v6 : Array(IPAddress) + # we currently only support WPA2-PSK wireless security mechanism property security : WPA @@ -246,6 +307,40 @@ class WirelessAPSetup end def initialize(@ssid, @security) + @main_ip_v4 = NotSetup.new + @main_ip_v6 = NotSetup.new + @aliasses_v4 = Array(IPAddress).new + @aliasses_v6 = Array(IPAddress).new + @up = true + end + + + def to_s(io : IO) + io << to_string + end + + def to_string + String.build do |str| + str << "#{CBLUE}#{ssid}: {ssid}#{CRESET}\n" + + str << "\t#description #{description.not_nil!}\n" unless description.nil? + str << "\t#{@up? "up" : "down"}\n" + str << "\tmtu #{mtu}\n" unless mtu.nil? + + # ipv4 + str << "\tinet #{@main_ip_v4}\n" + @aliasses_v4.each do |a| + str << "\talias #{a}\n" + end + + # ipv6 + str << "\tinet6 #{@main_ip_v6}\n" + unless @aliasses_v6.empty? + @aliasses_v6.each do |a| + str << "\talias6 #{a}\n" + end + end + end end end @@ -255,11 +350,6 @@ end # class InterfaceConfiguration - class DHCP - def to_s(io : IO) - io << "dhcp" - end - end property name : String property up : Bool @@ -302,7 +392,7 @@ class InterfaceConfiguration str << "#{CRED}#{@name}#{CRESET}\n" end - str << "\t#description #{description.not_nil!}\n" unless description.nil? + str << "\tdescription: '#{description.not_nil!}'\n" unless description.nil? str << "\t#{@up? "up" : "down"}\n" @@ -325,6 +415,14 @@ class InterfaceConfiguration str << "\talias6 #{a}\n" end end + + if wireless_networks.empty? + puts "no wireless connection configured" if wireless + else + wireless_networks.each do |k,v| + puts v + end + end end end @@ -440,9 +538,9 @@ class NetworkConfigurationParser when /^inet6? dhcp/ # IPaddress is DHCP if /^inet /.match(line) - main_ip_v4 = InterfaceConfiguration::DHCP.new + main_ip_v4 = DHCP.new else - main_ip_v6 = InterfaceConfiguration::DHCP.new + main_ip_v6 = DHCP.new end when /^inet6 autoconf/ # IPaddress is autoconfigured @@ -458,14 +556,98 @@ class NetworkConfigurationParser else main_ip_v6 = IPAddress.parse ipstr end - when /^join [^ ]+ wpakey .*/ + when /^join [^ \t]+ wpakey .*/ # WPA2-PSK, other security mechanisms are not supported, yet + ssid = /^join ([^ \t]+)/.match(line).try &.[1] + wpakeystr = /^join [^ \t]+ wpakey ([^ \t]+)/.match(line).try &.[1] - when /^network [^ ]+ inet .*/ - puts "TODO: network SSID inet IP/prefix" + if ssid.nil? + puts "wrong SSID in line: #{line}" + next + end - when /^network [^ ]+ dhcp/ - puts "TODO: network SSID dhcp" + if wpakeystr.nil? + puts "wrong wpa key in line: #{line}" + next + end + + # TODO + new_ap = WirelessAPSetup.new ssid, WirelessAPSetup::WPA.new(wpakeystr) + wireless_networks[ssid] = new_ap + + + when /^network [^ \t]+ inet6 autoconf/ + puts "TODO: network SSID inet6 autoconf" + + ssid = /^network ([^ \t]+)/.match(line).try &.[1] + ipstr = /^network [^ \t]+ inet6? ([^ \t]+)/.match(line).try &.[1] + + if ssid.nil? + puts "wrong SSID in line: #{line}" + next + end + + if ipstr.nil? + puts "wrong ip address in line: #{line}" + next + end + + # TODO + access_point = wireless_networks[ssid].not_nil! + + when /^network [^ \t]+ inet6? .*/ + ssid = nil + ipstr = nil + + /^network (?[^ \t]+) inet6? (?[^ \t]+)/.match(line).try do |m| + ssid = m["ssid"] + ipstr = m["ip"] + end + # ssid = /^network ([^ \t]+)/.match(line).try &.[1] + # ipstr = /^network [^ \t]+ inet6? (?[^ \t]+)/.match(line).try &.["ip"] + + if ssid.nil? + puts "wrong SSID in line: #{line}" + next + end + + if ipstr.nil? + puts "wrong ip address in line: #{line}" + next + end + + # TODO + ipaddr = IPAddress.parse ipstr + + # TODO + access_point = wireless_networks[ssid].not_nil! + + if ipaddr.ipv4? + access_point.main_ip_v4 = ipaddr + elsif ipaddr.ipv6? + access_point.main_ip_v6 = ipaddr + else + puts "wrong ip address in line: #{line} (neither ipv4 or ipv6)" + end + + when /^network [^ ]+ dhcp6?/ + ssid = /^network ([^ \t]+)/.match(line).try &.[1] + + if ssid.nil? + puts "wrong SSID in line: #{line}" + next + end + + # TODO + access_point = wireless_networks[ssid].not_nil! + + if /dhcp6/.match(line) + access_point.main_ip_v6 = DHCP.new + elsif /dhcp/.match(line) + access_point.main_ip_v4 = DHCP.new + else + puts "wrong dhcp instruction in line: #{line}" + end when /^network [^ ]+ dns .*/ puts "TODO: network SSID dns" @@ -491,18 +673,25 @@ class NetworkConfigurationParser end end -def which(cmd : String) - if Process.run("which", [ cmd ]).success? - puts "#{cmd} installed" - true - else - puts "#{cmd} not installed" - false + + +class Autodetect + class_property print_autodetect : Bool = false + + def self.which(cmd : String) + if Process.run("which", [ cmd ]).success? + puts "#{cmd} installed" if print_autodetect + true + else + puts "#{cmd} not installed" if print_autodetect + false + end end end Do.simulation = simulation +Autodetect.print_autodetect = print_autodetect # # discover available configuration commands @@ -517,7 +706,8 @@ possible_network_configuration_cmds = { # udhcpc = busybox dhcp client possible_dhcp_clients = { - "udhcpc" => NetworkCommands::UDHCPCCommand + "udhcpc" => NetworkCommands::UDHCPCCommand, + "dhclient" => NetworkCommands::DHClientCommand } # iw = linux @@ -526,24 +716,30 @@ possible_wireless_configuration_cmds = { } key = prefered_network_configuration_program -key = possible_network_configuration_cmds.keys.find { |key| which(key) } if key.nil? +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 = possible_dhcp_clients.keys.find { |key| which(key) } if key.nil? +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 = possible_wireless_configuration_cmds.keys.find { |key| which(key) } if key.nil? +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!] if file.nil? - raise "Cannot choose files yet" -else + raise "Cannot search for files yet" +end + +case command +when "list" + # TODO: why having to force "not_nil!" ? Seems like a compiler bug + puts NetworkConfigurationParser.parse_file(file.not_nil!) +when "connect" # TODO: why having to force "not_nil!" ? Seems like a compiler bug network_configuration = NetworkConfigurationParser.parse_file(file.not_nil!) network_configuration.execute