aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2020-08-26 13:37:03 +0300
committerSergey Poznyakoff <gray@gnu.org>2020-08-26 13:37:03 +0300
commit8c046209f3739928a3b7a05656a5f2be843e2f62 (patch)
treeee5c369f2fd449f115b791c13ea762d33c7fd683
parent8ab3fbee8449d709d07654f67d7c24e0a72dffd0 (diff)
downloadnsu-8c046209f3739928a3b7a05656a5f2be843e2f62.tar.gz
nsu-8c046209f3739928a3b7a05656a5f2be843e2f62.tar.bz2
Implement TSIG
* Makefile: Link with libnettle * main.c (find_ns): Fill array of struct sockaddr_in. (free_ns): Remove. (tsig_alg_name,tsig_alg_digest_size) (nsu_signer_set_key,nsu_signer_update) (nsu_signer_digest,nsu_signer_free): New functions. (main): New option -k. (run_update): Try next server from the list if the current return is ns_r_notimpl or ns_r_servfail. * nsu.c (nsu_nmkquery): Implement signing algorithm as per RFC 2845. * nsu.h (nsu_signer): New struct. (tsig_alg_name,tsig_alg_digest_size) (nsu_signer_set_key,nsu_signer_update) (nsu_signer_digest,nsu_signer_free): New protos. (nsu_nmkquery,nsu_mkquery): Change signature. * lexer.l: Fix improper calls to yyerror. * nsu_app.h: Add missing prototype. * typestr.c: Add missing includes.
-rw-r--r--Makefile2
-rw-r--r--lexer.l4
-rw-r--r--main.c573
-rw-r--r--nsu.c205
-rw-r--r--nsu.h20
-rw-r--r--nsu_app.h6
-rw-r--r--typestr.c1
7 files changed, 716 insertions, 95 deletions
diff --git a/Makefile b/Makefile
index 493bbd8..73d4d4a 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ NSUSRC=nsu.c typestr.c
NSUOBJ=${NSUSRC: .c=.o}
nsu: main.o $(NSUOBJ) $(APPOBJ)
- cc $(CFLAGS) -o nsu $(NSUOBJ) $(APPOBJ) -lresolv
+ cc $(CFLAGS) -o nsu $(NSUOBJ) $(APPOBJ) -lresolv -lnettle
$(NSUOBJ) $(APPOBJ): nsu.h
diff --git a/lexer.l b/lexer.l
index 97d240c..fedd2e1 100644
--- a/lexer.l
+++ b/lexer.l
@@ -280,7 +280,7 @@ a_parser(nsu_rr *rr)
if (getaddrinfo(yylval.s, NULL, &hints, &res)
|| res->ai_addrlen != sizeof(struct sockaddr_in)) {
- yyerror("%s: invalid IP address", yylval.s);
+ abend("%s: invalid IP address", yylval.s);
return RDATA_PARSE_ERR;
}
@@ -378,7 +378,7 @@ mx_parser(nsu_rr *rr)
return RDATA_PARSE_ERR;
default:
- yyerror("expected domain-name, but found %s", yytext);
+ abend("expected domain-name, but found %s", yytext);
yyless(0);
BEGIN(INITIAL);
return RDATA_PARSE_ERR;
diff --git a/main.c b/main.c
index c0acf25..2038a89 100644
--- a/main.c
+++ b/main.c
@@ -12,12 +12,22 @@
#include <arpa/nameser.h>
#include <resolv.h>
#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <nettle/md5.h>
+#include <nettle/sha1.h>
+#include <nettle/sha2.h>
+#include <nettle/hmac.h>
+#include <nettle/base64.h>
#include "nsu.h"
#include "nsu_app.h"
char const *progname;
unsigned long default_ttl;
char const *domain_name;
+struct sockaddr_in *server_addr;
+char const domain_service[] = "53";
+struct nsu_signer *tsig;
void
vdiagprt(char const *fmt, va_list ap)
@@ -91,15 +101,54 @@ ns_rcodestr(int code)
}
void
-ns_add(char **nstab, int *nsnum, ns_msg handle, ns_sect section)
+nstab_add(ns_msg handle, ns_rr rr, struct sockaddr_in *nstab, int *nsnum)
+{
+ char name[MAXDNAME];
+ struct addrinfo hints, *addr, *ap;
+
+ if (ns_name_uncompress(ns_msg_base(handle),
+ ns_msg_end(handle),
+ ns_rr_rdata(rr),
+ name,
+ sizeof(name)) < 0) {
+ abend("ns_name_uncompress failed");
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+
+ if (getaddrinfo(name, domain_service, &hints, &addr)) {
+ abend("%s: invalid IP address", name);
+ }
+
+ for (ap = addr; ap && *nsnum < MAX_NS; ap = ap->ai_next) {
+ if (ap->ai_addrlen == sizeof(struct sockaddr_in)) {
+ int i;
+
+ for (i = 0; i < *nsnum; i++) {
+ if (!memcmp(&nstab[i], ap->ai_addr,
+ ap->ai_addrlen)) {
+ goto next;
+ }
+ }
+ memcpy(&nstab[*nsnum], ap->ai_addr, ap->ai_addrlen);
+ ++ *nsnum;
+ }
+ next:
+ continue;
+ }
+ freeaddrinfo(addr);
+}
+
+void
+ns_add(struct sockaddr_in *nstab, int *nsnum, ns_msg handle, ns_sect section)
{
int rrnum; /* resource record number */
- int n = *nsnum;
for (rrnum = 0; rrnum < ns_msg_count(handle, section); rrnum++) {
ns_rr rr;
- if (n == MAX_NS)
+ if (*nsnum == MAX_NS)
break;
if (ns_parserr(&handle, section, rrnum, &rr)) {
@@ -107,31 +156,9 @@ ns_add(char **nstab, int *nsnum, ns_msg handle, ns_sect section)
}
if (ns_rr_type(rr) == ns_t_ns) {
- int i;
- char *p = malloc(MAXDNAME);
- if (!p)
- abend_nomem();
- /* Expand the name server's domain name */
- if (ns_name_uncompress(ns_msg_base(handle),
- ns_msg_end(handle),
- ns_rr_rdata(rr),
- p,
- MAXDNAME) < 0) {
- abend("ns_name_uncompress failed");
- }
- for (i = 0; i < n; i++) {
- if (!strcasecmp(nstab[i], p)) {
- free(p);
- goto next;
- }
- }
-
- nstab[n++] = p;
- next:
- continue;
+ nstab_add(handle, rr, nstab, nsnum);
}
}
- *nsnum = n;
}
typedef union {
@@ -141,13 +168,14 @@ typedef union {
void
-ns_add_master(char const *domain, char **nstab, int *nsnum)
+ns_add_master(char const *domain, struct sockaddr_in *nstab, int *nsnum)
{
QUERYBUF response, query;
int rlen, qlen;
ns_msg handle;
ns_rr rr;
-
+ char name[MAXDNAME];
+
qlen = res_mkquery(ns_o_query, /* regular query */
domain, /* the zone to look up */
ns_c_in, /* Internet type */
@@ -204,24 +232,11 @@ ns_add_master(char const *domain, char **nstab, int *nsnum)
if (default_ttl == 0)
default_ttl = rr.ttl;
-
- char *p = malloc(MAXDNAME);
- if (!p)
- abend_nomem();
-
- if (ns_name_uncompress(ns_msg_base(handle),
- ns_msg_end(handle),
- ns_rr_rdata(rr),
- p,
- MAXDNAME) < 0) {
- abend("ns_name_uncompress failed");
- }
- nstab[*nsnum] = p;
- ++ *nsnum;
+ nstab_add(handle, rr, nstab, nsnum);
}
static int
-find_ns(char const *domain, char **nstab, int verbose)
+find_ns(char const *domain, struct sockaddr_in *nstab, int verbose)
{
int nscount = 0;
QUERYBUF response; /* response buffers */
@@ -263,14 +278,6 @@ find_ns(char const *domain, char **nstab, int verbose)
return nscount;
}
-
-static int
-free_ns(char **nstab, int nscount)
-{
- int i;
- for (i = 0; i < nscount; i++)
- free(nstab[i]);
-}
static void
set_server(char *name)
@@ -283,7 +290,7 @@ set_server(char *name)
if (service)
*service++ = 0;
else
- service = "53";
+ service = (char*)domain_service;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
@@ -292,6 +299,10 @@ set_server(char *name)
|| r->ai_addrlen != sizeof(struct sockaddr_in)) {
abend("%s: invalid IP address", name);
}
+ server_addr = malloc(r->ai_addrlen);
+ if (!server_addr)
+ abend_nomem();
+ memcpy(server_addr, r->ai_addr, r->ai_addrlen);
memcpy(&_res.nsaddr_list[0], r->ai_addr, r->ai_addrlen);
_res.nscount = 1;
freeaddrinfo(r);
@@ -309,14 +320,362 @@ get_arg_ul(char const *arg, unsigned long *np)
*np = n;
return 0;
}
+
+struct tsig_alg {
+ char *name;
+ int code;
+ size_t digest_size;
+ size_t ctx_size;
+ const struct nettle_hash *nhash;
+};
+
+char const *
+tsig_alg_name(struct tsig_alg const *alg)
+{
+ return alg->name;
+}
+
+size_t
+tsig_alg_digest_size(struct tsig_alg const *alg)
+{
+ return alg->digest_size;
+}
+
+enum { CTX_INNER, CTX_OUTER, CTX_STATE };
+
+static inline void *
+nsu_signer_ctx(struct nsu_signer *sgn, int n)
+{
+ return sgn->ctx + n * sgn->alg->ctx_size;
+}
+
+void
+nsu_signer_set_key(struct nsu_signer *sgn, u_char const *key, size_t len)
+{
+ hmac_set_key(nsu_signer_ctx(sgn, CTX_OUTER),
+ nsu_signer_ctx(sgn, CTX_INNER),
+ nsu_signer_ctx(sgn, CTX_STATE),
+ sgn->alg->nhash,
+ len, key);
+}
+
+void
+nsu_signer_update(struct nsu_signer *sgn, u_char const *data, size_t len)
+{
+ hmac_update(nsu_signer_ctx(sgn, CTX_STATE), sgn->alg->nhash, len, data);
+}
+
+void
+nsu_signer_digest(struct nsu_signer *sgn, u_char *data, size_t len)
+{
+ hmac_digest(nsu_signer_ctx(sgn, CTX_OUTER),
+ nsu_signer_ctx(sgn, CTX_INNER),
+ nsu_signer_ctx(sgn, CTX_STATE),
+ sgn->alg->nhash,
+ len, data);
+}
+
+#define CODE_END 0
+#define CODE_ALIAS (-1)
+
+static struct tsig_alg algtab[] = {
+ { .name = "HMAC-MD5.SIG-ALG.REG.INT",
+ .code = 157,
+ .digest_size = MD5_DIGEST_SIZE,
+ .ctx_size = sizeof(struct md5_ctx),
+ .nhash = &nettle_md5,
+ },
+ { .name = "HMAC-MD5",
+ .code = CODE_ALIAS,
+ },
+ { .name = "HMAC-SHA1",
+ .code = 161,
+ .digest_size = SHA1_DIGEST_SIZE,
+ .nhash = &nettle_sha1,
+ .ctx_size = sizeof(struct sha1_ctx),
+ },
+ { .name = "HMAC-SHA",
+ .code = CODE_ALIAS
+ },
+ { .name = "HMAC-SHA224",
+ .code = 162,
+ .digest_size = SHA224_DIGEST_SIZE,
+ .nhash = &nettle_sha224,
+ .ctx_size = sizeof(struct sha224_ctx),
+ },
+ { .name = "HMAC-SHA256",
+ .code = 163,
+ .digest_size = SHA256_DIGEST_SIZE,
+ .nhash = &nettle_sha256,
+ .ctx_size = sizeof(struct sha256_ctx),
+ },
+ { .name = "HMAC-SHA384",
+ .code = 164,
+ .digest_size = SHA384_DIGEST_SIZE,
+ .nhash = &nettle_sha384,
+ .ctx_size = sizeof(struct sha384_ctx),
+ },
+ { .name = "HMAC-SHA512",
+ .code = 165,
+ .digest_size = SHA512_DIGEST_SIZE,
+ .nhash = &nettle_sha512,
+ .ctx_size = sizeof(struct sha512_ctx),
+ },
+ { .code = CODE_END }
+};
+
+static struct tsig_alg const *
+alg_find_code(int code)
+{
+ struct tsig_alg *ap;
+ for (ap = algtab; ap->code != CODE_END; ap++)
+ if (ap->code == code)
+ return ap;
+ return NULL;
+}
+
+static int
+is_suffix(char const *suf, char const *str)
+{
+ size_t suflen = strlen(suf);
+ size_t len = strlen(str);
+ return suflen < len && memcmp(str + len - suflen, suf, suflen) == 0;
+}
+
+static char const *priv_suffix = ".private";
+static char const *key_suffix = ".key";
+
+static struct tsig_alg const *
+keyfile_read_private(char const *name, uint8_t **pkey, size_t *pkey_len)
+{
+ char *tmpname = NULL;
+ FILE *fp;
+ char buf[512];
+ int lno = 0;
+
+ struct tsig_alg const *alg = NULL;
+ uint8_t *key;
+ size_t key_len;
+
+ static char algorithm_field[] = "Algorithm";
+ static int algorithm_length = sizeof(algorithm_field) - 1;
+ static char key_field[] = "Key";
+ static int key_length = sizeof(key_field) - 1;
+
+ if (is_suffix(key_suffix, name)) {
+ size_t baselen = strlen(name) - strlen(key_suffix);
+ tmpname = malloc(baselen + strlen(priv_suffix) + 1);
+ if (!tmpname)
+ abend_nomem();
+ memcpy(tmpname, name, baselen);
+ strcpy(tmpname + baselen, priv_suffix);
+ name = tmpname;
+ } else if (access(name, F_OK) && errno == ENOENT) {
+ size_t namelen = strlen(name);
+ if (name[namelen-1] == '.') {
+ tmpname = malloc(namelen + strlen(priv_suffix));
+ if (!tmpname)
+ abend_nomem();
+ memcpy(tmpname, name, namelen - 1);
+ strcpy(tmpname + namelen - 1, priv_suffix);
+ name = tmpname;
+ }
+ }
+
+ fp = fopen(name, "r");
+ if (!fp)
+ abend("can't open %s: %s", name, strerror(errno));
+ while (fgets(buf, sizeof(buf), fp)) {
+ size_t len, n;
+ lno++;
+ len = strlen(buf);
+ if (len == 0)
+ break;
+ if (buf[len-1] != '\n')
+ abend("%s:%d: line too long", name, lno);
+ buf[--len] = '\n';
+ n = strcspn(buf, ":");
+ if (buf[n] == 0)
+ abend("%s:%d: malformed line", name, lno);
+ if (n == algorithm_length &&
+ memcmp(algorithm_field, buf, n) == 0) {
+ char *p;
+ long code;
+ errno = 0;
+ code = strtol(buf + n + 1, &p, 10);
+ if (errno || *p != ' ')
+ abend("%s:%d: malformed line", name, lno);
+
+ alg = alg_find_code(code);
+ if (!alg)
+ abend("%s:%d: unsupported algorithm %s",
+ name, lno, buf + n);
+ } else if (n == key_length && memcmp(key_field, buf, n) == 0) {
+ struct base64_decode_ctx ctx;
+ size_t src_len;
+ char *p;
+
+ p = buf + n + 1;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ src_len = strlen(p);
+ key_len = BASE64_DECODE_LENGTH(src_len);
+ key = malloc(key_len);
+ if (!key)
+ abend_nomem();
+
+ base64_decode_init(&ctx);
+ base64_decode_update(&ctx, &key_len, key, src_len, p);
+ if (!base64_decode_final(&ctx))
+ abend("%s:%d: bad base64", name, lno);
+ }
+ }
+ fclose(fp);
+
+ if (!alg)
+ abend("%s:%d: no %s clause", algorithm_field);
+ if (!key)
+ abend("%s:%d: no %s clause", key_field);
+
+ free(tmpname);
+
+ *pkey = key;
+ *pkey_len = key_len;
+ return alg;
+}
+
+static char *
+keyfile_read_key(char const *name, uint8_t **pkey, size_t *pkey_len)
+{
+ char *tmpname = NULL;
+ char buf[512];
+ char *ret, *keyptr;
+ size_t n;
+ uint8_t *key;
+ FILE *fp;
+ int i;
+ size_t src_len, key_len;
+ struct base64_decode_ctx ctx;
+
+ if (is_suffix(priv_suffix, name)) {
+ size_t baselen = strlen(name) - strlen(priv_suffix);
+ tmpname = malloc(baselen + strlen(key_suffix) + 1);
+ if (!tmpname)
+ abend_nomem();
+ memcpy(tmpname, name, baselen);
+ strcpy(tmpname + baselen, key_suffix);
+ name = tmpname;
+ } else if (access(name, F_OK) && errno == ENOENT) {
+ size_t namelen = strlen(name);
+ if (name[namelen-1] == '.') {
+ tmpname = malloc(namelen + strlen(key_suffix));
+ if (!tmpname)
+ abend_nomem();
+ memcpy(tmpname, name, namelen - 1);
+ strcpy(tmpname + namelen - 1, key_suffix);
+ name = tmpname;
+ }
+ }
+
+ fp = fopen(name, "r");
+ if (!fp)
+ abend("can't open %s: %s", name, strerror(errno));
+ if (!fgets(buf, sizeof(buf), fp))
+ abend("%s: read error", name);
+ fclose(fp);
+
+ n = strcspn(buf, " ");
+ if (strncmp(buf + n, " IN KEY", 7))
+ abend("%s:%d: malformed line", name, 1);
+
+ keyptr = buf + n + 7;
+ for (i = 0; i < 3; i++) {
+ while (*keyptr && isspace(*keyptr))
+ keyptr++;
+ while (*keyptr && isdigit(*keyptr))
+ keyptr++;
+ }
+ while (*keyptr && isspace(*keyptr))
+ keyptr++;
+
+ src_len = strlen(keyptr);
+ key_len = BASE64_DECODE_LENGTH(src_len);
+ key = malloc(key_len);
+ if (!key)
+ abend_nomem();
+
+ base64_decode_init(&ctx);
+ base64_decode_update(&ctx, &key_len, key, src_len, keyptr);
+ if (!base64_decode_final(&ctx))
+ abend("%s:%d: bad base64", name, 1);
+ *pkey = key;
+ *pkey_len = key_len;
+
+ if (buf[n-1] == '.')
+ --n;
+ ret = malloc(n+1);
+ if (!ret)
+ abend_nomem();
+ memcpy(ret, buf, n);
+ ret[n] = 0;
+
+ free(tmpname);
+
+ return ret;
+}
+
+static struct nsu_signer *
+keyfile_read(char const *name)
+{
+ struct tsig_alg const *alg;
+ uint8_t *pkey, *key;
+ size_t pkey_len, key_len;
+ char *keyname;
+ struct nsu_signer *sgn;
+
+ alg = keyfile_read_private(name, &pkey, &pkey_len);
+ keyname = keyfile_read_key(name, &key, &key_len);
+
+ if (pkey_len != key_len || memcmp(pkey, key, key_len))
+ abend("%s: keys don't match", name);
+ free(pkey);
+
+ sgn = calloc(1, sizeof(*sgn));
+ if (!sgn)
+ abend_nomem();
+ sgn->ctx = malloc(3 * alg->ctx_size);
+ if (!sgn->ctx)
+ abend_nomem();
+
+ sgn->name = keyname;
+ sgn->alg = alg;
+ sgn->fudge = 300; //FIXME
+
+ nsu_signer_set_key(sgn, key, key_len);
+ free(key);
+
+ return sgn;
+}
+
+void
+nsu_signer_free(struct nsu_signer *sgn)
+{
+ if (!sgn)
+ return;
+ free(sgn->name);
+ free(sgn->ctx);
+ free(sgn);
+}
+
/*
- * nsu DOMAIN LABEL TYPE TTL VALUE...
+ * nsu
*/
int
main(int argc, char **argv)
{
char *input_file = NULL;
+ char *keyfile_name = NULL;
int i;
nsu_rr upd;
char *p;
@@ -324,7 +683,7 @@ main(int argc, char **argv)
progname = argv[0];
res_init();
yy_flex_debug = 0;
- while ((i = getopt(argc, argv, "d:f:s:t:x")) != EOF) {
+ while ((i = getopt(argc, argv, "d:f:k:s:t:x")) != EOF) {
switch (i) {
case 'd':
domain_name = optarg;
@@ -333,6 +692,10 @@ main(int argc, char **argv)
case 'f':
input_file = optarg;
break;
+
+ case 'k':
+ keyfile_name = optarg;
+ break;
case 's':
set_server(optarg);
@@ -358,6 +721,9 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
+ if (keyfile_name)
+ tsig = keyfile_read(keyfile_name);
+
if (input_file)
lexer_from_file(input_file);
else if (argc > 0)
@@ -368,8 +734,14 @@ main(int argc, char **argv)
exit(0);
}
+enum {
+ update_fail = -1,
+ update_ok,
+ update_retry
+};
+
int
-send_update_query(char const *zone, u_char *qbuf, int qlen)
+send_update_query(res_state state, char const *zone, u_char *qbuf, int qlen)
{
union {
HEADER hdr;
@@ -379,54 +751,86 @@ send_update_query(char const *zone, u_char *qbuf, int qlen)
ns_msg handle;
int rcode;
- rlen = res_send(qbuf, qlen,
- (u_char *) &response,
- sizeof(response));
+ if (state->options & RES_DEBUG) {
+ char name[NS_MAXDNAME];
+
+ getnameinfo((struct sockaddr*)state->nsaddr_list,
+ sizeof(state->nsaddr_list[0]),
+ name, sizeof(name),
+ NULL, 0,
+ NI_NUMERICHOST);
+ printf(";; trying nameserver %s", name);
+ }
+
+ rlen = res_nsend(state, qbuf, qlen,
+ (u_char *) &response,
+ sizeof(response));
if (rlen < 0) {
if (errno == ECONNREFUSED) { /* no server on the host */
- panic("no name servers configured?");
+ panic("connection refused");
} else {
/* anything else: no response */
panic("no response");
}
- return 1;
+ return update_fail;
}
if (ns_initparse(response.buf, rlen, &handle) < 0) {
panic("ns_initparse: %s", strerror(errno));
- return 1;
+ return update_fail;
}
rcode = ns_msg_getflag(handle, ns_f_rcode);
switch (rcode) {
+/*
+ * If a response is received whose RCODE is SERVFAIL or NOTIMP, or
+ * if no response is received within an implementation dependent timeout
+ * period, or if an ICMP error is received indicating that the server's
+ * port is unreachable, then the requestor will delete the unusable
+ * server from its internal name server list and try the next one,
+ * repeating until the name server list is empty. If the requestor runs
+ * out of servers to try, an appropriate error will be returned to the
+ * requestor's caller.
+ */
+ case ns_r_notimpl:
+ case ns_r_servfail:
+ if (state->options & RES_DEBUG) {
+ char name[NS_MAXDNAME];
+
+ getnameinfo((struct sockaddr*)state->nsaddr_list,
+ sizeof(state->nsaddr_list[0]),
+ name, sizeof(name),
+ NULL, 0,
+ NI_NUMERICHOST);
+ printf(";; nameserver %s: %s", name,
+ ns_rcodestr(rcode));
+ }
+ return update_retry;
+
case ns_r_noerror:
break;
- case ns_r_yxdomain:
- case ns_r_yxrrset:
- case ns_r_nxrrset:
- panic("%s: %s", zone, ns_rcodestr(rcode));
- break;
-
default:
panic("%s: %s", zone, ns_rcodestr(rcode));
- return 1;
+ return update_fail;
}
- return 0;
+ return update_ok;
}
int
run_update(nsu_rr *prereq, int num_prereq,
nsu_rr *update, int num_update)
{
- char *nstab[MAX_NS];
+ struct sockaddr_in nstab[MAX_NS];
int nscount = 0;
int qlen;
QUERYBUF query;
char const *zone = NULL;
int rc;
-
+ int i;
+ struct __res_state rstate;
+
if (!domain_name) {
char *p = update->name;
@@ -446,17 +850,32 @@ run_update(nsu_rr *prereq, int num_prereq,
if (nscount == 0)
return -1;
}
-
+
+#if 0
+ /* Override the name servers */
+ if (server_addr) {
+ memcpy(nstab, server_addr, sizeof(nstab[0]));
+ nscount = 1;
+ }
+#endif
qlen = nsu_mkquery(zone,
prereq, num_prereq,
update, num_update,
- NULL,
+ tsig,
query.buf, sizeof(query.buf));
-
+
if (qlen < 0)
abend("failed to create update query");
- rc = send_update_query(zone, query.buf, qlen);
- free_ns(nstab, nscount);
- return rc;
+
+ res_ninit(&rstate);
+ rstate.nscount = 1;
+ for (i = 0; i < nscount; i++) {
+ memcpy(&rstate.nsaddr_list[0], nstab + i,
+ sizeof(rstate.nsaddr_list[0]));
+ rc = send_update_query(&rstate, zone, query.buf, qlen);
+ if (rc != update_retry)
+ break;
+ }
+ return rc == update_ok ? 0 : -1;
}
diff --git a/nsu.c b/nsu.c
index 7fec58e..beeef9d 100644
--- a/nsu.c
+++ b/nsu.c
@@ -2,6 +2,7 @@
#include <stdio.h>
#include <assert.h>
#include <string.h>
+#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
@@ -81,12 +82,42 @@ nsu_put_data(nsu_rr *updr, u_char *cp, int buflen,
return len;
}
+static inline char const *
+lcase(u_char const *dname, u_char *buf)
+{
+ static char uc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ size_t n = strcspn(dname, uc);
+
+ if (dname[n] == 0)
+ return dname;
+ else {
+ size_t i = 0;
+ u_char *p = buf;
+
+ while (1) {
+ memcpy(p, dname, n);
+ if (dname[n]) {
+ p[n] = tolower(dname[n]);
+ n++;
+ } else {
+ p[n] = 0;
+ break;
+ }
+ p += n;
+ dname += n;
+ n = strcspn(dname, uc);
+ }
+
+ return buf;
+ }
+}
+
int
nsu_nmkquery(res_state statp,
char const *zone,
nsu_rr *prereq, int num_prereq,
nsu_rr *update, int num_update,
- ns_rr *tsig,
+ struct nsu_signer *tsig,
u_char *buf, int buflen)
{
UPDATE_HEADER *hp;
@@ -130,8 +161,6 @@ nsu_nmkquery(res_state statp,
hp->zocount = htons(1);
hp->prcount = htons(num_prereq);
hp->upcount = htons(num_update);
- if (tsig)
- hp->adcount = htons(1);
cp = buf + HFIXEDSZ;
buflen -= HFIXEDSZ;
@@ -336,20 +365,172 @@ nsu_nmkquery(res_state statp,
}
if (tsig) {
- n = ns_name_compress(tsig->name, cp, buflen,
+ size_t msglen = cp - buf; /* Length of the original message */
+ size_t tsig_digest_len = tsig_alg_digest_size(tsig->alg);
+ struct timeval tv;
+ u_char lcbuf[NS_MAXDNAME]; /* Buffer for converting domain names
+ to lower case. */
+
+ u_char *tsig_name_ptr; /* Pointer to the start of the TSIG
+ name */
+ size_t tsig_name_len; /* Length of the TSIG name in wire
+ format */
+
+
+ /*
+ * According to RFC 2845 (2.3. Record Format), RDATA
+ * has the following fields:
+ *
+ * N Field Name Data Type Notes
+ * -----------------------------------------------------------
+ * 0 Algorithm Name domain-name Name of the algorithm
+ * in domain name syntax.
+ * 1 Time Signed u_int48_t seconds since 1-Jan-70 UTC.
+ * 2 Fudge u_int16_t seconds of error permitted
+ * in Time Signed.
+ * 3 MAC Size u_int16_t number of octets in MAC.
+ * 4 MAC octet stream defined by Algorithm Name.
+ * 5 Original ID u_int16_t original message ID
+ * 6 Error u_int16_t expanded RCODE covering
+ * TSIG processing.
+ * 7 Other Len u_int16_t length, in octets, of
+ * Other Data.
+ * 8 Other Data octet stream empty unless
+ * Error == BADTIME
+ *
+ * Fields 0-2 and 6-8 (inclusive) are digested.
+ */
+ static int rdata_nlen = 16; /* Total length of RDATA minus
+ variable-length fields 0 and
+ 4. */
+ u_char *rdatap; /* Pointer to the start of rdata */
+ size_t ilen; /* Length of the initial portion of RDATA
+ (fields 0-2) */
+ u_char *classp; /* Pointer to the CLASS field. Together with
+ TTL this makes 6 bytes of data. */
+ u_char *macp; /* Pointer to field 4 */
+ u_char *tailp; /* Pointer to field 6 */
+
+
+ /* Create the RR */
+
+ /* Save pointer to the start of the TSIG name. */
+ tsig_name_ptr = cp;
+
+ /* Convert the name to lower case */
+ n = ns_name_compress(lcase(tsig->name, lcbuf), cp, buflen,
+ (const u_char **) dnptrs,
+ (const u_char **) lastdnptr);
+ if (n < 0)
+ return -1;
+ tsig_name_len = n;
+ cp += n;
+ buflen -= n;
+
+ /* Need 10 bytes for RR fields */
+ if (buflen < 10)
+ return -1;
+
+ NS_PUT16(ns_t_tsig, cp);
+
+ /* Save the pointer to CLASS and TTL */
+ classp = cp;
+ NS_PUT16(ns_c_any, cp);
+ NS_PUT32(0, cp);
+
+ /* Save pointer to the RDLENGTH slot */
+ lencp = cp;
+ NS_PUT16(0, cp);
+
+ buflen -= 10;
+
+ /* Mark start of the RDATA */
+ rdatap = cp;
+
+ /* RDATA
+ * Field Name Data Type Notes
+ * -----------------------------------------------------------
+ * Algorithm Name domain-name Name of the algorithm
+ * in domain name syntax.
+ */
+
+ n = ns_name_compress(lcase(tsig_alg_name(tsig->alg), lcbuf),
+ cp, buflen,
(const u_char **) dnptrs,
(const u_char **) lastdnptr);
- if (n < 0 || buflen < 10 + tsig->rdlength)
+ if (n < 0)
return -1;
cp += n;
buflen -= n;
- NS_PUT16(tsig->type, cp);
- NS_PUT16(tsig->rr_class, cp);
- NS_PUT32(tsig->ttl, cp);
- NS_PUT16(tsig->rdlength, cp);
- memcpy(cp, tsig->rdata, tsig->rdlength);
- cp += tsig->rdlength;
+ if (buflen < rdata_nlen + tsig_digest_len)
+ return -1;
+
+ /*
+ * -----------------------------------------------------------
+ * Time Signed u_int48_t seconds since 1-Jan-70 UTC.
+ *
+ * FIXME: Pack u_int48_t as two zero bytes followed by
+ * uint32_t value in network order.
+ */
+ gettimeofday(&tv, NULL);
+ NS_PUT16(0, cp);
+ NS_PUT32(tv.tv_sec, cp);
+
+ /*
+ * -----------------------------------------------------------
+ * Fudge u_int16_t seconds of error permitted
+ * in Time Signed.
+ */
+ NS_PUT16(tsig->fudge, cp);
+
+ /* Save the length of the initial data portion */
+ ilen = cp - rdatap;
+
+ /* Add the digest length */
+ NS_PUT16(tsig_digest_len, cp);
+
+ /* Save pointer to the MAC area */
+ macp = cp;
+
+ /* Reserve sig_len bytes for MAC */
+ cp += tsig_digest_len;
+ buflen -= tsig_digest_len;
+
+ /* Original ID */
+ *cp++ = buf[0];
+ *cp++ = buf[1];
+
+ /* Save pointer to the tail area */
+ tailp = cp;
+
+ /* Error */
+ NS_PUT16(hp->rcode, cp);
+ /* Other len */
+ NS_PUT16(0, cp);
+
+ /* Hash the original message */
+ nsu_signer_update(tsig, buf, msglen);
+
+ /* Add NAME to the digest */
+ nsu_signer_update(tsig, tsig_name_ptr, tsig_name_len);
+
+ /* Add CLASS and TTL to the digest. */
+ nsu_signer_update(tsig, classp, 6);
+
+ /* Hash the Algorighm Name, Time Signed and Fudge */
+ nsu_signer_update(tsig, rdatap, ilen);
+
+ /* Hash the tail portion of rdata: Error, Other Length (0) */
+ nsu_signer_update(tsig, tailp, cp-tailp);
+
+ /* Add the digest */
+ nsu_signer_digest(tsig, macp, tsig_digest_len);
+
+ /* Store the actual RDATA length */
+ NS_PUT16(cp - rdatap, lencp);
+
+ hp->adcount = htons(1);
}
return cp - buf;
@@ -359,7 +540,7 @@ int
nsu_mkquery(char const *zone,
nsu_rr *prereq, int num_prereq,
nsu_rr *update, int num_update,
- ns_rr *tsig,
+ struct nsu_signer *tsig,
u_char *buf, int buflen)
{
return nsu_nmkquery(&_res,
diff --git a/nsu.h b/nsu.h
index 76080e6..a9c2613 100644
--- a/nsu.h
+++ b/nsu.h
@@ -70,6 +70,22 @@ typedef struct {
unsigned adcount: 16; /*%< number of RRs in the Additional Data Section */
} UPDATE_HEADER;
+struct tsig_alg;
+
+struct nsu_signer {
+ char *name;
+ struct tsig_alg const *alg;
+ unsigned short fudge;
+ char *ctx;
+};
+
+char const *tsig_alg_name(struct tsig_alg const *alg);
+size_t tsig_alg_digest_size(struct tsig_alg const *alg);
+
+void nsu_signer_set_key(struct nsu_signer *sgn, u_char const *key, size_t len);
+void nsu_signer_update(struct nsu_signer *sgn, u_char const *data, size_t len);
+void nsu_signer_digest(struct nsu_signer *sgn, u_char *data, size_t len);
+
int nsu_str_to_type(char const *str);
char const *nsu_type_to_str(int type);
@@ -77,12 +93,12 @@ int nsu_nmkquery(res_state statp,
char const *zone,
nsu_rr *prereq, int num_prereq,
nsu_rr *update, int num_update,
- ns_rr *tsig,
+ struct nsu_signer *tsig,
u_char *buf, int buflen);
int nsu_mkquery(char const *zone,
nsu_rr *prereq, int num_prereq,
nsu_rr *update, int num_update,
- ns_rr *tsig,
+ struct nsu_signer *tsig,
u_char *buf, int buflen);
diff --git a/nsu_app.h b/nsu_app.h
index d3cf9de..0da82c6 100644
--- a/nsu_app.h
+++ b/nsu_app.h
@@ -3,11 +3,15 @@ void need_domain();
extern unsigned long default_ttl;
extern int yydebug;
extern int yy_flex_debug;
+extern struct nsu_signer *tsig;
void panic(char const *fmt, ...);
void abend(char const *fmt, ...);
void abend_nomem(void);
-
+int yyerror(char const *);
+int yyparse(void);
+int yylex(void);
+
void lexer_from_file(char const *name);
void lexer_from_argv(int argc, char **argv);
diff --git a/typestr.c b/typestr.c
index af2ce42..05b877e 100644
--- a/typestr.c
+++ b/typestr.c
@@ -1,4 +1,5 @@
#include <resolv.h>
+#include <string.h>
#include "nsu.h"
static char *nstypestr[] = {

Return to:

Send suggestions and report system problems to the System administrator.