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