849 lines
22 KiB
C
849 lines
22 KiB
C
/*
|
|
* Copyright (C) 2000, 2001 Nominum, Inc.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
|
|
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
|
* INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
|
|
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
|
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
|
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2004 - 2012 Nominum, Inc.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software and its
|
|
* documentation for any purpose with or without fee is hereby granted,
|
|
* provided that the above copyright notice and this permission notice
|
|
* appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
|
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define ISC_BUFFER_USEINLINE
|
|
|
|
#include <isc/base64.h>
|
|
#include <isc/buffer.h>
|
|
#include <isc/hmacmd5.h>
|
|
#include <isc/hmacsha.h>
|
|
#include <isc/lex.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/region.h>
|
|
#include <isc/result.h>
|
|
#include <isc/util.h>
|
|
|
|
#include <dns/callbacks.h>
|
|
#include <dns/fixedname.h>
|
|
#include <dns/message.h>
|
|
#include <dns/name.h>
|
|
#include <dns/rdata.h>
|
|
#include <dns/rdataclass.h>
|
|
#include <dns/rdatatype.h>
|
|
#include <dns/ttl.h>
|
|
|
|
#include "dns.h"
|
|
#include "log.h"
|
|
#include "opt.h"
|
|
|
|
#define WHITESPACE " \t\n"
|
|
|
|
#define MAX_RDATA_LENGTH 65535
|
|
#define EDNSLEN 11
|
|
|
|
const char *perf_dns_rcode_strings[] = {
|
|
"NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN",
|
|
"NOTIMP", "REFUSED", "YXDOMAIN", "YXRRSET",
|
|
"NXRRSET", "NOTAUTH", "NOTZONE", "rcode11",
|
|
"rcode12", "rcode13", "rcode14", "rcode15"
|
|
};
|
|
|
|
#define TSIG_HMACMD5_NAME "\010hmac-md5\007sig-alg\003reg\003int"
|
|
#define TSIG_HMACSHA1_NAME "\011hmac-sha1"
|
|
#define TSIG_HMACSHA224_NAME "\013hmac-sha224"
|
|
#define TSIG_HMACSHA256_NAME "\013hmac-sha256"
|
|
#define TSIG_HMACSHA384_NAME "\013hmac-sha384"
|
|
#define TSIG_HMACSHA512_NAME "\013hmac-sha512"
|
|
|
|
typedef enum {
|
|
TSIG_HMACMD5,
|
|
TSIG_HMACSHA1,
|
|
TSIG_HMACSHA224,
|
|
TSIG_HMACSHA256,
|
|
TSIG_HMACSHA384,
|
|
TSIG_HMACSHA512
|
|
} hmac_type_t;
|
|
|
|
typedef union {
|
|
isc_hmacmd5_t hmacmd5;
|
|
isc_hmacsha1_t hmacsha1;
|
|
isc_hmacsha224_t hmacsha224;
|
|
isc_hmacsha256_t hmacsha256;
|
|
isc_hmacsha384_t hmacsha384;
|
|
isc_hmacsha512_t hmacsha512;
|
|
} hmac_ctx_t;
|
|
|
|
struct perf_dnstsigkey {
|
|
isc_mem_t *mctx;
|
|
unsigned int alglen;
|
|
isc_constregion_t alg;
|
|
hmac_type_t hmactype;
|
|
unsigned int digestlen;
|
|
dns_fixedname_t fname;
|
|
dns_name_t *name;
|
|
unsigned char secretdata[256];
|
|
isc_buffer_t secret;
|
|
};
|
|
|
|
struct perf_dnsctx {
|
|
isc_mem_t *mctx;
|
|
dns_compress_t compress;
|
|
isc_lex_t *lexer;
|
|
};
|
|
|
|
perf_dnsctx_t *
|
|
perf_dns_createctx(isc_boolean_t updates)
|
|
{
|
|
isc_mem_t *mctx;
|
|
perf_dnsctx_t *ctx;
|
|
isc_result_t result;
|
|
|
|
if (!updates)
|
|
return NULL;
|
|
|
|
mctx = NULL;
|
|
result = isc_mem_create(0, 0, &mctx);
|
|
if (result != ISC_R_SUCCESS)
|
|
perf_log_fatal("creating memory context: %s",
|
|
isc_result_totext(result));
|
|
|
|
ctx = isc_mem_get(mctx, sizeof(*ctx));
|
|
if (ctx == NULL)
|
|
perf_log_fatal("out of memory");
|
|
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
ctx->mctx = mctx;
|
|
|
|
result = dns_compress_init(&ctx->compress, 0, ctx->mctx);
|
|
if (result != ISC_R_SUCCESS) {
|
|
perf_log_fatal("creating compression context: %s",
|
|
isc_result_totext(result));
|
|
}
|
|
dns_compress_setmethods(&ctx->compress, DNS_COMPRESS_GLOBAL14);
|
|
|
|
result = isc_lex_create(ctx->mctx, 1024, &ctx->lexer);
|
|
if (result != ISC_R_SUCCESS) {
|
|
perf_log_fatal("creating lexer: %s",
|
|
isc_result_totext(result));
|
|
}
|
|
|
|
return (ctx);
|
|
}
|
|
|
|
void
|
|
perf_dns_destroyctx(perf_dnsctx_t **ctxp)
|
|
{
|
|
perf_dnsctx_t *ctx;
|
|
isc_mem_t *mctx;
|
|
|
|
INSIST(ctxp != NULL);
|
|
ctx = *ctxp;
|
|
*ctxp = NULL;
|
|
|
|
if (ctx == NULL)
|
|
return;
|
|
|
|
mctx = ctx->mctx;
|
|
isc_lex_destroy(&ctx->lexer);
|
|
dns_compress_invalidate(&ctx->compress);
|
|
isc_mem_put(mctx, ctx, sizeof(*ctx));
|
|
isc_mem_destroy(&mctx);
|
|
}
|
|
|
|
static isc_result_t
|
|
name_fromstring(dns_name_t *name, dns_name_t *origin,
|
|
const char *str, unsigned int len,
|
|
isc_buffer_t *target, const char *type)
|
|
{
|
|
isc_buffer_t buffer;
|
|
isc_result_t result;
|
|
|
|
isc_buffer_init(&buffer, str, len);
|
|
isc_buffer_add(&buffer, len);
|
|
result = dns_name_fromtext(name, &buffer, origin, 0, target);
|
|
if (result != ISC_R_SUCCESS)
|
|
perf_log_warning("invalid %s name: %.*s", type, (int)len, str);
|
|
return result;
|
|
}
|
|
|
|
#define SET_KEY(key, type) do { \
|
|
(key)->alg.base = TSIG_HMAC ## type ## _NAME; \
|
|
(key)->alg.length = sizeof(TSIG_HMAC ## type ## _NAME); \
|
|
(key)->hmactype = TSIG_HMAC ## type; \
|
|
(key)->digestlen = ISC_ ## type ## _DIGESTLENGTH; \
|
|
} while (0)
|
|
|
|
perf_dnstsigkey_t *
|
|
perf_dns_parsetsigkey(const char *arg, isc_mem_t *mctx)
|
|
{
|
|
perf_dnstsigkey_t *tsigkey;
|
|
const char *sep1, *sep2, *alg, *name, *secret;
|
|
int alglen, namelen;
|
|
isc_result_t result;
|
|
|
|
tsigkey = isc_mem_get(mctx, sizeof (*tsigkey));
|
|
if (tsigkey == NULL)
|
|
perf_log_fatal("out of memory");
|
|
memset(tsigkey, 0, sizeof (*tsigkey));
|
|
tsigkey->mctx = mctx;
|
|
|
|
sep1 = strchr(arg, ':');
|
|
if (sep1 == NULL) {
|
|
perf_log_warning("invalid TSIG [alg:]name:secret");
|
|
perf_opt_usage();
|
|
exit(1);
|
|
}
|
|
|
|
sep2 = strchr(sep1 + 1, ':');
|
|
if (sep2 == NULL) {
|
|
/* name:key */
|
|
alg = NULL;
|
|
alglen = 0;
|
|
name = arg;
|
|
namelen = sep1 - arg;
|
|
secret = sep1 + 1;
|
|
} else {
|
|
/* [alg:]name:secret */
|
|
alg = arg;
|
|
alglen = sep1 - arg;
|
|
name = sep1 + 1;
|
|
namelen = sep2 - sep1 - 1;
|
|
secret = sep2 + 1;
|
|
}
|
|
|
|
/* Algorithm */
|
|
|
|
if (alg == NULL || strncasecmp(alg, "hmac-md5:", 9) == 0) {
|
|
SET_KEY(tsigkey, MD5);
|
|
} else if (strncasecmp(alg, "hmac-sha1:", 10) == 0) {
|
|
SET_KEY(tsigkey, SHA1);
|
|
} else if (strncasecmp(alg, "hmac-sha224:", 12) == 0) {
|
|
SET_KEY(tsigkey, SHA224);
|
|
} else if (strncasecmp(alg, "hmac-sha256:", 12) == 0) {
|
|
SET_KEY(tsigkey, SHA256);
|
|
} else if (strncasecmp(alg, "hmac-sha384:", 12) == 0) {
|
|
SET_KEY(tsigkey, SHA384);
|
|
} else if (strncasecmp(alg, "hmac-sha512:", 12) == 0) {
|
|
SET_KEY(tsigkey, SHA512);
|
|
} else {
|
|
perf_log_warning("invalid TSIG algorithm %.*s", alglen, alg);
|
|
perf_opt_usage();
|
|
exit(1);
|
|
}
|
|
|
|
/* Name */
|
|
|
|
dns_fixedname_init(&tsigkey->fname);
|
|
tsigkey->name = dns_fixedname_name(&tsigkey->fname);
|
|
result = name_fromstring(tsigkey->name, dns_rootname, name, namelen,
|
|
NULL, "TSIG key");
|
|
if (result != ISC_R_SUCCESS) {
|
|
perf_opt_usage();
|
|
exit(1);
|
|
}
|
|
(void)dns_name_downcase(tsigkey->name, tsigkey->name, NULL);
|
|
|
|
/* Secret */
|
|
|
|
isc_buffer_init(&tsigkey->secret, tsigkey->secretdata,
|
|
sizeof(tsigkey->secretdata));
|
|
result = isc_base64_decodestring(secret, &tsigkey->secret);
|
|
if (result != ISC_R_SUCCESS) {
|
|
perf_log_warning("invalid TSIG secret '%s'", secret);
|
|
perf_opt_usage();
|
|
exit(1);
|
|
}
|
|
|
|
return tsigkey;
|
|
}
|
|
|
|
void
|
|
perf_dns_destroytsigkey(perf_dnstsigkey_t **tsigkeyp)
|
|
{
|
|
perf_dnstsigkey_t *tsigkey;
|
|
|
|
INSIST(tsigkeyp != NULL && *tsigkeyp != NULL);
|
|
|
|
tsigkey = *tsigkeyp;
|
|
*tsigkeyp = NULL;
|
|
|
|
isc_mem_put(tsigkey->mctx, tsigkey, sizeof(*tsigkey));
|
|
}
|
|
|
|
/*
|
|
* Appends an OPT record to the packet.
|
|
*/
|
|
static isc_result_t
|
|
add_edns(isc_buffer_t *packet, isc_boolean_t dnssec) {
|
|
unsigned char *base;
|
|
|
|
if (isc_buffer_availablelength(packet) < EDNSLEN) {
|
|
perf_log_warning("failed to add OPT to query packet");
|
|
return (ISC_R_NOSPACE);
|
|
}
|
|
|
|
base = isc_buffer_base(packet);
|
|
|
|
isc_buffer_putuint8(packet, 0); /* root name */
|
|
isc_buffer_putuint16(packet, dns_rdatatype_opt);/* type */
|
|
isc_buffer_putuint16(packet, MAX_EDNS_PACKET); /* class */
|
|
isc_buffer_putuint8(packet, 0); /* xrcode */
|
|
isc_buffer_putuint8(packet, 0); /* version */
|
|
if (dnssec) /* flags */
|
|
isc_buffer_putuint16(packet, 0x8000);
|
|
else
|
|
isc_buffer_putuint16(packet, 0);
|
|
isc_buffer_putuint16(packet, 0); /* rdlen */
|
|
|
|
base[11]++; /* increment record count */
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static void
|
|
hmac_init(perf_dnstsigkey_t *tsigkey, hmac_ctx_t *ctx)
|
|
{
|
|
unsigned char *secret;
|
|
unsigned int length;
|
|
|
|
secret = isc_buffer_base(&tsigkey->secret);
|
|
length = isc_buffer_usedlength(&tsigkey->secret);
|
|
|
|
switch (tsigkey->hmactype) {
|
|
case TSIG_HMACMD5:
|
|
isc_hmacmd5_init(&ctx->hmacmd5, secret, length);
|
|
break;
|
|
case TSIG_HMACSHA1:
|
|
isc_hmacsha1_init(&ctx->hmacsha1, secret, length);
|
|
break;
|
|
case TSIG_HMACSHA224:
|
|
isc_hmacsha224_init(&ctx->hmacsha224, secret, length);
|
|
break;
|
|
case TSIG_HMACSHA256:
|
|
isc_hmacsha256_init(&ctx->hmacsha256, secret, length);
|
|
break;
|
|
case TSIG_HMACSHA384:
|
|
isc_hmacsha384_init(&ctx->hmacsha384, secret, length);
|
|
break;
|
|
case TSIG_HMACSHA512:
|
|
isc_hmacsha512_init(&ctx->hmacsha512, secret, length);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
hmac_update(perf_dnstsigkey_t *tsigkey, hmac_ctx_t *ctx,
|
|
unsigned char *data, unsigned int length)
|
|
{
|
|
switch (tsigkey->hmactype) {
|
|
case TSIG_HMACMD5:
|
|
isc_hmacmd5_update(&ctx->hmacmd5, data, length);
|
|
break;
|
|
case TSIG_HMACSHA1:
|
|
isc_hmacsha1_update(&ctx->hmacsha1, data, length);
|
|
break;
|
|
case TSIG_HMACSHA224:
|
|
isc_hmacsha224_update(&ctx->hmacsha224, data, length);
|
|
break;
|
|
case TSIG_HMACSHA256:
|
|
isc_hmacsha256_update(&ctx->hmacsha256, data, length);
|
|
break;
|
|
case TSIG_HMACSHA384:
|
|
isc_hmacsha384_update(&ctx->hmacsha384, data, length);
|
|
break;
|
|
case TSIG_HMACSHA512:
|
|
isc_hmacsha512_update(&ctx->hmacsha512, data, length);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
hmac_sign(perf_dnstsigkey_t *tsigkey, hmac_ctx_t *ctx, unsigned char *digest,
|
|
unsigned int digestlen)
|
|
{
|
|
switch (tsigkey->hmactype) {
|
|
case TSIG_HMACMD5:
|
|
isc_hmacmd5_sign(&ctx->hmacmd5, digest);
|
|
break;
|
|
case TSIG_HMACSHA1:
|
|
isc_hmacsha1_sign(&ctx->hmacsha1, digest, digestlen);
|
|
break;
|
|
case TSIG_HMACSHA224:
|
|
isc_hmacsha224_sign(&ctx->hmacsha224, digest, digestlen);
|
|
break;
|
|
case TSIG_HMACSHA256:
|
|
isc_hmacsha256_sign(&ctx->hmacsha256, digest, digestlen);
|
|
break;
|
|
case TSIG_HMACSHA384:
|
|
isc_hmacsha384_sign(&ctx->hmacsha384, digest, digestlen);
|
|
break;
|
|
case TSIG_HMACSHA512:
|
|
isc_hmacsha512_sign(&ctx->hmacsha512, digest, digestlen);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Appends a TSIG record to the packet.
|
|
*/
|
|
static isc_result_t
|
|
add_tsig(isc_buffer_t *packet, perf_dnstsigkey_t *tsigkey)
|
|
{
|
|
unsigned char *base;
|
|
hmac_ctx_t hmac;
|
|
isc_region_t name_r;
|
|
isc_region_t *alg_r;
|
|
unsigned int rdlen, totallen;
|
|
unsigned char tmpdata[512];
|
|
isc_buffer_t tmp;
|
|
isc_uint32_t now;
|
|
unsigned char digest[ISC_SHA256_DIGESTLENGTH];
|
|
|
|
hmac_init(tsigkey, &hmac);
|
|
now = time(NULL);
|
|
dns_name_toregion(tsigkey->name, &name_r);
|
|
alg_r = (isc_region_t *) &tsigkey->alg;
|
|
|
|
/* Make sure everything will fit */
|
|
rdlen = tsigkey->alglen + 16 + tsigkey->digestlen;
|
|
totallen = name_r.length + 10 + rdlen;
|
|
if (totallen > isc_buffer_availablelength(packet)) {
|
|
perf_log_warning("adding TSIG: out of space");
|
|
return (ISC_R_NOSPACE);
|
|
}
|
|
|
|
base = isc_buffer_base(packet);
|
|
|
|
/* Digest the message */
|
|
hmac_update(tsigkey, &hmac, isc_buffer_base(packet),
|
|
isc_buffer_usedlength(packet));
|
|
|
|
/* Digest the TSIG record */
|
|
isc_buffer_init(&tmp, tmpdata, sizeof tmpdata);
|
|
isc_buffer_copyregion(&tmp, &name_r); /* name */
|
|
isc_buffer_putuint16(&tmp, dns_rdataclass_any); /* class */
|
|
isc_buffer_putuint32(&tmp, 0); /* ttl */
|
|
isc_buffer_copyregion(&tmp, alg_r); /* alg */
|
|
isc_buffer_putuint16(&tmp, 0); /* time high */
|
|
isc_buffer_putuint32(&tmp, now); /* time low */
|
|
isc_buffer_putuint16(&tmp, 300); /* fudge */
|
|
isc_buffer_putuint16(&tmp, 0); /* error */
|
|
isc_buffer_putuint16(&tmp, 0); /* other length */
|
|
hmac_update(tsigkey, &hmac, isc_buffer_base(&tmp),
|
|
isc_buffer_usedlength(&tmp));
|
|
hmac_sign(tsigkey, &hmac, digest, tsigkey->digestlen);
|
|
|
|
/* Add the TSIG record. */
|
|
isc_buffer_copyregion(packet, &name_r); /* name */
|
|
isc_buffer_putuint16(packet, dns_rdatatype_tsig); /* type */
|
|
isc_buffer_putuint16(packet, dns_rdataclass_any); /* class */
|
|
isc_buffer_putuint32(packet, 0); /* ttl */
|
|
isc_buffer_putuint16(packet, rdlen); /* rdlen */
|
|
isc_buffer_copyregion(packet, alg_r); /* alg */
|
|
isc_buffer_putuint16(packet, 0); /* time high */
|
|
isc_buffer_putuint32(packet, now); /* time low */
|
|
isc_buffer_putuint16(packet, 300); /* fudge */
|
|
isc_buffer_putuint16(packet, tsigkey->digestlen); /* digest len */
|
|
isc_buffer_putmem(packet, digest, tsigkey->digestlen); /* digest */
|
|
isc_buffer_putmem(packet, base, 2); /* orig ID */
|
|
isc_buffer_putuint16(packet, 0); /* error */
|
|
isc_buffer_putuint16(packet, 0); /* other len */
|
|
|
|
base[11]++; /* increment record count */
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static isc_result_t
|
|
build_query(const isc_textregion_t *line, isc_buffer_t *msg)
|
|
{
|
|
char *domain_str;
|
|
int domain_len;
|
|
dns_name_t name;
|
|
dns_offsets_t offsets;
|
|
isc_textregion_t qtype_r;
|
|
dns_rdatatype_t qtype;
|
|
isc_result_t result;
|
|
|
|
domain_str = line->base;
|
|
domain_len = strcspn(line->base, WHITESPACE);
|
|
|
|
qtype_r.base = line->base + domain_len;
|
|
while (isspace(*qtype_r.base & 0xff))
|
|
qtype_r.base++;
|
|
qtype_r.length = strcspn(qtype_r.base, WHITESPACE);
|
|
|
|
/* Create the question section */
|
|
DNS_NAME_INIT(&name, offsets);
|
|
result = name_fromstring(&name, dns_rootname, domain_str, domain_len,
|
|
msg, "domain");
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
if (qtype_r.length == 0) {
|
|
perf_log_warning("invalid query input format: %s", line->base);
|
|
return (ISC_R_FAILURE);
|
|
}
|
|
result = dns_rdatatype_fromtext(&qtype, &qtype_r);
|
|
if (result != ISC_R_SUCCESS) {
|
|
perf_log_warning("invalid query type: %.*s",
|
|
(int) qtype_r.length, qtype_r.base);
|
|
return (ISC_R_FAILURE);
|
|
}
|
|
|
|
isc_buffer_putuint16(msg, qtype);
|
|
isc_buffer_putuint16(msg, dns_rdataclass_in);
|
|
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
static isc_boolean_t
|
|
token_equals(const isc_textregion_t *token, const char *str)
|
|
{
|
|
return (strlen(str) == token->length &&
|
|
strncasecmp(str, token->base, token->length) == 0);
|
|
}
|
|
|
|
/*
|
|
* Reads one line containing an individual update for a dynamic update message.
|
|
*/
|
|
static isc_result_t
|
|
read_update_line(perf_dnsctx_t *ctx, const isc_textregion_t *line, char *str,
|
|
dns_name_t *zname, int want_ttl, int need_type,
|
|
int want_rdata, int need_rdata, dns_name_t *name,
|
|
isc_uint32_t *ttlp, dns_rdatatype_t *typep,
|
|
dns_rdata_t *rdata, isc_buffer_t *rdatabuf)
|
|
{
|
|
char *curr_str;
|
|
unsigned int curr_len;
|
|
isc_buffer_t buffer;
|
|
isc_textregion_t src;
|
|
dns_rdatacallbacks_t callbacks;
|
|
isc_result_t result;
|
|
|
|
while (isspace(*str & 0xff))
|
|
str++;
|
|
|
|
/* Read the owner name */
|
|
curr_str = str;
|
|
curr_len = strcspn(curr_str, WHITESPACE);
|
|
result = name_fromstring(name, zname, curr_str, curr_len, NULL,
|
|
"owner");
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
str += curr_len;
|
|
while (isspace(*str & 0xff))
|
|
str++;
|
|
|
|
/* Read the ttl */
|
|
if (want_ttl) {
|
|
curr_str = str;
|
|
curr_len = strcspn(curr_str, WHITESPACE);
|
|
src.base = curr_str;
|
|
src.length = curr_len;
|
|
result = dns_ttl_fromtext(&src, ttlp);
|
|
if (result != ISC_R_SUCCESS) {
|
|
perf_log_warning("invalid ttl: %.*s",
|
|
curr_len, curr_str);
|
|
return (result);
|
|
}
|
|
str += curr_len;
|
|
while (isspace(*str & 0xff))
|
|
str++;
|
|
}
|
|
|
|
/* Read the type */
|
|
curr_str = str;
|
|
curr_len = strcspn(curr_str, WHITESPACE);
|
|
if (curr_len == 0) {
|
|
if (!need_type)
|
|
return (ISC_R_SUCCESS);
|
|
perf_log_warning("invalid update command: %s", line->base);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
src.base = curr_str;
|
|
src.length = curr_len;
|
|
result = dns_rdatatype_fromtext(typep, &src);
|
|
if (result != ISC_R_SUCCESS) {
|
|
perf_log_warning("invalid type: %.*s", curr_len, curr_str);
|
|
return (result);
|
|
}
|
|
str += curr_len;
|
|
while (isspace(*str & 0xff))
|
|
str++;
|
|
|
|
/* Read the rdata */
|
|
if (!want_rdata)
|
|
return (ISC_R_SUCCESS);
|
|
|
|
if (*str == 0) {
|
|
if (!need_rdata)
|
|
return (ISC_R_SUCCESS);
|
|
perf_log_warning("invalid update command: %s\n", line->base);
|
|
return (ISC_R_FAILURE);
|
|
}
|
|
|
|
isc_buffer_init(&buffer, str, strlen(str));
|
|
isc_buffer_add(&buffer, strlen(str));
|
|
result = isc_lex_openbuffer(ctx->lexer, &buffer);
|
|
if (result != ISC_R_SUCCESS) {
|
|
perf_log_warning("setting up lexer: %s",
|
|
isc_result_totext(result));
|
|
return (result);
|
|
}
|
|
dns_rdatacallbacks_init_stdio(&callbacks);
|
|
result = dns_rdata_fromtext(rdata, dns_rdataclass_in, *typep,
|
|
ctx->lexer, zname, 0, ctx->mctx, rdatabuf,
|
|
&callbacks);
|
|
(void)isc_lex_close(ctx->lexer);
|
|
if (result != ISC_R_SUCCESS) {
|
|
perf_log_warning("parsing rdata: %s", str);
|
|
return (result);
|
|
}
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* Reads a complete dynamic update message and sends it.
|
|
*/
|
|
static isc_result_t
|
|
build_update(perf_dnsctx_t *ctx, const isc_textregion_t *record,
|
|
isc_buffer_t *msg)
|
|
{
|
|
isc_textregion_t input;
|
|
char *msgbase;
|
|
isc_buffer_t rdlenbuf, rdatabuf;
|
|
unsigned char rdataarray[MAX_RDATA_LENGTH];
|
|
isc_textregion_t token;
|
|
char *str;
|
|
isc_boolean_t is_update;
|
|
int updates = 0;
|
|
int prereqs = 0;
|
|
dns_fixedname_t fzname, foname;
|
|
dns_name_t *zname, *oname;
|
|
isc_uint32_t ttl;
|
|
dns_rdatatype_t rdtype;
|
|
dns_rdataclass_t rdclass;
|
|
dns_rdata_t rdata;
|
|
isc_uint16_t rdlen;
|
|
isc_result_t result;
|
|
|
|
/* Reset compression context */
|
|
dns_compress_rollback(&ctx->compress, 0);
|
|
|
|
input = *record;
|
|
msgbase = isc_buffer_base(msg);
|
|
|
|
/* Initialize */
|
|
|
|
dns_fixedname_init(&foname);
|
|
oname = dns_fixedname_name(&foname);
|
|
|
|
/* Parse zone name */
|
|
dns_fixedname_init(&fzname);
|
|
zname = dns_fixedname_name(&fzname);
|
|
result = name_fromstring(zname, dns_rootname,
|
|
input.base, strlen(input.base),
|
|
NULL, "zone");
|
|
if (result != ISC_R_SUCCESS)
|
|
goto done;
|
|
|
|
/* Render zone section */
|
|
result = dns_name_towire(zname, &ctx->compress, msg);
|
|
if (result != ISC_R_SUCCESS) {
|
|
perf_log_warning("error rendering zone name: %s",
|
|
isc_result_totext(result));
|
|
goto done;
|
|
}
|
|
isc_buffer_putuint16(msg, dns_rdatatype_soa);
|
|
isc_buffer_putuint16(msg, dns_rdataclass_in);
|
|
|
|
while (ISC_TRUE) {
|
|
input.base += strlen(input.base) + 1;
|
|
if (input.base >= record->base + record->length) {
|
|
perf_log_warning("warning: incomplete update");
|
|
goto done;
|
|
}
|
|
|
|
ttl = 0;
|
|
rdtype = dns_rdatatype_any;
|
|
isc_buffer_init(&rdatabuf, rdataarray, sizeof(rdataarray));
|
|
dns_rdata_init(&rdata);
|
|
rdlen = 0;
|
|
rdclass = dns_rdataclass_in;
|
|
is_update = ISC_FALSE;
|
|
|
|
token.base = input.base;
|
|
token.length = strcspn(token.base, WHITESPACE);
|
|
str = input.base + token.length;
|
|
if (token_equals(&token, "send")) {
|
|
break;
|
|
} else if (token_equals(&token, "add")) {
|
|
result = read_update_line(ctx, &input, str, zname,
|
|
ISC_TRUE,
|
|
ISC_TRUE, ISC_TRUE, ISC_TRUE,
|
|
oname, &ttl, &rdtype,
|
|
&rdata, &rdatabuf);
|
|
rdclass = dns_rdataclass_in;
|
|
is_update = ISC_TRUE;
|
|
} else if (token_equals(&token, "delete")) {
|
|
result = read_update_line(ctx, &input, str, zname,
|
|
ISC_FALSE,
|
|
ISC_FALSE, ISC_TRUE,
|
|
ISC_FALSE, oname, &ttl,
|
|
&rdtype, &rdata, &rdatabuf);
|
|
if (isc_buffer_usedlength(&rdatabuf) > 0)
|
|
rdclass = dns_rdataclass_none;
|
|
else
|
|
rdclass = dns_rdataclass_any;
|
|
is_update = ISC_TRUE;
|
|
} else if (token_equals(&token, "require")) {
|
|
result = read_update_line(ctx, &input, str, zname,
|
|
ISC_FALSE,
|
|
ISC_FALSE, ISC_TRUE,
|
|
ISC_FALSE, oname, &ttl,
|
|
&rdtype, &rdata, &rdatabuf);
|
|
if (isc_buffer_usedlength(&rdatabuf) > 0)
|
|
rdclass = dns_rdataclass_in;
|
|
else
|
|
rdclass = dns_rdataclass_any;
|
|
is_update = ISC_FALSE;
|
|
} else if (token_equals(&token, "prohibit")) {
|
|
result = read_update_line(ctx, &input, str, zname,
|
|
ISC_FALSE,
|
|
ISC_FALSE, ISC_FALSE,
|
|
ISC_FALSE, oname, &ttl,
|
|
&rdtype, &rdata, &rdatabuf);
|
|
rdclass = dns_rdataclass_none;
|
|
is_update = ISC_FALSE;
|
|
} else {
|
|
perf_log_warning("invalid update command: %s",
|
|
input.base);
|
|
result = ISC_R_FAILURE;
|
|
}
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
goto done;
|
|
|
|
if (!is_update && updates > 0) {
|
|
perf_log_warning("prereqs must precede updates");
|
|
result = ISC_R_FAILURE;
|
|
goto done;
|
|
}
|
|
|
|
/* Render record */
|
|
result = dns_name_towire(oname, &ctx->compress, msg);
|
|
if (result != ISC_R_SUCCESS) {
|
|
perf_log_warning("rendering record name: %s",
|
|
isc_result_totext(result));
|
|
goto done;
|
|
}
|
|
if (isc_buffer_availablelength(msg) < 10) {
|
|
perf_log_warning("out of space in message buffer");
|
|
result = ISC_R_NOSPACE;
|
|
goto done;
|
|
}
|
|
|
|
isc_buffer_putuint16(msg, rdtype);
|
|
isc_buffer_putuint16(msg, rdclass);
|
|
isc_buffer_putuint32(msg, ttl);
|
|
rdlenbuf = *msg;
|
|
isc_buffer_putuint16(msg, 0); /* rdlen */
|
|
rdlen = isc_buffer_usedlength(&rdatabuf);
|
|
if (rdlen > 0) {
|
|
result = dns_rdata_towire(&rdata, &ctx->compress, msg);
|
|
if (result != ISC_R_SUCCESS) {
|
|
perf_log_warning("rendering rdata: %s",
|
|
isc_result_totext(result));
|
|
goto done;
|
|
}
|
|
rdlen = msg->used - rdlenbuf.used - 2;
|
|
isc_buffer_putuint16(&rdlenbuf, rdlen);
|
|
}
|
|
if (is_update)
|
|
updates++;
|
|
else
|
|
prereqs++;
|
|
}
|
|
|
|
msgbase[7] = prereqs; /* ANCOUNT = number of prereqs */
|
|
msgbase[9] = updates; /* AUCOUNT = number of updates */
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
done:
|
|
return result;
|
|
}
|
|
|
|
isc_result_t
|
|
perf_dns_buildrequest(perf_dnsctx_t *ctx, const isc_textregion_t *record,
|
|
isc_uint16_t qid,
|
|
isc_boolean_t edns, isc_boolean_t dnssec,
|
|
perf_dnstsigkey_t *tsigkey, isc_buffer_t *msg)
|
|
{
|
|
unsigned int flags;
|
|
isc_result_t result;
|
|
|
|
if (ctx != NULL)
|
|
flags = dns_opcode_update << 11;
|
|
else
|
|
flags = DNS_MESSAGEFLAG_RD;
|
|
|
|
/* Create the DNS packet header */
|
|
isc_buffer_putuint16(msg, qid);
|
|
isc_buffer_putuint16(msg, flags); /* flags */
|
|
isc_buffer_putuint16(msg, 1); /* qdcount */
|
|
isc_buffer_putuint16(msg, 0); /* ancount */
|
|
isc_buffer_putuint16(msg, 0); /* aucount */
|
|
isc_buffer_putuint16(msg, 0); /* arcount */
|
|
|
|
if (ctx != NULL) {
|
|
result = build_update(ctx, record, msg);
|
|
} else {
|
|
result = build_query(record, msg);
|
|
}
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
if (edns) {
|
|
result = add_edns(msg, dnssec);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
}
|
|
|
|
if (tsigkey != NULL) {
|
|
result = add_tsig(msg, tsigkey);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
}
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|