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