feat: add DNS-over-TLS (DoT) listener #25
66
src/dot.rs
66
src/dot.rs
@@ -60,6 +60,9 @@ fn fallback_tls(ctx: &ServerCtx) -> Option<Arc<ServerConfig>> {
|
|||||||
|
|
||||||
/// Start the DNS-over-TLS listener (RFC 7858).
|
/// Start the DNS-over-TLS listener (RFC 7858).
|
||||||
pub async fn start_dot(ctx: Arc<ServerCtx>, config: &DotConfig) {
|
pub async fn start_dot(ctx: Arc<ServerCtx>, config: &DotConfig) {
|
||||||
|
if config.cert_path.is_some() != config.key_path.is_some() {
|
||||||
|
warn!("DoT: both cert_path and key_path must be set — ignoring partial config, using self-signed");
|
||||||
|
}
|
||||||
let tls_config = match (&config.cert_path, &config.key_path) {
|
let tls_config = match (&config.cert_path, &config.key_path) {
|
||||||
(Some(cert), Some(key)) => match load_tls_config(cert, key) {
|
(Some(cert), Some(key)) => match load_tls_config(cert, key) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
@@ -68,14 +71,7 @@ pub async fn start_dot(ctx: Arc<ServerCtx>, config: &DotConfig) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(Some(_), None) | (None, Some(_)) => {
|
_ => match fallback_tls(&ctx) {
|
||||||
warn!("DoT: both cert_path and key_path must be set — ignoring partial config, using self-signed");
|
|
||||||
match fallback_tls(&ctx) {
|
|
||||||
Some(cfg) => cfg,
|
|
||||||
None => return,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(None, None) => match fallback_tls(&ctx) {
|
|
||||||
Some(cfg) => cfg,
|
Some(cfg) => cfg,
|
||||||
None => return,
|
None => return,
|
||||||
},
|
},
|
||||||
@@ -190,32 +186,15 @@ where
|
|||||||
resp.header.id = query_id;
|
resp.header.id = query_id;
|
||||||
resp.header.response = true;
|
resp.header.response = true;
|
||||||
resp.header.rescode = ResultCode::FORMERR;
|
resp.header.rescode = ResultCode::FORMERR;
|
||||||
let mut out_buf = BytePacketBuffer::new();
|
if send_response(&mut stream, &resp, remote_addr).await.is_err() {
|
||||||
if resp.write(&mut out_buf).is_err() {
|
|
||||||
debug!("DoT: failed to serialize FORMERR for {}", remote_addr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if write_framed(&mut stream, out_buf.filled()).await.is_err() {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let resp_buffer = match resolve_query(query.clone(), remote_addr, ctx).await {
|
match resolve_query(query.clone(), remote_addr, ctx).await {
|
||||||
Ok(buf) => buf,
|
Ok(resp_buffer) => {
|
||||||
Err(e) => {
|
|
||||||
warn!("{} | RESOLVE ERROR | {}", remote_addr, e);
|
|
||||||
// Build SERVFAIL that echoes the original question section.
|
|
||||||
let resp = DnsPacket::response_from(&query, ResultCode::SERVFAIL);
|
|
||||||
let mut out_buf = BytePacketBuffer::new();
|
|
||||||
if resp.write(&mut out_buf).is_err() {
|
|
||||||
debug!("DoT: failed to serialize SERVFAIL for {}", remote_addr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
out_buf
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if write_framed(&mut stream, resp_buffer.filled())
|
if write_framed(&mut stream, resp_buffer.filled())
|
||||||
.await
|
.await
|
||||||
.is_err()
|
.is_err()
|
||||||
@@ -223,6 +202,37 @@ where
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("{} | RESOLVE ERROR | {}", remote_addr, e);
|
||||||
|
// SERVFAIL that echoes the original question section.
|
||||||
|
let resp = DnsPacket::response_from(&query, ResultCode::SERVFAIL);
|
||||||
|
if send_response(&mut stream, &resp, remote_addr).await.is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize a DNS response and send it framed. Logs serialization failures
|
||||||
|
/// and returns Err so the caller can tear down the connection.
|
||||||
|
async fn send_response<S>(
|
||||||
|
stream: &mut S,
|
||||||
|
resp: &DnsPacket,
|
||||||
|
remote_addr: SocketAddr,
|
||||||
|
) -> std::io::Result<()>
|
||||||
|
where
|
||||||
|
S: AsyncWriteExt + Unpin,
|
||||||
|
{
|
||||||
|
let mut out_buf = BytePacketBuffer::new();
|
||||||
|
if resp.write(&mut out_buf).is_err() {
|
||||||
|
debug!(
|
||||||
|
"DoT: failed to serialize {:?} response for {}",
|
||||||
|
resp.header.rescode, remote_addr
|
||||||
|
);
|
||||||
|
return Err(std::io::Error::other("serialize failed"));
|
||||||
|
}
|
||||||
|
write_framed(stream, out_buf.filled()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a DNS message with its 2-byte length prefix, coalesced into one syscall.
|
/// Write a DNS message with its 2-byte length prefix, coalesced into one syscall.
|
||||||
|
|||||||
Reference in New Issue
Block a user