diff --git a/CHANGELOG b/CHANGELOG index 53407ee..2cc4b73 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,6 +21,9 @@ version 2.61 Better logging of DHCPv6 options. + Add --host-record. Thanks to Rob Zwissler for the + suggestion. + version 2.60 Fix compilation problem in Mac OS X Lion. Thanks to Olaf diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index 1a1ac54..487e23f 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -416,6 +416,22 @@ zone files: the port, weight and priority numbers are in a different order. More than one SRV record for a given service/domain is allowed, all that match are returned. .TP +.B --host-record=[,....][],[IPv6-address] +Add A, AAAA and PTR records to the DNS. This adds one or more names to +the DNS with associated IPv4 (A) and IPv6 (AAAA) records. A name may +appear in more than one +.B host-record +and therefore be assigned more than one address. Only the first +address creates a PTR record linking the address to the name. This is +the same rule as is used reading hosts-files. +.B host-record +options are considered to be read before host-files, so a name +appearing there inhibits PTR-record creation if it appears in +hosts-file also. Unlike host-files, names are not expanded, even when +.B expand-hosts +is in effect. Short and long names may appear in the same +.B host-record, eg. --host-record=laptop,laptop.thekelleys.org,192.168.0.1,1234::100 +.TP .B \-Y, --txt-record=[[,],] Return a TXT DNS record. The value of TXT record is a set of strings, so any number may be included, delimited by commas; use quotes to put diff --git a/src/cache.c b/src/cache.c index 896132b..6940204 100644 --- a/src/cache.c +++ b/src/cache.c @@ -634,10 +634,11 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr, return NULL; } + static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen, - unsigned short flags, int index, struct crec **rhash, int hashsz) + int index, struct crec **rhash, int hashsz) { - struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6)); + struct crec *lookup = cache_find_by_name(NULL, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6)); int i, nameexists = 0; struct cname *a; unsigned int j; @@ -670,10 +671,10 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl j = (j*2 +((unsigned char *)addr)[i]) % hashsz; for (lookup = rhash[j]; lookup; lookup = lookup->next) - if ((lookup->flags & flags & (F_IPV4 | F_IPV6)) && + if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) && memcmp(&lookup->addr.addr, addr, addrlen) == 0) { - flags &= ~F_REVERSE; + cache->flags &= ~F_REVERSE; break; } @@ -684,7 +685,6 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl rhash[j] = cache; } - cache->flags = flags; cache->uid = index; memcpy(&cache->addr.addr, addr, addrlen); cache_hash(cache); @@ -692,7 +692,7 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl /* don't need to do alias stuff for second and subsequent addresses. */ if (!nameexists) for (a = daemon->cnames; a; a = a->next) - if (hostname_isequal(cache->name.sname, a->target) && + if (hostname_isequal(cache_get_name(cache), a->target) && (lookup = whine_malloc(sizeof(struct crec)))) { lookup->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_HOSTS | F_CNAME; @@ -771,26 +771,19 @@ static int read_hostsfile(char *filename, int index, int cache_size, struct crec { lineno++; -#ifdef HAVE_IPV6 if (inet_pton(AF_INET, token, &addr) > 0) { flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4; addrlen = INADDRSZ; domain_suffix = get_domain(addr.addr.addr4); } +#ifdef HAVE_IPV6 else if (inet_pton(AF_INET6, token, &addr) > 0) { flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6; addrlen = IN6ADDRSZ; domain_suffix = get_domain6(&addr.addr.addr6); } -#else - if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1) - { - flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4; - addrlen = INADDRSZ; - domain_suffix = get_domain(addr.addr.addr4); - } #endif else { @@ -830,13 +823,15 @@ static int read_hostsfile(char *filename, int index, int cache_size, struct crec strcpy(cache->name.sname, canon); strcat(cache->name.sname, "."); strcat(cache->name.sname, domain_suffix); - add_hosts_entry(cache, &addr, addrlen, flags, index, rhash, hashsz); + cache->flags = flags; + add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz); name_count++; } if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME))) { strcpy(cache->name.sname, canon); - add_hosts_entry(cache, &addr, addrlen, flags, index, rhash, hashsz); + cache->flags = flags; + add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz); name_count++; } free(canon); @@ -860,6 +855,8 @@ void cache_reload(void) struct crec *cache, **up, *tmp; int revhashsz, i, total_size = daemon->cachesize; struct hostsfile *ah; + struct host_record *hr; + struct name_list *nl; cache_inserted = cache_live_freed = 0; @@ -886,6 +883,34 @@ void cache_reload(void) up = &cache->hash_next; } + /* borrow the packet buffer for a temporary by-address hash */ + memset(daemon->packet, 0, daemon->packet_buff_sz); + revhashsz = daemon->packet_buff_sz / sizeof(struct crec *); + /* we overwrote the buffer... */ + daemon->srv_save = NULL; + + /* Do host_records in config. */ + for (hr = daemon->host_records; hr; hr = hr->next) + for (nl = hr->names; nl; nl = nl->next) + { + if (hr->addr.s_addr != 0 && + (cache = whine_malloc(sizeof(struct crec)))) + { + cache->name.namep = nl->name; + cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG; + add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, 0, (struct crec **)daemon->packet, revhashsz); + } +#ifdef HAVE_IPV6 + if (!IN6_IS_ADDR_UNSPECIFIED(&hr->addr6) && + (cache = whine_malloc(sizeof(struct crec)))) + { + cache->name.namep = nl->name; + cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG; + add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, 0, (struct crec **)daemon->packet, revhashsz); + } +#endif + } + if (option_bool(OPT_NO_HOSTS) && !daemon->addn_hosts) { if (daemon->cachesize > 0) @@ -893,12 +918,6 @@ void cache_reload(void) return; } - /* borrow the packet buffer for a temporary by-address hash */ - memset(daemon->packet, 0, daemon->packet_buff_sz); - revhashsz = daemon->packet_buff_sz / sizeof(struct crec *); - /* we overwrote the buffer... */ - daemon->srv_save = NULL; - if (!option_bool(OPT_NO_HOSTS)) total_size = read_hostsfile(HOSTSFILE, 0, total_size, (struct crec **)daemon->packet, revhashsz); diff --git a/src/dnsmasq.h b/src/dnsmasq.h index f8c354d..b35b580 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -274,6 +274,18 @@ struct cname { struct cname *next; }; +struct host_record { + struct name_list { + char *name; + struct name_list *next; + } *names; + struct in_addr addr; +#ifdef HAVE_IPV6 + struct in6_addr addr6; +#endif + struct host_record *next; +}; + struct interface_name { char *name; /* domain name */ char *intr; /* interface name */ @@ -695,6 +707,7 @@ extern struct daemon { struct naptr *naptr; struct txt_record *txt; struct ptr_record *ptr; + struct host_record *host_records, *host_records_tail; struct cname *cnames; struct interface_name *int_names; char *mxtarget; diff --git a/src/option.c b/src/option.c index ec356fa..08554d8 100644 --- a/src/option.c +++ b/src/option.c @@ -116,6 +116,7 @@ struct myoption { #define LOPT_LUASCRIPT 305 #define LOPT_RA 306 #define LOPT_DUID 307 +#define LOPT_HOST_REC 308 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -237,6 +238,7 @@ static const struct myoption opts[] = { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT }, { "enable-ra", 0, 0, LOPT_RA }, { "dhcp-duid", 1, 0, LOPT_DUID }, + { "host-record", 1, 0, LOPT_HOST_REC }, { NULL, 0, 0, 0 } }; @@ -365,6 +367,7 @@ static struct { { LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL }, { 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 }, { 0, 0, NULL, NULL, NULL } }; @@ -3030,6 +3033,59 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line) break; } + case LOPT_HOST_REC: /* --host-record */ + { + struct host_record *new = opt_malloc(sizeof(struct host_record)); + memset(new, 0, sizeof(struct host_record)); + + if (!arg || !(comma = split(arg))) + problem = _("Bad host-record"); + else + while (arg) + { + struct all_addr addr; + if (inet_pton(AF_INET, arg, &addr)) + new->addr = addr.addr.addr4; +#ifdef HAVE_IPV6 + else if (inet_pton(AF_INET6, arg, &addr)) + new->addr6 = addr.addr.addr6; +#endif + else + { + int nomem; + char *canon = canonicalise(arg, &nomem); + struct name_list *nl = opt_malloc(sizeof(struct name_list)); + if (!canon) + { + problem = _("Bad name in host-record"); + break; + } + nl->name = canon; + /* keep order, so that PTR record goes to first name */ + nl->next = NULL; + if (!new->names) + new->names = nl; + else + { + struct name_list *tmp; + for (tmp = new->names; tmp->next; tmp = tmp->next); + tmp->next = nl; + } + } + arg = comma; + comma = split(arg); + } + + /* Keep list order */ + if (!daemon->host_records_tail) + daemon->host_records = new; + else + daemon->host_records_tail->next = new; + new->next = NULL; + daemon->host_records_tail = new; + break; + } + default: return _("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)");