1#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
2use std::{
8 net::{IpAddr, Ipv4Addr, SocketAddr},
9 sync::Arc,
10};
11
12use anyhow::Result;
13use hickory_server::{
14 authority::{Catalog, ZoneType},
15 server::ServerFuture,
16 store::in_memory::InMemoryAuthority,
17};
18use tokio::net::UdpSocket;
19
20pub fn BuildCatalog(_DNSPort:u16) -> Result<Catalog> {
25 let mut Catalog = Catalog::new();
26
27 let EditorLandOrigin = hickory_proto::rr::Name::from_ascii("editor.land.").unwrap();
28
29 let Authority = InMemoryAuthority::empty(EditorLandOrigin.clone(), ZoneType::Primary, false, None);
30
31 let EditorLandLower = hickory_proto::rr::LowerName::from(&EditorLandOrigin);
32 let AuthorityArc = Arc::new(Authority);
33 Catalog.upsert(EditorLandLower, vec![AuthorityArc]);
34
35 Ok(Catalog)
36}
37
38pub async fn Serve(Catalog:Catalog, Port:u16) -> Result<()> {
43 let Address:SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), Port);
44
45 let BindingIP = Address.ip();
46 match BindingIP {
47 IpAddr::V4(IP) => {
48 if !IP.is_loopback() {
49 return Err(anyhow::anyhow!(
50 "SECURITY: DNS server attempted to bind to non-loopback address: {}. Only 127.x.x.x addresses are \
51 allowed.",
52 IP
53 ));
54 }
55 },
56 IpAddr::V6(IP) if IP.is_loopback() => {},
57 _ => {
58 return Err(anyhow::anyhow!(
59 "SECURITY: DNS server attempted to bind to invalid address: {}. Only loopback addresses are allowed.",
60 BindingIP
61 ));
62 },
63 }
64
65 tracing::info!("Binding DNS server to loopback address: {}", Address);
66
67 let UDPSocket = UdpSocket::bind(Address)
68 .await
69 .map_err(|E| anyhow::anyhow!("SECURITY: Failed to bind DNS server to {}: {}.", Address, E))?;
70
71 let BoundAddress = UDPSocket
72 .local_addr()
73 .map_err(|E| anyhow::anyhow!("SECURITY: Failed to retrieve bound socket address: {}", E))?;
74
75 if !BoundAddress.ip().is_loopback() {
76 return Err(anyhow::anyhow!(
77 "SECURITY: UDP socket bound to non-loopback address: {}.",
78 BoundAddress.ip()
79 ));
80 }
81
82 let mut Server = ServerFuture::new(Catalog);
83 Server.register_socket(UDPSocket);
84
85 let TCPListener = tokio::net::TcpListener::bind(Address)
86 .await
87 .map_err(|E| anyhow::anyhow!("SECURITY: Failed to bind TCP listener to {}: {}", Address, E))?;
88
89 let TCPBoundAddress = TCPListener
90 .local_addr()
91 .map_err(|E| anyhow::anyhow!("SECURITY: Failed to retrieve TCP listener bound address: {}", E))?;
92
93 if !TCPBoundAddress.ip().is_loopback() {
94 return Err(anyhow::anyhow!(
95 "SECURITY: TCP listener bound to non-loopback address: {}.",
96 TCPBoundAddress.ip()
97 ));
98 }
99
100 Server.register_listener(TCPListener, std::time::Duration::from_secs(5));
101
102 tracing::info!("DNS server bound to loopback: UDP={}, TCP={}", BoundAddress, TCPBoundAddress);
103
104 match Server.block_until_done().await {
105 Ok(_) => {
106 tracing::info!("DNS server shutdown gracefully");
107 Ok(())
108 },
109 Err(E) => {
110 let ErrorMessage = format!("DNS server error: {:?}", E);
111 tracing::error!("{}", ErrorMessage);
112 Err(anyhow::anyhow!(ErrorMessage))
113 },
114 }
115}
116
117pub fn ServeSync(Catalog:Catalog, Port:u16) -> Result<()> {
119 let Runtime = tokio::runtime::Runtime::new()?;
120 Runtime.block_on(Serve(Catalog, Port))?;
121 Ok(())
122}
123
124#[cfg(test)]
125mod tests {
126 use hickory_proto::rr::Name;
127
128 use super::*;
129
130 #[test]
131 fn TestBuildCatalog() { let _Catalog = BuildCatalog(5353).expect("Failed to build catalog"); }
132
133 #[test]
134 fn TestSocketAddressIsLoopback() {
135 let Address:SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 5353);
136 assert!(Address.ip().is_loopback());
137 }
138}