summaryrefslogtreecommitdiffstats
path: root/src/bin/server/tls.rs
blob: eb94637bf5cf6109d61d960337526842b0899812 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use std::io::{Read, Write};
use std::net::TcpStream;

use zagan_bet::protocol::{FrameBuffer, write_frame};

#[derive(Clone)]
pub struct TlsServer {
    acceptor: native_tls::TlsAcceptor,
}

impl TlsServer {
    pub fn new(cert_path: &str) -> Result<Self, Box<dyn std::error::Error>> {
        let cert_data = match std::fs::read(cert_path) {
            Ok(data) => data,
            Err(error) => {
                return Err(format!(
                    "Could not read TLS identity '{}': {}\n\
                     \n\
                     First-time setup:\n\
                     \x20 cargo run --bin server -- --init\n\
                     \n\
                     Or generate it manually:\n\
                     \x20 openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem \\\n\
                     \x20   -days 3650 -nodes -subj \"/CN=localhost\"\n\
                     \x20 openssl pkcs12 -export -out identity.p12 -inkey key.pem -in cert.pem \\\n\
                     \x20   -passout pass:",
                    cert_path, error
                )
                .into());
            }
        };

        let identity = native_tls::Identity::from_pkcs12(&cert_data, "")?;
        let acceptor = native_tls::TlsAcceptor::new(identity)?;

        Ok(Self { acceptor })
    }

    pub fn accept(&self, stream: TcpStream) -> Result<TlsStream, Box<dyn std::error::Error>> {
        let stream = self.acceptor.accept(stream)?;
        Ok(TlsStream::new(stream))
    }
}

pub struct TlsStream {
    stream: native_tls::TlsStream<TcpStream>,
    frames: FrameBuffer,
}

impl TlsStream {
    pub fn new(stream: native_tls::TlsStream<TcpStream>) -> Self {
        Self {
            stream,
            frames: FrameBuffer::new(),
        }
    }

    /// Read one protocol frame. `Ok(0)` is EOF with no partial frame.
    pub fn read_frame(&mut self, out: &mut String) -> std::io::Result<usize> {
        match self.frames.read_frame(&mut self.stream)? {
            Some(frame) => {
                let length = frame.len();
                out.push_str(&frame);
                Ok(length)
            }
            None => Ok(0),
        }
    }

    pub fn write_frame(&mut self, msg: &str) -> std::io::Result<()> {
        write_frame(&mut self.stream, msg)
    }

    pub fn peer_addr(&self) -> std::io::Result<std::net::SocketAddr> {
        self.stream.get_ref().peer_addr()
    }

    /// Read timeout for the socket. The connection loop uses a short one so it
    /// can advance crash rounds while no request is in flight.
    pub fn set_read_timeout(&self, duration: Option<std::time::Duration>) -> std::io::Result<()> {
        self.stream.get_ref().set_read_timeout(duration)
    }
}

impl Read for TlsStream {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        self.stream.read(buf)
    }
}

impl Write for TlsStream {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.stream.write(buf)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        self.stream.flush()
    }
}