summaryrefslogtreecommitdiffstats
path: root/src/bin/server/tls.rs
blob: 62a25db0f215e23e95814a68c11bc7c89c89a6ab (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
use std::io::{Read, Write};
use std::net::TcpStream;

use zagan_bet::protocol::LineBuffer;

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

impl TlsServer {
    pub fn new(
        cert_path: &str,
        _key_path: &str,
        unsafe_mode: bool,
    ) -> Result<Self, Box<dyn std::error::Error>> {
        let cert_data = if unsafe_mode {
            if std::path::Path::new(cert_path).exists() {
                std::fs::read(cert_path)?
            } else {
                return Err(format!(
                    "Certificate file '{}' not found. In --unsafe mode, you must provide a valid PKCS12 certificate file.\n\
                     You can generate one with:\n\
                     openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes\n\
                     openssl pkcs12 -export -out identity.p12 -inkey key.pem -in cert.pem",
                    cert_path
                ).into());
            }
        } else {
            std::fs::read(cert_path)?
        };

        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>,
    lines: LineBuffer,
}

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

    pub fn read_line(&mut self, s: &mut String) -> std::io::Result<usize> {
        match self.lines.read_line(&mut self.stream)? {
            Some(line) => {
                s.push_str(&line);
                s.push('\n');
                Ok(line.len() + 1)
            }
            None => Ok(0),
        }
    }

    pub fn write_line(&mut self, msg: &str) -> std::io::Result<()> {
        use std::io::Write;
        self.stream.write_all(msg.as_bytes())?;
        self.stream.write_all(b"\n")?;
        self.stream.flush()?;
        Ok(())
    }

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

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()
    }
}