]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSPosix/dnsextd.c
64895aba6e757201837335c58b2224655821c80c
[apple/mdnsresponder.git] / mDNSPosix / dnsextd.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2004 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 Change History (most recent first):
18
19 $Log: dnsextd.c,v $
20 Revision 1.33.2.2 2006/08/29 06:24:34 cheshire
21 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
22
23 Revision 1.33.2.1 2005/08/05 21:14:00 ksekar
24 <rdar://problem/4012279> Long-lived queries not working on windows
25 Change constant names
26
27 Revision 1.33 2005/03/11 19:09:02 ksekar
28 Fixed ZERO_LLQID macro
29
30 Revision 1.32 2005/03/10 22:54:33 ksekar
31 <rdar://problem/4046285> dnsextd leaks memory/ports
32
33 Revision 1.31 2005/02/24 02:37:57 ksekar
34 <rdar://problem/4021977> dnsextd memory management improvements
35
36 Revision 1.30 2005/01/27 22:57:56 cheshire
37 Fix compile errors on gcc4
38
39 Revision 1.29 2004/12/22 00:13:50 ksekar
40 <rdar://problem/3873993> Change version, port, and polling interval for LLQ
41
42 Revision 1.28 2004/12/17 00:30:00 ksekar
43 <rdar://problem/3924045> dnsextd memory leak
44
45 Revision 1.27 2004/12/17 00:27:32 ksekar
46 Ignore SIGPIPE
47
48 Revision 1.26 2004/12/17 00:21:33 ksekar
49 Fixes for new CacheRecord structure with indirect name pointer
50
51 Revision 1.25 2004/12/16 20:13:02 cheshire
52 <rdar://problem/3324626> Cache memory management improvements
53
54 Revision 1.24 2004/12/14 17:09:06 ksekar
55 fixed incorrect usage instructions
56
57 Revision 1.23 2004/12/06 20:24:31 ksekar
58 <rdar://problem/3907303> dnsextd leaks sockets
59
60 Revision 1.22 2004/12/03 20:20:29 ksekar
61 <rdar://problem/3904149> dnsextd: support delivery of large records via LLQ events
62
63 Revision 1.21 2004/12/03 06:11:34 ksekar
64 <rdar://problem/3885059> clean up dnsextd arguments
65
66 Revision 1.20 2004/12/01 04:27:28 cheshire
67 <rdar://problem/3872803> Darwin patches for Solaris and Suse
68 Don't use uint32_t, etc. -- they require stdint.h, which doesn't exist on FreeBSD 4.x, Solaris, etc.
69
70 Revision 1.19 2004/12/01 01:16:29 cheshire
71 Solaris compatibility fixes
72
73 Revision 1.18 2004/11/30 23:51:06 cheshire
74 Remove double semicolons
75
76 Revision 1.17 2004/11/30 22:37:01 cheshire
77 Update copyright dates and add "Mode: C; tab-width: 4" headers
78
79 Revision 1.16 2004/11/25 02:02:28 ksekar
80 Fixed verbose log message argument
81
82 Revision 1.15 2004/11/19 02:35:02 ksekar
83 <rdar://problem/3886317> Wide Area Security: Add LLQ-ID to events
84
85 Revision 1.14 2004/11/17 06:17:58 cheshire
86 Update comments to show correct SRV names: _dns-update._udp.<zone>. and _dns-llq._udp.<zone>.
87
88 Revision 1.13 2004/11/13 02:22:36 ksekar
89 <rdar://problem/3878201> Refresh Acks from daemon malformatted
90
91 Revision 1.12 2004/11/12 01:05:01 ksekar
92 <rdar://problem/3876757> dnsextd: daemon registers the SRV same record
93 twice at startup
94
95 Revision 1.11 2004/11/12 01:03:31 ksekar
96 <rdar://problem/3876776> dnsextd: KnownAnswers (CacheRecords) leaked
97
98 Revision 1.10 2004/11/12 00:35:28 ksekar
99 <rdar://problem/3876705> dnsextd: uninitialized pointer can cause crash
100
101 Revision 1.9 2004/11/10 20:38:17 ksekar
102 <rdar://problem/3874168> dnsextd: allow a "fudge" in LLQ lease echo
103
104 Revision 1.8 2004/11/01 17:48:14 cheshire
105 Changed SOA serial number back to signed. RFC 1035 may describe it as "unsigned", but
106 it's wrong. The SOA serial is a modular counter, as explained in "DNS & BIND", page
107 137. Since C doesn't have a modular type, we used signed, C's closest approximation.
108
109 Revision 1.7 2004/10/30 00:06:58 ksekar
110 <rdar://problem/3722535> Support Long Lived Queries in DNS Extension daemon
111
112 Revision 1.6 2004/09/17 01:08:54 cheshire
113 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
114 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
115 declared in that file are ONLY appropriate to single-address-space embedded applications.
116 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
117
118 Revision 1.5 2004/09/16 00:50:54 cheshire
119 Don't use MSG_WAITALL -- it returns "Invalid argument" on some Linux versions
120
121 Revision 1.4 2004/09/14 23:27:48 cheshire
122 Fix compile errors
123
124 Revision 1.3 2004/09/02 01:39:40 cheshire
125 For better readability, follow consistent convention that QR bit comes first, followed by OP bits
126
127 Revision 1.2 2004/08/24 23:27:57 cheshire
128 Fixes for Linux compatibility:
129 Don't use strings.h
130 Don't assume SIGINFO
131 Don't try to set servaddr.sin_len on platforms that don't have sa_len
132
133 Revision 1.1 2004/08/11 00:43:26 ksekar
134 <rdar://problem/3722542>: DNS Extension daemon for DNS Update Lease
135
136 */
137
138 #include "../mDNSCore/mDNSEmbeddedAPI.h"
139 #include "../mDNSCore/DNSCommon.h"
140 #include "../mDNSCore/mDNS.c"
141 //!!!KRS we #include mDNS.c for the various constants defined there - we should move these to DNSCommon.h
142
143 #include <signal.h>
144 #include <pthread.h>
145 #include <stdlib.h>
146 #include <unistd.h>
147 #include <sys/types.h>
148 #include <sys/socket.h>
149 #include <netinet/in.h>
150 #include <arpa/inet.h>
151 #include <stdio.h>
152 #include <syslog.h>
153 #include <string.h>
154 #include <sys/time.h>
155 #include <time.h>
156 #include <errno.h>
157
158 // Compatibility workaround
159 #ifndef AF_LOCAL
160 #define AF_LOCAL AF_UNIX
161 #endif
162
163 //
164 // Constants
165 //
166
167 #define LOOPBACK "127.0.0.1"
168 #define NS_PORT 53
169 #define DAEMON_PORT 5352 // default, may be overridden via command line argument
170 #define LISTENQ 128 // tcp connection backlog
171 #define RECV_BUFLEN 9000
172 #define LEASETABLE_INIT_NBUCKETS 256 // initial hashtable size (doubles as table fills)
173 #define LLQ_TABLESIZE 1024 // !!!KRS make this dynamically growable
174 #define EXPIRATION_INTERVAL 300 // check for expired records every 5 minutes
175 #define SRV_TTL 7200 // TTL For _dns-update SRV records
176
177 // LLQ Lease bounds (seconds)
178 #define LLQ_MIN_LEASE (15 * 60)
179 #define LLQ_MAX_LEASE (120 * 60)
180 #define LLQ_LEASE_FUDGE 60
181
182 // LLQ SOA poll interval (microseconds)
183 #define LLQ_MONITOR_ERR_INTERVAL (60 * 1000000)
184 #define LLQ_MONITOR_INTERVAL 250000
185 #ifdef SIGINFO
186 #define INFO_SIGNAL SIGINFO
187 #else
188 #define INFO_SIGNAL SIGUSR1
189 #endif
190
191 #define SAME_INADDR(x,y) (*((mDNSu32 *)&x) == *((mDNSu32 *)&y))
192 #define ZERO_LLQID(x) (!memcmp(x, "\x0\x0\x0\x0\x0\x0\x0\x0", 8))
193
194 //
195 // Data Structures
196 // Structs/fields that must be locked for thread safety are explicitly commented
197 //
198
199 typedef struct
200 {
201 struct sockaddr_in src;
202 size_t len;
203 DNSMessage msg;
204 // Note: extra storage for oversized (TCP) messages goes here
205 } PktMsg;
206
207 // lease table entry
208 typedef struct RRTableElem
209 {
210 struct RRTableElem *next;
211 struct sockaddr_in cli; // client's source address
212 long expire; // expiration time, in seconds since epoch
213 domainname zone; // from zone field of update message
214 domainname name; // name of the record
215 CacheRecord rr; // last field in struct allows for allocation of oversized RRs
216 } RRTableElem;
217
218 typedef enum
219 {
220 RequestReceived = 0,
221 ChallengeSent = 1,
222 Established = 2
223 } LLQState;
224
225 typedef struct AnswerListElem
226 {
227 struct AnswerListElem *next;
228 domainname name;
229 mDNSu16 type;
230 CacheRecord *KnownAnswers; // All valid answers delivered to client
231 CacheRecord *EventList; // New answers (adds/removes) to be sent to client
232 int refcount;
233 } AnswerListElem;
234
235 // llq table entry
236 typedef struct LLQEntry
237 {
238 struct LLQEntry *next;
239 struct sockaddr_in cli; // clien'ts source address
240 domainname qname;
241 mDNSu16 qtype;
242 mDNSu8 id[8];
243 LLQState state;
244 mDNSu32 lease; // original lease, in seconds
245 mDNSs32 expire; // expiration, absolute, in seconds since epoch
246 AnswerListElem *AnswerList;
247 } LLQEntry;
248
249 // daemon-wide information
250 typedef struct
251 {
252 // server variables - read only after initialization (no locking)
253 struct in_addr saddr; // server address
254 domainname zone; // zone being updated
255 int tcpsd; // listening TCP socket
256 int udpsd; // listening UDP socket
257
258 // daemon variables - read only after initialization (no locking)
259 uDNS_AuthInfo *AuthInfo; // linked list of keys for signing deletion updates
260 mDNSIPPort port; // listening port
261
262 // lease table variables (locked via mutex after initialization)
263 RRTableElem **table; // hashtable for records with leases
264 pthread_mutex_t tablelock; // mutex for lease table
265 mDNSs32 nbuckets; // buckets allocated
266 mDNSs32 nelems; // elements in table
267
268 // LLQ table variables
269 LLQEntry *LLQTable[LLQ_TABLESIZE]; // !!!KRS change this and RRTable to use a common data structure
270 AnswerListElem *AnswerTable[LLQ_TABLESIZE];
271 int LLQEventListenSock; // Unix domain socket pair - polling thread writes to ServPollSock, which wakes
272 int LLQServPollSock; // the main thread listening on EventListenSock, indicating that the zone has changed
273 } DaemonInfo;
274
275 // args passed to UDP request handler thread as void*
276 typedef struct
277 {
278 PktMsg pkt;
279 struct sockaddr_in cliaddr;
280 DaemonInfo *d;
281 } UDPRequestArgs;
282
283 // args passed to TCP request handler thread as void*
284 typedef struct
285 {
286 int sd; // socket connected to client
287 struct sockaddr_in cliaddr;
288 DaemonInfo *d;
289 } TCPRequestArgs;
290
291 //
292 // Global Variables
293 //
294
295 // booleans to determine runtime output
296 // read-only after initialization (no mutex protection)
297 static mDNSBool foreground = 0;
298 static mDNSBool verbose = 0;
299
300 // globals set via signal handler (accessed exclusively by main select loop and signal handler)
301 static mDNSBool terminate = 0;
302 static mDNSBool dumptable = 0;
303
304 //
305 // Logging Routines
306 // Log messages are delivered to syslog unless -f option specified
307 //
308
309 // common message logging subroutine
310 mDNSlocal void PrintLog(const char *buffer)
311 {
312 if (foreground)
313 {
314 fprintf(stderr,"%s\n", buffer);
315 fflush(stderr);
316 }
317 else
318 {
319 openlog("dnsextd", LOG_CONS | LOG_PERROR, LOG_DAEMON);
320 syslog(LOG_ERR, "%s", buffer);
321 closelog();
322 }
323 }
324
325 // Verbose Logging (conditional on -v option)
326 mDNSlocal void VLog(const char *format, ...)
327 {
328 char buffer[512];
329 va_list ptr;
330
331 if (!verbose) return;
332 va_start(ptr,format);
333 buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
334 va_end(ptr);
335 PrintLog(buffer);
336 }
337
338 // Unconditional Logging
339 mDNSlocal void Log(const char *format, ...)
340 {
341 char buffer[512];
342 va_list ptr;
343
344 va_start(ptr,format);
345 buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
346 va_end(ptr);
347 PrintLog(buffer);
348 }
349
350 // Error Logging
351 // prints message "dnsextd <function>: <operation> - <error message>"
352 // must be compiled w/ -D_REENTRANT for thread-safe errno usage
353 mDNSlocal void LogErr(const char *fn, const char *operation)
354 {
355 char buf[512];
356 snprintf(buf, sizeof(buf), "%s: %s - %s", fn, operation, strerror(errno));
357 PrintLog(buf);
358 }
359
360 //
361 // Networking Utility Routines
362 //
363
364 // Convert DNS Message Header from Network to Host byte order
365 mDNSlocal void HdrNToH(PktMsg *pkt)
366 {
367 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
368 mDNSu8 *ptr = (mDNSu8 *)&pkt->msg.h.numQuestions;
369 pkt->msg.h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
370 pkt->msg.h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
371 pkt->msg.h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
372 pkt->msg.h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
373 }
374
375 // Convert DNS Message Header from Host to Network byte order
376 mDNSlocal void HdrHToN(PktMsg *pkt)
377 {
378 mDNSu16 numQuestions = pkt->msg.h.numQuestions;
379 mDNSu16 numAnswers = pkt->msg.h.numAnswers;
380 mDNSu16 numAuthorities = pkt->msg.h.numAuthorities;
381 mDNSu16 numAdditionals = pkt->msg.h.numAdditionals;
382 mDNSu8 *ptr = (mDNSu8 *)&pkt->msg.h.numQuestions;
383
384 // Put all the integer values in IETF byte-order (MSB first, LSB second)
385 *ptr++ = (mDNSu8)(numQuestions >> 8);
386 *ptr++ = (mDNSu8)(numQuestions & 0xFF);
387 *ptr++ = (mDNSu8)(numAnswers >> 8);
388 *ptr++ = (mDNSu8)(numAnswers & 0xFF);
389 *ptr++ = (mDNSu8)(numAuthorities >> 8);
390 *ptr++ = (mDNSu8)(numAuthorities & 0xFF);
391 *ptr++ = (mDNSu8)(numAdditionals >> 8);
392 *ptr++ = (mDNSu8)(numAdditionals & 0xFF);
393 }
394
395 // create a socket connected to nameserver
396 // caller terminates connection via close()
397 mDNSlocal int ConnectToServer(DaemonInfo *d)
398 {
399 struct sockaddr_in servaddr;
400 int sd;
401
402 bzero(&servaddr, sizeof(servaddr));
403 if (d->saddr.s_addr) servaddr.sin_addr = d->saddr;
404 else inet_pton(AF_INET, LOOPBACK, &d->saddr); // use loopback if server not explicitly specified
405 servaddr.sin_port = htons(NS_PORT);
406 servaddr.sin_family = AF_INET;
407 #ifndef NOT_HAVE_SA_LEN
408 servaddr.sin_len = sizeof(servaddr);
409 #endif
410 sd = socket(AF_INET, SOCK_STREAM, 0);
411 if (sd < 0) { LogErr("ConnectToServer", "socket"); return -1; }
412 if (connect(sd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { LogErr("ConnectToServer", "connect"); return -1; }
413 return sd;
414 }
415
416 // send an entire block of data over a connected socket, blocking if buffers are full
417 mDNSlocal int MySend(int sd, const void *msg, int len)
418 {
419 int n, nsent = 0;
420
421 while (nsent < len)
422 {
423 n = send(sd, (char *)msg + nsent, len - nsent, 0);
424 if (n < 0) { LogErr("MySend", "send"); return -1; }
425 nsent += n;
426 }
427 return 0;
428 }
429
430 // Transmit a DNS message, prefixed by its length, over TCP, blocking if necessary
431 mDNSlocal int SendTCPMsg(int sd, PktMsg *pkt)
432 {
433 // send the lenth, in network byte order
434 mDNSu16 len = htons((mDNSu16)pkt->len);
435 if (MySend(sd, &len, sizeof(len)) < 0) return -1;
436
437 // send the message
438 return MySend(sd, &pkt->msg, pkt->len);
439 }
440
441 // Receive len bytes, waiting until we have all of them.
442 // Returns number of bytes read (which should always be the number asked for).
443 static int my_recv(const int sd, void *const buf, const int len)
444 {
445 // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions;
446 // use an explicit while() loop instead.
447 // Also, don't try to do '+=' arithmetic on the original "void *" pointer --
448 // arithmetic on "void *" pointers is compiler-dependent.
449 int remaining = len;
450 char *ptr = (char *)buf;
451 while (remaining)
452 {
453 ssize_t num_read = recv(sd, ptr, remaining, 0);
454 if ((num_read == 0) || (num_read < 0) || (num_read > remaining)) return -1;
455 ptr += num_read;
456 remaining -= num_read;
457 }
458 return(len);
459 }
460
461 // Return a DNS Message read off of a TCP socket, or NULL on failure
462 // If storage is non-null, result is placed in that buffer. Otherwise,
463 // returned value is allocated with Malloc, and contains sufficient extra
464 // storage for a Lease OPT RR
465
466 mDNSlocal PktMsg *ReadTCPMsg(int sd, PktMsg *storage)
467 {
468 int nread, allocsize;
469 mDNSu16 msglen = 0;
470 PktMsg *pkt = NULL;
471 unsigned int srclen;
472
473 nread = my_recv(sd, &msglen, sizeof(msglen));
474 if (nread < 0) { LogErr("TCPRequestForkFn", "recv"); goto error; }
475 msglen = ntohs(msglen);
476 if (nread != sizeof(msglen)) { Log("Could not read length field of message"); goto error; }
477
478 if (storage)
479 {
480 if (msglen > sizeof(storage->msg)) { Log("ReadTCPMsg: provided buffer too small."); goto error; }
481 pkt = storage;
482 }
483 else
484 {
485 // buffer extra space to add an OPT RR
486 if (msglen > sizeof(DNSMessage)) allocsize = sizeof(PktMsg) - sizeof(DNSMessage) + msglen;
487 else allocsize = sizeof(PktMsg);
488 pkt = malloc(allocsize);
489 if (!pkt) { LogErr("ReadTCPMsg", "malloc"); goto error; }
490 bzero(pkt, sizeof(*pkt));
491 }
492
493 pkt->len = msglen;
494 srclen = sizeof(pkt->src);
495 if (getpeername(sd, (struct sockaddr *)&pkt->src, &srclen) ||
496 srclen != sizeof(pkt->src)) { LogErr("ReadTCPMsg", "getpeername"); bzero(&pkt->src, sizeof(pkt->src)); }
497 nread = my_recv(sd, &pkt->msg, msglen);
498 if (nread < 0) { LogErr("TCPRequestForkFn", "recv"); goto error; }
499 if (nread != msglen) { Log("Could not read entire message"); goto error; }
500 if (pkt->len < sizeof(DNSMessageHeader))
501 { Log("ReadTCPMsg: Message too short (%d bytes)", pkt->len); goto error; }
502 HdrNToH(pkt);
503 return pkt;
504 //!!!KRS convert to HBO here?
505 error:
506 if (pkt && pkt != storage) free(pkt);
507 return NULL;
508 }
509
510 //
511 // Dynamic Update Utility Routines
512 //
513
514 // Get the lease life of records in a dynamic update
515 // returns -1 on error or if no lease present
516 mDNSlocal mDNSs32 GetPktLease(PktMsg *pkt)
517 {
518 mDNSs32 lease = -1;
519 const mDNSu8 *ptr = NULL, *end = (mDNSu8 *)&pkt->msg + pkt->len;
520 LargeCacheRecord lcr;
521 int i;
522
523 HdrNToH(pkt);
524 ptr = LocateAdditionals(&pkt->msg, end);
525 if (ptr)
526 for (i = 0; i < pkt->msg.h.numAdditionals; i++)
527 {
528 ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr);
529 if (!ptr) { Log("Unable to read additional record"); break; }
530 if (lcr.r.resrec.rrtype == kDNSType_OPT)
531 {
532 if (lcr.r.resrec.rdlength < LEASE_OPT_RDLEN) continue;
533 if (lcr.r.resrec.rdata->u.opt.opt != kDNSOpt_Lease) continue;
534 lease = (mDNSs32)lcr.r.resrec.rdata->u.opt.OptData.lease;
535 break;
536 }
537 }
538
539 HdrHToN(pkt);
540 return lease;
541 }
542
543 // check if a request and server response complete a successful dynamic update
544 mDNSlocal mDNSBool SuccessfulUpdateTransaction(PktMsg *request, PktMsg *reply)
545 {
546 char buf[32];
547 char *vlogmsg = NULL;
548
549 // check messages
550 if (!request || !reply) { vlogmsg = "NULL message"; goto failure; }
551 if (request->len < sizeof(DNSMessageHeader) || reply->len < sizeof(DNSMessageHeader)) { vlogmsg = "Malformatted message"; goto failure; }
552
553 // check request operation
554 if ((request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask))
555 { vlogmsg = "Request opcode not an update"; goto failure; }
556
557 // check result
558 if ((reply->msg.h.flags.b[1] & kDNSFlag1_RC)) { vlogmsg = "Reply contains non-zero rcode"; goto failure; }
559 if ((reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (kDNSFlag0_OP_Update | kDNSFlag0_QR_Response))
560 { vlogmsg = "Reply opcode not an update response"; goto failure; }
561
562 VLog("Successful update from %s", inet_ntop(AF_INET, &request->src.sin_addr, buf, 32));
563 return mDNStrue;
564
565 failure:
566 VLog("Request %s: %s", inet_ntop(AF_INET, &request->src.sin_addr, buf, 32), vlogmsg);
567 return mDNSfalse;
568 }
569
570 // Allocate an appropriately sized CacheRecord and copy data from original.
571 // Name pointer in CacheRecord object is set to point to the name specified
572 //
573 mDNSlocal CacheRecord *CopyCacheRecord(const CacheRecord *orig, domainname *name)
574 {
575 CacheRecord *cr;
576 size_t size = sizeof(*cr);
577 if (orig->resrec.rdlength > InlineCacheRDSize) size += orig->resrec.rdlength - InlineCacheRDSize;
578 cr = malloc(size);
579 if (!cr) { LogErr("CopyCacheRecord", "malloc"); return NULL; }
580 memcpy(cr, orig, size);
581 cr->resrec.rdata = (RData*)&cr->rdatastorage;
582 cr->resrec.name = name;
583
584 return cr;
585 }
586
587
588 //
589 // Lease Hashtable Utility Routines
590 //
591
592 // double hash table size
593 // caller must lock table prior to invocation
594 mDNSlocal void RehashTable(DaemonInfo *d)
595 {
596 RRTableElem *ptr, *tmp, **new;
597 int i, bucket, newnbuckets = d->nbuckets * 2;
598
599 VLog("Rehashing lease table (new size %d buckets)", newnbuckets);
600 new = malloc(sizeof(RRTableElem *) * newnbuckets);
601 if (!new) { LogErr("RehashTable", "malloc"); return; }
602 bzero(new, newnbuckets * sizeof(RRTableElem *));
603
604 for (i = 0; i < d->nbuckets; i++)
605 {
606 ptr = d->table[i];
607 while (ptr)
608 {
609 bucket = ptr->rr.resrec.namehash % newnbuckets;
610 tmp = ptr;
611 ptr = ptr->next;
612 tmp->next = new[bucket];
613 new[bucket] = tmp;
614 }
615 }
616 d->nbuckets = newnbuckets;
617 free(d->table);
618 d->table = new;
619 }
620
621 // print entire contents of hashtable, invoked via SIGINFO
622 mDNSlocal void PrintLeaseTable(DaemonInfo *d)
623 {
624 int i;
625 RRTableElem *ptr;
626 char rrbuf[80], addrbuf[16];
627 struct timeval now;
628 int hr, min, sec;
629
630 if (gettimeofday(&now, NULL)) { LogErr("PrintTable", "gettimeofday"); return; }
631 if (pthread_mutex_lock(&d->tablelock)) { LogErr("PrintTable", "pthread_mutex_lock"); return; }
632
633 Log("Dumping Lease Table Contents (table contains %d resource records)", d->nelems);
634 for (i = 0; i < d->nbuckets; i++)
635 {
636 for (ptr = d->table[i]; ptr; ptr = ptr->next)
637 {
638 hr = ((ptr->expire - now.tv_sec) / 60) / 60;
639 min = ((ptr->expire - now.tv_sec) / 60) % 60;
640 sec = (ptr->expire - now.tv_sec) % 60;
641 Log("Update from %s, Expires in %d:%d:%d\n\t%s", inet_ntop(AF_INET, &ptr->cli.sin_addr, addrbuf, 16), hr, min, sec,
642 GetRRDisplayString_rdb(&ptr->rr.resrec, &ptr->rr.resrec.rdata->u, rrbuf));
643 }
644 }
645 pthread_mutex_unlock(&d->tablelock);
646 }
647
648 //
649 // Startup SRV Registration Routines
650 // Register _dns-update._udp/_tcp.<zone> SRV records indicating the port on which
651 // the daemon accepts requests
652 //
653
654 // delete all RRS of a given name/type
655 mDNSlocal mDNSu8 *putRRSetDeletion(DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit, ResourceRecord *rr)
656 {
657 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
658 if (!ptr || ptr + 10 >= limit) return NULL; // out of space
659 ptr[0] = (mDNSu8)(rr->rrtype >> 8);
660 ptr[1] = (mDNSu8)(rr->rrtype & 0xFF);
661 ptr[2] = (mDNSu8)((mDNSu16)kDNSQClass_ANY >> 8);
662 ptr[3] = (mDNSu8)((mDNSu16)kDNSQClass_ANY & 0xFF);
663 bzero(ptr+4, sizeof(rr->rroriginalttl) + sizeof(rr->rdlength)); // zero ttl/rdata
664 msg->h.mDNS_numUpdates++;
665 return ptr + 10;
666 }
667
668 mDNSlocal mDNSu8 *PutUpdateSRV(DaemonInfo *d, PktMsg *pkt, mDNSu8 *ptr, char *regtype, mDNSBool registration)
669 {
670 AuthRecord rr;
671 char hostname[1024], buf[80];
672 mDNSu8 *end = (mDNSu8 *)&pkt->msg + sizeof(DNSMessage);
673
674 mDNS_SetupResourceRecord(&rr, NULL, 0, kDNSType_SRV, SRV_TTL, kDNSRecordTypeUnique, NULL, NULL);
675 rr.resrec.rrclass = kDNSClass_IN;
676 rr.resrec.rdata->u.srv.priority = 0;
677 rr.resrec.rdata->u.srv.weight = 0;
678 rr.resrec.rdata->u.srv.port.NotAnInteger = d->port.NotAnInteger;
679 if (!gethostname(hostname, 1024) < 0 || MakeDomainNameFromDNSNameString(&rr.resrec.rdata->u.srv.target, hostname))
680 rr.resrec.rdata->u.srv.target.c[0] = '\0';
681
682 MakeDomainNameFromDNSNameString(rr.resrec.name, regtype);
683 AppendDomainName(rr.resrec.name, &d->zone);
684 VLog("%s %s", registration ? "Registering SRV record" : "Deleting existing RRSet",
685 GetRRDisplayString_rdb(&rr.resrec, &rr.resrec.rdata->u, buf));
686 if (registration) ptr = PutResourceRecord(&pkt->msg, ptr, &pkt->msg.h.mDNS_numUpdates, &rr.resrec);
687 else ptr = putRRSetDeletion(&pkt->msg, ptr, end, &rr.resrec);
688 return ptr;
689 }
690
691
692 // perform dynamic update.
693 // specify deletion by passing false for the register parameter, otherwise register the records.
694 mDNSlocal int UpdateSRV(DaemonInfo *d, mDNSBool registration)
695 {
696 int sd = -1;
697 mDNSOpaque16 id;
698 PktMsg pkt;
699 mDNSu8 *ptr = pkt.msg.data;
700 mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage);
701 mDNSu16 nAdditHBO; // num additionas, in host byte order, required by message digest routine
702 PktMsg *reply = NULL;
703
704 int result = -1;
705
706 // Initialize message
707 id.NotAnInteger = 0;
708 InitializeDNSMessage(&pkt.msg.h, id, UpdateReqFlags);
709 pkt.src.sin_addr.s_addr = htonl(INADDR_ANY); // address field set solely for verbose logging in subroutines
710 pkt.src.sin_family = AF_INET;
711
712 // format message body
713 ptr = putZone(&pkt.msg, ptr, end, &d->zone, mDNSOpaque16fromIntVal(kDNSClass_IN));
714 if (!ptr) goto end;
715
716 ptr = PutUpdateSRV(d, &pkt, ptr, "_dns-update._udp.", registration); if (!ptr) goto end;
717 ptr = PutUpdateSRV(d, &pkt, ptr, "_dns-update._tcp.", registration); if (!ptr) goto end;
718 ptr = PutUpdateSRV(d, &pkt, ptr, "_dns-llq._udp.", registration); if (!ptr) goto end;
719
720 nAdditHBO = pkt.msg.h.numAdditionals;
721 HdrHToN(&pkt);
722 if (d->AuthInfo)
723 {
724 ptr = DNSDigest_SignMessage(&pkt.msg, &ptr, &nAdditHBO, d->AuthInfo);
725 if (!ptr) goto end;
726 }
727 pkt.len = ptr - (mDNSu8 *)&pkt.msg;
728
729 // send message, receive reply
730 sd = ConnectToServer(d);
731 if (sd < 0) { Log("UpdateSRV: ConnectToServer failed"); goto end; }
732 if (SendTCPMsg(sd, &pkt)) { Log("UpdateSRV: SendTCPMsg failed"); }
733 reply = ReadTCPMsg(sd, NULL);
734 if (!SuccessfulUpdateTransaction(&pkt, reply))
735 Log("SRV record registration failed with rcode %d", reply->msg.h.flags.b[1] & kDNSFlag1_RC);
736 else result = 0;
737
738 end:
739 if (!ptr) { Log("UpdateSRV: Error constructing lease expiration update"); }
740 if (sd >= 0) close(sd);
741 if (reply) free(reply);
742 return result;
743 }
744
745 // wrapper routines/macros
746 #define ClearUpdateSRV(d) UpdateSRV(d, 0)
747
748 // clear any existing records prior to registration
749 mDNSlocal int SetUpdateSRV(DaemonInfo *d)
750 {
751 int err;
752
753 err = ClearUpdateSRV(d); // clear any existing record
754 if (!err) err = UpdateSRV(d, 1);
755 return err;
756 }
757
758 //
759 // Argument Parsing and Configuration
760 //
761
762 // read authentication information for a zone from command line argument
763 // global optind corresponds to keyname argument on entry
764 mDNSlocal int ReadAuthKey(int argc, char *argv[], DaemonInfo *d)
765 {
766 uDNS_AuthInfo *auth = NULL;
767 unsigned char keybuf[512];
768 mDNSs32 keylen;
769
770 auth = malloc(sizeof(*auth));
771 if (!auth) { perror("ReadAuthKey, malloc"); goto error; }
772 auth->next = NULL;
773 if (argc < optind + 1) return -1; // keyname + secret
774 if (!MakeDomainNameFromDNSNameString(&auth->keyname, optarg))
775 { fprintf(stderr, "Bad key name %s", optarg); goto error; }
776 keylen = DNSDigest_Base64ToBin(argv[optind++], keybuf, 512);
777 if (keylen < 0)
778 { fprintf(stderr, "Bad shared secret %s (must be base-64 encoded string)", argv[optind-1]); goto error; }
779 DNSDigest_ConstructHMACKey(auth, keybuf, (mDNSu32)keylen);
780 d->AuthInfo = auth;
781 return 0;
782
783 error:
784 if (auth) free(auth);
785 return -1;
786 }
787
788 mDNSlocal int SetPort(DaemonInfo *d, char *PortAsString)
789 {
790 long l;
791
792 l = strtol(PortAsString, NULL, 10); // convert string to long
793 if ((!l && errno == EINVAL) || l > 65535) return -1; // error check conversion
794 d->port.NotAnInteger = htons((mDNSu16)l); // set to network byte order
795 return 0;
796 }
797
798 mDNSlocal void PrintUsage(void)
799 {
800 fprintf(stderr, "Usage: dnsextd -z <zone> [-vf] [ -s server ] [-k keyname secret] ...\n"
801 "Use \"dnsextd -h\" for help\n");
802 }
803
804 mDNSlocal void PrintHelp(void)
805 {
806 fprintf(stderr, "\n\n");
807 PrintUsage();
808
809 fprintf(stderr,
810 "dnsextd is a daemon that implements DNS extensions supporting Dynamic DNS Update Leases\n"
811 "and Long Lived Queries, used in Wide-Area DNS Service Discovery, on behalf of name servers\n"
812 "that do not natively support these extensions. (See dns-sd.org for more info on DNS Service\n"
813 "Discovery, Update Leases, and Long Lived Queries.)\n\n"
814
815 "dnsextd requires one argument,the zone, which is the domain for which Update Leases\n"
816 "and Long Lived Queries are to be administered. dnsextd communicates directly with the\n"
817 "primary master server for this zone.\n\n"
818
819 "The options are as follows:\n\n"
820
821 "-f Run daemon in foreground.\n\n"
822
823 "-h Print help.\n\n"
824
825 "-k Specify TSIG authentication key for dynamic updates from daemon to name server.\n"
826 " -k option is followed by the name of the key, and the shared secret as a base-64\n"
827 " encoded string. This key/secret are used by the daemon to delete resource records\n"
828 " from the server when leases expire. Clients are responsible for signing their\n"
829 " update requests.\n\n"
830
831 "-s Specify address (IPv4 address in dotted-decimal notation) of the Primary Master\n"
832 " name server. Defaults to loopback (127.0.0.1), i.e. daemon and name server\n"
833 " running on the same machine.\n\n"
834
835 "-v Verbose output.\n\n"
836 );
837 }
838
839 // Note: ProcessArgs called before process is daemonized, and therefore must open no descriptors
840 // returns 0 (success) if program is to continue execution
841 // output control arguments (-f, -v) do not affect this routine
842 mDNSlocal int ProcessArgs(int argc, char *argv[], DaemonInfo *d)
843 {
844 int opt;
845
846 if (argc < 2) goto arg_error;
847
848 d->port.NotAnInteger = htons(DAEMON_PORT); // default, may be overriden by command option
849 while ((opt = getopt(argc, argv, "z:p:hfvs:k:")) != -1)
850 {
851 switch(opt)
852 {
853 case 'p': if (SetPort(d, optarg) < 0) goto arg_error;
854 break;
855
856 case 'h': PrintHelp(); return -1;
857 case 'f': foreground = 1; break;
858 case 'v': verbose = 1; break;
859 case 's': if (!inet_pton(AF_INET, optarg, &d->saddr)) goto arg_error;
860 break;
861 case 'k': if (ReadAuthKey(argc, argv, d) < 0) goto arg_error;
862 break;
863 case 'z': if (!MakeDomainNameFromDNSNameString(&d->zone, optarg))
864 {
865 fprintf(stderr, "Bad zone %s", optarg);
866 goto arg_error;
867 }
868 break;
869 default: goto arg_error;
870 }
871 }
872
873 if (!d->zone.c[0]) goto arg_error; // zone is the only required argument
874 if (d->AuthInfo) AssignDomainName(&d->AuthInfo->zone, &d->zone); // if we have a shared secret, use it for the entire zone
875 return 0;
876
877 arg_error:
878 PrintUsage();
879 return -1;
880 }
881
882
883 //
884 // Initialization Routines
885 //
886
887 // Allocate memory, initialize locks and bookkeeping variables
888 mDNSlocal int InitLeaseTable(DaemonInfo *d)
889 {
890 if (pthread_mutex_init(&d->tablelock, NULL)) { LogErr("InitLeaseTable", "pthread_mutex_init"); return -1; }
891 d->nbuckets = LEASETABLE_INIT_NBUCKETS;
892 d->nelems = 0;
893 d->table = malloc(sizeof(RRTableElem *) * LEASETABLE_INIT_NBUCKETS);
894 if (!d->table) { LogErr("InitLeaseTable", "malloc"); return -1; }
895 bzero(d->table, sizeof(RRTableElem *) * LEASETABLE_INIT_NBUCKETS);
896 return 0;
897 }
898 mDNSlocal int SetupSockets(DaemonInfo *daemon)
899 {
900 struct sockaddr_in daddr;
901 int sockpair[2];
902
903 // set up sockets on which we receive requests
904 bzero(&daddr, sizeof(daddr));
905 daddr.sin_family = AF_INET;
906 daddr.sin_addr.s_addr = htonl(INADDR_ANY);
907
908 if (daemon->port.NotAnInteger) daddr.sin_port = daemon->port.NotAnInteger;
909 else daddr.sin_port = htons(DAEMON_PORT);
910
911 daemon->tcpsd = socket(AF_INET, SOCK_STREAM, 0);
912 if (!daemon->tcpsd) { LogErr("SetupSockets", "socket"); return -1; }
913 if (bind(daemon->tcpsd, (struct sockaddr *)&daddr, sizeof(daddr)) < 0) { LogErr("SetupSockets", "bind"); return -1; }
914 if (listen(daemon->tcpsd, LISTENQ) < 0) { LogErr("SetupSockets", "listen"); return -1; }
915
916 daemon->udpsd = socket(AF_INET, SOCK_DGRAM, 0);
917 if (!daemon->udpsd) { LogErr("SetupSockets", "socket"); return -1; }
918 if (bind(daemon->udpsd, (struct sockaddr *)&daddr, sizeof(daddr)) < 0) { LogErr("SetupSockets", "bind"); return -1; }
919
920 // set up Unix domain socket pair for LLQ polling thread to signal main thread that a change to the zone occurred
921 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockpair) < 0) { LogErr("SetupSockets", "socketpair"); return -1; }
922 daemon->LLQEventListenSock = sockpair[0];
923 daemon->LLQServPollSock = sockpair[1];
924 return 0;
925 }
926
927 //
928 // periodic table updates
929 //
930
931 // Delete a resource record from the nameserver via a dynamic update
932 mDNSlocal void DeleteRecord(DaemonInfo *d, CacheRecord *rr, domainname *zone)
933 {
934 int sd = -1;
935 mDNSOpaque16 id;
936 PktMsg pkt;
937 mDNSu8 *ptr = pkt.msg.data;
938 mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage);
939 mDNSu16 nAdditHBO; // num additionas, in host byte order, required by message digest routine
940 char buf[80];
941 PktMsg *reply = NULL;
942
943 VLog("Expiring record %s", GetRRDisplayString_rdb(&rr->resrec, &rr->resrec.rdata->u, buf));
944 sd = ConnectToServer(d);
945 if (sd < 0) { Log("DeleteRecord: ConnectToServer failed"); goto end; }
946
947 id.NotAnInteger = 0;
948 InitializeDNSMessage(&pkt.msg.h, id, UpdateReqFlags);
949
950 ptr = putZone(&pkt.msg, ptr, end, zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
951 if (!ptr) goto end;
952 ptr = putDeletionRecord(&pkt.msg, ptr, &rr->resrec);
953 if (!ptr) goto end;
954
955 nAdditHBO = pkt.msg.h.numAdditionals;
956 HdrHToN(&pkt);
957
958 if (d->AuthInfo)
959 {
960 ptr = DNSDigest_SignMessage(&pkt.msg, &ptr, &nAdditHBO, d->AuthInfo);
961 if (!ptr) goto end;
962 }
963
964 pkt.len = ptr - (mDNSu8 *)&pkt.msg;
965 pkt.src.sin_addr.s_addr = htonl(INADDR_ANY); // address field set solely for verbose logging in subroutines
966 pkt.src.sin_family = AF_INET;
967 if (SendTCPMsg(sd, &pkt)) { Log("DeleteRecord: SendTCPMsg failed"); }
968 reply = ReadTCPMsg(sd, NULL);
969 if (!SuccessfulUpdateTransaction(&pkt, reply))
970 Log("Expiration update failed with rcode %d", reply->msg.h.flags.b[1] & kDNSFlag1_RC);
971
972 end:
973 if (!ptr) { Log("DeleteRecord: Error constructing lease expiration update"); }
974 if (sd >= 0) close(sd);
975 if (reply) free(reply);
976 }
977
978 // iterate over table, deleting expired records
979 mDNSlocal void DeleteExpiredRecords(DaemonInfo *d)
980 {
981 int i;
982 RRTableElem *ptr, *prev, *fptr;
983 struct timeval now;
984
985 if (gettimeofday(&now, NULL)) { LogErr("DeleteExpiredRecords ", "gettimeofday"); return; }
986 if (pthread_mutex_lock(&d->tablelock)) { LogErr("DeleteExpiredRecords", "pthread_mutex_lock"); return; }
987 for (i = 0; i < d->nbuckets; i++)
988 {
989 ptr = d->table[i];
990 prev = NULL;
991 while (ptr)
992 {
993 if (ptr->expire - now.tv_sec < 0)
994 {
995 // delete record from server
996 DeleteRecord(d, &ptr->rr, &ptr->zone);
997 if (prev) prev->next = ptr->next;
998 else d->table[i] = ptr->next;
999 fptr = ptr;
1000 ptr = ptr->next;
1001 free(fptr);
1002 d->nelems--;
1003 }
1004 else
1005 {
1006 prev = ptr;
1007 ptr = ptr->next;
1008 }
1009 }
1010 }
1011 pthread_mutex_unlock(&d->tablelock);
1012 }
1013
1014 //
1015 // main update request handling
1016 //
1017
1018 // Add, delete, or refresh records in table based on contents of a successfully completed dynamic update
1019 mDNSlocal void UpdateLeaseTable(PktMsg *pkt, DaemonInfo *d, mDNSs32 lease)
1020 {
1021 RRTableElem **rptr, *tmp;
1022 int i, allocsize, bucket;
1023 LargeCacheRecord lcr;
1024 ResourceRecord *rr = &lcr.r.resrec;
1025 const mDNSu8 *ptr, *end;
1026 struct timeval time;
1027 DNSQuestion zone;
1028 char buf[80];
1029
1030 if (pthread_mutex_lock(&d->tablelock)) { LogErr("UpdateLeaseTable", "pthread_mutex_lock"); return; }
1031 HdrNToH(pkt);
1032 ptr = pkt->msg.data;
1033 end = (mDNSu8 *)&pkt->msg + pkt->len;
1034 ptr = getQuestion(&pkt->msg, ptr, end, 0, &zone);
1035 if (!ptr) { Log("UpdateLeaseTable: cannot read zone"); goto cleanup; }
1036 ptr = LocateAuthorities(&pkt->msg, end);
1037 if (!ptr) { Log("UpdateLeaseTable: Format error"); goto cleanup; }
1038
1039 for (i = 0; i < pkt->msg.h.mDNS_numUpdates; i++)
1040 {
1041 mDNSBool DeleteAllRRSets = mDNSfalse, DeleteOneRRSet = mDNSfalse, DeleteOneRR = mDNSfalse;
1042
1043 ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
1044 if (!ptr) { Log("UpdateLeaseTable: GetLargeResourceRecord returned NULL"); goto cleanup; }
1045 bucket = rr->namehash % d->nbuckets;
1046 rptr = &d->table[bucket];
1047
1048 // handle deletions
1049 if (rr->rrtype == kDNSQType_ANY && !rr->rroriginalttl && rr->rrclass == kDNSQClass_ANY && !rr->rdlength)
1050 DeleteAllRRSets = mDNStrue; // delete all rrsets for a name
1051 else if (!rr->rroriginalttl && rr->rrclass == kDNSQClass_ANY && !rr->rdlength)
1052 DeleteOneRRSet = mDNStrue;
1053 else if (!rr->rroriginalttl && rr->rrclass == kDNSClass_NONE)
1054 DeleteOneRR = mDNStrue;
1055
1056 if (DeleteAllRRSets || DeleteOneRRSet || DeleteOneRR)
1057 {
1058 while (*rptr)
1059 {
1060 if (SameDomainName((*rptr)->rr.resrec.name, rr->name) &&
1061 (DeleteAllRRSets ||
1062 (DeleteOneRRSet && (*rptr)->rr.resrec.rrtype == rr->rrtype) ||
1063 (DeleteOneRR && SameResourceRecord(&(*rptr)->rr.resrec, rr))))
1064 {
1065 tmp = *rptr;
1066 VLog("Received deletion update for %s", GetRRDisplayString_rdb(&tmp->rr.resrec, &tmp->rr.resrec.rdata->u, buf));
1067 *rptr = (*rptr)->next;
1068 free(tmp);
1069 d->nelems--;
1070 }
1071 else rptr = &(*rptr)->next;
1072 }
1073 }
1074 else if (lease > 0)
1075 {
1076 // see if add or refresh
1077 while (*rptr && !SameResourceRecord(&(*rptr)->rr.resrec, rr)) rptr = &(*rptr)->next;
1078 if (*rptr)
1079 {
1080 // refresh
1081 if (gettimeofday(&time, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; }
1082 (*rptr)->expire = time.tv_sec + (unsigned)lease;
1083 VLog("Refreshing lease for %s", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf));
1084 }
1085 else
1086 {
1087 // New record - add to table
1088 if (d->nelems > d->nbuckets)
1089 {
1090 RehashTable(d);
1091 bucket = rr->namehash % d->nbuckets;
1092 rptr = &d->table[bucket];
1093 }
1094 if (gettimeofday(&time, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; }
1095 allocsize = sizeof(RRTableElem);
1096 if (rr->rdlength > InlineCacheRDSize) allocsize += (rr->rdlength - InlineCacheRDSize);
1097 tmp = malloc(allocsize);
1098 if (!tmp) { LogErr("UpdateLeaseTable", "malloc"); goto cleanup; }
1099 memcpy(&tmp->rr, &lcr.r, sizeof(CacheRecord) + rr->rdlength - InlineCacheRDSize);
1100 tmp->rr.resrec.rdata = (RData *)&tmp->rr.rdatastorage;
1101 AssignDomainName(&tmp->name, rr->name);
1102 tmp->rr.resrec.name = &tmp->name;
1103 tmp->expire = time.tv_sec + (unsigned)lease;
1104 tmp->cli.sin_addr = pkt->src.sin_addr;
1105 AssignDomainName(&tmp->zone, &zone.qname);
1106 tmp->next = d->table[bucket];
1107 d->table[bucket] = tmp;
1108 d->nelems++;
1109 VLog("Adding update for %s to lease table", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf));
1110 }
1111 }
1112 }
1113
1114 cleanup:
1115 pthread_mutex_unlock(&d->tablelock);
1116 HdrHToN(pkt);
1117 }
1118
1119 // Given a successful reply from a server, create a new reply that contains lease information
1120 // Replies are currently not signed !!!KRS change this
1121 mDNSlocal PktMsg *FormatLeaseReply(DaemonInfo *d, PktMsg *orig, mDNSu32 lease)
1122 {
1123 PktMsg *reply;
1124 mDNSu8 *ptr, *end;
1125 mDNSOpaque16 flags;
1126
1127 (void)d; //unused
1128 reply = malloc(sizeof(*reply));
1129 if (!reply) { LogErr("FormatLeaseReply", "malloc"); return NULL; }
1130 flags.b[0] = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update;
1131 flags.b[1] = 0;
1132
1133 InitializeDNSMessage(&reply->msg.h, orig->msg.h.id, flags);
1134 reply->src.sin_addr.s_addr = htonl(INADDR_ANY); // unused except for log messages
1135 reply->src.sin_family = AF_INET;
1136 ptr = reply->msg.data;
1137 end = (mDNSu8 *)&reply->msg + sizeof(DNSMessage);
1138 ptr = putUpdateLease(&reply->msg, ptr, lease);
1139 if (!ptr) { Log("FormatLeaseReply: putUpdateLease failed"); free(reply); return NULL; }
1140 reply->len = ptr - (mDNSu8 *)&reply->msg;
1141 return reply;
1142 }
1143
1144 // pkt is thread-local, not requiring locking
1145 mDNSlocal PktMsg *HandleRequest(PktMsg *pkt, DaemonInfo *d)
1146 {
1147 int sd = -1;
1148 PktMsg *reply = NULL, *LeaseReply;
1149 mDNSs32 lease;
1150 char buf[32];
1151
1152 // send msg to server, read reply
1153 sd = ConnectToServer(d);
1154 if (sd < 0)
1155 { Log("Discarding request from %s due to connection errors", inet_ntop(AF_INET, &pkt->src.sin_addr, buf, 32)); goto cleanup; }
1156 if (SendTCPMsg(sd, pkt) < 0)
1157 { Log("Couldn't relay message from %s to server. Discarding.", inet_ntop(AF_INET, &pkt->src.sin_addr, buf, 32)); goto cleanup; }
1158 reply = ReadTCPMsg(sd, NULL);
1159
1160 // process reply
1161 if (!SuccessfulUpdateTransaction(pkt, reply))
1162 { VLog("Message from %s not a successful update.", inet_ntop(AF_INET, &pkt->src.sin_addr, buf, 32)); goto cleanup; }
1163 lease = GetPktLease(pkt);
1164 UpdateLeaseTable(pkt, d, lease);
1165 if (lease > 0)
1166 {
1167 LeaseReply = FormatLeaseReply(d, reply, lease);
1168 if (!LeaseReply) Log("HandleRequest - unable to format lease reply");
1169 free(reply);
1170 reply = LeaseReply;
1171 }
1172 cleanup:
1173 if (sd >= 0) close(sd);
1174 return reply;
1175 }
1176
1177
1178 //
1179 // LLQ Support Routines
1180 //
1181
1182 // Set fields of an LLQ Opt Resource Record
1183 mDNSlocal void FormatLLQOpt(AuthRecord *opt, int opcode, mDNSu8 *id, mDNSs32 lease)
1184 {
1185 bzero(opt, sizeof(*opt));
1186 mDNS_SetupResourceRecord(opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
1187 opt->resrec.rdlength = LLQ_OPT_RDLEN;
1188 opt->resrec.rdestimate = LLQ_OPT_RDLEN;
1189 opt->resrec.rdata->u.opt.opt = kDNSOpt_LLQ;
1190 opt->resrec.rdata->u.opt.optlen = sizeof(LLQOptData);
1191 opt->resrec.rdata->u.opt.OptData.llq.vers = kLLQ_Vers;
1192 opt->resrec.rdata->u.opt.OptData.llq.llqOp = opcode;
1193 opt->resrec.rdata->u.opt.OptData.llq.err = LLQErr_NoError;
1194 memcpy(opt->resrec.rdata->u.opt.OptData.llq.id, id, 8);
1195 opt->resrec.rdata->u.opt.OptData.llq.lease = lease;
1196 }
1197
1198 // Calculate effective remaining lease of an LLQ
1199 mDNSlocal mDNSu32 LLQLease(LLQEntry *e)
1200 {
1201 struct timeval t;
1202
1203 gettimeofday(&t, NULL);
1204 if (e->expire < t.tv_sec) return 0;
1205 else return e->expire - t.tv_sec;
1206 }
1207
1208 mDNSlocal void DeleteLLQ(DaemonInfo *d, LLQEntry *e)
1209 {
1210 int bucket = DomainNameHashValue(&e->qname) % LLQ_TABLESIZE;
1211 LLQEntry **ptr = &d->LLQTable[bucket];
1212 AnswerListElem *a = e->AnswerList;
1213 char addr[32];
1214
1215 inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
1216 VLog("Deleting LLQ table entry for %##s client %s", e->qname.c, addr);
1217
1218 // free shared answer structure if ref count drops to zero
1219 if (a && !(--a->refcount))
1220 {
1221 CacheRecord *cr = a->KnownAnswers, *tmp;
1222 AnswerListElem **tbl = &d->AnswerTable[bucket];
1223
1224 while (cr)
1225 {
1226 tmp = cr;
1227 cr = cr->next;
1228 free(tmp);
1229 }
1230
1231 while (*tbl && *tbl != a) tbl = &(*tbl)->next;
1232 if (*tbl) { *tbl = (*tbl)->next; free(a); }
1233 else Log("Error: DeleteLLQ - AnswerList not found in table");
1234 }
1235
1236 // remove LLQ from table, free memory
1237 while(*ptr && *ptr != e) ptr = &(*ptr)->next;
1238 if (!*ptr) { Log("Error: DeleteLLQ - LLQ not in table"); return; }
1239 *ptr = (*ptr)->next;
1240 free(e);
1241 }
1242
1243 mDNSlocal int SendLLQ(DaemonInfo *d, PktMsg *pkt, struct sockaddr_in dst)
1244 {
1245 char addr[32];
1246 int err = -1;
1247
1248 HdrHToN(pkt);
1249 if (sendto(d->udpsd, &pkt->msg, pkt->len, 0, (struct sockaddr *)&dst, sizeof(dst)) != (int)pkt->len)
1250 {
1251 LogErr("DaemonInfo", "sendto");
1252 Log("Could not send response to client %s", inet_ntop(AF_INET, &dst.sin_addr, addr, 32));
1253 }
1254 else err = 0;
1255 HdrNToH(pkt);
1256 return err;
1257 }
1258
1259 // if non-negative, sd is a TCP socket connected to the nameserver
1260 // otherwise, this routine creates and closes its own socket
1261 mDNSlocal CacheRecord *AnswerQuestion(DaemonInfo *d, AnswerListElem *e, int sd)
1262 {
1263 PktMsg q;
1264 int i;
1265 const mDNSu8 *ansptr;
1266 mDNSu8 *end = q.msg.data;
1267 mDNSOpaque16 id, flags = QueryFlags;
1268 PktMsg *reply = NULL;
1269 LargeCacheRecord lcr;
1270 CacheRecord *AnswerList = NULL;
1271 mDNSu8 rcode;
1272 mDNSBool CloseSDOnExit = sd < 0;
1273
1274 VLog("Querying server for %##s type %d", e->name.c, e->type);
1275
1276 flags.b[0] |= kDNSFlag0_RD; // recursion desired
1277 id.NotAnInteger = 0;
1278 InitializeDNSMessage(&q.msg.h, id, flags);
1279
1280 end = putQuestion(&q.msg, end, end + AbsoluteMaxDNSMessageData, &e->name, e->type, kDNSClass_IN);
1281 if (!end) { Log("Error: AnswerQuestion - putQuestion returned NULL"); goto end; }
1282 q.len = (int)(end - (mDNSu8 *)&q.msg);
1283
1284 if (sd < 0) sd = ConnectToServer(d);
1285 if (sd < 0) { Log("AnswerQuestion: ConnectToServer failed"); goto end; }
1286 if (SendTCPMsg(sd, &q)) { Log("AnswerQuestion: SendTCPMsg failed"); close(sd); goto end; }
1287 reply = ReadTCPMsg(sd, NULL);
1288
1289 if ((reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery))
1290 { Log("AnswerQuestion: %##s type %d - Invalid response flags from server"); goto end; }
1291 rcode = (mDNSu8)(reply->msg.h.flags.b[1] & kDNSFlag1_RC);
1292 if (rcode && rcode != kDNSFlag1_RC_NXDomain) { Log("AnswerQuestion: %##s type %d - non-zero rcode %d from server", e->name.c, e->type, rcode); goto end; }
1293
1294 end = (mDNSu8 *)&reply->msg + reply->len;
1295 ansptr = LocateAnswers(&reply->msg, end);
1296 if (!ansptr) { Log("Error: AnswerQuestion - LocateAnswers returned NULL"); goto end; }
1297
1298 for (i = 0; i < reply->msg.h.numAnswers; i++)
1299 {
1300 ansptr = GetLargeResourceRecord(NULL, &reply->msg, ansptr, end, 0, kDNSRecordTypePacketAns, &lcr);
1301 if (!ansptr) { Log("AnswerQuestions: GetLargeResourceRecord returned NULL"); goto end; }
1302 if (lcr.r.resrec.rrtype != e->type || lcr.r.resrec.rrclass != kDNSClass_IN || !SameDomainName(lcr.r.resrec.name, &e->name))
1303 {
1304 Log("AnswerQuestion: response %##s type #d does not answer question %##s type #d. Discarding",
1305 lcr.r.resrec.name->c, lcr.r.resrec.rrtype, e->name.c, e->type);
1306 }
1307 else
1308 {
1309 CacheRecord *cr = CopyCacheRecord(&lcr.r, &e->name);
1310 if (!cr) { Log("Error: AnswerQuestion - CopyCacheRecord returned NULL"); goto end; }
1311 cr->next = AnswerList;
1312 AnswerList = cr;
1313 }
1314 }
1315
1316 end:
1317 if (sd > -1 && CloseSDOnExit) close(sd);
1318 if (reply) free(reply);
1319 return AnswerList;
1320 }
1321
1322 // Routine sets EventList to contain Add/Remove events, and deletes any removes from the KnownAnswer list
1323 mDNSlocal void UpdateAnswerList(DaemonInfo *d, AnswerListElem *a, int sd)
1324 {
1325 CacheRecord *cr, *NewAnswers, **na, **ka; // "new answer", "known answer"
1326
1327 // get up to date answers
1328 NewAnswers = AnswerQuestion(d, a, sd);
1329
1330 // first pass - mark all answers for deletion
1331 for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next)
1332 (*ka)->resrec.rroriginalttl = (unsigned)-1; // -1 means delete
1333
1334 // second pass - mark answers pre-existent
1335 for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next)
1336 {
1337 for (na = &NewAnswers; *na; na = &(*na)->next)
1338 {
1339 if (SameResourceRecord(&(*ka)->resrec, &(*na)->resrec))
1340 { (*ka)->resrec.rroriginalttl = 0; break; } // 0 means no change
1341 }
1342 }
1343
1344 // third pass - add new records to Event list
1345 na = &NewAnswers;
1346 while (*na)
1347 {
1348 for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next)
1349 if (SameResourceRecord(&(*ka)->resrec, &(*na)->resrec)) break;
1350 if (!*ka)
1351 {
1352 // answer is not in list - splice from NewAnswers list, add to Event list
1353 cr = *na;
1354 *na = (*na)->next; // splice from list
1355 cr->next = a->EventList; // add spliced record to event list
1356 a->EventList = cr;
1357 cr->resrec.rroriginalttl = 1; // 1 means add
1358 }
1359 else na = &(*na)->next;
1360 }
1361
1362 // move all the removes from the answer list to the event list
1363 ka = &a->KnownAnswers;
1364 while (*ka)
1365 {
1366 if ((*ka)->resrec.rroriginalttl == (unsigned)-1)
1367 {
1368 cr = *ka;
1369 *ka = (*ka)->next;
1370 cr->next = a->EventList;
1371 a->EventList = cr;
1372 }
1373 else ka = &(*ka)->next;
1374 }
1375
1376 // lastly, free the remaining records (known answers) in NewAnswers list
1377 while (NewAnswers)
1378 {
1379 cr = NewAnswers;
1380 NewAnswers = NewAnswers->next;
1381 free(cr);
1382 }
1383 }
1384
1385 mDNSlocal void SendEvents(DaemonInfo *d, LLQEntry *e)
1386 {
1387 PktMsg response;
1388 CacheRecord *cr;
1389 mDNSu8 *end = (mDNSu8 *)&response.msg.data;
1390 mDNSOpaque16 msgID;
1391 char rrbuf[80], addrbuf[32];
1392 AuthRecord opt;
1393
1394 msgID.NotAnInteger = random();
1395 if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32);
1396 InitializeDNSMessage(&response.msg.h, msgID, ResponseFlags);
1397 end = putQuestion(&response.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
1398 if (!end) { Log("Error: SendEvents - putQuestion returned NULL"); return; }
1399
1400 // put adds/removes in packet
1401 for (cr = e->AnswerList->EventList; cr; cr = cr->next)
1402 {
1403 if (verbose) GetRRDisplayString_rdb(&cr->resrec, &cr->resrec.rdata->u, rrbuf);
1404 VLog("%s (%s): %s", addrbuf, (mDNSs32)cr->resrec.rroriginalttl < 0 ? "Remove": "Add", rrbuf);
1405 end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAnswers, &cr->resrec, cr->resrec.rroriginalttl);
1406 if (!end) { Log("Error: SendEvents - UpdateAnswerList returned NULL"); return; }
1407 }
1408
1409 FormatLLQOpt(&opt, kLLQOp_Event, e->id, LLQLease(e));
1410 end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAdditionals, &opt.resrec, 0);
1411 if (!end) { Log("Error: SendEvents - PutResourceRecordTTLJumbo"); return; }
1412
1413 response.len = (int)(end - (mDNSu8 *)&response.msg);
1414 if (SendLLQ(d, &response, e->cli) < 0) LogMsg("Error: SendEvents - SendLLQ");
1415 }
1416
1417 mDNSlocal void PrintLLQTable(DaemonInfo *d)
1418 {
1419 LLQEntry *e;
1420 char addr[32];
1421 int i;
1422
1423 Log("Printing LLQ table contents");
1424
1425 for (i = 0; i < LLQ_TABLESIZE; i++)
1426 {
1427 e = d->LLQTable[i];
1428 while(e)
1429 {
1430 inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
1431 Log("LLQ from %##s type %d lease %d (%d remaining)",
1432 addr, e->qname.c, e->qtype, e->lease, LLQLease(e));
1433 e = e->next;
1434 }
1435 }
1436 }
1437
1438 // Send events to clients as a result of a change in the zone
1439 mDNSlocal void GenLLQEvents(DaemonInfo *d)
1440 {
1441 LLQEntry **e;
1442 int i, sd;
1443 struct timeval t;
1444
1445 VLog("Generating LLQ Events");
1446
1447 gettimeofday(&t, NULL);
1448 sd = ConnectToServer(d);
1449 if (sd < 0) { Log("GenLLQEvents: ConnectToServer failed"); return; }
1450
1451 // get all answers up to date
1452 for (i = 0; i < LLQ_TABLESIZE; i++)
1453 {
1454 AnswerListElem *a = d->AnswerTable[i];
1455 while(a)
1456 {
1457 UpdateAnswerList(d, a, sd);
1458 a = a->next;
1459 }
1460 }
1461
1462 // for each established LLQ, send events
1463 for (i = 0; i < LLQ_TABLESIZE; i++)
1464 {
1465 e = &d->LLQTable[i];
1466 while(*e)
1467 {
1468 if ((*e)->expire < t.tv_sec) DeleteLLQ(d, *e);
1469 else
1470 {
1471 if ((*e)->state == Established && (*e)->AnswerList->EventList) SendEvents(d, *e);
1472 e = &(*e)->next;
1473 }
1474 }
1475 }
1476
1477 // now that all LLQs are updated, we move Add events from the Event list to the Known Answer list, and free Removes
1478 for (i = 0; i < LLQ_TABLESIZE; i++)
1479 {
1480 AnswerListElem *a = d->AnswerTable[i];
1481 while(a)
1482 {
1483 if (a->EventList)
1484 {
1485 CacheRecord *cr = a->EventList, *tmp;
1486 while (cr)
1487 {
1488 tmp = cr;
1489 cr = cr->next;
1490 if ((signed)tmp->resrec.rroriginalttl < 0) free(tmp);
1491 else
1492 {
1493 tmp->next = a->KnownAnswers;
1494 a->KnownAnswers = tmp;
1495 tmp->resrec.rroriginalttl = 0;
1496 }
1497 }
1498 a->EventList = NULL;
1499 }
1500 a = a->next;
1501 }
1502 }
1503
1504 close(sd);
1505 }
1506
1507 // Monitor zone for changes that may produce LLQ events
1508 mDNSlocal void *LLQEventMonitor(void *DInfoPtr)
1509 {
1510 DaemonInfo *d = DInfoPtr;
1511 PktMsg q;
1512 mDNSu8 *end = q.msg.data;
1513 const mDNSu8 *ptr;
1514 mDNSOpaque16 id, flags = QueryFlags;
1515 PktMsg reply;
1516 mDNSs32 serial = 0;
1517 mDNSBool SerialInitialized = mDNSfalse;
1518 int sd;
1519 LargeCacheRecord lcr;
1520 ResourceRecord *rr = &lcr.r.resrec;
1521 int i, sleeptime = 0;
1522 domainname zone;
1523 char pingmsg[4];
1524
1525 // create question
1526 id.NotAnInteger = 0;
1527 InitializeDNSMessage(&q.msg.h, id, flags);
1528 AssignDomainName(&zone, &d->zone);
1529 end = putQuestion(&q.msg, end, end + AbsoluteMaxDNSMessageData, &zone, kDNSType_SOA, kDNSClass_IN);
1530 if (!end) { Log("Error: LLQEventMonitor - putQuestion returned NULL"); return NULL; }
1531 q.len = (int)(end - (mDNSu8 *)&q.msg);
1532
1533 sd = ConnectToServer(d);
1534 if (sd < 0) { Log("LLQEventMonitor: ConnectToServer failed"); return NULL; }
1535
1536 while(1)
1537 {
1538 usleep(sleeptime);
1539 sleeptime = LLQ_MONITOR_ERR_INTERVAL; // if we bail on error below, rate limit retry
1540
1541 // send message, receive response
1542 if (SendTCPMsg(sd, &q)) { Log("LLQEventMonitor: SendTCPMsg failed"); continue; }
1543 if (!ReadTCPMsg(sd, &reply)) { Log("LLQEventMonitor: ReadTCPMsg failed"); continue; }
1544 end = (mDNSu8 *)&reply.msg + reply.len;
1545 if (reply.msg.h.flags.b[1] & kDNSFlag1_RC) { Log("LLQEventMonitor - received non-zero rcode"); continue; }
1546
1547 // find answer
1548 ptr = LocateAnswers(&reply.msg, end);
1549 if (!ptr) { Log("Error: LLQEventMonitor - LocateAnswers returned NULL"); continue; }
1550 for (i = 0; i < reply.msg.h.numAnswers; i++)
1551 {
1552 ptr = GetLargeResourceRecord(NULL, &reply.msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
1553 if (!ptr) { Log("Error: LLQEventMonitor - GetLargeResourceRecord returned NULL"); continue; }
1554 if (rr->rrtype != kDNSType_SOA || rr->rrclass != kDNSClass_IN || !SameDomainName(rr->name, &zone)) continue;
1555 if (!SerialInitialized)
1556 {
1557 // first time through loop
1558 SerialInitialized = mDNStrue;
1559 serial = rr->rdata->u.soa.serial;
1560 sleeptime = LLQ_MONITOR_INTERVAL;
1561 break;
1562 }
1563 else if (rr->rdata->u.soa.serial != serial)
1564 {
1565 // update serial, wake main thread
1566 serial = rr->rdata->u.soa.serial;
1567 VLog("LLQEventMonitor: zone changed. Signaling main thread.");
1568 if (send(d->LLQServPollSock, pingmsg, sizeof(pingmsg), 0) != sizeof(pingmsg))
1569 { LogErr("LLQEventMonitor", "send"); break; }
1570 }
1571 sleeptime = LLQ_MONITOR_INTERVAL;
1572 break;
1573 }
1574 if (!ptr) Log("LLQEventMonitor: response to query did not contain SOA");
1575 }
1576 }
1577
1578 mDNSlocal void SetAnswerList(DaemonInfo *d, LLQEntry *e)
1579 {
1580 int bucket = DomainNameHashValue(&e->qname) % LLQ_TABLESIZE;
1581 AnswerListElem *a = d->AnswerTable[bucket];
1582 while (a && (a->type != e->qtype ||!SameDomainName(&a->name, &e->qname))) a = a->next;
1583 if (!a)
1584 {
1585 a = malloc(sizeof(*a));
1586 if (!a) { LogErr("SetAnswerList", "malloc"); return; }
1587 AssignDomainName(&a->name, &e->qname);
1588 a->type = e->qtype;
1589 a->refcount = 0;
1590 a->KnownAnswers = NULL;
1591 a->EventList = NULL;
1592 a->next = d->AnswerTable[bucket];
1593 d->AnswerTable[bucket] = a;
1594
1595 // to get initial answer list, call UpdateAnswerList and move cache records from EventList to KnownAnswers
1596 UpdateAnswerList(d, a, -1);
1597 a->KnownAnswers = a->EventList;
1598 a->EventList = NULL;
1599 }
1600
1601 e->AnswerList = a;
1602 a->refcount ++;
1603 }
1604
1605 // Allocate LLQ entry, insert into table
1606 mDNSlocal LLQEntry *NewLLQ(DaemonInfo *d, struct sockaddr_in cli, domainname *qname, mDNSu16 qtype, mDNSu32 lease)
1607 {
1608 char addr[32];
1609 struct timeval t;
1610 int bucket = DomainNameHashValue(qname) % LLQ_TABLESIZE;
1611 LLQEntry *e;
1612
1613 e = malloc(sizeof(*e));
1614 if (!e) { LogErr("NewLLQ", "malloc"); return NULL; }
1615
1616 inet_ntop(AF_INET, &cli.sin_addr, addr, 32);
1617 VLog("Allocating LLQ entry for client %s question %##s type %d", addr, qname->c, qtype);
1618
1619 // initialize structure
1620 e->cli = cli;
1621 AssignDomainName(&e->qname, qname);
1622 e->qtype = qtype;
1623 memset(e->id, 0, 8);
1624 e->state = RequestReceived;
1625 e->AnswerList = NULL;
1626
1627 if (lease < LLQ_MIN_LEASE) lease = LLQ_MIN_LEASE;
1628 else if (lease > LLQ_MAX_LEASE) lease = LLQ_MIN_LEASE;
1629 gettimeofday(&t, NULL);
1630 e->expire = t.tv_sec + (int)lease;
1631 e->lease = lease;
1632
1633 // add to table
1634 e->next = d->LLQTable[bucket];
1635 d->LLQTable[bucket] = e;
1636
1637 return e;
1638 }
1639
1640 // Handle a refresh request from client
1641 mDNSlocal void LLQRefresh(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID)
1642 {
1643 AuthRecord opt;
1644 PktMsg ack;
1645 mDNSu8 *end = (mDNSu8 *)&ack.msg.data;
1646 char addr[32];
1647
1648 inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
1649 VLog("%s LLQ for %##s from %s", llq->lease ? "Refreshing" : "Deleting", e->qname.c, addr);
1650
1651 if (llq->lease)
1652 {
1653 if (llq->lease < LLQ_MIN_LEASE) llq->lease = LLQ_MIN_LEASE;
1654 else if (llq->lease > LLQ_MAX_LEASE) llq->lease = LLQ_MIN_LEASE;
1655 }
1656
1657 ack.src.sin_addr.s_addr = 0; // unused
1658 InitializeDNSMessage(&ack.msg.h, msgID, ResponseFlags);
1659 end = putQuestion(&ack.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
1660 if (!end) { Log("Error: putQuestion"); return; }
1661
1662 FormatLLQOpt(&opt, kLLQOp_Refresh, e->id, llq->lease ? LLQLease(e) : 0);
1663 end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAdditionals, &opt.resrec, 0);
1664 if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
1665
1666 ack.len = (int)(end - (mDNSu8 *)&ack.msg);
1667 if (SendLLQ(d, &ack, e->cli)) Log("Error: LLQRefresh");
1668
1669 if (llq->lease) e->state = Established;
1670 else DeleteLLQ(d, e);
1671 }
1672
1673 // Complete handshake with Ack an initial answers
1674 mDNSlocal void LLQCompleteHandshake(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID)
1675 {
1676 char addr[32];
1677 CacheRecord *ptr;
1678 AuthRecord opt;
1679 PktMsg ack;
1680 mDNSu8 *end = (mDNSu8 *)&ack.msg.data;
1681 char rrbuf[80], addrbuf[32];
1682
1683 inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
1684
1685 if (memcmp(llq->id, e->id, 8) ||
1686 llq->vers != kLLQ_Vers ||
1687 llq->llqOp != kLLQOp_Setup ||
1688 llq->err != LLQErr_NoError ||
1689 llq->lease > e->lease + LLQ_LEASE_FUDGE ||
1690 llq->lease < e->lease - LLQ_LEASE_FUDGE)
1691 { Log("Incorrect challenge response from %s", addr); return; }
1692
1693 if (e->state == Established) VLog("Retransmitting LLQ ack + answers for %##s", e->qname.c);
1694 else VLog("Delivering LLQ ack + answers for %##s", e->qname.c);
1695
1696 // format ack + answers
1697 ack.src.sin_addr.s_addr = 0; // unused
1698 InitializeDNSMessage(&ack.msg.h, msgID, ResponseFlags);
1699 end = putQuestion(&ack.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
1700 if (!end) { Log("Error: putQuestion"); return; }
1701
1702 if (e->state != Established) { SetAnswerList(d, e); e->state = Established; }
1703
1704 if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32);
1705 for (ptr = e->AnswerList->KnownAnswers; ptr; ptr = ptr->next)
1706 {
1707 if (verbose) GetRRDisplayString_rdb(&ptr->resrec, &ptr->resrec.rdata->u, rrbuf);
1708 VLog("%s Intitial Answer - %s", addr, rrbuf);
1709 end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAnswers, &ptr->resrec, 1);
1710 if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
1711 }
1712
1713 FormatLLQOpt(&opt, kLLQOp_Setup, e->id, LLQLease(e));
1714 end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAdditionals, &opt.resrec, 0);
1715 if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
1716
1717 ack.len = (int)(end - (mDNSu8 *)&ack.msg);
1718 if (SendLLQ(d, &ack, e->cli)) Log("Error: LLQCompleteHandshake");
1719 }
1720
1721 mDNSlocal void LLQSetupChallenge(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID)
1722 {
1723 struct timeval t;
1724 mDNSu32 randval;
1725 PktMsg challenge;
1726 mDNSu8 *end = challenge.msg.data;
1727 AuthRecord opt;
1728
1729 if (e->state == ChallengeSent) VLog("Retransmitting LLQ setup challenge for %##s", e->qname.c);
1730 else VLog("Sending LLQ setup challenge for %##s", e->qname.c);
1731
1732 if (!ZERO_LLQID(llq->id)) { Log("Error: LLQSetupChallenge - nonzero ID"); return; } // server bug
1733 if (llq->llqOp != kLLQOp_Setup) { Log("LLQSetupChallenge - incorrrect operation from client"); return; } // client error
1734
1735 if (ZERO_LLQID(e->id)) // don't regenerate random ID for retransmissions
1736 {
1737 // construct ID <time><random>
1738 gettimeofday(&t, NULL);
1739 randval = random();
1740 memcpy(e->id, &t.tv_sec, sizeof(t.tv_sec));
1741 memcpy(e->id + sizeof(t.tv_sec), &randval, sizeof(randval));
1742 }
1743
1744 // format response (query + LLQ opt rr)
1745 challenge.src.sin_addr.s_addr = 0; // unused
1746 InitializeDNSMessage(&challenge.msg.h, msgID, ResponseFlags);
1747 end = putQuestion(&challenge.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
1748 if (!end) { Log("Error: putQuestion"); return; }
1749 FormatLLQOpt(&opt, kLLQOp_Setup, e->id, LLQLease(e));
1750 end = PutResourceRecordTTLJumbo(&challenge.msg, end, &challenge.msg.h.numAdditionals, &opt.resrec, 0);
1751 if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
1752 challenge.len = (int)(end - (mDNSu8 *)&challenge.msg);
1753 if (SendLLQ(d, &challenge, e->cli)) { Log("Error: LLQSetupChallenge"); return; }
1754 e->state = ChallengeSent;
1755 }
1756
1757 // Take action on an LLQ message from client. Entry must be initialized and in table
1758 mDNSlocal void UpdateLLQ(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID)
1759 {
1760 switch(e->state)
1761 {
1762 case RequestReceived:
1763 LLQSetupChallenge(d, e, llq, msgID);
1764 return;
1765 case ChallengeSent:
1766 if (ZERO_LLQID(llq->id)) LLQSetupChallenge(d, e, llq, msgID); // challenge sent and lost
1767 else LLQCompleteHandshake(d, e, llq, msgID);
1768 return;
1769 case Established:
1770 if (ZERO_LLQID(llq->id))
1771 {
1772 // client started over. reset state.
1773 LLQEntry *newe = NewLLQ(d, e->cli, &e->qname, e->qtype, llq->lease);
1774 if (!newe) return;
1775 DeleteLLQ(d, e);
1776 LLQSetupChallenge(d, newe, llq, msgID);
1777 return;
1778 }
1779 else if (llq->llqOp == kLLQOp_Setup)
1780 { LLQCompleteHandshake(d, e, llq, msgID); return; } // Ack lost
1781 else if (llq->llqOp == kLLQOp_Refresh)
1782 { LLQRefresh(d, e, llq, msgID); return; }
1783 else { Log("Unhandled message for established LLQ"); return; }
1784 }
1785 }
1786
1787 mDNSlocal LLQEntry *LookupLLQ(DaemonInfo *d, struct sockaddr_in cli, domainname *qname, mDNSu16 qtype, mDNSu8 *id)
1788 {
1789 int bucket = bucket = DomainNameHashValue(qname) % LLQ_TABLESIZE;
1790 LLQEntry *ptr = d->LLQTable[bucket];
1791
1792 while(ptr)
1793 {
1794 if (((ptr->state == ChallengeSent && ZERO_LLQID(id)) || // zero-id due to packet loss OK in state ChallengeSent
1795 !memcmp(id, ptr->id, 8)) && // id match
1796 SAME_INADDR(cli, ptr->cli) && qtype == ptr->qtype && SameDomainName(&ptr->qname, qname)) // same source, type, qname
1797 return ptr;
1798 ptr = ptr->next;
1799 }
1800 return NULL;
1801 }
1802
1803 mDNSlocal int RecvLLQ(DaemonInfo *d, PktMsg *pkt)
1804 {
1805 DNSQuestion q;
1806 LargeCacheRecord opt;
1807 int i, err = -1;
1808 char addr[32];
1809 const mDNSu8 *qptr = pkt->msg.data;
1810 const mDNSu8 *end = (mDNSu8 *)&pkt->msg + pkt->len;
1811 const mDNSu8 *aptr = LocateAdditionals(&pkt->msg, end);
1812 LLQOptData *llq = NULL;
1813 LLQEntry *e = NULL;
1814
1815 HdrNToH(pkt);
1816 inet_ntop(AF_INET, &pkt->src.sin_addr, addr, 32);
1817
1818 VLog("Received LLQ msg from %s", addr);
1819 // sanity-check packet
1820 if (!pkt->msg.h.numQuestions || !pkt->msg.h.numAdditionals)
1821 {
1822 Log("Malformatted LLQ from %s with %d questions, %d additionals", addr, pkt->msg.h.numQuestions, pkt->msg.h.numAdditionals);
1823 goto end;
1824 }
1825
1826 // find the OPT RR - must be last in message
1827 for (i = 0; i < pkt->msg.h.numAdditionals; i++)
1828 {
1829 aptr = GetLargeResourceRecord(NULL, &pkt->msg, aptr, end, 0, kDNSRecordTypePacketAdd, &opt);
1830 if (!aptr) { Log("Malformatted LLQ from %s: could not get Additional record %d", addr, i); goto end; }
1831 }
1832
1833 // validate OPT
1834 if (opt.r.resrec.rrtype != kDNSType_OPT) { Log("Malformatted LLQ from %s: last Additional not an Opt RR", addr); goto end; }
1835 if (opt.r.resrec.rdlength < pkt->msg.h.numQuestions * LLQ_OPT_RDLEN) { Log("Malformatted LLQ from %s: Opt RR to small (%d bytes for %d questions)", addr, opt.r.resrec.rdlength, pkt->msg.h.numQuestions); }
1836
1837 // dispatch each question
1838 for (i = 0; i < pkt->msg.h.numQuestions; i++)
1839 {
1840 qptr = getQuestion(&pkt->msg, qptr, end, 0, &q);
1841 if (!qptr) { Log("Malformatted LLQ from %s: cannot read question %d", addr, i); goto end; }
1842 llq = (LLQOptData *)&opt.r.resrec.rdata->u.opt.OptData.llq + i; // point into OptData at index i
1843 if (llq->vers != kLLQ_Vers) { Log("LLQ from %s contains bad version %d (expected %d)", addr, llq->vers, kLLQ_Vers); goto end; }
1844
1845 e = LookupLLQ(d, pkt->src, &q.qname, q.qtype, llq->id);
1846 if (!e)
1847 {
1848 // no entry - if zero ID, create new
1849 e = NewLLQ(d, pkt->src, &q.qname, q.qtype, llq->lease);
1850 if (!e) goto end;
1851 }
1852 UpdateLLQ(d, e, llq, pkt->msg.h.id);
1853 }
1854 err = 0;
1855
1856 end:
1857 HdrHToN(pkt);
1858 return err;
1859 }
1860
1861 mDNSlocal mDNSBool IsLLQRequest(PktMsg *pkt)
1862 {
1863 const mDNSu8 *ptr = NULL, *end = (mDNSu8 *)&pkt->msg + pkt->len;
1864 LargeCacheRecord lcr;
1865 int i;
1866 mDNSBool result = mDNSfalse;
1867
1868 HdrNToH(pkt);
1869 if ((mDNSu8)(pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (mDNSu8)(kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery)) goto end;
1870
1871 if (!pkt->msg.h.numAdditionals) goto end;
1872 ptr = LocateAdditionals(&pkt->msg, end);
1873 if (!ptr) goto end;
1874
1875 // find last Additional
1876 for (i = 0; i < pkt->msg.h.numAdditionals; i++)
1877 {
1878 ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr);
1879 if (!ptr) { Log("Unable to read additional record"); goto end; }
1880 }
1881
1882 if (lcr.r.resrec.rrtype == kDNSType_OPT &&
1883 lcr.r.resrec.rdlength >= LLQ_OPT_RDLEN &&
1884 lcr.r.resrec.rdata->u.opt.opt == kDNSOpt_LLQ)
1885 { result = mDNStrue; goto end; }
1886
1887 end:
1888 HdrHToN(pkt);
1889 return result;
1890 }
1891
1892 // !!!KRS implement properly
1893 mDNSlocal mDNSBool IsLLQAck(PktMsg *pkt)
1894 {
1895 if (pkt->msg.h.flags.NotAnInteger == ResponseFlags.NotAnInteger &&
1896 pkt->msg.h.numQuestions && !pkt->msg.h.numAnswers && !pkt->msg.h.numAuthorities) return mDNStrue;
1897 return mDNSfalse;
1898 }
1899
1900
1901 // request handler wrappers for TCP and UDP requests
1902 // (read message off socket, fork thread that invokes main processing routine and handles cleanup)
1903 mDNSlocal void *UDPUpdateRequestForkFn(void *vptr)
1904 {
1905 char buf[32];
1906 UDPRequestArgs *req = vptr;
1907 PktMsg *reply = NULL;
1908
1909 VLog("Received UDP request: %d bytes from %s", req->pkt.len, inet_ntop(AF_INET, &req->pkt.src.sin_addr, buf, 32));
1910 //!!!KRS strictly speaking, we shouldn't use TCP for a UDP request because the server may give us a long answer that would require truncation for UDP delivery to client
1911 reply = HandleRequest(&req->pkt, req->d);
1912 if (reply)
1913 {
1914 if (sendto(req->d->udpsd, &reply->msg, reply->len, 0, (struct sockaddr *)&req->pkt.src, sizeof(req->pkt.src)) != (int)reply->len)
1915 LogErr("UDPUpdateRequestForkFn", "sendto");
1916 }
1917
1918 if (reply) free(reply);
1919 free(req);
1920 pthread_exit(NULL);
1921 }
1922
1923 //!!!KRS this needs to be changed to use non-blocking sockets
1924 mDNSlocal int RecvUDPRequest(int sd, DaemonInfo *d)
1925 {
1926 UDPRequestArgs *req;
1927 pthread_t tid;
1928 unsigned int clisize = sizeof(req->cliaddr);
1929
1930 req = malloc(sizeof(UDPRequestArgs));
1931 if (!req) { LogErr("RecvUDPRequest", "malloc"); return -1; }
1932 bzero(req, sizeof(*req));
1933 req->d = d;
1934 req->pkt.len = recvfrom(sd, &req->pkt.msg, sizeof(req->pkt.msg), 0, (struct sockaddr *)&req->cliaddr, &clisize);
1935 if ((int)req->pkt.len < 0) { LogErr("RecvUDPRequest", "recvfrom"); free(req); return -1; }
1936 if (clisize != sizeof(req->cliaddr)) { Log("Client address of unknown size %d", clisize); free(req); return -1; }
1937 req->pkt.src = req->cliaddr;
1938
1939 if (IsLLQRequest(&req->pkt))
1940 {
1941 // LLQ messages handled by main thread
1942 int err = RecvLLQ(d, &req->pkt);
1943 free(req);
1944 return err;
1945 }
1946
1947 if (IsLLQAck(&req->pkt)) { free(req); return 0; } // !!!KRS need to do acks + retrans
1948
1949 if (pthread_create(&tid, NULL, UDPUpdateRequestForkFn, req)) { LogErr("RecvUDPRequest", "pthread_create"); free(req); return -1; }
1950 pthread_detach(tid);
1951 return 0;
1952 }
1953
1954 mDNSlocal void *TCPRequestForkFn(void *vptr)
1955 {
1956 TCPRequestArgs *req = vptr;
1957 PktMsg *in = NULL, *out = NULL;
1958 char buf[32];
1959
1960 //!!!KRS if this read blocks indefinitely, we can run out of threads
1961 // read the request
1962 in = ReadTCPMsg(req->sd, NULL);
1963 if (!in)
1964 {
1965 LogMsg("TCPRequestForkFn: Could not read message from %s", inet_ntop(AF_INET, &req->cliaddr.sin_addr, buf, 32));
1966 goto cleanup;
1967 }
1968
1969 VLog("Received TCP request: %d bytes from %s", in->len, inet_ntop(AF_INET, &req->cliaddr.sin_addr, buf, 32));
1970 // create the reply
1971 out = HandleRequest(in, req->d);
1972 if (!out)
1973 {
1974 LogMsg("TCPRequestForkFn: No reply for client %s", inet_ntop(AF_INET, &req->cliaddr.sin_addr, buf, 32));
1975 goto cleanup;
1976 }
1977
1978 // deliver reply to client
1979 if (SendTCPMsg(req->sd, out) < 0)
1980 {
1981 LogMsg("TCPRequestForkFn: Unable to send reply to client %s", inet_ntop(AF_INET, &req->cliaddr.sin_addr, buf, 32));
1982 goto cleanup;
1983 }
1984
1985 cleanup:
1986 free(req);
1987 if (in) free(in);
1988 if (out) free(out);
1989 pthread_exit(NULL);
1990 }
1991
1992 mDNSlocal int RecvTCPRequest(int sd, DaemonInfo *d)
1993 {
1994 TCPRequestArgs *req;
1995 pthread_t tid;
1996 unsigned int clilen = sizeof(req->cliaddr);
1997
1998 req = malloc(sizeof(TCPRequestArgs));
1999 if (!req) { LogErr("RecvTCPRequest", "malloc"); return -1; }
2000 bzero(req, sizeof(*req));
2001 req->d = d;
2002 req->sd = accept(sd, (struct sockaddr *)&req->cliaddr, &clilen);
2003 if (req->sd < 0) { LogErr("RecvTCPRequest", "accept"); return -1; }
2004 if (clilen != sizeof(req->cliaddr)) { Log("Client address of unknown size %d", clilen); free(req); return -1; }
2005 if (pthread_create(&tid, NULL, TCPRequestForkFn, req)) { LogErr("RecvTCPRequest", "pthread_create"); free(req); return -1; }
2006 pthread_detach(tid);
2007 return 0;
2008 }
2009
2010 // main event loop
2011 // listen for incoming requests, periodically check table for expired records, respond to signals
2012 mDNSlocal int ListenForUpdates(DaemonInfo *d)
2013 {
2014 int err;
2015 int maxfdp1;
2016 fd_set rset;
2017 struct timeval timenow, timeout = { 0, 0 };
2018 long NextTableCheck = 0;
2019
2020 VLog("Listening for requests...");
2021
2022 FD_ZERO(&rset);
2023 maxfdp1 = d->tcpsd + 1;
2024 if (d->udpsd + 1 > maxfdp1) maxfdp1 = d->udpsd + 1;
2025 if (d->LLQEventListenSock + 1 > maxfdp1) maxfdp1 = d->LLQEventListenSock + 1;
2026
2027 while(1)
2028 {
2029 // expire records if necessary, set timeout
2030 if (gettimeofday(&timenow, NULL)) { LogErr("ListenForUpdates", "gettimeofday"); return -1; }
2031 if (timenow.tv_sec >= NextTableCheck)
2032 {
2033 DeleteExpiredRecords(d);
2034 NextTableCheck = timenow.tv_sec + EXPIRATION_INTERVAL;
2035 }
2036 timeout.tv_sec = NextTableCheck - timenow.tv_sec;
2037
2038 FD_SET(d->tcpsd, &rset);
2039 FD_SET(d->udpsd, &rset);
2040 FD_SET(d->LLQEventListenSock, &rset);
2041
2042 err = select(maxfdp1, &rset, NULL, NULL, &timeout);
2043 if (err < 0)
2044 {
2045 if (errno == EINTR)
2046 {
2047 if (terminate) { DeleteExpiredRecords(d); return 0; }
2048 else if (dumptable) { PrintLeaseTable(d); PrintLLQTable(d); dumptable = 0; }
2049 else Log("Received unhandled signal - continuing");
2050 }
2051 else { LogErr("ListenForUpdates", "select"); return -1; }
2052 }
2053 else
2054 {
2055 if (FD_ISSET(d->tcpsd, &rset)) RecvTCPRequest(d->tcpsd, d);
2056 if (FD_ISSET(d->udpsd, &rset)) RecvUDPRequest(d->udpsd, d);
2057 if (FD_ISSET(d->LLQEventListenSock, &rset))
2058 {
2059 // clear signalling data off socket
2060 char buf[32];
2061 recv(d->LLQEventListenSock, buf, 32, 0);
2062 GenLLQEvents(d);
2063 }
2064 }
2065 }
2066 return 0;
2067 }
2068
2069 // signal handler sets global variables, which are inspected by main event loop
2070 // (select automatically returns due to the handled signal)
2071 mDNSlocal void HndlSignal(int sig)
2072 {
2073 if (sig == SIGTERM || sig == SIGINT ) { terminate = 1; return; }
2074 if (sig == INFO_SIGNAL) { dumptable = 1; return; }
2075 }
2076
2077 int main(int argc, char *argv[])
2078 {
2079 pthread_t LLQtid;
2080 DaemonInfo *d;
2081
2082 d = malloc(sizeof(*d));
2083 if (!d) { LogErr("main", "malloc"); exit(1); }
2084 bzero(d, sizeof(DaemonInfo));
2085
2086 if (signal(SIGTERM, HndlSignal) == SIG_ERR) perror("Can't catch SIGTERM");
2087 if (signal(INFO_SIGNAL, HndlSignal) == SIG_ERR) perror("Can't catch SIGINFO");
2088 if (signal(SIGINT, HndlSignal) == SIG_ERR) perror("Can't catch SIGINT");
2089 if (signal(SIGPIPE, SIG_IGN ) == SIG_ERR) perror("Can't ignore SIGPIPE");
2090
2091 if (ProcessArgs(argc, argv, d) < 0) exit(1);
2092
2093 if (!foreground)
2094 {
2095 if (daemon(0,0))
2096 {
2097 LogErr("main", "daemon");
2098 fprintf(stderr, "Could not daemonize process, running in foreground");
2099 foreground = 1;
2100 }
2101 }
2102
2103 if (InitLeaseTable(d) < 0) exit(1);
2104 if (SetupSockets(d) < 0) exit(1);
2105 if (SetUpdateSRV(d) < 0) exit(1);
2106
2107 if (pthread_create(&LLQtid, NULL, LLQEventMonitor, d)) { LogErr("main", "pthread_create"); }
2108 else
2109 {
2110 pthread_detach(LLQtid);
2111 ListenForUpdates(d);
2112 }
2113
2114 if (ClearUpdateSRV(d) < 0) exit(1); // clear update srv's even if ListenForUpdates or pthread_create returns an error
2115 free(d);
2116 exit(0);
2117
2118 }