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.33.2.1 2005/08/05 21:14:00 ksekar
28 <rdar://problem/4012279> Long-lived queries not working on windows
31 Revision 1.33 2005/03/11 19:09:02 ksekar
32 Fixed ZERO_LLQID macro
34 Revision 1.32 2005/03/10 22:54:33 ksekar
35 <rdar://problem/4046285> dnsextd leaks memory/ports
37 Revision 1.31 2005/02/24 02:37:57 ksekar
38 <rdar://problem/4021977> dnsextd memory management improvements
40 Revision 1.30 2005/01/27 22:57:56 cheshire
41 Fix compile errors on gcc4
43 Revision 1.29 2004/12/22 00:13:50 ksekar
44 <rdar://problem/3873993> Change version, port, and polling interval for LLQ
46 Revision 1.28 2004/12/17 00:30:00 ksekar
47 <rdar://problem/3924045> dnsextd memory leak
49 Revision 1.27 2004/12/17 00:27:32 ksekar
52 Revision 1.26 2004/12/17 00:21:33 ksekar
53 Fixes for new CacheRecord structure with indirect name pointer
55 Revision 1.25 2004/12/16 20:13:02 cheshire
56 <rdar://problem/3324626> Cache memory management improvements
58 Revision 1.24 2004/12/14 17:09:06 ksekar
59 fixed incorrect usage instructions
61 Revision 1.23 2004/12/06 20:24:31 ksekar
62 <rdar://problem/3907303> dnsextd leaks sockets
64 Revision 1.22 2004/12/03 20:20:29 ksekar
65 <rdar://problem/3904149> dnsextd: support delivery of large records via LLQ events
67 Revision 1.21 2004/12/03 06:11:34 ksekar
68 <rdar://problem/3885059> clean up dnsextd arguments
70 Revision 1.20 2004/12/01 04:27:28 cheshire
71 <rdar://problem/3872803> Darwin patches for Solaris and Suse
72 Don't use uint32_t, etc. -- they require stdint.h, which doesn't exist on FreeBSD 4.x, Solaris, etc.
74 Revision 1.19 2004/12/01 01:16:29 cheshire
75 Solaris compatibility fixes
77 Revision 1.18 2004/11/30 23:51:06 cheshire
78 Remove double semicolons
80 Revision 1.17 2004/11/30 22:37:01 cheshire
81 Update copyright dates and add "Mode: C; tab-width: 4" headers
83 Revision 1.16 2004/11/25 02:02:28 ksekar
84 Fixed verbose log message argument
86 Revision 1.15 2004/11/19 02:35:02 ksekar
87 <rdar://problem/3886317> Wide Area Security: Add LLQ-ID to events
89 Revision 1.14 2004/11/17 06:17:58 cheshire
90 Update comments to show correct SRV names: _dns-update._udp.<zone>. and _dns-llq._udp.<zone>.
92 Revision 1.13 2004/11/13 02:22:36 ksekar
93 <rdar://problem/3878201> Refresh Acks from daemon malformatted
95 Revision 1.12 2004/11/12 01:05:01 ksekar
96 <rdar://problem/3876757> dnsextd: daemon registers the SRV same record
99 Revision 1.11 2004/11/12 01:03:31 ksekar
100 <rdar://problem/3876776> dnsextd: KnownAnswers (CacheRecords) leaked
102 Revision 1.10 2004/11/12 00:35:28 ksekar
103 <rdar://problem/3876705> dnsextd: uninitialized pointer can cause crash
105 Revision 1.9 2004/11/10 20:38:17 ksekar
106 <rdar://problem/3874168> dnsextd: allow a "fudge" in LLQ lease echo
108 Revision 1.8 2004/11/01 17:48:14 cheshire
109 Changed SOA serial number back to signed. RFC 1035 may describe it as "unsigned", but
110 it's wrong. The SOA serial is a modular counter, as explained in "DNS & BIND", page
111 137. Since C doesn't have a modular type, we used signed, C's closest approximation.
113 Revision 1.7 2004/10/30 00:06:58 ksekar
114 <rdar://problem/3722535> Support Long Lived Queries in DNS Extension daemon
116 Revision 1.6 2004/09/17 01:08:54 cheshire
117 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
118 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
119 declared in that file are ONLY appropriate to single-address-space embedded applications.
120 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
122 Revision 1.5 2004/09/16 00:50:54 cheshire
123 Don't use MSG_WAITALL -- it returns "Invalid argument" on some Linux versions
125 Revision 1.4 2004/09/14 23:27:48 cheshire
128 Revision 1.3 2004/09/02 01:39:40 cheshire
129 For better readability, follow consistent convention that QR bit comes first, followed by OP bits
131 Revision 1.2 2004/08/24 23:27:57 cheshire
132 Fixes for Linux compatibility:
135 Don't try to set servaddr.sin_len on platforms that don't have sa_len
137 Revision 1.1 2004/08/11 00:43:26 ksekar
138 <rdar://problem/3722542>: DNS Extension daemon for DNS Update Lease
142 #include "../mDNSCore/mDNSEmbeddedAPI.h"
143 #include "../mDNSCore/DNSCommon.h"
144 #include "../mDNSCore/mDNS.c"
145 //!!!KRS we #include mDNS.c for the various constants defined there - we should move these to DNSCommon.h
151 #include <sys/types.h>
152 #include <sys/socket.h>
153 #include <netinet/in.h>
154 #include <arpa/inet.h>
158 #include <sys/time.h>
162 // Compatibility workaround
164 #define AF_LOCAL AF_UNIX
171 #define LOOPBACK "127.0.0.1"
173 #define DAEMON_PORT 5352 // default, may be overridden via command line argument
174 #define LISTENQ 128 // tcp connection backlog
175 #define RECV_BUFLEN 9000
176 #define LEASETABLE_INIT_NBUCKETS 256 // initial hashtable size (doubles as table fills)
177 #define LLQ_TABLESIZE 1024 // !!!KRS make this dynamically growable
178 #define EXPIRATION_INTERVAL 300 // check for expired records every 5 minutes
179 #define SRV_TTL 7200 // TTL For _dns-update SRV records
181 // LLQ Lease bounds (seconds)
182 #define LLQ_MIN_LEASE (15 * 60)
183 #define LLQ_MAX_LEASE (120 * 60)
184 #define LLQ_LEASE_FUDGE 60
186 // LLQ SOA poll interval (microseconds)
187 #define LLQ_MONITOR_ERR_INTERVAL (60 * 1000000)
188 #define LLQ_MONITOR_INTERVAL 250000
190 #define INFO_SIGNAL SIGINFO
192 #define INFO_SIGNAL SIGUSR1
195 #define SAME_INADDR(x,y) (*((mDNSu32 *)&x) == *((mDNSu32 *)&y))
196 #define ZERO_LLQID(x) (!memcmp(x, "\x0\x0\x0\x0\x0\x0\x0\x0", 8))
200 // Structs/fields that must be locked for thread safety are explicitly commented
205 struct sockaddr_in src
;
208 // Note: extra storage for oversized (TCP) messages goes here
212 typedef struct RRTableElem
214 struct RRTableElem
*next
;
215 struct sockaddr_in cli
; // client's source address
216 long expire
; // expiration time, in seconds since epoch
217 domainname zone
; // from zone field of update message
218 domainname name
; // name of the record
219 CacheRecord rr
; // last field in struct allows for allocation of oversized RRs
229 typedef struct AnswerListElem
231 struct AnswerListElem
*next
;
234 CacheRecord
*KnownAnswers
; // All valid answers delivered to client
235 CacheRecord
*EventList
; // New answers (adds/removes) to be sent to client
240 typedef struct LLQEntry
242 struct LLQEntry
*next
;
243 struct sockaddr_in cli
; // clien'ts source address
248 mDNSu32 lease
; // original lease, in seconds
249 mDNSs32 expire
; // expiration, absolute, in seconds since epoch
250 AnswerListElem
*AnswerList
;
253 // daemon-wide information
256 // server variables - read only after initialization (no locking)
257 struct in_addr saddr
; // server address
258 domainname zone
; // zone being updated
259 int tcpsd
; // listening TCP socket
260 int udpsd
; // listening UDP socket
262 // daemon variables - read only after initialization (no locking)
263 uDNS_AuthInfo
*AuthInfo
; // linked list of keys for signing deletion updates
264 mDNSIPPort port
; // listening port
266 // lease table variables (locked via mutex after initialization)
267 RRTableElem
**table
; // hashtable for records with leases
268 pthread_mutex_t tablelock
; // mutex for lease table
269 mDNSs32 nbuckets
; // buckets allocated
270 mDNSs32 nelems
; // elements in table
272 // LLQ table variables
273 LLQEntry
*LLQTable
[LLQ_TABLESIZE
]; // !!!KRS change this and RRTable to use a common data structure
274 AnswerListElem
*AnswerTable
[LLQ_TABLESIZE
];
275 int LLQEventListenSock
; // Unix domain socket pair - polling thread writes to ServPollSock, which wakes
276 int LLQServPollSock
; // the main thread listening on EventListenSock, indicating that the zone has changed
279 // args passed to UDP request handler thread as void*
283 struct sockaddr_in cliaddr
;
287 // args passed to TCP request handler thread as void*
290 int sd
; // socket connected to client
291 struct sockaddr_in cliaddr
;
299 // booleans to determine runtime output
300 // read-only after initialization (no mutex protection)
301 static mDNSBool foreground
= 0;
302 static mDNSBool verbose
= 0;
304 // globals set via signal handler (accessed exclusively by main select loop and signal handler)
305 static mDNSBool terminate
= 0;
306 static mDNSBool dumptable
= 0;
310 // Log messages are delivered to syslog unless -f option specified
313 // common message logging subroutine
314 mDNSlocal
void PrintLog(const char *buffer
)
318 fprintf(stderr
,"%s\n", buffer
);
323 openlog("dnsextd", LOG_CONS
| LOG_PERROR
, LOG_DAEMON
);
324 syslog(LOG_ERR
, "%s", buffer
);
329 // Verbose Logging (conditional on -v option)
330 mDNSlocal
void VLog(const char *format
, ...)
335 if (!verbose
) return;
336 va_start(ptr
,format
);
337 buffer
[mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
)] = 0;
342 // Unconditional Logging
343 mDNSlocal
void Log(const char *format
, ...)
348 va_start(ptr
,format
);
349 buffer
[mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
)] = 0;
355 // prints message "dnsextd <function>: <operation> - <error message>"
356 // must be compiled w/ -D_REENTRANT for thread-safe errno usage
357 mDNSlocal
void LogErr(const char *fn
, const char *operation
)
360 snprintf(buf
, sizeof(buf
), "%s: %s - %s", fn
, operation
, strerror(errno
));
365 // Networking Utility Routines
368 // Convert DNS Message Header from Network to Host byte order
369 mDNSlocal
void HdrNToH(PktMsg
*pkt
)
371 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
372 mDNSu8
*ptr
= (mDNSu8
*)&pkt
->msg
.h
.numQuestions
;
373 pkt
->msg
.h
.numQuestions
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
374 pkt
->msg
.h
.numAnswers
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
375 pkt
->msg
.h
.numAuthorities
= (mDNSu16
)((mDNSu16
)ptr
[4] << 8 | ptr
[5]);
376 pkt
->msg
.h
.numAdditionals
= (mDNSu16
)((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
379 // Convert DNS Message Header from Host to Network byte order
380 mDNSlocal
void HdrHToN(PktMsg
*pkt
)
382 mDNSu16 numQuestions
= pkt
->msg
.h
.numQuestions
;
383 mDNSu16 numAnswers
= pkt
->msg
.h
.numAnswers
;
384 mDNSu16 numAuthorities
= pkt
->msg
.h
.numAuthorities
;
385 mDNSu16 numAdditionals
= pkt
->msg
.h
.numAdditionals
;
386 mDNSu8
*ptr
= (mDNSu8
*)&pkt
->msg
.h
.numQuestions
;
388 // Put all the integer values in IETF byte-order (MSB first, LSB second)
389 *ptr
++ = (mDNSu8
)(numQuestions
>> 8);
390 *ptr
++ = (mDNSu8
)(numQuestions
& 0xFF);
391 *ptr
++ = (mDNSu8
)(numAnswers
>> 8);
392 *ptr
++ = (mDNSu8
)(numAnswers
& 0xFF);
393 *ptr
++ = (mDNSu8
)(numAuthorities
>> 8);
394 *ptr
++ = (mDNSu8
)(numAuthorities
& 0xFF);
395 *ptr
++ = (mDNSu8
)(numAdditionals
>> 8);
396 *ptr
++ = (mDNSu8
)(numAdditionals
& 0xFF);
399 // create a socket connected to nameserver
400 // caller terminates connection via close()
401 mDNSlocal
int ConnectToServer(DaemonInfo
*d
)
403 struct sockaddr_in servaddr
;
406 bzero(&servaddr
, sizeof(servaddr
));
407 if (d
->saddr
.s_addr
) servaddr
.sin_addr
= d
->saddr
;
408 else inet_pton(AF_INET
, LOOPBACK
, &d
->saddr
); // use loopback if server not explicitly specified
409 servaddr
.sin_port
= htons(NS_PORT
);
410 servaddr
.sin_family
= AF_INET
;
411 #ifndef NOT_HAVE_SA_LEN
412 servaddr
.sin_len
= sizeof(servaddr
);
414 sd
= socket(AF_INET
, SOCK_STREAM
, 0);
415 if (sd
< 0) { LogErr("ConnectToServer", "socket"); return -1; }
416 if (connect(sd
, (struct sockaddr
*)&servaddr
, sizeof(servaddr
)) < 0) { LogErr("ConnectToServer", "connect"); return -1; }
420 // send an entire block of data over a connected socket, blocking if buffers are full
421 mDNSlocal
int MySend(int sd
, const void *msg
, int len
)
427 n
= send(sd
, (char *)msg
+ nsent
, len
- nsent
, 0);
428 if (n
< 0) { LogErr("MySend", "send"); return -1; }
434 // Transmit a DNS message, prefixed by its length, over TCP, blocking if necessary
435 mDNSlocal
int SendTCPMsg(int sd
, PktMsg
*pkt
)
437 // send the lenth, in network byte order
438 mDNSu16 len
= htons((mDNSu16
)pkt
->len
);
439 if (MySend(sd
, &len
, sizeof(len
)) < 0) return -1;
442 return MySend(sd
, &pkt
->msg
, pkt
->len
);
445 // Receive len bytes, waiting until we have all of them.
446 // Returns number of bytes read (which should always be the number asked for).
447 static int my_recv(const int sd
, void *const buf
, const int len
)
449 // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions;
450 // use an explicit while() loop instead.
451 // Also, don't try to do '+=' arithmetic on the original "void *" pointer --
452 // arithmetic on "void *" pointers is compiler-dependent.
454 char *ptr
= (char *)buf
;
457 ssize_t num_read
= recv(sd
, ptr
, remaining
, 0);
458 if ((num_read
== 0) || (num_read
< 0) || (num_read
> remaining
)) return -1;
460 remaining
-= num_read
;
465 // Return a DNS Message read off of a TCP socket, or NULL on failure
466 // If storage is non-null, result is placed in that buffer. Otherwise,
467 // returned value is allocated with Malloc, and contains sufficient extra
468 // storage for a Lease OPT RR
470 mDNSlocal PktMsg
*ReadTCPMsg(int sd
, PktMsg
*storage
)
472 int nread
, allocsize
;
477 nread
= my_recv(sd
, &msglen
, sizeof(msglen
));
478 if (nread
< 0) { LogErr("TCPRequestForkFn", "recv"); goto error
; }
479 msglen
= ntohs(msglen
);
480 if (nread
!= sizeof(msglen
)) { Log("Could not read length field of message"); goto error
; }
484 if (msglen
> sizeof(storage
->msg
)) { Log("ReadTCPMsg: provided buffer too small."); goto error
; }
489 // buffer extra space to add an OPT RR
490 if (msglen
> sizeof(DNSMessage
)) allocsize
= sizeof(PktMsg
) - sizeof(DNSMessage
) + msglen
;
491 else allocsize
= sizeof(PktMsg
);
492 pkt
= malloc(allocsize
);
493 if (!pkt
) { LogErr("ReadTCPMsg", "malloc"); goto error
; }
494 bzero(pkt
, sizeof(*pkt
));
498 srclen
= sizeof(pkt
->src
);
499 if (getpeername(sd
, (struct sockaddr
*)&pkt
->src
, &srclen
) ||
500 srclen
!= sizeof(pkt
->src
)) { LogErr("ReadTCPMsg", "getpeername"); bzero(&pkt
->src
, sizeof(pkt
->src
)); }
501 nread
= my_recv(sd
, &pkt
->msg
, msglen
);
502 if (nread
< 0) { LogErr("TCPRequestForkFn", "recv"); goto error
; }
503 if (nread
!= msglen
) { Log("Could not read entire message"); goto error
; }
504 if (pkt
->len
< sizeof(DNSMessageHeader
))
505 { Log("ReadTCPMsg: Message too short (%d bytes)", pkt
->len
); goto error
; }
508 //!!!KRS convert to HBO here?
510 if (pkt
&& pkt
!= storage
) free(pkt
);
515 // Dynamic Update Utility Routines
518 // Get the lease life of records in a dynamic update
519 // returns -1 on error or if no lease present
520 mDNSlocal mDNSs32
GetPktLease(PktMsg
*pkt
)
523 const mDNSu8
*ptr
= NULL
, *end
= (mDNSu8
*)&pkt
->msg
+ pkt
->len
;
524 LargeCacheRecord lcr
;
528 ptr
= LocateAdditionals(&pkt
->msg
, end
);
530 for (i
= 0; i
< pkt
->msg
.h
.numAdditionals
; i
++)
532 ptr
= GetLargeResourceRecord(NULL
, &pkt
->msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &lcr
);
533 if (!ptr
) { Log("Unable to read additional record"); break; }
534 if (lcr
.r
.resrec
.rrtype
== kDNSType_OPT
)
536 if (lcr
.r
.resrec
.rdlength
< LEASE_OPT_RDLEN
) continue;
537 if (lcr
.r
.resrec
.rdata
->u
.opt
.opt
!= kDNSOpt_Lease
) continue;
538 lease
= (mDNSs32
)lcr
.r
.resrec
.rdata
->u
.opt
.OptData
.lease
;
547 // check if a request and server response complete a successful dynamic update
548 mDNSlocal mDNSBool
SuccessfulUpdateTransaction(PktMsg
*request
, PktMsg
*reply
)
551 char *vlogmsg
= NULL
;
554 if (!request
|| !reply
) { vlogmsg
= "NULL message"; goto failure
; }
555 if (request
->len
< sizeof(DNSMessageHeader
) || reply
->len
< sizeof(DNSMessageHeader
)) { vlogmsg
= "Malformatted message"; goto failure
; }
557 // check request operation
558 if ((request
->msg
.h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
) != (request
->msg
.h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
))
559 { vlogmsg
= "Request opcode not an update"; goto failure
; }
562 if ((reply
->msg
.h
.flags
.b
[1] & kDNSFlag1_RC
)) { vlogmsg
= "Reply contains non-zero rcode"; goto failure
; }
563 if ((reply
->msg
.h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
) != (kDNSFlag0_OP_Update
| kDNSFlag0_QR_Response
))
564 { vlogmsg
= "Reply opcode not an update response"; goto failure
; }
566 VLog("Successful update from %s", inet_ntop(AF_INET
, &request
->src
.sin_addr
, buf
, 32));
570 VLog("Request %s: %s", inet_ntop(AF_INET
, &request
->src
.sin_addr
, buf
, 32), vlogmsg
);
574 // Allocate an appropriately sized CacheRecord and copy data from original.
575 // Name pointer in CacheRecord object is set to point to the name specified
577 mDNSlocal CacheRecord
*CopyCacheRecord(const CacheRecord
*orig
, domainname
*name
)
580 size_t size
= sizeof(*cr
);
581 if (orig
->resrec
.rdlength
> InlineCacheRDSize
) size
+= orig
->resrec
.rdlength
- InlineCacheRDSize
;
583 if (!cr
) { LogErr("CopyCacheRecord", "malloc"); return NULL
; }
584 memcpy(cr
, orig
, size
);
585 cr
->resrec
.rdata
= (RData
*)&cr
->rdatastorage
;
586 cr
->resrec
.name
= name
;
593 // Lease Hashtable Utility Routines
596 // double hash table size
597 // caller must lock table prior to invocation
598 mDNSlocal
void RehashTable(DaemonInfo
*d
)
600 RRTableElem
*ptr
, *tmp
, **new;
601 int i
, bucket
, newnbuckets
= d
->nbuckets
* 2;
603 VLog("Rehashing lease table (new size %d buckets)", newnbuckets
);
604 new = malloc(sizeof(RRTableElem
*) * newnbuckets
);
605 if (!new) { LogErr("RehashTable", "malloc"); return; }
606 bzero(new, newnbuckets
* sizeof(RRTableElem
*));
608 for (i
= 0; i
< d
->nbuckets
; i
++)
613 bucket
= ptr
->rr
.resrec
.namehash
% newnbuckets
;
616 tmp
->next
= new[bucket
];
620 d
->nbuckets
= newnbuckets
;
625 // print entire contents of hashtable, invoked via SIGINFO
626 mDNSlocal
void PrintLeaseTable(DaemonInfo
*d
)
630 char rrbuf
[80], addrbuf
[16];
634 if (gettimeofday(&now
, NULL
)) { LogErr("PrintTable", "gettimeofday"); return; }
635 if (pthread_mutex_lock(&d
->tablelock
)) { LogErr("PrintTable", "pthread_mutex_lock"); return; }
637 Log("Dumping Lease Table Contents (table contains %d resource records)", d
->nelems
);
638 for (i
= 0; i
< d
->nbuckets
; i
++)
640 for (ptr
= d
->table
[i
]; ptr
; ptr
= ptr
->next
)
642 hr
= ((ptr
->expire
- now
.tv_sec
) / 60) / 60;
643 min
= ((ptr
->expire
- now
.tv_sec
) / 60) % 60;
644 sec
= (ptr
->expire
- now
.tv_sec
) % 60;
645 Log("Update from %s, Expires in %d:%d:%d\n\t%s", inet_ntop(AF_INET
, &ptr
->cli
.sin_addr
, addrbuf
, 16), hr
, min
, sec
,
646 GetRRDisplayString_rdb(&ptr
->rr
.resrec
, &ptr
->rr
.resrec
.rdata
->u
, rrbuf
));
649 pthread_mutex_unlock(&d
->tablelock
);
653 // Startup SRV Registration Routines
654 // Register _dns-update._udp/_tcp.<zone> SRV records indicating the port on which
655 // the daemon accepts requests
658 // delete all RRS of a given name/type
659 mDNSlocal mDNSu8
*putRRSetDeletion(DNSMessage
*msg
, mDNSu8
*ptr
, mDNSu8
*limit
, ResourceRecord
*rr
)
661 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->name
);
662 if (!ptr
|| ptr
+ 10 >= limit
) return NULL
; // out of space
663 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8);
664 ptr
[1] = (mDNSu8
)(rr
->rrtype
& 0xFF);
665 ptr
[2] = (mDNSu8
)((mDNSu16
)kDNSQClass_ANY
>> 8);
666 ptr
[3] = (mDNSu8
)((mDNSu16
)kDNSQClass_ANY
& 0xFF);
667 bzero(ptr
+4, sizeof(rr
->rroriginalttl
) + sizeof(rr
->rdlength
)); // zero ttl/rdata
668 msg
->h
.mDNS_numUpdates
++;
672 mDNSlocal mDNSu8
*PutUpdateSRV(DaemonInfo
*d
, PktMsg
*pkt
, mDNSu8
*ptr
, char *regtype
, mDNSBool registration
)
675 char hostname
[1024], buf
[80];
676 mDNSu8
*end
= (mDNSu8
*)&pkt
->msg
+ sizeof(DNSMessage
);
678 mDNS_SetupResourceRecord(&rr
, NULL
, 0, kDNSType_SRV
, SRV_TTL
, kDNSRecordTypeUnique
, NULL
, NULL
);
679 rr
.resrec
.rrclass
= kDNSClass_IN
;
680 rr
.resrec
.rdata
->u
.srv
.priority
= 0;
681 rr
.resrec
.rdata
->u
.srv
.weight
= 0;
682 rr
.resrec
.rdata
->u
.srv
.port
.NotAnInteger
= d
->port
.NotAnInteger
;
683 if (!gethostname(hostname
, 1024) < 0 || MakeDomainNameFromDNSNameString(&rr
.resrec
.rdata
->u
.srv
.target
, hostname
))
684 rr
.resrec
.rdata
->u
.srv
.target
.c
[0] = '\0';
686 MakeDomainNameFromDNSNameString(rr
.resrec
.name
, regtype
);
687 AppendDomainName(rr
.resrec
.name
, &d
->zone
);
688 VLog("%s %s", registration
? "Registering SRV record" : "Deleting existing RRSet",
689 GetRRDisplayString_rdb(&rr
.resrec
, &rr
.resrec
.rdata
->u
, buf
));
690 if (registration
) ptr
= PutResourceRecord(&pkt
->msg
, ptr
, &pkt
->msg
.h
.mDNS_numUpdates
, &rr
.resrec
);
691 else ptr
= putRRSetDeletion(&pkt
->msg
, ptr
, end
, &rr
.resrec
);
696 // perform dynamic update.
697 // specify deletion by passing false for the register parameter, otherwise register the records.
698 mDNSlocal
int UpdateSRV(DaemonInfo
*d
, mDNSBool registration
)
703 mDNSu8
*ptr
= pkt
.msg
.data
;
704 mDNSu8
*end
= (mDNSu8
*)&pkt
.msg
+ sizeof(DNSMessage
);
705 mDNSu16 nAdditHBO
; // num additionas, in host byte order, required by message digest routine
706 PktMsg
*reply
= NULL
;
710 // Initialize message
712 InitializeDNSMessage(&pkt
.msg
.h
, id
, UpdateReqFlags
);
713 pkt
.src
.sin_addr
.s_addr
= htonl(INADDR_ANY
); // address field set solely for verbose logging in subroutines
714 pkt
.src
.sin_family
= AF_INET
;
716 // format message body
717 ptr
= putZone(&pkt
.msg
, ptr
, end
, &d
->zone
, mDNSOpaque16fromIntVal(kDNSClass_IN
));
720 ptr
= PutUpdateSRV(d
, &pkt
, ptr
, "_dns-update._udp.", registration
); if (!ptr
) goto end
;
721 ptr
= PutUpdateSRV(d
, &pkt
, ptr
, "_dns-update._tcp.", registration
); if (!ptr
) goto end
;
722 ptr
= PutUpdateSRV(d
, &pkt
, ptr
, "_dns-llq._udp.", registration
); if (!ptr
) goto end
;
724 nAdditHBO
= pkt
.msg
.h
.numAdditionals
;
728 ptr
= DNSDigest_SignMessage(&pkt
.msg
, &ptr
, &nAdditHBO
, d
->AuthInfo
);
731 pkt
.len
= ptr
- (mDNSu8
*)&pkt
.msg
;
733 // send message, receive reply
734 sd
= ConnectToServer(d
);
735 if (sd
< 0) { Log("UpdateSRV: ConnectToServer failed"); goto end
; }
736 if (SendTCPMsg(sd
, &pkt
)) { Log("UpdateSRV: SendTCPMsg failed"); }
737 reply
= ReadTCPMsg(sd
, NULL
);
738 if (!SuccessfulUpdateTransaction(&pkt
, reply
))
739 Log("SRV record registration failed with rcode %d", reply
->msg
.h
.flags
.b
[1] & kDNSFlag1_RC
);
743 if (!ptr
) { Log("UpdateSRV: Error constructing lease expiration update"); }
744 if (sd
>= 0) close(sd
);
745 if (reply
) free(reply
);
749 // wrapper routines/macros
750 #define ClearUpdateSRV(d) UpdateSRV(d, 0)
752 // clear any existing records prior to registration
753 mDNSlocal
int SetUpdateSRV(DaemonInfo
*d
)
757 err
= ClearUpdateSRV(d
); // clear any existing record
758 if (!err
) err
= UpdateSRV(d
, 1);
763 // Argument Parsing and Configuration
766 // read authentication information for a zone from command line argument
767 // global optind corresponds to keyname argument on entry
768 mDNSlocal
int ReadAuthKey(int argc
, char *argv
[], DaemonInfo
*d
)
770 uDNS_AuthInfo
*auth
= NULL
;
771 unsigned char keybuf
[512];
774 auth
= malloc(sizeof(*auth
));
775 if (!auth
) { perror("ReadAuthKey, malloc"); goto error
; }
777 if (argc
< optind
+ 1) return -1; // keyname + secret
778 if (!MakeDomainNameFromDNSNameString(&auth
->keyname
, optarg
))
779 { fprintf(stderr
, "Bad key name %s", optarg
); goto error
; }
780 keylen
= DNSDigest_Base64ToBin(argv
[optind
++], keybuf
, 512);
782 { fprintf(stderr
, "Bad shared secret %s (must be base-64 encoded string)", argv
[optind
-1]); goto error
; }
783 DNSDigest_ConstructHMACKey(auth
, keybuf
, (mDNSu32
)keylen
);
788 if (auth
) free(auth
);
792 mDNSlocal
int SetPort(DaemonInfo
*d
, char *PortAsString
)
796 l
= strtol(PortAsString
, NULL
, 10); // convert string to long
797 if ((!l
&& errno
== EINVAL
) || l
> 65535) return -1; // error check conversion
798 d
->port
.NotAnInteger
= htons((mDNSu16
)l
); // set to network byte order
802 mDNSlocal
void PrintUsage(void)
804 fprintf(stderr
, "Usage: dnsextd -z <zone> [-vf] [ -s server ] [-k keyname secret] ...\n"
805 "Use \"dnsextd -h\" for help\n");
808 mDNSlocal
void PrintHelp(void)
810 fprintf(stderr
, "\n\n");
814 "dnsextd is a daemon that implements DNS extensions supporting Dynamic DNS Update Leases\n"
815 "and Long Lived Queries, used in Wide-Area DNS Service Discovery, on behalf of name servers\n"
816 "that do not natively support these extensions. (See dns-sd.org for more info on DNS Service\n"
817 "Discovery, Update Leases, and Long Lived Queries.)\n\n"
819 "dnsextd requires one argument,the zone, which is the domain for which Update Leases\n"
820 "and Long Lived Queries are to be administered. dnsextd communicates directly with the\n"
821 "primary master server for this zone.\n\n"
823 "The options are as follows:\n\n"
825 "-f Run daemon in foreground.\n\n"
829 "-k Specify TSIG authentication key for dynamic updates from daemon to name server.\n"
830 " -k option is followed by the name of the key, and the shared secret as a base-64\n"
831 " encoded string. This key/secret are used by the daemon to delete resource records\n"
832 " from the server when leases expire. Clients are responsible for signing their\n"
833 " update requests.\n\n"
835 "-s Specify address (IPv4 address in dotted-decimal notation) of the Primary Master\n"
836 " name server. Defaults to loopback (127.0.0.1), i.e. daemon and name server\n"
837 " running on the same machine.\n\n"
839 "-v Verbose output.\n\n"
843 // Note: ProcessArgs called before process is daemonized, and therefore must open no descriptors
844 // returns 0 (success) if program is to continue execution
845 // output control arguments (-f, -v) do not affect this routine
846 mDNSlocal
int ProcessArgs(int argc
, char *argv
[], DaemonInfo
*d
)
850 if (argc
< 2) goto arg_error
;
852 d
->port
.NotAnInteger
= htons(DAEMON_PORT
); // default, may be overriden by command option
853 while ((opt
= getopt(argc
, argv
, "z:p:hfvs:k:")) != -1)
857 case 'p': if (SetPort(d
, optarg
) < 0) goto arg_error
;
860 case 'h': PrintHelp(); return -1;
861 case 'f': foreground
= 1; break;
862 case 'v': verbose
= 1; break;
863 case 's': if (!inet_pton(AF_INET
, optarg
, &d
->saddr
)) goto arg_error
;
865 case 'k': if (ReadAuthKey(argc
, argv
, d
) < 0) goto arg_error
;
867 case 'z': if (!MakeDomainNameFromDNSNameString(&d
->zone
, optarg
))
869 fprintf(stderr
, "Bad zone %s", optarg
);
873 default: goto arg_error
;
877 if (!d
->zone
.c
[0]) goto arg_error
; // zone is the only required argument
878 if (d
->AuthInfo
) AssignDomainName(&d
->AuthInfo
->zone
, &d
->zone
); // if we have a shared secret, use it for the entire zone
888 // Initialization Routines
891 // Allocate memory, initialize locks and bookkeeping variables
892 mDNSlocal
int InitLeaseTable(DaemonInfo
*d
)
894 if (pthread_mutex_init(&d
->tablelock
, NULL
)) { LogErr("InitLeaseTable", "pthread_mutex_init"); return -1; }
895 d
->nbuckets
= LEASETABLE_INIT_NBUCKETS
;
897 d
->table
= malloc(sizeof(RRTableElem
*) * LEASETABLE_INIT_NBUCKETS
);
898 if (!d
->table
) { LogErr("InitLeaseTable", "malloc"); return -1; }
899 bzero(d
->table
, sizeof(RRTableElem
*) * LEASETABLE_INIT_NBUCKETS
);
902 mDNSlocal
int SetupSockets(DaemonInfo
*daemon
)
904 struct sockaddr_in daddr
;
907 // set up sockets on which we receive requests
908 bzero(&daddr
, sizeof(daddr
));
909 daddr
.sin_family
= AF_INET
;
910 daddr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
912 if (daemon
->port
.NotAnInteger
) daddr
.sin_port
= daemon
->port
.NotAnInteger
;
913 else daddr
.sin_port
= htons(DAEMON_PORT
);
915 daemon
->tcpsd
= socket(AF_INET
, SOCK_STREAM
, 0);
916 if (!daemon
->tcpsd
) { LogErr("SetupSockets", "socket"); return -1; }
917 if (bind(daemon
->tcpsd
, (struct sockaddr
*)&daddr
, sizeof(daddr
)) < 0) { LogErr("SetupSockets", "bind"); return -1; }
918 if (listen(daemon
->tcpsd
, LISTENQ
) < 0) { LogErr("SetupSockets", "listen"); return -1; }
920 daemon
->udpsd
= socket(AF_INET
, SOCK_DGRAM
, 0);
921 if (!daemon
->udpsd
) { LogErr("SetupSockets", "socket"); return -1; }
922 if (bind(daemon
->udpsd
, (struct sockaddr
*)&daddr
, sizeof(daddr
)) < 0) { LogErr("SetupSockets", "bind"); return -1; }
924 // set up Unix domain socket pair for LLQ polling thread to signal main thread that a change to the zone occurred
925 if (socketpair(AF_LOCAL
, SOCK_STREAM
, 0, sockpair
) < 0) { LogErr("SetupSockets", "socketpair"); return -1; }
926 daemon
->LLQEventListenSock
= sockpair
[0];
927 daemon
->LLQServPollSock
= sockpair
[1];
932 // periodic table updates
935 // Delete a resource record from the nameserver via a dynamic update
936 mDNSlocal
void DeleteRecord(DaemonInfo
*d
, CacheRecord
*rr
, domainname
*zone
)
941 mDNSu8
*ptr
= pkt
.msg
.data
;
942 mDNSu8
*end
= (mDNSu8
*)&pkt
.msg
+ sizeof(DNSMessage
);
943 mDNSu16 nAdditHBO
; // num additionas, in host byte order, required by message digest routine
945 PktMsg
*reply
= NULL
;
947 VLog("Expiring record %s", GetRRDisplayString_rdb(&rr
->resrec
, &rr
->resrec
.rdata
->u
, buf
));
948 sd
= ConnectToServer(d
);
949 if (sd
< 0) { Log("DeleteRecord: ConnectToServer failed"); goto end
; }
952 InitializeDNSMessage(&pkt
.msg
.h
, id
, UpdateReqFlags
);
954 ptr
= putZone(&pkt
.msg
, ptr
, end
, zone
, mDNSOpaque16fromIntVal(rr
->resrec
.rrclass
));
956 ptr
= putDeletionRecord(&pkt
.msg
, ptr
, &rr
->resrec
);
959 nAdditHBO
= pkt
.msg
.h
.numAdditionals
;
964 ptr
= DNSDigest_SignMessage(&pkt
.msg
, &ptr
, &nAdditHBO
, d
->AuthInfo
);
968 pkt
.len
= ptr
- (mDNSu8
*)&pkt
.msg
;
969 pkt
.src
.sin_addr
.s_addr
= htonl(INADDR_ANY
); // address field set solely for verbose logging in subroutines
970 pkt
.src
.sin_family
= AF_INET
;
971 if (SendTCPMsg(sd
, &pkt
)) { Log("DeleteRecord: SendTCPMsg failed"); }
972 reply
= ReadTCPMsg(sd
, NULL
);
973 if (!SuccessfulUpdateTransaction(&pkt
, reply
))
974 Log("Expiration update failed with rcode %d", reply
->msg
.h
.flags
.b
[1] & kDNSFlag1_RC
);
977 if (!ptr
) { Log("DeleteRecord: Error constructing lease expiration update"); }
978 if (sd
>= 0) close(sd
);
979 if (reply
) free(reply
);
982 // iterate over table, deleting expired records
983 mDNSlocal
void DeleteExpiredRecords(DaemonInfo
*d
)
986 RRTableElem
*ptr
, *prev
, *fptr
;
989 if (gettimeofday(&now
, NULL
)) { LogErr("DeleteExpiredRecords ", "gettimeofday"); return; }
990 if (pthread_mutex_lock(&d
->tablelock
)) { LogErr("DeleteExpiredRecords", "pthread_mutex_lock"); return; }
991 for (i
= 0; i
< d
->nbuckets
; i
++)
997 if (ptr
->expire
- now
.tv_sec
< 0)
999 // delete record from server
1000 DeleteRecord(d
, &ptr
->rr
, &ptr
->zone
);
1001 if (prev
) prev
->next
= ptr
->next
;
1002 else d
->table
[i
] = ptr
->next
;
1015 pthread_mutex_unlock(&d
->tablelock
);
1019 // main update request handling
1022 // Add, delete, or refresh records in table based on contents of a successfully completed dynamic update
1023 mDNSlocal
void UpdateLeaseTable(PktMsg
*pkt
, DaemonInfo
*d
, mDNSs32 lease
)
1025 RRTableElem
**rptr
, *tmp
;
1026 int i
, allocsize
, bucket
;
1027 LargeCacheRecord lcr
;
1028 ResourceRecord
*rr
= &lcr
.r
.resrec
;
1029 const mDNSu8
*ptr
, *end
;
1030 struct timeval time
;
1034 if (pthread_mutex_lock(&d
->tablelock
)) { LogErr("UpdateLeaseTable", "pthread_mutex_lock"); return; }
1036 ptr
= pkt
->msg
.data
;
1037 end
= (mDNSu8
*)&pkt
->msg
+ pkt
->len
;
1038 ptr
= getQuestion(&pkt
->msg
, ptr
, end
, 0, &zone
);
1039 if (!ptr
) { Log("UpdateLeaseTable: cannot read zone"); goto cleanup
; }
1040 ptr
= LocateAuthorities(&pkt
->msg
, end
);
1041 if (!ptr
) { Log("UpdateLeaseTable: Format error"); goto cleanup
; }
1043 for (i
= 0; i
< pkt
->msg
.h
.mDNS_numUpdates
; i
++)
1045 mDNSBool DeleteAllRRSets
= mDNSfalse
, DeleteOneRRSet
= mDNSfalse
, DeleteOneRR
= mDNSfalse
;
1047 ptr
= GetLargeResourceRecord(NULL
, &pkt
->msg
, ptr
, end
, 0, kDNSRecordTypePacketAns
, &lcr
);
1048 if (!ptr
) { Log("UpdateLeaseTable: GetLargeResourceRecord returned NULL"); goto cleanup
; }
1049 bucket
= rr
->namehash
% d
->nbuckets
;
1050 rptr
= &d
->table
[bucket
];
1053 if (rr
->rrtype
== kDNSQType_ANY
&& !rr
->rroriginalttl
&& rr
->rrclass
== kDNSQClass_ANY
&& !rr
->rdlength
)
1054 DeleteAllRRSets
= mDNStrue
; // delete all rrsets for a name
1055 else if (!rr
->rroriginalttl
&& rr
->rrclass
== kDNSQClass_ANY
&& !rr
->rdlength
)
1056 DeleteOneRRSet
= mDNStrue
;
1057 else if (!rr
->rroriginalttl
&& rr
->rrclass
== kDNSClass_NONE
)
1058 DeleteOneRR
= mDNStrue
;
1060 if (DeleteAllRRSets
|| DeleteOneRRSet
|| DeleteOneRR
)
1064 if (SameDomainName((*rptr
)->rr
.resrec
.name
, rr
->name
) &&
1066 (DeleteOneRRSet
&& (*rptr
)->rr
.resrec
.rrtype
== rr
->rrtype
) ||
1067 (DeleteOneRR
&& SameResourceRecord(&(*rptr
)->rr
.resrec
, rr
))))
1070 VLog("Received deletion update for %s", GetRRDisplayString_rdb(&tmp
->rr
.resrec
, &tmp
->rr
.resrec
.rdata
->u
, buf
));
1071 *rptr
= (*rptr
)->next
;
1075 else rptr
= &(*rptr
)->next
;
1080 // see if add or refresh
1081 while (*rptr
&& !SameResourceRecord(&(*rptr
)->rr
.resrec
, rr
)) rptr
= &(*rptr
)->next
;
1085 if (gettimeofday(&time
, NULL
)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup
; }
1086 (*rptr
)->expire
= time
.tv_sec
+ (unsigned)lease
;
1087 VLog("Refreshing lease for %s", GetRRDisplayString_rdb(&lcr
.r
.resrec
, &lcr
.r
.resrec
.rdata
->u
, buf
));
1091 // New record - add to table
1092 if (d
->nelems
> d
->nbuckets
)
1095 bucket
= rr
->namehash
% d
->nbuckets
;
1096 rptr
= &d
->table
[bucket
];
1098 if (gettimeofday(&time
, NULL
)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup
; }
1099 allocsize
= sizeof(RRTableElem
);
1100 if (rr
->rdlength
> InlineCacheRDSize
) allocsize
+= (rr
->rdlength
- InlineCacheRDSize
);
1101 tmp
= malloc(allocsize
);
1102 if (!tmp
) { LogErr("UpdateLeaseTable", "malloc"); goto cleanup
; }
1103 memcpy(&tmp
->rr
, &lcr
.r
, sizeof(CacheRecord
) + rr
->rdlength
- InlineCacheRDSize
);
1104 tmp
->rr
.resrec
.rdata
= (RData
*)&tmp
->rr
.rdatastorage
;
1105 AssignDomainName(&tmp
->name
, rr
->name
);
1106 tmp
->rr
.resrec
.name
= &tmp
->name
;
1107 tmp
->expire
= time
.tv_sec
+ (unsigned)lease
;
1108 tmp
->cli
.sin_addr
= pkt
->src
.sin_addr
;
1109 AssignDomainName(&tmp
->zone
, &zone
.qname
);
1110 tmp
->next
= d
->table
[bucket
];
1111 d
->table
[bucket
] = tmp
;
1113 VLog("Adding update for %s to lease table", GetRRDisplayString_rdb(&lcr
.r
.resrec
, &lcr
.r
.resrec
.rdata
->u
, buf
));
1119 pthread_mutex_unlock(&d
->tablelock
);
1123 // Given a successful reply from a server, create a new reply that contains lease information
1124 // Replies are currently not signed !!!KRS change this
1125 mDNSlocal PktMsg
*FormatLeaseReply(DaemonInfo
*d
, PktMsg
*orig
, mDNSu32 lease
)
1132 reply
= malloc(sizeof(*reply
));
1133 if (!reply
) { LogErr("FormatLeaseReply", "malloc"); return NULL
; }
1134 flags
.b
[0] = kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
;
1137 InitializeDNSMessage(&reply
->msg
.h
, orig
->msg
.h
.id
, flags
);
1138 reply
->src
.sin_addr
.s_addr
= htonl(INADDR_ANY
); // unused except for log messages
1139 reply
->src
.sin_family
= AF_INET
;
1140 ptr
= reply
->msg
.data
;
1141 end
= (mDNSu8
*)&reply
->msg
+ sizeof(DNSMessage
);
1142 ptr
= putUpdateLease(&reply
->msg
, ptr
, lease
);
1143 if (!ptr
) { Log("FormatLeaseReply: putUpdateLease failed"); free(reply
); return NULL
; }
1144 reply
->len
= ptr
- (mDNSu8
*)&reply
->msg
;
1148 // pkt is thread-local, not requiring locking
1149 mDNSlocal PktMsg
*HandleRequest(PktMsg
*pkt
, DaemonInfo
*d
)
1152 PktMsg
*reply
= NULL
, *LeaseReply
;
1156 // send msg to server, read reply
1157 sd
= ConnectToServer(d
);
1159 { Log("Discarding request from %s due to connection errors", inet_ntop(AF_INET
, &pkt
->src
.sin_addr
, buf
, 32)); goto cleanup
; }
1160 if (SendTCPMsg(sd
, pkt
) < 0)
1161 { Log("Couldn't relay message from %s to server. Discarding.", inet_ntop(AF_INET
, &pkt
->src
.sin_addr
, buf
, 32)); goto cleanup
; }
1162 reply
= ReadTCPMsg(sd
, NULL
);
1165 if (!SuccessfulUpdateTransaction(pkt
, reply
))
1166 { VLog("Message from %s not a successful update.", inet_ntop(AF_INET
, &pkt
->src
.sin_addr
, buf
, 32)); goto cleanup
; }
1167 lease
= GetPktLease(pkt
);
1168 UpdateLeaseTable(pkt
, d
, lease
);
1171 LeaseReply
= FormatLeaseReply(d
, reply
, lease
);
1172 if (!LeaseReply
) Log("HandleRequest - unable to format lease reply");
1177 if (sd
>= 0) close(sd
);
1183 // LLQ Support Routines
1186 // Set fields of an LLQ Opt Resource Record
1187 mDNSlocal
void FormatLLQOpt(AuthRecord
*opt
, int opcode
, mDNSu8
*id
, mDNSs32 lease
)
1189 bzero(opt
, sizeof(*opt
));
1190 mDNS_SetupResourceRecord(opt
, mDNSNULL
, mDNSInterface_Any
, kDNSType_OPT
, kStandardTTL
, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
1191 opt
->resrec
.rdlength
= LLQ_OPT_RDLEN
;
1192 opt
->resrec
.rdestimate
= LLQ_OPT_RDLEN
;
1193 opt
->resrec
.rdata
->u
.opt
.opt
= kDNSOpt_LLQ
;
1194 opt
->resrec
.rdata
->u
.opt
.optlen
= sizeof(LLQOptData
);
1195 opt
->resrec
.rdata
->u
.opt
.OptData
.llq
.vers
= kLLQ_Vers
;
1196 opt
->resrec
.rdata
->u
.opt
.OptData
.llq
.llqOp
= opcode
;
1197 opt
->resrec
.rdata
->u
.opt
.OptData
.llq
.err
= LLQErr_NoError
;
1198 memcpy(opt
->resrec
.rdata
->u
.opt
.OptData
.llq
.id
, id
, 8);
1199 opt
->resrec
.rdata
->u
.opt
.OptData
.llq
.lease
= lease
;
1202 // Calculate effective remaining lease of an LLQ
1203 mDNSlocal mDNSu32
LLQLease(LLQEntry
*e
)
1207 gettimeofday(&t
, NULL
);
1208 if (e
->expire
< t
.tv_sec
) return 0;
1209 else return e
->expire
- t
.tv_sec
;
1212 mDNSlocal
void DeleteLLQ(DaemonInfo
*d
, LLQEntry
*e
)
1214 int bucket
= DomainNameHashValue(&e
->qname
) % LLQ_TABLESIZE
;
1215 LLQEntry
**ptr
= &d
->LLQTable
[bucket
];
1216 AnswerListElem
*a
= e
->AnswerList
;
1219 inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addr
, 32);
1220 VLog("Deleting LLQ table entry for %##s client %s", e
->qname
.c
, addr
);
1222 // free shared answer structure if ref count drops to zero
1223 if (a
&& !(--a
->refcount
))
1225 CacheRecord
*cr
= a
->KnownAnswers
, *tmp
;
1226 AnswerListElem
**tbl
= &d
->AnswerTable
[bucket
];
1235 while (*tbl
&& *tbl
!= a
) tbl
= &(*tbl
)->next
;
1236 if (*tbl
) { *tbl
= (*tbl
)->next
; free(a
); }
1237 else Log("Error: DeleteLLQ - AnswerList not found in table");
1240 // remove LLQ from table, free memory
1241 while(*ptr
&& *ptr
!= e
) ptr
= &(*ptr
)->next
;
1242 if (!*ptr
) { Log("Error: DeleteLLQ - LLQ not in table"); return; }
1243 *ptr
= (*ptr
)->next
;
1247 mDNSlocal
int SendLLQ(DaemonInfo
*d
, PktMsg
*pkt
, struct sockaddr_in dst
)
1253 if (sendto(d
->udpsd
, &pkt
->msg
, pkt
->len
, 0, (struct sockaddr
*)&dst
, sizeof(dst
)) != (int)pkt
->len
)
1255 LogErr("DaemonInfo", "sendto");
1256 Log("Could not send response to client %s", inet_ntop(AF_INET
, &dst
.sin_addr
, addr
, 32));
1263 // if non-negative, sd is a TCP socket connected to the nameserver
1264 // otherwise, this routine creates and closes its own socket
1265 mDNSlocal CacheRecord
*AnswerQuestion(DaemonInfo
*d
, AnswerListElem
*e
, int sd
)
1269 const mDNSu8
*ansptr
;
1270 mDNSu8
*end
= q
.msg
.data
;
1271 mDNSOpaque16 id
, flags
= QueryFlags
;
1272 PktMsg
*reply
= NULL
;
1273 LargeCacheRecord lcr
;
1274 CacheRecord
*AnswerList
= NULL
;
1276 mDNSBool CloseSDOnExit
= sd
< 0;
1278 VLog("Querying server for %##s type %d", e
->name
.c
, e
->type
);
1280 flags
.b
[0] |= kDNSFlag0_RD
; // recursion desired
1281 id
.NotAnInteger
= 0;
1282 InitializeDNSMessage(&q
.msg
.h
, id
, flags
);
1284 end
= putQuestion(&q
.msg
, end
, end
+ AbsoluteMaxDNSMessageData
, &e
->name
, e
->type
, kDNSClass_IN
);
1285 if (!end
) { Log("Error: AnswerQuestion - putQuestion returned NULL"); goto end
; }
1286 q
.len
= (int)(end
- (mDNSu8
*)&q
.msg
);
1288 if (sd
< 0) sd
= ConnectToServer(d
);
1289 if (sd
< 0) { Log("AnswerQuestion: ConnectToServer failed"); goto end
; }
1290 if (SendTCPMsg(sd
, &q
)) { Log("AnswerQuestion: SendTCPMsg failed"); close(sd
); goto end
; }
1291 reply
= ReadTCPMsg(sd
, NULL
);
1293 if ((reply
->msg
.h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
) != (kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
))
1294 { Log("AnswerQuestion: %##s type %d - Invalid response flags from server"); goto end
; }
1295 rcode
= (mDNSu8
)(reply
->msg
.h
.flags
.b
[1] & kDNSFlag1_RC
);
1296 if (rcode
&& rcode
!= kDNSFlag1_RC_NXDomain
) { Log("AnswerQuestion: %##s type %d - non-zero rcode %d from server", e
->name
.c
, e
->type
, rcode
); goto end
; }
1298 end
= (mDNSu8
*)&reply
->msg
+ reply
->len
;
1299 ansptr
= LocateAnswers(&reply
->msg
, end
);
1300 if (!ansptr
) { Log("Error: AnswerQuestion - LocateAnswers returned NULL"); goto end
; }
1302 for (i
= 0; i
< reply
->msg
.h
.numAnswers
; i
++)
1304 ansptr
= GetLargeResourceRecord(NULL
, &reply
->msg
, ansptr
, end
, 0, kDNSRecordTypePacketAns
, &lcr
);
1305 if (!ansptr
) { Log("AnswerQuestions: GetLargeResourceRecord returned NULL"); goto end
; }
1306 if (lcr
.r
.resrec
.rrtype
!= e
->type
|| lcr
.r
.resrec
.rrclass
!= kDNSClass_IN
|| !SameDomainName(lcr
.r
.resrec
.name
, &e
->name
))
1308 Log("AnswerQuestion: response %##s type #d does not answer question %##s type #d. Discarding",
1309 lcr
.r
.resrec
.name
->c
, lcr
.r
.resrec
.rrtype
, e
->name
.c
, e
->type
);
1313 CacheRecord
*cr
= CopyCacheRecord(&lcr
.r
, &e
->name
);
1314 if (!cr
) { Log("Error: AnswerQuestion - CopyCacheRecord returned NULL"); goto end
; }
1315 cr
->next
= AnswerList
;
1321 if (sd
> -1 && CloseSDOnExit
) close(sd
);
1322 if (reply
) free(reply
);
1326 // Routine sets EventList to contain Add/Remove events, and deletes any removes from the KnownAnswer list
1327 mDNSlocal
void UpdateAnswerList(DaemonInfo
*d
, AnswerListElem
*a
, int sd
)
1329 CacheRecord
*cr
, *NewAnswers
, **na
, **ka
; // "new answer", "known answer"
1331 // get up to date answers
1332 NewAnswers
= AnswerQuestion(d
, a
, sd
);
1334 // first pass - mark all answers for deletion
1335 for (ka
= &a
->KnownAnswers
; *ka
; ka
= &(*ka
)->next
)
1336 (*ka
)->resrec
.rroriginalttl
= (unsigned)-1; // -1 means delete
1338 // second pass - mark answers pre-existent
1339 for (ka
= &a
->KnownAnswers
; *ka
; ka
= &(*ka
)->next
)
1341 for (na
= &NewAnswers
; *na
; na
= &(*na
)->next
)
1343 if (SameResourceRecord(&(*ka
)->resrec
, &(*na
)->resrec
))
1344 { (*ka
)->resrec
.rroriginalttl
= 0; break; } // 0 means no change
1348 // third pass - add new records to Event list
1352 for (ka
= &a
->KnownAnswers
; *ka
; ka
= &(*ka
)->next
)
1353 if (SameResourceRecord(&(*ka
)->resrec
, &(*na
)->resrec
)) break;
1356 // answer is not in list - splice from NewAnswers list, add to Event list
1358 *na
= (*na
)->next
; // splice from list
1359 cr
->next
= a
->EventList
; // add spliced record to event list
1361 cr
->resrec
.rroriginalttl
= 1; // 1 means add
1363 else na
= &(*na
)->next
;
1366 // move all the removes from the answer list to the event list
1367 ka
= &a
->KnownAnswers
;
1370 if ((*ka
)->resrec
.rroriginalttl
== (unsigned)-1)
1374 cr
->next
= a
->EventList
;
1377 else ka
= &(*ka
)->next
;
1380 // lastly, free the remaining records (known answers) in NewAnswers list
1384 NewAnswers
= NewAnswers
->next
;
1389 mDNSlocal
void SendEvents(DaemonInfo
*d
, LLQEntry
*e
)
1393 mDNSu8
*end
= (mDNSu8
*)&response
.msg
.data
;
1395 char rrbuf
[80], addrbuf
[32];
1398 msgID
.NotAnInteger
= random();
1399 if (verbose
) inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addrbuf
, 32);
1400 InitializeDNSMessage(&response
.msg
.h
, msgID
, ResponseFlags
);
1401 end
= putQuestion(&response
.msg
, end
, end
+ AbsoluteMaxDNSMessageData
, &e
->qname
, e
->qtype
, kDNSClass_IN
);
1402 if (!end
) { Log("Error: SendEvents - putQuestion returned NULL"); return; }
1404 // put adds/removes in packet
1405 for (cr
= e
->AnswerList
->EventList
; cr
; cr
= cr
->next
)
1407 if (verbose
) GetRRDisplayString_rdb(&cr
->resrec
, &cr
->resrec
.rdata
->u
, rrbuf
);
1408 VLog("%s (%s): %s", addrbuf
, (mDNSs32
)cr
->resrec
.rroriginalttl
< 0 ? "Remove": "Add", rrbuf
);
1409 end
= PutResourceRecordTTLJumbo(&response
.msg
, end
, &response
.msg
.h
.numAnswers
, &cr
->resrec
, cr
->resrec
.rroriginalttl
);
1410 if (!end
) { Log("Error: SendEvents - UpdateAnswerList returned NULL"); return; }
1413 FormatLLQOpt(&opt
, kLLQOp_Event
, e
->id
, LLQLease(e
));
1414 end
= PutResourceRecordTTLJumbo(&response
.msg
, end
, &response
.msg
.h
.numAdditionals
, &opt
.resrec
, 0);
1415 if (!end
) { Log("Error: SendEvents - PutResourceRecordTTLJumbo"); return; }
1417 response
.len
= (int)(end
- (mDNSu8
*)&response
.msg
);
1418 if (SendLLQ(d
, &response
, e
->cli
) < 0) LogMsg("Error: SendEvents - SendLLQ");
1421 mDNSlocal
void PrintLLQTable(DaemonInfo
*d
)
1427 Log("Printing LLQ table contents");
1429 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1434 inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addr
, 32);
1435 Log("LLQ from %##s type %d lease %d (%d remaining)",
1436 addr
, e
->qname
.c
, e
->qtype
, e
->lease
, LLQLease(e
));
1442 // Send events to clients as a result of a change in the zone
1443 mDNSlocal
void GenLLQEvents(DaemonInfo
*d
)
1449 VLog("Generating LLQ Events");
1451 gettimeofday(&t
, NULL
);
1452 sd
= ConnectToServer(d
);
1453 if (sd
< 0) { Log("GenLLQEvents: ConnectToServer failed"); return; }
1455 // get all answers up to date
1456 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1458 AnswerListElem
*a
= d
->AnswerTable
[i
];
1461 UpdateAnswerList(d
, a
, sd
);
1466 // for each established LLQ, send events
1467 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1469 e
= &d
->LLQTable
[i
];
1472 if ((*e
)->expire
< t
.tv_sec
) DeleteLLQ(d
, *e
);
1475 if ((*e
)->state
== Established
&& (*e
)->AnswerList
->EventList
) SendEvents(d
, *e
);
1481 // now that all LLQs are updated, we move Add events from the Event list to the Known Answer list, and free Removes
1482 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1484 AnswerListElem
*a
= d
->AnswerTable
[i
];
1489 CacheRecord
*cr
= a
->EventList
, *tmp
;
1494 if ((signed)tmp
->resrec
.rroriginalttl
< 0) free(tmp
);
1497 tmp
->next
= a
->KnownAnswers
;
1498 a
->KnownAnswers
= tmp
;
1499 tmp
->resrec
.rroriginalttl
= 0;
1502 a
->EventList
= NULL
;
1511 // Monitor zone for changes that may produce LLQ events
1512 mDNSlocal
void *LLQEventMonitor(void *DInfoPtr
)
1514 DaemonInfo
*d
= DInfoPtr
;
1516 mDNSu8
*end
= q
.msg
.data
;
1518 mDNSOpaque16 id
, flags
= QueryFlags
;
1521 mDNSBool SerialInitialized
= mDNSfalse
;
1523 LargeCacheRecord lcr
;
1524 ResourceRecord
*rr
= &lcr
.r
.resrec
;
1525 int i
, sleeptime
= 0;
1530 id
.NotAnInteger
= 0;
1531 InitializeDNSMessage(&q
.msg
.h
, id
, flags
);
1532 AssignDomainName(&zone
, &d
->zone
);
1533 end
= putQuestion(&q
.msg
, end
, end
+ AbsoluteMaxDNSMessageData
, &zone
, kDNSType_SOA
, kDNSClass_IN
);
1534 if (!end
) { Log("Error: LLQEventMonitor - putQuestion returned NULL"); return NULL
; }
1535 q
.len
= (int)(end
- (mDNSu8
*)&q
.msg
);
1537 sd
= ConnectToServer(d
);
1538 if (sd
< 0) { Log("LLQEventMonitor: ConnectToServer failed"); return NULL
; }
1543 sleeptime
= LLQ_MONITOR_ERR_INTERVAL
; // if we bail on error below, rate limit retry
1545 // send message, receive response
1546 if (SendTCPMsg(sd
, &q
)) { Log("LLQEventMonitor: SendTCPMsg failed"); continue; }
1547 if (!ReadTCPMsg(sd
, &reply
)) { Log("LLQEventMonitor: ReadTCPMsg failed"); continue; }
1548 end
= (mDNSu8
*)&reply
.msg
+ reply
.len
;
1549 if (reply
.msg
.h
.flags
.b
[1] & kDNSFlag1_RC
) { Log("LLQEventMonitor - received non-zero rcode"); continue; }
1552 ptr
= LocateAnswers(&reply
.msg
, end
);
1553 if (!ptr
) { Log("Error: LLQEventMonitor - LocateAnswers returned NULL"); continue; }
1554 for (i
= 0; i
< reply
.msg
.h
.numAnswers
; i
++)
1556 ptr
= GetLargeResourceRecord(NULL
, &reply
.msg
, ptr
, end
, 0, kDNSRecordTypePacketAns
, &lcr
);
1557 if (!ptr
) { Log("Error: LLQEventMonitor - GetLargeResourceRecord returned NULL"); continue; }
1558 if (rr
->rrtype
!= kDNSType_SOA
|| rr
->rrclass
!= kDNSClass_IN
|| !SameDomainName(rr
->name
, &zone
)) continue;
1559 if (!SerialInitialized
)
1561 // first time through loop
1562 SerialInitialized
= mDNStrue
;
1563 serial
= rr
->rdata
->u
.soa
.serial
;
1564 sleeptime
= LLQ_MONITOR_INTERVAL
;
1567 else if (rr
->rdata
->u
.soa
.serial
!= serial
)
1569 // update serial, wake main thread
1570 serial
= rr
->rdata
->u
.soa
.serial
;
1571 VLog("LLQEventMonitor: zone changed. Signaling main thread.");
1572 if (send(d
->LLQServPollSock
, pingmsg
, sizeof(pingmsg
), 0) != sizeof(pingmsg
))
1573 { LogErr("LLQEventMonitor", "send"); break; }
1575 sleeptime
= LLQ_MONITOR_INTERVAL
;
1578 if (!ptr
) Log("LLQEventMonitor: response to query did not contain SOA");
1582 mDNSlocal
void SetAnswerList(DaemonInfo
*d
, LLQEntry
*e
)
1584 int bucket
= DomainNameHashValue(&e
->qname
) % LLQ_TABLESIZE
;
1585 AnswerListElem
*a
= d
->AnswerTable
[bucket
];
1586 while (a
&& (a
->type
!= e
->qtype
||!SameDomainName(&a
->name
, &e
->qname
))) a
= a
->next
;
1589 a
= malloc(sizeof(*a
));
1590 if (!a
) { LogErr("SetAnswerList", "malloc"); return; }
1591 AssignDomainName(&a
->name
, &e
->qname
);
1594 a
->KnownAnswers
= NULL
;
1595 a
->EventList
= NULL
;
1596 a
->next
= d
->AnswerTable
[bucket
];
1597 d
->AnswerTable
[bucket
] = a
;
1599 // to get initial answer list, call UpdateAnswerList and move cache records from EventList to KnownAnswers
1600 UpdateAnswerList(d
, a
, -1);
1601 a
->KnownAnswers
= a
->EventList
;
1602 a
->EventList
= NULL
;
1609 // Allocate LLQ entry, insert into table
1610 mDNSlocal LLQEntry
*NewLLQ(DaemonInfo
*d
, struct sockaddr_in cli
, domainname
*qname
, mDNSu16 qtype
, mDNSu32 lease
)
1614 int bucket
= DomainNameHashValue(qname
) % LLQ_TABLESIZE
;
1617 e
= malloc(sizeof(*e
));
1618 if (!e
) { LogErr("NewLLQ", "malloc"); return NULL
; }
1620 inet_ntop(AF_INET
, &cli
.sin_addr
, addr
, 32);
1621 VLog("Allocating LLQ entry for client %s question %##s type %d", addr
, qname
->c
, qtype
);
1623 // initialize structure
1625 AssignDomainName(&e
->qname
, qname
);
1627 memset(e
->id
, 0, 8);
1628 e
->state
= RequestReceived
;
1629 e
->AnswerList
= NULL
;
1631 if (lease
< LLQ_MIN_LEASE
) lease
= LLQ_MIN_LEASE
;
1632 else if (lease
> LLQ_MAX_LEASE
) lease
= LLQ_MIN_LEASE
;
1633 gettimeofday(&t
, NULL
);
1634 e
->expire
= t
.tv_sec
+ (int)lease
;
1638 e
->next
= d
->LLQTable
[bucket
];
1639 d
->LLQTable
[bucket
] = e
;
1644 // Handle a refresh request from client
1645 mDNSlocal
void LLQRefresh(DaemonInfo
*d
, LLQEntry
*e
, LLQOptData
*llq
, mDNSOpaque16 msgID
)
1649 mDNSu8
*end
= (mDNSu8
*)&ack
.msg
.data
;
1652 inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addr
, 32);
1653 VLog("%s LLQ for %##s from %s", llq
->lease
? "Refreshing" : "Deleting", e
->qname
.c
, addr
);
1657 if (llq
->lease
< LLQ_MIN_LEASE
) llq
->lease
= LLQ_MIN_LEASE
;
1658 else if (llq
->lease
> LLQ_MAX_LEASE
) llq
->lease
= LLQ_MIN_LEASE
;
1661 ack
.src
.sin_addr
.s_addr
= 0; // unused
1662 InitializeDNSMessage(&ack
.msg
.h
, msgID
, ResponseFlags
);
1663 end
= putQuestion(&ack
.msg
, end
, end
+ AbsoluteMaxDNSMessageData
, &e
->qname
, e
->qtype
, kDNSClass_IN
);
1664 if (!end
) { Log("Error: putQuestion"); return; }
1666 FormatLLQOpt(&opt
, kLLQOp_Refresh
, e
->id
, llq
->lease
? LLQLease(e
) : 0);
1667 end
= PutResourceRecordTTLJumbo(&ack
.msg
, end
, &ack
.msg
.h
.numAdditionals
, &opt
.resrec
, 0);
1668 if (!end
) { Log("Error: PutResourceRecordTTLJumbo"); return; }
1670 ack
.len
= (int)(end
- (mDNSu8
*)&ack
.msg
);
1671 if (SendLLQ(d
, &ack
, e
->cli
)) Log("Error: LLQRefresh");
1673 if (llq
->lease
) e
->state
= Established
;
1674 else DeleteLLQ(d
, e
);
1677 // Complete handshake with Ack an initial answers
1678 mDNSlocal
void LLQCompleteHandshake(DaemonInfo
*d
, LLQEntry
*e
, LLQOptData
*llq
, mDNSOpaque16 msgID
)
1684 mDNSu8
*end
= (mDNSu8
*)&ack
.msg
.data
;
1685 char rrbuf
[80], addrbuf
[32];
1687 inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addr
, 32);
1689 if (memcmp(llq
->id
, e
->id
, 8) ||
1690 llq
->vers
!= kLLQ_Vers
||
1691 llq
->llqOp
!= kLLQOp_Setup
||
1692 llq
->err
!= LLQErr_NoError
||
1693 llq
->lease
> e
->lease
+ LLQ_LEASE_FUDGE
||
1694 llq
->lease
< e
->lease
- LLQ_LEASE_FUDGE
)
1695 { Log("Incorrect challenge response from %s", addr
); return; }
1697 if (e
->state
== Established
) VLog("Retransmitting LLQ ack + answers for %##s", e
->qname
.c
);
1698 else VLog("Delivering LLQ ack + answers for %##s", e
->qname
.c
);
1700 // format ack + answers
1701 ack
.src
.sin_addr
.s_addr
= 0; // unused
1702 InitializeDNSMessage(&ack
.msg
.h
, msgID
, ResponseFlags
);
1703 end
= putQuestion(&ack
.msg
, end
, end
+ AbsoluteMaxDNSMessageData
, &e
->qname
, e
->qtype
, kDNSClass_IN
);
1704 if (!end
) { Log("Error: putQuestion"); return; }
1706 if (e
->state
!= Established
) { SetAnswerList(d
, e
); e
->state
= Established
; }
1708 if (verbose
) inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addrbuf
, 32);
1709 for (ptr
= e
->AnswerList
->KnownAnswers
; ptr
; ptr
= ptr
->next
)
1711 if (verbose
) GetRRDisplayString_rdb(&ptr
->resrec
, &ptr
->resrec
.rdata
->u
, rrbuf
);
1712 VLog("%s Intitial Answer - %s", addr
, rrbuf
);
1713 end
= PutResourceRecordTTLJumbo(&ack
.msg
, end
, &ack
.msg
.h
.numAnswers
, &ptr
->resrec
, 1);
1714 if (!end
) { Log("Error: PutResourceRecordTTLJumbo"); return; }
1717 FormatLLQOpt(&opt
, kLLQOp_Setup
, e
->id
, LLQLease(e
));
1718 end
= PutResourceRecordTTLJumbo(&ack
.msg
, end
, &ack
.msg
.h
.numAdditionals
, &opt
.resrec
, 0);
1719 if (!end
) { Log("Error: PutResourceRecordTTLJumbo"); return; }
1721 ack
.len
= (int)(end
- (mDNSu8
*)&ack
.msg
);
1722 if (SendLLQ(d
, &ack
, e
->cli
)) Log("Error: LLQCompleteHandshake");
1725 mDNSlocal
void LLQSetupChallenge(DaemonInfo
*d
, LLQEntry
*e
, LLQOptData
*llq
, mDNSOpaque16 msgID
)
1730 mDNSu8
*end
= challenge
.msg
.data
;
1733 if (e
->state
== ChallengeSent
) VLog("Retransmitting LLQ setup challenge for %##s", e
->qname
.c
);
1734 else VLog("Sending LLQ setup challenge for %##s", e
->qname
.c
);
1736 if (!ZERO_LLQID(llq
->id
)) { Log("Error: LLQSetupChallenge - nonzero ID"); return; } // server bug
1737 if (llq
->llqOp
!= kLLQOp_Setup
) { Log("LLQSetupChallenge - incorrrect operation from client"); return; } // client error
1739 if (ZERO_LLQID(e
->id
)) // don't regenerate random ID for retransmissions
1741 // construct ID <time><random>
1742 gettimeofday(&t
, NULL
);
1744 memcpy(e
->id
, &t
.tv_sec
, sizeof(t
.tv_sec
));
1745 memcpy(e
->id
+ sizeof(t
.tv_sec
), &randval
, sizeof(randval
));
1748 // format response (query + LLQ opt rr)
1749 challenge
.src
.sin_addr
.s_addr
= 0; // unused
1750 InitializeDNSMessage(&challenge
.msg
.h
, msgID
, ResponseFlags
);
1751 end
= putQuestion(&challenge
.msg
, end
, end
+ AbsoluteMaxDNSMessageData
, &e
->qname
, e
->qtype
, kDNSClass_IN
);
1752 if (!end
) { Log("Error: putQuestion"); return; }
1753 FormatLLQOpt(&opt
, kLLQOp_Setup
, e
->id
, LLQLease(e
));
1754 end
= PutResourceRecordTTLJumbo(&challenge
.msg
, end
, &challenge
.msg
.h
.numAdditionals
, &opt
.resrec
, 0);
1755 if (!end
) { Log("Error: PutResourceRecordTTLJumbo"); return; }
1756 challenge
.len
= (int)(end
- (mDNSu8
*)&challenge
.msg
);
1757 if (SendLLQ(d
, &challenge
, e
->cli
)) { Log("Error: LLQSetupChallenge"); return; }
1758 e
->state
= ChallengeSent
;
1761 // Take action on an LLQ message from client. Entry must be initialized and in table
1762 mDNSlocal
void UpdateLLQ(DaemonInfo
*d
, LLQEntry
*e
, LLQOptData
*llq
, mDNSOpaque16 msgID
)
1766 case RequestReceived
:
1767 LLQSetupChallenge(d
, e
, llq
, msgID
);
1770 if (ZERO_LLQID(llq
->id
)) LLQSetupChallenge(d
, e
, llq
, msgID
); // challenge sent and lost
1771 else LLQCompleteHandshake(d
, e
, llq
, msgID
);
1774 if (ZERO_LLQID(llq
->id
))
1776 // client started over. reset state.
1777 LLQEntry
*newe
= NewLLQ(d
, e
->cli
, &e
->qname
, e
->qtype
, llq
->lease
);
1780 LLQSetupChallenge(d
, newe
, llq
, msgID
);
1783 else if (llq
->llqOp
== kLLQOp_Setup
)
1784 { LLQCompleteHandshake(d
, e
, llq
, msgID
); return; } // Ack lost
1785 else if (llq
->llqOp
== kLLQOp_Refresh
)
1786 { LLQRefresh(d
, e
, llq
, msgID
); return; }
1787 else { Log("Unhandled message for established LLQ"); return; }
1791 mDNSlocal LLQEntry
*LookupLLQ(DaemonInfo
*d
, struct sockaddr_in cli
, domainname
*qname
, mDNSu16 qtype
, mDNSu8
*id
)
1793 int bucket
= bucket
= DomainNameHashValue(qname
) % LLQ_TABLESIZE
;
1794 LLQEntry
*ptr
= d
->LLQTable
[bucket
];
1798 if (((ptr
->state
== ChallengeSent
&& ZERO_LLQID(id
)) || // zero-id due to packet loss OK in state ChallengeSent
1799 !memcmp(id
, ptr
->id
, 8)) && // id match
1800 SAME_INADDR(cli
, ptr
->cli
) && qtype
== ptr
->qtype
&& SameDomainName(&ptr
->qname
, qname
)) // same source, type, qname
1807 mDNSlocal
int RecvLLQ(DaemonInfo
*d
, PktMsg
*pkt
)
1810 LargeCacheRecord opt
;
1813 const mDNSu8
*qptr
= pkt
->msg
.data
;
1814 const mDNSu8
*end
= (mDNSu8
*)&pkt
->msg
+ pkt
->len
;
1815 const mDNSu8
*aptr
= LocateAdditionals(&pkt
->msg
, end
);
1816 LLQOptData
*llq
= NULL
;
1820 inet_ntop(AF_INET
, &pkt
->src
.sin_addr
, addr
, 32);
1822 VLog("Received LLQ msg from %s", addr
);
1823 // sanity-check packet
1824 if (!pkt
->msg
.h
.numQuestions
|| !pkt
->msg
.h
.numAdditionals
)
1826 Log("Malformatted LLQ from %s with %d questions, %d additionals", addr
, pkt
->msg
.h
.numQuestions
, pkt
->msg
.h
.numAdditionals
);
1830 // find the OPT RR - must be last in message
1831 for (i
= 0; i
< pkt
->msg
.h
.numAdditionals
; i
++)
1833 aptr
= GetLargeResourceRecord(NULL
, &pkt
->msg
, aptr
, end
, 0, kDNSRecordTypePacketAdd
, &opt
);
1834 if (!aptr
) { Log("Malformatted LLQ from %s: could not get Additional record %d", addr
, i
); goto end
; }
1838 if (opt
.r
.resrec
.rrtype
!= kDNSType_OPT
) { Log("Malformatted LLQ from %s: last Additional not an Opt RR", addr
); goto end
; }
1839 if (opt
.r
.resrec
.rdlength
< pkt
->msg
.h
.numQuestions
* LLQ_OPT_RDLEN
) { Log("Malformatted LLQ from %s: Opt RR to small (%d bytes for %d questions)", addr
, opt
.r
.resrec
.rdlength
, pkt
->msg
.h
.numQuestions
); }
1841 // dispatch each question
1842 for (i
= 0; i
< pkt
->msg
.h
.numQuestions
; i
++)
1844 qptr
= getQuestion(&pkt
->msg
, qptr
, end
, 0, &q
);
1845 if (!qptr
) { Log("Malformatted LLQ from %s: cannot read question %d", addr
, i
); goto end
; }
1846 llq
= (LLQOptData
*)&opt
.r
.resrec
.rdata
->u
.opt
.OptData
.llq
+ i
; // point into OptData at index i
1847 if (llq
->vers
!= kLLQ_Vers
) { Log("LLQ from %s contains bad version %d (expected %d)", addr
, llq
->vers
, kLLQ_Vers
); goto end
; }
1849 e
= LookupLLQ(d
, pkt
->src
, &q
.qname
, q
.qtype
, llq
->id
);
1852 // no entry - if zero ID, create new
1853 e
= NewLLQ(d
, pkt
->src
, &q
.qname
, q
.qtype
, llq
->lease
);
1856 UpdateLLQ(d
, e
, llq
, pkt
->msg
.h
.id
);
1865 mDNSlocal mDNSBool
IsLLQRequest(PktMsg
*pkt
)
1867 const mDNSu8
*ptr
= NULL
, *end
= (mDNSu8
*)&pkt
->msg
+ pkt
->len
;
1868 LargeCacheRecord lcr
;
1870 mDNSBool result
= mDNSfalse
;
1873 if ((mDNSu8
)(pkt
->msg
.h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
) != (mDNSu8
)(kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
)) goto end
;
1875 if (!pkt
->msg
.h
.numAdditionals
) goto end
;
1876 ptr
= LocateAdditionals(&pkt
->msg
, end
);
1879 // find last Additional
1880 for (i
= 0; i
< pkt
->msg
.h
.numAdditionals
; i
++)
1882 ptr
= GetLargeResourceRecord(NULL
, &pkt
->msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &lcr
);
1883 if (!ptr
) { Log("Unable to read additional record"); goto end
; }
1886 if (lcr
.r
.resrec
.rrtype
== kDNSType_OPT
&&
1887 lcr
.r
.resrec
.rdlength
>= LLQ_OPT_RDLEN
&&
1888 lcr
.r
.resrec
.rdata
->u
.opt
.opt
== kDNSOpt_LLQ
)
1889 { result
= mDNStrue
; goto end
; }
1896 // !!!KRS implement properly
1897 mDNSlocal mDNSBool
IsLLQAck(PktMsg
*pkt
)
1899 if (pkt
->msg
.h
.flags
.NotAnInteger
== ResponseFlags
.NotAnInteger
&&
1900 pkt
->msg
.h
.numQuestions
&& !pkt
->msg
.h
.numAnswers
&& !pkt
->msg
.h
.numAuthorities
) return mDNStrue
;
1905 // request handler wrappers for TCP and UDP requests
1906 // (read message off socket, fork thread that invokes main processing routine and handles cleanup)
1907 mDNSlocal
void *UDPUpdateRequestForkFn(void *vptr
)
1910 UDPRequestArgs
*req
= vptr
;
1911 PktMsg
*reply
= NULL
;
1913 VLog("Received UDP request: %d bytes from %s", req
->pkt
.len
, inet_ntop(AF_INET
, &req
->pkt
.src
.sin_addr
, buf
, 32));
1914 //!!!KRS strictly speaking, we shouldn't use TCP for a UDP request because the server may give us a long answer that would require truncation for UDP delivery to client
1915 reply
= HandleRequest(&req
->pkt
, req
->d
);
1918 if (sendto(req
->d
->udpsd
, &reply
->msg
, reply
->len
, 0, (struct sockaddr
*)&req
->pkt
.src
, sizeof(req
->pkt
.src
)) != (int)reply
->len
)
1919 LogErr("UDPUpdateRequestForkFn", "sendto");
1922 if (reply
) free(reply
);
1927 //!!!KRS this needs to be changed to use non-blocking sockets
1928 mDNSlocal
int RecvUDPRequest(int sd
, DaemonInfo
*d
)
1930 UDPRequestArgs
*req
;
1932 unsigned int clisize
= sizeof(req
->cliaddr
);
1934 req
= malloc(sizeof(UDPRequestArgs
));
1935 if (!req
) { LogErr("RecvUDPRequest", "malloc"); return -1; }
1936 bzero(req
, sizeof(*req
));
1938 req
->pkt
.len
= recvfrom(sd
, &req
->pkt
.msg
, sizeof(req
->pkt
.msg
), 0, (struct sockaddr
*)&req
->cliaddr
, &clisize
);
1939 if ((int)req
->pkt
.len
< 0) { LogErr("RecvUDPRequest", "recvfrom"); free(req
); return -1; }
1940 if (clisize
!= sizeof(req
->cliaddr
)) { Log("Client address of unknown size %d", clisize
); free(req
); return -1; }
1941 req
->pkt
.src
= req
->cliaddr
;
1943 if (IsLLQRequest(&req
->pkt
))
1945 // LLQ messages handled by main thread
1946 int err
= RecvLLQ(d
, &req
->pkt
);
1951 if (IsLLQAck(&req
->pkt
)) { free(req
); return 0; } // !!!KRS need to do acks + retrans
1953 if (pthread_create(&tid
, NULL
, UDPUpdateRequestForkFn
, req
)) { LogErr("RecvUDPRequest", "pthread_create"); free(req
); return -1; }
1954 pthread_detach(tid
);
1958 mDNSlocal
void *TCPRequestForkFn(void *vptr
)
1960 TCPRequestArgs
*req
= vptr
;
1961 PktMsg
*in
= NULL
, *out
= NULL
;
1964 //!!!KRS if this read blocks indefinitely, we can run out of threads
1966 in
= ReadTCPMsg(req
->sd
, NULL
);
1969 LogMsg("TCPRequestForkFn: Could not read message from %s", inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
1973 VLog("Received TCP request: %d bytes from %s", in
->len
, inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
1975 out
= HandleRequest(in
, req
->d
);
1978 LogMsg("TCPRequestForkFn: No reply for client %s", inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
1982 // deliver reply to client
1983 if (SendTCPMsg(req
->sd
, out
) < 0)
1985 LogMsg("TCPRequestForkFn: Unable to send reply to client %s", inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
1996 mDNSlocal
int RecvTCPRequest(int sd
, DaemonInfo
*d
)
1998 TCPRequestArgs
*req
;
2000 unsigned int clilen
= sizeof(req
->cliaddr
);
2002 req
= malloc(sizeof(TCPRequestArgs
));
2003 if (!req
) { LogErr("RecvTCPRequest", "malloc"); return -1; }
2004 bzero(req
, sizeof(*req
));
2006 req
->sd
= accept(sd
, (struct sockaddr
*)&req
->cliaddr
, &clilen
);
2007 if (req
->sd
< 0) { LogErr("RecvTCPRequest", "accept"); return -1; }
2008 if (clilen
!= sizeof(req
->cliaddr
)) { Log("Client address of unknown size %d", clilen
); free(req
); return -1; }
2009 if (pthread_create(&tid
, NULL
, TCPRequestForkFn
, req
)) { LogErr("RecvTCPRequest", "pthread_create"); free(req
); return -1; }
2010 pthread_detach(tid
);
2015 // listen for incoming requests, periodically check table for expired records, respond to signals
2016 mDNSlocal
int ListenForUpdates(DaemonInfo
*d
)
2021 struct timeval timenow
, timeout
= { 0, 0 };
2022 long NextTableCheck
= 0;
2024 VLog("Listening for requests...");
2027 maxfdp1
= d
->tcpsd
+ 1;
2028 if (d
->udpsd
+ 1 > maxfdp1
) maxfdp1
= d
->udpsd
+ 1;
2029 if (d
->LLQEventListenSock
+ 1 > maxfdp1
) maxfdp1
= d
->LLQEventListenSock
+ 1;
2033 // expire records if necessary, set timeout
2034 if (gettimeofday(&timenow
, NULL
)) { LogErr("ListenForUpdates", "gettimeofday"); return -1; }
2035 if (timenow
.tv_sec
>= NextTableCheck
)
2037 DeleteExpiredRecords(d
);
2038 NextTableCheck
= timenow
.tv_sec
+ EXPIRATION_INTERVAL
;
2040 timeout
.tv_sec
= NextTableCheck
- timenow
.tv_sec
;
2042 FD_SET(d
->tcpsd
, &rset
);
2043 FD_SET(d
->udpsd
, &rset
);
2044 FD_SET(d
->LLQEventListenSock
, &rset
);
2046 err
= select(maxfdp1
, &rset
, NULL
, NULL
, &timeout
);
2051 if (terminate
) { DeleteExpiredRecords(d
); return 0; }
2052 else if (dumptable
) { PrintLeaseTable(d
); PrintLLQTable(d
); dumptable
= 0; }
2053 else Log("Received unhandled signal - continuing");
2055 else { LogErr("ListenForUpdates", "select"); return -1; }
2059 if (FD_ISSET(d
->tcpsd
, &rset
)) RecvTCPRequest(d
->tcpsd
, d
);
2060 if (FD_ISSET(d
->udpsd
, &rset
)) RecvUDPRequest(d
->udpsd
, d
);
2061 if (FD_ISSET(d
->LLQEventListenSock
, &rset
))
2063 // clear signalling data off socket
2065 recv(d
->LLQEventListenSock
, buf
, 32, 0);
2073 // signal handler sets global variables, which are inspected by main event loop
2074 // (select automatically returns due to the handled signal)
2075 mDNSlocal
void HndlSignal(int sig
)
2077 if (sig
== SIGTERM
|| sig
== SIGINT
) { terminate
= 1; return; }
2078 if (sig
== INFO_SIGNAL
) { dumptable
= 1; return; }
2081 int main(int argc
, char *argv
[])
2086 d
= malloc(sizeof(*d
));
2087 if (!d
) { LogErr("main", "malloc"); exit(1); }
2088 bzero(d
, sizeof(DaemonInfo
));
2090 if (signal(SIGTERM
, HndlSignal
) == SIG_ERR
) perror("Can't catch SIGTERM");
2091 if (signal(INFO_SIGNAL
, HndlSignal
) == SIG_ERR
) perror("Can't catch SIGINFO");
2092 if (signal(SIGINT
, HndlSignal
) == SIG_ERR
) perror("Can't catch SIGINT");
2093 if (signal(SIGPIPE
, SIG_IGN
) == SIG_ERR
) perror("Can't ignore SIGPIPE");
2095 if (ProcessArgs(argc
, argv
, d
) < 0) exit(1);
2101 LogErr("main", "daemon");
2102 fprintf(stderr
, "Could not daemonize process, running in foreground");
2107 if (InitLeaseTable(d
) < 0) exit(1);
2108 if (SetupSockets(d
) < 0) exit(1);
2109 if (SetUpdateSRV(d
) < 0) exit(1);
2111 if (pthread_create(&LLQtid
, NULL
, LLQEventMonitor
, d
)) { LogErr("main", "pthread_create"); }
2114 pthread_detach(LLQtid
);
2115 ListenForUpdates(d
);
2118 if (ClearUpdateSRV(d
) < 0) exit(1); // clear update srv's even if ListenForUpdates or pthread_create returns an error