diff --git a/src/auth.c b/src/auth.c index 61ee135..050fe16 100644 --- a/src/auth.c +++ b/src/auth.c @@ -84,7 +84,13 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n struct auth_zone *zone = NULL; struct subnet *subnet = NULL; char *cut; - + struct mx_srv_record *rec, *move, **up; + struct txt_record *txt; + struct interface_name *intr; + struct naptr *na; + struct all_addr addr; + struct cname *a; + if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY ) return 0; @@ -99,13 +105,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n { unsigned short flag = 0; int found = 0; - struct mx_srv_record *rec, *move, **up; - struct txt_record *txt; - struct interface_name *intr; - struct naptr *na; - struct all_addr addr; - struct cname *a; - + /* save pointer to name for copying into answers */ nameoffset = p - (unsigned char *)header; @@ -345,30 +345,33 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n flag = F_IPV6; #endif - if (qtype == T_SOA && !cut) + if (!cut) { - soa = 1; /* inhibits auth section */ - found = 1; - log_query(F_RRNAME | F_AUTH, zone->domain, NULL, ""); + nxdomain = 0; + + if (qtype == T_SOA) + { + soa = 1; /* inhibits auth section */ + found = 1; + log_query(F_RRNAME | F_AUTH, zone->domain, NULL, ""); + } + else if (qtype == T_AXFR) + { + soa = 1; /* inhibits auth section */ + ns = 1; /* ensure we include NS records! */ + axfr = 1; + found = 1; + axfroffset = nameoffset; + log_query(F_RRNAME | F_AUTH, zone->domain, NULL, ""); + } + else if (qtype == T_NS) + { + ns = 1; /* inhibits auth section */ + found = 1; + log_query(F_RRNAME | F_AUTH, zone->domain, NULL, ""); + } } - if (qtype == T_AXFR && !cut) - { - soa = 1; /* inhibits auth section */ - axfr = 1; - found = 1; - axfroffset = nameoffset; - log_query(F_RRNAME | F_AUTH, zone->domain, NULL, ""); - } - - if (qtype == T_NS && !cut) - { - ns = 1; /* inhibits auth section */ - found = 1; - log_query(F_RRNAME | F_AUTH, zone->domain, NULL, ""); - } - - if (!option_bool(OPT_DHCP_FQDN) && cut) { *cut = 0; /* remove domain part */ @@ -423,7 +426,8 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n if (auth) { char *authname; - + int newoffset, offset = 0; + if (!subnet) authname = zone->domain; else @@ -464,17 +468,8 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n } /* handle NS and SOA in auth section or for explicit queries */ - if ((anscount != 0 || ns) && - add_resource_record(header, limit, &trunc, 0, &ansp, - daemon->auth_ttl, NULL, T_NS, C_IN, "d", authname, daemon->authserver)) - { - if (ns) - anscount++; - else - authcount++; - } - - if ((anscount == 0 || soa) && + newoffset = ansp - (unsigned char *)header; + if (((anscount == 0 && !ns) || soa) && add_resource_record(header, limit, &trunc, 0, &ansp, daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll", authname, daemon->authserver, daemon->hostmaster, @@ -482,14 +477,149 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n daemon->soa_retry, daemon->soa_expiry, daemon->auth_ttl)) { + offset = newoffset; if (soa) anscount++; else authcount++; } + + if (anscount != 0 || ns) + { + struct name_list *secondary; + + newoffset = ansp - (unsigned char *)header; + if (add_resource_record(header, limit, &trunc, -offset, &ansp, + daemon->auth_ttl, NULL, T_NS, C_IN, "d", offset == 0 ? authname : NULL, daemon->authserver)) + { + if (offset == 0) + offset = newoffset; + if (ns) + anscount++; + else + authcount++; + } + if (!subnet) + for (secondary = daemon->secondary_forward_server; secondary; secondary = secondary->next) + if (add_resource_record(header, limit, &trunc, offset, &ansp, + daemon->auth_ttl, NULL, T_NS, C_IN, "d", secondary->name)) + { + if (ns) + anscount++; + else + authcount++; + } + } + if (axfr) { + for (rec = daemon->mxnames; rec; rec = rec->next) + if (in_zone(zone, rec->name, &cut)) + { + if (cut) + *cut = 0; + + if (rec->issrv) + { + if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, + NULL, T_SRV, C_IN, "sssd", cut ? rec->name : NULL, + rec->priority, rec->weight, rec->srvport, rec->target)) + + anscount++; + } + else + { + if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, + NULL, T_MX, C_IN, "sd", cut ? rec->name : NULL, rec->weight, rec->target)) + anscount++; + } + + /* restore config data */ + if (cut) + *cut = '.'; + } + + for (txt = daemon->rr; txt; txt = txt->next) + if (in_zone(zone, txt->name, &cut)) + { + if (cut) + *cut = 0; + + if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, + NULL, txt->class, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt)) + anscount++; + + /* restore config data */ + if (cut) + *cut = '.'; + } + + for (txt = daemon->txt; txt; txt = txt->next) + if (txt->class == C_IN && in_zone(zone, txt->name, &cut)) + { + if (cut) + *cut = 0; + + if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, + NULL, T_TXT, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt)) + anscount++; + + /* restore config data */ + if (cut) + *cut = '.'; + } + + for (na = daemon->naptr; na; na = na->next) + if (in_zone(zone, na->name, &cut)) + { + if (cut) + *cut = 0; + + if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, + NULL, T_NAPTR, C_IN, "sszzzd", cut ? na->name : NULL, + na->order, na->pref, na->flags, na->services, na->regexp, na->replace)) + anscount++; + + /* restore config data */ + if (cut) + *cut = '.'; + } + + for (intr = daemon->int_names; intr; intr = intr->next) + if (in_zone(zone, intr->name, &cut) && (addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr != (in_addr_t) -1) + { + if (cut) + *cut = 0; + + if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, + daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addr)) + anscount++; + + /* restore config data */ + if (cut) + *cut = '.'; + } + + for (a = daemon->cnames; a; a = a->next) + if (in_zone(zone, a->alias, &cut)) + { + strcpy(name, a->target); + if (!strchr(name, '.')) + { + strcat(name, "."); + strcat(name, zone->domain); + } + + if (cut) + *cut = 0; + + if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, + daemon->auth_ttl, NULL, + T_CNAME, C_IN, "d", cut ? a->alias : NULL, name)) + anscount++; + } + cache_enumerate(1); while ((crecp = cache_enumerate(0))) { @@ -525,20 +655,12 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n qtype = T_AAAA; #endif if (cut) - { - *cut = 0; - if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, - daemon->auth_ttl, NULL, qtype, C_IN, - (crecp->flags & F_IPV4) ? "4" : "6", name, &crecp->addr)) - anscount++; - } - else - { - if (add_resource_record(header, limit, &trunc, axfroffset, &ansp, - daemon->auth_ttl, NULL, qtype, C_IN, - (crecp->flags & F_IPV4) ? "4" : "6", &crecp->addr)) - anscount++; - } + *cut = 0; + + if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, + daemon->auth_ttl, NULL, qtype, C_IN, + (crecp->flags & F_IPV4) ? "4" : "6", cut ? name : NULL, &crecp->addr)) + anscount++; } } } @@ -555,7 +677,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n } - } +} /* done all questions, set up header and return length of result */ /* clear authoritative and truncated flags, set QR flag */ diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 807a8aa..0d9ab7e 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -756,6 +756,7 @@ extern struct daemon { char *username, *groupname, *scriptuser; char *luascript; char *authserver, *authinterface, *hostmaster; + struct name_list *secondary_forward_server; int group_set, osport; char *domain_suffix; struct cond_domain *cond_domain; @@ -901,7 +902,7 @@ size_t resize_packet(struct dns_header *header, size_t plen, size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3); int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp, unsigned long ttl, - unsigned int *offset, unsigned short type, unsigned short class, char *format, ...); + int *offset, unsigned short type, unsigned short class, char *format, ...); unsigned char *skip_questions(struct dns_header *header, size_t plen); int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, char *name, int isExtract, int extrabytes); diff --git a/src/lease.c b/src/lease.c index 7982476..656f006 100644 --- a/src/lease.c +++ b/src/lease.c @@ -420,6 +420,9 @@ void lease_update_dns(int force) if (daemon->port != 0 && (dns_dirty || force)) { + /* force transfer to authoritative secondaries */ + daemon->soa_sn++; + cache_unhash_dhcp(); for (lease = leases; lease; lease = lease->next) diff --git a/src/option.c b/src/option.c index 6a2519c..d32f264 100644 --- a/src/option.c +++ b/src/option.c @@ -123,8 +123,9 @@ struct myoption { #define LOPT_MAXCTTL 312 #define LOPT_AUTHZONE 313 #define LOPT_AUTHSERV 314 -#define LOPT_AUTHTTL 315 +#define LOPT_AUTHTTL 315 #define LOPT_AUTHSOA 316 +#define LOPT_AUTHSFS 317 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -255,6 +256,7 @@ static const struct myoption opts[] = { "auth-server", 1, 0, LOPT_AUTHSERV }, { "auth-ttl", 1, 0, LOPT_AUTHTTL }, { "auth-soa", 1, 0, LOPT_AUTHSOA }, + { "auth-sec-servers", 1, 0, LOPT_AUTHSFS }, { NULL, 0, 0, 0 } }; @@ -391,6 +393,7 @@ static struct { { LOPT_AUTHZONE, ARG_DUP, ",[,]", gettext_noop("Domain to export to global DNS"), NULL }, { LOPT_AUTHTTL, ARG_ONE, "", gettext_noop("Set TTL for authoritative replies"), NULL }, { LOPT_AUTHSOA, ARG_ONE, "[,...]", gettext_noop("Set authoritive zone information"), NULL }, + { LOPT_AUTHSFS, ARG_ONE, "[,...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL }, { 0, 0, NULL, NULL, NULL } }; @@ -1525,7 +1528,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma new->next = daemon->dhcp_hosts_file; daemon->dhcp_hosts_file = new; } - else if (option == LOPT_DHCP_OPTS) + else if (option == LOPT_DHCP_OPTS) { new->next = daemon->dhcp_opts_file; daemon->dhcp_opts_file = new; @@ -1541,7 +1544,22 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma daemon->authinterface = opt_string_alloc(comma); break; - + + case LOPT_AUTHSFS: /* --auth-sec-servers */ + { + struct name_list *new; + + do { + comma = split(arg); + new = safe_malloc(sizeof(struct name_list)); + new->name = opt_string_alloc(arg); + new->next = daemon->secondary_forward_server; + daemon->secondary_forward_server = new; + arg = comma; + } while (arg); + break; + } + case LOPT_AUTHZONE: /* --auth-zone */ { struct auth_zone *new; diff --git a/src/rfc1035.c b/src/rfc1035.c index 6d5845c..932199b 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -1186,7 +1186,7 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, } int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp, - unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...) + unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...) { va_list ap; unsigned char *sav, *p = *pp; @@ -1206,7 +1206,9 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int } else { - p = do_rfc1035_name(p, va_arg(ap, char *)); + char *name = va_arg(ap, char *); + if (name) + p = do_rfc1035_name(p, name); if (nameoffset < 0) { PUTSHORT(-nameoffset | 0xc000, p); @@ -1699,7 +1701,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ans = found = 1; if (!dryrun) { - unsigned int offset; + int offset; log_query(F_CONFIG | F_RRNAME, name, NULL, ""); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, &offset, T_MX, C_IN, "sd", rec->weight, rec->target)) @@ -1737,7 +1739,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, found = ans = 1; if (!dryrun) { - unsigned int offset; + int offset; log_query(F_CONFIG | F_RRNAME, name, NULL, ""); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, &offset, T_SRV, C_IN, "sssd",