import of dnsmasq-2.34.tar.gz

This commit is contained in:
Simon Kelley
2006-10-16 20:04:18 +01:00
parent 208b65c5cf
commit 1697269ce7
33 changed files with 3660 additions and 2711 deletions

View File

@@ -20,6 +20,40 @@ static int bignames_left, log_queries, cache_size, hash_size;
static int uid;
static char *addrbuff;
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
static const struct {
unsigned int type;
const char * const name;
} typestr[] = {
{ 1, "A" },
{ 2, "NS" },
{ 5, "CNAME" },
{ 6, "SOA" },
{ 10, "NULL" },
{ 11, "WKS" },
{ 12, "PTR" },
{ 13, "HINFO" },
{ 15, "MX" },
{ 16, "TXT" },
{ 22, "NSAP" },
{ 23, "NSAP_PTR" },
{ 24, "SIG" },
{ 25, "KEY" },
{ 28, "AAAA" },
{ 33, "SRV" },
{ 36, "KX" },
{ 37, "CERT" },
{ 38, "A6" },
{ 39, "DNAME" },
{ 41, "OPT" },
{ 250, "TSIG" },
{ 251, "IXFR" },
{ 252, "AXFR" },
{ 253, "MAILB" },
{ 254, "MAILA" },
{ 255, "ANY" }
};
static void cache_free(struct crec *crecp);
static void cache_unlink(struct crec *crecp);
static void cache_link(struct crec *crecp);
@@ -66,17 +100,19 @@ void cache_init(int size, int logq)
static struct crec **hash_bucket(char *name)
{
unsigned int c, val = 0;
/* don't use tolower and friends here - they may be messed up by LOCALE */
unsigned int c, val = 017465; /* Barker code - minimum self-correlationin cyclic shift */
const unsigned char *mix_tab = (const unsigned char*)typestr;
while((c = (unsigned char) *name++))
if (c >= 'A' && c <= 'Z')
val += c + 'a' - 'A';
else
val += c;
{
/* don't use tolower and friends here - they may be messed up by LOCALE */
if (c >= 'A' && c <= 'Z')
c += 'a' - 'A';
val = ((val << 7) | (val >> (32 - 7))) + (mix_tab[(val + c) & 0x1F] ^ c);
}
/* hash_size is a power of two */
return hash_table + (val & (hash_size - 1));
return hash_table + ((val ^ (val >> 16)) & (hash_size - 1));
}
static void cache_hash(struct crec *crecp)
@@ -909,38 +945,6 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
else if (flags & F_QUERY)
{
unsigned int i;
static const struct {
unsigned int type;
const char * const name;
} typestr[] = {
{ 1, "A" },
{ 2, "NS" },
{ 5, "CNAME" },
{ 6, "SOA" },
{ 10, "NULL" },
{ 11, "WKS" },
{ 12, "PTR" },
{ 13, "HINFO" },
{ 15, "MX" },
{ 16, "TXT" },
{ 22, "NSAP" },
{ 23, "NSAP_PTR" },
{ 24, "SIG" },
{ 25, "KEY" },
{ 28, "AAAA" },
{ 33, "SRV" },
{ 36, "KX" },
{ 37, "CERT" },
{ 38, "A6" },
{ 39, "DNAME" },
{ 41, "OPT" },
{ 250, "TSIG" },
{ 251, "IXFR" },
{ 252, "AXFR" },
{ 253, "MAILB" },
{ 254, "MAILA" },
{ 255, "ANY" }
};
if (type != 0)
{

View File

@@ -10,15 +10,14 @@
GNU General Public License for more details.
*/
#define VERSION "2.33"
#define VERSION "2.34"
#define FTABSIZ 150 /* max number of outstanding requests (default) */
#define MAX_PROCS 20 /* max no children for TCP requests */
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
#define EDNS_PKTSZ 1280 /* default max EDNS.0 UDP packet from RFC2671 */
#define TIMEOUT 20 /* drop UDP queries after TIMEOUT seconds */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
#define LOGRATE 120 /* log table overflows every LOGRATE seconds */
#define CACHESIZ 150 /* default cache size */
#define MAXLEASES 150 /* maximum number of DHCP leases */
#define PING_WAIT 3 /* wait for ping address-in-use test */

View File

@@ -282,8 +282,8 @@ char *dbus_init(struct daemon *daemon)
}
int set_dbus_listeners(struct daemon *daemon, int maxfd,
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)
{
struct watch *w;
@@ -293,8 +293,7 @@ int set_dbus_listeners(struct daemon *daemon, int maxfd,
unsigned int flags = dbus_watch_get_flags(w->watch);
int fd = dbus_watch_get_fd(w->watch);
if (fd > maxfd)
maxfd = fd;
bump_maxfd(fd, maxfdp);
if (flags & DBUS_WATCH_READABLE)
FD_SET(fd, rset);
@@ -304,7 +303,6 @@ int set_dbus_listeners(struct daemon *daemon, int maxfd,
FD_SET(fd, eset);
}
return maxfd;
}
void check_dbus_listeners(struct daemon *daemon,

View File

@@ -138,7 +138,7 @@ void dhcp_packet(struct daemon *daemon, time_t now)
msg.msg_name = &dest;
msg.msg_namelen = sizeof(dest);
while ((sz = recvmsg(daemon->dhcpfd, &msg, 0)) && errno == EINTR);
while ((sz = recvmsg(daemon->dhcpfd, &msg, 0)) == -1 && errno == EINTR);
if ((msg.msg_flags & MSG_TRUNC) ||
sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
@@ -205,8 +205,7 @@ void dhcp_packet(struct daemon *daemon, time_t now)
iov.iov_len = dhcp_reply(daemon, parm.current, ifr.ifr_name, (size_t)sz, now, unicast_dest);
lease_update_file(daemon, now);
lease_update_dns(daemon);
lease_collect(daemon);
if (iov.iov_len == 0)
return;
@@ -625,7 +624,7 @@ void dhcp_read_ethers(struct daemon *daemon)
struct in_addr addr;
unsigned char hwaddr[ETHER_ADDR_LEN];
struct dhcp_config **up, *tmp;
struct dhcp_config *config, *configs = daemon->dhcp_conf;
struct dhcp_config *config;
int count = 0, lineno = 0;
addr.s_addr = 0; /* eliminate warning */
@@ -637,7 +636,7 @@ void dhcp_read_ethers(struct daemon *daemon)
}
/* This can be called again on SIGHUP, so remove entries created last time round. */
for (up = &daemon->dhcp_conf, config = configs; config; config = tmp)
for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
{
tmp = config->next;
if (config->flags & CONFIG_FROM_ETHERS)
@@ -686,7 +685,7 @@ void dhcp_read_ethers(struct daemon *daemon)
flags = CONFIG_ADDR;
for (config = configs; config; config = config->next)
for (config = daemon->dhcp_conf; config; config = config->next)
if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
break;
}
@@ -700,14 +699,14 @@ void dhcp_read_ethers(struct daemon *daemon)
flags = CONFIG_NAME;
for (config = configs; config; config = config->next)
for (config = daemon->dhcp_conf; config; config = config->next)
if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
break;
}
if (!config)
{
for (config = configs; config; config = config->next)
for (config = daemon->dhcp_conf; config; config = config->next)
if ((config->flags & CONFIG_HWADDR) &&
config->wildcard_mask == 0 &&
config->hwaddr_len == ETHER_ADDR_LEN &&
@@ -721,8 +720,8 @@ void dhcp_read_ethers(struct daemon *daemon)
continue;
config->flags = CONFIG_FROM_ETHERS;
config->wildcard_mask = 0;
config->next = configs;
configs = config;
config->next = daemon->dhcp_conf;
daemon->dhcp_conf = config;
}
config->flags |= flags;
@@ -749,8 +748,6 @@ void dhcp_read_ethers(struct daemon *daemon)
fclose(f);
syslog(LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
daemon->dhcp_conf = configs;
}
void dhcp_update_configs(struct dhcp_config *configs)

View File

@@ -24,6 +24,9 @@ static char *compile_opts =
#ifdef HAVE_BROKEN_RTC
"no-RTC "
#endif
#ifdef NO_FORK
"no-MMU "
#endif
#ifndef HAVE_ISC_READER
"no-"
#endif
@@ -40,7 +43,7 @@ static char *compile_opts =
static pid_t pid;
static int pipewrite;
static int set_dns_listeners(struct daemon *daemon, fd_set *set, int maxfd);
static int set_dns_listeners(struct daemon *daemon, time_t now, fd_set *set, int *maxfdp);
static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now);
static void sig_handler(int sig);
@@ -195,62 +198,18 @@ int main (int argc, char **argv)
/* prime the pipe to load stuff first time. */
sig = SIGHUP;
write(pipewrite, &sig, 1);
if (daemon->options & OPT_DEBUG)
{
#ifdef LOG_PERROR
openlog("dnsmasq", LOG_PERROR, daemon->log_fac);
#else
openlog("dnsmasq", 0, daemon->log_fac);
#endif
#ifdef HAVE_LINUX_NETWORK
prctl(PR_SET_DUMPABLE, 1);
#endif
}
else
if (!(daemon->options & OPT_DEBUG))
{
FILE *pidfile;
struct passwd *ent_pw = daemon->username ? getpwnam(daemon->username) : NULL;
fd_set test_set;
int maxfd, i;
int maxfd = -1, i;
int nullfd = open("/dev/null", O_RDWR);
#ifdef HAVE_LINUX_NETWORK
cap_user_header_t hdr = NULL;
cap_user_data_t data = NULL;
/* On linux, we keep CAP_NETADMIN (for ARP-injection) and
CAP_NET_RAW (for icmp) if we're doing dhcp */
if (ent_pw && ent_pw->pw_uid != 0)
{
hdr = safe_malloc(sizeof(*hdr));
data = safe_malloc(sizeof(*data));
hdr->version = _LINUX_CAPABILITY_VERSION;
hdr->pid = 0; /* this process */
data->effective = data->permitted = data->inheritable =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
(1 << CAP_SETGID) | (1 << CAP_SETUID);
/* Tell kernel to not clear capabilities when dropping root */
if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1) == -1)
{
bad_capabilities = errno;
ent_pw = NULL;
}
}
#endif
FD_ZERO(&test_set);
maxfd = set_dns_listeners(daemon, &test_set, -1);
#ifdef HAVE_DBUS
maxfd = set_dbus_listeners(daemon, maxfd, &test_set, &test_set, &test_set);
#endif
/* The following code "daemonizes" the process.
See Stevens section 12.4 */
#ifndef NO_FORK
#ifndef NO_FORK
if (!(daemon->options & OPT_NO_FORK))
{
if (fork() != 0 )
@@ -274,7 +233,12 @@ int main (int argc, char **argv)
}
umask(0);
FD_ZERO(&test_set);
set_dns_listeners(daemon, now, &test_set, &maxfd);
#ifdef HAVE_DBUS
set_dbus_listeners(daemon, &maxfd, &test_set, &test_set, &test_set);
#endif
for (i=0; i<64; i++)
{
if (i == piperead || i == pipewrite)
@@ -303,7 +267,16 @@ int main (int argc, char **argv)
else
close(i);
}
}
/* if we are to run scripts, we need to fork a helper before dropping root. */
daemon->helperfd = create_helper(daemon);
if (!(daemon->options & OPT_DEBUG))
{
/* UID changing, etc */
struct passwd *ent_pw = daemon->username ? getpwnam(daemon->username) : NULL;
if (daemon->groupname || ent_pw)
{
gid_t dummy;
@@ -318,32 +291,55 @@ int main (int argc, char **argv)
setgid(gp->gr_gid);
}
}
if (ent_pw && ent_pw->pw_uid != 0)
{
/* finally drop root */
setuid(ent_pw->pw_uid);
{
#ifdef HAVE_LINUX_NETWORK
data->effective = data->permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
data->inheritable = 0;
/* On linux, we keep CAP_NETADMIN (for ARP-injection) and
CAP_NET_RAW (for icmp) if we're doing dhcp */
cap_user_header_t hdr = safe_malloc(sizeof(*hdr));
cap_user_data_t data = safe_malloc(sizeof(*data));
hdr->version = _LINUX_CAPABILITY_VERSION;
hdr->pid = 0; /* this process */
data->effective = data->permitted = data->inheritable =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
(1 << CAP_SETGID) | (1 << CAP_SETUID);
/* lose the setuid and setgid capbilities */
capset(hdr, data);
/* Tell kernel to not clear capabilities when dropping root */
if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1) == -1)
bad_capabilities = errno;
else
#endif
{
/* finally drop root */
setuid(ent_pw->pw_uid);
#ifdef HAVE_LINUX_NETWORK
data->effective = data->permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
data->inheritable = 0;
/* lose the setuid and setgid capbilities */
capset(hdr, data);
#endif
}
}
openlog("dnsmasq", LOG_PID, daemon->log_fac);
}
log_start(daemon);
#ifdef HAVE_LINUX_NETWORK
if (daemon->options & OPT_DEBUG)
prctl(PR_SET_DUMPABLE, 1);
#endif
if (daemon->cachesize != 0)
syslog(LOG_INFO, _("started, version %s cachesize %d"), VERSION, daemon->cachesize);
else
syslog(LOG_INFO, _("started, version %s cache disabled"), VERSION);
syslog(LOG_INFO, _("compile time options: %s"), compile_opts);
#ifdef HAVE_DBUS
if (daemon->options & OPT_DBUS)
{
@@ -399,53 +395,59 @@ int main (int argc, char **argv)
pid = getpid();
/* Start lease-change script */
if (daemon->dhcp)
lease_collect(daemon);
while (1)
{
int maxfd;
int maxfd = -1;
struct timeval t, *tp = NULL;
fd_set rset, wset, eset;
t.tv_sec = 0; /* no warning */
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&eset);
maxfd = set_dns_listeners(daemon, &rset, -1);
/* 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
listening for queries */
if ((t.tv_sec = set_dns_listeners(daemon, now, &rset, &maxfd)) != 0)
{
t.tv_usec = 0;
tp = &t;
}
#ifdef HAVE_DBUS
/* Whilst polling for the dbus, wake every quarter second */
if ((daemon->options & OPT_DBUS) && !daemon->dbus)
{
t.tv_sec = 0;
t.tv_usec = 250000;
tp = &t;
tp->tv_sec = 0;
tp->tv_usec = 250000;
}
maxfd = set_dbus_listeners(daemon, maxfd, &rset, &wset, &eset);
set_dbus_listeners(daemon, &maxfd, &rset, &wset, &eset);
#endif
if (daemon->dhcp)
{
FD_SET(daemon->dhcpfd, &rset);
if (daemon->dhcpfd > maxfd)
maxfd = daemon->dhcpfd;
bump_maxfd(daemon->dhcpfd, &maxfd);
}
#ifdef HAVE_LINUX_NETWORK
FD_SET(daemon->netlinkfd, &rset);
if (daemon->netlinkfd > maxfd)
maxfd = daemon->netlinkfd;
bump_maxfd(daemon->netlinkfd, &maxfd);
#endif
FD_SET(piperead, &rset);
if (piperead > maxfd)
maxfd = piperead;
bump_maxfd(piperead, &maxfd);
while (helper_buf_empty() && do_script_run(daemon));
if (!helper_buf_empty())
{
FD_SET(daemon->helperfd, &wset);
bump_maxfd(daemon->helperfd, &maxfd);
}
if (select(maxfd+1, &rset, &wset, &eset, tp) < 0)
{
/* otherwise undefined after error */
@@ -502,6 +504,8 @@ int main (int argc, char **argv)
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
{
@@ -541,22 +545,33 @@ int main (int argc, char **argv)
{
lease_prune(NULL, now);
lease_update_file(daemon, now);
lease_collect(daemon);
}
break;
case SIGTERM:
{
int i;
syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
/* 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);
syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
exit(0);
}
@@ -568,22 +583,13 @@ int main (int argc, char **argv)
whose pid != script_pid are TCP server threads. */
while ((p = waitpid(-1, NULL, WNOHANG)) > 0)
{
if (p == daemon->script_pid)
{
daemon->script_pid = 0;
lease_collect(daemon);
}
else
{
int i;
for (i = 0 ; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] == p)
{
daemon->tcp_pids[i] = 0;
daemon->num_kids--;
break;
}
}
int i;
for (i = 0 ; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] == p)
{
daemon->tcp_pids[i] = 0;
break;
}
}
break;
}
@@ -611,6 +617,9 @@ int main (int argc, char **argv)
if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
dhcp_packet(daemon, now);
if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset))
helper_write(daemon);
}
}
@@ -618,7 +627,8 @@ static void sig_handler(int sig)
{
if (pid == 0)
{
/* ignore anything other than TERM during startup */
/* ignore anything other than TERM during startup
and in helper proc. (helper ignore TERM too) */
if (sig == SIGTERM)
exit(0);
}
@@ -632,7 +642,7 @@ static void sig_handler(int sig)
}
else
{
/* alarm is used to kill children after a fixed time. */
/* alarm is used to kill TCP children after a fixed time. */
if (sig == SIGALRM)
_exit(0);
}
@@ -653,29 +663,42 @@ void clear_cache_and_reload(struct daemon *daemon, time_t now)
}
}
static int set_dns_listeners(struct daemon *daemon, fd_set *set, int maxfd)
static int set_dns_listeners(struct daemon *daemon, time_t now, fd_set *set, int *maxfdp)
{
struct serverfd *serverfdp;
struct listener *listener;
int wait, i;
/* will we be able to get memory? */
get_new_frec(daemon, now, &wait);
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
{
FD_SET(serverfdp->fd, set);
if (serverfdp->fd > maxfd)
maxfd = serverfdp->fd;
bump_maxfd(serverfdp->fd, maxfdp);
}
for (listener = daemon->listeners; listener; listener = listener->next)
{
FD_SET(listener->fd, set);
if (listener->fd > maxfd)
maxfd = listener->fd;
FD_SET(listener->tcpfd, set);
if (listener->tcpfd > maxfd)
maxfd = listener->tcpfd;
/* only listen for queries if we have resources */
if (wait == 0)
{
FD_SET(listener->fd, set);
bump_maxfd(listener->fd, maxfdp);
}
/* death of a child goes through the select loop, so
we don't need to explicitly arrange to wake up here */
for (i = 0; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] == 0)
{
FD_SET(listener->tcpfd, set);
bump_maxfd(listener->tcpfd, maxfdp);
break;
}
}
return maxfd;
return wait;
}
static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
@@ -723,7 +746,7 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
break;
}
if ((daemon->num_kids >= MAX_PROCS) || !iface)
if (!iface)
{
shutdown(confd, SHUT_RDWR);
close(confd);
@@ -740,7 +763,6 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
daemon->tcp_pids[i] = p;
break;
}
daemon->num_kids++;
}
close(confd);
}
@@ -869,7 +891,7 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
struct timeval tv;
fd_set rset;
struct sockaddr_in faddr;
int maxfd;
int maxfd = fd;
socklen_t len = sizeof(faddr);
tv.tv_usec = 250000;
@@ -877,7 +899,7 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
FD_ZERO(&rset);
FD_SET(fd, &rset);
maxfd = set_dns_listeners(daemon, &rset, fd);
set_dns_listeners(daemon, now, &rset, &maxfd);
if (select(maxfd+1, &rset, NULL, NULL, &tv) < 0)
FD_ZERO(&rset);

View File

@@ -111,6 +111,7 @@ extern int capset(cap_user_header_t header, cap_user_data_t data);
#define OPT_BOOTP_DYNAMIC 1048576
#define OPT_NO_PING 2097152
#define OPT_LEASE_RO 4194304
#define OPT_RELOAD 8388608
struct all_addr {
union {
@@ -229,8 +230,7 @@ struct serverfd {
struct server {
union mysockaddr addr, source_addr;
struct serverfd *sfd; /* non-NULL if this server has its own fd bound to
a source port */
struct serverfd *sfd;
char *domain; /* set if this server only handles a domain. */
int flags, tcpfd;
struct server *next;
@@ -283,12 +283,19 @@ struct frec {
struct frec *next;
};
/* actions in the daemon->helper RPC */
#define ACTION_DEL 1
#define ACTION_OLD_HOSTNAME 2
#define ACTION_OLD 3
#define ACTION_ADD 4
#define DHCP_CHADDR_MAX 16
struct dhcp_lease {
int clid_len; /* length of client identifier */
unsigned char *clid; /* clientid */
char *hostname, *fqdn; /* name from client-hostname option or config */
char *old_hostname; /* hostname before it moved to another lease */
char auth_name; /* hostname came from config, not from client */
char new; /* newly created */
char changed; /* modified */
@@ -300,6 +307,8 @@ struct dhcp_lease {
int hwaddr_len, hwaddr_type;
unsigned char hwaddr[DHCP_CHADDR_MAX];
struct in_addr addr;
unsigned char *vendorclass, *userclass;
unsigned int vendorclass_len, userclass_len;
struct dhcp_lease *next;
};
@@ -312,6 +321,7 @@ struct dhcp_netid_list {
struct dhcp_netid *list;
struct dhcp_netid_list *next;
};
struct dhcp_config {
unsigned int flags;
int clid_len; /* length of client identifier */
@@ -450,11 +460,10 @@ struct daemon {
struct server *last_server;
struct server *srv_save; /* Used for resend on DoD */
size_t packet_len; /* " " */
pid_t script_pid, tcp_pids[MAX_PROCS];
int num_kids;
pid_t tcp_pids[MAX_PROCS];
/* DHCP state */
int dhcpfd;
int dhcpfd, helperfd;
#ifdef HAVE_LINUX_NETWORK
int netlinkfd;
#else
@@ -534,6 +543,9 @@ int memcmp_masked(unsigned char *a, unsigned char *b, int len,
unsigned int mask);
int expand_buf(struct iovec *iov, size_t size);
char *print_mac(struct daemon *daemon, unsigned char *mac, int len);
void bump_maxfd(int fd, int *max);
void log_start(struct daemon *daemon);
int read_write(int fd, unsigned char *packet, int size, int rw);
/* option.c */
struct daemon *read_opts (int argc, char **argv, char *compile_opts);
@@ -544,6 +556,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now);
unsigned char *tcp_request(struct daemon *daemon, int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask);
void server_gone(struct daemon *daemon, struct server *server);
struct frec *get_new_frec(struct daemon *daemon, time_t now, int *wait);
/* network.c */
struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds);
@@ -592,7 +605,7 @@ struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int h
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
void lease_prune(struct dhcp_lease *target, time_t now);
void lease_update_from_configs(struct daemon *daemon);
void lease_collect(struct daemon *daemon);
int do_script_run(struct daemon *daemon);
/* 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);
@@ -629,6 +642,13 @@ int iface_enumerate(struct daemon *daemon, void *parm,
char *dbus_init(struct daemon *daemon);
void check_dbus_listeners(struct daemon *daemon,
fd_set *rset, fd_set *wset, fd_set *eset);
int set_dbus_listeners(struct daemon *daemon, int maxfd,
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
/* helper.c */
int create_helper(struct daemon *daemon);
void helper_write(struct daemon *daemon);
void queue_script(struct daemon *daemon, int action,
struct dhcp_lease *lease, char *hostname);
int helper_buf_empty(void);

View File

@@ -14,7 +14,6 @@
static struct frec *frec_list = NULL;
static struct frec *get_new_frec(struct daemon *daemon, time_t now);
static struct frec *lookup_frec(unsigned short id);
static struct frec *lookup_frec_by_sender(unsigned short id,
union mysockaddr *addr,
@@ -232,7 +231,7 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
if (gotname)
flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain);
if (!flags && !(forward = get_new_frec(daemon, now)))
if (!flags && !(forward = get_new_frec(daemon, now, NULL)))
/* table full - server failure. */
flags = F_NEG;
@@ -459,7 +458,6 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
header->arcount = htons(0);
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{
forward->forwardall = 1;
header->qr = 0;
header->tc = 0;
forward_query(daemon, -1, NULL, NULL, 0, header, nn, now, forward);
@@ -657,31 +655,6 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
header, (size_t)n, now, NULL);
}
static int read_write(int fd, unsigned char *packet, int size, int rw)
{
ssize_t n, done;
for (done = 0; done < size; done += n)
{
retry:
if (rw)
n = read(fd, &packet[done], (size_t)(size - done));
else
n = write(fd, &packet[done], (size_t)(size - done));
if (n == 0)
return 0;
else if (n == -1)
{
if (errno == EINTR)
goto retry;
else
return 0;
}
}
return 1;
}
/* The daemon forks before calling this: it should deal with one connection,
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
@@ -837,49 +810,10 @@ unsigned char *tcp_request(struct daemon *daemon, int confd, time_t now,
}
}
static struct frec *get_new_frec(struct daemon *daemon, time_t now)
static struct frec *allocate_frec(time_t now)
{
struct frec *f = frec_list, *oldest = NULL;
time_t oldtime = now;
int count = 0;
static time_t warntime = 0;
while (f)
{
if (f->new_id == 0)
{
f->time = now;
return f;
}
if (difftime(f->time, oldtime) <= 0)
{
oldtime = f->time;
oldest = f;
}
count++;
f = f->next;
}
struct frec *f;
/* can't find empty one, use oldest if there is one
and it's older than timeout */
if (oldest && difftime(now, oldtime) > TIMEOUT)
{
oldest->time = now;
return oldest;
}
if (count > daemon->ftabsize)
{ /* limit logging rate so syslog isn't DOSed either */
if (!warntime || difftime(now, warntime) > LOGRATE)
{
warntime = now;
syslog(LOG_WARNING, _("forwarding table overflow: check for server loops."));
}
return NULL;
}
if ((f = (struct frec *)malloc(sizeof(struct frec))))
{
f->next = frec_list;
@@ -887,6 +821,61 @@ static struct frec *get_new_frec(struct daemon *daemon, time_t now)
f->new_id = 0;
frec_list = f;
}
return f;
}
/* if wait==NULL return a free or older than TIMEOUT record.
else return *wait zero if one available, or *wait is delay to
when the oldest in-use record will expire. */
struct frec *get_new_frec(struct daemon *daemon, time_t now, int *wait)
{
struct frec *f, *oldest;
int count;
if (wait)
*wait = 0;
for (f = frec_list, oldest = NULL, count = 0; f; f = f->next, count++)
if (f->new_id == 0)
{
f->time = now;
return f;
}
else if (!oldest || difftime(f->time, oldest->time) <= 0)
oldest = f;
/* can't find empty one, use oldest if there is one
and it's older than timeout */
if (oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
{
/* keep stuff for twice timeout if we can by allocating a new
record instead */
if (difftime(now, oldest->time) < 2*TIMEOUT &&
count <= daemon->ftabsize &&
(f = allocate_frec(now)))
return f;
if (!wait)
{
oldest->new_id = 0;
oldest->time = now;
}
return oldest;
}
/* none available, calculate time 'till oldest record expires */
if (count > daemon->ftabsize)
{
if (oldest && wait)
*wait = oldest->time + (time_t)TIMEOUT - now;
return NULL;
}
if (!(f = allocate_frec(now)) && wait)
/* wait one second on malloc failure */
*wait = 1;
return f; /* OK if malloc fails and this is NULL */
}

330
src/helper.c Normal file
View File

@@ -0,0 +1,330 @@
/* dnsmasq is Copyright (c) 2000-2006 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.
*/
#include "dnsmasq.h"
/* This file has code to fork a helper process which recieves data via a pipe
shared with the main process and which is responsible for calling a script when
DHCP leases change.
The helper process is forked before the main process drops root, so it retains root
privs to pass on to the script. For this reason it tries to be paranoid about
data received from the main process, in case that has been compromised. We don't
want the helper to give an attacker root. In particular, the script to be run is
not settable via the pipe, once the fork has taken place it is not alterable by the
main process.
*/
struct script_data
{
unsigned char action, hwaddr_len, hwaddr_type;
unsigned char clid_len, hostname_len, uclass_len, vclass_len;
struct in_addr addr;
#ifdef HAVE_BROKEN_RTC
unsigned int length;
#else
time_t expires;
#endif
unsigned char hwaddr[DHCP_CHADDR_MAX];
};
static struct script_data *buf;
static size_t bytes_in_buf, buf_size;
int create_helper(struct daemon *daemon)
{
pid_t pid;
int i, pipefd[2];
struct sigaction sigact;
buf = NULL;
buf_size = bytes_in_buf = 0;
if (!daemon->dhcp || !daemon->lease_change_command)
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;
if (pid != 0)
{
close(pipefd[0]); /* close reader side */
return pipefd[1];
}
/* ignore SIGTERM, so that we can clean up when the main process gets hit */
sigact.sa_handler = SIG_IGN;
sigact.sa_flags = 0;
sigemptyset(&sigact.sa_mask);
sigaction(SIGTERM, &sigact, NULL);
/* close all the sockets etc, we don't need them here */
for (i = 0; i < 64; i++)
if (i != STDOUT_FILENO && i != STDERR_FILENO &&
i != STDIN_FILENO && i != pipefd[0])
close(i);
/* we open our own log connection. */
log_start(daemon);
/* 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 */
while(1)
{
struct script_data data;
char *p, *action_str, *hostname = NULL;
unsigned char *buf = (unsigned char *)daemon->namebuff;
/* we read zero bytes when pipe closed: this is our signal to exit */
if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
_exit(0);
if (data.action == ACTION_DEL)
action_str = "del";
else if (data.action == ACTION_ADD)
action_str = "add";
else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
action_str = "old";
else
continue;
/* stringify MAC into dhcp_buff */
p = daemon->dhcp_buff;
if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
p += sprintf(p, "%.2x-", data.hwaddr_type);
for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
{
p += sprintf(p, "%.2x", data.hwaddr[i]);
if (i != data.hwaddr_len - 1)
p += sprintf(p, ":");
}
/* and CLID into packet */
if (!read_write(pipefd[0], buf, data.clid_len, 1))
continue;
for (p = daemon->packet, i = 0; i < data.clid_len; i++)
{
p += sprintf(p, "%.2x", buf[i]);
if (i != data.clid_len - 1)
p += sprintf(p, ":");
}
/* and expiry or length into dhcp_buff2 */
#ifdef HAVE_BROKEN_RTC
sprintf(daemon->dhcp_buff2, "%u ", data.length);
#else
sprintf(daemon->dhcp_buff2, "%lu ", (unsigned long)data.expires);
#endif
if (!read_write(pipefd[0], buf, data.hostname_len + data.uclass_len + data.vclass_len, 1))
continue;
if ((pid = fork()) == -1)
continue;
/* wait for child to complete */
if (pid != 0)
{
int status;
waitpid(pid, &status, 0);
if (WIFSIGNALED(status))
syslog(LOG_WARNING, _("child process killed by signal %d"), WTERMSIG(status));
else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
syslog(LOG_WARNING, _("child process exited with status %d"), WEXITSTATUS(status));
continue;
}
if (data.clid_len != 0)
setenv("DNSMASQ_CLIENT_ID", daemon->packet, 1);
else
unsetenv("DNSMASQ_CLIENT_ID");
#ifdef HAVE_BROKEN_RTC
setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, 1);
unsetenv("DNSMASQ_LEASE_EXPIRES");
#else
setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, 1);
unsetenv("DNSMASQ_LEASE_LENGTH");
#endif
if (data.vclass_len != 0)
{
buf[data.vclass_len - 1] = 0; /* don't trust zero-term */
/* cannot have = chars in env - truncate if found . */
if ((p = strchr((char *)buf, '=')))
*p = 0;
setenv("DNSMASQ_VENDOR_CLASS", (char *)buf, 1);
buf += data.vclass_len;
}
else
unsetenv("DNSMASQ_VENDOR_CLASS");
if (data.uclass_len != 0)
{
unsigned char *end = buf + data.uclass_len;
buf[data.uclass_len - 1] = 0; /* don't trust zero-term */
for (i = 0; buf < end;)
{
size_t len = strlen((char *)buf) + 1;
if ((p = strchr((char *)buf, '=')))
*p = 0;
if (strlen((char *)buf) != 0)
{
sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i++);
setenv(daemon->dhcp_buff2, (char *)buf, 1);
}
buf += len;
}
}
if (data.hostname_len != 0)
{
hostname = (char *)buf;
hostname[data.hostname_len - 1] = 0;
canonicalise(hostname);
}
if (data.action == ACTION_OLD_HOSTNAME && hostname)
{
setenv("DNSMASQ_OLD_HOSTNAME", hostname, 1);
hostname = NULL;
}
else
unsetenv("DNSMASQ_OLD_HOSTNAME");
p = strrchr(daemon->lease_change_command, '/');
execl(daemon->lease_change_command,
p ? p+1 : daemon->lease_change_command,
action_str, daemon->dhcp_buff, inet_ntoa(data.addr), hostname, (char*)NULL);
/* log socket should still be open, right? */
syslog(LOG_ERR, _("failed to execute %s: %m"),
daemon->lease_change_command);
_exit(0);
}
}
/* pack up lease data into a buffer */
void queue_script(struct daemon *daemon, int action, struct dhcp_lease *lease, char *hostname)
{
unsigned char *p;
size_t size;
unsigned int hostname_len = 0, clid_len = 0, vclass_len = 0, uclass_len = 0;
/* no script */
if (daemon->helperfd == -1)
return;
if (action == ACTION_ADD)
{
if (lease->vendorclass)
vclass_len = lease->vendorclass_len;
if (lease->userclass)
uclass_len = lease->userclass_len;
}
if (lease->clid)
clid_len = lease->clid_len;
if (hostname)
hostname_len = strlen(hostname) + 1;
size = sizeof(struct script_data) + clid_len + vclass_len + uclass_len + hostname_len;
if (size > buf_size)
{
struct script_data *new;
/* start with resonable size, will almost never need extending. */
if (size < sizeof(struct script_data) + 200)
size = sizeof(struct script_data) + 200;
if (!(new = malloc(size)))
return;
if (buf)
free(buf);
buf = new;
buf_size = size;
}
buf->action = action;
buf->hwaddr_len = lease->hwaddr_len;
buf->hwaddr_type = lease->hwaddr_type;
buf->clid_len = clid_len;
buf->vclass_len = vclass_len;
buf->uclass_len = uclass_len;
buf->hostname_len = hostname_len;
buf->addr = lease->addr;
memcpy(buf->hwaddr, lease->hwaddr, lease->hwaddr_len);
#ifdef HAVE_BROKEN_RTC
buf->length = lease->length;
#else
buf->expires = lease->expires;
#endif
p = (unsigned char *)(buf+1);
if (buf->clid_len != 0)
{
memcpy(p, lease->clid, clid_len);
p += clid_len;
}
if (buf->vclass_len != 0)
{
memcpy(p, lease->vendorclass, vclass_len);
p += vclass_len;
}
if (buf->uclass_len != 0)
{
memcpy(p, lease->userclass, uclass_len);
p += uclass_len;
}
if (buf->hostname_len != 0)
{
memcpy(p, hostname, hostname_len);
p += hostname_len;
}
bytes_in_buf = p - (unsigned char *)buf;
}
int helper_buf_empty(void)
{
return bytes_in_buf == 0;
}
void helper_write(struct daemon *daemon)
{
ssize_t rc;
if (bytes_in_buf == 0)
return;
if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
{
if (bytes_in_buf != (size_t)rc)
memmove(buf, buf + rc, bytes_in_buf - rc);
bytes_in_buf -= rc;
}
else
{
if (errno == EAGAIN || errno == EINTR)
return;
bytes_in_buf = 0;
}
}

View File

@@ -418,8 +418,11 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix, int
{
if (lease_tmp->auth_name && !auth)
return;
lease_tmp->changed = 1; /* call script on change */
new_name = lease_tmp->hostname;
/* this shouldn't happen unless updates are very quick and the
script very slow, we just avoid a memory leak if it does. */
if (lease_tmp->old_hostname)
free(lease_tmp->old_hostname);
lease_tmp->old_hostname = lease_tmp->hostname;
lease_tmp->hostname = NULL;
if (lease_tmp->fqdn)
{
@@ -441,7 +444,13 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix, int
}
if (lease->hostname)
free(lease->hostname);
{
/* run script to say we lost our old name */
if (lease->old_hostname)
free(lease->old_hostname);
lease->old_hostname = lease->hostname;
}
if (lease->fqdn)
free(lease->fqdn);
@@ -454,121 +463,81 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix, int
lease->changed = 1; /* run script on change */
}
#ifndef NO_FORK
static pid_t run_script(struct daemon *daemon, char *action, struct dhcp_lease *lease)
{
if (daemon->lease_change_command)
{
char *addr = inet_ntoa(lease->addr);
char *com = strrchr(daemon->lease_change_command, '/');
char *p;
pid_t pid;
int i;
/* stringify MAC into dhcp_buff */
p = daemon->dhcp_buff;
if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0)
p += sprintf(p, "%.2x-", lease->hwaddr_type);
for (i = 0; i < lease->hwaddr_len; i++)
{
p += sprintf(p, "%.2x", lease->hwaddr[i]);
if (i != lease->hwaddr_len - 1)
p += sprintf(p, ":");
}
/* and CLID into namebuff */
p = daemon->namebuff;
if (lease->clid)
for (i = 0; i < lease->clid_len; i++)
{
p += sprintf(p, "%.2x", lease->clid[i]);
if (i != lease->clid_len - 1)
p += sprintf(p, ":");
}
/* and expiry or length into dhcp_buff2 */
#ifdef HAVE_BROKEN_RTC
sprintf(daemon->dhcp_buff2, "%u ", lease->length);
#else
sprintf(daemon->dhcp_buff2, "%lu ", (unsigned long)lease->expires);
#endif
pid = fork();
if (pid == -1)
return 0; /* fork error */
else if (pid != 0)
return pid;
if (lease->clid && lease->clid_len != 0)
setenv("DNSMASQ_CLIENT_ID", daemon->namebuff, 1);
else
unsetenv("DNSMASQ_CLIENT_ID");
#ifdef HAVE_BROKEN_RTC
setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, 1);
unsetenv("DNSMASQ_LEASE_EXPIRES");
#else
setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, 1);
unsetenv("DNSMASQ_LEASE_LENGTH");
#endif
execl(daemon->lease_change_command,
com ? com+1 : daemon->lease_change_command,
action, daemon->dhcp_buff, addr, lease->hostname, (char*)NULL);
/* log socket should still be open, right? */
syslog(LOG_ERR, _("failed to execute %s: %m"),
daemon->lease_change_command);
_exit(0);
}
return 0;
}
#endif
/* deleted leases get transferred to the old_leases list.
remove them here, after calling the lease change
script. Also run the lease change script on new leases */
void lease_collect(struct daemon *daemon)
script. Also run the lease change script on new/modified leases.
Return zero if nothing to do. */
int do_script_run(struct daemon *daemon)
{
struct dhcp_lease *lease;
while (old_leases)
if (old_leases)
{
if (daemon->script_pid != 0)
return; /* busy */
lease = old_leases;
old_leases = lease->next;
#ifndef NO_FORK
daemon->script_pid = run_script(daemon, "del", lease);
#endif
if (lease->hostname)
free(lease->hostname);
if (lease->fqdn)
free(lease->fqdn);
if (lease->clid)
free(lease->clid);
free(lease);
/* If the lease still has an old_hostname, do the "old" action on that first */
if (lease->old_hostname)
{
queue_script(daemon, ACTION_OLD_HOSTNAME, lease, lease->old_hostname);
free(lease->old_hostname);
lease->old_hostname = NULL;
return 1;
}
else
{
queue_script(daemon, ACTION_DEL, lease, lease->hostname);
old_leases = lease->next;
if (lease->hostname)
free(lease->hostname);
if (lease->fqdn)
free(lease->fqdn);
if (lease->clid)
free(lease->clid);
if (lease->vendorclass)
free(lease->vendorclass);
if (lease->userclass)
free(lease->userclass);
free(lease);
return 1;
}
}
/* make sure we announce the loss of a hostname before its new location. */
for (lease = leases; lease; lease = lease->next)
if (lease->old_hostname)
{
queue_script(daemon, ACTION_OLD_HOSTNAME, lease, lease->old_hostname);
free(lease->old_hostname);
lease->old_hostname = NULL;
return 1;
}
for (lease = leases; lease; lease = lease->next)
if (lease->new || lease->changed ||
(lease->aux_changed && (daemon->options & OPT_LEASE_RO)))
{
if (daemon->script_pid != 0)
return; /* busy */
#ifndef NO_FORK
daemon->script_pid = run_script(daemon, lease->new ? "add" : "old", lease);
#endif
queue_script(daemon, lease->new ? ACTION_ADD : ACTION_OLD, lease, lease->hostname);
lease->new = lease->changed = lease->aux_changed = 0;
/* 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;
}
if (lease->userclass)
{
free(lease->userclass);
lease->userclass = NULL;
}
return 1;
}
return 0; /* nothing to do */
}

View File

@@ -403,6 +403,14 @@ void check_servers(struct daemon *daemon)
{
port = prettyprint_addr(&new->addr, daemon->namebuff);
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
if (new->addr.sa.sa_family == AF_INET &&
new->addr.in.sin_addr.s_addr == 0)
{
free(new);
continue;
}
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&new->addr, &iface->addr))
break;

View File

@@ -26,6 +26,10 @@ struct myoption {
#define OPTSTRING "9531yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:6:7:8:0:"
/* options which don't have a one-char version */
#define LOPT_RELOAD 256
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
#else
@@ -97,39 +101,41 @@ static const struct myoption opts[] =
{"log-facility", 1, 0 ,'8'},
{"leasefile-ro", 0, 0, '9'},
{"dns-forward-max", 1, 0, '0'},
{"clear-on-reload", 0, 0, LOPT_RELOAD },
{ NULL, 0, 0, 0 }
};
struct optflags {
char c;
int c;
unsigned int flag;
};
static const struct optflags optmap[] = {
{ 'b', OPT_BOGUSPRIV },
{ 'f', OPT_FILTER },
{ 'q', OPT_LOG },
{ 'e', OPT_SELFMX },
{ 'h', OPT_NO_HOSTS },
{ 'n', OPT_NO_POLL },
{ 'd', OPT_DEBUG },
{ 'k', OPT_NO_FORK },
{ 'K', OPT_AUTHORITATIVE },
{ 'o', OPT_ORDER },
{ 'R', OPT_NO_RESOLV },
{ 'E', OPT_EXPAND },
{ 'L', OPT_LOCALMX },
{ 'N', OPT_NO_NEG },
{ 'D', OPT_NODOTS_LOCAL },
{ 'z', OPT_NOWILD },
{ 'Z', OPT_ETHERS },
{ 'y', OPT_LOCALISE },
{ '1', OPT_DBUS },
{ '3', OPT_BOOTP_DYNAMIC },
{ '5', OPT_NO_PING },
{ '9', OPT_LEASE_RO },
{ 'v', 0},
{ 'w', 0},
{ 'b', OPT_BOGUSPRIV },
{ 'f', OPT_FILTER },
{ 'q', OPT_LOG },
{ 'e', OPT_SELFMX },
{ 'h', OPT_NO_HOSTS },
{ 'n', OPT_NO_POLL },
{ 'd', OPT_DEBUG },
{ 'k', OPT_NO_FORK },
{ 'K', OPT_AUTHORITATIVE },
{ 'o', OPT_ORDER },
{ 'R', OPT_NO_RESOLV },
{ 'E', OPT_EXPAND },
{ 'L', OPT_LOCALMX },
{ 'N', OPT_NO_NEG },
{ 'D', OPT_NODOTS_LOCAL },
{ 'z', OPT_NOWILD },
{ 'Z', OPT_ETHERS },
{ 'y', OPT_LOCALISE },
{ '1', OPT_DBUS },
{ '3', OPT_BOOTP_DYNAMIC },
{ '5', OPT_NO_PING },
{ '9', OPT_LEASE_RO },
{ LOPT_RELOAD, OPT_RELOAD },
{ 'v', 0},
{ 'w', 0},
{ 0, 0 }
};
@@ -201,6 +207,7 @@ static const struct {
{ "-8, --log-facility=facilty", gettext_noop("Log to this syslog facility. (defaults to DAEMON)"), NULL },
{ "-9, --leasefile-ro", gettext_noop("Read leases at startup, but never write the lease file."), NULL },
{ "-0, --dns-forward-max=<queries>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
{ " --clear-on-reload", gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
{ NULL, NULL, NULL }
};

View File

@@ -72,7 +72,7 @@ static void bootp_option_put(struct dhcp_packet *mess,
struct dhcp_boot *boot_opts, struct dhcp_netid *netids);
static struct in_addr option_addr(unsigned char *opt);
static unsigned int option_uint(unsigned char *opt, int size);
static void log_packet(struct daemon *daemon, char *type, struct in_addr *addr,
static void log_packet(struct daemon *daemon, char *type, void *addr,
struct dhcp_packet *mess, 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_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
@@ -214,7 +214,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
{
struct dhcp_context *context_tmp, *context_new = NULL;
struct in_addr addr = mess->ciaddr;
struct in_addr addr;
int force = 0;
if (subnet_addr.s_addr)
@@ -227,19 +227,33 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
addr = mess->giaddr;
force = 1;
}
else
{
/* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
addr = mess->ciaddr;
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
if (context_tmp->netmask.s_addr &&
is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
is_same_net(addr, context_tmp->end, context_tmp->netmask))
{
context_new = context;
break;
}
}
if (!context_new)
for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
if (context_tmp->netmask.s_addr &&
is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
is_same_net(addr, context_tmp->end, context_tmp->netmask))
{
context_tmp->current = context_new;
context_new = context_tmp;
}
for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
if (context_tmp->netmask.s_addr &&
is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
is_same_net(addr, context_tmp->end, context_tmp->netmask))
{
context_tmp->current = context_new;
context_new = context_tmp;
}
if (context_new || force)
context = context_new;
}
if (!context)
@@ -461,7 +475,10 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
so zero the counts so that we don't get spurious matches between
the vendor string and the counts. If the lengths don't add up, we
assume that the option is a single string and non RFC3004 compliant
and just do the substring match. dhclient provides these broken options. */
and just do the substring match. dhclient provides these broken options.
The code, later, which sends user-class data to the lease-change script
relies on the transformation done here.
*/
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
{
@@ -552,7 +569,8 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
return 0;
case DHCPRELEASE:
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
if (!(context = narrow_context(context, mess->ciaddr)) ||
!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
(context->local.s_addr != option_addr(opt).s_addr))
return 0;
@@ -613,7 +631,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
message = _("no address available");
}
log_packet(daemon, "DISCOVER", opt ? (struct in_addr *)option_ptr(opt) : NULL, mess, iface_name, message);
log_packet(daemon, "DISCOVER", opt ? option_ptr(opt) : NULL, mess, iface_name, message);
if (message || !(context = narrow_context(context, mess->yiaddr)))
return 0;
@@ -743,11 +761,42 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
else if (!clid && mess->hlen == 0)
message = _("no unique-id");
else if (!lease &&
!(lease = lease_allocate(mess->yiaddr)))
message = _("no leases left");
else if (!lease)
{
if (!(lease = lease_allocate(mess->yiaddr)))
message = _("no leases left");
else
{
/* copy user-class and vendor class into new lease, for the script */
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
{
int len = option_len(opt);
unsigned char *ucp = option_ptr(opt);
/* If the user-class option started as counted strings, the first byte will be zero. */
if (len != 0 && ucp[0] == 0)
ucp++, len--;
if ((lease->userclass = malloc(len+1)))
{
memcpy(lease->userclass, ucp, len);
lease->userclass[len] = 0;
lease->userclass_len = len+1;
}
}
if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
{
int len = option_len(opt);
unsigned char *ucp = option_ptr(opt);
if ((lease->vendorclass = malloc(len+1)))
{
memcpy(lease->vendorclass, ucp, len);
lease->vendorclass[len] = 0;
lease->vendorclass_len = len+1;
}
}
}
}
}
if (message)
{
log_packet(daemon, "NAK", &mess->yiaddr, mess, iface_name, message);
@@ -869,14 +918,20 @@ static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *
return time;
}
static void log_packet(struct daemon *daemon, char *type, struct in_addr *addr,
static void log_packet(struct daemon *daemon, char *type, void *addr,
struct dhcp_packet *mess, char *interface, char *string)
{
struct in_addr a;
/* addr may be misaligned */
if (addr)
memcpy(&a, addr, sizeof(a));
syslog(LOG_INFO, "%s%s(%s) %s%s%s %s",
type ? "DHCP" : "BOOTP",
type ? type : "",
interface,
addr ? inet_ntoa(*addr) : "",
addr ? inet_ntoa(a) : "",
addr ? " " : "",
print_mac(daemon, mess->chaddr, mess->hlen),
string ? string : "");

View File

@@ -410,3 +410,48 @@ char *print_mac(struct daemon *daemon, unsigned char *mac, int len)
return daemon->namebuff;
}
void bump_maxfd(int fd, int *max)
{
if (fd > *max)
*max = fd;
}
void log_start(struct daemon *daemon)
{
if (daemon->options & OPT_DEBUG)
{
#ifdef LOG_PERROR
openlog("dnsmasq", LOG_PERROR, daemon->log_fac);
#else
openlog("dnsmasq", 0, daemon->log_fac);
#endif
}
else
openlog("dnsmasq", LOG_PID, daemon->log_fac);
}
int read_write(int fd, unsigned char *packet, int size, int rw)
{
ssize_t n, done;
for (done = 0; done < size; done += n)
{
retry:
if (rw)
n = read(fd, &packet[done], (size_t)(size - done));
else
n = write(fd, &packet[done], (size_t)(size - done));
if (n == 0)
return 0;
else if (n == -1)
{
if (errno == EINTR || errno == ENOMEM || errno == ENOBUFS)
goto retry;
else
return 0;
}
}
return 1;
}