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