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.41 2005/09/24 01:10:54 cheshire
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
34 Revision 1.39 2005/08/22 23:30:30 ksekar
35 <rdar://problem/4158123> memory leak in dnsextd.c
37 Revision 1.38 2005/06/29 10:03:56 cheshire
38 Fix compile errors and warnings
40 Revision 1.37 2005/06/27 22:12:17 ksekar
41 <rdar://problem/4163315> dnsextd performance improvements
43 Revision 1.36 2005/06/14 23:14:41 ksekar
44 <rdar://problem/3934233> Unable to refresh LLQs
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
49 Revision 1.34 2005/03/16 18:47:37 ksekar
50 <rdar://problem/4046465> dnsextd doesn't clean up on exit
52 Revision 1.33 2005/03/11 19:09:02 ksekar
53 Fixed ZERO_LLQID macro
55 Revision 1.32 2005/03/10 22:54:33 ksekar
56 <rdar://problem/4046285> dnsextd leaks memory/ports
58 Revision 1.31 2005/02/24 02:37:57 ksekar
59 <rdar://problem/4021977> dnsextd memory management improvements
61 Revision 1.30 2005/01/27 22:57:56 cheshire
62 Fix compile errors on gcc4
64 Revision 1.29 2004/12/22 00:13:50 ksekar
65 <rdar://problem/3873993> Change version, port, and polling interval for LLQ
67 Revision 1.28 2004/12/17 00:30:00 ksekar
68 <rdar://problem/3924045> dnsextd memory leak
70 Revision 1.27 2004/12/17 00:27:32 ksekar
73 Revision 1.26 2004/12/17 00:21:33 ksekar
74 Fixes for new CacheRecord structure with indirect name pointer
76 Revision 1.25 2004/12/16 20:13:02 cheshire
77 <rdar://problem/3324626> Cache memory management improvements
79 Revision 1.24 2004/12/14 17:09:06 ksekar
80 fixed incorrect usage instructions
82 Revision 1.23 2004/12/06 20:24:31 ksekar
83 <rdar://problem/3907303> dnsextd leaks sockets
85 Revision 1.22 2004/12/03 20:20:29 ksekar
86 <rdar://problem/3904149> dnsextd: support delivery of large records via LLQ events
88 Revision 1.21 2004/12/03 06:11:34 ksekar
89 <rdar://problem/3885059> clean up dnsextd arguments
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.
95 Revision 1.19 2004/12/01 01:16:29 cheshire
96 Solaris compatibility fixes
98 Revision 1.18 2004/11/30 23:51:06 cheshire
99 Remove double semicolons
101 Revision 1.17 2004/11/30 22:37:01 cheshire
102 Update copyright dates and add "Mode: C; tab-width: 4" headers
104 Revision 1.16 2004/11/25 02:02:28 ksekar
105 Fixed verbose log message argument
107 Revision 1.15 2004/11/19 02:35:02 ksekar
108 <rdar://problem/3886317> Wide Area Security: Add LLQ-ID to events
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>.
113 Revision 1.13 2004/11/13 02:22:36 ksekar
114 <rdar://problem/3878201> Refresh Acks from daemon malformatted
116 Revision 1.12 2004/11/12 01:05:01 ksekar
117 <rdar://problem/3876757> dnsextd: daemon registers the SRV same record
120 Revision 1.11 2004/11/12 01:03:31 ksekar
121 <rdar://problem/3876776> dnsextd: KnownAnswers (CacheRecords) leaked
123 Revision 1.10 2004/11/12 00:35:28 ksekar
124 <rdar://problem/3876705> dnsextd: uninitialized pointer can cause crash
126 Revision 1.9 2004/11/10 20:38:17 ksekar
127 <rdar://problem/3874168> dnsextd: allow a "fudge" in LLQ lease echo
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.
134 Revision 1.7 2004/10/30 00:06:58 ksekar
135 <rdar://problem/3722535> Support Long Lived Queries in DNS Extension daemon
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.
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
146 Revision 1.4 2004/09/14 23:27:48 cheshire
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
152 Revision 1.2 2004/08/24 23:27:57 cheshire
153 Fixes for Linux compatibility:
156 Don't try to set servaddr.sin_len on platforms that don't have sa_len
158 Revision 1.1 2004/08/11 00:43:26 ksekar
159 <rdar://problem/3722542>: DNS Extension daemon for DNS Update Lease
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
172 #include <sys/types.h>
173 #include <sys/socket.h>
174 #include <netinet/in.h>
175 #include <arpa/inet.h>
179 #include <sys/time.h>
180 #include <sys/resource.h>
184 // Compatibility workaround
186 #define AF_LOCAL AF_UNIX
193 #define LOOPBACK "127.0.0.1"
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
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
208 // LLQ SOA poll interval (microseconds)
209 #define LLQ_MONITOR_ERR_INTERVAL (60 * 1000000)
210 #define LLQ_MONITOR_INTERVAL 250000
212 #define INFO_SIGNAL SIGINFO
214 #define INFO_SIGNAL SIGUSR1
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))
222 // Structs/fields that must be locked for thread safety are explicitly commented
227 struct sockaddr_in src
;
230 // Note: extra storage for oversized (TCP) messages goes here
234 typedef struct RRTableElem
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
251 typedef struct AnswerListElem
253 struct AnswerListElem
*next
;
256 CacheRecord
*KnownAnswers
; // All valid answers delivered to client
257 CacheRecord
*EventList
; // New answers (adds/removes) to be sent to client
259 mDNSBool UseTCP
; // Use TCP if UDP would cause truncation
260 pthread_t tid
; // Allow parallel list updates
264 typedef struct LLQEntry
266 struct LLQEntry
*next
;
267 struct sockaddr_in cli
; // clien'ts source address
272 mDNSu32 lease
; // original lease, in seconds
273 mDNSs32 expire
; // expiration, absolute, in seconds since epoch
274 AnswerListElem
*AnswerList
;
277 // daemon-wide information
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
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
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
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
304 // args passed to UDP request handler thread as void*
308 struct sockaddr_in cliaddr
;
312 // args passed to TCP request handler thread as void*
315 int sd
; // socket connected to client
316 struct sockaddr_in cliaddr
;
320 // args passed to UpdateAnswerList thread as void*
325 } UpdateAnswerListArgs
;
331 // booleans to determine runtime output
332 // read-only after initialization (no mutex protection)
333 static mDNSBool foreground
= 0;
334 static mDNSBool verbose
= 0;
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;
342 // Log messages are delivered to syslog unless -f option specified
345 // common message logging subroutine
346 mDNSlocal
void PrintLog(const char *buffer
)
350 fprintf(stderr
,"%s\n", buffer
);
355 openlog("dnsextd", LOG_CONS
| LOG_PERROR
, LOG_DAEMON
);
356 syslog(LOG_ERR
, "%s", buffer
);
361 // Verbose Logging (conditional on -v option)
362 mDNSlocal
void VLog(const char *format
, ...)
367 if (!verbose
) return;
368 va_start(ptr
,format
);
369 buffer
[mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
)] = 0;
374 // Unconditional Logging
375 mDNSlocal
void Log(const char *format
, ...)
380 va_start(ptr
,format
);
381 buffer
[mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
)] = 0;
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
)
391 char buf
[512], errbuf
[256];
392 strerror_r(errno
, errbuf
, sizeof(errbuf
));
393 snprintf(buf
, sizeof(buf
), "%s: %s - %s", fn
, operation
, errbuf
);
398 // Networking Utility Routines
401 // Convert DNS Message Header from Network to Host byte order
402 mDNSlocal
void HdrNToH(PktMsg
*pkt
)
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]);
412 // Convert DNS Message Header from Host to Network byte order
413 mDNSlocal
void HdrHToN(PktMsg
*pkt
)
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
;
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);
432 // create a socket connected to nameserver
433 // caller terminates connection via close()
434 mDNSlocal
int ConnectToServer(DaemonInfo
*d
)
436 int ntries
= 0, retry
= 0;
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
;
446 LogErr("ConnectToServer", "connect");
447 Log("ConnectToServer - retrying connection");
448 if (!retry
) retry
= 500000 + random() % 500000;
452 else { Log("ConnectToServer - %d failed attempts. Aborting.", ntries
); return -1; }
456 // send an entire block of data over a connected socket
457 mDNSlocal
int MySend(int sd
, const void *msg
, int len
)
459 int selectval
, n
, nsent
= 0;
461 struct timeval timeout
= { 3, 0 }; // until we remove all calls from main thread, keep timeout short
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; }
477 // Transmit a DNS message, prefixed by its length, over TCP, blocking if necessary
478 mDNSlocal
int SendTCPMsg(int sd
, PktMsg
*pkt
)
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;
485 return MySend(sd
, &pkt
->msg
, pkt
->len
);
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
)
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.
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
;
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;
513 remaining
-= num_read
;
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
523 mDNSlocal PktMsg
*ReadTCPMsg(int sd
, PktMsg
*storage
)
525 int nread
, allocsize
;
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
; }
537 if (msglen
> sizeof(storage
->msg
)) { Log("ReadTCPMsg: provided buffer too small."); goto error
; }
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
));
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
; }
561 //!!!KRS convert to HBO here?
563 if (pkt
&& pkt
!= storage
) free(pkt
);
567 mDNSlocal
int UDPServerTransaction(const DaemonInfo
*d
, const PktMsg
*request
, PktMsg
*reply
, mDNSBool
*trunc
)
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);
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
; }
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
;
590 if (sd
>= 0) close(sd
);
595 // Dynamic Update Utility Routines
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
)
603 const mDNSu8
*ptr
= NULL
, *end
= (mDNSu8
*)&pkt
->msg
+ pkt
->len
;
604 LargeCacheRecord lcr
;
608 ptr
= LocateAdditionals(&pkt
->msg
, end
);
610 for (i
= 0; i
< pkt
->msg
.h
.numAdditionals
; i
++)
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
)
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
;
627 // check if a request and server response complete a successful dynamic update
628 mDNSlocal mDNSBool
SuccessfulUpdateTransaction(PktMsg
*request
, PktMsg
*reply
)
631 char *vlogmsg
= NULL
;
634 if (!request
|| !reply
) { vlogmsg
= "NULL message"; goto failure
; }
635 if (request
->len
< sizeof(DNSMessageHeader
) || reply
->len
< sizeof(DNSMessageHeader
)) { vlogmsg
= "Malformatted message"; goto failure
; }
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
; }
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
; }
646 VLog("Successful update from %s", inet_ntop(AF_INET
, &request
->src
.sin_addr
, buf
, 32));
650 VLog("Request %s: %s", inet_ntop(AF_INET
, &request
->src
.sin_addr
, buf
, 32), vlogmsg
);
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
657 mDNSlocal CacheRecord
*CopyCacheRecord(const CacheRecord
*orig
, domainname
*name
)
660 size_t size
= sizeof(*cr
);
661 if (orig
->resrec
.rdlength
> InlineCacheRDSize
) size
+= orig
->resrec
.rdlength
- InlineCacheRDSize
;
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
;
673 // Lease Hashtable Utility Routines
676 // double hash table size
677 // caller must lock table prior to invocation
678 mDNSlocal
void RehashTable(DaemonInfo
*d
)
680 RRTableElem
*ptr
, *tmp
, **new;
681 int i
, bucket
, newnbuckets
= d
->nbuckets
* 2;
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
*));
688 for (i
= 0; i
< d
->nbuckets
; i
++)
693 bucket
= ptr
->rr
.resrec
.namehash
% newnbuckets
;
696 tmp
->next
= new[bucket
];
700 d
->nbuckets
= newnbuckets
;
705 // print entire contents of hashtable, invoked via SIGINFO
706 mDNSlocal
void PrintLeaseTable(DaemonInfo
*d
)
710 char rrbuf
[80], addrbuf
[16];
714 if (gettimeofday(&now
, NULL
)) { LogErr("PrintTable", "gettimeofday"); return; }
715 if (pthread_mutex_lock(&d
->tablelock
)) { LogErr("PrintTable", "pthread_mutex_lock"); return; }
717 Log("Dumping Lease Table Contents (table contains %d resource records)", d
->nelems
);
718 for (i
= 0; i
< d
->nbuckets
; i
++)
720 for (ptr
= d
->table
[i
]; ptr
; ptr
= ptr
->next
)
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
));
729 pthread_mutex_unlock(&d
->tablelock
);
733 // Startup SRV Registration Routines
734 // Register _dns-update._udp/_tcp.<zone> SRV records indicating the port on which
735 // the daemon accepts requests
738 // delete all RRS of a given name/type
739 mDNSlocal mDNSu8
*putRRSetDeletion(DNSMessage
*msg
, mDNSu8
*ptr
, mDNSu8
*limit
, ResourceRecord
*rr
)
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
++;
752 mDNSlocal mDNSu8
*PutUpdateSRV(DaemonInfo
*d
, PktMsg
*pkt
, mDNSu8
*ptr
, char *regtype
, mDNSBool registration
)
755 char hostname
[1024], buf
[80];
756 mDNSu8
*end
= (mDNSu8
*)&pkt
->msg
+ sizeof(DNSMessage
);
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';
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
);
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
)
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
;
790 // Initialize message
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
;
796 // format message body
797 ptr
= putZone(&pkt
.msg
, ptr
, end
, &d
->zone
, mDNSOpaque16fromIntVal(kDNSClass_IN
));
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
;
804 nAdditHBO
= pkt
.msg
.h
.numAdditionals
;
808 ptr
= DNSDigest_SignMessage(&pkt
.msg
, &ptr
, &nAdditHBO
, d
->AuthInfo
);
811 pkt
.len
= ptr
- (mDNSu8
*)&pkt
.msg
;
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
);
823 if (!ptr
) { Log("UpdateSRV: Error constructing lease expiration update"); }
824 if (sd
>= 0) close(sd
);
825 if (reply
) free(reply
);
829 // wrapper routines/macros
830 #define ClearUpdateSRV(d) UpdateSRV(d, 0)
832 // clear any existing records prior to registration
833 mDNSlocal
int SetUpdateSRV(DaemonInfo
*d
)
837 err
= ClearUpdateSRV(d
); // clear any existing record
838 if (!err
) err
= UpdateSRV(d
, 1);
843 // Argument Parsing and Configuration
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
)
850 uDNS_AuthInfo
*auth
= NULL
;
851 unsigned char keybuf
[512];
854 auth
= malloc(sizeof(*auth
));
855 if (!auth
) { perror("ReadAuthKey, malloc"); goto error
; }
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);
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
);
868 if (auth
) free(auth
);
872 mDNSlocal
int SetPort(DaemonInfo
*d
, char *PortAsString
)
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
882 mDNSlocal
void PrintUsage(void)
884 fprintf(stderr
, "Usage: dnsextd -z <zone> [-vf] [ -s server ] [-k keyname secret] ...\n"
885 "Use \"dnsextd -h\" for help\n");
888 mDNSlocal
void PrintHelp(void)
890 fprintf(stderr
, "\n\n");
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"
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"
903 "The options are as follows:\n\n"
905 "-f Run daemon in foreground.\n\n"
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"
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"
919 "-v Verbose output.\n\n"
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
)
929 struct in_addr server
;
931 if (argc
< 2) goto arg_error
;
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
937 while ((opt
= getopt(argc
, argv
, "z:p:hfvs:k:")) != -1)
941 case 'p': if (SetPort(d
, optarg
) < 0) goto arg_error
;
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
;
949 case 'k': if (ReadAuthKey(argc
, argv
, d
) < 0) goto arg_error
;
951 case 'z': if (!MakeDomainNameFromDNSNameString(&d
->zone
, optarg
))
953 fprintf(stderr
, "Bad zone %s", optarg
);
957 default: goto arg_error
;
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
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
);
982 // Initialization Routines
985 // Allocate memory, initialize locks and bookkeeping variables
986 mDNSlocal
int InitLeaseTable(DaemonInfo
*d
)
988 if (pthread_mutex_init(&d
->tablelock
, NULL
)) { LogErr("InitLeaseTable", "pthread_mutex_init"); return -1; }
989 d
->nbuckets
= LEASETABLE_INIT_NBUCKETS
;
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
);
996 mDNSlocal
int SetupSockets(DaemonInfo
*daemon
)
998 struct sockaddr_in daddr
;
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
);
1006 if (daemon
->port
.NotAnInteger
) daddr
.sin_port
= daemon
->port
.NotAnInteger
;
1007 else daddr
.sin_port
= htons(DAEMON_PORT
);
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; }
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; }
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];
1026 // periodic table updates
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
)
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
1039 PktMsg
*reply
= NULL
;
1041 VLog("Expiring record %s", GetRRDisplayString_rdb(&rr
->resrec
, &rr
->resrec
.rdata
->u
, buf
));
1043 id
.NotAnInteger
= 0;
1044 InitializeDNSMessage(&pkt
.msg
.h
, id
, UpdateReqFlags
);
1046 ptr
= putZone(&pkt
.msg
, ptr
, end
, zone
, mDNSOpaque16fromIntVal(rr
->resrec
.rrclass
));
1048 ptr
= putDeletionRecord(&pkt
.msg
, ptr
, &rr
->resrec
);
1051 nAdditHBO
= pkt
.msg
.h
.numAdditionals
;
1056 ptr
= DNSDigest_SignMessage(&pkt
.msg
, &ptr
, &nAdditHBO
, d
->AuthInfo
);
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);
1069 if (!ptr
) { Log("DeleteOneRecord: Error constructing lease expiration update"); }
1070 if (reply
) free(reply
);
1073 // iterate over table, deleting expired records (or all records if DeleteAll is true)
1074 mDNSlocal
void DeleteRecords(DaemonInfo
*d
, mDNSBool DeleteAll
)
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; }
1082 for (i
= 0; i
< d
->nbuckets
; i
++)
1084 RRTableElem
**ptr
= &d
->table
[i
];
1087 if (DeleteAll
|| (*ptr
)->expire
- now
.tv_sec
< 0)
1090 // delete record from server
1091 DeleteOneRecord(d
, &(*ptr
)->rr
, &(*ptr
)->zone
, sd
);
1093 *ptr
= (*ptr
)->next
;
1097 else ptr
= &(*ptr
)->next
;
1100 pthread_mutex_unlock(&d
->tablelock
);
1105 // main update request handling
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
)
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
;
1120 if (pthread_mutex_lock(&d
->tablelock
)) { LogErr("UpdateLeaseTable", "pthread_mutex_lock"); return; }
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
; }
1129 for (i
= 0; i
< pkt
->msg
.h
.mDNS_numUpdates
; i
++)
1131 mDNSBool DeleteAllRRSets
= mDNSfalse
, DeleteOneRRSet
= mDNSfalse
, DeleteOneRR
= mDNSfalse
;
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
];
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
;
1146 if (DeleteAllRRSets
|| DeleteOneRRSet
|| DeleteOneRR
)
1150 if (SameDomainName((*rptr
)->rr
.resrec
.name
, rr
->name
) &&
1152 (DeleteOneRRSet
&& (*rptr
)->rr
.resrec
.rrtype
== rr
->rrtype
) ||
1153 (DeleteOneRR
&& SameResourceRecord(&(*rptr
)->rr
.resrec
, rr
))))
1156 VLog("Received deletion update for %s", GetRRDisplayString_rdb(&tmp
->rr
.resrec
, &tmp
->rr
.resrec
.rdata
->u
, buf
));
1157 *rptr
= (*rptr
)->next
;
1161 else rptr
= &(*rptr
)->next
;
1166 // see if add or refresh
1167 while (*rptr
&& !SameResourceRecord(&(*rptr
)->rr
.resrec
, rr
)) rptr
= &(*rptr
)->next
;
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
));
1177 // New record - add to table
1178 if (d
->nelems
> d
->nbuckets
)
1181 bucket
= rr
->namehash
% d
->nbuckets
;
1182 rptr
= &d
->table
[bucket
];
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
;
1199 VLog("Adding update for %s to lease table", GetRRDisplayString_rdb(&lcr
.r
.resrec
, &lcr
.r
.resrec
.rdata
->u
, buf
));
1205 pthread_mutex_unlock(&d
->tablelock
);
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
)
1218 reply
= malloc(sizeof(*reply
));
1219 if (!reply
) { LogErr("FormatLeaseReply", "malloc"); return NULL
; }
1220 flags
.b
[0] = kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
;
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
;
1234 // pkt is thread-local, not requiring locking
1235 mDNSlocal PktMsg
*HandleRequest(PktMsg
*pkt
, DaemonInfo
*d
)
1238 PktMsg
*reply
= NULL
, *LeaseReply
, buf
;
1240 char addrbuf
[32], pingmsg
[4];
1242 // send msg to server, read reply
1244 if (pkt
->len
<= 512)
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
1255 sd
= ConnectToServer(d
);
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
);
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
);
1270 LeaseReply
= FormatLeaseReply(d
, reply
, lease
);
1271 if (!LeaseReply
) Log("HandleRequest - unable to format lease reply");
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");
1279 if (sd
>= 0) close(sd
);
1282 reply
= malloc(sizeof(*reply
));
1283 if (!reply
) LogErr("HandleRequest", "malloc");
1284 else { reply
->len
= buf
.len
; memcpy(&reply
->msg
, &buf
.msg
, buf
.len
); }
1291 // LLQ Support Routines
1294 // Set fields of an LLQ Opt Resource Record
1295 mDNSlocal
void FormatLLQOpt(AuthRecord
*opt
, int opcode
, mDNSu8
*id
, mDNSs32 lease
)
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
;
1310 // Calculate effective remaining lease of an LLQ
1311 mDNSlocal mDNSu32
LLQLease(LLQEntry
*e
)
1315 gettimeofday(&t
, NULL
);
1316 if (e
->expire
< t
.tv_sec
) return 0;
1317 else return e
->expire
- t
.tv_sec
;
1320 mDNSlocal
void DeleteLLQ(DaemonInfo
*d
, LLQEntry
*e
)
1322 int bucket
= DomainNameHashValue(&e
->qname
) % LLQ_TABLESIZE
;
1323 LLQEntry
**ptr
= &d
->LLQTable
[bucket
];
1324 AnswerListElem
*a
= e
->AnswerList
;
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
);
1330 if (a
&& !(--a
->refcount
) && d
->AnswerTableCount
>= LLQ_TABLESIZE
)
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
1337 CacheRecord
*cr
= a
->KnownAnswers
, *tmp
;
1338 AnswerListElem
**tbl
= &d
->AnswerTable
[bucket
];
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");
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
;
1359 mDNSlocal
int SendLLQ(DaemonInfo
*d
, PktMsg
*pkt
, struct sockaddr_in dst
)
1365 if (sendto(d
->udpsd
, &pkt
->msg
, pkt
->len
, 0, (struct sockaddr
*)&dst
, sizeof(dst
)) != (int)pkt
->len
)
1367 LogErr("DaemonInfo", "sendto");
1368 Log("Could not send response to client %s", inet_ntop(AF_INET
, &dst
.sin_addr
, addr
, 32));
1375 mDNSlocal CacheRecord
*AnswerQuestion(DaemonInfo
*d
, AnswerListElem
*e
)
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
;
1387 VLog("Querying server for %##s type %d", e
->name
.c
, e
->type
);
1389 flags
.b
[0] |= kDNSFlag0_RD
; // recursion desired
1390 id
.NotAnInteger
= 0;
1391 InitializeDNSMessage(&q
.msg
.h
, id
, flags
);
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
);
1401 if (UDPServerTransaction(d
, &q
, &buf
, &trunc
) < 0)
1402 Log("AnswerQuestion %##s - UDPServerTransaction failed. Trying TCP", e
->name
.c
);
1404 { VLog("AnswerQuestion %##s - answer truncated. Using TCP", e
->name
.c
); e
->UseTCP
= mDNStrue
; }
1405 else reply
= &buf
; // success
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
);
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
; }
1422 end
= (mDNSu8
*)&reply
->msg
+ reply
->len
;
1423 ansptr
= LocateAnswers(&reply
->msg
, end
);
1424 if (!ansptr
) { Log("Error: AnswerQuestion - LocateAnswers returned NULL"); goto end
; }
1426 for (i
= 0; i
< reply
->msg
.h
.numAnswers
; i
++)
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
))
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
);
1437 CacheRecord
*cr
= CopyCacheRecord(&lcr
.r
, &e
->name
);
1438 if (!cr
) { Log("Error: AnswerQuestion - CopyCacheRecord returned NULL"); goto end
; }
1439 cr
->next
= AnswerList
;
1445 if (reply
&& reply
!= &buf
) free(reply
);
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
)
1452 CacheRecord
*cr
, *NewAnswers
, **na
, **ka
; // "new answer", "known answer"
1453 DaemonInfo
*d
= ((UpdateAnswerListArgs
*)args
)->d
;
1454 AnswerListElem
*a
= ((UpdateAnswerListArgs
*)args
)->a
;
1459 // get up to date answers
1460 NewAnswers
= AnswerQuestion(d
, a
);
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
1466 // second pass - mark answers pre-existent
1467 for (ka
= &a
->KnownAnswers
; *ka
; ka
= &(*ka
)->next
)
1469 for (na
= &NewAnswers
; *na
; na
= &(*na
)->next
)
1471 if (SameResourceRecord(&(*ka
)->resrec
, &(*na
)->resrec
))
1472 { (*ka
)->resrec
.rroriginalttl
= 0; break; } // 0 means no change
1476 // third pass - add new records to Event list
1480 for (ka
= &a
->KnownAnswers
; *ka
; ka
= &(*ka
)->next
)
1481 if (SameResourceRecord(&(*ka
)->resrec
, &(*na
)->resrec
)) break;
1484 // answer is not in list - splice from NewAnswers list, add to Event list
1486 *na
= (*na
)->next
; // splice from list
1487 cr
->next
= a
->EventList
; // add spliced record to event list
1489 cr
->resrec
.rroriginalttl
= 1; // 1 means add
1491 else na
= &(*na
)->next
;
1494 // move all the removes from the answer list to the event list
1495 ka
= &a
->KnownAnswers
;
1498 if ((*ka
)->resrec
.rroriginalttl
== (unsigned)-1)
1502 cr
->next
= a
->EventList
;
1505 else ka
= &(*ka
)->next
;
1508 // lastly, free the remaining records (known answers) in NewAnswers list
1512 NewAnswers
= NewAnswers
->next
;
1519 mDNSlocal
void SendEvents(DaemonInfo
*d
, LLQEntry
*e
)
1523 mDNSu8
*end
= (mDNSu8
*)&response
.msg
.data
;
1525 char rrbuf
[80], addrbuf
[32];
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; }
1534 // put adds/removes in packet
1535 for (cr
= e
->AnswerList
->EventList
; cr
; cr
= cr
->next
)
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; }
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; }
1547 response
.len
= (int)(end
- (mDNSu8
*)&response
.msg
);
1548 if (SendLLQ(d
, &response
, e
->cli
) < 0) LogMsg("Error: SendEvents - SendLLQ");
1551 mDNSlocal
void PrintLLQAnswers(DaemonInfo
*d
)
1556 Log("Printing LLQ Answer Table contents");
1558 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1560 AnswerListElem
*a
= d
->AnswerTable
[i
];
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
));
1573 mDNSlocal
void PrintLLQTable(DaemonInfo
*d
)
1579 Log("Printing LLQ table contents");
1581 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1590 case RequestReceived
: state
= "RequestReceived"; break;
1591 case ChallengeSent
: state
= "ChallengeSent"; break;
1592 case Established
: state
= "Established"; break;
1593 default: state
= "unknown";
1595 inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addr
, 32);
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
);
1604 // Send events to clients as a result of a change in the zone
1605 mDNSlocal
void GenLLQEvents(DaemonInfo
*d
)
1610 UpdateAnswerListArgs
*args
;
1612 VLog("Generating LLQ Events");
1614 gettimeofday(&t
, NULL
);
1616 // get all answers up to date
1617 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1619 AnswerListElem
*a
= d
->AnswerTable
[i
];
1622 args
= malloc(sizeof(*args
));
1623 if (!args
) { LogErr("GenLLQEvents", "malloc"); return; }
1626 if (pthread_create(&a
->tid
, NULL
, UpdateAnswerList
, args
) < 0) { LogErr("GenLLQEvents", "pthread_create"); return; }
1632 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1634 AnswerListElem
*a
= d
->AnswerTable
[i
];
1637 if (pthread_join(a
->tid
, NULL
)) LogErr("GenLLQEvents", "pthread_join");
1642 // for each established LLQ, send events
1643 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1645 e
= &d
->LLQTable
[i
];
1648 if ((*e
)->expire
< t
.tv_sec
) DeleteLLQ(d
, *e
);
1651 if ((*e
)->state
== Established
&& (*e
)->AnswerList
->EventList
) SendEvents(d
, *e
);
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
++)
1660 AnswerListElem
*a
= d
->AnswerTable
[i
];
1665 CacheRecord
*cr
= a
->EventList
, *tmp
;
1670 if ((signed)tmp
->resrec
.rroriginalttl
< 0) free(tmp
);
1673 tmp
->next
= a
->KnownAnswers
;
1674 a
->KnownAnswers
= tmp
;
1675 tmp
->resrec
.rroriginalttl
= 0;
1678 a
->EventList
= NULL
;
1685 mDNSlocal
void SetAnswerList(DaemonInfo
*d
, LLQEntry
*e
)
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
;
1692 a
= malloc(sizeof(*a
));
1693 if (!a
) { LogErr("SetAnswerList", "malloc"); return; }
1694 AssignDomainName(&a
->name
, &e
->qname
);
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
);
1709 // Allocate LLQ entry, insert into table
1710 mDNSlocal LLQEntry
*NewLLQ(DaemonInfo
*d
, struct sockaddr_in cli
, domainname
*qname
, mDNSu16 qtype
, mDNSu32 lease
)
1714 int bucket
= DomainNameHashValue(qname
) % LLQ_TABLESIZE
;
1717 e
= malloc(sizeof(*e
));
1718 if (!e
) { LogErr("NewLLQ", "malloc"); return NULL
; }
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
);
1723 // initialize structure
1725 AssignDomainName(&e
->qname
, qname
);
1727 memset(e
->id
, 0, 8);
1728 e
->state
= RequestReceived
;
1729 e
->AnswerList
= NULL
;
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
;
1738 e
->next
= d
->LLQTable
[bucket
];
1739 d
->LLQTable
[bucket
] = e
;
1744 // Handle a refresh request from client
1745 mDNSlocal
void LLQRefresh(DaemonInfo
*d
, LLQEntry
*e
, LLQOptData
*llq
, mDNSOpaque16 msgID
)
1749 mDNSu8
*end
= (mDNSu8
*)&ack
.msg
.data
;
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
);
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
;
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; }
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; }
1773 ack
.len
= (int)(end
- (mDNSu8
*)&ack
.msg
);
1774 if (SendLLQ(d
, &ack
, e
->cli
)) Log("Error: LLQRefresh");
1776 if (llq
->lease
) e
->state
= Established
;
1777 else DeleteLLQ(d
, e
);
1780 // Complete handshake with Ack an initial answers
1781 mDNSlocal
void LLQCompleteHandshake(DaemonInfo
*d
, LLQEntry
*e
, LLQOptData
*llq
, mDNSOpaque16 msgID
)
1787 mDNSu8
*end
= (mDNSu8
*)&ack
.msg
.data
;
1788 char rrbuf
[80], addrbuf
[32];
1790 inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addr
, 32);
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; }
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
);
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; }
1809 if (e
->state
!= Established
) { SetAnswerList(d
, e
); e
->state
= Established
; }
1811 if (verbose
) inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addrbuf
, 32);
1812 for (ptr
= e
->AnswerList
->KnownAnswers
; ptr
; ptr
= ptr
->next
)
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; }
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; }
1824 ack
.len
= (int)(end
- (mDNSu8
*)&ack
.msg
);
1825 if (SendLLQ(d
, &ack
, e
->cli
)) Log("Error: LLQCompleteHandshake");
1828 mDNSlocal
void LLQSetupChallenge(DaemonInfo
*d
, LLQEntry
*e
, LLQOptData
*llq
, mDNSOpaque16 msgID
)
1833 mDNSu8
*end
= challenge
.msg
.data
;
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
);
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
1842 if (ZERO_LLQID(e
->id
)) // don't regenerate random ID for retransmissions
1844 // construct ID <time><random>
1845 gettimeofday(&t
, NULL
);
1847 memcpy(e
->id
, &t
.tv_sec
, sizeof(t
.tv_sec
));
1848 memcpy(e
->id
+ sizeof(t
.tv_sec
), &randval
, sizeof(randval
));
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
;
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
)
1869 case RequestReceived
:
1870 LLQSetupChallenge(d
, e
, llq
, msgID
);
1873 if (ZERO_LLQID(llq
->id
)) LLQSetupChallenge(d
, e
, llq
, msgID
); // challenge sent and lost
1874 else LLQCompleteHandshake(d
, e
, llq
, msgID
);
1877 if (ZERO_LLQID(llq
->id
))
1879 // client started over. reset state.
1880 LLQEntry
*newe
= NewLLQ(d
, e
->cli
, &e
->qname
, e
->qtype
, llq
->lease
);
1883 LLQSetupChallenge(d
, newe
, llq
, msgID
);
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; }
1894 mDNSlocal LLQEntry
*LookupLLQ(DaemonInfo
*d
, struct sockaddr_in cli
, domainname
*qname
, mDNSu16 qtype
, mDNSu8
*id
)
1896 int bucket
= bucket
= DomainNameHashValue(qname
) % LLQ_TABLESIZE
;
1897 LLQEntry
*ptr
= d
->LLQTable
[bucket
];
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
1910 mDNSlocal
int RecvLLQ(DaemonInfo
*d
, PktMsg
*pkt
)
1913 LargeCacheRecord opt
;
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
;
1923 inet_ntop(AF_INET
, &pkt
->src
.sin_addr
, addr
, 32);
1925 VLog("Received LLQ msg from %s", addr
);
1926 // sanity-check packet
1927 if (!pkt
->msg
.h
.numQuestions
|| !pkt
->msg
.h
.numAdditionals
)
1929 Log("Malformatted LLQ from %s with %d questions, %d additionals", addr
, pkt
->msg
.h
.numQuestions
, pkt
->msg
.h
.numAdditionals
);
1933 // find the OPT RR - must be last in message
1934 for (i
= 0; i
< pkt
->msg
.h
.numAdditionals
; i
++)
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
; }
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
); }
1944 // dispatch each question
1945 for (i
= 0; i
< pkt
->msg
.h
.numQuestions
; i
++)
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
; }
1952 e
= LookupLLQ(d
, pkt
->src
, &q
.qname
, q
.qtype
, llq
->id
);
1955 // no entry - if zero ID, create new
1956 e
= NewLLQ(d
, pkt
->src
, &q
.qname
, q
.qtype
, llq
->lease
);
1959 UpdateLLQ(d
, e
, llq
, pkt
->msg
.h
.id
);
1968 mDNSlocal mDNSBool
IsLLQRequest(PktMsg
*pkt
)
1970 const mDNSu8
*ptr
= NULL
, *end
= (mDNSu8
*)&pkt
->msg
+ pkt
->len
;
1971 LargeCacheRecord lcr
;
1973 mDNSBool result
= mDNSfalse
;
1976 if ((mDNSu8
)(pkt
->msg
.h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
) != (mDNSu8
)(kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
)) goto end
;
1978 if (!pkt
->msg
.h
.numAdditionals
) goto end
;
1979 ptr
= LocateAdditionals(&pkt
->msg
, end
);
1982 // find last Additional
1983 for (i
= 0; i
< pkt
->msg
.h
.numAdditionals
; i
++)
1985 ptr
= GetLargeResourceRecord(NULL
, &pkt
->msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &lcr
);
1986 if (!ptr
) { Log("Unable to read additional record"); goto end
; }
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
; }
1999 // !!!KRS implement properly
2000 mDNSlocal mDNSBool
IsLLQAck(PktMsg
*pkt
)
2002 if (pkt
->msg
.h
.flags
.NotAnInteger
== ResponseFlags
.NotAnInteger
&&
2003 pkt
->msg
.h
.numQuestions
&& !pkt
->msg
.h
.numAnswers
&& !pkt
->msg
.h
.numAuthorities
) return mDNStrue
;
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
)
2013 UDPRequestArgs
*req
= vptr
;
2014 PktMsg
*reply
= NULL
;
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
);
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");
2025 if (reply
) free(reply
);
2030 //!!!KRS this needs to be changed to use non-blocking sockets
2031 mDNSlocal
int RecvUDPRequest(int sd
, DaemonInfo
*d
)
2033 UDPRequestArgs
*req
;
2035 unsigned int clisize
= sizeof(req
->cliaddr
);
2037 req
= malloc(sizeof(UDPRequestArgs
));
2038 if (!req
) { LogErr("RecvUDPRequest", "malloc"); return -1; }
2039 bzero(req
, sizeof(*req
));
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
;
2046 if (IsLLQRequest(&req
->pkt
))
2048 // LLQ messages handled by main thread
2049 int err
= RecvLLQ(d
, &req
->pkt
);
2054 if (IsLLQAck(&req
->pkt
)) { free(req
); return 0; } // !!!KRS need to do acks + retrans
2056 if (pthread_create(&tid
, NULL
, UDPUpdateRequestForkFn
, req
)) { LogErr("RecvUDPRequest", "pthread_create"); free(req
); return -1; }
2057 pthread_detach(tid
);
2061 mDNSlocal
void *TCPRequestForkFn(void *vptr
)
2063 TCPRequestArgs
*req
= vptr
;
2064 PktMsg
*in
= NULL
, *out
= NULL
;
2067 //!!!KRS if this read blocks indefinitely, we can run out of threads
2069 in
= ReadTCPMsg(req
->sd
, NULL
);
2072 LogMsg("TCPRequestForkFn: Could not read message from %s", inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
2076 VLog("Received TCP request: %d bytes from %s", in
->len
, inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
2078 out
= HandleRequest(in
, req
->d
);
2081 LogMsg("TCPRequestForkFn: No reply for client %s", inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
2085 // deliver reply to client
2086 if (SendTCPMsg(req
->sd
, out
) < 0)
2088 LogMsg("TCPRequestForkFn: Unable to send reply to client %s", inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
2100 mDNSlocal
int RecvTCPRequest(int sd
, DaemonInfo
*d
)
2102 TCPRequestArgs
*req
= NULL
;
2104 unsigned int clilen
= sizeof(req
->cliaddr
);
2106 req
= malloc(sizeof(TCPRequestArgs
));
2107 if (!req
) { LogErr("RecvTCPRequest", "malloc"); goto error
; }
2108 bzero(req
, sizeof(*req
));
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
);
2123 // listen for incoming requests, periodically check table for expired records, respond to signals
2124 mDNSlocal
int ListenForUpdates(DaemonInfo
*d
)
2128 struct timeval timenow
, timeout
, EventTS
, tablecheck
= { 0, 0 };
2129 mDNSBool EventsPending
= mDNSfalse
;
2131 VLog("Listening for requests...");
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;
2141 timeout
.tv_sec
= timeout
.tv_usec
= 0;
2142 if (gettimeofday(&timenow
, NULL
)) { LogErr("ListenForUpdates", "gettimeofday"); return -1; }
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
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
;
2158 FD_SET(d
->tcpsd
, &rset
);
2159 FD_SET(d
->udpsd
, &rset
);
2160 FD_SET(d
->LLQEventListenSock
, &rset
);
2162 nfds
= select(maxfdp1
, &rset
, NULL
, NULL
, &timeout
);
2169 // close sockets to prevent clients from making new requests during shutdown
2172 d
->tcpsd
= d
->udpsd
= -1;
2173 DeleteRecords(d
, mDNStrue
);
2176 else if (dumptable
) { PrintLeaseTable(d
); PrintLLQTable(d
); PrintLLQAnswers(d
); dumptable
= 0; }
2177 else Log("Received unhandled signal - continuing");
2181 LogErr("ListenForUpdates", "select"); return -1;
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
))
2190 // clear signalling data off socket
2192 recv(d
->LLQEventListenSock
, buf
, 256, 0);
2195 EventsPending
= mDNStrue
;
2196 if (gettimeofday(&EventTS
, NULL
)) { LogErr("ListenForUpdates", "gettimeofday"); return -1; }
2203 if (EventsPending
) { GenLLQEvents(d
); EventsPending
= mDNSfalse
; }
2204 else { DeleteRecords(d
, mDNSfalse
); tablecheck
.tv_sec
= 0; }
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
)
2214 if (sig
== SIGTERM
|| sig
== SIGINT
) { terminate
= 1; return; }
2215 if (sig
== INFO_SIGNAL
) { dumptable
= 1; return; }
2218 int main(int argc
, char *argv
[])
2223 d
= malloc(sizeof(*d
));
2224 if (!d
) { LogErr("main", "malloc"); exit(1); }
2225 bzero(d
, sizeof(DaemonInfo
));
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");
2232 // remove open file limit
2233 rlim
.rlim_max
= RLIM_INFINITY
;
2234 rlim
.rlim_cur
= RLIM_INFINITY
;
2235 if (setrlimit(RLIMIT_NOFILE
, &rlim
) < 0)
2237 LogErr("main", "setrlimit");
2238 Log("Using default file descriptor resource limit");
2241 if (ProcessArgs(argc
, argv
, d
) < 0) exit(1);
2247 LogErr("main", "daemon");
2248 fprintf(stderr
, "Could not daemonize process, running in foreground");
2253 if (InitLeaseTable(d
) < 0) exit(1);
2254 if (SetupSockets(d
) < 0) exit(1);
2255 if (SetUpdateSRV(d
) < 0) exit(1);
2257 ListenForUpdates(d
);
2258 if (ClearUpdateSRV(d
) < 0) exit(1); // clear update srv's even if ListenForUpdates or pthread_create returns an error