From c822620967ace7c2255abc6c40557a79c0a2a912 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Wed, 8 Aug 2018 23:46:03 +0100 Subject: [PATCH] Add --dhcp-name-match --- CHANGELOG | 2 ++ man/dnsmasq.8 | 3 +++ src/dnsmasq.h | 8 ++++++++ src/option.c | 33 +++++++++++++++++++++++++++++++-- src/rfc2131.c | 35 +++++++++++++++++++++++++++++++++-- 5 files changed, 77 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3aca0dd..277719f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -49,6 +49,8 @@ version 2.80 DNS configuration which confused systemd-resolvd. Thanks to Steve Dodd for characterising the problem. + Add --dhcp-name-match config option. + version 2.79 Fix parsing of CNAME arguments, which are confused by extra spaces. diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index ce895b8..7f53e9f 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -1329,6 +1329,9 @@ The special form with vi-encap: matches against vendor-identifying vendor classes for the specified enterprise. Please see RFC 3925 for more details of these rare and interesting beasts. .TP +.B --dhcp-name-match=set:,[*] +Set the tag if the given name is supplied by a dhcp client. There may be a single trailing wildcard *, which has the usual meaning. Combined with dhcp-ignore or dhcp-ignore-names this gives the ability to ignore certain clients by name, or disallow certain hostnames from being claimed by a client. +.TP .B --tag-if=set:[,set:[,tag:[,tag:]]] Perform boolean operations on tags. Any tag appearing as set: is set if all the tags which appear as tag: are set, (or unset when tag:! is used) diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 4cf12bf..201fc80 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -822,6 +822,13 @@ struct dhcp_boot { struct dhcp_boot *next; }; +struct dhcp_match_name { + char *name; + int wildcard; + struct dhcp_netid *netid; + struct dhcp_match_name *next; +}; + struct pxe_service { unsigned short CSA, type; char *menu, *basename, *sname; @@ -1014,6 +1021,7 @@ extern struct daemon { struct ra_interface *ra_interfaces; struct dhcp_config *dhcp_conf; struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6, *dhcp_match6; + struct dhcp_match_name *dhcp_name_match; struct dhcp_vendor *dhcp_vendors; struct dhcp_mac *dhcp_macs; struct dhcp_boot *boot_config; diff --git a/src/option.c b/src/option.c index 5bdc499..0837d4d 100644 --- a/src/option.c +++ b/src/option.c @@ -164,6 +164,7 @@ struct myoption { #define LOPT_DUMPFILE 352 #define LOPT_DUMPMASK 353 #define LOPT_UBUS 354 +#define LOPT_NAME_MATCH 355 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -273,7 +274,8 @@ static const struct myoption opts[] = { "stop-dns-rebind", 0, 0, LOPT_REBIND }, { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND }, { "all-servers", 0, 0, LOPT_NOLAST }, - { "dhcp-match", 1, 0, LOPT_MATCH }, + { "dhcp-match", 1, 0, LOPT_MATCH }, + { "dhcp-name-match", 1, 0, LOPT_NAME_MATCH }, { "dhcp-broadcast", 2, 0, LOPT_BROADCAST }, { "neg-ttl", 1, 0, LOPT_NEGTTL }, { "max-ttl", 1, 0, LOPT_MAXTTL }, @@ -456,6 +458,7 @@ static struct { { LOPT_NO_REBIND, ARG_DUP, "//", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL }, { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL }, { LOPT_MATCH, ARG_DUP, "set:,", gettext_noop("Set tag if client includes matching option in request."), NULL }, + { LOPT_NAME_MATCH, ARG_DUP, "set:,[*]", gettext_noop("Set tag if client provides given name."), NULL }, { LOPT_ALTPORT, ARG_ONE, "[=]", gettext_noop("Use alternative ports for DHCP."), NULL }, { LOPT_NAPTR, ARG_DUP, ",", gettext_noop("Specify NAPTR DNS record."), NULL }, { LOPT_MINPORT, ARG_ONE, "", gettext_noop("Specify lowest port available for DNS query transmission."), NULL }, @@ -3286,7 +3289,33 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma option == LOPT_FORCE ? DHOPT_FORCE : (option == LOPT_MATCH ? DHOPT_MATCH : (option == LOPT_OPTS ? DHOPT_BANK : 0))); - + + case LOPT_NAME_MATCH: /* --dhcp-name-match */ + { + struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name)); + struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid)); + ssize_t len; + + if (!(comma = split(arg)) || (len = strlen(comma)) == 0) + ret_err(gen_err); + + new->wildcard = 0; + new->netid = id; + id->net = opt_string_alloc(set_prefix(arg)); + + if (comma[len-1] == '*') + { + comma[len-1] = 0; + new->wildcard = 1; + } + new->name = opt_string_alloc(comma); + + new->next = daemon->dhcp_name_match; + daemon->dhcp_name_match = new; + + break; + } + case 'M': /* --dhcp-boot */ { struct dhcp_netid *id = NULL; diff --git a/src/rfc2131.c b/src/rfc2131.c index f68e702..5809126 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -700,8 +700,39 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, client_hostname = daemon->dhcp_buff; } - if (client_hostname && option_bool(OPT_LOG_OPTS)) - my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname); + if (client_hostname) + { + struct dhcp_match_name *m; + size_t nl = strlen(client_hostname); + + if (option_bool(OPT_LOG_OPTS)) + my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname); + + + for (m = daemon->dhcp_name_match; m; m = m->next) + { + size_t ml = strlen(m->name); + char save = 0; + + if (nl < ml) + continue; + if (nl > ml) + { + save = client_hostname[ml]; + client_hostname[ml] = 0; + } + + if (hostname_isequal(client_hostname, m->name) && + (save == 0 || m->wildcard)) + { + m->netid->next = netid; + netid = m->netid; + } + + if (save != 0) + client_hostname[ml] = save; + } + } if (have_config(config, CONFIG_NAME)) {