~jshholland/reltci

d38dfe8ead87bdc2d46f28a5a3d698b45380363e — Josh Holland 2 years ago 40584c7
Flesh out Gemini server

Now handles more stuff more carefully, including checking we are
responding to the right scheme and hostname.
2 files changed, 47 insertions(+), 26 deletions(-)

M src/gemini.rs
M src/main.rs
M src/gemini.rs => src/gemini.rs +45 -25
@@ 114,7 114,6 @@ impl Request {
            }
        };
        let req = std::str::from_utf8(req)?;
        // TODO(2020-09-16) don't just regenerate the base url every time
        let url = Url::parse(req)?;
        if url.scheme() != "gemini" {
            return Err(RequestParseError::InvalidScheme);


@@ 147,34 146,55 @@ impl Response {
    }
}

fn handle_conn(mut stream: TlsStream<TcpStream>) -> anyhow::Result<()> {
    let url = match Request::parse(&mut stream) {
        Ok(Request { url }) => url,
        Err(e) => {
#[derive(Debug, Clone)]
pub struct Server {
    /// The hostname we are serving content for.
    pub hostname: String,
}

impl Server {
    pub fn new(hostname: String) -> Self {
        Server { hostname }
    }

    pub fn run(&mut self) -> anyhow::Result<()> {
        let id = Identity::from_pkcs12(&std::fs::read("identity.pfx")?, "")?;
        let listener = TcpListener::bind("localhost:1965")?;
        let acceptor = TlsAcceptor::new(id)?;
        for stream in listener.incoming() {
            let accepted = acceptor.accept(stream?)?;
            if let Err(e) = self.handle_conn(accepted) {
                eprintln!("error handling request: {}", e);
            }
        }
        Ok(())
    }

    fn handle_conn(&self, mut stream: TlsStream<TcpStream>) -> anyhow::Result<()> {
        let url = match Request::parse(&mut stream) {
            Ok(Request { url }) => url,
            Err(e) => {
                let resp = Response {
                    status: Status::TempFail,
                    meta: format!("couldn't parse request: {}", e),
                    body: Vec::new(),
                };
                resp.send(&mut stream)?;
                return Err(e.into());
            }
        };
        if url.host_str().unwrap() != self.hostname {
            let resp = Response {
                status: Status::TempFail,
                meta: format!("couldn't parse request: {}", e),
                status: Status::ProxyRefused,
                meta: format!("will only serve requests for {}", self.hostname),
                body: Vec::new(),
            };
            resp.send(&mut stream)?;
            return Err(e.into());
        }
    };
    let body = format!("Hello, Gemini!  You requested the URL <{}>", url);
    let resp = Response::success("text/plain".to_string(), body.into_bytes());
    resp.send(&mut stream)?;
    Ok(())
}

pub fn run() -> anyhow::Result<()> {
    let id = Identity::from_pkcs12(&std::fs::read("identity.pfx")?, "")?;
    let listener = TcpListener::bind("localhost:1965")?;
    let acceptor = TlsAcceptor::new(id)?;
    for stream in listener.incoming() {
        let accepted = acceptor.accept(stream?)?;
        if let Err(e) = handle_conn(accepted) {
            eprintln!("error handling request: {}", e);
            anyhow::bail!("invalid proxy request");
        }
        let body = format!("Hello, Gemini!  You requested the URL <{}>", url);
        let resp = Response::success("text/plain".to_string(), body.into_bytes());
        resp.send(&mut stream)?;
        Ok(())
    }
    Ok(())
}

M src/main.rs => src/main.rs +2 -1
@@ 2,5 2,6 @@ mod config;
mod gemini;

fn main() -> anyhow::Result<()> {
    gemini::run()
    let mut server = gemini::Server::new("localhost".to_string());
    server.run()
}