import of dnsmasq-2.40.tar.gz

This commit is contained in:
Simon Kelley
2007-08-29 11:24:47 +01:00
parent f2621c7ff0
commit 5aabfc78bc
38 changed files with 6006 additions and 4068 deletions

111
CHANGELOG
View File

@@ -2234,3 +2234,114 @@ release 2.39
to add a domain name with a dynamic IP address taken from to add a domain name with a dynamic IP address taken from
the address of a local network interface. Useful for the address of a local network interface. Useful for
networks with dynamic IPs. networks with dynamic IPs.
version 2.40
Make SIGUSR2 close-and-reopen the logfile when logging
direct to a file. Thanks to Carlos Carvalho for
suggesting this. When a logfile is created, change
its ownership to the user dnsmasq will run as, don't
leave it owned by root.
Set a special tag, "known" for hosts which are matched by
a dhcp-host or /etc/ethers line. This is especially
useful to be able to do --dhcp-ignore=#known, like ISCs
"deny unknown-clients".
Explicitly set a umask before creating the leases file,
rather than relying on whatever we inherited. The
permissions are set to 644.
Fix handling of fully-qualified names in --dhcp-host
directives and in /etc/ethers. These are now rejected
if the domain doesn't match that given by --domain,
and used correctly otherwise. Before, putting
a FQDN here could cause the whole FQDN to be used as
hostname. Thanks to Michael Heimpold for the bug report.
Massive but trivial edit to make the "daemon" variable
global, instead of copying the same value around as the
first argument to half the functions in the program.
Updated Spanish manpage and message catalog. Thanks
to Chris Chatham.
Added patch for support of DNS LOC records in
contrib/dns-loc. Thanks to Lorenz Schori.
Fixed error in manpage: dhcp-ignore-name ->
dhcp-ignore-names. Thanks to Daniel Mentz for spotting
this.
Use client-id as hash-seed for DHCP address allocation
with Firewire and Infiniband, as these don't supply an MAC
address.
Tweaked TFTP file-open code to make it behave sensibly
when the filesystem changes under its feet.
Added DNSMASQ_TIME_REMAINING environment variable to the
lease-script.
Always send replies to DHCPINFORM requests to the source
of the request and not to the address in ciaddr. This
allows third-party queries.
Return "lease time remaining" in the reply to a DHCPINFORM
request if there exists a lease for the host sending the
request.
Added --dhcp-hostsfile option. This gives a superset of
the functionality provided by /etc/ethers. Thanks to
Greg Kurtzer for the suggestion.
Accept keyword "server" as a synonym for "nameserver" in
resolv.conf. Thanks to Andrew Bartlett for the report.
Add --tftp-unique-root option. Suggestion from Dermot
Bradley.
Tweak TFTP retry timer to avoid problems with difficult
clients. Thanks to Dermot Bradley for assistance with
this.
Continue to use unqualified hostnames provided by DHCP
clients, even if the domain part is illegal. (The domain
is ignored, and an error logged.) Previously in this
situation, the whole name whould have been
rejected. Thanks to Jima for the patch.
Handle EINTR returns from wait() correctly and reap
our children's children if necessary. This fixes
a problem with zombie-creation under *BSD when using
--dhcp-script.
Escape spaces in hostnames when they are stored in the
leases file and passed to the lease-change
script. Suggestion from Ben Voigt.
Re-run the lease chamge script with an "old" event for
each lease when dnsmasq receives a SIGHUP.
Added more useful exit codes, including passing on a
non-zero exit code from the lease-script "init" call when
--leasefile-ro is set.
Log memory allocation failure whilst the daemon is
running. Allocation failures during startup are fatal,
but lack of memory whilst running is worked around.
This used to be silent, but now is logged.
Fixed misaligned memory access which caused problems on
Blackfin CPUs. Thanks to Alex Landau for the patch.
Don't include (useless) script-calling code when NO_FORK
is set. Since this tends to be used on very small uclinux
systems, it's worth-while to save some code-size.
Don't set REUSEADDR on TFTP listening socket. There's no
need to do so, and it creates confusing behaviour when
inetd is also listening on the same port. Thanks to Erik
Brown for spotting the problem.

12
contrib/dns-loc/README Normal file
View File

@@ -0,0 +1,12 @@
Hi Simon
Here is a patch against dnsmasq 2.39 which provides support for LOC
entries in order to assign location information to dns records
(rfc1876). I tested it on OSX and on OpenWRT.
Cheers
Lorenz
More info:
http://www.ckdhr.com/dns-loc/
http://www.faqs.org/rfcs/rfc1876.html

View File

@@ -0,0 +1,522 @@
diff -Nur dnsmasq-2.39-orig/bld/Makefile dnsmasq-2.39/bld/Makefile
--- dnsmasq-2.39-orig/bld/Makefile 2007-02-17 14:37:06.000000000 +0100
+++ dnsmasq-2.39/bld/Makefile 2007-05-20 18:23:44.000000000 +0200
@@ -2,7 +2,7 @@
PKG_CONFIG ?= pkg-config
-OBJS = cache.o rfc1035.o util.o option.o forward.o isc.o network.o \
+OBJS = cache.o rfc1035.o rfc1876.o util.o option.o forward.o isc.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o
diff -Nur dnsmasq-2.39-orig/src/dnsmasq.h dnsmasq-2.39/src/dnsmasq.h
--- dnsmasq-2.39-orig/src/dnsmasq.h 2007-04-20 12:53:38.000000000 +0200
+++ dnsmasq-2.39/src/dnsmasq.h 2007-05-20 19:50:37.000000000 +0200
@@ -162,6 +162,12 @@
struct interface_name *next;
};
+struct loc_record {
+ char *name, loc[16];
+ unsigned short class;
+ struct loc_record *next;
+};
+
union bigname {
char name[MAXDNAME];
union bigname *next; /* freelist */
@@ -476,6 +482,7 @@
struct mx_srv_record *mxnames;
struct txt_record *txt;
struct ptr_record *ptr;
+ struct loc_record *loc;
struct interface_name *int_names;
char *mxtarget;
char *lease_file;
@@ -725,3 +732,6 @@
void tftp_request(struct listener *listen, struct daemon *daemon, time_t now);
void check_tftp_listeners(struct daemon *daemon, fd_set *rset, time_t now);
#endif
+
+/* rfc1876 */
+u_int32_t loc_aton(const char *ascii, u_char *binary);
diff -Nur dnsmasq-2.39-orig/src/option.c dnsmasq-2.39/src/option.c
--- dnsmasq-2.39-orig/src/option.c 2007-04-19 23:34:49.000000000 +0200
+++ dnsmasq-2.39/src/option.c 2007-05-20 20:15:15.000000000 +0200
@@ -43,6 +43,7 @@
#define LOPT_REMOTE 269
#define LOPT_SUBSCR 270
#define LOPT_INTNAME 271
+#define LOPT_LOC 272
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -122,6 +123,7 @@
{"tftp-root", 1, 0, LOPT_PREFIX },
{"tftp-max", 1, 0, LOPT_TFTP_MAX },
{"ptr-record", 1, 0, LOPT_PTR },
+ {"loc-record", 1, 0, LOPT_LOC },
#if defined(__FreeBSD__) || defined(__DragonFly__)
{"bridge-interface", 1, 0 , LOPT_BRIDGE },
#endif
@@ -235,6 +237,7 @@
{ "-y, --localise-queries", gettext_noop("Answer DNS queries based on the interface a query was sent to."), NULL },
{ "-Y --txt-record=name,txt....", gettext_noop("Specify TXT DNS record."), NULL },
{ " --ptr-record=name,target", gettext_noop("Specify PTR DNS record."), NULL },
+ { " --loc-record=name,lat lon alt", gettext_noop("Specify LOC DNS record."), NULL },
{ " --interface-name=name,interface", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
{ "-z, --bind-interfaces", gettext_noop("Bind only to interfaces in use."), NULL },
{ "-Z, --read-ethers", gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
@@ -1835,6 +1838,37 @@
new->intr = safe_string_alloc(comma);
break;
}
+
+ case LOPT_LOC:
+ {
+ struct loc_record *new;
+ unsigned char *p, *q;
+
+ comma = split(arg);
+
+ if (!canonicalise_opt(arg))
+ {
+ option = '?';
+ problem = _("bad LOC record");
+ break;
+ }
+
+ new = safe_malloc(sizeof(struct loc_record));
+ new->next = daemon->loc;
+ daemon->loc = new;
+ new->class = C_IN;
+ if (!comma || loc_aton(comma,new->loc)!=16)
+ {
+ option = '?';
+ problem = _("bad LOC record");
+ break;
+ }
+
+ if (comma)
+ *comma = 0;
+ new->name = safe_string_alloc(arg);
+ break;
+ }
case LOPT_PTR: /* --ptr-record */
{
diff -Nur dnsmasq-2.39-orig/src/rfc1035.c dnsmasq-2.39/src/rfc1035.c
--- dnsmasq-2.39-orig/src/rfc1035.c 2007-04-20 12:54:26.000000000 +0200
+++ dnsmasq-2.39/src/rfc1035.c 2007-05-20 18:22:46.000000000 +0200
@@ -1112,6 +1112,27 @@
}
}
+ if (qtype == T_LOC || qtype == T_ANY)
+ {
+ struct loc_record *t;
+ for(t = daemon->loc; t ; t = t->next)
+ {
+ if (t->class == qclass && hostname_isequal(name, t->name))
+ {
+ ans = 1;
+ if (!dryrun)
+ {
+ log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, 0, NULL, 0);
+ if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
+ daemon->local_ttl, NULL,
+ T_LOC, t->class, "t", 16, t->loc))
+ anscount++;
+
+ }
+ }
+ }
+ }
+
if (qclass == C_IN)
{
if (qtype == T_PTR || qtype == T_ANY)
diff -Nur dnsmasq-2.39-orig/src/rfc1876.c dnsmasq-2.39/src/rfc1876.c
--- dnsmasq-2.39-orig/src/rfc1876.c 1970-01-01 01:00:00.000000000 +0100
+++ dnsmasq-2.39/src/rfc1876.c 2007-05-20 19:50:10.000000000 +0200
@@ -0,0 +1,379 @@
+/*
+ * routines to convert between on-the-wire RR format and zone file
+ * format. Does not contain conversion to/from decimal degrees;
+ * divide or multiply by 60*60*1000 for that.
+ */
+
+#include "dnsmasq.h"
+
+static unsigned int poweroften[10] = {1, 10, 100, 1000, 10000, 100000,
+ 1000000,10000000,100000000,1000000000};
+
+/* takes an XeY precision/size value, returns a string representation.*/
+static const char *
+precsize_ntoa(u_int8_t prec)
+{
+ static char retbuf[sizeof("90000000.00")];
+ unsigned long val;
+ int mantissa, exponent;
+
+ mantissa = (int)((prec >> 4) & 0x0f) % 10;
+ exponent = (int)((prec >> 0) & 0x0f) % 10;
+
+ val = mantissa * poweroften[exponent];
+
+ (void) sprintf(retbuf,"%d.%.2d", val/100, val%100);
+ return (retbuf);
+}
+
+/* converts ascii size/precision X * 10**Y(cm) to 0xXY. moves pointer.*/
+static u_int8_t
+precsize_aton(char **strptr)
+{
+ unsigned int mval = 0, cmval = 0;
+ u_int8_t retval = 0;
+ register char *cp;
+ register int exponent;
+ register int mantissa;
+
+ cp = *strptr;
+
+ while (isdigit(*cp))
+ mval = mval * 10 + (*cp++ - '0');
+
+ if (*cp == '.') { /* centimeters */
+ cp++;
+ if (isdigit(*cp)) {
+ cmval = (*cp++ - '0') * 10;
+ if (isdigit(*cp)) {
+ cmval += (*cp++ - '0');
+ }
+ }
+ }
+ cmval = (mval * 100) + cmval;
+
+ for (exponent = 0; exponent < 9; exponent++)
+ if (cmval < poweroften[exponent+1])
+ break;
+
+ mantissa = cmval / poweroften[exponent];
+ if (mantissa > 9)
+ mantissa = 9;
+
+ retval = (mantissa << 4) | exponent;
+
+ *strptr = cp;
+
+ return (retval);
+}
+
+/* converts ascii lat/lon to unsigned encoded 32-bit number.
+ * moves pointer. */
+static u_int32_t
+latlon2ul(char **latlonstrptr,int *which)
+{
+ register char *cp;
+ u_int32_t retval;
+ int deg = 0, min = 0, secs = 0, secsfrac = 0;
+
+ cp = *latlonstrptr;
+
+ while (isdigit(*cp))
+ deg = deg * 10 + (*cp++ - '0');
+
+ while (isspace(*cp))
+ cp++;
+
+ if (!(isdigit(*cp)))
+ goto fndhemi;
+
+ while (isdigit(*cp))
+ min = min * 10 + (*cp++ - '0');
+ while (isspace(*cp))
+ cp++;
+
+ if (!(isdigit(*cp)))
+ goto fndhemi;
+
+ while (isdigit(*cp))
+ secs = secs * 10 + (*cp++ - '0');
+
+ if (*cp == '.') { /* decimal seconds */
+ cp++;
+ if (isdigit(*cp)) {
+ secsfrac = (*cp++ - '0') * 100;
+ if (isdigit(*cp)) {
+ secsfrac += (*cp++ - '0') * 10;
+ if (isdigit(*cp)) {
+ secsfrac += (*cp++ - '0');
+ }
+ }
+ }
+ }
+
+ while (!isspace(*cp)) /* if any trailing garbage */
+ cp++;
+
+ while (isspace(*cp))
+ cp++;
+
+ fndhemi:
+ switch (*cp) {
+ case 'N': case 'n':
+ case 'E': case 'e':
+ retval = ((unsigned)1<<31)
+ + (((((deg * 60) + min) * 60) + secs) * 1000)
+ + secsfrac;
+ break;
+ case 'S': case 's':
+ case 'W': case 'w':
+ retval = ((unsigned)1<<31)
+ - (((((deg * 60) + min) * 60) + secs) * 1000)
+ - secsfrac;
+ break;
+ default:
+ retval = 0; /* invalid value -- indicates error */
+ break;
+ }
+
+ switch (*cp) {
+ case 'N': case 'n':
+ case 'S': case 's':
+ *which = 1; /* latitude */
+ break;
+ case 'E': case 'e':
+ case 'W': case 'w':
+ *which = 2; /* longitude */
+ break;
+ default:
+ *which = 0; /* error */
+ break;
+ }
+
+ cp++; /* skip the hemisphere */
+
+ while (!isspace(*cp)) /* if any trailing garbage */
+ cp++;
+
+ while (isspace(*cp)) /* move to next field */
+ cp++;
+
+ *latlonstrptr = cp;
+
+ return (retval);
+}
+
+/* converts a zone file representation in a string to an RDATA
+ * on-the-wire representation. */
+u_int32_t
+loc_aton(const char *ascii, u_char *binary)
+{
+ const char *cp, *maxcp;
+ u_char *bcp;
+
+ u_int32_t latit = 0, longit = 0, alt = 0;
+ u_int32_t lltemp1 = 0, lltemp2 = 0;
+ int altmeters = 0, altfrac = 0, altsign = 1;
+ u_int8_t hp = 0x16; /* default = 1e6 cm = 10000.00m = 10km */
+ u_int8_t vp = 0x13; /* default = 1e3 cm = 10.00m */
+ u_int8_t siz = 0x12; /* default = 1e2 cm = 1.00m */
+ int which1 = 0, which2 = 0;
+
+ cp = ascii;
+ maxcp = cp + strlen(ascii);
+
+ lltemp1 = latlon2ul(&cp, &which1);
+ lltemp2 = latlon2ul(&cp, &which2);
+
+ switch (which1 + which2) {
+ case 3: /* 1 + 2, the only valid combination */
+ if ((which1 == 1) && (which2 == 2)) { /* normal case */
+ latit = lltemp1;
+ longit = lltemp2;
+ } else if ((which1 == 2) && (which2 == 1)) {/*reversed*/
+ longit = lltemp1;
+ latit = lltemp2;
+ } else { /* some kind of brokenness */
+ return 0;
+ }
+ break;
+ default: /* we didn't get one of each */
+ return 0;
+ }
+
+ /* altitude */
+ if (*cp == '-') {
+ altsign = -1;
+ cp++;
+ }
+
+ if (*cp == '+')
+ cp++;
+
+ while (isdigit(*cp))
+ altmeters = altmeters * 10 + (*cp++ - '0');
+
+ if (*cp == '.') { /* decimal meters */
+ cp++;
+ if (isdigit(*cp)) {
+ altfrac = (*cp++ - '0') * 10;
+ if (isdigit(*cp)) {
+ altfrac += (*cp++ - '0');
+ }
+ }
+ }
+
+ alt = (10000000 + (altsign * (altmeters * 100 + altfrac)));
+
+ while (!isspace(*cp) && (cp < maxcp))
+ /* if trailing garbage or m */
+ cp++;
+
+ while (isspace(*cp) && (cp < maxcp))
+ cp++;
+ if (cp >= maxcp)
+ goto defaults;
+
+ siz = precsize_aton(&cp);
+
+ while (!isspace(*cp) && (cp < maxcp))/*if trailing garbage or m*/
+ cp++;
+
+ while (isspace(*cp) && (cp < maxcp))
+ cp++;
+
+ if (cp >= maxcp)
+ goto defaults;
+
+ hp = precsize_aton(&cp);
+
+ while (!isspace(*cp) && (cp < maxcp))/*if trailing garbage or m*/
+ cp++;
+
+ while (isspace(*cp) && (cp < maxcp))
+ cp++;
+
+ if (cp >= maxcp)
+ goto defaults;
+
+ vp = precsize_aton(&cp);
+
+ defaults:
+
+ bcp = binary;
+ *bcp++ = (u_int8_t) 0; /* version byte */
+ *bcp++ = siz;
+ *bcp++ = hp;
+ *bcp++ = vp;
+ PUTLONG(latit,bcp);
+ PUTLONG(longit,bcp);
+ PUTLONG(alt,bcp);
+
+ return (16); /* size of RR in octets */
+}
+
+/* takes an on-the-wire LOC RR and prints it in zone file
+ * (human readable) format. */
+char *
+loc_ntoa(const u_char *binary,char *ascii)
+{
+ static char tmpbuf[255*3];
+
+ register char *cp;
+ register const u_char *rcp;
+
+ int latdeg, latmin, latsec, latsecfrac;
+ int longdeg, longmin, longsec, longsecfrac;
+ char northsouth, eastwest;
+ int altmeters, altfrac, altsign;
+
+ const int referencealt = 100000 * 100;
+
+ int32_t latval, longval, altval;
+ u_int32_t templ;
+ u_int8_t sizeval, hpval, vpval, versionval;
+
+ char *sizestr, *hpstr, *vpstr;
+
+ rcp = binary;
+ if (ascii)
+ cp = ascii;
+ else {
+ cp = tmpbuf;
+ }
+
+ versionval = *rcp++;
+
+ if (versionval) {
+ sprintf(cp,"; error: unknown LOC RR version");
+ return (cp);
+ }
+
+ sizeval = *rcp++;
+
+ hpval = *rcp++;
+ vpval = *rcp++;
+
+ GETLONG(templ,rcp);
+ latval = (templ - ((unsigned)1<<31));
+
+ GETLONG(templ,rcp);
+ longval = (templ - ((unsigned)1<<31));
+
+ GETLONG(templ,rcp);
+ if (templ < referencealt) { /* below WGS 84 spheroid */
+ altval = referencealt - templ;
+ altsign = -1;
+ } else {
+ altval = templ - referencealt;
+ altsign = 1;
+ }
+
+ if (latval < 0) {
+ northsouth = 'S';
+ latval = -latval;
+ }
+ else
+ northsouth = 'N';
+
+ latsecfrac = latval % 1000;
+ latval = latval / 1000;
+ latsec = latval % 60;
+ latval = latval / 60;
+ latmin = latval % 60;
+ latval = latval / 60;
+ latdeg = latval;
+
+ if (longval < 0) {
+ eastwest = 'W';
+ longval = -longval;
+ }
+ else
+ eastwest = 'E';
+
+ longsecfrac = longval % 1000;
+ longval = longval / 1000;
+ longsec = longval % 60;
+ longval = longval / 60;
+ longmin = longval % 60;
+ longval = longval / 60;
+ longdeg = longval;
+
+ altfrac = altval % 100;
+ altmeters = (altval / 100) * altsign;
+
+ sizestr = strdup(precsize_ntoa(sizeval));
+ hpstr = strdup(precsize_ntoa(hpval));
+ vpstr = strdup(precsize_ntoa(vpval));
+
+ sprintf(cp,
+ "%d %.2d %.2d.%.3d %c %d %.2d %.2d.%.3d %c %d.%.2dm %sm %sm %sm",
+ latdeg, latmin, latsec, latsecfrac, northsouth,
+ longdeg, longmin, longsec, longsecfrac, eastwest,
+ altmeters, altfrac, sizestr, hpstr, vpstr);
+ free(sizestr);
+ free(hpstr);
+ free(vpstr);
+
+ return (cp);
+}

View File

@@ -23,6 +23,6 @@
# will port forward port 53 UDP and TCP from this host to port 53 on dnsserver. # will port forward port 53 UDP and TCP from this host to port 53 on dnsserver.
# #
# Port forwards will recreated when dnsmasq restarts after a reboot, and # Port forwards will recreated when dnsmasq restarts after a reboot, and
# removed when DHCP leases expire. After editing this file, restart dnsmasq # removed when DHCP leases expire. After editing this file, send
# to install new iptables entries in the kernel. # SIGHUP to dnsmasq to install new iptables entries in the kernel.

View File

@@ -1,7 +1,6 @@
CFLAGS?= -O2 CFLAGS?= -O2 -Wall -W
all: dhcp_release.c all: dhcp_release dhcp_lease_time
$(CC) $(CFLAGS) $(RPM_OPT_FLAGS) -Wall -W dhcp_release.c -o dhcp_release
clean: clean:
rm -f *~ *.o core dhcp_release rm -f *~ *.o core dhcp_release dhcp_lease_time

View File

@@ -0,0 +1,214 @@
/* Copyright (c) 2007 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
/* dhcp_lease_time <address> */
/* Send a DHCPINFORM message to a dnsmasq server running on the local host
and print (to stdout) the time remaining in any lease for the given
address. The time is given as string printed to stdout.
If an error occurs or no lease exists for the given address,
nothing is sent to stdout a message is sent to stderr and a
non-zero error code is returned.
Requires dnsmasq 2.40 or later.
*/
#include <sys/types.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <errno.h>
#define DHCP_CHADDR_MAX 16
#define BOOTREQUEST 1
#define DHCP_COOKIE 0x63825363
#define OPTION_PAD 0
#define OPTION_LEASE_TIME 51
#define OPTION_OVERLOAD 52
#define OPTION_MESSAGE_TYPE 53
#define OPTION_END 255
#define DHCPINFORM 8
#define DHCP_SERVER_PORT 67
#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
#define option_ptr(opt) ((void *)&(((unsigned char *)(opt))[2]))
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
struct dhcp_packet {
u8 op, htype, hlen, hops;
u32 xid;
u16 secs, flags;
struct in_addr ciaddr, yiaddr, siaddr, giaddr;
u8 chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
u32 cookie;
unsigned char options[308];
};
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
{
while (*p != OPTION_END)
{
if (p >= end)
return NULL; /* malformed packet */
else if (*p == OPTION_PAD)
p++;
else
{
int opt_len;
if (p >= end - 2)
return NULL; /* malformed packet */
opt_len = option_len(p);
if (p >= end - (2 + opt_len))
return NULL; /* malformed packet */
if (*p == opt && opt_len >= minsize)
return p;
p += opt_len + 2;
}
}
return opt == OPTION_END ? p : NULL;
}
static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
{
unsigned char *ret, *overload;
/* skip over DHCP cookie; */
if ((ret = option_find1(&mess->options[0], ((unsigned char *)mess) + size, opt_type, minsize)))
return ret;
/* look for overload option. */
if (!(overload = option_find1(&mess->options[0], ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
return NULL;
/* Can we look in filename area ? */
if ((overload[2] & 1) &&
(ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
return ret;
/* finally try sname area */
if ((overload[2] & 2) &&
(ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
return ret;
return NULL;
}
static unsigned int option_uint(unsigned char *opt, int size)
{
/* this worries about unaligned data and byte order */
unsigned int ret = 0;
int i;
unsigned char *p = option_ptr(opt);
for (i = 0; i < size; i++)
ret = (ret << 8) | *p++;
return ret;
}
int main(int argc, char **argv)
{
struct in_addr lease;
struct dhcp_packet packet;
unsigned char *p = packet.options;
struct sockaddr_in dest;
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
ssize_t rc;
if (argc < 2)
{
fprintf(stderr, "usage: dhcp_lease_time <address>\n");
exit(1);
}
if (fd == -1)
{
perror("cannot create socket");
exit(1);
}
lease.s_addr = inet_addr(argv[1]);
memset(&packet, 0, sizeof(packet));
packet.hlen = 0;
packet.htype = 0;
packet.op = BOOTREQUEST;
packet.ciaddr = lease;
packet.cookie = htonl(DHCP_COOKIE);
*(p++) = OPTION_MESSAGE_TYPE;
*(p++) = 1;
*(p++) = DHCPINFORM;
*(p++) = OPTION_END;
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("127.0.0.1");
dest.sin_port = ntohs(DHCP_SERVER_PORT);
if (sendto(fd, &packet, sizeof(packet), 0,
(struct sockaddr *)&dest, sizeof(dest)) == -1)
{
perror("sendto failed");
exit(1);
}
alarm(3); /* noddy timeout. */
rc = recv(fd, &packet, sizeof(packet), 0);
if (rc < (ssize_t)(sizeof(packet) - sizeof(packet.options)))
{
perror("recv failed");
exit(1);
}
if ((p = option_find(&packet, (size_t)rc, OPTION_LEASE_TIME, 4)))
{
unsigned int t = option_uint(p, 4);
if (t == 0xffffffff)
printf("infinite");
else
{
unsigned int x;
if ((x = t/86400))
printf("%dd", x);
if ((x = (t/3600)%24))
printf("%dh", x);
if ((x = (t/60)%60))
printf("%dm", x);
if ((x = t%60))
printf("%ds", x);
}
return 0;
}
return 1; /* no lease */
}

View File

@@ -180,6 +180,12 @@
# any machine with ethernet address starting 11:22:33: # any machine with ethernet address starting 11:22:33:
#dhcp-host=11:22:33:*:*:*,net:red #dhcp-host=11:22:33:*:*:*,net:red
# Ignore any clients which are specified in dhcp-host lines
# or /etc/ethers. Equivalent to ISC "deny unkown-clients".
# This relies on the special "known" tag which is set when
# a host is matched.
#dhcp-ignore=#known
# Send extra options which are tagged as "red" to any machine whose # Send extra options which are tagged as "red" to any machine whose
# DHCP vendorclass string includes the substring "Linux" # DHCP vendorclass string includes the substring "Linux"
#dhcp-vendorclass=red,Linux #dhcp-vendorclass=red,Linux
@@ -204,7 +210,7 @@
# run "dnsmasq --help dhcp" to get a list. # run "dnsmasq --help dhcp" to get a list.
# Note that all the common settings, such as netmask and # Note that all the common settings, such as netmask and
# broadcast address, DNS server and default route, are given # broadcast address, DNS server and default route, are given
# sane defaults by dnsmasq. You very likely will not need any # sane defaults by dnsmasq. You very likely will not need
# any dhcp-options. If you use Windows clients and Samba, there # any dhcp-options. If you use Windows clients and Samba, there
# are some options which are recommended, they are detailed at the # are some options which are recommended, they are detailed at the
# end of this section. # end of this section.
@@ -333,7 +339,7 @@
# whether it has a record of the lease or not. This avoids long timeouts # whether it has a record of the lease or not. This avoids long timeouts
# when a machine wakes up on a new network. DO NOT enable this if there's # when a machine wakes up on a new network. DO NOT enable this if there's
# the slighest chance that you might end up accidentally configuring a DHCP # the slighest chance that you might end up accidentally configuring a DHCP
# server for your campus/company accidentally. The ISC server uses the same # server for your campus/company accidentally. The ISC server uses
# the same option, and this URL provides more information: # the same option, and this URL provides more information:
# http://www.isc.org/index.pl?/sw/dhcp/authoritative.php # http://www.isc.org/index.pl?/sw/dhcp/authoritative.php
#dhcp-authoritative #dhcp-authoritative

View File

@@ -6,8 +6,8 @@ dnsmasq \- A lightweight DHCP and caching DNS server.
.I [OPTION]... .I [OPTION]...
.SH "DESCRIPTION" .SH "DESCRIPTION"
.BR dnsmasq .BR dnsmasq
is a lightweight DNS, TFTP and DHCP server. It is intended to provide coupled DNS and DHCP service to a is a lightweight DNS, TFTP and DHCP server. It is intended to provide
LAN. coupled DNS and DHCP service to a LAN.
.PP .PP
Dnsmasq accepts DNS queries and either answers them from a small, local, Dnsmasq accepts DNS queries and either answers them from a small, local,
cache or forwards them to a real, recursive, DNS server. It loads the cache or forwards them to a real, recursive, DNS server. It loads the
@@ -73,7 +73,9 @@ the facilty given contains at least one '/' character, it is taken to
be a filename, and dnsmasq logs to the given file, instead of be a filename, and dnsmasq logs to the given file, instead of
syslog. (Errors whilst reading configuration will still go to syslog, syslog. (Errors whilst reading configuration will still go to syslog,
but all output from a successful startup, and all output whilst but all output from a successful startup, and all output whilst
running, will go exclusively to the file.) running, will go exclusively to the file.) When logging to a file,
dnsmasq will close and reopen the file when it receives SIGUSR2. This
allows the log file to be rotated without stopping dnsmasq.
.TP .TP
.B --log-async[=<lines>] .B --log-async[=<lines>]
Enable asynchronous logging and optionally set the limit on the Enable asynchronous logging and optionally set the limit on the
@@ -252,7 +254,7 @@ or domain parts, to upstream nameservers. If the name is not known
from /etc/hosts or DHCP then a "not found" answer is returned. from /etc/hosts or DHCP then a "not found" answer is returned.
.TP .TP
.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>][@<source>[#<port>]]] .B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>][@<source>[#<port>]]]
Specify IP address of upstream severs directly. Setting this flag does Specify IP address of upstream servers directly. Setting this flag does
not suppress reading of /etc/resolv.conf, use -R to do that. If one or not suppress reading of /etc/resolv.conf, use -R to do that. If one or
more more
optional domains are given, that server is used only for those domains optional domains are given, that server is used only for those domains
@@ -364,8 +366,7 @@ Set the size of dnsmasq's cache. The default is 150 names. Setting the cache siz
.B \-N, --no-negcache .B \-N, --no-negcache
Disable negative caching. Negative caching allows dnsmasq to remember Disable negative caching. Negative caching allows dnsmasq to remember
"no such domain" answers from upstream nameservers and answer "no such domain" answers from upstream nameservers and answer
identical queries without forwarding them again. This flag disables identical queries without forwarding them again.
negative caching.
.TP .TP
.B \-0, --dns-forward-max=<queries> .B \-0, --dns-forward-max=<queries>
Set the maximum number of concurrent DNS queries. The default value is Set the maximum number of concurrent DNS queries. The default value is
@@ -441,9 +442,12 @@ instance
This is This is
useful when there is another DHCP server on the network which should useful when there is another DHCP server on the network which should
be used by some machines. The net:<network-id> sets the network-id tag be used by some machines. The net:<network-id> sets the network-id tag
whenever this dhcp-host directive is in use. whenever this dhcp-host directive is in use.This can be used to
This can be used to selectively send DHCP options just selectively send DHCP options just for this host. When a host matches any
for this host. dhcp-host directive (or one implied by /etc/ethers) then the special
network-id tag "known" is set. This allows dnsmasq to be configured to
ignore requests from unknown machines using
.B --dhcp-ignore=#known
Ethernet addresses (but not client-ids) may have Ethernet addresses (but not client-ids) may have
wildcard bytes, so for example wildcard bytes, so for example
.B --dhcp-host=00:20:e0:3b:13:*,ignore .B --dhcp-host=00:20:e0:3b:13:*,ignore
@@ -456,6 +460,13 @@ ARP type by preceding them with the ARP-type (in HEX) and "-". so
will only match a will only match a
Token-Ring hardware address, since the ARP-address type for token ring Token-Ring hardware address, since the ARP-address type for token ring
is 6. is 6.
.TP
.B --dhcp-hostsfile=<file>
Read DHCP host information from the specified file. The file contains
information about one host per line. The format of a line is the same
as text to the right of '=' in --dhcp-host. The advantage of storing DHCP host information
in this file is that it can be changed without re-starting dnsmasq:
the file will be re-read when dnsmasq receives SIGHUP.
.TP .TP
.B \-Z, --read-ethers .B \-Z, --read-ethers
Read /etc/ethers for information about hosts for the DHCP server. The Read /etc/ethers for information about hosts for the DHCP server. The
@@ -463,7 +474,8 @@ format of /etc/ethers is a hardware address, followed by either a
hostname or dotted-quad IP address. When read by dnsmasq these lines hostname or dotted-quad IP address. When read by dnsmasq these lines
have exactly the same effect as have exactly the same effect as
.B --dhcp-host .B --dhcp-host
options containing the same information. options containing the same information. /etc/ethers is re-read when
dnsmasq receives SIGHUP.
.TP .TP
.B \-O, --dhcp-option=[<network-id>,[<network-id>,]][vendor:[<vendor-class>],][<opt>|option:<opt-name>],[<value>[,<value>]] .B \-O, --dhcp-option=[<network-id>,[<network-id>,]][vendor:[<vendor-class>],][<opt>|option:<opt-name>],[<value>[,<value>]]
Specify different or extra options to DHCP clients. By default, Specify different or extra options to DHCP clients. By default,
@@ -577,7 +589,7 @@ When all the given network-ids match the set of network-ids derived
from the net, host, vendor and user classes, ignore the host and do from the net, host, vendor and user classes, ignore the host and do
not allocate it a DHCP lease. not allocate it a DHCP lease.
.TP .TP
.B --dhcp-ignore-name[=<network-id>[,<network-id>]] .B --dhcp-ignore-names[=<network-id>[,<network-id>]]
When all the given network-ids match the set of network-ids derived When all the given network-ids match the set of network-ids derived
from the net, host, vendor and user classes, ignore any hostname from the net, host, vendor and user classes, ignore any hostname
provided by the host. Note that, unlike dhcp-ignore, it is permissable provided by the host. Note that, unlike dhcp-ignore, it is permissable
@@ -660,7 +672,9 @@ since these data are not held in dnsmasq's lease
database. If dnsmasq was compiled with HAVE_BROKEN_RTC, then database. If dnsmasq was compiled with HAVE_BROKEN_RTC, then
the length of the lease (in seconds) is stored in the length of the lease (in seconds) is stored in
DNSMASQ_LEASE_LENGTH, otherwise the time of lease expiry is stored in DNSMASQ_LEASE_LENGTH, otherwise the time of lease expiry is stored in
DNSMASQ_LEASE_EXPIRES. If a lease used to have a hostname, which is DNSMASQ_LEASE_EXPIRES. The number of seconds until lease expiry is
always stored in DNSMASQ_TIME_REMAINING.
If a lease used to have a hostname, which is
removed, an "old" event is generated with the new state of the lease, removed, an "old" event is generated with the new state of the lease,
ie no name, and the former name is provided in the environment ie no name, and the former name is provided in the environment
variable DNSMASQ_OLD_HOSTNAME. variable DNSMASQ_OLD_HOSTNAME.
@@ -672,7 +686,9 @@ changes occur, the script is not invoked again until any existing
invocation exits. At dnsmasq startup, the script will be invoked for invocation exits. At dnsmasq startup, the script will be invoked for
all existing leases as they are read from the lease file. Expired all existing leases as they are read from the lease file. Expired
leases will be called with "del" and others with "old". <path> leases will be called with "del" and others with "old". <path>
must be an absolute pathname, no PATH search occurs. must be an absolute pathname, no PATH search occurs. When dnsmasq
receives a HUP signal, the script will be invoked for existing leases
with an "old " event.
.TP .TP
.B \-9, --leasefile-ro .B \-9, --leasefile-ro
Completely suppress use of the lease database file. The file will not Completely suppress use of the lease database file. The file will not
@@ -726,8 +742,15 @@ rejected, to stop clients getting outside the specified root.
Absolute paths (starting with /) are allowed, but they must be within Absolute paths (starting with /) are allowed, but they must be within
the tftp-root. the tftp-root.
.TP .TP
.B --tftp-unique-root
Add the IP address of the TFTP client as a path component on the end
of the TFTP-root (in standard dotted-quad format). Only valid if a
tftp-root is set and the directory exists. For instance, if tftp-root is "/tftp" and client
1.2.3.4 requests file "myfile" then the effective path will be
"/tftp/1.2.3.4/myfile" if /tftp/1.2.3.4 exists or /tftp/myfile otherwise.
.TP
.B --tftp-secure .B --tftp-secure
Enable TFTP secure mode: without this, any file which is readble by Enable TFTP secure mode: without this, any file which is readable by
the dnsmasq process under normal unix access-control rules is the dnsmasq process under normal unix access-control rules is
available via TFTP. When the --tftp-secure flag is given, only files available via TFTP. When the --tftp-secure flag is given, only files
owned by the user running the dnsmasq process are accessible. If owned by the user running the dnsmasq process are accessible. If
@@ -783,8 +806,12 @@ corresponding to tab, bell, backspace, return and newline.
When it receives a SIGHUP, When it receives a SIGHUP,
.B dnsmasq .B dnsmasq
clears its cache and then re-loads clears its cache and then re-loads
.I /etc/hosts and /etc/ethers. .I /etc/hosts
If and
.I /etc/ethers
and any file given by --dhcp-hostsfile.
The dhcp lease change script is called for all
existing DHCP leases. If
.B .B
--no-poll --no-poll
is set SIGHUP also re-reads is set SIGHUP also re-reads
@@ -799,7 +826,29 @@ the number of names which have had to removed from the cache before
they expired in order to make room for new names and the total number they expired in order to make room for new names and the total number
of names that have been inserted into the cache. In of names that have been inserted into the cache. In
.B --no-daemon .B --no-daemon
mode or when full logging is enabled (-q), a complete dump of the contents of the cache is made. mode or when full logging is enabled (-q), a complete dump of the
contents of the cache is made.
.PP
When it receives SIGUSR2 and it is logging direct to a file (see
.B --log-facility
)
.B dnsmasq
will close and reopen the log file. Note that during this operation,
dnsmasq will not be running as root. When it first creates the logfile
dnsmasq changes the ownership of the file to the non-root user it will run
as. Logrotate should be configured to create a new log file with
the ownership which matches the exising one before sending SIGUSR2.
If TCP DNS queries are in progress, the old logfile will remain open in
child processes which are handling TCP queries and may continue to be
written. There is a limit of 150 seconds, after which all existing TCP
processes will have expired: for this reason, it is not wise to
configure logfile compression for logfiles which have just been
rotated. Using logrotate, the required options are
.B create
and
.B delaycompress.
.PP .PP
Dnsmasq is a DNS query forwarder: it it not capable of recursively Dnsmasq is a DNS query forwarder: it it not capable of recursively
answering arbitrary queries starting from the root servers but answering arbitrary queries starting from the root servers but
@@ -903,6 +952,27 @@ parameter in a BOOTP request is matched against netids in
configurations, as is the tag "bootp", allowing some control over the options returned to configurations, as is the tag "bootp", allowing some control over the options returned to
different classes of hosts. different classes of hosts.
.SH EXIT CODES
.PP
0 - Dnsmasq successfully forked into the background, or terminated
normally if backgrounding is not enabled.
.PP
1 - A problem with configuration was detected.
.PP
2 - A problem with network access occurred (address in use, attempt
to use privileged ports without permission).
.PP
3 - A problem occured with a filesystem operation (missing
file/directory, permissions).
.PP
4 - Memory allocation failure.
.PP
5 - Other miscellaneous problem.
.PP
11 or greater - a non zero return code was received from the
lease-script process "init" call. The exit code from dnsmasq is the
script's exit code with 10 added.
.SH LIMITS .SH LIMITS
The default values for resource limits in dnsmasq are generally The default values for resource limits in dnsmasq are generally
conservative, and appropriate for embedded router type devices with conservative, and appropriate for embedded router type devices with

View File

@@ -4,7 +4,7 @@ dnsmasq \- Un ligero servidor DHCP y DNS con cach
.SH SINOPSIS .SH SINOPSIS
.B dnsmasq .B dnsmasq
.I [OPCION]... .I [OPCION]...
.SH "DESCRIPCION" .SH "DESCRIPCIÓN"
.BR dnsmasq .BR dnsmasq
es un ligero servidor DNS, TFTP y DHCP. Su propósito es proveer servicios DNS es un ligero servidor DNS, TFTP y DHCP. Su propósito es proveer servicios DNS
y DHCP a una red de área local. y DHCP a una red de área local.
@@ -16,7 +16,7 @@ hosts locales los cuales no aparecen en el DNS mundial puedan ser
resueltos. También responde a búsquedas DNS para hosts configurados resueltos. También responde a búsquedas DNS para hosts configurados
vía DHCP. vía DHCP.
.PP .PP
El servidor DHCP dnsmasq incluye soporte para assignación de direcciones El servidor DHCP dnsmasq incluye soporte para asignación de direcciones
estáticas, redes múltiples, DHCP-relay y especificadores de subredes estáticas, redes múltiples, DHCP-relay y especificadores de subredes
RFC3011. Automáticamente envía un predeterminado sensible de opciones RFC3011. Automáticamente envía un predeterminado sensible de opciones
DHCP, y puede ser configurado para enviar cualquier opciones DHCP deseadas, DHCP, y puede ser configurado para enviar cualquier opciones DHCP deseadas,
@@ -24,7 +24,8 @@ incluyendo opciones encapsuladas por vendedores. Incluye un servidor seguro
TFTP solo-lectura para permitir el inicio vía red/PXE de hosts DHCP. Tambíen TFTP solo-lectura para permitir el inicio vía red/PXE de hosts DHCP. Tambíen
incluye soporte para BOOTP. incluye soporte para BOOTP.
.PP .PP
Dnsmasq incluye soporte IPv6 para DNS, pero no para DHCP. Dnsmasq
incluye soporte IPv6 para DNS, pero no para DHCP.
.SH OPCIONES .SH OPCIONES
Nótese que en general parámetros ausentes son permitidos y deshabilitan Nótese que en general parámetros ausentes son permitidos y deshabilitan
funciones, por ejemplo "--pid-file=" deshabilita la escritura de un funciones, por ejemplo "--pid-file=" deshabilita la escritura de un
@@ -77,6 +78,9 @@ en operaci
archivo, en vez de syslog. (Errores durante la lectura de la configuración archivo, en vez de syslog. (Errores durante la lectura de la configuración
irán a syslog todavía, pero todo output desde un inicio exitoso, y todo irán a syslog todavía, pero todo output desde un inicio exitoso, y todo
output mientras en ejecución, irá a este archivo exclusivamente.) output mientras en ejecución, irá a este archivo exclusivamente.)
Al bitacorear a un archivo, dnsmasq cerrará y reabrirá el archivo al
recibir un SIGUSR2. Esto permite que el archivo de bitácora sea rotado
sin detener a dnsmasq.
.TP .TP
.B --log-async[=<líneas>] .B --log-async[=<líneas>]
Habilitar bitacoréo asincrónico y opcionalmente fijar el límite de número Habilitar bitacoréo asincrónico y opcionalmente fijar el límite de número
@@ -86,7 +90,7 @@ funcionando sin ser bloqueado por syslog, y permite a syslog usar dnsmasq
para búsquedas DNS sin riesgo de tranque. Si la coleta de líneas de bitácora para búsquedas DNS sin riesgo de tranque. Si la coleta de líneas de bitácora
se llena, dnsmasq bitacoreará el desbordamiento, y el número de mensajes se llena, dnsmasq bitacoreará el desbordamiento, y el número de mensajes
perdidos. El tamaño predeterminado de coleta es 5, un valor sano sería 5-25, perdidos. El tamaño predeterminado de coleta es 5, un valor sano sería 5-25,
y el valor máximo de 100 es impuesto. y un límite de 100 es impuesto.
.TP .TP
.B \-x, --pid-file=<path> .B \-x, --pid-file=<path>
Especificar un path alterno donde dnsmasq debe guardar su PID. Especificar un path alterno donde dnsmasq debe guardar su PID.
@@ -135,7 +139,7 @@ es usada. Si ninguna opci
o o
.B \--listen-address .B \--listen-address
es brindada, dnsmasq escucha en todas las interfaces disponibles excepto es brindada, dnsmasq escucha en todas las interfaces disponibles excepto
cualquiera fijada con la opción cualquiera fijada con opciones
.B \--except-interface .B \--except-interface
Interfaces IP alias (por ejemplo, "eth1:0") no pueden ser utilizadas con Interfaces IP alias (por ejemplo, "eth1:0") no pueden ser utilizadas con
.B --interface .B --interface
@@ -150,9 +154,9 @@ las opciones
.B --interface .B --interface
y y
.B --except-interface .B --except-interface
no importa y la opción no importa y las opciones
.B --except-interface .B --except-interface
siempre invalida las otras. siempre invalidan a las otras.
.TP .TP
.B \-2, --no-dhcp-interface=<nombre de interface> .B \-2, --no-dhcp-interface=<nombre de interface>
No proveer DHCP ni TFTP en la interface especificada, pero sí No proveer DHCP ni TFTP en la interface especificada, pero sí
@@ -168,7 +172,7 @@ direcciones IP y interfaces es usada. N
.B \--interface .B \--interface
es brindada, pero sí se brinda la opción es brindada, pero sí se brinda la opción
.B \--listen-address .B \--listen-address
entonces dnsmasq no escuchará automáticamente en la interface , entonces dnsmasq no escuchará automáticamente en la interface
loopback. Para obtener esto, su dirección IP, 127.0.0.1, debe ser loopback. Para obtener esto, su dirección IP, 127.0.0.1, debe ser
explícitamente brindada como una opción explícitamente brindada como una opción
.B \--listen-address .B \--listen-address
@@ -213,7 +217,7 @@ y 1.2.3.67 a 6.7.8.67. Esto es lo que
ruteadores Cisco PIX llaman "DNS doctoring". ruteadores Cisco PIX llaman "DNS doctoring".
.TP .TP
.B \-B, --bogus-nxdomain=<dirección IP> .B \-B, --bogus-nxdomain=<dirección IP>
Transformar respuestas que contienen la dirección IP brindada en Transformar respuestas que contienen la dirección IP brindada a
respuestas tipo "Dominio no existe". La intención de esto es actuar respuestas tipo "Dominio no existe". La intención de esto es actuar
en contra de una movida desviada hecha por Verisign en septiembre en contra de una movida desviada hecha por Verisign en septiembre
del 2003, cuando comenzaron a retornar la dirección de un servidor del 2003, cuando comenzaron a retornar la dirección de un servidor
@@ -255,7 +259,7 @@ opci
.B \-o, --strict-order .B \-o, --strict-order
Por predeterminado, dnsmasq enviará búsquedas a cualquiera de los Por predeterminado, dnsmasq enviará búsquedas a cualquiera de los
servidores upstream que conoce, y trata de favorecer servidores los servidores upstream que conoce, y trata de favorecer servidores los
cuales sabe que están activos. Fijar esta opcion forza a dnsmasq a cuales sabe que están activos. Fijar esta opción forza a dnsmasq a
probar cada búsqueda con cada servidor estrictamente en el orden probar cada búsqueda con cada servidor estrictamente en el orden
que aparecen en /etc/resolv.conf que aparecen en /etc/resolv.conf
.TP .TP
@@ -306,13 +310,13 @@ para hacer los archivos de configuraci
La segunda dirección IP opcional después del carácter @ le dice La segunda dirección IP opcional después del carácter @ le dice
a dnsmasq como fijar la dirección de remitente de las búsquedas a dnsmasq como fijar la dirección de remitente de las búsquedas
hacia este servidor DNS. Debe ser una dirección perteneciente a hacia este servidor DNS. Debe ser una dirección perteneciente a
la máquina en la cual corre dnsmasq, o de forma contraria esta la máquina en la cual corre dnsmasq, de forma contraria esta
línea de servidor será bitacoreada y después ignorada. La opción línea de servidor será bitacoreada y después ignorada. La opción
query-port es ignorada para cualquier servidores que tengan una query-port es ignorada para cualquier servidores que tengan una
dirección remitente especificada, pero el puerto puede ser dirección remitente especificada, pero el puerto puede ser
especificado directamente como parte de la dirección remitente. especificado directamente como parte de la dirección remitente.
.TP .TP
.B \-A, --address=/<domain>/[domain/]<ipaddr> .B \-A, --address=/<dominio>/[dominio/]<dirección IP>
Especificar una dirección IP para retornar por cualquier host en Especificar una dirección IP para retornar por cualquier host en
los dominios brindados. Búsquedas en estos dominios nunca son los dominios brindados. Búsquedas en estos dominios nunca son
reenviadas, y siempre son respondidas con la dirección IP reenviadas, y siempre son respondidas con la dirección IP
@@ -354,7 +358,7 @@ la m
Máquinas locales son aquellas en /etc/hosts o con arriendos DHCP. Máquinas locales son aquellas en /etc/hosts o con arriendos DHCP.
.TP .TP
.B \-W, --srv-host=<_servicio>.<_prot>.[<dominio>],[<target>[,<puerto>[,<prioridad>[,<peso>]]]] .B \-W, --srv-host=<_servicio>.<_prot>.[<dominio>],[<target>[,<puerto>[,<prioridad>[,<peso>]]]]
Retornar un record SRV DNS. Ver RFC2782 para detalles. Si no es Retornar un record DNS SRV. Ver RFC2782 para detalles. Si no es
brindada, el dominio se predetermina a el brindado por brindada, el dominio se predetermina a el brindado por
.B --domain. .B --domain.
El predeterminado para el dominio target está vacío, el predeterminado El predeterminado para el dominio target está vacío, el predeterminado
@@ -372,6 +376,18 @@ comas.
.B --ptr-record=<nombre>[,<target>] .B --ptr-record=<nombre>[,<target>]
Retornar un récord DNS PTR. Retornar un récord DNS PTR.
.TP .TP
.B --interface-name=<nombre>,<interface>
Retornar un récord DNS, asociando el nombre con la dirección primaria
en la interface brindada. Esta opción especifica un expediente tipo A
para el nombre brindado de la misma forma que una línea de /etc/hosts,
excepto que la dirección no es constante y es en vez tomada de la
interface brindada. Si la interface está deshabilitada, nó configurada,
o nó existente, un récord vacío es devuelto. El récord PTR relevante
tambien es creado, trazando la dirección de la interface a el nombre.
Más de un nombre puede ser asociado con una dirección de interface,
repitiendo la opción. En tal caso, la primera instancia es usada para
la traza reversa dirección-a-nombre.
.TP
.B \-c, --cache-size=<tamaño de caché> .B \-c, --cache-size=<tamaño de caché>
Fijar el tamaño del caché de dnsmasq. El predeterminado es 150 nombres. Fijar el tamaño del caché de dnsmasq. El predeterminado es 150 nombres.
Fijar el tamaño a cero deshabilita el caché. Fijar el tamaño a cero deshabilita el caché.
@@ -380,7 +396,6 @@ Fijar el tama
Deshabilitar caché negativo. El caché negativo le permite a dnsmasq Deshabilitar caché negativo. El caché negativo le permite a dnsmasq
recordar resultados tipo "dominio no existe" desde servidores DNS recordar resultados tipo "dominio no existe" desde servidores DNS
upstream y responder búsquedas idénticas sin reenviarlas nuevamente. upstream y responder búsquedas idénticas sin reenviarlas nuevamente.
Esta opción deshabilita el caché negativo.
.TP .TP
.B \-0, --dns-forward-max=<búsquedas> .B \-0, --dns-forward-max=<búsquedas>
Fijar el número máximo de búsquedas DNS simultáneas. El valor Fijar el número máximo de búsquedas DNS simultáneas. El valor
@@ -391,14 +406,14 @@ generar un n
.TP .TP
.B \-F, --dhcp-range=[[net:]network-id,]<dirección-inicio>,<dirección-final>[[,<máscara>],<broadcast>][,<tiempo de arriendo predeterminado>] .B \-F, --dhcp-range=[[net:]network-id,]<dirección-inicio>,<dirección-final>[[,<máscara>],<broadcast>][,<tiempo de arriendo predeterminado>]
Habilitar el servidor DHCP. Direcciones serán distribuidas desde el Habilitar el servidor DHCP. Direcciones serán distribuidas desde el
rango <start-addr> hasta <end-addr> y desde direcciones definidas rango <dirección-inicio> hasta <dirección-final> y desde direcciones definidas
estáticamente en opciones estáticamente en opciones
.B dhcp-host .B dhcp-host
Si el tiempo de arriendo es brindado, entonces arriendos serán Si el tiempo de arriendo es especificado, entonces arriendos serán
dados por esa cantidad de tiempo. El tiempo de arriendo es en otorgados por esa cantidad de tiempo. El tiempo de arriendo es en
segundos, o minutos (por ejemplo, 45m), o horas (por ejemplo, 1h), o el segundos, o minutos (por ejemplo, 45m), o horas (por ejemplo, 1h), o el
literal "infinite". Esta opción puede ser repetida, con diferentes literal "infinite". Esta opción puede ser repetida, con diferentes
direcciones para habilitar servicio DHCP en más de una red. Para direcciones, para habilitar servicio DHCP en más de una red. Para
redes conectadas diréctamente (en otras palabras, redes en las redes conectadas diréctamente (en otras palabras, redes en las
cuales la máquina corriendo dnsmasq tiene una interface) la cuales la máquina corriendo dnsmasq tiene una interface) la
máscara de subred es opcional. Pero, es requerida para redes que máscara de subred es opcional. Pero, es requerida para redes que
@@ -430,7 +445,7 @@ alocada el mismo nombre de host, direcci
Un nombre de host especificado de esta manera toma presedencia Un nombre de host especificado de esta manera toma presedencia
sobre cualquiera suministrado por el cliente DHCP en la máquina. sobre cualquiera suministrado por el cliente DHCP en la máquina.
También se permite omitir la direccion de hardware y incluir el También se permite omitir la direccion de hardware y incluir el
nombre host; en tal caso la dirección IP y los tiempos de arriendo nombre de host; en tal caso la dirección IP y los tiempos de arriendo
serán aplicables a cualquier máquina que reclame ese nombre. serán aplicables a cualquier máquina que reclame ese nombre.
Por ejemplo: Por ejemplo:
.B --dhcp-host=00:20:e0:3b:13:af,wap,infinite .B --dhcp-host=00:20:e0:3b:13:af,wap,infinite
@@ -462,7 +477,11 @@ Esto es
usado por algúnas máquinas. El net:<network-id> fija la etiqueta usado por algúnas máquinas. El net:<network-id> fija la etiqueta
network-id cuando sea que esta directiva dhcp-host está en uso. network-id cuando sea que esta directiva dhcp-host está en uso.
Esto puede ser usado para enviar selectivamente opciones DHCP Esto puede ser usado para enviar selectivamente opciones DHCP
a este host. a este host. Cuando un host coincide con cualquier directiva
dhcp-host (o una implicada por /etc/ethers) entonces la etiqueta
network-id especial "known" es fijada. Esto permite que dnsmasq sea
configurado para ignorar pedidos desde máquinas desconocidas usando
.B --dhcp-ignore=#known
Direcciones ethernet (pero no client-ids) pueden tener bytes Direcciones ethernet (pero no client-ids) pueden tener bytes
comodínes, así que por ejemplo comodínes, así que por ejemplo
.B --dhcp-host=00:20:e0:3b:13:*,ignore .B --dhcp-host=00:20:e0:3b:13:*,ignore
@@ -473,16 +492,19 @@ Direcciones de hardware normalmente coinciden con cualquier
tipo de red (ARP), pero es posible restringirlas a un tipo ARP tipo de red (ARP), pero es posible restringirlas a un tipo ARP
singular precediendolo con el tipo ARP (en HEX) y "-". Así que singular precediendolo con el tipo ARP (en HEX) y "-". Así que
.B --dhcp-host=06-00:20:e0:3b:13:af,1.2.3.4 .B --dhcp-host=06-00:20:e0:3b:13:af,1.2.3.4
solo coincidaría una dirección de hardware Token-Ring, dado que solo coincidiría con una dirección de hardware Token-Ring, dado que
el tipo ARP para Token-Ring es 6. el tipo ARP para Token-Ring es 6.
.TP .TP
.B --dhcp-hostsfile=<archivo>
Leer información host DHCP desde el archivo especificado. El archivo contiene información de un host por línea. El formato de una línea es igual que texto hacia la derecha de '=' en --dhcp-host. La ventaja de almacenar información host DHCP en este archivo es que puede ser cambiada sin tener que reiniciar dnsmasq. El archivo será re-leído cuando dnsmasq recibe un SIGHUP.
.TP
.B \-Z, --read-ethers .B \-Z, --read-ethers
Leer /etc/ethers en busca de información sobre hosts para el servidor Leer /etc/ethers en busca de información sobre hosts para el servidor
DHCP. El formato de /etc/ethers es una dirección de hardware, seguida DHCP. El formato de /etc/ethers es una dirección de hardware, seguida
por ya sea un nombre de host o una dirección IP. Al ser leidas por por ya sea un nombre de host o una dirección IP. Al ser leidas por
dnsmasq, estas líneas tienen exáctamente el mismo efecto que opciones dnsmasq, estas líneas tienen exáctamente el mismo efecto que opciones
.B --dhcp-host .B --dhcp-host
que contienen la misma información. que contienen la misma información. /etc/ethers es re-leída cuando dnsmasq recibe un SIGHUP.
.TP .TP
.B \-O, --dhcp-option=[<network-id>,[<network-id>,]][vendor:[<vendor-class>],][<opt>|option:<opt-name>],[<value>[,<value>]] .B \-O, --dhcp-option=[<network-id>,[<network-id>,]][vendor:[<vendor-class>],][<opt>|option:<opt-name>],[<value>[,<value>]]
Especificar opciones diferentes o extra a clientes DHCP. Por Especificar opciones diferentes o extra a clientes DHCP. Por
@@ -496,21 +518,21 @@ otras opciones. La opci
número decimal o como "option:<option-name>". Los números de opción número decimal o como "option:<option-name>". Los números de opción
están especificados en RFC2132 y RFCs subsiguientes. El juego de están especificados en RFC2132 y RFCs subsiguientes. El juego de
option-names conocido por dnsmasq puede ser descubierto ejecutando option-names conocido por dnsmasq puede ser descubierto ejecutando
"dnsmasq --help dhcp". Por ejemplo, para fijar a ruta predeterminada a "dnsmasq --help dhcp". Por ejemplo, para fijar la ruta predeterminada a
192.168.4.4, hágase un 192.168.4.4, hágase un
.B --dhcp-option=3,192.168.4.4 .B --dhcp-option=3,192.168.4.4
o o
.B --dhcp-option = option:router, 192.168.4.4 .B --dhcp-option=option:router, 192.168.4.4
y para fijar la dirección de servidor de tiempo a 192.168.0.4, y para fijar la dirección de servidor de tiempo a 192.168.0.4,
hágase un hágase un
.B --dhcp-option=42,192.168.0.4 .B --dhcp-option=42,192.168.0.4
o o
.B --dhcp-option = option:ntp-server, 192.168.0.4 .B --dhcp-option=option:ntp-server, 192.168.0.4
La dirección especial 0.0.0.0 es entendida que significa "la La dirección especial 0.0.0.0 es entendida que significa "la
dirección de la máquina que corre dnsmasq". Tipos de data permitidos dirección de la máquina que corre dnsmasq". Tipos de data permitidos
son direcciones IP de cuatro segmentos, un número decimal, dígitos hex son direcciones IP de cuatro segmentos, un número decimal, dígitos hex
separados por colones, y un string de texto. Si las network-ids separados por colones, y un string de texto. Si las network-ids
opcionales son brindadas, entonces esta opcion es solo enviada cuando opcionales son brindadas, entonces esta opción es solo enviada cuando
todas las network-ids coinciden. todas las network-ids coinciden.
Procesamiento especial es llevado a cabo en un argumento de texto para Procesamiento especial es llevado a cabo en un argumento de texto para
@@ -554,7 +576,7 @@ encapsuladas.
.B --dhcp-option-force=[<network-id>,[<network-id>,]][vendor:[<vendor-class>],]<opt>,[<value>[,<value>]] .B --dhcp-option-force=[<network-id>,[<network-id>,]][vendor:[<vendor-class>],]<opt>,[<value>[,<value>]]
Esto funciona exáctamente de la misma forma que Esto funciona exáctamente de la misma forma que
.B --dhcp-option .B --dhcp-option
excepto que la opción siempre será enviada, aún si el cliente no lo pide en excepto que la opción siempre será enviada, aún si el cliente no la pide en
la lista de pedido de parámetros. Esto se necesita aveces, por ejemplo cuando la lista de pedido de parámetros. Esto se necesita aveces, por ejemplo cuando
enviando opciones a PXELinux. enviando opciones a PXELinux.
.TP .TP
@@ -580,7 +602,7 @@ por ejemplo, usar esto para especificar una impresora diferente para
hosts en la clase "cuentas" que para los de la clase "ingenieria". hosts en la clase "cuentas" que para los de la clase "ingenieria".
.TP .TP
.B \-4, --dhcp-mac=<network-id>,<dirección MAC> .B \-4, --dhcp-mac=<network-id>,<dirección MAC>
Mapear desde una dirección MAC a una network id. La dirección MAC Trazar desde una dirección MAC a una network id. La dirección MAC
puede incluir comodínes. Por ejemplo: puede incluir comodínes. Por ejemplo:
.B --dhcp-mac=3com,01:34:23:*:*:* .B --dhcp-mac=3com,01:34:23:*:*:*
fijaría el tag "3com" a cualquier host el cual su MAC coincida con fijaría el tag "3com" a cualquier host el cual su MAC coincida con
@@ -602,7 +624,7 @@ Cuando todos los network ids brindados coincidan con el juego de
network ids derivados de las clases net, host, y vendor, ignorar network ids derivados de las clases net, host, y vendor, ignorar
el host y no brindarle un arriendo DHCP. el host y no brindarle un arriendo DHCP.
.TP .TP
.B --dhcp-ignore-name[=<network-id>[,<network-id>]] .B --dhcp-ignore-names[=<network-id>[,<network-id>]]
Cuando todos los network-ids brindados coinciden con el juego de Cuando todos los network-ids brindados coinciden con el juego de
network-ids derivado de la red, host, classes de vendedor y usuario, network-ids derivado de la red, host, classes de vendedor y usuario,
ignorar cualquier nombre de host proveido por el host. Nótese que, ignorar cualquier nombre de host proveido por el host. Nótese que,
@@ -653,7 +675,7 @@ cuidado.
.TP .TP
.B --log-dhcp .B --log-dhcp
Bitacoréo extra para DHCP: Bitacorear todas las opciones enviadas a Bitacoréo extra para DHCP: Bitacorear todas las opciones enviadas a
clientes DHCP y los tags netid usados para determinarlos. clientes DHCP y las etiquetas netid usadas para determinarlos.
.TP .TP
.B \-l, --dhcp-leasefile=<path> .B \-l, --dhcp-leasefile=<path>
Usar el archivo especificado para almacenar información de arriendos Usar el archivo especificado para almacenar información de arriendos
@@ -665,7 +687,7 @@ DNS si tienen un nombre de host. Esta funcionalidad pudo haber sido
excluida de dnsmasq a la hora de compilación, y en tal caso ocurrirá excluida de dnsmasq a la hora de compilación, y en tal caso ocurrirá
un error. Nótese que la integración de archivos de un error. Nótese que la integración de archivos de
arriendo ISC es una caracterísctica depreciada. No debería ser usada arriendo ISC es una caracterísctica depreciada. No debería ser usada
en instalaciones nuevas, y será eliminada en versiones futuras. en instalaciones nuevas, y será eliminada en una versión futura.
.TP .TP
.B \-6 --dhcp-script=<path> .B \-6 --dhcp-script=<path>
Cuando un arriendo DHCP nuevo es creado, o uno viejo es Cuando un arriendo DHCP nuevo es creado, o uno viejo es
@@ -689,26 +711,30 @@ datos no son almacenados en la base de datos de arriendos de dnsmasq.
Si dnsmasq fue compilado con HAVE_BROKEN_RTC, entonces la duración del Si dnsmasq fue compilado con HAVE_BROKEN_RTC, entonces la duración del
arriendo (en segundos) es almacenada en DNSMASQ_LEASE_LENGTH, de otra arriendo (en segundos) es almacenada en DNSMASQ_LEASE_LENGTH, de otra
manera el tiempo de vencimiento es almacenado en DNSMASQ_LEASE_EXPIRES. manera el tiempo de vencimiento es almacenado en DNSMASQ_LEASE_EXPIRES.
El número de segundos faltante para el vencimiento del arriendo siempre
es almacenado en DNSMASQ_TIME_REMAINING.
Si un arriendo solía tener un nombre de host, el cual es removido, un Si un arriendo solía tener un nombre de host, el cual es removido, un
evento "old" es generado con el nuevo estado del arriendo, (por ejemplo, sin evento "old" es generado con el nuevo estado del arriendo, (por ejemplo, sin
nombre), y el nombre anterior es brindado en la variable de ambiente nombre), y el nombre anterior es brindado en la variable de ambiente
DNSMASQ_OLD_HOSTNAME. Todos los descriptores de archivo están cerrados DNSMASQ_OLD_HOSTNAME.
Todos los descriptores de archivo están cerrados
excepto stdin, stdout, y stderr los cuales están abiertos a /dev/null excepto stdin, stdout, y stderr los cuales están abiertos a /dev/null
(excepto en modo debug). (excepto en modo debug).
Este guión no es invocado concurrentemente: si cambios de arriendos Este guión no es invocado concurrentemente: si cambios de arriendos
subsiguientes ocurren, el guión no es invocado otra vez hasta que subsiguientes ocurren, el guión no es invocado otra vez hasta que
cualquier invocación existente haga exit. Al inicio de dnsmasq, el guión cualquier invocación existente haga exit. Al inicio de dnsmasq, el guión
será invocado para todos los arriendos existenetes mientras van siendo será invocado para todos los arriendos existentes mientras van siendo
leídos desde el archivo de arriendos. Arriendos vencidos serán llamados leídos desde el archivo de arriendos. Arriendos vencidos serán llamados
con "del" y otros con "old". <path> debe ser un path absoluto, ninguna con "del" y otros con "old". <path> debe ser un path absoluto, ninguna
búsqueda PATH ocurre. búsqueda PATH ocurre. Cuando dnsmasq recibe una señal HUP, el guión será
invocado para arriendos existentes con un evento "old".
.TP .TP
.B \-9, --leasefile-ro .B \-9, --leasefile-ro
Suprimir completamente el uso del archivo de arriendos. El archivo no será Suprimir completamente el uso del archivo de arriendos. El archivo no será
creado, leído, o escrito. Cambiar la manera en la cuál el archivo guión de creado, leído, ni escrito. Cambiar la manera en la cuál el archivo guión de
cambio de arriendo es llamado, de tal forma que la base de datos de arriendos cambio de arriendo (si es brindado) es llamado, de tal forma que la base de
pueda ser mantenida en almacenaje externo por el archivo guión. Adicionálmente datos de arriendospueda ser mantenida en almacenaje externo por el archivo
a las invocaciones brindadas en guión. Adicionálmente a las invocaciones brindadas en
.B --dhcp-script .B --dhcp-script
el archivo de cambio de arriendos es llamado una vez, al inicio de dnsmasq, el archivo de cambio de arriendos es llamado una vez, al inicio de dnsmasq,
con el único argumento "init". Cuando invocado de esta forma, el guión debería con el único argumento "init". Cuando invocado de esta forma, el guión debería
@@ -760,6 +786,13 @@ rechazados, para prevenir que clientes salgan de la ra
absolutos (los que comienzan con "/") están permitidos, pero deben estar absolutos (los que comienzan con "/") están permitidos, pero deben estar
dentro del tftp-root. dentro del tftp-root.
.TP .TP
.B --tftp-unique-root
Agregar la dirección IP del cliente TFTP como un componente path del lado del
TFTP-root (en formato estándar de cuatro puntos). Solo válido si un tftp-root
está fijado y el directorio existe. Por ejemplo, si tftp-root es "/tftp" y el
cliente 1.2.3.4 pide el archivo "miarchivo" entonces el path efectivo será
"/tftp/1.2.3.4/miarchivo" si /tftp/1.2.3.4 existe o /tftp/miarchivo si no.
.TP
.B --tftp-secure .B --tftp-secure
Habilitar modo TFTP seguro: sin esto, cualquier archivo que es leíble por el Habilitar modo TFTP seguro: sin esto, cualquier archivo que es leíble por el
proceso dnsmasq bajo reglas normales de control de acceso UNIX, está disponible proceso dnsmasq bajo reglas normales de control de acceso UNIX, está disponible
@@ -818,8 +851,12 @@ siguientes escapes son permitidos: \\\\ \\" \\t \\a \\b \\r y \\n. El
Al recibir un SIGHUP Al recibir un SIGHUP
.B dnsmasq .B dnsmasq
libera su cache y entonces recarga libera su cache y entonces recarga
.I /etc/hosts y /etc/ethers. .I /etc/hosts
Si y
.I /etc/ethers
al igual que cualquier archivo brindado con --dhcp-hostsfile.
El archivo guión de cambio de arriendos es llamado para todos los arriendos
DHCP existentes. Si
.B .B
--no-poll --no-poll
está fijado entonces SIGHUP también re-lee está fijado entonces SIGHUP también re-lee
@@ -829,7 +866,7 @@ NO re-lee el archivo de configuraci
.PP .PP
Al recibir un SIGUSR1, Al recibir un SIGUSR1,
.B dnsmasq .B dnsmasq
escribe estadisticas de caché a la biácora del sistema. Escribe el tamaño escribe estadísticas de caché a la bitácora del sistema. Escribe el tamaño
del caché, el numero de nombres que han tenido que ser removidos del del caché, el numero de nombres que han tenido que ser removidos del
caché antes de que vencieran para hacer espacio para nombres nuevos, y el caché antes de que vencieran para hacer espacio para nombres nuevos, y el
número total de nombres que han sido insertados en el caché. En modo número total de nombres que han sido insertados en el caché. En modo
@@ -837,6 +874,26 @@ n
o cuando bitacoréo completo está habilitado (-q), una descarga completa de o cuando bitacoréo completo está habilitado (-q), una descarga completa de
el contenido del caché es hecha. el contenido del caché es hecha.
.PP .PP
Cuando recibe un SIGUSR2 y está bitacoreando diréctamente a un archivo (ver
.B --log-facility
)
.B dnsmasq
cerrará y reabrirá el archivo de bitácora. Nótese que durante esta
operación, dnsmasq no estará corriendo como root. Al crear el archivo de
bitácora, dnsmasq cambia el dueño del archivo a el usuario normal como
el que correrá. Logrotate debe ser configurado para crear un archivo de
bitácora nuevo con permisos iguales al existente, antes de enviar
SIGUSR2. Si búsquedas DNS TCP están en progreso, el archivo de bitácora
viejo se mantendrá abierto en procesos hijos que están manejando
búsquedas TCP, y puede continuarse a escribirle. Hay un límite de 150
segundos, después de lo cual todos los procesos TCP existentes se habrán
vencido: por esta razón, no es sabio configurar compresión de archivos
de bitácora para archivos que acaban de ser rotados. Con logrotate, las
opciones requeridas son
.B create
y
.B delaycompress.
.PP
Dnsmasq es un reenviador de búsquedas DNS: no puede responder búsquedas Dnsmasq es un reenviador de búsquedas DNS: no puede responder búsquedas
arbitrarias comenzando desde los servidores root pero reenvía dichas arbitrarias comenzando desde los servidores root pero reenvía dichas
búsquedas a un servidor DNS recursivo, el cual es típicamente proveído búsquedas a un servidor DNS recursivo, el cual es típicamente proveído
@@ -862,7 +919,7 @@ haya existido. Dnsmasq simplemente sigue revisando en caso de que
.I /etc/resolv.conf .I /etc/resolv.conf
sea creado en algún momento. A dnsmasq se le puede decir que revise más sea creado en algún momento. A dnsmasq se le puede decir que revise más
de un archivo resolv.conf. Esto es útil en una laptop, donde ambos PPP y de un archivo resolv.conf. Esto es útil en una laptop, donde ambos PPP y
DHCP podrían estar siendo usados: dnsmasq puede ser fijado para revisar ambos: DHCP podrían estar siendo usados: dnsmasq puede ser fijado para revisar ambos
.I /etc/ppp/resolv.conf .I /etc/ppp/resolv.conf
y y
.I /etc/dhcpc/resolv.conf .I /etc/dhcpc/resolv.conf
@@ -938,10 +995,31 @@ est
(Fijar --bootp-dynamic elimina la necesidad de trazados estáticos.) El (Fijar --bootp-dynamic elimina la necesidad de trazados estáticos.) El
parámetro de nombre de archivos en un pedido BOOTP es revisado para parámetro de nombre de archivos en un pedido BOOTP es revisado para
ver si coincide con algún network-id en configuraciónes ver si coincide con algún network-id en configuraciónes
.B dhcp-option .B dhcp-option
al igual que la etiqueta "bootp", permitiendo así algún control sobre al igual que la etiqueta "bootp", permitiendo así algún control sobre
las opciones devueltas a diferentes clases de hosts. las opciones devueltas a diferentes clases de hosts.
.SH CÓDIGOS EXIT
.PP
0 - Dnsmasq hizo fork hacia el fondo exitosamente, o terminó de manera
normal si ir al fondo no está habilitado.
.PP
1 - Un problema con la configuración ha sido detectado.
.PP
2 - Un problema con acceso a redes ocurrió (dirección en uso, intento
de usar puertos privilegiados sin permiso).
.PP
3 - Un problema con una operación de sistema de archivos ocurrió (archivo
o directorio ausente, permisos).
.PP
4 - Falla de alocación de memoria.
.PP
5 - Otro problema misceláneo.
.PP
11 o mayor - un codigo de retorno no cero fué recibido del llamado "init"
del proceso de archivo guión de arriendos. El código exit de dnsmasq es
el código exit del archivo guión con 10 sumado.
.SH LIMITES .SH LIMITES
Los valores predeterminados para limites de recursos son generálmente Los valores predeterminados para limites de recursos son generálmente
conservadores, y apropiados para uso en dispositivos tipo enrutador conservadores, y apropiados para uso en dispositivos tipo enrutador
@@ -952,19 +1030,20 @@ no escalaban tan bien.
.PP .PP
Dnsmasq es capaz de soportar con DNS y DHCP a por lo menos mil (1,000) Dnsmasq es capaz de soportar con DNS y DHCP a por lo menos mil (1,000)
clientes. Por supuesto que para lograr esto debe ser aumentadoo el valor de clientes. Por supuesto que para lograr esto debe aumentarse el valor de
.B --dhcp-lease-max .B --dhcp-lease-max
debe ser incrementado, y tiempos de arriendo no deben ser muy cortos , y tiempos de arriendo no deben ser muy cortos (menos de una hora).
(menos de una hora). El valor de El valor de
.B --dns-forward-max .B --dns-forward-max
puede ser aumentado: comienze con el equivalente a el número de clientes y puede ser aumentado: comienze con el equivalente a el número de clientes y
auméntelo si parece lento el DNS. Nótese que el rendimiento DNS depende auméntelo si parece lento el DNS. Nótese que el rendimiento DNS depende
también de los servidores DNS upstream. El tamaño del caché DNS puede ser también de los servidores DNS upstream. El tamaño del caché DNS puede ser
incrementado: el límite obligatorio es 10,000 nombres y el predeterminado incrementado: el límite obligatorio es 10,000 nombres y el predeterminado
(150) es muy bajo. Enviarle un SIGUSR1 a dnsmasq hace que bitacorée (150) es muy bajo. El enviarle un SIGUSR1 a dnsmasq hace que bitacorée
información que es útil para afinar el tamaño de caché. Ver la sección información que es útil para afinar el tamaño de caché. Ver la sección
.B NOTAS .B NOTAS
para detalles. para detalles.
.PP .PP
El servidor TFTP incorporado es capáz de soportar varias transferencias El servidor TFTP incorporado es capáz de soportar varias transferencias
simultáneas de archivos: el límite absoluto está relacionado con el número simultáneas de archivos: el límite absoluto está relacionado con el número
@@ -975,6 +1054,7 @@ demasiado alto con
será de-escalado y el límite real será bitacoreado al inicio. Nótese que más será de-escalado y el límite real será bitacoreado al inicio. Nótese que más
transferencias son posibles cuando el mismo archivo es enviado qué cuando transferencias son posibles cuando el mismo archivo es enviado qué cuando
cada transferencia envía un archivo diferente. cada transferencia envía un archivo diferente.
.PP .PP
Es posible usar dnsmasq para negar publicidad Web usando una lista de Es posible usar dnsmasq para negar publicidad Web usando una lista de
servidores de banners bien conocidos, todos resolviendose a 127.0.0.1 o servidores de banners bien conocidos, todos resolviendose a 127.0.0.1 o

604
po/de.po

File diff suppressed because it is too large Load Diff

713
po/es.po

File diff suppressed because it is too large Load Diff

653
po/fi.po

File diff suppressed because it is too large Load Diff

644
po/fr.po

File diff suppressed because it is too large Load Diff

635
po/id.po

File diff suppressed because it is too large Load Diff

653
po/it.po

File diff suppressed because it is too large Load Diff

621
po/no.po

File diff suppressed because it is too large Load Diff

624
po/pl.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

639
po/ro.po

File diff suppressed because it is too large Load Diff

View File

@@ -26,7 +26,7 @@ static struct iovec ifreq = {
.iov_len = 0 .iov_len = 0
}; };
void init_bpf(struct daemon *daemon) void init_bpf(void)
{ {
int i = 0; int i = 0;
@@ -37,19 +37,14 @@ void init_bpf(struct daemon *daemon)
{ {
sprintf(ifreq.iov_base, "/dev/bpf%d", i++); sprintf(ifreq.iov_base, "/dev/bpf%d", i++);
if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1) if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1)
{ return;
int flags = fcntl(daemon->dhcp_raw_fd, F_GETFD);
if (flags != -1)
fcntl(daemon->dhcp_raw_fd, F_SETFD, flags | FD_CLOEXEC);
return;
}
} }
if (errno != EBUSY) if (errno != EBUSY)
die(_("cannot create DHCP BPF socket: %s"), NULL); die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
} }
} }
void send_via_bpf(struct daemon *daemon, struct dhcp_packet *mess, size_t len, void send_via_bpf(struct dhcp_packet *mess, size_t len,
struct in_addr iface_addr, struct ifreq *ifr) struct in_addr iface_addr, struct ifreq *ifr)
{ {
/* Hairy stuff, packet either has to go to the /* Hairy stuff, packet either has to go to the
@@ -145,7 +140,7 @@ void send_via_bpf(struct daemon *daemon, struct dhcp_packet *mess, size_t len,
while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send()); while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
} }
int iface_enumerate(struct daemon *daemon, void *parm, int (*ipv4_callback)(), int (*ipv6_callback)()) int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
{ {
char *ptr; char *ptr;
struct ifreq *ifr; struct ifreq *ifr;
@@ -206,7 +201,7 @@ int iface_enumerate(struct daemon *daemon, void *parm, int (*ipv4_callback)(), i
netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr; netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1) if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1)
broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr; broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (!((*ipv4_callback)(daemon, addr, if (!((*ipv4_callback)(addr,
(int)if_nametoindex(ifr->ifr_name), (int)if_nametoindex(ifr->ifr_name),
netmask, broadcast, netmask, broadcast,
parm))) parm)))
@@ -222,7 +217,7 @@ int iface_enumerate(struct daemon *daemon, void *parm, int (*ipv4_callback)(), i
addr->s6_addr[2] = 0; addr->s6_addr[2] = 0;
addr->s6_addr[3] = 0; addr->s6_addr[3] = 0;
} }
if (!((*ipv6_callback)(daemon, addr, if (!((*ipv6_callback)(addr,
(int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id, (int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
(int)if_nametoindex(ifr->ifr_name), (int)if_nametoindex(ifr->ifr_name),
parm))) parm)))

View File

@@ -12,13 +12,13 @@
#include "dnsmasq.h" #include "dnsmasq.h"
static struct crec *cache_head, *cache_tail, **hash_table; static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL;
static struct crec *dhcp_spare, *new_chain; static struct crec *dhcp_spare = NULL, *new_chain = NULL;
static int cache_inserted, cache_live_freed, insert_error; static int cache_inserted = 0, cache_live_freed = 0, insert_error;
static union bigname *big_free; static union bigname *big_free = NULL;
static int bignames_left, log_queries, cache_size, hash_size; static int bignames_left, hash_size;
static int uid; static int uid = 0;
static char *addrbuff; static char *addrbuff = NULL;
/* type->string mapping: this is also used by the name-hash function as a mixing table. */ /* type->string mapping: this is also used by the name-hash function as a mixing table. */
static const struct { static const struct {
@@ -63,32 +63,21 @@ static char *record_source(struct hostsfile *add_hosts, int index);
static void rehash(int size); static void rehash(int size);
static void cache_hash(struct crec *crecp); static void cache_hash(struct crec *crecp);
void cache_init(int size, int logq) void cache_init(void)
{ {
struct crec *crecp; struct crec *crecp;
int i; int i;
if ((log_queries = logq)) if (daemon->options & OPT_LOG)
addrbuff = safe_malloc(ADDRSTRLEN); addrbuff = safe_malloc(ADDRSTRLEN);
else
addrbuff = NULL; bignames_left = daemon->cachesize/10;
cache_head = cache_tail = NULL; if (daemon->cachesize > 0)
dhcp_spare = NULL;
new_chain = NULL;
hash_table = NULL;
cache_size = size;
big_free = NULL;
bignames_left = size/10;
uid = 0;
cache_inserted = cache_live_freed = 0;
if (cache_size > 0)
{ {
crecp = safe_malloc(size*sizeof(struct crec)); crecp = safe_malloc(daemon->cachesize*sizeof(struct crec));
for (i=0; i<size; i++, crecp++) for (i=0; i < daemon->cachesize; i++, crecp++)
{ {
cache_link(crecp); cache_link(crecp);
crecp->flags = 0; crecp->flags = 0;
@@ -97,7 +86,7 @@ void cache_init(int size, int logq)
} }
/* create initial hash table*/ /* create initial hash table*/
rehash(cache_size); rehash(daemon->cachesize);
} }
/* In most cases, we create the hash table once here by calling this with (hash_table == NULL) /* In most cases, we create the hash table once here by calling this with (hash_table == NULL)
@@ -115,7 +104,7 @@ static void rehash(int size)
/* must succeed in getting first instance, failure later is non-fatal */ /* must succeed in getting first instance, failure later is non-fatal */
if (!hash_table) if (!hash_table)
new = safe_malloc(new_size * sizeof(struct crec *)); new = safe_malloc(new_size * sizeof(struct crec *));
else if (new_size <= hash_size || !(new = malloc(new_size * sizeof(struct crec *)))) else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *))))
return; return;
for(i = 0; i < new_size; i++) for(i = 0; i < new_size; i++)
@@ -238,17 +227,12 @@ char *cache_get_name(struct crec *crecp)
static int is_outdated_cname_pointer(struct crec *crecp) static int is_outdated_cname_pointer(struct crec *crecp)
{ {
struct crec *target = crecp->addr.cname.cache;
if (!(crecp->flags & F_CNAME)) if (!(crecp->flags & F_CNAME))
return 0; return 0;
if (!target) if (crecp->addr.cname.cache && crecp->addr.cname.uid == crecp->addr.cname.cache->uid)
return 1;
if (crecp->addr.cname.uid == target->uid)
return 0; return 0;
return 1; return 1;
} }
@@ -368,21 +352,12 @@ void cache_start_insert(void)
struct crec *cache_insert(char *name, struct all_addr *addr, struct crec *cache_insert(char *name, struct all_addr *addr,
time_t now, unsigned long ttl, unsigned short flags) time_t now, unsigned long ttl, unsigned short flags)
{ {
#ifdef HAVE_IPV6
int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;
#else
int addrlen = INADDRSZ;
#endif
struct crec *new; struct crec *new;
union bigname *big_name = NULL; union bigname *big_name = NULL;
int freed_all = flags & F_REVERSE; int freed_all = flags & F_REVERSE;
log_query(flags | F_UPSTREAM, name, addr, 0, NULL, 0); log_query(flags | F_UPSTREAM, name, addr, 0, NULL, 0);
/* name is needed as workspace by log_query in this case */
if ((flags & F_NEG) && (flags & F_REVERSE))
name = NULL;
/* CONFIG bit no needed except for logging */ /* CONFIG bit no needed except for logging */
flags &= ~F_CONFIG; flags &= ~F_CONFIG;
@@ -436,7 +411,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
big_free = big_free->next; big_free = big_free->next;
} }
else if (!bignames_left || else if (!bignames_left ||
!(big_name = (union bigname *)malloc(sizeof(union bigname)))) !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
{ {
insert_error = 1; insert_error = 1;
return NULL; return NULL;
@@ -457,12 +432,14 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
new->name.bname = big_name; new->name.bname = big_name;
new->flags |= F_BIGNAME; new->flags |= F_BIGNAME;
} }
if (name) if (name)
strcpy(cache_get_name(new), name); strcpy(cache_get_name(new), name);
else else
*cache_get_name(new) = 0; *cache_get_name(new) = 0;
if (addr) if (addr)
memcpy(&new->addr.addr, addr, addrlen); new->addr.addr = *addr;
else else
new->addr.cname.cache = NULL; new->addr.cname.cache = NULL;
@@ -754,8 +731,8 @@ static int read_hostsfile(char *filename, int opts, char *buff, char *domain_suf
{ {
/* If set, add a version of the name with a default domain appended */ /* If set, add a version of the name with a default domain appended */
if ((opts & OPT_EXPAND) && domain_suffix && !fqdn && if ((opts & OPT_EXPAND) && domain_suffix && !fqdn &&
(cache = malloc(sizeof(struct crec) + (cache = whine_malloc(sizeof(struct crec) +
strlen(token)+2+strlen(domain_suffix)-SMALLDNAME))) strlen(token)+2+strlen(domain_suffix)-SMALLDNAME)))
{ {
strcpy(cache->name.sname, token); strcpy(cache->name.sname, token);
strcat(cache->name.sname, "."); strcat(cache->name.sname, ".");
@@ -764,7 +741,7 @@ static int read_hostsfile(char *filename, int opts, char *buff, char *domain_suf
addr_dup = 1; addr_dup = 1;
name_count++; name_count++;
} }
if ((cache = malloc(sizeof(struct crec) + strlen(token)+1-SMALLDNAME))) if ((cache = whine_malloc(sizeof(struct crec) + strlen(token)+1-SMALLDNAME)))
{ {
strcpy(cache->name.sname, token); strcpy(cache->name.sname, token);
add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup); add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
@@ -787,7 +764,7 @@ static int read_hostsfile(char *filename, int opts, char *buff, char *domain_suf
void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *addn_hosts) void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *addn_hosts)
{ {
struct crec *cache, **up, *tmp; struct crec *cache, **up, *tmp;
int i, total_size = cache_size; int i, total_size = daemon->cachesize;
cache_inserted = cache_live_freed = 0; cache_inserted = cache_live_freed = 0;
@@ -816,7 +793,7 @@ void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *a
if ((opts & OPT_NO_HOSTS) && !addn_hosts) if ((opts & OPT_NO_HOSTS) && !addn_hosts)
{ {
if (cache_size > 0) if (daemon->cachesize > 0)
my_syslog(LOG_INFO, _("cleared cache")); my_syslog(LOG_INFO, _("cleared cache"));
return; return;
} }
@@ -847,7 +824,7 @@ void cache_unhash_dhcp(void)
up = &cache->hash_next; up = &cache->hash_next;
} }
void cache_add_dhcp_entry(struct daemon *daemon, char *host_name, void cache_add_dhcp_entry(char *host_name,
struct in_addr *host_address, time_t ttd) struct in_addr *host_address, time_t ttd)
{ {
struct crec *crec; struct crec *crec;
@@ -887,7 +864,7 @@ void cache_add_dhcp_entry(struct daemon *daemon, char *host_name,
if ((crec = dhcp_spare)) if ((crec = dhcp_spare))
dhcp_spare = dhcp_spare->next; dhcp_spare = dhcp_spare->next;
else /* need new one */ else /* need new one */
crec = malloc(sizeof(struct crec)); crec = whine_malloc(sizeof(struct crec));
if (crec) /* malloc may fail */ if (crec) /* malloc may fail */
{ {
@@ -902,13 +879,13 @@ void cache_add_dhcp_entry(struct daemon *daemon, char *host_name,
} }
} }
void dump_cache(struct daemon *daemon, time_t now) void dump_cache(time_t now)
{ {
my_syslog(LOG_INFO, _("time %lu, cache size %d, %d/%d cache insertions re-used unexpired cache entries."), my_syslog(LOG_INFO, _("time %lu, cache size %d, %d/%d cache insertions re-used unexpired cache entries."),
(unsigned long)now, daemon->cachesize, cache_live_freed, cache_inserted); (unsigned long)now, daemon->cachesize, cache_live_freed, cache_inserted);
if ((daemon->options & (OPT_DEBUG | OPT_LOG)) && if ((daemon->options & (OPT_DEBUG | OPT_LOG)) &&
(addrbuff || (addrbuff = malloc(ADDRSTRLEN)))) (addrbuff || (addrbuff = whine_malloc(ADDRSTRLEN))))
{ {
struct crec *cache ; struct crec *cache ;
int i; int i;
@@ -982,54 +959,60 @@ static char *record_source(struct hostsfile *addn_hosts, int index)
void log_query(unsigned short flags, char *name, struct all_addr *addr, void log_query(unsigned short flags, char *name, struct all_addr *addr,
unsigned short type, struct hostsfile *addn_hosts, int index) unsigned short type, struct hostsfile *addn_hosts, int index)
{ {
char *source; char *source, *dest = addrbuff;
char *verb = "is"; char *verb = "is";
char types[20]; char types[20];
if (!log_queries) if (!(daemon->options & OPT_LOG))
return; return;
if (addr)
{
#ifdef HAVE_IPV6
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, addrbuff, ADDRSTRLEN);
#else
strncpy(addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
#endif
}
if (flags & F_REVERSE)
{
dest = name;
name = addrbuff;
}
if (flags & F_NEG) if (flags & F_NEG)
{ {
if (flags & F_REVERSE)
#ifdef HAVE_IPV6
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, name, MAXDNAME);
#else
strcpy(name, inet_ntoa(addr->addr.addr4));
#endif
if (flags & F_NXDOMAIN) if (flags & F_NXDOMAIN)
strcpy(addrbuff, "<NXDOMAIN>"); {
if (flags & F_IPV4)
dest = "NXDOMAIN-IPv4";
else
dest = "NXDOMAIN-IPv6";
}
else else
strcpy(addrbuff, "<NODATA>"); {
if (flags & F_IPV4)
if (flags & F_IPV4) dest = "NODATA-IPv4";
strcat(addrbuff, "-IPv4"); else
else if (flags & F_IPV6) dest = "NODATA-IPv6";
strcat(addrbuff, "-IPv6"); }
} }
else if (flags & F_CNAME) else if (flags & F_CNAME)
{ {
/* nasty abuse of IPV4 and IPV6 flags */ /* nasty abuse of IPV4 and IPV6 flags */
if (flags & F_IPV4) if (flags & F_IPV4)
strcpy(addrbuff, "<MX>"); dest = "<MX>";
else if (flags & F_IPV6) else if (flags & F_IPV6)
strcpy(addrbuff, "<SRV>"); dest = "<SRV>";
else if (flags & F_NXDOMAIN) else if (flags & F_NXDOMAIN)
strcpy(addrbuff, "<TXT>"); dest = "<TXT>";
else if (flags & F_BIGNAME) else if (flags & F_BIGNAME)
strcpy(addrbuff, "<PTR>"); dest = "<PTR>";
else else
strcpy(addrbuff, "<CNAME>"); dest = "<CNAME>";
} }
else
#ifdef HAVE_IPV6
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, addrbuff, ADDRSTRLEN);
#else
strcpy(addrbuff, inet_ntoa(addr->addr.addr4));
#endif
if (flags & F_DHCP) if (flags & F_DHCP)
source = "DHCP"; source = "DHCP";
@@ -1064,9 +1047,6 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
if (strlen(name) == 0) if (strlen(name) == 0)
name = "."; name = ".";
if ((flags & F_FORWARD) | (flags & F_NEG)) my_syslog(LOG_DEBUG, "%s %s %s %s", source, name, verb, dest);
my_syslog(LOG_DEBUG, "%s %s %s %s", source, name, verb, addrbuff);
else if (flags & F_REVERSE)
my_syslog(LOG_DEBUG, "%s %s is %s", source, addrbuff, name);
} }

View File

@@ -10,7 +10,7 @@
GNU General Public License for more details. GNU General Public License for more details.
*/ */
#define VERSION "2.39" #define VERSION "2.40"
#define FTABSIZ 150 /* max number of outstanding requests (default) */ #define FTABSIZ 150 /* max number of outstanding requests (default) */
#define MAX_PROCS 20 /* max no children for TCP requests */ #define MAX_PROCS 20 /* max no children for TCP requests */
@@ -199,7 +199,8 @@ NOTES:
#undef HAVE_SOCKADDR_SA_LEN #undef HAVE_SOCKADDR_SA_LEN
/* Never use fork() on uClinux. Note that this is subtly different from the /* Never use fork() on uClinux. Note that this is subtly different from the
--keep-in-foreground option, since it also suppresses forking new --keep-in-foreground option, since it also suppresses forking new
processes for TCP connections. It's intended for use on MMU-less kernels. */ processes for TCP connections and disables the call-a-script on leasechange
system. It's intended for use on MMU-less kernels. */
#define NO_FORK #define NO_FORK
#elif defined(__UCLIBC__) #elif defined(__UCLIBC__)

View File

@@ -25,28 +25,25 @@ struct watch {
static dbus_bool_t add_watch(DBusWatch *watch, void *data) static dbus_bool_t add_watch(DBusWatch *watch, void *data)
{ {
struct daemon *daemon = data;
struct watch *w; struct watch *w;
for (w = daemon->watches; w; w = w->next) for (w = daemon->watches; w; w = w->next)
if (w->watch == watch) if (w->watch == watch)
return TRUE; return TRUE;
if (!(w = malloc(sizeof(struct watch)))) if (!(w = whine_malloc(sizeof(struct watch))))
return FALSE; return FALSE;
w->watch = watch; w->watch = watch;
w->next = daemon->watches; w->next = daemon->watches;
daemon->watches = w; daemon->watches = w;
dbus_watch_set_data (watch, (void *)daemon, NULL); w = data; /* no warning */
return TRUE; return TRUE;
} }
static void remove_watch(DBusWatch *watch, void *data) static void remove_watch(DBusWatch *watch, void *data)
{ {
struct daemon *daemon = data;
struct watch **up, *w; struct watch **up, *w;
for (up = &(daemon->watches), w = daemon->watches; w; w = w->next) for (up = &(daemon->watches), w = daemon->watches; w; w = w->next)
@@ -57,9 +54,11 @@ static void remove_watch(DBusWatch *watch, void *data)
} }
else else
up = &(w->next); up = &(w->next);
w = data; /* no warning */
} }
static void dbus_read_servers(struct daemon *daemon, DBusMessage *message) static void dbus_read_servers(DBusMessage *message)
{ {
struct server *serv, *tmp, **up; struct server *serv, *tmp, **up;
DBusMessageIter iter; DBusMessageIter iter;
@@ -161,11 +160,11 @@ static void dbus_read_servers(struct daemon *daemon, DBusMessage *message)
} }
} }
if (!serv && (serv = malloc(sizeof (struct server)))) if (!serv && (serv = whine_malloc(sizeof (struct server))))
{ {
/* Not found, create a new one. */ /* Not found, create a new one. */
if (domain) if (domain)
serv->domain = malloc(strlen(domain)+1); serv->domain = whine_malloc(strlen(domain)+1);
if (domain && !serv->domain) if (domain && !serv->domain)
{ {
free(serv); free(serv);
@@ -208,7 +207,7 @@ static void dbus_read_servers(struct daemon *daemon, DBusMessage *message)
tmp = serv->next; tmp = serv->next;
if (serv->flags & SERV_MARK) if (serv->flags & SERV_MARK)
{ {
server_gone(daemon, serv); server_gone(serv);
*up = serv->next; *up = serv->next;
free(serv); free(serv);
} }
@@ -223,8 +222,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
void *user_data) void *user_data)
{ {
char *method = (char *)dbus_message_get_member(message); char *method = (char *)dbus_message_get_member(message);
struct daemon *daemon = (struct daemon *)user_data;
if (strcmp(method, "GetVersion") == 0) if (strcmp(method, "GetVersion") == 0)
{ {
char *v = VERSION; char *v = VERSION;
@@ -237,21 +235,23 @@ DBusHandlerResult message_handler(DBusConnection *connection,
else if (strcmp(method, "SetServers") == 0) else if (strcmp(method, "SetServers") == 0)
{ {
my_syslog(LOG_INFO, _("setting upstream servers from DBus")); my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
dbus_read_servers(daemon, message); dbus_read_servers(message);
check_servers(daemon); check_servers();
} }
else if (strcmp(method, "ClearCache") == 0) else if (strcmp(method, "ClearCache") == 0)
clear_cache_and_reload(daemon, dnsmasq_time()); clear_cache_and_reload(dnsmasq_time());
else else
return (DBUS_HANDLER_RESULT_NOT_YET_HANDLED); return (DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
method = user_data; /* no warning */
return (DBUS_HANDLER_RESULT_HANDLED); return (DBUS_HANDLER_RESULT_HANDLED);
} }
/* returns NULL or error message, may fail silently if dbus daemon not yet up. */ /* returns NULL or error message, may fail silently if dbus daemon not yet up. */
char *dbus_init(struct daemon *daemon) char *dbus_init(void)
{ {
DBusConnection *connection = NULL; DBusConnection *connection = NULL;
DBusObjectPathVTable dnsmasq_vtable = {NULL, &message_handler, NULL, NULL, NULL, NULL }; DBusObjectPathVTable dnsmasq_vtable = {NULL, &message_handler, NULL, NULL, NULL, NULL };
@@ -264,14 +264,14 @@ char *dbus_init(struct daemon *daemon)
dbus_connection_set_exit_on_disconnect(connection, FALSE); dbus_connection_set_exit_on_disconnect(connection, FALSE);
dbus_connection_set_watch_functions(connection, add_watch, remove_watch, dbus_connection_set_watch_functions(connection, add_watch, remove_watch,
NULL, (void *)daemon, NULL); NULL, NULL, NULL);
dbus_error_init (&dbus_error); dbus_error_init (&dbus_error);
dbus_bus_request_name (connection, DNSMASQ_SERVICE, 0, &dbus_error); dbus_bus_request_name (connection, DNSMASQ_SERVICE, 0, &dbus_error);
if (dbus_error_is_set (&dbus_error)) if (dbus_error_is_set (&dbus_error))
return (char *)dbus_error.message; return (char *)dbus_error.message;
if (!dbus_connection_register_object_path(connection, DNSMASQ_PATH, if (!dbus_connection_register_object_path(connection, DNSMASQ_PATH,
&dnsmasq_vtable, daemon)) &dnsmasq_vtable, NULL))
return _("could not register a DBus message handler"); return _("could not register a DBus message handler");
daemon->dbus = connection; daemon->dbus = connection;
@@ -283,7 +283,7 @@ char *dbus_init(struct daemon *daemon)
} }
void set_dbus_listeners(struct daemon *daemon, int *maxfdp, void set_dbus_listeners(int *maxfdp,
fd_set *rset, fd_set *wset, fd_set *eset) fd_set *rset, fd_set *wset, fd_set *eset)
{ {
struct watch *w; struct watch *w;
@@ -306,8 +306,7 @@ void set_dbus_listeners(struct daemon *daemon, int *maxfdp,
} }
} }
void check_dbus_listeners(struct daemon *daemon, void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset)
fd_set *rset, fd_set *wset, fd_set *eset)
{ {
DBusConnection *connection = (DBusConnection *)daemon->dbus; DBusConnection *connection = (DBusConnection *)daemon->dbus;
struct watch *w; struct watch *w;

View File

@@ -18,10 +18,10 @@ struct iface_param {
int ind; int ind;
}; };
static int complete_context(struct daemon *daemon, struct in_addr local, int if_index, static int complete_context(struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam); struct in_addr netmask, struct in_addr broadcast, void *vparam);
void dhcp_init(struct daemon *daemon) void dhcp_init(void)
{ {
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in saddr; struct sockaddr_in saddr;
@@ -29,7 +29,7 @@ void dhcp_init(struct daemon *daemon)
struct dhcp_config *configs, *cp; struct dhcp_config *configs, *cp;
if (fd == -1) if (fd == -1)
die (_("cannot create DHCP socket : %s"), NULL); die (_("cannot create DHCP socket : %s"), NULL, EC_BADNET);
if (!fix_fd(fd) || if (!fix_fd(fd) ||
#if defined(HAVE_LINUX_NETWORK) #if defined(HAVE_LINUX_NETWORK)
@@ -38,7 +38,7 @@ void dhcp_init(struct daemon *daemon)
setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 || setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
#endif #endif
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1) setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
die(_("failed to set options on DHCP socket: %s"), NULL); die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);
/* When bind-interfaces is set, there might be more than one dnmsasq /* When bind-interfaces is set, there might be more than one dnmsasq
instance binding port 67. That's OK if they serve different networks. instance binding port 67. That's OK if they serve different networks.
@@ -56,7 +56,7 @@ void dhcp_init(struct daemon *daemon)
int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt)); int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
#endif #endif
if (rc == -1) if (rc == -1)
die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL); die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
} }
#endif #endif
@@ -69,7 +69,7 @@ void dhcp_init(struct daemon *daemon)
#endif #endif
if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in))) if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
die(_("failed to bind DHCP server socket: %s"), NULL); die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
daemon->dhcpfd = fd; daemon->dhcpfd = fd;
@@ -82,30 +82,32 @@ void dhcp_init(struct daemon *daemon)
daemon->dhcp_icmp_fd = -1; daemon->dhcp_icmp_fd = -1;
else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 || else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 ) setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
die(_("cannot create ICMP raw socket: %s."), NULL); die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET);
/* Make BPF raw send socket */ /* Make BPF raw send socket */
init_bpf(daemon); init_bpf();
#endif #endif
/* If the same IP appears in more than one host config, then DISCOVER /* If the same IP appears in more than one host config, then DISCOVER
for one of the hosts will get the address, but REQUEST will be NAKed, for one of the hosts will get the address, but REQUEST will be NAKed,
since the address is reserved by the other one -> protocol loop. */ since the address is reserved by the other one -> protocol loop.
Also check that FQDNs match the domain we are using. */
for (configs = daemon->dhcp_conf; configs; configs = configs->next) for (configs = daemon->dhcp_conf; configs; configs = configs->next)
for (cp = configs->next; cp; cp = cp->next) {
if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr) char *domain;
die(_("duplicate IP address %s in dhcp-config directive."), inet_ntoa(cp->addr)); for (cp = configs->next; cp; cp = cp->next)
if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
die(_("duplicate IP address %s in dhcp-config directive."), inet_ntoa(cp->addr), EC_BADCONF);
if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
die(_("illegal domain %s in dhcp-config directive."), domain, EC_BADCONF);
}
daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet); daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet);
daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len); daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
/* These two each hold a DHCP option max size 255
and get a terminating zero added */
daemon->dhcp_buff = safe_malloc(256);
daemon->dhcp_buff2 = safe_malloc(256);
daemon->ping_results = NULL;
} }
void dhcp_packet(struct daemon *daemon, time_t now) void dhcp_packet(time_t now)
{ {
struct dhcp_packet *mess; struct dhcp_packet *mess;
struct dhcp_context *context; struct dhcp_context *context;
@@ -116,7 +118,7 @@ void dhcp_packet(struct daemon *daemon, time_t now)
struct cmsghdr *cmptr; struct cmsghdr *cmptr;
struct iovec iov; struct iovec iov;
ssize_t sz; ssize_t sz;
int iface_index = 0, unicast_dest = 0; int iface_index = 0, unicast_dest = 0, is_inform = 0;
struct in_addr iface_addr, *addrp = NULL; struct in_addr iface_addr, *addrp = NULL;
struct iface_param parm; struct iface_param parm;
@@ -204,7 +206,7 @@ void dhcp_packet(struct daemon *daemon, time_t now)
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
} }
if (!iface_check(daemon, AF_INET, (struct all_addr *)addrp, &ifr, &iface_index)) if (!iface_check(AF_INET, (struct all_addr *)addrp, &ifr, &iface_index))
return; return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
@@ -232,12 +234,13 @@ void dhcp_packet(struct daemon *daemon, time_t now)
parm.current = NULL; parm.current = NULL;
parm.ind = iface_index; parm.ind = iface_index;
if (!iface_enumerate(daemon, &parm, complete_context, NULL)) if (!iface_enumerate(&parm, complete_context, NULL))
return; return;
lease_prune(NULL, now); /* lose any expired leases */ lease_prune(NULL, now); /* lose any expired leases */
iov.iov_len = dhcp_reply(daemon, parm.current, ifr.ifr_name, (size_t)sz, now, unicast_dest); iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, (size_t)sz,
lease_update_file(daemon, now); now, unicast_dest, &is_inform);
lease_update_dns(daemon); lease_update_file(now);
lease_update_dns();
if (iov.iov_len == 0) if (iov.iov_len == 0)
return; return;
@@ -266,8 +269,10 @@ void dhcp_packet(struct daemon *daemon, time_t now)
{ {
/* If the client's idea of its own address tallys with /* If the client's idea of its own address tallys with
the source address in the request packet, we believe the the source address in the request packet, we believe the
source port too, and send back to that. */ source port too, and send back to that. If we're replying
if (dest.sin_addr.s_addr != mess->ciaddr.s_addr || !dest.sin_port) to a DHCPINFORM, trust the source address always. */
if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
dest.sin_port == 0 || dest.sin_addr.s_addr == 0)
{ {
dest.sin_port = htons(DHCP_CLIENT_PORT); dest.sin_port = htons(DHCP_CLIENT_PORT);
dest.sin_addr = mess->ciaddr; dest.sin_addr = mess->ciaddr;
@@ -308,7 +313,7 @@ void dhcp_packet(struct daemon *daemon, time_t now)
#else #else
else else
{ {
send_via_bpf(daemon, mess, iov.iov_len, iface_addr, &ifr); send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
return; return;
} }
#endif #endif
@@ -326,15 +331,12 @@ void dhcp_packet(struct daemon *daemon, time_t now)
Note that the current chain may be superceded later for configured hosts or those coming via gateways. */ Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
static int complete_context(struct daemon *daemon, struct in_addr local, int if_index, static int complete_context(struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam) struct in_addr netmask, struct in_addr broadcast, void *vparam)
{ {
struct dhcp_context *context; struct dhcp_context *context;
struct iface_param *param = vparam; struct iface_param *param = vparam;
if (if_index != param->ind)
return 1; /* no for us. */
for (context = daemon->dhcp; context; context = context->next) for (context = daemon->dhcp; context; context = context->next)
{ {
if (!(context->flags & CONTEXT_NETMASK) && if (!(context->flags & CONTEXT_NETMASK) &&
@@ -359,7 +361,7 @@ static int complete_context(struct daemon *daemon, struct in_addr local, int if_
is_same_net(local, context->end, context->netmask)) is_same_net(local, context->end, context->netmask))
{ {
/* link it onto the current chain if we've not seen it before */ /* link it onto the current chain if we've not seen it before */
if (context->current == context) if (if_index == param->ind && context->current == context)
{ {
context->router = local; context->router = local;
context->local = local; context->local = local;
@@ -482,7 +484,7 @@ int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly)
return 1; return 1;
} }
int address_allocate(struct dhcp_context *context, struct daemon *daemon, int address_allocate(struct dhcp_context *context,
struct in_addr *addrp, unsigned char *hwaddr, int hw_len, struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
struct dhcp_netid *netids, time_t now) struct dhcp_netid *netids, time_t now)
{ {
@@ -543,7 +545,7 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
else if (++count == max || r->addr.s_addr == addr.s_addr) else if (++count == max || r->addr.s_addr == addr.s_addr)
return 1; return 1;
if (icmp_ping(daemon, addr)) if (icmp_ping(addr))
/* address in use: perturb address selection so that we are /* address in use: perturb address selection so that we are
less likely to try this address again. */ less likely to try this address again. */
c->addr_epoch++; c->addr_epoch++;
@@ -552,7 +554,7 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
/* at this point victim may hold an expired record */ /* at this point victim may hold an expired record */
if (!victim) if (!victim)
{ {
if ((victim = malloc(sizeof(struct ping_result)))) if ((victim = whine_malloc(sizeof(struct ping_result))))
{ {
victim->next = daemon->ping_results; victim->next = daemon->ping_results;
daemon->ping_results = victim; daemon->ping_results = victim;
@@ -648,7 +650,7 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
return NULL; return NULL;
} }
void dhcp_read_ethers(struct daemon *daemon) void dhcp_read_ethers(void)
{ {
FILE *f = fopen(ETHERSFILE, "r"); FILE *f = fopen(ETHERSFILE, "r");
unsigned int flags; unsigned int flags;
@@ -724,12 +726,12 @@ void dhcp_read_ethers(struct daemon *daemon)
} }
else else
{ {
if (!canonicalise(ip)) if (!canonicalise(ip) || strip_hostname(ip))
{ {
my_syslog(LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno); my_syslog(LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
continue; continue;
} }
flags = CONFIG_NAME; flags = CONFIG_NAME;
for (config = daemon->dhcp_conf; config; config = config->next) for (config = daemon->dhcp_conf; config; config = config->next)
@@ -749,7 +751,7 @@ void dhcp_read_ethers(struct daemon *daemon)
if (!config) if (!config)
{ {
if (!(config = malloc(sizeof(struct dhcp_config)))) if (!(config = whine_malloc(sizeof(struct dhcp_config))))
continue; continue;
config->flags = CONFIG_FROM_ETHERS; config->flags = CONFIG_FROM_ETHERS;
config->wildcard_mask = 0; config->wildcard_mask = 0;
@@ -761,7 +763,7 @@ void dhcp_read_ethers(struct daemon *daemon)
if (flags & CONFIG_NAME) if (flags & CONFIG_NAME)
{ {
if ((config->hostname = malloc(strlen(ip)+1))) if ((config->hostname = whine_malloc(strlen(ip)+1)))
strcpy(config->hostname, ip); strcpy(config->hostname, ip);
else else
config->flags &= ~CONFIG_NAME; config->flags &= ~CONFIG_NAME;
@@ -783,6 +785,61 @@ void dhcp_read_ethers(struct daemon *daemon)
my_syslog(LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count); my_syslog(LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
} }
void dhcp_read_hosts(void)
{
struct dhcp_config *configs, *cp, **up;
int count;
/* remove existing... */
for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
{
cp = configs->next;
if (configs->flags & CONFIG_BANK)
{
if (configs->flags & CONFIG_CLID)
free(configs->clid);
if (configs->flags & CONFIG_NETID)
free(configs->netid.net);
if (configs->flags & CONFIG_NAME)
free(configs->hostname);
*up = configs->next;
free(configs);
}
else
up = &configs->next;
}
one_file(daemon->dhcp_hosts_file, 1, 1);
for (count = 0, configs = daemon->dhcp_conf; configs; configs = configs->next)
{
if (configs->flags & CONFIG_BANK)
{
char *domain;
count++;
for (cp = configs->next; cp; cp = cp->next)
if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
{
my_syslog(LOG_ERR, _("duplicate IP address %s in %s."), inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
configs->flags &= ~CONFIG_ADDR;
}
if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
{
my_syslog(LOG_ERR, _("illegal domain %s in %s."), domain, daemon->dhcp_hosts_file);
free(configs->hostname);
configs->flags &= ~CONFIG_NAME;
}
}
}
my_syslog(LOG_INFO, _("read %s - %d hosts"), daemon->dhcp_hosts_file, count);
}
void dhcp_update_configs(struct dhcp_config *configs) void dhcp_update_configs(struct dhcp_config *configs)
{ {
/* Some people like to keep all static IP addresses in /etc/hosts. /* Some people like to keep all static IP addresses in /etc/hosts.
@@ -819,7 +876,7 @@ void dhcp_update_configs(struct dhcp_config *configs)
/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts /* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
for this address. If it has a domain part, that must match the set domain and for this address. If it has a domain part, that must match the set domain and
it gets stripped. */ it gets stripped. */
char *host_from_dns(struct daemon *daemon, struct in_addr addr) char *host_from_dns(struct in_addr addr)
{ {
struct crec *lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4); struct crec *lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
char *hostname = NULL; char *hostname = NULL;
@@ -829,28 +886,25 @@ char *host_from_dns(struct daemon *daemon, struct in_addr addr)
hostname = daemon->dhcp_buff; hostname = daemon->dhcp_buff;
strncpy(hostname, cache_get_name(lookup), 256); strncpy(hostname, cache_get_name(lookup), 256);
hostname[255] = 0; hostname[255] = 0;
hostname = strip_hostname(daemon, hostname); if (strip_hostname(hostname))
hostname = NULL;
} }
return hostname; return hostname;
} }
char *strip_hostname(struct daemon *daemon, char *hostname) /* return illegal domain or NULL if OK */
char *strip_hostname(char *hostname)
{ {
char *dot = strchr(hostname, '.'); char *dot = strchr(hostname, '.');
if (dot)
{ if (!dot)
if (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix)) return NULL;
{
my_syslog(LOG_WARNING, _("Ignoring DHCP host name %s because it has an illegal domain part"), hostname); *dot = 0; /* truncate */
hostname = NULL;
} if (*(dot+1) && (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix)))
else return dot+1;
{
*dot = 0; /* truncate */ return NULL;
if (strlen(hostname) == 0)
hostname = NULL; /* nothing left */
}
}
return hostname;
} }

View File

@@ -12,6 +12,8 @@
#include "dnsmasq.h" #include "dnsmasq.h"
struct daemon *daemon;
static char *compile_opts = static char *compile_opts =
#ifndef HAVE_IPV6 #ifndef HAVE_IPV6
"no-" "no-"
@@ -44,36 +46,37 @@ static char *compile_opts =
#endif #endif
"TFTP"; "TFTP";
static pid_t pid; static volatile pid_t pid = 0;
static int pipewrite; static volatile int pipewrite;
static int set_dns_listeners(struct daemon *daemon, time_t now, fd_set *set, int *maxfdp); static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp);
static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now); static void check_dns_listeners(fd_set *set, time_t now);
static void sig_handler(int sig); static void sig_handler(int sig);
static void async_event(int pipe, time_t now);
static void poll_resolv(void);
int main (int argc, char **argv) int main (int argc, char **argv)
{ {
struct daemon *daemon;
int bind_fallback = 0; int bind_fallback = 0;
int bad_capabilities = 0; int bad_capabilities = 0;
time_t now, last = 0; time_t now, last = 0;
struct sigaction sigact; struct sigaction sigact;
struct iname *if_tmp; struct iname *if_tmp;
int piperead, pipefd[2], log_fd; int piperead, pipefd[2];
unsigned char sig; struct passwd *ent_pw;
long i, max_fd = sysconf(_SC_OPEN_MAX);
#ifndef NO_GETTEXT #ifndef NO_GETTEXT
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
bindtextdomain("dnsmasq", LOCALEDIR); bindtextdomain("dnsmasq", LOCALEDIR);
textdomain("dnsmasq"); textdomain("dnsmasq");
#endif #endif
pid = 0;
sigact.sa_handler = sig_handler; sigact.sa_handler = sig_handler;
sigact.sa_flags = 0; sigact.sa_flags = 0;
sigemptyset(&sigact.sa_mask); sigemptyset(&sigact.sa_mask);
sigaction(SIGUSR1, &sigact, NULL); sigaction(SIGUSR1, &sigact, NULL);
sigaction(SIGUSR2, &sigact, NULL);
sigaction(SIGHUP, &sigact, NULL); sigaction(SIGHUP, &sigact, NULL);
sigaction(SIGTERM, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGALRM, &sigact, NULL); sigaction(SIGALRM, &sigact, NULL);
@@ -83,9 +86,10 @@ int main (int argc, char **argv)
sigact.sa_handler = SIG_IGN; sigact.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sigact, NULL); sigaction(SIGPIPE, &sigact, NULL);
daemon = read_opts(argc, argv, compile_opts); umask(022); /* known umask, create leases and pid files as 0644 */
log_fd = log_start(daemon);
read_opts(argc, argv, compile_opts);
if (daemon->edns_pktsz < PACKETSZ) if (daemon->edns_pktsz < PACKETSZ)
daemon->edns_pktsz = PACKETSZ; daemon->edns_pktsz = PACKETSZ;
daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ? daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ?
@@ -99,11 +103,16 @@ int main (int argc, char **argv)
} }
#ifndef HAVE_ISC_READER #ifndef HAVE_ISC_READER
else if (!daemon->dhcp) else if (!daemon->dhcp)
die(_("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h"), NULL); die(_("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h"), NULL, EC_BADCONF);
#endif #endif
/* Close any file descriptors we inherited apart from std{in|out|err} */
for (i = 0; i < max_fd; i++)
if (i != STDOUT_FILENO && i != STDERR_FILENO && i != STDIN_FILENO)
close(i);
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_LINUX_NETWORK
netlink_init(daemon); netlink_init();
#elif !(defined(IP_RECVDSTADDR) && \ #elif !(defined(IP_RECVDSTADDR) && \
defined(IP_RECVIF) && \ defined(IP_RECVIF) && \
defined(IP_SENDSRCADDR)) defined(IP_SENDSRCADDR))
@@ -116,33 +125,9 @@ int main (int argc, char **argv)
#ifndef HAVE_TFTP #ifndef HAVE_TFTP
if (daemon->options & OPT_TFTP) if (daemon->options & OPT_TFTP)
die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL); die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF);
#endif #endif
daemon->interfaces = NULL;
if (!enumerate_interfaces(daemon))
die(_("failed to find list of interfaces: %s"), NULL);
if (daemon->options & OPT_NOWILD)
{
daemon->listeners = create_bound_listeners(daemon);
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
if (if_tmp->name && !if_tmp->used)
die(_("unknown interface %s"), if_tmp->name);
for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next)
if (!if_tmp->used)
{
prettyprint_addr(&if_tmp->addr, daemon->namebuff);
die(_("no interface with address %s"), daemon->namebuff);
}
}
else if (!(daemon->listeners = create_wildcard_listeners(daemon->port, daemon->options & OPT_TFTP)))
die(_("failed to create listening socket: %s"), NULL);
cache_init(daemon->cachesize, daemon->options & OPT_LOG);
now = dnsmasq_time(); now = dnsmasq_time();
if (daemon->dhcp) if (daemon->dhcp)
@@ -154,23 +139,49 @@ int main (int argc, char **argv)
if (!tmp->isloop) if (!tmp->isloop)
c++; c++;
if (c != 1) if (c != 1)
die(_("must set exactly one interface on broken systems without IP_RECVIF"), NULL); die(_("must set exactly one interface on broken systems without IP_RECVIF"), NULL, EC_BADCONF);
#endif #endif
dhcp_init(daemon); /* Note that order matters here, we must call lease_init before
lease_init(daemon, now); creating any file descriptors which shouldn't be leaked
to the lease-script init process. */
lease_init(now);
dhcp_init();
} }
if (!enumerate_interfaces())
die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
if (daemon->options & OPT_NOWILD)
{
daemon->listeners = create_bound_listeners();
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
if (if_tmp->name && !if_tmp->used)
die(_("unknown interface %s"), if_tmp->name, EC_BADNET);
for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next)
if (!if_tmp->used)
{
prettyprint_addr(&if_tmp->addr, daemon->namebuff);
die(_("no interface with address %s"), daemon->namebuff, EC_BADNET);
}
}
else if (!(daemon->listeners = create_wildcard_listeners()))
die(_("failed to create listening socket: %s"), NULL, EC_BADNET);
cache_init();
if (daemon->options & OPT_DBUS) if (daemon->options & OPT_DBUS)
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
{ {
char *err; char *err;
daemon->dbus = NULL; daemon->dbus = NULL;
daemon->watches = NULL; daemon->watches = NULL;
if ((err = dbus_init(daemon))) if ((err = dbus_init()))
die(_("DBus error: %s"), err); die(_("DBus error: %s"), err, EC_MISC);
} }
#else #else
die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL); die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL, EC_BADCONF);
#endif #endif
/* If query_port is set then create a socket now, before dumping root /* If query_port is set then create a socket now, before dumping root
@@ -199,22 +210,20 @@ int main (int argc, char **argv)
#endif #endif
} }
/* Use a pipe to carry signals back to the event loop in a race-free manner */ /* Use a pipe to carry signals and other events back to the event loop
in a race-free manner */
if (pipe(pipefd) == -1 || !fix_fd(pipefd[0]) || !fix_fd(pipefd[1])) if (pipe(pipefd) == -1 || !fix_fd(pipefd[0]) || !fix_fd(pipefd[1]))
die(_("cannot create pipe: %s"), NULL); die(_("cannot create pipe: %s"), NULL, EC_MISC);
piperead = pipefd[0]; piperead = pipefd[0];
pipewrite = pipefd[1]; pipewrite = pipefd[1];
/* prime the pipe to load stuff first time. */ /* prime the pipe to load stuff first time. */
sig = SIGHUP; send_event(pipewrite, EVENT_RELOAD, 0);
write(pipewrite, &sig, 1);
if (!(daemon->options & OPT_DEBUG)) if (!(daemon->options & OPT_DEBUG))
{ {
FILE *pidfile; FILE *pidfile;
fd_set test_set; int nullfd;
int maxfd = -1, i;
int nullfd = open("/dev/null", O_RDWR);
/* The following code "daemonizes" the process. /* The following code "daemonizes" the process.
See Stevens section 12.4 */ See Stevens section 12.4 */
@@ -222,19 +231,24 @@ int main (int argc, char **argv)
#ifndef NO_FORK #ifndef NO_FORK
if (!(daemon->options & OPT_NO_FORK)) if (!(daemon->options & OPT_NO_FORK))
{ {
if (fork() != 0 ) pid_t pid;
_exit(0);
if ((pid = fork()) == -1 )
die(_("cannot fork into background: %s"), NULL, EC_MISC);
if (pid != 0)
_exit(EC_GOOD);
setsid(); setsid();
pid = fork();
if (fork() != 0)
if (pid != 0 && pid != -1)
_exit(0); _exit(0);
} }
#endif #endif
chdir("/"); chdir("/");
umask(022); /* make pidfile 0644 */
/* write pidfile _after_ forking ! */ /* write pidfile _after_ forking ! */
if (daemon->runfile && (pidfile = fopen(daemon->runfile, "w"))) if (daemon->runfile && (pidfile = fopen(daemon->runfile, "w")))
{ {
@@ -242,51 +256,27 @@ int main (int argc, char **argv)
fclose(pidfile); fclose(pidfile);
} }
umask(0); /* open stdout etc to /dev/null */
nullfd = open("/dev/null", O_RDWR);
FD_ZERO(&test_set); dup2(nullfd, STDOUT_FILENO);
set_dns_listeners(daemon, now, &test_set, &maxfd); dup2(nullfd, STDERR_FILENO);
#ifdef HAVE_DBUS dup2(nullfd, STDIN_FILENO);
set_dbus_listeners(daemon, &maxfd, &test_set, &test_set, &test_set); close(nullfd);
#endif
for (i=0; i<64; i++)
{
if (i == piperead || i == pipewrite || i == log_fd)
continue;
#ifdef HAVE_LINUX_NETWORK
if (i == daemon->netlinkfd)
continue;
#endif
if (daemon->dhcp &&
((daemon->lease_stream && i == fileno(daemon->lease_stream)) ||
#ifndef HAVE_LINUX_NETWORK
i == daemon->dhcp_raw_fd ||
i == daemon->dhcp_icmp_fd ||
#endif
i == daemon->dhcpfd))
continue;
if (i <= maxfd && FD_ISSET(i, &test_set))
continue;
/* open stdout etc to /dev/null */
if (i == STDOUT_FILENO || i == STDERR_FILENO || i == STDIN_FILENO)
dup2(nullfd, i);
else
close(i);
}
} }
/* if we are to run scripts, we need to fork a helper before dropping root. */ /* if we are to run scripts, we need to fork a helper before dropping root. */
daemon->helperfd = create_helper(daemon, log_fd); #ifndef NO_FORK
daemon->helperfd = create_helper(pipewrite, max_fd);
#endif
ent_pw = daemon->username ? getpwnam(daemon->username) : NULL;
/* before here, we should only call die(), after here, only call syslog() */
log_start(ent_pw);
if (!(daemon->options & OPT_DEBUG)) if (!(daemon->options & OPT_DEBUG))
{ {
/* UID changing, etc */ /* UID changing, etc */
struct passwd *ent_pw = daemon->username ? getpwnam(daemon->username) : NULL;
if (daemon->groupname || ent_pw) if (daemon->groupname || ent_pw)
{ {
gid_t dummy; gid_t dummy;
@@ -397,10 +387,8 @@ int main (int argc, char **argv)
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
if (daemon->options & OPT_TFTP) if (daemon->options & OPT_TFTP)
{ {
long max_fd = sysconf(_SC_OPEN_MAX);
#ifdef FD_SETSIZE #ifdef FD_SETSIZE
if (FD_SETSIZE < max_fd) if (FD_SETSIZE < (unsigned)max_fd)
max_fd = FD_SETSIZE; max_fd = FD_SETSIZE;
#endif #endif
@@ -441,7 +429,7 @@ int main (int argc, char **argv)
my_syslog(LOG_WARNING, _("running as root")); my_syslog(LOG_WARNING, _("running as root"));
} }
check_servers(daemon); check_servers();
pid = getpid(); pid = getpid();
@@ -458,7 +446,7 @@ int main (int argc, char **argv)
/* if we are out of resources, find how long we have to wait /* if we are out of resources, find how long we have to wait
for some to come free, we'll loop around then and restart for some to come free, we'll loop around then and restart
listening for queries */ listening for queries */
if ((t.tv_sec = set_dns_listeners(daemon, now, &rset, &maxfd)) != 0) if ((t.tv_sec = set_dns_listeners(now, &rset, &maxfd)) != 0)
{ {
t.tv_usec = 0; t.tv_usec = 0;
tp = &t; tp = &t;
@@ -474,7 +462,7 @@ int main (int argc, char **argv)
} }
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
set_dbus_listeners(daemon, &maxfd, &rset, &wset, &eset); set_dbus_listeners(&maxfd, &rset, &wset, &eset);
#endif #endif
if (daemon->dhcp) if (daemon->dhcp)
@@ -491,13 +479,18 @@ int main (int argc, char **argv)
FD_SET(piperead, &rset); FD_SET(piperead, &rset);
bump_maxfd(piperead, &maxfd); bump_maxfd(piperead, &maxfd);
while (helper_buf_empty() && do_script_run(daemon)); #ifndef NO_FORK
while (helper_buf_empty() && do_script_run(now));
if (!helper_buf_empty()) if (!helper_buf_empty())
{ {
FD_SET(daemon->helperfd, &wset); FD_SET(daemon->helperfd, &wset);
bump_maxfd(daemon->helperfd, &maxfd); bump_maxfd(daemon->helperfd, &maxfd);
} }
#else
/* need this for other side-effects */
while (do_script_run(now));
#endif
/* must do this just before select(), when we know no /* must do this just before select(), when we know no
more calls to my_syslog() can occur */ more calls to my_syslog() can occur */
@@ -521,140 +514,19 @@ int main (int argc, char **argv)
#ifdef HAVE_ISC_READER #ifdef HAVE_ISC_READER
if (daemon->lease_file && !daemon->dhcp) if (daemon->lease_file && !daemon->dhcp)
load_dhcp(daemon, now); load_dhcp(now);
#endif #endif
if (!(daemon->options & OPT_NO_POLL)) if (!(daemon->options & OPT_NO_POLL))
{ poll_resolv();
struct resolvc *res, *latest;
struct stat statbuf;
time_t last_change = 0;
/* There may be more than one possible file.
Go through and find the one which changed _last_.
Warn of any which can't be read. */
for (latest = NULL, res = daemon->resolv_files; res; res = res->next)
if (stat(res->name, &statbuf) == -1)
{
if (!res->logged)
my_syslog(LOG_WARNING, _("failed to access %s: %s"), res->name, strerror(errno));
res->logged = 1;
}
else
{
res->logged = 0;
if (statbuf.st_mtime != res->mtime)
{
res->mtime = statbuf.st_mtime;
if (difftime(statbuf.st_mtime, last_change) > 0.0)
{
last_change = statbuf.st_mtime;
latest = res;
}
}
}
if (latest)
{
static int warned = 0;
if (reload_servers(latest->name, daemon))
{
my_syslog(LOG_INFO, _("reading %s"), latest->name);
warned = 0;
check_servers(daemon);
if (daemon->options & OPT_RELOAD)
cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts);
}
else
{
latest->mtime = 0;
if (!warned)
{
my_syslog(LOG_WARNING, _("no servers found in %s, will retry"), latest->name);
warned = 1;
}
}
}
}
} }
if (FD_ISSET(piperead, &rset)) if (FD_ISSET(piperead, &rset))
{ async_event(piperead, now);
pid_t p;
if (read(piperead, &sig, 1) == 1)
switch (sig)
{
case SIGHUP:
clear_cache_and_reload(daemon, now);
if (daemon->resolv_files && (daemon->options & OPT_NO_POLL))
{
reload_servers(daemon->resolv_files->name, daemon);
check_servers(daemon);
}
break;
case SIGUSR1:
dump_cache(daemon, now);
break;
case SIGALRM:
if (daemon->dhcp)
{
lease_prune(NULL, now);
lease_update_file(daemon, now);
}
break;
case SIGTERM:
{
int i;
/* Knock all our children on the head. */
for (i = 0; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] != 0)
kill(daemon->tcp_pids[i], SIGALRM);
/* handle pending lease transitions */
if (daemon->helperfd != -1)
{
/* block in writes until all done */
if ((i = fcntl(daemon->helperfd, F_GETFL)) != -1)
fcntl(daemon->helperfd, F_SETFL, i & ~O_NONBLOCK);
do {
helper_write(daemon);
} while (!helper_buf_empty() || do_script_run(daemon));
close(daemon->helperfd);
}
if (daemon->lease_stream)
fclose(daemon->lease_stream);
my_syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
exit(0);
}
case SIGCHLD:
/* See Stevens 5.10 */
/* Note that if a script process forks and then exits
without waiting for its child, we will reap that child.
It is not therefore safe to assume that any dieing children
whose pid != script_pid are TCP server threads. */
while ((p = waitpid(-1, NULL, WNOHANG)) > 0)
{
int i;
for (i = 0 ; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] == p)
{
daemon->tcp_pids[i] = 0;
break;
}
}
break;
}
}
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_LINUX_NETWORK
if (FD_ISSET(daemon->netlinkfd, &rset)) if (FD_ISSET(daemon->netlinkfd, &rset))
netlink_multicast(daemon); netlink_multicast();
#endif #endif
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
@@ -662,25 +534,28 @@ int main (int argc, char **argv)
if ((daemon->options & OPT_DBUS) && !daemon->dbus) if ((daemon->options & OPT_DBUS) && !daemon->dbus)
{ {
char *err; char *err;
if ((err = dbus_init(daemon))) if ((err = dbus_init()))
my_syslog(LOG_WARNING, _("DBus error: %s"), err); my_syslog(LOG_WARNING, _("DBus error: %s"), err);
if (daemon->dbus) if (daemon->dbus)
my_syslog(LOG_INFO, _("connected to system DBus")); my_syslog(LOG_INFO, _("connected to system DBus"));
} }
check_dbus_listeners(daemon, &rset, &wset, &eset); check_dbus_listeners(&rset, &wset, &eset);
#endif #endif
check_dns_listeners(daemon, &rset, now); check_dns_listeners(&rset, now);
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
check_tftp_listeners(daemon, &rset, now); check_tftp_listeners(&rset, now);
#endif #endif
if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset)) if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
dhcp_packet(daemon, now); dhcp_packet(now);
#ifndef NO_FORK
if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset)) if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset))
helper_write(daemon); helper_write();
#endif
} }
} }
@@ -691,40 +566,217 @@ static void sig_handler(int sig)
/* ignore anything other than TERM during startup /* ignore anything other than TERM during startup
and in helper proc. (helper ignore TERM too) */ and in helper proc. (helper ignore TERM too) */
if (sig == SIGTERM) if (sig == SIGTERM)
exit(0); exit(EC_MISC);
} }
else if (pid == getpid()) else if (pid != getpid())
{
/* master process */
unsigned char sigchr = sig;
int errsave = errno;
write(pipewrite, &sigchr, 1);
errno = errsave;
}
else
{ {
/* alarm is used to kill TCP children after a fixed time. */ /* alarm is used to kill TCP children after a fixed time. */
if (sig == SIGALRM) if (sig == SIGALRM)
_exit(0); _exit(0);
} }
else
{
/* master process */
int event, errsave = errno;
if (sig == SIGHUP)
event = EVENT_RELOAD;
else if (sig == SIGCHLD)
event = EVENT_CHILD;
else if (sig == SIGALRM)
event = EVENT_ALARM;
else if (sig == SIGTERM)
event = EVENT_TERM;
else if (sig == SIGUSR1)
event = EVENT_DUMP;
else if (sig == SIGUSR2)
event = EVENT_REOPEN;
else
return;
send_event(pipewrite, event, 0);
errno = errsave;
}
} }
void send_event(int fd, int event, int data)
{
struct event_desc ev;
ev.event = event;
ev.data = data;
/* pipe is non-blocking and struct event_desc is smaller than
PIPE_BUF, so this either fails or writes everything */
while (write(fd, &ev, sizeof(ev)) == -1 && errno == EINTR);
}
void clear_cache_and_reload(struct daemon *daemon, time_t now) static void async_event(int pipe, time_t now)
{
pid_t p;
struct event_desc ev;
int i;
if (read_write(pipe, (unsigned char *)&ev, sizeof(ev), 1))
switch (ev.event)
{
case EVENT_RELOAD:
clear_cache_and_reload(now);
if (daemon->resolv_files && (daemon->options & OPT_NO_POLL))
{
reload_servers(daemon->resolv_files->name);
check_servers();
}
rerun_scripts();
break;
case EVENT_DUMP:
dump_cache(now);
break;
case EVENT_ALARM:
if (daemon->dhcp)
{
lease_prune(NULL, now);
lease_update_file(now);
}
break;
case EVENT_CHILD:
/* See Stevens 5.10 */
while ((p = waitpid(-1, NULL, WNOHANG)) != 0)
if (p == -1)
{
if (errno != EINTR)
break;
}
else
for (i = 0 ; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] == p)
daemon->tcp_pids[i] = 0;
break;
case EVENT_KILLED:
my_syslog(LOG_WARNING, _("child process killed by signal %d"), ev.data);
break;
case EVENT_EXITED:
my_syslog(LOG_WARNING, _("child process exited with status %d"), ev.data);
break;
case EVENT_EXEC_ERR:
my_syslog(LOG_ERR, _("failed to execute %s: %s"), daemon->lease_change_command, strerror(ev.data));
break;
case EVENT_PIPE_ERR:
my_syslog(LOG_ERR, _("failed to create helper: %s"), strerror(ev.data));
break;
case EVENT_REOPEN:
/* Note: this may leave TCP-handling processes with the old file still open.
Since any such process will die in CHILD_LIFETIME or probably much sooner,
we leave them logging to the old file. */
if (daemon->log_file != NULL)
log_reopen(daemon->log_file);
break;
case EVENT_TERM:
/* Knock all our children on the head. */
for (i = 0; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] != 0)
kill(daemon->tcp_pids[i], SIGALRM);
#ifndef NO_FORK
/* handle pending lease transitions */
if (daemon->helperfd != -1)
{
/* block in writes until all done */
if ((i = fcntl(daemon->helperfd, F_GETFL)) != -1)
fcntl(daemon->helperfd, F_SETFL, i & ~O_NONBLOCK);
do {
helper_write();
} while (!helper_buf_empty() || do_script_run(now));
close(daemon->helperfd);
}
#endif
if (daemon->lease_stream)
fclose(daemon->lease_stream);
my_syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
flush_log();
exit(EC_GOOD);
}
}
static void poll_resolv()
{
struct resolvc *res, *latest;
struct stat statbuf;
time_t last_change = 0;
/* There may be more than one possible file.
Go through and find the one which changed _last_.
Warn of any which can't be read. */
for (latest = NULL, res = daemon->resolv_files; res; res = res->next)
if (stat(res->name, &statbuf) == -1)
{
if (!res->logged)
my_syslog(LOG_WARNING, _("failed to access %s: %s"), res->name, strerror(errno));
res->logged = 1;
}
else
{
res->logged = 0;
if (statbuf.st_mtime != res->mtime)
{
res->mtime = statbuf.st_mtime;
if (difftime(statbuf.st_mtime, last_change) > 0.0)
{
last_change = statbuf.st_mtime;
latest = res;
}
}
}
if (latest)
{
static int warned = 0;
if (reload_servers(latest->name))
{
my_syslog(LOG_INFO, _("reading %s"), latest->name);
warned = 0;
check_servers();
if (daemon->options & OPT_RELOAD)
cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts);
}
else
{
latest->mtime = 0;
if (!warned)
{
my_syslog(LOG_WARNING, _("no servers found in %s, will retry"), latest->name);
warned = 1;
}
}
}
}
void clear_cache_and_reload(time_t now)
{ {
cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts); cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts);
if (daemon->dhcp) if (daemon->dhcp)
{ {
if (daemon->options & OPT_ETHERS) if (daemon->options & OPT_ETHERS)
dhcp_read_ethers(daemon); dhcp_read_ethers();
if (daemon->dhcp_hosts_file)
dhcp_read_hosts();
dhcp_update_configs(daemon->dhcp_conf); dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs(daemon); lease_update_from_configs();
lease_update_file(daemon, now); lease_update_file(now);
lease_update_dns(daemon); lease_update_dns();
} }
} }
static int set_dns_listeners(struct daemon *daemon, time_t now, fd_set *set, int *maxfdp) static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
{ {
struct serverfd *serverfdp; struct serverfd *serverfdp;
struct listener *listener; struct listener *listener;
@@ -742,7 +794,7 @@ static int set_dns_listeners(struct daemon *daemon, time_t now, fd_set *set, int
#endif #endif
/* will we be able to get memory? */ /* will we be able to get memory? */
get_new_frec(daemon, now, &wait); get_new_frec(now, &wait);
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
{ {
@@ -782,23 +834,23 @@ static int set_dns_listeners(struct daemon *daemon, time_t now, fd_set *set, int
return wait; return wait;
} }
static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now) static void check_dns_listeners(fd_set *set, time_t now)
{ {
struct serverfd *serverfdp; struct serverfd *serverfdp;
struct listener *listener; struct listener *listener;
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
if (FD_ISSET(serverfdp->fd, set)) if (FD_ISSET(serverfdp->fd, set))
reply_query(serverfdp, daemon, now); reply_query(serverfdp, now);
for (listener = daemon->listeners; listener; listener = listener->next) for (listener = daemon->listeners; listener; listener = listener->next)
{ {
if (FD_ISSET(listener->fd, set)) if (FD_ISSET(listener->fd, set))
receive_query(listener, daemon, now); receive_query(listener, now);
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
if (listener->tftpfd != -1 && FD_ISSET(listener->tftpfd, set)) if (listener->tftpfd != -1 && FD_ISSET(listener->tftpfd, set))
tftp_request(listener, daemon, now); tftp_request(listener, now);
#endif #endif
if (FD_ISSET(listener->tcpfd, set)) if (FD_ISSET(listener->tcpfd, set))
@@ -825,7 +877,7 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
interface too, for localisation. */ interface too, for localisation. */
/* interface may be new since startup */ /* interface may be new since startup */
if (enumerate_interfaces(daemon) && if (enumerate_interfaces() &&
getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1) getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1)
for (iface = daemon->interfaces; iface; iface = iface->next) for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, &tcp_addr)) if (sockaddr_isequal(&iface->addr, &tcp_addr))
@@ -880,7 +932,7 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
if (listener->family == AF_INET) if (listener->family == AF_INET)
dst_addr_4 = iface->addr.in.sin_addr; dst_addr_4 = iface->addr.in.sin_addr;
buff = tcp_request(daemon, confd, now, dst_addr_4, iface->netmask); buff = tcp_request(confd, now, dst_addr_4, iface->netmask);
shutdown(confd, SHUT_RDWR); shutdown(confd, SHUT_RDWR);
close(confd); close(confd);
@@ -896,7 +948,10 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
} }
#ifndef NO_FORK #ifndef NO_FORK
if (!(daemon->options & OPT_DEBUG)) if (!(daemon->options & OPT_DEBUG))
_exit(0); {
flush_log();
_exit(0);
}
#endif #endif
} }
} }
@@ -922,7 +977,7 @@ int make_icmp_sock(void)
return fd; return fd;
} }
int icmp_ping(struct daemon *daemon, struct in_addr addr) int icmp_ping(struct in_addr addr)
{ {
/* Try and get an ICMP echo from a machine. */ /* Try and get an ICMP echo from a machine. */
@@ -986,7 +1041,7 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
FD_ZERO(&rset); FD_ZERO(&rset);
FD_ZERO(&wset); FD_ZERO(&wset);
FD_SET(fd, &rset); FD_SET(fd, &rset);
set_dns_listeners(daemon, now, &rset, &maxfd); set_dns_listeners(now, &rset, &maxfd);
set_log_writer(&wset, &maxfd); set_log_writer(&wset, &maxfd);
if (select(maxfd+1, &rset, &wset, NULL, &tv) < 0) if (select(maxfd+1, &rset, &wset, NULL, &tv) < 0)
@@ -998,10 +1053,10 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
now = dnsmasq_time(); now = dnsmasq_time();
check_log_writer(&wset); check_log_writer(&wset);
check_dns_listeners(daemon, &rset, now); check_dns_listeners(&rset, now);
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
check_tftp_listeners(daemon, &rset, now); check_tftp_listeners(&rset, now);
#endif #endif
if (FD_ISSET(fd, &rset) && if (FD_ISSET(fd, &rset) &&

View File

@@ -82,6 +82,34 @@ extern int capset(cap_user_header_t header, cap_user_data_t data);
#include <sys/prctl.h> #include <sys/prctl.h>
#endif #endif
/* daemon is function in teh C library.... */
#define daemon dnsmasq_daemon
/* Async event queue */
struct event_desc {
int event, data;
};
#define EVENT_RELOAD 1
#define EVENT_DUMP 2
#define EVENT_ALARM 3
#define EVENT_TERM 4
#define EVENT_CHILD 5
#define EVENT_REOPEN 6
#define EVENT_EXITED 7
#define EVENT_KILLED 8
#define EVENT_EXEC_ERR 9
#define EVENT_PIPE_ERR 10
/* Exit codes. */
#define EC_GOOD 0
#define EC_BADCONF 1
#define EC_BADNET 2
#define EC_FILE 3
#define EC_NOMEM 4
#define EC_MISC 5
#define EC_INIT_OFFSET 10
/* Min buffer size: we check after adding each record, so there must be /* Min buffer size: we check after adding each record, so there must be
memory for the largest packet, and the largest record so the memory for the largest packet, and the largest record so the
min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000. min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000.
@@ -117,6 +145,7 @@ extern int capset(cap_user_header_t header, cap_user_data_t data);
#define OPT_TFTP_SECURE (1<<26) #define OPT_TFTP_SECURE (1<<26)
#define OPT_TFTP_NOBLOCK (1<<27) #define OPT_TFTP_NOBLOCK (1<<27)
#define OPT_LOG_OPTS (1<<28) #define OPT_LOG_OPTS (1<<28)
#define OPT_TFTP_APREF (1<<29)
struct all_addr { struct all_addr {
union { union {
@@ -229,15 +258,14 @@ union mysockaddr {
#define SERV_FROM_RESOLV 1 /* 1 for servers from resolv, 0 for command line. */ #define SERV_FROM_RESOLV 1 /* 1 for servers from resolv, 0 for command line. */
#define SERV_NO_ADDR 2 /* no server, this domain is local only */ #define SERV_NO_ADDR 2 /* no server, this domain is local only */
#define SERV_LITERAL_ADDRESS 4 /* addr is the answer, not the server */ #define SERV_LITERAL_ADDRESS 4 /* addr is the answer, not the server */
#define SERV_HAS_SOURCE 8 /* source address specified */ #define SERV_HAS_DOMAIN 8 /* server for one domain only */
#define SERV_HAS_DOMAIN 16 /* server for one domain only */ #define SERV_HAS_SOURCE 16 /* source address defined */
#define SERV_FOR_NODOTS 32 /* server for names with no domain part only */ #define SERV_FOR_NODOTS 32 /* server for names with no domain part only */
#define SERV_WARNED_RECURSIVE 64 /* avoid warning spam */ #define SERV_WARNED_RECURSIVE 64 /* avoid warning spam */
#define SERV_FROM_DBUS 128 /* 1 if source is DBus */ #define SERV_FROM_DBUS 128 /* 1 if source is DBus */
#define SERV_MARK 256 /* for mark-and-delete */ #define SERV_MARK 256 /* for mark-and-delete */
#define SERV_TYPE (SERV_HAS_DOMAIN | SERV_FOR_NODOTS) #define SERV_TYPE (SERV_HAS_DOMAIN | SERV_FOR_NODOTS)
struct serverfd { struct serverfd {
int fd; int fd;
union mysockaddr source_addr; union mysockaddr source_addr;
@@ -364,6 +392,7 @@ struct dhcp_config {
#define CONFIG_FROM_ETHERS 256 /* entry created by /etc/ethers */ #define CONFIG_FROM_ETHERS 256 /* entry created by /etc/ethers */
#define CONFIG_ADDR_HOSTS 512 /* address added by from /etc/hosts */ #define CONFIG_ADDR_HOSTS 512 /* address added by from /etc/hosts */
#define CONFIG_DECLINED 1024 /* address declined by client */ #define CONFIG_DECLINED 1024 /* address declined by client */
#define CONFIG_BANK 2048 /* from dhcp hosts file */
struct dhcp_opt { struct dhcp_opt {
int opt, len, flags; int opt, len, flags;
@@ -452,6 +481,8 @@ struct ping_result {
struct tftp_file { struct tftp_file {
int refcount, fd; int refcount, fd;
off_t size; off_t size;
dev_t dev;
ino_t inode;
char filename[]; char filename[];
}; };
@@ -466,7 +497,7 @@ struct tftp_transfer {
struct tftp_transfer *next; struct tftp_transfer *next;
}; };
struct daemon { extern struct daemon {
/* datastuctures representing the command-line and /* datastuctures representing the command-line and
config file arguments. All set (including defaults) config file arguments. All set (including defaults)
in option.c */ in option.c */
@@ -500,6 +531,7 @@ struct daemon {
struct dhcp_mac *dhcp_macs; struct dhcp_mac *dhcp_macs;
struct dhcp_boot *boot_config; struct dhcp_boot *boot_config;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names; struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names;
char *dhcp_hosts_file;
int dhcp_max, tftp_max; int dhcp_max, tftp_max;
unsigned int min_leasetime; unsigned int min_leasetime;
struct doctor *doctors; struct doctor *doctors;
@@ -542,10 +574,10 @@ struct daemon {
/* TFTP stuff */ /* TFTP stuff */
struct tftp_transfer *tftp_trans; struct tftp_transfer *tftp_trans;
char *tftp_prefix; char *tftp_prefix;
}; } *daemon;
/* cache.c */ /* cache.c */
void cache_init(int cachesize, int log); void cache_init(void);
void log_query(unsigned short flags, char *name, struct all_addr *addr, void log_query(unsigned short flags, char *name, struct all_addr *addr,
unsigned short type, struct hostsfile *addn_hosts, int index); unsigned short type, struct hostsfile *addn_hosts, int index);
struct crec *cache_find_by_addr(struct crec *crecp, struct crec *cache_find_by_addr(struct crec *crecp,
@@ -558,9 +590,9 @@ void cache_start_insert(void);
struct crec *cache_insert(char *name, struct all_addr *addr, struct crec *cache_insert(char *name, struct all_addr *addr,
time_t now, unsigned long ttl, unsigned short flags); time_t now, unsigned long ttl, unsigned short flags);
void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *addn_hosts); void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *addn_hosts);
void cache_add_dhcp_entry(struct daemon *daemon, char *host_name, struct in_addr *host_address, time_t ttd); void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t ttd);
void cache_unhash_dhcp(void); void cache_unhash_dhcp(void);
void dump_cache(struct daemon *daemon, time_t now); void dump_cache(time_t now);
char *cache_get_name(struct crec *crecp); char *cache_get_name(struct crec *crecp);
/* rfc1035.c */ /* rfc1035.c */
@@ -569,15 +601,14 @@ unsigned short extract_request(HEADER *header, size_t qlen,
size_t setup_reply(HEADER *header, size_t qlen, size_t setup_reply(HEADER *header, size_t qlen,
struct all_addr *addrp, unsigned short flags, struct all_addr *addrp, unsigned short flags,
unsigned long local_ttl); unsigned long local_ttl);
void extract_addresses(HEADER *header, size_t qlen, char *namebuff, void extract_addresses(HEADER *header, size_t qlen, char *namebuff, time_t now);
time_t now, struct daemon *daemon); size_t answer_request(HEADER *header, char *limit, size_t qlen,
size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *daemon,
struct in_addr local_addr, struct in_addr local_netmask, time_t now); struct in_addr local_addr, struct in_addr local_netmask, time_t now);
int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name, int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
struct bogus_addr *addr, time_t now); struct bogus_addr *addr, time_t now);
unsigned char *find_pseudoheader(HEADER *header, size_t plen, unsigned char *find_pseudoheader(HEADER *header, size_t plen,
size_t *len, unsigned char **p, int *is_sign); size_t *len, unsigned char **p, int *is_sign);
int check_for_local_domain(char *name, time_t now, struct daemon *daemon); int check_for_local_domain(char *name, time_t now);
unsigned int questions_crc(HEADER *header, size_t plen, char *buff); unsigned int questions_crc(HEADER *header, size_t plen, char *buff);
size_t resize_packet(HEADER *header, size_t plen, size_t resize_packet(HEADER *header, size_t plen,
unsigned char *pheader, size_t hlen); unsigned char *pheader, size_t hlen);
@@ -588,6 +619,7 @@ int legal_char(char c);
int canonicalise(char *s); int canonicalise(char *s);
unsigned char *do_rfc1035_name(unsigned char *p, char *sval); unsigned char *do_rfc1035_name(unsigned char *p, char *sval);
void *safe_malloc(size_t size); void *safe_malloc(size_t size);
void *whine_malloc(size_t size);
int sa_len(union mysockaddr *addr); int sa_len(union mysockaddr *addr);
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2); int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
int hostname_isequal(char *a, char *b); int hostname_isequal(char *a, char *b);
@@ -601,49 +633,52 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
int memcmp_masked(unsigned char *a, unsigned char *b, int len, int memcmp_masked(unsigned char *a, unsigned char *b, int len,
unsigned int mask); unsigned int mask);
int expand_buf(struct iovec *iov, size_t size); int expand_buf(struct iovec *iov, size_t size);
char *print_mac(struct daemon *daemon, unsigned char *mac, int len); char *print_mac(char *buff, unsigned char *mac, int len);
void bump_maxfd(int fd, int *max); void bump_maxfd(int fd, int *max);
int read_write(int fd, unsigned char *packet, int size, int rw); int read_write(int fd, unsigned char *packet, int size, int rw);
/* log.c */ /* log.c */
void die(char *message, char *arg1); void die(char *message, char *arg1, int exit_code);
int log_start(struct daemon *daemon); void log_start(struct passwd *ent_pw);
int log_reopen(char *log_file);
void my_syslog(int priority, const char *format, ...); void my_syslog(int priority, const char *format, ...);
void set_log_writer(fd_set *set, int *maxfdp); void set_log_writer(fd_set *set, int *maxfdp);
void check_log_writer(fd_set *set); void check_log_writer(fd_set *set);
void flush_log(void);
/* option.c */ /* option.c */
struct daemon *read_opts (int argc, char **argv, char *compile_opts); void read_opts (int argc, char **argv, char *compile_opts);
char *option_string(unsigned char opt); char *option_string(unsigned char opt);
void one_file(char *file, int nest, int hosts);
/* forward.c */ /* forward.c */
void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now); void reply_query(struct serverfd *sfd, time_t now);
void receive_query(struct listener *listen, struct daemon *daemon, time_t now); void receive_query(struct listener *listen, time_t now);
unsigned char *tcp_request(struct daemon *daemon, int confd, time_t now, unsigned char *tcp_request(int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask); struct in_addr local_addr, struct in_addr netmask);
void server_gone(struct daemon *daemon, struct server *server); void server_gone(struct server *server);
struct frec *get_new_frec(struct daemon *daemon, time_t now, int *wait); struct frec *get_new_frec(time_t now, int *wait);
/* network.c */ /* network.c */
struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds); struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds);
int reload_servers(char *fname, struct daemon *daemon); int reload_servers(char *fname);
void check_servers(struct daemon *daemon); void check_servers(void);
int enumerate_interfaces(struct daemon *daemon); int enumerate_interfaces();
struct listener *create_wildcard_listeners(int port, int have_tftp); struct listener *create_wildcard_listeners(void);
struct listener *create_bound_listeners(struct daemon *daemon); struct listener *create_bound_listeners(void);
int iface_check(struct daemon *daemon, int family, struct all_addr *addr, int iface_check(int family, struct all_addr *addr,
struct ifreq *ifr, int *indexp); struct ifreq *ifr, int *indexp);
int fix_fd(int fd); int fix_fd(int fd);
struct in_addr get_ifaddr(struct daemon* daemon, char *intr); struct in_addr get_ifaddr(char *intr);
/* dhcp.c */ /* dhcp.c */
void dhcp_init(struct daemon *daemon); void dhcp_init(void);
void dhcp_packet(struct daemon *daemon, time_t now); void dhcp_packet(time_t now);
struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr addr); struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr addr);
struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr); struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr);
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly); int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly);
int address_allocate(struct dhcp_context *context, struct daemon *daemon, int address_allocate(struct dhcp_context *context,
struct in_addr *addrp, unsigned char *hwaddr, int hw_len, struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
struct dhcp_netid *netids, time_t now); struct dhcp_netid *netids, time_t now);
struct dhcp_config *find_config(struct dhcp_config *configs, struct dhcp_config *find_config(struct dhcp_config *configs,
@@ -652,15 +687,16 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
unsigned char *hwaddr, int hw_len, unsigned char *hwaddr, int hw_len,
int hw_type, char *hostname); int hw_type, char *hostname);
void dhcp_update_configs(struct dhcp_config *configs); void dhcp_update_configs(struct dhcp_config *configs);
void dhcp_read_ethers(struct daemon *daemon); void dhcp_read_ethers(void);
void dhcp_read_hosts(void);
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr); struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr);
char *strip_hostname(struct daemon *daemon, char *hostname); char *strip_hostname(char *hostname);
char *host_from_dns(struct daemon *daemon, struct in_addr addr); char *host_from_dns(struct in_addr addr);
/* lease.c */ /* lease.c */
void lease_update_file(struct daemon *daemon, time_t now); void lease_update_file(time_t now);
void lease_update_dns(struct daemon *daemon); void lease_update_dns();
void lease_init(struct daemon *daemon, time_t now); void lease_init(time_t now);
struct dhcp_lease *lease_allocate(struct in_addr addr); struct dhcp_lease *lease_allocate(struct in_addr addr);
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr, void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
unsigned char *clid, int hw_len, int hw_type, int clid_len); unsigned char *clid, int hw_len, int hw_type, int clid_len);
@@ -671,57 +707,58 @@ struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int h
unsigned char *clid, int clid_len); unsigned char *clid, int clid_len);
struct dhcp_lease *lease_find_by_addr(struct in_addr addr); struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
void lease_prune(struct dhcp_lease *target, time_t now); void lease_prune(struct dhcp_lease *target, time_t now);
void lease_update_from_configs(struct daemon *daemon); void lease_update_from_configs(void);
int do_script_run(struct daemon *daemon); int do_script_run(time_t now);
void rerun_scripts(void);
/* rfc2131.c */ /* rfc2131.c */
size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *iface_name, size_t sz, time_t now, int unicast_dest); size_t dhcp_reply(struct dhcp_context *context, char *iface_name,
size_t sz, time_t now, int unicast_dest, int *is_inform);
/* dnsmasq.c */ /* dnsmasq.c */
int make_icmp_sock(void); int make_icmp_sock(void);
int icmp_ping(struct daemon *daemon, struct in_addr addr); int icmp_ping(struct in_addr addr);
void clear_cache_and_reload(struct daemon *daemon, time_t now); void send_event(int fd, int event, int data);
void clear_cache_and_reload(time_t now);
/* isc.c */ /* isc.c */
#ifdef HAVE_ISC_READER #ifdef HAVE_ISC_READER
void load_dhcp(struct daemon *daemon, time_t now); void load_dhcp(time_t now);
#endif #endif
/* netlink.c */ /* netlink.c */
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_LINUX_NETWORK
void netlink_init(struct daemon *daemon); void netlink_init(void);
int iface_enumerate(struct daemon *daemon, void *parm, int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)());
int (*ipv4_callback)(), int (*ipv6_callback)()); void netlink_multicast(void);
void netlink_multicast(struct daemon *daemon);
#endif #endif
/* bpf.c */ /* bpf.c */
#ifndef HAVE_LINUX_NETWORK #ifndef HAVE_LINUX_NETWORK
void init_bpf(struct daemon *daemon); void init_bpf(void);
void send_via_bpf(struct daemon *daemon, struct dhcp_packet *mess, size_t len, void send_via_bpf(struct dhcp_packet *mess, size_t len,
struct in_addr iface_addr, struct ifreq *ifr); struct in_addr iface_addr, struct ifreq *ifr);
int iface_enumerate(struct daemon *daemon, void *parm, int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)());
int (*ipv4_callback)(), int (*ipv6_callback)());
#endif #endif
/* dbus.c */ /* dbus.c */
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
char *dbus_init(struct daemon *daemon); char *dbus_init(void);
void check_dbus_listeners(struct daemon *daemon, void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset);
fd_set *rset, fd_set *wset, fd_set *eset); void set_dbus_listeners(int *maxfdp, fd_set *rset, fd_set *wset, fd_set *eset);
void set_dbus_listeners(struct daemon *daemon, int *maxfdp,
fd_set *rset, fd_set *wset, fd_set *eset);
#endif #endif
/* helper.c */ /* helper.c */
int create_helper(struct daemon *daemon, int log_fd); #ifndef NO_FORK
void helper_write(struct daemon *daemon); int create_helper(int log_fd, long max_fd);
void queue_script(struct daemon *daemon, int action, void helper_write(void);
struct dhcp_lease *lease, char *hostname); void queue_script(int action, struct dhcp_lease *lease,
char *hostname, time_t now);
int helper_buf_empty(void); int helper_buf_empty(void);
#endif
/* tftp.c */ /* tftp.c */
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
void tftp_request(struct listener *listen, struct daemon *daemon, time_t now); void tftp_request(struct listener *listen, time_t now);
void check_tftp_listeners(struct daemon *daemon, fd_set *rset, time_t now); void check_tftp_listeners(fd_set *rset, time_t now);
#endif #endif

View File

@@ -107,7 +107,7 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
} }
} }
static unsigned short search_servers(struct daemon *daemon, time_t now, struct all_addr **addrpp, static unsigned short search_servers(time_t now, struct all_addr **addrpp,
unsigned short qtype, char *qdomain, int *type, char **domain) unsigned short qtype, char *qdomain, int *type, char **domain)
{ {
@@ -189,7 +189,7 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
/* don't forward simple names, make exception from NS queries and empty name. */ /* don't forward simple names, make exception from NS queries and empty name. */
flags = F_NXDOMAIN; flags = F_NXDOMAIN;
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon)) if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now))
flags = F_NOERR; flags = F_NOERR;
if (flags == F_NXDOMAIN || flags == F_NOERR) if (flags == F_NXDOMAIN || flags == F_NOERR)
@@ -199,7 +199,7 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
} }
/* returns new last_server */ /* returns new last_server */
static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *udpaddr, static void forward_query(int udpfd, union mysockaddr *udpaddr,
struct all_addr *dst_addr, unsigned int dst_iface, struct all_addr *dst_addr, unsigned int dst_iface,
HEADER *header, size_t plen, time_t now, struct frec *forward) HEADER *header, size_t plen, time_t now, struct frec *forward)
{ {
@@ -231,9 +231,9 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
else else
{ {
if (gotname) if (gotname)
flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain); flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
if (!flags && !(forward = get_new_frec(daemon, now, NULL))) if (!flags && !(forward = get_new_frec(now, NULL)))
/* table full - server failure. */ /* table full - server failure. */
flags = F_NEG; flags = F_NEG;
@@ -344,7 +344,7 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
return; return;
} }
static size_t process_reply(struct daemon *daemon, HEADER *header, time_t now, static size_t process_reply(HEADER *header, time_t now,
struct server *server, size_t n) struct server *server, size_t n)
{ {
unsigned char *pheader, *sizep; unsigned char *pheader, *sizep;
@@ -389,7 +389,7 @@ static size_t process_reply(struct daemon *daemon, HEADER *header, time_t now,
{ {
if (header->rcode == NXDOMAIN && if (header->rcode == NXDOMAIN &&
extract_request(header, n, daemon->namebuff, NULL) && extract_request(header, n, daemon->namebuff, NULL) &&
check_for_local_domain(daemon->namebuff, now, daemon)) check_for_local_domain(daemon->namebuff, now))
{ {
/* if we forwarded a query for a locally known name (because it was for /* if we forwarded a query for a locally known name (because it was for
an unknown type) and the answer is NXDOMAIN, convert that to NODATA, an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
@@ -399,7 +399,7 @@ static size_t process_reply(struct daemon *daemon, HEADER *header, time_t now,
header->rcode = NOERROR; header->rcode = NOERROR;
} }
extract_addresses(header, n, daemon->namebuff, now, daemon); extract_addresses(header, n, daemon->namebuff, now);
} }
/* do this after extract_addresses. Ensure NODATA reply and remove /* do this after extract_addresses. Ensure NODATA reply and remove
@@ -419,7 +419,7 @@ static size_t process_reply(struct daemon *daemon, HEADER *header, time_t now,
} }
/* sets new last_server */ /* sets new last_server */
void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now) void reply_query(struct serverfd *sfd, time_t now)
{ {
/* packet from peer server, extract data for cache, and send to /* packet from peer server, extract data for cache, and send to
original requester */ original requester */
@@ -467,7 +467,7 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
{ {
header->qr = 0; header->qr = 0;
header->tc = 0; header->tc = 0;
forward_query(daemon, -1, NULL, NULL, 0, header, nn, now, forward); forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
return; return;
} }
} }
@@ -499,7 +499,7 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
if (forward->forwardall == 0 || --forward->forwardall == 1 || if (forward->forwardall == 0 || --forward->forwardall == 1 ||
(header->rcode != REFUSED && header->rcode != SERVFAIL)) (header->rcode != REFUSED && header->rcode != SERVFAIL))
{ {
if ((nn = process_reply(daemon, header, now, server, (size_t)n))) if ((nn = process_reply(header, now, server, (size_t)n)))
{ {
header->id = htons(forward->orig_id); header->id = htons(forward->orig_id);
header->ra = 1; /* recursion if available */ header->ra = 1; /* recursion if available */
@@ -511,7 +511,7 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
} }
} }
void receive_query(struct listener *listen, struct daemon *daemon, time_t now) void receive_query(struct listener *listen, time_t now)
{ {
HEADER *header = (HEADER *)daemon->packet; HEADER *header = (HEADER *)daemon->packet;
union mysockaddr source_addr; union mysockaddr source_addr;
@@ -628,7 +628,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
return; return;
#endif #endif
if (!iface_check(daemon, listen->family, &dst_addr, &ifr, &if_index)) if (!iface_check(listen->family, &dst_addr, &ifr, &if_index))
return; return;
if (listen->family == AF_INET && if (listen->family == AF_INET &&
@@ -651,12 +651,12 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
#endif #endif
} }
m = answer_request (header, ((char *) header) + PACKETSZ, (size_t)n, daemon, m = answer_request (header, ((char *) header) + PACKETSZ, (size_t)n,
dst_addr_4, netmask, now); dst_addr_4, netmask, now);
if (m >= 1) if (m >= 1)
send_from(listen->fd, daemon->options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr, if_index); send_from(listen->fd, daemon->options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr, if_index);
else else
forward_query(daemon, listen->fd, &source_addr, &dst_addr, if_index, forward_query(listen->fd, &source_addr, &dst_addr, if_index,
header, (size_t)n, now, NULL); header, (size_t)n, now, NULL);
} }
@@ -664,7 +664,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
blocking as neccessary, and then return. Note, need to be a bit careful blocking as neccessary, and then return. Note, need to be a bit careful
about resources for debug mode, when the fork is suppressed: that's about resources for debug mode, when the fork is suppressed: that's
done by the caller. */ done by the caller. */
unsigned char *tcp_request(struct daemon *daemon, int confd, time_t now, unsigned char *tcp_request(int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask) struct in_addr local_addr, struct in_addr netmask)
{ {
int size = 0; int size = 0;
@@ -672,7 +672,7 @@ unsigned char *tcp_request(struct daemon *daemon, int confd, time_t now,
unsigned short qtype, gotname; unsigned short qtype, gotname;
unsigned char c1, c2; unsigned char c1, c2;
/* Max TCP packet + slop */ /* Max TCP packet + slop */
unsigned char *packet = malloc(65536 + MAXDNAME + RRFIXEDSZ); unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
HEADER *header; HEADER *header;
struct server *last_server; struct server *last_server;
@@ -708,8 +708,11 @@ unsigned char *tcp_request(struct daemon *daemon, int confd, time_t now,
} }
/* m > 0 if answered from cache */ /* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, daemon, m = answer_request(header, ((char *) header) + 65536, (unsigned int)size,
local_addr, netmask, now); local_addr, netmask, now);
/* Do this by steam now we're not in the select() loop */
check_log_writer(NULL);
if (m == 0) if (m == 0)
{ {
@@ -719,7 +722,7 @@ unsigned char *tcp_request(struct daemon *daemon, int confd, time_t now,
char *domain = NULL; char *domain = NULL;
if (gotname) if (gotname)
flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain); flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server) if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server)
last_server = daemon->servers; last_server = daemon->servers;
@@ -799,7 +802,7 @@ unsigned char *tcp_request(struct daemon *daemon, int confd, time_t now,
someone might be attempting to insert bogus values into the cache by someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */ sending replies containing questions and bogus answers. */
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff)) if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
m = process_reply(daemon, header, now, last_server, (unsigned int)m); m = process_reply(header, now, last_server, (unsigned int)m);
break; break;
} }
@@ -809,6 +812,8 @@ unsigned char *tcp_request(struct daemon *daemon, int confd, time_t now,
if (m == 0) if (m == 0)
m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl); m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
} }
check_log_writer(NULL);
c1 = m>>8; c1 = m>>8;
c2 = m; c2 = m;
@@ -823,7 +828,7 @@ static struct frec *allocate_frec(time_t now)
{ {
struct frec *f; struct frec *f;
if ((f = (struct frec *)malloc(sizeof(struct frec)))) if ((f = (struct frec *)whine_malloc(sizeof(struct frec))))
{ {
f->next = frec_list; f->next = frec_list;
f->time = now; f->time = now;
@@ -837,7 +842,7 @@ static struct frec *allocate_frec(time_t now)
/* if wait==NULL return a free or older than TIMEOUT record. /* if wait==NULL return a free or older than TIMEOUT record.
else return *wait zero if one available, or *wait is delay to else return *wait zero if one available, or *wait is delay to
when the oldest in-use record will expire. */ when the oldest in-use record will expire. */
struct frec *get_new_frec(struct daemon *daemon, time_t now, int *wait) struct frec *get_new_frec(time_t now, int *wait)
{ {
struct frec *f, *oldest; struct frec *f, *oldest;
int count; int count;
@@ -918,7 +923,7 @@ static struct frec *lookup_frec_by_sender(unsigned short id,
} }
/* A server record is going away, remove references to it */ /* A server record is going away, remove references to it */
void server_gone(struct daemon *daemon, struct server *server) void server_gone(struct server *server)
{ {
struct frec *f; struct frec *f;

View File

@@ -24,11 +24,14 @@
main process. main process.
*/ */
#ifndef NO_FORK
struct script_data struct script_data
{ {
unsigned char action, hwaddr_len, hwaddr_type; unsigned char action, hwaddr_len, hwaddr_type;
unsigned char clid_len, hostname_len, uclass_len, vclass_len; unsigned char clid_len, hostname_len, uclass_len, vclass_len;
struct in_addr addr; struct in_addr addr;
unsigned int remaining_time;
#ifdef HAVE_BROKEN_RTC #ifdef HAVE_BROKEN_RTC
unsigned int length; unsigned int length;
#else #else
@@ -37,48 +40,47 @@ struct script_data
unsigned char hwaddr[DHCP_CHADDR_MAX]; unsigned char hwaddr[DHCP_CHADDR_MAX];
}; };
static struct script_data *buf; static struct script_data *buf = NULL;
static size_t bytes_in_buf, buf_size; static size_t bytes_in_buf = 0, buf_size = 0;
int create_helper(struct daemon *daemon, int log_fd) int create_helper(int event_fd, long max_fd)
{ {
pid_t pid; pid_t pid;
int i, pipefd[2]; int i, pipefd[2];
struct sigaction sigact; struct sigaction sigact;
buf = NULL;
buf_size = bytes_in_buf = 0;
if (!daemon->dhcp || !daemon->lease_change_command) if (!daemon->dhcp || !daemon->lease_change_command)
return -1; return -1;
/* create the pipe through which the main program sends us commands,
then fork our process. */
if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
return -1;
/* create the pipe through which the main program sends us commands,
then fork our process. By now it's too late to die(), we just log
any failure via the main process. */
if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
{
send_event(event_fd, EVENT_PIPE_ERR, errno);
return -1;
}
if (pid != 0) if (pid != 0)
{ {
close(pipefd[0]); /* close reader side */ close(pipefd[0]); /* close reader side */
return pipefd[1]; return pipefd[1];
} }
/* ignore SIGTERM, so that we can clean up when the main process gets hit */ /* ignore SIGTERM, so that we can clean up when the main process gets hit
and SIGALRM so that we can use sleep() */
sigact.sa_handler = SIG_IGN; sigact.sa_handler = SIG_IGN;
sigact.sa_flags = 0; sigact.sa_flags = 0;
sigemptyset(&sigact.sa_mask); sigemptyset(&sigact.sa_mask);
sigaction(SIGTERM, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGALRM, &sigact, NULL);
/* close all the sockets etc, we don't need them here */ /* close all the sockets etc, we don't need them here */
for (i = 0; i < 64; i++) for (max_fd--; max_fd > 0; max_fd--)
if (i != STDOUT_FILENO && i != STDERR_FILENO && if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO &&
i != STDIN_FILENO && i != pipefd[0] && i != log_fd) max_fd != STDIN_FILENO && max_fd != pipefd[0] && max_fd != event_fd)
close(i); close(max_fd);
/* don't give our end of the pipe to our children */
if ((i = fcntl(pipefd[0], F_GETFD)) != -1)
fcntl(pipefd[0], F_SETFD, i | FD_CLOEXEC);
/* loop here */ /* loop here */
while(1) while(1)
{ {
@@ -130,18 +132,36 @@ int create_helper(struct daemon *daemon, int log_fd)
if (!read_write(pipefd[0], buf, data.hostname_len + data.uclass_len + data.vclass_len, 1)) if (!read_write(pipefd[0], buf, data.hostname_len + data.uclass_len + data.vclass_len, 1))
continue; continue;
if ((pid = fork()) == -1) /* possible fork errors are all temporary resource problems */
continue; while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
sleep(2);
if (pid == -1)
continue;
/* wait for child to complete */ /* wait for child to complete */
if (pid != 0) if (pid != 0)
{ {
int status; /* reap our children's children, if necessary */
waitpid(pid, &status, 0); while (1)
if (WIFSIGNALED(status)) {
my_syslog(LOG_WARNING, _("child process killed by signal %d"), WTERMSIG(status)); int status;
else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) pid_t rc = wait(&status);
my_syslog(LOG_WARNING, _("child process exited with status %d"), WEXITSTATUS(status));
if (rc == pid)
{
/* On error send event back to main process for logging */
if (WIFSIGNALED(status))
send_event(event_fd, EVENT_KILLED, WTERMSIG(status));
else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status));
break;
}
if (rc == -1 && errno != EINTR)
break;
}
continue; continue;
} }
@@ -189,11 +209,15 @@ int create_helper(struct daemon *daemon, int log_fd)
} }
} }
sprintf(daemon->dhcp_buff2, "%u ", data.remaining_time);
setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, 1);
if (data.hostname_len != 0) if (data.hostname_len != 0)
{ {
hostname = (char *)buf; hostname = (char *)buf;
hostname[data.hostname_len - 1] = 0; hostname[data.hostname_len - 1] = 0;
canonicalise(hostname); if (!canonicalise(hostname))
hostname = NULL;
} }
if (data.action == ACTION_OLD_HOSTNAME && hostname) if (data.action == ACTION_OLD_HOSTNAME && hostname)
@@ -203,25 +227,29 @@ int create_helper(struct daemon *daemon, int log_fd)
} }
else else
unsetenv("DNSMASQ_OLD_HOSTNAME"); unsetenv("DNSMASQ_OLD_HOSTNAME");
/* we need to have the event_fd around if exec fails */
if ((i = fcntl(event_fd, F_GETFD)) != -1)
fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
close(pipefd[0]);
p = strrchr(daemon->lease_change_command, '/'); p = strrchr(daemon->lease_change_command, '/');
execl(daemon->lease_change_command, execl(daemon->lease_change_command,
p ? p+1 : daemon->lease_change_command, p ? p+1 : daemon->lease_change_command,
action_str, daemon->dhcp_buff, inet_ntoa(data.addr), hostname, (char*)NULL); action_str, daemon->dhcp_buff, inet_ntoa(data.addr), hostname, (char*)NULL);
/* log socket should still be open, right? */ /* failed, send event so the main process logs the problem */
my_syslog(LOG_ERR, _("failed to execute %s: %s"), send_event(event_fd, EVENT_EXEC_ERR, errno);
daemon->lease_change_command, strerror(errno));
_exit(0); _exit(0);
} }
} }
/* pack up lease data into a buffer */ /* pack up lease data into a buffer */
void queue_script(struct daemon *daemon, int action, struct dhcp_lease *lease, char *hostname) void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
{ {
unsigned char *p; unsigned char *p;
size_t size; size_t size;
unsigned int hostname_len = 0, clid_len = 0, vclass_len = 0, uclass_len = 0; unsigned int i, hostname_len = 0, clid_len = 0, vclass_len = 0, uclass_len = 0;
/* no script */ /* no script */
if (daemon->helperfd == -1) if (daemon->helperfd == -1)
@@ -246,7 +274,7 @@ void queue_script(struct daemon *daemon, int action, struct dhcp_lease *lease, c
if (size < sizeof(struct script_data) + 200) if (size < sizeof(struct script_data) + 200)
size = sizeof(struct script_data) + 200; size = sizeof(struct script_data) + 200;
if (!(new = malloc(size))) if (!(new = whine_malloc(size)))
return; return;
if (buf) if (buf)
free(buf); free(buf);
@@ -268,29 +296,31 @@ void queue_script(struct daemon *daemon, int action, struct dhcp_lease *lease, c
#else #else
buf->expires = lease->expires; buf->expires = lease->expires;
#endif #endif
buf->remaining_time = (unsigned int)difftime(lease->expires, now);
p = (unsigned char *)(buf+1); p = (unsigned char *)(buf+1);
if (buf->clid_len != 0) if (clid_len != 0)
{ {
memcpy(p, lease->clid, clid_len); memcpy(p, lease->clid, clid_len);
p += clid_len; p += clid_len;
} }
if (buf->vclass_len != 0) if (vclass_len != 0)
{ {
memcpy(p, lease->vendorclass, vclass_len); memcpy(p, lease->vendorclass, vclass_len);
p += vclass_len; p += vclass_len;
} }
if (buf->uclass_len != 0) if (uclass_len != 0)
{ {
memcpy(p, lease->userclass, uclass_len); memcpy(p, lease->userclass, uclass_len);
p += uclass_len; p += uclass_len;
} }
if (buf->hostname_len != 0) /* substitute * for space */
{ for (i = 0; i < hostname_len; i++)
memcpy(p, hostname, hostname_len); if ((daemon->options & OPT_LEASE_RO) && hostname[i] == ' ')
p += hostname_len; *(p++) = '*';
} else
*(p++) = hostname[i];
bytes_in_buf = p - (unsigned char *)buf; bytes_in_buf = p - (unsigned char *)buf;
} }
@@ -299,7 +329,7 @@ int helper_buf_empty(void)
return bytes_in_buf == 0; return bytes_in_buf == 0;
} }
void helper_write(struct daemon *daemon) void helper_write(void)
{ {
ssize_t rc; ssize_t rc;
@@ -320,5 +350,6 @@ void helper_write(struct daemon *daemon)
} }
} }
#endif

View File

@@ -57,7 +57,7 @@ static int next_token (char *token, int buffsize, FILE * fp)
return count ? 1 : 0; return count ? 1 : 0;
} }
void load_dhcp(struct daemon *daemon, time_t now) void load_dhcp(time_t now)
{ {
char *hostname = daemon->namebuff; char *hostname = daemon->namebuff;
char token[MAXTOK], *dot; char token[MAXTOK], *dot;
@@ -189,20 +189,20 @@ void load_dhcp(struct daemon *daemon, time_t now)
break; break;
} }
if (!lease && (lease = malloc(sizeof(struct isc_lease)))) if (!lease && (lease = whine_malloc(sizeof(struct isc_lease))))
{ {
lease->expires = ttd; lease->expires = ttd;
lease->addr = host_address; lease->addr = host_address;
lease->fqdn = NULL; lease->fqdn = NULL;
lease->next = leases; lease->next = leases;
if (!(lease->name = malloc(strlen(hostname)+1))) if (!(lease->name = whine_malloc(strlen(hostname)+1)))
free(lease); free(lease);
else else
{ {
leases = lease; leases = lease;
strcpy(lease->name, hostname); strcpy(lease->name, hostname);
if (daemon->domain_suffix && if (daemon->domain_suffix &&
(lease->fqdn = malloc(strlen(hostname) + strlen(daemon->domain_suffix) + 2))) (lease->fqdn = whine_malloc(strlen(hostname) + strlen(daemon->domain_suffix) + 2)))
{ {
strcpy(lease->fqdn, hostname); strcpy(lease->fqdn, hostname);
strcat(lease->fqdn, "."); strcat(lease->fqdn, ".");
@@ -239,8 +239,8 @@ void load_dhcp(struct daemon *daemon, time_t now)
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
{ {
cache_add_dhcp_entry(daemon, lease->fqdn, &lease->addr, lease->expires); cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
cache_add_dhcp_entry(daemon, lease->name, &lease->addr, lease->expires); cache_add_dhcp_entry(lease->name, &lease->addr, lease->expires);
} }
} }

View File

@@ -12,18 +12,22 @@
#include "dnsmasq.h" #include "dnsmasq.h"
static struct dhcp_lease *leases, *old_leases; static struct dhcp_lease *leases = NULL, *old_leases = NULL;
static int dns_dirty, file_dirty, leases_left; static int dns_dirty, file_dirty, leases_left;
void lease_init(struct daemon *daemon, time_t now) void lease_init(time_t now)
{ {
unsigned long ei; unsigned long ei;
struct in_addr addr; struct in_addr addr;
struct dhcp_lease *lease; struct dhcp_lease *lease;
int flags, clid_len, hw_len, hw_type; int clid_len, hw_len, hw_type;
FILE *leasestream; FILE *leasestream;
leases = old_leases = NULL; /* These two each hold a DHCP option max size 255
and get a terminating zero added */
daemon->dhcp_buff = safe_malloc(256);
daemon->dhcp_buff2 = safe_malloc(256);
leases_left = daemon->dhcp_max; leases_left = daemon->dhcp_max;
if (daemon->options & OPT_LEASE_RO) if (daemon->options & OPT_LEASE_RO)
@@ -47,11 +51,7 @@ void lease_init(struct daemon *daemon, time_t now)
leasestream = daemon->lease_stream = fopen(daemon->lease_file, "a+"); leasestream = daemon->lease_stream = fopen(daemon->lease_file, "a+");
if (!leasestream) if (!leasestream)
die(_("cannot open or create lease file %s: %s"), daemon->lease_file); die(_("cannot open or create lease file %s: %s"), daemon->lease_file, EC_FILE);
flags = fcntl(fileno(leasestream), F_GETFD);
if (flags != -1)
fcntl(fileno(leasestream), F_SETFD, flags | FD_CLOEXEC);
/* a+ mode lease pointer at end. */ /* a+ mode lease pointer at end. */
rewind(leasestream); rewind(leasestream);
@@ -77,10 +77,8 @@ void lease_init(struct daemon *daemon, time_t now)
clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL); clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
if (!(lease = lease_allocate(addr))) if (!(lease = lease_allocate(addr)))
die (_("too many stored leases"), NULL); die (_("too many stored leases"), NULL, EC_MISC);
/* not actually new */
lease->new = 0;
#ifdef HAVE_BROKEN_RTC #ifdef HAVE_BROKEN_RTC
if (ei != 0) if (ei != 0)
lease->expires = (time_t)ei + now; lease->expires = (time_t)ei + now;
@@ -96,7 +94,17 @@ void lease_init(struct daemon *daemon, time_t now)
lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len); lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len);
if (strcmp(daemon->dhcp_buff, "*") != 0) if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, daemon->domain_suffix, 0); {
char *p;
/* unprotect spaces */
for (p = strchr(daemon->dhcp_buff, '*'); p; p = strchr(p, '*'))
*p = ' ';
lease_set_hostname(lease, daemon->dhcp_buff, daemon->domain_suffix, 0);
}
/* set these correctly: the "old" events are generated later from
the startup synthesised SIGHUP. */
lease->new = lease->changed = 0;
} }
if (!daemon->lease_stream) if (!daemon->lease_stream)
@@ -110,13 +118,13 @@ void lease_init(struct daemon *daemon, time_t now)
errno = ENOENT; errno = ENOENT;
else if (WEXITSTATUS(rc) == 126) else if (WEXITSTATUS(rc) == 126)
errno = EACCES; errno = EACCES;
die(_("cannot run lease-init script %s: %s"), daemon->lease_change_command); die(_("cannot run lease-init script %s: %s"), daemon->lease_change_command, EC_FILE);
} }
if (WEXITSTATUS(rc) != 0) if (WEXITSTATUS(rc) != 0)
{ {
sprintf(daemon->dhcp_buff, "%d", WEXITSTATUS(rc)); sprintf(daemon->dhcp_buff, "%d", WEXITSTATUS(rc));
die(_("lease-init script returned exit code %s"), daemon->dhcp_buff); die(_("lease-init script returned exit code %s"), daemon->dhcp_buff, WEXITSTATUS(rc) + EC_INIT_OFFSET);
} }
} }
@@ -126,7 +134,7 @@ void lease_init(struct daemon *daemon, time_t now)
dns_dirty = 1; dns_dirty = 1;
} }
void lease_update_from_configs(struct daemon *daemon) void lease_update_from_configs(void)
{ {
/* changes to the config may change current leases. */ /* changes to the config may change current leases. */
@@ -140,11 +148,11 @@ void lease_update_from_configs(struct daemon *daemon)
(config->flags & CONFIG_NAME) && (config->flags & CONFIG_NAME) &&
(!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr)) (!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr))
lease_set_hostname(lease, config->hostname, daemon->domain_suffix, 1); lease_set_hostname(lease, config->hostname, daemon->domain_suffix, 1);
else if ((name = host_from_dns(daemon, lease->addr))) else if ((name = host_from_dns(lease->addr)))
lease_set_hostname(lease, name, daemon->domain_suffix, 1); /* updates auth flag only */ lease_set_hostname(lease, name, daemon->domain_suffix, 1); /* updates auth flag only */
} }
static void ourprintf(struct daemon *daemon, int *errp, char *format, ...) static void ourprintf(int *errp, char *format, ...)
{ {
va_list ap; va_list ap;
@@ -154,11 +162,12 @@ static void ourprintf(struct daemon *daemon, int *errp, char *format, ...)
va_end(ap); va_end(ap);
} }
void lease_update_file(struct daemon *daemon, time_t now) void lease_update_file(time_t now)
{ {
struct dhcp_lease *lease; struct dhcp_lease *lease;
time_t next_event; time_t next_event;
int i, err = 0; int i, err = 0;
char *p;
if (file_dirty != 0 && daemon->lease_stream) if (file_dirty != 0 && daemon->lease_stream)
{ {
@@ -170,29 +179,37 @@ void lease_update_file(struct daemon *daemon, time_t now)
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
{ {
#ifdef HAVE_BROKEN_RTC #ifdef HAVE_BROKEN_RTC
ourprintf(daemon, &err, "%u ", lease->length); ourprintf(&err, "%u ", lease->length);
#else #else
ourprintf(daemon, &err, "%lu ", (unsigned long)lease->expires); ourprintf(&err, "%lu ", (unsigned long)lease->expires);
#endif #endif
if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0) if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0)
ourprintf(daemon, &err, "%.2x-", lease->hwaddr_type); ourprintf(&err, "%.2x-", lease->hwaddr_type);
for (i = 0; i < lease->hwaddr_len; i++) for (i = 0; i < lease->hwaddr_len; i++)
{ {
ourprintf(daemon, &err, "%.2x", lease->hwaddr[i]); ourprintf(&err, "%.2x", lease->hwaddr[i]);
if (i != lease->hwaddr_len - 1) if (i != lease->hwaddr_len - 1)
ourprintf(daemon, &err, ":"); ourprintf(&err, ":");
} }
ourprintf(daemon, &err, " %s %s ", inet_ntoa(lease->addr),
lease->hostname && strlen(lease->hostname) != 0 ? lease->hostname : "*"); ourprintf(&err, " %s ", inet_ntoa(lease->addr));
/* substitute * for space: "*" is an illegal name, as is " " */
if (lease->hostname)
for (p = lease->hostname; *p; p++)
ourprintf(&err, "%c", *p == ' ' ? '*' : *p);
else
ourprintf(&err, "*");
ourprintf(&err, " ");
if (lease->clid && lease->clid_len != 0) if (lease->clid && lease->clid_len != 0)
{ {
for (i = 0; i < lease->clid_len - 1; i++) for (i = 0; i < lease->clid_len - 1; i++)
ourprintf(daemon, &err, "%.2x:", lease->clid[i]); ourprintf(&err, "%.2x:", lease->clid[i]);
ourprintf(daemon, &err, "%.2x\n", lease->clid[i]); ourprintf(&err, "%.2x\n", lease->clid[i]);
} }
else else
ourprintf(daemon, &err, "*\n"); ourprintf(&err, "*\n");
} }
if (fflush(daemon->lease_stream) != 0 || if (fflush(daemon->lease_stream) != 0 ||
@@ -223,7 +240,7 @@ void lease_update_file(struct daemon *daemon, time_t now)
alarm((unsigned)difftime(next_event, now)); alarm((unsigned)difftime(next_event, now));
} }
void lease_update_dns(struct daemon *daemon) void lease_update_dns(void)
{ {
struct dhcp_lease *lease; struct dhcp_lease *lease;
@@ -233,8 +250,8 @@ void lease_update_dns(struct daemon *daemon)
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
{ {
cache_add_dhcp_entry(daemon, lease->fqdn, &lease->addr, lease->expires); cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
cache_add_dhcp_entry(daemon, lease->hostname, &lease->addr, lease->expires); cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires);
} }
dns_dirty = 0; dns_dirty = 0;
@@ -306,7 +323,7 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
struct dhcp_lease *lease_allocate(struct in_addr addr) struct dhcp_lease *lease_allocate(struct in_addr addr)
{ {
struct dhcp_lease *lease; struct dhcp_lease *lease;
if (!leases_left || !(lease = malloc(sizeof(struct dhcp_lease)))) if (!leases_left || !(lease = whine_malloc(sizeof(struct dhcp_lease))))
return NULL; return NULL;
memset(lease, 0, sizeof(struct dhcp_lease)); memset(lease, 0, sizeof(struct dhcp_lease));
@@ -378,9 +395,8 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
if (lease->clid_len != clid_len) if (lease->clid_len != clid_len)
{ {
lease->aux_changed = file_dirty = 1; lease->aux_changed = file_dirty = 1;
if (lease->clid) free(lease->clid);
free(lease->clid); if (!(lease->clid = whine_malloc(clid_len)))
if (!(lease->clid = malloc(clid_len)))
return; return;
} }
else if (memcmp(lease->clid, clid, clid_len) != 0) else if (memcmp(lease->clid, clid, clid_len) != 0)
@@ -420,8 +436,7 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix, int
return; return;
/* this shouldn't happen unless updates are very quick and the /* this shouldn't happen unless updates are very quick and the
script very slow, we just avoid a memory leak if it does. */ script very slow, we just avoid a memory leak if it does. */
if (lease_tmp->old_hostname) free(lease_tmp->old_hostname);
free(lease_tmp->old_hostname);
lease_tmp->old_hostname = lease_tmp->hostname; lease_tmp->old_hostname = lease_tmp->hostname;
lease_tmp->hostname = NULL; lease_tmp->hostname = NULL;
if (lease_tmp->fqdn) if (lease_tmp->fqdn)
@@ -432,10 +447,10 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix, int
break; break;
} }
if (!new_name && (new_name = malloc(strlen(name) + 1))) if (!new_name && (new_name = whine_malloc(strlen(name) + 1)))
strcpy(new_name, name); strcpy(new_name, name);
if (suffix && !new_fqdn && (new_fqdn = malloc(strlen(name) + strlen(suffix) + 2))) if (suffix && !new_fqdn && (new_fqdn = whine_malloc(strlen(name) + strlen(suffix) + 2)))
{ {
strcpy(new_fqdn, name); strcpy(new_fqdn, name);
strcat(new_fqdn, "."); strcat(new_fqdn, ".");
@@ -446,13 +461,11 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix, int
if (lease->hostname) if (lease->hostname)
{ {
/* run script to say we lost our old name */ /* run script to say we lost our old name */
if (lease->old_hostname) free(lease->old_hostname);
free(lease->old_hostname);
lease->old_hostname = lease->hostname; lease->old_hostname = lease->hostname;
} }
if (lease->fqdn) free(lease->fqdn);
free(lease->fqdn);
lease->hostname = new_name; lease->hostname = new_name;
lease->fqdn = new_fqdn; lease->fqdn = new_fqdn;
@@ -463,12 +476,20 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix, int
lease->changed = 1; /* run script on change */ lease->changed = 1; /* run script on change */
} }
void rerun_scripts(void)
{
struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next)
lease->changed = 1;
}
/* deleted leases get transferred to the old_leases list. /* deleted leases get transferred to the old_leases list.
remove them here, after calling the lease change remove them here, after calling the lease change
script. Also run the lease change script on new/modified leases. script. Also run the lease change script on new/modified leases.
Return zero if nothing to do. */ Return zero if nothing to do. */
int do_script_run(struct daemon *daemon) int do_script_run(time_t now)
{ {
struct dhcp_lease *lease; struct dhcp_lease *lease;
@@ -479,26 +500,25 @@ int do_script_run(struct daemon *daemon)
/* If the lease still has an old_hostname, do the "old" action on that first */ /* If the lease still has an old_hostname, do the "old" action on that first */
if (lease->old_hostname) if (lease->old_hostname)
{ {
queue_script(daemon, ACTION_OLD_HOSTNAME, lease, lease->old_hostname); #ifndef NO_FORK
queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
#endif
free(lease->old_hostname); free(lease->old_hostname);
lease->old_hostname = NULL; lease->old_hostname = NULL;
return 1; return 1;
} }
else else
{ {
queue_script(daemon, ACTION_DEL, lease, lease->hostname); #ifndef NO_FORK
queue_script(ACTION_DEL, lease, lease->hostname, now);
#endif
old_leases = lease->next; old_leases = lease->next;
if (lease->hostname) free(lease->hostname);
free(lease->hostname); free(lease->fqdn);
if (lease->fqdn) free(lease->clid);
free(lease->fqdn); free(lease->vendorclass);
if (lease->clid) free(lease->userclass);
free(lease->clid);
if (lease->vendorclass)
free(lease->vendorclass);
if (lease->userclass)
free(lease->userclass);
free(lease); free(lease);
return 1; return 1;
@@ -509,7 +529,9 @@ int do_script_run(struct daemon *daemon)
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
if (lease->old_hostname) if (lease->old_hostname)
{ {
queue_script(daemon, ACTION_OLD_HOSTNAME, lease, lease->old_hostname); #ifndef NO_FORK
queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
#endif
free(lease->old_hostname); free(lease->old_hostname);
lease->old_hostname = NULL; lease->old_hostname = NULL;
return 1; return 1;
@@ -519,21 +541,18 @@ int do_script_run(struct daemon *daemon)
if (lease->new || lease->changed || if (lease->new || lease->changed ||
(lease->aux_changed && (daemon->options & OPT_LEASE_RO))) (lease->aux_changed && (daemon->options & OPT_LEASE_RO)))
{ {
queue_script(daemon, lease->new ? ACTION_ADD : ACTION_OLD, lease, lease->hostname); #ifndef NO_FORK
queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, lease->hostname, now);
#endif
lease->new = lease->changed = lease->aux_changed = 0; lease->new = lease->changed = lease->aux_changed = 0;
/* these are used for the "add" call, then junked, since they're not in the database */ /* these are used for the "add" call, then junked, since they're not in the database */
if (lease->vendorclass) free(lease->vendorclass);
{ lease->vendorclass = NULL;
free(lease->vendorclass);
lease->vendorclass = NULL;
}
if (lease->userclass)
{
free(lease->userclass);
lease->userclass = NULL;
}
free(lease->userclass);
lease->userclass = NULL;
return 1; return 1;
} }

210
src/log.c
View File

@@ -33,9 +33,11 @@ static int entries_alloced = 0;
static int entries_lost = 0; static int entries_lost = 0;
static int connection_good = 1; static int connection_good = 1;
static int max_logs = 0; static int max_logs = 0;
static int connection_type = SOCK_DGRAM;
struct log_entry { struct log_entry {
int offset, length; int offset, length;
pid_t pid; /* to avoid duplicates over a fork */
struct log_entry *next; struct log_entry *next;
char payload[MAX_MESSAGE]; char payload[MAX_MESSAGE];
}; };
@@ -44,10 +46,8 @@ static struct log_entry *entries = NULL;
static struct log_entry *free_entries = NULL; static struct log_entry *free_entries = NULL;
int log_start(struct daemon *daemon) void log_start(struct passwd *ent_pw)
{ {
int flags;
log_stderr = !!(daemon->options & OPT_DEBUG); log_stderr = !!(daemon->options & OPT_DEBUG);
if (daemon->log_fac != -1) if (daemon->log_fac != -1)
@@ -58,43 +58,78 @@ int log_start(struct daemon *daemon)
#endif #endif
if (daemon->log_file) if (daemon->log_file)
{ {
log_fd = open(daemon->log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP);
log_to_file = 1; log_to_file = 1;
daemon->max_logs = 0; daemon->max_logs = 0;
} }
else
log_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (log_fd == -1) max_logs = daemon->max_logs;
die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log");
if (!log_reopen(daemon->log_file))
die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log", EC_FILE);
/* If we're running as root and going to change uid later,
change the ownership here so that the file is always owned by
the dnsmasq user. Then logrotate can just copy the owner.
Failure of the chown call is OK, (for instance when started as non-root) */
if (log_to_file && ent_pw && ent_pw->pw_uid != 0)
fchown(log_fd, ent_pw->pw_uid, -1);
/* if queuing is inhibited, make sure we allocate /* if queuing is inhibited, make sure we allocate
the one required buffer now. */ the one required buffer now. */
if ((max_logs = daemon->max_logs) == 0) if (max_logs == 0)
{ {
free_entries = safe_malloc(sizeof(struct log_entry)); free_entries = safe_malloc(sizeof(struct log_entry));
free_entries->next = NULL; free_entries->next = NULL;
entries_alloced = 1; entries_alloced = 1;
} }
}
if ((flags = fcntl(log_fd, F_GETFD)) != -1) int log_reopen(char *log_file)
fcntl(log_fd, F_SETFD, flags | FD_CLOEXEC); {
int flags;
if (log_fd != -1)
close(log_fd);
/* if max_log is zero, leave the socket blocking */ /* NOTE: umask is set to 022 by the time this gets called */
if (log_file)
log_fd = open(log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP);
else
log_fd = socket(AF_UNIX, connection_type, 0);
if (log_fd == -1)
return 0;
/* if max_logs is zero, leave the socket blocking */
if (max_logs != 0 && (flags = fcntl(log_fd, F_GETFL)) != -1) if (max_logs != 0 && (flags = fcntl(log_fd, F_GETFL)) != -1)
fcntl(log_fd, F_SETFL, flags | O_NONBLOCK); fcntl(log_fd, F_SETFL, flags | O_NONBLOCK);
return log_fd; return 1;
} }
static void free_entry(void)
{
struct log_entry *tmp = entries;
entries = tmp->next;
tmp->next = free_entries;
free_entries = tmp;
}
static void log_write(void) static void log_write(void)
{ {
ssize_t rc; ssize_t rc;
int tried_stream = 0;
while (entries) while (entries)
{ {
/* Avoid duplicates over a fork() */
if (entries->pid != getpid())
{
free_entry();
continue;
}
connection_good = 1; connection_good = 1;
if ((rc = write(log_fd, entries->payload + entries->offset, entries->length)) != -1) if ((rc = write(log_fd, entries->payload + entries->offset, entries->length)) != -1)
@@ -103,11 +138,7 @@ static void log_write(void)
entries->offset += rc; entries->offset += rc;
if (entries->length == 0) if (entries->length == 0)
{ {
struct log_entry *tmp = entries; free_entry();
entries = tmp->next;
tmp->next = free_entries;
free_entries = tmp;
if (entries_lost != 0) if (entries_lost != 0)
{ {
int e = entries_lost; int e = entries_lost;
@@ -129,71 +160,63 @@ static void log_write(void)
connection_good = 0; connection_good = 0;
return; return;
} }
/* Once a stream socket hits EPIPE, we have to close and re-open */ /* errors handling after this assumes sockets */
if (errno == EPIPE) if (!log_to_file)
goto reopen_stream;
if (!log_to_file &&
(errno == ECONNREFUSED ||
errno == ENOTCONN ||
errno == EDESTADDRREQ ||
errno == ECONNRESET))
{ {
/* socket went (syslogd down?), try and reconnect. If we fail, /* Once a stream socket hits EPIPE, we have to close and re-open
stop trying until the next call to my_syslog() (we ignore SIGPIPE) */
ECONNREFUSED -> connection went down if (errno == EPIPE)
ENOTCONN -> nobody listening
(ECONNRESET, EDESTADDRREQ are *BSD equivalents)
EPIPE comes from broken stream socket (we ignore SIGPIPE) */
struct sockaddr_un logaddr;
#ifdef HAVE_SOCKADDR_SA_LEN
logaddr.sun_len = sizeof(logaddr) - sizeof(logaddr.sun_path) + strlen(_PATH_LOG) + 1;
#endif
logaddr.sun_family = AF_LOCAL;
strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path));
/* Got connection back? try again. */
if (connect(log_fd, (struct sockaddr *)&logaddr, sizeof(logaddr)) != -1)
continue;
/* errors from connect which mean we should keep trying */
if (errno == ENOENT ||
errno == EALREADY ||
errno == ECONNREFUSED ||
errno == EISCONN ||
errno == EINTR ||
errno == EAGAIN)
{ {
/* try again on next syslog() call */ if (log_reopen(NULL))
connection_good = 0; continue;
return;
} }
else if (errno == ECONNREFUSED ||
/* we start with a SOCK_DGRAM socket, but syslog may want SOCK_STREAM */ errno == ENOTCONN ||
if (!tried_stream && errno == EPROTOTYPE) errno == EDESTADDRREQ ||
errno == ECONNRESET)
{ {
reopen_stream: /* socket went (syslogd down?), try and reconnect. If we fail,
tried_stream = 1; stop trying until the next call to my_syslog()
close(log_fd); ECONNREFUSED -> connection went down
if ((log_fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1) ENOTCONN -> nobody listening
(ECONNRESET, EDESTADDRREQ are *BSD equivalents) */
struct sockaddr_un logaddr;
#ifdef HAVE_SOCKADDR_SA_LEN
logaddr.sun_len = sizeof(logaddr) - sizeof(logaddr.sun_path) + strlen(_PATH_LOG) + 1;
#endif
logaddr.sun_family = AF_LOCAL;
strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path));
/* Got connection back? try again. */
if (connect(log_fd, (struct sockaddr *)&logaddr, sizeof(logaddr)) != -1)
continue;
/* errors from connect which mean we should keep trying */
if (errno == ENOENT ||
errno == EALREADY ||
errno == ECONNREFUSED ||
errno == EISCONN ||
errno == EINTR ||
errno == EAGAIN)
{ {
int flags; /* try again on next syslog() call */
connection_good = 0;
if ((flags = fcntl(log_fd, F_GETFD)) != -1) return;
fcntl(log_fd, F_SETFD, flags | FD_CLOEXEC); }
/* if max_log is zero, leave the socket blocking */ /* try the other sort of socket... */
if (max_logs != 0 && (flags = fcntl(log_fd, F_GETFL)) != -1) if (errno == EPROTOTYPE)
fcntl(log_fd, F_SETFL, flags | O_NONBLOCK); {
connection_type = connection_type == SOCK_DGRAM ? SOCK_STREAM : SOCK_DGRAM;
continue; if (log_reopen(NULL))
continue;
} }
} }
} }
/* give up - fall back to syslog() - this handles out-of-space /* give up - fall back to syslog() - this handles out-of-space
when logging to a file, for instance. */ when logging to a file, for instance. */
log_fd = -1; log_fd = -1;
@@ -209,7 +232,8 @@ void my_syslog(int priority, const char *format, ...)
time_t time_now; time_t time_now;
char *p; char *p;
size_t len; size_t len;
pid_t pid = getpid();
va_start(ap, format); va_start(ap, format);
if (log_stderr) if (log_stderr)
@@ -258,11 +282,12 @@ void my_syslog(int priority, const char *format, ...)
if (!log_to_file) if (!log_to_file)
p += sprintf(p, "<%d>", priority | log_fac); p += sprintf(p, "<%d>", priority | log_fac);
p += sprintf(p, "%.15s dnsmasq[%d]: ", ctime(&time_now) + 4, getpid()); p += sprintf(p, "%.15s dnsmasq[%d]: ", ctime(&time_now) + 4, pid);
len = p - entry->payload; len = p - entry->payload;
len += vsnprintf(p, MAX_MESSAGE - len, format, ap) + 1; /* include zero-terminator */ len += vsnprintf(p, MAX_MESSAGE - len, format, ap) + 1; /* include zero-terminator */
entry->length = len > MAX_MESSAGE ? MAX_MESSAGE : len; entry->length = len > MAX_MESSAGE ? MAX_MESSAGE : len;
entry->offset = 0; entry->offset = 0;
entry->pid = pid;
/* replace terminator with \n */ /* replace terminator with \n */
if (log_to_file) if (log_to_file)
@@ -321,11 +346,24 @@ void set_log_writer(fd_set *set, int *maxfdp)
void check_log_writer(fd_set *set) void check_log_writer(fd_set *set)
{ {
if (log_fd != -1 && FD_ISSET(log_fd, set)) if (log_fd != -1 && (!set || FD_ISSET(log_fd, set)))
log_write(); log_write();
} }
void die(char *message, char *arg1) void flush_log(void)
{
/* block until queue empty */
if (log_fd != -1)
{
int flags;
if ((flags = fcntl(log_fd, F_GETFL)) != -1)
fcntl(log_fd, F_SETFL, flags & ~O_NONBLOCK);
log_write();
close(log_fd);
}
}
void die(char *message, char *arg1, int exit_code)
{ {
char *errmess = strerror(errno); char *errmess = strerror(errno);
@@ -333,10 +371,12 @@ void die(char *message, char *arg1)
arg1 = errmess; arg1 = errmess;
log_stderr = 1; /* print as well as log when we die.... */ log_stderr = 1; /* print as well as log when we die.... */
fputc('\n', stderr); /* prettyfy startup-script message */
my_syslog(LOG_CRIT, message, arg1, errmess); my_syslog(LOG_CRIT, message, arg1, errmess);
log_stderr = 0; log_stderr = 0;
my_syslog(LOG_CRIT, _("FAILED to start up")); my_syslog(LOG_CRIT, _("FAILED to start up"));
flush_log();
exit(1); exit(exit_code);
} }

View File

@@ -29,9 +29,9 @@
static struct iovec iov; static struct iovec iov;
static void nl_err(struct nlmsghdr *h); static void nl_err(struct nlmsghdr *h);
static void nl_routechange(struct daemon *daemon, struct nlmsghdr *h); static void nl_routechange(struct nlmsghdr *h);
void netlink_init(struct daemon *daemon) void netlink_init(void)
{ {
struct sockaddr_nl addr; struct sockaddr_nl addr;
@@ -56,19 +56,13 @@ void netlink_init(struct daemon *daemon)
} }
if (daemon->netlinkfd == -1) if (daemon->netlinkfd == -1)
die(_("cannot create netlink socket: %s"), NULL); die(_("cannot create netlink socket: %s"), NULL, EC_MISC);
else
{
int flags = fcntl(daemon->netlinkfd, F_GETFD);
if (flags != -1)
fcntl(daemon->netlinkfd, F_SETFD, flags | FD_CLOEXEC);
}
iov.iov_len = 200; iov.iov_len = 200;
iov.iov_base = safe_malloc(iov.iov_len); iov.iov_base = safe_malloc(iov.iov_len);
} }
static ssize_t netlink_recv(struct daemon *daemon) static ssize_t netlink_recv(void)
{ {
struct msghdr msg; struct msghdr msg;
ssize_t rc; ssize_t rc;
@@ -107,7 +101,7 @@ static ssize_t netlink_recv(struct daemon *daemon)
return rc; return rc;
} }
int iface_enumerate(struct daemon *daemon, void *parm, int (*ipv4_callback)(), int (*ipv6_callback)()) int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
{ {
struct sockaddr_nl addr; struct sockaddr_nl addr;
struct nlmsghdr *h; struct nlmsghdr *h;
@@ -142,14 +136,14 @@ int iface_enumerate(struct daemon *daemon, void *parm, int (*ipv4_callback)(), i
while (1) while (1)
{ {
if ((len = netlink_recv(daemon)) == -1) if ((len = netlink_recv()) == -1)
return 0; return 0;
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
if (h->nlmsg_type == NLMSG_ERROR) if (h->nlmsg_type == NLMSG_ERROR)
nl_err(h); nl_err(h);
else if (h->nlmsg_seq != seq) else if (h->nlmsg_seq != seq)
nl_routechange(daemon, h); /* May be multicast arriving async */ nl_routechange(h); /* May be multicast arriving async */
else if (h->nlmsg_type == NLMSG_DONE) else if (h->nlmsg_type == NLMSG_DONE)
{ {
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
@@ -186,7 +180,7 @@ int iface_enumerate(struct daemon *daemon, void *parm, int (*ipv4_callback)(), i
} }
if (addr.s_addr && ipv4_callback) if (addr.s_addr && ipv4_callback)
if (!((*ipv4_callback)(daemon, addr, ifa->ifa_index, netmask, broadcast, parm))) if (!((*ipv4_callback)(addr, ifa->ifa_index, netmask, broadcast, parm)))
return 0; return 0;
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
@@ -202,7 +196,7 @@ int iface_enumerate(struct daemon *daemon, void *parm, int (*ipv4_callback)(), i
} }
if (addrp && ipv6_callback) if (addrp && ipv6_callback)
if (!((*ipv6_callback)(daemon, addrp, ifa->ifa_index, ifa->ifa_index, parm))) if (!((*ipv6_callback)(addrp, ifa->ifa_index, ifa->ifa_index, parm)))
return 0; return 0;
} }
#endif #endif
@@ -210,18 +204,18 @@ int iface_enumerate(struct daemon *daemon, void *parm, int (*ipv4_callback)(), i
} }
} }
void netlink_multicast(struct daemon *daemon) void netlink_multicast(void)
{ {
ssize_t len; ssize_t len;
struct nlmsghdr *h; struct nlmsghdr *h;
if ((len = netlink_recv(daemon)) != -1) if ((len = netlink_recv()) != -1)
{ {
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
if (h->nlmsg_type == NLMSG_ERROR) if (h->nlmsg_type == NLMSG_ERROR)
nl_err(h); nl_err(h);
else else
nl_routechange(daemon, h); nl_routechange(h);
} }
} }
@@ -237,7 +231,7 @@ static void nl_err(struct nlmsghdr *h)
This helps on DoD links, where frequently the packet which triggers dialling is This helps on DoD links, where frequently the packet which triggers dialling is
a DNS query, which then gets lost. By re-sending, we can avoid the lookup a DNS query, which then gets lost. By re-sending, we can avoid the lookup
failing. */ failing. */
static void nl_routechange(struct daemon *daemon, struct nlmsghdr *h) static void nl_routechange(struct nlmsghdr *h)
{ {
if (h->nlmsg_type == RTM_NEWROUTE && daemon->srv_save) if (h->nlmsg_type == RTM_NEWROUTE && daemon->srv_save)
{ {

View File

@@ -12,7 +12,7 @@
#include "dnsmasq.h" #include "dnsmasq.h"
int iface_check(struct daemon *daemon, int family, struct all_addr *addr, int iface_check(int family, struct all_addr *addr,
struct ifreq *ifr, int *indexp) struct ifreq *ifr, int *indexp)
{ {
struct iname *tmp; struct iname *tmp;
@@ -84,7 +84,7 @@ int iface_check(struct daemon *daemon, int family, struct all_addr *addr,
return ret; return ret;
} }
static int iface_allowed(struct daemon *daemon, struct irec **irecp, int if_index, static int iface_allowed(struct irec **irecp, int if_index,
union mysockaddr *addr, struct in_addr netmask) union mysockaddr *addr, struct in_addr netmask)
{ {
struct irec *iface; struct irec *iface;
@@ -134,9 +134,10 @@ static int iface_allowed(struct daemon *daemon, struct irec **irecp, int if_inde
break; break;
} }
if (!lo && (lo = malloc(sizeof(struct iname)))) if (!lo &&
(lo = whine_malloc(sizeof(struct iname))) &&
(lo->name = whine_malloc(strlen(ifr.ifr_name)+1)))
{ {
lo->name = safe_malloc(strlen(ifr.ifr_name)+1);
strcpy(lo->name, ifr.ifr_name); strcpy(lo->name, ifr.ifr_name);
lo->isloop = lo->used = 1; lo->isloop = lo->used = 1;
lo->next = daemon->if_names; lo->next = daemon->if_names;
@@ -145,7 +146,7 @@ static int iface_allowed(struct daemon *daemon, struct irec **irecp, int if_inde
} }
if (addr->sa.sa_family == AF_INET && if (addr->sa.sa_family == AF_INET &&
!iface_check(daemon, AF_INET, (struct all_addr *)&addr->in.sin_addr, &ifr, NULL)) !iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, &ifr, NULL))
return 1; return 1;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
@@ -154,12 +155,12 @@ static int iface_allowed(struct daemon *daemon, struct irec **irecp, int if_inde
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
if (addr->sa.sa_family == AF_INET6 && if (addr->sa.sa_family == AF_INET6 &&
!iface_check(daemon, AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, &ifr, NULL)) !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, &ifr, NULL))
return 1; return 1;
#endif #endif
/* add to list */ /* add to list */
if ((iface = malloc(sizeof(struct irec)))) if ((iface = whine_malloc(sizeof(struct irec))))
{ {
iface->addr = *addr; iface->addr = *addr;
iface->netmask = netmask; iface->netmask = netmask;
@@ -174,7 +175,7 @@ static int iface_allowed(struct daemon *daemon, struct irec **irecp, int if_inde
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
static int iface_allowed_v6(struct daemon *daemon, struct in6_addr *local, static int iface_allowed_v6(struct in6_addr *local,
int scope, int if_index, void *vparam) int scope, int if_index, void *vparam)
{ {
union mysockaddr addr; union mysockaddr addr;
@@ -191,11 +192,11 @@ static int iface_allowed_v6(struct daemon *daemon, struct in6_addr *local,
addr.in6.sin6_port = htons(daemon->port); addr.in6.sin6_port = htons(daemon->port);
addr.in6.sin6_scope_id = scope; addr.in6.sin6_scope_id = scope;
return iface_allowed(daemon, (struct irec **)vparam, if_index, &addr, netmask); return iface_allowed((struct irec **)vparam, if_index, &addr, netmask);
} }
#endif #endif
static int iface_allowed_v4(struct daemon *daemon, struct in_addr local, int if_index, static int iface_allowed_v4(struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam) struct in_addr netmask, struct in_addr broadcast, void *vparam)
{ {
union mysockaddr addr; union mysockaddr addr;
@@ -209,30 +210,28 @@ static int iface_allowed_v4(struct daemon *daemon, struct in_addr local, int if_
addr.in.sin_addr = local; addr.in.sin_addr = local;
addr.in.sin_port = htons(daemon->port); addr.in.sin_port = htons(daemon->port);
return iface_allowed(daemon, (struct irec **)vparam, if_index, &addr, netmask); return iface_allowed((struct irec **)vparam, if_index, &addr, netmask);
} }
int enumerate_interfaces(struct daemon *daemon) int enumerate_interfaces(void)
{ {
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
return iface_enumerate(daemon, &daemon->interfaces, iface_allowed_v4, iface_allowed_v6); return iface_enumerate(&daemon->interfaces, iface_allowed_v4, iface_allowed_v6);
#else #else
return iface_enumerate(daemon, &daemon->interfaces, iface_allowed_v4, NULL); return iface_enumerate(&daemon->interfaces, iface_allowed_v4, NULL);
#endif #endif
} }
/* set NONBLOCK and CLOEXEC bits on fd: See Stevens 16.6 */ /* set NONBLOCK bit on fd: See Stevens 16.6 */
int fix_fd(int fd) int fix_fd(int fd)
{ {
int flags; int flags;
if ((flags = fcntl(fd, F_GETFL)) == -1 || if ((flags = fcntl(fd, F_GETFL)) == -1 ||
fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
(flags = fcntl(fd, F_GETFD)) == -1 ||
fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
return 0; return 0;
return 1; return 1;
} }
@@ -289,7 +288,7 @@ static int create_ipv6_listener(struct listener **link, int port)
} }
#endif #endif
struct listener *create_wildcard_listeners(int port, int have_tftp) struct listener *create_wildcard_listeners(void)
{ {
union mysockaddr addr; union mysockaddr addr;
int opt = 1; int opt = 1;
@@ -299,7 +298,7 @@ struct listener *create_wildcard_listeners(int port, int have_tftp)
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
addr.in.sin_family = AF_INET; addr.in.sin_family = AF_INET;
addr.in.sin_addr.s_addr = INADDR_ANY; addr.in.sin_addr.s_addr = INADDR_ANY;
addr.in.sin_port = htons(port); addr.in.sin_port = htons(daemon->port);
#ifdef HAVE_SOCKADDR_SA_LEN #ifdef HAVE_SOCKADDR_SA_LEN
addr.in.sin_len = sizeof(struct sockaddr_in); addr.in.sin_len = sizeof(struct sockaddr_in);
#endif #endif
@@ -313,7 +312,7 @@ struct listener *create_wildcard_listeners(int port, int have_tftp)
listen(tcpfd, 5) == -1 || listen(tcpfd, 5) == -1 ||
!fix_fd(tcpfd) || !fix_fd(tcpfd) ||
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
!create_ipv6_listener(&l6, port) || !create_ipv6_listener(&l6, daemon->port) ||
#endif #endif
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
!fix_fd(fd) || !fix_fd(fd) ||
@@ -327,14 +326,13 @@ struct listener *create_wildcard_listeners(int port, int have_tftp)
return NULL; return NULL;
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
if (have_tftp) if (daemon->options & OPT_TFTP)
{ {
addr.in.sin_port = htons(TFTP_PORT); addr.in.sin_port = htons(TFTP_PORT);
if ((tftpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) if ((tftpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
return NULL; return NULL;
if (setsockopt(tftpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || if (!fix_fd(tftpfd) ||
!fix_fd(tftpfd) ||
#if defined(HAVE_LINUX_NETWORK) #if defined(HAVE_LINUX_NETWORK)
setsockopt(tftpfd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 || setsockopt(tftpfd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
@@ -356,7 +354,7 @@ struct listener *create_wildcard_listeners(int port, int have_tftp)
return l; return l;
} }
struct listener *create_bound_listeners(struct daemon *daemon) struct listener *create_bound_listeners(void)
{ {
struct listener *listeners = NULL; struct listener *listeners = NULL;
struct irec *iface; struct irec *iface;
@@ -376,14 +374,14 @@ struct listener *create_bound_listeners(struct daemon *daemon)
setsockopt(new->tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || setsockopt(new->tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
!fix_fd(new->tcpfd) || !fix_fd(new->tcpfd) ||
!fix_fd(new->fd)) !fix_fd(new->fd))
die(_("failed to create listening socket: %s"), NULL); die(_("failed to create listening socket: %s"), NULL, EC_BADNET);
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
if (iface->addr.sa.sa_family == AF_INET6) if (iface->addr.sa.sa_family == AF_INET6)
{ {
if (setsockopt(new->fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 || if (setsockopt(new->fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
setsockopt(new->tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1) setsockopt(new->tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
die(_("failed to set IPV6 options on listening socket: %s"), NULL); die(_("failed to set IPV6 options on listening socket: %s"), NULL, EC_BADNET);
} }
#endif #endif
@@ -402,14 +400,14 @@ struct listener *create_bound_listeners(struct daemon *daemon)
{ {
prettyprint_addr(&iface->addr, daemon->namebuff); prettyprint_addr(&iface->addr, daemon->namebuff);
die(_("failed to bind listening socket for %s: %s"), die(_("failed to bind listening socket for %s: %s"),
daemon->namebuff); daemon->namebuff, EC_BADNET);
} }
} }
else else
{ {
listeners = new; listeners = new;
if (listen(new->tcpfd, 5) == -1) if (listen(new->tcpfd, 5) == -1)
die(_("failed to listen on socket: %s"), NULL); die(_("failed to listen on socket: %s"), NULL, EC_BADNET);
} }
if ((daemon->options & OPT_TFTP) && iface->addr.sa.sa_family == AF_INET && iface->dhcp_ok) if ((daemon->options & OPT_TFTP) && iface->addr.sa.sa_family == AF_INET && iface->dhcp_ok)
@@ -420,7 +418,7 @@ struct listener *create_bound_listeners(struct daemon *daemon)
setsockopt(new->tftpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || setsockopt(new->tftpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
!fix_fd(new->tftpfd) || !fix_fd(new->tftpfd) ||
bind(new->tftpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1) bind(new->tftpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1)
die(_("failed to create TFTP socket: %s"), NULL); die(_("failed to create TFTP socket: %s"), NULL, EC_BADNET);
iface->addr.in.sin_port = save; iface->addr.in.sin_port = save;
} }
} }
@@ -439,7 +437,7 @@ struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds)
/* need to make a new one. */ /* need to make a new one. */
errno = ENOMEM; /* in case malloc fails. */ errno = ENOMEM; /* in case malloc fails. */
if (!(sfd = malloc(sizeof(struct serverfd)))) if (!(sfd = whine_malloc(sizeof(struct serverfd))))
return NULL; return NULL;
if ((sfd->fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0)) == -1) if ((sfd->fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0)) == -1)
@@ -465,7 +463,7 @@ struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds)
return sfd; return sfd;
} }
void check_servers(struct daemon *daemon) void check_servers(void)
{ {
struct irec *iface; struct irec *iface;
struct server *new, *tmp, *ret = NULL; struct server *new, *tmp, *ret = NULL;
@@ -515,10 +513,12 @@ void check_servers(struct daemon *daemon)
if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS)) if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS))
{ {
char *s1, *s2; char *s1, *s2;
if (new->flags & SERV_HAS_DOMAIN) if (!(new->flags & SERV_HAS_DOMAIN))
s1 = _("domain"), s2 = new->domain; s1 = _("unqualified"), s2 = _("names");
else if (strlen(new->domain) == 0)
s1 = _("default"), s2 = "";
else else
s1 = _("unqualified"), s2 = _("domains"); s1 = _("domain"), s2 = new->domain;
if (new->flags & SERV_NO_ADDR) if (new->flags & SERV_NO_ADDR)
my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2); my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
@@ -534,7 +534,7 @@ void check_servers(struct daemon *daemon)
/* Return zero if no servers found, in that case we keep polling. /* Return zero if no servers found, in that case we keep polling.
This is a protection against an update-time/write race on resolv.conf */ This is a protection against an update-time/write race on resolv.conf */
int reload_servers(char *fname, struct daemon *daemon) int reload_servers(char *fname)
{ {
FILE *f; FILE *f;
char *line; char *line;
@@ -561,7 +561,7 @@ int reload_servers(char *fname, struct daemon *daemon)
serv->next = old_servers; serv->next = old_servers;
old_servers = serv; old_servers = serv;
/* forward table rules reference servers, so have to blow them away */ /* forward table rules reference servers, so have to blow them away */
server_gone(daemon, serv); server_gone(serv);
} }
else else
{ {
@@ -576,7 +576,9 @@ int reload_servers(char *fname, struct daemon *daemon)
union mysockaddr addr, source_addr; union mysockaddr addr, source_addr;
char *token = strtok(line, " \t\n\r"); char *token = strtok(line, " \t\n\r");
if (!token || strcmp(token, "nameserver") != 0) if (!token)
continue;
if (strcmp(token, "nameserver") != 0 && strcmp(token, "server") != 0)
continue; continue;
if (!(token = strtok(NULL, " \t\n\r"))) if (!(token = strtok(NULL, " \t\n\r")))
continue; continue;
@@ -614,7 +616,7 @@ int reload_servers(char *fname, struct daemon *daemon)
serv = old_servers; serv = old_servers;
old_servers = old_servers->next; old_servers = old_servers->next;
} }
else if (!(serv = malloc(sizeof (struct server)))) else if (!(serv = whine_malloc(sizeof (struct server))))
continue; continue;
/* this list is reverse ordered: /* this list is reverse ordered:
@@ -646,7 +648,7 @@ int reload_servers(char *fname, struct daemon *daemon)
/* Use an IPv4 listener socket for ioctling */ /* Use an IPv4 listener socket for ioctling */
struct in_addr get_ifaddr(struct daemon* daemon, char *intr) struct in_addr get_ifaddr(char *intr)
{ {
struct listener *l; struct listener *l;
struct ifreq ifr; struct ifreq ifr;

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 - 2006 Simon Kelley /* dnsmasq is Copyright (c) 2000 - 2007 Simon Kelley
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -43,6 +43,9 @@ struct myoption {
#define LOPT_REMOTE 269 #define LOPT_REMOTE 269
#define LOPT_SUBSCR 270 #define LOPT_SUBSCR 270
#define LOPT_INTNAME 271 #define LOPT_INTNAME 271
#define LOPT_BANK 272
#define LOPT_DHCP_HOST 273
#define LOPT_APREF 274
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
static const struct option opts[] = static const struct option opts[] =
@@ -119,6 +122,7 @@ static const struct myoption opts[] =
{"dhcp-ignore-names", 2, 0, LOPT_NO_NAMES }, {"dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
{"enable-tftp", 0, 0, LOPT_TFTP }, {"enable-tftp", 0, 0, LOPT_TFTP },
{"tftp-secure", 0, 0, LOPT_SECURE }, {"tftp-secure", 0, 0, LOPT_SECURE },
{"tftp-unique-root", 0, 0, LOPT_APREF },
{"tftp-root", 1, 0, LOPT_PREFIX }, {"tftp-root", 1, 0, LOPT_PREFIX },
{"tftp-max", 1, 0, LOPT_TFTP_MAX }, {"tftp-max", 1, 0, LOPT_TFTP_MAX },
{"ptr-record", 1, 0, LOPT_PTR }, {"ptr-record", 1, 0, LOPT_PTR },
@@ -133,6 +137,7 @@ static const struct myoption opts[] =
{"dhcp-remoteid", 1, 0, LOPT_REMOTE }, {"dhcp-remoteid", 1, 0, LOPT_REMOTE },
{"dhcp-subscrid", 1, 0, LOPT_SUBSCR }, {"dhcp-subscrid", 1, 0, LOPT_SUBSCR },
{"interface-name", 1, 0, LOPT_INTNAME }, {"interface-name", 1, 0, LOPT_INTNAME },
{"dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
{ NULL, 0, 0, 0 } { NULL, 0, 0, 0 }
}; };
@@ -169,6 +174,7 @@ static const struct optflags optmap[] = {
{ LOPT_SECURE, OPT_TFTP_SECURE }, { LOPT_SECURE, OPT_TFTP_SECURE },
{ LOPT_NOBLOCK, OPT_TFTP_NOBLOCK }, { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK },
{ LOPT_LOG_OPTS, OPT_LOG_OPTS }, { LOPT_LOG_OPTS, OPT_LOG_OPTS },
{ LOPT_APREF, OPT_TFTP_APREF },
{ 'v', 0}, { 'v', 0},
{ 'w', 0}, { 'w', 0},
{ 0, 0 } { 0, 0 }
@@ -192,7 +198,8 @@ static const struct {
{ "-f, --filterwin2k", gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL }, { "-f, --filterwin2k", gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
{ "-F, --dhcp-range=ipaddr,ipaddr,time", gettext_noop("Enable DHCP in the range given with lease duration."), NULL }, { "-F, --dhcp-range=ipaddr,ipaddr,time", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
{ "-g, --group=groupname", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP }, { "-g, --group=groupname", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
{ "-G, --dhcp-host=<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL }, { "-G, --dhcp-host=<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
{ " --dhcp-hostsfile=<filename>", gettext_noop("Read DHCP host specs from file"), NULL },
{ "-h, --no-hosts", gettext_noop("Do NOT load %s file."), HOSTSFILE }, { "-h, --no-hosts", gettext_noop("Do NOT load %s file."), HOSTSFILE },
{ "-H, --addn-hosts=path", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE }, { "-H, --addn-hosts=path", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
{ "-i, --interface=interface", gettext_noop("Specify interface(s) to listen on."), NULL }, { "-i, --interface=interface", gettext_noop("Specify interface(s) to listen on."), NULL },
@@ -255,6 +262,7 @@ static const struct {
{ " --dhcp-ignore-names[=<id>]", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL }, { " --dhcp-ignore-names[=<id>]", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
{ " --enable-tftp", gettext_noop("Enable integrated read-only TFTP server."), NULL }, { " --enable-tftp", gettext_noop("Enable integrated read-only TFTP server."), NULL },
{ " --tftp-root=<directory>", gettext_noop("Export files by TFTP only from the specified subtree."), NULL }, { " --tftp-root=<directory>", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
{ " --tftp-unique-root", gettext_noop("Add client IP address to tftp-root."), NULL },
{ " --tftp-secure", gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL }, { " --tftp-secure", gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
{ " --tftp-max=<connections>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" }, { " --tftp-max=<connections>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
{ " --tftp-no-blocksize", gettext_noop("Disable the TFTP blocksize extension."), NULL }, { " --tftp-no-blocksize", gettext_noop("Disable the TFTP blocksize extension."), NULL },
@@ -367,8 +375,6 @@ char *option_string(unsigned char opt)
static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,."; static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
static void one_file(struct daemon *daemon, char *file, int nest);
static char hide_meta(char c) static char hide_meta(char c)
{ {
unsigned int i; unsigned int i;
@@ -460,7 +466,7 @@ static int atoi_check(char *a, int *res)
return 1; return 1;
} }
static void add_txt(struct daemon *daemon, char *name, char *txt) static void add_txt(char *name, char *txt)
{ {
size_t len = strlen(txt); size_t len = strlen(txt);
struct txt_record *r = safe_malloc(sizeof(struct txt_record)); struct txt_record *r = safe_malloc(sizeof(struct txt_record));
@@ -525,7 +531,7 @@ static void display_opts(void)
} }
/* This is too insanely large to keep in-line in the switch */ /* This is too insanely large to keep in-line in the switch */
static char *parse_dhcp_opt(struct daemon *daemon, char *arg, int forced) static char *parse_dhcp_opt(char *arg, int forced)
{ {
struct dhcp_opt *new = safe_malloc(sizeof(struct dhcp_opt)); struct dhcp_opt *new = safe_malloc(sizeof(struct dhcp_opt));
char lenchar = 0, *cp; char lenchar = 0, *cp;
@@ -749,7 +755,7 @@ static char *parse_dhcp_opt(struct daemon *daemon, char *arg, int forced)
} }
if (!(m = realloc(m, len + strlen(arg) + 2 + header_size))) if (!(m = realloc(m, len + strlen(arg) + 2 + header_size)))
die(_("could not get memory"), NULL); die(_("could not get memory"), NULL, EC_NOMEM);
p = m + header_size; p = m + header_size;
q = p + len; q = p + len;
@@ -809,7 +815,7 @@ static char *parse_dhcp_opt(struct daemon *daemon, char *arg, int forced)
} }
static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem, int nest) static char *one_opt(int option, char *arg, char *problem, int nest)
{ {
int i; int i;
char *comma; char *comma;
@@ -830,7 +836,7 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
{ {
char *file = safe_string_alloc(arg); char *file = safe_string_alloc(arg);
if (file) if (file)
one_file(daemon, file, nest); one_file(file, nest, 0);
break; break;
} }
@@ -844,7 +850,7 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
break; break;
if (!(dir_stream = opendir(directory))) if (!(dir_stream = opendir(directory)))
die(_("cannot access directory %s: %s"), directory); die(_("cannot access directory %s: %s"), directory, EC_FILE);
while ((ent = readdir(dir_stream))) while ((ent = readdir(dir_stream)))
{ {
@@ -863,13 +869,13 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
strcat(path, "/"); strcat(path, "/");
strcat(path, ent->d_name); strcat(path, ent->d_name);
if (stat(path, &buf) == -1) if (stat(path, &buf) == -1)
die(_("cannot access %s: %s"), path); die(_("cannot access %s: %s"), path, EC_FILE);
/* only reg files allowed. */ /* only reg files allowed. */
if (!S_ISREG(buf.st_mode)) if (!S_ISREG(buf.st_mode))
continue; continue;
/* dir is one level, so files must be readable */ /* dir is one level, so files must be readable */
one_file(daemon, path, nest + 1); one_file(path, nest + 1, 0);
free(path); free(path);
} }
@@ -900,6 +906,15 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
case 'x': /* --pid-file */ case 'x': /* --pid-file */
daemon->runfile = safe_string_alloc(arg); daemon->runfile = safe_string_alloc(arg);
break; break;
case LOPT_DHCP_HOST: /* --dhcp-hostfile */
if (daemon->dhcp_hosts_file)
{
problem = _("only one dhcp-hostsfile allowed");
option = '?';
}
daemon->dhcp_hosts_file = safe_string_alloc(arg);
break;
case 'r': /* --resolv-file */ case 'r': /* --resolv-file */
{ {
@@ -1210,7 +1225,7 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
} }
else else
newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY; newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, arg, &newlist->addr.in6.sin6_addr) > 0) else if (inet_pton(AF_INET6, arg, &newlist->addr.in6.sin6_addr) > 0)
{ {
@@ -1494,17 +1509,24 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
} }
break; break;
} }
/* Note, must not die() via safe_* if option is LOPT_BANK, since
when called with this we are re-loading the file. */
case LOPT_BANK:
case 'G': /* --dhcp-host */ case 'G': /* --dhcp-host */
{ {
int j, k = 0; int j, k = 0;
char *a[6] = { NULL, NULL, NULL, NULL, NULL, NULL }; char *a[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
struct dhcp_config *new = safe_malloc(sizeof(struct dhcp_config)); struct dhcp_config *new;
struct in_addr in; struct in_addr in;
if (option != LOPT_BANK)
new = safe_malloc(sizeof(struct dhcp_config));
else if (!(new = whine_malloc(sizeof(struct dhcp_config))))
break;
new->next = daemon->dhcp_conf; new->next = daemon->dhcp_conf;
new->flags = 0; new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
if ((a[0] = arg)) if ((a[0] = arg))
for (k = 1; k < 6; k++) for (k = 1; k < 6; k++)
@@ -1529,18 +1551,28 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
if (strchr(arg, ':')) if (strchr(arg, ':'))
len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL); len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
else else
len = (int) strlen(arg); {
unhide_metas(arg);
new->flags |= CONFIG_CLID; len = (int) strlen(arg);
new->clid_len = len; }
new->clid = safe_malloc(len);
memcpy(new->clid, arg, len); if ((new->clid = (option == LOPT_BANK) ? whine_malloc(len): safe_malloc(len)))
{
new->flags |= CONFIG_CLID;
new->clid_len = len;
memcpy(new->clid, arg, len);
}
} }
} }
else if (strstr(arg, "net:") == arg) else if (strstr(arg, "net:") == arg)
{ {
new->flags |= CONFIG_NETID; int len = strlen(arg + 4) + 1;
new->netid.net = safe_string_alloc(arg+4); if ((new->netid.net = (option == LOPT_BANK) ? whine_malloc(len): safe_malloc(len)))
{
new->flags |= CONFIG_NETID;
strcpy(new->netid.net, arg+4);
unhide_metas(new->netid.net);
}
} }
else else
{ {
@@ -1599,8 +1631,17 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
new->flags |= CONFIG_DISABLE; new->flags |= CONFIG_DISABLE;
else else
{ {
new->hostname = safe_string_alloc(a[j]); int len = strlen(a[j]) + 1;
new->flags |= CONFIG_NAME; if (!canonicalise_opt(a[j]))
{
problem = _("bad DHCP host name");
option = '?';
}
else if ((new->hostname = (option == LOPT_BANK) ? whine_malloc(len): safe_malloc(len)))
{
new->flags |= CONFIG_NAME;
strcpy(new->hostname, a[j]);
}
} }
} }
else else
@@ -1614,17 +1655,13 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
} }
} }
if (option == '?') daemon->dhcp_conf = new;
problem = _("bad dhcp-host");
else
daemon->dhcp_conf = new;
break; break;
} }
case 'O': case 'O':
case LOPT_FORCE: case LOPT_FORCE:
if ((problem = parse_dhcp_opt(daemon, arg, option == LOPT_FORCE))) if ((problem = parse_dhcp_opt(arg, option == LOPT_FORCE)))
option = '?'; option = '?';
break; break;
@@ -2007,21 +2044,30 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
return option == '?' ? problem : NULL; return option == '?' ? problem : NULL;
} }
static void one_file(struct daemon *daemon, char *file, int nest) void one_file(char *file, int nest, int hosts)
{ {
int i, option, lineno = 0; int i, option, lineno = 0;
FILE *f; FILE *f;
char *p, *arg, *start, *buff = daemon->namebuff; char *p, *arg, *start, *buff = daemon->namebuff;
if (nest > 20) if (nest > 20)
die(_("files nested too deep in %s"), file); die(_("files nested too deep in %s"), file, EC_BADCONF);
if (!(f = fopen(file, "r"))) if (!(f = fopen(file, "r")))
{ {
if (errno == ENOENT && nest == 0) if (errno == ENOENT && nest == 0)
return; /* No conffile, all done. */ return; /* No conffile, all done. */
else else
die(_("cannot read %s: %s"), file); {
char *str = _("cannot read %s: %s");
if (hosts)
{
my_syslog(LOG_ERR, str, file, strerror(errno));
return;
}
else
die(str, file, EC_FILE);
}
} }
while (fgets(buff, MAXDNAME, f)) while (fgets(buff, MAXDNAME, f))
@@ -2042,7 +2088,7 @@ static void one_file(struct daemon *daemon, char *file, int nest)
memmove(p, p+1, strlen(p+1)+1); memmove(p, p+1, strlen(p+1)+1);
for(; *p && *p != '"'; p++) for(; *p && *p != '"'; p++)
{ {
if (*p == '\\' && strchr("\"tn\033br\\", p[1])) if (*p == '\\' && strchr("\"tnebr\\", p[1]))
{ {
if (p[1] == 't') if (p[1] == 't')
p[1] = '\t'; p[1] = '\t';
@@ -2085,55 +2131,71 @@ static void one_file(struct daemon *daemon, char *file, int nest)
if (*buff == 0) if (*buff == 0)
continue; continue;
if ((p=strchr(buff, '='))) if (hosts)
arg = buff;
else if ((p=strchr(buff, '=')))
{ {
/* allow spaces around "=" */ /* allow spaces around "=" */
for (arg = p+1; isspace(*arg); arg++); arg = p+1;
for (; p >= buff && (isspace(*p) || *p == '='); p--) for (; p >= buff && (isspace(*p) || *p == '='); p--)
*p = 0; *p = 0;
} }
else else
arg = NULL; arg = NULL;
/* skip leading space */ if (hosts)
for (start = buff; *start && isspace(*start); start++); option = LOPT_BANK;
for (option = 0, i = 0; opts[i].name; i++)
if (strcmp(opts[i].name, start) == 0)
{
option = opts[i].val;
break;
}
if (!option)
errmess = _("bad option");
else if (opts[i].has_arg == 0 && arg)
errmess = _("extraneous parameter");
else if (opts[i].has_arg == 1 && !arg)
errmess = _("missing parameter");
else else
errmess = one_opt(daemon, option, arg, _("error"), nest + 1); {
/* skip leading space */
for (start = buff; *start && isspace(*start); start++);
for (option = 0, i = 0; opts[i].name; i++)
if (strcmp(opts[i].name, start) == 0)
{
option = opts[i].val;
break;
}
if (!option)
errmess = _("bad option");
else if (opts[i].has_arg == 0 && arg)
errmess = _("extraneous parameter");
else if (opts[i].has_arg == 1 && !arg)
errmess = _("missing parameter");
}
if (!errmess)
{
if (arg)
for (; isspace(*arg); arg++);
errmess = one_opt(option, arg, _("error"), nest + 1);
}
if (errmess) if (errmess)
{ {
oops: oops:
sprintf(buff, _("%s at line %d of %%s"), errmess, lineno); sprintf(buff, _("%s at line %d of %%s"), errmess, lineno);
die(buff, file); if (hosts)
my_syslog(LOG_ERR, buff, file);
else
die(buff, file, EC_BADCONF);
} }
} }
fclose(f); fclose(f);
} }
struct daemon *read_opts(int argc, char **argv, char *compile_opts) void read_opts(int argc, char **argv, char *compile_opts)
{ {
struct daemon *daemon = safe_malloc(sizeof(struct daemon));
char *buff = safe_malloc(MAXDNAME); char *buff = safe_malloc(MAXDNAME);
int option, nest = 0; int option, nest = 0;
char *errmess, *arg, *conffile = CONFFILE; char *errmess, *arg, *conffile = CONFFILE;
opterr = 0; opterr = 0;
daemon = safe_malloc(sizeof(struct daemon));
memset(daemon, 0, sizeof(struct daemon)); memset(daemon, 0, sizeof(struct daemon));
daemon->namebuff = buff; daemon->namebuff = buff;
@@ -2151,9 +2213,9 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts)
daemon->tftp_max = TFTP_MAX_CONNECTIONS; daemon->tftp_max = TFTP_MAX_CONNECTIONS;
daemon->edns_pktsz = EDNS_PKTSZ; daemon->edns_pktsz = EDNS_PKTSZ;
daemon->log_fac = -1; daemon->log_fac = -1;
add_txt(daemon, "version.bind", "dnsmasq-" VERSION ); add_txt("version.bind", "dnsmasq-" VERSION );
add_txt(daemon, "authors.bind", "Simon Kelley"); add_txt("authors.bind", "Simon Kelley");
add_txt(daemon, "copyright.bind", COPYRIGHT); add_txt("copyright.bind", COPYRIGHT);
while (1) while (1)
{ {
@@ -2202,17 +2264,17 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts)
else else
{ {
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
errmess = one_opt(daemon, option, arg, _("try --help"), 0); errmess = one_opt(option, arg, _("try --help"), 0);
#else #else
errmess = one_opt(daemon, option, arg, _("try -w"), 0); errmess = one_opt(option, arg, _("try -w"), 0);
#endif #endif
if (errmess) if (errmess)
die(_("bad command line options: %s"), errmess); die(_("bad command line options: %s"), errmess, EC_BADCONF);
} }
} }
if (conffile) if (conffile)
one_file(daemon, conffile, nest); one_file(conffile, nest, 0);
/* port might no be known when the address is parsed - fill in here */ /* port might no be known when the address is parsed - fill in here */
if (daemon->servers) if (daemon->servers)
@@ -2226,8 +2288,8 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts)
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else if (tmp->source_addr.sa.sa_family == AF_INET6) else if (tmp->source_addr.sa.sa_family == AF_INET6)
tmp->source_addr.in6.sin6_port = htons(daemon->query_port); tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
#endif #endif
} }
} }
if (daemon->if_addrs) if (daemon->if_addrs)
@@ -2248,7 +2310,7 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts)
struct mx_srv_record *mx; struct mx_srv_record *mx;
if (gethostname(buff, MAXDNAME) == -1) if (gethostname(buff, MAXDNAME) == -1)
die(_("cannot get host-name: %s"), NULL); die(_("cannot get host-name: %s"), NULL, EC_MISC);
for (mx = daemon->mxnames; mx; mx = mx->next) for (mx = daemon->mxnames; mx; mx = mx->next)
if (!mx->issrv && hostname_isequal(mx->name, buff)) if (!mx->issrv && hostname_isequal(mx->name, buff))
@@ -2276,7 +2338,7 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts)
daemon->resolv_files && daemon->resolv_files &&
daemon->resolv_files->next && daemon->resolv_files->next &&
(daemon->options & OPT_NO_POLL)) (daemon->options & OPT_NO_POLL))
die(_("only one resolv.conf file allowed in no-poll mode."), NULL); die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
if (daemon->options & OPT_RESOLV_DOMAIN) if (daemon->options & OPT_RESOLV_DOMAIN)
{ {
@@ -2286,10 +2348,10 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts)
if ((daemon->options & OPT_NO_RESOLV) || if ((daemon->options & OPT_NO_RESOLV) ||
!daemon->resolv_files || !daemon->resolv_files ||
(daemon->resolv_files)->next) (daemon->resolv_files)->next)
die(_("must have exactly one resolv.conf to read domain from."), NULL); die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
if (!(f = fopen((daemon->resolv_files)->name, "r"))) if (!(f = fopen((daemon->resolv_files)->name, "r")))
die(_("failed to read %s: %s"), (daemon->resolv_files)->name); die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
while ((line = fgets(buff, MAXDNAME, f))) while ((line = fgets(buff, MAXDNAME, f)))
{ {
@@ -2307,7 +2369,7 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts)
fclose(f); fclose(f);
if (!daemon->domain_suffix) if (!daemon->domain_suffix)
die(_("no search directive found in %s"), (daemon->resolv_files)->name); die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
} }
if (daemon->domain_suffix) if (daemon->domain_suffix)
@@ -2327,8 +2389,6 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts)
srv->name = safe_string_alloc(buff); srv->name = safe_string_alloc(buff);
} }
} }
return daemon;
} }

View File

@@ -304,10 +304,10 @@ static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen
static unsigned char *skip_questions(HEADER *header, size_t plen) static unsigned char *skip_questions(HEADER *header, size_t plen)
{ {
int q, qdcount = ntohs(header->qdcount); int q;
unsigned char *ansp = (unsigned char *)(header+1); unsigned char *ansp = (unsigned char *)(header+1);
for (q = 0; q<qdcount; q++) for (q = ntohs(header->qdcount); q != 0; q--)
{ {
if (!(ansp = skip_name(ansp, header, plen))) if (!(ansp = skip_name(ansp, header, plen)))
return NULL; return NULL;
@@ -349,7 +349,7 @@ unsigned int questions_crc(HEADER *header, size_t plen, char *name)
unsigned int crc = 0xffffffff; unsigned int crc = 0xffffffff;
unsigned char *p1, *p = (unsigned char *)(header+1); unsigned char *p1, *p = (unsigned char *)(header+1);
for (q = 0; q < ntohs(header->qdcount); q++) for (q = ntohs(header->qdcount); q != 0; q--)
{ {
if (!extract_name(header, plen, &p, name, 1)) if (!extract_name(header, plen, &p, name, 1))
return crc; /* bad packet */ return crc; /* bad packet */
@@ -426,7 +426,7 @@ unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsi
if (header->opcode == QUERY) if (header->opcode == QUERY)
{ {
for (i = 0; i < ntohs(header->qdcount); i++) for (i = ntohs(header->qdcount); i != 0; i--)
{ {
if (!(ansp = skip_name(ansp, header, plen))) if (!(ansp = skip_name(ansp, header, plen)))
return NULL; return NULL;
@@ -496,21 +496,8 @@ static int private_net(struct in_addr addr)
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ || ((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ; ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
} }
static void dns_doctor(HEADER *header, struct doctor *doctor, struct in_addr *addr)
{
for (; doctor; doctor = doctor->next)
if (is_same_net(doctor->in, *addr, doctor->mask))
{
addr->s_addr &= ~doctor->mask.s_addr;
addr->s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
/* Since we munged the data, the server it came from is no longer authoritative */
header->aa = 0;
break;
}
}
static int find_soa(HEADER *header, struct doctor *doctor, size_t qlen) static int find_soa(HEADER *header, size_t qlen)
{ {
unsigned char *p; unsigned char *p;
int qtype, qclass, rdlen; int qtype, qclass, rdlen;
@@ -522,7 +509,7 @@ static int find_soa(HEADER *header, struct doctor *doctor, size_t qlen)
!(p = skip_section(p, ntohs(header->ancount), header, qlen))) !(p = skip_section(p, ntohs(header->ancount), header, qlen)))
return 0; /* bad packet */ return 0; /* bad packet */
for (i=0; i<ntohs(header->nscount); i++) for (i = ntohs(header->nscount); i != 0; i--)
{ {
if (!(p = skip_name(p, header, qlen))) if (!(p = skip_name(p, header, qlen)))
return 0; /* bad packet */ return 0; /* bad packet */
@@ -557,8 +544,8 @@ static int find_soa(HEADER *header, struct doctor *doctor, size_t qlen)
return 0; /* bad packet */ return 0; /* bad packet */
} }
if (doctor) if (daemon->doctors)
for (i=0; i<ntohs(header->arcount); i++) for (i = ntohs(header->arcount); i != 0; i--)
{ {
if (!(p = skip_name(p, header, qlen))) if (!(p = skip_name(p, header, qlen)))
return 0; /* bad packet */ return 0; /* bad packet */
@@ -569,7 +556,24 @@ static int find_soa(HEADER *header, struct doctor *doctor, size_t qlen)
GETSHORT(rdlen, p); GETSHORT(rdlen, p);
if ((qclass == C_IN) && (qtype == T_A)) if ((qclass == C_IN) && (qtype == T_A))
dns_doctor(header, doctor, (struct in_addr *)p); {
struct doctor *doctor;
struct in_addr addr;
/* alignment */
memcpy(&addr, p, INADDRSZ);
for (doctor = daemon->doctors; doctor; doctor = doctor->next)
if (is_same_net(doctor->in, addr, doctor->mask))
{
addr.s_addr &= ~doctor->mask.s_addr;
addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
/* Since we munged the data, the server it came from is no longer authoritative */
header->aa = 0;
memcpy(p, &addr, INADDRSZ);
break;
}
}
p += rdlen; p += rdlen;
@@ -583,11 +587,12 @@ static int find_soa(HEADER *header, struct doctor *doctor, size_t qlen)
/* Note that the following code can create CNAME chains that don't point to a real record, /* Note that the following code can create CNAME chains that don't point to a real record,
either because of lack of memory, or lack of SOA records. These are treated by the cache code as either because of lack of memory, or lack of SOA records. These are treated by the cache code as
expired and cleaned out that way. */ expired and cleaned out that way. */
void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, struct daemon *daemon) void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
{ {
unsigned char *p, *p1, *endrr; unsigned char *p, *p1, *endrr;
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0; int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
unsigned long ttl = 0; unsigned long ttl = 0;
struct all_addr addr;
cache_start_insert(); cache_start_insert();
@@ -595,13 +600,13 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
if (daemon->doctors) if (daemon->doctors)
{ {
searched_soa = 1; searched_soa = 1;
ttl = find_soa(header, daemon->doctors, qlen); ttl = find_soa(header, qlen);
} }
/* go through the questions. */ /* go through the questions. */
p = (unsigned char *)(header+1); p = (unsigned char *)(header+1);
for (i = 0; i<ntohs(header->qdcount); i++) for (i = ntohs(header->qdcount); i != 0; i--)
{ {
int found = 0, cname_count = 5; int found = 0, cname_count = 5;
struct crec *cpp = NULL; struct crec *cpp = NULL;
@@ -621,7 +626,6 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
represent them in the cache. */ represent them in the cache. */
if (qtype == T_PTR) if (qtype == T_PTR)
{ {
struct all_addr addr;
int name_encoding = in_arpa_name_2_addr(name, &addr); int name_encoding = in_arpa_name_2_addr(name, &addr);
if (!name_encoding) if (!name_encoding)
@@ -633,7 +637,7 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
if (!(p1 = skip_questions(header, qlen))) if (!(p1 = skip_questions(header, qlen)))
return; return;
for (j = 0; j<ntohs(header->ancount); j++) for (j = ntohs(header->ancount); j != 0; j--)
{ {
if (!(res = extract_name(header, qlen, &p1, name, 0))) if (!(res = extract_name(header, qlen, &p1, name, 0)))
return; /* bad packet */ return; /* bad packet */
@@ -675,22 +679,29 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
if (!searched_soa) if (!searched_soa)
{ {
searched_soa = 1; searched_soa = 1;
ttl = find_soa(header, NULL, qlen); ttl = find_soa(header, qlen);
} }
if (ttl) if (ttl)
cache_insert(name, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags); cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);
} }
} }
else else
{ {
/* everything other than PTR */ /* everything other than PTR */
struct crec *newc; struct crec *newc;
int addrlen;
if (qtype == T_A) if (qtype == T_A)
flags |= F_IPV4; {
addrlen = INADDRSZ;
flags |= F_IPV4;
}
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else if (qtype == T_AAAA) else if (qtype == T_AAAA)
flags |= F_IPV6; {
addrlen = IN6ADDRSZ;
flags |= F_IPV6;
}
#endif #endif
else else
continue; continue;
@@ -701,7 +712,7 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
if (!(p1 = skip_questions(header, qlen))) if (!(p1 = skip_questions(header, qlen)))
return; return;
for (j = 0; j<ntohs(header->ancount); j++) for (j = ntohs(header->ancount); j != 0; j--)
{ {
if (!(res = extract_name(header, qlen, &p1, name, 0))) if (!(res = extract_name(header, qlen, &p1, name, 0)))
return; /* bad packet */ return; /* bad packet */
@@ -736,9 +747,9 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
else else
{ {
found = 1; found = 1;
if (aqtype == T_A) /* copy address into aligned storage */
dns_doctor(header, daemon->doctors, (struct in_addr *)p1); memcpy(&addr, p1, addrlen);
newc = cache_insert(name, (struct all_addr *)p1, now, attl, flags | F_FORWARD); newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
if (newc && cpp) if (newc && cpp)
{ {
cpp->addr.cname.cache = newc; cpp->addr.cname.cache = newc;
@@ -759,13 +770,13 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
if (!searched_soa) if (!searched_soa)
{ {
searched_soa = 1; searched_soa = 1;
ttl = find_soa(header, NULL, qlen); ttl = find_soa(header, qlen);
} }
/* If there's no SOA to get the TTL from, but there is a CNAME /* If there's no SOA to get the TTL from, but there is a CNAME
pointing at this, inherit it's TTL */ pointing at this, inherit it's TTL */
if (ttl || cpp) if (ttl || cpp)
{ {
newc = cache_insert(name, (struct all_addr *)p, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags); newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);
if (newc && cpp) if (newc && cpp)
{ {
cpp->addr.cname.cache = newc; cpp->addr.cname.cache = newc;
@@ -860,7 +871,7 @@ size_t setup_reply(HEADER *header, size_t qlen,
} }
/* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */ /* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
int check_for_local_domain(char *name, time_t now, struct daemon *daemon) int check_for_local_domain(char *name, time_t now)
{ {
struct crec *crecp; struct crec *crecp;
struct mx_srv_record *mx; struct mx_srv_record *mx;
@@ -906,7 +917,7 @@ int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
if (!(p = skip_questions(header, qlen))) if (!(p = skip_questions(header, qlen)))
return 0; /* bad packet */ return 0; /* bad packet */
for (i=0; i<ntohs(header->ancount); i++) for (i = ntohs(header->ancount); i != 0; i--)
{ {
if (!extract_name(header, qlen, &p, name, 1)) if (!extract_name(header, qlen, &p, name, 1))
return 0; /* bad packet */ return 0; /* bad packet */
@@ -1019,7 +1030,7 @@ static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigne
} }
/* return zero if we can't answer from cache, or packet size if we can */ /* return zero if we can't answer from cache, or packet size if we can */
size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *daemon, size_t answer_request(HEADER *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask, time_t now) struct in_addr local_addr, struct in_addr local_netmask, time_t now)
{ {
char *name = daemon->namebuff; char *name = daemon->namebuff;
@@ -1028,7 +1039,6 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
struct all_addr addr; struct all_addr addr;
unsigned int nameoffset; unsigned int nameoffset;
unsigned short flag; unsigned short flag;
int qdcount = ntohs(header->qdcount);
int q, ans, anscount = 0, addncount = 0; int q, ans, anscount = 0, addncount = 0;
int dryrun = 0, sec_reqd = 0; int dryrun = 0, sec_reqd = 0;
int is_sign; int is_sign;
@@ -1063,7 +1073,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
dryrun = 1; dryrun = 1;
} }
if (!qdcount || header->opcode != QUERY ) if (ntohs(header->qdcount) == 0 || header->opcode != QUERY )
return 0; return 0;
for (rec = daemon->mxnames; rec; rec = rec->next) for (rec = daemon->mxnames; rec; rec = rec->next)
@@ -1077,7 +1087,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
/* now process each question, answers go in RRs after the question */ /* now process each question, answers go in RRs after the question */
p = (unsigned char *)(header+1); p = (unsigned char *)(header+1);
for (q=0; q<qdcount; q++) for (q = ntohs(header->qdcount); q != 0; q--)
{ {
/* save pointer to name for copying into answers */ /* save pointer to name for copying into answers */
nameoffset = p - (unsigned char *)header; nameoffset = p - (unsigned char *)header;
@@ -1127,8 +1137,13 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
if (is_arpa == F_IPV4) if (is_arpa == F_IPV4)
for (intr = daemon->int_names; intr; intr = intr->next) for (intr = daemon->int_names; intr; intr = intr->next)
if (addr.addr.addr4.s_addr == get_ifaddr(daemon, intr->intr).s_addr) {
break; if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr)
break;
else
while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
intr = intr->next;
}
if (intr) if (intr)
{ {
@@ -1252,7 +1267,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
ans = 1; ans = 1;
if (!dryrun) if (!dryrun)
{ {
if ((addr.addr.addr4 = get_ifaddr(daemon, intr->intr)).s_addr == (in_addr_t) -1) if ((addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr == (in_addr_t) -1)
log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, 0, NULL, 0); log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, 0, NULL, 0);
else else
{ {

View File

@@ -74,17 +74,16 @@ static void option_put_string(struct dhcp_packet *mess, unsigned char *end,
int opt, char *string, int null_term); int opt, char *string, int null_term);
static struct in_addr option_addr(unsigned char *opt); static struct in_addr option_addr(unsigned char *opt);
static unsigned int option_uint(unsigned char *opt, int size); static unsigned int option_uint(unsigned char *opt, int size);
static void log_packet(struct daemon *daemon, char *type, void *addr, static void log_packet(char *type, void *addr,
unsigned char *ext_mac, int mac_len, char *interface, char *string); unsigned char *ext_mac, int mac_len, char *interface, char *string);
static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize); static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize); static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
static size_t dhcp_packet_size(struct daemon *daemon, struct dhcp_packet *mess, struct dhcp_netid *netid); static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid);
static void clear_packet(struct dhcp_packet *mess, unsigned char *end); static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
static void do_options(struct dhcp_context *context, static void do_options(struct dhcp_context *context,
struct dhcp_packet *mess, struct dhcp_packet *mess,
unsigned char *real_end, unsigned char *real_end,
unsigned char *req_options, unsigned char *req_options,
struct daemon *daemon,
char *hostname, char *hostname,
struct dhcp_netid *netid, struct dhcp_netid *netid,
struct in_addr subnet_addr, struct in_addr subnet_addr,
@@ -96,8 +95,8 @@ static unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwad
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *iface_name, size_t dhcp_reply(struct dhcp_context *context, char *iface_name,
size_t sz, time_t now, int unicast_dest) size_t sz, time_t now, int unicast_dest, int *is_inform)
{ {
unsigned char *opt, *clid = NULL; unsigned char *opt, *clid = NULL;
struct dhcp_lease *ltmp, *lease = NULL; struct dhcp_lease *ltmp, *lease = NULL;
@@ -121,6 +120,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
unsigned char *agent_id = NULL; unsigned char *agent_id = NULL;
unsigned char *emac = NULL; unsigned char *emac = NULL;
int emac_len; int emac_len;
struct dhcp_netid known_id;
subnet_addr.s_addr = 0; subnet_addr.s_addr = 0;
@@ -324,6 +324,14 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
config = find_config(daemon->dhcp_conf, context, clid, clid_len, config = find_config(daemon->dhcp_conf, context, clid, clid_len,
mess->chaddr, mess->hlen, mess->htype, NULL); mess->chaddr, mess->hlen, mess->htype, NULL);
/* set "known" tag for known hosts */
if (config)
{
known_id.net = "known";
known_id.next = netid;
netid = &known_id;
}
if (mess_type == 0) if (mess_type == 0)
{ {
@@ -394,7 +402,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
lease_prune(lease, now); lease_prune(lease, now);
lease = NULL; lease = NULL;
} }
if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now)) if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now))
message = _("no address available"); message = _("no address available");
} }
else else
@@ -428,14 +436,14 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
lease_set_expires(lease, 0xffffffff, now); /* infinite lease */ lease_set_expires(lease, 0xffffffff, now); /* infinite lease */
clear_packet(mess, end); clear_packet(mess, end);
do_options(context, mess, end, NULL, daemon, do_options(context, mess, end, NULL,
hostname, netid, subnet_addr, 0, 0, NULL); hostname, netid, subnet_addr, 0, 0, NULL);
} }
} }
log_packet(daemon, NULL, logaddr, mess->chaddr, mess->hlen, iface_name, message); log_packet(NULL, logaddr, mess->chaddr, mess->hlen, iface_name, message);
return message ? 0 : dhcp_packet_size(daemon, mess, netid); return message ? 0 : dhcp_packet_size(mess, netid);
} }
if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4))) if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
@@ -504,16 +512,27 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname)) if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
offer_hostname = hostname; offer_hostname = hostname;
} }
else if (client_hostname && (hostname = strip_hostname(daemon, client_hostname)) && !config) else if (client_hostname)
{ {
/* Search again now we have a hostname. char *d = strip_hostname(client_hostname);
Only accept configs without CLID and HWADDR here, (they won't match) if (d)
to avoid impersonation by name. */ my_syslog(LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), d, client_hostname);
struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
mess->chaddr, mess->hlen, if (strlen(client_hostname) != 0)
mess->htype, hostname); {
if (!have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR)) hostname = client_hostname;
config = new; if (!config)
{
/* Search again now we have a hostname.
Only accept configs without CLID and HWADDR here, (they won't match)
to avoid impersonation by name. */
struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
mess->chaddr, mess->hlen,
mess->htype, hostname);
if (!have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR))
config = new;
}
}
} }
if (have_config(config, CONFIG_NETID)) if (have_config(config, CONFIG_NETID))
@@ -610,7 +629,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ))) if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
return 0; return 0;
log_packet(daemon, "DECLINE", option_ptr(opt), emac, emac_len, iface_name, daemon->dhcp_buff); log_packet("DECLINE", option_ptr(opt), emac, emac_len, iface_name, daemon->dhcp_buff);
if (lease && lease->addr.s_addr == option_addr(opt).s_addr) if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
lease_prune(lease, now); lease_prune(lease, now);
@@ -642,7 +661,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
else else
message = _("unknown lease"); message = _("unknown lease");
log_packet(daemon, "RELEASE", &mess->ciaddr, emac, emac_len, iface_name, message); log_packet("RELEASE", &mess->ciaddr, emac, emac_len, iface_name, message);
return 0; return 0;
@@ -670,7 +689,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len, unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len); ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
my_syslog(LOG_WARNING, _("not using configured address %s because it is leased to %s"), my_syslog(LOG_WARNING, _("not using configured address %s because it is leased to %s"),
addrs, print_mac(daemon, mac, len)); addrs, print_mac(daemon->namebuff, mac, len));
} }
else else
{ {
@@ -695,16 +714,18 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
else if (opt && address_available(context, addr) && !lease_find_by_addr(addr) && else if (opt && address_available(context, addr) && !lease_find_by_addr(addr) &&
!config_find_by_address(daemon->dhcp_conf, addr)) !config_find_by_address(daemon->dhcp_conf, addr))
mess->yiaddr = addr; mess->yiaddr = addr;
else if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now)) else if (emac_len == 0)
message = _("no unique-id");
else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, netid, now))
message = _("no address available"); message = _("no address available");
} }
log_packet(daemon, "DISCOVER", opt ? option_ptr(opt) : NULL, emac, emac_len, iface_name, message); log_packet("DISCOVER", opt ? option_ptr(opt) : NULL, emac, emac_len, iface_name, message);
if (message || !(context = narrow_context(context, mess->yiaddr))) if (message || !(context = narrow_context(context, mess->yiaddr)))
return 0; return 0;
log_packet(daemon, "OFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL); log_packet("OFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL);
if (context->netid.net) if (context->netid.net)
{ {
@@ -723,10 +744,10 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
option_put(mess, end, OPTION_T1, 4, (time/2)); option_put(mess, end, OPTION_T1, 4, (time/2));
option_put(mess, end, OPTION_T2, 4, (time*7)/8); option_put(mess, end, OPTION_T2, 4, (time*7)/8);
} }
do_options(context, mess, end, req_options, daemon, offer_hostname, do_options(context, mess, end, req_options, offer_hostname,
netid, subnet_addr, fqdn_flags, borken_opt, agent_id); netid, subnet_addr, fqdn_flags, borken_opt, agent_id);
return dhcp_packet_size(daemon, mess, netid); return dhcp_packet_size(mess, netid);
case DHCPREQUEST: case DHCPREQUEST:
if (ignore || have_config(config, CONFIG_DISABLE)) if (ignore || have_config(config, CONFIG_DISABLE))
@@ -787,7 +808,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
mess->yiaddr = mess->ciaddr; mess->yiaddr = mess->ciaddr;
} }
log_packet(daemon, "REQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL); log_packet("REQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL);
if (!message) if (!message)
{ {
@@ -830,7 +851,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
else if ((ltmp = lease_find_by_addr(mess->yiaddr)) && ltmp != lease) else if ((ltmp = lease_find_by_addr(mess->yiaddr)) && ltmp != lease)
message = _("address in use"); message = _("address in use");
else if (!clid && mess->hlen == 0) else if (emac_len == 0)
message = _("no unique-id"); message = _("no unique-id");
else if (!lease) else if (!lease)
@@ -844,7 +865,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
if (message) if (message)
{ {
log_packet(daemon, "NAK", &mess->yiaddr, emac, emac_len, iface_name, message); log_packet("NAK", &mess->yiaddr, emac, emac_len, iface_name, message);
mess->yiaddr.s_addr = 0; mess->yiaddr.s_addr = 0;
clear_packet(mess, end); clear_packet(mess, end);
@@ -874,9 +895,8 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
/* If the user-class option started as counted strings, the first byte will be zero. */ /* If the user-class option started as counted strings, the first byte will be zero. */
if (len != 0 && ucp[0] == 0) if (len != 0 && ucp[0] == 0)
ucp++, len--; ucp++, len--;
if (lease->userclass) free(lease->userclass);
free(lease->userclass); if ((lease->userclass = whine_malloc(len+1)))
if ((lease->userclass = malloc(len+1)))
{ {
memcpy(lease->userclass, ucp, len); memcpy(lease->userclass, ucp, len);
lease->userclass[len] = 0; lease->userclass[len] = 0;
@@ -887,9 +907,8 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
{ {
int len = option_len(opt); int len = option_len(opt);
unsigned char *ucp = option_ptr(opt); unsigned char *ucp = option_ptr(opt);
if (lease->vendorclass) free(lease->vendorclass);
free(lease->vendorclass); if ((lease->vendorclass = whine_malloc(len+1)))
if ((lease->vendorclass = malloc(len+1)))
{ {
memcpy(lease->vendorclass, ucp, len); memcpy(lease->vendorclass, ucp, len);
lease->vendorclass[len] = 0; lease->vendorclass[len] = 0;
@@ -898,7 +917,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
} }
} }
if (!hostname_auth && (client_hostname = host_from_dns(daemon, mess->yiaddr))) if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
{ {
hostname = client_hostname; hostname = client_hostname;
hostname_auth = 1; hostname_auth = 1;
@@ -927,7 +946,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
lease_set_expires(lease, time, now); lease_set_expires(lease, time, now);
log_packet(daemon, "ACK", &mess->yiaddr, emac, emac_len, iface_name, hostname); log_packet("ACK", &mess->yiaddr, emac, emac_len, iface_name, hostname);
clear_packet(mess, end); clear_packet(mess, end);
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK); option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
@@ -940,24 +959,34 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz); option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz); option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
} }
do_options(context, mess, end, req_options, daemon, hostname, do_options(context, mess, end, req_options, hostname,
netid, subnet_addr, fqdn_flags, borken_opt, agent_id); netid, subnet_addr, fqdn_flags, borken_opt, agent_id);
} }
return dhcp_packet_size(daemon, mess, netid); return dhcp_packet_size(mess, netid);
case DHCPINFORM: case DHCPINFORM:
if (ignore || have_config(config, CONFIG_DISABLE)) if (ignore || have_config(config, CONFIG_DISABLE))
message = _("ignored"); message = _("ignored");
log_packet(daemon, "INFORM", &mess->ciaddr, emac, emac_len, iface_name, message); log_packet("INFORM", &mess->ciaddr, emac, emac_len, iface_name, message);
if (message || mess->ciaddr.s_addr == 0 || if (message || mess->ciaddr.s_addr == 0 ||
!(context = narrow_context(context, mess->ciaddr))) !(context = narrow_context(context, mess->ciaddr)))
return 0; return 0;
log_packet(daemon, "ACK", &mess->ciaddr, emac, emac_len, iface_name, hostname); /* Find a least based on IP address if we didn't
get one from MAC address/client-d */
if (!lease &&
(lease = lease_find_by_addr(mess->ciaddr)) &&
lease->hostname)
hostname = lease->hostname;
if (!hostname)
hostname = host_from_dns(mess->ciaddr);
log_packet("ACK", &mess->ciaddr, emac, emac_len, iface_name, hostname);
if (context->netid.net) if (context->netid.net)
{ {
context->netid.next = netid; context->netid.next = netid;
@@ -967,12 +996,20 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
clear_packet(mess, end); clear_packet(mess, end);
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK); option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr)); option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
if (!hostname)
hostname = host_from_dns(daemon, mess->yiaddr); if (lease)
do_options(context, mess, end, req_options, daemon, hostname, {
if (lease->expires == 0)
time = 0xffffffff;
else
time = (unsigned int)difftime(lease->expires, now);
option_put(mess, end, OPTION_LEASE_TIME, 4, time);
}
do_options(context, mess, end, req_options, hostname,
netid, subnet_addr, fqdn_flags, borken_opt, agent_id); netid, subnet_addr, fqdn_flags, borken_opt, agent_id);
return dhcp_packet_size(daemon, mess, netid); *is_inform = 1; /* handle reply differently */
return dhcp_packet_size(mess, netid);
} }
return 0; return 0;
@@ -1064,8 +1101,8 @@ static int sanitise(unsigned char *opt, char *buf)
return 1; return 1;
} }
static void log_packet(struct daemon *daemon, char *type, void *addr, static void log_packet(char *type, void *addr, unsigned char *ext_mac,
unsigned char *ext_mac, int mac_len, char *interface, char *string) int mac_len, char *interface, char *string)
{ {
struct in_addr a; struct in_addr a;
@@ -1079,11 +1116,11 @@ static void log_packet(struct daemon *daemon, char *type, void *addr,
interface, interface,
addr ? inet_ntoa(a) : "", addr ? inet_ntoa(a) : "",
addr ? " " : "", addr ? " " : "",
print_mac(daemon, ext_mac, mac_len), print_mac(daemon->namebuff, ext_mac, mac_len),
string ? string : ""); string ? string : "");
} }
static void log_options(struct daemon *daemon, unsigned char *start) static void log_options(unsigned char *start)
{ {
while (*start != OPTION_END) while (*start != OPTION_END)
{ {
@@ -1093,7 +1130,7 @@ static void log_options(struct daemon *daemon, unsigned char *start)
start[1], start[0], start[1], start[0],
text ? ":" : "", text ? text : "", text ? ":" : "", text ? text : "",
start[1] == 0 ? "" : " ", start[1] == 0 ? "" : " ",
start[1] == 0 ? "" : print_mac(daemon, &start[2], trunc), start[1] == 0 ? "" : print_mac(daemon->namebuff, &start[2], trunc),
trunc == start[1] ? "" : "..."); trunc == start[1] ? "" : "...");
start += start[1] + 2; start += start[1] + 2;
} }
@@ -1194,7 +1231,7 @@ static unsigned char *find_overload(struct dhcp_packet *mess)
return NULL; return NULL;
} }
static size_t dhcp_packet_size(struct daemon *daemon, struct dhcp_packet *mess, struct dhcp_netid *netid) static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid)
{ {
unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32)); unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
unsigned char *overload; unsigned char *overload;
@@ -1222,19 +1259,19 @@ static size_t dhcp_packet_size(struct daemon *daemon, struct dhcp_packet *mess,
{ {
*dhcp_skip_opts(mess->file) = OPTION_END; *dhcp_skip_opts(mess->file) = OPTION_END;
if (daemon->options & OPT_LOG_OPTS) if (daemon->options & OPT_LOG_OPTS)
log_options(daemon, mess->file); log_options(mess->file);
} }
if (option_uint(overload, 1) & 2) if (option_uint(overload, 1) & 2)
{ {
*dhcp_skip_opts(mess->sname) = OPTION_END; *dhcp_skip_opts(mess->sname) = OPTION_END;
if (daemon->options & OPT_LOG_OPTS) if (daemon->options & OPT_LOG_OPTS)
log_options(daemon, mess->sname); log_options(mess->sname);
} }
} }
*p++ = OPTION_END; *p++ = OPTION_END;
if (daemon->options & OPT_LOG_OPTS) if (daemon->options & OPT_LOG_OPTS)
log_options(daemon, &mess->options[0] + sizeof(u32)); log_options(&mess->options[0] + sizeof(u32));
ret = (size_t)(p - (unsigned char *)mess); ret = (size_t)(p - (unsigned char *)mess);
@@ -1419,7 +1456,6 @@ static void do_options(struct dhcp_context *context,
struct dhcp_packet *mess, struct dhcp_packet *mess,
unsigned char *real_end, unsigned char *real_end,
unsigned char *req_options, unsigned char *req_options,
struct daemon *daemon,
char *hostname, char *hostname,
struct dhcp_netid *netid, struct dhcp_netid *netid,
struct in_addr subnet_addr, struct in_addr subnet_addr,

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2006 Simon Kelley /* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
static struct tftp_file *check_tftp_fileperm(struct daemon *daemon, ssize_t *len); static struct tftp_file *check_tftp_fileperm(ssize_t *len);
static void free_transfer(struct tftp_transfer *transfer); static void free_transfer(struct tftp_transfer *transfer);
static ssize_t tftp_err(int err, char *packet, char *mess, char *file); static ssize_t tftp_err(int err, char *packet, char *mess, char *file);
static ssize_t tftp_err_oops(char *packet, char *file); static ssize_t tftp_err_oops(char *packet, char *file);
@@ -34,7 +34,7 @@ static char *next(char **p, char *end);
#define ERR_FULL 3 #define ERR_FULL 3
#define ERR_ILL 4 #define ERR_ILL 4
void tftp_request(struct listener *listen, struct daemon *daemon, time_t now) void tftp_request(struct listener *listen, time_t now)
{ {
ssize_t len; ssize_t len;
char *packet = daemon->packet; char *packet = daemon->packet;
@@ -46,7 +46,7 @@ void tftp_request(struct listener *listen, struct daemon *daemon, time_t now)
struct ifreq ifr; struct ifreq ifr;
int is_err = 1, if_index = 0; int is_err = 1, if_index = 0;
struct iname *tmp; struct iname *tmp;
struct tftp_transfer *transfer, *t; struct tftp_transfer *transfer;
union { union {
struct cmsghdr align; /* this ensures alignment */ struct cmsghdr align; /* this ensures alignment */
@@ -106,7 +106,7 @@ void tftp_request(struct listener *listen, struct daemon *daemon, time_t now)
if (addr.sin_addr.s_addr == 0) if (addr.sin_addr.s_addr == 0)
return; return;
if (!iface_check(daemon, AF_INET, (struct all_addr *)&addr.sin_addr, if (!iface_check(AF_INET, (struct all_addr *)&addr.sin_addr,
&ifr, &if_index)) &ifr, &if_index))
return; return;
@@ -124,7 +124,7 @@ void tftp_request(struct listener *listen, struct daemon *daemon, time_t now)
addr.sin_len = sizeof(addr); addr.sin_len = sizeof(addr);
#endif #endif
if (!(transfer = malloc(sizeof(struct tftp_transfer)))) if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
return; return;
if ((transfer->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) if ((transfer->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
@@ -134,7 +134,7 @@ void tftp_request(struct listener *listen, struct daemon *daemon, time_t now)
} }
transfer->peer = peer; transfer->peer = peer;
transfer->timeout = now + 1; transfer->timeout = now + 2;
transfer->backoff = 1; transfer->backoff = 1;
transfer->block = 1; transfer->block = 1;
transfer->blocksize = 512; transfer->blocksize = 512;
@@ -188,7 +188,20 @@ void tftp_request(struct listener *listen, struct daemon *daemon, time_t now)
strncat(daemon->namebuff, daemon->tftp_prefix, MAXDNAME); strncat(daemon->namebuff, daemon->tftp_prefix, MAXDNAME);
if (daemon->tftp_prefix[strlen(daemon->tftp_prefix)-1] != '/') if (daemon->tftp_prefix[strlen(daemon->tftp_prefix)-1] != '/')
strncat(daemon->namebuff, "/", MAXDNAME); strncat(daemon->namebuff, "/", MAXDNAME);
if (daemon->options & OPT_TFTP_APREF)
{
size_t oldlen = strlen(daemon->namebuff);
struct stat statbuf;
strncat(daemon->namebuff, inet_ntoa(peer.sin_addr), MAXDNAME);
strncat(daemon->namebuff, "/", MAXDNAME);
/* remove unique-directory if it doesn't exist */
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
daemon->namebuff[oldlen] = 0;
}
/* Absolute pathnames OK if they match prefix */ /* Absolute pathnames OK if they match prefix */
if (filename[0] == '/') if (filename[0] == '/')
{ {
@@ -203,24 +216,8 @@ void tftp_request(struct listener *listen, struct daemon *daemon, time_t now)
strncat(daemon->namebuff, filename, MAXDNAME); strncat(daemon->namebuff, filename, MAXDNAME);
daemon->namebuff[MAXDNAME-1] = 0; daemon->namebuff[MAXDNAME-1] = 0;
/* If we're doing many tranfers from the same file, only /* check permissions and open file */
open it once this saves lots of file descriptors if ((transfer->file = check_tftp_fileperm(&len)))
when mass-booting a big cluster, for instance. */
for (t = daemon->tftp_trans; t; t = t->next)
if (strcmp(t->file->filename, daemon->namebuff) == 0)
break;
if (t)
{
/* file already open */
transfer->file = t->file;
transfer->file->refcount++;
}
else
/* check permissions and open file */
transfer->file = check_tftp_fileperm(daemon, &len);
if (transfer->file)
{ {
if ((len = get_block(packet, transfer)) == -1) if ((len = get_block(packet, transfer)) == -1)
len = tftp_err_oops(packet, daemon->namebuff); len = tftp_err_oops(packet, daemon->namebuff);
@@ -242,80 +239,90 @@ void tftp_request(struct listener *listen, struct daemon *daemon, time_t now)
} }
} }
static struct tftp_file *check_tftp_fileperm(struct daemon *daemon, ssize_t *len) static struct tftp_file *check_tftp_fileperm(ssize_t *len)
{ {
char *packet = daemon->packet, *namebuff = daemon->namebuff; char *packet = daemon->packet, *namebuff = daemon->namebuff;
struct tftp_file *file; struct tftp_file *file;
struct tftp_transfer *t;
uid_t uid = geteuid(); uid_t uid = geteuid();
struct stat statbuf; struct stat statbuf;
int fd = -1;
/* trick to ban moving out of the subtree */ /* trick to ban moving out of the subtree */
if (daemon->tftp_prefix && strstr(namebuff, "/../")) if (daemon->tftp_prefix && strstr(namebuff, "/../"))
goto perm;
if ((fd = open(namebuff, O_RDONLY)) == -1)
{ {
errno = EACCES; if (errno == ENOENT)
goto perm; {
} *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
return NULL;
if (stat(namebuff, &statbuf) == -1) }
{
if (errno == ENOENT || errno == ENOTDIR)
goto nofile;
else if (errno == EACCES) else if (errno == EACCES)
goto perm; goto perm;
else else
goto oops; goto oops;
} }
/* stat the file descriptor to avoid stat->open races */
if (fstat(fd, &statbuf) == -1)
goto oops;
/* running as root, must be world-readable */ /* running as root, must be world-readable */
if (uid == 0) if (uid == 0)
{ {
if (!(statbuf.st_mode & S_IROTH)) if (!(statbuf.st_mode & S_IROTH))
{ goto perm;
errno = EACCES;
goto perm;
}
} }
/* in secure mode, must be owned by user running dnsmasq */ /* in secure mode, must be owned by user running dnsmasq */
else if ((daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid) else if ((daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid)
{ goto perm;
errno = EACCES;
goto perm; /* If we're doing many tranfers from the same file, only
} open it once this saves lots of file descriptors
when mass-booting a big cluster, for instance.
if (!(file = malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1))) Be conservative and only share when inode and name match
this keeps error messages sane. */
for (t = daemon->tftp_trans; t; t = t->next)
if (t->file->dev == statbuf.st_dev &&
t->file->inode == statbuf.st_ino &&
strcmp(t->file->filename, namebuff) == 0)
{
close(fd);
t->file->refcount++;
return t->file;
}
if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1)))
{ {
errno = ENOMEM; errno = ENOMEM;
goto oops; goto oops;
} }
if ((file->fd = open(namebuff, O_RDONLY)) == -1) file->fd = fd;
{
free(file);
if (errno == EACCES || errno == EISDIR)
goto perm;
else
goto oops;
}
file->size = statbuf.st_size; file->size = statbuf.st_size;
file->dev = statbuf.st_dev;
file->inode = statbuf.st_ino;
file->refcount = 1; file->refcount = 1;
strcpy(file->filename, namebuff); strcpy(file->filename, namebuff);
return file; return file;
nofile:
*len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
return NULL;
perm: perm:
errno = EACCES;
*len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff); *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff);
if (fd != -1)
close(fd);
return NULL; return NULL;
oops: oops:
*len = tftp_err_oops(packet, namebuff); *len = tftp_err_oops(packet, namebuff);
if (fd != -1)
close(fd);
return NULL; return NULL;
} }
void check_tftp_listeners(struct daemon *daemon, fd_set *rset, time_t now) void check_tftp_listeners(fd_set *rset, time_t now)
{ {
struct tftp_transfer *transfer, *tmp, **up; struct tftp_transfer *transfer, *tmp, **up;
ssize_t len; ssize_t len;
@@ -375,7 +382,7 @@ void check_tftp_listeners(struct daemon *daemon, fd_set *rset, time_t now)
int endcon = 0; int endcon = 0;
/* timeout, retransmit */ /* timeout, retransmit */
transfer->timeout += 1<<(transfer->backoff); transfer->timeout += 1 + (1<<transfer->backoff);
/* we overwrote the buffer... */ /* we overwrote the buffer... */
daemon->srv_save = NULL; daemon->srv_save = NULL;

View File

@@ -109,6 +109,7 @@ int canonicalise(char *s)
also fail empty string and label > 63 chars */ also fail empty string and label > 63 chars */
size_t dotgap = 0, l = strlen(s); size_t dotgap = 0, l = strlen(s);
char c; char c;
int nowhite = 0;
if (l == 0 || l > MAXDNAME) return 0; if (l == 0 || l > MAXDNAME) return 0;
@@ -124,9 +125,11 @@ int canonicalise(char *s)
dotgap = 0; dotgap = 0;
else if (!legal_char(c) || (++dotgap > MAXLABEL)) else if (!legal_char(c) || (++dotgap > MAXLABEL))
return 0; return 0;
else if (c != ' ')
nowhite = 1;
s++; s++;
} }
return 1; return nowhite;
} }
unsigned char *do_rfc1035_name(unsigned char *p, char *sval) unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
@@ -151,11 +154,21 @@ void *safe_malloc(size_t size)
void *ret = malloc(size); void *ret = malloc(size);
if (!ret) if (!ret)
die(_("could not get memory"), NULL); die(_("could not get memory"), NULL, EC_NOMEM);
return ret; return ret;
} }
void *whine_malloc(size_t size)
{
void *ret = malloc(size);
if (!ret)
my_syslog(LOG_ERR, _("failed to allocate %d bytes"), (int) size);
return ret;
}
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2) int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
{ {
if (s1->sa.sa_family == s2->sa.sa_family) if (s1->sa.sa_family == s2->sa.sa_family)
@@ -229,23 +242,6 @@ int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr); return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
} }
int retry_send(void)
{
struct timespec waiter;
if (errno == EAGAIN)
{
waiter.tv_sec = 0;
waiter.tv_nsec = 10000;
nanosleep(&waiter, NULL);
return 1;
}
if (errno == EINTR)
return 1;
return 0;
}
/* returns port number from address */ /* returns port number from address */
int prettyprint_addr(union mysockaddr *addr, char *buf) int prettyprint_addr(union mysockaddr *addr, char *buf)
{ {
@@ -351,7 +347,7 @@ int expand_buf(struct iovec *iov, size_t size)
if (size <= iov->iov_len) if (size <= iov->iov_len)
return 1; return 1;
if (!(new = malloc(size))) if (!(new = whine_malloc(size)))
{ {
errno = ENOMEM; errno = ENOMEM;
return 0; return 0;
@@ -369,9 +365,9 @@ int expand_buf(struct iovec *iov, size_t size)
return 1; return 1;
} }
char *print_mac(struct daemon *daemon, unsigned char *mac, int len) char *print_mac(char *buff, unsigned char *mac, int len)
{ {
char *p = daemon->namebuff; char *p = buff;
int i; int i;
if (len == 0) if (len == 0)
@@ -380,7 +376,7 @@ char *print_mac(struct daemon *daemon, unsigned char *mac, int len)
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":"); p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
return daemon->namebuff; return buff;
} }
void bump_maxfd(int fd, int *max) void bump_maxfd(int fd, int *max)
@@ -389,6 +385,23 @@ void bump_maxfd(int fd, int *max)
*max = fd; *max = fd;
} }
int retry_send(void)
{
struct timespec waiter;
if (errno == EAGAIN)
{
waiter.tv_sec = 0;
waiter.tv_nsec = 10000;
nanosleep(&waiter, NULL);
return 1;
}
if (errno == EINTR)
return 1;
return 0;
}
int read_write(int fd, unsigned char *packet, int size, int rw) int read_write(int fd, unsigned char *packet, int size, int rw)
{ {
ssize_t n, done; ssize_t n, done;
@@ -405,7 +418,7 @@ int read_write(int fd, unsigned char *packet, int size, int rw)
return 0; return 0;
else if (n == -1) else if (n == -1)
{ {
if (errno == EINTR || errno == ENOMEM || errno == ENOBUFS) if (retry_send() || errno == ENOMEM || errno == ENOBUFS)
goto retry; goto retry;
else else
return 0; return 0;