#if __APPLE__
#define daemon yes_we_know_that_daemon_is_deprecated_in_os_x_10_5_thankyou
#endif
#include <signal.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <time.h>
#include <errno.h>
#if __APPLE__
#undef daemon
extern int daemon(int, int);
#endif
#ifdef NOT_HAVE_DAEMON
#include "../mDNSPosix/mDNSUNP.h" // For daemon()
#endif // NOT_HAVE_DAEMON
#include "dnsextd.h"
#include "../mDNSShared/uds_daemon.h"
#include "../mDNSShared/dnssd_ipc.h"
#include "../mDNSCore/uDNS.h"
#include "../mDNSShared/DebugServices.h"
#ifndef AF_LOCAL
#define AF_LOCAL AF_UNIX
#endif
mDNSexport const char ProgramName[] = "dnsextd";
#define LOOPBACK "127.0.0.1"
#if !defined(LISTENQ)
# define LISTENQ 128 // tcp connection backlog
#endif
#define RECV_BUFLEN 9000
#define LEASETABLE_INIT_NBUCKETS 256 // initial hashtable size (doubles as table fills)
#define EXPIRATION_INTERVAL 300 // check for expired records every 5 minutes
#define SRV_TTL 7200 // TTL For _dns-update SRV records
#define CONFIG_FILE "/etc/dnsextd.conf"
#define TCP_SOCKET_FLAGS kTCPSocketFlags_UseTLS
#define LLQ_MIN_LEASE (15 * 60)
#define LLQ_MAX_LEASE (120 * 60)
#define LLQ_LEASE_FUDGE 60
#define LLQ_MONITOR_ERR_INTERVAL (60 * 1000000)
#define LLQ_MONITOR_INTERVAL 250000
#ifdef SIGINFO
#define INFO_SIGNAL SIGINFO
#else
#define INFO_SIGNAL SIGUSR1
#endif
#define SAME_INADDR(x,y) (*((mDNSu32 *)&x) == *((mDNSu32 *)&y))
typedef struct
{
PktMsg pkt;
struct sockaddr_in cliaddr;
DaemonInfo *d;
int sd;
} UDPContext;
typedef struct
{
PktMsg pkt;
struct sockaddr_in cliaddr;
TCPSocket *sock; DaemonInfo *d;
} TCPContext;
typedef struct
{
DaemonInfo *d;
AnswerListElem *a;
} UpdateAnswerListArgs;
static mDNSBool foreground = 0;
static mDNSBool verbose = 0;
static mDNSBool terminate = 0;
static mDNSBool dumptable = 0;
static mDNSBool hangup = 0;
static char * cfgfile = NULL;
mDNSlocal void PrintLog(const char *buffer)
{
if (foreground)
{
fprintf(stderr,"%s\n", buffer);
fflush(stderr);
}
else
{
openlog("dnsextd", LOG_CONS, LOG_DAEMON);
syslog(LOG_ERR, "%s", buffer);
closelog();
}
}
mDNSlocal void VLog(const char *format, ...)
{
char buffer[512];
va_list ptr;
if (!verbose) return;
va_start(ptr,format);
buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
va_end(ptr);
PrintLog(buffer);
}
mDNSlocal void Log(const char *format, ...)
{
char buffer[512];
va_list ptr;
va_start(ptr,format);
buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
va_end(ptr);
PrintLog(buffer);
}
mDNSlocal void LogErr(const char *fn, const char *operation)
{
char buf[512], errbuf[256];
strerror_r(errno, errbuf, sizeof(errbuf));
snprintf(buf, sizeof(buf), "%s: %s - %s", fn, operation, errbuf);
PrintLog(buf);
}
mDNSlocal void HdrNToH(PktMsg *pkt)
{
mDNSu8 *ptr = (mDNSu8 *)&pkt->msg.h.numQuestions;
pkt->msg.h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
pkt->msg.h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
pkt->msg.h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
pkt->msg.h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
}
mDNSlocal void HdrHToN(PktMsg *pkt)
{
mDNSu16 numQuestions = pkt->msg.h.numQuestions;
mDNSu16 numAnswers = pkt->msg.h.numAnswers;
mDNSu16 numAuthorities = pkt->msg.h.numAuthorities;
mDNSu16 numAdditionals = pkt->msg.h.numAdditionals;
mDNSu8 *ptr = (mDNSu8 *)&pkt->msg.h.numQuestions;
*ptr++ = (mDNSu8)(numQuestions >> 8);
*ptr++ = (mDNSu8)(numQuestions & 0xFF);
*ptr++ = (mDNSu8)(numAnswers >> 8);
*ptr++ = (mDNSu8)(numAnswers & 0xFF);
*ptr++ = (mDNSu8)(numAuthorities >> 8);
*ptr++ = (mDNSu8)(numAuthorities & 0xFF);
*ptr++ = (mDNSu8)(numAdditionals >> 8);
*ptr++ = (mDNSu8)(numAdditionals & 0xFF);
}
mDNSlocal mStatus AddSourceToEventLoop( DaemonInfo * self, TCPSocket *sock, EventCallback callback, void *context )
{
EventSource * newSource;
mStatus err = mStatus_NoError;
if ( self->eventSources.LinkOffset == 0 )
{
InitLinkedList( &self->eventSources, offsetof( EventSource, next));
}
newSource = ( EventSource*) malloc( sizeof *newSource );
if ( newSource == NULL )
{
err = mStatus_NoMemoryErr;
goto exit;
}
newSource->callback = callback;
newSource->context = context;
newSource->sock = sock;
newSource->fd = mDNSPlatformTCPGetFD( sock );
AddToTail( &self->eventSources, newSource );
exit:
return err;
}
mDNSlocal mStatus RemoveSourceFromEventLoop( DaemonInfo * self, TCPSocket *sock )
{
EventSource * source;
mStatus err;
for ( source = ( EventSource* ) self->eventSources.Head; source; source = source->next )
{
if ( source->sock == sock )
{
RemoveFromList( &self->eventSources, source );
free( source );
err = mStatus_NoError;
goto exit;
}
}
err = mStatus_NoSuchNameErr;
exit:
return err;
}
mDNSlocal TCPSocket *ConnectToServer(DaemonInfo *d)
{
int ntries = 0, retry = 0;
while (1)
{
mDNSIPPort port = zeroIPPort;
int fd;
TCPSocket *sock = mDNSPlatformTCPSocket( NULL, 0, &port );
if ( !sock ) { LogErr("ConnectToServer", "socket"); return NULL; }
fd = mDNSPlatformTCPGetFD( sock );
if (!connect( fd, (struct sockaddr *)&d->ns_addr, sizeof(d->ns_addr))) return sock;
mDNSPlatformTCPCloseConnection( sock );
if (++ntries < 10)
{
LogErr("ConnectToServer", "connect");
Log("ConnectToServer - retrying connection");
if (!retry) retry = 500000 + random() % 500000;
usleep(retry);
retry *= 2;
}
else { Log("ConnectToServer - %d failed attempts. Aborting.", ntries); return NULL; }
}
}
mDNSlocal int MySend(TCPSocket *sock, const void *msg, int len)
{
int selectval, n, nsent = 0;
fd_set wset;
struct timeval timeout = { 3, 0 };
while (nsent < len)
{
int fd;
FD_ZERO(&wset);
fd = mDNSPlatformTCPGetFD( sock );
FD_SET( fd, &wset );
selectval = select( fd+1, NULL, &wset, NULL, &timeout);
if (selectval < 0) { LogErr("MySend", "select"); return -1; }
if (!selectval || !FD_ISSET(fd, &wset)) { Log("MySend - timeout"); return -1; }
n = mDNSPlatformWriteTCP( sock, ( char* ) msg + nsent, len - nsent);
if (n < 0) { LogErr("MySend", "send"); return -1; }
nsent += n;
}
return 0;
}
mDNSlocal int SendPacket(TCPSocket *sock, PktMsg *pkt)
{
mDNSu16 len = htons((mDNSu16)pkt->len);
if (MySend(sock, &len, sizeof(len)) < 0) return -1;
VLog("SendPacket Q:%d A:%d A:%d A:%d ",
ntohs(pkt->msg.h.numQuestions),
ntohs(pkt->msg.h.numAnswers),
ntohs(pkt->msg.h.numAuthorities),
ntohs(pkt->msg.h.numAdditionals));
return MySend(sock, &pkt->msg, pkt->len);
}
static int my_recv(TCPSocket *sock, void *const buf, const int len, mDNSBool * closed)
{
fd_set rset;
struct timeval timeout = { 3, 0 }; int selectval, remaining = len;
char *ptr = (char *)buf;
ssize_t num_read;
while (remaining)
{
int fd;
fd = mDNSPlatformTCPGetFD( sock );
FD_ZERO(&rset);
FD_SET(fd, &rset);
selectval = select(fd+1, &rset, NULL, NULL, &timeout);
if (selectval < 0) { LogErr("my_recv", "select"); return -1; }
if (!selectval || !FD_ISSET(fd, &rset))
{
Log("my_recv - timeout");
return -1;
}
num_read = mDNSPlatformReadTCP( sock, ptr, remaining, closed );
if (((num_read == 0) && *closed) || (num_read < 0) || (num_read > remaining)) return -1;
if (num_read == 0) return 0;
ptr += num_read;
remaining -= num_read;
}
return(len);
}
mDNSlocal PktMsg*
RecvPacket
(
TCPSocket * sock,
PktMsg * storage,
mDNSBool * closed
)
{
int nread;
int allocsize;
mDNSu16 msglen = 0;
PktMsg * pkt = NULL;
unsigned int srclen;
int fd;
mStatus err = 0;
fd = mDNSPlatformTCPGetFD( sock );
nread = my_recv( sock, &msglen, sizeof( msglen ), closed );
require_action_quiet( nread != -1, exit, err = mStatus_UnknownErr );
require_action_quiet( nread > 0, exit, err = mStatus_NoError );
msglen = ntohs( msglen );
require_action_quiet( nread == sizeof( msglen ), exit, err = mStatus_UnknownErr; Log( "Could not read length field of message") );
if ( storage )
{
require_action_quiet( msglen <= sizeof( storage->msg ), exit, err = mStatus_UnknownErr; Log( "RecvPacket: provided buffer too small." ) );
pkt = storage;
}
else
{
if ( msglen > sizeof(DNSMessage))
{
allocsize = sizeof(PktMsg) - sizeof(DNSMessage) + msglen;
}
else
{
allocsize = sizeof(PktMsg);
}
pkt = malloc(allocsize);
require_action_quiet( pkt, exit, err = mStatus_NoMemoryErr; LogErr( "RecvPacket", "malloc" ) );
mDNSPlatformMemZero( pkt, sizeof( *pkt ) );
}
pkt->len = msglen;
srclen = sizeof(pkt->src);
if ( getpeername( fd, ( struct sockaddr* ) &pkt->src, &srclen ) || ( srclen != sizeof( pkt->src ) ) )
{
LogErr("RecvPacket", "getpeername");
mDNSPlatformMemZero(&pkt->src, sizeof(pkt->src));
}
nread = my_recv(sock, &pkt->msg, msglen, closed );
require_action_quiet( nread >= 0, exit, err = mStatus_UnknownErr ; LogErr( "RecvPacket", "recv" ) );
require_action_quiet( nread == msglen, exit, err = mStatus_UnknownErr ; Log( "Could not read entire message" ) );
require_action_quiet( pkt->len >= sizeof( DNSMessageHeader ), exit, err = mStatus_UnknownErr ; Log( "RecvPacket: Message too short (%d bytes)", pkt->len ) );
exit:
if ( err && pkt )
{
if ( pkt != storage )
{
free(pkt);
}
pkt = NULL;
}
return pkt;
}
mDNSlocal DNSZone*
FindZone
(
DaemonInfo * self,
domainname * name
)
{
DNSZone * zone;
for ( zone = self->zones; zone; zone = zone->next )
{
if ( SameDomainName( &zone->name, name ) )
{
break;
}
}
return zone;
}
mDNSlocal mDNSBool
ZoneHandlesName
(
const domainname * zname,
const domainname * dname
)
{
mDNSu16 i = DomainNameLength( zname );
mDNSu16 j = DomainNameLength( dname );
if ( ( i == ( MAX_DOMAIN_NAME + 1 ) ) || ( j == ( MAX_DOMAIN_NAME + 1 ) ) || ( i > j ) || ( memcmp( zname->c, dname->c + ( j - i ), i ) != 0 ) )
{
return mDNSfalse;
}
return mDNStrue;
}
mDNSlocal mDNSBool IsQuery( PktMsg * pkt )
{
return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery );
}
mDNSlocal mDNSBool IsUpdate( PktMsg * pkt )
{
return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_OP_Update );
}
mDNSlocal mDNSBool IsNotify(PktMsg *pkt)
{
return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == ( mDNSu8) ( kDNSFlag0_OP_Notify );
}
mDNSlocal mDNSBool IsLLQRequest(PktMsg *pkt)
{
const mDNSu8 *ptr = NULL, *end = (mDNSu8 *)&pkt->msg + pkt->len;
LargeCacheRecord lcr;
int i;
mDNSBool result = mDNSfalse;
HdrNToH(pkt);
if ((mDNSu8)(pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (mDNSu8)(kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery)) goto end;
if (!pkt->msg.h.numAdditionals) goto end;
ptr = LocateAdditionals(&pkt->msg, end);
if (!ptr) goto end;
for (i = 0; i < pkt->msg.h.numAdditionals; i++)
{
ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr);
if (!ptr) { Log("Unable to read additional record"); goto end; }
}
if ( lcr.r.resrec.rrtype == kDNSType_OPT && lcr.r.resrec.rdlength >= DNSOpt_LLQData_Space && lcr.r.resrec.rdata->u.opt[0].opt == kDNSOpt_LLQ )
{
result = mDNStrue;
}
end:
HdrHToN(pkt);
return result;
}
mDNSlocal mDNSBool IsLLQAck(PktMsg *pkt)
{
if ((pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery ) &&
pkt->msg.h.numQuestions && !pkt->msg.h.numAnswers && !pkt->msg.h.numAuthorities) return mDNStrue;
return mDNSfalse;
}
mDNSlocal mDNSBool
IsPublicSRV
(
DaemonInfo * self,
DNSQuestion * q
)
{
DNameListElem * elem;
mDNSBool ret = mDNSfalse;
int i = ( int ) DomainNameLength( &q->qname ) - 1;
for ( elem = self->public_names; elem; elem = elem->next )
{
int j = ( int ) DomainNameLength( &elem->name ) - 1;
if ( i > j )
{
for ( ; i >= 0; i--, j-- )
{
if ( q->qname.c[ i ] != elem->name.c[ j ] )
{
ret = mDNStrue;
goto exit;
}
}
}
}
exit:
return ret;
}
mDNSlocal void
SetZone
(
DaemonInfo * self,
PktMsg * pkt
)
{
domainname zname;
mDNSu8 QR_OP;
const mDNSu8 * ptr = pkt->msg.data;
mDNSBool exception = mDNSfalse;
pkt->zone = NULL;
pkt->isZonePublic = mDNStrue;
zname.c[0] = '\0';
QR_OP = ( mDNSu8 ) ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask );
if ( IsQuery( pkt ) )
{
DNSQuestion question;
ptr = getQuestion( &pkt->msg, ptr, ( ( mDNSu8* ) &pkt->msg ) + pkt->len, NULL, &question );
AppendDomainName( &zname, &question.qname );
exception = ( ( question.qtype == kDNSType_SOA ) || ( question.qtype == kDNSType_NS ) || ( ( question.qtype == kDNSType_SRV ) && IsPublicSRV( self, &question ) ) );
}
else if ( IsUpdate( pkt ) )
{
DNSQuestion question;
ptr = getQuestion( &pkt->msg, ptr, ( ( mDNSu8* ) &pkt->msg ) + pkt->len, NULL, &question );
AppendDomainName( &zname, &question.qname );
exception = mDNSfalse;
}
if ( zname.c[0] != '\0' )
{
for ( pkt->zone = self->zones; pkt->zone; pkt->zone = pkt->zone->next )
{
if ( ZoneHandlesName( &pkt->zone->name, &zname ) )
{
VLog( "found correct zone %##s for query", pkt->zone->name.c );
pkt->isZonePublic = ( ( pkt->zone->type == kDNSZonePublic ) || exception );
VLog( "zone %##s is %s", pkt->zone->name.c, ( pkt->isZonePublic ) ? "public" : "private" );
break;
}
}
}
}
mDNSlocal int
UDPServerTransaction(const DaemonInfo *d, const PktMsg *request, PktMsg *reply, mDNSBool *trunc)
{
fd_set rset;
struct timeval timeout = { 3, 0 }; int sd;
int res;
mStatus err = mStatus_NoError;
*trunc = mDNSfalse;
sd = socket( AF_INET, SOCK_DGRAM, 0 );
require_action( sd >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "socket" ) );
VLog("UDPServerTransaction Q:%d A:%d A:%d A:%d ",
ntohs(request->msg.h.numQuestions),
ntohs(request->msg.h.numAnswers),
ntohs(request->msg.h.numAuthorities),
ntohs(request->msg.h.numAdditionals));
res = sendto( sd, (char *)&request->msg, request->len, 0, ( struct sockaddr* ) &d->ns_addr, sizeof( d->ns_addr ) );
require_action( res == (int) request->len, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "sendto" ) );
FD_ZERO( &rset );
FD_SET( sd, &rset );
res = select( sd + 1, &rset, NULL, NULL, &timeout );
require_action( res >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "select" ) );
require_action( ( res > 0 ) && FD_ISSET( sd, &rset ), exit, err = mStatus_UnknownErr; Log( "UDPServerTransaction - timeout" ) );
reply->len = recvfrom( sd, &reply->msg, sizeof(reply->msg), 0, NULL, NULL );
require_action( ( ( int ) reply->len ) >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "recvfrom" ) );
require_action( reply->len >= sizeof( DNSMessageHeader ), exit, err = mStatus_UnknownErr; Log( "UDPServerTransaction - Message too short (%d bytes)", reply->len ) );
if ( reply->msg.h.flags.b[0] & kDNSFlag0_TC )
{
*trunc = mDNStrue;
}
exit:
if ( sd >= 0 )
{
close( sd );
}
return err;
}
mDNSlocal mDNSBool SuccessfulUpdateTransaction(PktMsg *request, PktMsg *reply)
{
char buf[32];
char *vlogmsg = NULL;
if (!request || !reply) { vlogmsg = "NULL message"; goto failure; }
if (request->len < sizeof(DNSMessageHeader) || reply->len < sizeof(DNSMessageHeader)) { vlogmsg = "Malformatted message"; goto failure; }
if ((request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask))
{ vlogmsg = "Request opcode not an update"; goto failure; }
if ((reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask)) { vlogmsg = "Reply contains non-zero rcode"; goto failure; }
if ((reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (kDNSFlag0_OP_Update | kDNSFlag0_QR_Response))
{ vlogmsg = "Reply opcode not an update response"; goto failure; }
VLog("Successful update from %s", inet_ntop(AF_INET, &request->src.sin_addr, buf, 32));
return mDNStrue;
failure:
VLog("Request %s: %s", inet_ntop(AF_INET, &request->src.sin_addr, buf, 32), vlogmsg);
return mDNSfalse;
}
mDNSlocal CacheRecord *CopyCacheRecord(const CacheRecord *orig, domainname *name)
{
CacheRecord *cr;
size_t size = sizeof(*cr);
if (orig->resrec.rdlength > InlineCacheRDSize) size += orig->resrec.rdlength - InlineCacheRDSize;
cr = malloc(size);
if (!cr) { LogErr("CopyCacheRecord", "malloc"); return NULL; }
memcpy(cr, orig, size);
cr->resrec.rdata = (RData*)&cr->smallrdatastorage;
cr->resrec.name = name;
return cr;
}
mDNSlocal void RehashTable(DaemonInfo *d)
{
RRTableElem *ptr, *tmp, **new;
int i, bucket, newnbuckets = d->nbuckets * 2;
VLog("Rehashing lease table (new size %d buckets)", newnbuckets);
new = malloc(sizeof(RRTableElem *) * newnbuckets);
if (!new) { LogErr("RehashTable", "malloc"); return; }
mDNSPlatformMemZero(new, newnbuckets * sizeof(RRTableElem *));
for (i = 0; i < d->nbuckets; i++)
{
ptr = d->table[i];
while (ptr)
{
bucket = ptr->rr.resrec.namehash % newnbuckets;
tmp = ptr;
ptr = ptr->next;
tmp->next = new[bucket];
new[bucket] = tmp;
}
}
d->nbuckets = newnbuckets;
free(d->table);
d->table = new;
}
mDNSlocal void PrintLeaseTable(DaemonInfo *d)
{
int i;
RRTableElem *ptr;
char rrbuf[MaxMsg], addrbuf[16];
struct timeval now;
int hr, min, sec;
if (gettimeofday(&now, NULL)) { LogErr("PrintTable", "gettimeofday"); return; }
if (pthread_mutex_lock(&d->tablelock)) { LogErr("PrintTable", "pthread_mutex_lock"); return; }
Log("Dumping Lease Table Contents (table contains %d resource records)", d->nelems);
for (i = 0; i < d->nbuckets; i++)
{
for (ptr = d->table[i]; ptr; ptr = ptr->next)
{
hr = ((ptr->expire - now.tv_sec) / 60) / 60;
min = ((ptr->expire - now.tv_sec) / 60) % 60;
sec = (ptr->expire - now.tv_sec) % 60;
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,
GetRRDisplayString_rdb(&ptr->rr.resrec, &ptr->rr.resrec.rdata->u, rrbuf));
}
}
pthread_mutex_unlock(&d->tablelock);
}
mDNSlocal mDNSu8 *putRRSetDeletion(DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit, ResourceRecord *rr)
{
ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
if (!ptr || ptr + 10 >= limit) return NULL; ptr[0] = (mDNSu8)(rr->rrtype >> 8);
ptr[1] = (mDNSu8)(rr->rrtype & 0xFF);
ptr[2] = (mDNSu8)((mDNSu16)kDNSQClass_ANY >> 8);
ptr[3] = (mDNSu8)((mDNSu16)kDNSQClass_ANY & 0xFF);
mDNSPlatformMemZero(ptr+4, sizeof(rr->rroriginalttl) + sizeof(rr->rdlength)); msg->h.mDNS_numUpdates++;
return ptr + 10;
}
mDNSlocal mDNSu8 *PutUpdateSRV(DaemonInfo *d, DNSZone * zone, PktMsg *pkt, mDNSu8 *ptr, char *regtype, mDNSIPPort port, mDNSBool registration)
{
AuthRecord rr;
char hostname[1024], buf[MaxMsg];
mDNSu8 *end = (mDNSu8 *)&pkt->msg + sizeof(DNSMessage);
( void ) d;
mDNS_SetupResourceRecord(&rr, NULL, 0, kDNSType_SRV, SRV_TTL, kDNSRecordTypeUnique, AuthRecordAny, NULL, NULL);
rr.resrec.rrclass = kDNSClass_IN;
rr.resrec.rdata->u.srv.priority = 0;
rr.resrec.rdata->u.srv.weight = 0;
rr.resrec.rdata->u.srv.port = port;
if (gethostname(hostname, 1024) < 0 || !MakeDomainNameFromDNSNameString(&rr.resrec.rdata->u.srv.target, hostname))
rr.resrec.rdata->u.srv.target.c[0] = '\0';
MakeDomainNameFromDNSNameString(&rr.namestorage, regtype);
AppendDomainName(&rr.namestorage, &zone->name);
VLog("%s %s", registration ? "Registering SRV record" : "Deleting existing RRSet",
GetRRDisplayString_rdb(&rr.resrec, &rr.resrec.rdata->u, buf));
if (registration) ptr = PutResourceRecord(&pkt->msg, ptr, &pkt->msg.h.mDNS_numUpdates, &rr.resrec);
else ptr = putRRSetDeletion(&pkt->msg, ptr, end, &rr.resrec);
return ptr;
}
mDNSlocal int UpdateSRV(DaemonInfo *d, mDNSBool registration)
{
TCPSocket *sock = NULL;
DNSZone * zone;
int err = mStatus_NoError;
sock = ConnectToServer( d );
require_action( sock, exit, err = mStatus_UnknownErr; Log( "UpdateSRV: ConnectToServer failed" ) );
for ( zone = d->zones; zone; zone = zone->next )
{
PktMsg pkt;
mDNSu8 *ptr = pkt.msg.data;
mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage);
PktMsg *reply = NULL;
mDNSBool closed;
mDNSBool ok;
InitializeDNSMessage(&pkt.msg.h, zeroID, UpdateReqFlags);
pkt.src.sin_addr.s_addr = zerov4Addr.NotAnInteger; pkt.src.sin_family = AF_INET;
ptr = putZone(&pkt.msg, ptr, end, &zone->name, mDNSOpaque16fromIntVal(kDNSClass_IN));
require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
if ( zone->type == kDNSZonePrivate )
{
ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update-tls._tcp.", d->private_port, registration);
require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-query-tls._tcp.", d->private_port, registration);
require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq-tls._tcp.", d->private_port, registration);
require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
if ( !registration )
{
ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update._udp.", d->llq_port, registration);
require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq._udp.", d->llq_port, registration);
require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
}
}
else
{
if ( !registration )
{
ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update-tls.", d->private_port, registration);
require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-query-tls.", d->private_port, registration);
require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq-tls.", d->private_port, registration);
require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
}
ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update._udp.", d->llq_port, registration);
require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq._udp.", d->llq_port, registration);
require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
}
HdrHToN(&pkt);
if ( zone->updateKeys )
{
DNSDigest_SignMessage( &pkt.msg, &ptr, zone->updateKeys, 0 );
require_action( ptr, exit, Log("UpdateSRV: Error constructing lease expiration update" ) );
}
pkt.len = ptr - (mDNSu8 *)&pkt.msg;
err = SendPacket( sock, &pkt );
require_action( !err, exit, Log( "UpdateSRV: SendPacket failed" ) );
reply = RecvPacket( sock, NULL, &closed );
require_action( reply, exit, err = mStatus_UnknownErr; Log( "UpdateSRV: RecvPacket returned NULL" ) );
ok = SuccessfulUpdateTransaction( &pkt, reply );
if ( !ok )
{
Log("SRV record registration failed with rcode %d", reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask);
}
free( reply );
}
exit:
if ( sock )
{
mDNSPlatformTCPCloseConnection( sock );
}
return err;
}
#define ClearUpdateSRV(d) UpdateSRV(d, 0)
mDNSlocal int SetUpdateSRV(DaemonInfo *d)
{
int err;
err = ClearUpdateSRV(d); if (!err) err = UpdateSRV(d, 1);
return err;
}
mDNSlocal void PrintUsage(void)
{
fprintf(stderr, "Usage: dnsextd [-f <config file>] [-vhd] ...\n"
"Use \"dnsextd -h\" for help\n");
}
mDNSlocal void PrintHelp(void)
{
fprintf(stderr, "\n\n");
PrintUsage();
fprintf(stderr,
"dnsextd is a daemon that implements DNS extensions supporting Dynamic DNS Update Leases\n"
"and Long Lived Queries, used in Wide-Area DNS Service Discovery, on behalf of name servers\n"
"that do not natively support these extensions. (See dns-sd.org for more info on DNS Service\n"
"Discovery, Update Leases, and Long Lived Queries.)\n\n"
"dnsextd requires one argument,the zone, which is the domain for which Update Leases\n"
"and Long Lived Queries are to be administered. dnsextd communicates directly with the\n"
"primary master server for this zone.\n\n"
"The options are as follows:\n\n"
"-f Specify configuration file. The default is /etc/dnsextd.conf.\n\n"
"-d Run daemon in foreground.\n\n"
"-h Print help.\n\n"
"-v Verbose output.\n\n"
);
}
mDNSlocal int ProcessArgs(int argc, char *argv[], DaemonInfo *d)
{
DNSZone * zone;
int opt;
int err = 0;
cfgfile = strdup( CONFIG_FILE );
require_action( cfgfile, arg_error, err = mStatus_NoMemoryErr );
mDNSPlatformMemZero( &d->addr, sizeof( d->addr ) );
d->addr.sin_addr.s_addr = zerov4Addr.NotAnInteger;
d->addr.sin_port = UnicastDNSPort.NotAnInteger;
d->addr.sin_family = AF_INET;
#ifndef NOT_HAVE_SA_LEN
d->addr.sin_len = sizeof( d->addr );
#endif
mDNSPlatformMemZero(&d->ns_addr, sizeof(d->ns_addr));
d->ns_addr.sin_family = AF_INET;
inet_pton( AF_INET, LOOPBACK, &d->ns_addr.sin_addr );
d->ns_addr.sin_port = NSIPCPort.NotAnInteger;
#ifndef NOT_HAVE_SA_LEN
d->ns_addr.sin_len = sizeof( d->ns_addr );
#endif
d->private_port = PrivateDNSPort;
d->llq_port = DNSEXTPort;
while ((opt = getopt(argc, argv, "f:hdv")) != -1)
{
switch(opt)
{
case 'f': free( cfgfile ); cfgfile = strdup( optarg ); require_action( cfgfile, arg_error, err = mStatus_NoMemoryErr ); break;
case 'h': PrintHelp(); return -1;
case 'd': foreground = 1; break; case 'v': verbose = 1; break;
default: goto arg_error;
}
}
err = ParseConfig( d, cfgfile );
require_noerr( err, arg_error );
require_action( d->zones, arg_error, err = mStatus_UnknownErr );
for ( zone = d->zones; zone; zone = zone->next )
{
if ( zone->updateKeys )
{
AssignDomainName( &zone->updateKeys->domain, &zone->name );
}
}
return 0;
arg_error:
PrintUsage();
return -1;
}
mDNSlocal int InitLeaseTable(DaemonInfo *d)
{
if (pthread_mutex_init(&d->tablelock, NULL)) { LogErr("InitLeaseTable", "pthread_mutex_init"); return -1; }
d->nbuckets = LEASETABLE_INIT_NBUCKETS;
d->nelems = 0;
d->table = malloc(sizeof(RRTableElem *) * LEASETABLE_INIT_NBUCKETS);
if (!d->table) { LogErr("InitLeaseTable", "malloc"); return -1; }
mDNSPlatformMemZero(d->table, sizeof(RRTableElem *) * LEASETABLE_INIT_NBUCKETS);
return 0;
}
mDNSlocal int
SetupSockets
(
DaemonInfo * self
)
{
static const int kOn = 1;
int sockpair[2];
mDNSBool private = mDNSfalse;
struct sockaddr_in daddr;
DNSZone * zone;
mStatus err = 0;
self->tcpsd = socket( AF_INET, SOCK_STREAM, 0 );
require_action( dnssd_SocketValid(self->tcpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
#if defined(SO_REUSEADDR)
err = setsockopt(self->tcpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->tcpsd" ) );
#endif
err = bind( self->tcpsd, ( struct sockaddr* ) &self->addr, sizeof( self->addr ) );
require_action( !err, exit, LogErr( "SetupSockets", "bind self->tcpsd" ) );
err = listen( self->tcpsd, LISTENQ );
require_action( !err, exit, LogErr( "SetupSockets", "listen" ) );
self->udpsd = socket( AF_INET, SOCK_DGRAM, 0 );
require_action( dnssd_SocketValid(self->udpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
#if defined(SO_REUSEADDR)
err = setsockopt(self->udpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->udpsd" ) );
#endif
err = bind( self->udpsd, ( struct sockaddr* ) &self->addr, sizeof( self->addr ) );
require_action( !err, exit, LogErr( "SetupSockets", "bind self->udpsd" ) );
mDNSPlatformMemZero(&self->llq_addr, sizeof(self->llq_addr));
self->llq_addr.sin_family = AF_INET;
self->llq_addr.sin_addr.s_addr = zerov4Addr.NotAnInteger;
self->llq_addr.sin_port = ( self->llq_port.NotAnInteger ) ? self->llq_port.NotAnInteger : DNSEXTPort.NotAnInteger;
if (self->llq_addr.sin_port == self->addr.sin_port)
{
self->llq_tcpsd = self->tcpsd;
self->llq_udpsd = self->udpsd;
}
else
{
self->llq_tcpsd = socket( AF_INET, SOCK_STREAM, 0 );
require_action( dnssd_SocketValid(self->llq_tcpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
#if defined(SO_REUSEADDR)
err = setsockopt(self->llq_tcpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->llq_tcpsd" ) );
#endif
err = bind( self->llq_tcpsd, ( struct sockaddr* ) &self->llq_addr, sizeof( self->llq_addr ) );
require_action( !err, exit, LogErr( "SetupSockets", "bind self->llq_tcpsd" ) );
err = listen( self->llq_tcpsd, LISTENQ );
require_action( !err, exit, LogErr( "SetupSockets", "listen" ) );
self->llq_udpsd = socket( AF_INET, SOCK_DGRAM, 0 );
require_action( dnssd_SocketValid(self->llq_udpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
#if defined(SO_REUSEADDR)
err = setsockopt(self->llq_udpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->llq_udpsd" ) );
#endif
err = bind(self->llq_udpsd, ( struct sockaddr* ) &self->llq_addr, sizeof( self->llq_addr ) );
require_action( !err, exit, LogErr( "SetupSockets", "bind self->llq_udpsd" ) );
}
err = socketpair( AF_LOCAL, SOCK_STREAM, 0, sockpair );
require_action( !err, exit, LogErr( "SetupSockets", "socketpair" ) );
self->LLQEventListenSock = sockpair[0];
self->LLQEventNotifySock = sockpair[1];
self->llq_tcpsd = socket( AF_INET, SOCK_STREAM, 0 );
require_action( dnssd_SocketValid(self->tlssd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
mDNSPlatformMemZero(&daddr, sizeof(daddr));
daddr.sin_family = AF_INET;
daddr.sin_addr.s_addr = zerov4Addr.NotAnInteger;
daddr.sin_port = ( self->private_port.NotAnInteger ) ? self->private_port.NotAnInteger : PrivateDNSPort.NotAnInteger;
self->tlssd = socket( AF_INET, SOCK_STREAM, 0 );
require_action( dnssd_SocketValid(self->tlssd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
#if defined(SO_REUSEADDR)
err = setsockopt(self->tlssd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->tlssd" ) );
#endif
err = bind( self->tlssd, ( struct sockaddr* ) &daddr, sizeof( daddr ) );
require_action( !err, exit, LogErr( "SetupSockets", "bind self->tlssd" ) );
err = listen( self->tlssd, LISTENQ );
require_action( !err, exit, LogErr( "SetupSockets", "listen" ) );
for ( zone = self->zones; zone; zone = zone->next )
{
if ( zone->type == kDNSZonePrivate )
{
private = mDNStrue;
break;
}
}
if ( private )
{
err = mDNSPlatformTLSSetupCerts();
require_action( !err, exit, LogErr( "SetupSockets", "mDNSPlatformTLSSetupCerts" ) );
}
exit:
return err;
}
mDNSlocal void DeleteOneRecord(DaemonInfo *d, CacheRecord *rr, domainname *zname, TCPSocket *sock)
{
DNSZone * zone;
PktMsg pkt;
mDNSu8 *ptr = pkt.msg.data;
mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage);
char buf[MaxMsg];
mDNSBool closed;
PktMsg *reply = NULL;
VLog("Expiring record %s", GetRRDisplayString_rdb(&rr->resrec, &rr->resrec.rdata->u, buf));
InitializeDNSMessage(&pkt.msg.h, zeroID, UpdateReqFlags);
ptr = putZone(&pkt.msg, ptr, end, zname, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
if (!ptr) goto end;
ptr = putDeletionRecord(&pkt.msg, ptr, &rr->resrec);
if (!ptr) goto end;
HdrHToN(&pkt);
zone = FindZone( d, zname );
if ( zone && zone->updateKeys)
{
DNSDigest_SignMessage(&pkt.msg, &ptr, zone->updateKeys, 0 );
if (!ptr) goto end;
}
pkt.len = ptr - (mDNSu8 *)&pkt.msg;
pkt.src.sin_addr.s_addr = zerov4Addr.NotAnInteger; pkt.src.sin_family = AF_INET;
if (SendPacket( sock, &pkt)) { Log("DeleteOneRecord: SendPacket failed"); }
reply = RecvPacket( sock, NULL, &closed );
if (reply) HdrNToH(reply);
require_action( reply, end, Log( "DeleteOneRecord: RecvPacket returned NULL" ) );
if (!SuccessfulUpdateTransaction(&pkt, reply))
Log("Expiration update failed with rcode %d", reply ? reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask : -1);
end:
if (!ptr) { Log("DeleteOneRecord: Error constructing lease expiration update"); }
if (reply) free(reply);
}
mDNSlocal void DeleteRecords(DaemonInfo *d, mDNSBool DeleteAll)
{
struct timeval now;
int i;
TCPSocket *sock = ConnectToServer(d);
if (!sock) { Log("DeleteRecords: ConnectToServer failed"); return; }
if (gettimeofday(&now, NULL)) { LogErr("DeleteRecords ", "gettimeofday"); return; }
if (pthread_mutex_lock(&d->tablelock)) { LogErr("DeleteRecords", "pthread_mutex_lock"); return; }
for (i = 0; i < d->nbuckets; i++)
{
RRTableElem **ptr = &d->table[i];
while (*ptr)
{
if (DeleteAll || (*ptr)->expire - now.tv_sec < 0)
{
RRTableElem *fptr;
DeleteOneRecord(d, &(*ptr)->rr, &(*ptr)->zone, sock);
fptr = *ptr;
*ptr = (*ptr)->next;
free(fptr);
d->nelems--;
}
else ptr = &(*ptr)->next;
}
}
pthread_mutex_unlock(&d->tablelock);
mDNSPlatformTCPCloseConnection( sock );
}
mDNSlocal void UpdateLeaseTable(PktMsg *pkt, DaemonInfo *d, mDNSs32 lease)
{
RRTableElem **rptr, *tmp;
int i, allocsize, bucket;
LargeCacheRecord lcr;
ResourceRecord *rr = &lcr.r.resrec;
const mDNSu8 *ptr, *end;
struct timeval tv;
DNSQuestion zone;
char buf[MaxMsg];
if (pthread_mutex_lock(&d->tablelock)) { LogErr("UpdateLeaseTable", "pthread_mutex_lock"); return; }
HdrNToH(pkt);
ptr = pkt->msg.data;
end = (mDNSu8 *)&pkt->msg + pkt->len;
ptr = getQuestion(&pkt->msg, ptr, end, 0, &zone);
if (!ptr) { Log("UpdateLeaseTable: cannot read zone"); goto cleanup; }
ptr = LocateAuthorities(&pkt->msg, end);
if (!ptr) { Log("UpdateLeaseTable: Format error"); goto cleanup; }
for (i = 0; i < pkt->msg.h.mDNS_numUpdates; i++)
{
mDNSBool DeleteAllRRSets = mDNSfalse, DeleteOneRRSet = mDNSfalse, DeleteOneRR = mDNSfalse;
ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
if (!ptr || lcr.r.resrec.RecordType == kDNSRecordTypePacketNegative) { Log("UpdateLeaseTable: GetLargeResourceRecord failed"); goto cleanup; }
bucket = rr->namehash % d->nbuckets;
rptr = &d->table[bucket];
if (rr->rrtype == kDNSQType_ANY && !rr->rroriginalttl && rr->rrclass == kDNSQClass_ANY && !rr->rdlength)
DeleteAllRRSets = mDNStrue; else if (!rr->rroriginalttl && rr->rrclass == kDNSQClass_ANY && !rr->rdlength)
DeleteOneRRSet = mDNStrue;
else if (!rr->rroriginalttl && rr->rrclass == kDNSClass_NONE)
DeleteOneRR = mDNStrue;
if (DeleteAllRRSets || DeleteOneRRSet || DeleteOneRR)
{
while (*rptr)
{
if (SameDomainName((*rptr)->rr.resrec.name, rr->name) &&
(DeleteAllRRSets ||
(DeleteOneRRSet && (*rptr)->rr.resrec.rrtype == rr->rrtype) ||
(DeleteOneRR && IdenticalResourceRecord(&(*rptr)->rr.resrec, rr))))
{
tmp = *rptr;
VLog("Received deletion update for %s", GetRRDisplayString_rdb(&tmp->rr.resrec, &tmp->rr.resrec.rdata->u, buf));
*rptr = (*rptr)->next;
free(tmp);
d->nelems--;
}
else rptr = &(*rptr)->next;
}
}
else if (lease > 0)
{
while (*rptr && !IdenticalResourceRecord(&(*rptr)->rr.resrec, rr)) rptr = &(*rptr)->next;
if (*rptr)
{
if (gettimeofday(&tv, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; }
(*rptr)->expire = tv.tv_sec + (unsigned)lease;
VLog("Refreshing lease for %s", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf));
}
else
{
if (d->nelems > d->nbuckets)
{
RehashTable(d);
bucket = rr->namehash % d->nbuckets;
rptr = &d->table[bucket];
}
if (gettimeofday(&tv, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; }
allocsize = sizeof(RRTableElem);
if (rr->rdlength > InlineCacheRDSize) allocsize += (rr->rdlength - InlineCacheRDSize);
tmp = malloc(allocsize);
if (!tmp) { LogErr("UpdateLeaseTable", "malloc"); goto cleanup; }
memcpy(&tmp->rr, &lcr.r, sizeof(CacheRecord) + rr->rdlength - InlineCacheRDSize);
tmp->rr.resrec.rdata = (RData *)&tmp->rr.smallrdatastorage;
AssignDomainName(&tmp->name, rr->name);
tmp->rr.resrec.name = &tmp->name;
tmp->expire = tv.tv_sec + (unsigned)lease;
tmp->cli.sin_addr = pkt->src.sin_addr;
AssignDomainName(&tmp->zone, &zone.qname);
tmp->next = d->table[bucket];
d->table[bucket] = tmp;
d->nelems++;
VLog("Adding update for %s to lease table", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf));
}
}
}
cleanup:
pthread_mutex_unlock(&d->tablelock);
HdrHToN(pkt);
}
mDNSlocal PktMsg *FormatLeaseReply(DaemonInfo *d, PktMsg *orig, mDNSu32 lease)
{
PktMsg *reply;
mDNSu8 *ptr, *end;
mDNSOpaque16 flags;
(void)d; reply = malloc(sizeof(*reply));
if (!reply) { LogErr("FormatLeaseReply", "malloc"); return NULL; }
flags.b[0] = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update;
flags.b[1] = 0;
InitializeDNSMessage(&reply->msg.h, orig->msg.h.id, flags);
reply->src.sin_addr.s_addr = zerov4Addr.NotAnInteger; reply->src.sin_family = AF_INET;
ptr = reply->msg.data;
end = (mDNSu8 *)&reply->msg + sizeof(DNSMessage);
ptr = putUpdateLease(&reply->msg, ptr, lease);
if (!ptr) { Log("FormatLeaseReply: putUpdateLease failed"); free(reply); return NULL; }
reply->len = ptr - (mDNSu8 *)&reply->msg;
HdrHToN(reply);
return reply;
}
mDNSlocal PktMsg*
HandleRequest
(
DaemonInfo * self,
PktMsg * request
)
{
PktMsg * reply = NULL;
PktMsg * leaseReply;
PktMsg buf;
char addrbuf[32];
TCPSocket * sock = NULL;
mStatus err;
mDNSs32 lease = 0;
if ((request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) == kDNSFlag0_OP_Update)
{
int i, adds = 0, dels = 0;
const mDNSu8 *ptr, *end = (mDNSu8 *)&request->msg + request->len;
HdrNToH(request);
lease = GetPktLease(&mDNSStorage, &request->msg, end);
ptr = LocateAuthorities(&request->msg, end);
for (i = 0; i < request->msg.h.mDNS_numUpdates; i++)
{
LargeCacheRecord lcr;
ptr = GetLargeResourceRecord(NULL, &request->msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
if (lcr.r.resrec.RecordType != kDNSRecordTypePacketNegative && lcr.r.resrec.rroriginalttl) adds++; else dels++;
}
HdrHToN(request);
if (adds && !lease)
{
static const mDNSOpaque16 UpdateRefused = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, kDNSFlag1_RC_Refused } };
Log("Rejecting Update Request with %d additions but no lease", adds);
reply = malloc(sizeof(*reply));
mDNSPlatformMemZero(&reply->src, sizeof(reply->src));
reply->len = sizeof(DNSMessageHeader);
reply->zone = NULL;
reply->isZonePublic = 0;
InitializeDNSMessage(&reply->msg.h, request->msg.h.id, UpdateRefused);
return(reply);
}
if (lease > 7200) lease = 7200;
}
if ( request->len <= 512 )
{
mDNSBool trunc;
if ( UDPServerTransaction( self, request, &buf, &trunc) < 0 )
{
Log("HandleRequest - UDPServerTransaction failed. Trying TCP");
}
else if ( trunc )
{
VLog("HandleRequest - answer truncated. Using TCP");
}
else
{
reply = &buf; }
}
if ( !reply )
{
mDNSBool closed;
int res;
sock = ConnectToServer( self );
require_action_quiet( sock, exit, err = mStatus_UnknownErr ; Log( "Discarding request from %s due to connection errors", inet_ntop( AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) );
res = SendPacket( sock, request );
require_action_quiet( res >= 0, exit, err = mStatus_UnknownErr ; Log( "Couldn't relay message from %s to server. Discarding.", inet_ntop(AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) );
reply = RecvPacket( sock, &buf, &closed );
}
if ( reply && ( ( reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == ( kDNSFlag0_OP_Update | kDNSFlag0_QR_Response ) ) )
{
char pingmsg[4];
mDNSBool ok = SuccessfulUpdateTransaction( request, reply );
require_action( ok, exit, err = mStatus_UnknownErr; VLog( "Message from %s not a successful update.", inet_ntop(AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) );
UpdateLeaseTable( request, self, lease );
if ( lease > 0 )
{
leaseReply = FormatLeaseReply( self, reply, lease );
if ( !leaseReply )
{
Log("HandleRequest - unable to format lease reply");
}
reply = leaseReply;
}
if ( send( self->LLQEventNotifySock, pingmsg, sizeof( pingmsg ), 0 ) != sizeof( pingmsg ) )
{
LogErr("HandleRequest", "send");
}
}
exit:
if ( sock )
{
mDNSPlatformTCPCloseConnection( sock );
}
if ( reply == &buf )
{
reply = malloc( sizeof( *reply ) );
if ( reply )
{
reply->len = buf.len;
memcpy(&reply->msg, &buf.msg, buf.len);
}
else
{
LogErr("HandleRequest", "malloc");
}
}
return reply;
}
mDNSlocal void FormatLLQOpt(AuthRecord *opt, int opcode, const mDNSOpaque64 *const id, mDNSs32 lease)
{
mDNSPlatformMemZero(opt, sizeof(*opt));
mDNS_SetupResourceRecord(opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
opt->resrec.rrclass = NormalMaxDNSMessageData;
opt->resrec.rdlength = sizeof(rdataOPT); opt->resrec.rdestimate = sizeof(rdataOPT);
opt->resrec.rdata->u.opt[0].opt = kDNSOpt_LLQ;
opt->resrec.rdata->u.opt[0].u.llq.vers = kLLQ_Vers;
opt->resrec.rdata->u.opt[0].u.llq.llqOp = opcode;
opt->resrec.rdata->u.opt[0].u.llq.err = LLQErr_NoError;
opt->resrec.rdata->u.opt[0].u.llq.id = *id;
opt->resrec.rdata->u.opt[0].u.llq.llqlease = lease;
}
mDNSlocal mDNSu32 LLQLease(LLQEntry *e)
{
struct timeval t;
gettimeofday(&t, NULL);
if (e->expire < t.tv_sec) return 0;
else return e->expire - t.tv_sec;
}
mDNSlocal void DeleteLLQ(DaemonInfo *d, LLQEntry *e)
{
int bucket = DomainNameHashValue(&e->qname) % LLQ_TABLESIZE;
LLQEntry **ptr = &d->LLQTable[bucket];
AnswerListElem *a = e->AnswerList;
char addr[32];
inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
VLog("Deleting LLQ table entry for %##s client %s", e->qname.c, addr);
if (a && !(--a->refcount) && d->AnswerTableCount >= LLQ_TABLESIZE)
{
CacheRecord *cr = a->KnownAnswers, *tmp;
AnswerListElem **tbl = &d->AnswerTable[bucket];
while (cr)
{
tmp = cr;
cr = cr->next;
free(tmp);
}
while (*tbl && *tbl != a) tbl = &(*tbl)->next;
if (*tbl) { *tbl = (*tbl)->next; free(a); d->AnswerTableCount--; }
else Log("Error: DeleteLLQ - AnswerList not found in table");
}
while(*ptr && *ptr != e) ptr = &(*ptr)->next;
if (!*ptr) { Log("Error: DeleteLLQ - LLQ not in table"); return; }
*ptr = (*ptr)->next;
free(e);
}
mDNSlocal int SendLLQ(DaemonInfo *d, PktMsg *pkt, struct sockaddr_in dst, TCPSocket *sock)
{
char addr[32];
int err = -1;
HdrHToN(pkt);
if ( sock )
{
if ( SendPacket( sock, pkt ) != 0 )
{
LogErr("DaemonInfo", "MySend");
Log("Could not send response to client %s", inet_ntop(AF_INET, &dst.sin_addr, addr, 32));
}
}
else
{
if (sendto(d->llq_udpsd, &pkt->msg, pkt->len, 0, (struct sockaddr *)&dst, sizeof(dst)) != (int)pkt->len)
{
LogErr("DaemonInfo", "sendto");
Log("Could not send response to client %s", inet_ntop(AF_INET, &dst.sin_addr, addr, 32));
}
}
err = 0;
HdrNToH(pkt);
return err;
}
mDNSlocal CacheRecord *AnswerQuestion(DaemonInfo *d, AnswerListElem *e)
{
PktMsg q;
int i;
TCPSocket *sock = NULL;
const mDNSu8 *ansptr;
mDNSu8 *end = q.msg.data;
PktMsg buf, *reply = NULL;
LargeCacheRecord lcr;
CacheRecord *AnswerList = NULL;
mDNSu8 rcode;
VLog("Querying server for %##s type %d", e->name.c, e->type);
InitializeDNSMessage(&q.msg.h, zeroID, uQueryFlags);
end = putQuestion(&q.msg, end, end + AbsoluteMaxDNSMessageData, &e->name, e->type, kDNSClass_IN);
if (!end) { Log("Error: AnswerQuestion - putQuestion returned NULL"); goto end; }
q.len = (int)(end - (mDNSu8 *)&q.msg);
HdrHToN(&q);
if (!e->UseTCP)
{
mDNSBool trunc;
if (UDPServerTransaction(d, &q, &buf, &trunc) < 0)
Log("AnswerQuestion %##s - UDPServerTransaction failed. Trying TCP", e->name.c);
else if (trunc)
{ VLog("AnswerQuestion %##s - answer truncated. Using TCP", e->name.c); e->UseTCP = mDNStrue; }
else reply = &buf; }
if (!reply)
{
mDNSBool closed;
sock = ConnectToServer(d);
if (!sock) { Log("AnswerQuestion: ConnectToServer failed"); goto end; }
if (SendPacket( sock, &q)) { Log("AnswerQuestion: SendPacket failed"); mDNSPlatformTCPCloseConnection( sock ); goto end; }
reply = RecvPacket( sock, NULL, &closed );
mDNSPlatformTCPCloseConnection( sock );
require_action( reply, end, Log( "AnswerQuestion: RecvPacket returned NULL" ) );
}
HdrNToH(&q);
if (reply) HdrNToH(reply);
if ((reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery))
{ Log("AnswerQuestion: %##s type %d - Invalid response flags from server"); goto end; }
rcode = (mDNSu8)(reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask);
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; }
end = (mDNSu8 *)&reply->msg + reply->len;
ansptr = LocateAnswers(&reply->msg, end);
if (!ansptr) { Log("Error: AnswerQuestion - LocateAnswers returned NULL"); goto end; }
for (i = 0; i < reply->msg.h.numAnswers; i++)
{
ansptr = GetLargeResourceRecord(NULL, &reply->msg, ansptr, end, 0, kDNSRecordTypePacketAns, &lcr);
if (!ansptr) { Log("AnswerQuestions: GetLargeResourceRecord returned NULL"); goto end; }
if (lcr.r.resrec.RecordType != kDNSRecordTypePacketNegative)
{
if (lcr.r.resrec.rrtype != e->type || lcr.r.resrec.rrclass != kDNSClass_IN || !SameDomainName(lcr.r.resrec.name, &e->name))
{
Log("AnswerQuestion: response %##s type #d does not answer question %##s type #d. Discarding",
lcr.r.resrec.name->c, lcr.r.resrec.rrtype, e->name.c, e->type);
}
else
{
CacheRecord *cr = CopyCacheRecord(&lcr.r, &e->name);
if (!cr) { Log("Error: AnswerQuestion - CopyCacheRecord returned NULL"); goto end; }
cr->next = AnswerList;
AnswerList = cr;
}
}
}
end:
if (reply && reply != &buf) free(reply);
return AnswerList;
}
mDNSlocal void *UpdateAnswerList(void *args)
{
CacheRecord *cr, *NewAnswers, **na, **ka; DaemonInfo *d = ((UpdateAnswerListArgs *)args)->d;
AnswerListElem *a = ((UpdateAnswerListArgs *)args)->a;
free(args);
args = NULL;
NewAnswers = AnswerQuestion(d, a);
for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next)
(*ka)->resrec.rroriginalttl = (unsigned)-1;
for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next)
{
for (na = &NewAnswers; *na; na = &(*na)->next)
{
if (IdenticalResourceRecord(&(*ka)->resrec, &(*na)->resrec))
{ (*ka)->resrec.rroriginalttl = 0; break; } }
}
na = &NewAnswers;
while (*na)
{
for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next)
if (IdenticalResourceRecord(&(*ka)->resrec, &(*na)->resrec)) break;
if (!*ka)
{
cr = *na;
*na = (*na)->next; cr->next = a->EventList; a->EventList = cr;
cr->resrec.rroriginalttl = 1; }
else na = &(*na)->next;
}
ka = &a->KnownAnswers;
while (*ka)
{
if ((*ka)->resrec.rroriginalttl == (unsigned)-1)
{
cr = *ka;
*ka = (*ka)->next;
cr->next = a->EventList;
a->EventList = cr;
}
else ka = &(*ka)->next;
}
while (NewAnswers)
{
cr = NewAnswers;
NewAnswers = NewAnswers->next;
free(cr);
}
return NULL;
}
mDNSlocal void SendEvents(DaemonInfo *d, LLQEntry *e)
{
PktMsg response;
CacheRecord *cr;
mDNSu8 *end = (mDNSu8 *)&response.msg.data;
mDNSOpaque16 msgID;
char rrbuf[MaxMsg], addrbuf[32];
AuthRecord opt;
msgID.NotAnInteger = random();
if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32);
InitializeDNSMessage(&response.msg.h, msgID, ResponseFlags);
end = putQuestion(&response.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
if (!end) { Log("Error: SendEvents - putQuestion returned NULL"); return; }
for (cr = e->AnswerList->EventList; cr; cr = cr->next)
{
if (verbose) GetRRDisplayString_rdb(&cr->resrec, &cr->resrec.rdata->u, rrbuf);
VLog("%s (%s): %s", addrbuf, (mDNSs32)cr->resrec.rroriginalttl < 0 ? "Remove": "Add", rrbuf);
end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAnswers, &cr->resrec, cr->resrec.rroriginalttl);
if (!end) { Log("Error: SendEvents - PutResourceRecordTTLJumbo returned NULL"); return; }
}
FormatLLQOpt(&opt, kLLQOp_Event, &e->id, LLQLease(e));
end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAdditionals, &opt.resrec, 0);
if (!end) { Log("Error: SendEvents - PutResourceRecordTTLJumbo"); return; }
response.len = (int)(end - (mDNSu8 *)&response.msg);
if (SendLLQ(d, &response, e->cli, NULL ) < 0) LogMsg("Error: SendEvents - SendLLQ");
}
mDNSlocal void PrintLLQAnswers(DaemonInfo *d)
{
int i;
char rrbuf[MaxMsg];
Log("Printing LLQ Answer Table contents");
for (i = 0; i < LLQ_TABLESIZE; i++)
{
AnswerListElem *a = d->AnswerTable[i];
while(a)
{
int ancount = 0;
const CacheRecord *rr = a->KnownAnswers;
while (rr) { ancount++; rr = rr->next; }
Log("%p : Question %##s; type %d; referenced by %d LLQs; %d answers:", a, a->name.c, a->type, a->refcount, ancount);
for (rr = a->KnownAnswers; rr; rr = rr->next) Log("\t%s", GetRRDisplayString_rdb(&rr->resrec, &rr->resrec.rdata->u, rrbuf));
a = a->next;
}
}
}
mDNSlocal void PrintLLQTable(DaemonInfo *d)
{
LLQEntry *e;
char addr[32];
int i;
Log("Printing LLQ table contents");
for (i = 0; i < LLQ_TABLESIZE; i++)
{
e = d->LLQTable[i];
while(e)
{
char *state;
switch (e->state)
{
case RequestReceived: state = "RequestReceived"; break;
case ChallengeSent: state = "ChallengeSent"; break;
case Established: state = "Established"; break;
default: state = "unknown";
}
inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
Log("LLQ from %s in state %s; %##s; type %d; orig lease %d; remaining lease %d; AnswerList %p)",
addr, state, e->qname.c, e->qtype, e->lease, LLQLease(e), e->AnswerList);
e = e->next;
}
}
}
mDNSlocal void GenLLQEvents(DaemonInfo *d)
{
LLQEntry **e;
int i;
struct timeval t;
UpdateAnswerListArgs *args;
VLog("Generating LLQ Events");
gettimeofday(&t, NULL);
for (i = 0; i < LLQ_TABLESIZE; i++)
{
AnswerListElem *a = d->AnswerTable[i];
while(a)
{
args = malloc(sizeof(*args));
if (!args) { LogErr("GenLLQEvents", "malloc"); return; }
args->d = d;
args->a = a;
if (pthread_create(&a->tid, NULL, UpdateAnswerList, args) < 0) { LogErr("GenLLQEvents", "pthread_create"); return; }
usleep(1);
a = a->next;
}
}
for (i = 0; i < LLQ_TABLESIZE; i++)
{
AnswerListElem *a = d->AnswerTable[i];
while(a)
{
if (pthread_join(a->tid, NULL)) LogErr("GenLLQEvents", "pthread_join");
a = a->next;
}
}
for (i = 0; i < LLQ_TABLESIZE; i++)
{
e = &d->LLQTable[i];
while(*e)
{
if ((*e)->expire < t.tv_sec) DeleteLLQ(d, *e);
else
{
if ((*e)->state == Established && (*e)->AnswerList->EventList) SendEvents(d, *e);
e = &(*e)->next;
}
}
}
for (i = 0; i < LLQ_TABLESIZE; i++)
{
AnswerListElem *a = d->AnswerTable[i];
while(a)
{
if (a->EventList)
{
CacheRecord *cr = a->EventList, *tmp;
while (cr)
{
tmp = cr;
cr = cr->next;
if ((signed)tmp->resrec.rroriginalttl < 0) free(tmp);
else
{
tmp->next = a->KnownAnswers;
a->KnownAnswers = tmp;
tmp->resrec.rroriginalttl = 0;
}
}
a->EventList = NULL;
}
a = a->next;
}
}
}
mDNSlocal void SetAnswerList(DaemonInfo *d, LLQEntry *e)
{
int bucket = DomainNameHashValue(&e->qname) % LLQ_TABLESIZE;
AnswerListElem *a = d->AnswerTable[bucket];
while (a && (a->type != e->qtype ||!SameDomainName(&a->name, &e->qname))) a = a->next;
if (!a)
{
a = malloc(sizeof(*a));
if (!a) { LogErr("SetAnswerList", "malloc"); return; }
AssignDomainName(&a->name, &e->qname);
a->type = e->qtype;
a->refcount = 0;
a->EventList = NULL;
a->UseTCP = mDNSfalse;
a->next = d->AnswerTable[bucket];
d->AnswerTable[bucket] = a;
d->AnswerTableCount++;
a->KnownAnswers = AnswerQuestion(d, a);
}
e->AnswerList = a;
a->refcount ++;
}
mDNSlocal LLQEntry *NewLLQ(DaemonInfo *d, struct sockaddr_in cli, domainname *qname, mDNSu16 qtype, mDNSu32 lease )
{
char addr[32];
struct timeval t;
int bucket = DomainNameHashValue(qname) % LLQ_TABLESIZE;
LLQEntry *e;
e = malloc(sizeof(*e));
if (!e) { LogErr("NewLLQ", "malloc"); return NULL; }
inet_ntop(AF_INET, &cli.sin_addr, addr, 32);
VLog("Allocating LLQ entry for client %s question %##s type %d", addr, qname->c, qtype);
e->cli = cli;
AssignDomainName(&e->qname, qname);
e->qtype = qtype;
e->id = zeroOpaque64;
e->state = RequestReceived;
e->AnswerList = NULL;
if (lease < LLQ_MIN_LEASE) lease = LLQ_MIN_LEASE;
else if (lease > LLQ_MAX_LEASE) lease = LLQ_MAX_LEASE;
gettimeofday(&t, NULL);
e->expire = t.tv_sec + (int)lease;
e->lease = lease;
e->next = d->LLQTable[bucket];
d->LLQTable[bucket] = e;
return e;
}
mDNSlocal void LLQRefresh(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID, TCPSocket *sock )
{
AuthRecord opt;
PktMsg ack;
mDNSu8 *end = (mDNSu8 *)&ack.msg.data;
char addr[32];
inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
VLog("%s LLQ for %##s from %s", llq->llqlease ? "Refreshing" : "Deleting", e->qname.c, addr);
if (llq->llqlease)
{
struct timeval t;
if (llq->llqlease < LLQ_MIN_LEASE) llq->llqlease = LLQ_MIN_LEASE;
else if (llq->llqlease > LLQ_MAX_LEASE) llq->llqlease = LLQ_MIN_LEASE;
gettimeofday(&t, NULL);
e->expire = t.tv_sec + llq->llqlease;
}
ack.src.sin_addr.s_addr = 0; InitializeDNSMessage(&ack.msg.h, msgID, ResponseFlags);
end = putQuestion(&ack.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
if (!end) { Log("Error: putQuestion"); return; }
FormatLLQOpt(&opt, kLLQOp_Refresh, &e->id, llq->llqlease ? LLQLease(e) : 0);
end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAdditionals, &opt.resrec, 0);
if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
ack.len = (int)(end - (mDNSu8 *)&ack.msg);
if (SendLLQ(d, &ack, e->cli, sock)) Log("Error: LLQRefresh");
if (llq->llqlease) e->state = Established;
else DeleteLLQ(d, e);
}
mDNSlocal void LLQCompleteHandshake(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID, TCPSocket *sock)
{
char addr[32];
CacheRecord *ptr;
AuthRecord opt;
PktMsg ack;
mDNSu8 *end = (mDNSu8 *)&ack.msg.data;
char rrbuf[MaxMsg], addrbuf[32];
inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
if (!mDNSSameOpaque64(&llq->id, &e->id) ||
llq->vers != kLLQ_Vers ||
llq->llqOp != kLLQOp_Setup ||
llq->err != LLQErr_NoError ||
llq->llqlease > e->lease + LLQ_LEASE_FUDGE ||
llq->llqlease < e->lease - LLQ_LEASE_FUDGE)
{
Log("Incorrect challenge response from %s", addr);
return;
}
if (e->state == Established) VLog("Retransmitting LLQ ack + answers for %##s", e->qname.c);
else VLog("Delivering LLQ ack + answers for %##s", e->qname.c);
ack.src.sin_addr.s_addr = 0; InitializeDNSMessage(&ack.msg.h, msgID, ResponseFlags);
end = putQuestion(&ack.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
if (!end) { Log("Error: putQuestion"); return; }
if (e->state != Established) { SetAnswerList(d, e); e->state = Established; }
if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32);
for (ptr = e->AnswerList->KnownAnswers; ptr; ptr = ptr->next)
{
if (verbose) GetRRDisplayString_rdb(&ptr->resrec, &ptr->resrec.rdata->u, rrbuf);
VLog("%s Intitial Answer - %s", addr, rrbuf);
end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAnswers, &ptr->resrec, 1);
if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
}
FormatLLQOpt(&opt, kLLQOp_Setup, &e->id, LLQLease(e));
end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAdditionals, &opt.resrec, 0);
if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
ack.len = (int)(end - (mDNSu8 *)&ack.msg);
if (SendLLQ(d, &ack, e->cli, sock)) Log("Error: LLQCompleteHandshake");
}
mDNSlocal void LLQSetupChallenge(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID)
{
struct timeval t;
PktMsg challenge;
mDNSu8 *end = challenge.msg.data;
AuthRecord opt;
if (e->state == ChallengeSent) VLog("Retransmitting LLQ setup challenge for %##s", e->qname.c);
else VLog("Sending LLQ setup challenge for %##s", e->qname.c);
if (!mDNSOpaque64IsZero(&llq->id)) { Log("Error: LLQSetupChallenge - nonzero ID"); return; } if (llq->llqOp != kLLQOp_Setup) { Log("LLQSetupChallenge - incorrrect operation from client"); return; }
if (mDNSOpaque64IsZero(&e->id)) {
gettimeofday(&t, NULL);
e->id.l[0] = t.tv_sec;
e->id.l[1] = random();
}
challenge.src.sin_addr.s_addr = 0; InitializeDNSMessage(&challenge.msg.h, msgID, ResponseFlags);
end = putQuestion(&challenge.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
if (!end) { Log("Error: putQuestion"); return; }
FormatLLQOpt(&opt, kLLQOp_Setup, &e->id, LLQLease(e));
end = PutResourceRecordTTLJumbo(&challenge.msg, end, &challenge.msg.h.numAdditionals, &opt.resrec, 0);
if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
challenge.len = (int)(end - (mDNSu8 *)&challenge.msg);
if (SendLLQ(d, &challenge, e->cli, NULL)) { Log("Error: LLQSetupChallenge"); return; }
e->state = ChallengeSent;
}
mDNSlocal void UpdateLLQ(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID, TCPSocket *sock )
{
switch(e->state)
{
case RequestReceived:
if ( sock )
{
struct timeval t;
gettimeofday(&t, NULL);
e->id.l[0] = t.tv_sec; e->id.l[1] = random();
llq->id = e->id;
LLQCompleteHandshake( d, e, llq, msgID, sock );
e->state = Established;
}
else
{
LLQSetupChallenge(d, e, llq, msgID);
}
return;
case ChallengeSent:
if (mDNSOpaque64IsZero(&llq->id)) LLQSetupChallenge(d, e, llq, msgID); else LLQCompleteHandshake(d, e, llq, msgID, sock );
return;
case Established:
if (mDNSOpaque64IsZero(&llq->id))
{
LLQEntry *newe = NewLLQ(d, e->cli, &e->qname, e->qtype, llq->llqlease );
if (!newe) return;
DeleteLLQ(d, e);
LLQSetupChallenge(d, newe, llq, msgID);
return;
}
else if (llq->llqOp == kLLQOp_Setup)
{ LLQCompleteHandshake(d, e, llq, msgID, sock); return; } else if (llq->llqOp == kLLQOp_Refresh)
{ LLQRefresh(d, e, llq, msgID, sock); return; }
else { Log("Unhandled message for established LLQ"); return; }
}
}
mDNSlocal LLQEntry *LookupLLQ(DaemonInfo *d, struct sockaddr_in cli, domainname *qname, mDNSu16 qtype, const mDNSOpaque64 *const id)
{
int bucket = bucket = DomainNameHashValue(qname) % LLQ_TABLESIZE;
LLQEntry *ptr = d->LLQTable[bucket];
while(ptr)
{
if (((ptr->state == ChallengeSent && mDNSOpaque64IsZero(id) && (cli.sin_port == ptr->cli.sin_port)) || mDNSSameOpaque64(id, &ptr->id)) && (cli.sin_addr.s_addr == ptr->cli.sin_addr.s_addr) && (qtype == ptr->qtype) && SameDomainName(&ptr->qname, qname)) return ptr;
ptr = ptr->next;
}
return NULL;
}
mDNSlocal int
RecvNotify
(
DaemonInfo * d,
PktMsg * pkt
)
{
int res;
int err = 0;
pkt->msg.h.flags.b[0] |= kDNSFlag0_QR_Response;
res = sendto( d->udpsd, &pkt->msg, pkt->len, 0, ( struct sockaddr* ) &pkt->src, sizeof( pkt->src ) );
require_action( res == ( int ) pkt->len, exit, err = mStatus_UnknownErr; LogErr( "RecvNotify", "sendto" ) );
exit:
return err;
}
mDNSlocal int RecvLLQ( DaemonInfo *d, PktMsg *pkt, TCPSocket *sock )
{
DNSQuestion q;
LargeCacheRecord opt;
int i, err = -1;
char addr[32];
const mDNSu8 *qptr = pkt->msg.data;
const mDNSu8 *end = (mDNSu8 *)&pkt->msg + pkt->len;
const mDNSu8 *aptr;
LLQOptData *llq = NULL;
LLQEntry *e = NULL;
HdrNToH(pkt);
aptr = LocateAdditionals(&pkt->msg, end); inet_ntop(AF_INET, &pkt->src.sin_addr, addr, 32);
VLog("Received LLQ msg from %s", addr);
if (!pkt->msg.h.numQuestions || !pkt->msg.h.numAdditionals)
{
Log("Malformatted LLQ from %s with %d questions, %d additionals", addr, pkt->msg.h.numQuestions, pkt->msg.h.numAdditionals);
goto end;
}
for (i = 0; i < pkt->msg.h.numAdditionals; i++)
{
aptr = GetLargeResourceRecord(NULL, &pkt->msg, aptr, end, 0, kDNSRecordTypePacketAdd, &opt);
if (!aptr) { Log("Malformatted LLQ from %s: could not get Additional record %d", addr, i); goto end; }
if (opt.r.resrec.RecordType != kDNSRecordTypePacketNegative && opt.r.resrec.rrtype == kDNSType_OPT) break;
}
if (opt.r.resrec.rrtype != kDNSType_OPT) { Log("Malformatted LLQ from %s: last Additional not an OPT RR", addr); goto end; }
if (opt.r.resrec.rdlength < pkt->msg.h.numQuestions * DNSOpt_LLQData_Space) { Log("Malformatted LLQ from %s: OPT RR to small (%d bytes for %d questions)", addr, opt.r.resrec.rdlength, pkt->msg.h.numQuestions); }
for (i = 0; i < pkt->msg.h.numQuestions; i++)
{
qptr = getQuestion(&pkt->msg, qptr, end, 0, &q);
if (!qptr) { Log("Malformatted LLQ from %s: cannot read question %d", addr, i); goto end; }
llq = (LLQOptData *)&opt.r.resrec.rdata->u.opt[0].u.llq + i; if (llq->vers != kLLQ_Vers) { Log("LLQ from %s contains bad version %d (expected %d)", addr, llq->vers, kLLQ_Vers); goto end; }
e = LookupLLQ(d, pkt->src, &q.qname, q.qtype, &llq->id);
if (!e)
{
e = NewLLQ(d, pkt->src, &q.qname, q.qtype, llq->llqlease );
if (!e) goto end;
}
UpdateLLQ(d, e, llq, pkt->msg.h.id, sock);
}
err = 0;
end:
HdrHToN(pkt);
return err;
}
mDNSlocal mDNSBool IsAuthorized( DaemonInfo * d, PktMsg * pkt, DomainAuthInfo ** key, mDNSu16 * rcode, mDNSu16 * tcode )
{
const mDNSu8 * lastPtr = NULL;
const mDNSu8 * ptr = NULL;
DomainAuthInfo * keys;
mDNSu8 * end = ( mDNSu8* ) &pkt->msg + pkt->len;
LargeCacheRecord lcr;
mDNSBool hasTSIG = mDNSfalse;
mDNSBool strip = mDNSfalse;
mDNSBool ok = mDNSfalse;
int i;
( void ) d;
HdrNToH(pkt);
*key = NULL;
if ( pkt->msg.h.numAdditionals )
{
ptr = LocateAdditionals(&pkt->msg, end);
if (ptr)
{
for (i = 0; i < pkt->msg.h.numAdditionals; i++)
{
lastPtr = ptr;
ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr);
if (!ptr)
{
Log("Unable to read additional record");
lastPtr = NULL;
break;
}
}
hasTSIG = ( ptr && lcr.r.resrec.RecordType != kDNSRecordTypePacketNegative && lcr.r.resrec.rrtype == kDNSType_TSIG );
}
else
{
LogMsg( "IsAuthorized: unable to find Additional section" );
}
}
if ( !pkt->zone )
{
ok = mDNStrue;
strip = mDNSfalse;
goto exit;
}
if ( IsQuery( pkt ) )
{
keys = pkt->zone->queryKeys;
strip = mDNStrue;
}
else if ( IsUpdate( pkt ) )
{
keys = pkt->zone->updateKeys;
strip = mDNSfalse;
}
else
{
ok = mDNStrue;
strip = mDNSfalse;
goto exit;
}
if ( pkt->isZonePublic )
{
ok = mDNStrue;
goto exit;
}
if ( ( hasTSIG && !keys ) || ( !hasTSIG && keys ) )
{
Log( "Invalid TSIG spec %##s for zone %##s", lcr.r.resrec.name->c, pkt->zone->name.c );
*rcode = kDNSFlag1_RC_NotAuth;
*tcode = TSIG_ErrBadKey;
strip = mDNStrue;
ok = mDNSfalse;
goto exit;
}
for ( *key = keys; *key; *key = (*key)->next )
{
if ( SameDomainName( lcr.r.resrec.name, &(*key)->keyname ) )
{
break;
}
}
if ( !(*key) )
{
Log( "Invalid TSIG name %##s for zone %##s", lcr.r.resrec.name->c, pkt->zone->name.c );
*rcode = kDNSFlag1_RC_NotAuth;
*tcode = TSIG_ErrBadKey;
strip = mDNStrue;
ok = mDNSfalse;
goto exit;
}
pkt->msg.h.numAdditionals--;
HdrHToN( pkt );
ok = DNSDigest_VerifyMessage( &pkt->msg, ( mDNSu8* ) lastPtr, &lcr, (*key), rcode, tcode );
HdrNToH( pkt );
pkt->msg.h.numAdditionals++;
exit:
if ( hasTSIG && strip )
{
pkt->msg.h.numAdditionals--;
pkt->len = lastPtr - ( mDNSu8* ) ( &pkt->msg );
}
HdrHToN(pkt);
return ok;
}
mDNSlocal void*
UDPMessageHandler
(
void * vptr
)
{
UDPContext * context = ( UDPContext* ) vptr;
PktMsg * reply = NULL;
int res;
mStatus err;
reply = HandleRequest( context->d, &context->pkt );
require_action( reply, exit, err = mStatus_UnknownErr );
res = sendto( context->sd, &reply->msg, reply->len, 0, ( struct sockaddr* ) &context->pkt.src, sizeof( context->pkt.src ) );
require_action_quiet( res == ( int ) reply->len, exit, LogErr( "UDPMessageHandler", "sendto" ) );
exit:
if ( reply )
{
free( reply );
}
free( context );
pthread_exit( NULL );
return NULL;
}
mDNSlocal int
RecvUDPMessage
(
DaemonInfo * self,
int sd
)
{
UDPContext * context = NULL;
pthread_t tid;
mDNSu16 rcode;
mDNSu16 tcode;
DomainAuthInfo * key;
unsigned int clisize = sizeof( context->cliaddr );
int res;
mStatus err = mStatus_NoError;
context = malloc( sizeof( UDPContext ) );
require_action( context, exit, err = mStatus_NoMemoryErr ; LogErr( "RecvUDPMessage", "malloc" ) );
mDNSPlatformMemZero( context, sizeof( *context ) );
context->d = self;
context->sd = sd;
res = recvfrom(sd, &context->pkt.msg, sizeof(context->pkt.msg), 0, (struct sockaddr *)&context->cliaddr, &clisize);
require_action( res >= 0, exit, err = mStatus_UnknownErr ; LogErr( "RecvUDPMessage", "recvfrom" ) );
context->pkt.len = res;
require_action( clisize == sizeof( context->cliaddr ), exit, err = mStatus_UnknownErr ; Log( "Client address of unknown size %d", clisize ) );
context->pkt.src = context->cliaddr;
SetZone( context->d, &context->pkt );
if ( IsNotify( &context->pkt ) )
{
int e = RecvNotify( self, &context->pkt );
free(context);
return e;
}
else if ( IsAuthorized( context->d, &context->pkt, &key, &rcode, &tcode ) )
{
if ( IsLLQRequest( &context->pkt ) )
{
int e = RecvLLQ( self, &context->pkt, NULL );
free(context);
return e;
}
if ( IsLLQAck(&context->pkt ) )
{
free(context);
return 0;
}
err = pthread_create( &tid, NULL, UDPMessageHandler, context );
require_action( !err, exit, LogErr( "RecvUDPMessage", "pthread_create" ) );
pthread_detach(tid);
}
else
{
PktMsg reply;
int e;
memcpy( &reply, &context->pkt, sizeof( PktMsg ) );
reply.msg.h.flags.b[0] = kDNSFlag0_QR_Response | kDNSFlag0_AA | kDNSFlag0_RD;
reply.msg.h.flags.b[1] = kDNSFlag1_RA | kDNSFlag1_RC_NXDomain;
e = sendto( sd, &reply.msg, reply.len, 0, ( struct sockaddr* ) &context->pkt.src, sizeof( context->pkt.src ) );
require_action_quiet( e == ( int ) reply.len, exit, LogErr( "RecvUDPMessage", "sendto" ) );
err = mStatus_NoAuth;
}
exit:
if ( err && context )
{
free( context );
}
return err;
}
mDNSlocal void
FreeTCPContext
(
TCPContext * context
)
{
if ( context )
{
if ( context->sock )
{
mDNSPlatformTCPCloseConnection( context->sock );
}
free( context );
}
}
mDNSlocal void*
TCPMessageHandler
(
void * vptr
)
{
TCPContext * context = ( TCPContext* ) vptr;
PktMsg * reply = NULL;
int res;
char buf[32];
reply = HandleRequest( context->d, &context->pkt );
require_action_quiet( reply, exit, LogMsg( "TCPMessageHandler: No reply for client %s", inet_ntop( AF_INET, &context->cliaddr.sin_addr, buf, 32 ) ) );
res = SendPacket( context->sock, reply );
require_action( res >= 0, exit, LogMsg("TCPMessageHandler: Unable to send reply to client %s", inet_ntop(AF_INET, &context->cliaddr.sin_addr, buf, 32 ) ) );
exit:
FreeTCPContext( context );
if ( reply )
{
free( reply );
}
pthread_exit(NULL);
}
mDNSlocal void
RecvTCPMessage
(
void * param
)
{
TCPContext * context = ( TCPContext* ) param;
mDNSu16 rcode;
mDNSu16 tcode;
pthread_t tid;
DomainAuthInfo * key;
PktMsg * pkt;
mDNSBool closed;
mDNSBool freeContext = mDNStrue;
mStatus err = mStatus_NoError;
pkt = RecvPacket( context->sock, &context->pkt, &closed );
if (pkt) HdrNToH(pkt);
require_action( pkt || !closed, exit, err = mStatus_UnknownErr; LogMsg( "client disconnected" ) );
if ( pkt )
{
RemoveSourceFromEventLoop( context->d, context->sock );
SetZone( context->d, &context->pkt );
if ( IsAuthorized( context->d, &context->pkt, &key, &rcode, &tcode ) )
{
if ( IsLLQRequest( &context->pkt ) )
{
RecvLLQ( context->d, &context->pkt, context->sock);
}
else
{
err = pthread_create( &tid, NULL, TCPMessageHandler, context );
if ( err )
{
LogErr( "RecvTCPMessage", "pthread_create" );
err = mStatus_NoError;
goto exit;
}
freeContext = mDNSfalse;
pthread_detach(tid);
}
}
else
{
PktMsg reply;
LogMsg( "Client %s Not authorized for zone %##s", inet_ntoa( context->pkt.src.sin_addr ), pkt->zone->name.c );
memcpy( &reply, &context->pkt, sizeof( PktMsg ) );
reply.msg.h.flags.b[0] = kDNSFlag0_QR_Response | kDNSFlag0_AA | kDNSFlag0_RD;
reply.msg.h.flags.b[1] = kDNSFlag1_RA | kDNSFlag1_RC_Refused;
SendPacket( context->sock, &reply );
}
}
else
{
freeContext = mDNSfalse;
}
exit:
if ( err )
{
RemoveSourceFromEventLoop( context->d, context->sock );
}
if ( freeContext )
{
FreeTCPContext( context );
}
}
mDNSlocal int
AcceptTCPConnection
(
DaemonInfo * self,
int sd,
TCPSocketFlags flags
)
{
TCPContext * context = NULL;
unsigned int clilen = sizeof( context->cliaddr);
int newSock;
mStatus err = mStatus_NoError;
context = ( TCPContext* ) malloc( sizeof( TCPContext ) );
require_action( context, exit, err = mStatus_NoMemoryErr; LogErr( "AcceptTCPConnection", "malloc" ) );
mDNSPlatformMemZero( context, sizeof( sizeof( TCPContext ) ) );
context->d = self;
newSock = accept( sd, ( struct sockaddr* ) &context->cliaddr, &clilen );
require_action( newSock != -1, exit, err = mStatus_UnknownErr; LogErr( "AcceptTCPConnection", "accept" ) );
context->sock = mDNSPlatformTCPAccept( flags, newSock );
require_action( context->sock, exit, err = mStatus_UnknownErr; LogErr( "AcceptTCPConnection", "mDNSPlatformTCPAccept" ) );
err = AddSourceToEventLoop( self, context->sock, RecvTCPMessage, context );
require_action( !err, exit, LogErr( "AcceptTCPConnection", "AddSourceToEventLoop" ) );
exit:
if ( err && context )
{
free( context );
context = NULL;
}
return err;
}
mDNSlocal int Run(DaemonInfo *d)
{
int staticMaxFD, nfds;
fd_set rset;
struct timeval timenow, timeout, EventTS, tablecheck = { 0, 0 };
mDNSBool EventsPending = mDNSfalse;
VLog("Listening for requests...");
staticMaxFD = 0;
if ( d->tcpsd + 1 > staticMaxFD ) staticMaxFD = d->tcpsd + 1;
if ( d->udpsd + 1 > staticMaxFD ) staticMaxFD = d->udpsd + 1;
if ( d->tlssd + 1 > staticMaxFD ) staticMaxFD = d->tlssd + 1;
if ( d->llq_tcpsd + 1 > staticMaxFD ) staticMaxFD = d->llq_tcpsd + 1;
if ( d->llq_udpsd + 1 > staticMaxFD ) staticMaxFD = d->llq_udpsd + 1;
if ( d->LLQEventListenSock + 1 > staticMaxFD ) staticMaxFD = d->LLQEventListenSock + 1;
while(1)
{
EventSource * source;
int maxFD;
timeout.tv_sec = timeout.tv_usec = 0;
if (gettimeofday(&timenow, NULL)) { LogErr("Run", "gettimeofday"); return -1; }
if (EventsPending)
{
if (timenow.tv_sec - EventTS.tv_sec >= 5) { GenLLQEvents(d); EventsPending = mDNSfalse; } else timeout.tv_usec = 500000; }
if (!EventsPending)
{
if (tablecheck.tv_sec && timenow.tv_sec - tablecheck.tv_sec >= 0)
{ DeleteRecords(d, mDNSfalse); tablecheck.tv_sec = 0; } if (!tablecheck.tv_sec) tablecheck.tv_sec = timenow.tv_sec + EXPIRATION_INTERVAL;
timeout.tv_sec = tablecheck.tv_sec - timenow.tv_sec;
}
FD_ZERO(&rset);
FD_SET( d->tcpsd, &rset );
FD_SET( d->udpsd, &rset );
FD_SET( d->tlssd, &rset );
FD_SET( d->llq_tcpsd, &rset );
FD_SET( d->llq_udpsd, &rset );
FD_SET( d->LLQEventListenSock, &rset );
maxFD = staticMaxFD;
for ( source = ( EventSource* ) d->eventSources.Head; source; source = source->next )
{
FD_SET( source->fd, &rset );
if ( source->fd > maxFD )
{
maxFD = source->fd;
}
}
nfds = select( maxFD + 1, &rset, NULL, NULL, &timeout);
if (nfds < 0)
{
if (errno == EINTR)
{
if (terminate)
{
close( d->tcpsd );
close( d->udpsd );
close( d->tlssd );
close( d->llq_tcpsd );
close( d->llq_udpsd );
d->tcpsd = d->udpsd = d->tlssd = d->llq_tcpsd = d->llq_udpsd = -1;
DeleteRecords(d, mDNStrue);
return 0;
}
else if (dumptable)
{
Log( "Received SIGINFO" );
PrintLeaseTable(d);
PrintLLQTable(d);
PrintLLQAnswers(d);
dumptable = 0;
}
else if (hangup)
{
int err;
Log( "Received SIGHUP" );
err = ParseConfig( d, cfgfile );
if ( err )
{
LogErr( "Run", "ParseConfig" );
return -1;
}
hangup = 0;
}
else
{
Log("Received unhandled signal - continuing");
}
}
else
{
LogErr("Run", "select"); return -1;
}
}
else if (nfds)
{
if (FD_ISSET(d->udpsd, &rset)) RecvUDPMessage( d, d->udpsd );
if (FD_ISSET(d->llq_udpsd, &rset)) RecvUDPMessage( d, d->llq_udpsd );
if (FD_ISSET(d->tcpsd, &rset)) AcceptTCPConnection( d, d->tcpsd, 0 );
if (FD_ISSET(d->llq_tcpsd, &rset)) AcceptTCPConnection( d, d->llq_tcpsd, 0 );
if (FD_ISSET(d->tlssd, &rset)) AcceptTCPConnection( d, d->tlssd, TCP_SOCKET_FLAGS );
if (FD_ISSET(d->LLQEventListenSock, &rset))
{
char buf[256];
recv(d->LLQEventListenSock, buf, 256, 0);
if (!EventsPending)
{
EventsPending = mDNStrue;
if (gettimeofday(&EventTS, NULL)) { LogErr("Run", "gettimeofday"); return -1; }
}
}
for ( source = ( EventSource* ) d->eventSources.Head; source; source = source->next )
{
if ( FD_ISSET( source->fd, &rset ) )
{
source->callback( source->context );
break; }
}
}
else
{
if (EventsPending) { GenLLQEvents(d); EventsPending = mDNSfalse; }
else { DeleteRecords(d, mDNSfalse); tablecheck.tv_sec = 0; }
}
}
return 0;
}
mDNSlocal void HndlSignal(int sig)
{
if (sig == SIGTERM || sig == SIGINT ) { terminate = 1; return; }
if (sig == INFO_SIGNAL) { dumptable = 1; return; }
if (sig == SIGHUP) { hangup = 1; return; }
}
mDNSlocal mStatus
SetPublicSRV
(
DaemonInfo * d,
const char * name
)
{
DNameListElem * elem;
mStatus err = mStatus_NoError;
elem = ( DNameListElem* ) malloc( sizeof( DNameListElem ) );
require_action( elem, exit, err = mStatus_NoMemoryErr );
MakeDomainNameFromDNSNameString( &elem->name, name );
elem->next = d->public_names;
d->public_names = elem;
exit:
return err;
}
int main(int argc, char *argv[])
{
int started_via_launchd = 0;
DaemonInfo *d;
struct rlimit rlim;
Log("dnsextd starting");
d = malloc(sizeof(*d));
if (!d) { LogErr("main", "malloc"); exit(1); }
mDNSPlatformMemZero(d, sizeof(DaemonInfo));
SetPublicSRV(d, "_dns-update._udp.");
SetPublicSRV(d, "_dns-llq._udp.");
SetPublicSRV(d, "_dns-update-tls._tcp.");
SetPublicSRV(d, "_dns-query-tls._tcp.");
SetPublicSRV(d, "_dns-llq-tls._tcp.");
if (signal(SIGHUP, HndlSignal) == SIG_ERR) perror("Can't catch SIGHUP");
if (signal(SIGTERM, HndlSignal) == SIG_ERR) perror("Can't catch SIGTERM");
if (signal(INFO_SIGNAL, HndlSignal) == SIG_ERR) perror("Can't catch SIGINFO");
if (signal(SIGINT, HndlSignal) == SIG_ERR) perror("Can't catch SIGINT");
if (signal(SIGPIPE, SIG_IGN ) == SIG_ERR) perror("Can't ignore SIGPIPE");
rlim.rlim_max = RLIM_INFINITY;
rlim.rlim_cur = RLIM_INFINITY;
if (setrlimit(RLIMIT_NOFILE, &rlim) < 0)
{
LogErr("main", "setrlimit");
Log("Using default file descriptor resource limit");
}
if (argc > 1 && !strcasecmp(argv[1], "-launchd"))
{
Log("started_via_launchd");
started_via_launchd = 1;
argv++;
argc--;
}
if (ProcessArgs(argc, argv, d) < 0) { LogErr("main", "ProcessArgs"); exit(1); }
if (!foreground && !started_via_launchd)
{
if (daemon(0,0))
{
LogErr("main", "daemon");
foreground = 1;
}
}
if (InitLeaseTable(d) < 0) { LogErr("main", "InitLeaseTable"); exit(1); }
if (SetupSockets(d) < 0) { LogErr("main", "SetupSockets"); exit(1); }
if (SetUpdateSRV(d) < 0) { LogErr("main", "SetUpdateSRV"); exit(1); }
Run(d);
Log("dnsextd stopping");
if (ClearUpdateSRV(d) < 0) { LogErr("main", "ClearUpdateSRV"); exit(1); } free(d);
exit(0);
}
void mDNSCoreInitComplete( mDNS * const m, mStatus result) { ( void ) m; ( void ) result; }
void mDNS_ConfigChanged(mDNS *const m) { ( void ) m; }
void mDNSCoreMachineSleep(mDNS * const m, mDNSBool wake) { ( void ) m; ( void ) wake; }
void mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end,
const mDNSAddr *const srcaddr, const mDNSIPPort srcport,
const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID iid)
{ ( void ) m; ( void ) msg; ( void ) end; ( void ) srcaddr; ( void ) srcport; ( void ) dstaddr; ( void ) dstport; ( void ) iid; }
DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout)
{ ( void ) m; ( void ) d; ( void ) interface; ( void ) addr; ( void ) port; ( void ) scoped; ( void ) timeout; return(NULL); }
void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID) { (void)domain; (void) InterfaceID;}
void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext)
{ ( void ) m; ( void ) fqdn; ( void ) StatusCallback; ( void ) StatusContext; }
mDNSs32 mDNS_Execute (mDNS *const m) { ( void ) m; return 0; }
mDNSs32 mDNS_TimeNow(const mDNS *const m) { ( void ) m; return 0; }
mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr) { ( void ) m; ( void ) rr; return 0; }
void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
{ ( void ) m; ( void ) set; ( void ) flapping; }
const char * const mDNS_DomainTypeNames[1] = {};
mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom,
const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context)
{ ( void ) m; ( void ) question; ( void ) DomainType; ( void ) dom; ( void ) InterfaceID; ( void ) Callback; ( void ) Context; return 0; }
mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr) { ( void ) m; ( void ) rr; return 0; }
mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
{ ( void ) m; ( void ) set; ( void ) flapping; return 0; }
void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn) { ( void ) m; ( void ) fqdn; }
void mDNS_SetFQDN(mDNS * const m) { ( void ) m; }
void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router)
{ ( void ) m; ( void ) v4addr; ( void ) v6addr; ( void ) router; }
mStatus uDNS_SetupDNSConfig( mDNS *const m ) { ( void ) m; return 0; }
mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, const char *autoTunnelPrefix)
{ ( void ) m; ( void ) info; ( void ) domain; ( void ) keyname; ( void ) b64keydata; ( void ) hostname; (void) port; ( void ) autoTunnelPrefix; return 0; }
mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) { ( void ) m; ( void ) question; return 0; }
void TriggerEventCompletion(void);
void TriggerEventCompletion() {}
mDNS mDNSStorage;
const char mDNSResponderVersionString_SCCS[] = "@(#) dnsextd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
#if _BUILDING_XCODE_PROJECT_
const char *__crashreporter_info__ = mDNSResponderVersionString_SCCS + 5;
asm(".desc ___crashreporter_info__, 0x10");
#endif