pai-sho 0.4.0

Drop one binary on a box with no way in. It dials home and punches through, so its ports show up on your laptop, each under its own name. Encrypted, peer-to-peer, default deny.
//! CLI client - sends commands to daemon over Unix socket.

use crate::protocol::{Request, Response};
use crate::Command;
use anyhow::{Context, Result};
use std::path::Path;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::UnixStream;

pub async fn send_command(socket_path: &Path, command: Command) -> Result<()> {
    let request = match command {
        Command::AddPeer { ticket } => Request::AddPeer { ticket },
        Command::RemovePeer { ticket } => Request::RemovePeer { ticket },
        Command::Expose { port, to } => Request::Expose { port, to },
        Command::Unexpose { port, to } => Request::Unexpose { port, to },
        Command::List => Request::List,
        Command::Ticket => Request::Ticket,
        Command::GrantToken { label } => Request::GrantToken { label },
        Command::Pin { key, label } => Request::Pin { key, label },
        Command::Project { peer, ip, name } => Request::Project {
            peer,
            ip: ip.map(|ip| ip.to_string()),
            name,
        },
        Command::Unproject { peer } => Request::Unproject { peer },
        Command::Surfaces => Request::Surfaces,
        Command::Daemon { .. } => unreachable!("daemon handled separately"),
    };

    let stream = UnixStream::connect(socket_path)
        .await
        .context("failed to connect to daemon - is it running?")?;

    let (reader, mut writer) = stream.into_split();
    let mut reader = BufReader::new(reader);

    // Send request
    let request_json = serde_json::to_string(&request)?;
    writer.write_all(request_json.as_bytes()).await?;
    writer.write_all(b"\n").await?;

    // Read response
    let mut line = String::new();
    reader.read_line(&mut line).await?;
    let response: Response = serde_json::from_str(&line)?;

    // Print response
    match response {
        Response::Ok => println!("OK"),
        Response::Ticket(ticket) => println!("{}", ticket),
        Response::Token(token) => println!("{}", token),
        Response::List(info) => {
            println!("{}", serde_json::to_string_pretty(&info)?);
        }
        Response::Surfaces(surfaces) => {
            println!("{}", serde_json::to_string_pretty(&surfaces)?);
        }
        Response::Error(e) => {
            eprintln!("Error: {}", e);
            std::process::exit(1);
        }
    }

    Ok(())
}