import latest mDNSResponder

This commit is contained in:
christos 2018-01-14 18:43:01 +00:00
parent ee071a7a97
commit d3be8271c6
44 changed files with 47593 additions and 29345 deletions

View File

@ -44,7 +44,7 @@
#include "ClientCommon.h"
const char *GetNextLabel(const char *cstr, char label[64])
{
{
char *ptr = label;
while (*cstr && *cstr != '.') // While we have characters in the label...
{
@ -72,4 +72,4 @@ const char *GetNextLabel(const char *cstr, char label[64])
if (ptr == label) return(NULL); // Illegal empty label
if (*cstr) cstr++; // Skip over the trailing dot (if present)
return(cstr);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,178 @@
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2012 Apple Inc. All rights reserved.
*
* dnsctl.c
* Command-line tool using libdns_services.dylib
*
* To build only this tool, copy and paste the following on the command line:
* On Apple 64bit Platforms ONLY OSX/iOS:
* clang -Wall dnsctl.c /usr/lib/libdns_services.dylib -o dnsctl
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <net/if.h> // if_nametoindex()
#include <dispatch/dispatch.h>
#include "dns_services.h"
//*************************************************************************************************************
// Globals:
//*************************************************************************************************************
static const char kFilePathSep = '/';
static DNSXConnRef ClientRef = NULL;
//*************************************************************************************************************
// Utility Funcs:
//*************************************************************************************************************
static void printtimestamp(void)
{
struct tm tm;
int ms;
static char date[16];
static char new_date[16];
struct timeval tv;
gettimeofday(&tv, NULL);
localtime_r((time_t*)&tv.tv_sec, &tm);
ms = tv.tv_usec/1000;
strftime(new_date, sizeof(new_date), "%a %d %b %Y", &tm);
//display date only if it has changed
if (strncmp(date, new_date, sizeof(new_date)))
{
printf("DATE: ---%s---\n", new_date);
strncpy(date, new_date, sizeof(date));
}
printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms);
}
static void print_usage(const char *arg0)
{
fprintf(stderr, "%s USAGE: \n", arg0);
fprintf(stderr, "%s -DP Enable DNS Proxy with Default Parameters \n", arg0);
fprintf(stderr, "%s -DP [-o <output interface>] [-i <input interface(s)>] Enable DNS Proxy \n", arg0);
}
//*************************************************************************************************************
// CallBack Funcs:
//*************************************************************************************************************
// DNSXEnableProxy Callback from the Daemon
static void dnsproxy_reply(DNSXConnRef connRef, DNSXErrorType errCode)
{
(void) connRef;
printtimestamp();
switch (errCode)
{
case kDNSX_NoError : printf(" SUCCESS \n"); break;
case kDNSX_DictError : printf(" DICT ERROR \n"); break;
case kDNSX_DaemonNotRunning : printf(" NO DAEMON \n");
DNSXRefDeAlloc(ClientRef); break;
case kDNSX_Engaged : printf(" ENGAGED \n");
DNSXRefDeAlloc(ClientRef); break;
case kDNSX_UnknownErr :
default : printf("UNKNOWN ERR \n");
DNSXRefDeAlloc(ClientRef); break;
}
fflush(NULL);
}
//*************************************************************************************************************
int main(int argc, char **argv)
{
DNSXErrorType err;
// Default i/p intf is lo0 and o/p intf is primary interface
IfIndex Ipintfs[MaxInputIf] = {1, 0, 0, 0, 0};
IfIndex Opintf = kDNSIfindexAny;
// Extract program name from argv[0], which by convention contains the path to this executable
const char *a0 = strrchr(argv[0], kFilePathSep) + 1;
if (a0 == (const char *)1)
a0 = argv[0];
// Must run as root
if (0 != geteuid())
{
fprintf(stderr, "%s MUST run as root!!\n", a0);
exit(-1);
}
if ((sizeof(argv) == 8))
printf("dnsctl running in 64-bit mode\n");
else if ((sizeof(argv) == 4))
printf("dnsctl running in 32-bit mode\n");
// expects atleast one argument
if (argc < 2)
goto Usage;
if ( !strcmp(argv[1], "-DP") || !strcmp(argv[1], "-dp") )
{
if (argc == 2)
{
printtimestamp();
printf("Proceeding to Enable DNSProxy on mDNSResponder with Default Parameters\n");
dispatch_queue_t my_Q = dispatch_queue_create("com.apple.dnsctl.callback_queue", NULL);
err = DNSXEnableProxy(&ClientRef, kDNSProxyEnable, Ipintfs, Opintf, my_Q, dnsproxy_reply);
}
else if (argc > 2)
{
argc--;
argv++;
if (!strcmp(argv[1], "-o"))
{
Opintf = if_nametoindex(argv[2]);
if (!Opintf)
Opintf = atoi(argv[2]);
if (!Opintf)
{
fprintf(stderr, "Could not parse o/p interface [%s]: Passing default primary \n", argv[2]);
Opintf = kDNSIfindexAny;
}
argc -= 2;
argv += 2;
}
if (argc > 2 && !strcmp(argv[1], "-i"))
{
int i;
argc--;
argv++;
for (i = 0; i < MaxInputIf && argc > 1; i++)
{
Ipintfs[i] = if_nametoindex(argv[1]);
if (!Ipintfs[i])
Ipintfs[i] = atoi(argv[1]);
if (!Ipintfs[i])
{
fprintf(stderr, "Could not parse i/p interface [%s]: Passing default lo0 \n", argv[2]);
Ipintfs[i] = 1;
}
argc--;
argv++;
}
}
printtimestamp();
printf("Proceeding to Enable DNSProxy on mDNSResponder \n");
dispatch_queue_t my_Q = dispatch_queue_create("com.apple.dnsctl.callback_queue", NULL);
err = DNSXEnableProxy(&ClientRef, kDNSProxyEnable, Ipintfs, Opintf, my_Q, dnsproxy_reply);
}
}
else
{
goto Usage;
}
dispatch_main();
Usage:
print_usage(a0);
return 0;
}

View File

@ -0,0 +1,280 @@
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ***************************************************************************
// CryptoAlg.c:
// Interface to DNSSEC cryptographic algorithms. The crypto support itself is
// provided by the platform and the functions in this file just provide an
// interface to access them in a more generic way.
// ***************************************************************************
#include "mDNSEmbeddedAPI.h"
#include "CryptoAlg.h"
AlgFuncs *DigestAlgFuncs[DIGEST_TYPE_MAX];
AlgFuncs *CryptoAlgFuncs[CRYPTO_ALG_MAX];
AlgFuncs *EncAlgFuncs[ENC_ALG_MAX];
mDNSexport mStatus DigestAlgInit(mDNSu8 digestType, AlgFuncs *func)
{
if (digestType >= DIGEST_TYPE_MAX)
{
LogMsg("DigestAlgInit: digestType %d exceeds bounds", digestType);
return mStatus_BadParamErr;
}
// As digestTypes may not be consecutive, check for specific digest types
// that we support
if (digestType != SHA1_DIGEST_TYPE &&
digestType != SHA256_DIGEST_TYPE)
{
LogMsg("DigestAlgInit: digestType %d not supported", digestType);
return mStatus_BadParamErr;
}
DigestAlgFuncs[digestType] = func;
return mStatus_NoError;
}
mDNSexport mStatus CryptoAlgInit(mDNSu8 alg, AlgFuncs *func)
{
if (alg >= CRYPTO_ALG_MAX)
{
LogMsg("CryptoAlgInit: alg %d exceeds bounds", alg);
return mStatus_BadParamErr;
}
// As algs may not be consecutive, check for specific algorithms
// that we support
if (alg != CRYPTO_RSA_SHA1 && alg != CRYPTO_RSA_SHA256 && alg != CRYPTO_RSA_SHA512 &&
alg != CRYPTO_DSA_NSEC3_SHA1 && alg != CRYPTO_RSA_NSEC3_SHA1)
{
LogMsg("CryptoAlgInit: alg %d not supported", alg);
return mStatus_BadParamErr;
}
CryptoAlgFuncs[alg] = func;
return mStatus_NoError;
}
mDNSexport mStatus EncAlgInit(mDNSu8 alg, AlgFuncs *func)
{
if (alg >= ENC_ALG_MAX)
{
LogMsg("EncAlgInit: alg %d exceeds bounds", alg);
return mStatus_BadParamErr;
}
// As algs may not be consecutive, check for specific algorithms
// that we support
if (alg != ENC_BASE32 && alg != ENC_BASE64)
{
LogMsg("EncAlgInit: alg %d not supported", alg);
return mStatus_BadParamErr;
}
EncAlgFuncs[alg] = func;
return mStatus_NoError;
}
mDNSexport AlgContext *AlgCreate(AlgType type, mDNSu8 alg)
{
AlgFuncs *func = mDNSNULL;
AlgContext *ctx;
if (type == CRYPTO_ALG)
{
if (alg >= CRYPTO_ALG_MAX) return mDNSNULL;
func = CryptoAlgFuncs[alg];
}
else if (type == DIGEST_ALG)
{
if (alg >= DIGEST_TYPE_MAX) return mDNSNULL;
func = DigestAlgFuncs[alg];
}
else if (type == ENC_ALG)
{
if (alg >= ENC_ALG_MAX) return mDNSNULL;
func = EncAlgFuncs[alg];
}
if (!func)
{
// If there is no support from the platform, this case can happen.
LogInfo("AlgCreate: func is NULL");
return mDNSNULL;
}
if (func->Create)
{
mStatus err;
ctx = mDNSPlatformMemAllocate(sizeof(AlgContext));
if (!ctx) return mDNSNULL;
// Create expects ctx->alg to be initialized
ctx->alg = alg;
err = func->Create(ctx);
if (err == mStatus_NoError)
{
ctx->type = type;
return ctx;
}
mDNSPlatformMemFree(ctx);
}
return mDNSNULL;
}
mDNSexport mStatus AlgDestroy(AlgContext *ctx)
{
AlgFuncs *func = mDNSNULL;
if (ctx->type == CRYPTO_ALG)
func = CryptoAlgFuncs[ctx->alg];
else if (ctx->type == DIGEST_ALG)
func = DigestAlgFuncs[ctx->alg];
else if (ctx->type == ENC_ALG)
func = EncAlgFuncs[ctx->alg];
if (!func)
{
LogMsg("AlgDestroy: ERROR!! func is NULL");
mDNSPlatformMemFree(ctx);
return mStatus_BadParamErr;
}
if (func->Destroy)
func->Destroy(ctx);
mDNSPlatformMemFree(ctx);
return mStatus_NoError;
}
mDNSexport mDNSu32 AlgLength(AlgContext *ctx)
{
AlgFuncs *func = mDNSNULL;
if (ctx->type == CRYPTO_ALG)
func = CryptoAlgFuncs[ctx->alg];
else if (ctx->type == DIGEST_ALG)
func = DigestAlgFuncs[ctx->alg];
else if (ctx->type == ENC_ALG)
func = EncAlgFuncs[ctx->alg];
// This should never happen as AlgCreate would have failed
if (!func)
{
LogMsg("AlgLength: ERROR!! func is NULL");
return 0;
}
if (func->Length)
return (func->Length(ctx));
else
return 0;
}
mDNSexport mStatus AlgAdd(AlgContext *ctx, const void *data, mDNSu32 len)
{
AlgFuncs *func = mDNSNULL;
if (ctx->type == CRYPTO_ALG)
func = CryptoAlgFuncs[ctx->alg];
else if (ctx->type == DIGEST_ALG)
func = DigestAlgFuncs[ctx->alg];
else if (ctx->type == ENC_ALG)
func = EncAlgFuncs[ctx->alg];
// This should never happen as AlgCreate would have failed
if (!func)
{
LogMsg("AlgAdd: ERROR!! func is NULL");
return mStatus_BadParamErr;
}
if (func->Add)
return (func->Add(ctx, data, len));
else
return mStatus_BadParamErr;
}
mDNSexport mStatus AlgVerify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen)
{
AlgFuncs *func = mDNSNULL;
if (ctx->type == CRYPTO_ALG)
func = CryptoAlgFuncs[ctx->alg];
else if (ctx->type == DIGEST_ALG)
func = DigestAlgFuncs[ctx->alg];
else if (ctx->type == ENC_ALG)
func = EncAlgFuncs[ctx->alg];
// This should never happen as AlgCreate would have failed
if (!func)
{
LogMsg("AlgVerify: ERROR!! func is NULL");
return mStatus_BadParamErr;
}
if (func->Verify)
return (func->Verify(ctx, key, keylen, signature, siglen));
else
return mStatus_BadParamErr;
}
mDNSexport mDNSu8* AlgEncode(AlgContext *ctx)
{
AlgFuncs *func = mDNSNULL;
if (ctx->type == CRYPTO_ALG)
func = CryptoAlgFuncs[ctx->alg];
else if (ctx->type == DIGEST_ALG)
func = DigestAlgFuncs[ctx->alg];
else if (ctx->type == ENC_ALG)
func = EncAlgFuncs[ctx->alg];
// This should never happen as AlgCreate would have failed
if (!func)
{
LogMsg("AlgEncode: ERROR!! func is NULL");
return mDNSNULL;
}
if (func->Encode)
return (func->Encode(ctx));
else
return mDNSNULL;
}
mDNSexport mStatus AlgFinal(AlgContext *ctx, void *data, mDNSu32 len)
{
AlgFuncs *func = mDNSNULL;
if (ctx->type == CRYPTO_ALG)
func = CryptoAlgFuncs[ctx->alg];
else if (ctx->type == DIGEST_ALG)
func = DigestAlgFuncs[ctx->alg];
else if (ctx->type == ENC_ALG)
func = EncAlgFuncs[ctx->alg];
// This should never happen as AlgCreate would have failed
if (!func)
{
LogMsg("AlgEncode: ERROR!! func is NULL");
return mDNSNULL;
}
if (func->Final)
return (func->Final(ctx, data, len));
else
return mStatus_BadParamErr;
}

View File

@ -0,0 +1,61 @@
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __CRYPTO_ALG_H
#define __CRYPTO_ALG_H
typedef enum
{
CRYPTO_ALG,
DIGEST_ALG,
ENC_ALG,
} AlgType;
typedef struct
{
void *context;
AlgType type;
mDNSu8 alg;
} AlgContext;
typedef struct
{
mStatus (*Create)(AlgContext *ctx);
mStatus (*Destroy)(AlgContext *ctx);
mDNSu32 (*Length)(AlgContext *ctx);
mStatus (*Add)(AlgContext *ctx, const void *data, mDNSu32 len);
// Verify the ctx using the key and compare it against signature/siglen
mStatus (*Verify)(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen);
// Encode the data and return the encoded data
mDNSu8* (*Encode)(AlgContext *ctx);
// Return the finalized data in data whose length is len (used by hash algorithms)
mStatus (*Final)(AlgContext *ctx, void *data, mDNSu32 len);
} AlgFuncs;
mDNSexport mStatus DigestAlgInit(mDNSu8 digestType, AlgFuncs *func);
mDNSexport mStatus CryptoAlgInit(mDNSu8 algType, AlgFuncs *func);
mDNSexport mStatus EncAlgInit(mDNSu8 algType, AlgFuncs *func);
extern AlgContext *AlgCreate(AlgType type, mDNSu8 alg);
extern mStatus AlgDestroy(AlgContext *ctx);
extern mDNSu32 AlgLength(AlgContext *ctx);
extern mStatus AlgAdd(AlgContext *ctx, const void *data, mDNSu32 len);
extern mStatus AlgVerify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen);
extern mDNSu8* AlgEncode(AlgContext *ctx);
extern mStatus AlgFinal(AlgContext *ctx, void *data, mDNSu32 len);
#endif // __CRYPTO_ALG_H

File diff suppressed because it is too large Load Diff

View File

@ -21,7 +21,7 @@
#include "mDNSEmbeddedAPI.h"
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
//*************************************************************************************************************
@ -30,7 +30,7 @@
// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
// To expand "version" to its value before making the string, use STRINGIFY(version) instead
#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
// ***************************************************************************
@ -39,7 +39,7 @@
#endif
typedef enum
{
{
kDNSFlag0_QR_Mask = 0x80, // Query or response?
kDNSFlag0_QR_Query = 0x00,
kDNSFlag0_QR_Response = 0x80,
@ -75,14 +75,14 @@ typedef enum
kDNSFlag1_RC_NXRRSet = 0x08,
kDNSFlag1_RC_NotAuth = 0x09,
kDNSFlag1_RC_NotZone = 0x0A
} DNS_Flags;
} DNS_Flags;
typedef enum
{
{
TSIG_ErrBadSig = 16,
TSIG_ErrBadKey = 17,
TSIG_ErrBadTime = 18
} TSIG_ErrorCode;
} TSIG_ErrorCode;
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
@ -101,6 +101,8 @@ extern mDNSu32 mDNSRandom(mDNSu32 max); // Returns pseudo-random result from ze
#pragma mark - Domain Name Utility Functions
#endif
#define mDNSSubTypeLabel "\x04_sub"
#define mDNSIsDigit(X) ((X) >= '0' && (X) <= '9')
#define mDNSIsUpperCase(X) ((X) >= 'A' && (X) <= 'Z')
#define mDNSIsLowerCase(X) ((X) >= 'a' && (X) <= 'z')
@ -157,7 +159,8 @@ extern void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBo
// A given RRType answers a QuestionType if RRType is CNAME, or types match, or QuestionType is ANY,
// or the RRType is NSEC and positively asserts the nonexistence of the type being requested
#define RRTypeAnswersQuestionType(R,Q) ((R)->rrtype == kDNSType_CNAME || (R)->rrtype == (Q) || (Q) == kDNSQType_ANY || RRAssertsNonexistence((R),(Q)))
#define RRAssertsNonexistence(R,T) ((R)->rrtype == kDNSType_NSEC && (T) < kDNSQType_ANY && !((R)->rdata->u.nsec.bitmap[(T)>>3] & (128 >> ((T)&7))))
// Unicast NSEC records have the NSEC bit set whereas the multicast NSEC ones don't
#define UNICAST_NSEC(rr) ((rr)->rrtype == kDNSType_NSEC && RRAssertsExistence((rr), kDNSType_NSEC))
extern mDNSu32 RDataHashValue(const ResourceRecord *const rr);
extern mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename);
@ -168,6 +171,7 @@ extern mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const
extern mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const rr, const DNSQuestion *const q);
extern mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate);
extern mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd);
extern mStatus DNSNameToLowerCase(domainname *d, domainname *result);
#define GetRRDomainNameTarget(RR) ( \
((RR)->rrtype == kDNSType_NS || (RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR || (RR)->rrtype == kDNSType_DNAME) ? &(RR)->rdata->u.name : \
@ -203,9 +207,9 @@ extern mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr,
#define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl)
// The PutRR_OS variants assume a local variable 'm', put build the packet at m->omsg,
// and assume a local variable 'OwnerRecordSpace' indicating how many bytes (if any) to reserve to add an OWNER option at the end
// and assume local variables 'OwnerRecordSpace' & 'TraceRecordSpace' indicating how many bytes (if any) to reserve to add an OWNER/TRACER option at the end
#define PutRR_OS_TTL(ptr, count, rr, ttl) \
PutResourceRecordTTLWithLimit(&m->omsg, (ptr), (count), (rr), (ttl), m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace)
PutResourceRecordTTLWithLimit(&m->omsg, (ptr), (count), (rr), (ttl), m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace)
#define PutRR_OS(P, C, RR) PutRR_OS_TTL((P), (C), (RR), (RR)->rroriginalttl)
@ -220,6 +224,12 @@ extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease);
extern mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease, mDNSu8 *limit);
extern mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *ptr, DomainAuthInfo *authInfo, mDNSu8 *limit);
extern mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit);
extern int baseEncode(char *buffer, int blen, const mDNSu8 *data, int len, int encAlg);
extern void NSEC3Parse(const ResourceRecord *const rr, mDNSu8 **salt, int *hashLength, mDNSu8 **nxtName, int *bitmaplen, mDNSu8 **bitmap);
extern const mDNSu8 *NSEC3HashName(const domainname *name, rdataNSEC3 *nsec3, const mDNSu8 *AnonData, int AnonDataLen,
const mDNSu8 hash[NSEC3_MAX_HASH_LEN], int *dlen);
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
@ -237,6 +247,8 @@ extern const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *pt
extern const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end);
extern const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr,
const mDNSu8 * end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr);
extern mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end,
LargeCacheRecord *const largecr, mDNSu16 rdlength);
extern const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end);
extern const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
DNSQuestion *question);
@ -249,6 +261,12 @@ extern mDNSu32 GetPktLease(mDNS *m, DNSMessage *msg, const mDNSu8 *end);
extern void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport,
const mDNSAddr *srcaddr, mDNSIPPort srcport,
const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end);
extern mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type);
extern mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type);
extern mDNSBool BitmapTypeCheck(mDNSu8 *bmap, int bitmaplen, mDNSu16 type);
extern mDNSu16 swap16(mDNSu16 x);
extern mDNSu32 swap32(mDNSu32 x);
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
@ -257,7 +275,9 @@ extern void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *trans
#endif
extern mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo);
mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst,
mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo,
mDNSBool useBackgroundTrafficClass);
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
@ -277,16 +297,19 @@ extern void mDNS_Unlock_(mDNS *const m, const char * const functionname);
#define mDNS_Unlock(X) mDNS_Unlock_((X), __func__)
#define mDNS_CheckLock(X) { if ((X)->mDNS_busy != (X)->mDNS_reentrancy+1) \
LogMsg("%s: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", __func__, (X)->mDNS_busy, (X)->mDNS_reentrancy); }
#define mDNS_DropLockBeforeCallback() do { m->mDNS_reentrancy++; \
if (m->mDNS_busy != m->mDNS_reentrancy) LogMsg("%s: Locking Failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", __func__, m->mDNS_busy, m->mDNS_reentrancy); \
} while (0)
} while (0)
#define mDNS_ReclaimLockAfterCallback() do { \
if (m->mDNS_busy != m->mDNS_reentrancy) LogMsg("%s: Unlocking Failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", __func__, m->mDNS_busy, m->mDNS_reentrancy); \
m->mDNS_reentrancy--; } while (0)
#ifdef __cplusplus
}
}
#endif
#endif // __DNSCOMMON_H_

View File

@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
* Copyright (c) 2002-2011 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,30 +24,30 @@ extern "C" {
#include "DNSCommon.h"
// Disable certain benign warnings with Microsoft compilers
#if(defined(_MSC_VER))
// Disable "conditional expression is constant" warning for debug macros.
// Otherwise, this generates warnings for the perfectly natural construct "while(1)"
// If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
#if (defined(_MSC_VER))
// Disable "conditional expression is constant" warning for debug macros.
// Otherwise, this generates warnings for the perfectly natural construct "while(1)"
// If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
#pragma warning(disable:4127)
#endif
// ***************************************************************************
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - Byte Swapping Functions
#endif
mDNSlocal mDNSu16 NToH16(mDNSu8 * bytes)
{
{
return (mDNSu16)((mDNSu16)bytes[0] << 8 | (mDNSu16)bytes[1]);
}
}
mDNSlocal mDNSu32 NToH32(mDNSu8 * bytes)
{
{
return (mDNSu32)((mDNSu32) bytes[0] << 24 | (mDNSu32) bytes[1] << 16 | (mDNSu32) bytes[2] << 8 | (mDNSu32)bytes[3]);
}
}
// ***************************************************************************
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - MD5 Hash Functions
#endif
@ -182,17 +182,6 @@ mDNSlocal mDNSu32 NToH32(mDNSu8 * bytes)
//from CommonDigest.h
#define MD5_DIGEST_LENGTH 16 /* digest length in bytes */
#define MD5_BLOCK_BYTES 64 /* block size in bytes */
#define MD5_BLOCK_LONG (MD5_BLOCK_BYTES / sizeof(mDNSu32))
typedef struct MD5state_st
{
mDNSu32 A,B,C,D;
mDNSu32 Nl,Nh;
mDNSu32 data[MD5_BLOCK_LONG];
int num;
} MD5_CTX;
// from openssl/md5.h
@ -201,9 +190,6 @@ typedef struct MD5state_st
#define MD5_LBLOCK (MD5_CBLOCK/4)
#define MD5_DIGEST_LENGTH 16
int MD5_Init(MD5_CTX *c);
int MD5_Update(MD5_CTX *c, const void *data, unsigned long len);
int MD5_Final(unsigned char *md, MD5_CTX *c);
void MD5_Transform(MD5_CTX *c, const unsigned char *b);
// From md5_locl.h
@ -216,7 +202,7 @@ void MD5_Transform(MD5_CTX *c, const unsigned char *b);
# if defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(__INTEL__)
# define md5_block_host_order md5_block_asm_host_order
# elif defined(__sparc) && defined(OPENSSL_SYS_ULTRASPARC)
void md5_block_asm_data_order_aligned (MD5_CTX *c, const mDNSu32 *p,int num);
void md5_block_asm_data_order_aligned (MD5_CTX *c, const mDNSu32 *p,int num);
# define HASH_BLOCK_DATA_ORDER_ALIGNED md5_block_asm_data_order_aligned
# endif
#endif
@ -267,7 +253,7 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num);
ll=(c)->B; HOST_l2c(ll,(s)); \
ll=(c)->C; HOST_l2c(ll,(s)); \
ll=(c)->D; HOST_l2c(ll,(s)); \
} while (0)
} while (0)
#define HASH_BLOCK_HOST_ORDER md5_block_host_order
#if !defined(L_ENDIAN) || defined(md5_block_data_order)
#define HASH_BLOCK_DATA_ORDER md5_block_data_order
@ -419,20 +405,20 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num);
# if defined(__POWERPC__)
# define ROTATE(a,n) (unsigned MD32_REG_T)__rlwinm((int)a,n,0,31)
# elif defined(__MC68K__)
/* Motorola specific tweak. <appro@fy.chalmers.se> */
/* Motorola specific tweak. <appro@fy.chalmers.se> */
# define ROTATE(a,n) (n<24 ? __rol(a,n) : __ror(a,32-n))
# else
# define ROTATE(a,n) __rol(a,n)
# endif
# elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
/*
/*
* Some GNU C inline assembler templates. Note that these are
* rotates by *constant* number of bits! But that's exactly
* what we need here...
*
* <appro@fy.chalmers.se>
*/
/*
/*
* LLVM is more strict about compatibility of types between input & output constraints,
* but we want these to be rotations of 32 bits, not 64, so we explicitly drop the
* most significant bytes by casting to an unsigned int.
@ -441,8 +427,8 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num);
# define ROTATE(a,n) ({ register unsigned int ret; \
asm ( \
"roll %1,%0" \
: "=r"(ret) \
: "I"(n), "0"((unsigned int)a) \
: "=r" (ret) \
: "I" (n), "0" ((unsigned int)a) \
: "cc"); \
ret; \
})
@ -450,8 +436,8 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num);
# define ROTATE(a,n) ({ register unsigned int ret; \
asm ( \
"rlwinm %0,%1,%2,0,31" \
: "=r"(ret) \
: "r"(a), "I"(n)); \
: "=r" (ret) \
: "r" (a), "I" (n)); \
ret; \
})
# endif
@ -462,29 +448,29 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num);
* intrinsic function if available.
*/
# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
/* some GNU C inline assembler templates by <appro@fy.chalmers.se> */
/* some GNU C inline assembler templates by <appro@fy.chalmers.se> */
# if (defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)) && !defined(I386_ONLY)
# define BE_FETCH32(a) ({ register unsigned int l=(a);\
# define BE_FETCH32(a) ({ register unsigned int l=(a); \
asm ( \
"bswapl %0" \
: "=r"(l) : "0"(l)); \
: "=r" (l) : "0" (l)); \
l; \
})
# elif defined(__powerpc)
# define LE_FETCH32(a) ({ register unsigned int l; \
asm ( \
"lwbrx %0,0,%1" \
: "=r"(l) \
: "r"(a)); \
: "=r" (l) \
: "r" (a)); \
l; \
})
# elif defined(__sparc) && defined(OPENSSL_SYS_ULTRASPARC)
# define LE_FETCH32(a) ({ register unsigned int l; \
asm ( \
"lda [%1]#ASI_PRIMARY_LITTLE,%0"\
: "=r"(l) \
: "r"(a)); \
"lda [%1]#ASI_PRIMARY_LITTLE,%0" \
: "=r" (l) \
: "r" (a)); \
l; \
})
# endif
@ -571,9 +557,16 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num);
#endif
#endif
// None of the invocations of the following macros actually use the result,
// so cast them to void to avoid any compiler warnings/errors about not using
// the result (e.g. when using clang).
// If the resultant values need to be used at some point, these must be changed.
#define HOST_c2l(c,l) ((void)_HOST_c2l(c,l))
#define HOST_l2c(l,c) ((void)_HOST_l2c(l,c))
#if defined(DATA_ORDER_IS_BIG_ENDIAN)
#define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++)))<<24), \
#define _HOST_c2l(c,l) (l =(((unsigned long)(*((c)++)))<<24), \
l|=(((unsigned long)(*((c)++)))<<16), \
l|=(((unsigned long)(*((c)++)))<< 8), \
l|=(((unsigned long)(*((c)++))) ), \
@ -601,7 +594,7 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num);
case 2: l|=((unsigned long)(*(--(c))))<<16; \
case 1: l|=((unsigned long)(*(--(c))))<<24; \
} }
#define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \
#define _HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \
*((c)++)=(unsigned char)(((l)>>16)&0xff), \
*((c)++)=(unsigned char)(((l)>> 8)&0xff), \
*((c)++)=(unsigned char)(((l) )&0xff), \
@ -609,7 +602,7 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num);
#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
#define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \
#define _HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \
l|=(((unsigned long)(*((c)++)))<< 8), \
l|=(((unsigned long)(*((c)++)))<<16), \
l|=(((unsigned long)(*((c)++)))<<24), \
@ -637,7 +630,7 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num);
case 2: l|=((unsigned long)(*(--(c))))<< 8; \
case 1: l|=((unsigned long)(*(--(c)))); \
} }
#define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \
#define _HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \
*((c)++)=(unsigned char)(((l)>> 8)&0xff), \
*((c)++)=(unsigned char)(((l)>>16)&0xff), \
*((c)++)=(unsigned char)(((l)>>24)&0xff), \
@ -650,7 +643,7 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num);
*/
int HASH_UPDATE (HASH_CTX *c, const void *data_, unsigned long len)
{
{
const unsigned char *data=(const unsigned char *)data_;
register HASH_LONG * p;
register unsigned long l;
@ -763,11 +756,11 @@ int HASH_UPDATE (HASH_CTX *c, const void *data_, unsigned long len)
*p=l;
}
return 1;
}
}
void HASH_TRANSFORM (HASH_CTX *c, const unsigned char *data)
{
{
#if defined(HASH_BLOCK_DATA_ORDER_ALIGNED)
if ((((unsigned long)data)%4) == 0)
/* data is properly aligned so that we can cast it: */
@ -783,11 +776,11 @@ void HASH_TRANSFORM (HASH_CTX *c, const unsigned char *data)
#if defined(HASH_BLOCK_DATA_ORDER)
HASH_BLOCK_DATA_ORDER (c,data,1);
#endif
}
}
int HASH_FINAL (unsigned char *md, HASH_CTX *c)
{
{
register HASH_LONG *p;
register unsigned long l;
register int i,j;
@ -848,7 +841,7 @@ int HASH_FINAL (unsigned char *md, HASH_CTX *c)
OPENSSL_cleanse((void *)c,sizeof(HASH_CTX));
*/
return 1;
}
}
#ifndef MD32_REG_T
#define MD32_REG_T long
@ -879,9 +872,9 @@ int HASH_FINAL (unsigned char *md, HASH_CTX *c)
// from md5_locl.h (continued)
/*
#define F(x,y,z) (((x) & (y)) | ((~(x)) & (z)))
#define G(x,y,z) (((x) & (z)) | ((y) & (~(z))))
*/
#define F(x,y,z) (((x) & (y)) | ((~(x)) & (z)))
#define G(x,y,z) (((x) & (z)) | ((y) & (~(z))))
*/
/* As pointed out by Wei Dai <weidai@eskimo.com>, the above can be
* simplified to the code below. Wei attributes these optimizations
@ -895,7 +888,7 @@ int HASH_FINAL (unsigned char *md, HASH_CTX *c)
#define R0(a,b,c,d,k,s,t) { \
a+=((k)+(t)+F((b),(c),(d))); \
a=ROTATE(a,s); \
a+=b; };\
a+=b; }; \
#define R1(a,b,c,d,k,s,t) { \
a+=((k)+(t)+G((b),(c),(d))); \
@ -924,7 +917,7 @@ int HASH_FINAL (unsigned char *md, HASH_CTX *c)
#define INIT_DATA_D (unsigned long)0x10325476L
int MD5_Init(MD5_CTX *c)
{
{
c->A=INIT_DATA_A;
c->B=INIT_DATA_B;
c->C=INIT_DATA_C;
@ -933,11 +926,11 @@ int MD5_Init(MD5_CTX *c)
c->Nh=0;
c->num=0;
return 1;
}
}
#ifndef md5_block_host_order
void md5_block_host_order (MD5_CTX *c, const void *data, int num)
{
{
const mDNSu32 *X=(const mDNSu32 *)data;
register unsigned MD32_REG_T A,B,C,D;
@ -946,7 +939,7 @@ void md5_block_host_order (MD5_CTX *c, const void *data, int num)
C=c->C;
D=c->D;
for (;num--;X+=HASH_LBLOCK)
for (; num--; X+=HASH_LBLOCK)
{
/* Round 0 */
R0(A,B,C,D,X[ 0], 7,0xd76aa478L);
@ -1022,7 +1015,7 @@ void md5_block_host_order (MD5_CTX *c, const void *data, int num)
C = c->C += C;
D = c->D += D;
}
}
}
#endif
#ifndef md5_block_data_order
@ -1030,14 +1023,14 @@ void md5_block_host_order (MD5_CTX *c, const void *data, int num)
#undef X
#endif
void md5_block_data_order (MD5_CTX *c, const void *data_, int num)
{
{
const unsigned char *data=data_;
register unsigned MD32_REG_T A,B,C,D,l;
#ifndef MD32_XARRAY
/* See comment in crypto/sha/sha_locl.h for details. */
unsigned MD32_REG_T XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7,
XX8, XX9,XX10,XX11,XX12,XX13,XX14,XX15;
# define X(i) XX##i
# define X(i) XX ## i
#else
mDNSu32 XX[MD5_LBLOCK];
# define X(i) XX[i]
@ -1048,7 +1041,7 @@ void md5_block_data_order (MD5_CTX *c, const void *data_, int num)
C=c->C;
D=c->D;
for (;num--;)
for (; num--;)
{
HOST_c2l(data,l); X( 0)=l; HOST_c2l(data,l); X( 1)=l;
/* Round 0 */
@ -1125,11 +1118,11 @@ void md5_block_data_order (MD5_CTX *c, const void *data_, int num)
C = c->C += C;
D = c->D += D;
}
}
}
#endif
// ***************************************************************************
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - base64 -> binary conversion
#endif
@ -1141,14 +1134,14 @@ static const char Pad64 = '=';
#define mDNSisspace(x) (x == '\t' || x == '\n' || x == '\v' || x == '\f' || x == '\r' || x == ' ')
mDNSlocal const char *mDNSstrchr(const char *s, int c)
{
{
while (1)
{
if (c == *s) return s;
if (!*s) return mDNSNULL;
s++;
}
}
}
// skips all whitespace anywhere.
// converts characters, four at a time, starting at (or after)
@ -1157,7 +1150,7 @@ mDNSlocal const char *mDNSstrchr(const char *s, int c)
// adapted from BIND sources
mDNSlocal mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 targsize)
{
{
int tarindex, state, ch;
const char *pos;
@ -1270,10 +1263,10 @@ mDNSlocal mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32
}
return (tarindex);
}
}
// ***************************************************************************
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - API exported to mDNS Core
#endif
@ -1287,7 +1280,7 @@ mDNSlocal mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32
// Adapted from Appendix, RFC 2104
mDNSlocal void DNSDigest_ConstructHMACKey(DomainAuthInfo *info, const mDNSu8 *key, mDNSu32 len)
{
{
MD5_CTX k;
mDNSu8 buf[MD5_LEN];
int i;
@ -1315,19 +1308,19 @@ mDNSlocal void DNSDigest_ConstructHMACKey(DomainAuthInfo *info, const mDNSu8 *ke
info->keydata_opad[i] ^= HMAC_OPAD;
}
}
}
mDNSexport mDNSs32 DNSDigest_ConstructHMACKeyfromBase64(DomainAuthInfo *info, const char *b64key)
{
{
mDNSu8 keybuf[1024];
mDNSs32 keylen = DNSDigest_Base64ToBin(b64key, keybuf, sizeof(keybuf));
if (keylen < 0) return(keylen);
DNSDigest_ConstructHMACKey(info, keybuf, (mDNSu32)keylen);
return(keylen);
}
}
mDNSexport void DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, DomainAuthInfo *info, mDNSu16 tcode)
{
{
AuthRecord tsig;
mDNSu8 *rdata, *const countPtr = (mDNSu8 *)&msg->h.numAdditionals; // Get existing numAdditionals value
mDNSu32 utc32;
@ -1424,16 +1417,15 @@ mDNSexport void DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, DomainAuthI
// Write back updated numAdditionals value
countPtr[0] = (mDNSu8)(numAdditionals >> 8);
countPtr[1] = (mDNSu8)(numAdditionals & 0xFF);
}
}
mDNSexport mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeCacheRecord * lcr, DomainAuthInfo *info, mDNSu16 * rcode, mDNSu16 * tcode)
{
{
mDNSu8 * ptr = (mDNSu8*) &lcr->r.resrec.rdata->u.data;
mDNSs32 now;
mDNSs32 then;
mDNSu8 thisDigest[MD5_LEN];
mDNSu8 thatDigest[MD5_LEN];
mDNSu32 macsize;
mDNSOpaque16 buf;
mDNSu8 utc48[6];
mDNSs32 delta;
@ -1497,8 +1489,6 @@ mDNSexport mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeC
// MAC size
macsize = (mDNSu32) NToH16(ptr);
ptr += sizeof(mDNSu16);
// MAC
@ -1569,7 +1559,7 @@ mDNSexport mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeC
exit:
return ok;
}
}
#ifdef __cplusplus

View File

@ -0,0 +1,597 @@
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2012 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mDNSEmbeddedAPI.h"
#include "CryptoAlg.h"
#include "anonymous.h"
#include "DNSCommon.h"
// Define ANONYMOUS_DISABLED to remove all the anonymous functionality
// and use the stub functions implemented later in this file.
#ifndef ANONYMOUS_DISABLED
#define ANON_NSEC3_ITERATIONS 1
mDNSlocal mDNSBool InitializeNSEC3Record(ResourceRecord *rr, const mDNSu8 *AnonData, int len, mDNSu32 salt)
{
const mDNSu8 *ptr;
rdataNSEC3 *nsec3 = (rdataNSEC3 *)rr->rdata->u.data;
mDNSu8 *tmp, *nxt;
unsigned short iter = ANON_NSEC3_ITERATIONS;
int hlen;
const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
// Construct the RDATA first and construct the owner name based on that.
ptr = (const mDNSu8 *)&salt;
debugf("InitializeNSEC3Record: %x%x%x%x, name %##s", ptr[0], ptr[1], ptr[2], ptr[3], rr->name->c);
// Set the RDATA
nsec3->alg = SHA1_DIGEST_TYPE;
nsec3->flags = 0;
nsec3->iterations = swap16(iter);
nsec3->saltLength = 4;
tmp = (mDNSu8 *)&nsec3->salt;
*tmp++ = ptr[0];
*tmp++ = ptr[1];
*tmp++ = ptr[2];
*tmp++ = ptr[3];
// hashLength, nxt, bitmap
*tmp++ = SHA1_HASH_LENGTH; // hash length
nxt = tmp;
tmp += SHA1_HASH_LENGTH;
*tmp++ = 0; // window number
*tmp++ = NSEC_MCAST_WINDOW_SIZE; // window length
mDNSPlatformMemZero(tmp, NSEC_MCAST_WINDOW_SIZE);
tmp[kDNSType_PTR >> 3] |= 128 >> (kDNSType_PTR & 7);
// Hash the base service name + salt + AnonData
if (!NSEC3HashName(rr->name, nsec3, AnonData, len, hashName, &hlen))
{
LogMsg("InitializeNSEC3Record: NSEC3HashName failed for ##s", rr->name->c);
return mDNSfalse;
}
if (hlen != SHA1_HASH_LENGTH)
{
LogMsg("InitializeNSEC3Record: hlen wrong %d", hlen);
return mDNSfalse;
}
mDNSPlatformMemCopy(nxt, hashName, hlen);
return mDNStrue;
}
mDNSlocal ResourceRecord *ConstructNSEC3Record(const domainname *service, const mDNSu8 *AnonData, int len, mDNSu32 salt)
{
ResourceRecord *rr;
int dlen;
domainname *name;
// We are just allocating an RData which has StandardAuthRDSize
if (StandardAuthRDSize < MCAST_NSEC3_RDLENGTH)
{
LogMsg("ConstructNSEC3Record: StandardAuthRDSize %d smaller than MCAST_NSEC3_RDLENGTH %d", StandardAuthRDSize, MCAST_NSEC3_RDLENGTH);
return mDNSNULL;
}
dlen = DomainNameLength(service);
// Allocate space for the name and RData.
rr = mDNSPlatformMemAllocate(sizeof(ResourceRecord) + dlen + sizeof(RData));
if (!rr)
return mDNSNULL;
name = (domainname *)((mDNSu8 *)rr + sizeof(ResourceRecord));
rr->RecordType = kDNSRecordTypePacketAuth;
rr->InterfaceID = mDNSInterface_Any;
rr->name = (const domainname *)name;
rr->rrtype = kDNSType_NSEC3;
rr->rrclass = kDNSClass_IN;
rr->rroriginalttl = kStandardTTL;
rr->rDNSServer = mDNSNULL;
rr->rdlength = MCAST_NSEC3_RDLENGTH;
rr->rdestimate = MCAST_NSEC3_RDLENGTH;
rr->rdata = (RData *)((mDNSu8 *)rr->name + dlen);
AssignDomainName(name, service);
if (!InitializeNSEC3Record(rr, AnonData, len, salt))
{
mDNSPlatformMemFree(rr);
return mDNSNULL;
}
return rr;
}
mDNSlocal ResourceRecord *CopyNSEC3ResourceRecord(AnonymousInfo *si, const ResourceRecord *rr)
{
int len;
domainname *name;
ResourceRecord *nsec3rr;
if (rr->rdlength < MCAST_NSEC3_RDLENGTH)
{
LogMsg("CopyNSEC3ResourceRecord: rdlength %d smaller than MCAST_NSEC3_RDLENGTH %d", rr->rdlength, MCAST_NSEC3_RDLENGTH);
return mDNSNULL;
}
// Allocate space for the name and the rdata along with the ResourceRecord
len = DomainNameLength(rr->name);
nsec3rr = mDNSPlatformMemAllocate(sizeof(ResourceRecord) + len + sizeof(RData));
if (!nsec3rr)
return mDNSNULL;
*nsec3rr = *rr;
name = (domainname *)((mDNSu8 *)nsec3rr + sizeof(ResourceRecord));
nsec3rr->name = (const domainname *)name;
AssignDomainName(name, rr->name);
nsec3rr->rdata = (RData *)((mDNSu8 *)nsec3rr->name + len);
mDNSPlatformMemCopy(nsec3rr->rdata->u.data, rr->rdata->u.data, rr->rdlength);
si->nsec3RR = nsec3rr;
return nsec3rr;
}
// When a service is started or a browse is started with the Anonymous data, we allocate a new random
// number and based on that allocate a new NSEC3 resource record whose hash is a function of random number (salt) and
// the anonymous data.
//
// If we receive a packet with the NSEC3 option, we need to cache that along with the resource record so that we can
// check against the question to see whether it answers them or not. In that case, we pass the "rr" that we received.
mDNSexport AnonymousInfo *AllocateAnonInfo(const domainname *service, const mDNSu8 *data, int len, const ResourceRecord *rr)
{
AnonymousInfo *ai;
ai = (AnonymousInfo *)mDNSPlatformMemAllocate(sizeof(AnonymousInfo));
if (!ai)
{
return mDNSNULL;
}
mDNSPlatformMemZero(ai, sizeof(AnonymousInfo));
if (rr)
{
if (!CopyNSEC3ResourceRecord(ai, rr))
{
mDNSPlatformMemFree(ai);
return mDNSNULL;
}
return ai;
}
ai->salt = mDNSRandom(0xFFFFFFFF);
ai->AnonData = mDNSPlatformMemAllocate(len);
if (!ai->AnonData)
{
mDNSPlatformMemFree(ai);
return mDNSNULL;
}
ai->AnonDataLen = len;
mDNSPlatformMemCopy(ai->AnonData, data, len);
ai->nsec3RR = ConstructNSEC3Record(service, data, len, ai->salt);
if (!ai->nsec3RR)
{
mDNSPlatformMemFree(ai);
return mDNSNULL;
}
return ai;
}
mDNSexport void FreeAnonInfo(AnonymousInfo *ai)
{
if (ai->nsec3RR)
mDNSPlatformMemFree(ai->nsec3RR);
if (ai->AnonData)
mDNSPlatformMemFree(ai->AnonData);
mDNSPlatformMemFree(ai);
}
mDNSexport void ReInitAnonInfo(AnonymousInfo **AnonInfo, const domainname *name)
{
if (*AnonInfo)
{
AnonymousInfo *ai = *AnonInfo;
*AnonInfo = AllocateAnonInfo(name, ai->AnonData, ai->AnonDataLen, mDNSNULL);
if (!(*AnonInfo))
*AnonInfo = ai;
else
FreeAnonInfo(ai);
}
}
// This function should be used only if you know that the question and
// the resource record belongs to the same set. The main usage is
// in ProcessQuery where we find the question to be part of the same
// set as the resource record, but it needs the AnonData to be
// initialized so that it can walk the cache records to see if they
// answer the question.
mDNSexport void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion)
{
if (!q->AnonInfo || !rr->AnonInfo)
{
LogMsg("SetAnonData: question %##s(%p), rr %##s(%p), NULL", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo);
return;
}
debugf("SetAnonData: question %##s(%p), rr %##s(%p)", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo);
if (ForQuestion)
{
if (!q->AnonInfo->AnonData)
{
q->AnonInfo->AnonData = mDNSPlatformMemAllocate(rr->AnonInfo->AnonDataLen);
if (!q->AnonInfo->AnonData)
return;
}
mDNSPlatformMemCopy(q->AnonInfo->AnonData, rr->AnonInfo->AnonData, rr->AnonInfo->AnonDataLen);
q->AnonInfo->AnonDataLen = rr->AnonInfo->AnonDataLen;
}
else
{
if (!rr->AnonInfo->AnonData)
{
rr->AnonInfo->AnonData = mDNSPlatformMemAllocate(q->AnonInfo->AnonDataLen);
if (!rr->AnonInfo->AnonData)
return;
}
mDNSPlatformMemCopy(rr->AnonInfo->AnonData, q->AnonInfo->AnonData, q->AnonInfo->AnonDataLen);
rr->AnonInfo->AnonDataLen = q->AnonInfo->AnonDataLen;
}
}
// returns -1 if the caller should ignore the result
// returns 1 if the record answers the question
// returns 0 if the record does not answer the question
mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
{
mDNSexport mDNS mDNSStorage;
ResourceRecord *nsec3RR;
int i;
AnonymousInfo *qai, *rai;
mDNSu8 *AnonData;
int AnonDataLen;
rdataNSEC3 *nsec3;
int hlen;
const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
int nxtLength;
mDNSu8 *nxtName;
debugf("AnonInfoAnswersQuestion: question qname %##s", q->qname.c);
// Currently only PTR records can have anonymous information
if (q->qtype != kDNSType_PTR)
{
return -1;
}
// We allow anonymous questions to be answered by both normal services (without the
// anonymous information) and anonymous services that are part of the same set. And
// normal questions discover normal services and all anonymous services.
//
// The three cases have been enumerated clearly even though they all behave the
// same way.
if (!q->AnonInfo)
{
debugf("AnonInfoAnswersQuestion: not a anonymous type question");
if (!rr->AnonInfo)
{
// case 1
return -1;
}
else
{
// case 2
debugf("AnonInfoAnswersQuestion: Question %##s not answered using anonymous record %##s", q->qname.c, rr->name->c);
return -1;
}
}
else
{
// case 3
if (!rr->AnonInfo)
{
debugf("AnonInfoAnswersQuestion: not a anonymous type record");
return -1;
}
}
// case 4: We have the anonymous information both in the question and the record. We need
// two sets of information to validate.
//
// 1) Anonymous data that identifies the set/group
// 2) NSEC3 record that contains the hash and the salt
//
// If the question is a remote one, it does not have the anonymous information to validate (just
// the NSEC3 record) and hence the anonymous data should come from the local resource record. If the
// question is local, it can come from either of them and if there is a mismatch between the
// question and record, it won't validate.
qai = q->AnonInfo;
rai = rr->AnonInfo;
if (qai->AnonData && rai->AnonData)
{
// Before a cache record is created, if there is a matching question i.e., part
// of the same set, then when the cache is created we also set the anonymous
// information. Otherwise, the cache record contains just the NSEC3 record and we
// won't be here for that case.
//
// It is also possible that a local question is matched against the local AuthRecord
// as that is also the case for which the AnonData would be non-NULL for both.
// We match questions against AuthRecords (rather than the cache) for LocalOnly case and
// to see whether a .local query should be suppressed or not. The latter never happens
// because PTR queries are never suppressed.
// If they don't belong to the same anonymous set, then no point in validating.
if ((qai->AnonDataLen != rai->AnonDataLen) ||
mDNSPlatformMemCmp(qai->AnonData, rai->AnonData, qai->AnonDataLen) != 0)
{
debugf("AnonInfoAnswersQuestion: AnonData mis-match for record %s question %##s ",
RRDisplayString(&mDNSStorage, rr), q->qname.c);
return 0;
}
// AnonData matches i.e they belong to the same group and the same service.
LogInfo("AnonInfoAnswersQuestion: Answering qname %##s, rname %##s, without validation", q->qname.c,
rr->name->c);
return 1;
}
else
{
debugf("AnonInfoAnswersQuestion: question %p, record %p", qai->AnonData, rai->AnonData);
}
if (qai->AnonData)
{
// If there is AnonData, then this is a local question. The
// NSEC3 RR comes from the resource record which could be part
// of the cache or local auth record. The cache entry could
// be from a remote host or created when we heard our own
// announcements. In any case, we use that to see if it matches
// the question.
AnonData = qai->AnonData;
AnonDataLen = qai->AnonDataLen;
nsec3RR = rai->nsec3RR;
}
else
{
// Remote question or hearing our own question back
AnonData = rai->AnonData;
AnonDataLen = rai->AnonDataLen;
nsec3RR = qai->nsec3RR;
}
if (!AnonData || !nsec3RR)
{
// AnonData can be NULL for the cache entry and if we are hearing our own question back, AnonData is NULL for
// that too and we can end up here for that case.
debugf("AnonInfoAnswersQuestion: AnonData %p or nsec3RR %p, NULL for question %##s, record %s", AnonData, nsec3RR,
q->qname.c, RRDisplayString(&mDNSStorage, rr));
return 0;
}
debugf("AnonInfoAnswersQuestion: Validating question %##s, ResourceRecord %s", q->qname.c, RRDisplayString(&mDNSStorage, nsec3RR));
nsec3 = (rdataNSEC3 *)nsec3RR->rdata->u.data;
if (!NSEC3HashName(nsec3RR->name, nsec3, AnonData, AnonDataLen, hashName, &hlen))
{
LogMsg("AnonInfoAnswersQuestion: NSEC3HashName failed for ##s", nsec3RR->name->c);
return mDNSfalse;
}
if (hlen != SHA1_HASH_LENGTH)
{
LogMsg("AnonInfoAnswersQuestion: hlen wrong %d", hlen);
return mDNSfalse;
}
NSEC3Parse(nsec3RR, mDNSNULL, &nxtLength, &nxtName, mDNSNULL, mDNSNULL);
if (hlen != nxtLength)
{
LogMsg("AnonInfoAnswersQuestion: ERROR!! hlen %d not same as nxtLength %d", hlen, nxtLength);
return mDNSfalse;
}
for (i = 0; i < nxtLength; i++)
{
if (nxtName[i] != hashName[i])
{
debugf("AnonInfoAnswersQuestion: mismatch output %x, digest %x, i %d", nxtName[i+1], hashName[i], i);
return 0;
}
}
LogInfo("AnonInfoAnswersQuestion: ResourceRecord %s matched question %##s (%s)", RRDisplayString(&mDNSStorage, nsec3RR), q->qname.c, DNSTypeName(q->qtype));
return 1;
}
// Find a matching NSEC3 record for the name. We parse the questions and the records in the packet in order.
// Similarly we also parse the NSEC3 records in order and this mapping to the questions and records
// respectively.
mDNSlocal CacheRecord *FindMatchingNSEC3ForName(mDNS *const m, CacheRecord **nsec3, const domainname *name)
{
CacheRecord *cr;
CacheRecord **prev = nsec3;
(void) m;
for (cr = *nsec3; cr; cr = cr->next)
{
if (SameDomainName(cr->resrec.name, name))
{
debugf("FindMatchingNSEC3ForName: NSEC3 record %s matched %##s", CRDisplayString(m, cr), name->c);
*prev = cr->next;
cr->next = mDNSNULL;
return cr;
}
prev = &cr->next;
}
return mDNSNULL;
}
mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q)
{
CacheRecord *nsec3CR;
if (q->qtype != kDNSType_PTR)
return;
nsec3CR = FindMatchingNSEC3ForName(m, McastNSEC3Records, &q->qname);
if (nsec3CR)
{
q->AnonInfo = AllocateAnonInfo(mDNSNULL, mDNSNULL, 0, &nsec3CR->resrec);
if (q->AnonInfo)
{
debugf("InitializeAnonInfoForQuestion: Found a matching NSEC3 record %s, for %##s (%s)",
RRDisplayString(m, q->AnonInfo->nsec3RR), q->qname.c, DNSTypeName(q->qtype));
}
ReleaseCacheRecord(m, nsec3CR);
}
}
mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr)
{
CacheRecord *nsec3CR;
if (!(*McastNSEC3Records))
return;
// If already initialized or not a PTR type, we don't have to do anything
if (cr->resrec.AnonInfo || cr->resrec.rrtype != kDNSType_PTR)
return;
nsec3CR = FindMatchingNSEC3ForName(m, McastNSEC3Records, cr->resrec.name);
if (nsec3CR)
{
cr->resrec.AnonInfo = AllocateAnonInfo(mDNSNULL, mDNSNULL, 0, &nsec3CR->resrec);
if (cr->resrec.AnonInfo)
{
debugf("InitializeAnonInfoForCR: Found a matching NSEC3 record %s, for %##s (%s)",
RRDisplayString(m, cr->resrec.AnonInfo->nsec3RR), cr->resrec.name->c,
DNSTypeName(cr->resrec.rrtype));
}
ReleaseCacheRecord(m, nsec3CR);
}
}
mDNSexport mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2)
{
// if a1 is NULL and a2 is not NULL AND vice-versa
// return false as there is a change.
if ((a1 != mDNSNULL) != (a2 != mDNSNULL))
return mDNSfalse;
// Both could be NULL or non-NULL
if (a1 && a2)
{
// The caller already verified that the owner name is the same.
// Check whether the RData is same.
if (!IdenticalSameNameRecord(a1->nsec3RR, a2->nsec3RR))
{
debugf("IdenticalAnonInfo: nsec3RR mismatch");
return mDNSfalse;
}
}
return mDNStrue;
}
mDNSexport void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom)
{
AnonymousInfo *aifrom = crfrom->resrec.AnonInfo;
AnonymousInfo *aito = crto->resrec.AnonInfo;
(void) m;
if (!aifrom)
return;
if (aito)
{
crto->resrec.AnonInfo = aifrom;
FreeAnonInfo(aito);
crfrom->resrec.AnonInfo = mDNSNULL;
}
else
{
FreeAnonInfo(aifrom);
crfrom->resrec.AnonInfo = mDNSNULL;
}
}
#else // !ANONYMOUS_DISABLED
mDNSexport void ReInitAnonInfo(AnonymousInfo **si, const domainname *name)
{
(void)si;
(void)name;
}
mDNSexport AnonymousInfo * AllocateAnonInfo(const domainname *service, const mDNSu8 *AnonData, int len, const ResourceRecord *rr)
{
(void)service;
(void)AnonData;
(void)len;
(void)rr;
return mDNSNULL;
}
mDNSexport void FreeAnonInfo(AnonymousInfo *ai)
{
(void)ai;
}
mDNSexport void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion)
{
(void)q;
(void)rr;
(void)ForQuestion;
}
mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
{
(void)rr;
(void)q;
return mDNSfalse;
}
mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q)
{
(void)m;
(void)McastNSEC3Records;
(void)q;
}
mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr)
{
(void)m;
(void)McastNSEC3Records;
(void)cr;
}
mDNSexport void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom)
{
(void)m;
(void)crto;
(void)crfrom;
}
mDNSexport mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2)
{
(void)a1;
(void)a2;
return mDNStrue;
}
#endif // !ANONYMOUS_DISABLED

View File

@ -0,0 +1,31 @@
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2012 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __ANONYMOUS_H_
#define __ANONYMOUS_H_
extern void ReInitAnonInfo(AnonymousInfo **si, const domainname *name);
extern AnonymousInfo *AllocateAnonInfo(const domainname *service, const mDNSu8 *AnonData, int len, const ResourceRecord *rr);
extern void FreeAnonInfo(AnonymousInfo *ai);
extern void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion);
extern int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q);
extern void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr);
extern void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q);
extern void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom);
extern mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2);
#endif

View File

@ -0,0 +1,836 @@
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "dnsproxy.h"
#ifndef UNICAST_DISABLED
// Implementation Notes
//
// DNS Proxy listens on port 53 (UDPv4v6 & TCPv4v6) for DNS queries. It handles only
// the "Query" opcode of the DNS protocol described in RFC 1035. For all other opcodes, it returns
// "Not Implemented" error. The platform interface mDNSPlatformInitDNSProxySkts
// sets up the sockets and whenever it receives a packet, it calls ProxyTCPCallback or ProxyUDPCallback
// defined here. For TCP socket, the platform does the "accept" and only sends the received packets
// on the newly accepted socket. A single UDP socket (per address family) is used to send/recv
// requests/responses from all clients. For TCP, there is one socket per request. Hence, there is some
// extra state that needs to be disposed at the end.
//
// When a DNS request is received, ProxyCallbackCommon checks for malformed packet etc. and also checks
// for duplicates, before creating DNSProxyClient state and starting a question with the "core"
// (mDNS_StartQuery). When the callback for the question happens, it gathers all the necessary
// resource records, constructs a response and sends it back to the client.
//
// - Question callback is called with only one resource record at a time. We need all the resource
// records to construct the response. Hence, we lookup all the records ourselves.
//
// - The response may not fit the client's buffer size. In that case, we need to set the truncate bit
// and the client would retry using TCP.
//
// - The client may have set the DNSSEC OK bit in the EDNS0 option and that means we also have to
// return the RRSIGs or the NSEC records with the RRSIGs in the Additional section. We need to
// ask the "core" to fetch the DNSSEC records and do the validation if the CD bit is not set.
//
// Once the response is sent to the client, the client state is disposed. When there is no response
// from the "core", it eventually times out and we will not find any answers in the cache and we send a
// "NXDomain" response back. Thus, we don't need any special timers to reap the client state in the case
// of errors.
typedef struct DNSProxyClient_struct DNSProxyClient;
struct DNSProxyClient_struct {
DNSProxyClient *next;
mDNSAddr addr; // Client's IP address
mDNSIPPort port; // Client's port number
mDNSOpaque16 msgid; // DNS msg id
mDNSInterfaceID interfaceID; // Interface on which we received the request
void *socket; // Return socket
mDNSBool tcp; // TCP or UDP ?
mDNSOpaque16 requestFlags; // second 16 bit word in the DNSMessageHeader of the request
mDNSu8 *optRR; // EDNS0 option
mDNSu16 optLen; // Total Length of the EDNS0 option
mDNSu16 rcvBufSize; // How much can the client receive ?
mDNSBool DNSSECOK; // DNSSEC OK ?
void *context; // Platform context to be disposed if non-NULL
domainname qname; // q->qname can't be used for duplicate check
DNSQuestion q; // as it can change underneath us for CNAMEs
};
#define MIN_DNS_MESSAGE_SIZE 512
DNSProxyClient *DNSProxyClients;
mDNSlocal void FreeDNSProxyClient(DNSProxyClient *pc)
{
if (pc->optRR)
mDNSPlatformMemFree(pc->optRR);
mDNSPlatformMemFree(pc);
}
mDNSlocal mDNSBool ParseEDNS0(DNSProxyClient *pc, const mDNSu8 *ptr, int length, const mDNSu8 *limit)
{
mDNSu16 rrtype, rrclass;
mDNSu8 rcode, version;
mDNSu16 flag;
if (ptr + length > limit)
{
LogInfo("ParseEDNS0: Not enough space in the packet");
return mDNSfalse;
}
// Skip the root label
ptr++;
rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]);
if (rrtype != kDNSType_OPT)
{
LogInfo("ParseEDNS0: Not the right type %d", rrtype);
return mDNSfalse;
}
rrclass = (mDNSu16) ((mDNSu16)ptr[2] << 8 | ptr[3]);
rcode = ptr[4];
version = ptr[5];
flag = (mDNSu16) ((mDNSu16)ptr[6] << 8 | ptr[7]);
debugf("rrtype is %s, length is %d, rcode %d, version %d, flag 0x%x", DNSTypeName(rrtype), rrclass, rcode, version, flag);
pc->rcvBufSize = rrclass;
pc->DNSSECOK = ptr[6] & 0x80;
return mDNStrue;
}
mDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit)
{
DNSProxyClient *pc = (DNSProxyClient *)q->QuestionContext;
(void) msg;
h->flags = pc->requestFlags;
if (pc->optRR)
{
if (ptr + pc->optLen > limit)
{
LogInfo("DNSProxySetAttributes: Cannot set EDNS0 option start %p, OptLen %d, end %p", ptr, pc->optLen, limit);
return ptr;
}
h->numAdditionals++;
mDNSPlatformMemCopy(ptr, pc->optRR, pc->optLen);
ptr += pc->optLen;
}
return ptr;
}
mDNSlocal mDNSu8 *AddEDNS0Option(mDNS *const m, mDNSu8 *ptr, mDNSu8 *limit)
{
int len = 4096;
if (ptr + 11 > limit)
{
LogInfo("AddEDNS0Option: not enough space");
return mDNSNULL;
}
m->omsg.h.numAdditionals++;
ptr[0] = 0;
ptr[1] = (mDNSu8) (kDNSType_OPT >> 8);
ptr[2] = (mDNSu8) (kDNSType_OPT & 0xFF);
ptr[3] = (mDNSu8) (len >> 8);
ptr[4] = (mDNSu8) (len & 0xFF);
ptr[5] = 0; // rcode
ptr[6] = 0; // version
ptr[7] = 0;
ptr[8] = 0; // flags
ptr[9] = 0; // rdlength
ptr[10] = 0; // rdlength
debugf("AddEDNS0 option");
return (ptr + 11);
}
// Currently RD and CD bit should be copied if present in the request or cleared if
// not present in the request. RD bit is normally set in the response and hence the
// cache reflects the right value. CD bit behaves differently. If the CD bit is set
// the first time, the cache retains it, if it is present in response (assuming the
// upstream server does it right). Next time through we should not use the cached
// value of the CD bit blindly. It depends on whether it was in the request or not.
mDNSlocal mDNSOpaque16 SetResponseFlags(DNSProxyClient *pc, const mDNSOpaque16 responseFlags)
{
mDNSOpaque16 rFlags = responseFlags;
if (pc->requestFlags.b[0] & kDNSFlag0_RD)
rFlags.b[0] |= kDNSFlag0_RD;
else
rFlags.b[0] &= ~kDNSFlag0_RD;
if (pc->requestFlags.b[1] & kDNSFlag1_CD)
rFlags.b[1] |= kDNSFlag1_CD;
else
rFlags.b[1] &= ~kDNSFlag1_CD;
return rFlags;
}
mDNSlocal mDNSu8 *AddResourceRecords(mDNS *const m, DNSProxyClient *pc, mDNSu8 **prevptr, mStatus *error)
{
mDNSu32 slot;
CacheGroup *cg;
CacheRecord *cr;
int len = sizeof(DNSMessageHeader);
mDNSu8 *orig = m->omsg.data;
mDNSBool first = mDNStrue;
mDNSu8 *ptr = mDNSNULL;
mDNSs32 now;
mDNSs32 ttl;
CacheRecord *nsec = mDNSNULL;
CacheRecord *soa = mDNSNULL;
CacheRecord *cname = mDNSNULL;
mDNSu8 *limit;
*error = mStatus_NoError;
*prevptr = mDNSNULL;
mDNS_Lock(m);
now = m->timenow;
mDNS_Unlock(m);
if (!pc->tcp)
{
if (!pc->rcvBufSize)
{
limit = m->omsg.data + MIN_DNS_MESSAGE_SIZE;
}
else
{
limit = (pc->rcvBufSize > AbsoluteMaxDNSMessageData ? m->omsg.data + AbsoluteMaxDNSMessageData : m->omsg.data + pc->rcvBufSize);
}
}
else
{
// For TCP, limit is not determined by EDNS0 but by 16 bit rdlength field and
// AbsoluteMaxDNSMessageData is smaller than 64k.
limit = m->omsg.data + AbsoluteMaxDNSMessageData;
}
LogInfo("AddResourceRecords: Limit is %d", limit - m->omsg.data);
if (!SameDomainName(&pc->qname, &pc->q.qname))
{
AssignDomainName(&pc->q.qname, &pc->qname);
pc->q.qnamehash = DomainNameHashValue(&pc->q.qname);
}
again:
nsec = soa = cname = mDNSNULL;
slot = HashSlot(&pc->q.qname);
cg = CacheGroupForName(m, slot, pc->q.qnamehash, &pc->q.qname);
if (!cg)
{
LogInfo("AddResourceRecords: CacheGroup not found");
*error = mStatus_NoSuchRecord;
return mDNSNULL;
}
// Set ValidatingResponse so that you can get RRSIGs also matching
// the question
if (pc->DNSSECOK)
pc->q.ValidatingResponse = 1;
for (cr = cg->members; cr; cr = cr->next)
{
if (SameNameRecordAnswersQuestion(&cr->resrec, &pc->q))
{
if (first)
{
// If this is the first time, initialize the header and the question.
// This code needs to be here so that we can use the responseFlags from the
// cache record
mDNSOpaque16 responseFlags = SetResponseFlags(pc, cr->responseFlags);
InitializeDNSMessage(&m->omsg.h, pc->msgid, responseFlags);
ptr = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &pc->qname, pc->q.qtype, pc->q.qclass);
if (!ptr)
{
LogInfo("AddResourceRecords: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype));
return mDNSNULL;
}
first = mDNSfalse;
}
// - For NegativeAnswers there is nothing to add
// - If DNSSECOK is set, we also automatically lookup the RRSIGs which
// will also be returned. If the client is explicitly looking up
// a DNSSEC record (e.g., DNSKEY, DS) we should return the response.
// DNSSECOK bit only influences whether we add the RRSIG or not.
if (cr->resrec.RecordType != kDNSRecordTypePacketNegative)
{
LogInfo("AddResourceRecords: Answering question with %s", CRDisplayString(m, cr));
ttl = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond;
ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAnswers, &cr->resrec, ttl, limit);
if (!ptr)
{
*prevptr = orig;
return mDNSNULL;
}
len += (ptr - orig);
orig = ptr;
}
// If we have nsecs (wildcard expanded answer or negative response), add them
// in the additional section below if the DNSSECOK bit is set
if (pc->DNSSECOK && cr->nsec)
{
LogInfo("AddResourceRecords: nsec set for %s", CRDisplayString(m ,cr));
nsec = cr->nsec;
}
if (cr->soa)
{
LogInfo("AddResourceRecords: soa set for %s", CRDisplayString(m ,cr));
soa = cr->soa;
}
// If we are using CNAME to answer a question and CNAME is not the type we
// are looking for, note down the CNAME record so that we can follow them
// later. Before we follow the CNAME, print the RRSIGs and any nsec (wildcard
// expanded) if any.
if ((pc->q.qtype != cr->resrec.rrtype) && cr->resrec.rrtype == kDNSType_CNAME)
{
LogInfo("AddResourceRecords: cname set for %s", CRDisplayString(m ,cr));
cname = cr;
}
}
}
// Along with the nsec records, we also cache the SOA record. For non-DNSSEC question, we need
// to send the SOA back. Normally we either cache the SOA record (non-DNSSEC question) pointed
// to by "cr->soa" or the NSEC/SOA records along with their RRSIGs (DNSSEC question) pointed to
// by "cr->nsec". Two cases:
//
// - if we issue a DNSSEC question followed by non-DNSSEC question for the same name,
// we only have the nsec records and we need to filter the SOA record alone for the
// non-DNSSEC questions.
//
// - if we issue a non-DNSSEC question followed by DNSSEC question for the same name,
// the "core" flushes the cache entry and re-issue the question with EDNS0/DOK bit and
// in this case we return all the DNSSEC records we have.
for (; nsec; nsec = nsec->next)
{
if (!pc->DNSSECOK && DNSSECRecordType(nsec->resrec.rrtype))
continue;
LogInfo("AddResourceRecords:NSEC Answering question with %s", CRDisplayString(m, nsec));
ttl = nsec->resrec.rroriginalttl - (now - nsec->TimeRcvd) / mDNSPlatformOneSecond;
ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAuthorities, &nsec->resrec, ttl, limit);
if (!ptr)
{
*prevptr = orig;
return mDNSNULL;
}
len += (ptr - orig);
orig = ptr;
}
if (soa)
{
LogInfo("AddResourceRecords: SOA Answering question with %s", CRDisplayString(m, soa));
ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAuthorities, &soa->resrec, soa->resrec.rroriginalttl, limit);
if (!ptr)
{
*prevptr = orig;
return mDNSNULL;
}
len += (ptr - orig);
orig = ptr;
}
if (cname)
{
AssignDomainName(&pc->q.qname, &cname->resrec.rdata->u.name);
pc->q.qnamehash = DomainNameHashValue(&pc->q.qname);
goto again;
}
if (!ptr)
{
LogInfo("AddResourceRecords: Did not find any valid ResourceRecords");
*error = mStatus_NoSuchRecord;
return mDNSNULL;
}
if (pc->rcvBufSize)
{
ptr = AddEDNS0Option(m, ptr, limit);
if (!ptr)
{
*prevptr = orig;
return mDNSNULL;
}
len += (ptr - orig);
orig = ptr;
}
LogInfo("AddResourceRecord: Added %d bytes to the packet", len);
return ptr;
}
mDNSlocal void ProxyClientCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
{
DNSProxyClient *pc = question->QuestionContext;
DNSProxyClient **ppc = &DNSProxyClients;
mDNSu8 *ptr;
mDNSu8 *prevptr;
mStatus error;
if (!AddRecord)
return;
LogInfo("ProxyClientCallback: ResourceRecord %s", RRDisplayString(m, answer));
// We asked for validation and not timed out yet, then wait for the DNSSEC result.
// We have to set the AD bit in the response if it is secure which can't be done
// till we get the DNSSEC result back (indicated by QC_dnssec).
if (question->ValidationRequired)
{
mDNSs32 now;
mDNS_Lock(m);
now = m->timenow;
mDNS_Unlock(m);
if (((now - question->StopTime) < 0) && AddRecord != QC_dnssec)
{
LogInfo("ProxyClientCallback: No DNSSEC answer yet for Question %##s (%s), AddRecord %d, answer %s", question->qname.c,
DNSTypeName(question->qtype), AddRecord, RRDisplayString(m, answer));
return;
}
}
if (answer->RecordType != kDNSRecordTypePacketNegative)
{
if (answer->rrtype != question->qtype)
{
// Wait till we get called for the real response
LogInfo("ProxyClientCallback: Received %s, not answering yet", RRDisplayString(m, answer));
return;
}
}
ptr = AddResourceRecords(m, pc, &prevptr, &error);
if (!ptr)
{
LogInfo("ProxyClientCallback: AddResourceRecords NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype));
if (error == mStatus_NoError && prevptr)
{
// No space to add the record. Set the Truncate bit for UDP.
//
// TBD: For TCP, we need to send the rest of the data. But finding out what is left
// is harder. We should allocate enough buffer in the first place to send all
// of the data.
if (!pc->tcp)
{
m->omsg.h.flags.b[0] |= kDNSFlag0_TC;
ptr = prevptr;
}
else
{
LogInfo("ProxyClientCallback: ERROR!! Not enough space to return in TCP for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype));
ptr = prevptr;
}
}
else
{
mDNSOpaque16 flags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery, kDNSFlag1_RC_ServFail } };
// We could not find the record for some reason. Return a response, so that the client
// is not waiting forever.
LogInfo("ProxyClientCallback: No response");
if (!mDNSOpaque16IsZero(pc->q.responseFlags))
flags = pc->q.responseFlags;
InitializeDNSMessage(&m->omsg.h, pc->msgid, flags);
ptr = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &pc->qname, pc->q.qtype, pc->q.qclass);
if (!ptr)
{
LogInfo("ProxyClientCallback: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype));
goto done;
}
}
}
if (question->ValidationRequired)
{
if (question->ValidationState == DNSSECValDone && question->ValidationStatus == DNSSEC_Secure)
{
LogInfo("ProxyClientCallback: Setting AD bit for Question %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
m->omsg.h.flags.b[1] |= kDNSFlag1_AD;
}
else
{
// If some external resolver sets the AD bit and we did not validate the response securely, don't set
// the AD bit. It is possible that we did not see all the records that the upstream resolver saw or
// a buggy implementation somewhere.
if (m->omsg.h.flags.b[1] & kDNSFlag1_AD)
{
LogInfo("ProxyClientCallback: AD bit set in the response for response that was not validated locally %##s (%s)",
question->qname.c, DNSTypeName(question->qtype));
m->omsg.h.flags.b[1] &= ~kDNSFlag1_AD;
}
}
}
if (!pc->tcp)
{
mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, (UDPSocket *)pc->socket, &pc->addr, pc->port, mDNSNULL, mDNSNULL, mDNSfalse);
}
else
{
mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &pc->addr, pc->port, (TCPSocket *)pc->socket, mDNSNULL, mDNSfalse);
}
done:
mDNS_StopQuery(m, question);
while (*ppc && *ppc != pc)
ppc=&(*ppc)->next;
if (!*ppc)
{
LogMsg("ProxyClientCallback: question %##s (%s) not found", question->qname.c, DNSTypeName(question->qtype));
return;
}
*ppc = pc->next;
mDNSPlatformDisposeProxyContext(pc->context);
FreeDNSProxyClient(pc);
}
mDNSlocal void SendError(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *dstaddr,
const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSBool tcp, void *context, mDNSu8 rcode)
{
int pktlen = (int)(end - (mDNSu8 *)pkt);
DNSMessage *msg = (DNSMessage *)pkt;
(void) InterfaceID;
// RFC 1035 requires that we copy the question back and RFC 2136 is okay with sending nothing
// in the body or send back whatever we get for updates. It is easy to return whatever we get
// in the question back to the responder. We return as much as we can fit in our standard
// output packet.
if (pktlen > AbsoluteMaxDNSMessageData)
pktlen = AbsoluteMaxDNSMessageData;
mDNSPlatformMemCopy(&m->omsg.h, &msg->h, sizeof(DNSMessageHeader));
m->omsg.h.flags.b[0] |= kDNSFlag0_QR_Response;
m->omsg.h.flags.b[1] = rcode;
mDNSPlatformMemCopy(m->omsg.data, (mDNSu8 *)&msg->h.numQuestions, pktlen);
if (!tcp)
{
mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, mDNSInterface_Any, socket, dstaddr, dstport, mDNSNULL, mDNSNULL,
mDNSfalse);
}
else
{
mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, mDNSInterface_Any, mDNSNULL, dstaddr, dstport, (TCPSocket *)socket,
mDNSNULL, mDNSfalse);
}
mDNSPlatformDisposeProxyContext(context);
}
mDNSlocal DNSQuestion *IsDuplicateClient(const mDNS *const m, const mDNSAddr *const addr, const mDNSIPPort port, const mDNSOpaque16 id,
const DNSQuestion *const question)
{
DNSProxyClient *pc;
(void) m; // unused
for (pc = DNSProxyClients; pc; pc = pc->next)
{
if (mDNSSameAddress(&pc->addr, addr) &&
mDNSSameIPPort(pc->port, port) &&
mDNSSameOpaque16(pc->msgid, id) &&
pc->q.qtype == question->qtype &&
pc->q.qclass == question->qclass &&
SameDomainName(&pc->qname, &question->qname))
{
LogInfo("IsDuplicateClient: Found a duplicate client in the list");
return(&pc->q);
}
}
return(mDNSNULL);
}
mDNSlocal mDNSBool CheckDNSProxyIpIntf(const mDNS *const m, mDNSInterfaceID InterfaceID)
{
int i;
mDNSu32 ip_ifindex = (mDNSu32)(unsigned long)InterfaceID;
LogInfo("CheckDNSProxyIpIntf: Stored Input Interface List: [%d] [%d] [%d] [%d] [%d]", m->dp_ipintf[0], m->dp_ipintf[1], m->dp_ipintf[2],
m->dp_ipintf[3], m->dp_ipintf[4]);
for (i = 0; i < MaxIp; i++)
{
if (ip_ifindex == m->dp_ipintf[i])
return mDNStrue;
}
return mDNSfalse;
}
mDNSlocal void ProxyCallbackCommon(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr,
const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSBool tcp, void *context)
{
DNSMessage *msg = (DNSMessage *)pkt;
mDNSu8 QR_OP;
const mDNSu8 *ptr;
DNSQuestion q, *qptr;
DNSProxyClient *pc;
const mDNSu8 *optRR;
int optLen = 0;
DNSProxyClient **ppc = &DNSProxyClients;
(void) dstaddr;
(void) dstport;
debugf("ProxyCallbackCommon: DNS Query coming from InterfaceID %p", InterfaceID);
// Ignore if the DNS Query is not from a Valid Input InterfaceID
if (!CheckDNSProxyIpIntf(m, InterfaceID))
return;
if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader))
{
debugf("ProxyCallbackCommon: DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt);
return;
}
QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
if (QR_OP != kDNSFlag0_QR_Query)
{
LogInfo("ProxyCallbackCommon: Not a query(%d) for pkt from %#a:%d", QR_OP, srcaddr, mDNSVal16(srcport));
SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_NotImpl);
return;
}
// Read the integer parts which are in IETF byte-order (MSB first, LSB second)
ptr = (mDNSu8 *)&msg->h.numQuestions;
msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
if (msg->h.numQuestions != 1 || msg->h.numAnswers || msg->h.numAuthorities)
{
LogInfo("ProxyCallbackCommon: Malformed pkt from %#a:%d, Q:%d, An:%d, Au:%d", srcaddr, mDNSVal16(srcport),
msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities);
SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_FormErr);
return;
}
ptr = msg->data;
ptr = getQuestion(msg, ptr, end, InterfaceID, &q);
if (!ptr)
{
LogInfo("ProxyCallbackCommon: Question cannot be parsed for pkt from %#a:%d", srcaddr, mDNSVal16(srcport));
SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_FormErr);
return;
}
else
{
LogInfo("ProxyCallbackCommon: Question %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
}
ptr = LocateOptRR(msg, end, 0);
if (ptr)
{
optRR = ptr;
ptr = skipResourceRecord(msg, ptr, end);
// Be liberal and ignore the EDNS0 option if we can't parse it properly
if (!ptr)
{
LogInfo("ProxyCallbackCommon: EDNS0 cannot be parsed for pkt from %#a:%d, ignoring", srcaddr, mDNSVal16(srcport));
}
else
{
optLen = ptr - optRR;
LogInfo("ProxyCallbackCommon: EDNS0 opt length %d present in Question %##s (%s)", optLen, q.qname.c, DNSTypeName(q.qtype));
}
}
else
{
LogInfo("ProxyCallbackCommon: EDNS0 opt not present in Question %##s (%s), ptr %p", q.qname.c, DNSTypeName(q.qtype), ptr);
}
qptr = IsDuplicateClient(m, srcaddr, srcport, msg->h.id, &q);
if (qptr)
{
LogInfo("ProxyCallbackCommon: Found a duplicate for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport));
return;
}
pc = mDNSPlatformMemAllocate(sizeof(DNSProxyClient));
if (!pc)
{
LogMsg("ProxyCallbackCommon: Memory failure for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport));
return;
}
mDNSPlatformMemZero(pc, sizeof(DNSProxyClient));
pc->addr = *srcaddr;
pc->port = srcport;
pc->msgid = msg->h.id;
pc->interfaceID = InterfaceID; // input interface
pc->socket = socket;
pc->tcp = tcp;
pc->requestFlags = msg->h.flags;
pc->context = context;
AssignDomainName(&pc->qname, &q.qname);
if (optRR)
{
if (!ParseEDNS0(pc, optRR, optLen, end))
{
LogInfo("ProxyCallbackCommon: Invalid EDNS0 option for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport));
}
else
{
pc->optRR = mDNSPlatformMemAllocate(optLen);
if (!pc->optRR)
{
LogMsg("ProxyCallbackCommon: Memory failure for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport));
FreeDNSProxyClient(pc);
return;
}
mDNSPlatformMemCopy(pc->optRR, optRR, optLen);
pc->optLen = optLen;
}
}
debugf("ProxyCallbackCommon: DNS Query forwarding to interface index %d", m->dp_opintf);
mDNS_SetupQuestion(&pc->q, (mDNSInterfaceID)(unsigned long)m->dp_opintf, &q.qname, q.qtype, ProxyClientCallback, pc);
pc->q.TimeoutQuestion = 1;
// Even though we don't care about intermediate responses, set ReturnIntermed so that
// we get the negative responses
pc->q.ReturnIntermed = mDNStrue;
pc->q.ProxyQuestion = mDNStrue;
pc->q.ProxyDNSSECOK = pc->DNSSECOK;
pc->q.responseFlags = zeroID;
if (pc->DNSSECOK)
{
if (!(msg->h.flags.b[1] & kDNSFlag1_CD) && pc->q.qtype != kDNSType_RRSIG && pc->q.qtype != kDNSQType_ANY)
{
LogInfo("ProxyCallbackCommon: Setting Validation required bit for %#a:%d, validating %##s (%s)", srcaddr, mDNSVal16(srcport),
q.qname.c, DNSTypeName(q.qtype));
pc->q.ValidationRequired = DNSSEC_VALIDATION_SECURE;
}
else
{
LogInfo("ProxyCallbackCommon: CD bit not set OR not a valid type for %#a:%d, not validating %##s (%s)", srcaddr, mDNSVal16(srcport),
q.qname.c, DNSTypeName(q.qtype));
}
}
else
{
LogInfo("ProxyCallbackCommon: DNSSEC OK bit not set for %#a:%d, not validating %##s (%s)", srcaddr, mDNSVal16(srcport),
q.qname.c, DNSTypeName(q.qtype));
}
while (*ppc)
ppc = &((*ppc)->next);
*ppc = pc;
mDNS_StartQuery(m, &pc->q);
}
mDNSexport void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr,
const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context)
{
LogInfo("ProxyUDPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt);
ProxyCallbackCommon(m, socket, pkt, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNSfalse, context);
}
mDNSexport void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr,
const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context)
{
LogInfo("ProxyTCPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt);
// If the connection was closed from the other side, locate the client
// state and free it.
if ((end - (mDNSu8 *)pkt) == 0)
{
DNSProxyClient **ppc = &DNSProxyClients;
DNSProxyClient **prevpc;
prevpc = ppc;
while (*ppc && (*ppc)->socket != socket)
{
prevpc = ppc;
ppc=&(*ppc)->next;
}
if (!*ppc)
{
mDNSPlatformDisposeProxyContext(socket);
LogMsg("ProxyTCPCallback: socket cannot be found");
return;
}
*prevpc = (*ppc)->next;
LogInfo("ProxyTCPCallback: free");
mDNSPlatformDisposeProxyContext(socket);
FreeDNSProxyClient(*ppc);
return;
}
ProxyCallbackCommon(m, socket, pkt, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNStrue, context);
}
mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf)
{
int i;
// Store DNSProxy Interface fields in mDNS struct
for (i = 0; i < MaxIp; i++)
m->dp_ipintf[i] = IpIfArr[i];
m->dp_opintf = OpIf;
LogInfo("DNSProxyInit Storing interface list: Input [%d, %d, %d, %d, %d] Output [%d]", m->dp_ipintf[0],
m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf);
}
mDNSexport void DNSProxyTerminate(mDNS *const m)
{
int i;
// Clear DNSProxy Interface fields from mDNS struct
for (i = 0; i < MaxIp; i++)
m->dp_ipintf[i] = 0;
m->dp_opintf = 0;
LogInfo("DNSProxyTerminate Cleared interface list: Input [%d, %d, %d, %d, %d] Output [%d]", m->dp_ipintf[0],
m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf);
}
#else // UNICAST_DISABLED
mDNSexport void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context)
{
(void) m;
(void) socket;
(void) pkt;
(void) end;
(void) srcaddr;
(void) srcport;
(void) dstaddr;
(void) dstport;
(void) InterfaceID;
(void) context;
}
mDNSexport void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context)
{
(void) m;
(void) socket;
(void) pkt;
(void) end;
(void) srcaddr;
(void) srcport;
(void) dstaddr;
(void) dstport;
(void) InterfaceID;
(void) context;
}
mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf)
{
(void) m;
(void) IpIfArr;
(void) OpIf;
}
extern void DNSProxyTerminate(mDNS *const m)
{
(void) m;
}
#endif // UNICAST_DISABLED

View File

@ -0,0 +1,30 @@
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __DNS_PROXY_H
#define __DNS_PROXY_H
#include "mDNSEmbeddedAPI.h"
#include "DNSCommon.h"
extern void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr,
const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context);
extern void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr,
const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context);
extern void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf);
extern void DNSProxyTerminate(mDNS *const m);
#endif // __DNS_PROXY_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,157 @@
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __DNSSEC_H
#define __DNSSEC_H
#include "CryptoAlg.h"
#include "mDNSDebug.h"
typedef enum
{
RRVS_rr, RRVS_rrsig, RRVS_key, RRVS_rrsig_key, RRVS_ds, RRVS_done,
} RRVerifierSet;
typedef struct RRVerifier_struct RRVerifier;
typedef struct DNSSECVerifier_struct DNSSECVerifier;
typedef struct AuthChain_struct AuthChain;
typedef struct InsecureContext_struct InsecureContext;
struct RRVerifier_struct
{
RRVerifier *next;
mDNSu16 rrtype;
mDNSu16 rrclass;
mDNSu32 rroriginalttl;
mDNSu16 rdlength;
mDNSu16 found;
mDNSu32 namehash;
mDNSu32 rdatahash;
domainname name;
mDNSu8 *rdata;
};
// Each AuthChain element has one rrset (with multiple resource records of same type), rrsig and key
// that validates the rrset.
struct AuthChain_struct
{
AuthChain *next; // Next element in the chain
RRVerifier *rrset; // RRSET that is authenticated
RRVerifier *rrsig; // Signature for that RRSET
RRVerifier *key; // Public key for that RRSET
};
#define ResetAuthChain(dv) { \
(dv)->ac = mDNSNULL; \
(dv)->actail = &((dv)->ac); \
}
typedef void DNSSECVerifierCallback (mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status);
//
// When we do a validation for a question, there might be additional validations that needs to be done e.g.,
// wildcard expanded answer. It is also possible that in the case of nsec we need to prove both that a wildcard
// does not apply and the closest encloser proves that name does not exist. We identify these with the following
// flags.
//
// Note: In the following, by "marking the validation", we mean that as part of validation we need to prove
// the ones that are marked with.
//
// A wildcard may be used to answer a question. In that case, we need to verify that the right wildcard was
// used in answering the question. This is done by marking the validation with WILDCARD_PROVES_ANSWER_EXPANDED.
//
// Sometimes we get a NXDOMAIN response. In this case, we may have a wildcard where we need to prove
// that the wildcard proves that the name does not exist. This is done by marking the validation with
// WILDCARD_PROVES_NONAME_EXISTS.
//
// In the case of NODATA error, sometimes the name may exist but the query type does not exist. This is done by
// marking the validation with NSEC_PROVES_NOTYPE_EXISTS.
//
// In both NXDOMAIN and NODATA proofs, we may have to prove that the NAME does not exist. This is done by marking
// the validation with NSEC_PROVES_NONAME_EXISTS.
//
#define WILDCARD_PROVES_ANSWER_EXPANDED 0x00000001
#define WILDCARD_PROVES_NONAME_EXISTS 0x00000002
#define NSEC_PROVES_NOTYPE_EXISTS 0x00000004
#define NSEC_PROVES_NONAME_EXISTS 0x00000008
#define NSEC3_OPT_OUT 0x00000010 // OptOut was set in NSEC3
struct DNSSECVerifier_struct
{
domainname origName; // Original question name that needs verification
mDNSu16 origType; // Original question type corresponding to origName
mDNSu16 currQtype; // Current question type that is being verified
mDNSInterfaceID InterfaceID; // InterfaceID of the question
DNSQuestion q;
mDNSu8 recursed; // Number of times recursed during validation
mDNSu8 ValidationRequired; // Copy of the question's ValidationRequired status
mDNSu8 InsecureProofDone;
mDNSu8 NumPackets; // Number of packets that we send on the wire for DNSSEC verification.
mDNSs32 StartTime; // Time the DNSSEC verification starts
mDNSu32 flags;
RRVerifierSet next;
domainname *wildcardName; // set if the answer is wildcard expanded
RRVerifier *pendingNSEC;
DNSSECVerifierCallback *DVCallback;
DNSSECVerifier *parent;
RRVerifier *rrset; // rrset for which we have to verify
RRVerifier *rrsig; // RRSIG for rrset
RRVerifier *key; // DNSKEY for rrset
RRVerifier *rrsigKey; // RRSIG for DNSKEY
RRVerifier *ds; // DS for DNSKEY set in parent zone
AuthChain *saveac;
AuthChain *ac;
AuthChain **actail;
AlgContext *ctx;
};
struct InsecureContext_struct
{
DNSSECVerifier *dv; // dv for which we are doing the insecure proof
mDNSu8 skip; // labels to skip for forming the name from origName
DNSSECStatus status; // status to deliver when done
mDNSu8 triggerLabelCount; // Label count of the name that triggered the insecure proof
DNSQuestion q;
};
#define LogDNSSEC LogOperation
#define DNS_SERIAL_GT(a, b) ((int)((a) - (b)) > 0)
#define DNS_SERIAL_LT(a, b) ((int)((a) - (b)) < 0)
extern void StartDNSSECVerification(mDNS *const m, void *context);
extern RRVerifier* AllocateRRVerifier(const ResourceRecord *const rr, mStatus *status);
extern mStatus AddRRSetToVerifier(DNSSECVerifier *dv, const ResourceRecord *const rr, RRVerifier *rv, RRVerifierSet set);
extern void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q);
extern void FreeDNSSECVerifier(mDNS *const m, DNSSECVerifier *dv);
extern DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID,
mDNSu8 ValidationRequired, DNSSECVerifierCallback dvcallback, mDNSQuestionCallback qcallback);
extern void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname,
mDNSu16 qtype, mDNSQuestionCallback *callback, void *context);
extern void ValidateRRSIG(DNSSECVerifier *dv, RRVerifierSet type, const ResourceRecord *const rr);
extern void AuthChainLink(DNSSECVerifier *dv, AuthChain *ae);
extern mStatus DNSNameToLowerCase(domainname *d, domainname *result);
extern int DNSMemCmp(const mDNSu8 *const m1, const mDNSu8 *const m2, int len);
extern int DNSSECCanonicalOrder(const domainname *const d1, const domainname *const d2, int *subdomain);
extern void ProveInsecure(mDNS *const m, DNSSECVerifier *dv, InsecureContext *ic, domainname *trigger);
extern void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value);
extern char *DNSSECStatusName(DNSSECStatus status);
// DNSSECProbe belongs in DNSSECSupport.h but then we don't want to expose yet another plaform specific dnssec file
// to other platforms where dnssec is not supported.
extern void DNSSECProbe(mDNS *const m);
#endif // __DNSSEC_H

File diff suppressed because it is too large Load Diff

View File

@ -37,13 +37,13 @@
#define MDNS_CHECK_PRINTF_STYLE_FUNCTIONS 0
typedef enum
{
{
MDNS_LOG_MSG,
MDNS_LOG_OPERATION,
MDNS_LOG_SPS,
MDNS_LOG_INFO,
MDNS_LOG_DEBUG,
} mDNSLogLevel_t;
} mDNSLogLevel_t;
// Set this symbol to 1 to answer remote queries for our Address, reverse mapping PTR, and HINFO records
#define ANSWER_REMOTE_HOSTNAME_QUERIES 0
@ -64,7 +64,7 @@ typedef enum
#endif
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
// Variable argument macro support. Use ANSI C99 __VA_ARGS__ where possible. Otherwise, use the next best thing.
@ -94,32 +94,32 @@ typedef enum
#if (MDNS_HAS_VA_ARG_MACROS)
#if (MDNS_C99_VA_ARGS)
#define debug_noop( ... ) ((void)0)
#define LogMsg( ... ) LogMsgWithLevel(MDNS_LOG_MSG, __VA_ARGS__)
#define LogOperation( ... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, __VA_ARGS__); } while (0)
#define LogSPS( ... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, __VA_ARGS__); } while (0)
#define LogInfo( ... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, __VA_ARGS__); } while (0)
#define debug_noop(... ) ((void)0)
#define LogMsg(... ) LogMsgWithLevel(MDNS_LOG_MSG, __VA_ARGS__)
#define LogOperation(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, __VA_ARGS__);} while (0)
#define LogSPS(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, __VA_ARGS__);} while (0)
#define LogInfo(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, __VA_ARGS__);} while (0)
#elif (MDNS_GNU_VA_ARGS)
#define debug_noop( ARGS... ) ((void)0)
#define LogMsg( ARGS... ) LogMsgWithLevel(MDNS_LOG_MSG, ARGS)
#define LogOperation( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, ARGS); } while (0)
#define LogSPS( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, ARGS); } while (0)
#define LogInfo( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, ARGS); } while (0)
#define LogOperation( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, ARGS);} while (0)
#define LogSPS( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, ARGS);} while (0)
#define LogInfo( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, ARGS);} while (0)
#else
#error Unknown variadic macros
#endif
#else
// If your platform does not support variadic macros, you need to define the following variadic functions.
// See mDNSShared/mDNSDebug.c for sample implementation
// If your platform does not support variadic macros, you need to define the following variadic functions.
// See mDNSShared/mDNSDebug.c for sample implementation
#define debug_noop 1 ? (void)0 : (void)
#define LogMsg LogMsg_
#define LogOperation (mDNS_LoggingEnabled == 0) ? ((void)0) : LogOperation_
#define LogSPS (mDNS_LoggingEnabled == 0) ? ((void)0) : LogSPS_
#define LogInfo (mDNS_LoggingEnabled == 0) ? ((void)0) : LogInfo_
extern void LogMsg_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
extern void LogOperation_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
extern void LogSPS_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
extern void LogInfo_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
extern void LogMsg_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
extern void LogOperation_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
extern void LogSPS_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
extern void LogInfo_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
#endif
#if MDNS_DEBUGMSGS
@ -138,6 +138,8 @@ extern void verbosedebugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1
extern int mDNS_LoggingEnabled;
extern int mDNS_PacketLoggingEnabled;
extern int mDNS_McastLoggingEnabled;
extern int mDNS_McastTracingEnabled;
extern int mDNS_DebugMode; // If non-zero, LogMsg() writes to stderr instead of syslog
extern const char ProgramName[];
@ -158,7 +160,7 @@ extern void udns_validatelists(void *const v);
#endif
#ifdef __cplusplus
}
}
#endif
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,33 @@
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __NSEC_H
#define __NSEC_H
#include "dnssec.h"
extern mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode);
extern void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv);
extern void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *rr);
extern mDNSBool NSECAnswersDS(mDNS *const m, ResourceRecord *rr, DNSQuestion *q);
extern int CountLabelsMatch(const domainname *const d1, const domainname *const d2);
extern void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status);
extern void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNSSECVerifier *pdv, CacheRecord *ncr,
DNSSECVerifierCallback callback);
extern CacheRecord *NSECRecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype);
extern void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status);
#endif // __NSEC_H

View File

@ -0,0 +1,769 @@
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ***************************************************************************
// nsec3.c: This file contains support functions to validate NSEC3 records for
// NODATA and NXDOMAIN error.
// ***************************************************************************
#include "mDNSEmbeddedAPI.h"
#include "DNSCommon.h"
#include "CryptoAlg.h"
#include "nsec3.h"
#include "nsec.h"
// Define DNSSEC_DISABLED to remove all the DNSSEC functionality
// and use the stub functions implemented later in this file.
#ifndef DNSSEC_DISABLED
typedef enum
{
NSEC3ClosestEncloser,
NSEC3Covers,
NSEC3CEProof
} NSEC3FindValues;
//#define NSEC3_DEBUG 1
#if NSEC3_DEBUG
mDNSlocal void PrintHash(mDNSu8 *digest, int digestlen, char *buffer, int buflen)
{
int length = 0;
for (int j = 0; j < digestlen; j++)
{
length += mDNS_snprintf(buffer+length, buflen-length-1, "%x", digest[j]);
}
}
#endif
mDNSlocal mDNSBool NSEC3OptOut(CacheRecord *cr)
{
const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data;
rdataNSEC3 *nsec3 = (rdataNSEC3 *)rdb->data;
return (nsec3->flags & NSEC3_FLAGS_OPTOUT);
}
mDNSlocal int NSEC3SameName(const mDNSu8 *name, int namelen, const mDNSu8 *nsecName, int nsecLen)
{
int i;
// Note: With NSEC3, the lengths should always be same.
if (namelen != nsecLen)
{
LogMsg("NSEC3SameName: ERROR!! namelen %d, nsecLen %d", namelen, nsecLen);
return ((namelen < nsecLen) ? -1 : 1);
}
for (i = 0; i < namelen; i++)
{
mDNSu8 ac = *name++;
mDNSu8 bc = *nsecName++;
if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
if (ac != bc)
{
verbosedebugf("NSEC3SameName: returning ac %c, bc %c", ac, bc);
return ((ac < bc) ? -1 : 1);
}
}
return 0;
}
// Does the NSEC3 in "ncr" covers the "name" ?
// hashName is hash of the "name" and b32Name is the base32 encoded equivalent.
mDNSlocal mDNSBool NSEC3CoversName(mDNS *const m, CacheRecord *ncr, const mDNSu8 *hashName, int hashLen, const mDNSu8 *b32Name,
int b32len)
{
mDNSu8 *nxtName;
int nxtLength;
int ret, ret1, ret2;
const mDNSu8 b32nxtname[NSEC3_MAX_B32_LEN+1];
int b32nxtlen;
NSEC3Parse(&ncr->resrec, mDNSNULL, &nxtLength, &nxtName, mDNSNULL, mDNSNULL);
if (nxtLength != hashLen || ncr->resrec.name->c[0] != b32len)
return mDNSfalse;
// Compare the owner names and the "nxt" names.
//
// Owner name is base32 encoded and hence use the base32 encoded name b32name.
// nxt name is binary and hence use the binary value in hashName.
ret1 = NSEC3SameName(&ncr->resrec.name->c[1], ncr->resrec.name->c[0], b32Name, b32len);
ret2 = DNSMemCmp(nxtName, hashName, hashLen);
#if NSEC3_DEBUG
{
char nxtbuf1[50];
char nxtbuf2[50];
PrintHash(nxtName, nxtLength, nxtbuf1, sizeof(nxtbuf1));
PrintHash((mDNSu8 *)hashName, hashLen, nxtbuf2, sizeof(nxtbuf2));
LogMsg("NSEC3CoversName: Owner name %s, name %s", &ncr->resrec.name->c[1], b32Name);
LogMsg("NSEC3CoversName: Nxt hash name %s, name %s", nxtbuf1, nxtbuf2);
}
#endif
// "name" is greater than the owner name and smaller than nxtName. This also implies
// that nxtName > owner name implying that it is normal NSEC3.
if (ret1 < 0 && ret2 > 0)
{
LogDNSSEC("NSEC3CoversName: NSEC3 %s covers %s (Normal)", CRDisplayString(m, ncr), b32Name);
return mDNStrue;
}
// Need to compare the owner name and "nxt" to see if this is the last
// NSEC3 in the zone. Only the owner name is in base32 and hence we need to
// convert the nxtName to base32.
b32nxtlen = baseEncode((char *)b32nxtname, sizeof(b32nxtname), nxtName, nxtLength, ENC_BASE32);
if (!b32nxtlen)
{
LogDNSSEC("NSEC3CoversName: baseEncode of nxtName of %s failed", CRDisplayString(m, ncr));
return mDNSfalse;
}
if (b32len != b32nxtlen)
{
LogDNSSEC("NSEC3CoversName: baseEncode of nxtName for %s resulted in wrong length b32nxtlen %d, b32len %d",
CRDisplayString(m, ncr), b32len, b32nxtlen);
return mDNSfalse;
}
LogDNSSEC("NSEC3CoversName: Owner name %s, b32nxtname %s, ret1 %d, ret2 %d", &ncr->resrec.name->c[1], b32nxtname, ret1, ret2);
// If it is the last NSEC3 in the zone nxt < "name" and NSEC3SameName returns -1.
//
// - ret1 < 0 means "name > owner"
// - ret2 > 0 means "name < nxt"
//
// Note: We also handle the case of only NSEC3 in the zone where NSEC3SameName returns zero.
ret = NSEC3SameName(b32nxtname, b32nxtlen, &ncr->resrec.name->c[1], ncr->resrec.name->c[0]);
if (ret <= 0 &&
(ret1 < 0 || ret2 > 0))
{
LogDNSSEC("NSEC3CoversName: NSEC3 %s covers %s (Last), ret1 %d, ret2 %d", CRDisplayString(m, ncr), b32Name, ret1, ret2);
return mDNStrue;
}
return mDNSfalse;
}
// This function can be called with NSEC3ClosestEncloser, NSEC3Covers and NSEC3CEProof
//
// Passing in NSEC3ClosestEncloser means "find an exact match for the origName".
// Passing in NSEC3Covers means "find an NSEC3 that covers the origName".
//
// i.e., in both cases the nsec3 records are iterated to find the best match and returned.
// With NSEC3ClosestEncloser, as we are just looking for a name match, extra checks for
// the types being present or absent will not be checked.
//
// If NSEC3CEProof is passed, the name is tried as such first by iterating through all NSEC3s
// finding a ClosestEncloser or CloserEncloser and then one label skipped from the left and
// retried again till both the closest and closer encloser is found.
//
// ncr is the negative cache record that has the NSEC3 chain
// origName is the name for which we are trying to find the ClosestEncloser etc.
// closestEncloser and closerEncloser are the return values of the function
// ce is the closest encloser that will be returned if we find one
mDNSlocal mDNSBool NSEC3Find(mDNS *const m, NSEC3FindValues val, CacheRecord *ncr, domainname *origName, CacheRecord **closestEncloser,
CacheRecord **closerEncloser, const domainname **ce, mDNSu16 qtype)
{
int i;
int labelCount = CountLabels(origName);
CacheRecord *cr;
rdataNSEC3 *nsec3;
(void) qtype; // unused
// Pick the first NSEC for the iterations, salt etc.
for (cr = ncr->nsec; cr; cr = cr->next)
{
if (cr->resrec.rrtype == kDNSType_NSEC3)
{
const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data;
nsec3 = (rdataNSEC3 *)rdb->data;
break;
}
}
if (!cr)
{
LogMsg("NSEC3Find: cr NULL");
return mDNSfalse;
}
// Note: The steps defined in this function are for "NSEC3CEProof". As part of NSEC3CEProof,
// we need to find both the closestEncloser and closerEncloser which can also be found
// by passing NSEC3ClosestEncloser and NSEC3Covers respectively.
//
// Section 8.3 of RFC 5155.
// 1. Set SNAME=QNAME. Clear the flag.
//
// closerEncloser is the "flag". "name" below is SNAME.
if (closestEncloser)
{
*ce = mDNSNULL;
*closestEncloser = mDNSNULL;
}
if (closerEncloser)
*closerEncloser = mDNSNULL;
// If we are looking for a closestEncloser or a covering NSEC3, we don't have
// to truncate the name. For the give name, try to find the closest or closer
// encloser.
if (val != NSEC3CEProof)
{
labelCount = 0;
}
for (i = 0; i < labelCount + 1; i++)
{
int hlen;
const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
const domainname *name;
const mDNSu8 b32Name[NSEC3_MAX_B32_LEN+1];
int b32len;
name = SkipLeadingLabels(origName, i);
if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen))
{
LogMsg("NSEC3Find: NSEC3HashName failed for ##s", name->c);
continue;
}
b32len = baseEncode((char *)b32Name, sizeof(b32Name), (mDNSu8 *)hashName, hlen, ENC_BASE32);
if (!b32len)
{
LogMsg("NSEC3Find: baseEncode of name %##s failed", name->c);
continue;
}
for (cr = ncr->nsec; cr; cr = cr->next)
{
const domainname *nsecZone;
int result, subdomain;
if (cr->resrec.rrtype != kDNSType_NSEC3)
continue;
nsecZone = SkipLeadingLabels(cr->resrec.name, 1);
if (!nsecZone)
{
LogMsg("NSEC3Find: SkipLeadingLabel failed for %s, current name %##s",
CRDisplayString(m, cr), name->c);
continue;
}
// NSEC3 owner names are formed by hashing the owner name and then appending
// the zone name to it. If we skip the first label, the rest should be
// the zone name. See whether it is the subdomain of the name we are looking
// for.
result = DNSSECCanonicalOrder(origName, nsecZone, &subdomain);
// The check can't be a strict subdomain check. When NSEC3ClosestEncloser is
// passed in, there can be an exact match. If it is a subdomain or an exact
// match, we should continue with the proof.
if (!(subdomain || !result))
{
LogMsg("NSEC3Find: NSEC3 %s not a subdomain of %##s, result %d", CRDisplayString(m, cr),
origName->c, result);
continue;
}
// 2.1) If there is no NSEC3 RR in the response that matches SNAME
// (i.e., an NSEC3 RR whose owner name is the same as the hash of
// SNAME, prepended as a single label to the zone name), clear
// the flag.
//
// Note: We don't try to determine the actual zone name. We know that
// the labels following the hash (nsecZone) is the ancestor and we don't
// know where the zone cut is. Hence, we verify just the hash to be
// the same.
if (val == NSEC3ClosestEncloser || val == NSEC3CEProof)
{
if (!NSEC3SameName(&cr->resrec.name->c[1], cr->resrec.name->c[0], (const mDNSu8 *)b32Name, b32len))
{
int bmaplen;
mDNSu8 *bmap;
// For NSEC3ClosestEncloser, we are finding an exact match and
// "type" specific checks should be done by the caller.
if (val != NSEC3ClosestEncloser)
{
// DNAME bit must not be set and NS bit may be set only if SOA bit is set
NSEC3Parse(&cr->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
if (BitmapTypeCheck(bmap, bmaplen, kDNSType_DNAME))
{
LogDNSSEC("NSEC3Find: DNAME bit set in %s, ignoring", CRDisplayString(m, cr));
return mDNSfalse;
}
// This is the closest encloser and should come from the right zone.
if (BitmapTypeCheck(bmap, bmaplen, kDNSType_NS) &&
!BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA))
{
LogDNSSEC("NSEC3Find: NS bit set without SOA bit in %s, ignoring", CRDisplayString(m, cr));
return mDNSfalse;
}
}
LogDNSSEC("NSEC3Find: ClosestEncloser %s found for name %##s", CRDisplayString(m, cr), name->c);
if (closestEncloser)
{
*ce = name;
*closestEncloser = cr;
}
if (val == NSEC3ClosestEncloser)
return mDNStrue;
else
break;
}
}
if ((val == NSEC3Covers || val == NSEC3CEProof) && !(*closerEncloser))
{
if (NSEC3CoversName(m, cr, hashName, hlen, b32Name, b32len))
{
// 2.2) If there is an NSEC3 RR in the response that covers SNAME, set the flag.
if (closerEncloser)
*closerEncloser = cr;
if (val == NSEC3Covers)
return mDNStrue;
else
break;
}
}
}
// 2.3) If there is a matching NSEC3 RR in the response and the flag
// was set, then the proof is complete, and SNAME is the closest
// encloser.
if (val == NSEC3CEProof)
{
if (*closestEncloser && *closerEncloser)
{
LogDNSSEC("NSEC3Find: Found closest and closer encloser");
return mDNStrue;
}
// 2.4) If there is a matching NSEC3 RR in the response, but the flag
// is not set, then the response is bogus.
//
// Note: We don't have to wait till we finish trying all the names. If the matchName
// happens, we found the closest encloser which means we should have found the closer
// encloser before.
if (*closestEncloser && !(*closerEncloser))
{
LogDNSSEC("NSEC3Find: Found closest, but not closer encloser");
return mDNSfalse;
}
}
// 3. Truncate SNAME by one label from the left, go to step 2.
}
LogDNSSEC("NSEC3Find: Cannot find name %##s (%s)", origName->c, DNSTypeName(qtype));
return mDNSfalse;
}
mDNSlocal mDNSBool NSEC3ClosestEncloserProof(mDNS *const m, CacheRecord *ncr, domainname *name, CacheRecord **closestEncloser, CacheRecord **closerEncloser,
const domainname **ce, mDNSu16 qtype)
{
if (!NSEC3Find(m, NSEC3CEProof, ncr, name, closestEncloser, closerEncloser, ce, qtype))
{
LogDNSSEC("NSEC3ClosestEncloserProof: ERROR!! Cannot do closest encloser proof");
return mDNSfalse;
}
// Note: It is possible that closestEncloser and closerEncloser are the same.
if (!closestEncloser || !closerEncloser || !ce)
{
LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %p or CloserEncloser %p ce %p, something is NULL", *closestEncloser,
*closerEncloser, *ce);
return mDNSfalse;
}
// If the name exists, we should not have gotten the name error
if (SameDomainName((*ce), name))
{
LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %s same as origName %##s", CRDisplayString(m, *closestEncloser),
(*ce)->c);
return mDNSfalse;
}
return mDNStrue;
}
mDNSlocal mDNSBool VerifyNSEC3(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr, CacheRecord *closestEncloser,
CacheRecord *closerEncloser, CacheRecord *wildcard, DNSSECVerifierCallback callback)
{
mStatus status;
RRVerifier *r;
// We have three NSEC3s. If any of two are same, we should just prove one of them.
// This is just not an optimization; DNSSECNegativeValidationCB does not handle
// identical NSEC3s very well.
if (closestEncloser == closerEncloser)
closestEncloser = mDNSNULL;
if (closerEncloser == wildcard)
closerEncloser = mDNSNULL;
if (closestEncloser == wildcard)
closestEncloser = mDNSNULL;
dv->pendingNSEC = mDNSNULL;
if (closestEncloser)
{
r = AllocateRRVerifier(&closestEncloser->resrec, &status);
if (!r)
return mDNSfalse;
r->next = dv->pendingNSEC;
dv->pendingNSEC = r;
}
if (closerEncloser)
{
r = AllocateRRVerifier(&closerEncloser->resrec, &status);
if (!r)
return mDNSfalse;
r->next = dv->pendingNSEC;
dv->pendingNSEC = r;
}
if (wildcard)
{
r = AllocateRRVerifier(&wildcard->resrec, &status);
if (!r)
return mDNSfalse;
r->next = dv->pendingNSEC;
dv->pendingNSEC = r;
}
if (!dv->pendingNSEC)
{
LogMsg("VerifyNSEC3: ERROR!! pending NSEC null");
return mDNSfalse;
}
r = dv->pendingNSEC;
dv->pendingNSEC = r->next;
r->next = mDNSNULL;
LogDNSSEC("VerifyNSEC3: Verifying %##s (%s)", r->name.c, DNSTypeName(r->rrtype));
if (!dv->pendingNSEC)
VerifyNSEC(m, mDNSNULL, r, dv, ncr, mDNSNULL);
else
VerifyNSEC(m, mDNSNULL, r, dv, ncr, callback);
return mDNStrue;
}
mDNSexport void NSEC3NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
{
CacheRecord *closerEncloser;
CacheRecord *closestEncloser;
CacheRecord *wildcard;
const domainname *ce = mDNSNULL;
domainname wild;
if (!NSEC3ClosestEncloserProof(m, ncr, &dv->q.qname, &closestEncloser, &closerEncloser, &ce, dv->q.qtype))
{
goto error;
}
LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser %s, ce %##s", CRDisplayString(m, closestEncloser), ce->c);
LogDNSSEC("NSEC3NameErrorProof: CloserEncloser %s", CRDisplayString(m, closerEncloser));
// *.closestEncloser should be covered by some nsec3 which would then prove
// that the wildcard does not exist
wild.c[0] = 1;
wild.c[1] = '*';
wild.c[2] = 0;
if (!AppendDomainName(&wild, ce))
{
LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce->c);
goto error;
}
if (!NSEC3Find(m, NSEC3Covers, ncr, &wild, mDNSNULL, &wildcard, mDNSNULL, dv->q.qtype))
{
LogMsg("NSEC3NameErrorProof: Cannot find encloser for wildcard");
goto error;
}
else
{
LogDNSSEC("NSEC3NameErrorProof: Wildcard %##s covered by %s", wild.c, CRDisplayString(m, wildcard));
if (wildcard == closestEncloser)
{
LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser matching Wildcard %s", CRDisplayString(m, wildcard));
}
}
if (NSEC3OptOut(closerEncloser))
{
dv->flags |= NSEC3_OPT_OUT;
}
if (!VerifyNSEC3(m, dv, ncr, closestEncloser, closerEncloser, wildcard, NameErrorNSECCallback))
goto error;
else
return;
error:
dv->DVCallback(m, dv, DNSSEC_Bogus);
}
// Section 8.5, 8.6 of RFC 5155 first paragraph
mDNSlocal mDNSBool NSEC3NoDataError(mDNS *const m, CacheRecord *ncr, domainname *name, mDNSu16 qtype, CacheRecord **closestEncloser)
{
const domainname *ce = mDNSNULL;
*closestEncloser = mDNSNULL;
// Note: This also covers ENT in which case the bitmap is empty
if (NSEC3Find(m, NSEC3ClosestEncloser, ncr, name, closestEncloser, mDNSNULL, &ce, qtype))
{
int bmaplen;
mDNSu8 *bmap;
mDNSBool ns, soa;
NSEC3Parse(&(*closestEncloser)->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
if (BitmapTypeCheck(bmap, bmaplen, qtype) || BitmapTypeCheck(bmap, bmaplen, kDNSType_CNAME))
{
LogMsg("NSEC3NoDataError: qtype %s exists in %s", DNSTypeName(qtype), CRDisplayString(m, *closestEncloser));
return mDNSfalse;
}
ns = BitmapTypeCheck(bmap, bmaplen, kDNSType_NS);
soa = BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA);
if (qtype != kDNSType_DS)
{
// For non-DS type questions, we don't want to use the parent side records to
// answer it
if (ns && !soa)
{
LogDNSSEC("NSEC3NoDataError: Parent side NSEC %s, can't use for child qname %##s (%s)",
CRDisplayString(m, *closestEncloser), name->c, DNSTypeName(qtype));
return mDNSfalse;
}
}
else
{
if (soa)
{
LogDNSSEC("NSEC3NoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)",
CRDisplayString(m, *closestEncloser), name->c, DNSTypeName(qtype));
return mDNSfalse;
}
}
LogDNSSEC("NSEC3NoDataError: Name -%##s- exists, but qtype %s does not exist in %s", name->c, DNSTypeName(qtype), CRDisplayString(m, *closestEncloser));
return mDNStrue;
}
return mDNSfalse;
}
mDNSexport void NSEC3NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
{
CacheRecord *closerEncloser = mDNSNULL;
CacheRecord *closestEncloser = mDNSNULL;
CacheRecord *wildcard = mDNSNULL;
const domainname *ce = mDNSNULL;
domainname wild;
// Section 8.5, 8.6 of RFC 5155
if (NSEC3NoDataError(m, ncr, &dv->q.qname, dv->q.qtype, &closestEncloser))
{
goto verify;
}
// Section 8.6, 8.7: if we can't find the NSEC3 RR, verify the closest encloser proof
// for QNAME and the "next closer" should have the opt out
if (!NSEC3ClosestEncloserProof(m, ncr, &dv->q.qname, &closestEncloser, &closerEncloser, &ce, dv->q.qtype))
{
goto error;
}
// Section 8.7: find a matching NSEC3 for *.closestEncloser
wild.c[0] = 1;
wild.c[1] = '*';
wild.c[2] = 0;
if (!AppendDomainName(&wild, ce))
{
LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce->c);
goto error;
}
if (!NSEC3Find(m, NSEC3ClosestEncloser, ncr, &wild, &wildcard, mDNSNULL, &ce, dv->q.qtype))
{
// Not a wild card case. Section 8.6 second para applies.
LogDNSSEC("NSEC3NoDataProof: Cannot find encloser for wildcard, perhaps not a wildcard case");
if (!NSEC3OptOut(closerEncloser))
{
LogDNSSEC("NSEC3DataProof: opt out not set for %##s (%s), bogus", dv->q.qname.c, DNSTypeName(dv->q.qtype));
goto error;
}
LogDNSSEC("NSEC3DataProof: opt out set, proof complete for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
dv->flags |= NSEC3_OPT_OUT;
}
else
{
int bmaplen;
mDNSu8 *bmap;
NSEC3Parse(&wildcard->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
if (BitmapTypeCheck(bmap, bmaplen, dv->q.qtype) || BitmapTypeCheck(bmap, bmaplen, kDNSType_CNAME))
{
LogDNSSEC("NSEC3NoDataProof: qtype %s exists in %s", DNSTypeName(dv->q.qtype), CRDisplayString(m, wildcard));
goto error;
}
if (dv->q.qtype == kDNSType_DS && BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA))
{
LogDNSSEC("NSEC3NoDataProof: Child side wildcard NSEC3 %s, can't use for parent qname %##s (%s)",
CRDisplayString(m, wildcard), dv->q.qname.c, DNSTypeName(dv->q.qtype));
goto error;
}
else if (dv->q.qtype != kDNSType_DS && !BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA) &&
BitmapTypeCheck(bmap, bmaplen, kDNSType_NS))
{
// Don't use the parent side record for this
LogDNSSEC("NSEC3NoDataProof: Parent side wildcard NSEC3 %s, can't use for child qname %##s (%s)",
CRDisplayString(m, wildcard), dv->q.qname.c, DNSTypeName(dv->q.qtype));
goto error;
}
LogDNSSEC("NSEC3NoDataProof: Wildcard %##s matched by %s", wild.c, CRDisplayString(m, wildcard));
}
verify:
if (!VerifyNSEC3(m, dv, ncr, closestEncloser, closerEncloser, wildcard, NoDataNSECCallback))
goto error;
else
return;
error:
dv->DVCallback(m, dv, DNSSEC_Bogus);
}
mDNSexport mDNSBool NSEC3WildcardAnswerProof(mDNS *const m, CacheRecord *ncr, DNSSECVerifier *dv)
{
int skip;
const domainname *nc;
CacheRecord *closerEncloser;
(void) m;
// Find the next closer name and prove that it is covered by the NSEC3
skip = CountLabels(&dv->origName) - CountLabels(dv->wildcardName) - 1;
if (skip)
nc = SkipLeadingLabels(&dv->origName, skip);
else
nc = &dv->origName;
LogDNSSEC("NSEC3WildcardAnswerProof: wildcard name %##s", nc->c);
if (!NSEC3Find(m, NSEC3Covers, ncr, (domainname *)nc, mDNSNULL, &closerEncloser, mDNSNULL, dv->q.qtype))
{
LogMsg("NSEC3WildcardAnswerProof: Cannot find closer encloser");
return mDNSfalse;
}
if (!closerEncloser)
{
LogMsg("NSEC3WildcardAnswerProof: closerEncloser NULL");
return mDNSfalse;
}
if (NSEC3OptOut(closerEncloser))
{
dv->flags |= NSEC3_OPT_OUT;
}
// NSEC3 Verification is done by the caller
return mDNStrue;
}
mDNSexport CacheRecord *NSEC3RecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype)
{
CacheGroup *cg;
CacheRecord *cr;
CacheRecord *ncr;
mDNSu32 slot, namehash;
slot = HashSlot(name);
namehash = DomainNameHashValue(name);
cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, name);
if (!cg)
{
LogDNSSEC("NSEC3RecordForName: cg NULL for %##s", name);
return mDNSNULL;
}
for (ncr = cg->members; ncr; ncr = ncr->next)
{
if (ncr->resrec.RecordType != kDNSRecordTypePacketNegative ||
ncr->resrec.rrtype != qtype)
{
continue;
}
for (cr = ncr->nsec; cr; cr = cr->next)
{
int hlen, b32len;
const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
const mDNSu8 b32Name[NSEC3_MAX_B32_LEN+1];
const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data;
rdataNSEC3 *nsec3;
if (cr->resrec.rrtype != kDNSType_NSEC3)
continue;
nsec3 = (rdataNSEC3 *)rdb->data;
if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen))
{
LogMsg("NSEC3RecordIsDelegation: NSEC3HashName failed for ##s", name->c);
return mDNSNULL;
}
b32len = baseEncode((char *)b32Name, sizeof(b32Name), (mDNSu8 *)hashName, hlen, ENC_BASE32);
if (!b32len)
{
LogMsg("NSEC3RecordIsDelegation: baseEncode of name %##s failed", name->c);
return mDNSNULL;
}
// Section 2.3 of RFC 4035 states that:
//
// Each owner name in the zone that has authoritative data or a delegation point NS RRset MUST
// have an NSEC resource record.
//
// This applies to NSEC3 record. So, if we have an NSEC3 record matching the question name with the
// NS bit set, then this is a delegation.
//
if (!NSEC3SameName(&cr->resrec.name->c[1], cr->resrec.name->c[0], (const mDNSu8 *)b32Name, b32len))
{
int bmaplen;
mDNSu8 *bmap;
LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s matches name %##s, b32name %s", CRDisplayString(m, cr), name->c, b32Name);
NSEC3Parse(&cr->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
// See the Insecure Delegation Proof section in dnssec-bis: DS bit and SOA bit
// should be absent
if (BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA) ||
BitmapTypeCheck(bmap, bmaplen, kDNSType_DS))
{
LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s has DS or SOA bit set, ignoring", CRDisplayString(m, cr));
return mDNSNULL;
}
if (BitmapTypeCheck(bmap, bmaplen, kDNSType_NS))
return cr;
else
return mDNSNULL;
}
// If opt-out is not set, then it does not cover any delegations
if (!(nsec3->flags & NSEC3_FLAGS_OPTOUT))
continue;
// Opt-out allows insecure delegations to exist without the NSEC3 RR at the
// hashed owner name (see RFC 5155 section 6.0).
if (NSEC3CoversName(m, cr, hashName, hlen, b32Name, b32len))
{
LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s covers name %##s with optout", CRDisplayString(m, cr), name->c);
return cr;
}
}
}
return mDNSNULL;
}
#else // !DNSSEC_DISABLED
#endif // !DNSSEC_DISABLED

View File

@ -0,0 +1,28 @@
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __NSEC3_H
#define __NSEC3_H
#include "dnssec.h"
extern mDNSBool NSEC3WildcardAnswerProof(mDNS *const m, CacheRecord *ncr, DNSSECVerifier *dv);
extern void NSEC3NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr);
extern void NSEC3NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr);
extern CacheRecord *NSEC3RecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype);
#endif // __NSEC3_H

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
* Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@
#include "DNSCommon.h"
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
#define RESTART_GOODBYE_DELAY (6 * mDNSPlatformOneSecond) // delay after restarting LLQ before nuking previous known answers (avoids flutter if we restart before we have networking up)
@ -32,15 +32,24 @@
//#define MAX_UCAST_POLL_INTERVAL (1 * 60 * mDNSPlatformOneSecond)
#define LLQ_POLL_INTERVAL (15 * 60 * mDNSPlatformOneSecond) // Polling interval for zones w/ an advertised LLQ port (ie not static zones) if LLQ fails due to NAT, etc.
#define RESPONSE_WINDOW (60 * mDNSPlatformOneSecond) // require server responses within one minute of request
#define MAX_UCAST_UNANSWERED_QUERIES 2 // the number of unanswered queries from any one uDNS server before trying another server
#define MAX_DNSSEC_UNANSWERED_QUERIES 1 // number of unanswered queries from any one uDNS server before turning off DNSSEC Validation
#define MAX_UCAST_UNANSWERED_QUERIES 2 // number of unanswered queries from any one uDNS server before trying another server
#define DNSSERVER_PENALTY_TIME (60 * mDNSPlatformOneSecond) // number of seconds for which new questions don't pick this server
// On some interfaces, we want to delay the first retransmission to a minimum of 2 seconds
// rather than the default (1 second).
#define MIN_UCAST_RETRANS_TIMEOUT (2 * mDNSPlatformOneSecond)
#define DEFAULT_UPDATE_LEASE 7200
#define QuestionIntervalStep 3
#define QuestionIntervalStep2 (QuestionIntervalStep*QuestionIntervalStep)
#define QuestionIntervalStep3 (QuestionIntervalStep*QuestionIntervalStep*QuestionIntervalStep)
#define InitialQuestionInterval ((mDNSPlatformOneSecond + QuestionIntervalStep-1) / QuestionIntervalStep)
#define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
// just move to MaxQuestionInterval once over this threshold
#define QuestionIntervalThreshold (QuestionIntervalStep3 * mDNSPlatformOneSecond)
// For Unicast record registrations, we initialize the interval to 1 second. When we send any query for
// the record registration e.g., GetZoneData, we always back off by QuestionIntervalStep
@ -58,6 +67,15 @@
// then use the default value of 30 seconds
#define DEFAULT_UDNS_TIMEOUT 30 // in seconds
// For questions that are validating responses (q->ValidatingResponse == 1), use 10 seconds
// which accomodates two DNS servers and two queries per DNS server.
#define DEFAULT_UDNSSEC_TIMEOUT 10 // in seconds
// If we are sending queries with EDNS0/DO option and we have no indications that the server
// is DNSSEC aware and we have already reached MAX_DNSSEC_RETRANSMISSIONS, we disable
// validation (for optional case only) for any questions that uses this server
#define MAX_DNSSEC_RETRANSMISSIONS 3
// Entry points into unicast-specific routines
extern void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo);
@ -75,7 +93,6 @@ extern void SleepRecordRegistrations(mDNS *m);
extern mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr);
extern void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q);
extern CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name);
extern mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr);
extern mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt);
extern mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question);
@ -97,23 +114,25 @@ extern void CheckNATMappings(mDNS *m);
extern mStatus uDNS_SetupDNSConfig(mDNS *const m);
// uDNS_SetupSearchDomains by default adds search domains. It also can be called with one or
// more values for "action" which does the following:
//
// -UDNS_START_WAB_QUERY - start Wide Area Bonjour (domain enumeration) queries
// uDNS_SetupWABQueries reads search domains from the platform layer and starts the Wide Area Bonjour
// (WAB) domain enumeration queries if necessary.
#define UDNS_START_WAB_QUERY 0x00000001
#define UDNS_WAB_BROWSE_QUERY 0x00000001 // Browse queries (b, db)
#define UDNS_WAB_LBROWSE_QUERY 0x00000002 // Browse queries (lb)
#define UDNS_WAB_REG_QUERY 0x00000004 // Registration queries (r and dr)
extern mStatus uDNS_SetupSearchDomains(mDNS *const m, int action);
extern void uDNS_SetupWABQueries(mDNS *const m);
extern void uDNS_StartWABQueries(mDNS *const m, int queryType);
extern void uDNS_StopWABQueries(mDNS *const m, int queryType);
extern domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal);
typedef enum
{
{
uDNS_LLQ_Not = 0, // Normal uDNS answer: Flush any stale records from cache, and respect record TTL
uDNS_LLQ_Ignore, // LLQ initial challenge packet: ignore -- has no useful records for us
uDNS_LLQ_Entire, // LLQ initial set of answers: Flush any stale records from cache, but assume TTL is 2 x LLQ refresh interval
uDNS_LLQ_Events // LLQ event packet: don't flush cache; assume TTL is 2 x LLQ refresh interval
} uDNS_LLQType;
} uDNS_LLQType;
extern uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, DNSQuestion **matchQuestion);
extern DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name);
@ -121,12 +140,12 @@ extern DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const
extern void DisposeTCPConn(struct tcpInfo_t *tcp);
// NAT traversal
extern void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len); // Called for each received NAT-PMP packet
extern void uDNS_ReceiveNATPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len); // Called for each received PCP or NAT-PMP packet
extern void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr);
extern void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease);
extern void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease, NATTProtocol protocol);
#ifdef __cplusplus
}
}
#endif
#endif // __UDNS_H_

View File

@ -47,13 +47,8 @@ extern int daemon(int, int);
#include "mDNSPosix.h"
#include "mDNSUNP.h" // For daemon()
#include "uds_daemon.h"
#include "DNSCommon.h"
#include "PlatformCommon.h"
#ifndef MDNSD_USER
#define MDNSD_USER "nobody"
#endif
#define CONFIG_FILE "/etc/mdnsd.conf"
static domainname DynDNSZone; // Default wide-area zone for service registration
static domainname DynDNSHostname;
@ -63,7 +58,7 @@ static CacheEntity gRRCache[RR_CACHE_SIZE];
static mDNS_PlatformSupport PlatformStorage;
mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
{
{
(void)m; // Unused
if (result == mStatus_NoError)
{
@ -83,31 +78,29 @@ mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
CacheEntity *storage = malloc(sizeof(CacheEntity) * RR_CACHE_SIZE);
if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE);
}
}
}
// %%% Reconfigure() probably belongs in the platform support layer (mDNSPosix.c), not the daemon cde
// -- all client layers running on top of mDNSPosix.c need to handle network configuration changes,
// not only the Unix Domain Socket Daemon
static void Reconfigure(mDNS *m)
{
{
mDNSAddr DynDNSIP;
const mDNSAddr dummy = { mDNSAddrType_IPv4, { { { 1, 1, 1, 1 } } } };;
mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL);
mDNS_Lock(m);
if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0)
LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable");
mDNS_Unlock(m);
ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone, NULL);
mDNSPlatformSourceAddrForDest(&DynDNSIP, &dummy);
if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL);
if (DynDNSIP.type) mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL, NULL);
mDNS_ConfigChanged(m);
}
}
// Do appropriate things at startup with command line arguments. Calls exit() if unhappy.
mDNSlocal void ParseCmdLinArgs(int argc, char **argv)
{
{
if (argc > 1)
{
if (0 == strcmp(argv[1], "-debug")) mDNS_DebugMode = mDNStrue;
@ -123,60 +116,24 @@ mDNSlocal void ParseCmdLinArgs(int argc, char **argv)
exit(-1);
#endif
}
}
}
mDNSlocal void DumpStateLog(mDNS *const m)
// Dump a little log of what we've been up to.
{
DNSServer *s;
PosixNetworkInterface *i;
{
LogMsg("---- BEGIN STATE LOG ----");
udsserver_info(m);
LogMsgNoIdent("----- Network Interfaces -------");
for (i = (PosixNetworkInterface*)(m->HostInterfaces);
i; i = (PosixNetworkInterface *)(i->coreIntf.next)) {
LogMsg("%p %p %d %s%s%s%s%s %-8s %#a", i,
(void *)(i->coreIntf.InterfaceID), i->index,
i->coreIntf.InterfaceActive ? "-" : "D",
i->coreIntf.IPv4Available ? "4" : "-",
i->coreIntf.IPv6Available ? "6" : "-",
i->coreIntf.Advertise ? "A" : "-",
i->coreIntf.McastTxRx ? "M" : "-",
i->intfName, &(i->coreIntf.ip));
}
LogMsgNoIdent("--------- DNS Servers ----------");
if (!mDNSStorage.DNSServers) LogMsgNoIdent("<None>");
else
{
for (s = m->DNSServers; s; s = s->next)
{
LogMsgNoIdent("DNS Server %##s %#a:%d %s",
s->domain.c, &s->addr, mDNSVal16(s->port),
s->teststate == DNSServer_Untested ? "(Untested)" :
s->teststate == DNSServer_Passed ? "" :
s->teststate == DNSServer_Failed ? "(Failed)" :
s->teststate == DNSServer_Disabled ? "(Disabled)" : "(Unknown state)");
}
}
LogMsg("---- END STATE LOG ----");
}
}
mDNSlocal mStatus MainLoop(mDNS *m) // Loop until we quit.
{
{
sigset_t signals;
mDNSBool gotData = mDNSfalse;
mDNSPosixListenForSignalInEventLoop(SIGINT);
mDNSPosixListenForSignalInEventLoop(SIGTERM);
mDNSPosixListenForSignalInEventLoop(SIGUSR1);
#ifdef HAVE_SIGINFO
mDNSPosixListenForSignalInEventLoop(SIGUSR2);
mDNSPosixListenForSignalInEventLoop(SIGINFO);
#endif
mDNSPosixListenForSignalInEventLoop(SIGPIPE);
mDNSPosixListenForSignalInEventLoop(SIGHUP) ;
@ -203,31 +160,16 @@ mDNSlocal mStatus MainLoop(mDNS *m) // Loop until we quit.
(void) mDNSPosixRunEventLoopOnce(m, &timeout, &signals, &gotData);
if (sigismember(&signals, SIGHUP )) Reconfigure(m);
#ifdef HAVE_SIGINFO
/* use OSX-compatible signals since we can, and gain enhanced debugging */
if (sigismember(&signals, SIGINFO)) DumpStateLog(m);
if (sigismember(&signals, SIGUSR1))
{
mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1;
LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled");
}
if (sigismember(&signals, SIGUSR2))
{
mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1;
LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled");
}
#else
if (sigismember(&signals, SIGUSR1)) DumpStateLog(m);
#endif
// SIGPIPE happens when we try to write to a dead client; death should be detected soon in request_callback() and cleaned up.
if (sigismember(&signals, SIGPIPE)) LogMsg("Received SIGPIPE - ignoring");
if (sigismember(&signals, SIGINT) || sigismember(&signals, SIGTERM)) break;
}
return EINTR;
}
}
int main(int argc, char **argv)
{
{
mStatus err;
ParseCmdLinArgs(argc, argv);
@ -245,21 +187,11 @@ int main(int argc, char **argv)
// Now that we're finished with anything privileged, switch over to running as "nobody"
if (mStatus_NoError == err)
{
const struct passwd *pw = getpwnam(MDNSD_USER);
const struct passwd *pw = getpwnam("nobody");
if (pw != NULL)
{
setgid(pw->pw_gid);
setuid(pw->pw_uid);
}
else
#ifdef MDNSD_NOROOT
{
LogMsg("WARNING: mdnsd exiting because user \""MDNSD_USER"\" does not exist");
err = mStatus_Invalid;
}
#else
LogMsg("WARNING: mdnsd continuing as root because user \""MDNSD_USER"\" does not exist");
#endif
LogMsg("WARNING: mdnsd continuing as root because user \"nobody\" does not exist");
}
if (mStatus_NoError == err)
@ -277,50 +209,50 @@ int main(int argc, char **argv)
#endif
return err;
}
}
// uds_daemon support ////////////////////////////////////////////////////////////
mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context, void **platform_data)
/* Support routine for uds_daemon.c */
{
{
// Depends on the fact that udsEventCallback == mDNSPosixEventCallback
(void) platform_data;
return mDNSPosixAddFDToEventLoop(fd, callback, context);
}
}
int udsSupportReadFD(dnssd_sock_t fd, char *buf, int len, int flags, void *platform_data)
{
{
(void) platform_data;
return recv(fd, buf, len, flags);
}
}
mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data) // Note: This also CLOSES the file descriptor
{
{
mStatus err = mDNSPosixRemoveFDFromEventLoop(fd);
(void) platform_data;
close(fd);
return err;
}
}
mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay)
{
{
(void)m;
(void)delay;
// No-op, for now
}
}
#if _BUILDING_XCODE_PROJECT_
// If the process crashes, then this string will be magically included in the automatically-generated crash log
const char *__crashreporter_info__ = mDNSResponderVersionString_SCCS + 5;
asm(".desc ___crashreporter_info__, 0x10");
asm (".desc ___crashreporter_info__, 0x10");
#endif
// For convenience when using the "strings" command, this is the last thing in the file
#if mDNSResponderVersion > 1
mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder-" STRINGIFY(mDNSResponderVersion);
mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder-" STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
#elif MDNS_VERSIONSTR_NODTS
mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build)";
#else
mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build)";
mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build) (" __DATE__ " " __TIME__ ")";
#endif

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,7 @@
#include <sys/time.h>
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
// PosixNetworkInterface is a record extension of the core NetworkInterfaceInfo
@ -34,8 +34,9 @@
typedef struct PosixNetworkInterface PosixNetworkInterface;
struct PosixNetworkInterface
{
NetworkInterfaceInfo coreIntf;
{
NetworkInterfaceInfo coreIntf; // MUST be the first element in this structure
mDNSs32 LastSeen;
const char * intfName;
PosixNetworkInterface * aliasIntf;
int index;
@ -43,23 +44,23 @@ struct PosixNetworkInterface
#if HAVE_IPV6
int multicastSocket6;
#endif
};
};
// This is a global because debugf_() needs to be able to check its value
extern int gMDNSPlatformPosixVerboseLevel;
struct mDNS_PlatformSupport_struct
{
{
int unicastSocket4;
#if HAVE_IPV6
int unicastSocket6;
#endif
};
};
#define uDNS_SERVERS_FILE "/etc/resolv.conf"
extern int ParseDNSServers(mDNS *m, const char *filePath);
extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m);
// See comment in implementation.
// See comment in implementation.
// Call mDNSPosixGetFDSet before calling select(), to update the parameters
// as may be necessary to meet the needs of the mDNSCore code.
@ -79,7 +80,7 @@ extern mStatus mDNSPosixIgnoreSignalInEventLoop( int signum);
extern mStatus mDNSPosixRunEventLoopOnce( mDNS *m, const struct timeval *pTimeout, sigset_t *pSignalsReceived, mDNSBool *pDataDispatched);
#ifdef __cplusplus
}
}
#endif
#endif

View File

@ -31,7 +31,7 @@
macro, usually defined in <sys/param.h> or someplace like that, to make sure the
CMSG_NXTHDR macro is well-formed. On such platforms, the symbol NEED_ALIGN_MACRO
should be set to the name of the header to include to get the ALIGN(P) macro.
*/
*/
#ifdef NEED_ALIGN_MACRO
#include NEED_ALIGN_MACRO
#endif
@ -40,7 +40,7 @@
other platforms don't even have that include file. So,
if we haven't yet got a definition, let's try to find
<sys/sockio.h>.
*/
*/
#ifndef SIOCGIFCONF
#include <sys/sockio.h>
@ -48,16 +48,14 @@
/* sockaddr_dl is only referenced if we're using IP_RECVIF,
so only include the header in that case.
*/
*/
#ifdef IP_RECVIF
#include <net/if_dl.h>
#endif
#if defined(AF_INET6) && HAVE_IPV6 && !HAVE_LINUX && !defined(sun)
#if defined(__FreeBSD__) || defined(__DragonFly__)
#if defined(AF_INET6) && HAVE_IPV6 && !HAVE_LINUX
#include <net/if_var.h>
#endif
#include <netinet/in_var.h>
// Note: netinet/in_var.h implicitly includes netinet6/in6_var.h for us
#endif
@ -71,7 +69,7 @@ void plen_to_mask(int plen, char *addr) {
int i;
int colons=7; /* Number of colons in IPv6 address */
int bits_in_block=16; /* Bits per IPv6 block */
for(i=0;i<=colons;i++) {
for(i=0; i<=colons; i++) {
int block, ones=0xffff, ones_in_block;
if (plen>bits_in_block) ones_in_block=bits_in_block;
else ones_in_block=plen;
@ -79,12 +77,12 @@ void plen_to_mask(int plen, char *addr) {
i==0 ? sprintf(addr, "%x", block) : sprintf(addr, "%s:%x", addr, block);
plen -= ones_in_block;
}
}
}
/* Gets IPv6 interface information from the /proc filesystem in linux*/
struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases)
{
struct ifi_info *ifi, *ifihead, **ifipnext;
{
struct ifi_info *ifi, *ifihead, **ifipnext, *ifipold, **ifiptr;
FILE *fp;
char addr[8][5];
int flags, myflags, index, plen, scope;
@ -94,6 +92,8 @@ struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases)
struct sockaddr_in6 *sin6;
struct in6_addr *addrptr;
int err;
int sockfd = -1;
struct ifreq ifr;
res0=NULL;
ifihead = NULL;
@ -101,14 +101,16 @@ struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases)
lastname[0] = 0;
if ((fp = fopen(PROC_IFINET6_PATH, "r")) != NULL) {
sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
if (sockfd < 0) {
goto gotError;
}
while (fscanf(fp,
"%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %8s\n",
addr[0],addr[1],addr[2],addr[3],
addr[4],addr[5],addr[6],addr[7],
&index, &plen, &scope, &flags, ifname) != EOF) {
char ipv6addr[INET6_ADDRSTRLEN];
myflags = 0;
if (strncmp(lastname, ifname, IFNAMSIZ) == 0) {
if (doaliases == 0)
@ -121,6 +123,8 @@ struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases)
goto gotError;
}
ifipold = *ifipnext; /* need this later */
ifiptr = ifipnext;
*ifipnext = ifi; /* prev points to this new one */
ifipnext = &ifi->ifi_next; /* pointer to next one goes here */
@ -143,6 +147,7 @@ struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases)
memcpy(ifi->ifi_addr, res0->ai_addr, sizeof(struct sockaddr_in6));
/* Add netmask of the interface */
char ipv6addr[INET6_ADDRSTRLEN];
plen_to_mask(plen, ipv6addr);
ifi->ifi_netmask = calloc(1, sizeof(struct sockaddr_in6));
if (ifi->ifi_addr == NULL) {
@ -164,16 +169,32 @@ struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases)
/* Add interface index */
ifi->ifi_index = index;
/* If interface is in /proc then it is up*/
ifi->ifi_flags = IFF_UP;
/* Add interface flags*/
memcpy(ifr.ifr_name, ifname, IFNAMSIZ);
if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
if (errno == EADDRNOTAVAIL) {
/*
* If the main interface is configured with no IP address but
* an alias interface exists with an IP address, you get
* EADDRNOTAVAIL for the main interface
*/
free(ifi->ifi_addr);
free(ifi);
ifipnext = ifiptr;
*ifipnext = ifipold;
continue;
} else {
goto gotError;
}
}
ifi->ifi_flags = ifr.ifr_flags;
freeaddrinfo(res0);
res0=NULL;
}
}
goto done;
gotError:
gotError:
if (ifihead != NULL) {
free_ifi_info(ifihead);
ifihead = NULL;
@ -182,15 +203,18 @@ struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases)
freeaddrinfo(res0);
res0=NULL;
}
done:
return(ifihead); /* pointer to first structure in linked list */
done:
if (sockfd != -1) {
assert(close(sockfd) == 0);
}
return(ifihead); /* pointer to first structure in linked list */
}
#endif // defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX
struct ifi_info *get_ifi_info(int family, int doaliases)
{
int junk;
struct ifi_info *ifi, *ifihead, **ifipnext;
struct ifi_info *ifi, *ifihead, **ifipnext, *ifipold, **ifiptr;
int sockfd, sockf6, len, lastlen, flags, myflags;
#ifdef NOT_HAVE_IF_NAMETOINDEX
int index = 200;
@ -278,28 +302,12 @@ struct ifi_info *get_ifi_info(int family, int doaliases)
if ((flags & IFF_UP) == 0)
continue; /* ignore if interface not up */
/* Skip addresses we can't use */
#ifdef SIOCGIFAFLAG_IN6
if (ifr->ifr_addr.sa_family == AF_INET6) {
struct in6_ifreq ifr6;
if (sockf6 == -1)
sockf6 = socket(AF_INET6, SOCK_DGRAM, 0);
memset(&ifr6, 0, sizeof(ifr6));
memcpy(&ifr6.ifr_name, &ifr->ifr_name, sizeof(ifr6.ifr_name));
memcpy(&ifr6.ifr_addr, &ifr->ifr_addr, sizeof(ifr6.ifr_addr));
if (ioctl(sockf6, SIOCGIFAFLAG_IN6, &ifr6) < 0)
goto gotError;
if (ifr6.ifr_ifru.ifru_flags6 &
(IN6_IFF_NOTREADY | IN6_IFF_DETACHED))
continue;
}
#endif
ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info));
if (ifi == NULL) {
goto gotError;
}
ifipold = *ifipnext; /* need this later */
ifiptr = ifipnext;
*ifipnext = ifi; /* prev points to this new one */
ifipnext = &ifi->ifi_next; /* pointer to next one goes here */
@ -331,7 +339,23 @@ struct ifi_info *get_ifi_info(int family, int doaliases)
memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));
#ifdef SIOCGIFNETMASK
if (ioctl(sockfd, SIOCGIFNETMASK, &ifrcopy) < 0) goto gotError;
if (ioctl(sockfd, SIOCGIFNETMASK, &ifrcopy) < 0) {
if (errno == EADDRNOTAVAIL) {
/*
* If the main interface is configured with no IP address but
* an alias interface exists with an IP address, you get
* EADDRNOTAVAIL for the main interface
*/
free(ifi->ifi_addr);
free(ifi);
ifipnext = ifiptr;
*ifipnext = ifipold;
continue;
} else {
goto gotError;
}
}
ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
if (ifi->ifi_netmask == NULL) goto gotError;
sinptr = (struct sockaddr_in *) &ifrcopy.ifr_addr;
@ -406,7 +430,22 @@ struct ifi_info *get_ifi_info(int family, int doaliases)
memset(&ifr6, 0, sizeof(ifr6));
memcpy(&ifr6.ifr_name, &ifr->ifr_name, sizeof(ifr6.ifr_name ));
memcpy(&ifr6.ifr_ifru.ifru_addr, &ifr->ifr_addr, sizeof(ifr6.ifr_ifru.ifru_addr));
if (ioctl(sockf6, SIOCGIFNETMASK_IN6, &ifr6) < 0) goto gotError;
if (ioctl(sockf6, SIOCGIFNETMASK_IN6, &ifr6) < 0) {
if (errno == EADDRNOTAVAIL) {
/*
* If the main interface is configured with no IP address but
* an alias interface exists with an IP address, you get
* EADDRNOTAVAIL for the main interface
*/
free(ifi->ifi_addr);
free(ifi);
ifipnext = ifiptr;
*ifipnext = ifipold;
continue;
} else {
goto gotError;
}
}
ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in6));
if (ifi->ifi_netmask == NULL) goto gotError;
sinptr6 = (struct sockaddr_in6 *) &ifr6.ifr_ifru.ifru_addr;
@ -531,12 +570,12 @@ recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp,
#ifdef IP_PKTINFO
#if in_pktinfo_definition_is_missing
struct in_pktinfo
{
struct in_pktinfo
{
int ipi_ifindex;
struct in_addr ipi_spec_dst;
struct in_addr ipi_addr;
};
};
#endif
if (cmptr->cmsg_level == IPPROTO_IP &&
cmptr->cmsg_type == IP_PKTINFO) {
@ -599,7 +638,7 @@ struct in_pktinfo
#if defined(IPV6_PKTINFO) && HAVE_IPV6
if (cmptr->cmsg_level == IPPROTO_IPV6 &&
cmptr->cmsg_type == IPV6_PKTINFO) {
cmptr->cmsg_type == IPV6_2292_PKTINFO) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&pktp->ipi_addr;
struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmptr);
@ -618,7 +657,7 @@ struct in_pktinfo
#if defined(IPV6_HOPLIMIT) && HAVE_IPV6
if (cmptr->cmsg_level == IPPROTO_IPV6 &&
cmptr->cmsg_type == IPV6_HOPLIMIT) {
cmptr->cmsg_type == IPV6_2292_HOPLIMIT) {
*ttl = *(int*)CMSG_DATA(cmptr);
continue;
}
@ -640,7 +679,7 @@ struct in_pktinfo
#include <sys/signal.h>
int daemon(int nochdir, int noclose)
{
{
switch (fork())
{
case -1: return (-1); // Fork failed
@ -676,5 +715,5 @@ int daemon(int nochdir, int noclose)
}
}
return (0);
}
}
#endif /* NOT_HAVE_DAEMON */

View File

@ -25,14 +25,23 @@
#ifdef HAVE_LINUX
#include <linux/socket.h>
#define IPV6_2292_PKTINFO IPV6_2292PKTINFO
#define IPV6_2292_HOPLIMIT IPV6_2292HOPLIMIT
#else
// The following are the supported non-linux posix OSes -
// netbsd, freebsd and openbsd.
#if HAVE_IPV6
#define IPV6_2292_PKTINFO 19
#define IPV6_2292_HOPLIMIT 20
#endif
#endif
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
#ifdef NOT_HAVE_SOCKLEN_T
typedef unsigned int socklen_t;
typedef unsigned int socklen_t;
#endif
#if !defined(_SS_MAXSIZE)
@ -82,8 +91,8 @@ struct ifi_info {
int ifi_index; /* interface index */
struct sockaddr *ifi_addr; /* primary address */
struct sockaddr *ifi_netmask;
struct sockaddr *ifi_brdaddr;/* broadcast address */
struct sockaddr *ifi_dstaddr;/* destination address */
struct sockaddr *ifi_brdaddr; /* broadcast address */
struct sockaddr *ifi_dstaddr; /* destination address */
struct ifi_info *ifi_next; /* next of these structures */
};
@ -115,7 +124,7 @@ extern int daemon(int nochdir, int noclose);
#endif
#ifdef __cplusplus
}
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -71,7 +71,7 @@ int RemoveFromList( GenLinkedList *pList, void *elem)
/* Remove a linked list element from the list. Return 0 if it was not found. */
/* If the element is removed, its link will be set to NULL. */
{
void *iElem, *lastElem;
void *iElem, *lastElem;
for ( iElem = pList->Head, lastElem = NULL; iElem; iElem = GETLINK( iElem, pList->LinkOffset)) {
if ( iElem == elem) {
@ -95,7 +95,7 @@ void *iElem, *lastElem;
int ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem)
/* Replace an element in the list with a new element, in the same position. */
{
void *iElem, *lastElem;
void *iElem, *lastElem;
if ( elemInList == NULL || newElem == NULL)
return 0;
@ -140,7 +140,7 @@ void InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset,
void DLLAddToHead( GenDoubleLinkedList *pList, void *elem)
/* Add a linked list element to the head of the list. */
{
void *pNext;
void *pNext;
pNext = pList->Head;
@ -161,7 +161,7 @@ void DLLRemoveFromList( GenDoubleLinkedList *pList, void *elem)
/* Remove a linked list element from the list. */
/* When the element is removed, its link will be set to NULL. */
{
void *pNext, *pPrev;
void *pNext, *pPrev;
pNext = GETLINK( elem, pList->FwdLinkOffset);
pPrev = GETLINK( elem, pList->BackLinkOffset);
@ -215,7 +215,7 @@ void *GetTailPtr( GenLinkedOffsetList *pList)
void *GetOffsetLink( GenLinkedOffsetList *pList, void *elem)
/* Return the link pointer contained within element e for pList, or NULL if it is 0. */
{
size_t nextOffset;
size_t nextOffset;
nextOffset = GETOFFSET( elem, pList->LinkOffset);
@ -260,7 +260,7 @@ int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem)
/* Remove a linked list element from the list. Return 0 if it was not found. */
/* If the element is removed, its link will be set to NULL. */
{
void *iElem, *lastElem;
void *iElem, *lastElem;
for ( iElem = GetHeadPtr( pList), lastElem = NULL; iElem;
iElem = GetOffsetLink( pList, iElem))
@ -287,7 +287,7 @@ void *iElem, *lastElem;
int OffsetReplaceElem( GenLinkedOffsetList *pList, void *elemInList, void *newElem)
/* Replace an element in the list with a new element, in the same position. */
{
void *iElem, *lastElem;
void *iElem, *lastElem;
if ( elemInList == NULL || newElem == NULL)
return 0;

View File

@ -28,12 +28,12 @@
#include "PlatformCommon.h"
#ifdef NOT_HAVE_SOCKLEN_T
typedef unsigned int socklen_t;
typedef unsigned int socklen_t;
#endif
// Bind a UDP socket to find the source address to a destination
mDNSexport void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst)
{
{
union { struct sockaddr s; struct sockaddr_in a4; struct sockaddr_in6 a6; } addr;
socklen_t len = sizeof(addr);
socklen_t inner_len = 0;
@ -75,11 +75,11 @@ mDNSexport void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAdd
else src->ip.v6 = *(mDNSv6Addr*)&addr.a6.sin6_addr;
exit:
close(sock);
}
}
// dst must be at least MAX_ESCAPED_DOMAIN_NAME bytes, and option must be less than 32 bytes in length
mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f)
{
{
char buf[32+1+MAX_ESCAPED_DOMAIN_NAME]; // Option name, one space, option value
unsigned int len = strlen(option);
if (len + 1 + MAX_ESCAPED_DOMAIN_NAME > sizeof(buf)-1) { LogMsg("GetConfigOption: option %s too long", option); return mDNSfalse; }
@ -97,10 +97,10 @@ mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f)
}
debugf("Option %s not set", option);
return mDNSfalse;
}
}
mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled)
{
{
char buf[MAX_ESCAPED_DOMAIN_NAME] = "";
mStatus err;
FILE *f = fopen(filename, "r");
@ -129,27 +129,27 @@ mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const fi
{
DomainAuthInfo *info = (DomainAuthInfo*)mDNSPlatformMemAllocate(sizeof(*info));
// for now we assume keyname = service reg domain and we use same key for service and hostname registration
err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, NULL, 0, NULL);
err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, NULL, 0, mDNSfalse);
if (err) LogMsg("ERROR: mDNS_SetSecretForDomain returned %d for domain %##s", err, domain->c);
}
return;
badf:
badf:
LogMsg("ERROR: malformatted config file");
if (f) fclose(f);
}
}
#if MDNS_DEBUGMSGS
mDNSexport void mDNSPlatformWriteDebugMsg(const char *msg)
{
{
fprintf(stderr,"%s\n", msg);
fflush(stderr);
}
}
#endif
mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, mDNSLogLevel_t loglevel)
{
{
#if APPLE_OSX_mDNSResponder && LogTimeStamps
extern mDNS mDNSStorage;
extern mDNSu32 mDNSPlatformClockDivisor;
@ -190,7 +190,10 @@ mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, m
if (ident && ident[0] && mDNSPlatformClockDivisor)
syslog(syslog_level, "%8d.%03d: %s", (int)(t/1000), ms, buffer);
else
#endif
#elif APPLE_OSX_mDNSResponder
mDNSPlatformLogToFile(syslog_level, buffer);
#else
syslog(syslog_level, "%s", buffer);
#endif
}
}
}

View File

@ -16,18 +16,32 @@
.\"
.Dd April 2004 \" Date
.Dt dns-sd 1 \" Document Title
.Os NetBSD \" Operating System
.Os Darwin \" Operating System
.\"
.Sh NAME
.Nm dns-sd
.Nd Multicast DNS (mDNS) & DNS Service Discovery (DNS-SD) Test Tool \" For whatis
.\"
.Sh SYNOPSIS
.Nm Fl E
.Pp
.Nm Fl F
.Pp
.Nm Fl R Ar name type domain port Op Ar key=value ...
.Pp
.Nm Fl B Ar type domain
.Pp
.Nm Fl L Ar name type domain
.Pp
.Nm Fl P Ar name type domain port host IP Op Ar key=value ...
.Pp
.Nm Fl q Ar name rrtype rrclass
.Pp
.Nm Fl Z Ar type domain
.Pp
.Nm Fl G Ns \ v4/v6/v4v6 Ar name
.Pp
.Nm Fl V
.\"
.Sh DESCRIPTION
The
@ -43,6 +57,11 @@ The library API that
.Nm
uses is documented in
.Pa /usr/include/dns_sd.h .
The
.Nm
command replaces the older
mDNS
command.
.Pp
The
.Nm
@ -73,7 +92,17 @@ directly call DNS-SD APIs using the dnssd package documented at
.br
Similar bindings for other languages are also in development.
.Pp
.Bl -tag -width R
.Bl -tag -width E
.It Nm Fl E
return a list of domains recommended for registering(advertising) services.
.Pp
.It Nm Fl F
return a list of domains recommended for browsing services.
.Pp
Normally, on your home network, the only domain you are likely to see is "local".
However if your network administrator has created Domain Enumeration records,
then you may also see other recommended domains for registering and browsing.
.Pp
.It Nm Fl R Ar name type domain port Op Ar key=value ...
register (advertise) a service in the specified
.Ar domain
@ -91,7 +120,7 @@ up to 63 UTF-8 bytes long.
.Ar type
must be of the form "_app-proto._tcp" or "_app-proto._udp", where
"app-proto" is an application protocol name registered at
.Pa http://www.dns-sd.org/ServiceTypes.html .
.Pa http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml .
.Pp
.Ar domain
is the domain in which to register the service.
@ -110,7 +139,7 @@ Additional attributes of the service may optionally be described by
key/value pairs, which are stored in the advertised service's DNS TXT
record. Allowable keys and values are listed with the service
registration at
.Pa http://www.dns-sd.org/ServiceTypes.html .
.Pa http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml .
.It Nm Fl B Ar type domain
browse for instances of service
.Ar type
@ -120,7 +149,7 @@ in
For valid
.Ar type Ns s
see
.Pa http://www.dns-sd.org/ServiceTypes.html
.Pa http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml .
as described above. Omitting the
.Ar domain
or using "." means "pick a sensible default."
@ -130,12 +159,39 @@ named service: the hostname of the machine where that service is
available, the port number on which the service is listening, and (if
present) TXT record attributes describing properties of the service.
.Pp
Note that in a typical application, browsing happens rarely, while lookup
Note that in a typical application, browsing may only happen rarely, while lookup
(or "resolving") happens every time the service is used. For example, a
user browses the network to pick a default printer fairly rarely, but once
a default printer has been picked, that named service is resolved to its
current IP address and port number every time the user presses Cmd-P to
print.
.Pp
.It Nm Fl P Ar name type domain port host IP Op Ar key=value ...
create a proxy advertisement for a service running on(offered by) some other machine.
The two new options are Host, a name for the device and IP, the address of it.
.Pp
The service for which you create a proxy advertisement does not necessarily have to be on your local network.
You can set up a local proxy for a website on the Internet.
.Pp
.It Nm Fl q Ar name rrtype rrclass
look up any DNS name, resource record type, and resource record class,
not necessarily DNS-SD names and record types.
If rrtype is not specified, it queries for the IPv4 address of the name,
if rrclass is not specified, IN class is assumed. If the name is not a fully
qualified domain name, then search domains may be appended.
.Pp
.It Nm Fl Z Ar type domain
browse for service instances and display output in zone file format.
.Pp
.It Nm Fl G Ns \ v4/v6/v4v6 Ar name
look up the IP address information of the name.
If v4 is specified, the IPv4 address of the name is looked up,
if v6 is specified the IPv6 address is looked up. If v4v6 is specified both the IPv4 and IPv6
address is looked up. If the name is not a fully qualified domain name,
then search domains may be appended.
.Pp
.It Nm Fl V
return the version of the currently running daemon/system service.
.El
.Sh EXAMPLES
.Pp
@ -172,15 +228,39 @@ window and you should see the "Remove" event reported to the
.Nm Fl B
window.
.Pp
In the example below, the www.apple.com web page is advertised as a service called "apple",
running on a target host called apple.local, which resolves to 17.149.160.49.
.Pp
.Dl Nm Fl P Ns \ apple _http._tcp \&"\&"\& 80 apple.local 17.149.160.49
.Pp
The Bonjour menu in the Safari web browser will now show "apple".
The same IP address can be reached by entering apple.local in the web browser.
In either case, the request will be resolved to the IP address and browser will show
contents associated with www.apple.com.
.Pp
If a client wants to be notified of changes in server state, it can
initiate a query for the service's particular record and leave it running.
For example, to monitor the status of an iChat user you can use:
.Pp
.Dl Nm Fl q Ns \ someone@ex1._presence._tcp.local txt
.Pp
Everytime status of that user(someone) changes, you will see a new TXT record result reported.
.Pp
You can also query for a unicast name like www.apple.com and monitor its status.
.Pp
.Dl Nm Fl q Ns \ www.apple.com
.Pp
.Sh FILES
.Pa /usr/bin/dns-sd \" Pathname
.\"
.Sh SEE ALSO
.Xr mdnsd 8
.Xr mDNSResponder 8
.\"
.Sh BUGS
.Nm
bugs are tracked in Apple Radar component "mDNSResponder".
.\"
.Sh HISTORY
The
.Nm
command first appeared in
.Nx 6.0 ,
having originated in Mac OS X 10.4 (Tiger).
command first appeared in Mac OS X 10.4 (Tiger).

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,585 @@
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mDNSEmbeddedAPI.h"
#include "DebugServices.h"
#include "dnsextd.h"
void yyerror( const char* error );
int yylex(void);
typedef struct StringListElem
{
char * string;
struct StringListElem * next;
} StringListElem;
typedef struct OptionsInfo
{
char server_address[ 256 ];
int server_port;
char source_address[ 256 ];
int source_port;
int private_port;
int llq_port;
} OptionsInfo;
typedef struct ZoneInfo
{
char name[ 256 ];
char certificate_name[ 256 ];
char allow_clients_file[ 256 ];
char allow_clients[ 256 ];
char key[ 256 ];
} ZoneInfo;
typedef struct KeySpec
{
char name[ 256 ];
char algorithm[ 256 ];
char secret[ 256 ];
struct KeySpec * next;
} KeySpec;
typedef struct ZoneSpec
{
char name[ 256 ];
DNSZoneSpecType type;
StringListElem * allowUpdate;
StringListElem * allowQuery;
char key[ 256 ];
struct ZoneSpec * next;
} ZoneSpec;
static StringListElem * g_stringList = NULL;
static KeySpec * g_keys;
static ZoneSpec * g_zones;
static ZoneSpec g_zoneSpec;
static const char * g_filename;
#define YYPARSE_PARAM context
void
SetupOptions
(
OptionsInfo * info,
void * context
);
%}
%union
{
int number;
char * string;
}
%token OPTIONS
%token LISTEN_ON
%token NAMESERVER
%token PORT
%token ADDRESS
%token LLQ
%token PUBLIC
%token PRIVATE
%token ALLOWUPDATE
%token ALLOWQUERY
%token KEY
%token ALGORITHM
%token SECRET
%token ISSUER
%token SERIAL
%token ZONE
%token TYPE
%token ALLOW
%token OBRACE
%token EBRACE
%token SEMICOLON
%token IN
%token <string> DOTTED_DECIMAL_ADDRESS
%token <string> WILDCARD
%token <string> DOMAINNAME
%token <string> HOSTNAME
%token <string> QUOTEDSTRING
%token <number> NUMBER
%type <string> addressstatement
%type <string> networkaddress
%%
commands:
|
commands command SEMICOLON
;
command:
options_set
|
zone_set
|
key_set
;
options_set:
OPTIONS optionscontent
{
// SetupOptions( &g_optionsInfo, context );
}
;
optionscontent:
OBRACE optionsstatements EBRACE
;
optionsstatements:
|
optionsstatements optionsstatement SEMICOLON
;
optionsstatement:
statements
|
LISTEN_ON addresscontent
{
}
|
LISTEN_ON PORT NUMBER addresscontent
{
}
|
NAMESERVER ADDRESS networkaddress
{
}
|
NAMESERVER ADDRESS networkaddress PORT NUMBER
{
}
|
PRIVATE PORT NUMBER
{
( ( DaemonInfo* ) context )->private_port = mDNSOpaque16fromIntVal( $3 );
}
|
LLQ PORT NUMBER
{
( ( DaemonInfo* ) context )->llq_port = mDNSOpaque16fromIntVal( $3 );
}
;
key_set:
KEY QUOTEDSTRING OBRACE SECRET QUOTEDSTRING SEMICOLON EBRACE
{
KeySpec * keySpec;
keySpec = ( KeySpec* ) malloc( sizeof( KeySpec ) );
if ( !keySpec )
{
LogMsg("ERROR: memory allocation failure");
YYABORT;
}
strncpy( keySpec->name, $2, sizeof( keySpec->name ) );
strncpy( keySpec->secret, $5, sizeof( keySpec->secret ) );
keySpec->next = g_keys;
g_keys = keySpec;
}
;
zone_set:
ZONE QUOTEDSTRING zonecontent
{
ZoneSpec * zoneSpec;
zoneSpec = ( ZoneSpec* ) malloc( sizeof( ZoneSpec ) );
if ( !zoneSpec )
{
LogMsg("ERROR: memory allocation failure");
YYABORT;
}
strncpy( zoneSpec->name, $2, sizeof( zoneSpec->name ) );
zoneSpec->type = g_zoneSpec.type;
strcpy( zoneSpec->key, g_zoneSpec.key );
zoneSpec->allowUpdate = g_zoneSpec.allowUpdate;
zoneSpec->allowQuery = g_zoneSpec.allowQuery;
zoneSpec->next = g_zones;
g_zones = zoneSpec;
}
|
ZONE QUOTEDSTRING IN zonecontent
{
ZoneSpec * zoneSpec;
zoneSpec = ( ZoneSpec* ) malloc( sizeof( ZoneSpec ) );
if ( !zoneSpec )
{
LogMsg("ERROR: memory allocation failure");
YYABORT;
}
strncpy( zoneSpec->name, $2, sizeof( zoneSpec->name ) );
zoneSpec->type = g_zoneSpec.type;
strcpy( zoneSpec->key, g_zoneSpec.key );
zoneSpec->allowUpdate = g_zoneSpec.allowUpdate;
zoneSpec->allowQuery = g_zoneSpec.allowQuery;
zoneSpec->next = g_zones;
g_zones = zoneSpec;
}
;
zonecontent:
OBRACE zonestatements EBRACE
zonestatements:
|
zonestatements zonestatement SEMICOLON
;
zonestatement:
TYPE PUBLIC
{
g_zoneSpec.type = kDNSZonePublic;
}
|
TYPE PRIVATE
{
g_zoneSpec.type = kDNSZonePrivate;
}
|
ALLOWUPDATE keycontent
{
g_zoneSpec.allowUpdate = g_stringList;
g_stringList = NULL;
}
|
ALLOWQUERY keycontent
{
g_zoneSpec.allowQuery = g_stringList;
g_stringList = NULL;
}
;
addresscontent:
OBRACE addressstatements EBRACE
{
}
addressstatements:
|
addressstatements addressstatement SEMICOLON
{
}
;
addressstatement:
DOTTED_DECIMAL_ADDRESS
{
}
;
keycontent:
OBRACE keystatements EBRACE
{
}
keystatements:
|
keystatements keystatement SEMICOLON
{
}
;
keystatement:
KEY DOMAINNAME
{
StringListElem * elem;
elem = ( StringListElem* ) malloc( sizeof( StringListElem ) );
if ( !elem )
{
LogMsg("ERROR: memory allocation failure");
YYABORT;
}
elem->string = $2;
elem->next = g_stringList;
g_stringList = elem;
}
;
networkaddress:
DOTTED_DECIMAL_ADDRESS
|
HOSTNAME
|
WILDCARD
;
block:
OBRACE zonestatements EBRACE SEMICOLON
;
statements:
|
statements statement
;
statement:
block
{
$<string>$ = NULL;
}
|
QUOTEDSTRING
{
$<string>$ = $1;
}
%%
int yywrap(void);
extern int yylineno;
void yyerror( const char *str )
{
fprintf( stderr,"%s:%d: error: %s\n", g_filename, yylineno, str );
}
int yywrap()
{
return 1;
}
int
ParseConfig
(
DaemonInfo * d,
const char * file
)
{
extern FILE * yyin;
DNSZone * zone;
DomainAuthInfo * key;
KeySpec * keySpec;
ZoneSpec * zoneSpec;
int err = 0;
g_filename = file;
// Tear down the current zone specifiers
zone = d->zones;
while ( zone )
{
DNSZone * next = zone->next;
key = zone->updateKeys;
while ( key )
{
DomainAuthInfo * nextKey = key->next;
free( key );
key = nextKey;
}
key = zone->queryKeys;
while ( key )
{
DomainAuthInfo * nextKey = key->next;
free( key );
key = nextKey;
}
free( zone );
zone = next;
}
d->zones = NULL;
yyin = fopen( file, "r" );
require_action( yyin, exit, err = 0 );
err = yyparse( ( void* ) d );
require_action( !err, exit, err = 1 );
for ( zoneSpec = g_zones; zoneSpec; zoneSpec = zoneSpec->next )
{
StringListElem * elem;
mDNSu8 * ok;
zone = ( DNSZone* ) malloc( sizeof( DNSZone ) );
require_action( zone, exit, err = 1 );
memset( zone, 0, sizeof( DNSZone ) );
zone->next = d->zones;
d->zones = zone;
// Fill in the domainname
ok = MakeDomainNameFromDNSNameString( &zone->name, zoneSpec->name );
require_action( ok, exit, err = 1 );
// Fill in the type
zone->type = zoneSpec->type;
// Fill in the allow-update keys
for ( elem = zoneSpec->allowUpdate; elem; elem = elem->next )
{
mDNSBool found = mDNSfalse;
for ( keySpec = g_keys; keySpec; keySpec = keySpec->next )
{
if ( strcmp( elem->string, keySpec->name ) == 0 )
{
DomainAuthInfo * authInfo = malloc( sizeof( DomainAuthInfo ) );
mDNSs32 keylen;
require_action( authInfo, exit, err = 1 );
memset( authInfo, 0, sizeof( DomainAuthInfo ) );
ok = MakeDomainNameFromDNSNameString( &authInfo->keyname, keySpec->name );
if (!ok) { free(authInfo); err = 1; goto exit; }
keylen = DNSDigest_ConstructHMACKeyfromBase64( authInfo, keySpec->secret );
if (keylen < 0) { free(authInfo); err = 1; goto exit; }
authInfo->next = zone->updateKeys;
zone->updateKeys = authInfo;
found = mDNStrue;
break;
}
}
// Log this
require_action( found, exit, err = 1 );
}
// Fill in the allow-query keys
for ( elem = zoneSpec->allowQuery; elem; elem = elem->next )
{
mDNSBool found = mDNSfalse;
for ( keySpec = g_keys; keySpec; keySpec = keySpec->next )
{
if ( strcmp( elem->string, keySpec->name ) == 0 )
{
DomainAuthInfo * authInfo = malloc( sizeof( DomainAuthInfo ) );
mDNSs32 keylen;
require_action( authInfo, exit, err = 1 );
memset( authInfo, 0, sizeof( DomainAuthInfo ) );
ok = MakeDomainNameFromDNSNameString( &authInfo->keyname, keySpec->name );
if (!ok) { free(authInfo); err = 1; goto exit; }
keylen = DNSDigest_ConstructHMACKeyfromBase64( authInfo, keySpec->secret );
if (keylen < 0) { free(authInfo); err = 1; goto exit; }
authInfo->next = zone->queryKeys;
zone->queryKeys = authInfo;
found = mDNStrue;
break;
}
}
// Log this
require_action( found, exit, err = 1 );
}
}
exit:
return err;
}
void
SetupOptions
(
OptionsInfo * info,
void * context
)
{
DaemonInfo * d = ( DaemonInfo* ) context;
if ( strlen( info->source_address ) )
{
inet_pton( AF_INET, info->source_address, &d->addr.sin_addr );
}
if ( info->source_port )
{
d->addr.sin_port = htons( ( mDNSu16 ) info->source_port );
}
if ( strlen( info->server_address ) )
{
inet_pton( AF_INET, info->server_address, &d->ns_addr.sin_addr );
}
if ( info->server_port )
{
d->ns_addr.sin_port = htons( ( mDNSu16 ) info->server_port );
}
if ( info->private_port )
{
d->private_port = mDNSOpaque16fromIntVal( info->private_port );
}
if ( info->llq_port )
{
d->llq_port = mDNSOpaque16fromIntVal( info->llq_port );
}
}

View File

@ -43,10 +43,10 @@
#endif
/*********************************************************************************************
*
* Supporting Functions
*
*********************************************************************************************/
*
* Supporting Functions
*
*********************************************************************************************/
#define mDNSIsDigit(X) ((X) >= '0' && (X) <= '9')
@ -54,7 +54,7 @@
// (DNSServiceConstructFullName depends this returning 1 for true, rather than any non-zero value meaning true)
static int DomainEndsInDot(const char *dom)
{
{
while (dom[0] && dom[1])
{
if (dom[0] == '\\') // advance past escaped byte sequence
@ -66,16 +66,16 @@ static int DomainEndsInDot(const char *dom)
else dom++; // else goto next character
}
return (dom[0] == '.');
}
}
static uint8_t *InternalTXTRecordSearch
(
(
uint16_t txtLen,
const void *txtRecord,
const char *key,
unsigned long *keylen
)
{
)
{
uint8_t *p = (uint8_t*)txtRecord;
uint8_t *e = p + txtLen;
*keylen = (unsigned long) strlen(key);
@ -87,26 +87,26 @@ static uint8_t *InternalTXTRecordSearch
if (*keylen == x[0] || x[1+*keylen] == '=') return(x);
}
return(NULL);
}
}
/*********************************************************************************************
*
* General Utility Functions
*
*********************************************************************************************/
*
* General Utility Functions
*
*********************************************************************************************/
// Note: Need to make sure we don't write more than kDNSServiceMaxDomainName (1009) bytes to fullName
// In earlier builds this constant was defined to be 1005, so to avoid buffer overruns on clients
// compiled with that constant we'll actually limit the output to 1005 bytes.
DNSServiceErrorType DNSSD_API DNSServiceConstructFullName
(
(
char *const fullName,
const char *const service, // May be NULL
const char *const regtype,
const char *const domain
)
{
)
{
const size_t len = !regtype ? 0 : strlen(regtype) - DomainEndsInDot(regtype);
char *fn = fullName;
char *const lim = fullName + 1005;
@ -143,11 +143,11 @@ DNSServiceErrorType DNSSD_API DNSServiceConstructFullName
*fn++ = '.';
}
while (*r) if (fn+1 >= lim) goto fail; else *fn++ = *r++;
if (!DomainEndsInDot(regtype)) { if (fn+1 >= lim) goto fail; else *fn++ = '.'; }
while (*r) if (fn+1 >= lim) goto fail;else *fn++ = *r++;
if (!DomainEndsInDot(regtype)) { if (fn+1 >= lim) goto fail;else *fn++ = '.';}
while (*d) if (fn+1 >= lim) goto fail; else *fn++ = *d++;
if (!DomainEndsInDot(domain)) { if (fn+1 >= lim) goto fail; else *fn++ = '.'; }
while (*d) if (fn+1 >= lim) goto fail;else *fn++ = *d++;
if (!DomainEndsInDot(domain)) { if (fn+1 >= lim) goto fail;else *fn++ = '.';}
*fn = '\0';
return kDNSServiceErr_NoError;
@ -155,57 +155,57 @@ DNSServiceErrorType DNSSD_API DNSServiceConstructFullName
fail:
*fn = '\0';
return kDNSServiceErr_BadParam;
}
}
/*********************************************************************************************
*
* TXT Record Construction Functions
*
*********************************************************************************************/
*
* TXT Record Construction Functions
*
*********************************************************************************************/
typedef struct _TXTRecordRefRealType
{
{
uint8_t *buffer; // Pointer to data
uint16_t buflen; // Length of buffer
uint16_t datalen; // Length currently in use
uint16_t malloced; // Non-zero if buffer was allocated via malloc()
} TXTRecordRefRealType;
} TXTRecordRefRealType;
#define txtRec ((TXTRecordRefRealType*)txtRecord)
// The opaque storage defined in the public dns_sd.h header is 16 bytes;
// make sure we don't exceed that.
struct CompileTimeAssertionCheck_dnssd_clientlib
{
{
char assert0[(sizeof(TXTRecordRefRealType) <= 16) ? 1 : -1];
};
};
void DNSSD_API TXTRecordCreate
(
(
TXTRecordRef *txtRecord,
uint16_t bufferLen,
void *buffer
)
{
)
{
txtRec->buffer = buffer;
txtRec->buflen = buffer ? bufferLen : (uint16_t)0;
txtRec->datalen = 0;
txtRec->malloced = 0;
}
}
void DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtRecord)
{
{
if (txtRec->malloced) free(txtRec->buffer);
}
}
DNSServiceErrorType DNSSD_API TXTRecordSetValue
(
(
TXTRecordRef *txtRecord,
const char *key,
uint8_t valueSize,
const void *value
)
{
)
{
uint8_t *start, *p;
const char *k;
unsigned long keysize, keyvalsize;
@ -241,14 +241,14 @@ DNSServiceErrorType DNSSD_API TXTRecordSetValue
*start = (uint8_t)(p - start - 1);
txtRec->datalen += p - start;
return(kDNSServiceErr_NoError);
}
}
DNSServiceErrorType DNSSD_API TXTRecordRemoveValue
(
(
TXTRecordRef *txtRecord,
const char *key
)
{
)
{
unsigned long keylen, itemlen, remainder;
uint8_t *item = InternalTXTRecordSearch(txtRec->datalen, txtRec->buffer, key, &keylen);
if (!item) return(kDNSServiceErr_NoSuchKey);
@ -258,58 +258,58 @@ DNSServiceErrorType DNSSD_API TXTRecordRemoveValue
memmove(item, item + itemlen, remainder);
txtRec->datalen -= itemlen;
return(kDNSServiceErr_NoError);
}
}
uint16_t DNSSD_API TXTRecordGetLength (const TXTRecordRef *txtRecord) { return(txtRec->datalen); }
const void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); }
/*********************************************************************************************
*
* TXT Record Parsing Functions
*
*********************************************************************************************/
*
* TXT Record Parsing Functions
*
*********************************************************************************************/
int DNSSD_API TXTRecordContainsKey
(
(
uint16_t txtLen,
const void *txtRecord,
const char *key
)
{
)
{
unsigned long keylen;
return (InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen) ? 1 : 0);
}
}
const void * DNSSD_API TXTRecordGetValuePtr
(
(
uint16_t txtLen,
const void *txtRecord,
const char *key,
uint8_t *valueLen
)
{
)
{
unsigned long keylen;
uint8_t *item = InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen);
if (!item || item[0] <= keylen) return(NULL); // If key not found, or found with no value, return NULL
*valueLen = (uint8_t)(item[0] - (keylen + 1));
return (item + 1 + keylen + 1);
}
}
uint16_t DNSSD_API TXTRecordGetCount
(
(
uint16_t txtLen,
const void *txtRecord
)
{
)
{
uint16_t count = 0;
uint8_t *p = (uint8_t*)txtRecord;
uint8_t *e = p + txtLen;
while (p<e) { p += 1 + p[0]; count++; }
return((p>e) ? (uint16_t)0 : count);
}
}
DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex
(
(
uint16_t txtLen,
const void *txtRecord,
uint16_t itemIndex,
@ -317,8 +317,8 @@ DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex
char *key,
uint8_t *valueLen,
const void **value
)
{
)
{
uint16_t count = 0;
uint8_t *p = (uint8_t*)txtRecord;
uint8_t *e = p + txtLen;
@ -345,22 +345,22 @@ DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex
return(kDNSServiceErr_NoError);
}
return(kDNSServiceErr_Invalid);
}
}
/*********************************************************************************************
*
* SCCS-compatible version string
*
*********************************************************************************************/
*
* SCCS-compatible version string
*
*********************************************************************************************/
// For convenience when using the "strings" command, this is the last thing in the file
// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
// To expand "version" to its value before making the string, use STRINGIFY(version) instead
#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
// NOT static -- otherwise the compiler may optimize it out
// The "@(#) " pattern is a special prefix the "what" command looks for
const char VersionString_SCCS_libdnssd[] = "@(#) libdns_sd " STRINGIFY(mDNSResponderVersion);
const char VersionString_SCCS_libdnssd[] = "@(#) libdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,7 @@
#if defined(_WIN32)
char *win32_strerror(int inErrorCode)
{
{
static char buffer[1024];
DWORD n;
memset(buffer, 0, sizeof(buffer));
@ -50,21 +50,21 @@ char *win32_strerror(int inErrorCode)
buffer[--n] = '\0';
}
return buffer;
}
}
#endif
void put_uint32(const uint32_t l, char **ptr)
{
{
(*ptr)[0] = (char)((l >> 24) & 0xFF);
(*ptr)[1] = (char)((l >> 16) & 0xFF);
(*ptr)[2] = (char)((l >> 8) & 0xFF);
(*ptr)[3] = (char)((l ) & 0xFF);
*ptr += sizeof(uint32_t);
}
}
uint32_t get_uint32(const char **ptr, const char *end)
{
{
if (!*ptr || *ptr + sizeof(uint32_t) > end)
{
*ptr = NULL;
@ -76,17 +76,17 @@ uint32_t get_uint32(const char **ptr, const char *end)
*ptr += sizeof(uint32_t);
return((uint32_t) ((uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | p[3]));
}
}
}
void put_uint16(uint16_t s, char **ptr)
{
{
(*ptr)[0] = (char)((s >> 8) & 0xFF);
(*ptr)[1] = (char)((s ) & 0xFF);
*ptr += sizeof(uint16_t);
}
}
uint16_t get_uint16(const char **ptr, const char *end)
{
{
if (!*ptr || *ptr + sizeof(uint16_t) > end)
{
*ptr = NULL;
@ -98,18 +98,18 @@ uint16_t get_uint16(const char **ptr, const char *end)
*ptr += sizeof(uint16_t);
return((uint16_t) ((uint16_t)p[0] << 8 | p[1]));
}
}
}
int put_string(const char *str, char **ptr)
{
{
if (!str) str = "";
strcpy(*ptr, str);
*ptr += strlen(str) + 1;
return 0;
}
}
int get_string(const char **ptr, const char *const end, char *buffer, int buflen)
{
{
if (!*ptr)
{
*buffer = 0;
@ -128,16 +128,16 @@ int get_string(const char **ptr, const char *const end, char *buffer, int buflen
*ptr = NULL; // clear pointer,
return(-1); // and return failure indication
}
}
}
void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr)
{
{
memcpy(*ptr, rdata, rdlen);
*ptr += rdlen;
}
}
const char *get_rdata(const char **ptr, const char *end, int rdlen)
{
{
if (!*ptr || *ptr + rdlen > end)
{
*ptr = NULL;
@ -149,13 +149,13 @@ const char *get_rdata(const char **ptr, const char *end, int rdlen)
*ptr += rdlen;
return rd;
}
}
}
void ConvertHeaderBytes(ipc_msg_hdr *hdr)
{
{
hdr->version = htonl(hdr->version);
hdr->datalen = htonl(hdr->datalen);
hdr->ipc_flags = htonl(hdr->ipc_flags);
hdr->op = htonl(hdr->op );
hdr->reg_index = htonl(hdr->reg_index);
}
}

View File

@ -60,6 +60,7 @@ extern char *win32_strerror(int inErrorCode);
# include <sys/stat.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# define dnssd_InvalidSocket -1
# define dnssd_SocketValid(s) ((s) >= 0)
# define dnssd_EWOULDBLOCK EWOULDBLOCK
@ -84,8 +85,9 @@ extern char *win32_strerror(int inErrorCode);
# ifndef MDNS_UDS_SERVERPATH
# define MDNS_UDS_SERVERPATH "/var/run/mDNSResponder"
# endif
# define MDNS_UDS_SERVERPATH_ENVVAR "DNSSD_UDS_PATH"
# define LISTENQ 100
// longest legal control path length
// longest legal control path length
# define MAX_CTLPATH 256
# define dnssd_sockaddr_t struct sockaddr_un
#endif
@ -106,10 +108,7 @@ extern char *win32_strerror(int inErrorCode);
// structures correctly anyway, so a plain "struct" is usually fine. In the event that structures are not packed
// correctly, our compile-time assertion checks will catch it and prevent inadvertent generation of non-working code.
#ifndef packedstruct
#ifdef __packed
#define packedstruct struct __packed
#define packedunion union __packed
#elif ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9)))
#if ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9)))
#define packedstruct struct __attribute__((__packed__))
#define packedunion union __attribute__((__packed__))
#else
@ -119,7 +118,7 @@ extern char *win32_strerror(int inErrorCode);
#endif
typedef enum
{
{
request_op_none = 0, // No request yet received on this connection
connection_request = 1, // connected socket via DNSServiceConnect()
reg_record_request, // reg/remove record only valid for connected sockets
@ -137,12 +136,15 @@ typedef enum
port_mapping_request, // New in Leopard and B4W 2.0
addrinfo_request,
send_bpf, // New in SL
getpid_request,
release_request,
connection_delegate_request,
cancel_request = 63
} request_op_t;
} request_op_t;
typedef enum
{
{
enumeration_reply_op = 64,
reg_service_reply_op,
browse_reply_op,
@ -152,23 +154,23 @@ typedef enum
getproperty_reply_op, // New in B4W 1.0.4
port_mapping_reply_op, // New in Leopard and B4W 2.0
addrinfo_reply_op
} reply_op_t;
} reply_op_t;
#if defined(_WIN64)
# pragma pack(4)
# pragma pack(push,4)
#endif
// Define context object big enough to hold a 64-bit pointer,
// to accomodate 64-bit clients communicating with 32-bit daemon.
// There's no reason for the daemon to ever be a 64-bit process, but its clients might be
typedef packedunion
{
{
void *context;
uint32_t u32[2];
} client_context_t;
} client_context_t;
typedef packedstruct
{
{
uint32_t version;
uint32_t datalen;
uint32_t ipc_flags;
@ -177,7 +179,11 @@ typedef packedstruct
uint32_t reg_index; // identifier for a record registered via DNSServiceRegisterRecord() on a
// socket connected by DNSServiceCreateConnection(). Must be unique in the scope of the connection, such that and
// index/socket pair uniquely identifies a record. (Used to select records for removal by DNSServiceRemoveRecord())
} ipc_msg_hdr;
} ipc_msg_hdr;
#if defined(_WIN64)
# pragma pack(pop)
#endif
// routines to write to and extract data from message buffers.
// caller responsible for bounds checking.
@ -201,16 +207,16 @@ int get_string(const char **ptr, const char *const end, char *buffer, int buflen
void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr);
const char *get_rdata(const char **ptr, const char *end, int rdlen); // return value is rdata pointed to by *ptr -
// rdata is not copied from buffer.
// rdata is not copied from buffer.
void ConvertHeaderBytes(ipc_msg_hdr *hdr);
struct CompileTimeAssertionChecks_dnssd_ipc
{
{
// Check that the compiler generated our on-the-wire packet format structure definitions
// properly packed, without adding padding bytes to align fields on 32-bit or 64-bit boundaries.
char assert0[(sizeof(client_context_t) == 8) ? 1 : -1];
char assert1[(sizeof(ipc_msg_hdr) == 28) ? 1 : -1];
};
};
#endif // DNSSD_IPC_H

View File

@ -39,6 +39,8 @@
mDNSexport int mDNS_LoggingEnabled = 0;
mDNSexport int mDNS_PacketLoggingEnabled = 0;
mDNSexport int mDNS_McastLoggingEnabled = 0;
mDNSexport int mDNS_McastTracingEnabled = 0;
#if MDNS_DEBUGMSGS
mDNSexport int mDNS_DebugMode = mDNStrue;
@ -50,23 +52,23 @@ mDNSexport int mDNS_DebugMode = mDNSfalse;
// how to print special data types like IP addresses and length-prefixed domain names
#if MDNS_DEBUGMSGS > 1
mDNSexport void verbosedebugf_(const char *format, ...)
{
{
char buffer[512];
va_list ptr;
va_start(ptr,format);
buffer[mDNS_vsnprintf(buffer, sizeof(buffer), format, ptr)] = 0;
va_end(ptr);
mDNSPlatformWriteDebugMsg(buffer);
}
}
#endif
// Log message with default "mDNSResponder" ident string at the start
mDNSlocal void LogMsgWithLevelv(mDNSLogLevel_t logLevel, const char *format, va_list ptr)
{
{
char buffer[512];
buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
mDNSPlatformWriteLogMsg(ProgramName, buffer, logLevel);
}
}
#define LOG_HELPER_BODY(L) \
{ \
@ -90,4 +92,4 @@ void debugf_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_DEBUG)
// Log message with default "mDNSResponder" ident string at the start
mDNSexport void LogMsgWithLevel(mDNSLogLevel_t logLevel, const char *format, ...)
LOG_HELPER_BODY(logLevel)
LOG_HELPER_BODY(logLevel)

View File

@ -50,7 +50,7 @@ has no user-specifiable command-line argument, and users should not run
.Nm
manually.
.Pp
.Ss LOGGING
.Sh LOGGING
There are several methods with which to examine
.Nm Ns 's internal state for debugging and diagnostic purposes. The syslog(1)
logging levels map as follows:
@ -83,8 +83,8 @@ A SIGINFO signal will dump a snapshot summary of the internal state to
.Sh FILES
.Pa /usr/sbin/mDNSResponder \" Pathname
.\"
.Sh SEE ALSO
.Xr mDNS 1
.Pp
.Sh INFO
.Pp
For information on Multicast DNS, see
.Pa http://www.multicastdns.org/

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,11 @@ extern mDNSs32 udsserver_idle(mDNSs32 nextevent);
extern void udsserver_info(mDNS *const m); // print out info about current state
extern void udsserver_handle_configchange(mDNS *const m);
extern int udsserver_exit(void); // should be called prior to app exit
extern void LogMcastStateInfo(mDNS *const m, mDNSBool mflag, mDNSBool start, mDNSBool mstatelog);
#define LogMcastQ (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMcastQuestion
#define LogMcastS (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMcastService
#define LogMcast (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMsg
#define LogMcastNoIdent (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMsgNoIdent
/* Routines that uds_daemon expects to link against: */
@ -50,30 +55,35 @@ extern mDNS mDNSStorage;
extern DNameListElem *AutoRegistrationDomains;
extern DNameListElem *AutoBrowseDomains;
extern mDNSs32 ChopSubTypes(char *regtype);
extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p);
extern mDNSs32 ChopSubTypes(char *regtype, char **AnonData);
extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p, char **AnonData);
extern int CountExistingRegistrations(domainname *srv, mDNSIPPort port);
extern void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result);
extern int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs);
#if APPLE_OSX_mDNSResponder
extern void machserver_automatic_browse_domain_changed(const domainname *d, mDNSBool add);
extern void machserver_automatic_registration_domain_changed(const domainname *d, mDNSBool add);
// External support
extern void mDNSInitPacketFilter(void);
extern void external_start_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype);
extern void external_stop_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype);
extern void external_start_advertising_service(const ResourceRecord *const resourceRecord);
extern void external_stop_advertising_service(const ResourceRecord *const resourceRecord);
extern void external_start_resolving_service(const domainname *const fqdn);
extern void external_stop_resolving_service(const domainname *const fqdn);
#else
#define external_start_browsing_for_service(A,B,C) (void)(A)
#define external_stop_browsing_for_service(A,B,C) (void)(A)
#define external_start_advertising_service(A) (void)(A)
#define external_stop_advertising_service(A) (void)(A)
#define external_start_resolving_service(A) (void)(A)
#define external_stop_resolving_service(A) (void)(A)
// D2D interface support
extern void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags);
extern void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags);
extern void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags);
extern void external_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags);
extern void external_start_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags);
extern void external_stop_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags);
extern void external_connection_release(const domainname *instance);
#else // APPLE_OSX_mDNSResponder
#define external_start_browsing_for_service(A,B,C,D) (void)(A)
#define external_stop_browsing_for_service(A,B,C,D) (void)(A)
#define external_start_advertising_service(A,B) (void)(A)
#define external_stop_advertising_service(A,B) (void)(A)
#define external_start_resolving_service(A,B,C) (void)(A)
#define external_stop_resolving_service(A,B,C) (void)(A)
#define external_connection_release(A) (void)(A)
#endif // APPLE_OSX_mDNSResponder
extern const char mDNSResponderVersionString_SCCS[];