Skip to content

Commit

Permalink
[LLT-4202] Make lookup return error in case of failure on forward
Browse files Browse the repository at this point in the history
Make the forward error visible instead of just filling the response
buffer with an empty response.
  • Loading branch information
dfetti committed Nov 7, 2023
1 parent 887bba2 commit ffcddd5
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 52 deletions.
25 changes: 23 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

80 changes: 44 additions & 36 deletions crates/server/src/authority/catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,15 @@ impl RequestHandler for Catalog {
MessageType::Query => match request.op_code() {
OpCode::Query => {
debug!("query received: {}", request.id());
let info = self.lookup(request, response_edns, response_handle).await;
let info = self
.lookup(request, response_edns, response_handle)
.await
.unwrap_or_else(|e| match e {
LookupError::ResponseCode(code) => {
ResponseInfo::response_code_error(request.header().id(), code)
}
_ => ResponseInfo::unknown(request.header().id()),
});

Ok(info)
}
Expand Down Expand Up @@ -344,7 +352,7 @@ impl Catalog {
request: &Request,
response_edns: Option<Edns>,
response_handle: R,
) -> ResponseInfo {
) -> Result<ResponseInfo, LookupError> {
let request_info = request.request_info();
let authority = self.find(request_info.query.name());

Expand All @@ -363,20 +371,18 @@ impl Catalog {
// if this is empty then the there are no authorities registered that can handle the request
let response = MessageResponseBuilder::new(Some(request.raw_query()));

let result = send_response(
send_response(
response_edns,
response.error_msg(request.header(), ResponseCode::Refused),
response_handle,
)
.await;
.await
.map_err(|e| {
debug!("failed to send response: {}", e);
LookupError::Io(e)
})?;

match result {
Err(e) => {
debug!("failed to send response: {}", e);
ResponseInfo::serve_failed()
}
Ok(r) => r,
}
Err(LookupError::ResponseCode(ResponseCode::Refused))
}
}

Expand All @@ -403,7 +409,7 @@ async fn lookup<'a, R: ResponseHandler + Unpin>(
request: &Request,
response_edns: Option<Edns>,
response_handle: R,
) -> ResponseInfo {
) -> Result<ResponseInfo, LookupError> {
let query = request_info.query;
debug!(
"request: {} found authority: {}",
Expand All @@ -419,7 +425,7 @@ async fn lookup<'a, R: ResponseHandler + Unpin>(
query,
request.edns(),
)
.await;
.await?;

let response = MessageResponseBuilder::new(Some(request.raw_query())).build(
response_header,
Expand All @@ -429,15 +435,12 @@ async fn lookup<'a, R: ResponseHandler + Unpin>(
sections.additionals.iter(),
);

let result = send_response(response_edns.clone(), response, response_handle.clone()).await;

match result {
Err(e) => {
debug!("error sending response: {}", e);
ResponseInfo::serve_failed()
}
Ok(i) => i,
}
send_response(response_edns.clone(), response, response_handle.clone())
.await
.map_err(|e| {
debug!("failed to send response: {}", e);
LookupError::Io(e)
})
}

#[allow(unused_variables)]
Expand Down Expand Up @@ -471,7 +474,7 @@ async fn build_response(
request_header: &Header,
query: &LowerQuery,
edns: Option<&Edns>,
) -> (Header, LookupSections) {
) -> Result<(Header, LookupSections), LookupError> {
let lookup_options = lookup_options_for_edns(edns);

// log algorithms being requested
Expand Down Expand Up @@ -499,14 +502,14 @@ async fn build_response(
request_id,
query,
)
.await
.await?
}
ZoneType::Forward | ZoneType::Hint => {
send_forwarded_response(future, request_header, &mut response_header).await
send_forwarded_response(future, request_header, &mut response_header).await?
}
};

(response_header, sections)
Ok((response_header, sections))
}

async fn send_authoritative_response(
Expand All @@ -516,7 +519,7 @@ async fn send_authoritative_response(
lookup_options: LookupOptions,
request_id: u16,
query: &LowerQuery,
) -> LookupSections {
) -> Result<LookupSections, LookupError> {
// In this state we await the records, on success we transition to getting
// NS records, which indicate an authoritative response.
//
Expand All @@ -531,12 +534,12 @@ async fn send_authoritative_response(
// TODO: there are probably other error cases that should just drop through (FormErr, ServFail)
Err(LookupError::ResponseCode(ResponseCode::Refused)) => {
response_header.set_response_code(ResponseCode::Refused);
return LookupSections {
return Ok(LookupSections {
answers: Box::<AuthLookup>::default(),
ns: Box::<AuthLookup>::default(),
soa: Box::<AuthLookup>::default(),
additionals: Box::<AuthLookup>::default(),
};
});
}
Err(e) => {
if e.is_nx_domain() {
Expand Down Expand Up @@ -605,19 +608,19 @@ async fn send_authoritative_response(
),
};

LookupSections {
Ok(LookupSections {
answers,
ns: ns.unwrap_or_else(|| Box::<AuthLookup>::default()),
soa: soa.unwrap_or_else(|| Box::<AuthLookup>::default()),
additionals,
}
})
}

async fn send_forwarded_response(
future: impl Future<Output = Result<Box<dyn LookupObject>, LookupError>>,
request_header: &Header,
response_header: &mut Header,
) -> LookupSections {
) -> Result<LookupSections, LookupError> {
response_header.set_recursion_available(true);
response_header.set_authoritative(false);

Expand All @@ -632,26 +635,31 @@ async fn send_forwarded_response(
request_header.id()
);

Box::new(EmptyLookup)
return Err(LookupError::ResponseCode(ResponseCode::Refused));
} else {
match future.await {
Err(e) => {
debug!("error resolving: {}", e);
if e.is_io() || e.is_unknown() {
return Err(e);
}

// If the server responds with NXDomain we want to copy this behavior
if e.is_nx_domain() {
response_header.set_response_code(ResponseCode::NXDomain);
}
debug!("error resolving: {}", e);
Box::new(EmptyLookup)
}
Ok(rsp) => rsp,
}
};

LookupSections {
Ok(LookupSections {
answers,
ns: Box::<AuthLookup>::default(),
soa: Box::<AuthLookup>::default(),
additionals: Box::<AuthLookup>::default(),
}
})
}

struct LookupSections {
Expand Down
5 changes: 5 additions & 0 deletions crates/server/src/authority/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ impl LookupError {
pub fn is_refused(&self) -> bool {
matches!(*self, Self::ResponseCode(ResponseCode::Refused))
}

/// This is an unknown error
pub fn is_unknown(&self) -> bool {
matches!(*self, Self::ResponseCode(ResponseCode::Unknown(_)))
}
}

impl From<ResponseCode> for LookupError {
Expand Down
14 changes: 14 additions & 0 deletions crates/server/src/server/request_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,20 @@ impl ResponseInfo {
header.set_response_code(ResponseCode::ServFail);
header.into()
}

pub(crate) fn response_code_error(id: u16, code: ResponseCode) -> Self {
let mut header = Header::new();
header.set_id(id);
header.set_response_code(code);
header.into()
}

pub(crate) fn unknown(id: u16) -> Self {
let mut header = Header::new();
header.set_id(id);
header.set_response_code(ResponseCode::Unknown(0));
header.into()
}
}

impl From<Header> for ResponseInfo {
Expand Down
2 changes: 0 additions & 2 deletions crates/server/src/store/sqlite/authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,6 @@ impl SqliteAuthority {
#[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
#[allow(clippy::blocks_in_if_conditions)]
pub async fn authorize(&self, update_message: &MessageRequest) -> UpdateResult<()> {
use tracing::debug;

// 3.3.3 - Pseudocode for Permission Checking
//
// if (security policy exists)
Expand Down
6 changes: 3 additions & 3 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ test feature='' ignore='':
cargo ws exec {{ignore}} cargo {{MSRV}} test --all-targets --benches --examples --bins --tests {{feature}}

# This tests compatibility with BIND9, TODO: support other feature sets besides openssl for tests
compatibility: init-bind9
cargo test --manifest-path tests/compatibility-tests/Cargo.toml --all-targets --benches --examples --bins --tests --no-default-features --features=none;
cargo test --manifest-path tests/compatibility-tests/Cargo.toml --all-targets --benches --examples --bins --tests --no-default-features --features=bind;
# compatibility: init-bind9
# cargo test --manifest-path tests/compatibility-tests/Cargo.toml --all-targets --benches --examples --bins --tests --no-default-features --features=none;
# cargo test --manifest-path tests/compatibility-tests/Cargo.toml --all-targets --benches --examples --bins --tests --no-default-features --features=bind;

# Build all bench marking tools, i.e. check that they work, but don't run
build-bench:
Expand Down
18 changes: 9 additions & 9 deletions tests/integration-tests/tests/catalog_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ async fn test_catalog_lookup() {
let question_req = Request::new(question_req, ([127, 0, 0, 1], 5553).into(), Protocol::Udp);

let response_handler = TestResponseHandler::new();
catalog
let _ = catalog
.lookup(&question_req, None, response_handler.clone())
.await;
let result = response_handler.into_message().await;
Expand Down Expand Up @@ -176,7 +176,7 @@ async fn test_catalog_lookup() {
let question_req = Request::new(question_req, ([127, 0, 0, 1], 5553).into(), Protocol::Udp);

let response_handler = TestResponseHandler::new();
catalog
let _ = catalog
.lookup(&question_req, None, response_handler.clone())
.await;
let result = response_handler.into_message().await;
Expand Down Expand Up @@ -220,7 +220,7 @@ async fn test_catalog_lookup_soa() {
let question_req = Request::new(question_req, ([127, 0, 0, 1], 5553).into(), Protocol::Udp);

let response_handler = TestResponseHandler::new();
catalog
let _ = catalog
.lookup(&question_req, None, response_handler.clone())
.await;
let result = response_handler.into_message().await;
Expand Down Expand Up @@ -285,7 +285,7 @@ async fn test_catalog_nx_soa() {
let question_req = Request::new(question_req, ([127, 0, 0, 1], 5553).into(), Protocol::Udp);

let response_handler = TestResponseHandler::new();
catalog
let _ = catalog
.lookup(&question_req, None, response_handler.clone())
.await;
let result = response_handler.into_message().await;
Expand Down Expand Up @@ -334,7 +334,7 @@ async fn test_non_authoritive_nx_refused() {
let question_req = Request::new(question_req, ([127, 0, 0, 1], 5553).into(), Protocol::Udp);

let response_handler = TestResponseHandler::new();
catalog
let _ = catalog
.lookup(&question_req, None, response_handler.clone())
.await;
let result = response_handler.into_message().await;
Expand Down Expand Up @@ -387,7 +387,7 @@ async fn test_axfr() {
let question_req = Request::new(question_req, ([127, 0, 0, 1], 5553).into(), Protocol::Udp);

let response_handler = TestResponseHandler::new();
catalog
let _ = catalog
.lookup(&question_req, None, response_handler.clone())
.await;
let result = response_handler.into_message().await;
Expand Down Expand Up @@ -515,7 +515,7 @@ async fn test_axfr_refused() {
let question_req = Request::new(question_req, ([127, 0, 0, 1], 5553).into(), Protocol::Udp);

let response_handler = TestResponseHandler::new();
catalog
let _ = catalog
.lookup(&question_req, None, response_handler.clone())
.await;
let result = response_handler.into_message().await;
Expand Down Expand Up @@ -555,7 +555,7 @@ async fn test_cname_additionals() {
let question_req = Request::new(question_req, ([127, 0, 0, 1], 5553).into(), Protocol::Udp);

let response_handler = TestResponseHandler::new();
catalog
let _ = catalog
.lookup(&question_req, None, response_handler.clone())
.await;
let result = response_handler.into_message().await;
Expand Down Expand Up @@ -602,7 +602,7 @@ async fn test_multiple_cname_additionals() {
let question_req = Request::new(question_req, ([127, 0, 0, 1], 5553).into(), Protocol::Udp);

let response_handler = TestResponseHandler::new();
catalog
let _ = catalog
.lookup(&question_req, None, response_handler.clone())
.await;
let result = response_handler.into_message().await;
Expand Down

0 comments on commit ffcddd5

Please sign in to comment.