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::{
22 Server,
23 net::runtime::TokioRuntimeProvider,
24 store::in_memory::InMemoryZoneHandler,
25 zone_handler::{AxfrPolicy, Catalog, ZoneType},
26};
27use tokio::net::UdpSocket;
28
29const DNS_TCP_RESPONSE_BUFFER_SIZE:usize = 65_535;
36
37pub fn BuildCatalog(_DNSPort:u16) -> Result<Catalog> {
42 let mut Catalog = Catalog::new();
43
44 let EditorLandOrigin = hickory_proto::rr::Name::from_ascii("editor.land.").unwrap();
45
46 let Authority = InMemoryZoneHandler::<TokioRuntimeProvider>::empty(
53 EditorLandOrigin.clone(),
54 ZoneType::Primary,
55 AxfrPolicy::Deny,
56 None,
57 );
58
59 let EditorLandLower = hickory_proto::rr::LowerName::from(&EditorLandOrigin);
60 let AuthorityArc = Arc::new(Authority);
61 Catalog.upsert(EditorLandLower, vec![AuthorityArc]);
62
63 Ok(Catalog)
64}
65
66pub async fn Serve(Catalog:Catalog, Port:u16) -> Result<()> {
71 let Address:SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), Port);
72
73 let BindingIP = Address.ip();
74 match BindingIP {
75 IpAddr::V4(IP) => {
76 if !IP.is_loopback() {
77 return Err(anyhow::anyhow!(
78 "SECURITY: DNS server attempted to bind to non-loopback address: {}. Only 127.x.x.x addresses are \
79 allowed.",
80 IP
81 ));
82 }
83 },
84 IpAddr::V6(IP) if IP.is_loopback() => {},
85 _ => {
86 return Err(anyhow::anyhow!(
87 "SECURITY: DNS server attempted to bind to invalid address: {}. Only loopback addresses are allowed.",
88 BindingIP
89 ));
90 },
91 }
92
93 tracing::info!("Binding DNS server to loopback address: {}", Address);
94
95 let UDPSocket = UdpSocket::bind(Address)
96 .await
97 .map_err(|E| anyhow::anyhow!("SECURITY: Failed to bind DNS server to {}: {}.", Address, E))?;
98
99 let BoundAddress = UDPSocket
100 .local_addr()
101 .map_err(|E| anyhow::anyhow!("SECURITY: Failed to retrieve bound socket address: {}", E))?;
102
103 if !BoundAddress.ip().is_loopback() {
104 return Err(anyhow::anyhow!(
105 "SECURITY: UDP socket bound to non-loopback address: {}.",
106 BoundAddress.ip()
107 ));
108 }
109
110 let mut Server = Server::new(Catalog);
114 Server.register_socket(UDPSocket);
115
116 let TCPListener = tokio::net::TcpListener::bind(Address)
117 .await
118 .map_err(|E| anyhow::anyhow!("SECURITY: Failed to bind TCP listener to {}: {}", Address, E))?;
119
120 let TCPBoundAddress = TCPListener
121 .local_addr()
122 .map_err(|E| anyhow::anyhow!("SECURITY: Failed to retrieve TCP listener bound address: {}", E))?;
123
124 if !TCPBoundAddress.ip().is_loopback() {
125 return Err(anyhow::anyhow!(
126 "SECURITY: TCP listener bound to non-loopback address: {}.",
127 TCPBoundAddress.ip()
128 ));
129 }
130
131 Server.register_listener(TCPListener, std::time::Duration::from_secs(5), DNS_TCP_RESPONSE_BUFFER_SIZE);
132
133 tracing::info!("DNS server bound to loopback: UDP={}, TCP={}", BoundAddress, TCPBoundAddress);
134
135 match Server.block_until_done().await {
136 Ok(_) => {
137 tracing::info!("DNS server shutdown gracefully");
138 Ok(())
139 },
140 Err(E) => {
141 let ErrorMessage = format!("DNS server error: {:?}", E);
142 tracing::error!("{}", ErrorMessage);
143 Err(anyhow::anyhow!(ErrorMessage))
144 },
145 }
146}
147
148pub fn ServeSync(Catalog:Catalog, Port:u16) -> Result<()> {
150 let Runtime = tokio::runtime::Runtime::new()?;
151 Runtime.block_on(Serve(Catalog, Port))?;
152 Ok(())
153}
154
155#[cfg(test)]
156mod tests {
157 use hickory_proto::rr::Name;
158
159 use super::*;
160
161 #[test]
162 fn TestBuildCatalog() { let _Catalog = BuildCatalog(5353).expect("Failed to build catalog"); }
163
164 #[test]
165 fn TestSocketAddressIsLoopback() {
166 let Address:SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 5353);
167 assert!(Address.ip().is_loopback());
168 }
169}