diff --git a/publicip/src/lib.rs b/publicip/src/lib.rs index 2db4375..a2179a5 100644 --- a/publicip/src/lib.rs +++ b/publicip/src/lib.rs @@ -27,12 +27,26 @@ static CLOUDFLARE_QNAME: &[&str] = &["whoami", "cloudflare"]; const CLOUDFLARE_IPV4: Ipv4Addr = Ipv4Addr::new(1, 1, 1, 1); const CLOUDFLARE_IPV6: Ipv6Addr = Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1111); -pub fn public_ip() -> Result<(Option, Option), Error> { +pub enum Preference { + Ipv4, + Ipv6, +} + +pub fn get_both() -> Result<(Option, Option), Error> { let ipv4 = Request::start(CLOUDFLARE_IPV4)?; let ipv6 = Request::start(CLOUDFLARE_IPV6)?; Ok((ipv4.read_response().ok(), ipv6.read_response().ok())) } +pub fn get_any(preference: Preference) -> Result, Error> { + let (v4, v6) = get_both()?; + let (v4, v6) = (v4.map(IpAddr::from), v6.map(IpAddr::from)); + Ok(match preference { + Preference::Ipv4 => v4.or(v6), + Preference::Ipv6 => v6.or(v4), + }) +} + struct Request { socket: UdpSocket, id: [u8; 2], @@ -169,7 +183,7 @@ mod tests { #[ignore] fn it_works() -> Result<(), Error> { let now = Instant::now(); - let (v4, v6) = public_ip()?; + let (v4, v6) = get_both()?; println!("Done in {}ms", now.elapsed().as_millis()); println!("v4: {:?}, v6: {:?}", v4, v6); assert!(v4.is_some()); diff --git a/server/src/initialize.rs b/server/src/initialize.rs index ea12b1b..3267978 100644 --- a/server/src/initialize.rs +++ b/server/src/initialize.rs @@ -2,6 +2,7 @@ use crate::*; use db::DatabaseCidr; use dialoguer::{theme::ColorfulTheme, Input}; use indoc::printdoc; +use publicip::Preference; use rusqlite::{params, Connection}; use shared::{ prompts, CidrContents, Endpoint, Hostname, PeerContents, PERSISTENT_KEEPALIVE_INTERVAL_SECS, @@ -105,11 +106,24 @@ pub fn init_wizard(conf: &ServerConfig, opts: InitializeOpts) -> Result<(), Erro "(are you not running as root?)".bold() ) })?; + printdoc!( + "\nTime to setup your innernet network. + + Your network name can be any hostname-valid string, i.e. \"evilcorp\", and + your network CIDR should be in the RFC1918 IPv4 (10/8, 172.16/12, or 192.168/16), + or RFC4193 IPv6 (fd00::/8) ranges. + + The external endpoint specified is a : string that is the address clients + will connect to. It's up to you to forward/open ports in your routers/firewalls + as needed. + + For more usage instructions, see https://github.com/tonarino/innernet#usage + \n" + ); let name: Hostname = if let Some(name) = opts.network_name { name } else { - println!("Here you'll specify the network CIDR, which will encompass the entire network."); Input::with_theme(&theme) .with_prompt("Network name") .interact()? @@ -130,14 +144,11 @@ pub fn init_wizard(conf: &ServerConfig, opts: InitializeOpts) -> Result<(), Erro let endpoint: Endpoint = if let Some(endpoint) = opts.external_endpoint { endpoint } else { - let (v4, v6) = publicip::public_ip()?; - let external_ip = v4.map(IpAddr::from).or(v6.map(IpAddr::from)); - if opts.auto_external_endpoint { - let ip = external_ip.ok_or("couldn't get external IP")?; + let ip = publicip::get_any(Preference::Ipv4)?.ok_or("couldn't get external IP")?; SocketAddr::new(ip, 51820).into() } else { - prompts::ask_endpoint(external_ip)? + prompts::ask_endpoint()? } }; @@ -197,7 +208,7 @@ pub fn init_wizard(conf: &ServerConfig, opts: InitializeOpts) -> Result<(), Erro " {star} Setup finished. - Network {interface} has been {created}! + Network {interface} has been {created}, but it's not started yet! Your new network starts with only one peer: this innernet server. Next, you'll want to create additional CIDRs and peers using the commands: @@ -205,7 +216,8 @@ pub fn init_wizard(conf: &ServerConfig, opts: InitializeOpts) -> Result<(), Erro {wg_manage_server} {add_cidr} {interface}, and {wg_manage_server} {add_peer} {interface} - See the documentation for more detailed instruction on designing your network. + See https://github.com/tonarino/innernet for more detailed instruction + on designing your network. When you're ready to start the network, you can auto-start the server: diff --git a/shared/src/prompts.rs b/shared/src/prompts.rs index e319c8e..8d10117 100644 --- a/shared/src/prompts.rs +++ b/shared/src/prompts.rs @@ -7,10 +7,8 @@ use colored::*; use dialoguer::{theme::ColorfulTheme, Confirm, Input, Select}; use ipnetwork::IpNetwork; use lazy_static::lazy_static; -use std::{ - net::{IpAddr, SocketAddr}, - time::SystemTime, -}; +use publicip::Preference; +use std::{net::SocketAddr, time::SystemTime}; use wgctrl::{InterfaceName, KeyPair}; lazy_static! { @@ -356,15 +354,10 @@ pub fn set_listen_port( } } -pub fn ask_endpoint(external_ip: Option) -> Result { +pub fn ask_endpoint() -> Result { println!("getting external IP address."); - let external_ip = if external_ip.is_some() { - external_ip - } else { - let (v4, v6) = publicip::public_ip()?; - v4.map(IpAddr::from).or(v6.map(IpAddr::from)) - }; + let external_ip = publicip::get_any(Preference::Ipv4)?; let mut endpoint_builder = Input::with_theme(&*THEME); if let Some(ip) = external_ip { @@ -379,11 +372,7 @@ pub fn ask_endpoint(external_ip: Option) -> Result { } pub fn override_endpoint(unset: bool) -> Result>, Error> { - let endpoint = if !unset { - Some(ask_endpoint(None)?) - } else { - None - }; + let endpoint = if !unset { Some(ask_endpoint()?) } else { None }; Ok( if Confirm::with_theme(&*THEME)