mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
import of dnsmasq-2.0.tar.gz
This commit is contained in:
16
src/Makefile
Normal file
16
src/Makefile
Normal file
@@ -0,0 +1,16 @@
|
||||
# Uncomment this on Solaris.
|
||||
#LIBS = -lsocket -lnsl
|
||||
|
||||
CFLAGS?= -O2
|
||||
|
||||
OBJS = cache.o rfc1035.o util.o option.o forward.o \
|
||||
network.o dnsmasq.o dhcp.o lease.o rfc2131.o
|
||||
|
||||
.c.o: dnsmasq.h config.h
|
||||
$(CC) $(CFLAGS) $(RPM_OPT_FLAGS) -Wall -W -c $*.c
|
||||
|
||||
dnsmasq : $(OBJS) dnsmasq.h config.h
|
||||
$(CC) -o $@ $(OBJS) $(LIBS)
|
||||
|
||||
|
||||
|
||||
797
src/cache.c
Normal file
797
src/cache.c
Normal file
@@ -0,0 +1,797 @@
|
||||
/* dnsmasq is Copyright (c) 2000 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static struct crec *cache_head, *cache_tail, **hash_table;
|
||||
static struct crec *dhcp_inuse, *dhcp_spare, *new_chain;
|
||||
static int cache_inserted, cache_live_freed, insert_error;
|
||||
static union bigname *big_free;
|
||||
static int bignames_left, log_queries, cache_size, hash_size;
|
||||
static char *addn_file;
|
||||
|
||||
static void cache_free(struct crec *crecp);
|
||||
static void cache_unlink(struct crec *crecp);
|
||||
static void cache_link(struct crec *crecp);
|
||||
|
||||
void cache_init(int size, int logq)
|
||||
{
|
||||
struct crec *crecp;
|
||||
int i;
|
||||
|
||||
log_queries = logq;
|
||||
cache_head = cache_tail = NULL;
|
||||
dhcp_inuse = dhcp_spare = NULL;
|
||||
new_chain = NULL;
|
||||
cache_size = size;
|
||||
big_free = NULL;
|
||||
bignames_left = size/10;
|
||||
addn_file = NULL;
|
||||
|
||||
cache_inserted = cache_live_freed = 0;
|
||||
|
||||
if (cache_size > 0)
|
||||
{
|
||||
crecp = safe_malloc(size*sizeof(struct crec));
|
||||
|
||||
for (i=0; i<size; i++, crecp++)
|
||||
{
|
||||
cache_link(crecp);
|
||||
crecp->flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* hash_size is a power of two. */
|
||||
for (hash_size = 64; hash_size < cache_size/10; hash_size = hash_size << 1);
|
||||
hash_table = safe_malloc(hash_size*sizeof(struct crec *));
|
||||
for(i=0; i < hash_size; i++)
|
||||
hash_table[i] = NULL;
|
||||
}
|
||||
|
||||
static struct crec **hash_bucket(unsigned char *name)
|
||||
{
|
||||
unsigned int c, val = 0;
|
||||
|
||||
/* don't use tolower and friends here - they may be messed up by LOCALE */
|
||||
while((c = *name++))
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
val += c + 'a' - 'A';
|
||||
else
|
||||
val += c;
|
||||
|
||||
/* hash_size is a power of two */
|
||||
return hash_table + (val & (hash_size - 1));
|
||||
}
|
||||
|
||||
static void cache_hash(struct crec *crecp)
|
||||
{
|
||||
struct crec **bucket = hash_bucket(cache_get_name(crecp));
|
||||
crecp->hash_next = *bucket;
|
||||
*bucket = crecp;
|
||||
}
|
||||
|
||||
static void cache_free(struct crec *crecp)
|
||||
{
|
||||
crecp->flags &= ~F_FORWARD;
|
||||
crecp->flags &= ~F_REVERSE;
|
||||
|
||||
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)
|
||||
return crecp->name.namep;
|
||||
|
||||
return crecp->name.sname;
|
||||
}
|
||||
|
||||
static void 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. */
|
||||
|
||||
#define F_CACHESTATUS (F_HOSTS | F_DHCP | F_FORWARD | F_REVERSE | F_IPV4 | F_IPV6)
|
||||
struct crec *crecp, **up;
|
||||
flags &= (F_FORWARD | F_REVERSE | F_IPV6 | F_IPV4);
|
||||
|
||||
if (flags & F_FORWARD)
|
||||
{
|
||||
for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
|
||||
if ((!(crecp->flags & F_IMMORTAL) && difftime(now, crecp->ttd) > 0) ||
|
||||
((flags == (crecp->flags & F_CACHESTATUS)) && hostname_isequal(cache_get_name(crecp), name)))
|
||||
{
|
||||
*up = crecp->hash_next;
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
{
|
||||
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 = crecp->hash_next)
|
||||
if ((!(crecp->flags & F_IMMORTAL) && difftime(now, crecp->ttd) > 0) ||
|
||||
((flags == (crecp->flags & F_CACHESTATUS)) && memcmp(&crecp->addr, addr, addrlen) == 0))
|
||||
{
|
||||
*up = crecp->hash_next;
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
{
|
||||
cache_unlink(crecp);
|
||||
cache_free(crecp);
|
||||
}
|
||||
}
|
||||
else
|
||||
up = &crecp->hash_next;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
void cache_insert(char *name, struct all_addr *addr,
|
||||
time_t now, unsigned long ttl, unsigned short flags)
|
||||
{
|
||||
#ifdef HAVE_IPV6
|
||||
int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;
|
||||
#else
|
||||
int addrlen = INADDRSZ;
|
||||
#endif
|
||||
struct crec *new;
|
||||
union bigname *big_name = NULL;
|
||||
int freed_all = flags & F_REVERSE;
|
||||
|
||||
log_query(flags | F_UPSTREAM, name, addr);
|
||||
|
||||
/* name is needed as workspace by log_query in this case */
|
||||
if ((flags & F_NEG) && (flags & F_REVERSE))
|
||||
name = NULL;
|
||||
|
||||
/* CONFIG bit no needed except for logging */
|
||||
flags &= ~F_CONFIG;
|
||||
|
||||
/* if previous insertion failed give up now. */
|
||||
if (insert_error)
|
||||
return;
|
||||
|
||||
/* First remove any expired entries and entries for the name/address we
|
||||
are currently inserting. */
|
||||
cache_scan_free(name, addr, now, flags);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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 (freed_all)
|
||||
{
|
||||
cache_scan_free(cache_get_name(new), &new->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 *)malloc(sizeof(union bigname))))
|
||||
{
|
||||
insert_error = 1;
|
||||
return;
|
||||
}
|
||||
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)
|
||||
memcpy(&new->addr, addr, addrlen);
|
||||
new->ttd = now + (time_t)ttl;
|
||||
new->next = new_chain;
|
||||
new_chain = 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;
|
||||
cache_hash(new_chain);
|
||||
cache_link(new_chain);
|
||||
new_chain = tmp;
|
||||
cache_inserted++;
|
||||
}
|
||||
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;
|
||||
|
||||
for (up = hash_bucket(name), crecp = *up; crecp; crecp = next)
|
||||
{
|
||||
next = crecp->hash_next;
|
||||
|
||||
if ((crecp->flags & F_IMMORTAL) || difftime(now, crecp->ttd) < 0)
|
||||
{
|
||||
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 */
|
||||
if (!insert)
|
||||
{
|
||||
insert = up;
|
||||
up = &crecp->hash_next;
|
||||
}
|
||||
else
|
||||
{
|
||||
*up = crecp->hash_next;
|
||||
crecp->hash_next = *insert;
|
||||
*insert = crecp;
|
||||
insert = &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 */
|
||||
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 = crecp->hash_next)
|
||||
if ((crecp->flags & F_IMMORTAL) || difftime(now, crecp->ttd) < 0)
|
||||
{
|
||||
if ((crecp->flags & F_REVERSE) &&
|
||||
(crecp->flags & prot) &&
|
||||
memcmp(&crecp->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, addrlen) == 0)
|
||||
return ans;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen, unsigned short flags)
|
||||
{
|
||||
struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
|
||||
|
||||
/* Remove duplicates in hosts files. */
|
||||
if (lookup && (lookup->flags & F_HOSTS) &&
|
||||
memcmp(&lookup->addr, addr, addrlen) == 0)
|
||||
free(cache);
|
||||
else
|
||||
{
|
||||
cache->flags = flags;
|
||||
memcpy(&cache->addr, addr, addrlen);
|
||||
cache_hash(cache);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void read_hostsfile(char *filename, int opts, char *buff, char *domain_suffix, unsigned short addn_flag)
|
||||
{
|
||||
FILE *f = fopen(filename, "r");
|
||||
char *line;
|
||||
int count = 0, lineno = 0;
|
||||
|
||||
if (!f)
|
||||
{
|
||||
syslog(LOG_ERR, "failed to load names from %s: %m", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((line = fgets(buff, MAXDNAME, f)))
|
||||
{
|
||||
struct all_addr addr;
|
||||
char *token = strtok(line, " \t\n\r");
|
||||
int addrlen;
|
||||
unsigned short flags;
|
||||
|
||||
lineno++;
|
||||
|
||||
if (!token || (*token == '#'))
|
||||
continue;
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (inet_pton(AF_INET, token, &addr) == 1)
|
||||
{
|
||||
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
|
||||
addrlen = INADDRSZ;
|
||||
}
|
||||
else if (inet_pton(AF_INET6, token, &addr) == 1)
|
||||
{
|
||||
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
|
||||
addrlen = IN6ADDRSZ;
|
||||
}
|
||||
#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;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
continue;
|
||||
|
||||
while ((token = strtok(NULL, " \t\n\r")) && (*token != '#'))
|
||||
{
|
||||
struct crec *cache;
|
||||
if (canonicalise(token))
|
||||
{
|
||||
count++;
|
||||
/* If set, add a version of the name with a default domain appended */
|
||||
if ((opts & OPT_EXPAND) && domain_suffix && !strchr(token, '.') &&
|
||||
(cache = malloc(sizeof(struct crec) +
|
||||
strlen(token)+2+strlen(domain_suffix)-SMALLDNAME)))
|
||||
{
|
||||
strcpy(cache->name.sname, token);
|
||||
strcat(cache->name.sname, ".");
|
||||
strcat(cache->name.sname, domain_suffix);
|
||||
add_hosts_entry(cache, &addr, addrlen, flags | addn_flag);
|
||||
/* Only first name is cannonical and used for reverse lookups */
|
||||
flags &= ~F_REVERSE;
|
||||
}
|
||||
if ((cache = malloc(sizeof(struct crec) + strlen(token)+1-SMALLDNAME)))
|
||||
{
|
||||
strcpy(cache->name.sname, token);
|
||||
add_hosts_entry(cache, &addr, addrlen, flags | addn_flag);
|
||||
/* Clear this here in case not done above. */
|
||||
flags &= ~F_REVERSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
syslog(LOG_ERR, "bad name at %s line %d", filename, lineno);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
syslog(LOG_INFO, "read %s - %d addresses", filename, count);
|
||||
}
|
||||
|
||||
void cache_reload(int opts, char *buff, char *domain_suffix, char *addn_hosts)
|
||||
{
|
||||
struct crec *cache, **up, *tmp;
|
||||
int i;
|
||||
|
||||
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 ((opts & OPT_NO_HOSTS) && !addn_hosts)
|
||||
{
|
||||
if (cache_size > 0)
|
||||
syslog(LOG_INFO, "cleared cache");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(opts & OPT_NO_HOSTS))
|
||||
read_hostsfile(HOSTSFILE, opts, buff, domain_suffix, 0);
|
||||
if (addn_hosts)
|
||||
{
|
||||
read_hostsfile(addn_hosts, opts, buff, domain_suffix, F_ADDN);
|
||||
addn_file = addn_hosts;
|
||||
}
|
||||
}
|
||||
|
||||
void cache_unhash_dhcp(void)
|
||||
{
|
||||
struct crec *tmp, *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;
|
||||
else
|
||||
up = &cache->hash_next;
|
||||
|
||||
/* prev field links all dhcp entries */
|
||||
for (cache = dhcp_inuse; cache; cache = tmp)
|
||||
{
|
||||
tmp = cache->prev;
|
||||
cache->prev = dhcp_spare;
|
||||
dhcp_spare = cache;
|
||||
}
|
||||
|
||||
dhcp_inuse = NULL;
|
||||
}
|
||||
|
||||
void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t ttd, unsigned short flags)
|
||||
{
|
||||
struct crec *crec;
|
||||
|
||||
if ((crec = cache_find_by_name(NULL, host_name, 0, F_IPV4)))
|
||||
{
|
||||
if (crec->flags & F_HOSTS)
|
||||
syslog(LOG_WARNING, "Ignoring DHCP lease for %s because it clashes with an /etc/hosts entry.", host_name);
|
||||
else if (!(crec->flags & F_DHCP))
|
||||
{
|
||||
if (crec->flags & F_NEG)
|
||||
{
|
||||
/* name may have been searched for before being allocated to DHCP and
|
||||
therefore got a negative cache entry. If so delete it and continue. */
|
||||
cache_scan_free(host_name, NULL, 0, F_IPV4 | F_FORWARD);
|
||||
goto newrec;
|
||||
}
|
||||
else
|
||||
syslog(LOG_WARNING, "Ignoring DHCP lease for %s because it clashes with a cached name.", cache_get_name(crec));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, F_IPV4)) && (crec->flags & F_NEG))
|
||||
cache_scan_free(NULL, (struct all_addr *)host_address, 0, F_IPV4 | F_REVERSE);
|
||||
|
||||
newrec:
|
||||
if ((crec = dhcp_spare))
|
||||
dhcp_spare = dhcp_spare->prev;
|
||||
else /* need new one */
|
||||
crec = malloc(sizeof(struct crec));
|
||||
|
||||
if (crec) /* malloc may fail */
|
||||
{
|
||||
crec->flags = F_DHCP | F_FORWARD | F_IPV4 | flags;
|
||||
if (ttd == 0)
|
||||
crec->flags |= F_IMMORTAL;
|
||||
else
|
||||
crec->ttd = ttd;
|
||||
memcpy(&crec->addr, host_address, INADDRSZ);
|
||||
crec->name.namep = host_name;
|
||||
crec->prev = dhcp_inuse;
|
||||
dhcp_inuse = crec;
|
||||
cache_hash(crec);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dump_cache(int debug, int cache_size)
|
||||
{
|
||||
syslog(LOG_INFO, "Cache size %d, %d/%d cache insertions re-used unexpired cache entries.",
|
||||
cache_size, cache_live_freed, cache_inserted);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
struct crec *cache ;
|
||||
char addrbuff[ADDRSTRLEN];
|
||||
int i;
|
||||
syslog(LOG_DEBUG, "Host Address Flags Expires\n");
|
||||
|
||||
for (i=0; i<hash_size; i++)
|
||||
for (cache = hash_table[i]; cache; cache = cache->hash_next)
|
||||
{
|
||||
if ((cache->flags & F_NEG) && (cache->flags & F_FORWARD))
|
||||
addrbuff[0] = 0;
|
||||
#ifdef HAVE_IPV6
|
||||
else if (cache->flags & F_IPV4)
|
||||
inet_ntop(AF_INET, &cache->addr, addrbuff, ADDRSTRLEN);
|
||||
else if (cache->flags & F_IPV6)
|
||||
inet_ntop(AF_INET6, &cache->addr, addrbuff, ADDRSTRLEN);
|
||||
#else
|
||||
else
|
||||
strcpy(addrbuff, inet_ntoa(cache->addr.addr.addr4));
|
||||
#endif
|
||||
syslog(LOG_DEBUG, "%-40.40s %-30.30s %s%s%s%s%s%s%s%s%s%s %s",
|
||||
cache_get_name(cache), addrbuff,
|
||||
cache->flags & F_IPV4 ? "4" : "",
|
||||
cache->flags & F_IPV6 ? "6" : "",
|
||||
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" : " ",
|
||||
cache->flags & F_ADDN ? "A" : " ",
|
||||
cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd))) ;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void log_query(unsigned short flags, char *name, struct all_addr *addr)
|
||||
{
|
||||
char *source;
|
||||
char *verb = "is";
|
||||
char addrbuff[ADDRSTRLEN];
|
||||
|
||||
if (!log_queries)
|
||||
return;
|
||||
|
||||
if (flags & F_NEG)
|
||||
{
|
||||
if (flags & F_REVERSE)
|
||||
#ifdef HAVE_IPV6
|
||||
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
|
||||
addr, name, MAXDNAME);
|
||||
#else
|
||||
strcpy(name, inet_ntoa(addr->addr.addr4));
|
||||
#endif
|
||||
|
||||
if (flags & F_NXDOMAIN)
|
||||
strcpy(addrbuff, "<NXDOMAIN>-");
|
||||
else
|
||||
strcpy(addrbuff, "<NODATA>-");
|
||||
|
||||
if (flags & F_IPV4)
|
||||
strcat(addrbuff, "IPv4");
|
||||
else
|
||||
strcat(addrbuff, "IPv6");
|
||||
}
|
||||
else
|
||||
#ifdef HAVE_IPV6
|
||||
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
|
||||
addr, addrbuff, ADDRSTRLEN);
|
||||
#else
|
||||
strcpy(addrbuff, inet_ntoa(addr->addr.addr4));
|
||||
#endif
|
||||
|
||||
if (flags & F_DHCP)
|
||||
source = "DHCP";
|
||||
else if (flags & F_HOSTS)
|
||||
{
|
||||
if (flags & F_ADDN)
|
||||
source = addn_file;
|
||||
else
|
||||
source = HOSTSFILE;
|
||||
}
|
||||
else if (flags & F_CONFIG)
|
||||
source = "config";
|
||||
else if (flags & F_UPSTREAM)
|
||||
source = "reply";
|
||||
else if (flags & F_SERVER)
|
||||
{
|
||||
source = "forwarded";
|
||||
verb = "to";
|
||||
}
|
||||
else if (flags & F_QUERY)
|
||||
{
|
||||
source = "query";
|
||||
verb = "from";
|
||||
}
|
||||
else
|
||||
source = "cached";
|
||||
|
||||
if ((flags & F_FORWARD) | (flags & F_NEG))
|
||||
syslog(LOG_DEBUG, "%s %s %s %s", source, name, verb, addrbuff);
|
||||
else if (flags & F_REVERSE)
|
||||
syslog(LOG_DEBUG, "%s %s is %s", source, addrbuff, name);
|
||||
}
|
||||
|
||||
256
src/config.h
Normal file
256
src/config.h
Normal file
@@ -0,0 +1,256 @@
|
||||
/* dnsmasq is Copyright (c) 2000 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
#define VERSION "2.0"
|
||||
|
||||
#define FTABSIZ 150 /* max number of outstanding requests */
|
||||
#define TIMEOUT 40 /* drop queries after TIMEOUT seconds */
|
||||
#define LOGRATE 120 /* log table overflows every LOGRATE seconds */
|
||||
#define CACHESIZ 150 /* default cache size */
|
||||
#define SMALLDNAME 40 /* most domain names are smaller than this */
|
||||
#define CONFFILE "/etc/dnsmasq.conf"
|
||||
#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"
|
||||
#ifdef __FreeBSD__
|
||||
# define LEASEFILE "/var/db/dnsmasq.leases"
|
||||
#else
|
||||
# define LEASEFILE "/var/lib/misc/dnsmasq.leases"
|
||||
#endif
|
||||
#define DEFLEASE 3600 /* default lease time, 1 hour */
|
||||
#define CHUSER "nobody"
|
||||
#define CHGRP "dip"
|
||||
#define IP6INTERFACES "/proc/net/if_inet6"
|
||||
#define DHCP_SERVER_PORT 67
|
||||
#define DHCP_CLIENT_PORT 68
|
||||
|
||||
/* Logfile stuff - change this to change the options and facility */
|
||||
/* debug is true if the --no-daemon flag is given */
|
||||
#ifdef LOG_PERROR
|
||||
# define DNSMASQ_LOG_OPT(debug) (debug) ? LOG_PERROR : LOG_PID
|
||||
#else
|
||||
# define DNSMASQ_LOG_OPT(debug) (debug) ? 0 : LOG_PID
|
||||
#endif
|
||||
|
||||
#ifdef LOG_LOCAL0
|
||||
# define DNSMASQ_LOG_FAC(debug) (debug) ? LOG_LOCAL0 : LOG_DAEMON
|
||||
#else
|
||||
# define DNSMASQ_LOG_FAC(debug) LOG_DAEMON
|
||||
#endif
|
||||
|
||||
|
||||
/* Decide if we're going to support IPv6 */
|
||||
/* We assume that systems which don't have IPv6
|
||||
headers don't have ntop and pton either */
|
||||
|
||||
#if defined(INET6_ADDRSTRLEN)
|
||||
# define HAVE_IPV6
|
||||
# define ADDRSTRLEN INET6_ADDRSTRLEN
|
||||
#elif defined(INET_ADDRSTRLEN)
|
||||
# undef HAVE_IPV6
|
||||
# define ADDRSTRLEN INET_ADDRSTRLEN
|
||||
#else
|
||||
# undef HAVE_IPV6
|
||||
# define ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */
|
||||
#endif
|
||||
|
||||
/* Get linux C library versions. */
|
||||
#if defined(__linux__) && !defined(__UCLIBC__) && !defined(__uClinux__)
|
||||
# include <libio.h>
|
||||
#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_IPV6_PROC
|
||||
define this to do IPv6 interface discovery using
|
||||
proc/net/if_inet6 ala LINUX.
|
||||
|
||||
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_RANDOM
|
||||
define this if you have the 4.2BSD random() function (and its
|
||||
associated srandom() function), which is at least as good as (if not
|
||||
better than) the rand() function.
|
||||
|
||||
HAVE_DEV_RANDOM
|
||||
define this if you have the /dev/random device, which gives truly
|
||||
random numbers but may run out of random numbers.
|
||||
|
||||
HAVE_DEV_URANDOM
|
||||
define this if you have the /dev/urandom device, which gives
|
||||
semi-random numbers when it runs out of truly random numbers.
|
||||
|
||||
HAVE_SOCKADDR_SA_LEN
|
||||
define this if struct sockaddr has sa_len field (*BSD)
|
||||
|
||||
HAVE_PSELECT
|
||||
If your C library implements pselect, define this.
|
||||
|
||||
HAVE_PF_PACKET
|
||||
If your OS implements packet sockets, define this.
|
||||
|
||||
HAVE_BPF
|
||||
If your OS implements Berkeley PAcket filter, define this.
|
||||
|
||||
NOTES:
|
||||
For Linux you should define
|
||||
HAVE_LINUX_IPV6_PROC
|
||||
HAVE_GETOPT_LONG
|
||||
HAVE_RANDOM
|
||||
HAVE_DEV_RANDOM
|
||||
HAVE_DEV_URANDOM
|
||||
HAVE_PF_PACKET
|
||||
you should NOT define
|
||||
HAVE_ARC4RANDOM
|
||||
HAVE_SOCKADDR_SA_LEN
|
||||
|
||||
For *BSD systems you should define
|
||||
HAVE_SOCKADDR_SA_LEN
|
||||
HAVE_RANDOM
|
||||
HAVE_BPF
|
||||
you should NOT define
|
||||
HAVE_LINUX_IPV6_PROC
|
||||
and you MAY define
|
||||
HAVE_ARC4RANDOM - OpenBSD and FreeBSD
|
||||
HAVE_DEV_URANDOM - OpenBSD and FreeBSD
|
||||
HAVE_DEV_RANDOM - FreeBSD (OpenBSD with hardware random number generator)
|
||||
HAVE_GETOPT_LONG - only if you link GNU getopt.
|
||||
|
||||
*/
|
||||
|
||||
/* Must preceed __linux__ since uClinux defines __linux__ too. */
|
||||
#if defined(__uClinux__) || defined(__UCLIBC__)
|
||||
#undef HAVE_LINUX_IPV6_PROC
|
||||
#define HAVE_GETOPT_LONG
|
||||
#undef HAVE_ARC4RANDOM
|
||||
#define HAVE_RANDOM
|
||||
#define HAVE_DEV_URANDOM
|
||||
#define HAVE_DEV_RANDOM
|
||||
#define HAVE_PF_PACKET
|
||||
#undef HAVE_SOCKADDR_SA_LEN
|
||||
#undef HAVE_PSELECT
|
||||
/* Don't fork into background on uClinux */
|
||||
#if defined(__uClinux__)
|
||||
# define NO_FORK
|
||||
#endif
|
||||
|
||||
/* libc5 - must precede __linux__ too */
|
||||
/* Note to build a libc5 binary on a modern Debian system:
|
||||
install the packages altgcc libc5 and libc5-altdev
|
||||
then run "make CC=i486-linuxlibc1-gcc" */
|
||||
/* Note that compling dnsmasq 2.x under libc5 and kernel 2.0.x
|
||||
is probably doomed - no packet socket for starters. */
|
||||
#elif defined(__linux__) && \
|
||||
defined(_LINUX_C_LIB_VERSION_MAJOR) && \
|
||||
(_LINUX_C_LIB_VERSION_MAJOR == 5 )
|
||||
#undef HAVE_IPV6
|
||||
#undef HAVE_LINUX_IPV6_PROC
|
||||
#define HAVE_GETOPT_LONG
|
||||
#undef HAVE_ARC4RANDOM
|
||||
#define HAVE_RANDOM
|
||||
#define HAVE_DEV_URANDOM
|
||||
#define HAVE_DEV_RANDOM
|
||||
#undef HAVE_PF_PACKET
|
||||
#undef HAVE_SOCKADDR_SA_LEN
|
||||
#undef HAVE_PSELECT
|
||||
/* Fix various misfeatures of libc5 headers */
|
||||
#define T_SRV 33
|
||||
typedef unsigned long in_addr_t;
|
||||
typedef size_t socklen_t;
|
||||
|
||||
/* This is for glibc 2.x */
|
||||
#elif defined(__linux__)
|
||||
#define HAVE_LINUX_IPV6_PROC
|
||||
#define HAVE_GETOPT_LONG
|
||||
#undef HAVE_ARC4RANDOM
|
||||
#define HAVE_RANDOM
|
||||
#define HAVE_DEV_URANDOM
|
||||
#define HAVE_DEV_RANDOM
|
||||
#undef HAVE_SOCKADDR_SA_LEN
|
||||
#define HAVE_PSELECT
|
||||
#define HAVE_PF_PACKET
|
||||
/* glibc < 2.2 has broken Sockaddr_in6 so we have to use our own. */
|
||||
/* glibc < 2.2 doesn't define in_addr_t */
|
||||
#if defined(__GLIBC__) && (__GLIBC__ == 2) && \
|
||||
defined(__GLIBC_MINOR__) && (__GLIBC_MINOR__ < 2)
|
||||
typedef unsigned long in_addr_t;
|
||||
#if defined(HAVE_IPV6)
|
||||
# define HAVE_BROKEN_SOCKADDR_IN6
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#undef HAVE_LINUX_IPV6_PROC
|
||||
#undef HAVE_GETOPT_LONG
|
||||
#define HAVE_ARC4RANDOM
|
||||
#define HAVE_RANDOM
|
||||
#define HAVE_DEV_URANDOM
|
||||
#define HAVE_SOCKADDR_SA_LEN
|
||||
#undef HAVE_PSELECT
|
||||
#define HAVE_BPF
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#undef HAVE_LINUX_IPV6_PROC
|
||||
#undef HAVE_GETOPT_LONG
|
||||
#define HAVE_ARC4RANDOM
|
||||
#define HAVE_RANDOM
|
||||
#define HAVE_DEV_URANDOM
|
||||
#define HAVE_SOCKADDR_SA_LEN
|
||||
#undef HAVE_PSELECT
|
||||
#define HAVE_BPF
|
||||
/* Define before sys/socket.h is included so we get socklen_t */
|
||||
#define _BSD_SOCKLEN_T_
|
||||
/* The two below are not defined in Mac OS X arpa/nameserv.h */
|
||||
#define IN6ADDRSZ 16
|
||||
#define T_SRV 33
|
||||
|
||||
#elif defined(__NetBSD__)
|
||||
#undef HAVE_LINUX_IPV6_PROC
|
||||
#undef HAVE_GETOPT_LONG
|
||||
#undef HAVE_ARC4RANDOM
|
||||
#define HAVE_RANDOM
|
||||
#undef HAVE_DEV_URANDOM
|
||||
#undef HAVE_DEV_RANDOM
|
||||
#define HAVE_SOCKADDR_SA_LEN
|
||||
#undef HAVE_PSELECT
|
||||
#define HAVE_BPF
|
||||
|
||||
/* env "LIBS=-lsocket -lnsl" make */
|
||||
#elif defined(__sun) || defined(__sun__)
|
||||
#undef HAVE_LINUX_IPV6_PROC
|
||||
#undef HAVE_GETOPT_LONG
|
||||
#undef HAVE_ARC4RANDOM
|
||||
#define HAVE_RANDOM
|
||||
#undef HAVE_DEV_URANDOM
|
||||
#undef HAVE_DEV_RANDOM
|
||||
#undef HAVE_SOCKADDR_SA_LEN
|
||||
#undef HAVE_PSELECT
|
||||
#define HAVE_BPF
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
273
src/dhcp.c
Normal file
273
src/dhcp.c
Normal file
@@ -0,0 +1,273 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
void dhcp_packet(struct dhcp_context *context, char *packet,
|
||||
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
|
||||
time_t now, char *namebuff, char *domain_suffix,
|
||||
char *dhcp_file, char *dhcp_sname,
|
||||
struct in_addr dhcp_next_server)
|
||||
{
|
||||
struct udp_dhcp_packet *rawpacket = (struct udp_dhcp_packet *) packet;
|
||||
struct dhcp_packet *mess = (struct dhcp_packet *)&rawpacket->data;
|
||||
int sz, newlen;
|
||||
|
||||
sz = recvfrom(context->fd, &rawpacket->data,
|
||||
PACKETSZ - (sizeof(struct ip) + sizeof(struct udphdr)),
|
||||
0, NULL, 0);
|
||||
if ((unsigned int)sz > (sizeof(*mess) - sizeof(mess->options)))
|
||||
{
|
||||
lease_prune(NULL, now); /* lose any expired leases */
|
||||
newlen = dhcp_reply(context, mess, sz, now, namebuff, dhcp_opts,
|
||||
dhcp_configs, domain_suffix, dhcp_file,
|
||||
dhcp_sname, dhcp_next_server );
|
||||
lease_update_dns(0);
|
||||
|
||||
if (newlen != 0)
|
||||
{
|
||||
int broadcast = ntohs(mess->flags) & 0x8000;
|
||||
|
||||
/* newlen -ve forces broadcast */
|
||||
if (newlen < 0)
|
||||
{
|
||||
broadcast = 1;
|
||||
newlen = -newlen;
|
||||
}
|
||||
|
||||
if (mess->giaddr.s_addr || mess->ciaddr.s_addr)
|
||||
{
|
||||
/* To send to BOOTP relay or configured client, use
|
||||
the IP packet */
|
||||
|
||||
struct sockaddr_in dest;
|
||||
dest.sin_family = AF_INET;
|
||||
|
||||
if (mess->giaddr.s_addr)
|
||||
{
|
||||
dest.sin_port = htons(DHCP_SERVER_PORT);
|
||||
dest.sin_addr = mess->giaddr;
|
||||
}
|
||||
else
|
||||
{
|
||||
dest.sin_port = htons(DHCP_CLIENT_PORT);
|
||||
dest.sin_addr = mess->ciaddr;
|
||||
}
|
||||
|
||||
sendto(context->fd, mess, newlen, 0, (struct sockaddr *)&dest, sizeof(dest));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Hairy stuff, packet either has to go to the
|
||||
net broadcast or the destination can't reply to ARP yet,
|
||||
but we do know the physical address.
|
||||
Build the packet by steam, and send directly, bypassing
|
||||
the kernel IP stack */
|
||||
|
||||
u32 i, sum;
|
||||
#ifdef HAVE_PF_PACKET
|
||||
struct sockaddr_ll dest;
|
||||
|
||||
dest.sll_family = AF_PACKET;
|
||||
dest.sll_halen = ETHER_ADDR_LEN;
|
||||
dest.sll_ifindex = context->ifindex;
|
||||
dest.sll_protocol = htons(ETHERTYPE_IP);
|
||||
|
||||
if (broadcast)
|
||||
{
|
||||
memset(dest.sll_addr, 255, ETHER_ADDR_LEN);
|
||||
rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(dest.sll_addr, mess->chaddr, ETHER_ADDR_LEN);
|
||||
rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_BPF
|
||||
struct ether_header header;
|
||||
struct iovec iov [2];
|
||||
|
||||
header.ether_type = htons(ETHERTYPE_IP);
|
||||
memcpy(header.ether_shost, context->hwaddr, ETHER_ADDR_LEN);
|
||||
|
||||
if (broadcast)
|
||||
{
|
||||
memset(header.ether_dhost, 255, ETHER_ADDR_LEN);
|
||||
rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(header.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
|
||||
rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr;
|
||||
}
|
||||
#endif
|
||||
|
||||
rawpacket->ip.ip_p = IPPROTO_UDP;
|
||||
rawpacket->ip.ip_src.s_addr = context->serv_addr.s_addr;
|
||||
rawpacket->ip.ip_len = htons(sizeof(struct ip) +
|
||||
sizeof(struct udphdr) +
|
||||
newlen) ;
|
||||
rawpacket->ip.ip_hl = sizeof(struct ip) / 4;
|
||||
rawpacket->ip.ip_v = IPVERSION;
|
||||
rawpacket->ip.ip_tos = 0;
|
||||
rawpacket->ip.ip_id = htons(0);
|
||||
rawpacket->ip.ip_off = htons(0x4000); /* don't fragment */
|
||||
rawpacket->ip.ip_ttl = IPDEFTTL;
|
||||
rawpacket->ip.ip_sum = 0;
|
||||
for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
|
||||
sum += ((u16 *)&rawpacket->ip)[i];
|
||||
while (sum>>16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
rawpacket->ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
|
||||
|
||||
rawpacket->udp.uh_sport = htons(DHCP_SERVER_PORT);
|
||||
rawpacket->udp.uh_dport = htons(DHCP_CLIENT_PORT);
|
||||
((u8 *)&rawpacket->data)[newlen] = 0; /* for checksum, in case length is odd. */
|
||||
rawpacket->udp.uh_sum = 0;
|
||||
rawpacket->udp.uh_ulen = sum = htons(sizeof(struct udphdr) + newlen);
|
||||
sum += htons(IPPROTO_UDP);
|
||||
for (i = 0; i < 4; i++)
|
||||
sum += ((u16 *)&rawpacket->ip.ip_src)[i];
|
||||
for (i = 0; i < (sizeof(struct udphdr) + newlen + 1) / 2; i++)
|
||||
sum += ((u16 *)&rawpacket->udp)[i];
|
||||
while (sum>>16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
rawpacket->udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
|
||||
|
||||
#ifdef HAVE_PF_PACKET
|
||||
sendto(context->rawfd, rawpacket, ntohs(rawpacket->ip.ip_len),
|
||||
0, (struct sockaddr *)&dest, sizeof(dest));
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_BPF
|
||||
iov[0].iov_base = (char *)&header;
|
||||
iov[0].iov_len = sizeof(struct ether_header);
|
||||
iov[1].iov_base = (char *)rawpacket;
|
||||
iov[1].iov_len = ntohs(rawpacket->ip.ip_len);
|
||||
writev(context->rawfd, iov, 2);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int address_available(struct dhcp_context *context, struct in_addr taddr)
|
||||
{
|
||||
/* Check is an address is OK for this network, ie
|
||||
within allowable range and not in an existing lease */
|
||||
|
||||
unsigned int addr, start, end;
|
||||
|
||||
addr = ntohl(taddr.s_addr);
|
||||
start = ntohl(context->start.s_addr);
|
||||
end = ntohl(context->end.s_addr);
|
||||
|
||||
if (addr < start)
|
||||
return 0;
|
||||
|
||||
if (addr > end)
|
||||
return 0;
|
||||
|
||||
if (lease_find_by_addr(taddr))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int address_allocate(struct dhcp_context *context, struct dhcp_config *configs,
|
||||
struct in_addr *addrp)
|
||||
{
|
||||
/* Find a free address: exlude anything in use and anything allocated to
|
||||
a particular hwaddr/clientid/hostname in our configuration */
|
||||
|
||||
struct dhcp_config *config;
|
||||
struct in_addr start = context->last;
|
||||
|
||||
do {
|
||||
if (context->last.s_addr == context->end.s_addr)
|
||||
context->last = context->start;
|
||||
else
|
||||
context->last.s_addr = htonl(ntohl(context->last.s_addr) + 1);
|
||||
|
||||
|
||||
if (!lease_find_by_addr(context->last))
|
||||
{
|
||||
for (config = configs; config; config = config->next)
|
||||
if (config->addr.s_addr == context->last.s_addr)
|
||||
break;
|
||||
|
||||
if (!config)
|
||||
{
|
||||
*addrp = context->last;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} while (context->last.s_addr != start.s_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
|
||||
{
|
||||
if (!context)
|
||||
return 1;
|
||||
if (config->addr.s_addr == 0)
|
||||
return 1;
|
||||
if ((config->addr.s_addr & context->netmask.s_addr) == (context->start.s_addr & context->netmask.s_addr))
|
||||
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, char *hostname)
|
||||
{
|
||||
struct dhcp_config *config;
|
||||
|
||||
if (clid_len)
|
||||
for (config = configs; config; config = config->next)
|
||||
{
|
||||
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 (memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 &&
|
||||
is_addr_in_context(context, config))
|
||||
return config;
|
||||
|
||||
if (hostname)
|
||||
for (config = configs; config; config = config->next)
|
||||
if (config->hostname && strcmp(config->hostname, hostname) == 0 &&
|
||||
is_addr_in_context(context, config))
|
||||
return config;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
455
src/dnsmasq.c
Normal file
455
src/dnsmasq.c
Normal file
@@ -0,0 +1,455 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/* See RFC1035 for details of the protocol this code talks. */
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static int sigterm, sighup, sigusr1, sigusr2;
|
||||
|
||||
static void sig_handler(int sig)
|
||||
{
|
||||
if (sig == SIGTERM)
|
||||
sigterm = 1;
|
||||
else if (sig == SIGHUP)
|
||||
sighup = 1;
|
||||
else if (sig == SIGUSR1)
|
||||
sigusr1 = 1;
|
||||
else if (sig == SIGUSR2)
|
||||
sigusr2 = 1;
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
char *int_err_string;
|
||||
int cachesize = CACHESIZ;
|
||||
int port = NAMESERVER_PORT;
|
||||
int query_port = 0;
|
||||
int first_loop = 1;
|
||||
unsigned long local_ttl = 0;
|
||||
unsigned int options;
|
||||
char *runfile = RUNFILE;
|
||||
time_t resolv_changed = 0;
|
||||
time_t now, last = 0;
|
||||
struct irec *iface, *interfaces = NULL;
|
||||
char *mxname = NULL;
|
||||
char *mxtarget = NULL;
|
||||
char *lease_file = NULL;
|
||||
char *addn_hosts = NULL;
|
||||
char *domain_suffix = NULL;
|
||||
char *username = CHUSER;
|
||||
char *groupname = CHGRP;
|
||||
struct iname *if_names = NULL;
|
||||
struct iname *if_addrs = NULL;
|
||||
struct iname *if_except = NULL;
|
||||
struct iname *if_tmp;
|
||||
struct server *serv_addrs = NULL;
|
||||
char *dnamebuff, *packet;
|
||||
struct server *servers, *last_server;
|
||||
struct resolvc default_resolv = { NULL, 1, 0, RESOLVFILE };
|
||||
struct resolvc *resolv = &default_resolv;
|
||||
struct bogus_addr *bogus_addr = NULL;
|
||||
struct serverfd *serverfdp, *sfds = NULL;
|
||||
struct dhcp_context *dhcp_tmp, *dhcp = NULL;
|
||||
struct dhcp_config *dhcp_configs = NULL;
|
||||
struct dhcp_opt *dhcp_options = NULL;
|
||||
char *dhcp_file = NULL, *dhcp_sname = NULL;
|
||||
struct in_addr dhcp_next_server;
|
||||
int leasefd = 0;
|
||||
struct sigaction sigact;
|
||||
sigset_t sigmask;
|
||||
|
||||
sighup = 1; /* init cache the first time through */
|
||||
sigusr1 = 0; /* but don't dump */
|
||||
sigusr2 = 0; /* or rescan interfaces */
|
||||
sigterm = 0; /* or die */
|
||||
|
||||
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);
|
||||
|
||||
/* now block all the signals, they stay that way except
|
||||
during the call to pselect */
|
||||
sigaddset(&sigact.sa_mask, SIGUSR1);
|
||||
sigaddset(&sigact.sa_mask, SIGUSR2);
|
||||
sigaddset(&sigact.sa_mask, SIGTERM);
|
||||
sigaddset(&sigact.sa_mask, SIGHUP);
|
||||
sigprocmask(SIG_BLOCK, &sigact.sa_mask, &sigmask);
|
||||
|
||||
/* These get allocated here to avoid overflowing the small stack
|
||||
on embedded systems. dnamebuff is big enough to hold one
|
||||
maximal sixed domain name and gets passed into all the processing
|
||||
code. We manage to get away with one buffer. */
|
||||
dnamebuff = safe_malloc(MAXDNAME);
|
||||
/* Size: we check after adding each record, so there must be
|
||||
memory for the largest packet, and the largest record */
|
||||
packet = safe_malloc(PACKETSZ+MAXDNAME+RRFIXEDSZ);
|
||||
|
||||
dhcp_next_server.s_addr = 0;
|
||||
options = read_opts(argc, argv, dnamebuff, &resolv, &mxname, &mxtarget, &lease_file,
|
||||
&username, &groupname, &domain_suffix, &runfile,
|
||||
&if_names, &if_addrs, &if_except, &bogus_addr,
|
||||
&serv_addrs, &cachesize, &port, &query_port, &local_ttl, &addn_hosts,
|
||||
&dhcp, &dhcp_configs, &dhcp_options,
|
||||
&dhcp_file, &dhcp_sname, &dhcp_next_server);
|
||||
|
||||
if (!lease_file)
|
||||
lease_file = LEASEFILE;
|
||||
else
|
||||
{
|
||||
if (!dhcp)
|
||||
{
|
||||
complain("********* dhcp-lease option set, but not dhcp-range.", NULL);
|
||||
complain("********* Are you trying to use the obsolete ISC dhcpd integration?", NULL);
|
||||
complain("********* Please configure the dnsmasq integrated DHCP server by using", NULL);
|
||||
complain("********* the \"dhcp-range\" option, and remove any other DHCP server.", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if ((int_err_string = enumerate_interfaces(&interfaces, if_names, if_addrs, if_except, dhcp, port)))
|
||||
die(int_err_string, NULL);
|
||||
|
||||
for (if_tmp = if_names; if_tmp; if_tmp = if_tmp->next)
|
||||
if (if_tmp->name && !if_tmp->found)
|
||||
die("unknown interface %s", if_tmp->name);
|
||||
|
||||
for (if_tmp = if_addrs; if_tmp; if_tmp = if_tmp->next)
|
||||
if (!if_tmp->found)
|
||||
{
|
||||
#ifdef HAVE_IPV6
|
||||
if (if_tmp->addr.sa.sa_family == AF_INET)
|
||||
inet_ntop(AF_INET, &if_tmp->addr.in.sin_addr,
|
||||
dnamebuff, MAXDNAME);
|
||||
else
|
||||
inet_ntop(AF_INET6, &if_tmp->addr.in6.sin6_addr,
|
||||
dnamebuff, MAXDNAME);
|
||||
die("no interface with address %s", dnamebuff);
|
||||
#else
|
||||
die("no interface with address %s", inet_ntoa(if_tmp->addr.in.sin_addr));
|
||||
#endif
|
||||
}
|
||||
|
||||
forward_init(1);
|
||||
cache_init(cachesize, options & OPT_LOG);
|
||||
|
||||
if (dhcp)
|
||||
{
|
||||
|
||||
#if !defined(HAVE_PF_PACKET) && !defined(HAVE_BPF)
|
||||
die("no DHCP support available on this OS.", NULL);
|
||||
#endif
|
||||
|
||||
for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
|
||||
if (!dhcp_tmp->iface)
|
||||
die("No suitable interface for DHCP service at address %s", inet_ntoa(dhcp_tmp->start));
|
||||
|
||||
leasefd = lease_init(lease_file, domain_suffix, dnamebuff, packet, time(NULL), dhcp_configs);
|
||||
lease_update_dns(1); /* must follow cache_init and lease_init */
|
||||
}
|
||||
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
if (!(options & OPT_DEBUG))
|
||||
{
|
||||
FILE *pidfile;
|
||||
struct passwd *ent_pw;
|
||||
int i;
|
||||
|
||||
/* The following code "daemonizes" the process.
|
||||
See Stevens section 12.4 */
|
||||
|
||||
#ifndef NO_FORK
|
||||
if (fork() != 0 )
|
||||
exit(0);
|
||||
|
||||
setsid();
|
||||
|
||||
if (fork() != 0)
|
||||
exit(0);
|
||||
#endif
|
||||
|
||||
chdir("/");
|
||||
umask(022); /* make pidfile 0644 */
|
||||
|
||||
/* write pidfile _after_ forking ! */
|
||||
if (runfile && (pidfile = fopen(runfile, "w")))
|
||||
{
|
||||
fprintf(pidfile, "%d\n", (int) getpid());
|
||||
fclose(pidfile);
|
||||
}
|
||||
|
||||
umask(0);
|
||||
|
||||
for (i=0; i<64; i++)
|
||||
{
|
||||
for (iface = interfaces; iface; iface = iface->next)
|
||||
if (iface->fd == i)
|
||||
break;
|
||||
if (iface)
|
||||
continue;
|
||||
|
||||
for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
|
||||
if (dhcp_tmp->fd == i || dhcp_tmp->rawfd == i)
|
||||
break;
|
||||
if (dhcp_tmp)
|
||||
continue;
|
||||
|
||||
if (dhcp && (i == leasefd))
|
||||
continue;
|
||||
|
||||
close(i);
|
||||
}
|
||||
|
||||
/* Change uid and gid for security */
|
||||
if (username && (ent_pw = getpwnam(username)))
|
||||
{
|
||||
gid_t dummy;
|
||||
struct group *gp;
|
||||
/* remove all supplimentary groups */
|
||||
setgroups(0, &dummy);
|
||||
/* change group for /etc/ppp/resolv.conf
|
||||
otherwise get the group for "nobody" */
|
||||
if ((groupname && (gp = getgrnam(groupname))) ||
|
||||
(gp = getgrgid(ent_pw->pw_gid)))
|
||||
setgid(gp->gr_gid);
|
||||
/* finally drop root */
|
||||
setuid(ent_pw->pw_uid);
|
||||
}
|
||||
}
|
||||
|
||||
openlog("dnsmasq",
|
||||
DNSMASQ_LOG_OPT(options & OPT_DEBUG),
|
||||
DNSMASQ_LOG_FAC(options & OPT_DEBUG));
|
||||
|
||||
if (cachesize)
|
||||
syslog(LOG_INFO, "started, version %s cachesize %d", VERSION, cachesize);
|
||||
else
|
||||
syslog(LOG_INFO, "started, version %s cache disabled", VERSION);
|
||||
|
||||
if (options & OPT_LOCALMX)
|
||||
syslog(LOG_INFO, "serving MX record for local hosts target %s", mxtarget);
|
||||
else if (mxname)
|
||||
syslog(LOG_INFO, "serving MX record for mailhost %s target %s",
|
||||
mxname, mxtarget);
|
||||
|
||||
for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
|
||||
{
|
||||
strcpy(dnamebuff, inet_ntoa(dhcp_tmp->start));
|
||||
if (dhcp_tmp->lease_time == 0)
|
||||
sprintf(packet, "infinite");
|
||||
else
|
||||
sprintf(packet, "%ds", (int)dhcp_tmp->lease_time);
|
||||
syslog(LOG_INFO, "DHCP on %s, IP range %s -- %s, lease time %s",
|
||||
dhcp_tmp->iface, dnamebuff, inet_ntoa(dhcp_tmp->end), packet);
|
||||
}
|
||||
|
||||
if (getuid() == 0 || geteuid() == 0)
|
||||
syslog(LOG_WARNING, "failed to drop root privs");
|
||||
|
||||
servers = last_server = check_servers(serv_addrs, interfaces, &sfds);
|
||||
|
||||
while (sigterm == 0)
|
||||
{
|
||||
fd_set rset;
|
||||
|
||||
if (sighup)
|
||||
{
|
||||
cache_reload(options, dnamebuff, domain_suffix, addn_hosts);
|
||||
lease_update_dns(1);
|
||||
if (resolv && (options & OPT_NO_POLL))
|
||||
servers = last_server =
|
||||
check_servers(reload_servers(resolv->name, dnamebuff, servers, query_port),
|
||||
interfaces, &sfds);
|
||||
sighup = 0;
|
||||
}
|
||||
|
||||
if (sigusr1)
|
||||
{
|
||||
dump_cache(options & (OPT_DEBUG | OPT_LOG), cachesize);
|
||||
sigusr1 = 0;
|
||||
}
|
||||
|
||||
if (sigusr2)
|
||||
{
|
||||
if (getuid() != 0 && port <= 1024)
|
||||
syslog(LOG_ERR, "cannot re-scan interfaces unless --user=root");
|
||||
else
|
||||
{
|
||||
syslog(LOG_INFO, "rescanning network interfaces");
|
||||
int_err_string = enumerate_interfaces(&interfaces, if_names, if_addrs, if_except, NULL, port);
|
||||
if (int_err_string)
|
||||
syslog(LOG_ERR, int_err_string, strerror(errno));
|
||||
}
|
||||
sigusr2 = 0;
|
||||
}
|
||||
|
||||
FD_ZERO(&rset);
|
||||
|
||||
if (!first_loop)
|
||||
{
|
||||
int maxfd = 0;
|
||||
|
||||
for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
{
|
||||
FD_SET(serverfdp->fd, &rset);
|
||||
if (serverfdp->fd > maxfd)
|
||||
maxfd = serverfdp->fd;
|
||||
}
|
||||
|
||||
for (iface = interfaces; iface; iface = iface->next)
|
||||
{
|
||||
FD_SET(iface->fd, &rset);
|
||||
if (iface->fd > maxfd)
|
||||
maxfd = iface->fd;
|
||||
}
|
||||
|
||||
for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
|
||||
{
|
||||
FD_SET(dhcp_tmp->fd, &rset);
|
||||
if (dhcp_tmp->fd > maxfd)
|
||||
maxfd = dhcp_tmp->fd;
|
||||
}
|
||||
#ifdef HAVE_PSELECT
|
||||
if (pselect(maxfd+1, &rset, NULL, NULL, NULL, &sigmask) < 0)
|
||||
FD_ZERO(&rset); /* rset otherwise undefined after error */
|
||||
#else
|
||||
{
|
||||
sigset_t save_mask;
|
||||
sigprocmask(SIG_SETMASK, &sigmask, &save_mask);
|
||||
if (select(maxfd+1, &rset, NULL, NULL, NULL) < 0)
|
||||
FD_ZERO(&rset); /* rset otherwise undefined after error */
|
||||
sigprocmask(SIG_SETMASK, &save_mask, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
first_loop = 0;
|
||||
now = time(NULL);
|
||||
|
||||
/* Check for changes to resolv files once per second max. */
|
||||
if (last == 0 || difftime(now, last) > 1.0)
|
||||
{
|
||||
last = now;
|
||||
if (!(options & OPT_NO_POLL))
|
||||
{
|
||||
struct resolvc *res = resolv, *latest = NULL;
|
||||
time_t last_change = 0;
|
||||
struct stat statbuf;
|
||||
/* 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. */
|
||||
while (res)
|
||||
{
|
||||
if (stat(res->name, &statbuf) == -1)
|
||||
{
|
||||
if (!res->logged)
|
||||
syslog(LOG_WARNING, "failed to access %s: %m", res->name);
|
||||
res->logged = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
res->logged = 0;
|
||||
if (statbuf.st_mtime > last_change)
|
||||
{
|
||||
last_change = statbuf.st_mtime;
|
||||
latest = res;
|
||||
}
|
||||
}
|
||||
res = res->next;
|
||||
}
|
||||
|
||||
if (latest && last_change > resolv_changed)
|
||||
{
|
||||
resolv_changed = last_change;
|
||||
servers = last_server =
|
||||
check_servers(reload_servers(latest->name, dnamebuff, servers, query_port),
|
||||
interfaces, &sfds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
if (FD_ISSET(serverfdp->fd, &rset))
|
||||
last_server = reply_query(serverfdp->fd, options, packet, now,
|
||||
dnamebuff, last_server, bogus_addr);
|
||||
|
||||
for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
|
||||
if (FD_ISSET(dhcp_tmp->fd, &rset))
|
||||
dhcp_packet(dhcp_tmp, packet, dhcp_options, dhcp_configs,
|
||||
now, dnamebuff, domain_suffix, dhcp_file,
|
||||
dhcp_sname, dhcp_next_server);
|
||||
|
||||
for (iface = interfaces; iface; iface = iface->next)
|
||||
{
|
||||
if (FD_ISSET(iface->fd, &rset))
|
||||
{
|
||||
/* request packet, deal with query */
|
||||
union mysockaddr udpaddr;
|
||||
socklen_t udplen = sizeof(udpaddr);
|
||||
HEADER *header = (HEADER *)packet;
|
||||
int m, n = recvfrom(iface->fd, packet, PACKETSZ, 0, &udpaddr.sa, &udplen);
|
||||
udpaddr.sa.sa_family = iface->addr.sa.sa_family;
|
||||
#ifdef HAVE_IPV6
|
||||
if (udpaddr.sa.sa_family == AF_INET6)
|
||||
udpaddr.in6.sin6_flowinfo = htonl(0);
|
||||
#endif
|
||||
if (n >= (int)sizeof(HEADER) && !header->qr)
|
||||
{
|
||||
if (extract_request(header, (unsigned int)n, dnamebuff))
|
||||
{
|
||||
if (udpaddr.sa.sa_family == AF_INET)
|
||||
log_query(F_QUERY | F_IPV4 | F_FORWARD, dnamebuff,
|
||||
(struct all_addr *)&udpaddr.in.sin_addr);
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
log_query(F_QUERY | F_IPV6 | F_FORWARD, dnamebuff,
|
||||
(struct all_addr *)&udpaddr.in6.sin6_addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n,
|
||||
mxname, mxtarget, options, now, local_ttl, dnamebuff);
|
||||
if (m >= 1)
|
||||
{
|
||||
/* answered from cache, send reply */
|
||||
sendto(iface->fd, (char *)header, m, 0,
|
||||
&udpaddr.sa, sa_len(&udpaddr));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* cannot answer from cache, send on to real nameserver */
|
||||
last_server = forward_query(iface->fd, &udpaddr, header, n,
|
||||
options, dnamebuff, servers,
|
||||
last_server, now, local_ttl);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "exiting on receipt of SIGTERM");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
380
src/dnsmasq.h
Normal file
380
src/dnsmasq.h
Normal file
@@ -0,0 +1,380 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
/* for pselect.... */
|
||||
#define _XOPEN_SOURCE 600
|
||||
/* but then DNS headers don't compile without.... */
|
||||
#define _BSD_SOURCE
|
||||
|
||||
/* get these before config.h for IPv6 stuff... */
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
/* get this before config.h too. */
|
||||
#include <syslog.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/nameser.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#if defined(__sun) || defined(__sun__)
|
||||
#include <sys/sockio.h>
|
||||
#endif
|
||||
#include <sys/time.h>
|
||||
#include <net/if.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
# include <getopt.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/ip.h>
|
||||
#ifdef HAVE_PF_PACKET
|
||||
#include <netpacket/packet.h>
|
||||
#endif
|
||||
#ifdef HAVE_BPF
|
||||
#include <net/bpf.h>
|
||||
#include <net/if_dl.h>
|
||||
#endif
|
||||
#include <sys/uio.h>
|
||||
|
||||
#define OPT_BOGUSPRIV 1
|
||||
#define OPT_FILTER 2
|
||||
#define OPT_LOG 4
|
||||
#define OPT_SELFMX 8
|
||||
#define OPT_NO_HOSTS 16
|
||||
#define OPT_NO_POLL 32
|
||||
#define OPT_DEBUG 64
|
||||
#define OPT_ORDER 128
|
||||
#define OPT_NO_RESOLV 256
|
||||
#define OPT_EXPAND 512
|
||||
#define OPT_LOCALMX 1024
|
||||
#define OPT_NO_NEG 2048
|
||||
#define OPT_NODOTS_LOCAL 4096
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
union bigname {
|
||||
char name[MAXDNAME];
|
||||
union bigname *next; /* freelist */
|
||||
};
|
||||
|
||||
struct crec {
|
||||
struct crec *next, *prev, *hash_next;
|
||||
time_t ttd; /* time to die */
|
||||
struct all_addr 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_ADDN 16384
|
||||
#define F_NOERR 32768
|
||||
|
||||
/* struct sockaddr is not large enough to hold any address,
|
||||
and specifically not big enough to hold and IPv6 address.
|
||||
Blech. Roll our own. */
|
||||
union mysockaddr {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_in in;
|
||||
#ifdef HAVE_BROKEN_SOCKADDR_IN6
|
||||
/* early versions of glibc don't include sin6_scope_id in sockaddr_in6
|
||||
but latest kernels _require_ it to be set. The choice is to have
|
||||
dnsmasq fail to compile on back-level libc or fail to run
|
||||
on latest kernels with IPv6. Or to do this: sorry that it's so gross. */
|
||||
struct my_sockaddr_in6 {
|
||||
sa_family_t sin6_family; /* AF_INET6 */
|
||||
uint16_t sin6_port; /* transport layer port # */
|
||||
uint32_t sin6_flowinfo; /* IPv6 traffic class & flow info */
|
||||
struct in6_addr sin6_addr; /* IPv6 address */
|
||||
uint32_t sin6_scope_id; /* set of interfaces for a scope */
|
||||
} in6;
|
||||
#elif 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_SOURCE 8 /* source address specified */
|
||||
#define SERV_HAS_DOMAIN 16 /* server for one domain only */
|
||||
#define SERV_FOR_NODOTS 32 /* server for names with no domain part only */
|
||||
#define SERV_TYPE (SERV_HAS_DOMAIN | SERV_FOR_NODOTS)
|
||||
|
||||
struct serverfd {
|
||||
int fd;
|
||||
union mysockaddr source_addr;
|
||||
struct serverfd *next;
|
||||
};
|
||||
|
||||
struct server {
|
||||
union mysockaddr addr, source_addr;
|
||||
struct serverfd *sfd; /* non-NULL if this server has its own fd bound to
|
||||
a source port */
|
||||
char *domain; /* set if this server only handles a domain. */
|
||||
int flags;
|
||||
struct server *next;
|
||||
};
|
||||
|
||||
/* linked list of all the interfaces in the system and
|
||||
the sockets we have bound to each one. */
|
||||
struct irec {
|
||||
union mysockaddr addr;
|
||||
int fd;
|
||||
int valid;
|
||||
struct irec *next;
|
||||
};
|
||||
|
||||
/* interface and address parms from command line. */
|
||||
struct iname {
|
||||
char *name;
|
||||
union mysockaddr addr;
|
||||
int found;
|
||||
struct iname *next;
|
||||
};
|
||||
|
||||
/* resolv-file parms from command-line */
|
||||
struct resolvc {
|
||||
struct resolvc *next;
|
||||
int is_default;
|
||||
int logged;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct frec {
|
||||
union mysockaddr source;
|
||||
struct server *sentto;
|
||||
unsigned short orig_id, new_id;
|
||||
int fd;
|
||||
time_t time;
|
||||
struct frec *next;
|
||||
};
|
||||
|
||||
struct dhcp_lease {
|
||||
int clid_len; /* length of client identifier */
|
||||
unsigned char *clid; /* clientid */
|
||||
char *hostname, *fqdn; /* name from client-hostname option or config */
|
||||
time_t expires; /* lease expiry */
|
||||
unsigned char hwaddr[ETHER_ADDR_LEN];
|
||||
struct in_addr addr;
|
||||
struct dhcp_lease *next;
|
||||
};
|
||||
|
||||
struct dhcp_config {
|
||||
int clid_len; /* length of client identifier */
|
||||
unsigned char *clid; /* clientid */
|
||||
unsigned char hwaddr[ETHER_ADDR_LEN];
|
||||
char *hostname;
|
||||
struct in_addr addr;
|
||||
unsigned int lease_time;
|
||||
struct dhcp_config *next;
|
||||
};
|
||||
|
||||
struct dhcp_opt {
|
||||
unsigned char opt;
|
||||
unsigned char len;
|
||||
unsigned char *val;
|
||||
struct dhcp_opt *next;
|
||||
};
|
||||
|
||||
struct dhcp_context {
|
||||
int fd, rawfd, ifindex;
|
||||
char *iface;
|
||||
unsigned char hwaddr[ETHER_ADDR_LEN];
|
||||
unsigned int lease_time;
|
||||
struct in_addr serv_addr, netmask, broadcast;
|
||||
struct in_addr start, end, last; /* range of available addresses */
|
||||
struct dhcp_context *next;
|
||||
};
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
|
||||
|
||||
struct udp_dhcp_packet {
|
||||
struct ip ip;
|
||||
struct udphdr {
|
||||
u16 uh_sport; /* source port */
|
||||
u16 uh_dport; /* destination port */
|
||||
u16 uh_ulen; /* udp length */
|
||||
u16 uh_sum; /* udp checksum */
|
||||
} udp;
|
||||
struct dhcp_packet {
|
||||
u8 op, htype, hlen, hops;
|
||||
u32 xid;
|
||||
u16 secs, flags;
|
||||
struct in_addr ciaddr, yiaddr, siaddr, giaddr;
|
||||
u8 chaddr[16], sname[64], file[128];
|
||||
u32 cookie;
|
||||
u8 options[308];
|
||||
} data;
|
||||
};
|
||||
|
||||
|
||||
/* cache.c */
|
||||
void cache_init(int cachesize, int log);
|
||||
void log_query(unsigned short flags, char *name, struct all_addr *addr);
|
||||
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);
|
||||
void cache_insert(char *name, struct all_addr *addr,
|
||||
time_t now, unsigned long ttl, unsigned short flags);
|
||||
void cache_reload(int opts, char *buff, char *domain_suffix, char *addn_hosts);
|
||||
void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address,
|
||||
time_t ttd, unsigned short flags);
|
||||
void cache_unhash_dhcp(void);
|
||||
void dump_cache(int debug, int size);
|
||||
char *cache_get_name(struct crec *crecp);
|
||||
|
||||
/* rfc1035.c */
|
||||
unsigned short extract_request(HEADER *header, unsigned int qlen, char *name);
|
||||
int setup_reply(HEADER *header, unsigned int qlen,
|
||||
struct all_addr *addrp, unsigned short flags,
|
||||
unsigned long local_ttl);
|
||||
void extract_addresses(HEADER *header, unsigned int qlen, char *namebuff, time_t now);
|
||||
void extract_neg_addrs(HEADER *header, unsigned int qlen, char *namebuff, time_t now);
|
||||
int answer_request(HEADER *header, char *limit, unsigned int qlen, char *mxname,
|
||||
char *mxtarget, unsigned int options, time_t now, unsigned long local_ttl,
|
||||
char *namebuff);
|
||||
int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
|
||||
struct bogus_addr *addr, time_t now);
|
||||
|
||||
/* util.c */
|
||||
unsigned short rand16(void);
|
||||
int legal_char(char c);
|
||||
int canonicalise(char *s);
|
||||
void die(char *message, char *arg1);
|
||||
void complain(char *message, char *arg1);
|
||||
void *safe_malloc(int size);
|
||||
char *safe_string_alloc(char *cp);
|
||||
int sa_len(union mysockaddr *addr);
|
||||
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
|
||||
int hostname_isequal(unsigned char *a, unsigned char *b);
|
||||
|
||||
/* option.c */
|
||||
unsigned int read_opts(int argc, char **argv, char *buff, struct resolvc **resolv_file,
|
||||
char **mxname, char **mxtarget, char **lease_file,
|
||||
char **username, char **groupname,
|
||||
char **domain_suffix, char **runfile,
|
||||
struct iname **if_names, struct iname **if_addrs, struct iname **if_except,
|
||||
struct bogus_addr **bogus_addr, struct server **serv_addrs, int *cachesize,
|
||||
int *port, int *query_port, unsigned long *local_ttl, char **addn_hosts,
|
||||
struct dhcp_context **dhcp, struct dhcp_config **dhcp_conf, struct dhcp_opt **opts,
|
||||
char **dhcp_file, char **dhcp_sname, struct in_addr *dhcp_next_server);
|
||||
|
||||
/* forward.c */
|
||||
void forward_init(int first);
|
||||
void reap_forward(int fd);
|
||||
struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *header,
|
||||
int plen, unsigned int options, char *dnamebuff,
|
||||
struct server *servers, struct server *last_server,
|
||||
time_t now, unsigned long local_ttl);
|
||||
struct server *reply_query(int fd, int options, char *packet, time_t now,
|
||||
char *dnamebuff, struct server *last_server,
|
||||
struct bogus_addr *bogus_nxdomain);
|
||||
|
||||
/* network.c */
|
||||
struct server *reload_servers(char *fname, char *buff, struct server *servers, int query_port);
|
||||
struct server *check_servers(struct server *new, struct irec *interfaces, struct serverfd **sfds);
|
||||
char *enumerate_interfaces(struct irec **interfaces,
|
||||
struct iname *names,
|
||||
struct iname *addrs,
|
||||
struct iname *except,
|
||||
struct dhcp_context *dhcp,
|
||||
int port);
|
||||
|
||||
/* dhcp.c */
|
||||
void dhcp_packet(struct dhcp_context *context, char *packet,
|
||||
struct dhcp_opt *dhcp_opts,
|
||||
struct dhcp_config *dhcp_configs,
|
||||
time_t now, char *namebuff, char *domain_suffix,
|
||||
char *dhcp_file, char *dhcp_sname, struct in_addr dhcp_next_server);
|
||||
int address_available(struct dhcp_context *context, struct in_addr addr);
|
||||
int address_allocate(struct dhcp_context *context, struct dhcp_config *configs,
|
||||
struct in_addr *addrp);
|
||||
struct dhcp_config *find_config(struct dhcp_config *configs,
|
||||
struct dhcp_context *context,
|
||||
unsigned char *clid, int clid_len,
|
||||
unsigned char *hwaddr, char *hostname);
|
||||
|
||||
/* lease.c */
|
||||
void lease_update_dns(int force_dns);
|
||||
int lease_init(char *lease_file, char *domain, char *buff,
|
||||
char *buff2, time_t now, struct dhcp_config *dhcp_configs);
|
||||
struct dhcp_lease *lease_allocate(unsigned char *clid, int clid_len, struct in_addr addr);
|
||||
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr);
|
||||
void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix);
|
||||
void lease_set_expires(struct dhcp_lease *lease, time_t exp);
|
||||
struct dhcp_lease *lease_find_by_client(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);
|
||||
|
||||
/* rfc2131.c */
|
||||
int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
|
||||
unsigned int sz, time_t now, char *namebuff,
|
||||
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
|
||||
char *domain_suffix, char *dhcp_file, char *dhcp_sname,
|
||||
struct in_addr dhcp_next_server);
|
||||
382
src/forward.c
Normal file
382
src/forward.c
Normal file
@@ -0,0 +1,382 @@
|
||||
/* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static struct frec *frec_list;
|
||||
|
||||
static struct frec *get_new_frec(time_t now);
|
||||
static struct frec *lookup_frec(unsigned short id);
|
||||
static struct frec *lookup_frec_by_sender(unsigned short id,
|
||||
union mysockaddr *addr);
|
||||
static unsigned short get_id(void);
|
||||
|
||||
/* May be called more than once. */
|
||||
void forward_init(int first)
|
||||
{
|
||||
struct frec *f;
|
||||
|
||||
if (first)
|
||||
frec_list = NULL;
|
||||
for (f = frec_list; f; f = f->next)
|
||||
f->new_id = 0;
|
||||
}
|
||||
|
||||
/* delete all forward records recieved from socket fd */
|
||||
void reap_forward(int fd)
|
||||
{
|
||||
struct frec *f;
|
||||
|
||||
for (f = frec_list; f; f = f->next)
|
||||
if (f->fd == fd)
|
||||
f->new_id = 0;
|
||||
}
|
||||
|
||||
/* returns new last_server */
|
||||
struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *header,
|
||||
int plen, unsigned int options, char *dnamebuff,
|
||||
struct server *servers, struct server *last_server,
|
||||
time_t now, unsigned long local_ttl)
|
||||
{
|
||||
struct frec *forward;
|
||||
char *domain = NULL;
|
||||
int type = 0;
|
||||
struct server *serv;
|
||||
struct all_addr *addrp = NULL;
|
||||
unsigned short flags = 0;
|
||||
unsigned short gotname = extract_request(header, (unsigned int)plen, dnamebuff);
|
||||
|
||||
/* may be recursion not speced or no servers available. */
|
||||
if (!header->rd || !servers)
|
||||
forward = NULL;
|
||||
else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr)))
|
||||
{
|
||||
/* retry on existing query, send to next server */
|
||||
domain = forward->sentto->domain;
|
||||
type = forward->sentto->flags & SERV_TYPE;
|
||||
if (!(forward->sentto = forward->sentto->next))
|
||||
forward->sentto = servers; /* at end of list, recycle */
|
||||
header->id = htons(forward->new_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gotname)
|
||||
{
|
||||
/* 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(dnamebuff);
|
||||
unsigned int matchlen = 0;
|
||||
|
||||
for (serv=servers; serv; serv=serv->next)
|
||||
/* domain matches take priority over NODOTS matches */
|
||||
if ((serv->flags & SERV_FOR_NODOTS) && type != SERV_HAS_DOMAIN && !strchr(dnamebuff, '.'))
|
||||
{
|
||||
if (serv->flags & SERV_LITERAL_ADDRESS)
|
||||
{
|
||||
/* flags gets set if server is in fact an answer */
|
||||
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
|
||||
if (sflag & gotname) /* only OK if addrfamily == query */
|
||||
{
|
||||
type = SERV_FOR_NODOTS;
|
||||
flags = sflag;
|
||||
if (serv->addr.sa.sa_family == AF_INET)
|
||||
addrp = (struct all_addr *)&serv->addr.in.sin_addr;
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
flags = 0;
|
||||
}
|
||||
else if (serv->flags & SERV_HAS_DOMAIN)
|
||||
{
|
||||
unsigned int domainlen = strlen(serv->domain);
|
||||
if (namelen >= domainlen &&
|
||||
hostname_isequal(dnamebuff + namelen - domainlen, serv->domain) &&
|
||||
domainlen > matchlen)
|
||||
{
|
||||
if (serv->flags & SERV_LITERAL_ADDRESS)
|
||||
{ /* flags gets set if server is in fact an answer */
|
||||
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
|
||||
if (sflag & gotname) /* only OK if addrfamily == query */
|
||||
{
|
||||
type = SERV_HAS_DOMAIN;
|
||||
flags = sflag;
|
||||
domain = serv->domain;
|
||||
matchlen = domainlen;
|
||||
if (serv->addr.sa.sa_family == AF_INET)
|
||||
addrp = (struct all_addr *)&serv->addr.in.sin_addr;
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
flags = 0; /* may be better match from previous literal */
|
||||
domain = serv->domain;
|
||||
matchlen = domainlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flags) /* flags set here means a literal found */
|
||||
log_query(F_CONFIG | F_FORWARD | flags, dnamebuff, addrp);
|
||||
else
|
||||
{
|
||||
/* we may by policy not forward names without a domain part */
|
||||
if (gotname && (options & OPT_NODOTS_LOCAL) && !strchr(dnamebuff, '.'))
|
||||
flags = F_NXDOMAIN;
|
||||
else if (!(forward = get_new_frec(now)))
|
||||
/* table full - server failure. */
|
||||
flags = F_NEG;
|
||||
}
|
||||
|
||||
if (forward)
|
||||
{
|
||||
/* 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 || (options & OPT_ORDER))
|
||||
forward->sentto = servers;
|
||||
else
|
||||
forward->sentto = last_server;
|
||||
|
||||
forward->source = *udpaddr;
|
||||
forward->new_id = get_id();
|
||||
forward->fd = udpfd;
|
||||
forward->orig_id = ntohs(header->id);
|
||||
header->id = htons(forward->new_id);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 = forward->sentto;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int logflags = 0;
|
||||
|
||||
if (forward->sentto->addr.sa.sa_family == AF_INET)
|
||||
{
|
||||
logflags = F_SERVER | F_IPV4 | F_FORWARD;
|
||||
addrp = (struct all_addr *)&forward->sentto->addr.in.sin_addr;
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
{
|
||||
logflags = F_SERVER | F_IPV6 | F_FORWARD;
|
||||
addrp = (struct all_addr *)&forward->sentto->addr.in6.sin6_addr;
|
||||
}
|
||||
#endif
|
||||
/* only send to servers dealing with our domain.
|
||||
domain may be NULL, in which case server->domain
|
||||
must be NULL also. */
|
||||
|
||||
if (type == (forward->sentto->flags & SERV_TYPE) &&
|
||||
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, forward->sentto->domain)))
|
||||
{
|
||||
if (forward->sentto->flags & SERV_NO_ADDR)
|
||||
flags = F_NOERR; /* NULL servers are OK. */
|
||||
else if (!(forward->sentto->flags & SERV_LITERAL_ADDRESS) &&
|
||||
sendto(forward->sentto->sfd->fd, (char *)header, plen, 0,
|
||||
&forward->sentto->addr.sa,
|
||||
sa_len(&forward->sentto->addr)) != -1)
|
||||
{
|
||||
log_query(logflags, gotname ? dnamebuff : "query", addrp);
|
||||
/* for no-domain, don't update last_server */
|
||||
return domain ? last_server : (forward->sentto->next ? forward->sentto->next : servers);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(forward->sentto = forward->sentto->next))
|
||||
forward->sentto = servers;
|
||||
|
||||
/* check if we tried all without success */
|
||||
if (forward->sentto == firstsentto)
|
||||
break;
|
||||
}
|
||||
|
||||
/* could not send on, prepare to return */
|
||||
header->id = htons(forward->orig_id);
|
||||
forward->new_id = 0; /* cancel */
|
||||
}
|
||||
|
||||
/* could not send on, return empty answer or address if known for whole domain */
|
||||
plen = setup_reply(header, (unsigned int)plen, addrp, flags, local_ttl);
|
||||
sendto(udpfd, (char *)header, plen, 0, &udpaddr->sa, sa_len(udpaddr));
|
||||
|
||||
if (flags & (F_NOERR | F_NXDOMAIN))
|
||||
log_query(F_CONFIG | F_FORWARD | F_NEG | gotname | (flags & F_NXDOMAIN), dnamebuff, NULL);
|
||||
|
||||
return last_server;
|
||||
}
|
||||
|
||||
/* returns new last_server */
|
||||
struct server *reply_query(int fd, int options, char *packet, time_t now,
|
||||
char *dnamebuff, struct server *last_server, struct bogus_addr *bogus_nxdomain)
|
||||
{
|
||||
/* packet from peer server, extract data for cache, and send to
|
||||
original requester */
|
||||
struct frec *forward;
|
||||
HEADER *header;
|
||||
int n = recv(fd, packet, PACKETSZ, 0);
|
||||
|
||||
header = (HEADER *)packet;
|
||||
if (n >= (int)sizeof(HEADER) && header->qr)
|
||||
{
|
||||
if ((forward = lookup_frec(ntohs(header->id))))
|
||||
{
|
||||
if (header->rcode == NOERROR || header->rcode == NXDOMAIN)
|
||||
{
|
||||
if (!forward->sentto->domain)
|
||||
last_server = forward->sentto; /* known good */
|
||||
if (header->opcode == QUERY)
|
||||
{
|
||||
if (!(bogus_nxdomain &&
|
||||
header->rcode == NOERROR &&
|
||||
check_for_bogus_wildcard(header, (unsigned int)n, dnamebuff, bogus_nxdomain, now)))
|
||||
{
|
||||
if (header->rcode == NOERROR && ntohs(header->ancount) != 0)
|
||||
extract_addresses(header, (unsigned int)n, dnamebuff, now);
|
||||
else if (!(options & OPT_NO_NEG))
|
||||
extract_neg_addrs(header, (unsigned int)n, dnamebuff, now);
|
||||
}
|
||||
}
|
||||
}
|
||||
header->id = htons(forward->orig_id);
|
||||
/* There's no point returning an upstream reply marked as truncated,
|
||||
since that will prod the resolver into moving to TCP - which we
|
||||
don't support. */
|
||||
header->tc = 0; /* goodbye truncate */
|
||||
sendto(forward->fd, packet, n, 0,
|
||||
&forward->source.sa, sa_len(&forward->source));
|
||||
forward->new_id = 0; /* cancel */
|
||||
}
|
||||
}
|
||||
|
||||
return last_server;
|
||||
}
|
||||
|
||||
static struct frec *get_new_frec(time_t now)
|
||||
{
|
||||
struct frec *f = frec_list, *oldest = NULL;
|
||||
time_t oldtime = now;
|
||||
int count = 0;
|
||||
static time_t warntime = 0;
|
||||
|
||||
while (f)
|
||||
{
|
||||
if (f->new_id == 0)
|
||||
{
|
||||
f->time = now;
|
||||
return f;
|
||||
}
|
||||
|
||||
if (difftime(f->time, oldtime) <= 0)
|
||||
{
|
||||
oldtime = f->time;
|
||||
oldest = f;
|
||||
}
|
||||
|
||||
count++;
|
||||
f = f->next;
|
||||
}
|
||||
|
||||
/* can't find empty one, use oldest if there is one
|
||||
and it's older than timeout */
|
||||
if (oldest && difftime(now, oldtime) > TIMEOUT)
|
||||
{
|
||||
oldest->time = now;
|
||||
return oldest;
|
||||
}
|
||||
|
||||
if (count > FTABSIZ)
|
||||
{ /* limit logging rate so syslog isn't DOSed either */
|
||||
if (!warntime || difftime(now, warntime) > LOGRATE)
|
||||
{
|
||||
warntime = now;
|
||||
syslog(LOG_WARNING, "forwarding table overflow: check for server loops.");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((f = (struct frec *)malloc(sizeof(struct frec))))
|
||||
{
|
||||
f->next = frec_list;
|
||||
f->time = now;
|
||||
frec_list = f;
|
||||
}
|
||||
return f; /* OK if malloc fails and this is NULL */
|
||||
}
|
||||
|
||||
static struct frec *lookup_frec(unsigned short id)
|
||||
{
|
||||
struct frec *f;
|
||||
|
||||
for(f = frec_list; f; f = f->next)
|
||||
if (f->new_id == id)
|
||||
return f;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct frec *lookup_frec_by_sender(unsigned short id,
|
||||
union mysockaddr *addr)
|
||||
{
|
||||
struct frec *f;
|
||||
|
||||
for(f = frec_list; f; f = f->next)
|
||||
if (f->new_id &&
|
||||
f->orig_id == id &&
|
||||
sockaddr_isequal(&f->source, addr))
|
||||
return f;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* return unique random ids between 1 and 65535 */
|
||||
static unsigned short get_id(void)
|
||||
{
|
||||
unsigned short ret = 0;
|
||||
|
||||
while (ret == 0)
|
||||
{
|
||||
ret = rand16();
|
||||
|
||||
/* scrap ids already in use */
|
||||
if ((ret != 0) && lookup_frec(ret))
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
323
src/lease.c
Normal file
323
src/lease.c
Normal file
@@ -0,0 +1,323 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static struct dhcp_lease *leases;
|
||||
FILE *lease_file;
|
||||
int dns_dirty, file_dirty;
|
||||
|
||||
int lease_init(char *filename, char *domain, char *buff,
|
||||
char *buff2, time_t now, struct dhcp_config *dhcp_configs)
|
||||
{
|
||||
unsigned int e0, e1, e2, e3, e4, e5, a0, a1, a2, a3;
|
||||
unsigned long ei;
|
||||
time_t expires;
|
||||
unsigned char hwaddr[ETHER_ADDR_LEN];
|
||||
struct in_addr addr;
|
||||
struct dhcp_lease *lease;
|
||||
struct dhcp_config *config;
|
||||
int clid_len = 0;
|
||||
int has_old = 0;
|
||||
|
||||
leases = NULL;
|
||||
|
||||
if (!(lease_file = fopen(filename, "a+")))
|
||||
die("cannot open or create leases file: %s", NULL);
|
||||
|
||||
rewind(lease_file); /* file opened with mode a+ which sets pointer at end. */
|
||||
|
||||
while (fscanf(lease_file, "%lu %x:%x:%x:%x:%x:%x %d.%d.%d.%d %256s %500s",
|
||||
&ei, &e0, &e1, &e2, &e3, &e4, &e5, &a0, &a1, &a2, &a3, buff, buff2) == 13)
|
||||
{
|
||||
/* strictly time_t is opaque, but this hack should work on all sane systems,
|
||||
even when sizeof(time_t) == 8 */
|
||||
expires = (time_t)ei;
|
||||
|
||||
if (ei != 0 && difftime(now, expires) > 0)
|
||||
{
|
||||
has_old = 1;
|
||||
continue; /* expired */
|
||||
}
|
||||
|
||||
hwaddr[0] = e0;
|
||||
hwaddr[1] = e1;
|
||||
hwaddr[2] = e2;
|
||||
hwaddr[3] = e3;
|
||||
hwaddr[4] = e4;
|
||||
hwaddr[5] = e5;
|
||||
|
||||
addr.s_addr = htonl((a0<<24) + (a1<<16) + (a2<<8) + a3);
|
||||
|
||||
/* decode hex in place */
|
||||
if (strcmp(buff2, "*") == 0)
|
||||
clid_len = 0;
|
||||
else
|
||||
{
|
||||
int s = (strlen(buff2)/3) + 1;
|
||||
for (clid_len = 0; clid_len < s; clid_len++)
|
||||
{
|
||||
buff2[(clid_len*3)+2] = 0;
|
||||
buff2[clid_len] = strtol(&buff2[clid_len*3], NULL, 16);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(lease = lease_allocate(buff2, clid_len, addr)))
|
||||
die ("cannot get memory", NULL);
|
||||
|
||||
lease->expires = expires;
|
||||
memcpy(lease->hwaddr, hwaddr, ETHER_ADDR_LEN);
|
||||
|
||||
if (strcmp(buff, "*") != 0)
|
||||
lease_set_hostname(lease, buff, domain);
|
||||
}
|
||||
|
||||
dns_dirty = 1;
|
||||
file_dirty = has_old;
|
||||
|
||||
/* Deal with edits to the config file which may have changed the hostname
|
||||
associated with a hardware address. Do this after the main loop so that
|
||||
changes get written back out */
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
if ((config = find_config(dhcp_configs, NULL, lease->clid, lease->clid_len, lease->hwaddr, NULL)) &&
|
||||
(config->hostname))
|
||||
lease_set_hostname(lease, config->hostname, domain);
|
||||
|
||||
return fileno(lease_file);
|
||||
}
|
||||
|
||||
void lease_update_dns(int force_dns)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
int i;
|
||||
|
||||
if (file_dirty)
|
||||
{
|
||||
rewind(lease_file);
|
||||
ftruncate(fileno(lease_file), 0);
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
{
|
||||
fprintf(lease_file, "%lu %.2x:%.2x:%.2x:%.2x:%.2x:%.2x %s %s ",
|
||||
(unsigned long)lease->expires, lease->hwaddr[0], lease->hwaddr[1],
|
||||
lease->hwaddr[2], lease->hwaddr[3], lease->hwaddr[4],
|
||||
lease->hwaddr[5], inet_ntoa(lease->addr),
|
||||
lease->hostname ? lease->hostname : "*");
|
||||
|
||||
if (lease->clid_len)
|
||||
{
|
||||
for (i = 0; i < lease->clid_len - 1; i++)
|
||||
fprintf(lease_file, "%.2x:", lease->clid[i]);
|
||||
fprintf(lease_file, "%.2x\n", lease->clid[i]);
|
||||
}
|
||||
else
|
||||
fprintf(lease_file, "*\n");
|
||||
|
||||
}
|
||||
|
||||
fflush(lease_file);
|
||||
fsync(fileno(lease_file));
|
||||
file_dirty = 0;
|
||||
|
||||
}
|
||||
|
||||
if (dns_dirty || force_dns)
|
||||
{
|
||||
cache_unhash_dhcp();
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
{
|
||||
if (lease->fqdn)
|
||||
{
|
||||
cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires, F_REVERSE);
|
||||
cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires, 0);
|
||||
}
|
||||
else if (lease->hostname)
|
||||
cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires, F_REVERSE);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
*up = lease->next; /* unlink */
|
||||
if (lease->hostname)
|
||||
{
|
||||
free(lease->hostname);
|
||||
dns_dirty = 1;
|
||||
}
|
||||
if (lease->fqdn)
|
||||
free(lease->fqdn);
|
||||
if (lease->clid)
|
||||
free(lease->clid);
|
||||
free(lease);
|
||||
}
|
||||
else
|
||||
up = &lease->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct dhcp_lease *lease_find_by_client(unsigned char *clid, int clid_len)
|
||||
{
|
||||
/* zero length means clid from hwaddr: never match am option clid to
|
||||
a hardware-address derived clid */
|
||||
|
||||
struct dhcp_lease *lease;
|
||||
|
||||
if (clid_len)
|
||||
{
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
if (lease->clid && clid_len == lease->clid_len &&
|
||||
memcmp(clid, lease->clid, clid_len) == 0)
|
||||
return lease;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
if (!lease->clid &&
|
||||
memcmp(clid, lease->hwaddr, ETHER_ADDR_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(unsigned char *clid, int clid_len, struct in_addr addr)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
if (!(lease = malloc(sizeof(struct dhcp_lease))))
|
||||
return NULL;
|
||||
|
||||
lease->clid = NULL;
|
||||
lease->clid_len = clid_len;
|
||||
|
||||
if (clid_len)
|
||||
{
|
||||
if (!(lease->clid = malloc(clid_len)))
|
||||
{
|
||||
free(lease);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(lease->clid, clid, clid_len);
|
||||
}
|
||||
|
||||
lease->hostname = lease->fqdn = NULL;
|
||||
lease->addr = addr;
|
||||
memset(lease->hwaddr, 0, ETHER_ADDR_LEN);
|
||||
lease->expires = 1;
|
||||
|
||||
lease->next = leases;
|
||||
leases = lease;
|
||||
|
||||
file_dirty = 1;
|
||||
|
||||
return lease;
|
||||
}
|
||||
|
||||
void lease_set_expires(struct dhcp_lease *lease, time_t exp)
|
||||
{
|
||||
if (exp != lease->expires)
|
||||
file_dirty = dns_dirty = 1;
|
||||
|
||||
lease->expires = exp;
|
||||
}
|
||||
|
||||
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr)
|
||||
{
|
||||
if (memcmp(lease->hwaddr, hwaddr, ETHER_ADDR_LEN) != 0)
|
||||
{
|
||||
file_dirty = 1;
|
||||
memcpy(lease->hwaddr, hwaddr, ETHER_ADDR_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix)
|
||||
{
|
||||
struct dhcp_lease *lease_tmp;
|
||||
char *new_name = NULL, *new_fqdn = NULL;
|
||||
|
||||
if (lease->hostname && name && strcmp(lease->hostname, name) == 0)
|
||||
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. */
|
||||
|
||||
if (name)
|
||||
{
|
||||
for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next)
|
||||
if (lease_tmp->hostname && hostname_isequal(lease_tmp->hostname, name))
|
||||
{
|
||||
new_name = lease_tmp->hostname;
|
||||
lease_tmp->hostname = NULL;
|
||||
if (lease_tmp->fqdn)
|
||||
{
|
||||
new_fqdn = lease_tmp->fqdn;
|
||||
lease_tmp->fqdn = NULL;
|
||||
}
|
||||
}
|
||||
if (!new_name)
|
||||
{
|
||||
new_name = malloc(strlen(name) + 1);
|
||||
strcpy(new_name, name);
|
||||
}
|
||||
if (suffix && !new_fqdn)
|
||||
{
|
||||
new_fqdn = malloc(strlen(name) + strlen(suffix) + 2);
|
||||
strcpy(new_fqdn, name);
|
||||
strcat(new_fqdn, ".");
|
||||
strcat(new_fqdn, suffix);
|
||||
}
|
||||
}
|
||||
|
||||
if (lease->hostname)
|
||||
free(lease->hostname);
|
||||
if (lease->fqdn)
|
||||
free(lease->fqdn);
|
||||
|
||||
lease->hostname = new_name;
|
||||
lease->fqdn = new_fqdn;
|
||||
|
||||
file_dirty = dns_dirty = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
625
src/network.c
Normal file
625
src/network.c
Normal file
@@ -0,0 +1,625 @@
|
||||
/* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static char *add_iface(struct irec **list, unsigned int flags,
|
||||
char *name, union mysockaddr *addr,
|
||||
struct iname *names, struct iname *addrs,
|
||||
struct iname *except)
|
||||
{
|
||||
struct irec *iface;
|
||||
int fd, opt;
|
||||
struct iname *tmp;
|
||||
|
||||
/* we may need to check the whitelist */
|
||||
if (names)
|
||||
{
|
||||
for (tmp = names; tmp; tmp = tmp->next)
|
||||
if (tmp->name && (strcmp(tmp->name, name) == 0))
|
||||
{
|
||||
tmp->found = 1;
|
||||
break;
|
||||
}
|
||||
if (!(flags & IFF_LOOPBACK) && !tmp)
|
||||
/* not on whitelist and not loopback */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (addrs)
|
||||
{
|
||||
for (tmp = addrs; tmp; tmp = tmp->next)
|
||||
if (sockaddr_isequal(&tmp->addr, addr))
|
||||
{
|
||||
tmp->found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!tmp)
|
||||
/* not on whitelist */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* check blacklist */
|
||||
if (except)
|
||||
for (tmp = except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && strcmp(tmp->name, name) == 0)
|
||||
return NULL;
|
||||
|
||||
/* check whether the interface IP has been added already
|
||||
it is possible to have multiple interfaces with the same address
|
||||
and we may be re-scanning. */
|
||||
for (iface = *list; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&iface->addr, addr))
|
||||
break;
|
||||
if (iface)
|
||||
{
|
||||
iface->valid = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0)) == -1)
|
||||
return "failed to create socket: %s";
|
||||
|
||||
/* Set SO_REUSEADDR on the socket, this allows is to bind
|
||||
specific addresses even if BIND is running and has bound *:53 */
|
||||
opt = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
|
||||
bind(fd, &addr->sa, sa_len(addr)))
|
||||
{
|
||||
int errsave = errno;
|
||||
close(fd);
|
||||
errno = errsave;
|
||||
return "failed to bind socket: %s";
|
||||
}
|
||||
|
||||
/* If OK, add it to the head of the list */
|
||||
if (!(iface = malloc(sizeof(struct irec))))
|
||||
{
|
||||
close(fd);
|
||||
return "cannot allocate interface";
|
||||
}
|
||||
|
||||
iface->fd = fd;
|
||||
iface->addr = *addr;
|
||||
iface->next = *list;
|
||||
iface->valid = 1;
|
||||
*list = iface;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* get all interfaces in system and for each one allowed add it to the chain
|
||||
at interfacep. May be called more that once: interfaces which still exist
|
||||
are left on the chain, those which have gone have sockets close()ed an are
|
||||
unlinked. Return value is NULL if OK, an error string and the value of errno
|
||||
on error. */
|
||||
char *enumerate_interfaces(struct irec **interfacep,
|
||||
struct iname *names,
|
||||
struct iname *addrs,
|
||||
struct iname *except,
|
||||
struct dhcp_context *dhcp,
|
||||
int port)
|
||||
{
|
||||
/* this code is adapted from Stevens, page 434. It finally
|
||||
destroyed my faith in the C/unix API */
|
||||
int len = 100 * sizeof(struct ifreq);
|
||||
int errsave, lastlen = 0;
|
||||
struct irec *iface, *prev;
|
||||
char *buf, *ptr, *err = NULL;
|
||||
struct ifconf ifc;
|
||||
int fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
int rawfd = -1;
|
||||
|
||||
if (fd == -1)
|
||||
return "cannot create socket to enumerate interfaces: %s";
|
||||
|
||||
/* make all interfaces as old. Any left that way after the scan are reaped. */
|
||||
for (iface = *interfacep; iface; iface = iface->next)
|
||||
iface->valid = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (!(buf = malloc(len)))
|
||||
{
|
||||
err = "cannot allocate buffer";
|
||||
goto end;
|
||||
}
|
||||
ifc.ifc_len = len;
|
||||
ifc.ifc_buf = buf;
|
||||
if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
|
||||
{
|
||||
if (errno != EINVAL || lastlen != 0)
|
||||
{
|
||||
err = "ioctl error while enumerating interfaces: %s";
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ifc.ifc_len == lastlen)
|
||||
break; /* got a big enough buffer now */
|
||||
lastlen = ifc.ifc_len;
|
||||
}
|
||||
len += 10*sizeof(struct ifreq);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
for (ptr = buf; ptr < buf + ifc.ifc_len; )
|
||||
{
|
||||
struct ifreq *ifr = (struct ifreq *) ptr;
|
||||
union mysockaddr addr;
|
||||
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
ptr += ifr->ifr_addr.sa_len + IF_NAMESIZE;
|
||||
#else
|
||||
ptr += sizeof(struct ifreq);
|
||||
#endif
|
||||
|
||||
/* copy address since getting flags overwrites */
|
||||
if (ifr->ifr_addr.sa_family == AF_INET)
|
||||
{
|
||||
addr.in = *((struct sockaddr_in *) &ifr->ifr_addr);
|
||||
addr.in.sin_port = htons(port);
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
else if (ifr->ifr_addr.sa_family == AF_INET6)
|
||||
{
|
||||
#ifdef HAVE_BROKEN_SOCKADDR_IN6
|
||||
addr.in6 = *((struct my_sockaddr_in6 *) &ifr->ifr_addr);
|
||||
#else
|
||||
addr.in6 = *((struct sockaddr_in6 *) &ifr->ifr_addr);
|
||||
#endif
|
||||
addr.in6.sin6_port = htons(port);
|
||||
addr.in6.sin6_flowinfo = htonl(0);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
continue; /* unknown address family */
|
||||
|
||||
if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0)
|
||||
{
|
||||
err = "ioctl error getting interface flags: %m";
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((err = add_iface(interfacep, ifr->ifr_flags, ifr->ifr_name,
|
||||
&addr, names, addrs, except)))
|
||||
goto end;
|
||||
|
||||
/* dhcp is non-null only on the first call: set up the relevant
|
||||
interface-related DHCP stuff here. DHCP is IPv4 only.
|
||||
Because errors here are ultimately fatal we can return directly and not bother
|
||||
closing the descriptor.
|
||||
*/
|
||||
if (dhcp && addr.sa.sa_family == AF_INET &&
|
||||
!(ifr->ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)))
|
||||
{
|
||||
struct in_addr netmask, broadcast;
|
||||
struct dhcp_context *context;
|
||||
int opt = 1;
|
||||
|
||||
if (ioctl(fd, SIOCGIFNETMASK, ifr) < 0)
|
||||
return "ioctl error getting interface netmask: %s";
|
||||
|
||||
netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
|
||||
|
||||
if (ioctl(fd, SIOCGIFBRDADDR, ifr) < 0)
|
||||
return "ioctl error getting interface broadcast address: %s";
|
||||
|
||||
broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
|
||||
|
||||
for (context = dhcp; context; context = context->next)
|
||||
if (!context->iface && /* may be more than one iface with same addr */
|
||||
((addr.in.sin_addr.s_addr & netmask.s_addr) == (context->start.s_addr & netmask.s_addr)) &&
|
||||
((addr.in.sin_addr.s_addr & netmask.s_addr) == (context->end.s_addr & netmask.s_addr)))
|
||||
{
|
||||
struct sockaddr_in saddr;
|
||||
#ifdef HAVE_BPF
|
||||
char filename[50];
|
||||
int b = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
sprintf(filename, "/dev/bpf%d", b);
|
||||
if ((rawfd = open(filename, O_RDWR, 0)) == -1)
|
||||
{
|
||||
if (errno != EBUSY)
|
||||
return"Cannot create DHCP BPF socket: %s";
|
||||
b++;
|
||||
}
|
||||
else if (ioctl(rawfd, BIOCSETIF, ifr) < 0)
|
||||
return "Can't attach interface to BPF device: %s";
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (context->next)
|
||||
return "no support for DHCP on multiple networks under this OS";
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PF_PACKET
|
||||
if (rawfd == -1 && /* same packet socket for all interfaces */
|
||||
(rawfd = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_IP))) == -1)
|
||||
return "Cannot create DHCP packet socket: %s";
|
||||
|
||||
/* do this last so that the index is still in ifr for the
|
||||
call to setsockopt(SO_BINDTODEVICE) */
|
||||
if (ioctl(fd, SIOCGIFINDEX, ifr) < 0)
|
||||
return "ioctl error getting interface index: %m";
|
||||
context->ifindex = ifr->ifr_ifindex;
|
||||
#endif
|
||||
|
||||
context->rawfd = rawfd;
|
||||
context->serv_addr = addr.in.sin_addr;
|
||||
context->netmask = netmask;
|
||||
context->broadcast = broadcast;
|
||||
if (!(context->iface = malloc(strlen(ifr->ifr_name) + 1)))
|
||||
return "cannot allocate interface name";
|
||||
|
||||
strcpy(context->iface, ifr->ifr_name);
|
||||
saddr.sin_family = AF_INET;
|
||||
saddr.sin_port = htons(DHCP_SERVER_PORT);
|
||||
saddr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if ((context->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
|
||||
return "cannot create DHCP server socket: %s";
|
||||
|
||||
if (setsockopt(context->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
|
||||
#ifdef HAVE_PF_PACKET
|
||||
setsockopt(context->fd, SOL_SOCKET, SO_BINDTODEVICE, ifr, sizeof(*ifr)) == -1 ||
|
||||
#endif
|
||||
setsockopt(context->fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) == -1)
|
||||
return "failed to set options on DHCP socket: %s";
|
||||
|
||||
if (bind(context->fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
|
||||
return "failed to bind DHCP server socket: %s";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_BPF
|
||||
/* now go through the interfaces again, looking for AF_LINK records
|
||||
to get hardware addresses from */
|
||||
for (ptr = buf; ptr < buf + ifc.ifc_len; )
|
||||
{
|
||||
struct ifreq *ifr = (struct ifreq *) ptr;
|
||||
struct dhcp_context *context;
|
||||
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
ptr += ifr->ifr_addr.sa_len + IF_NAMESIZE;
|
||||
#else
|
||||
ptr += sizeof(struct ifreq);
|
||||
#endif
|
||||
if (ifr->ifr_addr.sa_family == AF_LINK)
|
||||
for (context = dhcp; context; context = context->next)
|
||||
if (context->iface && strcmp(context->iface, ifr->ifr_name) == 0)
|
||||
memcpy(context->hwaddr, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
|
||||
}
|
||||
#endif
|
||||
|
||||
end:
|
||||
errsave = errno; /* since errno gets overwritten by close */
|
||||
if (buf)
|
||||
free(buf);
|
||||
close(fd);
|
||||
if (err)
|
||||
{
|
||||
errno = errsave;
|
||||
return err;
|
||||
}
|
||||
|
||||
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
|
||||
/* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */
|
||||
/* This code snarfed from net-tools 1.60 and certainly linux specific, though
|
||||
it shouldn't break on other Unices, and their SIOGIFCONF might work. */
|
||||
{
|
||||
FILE *f = fopen(IP6INTERFACES, "r");
|
||||
|
||||
if (f)
|
||||
{
|
||||
union mysockaddr addr;
|
||||
unsigned int plen, scope, flags, if_idx;
|
||||
char devname[20], addrstring[32];
|
||||
|
||||
while (fscanf(f, "%32s %02x %02x %02x %02x %20s\n",
|
||||
addrstring, &if_idx, &plen, &scope, &flags, devname) != EOF)
|
||||
{
|
||||
int i;
|
||||
unsigned char *addr6p = (unsigned char *) &addr.in6.sin6_addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sa.sa_family = AF_INET6;
|
||||
for (i=0; i<16; i++)
|
||||
{
|
||||
unsigned int byte;
|
||||
sscanf(addrstring+i+i, "%02x", &byte);
|
||||
addr6p[i] = byte;
|
||||
}
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
/* For completeness - should never be defined on Linux. */
|
||||
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
addr.in6.sin6_port = htons(port);
|
||||
addr.in6.sin6_flowinfo = htonl(0);
|
||||
addr.in6.sin6_scope_id = htonl(scope);
|
||||
|
||||
if ((err = add_iface(interfacep, flags, devname, &addr, names, addrs, except)))
|
||||
{
|
||||
errsave = errno;
|
||||
fclose(f);
|
||||
errno = errsave;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
#endif /* LINUX */
|
||||
|
||||
/* now remove interfaces which were not found on this scan */
|
||||
for(prev = NULL, iface = *interfacep; iface; )
|
||||
{
|
||||
if (iface->valid)
|
||||
{
|
||||
prev = iface;
|
||||
iface = iface->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct irec *tmp = iface;
|
||||
close(iface->fd);
|
||||
/* remove pending queries from this interface */
|
||||
reap_forward(iface->fd);
|
||||
/* unlink */
|
||||
if (prev)
|
||||
prev->next = iface->next;
|
||||
else
|
||||
*interfacep = iface->next;
|
||||
iface = iface->next;
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL; /* no error */
|
||||
}
|
||||
|
||||
static struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds)
|
||||
{
|
||||
struct serverfd *sfd;
|
||||
|
||||
/* may have a suitable one already */
|
||||
for (sfd = *sfds; sfd; sfd = sfd->next )
|
||||
if (sockaddr_isequal(&sfd->source_addr, addr))
|
||||
return sfd;
|
||||
|
||||
/* need to make a new one. */
|
||||
errno = ENOMEM; /* in case malloc fails. */
|
||||
if (!(sfd = malloc(sizeof(struct serverfd))))
|
||||
return NULL;
|
||||
|
||||
if ((sfd->fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0)) == -1)
|
||||
{
|
||||
free(sfd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (bind(sfd->fd, (struct sockaddr *)addr, sa_len(addr)) == -1)
|
||||
{
|
||||
int errsave = errno; /* save error from bind. */
|
||||
close(sfd->fd);
|
||||
free(sfd);
|
||||
errno = errsave;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sfd->source_addr = *addr;
|
||||
sfd->next = *sfds;
|
||||
*sfds = sfd;
|
||||
|
||||
return sfd;
|
||||
}
|
||||
|
||||
struct server *check_servers(struct server *new, struct irec *interfaces, struct serverfd **sfds)
|
||||
{
|
||||
char addrbuff[ADDRSTRLEN];
|
||||
struct irec *iface;
|
||||
struct server *tmp, *ret = NULL;
|
||||
int port = 0;
|
||||
|
||||
/* forward table rules reference servers, so have to blow them away */
|
||||
forward_init(0);
|
||||
|
||||
for (;new; new = tmp)
|
||||
{
|
||||
tmp = new->next;
|
||||
|
||||
if (!(new->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)))
|
||||
{
|
||||
#ifdef HAVE_IPV6
|
||||
if (new->addr.sa.sa_family == AF_INET)
|
||||
{
|
||||
inet_ntop(AF_INET, &new->addr.in.sin_addr, addrbuff, ADDRSTRLEN);
|
||||
port = ntohs(new->addr.in.sin_port);
|
||||
}
|
||||
else if (new->addr.sa.sa_family == AF_INET6)
|
||||
{
|
||||
inet_ntop(AF_INET6, &new->addr.in6.sin6_addr, addrbuff, ADDRSTRLEN);
|
||||
port = ntohs(new->addr.in6.sin6_port);
|
||||
}
|
||||
#else
|
||||
strcpy(addrbuff, inet_ntoa(new->addr.in.sin_addr));
|
||||
port = ntohs(new->addr.in.sin_port);
|
||||
#endif
|
||||
for (iface = interfaces; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&new->addr, &iface->addr))
|
||||
break;
|
||||
if (iface)
|
||||
{
|
||||
syslog(LOG_WARNING, "ignoring nameserver %s - local interface", addrbuff);
|
||||
free(new);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Do we need a socket set? */
|
||||
if (!new->sfd && !(new->sfd = allocate_sfd(&new->source_addr, sfds)))
|
||||
{
|
||||
syslog(LOG_WARNING,
|
||||
"ignoring nameserver %s - cannot make/bind socket: %m", addrbuff);
|
||||
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 = "domain", s2 = new->domain;
|
||||
else
|
||||
s1 = "unqualified", s2 = "domains";
|
||||
|
||||
if (new->flags & SERV_NO_ADDR)
|
||||
syslog(LOG_INFO, "using local addresses only for %s %s", s1, s2);
|
||||
else if (!(new->flags & SERV_LITERAL_ADDRESS))
|
||||
syslog(LOG_INFO, "using nameserver %s#%d for %s %s", addrbuff, port, s1, s2);
|
||||
}
|
||||
else
|
||||
syslog(LOG_INFO, "using nameserver %s#%d", addrbuff, port);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct server *reload_servers(char *fname, char *buff, struct server *serv, int query_port)
|
||||
{
|
||||
FILE *f;
|
||||
char *line;
|
||||
struct server *old_servers = NULL;
|
||||
struct server *new_servers = NULL;
|
||||
|
||||
/* 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. */
|
||||
while (serv)
|
||||
{
|
||||
struct server *tmp = serv->next;
|
||||
if (serv->flags & SERV_FROM_RESOLV)
|
||||
{
|
||||
serv->next = old_servers;
|
||||
old_servers = serv;
|
||||
}
|
||||
else
|
||||
{
|
||||
serv->next = new_servers;
|
||||
new_servers = serv;
|
||||
}
|
||||
serv = tmp;
|
||||
}
|
||||
|
||||
/* buff happens to be NAXDNAME long... */
|
||||
f = fopen(fname, "r");
|
||||
if (!f)
|
||||
{
|
||||
syslog(LOG_ERR, "failed to read %s: %m", fname);
|
||||
}
|
||||
else
|
||||
{
|
||||
syslog(LOG_INFO, "reading %s", fname);
|
||||
while ((line = fgets(buff, MAXDNAME, f)))
|
||||
{
|
||||
union mysockaddr addr, source_addr;
|
||||
char *token = strtok(line, " \t\n\r");
|
||||
struct server *serv;
|
||||
|
||||
if (!token || strcmp(token, "nameserver") != 0)
|
||||
continue;
|
||||
if (!(token = strtok(NULL, " \t\n")))
|
||||
continue;
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (inet_pton(AF_INET, token, &addr.in.sin_addr))
|
||||
#else
|
||||
if ((addr.in.sin_addr.s_addr = inet_addr(token)) != (in_addr_t) -1)
|
||||
#endif
|
||||
{
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
source_addr.in.sin_len = addr.in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
source_addr.in.sin_family = addr.in.sin_family = AF_INET;
|
||||
addr.in.sin_port = htons(NAMESERVER_PORT);
|
||||
source_addr.in.sin_addr.s_addr = INADDR_ANY;
|
||||
source_addr.in.sin_port = htons(query_port);
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
else if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr))
|
||||
{
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
|
||||
addr.in6.sin6_port = htons(NAMESERVER_PORT);
|
||||
source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = htonl(0);
|
||||
source_addr.in6.sin6_addr= in6addr_any;
|
||||
source_addr.in6.sin6_port = htons(query_port);
|
||||
}
|
||||
#endif /* IPV6 */
|
||||
else
|
||||
continue;
|
||||
|
||||
if (old_servers)
|
||||
{
|
||||
serv = old_servers;
|
||||
old_servers = old_servers->next;
|
||||
}
|
||||
else if (!(serv = 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->sfd = NULL;
|
||||
serv->flags = SERV_FROM_RESOLV;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/* Free any memory not used. */
|
||||
while(old_servers)
|
||||
{
|
||||
struct server *tmp = old_servers->next;
|
||||
free(old_servers);
|
||||
old_servers = tmp;
|
||||
}
|
||||
|
||||
return new_servers;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
868
src/option.c
Normal file
868
src/option.c
Normal file
@@ -0,0 +1,868 @@
|
||||
/* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
struct myoption {
|
||||
const char *name;
|
||||
int has_arg;
|
||||
int *flag;
|
||||
int val;
|
||||
};
|
||||
|
||||
#define OPTSTRING "DNLERowefnbvhdqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:"
|
||||
|
||||
static struct myoption opts[] = {
|
||||
{"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", 1, 0, 'g'},
|
||||
{"resolv-file", 1, 0, 'r'},
|
||||
{"mx-host", 1, 0, 'm'},
|
||||
{"mx-target", 1, 0, 't'},
|
||||
{"cache-size", 1, 0, 'c'},
|
||||
{"port", 1, 0, 'p'},
|
||||
{"dhcp-leasefile", 1, 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", 1, 0, 'x'},
|
||||
{"strict-order", 0, 0, 'o'},
|
||||
{"server", 1, 0, 'S'},
|
||||
{"local", 1, 0, 'S' },
|
||||
{"address", 1, 0, 'A' },
|
||||
{"conf-file", 1, 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'},
|
||||
{"domain-needed", 0, 0, 'D'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
struct optflags {
|
||||
char c;
|
||||
unsigned int flag;
|
||||
};
|
||||
|
||||
static struct optflags optmap[] = {
|
||||
{ 'b', OPT_BOGUSPRIV },
|
||||
{ 'f', OPT_FILTER },
|
||||
{ 'q', OPT_LOG },
|
||||
{ 'e', OPT_SELFMX },
|
||||
{ 'h', OPT_NO_HOSTS },
|
||||
{ 'n', OPT_NO_POLL },
|
||||
{ 'd', OPT_DEBUG },
|
||||
{ 'o', OPT_ORDER },
|
||||
{ 'R', OPT_NO_RESOLV },
|
||||
{ 'E', OPT_EXPAND },
|
||||
{ 'L', OPT_LOCALMX},
|
||||
{ 'N', OPT_NO_NEG},
|
||||
{ 'D', OPT_NODOTS_LOCAL},
|
||||
{ 'v', 0},
|
||||
{ 'w', 0},
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static char *usage =
|
||||
"Usage: dnsmasq [options]\n"
|
||||
"\nValid options are :\n"
|
||||
"-a, --listen-address=ipaddr Specify local address(es) to listen on.\n"
|
||||
"-A, --address=/domain/ipaddr Return ipaddr for all hosts in specified domains.\n"
|
||||
"-b, --bogus-priv Fake reverse lookups for RFC1918 private address ranges.\n"
|
||||
"-B, --bogus-nxdomain=ipaddr Treat ipaddr as NXDOMAIN (defeats Verisign wildcard).\n"
|
||||
"-c, --cache-size=cachesize Specify the size of the cache in entries (defaults to %d).\n"
|
||||
"-C, --conf-file=path Specify configuration file (defaults to " CONFFILE ").\n"
|
||||
"-d, --no-daemon Do NOT fork into the background: run in debug mode.\n"
|
||||
"-D, --domain-needed Do NOT forward queries with no domain part.\n"
|
||||
"-e, --selfmx Return self-pointing MX records for local hosts.\n"
|
||||
"-E, --expand-hosts Expand simple names in /etc/hosts with domain-suffix.\n"
|
||||
"-f, --filterwin2k Don't forward spurious DNS requests from Windows hosts.\n"
|
||||
"-F, --dhcp-range=ipaddr,ipaddr,time Enable DHCP in the range given with lease duration.\n"
|
||||
"-g, --group=groupname Change to this group after startup (defaults to " CHGRP ").\n"
|
||||
"-G, --dhcp-host=<hostspec> Set address or hostname for a specified machine.\n"
|
||||
"-h, --no-hosts Do NOT load " HOSTSFILE " file.\n"
|
||||
"-H, --addn-hosts=path Specify a hosts file to be read in addition to " HOSTSFILE ".\n"
|
||||
"-i, --interface=interface Specify interface(s) to listen on.\n"
|
||||
"-I, --except-interface=int Specify interface(s) NOT to listen on.\n"
|
||||
"-l, --dhcp-leasefile=path Specify where to store DHCP leases (defaults to " LEASEFILE ").\n"
|
||||
"-L, --localmx Return MX records for local hosts.\n"
|
||||
"-m, --mx-host=host_name Specify the MX name to reply to.\n"
|
||||
"-M, --dhcp-boot=<bootp opts> Specify BOOTP options to DHCP server.\n"
|
||||
"-n, --no-poll Do NOT poll " RESOLVFILE " file, reload only on SIGHUP.\n"
|
||||
"-N, --no-negcache Do NOT cache failed search results.\n"
|
||||
"-o, --strict-order Use nameservers strictly in the order given in " RESOLVFILE ".\n"
|
||||
"-O, --dhcp-option=<optspec> Set extra options to be set to DHCP clients.\n"
|
||||
"-p, --port=number Specify port to listen for DNS requests on (defaults to 53).\n"
|
||||
"-q, --log-queries Log queries.\n"
|
||||
"-Q, --query-port=number Force the originating port for upstream queries.\n"
|
||||
"-R, --no-resolv Do NOT read resolv.conf.\n"
|
||||
"-r, --resolv-file=path Specify path to resolv.conf (defaults to " RESOLVFILE ").\n"
|
||||
"-S, --server=/domain/ipaddr Specify address(es) of upstream servers with optional domains.\n"
|
||||
" --local=/domain/ Never forward queries to specified domains.\n"
|
||||
"-s, --domain=domain Specify the domain to be assigned in DHCP leases.\n"
|
||||
"-t, --mx-target=host_name Specify the host in an MX reply.\n"
|
||||
"-T, --local-ttl=time Specify time-to-live in seconds for replies from /etc/hosts.\n"
|
||||
"-u, --user=username Change to this user after startup. (defaults to " CHUSER ").\n"
|
||||
"-v, --version Display dnsmasq version.\n"
|
||||
"-w, --help Display this message.\n"
|
||||
"-x, --pid-file=path Specify path of PID file. (defaults to " RUNFILE ").\n"
|
||||
"\n";
|
||||
|
||||
|
||||
unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **resolv_files,
|
||||
char **mxname, char **mxtarget, char **lease_file,
|
||||
char **username, char **groupname, char **domain_suffix, char **runfile,
|
||||
struct iname **if_names, struct iname **if_addrs, struct iname **if_except,
|
||||
struct bogus_addr **bogus_addr, struct server **serv_addrs, int *cachesize, int *port,
|
||||
int *query_port, unsigned long *local_ttl, char **addn_hosts, struct dhcp_context **dhcp,
|
||||
struct dhcp_config **dhcp_conf, struct dhcp_opt **dhcp_opts, char **dhcp_file,
|
||||
char **dhcp_sname, struct in_addr *dhcp_next_server)
|
||||
{
|
||||
int option = 0, i;
|
||||
unsigned int flags = 0;
|
||||
FILE *f = NULL;
|
||||
char *conffile = CONFFILE;
|
||||
int conffile_set = 0;
|
||||
|
||||
opterr = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (!f)
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
option = getopt_long(argc, argv, OPTSTRING, (struct option *)opts, NULL);
|
||||
#else
|
||||
option = getopt(argc, argv, OPTSTRING);
|
||||
#endif
|
||||
else
|
||||
{ /* f non-NULL, reading from conffile. */
|
||||
if (!fgets(buff, MAXDNAME, f))
|
||||
{
|
||||
/* At end of file, all done */
|
||||
fclose(f);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *p;
|
||||
/* fgets gets end of line char too. */
|
||||
while (strlen(buff) > 0 &&
|
||||
(buff[strlen(buff)-1] == '\n' ||
|
||||
buff[strlen(buff)-1] == ' ' ||
|
||||
buff[strlen(buff)-1] == '\t'))
|
||||
buff[strlen(buff)-1] = 0;
|
||||
if (*buff == '#' || *buff == 0)
|
||||
continue; /* comment */
|
||||
if ((p=strchr(buff, '=')))
|
||||
{
|
||||
optarg = p+1;
|
||||
*p = 0;
|
||||
}
|
||||
else
|
||||
optarg = NULL;
|
||||
|
||||
option = 0;
|
||||
for (i=0; opts[i].name; i++)
|
||||
if (strcmp(opts[i].name, buff) == 0)
|
||||
option = opts[i].val;
|
||||
if (!option)
|
||||
die("bad option %s", buff);
|
||||
}
|
||||
}
|
||||
|
||||
if (option == -1)
|
||||
{ /* end of command line args, start reading conffile. */
|
||||
if (!conffile)
|
||||
break; /* "confile=" option disables */
|
||||
option = 0;
|
||||
if (!(f = fopen(conffile, "r")))
|
||||
{
|
||||
if (errno == ENOENT && !conffile_set)
|
||||
break; /* No conffile, all done. */
|
||||
else
|
||||
die("cannot read %s: %s", conffile);
|
||||
}
|
||||
}
|
||||
|
||||
if (!f && option == 'w')
|
||||
{
|
||||
fprintf (stderr, usage, CACHESIZ);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!f && option == 'v')
|
||||
{
|
||||
fprintf(stderr, "dnsmasq version %s\n", VERSION);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
for (i=0; optmap[i].c; i++)
|
||||
if (option == optmap[i].c)
|
||||
{
|
||||
flags |= optmap[i].flag;
|
||||
option = 0;
|
||||
if (f && optarg)
|
||||
die("extraneous parameter for %s in config file.", buff);
|
||||
break;
|
||||
}
|
||||
|
||||
if (option && option != '?')
|
||||
{
|
||||
if (f && !optarg)
|
||||
die("missing parameter for %s in config file.", buff);
|
||||
|
||||
switch (option)
|
||||
{
|
||||
case 'C':
|
||||
conffile = safe_string_alloc(optarg);
|
||||
conffile_set = 1;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
*runfile = safe_string_alloc(optarg);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
{
|
||||
char *name = safe_string_alloc(optarg);
|
||||
struct resolvc *new, *list = *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 = safe_malloc(sizeof(struct resolvc));
|
||||
new->next = list;
|
||||
new->name = name;
|
||||
new->is_default = 0;
|
||||
new->logged = 0;
|
||||
list = new;
|
||||
}
|
||||
*resolv_files = list;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'm':
|
||||
if (!canonicalise(optarg))
|
||||
option = '?';
|
||||
else
|
||||
*mxname = safe_string_alloc(optarg);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
if (!canonicalise(optarg))
|
||||
option = '?';
|
||||
else
|
||||
*mxtarget = safe_string_alloc(optarg);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
*lease_file = safe_string_alloc(optarg);
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
if (*addn_hosts)
|
||||
option = '?';
|
||||
else
|
||||
*addn_hosts = safe_string_alloc(optarg);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
if (!canonicalise(optarg))
|
||||
option = '?';
|
||||
else
|
||||
*domain_suffix = safe_string_alloc(optarg);
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
*username = safe_string_alloc(optarg);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
*groupname = safe_string_alloc(optarg);
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
{
|
||||
struct iname *new = safe_malloc(sizeof(struct iname));
|
||||
new->next = *if_names;
|
||||
*if_names = new;
|
||||
/* new->name may be NULL if someone does
|
||||
"interface=" to disable all interfaces except loop. */
|
||||
new->name = safe_string_alloc(optarg);
|
||||
new->found = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'I':
|
||||
{
|
||||
struct iname *new = safe_malloc(sizeof(struct iname));
|
||||
new->next = *if_except;
|
||||
*if_except = new;
|
||||
new->name = safe_string_alloc(optarg);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'B':
|
||||
{
|
||||
struct in_addr addr;
|
||||
if ((addr.s_addr = inet_addr(optarg)) != (in_addr_t)-1)
|
||||
{
|
||||
struct bogus_addr *baddr = safe_malloc(sizeof(struct bogus_addr));
|
||||
baddr->next = *bogus_addr;
|
||||
*bogus_addr = baddr;
|
||||
baddr->addr = addr;
|
||||
}
|
||||
else
|
||||
option = '?'; /* error */
|
||||
break;
|
||||
}
|
||||
|
||||
case 'a':
|
||||
{
|
||||
struct iname *new = safe_malloc(sizeof(struct iname));
|
||||
new->next = *if_addrs;
|
||||
*if_addrs = new;
|
||||
new->found = 0;
|
||||
#ifdef HAVE_IPV6
|
||||
if (inet_pton(AF_INET, optarg, &new->addr.in.sin_addr))
|
||||
{
|
||||
new->addr.sa.sa_family = AF_INET;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
new->addr.in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
}
|
||||
else if (inet_pton(AF_INET6, optarg, &new->addr.in6.sin6_addr))
|
||||
{
|
||||
new->addr.sa.sa_family = AF_INET6;
|
||||
new->addr.in6.sin6_flowinfo = htonl(0);
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
new->addr.in6.sin6_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
if ((new->addr.in.sin_addr.s_addr = inet_addr(optarg)) != (in_addr_t)-1)
|
||||
{
|
||||
new->addr.sa.sa_family = AF_INET;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
new->addr.in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
else
|
||||
option = '?'; /* error */
|
||||
break;
|
||||
}
|
||||
|
||||
case 'S':
|
||||
case 'A':
|
||||
{
|
||||
struct server *serv, *newlist = NULL;
|
||||
|
||||
if (*optarg == '/')
|
||||
{
|
||||
char *end;
|
||||
optarg++;
|
||||
while ((end = strchr(optarg, '/')))
|
||||
{
|
||||
char *domain;
|
||||
*end = 0;
|
||||
if (!canonicalise(optarg))
|
||||
{
|
||||
option = '?';
|
||||
break;
|
||||
}
|
||||
domain = safe_string_alloc(optarg); /* NULL if strlen is zero */
|
||||
serv = safe_malloc(sizeof(struct server));
|
||||
serv->next = newlist;
|
||||
newlist = serv;
|
||||
serv->sfd = NULL;
|
||||
serv->domain = domain;
|
||||
serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
|
||||
optarg = end+1;
|
||||
}
|
||||
if (!newlist)
|
||||
{
|
||||
option = '?';
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
newlist = safe_malloc(sizeof(struct server));
|
||||
newlist->next = NULL;
|
||||
newlist->flags = 0;
|
||||
newlist->sfd = NULL;
|
||||
newlist->domain = NULL;
|
||||
}
|
||||
|
||||
if (option == 'A')
|
||||
{
|
||||
newlist->flags |= SERV_LITERAL_ADDRESS;
|
||||
if (!(newlist->flags & SERV_TYPE))
|
||||
{
|
||||
option = '?';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!*optarg)
|
||||
{
|
||||
newlist->flags |= SERV_NO_ADDR; /* no server */
|
||||
if (newlist->flags & SERV_LITERAL_ADDRESS)
|
||||
{
|
||||
option = '?';
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int source_port = 0, serv_port = NAMESERVER_PORT;
|
||||
char *portno, *source;
|
||||
|
||||
if ((source = strchr(optarg, '@'))) /* is there a source. */
|
||||
{
|
||||
*source = 0;
|
||||
if ((portno = strchr(source+1, '#')))
|
||||
{
|
||||
*portno = 0;
|
||||
source_port = atoi(portno+1);
|
||||
}
|
||||
}
|
||||
|
||||
if ((portno = strchr(optarg, '#'))) /* is there a port no. */
|
||||
{
|
||||
*portno = 0;
|
||||
serv_port = atoi(portno+1);
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (inet_pton(AF_INET, optarg, &newlist->addr.in.sin_addr))
|
||||
#else
|
||||
if ((newlist->addr.in.sin_addr.s_addr = inet_addr(optarg)) != (in_addr_t) -1)
|
||||
#endif
|
||||
{
|
||||
newlist->addr.in.sin_port = htons(serv_port);
|
||||
newlist->source_addr.in.sin_port = htons(source_port);
|
||||
newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
newlist->source_addr.in.sin_len = newlist->addr.in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
if (source)
|
||||
{
|
||||
#ifdef HAVE_IPV6
|
||||
if (inet_pton(AF_INET, source+1, &newlist->source_addr.in.sin_addr))
|
||||
#else
|
||||
if ((newlist->source_addr.in.sin_addr.s_addr = inet_addr(source+1)) != (in_addr_t) -1)
|
||||
#endif
|
||||
newlist->flags |= SERV_HAS_SOURCE;
|
||||
else
|
||||
option = '?'; /* error */
|
||||
}
|
||||
else
|
||||
newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
else if (inet_pton(AF_INET6, optarg, &newlist->addr.in6.sin6_addr))
|
||||
{
|
||||
newlist->addr.in6.sin6_port = htons(serv_port);
|
||||
newlist->source_addr.in6.sin6_port = htons(source_port);
|
||||
newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET6;
|
||||
newlist->addr.in6.sin6_flowinfo = newlist->source_addr.in6.sin6_flowinfo = htonl(0);
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
newlist->addr.in6.sin6_len = newlist->source_addr.in6.sin6_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
if (source)
|
||||
{
|
||||
if (inet_pton(AF_INET6, source+1, &newlist->source_addr.in6.sin6_addr))
|
||||
newlist->flags |= SERV_HAS_SOURCE;
|
||||
else
|
||||
option = '?'; /* error */
|
||||
}
|
||||
else
|
||||
newlist->source_addr.in6.sin6_addr = in6addr_any;
|
||||
}
|
||||
#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 = *serv_addrs;
|
||||
*serv_addrs = newlist;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'c':
|
||||
{
|
||||
int size = atoi(optarg);
|
||||
/* zero is OK, and means no caching. */
|
||||
|
||||
if (size < 0)
|
||||
size = 0;
|
||||
else if (size > 10000)
|
||||
size = 10000;
|
||||
|
||||
*cachesize = size;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'p':
|
||||
*port = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'Q':
|
||||
*query_port = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
*local_ttl = (unsigned long)atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
{
|
||||
char *comma;
|
||||
struct dhcp_context *new = safe_malloc(sizeof(struct dhcp_context));
|
||||
|
||||
new->next = *dhcp;
|
||||
*dhcp = new;
|
||||
new->lease_time = DEFLEASE;
|
||||
|
||||
if (!(comma = strchr(optarg, ',')) || (*comma = 0) ||
|
||||
((new->start.s_addr = inet_addr(optarg)) == (in_addr_t)-1))
|
||||
{
|
||||
option = '?';
|
||||
break;
|
||||
}
|
||||
|
||||
optarg = comma + 1;
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
{
|
||||
*(comma++) = 0;
|
||||
if (strcmp(comma, "infinite") == 0)
|
||||
new->lease_time = 0xffffffff;
|
||||
else
|
||||
{
|
||||
int fac = 1;
|
||||
if (strlen(comma) > 0)
|
||||
{
|
||||
switch (comma[strlen(comma) - 1])
|
||||
{
|
||||
case 'h':
|
||||
case 'H':
|
||||
fac *= 60;
|
||||
/* fall through */
|
||||
case 'm':
|
||||
case 'M':
|
||||
fac *= 60;
|
||||
|
||||
comma[strlen(comma) - 1] = 0;
|
||||
}
|
||||
|
||||
new->lease_time = atoi(comma) * fac;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((new->end.s_addr = inet_addr(optarg)) == (in_addr_t)-1)
|
||||
{
|
||||
option = '?';
|
||||
break;
|
||||
}
|
||||
|
||||
new->last = new->start;
|
||||
new->iface = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'G':
|
||||
{
|
||||
int j, k;
|
||||
char *a[4] = { NULL, NULL, NULL, NULL };
|
||||
unsigned int e0, e1, e2, e3, e4, e5;
|
||||
struct dhcp_config *new = safe_malloc(sizeof(struct dhcp_config));
|
||||
struct in_addr in;
|
||||
|
||||
new->next = *dhcp_conf;
|
||||
*dhcp_conf = new;
|
||||
|
||||
memset(new->hwaddr, 0, ETHER_ADDR_LEN);
|
||||
new->clid_len = 0;
|
||||
new->clid = NULL;
|
||||
new->hostname = NULL;
|
||||
new->addr.s_addr = 0;
|
||||
new->lease_time = DEFLEASE;
|
||||
|
||||
a[0] = optarg;
|
||||
for (k = 1; k < 4; k++)
|
||||
{
|
||||
if (!(a[k] = strchr(a[k-1], ',')))
|
||||
break;
|
||||
*(a[k]++) = 0;
|
||||
}
|
||||
|
||||
for(j = 0; j < k; j++)
|
||||
if (strchr(a[j], ':')) /* ethernet address or binary CLID */
|
||||
{
|
||||
char *arg = a[j];
|
||||
if ((arg[0] == 'i' || arg[0] == 'I') &&
|
||||
(arg[1] == 'd' || arg[1] == 'D') &&
|
||||
arg[2] == ':')
|
||||
{
|
||||
int s, len;
|
||||
arg += 3; /* dump id: */
|
||||
if (strchr(arg, ':'))
|
||||
{
|
||||
s = (strlen(arg)/3) + 1;
|
||||
/* decode in place */
|
||||
for (len = 0; len < s; len++)
|
||||
{
|
||||
if (arg[(len*3)+2] != ':')
|
||||
option = '?';
|
||||
arg[(len*3)+2] = 0;
|
||||
arg[len] = strtol(&arg[len*3], NULL, 16);
|
||||
}
|
||||
}
|
||||
else
|
||||
len = strlen(arg);
|
||||
|
||||
new->clid_len = len;
|
||||
new->clid = safe_malloc(len);
|
||||
memcpy(new->clid, arg, len);
|
||||
}
|
||||
else if (sscanf(a[j], "%x:%x:%x:%x:%x:%x",
|
||||
&e0, &e1, &e2, &e3, &e4, &e5) == 6)
|
||||
{
|
||||
new->hwaddr[0] = e0;
|
||||
new->hwaddr[1] = e1;
|
||||
new->hwaddr[2] = e2;
|
||||
new->hwaddr[3] = e3;
|
||||
new->hwaddr[4] = e4;
|
||||
new->hwaddr[5] = e5;
|
||||
}
|
||||
else
|
||||
option = '?';
|
||||
}
|
||||
else if (strchr(a[j], '.') && (in.s_addr = inet_addr(a[j])) != (in_addr_t)-1)
|
||||
new->addr = in;
|
||||
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 'h':
|
||||
case 'H':
|
||||
fac *= 60;
|
||||
/* fall through */
|
||||
case 'm':
|
||||
case 'M':
|
||||
fac *= 60;
|
||||
|
||||
*lastp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (cp = a[j]; *cp; cp++)
|
||||
if (!isdigit(*cp))
|
||||
break;
|
||||
|
||||
if (*cp)
|
||||
{
|
||||
if (lastp)
|
||||
*lastp = last;
|
||||
if (strcmp(a[j], "infinite") == 0)
|
||||
new->lease_time = 0xffffffff;
|
||||
else
|
||||
new->hostname = safe_string_alloc(a[j]);
|
||||
}
|
||||
else
|
||||
new->lease_time = atoi(a[j]) * fac;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'O':
|
||||
{
|
||||
struct dhcp_opt *new = safe_malloc(sizeof(struct dhcp_opt));
|
||||
char *cp, *comma = strchr(optarg, ',');
|
||||
int addrs;
|
||||
|
||||
new->next = *dhcp_opts;
|
||||
*dhcp_opts = new;
|
||||
|
||||
if (!comma || (*comma = 0) || (new->opt = atoi(optarg)) == 0)
|
||||
{
|
||||
option = '?';
|
||||
break;
|
||||
}
|
||||
|
||||
/* check for non-address list characters */
|
||||
for (addrs = 1, cp = comma+1; *cp; cp++)
|
||||
if (*cp == ',')
|
||||
addrs++;
|
||||
else if (!(*cp == '.' || *cp == ' ' || (*cp >='0' && *cp <= '9')))
|
||||
break;
|
||||
|
||||
if (*cp)
|
||||
{
|
||||
/* text arg */
|
||||
new->len = strlen(comma+1);
|
||||
new->val = safe_malloc(new->len);
|
||||
memcpy(new->val, comma+1, new->len);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct in_addr in;
|
||||
unsigned char *op;
|
||||
new->len = INADDRSZ * addrs;
|
||||
new->val = op = safe_malloc(new->len);
|
||||
while (addrs--)
|
||||
{
|
||||
cp = comma;
|
||||
if (cp && (comma = strchr(cp+1, ',')))
|
||||
*comma = 0;
|
||||
if (cp && (in.s_addr = inet_addr(cp+1)) == (in_addr_t)-1)
|
||||
option = '?';
|
||||
memcpy(op, &in, INADDRSZ);
|
||||
op += INADDRSZ;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'M':
|
||||
{
|
||||
char *comma;
|
||||
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*comma = 0;
|
||||
*dhcp_file = safe_string_alloc(optarg);
|
||||
if (comma)
|
||||
{
|
||||
optarg = comma+1;
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*comma = 0;
|
||||
*dhcp_sname = safe_string_alloc(optarg);
|
||||
if (comma && (dhcp_next_server->s_addr = inet_addr(comma+1)) == (in_addr_t)-1)
|
||||
option = '?';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (option == '?')
|
||||
{
|
||||
if (f)
|
||||
die("bad argument for option %s", buff);
|
||||
else
|
||||
die("bad command line options: try --help.", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* port might no be known when the address is parsed - fill in here */
|
||||
if (*serv_addrs)
|
||||
{
|
||||
struct server *tmp;
|
||||
for (tmp = *serv_addrs; 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(*query_port);
|
||||
#ifdef HAVE_IPV6
|
||||
else if (tmp->source_addr.sa.sa_family == AF_INET6)
|
||||
tmp->source_addr.in6.sin6_port = htons(*query_port);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (*if_addrs)
|
||||
{
|
||||
struct iname *tmp;
|
||||
for(tmp = *if_addrs; tmp; tmp = tmp->next)
|
||||
if (tmp->addr.sa.sa_family == AF_INET)
|
||||
tmp->addr.in.sin_port = htons(*port);
|
||||
#ifdef HAVE_IPV6
|
||||
else if (tmp->addr.sa.sa_family == AF_INET6)
|
||||
tmp->addr.in6.sin6_port = htons(*port);
|
||||
#endif /* IPv6 */
|
||||
}
|
||||
|
||||
/* only one of these need be specified: the other defaults to the
|
||||
host-name */
|
||||
if ((flags & OPT_LOCALMX) || *mxname || *mxtarget)
|
||||
{
|
||||
if (gethostname(buff, MAXDNAME) == -1)
|
||||
die("cannot get host-name: %s", NULL);
|
||||
|
||||
if (!*mxname)
|
||||
*mxname = safe_string_alloc(buff);
|
||||
|
||||
if (!*mxtarget)
|
||||
*mxtarget = safe_string_alloc(buff);
|
||||
}
|
||||
|
||||
if (flags & OPT_NO_RESOLV)
|
||||
*resolv_files = 0;
|
||||
else if (*resolv_files && (*resolv_files)->next && (flags & OPT_NO_POLL))
|
||||
die("only one resolv.conf file allowed in no-poll mode.", NULL);
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
||||
|
||||
1019
src/rfc1035.c
Normal file
1019
src/rfc1035.c
Normal file
File diff suppressed because it is too large
Load Diff
499
src/rfc2131.c
Normal file
499
src/rfc2131.c
Normal file
@@ -0,0 +1,499 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#define BOOTREQUEST 1
|
||||
#define BOOTREPLY 2
|
||||
#define DHCP_COOKIE 0x63825363
|
||||
|
||||
#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_CLIENT_ID 61
|
||||
#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_MAXMESSAGE 57
|
||||
#define OPTION_END 255
|
||||
|
||||
#define DHCPDISCOVER 1
|
||||
#define DHCPOFFER 2
|
||||
#define DHCPREQUEST 3
|
||||
#define DHCPDECLINE 4
|
||||
#define DHCPACK 5
|
||||
#define DHCPNAK 6
|
||||
#define DHCPRELEASE 7
|
||||
#define DHCPINFORM 8
|
||||
|
||||
static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val);
|
||||
static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname);
|
||||
static int option_len(unsigned char *opt);
|
||||
static void *option_ptr(unsigned char *opt);
|
||||
static struct in_addr option_addr(unsigned char *opt);
|
||||
static unsigned int option_uint(unsigned char *opt);
|
||||
static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface);
|
||||
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type);
|
||||
static unsigned char *do_req_options(struct dhcp_context *context,
|
||||
unsigned char *p, unsigned char *end,
|
||||
unsigned char *req_options,
|
||||
struct dhcp_opt *config_opts,
|
||||
char *domainname, char *hostname);
|
||||
|
||||
|
||||
int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
|
||||
unsigned int sz, time_t now, char *namebuff,
|
||||
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
|
||||
char *domain_suffix, char *dhcp_file, char *dhcp_sname,
|
||||
struct in_addr dhcp_next_server)
|
||||
{
|
||||
unsigned char *opt, *clid;
|
||||
struct dhcp_lease *lease;
|
||||
int clid_len;
|
||||
unsigned char *p = mess->options;
|
||||
char *hostname = NULL;
|
||||
char *req_options = NULL;
|
||||
unsigned int renewal_time, expires_time, def_time;
|
||||
struct dhcp_config *config;
|
||||
|
||||
if (mess->op != BOOTREQUEST ||
|
||||
mess->htype != ARPHRD_ETHER ||
|
||||
mess->hlen != ETHER_ADDR_LEN ||
|
||||
mess->cookie != htonl(DHCP_COOKIE))
|
||||
return 0;
|
||||
|
||||
mess->op = BOOTREPLY;
|
||||
|
||||
/* If there is no client identifier option, use the hardware address */
|
||||
if ((opt = option_find(mess, sz, OPTION_CLIENT_ID)))
|
||||
{
|
||||
clid = option_ptr(opt);
|
||||
clid_len = option_len(opt);
|
||||
}
|
||||
else
|
||||
{
|
||||
clid = mess->chaddr;
|
||||
clid_len = 0;
|
||||
}
|
||||
|
||||
/* do we have a lease in store? */
|
||||
lease = lease_find_by_client(clid, clid_len);
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS)))
|
||||
{
|
||||
int len = option_len(opt);
|
||||
req_options = namebuff;
|
||||
memcpy(req_options, option_ptr(opt), len);
|
||||
req_options[len] = OPTION_END;
|
||||
}
|
||||
|
||||
if ((config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, NULL)) &&
|
||||
config->hostname)
|
||||
hostname = config->hostname;
|
||||
else if ((opt = option_find(mess, sz, OPTION_HOSTNAME)))
|
||||
{
|
||||
int len = option_len(opt);
|
||||
/* namebuff is 1K long, use half for requested options and half for hostname */
|
||||
/* len < 256 by definition */
|
||||
hostname = namebuff + 500;
|
||||
memcpy(hostname, option_ptr(opt), len);
|
||||
/* May not be zero terminated */
|
||||
hostname[len] = 0;
|
||||
/* ensure there are no strange chars in there */
|
||||
if (!canonicalise(hostname))
|
||||
hostname = NULL;
|
||||
}
|
||||
|
||||
if (hostname)
|
||||
{
|
||||
char *dot = strchr(hostname, '.');
|
||||
if (dot)
|
||||
{
|
||||
if (!domain_suffix || !hostname_isequal(dot+1, domain_suffix))
|
||||
{
|
||||
syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname);
|
||||
hostname = NULL;
|
||||
}
|
||||
else
|
||||
*dot = 0; /* truncate */
|
||||
}
|
||||
}
|
||||
|
||||
/* search again now we have a hostname */
|
||||
config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, hostname);
|
||||
def_time = config ? config->lease_time : context->lease_time;
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_LEASE_TIME)))
|
||||
{
|
||||
unsigned int req_time = option_uint(opt);
|
||||
|
||||
if (def_time == 0xffffffff ||
|
||||
(req_time != 0xffffffff && req_time < def_time))
|
||||
expires_time = renewal_time = req_time;
|
||||
else
|
||||
expires_time = renewal_time = def_time;
|
||||
}
|
||||
else
|
||||
{
|
||||
renewal_time = def_time;
|
||||
if (lease)
|
||||
expires_time = (unsigned int)difftime(lease->expires, now);
|
||||
else
|
||||
expires_time = def_time;
|
||||
}
|
||||
|
||||
if (!(opt = option_find(mess, sz, OPTION_MESSAGE_TYPE)))
|
||||
return 0;
|
||||
|
||||
switch (opt[2])
|
||||
{
|
||||
case DHCPRELEASE:
|
||||
if (lease)
|
||||
{
|
||||
log_packet("RELEASE", &lease->addr, mess->chaddr, context->iface);
|
||||
lease_prune(lease, now);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case DHCPDISCOVER:
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
|
||||
mess->yiaddr = option_addr(opt);
|
||||
|
||||
log_packet("DISCOVER", opt ? &mess->yiaddr : NULL, mess->chaddr, context->iface);
|
||||
|
||||
if (lease)
|
||||
mess->yiaddr = lease->addr;
|
||||
else if (config && config->addr.s_addr && !lease_find_by_addr(config->addr))
|
||||
mess->yiaddr = config->addr;
|
||||
else if ((!opt || !address_available(context, mess->yiaddr)) &&
|
||||
!address_allocate(context, dhcp_configs, &mess->yiaddr))
|
||||
{
|
||||
syslog(LOG_WARNING, "address pool exhausted");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bootp_option_put(mess, dhcp_file, dhcp_sname);
|
||||
mess->siaddr = dhcp_next_server;
|
||||
p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
|
||||
p = option_put(p, &mess->options[308], OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->serv_addr.s_addr));
|
||||
p = option_put(p, &mess->options[308], OPTION_LEASE_TIME, 4, expires_time);
|
||||
p = do_req_options(context, p, &mess->options[308], req_options, dhcp_opts, domain_suffix, NULL);
|
||||
p = option_put(p, &mess->options[308], OPTION_END, 0, 0);
|
||||
|
||||
log_packet("OFFER" , &mess->yiaddr, mess->chaddr, context->iface);
|
||||
return p - (unsigned char *)mess;
|
||||
|
||||
|
||||
case DHCPREQUEST:
|
||||
if (mess->ciaddr.s_addr)
|
||||
{
|
||||
/* RENEWING or REBINDING */
|
||||
/* Must exist a lease for this address */
|
||||
log_packet("REQUEST", &mess->ciaddr, mess->chaddr, context->iface);
|
||||
|
||||
if (!lease || mess->ciaddr.s_addr != lease->addr.s_addr)
|
||||
{
|
||||
log_packet("NAK", &mess->ciaddr, mess->chaddr, context->iface);
|
||||
|
||||
mess->siaddr.s_addr = mess->yiaddr.s_addr = mess->ciaddr.s_addr = 0;
|
||||
bootp_option_put(mess, NULL, NULL);
|
||||
p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPNAK);
|
||||
p = option_put(p, &mess->options[308], OPTION_END, 0, 0);
|
||||
|
||||
return (unsigned char *)mess - p; /* -ve to force bcast */
|
||||
}
|
||||
|
||||
mess->yiaddr = mess->ciaddr;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* SELECTING or INIT_REBOOT */
|
||||
if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) &&
|
||||
(context->serv_addr.s_addr != option_addr(opt).s_addr))
|
||||
return 0;
|
||||
|
||||
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
|
||||
return 0;
|
||||
|
||||
mess->yiaddr = option_addr(opt);
|
||||
log_packet("REQUEST", &mess->yiaddr, mess->chaddr, context->iface);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* accept addresses in the dynamic range or ones allocated statically to
|
||||
particular hosts or an address which the host already has. */
|
||||
if (!lease &&
|
||||
!address_available(context, mess->yiaddr) &&
|
||||
(!config || config->addr.s_addr == 0 || config->addr.s_addr != mess->yiaddr.s_addr))
|
||||
{
|
||||
log_packet("NAK", &mess->yiaddr, mess->chaddr, context->iface);
|
||||
|
||||
mess->siaddr.s_addr = mess->yiaddr.s_addr = mess->ciaddr.s_addr = 0;
|
||||
bootp_option_put(mess, NULL, NULL);
|
||||
p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPNAK);
|
||||
p = option_put(p, &mess->options[308], OPTION_END, 0, 0);
|
||||
|
||||
return (unsigned char *)mess - p; /* -ve to force bcast */
|
||||
}
|
||||
|
||||
if (!lease &&
|
||||
!(lease = lease_allocate(clid, clid_len, mess->yiaddr)))
|
||||
return 0;
|
||||
}
|
||||
|
||||
lease_set_hwaddr(lease, mess->chaddr);
|
||||
lease_set_hostname(lease, hostname, domain_suffix);
|
||||
lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time);
|
||||
|
||||
bootp_option_put(mess, dhcp_file, dhcp_sname);
|
||||
mess->siaddr = dhcp_next_server;
|
||||
p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPACK);
|
||||
p = option_put(p, &mess->options[308], OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->serv_addr.s_addr));
|
||||
p = option_put(p, &mess->options[308], OPTION_LEASE_TIME, 4, renewal_time);
|
||||
p = do_req_options(context, p, &mess->options[308], req_options, dhcp_opts, domain_suffix, hostname);
|
||||
p = option_put(p, &mess->options[308], OPTION_END, 0, 0);
|
||||
|
||||
log_packet("ACK", &mess->yiaddr, mess->chaddr, context->iface);
|
||||
return p - (unsigned char *)mess;
|
||||
|
||||
case DHCPINFORM:
|
||||
log_packet("INFORM", &mess->ciaddr, mess->chaddr, context->iface);
|
||||
|
||||
p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPACK);
|
||||
p = option_put(p, &mess->options[308], OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->serv_addr.s_addr));
|
||||
p = do_req_options(context, p, &mess->options[308], req_options, dhcp_opts, domain_suffix, hostname);
|
||||
p = option_put(p, &mess->options[308], OPTION_END, 0, 0);
|
||||
|
||||
log_packet("ACK", &mess->ciaddr, mess->chaddr, context->iface);
|
||||
return p - (unsigned char *)mess;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface)
|
||||
{
|
||||
syslog(LOG_INFO, "DHCP%s(%s)%s%s hwaddr=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
|
||||
type,
|
||||
interface,
|
||||
addr ? " " : "",
|
||||
addr ? inet_ntoa(*addr) : "",
|
||||
hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
|
||||
}
|
||||
|
||||
static int option_len(unsigned char *opt)
|
||||
{
|
||||
return opt[1];
|
||||
}
|
||||
|
||||
static void *option_ptr(unsigned char *opt)
|
||||
{
|
||||
return &opt[2];
|
||||
}
|
||||
|
||||
static struct in_addr option_addr(unsigned char *opt)
|
||||
{
|
||||
/* this worries about unaligned data in the option. */
|
||||
/* struct in_addr is network byte order */
|
||||
struct in_addr ret;
|
||||
|
||||
memcpy(&ret, option_ptr(opt), INADDRSZ);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int option_uint(unsigned char *opt)
|
||||
{
|
||||
/* this worries about unaligned data and byte order */
|
||||
unsigned int ret;
|
||||
|
||||
memcpy(&ret, option_ptr(opt), sizeof(unsigned int));
|
||||
|
||||
return ntohl(ret);
|
||||
}
|
||||
|
||||
static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname)
|
||||
{
|
||||
memset(mess->sname, 0, sizeof(mess->sname));
|
||||
memset(mess->file, 0, sizeof(mess->file));
|
||||
if (sname)
|
||||
strncpy(mess->sname, sname, sizeof(mess->sname)-1);
|
||||
if (filename)
|
||||
strncpy(mess->file, filename, sizeof(mess->file)-1);
|
||||
}
|
||||
|
||||
static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (p + len + 2 < end)
|
||||
{
|
||||
*(p++) = opt;
|
||||
*(p++) = len;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
*(p++) = val >> (8 * (len - (i + 1)));
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int *overload)
|
||||
{
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
while (*p != OPTION_END)
|
||||
{
|
||||
if (end && (p >= end))
|
||||
return 0; /* malformed packet */
|
||||
else if (*p == OPTION_PAD)
|
||||
p++;
|
||||
else if (*p == OPTION_OVERLOAD)
|
||||
{
|
||||
if (end && (p >= end - 3))
|
||||
return 0; /* malformed packet */
|
||||
if (overload)
|
||||
*overload = *(p+2);
|
||||
p += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
int opt_len;;
|
||||
if (end && (p >= end - 2))
|
||||
return 0; /* malformed packet */
|
||||
opt_len = option_len(p);
|
||||
if (end && (p >= end - (2 + opt_len)))
|
||||
return 0; /* malformed packet */
|
||||
if (*p == opt)
|
||||
return p;
|
||||
p += opt_len + 2;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type)
|
||||
{
|
||||
int overload = 0;
|
||||
unsigned char *ret;
|
||||
|
||||
ret = option_find1(&mess->options[0], ((unsigned char *)mess) + size, opt_type, &overload);
|
||||
|
||||
if (!ret && (overload & 1))
|
||||
ret = option_find1(&mess->file[0], &mess->file[128], opt_type, &overload);
|
||||
|
||||
if (!ret && (overload & 2))
|
||||
ret = option_find1(&mess->sname[0], &mess->file[64], opt_type, &overload);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int in_list(unsigned char *list, int opt)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; list[i] != OPTION_END; i++)
|
||||
if (opt == list[i])
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dhcp_opt *option_find2(struct dhcp_opt *opts, int opt)
|
||||
{
|
||||
for (; opts; opts = opts->next)
|
||||
if (opts->opt == opt)
|
||||
return opts;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static unsigned char *do_req_options(struct dhcp_context *context,
|
||||
unsigned char *p, unsigned char *end,
|
||||
unsigned char *req_options,
|
||||
struct dhcp_opt *config_opts,
|
||||
char *domainname, char *hostname)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!req_options)
|
||||
return p;
|
||||
|
||||
if (in_list(req_options, OPTION_NETMASK) &&
|
||||
!option_find2(config_opts, OPTION_NETMASK))
|
||||
p = option_put(p, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
|
||||
|
||||
if (in_list(req_options, OPTION_BROADCAST) &&
|
||||
!option_find2(config_opts, OPTION_BROADCAST))
|
||||
p = option_put(p, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
|
||||
|
||||
if (in_list(req_options, OPTION_ROUTER) &&
|
||||
!option_find2(config_opts, OPTION_ROUTER))
|
||||
p = option_put(p, end, OPTION_ROUTER, INADDRSZ, ntohl(context->serv_addr.s_addr));
|
||||
|
||||
if (in_list(req_options, OPTION_DNSSERVER) &&
|
||||
!option_find2(config_opts, OPTION_DNSSERVER))
|
||||
p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->serv_addr.s_addr));
|
||||
|
||||
if (in_list(req_options, OPTION_DOMAINNAME) &&
|
||||
!option_find2(config_opts, OPTION_DOMAINNAME) &&
|
||||
domainname && (p + strlen(domainname) + 2 < end))
|
||||
{
|
||||
*(p++) = OPTION_DOMAINNAME;
|
||||
*(p++) = strlen(domainname);
|
||||
memcpy(p, domainname, strlen(domainname));
|
||||
p += strlen(domainname);
|
||||
}
|
||||
|
||||
/* Note that we ignore attempts to set the hostname using
|
||||
--dhcp-option=12,<name> */
|
||||
if (in_list(req_options, OPTION_HOSTNAME) &&
|
||||
hostname && (p + strlen(hostname) + 2 < end))
|
||||
{
|
||||
*(p++) = OPTION_HOSTNAME;
|
||||
*(p++) = strlen(hostname);
|
||||
memcpy(p, hostname, strlen(hostname));
|
||||
p += strlen(hostname);
|
||||
}
|
||||
|
||||
for (i = 0; req_options[i] != OPTION_END; i++)
|
||||
{
|
||||
struct dhcp_opt *opt = option_find2(config_opts, req_options[i]);
|
||||
if (req_options[i] != OPTION_HOSTNAME && opt && (p + opt->len + 2 < end))
|
||||
{
|
||||
*(p++) = opt->opt;
|
||||
*(p++) = opt->len;
|
||||
memcpy(p, opt->val, opt->len);
|
||||
p += opt->len;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
217
src/util.c
Normal file
217
src/util.c
Normal file
@@ -0,0 +1,217 @@
|
||||
/* dnsmasq is Copyright (c) 2000 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
|
||||
/* Code in this file contributed by Rob Funk. */
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
/* Prefer arc4random(3) over random(3) over rand(3) */
|
||||
/* Also prefer /dev/urandom over /dev/random, to preserve the entropy pool */
|
||||
#ifdef HAVE_ARC4RANDOM
|
||||
# define rand() arc4random()
|
||||
# define srand(s) (void)0
|
||||
# define RANDFILE (NULL)
|
||||
#else
|
||||
# ifdef HAVE_RANDOM
|
||||
# define rand() random()
|
||||
# define srand(s) srandom(s)
|
||||
# endif
|
||||
# ifdef HAVE_DEV_URANDOM
|
||||
# define RANDFILE "/dev/urandom"
|
||||
# else
|
||||
# ifdef HAVE_DEV_RANDOM
|
||||
# define RANDFILE "/dev/random"
|
||||
# else
|
||||
# define RANDFILE (NULL)
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
unsigned short rand16(void)
|
||||
{
|
||||
static int been_seeded = 0;
|
||||
const char *randfile = RANDFILE;
|
||||
|
||||
if (! been_seeded)
|
||||
{
|
||||
int fd, n = 0;
|
||||
unsigned int c = 0, seed = 0, badseed;
|
||||
char sbuf[sizeof(seed)];
|
||||
char *s;
|
||||
struct timeval now;
|
||||
|
||||
/* get the bad seed as a backup */
|
||||
/* (but we'd rather have something more random) */
|
||||
gettimeofday(&now, NULL);
|
||||
badseed = now.tv_sec ^ now.tv_usec ^ (getpid() << 16);
|
||||
|
||||
fd = open(randfile, O_RDONLY);
|
||||
if (fd < 0)
|
||||
seed = badseed;
|
||||
else
|
||||
{
|
||||
s = (char *) &seed;
|
||||
while ( (c < sizeof(seed)) &&
|
||||
((n = read(fd, sbuf, sizeof(seed)) > 0)) )
|
||||
{
|
||||
memcpy(s, sbuf, n);
|
||||
s += n;
|
||||
c += n;
|
||||
}
|
||||
if (n < 0)
|
||||
{
|
||||
seed = badseed;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
srand(seed);
|
||||
been_seeded = 1;
|
||||
}
|
||||
|
||||
/* Some rand() implementations have less randomness in low bits
|
||||
* than in high bits, so we only pay attention to the high ones.
|
||||
* But most implementations don't touch the high bit, so we
|
||||
* ignore that one.
|
||||
*/
|
||||
return( (unsigned short) (rand() >> 15) );
|
||||
}
|
||||
|
||||
int legal_char(char c)
|
||||
{
|
||||
/* check for legal char a-z A-Z 0-9 -
|
||||
(also / , used for RFC2317 and _ used in windows queries) */
|
||||
if ((c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '-' || c == '/' || c == '_')
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int canonicalise(char *s)
|
||||
{
|
||||
/* check for legal chars ans remove trailing . */
|
||||
int l = strlen(s);
|
||||
char c;
|
||||
|
||||
if (l>0 && s[l-1] == '.')
|
||||
s[l-1] = 0;
|
||||
|
||||
while ((c = *s++))
|
||||
if (c != '.' && !legal_char(c))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* for use during startup */
|
||||
void *safe_malloc(int size)
|
||||
{
|
||||
void *ret = malloc(size);
|
||||
|
||||
if (!ret)
|
||||
die("could not get memory", NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *safe_string_alloc(char *cp)
|
||||
{
|
||||
char *ret = NULL;
|
||||
|
||||
if (cp && strlen(cp) != 0)
|
||||
{
|
||||
ret = safe_malloc(strlen(cp)+1);
|
||||
strcpy(ret, cp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void complain(char *message, char *arg1)
|
||||
{
|
||||
char *errmess = strerror(errno);
|
||||
|
||||
if (!arg1)
|
||||
arg1 = errmess;
|
||||
|
||||
fprintf(stderr, "dnsmasq: ");
|
||||
fprintf(stderr, message, arg1, errmess);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
syslog(LOG_CRIT, message, arg1, errmess);
|
||||
}
|
||||
|
||||
void die(char *message, char *arg1)
|
||||
{
|
||||
complain(message, arg1);
|
||||
syslog(LOG_CRIT, "FAILED to start up");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
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 &&
|
||||
memcmp(&s1->in.sin_addr, &s2->in.sin_addr, sizeof(struct in_addr)) == 0)
|
||||
return 1;
|
||||
#ifdef HAVE_IPV6
|
||||
if (s1->sa.sa_family == AF_INET6 &&
|
||||
s1->in6.sin6_port == s2->in6.sin6_port &&
|
||||
s1->in6.sin6_flowinfo == s2->in6.sin6_flowinfo &&
|
||||
memcmp(&s1->in6.sin6_addr, &s2->in6.sin6_addr, sizeof(struct in6_addr)) == 0)
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sa_len(union mysockaddr *addr)
|
||||
{
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
return addr->sa.sa_len;
|
||||
#else
|
||||
#ifdef HAVE_IPV6
|
||||
if (addr->sa.sa_family == AF_INET6)
|
||||
return sizeof(addr->in6);
|
||||
else
|
||||
#endif
|
||||
return sizeof(addr->in);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* don't use strcasecmp and friends here - they may be messed up by LOCALE */
|
||||
int hostname_isequal(unsigned char *a, unsigned char *b)
|
||||
{
|
||||
unsigned int c1, c2;
|
||||
|
||||
do {
|
||||
c1 = *a++;
|
||||
c2 = *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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user