summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2005-03-31 17:48:32 +0100
committerSimon Kelley <simon@thekelleys.org.uk>2012-01-05 17:31:11 +0000
commit91dccd095886afb65935f0bf191de52758f79d69 (patch)
tree6bccc12d9ed338a2dc2a4ba715405d75f18bb6b4
parent0a852541d394503d1d345148e9b885faf760e140 (diff)
downloaddnsmasq-91dccd095886afb65935f0bf191de52758f79d69.tar.gz
import of dnsmasq-2.22.tar.gzv2.22
-rw-r--r--CHANGELOG22
-rw-r--r--dnsmasq-rh.spec2
-rw-r--r--dnsmasq-suse.spec2
-rw-r--r--dnsmasq.817
-rw-r--r--dnsmasq.conf.example7
-rw-r--r--rpm/dnsmasq-SuSE.patch2
-rw-r--r--src/config.h4
-rw-r--r--src/dhcp.c8
-rw-r--r--src/dnsmasq.h4
-rw-r--r--src/netlink.c1
-rw-r--r--src/option.c358
-rw-r--r--src/rfc1035.c3
-rw-r--r--src/rfc2131.c108
13 files changed, 350 insertions, 188 deletions
diff --git a/CHANGELOG b/CHANGELOG
index c193890..dbeb5f0 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1422,3 +1422,25 @@ version 2.21
Added wildcard matching for MAC addresses in dhcp-host
options. A sensible suggestion by Nathaniel McCallum.
+version 2.22
+ Fixed build problems on (many) systems with older libc
+ headers where <linux/types.h> is required before
+ <linux/netlink.h>. Enabled HAVE_RTNETLINK under uclibc now
+ that this fix is in place.
+
+ Added support for encapsulated vendor-class-specific DHCP
+ options. Thanks to Eric Shattow for help with this.
+
+ Fix regression in 2.21 which broke commas in filenames and
+ corrupted argv. Thanks to Eric Scott for the bugreport.
+
+ Fixed stupid thinko which caused dnsmasq to wedge during
+ startup with certain MX-record options. Another 2.21 regression.
+
+ Fixed broken-ness when reading /etc/ethers. 2.21 broke
+ this too.
+
+ Fixed wedge with certain DHCP options. Yet another 2.21
+ regression. Rob Holland and Roy Marples chased this one
+ down.
+
diff --git a/dnsmasq-rh.spec b/dnsmasq-rh.spec
index e306d40..a866afb 100644
--- a/dnsmasq-rh.spec
+++ b/dnsmasq-rh.spec
@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
-Version: 2.21
+Version: 2.22
Release: 1
Copyright: GPL
Group: System Environment/Daemons
diff --git a/dnsmasq-suse.spec b/dnsmasq-suse.spec
index 912fa29..a6d6602 100644
--- a/dnsmasq-suse.spec
+++ b/dnsmasq-suse.spec
@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
-Version: 2.21
+Version: 2.22
Release: 1
Copyright: GPL
Group: Productivity/Networking/DNS/Servers
diff --git a/dnsmasq.8 b/dnsmasq.8
index 65568fb..0f05f5a 100644
--- a/dnsmasq.8
+++ b/dnsmasq.8
@@ -408,7 +408,7 @@ have exactly the same effect as
.B --dhcp-host
options containing the same information.
.TP
-.B \-O, --dhcp-option=[<network-id>,[<network-id>,]]<opt>,[<value>[,<value>]]
+.B \-O, --dhcp-option=[<network-id>,[<network-id>,]][vendor:<vendor-class>]<opt>,[<value>[,<value>]]
Specfify different or extra options to DHCP clients. By default,
dnsmasq sends some standard options to DHCP clients, the netmask and
broadcast address are set to the same as the host running dnsmasq, and
@@ -426,10 +426,23 @@ machine running dnsmasq". Data types allowed are comma separated
dotted-quad IP addresses, a decimal number, colon-separated hex digits
and a text string. If the optional network-ids are given then
this option is only sent when all the network-ids are matched.
+
Be careful: no checking is done that the correct type of data for the
option number is sent, it is quite possible to
persuade dnsmasq to generate illegal DHCP packets with injudicious use
-of this flag.
+of this flag. When the value is a decimal number, dnsmasq must determine how
+large the data item is. It does this by examining the option number and/or the
+value, but can be overriden by appending a single letter flag as follows:
+b = one byte, s = two bytes, i = four bytes. This is mainly useful with
+encapsulated vendor class options (see below) where dnsmasq cannot determine data size from the option number.
+
+Encapsulated Vendor-class options may also be specified using
+--dhcp-option: for instance
+.B --dhcp-option=vendor:PXEClient,1,0.0.0.0
+sends the vendor class "PXEClient" and the encapsulated vendor class-specific option "mftp-address=0.0.0.0" Only one vendor class is allowed for any
+host, but multiple options are allowed, provided they all have
+the same vendor class. The address 0.0.0.0 is not treated specially in
+encapsulated vendor class options.
.TP
.B \-U, --dhcp-vendorclass=<network-id>,<vendor-class>
Map from a vendor-class string to a network id. Most DHCP clients provide a
diff --git a/dnsmasq.conf.example b/dnsmasq.conf.example
index a39c35d..9ac76e8 100644
--- a/dnsmasq.conf.example
+++ b/dnsmasq.conf.example
@@ -239,6 +239,13 @@ bogus-priv
# probably doesn't support this......
#dhcp-option=119,eng.apple.com,marketing.apple.com
+# Send encapsulated vendor-class specific options. The vendor-class
+# is sent as DHCP option 60, and all the options marked with the
+# vendor class are send encapsulated in DHCP option 43. The meaning of
+# the options is defined by the vendor-class. This example sets the
+# mtftp address to 0.0.0.0 for PXEClients
+#dhcp-option=vendor:PXEClient,1,0.0.0.0
+
# Set the boot filename and tftpd server name and address
# for BOOTP. You will only need this is you want to
# boot machines over the network.
diff --git a/rpm/dnsmasq-SuSE.patch b/rpm/dnsmasq-SuSE.patch
index 08bc38e..3c6c082 100644
--- a/rpm/dnsmasq-SuSE.patch
+++ b/rpm/dnsmasq-SuSE.patch
@@ -20,7 +20,7 @@
#define IP6INTERFACES "/proc/net/if_inet6"
#define UPTIME "/proc/uptime"
#define DHCP_SERVER_PORT 67
-@@ -187,7 +187,7 @@
+@@ -195,7 +195,7 @@
/* platform independent options. */
#undef HAVE_BROKEN_RTC
diff --git a/src/config.h b/src/config.h
index 1b61580..1797944 100644
--- a/src/config.h
+++ b/src/config.h
@@ -12,7 +12,7 @@
/* Author's email: simon@thekelleys.org.uk */
-#define VERSION "2.21"
+#define VERSION "2.22"
#define FTABSIZ 150 /* max number of outstanding requests */
#define MAX_PROCS 20 /* max no children for TCP requests */
@@ -207,7 +207,7 @@ NOTES:
#if defined(__uClinux__) || defined(__UCLIBC__)
#undef HAVE_LINUX_IPV6_PROC
#define HAVE_GETOPT_LONG
-#undef HAVE_RTNETLINK /* headers broken */
+#define HAVE_RTNETLINK
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
diff --git a/src/dhcp.c b/src/dhcp.c
index b12215b..63392cb 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -206,8 +206,9 @@ void dhcp_packet(struct daemon *daemon, time_t now)
return;
}
+ /* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp; context; context = context->next)
- context->current = NULL;
+ context->current = context;
#ifdef HAVE_RTNETLINK
if (!netlink_process(daemon, iface_index, mess->giaddr, iface_addr, &context))
@@ -381,7 +382,8 @@ struct dhcp_context *complete_context(struct daemon *daemon, struct in_addr loca
if (is_same_net(local, context->start, context->netmask) &&
is_same_net(local, context->end, context->netmask))
{
- if (!context->current)
+ /* link it onto the current chain if we've not seen it before */
+ if (context->current == context)
{
context->router = local;
context->local = local;
@@ -684,6 +686,7 @@ void dhcp_read_ethers(struct daemon *daemon)
{
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_HWADDR) &&
+ config->wildcard_mask == 0 &&
memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
break;
@@ -692,6 +695,7 @@ void dhcp_read_ethers(struct daemon *daemon)
if (!(config = malloc(sizeof(struct dhcp_config))))
continue;
config->flags = 0;
+ config->wildcard_mask = 0;
config->next = configs;
configs = config;
}
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 5a5b104..982928d 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -321,7 +321,7 @@ struct dhcp_config {
struct dhcp_opt {
int opt, len, is_addr;
- unsigned char *val;
+ unsigned char *val, *vendor_class;
struct dhcp_netid *netid;
struct dhcp_opt *next;
};
@@ -403,7 +403,7 @@ struct daemon {
struct hostsfile *addn_hosts;
struct dhcp_context *dhcp;
struct dhcp_config *dhcp_conf;
- struct dhcp_opt *dhcp_opts;
+ struct dhcp_opt *dhcp_opts, *vendor_opts;
struct dhcp_vendor *dhcp_vendors;
struct dhcp_boot *boot_config;
struct dhcp_netid_list *dhcp_ignore;
diff --git a/src/netlink.c b/src/netlink.c
index 5526b47..b8aac0f 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -16,6 +16,7 @@
#ifdef HAVE_RTNETLINK
+#include <linux/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
diff --git a/src/option.c b/src/option.c
index 4d54f4d..11a416f 100644
--- a/src/option.c
+++ b/src/option.c
@@ -187,13 +187,26 @@ static void add_txt(struct daemon *daemon, char *name, char *txt)
memcpy((r->txt)+1, txt, len);
}
+/* filenames are OK with unquoted commas, restore them here. */
+static char *safe_filename_alloc(char *filename)
+{
+ char *p, *ret = safe_string_alloc(filename);
+
+ if (ret)
+ for (p = ret; *p; p++)
+ if (*p == '\001')
+ *p = ',';
+
+ return ret;
+}
+
struct daemon *read_opts (int argc, char **argv)
{
struct daemon *daemon = safe_malloc(sizeof(struct daemon));
char *problem = NULL, *buff = safe_malloc(MAXDNAME);
int option = 0, i;
FILE *file_save = NULL, *f = NULL;
- char *p, *comma, *file_name_save = NULL, *conffile = CONFFILE;
+ char *p, *arg, *comma, *file_name_save = NULL, *conffile = CONFFILE;
int hosts_index = 1, conffile_set = 0;
int line_save = 0, lineno = 0;
opterr = 0;
@@ -229,10 +242,18 @@ struct daemon *read_opts (int argc, char **argv)
#else
option = getopt(argc, argv, OPTSTRING);
#endif
+ /* Copy optarg so that argv doesn't get changed */
if (optarg)
- for (p = optarg; *p; p++)
- if (*p == ',')
- *p = '\001';
+ {
+ strncpy(buff, optarg, MAXDNAME);
+ buff[MAXDNAME-1] = 0;
+ arg = buff;
+ for (p = arg; *p; p++)
+ if (*p == ',')
+ *p = '\001';
+ }
+ else
+ arg = NULL;
}
else
{ /* f non-NULL, reading from conffile. */
@@ -298,11 +319,11 @@ struct daemon *read_opts (int argc, char **argv)
continue;
if ((p=strchr(buff, '=')))
{
- optarg = p+1;
+ arg = p+1;
*p = 0;
}
else
- optarg = NULL;
+ arg = NULL;
option = 0;
for (i=0; opts[i].name; i++)
@@ -351,14 +372,14 @@ struct daemon *read_opts (int argc, char **argv)
{
daemon->options |= optmap[i].flag;
option = 0;
- if (f && optarg)
+ if (f && arg)
complain("extraneous parameter", lineno, conffile);
break;
}
if (option && option != '?')
{
- if (f && !optarg)
+ if (f && !arg)
{
complain("missing parameter", lineno, conffile);
continue;
@@ -369,7 +390,7 @@ struct daemon *read_opts (int argc, char **argv)
case 'C':
if (!f)
{
- conffile = safe_string_alloc(optarg);
+ conffile = safe_filename_alloc(arg);
conffile_set = 1;
break;
}
@@ -383,19 +404,20 @@ struct daemon *read_opts (int argc, char **argv)
file_name_save = conffile;
file_save = f;
line_save = lineno;
- conffile = safe_string_alloc(optarg);
+ conffile = safe_filename_alloc(arg);
conffile_set = 1;
lineno = 0;
goto fileopen;
case 'x':
- daemon->runfile = safe_string_alloc(optarg);
+ daemon->runfile = safe_filename_alloc(arg);
break;
case 'r':
{
- char *name = safe_string_alloc(optarg);
+ char *name = safe_filename_alloc(arg);
struct resolvc *new, *list = daemon->resolv_files;
+
if (list && list->is_default)
{
/* replace default resolv file - possibly with nothing */
@@ -425,7 +447,7 @@ struct daemon *read_opts (int argc, char **argv)
int pref = 1;
struct mx_srv_record *new;
- if ((comma = strchr(optarg, '\001')))
+ if ((comma = strchr(arg, '\001')))
{
char *prefstr;
*(comma++) = 0;
@@ -441,7 +463,7 @@ struct daemon *read_opts (int argc, char **argv)
}
}
- if (!canonicalise(optarg) || (comma && !canonicalise(comma)))
+ if (!canonicalise(arg) || (comma && !canonicalise(comma)))
{
option = '?';
problem = "bad MX name";
@@ -452,30 +474,30 @@ struct daemon *read_opts (int argc, char **argv)
new->next = daemon->mxnames;
daemon->mxnames = new;
new->issrv = 0;
- new->name = safe_string_alloc(optarg);
+ new->name = safe_string_alloc(arg);
new->target = safe_string_alloc(comma); /* may be NULL */
new->weight = pref;
break;
}
case 't':
- if (!canonicalise(optarg))
+ if (!canonicalise(arg))
{
option = '?';
problem = "bad MX target";
}
else
- daemon->mxtarget = safe_string_alloc(optarg);
+ daemon->mxtarget = safe_string_alloc(arg);
break;
case 'l':
- daemon->lease_file = safe_string_alloc(optarg);
+ daemon->lease_file = safe_filename_alloc(arg);
break;
case 'H':
{
struct hostsfile *new = safe_malloc(sizeof(struct hostsfile));
- new->fname = safe_string_alloc(optarg);
+ new->fname = safe_filename_alloc(arg);
new->index = hosts_index++;
new->next = daemon->addn_hosts;
daemon->addn_hosts = new;
@@ -483,57 +505,57 @@ struct daemon *read_opts (int argc, char **argv)
}
case 's':
- if (strcmp (optarg, "#") == 0)
+ if (strcmp (arg, "#") == 0)
daemon->options |= OPT_RESOLV_DOMAIN;
- else if (!canonicalise(optarg))
+ else if (!canonicalise(arg))
option = '?';
else
- daemon->domain_suffix = safe_string_alloc(optarg);
+ daemon->domain_suffix = safe_string_alloc(arg);
break;
case 'u':
- daemon->username = safe_string_alloc(optarg);
+ daemon->username = safe_string_alloc(arg);
break;
case 'g':
- daemon->groupname = safe_string_alloc(optarg);
+ daemon->groupname = safe_string_alloc(arg);
break;
case 'i':
do {
struct iname *new = safe_malloc(sizeof(struct iname));
- if ((comma = strchr(optarg, '\001')))
+ if ((comma = strchr(arg, '\001')))
*comma++ = 0;
new->next = daemon->if_names;
daemon->if_names = new;
/* new->name may be NULL if someone does
"interface=" to disable all interfaces except loop. */
- new->name = safe_string_alloc(optarg);
+ new->name = safe_string_alloc(arg);
new->isloop = new->used = 0;
- if (strchr(optarg, ':'))
+ if (strchr(arg, ':'))
daemon->options |= OPT_NOWILD;
- optarg = comma;
- } while (optarg);
+ arg = comma;
+ } while (arg);
break;
case 'I':
do {
struct iname *new = safe_malloc(sizeof(struct iname));
- if ((comma = strchr(optarg, '\001')))
+ if ((comma = strchr(arg, '\001')))
*comma++ = 0;
new->next = daemon->if_except;
daemon->if_except = new;
- new->name = safe_string_alloc(optarg);
- if (strchr(optarg, ':'))
+ new->name = safe_string_alloc(arg);
+ if (strchr(arg, ':'))
daemon->options |= OPT_NOWILD;
- optarg = comma;
- } while (optarg);
+ arg = comma;
+ } while (arg);
break;
case 'B':
{
struct in_addr addr;
- if ((addr.s_addr = inet_addr(optarg)) != (in_addr_t)-1)
+ if ((addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
{
struct bogus_addr *baddr = safe_malloc(sizeof(struct bogus_addr));
baddr->next = daemon->bogus_addr;
@@ -548,18 +570,18 @@ struct daemon *read_opts (int argc, char **argv)
case 'a':
do {
struct iname *new = safe_malloc(sizeof(struct iname));
- if ((comma = strchr(optarg, '\001')))
+ if ((comma = strchr(arg, '\001')))
*comma++ = 0;
new->next = daemon->if_addrs;
#ifdef HAVE_IPV6
- if (inet_pton(AF_INET, optarg, &new->addr.in.sin_addr))
+ if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr))
{
new->addr.sa.sa_family = AF_INET;
#ifdef HAVE_SOCKADDR_SA_LEN
new->addr.in.sin_len = sizeof(struct sockaddr_in);
#endif
}
- else if (inet_pton(AF_INET6, optarg, &new->addr.in6.sin6_addr))
+ else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr))
{
new->addr.sa.sa_family = AF_INET6;
new->addr.in6.sin6_flowinfo = htonl(0);
@@ -568,7 +590,7 @@ struct daemon *read_opts (int argc, char **argv)
#endif
}
#else
- if ((new->addr.in.sin_addr.s_addr = inet_addr(optarg)) != (in_addr_t)-1)
+ if ((new->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
{
new->addr.sa.sa_family = AF_INET;
#ifdef HAVE_SOCKADDR_SA_LEN
@@ -584,8 +606,8 @@ struct daemon *read_opts (int argc, char **argv)
}
daemon->if_addrs = new;
- optarg = comma;
- } while (optarg);
+ arg = comma;
+ } while (arg);
break;
case 'S':
@@ -593,28 +615,28 @@ struct daemon *read_opts (int argc, char **argv)
{
struct server *serv, *newlist = NULL;
- if (*optarg == '/')
+ if (*arg == '/')
{
char *end;
- optarg++;
- while ((end = strchr(optarg, '/')))
+ arg++;
+ while ((end = strchr(arg, '/')))
{
char *domain = NULL;
*end = 0;
/* # matches everything and becomes a zero length domain string */
- if (strcmp(optarg, "#") == 0)
+ if (strcmp(arg, "#") == 0)
domain = "";
- else if (!canonicalise(optarg) && strlen(optarg) != 0)
+ else if (!canonicalise(arg) && strlen(arg) != 0)
option = '?';
else
- domain = safe_string_alloc(optarg); /* NULL if strlen is zero */
+ domain = safe_string_alloc(arg); /* NULL if strlen is zero */
serv = safe_malloc(sizeof(struct server));
serv->next = newlist;
newlist = serv;
serv->sfd = NULL;
serv->domain = domain;
serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
- optarg = end+1;
+ arg = end+1;
}
if (!newlist)
{
@@ -639,7 +661,7 @@ struct daemon *read_opts (int argc, char **argv)
option = '?';
}
- if (!*optarg)
+ if (!*arg)
{
newlist->flags |= SERV_NO_ADDR; /* no server */
if (newlist->flags & SERV_LITERAL_ADDRESS)
@@ -650,7 +672,7 @@ struct daemon *read_opts (int argc, char **argv)
int source_port = 0, serv_port = NAMESERVER_PORT;
char *portno, *source;
- if ((source = strchr(optarg, '@'))) /* is there a source. */
+ if ((source = strchr(arg, '@'))) /* is there a source. */
{
*source = 0;
if ((portno = strchr(source+1, '#')))
@@ -664,7 +686,7 @@ struct daemon *read_opts (int argc, char **argv)
}
}
- if ((portno = strchr(optarg, '#'))) /* is there a port no. */
+ if ((portno = strchr(arg, '#'))) /* is there a port no. */
{
*portno = 0;
if (!atoi_check(portno+1, &serv_port))
@@ -675,9 +697,9 @@ struct daemon *read_opts (int argc, char **argv)
}
#ifdef HAVE_IPV6
- if (inet_pton(AF_INET, optarg, &newlist->addr.in.sin_addr))
+ if (inet_pton(AF_INET, arg, &newlist->addr.in.sin_addr))
#else
- if ((newlist->addr.in.sin_addr.s_addr = inet_addr(optarg)) != (in_addr_t) -1)
+ if ((newlist->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t) -1)
#endif
{
newlist->addr.in.sin_port = htons(serv_port);
@@ -701,7 +723,7 @@ struct daemon *read_opts (int argc, char **argv)
newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
}
#ifdef HAVE_IPV6
- else if (inet_pton(AF_INET6, optarg, &newlist->addr.in6.sin6_addr))
+ else if (inet_pton(AF_INET6, arg, &newlist->addr.in6.sin6_addr))
{
newlist->addr.in6.sin6_port = htons(serv_port);
newlist->source_addr.in6.sin6_port = htons(source_port);
@@ -752,7 +774,7 @@ struct daemon *read_opts (int argc, char **argv)
case 'c':
{
int size;
- if (!atoi_check(optarg, &size))
+ if (!atoi_check(arg, &size))
option = '?';
else
{
@@ -769,28 +791,28 @@ struct daemon *read_opts (int argc, char **argv)
}
case 'p':
- if (!atoi_check(optarg, &daemon->port))
+ if (!atoi_check(arg, &daemon->port))
option = '?';
break;
case 'P':
{
int i;
- if (!atoi_check(optarg, &i))
+ if (!atoi_check(arg, &i))
option = '?';
daemon->edns_pktsz = (unsigned short)i;
break;
}
case 'Q':
- if (!atoi_check(optarg, &daemon->query_port))
+ if (!atoi_check(arg, &daemon->query_port))
option = '?';
break;
case 'T':
{
int ttl;
- if (!atoi_check(optarg, &ttl))
+ if (!atoi_check(arg, &ttl))
option = '?';
else
daemon->local_ttl = (unsigned long)ttl;
@@ -798,7 +820,7 @@ struct daemon *read_opts (int argc, char **argv)
}
case 'X':
- if (!atoi_check(optarg, &daemon->dhcp_max))
+ if (!atoi_check(arg, &daemon->dhcp_max))
option = '?';
break;
@@ -819,25 +841,25 @@ struct daemon *read_opts (int argc, char **argv)
problem = "bad dhcp-range";
- for (cp = optarg; *cp; cp++)
+ for (cp = arg; *cp; cp++)
if (!(*cp == ' ' || *cp == '.' || (*cp >='0' && *cp <= '9')))
break;
- if (*cp != '\001' && (comma = strchr(optarg, '\001')))
+ if (*cp != '\001' && (comma = strchr(arg, '\001')))
{
*comma = 0;
- if (strstr(optarg, "net:") == optarg)
+ if (strstr(arg, "net:") == arg)
{
- new->netid.net = safe_string_alloc(optarg+4);
+ new->netid.net = safe_string_alloc(arg+4);
new->netid.next = NULL;
new->flags |= CONTEXT_FILTER;
}
else
- new->netid.net = safe_string_alloc(optarg);
+ new->netid.net = safe_string_alloc(arg);
a[0] = comma + 1;
}
else
- a[0] = optarg;
+ a[0] = arg;
for (k = 1; k < 5; k++)
@@ -940,7 +962,7 @@ struct daemon *read_opts (int argc, char **argv)
new->flags = 0;
- a[0] = optarg;
+ a[0] = arg;
for (k = 1; k < 6; k++)
{
if (!(a[k] = strchr(a[k-1], '\001')))
@@ -1069,76 +1091,81 @@ struct daemon *read_opts (int argc, char **argv)
case 'O':
{
struct dhcp_opt *new = safe_malloc(sizeof(struct dhcp_opt));
- char *cp;
+ char lenchar = 0, *cp;
int addrs, digs, is_addr, is_hex, is_dec;
- new->next = daemon->dhcp_opts;
new->len = 0;
new->is_addr = 0;
new->netid = NULL;
new->val = NULL;
+ new->vendor_class = NULL;
- if ((comma = strchr(optarg, '\001')))
+ if ((comma = strchr(arg, '\001')))
{
struct dhcp_netid *np = NULL;
*comma++ = 0;
do {
- for (cp = optarg; *cp; cp++)
+ for (cp = arg; *cp; cp++)
if (!(*cp == ' ' || (*cp >='0' && *cp <= '9')))
break;
if (!*cp)
break;
- new->netid = safe_malloc(sizeof (struct dhcp_netid));
- new->netid->net = safe_string_alloc(optarg);
- new->netid->next = np;
- np = new->netid;
- optarg = comma;
- if ((comma = strchr(optarg, '\001')))
+ if (strstr(arg, "vendor:") == arg)
+ new->vendor_class = safe_string_alloc(arg+7);
+ else
+ {
+ new->netid = safe_malloc(sizeof (struct dhcp_netid));
+ new->netid->net = safe_string_alloc(arg);
+ new->netid->next = np;
+ np = new->netid;
+ }
+ arg = comma;
+ if ((comma = strchr(arg, '\001')))
*comma++ = 0;
- } while (optarg);
+ } while (arg);
}
- if (!optarg || (new->opt = atoi(optarg)) == 0)
+ if (!arg || (new->opt = atoi(arg)) == 0)
{
option = '?';
problem = "bad dhcp-option";
}
- else if (comma && new->opt == 119)
+ else if (comma && new->opt == 119 && !new->vendor_class)
{
/* dns search, RFC 3397 */
unsigned char *q, *r, *tail;
unsigned char *p = NULL;
size_t newlen, len = 0;
- optarg = comma;
- if ((comma = strchr(optarg, '\001')))
+ arg = comma;
+ if ((comma = strchr(arg, '\001')))
*(comma++) = 0;
- while (optarg && *optarg)
+ while (arg && *arg)
{
- if (!canonicalise(optarg))
+ if (!canonicalise(arg))
{
option = '?';
problem = "bad domain in dhcp-option";
break;
}
- if (!(p = realloc(p, len + strlen(optarg) + 2)))
+ if (!(p = realloc(p, len + strlen(arg) + 2)))
die("could not get memory", NULL);
q = p + len;
/* add string on the end in RFC1035 format */
- while (*optarg)
+ while (*arg)
{
char *cp = q++;
int j;
- for (j = 0; *optarg && (*optarg != '.'); optarg++, j++)
- *q++ = *optarg;
+ for (j = 0; *arg && (*arg != '.'); arg++, j++)
+ *q++ = *arg;
*cp = j;
- if (*optarg)
- optarg++;
+ if (*arg)
+ arg++;
}
*q++ = 0;
@@ -1155,8 +1182,8 @@ struct daemon *read_opts (int argc, char **argv)
end:
len = newlen;
- optarg = comma;
- if (optarg && (comma = strchr(optarg, '\001')))
+ arg = comma;
+ if (arg && (comma = strchr(arg, '\001')))
*(comma++) = 0;
}
@@ -1184,7 +1211,15 @@ struct daemon *read_opts (int argc, char **argv)
is_dec = is_hex = 0;
else if (!((*cp >='0' && *cp <= '9') || *cp == '-'))
{
- is_dec = is_addr = 0;
+ is_addr = 0;
+ if (cp[1] == 0 && is_dec &&
+ (*cp == 'b' || *cp == 's' || *cp == 'i'))
+ {
+ lenchar = *cp;
+ *cp = 0;
+ }
+ else
+ is_dec = 0;
if (!((*cp >='A' && *cp <= 'F') ||
(*cp >='a' && *cp <= 'f')))
is_hex = 0;
@@ -1200,19 +1235,32 @@ struct daemon *read_opts (int argc, char **argv)
{
int i, val = atoi(comma);
/* assume numeric arg is 1 byte except for
- options where it is known otherwise. */
- switch (new->opt)
+ options where it is known otherwise.
+ For vendor class option, we have to hack. */
+ new->len = 1;
+ if (lenchar == 'b')
+ new->len = 1;
+ else if (lenchar == 's')
+ new->len = 2;
+ else if (lenchar == 'i')
+ new->len = 4;
+ else if (new->vendor_class)
{
- default:
- new->len = 1;
- break;
- case 13: case 22: case 25: case 26:
- new->len = 2;
- break;
- case 2: case 24: case 35: case 38:
- new->len = 4;
- break;
- }
+ if (val & 0xffff0000)
+ new->len = 4;
+ else if (val & 0xff00)
+ new->len = 2;
+ }
+ else
+ switch (new->opt)
+ {
+ case 13: case 22: case 25: case 26:
+ new->len = 2;
+ break;
+ case 2: case 24: case 35: case 38:
+ new->len = 4;
+ break;
+ }
new->val = safe_malloc(new->len);
for (i=0; i<new->len; i++)
new->val[i] = val>>((new->len - i - 1)*8);
@@ -1238,8 +1286,8 @@ struct daemon *read_opts (int argc, char **argv)
{
/* text arg */
new->len = strlen(comma);
- new->val = safe_malloc(new->len);
- memcpy(new->val, comma, new->len);
+ /* keep terminating zero on string */
+ new->val = safe_string_alloc(comma);
}
}
@@ -1255,44 +1303,53 @@ struct daemon *read_opts (int argc, char **argv)
free(new->netid);
if (new->val)
free(new->val);
+ if (new->vendor_class)
+ free(new->vendor_class);
free(new);
}
+ else if (new->vendor_class)
+ {
+ new->next = daemon->vendor_opts;
+ daemon->vendor_opts = new;
+ }
else
- daemon->dhcp_opts = new;
-
+ {
+ new->next = daemon->dhcp_opts;
+ daemon->dhcp_opts = new;
+ }
break;
}
case 'M':
{
struct dhcp_netid *id = NULL;
- while (optarg && strstr(optarg, "net:") == optarg)
+ while (arg && strstr(arg, "net:") == arg)
{
struct dhcp_netid *newid = safe_malloc(sizeof(struct dhcp_netid));
newid->next = id;
id = newid;
- if ((comma = strchr(optarg, '\001')))
+ if ((comma = strchr(arg, '\001')))
*comma++ = 0;
- newid->net = safe_string_alloc(optarg+4);
- optarg = comma;
+ newid->net = safe_string_alloc(arg+4);
+ arg = comma;
};
- if (!optarg)
+ if (!arg)
option = '?';
else
{
char *dhcp_file, *dhcp_sname = NULL;
struct in_addr dhcp_next_server;
- if ((comma = strchr(optarg, '\001')))
+ if ((comma = strchr(arg, '\001')))
*comma++ = 0;
- dhcp_file = safe_string_alloc(optarg);
+ dhcp_file = safe_string_alloc(arg);
dhcp_next_server.s_addr = 0;
if (comma)
{
- optarg = comma;
- if ((comma = strchr(optarg, '\001')))
+ arg = comma;
+ if ((comma = strchr(arg, '\001')))
*comma++ = 0;
- dhcp_sname = safe_string_alloc(optarg);
+ dhcp_sname = safe_string_alloc(arg);
if (comma && (dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1)
option = '?';
}
@@ -1323,13 +1380,13 @@ struct daemon *read_opts (int argc, char **argv)
case 'U':
case 'j':
{
- if (!(comma = strchr(optarg, '\001')))
+ if (!(comma = strchr(arg, '\001')))
option = '?';
else
{
struct dhcp_vendor *new = safe_malloc(sizeof(struct dhcp_vendor));
*comma = 0;
- new->netid.net = safe_string_alloc(optarg);
+ new->netid.net = safe_string_alloc(arg);
new->len = strlen(comma+1);
new->data = safe_malloc(new->len);
memcpy(new->data, comma+1, new->len);
@@ -1348,13 +1405,13 @@ struct daemon *read_opts (int argc, char **argv)
daemon->dhcp_ignore = new;
do {
struct dhcp_netid *member = safe_malloc(sizeof(struct dhcp_netid));
- if ((comma = strchr(optarg, '\001')))
+ if ((comma = strchr(arg, '\001')))
*comma++ = 0;
member->next = list;
list = member;
- member->net = safe_string_alloc(optarg);
- optarg = comma;
- } while (optarg);
+ member->net = safe_string_alloc(arg);
+ arg = comma;
+ } while (arg);
new->list = list;
break;
@@ -1369,7 +1426,7 @@ struct daemon *read_opts (int argc, char **argv)
mask.s_addr = 0xffffffff;
- a[0] = optarg;
+ a[0] = arg;
for (k = 1; k < 4; k++)
{
if (!(a[k] = strchr(a[k-1], '\001')))
@@ -1403,10 +1460,10 @@ struct daemon *read_opts (int argc, char **argv)
struct txt_record *new;
unsigned char *p, *q;
- if ((comma = strchr(optarg, '\001')))
+ if ((comma = strchr(arg, '\001')))
*(comma) = 0;
- if (!canonicalise(optarg))
+ if (!canonicalise(arg))
{
option = '?';
problem = "bad TXT record";
@@ -1461,7 +1518,7 @@ struct daemon *read_opts (int argc, char **argv)
if (comma)
*comma = 0;
- new->name = safe_string_alloc(optarg);
+ new->name = safe_string_alloc(arg);
break;
}
@@ -1471,35 +1528,35 @@ struct daemon *read_opts (int argc, char **argv)
char *name, *target = NULL;
struct mx_srv_record *new;
- if ((comma = strchr(optarg, '\001')))
+ if ((comma = strchr(arg, '\001')))
*(comma++) = 0;
- if (!canonicalise(optarg))
+ if (!canonicalise(arg))
{
option = '?';
problem = "bad SRV record";
break;
}
- name = safe_string_alloc(optarg);
+ name = safe_string_alloc(arg);
if (comma)
{
- optarg = comma;
- if ((comma = strchr(optarg, '\001')))
+ arg = comma;
+ if ((comma = strchr(arg, '\001')))
*(comma++) = 0;
- if (!canonicalise(optarg))
+ if (!canonicalise(arg))
{
option = '?';
problem = "bad SRV target";
break;
}
- target = safe_string_alloc(optarg);
+ target = safe_string_alloc(arg);
if (comma)
{
- optarg = comma;
- if ((comma = strchr(optarg, '\001')))
+ arg = comma;
+ if ((comma = strchr(arg, '\001')))
*(comma++) = 0;
- if (!atoi_check(optarg, &port))
+ if (!atoi_check(arg, &port))
{
option = '?';
problem = "invalid port number";
@@ -1507,10 +1564,10 @@ struct daemon *read_opts (int argc, char **argv)
}
if (comma)
{
- optarg = comma;
- if ((comma = strchr(optarg, '\001')))
+ arg = comma;
+ if ((comma = strchr(arg, '\001')))
*(comma++) = 0;
- if (!atoi_check(optarg, &priority))
+ if (!atoi_check(arg, &priority))
{
option = '?';
problem = "invalid priority";
@@ -1518,10 +1575,10 @@ struct daemon *read_opts (int argc, char **argv)
}
if (comma)
{
- optarg = comma;
- if ((comma = strchr(optarg, '\001')))
+ arg = comma;
+ if ((comma = strchr(arg, '\001')))
*(comma++) = 0;
- if (!atoi_check(optarg, &weight))
+ if (!atoi_check(arg, &weight))
{
option = '?';
problem = "invalid weight";
@@ -1601,11 +1658,12 @@ struct daemon *read_opts (int argc, char **argv)
if ((daemon->mxtarget || (daemon->options & OPT_LOCALMX)) && !mx)
{
- daemon->mxnames = safe_malloc(sizeof(struct mx_srv_record));
- daemon->mxnames->next = daemon->mxnames;
- daemon->mxnames->issrv = 0;
- daemon->mxnames->target = NULL;
- daemon->mxnames->name = safe_string_alloc(buff);
+ mx = safe_malloc(sizeof(struct mx_srv_record));
+ mx->next = daemon->mxnames;
+ mx->issrv = 0;
+ mx->target = NULL;
+ mx->name = safe_string_alloc(buff);
+ daemon->mxnames = mx;
}
if (!daemon->mxtarget)
diff --git a/src/rfc1035.c b/src/rfc1035.c
index ec29018..0b79a75 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -340,7 +340,8 @@ static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *heade
We ignore case in the names for the same reason. */
unsigned int questions_crc(HEADER *header, unsigned int plen, char *name)
{
- unsigned int q, crc = 0xffffffff;
+ int q;
+ unsigned int crc = 0xffffffff;
unsigned char *p1, *p = (unsigned char *)(header+1);
for (q = 0; q < ntohs(header->qdcount); q++)
diff --git a/src/rfc2131.c b/src/rfc2131.c
index 6745cb0..8f546c1 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -29,6 +29,7 @@
#define OPTION_HOSTNAME 12
#define OPTION_DOMAINNAME 15
#define OPTION_BROADCAST 28
+#define OPTION_VENDOR_CLASS_OPT 43
#define OPTION_REQUESTED_IP 50
#define OPTION_LEASE_TIME 51
#define OPTION_OVERLOAD 52
@@ -807,8 +808,7 @@ static int in_list(unsigned char *list, int opt)
static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
{
- struct dhcp_opt *tmp;
-
+ struct dhcp_opt *tmp;
for (tmp = opts; tmp; tmp = tmp->next)
if (tmp->opt == opt)
{
@@ -824,6 +824,40 @@ static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *
return netid ? option_find2(NULL, opts, opt) : NULL;
}
+static unsigned char *do_opt(struct dhcp_opt *opt, unsigned char *p, unsigned char *end, struct in_addr local)
+{
+ if (p + opt->len + 3 >= end)
+ return p;
+
+ *(p++) = opt->opt;
+ *(p++) = opt->len;
+
+ if (opt->len == 0)
+ return p;
+
+ if (opt->is_addr && !opt->vendor_class)
+ {
+ int j;
+ struct in_addr *a = (struct in_addr *)opt->val;
+ for (j = 0; j < opt->len; j+=INADDRSZ, a++)
+ {
+ /* zero means "self" (but not in vendorclass options.) */
+ if (a->s_addr == 0)
+ memcpy(p, &local, INADDRSZ);
+ else
+ memcpy(p, a, INADDRSZ);
+ p += INADDRSZ;
+ }
+ }
+ else
+ {
+ memcpy(p, opt->val, opt->len);
+ p += opt->len;
+ }
+
+ return p;
+}
+
static unsigned char *do_req_options(struct dhcp_context *context,
unsigned char *p, unsigned char *end,
unsigned char *req_options,
@@ -833,6 +867,7 @@ static unsigned char *do_req_options(struct dhcp_context *context,
struct in_addr subnet_addr)
{
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
+ char *vendor_class = NULL;
if (in_list(req_options, OPTION_MAXMESSAGE))
p = option_put(p, end, OPTION_MAXMESSAGE, 2, end - (unsigned char *)daemon->dhcp_packet);
@@ -877,8 +912,7 @@ static unsigned char *do_req_options(struct dhcp_context *context,
if (opt->opt == OPTION_HOSTNAME ||
opt->opt == OPTION_MAXMESSAGE ||
!in_list(req_options, opt->opt) ||
- opt != option_find2(netid, config_opts, opt->opt) ||
- p + opt->len + 3 >= end)
+ opt != option_find2(netid, config_opts, opt->opt))
continue;
/* For the options we have default values on
@@ -890,32 +924,54 @@ static unsigned char *do_req_options(struct dhcp_context *context,
opt->opt == OPTION_ROUTER ||
opt->opt == OPTION_DNSSERVER))
continue;
-
- *(p++) = opt->opt;
- *(p++) = opt->len;
- if (opt->len == 0)
- continue;
-
- if (opt->is_addr)
+
+ /* opt->val has terminating zero */
+ if (opt->opt == OPTION_VENDOR_ID)
+ vendor_class = opt->val;
+ else
+ p = do_opt(opt, p, end, context->local);
+ }
+
+ if (in_list(req_options, OPTION_VENDOR_ID))
+ {
+ for (opt = daemon->vendor_opts; opt; opt = opt->next)
+ if (!opt->netid || match_netid(opt->netid, netid))
+ {
+ if (vendor_class && strcmp(vendor_class, opt->vendor_class) != 0)
+ syslog(LOG_WARNING, "More than one vendor class matches, using %s", vendor_class);
+ else
+ vendor_class = opt->vendor_class;
+ }
+
+ if (vendor_class)
{
- int j;
- struct in_addr *a = (struct in_addr *)opt->val;
- for (j = 0; j < opt->len; j+=INADDRSZ, a++)
+ p = option_put_string(p, end, OPTION_VENDOR_ID, vendor_class);
+
+ if (in_list(req_options, OPTION_VENDOR_CLASS_OPT))
{
- /* zero means "self" */
- if (a->s_addr == 0)
- memcpy(p, &context->local, INADDRSZ);
- else
- memcpy(p, a, INADDRSZ);
- p += INADDRSZ;
+ unsigned char *plen, *oend = end;
+
+ /* encapsulated options can only be 256 bytes,
+ even of the packet is larger */
+ if (p + 256 < end)
+ oend = p + 256;
+
+ if (p + 3 >= oend)
+ return p;
+
+ *(p++) = OPTION_VENDOR_CLASS_OPT;
+ plen = p++; /* fill in later */
+
+ for (opt = daemon->vendor_opts; opt; opt = opt->next)
+ if ((!opt->netid || match_netid(opt->netid, netid)) &&
+ strcmp(vendor_class, opt->vendor_class) == 0)
+ p = do_opt(opt, p, oend, context->local);
+
+ *plen = p - plen - 1;
}
}
- else
- {
- memcpy(p, opt->val, opt->len);
- p += opt->len;
- }
- }
+ }
+
return p;
}