lib.rs (4804B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024, 2025, 2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 use std::{fs::Permissions, io::ErrorKind, net::SocketAddr, os::unix::fs::PermissionsExt as _}; 18 19 use listenfd::ListenFd; 20 use socket2::{Domain, Protocol, Socket, Type}; 21 use tokio::net::{TcpListener, UnixListener}; 22 use tracing::info; 23 24 pub mod api; 25 pub mod auth; 26 pub mod config; 27 pub mod constants; 28 pub mod db; 29 pub mod error; 30 pub mod extract; 31 pub mod notification; 32 pub mod subject; 33 #[cfg(test)] 34 pub mod test; 35 36 #[derive(Debug, Clone)] 37 pub enum Serve { 38 Tcp(SocketAddr), 39 Unix { 40 path: String, 41 permission: Permissions, 42 }, 43 Systemd, 44 } 45 46 impl Serve { 47 /// Resolve listener from a config and environment 48 fn resolve(&self) -> Result<Listener, std::io::Error> { 49 match self { 50 Serve::Tcp(socket_addr) => { 51 info!(target: "api", "Server listening on {socket_addr}"); 52 let listener = bind_tcp(socket_addr)?; 53 listener.set_nonblocking(true)?; 54 Ok(Listener::Tcp(TcpListener::from_std(listener)?)) 55 } 56 Serve::Unix { path, permission } => { 57 info!(target: "api", 58 "Server listening on unxis domain socket {path} {:o}", 59 permission.mode() 60 ); 61 if let Err(e) = std::fs::remove_file(path) { 62 let kind = e.kind(); 63 if kind != ErrorKind::NotFound { 64 return Err(e); 65 } 66 } 67 let listener = std::os::unix::net::UnixListener::bind(path)?; 68 std::fs::set_permissions(path, permission.clone())?; 69 listener.set_nonblocking(true)?; 70 Ok(Listener::Unix(UnixListener::from_std(listener)?)) 71 } 72 Serve::Systemd => { 73 let mut listenfd = ListenFd::from_env(); 74 if let Ok(Some(unix)) = listenfd.take_unix_listener(0) { 75 info!(target: "api", 76 "Server listening on activated unix socket {:?}", 77 unix.local_addr()? 78 ); 79 unix.set_nonblocking(true)?; 80 Ok(Listener::Unix(UnixListener::from_std(unix)?)) 81 } else if let Ok(Some(tcp)) = listenfd.take_tcp_listener(0) { 82 info!(target: "api", 83 "Server listening on activated TCP socket {:?}", 84 tcp.local_addr()? 85 ); 86 tcp.set_nonblocking(true)?; 87 Ok(Listener::Tcp(TcpListener::from_std(tcp)?)) 88 } else { 89 Err(std::io::Error::other("Missing systemd activated socket")) 90 } 91 } 92 } 93 } 94 } 95 96 /// Listening backlog, the same value `std::net::TcpListener::bind` uses. 97 const LISTEN_BACKLOG: i32 = 128; 98 99 /// Bind a listening TCP socket to `addr`. 100 /// 101 /// Like `std::net::TcpListener::bind`, except that a socket for an IPv6 102 /// address gets `IPV6_V6ONLY` cleared explicitly, so that the wildcard address 103 /// `::` serves IPv4 clients as well (as v4-mapped addresses) instead of 104 /// leaving that to the `net.ipv6.bindv6only` sysctl. Without this a host whose 105 /// resolver returns the IPv6 address of a name first -- which every dual-stack 106 /// `localhost` does -- can only reach us if the operator happened to pick the 107 /// matching address family in `BIND_TO`. The C components have no such 108 /// asymmetry: they resolve `BIND_TO` with `getaddrinfo()` and `AI_PASSIVE` and 109 /// listen on every address it returns. 110 fn bind_tcp(addr: &SocketAddr) -> Result<std::net::TcpListener, std::io::Error> { 111 let domain = Domain::for_address(*addr); 112 let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?; 113 if domain == Domain::IPV6 { 114 socket.set_only_v6(false)?; 115 } 116 // std sets this on unix too, keep the behaviour on restart 117 socket.set_reuse_address(true)?; 118 socket.bind(&(*addr).into())?; 119 socket.listen(LISTEN_BACKLOG)?; 120 Ok(socket.into()) 121 } 122 123 enum Listener { 124 Tcp(TcpListener), 125 Unix(UnixListener), 126 }