1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 Change History (most recent first):
20 Revision 1.33.2.2 2006/08/29 06:24:34 cheshire
21 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
23 Revision 1.33.2.1 2005/08/05 21:14:00 ksekar
24 <rdar://problem/4012279> Long-lived queries not working on windows
27 Revision 1.33 2005/03/11 19:09:02 ksekar
28 Fixed ZERO_LLQID macro
30 Revision 1.32 2005/03/10 22:54:33 ksekar
31 <rdar://problem/4046285> dnsextd leaks memory/ports
33 Revision 1.31 2005/02/24 02:37:57 ksekar
34 <rdar://problem/4021977> dnsextd memory management improvements
36 Revision 1.30 2005/01/27 22:57:56 cheshire
37 Fix compile errors on gcc4
39 Revision 1.29 2004/12/22 00:13:50 ksekar
40 <rdar://problem/3873993> Change version, port, and polling interval for LLQ
42 Revision 1.28 2004/12/17 00:30:00 ksekar
43 <rdar://problem/3924045> dnsextd memory leak
45 Revision 1.27 2004/12/17 00:27:32 ksekar
48 Revision 1.26 2004/12/17 00:21:33 ksekar
49 Fixes for new CacheRecord structure with indirect name pointer
51 Revision 1.25 2004/12/16 20:13:02 cheshire
52 <rdar://problem/3324626> Cache memory management improvements
54 Revision 1.24 2004/12/14 17:09:06 ksekar
55 fixed incorrect usage instructions
57 Revision 1.23 2004/12/06 20:24:31 ksekar
58 <rdar://problem/3907303> dnsextd leaks sockets
60 Revision 1.22 2004/12/03 20:20:29 ksekar
61 <rdar://problem/3904149> dnsextd: support delivery of large records via LLQ events
63 Revision 1.21 2004/12/03 06:11:34 ksekar
64 <rdar://problem/3885059> clean up dnsextd arguments
66 Revision 1.20 2004/12/01 04:27:28 cheshire
67 <rdar://problem/3872803> Darwin patches for Solaris and Suse
68 Don't use uint32_t, etc. -- they require stdint.h, which doesn't exist on FreeBSD 4.x, Solaris, etc.
70 Revision 1.19 2004/12/01 01:16:29 cheshire
71 Solaris compatibility fixes
73 Revision 1.18 2004/11/30 23:51:06 cheshire
74 Remove double semicolons
76 Revision 1.17 2004/11/30 22:37:01 cheshire
77 Update copyright dates and add "Mode: C; tab-width: 4" headers
79 Revision 1.16 2004/11/25 02:02:28 ksekar
80 Fixed verbose log message argument
82 Revision 1.15 2004/11/19 02:35:02 ksekar
83 <rdar://problem/3886317> Wide Area Security: Add LLQ-ID to events
85 Revision 1.14 2004/11/17 06:17:58 cheshire
86 Update comments to show correct SRV names: _dns-update._udp.<zone>. and _dns-llq._udp.<zone>.
88 Revision 1.13 2004/11/13 02:22:36 ksekar
89 <rdar://problem/3878201> Refresh Acks from daemon malformatted
91 Revision 1.12 2004/11/12 01:05:01 ksekar
92 <rdar://problem/3876757> dnsextd: daemon registers the SRV same record
95 Revision 1.11 2004/11/12 01:03:31 ksekar
96 <rdar://problem/3876776> dnsextd: KnownAnswers (CacheRecords) leaked
98 Revision 1.10 2004/11/12 00:35:28 ksekar
99 <rdar://problem/3876705> dnsextd: uninitialized pointer can cause crash
101 Revision 1.9 2004/11/10 20:38:17 ksekar
102 <rdar://problem/3874168> dnsextd: allow a "fudge" in LLQ lease echo
104 Revision 1.8 2004/11/01 17:48:14 cheshire
105 Changed SOA serial number back to signed. RFC 1035 may describe it as "unsigned", but
106 it's wrong. The SOA serial is a modular counter, as explained in "DNS & BIND", page
107 137. Since C doesn't have a modular type, we used signed, C's closest approximation.
109 Revision 1.7 2004/10/30 00:06:58 ksekar
110 <rdar://problem/3722535> Support Long Lived Queries in DNS Extension daemon
112 Revision 1.6 2004/09/17 01:08:54 cheshire
113 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
114 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
115 declared in that file are ONLY appropriate to single-address-space embedded applications.
116 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
118 Revision 1.5 2004/09/16 00:50:54 cheshire
119 Don't use MSG_WAITALL -- it returns "Invalid argument" on some Linux versions
121 Revision 1.4 2004/09/14 23:27:48 cheshire
124 Revision 1.3 2004/09/02 01:39:40 cheshire
125 For better readability, follow consistent convention that QR bit comes first, followed by OP bits
127 Revision 1.2 2004/08/24 23:27:57 cheshire
128 Fixes for Linux compatibility:
131 Don't try to set servaddr.sin_len on platforms that don't have sa_len
133 Revision 1.1 2004/08/11 00:43:26 ksekar
134 <rdar://problem/3722542>: DNS Extension daemon for DNS Update Lease
138 #include "../mDNSCore/mDNSEmbeddedAPI.h"
139 #include "../mDNSCore/DNSCommon.h"
140 #include "../mDNSCore/mDNS.c"
141 //!!!KRS we #include mDNS.c for the various constants defined there - we should move these to DNSCommon.h
147 #include <sys/types.h>
148 #include <sys/socket.h>
149 #include <netinet/in.h>
150 #include <arpa/inet.h>
154 #include <sys/time.h>
158 // Compatibility workaround
160 #define AF_LOCAL AF_UNIX
167 #define LOOPBACK "127.0.0.1"
169 #define DAEMON_PORT 5352 // default, may be overridden via command line argument
170 #define LISTENQ 128 // tcp connection backlog
171 #define RECV_BUFLEN 9000
172 #define LEASETABLE_INIT_NBUCKETS 256 // initial hashtable size (doubles as table fills)
173 #define LLQ_TABLESIZE 1024 // !!!KRS make this dynamically growable
174 #define EXPIRATION_INTERVAL 300 // check for expired records every 5 minutes
175 #define SRV_TTL 7200 // TTL For _dns-update SRV records
177 // LLQ Lease bounds (seconds)
178 #define LLQ_MIN_LEASE (15 * 60)
179 #define LLQ_MAX_LEASE (120 * 60)
180 #define LLQ_LEASE_FUDGE 60
182 // LLQ SOA poll interval (microseconds)
183 #define LLQ_MONITOR_ERR_INTERVAL (60 * 1000000)
184 #define LLQ_MONITOR_INTERVAL 250000
186 #define INFO_SIGNAL SIGINFO
188 #define INFO_SIGNAL SIGUSR1
191 #define SAME_INADDR(x,y) (*((mDNSu32 *)&x) == *((mDNSu32 *)&y))
192 #define ZERO_LLQID(x) (!memcmp(x, "\x0\x0\x0\x0\x0\x0\x0\x0", 8))
196 // Structs/fields that must be locked for thread safety are explicitly commented
201 struct sockaddr_in src
;
204 // Note: extra storage for oversized (TCP) messages goes here
208 typedef struct RRTableElem
210 struct RRTableElem
*next
;
211 struct sockaddr_in cli
; // client's source address
212 long expire
; // expiration time, in seconds since epoch
213 domainname zone
; // from zone field of update message
214 domainname name
; // name of the record
215 CacheRecord rr
; // last field in struct allows for allocation of oversized RRs
225 typedef struct AnswerListElem
227 struct AnswerListElem
*next
;
230 CacheRecord
*KnownAnswers
; // All valid answers delivered to client
231 CacheRecord
*EventList
; // New answers (adds/removes) to be sent to client
236 typedef struct LLQEntry
238 struct LLQEntry
*next
;
239 struct sockaddr_in cli
; // clien'ts source address
244 mDNSu32 lease
; // original lease, in seconds
245 mDNSs32 expire
; // expiration, absolute, in seconds since epoch
246 AnswerListElem
*AnswerList
;
249 // daemon-wide information
252 // server variables - read only after initialization (no locking)
253 struct in_addr saddr
; // server address
254 domainname zone
; // zone being updated
255 int tcpsd
; // listening TCP socket
256 int udpsd
; // listening UDP socket
258 // daemon variables - read only after initialization (no locking)
259 uDNS_AuthInfo
*AuthInfo
; // linked list of keys for signing deletion updates
260 mDNSIPPort port
; // listening port
262 // lease table variables (locked via mutex after initialization)
263 RRTableElem
**table
; // hashtable for records with leases
264 pthread_mutex_t tablelock
; // mutex for lease table
265 mDNSs32 nbuckets
; // buckets allocated
266 mDNSs32 nelems
; // elements in table
268 // LLQ table variables
269 LLQEntry
*LLQTable
[LLQ_TABLESIZE
]; // !!!KRS change this and RRTable to use a common data structure
270 AnswerListElem
*AnswerTable
[LLQ_TABLESIZE
];
271 int LLQEventListenSock
; // Unix domain socket pair - polling thread writes to ServPollSock, which wakes
272 int LLQServPollSock
; // the main thread listening on EventListenSock, indicating that the zone has changed
275 // args passed to UDP request handler thread as void*
279 struct sockaddr_in cliaddr
;
283 // args passed to TCP request handler thread as void*
286 int sd
; // socket connected to client
287 struct sockaddr_in cliaddr
;
295 // booleans to determine runtime output
296 // read-only after initialization (no mutex protection)
297 static mDNSBool foreground
= 0;
298 static mDNSBool verbose
= 0;
300 // globals set via signal handler (accessed exclusively by main select loop and signal handler)
301 static mDNSBool terminate
= 0;
302 static mDNSBool dumptable
= 0;
306 // Log messages are delivered to syslog unless -f option specified
309 // common message logging subroutine
310 mDNSlocal
void PrintLog(const char *buffer
)
314 fprintf(stderr
,"%s\n", buffer
);
319 openlog("dnsextd", LOG_CONS
| LOG_PERROR
, LOG_DAEMON
);
320 syslog(LOG_ERR
, "%s", buffer
);
325 // Verbose Logging (conditional on -v option)
326 mDNSlocal
void VLog(const char *format
, ...)
331 if (!verbose
) return;
332 va_start(ptr
,format
);
333 buffer
[mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
)] = 0;
338 // Unconditional Logging
339 mDNSlocal
void Log(const char *format
, ...)
344 va_start(ptr
,format
);
345 buffer
[mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
)] = 0;
351 // prints message "dnsextd <function>: <operation> - <error message>"
352 // must be compiled w/ -D_REENTRANT for thread-safe errno usage
353 mDNSlocal
void LogErr(const char *fn
, const char *operation
)
356 snprintf(buf
, sizeof(buf
), "%s: %s - %s", fn
, operation
, strerror(errno
));
361 // Networking Utility Routines
364 // Convert DNS Message Header from Network to Host byte order
365 mDNSlocal
void HdrNToH(PktMsg
*pkt
)
367 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
368 mDNSu8
*ptr
= (mDNSu8
*)&pkt
->msg
.h
.numQuestions
;
369 pkt
->msg
.h
.numQuestions
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
370 pkt
->msg
.h
.numAnswers
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
371 pkt
->msg
.h
.numAuthorities
= (mDNSu16
)((mDNSu16
)ptr
[4] << 8 | ptr
[5]);
372 pkt
->msg
.h
.numAdditionals
= (mDNSu16
)((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
375 // Convert DNS Message Header from Host to Network byte order
376 mDNSlocal
void HdrHToN(PktMsg
*pkt
)
378 mDNSu16 numQuestions
= pkt
->msg
.h
.numQuestions
;
379 mDNSu16 numAnswers
= pkt
->msg
.h
.numAnswers
;
380 mDNSu16 numAuthorities
= pkt
->msg
.h
.numAuthorities
;
381 mDNSu16 numAdditionals
= pkt
->msg
.h
.numAdditionals
;
382 mDNSu8
*ptr
= (mDNSu8
*)&pkt
->msg
.h
.numQuestions
;
384 // Put all the integer values in IETF byte-order (MSB first, LSB second)
385 *ptr
++ = (mDNSu8
)(numQuestions
>> 8);
386 *ptr
++ = (mDNSu8
)(numQuestions
& 0xFF);
387 *ptr
++ = (mDNSu8
)(numAnswers
>> 8);
388 *ptr
++ = (mDNSu8
)(numAnswers
& 0xFF);
389 *ptr
++ = (mDNSu8
)(numAuthorities
>> 8);
390 *ptr
++ = (mDNSu8
)(numAuthorities
& 0xFF);
391 *ptr
++ = (mDNSu8
)(numAdditionals
>> 8);
392 *ptr
++ = (mDNSu8
)(numAdditionals
& 0xFF);
395 // create a socket connected to nameserver
396 // caller terminates connection via close()
397 mDNSlocal
int ConnectToServer(DaemonInfo
*d
)
399 struct sockaddr_in servaddr
;
402 bzero(&servaddr
, sizeof(servaddr
));
403 if (d
->saddr
.s_addr
) servaddr
.sin_addr
= d
->saddr
;
404 else inet_pton(AF_INET
, LOOPBACK
, &d
->saddr
); // use loopback if server not explicitly specified
405 servaddr
.sin_port
= htons(NS_PORT
);
406 servaddr
.sin_family
= AF_INET
;
407 #ifndef NOT_HAVE_SA_LEN
408 servaddr
.sin_len
= sizeof(servaddr
);
410 sd
= socket(AF_INET
, SOCK_STREAM
, 0);
411 if (sd
< 0) { LogErr("ConnectToServer", "socket"); return -1; }
412 if (connect(sd
, (struct sockaddr
*)&servaddr
, sizeof(servaddr
)) < 0) { LogErr("ConnectToServer", "connect"); return -1; }
416 // send an entire block of data over a connected socket, blocking if buffers are full
417 mDNSlocal
int MySend(int sd
, const void *msg
, int len
)
423 n
= send(sd
, (char *)msg
+ nsent
, len
- nsent
, 0);
424 if (n
< 0) { LogErr("MySend", "send"); return -1; }
430 // Transmit a DNS message, prefixed by its length, over TCP, blocking if necessary
431 mDNSlocal
int SendTCPMsg(int sd
, PktMsg
*pkt
)
433 // send the lenth, in network byte order
434 mDNSu16 len
= htons((mDNSu16
)pkt
->len
);
435 if (MySend(sd
, &len
, sizeof(len
)) < 0) return -1;
438 return MySend(sd
, &pkt
->msg
, pkt
->len
);
441 // Receive len bytes, waiting until we have all of them.
442 // Returns number of bytes read (which should always be the number asked for).
443 static int my_recv(const int sd
, void *const buf
, const int len
)
445 // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions;
446 // use an explicit while() loop instead.
447 // Also, don't try to do '+=' arithmetic on the original "void *" pointer --
448 // arithmetic on "void *" pointers is compiler-dependent.
450 char *ptr
= (char *)buf
;
453 ssize_t num_read
= recv(sd
, ptr
, remaining
, 0);
454 if ((num_read
== 0) || (num_read
< 0) || (num_read
> remaining
)) return -1;
456 remaining
-= num_read
;
461 // Return a DNS Message read off of a TCP socket, or NULL on failure
462 // If storage is non-null, result is placed in that buffer. Otherwise,
463 // returned value is allocated with Malloc, and contains sufficient extra
464 // storage for a Lease OPT RR
466 mDNSlocal PktMsg
*ReadTCPMsg(int sd
, PktMsg
*storage
)
468 int nread
, allocsize
;
473 nread
= my_recv(sd
, &msglen
, sizeof(msglen
));
474 if (nread
< 0) { LogErr("TCPRequestForkFn", "recv"); goto error
; }
475 msglen
= ntohs(msglen
);
476 if (nread
!= sizeof(msglen
)) { Log("Could not read length field of message"); goto error
; }
480 if (msglen
> sizeof(storage
->msg
)) { Log("ReadTCPMsg: provided buffer too small."); goto error
; }
485 // buffer extra space to add an OPT RR
486 if (msglen
> sizeof(DNSMessage
)) allocsize
= sizeof(PktMsg
) - sizeof(DNSMessage
) + msglen
;
487 else allocsize
= sizeof(PktMsg
);
488 pkt
= malloc(allocsize
);
489 if (!pkt
) { LogErr("ReadTCPMsg", "malloc"); goto error
; }
490 bzero(pkt
, sizeof(*pkt
));
494 srclen
= sizeof(pkt
->src
);
495 if (getpeername(sd
, (struct sockaddr
*)&pkt
->src
, &srclen
) ||
496 srclen
!= sizeof(pkt
->src
)) { LogErr("ReadTCPMsg", "getpeername"); bzero(&pkt
->src
, sizeof(pkt
->src
)); }
497 nread
= my_recv(sd
, &pkt
->msg
, msglen
);
498 if (nread
< 0) { LogErr("TCPRequestForkFn", "recv"); goto error
; }
499 if (nread
!= msglen
) { Log("Could not read entire message"); goto error
; }
500 if (pkt
->len
< sizeof(DNSMessageHeader
))
501 { Log("ReadTCPMsg: Message too short (%d bytes)", pkt
->len
); goto error
; }
504 //!!!KRS convert to HBO here?
506 if (pkt
&& pkt
!= storage
) free(pkt
);
511 // Dynamic Update Utility Routines
514 // Get the lease life of records in a dynamic update
515 // returns -1 on error or if no lease present
516 mDNSlocal mDNSs32
GetPktLease(PktMsg
*pkt
)
519 const mDNSu8
*ptr
= NULL
, *end
= (mDNSu8
*)&pkt
->msg
+ pkt
->len
;
520 LargeCacheRecord lcr
;
524 ptr
= LocateAdditionals(&pkt
->msg
, end
);
526 for (i
= 0; i
< pkt
->msg
.h
.numAdditionals
; i
++)
528 ptr
= GetLargeResourceRecord(NULL
, &pkt
->msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &lcr
);
529 if (!ptr
) { Log("Unable to read additional record"); break; }
530 if (lcr
.r
.resrec
.rrtype
== kDNSType_OPT
)
532 if (lcr
.r
.resrec
.rdlength
< LEASE_OPT_RDLEN
) continue;
533 if (lcr
.r
.resrec
.rdata
->u
.opt
.opt
!= kDNSOpt_Lease
) continue;
534 lease
= (mDNSs32
)lcr
.r
.resrec
.rdata
->u
.opt
.OptData
.lease
;
543 // check if a request and server response complete a successful dynamic update
544 mDNSlocal mDNSBool
SuccessfulUpdateTransaction(PktMsg
*request
, PktMsg
*reply
)
547 char *vlogmsg
= NULL
;
550 if (!request
|| !reply
) { vlogmsg
= "NULL message"; goto failure
; }
551 if (request
->len
< sizeof(DNSMessageHeader
) || reply
->len
< sizeof(DNSMessageHeader
)) { vlogmsg
= "Malformatted message"; goto failure
; }
553 // check request operation
554 if ((request
->msg
.h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
) != (request
->msg
.h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
))
555 { vlogmsg
= "Request opcode not an update"; goto failure
; }
558 if ((reply
->msg
.h
.flags
.b
[1] & kDNSFlag1_RC
)) { vlogmsg
= "Reply contains non-zero rcode"; goto failure
; }
559 if ((reply
->msg
.h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
) != (kDNSFlag0_OP_Update
| kDNSFlag0_QR_Response
))
560 { vlogmsg
= "Reply opcode not an update response"; goto failure
; }
562 VLog("Successful update from %s", inet_ntop(AF_INET
, &request
->src
.sin_addr
, buf
, 32));
566 VLog("Request %s: %s", inet_ntop(AF_INET
, &request
->src
.sin_addr
, buf
, 32), vlogmsg
);
570 // Allocate an appropriately sized CacheRecord and copy data from original.
571 // Name pointer in CacheRecord object is set to point to the name specified
573 mDNSlocal CacheRecord
*CopyCacheRecord(const CacheRecord
*orig
, domainname
*name
)
576 size_t size
= sizeof(*cr
);
577 if (orig
->resrec
.rdlength
> InlineCacheRDSize
) size
+= orig
->resrec
.rdlength
- InlineCacheRDSize
;
579 if (!cr
) { LogErr("CopyCacheRecord", "malloc"); return NULL
; }
580 memcpy(cr
, orig
, size
);
581 cr
->resrec
.rdata
= (RData
*)&cr
->rdatastorage
;
582 cr
->resrec
.name
= name
;
589 // Lease Hashtable Utility Routines
592 // double hash table size
593 // caller must lock table prior to invocation
594 mDNSlocal
void RehashTable(DaemonInfo
*d
)
596 RRTableElem
*ptr
, *tmp
, **new;
597 int i
, bucket
, newnbuckets
= d
->nbuckets
* 2;
599 VLog("Rehashing lease table (new size %d buckets)", newnbuckets
);
600 new = malloc(sizeof(RRTableElem
*) * newnbuckets
);
601 if (!new) { LogErr("RehashTable", "malloc"); return; }
602 bzero(new, newnbuckets
* sizeof(RRTableElem
*));
604 for (i
= 0; i
< d
->nbuckets
; i
++)
609 bucket
= ptr
->rr
.resrec
.namehash
% newnbuckets
;
612 tmp
->next
= new[bucket
];
616 d
->nbuckets
= newnbuckets
;
621 // print entire contents of hashtable, invoked via SIGINFO
622 mDNSlocal
void PrintLeaseTable(DaemonInfo
*d
)
626 char rrbuf
[80], addrbuf
[16];
630 if (gettimeofday(&now
, NULL
)) { LogErr("PrintTable", "gettimeofday"); return; }
631 if (pthread_mutex_lock(&d
->tablelock
)) { LogErr("PrintTable", "pthread_mutex_lock"); return; }
633 Log("Dumping Lease Table Contents (table contains %d resource records)", d
->nelems
);
634 for (i
= 0; i
< d
->nbuckets
; i
++)
636 for (ptr
= d
->table
[i
]; ptr
; ptr
= ptr
->next
)
638 hr
= ((ptr
->expire
- now
.tv_sec
) / 60) / 60;
639 min
= ((ptr
->expire
- now
.tv_sec
) / 60) % 60;
640 sec
= (ptr
->expire
- now
.tv_sec
) % 60;
641 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
,
642 GetRRDisplayString_rdb(&ptr
->rr
.resrec
, &ptr
->rr
.resrec
.rdata
->u
, rrbuf
));
645 pthread_mutex_unlock(&d
->tablelock
);
649 // Startup SRV Registration Routines
650 // Register _dns-update._udp/_tcp.<zone> SRV records indicating the port on which
651 // the daemon accepts requests
654 // delete all RRS of a given name/type
655 mDNSlocal mDNSu8
*putRRSetDeletion(DNSMessage
*msg
, mDNSu8
*ptr
, mDNSu8
*limit
, ResourceRecord
*rr
)
657 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->name
);
658 if (!ptr
|| ptr
+ 10 >= limit
) return NULL
; // out of space
659 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8);
660 ptr
[1] = (mDNSu8
)(rr
->rrtype
& 0xFF);
661 ptr
[2] = (mDNSu8
)((mDNSu16
)kDNSQClass_ANY
>> 8);
662 ptr
[3] = (mDNSu8
)((mDNSu16
)kDNSQClass_ANY
& 0xFF);
663 bzero(ptr
+4, sizeof(rr
->rroriginalttl
) + sizeof(rr
->rdlength
)); // zero ttl/rdata
664 msg
->h
.mDNS_numUpdates
++;
668 mDNSlocal mDNSu8
*PutUpdateSRV(DaemonInfo
*d
, PktMsg
*pkt
, mDNSu8
*ptr
, char *regtype
, mDNSBool registration
)
671 char hostname
[1024], buf
[80];
672 mDNSu8
*end
= (mDNSu8
*)&pkt
->msg
+ sizeof(DNSMessage
);
674 mDNS_SetupResourceRecord(&rr
, NULL
, 0, kDNSType_SRV
, SRV_TTL
, kDNSRecordTypeUnique
, NULL
, NULL
);
675 rr
.resrec
.rrclass
= kDNSClass_IN
;
676 rr
.resrec
.rdata
->u
.srv
.priority
= 0;
677 rr
.resrec
.rdata
->u
.srv
.weight
= 0;
678 rr
.resrec
.rdata
->u
.srv
.port
.NotAnInteger
= d
->port
.NotAnInteger
;
679 if (!gethostname(hostname
, 1024) < 0 || MakeDomainNameFromDNSNameString(&rr
.resrec
.rdata
->u
.srv
.target
, hostname
))
680 rr
.resrec
.rdata
->u
.srv
.target
.c
[0] = '\0';
682 MakeDomainNameFromDNSNameString(rr
.resrec
.name
, regtype
);
683 AppendDomainName(rr
.resrec
.name
, &d
->zone
);
684 VLog("%s %s", registration
? "Registering SRV record" : "Deleting existing RRSet",
685 GetRRDisplayString_rdb(&rr
.resrec
, &rr
.resrec
.rdata
->u
, buf
));
686 if (registration
) ptr
= PutResourceRecord(&pkt
->msg
, ptr
, &pkt
->msg
.h
.mDNS_numUpdates
, &rr
.resrec
);
687 else ptr
= putRRSetDeletion(&pkt
->msg
, ptr
, end
, &rr
.resrec
);
692 // perform dynamic update.
693 // specify deletion by passing false for the register parameter, otherwise register the records.
694 mDNSlocal
int UpdateSRV(DaemonInfo
*d
, mDNSBool registration
)
699 mDNSu8
*ptr
= pkt
.msg
.data
;
700 mDNSu8
*end
= (mDNSu8
*)&pkt
.msg
+ sizeof(DNSMessage
);
701 mDNSu16 nAdditHBO
; // num additionas, in host byte order, required by message digest routine
702 PktMsg
*reply
= NULL
;
706 // Initialize message
708 InitializeDNSMessage(&pkt
.msg
.h
, id
, UpdateReqFlags
);
709 pkt
.src
.sin_addr
.s_addr
= htonl(INADDR_ANY
); // address field set solely for verbose logging in subroutines
710 pkt
.src
.sin_family
= AF_INET
;
712 // format message body
713 ptr
= putZone(&pkt
.msg
, ptr
, end
, &d
->zone
, mDNSOpaque16fromIntVal(kDNSClass_IN
));
716 ptr
= PutUpdateSRV(d
, &pkt
, ptr
, "_dns-update._udp.", registration
); if (!ptr
) goto end
;
717 ptr
= PutUpdateSRV(d
, &pkt
, ptr
, "_dns-update._tcp.", registration
); if (!ptr
) goto end
;
718 ptr
= PutUpdateSRV(d
, &pkt
, ptr
, "_dns-llq._udp.", registration
); if (!ptr
) goto end
;
720 nAdditHBO
= pkt
.msg
.h
.numAdditionals
;
724 ptr
= DNSDigest_SignMessage(&pkt
.msg
, &ptr
, &nAdditHBO
, d
->AuthInfo
);
727 pkt
.len
= ptr
- (mDNSu8
*)&pkt
.msg
;
729 // send message, receive reply
730 sd
= ConnectToServer(d
);
731 if (sd
< 0) { Log("UpdateSRV: ConnectToServer failed"); goto end
; }
732 if (SendTCPMsg(sd
, &pkt
)) { Log("UpdateSRV: SendTCPMsg failed"); }
733 reply
= ReadTCPMsg(sd
, NULL
);
734 if (!SuccessfulUpdateTransaction(&pkt
, reply
))
735 Log("SRV record registration failed with rcode %d", reply
->msg
.h
.flags
.b
[1] & kDNSFlag1_RC
);
739 if (!ptr
) { Log("UpdateSRV: Error constructing lease expiration update"); }
740 if (sd
>= 0) close(sd
);
741 if (reply
) free(reply
);
745 // wrapper routines/macros
746 #define ClearUpdateSRV(d) UpdateSRV(d, 0)
748 // clear any existing records prior to registration
749 mDNSlocal
int SetUpdateSRV(DaemonInfo
*d
)
753 err
= ClearUpdateSRV(d
); // clear any existing record
754 if (!err
) err
= UpdateSRV(d
, 1);
759 // Argument Parsing and Configuration
762 // read authentication information for a zone from command line argument
763 // global optind corresponds to keyname argument on entry
764 mDNSlocal
int ReadAuthKey(int argc
, char *argv
[], DaemonInfo
*d
)
766 uDNS_AuthInfo
*auth
= NULL
;
767 unsigned char keybuf
[512];
770 auth
= malloc(sizeof(*auth
));
771 if (!auth
) { perror("ReadAuthKey, malloc"); goto error
; }
773 if (argc
< optind
+ 1) return -1; // keyname + secret
774 if (!MakeDomainNameFromDNSNameString(&auth
->keyname
, optarg
))
775 { fprintf(stderr
, "Bad key name %s", optarg
); goto error
; }
776 keylen
= DNSDigest_Base64ToBin(argv
[optind
++], keybuf
, 512);
778 { fprintf(stderr
, "Bad shared secret %s (must be base-64 encoded string)", argv
[optind
-1]); goto error
; }
779 DNSDigest_ConstructHMACKey(auth
, keybuf
, (mDNSu32
)keylen
);
784 if (auth
) free(auth
);
788 mDNSlocal
int SetPort(DaemonInfo
*d
, char *PortAsString
)
792 l
= strtol(PortAsString
, NULL
, 10); // convert string to long
793 if ((!l
&& errno
== EINVAL
) || l
> 65535) return -1; // error check conversion
794 d
->port
.NotAnInteger
= htons((mDNSu16
)l
); // set to network byte order
798 mDNSlocal
void PrintUsage(void)
800 fprintf(stderr
, "Usage: dnsextd -z <zone> [-vf] [ -s server ] [-k keyname secret] ...\n"
801 "Use \"dnsextd -h\" for help\n");
804 mDNSlocal
void PrintHelp(void)
806 fprintf(stderr
, "\n\n");
810 "dnsextd is a daemon that implements DNS extensions supporting Dynamic DNS Update Leases\n"
811 "and Long Lived Queries, used in Wide-Area DNS Service Discovery, on behalf of name servers\n"
812 "that do not natively support these extensions. (See dns-sd.org for more info on DNS Service\n"
813 "Discovery, Update Leases, and Long Lived Queries.)\n\n"
815 "dnsextd requires one argument,the zone, which is the domain for which Update Leases\n"
816 "and Long Lived Queries are to be administered. dnsextd communicates directly with the\n"
817 "primary master server for this zone.\n\n"
819 "The options are as follows:\n\n"
821 "-f Run daemon in foreground.\n\n"
825 "-k Specify TSIG authentication key for dynamic updates from daemon to name server.\n"
826 " -k option is followed by the name of the key, and the shared secret as a base-64\n"
827 " encoded string. This key/secret are used by the daemon to delete resource records\n"
828 " from the server when leases expire. Clients are responsible for signing their\n"
829 " update requests.\n\n"
831 "-s Specify address (IPv4 address in dotted-decimal notation) of the Primary Master\n"
832 " name server. Defaults to loopback (127.0.0.1), i.e. daemon and name server\n"
833 " running on the same machine.\n\n"
835 "-v Verbose output.\n\n"
839 // Note: ProcessArgs called before process is daemonized, and therefore must open no descriptors
840 // returns 0 (success) if program is to continue execution
841 // output control arguments (-f, -v) do not affect this routine
842 mDNSlocal
int ProcessArgs(int argc
, char *argv
[], DaemonInfo
*d
)
846 if (argc
< 2) goto arg_error
;
848 d
->port
.NotAnInteger
= htons(DAEMON_PORT
); // default, may be overriden by command option
849 while ((opt
= getopt(argc
, argv
, "z:p:hfvs:k:")) != -1)
853 case 'p': if (SetPort(d
, optarg
) < 0) goto arg_error
;
856 case 'h': PrintHelp(); return -1;
857 case 'f': foreground
= 1; break;
858 case 'v': verbose
= 1; break;
859 case 's': if (!inet_pton(AF_INET
, optarg
, &d
->saddr
)) goto arg_error
;
861 case 'k': if (ReadAuthKey(argc
, argv
, d
) < 0) goto arg_error
;
863 case 'z': if (!MakeDomainNameFromDNSNameString(&d
->zone
, optarg
))
865 fprintf(stderr
, "Bad zone %s", optarg
);
869 default: goto arg_error
;
873 if (!d
->zone
.c
[0]) goto arg_error
; // zone is the only required argument
874 if (d
->AuthInfo
) AssignDomainName(&d
->AuthInfo
->zone
, &d
->zone
); // if we have a shared secret, use it for the entire zone
884 // Initialization Routines
887 // Allocate memory, initialize locks and bookkeeping variables
888 mDNSlocal
int InitLeaseTable(DaemonInfo
*d
)
890 if (pthread_mutex_init(&d
->tablelock
, NULL
)) { LogErr("InitLeaseTable", "pthread_mutex_init"); return -1; }
891 d
->nbuckets
= LEASETABLE_INIT_NBUCKETS
;
893 d
->table
= malloc(sizeof(RRTableElem
*) * LEASETABLE_INIT_NBUCKETS
);
894 if (!d
->table
) { LogErr("InitLeaseTable", "malloc"); return -1; }
895 bzero(d
->table
, sizeof(RRTableElem
*) * LEASETABLE_INIT_NBUCKETS
);
898 mDNSlocal
int SetupSockets(DaemonInfo
*daemon
)
900 struct sockaddr_in daddr
;
903 // set up sockets on which we receive requests
904 bzero(&daddr
, sizeof(daddr
));
905 daddr
.sin_family
= AF_INET
;
906 daddr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
908 if (daemon
->port
.NotAnInteger
) daddr
.sin_port
= daemon
->port
.NotAnInteger
;
909 else daddr
.sin_port
= htons(DAEMON_PORT
);
911 daemon
->tcpsd
= socket(AF_INET
, SOCK_STREAM
, 0);
912 if (!daemon
->tcpsd
) { LogErr("SetupSockets", "socket"); return -1; }
913 if (bind(daemon
->tcpsd
, (struct sockaddr
*)&daddr
, sizeof(daddr
)) < 0) { LogErr("SetupSockets", "bind"); return -1; }
914 if (listen(daemon
->tcpsd
, LISTENQ
) < 0) { LogErr("SetupSockets", "listen"); return -1; }
916 daemon
->udpsd
= socket(AF_INET
, SOCK_DGRAM
, 0);
917 if (!daemon
->udpsd
) { LogErr("SetupSockets", "socket"); return -1; }
918 if (bind(daemon
->udpsd
, (struct sockaddr
*)&daddr
, sizeof(daddr
)) < 0) { LogErr("SetupSockets", "bind"); return -1; }
920 // set up Unix domain socket pair for LLQ polling thread to signal main thread that a change to the zone occurred
921 if (socketpair(AF_LOCAL
, SOCK_STREAM
, 0, sockpair
) < 0) { LogErr("SetupSockets", "socketpair"); return -1; }
922 daemon
->LLQEventListenSock
= sockpair
[0];
923 daemon
->LLQServPollSock
= sockpair
[1];
928 // periodic table updates
931 // Delete a resource record from the nameserver via a dynamic update
932 mDNSlocal
void DeleteRecord(DaemonInfo
*d
, CacheRecord
*rr
, domainname
*zone
)
937 mDNSu8
*ptr
= pkt
.msg
.data
;
938 mDNSu8
*end
= (mDNSu8
*)&pkt
.msg
+ sizeof(DNSMessage
);
939 mDNSu16 nAdditHBO
; // num additionas, in host byte order, required by message digest routine
941 PktMsg
*reply
= NULL
;
943 VLog("Expiring record %s", GetRRDisplayString_rdb(&rr
->resrec
, &rr
->resrec
.rdata
->u
, buf
));
944 sd
= ConnectToServer(d
);
945 if (sd
< 0) { Log("DeleteRecord: ConnectToServer failed"); goto end
; }
948 InitializeDNSMessage(&pkt
.msg
.h
, id
, UpdateReqFlags
);
950 ptr
= putZone(&pkt
.msg
, ptr
, end
, zone
, mDNSOpaque16fromIntVal(rr
->resrec
.rrclass
));
952 ptr
= putDeletionRecord(&pkt
.msg
, ptr
, &rr
->resrec
);
955 nAdditHBO
= pkt
.msg
.h
.numAdditionals
;
960 ptr
= DNSDigest_SignMessage(&pkt
.msg
, &ptr
, &nAdditHBO
, d
->AuthInfo
);
964 pkt
.len
= ptr
- (mDNSu8
*)&pkt
.msg
;
965 pkt
.src
.sin_addr
.s_addr
= htonl(INADDR_ANY
); // address field set solely for verbose logging in subroutines
966 pkt
.src
.sin_family
= AF_INET
;
967 if (SendTCPMsg(sd
, &pkt
)) { Log("DeleteRecord: SendTCPMsg failed"); }
968 reply
= ReadTCPMsg(sd
, NULL
);
969 if (!SuccessfulUpdateTransaction(&pkt
, reply
))
970 Log("Expiration update failed with rcode %d", reply
->msg
.h
.flags
.b
[1] & kDNSFlag1_RC
);
973 if (!ptr
) { Log("DeleteRecord: Error constructing lease expiration update"); }
974 if (sd
>= 0) close(sd
);
975 if (reply
) free(reply
);
978 // iterate over table, deleting expired records
979 mDNSlocal
void DeleteExpiredRecords(DaemonInfo
*d
)
982 RRTableElem
*ptr
, *prev
, *fptr
;
985 if (gettimeofday(&now
, NULL
)) { LogErr("DeleteExpiredRecords ", "gettimeofday"); return; }
986 if (pthread_mutex_lock(&d
->tablelock
)) { LogErr("DeleteExpiredRecords", "pthread_mutex_lock"); return; }
987 for (i
= 0; i
< d
->nbuckets
; i
++)
993 if (ptr
->expire
- now
.tv_sec
< 0)
995 // delete record from server
996 DeleteRecord(d
, &ptr
->rr
, &ptr
->zone
);
997 if (prev
) prev
->next
= ptr
->next
;
998 else d
->table
[i
] = ptr
->next
;
1011 pthread_mutex_unlock(&d
->tablelock
);
1015 // main update request handling
1018 // Add, delete, or refresh records in table based on contents of a successfully completed dynamic update
1019 mDNSlocal
void UpdateLeaseTable(PktMsg
*pkt
, DaemonInfo
*d
, mDNSs32 lease
)
1021 RRTableElem
**rptr
, *tmp
;
1022 int i
, allocsize
, bucket
;
1023 LargeCacheRecord lcr
;
1024 ResourceRecord
*rr
= &lcr
.r
.resrec
;
1025 const mDNSu8
*ptr
, *end
;
1026 struct timeval time
;
1030 if (pthread_mutex_lock(&d
->tablelock
)) { LogErr("UpdateLeaseTable", "pthread_mutex_lock"); return; }
1032 ptr
= pkt
->msg
.data
;
1033 end
= (mDNSu8
*)&pkt
->msg
+ pkt
->len
;
1034 ptr
= getQuestion(&pkt
->msg
, ptr
, end
, 0, &zone
);
1035 if (!ptr
) { Log("UpdateLeaseTable: cannot read zone"); goto cleanup
; }
1036 ptr
= LocateAuthorities(&pkt
->msg
, end
);
1037 if (!ptr
) { Log("UpdateLeaseTable: Format error"); goto cleanup
; }
1039 for (i
= 0; i
< pkt
->msg
.h
.mDNS_numUpdates
; i
++)
1041 mDNSBool DeleteAllRRSets
= mDNSfalse
, DeleteOneRRSet
= mDNSfalse
, DeleteOneRR
= mDNSfalse
;
1043 ptr
= GetLargeResourceRecord(NULL
, &pkt
->msg
, ptr
, end
, 0, kDNSRecordTypePacketAns
, &lcr
);
1044 if (!ptr
) { Log("UpdateLeaseTable: GetLargeResourceRecord returned NULL"); goto cleanup
; }
1045 bucket
= rr
->namehash
% d
->nbuckets
;
1046 rptr
= &d
->table
[bucket
];
1049 if (rr
->rrtype
== kDNSQType_ANY
&& !rr
->rroriginalttl
&& rr
->rrclass
== kDNSQClass_ANY
&& !rr
->rdlength
)
1050 DeleteAllRRSets
= mDNStrue
; // delete all rrsets for a name
1051 else if (!rr
->rroriginalttl
&& rr
->rrclass
== kDNSQClass_ANY
&& !rr
->rdlength
)
1052 DeleteOneRRSet
= mDNStrue
;
1053 else if (!rr
->rroriginalttl
&& rr
->rrclass
== kDNSClass_NONE
)
1054 DeleteOneRR
= mDNStrue
;
1056 if (DeleteAllRRSets
|| DeleteOneRRSet
|| DeleteOneRR
)
1060 if (SameDomainName((*rptr
)->rr
.resrec
.name
, rr
->name
) &&
1062 (DeleteOneRRSet
&& (*rptr
)->rr
.resrec
.rrtype
== rr
->rrtype
) ||
1063 (DeleteOneRR
&& SameResourceRecord(&(*rptr
)->rr
.resrec
, rr
))))
1066 VLog("Received deletion update for %s", GetRRDisplayString_rdb(&tmp
->rr
.resrec
, &tmp
->rr
.resrec
.rdata
->u
, buf
));
1067 *rptr
= (*rptr
)->next
;
1071 else rptr
= &(*rptr
)->next
;
1076 // see if add or refresh
1077 while (*rptr
&& !SameResourceRecord(&(*rptr
)->rr
.resrec
, rr
)) rptr
= &(*rptr
)->next
;
1081 if (gettimeofday(&time
, NULL
)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup
; }
1082 (*rptr
)->expire
= time
.tv_sec
+ (unsigned)lease
;
1083 VLog("Refreshing lease for %s", GetRRDisplayString_rdb(&lcr
.r
.resrec
, &lcr
.r
.resrec
.rdata
->u
, buf
));
1087 // New record - add to table
1088 if (d
->nelems
> d
->nbuckets
)
1091 bucket
= rr
->namehash
% d
->nbuckets
;
1092 rptr
= &d
->table
[bucket
];
1094 if (gettimeofday(&time
, NULL
)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup
; }
1095 allocsize
= sizeof(RRTableElem
);
1096 if (rr
->rdlength
> InlineCacheRDSize
) allocsize
+= (rr
->rdlength
- InlineCacheRDSize
);
1097 tmp
= malloc(allocsize
);
1098 if (!tmp
) { LogErr("UpdateLeaseTable", "malloc"); goto cleanup
; }
1099 memcpy(&tmp
->rr
, &lcr
.r
, sizeof(CacheRecord
) + rr
->rdlength
- InlineCacheRDSize
);
1100 tmp
->rr
.resrec
.rdata
= (RData
*)&tmp
->rr
.rdatastorage
;
1101 AssignDomainName(&tmp
->name
, rr
->name
);
1102 tmp
->rr
.resrec
.name
= &tmp
->name
;
1103 tmp
->expire
= time
.tv_sec
+ (unsigned)lease
;
1104 tmp
->cli
.sin_addr
= pkt
->src
.sin_addr
;
1105 AssignDomainName(&tmp
->zone
, &zone
.qname
);
1106 tmp
->next
= d
->table
[bucket
];
1107 d
->table
[bucket
] = tmp
;
1109 VLog("Adding update for %s to lease table", GetRRDisplayString_rdb(&lcr
.r
.resrec
, &lcr
.r
.resrec
.rdata
->u
, buf
));
1115 pthread_mutex_unlock(&d
->tablelock
);
1119 // Given a successful reply from a server, create a new reply that contains lease information
1120 // Replies are currently not signed !!!KRS change this
1121 mDNSlocal PktMsg
*FormatLeaseReply(DaemonInfo
*d
, PktMsg
*orig
, mDNSu32 lease
)
1128 reply
= malloc(sizeof(*reply
));
1129 if (!reply
) { LogErr("FormatLeaseReply", "malloc"); return NULL
; }
1130 flags
.b
[0] = kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
;
1133 InitializeDNSMessage(&reply
->msg
.h
, orig
->msg
.h
.id
, flags
);
1134 reply
->src
.sin_addr
.s_addr
= htonl(INADDR_ANY
); // unused except for log messages
1135 reply
->src
.sin_family
= AF_INET
;
1136 ptr
= reply
->msg
.data
;
1137 end
= (mDNSu8
*)&reply
->msg
+ sizeof(DNSMessage
);
1138 ptr
= putUpdateLease(&reply
->msg
, ptr
, lease
);
1139 if (!ptr
) { Log("FormatLeaseReply: putUpdateLease failed"); free(reply
); return NULL
; }
1140 reply
->len
= ptr
- (mDNSu8
*)&reply
->msg
;
1144 // pkt is thread-local, not requiring locking
1145 mDNSlocal PktMsg
*HandleRequest(PktMsg
*pkt
, DaemonInfo
*d
)
1148 PktMsg
*reply
= NULL
, *LeaseReply
;
1152 // send msg to server, read reply
1153 sd
= ConnectToServer(d
);
1155 { Log("Discarding request from %s due to connection errors", inet_ntop(AF_INET
, &pkt
->src
.sin_addr
, buf
, 32)); goto cleanup
; }
1156 if (SendTCPMsg(sd
, pkt
) < 0)
1157 { Log("Couldn't relay message from %s to server. Discarding.", inet_ntop(AF_INET
, &pkt
->src
.sin_addr
, buf
, 32)); goto cleanup
; }
1158 reply
= ReadTCPMsg(sd
, NULL
);
1161 if (!SuccessfulUpdateTransaction(pkt
, reply
))
1162 { VLog("Message from %s not a successful update.", inet_ntop(AF_INET
, &pkt
->src
.sin_addr
, buf
, 32)); goto cleanup
; }
1163 lease
= GetPktLease(pkt
);
1164 UpdateLeaseTable(pkt
, d
, lease
);
1167 LeaseReply
= FormatLeaseReply(d
, reply
, lease
);
1168 if (!LeaseReply
) Log("HandleRequest - unable to format lease reply");
1173 if (sd
>= 0) close(sd
);
1179 // LLQ Support Routines
1182 // Set fields of an LLQ Opt Resource Record
1183 mDNSlocal
void FormatLLQOpt(AuthRecord
*opt
, int opcode
, mDNSu8
*id
, mDNSs32 lease
)
1185 bzero(opt
, sizeof(*opt
));
1186 mDNS_SetupResourceRecord(opt
, mDNSNULL
, mDNSInterface_Any
, kDNSType_OPT
, kStandardTTL
, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
1187 opt
->resrec
.rdlength
= LLQ_OPT_RDLEN
;
1188 opt
->resrec
.rdestimate
= LLQ_OPT_RDLEN
;
1189 opt
->resrec
.rdata
->u
.opt
.opt
= kDNSOpt_LLQ
;
1190 opt
->resrec
.rdata
->u
.opt
.optlen
= sizeof(LLQOptData
);
1191 opt
->resrec
.rdata
->u
.opt
.OptData
.llq
.vers
= kLLQ_Vers
;
1192 opt
->resrec
.rdata
->u
.opt
.OptData
.llq
.llqOp
= opcode
;
1193 opt
->resrec
.rdata
->u
.opt
.OptData
.llq
.err
= LLQErr_NoError
;
1194 memcpy(opt
->resrec
.rdata
->u
.opt
.OptData
.llq
.id
, id
, 8);
1195 opt
->resrec
.rdata
->u
.opt
.OptData
.llq
.lease
= lease
;
1198 // Calculate effective remaining lease of an LLQ
1199 mDNSlocal mDNSu32
LLQLease(LLQEntry
*e
)
1203 gettimeofday(&t
, NULL
);
1204 if (e
->expire
< t
.tv_sec
) return 0;
1205 else return e
->expire
- t
.tv_sec
;
1208 mDNSlocal
void DeleteLLQ(DaemonInfo
*d
, LLQEntry
*e
)
1210 int bucket
= DomainNameHashValue(&e
->qname
) % LLQ_TABLESIZE
;
1211 LLQEntry
**ptr
= &d
->LLQTable
[bucket
];
1212 AnswerListElem
*a
= e
->AnswerList
;
1215 inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addr
, 32);
1216 VLog("Deleting LLQ table entry for %##s client %s", e
->qname
.c
, addr
);
1218 // free shared answer structure if ref count drops to zero
1219 if (a
&& !(--a
->refcount
))
1221 CacheRecord
*cr
= a
->KnownAnswers
, *tmp
;
1222 AnswerListElem
**tbl
= &d
->AnswerTable
[bucket
];
1231 while (*tbl
&& *tbl
!= a
) tbl
= &(*tbl
)->next
;
1232 if (*tbl
) { *tbl
= (*tbl
)->next
; free(a
); }
1233 else Log("Error: DeleteLLQ - AnswerList not found in table");
1236 // remove LLQ from table, free memory
1237 while(*ptr
&& *ptr
!= e
) ptr
= &(*ptr
)->next
;
1238 if (!*ptr
) { Log("Error: DeleteLLQ - LLQ not in table"); return; }
1239 *ptr
= (*ptr
)->next
;
1243 mDNSlocal
int SendLLQ(DaemonInfo
*d
, PktMsg
*pkt
, struct sockaddr_in dst
)
1249 if (sendto(d
->udpsd
, &pkt
->msg
, pkt
->len
, 0, (struct sockaddr
*)&dst
, sizeof(dst
)) != (int)pkt
->len
)
1251 LogErr("DaemonInfo", "sendto");
1252 Log("Could not send response to client %s", inet_ntop(AF_INET
, &dst
.sin_addr
, addr
, 32));
1259 // if non-negative, sd is a TCP socket connected to the nameserver
1260 // otherwise, this routine creates and closes its own socket
1261 mDNSlocal CacheRecord
*AnswerQuestion(DaemonInfo
*d
, AnswerListElem
*e
, int sd
)
1265 const mDNSu8
*ansptr
;
1266 mDNSu8
*end
= q
.msg
.data
;
1267 mDNSOpaque16 id
, flags
= QueryFlags
;
1268 PktMsg
*reply
= NULL
;
1269 LargeCacheRecord lcr
;
1270 CacheRecord
*AnswerList
= NULL
;
1272 mDNSBool CloseSDOnExit
= sd
< 0;
1274 VLog("Querying server for %##s type %d", e
->name
.c
, e
->type
);
1276 flags
.b
[0] |= kDNSFlag0_RD
; // recursion desired
1277 id
.NotAnInteger
= 0;
1278 InitializeDNSMessage(&q
.msg
.h
, id
, flags
);
1280 end
= putQuestion(&q
.msg
, end
, end
+ AbsoluteMaxDNSMessageData
, &e
->name
, e
->type
, kDNSClass_IN
);
1281 if (!end
) { Log("Error: AnswerQuestion - putQuestion returned NULL"); goto end
; }
1282 q
.len
= (int)(end
- (mDNSu8
*)&q
.msg
);
1284 if (sd
< 0) sd
= ConnectToServer(d
);
1285 if (sd
< 0) { Log("AnswerQuestion: ConnectToServer failed"); goto end
; }
1286 if (SendTCPMsg(sd
, &q
)) { Log("AnswerQuestion: SendTCPMsg failed"); close(sd
); goto end
; }
1287 reply
= ReadTCPMsg(sd
, NULL
);
1289 if ((reply
->msg
.h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
) != (kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
))
1290 { Log("AnswerQuestion: %##s type %d - Invalid response flags from server"); goto end
; }
1291 rcode
= (mDNSu8
)(reply
->msg
.h
.flags
.b
[1] & kDNSFlag1_RC
);
1292 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
; }
1294 end
= (mDNSu8
*)&reply
->msg
+ reply
->len
;
1295 ansptr
= LocateAnswers(&reply
->msg
, end
);
1296 if (!ansptr
) { Log("Error: AnswerQuestion - LocateAnswers returned NULL"); goto end
; }
1298 for (i
= 0; i
< reply
->msg
.h
.numAnswers
; i
++)
1300 ansptr
= GetLargeResourceRecord(NULL
, &reply
->msg
, ansptr
, end
, 0, kDNSRecordTypePacketAns
, &lcr
);
1301 if (!ansptr
) { Log("AnswerQuestions: GetLargeResourceRecord returned NULL"); goto end
; }
1302 if (lcr
.r
.resrec
.rrtype
!= e
->type
|| lcr
.r
.resrec
.rrclass
!= kDNSClass_IN
|| !SameDomainName(lcr
.r
.resrec
.name
, &e
->name
))
1304 Log("AnswerQuestion: response %##s type #d does not answer question %##s type #d. Discarding",
1305 lcr
.r
.resrec
.name
->c
, lcr
.r
.resrec
.rrtype
, e
->name
.c
, e
->type
);
1309 CacheRecord
*cr
= CopyCacheRecord(&lcr
.r
, &e
->name
);
1310 if (!cr
) { Log("Error: AnswerQuestion - CopyCacheRecord returned NULL"); goto end
; }
1311 cr
->next
= AnswerList
;
1317 if (sd
> -1 && CloseSDOnExit
) close(sd
);
1318 if (reply
) free(reply
);
1322 // Routine sets EventList to contain Add/Remove events, and deletes any removes from the KnownAnswer list
1323 mDNSlocal
void UpdateAnswerList(DaemonInfo
*d
, AnswerListElem
*a
, int sd
)
1325 CacheRecord
*cr
, *NewAnswers
, **na
, **ka
; // "new answer", "known answer"
1327 // get up to date answers
1328 NewAnswers
= AnswerQuestion(d
, a
, sd
);
1330 // first pass - mark all answers for deletion
1331 for (ka
= &a
->KnownAnswers
; *ka
; ka
= &(*ka
)->next
)
1332 (*ka
)->resrec
.rroriginalttl
= (unsigned)-1; // -1 means delete
1334 // second pass - mark answers pre-existent
1335 for (ka
= &a
->KnownAnswers
; *ka
; ka
= &(*ka
)->next
)
1337 for (na
= &NewAnswers
; *na
; na
= &(*na
)->next
)
1339 if (SameResourceRecord(&(*ka
)->resrec
, &(*na
)->resrec
))
1340 { (*ka
)->resrec
.rroriginalttl
= 0; break; } // 0 means no change
1344 // third pass - add new records to Event list
1348 for (ka
= &a
->KnownAnswers
; *ka
; ka
= &(*ka
)->next
)
1349 if (SameResourceRecord(&(*ka
)->resrec
, &(*na
)->resrec
)) break;
1352 // answer is not in list - splice from NewAnswers list, add to Event list
1354 *na
= (*na
)->next
; // splice from list
1355 cr
->next
= a
->EventList
; // add spliced record to event list
1357 cr
->resrec
.rroriginalttl
= 1; // 1 means add
1359 else na
= &(*na
)->next
;
1362 // move all the removes from the answer list to the event list
1363 ka
= &a
->KnownAnswers
;
1366 if ((*ka
)->resrec
.rroriginalttl
== (unsigned)-1)
1370 cr
->next
= a
->EventList
;
1373 else ka
= &(*ka
)->next
;
1376 // lastly, free the remaining records (known answers) in NewAnswers list
1380 NewAnswers
= NewAnswers
->next
;
1385 mDNSlocal
void SendEvents(DaemonInfo
*d
, LLQEntry
*e
)
1389 mDNSu8
*end
= (mDNSu8
*)&response
.msg
.data
;
1391 char rrbuf
[80], addrbuf
[32];
1394 msgID
.NotAnInteger
= random();
1395 if (verbose
) inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addrbuf
, 32);
1396 InitializeDNSMessage(&response
.msg
.h
, msgID
, ResponseFlags
);
1397 end
= putQuestion(&response
.msg
, end
, end
+ AbsoluteMaxDNSMessageData
, &e
->qname
, e
->qtype
, kDNSClass_IN
);
1398 if (!end
) { Log("Error: SendEvents - putQuestion returned NULL"); return; }
1400 // put adds/removes in packet
1401 for (cr
= e
->AnswerList
->EventList
; cr
; cr
= cr
->next
)
1403 if (verbose
) GetRRDisplayString_rdb(&cr
->resrec
, &cr
->resrec
.rdata
->u
, rrbuf
);
1404 VLog("%s (%s): %s", addrbuf
, (mDNSs32
)cr
->resrec
.rroriginalttl
< 0 ? "Remove": "Add", rrbuf
);
1405 end
= PutResourceRecordTTLJumbo(&response
.msg
, end
, &response
.msg
.h
.numAnswers
, &cr
->resrec
, cr
->resrec
.rroriginalttl
);
1406 if (!end
) { Log("Error: SendEvents - UpdateAnswerList returned NULL"); return; }
1409 FormatLLQOpt(&opt
, kLLQOp_Event
, e
->id
, LLQLease(e
));
1410 end
= PutResourceRecordTTLJumbo(&response
.msg
, end
, &response
.msg
.h
.numAdditionals
, &opt
.resrec
, 0);
1411 if (!end
) { Log("Error: SendEvents - PutResourceRecordTTLJumbo"); return; }
1413 response
.len
= (int)(end
- (mDNSu8
*)&response
.msg
);
1414 if (SendLLQ(d
, &response
, e
->cli
) < 0) LogMsg("Error: SendEvents - SendLLQ");
1417 mDNSlocal
void PrintLLQTable(DaemonInfo
*d
)
1423 Log("Printing LLQ table contents");
1425 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1430 inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addr
, 32);
1431 Log("LLQ from %##s type %d lease %d (%d remaining)",
1432 addr
, e
->qname
.c
, e
->qtype
, e
->lease
, LLQLease(e
));
1438 // Send events to clients as a result of a change in the zone
1439 mDNSlocal
void GenLLQEvents(DaemonInfo
*d
)
1445 VLog("Generating LLQ Events");
1447 gettimeofday(&t
, NULL
);
1448 sd
= ConnectToServer(d
);
1449 if (sd
< 0) { Log("GenLLQEvents: ConnectToServer failed"); return; }
1451 // get all answers up to date
1452 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1454 AnswerListElem
*a
= d
->AnswerTable
[i
];
1457 UpdateAnswerList(d
, a
, sd
);
1462 // for each established LLQ, send events
1463 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1465 e
= &d
->LLQTable
[i
];
1468 if ((*e
)->expire
< t
.tv_sec
) DeleteLLQ(d
, *e
);
1471 if ((*e
)->state
== Established
&& (*e
)->AnswerList
->EventList
) SendEvents(d
, *e
);
1477 // now that all LLQs are updated, we move Add events from the Event list to the Known Answer list, and free Removes
1478 for (i
= 0; i
< LLQ_TABLESIZE
; i
++)
1480 AnswerListElem
*a
= d
->AnswerTable
[i
];
1485 CacheRecord
*cr
= a
->EventList
, *tmp
;
1490 if ((signed)tmp
->resrec
.rroriginalttl
< 0) free(tmp
);
1493 tmp
->next
= a
->KnownAnswers
;
1494 a
->KnownAnswers
= tmp
;
1495 tmp
->resrec
.rroriginalttl
= 0;
1498 a
->EventList
= NULL
;
1507 // Monitor zone for changes that may produce LLQ events
1508 mDNSlocal
void *LLQEventMonitor(void *DInfoPtr
)
1510 DaemonInfo
*d
= DInfoPtr
;
1512 mDNSu8
*end
= q
.msg
.data
;
1514 mDNSOpaque16 id
, flags
= QueryFlags
;
1517 mDNSBool SerialInitialized
= mDNSfalse
;
1519 LargeCacheRecord lcr
;
1520 ResourceRecord
*rr
= &lcr
.r
.resrec
;
1521 int i
, sleeptime
= 0;
1526 id
.NotAnInteger
= 0;
1527 InitializeDNSMessage(&q
.msg
.h
, id
, flags
);
1528 AssignDomainName(&zone
, &d
->zone
);
1529 end
= putQuestion(&q
.msg
, end
, end
+ AbsoluteMaxDNSMessageData
, &zone
, kDNSType_SOA
, kDNSClass_IN
);
1530 if (!end
) { Log("Error: LLQEventMonitor - putQuestion returned NULL"); return NULL
; }
1531 q
.len
= (int)(end
- (mDNSu8
*)&q
.msg
);
1533 sd
= ConnectToServer(d
);
1534 if (sd
< 0) { Log("LLQEventMonitor: ConnectToServer failed"); return NULL
; }
1539 sleeptime
= LLQ_MONITOR_ERR_INTERVAL
; // if we bail on error below, rate limit retry
1541 // send message, receive response
1542 if (SendTCPMsg(sd
, &q
)) { Log("LLQEventMonitor: SendTCPMsg failed"); continue; }
1543 if (!ReadTCPMsg(sd
, &reply
)) { Log("LLQEventMonitor: ReadTCPMsg failed"); continue; }
1544 end
= (mDNSu8
*)&reply
.msg
+ reply
.len
;
1545 if (reply
.msg
.h
.flags
.b
[1] & kDNSFlag1_RC
) { Log("LLQEventMonitor - received non-zero rcode"); continue; }
1548 ptr
= LocateAnswers(&reply
.msg
, end
);
1549 if (!ptr
) { Log("Error: LLQEventMonitor - LocateAnswers returned NULL"); continue; }
1550 for (i
= 0; i
< reply
.msg
.h
.numAnswers
; i
++)
1552 ptr
= GetLargeResourceRecord(NULL
, &reply
.msg
, ptr
, end
, 0, kDNSRecordTypePacketAns
, &lcr
);
1553 if (!ptr
) { Log("Error: LLQEventMonitor - GetLargeResourceRecord returned NULL"); continue; }
1554 if (rr
->rrtype
!= kDNSType_SOA
|| rr
->rrclass
!= kDNSClass_IN
|| !SameDomainName(rr
->name
, &zone
)) continue;
1555 if (!SerialInitialized
)
1557 // first time through loop
1558 SerialInitialized
= mDNStrue
;
1559 serial
= rr
->rdata
->u
.soa
.serial
;
1560 sleeptime
= LLQ_MONITOR_INTERVAL
;
1563 else if (rr
->rdata
->u
.soa
.serial
!= serial
)
1565 // update serial, wake main thread
1566 serial
= rr
->rdata
->u
.soa
.serial
;
1567 VLog("LLQEventMonitor: zone changed. Signaling main thread.");
1568 if (send(d
->LLQServPollSock
, pingmsg
, sizeof(pingmsg
), 0) != sizeof(pingmsg
))
1569 { LogErr("LLQEventMonitor", "send"); break; }
1571 sleeptime
= LLQ_MONITOR_INTERVAL
;
1574 if (!ptr
) Log("LLQEventMonitor: response to query did not contain SOA");
1578 mDNSlocal
void SetAnswerList(DaemonInfo
*d
, LLQEntry
*e
)
1580 int bucket
= DomainNameHashValue(&e
->qname
) % LLQ_TABLESIZE
;
1581 AnswerListElem
*a
= d
->AnswerTable
[bucket
];
1582 while (a
&& (a
->type
!= e
->qtype
||!SameDomainName(&a
->name
, &e
->qname
))) a
= a
->next
;
1585 a
= malloc(sizeof(*a
));
1586 if (!a
) { LogErr("SetAnswerList", "malloc"); return; }
1587 AssignDomainName(&a
->name
, &e
->qname
);
1590 a
->KnownAnswers
= NULL
;
1591 a
->EventList
= NULL
;
1592 a
->next
= d
->AnswerTable
[bucket
];
1593 d
->AnswerTable
[bucket
] = a
;
1595 // to get initial answer list, call UpdateAnswerList and move cache records from EventList to KnownAnswers
1596 UpdateAnswerList(d
, a
, -1);
1597 a
->KnownAnswers
= a
->EventList
;
1598 a
->EventList
= NULL
;
1605 // Allocate LLQ entry, insert into table
1606 mDNSlocal LLQEntry
*NewLLQ(DaemonInfo
*d
, struct sockaddr_in cli
, domainname
*qname
, mDNSu16 qtype
, mDNSu32 lease
)
1610 int bucket
= DomainNameHashValue(qname
) % LLQ_TABLESIZE
;
1613 e
= malloc(sizeof(*e
));
1614 if (!e
) { LogErr("NewLLQ", "malloc"); return NULL
; }
1616 inet_ntop(AF_INET
, &cli
.sin_addr
, addr
, 32);
1617 VLog("Allocating LLQ entry for client %s question %##s type %d", addr
, qname
->c
, qtype
);
1619 // initialize structure
1621 AssignDomainName(&e
->qname
, qname
);
1623 memset(e
->id
, 0, 8);
1624 e
->state
= RequestReceived
;
1625 e
->AnswerList
= NULL
;
1627 if (lease
< LLQ_MIN_LEASE
) lease
= LLQ_MIN_LEASE
;
1628 else if (lease
> LLQ_MAX_LEASE
) lease
= LLQ_MIN_LEASE
;
1629 gettimeofday(&t
, NULL
);
1630 e
->expire
= t
.tv_sec
+ (int)lease
;
1634 e
->next
= d
->LLQTable
[bucket
];
1635 d
->LLQTable
[bucket
] = e
;
1640 // Handle a refresh request from client
1641 mDNSlocal
void LLQRefresh(DaemonInfo
*d
, LLQEntry
*e
, LLQOptData
*llq
, mDNSOpaque16 msgID
)
1645 mDNSu8
*end
= (mDNSu8
*)&ack
.msg
.data
;
1648 inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addr
, 32);
1649 VLog("%s LLQ for %##s from %s", llq
->lease
? "Refreshing" : "Deleting", e
->qname
.c
, addr
);
1653 if (llq
->lease
< LLQ_MIN_LEASE
) llq
->lease
= LLQ_MIN_LEASE
;
1654 else if (llq
->lease
> LLQ_MAX_LEASE
) llq
->lease
= LLQ_MIN_LEASE
;
1657 ack
.src
.sin_addr
.s_addr
= 0; // unused
1658 InitializeDNSMessage(&ack
.msg
.h
, msgID
, ResponseFlags
);
1659 end
= putQuestion(&ack
.msg
, end
, end
+ AbsoluteMaxDNSMessageData
, &e
->qname
, e
->qtype
, kDNSClass_IN
);
1660 if (!end
) { Log("Error: putQuestion"); return; }
1662 FormatLLQOpt(&opt
, kLLQOp_Refresh
, e
->id
, llq
->lease
? LLQLease(e
) : 0);
1663 end
= PutResourceRecordTTLJumbo(&ack
.msg
, end
, &ack
.msg
.h
.numAdditionals
, &opt
.resrec
, 0);
1664 if (!end
) { Log("Error: PutResourceRecordTTLJumbo"); return; }
1666 ack
.len
= (int)(end
- (mDNSu8
*)&ack
.msg
);
1667 if (SendLLQ(d
, &ack
, e
->cli
)) Log("Error: LLQRefresh");
1669 if (llq
->lease
) e
->state
= Established
;
1670 else DeleteLLQ(d
, e
);
1673 // Complete handshake with Ack an initial answers
1674 mDNSlocal
void LLQCompleteHandshake(DaemonInfo
*d
, LLQEntry
*e
, LLQOptData
*llq
, mDNSOpaque16 msgID
)
1680 mDNSu8
*end
= (mDNSu8
*)&ack
.msg
.data
;
1681 char rrbuf
[80], addrbuf
[32];
1683 inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addr
, 32);
1685 if (memcmp(llq
->id
, e
->id
, 8) ||
1686 llq
->vers
!= kLLQ_Vers
||
1687 llq
->llqOp
!= kLLQOp_Setup
||
1688 llq
->err
!= LLQErr_NoError
||
1689 llq
->lease
> e
->lease
+ LLQ_LEASE_FUDGE
||
1690 llq
->lease
< e
->lease
- LLQ_LEASE_FUDGE
)
1691 { Log("Incorrect challenge response from %s", addr
); return; }
1693 if (e
->state
== Established
) VLog("Retransmitting LLQ ack + answers for %##s", e
->qname
.c
);
1694 else VLog("Delivering LLQ ack + answers for %##s", e
->qname
.c
);
1696 // format ack + answers
1697 ack
.src
.sin_addr
.s_addr
= 0; // unused
1698 InitializeDNSMessage(&ack
.msg
.h
, msgID
, ResponseFlags
);
1699 end
= putQuestion(&ack
.msg
, end
, end
+ AbsoluteMaxDNSMessageData
, &e
->qname
, e
->qtype
, kDNSClass_IN
);
1700 if (!end
) { Log("Error: putQuestion"); return; }
1702 if (e
->state
!= Established
) { SetAnswerList(d
, e
); e
->state
= Established
; }
1704 if (verbose
) inet_ntop(AF_INET
, &e
->cli
.sin_addr
, addrbuf
, 32);
1705 for (ptr
= e
->AnswerList
->KnownAnswers
; ptr
; ptr
= ptr
->next
)
1707 if (verbose
) GetRRDisplayString_rdb(&ptr
->resrec
, &ptr
->resrec
.rdata
->u
, rrbuf
);
1708 VLog("%s Intitial Answer - %s", addr
, rrbuf
);
1709 end
= PutResourceRecordTTLJumbo(&ack
.msg
, end
, &ack
.msg
.h
.numAnswers
, &ptr
->resrec
, 1);
1710 if (!end
) { Log("Error: PutResourceRecordTTLJumbo"); return; }
1713 FormatLLQOpt(&opt
, kLLQOp_Setup
, e
->id
, LLQLease(e
));
1714 end
= PutResourceRecordTTLJumbo(&ack
.msg
, end
, &ack
.msg
.h
.numAdditionals
, &opt
.resrec
, 0);
1715 if (!end
) { Log("Error: PutResourceRecordTTLJumbo"); return; }
1717 ack
.len
= (int)(end
- (mDNSu8
*)&ack
.msg
);
1718 if (SendLLQ(d
, &ack
, e
->cli
)) Log("Error: LLQCompleteHandshake");
1721 mDNSlocal
void LLQSetupChallenge(DaemonInfo
*d
, LLQEntry
*e
, LLQOptData
*llq
, mDNSOpaque16 msgID
)
1726 mDNSu8
*end
= challenge
.msg
.data
;
1729 if (e
->state
== ChallengeSent
) VLog("Retransmitting LLQ setup challenge for %##s", e
->qname
.c
);
1730 else VLog("Sending LLQ setup challenge for %##s", e
->qname
.c
);
1732 if (!ZERO_LLQID(llq
->id
)) { Log("Error: LLQSetupChallenge - nonzero ID"); return; } // server bug
1733 if (llq
->llqOp
!= kLLQOp_Setup
) { Log("LLQSetupChallenge - incorrrect operation from client"); return; } // client error
1735 if (ZERO_LLQID(e
->id
)) // don't regenerate random ID for retransmissions
1737 // construct ID <time><random>
1738 gettimeofday(&t
, NULL
);
1740 memcpy(e
->id
, &t
.tv_sec
, sizeof(t
.tv_sec
));
1741 memcpy(e
->id
+ sizeof(t
.tv_sec
), &randval
, sizeof(randval
));
1744 // format response (query + LLQ opt rr)
1745 challenge
.src
.sin_addr
.s_addr
= 0; // unused
1746 InitializeDNSMessage(&challenge
.msg
.h
, msgID
, ResponseFlags
);
1747 end
= putQuestion(&challenge
.msg
, end
, end
+ AbsoluteMaxDNSMessageData
, &e
->qname
, e
->qtype
, kDNSClass_IN
);
1748 if (!end
) { Log("Error: putQuestion"); return; }
1749 FormatLLQOpt(&opt
, kLLQOp_Setup
, e
->id
, LLQLease(e
));
1750 end
= PutResourceRecordTTLJumbo(&challenge
.msg
, end
, &challenge
.msg
.h
.numAdditionals
, &opt
.resrec
, 0);
1751 if (!end
) { Log("Error: PutResourceRecordTTLJumbo"); return; }
1752 challenge
.len
= (int)(end
- (mDNSu8
*)&challenge
.msg
);
1753 if (SendLLQ(d
, &challenge
, e
->cli
)) { Log("Error: LLQSetupChallenge"); return; }
1754 e
->state
= ChallengeSent
;
1757 // Take action on an LLQ message from client. Entry must be initialized and in table
1758 mDNSlocal
void UpdateLLQ(DaemonInfo
*d
, LLQEntry
*e
, LLQOptData
*llq
, mDNSOpaque16 msgID
)
1762 case RequestReceived
:
1763 LLQSetupChallenge(d
, e
, llq
, msgID
);
1766 if (ZERO_LLQID(llq
->id
)) LLQSetupChallenge(d
, e
, llq
, msgID
); // challenge sent and lost
1767 else LLQCompleteHandshake(d
, e
, llq
, msgID
);
1770 if (ZERO_LLQID(llq
->id
))
1772 // client started over. reset state.
1773 LLQEntry
*newe
= NewLLQ(d
, e
->cli
, &e
->qname
, e
->qtype
, llq
->lease
);
1776 LLQSetupChallenge(d
, newe
, llq
, msgID
);
1779 else if (llq
->llqOp
== kLLQOp_Setup
)
1780 { LLQCompleteHandshake(d
, e
, llq
, msgID
); return; } // Ack lost
1781 else if (llq
->llqOp
== kLLQOp_Refresh
)
1782 { LLQRefresh(d
, e
, llq
, msgID
); return; }
1783 else { Log("Unhandled message for established LLQ"); return; }
1787 mDNSlocal LLQEntry
*LookupLLQ(DaemonInfo
*d
, struct sockaddr_in cli
, domainname
*qname
, mDNSu16 qtype
, mDNSu8
*id
)
1789 int bucket
= bucket
= DomainNameHashValue(qname
) % LLQ_TABLESIZE
;
1790 LLQEntry
*ptr
= d
->LLQTable
[bucket
];
1794 if (((ptr
->state
== ChallengeSent
&& ZERO_LLQID(id
)) || // zero-id due to packet loss OK in state ChallengeSent
1795 !memcmp(id
, ptr
->id
, 8)) && // id match
1796 SAME_INADDR(cli
, ptr
->cli
) && qtype
== ptr
->qtype
&& SameDomainName(&ptr
->qname
, qname
)) // same source, type, qname
1803 mDNSlocal
int RecvLLQ(DaemonInfo
*d
, PktMsg
*pkt
)
1806 LargeCacheRecord opt
;
1809 const mDNSu8
*qptr
= pkt
->msg
.data
;
1810 const mDNSu8
*end
= (mDNSu8
*)&pkt
->msg
+ pkt
->len
;
1811 const mDNSu8
*aptr
= LocateAdditionals(&pkt
->msg
, end
);
1812 LLQOptData
*llq
= NULL
;
1816 inet_ntop(AF_INET
, &pkt
->src
.sin_addr
, addr
, 32);
1818 VLog("Received LLQ msg from %s", addr
);
1819 // sanity-check packet
1820 if (!pkt
->msg
.h
.numQuestions
|| !pkt
->msg
.h
.numAdditionals
)
1822 Log("Malformatted LLQ from %s with %d questions, %d additionals", addr
, pkt
->msg
.h
.numQuestions
, pkt
->msg
.h
.numAdditionals
);
1826 // find the OPT RR - must be last in message
1827 for (i
= 0; i
< pkt
->msg
.h
.numAdditionals
; i
++)
1829 aptr
= GetLargeResourceRecord(NULL
, &pkt
->msg
, aptr
, end
, 0, kDNSRecordTypePacketAdd
, &opt
);
1830 if (!aptr
) { Log("Malformatted LLQ from %s: could not get Additional record %d", addr
, i
); goto end
; }
1834 if (opt
.r
.resrec
.rrtype
!= kDNSType_OPT
) { Log("Malformatted LLQ from %s: last Additional not an Opt RR", addr
); goto end
; }
1835 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
); }
1837 // dispatch each question
1838 for (i
= 0; i
< pkt
->msg
.h
.numQuestions
; i
++)
1840 qptr
= getQuestion(&pkt
->msg
, qptr
, end
, 0, &q
);
1841 if (!qptr
) { Log("Malformatted LLQ from %s: cannot read question %d", addr
, i
); goto end
; }
1842 llq
= (LLQOptData
*)&opt
.r
.resrec
.rdata
->u
.opt
.OptData
.llq
+ i
; // point into OptData at index i
1843 if (llq
->vers
!= kLLQ_Vers
) { Log("LLQ from %s contains bad version %d (expected %d)", addr
, llq
->vers
, kLLQ_Vers
); goto end
; }
1845 e
= LookupLLQ(d
, pkt
->src
, &q
.qname
, q
.qtype
, llq
->id
);
1848 // no entry - if zero ID, create new
1849 e
= NewLLQ(d
, pkt
->src
, &q
.qname
, q
.qtype
, llq
->lease
);
1852 UpdateLLQ(d
, e
, llq
, pkt
->msg
.h
.id
);
1861 mDNSlocal mDNSBool
IsLLQRequest(PktMsg
*pkt
)
1863 const mDNSu8
*ptr
= NULL
, *end
= (mDNSu8
*)&pkt
->msg
+ pkt
->len
;
1864 LargeCacheRecord lcr
;
1866 mDNSBool result
= mDNSfalse
;
1869 if ((mDNSu8
)(pkt
->msg
.h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
) != (mDNSu8
)(kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
)) goto end
;
1871 if (!pkt
->msg
.h
.numAdditionals
) goto end
;
1872 ptr
= LocateAdditionals(&pkt
->msg
, end
);
1875 // find last Additional
1876 for (i
= 0; i
< pkt
->msg
.h
.numAdditionals
; i
++)
1878 ptr
= GetLargeResourceRecord(NULL
, &pkt
->msg
, ptr
, end
, 0, kDNSRecordTypePacketAdd
, &lcr
);
1879 if (!ptr
) { Log("Unable to read additional record"); goto end
; }
1882 if (lcr
.r
.resrec
.rrtype
== kDNSType_OPT
&&
1883 lcr
.r
.resrec
.rdlength
>= LLQ_OPT_RDLEN
&&
1884 lcr
.r
.resrec
.rdata
->u
.opt
.opt
== kDNSOpt_LLQ
)
1885 { result
= mDNStrue
; goto end
; }
1892 // !!!KRS implement properly
1893 mDNSlocal mDNSBool
IsLLQAck(PktMsg
*pkt
)
1895 if (pkt
->msg
.h
.flags
.NotAnInteger
== ResponseFlags
.NotAnInteger
&&
1896 pkt
->msg
.h
.numQuestions
&& !pkt
->msg
.h
.numAnswers
&& !pkt
->msg
.h
.numAuthorities
) return mDNStrue
;
1901 // request handler wrappers for TCP and UDP requests
1902 // (read message off socket, fork thread that invokes main processing routine and handles cleanup)
1903 mDNSlocal
void *UDPUpdateRequestForkFn(void *vptr
)
1906 UDPRequestArgs
*req
= vptr
;
1907 PktMsg
*reply
= NULL
;
1909 VLog("Received UDP request: %d bytes from %s", req
->pkt
.len
, inet_ntop(AF_INET
, &req
->pkt
.src
.sin_addr
, buf
, 32));
1910 //!!!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
1911 reply
= HandleRequest(&req
->pkt
, req
->d
);
1914 if (sendto(req
->d
->udpsd
, &reply
->msg
, reply
->len
, 0, (struct sockaddr
*)&req
->pkt
.src
, sizeof(req
->pkt
.src
)) != (int)reply
->len
)
1915 LogErr("UDPUpdateRequestForkFn", "sendto");
1918 if (reply
) free(reply
);
1923 //!!!KRS this needs to be changed to use non-blocking sockets
1924 mDNSlocal
int RecvUDPRequest(int sd
, DaemonInfo
*d
)
1926 UDPRequestArgs
*req
;
1928 unsigned int clisize
= sizeof(req
->cliaddr
);
1930 req
= malloc(sizeof(UDPRequestArgs
));
1931 if (!req
) { LogErr("RecvUDPRequest", "malloc"); return -1; }
1932 bzero(req
, sizeof(*req
));
1934 req
->pkt
.len
= recvfrom(sd
, &req
->pkt
.msg
, sizeof(req
->pkt
.msg
), 0, (struct sockaddr
*)&req
->cliaddr
, &clisize
);
1935 if ((int)req
->pkt
.len
< 0) { LogErr("RecvUDPRequest", "recvfrom"); free(req
); return -1; }
1936 if (clisize
!= sizeof(req
->cliaddr
)) { Log("Client address of unknown size %d", clisize
); free(req
); return -1; }
1937 req
->pkt
.src
= req
->cliaddr
;
1939 if (IsLLQRequest(&req
->pkt
))
1941 // LLQ messages handled by main thread
1942 int err
= RecvLLQ(d
, &req
->pkt
);
1947 if (IsLLQAck(&req
->pkt
)) { free(req
); return 0; } // !!!KRS need to do acks + retrans
1949 if (pthread_create(&tid
, NULL
, UDPUpdateRequestForkFn
, req
)) { LogErr("RecvUDPRequest", "pthread_create"); free(req
); return -1; }
1950 pthread_detach(tid
);
1954 mDNSlocal
void *TCPRequestForkFn(void *vptr
)
1956 TCPRequestArgs
*req
= vptr
;
1957 PktMsg
*in
= NULL
, *out
= NULL
;
1960 //!!!KRS if this read blocks indefinitely, we can run out of threads
1962 in
= ReadTCPMsg(req
->sd
, NULL
);
1965 LogMsg("TCPRequestForkFn: Could not read message from %s", inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
1969 VLog("Received TCP request: %d bytes from %s", in
->len
, inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
1971 out
= HandleRequest(in
, req
->d
);
1974 LogMsg("TCPRequestForkFn: No reply for client %s", inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
1978 // deliver reply to client
1979 if (SendTCPMsg(req
->sd
, out
) < 0)
1981 LogMsg("TCPRequestForkFn: Unable to send reply to client %s", inet_ntop(AF_INET
, &req
->cliaddr
.sin_addr
, buf
, 32));
1992 mDNSlocal
int RecvTCPRequest(int sd
, DaemonInfo
*d
)
1994 TCPRequestArgs
*req
;
1996 unsigned int clilen
= sizeof(req
->cliaddr
);
1998 req
= malloc(sizeof(TCPRequestArgs
));
1999 if (!req
) { LogErr("RecvTCPRequest", "malloc"); return -1; }
2000 bzero(req
, sizeof(*req
));
2002 req
->sd
= accept(sd
, (struct sockaddr
*)&req
->cliaddr
, &clilen
);
2003 if (req
->sd
< 0) { LogErr("RecvTCPRequest", "accept"); return -1; }
2004 if (clilen
!= sizeof(req
->cliaddr
)) { Log("Client address of unknown size %d", clilen
); free(req
); return -1; }
2005 if (pthread_create(&tid
, NULL
, TCPRequestForkFn
, req
)) { LogErr("RecvTCPRequest", "pthread_create"); free(req
); return -1; }
2006 pthread_detach(tid
);
2011 // listen for incoming requests, periodically check table for expired records, respond to signals
2012 mDNSlocal
int ListenForUpdates(DaemonInfo
*d
)
2017 struct timeval timenow
, timeout
= { 0, 0 };
2018 long NextTableCheck
= 0;
2020 VLog("Listening for requests...");
2023 maxfdp1
= d
->tcpsd
+ 1;
2024 if (d
->udpsd
+ 1 > maxfdp1
) maxfdp1
= d
->udpsd
+ 1;
2025 if (d
->LLQEventListenSock
+ 1 > maxfdp1
) maxfdp1
= d
->LLQEventListenSock
+ 1;
2029 // expire records if necessary, set timeout
2030 if (gettimeofday(&timenow
, NULL
)) { LogErr("ListenForUpdates", "gettimeofday"); return -1; }
2031 if (timenow
.tv_sec
>= NextTableCheck
)
2033 DeleteExpiredRecords(d
);
2034 NextTableCheck
= timenow
.tv_sec
+ EXPIRATION_INTERVAL
;
2036 timeout
.tv_sec
= NextTableCheck
- timenow
.tv_sec
;
2038 FD_SET(d
->tcpsd
, &rset
);
2039 FD_SET(d
->udpsd
, &rset
);
2040 FD_SET(d
->LLQEventListenSock
, &rset
);
2042 err
= select(maxfdp1
, &rset
, NULL
, NULL
, &timeout
);
2047 if (terminate
) { DeleteExpiredRecords(d
); return 0; }
2048 else if (dumptable
) { PrintLeaseTable(d
); PrintLLQTable(d
); dumptable
= 0; }
2049 else Log("Received unhandled signal - continuing");
2051 else { LogErr("ListenForUpdates", "select"); return -1; }
2055 if (FD_ISSET(d
->tcpsd
, &rset
)) RecvTCPRequest(d
->tcpsd
, d
);
2056 if (FD_ISSET(d
->udpsd
, &rset
)) RecvUDPRequest(d
->udpsd
, d
);
2057 if (FD_ISSET(d
->LLQEventListenSock
, &rset
))
2059 // clear signalling data off socket
2061 recv(d
->LLQEventListenSock
, buf
, 32, 0);
2069 // signal handler sets global variables, which are inspected by main event loop
2070 // (select automatically returns due to the handled signal)
2071 mDNSlocal
void HndlSignal(int sig
)
2073 if (sig
== SIGTERM
|| sig
== SIGINT
) { terminate
= 1; return; }
2074 if (sig
== INFO_SIGNAL
) { dumptable
= 1; return; }
2077 int main(int argc
, char *argv
[])
2082 d
= malloc(sizeof(*d
));
2083 if (!d
) { LogErr("main", "malloc"); exit(1); }
2084 bzero(d
, sizeof(DaemonInfo
));
2086 if (signal(SIGTERM
, HndlSignal
) == SIG_ERR
) perror("Can't catch SIGTERM");
2087 if (signal(INFO_SIGNAL
, HndlSignal
) == SIG_ERR
) perror("Can't catch SIGINFO");
2088 if (signal(SIGINT
, HndlSignal
) == SIG_ERR
) perror("Can't catch SIGINT");
2089 if (signal(SIGPIPE
, SIG_IGN
) == SIG_ERR
) perror("Can't ignore SIGPIPE");
2091 if (ProcessArgs(argc
, argv
, d
) < 0) exit(1);
2097 LogErr("main", "daemon");
2098 fprintf(stderr
, "Could not daemonize process, running in foreground");
2103 if (InitLeaseTable(d
) < 0) exit(1);
2104 if (SetupSockets(d
) < 0) exit(1);
2105 if (SetUpdateSRV(d
) < 0) exit(1);
2107 if (pthread_create(&LLQtid
, NULL
, LLQEventMonitor
, d
)) { LogErr("main", "pthread_create"); }
2110 pthread_detach(LLQtid
);
2111 ListenForUpdates(d
);
2114 if (ClearUpdateSRV(d
) < 0) exit(1); // clear update srv's even if ListenForUpdates or pthread_create returns an error