pub mod client_config;
pub mod netstack;
pub mod wireguard;
use std::future::Future;
use std::io;
use std::net::SocketAddr;
use std::pin::Pin;
use std::time::Duration;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::config::profile::{self, Profile};
use crate::keys::store::DeviceSecrets;
pub const CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
pub trait Stream: AsyncRead + AsyncWrite + Send + Unpin {}
impl<T: AsyncRead + AsyncWrite + Send + Unpin> Stream for T {}
pub type ConnectFuture<'a> =
Pin<Box<dyn Future<Output = Result<Box<dyn Stream>, TransportError>> + Send + 'a>>;
pub trait Transport: Send + Sync {
fn connect(&self, target: SocketAddr) -> ConnectFuture<'_>;
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum TransportError {
#[error(
"profile `{profile}` has no tunnel address. Once the server has this device \
(see `gitaway peer {profile}`), set it with \
`gitaway profile set {profile} transport.wireguard.address <address>/32`"
)]
NoAddress {
profile: String,
},
#[error(
"profile `{profile}`'s server public key is this device's own public key. Set the \
server's key, the PublicKey under [Peer] in a client config: \
`gitaway profile set {profile} transport.wireguard.server_public_key <key>`"
)]
OwnKey {
profile: String,
},
#[error("couldn't find the VPN server {endpoint}: {reason}")]
Endpoint {
endpoint: String,
reason: String,
},
#[error("couldn't start the tunnel: {0}")]
Setup(String),
#[error(
"no answer from the VPN server at {endpoint} within {} seconds. Check that the \
profile's server public key is right, that the server has this device's public key \
(`gitaway peer`), and that UDP port {} reaches it from here",
CONNECT_TIMEOUT.as_secs(),
endpoint.port()
)]
NoHandshake {
endpoint: SocketAddr,
},
#[error(
"the VPN server answered, but {target} didn't within {} seconds. Check that the \
server can reach {target} (the right-hand side of the profile's `[hosts]` entry) \
and allows this device to reach it",
CONNECT_TIMEOUT.as_secs()
)]
Timeout {
target: SocketAddr,
},
#[error("couldn't connect to {target} through the tunnel: {source}")]
Connect {
target: SocketAddr,
source: io::Error,
},
}
pub async fn open(
name: &str,
profile: &Profile,
secrets: DeviceSecrets,
) -> Result<Box<dyn Transport>, TransportError> {
match &profile.transport {
profile::Transport::WireGuard(settings) => Ok(Box::new(
wireguard::WireGuardTransport::open(name, settings, secrets).await?,
)),
}
}