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