3 * Copyright (c) 2018-2019 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * This is a Discovery Proxy module for the SRP gateway.
19 * The motivation here is that it makes sense to co-locate the SRP relay and the Discovery Proxy because
20 * these functions are likely to co-exist on the same node, listening on the same port. For homenet-style
21 * name resolution, we need a DNS proxy that implements DNSSD Discovery Proxy for local queries, but
22 * forwards other queries to an ISP resolver. The SRP gateway is already expecting to do this.
23 * This module implements the functions required to allow the SRP gateway to also do Discovery Relay.
25 * The Discovery Proxy relies on Apple's DNS-SD library and the mDNSResponder DNSSD server, which is included
26 * in Apple's open source mDNSResponder package, available here:
28 * https://opensource.apple.com/tarballs/mDNSResponder/
31 #define __APPLE_USE_RFC_3542
37 #include <sys/errno.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <sys/event.h>
49 #include "srp-crypto.h"
53 // Enumerate the list of interfaces, map them to interface indexes, give each one a name
54 // Have a tree of subdomains for matching
56 typedef struct dnssd_query
{
59 char *name
; // The name we are looking up.
60 const char *enclosing_domain
; // The domain the name is in, or NULL if not ours; if null, name is an FQDN.
61 dns_name_pointer_t enclosing_domain_pointer
;
64 dso_activity_t
*activity
;
65 int serviceFlags
; // Service flags to use with this query.
68 uint16_t type
, qclass
; // Original query type and class.
69 dns_towire_state_t towire
;
70 uint8_t *p_dso_length
; // Where to store the DSO length just before we write out a push notification.
71 dns_wire_t response
; // This has to be at the end because we don't zero the RRdata buffer.
74 const char push_subscription_activity_type
[] = "push subscription";
76 const char local_suffix
[] = ".local.";
77 #define PROXIED_DOMAIN "proxy.home.arpa."
78 const char proxied_domain
[] = PROXIED_DOMAIN
;
79 const char proxied_domain_ld
[] = "." PROXIED_DOMAIN
;
80 #define MY_NAME "proxy.example.com."
81 #define MY_IPV4_ADDR "192.0.2.1"
82 // #define MY_IPV6_ADDR "2001:db8::1" // for example
84 #define TOWIRE_CHECK(note, towire, func) { func; if ((towire)->error != 0 && failnote == NULL) failnote = (note); }
86 int64_t dso_transport_idle(void *context
, int64_t next_event
)
91 void dnssd_query_cancel(io_t
*io
)
93 dnssd_query_t
*query
= (dnssd_query_t
*)io
;
94 if (query
->io
.sock
!= -1) {
95 DNSServiceRefDeallocate(query
->ref
);
98 query
->connection
= NULL
;
102 dns_push_finalize(dso_activity_t
*activity
)
104 dnssd_query_t
*query
= (dnssd_query_t
*)activity
->context
;
105 INFO("dnssd_push_finalize: %s", activity
->name
);
106 dnssd_query_cancel(&query
->io
);
110 dnssd_query_finalize(io_t
*io
)
112 dnssd_query_t
*query
= (dnssd_query_t
*)io
;
113 INFO("dnssd_query_finalize on %s%s", query
->name
, query
->enclosing_domain
? ".local" : "");
114 if (query
->question
) {
115 message_free(query
->question
);
122 dnssd_query_callback(io_t
*io
)
124 dnssd_query_t
*query
= (dnssd_query_t
*)io
;
125 int status
= DNSServiceProcessResult(query
->ref
);
126 if (status
!= kDNSServiceErr_NoError
) {
127 ERROR("DNSServiceProcessResult on %s%s returned %d", query
->name
, query
->enclosing_domain
? ".local" : "", status
);
128 if (query
->activity
!= NULL
&& query
->connection
!= NULL
) {
129 dso_drop_activity(query
->connection
->dso
, query
->activity
);
131 dnssd_query_cancel(&query
->io
);
137 add_dnssd_query(dnssd_query_t
*query
)
139 io_t
*io
= &query
->io
;
140 io
->sock
= DNSServiceRefSockFD(query
->ref
);
141 io
->cancel
= dnssd_query_cancel
;
142 io
->cancel_on_close
= &query
->connection
->io
;
143 add_reader(io
, dnssd_query_callback
, dnssd_query_finalize
);
146 // Parse a NUL-terminated text string into a sequence of labels.
148 dns_pres_name_parse(const char *pname
)
150 const char *dot
= strchr(pname
, '.');
154 dot
= pname
+ strlen(pname
);
156 len
= (dot
- pname
) + 1 + (sizeof *ret
) - DNS_MAX_LABEL_SIZE
;
157 ret
= calloc(len
, 1);
161 ret
->len
= dot
- pname
;
163 memcpy(ret
->data
, pname
, ret
->len
);
165 ret
->data
[ret
->len
] = 0;
167 ret
->next
= dns_pres_name_parse(dot
+ 1);
173 dns_subdomain_of(dns_name_t
*name
, dns_name_t
*domain
, char *buf
, size_t buflen
)
175 int dnum
= 0, nnum
= 0;
178 size_t bytesleft
= buflen
;
180 for (dp
= domain
; dp
; dp
= dp
->next
) {
183 for (np
= name
; np
; np
= np
->next
) {
189 for (np
= name
; np
; np
= np
->next
) {
190 if (nnum
-- == dnum
) {
194 if (dns_names_equal(np
, domain
)) {
195 for (dp
= name
; dp
!= np
; dp
= dp
->next
) {
196 if (dp
->len
+ 1 > bytesleft
) {
197 // It's okay to return false here because a name that overflows the buffer isn't valid.
198 ERROR("dns_subdomain_of: out of buffer space!");
201 memcpy(bufp
, dp
->data
, dp
->len
);
203 bytesleft
= bytesleft
- dp
->len
;
204 if (dp
->next
!= np
) {
206 bytesleft
-= dp
->len
;
216 dp_simple_response(comm_t
*comm
, int rcode
)
218 if (comm
->send_response
) {
221 memset(&response
, 0, DNS_HEADER_SIZE
);
223 // We take the ID and the opcode from the incoming message, because if the
224 // header has been mangled, we (a) wouldn't have gotten here and (b) don't
225 // have any better choice anyway.
226 response
.id
= comm
->message
->wire
.id
;
227 dns_qr_set(&response
, dns_qr_response
);
228 dns_opcode_set(&response
, dns_opcode_get(&comm
->message
->wire
));
229 dns_rcode_set(&response
, rcode
);
230 iov
.iov_base
= &response
;
231 iov
.iov_len
= DNS_HEADER_SIZE
; // No RRs
232 comm
->send_response(comm
, comm
->message
, &iov
, 1);
237 dp_served(dns_name_t
*name
, char *buf
, size_t bufsize
)
239 static dns_name_t
*home_dot_arpa
= NULL
;
240 if (home_dot_arpa
== NULL
) {
241 home_dot_arpa
= dns_pres_name_parse(proxied_domain
);
242 if (home_dot_arpa
== NULL
) {
243 ERROR("Unable to parse %s!", proxied_domain
);
248 // For now we treat any query to home.arpa as local.
249 return dns_subdomain_of(name
, home_dot_arpa
, buf
, bufsize
);
252 // Utility function to find "local" on the end of a string of labels.
254 truncate_local(dns_name_t
*name
)
256 dns_label_t
*lp
, *prev
, *prevprev
;
258 prevprev
= prev
= NULL
;
259 // Find the root label.
260 for (lp
= name
; lp
&& lp
->len
; lp
= lp
->next
) {
264 if (lp
&& prev
&& prevprev
) {
265 if (prev
->len
== 5 && !strncasecmp(prev
->data
, "local", 5)) {
267 prevprev
->next
= NULL
;
276 dp_query_add_data_to_response(dnssd_query_t
*query
, const char *fullname
,
277 uint16_t rrtype
, uint16_t rrclass
, uint16_t rdlen
, const void *rdata
, uint32_t ttl
)
279 dns_towire_state_t
*towire
= &query
->towire
;
280 const char *failnote
= NULL
;
282 // Rewrite the domain if it's .local.
283 if (query
->enclosing_domain
!= NULL
) {
284 TOWIRE_CHECK("query name", towire
, dns_name_to_wire(NULL
, towire
, query
->name
));
285 if (query
->enclosing_domain_pointer
.message_start
!= NULL
) {
286 // This happens if we are sending a DNS response, because we can always point back to the question.
287 TOWIRE_CHECK("enclosing_domain_pointer", towire
,
288 dns_pointer_to_wire(NULL
, towire
, &query
->enclosing_domain_pointer
));
289 INFO(" dns answer: type %02d class %02d %s.%s (p)", rrtype
, rrclass
, query
->name
, query
->enclosing_domain
);
291 // This happens if we are sending a DNS Push notification.
292 TOWIRE_CHECK("enclosing_domain", towire
, dns_full_name_to_wire(NULL
, towire
, query
->enclosing_domain
));
293 INFO("push answer: type %02d class %02d %s.%s", rrtype
, rrclass
, query
->name
, query
->enclosing_domain
);
296 TOWIRE_CHECK("query->name", towire
, dns_full_name_to_wire(NULL
, towire
, query
->name
));
297 INFO("%s answer: type %02d class %02d %s.%s (p)",
298 query
->is_dns_push
? "push" : " dns", rrtype
, rrclass
, query
->name
, query
->enclosing_domain
);
300 TOWIRE_CHECK("rrtype", towire
, dns_u16_to_wire(towire
, rrtype
));
301 TOWIRE_CHECK("rrclass", towire
, dns_u16_to_wire(towire
, rrclass
));
302 TOWIRE_CHECK("ttl", towire
, dns_ttl_to_wire(towire
, ttl
));
305 // If necessary, correct domain names inside of rrdata.
306 if (rrclass
== dns_qclass_in
&& (rrtype
== dns_rrtype_srv
||
307 rrtype
== dns_rrtype_ptr
||
308 rrtype
== dns_rrtype_cname
)) {
312 answer
.type
= rrtype
;
313 answer
.qclass
= rrclass
;
314 if (!dns_rdata_parse_data(&answer
, rdata
, &offp
, rdlen
, rdlen
, 0)) {
315 ERROR("dp_query_add_data_to_response: rdata from mDNSResponder didn't parse!!");
319 case dns_rrtype_cname
:
321 name
= answer
.data
.ptr
.name
;
322 if (!truncate_local(name
)) {
325 TOWIRE_CHECK("rdlength begin", towire
, dns_rdlength_begin(towire
));
328 name
= answer
.data
.srv
.name
;
329 if (!truncate_local(name
)) {
332 TOWIRE_CHECK("rdlength begin", towire
, dns_rdlength_begin(towire
));
333 TOWIRE_CHECK("answer.data.srv.priority", towire
, dns_u16_to_wire(towire
, answer
.data
.srv
.priority
));
334 TOWIRE_CHECK("answer.data.srv.weight", towire
, dns_u16_to_wire(towire
, answer
.data
.srv
.weight
));
335 TOWIRE_CHECK("answer.data.srv.port", towire
, dns_u16_to_wire(towire
, answer
.data
.srv
.port
));
338 ERROR("dp_query_add_data_to_response: can't get here.");
342 // If we get here, the name ended in "local."
343 int bytes_written
= dns_name_to_wire_canonical(towire
->p
, towire
->lim
- towire
->p
, name
);
344 towire
->p
+= bytes_written
;
345 if (query
->enclosing_domain_pointer
.message_start
!= NULL
) {
346 TOWIRE_CHECK("enclosing_domain_pointer internal", towire
,
347 dns_pointer_to_wire(NULL
, towire
, &query
->enclosing_domain_pointer
));
349 TOWIRE_CHECK("enclosing_domain internal", towire
,
350 dns_full_name_to_wire(NULL
, towire
, query
->enclosing_domain
));
352 dns_rdlength_end(towire
);
355 TOWIRE_CHECK("rdlen", towire
, dns_u16_to_wire(towire
, rdlen
));
356 TOWIRE_CHECK("rdata", towire
, dns_rdata_raw_data_to_wire(towire
, rdata
, rdlen
));
359 TOWIRE_CHECK("rdlen", towire
, dns_u16_to_wire(towire
, rdlen
));
362 ERROR("dp_query_add_data_to_response: %s", failnote
);
366 typedef struct hardwired hardwired_t
;
374 } *hardwired_responses
;
377 dnssd_hardwired_add(const char *name
, const char *domain
, size_t rdlen
, uint8_t *rdata
, uint16_t type
)
380 int namelen
= strlen(name
);
381 size_t total
= (sizeof *hp
) + rdlen
+ namelen
* 2 + strlen(proxied_domain_ld
) + 2;
383 hp
= calloc(1, (sizeof *hp
) + rdlen
+ namelen
* 2 + strlen(proxied_domain_ld
) + 2);
384 hp
->rdata
= (uint8_t *)(hp
+ 1);
386 memcpy(hp
->rdata
, rdata
, rdlen
);
387 hp
->name
= (char *)hp
->rdata
+ rdlen
;
388 strcpy(hp
->name
, name
);
389 hp
->fullname
= hp
->name
+ namelen
+ 1;
390 strcpy(hp
->fullname
, name
);
391 strcpy(hp
->fullname
+ namelen
, proxied_domain_ld
);
392 if (hp
->fullname
+ strlen(hp
->fullname
) + 1 != (char *)hp
+ total
) {
393 ERROR("%p != %p", hp
->fullname
+ strlen(hp
->fullname
) + 1, ((char *)hp
) + total
);
396 hp
->next
= hardwired_responses
;
397 hardwired_responses
= hp
;
399 INFO("hardwired_add: fullname %s name %s type %d rdlen %d", hp
->fullname
, hp
->name
, hp
->type
, hp
->rdlen
);
403 dnssd_hardwired_setup(void)
406 dns_towire_state_t towire
;
409 memset(&towire, 0, sizeof towire); \
410 towire.message = &wire; \
411 towire.p = wire.data; \
412 towire.lim = towire.p + sizeof wire.data
414 // Browsing pointers...
416 dns_full_name_to_wire(NULL
, &towire
, proxied_domain
);
417 dnssd_hardwired_add("b._dns-sd._udp", proxied_domain_ld
, towire
.p
- wire
.data
, wire
.data
, dns_rrtype_ptr
);
418 dnssd_hardwired_add("lb._dns-sd._udp", proxied_domain_ld
, towire
.p
- wire
.data
, wire
.data
, dns_rrtype_ptr
);
421 // _dns-push-tls._tcp
423 dns_u16_to_wire(&towire
, 0); // priority
424 dns_u16_to_wire(&towire
, 0); // weight
425 dns_u16_to_wire(&towire
, 53); // port
426 // Define MY_NAME to reference a name for this server in a different zone.
428 dns_name_to_wire(NULL
, &towire
, "ns");
429 dns_full_name_to_wire(NULL
, &towire
, proxied_domain
);
431 dns_full_name_to_wire(NULL
, &towire
, MY_NAME
);
433 dnssd_hardwired_add("_dns-push-tls._tcp", proxied_domain_ld
, towire
.p
- wire
.data
, wire
.data
, dns_rrtype_srv
);
440 dns_rdata_a_to_wire(&towire
, MY_IPV4_ADDR
);
441 dnssd_hardwired_add("ns", proxied_domain_ld
, towire
.p
- wire
.data
, wire
.data
, dns_rrtype_a
);
447 dns_rdata_aaaa_to_wire(&towire
, MY_IPV6_ADDR
);
448 dnssd_hardwired_add("ns", proxied_domain_ld
, towire
.p
- wire
.data
, wire
.data
, dns_rrtype_aaaa
);
455 dns_full_name_to_wire(NULL
, &towire
, MY_NAME
);
457 dns_name_to_wire(NULL
, &towire
, "ns");
458 dns_full_name_to_wire(NULL
, &towire
, proxied_domain
);
460 dnssd_hardwired_add("", proxied_domain
, towire
.p
- wire
.data
, wire
.data
, dns_rrtype_ns
);
462 // SOA (piggybacking on what we already did for NS, which starts the same.
463 dns_name_to_wire(NULL
, &towire
, "postmaster");
464 dns_full_name_to_wire(NULL
, &towire
, proxied_domain
);
465 dns_u32_to_wire(&towire
, 0); // serial
466 dns_ttl_to_wire(&towire
, 7200); // refresh
467 dns_ttl_to_wire(&towire
, 3600); // retry
468 dns_ttl_to_wire(&towire
, 86400); // expire
469 dns_ttl_to_wire(&towire
, 120); // minimum
470 dnssd_hardwired_add("", proxied_domain
, towire
.p
- wire
.data
, wire
.data
, dns_rrtype_soa
);
474 dp_query_send_dns_response(dnssd_query_t
*query
)
477 dns_towire_state_t
*towire
= &query
->towire
;
478 const char *failnote
= NULL
;
480 // Send an SOA record if it's a .local query.
481 if (query
->enclosing_domain
!= NULL
) {
482 // DNSSD Hybrid, Section 6.1.
483 TOWIRE_CHECK("&query->enclosing_domain_pointer", towire
,
484 dns_pointer_to_wire(NULL
, towire
, &query
->enclosing_domain_pointer
));
485 TOWIRE_CHECK("dns_rrtype_soa", towire
,
486 dns_u16_to_wire(towire
, dns_rrtype_soa
));
487 TOWIRE_CHECK("dns_qclass_in", towire
,
488 dns_u16_to_wire(towire
, dns_qclass_in
));
489 TOWIRE_CHECK("ttl", towire
, dns_ttl_to_wire(towire
, 3600));
490 TOWIRE_CHECK("rdlength_begin ", towire
, dns_rdlength_begin(towire
));
492 TOWIRE_CHECK(MY_NAME
, towire
, dns_full_name_to_wire(NULL
, towire
, MY_NAME
));
494 TOWIRE_CHECK("\"ns\"", towire
, dns_name_to_wire(NULL
, towire
, "ns"));
495 TOWIRE_CHECK("&query->enclosing_domain_pointer", towire
,
496 dns_pointer_to_wire(NULL
, towire
, &query
->enclosing_domain_pointer
));
498 TOWIRE_CHECK("\"postmaster\"", towire
,
499 dns_name_to_wire(NULL
, towire
, "postmaster"));
500 TOWIRE_CHECK("&query->enclosing_domain_pointer", towire
,
501 dns_pointer_to_wire(NULL
, towire
, &query
->enclosing_domain_pointer
));
502 TOWIRE_CHECK("serial", towire
,dns_u32_to_wire(towire
, 0)); // serial
503 TOWIRE_CHECK("refresh", towire
, dns_ttl_to_wire(towire
, 7200)); // refresh
504 TOWIRE_CHECK("retry", towire
, dns_ttl_to_wire(towire
, 3600)); // retry
505 TOWIRE_CHECK("expire", towire
, dns_ttl_to_wire(towire
, 86400)); // expire
506 TOWIRE_CHECK("minimum", towire
, dns_ttl_to_wire(towire
, 120)); // minimum
507 dns_rdlength_end(towire
);
508 query
->response
.nscount
= htons(1);
510 // Response is authoritative and not recursive.
511 query
->response
.bitfield
= htons((ntohs(query
->response
.bitfield
) | dns_flags_aa
) & ~dns_flags_ra
);
513 // Response is recursive and not authoritative.
514 query
->response
.bitfield
= htons((ntohs(query
->response
.bitfield
) | dns_flags_ra
) & ~dns_flags_aa
);
516 // Not truncated, not authentic, checking not disabled.
517 query
->response
.bitfield
= htons(ntohs(query
->response
.bitfield
) & ~(dns_flags_rd
| dns_flags_tc
| dns_flags_ad
| dns_flags_cd
));
519 // This is a response
520 dns_qr_set(&query
->response
, dns_qr_response
);
522 dns_rcode_set(&query
->response
, dns_rcode_noerror
);
524 // Send an OPT RR if we got one
525 if (query
->is_edns0
) {
526 TOWIRE_CHECK("Root label", towire
, dns_u8_to_wire(towire
, 0)); // Root label
527 TOWIRE_CHECK("dns_rrtype_opt", towire
, dns_u16_to_wire(towire
, dns_rrtype_opt
));
528 TOWIRE_CHECK("UDP Payload size", towire
, dns_u16_to_wire(towire
, 4096)); // UDP Payload size
529 TOWIRE_CHECK("extended-rcode", towire
, dns_u8_to_wire(towire
, 0)); // extended-rcode
530 TOWIRE_CHECK("EDNS version 0", towire
, dns_u8_to_wire(towire
, 0)); // EDNS version 0
531 TOWIRE_CHECK("No extended flags", towire
, dns_u16_to_wire(towire
, 0)); // No extended flags
532 TOWIRE_CHECK("No payload", towire
, dns_u16_to_wire(towire
, 0)); // No payload
533 query
->response
.arcount
= htons(1);
537 ERROR("dp_query_send_dns_response failed on %s", failnote
);
540 iov
.iov_len
= (query
->towire
.p
- (uint8_t *)&query
->response
);
541 iov
.iov_base
= &query
->response
;
542 INFO("dp_query_send_dns_response: %s (len %zd)", query
->name
, iov
.iov_len
);
544 if (query
->connection
!= NULL
) {
545 query
->connection
->send_response(query
->connection
, query
->question
, &iov
, 1);
549 dnssd_query_cancel(&query
->io
);
550 // Query will be freed automatically next time through the io loop.
554 dp_query_towire_reset(dnssd_query_t
*query
)
556 query
->towire
.p
= &query
->response
.data
[0]; // We start storing RR data here.
557 query
->towire
.lim
= &query
->response
.data
[DNS_DATA_SIZE
]; // This is the limit to how much we can store.
558 query
->towire
.message
= &query
->response
;
559 query
->p_dso_length
= NULL
;
563 dns_push_start(dnssd_query_t
*query
)
565 const char *failnote
= NULL
;
567 // If we don't have a dso header yet, start one.
568 if (query
->p_dso_length
== NULL
) {
569 memset(&query
->response
, 0, (sizeof query
->response
) - DNS_DATA_SIZE
);
570 dns_opcode_set(&query
->response
, dns_opcode_dso
);
571 // This is a unidirectional DSO message, which is marked as a query
572 dns_qr_set(&query
->response
, dns_qr_query
);
573 // No error cuz not a response.
574 dns_rcode_set(&query
->response
, dns_rcode_noerror
);
576 TOWIRE_CHECK("kDSOType_DNSPushUpdate", &query
->towire
,
577 dns_u16_to_wire(&query
->towire
, kDSOType_DNSPushUpdate
));
578 if (query
->towire
.p
+ 2 > query
->towire
.lim
) {
579 ERROR("No room for dso length in DNS Push notification message.");
580 dp_query_towire_reset(query
);
583 query
->p_dso_length
= query
->towire
.p
;
584 query
->towire
.p
+= 2;
586 if (failnote
!= NULL
) {
587 ERROR("dns_push_start: couldn't start update: %s", failnote
);
592 dp_push_response(dnssd_query_t
*query
)
596 if (query
->p_dso_length
!= NULL
) {
597 int16_t dso_length
= query
->towire
.p
- query
->p_dso_length
- 2;
598 iov
.iov_len
= (query
->towire
.p
- (uint8_t *)&query
->response
);
599 iov
.iov_base
= &query
->response
;
600 INFO("dp_push_response: %s (len %zd)", query
->name
, iov
.iov_len
);
602 query
->towire
.p
= query
->p_dso_length
;
603 dns_u16_to_wire(&query
->towire
, dso_length
);
604 if (query
->connection
!= NULL
) {
605 query
->connection
->send_response(query
->connection
, query
->question
, &iov
, 1);
607 dp_query_towire_reset(query
);
612 dnssd_hardwired_response(dnssd_query_t
*query
, DNSServiceQueryRecordReply callback
)
615 bool got_response
= false;
617 for (hp
= hardwired_responses
; hp
; hp
= hp
->next
) {
618 if ((query
->type
== hp
->type
|| query
->type
== dns_rrtype_any
) &&
619 query
->qclass
== dns_qclass_in
&& !strcasecmp(hp
->name
, query
->name
)) {
620 if (query
->is_dns_push
) {
621 dns_push_start(query
);
622 dp_query_add_data_to_response(query
, hp
->fullname
, hp
->type
, dns_qclass_in
, hp
->rdlen
, hp
->rdata
, 3600);
624 // Store the response
625 dp_query_add_data_to_response(query
, hp
->fullname
, hp
->type
, dns_qclass_in
, hp
->rdlen
, hp
->rdata
, 3600);
626 query
->response
.ancount
= htons(ntohs(query
->response
.ancount
) + 1);
632 if (query
->is_dns_push
) {
633 dp_push_response(query
);
635 // Steal the question
636 query
->question
= query
->connection
->message
;
637 query
->connection
->message
= NULL
;
638 // Send the answer(s).
639 dp_query_send_dns_response(query
);
646 // This is the callback for dns query results.
648 dns_query_callback(DNSServiceRef sdRef
, DNSServiceFlags flags
, uint32_t interfaceIndex
, DNSServiceErrorType errorCode
,
649 const char *fullname
, uint16_t rrtype
, uint16_t rrclass
, uint16_t rdlen
, const void *rdata
,
650 uint32_t ttl
, void *context
)
652 dnssd_query_t
*query
= context
;
654 INFO("%s %d %d %x %d", fullname
, rrtype
, rrclass
, rdlen
, errorCode
);
656 if (errorCode
== kDNSServiceErr_NoError
) {
657 dp_query_add_data_to_response(query
, fullname
, rrtype
, rrclass
, rdlen
, rdata
,
658 ttl
> 10 ? 10 : ttl
); // Per dnssd-hybrid 5.5.1, limit ttl to 10 seconds
659 query
->response
.ancount
= htons(ntohs(query
->response
.ancount
) + 1);
660 // If there isn't more coming, send the response now
661 if (!(flags
& kDNSServiceFlagsMoreComing
)) {
662 dp_query_send_dns_response(query
);
664 } else if (errorCode
== kDNSServiceErr_NoSuchRecord
) {
665 // If we get "no such record," we can't really do much except return the answer.
666 dp_query_send_dns_response(query
);
668 dns_rcode_set(&query
->response
, dns_rcode_servfail
);
669 dp_query_send_dns_response(query
);
674 dp_query_wakeup(io_t
*io
)
676 dnssd_query_t
*query
= (dnssd_query_t
*)io
;
677 char name
[DNS_MAX_NAME_SIZE
+ 1];
678 int namelen
= strlen(query
->name
);
680 // Should never happen.
681 if ((namelen
+ query
->enclosing_domain
!= NULL
? sizeof local_suffix
: 0) > sizeof name
) {
682 ERROR("db_query_wakeup: no space to construct name.");
683 dnssd_query_cancel(&query
->io
);
686 strcpy(name
, query
->name
);
687 if (query
->enclosing_domain
!= NULL
) {
688 strcpy(name
+ namelen
, local_suffix
);
690 dp_query_send_dns_response(query
);
694 dp_query_start(comm_t
*comm
, dnssd_query_t
*query
, int *rcode
, DNSServiceQueryRecordReply callback
)
696 char name
[DNS_MAX_NAME_SIZE
+ 1];
699 if (query
->enclosing_domain
!= NULL
) {
700 if (dnssd_hardwired_response(query
, callback
)) {
701 *rcode
= dns_rcode_noerror
;
705 int len
= strlen(query
->name
);
706 if (len
+ sizeof local_suffix
> sizeof name
) {
707 *rcode
= dns_rcode_servfail
;
710 ERROR("question name %s is too long for .local.", name
);
713 memcpy(name
, query
->name
, len
);
714 memcpy(&name
[len
], local_suffix
, sizeof local_suffix
);
720 // Issue a DNSServiceQueryRecord call
721 int err
= DNSServiceQueryRecord(&query
->ref
, query
->serviceFlags
,
722 kDNSServiceInterfaceIndexAny
, np
, query
->type
,
723 query
->qclass
, callback
, query
);
724 if (err
!= kDNSServiceErr_NoError
) {
725 ERROR("dp_query_start: DNSServiceQueryRecord failed for '%s': %d", np
, err
);
726 *rcode
= dns_rcode_servfail
;
729 INFO("dp_query_start: DNSServiceQueryRecord started for '%s': %d", np
, err
);
732 // If this isn't a DNS Push subscription, we need to respond quickly with as much data as we have. It
733 // turns out that dig gives us a second, but also that responses seem to come back in on the order of a
734 // millisecond, so we'll wait 100ms.
735 if (!query
->is_dns_push
&& query
->enclosing_domain
) {
736 query
->io
.wakeup_time
= ioloop_now
+ IOLOOP_SECOND
/ 10;
737 query
->io
.wakeup
= dp_query_wakeup
;
740 add_dnssd_query(query
);
745 dp_query_generate(comm_t
*comm
, dns_rr_t
*question
, bool dns_push
, int *rcode
)
747 char name
[DNS_MAX_NAME_SIZE
+ 1];
748 const char *enclosing_domain
;
750 // If it's a query for a name served by the local discovery proxy, do an mDNS lookup.
751 if ((dp_served(question
->name
, name
, sizeof name
))) {
752 enclosing_domain
= proxied_domain
;
753 INFO("%s question: type %d class %d %s%s -> %s.local", dns_push
? "push" : " dns",
754 question
->type
, question
->qclass
, name
, proxied_domain
, name
);
756 dns_name_print(question
->name
, name
, sizeof name
);
757 enclosing_domain
= NULL
;
758 INFO("%s question: type %d class %d %s",
759 dns_push
? "push" : " dns", question
->type
, question
->qclass
, name
);
762 dnssd_query_t
*query
= malloc(sizeof *query
);
764 ERROR("Unable to allocate memory for query on %s", name
);
765 *rcode
= dns_rcode_servfail
;
768 // Zero out everything except the message data buffer, which is large and doesn't need it.
769 memset(query
, 0, (sizeof *query
) - (sizeof query
->response
) + DNS_HEADER_SIZE
);
771 // Steal the data from the question. If subdomain is not null, this is a local mDNS query; otherwise
773 INFO("name = %s", name
);
774 query
->name
= strdup(name
);
776 *rcode
= dns_rcode_servfail
;
778 ERROR("unable to allocate memory for question name on %s", name
);
781 // It is safe to assume that enclosing domain will not be freed out from under us.
782 query
->enclosing_domain
= enclosing_domain
;
783 query
->serviceFlags
= 0;
785 // If this is a local query, add ".local" to the end of the name and require multicast.
786 if (enclosing_domain
!= NULL
) {
787 query
->serviceFlags
|= kDNSServiceFlagsForceMulticast
;
789 query
->serviceFlags
|= kDNSServiceFlagsReturnIntermediates
;
791 // Name now contains the name we want mDNSResponder to look up.
793 // XXX make sure finalize does the right thing.
794 query
->connection
= comm
;
796 // Remember whether this is a long-lived query.
797 query
->is_dns_push
= dns_push
;
799 // Start writing the response
800 dp_query_towire_reset(query
);
802 query
->type
= question
->type
;
803 query
->qclass
= question
->qclass
;
805 // Just in case we don't need to do a DNSServiceQueryRecord query to satisfy it.
808 *rcode
= dns_rcode_noerror
;
812 // This is the callback for DNS push query results, as opposed to push updates.
814 dns_push_query_callback(DNSServiceRef sdRef
, DNSServiceFlags flags
, uint32_t interfaceIndex
, DNSServiceErrorType errorCode
,
815 const char *fullname
, uint16_t rrtype
, uint16_t rrclass
, uint16_t rdlen
, const void *rdata
,
816 uint32_t ttl
, void *context
)
818 dnssd_query_t
*query
= context
;
820 // From DNSSD-Hybrid, for mDNS queries:
821 // If we have cached answers, respond immediately, because we probably have all the answers.
822 // If we don't have cached answers, respond as soon as we get an answer (presumably more-coming will be false).
824 // The spec says to not query if we have cached answers. We trust the DNSServiceQueryRecord call to handle this.
826 // If we switch to using a single connection to mDNSResponder, we could have !more-coming trigger a flush of
827 // all outstanding queries that aren't waiting on a time trigger. This is because more-coming isn't
830 INFO("PUSH %s %d %d %x %d", fullname
, rrtype
, rrclass
, rdlen
, errorCode
);
832 // query_state_waiting means that we're answering a regular DNS question
833 if (errorCode
== kDNSServiceErr_NoError
) {
834 dns_push_start(query
);
836 // If kDNSServiceFlagsAdd is set, it's an add, otherwise a delete.
837 if (flags
& kDNSServiceFlagsAdd
) {
838 dp_query_add_data_to_response(query
, fullname
, rrtype
, rrclass
, rdlen
, rdata
, ttl
);
840 // I think if this happens it means delete all RRs of this type.
842 dp_query_add_data_to_response(query
, fullname
, rrtype
, dns_qclass_any
, rdlen
, rdata
, 0);
844 dp_query_add_data_to_response(query
, fullname
, rrtype
, dns_qclass_none
, rdlen
, rdata
, 0);
847 // If there isn't more coming, send a DNS Push notification now.
848 // XXX If enough comes to fill the response, send the message.
849 if (!(flags
& kDNSServiceFlagsMoreComing
)) {
850 dp_push_response(query
);
853 ERROR("dns_push_query_callback: unexpected error code %d", errorCode
);
854 if (query
->connection
!= NULL
) {
855 dso_drop_activity(query
->connection
->dso
, query
->activity
);
861 dns_push_subscribe(comm_t
*comm
, dns_wire_t
*header
, dso_state_t
*dso
, dns_rr_t
*question
,
862 const char *activity_name
, const char *opcode_name
)
865 dnssd_query_t
*query
= dp_query_generate(comm
, question
, true, &rcode
);
868 dp_simple_response(comm
, rcode
);
872 dso_activity_t
*activity
= dso_add_activity(dso
, activity_name
, push_subscription_activity_type
, query
, dns_push_finalize
);
873 query
->activity
= activity
;
874 if (!dp_query_start(comm
, query
, &rcode
, dns_push_query_callback
)) {
875 dso_drop_activity(dso
, activity
);
876 dp_simple_response(comm
, rcode
);
879 dp_simple_response(comm
, dns_rcode_noerror
);
883 dns_push_reconfirm(comm_t
*comm
, dns_wire_t
*header
, dso_state_t
*dso
)
886 char name
[DNS_MAX_NAME_SIZE
+ 1];
889 // The TLV offset should always be pointing into the message.
890 unsigned offp
= dso
->primary
.payload
- &header
->data
[0];
891 int len
= offp
+ dso
->primary
.length
;
893 // Parse the name, rrtype and class. We say there's no rdata even though there is
894 // because there's no ttl and also we want the raw rdata, not parsed rdata.
895 if (!dns_rr_parse(&question
, header
->data
, len
, &offp
, false) ||
896 !dns_u16_parse(header
->data
, len
, &offp
, &rdlen
)) {
897 dp_simple_response(comm
, dns_rcode_formerr
);
898 ERROR("dns_push_reconfirm: RR parse from %s failed", dso
->remote_name
);
901 if (rdlen
+ offp
!= len
) {
902 dp_simple_response(comm
, dns_rcode_formerr
);
903 ERROR("dns_push_reconfirm: RRdata parse from %s failed: length mismatch (%d != %d)",
904 dso
->remote_name
, rdlen
+ offp
, len
);
908 if ((dp_served(question
.name
, name
, sizeof name
))) {
909 int len
= strlen(name
);
910 if (len
+ sizeof local_suffix
> sizeof name
) {
911 dp_simple_response(comm
, dns_rcode_formerr
);
912 ERROR("dns_push_reconfirm: name is too long for .local suffix: %s", name
);
915 memcpy(&name
[len
], local_suffix
, sizeof local_suffix
);
917 dns_name_print(question
.name
, &name
[8], sizeof name
- 8);
919 // transmogrify name.
920 DNSServiceReconfirmRecord(0, kDNSServiceInterfaceIndexAny
, name
,
921 question
.type
, question
.qclass
, rdlen
, &header
->data
[offp
]);
922 dp_simple_response(comm
, dns_rcode_noerror
);
926 dns_push_unsubscribe(comm_t
*comm
, dns_wire_t
*header
, dso_state_t
*dso
, dns_rr_t
*question
,
927 dso_activity_t
*activity
, const char *opcode_name
)
929 dso_drop_activity(dso
, activity
);
930 // No response, unsubscribe is unidirectional.
934 dns_push_subscription_change(const char *opcode_name
, comm_t
*comm
, dns_wire_t
*header
, dso_state_t
*dso
)
936 // type-in-hex/class-in-hex/name-to-subscribe
937 char activity_name
[DNS_MAX_NAME_SIZE_ESCAPED
+ 3 + 4 + 4];
938 dso_activity_t
*activity
;
940 // The TLV offset should always be pointing into the message.
941 unsigned offp
= dso
->primary
.payload
- &header
->data
[0];
945 if (!dns_rr_parse(&question
, header
->data
, offp
+ dso
->primary
.length
, &offp
, false)) {
946 // Unsubscribes are unidirectional, so no response can be sent
947 if (dso
->primary
.opcode
!= kDSOType_DNSPushUnsubscribe
) {
948 dp_simple_response(comm
, dns_rcode_formerr
);
950 ERROR("RR parse for %s from %s failed", dso
->remote_name
, opcode_name
);
954 // Concoct an activity name.
955 snprintf(activity_name
, sizeof activity_name
, "%04x%04x", question
.type
, question
.qclass
);
956 if ((dp_served(question
.name
, &activity_name
[8], (sizeof activity_name
) - 8))) {
957 int len
= strlen(activity_name
);
958 if (len
+ sizeof local_suffix
+ 8 > sizeof (activity_name
)) {
959 ERROR("activity name overflow for %s", activity_name
);
962 strncpy(&activity_name
[len
], local_suffix
, sizeof local_suffix
);
964 dns_name_print(question
.name
, &activity_name
[8], (sizeof activity_name
) - 8);
967 activity
= dso_find_activity(dso
, activity_name
, push_subscription_activity_type
, NULL
);
968 if (activity
== NULL
) {
969 // Unsubscribe with no activity means no work to do; just return noerror.
970 if (dso
->primary
.opcode
!= kDSOType_DNSPushSubscribe
) {
971 ERROR("dso_message: %s for %s when no subscription exists.", opcode_name
, activity_name
);
972 if (dso
->primary
.opcode
== kDSOType_DNSPushReconfirm
) {
973 dp_simple_response(comm
, dns_rcode_noerror
);
976 // In this case we have a push subscribe for which no subscription exists, which means we can do it.
977 dns_push_subscribe(comm
, header
, dso
, &question
, activity_name
, opcode_name
);
980 // Subscribe with a matching activity means no work to do; just return noerror.
981 if (dso
->primary
.opcode
== kDSOType_DNSPushSubscribe
) {
982 dp_simple_response(comm
, dns_rcode_noerror
);
984 // Otherwise cancel the subscription.
986 dns_push_unsubscribe(comm
, header
, dso
, &question
, activity
, opcode_name
);
991 static void dso_message(comm_t
*comm
, dns_wire_t
*header
, dso_state_t
*dso
)
993 switch(dso
->primary
.opcode
) {
994 case kDSOType_DNSPushSubscribe
:
995 dns_push_subscription_change("DNS Push Subscribe", comm
, header
, dso
);
997 case kDSOType_DNSPushUnsubscribe
:
998 dns_push_subscription_change("DNS Push Unsubscribe", comm
, header
, dso
);
1001 case kDSOType_DNSPushReconfirm
:
1002 dns_push_reconfirm(comm
, header
, dso
);
1005 case kDSOType_DNSPushUpdate
:
1006 INFO("dso_message: bogus push update message %d", dso
->primary
.opcode
);
1011 INFO("dso_message: unexpected primary TLV %d", dso
->primary
.opcode
);
1012 dp_simple_response(comm
, dns_rcode_dsotypeni
);
1015 // XXX free the message if we didn't consume it.
1018 static void dns_push_callback(void *context
, void *header_context
,
1019 dso_state_t
*dso
, dso_event_type_t eventType
)
1021 dns_wire_t
*header
= header_context
;
1024 case kDSOEventType_DNSMessage
:
1025 // We shouldn't get here because we already handled any DNS messages
1026 INFO("dns_push_callback: DNS Message (opcode=%d) received from %s", dns_opcode_get(header
), dso
->remote_name
);
1028 case kDSOEventType_DNSResponse
:
1029 // We shouldn't get here because we already handled any DNS messages
1030 INFO("dns_push_callback: DNS Response (opcode=%d) received from %s", dns_opcode_get(header
), dso
->remote_name
);
1032 case kDSOEventType_DSOMessage
:
1033 INFO("dns_push_callback: DSO Message (Primary TLV=%d) received from %s",
1034 dso
->primary
.opcode
, dso
->remote_name
);
1035 dso_message((comm_t
*)context
, (dns_wire_t
*)header
, dso
);
1037 case kDSOEventType_DSOResponse
:
1038 INFO("dns_push_callback: DSO Response (Primary TLV=%d) received from %s",
1039 dso
->primary
.opcode
, dso
->remote_name
);
1042 case kDSOEventType_Finalize
:
1043 INFO("dns_push_callback: Finalize");
1046 case kDSOEventType_Connected
:
1047 INFO("dns_push_callback: Connected to %s", dso
->remote_name
);
1050 case kDSOEventType_ConnectFailed
:
1051 INFO("dns_push_callback: Connection to %s failed", dso
->remote_name
);
1054 case kDSOEventType_Disconnected
:
1055 INFO("dns_push_callback: Connection to %s disconnected", dso
->remote_name
);
1061 dp_dns_query(comm_t
*comm
, dns_rr_t
*question
)
1064 dnssd_query_t
*query
= dp_query_generate(comm
, question
, false, &rcode
);
1065 const char *failnote
= NULL
;
1067 dp_simple_response(comm
, rcode
);
1071 // For regular DNS queries, copy the ID, etc.
1072 query
->response
.id
= comm
->message
->wire
.id
;
1073 query
->response
.bitfield
= comm
->message
->wire
.bitfield
;
1074 dns_rcode_set(&query
->response
, dns_rcode_noerror
);
1076 // For DNS queries, we need to return the question.
1077 query
->response
.qdcount
= htons(1);
1078 if (query
->enclosing_domain
!= NULL
) {
1079 TOWIRE_CHECK("name", &query
->towire
, dns_name_to_wire(NULL
, &query
->towire
, query
->name
));
1080 TOWIRE_CHECK("enclosing_domain", &query
->towire
,
1081 dns_full_name_to_wire(&query
->enclosing_domain_pointer
,
1082 &query
->towire
, query
->enclosing_domain
));
1084 TOWIRE_CHECK("full name", &query
->towire
, dns_full_name_to_wire(NULL
, &query
->towire
, query
->name
));
1086 TOWIRE_CHECK("TYPE", &query
->towire
, dns_u16_to_wire(&query
->towire
, question
->type
)); // TYPE
1087 TOWIRE_CHECK("CLASS", &query
->towire
, dns_u16_to_wire(&query
->towire
, question
->qclass
)); // CLASS
1088 if (failnote
!= NULL
) {
1089 ERROR("dp_dns_query: failure encoding question: %s", failnote
);
1093 // We should check for OPT RR, but for now assume it's there.
1094 query
->is_edns0
= true;
1096 if (!dp_query_start(comm
, query
, &rcode
, dns_query_callback
)) {
1098 dp_simple_response(comm
, rcode
);
1104 // XXX make sure that finalize frees this.
1105 query
->question
= comm
->message
;
1106 comm
->message
= NULL
;
1109 void dso_transport_finalize(comm_t
*comm
)
1111 dso_state_t
*dso
= comm
->dso
;
1112 INFO("dso_transport_finalize: %s", dso
->remote_name
);
1114 ioloop_close(&comm
->io
);
1120 void dns_evaluate(comm_t
*comm
)
1123 unsigned offset
= 0;
1125 // Drop incoming responses--we're a server, so we only accept queries.
1126 if (dns_qr_get(&comm
->message
->wire
) == dns_qr_response
) {
1130 // If this is a DSO message, see if we have a session yet.
1131 switch(dns_opcode_get(&comm
->message
->wire
)) {
1132 case dns_opcode_dso
:
1133 if (!comm
->tcp_stream
) {
1134 ERROR("DSO message received on non-tcp socket %s", comm
->name
);
1135 dp_simple_response(comm
, dns_rcode_notimp
);
1140 comm
->dso
= dso_create(true, 0, comm
->name
, dns_push_callback
, comm
, comm
);
1142 ERROR("Unable to create a dso context for %s", comm
->name
);
1143 dp_simple_response(comm
, dns_rcode_servfail
);
1144 ioloop_close(&comm
->io
);
1147 comm
->dso
->transport_finalize
= dso_transport_finalize
;
1149 dso_message_received(comm
->dso
, (uint8_t *)&comm
->message
->wire
, comm
->message
->length
);
1152 case dns_opcode_query
:
1153 // In theory this is permitted but it can't really be implemented because there's no way
1154 // to say "here's the answer for this, and here's why that failed.
1155 if (ntohs(comm
->message
->wire
.qdcount
) != 1) {
1156 dp_simple_response(comm
, dns_rcode_formerr
);
1159 if (!dns_rr_parse(&question
, comm
->message
->wire
.data
, comm
->message
->length
, &offset
, 0)) {
1160 dp_simple_response(comm
, dns_rcode_formerr
);
1163 dp_dns_query(comm
, &question
);
1164 dns_rrdata_free(&question
);
1167 // No support for other opcodes yet.
1169 dp_simple_response(comm
, dns_rcode_notimp
);
1174 void dns_input(comm_t
*comm
)
1177 if (comm
->message
!= NULL
) {
1178 message_free(comm
->message
);
1179 comm
->message
= NULL
;
1184 usage(const char *progname
)
1186 ERROR("usage: %s", progname
);
1187 ERROR("ex: dnssd-proxy");
1191 // Called whenever we get a connection.
1193 connected(comm_t
*comm
)
1195 INFO("connection from %s", comm
->name
);
1200 main(int argc
, char **argv
)
1204 comm_t
*tcp4_listener
;
1205 comm_t
*udp4_listener
;
1209 // Read the configuration from the command line.
1210 for (i
= 1; i
< argc
; i
++) {
1211 return usage(argv
[0]);
1214 if (!ioloop_init()) {
1218 // Set up hardwired answers
1219 dnssd_hardwired_setup();
1221 // XXX Support IPv6!
1222 tcp4_listener
= setup_listener_socket(AF_INET
, IPPROTO_TCP
, port
, "IPv4 DNS Push Listener", dns_input
, connected
, 0);
1223 if (tcp4_listener
== NULL
) {
1224 ERROR("TCPv4 listener: fail.");
1228 udp4_listener
= setup_listener_socket(AF_INET
, IPPROTO_UDP
, port
, "IPv4 DNS UDP Listener", dns_input
, 0, 0);
1229 if (udp4_listener
== NULL
) {
1230 ERROR("UDP4 listener: fail.");
1236 something
= ioloop_events(0);
1237 INFO("dispatched %d events.", something
);
1244 // c-file-style: "bsd"
1245 // c-basic-offset: 4
1247 // indent-tabs-mode: nil