1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
24 Change History (most recent first):
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
30 Revision 1.34 2005/03/16 18:47:37 ksekar
31 <rdar://problem/4046465> dnsextd doesn't clean up on exit
33 Revision 1.33 2005/03/11 19:09:02 ksekar
34 Fixed ZERO_LLQID macro
36 Revision 1.32 2005/03/10 22:54:33 ksekar
37 <rdar://problem/4046285> dnsextd leaks memory/ports
39 Revision 1.31 2005/02/24 02:37:57 ksekar
40 <rdar://problem/4021977> dnsextd memory management improvements
42 Revision 1.30 2005/01/27 22:57:56 cheshire
43 Fix compile errors on gcc4
45 Revision 1.29 2004/12/22 00:13:50 ksekar
46 <rdar://problem/3873993> Change version, port, and polling interval for LLQ
48 Revision 1.28 2004/12/17 00:30:00 ksekar
49 <rdar://problem/3924045> dnsextd memory leak
51 Revision 1.27 2004/12/17 00:27:32 ksekar
54 Revision 1.26 2004/12/17 00:21:33 ksekar
55 Fixes for new CacheRecord structure with indirect name pointer
57 Revision 1.25 2004/12/16 20:13:02 cheshire
58 <rdar://problem/3324626> Cache memory management improvements
60 Revision 1.24 2004/12/14 17:09:06 ksekar
61 fixed incorrect usage instructions
63 Revision 1.23 2004/12/06 20:24:31 ksekar
64 <rdar://problem/3907303> dnsextd leaks sockets
66 Revision 1.22 2004/12/03 20:20:29 ksekar
67 <rdar://problem/3904149> dnsextd: support delivery of large records via LLQ events
69 Revision 1.21 2004/12/03 06:11:34 ksekar
70 <rdar://problem/3885059> clean up dnsextd arguments
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.
76 Revision 1.19 2004/12/01 01:16:29 cheshire
77 Solaris compatibility fixes
79 Revision 1.18 2004/11/30 23:51:06 cheshire
80 Remove double semicolons
82 Revision 1.17 2004/11/30 22:37:01 cheshire
83 Update copyright dates and add "Mode: C; tab-width: 4" headers
85 Revision 1.16 2004/11/25 02:02:28 ksekar
86 Fixed verbose log message argument
88 Revision 1.15 2004/11/19 02:35:02 ksekar
89 <rdar://problem/3886317> Wide Area Security: Add LLQ-ID to events
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>.
94 Revision 1.13 2004/11/13 02:22:36 ksekar
95 <rdar://problem/3878201> Refresh Acks from daemon malformatted
97 Revision 1.12 2004/11/12 01:05:01 ksekar
98 <rdar://problem/3876757> dnsextd: daemon registers the SRV same record
101 Revision 1.11 2004/11/12 01:03:31 ksekar
102 <rdar://problem/3876776> dnsextd: KnownAnswers (CacheRecords) leaked
104 Revision 1.10 2004/11/12 00:35:28 ksekar
105 <rdar://problem/3876705> dnsextd: uninitialized pointer can cause crash
107 Revision 1.9 2004/11/10 20:38:17 ksekar
108 <rdar://problem/3874168> dnsextd: allow a "fudge" in LLQ lease echo
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.
115 Revision 1.7 2004/10/30 00:06:58 ksekar
116 <rdar://problem/3722535> Support Long Lived Queries in DNS Extension daemon
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.
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
127 Revision 1.4 2004/09/14 23:27:48 cheshire
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
133 Revision 1.2 2004/08/24 23:27:57 cheshire
134 Fixes for Linux compatibility:
137 Don't try to set servaddr.sin_len on platforms that don't have sa_len
139 Revision 1.1 2004/08/11 00:43:26 ksekar
140 <rdar://problem/3722542>: DNS Extension daemon for DNS Update Lease
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
153 #include <sys/types.h>
154 #include <sys/socket.h>
155 #include <netinet/in.h>
156 #include <arpa/inet.h>
160 #include <sys/time.h>
164 // Compatibility workaround
166 #define AF_LOCAL AF_UNIX
173 #define LOOPBACK "127.0.0.1"
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
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
188 // LLQ SOA poll interval (microseconds)
189 #define LLQ_MONITOR_ERR_INTERVAL (60 * 1000000)
190 #define LLQ_MONITOR_INTERVAL 250000
192 #define INFO_SIGNAL SIGINFO
194 #define INFO_SIGNAL SIGUSR1
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))
202 // Structs/fields that must be locked for thread safety are explicitly commented
207 struct sockaddr_in src
;
210 // Note: extra storage for oversized (TCP) messages goes here
214 typedef struct RRTableElem
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
231 typedef struct AnswerListElem
233 struct AnswerListElem
*next
;
236 CacheRecord
*KnownAnswers
; // All valid answers delivered to client
237 CacheRecord
*EventList
; // New answers (adds/removes) to be sent to client
242 typedef struct LLQEntry
244 struct LLQEntry
*next
;
245 struct sockaddr_in cli
; // clien'ts source address
250 mDNSu32 lease
; // original lease, in seconds
251 mDNSs32 expire
; // expiration, absolute, in seconds since epoch
252 AnswerListElem
*AnswerList
;
255 // daemon-wide information
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
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
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
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
281 // args passed to UDP request handler thread as void*
285 struct sockaddr_in cliaddr
;
289 // args passed to TCP request handler thread as void*
292 int sd
; // socket connected to client
293 struct sockaddr_in cliaddr
;
301 // booleans to determine runtime output
302 // read-only after initialization (no mutex protection)
303 static mDNSBool foreground
= 0;
304 static mDNSBool verbose
= 0;
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;
312 // Log messages are delivered to syslog unless -f option specified
315 // common message logging subroutine
316 mDNSlocal
void PrintLog(const char *buffer
)
320 fprintf(stderr
,"%s\n", buffer
);
325 openlog("dnsextd", LOG_CONS
| LOG_PERROR
, LOG_DAEMON
);
326 syslog(LOG_ERR
, "%s", buffer
);
331 // Verbose Logging (conditional on -v option)
332 mDNSlocal
void VLog(const char *format
, ...)
337 if (!verbose
) return;
338 va_start(ptr
,format
);
339 buffer
[mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
)] = 0;
344 // Unconditional Logging
345 mDNSlocal
void Log(const char *format
, ...)
350 va_start(ptr
,format
);
351 buffer
[mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
)] = 0;
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
)
362 snprintf(buf
, sizeof(buf
), "%s: %s - %s", fn
, operation
, strerror(errno
));
367 // Networking Utility Routines
370 // Convert DNS Message Header from Network to Host byte order
371 mDNSlocal
void HdrNToH(PktMsg
*pkt
)
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]);
381 // Convert DNS Message Header from Host to Network byte order
382 mDNSlocal
void HdrHToN(PktMsg
*pkt
)
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
;
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);
401 // create a socket connected to nameserver
402 // caller terminates connection via close()
403 mDNSlocal
int ConnectToServer(DaemonInfo
*d
)
405 struct sockaddr_in servaddr
;
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
);
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; }
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
)
429 n
= send(sd
, (char *)msg
+ nsent
, len
- nsent
, 0);
430 if (n
< 0) { LogErr("MySend", "send"); return -1; }
436 // Transmit a DNS message, prefixed by its length, over TCP, blocking if necessary
437 mDNSlocal
int SendTCPMsg(int sd
, PktMsg
*pkt
)
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;
444 return MySend(sd
, &pkt
->msg
, pkt
->len
);
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
)
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.
456 char *ptr
= (char *)buf
;
459 ssize_t num_read
= recv(sd
, ptr
, remaining
, 0);
460 if ((num_read
== 0) || (num_read
< 0) || (num_read
> remaining
)) return -1;
462 remaining
-= num_read
;
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
472 mDNSlocal PktMsg
*ReadTCPMsg(int sd
, PktMsg
*storage
)
474 int nread
, allocsize
;
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
; }
486 if (msglen
> sizeof(storage
->msg
)) { Log("ReadTCPMsg: provided buffer too small."); goto error
; }
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
));
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
; }
510 //!!!KRS convert to HBO here?
512 if (pkt
&& pkt
!= storage
) free(pkt
);
517 // Dynamic Update Utility Routines
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
)
525 const mDNSu8
*ptr
= NULL
, *end
= (mDNSu8
*)&pkt
->msg
+ pkt
->len
;
526 LargeCacheRecord lcr
;
530 ptr
= LocateAdditionals(&pkt
->msg
, end
);
532 for (i
= 0; i
< pkt
->msg
.h
.numAdditionals
; i
++)
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
)
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
;
549 // check if a request and server response complete a successful dynamic update
550 mDNSlocal mDNSBool
SuccessfulUpdateTransaction(PktMsg
*request
, PktMsg
*reply
)
553 char *vlogmsg
= NULL
;
556 if (!request
|| !reply
) { vlogmsg
= "NULL message"; goto failure
; }
557 if (request
->len
< sizeof(DNSMessageHeader
) || reply
->len
< sizeof(DNSMessageHeader
)) { vlogmsg
= "Malformatted message"; goto failure
; }
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
; }
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
; }
568 VLog("Successful update from %s", inet_ntop(AF_INET
, &request
->src
.sin_addr
, buf
, 32));
572 VLog("Request %s: %s", inet_ntop(AF_INET
, &request
->src
.sin_addr
, buf
, 32), vlogmsg
);
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
579 mDNSlocal CacheRecord
*CopyCacheRecord(const CacheRecord
*orig
, domainname
*name
)
582 size_t size
= sizeof(*cr
);
583 if (orig
->resrec
.rdlength
> InlineCacheRDSize
) size
+= orig
->resrec
.rdlength
- InlineCacheRDSize
;
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
;
595 // Lease Hashtable Utility Routines
598 // double hash table size
599 // caller must lock table prior to invocation
600 mDNSlocal
void RehashTable(DaemonInfo
*d
)
602 RRTableElem
*ptr
, *tmp
, **new;
603 int i
, bucket
, newnbuckets
= d
->nbuckets
* 2;
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
*));
610 for (i
= 0; i
< d
->nbuckets
; i
++)
615 bucket
= ptr
->rr
.resrec
.namehash
% newnbuckets
;
618 tmp
->next
= new[bucket
];
622 d
->nbuckets
= newnbuckets
;
627 // print entire contents of hashtable, invoked via SIGINFO
628 mDNSlocal
void PrintLeaseTable(DaemonInfo
*d
)
632 char rrbuf
[80], addrbuf
[16];
636 if (gettimeofday(&now
, NULL
)) { LogErr("PrintTable", "gettimeofday"); return; }
637 if (pthread_mutex_lock(&d
->tablelock
)) { LogErr("PrintTable", "pthread_mutex_lock"); return; }
639 Log("Dumping Lease Table Contents (table contains %d resource records)", d
->nelems
);
640 for (i
= 0; i
< d
->nbuckets
; i
++)
642 for (ptr
= d
->table
[i
]; ptr
; ptr
= ptr
->next
)
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
));
651 pthread_mutex_unlock(&d
->tablelock
);
655 // Startup SRV Registration Routines
656 // Register _dns-update._udp/_tcp.<zone> SRV records indicating the port on which
657 // the daemon accepts requests
660 // delete all RRS of a given name/type
661 mDNSlocal mDNSu8
*putRRSetDeletion(DNSMessage
*msg
, mDNSu8
*ptr
, mDNSu8
*limit
, ResourceRecord
*rr
)
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
++;
674 mDNSlocal mDNSu8
*PutUpdateSRV(DaemonInfo
*d
, PktMsg
*pkt
, mDNSu8
*ptr
, char *regtype
, mDNSBool registration
)
677 char hostname
[1024], buf
[80];
678 mDNSu8
*end
= (mDNSu8
*)&pkt
->msg
+ sizeof(DNSMessage
);
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';
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
);
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
)
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
;
712 // Initialize message
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
;
718 // format message body
719 ptr
= putZone(&pkt
.msg
, ptr
, end
, &d
->zone
, mDNSOpaque16fromIntVal(kDNSClass_IN
));
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
;
726 nAdditHBO
= pkt
.msg
.h
.numAdditionals
;
730 ptr
= DNSDigest_SignMessage(&pkt
.msg
, &ptr
, &nAdditHBO
, d
->AuthInfo
);
733 pkt
.len
= ptr
- (mDNSu8
*)&pkt
.msg
;
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
);
745 if (!ptr
) { Log("UpdateSRV: Error constructing lease expiration update"); }
746 if (sd
>= 0) close(sd
);
747 if (reply
) free(reply
);
751 // wrapper routines/macros
752 #define ClearUpdateSRV(d) UpdateSRV(d, 0)
754 // clear any existing records prior to registration
755 mDNSlocal
int SetUpdateSRV(DaemonInfo
*d
)
759 err
= ClearUpdateSRV(d
); // clear any existing record
760 if (!err
) err
= UpdateSRV(d
, 1);
765 // Argument Parsing and Configuration
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
)
772 uDNS_AuthInfo
*auth
= NULL
;
773 unsigned char keybuf
[512];
776 auth
= malloc(sizeof(*auth
));
777 if (!auth
) { perror("ReadAuthKey, malloc"); goto error
; }
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);
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
);
790 if (auth
) free(auth
);
794 mDNSlocal
int SetPort(DaemonInfo
*d
, char *PortAsString
)
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
804 mDNSlocal
void PrintUsage(void)
806 fprintf(stderr
, "Usage: dnsextd -z <zone> [-vf] [ -s server ] [-k keyname secret] ...\n"
807 "Use \"dnsextd -h\" for help\n");
810 mDNSlocal
void PrintHelp(void)
812 fprintf(stderr
, "\n\n");
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"
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"
825 "The options are as follows:\n\n"
827 "-f Run daemon in foreground.\n\n"
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"
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"
841 "-v Verbose output.\n\n"
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
)
852 if (argc
< 2) goto arg_error
;
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)
859 case 'p': if (SetPort(d
, optarg
) < 0) goto arg_error
;
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
;
867 case 'k': if (ReadAuthKey(argc
, argv
, d
) < 0) goto arg_error
;
869 case 'z': if (!MakeDomainNameFromDNSNameString(&d
->zone
, optarg
))
871 fprintf(stderr
, "Bad zone %s", optarg
);
875 default: goto arg_error
;
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
890 // Initialization Routines
893 // Allocate memory, initialize locks and bookkeeping variables
894 mDNSlocal
int InitLeaseTable(DaemonInfo
*d
)
896 if (pthread_mutex_init(&d
->tablelock
, NULL
)) { LogErr("InitLeaseTable", "pthread_mutex_init"); return -1; }
897 d
->nbuckets
= LEASETABLE_INIT_NBUCKETS
;
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
);
904 mDNSlocal
int SetupSockets(DaemonInfo
*daemon
)
906 struct sockaddr_in daddr
;
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
);
914 if (daemon
->port
.NotAnInteger
) daddr
.sin_port
= daemon
->port
.NotAnInteger
;
915 else daddr
.sin_port
= htons(DAEMON_PORT
);
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; }
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; }
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];
934 // periodic table updates
937 // Delete a resource record from the nameserver via a dynamic update
938 mDNSlocal
void DeleteRecord(DaemonInfo
*d
, CacheRecord
*rr
, domainname
*zone
)
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
947 PktMsg
*reply
= NULL
;
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
; }
954 InitializeDNSMessage(&pkt
.msg
.h
, id
, UpdateReqFlags
);
956 ptr
= putZone(&pkt
.msg
, ptr
, end
, zone
, mDNSOpaque16fromIntVal(rr
->resrec
.rrclass
));
958 ptr
= putDeletionRecord(&pkt
.msg
, ptr
, &rr
->resrec
);
961 nAdditHBO
= pkt
.msg
.h
.numAdditionals
;
966 ptr
= DNSDigest_SignMessage(&pkt
.msg
, &ptr
, &nAdditHBO
, d
->AuthInfo
);
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
);
979 if (!ptr
) { Log("DeleteRecord: Error constructing lease expiration update"); }
980 if (sd
>= 0) close(sd
);
981 if (reply
) free(reply
);
984 // iterate over table, deleting expired records (or all records if DeleteAll is true)
985 mDNSlocal
void DeleteRecords(DaemonInfo
*d
, mDNSBool DeleteAll
)
988 RRTableElem
**ptr
, *fptr
;
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
++)
998 if (DeleteAll
|| (*ptr
)->expire
- now
.tv_sec
< 0)
1000 // delete record from server
1001 DeleteRecord(d
, &(*ptr
)->rr
, &(*ptr
)->zone
);
1003 *ptr
= (*ptr
)->next
;
1007 else ptr
= &(*ptr
)->next
;
1010 pthread_mutex_unlock(&d
->tablelock
);
1014 // main update request handling
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
)
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
;
1029 if (pthread_mutex_lock(&d
->tablelock
)) { LogErr("UpdateLeaseTable", "pthread_mutex_lock"); return; }
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
; }
1038 for (i
= 0; i
< pkt
->msg
.h
.mDNS_numUpdates
; i
++)
1040 mDNSBool DeleteAllRRSets
= mDNSfalse
, DeleteOneRRSet
= mDNSfalse
, DeleteOneRR
= mDNSfalse
;
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
];
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
;
1055 if (DeleteAllRRSets
|| DeleteOneRRSet
|| DeleteOneRR
)
1059 if (SameDomainName((*rptr
)->rr
.resrec
.name
, rr
->name
) &&
1061 (DeleteOneRRSet
&& (*rptr
)->rr
.resrec
.rrtype
== rr
->rrtype
) ||
1062 (DeleteOneRR
&& SameResourceRecord(&(*rptr
)->rr
.resrec
, rr
))))
1065 VLog("Received deletion update for %s", GetRRDisplayString_rdb(&tmp
->rr
.resrec
, &tmp
->rr
.resrec
.rdata
->u
, buf
));
1066 *rptr
= (*rptr
)->next
;
1070 else rptr
= &(*rptr
)->next
;
1075 // see if add or refresh
1076 while (*rptr
&& !SameResourceRecord(&(*rptr
)->rr
.resrec
, rr
)) rptr
= &(*rptr
)->next
;
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
));
1086 // New record - add to table
1087 if (d
->nelems
> d
->nbuckets
)
1090 bucket
= rr
->namehash
% d
->nbuckets
;
1091 rptr
= &d
->table
[bucket
];
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
;
1108 VLog("Adding update for %s to lease table", GetRRDisplayString_rdb(&lcr
.r
.resrec
, &lcr
.r
.resrec
.rdata
->u
, buf
));
1114 pthread_mutex_unlock(&d
->tablelock
);
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
)
1127 reply
= malloc(sizeof(*reply
));
1128 if (!reply
) { LogErr("FormatLeaseReply", "malloc"); return NULL
; }
1129 flags
.b
[0] = kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
;
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
;
1143 // pkt is thread-local, not requiring locking
1144 mDNSlocal PktMsg
*HandleRequest(PktMsg
*pkt
, DaemonInfo
*d
)
1147 PktMsg
*reply
= NULL
, *LeaseReply
;
1151 // send msg to server, read reply
1152 sd
= ConnectToServer(d
);
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
);
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
);
1166 LeaseReply
= FormatLeaseReply(d
, reply
, lease
);
1167 if (!LeaseReply
) Log("HandleRequest - unable to format lease reply");
1172 if (sd
>= 0) close(sd
);
1178 // LLQ Support Routines
1181 // Set fields of an LLQ Opt Resource Record
1182 mDNSlocal
void FormatLLQOpt(AuthRecord
*opt
, int opcode
, mDNSu8
*id
, mDNSs32 lease
)
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
;
1197 // Calculate effective remaining lease of an LLQ
1198 mDNSlocal mDNSu32
LLQLease(LLQEntry
*e
)
1202 gettimeofday(&t
, NULL
);
1203 if (e
->expire
< t
.tv_sec
) return 0;
1204 else return e
->expire
- t
.tv_sec
;
1207 mDNSlocal
void DeleteLLQ(DaemonInfo
*d
, LLQEntry
*e
)
1209 int bucket
= DomainNameHashValue(&e
->qname
) % LLQ_TABLESIZE
;
1210 LLQEntry
**ptr
= &d
->LLQTable
[bucket
];
1211 AnswerListElem
*a
= e
->AnswerList
;
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
);
1217 // free shared answer structure if ref count drops to zero
1218 if (a
&& !(--a
->refcount
))
1220 CacheRecord
*cr
= a
->KnownAnswers
, *tmp
;
1221 AnswerListElem
**tbl
= &d
->AnswerTable
[bucket
];
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");
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
;
1242 mDNSlocal
int SendLLQ(DaemonInfo
*d
, PktMsg
*pkt
, struct sockaddr_in dst
)
1248 if (sendto(d
->udpsd
, &pkt
->msg
, pkt
->len
, 0, (struct sockaddr
*)&dst
, sizeof(dst
)) != (int)pkt
->len
)
1250 LogErr("DaemonInfo", "sendto");
1251 Log("Could not send response to client %s", inet_ntop(AF_INET
, &dst
.sin_addr
, addr
, 32));
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
)
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
;
1271 mDNSBool CloseSDOnExit
= sd
< 0;
1273 VLog("Querying server for %##s type %d", e
->name
.c
, e
->type
);
1275 flags
.b
[0] |= kDNSFlag0_RD
; // recursion desired
1276 id
.NotAnInteger
= 0;
1277 InitializeDNSMessage(&q
.msg
.h
, id
, flags
);
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
);
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
);
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
; }
1293 end
= (mDNSu8
*)&reply
->msg
+ reply
->len
;
1294 ansptr
= LocateAnswers(&reply
->msg
, end
);
1295 if (!ansptr
) { Log("Error: AnswerQuestion - LocateAnswers returned NULL"); goto end
; }
1297 for (i
= 0; i
< reply
->msg
.h
.numAnswers
; i
++)
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
))
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
);
1308 CacheRecord
*cr
= CopyCacheRecord(&lcr
.r
, &e
->name
);
1309 if (!cr
) { Log("Error: AnswerQuestion - CopyCacheRecord returned NULL"); goto end
; }
1310 cr
->next
= AnswerList
;
1316 if (sd
> -1 && CloseSDOnExit
) close(sd
);
1317 if (reply
) free(reply
);
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
)
1324 CacheRecord
*cr
, *NewAnswers
, **na
, **ka
; // "new answer", "known answer"
1326 // get up to date answers
1327 NewAnswers
= AnswerQuestion(d
, a
, sd
);
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
1333 // second pass - mark answers pre-existent
1334 for (ka
= &a
->KnownAnswers
; *ka
; ka
= &(*ka
)->next
)
1336 for (na
= &NewAnswers
; *na
; na
= &(*na
)->next
)
1338 if (SameResourceRecord(&(*ka
)->resrec
, &(*na
)->resrec
))
1339 { (*ka
)->resrec
.rroriginalttl
= 0; break; } // 0 means no change
1343 // third pass - add new records to Event list
1347 for (ka
= &a
->KnownAnswers
; *ka
; ka
= &(*ka
)->next
)
1348 if (SameResourceRecord(&(*ka
)->resrec
, &(*na
)->resrec
)) break;
1351 // answer is not in list - splice from NewAnswers list, add to Event list
1353 *na
= (*na
)->next
; // splice from list
1354 cr
->next
= a
->EventList
; // add spliced record to event list
1356 cr
->resrec
.rroriginalttl
= 1; // 1 means add
1358 else na
= &(*na
)->next
;
1361 // move all the removes from the answer list to the event list
1362 ka
= &a
->KnownAnswers
;
1365 if ((*ka
)->resrec
.rroriginalttl
== (unsigned)-1)
1369 cr
->next
= a
->EventList
;
1372 else ka
= &(*ka
)->next
;
1375 // lastly, free the remaining records (known answers) in NewAnswers list
1379 NewAnswers
= NewAnswers
->next
;
1384 mDNSlocal
void SendEvents(DaemonInfo
*d
, LLQEntry
*e
)
1388 mDNSu8
*end
= (mDNSu8
*)&response
.msg
.data
;
1390 char rrbuf
[80], addrbuf
[32];
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; }
1399 // put adds/removes in packet
1400 for (cr
= e
->AnswerList
->EventList
; cr
; cr
= cr
->next
)
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; }
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; }
1412 response
.len
= (int)(end
- (mDNSu8
*)&response
.msg
);
1413 if (SendLLQ(d
, &response
, e
->cli
) < 0) LogMsg("Error: SendEvents - SendLLQ");
1416 mDNSlocal
void PrintLLQAnswers(DaemonInfo
*d
)
1421 Log("Printing LLQ Answer Table contents");
1423 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1425 AnswerListElem
*a
= d
->AnswerTable
[i
];
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
));
1438 mDNSlocal
void PrintLLQTable(DaemonInfo
*d
)
1444 Log("Printing LLQ table contents");
1446 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1455 case RequestReceived
: state
= "RequestReceived"; break;
1456 case ChallengeSent
: state
= "ChallengeSent"; break;
1457 case Established
: state
= "Established"; break;
1458 default: state
= "unknown";
1460 inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addr
, 32);
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
);
1469 // Send events to clients as a result of a change in the zone
1470 mDNSlocal
void GenLLQEvents(DaemonInfo
*d
)
1476 VLog("Generating LLQ Events");
1478 gettimeofday(&t
, NULL
);
1479 sd
= ConnectToServer(d
);
1480 if (sd
< 0) { Log("GenLLQEvents: ConnectToServer failed"); return; }
1482 // get all answers up to date
1483 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1485 AnswerListElem
*a
= d
->AnswerTable
[i
];
1488 UpdateAnswerList(d
, a
, sd
);
1493 // for each established LLQ, send events
1494 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1496 e
= &d
->LLQTable
[i
];
1499 if ((*e
)->expire
< t
.tv_sec
) DeleteLLQ(d
, *e
);
1502 if ((*e
)->state
== Established
&& (*e
)->AnswerList
->EventList
) SendEvents(d
, *e
);
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
++)
1511 AnswerListElem
*a
= d
->AnswerTable
[i
];
1516 CacheRecord
*cr
= a
->EventList
, *tmp
;
1521 if ((signed)tmp
->resrec
.rroriginalttl
< 0) free(tmp
);
1524 tmp
->next
= a
->KnownAnswers
;
1525 a
->KnownAnswers
= tmp
;
1526 tmp
->resrec
.rroriginalttl
= 0;
1529 a
->EventList
= NULL
;
1538 // Monitor zone for changes that may produce LLQ events
1539 mDNSlocal
void *LLQEventMonitor(void *DInfoPtr
)
1541 DaemonInfo
*d
= DInfoPtr
;
1543 mDNSu8
*end
= q
.msg
.data
;
1545 mDNSOpaque16 id
, flags
= QueryFlags
;
1548 mDNSBool SerialInitialized
= mDNSfalse
;
1550 LargeCacheRecord lcr
;
1551 ResourceRecord
*rr
= &lcr
.r
.resrec
;
1552 int i
, sleeptime
= 0;
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
);
1564 sd
= ConnectToServer(d
);
1565 if (sd
< 0) { Log("LLQEventMonitor: ConnectToServer failed"); return NULL
; }
1570 sleeptime
= LLQ_MONITOR_ERR_INTERVAL
; // if we bail on error below, rate limit retry
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; }
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
++)
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
)
1588 // first time through loop
1589 SerialInitialized
= mDNStrue
;
1590 serial
= rr
->rdata
->u
.soa
.serial
;
1591 sleeptime
= LLQ_MONITOR_INTERVAL
;
1594 else if (rr
->rdata
->u
.soa
.serial
!= serial
)
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; }
1602 sleeptime
= LLQ_MONITOR_INTERVAL
;
1605 if (!ptr
) Log("LLQEventMonitor: response to query did not contain SOA");
1609 mDNSlocal
void SetAnswerList(DaemonInfo
*d
, LLQEntry
*e
)
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
;
1616 a
= malloc(sizeof(*a
));
1617 if (!a
) { LogErr("SetAnswerList", "malloc"); return; }
1618 AssignDomainName(&a
->name
, &e
->qname
);
1621 a
->KnownAnswers
= NULL
;
1622 a
->EventList
= NULL
;
1623 a
->next
= d
->AnswerTable
[bucket
];
1624 d
->AnswerTable
[bucket
] = a
;
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
;
1636 // Allocate LLQ entry, insert into table
1637 mDNSlocal LLQEntry
*NewLLQ(DaemonInfo
*d
, struct sockaddr_in cli
, domainname
*qname
, mDNSu16 qtype
, mDNSu32 lease
)
1641 int bucket
= DomainNameHashValue(qname
) % LLQ_TABLESIZE
;
1644 e
= malloc(sizeof(*e
));
1645 if (!e
) { LogErr("NewLLQ", "malloc"); return NULL
; }
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
);
1650 // initialize structure
1652 AssignDomainName(&e
->qname
, qname
);
1654 memset(e
->id
, 0, 8);
1655 e
->state
= RequestReceived
;
1656 e
->AnswerList
= NULL
;
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
;
1665 e
->next
= d
->LLQTable
[bucket
];
1666 d
->LLQTable
[bucket
] = e
;
1671 // Handle a refresh request from client
1672 mDNSlocal
void LLQRefresh(DaemonInfo
*d
, LLQEntry
*e
, LLQOptData
*llq
, mDNSOpaque16 msgID
)
1676 mDNSu8
*end
= (mDNSu8
*)&ack
.msg
.data
;
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
);
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
;
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; }
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; }
1697 ack
.len
= (int)(end
- (mDNSu8
*)&ack
.msg
);
1698 if (SendLLQ(d
, &ack
, e
->cli
)) Log("Error: LLQRefresh");
1700 if (llq
->lease
) e
->state
= Established
;
1701 else DeleteLLQ(d
, e
);
1704 // Complete handshake with Ack an initial answers
1705 mDNSlocal
void LLQCompleteHandshake(DaemonInfo
*d
, LLQEntry
*e
, LLQOptData
*llq
, mDNSOpaque16 msgID
)
1711 mDNSu8
*end
= (mDNSu8
*)&ack
.msg
.data
;
1712 char rrbuf
[80], addrbuf
[32];
1714 inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addr
, 32);
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; }
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
);
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; }
1733 if (e
->state
!= Established
) { SetAnswerList(d
, e
); e
->state
= Established
; }
1735 if (verbose
) inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addrbuf
, 32);
1736 for (ptr
= e
->AnswerList
->KnownAnswers
; ptr
; ptr
= ptr
->next
)
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; }
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; }
1748 ack
.len
= (int)(end
- (mDNSu8
*)&ack
.msg
);
1749 if (SendLLQ(d
, &ack
, e
->cli
)) Log("Error: LLQCompleteHandshake");
1752 mDNSlocal
void LLQSetupChallenge(DaemonInfo
*d
, LLQEntry
*e
, LLQOptData
*llq
, mDNSOpaque16 msgID
)
1757 mDNSu8
*end
= challenge
.msg
.data
;
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
);
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
1766 if (ZERO_LLQID(e
->id
)) // don't regenerate random ID for retransmissions
1768 // construct ID <time><random>
1769 gettimeofday(&t
, NULL
);
1771 memcpy(e
->id
, &t
.tv_sec
, sizeof(t
.tv_sec
));
1772 memcpy(e
->id
+ sizeof(t
.tv_sec
), &randval
, sizeof(randval
));
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
;
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
)
1793 case RequestReceived
:
1794 LLQSetupChallenge(d
, e
, llq
, msgID
);
1797 if (ZERO_LLQID(llq
->id
)) LLQSetupChallenge(d
, e
, llq
, msgID
); // challenge sent and lost
1798 else LLQCompleteHandshake(d
, e
, llq
, msgID
);
1801 if (ZERO_LLQID(llq
->id
))
1803 // client started over. reset state.
1804 LLQEntry
*newe
= NewLLQ(d
, e
->cli
, &e
->qname
, e
->qtype
, llq
->lease
);
1807 LLQSetupChallenge(d
, newe
, llq
, msgID
);
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; }
1818 mDNSlocal LLQEntry
*LookupLLQ(DaemonInfo
*d
, struct sockaddr_in cli
, domainname
*qname
, mDNSu16 qtype
, mDNSu8
*id
)
1820 int bucket
= bucket
= DomainNameHashValue(qname
) % LLQ_TABLESIZE
;
1821 LLQEntry
*ptr
= d
->LLQTable
[bucket
];
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
1834 mDNSlocal
int RecvLLQ(DaemonInfo
*d
, PktMsg
*pkt
)
1837 LargeCacheRecord opt
;
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
;
1847 inet_ntop(AF_INET
, &pkt
->src
.sin_addr
, addr
, 32);
1849 VLog("Received LLQ msg from %s", addr
);
1850 // sanity-check packet
1851 if (!pkt
->msg
.h
.numQuestions
|| !pkt
->msg
.h
.numAdditionals
)
1853 Log("Malformatted LLQ from %s with %d questions, %d additionals", addr
, pkt
->msg
.h
.numQuestions
, pkt
->msg
.h
.numAdditionals
);
1857 // find the OPT RR - must be last in message
1858 for (i
= 0; i
< pkt
->msg
.h
.numAdditionals
; i
++)
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
; }
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
); }
1868 // dispatch each question
1869 for (i
= 0; i
< pkt
->msg
.h
.numQuestions
; i
++)
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
; }
1876 e
= LookupLLQ(d
, pkt
->src
, &q
.qname
, q
.qtype
, llq
->id
);
1879 // no entry - if zero ID, create new
1880 e
= NewLLQ(d
, pkt
->src
, &q
.qname
, q
.qtype
, llq
->lease
);
1883 UpdateLLQ(d
, e
, llq
, pkt
->msg
.h
.id
);
1892 mDNSlocal mDNSBool
IsLLQRequest(PktMsg
*pkt
)
1894 const mDNSu8
*ptr
= NULL
, *end
= (mDNSu8
*)&pkt
->msg
+ pkt
->len
;
1895 LargeCacheRecord lcr
;
1897 mDNSBool result
= mDNSfalse
;
1900 if ((mDNSu8
)(pkt
->msg
.h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
) != (mDNSu8
)(kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
)) goto end
;
1902 if (!pkt
->msg
.h
.numAdditionals
) goto end
;
1903 ptr
= LocateAdditionals(&pkt
->msg
, end
);
1906 // find last Additional
1907 for (i
= 0; i
< pkt
->msg
.h
.numAdditionals
; i
++)
1909 ptr
= GetLargeResourceRecord(NULL
, &pkt
->msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &lcr
);
1910 if (!ptr
) { Log("Unable to read additional record"); goto end
; }
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
; }
1923 // !!!KRS implement properly
1924 mDNSlocal mDNSBool
IsLLQAck(PktMsg
*pkt
)
1926 if (pkt
->msg
.h
.flags
.NotAnInteger
== ResponseFlags
.NotAnInteger
&&
1927 pkt
->msg
.h
.numQuestions
&& !pkt
->msg
.h
.numAnswers
&& !pkt
->msg
.h
.numAuthorities
) return mDNStrue
;
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
)
1937 UDPRequestArgs
*req
= vptr
;
1938 PktMsg
*reply
= NULL
;
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
);
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");
1949 if (reply
) free(reply
);
1954 //!!!KRS this needs to be changed to use non-blocking sockets
1955 mDNSlocal
int RecvUDPRequest(int sd
, DaemonInfo
*d
)
1957 UDPRequestArgs
*req
;
1959 unsigned int clisize
= sizeof(req
->cliaddr
);
1961 req
= malloc(sizeof(UDPRequestArgs
));
1962 if (!req
) { LogErr("RecvUDPRequest", "malloc"); return -1; }
1963 bzero(req
, sizeof(*req
));
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
;
1970 if (IsLLQRequest(&req
->pkt
))
1972 // LLQ messages handled by main thread
1973 int err
= RecvLLQ(d
, &req
->pkt
);
1978 if (IsLLQAck(&req
->pkt
)) { free(req
); return 0; } // !!!KRS need to do acks + retrans
1980 if (pthread_create(&tid
, NULL
, UDPUpdateRequestForkFn
, req
)) { LogErr("RecvUDPRequest", "pthread_create"); free(req
); return -1; }
1981 pthread_detach(tid
);
1985 mDNSlocal
void *TCPRequestForkFn(void *vptr
)
1987 TCPRequestArgs
*req
= vptr
;
1988 PktMsg
*in
= NULL
, *out
= NULL
;
1991 //!!!KRS if this read blocks indefinitely, we can run out of threads
1993 in
= ReadTCPMsg(req
->sd
, NULL
);
1996 LogMsg("TCPRequestForkFn: Could not read message from %s", inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
2000 VLog("Received TCP request: %d bytes from %s", in
->len
, inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
2002 out
= HandleRequest(in
, req
->d
);
2005 LogMsg("TCPRequestForkFn: No reply for client %s", inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
2009 // deliver reply to client
2010 if (SendTCPMsg(req
->sd
, out
) < 0)
2012 LogMsg("TCPRequestForkFn: Unable to send reply to client %s", inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
2023 mDNSlocal
int RecvTCPRequest(int sd
, DaemonInfo
*d
)
2025 TCPRequestArgs
*req
;
2027 unsigned int clilen
= sizeof(req
->cliaddr
);
2029 req
= malloc(sizeof(TCPRequestArgs
));
2030 if (!req
) { LogErr("RecvTCPRequest", "malloc"); return -1; }
2031 bzero(req
, sizeof(*req
));
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
);
2042 // listen for incoming requests, periodically check table for expired records, respond to signals
2043 mDNSlocal
int ListenForUpdates(DaemonInfo
*d
)
2048 struct timeval timenow
, timeout
= { 0, 0 };
2049 long NextTableCheck
= 0;
2051 VLog("Listening for requests...");
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;
2060 // expire records if necessary, set timeout
2061 if (gettimeofday(&timenow
, NULL
)) { LogErr("ListenForUpdates", "gettimeofday"); return -1; }
2062 if (timenow
.tv_sec
>= NextTableCheck
)
2064 DeleteRecords(d
, mDNSfalse
);
2065 NextTableCheck
= timenow
.tv_sec
+ EXPIRATION_INTERVAL
;
2067 timeout
.tv_sec
= NextTableCheck
- timenow
.tv_sec
;
2069 FD_SET(d
->tcpsd
, &rset
);
2070 FD_SET(d
->udpsd
, &rset
);
2071 FD_SET(d
->LLQEventListenSock
, &rset
);
2073 err
= select(maxfdp1
, &rset
, NULL
, NULL
, &timeout
);
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");
2082 else { LogErr("ListenForUpdates", "select"); return -1; }
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
))
2090 // clear signalling data off socket
2092 recv(d
->LLQEventListenSock
, buf
, 32, 0);
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
)
2104 if (sig
== SIGTERM
|| sig
== SIGINT
) { terminate
= 1; return; }
2105 if (sig
== INFO_SIGNAL
) { dumptable
= 1; return; }
2108 int main(int argc
, char *argv
[])
2113 d
= malloc(sizeof(*d
));
2114 if (!d
) { LogErr("main", "malloc"); exit(1); }
2115 bzero(d
, sizeof(DaemonInfo
));
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");
2122 if (ProcessArgs(argc
, argv
, d
) < 0) exit(1);
2128 LogErr("main", "daemon");
2129 fprintf(stderr
, "Could not daemonize process, running in foreground");
2134 if (InitLeaseTable(d
) < 0) exit(1);
2135 if (SetupSockets(d
) < 0) exit(1);
2136 if (SetUpdateSRV(d
) < 0) exit(1);
2138 if (pthread_create(&LLQtid
, NULL
, LLQEventMonitor
, d
)) { LogErr("main", "pthread_create"); }
2141 pthread_detach(LLQtid
);
2142 ListenForUpdates(d
);
2145 if (ClearUpdateSRV(d
) < 0) exit(1); // clear update srv's even if ListenForUpdates or pthread_create returns an error