Commit 7a258ce7 authored by Kaiden Fey's avatar Kaiden Fey

client/hubd: implementing upnp setup

parent a8d4677e
This diff is collapsed.
[package]
name = "qaul-hubd"
description = "A modular and configurable internet overlay router for qaul.net"
version = "0.1.0"
description = "A modular and easy to use internet overlay router for qaul.net"
version = "0.2.0"
authors = ["Katharina Fey <kookie@spacekookie>", "Leonora Tindall <nora@nora.codes>"]
license = "AGPL-3.0-or-later"
edition = "2018"
......@@ -16,5 +16,8 @@ ratman-configure = { path = "../../ratman/configure" }
async-std = { version = "=1.5", features = ["attributes"] }
clap = { version = "2.0", features = ["wrap_help", "color"] }
directories = "2.0"
igd = "0.11"
tracing = "0.1"
tracing-subscriber = { version = "0.2", features = ["fmt", "env-filter"] }
\ No newline at end of file
tracing-subscriber = { version = "0.2", features = ["fmt", "env-filter"] }
pnet = "0.26"
ipnetwork = "0.16"
\ No newline at end of file
......@@ -14,13 +14,18 @@ pub(crate) struct Config {
pub(crate) addr: String,
/// What port to bind on
pub(crate) port: u16,
/// Disable upnp port forwarding
pub(crate) no_upnp: bool,
/// Disable multicast local discovery
pub(crate) no_multicast: bool,
}
impl Config {
/// Consume the application config into a fully initialised router
pub(crate) fn into_router(self) -> Arc<Router> {
let mut buf = String::new();
let mut f = File::open(self.peers).expect("Peers configuration not found!");
let mut f = File::open(self.peers)
.unwrap_or_else(|_| crate::elog("Peers configuration not found!", 128));
f.read_to_string(&mut buf).unwrap();
let ep = Endpoint {
......@@ -30,7 +35,10 @@ impl Config {
port: self.port,
peers: buf
.split("\n")
.map(|s| s.parse().expect("Invalid peer port-address format!"))
.map(|s| {
s.parse()
.unwrap_or_else(|_| crate::elog("Invalid peer port-address format!", 2))
})
.collect(),
dynamic: false,
},
......@@ -50,8 +58,8 @@ impl Config {
pub(crate) fn cli<'a>() -> App<'a, 'a> {
App::new("qaul-hubd")
.about("Routing and state handling daemon for qaul.net and ratman networks")
.version("0.1.0")
.about(env!("CARGO_PKG_DESCRIPTION"))
.version(env!("CARGO_PKG_VERSION"))
.arg(
Arg::with_name("PEERS_PATH")
.short("p")
......@@ -89,6 +97,15 @@ pub(crate) fn cli<'a>() -> App<'a, 'a> {
.value_name("PORT")
.help("The hub's bound socket port"),
)
.arg(
Arg::with_name("NO_UPNP")
.long("no-upnp")
.help("Disable automatic UPNP port forwarding")
).arg(
Arg::with_name("NO_UDP_DISCOVER")
.long("no-udp-discover")
.help("Prevent qaul-hubd from registering a multicast address to find other clients on the same network")
)
}
/// Generate an application config from arguments and env vars
......@@ -124,5 +141,7 @@ pub(crate) fn match_fold<'a>(app: App<'a, 'a>) -> Config {
.ok()
.map(|s| str::parse(&s).unwrap()))
.unwrap_or(9001),
no_upnp: m.is_present("NO_UPNP"),
no_multicast: m.is_present("NO_UDP_DISCOVER"),
}
}
//! # qaul-hubd server
//!
//! A modular and configurable internet overlay server for qaul.net.
//! A modular and configurable internet overlay server for qaul.net.
mod cfg;
mod state;
mod log;
mod state;
mod upnp;
use async_std::{future, task::Poll};
use state::State;
pub(crate) fn elog<S: Into<String>>(msg: S, code: u16) -> ! {
tracing::error!("{}", msg.into());
std::process::exit(code.into());
}
#[async_std::main]
async fn main() {
log::parse_log_level();
let app = cfg::cli();
let cfg = cfg::match_fold(app);
let _state = State::new(&cfg).await;
// !no_upnp means upnp has _not_ been disabled
if !cfg.no_upnp {
upnp::open_port(cfg.port);
}
// Never return the main thread or it all dies
let _: () = future::poll_fn(|_| Poll::Pending).await;
}
//! Automatically forward a port to qaul-hubd to allow easy reverse
//! connections form a public router.
use igd::{search_gateway, Gateway, PortMappingProtocol as Protocol};
use ipnetwork::IpNetwork;
use pnet::datalink;
use std::io::Read;
use std::net::{Ipv4Addr, SocketAddrV4, TcpListener};
use tracing::trace;
//fn ip_is_local(ip: IpV4Addr)
/// Check if an IP is in one of the local IP ranges
///
/// We perform this check to see if an IP address _could_ be one that
/// a gateway can reach. This auto-detection can go wrong though if
/// there are multiple address spaces availabe (for example via a VPN)
fn check_local(ip: &Ipv4Addr) -> bool {
let [a, b, c, d] = ip.octets();
match (a, b, c, d) {
(10, _, _, _) => true,
(192, 168, _, _) => true,
(172, n, _, _) if n > 16 && n < 31 => true,
(_, _, _, _) => false,
}
}
fn find_local_ip() -> Option<Ipv4Addr> {
datalink::interfaces()
.into_iter()
.map(|_if| _if.ips)
.fold(None, |res, ips| {
let mut new = res;
for ip in ips {
use IpNetwork::*;
match (res, ip) {
(None, V4(n)) if check_local(&n.ip()) => {
new = Some(n.ip());
break;
}
(_, _) => {}
}
}
new
})
}
pub(crate) fn open_port(port: u16) {
let gw = search_gateway(Default::default()).unwrap();
let ip = gw.get_external_ip().unwrap();
trace!("Publicly accessible via: {}", ip);
let local_ip =
find_local_ip().unwrap_or_else(|| crate::elog("Couldn't find IP to bind to", 128));
trace!("Local ip: {}", local_ip);
let local_addr = SocketAddrV4::new(local_ip, 8080);
gw.add_port(Protocol::TCP, port, local_addr, 0, "qaul-hubd tcp driver")
.unwrap_or_else(|e| crate::elog(format!("{:?}", e), 128));
trace!("UPNP port {} opened with infinite lease!", port);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment