From 9f7f3b1216f56cf53a2448ac4b8325b1c4ec4085 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 28 May 2012 21:39:57 +0100 Subject: [PATCH] Add --dns-rr option. --- CHANGELOG | 2 ++ man/dnsmasq.8 | 6 ++++++ src/dnsmasq.h | 2 +- src/option.c | 41 ++++++++++++++++++++++++++++++++++++++++- src/rfc1035.c | 19 ++++++++++++++++++- src/util.c | 27 ++++++++++++++++++++++----- 6 files changed, 89 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 11d4c7f..1358625 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,6 +16,8 @@ version 2.62 router-advertisement configured, but DHCPv6 not configured. Thanks to Marien Zwart for the patch. + Add --dns-rr, to allow arbitrary DNS resource records. + version 2.61 Re-write interface discovery code on *BSD to use diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index 0a3b5df..3c51f78 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -454,6 +454,12 @@ hosts files) or from DHCP. If the target does not satisfy this criteria, the whole cname is ignored. The cname must be unique, but it is permissable to have more than one cname pointing to the same target. .TP +.B --dns-rr=,,[] +Return an arbitrary DNS Resource Record. The number is the type of the +record (which is always in the C_IN class). The value of the record is +given by the hex data, which may be of the for 01:23:45 or 01 23 45 or +012345 or any mixture of these. +.TP .B --interface-name=, Return a DNS record associating the name with the primary address on the given interface. This flag specifies an A record for the given diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 445efad..4b24acc 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -732,7 +732,7 @@ extern struct daemon { time_t last_resolv; struct mx_srv_record *mxnames; struct naptr *naptr; - struct txt_record *txt; + struct txt_record *txt, *rr; struct ptr_record *ptr; struct host_record *host_records, *host_records_tail; struct cname *cnames; diff --git a/src/option.c b/src/option.c index 5cb7d7b..3448532 100644 --- a/src/option.c +++ b/src/option.c @@ -118,6 +118,7 @@ struct myoption { #define LOPT_DUID 307 #define LOPT_HOST_REC 308 #define LOPT_TFTP_LC 309 +#define LOPT_RR 310 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -181,6 +182,7 @@ static const struct myoption opts[] = { "srv-host", 1, 0, 'W' }, { "localise-queries", 0, 0, 'y' }, { "txt-record", 1, 0, 'Y' }, + { "dns-rr", 1, 0, LOPT_RR }, { "enable-dbus", 0, 0, '1' }, { "bootp-dynamic", 2, 0, '3' }, { "dhcp-mac", 1, 0, '4' }, @@ -371,6 +373,7 @@ static struct { { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL }, { LOPT_DUID, ARG_ONE, ",", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL }, { LOPT_HOST_REC, ARG_DUP, ",
", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL }, + { LOPT_RR, ARG_DUP, ",,[]", gettext_noop("Specify arbitrary DNS resource record"), NULL }, { 0, 0, NULL, NULL, NULL } }; @@ -2214,6 +2217,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line) } if (len == -1) + problem = _("bad hex constant"); else if ((new->clid = opt_malloc(len))) { @@ -2931,7 +2935,42 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line) } break; } - + + case LOPT_RR: /* dns-rr */ + { + struct txt_record *new; + size_t len; + char *data; + int val; + + comma = split(arg); + data = split(comma); + + new = opt_malloc(sizeof(struct txt_record)); + new->next = daemon->rr; + daemon->rr = new; + + if (!atoi_check(comma, &val) || + !(new->name = canonicalise_opt(arg)) || + (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1)) + { + problem = _("bad RR record"); + break; + } + + new->class = val; + new->len = 0; + + if (data) + { + new->txt=opt_malloc(len); + new->len = len; + memcpy(new->txt, data, len); + } + + break; + } + case 'Y': /* --txt-record */ { struct txt_record *new; diff --git a/src/rfc1035.c b/src/rfc1035.c index 9b84101..67325ca 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -1250,7 +1250,8 @@ static int add_resource_record(struct dns_header *header, char *limit, int *trun case 't': usval = va_arg(ap, int); sval = va_arg(ap, char *); - memcpy(p, sval, usval); + if (usval != 0) + memcpy(p, sval, usval); p += usval; break; @@ -1393,6 +1394,22 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (qclass == C_IN) { + struct txt_record *t; + + for (t = daemon->rr; t; t = t->next) + if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name)) + { + ans = 1; + if (!dryrun) + { + log_query(F_CONFIG | F_RRNAME, name, NULL, ""); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + daemon->local_ttl, NULL, + t->class, C_IN, "t", t->len, t->txt)) + anscount ++; + } + } + if (qtype == T_PTR || qtype == T_ANY) { /* see if it's w.z.y.z.in-addr.arpa format */ diff --git a/src/util.c b/src/util.c index 07f0338..a1d47d6 100644 --- a/src/util.c +++ b/src/util.c @@ -426,7 +426,7 @@ int parse_hex(char *in, unsigned char *out, int maxlen, while (maxlen == -1 || i < maxlen) { - for (r = in; *r != 0 && *r != ':' && *r != '-'; r++) + for (r = in; *r != 0 && *r != ':' && *r != '-' && *r != ' '; r++) if (*r != '*' && !isxdigit((unsigned char)*r)) return -1; @@ -444,12 +444,29 @@ int parse_hex(char *in, unsigned char *out, int maxlen, else { *r = 0; - mask = mask << 1; if (strcmp(in, "*") == 0) - mask |= 1; + { + mask = (mask << 1) | 1; + i++; + } else - out[i] = strtol(in, NULL, 16); - i++; + { + int j, bytes = (1 + (r - in))/2; + for (j = 0; j < bytes; j++) + { + char sav; + if (j < bytes - 1) + { + sav = in[(j+1)*2]; + in[(j+1)*2] = 0; + } + out[i] = strtol(&in[j*2], NULL, 16); + mask = mask << 1; + i++; + if (j < bytes - 1) + in[(j+1)*2] = sav; + } + } } } in = r+1;