]> git.saurik.com Git - apple/mdnsresponder.git/blob - ServiceRegistration/dnssd-proxy.c
mDNSResponder-1096.100.3.tar.gz
[apple/mdnsresponder.git] / ServiceRegistration / dnssd-proxy.c
1 /* dnssd-proxy.c
2 *
3 * Copyright (c) 2018-2019 Apple Computer, Inc. All rights reserved.
4 *
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
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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.
16 *
17 * This is a Discovery Proxy module for the SRP gateway.
18 *
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.
24 *
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:
27 *
28 * https://opensource.apple.com/tarballs/mDNSResponder/
29 */
30
31 #define __APPLE_USE_RFC_3542
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <unistd.h>
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>
42 #include <fcntl.h>
43 #include <sys/time.h>
44 #include <ctype.h>
45
46 #include "dns_sd.h"
47 #include "srp.h"
48 #include "dns-msg.h"
49 #include "srp-crypto.h"
50 #include "dso.h"
51 #include "ioloop.h"
52
53 // Enumerate the list of interfaces, map them to interface indexes, give each one a name
54 // Have a tree of subdomains for matching
55
56 typedef struct dnssd_query {
57 io_t io;
58 DNSServiceRef ref;
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;
62 message_t *question;
63 comm_t *connection;
64 dso_activity_t *activity;
65 int serviceFlags; // Service flags to use with this query.
66 bool is_dns_push;
67 bool is_edns0;
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.
72 } dnssd_query_t;
73
74 const char push_subscription_activity_type[] = "push subscription";
75
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
83
84 #define TOWIRE_CHECK(note, towire, func) { func; if ((towire)->error != 0 && failnote == NULL) failnote = (note); }
85
86 int64_t dso_transport_idle(void *context, int64_t next_event)
87 {
88 return next_event;
89 }
90
91 void dnssd_query_cancel(io_t *io)
92 {
93 dnssd_query_t *query = (dnssd_query_t *)io;
94 if (query->io.sock != -1) {
95 DNSServiceRefDeallocate(query->ref);
96 query->io.sock = -1;
97 }
98 query->connection = NULL;
99 }
100
101 void
102 dns_push_finalize(dso_activity_t *activity)
103 {
104 dnssd_query_t *query = (dnssd_query_t *)activity->context;
105 INFO("dnssd_push_finalize: %s", activity->name);
106 dnssd_query_cancel(&query->io);
107 }
108
109 void
110 dnssd_query_finalize(io_t *io)
111 {
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);
116 }
117 free(query->name);
118 free(query);
119 }
120
121 static void
122 dnssd_query_callback(io_t *io)
123 {
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);
130 } else {
131 dnssd_query_cancel(&query->io);
132 }
133 }
134 }
135
136 static void
137 add_dnssd_query(dnssd_query_t *query)
138 {
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);
144 }
145
146 // Parse a NUL-terminated text string into a sequence of labels.
147 dns_name_t *
148 dns_pres_name_parse(const char *pname)
149 {
150 const char *dot = strchr(pname, '.');
151 dns_label_t *ret;
152 int len;
153 if (dot == NULL) {
154 dot = pname + strlen(pname);
155 }
156 len = (dot - pname) + 1 + (sizeof *ret) - DNS_MAX_LABEL_SIZE;
157 ret = calloc(len, 1);
158 if (ret == NULL) {
159 return NULL;
160 }
161 ret->len = dot - pname;
162 if (ret->len > 0) {
163 memcpy(ret->data, pname, ret->len);
164 }
165 ret->data[ret->len] = 0;
166 if (dot[0] == '.') {
167 ret->next = dns_pres_name_parse(dot + 1);
168 }
169 return ret;
170 }
171
172 bool
173 dns_subdomain_of(dns_name_t *name, dns_name_t *domain, char *buf, size_t buflen)
174 {
175 int dnum = 0, nnum = 0;
176 dns_name_t *np, *dp;
177 char *bufp = buf;
178 size_t bytesleft = buflen;
179
180 for (dp = domain; dp; dp = dp->next) {
181 dnum++;
182 }
183 for (np = name; np; np = np->next) {
184 nnum++;
185 }
186 if (nnum < dnum) {
187 return false;
188 }
189 for (np = name; np; np = np->next) {
190 if (nnum-- == dnum) {
191 break;
192 }
193 }
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!");
199 return false;
200 }
201 memcpy(bufp, dp->data, dp->len);
202 bufp += dp->len;
203 bytesleft = bytesleft - dp->len;
204 if (dp->next != np) {
205 *bufp++ = '.';
206 bytesleft -= dp->len;
207 }
208 }
209 *bufp = 0;
210 return true;
211 }
212 return false;
213 }
214
215 void
216 dp_simple_response(comm_t *comm, int rcode)
217 {
218 if (comm->send_response) {
219 struct iovec iov;
220 dns_wire_t response;
221 memset(&response, 0, DNS_HEADER_SIZE);
222
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);
233 }
234 }
235
236 bool
237 dp_served(dns_name_t *name, char *buf, size_t bufsize)
238 {
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);
244 return false;
245 }
246 }
247
248 // For now we treat any query to home.arpa as local.
249 return dns_subdomain_of(name, home_dot_arpa, buf, bufsize);
250 }
251
252 // Utility function to find "local" on the end of a string of labels.
253 bool
254 truncate_local(dns_name_t *name)
255 {
256 dns_label_t *lp, *prev, *prevprev;
257
258 prevprev = prev = NULL;
259 // Find the root label.
260 for (lp = name; lp && lp->len; lp = lp->next) {
261 prevprev = prev;
262 prev = lp;
263 }
264 if (lp && prev && prevprev) {
265 if (prev->len == 5 && !strncasecmp(prev->data, "local", 5)) {
266 dns_name_free(prev);
267 prevprev->next = NULL;
268 return true;
269 }
270 }
271 dns_name_free(name);
272 return false;
273 }
274
275 void
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)
278 {
279 dns_towire_state_t *towire = &query->towire;
280 const char *failnote = NULL;
281
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);
290 } else {
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);
294 }
295 } else {
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);
299 }
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));
303
304 if (rdlen > 0) {
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)) {
309 dns_rr_t answer;
310 dns_name_t *name;
311 unsigned offp = 0;
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!!");
316 goto raw;
317 }
318 switch(rrtype) {
319 case dns_rrtype_cname:
320 case dns_rrtype_ptr:
321 name = answer.data.ptr.name;
322 if (!truncate_local(name)) {
323 goto raw;
324 }
325 TOWIRE_CHECK("rdlength begin", towire, dns_rdlength_begin(towire));
326 break;
327 case dns_rrtype_srv:
328 name = answer.data.srv.name;
329 if (!truncate_local(name)) {
330 goto raw;
331 }
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));
336 break;
337 default:
338 ERROR("dp_query_add_data_to_response: can't get here.");
339 goto raw;
340 break;
341 }
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));
348 } else {
349 TOWIRE_CHECK("enclosing_domain internal", towire,
350 dns_full_name_to_wire(NULL, towire, query->enclosing_domain));
351 }
352 dns_rdlength_end(towire);
353 } else {
354 raw:
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));
357 }
358 } else {
359 TOWIRE_CHECK("rdlen", towire, dns_u16_to_wire(towire, rdlen));
360 }
361 if (failnote) {
362 ERROR("dp_query_add_data_to_response: %s", failnote);
363 }
364 }
365
366 typedef struct hardwired hardwired_t;
367 struct hardwired {
368 hardwired_t *next;
369 uint16_t type;
370 char *name;
371 char *fullname;
372 uint8_t *rdata;
373 uint16_t rdlen;
374 } *hardwired_responses;
375
376 void
377 dnssd_hardwired_add(const char *name, const char *domain, size_t rdlen, uint8_t *rdata, uint16_t type)
378 {
379 hardwired_t *hp;
380 int namelen = strlen(name);
381 size_t total = (sizeof *hp) + rdlen + namelen * 2 + strlen(proxied_domain_ld) + 2;
382
383 hp = calloc(1, (sizeof *hp) + rdlen + namelen * 2 + strlen(proxied_domain_ld) + 2);
384 hp->rdata = (uint8_t *)(hp + 1);
385 hp->rdlen = rdlen;
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);
394 }
395 hp->type = type;
396 hp->next = hardwired_responses;
397 hardwired_responses = hp;
398
399 INFO("hardwired_add: fullname %s name %s type %d rdlen %d", hp->fullname, hp->name, hp->type, hp->rdlen);
400 }
401
402 void
403 dnssd_hardwired_setup(void)
404 {
405 dns_wire_t wire;
406 dns_towire_state_t towire;
407
408 #define RESET \
409 memset(&towire, 0, sizeof towire); \
410 towire.message = &wire; \
411 towire.p = wire.data; \
412 towire.lim = towire.p + sizeof wire.data
413
414 // Browsing pointers...
415 RESET;
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);
419
420 // SRV
421 // _dns-push-tls._tcp
422 RESET;
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.
427 #ifndef MY_NAME
428 dns_name_to_wire(NULL, &towire, "ns");
429 dns_full_name_to_wire(NULL, &towire, proxied_domain);
430 #else
431 dns_full_name_to_wire(NULL, &towire, MY_NAME);
432 #endif
433 dnssd_hardwired_add("_dns-push-tls._tcp", proxied_domain_ld, towire.p - wire.data, wire.data, dns_rrtype_srv);
434
435 // A
436 #ifndef MY_NAME
437 // ns
438 #ifdef MY_IPV4_ADDR
439 RESET;
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);
442 #endif
443
444 // AAAA
445 #ifdef MY_IPV6_ADDR
446 RESET;
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);
449 #endif
450 #endif
451
452 // NS
453 RESET;
454 #ifdef MY_NAME
455 dns_full_name_to_wire(NULL, &towire, MY_NAME);
456 #else
457 dns_name_to_wire(NULL, &towire, "ns");
458 dns_full_name_to_wire(NULL, &towire, proxied_domain);
459 #endif
460 dnssd_hardwired_add("", proxied_domain, towire.p - wire.data, wire.data, dns_rrtype_ns);
461
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);
471 }
472
473 void
474 dp_query_send_dns_response(dnssd_query_t *query)
475 {
476 struct iovec iov;
477 dns_towire_state_t *towire = &query->towire;
478 const char *failnote = NULL;
479
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));
491 #ifdef MY_NAME
492 TOWIRE_CHECK(MY_NAME, towire, dns_full_name_to_wire(NULL, towire, MY_NAME));
493 #else
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));
497 #endif
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);
509
510 // Response is authoritative and not recursive.
511 query->response.bitfield = htons((ntohs(query->response.bitfield) | dns_flags_aa) & ~dns_flags_ra);
512 } else {
513 // Response is recursive and not authoritative.
514 query->response.bitfield = htons((ntohs(query->response.bitfield) | dns_flags_ra) & ~dns_flags_aa);
515 }
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));
518
519 // This is a response
520 dns_qr_set(&query->response, dns_qr_response);
521 // No error.
522 dns_rcode_set(&query->response, dns_rcode_noerror);
523
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);
534 }
535
536 if (towire->error) {
537 ERROR("dp_query_send_dns_response failed on %s", failnote);
538 }
539
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);
543
544 if (query->connection != NULL) {
545 query->connection->send_response(query->connection, query->question, &iov, 1);
546 }
547
548 // Free up state
549 dnssd_query_cancel(&query->io);
550 // Query will be freed automatically next time through the io loop.
551 }
552
553 void
554 dp_query_towire_reset(dnssd_query_t *query)
555 {
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;
560 }
561
562 void
563 dns_push_start(dnssd_query_t *query)
564 {
565 const char *failnote = NULL;
566
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);
575
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);
581 return;
582 }
583 query->p_dso_length = query->towire.p;
584 query->towire.p += 2;
585 }
586 if (failnote != NULL) {
587 ERROR("dns_push_start: couldn't start update: %s", failnote);
588 }
589 }
590
591 void
592 dp_push_response(dnssd_query_t *query)
593 {
594 struct iovec iov;
595
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);
601
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);
606 }
607 dp_query_towire_reset(query);
608 }
609 }
610
611 bool
612 dnssd_hardwired_response(dnssd_query_t *query, DNSServiceQueryRecordReply callback)
613 {
614 hardwired_t *hp;
615 bool got_response = false;
616
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);
623 } else {
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);
627 }
628 got_response = true;
629 }
630 }
631 if (got_response) {
632 if (query->is_dns_push) {
633 dp_push_response(query);
634 } else {
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);
640 }
641 return true;
642 }
643 return false;
644 }
645
646 // This is the callback for dns query results.
647 void
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)
651 {
652 dnssd_query_t *query = context;
653
654 INFO("%s %d %d %x %d", fullname, rrtype, rrclass, rdlen, errorCode);
655
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);
663 }
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);
667 } else {
668 dns_rcode_set(&query->response, dns_rcode_servfail);
669 dp_query_send_dns_response(query);
670 }
671 }
672
673 void
674 dp_query_wakeup(io_t *io)
675 {
676 dnssd_query_t *query = (dnssd_query_t *)io;
677 char name[DNS_MAX_NAME_SIZE + 1];
678 int namelen = strlen(query->name);
679
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);
684 }
685
686 strcpy(name, query->name);
687 if (query->enclosing_domain != NULL) {
688 strcpy(name + namelen, local_suffix);
689 }
690 dp_query_send_dns_response(query);
691 }
692
693 bool
694 dp_query_start(comm_t *comm, dnssd_query_t *query, int *rcode, DNSServiceQueryRecordReply callback)
695 {
696 char name[DNS_MAX_NAME_SIZE + 1];
697 char *np;
698
699 if (query->enclosing_domain != NULL) {
700 if (dnssd_hardwired_response(query, callback)) {
701 *rcode = dns_rcode_noerror;
702 return true;
703 }
704
705 int len = strlen(query->name);
706 if (len + sizeof local_suffix > sizeof name) {
707 *rcode = dns_rcode_servfail;
708 free(query->name);
709 free(query);
710 ERROR("question name %s is too long for .local.", name);
711 return false;
712 }
713 memcpy(name, query->name, len);
714 memcpy(&name[len], local_suffix, sizeof local_suffix);
715 np = name;
716 } else {
717 np = query->name;
718 }
719
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;
727 return false;
728 } else {
729 INFO("dp_query_start: DNSServiceQueryRecord started for '%s': %d", np, err);
730 }
731
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;
738 }
739
740 add_dnssd_query(query);
741 return true;
742 }
743
744 dnssd_query_t *
745 dp_query_generate(comm_t *comm, dns_rr_t *question, bool dns_push, int *rcode)
746 {
747 char name[DNS_MAX_NAME_SIZE + 1];
748 const char *enclosing_domain;
749
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);
755 } else {
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);
760 }
761
762 dnssd_query_t *query = malloc(sizeof *query);
763 if (query == NULL) {
764 ERROR("Unable to allocate memory for query on %s", name);
765 *rcode = dns_rcode_servfail;
766 return NULL;
767 }
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);
770
771 // Steal the data from the question. If subdomain is not null, this is a local mDNS query; otherwise
772 // we are recursing.
773 INFO("name = %s", name);
774 query->name = strdup(name);
775 if (!query->name) {
776 *rcode = dns_rcode_servfail;
777 free(query);
778 ERROR("unable to allocate memory for question name on %s", name);
779 return NULL;
780 }
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;
784
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;
788 } else {
789 query->serviceFlags |= kDNSServiceFlagsReturnIntermediates;
790 }
791 // Name now contains the name we want mDNSResponder to look up.
792
793 // XXX make sure finalize does the right thing.
794 query->connection = comm;
795
796 // Remember whether this is a long-lived query.
797 query->is_dns_push = dns_push;
798
799 // Start writing the response
800 dp_query_towire_reset(query);
801
802 query->type = question->type;
803 query->qclass = question->qclass;
804
805 // Just in case we don't need to do a DNSServiceQueryRecord query to satisfy it.
806 query->io.sock = -1;
807
808 *rcode = dns_rcode_noerror;
809 return query;
810 }
811
812 // This is the callback for DNS push query results, as opposed to push updates.
813 void
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)
817 {
818 dnssd_query_t *query = context;
819
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).
823
824 // The spec says to not query if we have cached answers. We trust the DNSServiceQueryRecord call to handle this.
825
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
828 // query-specific
829
830 INFO("PUSH %s %d %d %x %d", fullname, rrtype, rrclass, rdlen, errorCode);
831
832 // query_state_waiting means that we're answering a regular DNS question
833 if (errorCode == kDNSServiceErr_NoError) {
834 dns_push_start(query);
835
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);
839 } else {
840 // I think if this happens it means delete all RRs of this type.
841 if (rdlen == 0) {
842 dp_query_add_data_to_response(query, fullname, rrtype, dns_qclass_any, rdlen, rdata, 0);
843 } else {
844 dp_query_add_data_to_response(query, fullname, rrtype, dns_qclass_none, rdlen, rdata, 0);
845 }
846 }
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);
851 }
852 } else {
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);
856 }
857 }
858 }
859
860 void
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)
863 {
864 int rcode;
865 dnssd_query_t *query = dp_query_generate(comm, question, true, &rcode);
866
867 if (!query) {
868 dp_simple_response(comm, rcode);
869 return;
870 }
871
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);
877 return;
878 }
879 dp_simple_response(comm, dns_rcode_noerror);
880 }
881
882 void
883 dns_push_reconfirm(comm_t *comm, dns_wire_t *header, dso_state_t *dso)
884 {
885 dns_rr_t question;
886 char name[DNS_MAX_NAME_SIZE + 1];
887 uint16_t rdlen;
888
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;
892
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);
899 return;
900 }
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);
905 return;
906 }
907
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);
913 return;
914 }
915 memcpy(&name[len], local_suffix, sizeof local_suffix);
916 } else {
917 dns_name_print(question.name, &name[8], sizeof name - 8);
918 }
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);
923 }
924
925 void
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)
928 {
929 dso_drop_activity(dso, activity);
930 // No response, unsubscribe is unidirectional.
931 }
932
933 void
934 dns_push_subscription_change(const char *opcode_name, comm_t *comm, dns_wire_t *header, dso_state_t *dso)
935 {
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;
939
940 // The TLV offset should always be pointing into the message.
941 unsigned offp = dso->primary.payload - &header->data[0];
942 // Get the question
943 dns_rr_t question;
944
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);
949 }
950 ERROR("RR parse for %s from %s failed", dso->remote_name, opcode_name);
951 return;
952 }
953
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);
960 return;
961 }
962 strncpy(&activity_name[len], local_suffix, sizeof local_suffix);
963 } else {
964 dns_name_print(question.name, &activity_name[8], (sizeof activity_name) - 8);
965 }
966
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);
974 }
975 } else {
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);
978 }
979 } else {
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);
983 }
984 // Otherwise cancel the subscription.
985 else {
986 dns_push_unsubscribe(comm, header, dso, &question, activity, opcode_name);
987 }
988 }
989 }
990
991 static void dso_message(comm_t *comm, dns_wire_t *header, dso_state_t *dso)
992 {
993 switch(dso->primary.opcode) {
994 case kDSOType_DNSPushSubscribe:
995 dns_push_subscription_change("DNS Push Subscribe", comm, header, dso);
996 break;
997 case kDSOType_DNSPushUnsubscribe:
998 dns_push_subscription_change("DNS Push Unsubscribe", comm, header, dso);
999 break;
1000
1001 case kDSOType_DNSPushReconfirm:
1002 dns_push_reconfirm(comm, header, dso);
1003 break;
1004
1005 case kDSOType_DNSPushUpdate:
1006 INFO("dso_message: bogus push update message %d", dso->primary.opcode);
1007 dso_drop(dso);
1008 break;
1009
1010 default:
1011 INFO("dso_message: unexpected primary TLV %d", dso->primary.opcode);
1012 dp_simple_response(comm, dns_rcode_dsotypeni);
1013 break;
1014 }
1015 // XXX free the message if we didn't consume it.
1016 }
1017
1018 static void dns_push_callback(void *context, void *header_context,
1019 dso_state_t *dso, dso_event_type_t eventType)
1020 {
1021 dns_wire_t *header = header_context;
1022 switch(eventType)
1023 {
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);
1027 break;
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);
1031 break;
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);
1036 break;
1037 case kDSOEventType_DSOResponse:
1038 INFO("dns_push_callback: DSO Response (Primary TLV=%d) received from %s",
1039 dso->primary.opcode, dso->remote_name);
1040 break;
1041
1042 case kDSOEventType_Finalize:
1043 INFO("dns_push_callback: Finalize");
1044 break;
1045
1046 case kDSOEventType_Connected:
1047 INFO("dns_push_callback: Connected to %s", dso->remote_name);
1048 break;
1049
1050 case kDSOEventType_ConnectFailed:
1051 INFO("dns_push_callback: Connection to %s failed", dso->remote_name);
1052 break;
1053
1054 case kDSOEventType_Disconnected:
1055 INFO("dns_push_callback: Connection to %s disconnected", dso->remote_name);
1056 break;
1057 }
1058 }
1059
1060 void
1061 dp_dns_query(comm_t *comm, dns_rr_t *question)
1062 {
1063 int rcode;
1064 dnssd_query_t *query = dp_query_generate(comm, question, false, &rcode);
1065 const char *failnote = NULL;
1066 if (!query) {
1067 dp_simple_response(comm, rcode);
1068 return;
1069 }
1070
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);
1075
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));
1083 } else {
1084 TOWIRE_CHECK("full name", &query->towire, dns_full_name_to_wire(NULL, &query->towire, query->name));
1085 }
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);
1090 goto fail;
1091 }
1092
1093 // We should check for OPT RR, but for now assume it's there.
1094 query->is_edns0 = true;
1095
1096 if (!dp_query_start(comm, query, &rcode, dns_query_callback)) {
1097 fail:
1098 dp_simple_response(comm, rcode);
1099 free(query->name);
1100 free(query);
1101 return;
1102 }
1103
1104 // XXX make sure that finalize frees this.
1105 query->question = comm->message;
1106 comm->message = NULL;
1107 }
1108
1109 void dso_transport_finalize(comm_t *comm)
1110 {
1111 dso_state_t *dso = comm->dso;
1112 INFO("dso_transport_finalize: %s", dso->remote_name);
1113 if (comm) {
1114 ioloop_close(&comm->io);
1115 }
1116 free(dso);
1117 comm->dso = NULL;
1118 }
1119
1120 void dns_evaluate(comm_t *comm)
1121 {
1122 dns_rr_t question;
1123 unsigned offset = 0;
1124
1125 // Drop incoming responses--we're a server, so we only accept queries.
1126 if (dns_qr_get(&comm->message->wire) == dns_qr_response) {
1127 return;
1128 }
1129
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);
1136 return;
1137 }
1138
1139 if (!comm->dso) {
1140 comm->dso = dso_create(true, 0, comm->name, dns_push_callback, comm, comm);
1141 if (!comm->dso) {
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);
1145 return;
1146 }
1147 comm->dso->transport_finalize = dso_transport_finalize;
1148 }
1149 dso_message_received(comm->dso, (uint8_t *)&comm->message->wire, comm->message->length);
1150 break;
1151
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);
1157 return;
1158 }
1159 if (!dns_rr_parse(&question, comm->message->wire.data, comm->message->length, &offset, 0)) {
1160 dp_simple_response(comm, dns_rcode_formerr);
1161 return;
1162 }
1163 dp_dns_query(comm, &question);
1164 dns_rrdata_free(&question);
1165 break;
1166
1167 // No support for other opcodes yet.
1168 default:
1169 dp_simple_response(comm, dns_rcode_notimp);
1170 break;
1171 }
1172 }
1173
1174 void dns_input(comm_t *comm)
1175 {
1176 dns_evaluate(comm);
1177 if (comm->message != NULL) {
1178 message_free(comm->message);
1179 comm->message = NULL;
1180 }
1181 }
1182
1183 static int
1184 usage(const char *progname)
1185 {
1186 ERROR("usage: %s", progname);
1187 ERROR("ex: dnssd-proxy");
1188 return 1;
1189 }
1190
1191 // Called whenever we get a connection.
1192 void
1193 connected(comm_t *comm)
1194 {
1195 INFO("connection from %s", comm->name);
1196 return;
1197 }
1198
1199 int
1200 main(int argc, char **argv)
1201 {
1202 int i;
1203 int16_t port;
1204 comm_t *tcp4_listener;
1205 comm_t *udp4_listener;
1206
1207 port = htons(53);
1208
1209 // Read the configuration from the command line.
1210 for (i = 1; i < argc; i++) {
1211 return usage(argv[0]);
1212 }
1213
1214 if (!ioloop_init()) {
1215 return 1;
1216 }
1217
1218 // Set up hardwired answers
1219 dnssd_hardwired_setup();
1220
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.");
1225 return 1;
1226 }
1227
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.");
1231 return 1;
1232 }
1233
1234 do {
1235 int something = 0;
1236 something = ioloop_events(0);
1237 INFO("dispatched %d events.", something);
1238 } while (1);
1239 }
1240
1241 // Local Variables:
1242 // mode: C
1243 // tab-width: 4
1244 // c-file-style: "bsd"
1245 // c-basic-offset: 4
1246 // fill-column: 108
1247 // indent-tabs-mode: nil
1248 // End: