./ 0000755 0000000 0000000 00000000000 14256750714 007724 5 ustar root root ./src/ 0000755 0000000 0000000 00000000000 14256750714 010513 5 ustar root root ./src/rfc2131.c 0000644 0000000 0000000 00000241535 14256750714 011752 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "dnsmasq.h"
#ifdef HAVE_DHCP
#define BOOTREQUEST 1
#define BOOTREPLY 2
#define DHCP_COOKIE 0x63825363
/* The Linux in-kernel DHCP client silently ignores any packet
smaller than this. Sigh........... */
#define MIN_PACKETSZ 300
#define OPTION_PAD 0
#define OPTION_NETMASK 1
#define OPTION_ROUTER 3
#define OPTION_DNSSERVER 6
#define OPTION_HOSTNAME 12
#define OPTION_DOMAINNAME 15
#define OPTION_BROADCAST 28
#define OPTION_VENDOR_CLASS_OPT 43
#define OPTION_REQUESTED_IP 50
#define OPTION_LEASE_TIME 51
#define OPTION_OVERLOAD 52
#define OPTION_MESSAGE_TYPE 53
#define OPTION_SERVER_IDENTIFIER 54
#define OPTION_REQUESTED_OPTIONS 55
#define OPTION_MESSAGE 56
#define OPTION_MAXMESSAGE 57
#define OPTION_T1 58
#define OPTION_T2 59
#define OPTION_VENDOR_ID 60
#define OPTION_CLIENT_ID 61
#define OPTION_SNAME 66
#define OPTION_FILENAME 67
#define OPTION_USER_CLASS 77
#define OPTION_CLIENT_FQDN 81
#define OPTION_AGENT_ID 82
#define OPTION_ARCH 93
#define OPTION_PXE_UUID 97
#define OPTION_SUBNET_SELECT 118
#define OPTION_END 255
#define SUBOPT_CIRCUIT_ID 1
#define SUBOPT_REMOTE_ID 2
#define SUBOPT_SUBNET_SELECT 5 /* RFC 3527 */
#define SUBOPT_SUBSCR_ID 6 /* RFC 3393 */
#define SUBOPT_SERVER_OR 11 /* RFC 5107 */
#define SUBOPT_PXE_BOOT_ITEM 71 /* PXE standard */
#define SUBOPT_PXE_DISCOVERY 6
#define SUBOPT_PXE_SERVERS 8
#define SUBOPT_PXE_MENU 9
#define SUBOPT_PXE_MENU_PROMPT 10
#define DHCPDISCOVER 1
#define DHCPOFFER 2
#define DHCPREQUEST 3
#define DHCPDECLINE 4
#define DHCPACK 5
#define DHCPNAK 6
#define DHCPRELEASE 7
#define DHCPINFORM 8
#define have_config(config, mask) ((config) && ((config)->flags & (mask)))
#define option_len(opt) ((int) (((unsigned char*) (opt))[1]))
#define option_ptr(opt, i) ((void*) &(((unsigned char*) (opt))[2u + (unsigned int) (i)]))
static int sanitise(unsigned char* opt, char* buf);
static struct in_addr server_id(struct dhcp_context* context, struct in_addr override,
struct in_addr fallback);
static unsigned int calc_time(struct dhcp_context* context, struct dhcp_config* config,
unsigned char* opt);
static void option_put(struct dhcp_packet* mess, unsigned char* end, int opt, int len,
unsigned int val);
static void option_put_string(struct dhcp_packet* mess, unsigned char* end, int opt, char* string,
int null_term);
static struct in_addr option_addr(unsigned char* opt);
static struct in_addr option_addr_arr(unsigned char* opt, int offset);
static unsigned int option_uint(unsigned char* opt, int i, int size);
static void log_packet(char* type, void* addr, unsigned char* ext_mac, int mac_len, char* interface,
char* string, u32 xid);
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 size_t dhcp_packet_size(struct dhcp_packet* mess, struct dhcp_netid* netid,
unsigned char* agent_id, unsigned char* real_end);
static void clear_packet(struct dhcp_packet* mess, unsigned char* end);
static void do_options(struct dhcp_context* context, struct dhcp_packet* mess,
unsigned char* real_end, unsigned char* req_options, char* hostname,
char* domain, char* config_domain, struct dhcp_netid* netid,
struct in_addr subnet_addr, unsigned char fqdn_flags, int null_term,
int pxearch, unsigned char* uuid);
static void match_vendor_opts(unsigned char* opt, struct dhcp_opt* dopt);
static void do_encap_opts(struct dhcp_opt* opts, int encap, int flag, struct dhcp_packet* mess,
unsigned char* end, int null_term);
static void pxe_misc(struct dhcp_packet* mess, unsigned char* end, unsigned char* uuid);
static int prune_vendor_opts(struct dhcp_netid* netid);
static struct dhcp_opt* pxe_opts(int pxe_arch, struct dhcp_netid* netid);
struct dhcp_boot* find_boot(struct dhcp_netid* netid);
size_t dhcp_reply(struct dhcp_context* context, char* iface_name, int int_index, size_t sz,
time_t now, int unicast_dest, int* is_inform) {
unsigned char *opt, *clid = NULL;
struct dhcp_lease *ltmp, *lease = NULL;
struct dhcp_vendor* vendor;
struct dhcp_mac* mac;
struct dhcp_netid_list* id_list;
int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0, pxearch = -1;
struct dhcp_packet* mess = (struct dhcp_packet*) daemon->dhcp_packet.iov_base;
unsigned char* end = (unsigned char*) (mess + 1);
unsigned char* real_end = (unsigned char*) (mess + 1);
char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL, *domain = NULL;
int hostname_auth = 0, borken_opt = 0;
unsigned char* req_options = NULL;
char* message = NULL;
unsigned int time;
struct dhcp_config* config;
struct dhcp_netid* netid;
struct in_addr subnet_addr, fallback, override;
unsigned short fuzz = 0;
unsigned int mess_type = 0;
unsigned char fqdn_flags = 0;
unsigned char *agent_id = NULL, *uuid = NULL;
unsigned char* emac = NULL;
int emac_len = 0;
struct dhcp_netid known_id, iface_id;
struct dhcp_opt* o;
unsigned char pxe_uuid[17];
subnet_addr.s_addr = override.s_addr = 0;
/* set tag with name == interface */
iface_id.net = iface_name;
iface_id.next = NULL;
netid = &iface_id;
if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX) return 0;
if (mess->htype == 0 && mess->hlen != 0) return 0;
/* check for DHCP rather than BOOTP */
if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1))) {
mess_type = option_uint(opt, 0, 1);
/* only insist on a cookie for DHCP. */
if (*((u32*) &mess->options) != htonl(DHCP_COOKIE)) return 0;
/* two things to note here: expand_buf may move the packet,
so reassign mess from daemon->packet. Also, the size
sent includes the IP and UDP headers, hence the magic "-28" */
if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2))) {
size_t size = (size_t) option_uint(opt, 0, 2) - 28;
if (size > DHCP_PACKET_MAX)
size = DHCP_PACKET_MAX;
else if (size < sizeof(struct dhcp_packet))
size = sizeof(struct dhcp_packet);
if (expand_buf(&daemon->dhcp_packet, size)) {
mess = (struct dhcp_packet*) daemon->dhcp_packet.iov_base;
real_end = end = ((unsigned char*) mess) + size;
}
}
/* Some buggy clients set ciaddr when they shouldn't, so clear that here since
it can affect the context-determination code. */
if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
mess->ciaddr.s_addr = 0;
if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1))) {
/* Any agent-id needs to be copied back out, verbatim, as the last option
in the packet. Here, we shift it to the very end of the buffer, if it doesn't
get overwritten, then it will be shuffled back at the end of processing.
Note that the incoming options must not be overwritten here, so there has to
be enough free space at the end of the packet to copy the option. */
unsigned char* sopt;
unsigned int total = option_len(opt) + 2;
unsigned char* last_opt = option_find(mess, sz, OPTION_END, 0);
if (last_opt && last_opt < end - total) {
end -= total;
agent_id = end;
memcpy(agent_id, opt, total);
}
/* look for RFC3527 Link selection sub-option */
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)),
SUBOPT_SUBNET_SELECT, INADDRSZ)))
subnet_addr = option_addr(sopt);
/* look for RFC5107 server-identifier-override */
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)),
SUBOPT_SERVER_OR, INADDRSZ)))
override = option_addr(sopt);
/* if a circuit-id or remote-is option is provided, exact-match to options. */
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) {
int search;
if (vendor->match_type == MATCH_CIRCUIT)
search = SUBOPT_CIRCUIT_ID;
else if (vendor->match_type == MATCH_REMOTE)
search = SUBOPT_REMOTE_ID;
else if (vendor->match_type == MATCH_SUBSCRIBER)
search = SUBOPT_SUBSCR_ID;
else
continue;
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)),
search, 1)) &&
vendor->len == option_len(sopt) &&
memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0) {
vendor->netid.next = netid;
netid = &vendor->netid;
break;
}
}
}
/* Check for RFC3011 subnet selector - only if RFC3527 one not present */
if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
subnet_addr = option_addr(opt);
/* If there is no client identifier option, use the hardware address */
if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1))) {
clid_len = option_len(opt);
clid = option_ptr(opt, 0);
}
/* do we have a lease in store? */
lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
/* If this request is missing a clid, but we've seen one before,
use it again for option matching etc. */
if (lease && !clid && lease->clid) {
clid_len = lease->clid_len;
clid = lease->clid;
}
/* find mac to use for logging and hashing */
emac = extended_hwaddr(mess->htype, mess->hlen, mess->chaddr, clid_len, clid, &emac_len);
}
for (mac = daemon->dhcp_macs; mac; mac = mac->next)
if (mac->hwaddr_len == mess->hlen &&
(mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask)) {
mac->netid.next = netid;
netid = &mac->netid;
}
/* Determine network for this packet. Our caller will have already linked all the
contexts which match the addresses of the receiving interface but if the
machine has an address already, or came via a relay, or we have a subnet selector,
we search again. If we don't have have a giaddr or explicit subnet selector,
use the ciaddr. This is necessary because a machine which got a lease via a
relay won't use the relay to renew. If matching a ciaddr fails but we have a context
from the physical network, continue using that to allow correct DHCPNAK generation later. */
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;
int force = 0;
if (subnet_addr.s_addr) {
addr = subnet_addr;
force = 1;
} else if (mess->giaddr.s_addr) {
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;
}
if (context_new || force) context = context_new;
}
if (!context) {
// my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s
// %s"),
// subnet_addr.s_addr ? _("with subnet selector") : _("via"),
// subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ?
//inet_ntoa(mess->giaddr) : iface_name));
return 0;
}
/* keep _a_ local address available. */
fallback = context->local;
if (daemon->options & OPT_LOG_OPTS) {
struct dhcp_context* context_tmp;
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current) {
strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
my_syslog(MS_DHCP | LOG_INFO, _("%u Available DHCP subnet: %s/%s"),
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
else
my_syslog(MS_DHCP | LOG_INFO, _("%u Available DHCP range: %s -- %s"),
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
}
}
mess->op = BOOTREPLY;
config = find_config(daemon->dhcp_conf, context, clid, clid_len, 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) {
/* BOOTP request */
struct dhcp_netid id, bootp_id;
struct in_addr* logaddr = NULL;
/* must have a MAC addr for bootp */
if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY)) return 0;
if (have_config(config, CONFIG_DISABLE)) message = _("disabled");
end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
if (have_config(config, CONFIG_NAME)) {
hostname = config->hostname;
domain = config->domain;
}
if (have_config(config, CONFIG_NETID)) {
config->netid.next = netid;
netid = &config->netid;
}
/* Match incoming filename field as a netid. */
if (mess->file[0]) {
memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
id.net = (char*) daemon->dhcp_buff2;
id.next = netid;
netid = &id;
}
/* Add "bootp" as a tag to allow different options, address ranges etc
for BOOTP clients */
bootp_id.net = "bootp";
bootp_id.next = netid;
netid = &bootp_id;
for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
if (match_netid(id_list->list, netid, 0)) message = _("ignored");
if (!message) {
int nailed = 0;
if (have_config(config, CONFIG_ADDR)) {
nailed = 1;
logaddr = &config->addr;
mess->yiaddr = config->addr;
if ((lease = lease_find_by_addr(config->addr)) &&
(lease->hwaddr_len != mess->hlen || lease->hwaddr_type != mess->htype ||
memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
message = _("address in use");
} else {
if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
!address_available(context, lease->addr, netid)) {
if (lease) {
/* lease exists, wrong network. */
lease_prune(lease, now);
lease = NULL;
}
if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, netid,
now))
message = _("no address available");
} else
mess->yiaddr = lease->addr;
}
if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
message = _("wrong network");
else if (context->netid.net) {
context->netid.next = netid;
netid = &context->netid;
}
if (!message && !nailed) {
for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
if ((!id_list->list) || match_netid(id_list->list, netid, 0)) break;
if (!id_list) message = _("no address configured");
}
if (!message && !lease && (!(lease = lease_allocate(mess->yiaddr))))
message = _("no leases left");
if (!message) {
logaddr = &mess->yiaddr;
lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
if (hostname) lease_set_hostname(lease, hostname, 1);
/* infinite lease unless nailed in dhcp-host line. */
lease_set_expires(
lease, have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff, now);
lease_set_interface(lease, int_index);
clear_packet(mess, end);
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), domain,
netid, subnet_addr, 0, 0, 0, NULL);
}
}
log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);
return message ? 0 : dhcp_packet_size(mess, netid, agent_id, real_end);
}
if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4))) {
/* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
int len = option_len(opt);
char* pq = daemon->dhcp_buff;
unsigned char *pp, *op = option_ptr(opt, 0);
fqdn_flags = *op;
len -= 3;
op += 3;
pp = op;
/* Always force update, since the client has no way to do it itself. */
if (!(fqdn_flags & 0x01)) fqdn_flags |= 0x02;
fqdn_flags &= ~0x08;
fqdn_flags |= 0x01;
if (fqdn_flags & 0x04)
while (*op != 0 && ((op + (*op) + 1) - pp) < len) {
memcpy(pq, op + 1, *op);
pq += *op;
op += (*op) + 1;
*(pq++) = '.';
}
else {
memcpy(pq, op, len);
if (len > 0 && op[len - 1] == 0) borken_opt = 1;
pq += len + 1;
}
if (pq != daemon->dhcp_buff) pq--;
*pq = 0;
if (legal_hostname(daemon->dhcp_buff)) offer_hostname = client_hostname = daemon->dhcp_buff;
} else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1))) {
int len = option_len(opt);
memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
/* Microsoft clients are broken, and need zero-terminated strings
in options. We detect this state here, and do the same in
any options we send */
if (len > 0 && daemon->dhcp_buff[len - 1] == 0)
borken_opt = 1;
else
daemon->dhcp_buff[len] = 0;
if (legal_hostname(daemon->dhcp_buff)) client_hostname = daemon->dhcp_buff;
}
if (client_hostname && daemon->options & OPT_LOG_OPTS)
my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid),
client_hostname);
if (have_config(config, CONFIG_NAME)) {
hostname = config->hostname;
domain = config->domain;
hostname_auth = 1;
/* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
offer_hostname = hostname;
} else if (client_hostname) {
domain = strip_hostname(client_hostname);
if (strlen(client_hostname) != 0) {
hostname = client_hostname;
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 (new && !have_config(new, CONFIG_CLID) && !new->hwaddr) {
config = new;
/* set "known" tag for known hosts */
known_id.net = "known";
known_id.next = netid;
netid = &known_id;
}
}
}
}
if (have_config(config, CONFIG_NETID)) {
config->netid.next = netid;
netid = &config->netid;
}
/* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
Otherwise assume the option is an array, and look for a matching element.
If no data given, existance of the option is enough. */
for (o = daemon->dhcp_match; o; o = o->next) {
int i, matched = 0;
if (!(opt = option_find(mess, sz, o->opt, 1)) || o->len > option_len(opt)) continue;
if (o->len == 0)
matched = 1;
else if (o->flags & DHOPT_HEX) {
if (memcmp_masked(o->val, option_ptr(opt, 0), o->len, o->u.wildcard_mask)) matched = 1;
} else
for (i = 0; i <= (option_len(opt) - o->len);) {
if (memcmp(o->val, option_ptr(opt, i), o->len) == 0) {
matched = 1;
break;
}
if (o->flags & DHOPT_STRING)
i++;
else
i += o->len;
}
if (matched) {
o->netid->next = netid;
netid = o->netid;
}
}
/* user-class options are, according to RFC3004, supposed to contain
a set of counted strings. Here we check that this is so (by seeing
if the counts are consistent with the overall option length) and if
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.
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))) {
unsigned char* ucp = option_ptr(opt, 0);
int tmp, j;
for (j = 0; j < option_len(opt); j += ucp[j] + 1)
;
if (j == option_len(opt))
for (j = 0; j < option_len(opt); j = tmp) {
tmp = j + ucp[j] + 1;
ucp[j] = 0;
}
}
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) {
int mopt;
if (vendor->match_type == MATCH_VENDOR)
mopt = OPTION_VENDOR_ID;
else if (vendor->match_type == MATCH_USER)
mopt = OPTION_USER_CLASS;
else
continue;
if ((opt = option_find(mess, sz, mopt, 1))) {
int i;
for (i = 0; i <= (option_len(opt) - vendor->len); i++)
if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0) {
vendor->netid.next = netid;
netid = &vendor->netid;
break;
}
}
}
/* mark vendor-encapsulated options which match the client-supplied vendor class */
match_vendor_opts(option_find(mess, sz, OPTION_VENDOR_ID, 1), daemon->dhcp_opts);
if (daemon->options & OPT_LOG_OPTS) {
if (sanitise(option_find(mess, sz, OPTION_VENDOR_ID, 1), daemon->namebuff))
my_syslog(MS_DHCP | LOG_INFO, _("%u Vendor class: %s"), ntohl(mess->xid),
daemon->namebuff);
if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
my_syslog(MS_DHCP | LOG_INFO, _("%u User class: %s"), ntohl(mess->xid),
daemon->namebuff);
}
/* if all the netids in the ignore list are present, ignore this client */
for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
if (match_netid(id_list->list, netid, 0)) ignore = 1;
/* Can have setting to ignore the client ID for a particular MAC address or hostname */
if (have_config(config, CONFIG_NOCLID)) clid = NULL;
/* Check if client is PXE client. */
if (daemon->enable_pxe && (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) &&
strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0) {
if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17))) {
memcpy(pxe_uuid, option_ptr(opt, 0), 17);
uuid = pxe_uuid;
}
/* Check if this is really a PXE bootserver request, and handle specially if so. */
if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
(opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
(opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)),
SUBOPT_PXE_BOOT_ITEM, 4))) {
struct pxe_service* service;
int type = option_uint(opt, 0, 2);
int layer = option_uint(opt, 2, 2);
unsigned char save71[4];
struct dhcp_opt opt71;
if (ignore) return 0;
if (layer & 0x8000) {
my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
return 0;
}
memcpy(save71, option_ptr(opt, 0), 4);
for (service = daemon->pxe_services; service; service = service->next)
if (service->type == type) break;
if (!service || !service->basename) return 0;
clear_packet(mess, end);
mess->yiaddr = mess->ciaddr;
mess->ciaddr.s_addr = 0;
if (service->server.s_addr != 0)
mess->siaddr = service->server;
else
mess->siaddr = context->local;
snprintf((char*) mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
pxe_misc(mess, end, uuid);
prune_vendor_opts(netid);
opt71.val = save71;
opt71.opt = SUBOPT_PXE_BOOT_ITEM;
opt71.len = 4;
opt71.flags = DHOPT_VENDOR_MATCH;
opt71.netid = NULL;
opt71.next = daemon->dhcp_opts;
do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char*) mess->file,
mess->xid);
return dhcp_packet_size(mess, netid, agent_id, real_end);
}
if ((opt = option_find(mess, sz, OPTION_ARCH, 2))) {
pxearch = option_uint(opt, 0, 2);
/* proxy DHCP here. The DHCPREQUEST stuff is for gPXE */
if ((mess_type == DHCPDISCOVER || mess_type == DHCPREQUEST) &&
(context->flags & CONTEXT_PROXY)) {
struct dhcp_boot* boot = find_boot(netid);
mess->yiaddr.s_addr = 0;
if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0) {
mess->ciaddr.s_addr = 0;
mess->flags |= htons(0x8000); /* broadcast */
}
clear_packet(mess, end);
/* Provide the bootfile here, for gPXE, and in case we have no menu items
and set discovery_control = 8 */
if (boot) {
if (boot->next_server.s_addr) mess->siaddr = boot->next_server;
if (boot->file) strncpy((char*) mess->file, boot->file, sizeof(mess->file) - 1);
}
option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ,
htonl(context->local.s_addr));
pxe_misc(mess, end, uuid);
prune_vendor_opts(netid);
do_encap_opts(pxe_opts(pxearch, netid), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH,
mess, end, 0);
log_packet("PXE", NULL, emac, emac_len, iface_name,
ignore ? "proxy" : "proxy-ignored", mess->xid);
return ignore ? 0 : dhcp_packet_size(mess, netid, agent_id, real_end);
}
}
}
/* if we're just a proxy server, go no further */
if (context->flags & CONTEXT_PROXY) return 0;
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0))) {
req_options = (unsigned char*) daemon->dhcp_buff2;
memcpy(req_options, option_ptr(opt, 0), option_len(opt));
req_options[option_len(opt)] = OPTION_END;
}
switch (mess_type) {
case DHCPDECLINE:
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
return 0;
/* sanitise any message. Paranoid? Moi? */
sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ))) return 0;
log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name,
daemon->dhcp_buff, mess->xid);
if (lease && lease->addr.s_addr == option_addr(opt).s_addr) lease_prune(lease, now);
if (have_config(config, CONFIG_ADDR) && config->addr.s_addr == option_addr(opt).s_addr) {
prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
inet_ntoa(config->addr), daemon->dhcp_buff);
config->flags |= CONFIG_DECLINED;
config->decline_time = now;
} else
/* make sure this host gets a different address next time. */
for (; context; context = context->current) context->addr_epoch++;
return 0;
case DHCPRELEASE:
if (!(context = narrow_context(context, mess->ciaddr, netid)) ||
!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
return 0;
if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
lease_prune(lease, now);
else
message = _("unknown lease");
log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
return 0;
case DHCPDISCOVER:
if (ignore || have_config(config, CONFIG_DISABLE)) {
message = _("ignored");
opt = NULL;
} else {
struct in_addr addr, conf;
addr.s_addr = conf.s_addr = 0;
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
addr = option_addr(opt);
if (have_config(config, CONFIG_ADDR)) {
char* addrs = inet_ntoa(config->addr);
if ((ltmp = lease_find_by_addr(config->addr)) && ltmp != lease &&
!config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type)) {
int len;
unsigned char* mac =
extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len, ltmp->hwaddr,
ltmp->clid_len, ltmp->clid, &len);
my_syslog(MS_DHCP | LOG_WARNING,
_("not using configured address %s because it is leased to %s"),
addrs, print_mac(daemon->namebuff, mac, len));
} else {
struct dhcp_context* tmp;
for (tmp = context; tmp; tmp = tmp->current)
if (context->router.s_addr == config->addr.s_addr) break;
if (tmp)
my_syslog(MS_DHCP | LOG_WARNING,
_("not using configured address %s because it is in use by "
"the server or relay"),
addrs);
else if (have_config(config, CONFIG_DECLINED) &&
difftime(now, config->decline_time) < (float) DECLINE_BACKOFF)
my_syslog(MS_DHCP | LOG_WARNING,
_("not using configured address %s because it was previously "
"declined"),
addrs);
else
conf = config->addr;
}
}
if (conf.s_addr)
mess->yiaddr = conf;
else if (lease && address_available(context, lease->addr, netid) &&
!config_find_by_address(daemon->dhcp_conf, lease->addr))
mess->yiaddr = lease->addr;
else if (opt && address_available(context, addr, netid) &&
!lease_find_by_addr(addr) &&
!config_find_by_address(daemon->dhcp_conf, addr))
mess->yiaddr = addr;
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");
}
log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name,
message, mess->xid);
if (message || !(context = narrow_context(context, mess->yiaddr, netid))) return 0;
log_packet("DHCPOFFER", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
if (context->netid.net) {
context->netid.next = netid;
netid = &context->netid;
}
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
clear_packet(mess, end);
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ,
ntohl(server_id(context, override, fallback).s_addr));
option_put(mess, end, OPTION_LEASE_TIME, 4, time);
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
if (time != 0xffffffff) {
option_put(mess, end, OPTION_T1, 4, (time / 2));
option_put(mess, end, OPTION_T2, 4, (time * 7) / 8);
}
do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
return dhcp_packet_size(mess, netid, agent_id, real_end);
case DHCPREQUEST:
if (ignore || have_config(config, CONFIG_DISABLE)) return 0;
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ))) {
/* SELECTING or INIT_REBOOT */
mess->yiaddr = option_addr(opt);
/* send vendor and user class info for new or recreated lease */
do_classes = 1;
if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ))) {
/* SELECTING */
selecting = 1;
if (override.s_addr != 0) {
if (option_addr(opt).s_addr != override.s_addr) return 0;
} else {
for (; context; context = context->current)
if (context->local.s_addr == option_addr(opt).s_addr) break;
if (!context) {
/* In auth mode, a REQUEST sent to the wrong server
should be faulted, so that the client establishes
communication with us, otherwise, silently ignore. */
if (!(daemon->options & OPT_AUTHORITATIVE)) return 0;
message = _("wrong server-ID");
}
}
/* If a lease exists for this host and another address, squash it. */
if (lease && lease->addr.s_addr != mess->yiaddr.s_addr) {
lease_prune(lease, now);
lease = NULL;
}
} else {
/* INIT-REBOOT */
if (!lease && !(daemon->options & OPT_AUTHORITATIVE)) return 0;
if (lease && lease->addr.s_addr != mess->yiaddr.s_addr) {
message = _("wrong address");
/* avoid loops when client brain-dead */
lease_prune(lease, now);
lease = NULL;
}
}
} else {
/* RENEWING or REBINDING */
/* Check existing lease for this address.
We allow it to be missing if dhcp-authoritative mode
as long as we can allocate the lease now - checked below.
This makes for a smooth recovery from a lost lease DB */
if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
(!lease && !(daemon->options & OPT_AUTHORITATIVE))) {
message = _("lease not found");
/* ensure we broadcast NAK */
unicast_dest = 0;
}
/* desynchronise renewals */
fuzz = rand16();
mess->yiaddr = mess->ciaddr;
}
log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
if (!message) {
struct dhcp_config* addr_config;
struct dhcp_context* tmp = NULL;
if (have_config(config, CONFIG_ADDR))
for (tmp = context; tmp; tmp = tmp->current)
if (context->router.s_addr == config->addr.s_addr) break;
if (!(context = narrow_context(context, mess->yiaddr, netid))) {
/* If a machine moves networks whilst it has a lease, we catch that here. */
message = _("wrong network");
/* ensure we broadcast NAK */
unicast_dest = 0;
}
/* Check for renewal of a lease which is outside the allowed range. */
else if (!address_available(context, mess->yiaddr, netid) &&
(!have_config(config, CONFIG_ADDR) ||
config->addr.s_addr != mess->yiaddr.s_addr))
message = _("address not available");
/* Check if a new static address has been configured. Be very sure that
when the client does DISCOVER, it will get the static address, otherwise
an endless protocol loop will ensue. */
else if (!tmp && !selecting && have_config(config, CONFIG_ADDR) &&
(!have_config(config, CONFIG_DECLINED) ||
difftime(now, config->decline_time) > (float) DECLINE_BACKOFF) &&
config->addr.s_addr != mess->yiaddr.s_addr &&
(!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
message = _("static lease available");
/* Check to see if the address is reserved as a static address for another host */
else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) &&
addr_config != config)
message = _("address reserved");
else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr))) {
/* If a host is configured with more than one MAC address, it's OK to 'nix
a lease from one of it's MACs to give the address to another. */
if (config &&
config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type)) {
my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
inet_ntoa(ltmp->addr));
lease = ltmp;
} else
message = _("address in use");
}
if (!message) {
if (emac_len == 0)
message = _("no unique-id");
else if (!lease) {
if ((lease = lease_allocate(mess->yiaddr)))
do_classes = 1;
else
message = _("no leases left");
}
}
}
if (message) {
log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid);
mess->yiaddr.s_addr = 0;
clear_packet(mess, end);
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ,
ntohl(server_id(context, override, fallback).s_addr));
option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
/* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
a distant subnet which unicast a REQ to us won't work. */
if (!unicast_dest || mess->giaddr.s_addr != 0 || mess->ciaddr.s_addr == 0 ||
is_same_net(context->local, mess->ciaddr, context->netmask)) {
mess->flags |= htons(0x8000); /* broadcast */
mess->ciaddr.s_addr = 0;
}
} else {
if (do_classes) {
if (mess->giaddr.s_addr) lease->giaddr = mess->giaddr;
lease->changed = 1;
/* 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, 0);
/* If the user-class option started as counted strings, the first byte will
* be zero. */
if (len != 0 && ucp[0] == 0) ucp++, len--;
free(lease->userclass);
if ((lease->userclass = whine_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, 0);
free(lease->vendorclass);
if ((lease->vendorclass = whine_malloc(len + 1))) {
memcpy(lease->vendorclass, ucp, len);
lease->vendorclass[len] = 0;
lease->vendorclass_len = len + 1;
}
}
if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1))) {
int len = option_len(opt);
unsigned char* ucp = option_ptr(opt, 0);
free(lease->supplied_hostname);
if ((lease->supplied_hostname = whine_malloc(len + 1))) {
memcpy(lease->supplied_hostname, ucp, len);
lease->supplied_hostname[len] = 0;
lease->supplied_hostname_len = len + 1;
}
}
}
if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr))) {
hostname = client_hostname;
hostname_auth = 1;
}
if (context->netid.net) {
context->netid.next = netid;
netid = &context->netid;
}
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
/* if all the netids in the ignore_name list are present, ignore client-supplied name */
if (!hostname_auth) {
for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
if ((!id_list->list) || match_netid(id_list->list, netid, 0)) break;
if (id_list) hostname = NULL;
}
if (hostname) lease_set_hostname(lease, hostname, hostname_auth);
lease_set_expires(lease, time, now);
lease_set_interface(lease, int_index);
if (override.s_addr != 0)
lease->override = override;
else
override = lease->override;
log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname,
mess->xid);
clear_packet(mess, end);
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ,
ntohl(server_id(context, override, fallback).s_addr));
option_put(mess, end, OPTION_LEASE_TIME, 4, time);
if (time != 0xffffffff) {
while (fuzz > (time / 16)) fuzz = fuzz / 2;
option_put(mess, end, OPTION_T1, 4, (time / 2) - fuzz);
option_put(mess, end, OPTION_T2, 4, ((time / 8) * 7) - fuzz);
}
do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
}
return dhcp_packet_size(mess, netid, agent_id, real_end);
case DHCPINFORM:
if (ignore || have_config(config, CONFIG_DISABLE)) message = _("ignored");
log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
if (message || mess->ciaddr.s_addr == 0) return 0;
/* For DHCPINFORM only, cope without a valid context */
context = narrow_context(context, mess->ciaddr, netid);
/* 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("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
if (context && context->netid.net) {
context->netid.next = netid;
netid = &context->netid;
}
if (lease) {
if (override.s_addr != 0)
lease->override = override;
else
override = lease->override;
}
clear_packet(mess, end);
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ,
ntohl(server_id(context, override, fallback).s_addr));
if (lease) {
if (lease->expires == 0)
time = 0xffffffff;
else
time = (unsigned int) difftime(lease->expires, now);
option_put(mess, end, OPTION_LEASE_TIME, 4, time);
lease_set_interface(lease, int_index);
}
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr), domain,
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
*is_inform = 1; /* handle reply differently */
return dhcp_packet_size(mess, netid, agent_id, real_end);
}
return 0;
}
/* find a good value to use as MAC address for logging and address-allocation hashing.
This is normally just the chaddr field from the DHCP packet,
but eg Firewire will have hlen == 0 and use the client-id instead.
This could be anything, but will normally be EUI64 for Firewire.
We assume that if the first byte of the client-id equals the htype byte
then the client-id is using the usual encoding and use the rest of the
client-id: if not we can use the whole client-id. This should give
sane MAC address logs. */
unsigned char* extended_hwaddr(int hwtype, int hwlen, unsigned char* hwaddr, int clid_len,
unsigned char* clid, int* len_out) {
if (hwlen == 0 && clid && clid_len > 3) {
if (clid[0] == hwtype) {
*len_out = clid_len - 1;
return clid + 1;
}
#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394) {
*len_out = clid_len - 1;
return clid + 1;
}
#endif
*len_out = clid_len;
return clid;
}
*len_out = hwlen;
return hwaddr;
}
static unsigned int calc_time(struct dhcp_context* context, struct dhcp_config* config,
unsigned char* opt) {
unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
if (opt) {
unsigned int req_time = option_uint(opt, 0, 4);
if (req_time < 120) req_time = 120; /* sanity */
if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time)) time = req_time;
}
return time;
}
static struct in_addr server_id(struct dhcp_context* context, struct in_addr override,
struct in_addr fallback) {
if (override.s_addr != 0)
return override;
else if (context)
return context->local;
else
return fallback;
}
static int sanitise(unsigned char* opt, char* buf) {
char* p;
int i;
*buf = 0;
if (!opt) return 0;
p = option_ptr(opt, 0);
for (i = option_len(opt); i > 0; i--) {
char c = *p++;
if (isprint((int) c)) *buf++ = c;
}
*buf = 0; /* add terminator */
return 1;
}
static void log_packet(char* type, void* addr, unsigned char* ext_mac, int mac_len, char* interface,
char* string, u32 xid) {
struct in_addr a;
/* addr may be misaligned */
if (addr) memcpy(&a, addr, sizeof(a));
print_mac(daemon->namebuff, ext_mac, mac_len);
if (daemon->options & OPT_LOG_OPTS)
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s", ntohl(xid), type, interface,
addr ? inet_ntoa(a) : "", addr ? " " : "", daemon->namebuff, string ? string : "");
else
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s", type, interface, addr ? inet_ntoa(a) : "",
addr ? " " : "", daemon->namebuff, string ? string : "");
}
static void log_options(unsigned char* start, u32 xid) {
while (*start != OPTION_END) {
int is_ip, is_name, i;
char* text = option_string(start[0], &is_ip, &is_name);
unsigned char trunc = option_len(start);
if (is_ip)
for (daemon->namebuff[0] = 0, i = 0; i <= trunc - INADDRSZ; i += INADDRSZ) {
if (i != 0) strncat(daemon->namebuff, ", ", 256 - strlen(daemon->namebuff));
strncat(daemon->namebuff, inet_ntoa(option_addr_arr(start, i)),
256 - strlen(daemon->namebuff));
}
else if (!is_name || !sanitise(start, daemon->namebuff)) {
if (trunc > 13) trunc = 13;
print_mac(daemon->namebuff, option_ptr(start, 0), trunc);
}
my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d%s%s%s%s%s", ntohl(xid),
option_len(start), start[0], text ? ":" : "", text ? text : "",
trunc == 0 ? "" : " ", trunc == 0 ? "" : daemon->namebuff,
trunc == option_len(start) ? "" : "...");
start += start[1] + 2;
}
}
static unsigned char* option_find1(unsigned char* p, unsigned char* end, int opt, int minsize) {
while (1) {
if (p > end)
return NULL;
else if (*p == OPTION_END)
return opt == OPTION_END ? p : NULL;
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;
}
}
}
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] + sizeof(u32), ((unsigned char*) mess) + size,
opt_type, minsize)))
return ret;
/* look for overload option. */
if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((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 struct in_addr option_addr_arr(unsigned char* opt, int offset) {
/* this worries about unaligned data in the option. */
/* struct in_addr is network byte order */
struct in_addr ret;
memcpy(&ret, option_ptr(opt, offset), INADDRSZ);
return ret;
}
static struct in_addr option_addr(unsigned char* opt) {
return option_addr_arr(opt, 0);
}
static unsigned int option_uint(unsigned char* opt, int offset, int size) {
/* this worries about unaligned data and byte order */
unsigned int ret = 0;
int i;
unsigned char* p = option_ptr(opt, offset);
for (i = 0; i < size; i++) ret = (ret << 8) | *p++;
return ret;
}
static unsigned char* dhcp_skip_opts(unsigned char* start) {
while (*start != 0) start += start[1] + 2;
return start;
}
/* only for use when building packet: doesn't check for bad data. */
static unsigned char* find_overload(struct dhcp_packet* mess) {
unsigned char* p = &mess->options[0] + sizeof(u32);
while (*p != 0) {
if (*p == OPTION_OVERLOAD) return p;
p += p[1] + 2;
}
return NULL;
}
static size_t dhcp_packet_size(struct dhcp_packet* mess, struct dhcp_netid* netid,
unsigned char* agent_id, unsigned char* real_end) {
unsigned char* p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
unsigned char* overload;
size_t ret;
struct dhcp_netid_list* id_list;
struct dhcp_netid* n;
/* move agent_id back down to the end of the packet */
if (agent_id) {
memmove(p, agent_id, real_end - agent_id);
p += real_end - agent_id;
memset(p, 0, real_end - p); /* in case of overlap */
}
/* We do logging too */
if (netid && (daemon->options & OPT_LOG_OPTS)) {
char* s = daemon->namebuff;
for (*s = 0; netid; netid = netid->next) {
/* kill dupes. */
for (n = netid->next; n; n = n->next)
if (strcmp(netid->net, n->net) == 0) break;
if (!n) {
strncat(s, netid->net, (MAXDNAME - 1) - strlen(s));
if (netid->next) strncat(s, ", ", (MAXDNAME - 1) - strlen(s));
}
}
my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), ntohl(mess->xid), s);
}
/* add END options to the regions. */
overload = find_overload(mess);
if (overload && (option_uint(overload, 0, 1) & 1)) {
*dhcp_skip_opts(mess->file) = OPTION_END;
if (daemon->options & OPT_LOG_OPTS) log_options(mess->file, mess->xid);
} else if ((daemon->options & OPT_LOG_OPTS) && strlen((char*) mess->file) != 0)
my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid),
(char*) mess->file);
if (overload && (option_uint(overload, 0, 1) & 2)) {
*dhcp_skip_opts(mess->sname) = OPTION_END;
if (daemon->options & OPT_LOG_OPTS) log_options(mess->sname, mess->xid);
} else if ((daemon->options & OPT_LOG_OPTS) && strlen((char*) mess->sname) != 0)
my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid),
(char*) mess->sname);
*p++ = OPTION_END;
if (daemon->options & OPT_LOG_OPTS) {
if (mess->siaddr.s_addr != 0)
my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid),
inet_ntoa(mess->siaddr));
log_options(&mess->options[0] + sizeof(u32), mess->xid);
}
for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
if (match_netid(id_list->list, netid, 0))
mess->flags |= htons(0x8000); /* force broadcast */
ret = (size_t)(p - (unsigned char*) mess);
if (ret < MIN_PACKETSZ) ret = MIN_PACKETSZ;
return ret;
}
static unsigned char* free_space(struct dhcp_packet* mess, unsigned char* end, int opt, int len) {
unsigned char* p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
if (p + len + 3 >= end)
/* not enough space in options area, try and use overload, if poss */
{
unsigned char* overload;
if (!(overload = find_overload(mess)) && (mess->file[0] == 0 || mess->sname[0] == 0)) {
/* attempt to overload fname and sname areas, we've reserved space for the
overflow option previuously. */
overload = p;
*(p++) = OPTION_OVERLOAD;
*(p++) = 1;
}
p = NULL;
/* using filename field ? */
if (overload) {
if (mess->file[0] == 0) overload[2] |= 1;
if (overload[2] & 1) {
p = dhcp_skip_opts(mess->file);
if (p + len + 3 >= mess->file + sizeof(mess->file)) p = NULL;
}
if (!p) {
/* try to bring sname into play (it may be already) */
if (mess->sname[0] == 0) overload[2] |= 2;
if (overload[2] & 2) {
p = dhcp_skip_opts(mess->sname);
if (p + len + 3 >= mess->sname + sizeof(mess->file)) p = NULL;
}
}
}
if (!p)
my_syslog(MS_DHCP | LOG_WARNING,
_("cannot send DHCP/BOOTP option %d: no space left in packet"), opt);
}
if (p) {
*(p++) = opt;
*(p++) = len;
}
return p;
}
static void option_put(struct dhcp_packet* mess, unsigned char* end, int opt, int len,
unsigned int val) {
int i;
unsigned char* p = free_space(mess, end, opt, len);
if (p)
for (i = 0; i < len; i++) *(p++) = val >> (8 * (len - (i + 1)));
}
static void option_put_string(struct dhcp_packet* mess, unsigned char* end, int opt, char* string,
int null_term) {
unsigned char* p;
size_t len = strlen(string);
if (null_term && len != 255) len++;
if ((p = free_space(mess, end, opt, len))) memcpy(p, string, len);
}
/* return length, note this only does the data part */
static int do_opt(struct dhcp_opt* opt, unsigned char* p, struct dhcp_context* context,
int null_term) {
int len = opt->len;
if ((opt->flags & DHOPT_STRING) && null_term && len != 255) len++;
if (p && len != 0) {
if (context && (opt->flags & DHOPT_ADDR)) {
int j;
struct in_addr* a = (struct in_addr*) opt->val;
for (j = 0; j < opt->len; j += INADDRSZ, a++) {
/* zero means "self" (but not in vendorclass options.) */
if (a->s_addr == 0)
memcpy(p, &context->local, INADDRSZ);
else
memcpy(p, a, INADDRSZ);
p += INADDRSZ;
}
} else
memcpy(p, opt->val, len);
}
return len;
}
static int in_list(unsigned char* list, int opt) {
int i;
/* If no requested options, send everything, not nothing. */
if (!list) return 1;
for (i = 0; list[i] != OPTION_END; i++)
if (opt == list[i]) return 1;
return 0;
}
static struct dhcp_opt* option_find2(struct dhcp_netid* netid, struct dhcp_opt* opts, int opt) {
struct dhcp_opt* tmp;
for (tmp = opts; tmp; tmp = tmp->next)
if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)))
if (match_netid(tmp->netid, netid, netid ? 0 : 1)) return tmp;
return netid ? option_find2(NULL, opts, opt) : NULL;
}
/* mark vendor-encapsulated options which match the client-supplied or
config-supplied vendor class */
static void match_vendor_opts(unsigned char* opt, struct dhcp_opt* dopt) {
for (; dopt; dopt = dopt->next) {
dopt->flags &= ~DHOPT_VENDOR_MATCH;
if (opt && (dopt->flags & DHOPT_VENDOR)) {
int i, len = 0;
if (dopt->u.vendor_class) len = strlen((char*) dopt->u.vendor_class);
for (i = 0; i <= (option_len(opt) - len); i++)
if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0) {
dopt->flags |= DHOPT_VENDOR_MATCH;
break;
}
}
}
}
static void do_encap_opts(struct dhcp_opt* opt, int encap, int flag, struct dhcp_packet* mess,
unsigned char* end, int null_term) {
int len, enc_len;
struct dhcp_opt* start;
unsigned char* p;
/* find size in advance */
for (enc_len = 0, start = opt; opt; opt = opt->next)
if (opt->flags & flag) {
int new = do_opt(opt, NULL, NULL, null_term) + 2;
if (enc_len + new <= 255)
enc_len += new;
else {
p = free_space(mess, end, encap, enc_len);
for (; start && start != opt; start = start->next)
if (p && (start->flags & flag)) {
len = do_opt(start, p + 2, NULL, null_term);
*(p++) = start->opt;
*(p++) = len;
p += len;
}
enc_len = new;
start = opt;
}
}
if (enc_len != 0 && (p = free_space(mess, end, encap, enc_len + 1))) {
for (; start; start = start->next)
if (start->flags & flag) {
len = do_opt(start, p + 2, NULL, null_term);
*(p++) = start->opt;
*(p++) = len;
p += len;
}
*p = OPTION_END;
}
}
static void pxe_misc(struct dhcp_packet* mess, unsigned char* end, unsigned char* uuid) {
unsigned char* p;
option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17))) memcpy(p, uuid, 17);
}
static int prune_vendor_opts(struct dhcp_netid* netid) {
int force = 0;
struct dhcp_opt* opt;
/* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
for (opt = daemon->dhcp_opts; opt; opt = opt->next)
if (opt->flags & DHOPT_VENDOR_MATCH) {
if (!match_netid(opt->netid, netid, 1))
opt->flags &= ~DHOPT_VENDOR_MATCH;
else if (opt->flags & DHOPT_FORCE)
force = 1;
}
return force;
}
static struct dhcp_opt* pxe_opts(int pxe_arch, struct dhcp_netid* netid) {
#define NUM_OPTS 4
unsigned char *p, *q;
struct pxe_service* service;
static struct dhcp_opt *o, *ret;
int i, j = NUM_OPTS - 1;
/* We pass back references to these, hence they are declared static */
static unsigned char discovery_control;
static unsigned char fake_prompt[] = {0, 'P', 'X', 'E'};
static struct dhcp_opt* fake_opts = NULL;
/* We are found by broadcast, so disable multicast. It gets switched on again
if we point to other servers and don't give a unicast address. Note that
we don't provide our own address for services we are the boot server for because unicast
discovery is to port 4011 and we don't listen there. If you are using proxy DHCP
and DHCP relays, the relay will need to forward to the proxy too. */
discovery_control = 2;
ret = daemon->dhcp_opts;
if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt)))) return ret;
for (i = 0; i < NUM_OPTS; i++) {
fake_opts[i].flags = DHOPT_VENDOR_MATCH;
fake_opts[i].netid = NULL;
fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i + 1];
}
/* create the data for the PXE_MENU and PXE_SERVERS options. */
p = (unsigned char*) daemon->dhcp_buff;
q = (unsigned char*) daemon->dhcp_buff2;
for (i = 0, service = daemon->pxe_services; service; service = service->next)
if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1)) {
size_t len = strlen(service->menu);
/* opt 43 max size is 255. encapsulated option has type and length
bytes, so its max size is 253. */
if (p - (unsigned char*) daemon->dhcp_buff + len + 3 < 253) {
*(p++) = service->type >> 8;
*(p++) = service->type;
*(p++) = len;
memcpy(p, service->menu, len);
p += len;
i++;
} else {
toobig:
my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
return daemon->dhcp_opts;
}
if (!service->basename) {
if (service->server.s_addr != 0) {
if (q - (unsigned char*) daemon->dhcp_buff2 + 3 + INADDRSZ >= 253) goto toobig;
/* Boot service with known address - give it */
*(q++) = service->type >> 8;
*(q++) = service->type;
*(q++) = 1;
/* dest misaligned */
memcpy(q, &service->server.s_addr, INADDRSZ);
q += INADDRSZ;
} else if (service->type != 0)
/* We're not supplying a server, so let the client multicast.
type zero is "local boot" so no need for M/C on that. */
discovery_control = 0;
}
}
/* if no prompt, wait forever if there's a choice */
fake_prompt[0] = (i > 1) ? 255 : 0;
if (i == 0)
discovery_control = 8; /* no menu - just use use mess->filename */
else {
ret = &fake_opts[j--];
ret->len = p - (unsigned char*) daemon->dhcp_buff;
ret->val = (unsigned char*) daemon->dhcp_buff;
ret->opt = SUBOPT_PXE_MENU;
if (q - (unsigned char*) daemon->dhcp_buff2 != 0) {
ret = &fake_opts[j--];
ret->len = q - (unsigned char*) daemon->dhcp_buff2;
ret->val = (unsigned char*) daemon->dhcp_buff2;
ret->opt = SUBOPT_PXE_SERVERS;
}
}
for (o = daemon->dhcp_opts; o; o = o->next)
if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT) break;
if (!o) {
ret = &fake_opts[j--];
ret->len = sizeof(fake_prompt);
ret->val = fake_prompt;
ret->opt = SUBOPT_PXE_MENU_PROMPT;
}
if (discovery_control != 0) {
ret = &fake_opts[j--];
ret->len = 1;
ret->opt = SUBOPT_PXE_DISCOVERY;
ret->val = &discovery_control;
}
return ret;
}
static void clear_packet(struct dhcp_packet* mess, unsigned char* end) {
memset(mess->sname, 0, sizeof(mess->sname));
memset(mess->file, 0, sizeof(mess->file));
memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
mess->siaddr.s_addr = 0;
}
struct dhcp_boot* find_boot(struct dhcp_netid* netid) {
struct dhcp_boot* boot;
/* decide which dhcp-boot option we're using */
for (boot = daemon->boot_config; boot; boot = boot->next)
if (match_netid(boot->netid, netid, 0)) break;
if (!boot) /* No match, look for one without a netid */
for (boot = daemon->boot_config; boot; boot = boot->next)
if (match_netid(boot->netid, netid, 1)) break;
return boot;
}
static void do_options(struct dhcp_context* context, struct dhcp_packet* mess, unsigned char* end,
unsigned char* req_options, char* hostname, char* domain,
char* config_domain, struct dhcp_netid* netid, struct in_addr subnet_addr,
unsigned char fqdn_flags, int null_term, int pxe_arch, unsigned char* uuid) {
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
struct dhcp_boot* boot;
unsigned char* p;
int i, len, force_encap = 0;
unsigned char f0 = 0, s0 = 0;
int done_file = 0, done_server = 0;
if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"),
config_domain, hostname);
/* logging */
if ((daemon->options & OPT_LOG_OPTS) && req_options) {
char* q = daemon->namebuff;
for (i = 0; req_options[i] != OPTION_END; i++) {
char* s = option_string(req_options[i], NULL, NULL);
q += snprintf(q, MAXDNAME - (q - daemon->namebuff), "%d%s%s%s", req_options[i],
s ? ":" : "", s ? s : "", req_options[i + 1] == OPTION_END ? "" : ", ");
if (req_options[i + 1] == OPTION_END || (q - daemon->namebuff) > 40) {
q = daemon->namebuff;
my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid),
daemon->namebuff);
}
}
}
if (context) mess->siaddr = context->local;
/* See if we can send the boot stuff as options.
To do this we need a requested option list, BOOTP
and very old DHCP clients won't have this, we also
provide an manual option to disable it.
Some PXE ROMs have bugs (surprise!) and need zero-terminated
names, so we always send those. */
if ((boot = find_boot(netid))) {
if (boot->sname) {
if (!(daemon->options & OPT_NO_OVERRIDE) && req_options &&
in_list(req_options, OPTION_SNAME))
option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
else
strncpy((char*) mess->sname, boot->sname, sizeof(mess->sname) - 1);
}
if (boot->file) {
if (!(daemon->options & OPT_NO_OVERRIDE) && req_options &&
in_list(req_options, OPTION_FILENAME))
option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
else
strncpy((char*) mess->file, boot->file, sizeof(mess->file) - 1);
}
if (boot->next_server.s_addr) mess->siaddr = boot->next_server;
} else
/* Use the values of the relevant options if no dhcp-boot given and
they're not explicitly asked for as options. OPTION_END is used
as an internal way to specify siaddr without using dhcp-boot, for use in
dhcp-optsfile. */
{
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) && mess->file[0] == 0 &&
(opt = option_find2(netid, config_opts, OPTION_FILENAME)) &&
!(opt->flags & DHOPT_FORCE)) {
strncpy((char*) mess->file, (char*) opt->val, sizeof(mess->file) - 1);
done_file = 1;
}
if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
(opt = option_find2(netid, config_opts, OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE)) {
strncpy((char*) mess->sname, (char*) opt->val, sizeof(mess->sname) - 1);
done_server = 1;
}
if ((opt = option_find2(netid, config_opts, OPTION_END)))
mess->siaddr.s_addr = ((struct in_addr*) opt->val)->s_addr;
}
/* We don't want to do option-overload for BOOTP, so make the file and sname
fields look like they are in use, even when they aren't. This gets restored
at the end of this function. */
if (!req_options || (daemon->options & OPT_NO_OVERRIDE)) {
f0 = mess->file[0];
mess->file[0] = 1;
s0 = mess->sname[0];
mess->sname[0] = 1;
}
/* At this point, if mess->sname or mess->file are zeroed, they are available
for option overload, reserve space for the overload option. */
if (mess->file[0] == 0 || mess->sname[0] == 0) end -= 3;
/* rfc3011 says this doesn't need to be in the requested options list. */
if (subnet_addr.s_addr)
option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
/* replies to DHCPINFORM may not have a valid context */
if (context) {
if (!option_find2(netid, config_opts, OPTION_NETMASK))
option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
/* May not have a "guessed" broadcast address if we got no packets via a relay
from this net yet (ie just unicast renewals after a restart */
if (context->broadcast.s_addr && !option_find2(netid, config_opts, OPTION_BROADCAST))
option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
/* Same comments as broadcast apply, and also may not be able to get a sensible
default when using subnet select. User must configure by steam in that case. */
if (context->router.s_addr && in_list(req_options, OPTION_ROUTER) &&
!option_find2(netid, config_opts, OPTION_ROUTER))
option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
if (in_list(req_options, OPTION_DNSSERVER) &&
!option_find2(netid, config_opts, OPTION_DNSSERVER))
option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
}
if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
!option_find2(netid, config_opts, OPTION_DOMAINNAME))
option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
/* Note that we ignore attempts to set the fqdn using --dhc-option=81, */
if (hostname) {
if (in_list(req_options, OPTION_HOSTNAME) &&
!option_find2(netid, config_opts, OPTION_HOSTNAME))
option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
if (fqdn_flags != 0) {
len = strlen(hostname) + 3;
if (fqdn_flags & 0x04)
len += 2;
else if (null_term)
len++;
if (domain) len += strlen(domain) + 1;
if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len))) {
*(p++) = fqdn_flags;
*(p++) = 255;
*(p++) = 255;
if (fqdn_flags & 0x04) {
p = do_rfc1035_name(p, hostname, NULL);
if (domain) p = do_rfc1035_name(p, domain, NULL);
*p++ = 0;
} else {
memcpy(p, hostname, strlen(hostname));
p += strlen(hostname);
if (domain) {
*(p++) = '.';
memcpy(p, domain, strlen(domain));
p += strlen(domain);
}
if (null_term) *(p++) = 0;
}
}
}
}
for (opt = config_opts; opt; opt = opt->next) {
int optno = opt->opt;
/* was it asked for, or are we sending it anyway? */
if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno)) continue;
/* prohibit some used-internally options */
if (optno == OPTION_CLIENT_FQDN || optno == OPTION_MAXMESSAGE || optno == OPTION_OVERLOAD ||
optno == OPTION_PAD || optno == OPTION_END)
continue;
if (optno == OPTION_SNAME && done_server) continue;
if (optno == OPTION_FILENAME && done_file) continue;
/* netids match and not encapsulated? */
if (opt != option_find2(netid, config_opts, optno)) continue;
/* For the options we have default values on
dhc-option= means "don't include this option"
not "include a zero-length option" */
if (opt->len == 0 &&
(optno == OPTION_NETMASK || optno == OPTION_BROADCAST || optno == OPTION_ROUTER ||
optno == OPTION_DNSSERVER || optno == OPTION_DOMAINNAME || optno == OPTION_HOSTNAME))
continue;
/* vendor-class comes from elsewhere for PXE */
if (pxe_arch != -1 && optno == OPTION_VENDOR_ID) continue;
/* always force null-term for filename and servername - buggy PXE again. */
len = do_opt(opt, NULL, context,
(optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
if ((p = free_space(mess, end, optno, len))) {
do_opt(opt, p, context,
(optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
/* If we send a vendor-id, revisit which vendor-ops we consider
it appropriate to send. */
if (optno == OPTION_VENDOR_ID) match_vendor_opts(p - 2, config_opts);
}
}
/* Now send options to be encapsulated in arbitrary options,
eg dhcp-option=encap:172,17,.......
The may be more that one "outer" to do, so group
all the options which match each outer in turn. */
for (opt = config_opts; opt; opt = opt->next) opt->flags &= ~DHOPT_ENCAP_DONE;
for (opt = config_opts; opt; opt = opt->next)
if ((opt->flags & (DHOPT_ENCAPSULATE | DHOPT_ENCAP_DONE)) == DHOPT_ENCAPSULATE) {
struct dhcp_opt* o;
int found = 0;
for (o = config_opts; o; o = o->next) {
o->flags &= ~DHOPT_ENCAP_MATCH;
if ((o->flags & DHOPT_ENCAPSULATE) && opt->u.encap == o->u.encap) {
o->flags |= DHOPT_ENCAP_DONE;
if (match_netid(o->netid, netid, 1) &&
(o->flags & DHOPT_FORCE || in_list(req_options, o->u.encap))) {
o->flags |= DHOPT_ENCAP_MATCH;
found = 1;
}
}
}
if (found)
do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
}
/* Must precede pxe_opts, since it overwrites req_options */
force_encap = prune_vendor_opts(netid);
if (in_list(req_options, OPTION_VENDOR_CLASS_OPT)) force_encap = 1;
if (pxe_arch != -1) {
pxe_misc(mess, end, uuid);
config_opts = pxe_opts(pxe_arch, netid);
}
if (force_encap)
do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end,
null_term);
/* restore BOOTP anti-overload hack */
if (!req_options || (daemon->options & OPT_NO_OVERRIDE)) {
mess->file[0] = f0;
mess->sname[0] = s0;
}
}
#endif
./src/nameser.h 0000644 0000000 0000000 00000037162 14256750714 012327 0 ustar root root /* $OpenBSD: nameser.h,v 1.11 2005/12/20 02:06:56 millert Exp $ */
/*
* ++Copyright++ 1983, 1989, 1993
* -
* Copyright (c) 1983, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* -
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies, and that
* the name of Digital Equipment Corporation not be used in advertising or
* publicity pertaining to distribution of the document or software without
* specific, written prior permission.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
* -
* Portions Copyright (c) 1995 by International Business Machines, Inc.
*
* International Business Machines, Inc. (hereinafter called IBM) grants
* permission under its copyrights to use, copy, modify, and distribute this
* Software with or without fee, provided that the above copyright notice and
* all paragraphs of this notice appear in all copies, and that the name of IBM
* not be used in connection with the marketing of any product incorporating
* the Software or modifications thereof, without specific, written prior
* permission.
*
* To the extent it has a right to do so, IBM grants an immunity from suit
* under its patents, if any, for the use, sale or manufacture of products to
* the extent that such products are used for performing Domain Name System
* dynamic updates in TCP/IP networks by means of the Software. No immunity is
* granted for any product per se or for any other function of any product.
*
* THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
* DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
* IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
* --Copyright--
*/
/*
* @(#)nameser.h 8.1 (Berkeley) 6/2/93
* $From: nameser.h,v 8.11 1996/10/08 04:51:02 vixie Exp $
*/
#ifndef _NAMESER_H_
#define _NAMESER_H_
#include
#include
/*
* revision information. this is the release date in YYYYMMDD format.
* it can change every day so the right thing to do with it is use it
* in preprocessor commands such as "#if (__BIND > 19931104)". do not
* compare for equality; rather, use it to determine whether your resolver
* is new enough to contain a certain feature.
*/
#define __BIND 19960801 /* interface version stamp */
/*
* Define constants based on rfc883
*/
#define PACKETSZ 512 /* maximum packet size */
#define MAXDNAME 1025 /* maximum presentation domain name */
#define MAXCDNAME 255 /* maximum compressed domain name */
#define MAXLABEL 63 /* maximum length of domain label */
#define HFIXEDSZ 12 /* #/bytes of fixed data in header */
#define QFIXEDSZ 4 /* #/bytes of fixed data in query */
#define RRFIXEDSZ 10 /* #/bytes of fixed data in r record */
#define INT32SZ 4 /* for systems without 32-bit ints */
#define INT16SZ 2 /* for systems without 16-bit ints */
#define INADDRSZ 4 /* IPv4 T_A */
#define IN6ADDRSZ 16 /* IPv6 T_AAAA */
/*
* Internet nameserver port number
*/
#define NAMESERVER_PORT 53
/*
* Currently defined opcodes
*/
#define QUERY 0x0 /* standard query */
#define IQUERY 0x1 /* inverse query */
#define STATUS 0x2 /* nameserver status query */
/*#define xxx 0x3*/ /* 0x3 reserved */
#define NS_NOTIFY_OP 0x4 /* notify secondary of SOA change */
/*
* Currently defined response codes
*/
#define NOERROR 0 /* no error */
#define FORMERR 1 /* format error */
#define SERVFAIL 2 /* server failure */
#define NXDOMAIN 3 /* non existent domain */
#define NOTIMP 4 /* not implemented */
#define REFUSED 5 /* query refused */
/*
* Type values for resources and queries
*/
#define T_A 1 /* host address */
#define T_NS 2 /* authoritative server */
#define T_MD 3 /* mail destination */
#define T_MF 4 /* mail forwarder */
#define T_CNAME 5 /* canonical name */
#define T_SOA 6 /* start of authority zone */
#define T_MB 7 /* mailbox domain name */
#define T_MG 8 /* mail group member */
#define T_MR 9 /* mail rename name */
#define T_NULL 10 /* null resource record */
#define T_WKS 11 /* well known service */
#define T_PTR 12 /* domain name pointer */
#define T_HINFO 13 /* host information */
#define T_MINFO 14 /* mailbox information */
#define T_MX 15 /* mail routing information */
#define T_TXT 16 /* text strings */
#define T_RP 17 /* responsible person */
#define T_AFSDB 18 /* AFS cell database */
#define T_X25 19 /* X_25 calling address */
#define T_ISDN 20 /* ISDN calling address */
#define T_RT 21 /* router */
#define T_NSAP 22 /* NSAP address */
#define T_NSAP_PTR 23 /* reverse NSAP lookup (deprecated) */
#define T_SIG 24 /* security signature */
#define T_KEY 25 /* security key */
#define T_PX 26 /* X.400 mail mapping */
#define T_GPOS 27 /* geographical position (withdrawn) */
#define T_AAAA 28 /* IP6 Address */
#define T_LOC 29 /* Location Information */
#define T_NXT 30 /* Next Valid Name in Zone */
#define T_EID 31 /* Endpoint identifier */
#define T_NIMLOC 32 /* Nimrod locator */
#define T_SRV 33 /* Server selection */
#define T_ATMA 34 /* ATM Address */
#define T_NAPTR 35 /* Naming Authority PoinTeR */
#define T_KX 36 /* Key Exchanger */
#define T_CERT 37 /* CERT */
#define T_A6 38 /* A6 */
#define T_DNAME 39 /* DNAME */
#define T_SINK 40 /* SINK */
#define T_OPT 41 /* OPT pseudo-RR, RFC2671 */
#define T_APL 42 /* APL */
#define T_DS 43 /* Delegation Signer */
#define T_SSHFP 44 /* SSH Key Fingerprint */
#define T_RRSIG 46 /* RRSIG */
#define T_NSEC 47 /* NSEC */
#define T_DNSKEY 48 /* DNSKEY */
/* non standard */
#define T_UINFO 100 /* user (finger) information */
#define T_UID 101 /* user ID */
#define T_GID 102 /* group ID */
#define T_UNSPEC 103 /* Unspecified format (binary data) */
/* Query type values which do not appear in resource records */
#define T_TKEY 249 /* Transaction Key */
#define T_TSIG 250 /* Transaction Signature */
#define T_IXFR 251 /* incremental zone transfer */
#define T_AXFR 252 /* transfer zone of authority */
#define T_MAILB 253 /* transfer mailbox records */
#define T_MAILA 254 /* transfer mail agent records */
#define T_ANY 255 /* wildcard match */
/*
* Values for class field
*/
#define C_IN 1 /* the arpa internet */
#define C_CHAOS 3 /* for chaos net (MIT) */
#define C_HS 4 /* for Hesiod name server (MIT) (XXX) */
/* Query class values which do not appear in resource records */
#define C_ANY 255 /* wildcard match */
/*
* Flags field of the KEY RR rdata
*/
#define KEYFLAG_TYPEMASK 0xC000 /* Mask for "type" bits */
#define KEYFLAG_TYPE_AUTH_CONF 0x0000 /* Key usable for both */
#define KEYFLAG_TYPE_CONF_ONLY 0x8000 /* Key usable for confidentiality */
#define KEYFLAG_TYPE_AUTH_ONLY 0x4000 /* Key usable for authentication */
#define KEYFLAG_TYPE_NO_KEY 0xC000 /* No key usable for either; no key */
/* The type bits can also be interpreted independently, as single bits: */
#define KEYFLAG_NO_AUTH 0x8000 /* Key not usable for authentication */
#define KEYFLAG_NO_CONF 0x4000 /* Key not usable for confidentiality */
#define KEYFLAG_EXPERIMENTAL 0x2000 /* Security is *mandatory* if bit=0 */
#define KEYFLAG_RESERVED3 0x1000 /* reserved - must be zero */
#define KEYFLAG_RESERVED4 0x0800 /* reserved - must be zero */
#define KEYFLAG_USERACCOUNT 0x0400 /* key is assoc. with a user acct */
#define KEYFLAG_ENTITY 0x0200 /* key is assoc. with entity eg host */
#define KEYFLAG_ZONEKEY 0x0100 /* key is zone key for the zone named */
#define KEYFLAG_IPSEC 0x0080 /* key is for IPSEC use (host or user)*/
#define KEYFLAG_EMAIL 0x0040 /* key is for email (MIME security) */
#define KEYFLAG_RESERVED10 0x0020 /* reserved - must be zero */
#define KEYFLAG_RESERVED11 0x0010 /* reserved - must be zero */
#define KEYFLAG_SIGNATORYMASK 0x000F /* key can sign DNS RR's of same name */
#define KEYFLAG_RESERVED_BITMASK \
(KEYFLAG_RESERVED3 | KEYFLAG_RESERVED4 | KEYFLAG_RESERVED10 | KEYFLAG_RESERVED11)
/* The Algorithm field of the KEY and SIG RR's is an integer, {1..254} */
#define ALGORITHM_MD5RSA 1 /* MD5 with RSA */
#define ALGORITHM_EXPIRE_ONLY 253 /* No alg, no security */
#define ALGORITHM_PRIVATE_OID 254 /* Key begins with OID indicating alg */
/* Signatures */
/* Size of a mod or exp in bits */
#define MIN_MD5RSA_KEY_PART_BITS 512
#define MAX_MD5RSA_KEY_PART_BITS 2552
/* Total of binary mod and exp, bytes */
#define MAX_MD5RSA_KEY_BYTES ((MAX_MD5RSA_KEY_PART_BITS + 7 / 8) * 2 + 3)
/* Max length of text sig block */
#define MAX_KEY_BASE64 (((MAX_MD5RSA_KEY_BYTES + 2) / 3) * 4)
/*
* EDNS0 Z-field extended flags
*/
#define DNS_MESSAGEEXTFLAG_DO 0x8000U
/*
* Status return codes for T_UNSPEC conversion routines
*/
#define CONV_SUCCESS 0
#define CONV_OVERFLOW (-1)
#define CONV_BADFMT (-2)
#define CONV_BADCKSUM (-3)
#define CONV_BADBUFLEN (-4)
#if !defined(_BYTE_ORDER) || \
(_BYTE_ORDER != _BIG_ENDIAN && _BYTE_ORDER != _LITTLE_ENDIAN && _BYTE_ORDER != _PDP_ENDIAN)
/* you must determine what the correct bit order is for
* your compiler - the next line is an intentional error
* which will force your compiles to bomb until you fix
* the above macros.
*/
#error "Undefined or invalid _BYTE_ORDER";
#endif
/*
* Structure for query header. The order of the fields is machine- and
* compiler-dependent, depending on the byte/bit order and the layout
* of bit fields. We use bit fields only in int variables, as this
* is all ANSI requires. This requires a somewhat confusing rearrangement.
*/
typedef struct {
unsigned id : 16; /* query identification number */
#if _BYTE_ORDER == _BIG_ENDIAN
/* fields in third byte */
unsigned qr : 1; /* response flag */
unsigned opcode : 4; /* purpose of message */
unsigned aa : 1; /* authoritive answer */
unsigned tc : 1; /* truncated message */
unsigned rd : 1; /* recursion desired */
/* fields in fourth byte */
unsigned ra : 1; /* recursion available */
unsigned unused : 1; /* unused bits (MBZ as of 4.9.3a3) */
unsigned ad : 1; /* authentic data from named */
unsigned cd : 1; /* checking disabled by resolver */
unsigned rcode : 4; /* response code */
#endif
#if _BYTE_ORDER == _LITTLE_ENDIAN || _BYTE_ORDER == _PDP_ENDIAN
/* fields in third byte */
unsigned rd : 1; /* recursion desired */
unsigned tc : 1; /* truncated message */
unsigned aa : 1; /* authoritive answer */
unsigned opcode : 4; /* purpose of message */
unsigned qr : 1; /* response flag */
/* fields in fourth byte */
unsigned rcode : 4; /* response code */
unsigned cd : 1; /* checking disabled by resolver */
unsigned ad : 1; /* authentic data from named */
unsigned unused : 1; /* unused bits (MBZ as of 4.9.3a3) */
unsigned ra : 1; /* recursion available */
#endif
/* remaining bytes */
unsigned qdcount : 16; /* number of question entries */
unsigned ancount : 16; /* number of answer entries */
unsigned nscount : 16; /* number of authority entries */
unsigned arcount : 16; /* number of resource entries */
} HEADER;
/*
* Defines for handling compressed domain names
*/
#define INDIR_MASK 0xc0
extern u_int16_t _getshort(const unsigned char*);
extern u_int32_t _getlong(const unsigned char*);
/*
* Inline versions of get/put short/long. Pointer is advanced.
*
* These macros demonstrate the property of C whereby it can be
* portable or it can be elegant but rarely both.
*/
#define GETSHORT(s, cp) \
{ \
unsigned char* t_cp = (unsigned char*) (cp); \
(s) = ((u_int16_t) t_cp[0] << 8) | ((u_int16_t) t_cp[1]); \
(cp) += INT16SZ; \
}
#define GETLONG(l, cp) \
{ \
unsigned char* t_cp = (unsigned char*) (cp); \
(l) = ((u_int32_t) t_cp[0] << 24) | ((u_int32_t) t_cp[1] << 16) | \
((u_int32_t) t_cp[2] << 8) | ((u_int32_t) t_cp[3]); \
(cp) += INT32SZ; \
}
#define PUTSHORT(s, cp) \
{ \
u_int16_t t_s = (u_int16_t)(s); \
unsigned char* t_cp = (unsigned char*) (cp); \
*t_cp++ = t_s >> 8; \
*t_cp = t_s; \
(cp) += INT16SZ; \
}
#define PUTLONG(l, cp) \
{ \
u_int32_t t_l = (u_int32_t)(l); \
unsigned char* t_cp = (unsigned char*) (cp); \
*t_cp++ = t_l >> 24; \
*t_cp++ = t_l >> 16; \
*t_cp++ = t_l >> 8; \
*t_cp = t_l; \
(cp) += INT32SZ; \
}
#endif /* !_NAMESER_H_ */
./src/.clang-format 0000644 0000000 0000000 00000000565 14256750714 013074 0 ustar root root # https://clang.llvm.org/docs/ClangFormatStyleOptions.html
#
BasedOnStyle: Google
AccessModifierOffset: -2
AllowShortFunctionsOnASingleLine: Inline
BreakBeforeTernaryOperators: true
ColumnLimit: 100
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
IndentWidth: 4
PointerAlignment: Left
SpaceAfterCStyleCast: true
TabWidth: 4
UseTab: Never
PenaltyExcessCharacter: 32
./src/Android.bp 0000644 0000000 0000000 00000001233 14256750714 012415 0 ustar root root cc_binary {
name: "dnsmasq",
srcs: [
"cache.c",
"dhcp.c",
"dnsmasq.c",
"forward.c",
"helper.c",
"lease.c",
"log.c",
"netlink.c",
"network.c",
"option.c",
"rfc1035.c",
"rfc2131.c",
"util.c",
],
cflags: [
"-O2",
"-g",
"-W",
"-Wall",
"-D__ANDROID__",
"-DIPV6",
"-DNO_SCRIPT",
"-D_BSD_SOURCE",
"-Wno-unused-variable",
"-Wno-unused-parameter",
"-Werror",
],
system_shared_libs: ["libc"],
shared_libs: [
"libcutils",
"liblog",
],
}
./src/log.c 0000644 0000000 0000000 00000027105 14256750714 011445 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "dnsmasq.h"
#ifdef __ANDROID__
#include
#endif
/* Implement logging to /dev/log asynchronously. If syslogd is
making DNS lookups through dnsmasq, and dnsmasq blocks awaiting
syslogd, then the two daemons can deadlock. We get around this
by not blocking when talking to syslog, instead we queue up to
MAX_LOGS messages. If more are queued, they will be dropped,
and the drop event itself logged. */
/* The "wire" protocol for logging is defined in RFC 3164 */
/* From RFC 3164 */
#define MAX_MESSAGE 1024
/* defaults in case we die() before we log_start() */
static int log_fac = LOG_DAEMON;
static int log_stderr = 0;
static int log_fd = -1;
static int log_to_file = 0;
static int entries_alloced = 0;
static int entries_lost = 0;
static int connection_good = 1;
static int max_logs = 0;
static int connection_type = SOCK_DGRAM;
struct log_entry {
int offset, length;
pid_t pid; /* to avoid duplicates over a fork */
struct log_entry* next;
char payload[MAX_MESSAGE];
};
static struct log_entry* entries = NULL;
static struct log_entry* free_entries = NULL;
int log_start(struct passwd* ent_pw, int errfd) {
int ret = 0;
log_stderr = !!(daemon->options & OPT_DEBUG);
if (daemon->log_fac != -1) log_fac = daemon->log_fac;
#ifdef LOG_LOCAL0
else if (daemon->options & OPT_DEBUG)
log_fac = LOG_LOCAL0;
#endif
if (daemon->log_file) {
log_to_file = 1;
daemon->max_logs = 0;
}
max_logs = daemon->max_logs;
if (!log_reopen(daemon->log_file)) {
send_event(errfd, EVENT_LOG_ERR, errno);
_exit(0);
}
/* if queuing is inhibited, make sure we allocate
the one required buffer now. */
if (max_logs == 0) {
free_entries = safe_malloc(sizeof(struct log_entry));
free_entries->next = NULL;
entries_alloced = 1;
}
/* 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) != 0)
ret = errno;
return ret;
}
int log_reopen(char* log_file) {
if (log_fd != -1) close(log_fd);
/* 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);
return log_fd != -1;
} else
#if defined(__ANDROID__)
#define _PATH_LOG "" /* dummy */
log_fd = -1;
#else
{
int flags;
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)
fcntl(log_fd, F_SETFL, flags | O_NONBLOCK);
}
#endif
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) {
ssize_t rc;
while (entries) {
/* Avoid duplicates over a fork() */
if (entries->pid != getpid()) {
free_entry();
continue;
}
connection_good = 1;
if ((rc = write(log_fd, entries->payload + entries->offset, entries->length)) != -1) {
entries->length -= rc;
entries->offset += rc;
if (entries->length == 0) {
free_entry();
if (entries_lost != 0) {
int e = entries_lost;
entries_lost = 0; /* avoid wild recursion */
my_syslog(LOG_WARNING, _("overflow: %d log entries lost"), e);
}
}
continue;
}
if (errno == EINTR) continue;
if (errno == EAGAIN) return; /* syslogd busy, go again when select() or poll() says so */
if (errno == ENOBUFS) {
connection_good = 0;
return;
}
/* errors handling after this assumes sockets */
if (!log_to_file) {
/* Once a stream socket hits EPIPE, we have to close and re-open
(we ignore SIGPIPE) */
if (errno == EPIPE) {
if (log_reopen(NULL)) continue;
} else if (errno == ECONNREFUSED || errno == ENOTCONN || errno == EDESTADDRREQ ||
errno == ECONNRESET) {
/* socket went (syslogd down?), try and reconnect. If we fail,
stop trying until the next call to my_syslog()
ECONNREFUSED -> connection went down
ENOTCONN -> nobody listening
(ECONNRESET, EDESTADDRREQ are *BSD equivalents) */
struct sockaddr_un logaddr;
logaddr.sun_family = AF_UNIX;
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 */
connection_good = 0;
return;
}
/* try the other sort of socket... */
if (errno == EPROTOTYPE) {
connection_type = connection_type == SOCK_DGRAM ? SOCK_STREAM : SOCK_DGRAM;
if (log_reopen(NULL)) continue;
}
}
}
/* give up - fall back to syslog() - this handles out-of-space
when logging to a file, for instance. */
log_fd = -1;
my_syslog(LOG_CRIT, _("log failed: %s"), strerror(errno));
return;
}
}
/* priority is one of LOG_DEBUG, LOG_INFO, LOG_NOTICE, etc. See sys/syslog.h.
OR'd to priority can be MS_TFTP, MS_DHCP, ... to be able to do log separation between
DNS, DHCP and TFTP services.
*/
void my_syslog(int priority, const char* format, ...) {
va_list ap;
struct log_entry* entry;
time_t time_now;
char* p;
size_t len;
pid_t pid = getpid();
char* func = "";
#ifdef __ANDROID__
int alog_lvl;
#endif
if ((LOG_FACMASK & priority) == MS_TFTP)
func = "-tftp";
else if ((LOG_FACMASK & priority) == MS_DHCP)
func = "-dhcp";
priority = LOG_PRI(priority);
if (log_stderr) {
fprintf(stderr, "dnsmasq%s: ", func);
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
fputc('\n', stderr);
}
#ifdef __ANDROID__
if (priority <= LOG_ERR)
alog_lvl = ANDROID_LOG_ERROR;
else if (priority == LOG_WARNING)
alog_lvl = ANDROID_LOG_WARN;
else if (priority <= LOG_INFO)
alog_lvl = ANDROID_LOG_INFO;
else
alog_lvl = ANDROID_LOG_DEBUG;
va_start(ap, format);
__android_log_vprint(alog_lvl, "dnsmasq", format, ap);
va_end(ap);
#else
if (log_fd == -1) {
/* fall-back to syslog if we die during startup or fail during running. */
static int isopen = 0;
if (!isopen) {
openlog("dnsmasq", LOG_PID, log_fac);
isopen = 1;
}
va_start(ap, format);
vsyslog(priority, format, ap);
va_end(ap);
return;
}
if ((entry = free_entries))
free_entries = entry->next;
else if (entries_alloced < max_logs && (entry = malloc(sizeof(struct log_entry))))
entries_alloced++;
if (!entry)
entries_lost++;
else {
/* add to end of list, consumed from the start */
entry->next = NULL;
if (!entries)
entries = entry;
else {
struct log_entry* tmp;
for (tmp = entries; tmp->next; tmp = tmp->next)
;
tmp->next = entry;
}
time(&time_now);
p = entry->payload;
if (!log_to_file) p += sprintf(p, "<%d>", priority | log_fac);
p += sprintf(p, "%.15s dnsmasq%s[%d]: ", ctime(&time_now) + 4, func, (int) pid);
len = p - entry->payload;
va_start(ap, format);
len += vsnprintf(p, MAX_MESSAGE - len, format, ap) + 1; /* include zero-terminator */
va_end(ap);
entry->length = len > MAX_MESSAGE ? MAX_MESSAGE : len;
entry->offset = 0;
entry->pid = pid;
/* replace terminator with \n */
if (log_to_file) entry->payload[entry->length - 1] = '\n';
}
/* almost always, logging won't block, so try and write this now,
to save collecting too many log messages during a select loop. */
log_write();
/* Since we're doing things asynchronously, a cache-dump, for instance,
can now generate log lines very fast. With a small buffer (desirable),
that means it can overflow the log-buffer very quickly,
so that the cache dump becomes mainly a count of how many lines
overflowed. To avoid this, we delay here, the delay is controlled
by queue-occupancy, and grows exponentially. The delay is limited to (2^8)ms.
The scaling stuff ensures that when the queue is bigger than 8, the delay
only occurs for the last 8 entries. Once the queue is full, we stop delaying
to preserve performance.
*/
if (entries && max_logs != 0) {
int d;
for (d = 0, entry = entries; entry; entry = entry->next, d++)
;
if (d == max_logs)
d = 0;
else if (max_logs > 8)
d -= max_logs - 8;
if (d > 0) {
struct timespec waiter;
waiter.tv_sec = 0;
waiter.tv_nsec = 1000000 << (d - 1); /* 1 ms */
nanosleep(&waiter, NULL);
/* Have another go now */
log_write();
}
}
#endif
}
void set_log_writer(fd_set* set, int* maxfdp) {
if (entries && log_fd != -1 && connection_good) {
FD_SET(log_fd, set);
bump_maxfd(log_fd, maxfdp);
}
}
void check_log_writer(fd_set* set) {
if (log_fd != -1 && (!set || FD_ISSET(log_fd, set))) log_write();
}
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);
if (!arg1) arg1 = errmess;
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);
log_stderr = 0;
my_syslog(LOG_CRIT, _("FAILED to start up"));
flush_log();
exit(exit_code);
}
./src/netlink.c 0000644 0000000 0000000 00000021660 14256750714 012330 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "dnsmasq.h"
#ifdef HAVE_LINUX_NETWORK
#include
#include
#include
/* linux 2.6.19 buggers up the headers, patch it up here. */
#ifndef IFA_RTA
#define IFA_RTA(r) ((struct rtattr*) (((char*) (r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
#include
#endif
static struct iovec iov;
static u32 netlink_pid;
static void nl_err(struct nlmsghdr* h);
static void nl_routechange(struct nlmsghdr* h);
void netlink_init(void) {
struct sockaddr_nl addr;
socklen_t slen = sizeof(addr);
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_pid = 0; /* autobind */
#ifdef HAVE_IPV6
addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
#else
addr.nl_groups = RTMGRP_IPV4_ROUTE;
#endif
/* May not be able to have permission to set multicast groups don't die in that case */
if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1) {
if (bind(daemon->netlinkfd, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
addr.nl_groups = 0;
if (errno != EPERM ||
bind(daemon->netlinkfd, (struct sockaddr*) &addr, sizeof(addr)) == -1)
daemon->netlinkfd = -1;
}
}
if (daemon->netlinkfd == -1 ||
getsockname(daemon->netlinkfd, (struct sockaddr*) &addr, &slen) == 1)
die(_("cannot create netlink socket: %s"), NULL, EC_MISC);
/* save pid assigned by bind() and retrieved by getsockname() */
netlink_pid = addr.nl_pid;
iov.iov_len = 100;
iov.iov_base = safe_malloc(iov.iov_len);
}
static ssize_t netlink_recv(void) {
struct msghdr msg;
struct sockaddr_nl nladdr;
ssize_t rc;
while (1) {
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
while ((rc = recvmsg(daemon->netlinkfd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR)
;
/* make buffer big enough */
if (rc != -1 && (msg.msg_flags & MSG_TRUNC)) {
/* Very new Linux kernels return the actual size needed, older ones always return
* truncated size */
if ((size_t) rc == iov.iov_len) {
if (expand_buf(&iov, rc + 100)) continue;
} else
expand_buf(&iov, rc);
}
/* read it for real */
msg.msg_flags = 0;
while ((rc = recvmsg(daemon->netlinkfd, &msg, 0)) == -1 && errno == EINTR)
;
/* Make sure this is from the kernel */
if (rc == -1 || nladdr.nl_pid == 0) break;
}
/* discard stuff which is truncated at this point (expand_buf() may fail) */
if (msg.msg_flags & MSG_TRUNC) {
rc = -1;
errno = ENOMEM;
}
return rc;
}
int iface_enumerate(void* parm, int (*ipv4_callback)(), int (*ipv6_callback)()) {
struct sockaddr_nl addr;
struct nlmsghdr* h;
ssize_t len;
static unsigned int seq = 0;
int family = AF_INET;
struct {
struct nlmsghdr nlh;
struct rtgenmsg g;
} req;
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_groups = 0;
addr.nl_pid = 0; /* address to kernel */
again:
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = RTM_GETADDR;
req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK;
req.nlh.nlmsg_pid = 0;
req.nlh.nlmsg_seq = ++seq;
req.g.rtgen_family = family;
/* Don't block in recvfrom if send fails */
while ((len = sendto(daemon->netlinkfd, (void*) &req, sizeof(req), 0, (struct sockaddr*) &addr,
sizeof(addr))) == -1 &&
retry_send())
;
if (len == -1) return 0;
while (1) {
if ((len = netlink_recv()) == -1) {
if (errno == ENOBUFS) {
sleep(1);
goto again;
}
return 0;
}
for (h = (struct nlmsghdr*) iov.iov_base; NLMSG_OK(h, (size_t) len); h = NLMSG_NEXT(h, len))
if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid)
nl_routechange(h); /* May be multicast arriving async */
else if (h->nlmsg_type == NLMSG_ERROR)
nl_err(h);
else if (h->nlmsg_type == NLMSG_DONE) {
#ifdef HAVE_IPV6
if (family == AF_INET && ipv6_callback) {
family = AF_INET6;
goto again;
}
#endif
return 1;
} else if (h->nlmsg_type == RTM_NEWADDR) {
struct ifaddrmsg* ifa = NLMSG_DATA(h);
struct rtattr* rta = IFA_RTA(ifa);
unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
if (ifa->ifa_family == AF_INET) {
struct in_addr netmask, addr, broadcast;
netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
addr.s_addr = 0;
broadcast.s_addr = 0;
while (RTA_OK(rta, len1)) {
if (rta->rta_type == IFA_LOCAL)
addr = *((struct in_addr*) (rta + 1));
else if (rta->rta_type == IFA_BROADCAST)
broadcast = *((struct in_addr*) (rta + 1));
rta = RTA_NEXT(rta, len1);
}
if (addr.s_addr && ipv4_callback)
if (!((*ipv4_callback)(addr, ifa->ifa_index, netmask, broadcast, parm)))
return 0;
}
#ifdef HAVE_IPV6
else if (ifa->ifa_family == AF_INET6) {
struct in6_addr* addrp = NULL;
while (RTA_OK(rta, len1)) {
if (rta->rta_type == IFA_ADDRESS) addrp = ((struct in6_addr*) (rta + 1));
rta = RTA_NEXT(rta, len1);
}
if (addrp && ipv6_callback)
if (!((*ipv6_callback)(addrp, ifa->ifa_index, ifa->ifa_index, parm)))
return 0;
}
#endif
}
}
}
void netlink_multicast(void) {
ssize_t len;
struct nlmsghdr* h;
int flags;
/* don't risk blocking reading netlink messages here. */
if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||
fcntl(daemon->netlinkfd, F_SETFL, flags | O_NONBLOCK) == -1)
return;
if ((len = netlink_recv()) != -1) {
for (h = (struct nlmsghdr*) iov.iov_base; NLMSG_OK(h, (size_t) len); h = NLMSG_NEXT(h, len))
if (h->nlmsg_type == NLMSG_ERROR)
nl_err(h);
else
nl_routechange(h);
}
/* restore non-blocking status */
fcntl(daemon->netlinkfd, F_SETFL, flags);
}
static void nl_err(struct nlmsghdr* h) {
struct nlmsgerr* err = NLMSG_DATA(h);
if (err->error != 0)
my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
}
/* We arrange to receive netlink multicast messages whenever the network route is added.
If this happens and we still have a DNS packet in the buffer, we re-send it.
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
failing. Note that we only accept these messages from the kernel (pid == 0) */
static void nl_routechange(struct nlmsghdr* h) {
if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE) {
struct rtmsg* rtm = NLMSG_DATA(h);
int fd;
if (rtm->rtm_type != RTN_UNICAST || rtm->rtm_scope != RT_SCOPE_LINK) return;
/* Force re-reading resolv file right now, for luck. */
daemon->last_resolv = 0;
if (daemon->srv_save) {
if (daemon->srv_save->sfd)
fd = daemon->srv_save->sfd->fd;
else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
fd = daemon->rfd_save->fd;
else
return;
while (sendto(fd, daemon->packet, daemon->packet_len, 0, &daemon->srv_save->addr.sa,
sa_len(&daemon->srv_save->addr)) == -1 &&
retry_send())
;
}
}
}
#endif
./src/dnsmasq.c 0000644 0000000 0000000 00000104435 14256750714 012334 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "dnsmasq.h"
#include
#if defined(HAVE_BSD_NETWORK)
#error Should not HAVE_BSD_NETWORK
#endif
#if defined(HAVE_SOCKADDR_SA_LEN)
#error Should not HAVE_SOCKADDR_SA_LEN
#endif
#if defined(HAVE_SOLARIS_NETWORK)
#error Should not HAVE_SOLARIS_NETWORK
#endif
#if !defined(HAVE_LINUX_NETWORK)
#error Should HAVE_LINUX_NETWORK
#endif
struct daemon* daemon;
static char* compile_opts =
#ifndef HAVE_IPV6
"no-"
#endif
"IPv6 "
#ifndef HAVE_GETOPT_LONG
"no-"
#endif
"GNU-getopt "
#ifdef HAVE_BROKEN_RTC
"no-RTC "
#endif
#ifdef NO_FORK
"no-MMU "
#endif
#ifndef LOCALEDIR
"no-"
#endif
"I18N "
#ifndef HAVE_DHCP
"no-"
#endif
"DHCP "
#if defined(HAVE_DHCP) && !defined(HAVE_SCRIPT)
"no-scripts"
#endif
"";
static volatile pid_t pid = 0;
static volatile int pipewrite;
static int set_dns_listeners(time_t now, fd_set* set, int* maxfdp);
static void check_dns_listeners(fd_set* set, time_t now);
static void sig_handler(int sig);
static void async_event(int pipe, time_t now);
static void fatal_event(struct event_desc* ev);
static void poll_resolv(void);
#ifdef __ANDROID__
static int set_android_listeners(fd_set* set, int* maxfdp);
static int check_android_listeners(fd_set* set);
#endif
void setupSignalHandling() {
struct sigaction sigact;
sigact.sa_handler = sig_handler;
sigact.sa_flags = 0;
sigemptyset(&sigact.sa_mask);
sigaction(SIGUSR1, &sigact, NULL);
sigaction(SIGUSR2, &sigact, NULL);
sigaction(SIGHUP, &sigact, NULL);
sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGALRM, &sigact, NULL);
sigaction(SIGCHLD, &sigact, NULL);
/* ignore SIGPIPE */
sigact.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sigact, NULL);
}
void closeUnwantedFileDescriptors() {
const long kMaxFd = sysconf(_SC_OPEN_MAX);
long i;
struct stat stat_buf;
/* Close any file descriptors we inherited apart from std{in|out|err}. */
for (i = 0; i < kMaxFd; i++) {
// TODO: Evaluate just skipping STDIN, since netd does not
// (intentionally) pass in any other file descriptors.
if (i == STDOUT_FILENO || i == STDERR_FILENO || i == STDIN_FILENO) {
continue;
}
if (fstat(i, &stat_buf) != 0) {
if (errno == EBADF) continue;
if (errno == EACCES) continue; // Lessen the log spam.
my_syslog(LOG_ERR, "fstat(%d) error: %d/%s", i, errno, strerror(errno));
} else {
my_syslog(LOG_ERR, "Closing inherited file descriptor %d (%u:%u)", i, stat_buf.st_dev,
stat_buf.st_ino);
}
close(i);
}
}
int main(int argc, char** argv) {
int bind_fallback = 0;
time_t now;
struct iname* if_tmp;
int piperead, pipefd[2], err_pipe[2];
struct passwd* ent_pw = NULL;
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
uid_t script_uid = 0;
gid_t script_gid = 0;
#endif
struct group* gp = NULL;
long i, max_fd = sysconf(_SC_OPEN_MAX);
char* baduser = NULL;
int log_err;
#if defined(HAVE_LINUX_NETWORK)
cap_user_header_t hdr = NULL;
cap_user_data_t data = NULL;
#endif
#ifdef LOCALEDIR
setlocale(LC_ALL, "");
bindtextdomain("dnsmasq", LOCALEDIR);
textdomain("dnsmasq");
#endif
setupSignalHandling();
umask(022); /* known umask, create leases and pid files as 0644 */
read_opts(argc, argv, compile_opts);
if (daemon->edns_pktsz < PACKETSZ) daemon->edns_pktsz = PACKETSZ;
daemon->packet_buff_sz =
daemon->edns_pktsz > DNSMASQ_PACKETSZ ? daemon->edns_pktsz : DNSMASQ_PACKETSZ;
daemon->packet = safe_malloc(daemon->packet_buff_sz);
#ifdef HAVE_DHCP
if (!daemon->lease_file) {
if (daemon->dhcp) daemon->lease_file = LEASEFILE;
}
#endif
closeUnwantedFileDescriptors();
#ifdef HAVE_LINUX_NETWORK
netlink_init();
#elif !(defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR))
if (!(daemon->options & OPT_NOWILD)) {
bind_fallback = 1;
daemon->options |= OPT_NOWILD;
}
#endif
rand_init();
now = dnsmasq_time();
#ifdef HAVE_DHCP
if (daemon->dhcp) {
/* Note that order matters here, we must call lease_init before
creating any file descriptors which shouldn't be leaked
to the lease-script init process. */
lease_init(now);
dhcp_init();
}
#endif
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->port != 0 || (daemon->options & OPT_TFTP)) &&
!(daemon->listeners = create_wildcard_listeners()))
die(_("failed to create listening socket: %s"), NULL, EC_BADNET);
if (daemon->port != 0) cache_init();
if (daemon->port != 0) pre_allocate_sfds();
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
/* Note getpwnam returns static storage */
if (daemon->dhcp && daemon->lease_change_command && daemon->scriptuser) {
if ((ent_pw = getpwnam(daemon->scriptuser))) {
script_uid = ent_pw->pw_uid;
script_gid = ent_pw->pw_gid;
} else
baduser = daemon->scriptuser;
}
#endif
if (daemon->username && !(ent_pw = getpwnam(daemon->username)))
baduser = daemon->username;
else if (daemon->groupname && !(gp = getgrnam(daemon->groupname)))
baduser = daemon->groupname;
if (baduser) die(_("unknown user or group: %s"), baduser, EC_BADCONF);
/* implement group defaults, "dip" if available, or group associated with uid */
if (!daemon->group_set && !gp) {
if (!(gp = getgrnam(CHGRP)) && ent_pw) gp = getgrgid(ent_pw->pw_gid);
/* for error message */
if (gp) daemon->groupname = gp->gr_name;
}
#if defined(HAVE_LINUX_NETWORK)
/* determine capability API version here, while we can still
call safe_malloc */
if (ent_pw && ent_pw->pw_uid != 0) {
int capsize = 1; /* for header version 1 */
hdr = safe_malloc(sizeof(*hdr));
/* find version supported by kernel */
memset(hdr, 0, sizeof(*hdr));
capget(hdr, NULL);
if (hdr->version != LINUX_CAPABILITY_VERSION_1) {
/* if unknown version, use largest supported version (3) */
if (hdr->version != LINUX_CAPABILITY_VERSION_2)
hdr->version = LINUX_CAPABILITY_VERSION_3;
capsize = 2;
}
data = safe_malloc(sizeof(*data) * capsize);
memset(data, 0, sizeof(*data) * capsize);
}
#endif
/* Use a pipe to carry signals and other events back to the event loop
in a race-free manner and another to carry errors to daemon-invoking process */
safe_pipe(pipefd, 1);
piperead = pipefd[0];
pipewrite = pipefd[1];
/* prime the pipe to load stuff first time. */
send_event(pipewrite, EVENT_RELOAD, 0);
err_pipe[1] = -1;
if (!(daemon->options & OPT_DEBUG)) {
#ifndef __ANDROID__
int nullfd;
#endif
/* The following code "daemonizes" the process.
See Stevens section 12.4 */
if (chdir("/") != 0) die(_("cannot chdir to filesystem root: %s"), NULL, EC_MISC);
#ifndef NO_FORK
if (!(daemon->options & OPT_NO_FORK)) {
pid_t pid;
/* pipe to carry errors back to original process.
When startup is complete we close this and the process terminates. */
safe_pipe(err_pipe, 0);
if ((pid = fork()) == -1) /* fd == -1 since we've not forked, never returns. */
send_event(-1, EVENT_FORK_ERR, errno);
if (pid != 0) {
struct event_desc ev;
/* close our copy of write-end */
close(err_pipe[1]);
/* check for errors after the fork */
if (read_write(err_pipe[0], (unsigned char*) &ev, sizeof(ev), 1)) fatal_event(&ev);
_exit(EC_GOOD);
}
close(err_pipe[0]);
/* NO calls to die() from here on. */
setsid();
if ((pid = fork()) == -1) send_event(err_pipe[1], EVENT_FORK_ERR, errno);
if (pid != 0) _exit(0);
}
#endif
/* write pidfile _after_ forking ! */
if (daemon->runfile) {
FILE* pidfile;
/* only complain if started as root */
if ((pidfile = fopen(daemon->runfile, "w"))) {
fprintf(pidfile, "%d\n", (int) getpid());
fclose(pidfile);
} else if (getuid() == 0) {
send_event(err_pipe[1], EVENT_PIDFILE, errno);
_exit(0);
}
}
#ifndef __ANDROID__
/* open stdout etc to /dev/null */
nullfd = open("/dev/null", O_RDWR);
dup2(nullfd, STDOUT_FILENO);
dup2(nullfd, STDERR_FILENO);
dup2(nullfd, STDIN_FILENO);
close(nullfd);
#endif
}
log_err = log_start(ent_pw, err_pipe[1]);
/* if we are to run scripts, we need to fork a helper before dropping root. */
daemon->helperfd = -1;
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
if (daemon->dhcp && daemon->lease_change_command)
daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
#endif
if (!(daemon->options & OPT_DEBUG) && getuid() == 0) {
int bad_capabilities = 0;
gid_t dummy;
/* remove all supplimentary groups */
if (gp && (setgroups(0, &dummy) == -1 || setgid(gp->gr_gid) == -1)) {
send_event(err_pipe[1], EVENT_GROUP_ERR, errno);
_exit(0);
}
if (ent_pw && ent_pw->pw_uid != 0) {
#if defined(HAVE_LINUX_NETWORK)
/* On linux, we keep CAP_NETADMIN (for ARP-injection) and
CAP_NET_RAW (for icmp) if we're doing dhcp */
data->effective = data->permitted = data->inheritable =
#ifdef __ANDROID__
(1 << CAP_NET_BIND_SERVICE) |
#endif
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID);
/* Tell kernel to not clear capabilities when dropping root */
if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
bad_capabilities = errno;
#endif
if (bad_capabilities != 0) {
send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities);
_exit(0);
}
/* finally drop root */
if (setuid(ent_pw->pw_uid) == -1) {
send_event(err_pipe[1], EVENT_USER_ERR, errno);
_exit(0);
}
#ifdef HAVE_LINUX_NETWORK
data->effective = data->permitted =
#ifdef __ANDROID__
(1 << CAP_NET_BIND_SERVICE) |
#endif
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
data->inheritable = 0;
/* lose the setuid and setgid capbilities */
if (capset(hdr, data) == -1) {
send_event(err_pipe[1], EVENT_CAP_ERR, errno);
_exit(0);
}
#endif
}
}
#ifdef HAVE_LINUX_NETWORK
if (daemon->options & OPT_DEBUG) prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
#endif
if (daemon->port == 0)
my_syslog(LOG_INFO, _("started, version %s DNS disabled"), VERSION);
else if (daemon->cachesize != 0)
my_syslog(LOG_INFO, _("started, version %s cachesize %d"), VERSION, daemon->cachesize);
else
my_syslog(LOG_INFO, _("started, version %s cache disabled"), VERSION);
my_syslog(LOG_INFO, _("compile time options: %s"), compile_opts);
if (log_err != 0)
my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"), daemon->log_file,
strerror(log_err));
if (bind_fallback)
my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
if (!(daemon->options & OPT_NOWILD))
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
if (if_tmp->name && !if_tmp->used)
my_syslog(LOG_WARNING, _("warning: interface %s does not currently exist"),
if_tmp->name);
if (daemon->port != 0 && (daemon->options & OPT_NO_RESOLV)) {
if (daemon->resolv_files && !daemon->resolv_files->is_default)
my_syslog(LOG_WARNING,
_("warning: ignoring resolv-file flag because no-resolv is set"));
daemon->resolv_files = NULL;
if (!daemon->servers) my_syslog(LOG_WARNING, _("warning: no upstream servers configured"));
}
if (daemon->max_logs != 0)
my_syslog(LOG_INFO, _("asynchronous logging enabled, queue limit is %d messages"),
daemon->max_logs);
#ifdef HAVE_DHCP
if (daemon->dhcp) {
struct dhcp_context* dhcp_tmp;
for (dhcp_tmp = daemon->dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next) {
prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time);
strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start));
my_syslog(MS_DHCP | LOG_INFO,
(dhcp_tmp->flags & CONTEXT_STATIC)
? _("DHCP, static leases only on %.0s%s, lease time %s")
: (dhcp_tmp->flags & CONTEXT_PROXY)
? _("DHCP, proxy on subnet %.0s%s%.0s")
: _("DHCP, IP range %s -- %s, lease time %s"),
daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), daemon->dhcp_buff2);
}
}
#endif
/* finished start-up - release original process */
if (err_pipe[1] != -1) close(err_pipe[1]);
if (daemon->port != 0) check_servers();
pid = getpid();
while (1) {
int maxfd = -1;
struct timeval t, *tp = NULL;
fd_set rset, wset, eset;
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&eset);
/* 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(now, &rset, &maxfd)) != 0) {
t.tv_usec = 0;
tp = &t;
}
#ifdef __ANDROID__
set_android_listeners(&rset, &maxfd);
#endif
#ifdef HAVE_DHCP
if (daemon->dhcp) {
FD_SET(daemon->dhcpfd, &rset);
bump_maxfd(daemon->dhcpfd, &maxfd);
}
#endif
#ifdef HAVE_LINUX_NETWORK
FD_SET(daemon->netlinkfd, &rset);
bump_maxfd(daemon->netlinkfd, &maxfd);
#endif
FD_SET(piperead, &rset);
bump_maxfd(piperead, &maxfd);
#ifdef HAVE_DHCP
#ifdef HAVE_SCRIPT
while (helper_buf_empty() && do_script_run(now))
;
if (!helper_buf_empty()) {
FD_SET(daemon->helperfd, &wset);
bump_maxfd(daemon->helperfd, &maxfd);
}
#else
/* need this for other side-effects */
while (do_script_run(now))
;
#endif
#endif
/* must do this just before select(), when we know no
more calls to my_syslog() can occur */
set_log_writer(&wset, &maxfd);
if (select(maxfd + 1, &rset, &wset, &eset, tp) < 0) {
/* otherwise undefined after error */
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&eset);
}
now = dnsmasq_time();
check_log_writer(&wset);
/* Check for changes to resolv files once per second max. */
/* Don't go silent for long periods if the clock goes backwards. */
if (daemon->last_resolv == 0 || difftime(now, daemon->last_resolv) > 1.0 ||
difftime(now, daemon->last_resolv) < -1.0) {
daemon->last_resolv = now;
if (daemon->port != 0 && !(daemon->options & OPT_NO_POLL)) poll_resolv();
}
if (FD_ISSET(piperead, &rset)) async_event(piperead, now);
#ifdef HAVE_LINUX_NETWORK
if (FD_ISSET(daemon->netlinkfd, &rset)) netlink_multicast();
#endif
#ifdef __ANDROID__
check_android_listeners(&rset);
#endif
check_dns_listeners(&rset, now);
#ifdef HAVE_DHCP
if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset)) dhcp_packet(now);
#ifdef HAVE_SCRIPT
if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset)) helper_write();
#endif
#endif
}
}
static void sig_handler(int sig) {
if (pid == 0) {
/* ignore anything other than TERM during startup
and in helper proc. (helper ignore TERM too) */
if (sig == SIGTERM) exit(EC_MISC);
} else if (pid != getpid()) {
/* alarm is used to kill TCP children after a fixed time. */
if (sig == SIGALRM) _exit(0);
} else {
/* master process */
const int errsave = errno;
int event;
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;
/* error pipe, debug mode. */
if (fd == -1)
fatal_event(&ev);
else
/* 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)
;
}
static void fatal_event(struct event_desc* ev) {
errno = ev->data;
switch (ev->event) {
case EVENT_DIE:
exit(0);
case EVENT_FORK_ERR:
die(_("cannot fork into background: %s"), NULL, EC_MISC);
case EVENT_PIPE_ERR:
die(_("failed to create helper: %s"), NULL, EC_MISC);
case EVENT_CAP_ERR:
die(_("setting capabilities failed: %s"), NULL, EC_MISC);
case EVENT_USER_ERR:
case EVENT_HUSER_ERR:
die(_("failed to change user-id to %s: %s"),
ev->event == EVENT_USER_ERR ? daemon->username : daemon->scriptuser, EC_MISC);
case EVENT_GROUP_ERR:
die(_("failed to change group-id to %s: %s"), daemon->groupname, EC_MISC);
case EVENT_PIDFILE:
die(_("failed to open pidfile %s: %s"), daemon->runfile, EC_FILE);
case EVENT_LOG_ERR:
die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log", EC_FILE);
}
}
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->port != 0 && daemon->resolv_files && (daemon->options & OPT_NO_POLL)) {
reload_servers(daemon->resolv_files->name);
check_servers();
}
#ifdef HAVE_DHCP
rerun_scripts();
#endif
break;
case EVENT_DUMP:
if (daemon->port != 0) dump_cache(now);
break;
case EVENT_ALARM:
#ifdef HAVE_DHCP
if (daemon->dhcp) {
lease_prune(NULL, now);
lease_update_file(now);
}
#endif
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;
/* necessary for fatal errors in helper */
case EVENT_HUSER_ERR:
case EVENT_DIE:
fatal_event(&ev);
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);
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
/* 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);
if (daemon->runfile) unlink(daemon->runfile);
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();
} 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) {
if (daemon->port != 0) cache_reload();
#ifdef HAVE_DHCP
if (daemon->dhcp) {
reread_dhcp();
dhcp_update_configs(daemon->dhcp_conf);
check_dhcp_hosts(0);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns();
}
#endif
}
#ifdef __ANDROID__
static int set_android_listeners(fd_set* set, int* maxfdp) {
FD_SET(STDIN_FILENO, set);
bump_maxfd(STDIN_FILENO, maxfdp);
return 0;
}
static int check_android_listeners(fd_set* set) {
int retcode = 0;
if (FD_ISSET(STDIN_FILENO, set)) {
char buffer[1024];
int rc;
int consumed = 0;
if ((rc = read(STDIN_FILENO, buffer, sizeof(buffer) - 1)) < 0) {
my_syslog(LOG_ERR, _("Error reading from stdin (%s)"), strerror(errno));
return -1;
}
buffer[rc] = '\0';
while (consumed < rc) {
char* cmd;
char* current_cmd = &buffer[consumed];
char* params = current_cmd;
int len = strlen(current_cmd);
cmd = strsep(¶ms, "|");
if (!strcmp(cmd, "update_dns")) {
if (params != NULL) {
set_servers(params);
check_servers();
} else {
my_syslog(LOG_ERR, _("Misformatted msg '%s'"), current_cmd);
retcode = -1;
}
} else if (!strcmp(cmd, "update_ifaces")) {
if (params != NULL) {
set_interfaces(params);
} else {
my_syslog(LOG_ERR, _("Misformatted msg '%s'"), current_cmd);
retcode = -1;
}
} else {
my_syslog(LOG_ERR, _("Unknown cmd '%s'"), cmd);
retcode = -1;
}
consumed += len + 1;
}
}
return retcode;
}
#endif
static int set_dns_listeners(time_t now, fd_set* set, int* maxfdp) {
struct serverfd* serverfdp;
struct listener* listener;
int wait = 0, i;
/* will we be able to get memory? */
if (daemon->port != 0) get_new_frec(now, &wait);
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) {
FD_SET(serverfdp->fd, set);
bump_maxfd(serverfdp->fd, maxfdp);
}
if (daemon->port != 0 && !daemon->osport)
for (i = 0; i < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount != 0) {
FD_SET(daemon->randomsocks[i].fd, set);
bump_maxfd(daemon->randomsocks[i].fd, maxfdp);
}
for (listener = daemon->listeners; listener; listener = listener->next) {
/* only listen for queries if we have resources */
if (listener->fd != -1 && 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 */
if (listener->tcpfd != -1)
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 wait;
}
static void check_dns_listeners(fd_set* set, time_t now) {
struct serverfd* serverfdp;
struct listener* listener;
int i;
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
if (FD_ISSET(serverfdp->fd, set))
reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);
if (daemon->port != 0 && !daemon->osport)
for (i = 0; i < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount != 0 && FD_ISSET(daemon->randomsocks[i].fd, set))
reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
for (listener = daemon->listeners; listener; listener = listener->next) {
if (listener->fd != -1 && FD_ISSET(listener->fd, set)) receive_query(listener, now);
if (listener->tcpfd != -1 && FD_ISSET(listener->tcpfd, set)) {
int confd;
struct irec* iface = NULL;
pid_t p;
while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR)
;
if (confd == -1) continue;
if (daemon->options & OPT_NOWILD)
iface = listener->iface;
else {
union mysockaddr tcp_addr;
socklen_t tcp_len = sizeof(union mysockaddr);
/* Check for allowed interfaces when binding the wildcard address:
we do this by looking for an interface with the same address as
the local address of the TCP connection, then looking to see if that's
an allowed interface. As a side effect, we get the netmask of the
interface too, for localisation. */
/* interface may be new since startup */
if (enumerate_interfaces() &&
getsockname(confd, (struct sockaddr*) &tcp_addr, &tcp_len) != -1)
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, &tcp_addr)) break;
}
if (!iface) {
shutdown(confd, SHUT_RDWR);
close(confd);
}
#ifndef NO_FORK
else if (!(daemon->options & OPT_DEBUG) && (p = fork()) != 0) {
if (p != -1) {
int i;
for (i = 0; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] == 0) {
daemon->tcp_pids[i] = p;
break;
}
}
close(confd);
}
#endif
else {
unsigned char* buff;
struct server* s;
int flags;
struct in_addr dst_addr_4;
dst_addr_4.s_addr = 0;
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to
terminate the process. */
if (!(daemon->options & OPT_DEBUG)) alarm(CHILD_LIFETIME);
/* start with no upstream connections. */
for (s = daemon->servers; s; s = s->next) s->tcpfd = -1;
/* The connected socket inherits non-blocking
attribute from the listening socket.
Reset that here. */
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
if (listener->family == AF_INET) dst_addr_4 = iface->addr.in.sin_addr;
buff = tcp_request(confd, now, dst_addr_4, iface->netmask);
shutdown(confd, SHUT_RDWR);
close(confd);
if (buff) free(buff);
for (s = daemon->servers; s; s = s->next)
if (s->tcpfd != -1) {
shutdown(s->tcpfd, SHUT_RDWR);
close(s->tcpfd);
}
#ifndef NO_FORK
if (!(daemon->options & OPT_DEBUG)) {
flush_log();
_exit(0);
}
#endif
}
}
}
}
#ifdef HAVE_DHCP
int make_icmp_sock(void) {
int fd;
int zeroopt = 0;
if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1) {
if (!fix_fd(fd) ||
setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1) {
close(fd);
fd = -1;
}
}
return fd;
}
int icmp_ping(struct in_addr addr) {
/* Try and get an ICMP echo from a machine. */
/* Note that whilst in the three second wait, we check for
(and service) events on the DNS sockets, (so doing that
better not use any resources our caller has in use...)
but we remain deaf to signals or further DHCP packets. */
int fd;
struct sockaddr_in saddr;
struct {
struct ip ip;
struct icmp icmp;
} packet;
unsigned short id = rand16();
unsigned int i, j;
int gotreply = 0;
time_t start, now;
#if defined(HAVE_LINUX_NETWORK)
if ((fd = make_icmp_sock()) == -1) return 0;
#else
int opt = 2000;
fd = daemon->dhcp_icmp_fd;
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
#endif
saddr.sin_family = AF_INET;
saddr.sin_port = 0;
saddr.sin_addr = addr;
memset(&packet.icmp, 0, sizeof(packet.icmp));
packet.icmp.icmp_type = ICMP_ECHO;
packet.icmp.icmp_id = id;
for (j = 0, i = 0; i < sizeof(struct icmp) / 2; i++) j += ((u16*) &packet.icmp)[i];
while (j >> 16) j = (j & 0xffff) + (j >> 16);
packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j;
while (sendto(fd, (char*) &packet.icmp, sizeof(struct icmp), 0, (struct sockaddr*) &saddr,
sizeof(saddr)) == -1 &&
retry_send())
;
for (now = start = dnsmasq_time(); difftime(now, start) < (float) PING_WAIT;) {
struct timeval tv;
fd_set rset, wset;
struct sockaddr_in faddr;
int maxfd = fd;
socklen_t len = sizeof(faddr);
tv.tv_usec = 250000;
tv.tv_sec = 0;
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_SET(fd, &rset);
set_dns_listeners(now, &rset, &maxfd);
set_log_writer(&wset, &maxfd);
if (select(maxfd + 1, &rset, &wset, NULL, &tv) < 0) {
FD_ZERO(&rset);
FD_ZERO(&wset);
}
now = dnsmasq_time();
check_log_writer(&wset);
check_dns_listeners(&rset, now);
if (FD_ISSET(fd, &rset) &&
recvfrom(fd, &packet, sizeof(packet), 0, (struct sockaddr*) &faddr, &len) ==
sizeof(packet) &&
saddr.sin_addr.s_addr == faddr.sin_addr.s_addr &&
packet.icmp.icmp_type == ICMP_ECHOREPLY && packet.icmp.icmp_seq == 0 &&
packet.icmp.icmp_id == id) {
gotreply = 1;
break;
}
}
#if defined(HAVE_LINUX_NETWORK)
close(fd);
#else
opt = 1;
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
#endif
return gotreply;
}
#endif
./src/dnsmasq.h 0000644 0000000 0000000 00000057266 14256750714 012352 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#define COPYRIGHT "Copyright (C) 2000-2009 Simon Kelley"
#ifndef NO_LARGEFILE
/* Ensure we can use files >2GB (log files may grow this big) */
#define _LARGEFILE_SOURCE 1
#define _FILE_OFFSET_BITS 64
#endif
/* Get linux C library versions. */
#ifdef __linux__
#ifndef __ANDROID__
#define _GNU_SOURCE
#endif
#include
#endif
/* get these before config.h for IPv6 stuff... */
#include
#include
#include
#ifdef __APPLE__
#include
#include
#else
#ifdef __ANDROID__
#include "nameser.h"
#else
#include
#endif
#endif
/* and this. */
#include
#include "config.h"
#define gettext_noop(S) (S)
#ifndef LOCALEDIR
#define _(S) (S)
#else
#include
#include
#define _(S) gettext(S)
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__sun__) || defined(__sun) || \
defined(__ANDROID__)
#include
#else
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#ifndef HAVE_LINUX_NETWORK
#include
#endif
#if defined(HAVE_LINUX_NETWORK)
#include
/* There doesn't seem to be a universally-available
userpace header for these. */
extern int capset(cap_user_header_t header, cap_user_data_t data);
extern int capget(cap_user_header_t header, cap_user_data_t data);
#define LINUX_CAPABILITY_VERSION_1 0x19980330
#define LINUX_CAPABILITY_VERSION_2 0x20071026
#define LINUX_CAPABILITY_VERSION_3 0x20080522
#include
#endif
/* daemon is function in the 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
#define EVENT_USER_ERR 11
#define EVENT_CAP_ERR 12
#define EVENT_PIDFILE 13
#define EVENT_HUSER_ERR 14
#define EVENT_GROUP_ERR 15
#define EVENT_DIE 16
#define EVENT_LOG_ERR 17
#define EVENT_FORK_ERR 18
/* 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
memory for the largest packet, and the largest record so the
min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000.
This might be increased is EDNS packet size if greater than the minimum.
*/
#define DNSMASQ_PACKETSZ PACKETSZ + MAXDNAME + RRFIXEDSZ
#define OPT_BOGUSPRIV (1u << 0)
#define OPT_FILTER (1u << 1)
#define OPT_LOG (1u << 2)
#define OPT_SELFMX (1u << 3)
#define OPT_NO_HOSTS (1u << 4)
#define OPT_NO_POLL (1u << 5)
#define OPT_DEBUG (1u << 6)
#define OPT_ORDER (1u << 7)
#define OPT_NO_RESOLV (1u << 8)
#define OPT_EXPAND (1u << 9)
#define OPT_LOCALMX (1u << 10)
#define OPT_NO_NEG (1u << 11)
#define OPT_NODOTS_LOCAL (1u << 12)
#define OPT_NOWILD (1u << 13)
#define OPT_RESOLV_DOMAIN (1u << 15)
#define OPT_NO_FORK (1u << 16)
#define OPT_AUTHORITATIVE (1u << 17)
#define OPT_LOCALISE (1u << 18)
#define OPT_DBUS (1u << 19)
#define OPT_DHCP_FQDN (1u << 20)
#define OPT_NO_PING (1u << 21)
#define OPT_LEASE_RO (1u << 22)
#define OPT_ALL_SERVERS (1u << 23)
#define OPT_RELOAD (1u << 24)
#define OPT_TFTP (1u << 25)
#define OPT_TFTP_SECURE (1u << 26)
#define OPT_TFTP_NOBLOCK (1u << 27)
#define OPT_LOG_OPTS (1u << 28)
#define OPT_TFTP_APREF (1u << 29)
#define OPT_NO_OVERRIDE (1u << 30)
#define OPT_NO_REBIND (1u << 31)
/* extra flags for my_syslog, we use a couple of facilities since they are known
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
#define MS_TFTP LOG_USER
#define MS_DHCP LOG_DAEMON
struct all_addr {
union {
struct in_addr addr4;
#ifdef HAVE_IPV6
struct in6_addr addr6;
#endif
} addr;
};
struct bogus_addr {
struct in_addr addr;
struct bogus_addr* next;
};
/* dns doctor param */
struct doctor {
struct in_addr in, end, out, mask;
struct doctor* next;
};
struct mx_srv_record {
char *name, *target;
int issrv, srvport, priority, weight;
unsigned int offset;
struct mx_srv_record* next;
};
struct naptr {
char *name, *replace, *regexp, *services, *flags;
unsigned int order, pref;
struct naptr* next;
};
struct txt_record {
char *name, *txt;
unsigned short class, len;
struct txt_record* next;
};
struct ptr_record {
char *name, *ptr;
struct ptr_record* next;
};
struct cname {
char *alias, *target;
struct cname* next;
};
struct interface_name {
char* name; /* domain name */
char* intr; /* interface name */
struct interface_name* next;
};
union bigname {
char name[MAXDNAME];
union bigname* next; /* freelist */
};
struct crec {
struct crec *next, *prev, *hash_next;
time_t ttd; /* time to die */
int uid;
union {
struct all_addr addr;
struct {
struct crec* cache;
int uid;
} cname;
} addr;
unsigned short flags;
union {
char sname[SMALLDNAME];
union bigname* bname;
char* namep;
} name;
};
#define F_IMMORTAL 1
#define F_CONFIG 2
#define F_REVERSE 4
#define F_FORWARD 8
#define F_DHCP 16
#define F_NEG 32
#define F_HOSTS 64
#define F_IPV4 128
#define F_IPV6 256
#define F_BIGNAME 512
#define F_UPSTREAM 1024
#define F_SERVER 2048
#define F_NXDOMAIN 4096
#define F_QUERY 8192
#define F_CNAME 16384
#define F_NOERR 32768
/* struct sockaddr is not large enough to hold any address,
and specifically not big enough to hold an IPv6 address.
Blech. Roll our own. */
union mysockaddr {
struct sockaddr sa;
struct sockaddr_in in;
#if defined(HAVE_IPV6)
struct sockaddr_in6 in6;
#endif
};
#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_LITERAL_ADDRESS 4 /* addr is the answer, not the server */
#define SERV_HAS_DOMAIN 8 /* 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_WARNED_RECURSIVE 64 /* avoid warning spam */
#define SERV_FROM_DBUS 128 /* 1 if source is DBus */
#define SERV_MARK 256 /* for mark-and-delete */
#define SERV_TYPE (SERV_HAS_DOMAIN | SERV_FOR_NODOTS)
#define SERV_COUNTED 512 /* workspace for log code */
struct serverfd {
int fd;
union mysockaddr source_addr;
char interface[IF_NAMESIZE + 1];
struct serverfd* next;
uint32_t mark;
};
struct randfd {
int fd;
unsigned short refcount, family;
};
struct server {
union mysockaddr addr, source_addr;
char interface[IF_NAMESIZE + 1];
struct serverfd* sfd;
char* domain; /* set if this server only handles a domain. */
int flags, tcpfd;
unsigned int queries, failed_queries;
uint32_t mark;
struct server* next;
};
struct irec {
union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
int dhcp_ok, mtu;
struct irec* next;
};
struct listener {
int fd, tcpfd, family;
struct irec* iface; /* only valid for non-wildcard */
struct listener* next;
};
/* interface and address parms from command line. */
struct iname {
char* name;
union mysockaddr addr;
int isloop, used;
struct iname* next;
};
/* resolv-file parms from command-line */
struct resolvc {
struct resolvc* next;
int is_default, logged;
time_t mtime;
char* name;
};
/* adn-hosts parms from command-line */
#define AH_DIR 1
#define AH_INACTIVE 2
struct hostsfile {
struct hostsfile* next;
int flags;
char* fname;
int index; /* matches to cache entries for logging */
};
struct frec {
union mysockaddr source;
struct all_addr dest;
struct server* sentto; /* NULL means free */
struct randfd* rfd4;
#ifdef HAVE_IPV6
struct randfd* rfd6;
#endif
unsigned int iface;
unsigned short orig_id, new_id;
int fd, forwardall;
unsigned int crc;
time_t time;
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 */
char aux_changed; /* CLID or expiry changed */
time_t expires; /* lease expiry */
#ifdef HAVE_BROKEN_RTC
unsigned int length;
#endif
int hwaddr_len, hwaddr_type;
unsigned char hwaddr[DHCP_CHADDR_MAX];
struct in_addr addr, override, giaddr;
unsigned char *vendorclass, *userclass, *supplied_hostname;
unsigned int vendorclass_len, userclass_len, supplied_hostname_len;
int last_interface;
struct dhcp_lease* next;
};
struct dhcp_netid {
char* net;
struct dhcp_netid* next;
};
struct dhcp_netid_list {
struct dhcp_netid* list;
struct dhcp_netid_list* next;
};
struct hwaddr_config {
int hwaddr_len, hwaddr_type;
unsigned char hwaddr[DHCP_CHADDR_MAX];
unsigned int wildcard_mask;
struct hwaddr_config* next;
};
struct dhcp_config {
unsigned int flags;
int clid_len; /* length of client identifier */
unsigned char* clid; /* clientid */
char *hostname, *domain;
struct dhcp_netid netid;
struct in_addr addr;
time_t decline_time;
unsigned int lease_time;
struct hwaddr_config* hwaddr;
struct dhcp_config* next;
};
#define CONFIG_DISABLE 1
#define CONFIG_CLID 2
#define CONFIG_TIME 8
#define CONFIG_NAME 16
#define CONFIG_ADDR 32
#define CONFIG_NETID 64
#define CONFIG_NOCLID 128
#define CONFIG_ADDR_HOSTS 512 /* address added by from /etc/hosts */
#define CONFIG_DECLINED 1024 /* address declined by client */
#define CONFIG_BANK 2048 /* from dhcp hosts file */
struct dhcp_opt {
int opt, len, flags;
union {
int encap;
unsigned int wildcard_mask;
unsigned char* vendor_class;
} u;
unsigned char* val;
struct dhcp_netid* netid;
struct dhcp_opt* next;
};
#define DHOPT_ADDR 1
#define DHOPT_STRING 2
#define DHOPT_ENCAPSULATE 4
#define DHOPT_ENCAP_MATCH 8
#define DHOPT_FORCE 16
#define DHOPT_BANK 32
#define DHOPT_ENCAP_DONE 64
#define DHOPT_MATCH 128
#define DHOPT_VENDOR 256
#define DHOPT_HEX 512
#define DHOPT_VENDOR_MATCH 1024
struct dhcp_boot {
char *file, *sname;
struct in_addr next_server;
struct dhcp_netid* netid;
struct dhcp_boot* next;
};
struct pxe_service {
unsigned short CSA, type;
char *menu, *basename;
struct in_addr server;
struct dhcp_netid* netid;
struct pxe_service* next;
};
#define MATCH_VENDOR 1
#define MATCH_USER 2
#define MATCH_CIRCUIT 3
#define MATCH_REMOTE 4
#define MATCH_SUBSCRIBER 5
/* vendorclass, userclass, remote-id or cicuit-id */
struct dhcp_vendor {
int len, match_type, option;
char* data;
struct dhcp_netid netid;
struct dhcp_vendor* next;
};
struct dhcp_mac {
unsigned int mask;
int hwaddr_len, hwaddr_type;
unsigned char hwaddr[DHCP_CHADDR_MAX];
struct dhcp_netid netid;
struct dhcp_mac* next;
};
struct dhcp_bridge {
char iface[IF_NAMESIZE];
struct dhcp_bridge *alias, *next;
};
struct cond_domain {
char* domain;
struct in_addr start, end;
struct cond_domain* next;
};
struct dhcp_context {
unsigned int lease_time, addr_epoch;
struct in_addr netmask, broadcast;
struct in_addr local, router;
struct in_addr start, end; /* range of available addresses */
int flags;
struct dhcp_netid netid, *filter;
struct dhcp_context *next, *current;
};
#define CONTEXT_STATIC 1
#define CONTEXT_NETMASK 2
#define CONTEXT_BRDCAST 4
#define CONTEXT_PROXY 8
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];
u8 options[312];
};
struct ping_result {
struct in_addr addr;
time_t time;
struct ping_result* next;
};
extern struct daemon {
/* datastuctures representing the command-line and
config file arguments. All set (including defaults)
in option.c */
unsigned int options;
struct resolvc default_resolv, *resolv_files;
time_t last_resolv;
struct mx_srv_record* mxnames;
struct naptr* naptr;
struct txt_record* txt;
struct ptr_record* ptr;
struct cname* cnames;
struct interface_name* int_names;
char* mxtarget;
char* lease_file;
char *username, *groupname, *scriptuser;
int group_set, osport;
char* domain_suffix;
struct cond_domain* cond_domain;
char* runfile;
char* lease_change_command;
struct iname *if_names, *if_addrs, *if_except, *dhcp_except;
struct bogus_addr* bogus_addr;
struct server* servers;
int log_fac; /* log facility */
char* log_file; /* optional log file */
int max_logs; /* queue limit */
int cachesize, ftabsize;
int port, query_port, min_port;
unsigned long local_ttl, neg_ttl;
struct hostsfile* addn_hosts;
struct dhcp_context* dhcp;
struct dhcp_config* dhcp_conf;
struct dhcp_opt *dhcp_opts, *dhcp_match;
struct dhcp_vendor* dhcp_vendors;
struct dhcp_mac* dhcp_macs;
struct dhcp_boot* boot_config;
struct pxe_service* pxe_services;
int enable_pxe;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *force_broadcast, *bootp_dynamic;
char *dhcp_hosts_file, *dhcp_opts_file;
int dhcp_max;
int dhcp_server_port, dhcp_client_port;
unsigned int min_leasetime;
struct doctor* doctors;
unsigned short edns_pktsz;
uint32_t listen_mark;
/* globally used stuff for DNS */
char* packet; /* packet buffer */
int packet_buff_sz; /* size of above */
char* namebuff; /* MAXDNAME size buffer */
unsigned int local_answer, queries_forwarded;
struct frec* frec_list;
struct serverfd* sfds;
struct irec* interfaces;
struct listener* listeners;
struct server* last_server;
time_t forwardtime;
int forwardcount;
struct server* srv_save; /* Used for resend on DoD */
size_t packet_len; /* " " */
struct randfd* rfd_save; /* " " */
pid_t tcp_pids[MAX_PROCS];
struct randfd randomsocks[RANDOM_SOCKS];
/* DHCP state */
int dhcpfd, helperfd;
int netlinkfd;
struct iovec dhcp_packet;
char *dhcp_buff, *dhcp_buff2;
struct ping_result* ping_results;
FILE* lease_stream;
struct dhcp_bridge* bridges;
} * daemon;
/* cache.c */
void cache_init(void);
void log_query(unsigned short flags, char* name, struct all_addr* addr, char* arg);
char* record_source(int index);
void querystr(char* str, unsigned short type);
struct crec* cache_find_by_addr(struct crec* crecp, struct all_addr* addr, time_t now,
unsigned short prot);
struct crec* cache_find_by_name(struct crec* crecp, char* name, time_t now, unsigned short prot);
void cache_end_insert(void);
void cache_start_insert(void);
struct crec* cache_insert(char* name, struct all_addr* addr, time_t now, unsigned long ttl,
unsigned short flags);
void cache_reload(void);
void cache_add_dhcp_entry(char* host_name, struct in_addr* host_address, time_t ttd);
void cache_unhash_dhcp(void);
void dump_cache(time_t now);
char* cache_get_name(struct crec* crecp);
char* get_domain(struct in_addr addr);
/* rfc1035.c */
unsigned short extract_request(HEADER* header, size_t qlen, char* name, unsigned short* typep);
size_t setup_reply(HEADER* header, size_t qlen, struct all_addr* addrp, unsigned short flags,
unsigned long local_ttl);
int extract_addresses(HEADER* header, size_t qlen, char* namebuff, time_t now);
size_t answer_request(HEADER* header, char* limit, size_t qlen, 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, struct bogus_addr* addr,
time_t now);
unsigned char* find_pseudoheader(HEADER* header, size_t plen, size_t* len, unsigned char** p,
int* is_sign);
int check_for_local_domain(char* name, time_t now);
unsigned int questions_crc(HEADER* header, size_t plen, char* buff);
size_t resize_packet(HEADER* header, size_t plen, unsigned char* pheader, size_t hlen);
/* util.c */
void rand_init(void);
unsigned short rand16(void);
int legal_hostname(char* c);
char* canonicalise(char* s, int* nomem);
unsigned char* do_rfc1035_name(unsigned char* p, char* sval, char *limit);
void* safe_malloc(size_t size);
void safe_pipe(int* fd, int read_noblock);
void* whine_malloc(size_t size);
int sa_len(union mysockaddr* addr);
int sockaddr_isequal(union mysockaddr* s1, union mysockaddr* s2);
int hostname_isequal(char* a, char* b);
time_t dnsmasq_time(void);
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
int retry_send(void);
int parse_addr(int family, const char* addrstr, union mysockaddr* addr);
void prettyprint_time(char* buf, unsigned int t);
int prettyprint_addr(const union mysockaddr* addr, char* buf);
int parse_hex(char* in, unsigned char* out, int maxlen, unsigned int* wildcard_mask, int* mac_type);
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(char* buff, unsigned char* mac, int len);
void bump_maxfd(int fd, int* max);
int read_write(int fd, unsigned char* packet, int size, int rw);
/* log.c */
void die(char* message, char* arg1, int exit_code);
int log_start(struct passwd* ent_pw, int errfd);
int log_reopen(char* log_file);
void my_syslog(int priority, const char* format, ...);
void set_log_writer(fd_set* set, int* maxfdp);
void check_log_writer(fd_set* set);
void flush_log(void);
/* option.c */
void read_opts(int argc, char** argv, char* compile_opts);
char* option_string(unsigned char opt, int* is_ip, int* is_name);
void reread_dhcp(void);
/* forward.c */
void reply_query(int fd, int family, time_t now);
void receive_query(struct listener* listen, time_t now);
unsigned char* tcp_request(int confd, time_t now, struct in_addr local_addr, struct in_addr netmask);
void server_gone(struct server* server);
struct frec* get_new_frec(time_t now, int* wait);
/* network.c */
int indextoname(int fd, int index, char* name);
int local_bind(int fd, union mysockaddr* addr, char* intname, uint32_t mark, int is_tcp);
int random_sock(int family);
void pre_allocate_sfds(void);
int reload_servers(char* fname);
#ifdef __ANDROID__
int set_servers(const char* servers);
void set_interfaces(const char* interfaces);
#endif
void check_servers(void);
int enumerate_interfaces();
struct listener* create_wildcard_listeners(void);
struct listener* create_bound_listeners(void);
int iface_check(int family, struct all_addr* addr, char* name, int* indexp);
int fix_fd(int fd);
struct in_addr get_ifaddr(char* intr);
/* dhcp.c */
#ifdef HAVE_DHCP
void dhcp_init(void);
void dhcp_packet(time_t now);
struct dhcp_context* address_available(struct dhcp_context* context, struct in_addr addr,
struct dhcp_netid* netids);
struct dhcp_context* narrow_context(struct dhcp_context* context, struct in_addr taddr,
struct dhcp_netid* netids);
int match_netid(struct dhcp_netid* check, struct dhcp_netid* pool, int negonly);
int address_allocate(struct dhcp_context* context, struct in_addr* addrp, unsigned char* hwaddr,
int hw_len, struct dhcp_netid* netids, time_t now);
int config_has_mac(struct dhcp_config* config, unsigned char* hwaddr, int len, int type);
struct dhcp_config* find_config(struct dhcp_config* configs, struct dhcp_context* context,
unsigned char* clid, int clid_len, unsigned char* hwaddr,
int hw_len, int hw_type, char* hostname);
void dhcp_update_configs(struct dhcp_config* configs);
void check_dhcp_hosts(int fatal);
struct dhcp_config* config_find_by_address(struct dhcp_config* configs, struct in_addr addr);
char* strip_hostname(char* hostname);
char* host_from_dns(struct in_addr addr);
char* get_domain(struct in_addr addr);
#endif
/* lease.c */
#ifdef HAVE_DHCP
void lease_update_file(time_t now);
void lease_update_dns();
void lease_init(time_t now);
struct dhcp_lease* lease_allocate(struct in_addr addr);
void lease_set_hwaddr(struct dhcp_lease* lease, unsigned char* hwaddr, unsigned char* clid,
int hw_len, int hw_type, int clid_len);
void lease_set_hostname(struct dhcp_lease* lease, char* name, int auth);
void lease_set_expires(struct dhcp_lease* lease, unsigned int len, time_t now);
void lease_set_interface(struct dhcp_lease* lease, int interface);
struct dhcp_lease* lease_find_by_client(unsigned char* hwaddr, int hw_len, int hw_type,
unsigned char* clid, int clid_len);
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(void);
int do_script_run(time_t now);
void rerun_scripts(void);
#endif
/* rfc2131.c */
#ifdef HAVE_DHCP
size_t dhcp_reply(struct dhcp_context* context, char* iface_name, int int_index, size_t sz,
time_t now, int unicast_dest, int* is_inform);
unsigned char* extended_hwaddr(int hwtype, int hwlen, unsigned char* hwaddr, int clid_len,
unsigned char* clid, int* len_out);
#endif
/* dnsmasq.c */
#ifdef HAVE_DHCP
int make_icmp_sock(void);
int icmp_ping(struct in_addr addr);
#endif
void send_event(int fd, int event, int data);
void clear_cache_and_reload(time_t now);
/* netlink.c */
#ifdef HAVE_LINUX_NETWORK
void netlink_init(void);
void netlink_multicast(void);
#endif
/* bpf.c or netlink.c */
int iface_enumerate(void* parm, int (*ipv4_callback)(), int (*ipv6_callback)());
/* helper.c */
#if defined(HAVE_DHCP) && !defined(NO_FORK)
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
void helper_write(void);
void queue_script(int action, struct dhcp_lease* lease, char* hostname, time_t now);
int helper_buf_empty(void);
#endif
./src/util.c 0000644 0000000 0000000 00000026603 14256750714 011643 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
/* The SURF random number generator was taken from djbdns-1.05, by
Daniel J Bernstein, which is public domain. */
#include "dnsmasq.h"
#include
#ifdef HAVE_BROKEN_RTC
#include
#endif
#ifdef LOCALEDIR
#include
#endif
#ifdef HAVE_ARC4RANDOM
void rand_init(void) {
return;
}
unsigned short rand16(void) {
return (unsigned short) (arc4random() >> 15);
}
#else
/* SURF random number generator */
typedef unsigned int uint32;
static uint32 seed[32];
static uint32 in[12];
static uint32 out[8];
void rand_init() {
int fd = open(RANDFILE, O_RDONLY);
if (fd == -1 || !read_write(fd, (unsigned char*) &seed, sizeof(seed), 1) ||
!read_write(fd, (unsigned char*) &in, sizeof(in), 1))
die(_("failed to seed the random number generator: %s"), NULL, EC_MISC);
close(fd);
}
#define ROTATE(x, b) (((x) << (b)) | ((x) >> (32 - (b))))
#define MUSH(i, b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x, b));
static void surf(void) {
uint32 t[12];
uint32 x;
uint32 sum = 0;
int r;
int i;
int loop;
for (i = 0; i < 12; ++i) t[i] = in[i] ^ seed[12 + i];
for (i = 0; i < 8; ++i) out[i] = seed[24 + i];
x = t[11];
for (loop = 0; loop < 2; ++loop) {
for (r = 0; r < 16; ++r) {
sum += 0x9e3779b9;
MUSH(0, 5)
MUSH(1, 7) MUSH(2, 9) MUSH(3, 13) MUSH(4, 5) MUSH(5, 7) MUSH(6, 9) MUSH(7, 13)
MUSH(8, 5) MUSH(9, 7) MUSH(10, 9) MUSH(11, 13)
}
for (i = 0; i < 8; ++i) out[i] ^= t[i + 4];
}
}
unsigned short rand16(void) {
static int outleft = 0;
if (!outleft) {
if (!++in[0])
if (!++in[1])
if (!++in[2]) ++in[3];
surf();
outleft = 8;
}
return (unsigned short) out[--outleft];
}
#endif
static int check_name(char* in) {
/* remove trailing .
also fail empty string and label > 63 chars */
size_t dotgap = 0, l = strlen(in);
char c;
int nowhite = 0;
if (l == 0 || l > MAXDNAME) return 0;
if (in[l - 1] == '.') {
if (l == 1) return 0;
in[l - 1] = 0;
}
for (; (c = *in); in++) {
if (c == '.')
dotgap = 0;
else if (++dotgap > MAXLABEL)
return 0;
else if (isascii(c) && iscntrl(c))
/* iscntrl only gives expected results for ascii */
return 0;
#ifndef LOCALEDIR
else if (!isascii(c))
return 0;
#endif
else if (c != ' ')
nowhite = 1;
}
if (!nowhite) return 0;
return 1;
}
/* Hostnames have a more limited valid charset than domain names
so check for legal char a-z A-Z 0-9 - _
Note that this may receive a FQDN, so only check the first label
for the tighter criteria. */
int legal_hostname(char* name) {
char c;
if (!check_name(name)) return 0;
for (; (c = *name); name++)
/* check for legal char a-z A-Z 0-9 - _ . */
{
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
c == '-' || c == '_')
continue;
/* end of hostname part */
if (c == '.') return 1;
return 0;
}
return 1;
}
char* canonicalise(char* in, int* nomem) {
char* ret = NULL;
#ifdef LOCALEDIR
int rc;
#endif
if (nomem) *nomem = 0;
if (!check_name(in)) return NULL;
#ifdef LOCALEDIR
if ((rc = idna_to_ascii_lz(in, &ret, 0)) != IDNA_SUCCESS) {
if (ret) free(ret);
if (nomem && (rc == IDNA_MALLOC_ERROR || rc == IDNA_DLOPEN_ERROR)) {
my_syslog(LOG_ERR, _("failed to allocate memory"));
*nomem = 1;
}
return NULL;
}
#else
if ((ret = whine_malloc(strlen(in) + 1)))
strcpy(ret, in);
else if (nomem)
*nomem = 1;
#endif
return ret;
}
unsigned char* do_rfc1035_name(unsigned char* p, char* sval, char *limit) {
int j;
while (sval && *sval) {
if (limit && p + 1 > (unsigned char*)limit)
return p;
unsigned char* cp = p++;
for (j = 0; *sval && (*sval != '.'); sval++, j++) {
if (limit && p + 1 > (unsigned char*)limit)
return p;
*p++ = *sval;
}
*cp = j;
if (*sval) sval++;
}
return p;
}
/* for use during startup */
void* safe_malloc(size_t size) {
void* ret = malloc(size);
if (!ret) die(_("could not get memory"), NULL, EC_NOMEM);
return ret;
}
void safe_pipe(int* fd, int read_noblock) {
if (pipe(fd) == -1 || !fix_fd(fd[1]) || (read_noblock && !fix_fd(fd[0])))
die(_("cannot create pipe: %s"), NULL, EC_MISC);
}
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) {
if (s1->sa.sa_family == s2->sa.sa_family) {
if (s1->sa.sa_family == AF_INET && s1->in.sin_port == s2->in.sin_port &&
s1->in.sin_addr.s_addr == s2->in.sin_addr.s_addr)
return 1;
#ifdef HAVE_IPV6
if (s1->sa.sa_family == AF_INET6 && s1->in6.sin6_port == s2->in6.sin6_port &&
IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr) &&
(!IN6_IS_ADDR_LINKLOCAL(&s1->in6.sin6_addr) ||
(s1->in6.sin6_scope_id == s2->in6.sin6_scope_id)))
return 1;
#endif
}
return 0;
}
int sa_len(union mysockaddr* addr) {
if (addr->sa.sa_family == AF_INET6)
return sizeof(addr->in6);
else
return sizeof(addr->in);
}
/* don't use strcasecmp and friends here - they may be messed up by LOCALE */
int hostname_isequal(char* a, char* b) {
unsigned int c1, c2;
do {
c1 = (unsigned char) *a++;
c2 = (unsigned char) *b++;
if (c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A';
if (c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A';
if (c1 != c2) return 0;
} while (c1);
return 1;
}
time_t dnsmasq_time(void) {
#ifdef HAVE_BROKEN_RTC
struct tms dummy;
static long tps = 0;
if (tps == 0) tps = sysconf(_SC_CLK_TCK);
return (time_t)(times(&dummy) / tps);
#else
return time(NULL);
#endif
}
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);
}
int parse_addr(int family, const char* addrstr, union mysockaddr* addr) {
struct addrinfo *res, hints = {
.ai_flags = AI_NUMERICHOST,
.ai_family = family,
.ai_socktype = SOCK_DGRAM,
};
int ret = getaddrinfo(addrstr, NULL, &hints, &res);
if (ret) {
return ret;
}
switch (res->ai_family) {
case AF_INET:
addr->in = *((struct sockaddr_in*) res->ai_addr);
break;
#ifdef HAVE_IPV6
case AF_INET6:
addr->in6 = *((struct sockaddr_in6*) res->ai_addr);
break;
#endif
default:
errno = EAFNOSUPPORT;
ret = -1;
break;
}
freeaddrinfo(res);
return ret;
}
/* returns port number from address */
int prettyprint_addr(const union mysockaddr* addr, char* buf) {
int port = 0;
#ifdef HAVE_IPV6
char portstr[strlen("65535")];
getnameinfo((const struct sockaddr*) addr, sizeof(*addr), buf, ADDRSTRLEN, portstr,
sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
port = atoi(portstr);
#else
strcpy(buf, inet_ntoa(addr->in.sin_addr));
port = ntohs(addr->in.sin_port);
#endif
return port;
}
void prettyprint_time(char* buf, unsigned int t) {
if (t == 0xffffffff)
sprintf(buf, _("infinite"));
else {
unsigned int x, p = 0;
if ((x = t / 86400)) p += sprintf(&buf[p], "%dd", x);
if ((x = (t / 3600) % 24)) p += sprintf(&buf[p], "%dh", x);
if ((x = (t / 60) % 60)) p += sprintf(&buf[p], "%dm", x);
if ((x = t % 60)) p += sprintf(&buf[p], "%ds", x);
}
}
/* in may equal out, when maxlen may be -1 (No max len). */
int parse_hex(char* in, unsigned char* out, int maxlen, unsigned int* wildcard_mask, int* mac_type) {
int mask = 0, i = 0;
char* r;
if (mac_type) *mac_type = 0;
while (maxlen == -1 || i < maxlen) {
for (r = in; *r != 0 && *r != ':' && *r != '-'; r++)
;
if (*r == 0) maxlen = i;
if (r != in) {
if (*r == '-' && i == 0 && mac_type) {
*r = 0;
*mac_type = strtol(in, NULL, 16);
mac_type = NULL;
} else {
*r = 0;
mask = mask << 1;
if (strcmp(in, "*") == 0)
mask |= 1;
else
out[i] = strtol(in, NULL, 16);
i++;
}
}
in = r + 1;
}
if (wildcard_mask) *wildcard_mask = mask;
return i;
}
/* return 0 for no match, or (no matched octets) + 1 */
int memcmp_masked(unsigned char* a, unsigned char* b, int len, unsigned int mask) {
int i, count;
for (count = 1, i = len - 1; i >= 0; i--, mask = mask >> 1)
if (!(mask & 1)) {
if (a[i] == b[i])
count++;
else
return 0;
}
return count;
}
/* _note_ may copy buffer */
int expand_buf(struct iovec* iov, size_t size) {
void* new;
if (size <= (size_t) iov->iov_len) return 1;
if (!(new = whine_malloc(size))) {
errno = ENOMEM;
return 0;
}
if (iov->iov_base) {
memcpy(new, iov->iov_base, iov->iov_len);
free(iov->iov_base);
}
iov->iov_base = new;
iov->iov_len = size;
return 1;
}
char* print_mac(char* buff, unsigned char* mac, int len) {
char* p = buff;
int i;
if (len == 0)
sprintf(p, "");
else
for (i = 0; i < len; i++) p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
return buff;
}
void bump_maxfd(int fd, int* max) {
if (fd > *max) *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) {
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 (retry_send() || errno == ENOMEM || errno == ENOBUFS)
goto retry;
else
return 0;
}
}
return 1;
}
./src/helper.c 0000644 0000000 0000000 00000030626 14256750714 012145 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#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.
*/
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
static void my_setenv(const char* name, const char* value, int* error);
struct script_data {
unsigned char action, hwaddr_len, hwaddr_type;
unsigned char clid_len, hostname_len, uclass_len, vclass_len, shost_len;
struct in_addr addr, giaddr;
unsigned int remaining_time;
#ifdef HAVE_BROKEN_RTC
unsigned int length;
#else
time_t expires;
#endif
unsigned char hwaddr[DHCP_CHADDR_MAX];
char interface[IF_NAMESIZE];
};
static struct script_data* buf = NULL;
static size_t bytes_in_buf = 0, buf_size = 0;
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) {
pid_t pid;
int i, pipefd[2];
struct sigaction sigact;
/* 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) {
send_event(err_fd, EVENT_PIPE_ERR, errno);
_exit(0);
}
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
and SIGALRM so that we can use sleep() */
sigact.sa_handler = SIG_IGN;
sigact.sa_flags = 0;
sigemptyset(&sigact.sa_mask);
sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGALRM, &sigact, NULL);
if (!(daemon->options & OPT_DEBUG) && uid != 0) {
gid_t dummy;
if (setgroups(0, &dummy) == -1 || setgid(gid) == -1 || setuid(uid) == -1) {
if (daemon->options & OPT_NO_FORK) /* send error to daemon process if no-fork */
send_event(event_fd, EVENT_HUSER_ERR, errno);
else {
/* kill daemon */
send_event(event_fd, EVENT_DIE, 0);
/* return error */
send_event(err_fd, EVENT_HUSER_ERR, errno);
}
_exit(0);
}
}
/* close all the sockets etc, we don't need them here. This closes err_fd, so that
main process can return. */
for (max_fd--; max_fd >= 0; max_fd--)
if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && max_fd != STDIN_FILENO &&
max_fd != pipefd[0] && max_fd != event_fd)
close(max_fd);
/* loop here */
while (1) {
struct script_data data;
char *p, *action_str, *hostname = NULL;
unsigned char* buf = (unsigned char*) daemon->namebuff;
int err = 0;
/* 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 + data.shost_len, 1))
continue;
/* possible fork errors are all temporary resource problems */
while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM)) sleep(2);
if (pid == -1) continue;
/* wait for child to complete */
if (pid != 0) {
/* reap our children's children, if necessary */
while (1) {
int status;
pid_t rc = wait(&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;
}
if (data.clid_len != 0) my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
if (strlen(data.interface) != 0) my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
#ifdef HAVE_BROKEN_RTC
my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
#else
my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
#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;
my_setenv("DNSMASQ_VENDOR_CLASS", (char*) buf, &err);
buf += data.vclass_len;
}
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++);
my_setenv(daemon->dhcp_buff2, (char*) buf, &err);
}
buf += len;
}
}
if (data.shost_len != 0) {
buf[data.shost_len - 1] = 0; /* don't trust zero-term */
/* cannot have = chars in env - truncate if found . */
if ((p = strchr((char*) buf, '='))) *p = 0;
my_setenv("DNSMASQ_SUPPLIED_HOSTNAME", (char*) buf, &err);
buf += data.shost_len;
}
if (data.giaddr.s_addr != 0)
my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
sprintf(daemon->dhcp_buff2, "%u ", data.remaining_time);
my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
if (data.hostname_len != 0) {
char* dot;
hostname = (char*) buf;
hostname[data.hostname_len - 1] = 0;
if (!legal_hostname(hostname))
hostname = NULL;
else if ((dot = strchr(hostname, '.'))) {
my_setenv("DNSMASQ_DOMAIN", dot + 1, &err);
*dot = 0;
}
}
if (data.action == ACTION_OLD_HOSTNAME && hostname) {
my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
hostname = NULL;
}
/* 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, '/');
if (err == 0) {
execl(daemon->lease_change_command, p ? p + 1 : daemon->lease_change_command,
action_str, daemon->dhcp_buff, inet_ntoa(data.addr), hostname, (char*) NULL);
err = errno;
}
/* failed, send event so the main process logs the problem */
send_event(event_fd, EVENT_EXEC_ERR, err);
_exit(0);
}
}
static void my_setenv(const char* name, const char* value, int* error) {
if (*error == 0 && setenv(name, value, 1) != 0) *error = errno;
}
/* pack up lease data into a buffer */
void queue_script(int action, struct dhcp_lease* lease, char* hostname, time_t now) {
unsigned char* p;
size_t size;
unsigned int hostname_len = 0, clid_len = 0, vclass_len = 0;
unsigned int uclass_len = 0, shost_len = 0;
/* no script */
if (daemon->helperfd == -1) return;
if (lease->vendorclass) vclass_len = lease->vendorclass_len;
if (lease->userclass) uclass_len = lease->userclass_len;
if (lease->supplied_hostname) shost_len = lease->supplied_hostname_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 + shost_len + hostname_len;
if (size > buf_size) {
struct script_data* new;
/* start with reasonable size, will almost never need extending. */
if (size < sizeof(struct script_data) + 200) size = sizeof(struct script_data) + 200;
if (!(new = whine_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->shost_len = shost_len;
buf->hostname_len = hostname_len;
buf->addr = lease->addr;
buf->giaddr = lease->giaddr;
memcpy(buf->hwaddr, lease->hwaddr, lease->hwaddr_len);
buf->interface[0] = 0;
#ifdef HAVE_LINUX_NETWORK
if (lease->last_interface != 0) {
struct ifreq ifr;
ifr.ifr_ifindex = lease->last_interface;
if (ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) != -1)
strncpy(buf->interface, ifr.ifr_name, IF_NAMESIZE);
}
#else
if (lease->last_interface != 0) if_indextoname(lease->last_interface, buf->interface);
#endif
#ifdef HAVE_BROKEN_RTC
buf->length = lease->length;
#else
buf->expires = lease->expires;
#endif
buf->remaining_time = (unsigned int) difftime(lease->expires, now);
p = (unsigned char*) (buf + 1);
if (clid_len != 0) {
memcpy(p, lease->clid, clid_len);
p += clid_len;
}
if (vclass_len != 0) {
memcpy(p, lease->vendorclass, vclass_len);
p += vclass_len;
}
if (uclass_len != 0) {
memcpy(p, lease->userclass, uclass_len);
p += uclass_len;
}
if (shost_len != 0) {
memcpy(p, lease->supplied_hostname, shost_len);
p += shost_len;
}
if (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(void) {
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;
}
}
#endif
./src/network.c 0000644 0000000 0000000 00000113160 14256750714 012352 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "dnsmasq.h"
static const char SEPARATOR[] = "|";
#ifdef HAVE_LINUX_NETWORK
int indextoname(int fd, int index, char* name) {
struct ifreq ifr;
if (index == 0) return 0;
ifr.ifr_ifindex = index;
if (ioctl(fd, SIOCGIFNAME, &ifr) == -1) return 0;
strncpy(name, ifr.ifr_name, IF_NAMESIZE);
return 1;
}
#else
int indextoname(int fd, int index, char* name) {
if (index == 0 || !if_indextoname(index, name)) return 0;
return 1;
}
#endif
int iface_check(int family, struct all_addr* addr, char* name, int* indexp) {
struct iname* tmp;
int ret = 1;
/* Note: have to check all and not bail out early, so that we set the
"used" flags. */
if (indexp) {
/* One form of bridging on BSD has the property that packets
can be recieved on bridge interfaces which do not have an IP address.
We allow these to be treated as aliases of another interface which does have
an IP address with --dhcp-bridge=interface,alias,alias */
struct dhcp_bridge *bridge, *alias;
for (bridge = daemon->bridges; bridge; bridge = bridge->next) {
for (alias = bridge->alias; alias; alias = alias->next)
if (strncmp(name, alias->iface, IF_NAMESIZE) == 0) {
int newindex;
if (!(newindex = if_nametoindex(bridge->iface))) {
my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), name);
return 0;
} else {
*indexp = newindex;
strncpy(name, bridge->iface, IF_NAMESIZE);
break;
}
}
if (alias) break;
}
}
if (daemon->if_names || (addr && daemon->if_addrs)) {
ret = 0;
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0)) ret = tmp->used = 1;
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (addr && tmp->addr.sa.sa_family == family) {
if (family == AF_INET && tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
ret = tmp->used = 1;
#ifdef HAVE_IPV6
else if (family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr.addr6) &&
(!IN6_IS_ADDR_LINKLOCAL(&addr->addr.addr6) ||
(tmp->addr.in6.sin6_scope_id == (uint32_t) *indexp)))
ret = tmp->used = 1;
#endif
}
}
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0)) ret = 0;
return ret;
}
static int iface_allowed(struct irec** irecp, int if_index, union mysockaddr* addr,
struct in_addr netmask) {
struct irec* iface;
int fd, mtu = 0, loopback;
struct ifreq ifr;
int dhcp_ok = 1;
struct iname* tmp;
/* check whether the interface IP has been added already
we call this routine multiple times. */
for (iface = *irecp; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, addr)) return 1;
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1 || !indextoname(fd, if_index, ifr.ifr_name) ||
ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {
if (fd != -1) {
int errsave = errno;
close(fd);
errno = errsave;
}
return 0;
}
loopback = ifr.ifr_flags & IFF_LOOPBACK;
if (ioctl(fd, SIOCGIFMTU, &ifr) != -1) mtu = ifr.ifr_mtu;
close(fd);
/* If we are restricting the set of interfaces to use, make
sure that loopback interfaces are in that set. */
if (daemon->if_names && loopback) {
struct iname* lo;
for (lo = daemon->if_names; lo; lo = lo->next)
if (lo->name && strcmp(lo->name, ifr.ifr_name) == 0) {
lo->isloop = 1;
break;
}
if (!lo && (lo = whine_malloc(sizeof(struct iname))) &&
(lo->name = whine_malloc(strlen(ifr.ifr_name) + 1))) {
strcpy(lo->name, ifr.ifr_name);
lo->isloop = lo->used = 1;
lo->next = daemon->if_names;
daemon->if_names = lo;
}
}
if (addr->sa.sa_family == AF_INET &&
!iface_check(AF_INET, (struct all_addr*) &addr->in.sin_addr, ifr.ifr_name, NULL))
return 1;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0)) dhcp_ok = 0;
#ifdef HAVE_IPV6
int ifindex = (int) addr->in6.sin6_scope_id;
if (addr->sa.sa_family == AF_INET6 &&
!iface_check(AF_INET6, (struct all_addr*) &addr->in6.sin6_addr, ifr.ifr_name, &ifindex))
return 1;
#endif
/* add to list */
if ((iface = whine_malloc(sizeof(struct irec)))) {
iface->addr = *addr;
iface->netmask = netmask;
iface->dhcp_ok = dhcp_ok;
iface->mtu = mtu;
iface->next = *irecp;
*irecp = iface;
return 1;
}
errno = ENOMEM;
return 0;
}
#ifdef HAVE_IPV6
static int iface_allowed_v6(struct in6_addr* local, int scope, int if_index, void* vparam) {
union mysockaddr addr;
struct in_addr netmask; /* dummy */
netmask.s_addr = 0;
memset(&addr, 0, sizeof(addr));
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = *local;
addr.in6.sin6_port = htons(daemon->port);
/**
* Only populate the scope ID if the address is link-local.
* Scope IDs are not meaningful for global addresses. Also, we do not want to
* think that two addresses are different if they differ only in scope ID,
* because the kernel will treat them as if they are the same.
*/
if (IN6_IS_ADDR_LINKLOCAL(local)) {
addr.in6.sin6_scope_id = scope;
}
return iface_allowed((struct irec**) vparam, if_index, &addr, netmask);
}
#endif
static int iface_allowed_v4(struct in_addr local, int if_index, struct in_addr netmask,
struct in_addr broadcast, void* vparam) {
union mysockaddr addr;
memset(&addr, 0, sizeof(addr));
addr.in.sin_family = AF_INET;
addr.in.sin_addr = broadcast; /* warning */
addr.in.sin_addr = local;
addr.in.sin_port = htons(daemon->port);
return iface_allowed((struct irec**) vparam, if_index, &addr, netmask);
}
int enumerate_interfaces(void) {
#ifdef HAVE_IPV6
return iface_enumerate(&daemon->interfaces, iface_allowed_v4, iface_allowed_v6);
#else
return iface_enumerate(&daemon->interfaces, iface_allowed_v4, NULL);
#endif
}
/* set NONBLOCK bit on fd: See Stevens 16.6 */
int fix_fd(int fd) {
int flags;
if ((flags = fcntl(fd, F_GETFL)) == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
return 0;
return 1;
}
#if defined(HAVE_IPV6)
static int create_ipv6_listener(struct listener** link, int port) {
union mysockaddr addr;
int tcpfd, fd;
struct listener* l;
int opt = 1;
memset(&addr, 0, sizeof(addr));
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = in6addr_any;
addr.in6.sin6_port = htons(port);
/* No error of the kernel doesn't support IPv6 */
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
return (errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT || errno == EINVAL);
if ((tcpfd = socket(AF_INET6, SOCK_STREAM, 0)) == -1) return 0;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
setsockopt(tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 || !fix_fd(fd) ||
!fix_fd(tcpfd) ||
#ifdef IPV6_RECVPKTINFO
setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1 ||
#else
setsockopt(fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1 ||
#endif
bind(tcpfd, (struct sockaddr*) &addr, sa_len(&addr)) == -1 || listen(tcpfd, 5) == -1 ||
bind(fd, (struct sockaddr*) &addr, sa_len(&addr)) == -1)
return 0;
l = safe_malloc(sizeof(struct listener));
l->fd = fd;
l->tcpfd = tcpfd;
l->family = AF_INET6;
l->iface = NULL;
l->next = NULL;
*link = l;
return 1;
}
#endif
struct listener* create_wildcard_listeners(void) {
union mysockaddr addr;
int opt = 1;
struct listener *l, *l6 = NULL;
int tcpfd = -1, fd = -1;
memset(&addr, 0, sizeof(addr));
addr.in.sin_family = AF_INET;
addr.in.sin_addr.s_addr = INADDR_ANY;
addr.in.sin_port = htons(daemon->port);
if (daemon->port != 0) {
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ||
(tcpfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return NULL;
if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
bind(tcpfd, (struct sockaddr*) &addr, sa_len(&addr)) == -1 || listen(tcpfd, 5) == -1 ||
!fix_fd(tcpfd) ||
#ifdef HAVE_IPV6
!create_ipv6_listener(&l6, daemon->port) ||
#endif
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || !fix_fd(fd) ||
#if defined(HAVE_LINUX_NETWORK)
setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
#endif
bind(fd, (struct sockaddr*) &addr, sa_len(&addr)) == -1)
return NULL;
#ifdef __ANDROID__
uint32_t mark = daemon->listen_mark;
if (mark != 0 && (setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) == -1 ||
setsockopt(tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) == -1 ||
setsockopt(l6->fd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) == -1 ||
setsockopt(l6->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) == -1)) {
my_syslog(LOG_WARNING, _("setsockopt(SO_MARK, 0x%x: %s"), mark, strerror(errno));
close(fd);
close(tcpfd);
close(l6->fd);
close(l6->tcpfd);
return NULL;
}
}
#endif /* __ANDROID__ */
l = safe_malloc(sizeof(struct listener));
l->family = AF_INET;
l->fd = fd;
l->tcpfd = tcpfd;
l->iface = NULL;
l->next = l6;
return l;
}
#ifdef __ANDROID__
/**
* for a single given irec (interface name and address) create
* a set of sockets listening. This is a copy of the code inside the loop
* of create_bound_listeners below and is added here to allow us
* to create just a single new listener dynamically when our interface
* list is changed.
*
* iface - input of the new interface details to listen on
* listeners - output. Creates a new struct listener and inserts at head of the list
*
* die's on errors, so don't pass bad data.
*/
void create_bound_listener(struct listener** listeners, struct irec* iface) {
int rc, opt = 1;
#ifdef HAVE_IPV6
static int dad_count = 0;
#endif
struct listener* new = safe_malloc(sizeof(struct listener));
new->family = iface->addr.sa.sa_family;
new->iface = iface;
new->next = *listeners;
new->tcpfd = -1;
new->fd = -1;
*listeners = new;
if (daemon->port != 0) {
if ((new->tcpfd = socket(iface->addr.sa.sa_family, SOCK_STREAM, 0)) == -1 ||
(new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1 ||
setsockopt(new->fd, 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->fd))
die(_("failed to create listening socket: %s"), NULL, EC_BADNET);
#ifdef HAVE_IPV6
if (iface->addr.sa.sa_family == AF_INET6) {
if (setsockopt(new->fd, 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, EC_BADNET);
}
#endif
while (1) {
if ((rc = bind(new->fd, &iface->addr.sa, sa_len(&iface->addr))) != -1) break;
#ifdef HAVE_IPV6
/* An interface may have an IPv6 address which is still undergoing DAD.
If so, the bind will fail until the DAD completes, so we try over 20 seconds
before failing. */
/* TODO: What to do here? 20 seconds is way too long. We use optimistic addresses, so
bind() will only fail if the address has already failed DAD, in which case retrying
won't help. */
if (iface->addr.sa.sa_family == AF_INET6 &&
(errno == ENODEV || errno == EADDRNOTAVAIL) && dad_count++ < DAD_WAIT) {
sleep(1);
continue;
}
#endif
break;
}
if (rc == -1 || bind(new->tcpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1) {
prettyprint_addr(&iface->addr, daemon->namebuff);
die(_("failed to bind listening socket for %s: %s"), daemon->namebuff, EC_BADNET);
}
uint32_t mark = daemon->listen_mark;
if (mark != 0 && (setsockopt(new->fd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) == -1 ||
setsockopt(new->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) == -1))
die(_("failed to set SO_MARK on listen socket: %s"), NULL, EC_BADNET);
if (listen(new->tcpfd, 5) == -1) die(_("failed to listen on socket: %s"), NULL, EC_BADNET);
}
}
/**
* If a listener has a struct irec pointer whose address matches the newly
* malloc()d struct irec's address, update its pointer to refer to this new
* struct irec instance.
*
* Otherwise, any listeners that are preserved across interface list changes
* will point at interface structures that are free()d at the end of
* set_interfaces(), and can get overwritten by subsequent memory allocations.
*
* See b/17475756 for further discussion.
*/
void fixup_possible_existing_listener(struct irec* new_iface) {
/* find the listener, if present */
struct listener* l;
for (l = daemon->listeners; l; l = l->next) {
struct irec* listener_iface = l->iface;
if (listener_iface) {
if (sockaddr_isequal(&listener_iface->addr, &new_iface->addr)) {
l->iface = new_iface;
return;
}
}
}
}
/**
* Closes the sockets of the specified listener, deletes it from the list, and frees it.
*
*/
int delete_listener(struct listener** l) {
struct listener* listener = *l;
if (listener == NULL) return 0;
if (listener->iface) {
int port = prettyprint_addr(&listener->iface->addr, daemon->namebuff);
my_syslog(LOG_INFO, _("Closing listener [%s]:%d"), daemon->namebuff, port);
} else {
my_syslog(LOG_INFO, _("Closing wildcard listener family=%d"), listener->family);
}
if (listener->tcpfd != -1) {
close(listener->tcpfd);
listener->tcpfd = -1;
}
if (listener->fd != -1) {
close(listener->fd);
listener->fd = -1;
}
*l = listener->next;
free(listener);
return -1;
}
/**
* Close the sockets listening on the given interface
*
* This new function is needed as we're dynamically changing the interfaces
* we listen on. Before they'd be opened once in create_bound_listeners and stay
* until we exited. Now, if an interface moves off the to-listen list we need to
* close out the listeners and keep trucking.
*
* interface - input of the interface details to listen on
*/
int close_bound_listener(struct irec* iface) {
/* find the listener */
int ret = 0;
struct listener** l = &daemon->listeners;
while (*l) {
struct irec* listener_iface = (*l)->iface;
struct listener** next = &((*l)->next);
if (iface && listener_iface && sockaddr_isequal(&listener_iface->addr, &iface->addr)) {
// Listener bound to an IP address. There can be only one of these.
ret = delete_listener(l);
break;
}
if (iface == NULL && listener_iface == NULL) {
// Wildcard listener. There is one of these per address family.
ret = delete_listener(l);
continue;
}
l = next;
}
return ret;
}
#endif /* __ANDROID__ */
struct listener* create_bound_listeners(void) {
struct listener* listeners = NULL;
struct irec* iface;
#ifndef __ANDROID__
int rc, opt = 1;
#ifdef HAVE_IPV6
static int dad_count = 0;
#endif
#endif
for (iface = daemon->interfaces; iface; iface = iface->next) {
#ifdef __ANDROID__
create_bound_listener(&listeners, iface);
#else
struct listener* new = safe_malloc(sizeof(struct listener));
new->family = iface->addr.sa.sa_family;
new->iface = iface;
new->next = listeners;
new->tcpfd = -1;
new->fd = -1;
listeners = new;
if (daemon->port != 0) {
if ((new->tcpfd = socket(iface->addr.sa.sa_family, SOCK_STREAM, 0)) == -1 ||
(new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1 ||
setsockopt(new->fd, 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->fd))
die(_("failed to create listening socket: %s"), NULL, EC_BADNET);
#ifdef HAVE_IPV6
if (iface->addr.sa.sa_family == AF_INET6) {
if (setsockopt(new->fd, 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,
EC_BADNET);
}
#endif
while (1) {
if ((rc = bind(new->fd, &iface->addr.sa, sa_len(&iface->addr))) != -1) break;
#ifdef HAVE_IPV6
/* An interface may have an IPv6 address which is still undergoing DAD.
If so, the bind will fail until the DAD completes, so we try over 20 seconds
before failing. */
if (iface->addr.sa.sa_family == AF_INET6 &&
(errno == ENODEV || errno == EADDRNOTAVAIL) && dad_count++ < DAD_WAIT) {
sleep(1);
continue;
}
#endif
break;
}
if (rc == -1 || bind(new->tcpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1) {
prettyprint_addr(&iface->addr, daemon->namebuff);
die(_("failed to bind listening socket for %s: %s"), daemon->namebuff,
EC_BADNET);
}
if (listen(new->tcpfd, 5) == -1)
die(_("failed to listen on socket: %s"), NULL, EC_BADNET);
}
#endif /* !__ANDROID */
}
return listeners;
}
/* return a UDP socket bound to a random port, have to cope with straying into
occupied port nos and reserved ones. */
int random_sock(int family) {
int fd;
if ((fd = socket(family, SOCK_DGRAM, 0)) != -1) {
union mysockaddr addr;
unsigned int ports_avail = 65536u - (unsigned short) daemon->min_port;
int tries = ports_avail < 30 ? 3 * ports_avail : 100;
memset(&addr, 0, sizeof(addr));
addr.sa.sa_family = family;
/* don't loop forever if all ports in use. */
if (fix_fd(fd))
while (tries--) {
unsigned short port = rand16();
if (daemon->min_port != 0)
port = htons(daemon->min_port + (port % ((unsigned short) ports_avail)));
if (family == AF_INET) {
addr.in.sin_addr.s_addr = INADDR_ANY;
addr.in.sin_port = port;
} else {
addr.in6.sin6_addr = in6addr_any;
addr.in6.sin6_port = port;
}
if (bind(fd, (struct sockaddr*) &addr, sa_len(&addr)) == 0) return fd;
if (errno != EADDRINUSE && errno != EACCES) break;
}
close(fd);
}
return -1;
}
int local_bind(int fd, union mysockaddr* addr, char* intname, uint32_t mark, int is_tcp) {
union mysockaddr addr_copy = *addr;
/* cannot set source _port_ for TCP connections. */
if (is_tcp) {
if (addr_copy.sa.sa_family == AF_INET) addr_copy.in.sin_port = 0;
#ifdef HAVE_IPV6
else
addr_copy.in6.sin6_port = 0;
#endif
}
if (bind(fd, (struct sockaddr*) &addr_copy, sa_len(&addr_copy)) == -1) return 0;
#if defined(SO_BINDTODEVICE)
if (intname[0] != 0 &&
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, strlen(intname)) == -1)
return 0;
#endif
if (mark != 0 && setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) == -1) return 0;
return 1;
}
static struct serverfd* allocate_sfd(union mysockaddr* addr, char* intname, uint32_t mark) {
struct serverfd* sfd;
int errsave;
/* when using random ports, servers which would otherwise use
the INADDR_ANY/port0 socket have sfd set to NULL */
if (!daemon->osport && intname[0] == 0) {
errno = 0;
if (addr->sa.sa_family == AF_INET && addr->in.sin_addr.s_addr == INADDR_ANY &&
addr->in.sin_port == htons(0))
return NULL;
#ifdef HAVE_IPV6
if (addr->sa.sa_family == AF_INET6 &&
memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&
addr->in6.sin6_port == htons(0))
return NULL;
#endif
}
/* may have a suitable one already */
for (sfd = daemon->sfds; sfd; sfd = sfd->next)
if (sockaddr_isequal(&sfd->source_addr, addr) && mark == sfd->mark &&
strcmp(intname, sfd->interface) == 0)
return sfd;
/* need to make a new one. */
errno = ENOMEM; /* in case malloc fails. */
if (!(sfd = whine_malloc(sizeof(struct serverfd)))) return NULL;
if ((sfd->fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0)) == -1) {
free(sfd);
return NULL;
}
if (!local_bind(sfd->fd, addr, intname, mark, 0) || !fix_fd(sfd->fd)) {
errsave = errno; /* save error from bind. */
close(sfd->fd);
free(sfd);
errno = errsave;
return NULL;
}
strcpy(sfd->interface, intname);
sfd->source_addr = *addr;
sfd->mark = mark;
sfd->next = daemon->sfds;
daemon->sfds = sfd;
return sfd;
}
/* create upstream sockets during startup, before root is dropped which may be needed
this allows query_port to be a low port and interface binding */
void pre_allocate_sfds(void) {
struct server* srv;
if (daemon->query_port != 0) {
union mysockaddr addr;
memset(&addr, 0, sizeof(addr));
addr.in.sin_family = AF_INET;
addr.in.sin_addr.s_addr = INADDR_ANY;
addr.in.sin_port = htons(daemon->query_port);
allocate_sfd(&addr, "", 0);
#ifdef HAVE_IPV6
memset(&addr, 0, sizeof(addr));
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = in6addr_any;
addr.in6.sin6_port = htons(daemon->query_port);
allocate_sfd(&addr, "", 0);
#endif
}
for (srv = daemon->servers; srv; srv = srv->next)
if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
!allocate_sfd(&srv->source_addr, srv->interface, srv->mark) && errno != 0 &&
(daemon->options & OPT_NOWILD)) {
prettyprint_addr(&srv->addr, daemon->namebuff);
if (srv->interface[0] != 0) {
strcat(daemon->namebuff, " ");
strcat(daemon->namebuff, srv->interface);
}
die(_("failed to bind server socket for %s: %s"), daemon->namebuff, EC_BADNET);
}
}
void check_servers(void) {
struct irec* iface;
struct server* new, *tmp, *ret = NULL;
int port = 0;
for (new = daemon->servers; new; new = tmp) {
tmp = new->next;
if (!(new->flags&(SERV_LITERAL_ADDRESS | SERV_NO_ADDR))) {
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;
if (iface) {
my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"),
daemon->namebuff);
free(new);
continue;
}
/* Do we need a socket set? */
if (!new->sfd &&
!(new->sfd = allocate_sfd(&new->source_addr, new->interface, new->mark)) &&
errno != 0) {
my_syslog(LOG_WARNING, _("ignoring nameserver %s - cannot make/bind socket: %s"),
daemon->namebuff, strerror(errno));
free(new);
continue;
}
}
/* reverse order - gets it right. */
new->next = ret;
ret = new;
if (new->flags&(SERV_HAS_DOMAIN | SERV_FOR_NODOTS)) {
char *s1, *s2;
if (!(new->flags& SERV_HAS_DOMAIN))
s1 = _("unqualified"), s2 = _("names");
else if (strlen(new->domain) == 0)
s1 = _("default"), s2 = "";
else
s1 = _("domain"), s2 = new->domain;
if (new->flags & SERV_NO_ADDR)
my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
else if (!(new->flags& SERV_LITERAL_ADDRESS))
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port,
s1, s2);
} else if (new->interface[0] != 0)
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port,
new->interface);
else
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
}
daemon->servers = ret;
}
#ifdef __ANDROID__
/* #define __ANDROID_DEBUG__ 1 */
/*
* Ingests a new list of interfaces and starts to listen on them, adding only the new
* and stopping to listen to any interfaces not on the new list.
*
* interfaces - input in the format "bt-pan|eth0|wlan0|..>" up to 1024 bytes long
*/
void set_interfaces(const char* interfaces) {
struct iname* if_tmp;
struct iname* prev_if_names;
struct irec *old_iface, *new_iface, *prev_interfaces;
char s[1024];
char* next = s;
char* interface;
int was_wild = 0;
#ifdef __ANDROID_DEBUG__
my_syslog(LOG_DEBUG, _("set_interfaces(%s)"), interfaces);
#endif
prev_if_names = daemon->if_names;
daemon->if_names = NULL;
prev_interfaces = daemon->interfaces;
daemon->interfaces = NULL;
if (strlen(interfaces) > sizeof(s)) {
die(_("interface string too long: %s"), NULL, EC_BADNET);
}
strncpy(s, interfaces, sizeof(s));
while ((interface = strsep(&next, SEPARATOR))) {
if (!if_nametoindex(interface)) {
my_syslog(LOG_ERR, _("interface given in %s: '%s' has no ifindex; ignoring"),
__FUNCTION__, interface);
continue;
}
if_tmp = safe_malloc(sizeof(struct iname));
memset(if_tmp, 0, sizeof(struct iname));
if ((if_tmp->name = strdup(interface)) == NULL) {
die(_("malloc failure in set_interfaces: %s"), NULL, EC_BADNET);
}
if_tmp->next = daemon->if_names;
daemon->if_names = if_tmp;
}
/*
* Enumerate IP addresses (via RTM_GETADDR), adding IP entries to
* daemon->interfaces for interface names listed in daemon->if_names.
* The sockets are created by the create_bound_listener call below.
*/
if (!enumerate_interfaces()) {
die(_("enumerate interfaces error in set_interfaces: %s"), NULL, EC_BADNET);
}
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) {
if (if_tmp->name && !if_tmp->used) {
my_syslog(LOG_ERR, _("unknown interface given %s in set_interfaces()"), if_tmp->name);
}
}
/* success! - setup to free the old */
/* check for any that have been removed */
for (old_iface = prev_interfaces; old_iface; old_iface = old_iface->next) {
int found = 0;
for (new_iface = daemon->interfaces; new_iface; new_iface = new_iface->next) {
if (sockaddr_isequal(&old_iface->addr, &new_iface->addr)) {
found = 1;
break;
}
}
if (found) {
fixup_possible_existing_listener(new_iface);
} else {
#ifdef __ANDROID_DEBUG__
char debug_buff[MAXDNAME];
prettyprint_addr(&old_iface->addr, debug_buff);
my_syslog(LOG_DEBUG, _("closing listener for %s"), debug_buff);
#endif
close_bound_listener(old_iface);
}
}
/* remove wildchar listeners */
was_wild = close_bound_listener(NULL);
if (was_wild) daemon->options |= OPT_NOWILD;
/* check for any that have been added */
for (new_iface = daemon->interfaces; new_iface; new_iface = new_iface->next) {
int found = 0;
/* if the previous setup used a wildchar, then add any current interfaces */
if (!was_wild) {
for (old_iface = prev_interfaces; old_iface; old_iface = old_iface->next) {
if (sockaddr_isequal(&old_iface->addr, &new_iface->addr)) {
found = -1;
break;
}
}
}
if (!found) {
#ifdef __ANDROID_DEBUG__
char debug_buff[MAXDNAME];
prettyprint_addr(&new_iface->addr, debug_buff);
my_syslog(LOG_DEBUG, _("adding listener for %s"), debug_buff);
#endif
create_bound_listener(&(daemon->listeners), new_iface);
}
}
while (prev_if_names) {
if (prev_if_names->name) free(prev_if_names->name);
if_tmp = prev_if_names->next;
free(prev_if_names);
prev_if_names = if_tmp;
}
while (prev_interfaces) {
struct irec* tmp_irec = prev_interfaces->next;
free(prev_interfaces);
prev_interfaces = tmp_irec;
}
#ifdef __ANDROID_DEBUG__
my_syslog(LOG_DEBUG, _("done with setInterfaces"));
#endif
}
/*
* Takes a string in the format "0x100b|1.2.3.4|1.2.3.4|..." - up to 1024 bytes in length
* - The first element is the socket mark to set on sockets that forward DNS queries.
* - The subsequent elements are the DNS servers to forward queries to.
*/
int set_servers(const char* servers) {
char s[1024];
struct server* old_servers = NULL;
struct server* new_servers = NULL;
struct server* serv;
char* mark_string;
uint32_t mark;
strncpy(s, servers, sizeof(s));
/* move old servers to free list - we can reuse the memory
and not risk malloc if there are the same or fewer new servers.
Servers which were specced on the command line go to the new list. */
for (serv = daemon->servers; serv;) {
struct server* tmp = serv->next;
if (serv->flags & SERV_FROM_RESOLV) {
serv->next = old_servers;
old_servers = serv;
/* forward table rules reference servers, so have to blow them away */
server_gone(serv);
} else {
serv->next = new_servers;
new_servers = serv;
}
serv = tmp;
}
char* next = s;
char* saddr;
/* Parse the mark. */
mark_string = strsep(&next, SEPARATOR);
mark = strtoul(mark_string, NULL, 0);
while ((saddr = strsep(&next, SEPARATOR))) {
union mysockaddr addr, source_addr;
memset(&addr, 0, sizeof(addr));
memset(&source_addr, 0, sizeof(source_addr));
if (parse_addr(AF_INET, saddr, &addr) == 0) {
addr.in.sin_port = htons(NAMESERVER_PORT);
source_addr.in.sin_family = AF_INET;
source_addr.in.sin_addr.s_addr = INADDR_ANY;
source_addr.in.sin_port = htons(daemon->query_port);
}
#ifdef HAVE_IPV6
else if (parse_addr(AF_INET6, saddr, &addr) == 0) {
addr.in6.sin6_port = htons(NAMESERVER_PORT);
source_addr.in6.sin6_family = AF_INET6;
source_addr.in6.sin6_addr = in6addr_any;
source_addr.in6.sin6_port = htons(daemon->query_port);
}
#endif /* IPV6 */
else
continue;
if (old_servers) {
serv = old_servers;
old_servers = old_servers->next;
} else if (!(serv = whine_malloc(sizeof(struct server))))
continue;
/* this list is reverse ordered:
it gets reversed again in check_servers */
serv->next = new_servers;
new_servers = serv;
serv->addr = addr;
serv->source_addr = source_addr;
serv->domain = NULL;
serv->interface[0] = 0;
serv->mark = mark;
serv->sfd = NULL;
serv->flags = SERV_FROM_RESOLV;
serv->queries = serv->failed_queries = 0;
}
/* Free any memory not used. */
while (old_servers) {
struct server* tmp = old_servers->next;
free(old_servers);
old_servers = tmp;
}
daemon->servers = new_servers;
return 0;
}
#endif
/* 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 */
int reload_servers(char* fname) {
FILE* f;
char* line;
struct server* old_servers = NULL;
struct server* new_servers = NULL;
struct server* serv;
int gotone = 0;
/* buff happens to be MAXDNAME long... */
if (!(f = fopen(fname, "r"))) {
my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno));
return 0;
}
/* move old servers to free list - we can reuse the memory
and not risk malloc if there are the same or fewer new servers.
Servers which were specced on the command line go to the new list. */
for (serv = daemon->servers; serv;) {
struct server* tmp = serv->next;
if (serv->flags & SERV_FROM_RESOLV) {
serv->next = old_servers;
old_servers = serv;
/* forward table rules reference servers, so have to blow them away */
server_gone(serv);
} else {
serv->next = new_servers;
new_servers = serv;
}
serv = tmp;
}
while ((line = fgets(daemon->namebuff, MAXDNAME, f))) {
union mysockaddr addr, source_addr;
char* token = strtok(line, " \t\n\r");
if (!token) continue;
if (strcmp(token, "nameserver") != 0 && strcmp(token, "server") != 0) continue;
if (!(token = strtok(NULL, " \t\n\r"))) continue;
memset(&addr, 0, sizeof(addr));
memset(&source_addr, 0, sizeof(source_addr));
if (parse_addr(AF_INET, token, &addr) == 0) {
addr.in.sin_port = htons(NAMESERVER_PORT);
source_addr.in.sin_family = AF_INET;
source_addr.in.sin_addr.s_addr = INADDR_ANY;
source_addr.in.sin_port = htons(daemon->query_port);
}
#ifdef HAVE_IPV6
else if (parse_addr(AF_INET6, token, &addr) == 0) {
addr.in6.sin6_port = htons(NAMESERVER_PORT);
source_addr.in6.sin6_family = AF_INET6;
source_addr.in6.sin6_addr = in6addr_any;
source_addr.in6.sin6_port = htons(daemon->query_port);
}
#endif /* IPV6 */
else
continue;
if (old_servers) {
serv = old_servers;
old_servers = old_servers->next;
} else if (!(serv = whine_malloc(sizeof(struct server))))
continue;
/* this list is reverse ordered:
it gets reversed again in check_servers */
serv->next = new_servers;
new_servers = serv;
serv->addr = addr;
serv->source_addr = source_addr;
serv->domain = NULL;
serv->interface[0] = 0;
serv->mark = 0;
serv->sfd = NULL;
serv->flags = SERV_FROM_RESOLV;
serv->queries = serv->failed_queries = 0;
gotone = 1;
}
/* Free any memory not used. */
while (old_servers) {
struct server* tmp = old_servers->next;
free(old_servers);
old_servers = tmp;
}
daemon->servers = new_servers;
fclose(f);
return gotone;
}
/* Use an IPv4 listener socket for ioctling */
struct in_addr get_ifaddr(char* intr) {
struct listener* l;
struct ifreq ifr;
for (l = daemon->listeners; l && l->family != AF_INET; l = l->next)
;
strncpy(ifr.ifr_name, intr, IF_NAMESIZE);
ifr.ifr_addr.sa_family = AF_INET;
if (!l || ioctl(l->fd, SIOCGIFADDR, &ifr) == -1)
((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr = -1;
return ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr;
}
./src/rfc1035.c 0000644 0000000 0000000 00000144471 14256750714 011755 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "dnsmasq.h"
static int add_resource_record(HEADER* header, char* limit, int* truncp, unsigned int nameoffset,
unsigned char** pp, unsigned long ttl, unsigned int* offset,
unsigned short type, unsigned short class, char* format, ...);
#define CHECK_LEN(header, pp, plen, len) \
((size_t)((pp) - (unsigned char*) (header) + (len)) <= (plen))
#define ADD_RDLEN(header, pp, plen, len) \
(!CHECK_LEN(header, pp, plen, len) ? 0 : (long) ((pp) += (len)), 1)
static int extract_name(HEADER* header, size_t plen, unsigned char** pp, char* name, int isExtract,
int extrabytes) {
unsigned char *cp = (unsigned char*) name, *p = *pp, *p1 = NULL;
unsigned int j, l, hops = 0;
int retvalue = 1;
if (isExtract) *cp = 0;
while (1) {
unsigned int label_type;
if (!CHECK_LEN(header, p, plen, 1)) return 0;
if ((l = *p++) == 0)
/* end marker */
{
/* check that there are the correct no of bytes after the name */
if (!CHECK_LEN(header, p1 ? p1 : p, plen, extrabytes)) return 0;
if (isExtract) {
if (cp != (unsigned char*) name) cp--;
*cp = 0; /* terminate: lose final period */
} else if (*cp != 0)
retvalue = 2;
if (p1) /* we jumped via compression */
*pp = p1;
else
*pp = p;
return retvalue;
}
label_type = l & 0xc0;
if (label_type == 0xc0) /* pointer */
{
if (!CHECK_LEN(header, p, plen, 1)) return 0;
/* get offset */
l = (l & 0x3f) << 8;
l |= *p++;
if (!p1) /* first jump, save location to go back to */
p1 = p;
hops++; /* break malicious infinite loops */
if (hops > 255) return 0;
p = l + (unsigned char*) header;
} else if (label_type == 0x80)
return 0; /* reserved */
else if (label_type == 0x40) { /* ELT */
unsigned int count, digs;
if ((l & 0x3f) != 1) return 0; /* we only understand bitstrings */
if (!isExtract) return 0; /* Cannot compare bitsrings */
count = *p++;
if (count == 0) count = 256;
digs = ((count - 1) >> 2) + 1;
/* output is \[x/siz]. which is digs+9 chars */
if (cp - (unsigned char*) name + digs + 9 >= MAXDNAME) return 0;
if (!CHECK_LEN(header, p, plen, (count - 1) >> 3)) return 0;
*cp++ = '\\';
*cp++ = '[';
*cp++ = 'x';
for (j = 0; j < digs; j++) {
unsigned int dig;
if (j % 2 == 0)
dig = *p >> 4;
else
dig = *p++ & 0x0f;
*cp++ = dig < 10 ? dig + '0' : dig + 'A' - 10;
}
cp += sprintf((char*) cp, "/%d]", count);
/* do this here to overwrite the zero char from sprintf */
*cp++ = '.';
} else { /* label_type = 0 -> label. */
if (cp - (unsigned char*) name + l + 1 >= MAXDNAME) return 0;
if (!CHECK_LEN(header, p, plen, l)) return 0;
for (j = 0; j < l; j++, p++)
if (isExtract) {
unsigned char c = *p;
if (isascii(c) && !iscntrl(c) && c != '.')
*cp++ = *p;
else
return 0;
} else {
unsigned char c1 = *cp, c2 = *p;
if (c1 == 0)
retvalue = 2;
else {
cp++;
if (c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A';
if (c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A';
if (c1 != c2) retvalue = 2;
}
}
if (isExtract)
*cp++ = '.';
else if (*cp != 0 && *cp++ != '.')
retvalue = 2;
}
}
}
/* Max size of input string (for IPv6) is 75 chars.) */
#define MAXARPANAME 75
static int in_arpa_name_2_addr(char* namein, struct all_addr* addrp) {
int j;
char name[MAXARPANAME + 1], *cp1;
unsigned char* addr = (unsigned char*) addrp;
char *lastchunk = NULL, *penchunk = NULL;
if (strlen(namein) > MAXARPANAME) return 0;
memset(addrp, 0, sizeof(struct all_addr));
/* turn name into a series of asciiz strings */
/* j counts no of labels */
for (j = 1, cp1 = name; *namein; cp1++, namein++)
if (*namein == '.') {
penchunk = lastchunk;
lastchunk = cp1 + 1;
*cp1 = 0;
j++;
} else
*cp1 = *namein;
*cp1 = 0;
if (j < 3) return 0;
if (hostname_isequal(lastchunk, "arpa") && hostname_isequal(penchunk, "in-addr")) {
/* IP v4 */
/* address arives as a name of the form
www.xxx.yyy.zzz.in-addr.arpa
some of the low order address octets might be missing
and should be set to zero. */
for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1) + 1) {
/* check for digits only (weeds out things like
50.0/24.67.28.64.in-addr.arpa which are used
as CNAME targets according to RFC 2317 */
char* cp;
for (cp = cp1; *cp; cp++)
if (!isdigit((int) *cp)) return 0;
addr[3] = addr[2];
addr[2] = addr[1];
addr[1] = addr[0];
addr[0] = atoi(cp1);
}
return F_IPV4;
}
#ifdef HAVE_IPV6
else if (hostname_isequal(penchunk, "ip6") &&
(hostname_isequal(lastchunk, "int") || hostname_isequal(lastchunk, "arpa"))) {
/* IP v6:
Address arrives as 0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.ip6.[int|arpa]
or \[xfedcba9876543210fedcba9876543210/128].ip6.[int|arpa]
Note that most of these the various reprentations are obsolete and
left-over from the many DNS-for-IPv6 wars. We support all the formats
that we can since there is no reason not to.
*/
/* TODO: does this make sense? */
if (*name == '\\' && *(name + 1) == '[' && (*(name + 2) == 'x' || *(name + 2) == 'X')) {
for (j = 0, cp1 = name + 3; *cp1 && isxdigit((int) *cp1) && j < 32; cp1++, j++) {
char xdig[2];
xdig[0] = *cp1;
xdig[1] = 0;
if (j % 2)
addr[j / 2] |= strtol(xdig, NULL, 16);
else
addr[j / 2] = strtol(xdig, NULL, 16) << 4;
}
if (*cp1 == '/' && j == 32) return F_IPV6;
} else {
for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1) + 1) {
if (*(cp1 + 1) || !isxdigit((int) *cp1)) return 0;
for (j = sizeof(struct all_addr) - 1; j > 0; j--)
addr[j] = (addr[j] >> 4) | (addr[j - 1] << 4);
addr[0] = (addr[0] >> 4) | (strtol(cp1, NULL, 16) << 4);
}
return F_IPV6;
}
}
#endif
return 0;
}
static unsigned char* skip_name(unsigned char* ansp, HEADER* header, size_t plen, int extrabytes) {
while (1) {
unsigned int label_type;
if (!CHECK_LEN(header, ansp, plen, 1)) return NULL;
label_type = (*ansp) & 0xc0;
if (label_type == 0xc0) {
/* pointer for compression. */
ansp += 2;
break;
} else if (label_type == 0x80)
return NULL; /* reserved */
else if (label_type == 0x40) {
/* Extended label type */
unsigned int count;
if (!CHECK_LEN(header, ansp, plen, 2)) return NULL;
if (((*ansp++) & 0x3f) != 1) return NULL; /* we only understand bitstrings */
count = *(ansp++); /* Bits in bitstring */
if (count == 0) /* count == 0 means 256 bits */
ansp += 32;
else
ansp += ((count - 1) >> 3) + 1;
} else { /* label type == 0 Bottom six bits is length */
unsigned int len = (*ansp++) & 0x3f;
if (!ADD_RDLEN(header, ansp, plen, len)) return NULL;
if (len == 0) break; /* zero length label marks the end. */
}
}
if (!CHECK_LEN(header, ansp, plen, extrabytes)) return NULL;
return ansp;
}
static unsigned char* skip_questions(HEADER* header, size_t plen) {
int q;
unsigned char* ansp = (unsigned char*) (header + 1);
for (q = ntohs(header->qdcount); q != 0; q--) {
if (!(ansp = skip_name(ansp, header, plen, 4))) return NULL;
ansp += 4; /* class and type */
}
return ansp;
}
static unsigned char* skip_section(unsigned char* ansp, int count, HEADER* header, size_t plen) {
int i, rdlen;
for (i = 0; i < count; i++) {
if (!(ansp = skip_name(ansp, header, plen, 10))) return NULL;
ansp += 8; /* type, class, TTL */
GETSHORT(rdlen, ansp);
if (!ADD_RDLEN(header, ansp, plen, rdlen)) return NULL;
}
return ansp;
}
/* CRC the question section. This is used to safely detect query
retransmision and to detect answers to questions we didn't ask, which
might be poisoning attacks. Note that we decode the name rather
than CRC the raw bytes, since replies might be compressed differently.
We ignore case in the names for the same reason. Return all-ones
if there is not question section. */
unsigned int questions_crc(HEADER* header, size_t plen, char* name) {
int q;
unsigned int crc = 0xffffffff;
unsigned char *p1, *p = (unsigned char*) (header + 1);
for (q = ntohs(header->qdcount); q != 0; q--) {
if (!extract_name(header, plen, &p, name, 1, 4)) return crc; /* bad packet */
for (p1 = (unsigned char*) name; *p1; p1++) {
int i = 8;
char c = *p1;
if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
crc ^= c << 24;
while (i--) crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
}
/* CRC the class and type as well */
for (p1 = p; p1 < p + 4; p1++) {
int i = 8;
crc ^= *p1 << 24;
while (i--) crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
}
p += 4;
if (!CHECK_LEN(header, p, plen, 0)) return crc; /* bad packet */
}
return crc;
}
size_t resize_packet(HEADER* header, size_t plen, unsigned char* pheader, size_t hlen) {
unsigned char* ansp = skip_questions(header, plen);
/* if packet is malformed, just return as-is. */
if (!ansp) return plen;
if (!(ansp = skip_section(
ansp, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
header, plen)))
return plen;
/* restore pseudoheader */
if (pheader && ntohs(header->arcount) == 0) {
/* must use memmove, may overlap */
memmove(ansp, pheader, hlen);
header->arcount = htons(1);
ansp += hlen;
}
return ansp - (unsigned char*) header;
}
unsigned char* find_pseudoheader(HEADER* header, size_t plen, size_t* len, unsigned char** p,
int* is_sign) {
/* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
also return length of pseudoheader in *len and pointer to the UDP size in *p
Finally, check to see if a packet is signed. If it is we cannot change a single bit before
forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
int i, arcount = ntohs(header->arcount);
unsigned char* ansp = (unsigned char*) (header + 1);
unsigned short rdlen, type, class;
unsigned char* ret = NULL;
if (is_sign) {
*is_sign = 0;
if (header->opcode == QUERY) {
for (i = ntohs(header->qdcount); i != 0; i--) {
if (!(ansp = skip_name(ansp, header, plen, 4))) return NULL;
GETSHORT(type, ansp);
GETSHORT(class, ansp);
if (class == C_IN && type == T_TKEY) *is_sign = 1;
}
}
} else {
if (!(ansp = skip_questions(header, plen))) return NULL;
}
if (arcount == 0) return NULL;
if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
return NULL;
for (i = 0; i < arcount; i++) {
unsigned char *save, *start = ansp;
if (!(ansp = skip_name(ansp, header, plen, 10))) return NULL;
GETSHORT(type, ansp);
save = ansp;
GETSHORT(class, ansp);
ansp += 4; /* TTL */
GETSHORT(rdlen, ansp);
if (!ADD_RDLEN(header, ansp, plen, rdlen)) return NULL;
if (type == T_OPT) {
if (len) *len = ansp - start;
if (p) *p = save;
ret = start;
} else if (is_sign && i == arcount - 1 && class == C_ANY &&
(type == T_SIG || type == T_TSIG))
*is_sign = 1;
}
return ret;
}
/* is addr in the non-globally-routed IP space? */
static int private_net(struct in_addr addr) {
in_addr_t ip_addr = ntohl(addr.s_addr);
return ((ip_addr & 0xFF000000) == 0x7F000000) /* 127.0.0.0/8 (loopback) */ ||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */;
}
static unsigned char* do_doctor(unsigned char* p, int count, HEADER* header, size_t qlen) {
int i, qtype, qclass, rdlen;
unsigned long ttl;
for (i = count; i != 0; i--) {
if (!(p = skip_name(p, header, qlen, 10))) return 0; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
GETLONG(ttl, p);
GETSHORT(rdlen, p);
if ((qclass == C_IN) && (qtype == T_A)) {
struct doctor* doctor;
struct in_addr addr;
if (!CHECK_LEN(header, p, qlen, INADDRSZ)) return 0;
/* alignment */
memcpy(&addr, p, INADDRSZ);
for (doctor = daemon->doctors; doctor; doctor = doctor->next) {
if (doctor->end.s_addr == 0) {
if (!is_same_net(doctor->in, addr, doctor->mask)) continue;
} else if (ntohl(doctor->in.s_addr) > ntohl(addr.s_addr) ||
ntohl(doctor->end.s_addr) < ntohl(addr.s_addr))
continue;
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;
}
}
if (!ADD_RDLEN(header, p, qlen, rdlen)) return 0; /* bad packet */
}
return p;
}
static int find_soa(HEADER* header, size_t qlen) {
unsigned char* p;
int qtype, qclass, rdlen;
unsigned long ttl, minttl = ULONG_MAX;
int i, found_soa = 0;
/* first move to NS section and find TTL from any SOA section */
if (!(p = skip_questions(header, qlen)) ||
!(p = do_doctor(p, ntohs(header->ancount), header, qlen)))
return 0; /* bad packet */
for (i = ntohs(header->nscount); i != 0; i--) {
if (!(p = skip_name(p, header, qlen, 10))) return 0; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
GETLONG(ttl, p);
GETSHORT(rdlen, p);
if ((qclass == C_IN) && (qtype == T_SOA)) {
found_soa = 1;
if (ttl < minttl) minttl = ttl;
/* MNAME */
if (!(p = skip_name(p, header, qlen, 0))) return 0;
/* RNAME */
if (!(p = skip_name(p, header, qlen, 20))) return 0;
p += 16; /* SERIAL REFRESH RETRY EXPIRE */
GETLONG(ttl, p); /* minTTL */
if (ttl < minttl) minttl = ttl;
} else if (!ADD_RDLEN(header, p, qlen, rdlen))
return 0; /* bad packet */
}
/* rewrite addresses in additioal section too */
if (!do_doctor(p, ntohs(header->arcount), header, qlen)) return 0;
if (!found_soa) minttl = daemon->neg_ttl;
return minttl;
}
/* 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
expired and cleaned out that way.
Return 1 if we reject an address because it look like parct of dns-rebinding attack. */
int extract_addresses(HEADER* header, size_t qlen, char* name, time_t now) {
unsigned char *p, *p1, *endrr, *namep;
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
unsigned long ttl = 0;
struct all_addr addr;
cache_start_insert();
/* find_soa is needed for dns_doctor side-effects, so don't call it lazily if there are any. */
if (daemon->doctors) {
searched_soa = 1;
ttl = find_soa(header, qlen);
}
/* go through the questions. */
p = (unsigned char*) (header + 1);
for (i = ntohs(header->qdcount); i != 0; i--) {
int found = 0, cname_count = 5;
struct crec* cpp = NULL;
int flags = header->rcode == NXDOMAIN ? F_NXDOMAIN : 0;
unsigned long cttl = ULONG_MAX, attl;
namep = p;
if (!extract_name(header, qlen, &p, name, 1, 4)) return 0; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
if (qclass != C_IN) continue;
/* PTRs: we chase CNAMEs here, since we have no way to
represent them in the cache. */
if (qtype == T_PTR) {
int name_encoding = in_arpa_name_2_addr(name, &addr);
if (!name_encoding) continue;
if (!(flags & F_NXDOMAIN)) {
cname_loop:
if (!(p1 = skip_questions(header, qlen))) return 0;
for (j = ntohs(header->ancount); j != 0; j--) {
unsigned char* tmp = namep;
/* the loop body overwrites the original name, so get it back here. */
if (!extract_name(header, qlen, &tmp, name, 1, 0) ||
!(res = extract_name(header, qlen, &p1, name, 0, 10)))
return 0; /* bad packet */
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
GETLONG(attl, p1);
GETSHORT(ardlen, p1);
endrr = p1 + ardlen;
/* TTL of record is minimum of CNAMES and PTR */
if (attl < cttl) cttl = attl;
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR)) {
if (!extract_name(header, qlen, &p1, name, 1, 0)) return 0;
if (aqtype == T_CNAME) {
if (!cname_count--) return 0; /* looped CNAMES */
goto cname_loop;
}
cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE);
found = 1;
}
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0)) return 0; /* bad packet */
}
}
if (!found && !(daemon->options & OPT_NO_NEG)) {
if (!searched_soa) {
searched_soa = 1;
ttl = find_soa(header, qlen);
}
if (ttl)
cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);
}
} else {
/* everything other than PTR */
struct crec* newc;
int addrlen;
if (qtype == T_A) {
addrlen = INADDRSZ;
flags |= F_IPV4;
}
#ifdef HAVE_IPV6
else if (qtype == T_AAAA) {
addrlen = IN6ADDRSZ;
flags |= F_IPV6;
}
#endif
else
continue;
if (!(flags & F_NXDOMAIN)) {
cname_loop1:
if (!(p1 = skip_questions(header, qlen))) return 0;
for (j = ntohs(header->ancount); j != 0; j--) {
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
return 0; /* bad packet */
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
GETLONG(attl, p1);
GETSHORT(ardlen, p1);
endrr = p1 + ardlen;
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype)) {
if (aqtype == T_CNAME) {
if (!cname_count--) return 0; /* looped CNAMES */
newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
if (newc && cpp) {
cpp->addr.cname.cache = newc;
cpp->addr.cname.uid = newc->uid;
}
cpp = newc;
if (attl < cttl) cttl = attl;
if (!extract_name(header, qlen, &p1, name, 1, 0)) return 0;
goto cname_loop1;
} else {
found = 1;
/* copy address into aligned storage */
if (!CHECK_LEN(header, p1, qlen, addrlen)) return 0; /* bad packet */
memcpy(&addr, p1, addrlen);
/* check for returned address in private space */
if ((daemon->options & OPT_NO_REBIND) && (flags & F_IPV4) &&
private_net(addr.addr.addr4))
return 1;
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
if (newc && cpp) {
cpp->addr.cname.cache = newc;
cpp->addr.cname.uid = newc->uid;
}
cpp = NULL;
}
}
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0)) return 0; /* bad packet */
}
}
if (!found && !(daemon->options & OPT_NO_NEG)) {
if (!searched_soa) {
searched_soa = 1;
ttl = find_soa(header, qlen);
}
/* If there's no SOA to get the TTL from, but there is a CNAME
pointing at this, inherit its TTL */
if (ttl || cpp) {
newc =
cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);
if (newc && cpp) {
cpp->addr.cname.cache = newc;
cpp->addr.cname.uid = newc->uid;
}
}
}
}
}
/* Don't put stuff from a truncated packet into the cache, but do everything else */
if (!header->tc) cache_end_insert();
return 0;
}
/* If the packet holds exactly one query
return F_IPV4 or F_IPV6 and leave the name from the query in name.
Abuse F_BIGNAME to indicate an NS query - yuck. */
unsigned short extract_request(HEADER* header, size_t qlen, char* name, unsigned short* typep) {
unsigned char* p = (unsigned char*) (header + 1);
int qtype, qclass;
if (typep) *typep = 0;
if (ntohs(header->qdcount) != 1 || header->opcode != QUERY)
return 0; /* must be exactly one query. */
if (!extract_name(header, qlen, &p, name, 1, 4)) return 0; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
if (typep) *typep = qtype;
if (qclass == C_IN) {
if (qtype == T_A) return F_IPV4;
if (qtype == T_AAAA) return F_IPV6;
if (qtype == T_ANY) return F_IPV4 | F_IPV6;
if (qtype == T_NS || qtype == T_SOA) return F_QUERY | F_BIGNAME;
}
return F_QUERY;
}
size_t setup_reply(HEADER* header, size_t qlen, struct all_addr* addrp, unsigned short flags,
unsigned long ttl) {
unsigned char* p;
if (!(p = skip_questions(header, qlen))) return 0;
header->qr = 1; /* response */
header->aa = 0; /* authoritive */
header->ra = 1; /* recursion if available */
header->tc = 0; /* not truncated */
header->nscount = htons(0);
header->arcount = htons(0);
header->ancount = htons(0); /* no answers unless changed below */
if (flags == F_NEG)
header->rcode = SERVFAIL; /* couldn't get memory */
else if (flags == F_NOERR)
header->rcode = NOERROR; /* empty domain */
else if (flags == F_NXDOMAIN)
header->rcode = NXDOMAIN;
else if (flags == F_IPV4) { /* we know the address */
header->rcode = NOERROR;
header->ancount = htons(1);
header->aa = 1;
add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_A, C_IN, "4",
addrp);
}
#ifdef HAVE_IPV6
else if (flags == F_IPV6) {
header->rcode = NOERROR;
header->ancount = htons(1);
header->aa = 1;
add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_AAAA, C_IN, "6",
addrp);
}
#endif
else /* nowhere to forward to */
header->rcode = REFUSED;
return p - (unsigned char*) header;
}
/* 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 crec* crecp;
struct mx_srv_record* mx;
struct txt_record* txt;
struct interface_name* intr;
struct ptr_record* ptr;
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
(crecp->flags & (F_HOSTS | F_DHCP)))
return 1;
for (mx = daemon->mxnames; mx; mx = mx->next)
if (hostname_isequal(name, mx->name)) return 1;
for (txt = daemon->txt; txt; txt = txt->next)
if (hostname_isequal(name, txt->name)) return 1;
for (intr = daemon->int_names; intr; intr = intr->next)
if (hostname_isequal(name, intr->name)) return 1;
for (ptr = daemon->ptr; ptr; ptr = ptr->next)
if (hostname_isequal(name, ptr->name)) return 1;
return 0;
}
/* Is the packet a reply with the answer address equal to addr?
If so mung is into an NXDOMAIN reply and also put that information
in the cache. */
int check_for_bogus_wildcard(HEADER* header, size_t qlen, char* name, struct bogus_addr* baddr,
time_t now) {
unsigned char* p;
int i, qtype, qclass, rdlen;
unsigned long ttl;
struct bogus_addr* baddrp;
/* skip over questions */
if (!(p = skip_questions(header, qlen))) return 0; /* bad packet */
for (i = ntohs(header->ancount); i != 0; i--) {
if (!extract_name(header, qlen, &p, name, 1, 10)) return 0; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
GETLONG(ttl, p);
GETSHORT(rdlen, p);
if (qclass == C_IN && qtype == T_A) {
if (!CHECK_LEN(header, p, qlen, INADDRSZ)) return 0;
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
if (memcmp(&baddrp->addr, p, INADDRSZ) == 0) {
/* Found a bogus address. Insert that info here, since there no SOA record
to get the ttl from in the normal processing */
cache_start_insert();
cache_insert(name, NULL, now, ttl,
F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
cache_end_insert();
return 1;
}
}
if (!ADD_RDLEN(header, p, qlen, rdlen)) return 0;
}
return 0;
}
static int add_resource_record(HEADER* header, char* limit, int* truncp, unsigned int nameoffset,
unsigned char** pp, unsigned long ttl, unsigned int* offset,
unsigned short type, unsigned short class, char* format, ...) {
va_list ap;
unsigned char *sav, *p = *pp;
int j;
unsigned short usval;
long lval;
char* sval;
#define CHECK_LIMIT(size) \
if (limit && p + (size) > (unsigned char*)limit) { \
va_end(ap); \
goto truncated; \
}
if (truncp && *truncp) return 0;
va_start(ap, format); /* make ap point to 1st unamed argument */
/* nameoffset (1 or 2) + type (2) + class (2) + ttl (4) + 0 (2) */
CHECK_LIMIT(12);
PUTSHORT(nameoffset | 0xc000, p);
PUTSHORT(type, p);
PUTSHORT(class, p);
PUTLONG(ttl, p); /* TTL */
sav = p; /* Save pointer to RDLength field */
PUTSHORT(0, p); /* Placeholder RDLength */
for (; *format; format++) switch (*format) {
#ifdef HAVE_IPV6
case '6':
CHECK_LIMIT(IN6ADDRSZ);
sval = va_arg(ap, char*);
memcpy(p, sval, IN6ADDRSZ);
p += IN6ADDRSZ;
break;
#endif
case '4':
CHECK_LIMIT(INADDRSZ);
sval = va_arg(ap, char*);
memcpy(p, sval, INADDRSZ);
p += INADDRSZ;
break;
case 's':
CHECK_LIMIT(2);
usval = va_arg(ap, int);
PUTSHORT(usval, p);
break;
case 'l':
CHECK_LIMIT(4);
lval = va_arg(ap, long);
PUTLONG(lval, p);
break;
case 'd':
/* get domain-name answer arg and store it in RDATA field */
if (offset) *offset = p - (unsigned char*) header;
p = do_rfc1035_name(p, va_arg(ap, char*), limit);
if (!p) {
va_end(ap);
goto truncated;
}
CHECK_LIMIT(1);
*p++ = 0;
break;
case 't':
usval = va_arg(ap, int);
CHECK_LIMIT(usval);
sval = va_arg(ap, char*);
memcpy(p, sval, usval);
p += usval;
break;
case 'z':
sval = va_arg(ap, char*);
usval = sval ? strlen(sval) : 0;
if (usval > 255) usval = 255;
CHECK_LIMIT(usval + 1);
*p++ = (unsigned char) usval;
memcpy(p, sval, usval);
p += usval;
break;
}
#undef CHECK_LIMIT
va_end(ap); /* clean up variable argument pointer */
j = p - sav - 2;
/* this has already been checked against limit before */
PUTSHORT(j, sav); /* Now, store real RDLength */
/* check for overflow of buffer */
if (limit && ((unsigned char*) limit - p) < 0) {
truncated:
if (truncp) *truncp = 1;
return 0;
}
*pp = p;
return 1;
}
static unsigned long crec_ttl(struct crec* crecp, time_t now) {
/* Return 0 ttl for DHCP entries, which might change
before the lease expires. */
if (crecp->flags & (F_IMMORTAL | F_DHCP)) return daemon->local_ttl;
return crecp->ttd - now;
}
/* 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 in_addr local_addr,
struct in_addr local_netmask, time_t now) {
char* name = daemon->namebuff;
unsigned char *p, *ansp, *pheader;
int qtype, qclass;
struct all_addr addr;
unsigned int nameoffset;
unsigned short flag;
int q, ans, anscount = 0, addncount = 0;
int dryrun = 0, sec_reqd = 0;
int is_sign;
struct crec* crecp;
int nxdomain = 0, auth = 1, trunc = 0;
struct mx_srv_record* rec;
// Make sure we do not underflow here too.
if (qlen > (size_t)(limit - ((char*) header))) return 0;
/* If there is an RFC2671 pseudoheader then it will be overwritten by
partial replies, so we have to do a dry run to see if we can answer
the query. We check to see if the do bit is set, if so we always
forward rather than answering from the cache, which doesn't include
security information. */
if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign)) {
unsigned short udpsz, ext_rcode, flags;
unsigned char* psave = pheader;
GETSHORT(udpsz, pheader);
GETSHORT(ext_rcode, pheader);
GETSHORT(flags, pheader);
sec_reqd = flags & 0x8000; /* do bit */
/* If our client is advertising a larger UDP packet size
than we allow, trim it so that we don't get an overlarge
response from upstream */
if (!is_sign && (udpsz > daemon->edns_pktsz)) PUTSHORT(daemon->edns_pktsz, psave);
dryrun = 1;
}
if (ntohs(header->qdcount) == 0 || header->opcode != QUERY) return 0;
for (rec = daemon->mxnames; rec; rec = rec->next) rec->offset = 0;
rerun:
/* determine end of question section (we put answers there) */
if (!(ansp = skip_questions(header, qlen))) return 0; /* bad packet */
/* now process each question, answers go in RRs after the question */
p = (unsigned char*) (header + 1);
for (q = ntohs(header->qdcount); q != 0; q--) {
/* save pointer to name for copying into answers */
nameoffset = p - (unsigned char*) header;
/* now extract name as .-concatenated string into name */
if (!extract_name(header, qlen, &p, name, 1, 4)) return 0; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
ans = 0; /* have we answered this question */
if (qtype == T_TXT || qtype == T_ANY) {
struct txt_record* t;
for (t = daemon->txt; 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, "");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL, T_TXT, t->class, "t",
t->len, t->txt))
anscount++;
}
}
}
}
if (qclass == C_IN) {
if (qtype == T_PTR || qtype == T_ANY) {
/* see if it's w.z.y.z.in-addr.arpa format */
int is_arpa = in_arpa_name_2_addr(name, &addr);
struct ptr_record* ptr;
struct interface_name* intr = NULL;
for (ptr = daemon->ptr; ptr; ptr = ptr->next)
if (hostname_isequal(name, ptr->name)) break;
if (is_arpa == F_IPV4)
for (intr = daemon->int_names; intr; intr = intr->next) {
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) {
ans = 1;
if (!dryrun) {
log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL, T_PTR, C_IN, "d",
intr->name))
anscount++;
}
} else if (ptr) {
ans = 1;
if (!dryrun) {
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "");
for (ptr = daemon->ptr; ptr; ptr = ptr->next)
if (hostname_isequal(name, ptr->name) &&
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL, T_PTR, C_IN, "d",
ptr->ptr))
anscount++;
}
} else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
do {
/* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP))) continue;
if (crecp->flags & F_NEG) {
ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN) nxdomain = 1;
if (!dryrun) log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
} else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) {
ans = 1;
if (!(crecp->flags & (F_HOSTS | F_DHCP))) auth = 0;
if (!dryrun) {
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), NULL, T_PTR, C_IN,
"d", cache_get_name(crecp)))
anscount++;
}
}
} while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
else if (is_arpa == F_IPV4 && (daemon->options & OPT_BOGUSPRIV) &&
private_net(addr.addr.addr4)) {
/* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
ans = 1;
nxdomain = 1;
if (!dryrun)
log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, name, &addr,
NULL);
}
}
for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0) {
unsigned short type = T_A;
if (flag == F_IPV6)
#ifdef HAVE_IPV6
type = T_AAAA;
#else
break;
#endif
if (qtype != type && qtype != T_ANY) continue;
/* Check for "A for A" queries */
if (qtype == T_A && (addr.addr.addr4.s_addr = inet_addr(name)) != (in_addr_t) -1) {
ans = 1;
if (!dryrun) {
log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL, type, C_IN, "4", &addr))
anscount++;
}
continue;
}
/* interface name stuff */
if (qtype == T_A) {
struct interface_name* intr;
for (intr = daemon->int_names; intr; intr = intr->next)
if (hostname_isequal(name, intr->name)) break;
if (intr) {
ans = 1;
if (!dryrun) {
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, NULL);
else {
log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL, type, C_IN, "4",
&addr))
anscount++;
}
}
continue;
}
}
cname_restart:
if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME))) {
int localise = 0;
/* See if a putative address is on the network from which we recieved
the query, is so we'll filter other answers. */
if (local_addr.s_addr != 0 && (daemon->options & OPT_LOCALISE) &&
flag == F_IPV4) {
struct crec* save = crecp;
do {
if ((crecp->flags & F_HOSTS) &&
is_same_net(*((struct in_addr*) &crecp->addr), local_addr,
local_netmask)) {
localise = 1;
break;
}
} while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
crecp = save;
}
do {
/* don't answer wildcard queries with data not from /etc/hosts
or DHCP leases */
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP))) break;
if (crecp->flags & F_CNAME) {
if (!dryrun) {
log_query(crecp->flags, name, NULL, record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset, T_CNAME,
C_IN, "d",
cache_get_name(crecp->addr.cname.cache)))
anscount++;
}
strcpy(name, cache_get_name(crecp->addr.cname.cache));
goto cname_restart;
}
if (crecp->flags & F_NEG) {
ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN) nxdomain = 1;
if (!dryrun) log_query(crecp->flags, name, NULL, NULL);
} else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) {
/* If we are returning local answers depending on network,
filter here. */
if (localise && (crecp->flags & F_HOSTS) &&
!is_same_net(*((struct in_addr*) &crecp->addr), local_addr,
local_netmask))
continue;
if (!(crecp->flags & (F_HOSTS | F_DHCP))) auth = 0;
ans = 1;
if (!dryrun) {
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), NULL, type, C_IN,
type == T_A ? "4" : "6", &crecp->addr))
anscount++;
}
}
} while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
}
}
if (qtype == T_MX || qtype == T_ANY) {
int found = 0;
for (rec = daemon->mxnames; rec; rec = rec->next)
if (!rec->issrv && hostname_isequal(name, rec->name)) {
ans = found = 1;
if (!dryrun) {
unsigned int offset;
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL,
"");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, &offset, T_MX, C_IN, "sd",
rec->weight, rec->target)) {
anscount++;
if (rec->target) rec->offset = offset;
}
}
}
if (!found && (daemon->options & (OPT_SELFMX | OPT_LOCALMX)) &&
cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP)) {
ans = 1;
if (!dryrun) {
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "");
if (add_resource_record(
header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL,
T_MX, C_IN, "sd", 1,
(daemon->options & OPT_SELFMX) ? name : daemon->mxtarget))
anscount++;
}
}
}
if (qtype == T_SRV || qtype == T_ANY) {
int found = 0;
for (rec = daemon->mxnames; rec; rec = rec->next)
if (rec->issrv && hostname_isequal(name, rec->name)) {
found = ans = 1;
if (!dryrun) {
unsigned int offset;
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL,
"");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, &offset, T_SRV, C_IN, "sssd",
rec->priority, rec->weight, rec->srvport,
rec->target)) {
anscount++;
if (rec->target) rec->offset = offset;
}
}
}
if (!found && (daemon->options & OPT_FILTER) &&
(qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) {
ans = 1;
if (!dryrun) log_query(F_CONFIG | F_NEG, name, NULL, NULL);
}
}
if (qtype == T_NAPTR || qtype == T_ANY) {
struct naptr* na;
for (na = daemon->naptr; na; na = na->next)
if (hostname_isequal(name, na->name)) {
ans = 1;
if (!dryrun) {
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL,
"");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL, T_NAPTR, C_IN,
"sszzzd", na->order, na->pref, na->flags,
na->services, na->regexp, na->replace))
anscount++;
}
}
}
if (qtype == T_MAILB) ans = 1, nxdomain = 1;
if (qtype == T_SOA && (daemon->options & OPT_FILTER)) {
ans = 1;
if (!dryrun) log_query(F_CONFIG | F_NEG, name, &addr, NULL);
}
}
if (!ans) return 0; /* failed to answer a question */
}
if (dryrun) {
dryrun = 0;
goto rerun;
}
/* create an additional data section, for stuff in SRV and MX record replies. */
for (rec = daemon->mxnames; rec; rec = rec->next)
if (rec->offset != 0) {
/* squash dupes */
struct mx_srv_record* tmp;
for (tmp = rec->next; tmp; tmp = tmp->next)
if (tmp->offset != 0 && hostname_isequal(rec->target, tmp->target)) tmp->offset = 0;
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6))) {
#ifdef HAVE_IPV6
int type = crecp->flags & F_IPV4 ? T_A : T_AAAA;
#else
int type = T_A;
#endif
if (crecp->flags & F_NEG) continue;
if (add_resource_record(header, limit, NULL, rec->offset, &ansp,
crec_ttl(crecp, now), NULL, type, C_IN,
crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr))
addncount++;
}
}
/* done all questions, set up header and return length of result */
header->qr = 1; /* response */
header->aa = auth; /* authoritive - only hosts and DHCP derived names. */
header->ra = 1; /* recursion if available */
header->tc = trunc; /* truncation */
if (anscount == 0 && nxdomain)
header->rcode = NXDOMAIN;
else
header->rcode = NOERROR; /* no error */
header->ancount = htons(anscount);
header->nscount = htons(0);
header->arcount = htons(addncount);
return ansp - (unsigned char*) header;
}
./src/NOTICE 0000644 0000000 0000000 00000043127 14256750714 011426 0 ustar root root GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C) 19yy
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; either version 2 of the License, or
(at your option) any later version.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
./src/cache.c 0000644 0000000 0000000 00000113404 14256750714 011725 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "dnsmasq.h"
static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL;
#ifdef HAVE_DHCP
static struct crec* dhcp_spare = NULL;
#endif
static struct crec* new_chain = NULL;
static int cache_inserted = 0, cache_live_freed = 0, insert_error;
static union bigname* big_free = NULL;
static int bignames_left, hash_size;
static int uid = 0;
static char* addrbuff = NULL;
/* 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"}, {35, "NAPTR"}, {36, "KX"}, {37, "CERT"}, {38, "A6"},
{39, "DNAME"}, {41, "OPT"}, {48, "DNSKEY"}, {249, "TKEY"}, {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);
static void rehash(int size);
static void cache_hash(struct crec* crecp);
void cache_init(void) {
struct crec* crecp;
int i;
if (daemon->options & OPT_LOG) addrbuff = safe_malloc(ADDRSTRLEN);
bignames_left = daemon->cachesize / 10;
if (daemon->cachesize > 0) {
crecp = safe_malloc(daemon->cachesize * sizeof(struct crec));
for (i = 0; i < daemon->cachesize; i++, crecp++) {
cache_link(crecp);
crecp->flags = 0;
crecp->uid = uid++;
}
}
/* create initial hash table*/
rehash(daemon->cachesize);
}
/* In most cases, we create the hash table once here by calling this with (hash_table == NULL)
but if the hosts file(s) are big (some people have 50000 ad-block entries), the table
will be much too small, so the hosts reading code calls rehash every 1000 addresses, to
expand the table. */
static void rehash(int size) {
struct crec** new, **old, *p, *tmp;
int i, new_size, old_size;
/* hash_size is a power of two. */
for (new_size = 64; new_size < size / 10; new_size = new_size << 1)
;
/* must succeed in getting first instance, failure later is non-fatal */
if (!hash_table)
new = safe_malloc(new_size * sizeof(struct crec*));
else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec*))))
return;
for (i = 0; i < new_size; i++) new[i] = NULL;
old = hash_table;
old_size = hash_size;
hash_table = new;
hash_size = new_size;
if (old) {
for (i = 0; i < old_size; i++)
for (p = old[i]; p; p = tmp) {
tmp = p->hash_next;
cache_hash(p);
}
free(old);
}
}
static struct crec** hash_bucket(char* name) {
unsigned int c, val = 017465; /* Barker code - minimum self-correlation in cyclic shift */
const unsigned char* mix_tab = (const unsigned char*) typestr;
while ((c = (unsigned char) *name++)) {
/* 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) & 0x3F] ^ c);
}
/* hash_size is a power of two */
return hash_table + ((val ^ (val >> 16)) & (hash_size - 1));
}
static void cache_hash(struct crec* crecp) {
/* maintain an invariant that all entries with F_REVERSE set
are at the start of the hash-chain and all non-reverse
immortal entries are at the end of the hash-chain.
This allows reverse searches and garbage collection to be optimised */
struct crec** up = hash_bucket(cache_get_name(crecp));
if (!(crecp->flags & F_REVERSE)) {
while (*up && ((*up)->flags & F_REVERSE)) up = &((*up)->hash_next);
if (crecp->flags & F_IMMORTAL)
while (*up && !((*up)->flags & F_IMMORTAL)) up = &((*up)->hash_next);
}
crecp->hash_next = *up;
*up = crecp;
}
static void cache_free(struct crec* crecp) {
crecp->flags &= ~F_FORWARD;
crecp->flags &= ~F_REVERSE;
crecp->uid = uid++; /* invalidate CNAMES pointing to this. */
if (cache_tail)
cache_tail->next = crecp;
else
cache_head = crecp;
crecp->prev = cache_tail;
crecp->next = NULL;
cache_tail = crecp;
/* retrieve big name for further use. */
if (crecp->flags & F_BIGNAME) {
crecp->name.bname->next = big_free;
big_free = crecp->name.bname;
crecp->flags &= ~F_BIGNAME;
}
}
/* insert a new cache entry at the head of the list (youngest entry) */
static void cache_link(struct crec* crecp) {
if (cache_head) /* check needed for init code */
cache_head->prev = crecp;
crecp->next = cache_head;
crecp->prev = NULL;
cache_head = crecp;
if (!cache_tail) cache_tail = crecp;
}
/* remove an arbitrary cache entry for promotion */
static void cache_unlink(struct crec* crecp) {
if (crecp->prev)
crecp->prev->next = crecp->next;
else
cache_head = crecp->next;
if (crecp->next)
crecp->next->prev = crecp->prev;
else
cache_tail = crecp->prev;
}
char* cache_get_name(struct crec* crecp) {
if (crecp->flags & F_BIGNAME)
return crecp->name.bname->name;
else if (crecp->flags & (F_DHCP | F_CONFIG))
return crecp->name.namep;
return crecp->name.sname;
}
static int is_outdated_cname_pointer(struct crec* crecp) {
if (!(crecp->flags & F_CNAME)) return 0;
if (crecp->addr.cname.cache && crecp->addr.cname.uid == crecp->addr.cname.cache->uid) return 0;
return 1;
}
static int is_expired(time_t now, struct crec* crecp) {
if (crecp->flags & F_IMMORTAL) return 0;
if (difftime(now, crecp->ttd) < 0) return 0;
return 1;
}
static int cache_scan_free(char* name, struct all_addr* addr, time_t now, unsigned short flags) {
/* Scan and remove old entries.
If (flags & F_FORWARD) then remove any forward entries for name and any expired
entries but only in the same hash bucket as name.
If (flags & F_REVERSE) then remove any reverse entries for addr and any expired
entries in the whole cache.
If (flags == 0) remove any expired entries in the whole cache.
In the flags & F_FORWARD case, the return code is valid, and returns zero if the
name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
We take advantage of the fact that hash chains have stuff in the order
,, so that when we hit an entry which isn't reverse and is
immortal, we're done. */
struct crec *crecp, **up;
if (flags & F_FORWARD) {
for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp)) {
*up = crecp->hash_next;
if (!(crecp->flags & (F_HOSTS | F_DHCP))) {
cache_unlink(crecp);
cache_free(crecp);
}
} else if ((crecp->flags & F_FORWARD) &&
((flags & crecp->flags & (F_IPV4 | F_IPV6)) ||
((crecp->flags | flags) & F_CNAME)) &&
hostname_isequal(cache_get_name(crecp), name)) {
if (crecp->flags & (F_HOSTS | F_DHCP)) return 0;
*up = crecp->hash_next;
cache_unlink(crecp);
cache_free(crecp);
} else
up = &crecp->hash_next;
} else {
int i;
#ifdef HAVE_IPV6
int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;
#else
int addrlen = INADDRSZ;
#endif
for (i = 0; i < hash_size; i++)
for (crecp = hash_table[i], up = &hash_table[i];
crecp && ((crecp->flags & F_REVERSE) || !(crecp->flags & F_IMMORTAL));
crecp = crecp->hash_next)
if (is_expired(now, crecp)) {
*up = crecp->hash_next;
if (!(crecp->flags & (F_HOSTS | F_DHCP))) {
cache_unlink(crecp);
cache_free(crecp);
}
} else if (!(crecp->flags & (F_HOSTS | F_DHCP)) &&
(flags & crecp->flags & F_REVERSE) &&
(flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
memcmp(&crecp->addr.addr, addr, addrlen) == 0) {
*up = crecp->hash_next;
cache_unlink(crecp);
cache_free(crecp);
} else
up = &crecp->hash_next;
}
return 1;
}
/* Note: The normal calling sequence is
cache_start_insert
cache_insert * n
cache_end_insert
but an abort can cause the cache_end_insert to be missed
in which can the next cache_start_insert cleans things up. */
void cache_start_insert(void) {
/* Free any entries which didn't get committed during the last
insert due to error.
*/
while (new_chain) {
struct crec* tmp = new_chain->next;
cache_free(new_chain);
new_chain = tmp;
}
new_chain = NULL;
insert_error = 0;
}
struct crec* cache_insert(char* name, struct all_addr* addr, time_t now, unsigned long ttl,
unsigned short flags) {
struct crec* new;
union bigname* big_name = NULL;
int freed_all = flags & F_REVERSE;
int free_avail = 0;
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* CONFIG bit means something else when stored in cache entries */
flags &= ~F_CONFIG;
/* if previous insertion failed give up now. */
if (insert_error) return NULL;
/* First remove any expired entries and entries for the name/address we
are currently inserting. Fail is we attempt to delete a name from
/etc/hosts or DHCP. */
if (!cache_scan_free(name, addr, now, flags)) {
insert_error = 1;
return NULL;
}
/* Now get a cache entry from the end of the LRU list */
while (1) {
if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
{
insert_error = 1;
return NULL;
}
/* End of LRU list is still in use: if we didn't scan all the hash
chains for expired entries do that now. If we already tried that
then it's time to start spilling things. */
if (new->flags&(F_FORWARD | F_REVERSE)) {
/* If free_avail set, we believe that an entry has been freed.
Bugs have been known to make this not true, resulting in
a tight loop here. If that happens, abandon the
insert. Once in this state, all inserts will probably fail. */
if (free_avail) {
insert_error = 1;
return NULL;
}
if (freed_all) {
free_avail = 1; /* Must be free space now. */
cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags);
cache_live_freed++;
} else {
cache_scan_free(NULL, NULL, now, 0);
freed_all = 1;
}
continue;
}
/* Check if we need to and can allocate extra memory for a long name.
If that fails, give up now. */
if (name && (strlen(name) > SMALLDNAME - 1)) {
if (big_free) {
big_name = big_free;
big_free = big_free->next;
} else if (!bignames_left ||
!(big_name = (union bigname*) whine_malloc(sizeof(union bigname)))) {
insert_error = 1;
return NULL;
} else
bignames_left--;
}
/* Got the rest: finally grab entry. */
cache_unlink(new);
break;
}
new->flags = flags;
if (big_name) {
new->name.bname = big_name;
new->flags |= F_BIGNAME;
}
if (name)
strcpy(cache_get_name(new), name);
else
*cache_get_name(new) = 0;
if (addr)
new->addr.addr = *addr;
else
new->addr.cname.cache = NULL;
new->ttd = now + (time_t) ttl;
new->next = new_chain;
new_chain = new;
return new;
}
/* after end of insertion, commit the new entries */
void cache_end_insert(void) {
if (insert_error) return;
while (new_chain) {
struct crec* tmp = new_chain->next;
/* drop CNAMEs which didn't find a target. */
if (is_outdated_cname_pointer(new_chain))
cache_free(new_chain);
else {
cache_hash(new_chain);
cache_link(new_chain);
cache_inserted++;
}
new_chain = tmp;
}
new_chain = NULL;
}
struct crec* cache_find_by_name(struct crec* crecp, char* name, time_t now, unsigned short prot) {
struct crec* ans;
if (crecp) /* iterating */
ans = crecp->next;
else {
/* first search, look for relevant entries and push to top of list
also free anything which has expired */
struct crec *next, **up, **insert = NULL, **chainp = &ans;
int ins_flags = 0;
for (up = hash_bucket(name), crecp = *up; crecp; crecp = next) {
next = crecp->hash_next;
if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp)) {
if ((crecp->flags & F_FORWARD) && (crecp->flags & prot) &&
hostname_isequal(cache_get_name(crecp), name)) {
if (crecp->flags & (F_HOSTS | F_DHCP)) {
*chainp = crecp;
chainp = &crecp->next;
} else {
cache_unlink(crecp);
cache_link(crecp);
}
/* Move all but the first entry up the hash chain
this implements round-robin.
Make sure that re-ordering doesn't break the hash-chain
order invariants.
*/
if (insert && (crecp->flags & (F_REVERSE | F_IMMORTAL)) == ins_flags) {
*up = crecp->hash_next;
crecp->hash_next = *insert;
*insert = crecp;
insert = &crecp->hash_next;
} else {
if (!insert) {
insert = up;
ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL);
}
up = &crecp->hash_next;
}
} else
/* case : not expired, incorrect entry. */
up = &crecp->hash_next;
} else {
/* expired entry, free it */
*up = crecp->hash_next;
if (!(crecp->flags & (F_HOSTS | F_DHCP))) {
cache_unlink(crecp);
cache_free(crecp);
}
}
}
*chainp = cache_head;
}
if (ans && (ans->flags & F_FORWARD) && (ans->flags & prot) &&
hostname_isequal(cache_get_name(ans), name))
return ans;
return NULL;
}
struct crec* cache_find_by_addr(struct crec* crecp, struct all_addr* addr, time_t now,
unsigned short prot) {
struct crec* ans;
#ifdef HAVE_IPV6
int addrlen = (prot == F_IPV6) ? IN6ADDRSZ : INADDRSZ;
#else
int addrlen = INADDRSZ;
#endif
if (crecp) /* iterating */
ans = crecp->next;
else {
/* first search, look for relevant entries and push to top of list
also free anything which has expired. All the reverse entries are at the
start of the hash chain, so we can give up when we find the first
non-REVERSE one. */
int i;
struct crec **up, **chainp = &ans;
for (i = 0; i < hash_size; i++)
for (crecp = hash_table[i], up = &hash_table[i]; crecp && (crecp->flags & F_REVERSE);
crecp = crecp->hash_next)
if (!is_expired(now, crecp)) {
if ((crecp->flags & prot) && memcmp(&crecp->addr.addr, addr, addrlen) == 0) {
if (crecp->flags & (F_HOSTS | F_DHCP)) {
*chainp = crecp;
chainp = &crecp->next;
} else {
cache_unlink(crecp);
cache_link(crecp);
}
}
up = &crecp->hash_next;
} else {
*up = crecp->hash_next;
if (!(crecp->flags & (F_HOSTS | F_DHCP))) {
cache_unlink(crecp);
cache_free(crecp);
}
}
*chainp = cache_head;
}
if (ans && (ans->flags & F_REVERSE) && (ans->flags & prot) &&
memcmp(&ans->addr.addr, addr, addrlen) == 0)
return ans;
return NULL;
}
static void add_hosts_entry(struct crec* cache, struct all_addr* addr, int addrlen,
unsigned short flags, int index, int addr_dup) {
struct crec* lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
int i, nameexists = 0;
struct cname* a;
/* Remove duplicates in hosts files. */
if (lookup && (lookup->flags & F_HOSTS)) {
nameexists = 1;
if (memcmp(&lookup->addr.addr, addr, addrlen) == 0) {
free(cache);
return;
}
}
/* Ensure there is only one address -> name mapping (first one trumps)
We do this by steam here, first we see if the address is the same as
the last one we saw, which eliminates most in the case of an ad-block
file with thousands of entries for the same address.
Then we search and bail at the first matching address that came from
a HOSTS file. Since the first host entry gets reverse, we know
then that it must exist without searching exhaustively for it. */
if (addr_dup)
flags &= ~F_REVERSE;
else
for (i = 0; i < hash_size; i++) {
for (lookup = hash_table[i]; lookup; lookup = lookup->hash_next)
if ((lookup->flags & F_HOSTS) && (lookup->flags & flags & (F_IPV4 | F_IPV6)) &&
memcmp(&lookup->addr.addr, addr, addrlen) == 0) {
flags &= ~F_REVERSE;
break;
}
if (lookup) break;
}
cache->flags = flags;
cache->uid = index;
memcpy(&cache->addr.addr, addr, addrlen);
cache_hash(cache);
/* don't need to do alias stuff for second and subsequent addresses. */
if (!nameexists)
for (a = daemon->cnames; a; a = a->next)
if (hostname_isequal(cache->name.sname, a->target) &&
(lookup = whine_malloc(sizeof(struct crec)))) {
lookup->flags = F_FORWARD | F_IMMORTAL | F_CONFIG | F_HOSTS | F_CNAME;
lookup->name.namep = a->alias;
lookup->addr.cname.cache = cache;
lookup->addr.cname.uid = index;
cache_hash(lookup);
}
}
static int eatspace(FILE* f) {
int c, nl = 0;
while (1) {
if ((c = getc(f)) == '#')
while (c != '\n' && c != EOF) c = getc(f);
if (c == EOF) return 1;
if (!isspace(c)) {
ungetc(c, f);
return nl;
}
if (c == '\n') nl = 1;
}
}
static int gettok(FILE* f, char* token) {
int c, count = 0;
while (1) {
if ((c = getc(f)) == EOF) return (count == 0) ? EOF : 1;
if (isspace(c) || c == '#') {
ungetc(c, f);
return eatspace(f);
}
if (count < (MAXDNAME - 1)) {
token[count++] = c;
token[count] = 0;
}
}
}
static int read_hostsfile(char* filename, int index, int cache_size) {
FILE* f = fopen(filename, "r");
char *token = daemon->namebuff, *domain_suffix = NULL;
int addr_count = 0, name_count = cache_size, lineno = 0;
unsigned short flags = 0, saved_flags = 0;
struct all_addr addr, saved_addr;
int atnl, addrlen = 0, addr_dup;
if (!f) {
my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno));
return 0;
}
eatspace(f);
while ((atnl = gettok(f, token)) != EOF) {
addr_dup = 0;
lineno++;
#ifdef HAVE_IPV6
if (inet_pton(AF_INET, token, &addr) > 0) {
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
addrlen = INADDRSZ;
domain_suffix = get_domain(addr.addr.addr4);
} else if (inet_pton(AF_INET6, token, &addr) > 0) {
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
addrlen = IN6ADDRSZ;
domain_suffix = daemon->domain_suffix;
}
#else
if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1) {
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
addrlen = INADDRSZ;
domain_suffix = get_domain(addr.addr.addr4);
}
#endif
else {
my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno);
while (atnl == 0) atnl = gettok(f, token);
continue;
}
if (saved_flags == flags && memcmp(&addr, &saved_addr, addrlen) == 0)
addr_dup = 1;
else {
saved_flags = flags;
saved_addr = addr;
}
addr_count++;
/* rehash every 1000 names. */
if ((name_count - cache_size) > 1000) {
rehash(name_count);
cache_size = name_count;
}
while (atnl == 0) {
struct crec* cache;
int fqdn, nomem;
char* canon;
if ((atnl = gettok(f, token)) == EOF) break;
fqdn = !!strchr(token, '.');
if ((canon = canonicalise(token, &nomem))) {
/* If set, add a version of the name with a default domain appended */
if ((daemon->options & OPT_EXPAND) && domain_suffix && !fqdn &&
(cache = whine_malloc(sizeof(struct crec) + strlen(canon) + 2 +
strlen(domain_suffix) - SMALLDNAME))) {
strcpy(cache->name.sname, canon);
strcat(cache->name.sname, ".");
strcat(cache->name.sname, domain_suffix);
add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
addr_dup = 1;
name_count++;
}
if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon) + 1 - SMALLDNAME))) {
strcpy(cache->name.sname, canon);
add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
name_count++;
}
free(canon);
} else if (!nomem)
my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno);
}
}
fclose(f);
rehash(name_count);
my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
return name_count;
}
void cache_reload(void) {
struct crec *cache, **up, *tmp;
int i, total_size = daemon->cachesize;
struct hostsfile* ah;
cache_inserted = cache_live_freed = 0;
for (i = 0; i < hash_size; i++)
for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp) {
tmp = cache->hash_next;
if (cache->flags & F_HOSTS) {
*up = cache->hash_next;
free(cache);
} else if (!(cache->flags & F_DHCP)) {
*up = cache->hash_next;
if (cache->flags & F_BIGNAME) {
cache->name.bname->next = big_free;
big_free = cache->name.bname;
}
cache->flags = 0;
} else
up = &cache->hash_next;
}
if ((daemon->options & OPT_NO_HOSTS) && !daemon->addn_hosts) {
if (daemon->cachesize > 0) my_syslog(LOG_INFO, _("cleared cache"));
return;
}
if (!(daemon->options & OPT_NO_HOSTS)) total_size = read_hostsfile(HOSTSFILE, 0, total_size);
for (i = 0, ah = daemon->addn_hosts; ah; ah = ah->next) {
if (i <= ah->index) i = ah->index + 1;
if (ah->flags & AH_DIR)
ah->flags |= AH_INACTIVE;
else
ah->flags &= ~AH_INACTIVE;
}
for (ah = daemon->addn_hosts; ah; ah = ah->next)
if (!(ah->flags & AH_INACTIVE)) {
struct stat buf;
if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode)) {
DIR* dir_stream;
struct dirent* ent;
/* don't read this as a file */
ah->flags |= AH_INACTIVE;
if (!(dir_stream = opendir(ah->fname)))
my_syslog(LOG_ERR, _("cannot access directory %s: %s"), ah->fname,
strerror(errno));
else {
while ((ent = readdir(dir_stream))) {
size_t lendir = strlen(ah->fname);
size_t lenfile = strlen(ent->d_name);
struct hostsfile* ah1;
char* path;
/* ignore emacs backups and dotfiles */
if (lenfile == 0 || ent->d_name[lenfile - 1] == '~' ||
(ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
ent->d_name[0] == '.')
continue;
/* see if we have an existing record.
dir is ah->fname
file is ent->d_name
path to match is ah1->fname */
for (ah1 = daemon->addn_hosts; ah1; ah1 = ah1->next) {
if (lendir < strlen(ah1->fname) &&
strstr(ah1->fname, ah->fname) == ah1->fname &&
ah1->fname[lendir] == '/' &&
strcmp(ah1->fname + lendir + 1, ent->d_name) == 0) {
ah1->flags &= ~AH_INACTIVE;
break;
}
}
/* make new record */
if (!ah1) {
if (!(ah1 = whine_malloc(sizeof(struct hostsfile)))) continue;
if (!(path = whine_malloc(lendir + lenfile + 2))) {
free(ah1);
continue;
}
strcpy(path, ah->fname);
strcat(path, "/");
strcat(path, ent->d_name);
ah1->fname = path;
ah1->index = i++;
ah1->flags = AH_DIR;
ah1->next = daemon->addn_hosts;
daemon->addn_hosts = ah1;
}
/* inactivate record if not regular file */
if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 &&
!S_ISREG(buf.st_mode))
ah1->flags |= AH_INACTIVE;
}
closedir(dir_stream);
}
}
}
for (ah = daemon->addn_hosts; ah; ah = ah->next)
if (!(ah->flags & AH_INACTIVE))
total_size = read_hostsfile(ah->fname, ah->index, total_size);
}
char* get_domain(struct in_addr addr) {
struct cond_domain* c;
for (c = daemon->cond_domain; c; c = c->next)
if (ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
return c->domain;
return daemon->domain_suffix;
}
#ifdef HAVE_DHCP
void cache_unhash_dhcp(void) {
struct crec *cache, **up;
int i;
for (i = 0; i < hash_size; i++)
for (cache = hash_table[i], up = &hash_table[i]; cache; cache = cache->hash_next)
if (cache->flags & F_DHCP) {
*up = cache->hash_next;
cache->next = dhcp_spare;
dhcp_spare = cache;
} else
up = &cache->hash_next;
}
void cache_add_dhcp_entry(char* host_name, struct in_addr* host_address, time_t ttd) {
struct crec *crec = NULL, *aliasc;
unsigned short flags = F_DHCP | F_FORWARD | F_IPV4 | F_REVERSE;
int in_hosts = 0;
struct cname* a;
while ((crec = cache_find_by_name(crec, host_name, 0, F_IPV4 | F_CNAME))) {
/* check all addresses associated with name */
if (crec->flags & F_HOSTS) {
if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr) {
strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4));
my_syslog(LOG_WARNING,
_("not giving name %s to the DHCP lease of %s because "
"the name exists in %s with address %s"),
host_name, inet_ntoa(*host_address), record_source(crec->uid),
daemon->namebuff);
return;
} else
/* if in hosts, don't need DHCP record */
in_hosts = 1;
} else if (!(crec->flags & F_DHCP)) {
cache_scan_free(host_name, NULL, 0, crec->flags & (F_IPV4 | F_CNAME | F_FORWARD));
/* scan_free deletes all addresses associated with name */
break;
}
}
if (in_hosts) return;
if ((crec = cache_find_by_addr(NULL, (struct all_addr*) host_address, 0, F_IPV4))) {
if (crec->flags & F_NEG)
cache_scan_free(NULL, (struct all_addr*) host_address, 0, F_IPV4 | F_REVERSE);
else
/* avoid multiple reverse mappings */
flags &= ~F_REVERSE;
}
if ((crec = dhcp_spare))
dhcp_spare = dhcp_spare->next;
else /* need new one */
crec = whine_malloc(sizeof(struct crec));
if (crec) /* malloc may fail */
{
crec->flags = flags;
if (ttd == 0)
crec->flags |= F_IMMORTAL;
else
crec->ttd = ttd;
crec->addr.addr.addr.addr4 = *host_address;
crec->name.namep = host_name;
crec->uid = uid++;
cache_hash(crec);
for (a = daemon->cnames; a; a = a->next)
if (hostname_isequal(host_name, a->target)) {
if ((aliasc = dhcp_spare))
dhcp_spare = dhcp_spare->next;
else /* need new one */
aliasc = whine_malloc(sizeof(struct crec));
if (aliasc) {
aliasc->flags = F_FORWARD | F_CONFIG | F_DHCP | F_CNAME;
if (ttd == 0)
aliasc->flags |= F_IMMORTAL;
else
aliasc->ttd = ttd;
aliasc->name.namep = a->alias;
aliasc->addr.cname.cache = crec;
aliasc->addr.cname.uid = crec->uid;
cache_hash(aliasc);
}
}
}
}
#endif
void dump_cache(time_t now) {
struct server *serv, *serv1;
my_syslog(LOG_INFO, _("time %lu"), (unsigned long) now);
my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."),
daemon->cachesize, cache_live_freed, cache_inserted);
my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),
daemon->queries_forwarded, daemon->local_answer);
if (!addrbuff && !(addrbuff = whine_malloc(ADDRSTRLEN))) return;
/* sum counts from different records for same server */
for (serv = daemon->servers; serv; serv = serv->next) serv->flags &= ~SERV_COUNTED;
for (serv = daemon->servers; serv; serv = serv->next)
if (!(serv->flags & (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED))) {
int port;
unsigned int queries = 0, failed_queries = 0;
for (serv1 = serv; serv1; serv1 = serv1->next)
if (!(serv1->flags & (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED)) &&
sockaddr_isequal(&serv->addr, &serv1->addr)) {
serv1->flags |= SERV_COUNTED;
queries += serv1->queries;
failed_queries += serv1->failed_queries;
}
port = prettyprint_addr(&serv->addr, addrbuff);
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), addrbuff,
port, queries, failed_queries);
}
if ((daemon->options & (OPT_DEBUG | OPT_LOG))) {
struct crec* cache;
int i;
my_syslog(LOG_DEBUG,
"Host Address Flags "
" Expires");
for (i = 0; i < hash_size; i++)
for (cache = hash_table[i]; cache; cache = cache->hash_next) {
char *a, *p = daemon->namebuff;
p += sprintf(p, "%-40.40s ", cache_get_name(cache));
if ((cache->flags & F_NEG) && (cache->flags & F_FORWARD))
a = "";
else if (cache->flags & F_CNAME) {
a = "";
if (!is_outdated_cname_pointer(cache))
a = cache_get_name(cache->addr.cname.cache);
}
#ifdef HAVE_IPV6
else {
a = addrbuff;
if (cache->flags & F_IPV4)
inet_ntop(AF_INET, &cache->addr.addr, addrbuff, ADDRSTRLEN);
else if (cache->flags & F_IPV6)
inet_ntop(AF_INET6, &cache->addr.addr, addrbuff, ADDRSTRLEN);
}
#else
else
a = inet_ntoa(cache->addr.addr.addr.addr4);
#endif
p += sprintf(
p, "%-30.30s %s%s%s%s%s%s%s%s%s%s ", a, cache->flags & F_IPV4 ? "4" : "",
cache->flags & F_IPV6 ? "6" : "", cache->flags & F_CNAME ? "C" : "",
cache->flags & F_FORWARD ? "F" : " ", cache->flags & F_REVERSE ? "R" : " ",
cache->flags & F_IMMORTAL ? "I" : " ", cache->flags & F_DHCP ? "D" : " ",
cache->flags & F_NEG ? "N" : " ", cache->flags & F_NXDOMAIN ? "X" : " ",
cache->flags & F_HOSTS ? "H" : " ");
#ifdef HAVE_BROKEN_RTC
p += sprintf(p, "%lu",
cache->flags & F_IMMORTAL ? 0 : (unsigned long) (cache->ttd - now));
#else
p += sprintf(p, "%s", cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd)));
/* ctime includes trailing \n - eat it */
*(p - 1) = 0;
#endif
my_syslog(LOG_DEBUG, daemon->namebuff);
}
}
}
char* record_source(int index) {
struct hostsfile* ah;
if (index == 0) return HOSTSFILE;
for (ah = daemon->addn_hosts; ah; ah = ah->next)
if (ah->index == index) return ah->fname;
return "";
}
void querystr(char* str, unsigned short type) {
unsigned int i;
sprintf(str, "query[type=%d]", type);
for (i = 0; i < (sizeof(typestr) / sizeof(typestr[0])); i++)
if (typestr[i].type == type) sprintf(str, "query[%s]", typestr[i].name);
}
void log_query(unsigned short flags, char* name, struct all_addr* addr, char* arg) {
char *source, *dest = addrbuff;
char* verb = "is";
if (!(daemon->options & OPT_LOG)) return;
if (addr) {
#ifdef HAVE_IPV6
/* TODO: support scoped addresses. struct all_addr doesn't store scope IDs. */
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_NXDOMAIN) {
if (flags & F_IPV4)
dest = "NXDOMAIN-IPv4";
else if (flags & F_IPV6)
dest = "NXDOMAIN-IPv6";
else
dest = "NXDOMAIN";
} else {
if (flags & F_IPV4)
dest = "NODATA-IPv4";
else if (flags & F_IPV6)
dest = "NODATA-IPv6";
else
dest = "NODATA";
}
} else if (flags & F_CNAME) {
/* nasty abuse of NXDOMAIN and CNAME flags */
if (flags & F_NXDOMAIN)
dest = arg;
else
dest = "";
}
if (flags & F_CONFIG)
source = "config";
else if (flags & F_DHCP)
source = "DHCP";
else if (flags & F_HOSTS)
source = arg;
else if (flags & F_UPSTREAM)
source = "reply";
else if (flags & F_SERVER) {
source = "forwarded";
verb = "to";
} else if (flags & F_QUERY) {
source = arg;
verb = "from";
} else
source = "cached";
if (strlen(name) == 0) name = ".";
my_syslog(LOG_DEBUG, "%s %s %s %s", source, name, verb, dest);
}
./src/config.h 0000644 0000000 0000000 00000020121 14256750714 012125 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#define VERSION "2.51"
#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 10 /* drop UDP queries after TIMEOUT seconds */
#define FORWARD_TEST 50 /* try all servers every 50 queries */
#define FORWARD_TIME 10 /* or 10 seconds */
#define RANDOM_SOCKS 64 /* max simultaneous random ports */
#define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY 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 */
#define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */
#define DECLINE_BACKOFF 600 /* disable DECLINEd static addresses for this long */
#define DHCP_PACKET_MAX 16384 /* hard limit on DHCP packet size */
#define SMALLDNAME 40 /* most domain names are smaller than this */
#define HOSTSFILE "/etc/hosts"
#ifdef __uClinux__
#define RESOLVFILE "/etc/config/resolv.conf"
#else
#define RESOLVFILE "/etc/resolv.conf"
#endif
#define RUNFILE "/var/run/dnsmasq.pid"
#ifndef LEASEFILE
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
#define LEASEFILE "/var/db/dnsmasq.leases"
#elif defined(__sun__) || defined(__sun)
#define LEASEFILE "/var/cache/dnsmasq.leases"
#elif defined(__ANDROID__)
#define LEASEFILE "/data/misc/dhcp/dnsmasq.leases"
#else
#define LEASEFILE "/var/lib/misc/dnsmasq.leases"
#endif
#endif
#ifndef CONFFILE
#if defined(__FreeBSD__)
#define CONFFILE "/usr/local/etc/dnsmasq.conf"
#else
#define CONFFILE "/etc/dnsmasq.conf"
#endif
#endif
#define DEFLEASE 3600 /* default lease time, 1 hour */
#define CHUSER "nobody"
#define CHGRP "dip"
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define DHCP_SERVER_ALTPORT 1067
#define DHCP_CLIENT_ALTPORT 1068
#define LOG_MAX 5 /* log-queue length */
#define RANDFILE "/dev/urandom"
#define DAD_WAIT 20 /* retry binding IPv6 sockets for this long */
/* A small collection of RR-types which are missing on some platforms */
#ifndef T_SIG
#define T_SIG 24
#endif
#ifndef T_SRV
#define T_SRV 33
#endif
#ifndef T_OPT
#define T_OPT 41
#endif
#ifndef T_TKEY
#define T_TKEY 249
#endif
#ifndef T_TSIG
#define T_TSIG 250
#endif
/* Follows system specific switches. If you run on a
new system, you may want to edit these.
May replace this with Autoconf one day.
HAVE_LINUX_NETWORK
HAVE_BSD_NETWORK
HAVE_SOLARIS_NETWORK
define exactly one of these to alter interaction with kernel networking.
HAVE_BROKEN_RTC
define this on embedded systems which don't have an RTC
which keeps time over reboots. Causes dnsmasq to use uptime
for timing, and keep lease lengths rather than expiry times
in its leases file. This also make dnsmasq "flash disk friendly".
Normally, dnsmasq tries very hard to keep the on-disk leases file
up-to-date: rewriting it after every renewal. When HAVE_BROKEN_RTC
is in effect, the lease file is only written when a new lease is
created, or an old one destroyed. (Because those are the only times
it changes.) This vastly reduces the number of file writes, and makes
it viable to keep the lease file on a flash filesystem.
NOTE: when enabling or disabling this, be sure to delete any old
leases file, otherwise dnsmasq may get very confused.
HAVE_DHCP
define this to get dnsmasq's DHCP server.
HAVE_SCRIPT
define this to get the ability to call scripts on lease-change
HAVE_GETOPT_LONG
define this if you have GNU libc or GNU getopt.
HAVE_ARC4RANDOM
define this if you have arc4random() to get better security from DNS spoofs
by using really random ids (OpenBSD)
HAVE_SOCKADDR_SA_LEN
define this if struct sockaddr has sa_len field (*BSD)
NOTES:
For Linux you should define
HAVE_LINUX_NETWORK
HAVE_GETOPT_LONG
you should NOT define
HAVE_ARC4RANDOM
HAVE_SOCKADDR_SA_LEN
For *BSD systems you should define
HAVE_BSD_NETWORK
HAVE_SOCKADDR_SA_LEN
and you MAY define
HAVE_ARC4RANDOM - OpenBSD and FreeBSD and NetBSD version 2.0 or later
HAVE_GETOPT_LONG - NetBSD, later FreeBSD
(FreeBSD and OpenBSD only if you link GNU getopt)
*/
/* platform independent options- uncomment to enable */
#define HAVE_DHCP
#define HAVE_SCRIPT
/* #define HAVE_BROKEN_RTC */
/* Allow DHCP to be disabled with COPTS=-DNO_DHCP */
#ifdef NO_DHCP
#undef HAVE_DHCP
#endif
/* Allow scripts to be disabled with COPTS=-DNO_SCRIPT */
#ifdef NO_SCRIPT
#undef HAVE_SCRIPT
#endif
/* platform dependent options. */
/* Must preceed __linux__ since uClinux defines __linux__ too. */
#if defined(__uClinux__)
#define HAVE_LINUX_NETWORK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#undef HAVE_SOCKADDR_SA_LEN
/* Never use fork() on uClinux. Note that this is subtly different from the
--keep-in-foreground option, since it also suppresses forking new
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
#elif defined(__UCLIBC__)
#define HAVE_LINUX_NETWORK
#if defined(__UCLIBC_HAS_GNU_GETOPT__) || \
((__UCLIBC_MAJOR__ == 0) && (__UCLIBC_MINOR__ == 9) && (__UCLIBC_SUBLEVEL__ < 21))
#define HAVE_GETOPT_LONG
#endif
#undef HAVE_ARC4RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#if !defined(__ARCH_HAS_MMU__) && !defined(__UCLIBC_HAS_MMU__)
#define NO_FORK
#endif
#if defined(__UCLIBC_HAS_IPV6__)
#ifndef IPV6_V6ONLY
#define IPV6_V6ONLY 26
#endif
#endif
/* This is for glibc 2.x */
#elif defined(__linux__)
#define HAVE_LINUX_NETWORK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || \
defined(__FreeBSD_kernel__)
#define HAVE_BSD_NETWORK
/* Later verions of FreeBSD have getopt_long() */
#if defined(optional_argument) && defined(required_argument)
#define HAVE_GETOPT_LONG
#endif
#if !defined(__FreeBSD_kernel__)
#define HAVE_ARC4RANDOM
#endif
#define HAVE_SOCKADDR_SA_LEN
#elif defined(__APPLE__)
#define HAVE_BSD_NETWORK
#undef HAVE_GETOPT_LONG
#define HAVE_ARC4RANDOM
#define HAVE_SOCKADDR_SA_LEN
/* Define before sys/socket.h is included so we get socklen_t */
#define _BSD_SOCKLEN_T_
#elif defined(__NetBSD__)
#define HAVE_BSD_NETWORK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_SOCKADDR_SA_LEN
#elif defined(__sun) || defined(__sun__)
#define HAVE_SOLARIS_NETWORK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#define _XPG4_2
#define __EXTENSIONS__
#define ETHER_ADDR_LEN 6
#endif
/* Decide if we're going to support IPv6 */
/* IPv6 can be forced off with "make COPTS=-DNO_IPV6" */
/* We assume that systems which don't have IPv6
headers don't have ntop and pton either */
#if defined(INET6_ADDRSTRLEN) && defined(IPV6_V6ONLY) && !defined(NO_IPV6)
#define HAVE_IPV6
#define ADDRSTRLEN INET6_ADDRSTRLEN
#if defined(SOL_IPV6)
#define IPV6_LEVEL SOL_IPV6
#else
#define IPV6_LEVEL IPPROTO_IPV6
#endif
#elif defined(INET_ADDRSTRLEN)
#undef HAVE_IPV6
#define ADDRSTRLEN INET_ADDRSTRLEN
#else
#undef HAVE_IPV6
#define ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */
#endif
/* Can't do scripts without fork */
#ifdef NOFORK
#undef HAVE_SCRIPT
#endif
./src/forward.c 0000644 0000000 0000000 00000107263 14256750714 012334 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "dnsmasq.h"
static struct frec* lookup_frec(unsigned short id, unsigned int crc);
static struct frec* lookup_frec_by_sender(unsigned short id, union mysockaddr* addr,
unsigned int crc);
static unsigned short get_id(int force, unsigned short force_id, unsigned int crc);
static void free_frec(struct frec* f);
static struct randfd* allocate_rfd(int family);
/* Send a UDP packet with its source address set as "source"
unless nowild is true, when we just send it with the kernel default */
static void send_from(int fd, int nowild, char* packet, size_t len, union mysockaddr* to,
struct all_addr* source, unsigned int iface) {
struct msghdr msg;
struct iovec iov[1];
union {
struct cmsghdr align; /* this ensures alignment */
#if defined(HAVE_LINUX_NETWORK)
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(IP_SENDSRCADDR)
char control[CMSG_SPACE(sizeof(struct in_addr))];
#endif
#ifdef HAVE_IPV6
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
#endif
} control_u;
iov[0].iov_base = packet;
iov[0].iov_len = len;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
msg.msg_name = to;
msg.msg_namelen = sa_len(to);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if (!nowild) {
struct cmsghdr* cmptr;
msg.msg_control = &control_u;
msg.msg_controllen = sizeof(control_u);
cmptr = CMSG_FIRSTHDR(&msg);
if (to->sa.sa_family == AF_INET) {
#if defined(HAVE_LINUX_NETWORK)
struct in_pktinfo* pkt = (struct in_pktinfo*) CMSG_DATA(cmptr);
pkt->ipi_ifindex = 0;
pkt->ipi_spec_dst = source->addr.addr4;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = SOL_IP;
cmptr->cmsg_type = IP_PKTINFO;
#elif defined(IP_SENDSRCADDR)
struct in_addr* a = (struct in_addr*) CMSG_DATA(cmptr);
*a = source->addr.addr4;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
cmptr->cmsg_level = IPPROTO_IP;
cmptr->cmsg_type = IP_SENDSRCADDR;
#endif
} else
#ifdef HAVE_IPV6
{
struct in6_pktinfo* pkt = (struct in6_pktinfo*) CMSG_DATA(cmptr);
pkt->ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
pkt->ipi6_addr = source->addr.addr6;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
cmptr->cmsg_type = IPV6_PKTINFO;
cmptr->cmsg_level = IPV6_LEVEL;
}
#else
iface = 0; /* eliminate warning */
#endif
}
retry:
if (sendmsg(fd, &msg, 0) == -1) {
/* certain Linux kernels seem to object to setting the source address in the IPv6 stack
by returning EINVAL from sendmsg. In that case, try again without setting the
source address, since it will nearly alway be correct anyway. IPv6 stinks. */
if (errno == EINVAL && msg.msg_controllen) {
msg.msg_controllen = 0;
goto retry;
}
if (retry_send()) goto retry;
}
}
static unsigned short search_servers(time_t now, struct all_addr** addrpp, unsigned short qtype,
char* qdomain, int* type, char** domain)
{
/* If the query ends in the domain in one of our servers, set
domain to point to that name. We find the largest match to allow both
domain.org and sub.domain.org to exist. */
unsigned int namelen = strlen(qdomain);
unsigned int matchlen = 0;
struct server* serv;
unsigned short flags = 0;
for (serv = daemon->servers; serv; serv = serv->next)
/* domain matches take priority over NODOTS matches */
if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.') &&
namelen != 0) {
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
*type = SERV_FOR_NODOTS;
if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN;
else if (serv->flags & SERV_LITERAL_ADDRESS) {
if (sflag & qtype) {
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr*) &serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
else
*addrpp = (struct all_addr*) &serv->addr.in6.sin6_addr;
#endif
} else if (!flags || (flags & F_NXDOMAIN))
flags = F_NOERR;
}
} else if (serv->flags & SERV_HAS_DOMAIN) {
unsigned int domainlen = strlen(serv->domain);
char* matchstart = qdomain + namelen - domainlen;
if (namelen >= domainlen && hostname_isequal(matchstart, serv->domain) &&
domainlen >= matchlen &&
(domainlen == 0 || namelen == domainlen || *(serv->domain) == '.' ||
*(matchstart - 1) == '.')) {
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
*type = SERV_HAS_DOMAIN;
*domain = serv->domain;
matchlen = domainlen;
if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN;
else if (serv->flags & SERV_LITERAL_ADDRESS) {
if (sflag & qtype) {
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr*) &serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
else
*addrpp = (struct all_addr*) &serv->addr.in6.sin6_addr;
#endif
} else if (!flags || (flags & F_NXDOMAIN))
flags = F_NOERR;
}
}
}
if (flags == 0 && !(qtype & F_BIGNAME) && (daemon->options & OPT_NODOTS_LOCAL) &&
!strchr(qdomain, '.') && namelen != 0)
/* don't forward simple names, make exception for NS queries and empty name. */
flags = F_NXDOMAIN;
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now)) flags = F_NOERR;
if (flags) {
int logflags = 0;
if (flags == F_NXDOMAIN || flags == F_NOERR) logflags = F_NEG | qtype;
log_query(logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp, NULL);
}
return flags;
}
static int forward_query(int udpfd, union mysockaddr* udpaddr, struct all_addr* dst_addr,
unsigned int dst_iface, HEADER* header, size_t plen, time_t now,
struct frec* forward) {
char* domain = NULL;
int type = 0;
struct all_addr* addrp = NULL;
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
unsigned short flags = 0;
unsigned short gotname = extract_request(header, plen, daemon->namebuff, NULL);
struct server* start = NULL;
/* may be no servers available. */
if (!daemon->servers)
forward = NULL;
else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc))) {
/* retry on existing query, send to all available servers */
domain = forward->sentto->domain;
forward->sentto->failed_queries++;
if (!(daemon->options & OPT_ORDER)) {
forward->forwardall = 1;
daemon->last_server = NULL;
}
type = forward->sentto->flags & SERV_TYPE;
if (!(start = forward->sentto->next)) start = daemon->servers; /* at end of list, recycle */
header->id = htons(forward->new_id);
} else {
if (gotname) flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
if (!flags && !(forward = get_new_frec(now, NULL))) /* table full - server failure. */
flags = F_NEG;
if (forward) {
/* force unchanging id for signed packets */
int is_sign;
find_pseudoheader(header, plen, NULL, NULL, &is_sign);
forward->source = *udpaddr;
forward->dest = *dst_addr;
forward->iface = dst_iface;
forward->orig_id = ntohs(header->id);
forward->new_id = get_id(is_sign, forward->orig_id, crc);
forward->fd = udpfd;
forward->crc = crc;
forward->forwardall = 0;
header->id = htons(forward->new_id);
/* In strict_order mode, or when using domain specific servers
always try servers in the order specified in resolv.conf,
otherwise, use the one last known to work. */
if (type != 0 || (daemon->options & OPT_ORDER))
start = daemon->servers;
else if (!(start = daemon->last_server) || daemon->forwardcount++ > FORWARD_TEST ||
difftime(now, daemon->forwardtime) > FORWARD_TIME) {
start = daemon->servers;
forward->forwardall = 1;
daemon->forwardcount = 0;
daemon->forwardtime = now;
}
}
}
/* check for send errors here (no route to host)
if we fail to send to all nameservers, send back an error
packet straight away (helps modem users when offline) */
if (!flags && forward) {
struct server* firstsentto = start;
int forwarded = 0;
while (1) {
/* only send to servers dealing with our domain.
domain may be NULL, in which case server->domain
must be NULL also. */
if (type == (start->flags & SERV_TYPE) &&
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
!(start->flags & SERV_LITERAL_ADDRESS)) {
int fd;
/* find server socket to use, may need to get random one. */
if (start->sfd)
fd = start->sfd->fd;
else {
#ifdef HAVE_IPV6
if (start->addr.sa.sa_family == AF_INET6) {
if (!forward->rfd6 && !(forward->rfd6 = allocate_rfd(AF_INET6))) break;
daemon->rfd_save = forward->rfd6;
fd = forward->rfd6->fd;
} else
#endif
{
if (!forward->rfd4 && !(forward->rfd4 = allocate_rfd(AF_INET))) break;
daemon->rfd_save = forward->rfd4;
fd = forward->rfd4->fd;
}
#ifdef ANDROID
// Mark the socket so it goes out on the correct network. Note
// that we never clear the mark, only re-set it the next time we
// allocate a new random fd. This is because we buffer DNS
// queries (in daemon->srv_save, daemon->packet_len) and socket
// file descriptors (in daemon->rfd_save) with the expectation of
// being able to use them again.
//
// Server fds are marked separately in allocate_sfd.
setsockopt(fd, SOL_SOCKET, SO_MARK, &start->mark, sizeof(start->mark));
#endif
}
if (sendto(fd, (char*) header, plen, 0, &start->addr.sa, sa_len(&start->addr)) ==
-1) {
if (retry_send()) continue;
} else {
/* Keep info in case we want to re-send this packet */
daemon->srv_save = start;
daemon->packet_len = plen;
if (!gotname) strcpy(daemon->namebuff, "query");
if (start->addr.sa.sa_family == AF_INET)
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr*) &start->addr.in.sin_addr, NULL);
#ifdef HAVE_IPV6
else
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr*) &start->addr.in6.sin6_addr, NULL);
#endif
start->queries++;
forwarded = 1;
forward->sentto = start;
if (!forward->forwardall) break;
forward->forwardall++;
}
}
if (!(start = start->next)) start = daemon->servers;
if (start == firstsentto) break;
}
if (forwarded) return 1;
/* could not send on, prepare to return */
header->id = htons(forward->orig_id);
free_frec(forward); /* cancel */
}
/* could not send on, return empty answer or address if known for whole domain */
if (udpfd != -1) {
plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
send_from(udpfd, daemon->options & OPT_NOWILD, (char*) header, plen, udpaddr, dst_addr,
dst_iface);
}
return 0;
}
static size_t process_reply(HEADER* header, time_t now, struct server* server, size_t n) {
unsigned char *pheader, *sizep;
int munged = 0, is_sign;
size_t plen;
/* If upstream is advertising a larger UDP packet size
than we allow, trim it so that we don't get overlarge
requests for the client. We can't do this for signed packets. */
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)) && !is_sign) {
unsigned short udpsz;
unsigned char* psave = sizep;
GETSHORT(udpsz, sizep);
if (udpsz > daemon->edns_pktsz) PUTSHORT(daemon->edns_pktsz, psave);
}
if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
return n;
/* Complain loudly if the upstream server is non-recursive. */
if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0 && server &&
!(server->flags & SERV_WARNED_RECURSIVE)) {
prettyprint_addr(&server->addr, daemon->namebuff);
my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
if (!(daemon->options & OPT_LOG)) server->flags |= SERV_WARNED_RECURSIVE;
}
if (daemon->bogus_addr && header->rcode != NXDOMAIN &&
check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now)) {
munged = 1;
header->rcode = NXDOMAIN;
header->aa = 0;
} else {
if (header->rcode == NXDOMAIN && extract_request(header, n, daemon->namebuff, NULL) &&
check_for_local_domain(daemon->namebuff, now)) {
/* 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,
since we know that the domain exists, even if upstream doesn't */
munged = 1;
header->aa = 1;
header->rcode = NOERROR;
}
if (extract_addresses(header, n, daemon->namebuff, now)) {
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected"));
munged = 1;
}
}
/* do this after extract_addresses. Ensure NODATA reply and remove
nameserver info. */
if (munged) {
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
}
/* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
sections of the packet. Find the new length here and put back pseudoheader
if it was removed. */
return resize_packet(header, n, pheader, plen);
}
/* sets new last_server */
void reply_query(int fd, int family, time_t now) {
/* packet from peer server, extract data for cache, and send to
original requester */
HEADER* header;
union mysockaddr serveraddr;
struct frec* forward;
socklen_t addrlen = sizeof(serveraddr);
ssize_t n = recvfrom(fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
size_t nn;
struct server* server;
/* packet buffer overwritten */
daemon->srv_save = NULL;
/* Determine the address of the server replying so that we can mark that as good */
serveraddr.sa.sa_family = family;
#ifdef HAVE_IPV6
if (serveraddr.sa.sa_family == AF_INET6) serveraddr.in6.sin6_flowinfo = 0;
#endif
/* spoof check: answer must come from known server, */
for (server = daemon->servers; server; server = server->next)
if (!(server->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
sockaddr_isequal(&server->addr, &serveraddr))
break;
header = (HEADER*) daemon->packet;
if (!server || n < (int) sizeof(HEADER) || !header->qr ||
!(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
return;
server = forward->sentto;
if ((header->rcode == SERVFAIL || header->rcode == REFUSED) && !(daemon->options & OPT_ORDER) &&
forward->forwardall == 0)
/* for broken servers, attempt to send to another one. */
{
unsigned char* pheader;
size_t plen;
int is_sign;
/* recreate query from reply */
pheader = find_pseudoheader(header, (size_t) n, &plen, NULL, &is_sign);
if (!is_sign) {
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
if ((nn = resize_packet(header, (size_t) n, pheader, plen))) {
header->qr = 0;
header->tc = 0;
forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
return;
}
}
}
if ((forward->sentto->flags & SERV_TYPE) == 0) {
if (header->rcode == SERVFAIL || header->rcode == REFUSED)
server = NULL;
else {
struct server* last_server;
/* find good server by address if possible, otherwise assume the last one we sent to */
for (last_server = daemon->servers; last_server; last_server = last_server->next)
if (!(last_server->flags &
(SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
sockaddr_isequal(&last_server->addr, &serveraddr)) {
server = last_server;
break;
}
}
if (!(daemon->options & OPT_ALL_SERVERS)) daemon->last_server = server;
}
/* If the answer is an error, keep the forward record in place in case
we get a good reply from another server. Kill it when we've
had replies from all to avoid filling the forwarding table when
everything is broken */
if (forward->forwardall == 0 || --forward->forwardall == 1 ||
(header->rcode != REFUSED && header->rcode != SERVFAIL)) {
if ((nn = process_reply(header, now, server, (size_t) n))) {
header->id = htons(forward->orig_id);
header->ra = 1; /* recursion if available */
send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, nn,
&forward->source, &forward->dest, forward->iface);
}
free_frec(forward); /* cancel */
}
}
void receive_query(struct listener* listen, time_t now) {
HEADER* header = (HEADER*) daemon->packet;
union mysockaddr source_addr;
unsigned short type;
struct all_addr dst_addr;
struct in_addr netmask, dst_addr_4;
size_t m;
ssize_t n;
int if_index = 0;
struct iovec iov[1];
struct msghdr msg;
struct cmsghdr* cmptr;
union {
struct cmsghdr align; /* this ensures alignment */
#ifdef HAVE_IPV6
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
#endif
#if defined(HAVE_LINUX_NETWORK)
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(IP_RECVDSTADDR)
char control[CMSG_SPACE(sizeof(struct in_addr)) + CMSG_SPACE(sizeof(struct sockaddr_dl))];
#endif
} control_u;
/* packet buffer overwritten */
daemon->srv_save = NULL;
if (listen->family == AF_INET && (daemon->options & OPT_NOWILD)) {
dst_addr_4 = listen->iface->addr.in.sin_addr;
netmask = listen->iface->netmask;
} else {
dst_addr_4.s_addr = 0;
netmask.s_addr = 0;
}
iov[0].iov_base = daemon->packet;
iov[0].iov_len = daemon->edns_pktsz;
msg.msg_control = control_u.control;
msg.msg_controllen = sizeof(control_u);
msg.msg_flags = 0;
msg.msg_name = &source_addr;
msg.msg_namelen = sizeof(source_addr);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if ((n = recvmsg(listen->fd, &msg, 0)) == -1) return;
if (n < (int) sizeof(HEADER) || (msg.msg_flags & MSG_TRUNC) || header->qr) return;
source_addr.sa.sa_family = listen->family;
#ifdef HAVE_IPV6
if (listen->family == AF_INET6) source_addr.in6.sin6_flowinfo = 0;
#endif
if (!(daemon->options & OPT_NOWILD)) {
struct ifreq ifr;
if (msg.msg_controllen < sizeof(struct cmsghdr)) return;
#if defined(HAVE_LINUX_NETWORK)
if (listen->family == AF_INET)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) {
dst_addr_4 = dst_addr.addr.addr4 =
((struct in_pktinfo*) CMSG_DATA(cmptr))->ipi_spec_dst;
if_index = ((struct in_pktinfo*) CMSG_DATA(cmptr))->ipi_ifindex;
}
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
if (listen->family == AF_INET) {
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
dst_addr_4 = dst_addr.addr.addr4 = *((struct in_addr*) CMSG_DATA(cmptr));
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = ((struct sockaddr_dl*) CMSG_DATA(cmptr))->sdl_index;
}
#endif
#ifdef HAVE_IPV6
if (listen->family == AF_INET6) {
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO) {
dst_addr.addr.addr6 = ((struct in6_pktinfo*) CMSG_DATA(cmptr))->ipi6_addr;
if_index = ((struct in6_pktinfo*) CMSG_DATA(cmptr))->ipi6_ifindex;
}
}
#endif
/* enforce available interface configuration */
if (!indextoname(listen->fd, if_index, ifr.ifr_name) ||
!iface_check(listen->family, &dst_addr, ifr.ifr_name, &if_index))
return;
if (listen->family == AF_INET && (daemon->options & OPT_LOCALISE) &&
ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
return;
netmask = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr;
}
if (extract_request(header, (size_t) n, daemon->namebuff, &type)) {
char types[20];
querystr(types, type);
if (listen->family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr*) &source_addr.in.sin_addr, types);
#ifdef HAVE_IPV6
else
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr*) &source_addr.in6.sin6_addr, types);
#endif
}
m = answer_request(header, ((char*) header) + PACKETSZ, (size_t) n, dst_addr_4, netmask, now);
if (m >= 1) {
send_from(listen->fd, daemon->options & OPT_NOWILD, (char*) header, m, &source_addr,
&dst_addr, if_index);
daemon->local_answer++;
} else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index, header, (size_t) n, now,
NULL))
daemon->queries_forwarded++;
else
daemon->local_answer++;
}
/* 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
done by the caller. */
unsigned char* tcp_request(int confd, time_t now, struct in_addr local_addr,
struct in_addr netmask) {
int size = 0;
size_t m;
unsigned short qtype, gotname;
unsigned char c1, c2;
/* Max TCP packet + slop */
unsigned char* packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
HEADER* header;
struct server* last_server;
while (1) {
if (!packet || !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
!(size = c1 << 8 | c2) || !read_write(confd, packet, size, 1))
return packet;
if (size < (int) sizeof(HEADER)) continue;
header = (HEADER*) packet;
if ((gotname = extract_request(header, (unsigned int) size, daemon->namebuff, &qtype))) {
union mysockaddr peer_addr;
socklen_t peer_len = sizeof(union mysockaddr);
if (getpeername(confd, (struct sockaddr*) &peer_addr, &peer_len) != -1) {
char types[20];
querystr(types, qtype);
if (peer_addr.sa.sa_family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr*) &peer_addr.in.sin_addr, types);
#ifdef HAVE_IPV6
else
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr*) &peer_addr.in6.sin6_addr, types);
#endif
}
}
/* m > 0 if answered from cache */
m = answer_request(header, ((char*) header) + 65536, (unsigned int) size, local_addr,
netmask, now);
/* Do this by steam now we're not in the select() loop */
check_log_writer(NULL);
if (m == 0) {
unsigned short flags = 0;
struct all_addr* addrp = NULL;
int type = 0;
char* domain = NULL;
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server)
last_server = daemon->servers;
else
last_server = daemon->last_server;
if (!flags && last_server) {
struct server* firstsendto = NULL;
unsigned int crc = questions_crc(header, (unsigned int) size, daemon->namebuff);
/* Loop round available servers until we succeed in connecting to one.
Note that this code subtley ensures that consecutive queries on this connection
which can go to the same server, do so. */
while (1) {
if (!firstsendto)
firstsendto = last_server;
else {
if (!(last_server = last_server->next)) last_server = daemon->servers;
if (last_server == firstsendto) break;
}
/* server for wrong domain */
if (type != (last_server->flags & SERV_TYPE) ||
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
continue;
if ((last_server->tcpfd == -1) &&
(last_server->tcpfd =
socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
(!local_bind(last_server->tcpfd, &last_server->source_addr,
last_server->interface, last_server->mark, 1) ||
connect(last_server->tcpfd, &last_server->addr.sa,
sa_len(&last_server->addr)) == -1)) {
close(last_server->tcpfd);
last_server->tcpfd = -1;
}
if (last_server->tcpfd == -1) continue;
c1 = size >> 8;
c2 = size;
if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
!read_write(last_server->tcpfd, &c2, 1, 0) ||
!read_write(last_server->tcpfd, packet, size, 0) ||
!read_write(last_server->tcpfd, &c1, 1, 1) ||
!read_write(last_server->tcpfd, &c2, 1, 1)) {
close(last_server->tcpfd);
last_server->tcpfd = -1;
continue;
}
m = (c1 << 8) | c2;
if (!read_write(last_server->tcpfd, packet, m, 1)) return packet;
if (!gotname) strcpy(daemon->namebuff, "query");
if (last_server->addr.sa.sa_family == AF_INET)
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr*) &last_server->addr.in.sin_addr, NULL);
#ifdef HAVE_IPV6
else
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr*) &last_server->addr.in6.sin6_addr, NULL);
#endif
/* There's no point in updating the cache, since this process will exit and
lose the information after a few queries. We make this call for the alias and
bogus-nxdomain side-effects. */
/* If the crc of the question section doesn't match the crc we sent, then
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
if (crc == questions_crc(header, (unsigned int) m, daemon->namebuff))
m = process_reply(header, now, last_server, (unsigned int) m);
break;
}
}
/* In case of local answer or no connections made. */
if (m == 0)
m = setup_reply(header, (unsigned int) size, addrp, flags, daemon->local_ttl);
}
check_log_writer(NULL);
c1 = m >> 8;
c2 = m;
if (!read_write(confd, &c1, 1, 0) || !read_write(confd, &c2, 1, 0) ||
!read_write(confd, packet, m, 0))
return packet;
}
}
static struct frec* allocate_frec(time_t now) {
struct frec* f;
if ((f = (struct frec*) whine_malloc(sizeof(struct frec)))) {
f->next = daemon->frec_list;
f->time = now;
f->sentto = NULL;
f->rfd4 = NULL;
#ifdef HAVE_IPV6
f->rfd6 = NULL;
#endif
daemon->frec_list = f;
}
return f;
}
static struct randfd* allocate_rfd(int family) {
static int finger = 0;
int i;
/* limit the number of sockets we have open to avoid starvation of
(eg) TFTP. Once we have a reasonable number, randomness should be OK */
for (i = 0; i < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount == 0) {
if ((daemon->randomsocks[i].fd = random_sock(family)) == -1) break;
daemon->randomsocks[i].refcount = 1;
daemon->randomsocks[i].family = family;
return &daemon->randomsocks[i];
}
/* No free ones or cannot get new socket, grab an existing one */
for (i = 0; i < RANDOM_SOCKS; i++) {
int j = (i + finger) % RANDOM_SOCKS;
if (daemon->randomsocks[j].refcount != 0 && daemon->randomsocks[j].family == family &&
daemon->randomsocks[j].refcount != 0xffff) {
finger = j;
daemon->randomsocks[j].refcount++;
return &daemon->randomsocks[j];
}
}
return NULL; /* doom */
}
static void free_frec(struct frec* f) {
if (f->rfd4 && --(f->rfd4->refcount) == 0) close(f->rfd4->fd);
f->rfd4 = NULL;
f->sentto = NULL;
#ifdef HAVE_IPV6
if (f->rfd6 && --(f->rfd6->refcount) == 0) close(f->rfd6->fd);
f->rfd6 = NULL;
#endif
}
/* 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. Impose an absolute
limit of 4*TIMEOUT before we wipe things (for random sockets) */
struct frec* get_new_frec(time_t now, int* wait) {
struct frec *f, *oldest, *target;
int count;
if (wait) *wait = 0;
for (f = daemon->frec_list, oldest = NULL, target = NULL, count = 0; f; f = f->next, count++)
if (!f->sentto)
target = f;
else {
if (difftime(now, f->time) >= 4 * TIMEOUT) {
free_frec(f);
target = f;
}
if (!oldest || difftime(f->time, oldest->time) <= 0) oldest = f;
}
if (target) {
target->time = now;
return target;
}
/* 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) {
free_frec(oldest);
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 */
}
/* crc is all-ones if not known. */
static struct frec* lookup_frec(unsigned short id, unsigned int crc) {
struct frec* f;
for (f = daemon->frec_list; f; f = f->next)
if (f->sentto && f->new_id == id && (f->crc == crc || crc == 0xffffffff)) return f;
return NULL;
}
static struct frec* lookup_frec_by_sender(unsigned short id, union mysockaddr* addr,
unsigned int crc) {
struct frec* f;
for (f = daemon->frec_list; f; f = f->next)
if (f->sentto && f->orig_id == id && f->crc == crc && sockaddr_isequal(&f->source, addr))
return f;
return NULL;
}
/* A server record is going away, remove references to it */
void server_gone(struct server* server) {
struct frec* f;
for (f = daemon->frec_list; f; f = f->next)
if (f->sentto && f->sentto == server) free_frec(f);
if (daemon->last_server == server) daemon->last_server = NULL;
if (daemon->srv_save == server) daemon->srv_save = NULL;
}
/* return unique random ids.
For signed packets we can't change the ID without breaking the
signing, so we keep the same one. In this case force is set, and this
routine degenerates into killing any conflicting forward record. */
static unsigned short get_id(int force, unsigned short force_id, unsigned int crc) {
unsigned short ret = 0;
if (force) {
struct frec* f = lookup_frec(force_id, crc);
if (f) free_frec(f); /* free */
ret = force_id;
} else
do
ret = rand16();
while (lookup_frec(ret, crc));
return ret;
}
./src/lease.c 0000644 0000000 0000000 00000042164 14256750714 011757 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "dnsmasq.h"
#ifdef HAVE_DHCP
static struct dhcp_lease *leases = NULL, *old_leases = NULL;
static int dns_dirty, file_dirty, leases_left;
void lease_init(time_t now) {
unsigned long ei;
struct in_addr addr;
struct dhcp_lease* lease;
int clid_len, hw_len, hw_type;
FILE* leasestream;
/* 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;
if (daemon->options & OPT_LEASE_RO) {
/* run " init" once to get the
initial state of the database. If leasefile-ro is
set without a script, we just do without any
lease database. */
#ifdef HAVE_SCRIPT
if (daemon->lease_change_command) {
strcpy(daemon->dhcp_buff, daemon->lease_change_command);
strcat(daemon->dhcp_buff, " init");
leasestream = popen(daemon->dhcp_buff, "r");
} else
#endif
{
file_dirty = dns_dirty = 0;
return;
}
} else {
/* NOTE: need a+ mode to create file if it doesn't exist */
leasestream = daemon->lease_stream = fopen(daemon->lease_file, "a+");
if (!leasestream)
die(_("cannot open or create lease file %s: %s"), daemon->lease_file, EC_FILE);
/* a+ mode leaves pointer at end. */
rewind(leasestream);
}
/* client-id max length is 255 which is 255*2 digits + 254 colons
borrow DNS packet buffer which is always larger than 1000 bytes */
if (leasestream)
while (fscanf(leasestream, "%lu %255s %16s %255s %764s", &ei, daemon->dhcp_buff2,
daemon->namebuff, daemon->dhcp_buff, daemon->packet) == 5) {
hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char*) daemon->dhcp_buff2,
DHCP_CHADDR_MAX, NULL, &hw_type);
/* For backwards compatibility, no explict MAC address type means ether. */
if (hw_type == 0 && hw_len != 0) hw_type = ARPHRD_ETHER;
addr.s_addr = inet_addr(daemon->namebuff);
/* decode hex in place */
clid_len = 0;
if (strcmp(daemon->packet, "*") != 0)
clid_len =
parse_hex(daemon->packet, (unsigned char*) daemon->packet, 255, NULL, NULL);
if (!(lease = lease_allocate(addr))) die(_("too many stored leases"), NULL, EC_MISC);
#ifdef HAVE_BROKEN_RTC
if (ei != 0)
lease->expires = (time_t) ei + now;
else
lease->expires = (time_t) 0;
lease->length = ei;
#else
/* strictly time_t is opaque, but this hack should work on all sane systems,
even when sizeof(time_t) == 8 */
lease->expires = (time_t) ei;
#endif
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)
lease_set_hostname(lease, daemon->dhcp_buff, 0);
/* set these correctly: the "old" events are generated later from
the startup synthesised SIGHUP. */
lease->new = lease->changed = 0;
}
#ifdef HAVE_SCRIPT
if (!daemon->lease_stream) {
int rc = 0;
/* shell returns 127 for "command not found", 126 for bad permissions. */
if (!leasestream || (rc = pclose(leasestream)) == -1 || WEXITSTATUS(rc) == 127 ||
WEXITSTATUS(rc) == 126) {
if (WEXITSTATUS(rc) == 127)
errno = ENOENT;
else if (WEXITSTATUS(rc) == 126)
errno = EACCES;
die(_("cannot run lease-init script %s: %s"), daemon->lease_change_command, EC_FILE);
}
if (WEXITSTATUS(rc) != 0) {
sprintf(daemon->dhcp_buff, "%d", WEXITSTATUS(rc));
die(_("lease-init script returned exit code %s"), daemon->dhcp_buff,
WEXITSTATUS(rc) + EC_INIT_OFFSET);
}
}
#endif
/* Some leases may have expired */
file_dirty = 0;
lease_prune(NULL, now);
dns_dirty = 1;
}
void lease_update_from_configs(void) {
/* changes to the config may change current leases. */
struct dhcp_lease* lease;
struct dhcp_config* config;
char* name;
for (lease = leases; lease; lease = lease->next)
if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len,
lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) &&
(config->flags & CONFIG_NAME) &&
(!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr))
lease_set_hostname(lease, config->hostname, 1);
else if ((name = host_from_dns(lease->addr)))
lease_set_hostname(lease, name, 1); /* updates auth flag only */
}
static void ourprintf(int* errp, char* format, ...) {
va_list ap;
va_start(ap, format);
if (!(*errp) && vfprintf(daemon->lease_stream, format, ap) < 0) *errp = errno;
va_end(ap);
}
void lease_update_file(time_t now) {
struct dhcp_lease* lease;
time_t next_event;
int i, err = 0;
if (file_dirty != 0 && daemon->lease_stream) {
errno = 0;
rewind(daemon->lease_stream);
if (errno != 0 || ftruncate(fileno(daemon->lease_stream), 0) != 0) err = errno;
for (lease = leases; lease; lease = lease->next) {
#ifdef HAVE_BROKEN_RTC
ourprintf(&err, "%u ", lease->length);
#else
ourprintf(&err, "%lu ", (unsigned long) lease->expires);
#endif
if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0)
ourprintf(&err, "%.2x-", lease->hwaddr_type);
for (i = 0; i < lease->hwaddr_len; i++) {
ourprintf(&err, "%.2x", lease->hwaddr[i]);
if (i != lease->hwaddr_len - 1) ourprintf(&err, ":");
}
ourprintf(&err, " %s ", inet_ntoa(lease->addr));
ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*");
if (lease->clid && lease->clid_len != 0) {
for (i = 0; i < lease->clid_len - 1; i++) ourprintf(&err, "%.2x:", lease->clid[i]);
ourprintf(&err, "%.2x\n", lease->clid[i]);
} else
ourprintf(&err, "*\n");
}
if (fflush(daemon->lease_stream) != 0 || fsync(fileno(daemon->lease_stream)) < 0)
err = errno;
if (!err) file_dirty = 0;
}
/* Set alarm for when the first lease expires + slop. */
for (next_event = 0, lease = leases; lease; lease = lease->next)
if (lease->expires != 0 &&
(next_event == 0 || difftime(next_event, lease->expires + 10) > 0.0))
next_event = lease->expires + 10;
if (err) {
if (next_event == 0 || difftime(next_event, LEASE_RETRY + now) > 0.0)
next_event = LEASE_RETRY + now;
my_syslog(MS_DHCP | LOG_ERR, _("failed to write %s: %s (retry in %us)"), daemon->lease_file,
strerror(err), (unsigned int) difftime(next_event, now));
}
if (next_event != 0) alarm((unsigned) difftime(next_event, now));
}
void lease_update_dns(void) {
struct dhcp_lease* lease;
if (daemon->port != 0 && dns_dirty) {
cache_unhash_dhcp();
for (lease = leases; lease; lease = lease->next) {
if (lease->fqdn) cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
if (!(daemon->options & OPT_DHCP_FQDN) && lease->hostname)
cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires);
}
dns_dirty = 0;
}
}
void lease_prune(struct dhcp_lease* target, time_t now) {
struct dhcp_lease *lease, *tmp, **up;
for (lease = leases, up = &leases; lease; lease = tmp) {
tmp = lease->next;
if ((lease->expires != 0 && difftime(now, lease->expires) > 0) || lease == target) {
file_dirty = 1;
if (lease->hostname) dns_dirty = 1;
*up = lease->next; /* unlink */
/* Put on old_leases list 'till we
can run the script */
lease->next = old_leases;
old_leases = lease;
leases_left++;
} else
up = &lease->next;
}
}
struct dhcp_lease* lease_find_by_client(unsigned char* hwaddr, int hw_len, int hw_type,
unsigned char* clid, int clid_len) {
struct dhcp_lease* lease;
if (clid)
for (lease = leases; lease; lease = lease->next)
if (lease->clid && clid_len == lease->clid_len &&
memcmp(clid, lease->clid, clid_len) == 0)
return lease;
for (lease = leases; lease; lease = lease->next)
if ((!lease->clid || !clid) && hw_len != 0 && lease->hwaddr_len == hw_len &&
lease->hwaddr_type == hw_type && memcmp(hwaddr, lease->hwaddr, hw_len) == 0)
return lease;
return NULL;
}
struct dhcp_lease* lease_find_by_addr(struct in_addr addr) {
struct dhcp_lease* lease;
for (lease = leases; lease; lease = lease->next)
if (lease->addr.s_addr == addr.s_addr) return lease;
return NULL;
}
struct dhcp_lease* lease_allocate(struct in_addr addr) {
struct dhcp_lease* lease;
if (!leases_left || !(lease = whine_malloc(sizeof(struct dhcp_lease)))) return NULL;
memset(lease, 0, sizeof(struct dhcp_lease));
lease->new = 1;
lease->addr = addr;
lease->hwaddr_len = 256; /* illegal value */
lease->expires = 1;
#ifdef HAVE_BROKEN_RTC
lease->length = 0xffffffff; /* illegal value */
#endif
lease->next = leases;
leases = lease;
file_dirty = 1;
leases_left--;
return lease;
}
void lease_set_expires(struct dhcp_lease* lease, unsigned int len, time_t now) {
time_t exp = now + (time_t) len;
if (len == 0xffffffff) {
exp = 0;
len = 0;
}
if (exp != lease->expires) {
dns_dirty = 1;
lease->expires = exp;
#ifndef HAVE_BROKEN_RTC
lease->aux_changed = file_dirty = 1;
#endif
}
#ifdef HAVE_BROKEN_RTC
if (len != lease->length) {
lease->length = len;
lease->aux_changed = file_dirty = 1;
}
#endif
}
void lease_set_hwaddr(struct dhcp_lease* lease, unsigned char* hwaddr, unsigned char* clid,
int hw_len, int hw_type, int clid_len) {
if (hw_len != lease->hwaddr_len || hw_type != lease->hwaddr_type ||
(hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0)) {
memcpy(lease->hwaddr, hwaddr, hw_len);
lease->hwaddr_len = hw_len;
lease->hwaddr_type = hw_type;
lease->changed = file_dirty = 1; /* run script on change */
}
/* only update clid when one is available, stops packets
without a clid removing the record. Lease init uses
clid_len == 0 for no clid. */
if (clid_len != 0 && clid) {
if (!lease->clid) lease->clid_len = 0;
if (lease->clid_len != clid_len) {
lease->aux_changed = file_dirty = 1;
free(lease->clid);
if (!(lease->clid = whine_malloc(clid_len))) return;
} else if (memcmp(lease->clid, clid, clid_len) != 0)
lease->aux_changed = file_dirty = 1;
lease->clid_len = clid_len;
memcpy(lease->clid, clid, clid_len);
}
}
static void kill_name(struct dhcp_lease* lease) {
/* run script to say we lost our old name */
/* this shouldn't happen unless updates are very quick and the
script very slow, we just avoid a memory leak if it does. */
free(lease->old_hostname);
/* If we know the fqdn, pass that. The helper will derive the
unqualified name from it, free the unqulaified name here. */
if (lease->fqdn) {
lease->old_hostname = lease->fqdn;
free(lease->hostname);
} else
lease->old_hostname = lease->hostname;
lease->hostname = lease->fqdn = NULL;
}
void lease_set_hostname(struct dhcp_lease* lease, char* name, int auth) {
struct dhcp_lease* lease_tmp;
char *new_name = NULL, *new_fqdn = NULL;
if (lease->hostname && name && hostname_isequal(lease->hostname, name)) {
lease->auth_name = auth;
return;
}
if (!name && !lease->hostname) return;
/* If a machine turns up on a new net without dropping the old lease,
or two machines claim the same name, then we end up with two interfaces with
the same name. Check for that here and remove the name from the old lease.
Don't allow a name from the client to override a name from dnsmasq config. */
if (name) {
if ((new_name = whine_malloc(strlen(name) + 1))) {
char* suffix = get_domain(lease->addr);
strcpy(new_name, name);
if (suffix && (new_fqdn = whine_malloc(strlen(new_name) + strlen(suffix) + 2))) {
strcpy(new_fqdn, name);
strcat(new_fqdn, ".");
strcat(new_fqdn, suffix);
}
}
/* Depending on mode, we check either unqualified name or FQDN. */
for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next) {
if (daemon->options & OPT_DHCP_FQDN) {
if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn))
continue;
} else {
if (!new_name || !lease_tmp->hostname ||
!hostname_isequal(lease_tmp->hostname, new_name))
continue;
}
if (lease_tmp->auth_name && !auth) {
free(new_name);
free(new_fqdn);
return;
}
kill_name(lease_tmp);
break;
}
}
if (lease->hostname) kill_name(lease);
lease->hostname = new_name;
lease->fqdn = new_fqdn;
lease->auth_name = auth;
file_dirty = 1;
dns_dirty = 1;
lease->changed = 1; /* run script on change */
}
void lease_set_interface(struct dhcp_lease* lease, int interface) {
if (lease->last_interface == interface) return;
lease->last_interface = interface;
lease->changed = 1;
}
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.
remove them here, after calling the lease change
script. Also run the lease change script on new/modified leases.
Return zero if nothing to do. */
int do_script_run(time_t now) {
struct dhcp_lease* lease;
if (old_leases) {
lease = old_leases;
/* If the lease still has an old_hostname, do the "old" action on that first */
if (lease->old_hostname) {
#ifdef HAVE_SCRIPT
queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
#endif
free(lease->old_hostname);
lease->old_hostname = NULL;
return 1;
} else {
kill_name(lease);
#ifdef HAVE_SCRIPT
queue_script(ACTION_DEL, lease, lease->old_hostname, now);
#endif
old_leases = lease->next;
free(lease->old_hostname);
free(lease->clid);
free(lease->vendorclass);
free(lease->userclass);
free(lease->supplied_hostname);
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) {
#ifdef HAVE_SCRIPT
queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
#endif
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))) {
#ifdef HAVE_SCRIPT
queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease,
lease->fqdn ? lease->fqdn : lease->hostname, now);
#endif
lease->new = lease->changed = lease->aux_changed = 0;
/* these are used for the "add" call, then junked, since they're not in the database */
free(lease->vendorclass);
lease->vendorclass = NULL;
free(lease->userclass);
lease->userclass = NULL;
free(lease->supplied_hostname);
lease->supplied_hostname = NULL;
return 1;
}
return 0; /* nothing to do */
}
#endif
./src/dhcp.c 0000644 0000000 0000000 00000066716 14256750714 011615 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "dnsmasq.h"
#ifdef HAVE_DHCP
struct iface_param {
struct in_addr relay, primary;
struct dhcp_context* current;
int ind;
};
static int complete_context(struct in_addr local, int if_index, struct in_addr netmask,
struct in_addr broadcast, void* vparam);
void dhcp_init(void) {
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in saddr;
int oneopt = 1;
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
int mtu = IP_PMTUDISC_DONT;
#endif
if (fd == -1) die(_("cannot create DHCP socket: %s"), NULL, EC_BADNET);
if (!fix_fd(fd) ||
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
#endif
#if defined(HAVE_LINUX_NETWORK)
setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
#else
setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
#endif
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);
/* When bind-interfaces is set, there might be more than one dnmsasq
instance binding port 67. That's OK if they serve different networks.
Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD. */
if (daemon->options & OPT_NOWILD) {
#ifdef SO_REUSEPORT
int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt));
#else
int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
#endif
if (rc == -1)
die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
}
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(daemon->dhcp_server_port);
saddr.sin_addr.s_addr = INADDR_ANY;
if (bind(fd, (struct sockaddr*) &saddr, sizeof(struct sockaddr_in)))
die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
#ifdef __ANDROID__
if (setsockopt(fd, SOL_SOCKET, SO_MARK, &daemon->listen_mark, sizeof(daemon->listen_mark)) == -1)
die(_("failed to set DHCP socket mark: %s"), NULL, EC_BADNET);
#endif /* __ANDROID__ */
daemon->dhcpfd = fd;
check_dhcp_hosts(1);
daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet);
daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
}
void dhcp_packet(time_t now) {
struct dhcp_packet* mess;
struct dhcp_context* context;
struct iname* tmp;
struct ifreq ifr;
struct msghdr msg;
struct sockaddr_in dest;
struct cmsghdr* cmptr;
struct iovec iov;
ssize_t sz;
int iface_index = 0, unicast_dest = 0, is_inform = 0;
struct in_addr iface_addr, *addrp = NULL;
struct iface_param parm;
union {
struct cmsghdr align; /* this ensures alignment */
#if defined(HAVE_LINUX_NETWORK)
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#endif
} control_u;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &daemon->dhcp_packet;
msg.msg_iovlen = 1;
while (1) {
msg.msg_flags = 0;
while ((sz = recvmsg(daemon->dhcpfd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR)
;
if (sz == -1) return;
if (!(msg.msg_flags & MSG_TRUNC)) break;
/* Very new Linux kernels return the actual size needed,
older ones always return truncated size */
if ((size_t) sz == daemon->dhcp_packet.iov_len) {
if (!expand_buf(&daemon->dhcp_packet, sz + 100)) return;
} else {
expand_buf(&daemon->dhcp_packet, sz);
break;
}
}
/* expand_buf may have moved buffer */
mess = (struct dhcp_packet*) daemon->dhcp_packet.iov_base;
msg.msg_controllen = sizeof(control_u);
msg.msg_control = control_u.control;
msg.msg_flags = 0;
msg.msg_name = &dest;
msg.msg_namelen = sizeof(dest);
while ((sz = recvmsg(daemon->dhcpfd, &msg, 0)) == -1 && errno == EINTR)
;
if ((msg.msg_flags & MSG_TRUNC) || sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
return;
#if defined(HAVE_LINUX_NETWORK)
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) {
iface_index = ((struct in_pktinfo*) CMSG_DATA(cmptr))->ipi_ifindex;
if (((struct in_pktinfo*) CMSG_DATA(cmptr))->ipi_addr.s_addr != INADDR_BROADCAST)
unicast_dest = 1;
}
#endif
if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name)) return;
#ifdef MSG_BCAST
/* OpenBSD tells us when a packet was broadcast */
if (!(msg.msg_flags & MSG_BCAST)) unicast_dest = 1;
#endif
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1) {
addrp = &iface_addr;
iface_addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr;
}
if (!iface_check(AF_INET, (struct all_addr*) addrp, ifr.ifr_name, &iface_index)) return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0)) return;
/* interface may have been changed by alias in iface_check */
if (!addrp) {
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1) {
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"),
ifr.ifr_name);
return;
} else
iface_addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr;
}
/* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp; context; context = context->next) context->current = context;
parm.relay = mess->giaddr;
parm.primary = iface_addr;
parm.current = NULL;
parm.ind = iface_index;
if (!iface_enumerate(&parm, complete_context, NULL)) return;
lease_prune(NULL, now); /* lose any expired leases */
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t) sz, now,
unicast_dest, &is_inform);
lease_update_file(now);
lease_update_dns();
if (iov.iov_len == 0) return;
msg.msg_name = &dest;
msg.msg_namelen = sizeof(dest);
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_iov = &iov;
iov.iov_base = daemon->dhcp_packet.iov_base;
/* packet buffer may have moved */
mess = (struct dhcp_packet*) daemon->dhcp_packet.iov_base;
if (mess->giaddr.s_addr) {
/* Send to BOOTP relay */
dest.sin_port = htons(daemon->dhcp_server_port);
dest.sin_addr = mess->giaddr;
} else if (mess->ciaddr.s_addr) {
/* If the client's idea of its own address tallys with
the source address in the request packet, we believe the
source port too, and send back to that. If we're replying
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(daemon->dhcp_client_port);
dest.sin_addr = mess->ciaddr;
}
}
#if defined(HAVE_LINUX_NETWORK)
else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0) {
/* broadcast to 255.255.255.255 (or mac address invalid) */
struct in_pktinfo* pkt;
msg.msg_control = control_u.control;
msg.msg_controllen = sizeof(control_u);
cmptr = CMSG_FIRSTHDR(&msg);
pkt = (struct in_pktinfo*) CMSG_DATA(cmptr);
pkt->ipi_ifindex = iface_index;
pkt->ipi_spec_dst.s_addr = 0;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = SOL_IP;
cmptr->cmsg_type = IP_PKTINFO;
dest.sin_addr.s_addr = INADDR_BROADCAST;
dest.sin_port = htons(daemon->dhcp_client_port);
} else {
/* unicast to unconfigured client. Inject mac address direct into ARP cache.
struct sockaddr limits size to 14 bytes. */
struct arpreq req;
dest.sin_addr = mess->yiaddr;
dest.sin_port = htons(daemon->dhcp_client_port);
*((struct sockaddr_in*) &req.arp_pa) = dest;
req.arp_ha.sa_family = mess->htype;
memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
strncpy(req.arp_dev, ifr.ifr_name, 16);
req.arp_flags = ATF_COM;
ioctl(daemon->dhcpfd, SIOCSARP, &req);
}
#endif
while (sendmsg(daemon->dhcpfd, &msg, 0) == -1 && retry_send())
;
}
/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
of each interface (and any relay address) and does the following things:
1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
3) Fills in local (this host) and router (this host or relay) addresses.
4) Links contexts which are valid for hosts directly connected to the arrival interface on
->current.
Note that the current chain may be superceded later for configured hosts or those coming via
gateways. */
static int complete_context(struct in_addr local, int if_index, struct in_addr netmask,
struct in_addr broadcast, void* vparam) {
struct dhcp_context* context;
struct iface_param* param = vparam;
for (context = daemon->dhcp; context; context = context->next) {
if (!(context->flags & CONTEXT_NETMASK) && (is_same_net(local, context->start, netmask) ||
is_same_net(local, context->end, netmask))) {
if (context->netmask.s_addr != netmask.s_addr &&
!(is_same_net(local, context->start, netmask) &&
is_same_net(local, context->end, netmask))) {
strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
my_syslog(MS_DHCP | LOG_WARNING,
_("DHCP range %s -- %s is not consistent with netmask %s"),
daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
}
context->netmask = netmask;
}
if (context->netmask.s_addr) {
if (is_same_net(local, context->start, context->netmask) &&
is_same_net(local, context->end, context->netmask)) {
/* link it onto the current chain if we've not seen it before */
if (if_index == param->ind && context->current == context) {
context->router = local;
context->local = local;
context->current = param->current;
param->current = context;
}
if (!(context->flags & CONTEXT_BRDCAST)) {
if (is_same_net(broadcast, context->start, context->netmask))
context->broadcast = broadcast;
else
context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
}
} else if (param->relay.s_addr &&
is_same_net(param->relay, context->start, context->netmask)) {
context->router = param->relay;
context->local = param->primary;
/* fill in missing broadcast addresses for relayed ranges */
if (!(context->flags & CONTEXT_BRDCAST))
context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
}
}
}
return 1;
}
struct dhcp_context* address_available(struct dhcp_context* context, struct in_addr taddr,
struct dhcp_netid* netids) {
/* Check is an address is OK for this network, check all
possible ranges. Make sure that the address isn't in use
by the server itself. */
unsigned int start, end, addr = ntohl(taddr.s_addr);
struct dhcp_context* tmp;
for (tmp = context; tmp; tmp = tmp->current)
if (taddr.s_addr == context->router.s_addr) return NULL;
for (tmp = context; tmp; tmp = tmp->current) {
start = ntohl(tmp->start.s_addr);
end = ntohl(tmp->end.s_addr);
if (!(tmp->flags & CONTEXT_STATIC) && addr >= start && addr <= end &&
match_netid(tmp->filter, netids, 1))
return tmp;
}
return NULL;
}
struct dhcp_context* narrow_context(struct dhcp_context* context, struct in_addr taddr,
struct dhcp_netid* netids) {
/* We start of with a set of possible contexts, all on the current physical interface.
These are chained on ->current.
Here we have an address, and return the actual context correponding to that
address. Note that none may fit, if the address came a dhcp-host and is outside
any dhcp-range. In that case we return a static range if possible, or failing that,
any context on the correct subnet. (If there's more than one, this is a dodgy
configuration: maybe there should be a warning.) */
struct dhcp_context* tmp;
if (!(tmp = address_available(context, taddr, netids))) {
for (tmp = context; tmp; tmp = tmp->current)
if (is_same_net(taddr, tmp->start, tmp->netmask) && (tmp->flags & CONTEXT_STATIC))
break;
if (!tmp)
for (tmp = context; tmp; tmp = tmp->current)
if (is_same_net(taddr, tmp->start, tmp->netmask)) break;
}
/* Only one context allowed now */
if (tmp) tmp->current = NULL;
return tmp;
}
struct dhcp_config* config_find_by_address(struct dhcp_config* configs, struct in_addr addr) {
struct dhcp_config* config;
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr) return config;
return NULL;
}
/* Is every member of check matched by a member of pool?
If tagnotneeded, untagged is OK */
int match_netid(struct dhcp_netid* check, struct dhcp_netid* pool, int tagnotneeded) {
struct dhcp_netid* tmp1;
if (!check && !tagnotneeded) return 0;
for (; check; check = check->next) {
if (check->net[0] != '#') {
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp(check->net, tmp1->net) == 0) break;
if (!tmp1) return 0;
} else
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp((check->net) + 1, tmp1->net) == 0) return 0;
}
return 1;
}
int address_allocate(struct dhcp_context* context, struct in_addr* addrp, unsigned char* hwaddr,
int hw_len, struct dhcp_netid* netids, time_t now) {
/* Find a free address: exclude anything in use and anything allocated to
a particular hwaddr/clientid/hostname in our configuration.
Try to return from contexts which match netids first. */
struct in_addr start, addr;
struct dhcp_context *c, *d;
int i, pass;
unsigned int j;
/* hash hwaddr */
for (j = 0, i = 0; i < hw_len; i++) j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
for (pass = 0; pass <= 1; pass++)
for (c = context; c; c = c->current)
if (c->flags & CONTEXT_STATIC)
continue;
else if (!match_netid(c->filter, netids, pass))
continue;
else {
/* pick a seed based on hwaddr then iterate until we find a free address. */
start.s_addr = addr.s_addr = htonl(
ntohl(c->start.s_addr) +
((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
do {
/* eliminate addresses in use by the server. */
for (d = context; d; d = d->current)
if (addr.s_addr == d->router.s_addr) break;
/* Addresses which end in .255 and .0 are broken in Windows even when using
supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
then 192.168.0.255 is a valid IP address, but not for Windows as it's
in the class C range. See KB281579. We therefore don't allocate these
addresses to avoid hard-to-diagnose problems. Thanks Bill. */
if (!d && !lease_find_by_addr(addr) &&
!config_find_by_address(daemon->dhcp_conf, addr) &&
(!IN_CLASSC(ntohl(addr.s_addr)) || ((ntohl(addr.s_addr) & 0xff) != 0xff &&
((ntohl(addr.s_addr) & 0xff) != 0x0)))) {
struct ping_result *r, *victim = NULL;
int count,
max = (int) (0.6 * (((float) PING_CACHE_TIME) / ((float) PING_WAIT)));
*addrp = addr;
if (daemon->options & OPT_NO_PING) return 1;
/* check if we failed to ping addr sometime in the last
PING_CACHE_TIME seconds. If so, assume the same situation still exists.
This avoids problems when a stupid client bangs
on us repeatedly. As a final check, if we did more
than 60% of the possible ping checks in the last
PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
for (count = 0, r = daemon->ping_results; r; r = r->next)
if (difftime(now, r->time) > (float) PING_CACHE_TIME)
victim = r; /* old record */
else if (++count == max || r->addr.s_addr == addr.s_addr)
return 1;
if (icmp_ping(addr))
/* address in use: perturb address selection so that we are
less likely to try this address again. */
c->addr_epoch++;
else {
/* at this point victim may hold an expired record */
if (!victim) {
if ((victim = whine_malloc(sizeof(struct ping_result)))) {
victim->next = daemon->ping_results;
daemon->ping_results = victim;
}
}
/* record that this address is OK for 30s
without more ping checks */
if (victim) {
victim->addr = addr;
victim->time = now;
}
return 1;
}
}
addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1)) addr = c->start;
} while (addr.s_addr != start.s_addr);
}
return 0;
}
static int is_addr_in_context(struct dhcp_context* context, struct dhcp_config* config) {
if (!context) /* called via find_config() from lease_update_from_configs() */
return 1;
if (!(config->flags & CONFIG_ADDR)) return 1;
for (; context; context = context->current)
if (is_same_net(config->addr, context->start, context->netmask)) return 1;
return 0;
}
int config_has_mac(struct dhcp_config* config, unsigned char* hwaddr, int len, int type) {
struct hwaddr_config* conf_addr;
for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
if (conf_addr->wildcard_mask == 0 && conf_addr->hwaddr_len == len &&
(conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
return 1;
return 0;
}
struct dhcp_config* find_config(struct dhcp_config* configs, struct dhcp_context* context,
unsigned char* clid, int clid_len, unsigned char* hwaddr,
int hw_len, int hw_type, char* hostname) {
int count, new;
struct dhcp_config *config, *candidate;
struct hwaddr_config* conf_addr;
if (clid)
for (config = configs; config; config = config->next)
if (config->flags & CONFIG_CLID) {
if (config->clid_len == clid_len && memcmp(config->clid, clid, clid_len) == 0 &&
is_addr_in_context(context, config))
return config;
/* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
cope with that here */
if (*clid == 0 && config->clid_len == clid_len - 1 &&
memcmp(config->clid, clid + 1, clid_len - 1) == 0 &&
is_addr_in_context(context, config))
return config;
}
for (config = configs; config; config = config->next)
if (config_has_mac(config, hwaddr, hw_len, hw_type) && is_addr_in_context(context, config))
return config;
if (hostname && context)
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, hostname) &&
is_addr_in_context(context, config))
return config;
/* use match with fewest wildcast octets */
for (candidate = NULL, count = 0, config = configs; config; config = config->next)
if (is_addr_in_context(context, config))
for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
if (conf_addr->wildcard_mask != 0 && conf_addr->hwaddr_len == hw_len &&
(conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
(new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len,
conf_addr->wildcard_mask)) > count) {
count = new;
candidate = config;
}
return candidate;
}
void check_dhcp_hosts(int fatal) {
/* 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,
since the address is reserved by the other one -> protocol loop.
Also check that FQDNs match the domain we are using. */
struct dhcp_config *configs, *cp;
for (configs = daemon->dhcp_conf; configs; configs = configs->next) {
char* domain;
if ((configs->flags & DHOPT_BANK) || fatal) {
for (cp = configs->next; cp; cp = cp->next)
if ((configs->flags & cp->flags & CONFIG_ADDR) &&
configs->addr.s_addr == cp->addr.s_addr) {
if (fatal)
die(_("duplicate IP address %s in dhcp-config directive."),
inet_ntoa(cp->addr), EC_BADCONF);
else
my_syslog(MS_DHCP | LOG_ERR, _("duplicate IP address %s in %s."),
inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
configs->flags &= ~CONFIG_ADDR;
}
/* split off domain part */
if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
configs->domain = domain;
}
}
}
void dhcp_update_configs(struct dhcp_config* configs) {
/* Some people like to keep all static IP addresses in /etc/hosts.
This goes through /etc/hosts and sets static addresses for any DHCP config
records which don't have an address and whose name matches.
We take care to maintain the invariant that any IP address can appear
in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
restore the status-quo ante first. */
struct dhcp_config* config;
struct crec* crec;
for (config = configs; config; config = config->next)
if (config->flags & CONFIG_ADDR_HOSTS) config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
if (daemon->port != 0)
for (config = configs; config; config = config->next)
if (!(config->flags & CONFIG_ADDR) && (config->flags & CONFIG_NAME) &&
(crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
(crec->flags & F_HOSTS)) {
if (cache_find_by_name(crec, config->hostname, 0, F_IPV4)) {
/* use primary (first) address */
while (crec && !(crec->flags & F_REVERSE))
crec = cache_find_by_name(crec, config->hostname, 0, F_IPV4);
if (!crec) continue; /* should be never */
my_syslog(MS_DHCP | LOG_WARNING,
_("%s has more than one address in hostsfile, using %s for DHCP"),
config->hostname, inet_ntoa(crec->addr.addr.addr.addr4));
}
if (config_find_by_address(configs, crec->addr.addr.addr.addr4))
my_syslog(MS_DHCP | LOG_WARNING,
_("duplicate IP address %s (%s) in dhcp-config directive"),
inet_ntoa(crec->addr.addr.addr.addr4), config->hostname);
else {
config->addr = crec->addr.addr.addr.addr4;
config->flags |= CONFIG_ADDR | CONFIG_ADDR_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
it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
so check here that the domain name is legal as a hostname. */
char* host_from_dns(struct in_addr addr) {
struct crec* lookup;
char* hostname = NULL;
char *d1, *d2;
if (daemon->port == 0) return NULL; /* DNS disabled. */
lookup = cache_find_by_addr(NULL, (struct all_addr*) &addr, 0, F_IPV4);
if (lookup && (lookup->flags & F_HOSTS)) {
hostname = daemon->dhcp_buff;
strncpy(hostname, cache_get_name(lookup), 256);
hostname[255] = 0;
d1 = strip_hostname(hostname);
d2 = get_domain(addr);
if (!legal_hostname(hostname) || (d1 && (!d2 || !hostname_isequal(d1, d2))))
hostname = NULL;
}
return hostname;
}
/* return domain or NULL if none. */
char* strip_hostname(char* hostname) {
char* dot = strchr(hostname, '.');
if (!dot) return NULL;
*dot = 0; /* truncate */
if (strlen(dot + 1) != 0) return dot + 1;
return NULL;
}
#endif
./src/option.c 0000644 0000000 0000000 00000271557 14256750714 012210 0 ustar root root /* dnsmasq is Copyright (c) 2000-2009 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, or
(at your option) version 3 dated 29 June, 2007.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
/* define this to get facilitynames */
#define SYSLOG_NAMES
#include
#include "dnsmasq.h"
static volatile int mem_recover = 0;
static jmp_buf mem_jmp;
static void one_file(char* file, int nest, int hard_opt);
#ifndef HAVE_GETOPT_LONG
struct myoption {
const char* name;
int has_arg;
int* flag;
int val;
};
#endif
#define OPTSTRING \
"951yZDNLERKzowefnbvhdkqr: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:3:"
/* options which don't have a one-char version */
#define LOPT_RELOAD 256
#define LOPT_NO_NAMES 257
#define LOPT_TFTP 258
#define LOPT_SECURE 259
#define LOPT_PREFIX 260
#define LOPT_PTR 261
#define LOPT_BRIDGE 262
#define LOPT_TFTP_MAX 263
#define LOPT_FORCE 264
#define LOPT_NOBLOCK 265
#define LOPT_LOG_OPTS 266
#define LOPT_MAX_LOGS 267
#define LOPT_CIRCUIT 268
#define LOPT_REMOTE 269
#define LOPT_SUBSCR 270
#define LOPT_INTNAME 271
#define LOPT_BANK 272
#define LOPT_DHCP_HOST 273
#define LOPT_APREF 274
#define LOPT_OVERRIDE 275
#define LOPT_TFTPPORTS 276
#define LOPT_REBIND 277
#define LOPT_NOLAST 278
#define LOPT_OPTS 279
#define LOPT_DHCP_OPTS 280
#define LOPT_MATCH 281
#define LOPT_BROADCAST 282
#define LOPT_NEGTTL 283
#define LOPT_ALTPORT 284
#define LOPT_SCRIPTUSR 285
#define LOPT_LOCAL 286
#define LOPT_NAPTR 287
#define LOPT_MINPORT 288
#define LOPT_DHCP_FQDN 289
#define LOPT_CNAME 290
#define LOPT_PXE_PROMT 291
#define LOPT_PXE_SERV 292
#define LOPT_TEST 293
#define LOPT_LISTNMARK 294
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
#else
static const struct myoption opts[] =
#endif
{{"version", 0, 0, 'v'},
{"no-hosts", 0, 0, 'h'},
{"no-poll", 0, 0, 'n'},
{"help", 0, 0, 'w'},
{"no-daemon", 0, 0, 'd'},
{"log-queries", 0, 0, 'q'},
{"user", 1, 0, 'u'},
{"group", 2, 0, 'g'},
{"resolv-file", 2, 0, 'r'},
{"mx-host", 1, 0, 'm'},
{"mx-target", 1, 0, 't'},
{"cache-size", 2, 0, 'c'},
{"port", 1, 0, 'p'},
{"dhcp-leasefile", 2, 0, 'l'},
{"dhcp-lease", 1, 0, 'l'},
{"dhcp-host", 1, 0, 'G'},
{"dhcp-range", 1, 0, 'F'},
{"dhcp-option", 1, 0, 'O'},
{"dhcp-boot", 1, 0, 'M'},
{"domain", 1, 0, 's'},
{"domain-suffix", 1, 0, 's'},
{"interface", 1, 0, 'i'},
{"listen-address", 1, 0, 'a'},
{"bogus-priv", 0, 0, 'b'},
{"bogus-nxdomain", 1, 0, 'B'},
{"selfmx", 0, 0, 'e'},
{"filterwin2k", 0, 0, 'f'},
{"pid-file", 2, 0, 'x'},
{"strict-order", 0, 0, 'o'},
{"server", 1, 0, 'S'},
{"local", 1, 0, LOPT_LOCAL},
{"address", 1, 0, 'A'},
{"conf-file", 2, 0, 'C'},
{"no-resolv", 0, 0, 'R'},
{"expand-hosts", 0, 0, 'E'},
{"localmx", 0, 0, 'L'},
{"local-ttl", 1, 0, 'T'},
{"no-negcache", 0, 0, 'N'},
{"addn-hosts", 1, 0, 'H'},
{"query-port", 1, 0, 'Q'},
{"except-interface", 1, 0, 'I'},
{"no-dhcp-interface", 1, 0, '2'},
{"domain-needed", 0, 0, 'D'},
{"dhcp-lease-max", 1, 0, 'X'},
{"bind-interfaces", 0, 0, 'z'},
{"alias", 1, 0, 'V'},
{"dhcp-vendorclass", 1, 0, 'U'},
{"dhcp-userclass", 1, 0, 'j'},
{"dhcp-ignore", 1, 0, 'J'},
{"edns-packet-max", 1, 0, 'P'},
{"keep-in-foreground", 0, 0, 'k'},
{"dhcp-authoritative", 0, 0, 'K'},
{"srv-host", 1, 0, 'W'},
{"localise-queries", 0, 0, 'y'},
{"txt-record", 1, 0, 'Y'},
{"enable-dbus", 0, 0, '1'},
{"bootp-dynamic", 2, 0, '3'},
{"dhcp-mac", 1, 0, '4'},
{"no-ping", 0, 0, '5'},
{"dhcp-script", 1, 0, '6'},
{"conf-dir", 1, 0, '7'},
{"log-facility", 1, 0, '8'},
{"leasefile-ro", 0, 0, '9'},
{"dns-forward-max", 1, 0, '0'},
{"clear-on-reload", 0, 0, LOPT_RELOAD},
{"dhcp-ignore-names", 2, 0, LOPT_NO_NAMES},
{"enable-tftp", 0, 0, LOPT_TFTP},
{"tftp-secure", 0, 0, LOPT_SECURE},
{"tftp-unique-root", 0, 0, LOPT_APREF},
{"tftp-root", 1, 0, LOPT_PREFIX},
{"tftp-max", 1, 0, LOPT_TFTP_MAX},
{"ptr-record", 1, 0, LOPT_PTR},
{"naptr-record", 1, 0, LOPT_NAPTR},
{"bridge-interface", 1, 0, LOPT_BRIDGE},
{"dhcp-option-force", 1, 0, LOPT_FORCE},
{"tftp-no-blocksize", 0, 0, LOPT_NOBLOCK},
{"log-dhcp", 0, 0, LOPT_LOG_OPTS},
{"log-async", 2, 0, LOPT_MAX_LOGS},
{"dhcp-circuitid", 1, 0, LOPT_CIRCUIT},
{"dhcp-remoteid", 1, 0, LOPT_REMOTE},
{"dhcp-subscrid", 1, 0, LOPT_SUBSCR},
{"interface-name", 1, 0, LOPT_INTNAME},
{"dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST},
{"dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS},
{"dhcp-no-override", 0, 0, LOPT_OVERRIDE},
{"tftp-port-range", 1, 0, LOPT_TFTPPORTS},
{"stop-dns-rebind", 0, 0, LOPT_REBIND},
{"all-servers", 0, 0, LOPT_NOLAST},
{"dhcp-match", 1, 0, LOPT_MATCH},
{"dhcp-broadcast", 1, 0, LOPT_BROADCAST},
{"neg-ttl", 1, 0, LOPT_NEGTTL},
{"dhcp-alternate-port", 2, 0, LOPT_ALTPORT},
{"dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR},
{"min-port", 1, 0, LOPT_MINPORT},
{"dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN},
{"cname", 1, 0, LOPT_CNAME},
{"pxe-prompt", 1, 0, LOPT_PXE_PROMT},
{"pxe-service", 1, 0, LOPT_PXE_SERV},
#ifdef __ANDROID__
{"listen-mark", 1, 0, LOPT_LISTNMARK},
#endif /* __ANDROID__ */
{"test", 0, 0, LOPT_TEST},
{NULL, 0, 0, 0}};
/* These must have more the one '1' bit */
#define ARG_DUP 3
#define ARG_ONE 5
#define ARG_USED_CL 7
#define ARG_USED_FILE 9
static struct {
int opt;
unsigned int rept;
char* const flagdesc;
char* const desc;
char* const arg;
} usage[] = {
{'a', ARG_DUP, "ipaddr", gettext_noop("Specify local address(es) to listen on."), NULL},
{'A', ARG_DUP, "/domain/ipaddr",
gettext_noop("Return ipaddr for all hosts in specified domains."), NULL},
{'b', OPT_BOGUSPRIV, NULL,
gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL},
{'B', ARG_DUP, "ipaddr", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."),
NULL},
{'c', ARG_ONE, "cachesize",
gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$"},
{'C', ARG_DUP, "path", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE},
{'d', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."),
NULL},
{'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL},
{'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL},
{'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."),
NULL},
{'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."),
NULL},
{'F', ARG_DUP, "ipaddr,ipaddr,time",
gettext_noop("Enable DHCP in the range given with lease duration."), NULL},
{'g', ARG_ONE, "groupname",
gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP},
{'G', ARG_DUP, "", gettext_noop("Set address or hostname for a specified machine."),
NULL},
{LOPT_DHCP_HOST, ARG_ONE, "", gettext_noop("Read DHCP host specs from file"), NULL},
{LOPT_DHCP_OPTS, ARG_ONE, "", gettext_noop("Read DHCP option specs from file"), NULL},
{'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE},
{'H', ARG_DUP, "path", gettext_noop("Specify a hosts file to be read in addition to %s."),
HOSTSFILE},
{'i', ARG_DUP, "interface", gettext_noop("Specify interface(s) to listen on."), NULL},
{'I', ARG_DUP, "int", gettext_noop("Specify interface(s) NOT to listen on."), NULL},
{'j', ARG_DUP, ",", gettext_noop("Map DHCP user class to tag."), NULL},
{LOPT_CIRCUIT, ARG_DUP, ",", gettext_noop("Map RFC3046 circuit-id to tag."), NULL},
{LOPT_REMOTE, ARG_DUP, ",", gettext_noop("Map RFC3046 remote-id to tag."), NULL},
{LOPT_SUBSCR, ARG_DUP, ",", gettext_noop("Map RFC3993 subscriber-id to tag."),
NULL},
{'J', ARG_DUP, "=[,]", gettext_noop("Don't do DHCP for hosts with tag set."), NULL},
{LOPT_BROADCAST, ARG_DUP, "=[,]",
gettext_noop("Force broadcast replies for hosts with tag set."), NULL},
{'k', OPT_NO_FORK, NULL,
gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL},
{'K', OPT_AUTHORITATIVE, NULL,
gettext_noop("Assume we are the only DHCP server on the local network."), NULL},
{'l', ARG_ONE, "path", gettext_noop("Specify where to store DHCP leases (defaults to %s)."),
LEASEFILE},
{'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL},
{'m', ARG_DUP, "host_name,target,pref", gettext_noop("Specify an MX record."), NULL},
{'M', ARG_DUP, "", gettext_noop("Specify BOOTP options to DHCP server."), NULL},
{'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."),
RESOLVFILE},
{'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL},
{'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."),
RESOLVFILE},
{'O', ARG_DUP, "", gettext_noop("Specify options to be sent to DHCP clients."), NULL},
{LOPT_FORCE, ARG_DUP, "",
gettext_noop("DHCP option sent even if the client does not request it."), NULL},
{'p', ARG_ONE, "number",
gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL},
{'P', ARG_ONE, "",
gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*"},
{'q', OPT_LOG, NULL, gettext_noop("Log DNS queries."), NULL},
{'Q', ARG_ONE, "number", gettext_noop("Force the originating port for upstream DNS queries."),
NULL},
{'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL},
{'r', ARG_DUP, "path", gettext_noop("Specify path to resolv.conf (defaults to %s)."),
RESOLVFILE},
{'S', ARG_DUP, "/domain/ipaddr",
gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL},
{LOPT_LOCAL, ARG_DUP, "/domain/", gettext_noop("Never forward queries to specified domains."),
NULL},
{'s', ARG_DUP, "[,]",
gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL},
{'t', ARG_ONE, "host_name", gettext_noop("Specify default target in an MX record."), NULL},
{'T', ARG_ONE, "time",
gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL},
{LOPT_NEGTTL, ARG_ONE, "time",
gettext_noop("Specify time-to-live in seconds for negative caching."), NULL},
{'u', ARG_ONE, "username", gettext_noop("Change to this user after startup. (defaults to %s)."),
CHUSER},
{'U', ARG_DUP, ",", gettext_noop("Map DHCP vendor class to tag."), NULL},
{'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL},
{'V', ARG_DUP, "addr,addr,mask",
gettext_noop("Translate IPv4 addresses from upstream servers."), NULL},
{'W', ARG_DUP, "name,target,...", gettext_noop("Specify a SRV record."), NULL},
{'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp for known DHCP options."),
NULL},
{'x', ARG_ONE, "path", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE},
{'X', ARG_ONE, "number",
gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&"},
{'y', OPT_LOCALISE, NULL,
gettext_noop("Answer DNS queries based on the interface a query was sent to."), NULL},
{'Y', ARG_DUP, "name,txt....", gettext_noop("Specify TXT DNS record."), NULL},
{LOPT_PTR, ARG_DUP, "name,target", gettext_noop("Specify PTR DNS record."), NULL},
{LOPT_INTNAME, ARG_DUP, "name,interface",
gettext_noop("Give DNS name to IPv4 address of interface."), NULL},
{'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL},
{'1', OPT_DBUS, NULL,
gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL},
{'2', ARG_DUP, "interface",
gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL},
{'3', ARG_DUP, "[=[,]]", gettext_noop("Enable dynamic address allocation for bootp."),
NULL},
{'4', ARG_DUP, ",",
gettext_noop("Map MAC address (with wildcards) to option set."), NULL},
{LOPT_BRIDGE, ARG_DUP, "iface,alias,..",
gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL},
{'5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."),
NULL},
{'6', ARG_ONE, "path", gettext_noop("Script to run on DHCP lease creation and destruction."),
NULL},
{'7', ARG_DUP, "path", gettext_noop("Read configuration from all the files in this directory."),
NULL},
{'8', ARG_ONE, "|",
gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), NULL},
{'9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL},
{'0', ARG_ONE, "",
gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!"},
{LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE},
{LOPT_NO_NAMES, ARG_DUP, "[=[,]]",
gettext_noop("Ignore hostnames provided by DHCP clients."), NULL},
{LOPT_OVERRIDE, OPT_NO_OVERRIDE, NULL,
gettext_noop("Do NOT reuse filename and server fields for extra DHCP options."), NULL},
{LOPT_TFTP, OPT_TFTP, NULL, gettext_noop("Enable integrated read-only TFTP server."), NULL},
{LOPT_PREFIX, ARG_ONE, "",
gettext_noop("Export files by TFTP only from the specified subtree."), NULL},
{LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL},
{LOPT_SECURE, OPT_TFTP_SECURE, NULL,
gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL},
{LOPT_TFTP_MAX, ARG_ONE, "",
gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#"},
{LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."),
NULL},
{LOPT_TFTPPORTS, ARG_ONE, ",",
gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL},
{LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL},
{LOPT_MAX_LOGS, ARG_ONE, "[=]",
gettext_noop("Enable async. logging; optionally set queue length."), NULL},
{LOPT_REBIND, OPT_NO_REBIND, NULL,
gettext_noop("Stop DNS rebinding. Filter private IP ranges when resolving."), NULL},
{LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."),
NULL},
{LOPT_MATCH, ARG_DUP, ",",
gettext_noop("Set tag if client includes matching option in request."), NULL},
{LOPT_ALTPORT, ARG_ONE, "[=]", gettext_noop("Use alternative ports for DHCP."), NULL},
{LOPT_SCRIPTUSR, ARG_ONE, "", gettext_noop("Run lease-change script as this user."),
NULL},
{LOPT_NAPTR, ARG_DUP, ",", gettext_noop("Specify NAPTR DNS record."), NULL},
{LOPT_MINPORT, ARG_ONE, "",
gettext_noop("Specify lowest port available for DNS query transmission."), NULL},
{LOPT_DHCP_FQDN, OPT_DHCP_FQDN, NULL,
gettext_noop("Use only fully qualified domain names for DHCP clients."), NULL},
{LOPT_CNAME, ARG_DUP, ",",
gettext_noop("Specify alias name for LOCAL DNS name."), NULL},
{LOPT_PXE_PROMT, ARG_DUP, ",[]",
gettext_noop("Prompt to send to PXE clients."), NULL},
{LOPT_PXE_SERV, ARG_DUP, "", gettext_noop("Boot service for PXE menu."), NULL},
{LOPT_LISTNMARK, ARG_ONE, NULL, gettext_noop("Socket mark to use for listen sockets."), NULL},
{LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL},
{0, 0, NULL, NULL, NULL}};
#ifdef HAVE_DHCP
/* makes options which take a list of addresses */
#define OT_ADDR_LIST 0x80
/* DHCP-internal options, for logging. not valid in config file */
#define OT_INTERNAL 0x40
#define OT_NAME 0x20
static const struct {
char* name;
unsigned char val, size;
} opttab[] = {{"netmask", 1, OT_ADDR_LIST},
{"time-offset", 2, 4},
{"router", 3, OT_ADDR_LIST},
{"dns-server", 6, OT_ADDR_LIST},
{"log-server", 7, OT_ADDR_LIST},
{"lpr-server", 9, OT_ADDR_LIST},
{"hostname", 12, OT_INTERNAL | OT_NAME},
{"boot-file-size", 13, 2},
{"domain-name", 15, OT_NAME},
{"swap-server", 16, OT_ADDR_LIST},
{"root-path", 17, 0},
{"extension-path", 18, 0},
{"ip-forward-enable", 19, 1},
{"non-local-source-routing", 20, 1},
{"policy-filter", 21, OT_ADDR_LIST},
{"max-datagram-reassembly", 22, 2},
{"default-ttl", 23, 1},
{"mtu", 26, 2},
{"all-subnets-local", 27, 1},
{"broadcast", 28, OT_INTERNAL | OT_ADDR_LIST},
{"router-discovery", 31, 1},
{"router-solicitation", 32, OT_ADDR_LIST},
{"static-route", 33, OT_ADDR_LIST},
{"trailer-encapsulation", 34, 1},
{"arp-timeout", 35, 4},
{"ethernet-encap", 36, 1},
{"tcp-ttl", 37, 1},
{"tcp-keepalive", 38, 4},
{"nis-domain", 40, 0},
{"nis-server", 41, OT_ADDR_LIST},
{"ntp-server", 42, OT_ADDR_LIST},
{"vendor-encap", 43, OT_INTERNAL},
{"netbios-ns", 44, OT_ADDR_LIST},
{"netbios-dd", 45, OT_ADDR_LIST},
{"netbios-nodetype", 46, 1},
{"netbios-scope", 47, 0},
{"x-windows-fs", 48, OT_ADDR_LIST},
{"x-windows-dm", 49, OT_ADDR_LIST},
{"requested-address", 50, OT_INTERNAL | OT_ADDR_LIST},
{"lease-time", 51, OT_INTERNAL},
{"option-overload", 52, OT_INTERNAL},
{
"message-type",
53,
OT_INTERNAL,
},
{"server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST},
{"parameter-request", 55, OT_INTERNAL},
{"message", 56, OT_INTERNAL},
{"max-message-size", 57, OT_INTERNAL},
{"T1", 58, OT_INTERNAL},
{"T2", 59, OT_INTERNAL},
{"vendor-class", 60, 0},
{"client-id", 61, OT_INTERNAL},
{"nis+-domain", 64, 0},
{"nis+-server", 65, OT_ADDR_LIST},
{"tftp-server", 66, 0},
{"bootfile-name", 67, 0},
{"mobile-ip-home", 68, OT_ADDR_LIST},
{"smtp-server", 69, OT_ADDR_LIST},
{"pop3-server", 70, OT_ADDR_LIST},
{"nntp-server", 71, OT_ADDR_LIST},
{"irc-server", 74, OT_ADDR_LIST},
{"user-class", 77, 0},
{"FQDN", 81, OT_INTERNAL},
{"agent-id", 82, OT_INTERNAL},
{"client-arch", 93, 2},
{"client-interface-id", 94, 0},
{"client-machine-id", 97, 0},
{"subnet-select", 118, OT_INTERNAL},
{"domain-search", 119, 0},
{"sip-server", 120, 0},
{"classless-static-route", 121, 0},
{"server-ip-address", 255, OT_ADDR_LIST}, /* special, internal only, sets siaddr */
{NULL, 0, 0}};
char* option_string(unsigned char opt, int* is_ip, int* is_name) {
int i;
for (i = 0; opttab[i].name; i++)
if (opttab[i].val == opt) {
if (is_ip) *is_ip = !!(opttab[i].size & OT_ADDR_LIST);
if (is_name) *is_name = !!(opttab[i].size & OT_NAME);
return opttab[i].name;
}
return NULL;
}
#endif
/* We hide metacharaters in quoted strings by mapping them into the ASCII control
character space. Note that the \0, \t \b \r \033 and \n characters are carefully placed in the
following sequence so that they map to themselves: it is therefore possible to call
unhide_metas repeatedly on string without breaking things.
The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
couple of other places.
Note that space is included here so that
--dhcp-option=3, string
has five characters, whilst
--dhcp-option=3," string"
has six.
*/
static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
static char hide_meta(char c) {
unsigned int i;
for (i = 0; i < (sizeof(meta) - 1); i++)
if (c == meta[i]) return (char) i;
return c;
}
static char unhide_meta(char cr) {
unsigned int c = cr;
if (c < (sizeof(meta) - 1)) cr = meta[c];
return cr;
}
static void unhide_metas(char* cp) {
if (cp)
for (; *cp; cp++) *cp = unhide_meta(*cp);
}
static void* opt_malloc(size_t size) {
void* ret;
if (mem_recover) {
ret = whine_malloc(size);
if (!ret) longjmp(mem_jmp, 1);
} else
ret = safe_malloc(size);
return ret;
}
static char* opt_string_alloc(char* cp) {
char* ret = NULL;
if (cp && strlen(cp) != 0) {
ret = opt_malloc(strlen(cp) + 1);
strcpy(ret, cp);
/* restore hidden metachars */
unhide_metas(ret);
}
return ret;
}
/* find next comma, split string with zero and eliminate spaces.
return start of string following comma */
static char* split_chr(char* s, char c) {
char *comma, *p;
if (!s || !(comma = strchr(s, c))) return NULL;
p = comma;
*comma = ' ';
for (; isspace((int) *comma); comma++)
;
for (; (p >= s) && isspace((int) *p); p--) *p = 0;
return comma;
}
static char* split(char* s) {
return split_chr(s, ',');
}
static char* canonicalise_opt(char* s) {
char* ret;
int nomem;
if (!s) return 0;
unhide_metas(s);
if (!(ret = canonicalise(s, &nomem)) && nomem) {
if (mem_recover)
longjmp(mem_jmp, 1);
else
die(_("could not get memory"), NULL, EC_NOMEM);
}
return ret;
}
static int atoi_check(char* a, int* res) {
char* p;
if (!a) return 0;
unhide_metas(a);
for (p = a; *p; p++)
if (*p < '0' || *p > '9') return 0;
*res = atoi(a);
return 1;
}
static int atoi_check16(char* a, int* res) {
if (!(atoi_check(a, res)) || *res < 0 || *res > 0xffff) return 0;
return 1;
}
static void add_txt(char* name, char* txt) {
size_t len = strlen(txt);
struct txt_record* r = opt_malloc(sizeof(struct txt_record));
r->name = opt_string_alloc(name);
r->next = daemon->txt;
daemon->txt = r;
r->class = C_CHAOS;
r->txt = opt_malloc(len + 1);
r->len = len + 1;
*(r->txt) = len;
memcpy((r->txt) + 1, txt, len);
}
static void do_usage(void) {
char buff[100];
int i, j;
struct {
char handle;
int val;
} tab[] = {{'$', CACHESIZ}, {'*', EDNS_PKTSZ}, {'&', MAXLEASES}, {'!', FTABSIZ}, {'\0', 0}};
printf(_("Usage: dnsmasq [options]\n\n"));
#ifndef HAVE_GETOPT_LONG
printf(_("Use short options only on the command line.\n"));
#endif
printf(_("Valid options are:\n"));
for (i = 0; usage[i].opt != 0; i++) {
char* desc = usage[i].flagdesc;
char* eq = "=";
if (!desc || *desc == '[') eq = "";
if (!desc) desc = "";
for (j = 0; opts[j].name; j++)
if (opts[j].val == usage[i].opt) break;
if (usage[i].opt < 256)
sprintf(buff, "-%c, ", usage[i].opt);
else
sprintf(buff, " ");
sprintf(buff + 4, "--%s%s%s", opts[j].name, eq, desc);
printf("%-36.36s", buff);
if (usage[i].arg) {
strcpy(buff, usage[i].arg);
for (j = 0; tab[j].handle; j++)
if (tab[j].handle == *(usage[i].arg)) sprintf(buff, "%d", tab[j].val);
}
printf(_(usage[i].desc), buff);
printf("\n");
}
}
#ifdef HAVE_DHCP
static void display_opts(void) {
int i;
printf(_("Known DHCP options:\n"));
for (i = 0; opttab[i].name; i++)
if (!(opttab[i].size & OT_INTERNAL)) printf("%3d %s\n", opttab[i].val, opttab[i].name);
}
/* This is too insanely large to keep in-line in the switch */
static char* parse_dhcp_opt(char* arg, int flags) {
struct dhcp_opt* new = opt_malloc(sizeof(struct dhcp_opt));
char lenchar = 0, *cp;
int i, addrs, digs, is_addr, is_hex, is_dec, is_string, dots;
char *comma = NULL, *problem = NULL;
struct dhcp_netid* np = NULL;
unsigned char opt_len = 0;
new->len = 0;
new->flags = flags;
new->netid = NULL;
new->val = NULL;
new->opt = 0;
while (arg) {
comma = split(arg);
for (cp = arg; *cp; cp++)
if (*cp < '0' || *cp > '9') break;
if (!*cp) {
new->opt = atoi(arg);
opt_len = 0;
break;
}
if (strstr(arg, "option:") == arg) {
for (i = 0; opttab[i].name; i++)
if (!(opttab[i].size & OT_INTERNAL) && strcasecmp(opttab[i].name, arg + 7) == 0) {
new->opt = opttab[i].val;
opt_len = opttab[i].size;
break;
}
/* option: must follow tag and vendor string. */
break;
} else if (strstr(arg, "vendor:") == arg) {
new->u.vendor_class = (unsigned char*) opt_string_alloc(arg + 7);
new->flags |= DHOPT_VENDOR;
} else if (strstr(arg, "encap:") == arg) {
new->u.encap = atoi(arg + 6);
new->flags |= DHOPT_ENCAPSULATE;
} else {
new->netid = opt_malloc(sizeof(struct dhcp_netid));
/* allow optional "net:" for consistency */
if (strstr(arg, "net:") == arg)
new->netid->net = opt_string_alloc(arg + 4);
else
new->netid->net = opt_string_alloc(arg);
new->netid->next = np;
np = new->netid;
}
arg = comma;
}
if (new->opt == 0)
problem = _("bad dhcp-option");
else if (comma) {
/* characterise the value */
char c;
is_addr = is_hex = is_dec = is_string = 1;
addrs = digs = 1;
dots = 0;
for (cp = comma; (c = *cp); cp++)
if (c == ',') {
addrs++;
is_dec = is_hex = 0;
} else if (c == ':') {
digs++;
is_dec = is_addr = 0;
} else if (c == '/') {
is_dec = is_hex = 0;
if (cp == comma) /* leading / means a pathname */
is_addr = 0;
} else if (c == '.') {
is_dec = is_hex = 0;
dots++;
} else if (c == '-')
is_hex = is_addr = 0;
else if (c == ' ')
is_dec = is_hex = 0;
else if (!(c >= '0' && c <= '9')) {
is_addr = 0;
if (cp[1] == 0 && is_dec && (c == 'b' || c == 's' || c == 'i')) {
lenchar = c;
*cp = 0;
} else
is_dec = 0;
if (!((c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') ||
(c == '*' && (flags & DHOPT_MATCH))))
is_hex = 0;
}
/* We know that some options take addresses */
if (opt_len & OT_ADDR_LIST) {
is_string = is_dec = is_hex = 0;
if (!is_addr || dots == 0) problem = _("bad IP address");
}
if (is_hex && digs > 1) {
new->len = digs;
new->val = opt_malloc(new->len);
parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL,
NULL);
new->flags |= DHOPT_HEX;
} else if (is_dec) {
int i, val = atoi(comma);
/* assume numeric arg is 1 byte except for
options where it is known otherwise.
For vendor class option, we have to hack. */
if (opt_len != 0)
new->len = opt_len;
else if (val & 0xffff0000)
new->len = 4;
else if (val & 0xff00)
new->len = 2;
else
new->len = 1;
if (lenchar == 'b')
new->len = 1;
else if (lenchar == 's')
new->len = 2;
else if (lenchar == 'i')
new->len = 4;
new->val = opt_malloc(new->len);
for (i = 0; i < new->len; i++) new->val[i] = val >> ((new->len - i - 1) * 8);
} else if (is_addr) {
struct in_addr in;
unsigned char* op;
char* slash;
/* max length of address/subnet descriptor is five bytes,
add one for the option 120 enc byte too */
new->val = op = opt_malloc((5 * addrs) + 1);
new->flags |= DHOPT_ADDR;
if (!(new->flags& DHOPT_ENCAPSULATE) && new->opt == 120) {
*(op++) = 1; /* RFC 3361 "enc byte" */
new->flags &= ~DHOPT_ADDR;
}
while (addrs--) {
cp = comma;
comma = split(cp);
slash = split_chr(cp, '/');
in.s_addr = inet_addr(cp);
if (!slash) {
memcpy(op, &in, INADDRSZ);
op += INADDRSZ;
} else {
unsigned char* p = (unsigned char*) ∈
int netsize = atoi(slash);
*op++ = netsize;
if (netsize > 0) *op++ = *p++;
if (netsize > 8) *op++ = *p++;
if (netsize > 16) *op++ = *p++;
if (netsize > 24) *op++ = *p++;
new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
}
}
new->len = op - new->val;
} else if (is_string) {
/* text arg */
if ((new->opt == 119 || new->opt == 120) && !(new->flags& DHOPT_ENCAPSULATE)) {
/* dns search, RFC 3397, or SIP, RFC 3361 */
unsigned char *q, *r, *tail;
unsigned char *p, *m = NULL, *newp;
size_t newlen, len = 0;
int header_size = (new->opt == 119) ? 0 : 1;
arg = comma;
comma = split(arg);
while (arg && *arg) {
char* dom;
if (!(dom = arg = canonicalise_opt(arg))) {
problem = _("bad domain in dhcp-option");
break;
}
newp = opt_malloc(len + strlen(arg) + 2 + header_size);
if (m) memcpy(newp, m, header_size + len);
m = newp;
p = m + header_size;
q = p + len;
/* add string on the end in RFC1035 format */
while (*arg) {
unsigned char* cp = q++;
int j;
for (j = 0; *arg && (*arg != '.'); arg++, j++) *q++ = *arg;
*cp = j;
if (*arg) arg++;
}
*q++ = 0;
free(dom);
/* Now tail-compress using earlier names. */
newlen = q - p;
for (tail = p + len; *tail; tail += (*tail) + 1)
for (r = p; r - p < (int) len; r += (*r) + 1)
if (strcmp((char*) r, (char*) tail) == 0) {
PUTSHORT((r - p) | 0xc000, tail);
newlen = tail - p;
goto end;
}
end:
len = newlen;
arg = comma;
comma = split(arg);
}
/* RFC 3361, enc byte is zero for names */
if (new->opt == 120) m[0] = 0;
new->len = (int) len + header_size;
new->val = m;
} else {
new->len = strlen(comma);
/* keep terminating zero on string */
new->val = (unsigned char*) opt_string_alloc(comma);
new->flags |= DHOPT_STRING;
}
}
}
if ((new->len > 255) || (new->len > 253 && (new->flags&(DHOPT_VENDOR | DHOPT_ENCAPSULATE))))
problem = _("dhcp-option too long");
if (!problem) {
if (flags == DHOPT_MATCH) {
if ((new->flags&(DHOPT_ENCAPSULATE | DHOPT_VENDOR)) || !new->netid || new->netid->next)
problem = _("illegal dhcp-match");
else {
new->next = daemon->dhcp_match;
daemon->dhcp_match = new;
}
} else {
new->next = daemon->dhcp_opts;
daemon->dhcp_opts = new;
}
}
return problem;
}
#endif
static char* one_opt(int option, char* arg, char* gen_prob, int nest) {
int i;
char *comma, *problem = NULL;
;
if (option == '?') return gen_prob;
for (i = 0; usage[i].opt != 0; i++)
if (usage[i].opt == option) {
int rept = usage[i].rept;
if (nest == 0) {
/* command line */
if (rept == ARG_USED_CL) return _("illegal repeated flag");
if (rept == ARG_ONE) usage[i].rept = ARG_USED_CL;
} else {
/* allow file to override command line */
if (rept == ARG_USED_FILE) return _("illegal repeated keyword");
if (rept == ARG_USED_CL || rept == ARG_ONE) usage[i].rept = ARG_USED_FILE;
}
if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL) {
daemon->options |= rept;
return NULL;
}
break;
}
switch (option) {
case 'C': /* --conf-file */
{
char* file = opt_string_alloc(arg);
if (file) {
one_file(file, nest, 0);
free(file);
}
break;
}
case '7': /* --conf-dir */
{
DIR* dir_stream;
struct dirent* ent;
char *directory, *path;
struct list {
char* suffix;
struct list* next;
}* ignore_suffix = NULL, *li;
comma = split(arg);
if (!(directory = opt_string_alloc(arg))) break;
for (arg = comma; arg; arg = comma) {
comma = split(arg);
li = opt_malloc(sizeof(struct list));
li->next = ignore_suffix;
ignore_suffix = li;
/* Have to copy: buffer is overwritten */
li->suffix = opt_string_alloc(arg);
};
if (!(dir_stream = opendir(directory)))
die(_("cannot access directory %s: %s"), directory, EC_FILE);
while ((ent = readdir(dir_stream))) {
size_t len = strlen(ent->d_name);
struct stat buf;
/* ignore emacs backups and dotfiles */
if (len == 0 || ent->d_name[len - 1] == '~' ||
(ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') || ent->d_name[0] == '.')
continue;
for (li = ignore_suffix; li; li = li->next) {
/* check for proscribed suffices */
size_t ls = strlen(li->suffix);
if (len > ls && strcmp(li->suffix, &ent->d_name[len - ls]) == 0) break;
}
if (li) continue;
path = opt_malloc(strlen(directory) + len + 2);
strcpy(path, directory);
strcat(path, "/");
strcat(path, ent->d_name);
if (stat(path, &buf) == -1) die(_("cannot access %s: %s"), path, EC_FILE);
/* only reg files allowed. */
if (!S_ISREG(buf.st_mode)) continue;
/* dir is one level, so files must be readable */
one_file(path, nest + 1, 0);
free(path);
}
closedir(dir_stream);
free(directory);
for (; ignore_suffix; ignore_suffix = li) {
li = ignore_suffix->next;
free(ignore_suffix->suffix);
free(ignore_suffix);
}
break;
}
case '8': /* --log-facility */
/* may be a filename */
if (strchr(arg, '/'))
daemon->log_file = opt_string_alloc(arg);
else {
#ifdef __ANDROID__
problem = "Android does not support log facilities";
#else
for (i = 0; facilitynames[i].c_name; i++)
if (hostname_isequal((char*) facilitynames[i].c_name, arg)) break;
if (facilitynames[i].c_name)
daemon->log_fac = facilitynames[i].c_val;
else
problem = "bad log facility";
#endif
}
break;
case 'x': /* --pid-file */
daemon->runfile = opt_string_alloc(arg);
break;
case LOPT_DHCP_HOST: /* --dhcp-hostfile */
if (daemon->dhcp_hosts_file)
problem = _("only one dhcp-hostsfile allowed");
else
daemon->dhcp_hosts_file = opt_string_alloc(arg);
break;
case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
if (daemon->dhcp_opts_file)
problem = _("only one dhcp-optsfile allowed");
else
daemon->dhcp_opts_file = opt_string_alloc(arg);
break;
case 'r': /* --resolv-file */
{
char* name = opt_string_alloc(arg);
struct resolvc* new, *list = daemon->resolv_files;
if (list && list->is_default) {
/* replace default resolv file - possibly with nothing */
if (name) {
list->is_default = 0;
list->name = name;
} else
list = NULL;
} else if (name) {
new = opt_malloc(sizeof(struct resolvc));
new->next = list;
new->name = name;
new->is_default = 0;
new->mtime = 0;
new->logged = 0;
list = new;
}
daemon->resolv_files = list;
break;
}
case 'm': /* --mx-host */
{
int pref = 1;
struct mx_srv_record* new;
char *name, *target = NULL;
if ((comma = split(arg))) {
char* prefstr;
if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
problem = _("bad MX preference");
}
if (!(name = canonicalise_opt(arg)) || (comma && !(target = canonicalise_opt(comma))))
problem = _("bad MX name");
new = opt_malloc(sizeof(struct mx_srv_record));
new->next = daemon->mxnames;
daemon->mxnames = new;
new->issrv = 0;
new->name = name;
new->target = target; /* may be NULL */
new->weight = pref;
break;
}
case 't': /* --mx-target */
if (!(daemon->mxtarget = canonicalise_opt(arg))) problem = _("bad MX target");
break;
#ifdef HAVE_DHCP
case 'l': /* --dhcp-leasefile */
daemon->lease_file = opt_string_alloc(arg);
break;
case '6': /* --dhcp-script */
#if defined(NO_FORK)
problem = _("cannot run scripts under uClinux");
#elif !defined(HAVE_SCRIPT)
problem = _("recompile with HAVE_SCRIPT defined to enable lease-change scripts");
#else
daemon->lease_change_command = opt_string_alloc(arg);
#endif
break;
#endif
case 'H': /* --addn-hosts */
{
struct hostsfile* new = opt_malloc(sizeof(struct hostsfile));
static int hosts_index = 1;
new->fname = opt_string_alloc(arg);
new->index = hosts_index++;
new->flags = 0;
new->next = daemon->addn_hosts;
daemon->addn_hosts = new;
break;
}
case 's': /* --domain */
if (strcmp(arg, "#") == 0)
daemon->options |= OPT_RESOLV_DOMAIN;
else {
char* d;
comma = split(arg);
if (!(d = canonicalise_opt(arg)))
option = '?';
else {
if (comma) {
struct cond_domain* new = safe_malloc(sizeof(struct cond_domain));
unhide_metas(comma);
if ((arg = split_chr(comma, '/'))) {
int mask;
if ((new->start.s_addr = inet_addr(comma)) == (in_addr_t) -1 ||
!atoi_check(arg, &mask))
option = '?';
else {
mask = (1 << (32 - mask)) - 1;
new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
new->end.s_addr = new->start.s_addr | htonl(mask);
}
} else if ((arg = split(comma))) {
if ((new->start.s_addr = inet_addr(comma)) == (in_addr_t) -1 ||
(new->end.s_addr = inet_addr(arg)) == (in_addr_t) -1)
option = '?';
} else if ((new->start.s_addr = new->end.s_addr = inet_addr(comma)) ==
(in_addr_t) -1)
option = '?';
new->domain = d;
new->next = daemon->cond_domain;
daemon->cond_domain = new;
} else
daemon->domain_suffix = d;
}
}
break;
case 'u': /* --user */
daemon->username = opt_string_alloc(arg);
break;
case 'g': /* --group */
daemon->groupname = opt_string_alloc(arg);
daemon->group_set = 1;
break;
#ifdef HAVE_DHCP
case LOPT_SCRIPTUSR: /* --scriptuser */
daemon->scriptuser = opt_string_alloc(arg);
break;
#endif
case 'i': /* --interface */
do {
struct iname* new = opt_malloc(sizeof(struct iname));
comma = split(arg);
new->next = daemon->if_names;
daemon->if_names = new;
/* new->name may be NULL if someone does
"interface=" to disable all interfaces except loop. */
new->name = opt_string_alloc(arg);
new->isloop = new->used = 0;
arg = comma;
} while (arg);
break;
case 'I': /* --except-interface */
case '2': /* --no-dhcp-interface */
do {
struct iname* new = opt_malloc(sizeof(struct iname));
comma = split(arg);
new->name = opt_string_alloc(arg);
if (option == 'I') {
new->next = daemon->if_except;
daemon->if_except = new;
} else {
new->next = daemon->dhcp_except;
daemon->dhcp_except = new;
}
arg = comma;
} while (arg);
break;
case 'B': /* --bogus-nxdomain */
{
struct in_addr addr;
unhide_metas(arg);
if (arg && (addr.s_addr = inet_addr(arg)) != (in_addr_t) -1) {
struct bogus_addr* baddr = opt_malloc(sizeof(struct bogus_addr));
baddr->next = daemon->bogus_addr;
daemon->bogus_addr = baddr;
baddr->addr = addr;
} else
option = '?'; /* error */
break;
}
case 'a': /* --listen-address */
do {
struct iname* new = opt_malloc(sizeof(struct iname));
comma = split(arg);
unhide_metas(arg);
new->next = daemon->if_addrs;
if (arg && parse_addr(AF_INET, arg, &new->addr) != 0 &&
parse_addr(AF_INET6, arg, &new->addr) != 0) {
option = '?'; /* error */
break;
}
daemon->if_addrs = new;
arg = comma;
} while (arg);
break;
case 'S': /* --server */
case LOPT_LOCAL: /* --local */
case 'A': /* --address */
{
struct server *serv, *newlist = NULL;
unhide_metas(arg);
if (arg && *arg == '/') {
char* end;
arg++;
while ((end = split_chr(arg, '/'))) {
char* domain = NULL;
/* # matches everything and becomes a zero length domain string */
if (strcmp(arg, "#") == 0)
domain = "";
else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
option = '?';
serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server));
serv->next = newlist;
newlist = serv;
serv->domain = domain;
serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
arg = end;
}
if (!newlist) {
option = '?';
break;
}
} else {
newlist = opt_malloc(sizeof(struct server));
memset(newlist, 0, sizeof(struct server));
}
if (option == 'A') {
newlist->flags |= SERV_LITERAL_ADDRESS;
if (!(newlist->flags & SERV_TYPE)) option = '?';
}
if (!arg || !*arg) {
newlist->flags |= SERV_NO_ADDR; /* no server */
if (newlist->flags & SERV_LITERAL_ADDRESS) option = '?';
} else {
int source_port = 0, serv_port = NAMESERVER_PORT;
char *portno, *source;
if ((source = split_chr(arg, '@')) && /* is there a source. */
(portno = split_chr(source, '#')) && !atoi_check16(portno, &source_port))
problem = _("bad port");
if ((portno = split_chr(arg, '#')) && /* is there a port no. */
!atoi_check16(portno, &serv_port))
problem = _("bad port");
if (parse_addr(AF_INET, arg, &newlist->addr) == 0) {
newlist->addr.in.sin_port = htons(serv_port);
if (source) {
newlist->flags |= SERV_HAS_SOURCE;
if (parse_addr(AF_INET, source, &newlist->addr) != 0) {
#if defined(SO_BINDTODEVICE)
newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
strncpy(newlist->interface, source, IF_NAMESIZE);
#else
problem = _("interface binding not supported");
#endif
}
} else
newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
newlist->source_addr.in.sin_port = htons(source_port);
newlist->source_addr.sa.sa_family = AF_INET;
}
#ifdef HAVE_IPV6
else if (parse_addr(AF_INET6, arg, &newlist->addr) == 0) {
newlist->addr.in6.sin6_port = htons(serv_port);
if (source) {
newlist->flags |= SERV_HAS_SOURCE;
if (parse_addr(AF_INET6, source, &newlist->source_addr) != 0) {
#if defined(SO_BINDTODEVICE)
newlist->source_addr.in6.sin6_addr = in6addr_any;
strncpy(newlist->interface, source, IF_NAMESIZE);
#else
problem = _("interface binding not supported");
#endif
}
} else
newlist->source_addr.in6.sin6_addr = in6addr_any;
newlist->source_addr.in6.sin6_port = htons(source_port);
newlist->source_addr.sa.sa_family = AF_INET6;
}
#endif
else
option = '?'; /* error */
}
serv = newlist;
while (serv->next) {
serv->next->flags = serv->flags;
serv->next->addr = serv->addr;
serv->next->source_addr = serv->source_addr;
serv = serv->next;
}
serv->next = daemon->servers;
daemon->servers = newlist;
break;
}
case 'c': /* --cache-size */
{
int size;
if (!atoi_check(arg, &size))
option = '?';
else {
/* zero is OK, and means no caching. */
if (size < 0)
size = 0;
else if (size > 10000)
size = 10000;
daemon->cachesize = size;
}
break;
}
case 'p': /* --port */
if (!atoi_check16(arg, &daemon->port)) option = '?';
break;
case LOPT_MINPORT: /* --min-port */
if (!atoi_check16(arg, &daemon->min_port)) option = '?';
break;
case '0': /* --dns-forward-max */
if (!atoi_check(arg, &daemon->ftabsize)) option = '?';
break;
case LOPT_MAX_LOGS: /* --log-async */
daemon->max_logs = LOG_MAX; /* default */
if (arg && !atoi_check(arg, &daemon->max_logs))
option = '?';
else if (daemon->max_logs > 100)
daemon->max_logs = 100;
break;
case 'P': /* --edns-packet-max */
{
int i;
if (!atoi_check(arg, &i)) option = '?';
daemon->edns_pktsz = (unsigned short) i;
break;
}
case 'Q': /* --query-port */
if (!atoi_check16(arg, &daemon->query_port)) option = '?';
/* if explicitly set to zero, use single OS ephemeral port
and disable random ports */
if (daemon->query_port == 0) daemon->osport = 1;
break;
case 'T': /* --local-ttl */
case LOPT_NEGTTL: /* --neg-ttl */
{
int ttl;
if (!atoi_check(arg, &ttl))
option = '?';
else if (option == LOPT_NEGTTL)
daemon->neg_ttl = (unsigned long) ttl;
else
daemon->local_ttl = (unsigned long) ttl;
break;
}
#ifdef HAVE_DHCP
case 'X': /* --dhcp-lease-max */
if (!atoi_check(arg, &daemon->dhcp_max)) option = '?';
break;
#endif
case LOPT_BRIDGE: /* --bridge-interface */
{
struct dhcp_bridge* new = opt_malloc(sizeof(struct dhcp_bridge));
if (!(comma = split(arg))) {
problem = _("bad bridge-interface");
break;
}
strncpy(new->iface, arg, IF_NAMESIZE);
new->alias = NULL;
new->next = daemon->bridges;
daemon->bridges = new;
do {
arg = comma;
comma = split(arg);
if (strlen(arg) != 0) {
struct dhcp_bridge* b = opt_malloc(sizeof(struct dhcp_bridge));
b->next = new->alias;
new->alias = b;
strncpy(b->iface, arg, IF_NAMESIZE);
}
} while (comma);
break;
}
#ifdef HAVE_DHCP
case 'F': /* --dhcp-range */
{
int k, leasepos = 2;
char *cp, *a[5] = {NULL, NULL, NULL, NULL, NULL};
struct dhcp_context* new = opt_malloc(sizeof(struct dhcp_context));
new->next = daemon->dhcp;
new->lease_time = DEFLEASE;
new->addr_epoch = 0;
new->netmask.s_addr = 0;
new->broadcast.s_addr = 0;
new->router.s_addr = 0;
new->netid.net = NULL;
new->filter = NULL;
new->flags = 0;
gen_prob = _("bad dhcp-range");
if (!arg) {
option = '?';
break;
}
while (1) {
for (cp = arg; *cp; cp++)
if (!(*cp == ' ' || *cp == '.' || (*cp >= '0' && *cp <= '9'))) break;
if (*cp != ',' && (comma = split(arg))) {
if (strstr(arg, "net:") == arg) {
struct dhcp_netid* tt = opt_malloc(sizeof(struct dhcp_netid));
tt->net = opt_string_alloc(arg + 4);
tt->next = new->filter;
new->filter = tt;
} else {
if (new->netid.net)
problem = _("only one netid tag allowed");
else
new->netid.net = opt_string_alloc(arg);
}
arg = comma;
} else {
a[0] = arg;
break;
}
}
for (k = 1; k < 5; k++)
if (!(a[k] = split(a[k - 1]))) break;
if ((k < 2) || ((new->start.s_addr = inet_addr(a[0])) == (in_addr_t) -1))
option = '?';
else if (strcmp(a[1], "static") == 0) {
new->end = new->start;
new->flags |= CONTEXT_STATIC;
} else if (strcmp(a[1], "proxy") == 0) {
new->end = new->start;
new->flags |= CONTEXT_PROXY;
} else if ((new->end.s_addr = inet_addr(a[1])) == (in_addr_t) -1)
option = '?';
if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr)) {
struct in_addr tmp = new->start;
new->start = new->end;
new->end = tmp;
}
if (option != '?' && k >= 3 && strchr(a[2], '.') &&
((new->netmask.s_addr = inet_addr(a[2])) != (in_addr_t) -1)) {
new->flags |= CONTEXT_NETMASK;
leasepos = 3;
if (!is_same_net(new->start, new->end, new->netmask))
problem = _("inconsistent DHCP range");
}
daemon->dhcp = new;
if (k >= 4 && strchr(a[3], '.') &&
((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t) -1)) {
new->flags |= CONTEXT_BRDCAST;
leasepos = 4;
}
if (k >= leasepos + 1) {
if (strcmp(a[leasepos], "infinite") == 0)
new->lease_time = 0xffffffff;
else {
int fac = 1;
if (strlen(a[leasepos]) > 0) {
switch (a[leasepos][strlen(a[leasepos]) - 1]) {
case 'd':
case 'D':
fac *= 24;
/* fall though */
case 'h':
case 'H':
fac *= 60;
/* fall through */
case 'm':
case 'M':
fac *= 60;
/* fall through */
case 's':
case 'S':
a[leasepos][strlen(a[leasepos]) - 1] = 0;
}
new->lease_time = atoi(a[leasepos]) * fac;
/* Leases of a minute or less confuse
some clients, notably Apple's */
if (new->lease_time < 120) new->lease_time = 120;
}
}
}
break;
}
case LOPT_BANK:
case 'G': /* --dhcp-host */
{
int j, k = 0;
char* a[6] = {NULL, NULL, NULL, NULL, NULL, NULL};
struct dhcp_config* new;
struct in_addr in;
new = opt_malloc(sizeof(struct dhcp_config));
new->next = daemon->dhcp_conf;
new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
new->hwaddr = NULL;
if ((a[0] = arg))
for (k = 1; k < 6; k++)
if (!(a[k] = split(a[k - 1]))) break;
for (j = 0; j < k; j++)
if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
{
char* arg = a[j];
if ((arg[0] == 'i' || arg[0] == 'I') && (arg[1] == 'd' || arg[1] == 'D') &&
arg[2] == ':') {
if (arg[3] == '*')
new->flags |= CONFIG_NOCLID;
else {
int len;
arg += 3; /* dump id: */
if (strchr(arg, ':'))
len = parse_hex(arg, (unsigned char*) arg, -1, NULL, NULL);
else {
unhide_metas(arg);
len = (int) strlen(arg);
}
if ((new->clid = opt_malloc(len))) {
new->flags |= CONFIG_CLID;
new->clid_len = len;
memcpy(new->clid, arg, len);
}
}
} else if (strstr(arg, "net:") == arg) {
int len = strlen(arg + 4) + 1;
if ((new->netid.net = opt_malloc(len))) {
new->flags |= CONFIG_NETID;
strcpy(new->netid.net, arg + 4);
unhide_metas(new->netid.net);
}
} else {
struct hwaddr_config* newhw = opt_malloc(sizeof(struct hwaddr_config));
newhw->next = new->hwaddr;
new->hwaddr = newhw;
newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX,
&newhw->wildcard_mask, &newhw->hwaddr_type);
}
} else if (strchr(a[j], '.') && (in.s_addr = inet_addr(a[j])) != (in_addr_t) -1) {
new->addr = in;
new->flags |= CONFIG_ADDR;
} else {
char *cp, *lastp = NULL, last = 0;
int fac = 1;
if (strlen(a[j]) > 1) {
lastp = a[j] + strlen(a[j]) - 1;
last = *lastp;
switch (last) {
case 'd':
case 'D':
fac *= 24;
/* fall through */
case 'h':
case 'H':
fac *= 60;
/* fall through */
case 'm':
case 'M':
fac *= 60;
/* fall through */
case 's':
case 'S':
*lastp = 0;
}
}
for (cp = a[j]; *cp; cp++)
if (!isdigit((int) *cp) && *cp != ' ') break;
if (*cp) {
if (lastp) *lastp = last;
if (strcmp(a[j], "infinite") == 0) {
new->lease_time = 0xffffffff;
new->flags |= CONFIG_TIME;
} else if (strcmp(a[j], "ignore") == 0)
new->flags |= CONFIG_DISABLE;
else {
if (!(new->hostname = canonicalise_opt(a[j])) ||
!legal_hostname(new->hostname))
problem = _("bad DHCP host name");
else
new->flags |= CONFIG_NAME;
new->domain = NULL;
}
} else {
new->lease_time = atoi(a[j]) * fac;
/* Leases of a minute or less confuse
some clients, notably Apple's */
if (new->lease_time < 120) new->lease_time = 120;
new->flags |= CONFIG_TIME;
}
}
daemon->dhcp_conf = new;
break;
}
case 'O': /* --dhcp-option */
case LOPT_FORCE: /* --dhcp-option-force */
case LOPT_OPTS:
case LOPT_MATCH: /* --dhcp-match */
problem = parse_dhcp_opt(
arg, option == LOPT_FORCE
? DHOPT_FORCE
: (option == LOPT_MATCH ? DHOPT_MATCH
: (option == LOPT_OPTS ? DHOPT_BANK : 0)));
break;
case 'M': /* --dhcp-boot */
{
struct dhcp_netid* id = NULL;
while (arg && strstr(arg, "net:") == arg) {
struct dhcp_netid* newid = opt_malloc(sizeof(struct dhcp_netid));
newid->next = id;
id = newid;
comma = split(arg);
newid->net = opt_string_alloc(arg + 4);
arg = comma;
};
if (!arg)
option = '?';
else {
char *dhcp_file, *dhcp_sname = NULL;
struct in_addr dhcp_next_server;
comma = split(arg);
dhcp_file = opt_string_alloc(arg);
dhcp_next_server.s_addr = 0;
if (comma) {
arg = comma;
comma = split(arg);
dhcp_sname = opt_string_alloc(arg);
if (comma) {
unhide_metas(comma);
if ((dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t) -1)
option = '?';
}
}
if (option != '?') {
struct dhcp_boot* new = opt_malloc(sizeof(struct dhcp_boot));
new->file = dhcp_file;
new->sname = dhcp_sname;
new->next_server = dhcp_next_server;
new->netid = id;
new->next = daemon->boot_config;
daemon->boot_config = new;
}
}
break;
}
case LOPT_PXE_PROMT: /* --pxe-prompt */
{
struct dhcp_opt* new = opt_malloc(sizeof(struct dhcp_opt));
int timeout;
new->netid = NULL;
new->opt = 10; /* PXE_MENU_PROMPT */
while (arg && strstr(arg, "net:") == arg) {
struct dhcp_netid* nn = opt_malloc(sizeof(struct dhcp_netid));
comma = split(arg);
nn->next = new->netid;
new->netid = nn;
nn->net = opt_string_alloc(arg + 4);
arg = comma;
}
if (!arg)
option = '?';
else {
comma = split(arg);
unhide_metas(arg);
new->len = strlen(arg) + 1;
new->val = opt_malloc(new->len);
memcpy(new->val + 1, arg, new->len - 1);
new->u.vendor_class = (unsigned char*) "PXEClient";
new->flags = DHOPT_VENDOR;
if (comma && atoi_check(comma, &timeout))
*(new->val) = timeout;
else
*(new->val) = 255;
new->next = daemon->dhcp_opts;
daemon->dhcp_opts = new;
daemon->enable_pxe = 1;
}
break;
}
case LOPT_PXE_SERV: /* --pxe-service */
{
struct pxe_service* new = opt_malloc(sizeof(struct pxe_service));
char* CSA[] = {
"x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
"IA32_EFI", "BC_EFI", "Xscale_EFI", "x86-64_EFI", NULL};
static int boottype = 32768;
new->netid = NULL;
new->server.s_addr = 0;
while (arg && strstr(arg, "net:") == arg) {
struct dhcp_netid* nn = opt_malloc(sizeof(struct dhcp_netid));
comma = split(arg);
nn->next = new->netid;
new->netid = nn;
nn->net = opt_string_alloc(arg + 4);
arg = comma;
}
if (arg && (comma = split(arg))) {
for (i = 0; CSA[i]; i++)
if (strcasecmp(CSA[i], arg) == 0) break;
if (CSA[i] || atoi_check(arg, &i)) {
arg = comma;
comma = split(arg);
new->CSA = i;
new->menu = opt_string_alloc(arg);
if (comma) {
arg = comma;
comma = split(arg);
if (atoi_check(arg, &i)) {
new->type = i;
new->basename = NULL;
} else {
new->type = boottype++;
new->basename = opt_string_alloc(arg);
}
if (comma && (new->server.s_addr = inet_addr(comma)) == (in_addr_t) -1)
option = '?';
/* Order matters */
new->next = NULL;
if (!daemon->pxe_services)
daemon->pxe_services = new;
else {
struct pxe_service* s;
for (s = daemon->pxe_services; s->next; s = s->next)
;
s->next = new;
}
daemon->enable_pxe = 1;
break;
}
}
}
option = '?';
break;
}
case '4': /* --dhcp-mac */
{
if (!(comma = split(arg)))
option = '?';
else {
struct dhcp_mac* new = opt_malloc(sizeof(struct dhcp_mac));
if (strstr(arg, "net:") == arg)
new->netid.net = opt_string_alloc(arg + 4);
else
new->netid.net = opt_string_alloc(arg);
unhide_metas(comma);
new->hwaddr_len =
parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
new->next = daemon->dhcp_macs;
daemon->dhcp_macs = new;
}
} break;
case 'U': /* --dhcp-vendorclass */
case 'j': /* --dhcp-userclass */
case LOPT_CIRCUIT: /* --dhcp-circuitid */
case LOPT_REMOTE: /* --dhcp-remoteid */
case LOPT_SUBSCR: /* --dhcp-subscrid */
{
if (!(comma = split(arg)))
option = '?';
else {
char* p;
int dig = 0;
struct dhcp_vendor* new = opt_malloc(sizeof(struct dhcp_vendor));
if (strstr(arg, "net:") == arg)
new->netid.net = opt_string_alloc(arg + 4);
else
new->netid.net = opt_string_alloc(arg);
/* check for hex string - must digits may include : must not have nothing else,
only allowed for agent-options. */
for (p = comma; *p; p++)
if (isxdigit((int) *p))
dig = 1;
else if (*p != ':')
break;
unhide_metas(comma);
if (option == 'U' || option == 'j' || *p || !dig) {
new->len = strlen(comma);
new->data = opt_malloc(new->len);
memcpy(new->data, comma, new->len);
} else {
new->len = parse_hex(comma, (unsigned char*) comma, strlen(comma), NULL, NULL);
new->data = opt_malloc(new->len);
memcpy(new->data, comma, new->len);
}
switch (option) {
case 'j':
new->match_type = MATCH_USER;
break;
case 'U':
new->match_type = MATCH_VENDOR;
break;
case LOPT_CIRCUIT:
new->match_type = MATCH_CIRCUIT;
break;
case LOPT_REMOTE:
new->match_type = MATCH_REMOTE;
break;
case LOPT_SUBSCR:
new->match_type = MATCH_SUBSCRIBER;
break;
}
new->next = daemon->dhcp_vendors;
daemon->dhcp_vendors = new;
}
break;
}
case LOPT_ALTPORT: /* --dhcp-alternate-port */
if (!arg) {
daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
} else {
comma = split(arg);
if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
(comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
problem = _("invalid port number");
if (!comma) daemon->dhcp_client_port = daemon->dhcp_server_port + 1;
}
break;
case 'J': /* --dhcp-ignore */
case LOPT_NO_NAMES: /* --dhcp-ignore-names */
case LOPT_BROADCAST: /* --dhcp-broadcast */
case '3': /* --bootp-dynamic */
{
struct dhcp_netid_list* new = opt_malloc(sizeof(struct dhcp_netid_list));
struct dhcp_netid* list = NULL;
if (option == 'J') {
new->next = daemon->dhcp_ignore;
daemon->dhcp_ignore = new;
} else if (option == LOPT_BROADCAST) {
new->next = daemon->force_broadcast;
daemon->force_broadcast = new;
} else if (option == '3') {
new->next = daemon->bootp_dynamic;
daemon->bootp_dynamic = new;
} else {
new->next = daemon->dhcp_ignore_names;
daemon->dhcp_ignore_names = new;
}
while (arg) {
struct dhcp_netid* member = opt_malloc(sizeof(struct dhcp_netid));
comma = split(arg);
member->next = list;
list = member;
if (strstr(arg, "net:") == arg)
member->net = opt_string_alloc(arg + 4);
else
member->net = opt_string_alloc(arg);
arg = comma;
}
new->list = list;
break;
}
#endif
case 'V': /* --alias */
{
char *dash, *a[3] = {NULL, NULL, NULL};
int k = 0;
struct doctor* new = opt_malloc(sizeof(struct doctor));
new->next = daemon->doctors;
daemon->doctors = new;
new->mask.s_addr = 0xffffffff;
new->end.s_addr = 0;
if ((a[0] = arg))
for (k = 1; k < 3; k++) {
if (!(a[k] = split(a[k - 1]))) break;
unhide_metas(a[k]);
}
dash = split_chr(a[0], '-');
if ((k < 2) || ((new->in.s_addr = inet_addr(a[0])) == (in_addr_t) -1) ||
((new->out.s_addr = inet_addr(a[1])) == (in_addr_t) -1))
option = '?';
if (k == 3) new->mask.s_addr = inet_addr(a[2]);
if (dash && ((new->end.s_addr = inet_addr(dash)) == (in_addr_t) -1 ||
!is_same_net(new->in, new->end, new->mask) ||
ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
problem = _("invalid alias range");
break;
}
case LOPT_INTNAME: /* --interface-name */
{
struct interface_name* new, **up;
char* domain = NULL;
comma = split(arg);
if (!comma || !(domain = canonicalise_opt(arg))) problem = _("bad interface name");
new = opt_malloc(sizeof(struct interface_name));
new->next = NULL;
/* Add to the end of the list, so that first name
of an interface is used for PTR lookups. */
for (up = &daemon->int_names; *up; up = &((*up)->next))
;
*up = new;
new->name = domain;
new->intr = opt_string_alloc(comma);
break;
}
case LOPT_CNAME: /* --cname */
{
struct cname* new;
if (!(comma = split(arg)))
option = '?';
else {
char* alias = canonicalise_opt(arg);
char* target = canonicalise_opt(comma);
if (!alias || !target)
problem = _("bad CNAME");
else {
for (new = daemon->cnames; new; new = new->next)
if (hostname_isequal(new->alias, arg)) problem = _("duplicate CNAME");
new = opt_malloc(sizeof(struct cname));
new->next = daemon->cnames;
daemon->cnames = new;
new->alias = alias;
new->target = target;
}
}
break;
}
case LOPT_PTR: /* --ptr-record */
{
struct ptr_record* new;
char *dom, *target = NULL;
comma = split(arg);
if (!(dom = canonicalise_opt(arg)) || (comma && !(target = canonicalise_opt(comma))))
problem = _("bad PTR record");
else {
new = opt_malloc(sizeof(struct ptr_record));
new->next = daemon->ptr;
daemon->ptr = new;
new->name = dom;
new->ptr = target;
}
break;
}
case LOPT_NAPTR: /* --naptr-record */
{
char* a[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
int k = 0;
struct naptr* new;
int order, pref;
char *name, *replace = NULL;
if ((a[0] = arg))
for (k = 1; k < 7; k++)
if (!(a[k] = split(a[k - 1]))) break;
if (k < 6 || !(name = canonicalise_opt(a[0])) || !atoi_check16(a[1], &order) ||
!atoi_check16(a[2], &pref) || (k == 7 && !(replace = canonicalise_opt(a[6]))))
problem = _("bad NAPTR record");
else {
new = opt_malloc(sizeof(struct naptr));
new->next = daemon->naptr;
daemon->naptr = new;
new->name = name;
new->flags = opt_string_alloc(a[3]);
new->services = opt_string_alloc(a[4]);
new->regexp = opt_string_alloc(a[5]);
new->replace = replace;
new->order = order;
new->pref = pref;
}
break;
}
case 'Y': /* --txt-record */
{
struct txt_record* new;
unsigned char *p, *q;
if ((comma = split(arg))) comma--;
gen_prob = _("TXT record string too long");
if ((q = (unsigned char*) comma))
while (1) {
size_t len;
if ((p = (unsigned char*) strchr((char*) q + 1, ','))) {
if ((len = p - q - 1) > 255) option = '?';
*q = len;
for (q = q + 1; q < p; q++) *q = unhide_meta(*q);
} else {
if ((len = strlen((char*) q + 1)) > 255) option = '?';
*q = len;
for (q = q + 1; *q; q++) *q = unhide_meta(*q);
break;
}
}
new = opt_malloc(sizeof(struct txt_record));
new->next = daemon->txt;
daemon->txt = new;
new->class = C_IN;
if (comma) {
new->len = q - ((unsigned char*) comma);
new->txt = opt_malloc(new->len);
memcpy(new->txt, comma, new->len);
} else {
static char empty[] = "";
new->len = 1;
new->txt = empty;
}
/* ensure arg is terminated */
if (comma) *comma = 0;
if (!(new->name = canonicalise_opt(arg))) {
problem = _("bad TXT record");
break;
}
break;
}
case 'W': /* --srv-host */
{
int port = 1, priority = 0, weight = 0;
char *name, *target = NULL;
struct mx_srv_record* new;
comma = split(arg);
if (!(name = canonicalise_opt(arg))) problem = _("bad SRV record");
if (comma) {
arg = comma;
comma = split(arg);
if (!(target = canonicalise_opt(arg))) problem = _("bad SRV target");
if (comma) {
arg = comma;
comma = split(arg);
if (!atoi_check16(arg, &port)) problem = _("invalid port number");
if (comma) {
arg = comma;
comma = split(arg);
if (!atoi_check16(arg, &priority)) problem = _("invalid priority");
if (comma) {
arg = comma;
comma = split(arg);
if (!atoi_check16(arg, &weight)) problem = _("invalid weight");
}
}
}
}
new = opt_malloc(sizeof(struct mx_srv_record));
new->next = daemon->mxnames;
daemon->mxnames = new;
new->issrv = 1;
new->name = name;
new->target = target;
new->srvport = port;
new->priority = priority;
new->weight = weight;
break;
}
case LOPT_LISTNMARK: /* --listen-mark */
{
char* endptr;
uint32_t mark = strtoul(arg, &endptr, 0);
// my_syslog(LOG_WARNING, "passed-in mark: %s", arg);
if (!*endptr)
daemon->listen_mark = mark;
else
problem = _("invalid mark");
// my_syslog(LOG_WARNING, "daemon->listen_mark: 0x%x, *endptr=%d", daemon->listen_mark, *endptr);
break;
}
default:
return _("unsupported option (check that dnsmasq was compiled with DHCP support)");
}
if (problem) return problem;
if (option == '?') return gen_prob;
return NULL;
}
static void one_file(char* file, int nest, int hard_opt) {
volatile int lineno = 0;
int i, option;
FILE* f;
char *p, *arg, *start, *buff = daemon->namebuff;
static struct fileread {
dev_t dev;
ino_t ino;
struct fileread* next;
}* filesread = NULL;
struct stat statbuf;
/* ignore repeated files. */
if (hard_opt == 0 && stat(file, &statbuf) == 0) {
struct fileread* r;
for (r = filesread; r; r = r->next)
if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino) return;
r = safe_malloc(sizeof(struct fileread));
r->next = filesread;
filesread = r;
r->dev = statbuf.st_dev;
r->ino = statbuf.st_ino;
}
if (nest > 20) die(_("files nested too deep in %s"), file, EC_BADCONF);
if (!(f = fopen(file, "r"))) {
if (errno == ENOENT && nest == 0)
return; /* No conffile, all done. */
else {
char* str = _("cannot read %s: %s");
if (hard_opt != 0) {
my_syslog(LOG_ERR, str, file, strerror(errno));
return;
} else
die(str, file, EC_FILE);
}
}
while (fgets(buff, MAXDNAME, f)) {
int white;
unsigned int lastquote;
char* errmess;
/* Memory allocation failure longjmps here if mem_recover == 1 */
if (hard_opt) {
if (setjmp(mem_jmp)) continue;
mem_recover = 1;
}
lineno++;
errmess = NULL;
/* Implement quotes, inside quotes we allow \\ \" \n and \t
metacharacters get hidden also strip comments */
for (white = 1, lastquote = 0, p = buff; *p; p++) {
if (*p == '"') {
memmove(p, p + 1, strlen(p + 1) + 1);
for (; *p && *p != '"'; p++) {
if (*p == '\\' && strchr("\"tnebr\\", p[1])) {
if (p[1] == 't')
p[1] = '\t';
else if (p[1] == 'n')
p[1] = '\n';
else if (p[1] == 'b')
p[1] = '\b';
else if (p[1] == 'r')
p[1] = '\r';
else if (p[1] == 'e') /* escape */
p[1] = '\033';
memmove(p, p + 1, strlen(p + 1) + 1);
}
*p = hide_meta(*p);
}
if (*p == '"') {
memmove(p, p + 1, strlen(p + 1) + 1);
lastquote = p - buff;
} else {
errmess = _("missing \"");
goto oops;
}
}
if (white && *p == '#') {
*p = 0;
break;
}
white = isspace((int) unhide_meta(*p));
}
/* fgets gets end of line char too. */
while (strlen(buff) > lastquote && isspace((int) unhide_meta(buff[strlen(buff) - 1])))
buff[strlen(buff) - 1] = 0;
if (*buff == 0) continue;
if (hard_opt != 0)
arg = buff;
else if ((p = strchr(buff, '='))) {
/* allow spaces around "=" */
arg = p + 1;
for (; p >= buff && (isspace((int) *p) || *p == '='); p--) *p = 0;
} else
arg = NULL;
if (hard_opt != 0)
option = hard_opt;
else {
/* skip leading space */
for (start = buff; *start && isspace((int) *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((int) *arg); arg++)
;
errmess = one_opt(option, arg, _("error"), nest + 1);
}
if (errmess) {
oops:
sprintf(buff, _("%s at line %d of %%s"), errmess, lineno);
if (hard_opt != 0)
my_syslog(LOG_ERR, buff, file);
else
die(buff, file, EC_BADCONF);
}
}
mem_recover = 1;
fclose(f);
}
#ifdef HAVE_DHCP
void reread_dhcp(void) {
if (daemon->dhcp_hosts_file) {
struct dhcp_config *configs, *cp, **up;
/* remove existing... */
for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp) {
cp = configs->next;
if (configs->flags & CONFIG_BANK) {
struct hwaddr_config *mac, *tmp;
for (mac = configs->hwaddr; mac; mac = tmp) {
tmp = mac->next;
free(mac);
}
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, LOPT_BANK);
my_syslog(MS_DHCP | LOG_INFO, _("read %s"), daemon->dhcp_hosts_file);
}
if (daemon->dhcp_opts_file) {
struct dhcp_opt *opts, *cp, **up;
struct dhcp_netid *id, *next;
for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp) {
cp = opts->next;
if (opts->flags & DHOPT_BANK) {
if ((opts->flags & DHOPT_VENDOR)) free(opts->u.vendor_class);
free(opts->val);
for (id = opts->netid; id; id = next) {
next = id->next;
free(id->net);
free(id);
}
*up = opts->next;
free(opts);
} else
up = &opts->next;
}
one_file(daemon->dhcp_opts_file, 1, LOPT_OPTS);
my_syslog(MS_DHCP | LOG_INFO, _("read %s"), daemon->dhcp_opts_file);
}
}
#endif
void read_opts(int argc, char** argv, char* compile_opts) {
char* buff = opt_malloc(MAXDNAME);
int option, nest = 0, testmode = 0;
char *errmess, *arg, *conffile = CONFFILE;
opterr = 0;
daemon = opt_malloc(sizeof(struct daemon));
memset(daemon, 0, sizeof(struct daemon));
daemon->namebuff = buff;
/* Set defaults - everything else is zero or NULL */
daemon->cachesize = CACHESIZ;
daemon->ftabsize = FTABSIZ;
daemon->port = NAMESERVER_PORT;
daemon->dhcp_client_port = DHCP_CLIENT_PORT;
daemon->dhcp_server_port = DHCP_SERVER_PORT;
daemon->default_resolv.is_default = 1;
daemon->default_resolv.name = RESOLVFILE;
daemon->resolv_files = &daemon->default_resolv;
daemon->username = CHUSER;
daemon->runfile = RUNFILE;
daemon->dhcp_max = MAXLEASES;
daemon->edns_pktsz = EDNS_PKTSZ;
daemon->log_fac = -1;
add_txt("version.bind", "dnsmasq-" VERSION);
add_txt("authors.bind", "Simon Kelley");
add_txt("copyright.bind", COPYRIGHT);
while (1) {
#ifdef HAVE_GETOPT_LONG
option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
#else
option = getopt(argc, argv, OPTSTRING);
#endif
if (option == -1) break;
/* Copy optarg so that argv doesn't get changed */
if (optarg) {
strncpy(buff, optarg, MAXDNAME);
buff[MAXDNAME - 1] = 0;
arg = buff;
} else
arg = NULL;
/* command-line only stuff */
if (option == LOPT_TEST)
testmode = 1;
else if (option == 'w') {
if (argc != 3 || strcmp(argv[2], "dhcp") != 0) do_usage();
#ifdef HAVE_DHCP
else
display_opts();
#endif
exit(0);
} else if (option == 'v') {
printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
printf(_("Compile time options %s\n\n"), compile_opts);
printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
exit(0);
} else if (option == 'C') {
conffile = opt_string_alloc(arg);
nest++;
} else {
#ifdef HAVE_GETOPT_LONG
errmess = one_opt(option, arg, _("try --help"), 0);
#else
errmess = one_opt(option, arg, _("try -w"), 0);
#endif
if (errmess) die(_("bad command line options: %s"), errmess, EC_BADCONF);
}
}
if (conffile) one_file(conffile, nest, 0);
/* port might not be known when the address is parsed - fill in here */
if (daemon->servers) {
struct server* tmp;
for (tmp = daemon->servers; tmp; tmp = tmp->next)
if (!(tmp->flags & SERV_HAS_SOURCE)) {
if (tmp->source_addr.sa.sa_family == AF_INET)
tmp->source_addr.in.sin_port = htons(daemon->query_port);
#ifdef HAVE_IPV6
else if (tmp->source_addr.sa.sa_family == AF_INET6)
tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
#endif
}
}
if (daemon->if_addrs) {
struct iname* tmp;
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (tmp->addr.sa.sa_family == AF_INET) tmp->addr.in.sin_port = htons(daemon->port);
#ifdef HAVE_IPV6
else if (tmp->addr.sa.sa_family == AF_INET6)
tmp->addr.in6.sin6_port = htons(daemon->port);
#endif /* IPv6 */
}
/* only one of these need be specified: the other defaults to the host-name */
if ((daemon->options & OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget) {
struct mx_srv_record* mx;
if (gethostname(buff, MAXDNAME) == -1) die(_("cannot get host-name: %s"), NULL, EC_MISC);
for (mx = daemon->mxnames; mx; mx = mx->next)
if (!mx->issrv && hostname_isequal(mx->name, buff)) break;
if ((daemon->mxtarget || (daemon->options & OPT_LOCALMX)) && !mx) {
mx = opt_malloc(sizeof(struct mx_srv_record));
mx->next = daemon->mxnames;
mx->issrv = 0;
mx->target = NULL;
mx->name = opt_string_alloc(buff);
daemon->mxnames = mx;
}
if (!daemon->mxtarget) daemon->mxtarget = opt_string_alloc(buff);
for (mx = daemon->mxnames; mx; mx = mx->next)
if (!mx->issrv && !mx->target) mx->target = daemon->mxtarget;
}
if (!(daemon->options & OPT_NO_RESOLV) && daemon->resolv_files && daemon->resolv_files->next &&
(daemon->options & OPT_NO_POLL))
die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
if (daemon->options & OPT_RESOLV_DOMAIN) {
char* line;
FILE* f;
if ((daemon->options & OPT_NO_RESOLV) || !daemon->resolv_files ||
(daemon->resolv_files)->next)
die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
if (!(f = fopen((daemon->resolv_files)->name, "r")))
die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
while ((line = fgets(buff, MAXDNAME, f))) {
char* token = strtok(line, " \t\n\r");
if (!token || strcmp(token, "search") != 0) continue;
if ((token = strtok(NULL, " \t\n\r")) &&
(daemon->domain_suffix = canonicalise_opt(token)))
break;
}
fclose(f);
if (!daemon->domain_suffix)
die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
}
if (daemon->domain_suffix) {
/* add domain for any srv record without one. */
struct mx_srv_record* srv;
for (srv = daemon->mxnames; srv; srv = srv->next)
if (srv->issrv && strchr(srv->name, '.') &&
strchr(srv->name, '.') == strrchr(srv->name, '.')) {
strcpy(buff, srv->name);
strcat(buff, ".");
strcat(buff, daemon->domain_suffix);
free(srv->name);
srv->name = opt_string_alloc(buff);
}
} else if (daemon->options & OPT_DHCP_FQDN)
die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
if (testmode) {
fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
exit(0);
}
}
./COPYING 0000644 0000000 0000000 00000043127 14256750714 010766 0 ustar root root GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C) 19yy
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; either version 2 of the License, or
(at your option) any later version.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
./OWNERS 0000644 0000000 0000000 00000000045 14256750714 010663 0 ustar root root include platform/system/netd:/OWNERS
./MODULE_LICENSE_GPL 0000644 0000000 0000000 00000000000 14256750714 012466 0 ustar root root ./README.version 0000644 0000000 0000000 00000000140 14256750714 012263 0 ustar root root URL: http://www.thekelleys.org.uk/dnsmasq/dnsmasq-2.51.tar.gz
Version: 2.51
BugComponent: 31808
./NOTICE 0000644 0000000 0000000 00000043127 14256750714 010637 0 ustar root root GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C) 19yy
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; either version 2 of the License, or
(at your option) any later version.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
./COPYING-v3 0000644 0000000 0000000 00000104513 14256750714 011311 0 ustar root root GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
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, either version 3 of the License, or
(at your option) any later version.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
./CleanSpec.mk 0000644 0000000 0000000 00000004263 14256750714 012117 0 ustar root root # Copyright (C) 2007 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# If you don't need to do a full clean build but would like to touch
# a file or delete some intermediate files, add a clean step to the end
# of the list. These steps will only be run once, if they haven't been
# run before.
#
# E.g.:
# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
#
# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
# files that are missing or have been moved.
#
# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
# Use $(OUT_DIR) to refer to the "out" directory.
#
# If you need to re-do something that's already mentioned, just copy
# the command and add it to the bottom of the list. E.g., if a change
# that you made last week required touching a file and a change you
# made today requires touching the same file, just copy the old
# touch step and add it to the end of the list.
#
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
# For example:
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
./dnsmasq.conf.example 0000644 0000000 0000000 00000051613 14256750714 013701 0 ustar root root # Configuration file for dnsmasq.
#
# Format is one option per line, legal options are the same
# as the long options legal on the command line. See
# "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.
# The following two options make you a better netizen, since they
# tell dnsmasq to filter out queries which the public DNS cannot
# answer, and which load the servers (especially the root servers)
# uneccessarily. If you have a dial-on-demand link they also stop
# these requests from bringing up the link uneccessarily.
# Never forward plain names (without a dot or domain part)
#domain-needed
# Never forward addresses in the non-routed address spaces.
#bogus-priv
# Uncomment this to filter useless windows-originated DNS requests
# which can trigger dial-on-demand links needlessly.
# Note that (amongst other things) this blocks all SRV requests,
# so don't use it if you use eg Kerberos, SIP, XMMP or Google-talk.
# This option only affects forwarding, SRV records originating for
# dnsmasq (via srv-host= lines) are not suppressed by it.
#filterwin2k
# Change this line if you want dns to get its upstream servers from
# somewhere other that /etc/resolv.conf
#resolv-file=
# By default, dnsmasq will send queries to any of the upstream
# servers it knows about and tries to favour servers to are known
# to be up. Uncommenting this forces dnsmasq to try each query
# with each server strictly in the order they appear in
# /etc/resolv.conf
#strict-order
# If you don't want dnsmasq to read /etc/resolv.conf or any other
# file, getting its servers from this file instead (see below), then
# uncomment this.
#no-resolv
# If you don't want dnsmasq to poll /etc/resolv.conf or other resolv
# files for changes and re-read them then uncomment this.
#no-poll
# Add other name servers here, with domain specs if they are for
# non-public domains.
#server=/localnet/192.168.0.1
# Example of routing PTR queries to nameservers: this will send all
# address->name queries for 192.168.3/24 to nameserver 10.1.2.3
#server=/3.168.192.in-addr.arpa/10.1.2.3
# Add local-only domains here, queries in these domains are answered
# from /etc/hosts or DHCP only.
#local=/localnet/
# Add domains which you want to force to an IP address here.
# The example below send any host in doubleclick.net to a local
# webserver.
#address=/doubleclick.net/127.0.0.1
# --address (and --server) work with IPv6 addresses too.
#address=/www.thekelleys.org.uk/fe80::20d:60ff:fe36:f83
# You can control how dnsmasq talks to a server: this forces
# queries to 10.1.2.3 to be routed via eth1
# --server=10.1.2.3@eth1
# and this sets the source (ie local) address used to talk to
# 10.1.2.3 to 192.168.1.1 port 55 (there must be a interface with that
# IP on the machine, obviously).
# --server=10.1.2.3@192.168.1.1#55
# If you want dnsmasq to change uid and gid to something other
# than the default, edit the following lines.
#user=
#group=
# If you want dnsmasq to listen for DHCP and DNS requests only on
# specified interfaces (and the loopback) give the name of the
# interface (eg eth0) here.
# Repeat the line for more than one interface.
#interface=
# Or you can specify which interface _not_ to listen on
#except-interface=
# Or which to listen on by address (remember to include 127.0.0.1 if
# you use this.)
#listen-address=
# If you want dnsmasq to provide only DNS service on an interface,
# configure it as shown above, and then use the following line to
# disable DHCP on it.
#no-dhcp-interface=
# On systems which support it, dnsmasq binds the wildcard address,
# even when it is listening on only some interfaces. It then discards
# requests that it shouldn't reply to. This has the advantage of
# working even when interfaces come and go and change address. If you
# want dnsmasq to really bind only the interfaces it is listening on,
# uncomment this option. About the only time you may need this is when
# running another nameserver on the same machine.
#bind-interfaces
# If you don't want dnsmasq to read /etc/hosts, uncomment the
# following line.
#no-hosts
# or if you want it to read another file, as well as /etc/hosts, use
# this.
#addn-hosts=/etc/banner_add_hosts
# Set this (and domain: see below) if you want to have a domain
# automatically added to simple names in a hosts-file.
#expand-hosts
# Set the domain for dnsmasq. this is optional, but if it is set, it
# does the following things.
# 1) Allows DHCP hosts to have fully qualified domain names, as long
# as the domain part matches this setting.
# 2) Sets the "domain" DHCP option thereby potentially setting the
# domain of all systems configured by DHCP
# 3) Provides the domain part for "expand-hosts"
#domain=thekelleys.org.uk
# Set a different domain for a particular subnet
#domain=wireless.thekelleys.org.uk,192.168.2.0/24
# Same idea, but range rather then subnet
#domain=reserved.thekelleys.org.uk,192.68.3.100,192.168.3.200
# Uncomment this to enable the integrated DHCP server, you need
# to supply the range of addresses available for lease and optionally
# a lease time. If you have more than one network, you will need to
# repeat this for each network on which you want to supply DHCP
# service.
#dhcp-range=192.168.0.50,192.168.0.150,12h
# This is an example of a DHCP range where the netmask is given. This
# is needed for networks we reach the dnsmasq DHCP server via a relay
# agent. If you don't know what a DHCP relay agent is, you probably
# don't need to worry about this.
#dhcp-range=192.168.0.50,192.168.0.150,255.255.255.0,12h
# This is an example of a DHCP range with a network-id, so that
# some DHCP options may be set only for this network.
#dhcp-range=red,192.168.0.50,192.168.0.150
# Supply parameters for specified hosts using DHCP. There are lots
# of valid alternatives, so we will give examples of each. Note that
# IP addresses DO NOT have to be in the range given above, they just
# need to be on the same network. The order of the parameters in these
# do not matter, it's permissble to give name,adddress and MAC in any order
# Always allocate the host with ethernet address 11:22:33:44:55:66
# The IP address 192.168.0.60
#dhcp-host=11:22:33:44:55:66,192.168.0.60
# Always set the name of the host with hardware address
# 11:22:33:44:55:66 to be "fred"
#dhcp-host=11:22:33:44:55:66,fred
# Always give the host with ethernet address 11:22:33:44:55:66
# the name fred and IP address 192.168.0.60 and lease time 45 minutes
#dhcp-host=11:22:33:44:55:66,fred,192.168.0.60,45m
# Give a host with ethernet address 11:22:33:44:55:66 or
# 12:34:56:78:90:12 the IP address 192.168.0.60. Dnsmasq will assume
# that these two ethernet interfaces will never be in use at the same
# time, and give the IP address to the second, even if it is already
# in use by the first. Useful for laptops with wired and wireless
# addresses.
#dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.60
# Give the machine which says its name is "bert" IP address
# 192.168.0.70 and an infinite lease
#dhcp-host=bert,192.168.0.70,infinite
# Always give the host with client identifier 01:02:02:04
# the IP address 192.168.0.60
#dhcp-host=id:01:02:02:04,192.168.0.60
# Always give the host with client identifier "marjorie"
# the IP address 192.168.0.60
#dhcp-host=id:marjorie,192.168.0.60
# Enable the address given for "judge" in /etc/hosts
# to be given to a machine presenting the name "judge" when
# it asks for a DHCP lease.
#dhcp-host=judge
# Never offer DHCP service to a machine whose ethernet
# address is 11:22:33:44:55:66
#dhcp-host=11:22:33:44:55:66,ignore
# Ignore any client-id presented by the machine with ethernet
# address 11:22:33:44:55:66. This is useful to prevent a machine
# being treated differently when running under different OS's or
# between PXE boot and OS boot.
#dhcp-host=11:22:33:44:55:66,id:*
# Send extra options which are tagged as "red" to
# the machine with ethernet address 11:22:33:44:55:66
#dhcp-host=11:22:33:44:55:66,net:red
# Send extra options which are tagged as "red" to
# any machine with ethernet address starting 11:22:33:
#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
# DHCP vendorclass string includes the substring "Linux"
#dhcp-vendorclass=red,Linux
# Send extra options which are tagged as "red" to any machine one
# of whose DHCP userclass strings includes the substring "accounts"
#dhcp-userclass=red,accounts
# Send extra options which are tagged as "red" to any machine whose
# MAC address matches the pattern.
#dhcp-mac=red,00:60:8C:*:*:*
# If this line is uncommented, dnsmasq will read /etc/ethers and act
# on the ethernet-address/IP pairs found there just as if they had
# been given as --dhcp-host options. Useful if you keep
# MAC-address/host mappings there for other purposes.
#read-ethers
# Send options to hosts which ask for a DHCP lease.
# See RFC 2132 for details of available options.
# Common options can be given to dnsmasq by name:
# run "dnsmasq --help dhcp" to get a list.
# Note that all the common settings, such as netmask and
# broadcast address, DNS server and default route, are given
# sane defaults by dnsmasq. You very likely will not need
# any dhcp-options. If you use Windows clients and Samba, there
# are some options which are recommended, they are detailed at the
# end of this section.
# Override the default route supplied by dnsmasq, which assumes the
# router is the same machine as the one running dnsmasq.
#dhcp-option=3,1.2.3.4
# Do the same thing, but using the option name
#dhcp-option=option:router,1.2.3.4
# Override the default route supplied by dnsmasq and send no default
# route at all. Note that this only works for the options sent by
# default (1, 3, 6, 12, 28) the same line will send a zero-length option
# for all other option numbers.
#dhcp-option=3
# Set the NTP time server addresses to 192.168.0.4 and 10.10.0.5
#dhcp-option=option:ntp-server,192.168.0.4,10.10.0.5
# Set the NTP time server address to be the same machine as
# is running dnsmasq
#dhcp-option=42,0.0.0.0
# Set the NIS domain name to "welly"
#dhcp-option=40,welly
# Set the default time-to-live to 50
#dhcp-option=23,50
# Set the "all subnets are local" flag
#dhcp-option=27,1
# Send the etherboot magic flag and then etherboot options (a string).
#dhcp-option=128,e4:45:74:68:00:00
#dhcp-option=129,NIC=eepro100
# Specify an option which will only be sent to the "red" network
# (see dhcp-range for the declaration of the "red" network)
# Note that the net: part must precede the option: part.
#dhcp-option = net:red, option:ntp-server, 192.168.1.1
# The following DHCP options set up dnsmasq in the same way as is specified
# for the ISC dhcpcd in
# http://www.samba.org/samba/ftp/docs/textdocs/DHCP-Server-Configuration.txt
# adapted for a typical dnsmasq installation where the host running
# dnsmasq is also the host running samba.
# you may want to uncomment some or all of them if you use
# Windows clients and Samba.
#dhcp-option=19,0 # option ip-forwarding off
#dhcp-option=44,0.0.0.0 # set netbios-over-TCP/IP nameserver(s) aka WINS server(s)
#dhcp-option=45,0.0.0.0 # netbios datagram distribution server
#dhcp-option=46,8 # netbios node type
# Send RFC-3397 DNS domain search DHCP option. WARNING: Your DHCP client
# probably doesn't support this......
#dhcp-option=option:domain-search,eng.apple.com,marketing.apple.com
# Send RFC-3442 classless static routes (note the netmask encoding)
#dhcp-option=121,192.168.1.0/24,1.2.3.4,10.0.0.0/8,5.6.7.8
# Send vendor-class specific options encapsulated in DHCP option 43.
# The meaning of the options is defined by the vendor-class so
# options are sent only when the client supplied vendor class
# matches the class given here. (A substring match is OK, so "MSFT"
# matches "MSFT" and "MSFT 5.0"). This example sets the
# mtftp address to 0.0.0.0 for PXEClients.
#dhcp-option=vendor:PXEClient,1,0.0.0.0
# Send microsoft-specific option to tell windows to release the DHCP lease
# when it shuts down. Note the "i" flag, to tell dnsmasq to send the
# value as a four-byte integer - that's what microsoft wants. See
# http://technet2.microsoft.com/WindowsServer/en/library/a70f1bb7-d2d4-49f0-96d6-4b7414ecfaae1033.mspx?mfr=true
#dhcp-option=vendor:MSFT,2,1i
# Send the Encapsulated-vendor-class ID needed by some configurations of
# Etherboot to allow is to recognise the DHCP server.
#dhcp-option=vendor:Etherboot,60,"Etherboot"
# Send options to PXELinux. Note that we need to send the options even
# though they don't appear in the parameter request list, so we need
# to use dhcp-option-force here.
# See http://syslinux.zytor.com/pxe.php#special for details.
# Magic number - needed before anything else is recognised
#dhcp-option-force=208,f1:00:74:7e
# Configuration file name
#dhcp-option-force=209,configs/common
# Path prefix
#dhcp-option-force=210,/tftpboot/pxelinux/files/
# Reboot time. (Note 'i' to send 32-bit value)
#dhcp-option-force=211,30i
# Set the boot filename for netboot/PXE. You will only need
# this is you want to boot machines over the network and you will need
# a TFTP server; either dnsmasq's built in TFTP server or an
# external one. (See below for how to enable the TFTP server.)
#dhcp-boot=pxelinux.0
# Boot for Etherboot gPXE. The idea is to send two different
# filenames, the first loads gPXE, and the second tells gPXE what to
# load. The dhcp-match sets the gpxe tag for requests from gPXE.
#dhcp-match=gpxe,175 # gPXE sends a 175 option.
#dhcp-boot=net:#gpxe,undionly.kpxe
#dhcp-boot=mybootimage
# Encapsulated options for Etherboot gPXE. All the options are
# encapsulated within option 175
#dhcp-option=encap:175, 1, 5b # priority code
#dhcp-option=encap:175, 176, 1b # no-proxydhcp
#dhcp-option=encap:175, 177, string # bus-id
#dhcp-option=encap:175, 189, 1b # BIOS drive code
#dhcp-option=encap:175, 190, user # iSCSI username
#dhcp-option=encap:175, 191, pass # iSCSI password
# Test for the architecture of a netboot client. PXE clients are
# supposed to send their architecture as option 93. (See RFC 4578)
#dhcp-match=peecees, option:client-arch, 0 #x86-32
#dhcp-match=itanics, option:client-arch, 2 #IA64
#dhcp-match=hammers, option:client-arch, 6 #x86-64
#dhcp-match=mactels, option:client-arch, 7 #EFI x86-64
# Do real PXE, rather than just booting a single file, this is an
# alternative to dhcp-boot.
#pxe-prompt="What system shall I netboot?"
# or with timeout before first available action is taken:
#pxe-prompt="Press F8 for menu.", 60
# Available boot services. for PXE.
#pxe-service=x86PC, "Boot from local disk", 0
# Loads /pxelinux.0 from dnsmasq TFTP server.
#pxe-service=x86PC, "Install Linux", pxelinux
# Loads /pxelinux.0 from TFTP server at 1.2.3.4.
# Beware this fails on old PXE ROMS.
#pxe-service=x86PC, "Install Linux", pxelinux, 1.2.3.4
# Use bootserver on network, found my multicast or broadcast.
#pxe-service=x86PC, "Install windows from RIS server", 1
# Use bootserver at a known IP address.
#pxe-service=x86PC, "Install windows from RIS server", 1, 1.2.3.4
# If you have multicast-FTP available,
# information for that can be passed in a similar way using options 1
# to 5. See page 19 of
# http://download.intel.com/design/archives/wfm/downloads/pxespec.pdf
# Enable dnsmasq's built-in TFTP server
#enable-tftp
# Set the root directory for files availble via FTP.
#tftp-root=/var/ftpd
# Make the TFTP server more secure: with this set, only files owned by
# the user dnsmasq is running as will be send over the net.
#tftp-secure
# This option stops dnsmasq from negotiating a larger blocksize for TFTP
# transfers. It will slow things down, but may rescue some broken TFTP
# clients.
#tftp-no-blocksize
# Set the boot file name only when the "red" tag is set.
#dhcp-boot=net:red,pxelinux.red-net
# An example of dhcp-boot with an external TFTP server: the name and IP
# address of the server are given after the filename.
# Can fail with old PXE ROMS. Overridden by --pxe-service.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,192.168.0.3
# Set the limit on DHCP leases, the default is 150
#dhcp-lease-max=150
# The DHCP server needs somewhere on disk to keep its lease database.
# This defaults to a sane location, but if you want to change it, use
# the line below.
#dhcp-leasefile=/var/lib/misc/dnsmasq.leases
# Set the DHCP server to authoritative mode. In this mode it will barge in
# and take over the lease for any client which broadcasts on the network,
# 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
# the slighest chance that you might end up accidentally configuring a DHCP
# server for your campus/company accidentally. The ISC server uses
# the same option, and this URL provides more information:
# http://www.isc.org/index.pl?/sw/dhcp/authoritative.php
#dhcp-authoritative
# Run an executable when a DHCP lease is created or destroyed.
# The arguments sent to the script are "add" or "del",
# then the MAC address, the IP address and finally the hostname
# if there is one.
#dhcp-script=/bin/echo
# Set the cachesize here.
#cache-size=150
# If you want to disable negative caching, uncomment this.
#no-negcache
# Normally responses which come form /etc/hosts and the DHCP lease
# file have Time-To-Live set as zero, which conventionally means
# do not cache further. If you are happy to trade lower load on the
# server for potentially stale date, you can set a time-to-live (in
# seconds) here.
#local-ttl=
# If you want dnsmasq to detect attempts by Verisign to send queries
# to unregistered .com and .net hosts to its sitefinder service and
# have dnsmasq instead return the correct NXDOMAIN response, uncomment
# this line. You can add similar lines to do the same for other
# registries which have implemented wildcard A records.
#bogus-nxdomain=64.94.110.11
# If you want to fix up DNS results from upstream servers, use the
# alias option. This only works for IPv4.
# This alias makes a result of 1.2.3.4 appear as 5.6.7.8
#alias=1.2.3.4,5.6.7.8
# and this maps 1.2.3.x to 5.6.7.x
#alias=1.2.3.0,5.6.7.0,255.255.255.0
# and this maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40
#alias=192.168.0.10-192.168.0.40,10.0.0.0,255.255.255.0
# Change these lines if you want dnsmasq to serve MX records.
# Return an MX record named "maildomain.com" with target
# servermachine.com and preference 50
#mx-host=maildomain.com,servermachine.com,50
# Set the default target for MX records created using the localmx option.
#mx-target=servermachine.com
# Return an MX record pointing to the mx-target for all local
# machines.
#localmx
# Return an MX record pointing to itself for all local machines.
#selfmx
# Change the following lines if you want dnsmasq to serve SRV
# records. These are useful if you want to serve ldap requests for
# Active Directory and other windows-originated DNS requests.
# See RFC 2782.
# You may add multiple srv-host lines.
# The fields are ,,,,
# If the domain part if missing from the name (so that is just has the
# service and protocol sections) then the domain given by the domain=
# config option is used. (Note that expand-hosts does not need to be
# set for this to work.)
# A SRV record sending LDAP for the example.com domain to
# ldapserver.example.com port 289
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389
# A SRV record sending LDAP for the example.com domain to
# ldapserver.example.com port 289 (using domain=)
#domain=example.com
#srv-host=_ldap._tcp,ldapserver.example.com,389
# Two SRV records for LDAP, each with different priorities
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,1
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,2
# A SRV record indicating that there is no LDAP server for the domain
# example.com
#srv-host=_ldap._tcp.example.com
# The following line shows how to make dnsmasq serve an arbitrary PTR
# record. This is useful for DNS-SD. (Note that the
# domain-name expansion done for SRV records _does_not
# occur for PTR records.)
#ptr-record=_http._tcp.dns-sd-services,"New Employee Page._http._tcp.dns-sd-services"
# Change the following lines to enable dnsmasq to serve TXT records.
# These are used for things like SPF and zeroconf. (Note that the
# domain-name expansion done for SRV records _does_not
# occur for TXT records.)
#Example SPF.
#txt-record=example.com,"v=spf1 a -all"
#Example zeroconf
#txt-record=_http._tcp.example.com,name=value,paper=A4
# Provide an alias for a "local" DNS name. Note that this _only_ works
# for targets which are names from DHCP or /etc/hosts. Give host
# "bert" another name, bertrand
#cname=bertand,bert
# For debugging purposes, log each DNS query as it passes through
# dnsmasq.
#log-queries
# Log lots of extra information about DHCP transactions.
#log-dhcp
# Include a another lot of configuration options.
#conf-file=/etc/dnsmasq.more.conf
#conf-dir=/etc/dnsmasq.d
./CHANGELOG 0000644 0000000 0000000 00000044076 14256750714 011151 0 ustar root root version 2.51
Add support for internationalised DNS. Non-ASCII characters
in domain names found in /etc/hosts, /etc/ethers and
/etc/dnsmasq.conf will be correctly handled by translation to
punycode, as specified in RFC3490. This function is only
available if dnsmasq is compiled with internationalisation
support, and adds a dependency on GNU libidn. Without i18n
support, dnsmasq continues to be compilable with just
standard tools. Thanks to Yves Dorfsman for the
suggestion.
Add two more environment variables for lease-change scripts:
First, DNSMASQ_SUPPLIED_HOSTNAME; this is set to the hostname
supplied by a client, even if the actual hostname used is
over-ridden by dhcp-host or dhcp-ignore-names directives.
Also DNSMASQ_RELAY_ADDRESS which gives the address of
a DHCP relay, if used.
Suggestions from Michael Rack.
Fix regression which broke echo of relay-agent
options. Thanks to Michael Rack for spotting this.
Don't treat option 67 as being interchangeable with
dhcp-boot parameters if it's specified as
dhcp-option-force.
Make the code to call scripts on lease-change compile-time
optional. It can be switched off by editing src/config.h
or building with "make COPTS=-DNO_SCRIPT".
Make the TFTP server cope with filenames from Windows/DOS
which use '\' as pathname separator. Thanks to Ralf for
the patch.
Updated Polish translation. Thanks to Jan Psota.
Warn if an IP address is duplicated in /etc/ethers. Thanks
to Felix Schwarz for pointing this out.
Teach --conf-dir to take an option list of file suffices
which will be ignored when scanning the directory. Useful
for backup files etc. Thanks to Helmut Hullen for the
suggestion.
Add new DHCP option named tftpserver-address, which
corresponds to the third argument of dhcp-boot. This
allows the complete functionality of dhcp-boot to be
replicated with dhcp-option. Useful when using
dhcp-optsfile.
Test which upstream nameserver to use every 10 seconds
or 50 queries and not just when a query times out and
is retried. This should improve performance when there
is a slow nameserver in the list. Thanks to Joe for the
suggestion.
Don't do any PXE processing, even for clients with the
correct vendorclass, unless at least one pxe-prompt or
pxe-service option is given. This stops dnsmasq
interfering with proxy PXE subsystems when it is just
the DHCP server. Thanks to Spencer Clark for spotting this.
Limit the blocksize used for TFTP transfers to a value
which avoids packet fragmentation, based on the MTU of the
local interface. Many netboot ROMs can't cope with
fragmented packets.
Honour dhcp-ignore configuration for PXE and proxy-PXE
requests. Thanks to Niels Basjes for the bug report.
Updated French translation. Thanks to Gildas Le Nadan.
version 2.50
Fix security problem which allowed any host permitted to
do TFTP to possibly compromise dnsmasq by remote buffer
overflow when TFTP enabled. Thanks to Core Security
Technologies and Iván Arce, Pablo Hernán Jorge, Alejandro
Pablo Rodriguez, MartÃn Coco, Alberto Soliño Testa and
Pablo Annetta. This problem has Bugtraq id: 36121
and CVE: 2009-2957
Fix a problem which allowed a malicious TFTP client to
crash dnsmasq. Thanks to Steve Grubb at Red Hat for
spotting this. This problem has Bugtraq id: 36120 and
CVE: 2009-2958
version 2.49
Fix regression in 2.48 which disables the lease-change
script. Thanks to Jose Luis Duran for spotting this.
Log TFTP "file not found" errors. These were not logged,
since a normal PXELinux boot generates many of them, but
the lack of the messages seems to be more confusing than
routinely seeing them when there is no real error.
Update Spanish translation. Thanks to Chris Chatham.
version 2.48
Archived the extensive, backwards, changelog to
CHANGELOG.archive. The current changelog now runs from
version 2.43 and runs conventionally.
Fixed bug which broke binding of servers to physical
interfaces when interface names were longer than four
characters. Thanks to MURASE Katsunori for the patch.
Fixed netlink code to check that messages come from the
correct source, and not another userspace process. Thanks
to Steve Grubb for the patch.
Maintainability drive: removed bug and missing feature
workarounds for some old platforms. Solaris 9, OpenBSD
older than 4.1, Glibc older than 2.2, Linux 2.2.x and
DBus older than 1.1.x are no longer supported.
Don't read included configuration files more than once:
allows complex configuration structures without problems.
Mark log messages from the various subsystems in dnsmasq:
messages from the DHCP subsystem now have the ident string
"dnsmasq-dhcp" and messages from TFTP have ident
"dnsmasq-tftp". Thanks to Olaf Westrik for the patch.
Fix possible infinite DHCP protocol loop when an IP
address nailed to a hostname (not a MAC address) and a
host sometimes provides the name, sometimes not.
Allow --addn-hosts to take a directory: all the files
in the directory are read. Thanks to Phil Cornelius for
the suggestion.
Support --bridge-interface on all platforms, not just BSD.
Added support for advanced PXE functions. It's now
possible to define a prompt and menu options which will
be displayed when a client PXE boots. It's also possible to
hand-off booting to other boot servers. Proxy-DHCP, where
dnsmasq just supplies the PXE information and another DHCP
server does address allocation, is also allowed. See the
--pxe-prompt and --pxe-service keywords. Thanks to
Alkis Georgopoulos for the suggestion and Guilherme Moro
and Michael Brown for assistance.
Improvements to DHCP logging. Thanks to Tom Metro for
useful suggestions.
Add ability to build dnsmasq without DHCP support. To do
this, edit src/config.h or build with
"make COPTS=-DNO_DHCP". Thanks to Mahavir Jain for the patch.
Added --test command-line switch - syntax check
configuration files only.
Updated French translation. Thanks to Gildas Le Nadan.
version 2.47
Updated French translation. Thanks to Gildas Le Nadan.
Fixed interface enumeration code to work on NetBSD
5.0. Thanks to Roy Marples for the patch.
Updated config.h to use the same location for the lease
file on NetBSD as the other *BSD variants. Also allow
LEASEFILE and CONFFILE symbols to be overriden in CFLAGS.
Handle duplicate address detection on IPv6 more
intelligently. In IPv6, an interface can have an address
which is not usable, because it is still undergoing DAD
(such addresses are marked "tentative"). Attempting to
bind to an address in this state returns an error,
EADDRNOTAVAIL. Previously, on getting such an error,
dnsmasq would silently abandon the address, and never
listen on it. Now, it retries once per second for 20
seconds before generating a fatal error. 20 seconds should
be long enough for any DAD process to complete, but can be
adjusted in src/config.h if necessary. Thanks to Martin
Krafft for the bug report.
Add DBus introspection. Patch from Jeremy Laine.
Update Dbus configuration file. Patch from Colin Walters.
Fix for this bug:
http://bugs.freedesktop.org/show_bug.cgi?id=18961
Support arbitrarily encapsulated DHCP options, suggestion
and initial patch from Samium Gromoff. This is useful for
(eg) gPXE, which expect all its private options to be
encapsulated inside a single option 175. So, eg,
dhcp-option = encap:175, 190, "iscsi-client0"
dhcp-option = encap:175, 191, "iscsi-client0-secret"
will provide iSCSI parameters to gPXE.
Enhance --dhcp-match to allow testing of the contents of a
client-sent option, as well as its presence. This
application in mind for this is RFC 4578
client-architecture specifiers, but it's generally useful.
Joey Korkames suggested the enhancement.
Move from using the IP_XMIT_IF ioctl to IP_BOUND_IF on
OpenSolaris. Thanks to Bastian Machek for the heads-up.
No longer complain about blank lines in
/etc/ethers. Thanks to Jon Nelson for the patch.
Fix binding of servers to physical devices, eg
--server=/domain/1.2.3.4@eth0 which was broken from 2.43
onwards unless --query-port=0 set. Thanks to Peter Naulls
for the bug report.
Reply to DHCPINFORM requests even when the supplied ciaddr
doesn't fall in any dhcp-range. In this case it's not
possible to supply a complete configuration, but
individually-configured options (eg PAC) may be useful.
Allow the source address of an alias to be a range:
--alias=192.168.0.0,10.0.0.0,255.255.255.0 maps the whole
subnet 192.168.0.0->192.168.0.255 to 10.0.0.0->10.0.0.255,
as before.
--alias=192.168.0.10-192.168.0.40,10.0.0.0,255.255.255.0
maps only the 192.168.0.10->192.168.0.40 region. Thanks to
Ib Uhrskov for the suggestion.
Don't dynamically allocate DHCP addresses which may break
Windows. Addresses which end in .255 or .0 are broken in
Windows even when using supernetting.
--dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0 means
192.168.0.255 is a valid IP address, but not for Windows.
See Microsoft KB281579. We therefore no longer allocate
these addresses to avoid hard-to-diagnose problems.
Update Polish translation. Thanks to Jan Psota.
Delete the PID-file when dnsmasq shuts down. Note that by
this time, dnsmasq is normally not running as root, so
this will fail if the PID-file is stored in a root-owned
directory; such failure is silently ignored. To take
advantage of this feature, the PID-file must be stored in a
directory owned and write-able by the user running
dnsmasq.
version 2.46
Allow --bootp-dynamic to take a netid tag, so that it may
be selectively enabled. Thanks to Olaf Westrik for the
suggestion.
Remove ISC-leasefile reading code. This has been
deprecated for a long time, and last time I removed it, it
ended up going back by request of one user. This time,
it's gone for good; otherwise it would need to be
re-worked to support multiple domains (see below).
Support DHCP clients in multiple DNS domains. This is a
long-standing request. Clients are assigned to a domain
based in their IP address.
Add --dhcp-fqdn flag, which changes behaviour if DNS names
assigned to DHCP clients. When this is set, there must be
a domain associated with each client, and only
fully-qualified domain names are added to the DNS. The
advantage is that the only the FQDN needs to be unique,
so that two or more DHCP clients can share a hostname, as
long as they are in different domains.
Set environment variable DNSMASQ_DOMAIN when invoking
lease-change script. This may be useful information to
have now that it's variable.
Tighten up data-checking code for DNS packet
handling. Thanks to Steve Dodd who found certain illegal
packets which could crash dnsmasq. No memory overwrite was
possible, so this is not a security issue beyond the DoS
potential.
Update example config dhcp option 47, the previous
suggestion generated an illegal, zero-length,
option. Thanks to Matthias Andree for finding this.
Rewrite hosts-file reading code to remove the limit of
1024 characters per line. John C Meuser found this.
Create a net-id tag with the name of the interface on
which the DHCP request was received.
Fixed minor memory leak in DBus code, thanks to Jeremy
Laine for the patch.
Emit DBus signals as the DHCP lease database
changes. Thanks to Jeremy Laine for the patch.
Allow for more that one MAC address in a dhcp-host
line. This configuration tells dnsmasq that it's OK to
abandon a DHCP lease of the fixed address to one MAC
address, if another MAC address in the dhcp-host statement
asks for an address. This is useful to give a fixed
address to a host which has two network interfaces
(say, a laptop with wired and wireless interfaces.)
It's very important to ensure that only one interface
at a time is up, since dnsmasq abandons the first lease
and re-uses the address before the leased time has
elapsed. John Gray suggested this.
Tweak the response to a DHCP request packet with a wrong
server-id when --dhcp-authoritative is set; dnsmasq now
returns a DHCPNAK, rather than silently ignoring the
packet. Thanks to Chris Marget for spotting this
improvement.
Add --cname option. This provides a limited alias
function, usable for DHCP names. Thanks to AJ Weber for
suggestions on this.
Updated contrib/webmin with latest version from Neil
Fisher.
Updated Polish translation. Thanks to Jan Psota.
Correct the text names for DHCP options 64 and 65 to be
"nis+-domain" and "nis+-servers".
Updated Spanish translation. Thanks to Chris Chatham.
Force re-reading of /etc/resolv.conf when an "interface
up" event occurs.
version 2.45
Fix total DNS failure in release 2.44 unless --min-port
specified. Thanks to Steven Barth and Grant Coady for
bugreport. Also reject out-of-range port spec, which could
break things too: suggestion from Gilles Espinasse.
version 2.44
Fix crash when unknown client attempts to renew a DHCP
lease, problem introduced in version 2.43. Thanks to
Carlos Carvalho for help chasing this down.
Fix potential crash when a host which doesn't have a lease
does DHCPINFORM. Again introduced in 2.43. This bug has
never been reported in the wild.
Fix crash in netlink code introduced in 2.43. Thanks to
Jean Wolter for finding this.
Change implementation of min_port to work even if min-port
is large.
Patch to enable compilation of latest Mac OS X. Thanks to
David Gilman.
Update Spanish translation. Thanks to Christopher Chatham.
version 2.43
Updated Polish translation. Thanks to Jan Psota.
Flag errors when configuration options are repeated
illegally.
Further tweaks for GNU/kFreeBSD
Add --no-wrap to msgmerge call - provides nicer .po file
format.
Honour lease-time spec in dhcp-host lines even for
BOOTP. The user is assumed to known what they are doing in
this case. (Hosts without the time spec still get infinite
leases for BOOTP, over-riding the default in the
dhcp-range.) Thanks to Peter Katzmann for uncovering this.
Fix problem matching relay-agent ids. Thanks to Michael
Rack for the bug report.
Add --naptr-record option. Suggestion from Johan
Bergquist.
Implement RFC 5107 server-id-override DHCP relay agent
option.
Apply patches from Stefan Kruger for compilation on
Solaris 10 under Sun studio.
Yet more tweaking of Linux capability code, to suppress
pointless wingeing from kernel 2.6.25 and above.
Improve error checking during startup. Previously, some
errors which occurred during startup would be worked
around, with dnsmasq still starting up. Some were logged,
some silent. Now, they all cause a fatal error and dnsmasq
terminates with a non-zero exit code. The errors are those
associated with changing uid and gid, setting process
capabilities and writing the pidfile. Thanks to Uwe
Gansert and the Suse security team for pointing out
this improvement, and Bill Reimers for good implementation
suggestions.
Provide NO_LARGEFILE compile option to switch off largefile
support when compiling against versions of uclibc which
don't support it. Thanks to Stephane Billiart for the patch.
Implement random source ports for interactions with
upstream nameservers. New spoofing attacks have been found
against nameservers which do not do this, though it is not
clear if dnsmasq is vulnerable, since to doesn't implement
recursion. By default dnsmasq will now use a different
source port (and socket) for each query it sends
upstream. This behaviour can suppressed using the
--query-port option, and the old default behaviour
restored using --query-port=0. Explicit source-port
specifications in --server configs are still honoured.
Replace the random number generator, for better
security. On most BSD systems, dnsmasq uses the
arc4random() RNG, which is secure, but on other platforms,
it relied on the C-library RNG, which may be
guessable and therefore allow spoofing. This release
replaces the libc RNG with the SURF RNG, from Daniel
J. Berstein's DJBDNS package.
Don't attempt to change user or group or set capabilities
if dnsmasq is run as a non-root user. Without this, the
change from soft to hard errors when these fail causes
problems for non-root daemons listening on high
ports. Thanks to Patrick McLean for spotting this.
Updated French translation. Thanks to Gildas Le Nadan.
version 2.42
The changelog for version 2.42 and earlier is
available in CHANGELOG.archive.
./CHANGELOG.archive 0000644 0000000 0000000 00000315015 14256750714 012563 0 ustar root root release 0.4 - initial public release
release 0.5 - added caching, removed compiler warning on linux PPC
release 0.6 - TCP handling: close socket and return to connect state if we
can't read the first byte. This corrects a problem seen very
occasionally where dnsmasq would loop using all available CPU.
Added a patch from Cris Bailiff
to set SO_REUSEADDR on the tcp socket which stops problems when
dnsmasq is restarted and old connections still exist.
Stopped claiming in doc.html that smail is the default Debian
mailer, since it isn't any longer. (Pointed out by
David Karlin )
release 0.7 Create a pidfile at /var/run/dnsmasq.pid
Extensive armouring against "poison packets" courtesy of
Thomas Moestl
Set sockaddr.sa_family on outgoing address, patch from
David Symonds
Patch to clear cache on SIGHUP
from Jason L. Wagner
Fix bad bug resulting from not initialising value-result
address-length parameter to recvfrom() and accept() - it
worked by luck before!
release 0.95 Major rewrite: remove calls to gethostbyname() and talk
directly to the upstream server(s) instead.
This has many advantages.
(1) Dnsmasq no longer blocks during long lookups.
(2) All query types are handled now, (eg MX) not just internet
address queries. Addresses are cached, all other
queries are forwarded directly.
(3) Time-to-live data from upstream server is read and
used by dnsmasq to purge entries from the cache.
(4) /etc/hosts is still read and its contents served (unless
the -h option is given).
(5) Dnsmasq can get its upstream servers from
a file other than /etc/resolv.conf (-r option) this allows
dnsmasq to serve names to the machine it is running
on (put nameserver 127.0.0.1 in /etc/resolv.conf and
give dnsmasq the option -r /etc/resolv.dnsmasq)
(6) Dnsmasq will re-read its servers if the
modification time of resolv.conf changes. Along with
4 above this allows nameservers to be set
automatically by ppp or dhcp.
A really clever NAT-like technique allows the daemon to have lots
of queries in progress, but still remain very lightweight.
Dnsmasq has a small footprint and normally doesn't allocate
any more memory after start-up. The NAT-like forwarding was
inspired by a suggestion from Eli Chen
release 0.96 Fixed embarrasing thinko in cache linked-list code.
release 0.98 Some enhancements and bug-fixes.
Thanks to "Denis Carre" and Martin
Otte
(1) Dnsmasq now always sets the IP source address
of its replies correctly. Older versions would not always
do this on multi-homed and IP aliased hosts, which violates
the RFC.
(2) Dnsmasq no longer crashes if a server loop is created
(ie dnsmasq is told to use itself as an upstream server.)
Now it just logs the problem and doesn't use the bad
server address.
(3) Dnsmasq should now forward (but not cache) inverse queries
and server status queries; this feature has not been tested.
(4) Don't write the pid file when in non-daemon mode.
(5) Create the pid file mode 644, rather then 666 (!).
(6) Generate queries to upstream nameservers with unpredictable
ids, to thwart DNS spoofers.
(7) Dnsmasq no longer forwards queries when the
"recursion desired" bit is not set in the header.
(8) Fixed getopt code to work on compliers with unsigned char.
release 0.991 Added -b flag: when set causes dnsmasq to always answer
reverse queries on the RFC 1918 private IP space itself and
never forward them to an upstream server. If the name is not in
/etc/hosts, dnsmasq replies with the dotted-quad address.
Fixed a bug which stopped dnsmasq working on a box with
two or more interfaces with the same IP address.
Fixed cacheing of CNAMEs. Previously, a CNAME which pointed
to a name with many A records would not have all the addresses
returned when being answered from the cache.
Thanks to "Steve Hardy" for his input
on these fixes.
Fixed race which could cause dnsmasq to miss the second of
two closely-spaced updates of resolv.conf (Thanks to Eli Chen
for pointing this out.)
Fixed a bug which could cause dnsmasq to fail to cache some
dns names.
release 0.992 Small change to memory allocation so that names in /etc/hosts
don't use cache slots. Also make "-c 0" flag meaningfully
disable caching completely.
release 0.993 Return only the first (canonical) name from an entry in
/etc/hosts as reply to reverse query.
Handle wildcard queries for names/addresses in /etc/hosts
this is mainly to allow reverse lookups by dig to succeed.
(Bug reported by Simon J. Rowe" )
Subtle change to the logic which selects which of multiple
upstream servers we send queries to. This fixes a problem
where dnsmasq continuously sends queries to a server which
is returning error codes and ignores one which is working.
release 0.994 Fixed bug which broke lookup of names in /etc/hosts
which have upper-case letters in them. Thanks for Joao Clemente
for spotting that one.
Output cache statistics on receipt of SIGUSR1. These go
to syslog except in debug (-d) mode, when a complete cache
dump goes to stdout. Suggestion from Joao Clemente, code
based in John Volpe's.
Accept GNU long options on the command line. Code from
John Volpe for this.
Split source code into multiple files and produced
a proper makefile.
Included code from John Volpe to parse dhcp.leases file
written by ISC dhcpd. The hostnames in the leases file are
added to the cache and updated as dhcpd updates the
leases file. The code has been heavily re-worked by me,
so any bugs are probably mine.
release 0.995 Small tidy-ups to signal handling and cache code.
release 0.996 Added negative caching: If dnsmasq gets a "no such domain" reply
from an upstream nameserver, it will cache that information
for a time specified by the SOA RR in the reply. See RFC 2308
for details. This is useful with resolver libraries
which append assorted suffices to non-FQDN in an attempt to
resolve them, causing useless cache misses.
Added -i flag, which restricts dnsmasq to offering name service
only on specified interfaces.
release 0.997 Deleted INSTALL script and added "install" target to makefile.
Stopped distributing binaries in the tarball to avoid
libc version clashes.
Fixed interface detection code to
remove spurious startup errors in rare circumstances.
Dnsmasq now changes its uid, irrevocably, to nobody after
startup for security reasons. Thanks to Peter Bailey for
this patch.
Cope with infinite DHCP leases. Patch thanks to
Yaacov Akiba Slama.
Added rpm control files to .tar.gz distribution. Thanks to
Peter Baldwin at ClarkConnect for those.
Improved startup script for rpms. Thanks to Yaacov Akiba Slama.
release 1.0 Stable release: dnsmasq is now considered feature-complete
and stable.
release 1.1 Added --user argument to allow user to change to
a different userid.
Added --mx-target argument to allow mail to be delivered
away from the gateway machine running dnsmasq.
Fixed highly obscure bug with wildcard queries for
DHCP lease derived names.
Moved manpage from section 1 to section 8.
Added --no-poll option.
Added Suse-rpm support.
Thanks to Joerg Mayer for the last two.
release 1.2 Added IPv6 DNS record support. AAAA records are cached
and read from /etc/hosts. Reverse-lookups in the
ip6.int and ip6.arpa domains are suppored. Dnsmasq can
talk to upstream servers via IPv6 if it finds IP6 addresses
in /etc/resolv.conf and it offers DNS service automatically
if IPv6 support is present in the kernel.
Extended negative caching to NODATA replies.
Re-vamped CNAME processing to cope with RFC 2317's use of
CNAMES to PTR RRs in CIDR.
Added config.h and a couple of symbols to aid
compilation on non-linux systems.
release 1.3 Some versions of the Linux kernel return EINVAL rather
then ENPROTONOSUPPORT when IPv6 is not available,
causing dnsmasq to bomb out. This release fixes that.
Thanks to Steve Davis for pointing this one out.
Trivial change to startup logic so that dnsmasq logs
its stuff and reads config files straight away on
starting, rather than after the first query - principle
of least surprise applies here.
release 1.4 Fix a bug with DHPC lease parsing which broke in
non-UTC timezones. Thanks to Mark Wormgoor for
spotting and diagnosing this. Fixed versions in
the .spec files this time. Fixed bug in Suse startup
script. Thanks to Didi Niklaus for pointing this out.
release 1.5 Added --filterwin2k option which stops dnsmasq from forwarding
"spam" queries from win2k boxes. This is useful to stop spurious
connections over dial-on-demand links. Thanks to Steve Hardy
for this code.
Clear "truncated" bit in replies we return from upstream. This
stops resolvers from switching to TCP, which is pointless since
dnsmasq doesn't support TCP. This should solve problems
in resolving hotmail.com domains.
Don't include getopt.h when Gnu-long-options are disabled -
hopefully this will allow compilation on FreeBSD.
Added the --listen-address and --pid-file flags.
Fixed a bug which caused old entries in the DHCP leases file
to be used in preference to current ones under certain
circumstances.
release 1.6 If a machine gets named via DHCP and the DHCP name doesn't have
a domain part and domain suffix is set using the -s flag, then
that machine has two names with the same address, with and
without the domain suffix. When doing a _reverse_ lookup to
get the name, the "without suffix" name used to be returned,
now the "with suffix" one gets returned instead. This change
suggested by Arnold Schulz.
Fixed assorted typos in the documentation. Thanks
to David Kimdon.
Subtle rearrangement to the downloadable tarball, and stopped
distributing .debs, since dnsmasq is now an official Debian
package.
release 1.7 Fix a problem with cache not clearing properly
on receipt of SIGHUP. Bug spotted by Sat Deshpande.
In group-id changing code:
1) Drop supplimentary groups.
2) Change gid before dropping root (patch from Soewono Effendi.)
3) Change group to "dip" if it exists, to allow access
to /etc/ppp/resolv.conf (suggestion from Jorg Sommer.)
Update docs to reflect above changes.
Other documentation changes from David Miller.
Added suggested script fragment for dhcpcd.exe.
release 1.8 Fix unsafe use of tolower() macro - allows linking against
ulibc. (Patches from Soewono Effendi and Bjorn Andersson.)
Fix typo in usage string.
Added advice about RedHat PPP configuration to
documentation. (Thanks to C. Lee Taylor.)
Patches to fix problems on BSD systems from Marc Huber
and Can Erkin Acar. These add the options
HAVE_ARC4RANDOM and HAVE_SOCKADDR_SA_LEN to config.h.
Elaborated config.h - should really use autoconf.
Fix time-to-live calculation when chasing CNAMEs.
Fix use-after-free and missing initialisation bugs in
the cache code. (Thanks to Marc Huber.)
Builds on Solaris 9. (Thanks to Marc Huber.)
release 1.9 Fixes to rpm .spec files.
Don't put expired DHCP entries into the cache only to
throw them away again.
Put dnsmasq on a severe memory diet: this reduces both
the amount of heap space used and the stack size
required. The difference is not really visible with
bloated libcs like glibc, but should dramatically reduce
memory requirements when linked against ulibc for use on
embeded routers, and that's the point really. Thanks to
Matthew Natalier for prompting this.
Changed debug mode (-d) so that all logging appears on
stderr as well as going to syslogd.
Added HAVE_IPV6 config symbol to allow compilation
against a libc which doesn't have IPv6 support.
Added a facility to log all queries, enabled with -q flag.
Fixed packet size checking bug in address extraction code.
Halved default cache size - 300 was way OTT in typical use.
Added self-MX function, enabled by -e flag. Thanks to
Lyonel Vincent for the patch.
Added HAVE_FORK config symbol and stuff to support
uClinux. Thanks to Matthew Natalier for uClinux stuff.
release 1.10 Log warnings if resolv.conf or dhcp.leases are not
accessable for any reason, as suggested by Hinrich Eilts.
Fixed wrong address printing in error message about
no interface with address.
Updated docs and split installation instuctions into setup.html.
Fix bug in CNAME chasing code: One CNAME pointing
to many A records would lose A records after the
first. This bug was introduced in version 1.9.
Log startup failures at level Critical as well as
printing them to standard error.
Exit with return code 1 when given bad options.
Cleaned up code for no-cache operation.
Added -o option which forces dnsmasq to use to
upstream servers in the order they appear in /etc/resolv.conf.
Added upstream server use logging.
Log full cache dump on receipt of SIGUSR1 when query
logging is enabled (-q switch).
Added -S option to directly specify upstream servers and
added ability to direct queries for specific domains to
specfic servers. Suggested by Jens Vonderheide.
Upgraded random ID generation - patch from Rob Funk.
Fixed reading of domains in arguments with capital
letters or trailing periods.
Fixed potential SEGV when given bad options.
Read options from /etc/dnsmasq.conf if it exists.
Do sensible things with missing parameters, eg
"--resolv-file=" turns off reading /etc/resolv.conf.
release 1.11 Actually implement the -R flag promised in the 1.10 man page.
Improve and rationalise the return codes in answers to
queries. In the case that there are no available
upstream servers to forward a query to, return REFUSED.
This makes sendmail work better on modem connected
systems when the modem link is down (Thanks to Roger Plant).
Cache and return the NXDOMAIN status of failed queries:
this makes the `host` command work when traversing search
paths (Thanks to Peter Bailey). Set the "authoritative"
bit in replies containing names from /etc/hosts or DHCP.
Tolerate MS-DOS style line ending codes in /etc/hosts
and /etc/resolv.conf, for people who copy from winsock
installations.
Allow specification of more than one resolv.conf file. This is
intended for laptops which connect via DHCP or
PPP. Whichever resolv.conf was updated last is used.
Allow -S flags which specify a domain but no server
address. This gives local domains which are never forwarded.
Add -E flag to automatically add the domain suffix to
names in /etc/hosts -suggestion from Phil Harman.
Always return a zero time-to-live for names derived from
DHCP which stops anthing else caching these
names. Previously the TTL was derived from the lease
time but that is incorrect since a lease can be given
up early: dnsmasq would know this but anything with the
name cached with long TTL would not be updated.
Extended HAVE_IPV6 config flag to allow compliation on
old systems which don't have modern library routines
like inet_ntop(). Thanks to Phil Harman for the patch.
release 1.12 Allow more than one domain in server config lines and
make "local" a synonym for "server". This makes things
like "local=/localnet/thekelleys.org.uk/" legal. Allow
port to specified as part of server address.
Allow whole domains to have an IP address specified
in /etc/dnsmasq.conf. (/etc/hosts doesn't work domains).
address=/doubleclick.net/127.0.0.1 should catch all
those nasty banner ads. Inspired by a patch
from Daniel Gryniewicz
Log the source of each query when logging switched on.
Fix bug in script fragment for dhcpcd - thanks to Barry Stewart.
Fix bug which meant that strict-order and self-mx were
always enabled.
Builds with Linux libc5 now - for the Freesco project.
Fixed Makefile installation script (patch from Silvan
Minghetti) and added CC and CFLAGS variables.
Improve resource allocation to reduce vulnerability to
DOS attacks - the old version could have all queries
blocked by a continuous high-speed stream of
queries. Now some queries will succeed, and the excess
will be rejected with a server fail error. This change also
protects against server-loops; setting up a resolving
loop between two instances of dnsmasq is no longer
catastrophic. The servers will continue to run, looped
queries fail and a warning is logged. Thanks to C. Lee
Taylor for help with this.
release 1.13 Added support for building rpms suitable for modern Suse
systems. (patch from Andi )
Added options --group, --localmx, --local-ttl,
--no-negcache, --addn-host.
Moved all the various rpm-building bits into /rpm.
Fix builds with glibc 2.1 (thanks to Cristian
Ionescu-Idbohrn)
Preserve case in domain names, as per RFC1035.
Fixed ANY queries to domains with --address specification.
Fixed FreeBSD build. (thanks to Steven Honson)
Added -Q option which allows a specified port to be used
to talk to upstream servers. Useful for people who want
very paranoid firewalls which open individual UDP port.
(thanks to David Coe for the patch)
release 1.14 Fixed man page description of -b option which confused
/etc/hosts with /etc/resolv.conf. (thanks to Christopher
Weimann)
Fixed config.h to allow building under MACOS X and glibc
2.0.x. (thanks to Matthew Gregan and Serge Caron)
Added --except-interface option. (Suggested by Serge Caron)
Added SIGUSR2 facility to re-scan for new
interfaces. (Suggested by Serge Caron)
Fixed SEGV in option-reading code for invalid options.
(Thanks to Klaas Teschauer)
Fixed man page to clarify effect of SIGUSR1 on
/etc/resolv.conf.
(Thanks to Klaas Teschauer)
Check that recieved queries have only rfc1035-legal characters
in them. This check is mainly to avoid bad strings being
sent to syslog.
Fixed &&/& confusion in option.c and added DESTDIR
variable for "make install" (Thanks to Osvaldo
Marques for the patch.)
Fixed /etc/hosts parsing code to cope with MS-DOS
line-ends in the file. This was supposed to be done in
version 1.11, but something got missed. (Thanks to Doug
Copestake for helping to find this.)
Squash repeated name/address pairs read from hosts
files.
Tidied up resource handling in util.c (Thanks to
Cristian Ionescu-Idbohrn).
Added hashed searching of domain names. People are starting
to use dnsmasq with larger loads now, and bigger caches,
and large lists of ad-block addresses. This means doing
linear searches can start to use lots of CPU so I added hashed
searching and seriously optimised the cache code for
algorithmic efficiency. Also upped the limit on cache
size to 10000.
Fixed logging of the source of names from the additional
hosts file and from the "bogus private address" option.
Fixed spurious re-reading of empty lease files. (Thanks
to Lewis Baughman for spotting this.)
Fixed building under uclibc (patch from Cristian Ionescu-Idbohrn)
Do some socket tweaking to allow dnsmasq to co-exist
with BIND. Thanks to Stefan 'Sec' Zehl for the patch.
release 1.15 Added --bogus-nxdomain option.
Restrict checking of resolv.conf and DHCP leases files
to once per second. This is intended to improve
performance under heavy loads. Also make a system call
to get the current time once per query, rather than four
times.
Increased number of outstanding queries to 150 in
config.h
release 1.16 Allow "/" characters in domain names - this fixes
caching of RFC 2317 CNAME-PTR records.
Fixed brain-fart in -B option when GETOPT_LONG not
enabled - thanks to Steven Young and Jason Miller
for pointing this out.
Generalised bogus-nxdomain code: allow more than one
address to check, and deal with replies with multiple
answer records. (Based on contribution from Humberto
Massa.)
Updated the documentation to include information about
bogus-nxdomain and the Verisign tragedy.
Added libraries needed on Solaris to Makefile.
Added facility to set source address in queries to
upstream nameservers. This is useful with multihomed
hosts, especially when using VPNs. Thanks to Tom Fanning
for suggesting this feature.
Tweaked logging: log to facility LOCAL0 when in
debug/no-daemon mode and changed level of query logging
from INFO to DEBUG. Make log options controllable in
config.h
release 1.17 Fixed crash with DHCP hostnames > 40 characters.
Fixed name-comparision routines to not depend on Locale,
in theory this versions since 1.15 could lock up or give
wrong results when run with locale != 'C'.
Fix potential lockup in cache code. (thanks to Henning
Glawe for help chasing this down.)
Made lease-file reader bullet-proof.
Added -D option, suggested by Peter Fichtner.
release 1.18 Added round-robin DNS for names which have more than one
address. In this case all the addresses will be
returned, as before, but the order will change on each
query.
Remove stray tolower() and isalnum() calls missed in
last release to complete LOCALE independence.
Allow port numbers in source-address specifications.
For hostnames without a domain part which don't get
forwarded because -D is in effect, return NXDOMAIN not
an empty reply.
Add code to return the software version in repsonse to the
correct magic query in the same way as BIND. Use
"dig version.bind chaos txt" to make the query.
Added negative caching for PTR (address to name) records.
Ensure that names of the form typically used in PTR queries
(ie w.x.yz.in-addr.arpa and IPv6 equivalents) get
correct answers when queried as other types. It's
unlikely that anyone would do this, but the change makes
things pedantically correct.
Taught dnsmasq to understand "bitstring" names, as these
are used for PTR lookups of IPv6 addresses by some
resolvers and lookup tools. Dnsmasq now understands both
the ip6.int domain and the ip6.arpa domain and both
nibble and bitstring formats so it should work with any
client code. Standards for this stuff have flip-flopped
over the last few years, leaving many different clients
in their wake. See RFC2673 for details of bitstrings.
Allow '_' characters in domain names: Legal characters
are now [a-z][A-Z].-_ Check names read from hosts files
and leases files and reject illegal ones with a message
in syslog.
Make empty domain names in server and address options
have the special meaning "unqualified
names". (unqualified names are names without any dots in
them). It's now possible to do server=//1.2.3.4 and have
unqualified names sent to a special nameserver.
release 2.0rc1
Moved source code into src/ directory.
Fixes to cure compilation breakage when HAVE_IPV6 not
set, thanks to Claas Hilbrecht.
BIG CHANGE: added an integrated DHCP server and removed
the code to read ISC dhcp.leases. This wins in terms
of ease of setup and configuration flexibility and
total machine resources consumed.
Re-jiged the signal handling code to remove a race
condition and to be more portable.
release 2.0
Thanks to David Ashworth for feedback which informed many
of the fixes below.
Allow hosts to be specified by client ID in dhcp-hosts
options. These are now one of
dhcp-host=,....
dhcp-host=id:,.....
dhcp-host=id:,.....
Allow dhcp-host options to specify any IP address on the
DHCP-served network, not just the range available for
dynamic allocation.
Allow dhcp-host options for the same host with different
IP adresses where the correct one will be selected for
the network the host appears on.
Fix parsing of --dhcp-option to allow more than one
IP address and to allow text-type options.
Inhibit use of --dhcp-option to send hostname DHCP options.
Update the DNS with DHCP information after re-reading
/etc/hosts so that any DHCP derived names which have been
shadowed by now-deleted hosts entries become visible.
Fix typos in dnsmasq.conf.example
Fixes to Makefile(s) to help pkgsrc packaging - patch
from "pancake".
Add dhcp-boot option to support network boot.
Check for duplicate IP addresses in dhcp-hosts lines
and refuse to run if found. If allowed to remain these
can provoke an infinite loop in the DHCP protocol.
Attempted to rationalise the .spec files for rpm
building. There are now files for Redhat, Suse and
Mandrake. I hope they work OK.
Fixed hard-to-reproduce crash involving use of local
domains and IPv6 queries. Thanks to Roy Marples for
helping to track that one down.
release 2.1
Thanks to Matt Swift and Dag Wieers for many suggestions
which went into this release.
Tweak include files to allow compilation on FreeBSD 5
Fix unaligned access warnings on BSD/Alpha.
Allow empty DHCP options, like so: dhpc-option=44
Allow single-byte DHCP options like so: dhcp-option=20,1
Allow comments on the same line as options in
/etc/dnsmasq.conf
Don't complain when the same name and address is
allocated to a host using DHCP and /etc/hosts.
Added to the example configuration the dnsmasq equivalent
of the ISC dhcpd settings given in
http://www.samba.org/samba/ftp/docs/textdocs/DHCP-Server-Configuration.txt
Fixed long-existing strangeness in Linux IPv6 interface
discovery code. The flags field in /proc/net/if_inet6 is
_not_ the interface flags.
Fail gracefully when getting an ENODEV error when trying
to bind an IPv6 socket, rather than bailing out. Thanks
to Jan Ischebeck for feedback on that.
Allow the name->address mapping for static DHCP leases to
be set by /etc/hosts. It's now possible to have
dhcp-host=,wibble
or even
dhcp-host=wibble
and in /etc/hosts have
wibble 1.2.3.4
and for the correct thing to happen. Note that some sort
of dhcp-host line is still needed, it's not possible for
random host to claim an address in /etc/hosts without
some explicit configuration.
Make 0.0.0.0 in a dhcp-option to mean "the machine
running dnsmasq".
Fix lease time spec when specified in dhcp-range and not
in dhcp-host, previously this was always one hour.
Fix problem with setting domains as "local only". -
thanks to Chris Schank.
Added support for max message size DHCP option.
release 2.2
Fix total lack for DHCP functionality on
Linux systems with IPv6 enabled. - thanks to
Jonathon Hudson for spotting that.
Move default config file under FreeBSD - patch from
Steven Honson
release 2.3
Fix "install" makefile target. (reported by Rob Stevens)
Ensure that "local=/domain/" flag is obeyed for all
queries on a domain, not just A and AAAA. (Reported by
Peter Fichtner.)
Handle DHCPDECLINE messages and provide an error message
in DHCPNAK messages.
Add "domain" setting example to
dnsmasq.conf.example. Thanks to K P Kirchdorfer for
spotting that it was missing.
Subtle change to the DHCPREQUEST handling code to work
around a bug in the DHCP client in HP Jetdirect printers.
Thanks to Marko Stolle for finding this problem.
Return DHCP T1 and T2 times, with "fuzz" to desychronise lease
renewals, as specified in the RFC.
Ensure that the END option is always present in DHCP
packets , even if the packet is too small to fit all
the requested options.
Handle larger-than-default DHCP packets if required, up
to the ethernet MTU.
Fix a couple of places where the return code from
malloc() was not checked.
Cope with a machine taking a DHCP lease and then moving
network so that the lease address is no longer valid.
The DHCP server will now work via a BOOTP relay - remote
networks are configured with the dhcp-range option the
same as directly connected ones, but they need an
additional netmask parameter. Eg
--dhcp-range=192.168.4.10,192.168.4.50,255.255,255.0
will enable DHCP service via a BOOTP relay on the
192.168.4.0 network.
Add a limit on the number of available DHCP leases,
otherwise the daemon could be DOSed by a malicious
host. The default is 150, but it can be changed by the
dhcp-lease-max option.
Fixed compilation on OpenBSD (thanks to Frederic Brodbeck
for help with that.)
Reworked the DHCP network handling code for two good
effects: (1) The limit of one network only for DHCP on
FreeBSD is now gone, (2) The DHCP server copes with
dynamically created interfaces. The one-interface
limitation remains for OpenBSD, which is missing
extensions to the socket API which have been in Linux
since version 2.2 and FreeBSD since version 4.8.
Reworked the DNS network code to also cope with
dynamically created interfaces. dnsmasq will now listen
to the wildcard address and port 53 by default, so if no
--interface or --address options are given it will handle
dynamically created interfaces. The old behaviour can be
restored with --bind-interfaces for people running BIND
on one interface and dnsmasq on another. Note that
--interface and --address options still work, but the
filtering is done by dnsmasq, rather then the kernel.
This works on Linux, and FreeBSD>=5.0. On systems which
don't support the required API extensions, the old
behaviour is used, just as if --bind-interfaces had been set.
Allow IPv6 support to be disabled at compile time. To do
that, add -DNO_IPV6 to the CFLAGS. Thanks to Oleg
I. Vdovikin for the suggestion to do that.
Add ability to set DHCP options per network. This is done
by giving a network an identifier like this:
dhcp-range=red-net,192.168.0.10,192.168.0.50
and then labeling options intended for that network only
like this:
dhcp-option=red-net,6,1.1.1.1
Thanks to Oleg Vdovikin for arguing that one through.
Made errors in the configuration file non-fatal: dnsmasq
will now complain bitterly, but continue.
Added --read-ethers option, to allow dnsmasq to pull
static DHCP information from that file.
Thanks to Andi Cambeis for that suggestion.
Added HAVE_BROKEN_RTC compilation option to support
embedded systems without a stable RTC. Oleg Vdovikin
helped work out how to make that work.
release 2.4
Fixed inability to start when the lease file doesn't
already exist. Thanks to Dag Wieers for reporting that.
Fixed problem were dhcp-host configuration options did
not play well with entries in /etc/ethers for the same
host. Thanks again to Dag Wieers.
Tweaked DHCP code to favour moving to a newly-configured
static IP address rather than an old lease when doing
DHCP allocation.
Added --alias configuration option. This provides IPv4
rewrite facilities like Cisco "DNS doctoring". Suggested
by Chad Skeeters.
Fixed bug in /etc/ethers parsing code triggered by tab
characters. Qudos to Dag Wieers for hepling to nail that
one.
Added "bind-interfaces" option correctly.
release 2.5
Made "where are we allocating addresses?" code in DHCP
server cope with requests via a relay which is on a
directly connected network for which there is not a
configured netmask. This strange state of affairs occurs
with win4lin. Thanks to Alex Melt and Jim Horner for bug
reports and testing with this.
Fixed trivial-but-irritating missing #include which broke
compilation on *BSD.
Force --bind-interfaces if IP-aliased interface
specifications are used, since the sockets API provides
no other sane way to determine which alias of an
interface a packet was sent to. Thanks to Javier Kohen
for the bug report.
release 2.6
Support Token Ring DHCP. Thanks to Dag Wieers for help
testing. Note that Token ring support only works on Linux
currently.
Fix compilation on MacOS X. Thanks to Bernhard Ehlers for
the patch.
Added new "ignore" keyword for
dhcp-host. "dhcp-host=11:22:33:44:55:66,ignore" will
cause the DHCP server to ignore any host with the given
MAC address, leaving it to other servers on the
network. This also works with client-id and hostnames.
Suggestion by Alex Melt.
Fixed parsing of hex client IDs. Problem spotted by Peter
Fichtner.
Allow conf-file options in configuration file, to
provide an include function.
Re-read /etc/ethers on receipt of SIGHUP.
Added back the ability to read ISC dhcpd lease files, by
popular demand. Note that this is deprecated and for
backwards compatibility only. You can get back the 4K of
memory that the code occupies by undefining
"HAVE_ISC_READER" in src/config.h
Added ability to disable "pool" DHCP address allocation
whilst leaving static leases working. The syntax is
"dhcp-range=192.168.0.0,static"
Thanks to Grzegorz Nosek for the suggestion.
Generalized dnsmasq-rh.spec file to work on Mandrake too,
and removed dnsmasq-mdk.spec. Thanks to Doug Keller.
Allow DHCP options which are tied to specific static
leases in the same way as to specific networks.
Generalised the dhcp-option parser a bit to allow hex
strings as parameters. This is now legal:
dhcp-option=128,e4:45:74:68:00:00
Inspired by a patch from Joel Nordell.
Changed the semantics of argument-less dhcp-options for
the default-setting ones, ie 1, 3, 6 and 28. Now, doing
eg, dhcp-option=3 stops dnsmasq from sending a default
router option at all. Thanks to Scott Emmons for pointing
out that this is useful.
Fixed dnsmasq.conf parsing bug which interpreted port
numbers in server= lines as a comment. To start a
comment, a '#' character must now be a the start of a
line or preceded by whitespace. Thanks to Christian
Haggstrom for the bug report.
release 2.7
Allow the dhcp-host specification of id:* which makes
dnsmasq ignore any client-id. This is useful to ensure
that a dual-boot machine sees the same lease when one OS
gives a client-id and the other doesn't. It's also useful
when PXE boot DHCP does not use client IDs but the OS it boots
does. Thanks to Grzegorz Nosek for suggesting this enhancement.
No longer assume that ciaddr is zero in received DHCPDISCOVER
messages, just for security against broken clients.
Set default of siaddr field to the address of the machine running
dnsmasq when not explicitly set using dhcp-boot
option. This is the ISC dhcpd behaviour.
Send T1 and T2 options in DHCPOFFER packets. This is required
by the DHCP client in some JetDirect printers. Thanks
to Paul Mattal for work on this.
Fixed bug with DHCP on OpenBSD reported by Dominique Jacquel.
The code which added loopback interfaces to the list
was confusing the DHCP code, which expected one interface only.
Solved by adding loopback interfaces to address list instead.
Add dhcp-vendorclass option to allow options to be sent only
to certain classes of clients.
Tweaked option search code so that if a netid-qualified
option is used, any unqualified option is ignored.
Changed the method of picking new dynamic IP
addresses. This used to use the next consecutive
address as long it was free, now it uses a hash
from the client hardware address. This reduces the amount
of address movement for clients which let their lease
expire and allows consecutive DHCPOFFERS to the same host
to (almost always) be for the same address, without
storing state before a lease is granted.
Tweaked option handling code to return all possible
options rather than none when DHCP "requested options"
field is missing. This fixes interoperability with
ancient IBM LANMAN DHCP clients. Thanks to Jim Louvau for
help with this.
release 2.8
Pad DHCP packets to a minimum size of 300 bytes. This
fixes interoperability problems with the Linux in-kernel
DHCP/BOOTP client. Thanks to Richard Musil for
diagnosing this and supplying a patch.
Fixed option-parsing bug and potential memory leak. Patch
from Richard Musil.
Improved vendor class configuration and added user class
configuration. Specifically: (1) options are matched on
the netids from dhcp-range, dhcp-host, vendor class and
user class(es). Multiple net-ids are allowed and options
are searched on them all. (2) matches agains vendor class
and user class are now on a substring, if the given
string is a substring of the vendor/user class, then a
match occurs. Thanks again to Richard Musil for prompting
this.
Make "#" match any domain on --address and --server
flags. --address=/#/1.2.3.4 will return 1.2.3.4 for _any_
domain not otherwise matched. Of course
--server=/#/1.2.3.4 is exactly equivalent to
--server=1.2.3.4. Special request from Josh Howlett.
Fixed a nasty bug which would cause dnsmasq to lose track
of leases for hosts which had a --dhcp-host flag without
a name specification. The mechanism for this was that
the hostname could get erroneously set as a zero-length
string and then written to the leases file as a
mal-formed line. Restarting dnsmasq would then lose the lease.
Alex Hermann's work helped chase down this problem.
Add checks against DHCP clients which return zero-length
hostnames. This avoids the potential lease-loss problems
reffered to above. Also, if a client sends a hostname when
it creates a lease but subsequently sends no or a
zero-length hostname whilst renewing, continue to use the
existing hostname, don't wipe it out.
Tweaked option parsing to flag some parameter errors.
release 2.9
Fixed interface filter code for two effects: 1) Fixed bug
where queries sent via loopback interface
but to the address of another interface were ignored
unless the loopback interface was explicitly configured.
2) on OpenBSD failure to configure one interface now
causes a fatal error on startup rather than an huge
stream of log messages. Thanks to Erik Jan Tromp for
finding that bug.
Changed server selection strategy to improve performance
when there are many available servers and some are
broken. The new algorithm is to pick as before for the
first try, but if a query is retried, to send to all
available servers in parallel. The first one to reply
then becomes prefered for the next query. This should
improve reliability without generating significant extra
upstream load.
Fixed breakage of special servers/addresses for
unqualified domains introduced in version 2.8
Allow fallback to "bind-interfaces" at runtime: Some
verions of *BSD seem to have enough stuff in the header
files to build but no kernel support. Also now log if
"bind-interfaces" is forced on.
Log replies from upstream servers which refuse to do
recursion - dnsmasq is not a recursive nameserver and
relies on upstream servers to do the recursion, this
flags a configuration error.
Disable client-id matching for hosts whose MAC address is
read from /etc/ethers. Patch from Oleg I. Vdovikin.
Extended --mx-host flag to allow arbitrary targets for MX
records, suggested by Moritz Bunkus.
Fixed build under NetBSD 2.0 - thanks to Felix Deichmann
for the patch.
Deal correctly with repeated addresses in /etc/hosts. The
first name found is now returned for reverse lookups,
rather than all of them.
Add back fatal errors when nonexistant
interfaces or interface addresses are given but only in
"bind-interfaces" mode. Principle of least surprise applies.
Allow # as the argument to --domain, meaning "read the
domain from the first search directive in
/etc.resolv.conf". Feature suggested by Evan Jones.
release 2.10
Allow --query-port to be set to a low port by creating and
binding the socket before dropping root. (Suggestion from
Jamie Lokier)
Support TCP queries. It turned out to be possible to do
this with a couple of hundred lines of code, once I knew
how. The executable size went up by a few K on i386.
There are a few limitations: data obtained via TCP is not
cached, and dynamically-created interfaces may break under
certain circumstances. Source-address or query-port
specifications are ignored for TCP.
NAK attempts to renew a DHCP lease where the DHCP range
has changed and the lease is no longer in the allowed
range. Jamie Lokier pointed out this bug.
NAK attempts to renew a pool DHCP lease when a statically
allocated address has become available, forcing a host to
move to its allocated address. Lots of people have
suggested this change and been rebuffed (they know who
they are) the straws that broke the camel's back were Tim
Cutts and Jamie Lokier.
Remove any nameserver records from answers which are
modified by --alias flags. If the answer is modified, it
cannot any longer be authoritative.
Change behaviour of "bogus-priv" option to return NXDOMAIN
rather than a PTR record with the dotted-quad address as
name. The new behaviour doesn't provoke tcpwrappers like
the old behavior did.
Added a patch for the Suse rpm. That changes the default
group to one suitable for Suse and disables inclusion of
the ISC lease-file reader code. Thanks to Andy Cambeis for
his ongoing work on Suse packaging.
Support forwarding of EDNS.0 The maximum UDP packet size
defaults to 1280, but may be changed with the
--edns-packet-max option. Detect queries with the do bit
set and always forward them, since DNSSEC records are
not cached. This behaviour is required to make
DNSSECbis work properly though dnsmasq. Thanks to Simon
Josefsson for help with this.
Move default config file location under OpenBSD from
/usr/local/etc/dnsmasq.conf to /etc/dnsmasq.conf. Bug
report from Jonathan Weiss.
Use a lease with matching MAC address for a host which
doesn't present a client-id, even if there was a client ID
at some point in the past. This reduces surprises when
changing DHCP clients, adding id:* to a host, and from the
semantics change of /etc/ethers in 2.9. Thanks to Bernard
Sammer for finding that.
Added a "contrib" directory and in it the dnslist utility,
from Thomas Tuttle.
Fixed "fail to start up" problems under Linux with IPv6
enabled. It's not clear that these were an issue in
released versions, but they manifested themselves when TCP
support was added. Thanks to Michael Hamilton for
assistance with this.
version 2.11
Fixed DHCP problem which could result in two leases in the
database with the same address. This looked much more
alarming then it was, since it could only happen when a
machine changes MAC address but kept the same name. The
old lease would persist until it timed out but things
would still work OK.
Check that IP addresses in all dhcp-host directives are
unique and die horribly if they are not, since otherwise
endless protocol loops can occur.
Use IPV6_RECVPKTINFO as socket option rather than
IPV6_PKTINFO where available. This keeps late-model FreeBSD
happy.
Set source interface when replying to IPv6 UDP
queries. This is needed to cope with link-local addresses.
version 2.12
Added extra checks to ensure that DHCP created DNS entries
cannot generate multiple DNS address->name entries. Thanks to
Stefan Monnier for finding the exact set of configuration
options which could create this.
Don't set the the filterwin2k option in the example config
file and add warnings that is breaks Kerberos. Thanks to
Simon Josefsson and Timothy Folks for pointing that out.
Log types of incoming queries as well as source and domain.
Log NODATA replies generated as a result of the
filterwin2k option.
version 2.13
Fixed crash with un-named DHCP hosts introduced in 2.12.
Thanks to Nicolo Wojewoda and Gregory Gathy for bug reports.
version 2.14
Fix DHCP network detection for hosts which talk via a
relay. This makes lease renewal for such hosts work
correctly.
Support RFC3011 subnet selectors in the DHCP server.
Fix DHCP code to generate RFC-compliant responses
to hosts in the INIT-REBOOT state.
In the DHCP server, set the receive buffer size on
the transmit-only packet socket to zero, to avoid
waste of kernel buffers.
Fix DHCP address allocation code to use the whole of
the DHCP range, including the start and end addresses.
Attempt an ICMP "ping" on new addresses before allocating
them to leases, to avoid allocating addresses which are in use.
Handle rfc951 BOOTP as well as DHCP for hosts which have
MAC address to IP address mapping defined.
Fix compilation under MacOS X. Thanks to Chris Tomlinson.
Fix compilation under NetBSD. Thanks to Felix Deichmann.
Added "keep-in-foreground" option. Thanks to Sean
MacLennan for the patch.
version 2.15
Fixed NXDOMAIN/NODATA confusion for locally known
names. We now return a NODATA reponse for names which are
locally known. Now a query for (eg AAAA or MX) for a name
with an IPv4 address in /etc/hosts which fails upstream
will generate a NODATA response. Note that the query
is still tried upstream, but a NXDOMAIN reply gets
converted to NODATA. Thanks to Eric de Thouars, Eric
Spakman and Mike Mestnik for bug reports/testing.
Allow multiple dhcp-ranges within the same network. The
original intention was that there would be a dhcp-range
option for each network served, but there's no real reason
not to allow discontinuous ranges within a network so this
release adds support for that.
Check for dhcp-ranges which are inconsistent with their
netmask, and generate errors or warnings.
Improve error messages when there are problems with
configuration.
version 2.16
Fixed typo in OpenBSD-only code which stopped compilation
under that OS. Chris Weinhaupl gets credit for reporting
this.
Added dhcp-authoritative option which restores non-RFC
compliant but desirable behaviour of pre-2.14 versions and
avoids long timeouts while DHCP clients try to renew leases
which are unknown to dnsmasq. Thanks to John Mastwijk for
help with this.
Added support to the DHCP option code to allow RFC-3397
domain search DHCP option (119) to be sent.
Set NONBLOCK on all listening sockets to workaround non-POSIX
compliance in Linux 2.4 and 2.6. This fixes rare hangs which
occured when corrupted packets were received. Thanks to
Joris van Rantwijk for chasing that down.
Updated config.h for NetBSD. Thanks to Martin Lambers.
Do a better job of distinguishing between retransmissions
and new queries when forwarding. This fixes a bug
triggered by the polipo web cache which sends A and AAAA
queries both with the same transaction-ID. Thanks to
Joachim Berdal Haga and Juliusz Chroboczek for help with this.
Rewrote cache code to store CNAMES, rather then chasing
them before storage. This eliminates bad situations when
clients get inconsistent views depending on if data comes
from the cache.
Allow for more than one --addn-hosts flag.
Clarify logged message when a DHCP lease clashes with an
/etc/hosts entry. Thanks to Mat Swift for the suggestion.
Added dynamic-dnsmasq from Peter Willis to the contrib
section.
version 2.17
Correctly deduce the size of numeric dhcp-options, rather
than making wild guesses. Also cope with negative values.
Fixed use of C library reserved symbol "index" which broke
under certain combinations of library and compiler.
Make bind-interfaces work for IPv6 interfaces too.
Warn if an interface is given for listening which doesn't
currently exist when not in bind-interfaces mode. (This is
already a fatal error when bind-interfaces is set.)
Allow the --interface and --except-interface options to
take a comma-separated list of interfaces.
Tweak --dhcp-userclass matching code to work with the
ISC dhclient which violates RFC3004 unless its
configuration is very warped. Thanks to Cedric Duval for
the bug report.
Allow more than one network-id tag in a dhcp-option. All
the tags must match to enable the option.
Added dhcp-ignore option to disable classes of hosts based
on network-id tags. Also allow BOOTP options to be
controlled by network tags.
Fill in sname, file and siaddr fields in replies to
DHCPINFORM messages.
Don't send NAK replies to DHCPREQUEST packets for disabled
clients. Credit to Cedric Duval for spotting this.
Fix rare crash associated with long DNS names and CNAME
records. Thanks to Holger Hoffstatte and especially Steve
Grecni for help chasing that one down.
version 2.18
Reworked the Linux interface discovery code (again) to
cope with interfaces which have only IPv6 addresses and
interfaces with more than one IPv6 address. Thanks to
Martin Pels for help with that.
Fix problems which occured when more than one dhcp-range
was specified in the same subnet: sometimes parameters
(lease time, network-id tag) from the wrong one would be
used. Thanks to Rory Campbell-Lange for the bug report.
Reset cache statistics when clearing the cache.
Enable long command line options on FreeBSD when the
C library supports them.
version 2.19
Tweaked the Linux-only interface discovery code to cope
with interface-indexes larger than 8 bits in
/proc/net/if_inet6. This only affects Linux, obviously.
Thanks to Richard Atterer for the bug report.
Check for under-length option fields in DHCP packets, a
zero length client-id, in particluar, could seriously
confuse dnsmasq 'till now. Thanks to Will Murname for help
with that.
If a DHCP-allocated address has an associated name in
/etc/hosts, and the client does not provide a hostname
parameter and there is no hostname in a matching dhcp-host
option, send the /etc/hosts name as the hostname in
the DHCP lease. Thanks to Will Murname for the suggestion.
version 2.20
Allow more than one instance of dnsmasq to run on a
machine, each providing DHCP service on a different
interface, provided that --bind-interfaces is set. This
configuration used to work, but regressed in version 2.14
Fix compilation on Mac OS X. Thanks to Kevin Bullock.
Protect against overlong names and overlong
labels in configuration and from DHCP.
Fix interesting corner case in CNAME handling. This occurs
when a CNAME has a target which "shadowed" by a name in
/etc/hosts or from DHCP. Resolving the CNAME would sneak
the upstream value of the CNAME's target into the cache,
alongside the local value. Now that doesn't happen, though
resolving the CNAME still gives the unshadowed value. This
is arguably wrong but rather difficult to fix. The main
thing is to avoid getting strange results for the target
due to the cache pollution when resolving the
CNAME. Thanks to Pierre Habouzit for exploring the corner
and submitting a very clear bug report.
Fix subtle bug in the DNS packet parsing code. It's almost
impossible to describe this succinctly, but the one known
manifestation is the inability to cache the A record for
www.apple.com. Thanks to Bob Alexander for spotting that.
Support SRV records. Thanks to Robert Kean for the patches
for this.
Fixed sign confusion in the vendor-id matching code which
could cause crashes sometimes. (Credit to Mark Wiater for
help finding this.)
Added the ability to match the netid tag in a
dhcp-range. Combined with the ability to have multiple
ranges in a single subnet, this provides a means to
segregate hosts on different address ranges based on
vendorclass or userclass. Thanks to Mark Wiater for
prompting this enhancement.
Added preference values for MX records.
Added the --localise-queries option.
version 2.21
Improve handling of SERVFAIL and REFUSED errors. Receiving
these now initiates search for a new good server, and a
server which returns them is not a candidate as a good
server. Thanks to Istvan Varadi for pointing out the
problem.
Tweak the time code in BROKEN_RTC mode.
Sanity check lease times in dhcp-range and dhcp-host
configurations and force them to be at least two minutes
(120s) leases shorter than a minute confuse some clients,
notably Apple MacOS X. Rory Campbell-Lange found this
problem.
Only warn once about an upstream server which is refusing to do
recursive queries.
Fix DHCP address allocation problem when netid tags are in
use. Thanks to Will Murnane for the bug report and
subsequent testing.
Add an additional data section to the reply for MX and SRV
queries. Add support for DNS TXT records. Thanks to Robert
Kean and John Hampton for prompts and testing of these.
Apply address rewriting to records in the additional data section
of DNS packets. This makes things like MX records work
with the alias function. Thanks to Chad Skeeters for
pointing out the need for this.
Added support for quoted strings in config file.
Detect and defeat cache-poisoning attacks which attempt to
send (malicious) answers to questions we didn't
send. These are ignored now even if the attacker manages
to guess a random query-id.
Provide DHCP support for interfaces with multiple IP
addresses or aliases. This in only enabled under Linux.
See the FAQ entry for details.
Revisit the MAC-address and client-id matching code to
provide saner behaviour with PXE boots, where some
requests have a client-id and some don't.
Fixed off-by-one buffer overflow in lease file reading
code. Thanks to Rob Holland for the bug report.
Added wildcard matching for MAC addresses in dhcp-host
options. A sensible suggestion by Nathaniel McCallum.
version 2.22
Fixed build problems on (many) systems with older libc
headers where is required before
. Enabled HAVE_RTNETLINK under uclibc now
that this fix is in place.
Added support for encapsulated vendor-class-specific DHCP
options. Thanks to Eric Shattow for help with this.
Fix regression in 2.21 which broke commas in filenames and
corrupted argv. Thanks to Eric Scott for the bugreport.
Fixed stupid thinko which caused dnsmasq to wedge during
startup with certain MX-record options. Another 2.21 regression.
Fixed broken-ness when reading /etc/ethers. 2.21 broke
this too.
Fixed wedge with certain DHCP options. Yet another 2.21
regression. Rob Holland and Roy Marples chased this one
down.
version 2.23
Added a check to ensure that there cannot be more than one
dhcp-host option for any one IP address, even if the
addresses are assigned indirectly via a hostname and
/etc/hosts.
Include a "server identifier" in DHCPNAK replies, as
required by RFC2131.
Added method support for DBus
(http://www.freedesktop.org/Software/dbus)
This is a superior way to re-configure dnsmasq on-the-fly
with different upstream nameservers, as the host moves
between networks. DBus support must be enabled in
src/config.h and should be considered experimental at this
point. See DBus-interface for the specification of the
DBus method calls supported.
Added information to the FAQ about setting the DNS domain
in windows XP and Mac OS X, thanks to Rick Hull.
Added sanity check to resolv.conf polling code to cope
with backwards-moving clocks. Thanks to Leonardo Canducci
for help with this.
Handle so-called "A-for-A" queries, which are queries for
the address associated with a name which is already a
dotted-quad address. These should be handled by the
resolver code, but sometimes aren't and there's no point
in forwarding them.
Added "no-dhcp-interface" option to disable DHCP service
on an interface, whilst still providing DNS.
Fix format-string problem - config file names get passed
to fprintf as a format string, so % characters could cause
crashes. Thanks to Rob Holland for sleuthing that one.
Fixed multiple compiler warnings from gcc 4. Thanks to
Tim Cutts for the report.
Send the hostname option on DHCP offer messages as well as
DHCP ack messages. This is required by the Rio Digital
Audio Receiver. Thanks to Ron Frederick for the patch.
Add 'd' (for day) as a possible time multiplier in lease
time specifications. Thanks to Michael Deegan.
Make quoting suppress recognition of IP addresses, so
dhcp-option=66,1.2.3.4 now means something different to
dhcp-option=66,"1.2.3.4", which sets the option to a
string value. Thanks to Brian Macauley for the bug report.
Fixed the option parsing code to avoid segfaults from some
invalid configurations. Thanks to Wookey for spotting that one.
Provide information about which compile-time options were
selected, both in the log at startup and as part of the output
from dnsmasq --version. Thanks to Dirk Schenkewitz for
the suggestion.
Fix pathalogical behaviour when a broken client keeps sending
DHCPDISCOVER messages repeatedly and fast. Because dealing with
each of these takes a few seconds, (because of the ping) then a
queue of DHCP packets could build up. Now, the results of a ping
test are assumed to be valid for 30 seconds, so repeated waits are
not required. Thanks to Luca Landi for finding this.
Allow DHCPINFORM requests without hardware address
information. These are generated by some browsers, looking
for proxy information. Thanks to Stanley Jaddoe for the
bug report on that.
Add support of the "client FQDN" DHCP option. If present,
this is used to allow the client to tell dnsmasq its name,
in preference to (mis)using the hostname option. See
http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/\
draft-ietf-dhc-fqdn-option-10.txt
for details of the draft spec.
Added startup scripts for MacOS X Tiger/Panther to the
contrib collection. Thanks to Tim Cutts.
Tweak DHCP network selection so that clients which turn up
on our network in REBINDING state and with a lease for a
foreign network will get a NAK response. Thanks to Dan
Shechter for work on this and an initial patch and thanks
to Gyorgy Farkas for further testing.
Fix DNS query forwarding for empty queries and forward
queries even when the recursion-desired bit is clear. This
allows "dig +trace" to work. Problem report from Uwe
Gansert.
Added "const" declarations where appropriate, thanks to
Andreas Mohr for the patch.
Added --bootp-dynamic option and associated
functionality. Thanks to Josef Wolf for the suggestion.
version 2.24
Updated contrib/openvpn/dnsmasq.patch from Joseph Tate.
Tweaked DHCP NAK code, a DHCP NAK is now unicast as a
fallback in cases where a broadcast is futile: namely in
response to a unicast REQUEST from a non-local network
which was not sent via a relay.
Slightly changed the semantics of domain matching in
--server and --address configs. --server=/domain.com/ still
matches domain.com and sub.domain.com but does not
now match newdomain.com The semantics of
--server=/.domain.com/ are unchanged.
Thanks to Chris Blaise for the patch.
Added backwards-compatible internationalisation support.
The existing make targets, (all, dnsmasq, install) work as
before. New ones (all-i18n, and install-i18n) add gettext.
The translations live in po/ There are not too many
strings, so if anybody can provide translations (and for
the manpage....) please send them in.
Tweak behaviour on receipt of REFUSED or SERVFAIL rcodes,
now the query gets retried on all servers before returning
the error to the source of the query. Thanks to Javier
Kohen for the report.
Added Polish translation - thanks to Tomasz Sochanski.
Changed default manpage install location from /usr/man
to /usr/share/man
Added Spanish translation - thanks to Christopher Chatham.
Log a warning when a DHCP packet is truncated due to lack
of space. (Thanks to Michael Welle for the prompt to do
this.)
Added French translation - thanks to Lionel Tricon.
Added Indonesian translation - thanks to Salman AS.
Tweaked the netlink code to cope with interface broadcast
address not set, or set to 0.0.0.0.
Fixed problem assigning fixed addresses to hosts when more
than one dhcp-range is available. Thanks to Sorin Panca
for help chasing this down.
Added more explict error mesages to the hosts file and
ethers file reading code. Markus Kaiserswerth suffered to
make this happen.
Ensure that a hostname supplied by a DHCP client can never
override one configured on the server. Previously, any
host claiming a name would be given it, even if that
over-rode a dhcp-host declaration, leading to potentially
confusing situations.
Added Slackware package-build stuff into contrib/ The i18n
effort broke the current scripts, and working ones were
needed for testing, so they ended up here rather than make
Pat re-invent the wheel.
Added Romanian translation, thanks to Sorin Panca for
that.
version 2.25
Fixed RedHat spec file for FC4 - thanks to Werner Hoelzl
and Andrew Bird.
Fixed Suse spec file - thanks to Steven Springl.
Fixed DHCP bug when two distict subnets are on the same
physical interface. Thanks to Pawel Zawora for finding
this and suggesting the fix.
Added logging to make it explicit when dnsmasq falls back
from using RT-netlink sockets to the old ioctl API for
getting information about interfaces. Doing this
completely silently made remote debugging hard.
Merged uclibc build fixes from the OpenWRT package into
src/config.h
Added Norwegian translation - thanks to Jan Erik Askildt.
version 2.26
Fixed SuSe rpm patch problem - thanks to Steven Springl.
Fixed crash when attempting to send a DHCP NAK to a host
which believes it has a lease on an unknown
network. Thanks to Lutz Pressler for the bug report and
patch.
version 2.27
Tweaked DHCP behaviour when a client attempts to renew a lease
which dnsmasq doesn't know about. Previously that would always
result in a DHCPNAK. Now, in dhcp-authoritative mode, the
lease will be created, if it's legal. This makes dnsmasq work
better if the lease database is lost, for example on an OpenWRT
system which reboots. Thanks to Stephen Rose for work on
this.
Added the ability to support RFC-3442 style destination
descriptors in dhcp-options. This makes classless static
routes easy to do, eg dhcp-option=121,192.168.1.0/24,1.2.3.4
Added error-checking to the code which writes the lease
file. If this fails for any reason, an error is logged,
and a retry occurs after one minute. This should improve
things eg when a filesystem is full. Thanks to Jens Holze
for the bug report.
Fixed breakage of the "/#/ matches any domain" facility
which happened in 2.24. Thanks to Peter Surda for the bug
report.
Use "size_t" and "ssize_t" types where appropriate in the
code.
Fix buggy CNAME handling in mixed IPv4 and IPv6
queries. Thanks to Andreas Pelme for help finding that.
Added some code to attempt to re-transmit DNS queries when
a network interface comes up. 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 failing. This function is only active
when netlink support is compiled in, and therefore only
under Linux. Thanks to Jean Wolter for help with this.
Tweaked the DHCP tag-matching code to work correctly with
NOT-tag conditions. Thanks to Lutz Pressler for finding
the bug.
Generalised netid-tag matching in dhcp-range statements to
allow more than one tag.
Added --dhcp-mac to do MAC address matching in the same
way as vendorclass and userclass matching. A good
suggestion from Lutz Pressler.
Add workaround for buggy early Microsoft DHCP clients
which need zero-termination in string options.
Thanks to Fabiano Pires for help with this.
Generalised the DHCP code to cope with any hardware
address type, at least on Linux. *BSD is still limited to
ethernet only.
version 2.28
Eliminated all raw network access when running on
Linux. All DHCP network activity now goes through the IP
stack. Packet sockets are no longer required. Apart from
being a neat hack, this should also allow DHCP over IPsec
to work better. On *BSD and OS X, the old method of raw net
access through BPF is retained.
Simplified build options. Networking is now slimmed down
to a choice of "linux" or "other". Netlink is always used
under Linux. Since netlink has been available since 2.2
and non-optional in an IPv4-configured kernel since 2.4,
and the dnsmasq netlink code is now well tested, this
should work out fine.
Removed decayed build support for libc5 and Solaris.
Removed pselect code: use a pipe for race-free signal
handling instead, as this works everywhere.
No longer enable the ISC leasefile reading code in the
distributed sources. I doubt there are many people left
using this 1.x compatibility code. Those that are will
have to explicitly enable it in src/config.h.
Don't send the "DHCP maximum message size" option, even if
requested. RFC2131 says this is a "MUST NOT".
Support larger-than-minimum DHCP message. Dnsmasq is now
happy to get larger than 576-byte DHCP messages, and will
return large messages, if permitted by the "maximum
message size" option of the message to which it is
replying. There's now an arbitrary sanity limit of 16384
bytes.
Added --no-ping option. This fixes an RFC2131 "SHOULD".
Building on the 2.27 MAC-address changes, allow clients to
provide no MAC address at all, relying on the client-id as
a unique identifier. This should make things like DHCP for
USB come easier.
Fixed regression in netlink code under 2.2.x kernels which
occurred in 2.27. Erik Jan Tromp is the vintage kernel fan
who found this. P.S. It looks like this "netlink bind:
permission denied" problem occured in kernels at least as
late a 2.4.18. Good information from Alain Richoux.
Added a warning when it's impossible to give a host its
configured address because the address is leased
elsewhere. A sensible suggestion from Mircea Bardac.
Added minimal support for RFC 3046 DHCP relay agent-id
options. The DHCP server now echoes these back to the
relay, as required by the RFC. Also, RFC 3527 link selection
sub-options are honoured.
Set the process "dumpable" flag when running in debug
mode: this makes getting core dumps from root processes
much easier.
Fixed one-byte buffer overflow which seems to only cause
problems when dnsmasq is linked with uclibc. Thanks to
Eric House and Eric Spakman for help in chasing this down.
Tolerate configuration screwups which lead to the DHCP
server attemping to allocate its own address to a
client; eg setting the whole subnet range as a DHCP
range. Addresses in use by the server are now excluded
from use by clients.
Did some thinking about HAVE_BROKEN_RTC mode, and made it
much simpler and better. The key is to just keep lease
lengths in the lease file. Since these normally never
change, even as the lease is renewed, the lease file never
needs to change except when machines arrive on the network
or leave. This eliminates the code for timed writes, and
reduces the amount of wear on a flash filesystem to the
absolute minimum. Also re-did the basic time function in
this mode to use the portable times(), rather than parsing
/proc/uptime.
Believe the source port number when replying to unicast
DHCP requests and DHCP requests via a relay, instead of always
using the standard ports. This will allow relays on
non-standard ports and DHCPINFORM from unprivileged ports
to work. The source port sent by unconfigured clients is still
ignored, since this may be unreliable. This means that a DHCP
client must use the standard port to do full configuration.
version 2.29
Fixed compilation on OpenBSD (thanks to Tom Hensel for the
report).
Fixed false "no interface" errors when --bind-interfaces is
set along with --interface=lo or --listen-address. Thanks
to Paul Wise for the report.
Updated patch for SuSE rpm. Thanks to Steven Springl.
It turns out that there are some Linux kernel
configurations which make using the capability system
impossible. If this situation occurs then continue, running
as root, and log a warning. Thanks to Scott Wehrenberg
for help tracking this down.
version 2.30
Fixed crash when a DHCP client requested a broadcast
reply. This problem was introduced in version 2.28.
Thanks to Sandra Dekkers for the bug report.
version 2.31
Added --dhcp-script option. There have been calls for this
for a long time from many good people. Fabio Muzzi gets
the prize for finally convincing me.
Added example dbus config file and moved dbus stuff into
its own directory.
Removed horribly outdated Redhat RPM build files. These
are obsolete now that dnsmasq in in Fedora extras. Thanks
to Patrick "Jima" Laughton, the Fedora package
maintainer.
Added workaround for Linux kernel bug. This manifests
itself as failure of DHCP on kernels with "support for
classical IP over ATM" configured. That includes most
Debian kernel packages. Many thanks to A. Costa and
Benjamin Kudria for their huge efforts in chasing this
down.
Force-kill child processes when dnsmasq is sent a sigterm,
otherwise an unclosed TCP connection could keep dnsmasq
hanging round for a few minutes.
Tweaked config.h logic for uclibc build. It will now pick
up MMU and IPV6 status correctly on every system I tested.
version 2.32
Attempt a better job of replacing previous configuration
when re-reading /etc/hosts and /etc/ethers. SIGHUP is
still not identical to a restart under all circumstances,
but it is for the common case of name->MAC address in
/etc/ethers and name->IP address in /etc/hosts.
Fall back to broadcast for DHCP to an unconfigured client
when the MAC address size is greater than 14 bytes.
Fix problem in 2.28-onwards releases which breaks DNS on
Mac OS X. Thanks to Doug Fields for the bug report and
testing.
Added fix to allow compilation on c89-only compilers.
Thanks to John Mastwijk for the patch.
Tweak resolv file polling code to work better if there is
a race between updating the mtime and file contents. This
is not normally a problem, but it can be on systems which
replace nameservers whilst active. The code now continues
to read resolv.conf until it gets at least one usable
server. Thanks to Holger Mauermann for help with this.
If a client DECLINEs an address which is allocated to it
via dhcp-host or /etc/hosts, lock that address out of use
for ten minutes, instead of forever, and log when it's not
being used because of the lock-out. This should provide
less surprising behaviour when a configured address can't be
used. Thanks to Peter Surda and Heinz Deinhart for input
on this.
Fixed *BSD DHCP breakage with only some
arches/compilers, depending on structure padding rules.
Thanks to Jeb Campbell and Tom Hensel for help with this.
Added --conf-dir option. Suggestion from Aaron Tygart.
Applied patch from Brent Cook which allows netids in
dhcp-option configuration lines to be prefixed by
"net:". This is not required by the syntax, but it is
consistent with other configuration items.
Added --log-facility option. Suggestion from Fabio Muzzi.
Major update to Spanish translation. Many thanks to Chris
Chatham.
Fixed gcc-4.1 strict-alias compilation warning.
version 2.33
Remove bash-specific shellcode from the Makefile.
Fix breakage with some DHCP relay implementations which
was introduced in 2.28. Believing the source port in
DHCP requests and sending the reply there is sometimes a
bad thing to do, so I've reverted to always sending to
the relay on port 68. Thanks to Daniel Hamlin and Alex
(alde) for bug reports on this.
Moved the SuSe packaging files to contrib. I will no
longer attempt to maintain this in the source tarball. It
will be done externally, in the same way as packaging for
other distros. Suse packages are available from
ftp://ftp.suse.com/pub/people/ug/
Merged patch from Gentoo to honour $LDFLAGS environment.
Fix bug in resolv.conf processing when more than one file
is being checked.
Add --dns-forward-max option.
Warn if --resolv-file flags are ignored because of
--no-resolv. Thanks to Martin F Krafft for spotting this
one.
Add --leasefile-ro option which allows the use of an
external lease database. Many thanks to Steve Horbachuk
for assistance developing this feature.
Provide extra information to lease-change script via its
environment. If the host has a client-id, then
DNSMASQ_CLIENT_ID will be set. Either the lease length (in
DNSMASQ_LEASE_LENGTH) or lease expiry time (in
DNSMASQ_LEASE_EXPIRES) will be set, depending on the
HAVE_BROKEN_RTC compile-time option. This extra
information should make it possible to maintain the lease
database in external storage such as LDAP or a relational
database. Note that while leasefile-ro is set, the script
will be called with "old" events more often, since
changes to the client-id and lease length
(HAVE_BROKEN_RTC) or lease expiry time (otherwise)
are now flagged.
Add contrib/wrt/* which is an example implementation of an
external persistent lease database for *WRT distros with
the nvram command.
Add contrib/wrt/dhcp_release.c which is a small utility
which removes DHCP leases using DHCPRELEASE operation in
the DHCP protocol.
version 2.34
Tweak network-determination code for another corner case:
in this case a host forced to move between dhcp-ranges on
the same physical interface. Thanks to Matthias Andree.
Improve handling of high DNS loads by throttling acceptance of
new queries when resources are tight. This should be a
better response than the "forwarding table full..."
message which was logged before.
Fixed intermittent infinite loop when re-reading
/etc/ethers after SIGHUP. Thanks to Eldon Ziegler for the
bug report.
Provide extra information to the lease-change script: when
a lease loses its hostname (because a new lease comes
along and claims the same new), the "old" action is called
with the current state of the lease, ie no name. The
change is to provide the former name which the lease had
in the environment variable DNSMASQ_OLD_HOSTNAME. This
helps scripts which do stuff based on hostname, rather
than IP address. Also provide vendor-class and user-class
information to the lease-change script when a new lease is
created in the DNSMASQ_VENDOR_CLASS and
DNSMASQ_USER_CLASS environment variables. Suggestion
from Francois-Xavier Le Bail.
Run the lease change script as root, even when dnsmasq is
configured to change UID to an unprivileged user. Since
most uses of the lease change script need root, this
allows its use whilst keeping the security advantages of
running the daemon without privs. The script is invoked
via a small helper process which keeps root UID, and
validates all data received from the main process. To get
root, an attacker would have to break dnsmasq and then
break the helper through the restricted comms channel
linking the two.
Add contrib/port-forward/* which is a script to set up
port-forwards using the DHCP lease-change script. It's
possible to add a host to a config file by name, and when
that host gets a DHCP lease, the script will use iptables
to set up port-forwards to configured ports at the address
which the host is allocated. The script also handles
setting up the port-forward iptables entries after reboot,
using the persistent lease database, and removing them
when a host leaves and its DHCP lease expires.
Fix unaligned access problem which caused wrong log
messages with some clients on some architectures. Thanks
to Francois-Xavier Le Bail for the bugreport.
Fixed problem with DHCPRELEASE and multi-address
interfaces. Enhanced contrib/wrt/dhcp_release to cope
under these circumstances too. Thanks to Eldon Ziegler for
input on this.
Updated French translation: thanks to Gildas Le Nadan.
Upgraded the name hash function in the DNS cache. Thanks
to Oleg Khovayko for good work on this.
Added --clear-on-reload flag. Suggestion from Johannes
Stezenbach.
Treat a nameserver address of 0.0.0.0 as "nothing". Erwin
Cabrera spotted that specifying a nameserver as 0.0.0.0
breaks things badly; this is because the network stack
treats is as "this host" and an endless loop ensues.
Added Webmin module in contrib/webmin. Thanks to Neil
Fisher for that.
version 2.35
Generate an "old" script event when a client does a DHCPREQUEST
in INIT-REBOOT or SELECTING state and the lease already
exists. Supply vendor and user class information to these
script calls.
Added support for Dragonfly BSD to src/config.h
Removed "Upgrading to 2.0" document, which is ancient
history now.
Tweak DHCP networking code for BSD, esp OpenBSD. Added a
workaround for a bug in OpenBSD 4.0: there should finally
be support for multiple interfaces under OpenBSD now.
Note that no version of dnsmasq before 2.35 will work for
DHCP under OpenBSD 4.0 because of a kernel bug.
Thanks to Claudio Jeker, Jeb Campbell and Cristobal
Palmer for help with this.
Optimised the cache code for the case of large
/etc/hosts. This is mainly to remove the O(n-squared)
algorithm which made reading large (50000 lines) files
slow, but it also takes into account the size of
/etc/hosts when building hash tables, so overall
performance should be better. Thanks to "koko" for
pointing out the problem.
version 2.36
Added --dhcp-ignore-names flag which tells dnsmasq not to
use names provided by DHCP clients. Suggestion from
Thomas M Steenholdt.
Send netmask and broadcast address DHCP options always,
even if the client doesn't request them. This makes a few
odd clients work better.
Added simple TFTP function, optimised for net-boot. It is
now possible to net boot hosts using only dnsmasq. The
TFTP server is read-only, binary-mode only, and designed to be
secure; it adds about 4K to the dnsmasq binary.
Support DHCP option 120, SIP servers, (RFC 3361). Both
encodings are supported, so both --dhcp-option=120,192.168.2.3
and --dhcp-option=120,sip.example.net will work. Brian
Candler pointed out the need for this.
Allow spaces in domain names, to support DNS-SD.
Add --ptr-record flag, again for DNS-SD. Thanks to Stephan
Sokolow for the suggestion.
Tolerate leading space on lines in the config file. Thanks
to Luigi Rizzo for pointing this out.
Fixed netlink.c to cope with headers from the Linux 2.6.19
kernel. Thanks to Philip Wall for the bug report.
Added --dhcp-bridge option, but only to the FreeBSD
build. This fixes an oddity with a a particular bridged
network configuration on FreeBSD. Thanks to Luigi Rizzo
for the patch.
Added FAQ entry about running dnsmasq in a Linux
vserver. Thanks to Gildas le Nadan for the information.
Fixed problem with option parsing which interpreted "/" as
an address and not a string. Thanks to Luigi Rizzo
for the patch.
Ignore the --domain-needed flag when forwarding NS
and SOA queries, since NS queries of TLDs are always legit.
Marcus Better pointed out this problem.
Take care to forward signed DNS requests bit-perfect, so
as not to affect the validity of the signature. This
should allow DDNS updates to be forwarded.
version 2.37
Add better support for RFC-2855 DHCP-over-firewire and RFC
-4390 DHCP-over-InfiniBand. A good suggestion from Karl Svec.
Some efficiency tweaks to the cache code for very large
/etc/hosts files. Should improve reverse (address->name)
lookups and garbage collection. Thanks to Jan 'RedBully'
Seiffert for input on this.
Fix regression in 2.36 which made bogus-nxdomain
and DNS caching unreliable. Thanks to Dennis DeDonatis
and Jan Seiffert for bug reports.
Make DHCP encapsulated vendor-class options sane. Be
warned that some conceivable existing configurations
using these may break, but they work in a much
simpler and more logical way now. Prepending
"vendor:" to an option encapsulates it
in option 43, and the option is sent only if the
client-supplied vendor-class substring-matches with
the given client-id. Thanks to Dennis DeDonatis for
help with this.
Apply patch from Jan Seiffert to tidy up tftp.c
Add support for overloading the filename and servername
fields in DHCP packet. This gives extra option-space when
these fields are not being used or with a modern client
which supports moving them into options.
Added a LIMITS section to the man-page, with guidance on
maximum numbers of clients, file sizes and tuning.
release 2.38
Fix compilation on *BSD. Thanks to Tom Hensel.
Don't send length zero DHCP option 43 and cope with
encapsulated options whose total length exceeds 255 octets
by splitting them into multiple option 43 pieces.
Avoid queries being retried forever when --strict-order is
set and an upstream server returns a SERVFAIL
error. Thanks to Johannes Stezenbach for spotting this.
Fix BOOTP support, broken in version 2.37.
Add example dhcp-options for Etherboot.
Add \e (for ASCII ESCape) to the set of valid escapes
in config-file strings.
Added --dhcp-option-force flag and examples in the
configuration file which use this to control PXELinux.
Added --tftp-no-blocksize option.
Set netid tag "bootp" when BOOTP (rather than DHCP) is in
use. This makes it easy to customise which options are
sent to BOOTP clients. (BOOTP allows only 64 octets for
options, so it can be necessary to trim things.)
Fix rare hang in cache code, a 2.37 regression. This
probably needs an infinite DHCP lease and some bad luck to
trigger. Thanks to Detlef Reichelt for bug reports and testing.
release 2.39
Apply patch from Mike Baker/OpenWRT to ensure that names
like "localhost." in /etc/hosts with trailing period
are treated as fully-qualified.
Tolerate and ignore spaces around commas in the
configuration file in all circumstances. Note that this
may change the meaning of a few existing config files, for
instance
txt-record=mydomain.com, string
would have a leading space in the string before, and now
will not. To get the old behaviour back, use quotes:
txt-record=mydomain.com," string"
/a is no longer a valid escape in quoted strings.
Added symbolic DHCP option names. Instead of
dhcp-option = 3, 1.2.3.4
it is now possible to do
dhcp-option = option:router, 1.2.3.4
To see the list of known DHCP options, use the
command "dnsmasq --help dhcp"
Thanks to Luigi Rizzo for a patch and good work on this.
Overhauled the log code so that logging can be asynchronous;
dnsmasq then no longer blocks waiting for the syslog() library
call. This is important on systems where syslog
is being used to log over the network (and therefore doing
DNS lookups) and syslog is using dnsmasq as its DNS
server. Having dnsmasq block awaiting syslog under
such circumstances can lead to syslog and dnsmasq
deadlocking. The new behaviour is enabled with a new
--log-async flag, which can also be used to tune the
queue length. Paul Chambers found and diagnosed
this trap for the unwary. He also did much testing of
the solution along with Carlos Carvalho.
--log-facility can now take a file-name instead of a
facility name. When this is done, dnsmasq logs to the
file and not via syslog. (Failures early in startup,
whilst reading configuration, will still go to syslog,
and syslog is used as a log-of-last-resort if the file
cannot be written.)
Added --log-dhcp flag. Suggestion from Carlos Carvalho.
Made BINDIR, MANDIR and LOCALEDIR independently
over-rideable in the makefile. Suggestion from Thomas
Klausner.
Added 127.0.0.0/8 and 169.254.0.0/16 to the address
ranges affected by --bogus-priv. Thanks to Paul
Chambers for the patch.
Fixed failure of TFTP server with --listen-address. Thanks
to William Dinkel for the bug report.
Added --dhcp-circuitid and --dhcp-remoteid for RFC3046
relay agent data matching.
Added --dhcp-subscrid for RFC3993 subscriber-id relay
agent data matching.
Correctly garbage-collect connections when upstream
servers go away as a result of DBus transactions.
Allow absolute paths for TFTP transfers even when
--tftp-root is set, as long as the path matches the root,
so /var/ftp/myfile is OK with tftp-root=/var/ftp.
Thanks for Thomas Mizzi for the patch.
Updated Spanish translation - thanks to Chris Chatham.
Updated French translation - thanks to Gildas Le Nadan.
Added to example conf file example of routing PTR queries
for a subnet to a different nameserver. Suggestion from
Jon Nicholson.
Added --interface-name option. This provides a facility
to add a domain name with a dynamic IP address taken from
the address of a local network interface. Useful for
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.
version 2.41
Remove deprecated calls when compiled against libdbus 1.1.
Fix "strict-alias" warning in bpf.c
Reduce dependency on Gnu-make in build system: dnsmasq now
builds with system make under OpenBSD.
Port to Solaris. Dnsmasq 1.x used to run under Solaris,
and this release does so again, for Solaris 9 or better.
Allow the DNS function to be completely disabled, by
setting the port to zero "--port=0". The allows dnsmasq to
be used as a simple DHCP server, simple TFTP server, or
both, but without the DNS server getting in the way.
Fix a bug where NXDOMAIN could be returned for a query
even if the name's value was known for a different query
type. This bug could be prodded with
--local=/domain/ --address=/name.domain/1.2.3.4
An IPv6 query for name.domain would return NXDOMAIN, and
not the correct NOERROR. Thanks to Lars Nooden for
spotting the bug and Jima for diagnosis of the problem.
Added per-server stats to the information logged when
dnsmasq gets SIGUSR1.
Added counts of queries forwarded and queries answered
locally (from the cache, /etc/hosts or config).
Fixed possible crash bug in DBus IPv6 code. Thanks to Matt
Domsch and Jima.
Tighten checks for clashes between hosts-file and
DHCP-derived names. Multiple addresses associated with a
name in hosts-file no longer confuses the check.
Add --dhcp-no-override option to fix problems with some
combinations of stage zero and stage one
bootloaders. Thanks to Steve Alexander for the bug report.
Add --tftp-port-range option. Thanks to Daniel Mierswa for
the suggestion.
Add --stop-dns-rebind option. Thanks to Collin Mulliner
for the patch.
Added GPL version 3 as a license option.
Added --all-servers option. Thanks to Peter Naulls for the
patch.
Extend source address mechanism so that the interface used
to contact an upstream DNS server can be nailed
down. Something like "--server=1.2.3.4@eth1" will force
the use of eth1 for traffic to DNS-server 1.2.3.4. This
facility is only available on Linux and Solaris. Thanks to
Peter Naulls for prompting this.
Add --dhcp-optsfile option. Thanks to Carlos Carvalho for
the suggestion.
Fixed failure to set source address for server connections
when using TCP. Thanks to Simon Capper for finding this
bug.
Refuse to give a DHCP client the address it asks for if
the address range in question is not available to that
particular host. Thanks to Cedric Duval for the bug
report.
Changed behavior of DHCP server to always return total length of
a new lease in DHCPOFFER, even if an existing lease
exists. (It used to return the time remaining on the lease
whne one existed.) This fixes problems with the Sony Ericsson
K610i phone. Thanks to Hakon Stordahl for finding and
fixing this.
Add DNSMASQ_INTERFACE to the environment of the
lease-change script. Thanks to Nikos Mavrogiannopoulos for
the patch.
Fixed broken --alias functionality. Thanks to Michael
Meelis for the bug report.
Added French translation of the man page. Thank to Gildas
Le Nadan for that.
Add --dhcp-match flag, to check for arbitrary options in
DHCP messages from clients. This enables use of dnsmasq
with gPXE. Thanks to Rance Hall for the suggestion.
Added --dhcp-broadcast, to force broadcast replies to DHCP
clients which need them but are too dumb or too old to
ask. Thanks to Bodo Bellut for the suggestion.
Disable path-MTU discovery on DHCP and TFTP sockets. This
is never needed, and the presence of DF flags in the IP
header confuses some broken PXE ROMS. Thanks again to Bodo
Bellut for spotting this.
Fix problems with addresses which have multiple PTR
records - all but one of these could get lost.
Fix bug with --address and ANY query type seeing REFUSED
return code in replies. Thanks to Mike Wright for spotting
the problem.
Update Spanish translation. Thanks to Chris Chatham.
Add --neg-ttl option.
Add warnings about the bad effects of --filterwin2k on
SIP, XMPP and Google-talk to the example config file.
Fix va_list abuse in log.c. This fixes crashes on powerpc
when debug mode is set. Thanks to Cedric Duval for the
patch.
version 2.42
Define _GNU_SOURCE to avoid problems with later glibc
headers. Thanks to Jima for spotting the problem.
Add --dhcp-alternate-port option. Thanks to Jan Psota for
the suggestion.
Fix typo in code which is only used on BSD, when Dbus and
IPv6 support is enabled. Thanks to Roy Marples.
Updated Polish translations - thank to Jan Psota.
Fix OS detection logic to cope with GNU/FreeBSD.
Fix unitialised variable in DBus code - thanks to Roy
Marples.
Fix network enumeration code to work on later NetBSD -
thanks to Roy Marples.
Provide --dhcp-bridge on all BSD variants.
Define _LARGEFILE_SOURCE which removes an arbitrary 2GB
limit on logfiles. Thanks to Paul Chambers for spotting
the problem.
Fix RFC3046 agent-id echo code, broken for many
releases. Thanks to Jeremy Laine for spotting the problem
and providing a patch.
Added Solaris 10 service manifest from David Connelly in
contrib/Solaris10
Add --dhcp-scriptuser option.
Support new capability interface on suitable Linux
kernels, removes "legacy support in use" messages. Thanks
to Jorge Bastos for pointing this out.
Fix subtle bug in cache code which could cause dnsmasq to
lock spinning CPU in rare circumstances. Thanks to Alex
Chekholko for bug reports and help debugging.
Support netascii transfer mode for TFTP.