5 // Copyright (c) 2020 Apple Inc. All rights reserved.
8 #include "mDNSEmbeddedAPI.h"
9 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
11 #include "dnssec_v2.h"
12 #include "dnssec_v2_client.h"
13 #include "dnssec_v2_retrieval.h"
14 #include "dnssec_v2_helper.h"
16 //======================================================================================================================
17 // Local function prototype
18 //======================================================================================================================
20 // When calling remove_records_if_necessary/add_records_if_necessary, always check the boolean value stored in
21 // out_stop_immediately. If the value is true, then the caller should stop all the work immediately and return, because
22 // the functions might call the callback function which may cancel the current request and the question we are working
26 remove_records_if_necessary(
27 dnssec_context_t
* const _Nonnull dnssec_context
,
28 const dnssec_result_t dnssec_result
,
29 mDNS
* const mdnsresponder_context
,
30 DNSQuestion
* const _Nonnull question
,
31 mDNSBool
* _Nonnull out_stop_immediately
);
34 add_records_if_necessary(
35 dnssec_context_t
* const _Nonnull dnssec_context
,
36 const dnssec_result_t dnssec_result
,
37 const DNSServiceErrorType dns_error_from_core
,
38 mDNS
* const mdnsresponder_context
,
39 DNSQuestion
* const _Nonnull question
,
40 mDNSBool
* _Nonnull out_stop_immediately
);
43 handle_cname_response(dnssec_context_t
* const _Nonnull context
);
45 // Always check the return value of return_answer_to_user. If the value is true, then the caller should stop all the
46 // work immediately and return, because the functions may call the callback function which may cancel the current
47 // request and the question we are working on.
49 return_answer_to_user(
50 QueryRecordResultHandler user_handler
,
51 void * const user_context
,
52 dnssec_result_t result
,
53 ResourceRecord
* const _Nonnull rr
,
54 const QC_result add_or_remove
,
55 const DNSServiceErrorType dns_error
,
56 mDNS
* const _Nonnull m
,
57 DNSQuestion
* const _Nonnull primary_question
,
58 const DNSQuestion
* const _Nonnull current_question
);
61 add_record_to_be_returned_to_returned_answers_t(returned_answers_t
* const returned_asnwers
, const ResourceRecord
* const rr
);
64 contains_rr_in_returned_rrs(const ResourceRecord
* const rr
, const list_t
* const returned_rrs
/* list_t<ResourceRecord *> */);
66 mDNSlocal DNSServiceErrorType
67 get_dnsservice_error_type_from_originals(const original_t
* const _Nonnull original
, DNSServiceErrorType type_from_mDNSCore
);
69 #pragma mark - handle_validation_result
72 handle_cname_retrieval_error(dnssec_context_t
* const _Nonnull dnssec_context
,
73 mDNS
* const _Nonnull mdnsresponder_context
);
76 handle_retrieval_result(
77 DNSQuestion
* const _Nonnull question
,
78 dnssec_context_t
* _Nonnull original_dnssec_context
,
79 dnssec_retrieval_result_t retrieval_result
,
80 const DNSServiceErrorType dnssec_error
,
81 mDNS
* const _Nonnull mdnsresponder_context
) {
83 mDNSBool stop
= mDNSfalse
;
84 // We use a new variable here so that current_dnssec_context can be set to NULL when it is freed, to avoid corrupted
86 dnssec_context_t
* current_dnssec_context
= original_dnssec_context
;
87 mDNSu32 request_id
= current_dnssec_context
->original
.original_parameters
.request_id
;
88 mDNSu16 question_id
= mDNSVal16(question
->TargetQID
);
89 dnssec_result_t dnssec_result
;
90 mDNSBool stop_immediately
= mDNSfalse
;
92 switch (retrieval_result
) {
93 case dnssec_retrieval_no_error
:
94 case dnssec_retrieval_waiting_for_records
: // We are waiting for the response for some active queries.
95 case dnssec_retrieval_validate_again
: // We have a trust anchor for the current root, and will use it to validate immediately.
99 case dnssec_retrieval_no_new_change
:
103 case dnssec_retrieval_no_rrsig
: { // The returned answer is missing some necessary records to finish DNSSEC, it usually means the domain is not signed, thus it is an insecure zone.
104 mDNSu16 question_type
= current_dnssec_context
->original
.original_parameters
.question_type
;
105 response_type_t response_type
= current_dnssec_context
->original
.original_result_with_rrsig
.type
;
107 dnssec_result
= dnssec_insecure
;
110 if (response_type
== cname_response
&& question_type
!= kDNSServiceType_CNAME
) {
111 // After calling handle_cname_retrieval_error, dnssec_context will be freed, and should never be used.
112 handle_cname_retrieval_error(current_dnssec_context
, mdnsresponder_context
);
113 // Set current_dnssec_context to NULL to avoid any further access.
114 current_dnssec_context
= NULL
;
118 remove_records_if_necessary(current_dnssec_context
, dnssec_result
, mdnsresponder_context
, question
,
120 require_quiet(!stop_immediately
, exit
);
122 add_records_if_necessary(current_dnssec_context
, dnssec_result
, dnssec_error
, mdnsresponder_context
,
123 question
, &stop_immediately
);
124 require_quiet(!stop_immediately
, exit
);
127 case dnssec_retrieval_suppressed
: {
128 // Suppressed answers are generated by mDNSResponder itself, and we do not have a way to validate this
129 // generated record, thus the DNSSEC result should be indeterminate.
130 dnssec_result
= dnssec_indeterminate
;
133 remove_records_if_necessary(current_dnssec_context
, dnssec_result
, mdnsresponder_context
, question
,
135 require_quiet(!stop_immediately
, exit
);
136 add_records_if_necessary(current_dnssec_context
, dnssec_result
, dnssec_error
, mdnsresponder_context
,
137 question
, &stop_immediately
);
138 require_quiet(!stop_immediately
, exit
);
141 case dnssec_retrieval_cname_removed
: {
142 // if the CNAME is removed, then the sub request that follows this removed CNAME is no longer valid, and
143 // we need to stop it. If the CNAME record does not exist anymore, there is nothing we could do to validate
144 // the record(Maybe later we would get NSEC/NSEC3 records that denies the existence of this CNAME),
145 // therefore, it is marked as indeterminate.
146 dnssec_result
= dnssec_indeterminate
;
149 remove_records_if_necessary(current_dnssec_context
, dnssec_result
, mdnsresponder_context
, question
,
151 require_quiet(!stop_immediately
, exit
);
153 require_action_quiet(current_dnssec_context
->subtask_dnssec_context
!= mDNSNULL
, exit
, stop
= mDNStrue
;
154 log_error("[R%u->Q%u] Get dnssec_retrieval_cname_removed error while the context of CNAME request is NULL",
155 request_id
, question_id
));
157 stop_immediately
= stop_sub_cname_request_and_dnssec(question
, current_dnssec_context
, mDNStrue
, mdnsresponder_context
);
158 require_quiet(!stop_immediately
, exit
);
159 // Since the previous CNAME we rely on is removed, there is nothing we can do next, so we should stop the
160 // current processing.
166 log_error("[R%u->Q%u] handle_retrieval_result not handling this type of error; retrieval_result=%d",
167 request_id
, question_id
, retrieval_result
);
172 return stop
|| stop_immediately
;
175 //======================================================================================================================
178 handle_cname_retrieval_error(dnssec_context_t
* const _Nonnull dnssec_context
,
179 mDNS
* const _Nonnull mdnsresponder_context
)
181 dnssec_context_t
* const primary_dnssec_context
= GET_PRIMARY_DNSSEC_CONTEXT(dnssec_context
);
182 QueryRecordClientRequest
* const query_request
= GET_PRIMARY_REQUEST(dnssec_context
);
183 DNSQuestion
* const q
= &query_request
->op
.q
;
184 QueryRecordResultHandler user_handler
= primary_dnssec_context
->original
.original_parameters
.user_handler
;
185 void * const user_context
= primary_dnssec_context
->original
.original_parameters
.user_context
;
186 cnames_with_rrsig_t
*cname_ptr
;
187 list_node_t
*cname_node
;
188 dnssec_cname_t
* dnssec_cname
;
189 ResourceRecord
* rr_ptr
;
191 // Get the CNAME that has no RRSIG
192 cname_ptr
= &dnssec_context
->original
.original_result_with_rrsig
.u
.cname_with_rrsig
;
193 cname_node
= list_get_first(&cname_ptr
->cname_records
);
194 // Should never be NULL, but add checking here to avoid invalid memory access.
195 verify_action(cname_node
!= mDNSNULL
, return);
196 dnssec_cname
= (dnssec_cname_t
*)cname_node
->data
;
197 rr_ptr
= dnssec_cname
->dnssec_rr
.rr
; // this pointer points to the cached resource record in mDNSCore.
198 // Should never be NULL, since when dnssec_cname_t is initialized in initialize_dnssec_cname_t, the rr has to be non-NULL.
199 verify_action(rr_ptr
!= mDNSNULL
, return);
201 // After calling this function, dnssec_context has been freed, and should never be used.
202 stop_dnssec(query_request
);
204 query_request
->op
.resultHandler
= user_handler
; // change the user handler back to original one
205 query_request
->op
.resultContext
= user_context
; // change the user context back to original one
207 // Have to grab the lock to avoid any call when we are restarting the query.
208 mDNS_Lock(mdnsresponder_context
);
209 AnswerQuestionByFollowingCNAME(mdnsresponder_context
, q
, rr_ptr
);
210 mDNS_Unlock(mdnsresponder_context
);
212 // Disable DNSSEC for the current request.
213 q
->DNSSECStatus
.enable_dnssec
= mDNSfalse
;
214 q
->DNSSECStatus
.tried_dnssec_but_unsigned
= mDNStrue
;
215 q
->DNSSECStatus
.context
= mDNSNULL
;
218 //======================================================================================================================
219 // handle_validation_result
220 // When error happens or validation succeeds while we are validating records, handle it such as return the answer to user
221 //======================================================================================================================
223 #pragma mark - handle_validation_result
226 handle_cname_response(dnssec_context_t
* const _Nonnull context
);
229 handle_validation_result(
230 DNSQuestion
* const _Nonnull question
,
231 dnssec_context_t
* const _Nonnull dnssec_context
,
232 dnssec_validation_result_t validation_result
,
233 const DNSServiceErrorType dnssec_error
,
234 mDNS
* const _Nonnull mdnsresponder_context
) {
236 mStatus error
= mStatus_NoError
;
237 mDNSBool stop
= mDNSfalse
;
238 mDNSBool stop_immediately
= mDNSfalse
;
239 dnssec_result_t dnssec_result
;
241 switch (validation_result
) {
242 case dnssec_validation_trusted
: {
243 // return the secure answer to the user or continue the DNSSEC validation process if the verified answer is
244 // CNAME and user is not querying for CNAME record
245 mDNSu16 question_type
= dnssec_context
->original
.original_parameters
.question_type
;
246 response_type_t response_type
= dnssec_context
->original
.original_result_with_rrsig
.type
;
247 dnssec_context_t
* const cname_dnssec_context
= dnssec_context
->subtask_dnssec_context
;
248 dnssec_result
= dnssec_secure
;
250 if (response_type
== cname_response
&& question_type
!= kDNSType_CNAME
) {
251 if (cname_dnssec_context
!= mDNSNULL
) {
252 // stop the old CNAME request, if the reference has changed
253 const list_t
* cnames
= &dnssec_context
->original
.original_result_with_rrsig
.u
.cname_with_rrsig
.cname_records
;
254 const mDNSu8
* const new_cname
= ((dnssec_cname_t
*)list_get_first(cnames
)->data
)->cname
;
255 const mDNSu8
* const old_cname
= cname_dnssec_context
->original
.original_parameters
.question_name
.c
;
257 if (DOMAIN_NAME_EQUALS(new_cname
, old_cname
)) {
258 // CNAME reference does not change
263 // stop CNAME request and also deliver RMV event for those records that are returned to the client.
264 stop_immediately
= stop_sub_cname_request_and_dnssec(question
, dnssec_context
, mDNStrue
, mdnsresponder_context
);
265 require_quiet(!stop_immediately
, exit
);
267 error
= handle_cname_response(dnssec_context
);
268 if (error
!= mStatus_NoError
) {
273 // if the user requries CNAME refrence to be returned
274 mDNSBool return_intermediates
= ((dnssec_context
->original
.original_parameters
.flags
& kDNSServiceFlagsReturnIntermediates
) != 0);
275 if (!return_intermediates
) {
278 // fall outside of it intentionally
281 remove_records_if_necessary(dnssec_context
, dnssec_result
, mdnsresponder_context
, question
,
283 require_quiet(!stop_immediately
, exit
);
284 add_records_if_necessary(dnssec_context
, dnssec_result
, dnssec_error
, mdnsresponder_context
, question
,
286 require_quiet(!stop_immediately
, exit
);
289 case dnssec_validation_trust_anchor_does_not_macth
: {
290 const dnssec_zone_t
* const zone
= list_empty(&dnssec_context
->zone_chain
) ? mDNSNULL
: (dnssec_zone_t
*)list_get_last(&dnssec_context
->zone_chain
)->data
;
291 if (zone
!= mDNSNULL
&& is_root_domain(zone
->domain_name
.c
) && !trust_anchor_contains_dnskey(zone
->trust_anchor
)) {
292 // root DS trust anchor failed to validate the record, maybe update our trust anchor
294 log_error("root trust anchor does not verifies the validation tree");
297 // tries to fetch records from the DNS server instead of using local trust anchor
304 log_error("handle_validation_result not hanlding this type of error; validation_result=%d", validation_result
);
309 return stop
|| stop_immediately
;
313 #pragma mark handle_cname_response
314 // follow the CNAME reference chain, create another DNSSEC request to finish the CNAME query
316 handle_cname_response(dnssec_context_t
* const _Nonnull context
) {
317 response_type_t type
= context
->original
.original_result_with_rrsig
.type
;
318 domainname
* old_question_name
;
319 original_request_parameters_t
* parameters
;
320 domainname new_question_name
;
321 dnssec_context_t
* new_context
= mDNSNULL
;
322 mStatus error
= mStatus_NoError
;
324 // handle the cname referencing
325 list_t
*cnames
= &context
->original
.original_result_with_rrsig
.u
.cname_with_rrsig
.cname_records
;
326 verify(type
== cname_response
&& list_count_node(cnames
) == 1);
327 old_question_name
= (domainname
*)(((dnssec_cname_t
*)list_get_first(cnames
)->data
)->cname
);
328 AssignDomainName(&new_question_name
, old_question_name
);
330 // Create new dnssec_context_t for the CNAME
331 parameters
= &context
->original
.original_parameters
;
332 error
= create_dnssec_context_t(mDNSNULL
, parameters
->request_id
, &new_question_name
, parameters
->question_type
,
333 parameters
->question_class
, parameters
->interface_id
, parameters
->service_id
, parameters
->flags
,
334 parameters
->append_search_domains
, parameters
->pid
, parameters
->uuid
, parameters
->uid
,
335 #if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN)
336 parameters
->has_peer_audit_token
? ¶meters
->peer_audit_token
: mDNSNULL
,
337 parameters
->has_delegate_audit_token
? ¶meters
->delegate_audit_token
: mDNSNULL
,
339 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
340 parameters
->resolver_uuid
, parameters
->need_encryption
, parameters
->custom_id
,
342 parameters
->user_handler
, parameters
->user_context
,
343 context
->primary_dnssec_context
? context
->primary_dnssec_context
: context
,
345 require_action(error
== mStatus_NoError
, exit
,
346 log_error("create_dnssec_context_t failed; error_description='%s'", mStatusDescription(error
)));
348 new_context
->me
= &context
->request_to_follow_cname
;
350 // start a new dnssec request with new_question_name
351 error
= QueryRecordOpStartForClientRequest(&new_context
->me
->op
, parameters
->request_id
, &new_question_name
,
352 parameters
->question_type
, parameters
->question_class
, parameters
->interface_id
, parameters
->service_id
,
353 parameters
->flags
, parameters
->append_search_domains
, parameters
->pid
, parameters
->uuid
, parameters
->uid
,
354 #if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN)
355 parameters
->has_peer_audit_token
? ¶meters
->peer_audit_token
: mDNSNULL
,
356 parameters
->has_delegate_audit_token
? ¶meters
->delegate_audit_token
: mDNSNULL
,
358 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
359 parameters
->resolver_uuid
, parameters
->need_encryption
, parameters
->custom_id
,
361 query_record_result_reply_with_dnssec
, new_context
);
362 require_action(error
== mStatus_NoError
, exit
,
363 log_error("QueryRecordOpStart failed; error_description='%s'", mStatusDescription(error
)));
365 context
->subtask_dnssec_context
= new_context
;
368 if (error
!= mStatus_NoError
) {
369 if (new_context
!= mDNSNULL
) {
370 destroy_dnssec_context_t(new_context
);
376 #pragma mark remove_records_if_necessary
379 deliver_remove_for_returned_records(
380 dnssec_context_t
* const _Nonnull dnssec_context
,
381 const dnssec_result_t dnssec_result
,
382 mDNS
* const _Nonnull mdnsresponder_context
,
383 DNSQuestion
* const _Nonnull question
,
384 const response_type_t type
,
385 mDNSBool
* _Nonnull out_stop_immediately
);
388 remove_records_if_necessary(
389 dnssec_context_t
* const _Nonnull dnssec_context
,
390 const dnssec_result_t dnssec_result
,
391 mDNS
* const _Nonnull mdnsresponder_context
,
392 DNSQuestion
* const _Nonnull question
,
393 mDNSBool
* _Nonnull out_stop_immediately
) {
395 returned_answers_t
* const returned_answers
= &dnssec_context
->returned_answers
;
396 const original_t
* const original
= &dnssec_context
->original
;
397 mDNSBool delivered_remove_all
= mDNSfalse
;
398 mDNSBool delivered_remove_some
= mDNSfalse
;
400 // check if we ever returned answers back to the user
401 if (returned_answers
->error
== kDNSServiceErr_Invalid
) {
402 // there is no previous returned anwers(which means this is our first time to return the answer to the user)
406 // if we return No Error answer to the user
407 if (returned_answers
->error
== kDNSServiceErr_NoError
) {
408 require_action_quiet(returned_answers
->type
== original_response
|| returned_answers
->type
== cname_response
,
409 exit
, log_error("kDNSServiceErr_NoError must be matched with original_response or cname_response"));
410 if (original
->original_result_with_rrsig
.type
== returned_answers
->type
&& dnssec_result
== returned_answers
->dnssec_result
) {
411 // No Error response with no DNSSEC result change
412 delivered_remove_some
= deliver_remove_for_returned_records(dnssec_context
, dnssec_result
,
413 mdnsresponder_context
, question
, returned_answers
->type
, out_stop_immediately
);
414 require_quiet(!(*out_stop_immediately
), exit
);
416 // either the response is no longer the original response or the dnssec validation result has changed
417 delivered_remove_all
= mDNStrue
;
422 // if we return No Such Name or No Such Record to deny the existence of some records
423 if (returned_answers
->error
== kDNSServiceErr_NoSuchName
|| returned_answers
->error
== kDNSServiceErr_NoSuchRecord
) {
424 if (returned_answers
->type
== nsec_response
) {
425 if (original
->original_result_with_rrsig
.type
== nsec_response
&& dnssec_result
== dnssec_secure
) {
426 // unchanged NSEC response with no DNSSEC result change
427 delivered_remove_some
= deliver_remove_for_returned_records(dnssec_context
, dnssec_result
,
428 mdnsresponder_context
, question
, nsec_response
, out_stop_immediately
);
429 require_quiet(!(*out_stop_immediately
), exit
);
431 delivered_remove_all
= mDNStrue
;
433 } else if (returned_answers
->type
== nsec3_response
) {
434 if (original
->original_result_with_rrsig
.type
== nsec3_response
&& dnssec_result
== dnssec_secure
) {
435 // unchanged NSEC3 response with no DNSSEC result change
436 delivered_remove_some
= deliver_remove_for_returned_records(dnssec_context
, dnssec_result
,
437 mdnsresponder_context
, question
, nsec3_response
, out_stop_immediately
);
438 require_quiet(!(*out_stop_immediately
), exit
);
440 delivered_remove_all
= mDNStrue
;
442 } else if (returned_answers
->type
== original_response
) {
443 // The zone is unsigned
444 if (original
->original_result_with_rrsig
.type
== original_response
&& dnssec_result
== dnssec_insecure
) {
445 // The zone is unsigned, and returns No Such Name or No Such Record, nothing has changed
446 delivered_remove_some
= deliver_remove_for_returned_records(dnssec_context
, dnssec_result
,
447 mdnsresponder_context
, question
, original_response
, out_stop_immediately
);
448 require_quiet(!(*out_stop_immediately
), exit
);
450 delivered_remove_all
= mDNStrue
;
453 log_error("kDNSServiceErr_NoSuchName/kDNSServiceErr_NoSuchRecord must be matched with nsec_response/nsec3_response");
461 if (delivered_remove_all
) {
462 *out_stop_immediately
= deliver_remove_to_callback_with_all_returned_answers(dnssec_context
, returned_answers
,
463 mdnsresponder_context
, GET_PRIMARY_QUESTION(dnssec_context
), question
);
464 // Check if the callback in deliver_remove_to_callback_with_all_returned_answers has already deleted the
465 // current request and question. If so we should not touch anything.
466 if (!(*out_stop_immediately
)) {
467 uninitialize_returned_answers_t(returned_answers
);
468 initialize_returned_answers_t(returned_answers
, dnssec_indeterminate
, kDNSServiceErr_Invalid
);
471 return delivered_remove_all
|| delivered_remove_some
;
474 #pragma mark return_answer_to_user
475 // return_answer_to_user returns whether the caller should stop the DNSSEC-related processing. If it returns true,
476 // it means that the callback has stopped the request and deleted the current question, and all the current work should
477 // be stopped because the callback has stopped the request and deleted the current question. If we continue to process
478 // more, we may access the memory that has already been freed.
480 return_answer_to_user(
481 QueryRecordResultHandler user_handler
,
482 void * const user_context
,
483 dnssec_result_t result
,
484 ResourceRecord
* const _Nonnull rr
,
485 const QC_result add_or_remove
,
486 const DNSServiceErrorType dns_error
,
487 mDNS
* const _Nonnull m
,
488 DNSQuestion
* const _Nonnull primary_question
,
489 const DNSQuestion
* const _Nonnull current_question
) {
491 rr
->dnssec_result
= result
;
492 user_handler(m
, primary_question
, rr
, add_or_remove
, dns_error
, user_context
);
493 rr
->dnssec_result
= dnssec_indeterminate
;
495 return m
->CurrentQuestion
!= current_question
;
498 #pragma mark - add_records_if_necessary
501 add_records_if_necessary(
502 dnssec_context_t
* const _Nonnull dnssec_context
,
503 const dnssec_result_t dnssec_result
,
504 const DNSServiceErrorType dns_error_from_core
,
505 mDNS
* const mdnsresponder_context
,
506 DNSQuestion
* const _Nonnull question
,
507 mDNSBool
* _Nonnull out_stop_immediately
) {
509 originals_with_rrsig_t
* originals_with_rrsig
= &dnssec_context
->original
.original_result_with_rrsig
;
510 returned_answers_t
* const returned_answers
= &dnssec_context
->returned_answers
;
511 list_t
* const returned_rrs
= &returned_answers
->answers
;
512 mDNSBool add_records
= mDNSfalse
;
513 QueryRecordResultHandler user_handler
= dnssec_context
->original
.original_parameters
.user_handler
;
514 void * const user_context
= dnssec_context
->original
.original_parameters
.user_context
;
515 DNSServiceErrorType dns_error
= get_dnsservice_error_type_from_originals(&dnssec_context
->original
, dns_error_from_core
);
516 mDNSBool stop_immediately
= mDNSfalse
;
519 if (returned_answers
->error
== kDNSServiceErr_Invalid
) {
520 // It is our first time to return the response back to callback
521 initialize_returned_answers_t(returned_answers
, dnssec_result
, dns_error
);
523 returned_answers
->error
= dns_error_from_core
;
526 switch (originals_with_rrsig
->type
) {
527 case original_response
: {
528 list_t
* const dnssec_originals
= &originals_with_rrsig
->u
.original
.original_records
;
529 if (originals_with_rrsig
->u
.original
.negative_rr
!= mDNSNULL
) {
530 ResourceRecord
* const rr
= originals_with_rrsig
->u
.original
.negative_rr
;
531 mDNSBool contained_in_returned_answer
= contains_rr_in_returned_rrs(rr
, returned_rrs
);
532 if (contained_in_returned_answer
) {
536 error
= add_record_to_be_returned_to_returned_answers_t(&dnssec_context
->returned_answers
, rr
);
537 require_quiet(error
== mStatus_NoError
, exit
);
539 stop_immediately
= return_answer_to_user(user_handler
, user_context
, dnssec_result
, rr
, QC_add
,
540 dns_error
, mdnsresponder_context
, GET_PRIMARY_QUESTION(dnssec_context
), question
);
541 add_records
= mDNStrue
;
542 require_quiet(!stop_immediately
, exit
);
543 returned_answers
->type
= original_response
;
545 for (const list_node_t
* dnssec_original_node
= list_get_first(dnssec_originals
);
546 !list_has_ended(dnssec_originals
, dnssec_original_node
);
547 dnssec_original_node
= list_next(dnssec_original_node
)) {
549 dnssec_original_t
* const dnssec_original
= (dnssec_original_t
*)dnssec_original_node
->data
;
550 ResourceRecord
* const rr
= dnssec_original
->dnssec_rr
.rr
;
551 mDNSBool contained_in_returned_answer
= contains_rr_in_returned_rrs(rr
, returned_rrs
);
552 if (contained_in_returned_answer
) {
556 error
= add_record_to_be_returned_to_returned_answers_t(returned_answers
, rr
);
557 require_quiet(error
== mStatus_NoError
, exit
);
559 stop_immediately
= return_answer_to_user(user_handler
, user_context
, dnssec_result
, rr
, QC_add
,
560 dns_error
, mdnsresponder_context
, GET_PRIMARY_QUESTION(dnssec_context
), question
);
561 add_records
= mDNStrue
;
562 require_quiet(!stop_immediately
, exit
);
565 returned_answers
->type
= original_response
;
568 case cname_response
: {
569 list_t
* const dnssec_cnames
= &originals_with_rrsig
->u
.cname_with_rrsig
.cname_records
;
570 for (const list_node_t
* dnssec_cname_node
= list_get_first(dnssec_cnames
);
571 !list_has_ended(dnssec_cnames
, dnssec_cname_node
);
572 dnssec_cname_node
= list_next(dnssec_cname_node
)) {
574 dnssec_cname_t
* const dnssec_cname
= (dnssec_cname_t
*)dnssec_cname_node
->data
;
575 ResourceRecord
* const rr
= dnssec_cname
->dnssec_rr
.rr
;
576 mDNSBool contained_in_returned_answer
= contains_rr_in_returned_rrs(rr
, returned_rrs
);
577 if (contained_in_returned_answer
) {
581 error
= add_record_to_be_returned_to_returned_answers_t(returned_answers
, rr
);
582 require_quiet(error
== mStatus_NoError
, exit
);
584 stop_immediately
= return_answer_to_user(user_handler
, user_context
, dnssec_result
, rr
, QC_add
,
585 dns_error
, mdnsresponder_context
, GET_PRIMARY_QUESTION(dnssec_context
), question
);
586 add_records
= mDNStrue
;
587 require_quiet(!stop_immediately
, exit
);
589 returned_answers
->type
= cname_response
;
592 case nsec_response
: {
593 ResourceRecord
* const rr
= originals_with_rrsig
->u
.nsecs_with_rrsig
.negative_rr
;
594 mDNSBool contained_in_returned_answer
= contains_rr_in_returned_rrs(rr
, returned_rrs
);
595 if (contained_in_returned_answer
) {
599 error
= add_record_to_be_returned_to_returned_answers_t(&dnssec_context
->returned_answers
, rr
);
600 require_quiet(error
== mStatus_NoError
, exit
);
602 stop_immediately
= return_answer_to_user(user_handler
, user_context
, dnssec_result
, rr
, QC_add
, dns_error
,
603 mdnsresponder_context
, GET_PRIMARY_QUESTION(dnssec_context
), question
);
604 add_records
= mDNStrue
;
605 require_quiet(!stop_immediately
, exit
);
606 returned_answers
->type
= nsec_response
;
609 case nsec3_response
: {
610 ResourceRecord
* const rr
= originals_with_rrsig
->u
.nsec3s_with_rrsig
.negative_rr
;
611 mDNSBool contained_in_returned_answer
= contains_rr_in_returned_rrs(rr
, returned_rrs
);
612 if (contained_in_returned_answer
) {
616 error
= add_record_to_be_returned_to_returned_answers_t(&dnssec_context
->returned_answers
, rr
);
617 require_quiet(error
== mStatus_NoError
, exit
);
619 stop_immediately
= return_answer_to_user(user_handler
, user_context
, dnssec_result
, rr
, QC_add
, dns_error
,
620 mdnsresponder_context
, GET_PRIMARY_QUESTION(dnssec_context
), question
);
621 add_records
= mDNStrue
;
622 require_quiet(!stop_immediately
, exit
);
623 returned_answers
->type
= nsec3_response
;
631 if (out_stop_immediately
!= mDNSNULL
) {
632 *out_stop_immediately
= stop_immediately
;
638 #pragma mark - returned_answers_t
642 #pragma mark add_record_to_be_returned_to_returned_answers_t
644 add_record_to_be_returned_to_returned_answers_t(returned_answers_t
* const returned_asnwers
, const ResourceRecord
* const rr
) {
645 const ResourceRecord
** inserted_rr
;
648 require_action_quiet(rr
!= mDNSNULL
, exit
, error
= mStatus_BadReferenceErr
);
650 // No need to remember if we returned negative answer to the user since wo do not need to send RMV for negative record
651 if (rr
->RecordType
== kDNSRecordTypePacketNegative
) {
652 error
= mStatus_NoError
;
656 error
= list_append_uinitialized(&returned_asnwers
->answers
, sizeof(ResourceRecord
*), (void **)&inserted_rr
);
657 require_quiet(error
== mStatus_NoError
, exit
);
665 #pragma mark get_dnsservice_error_type_from_originals
666 mDNSlocal DNSServiceErrorType
667 get_dnsservice_error_type_from_originals(const original_t
* const _Nonnull original
, DNSServiceErrorType type_from_mDNSCore
) {
668 response_type_t type
= original
->original_result_with_rrsig
.type
;
669 DNSServiceErrorType error
= kDNSServiceErr_Invalid
;
671 if (type
== original_response
) {
672 if (original
->original_result_with_rrsig
.u
.original
.negative_rr
!= mDNSNULL
) {
673 error
= type_from_mDNSCore
;
675 error
= kDNSServiceErr_NoError
;
677 } else if (type
== cname_response
) {
678 error
= kDNSServiceErr_NoError
;
679 } else if (type
== nsec_response
) {
680 dnssec_validation_result_t validation_result
= original
->original_result_with_rrsig
.u
.nsecs_with_rrsig
.nsec_result
;
681 switch (validation_result
) {
682 case dnssec_validation_nsec_name_error
:
683 error
= kDNSServiceErr_NoSuchName
;
685 case dnssec_validation_nsec_no_data
:
686 error
= kDNSServiceErr_NoSuchRecord
;
688 case dnssec_validation_nsec_wildcard_answer
:
689 case dnssec_validation_nsec_wildcard_no_data
:
690 log_error("wildcard not handled");
694 } else if (type
== nsec3_response
) {
695 dnssec_validation_result_t validation_result
= original
->original_result_with_rrsig
.u
.nsec3s_with_rrsig
.nsec3_result
;
696 switch (validation_result
) {
697 case dnssec_validation_nsec3_name_error
:
698 error
= kDNSServiceErr_NoSuchName
;
700 case dnssec_validation_nsec3_no_data_response
:
701 error
= kDNSServiceErr_NoSuchRecord
;
703 case dnssec_validation_nsec3_wildcard_answer_response
:
704 case dnssec_validation_nsec3_wildcard_no_data
:
705 log_error("wildcard not handled");
711 log_error("Original response has type other than 'original_response', 'cname_response', 'nsec_response', 'nsec3_response'");
717 #pragma mark deliver_remove_to_callback_with_all_returned_answer
718 // deliver_remove_to_callback_with_all_returned_answers returns whether the caller should stop the DNSSEC-related
719 // processing. If it returns true, it means that the callback has stopped the request and deleted the current question,
720 // and all the current work should be stopped because the callback has stopped the request and deleted the current
721 // question. If we continue to process more, we may access the memory that has already been freed.
723 deliver_remove_to_callback_with_all_returned_answers(
724 const dnssec_context_t
* const _Nonnull context
,
725 const returned_answers_t
* const _Nonnull returned_answers
,
726 mDNS
* const _Nonnull m
,
727 DNSQuestion
* const _Nonnull primary_question
,
728 const DNSQuestion
* const _Nonnull current_question
) {
730 mDNSBool stop_immediately
= mDNSfalse
;
732 require_quiet(returned_answers
->error
!= kDNSServiceErr_Invalid
, exit
);
734 const list_t
* const returned_rrs
= &returned_answers
->answers
;
735 QueryRecordResultHandler user_handler
= context
->original
.original_parameters
.user_handler
;
736 void * const user_context
= context
->original
.original_parameters
.user_context
;
738 for (const list_node_t
* rr_node
= list_get_first(returned_rrs
); !list_has_ended(returned_rrs
, rr_node
); rr_node
= list_next(rr_node
)) {
739 ResourceRecord
* const * const rr_ptr
= (ResourceRecord
* const * const)rr_node
->data
;
740 ResourceRecord
* const rr
= *rr_ptr
;
741 // 1. No need to deliver RMV for negative answer, such as No Such Record.
742 // 2. No need to deliver RMV for CNAME answer.
743 if (rr
->RecordType
== kDNSRecordTypePacketNegative
|| rr
->rrtype
== kDNSType_CNAME
) {
746 stop_immediately
= return_answer_to_user(user_handler
, user_context
, returned_answers
->dnssec_result
, rr
,
747 QC_rmv
, returned_answers
->error
, m
, primary_question
, current_question
);
748 require_quiet(!stop_immediately
, exit
);
752 return stop_immediately
;
755 #pragma mark deliver_remove_for_returned_records
758 contains_rr_in_original_records(const ResourceRecord
* const rr
, const list_t
* const original_records
/* list_t<dnssec_original_t> */) {
759 mDNSBool contains
= mDNSfalse
;
761 for (const list_node_t
* original_record_node
= list_get_first(original_records
);
762 !list_has_ended(original_records
, original_record_node
);
763 original_record_node
= list_next(original_record_node
)) {
765 const dnssec_original_t
* const dnssec_original
= (dnssec_original_t
*)original_record_node
->data
;
766 if (dnssec_original
->dnssec_rr
.rr
== rr
) {
777 deliver_remove_for_returned_records(
778 dnssec_context_t
* const _Nonnull dnssec_context
,
779 const dnssec_result_t dnssec_result
,
780 mDNS
* const _Nonnull mdnsresponder_context
,
781 DNSQuestion
* const _Nonnull question
,
782 const response_type_t type
,
783 mDNSBool
* _Nonnull out_stop_immediately
) {
785 returned_answers_t
* const returned_answers
= &dnssec_context
->returned_answers
;
786 list_t
* const returned_rrs
= &returned_answers
->answers
;
787 QueryRecordResultHandler user_handler
= dnssec_context
->original
.original_parameters
.user_handler
;
788 void * const user_context
= dnssec_context
->original
.original_parameters
.user_context
;
789 originals_with_rrsig_t
* const originals_with_rrsig
= &dnssec_context
->original
.original_result_with_rrsig
;
790 mDNSu32 request_id
= dnssec_context
->original
.original_parameters
.request_id
;
791 mDNSu16 question_id
= mDNSVal16(question
->TargetQID
);
792 mDNSBool remove_some
= mDNSfalse
;
793 mDNSBool stop_immediately
= mDNSfalse
;
795 if (type
== original_response
) {
796 for (list_node_t
* rr_node
= list_get_first(returned_rrs
), *next_node
; !list_has_ended(returned_rrs
, rr_node
); rr_node
= next_node
) {
797 ResourceRecord
* const * const rr_ptr
= (ResourceRecord
* const * const)rr_node
->data
;
798 ResourceRecord
*rr
= *rr_ptr
;
799 next_node
= list_next(rr_node
);
800 mDNSBool contains
= mDNStrue
;
801 if (returned_answers
->error
== kDNSServiceErr_NoError
) {
802 contains
= contains_rr_in_original_records(rr
, &originals_with_rrsig
->u
.original
.original_records
);
803 } else if (returned_answers
->error
== kDNSServiceErr_NoSuchName
|| returned_answers
->error
== kDNSServiceErr_NoSuchRecord
) {
804 contains
= (rr
== originals_with_rrsig
->u
.original
.negative_rr
);
806 log_error("[R%u->Q%u] when the DNSSEC response is original response, only NoError, NoSuchName and NoSuchRecord are allowed"
807 " - response type: " PUB_S
", kDNSServiceErr type: %u",
808 request_id
, question_id
, response_type_value_to_string(type
), returned_answers
->error
);
812 stop_immediately
= return_answer_to_user(user_handler
, user_context
, dnssec_result
, rr
, QC_rmv
,
813 returned_answers
->error
, mdnsresponder_context
, GET_PRIMARY_QUESTION(dnssec_context
), question
);
814 remove_some
= mDNStrue
;
815 require_quiet(!stop_immediately
, exit
);
816 list_node_delete(rr_node
);
819 } else if (type
== nsec_response
) {
820 require_action_quiet(
821 returned_answers
->error
== kDNSServiceErr_NoSuchName
|| returned_answers
->error
== kDNSServiceErr_NoSuchRecord
,
823 log_error("[R%u->Q%u] when the NSEC response is original response, only NoSuchName and NoSuchRecord are allowed"
824 " - response type: " PUB_S
", kDNSServiceErr type: %u",
825 request_id
, question_id
, response_type_value_to_string(type
), returned_answers
->error
)
828 require_action_quiet(list_count_node(returned_rrs
) == 1, exit
,
829 log_error("[R%u->Q%u] Denail of existence answer returns more than one negative answer with NSEC proof - number_of_records: %u",
830 request_id
, question_id
, list_count_node(returned_rrs
)));
832 ResourceRecord
* const * const rr_ptr
= (ResourceRecord
* const * const)(list_get_first(returned_rrs
)->data
);
833 ResourceRecord
*rr
= *rr_ptr
;
834 if (rr
!= originals_with_rrsig
->u
.nsecs_with_rrsig
.negative_rr
) {
835 stop_immediately
= return_answer_to_user(user_handler
, user_context
, dnssec_result
, rr
, QC_rmv
,
836 returned_answers
->error
, mdnsresponder_context
, GET_PRIMARY_QUESTION(dnssec_context
), question
);
837 remove_some
= mDNStrue
;
838 require_quiet(!stop_immediately
, exit
);
839 // Deletes the removed record.
840 list_node_delete_all(returned_rrs
);
842 } else if (type
== nsec3_response
) {
843 require_action_quiet(
844 returned_answers
->error
== kDNSServiceErr_NoSuchName
|| returned_answers
->error
== kDNSServiceErr_NoSuchRecord
,
846 log_error("[R%u->Q%u] when the NSEC3 response is original response, only NoSuchName and NoSuchRecord are allowed"
847 " - response type: " PUB_S
", kDNSServiceErr type: %u",
848 request_id
, question_id
, response_type_value_to_string(type
), returned_answers
->error
)
851 require_action_quiet(list_count_node(returned_rrs
) == 1, exit
,
852 log_error("[R%u->Q%u] Denail of existence answer returns more than one negative answer with NSEC3 proof - number_of_records: %u",
853 request_id
, question_id
, list_count_node(returned_rrs
)));
855 ResourceRecord
* const * const rr_ptr
= (ResourceRecord
* const * const)(list_get_first(returned_rrs
)->data
);
856 ResourceRecord
*rr
= *rr_ptr
;
857 if (rr
!= originals_with_rrsig
->u
.nsec3s_with_rrsig
.negative_rr
) {
858 stop_immediately
= return_answer_to_user(user_handler
, user_context
, dnssec_result
, rr
, QC_rmv
,
859 returned_answers
->error
, mdnsresponder_context
, GET_PRIMARY_QUESTION(dnssec_context
), question
);
860 remove_some
= mDNStrue
;
861 require_quiet(!stop_immediately
, exit
);
862 // Deletes the removed record.
863 list_node_delete_all(returned_rrs
);
866 // cname_response or other invalid value
867 log_error("[R%u->Q%u] Invalid returned answers response type - response type: " PUB_S
, request_id
, question_id
,
868 response_type_value_to_string(type
));
873 if (out_stop_immediately
!= mDNSNULL
) {
874 *out_stop_immediately
= stop_immediately
;
879 #pragma mark contains_rr_in_returned_rrs
881 contains_rr_in_returned_rrs(const ResourceRecord
* const rr
, const list_t
* const returned_rrs
/* list_t<ResourceRecord *> */) {
882 mDNSBool contains
= mDNSfalse
;
884 for (const list_node_t
*rr_ptr_node
= list_get_first(returned_rrs
);
885 !list_has_ended(returned_rrs
, rr_ptr_node
);
886 rr_ptr_node
= list_next(rr_ptr_node
)) {
888 const ResourceRecord
* const * const rr_to_compare_ptr
= (const ResourceRecord
* const * const)rr_ptr_node
->data
;
889 const ResourceRecord
* const rr_to_compare
= *rr_to_compare_ptr
;
891 if (rr
== rr_to_compare
) {
901 #endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)