933 lines
18 KiB
C
Executable file
933 lines
18 KiB
C
Executable file
#include <u.h>
|
|
#include <libc.h>
|
|
#include <bio.h>
|
|
#include <ndb.h>
|
|
#include <ip.h>
|
|
#include "dns.h"
|
|
|
|
static Ndb *db;
|
|
|
|
static RR* dblookup1(char*, int, int, int);
|
|
static RR* addrrr(Ndbtuple*, Ndbtuple*);
|
|
static RR* nsrr(Ndbtuple*, Ndbtuple*);
|
|
static RR* cnamerr(Ndbtuple*, Ndbtuple*);
|
|
static RR* mxrr(Ndbtuple*, Ndbtuple*);
|
|
static RR* soarr(Ndbtuple*, Ndbtuple*);
|
|
static RR* ptrrr(Ndbtuple*, Ndbtuple*);
|
|
static Ndbtuple* look(Ndbtuple*, Ndbtuple*, char*);
|
|
static RR* doaxfr(Ndb*, char*);
|
|
static RR* nullrr(Ndbtuple *entry, Ndbtuple *pair);
|
|
static RR* txtrr(Ndbtuple *entry, Ndbtuple *pair);
|
|
static Lock dblock;
|
|
static void createptrs(void);
|
|
|
|
static int implemented[Tall] =
|
|
{
|
|
[Ta] 1,
|
|
[Tns] 1,
|
|
[Tsoa] 1,
|
|
[Tmx] 1,
|
|
[Tptr] 1,
|
|
[Tcname] 1,
|
|
[Tnull] 1,
|
|
[Ttxt] 1,
|
|
};
|
|
|
|
static void
|
|
nstrcpy(char *to, char *from, int len)
|
|
{
|
|
strncpy(to, from, len);
|
|
to[len-1] = 0;
|
|
}
|
|
|
|
int
|
|
opendatabase(void)
|
|
{
|
|
char buf[256];
|
|
Ndb *xdb;
|
|
|
|
if(db == nil){
|
|
snprint(buf, sizeof(buf), "%s/ndb", mntpt);
|
|
xdb = ndbopen(dbfile);
|
|
if(xdb != nil)
|
|
xdb->nohash = 1;
|
|
db = ndbcat(ndbopen(buf), xdb);
|
|
}
|
|
if(db == nil)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* lookup an RR in the network database, look for matches
|
|
* against both the domain name and the wildcarded domain name.
|
|
*
|
|
* the lock makes sure only one process can be accessing the data
|
|
* base at a time. This is important since there's a lot of
|
|
* shared state there.
|
|
*
|
|
* e.g. for x.research.bell-labs.com, first look for a match against
|
|
* the x.research.bell-labs.com. If nothing matches, try *.research.bell-labs.com.
|
|
*/
|
|
RR*
|
|
dblookup(char *name, int class, int type, int auth, int ttl)
|
|
{
|
|
RR *rp, *tp;
|
|
char buf[256];
|
|
char *wild, *cp;
|
|
DN *dp, *ndp;
|
|
int err;
|
|
|
|
/* so far only internet lookups are implemented */
|
|
if(class != Cin)
|
|
return 0;
|
|
|
|
err = Rname;
|
|
|
|
if(type == Tall){
|
|
rp = 0;
|
|
for (type = Ta; type < Tall; type++)
|
|
if(implemented[type])
|
|
rrcat(&rp, dblookup(name, class, type, auth, ttl));
|
|
return rp;
|
|
}
|
|
|
|
lock(&dblock);
|
|
dp = dnlookup(name, class, 1);
|
|
if(opendatabase() < 0)
|
|
goto out;
|
|
if(dp->rr)
|
|
err = 0;
|
|
|
|
/* first try the given name */
|
|
rp = 0;
|
|
if(cachedb)
|
|
rp = rrlookup(dp, type, NOneg);
|
|
else
|
|
rp = dblookup1(name, type, auth, ttl);
|
|
if(rp)
|
|
goto out;
|
|
|
|
/* try lower case version */
|
|
for(cp = name; *cp; cp++)
|
|
*cp = tolower(*cp);
|
|
if(cachedb)
|
|
rp = rrlookup(dp, type, NOneg);
|
|
else
|
|
rp = dblookup1(name, type, auth, ttl);
|
|
if(rp)
|
|
goto out;
|
|
|
|
/* walk the domain name trying the wildcard '*' at each position */
|
|
for(wild = strchr(name, '.'); wild; wild = strchr(wild+1, '.')){
|
|
snprint(buf, sizeof(buf), "*%s", wild);
|
|
ndp = dnlookup(buf, class, 1);
|
|
if(ndp->rr)
|
|
err = 0;
|
|
if(cachedb)
|
|
rp = rrlookup(ndp, type, NOneg);
|
|
else
|
|
rp = dblookup1(buf, type, auth, ttl);
|
|
if(rp)
|
|
break;
|
|
}
|
|
out:
|
|
/* add owner to uncached records */
|
|
if(rp){
|
|
for(tp = rp; tp; tp = tp->next)
|
|
tp->owner = dp;
|
|
} else {
|
|
/* don't call it non-existent if it's not ours */
|
|
if(err == Rname && !inmyarea(name))
|
|
err = Rserver;
|
|
dp->nonexistent = err;
|
|
}
|
|
|
|
unlock(&dblock);
|
|
return rp;
|
|
}
|
|
|
|
/*
|
|
* lookup an RR in the network database
|
|
*/
|
|
static RR*
|
|
dblookup1(char *name, int type, int auth, int ttl)
|
|
{
|
|
Ndbtuple *t, *nt;
|
|
RR *rp, *list, **l;
|
|
Ndbs s;
|
|
char dname[Domlen];
|
|
char *attr;
|
|
DN *dp;
|
|
RR *(*f)(Ndbtuple*, Ndbtuple*);
|
|
int found, x;
|
|
|
|
dp = 0;
|
|
switch(type){
|
|
case Tptr:
|
|
attr = "ptr";
|
|
f = ptrrr;
|
|
break;
|
|
case Ta:
|
|
attr = "ip";
|
|
f = addrrr;
|
|
break;
|
|
case Tnull:
|
|
attr = "nullrr";
|
|
f = nullrr;
|
|
break;
|
|
case Tns:
|
|
attr = "ns";
|
|
f = nsrr;
|
|
break;
|
|
case Tsoa:
|
|
attr = "soa";
|
|
f = soarr;
|
|
break;
|
|
case Tmx:
|
|
attr = "mx";
|
|
f = mxrr;
|
|
break;
|
|
case Tcname:
|
|
attr = "cname";
|
|
f = cnamerr;
|
|
break;
|
|
case Taxfr:
|
|
case Tixfr:
|
|
return doaxfr(db, name);
|
|
default:
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
* find a matching entry in the database
|
|
*/
|
|
free(ndbgetvalue(db, &s, "dom", name, attr, &t));
|
|
|
|
/*
|
|
* hack for local names
|
|
*/
|
|
if(t == 0 && strchr(name, '.') == 0)
|
|
free(ndbgetvalue(db, &s, "sys", name, attr, &t));
|
|
if(t == 0)
|
|
return nil;
|
|
|
|
/* search whole entry for default domain name */
|
|
strncpy(dname, name, sizeof dname);
|
|
for(nt = t; nt; nt = nt->entry)
|
|
if(strcmp(nt->attr, "dom") == 0){
|
|
nstrcpy(dname, nt->val, sizeof dname);
|
|
break;
|
|
}
|
|
|
|
/* ttl is maximum of soa minttl and entry's ttl ala rfc883 */
|
|
nt = look(t, s.t, "ttl");
|
|
if(nt){
|
|
x = atoi(nt->val);
|
|
if(x > ttl)
|
|
ttl = x;
|
|
}
|
|
|
|
/* default ttl is one day */
|
|
if(ttl < 0)
|
|
ttl = DEFTTL;
|
|
|
|
/*
|
|
* The database has 2 levels of precedence; line and entry.
|
|
* Pairs on the same line bind tighter than pairs in the
|
|
* same entry, so we search the line first.
|
|
*/
|
|
found = 0;
|
|
list = 0;
|
|
l = &list;
|
|
for(nt = s.t;; ){
|
|
if(found == 0 && strcmp(nt->attr, "dom") == 0){
|
|
nstrcpy(dname, nt->val, sizeof dname);
|
|
found = 1;
|
|
}
|
|
if(cistrcmp(attr, nt->attr) == 0){
|
|
rp = (*f)(t, nt);
|
|
rp->auth = auth;
|
|
rp->db = 1;
|
|
if(ttl)
|
|
rp->ttl = ttl;
|
|
if(dp == 0)
|
|
dp = dnlookup(dname, Cin, 1);
|
|
rp->owner = dp;
|
|
*l = rp;
|
|
l = &rp->next;
|
|
nt->ptr = 1;
|
|
}
|
|
nt = nt->line;
|
|
if(nt == s.t)
|
|
break;
|
|
}
|
|
|
|
/* search whole entry */
|
|
for(nt = t; nt; nt = nt->entry)
|
|
if(nt->ptr == 0 && cistrcmp(attr, nt->attr) == 0){
|
|
rp = (*f)(t, nt);
|
|
rp->db = 1;
|
|
if(ttl)
|
|
rp->ttl = ttl;
|
|
rp->auth = auth;
|
|
if(dp == 0)
|
|
dp = dnlookup(dname, Cin, 1);
|
|
rp->owner = dp;
|
|
*l = rp;
|
|
l = &rp->next;
|
|
}
|
|
ndbfree(t);
|
|
|
|
return list;
|
|
}
|
|
|
|
/*
|
|
* make various types of resource records from a database entry
|
|
*/
|
|
static RR*
|
|
addrrr(Ndbtuple *entry, Ndbtuple *pair)
|
|
{
|
|
RR *rp;
|
|
uchar addr[IPaddrlen];
|
|
|
|
USED(entry);
|
|
parseip(addr, pair->val);
|
|
if(isv4(addr))
|
|
rp = rralloc(Ta);
|
|
else
|
|
rp = rralloc(Taaaa);
|
|
rp->ip = dnlookup(pair->val, Cin, 1);
|
|
return rp;
|
|
}
|
|
static RR*
|
|
nullrr(Ndbtuple *entry, Ndbtuple *pair)
|
|
{
|
|
RR *rp;
|
|
|
|
USED(entry);
|
|
rp = rralloc(Tnull);
|
|
rp->null->data = (uchar*)estrdup(pair->val);
|
|
rp->null->dlen = strlen((char*)rp->null->data);
|
|
return rp;
|
|
}
|
|
/*
|
|
* txt rr strings are at most 255 bytes long. one
|
|
* can represent longer strings by multiple concatenated
|
|
* <= 255 byte ones.
|
|
*/
|
|
static RR*
|
|
txtrr(Ndbtuple *entry, Ndbtuple *pair)
|
|
{
|
|
RR *rp;
|
|
Txt *t, **l;
|
|
int i, len, sofar;
|
|
|
|
USED(entry);
|
|
rp = rralloc(Ttxt);
|
|
l = &rp->txt;
|
|
rp->txt = nil;
|
|
len = strlen(pair->val);
|
|
sofar = 0;
|
|
while(len > sofar){
|
|
t = emalloc(sizeof(*t));
|
|
t->next = nil;
|
|
|
|
i = len-sofar;
|
|
if(i > 255)
|
|
i = 255;
|
|
|
|
t->p = emalloc(i+1);
|
|
memmove(t->p, pair->val+sofar, i);
|
|
t->p[i] = 0;
|
|
sofar += i;
|
|
|
|
*l = t;
|
|
l = &t->next;
|
|
}
|
|
return rp;
|
|
}
|
|
static RR*
|
|
cnamerr(Ndbtuple *entry, Ndbtuple *pair)
|
|
{
|
|
RR *rp;
|
|
|
|
USED(entry);
|
|
rp = rralloc(Tcname);
|
|
rp->host = dnlookup(pair->val, Cin, 1);
|
|
return rp;
|
|
}
|
|
static RR*
|
|
mxrr(Ndbtuple *entry, Ndbtuple *pair)
|
|
{
|
|
RR * rp;
|
|
|
|
rp = rralloc(Tmx);
|
|
rp->host = dnlookup(pair->val, Cin, 1);
|
|
pair = look(entry, pair, "pref");
|
|
if(pair)
|
|
rp->pref = atoi(pair->val);
|
|
else
|
|
rp->pref = 1;
|
|
return rp;
|
|
}
|
|
static RR*
|
|
nsrr(Ndbtuple *entry, Ndbtuple *pair)
|
|
{
|
|
RR *rp;
|
|
Ndbtuple *t;
|
|
|
|
rp = rralloc(Tns);
|
|
rp->host = dnlookup(pair->val, Cin, 1);
|
|
t = look(entry, pair, "soa");
|
|
if(t && t->val[0] == 0)
|
|
rp->local = 1;
|
|
return rp;
|
|
}
|
|
static RR*
|
|
ptrrr(Ndbtuple *entry, Ndbtuple *pair)
|
|
{
|
|
RR *rp;
|
|
|
|
USED(entry);
|
|
rp = rralloc(Tns);
|
|
rp->ptr = dnlookup(pair->val, Cin, 1);
|
|
return rp;
|
|
}
|
|
static RR*
|
|
soarr(Ndbtuple *entry, Ndbtuple *pair)
|
|
{
|
|
RR *rp;
|
|
Ndbtuple *ns, *mb, *t;
|
|
char mailbox[Domlen];
|
|
Ndb *ndb;
|
|
char *p;
|
|
|
|
rp = rralloc(Tsoa);
|
|
rp->soa->serial = 1;
|
|
for(ndb = db; ndb; ndb = ndb->next)
|
|
if(ndb->mtime > rp->soa->serial)
|
|
rp->soa->serial = ndb->mtime;
|
|
rp->soa->refresh = Day;
|
|
rp->soa->retry = Hour;
|
|
rp->soa->expire = Day;
|
|
rp->soa->minttl = Day;
|
|
t = look(entry, pair, "ttl");
|
|
if(t)
|
|
rp->soa->minttl = atoi(t->val);
|
|
t = look(entry, pair, "refresh");
|
|
if(t)
|
|
rp->soa->refresh = atoi(t->val);
|
|
t = look(entry, pair, "serial");
|
|
if(t)
|
|
rp->soa->serial = strtoul(t->val, 0, 10);
|
|
|
|
ns = look(entry, pair, "ns");
|
|
if(ns == 0)
|
|
ns = look(entry, pair, "dom");
|
|
rp->host = dnlookup(ns->val, Cin, 1);
|
|
|
|
/* accept all of:
|
|
* mbox=person
|
|
* mbox=person@machine.dom
|
|
* mbox=person.machine.dom
|
|
*/
|
|
mb = look(entry, pair, "mbox");
|
|
if(mb == nil)
|
|
mb = look(entry, pair, "mb");
|
|
if(mb){
|
|
if(strchr(mb->val, '.')) {
|
|
p = strchr(mb->val, '@');
|
|
if(p != nil)
|
|
*p = '.';
|
|
rp->rmb = dnlookup(mb->val, Cin, 1);
|
|
} else {
|
|
snprint(mailbox, sizeof(mailbox), "%s.%s",
|
|
mb->val, ns->val);
|
|
rp->rmb = dnlookup(mailbox, Cin, 1);
|
|
}
|
|
} else {
|
|
snprint(mailbox, sizeof(mailbox), "postmaster.%s",
|
|
ns->val);
|
|
rp->rmb = dnlookup(mailbox, Cin, 1);
|
|
}
|
|
|
|
/* hang dns slaves off of the soa. this is
|
|
* for managing the area.
|
|
*/
|
|
for(t = entry; t != nil; t = t->entry)
|
|
if(strcmp(t->attr, "dnsslave") == 0)
|
|
addserver(&rp->soa->slaves, t->val);
|
|
|
|
return rp;
|
|
}
|
|
|
|
/*
|
|
* Look for a pair with the given attribute. look first on the same line,
|
|
* then in the whole entry.
|
|
*/
|
|
static Ndbtuple*
|
|
look(Ndbtuple *entry, Ndbtuple *line, char *attr)
|
|
{
|
|
Ndbtuple *nt;
|
|
|
|
/* first look on same line (closer binding) */
|
|
for(nt = line;;){
|
|
if(cistrcmp(attr, nt->attr) == 0)
|
|
return nt;
|
|
nt = nt->line;
|
|
if(nt == line)
|
|
break;
|
|
}
|
|
/* search whole tuple */
|
|
for(nt = entry; nt; nt = nt->entry)
|
|
if(cistrcmp(attr, nt->attr) == 0)
|
|
return nt;
|
|
return 0;
|
|
}
|
|
|
|
/* these are answered specially by the tcp version */
|
|
static RR*
|
|
doaxfr(Ndb *db, char *name)
|
|
{
|
|
USED(db);
|
|
USED(name);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* read the all the soa's from the database to determine area's.
|
|
* this is only used when we're not caching the database.
|
|
*/
|
|
static void
|
|
dbfile2area(Ndb *db)
|
|
{
|
|
Ndbtuple *t;
|
|
|
|
if(debug)
|
|
syslog(0, logfile, "rereading %s", db->file);
|
|
Bseek(&db->b, 0, 0);
|
|
while(t = ndbparse(db)){
|
|
ndbfree(t);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* read the database into the cache
|
|
*/
|
|
static void
|
|
dbpair2cache(DN *dp, Ndbtuple *entry, Ndbtuple *pair)
|
|
{
|
|
RR *rp;
|
|
Ndbtuple *t;
|
|
static ulong ord;
|
|
|
|
rp = 0;
|
|
if(cistrcmp(pair->attr, "ip") == 0){
|
|
dp->ordinal = ord++;
|
|
rp = addrrr(entry, pair);
|
|
} else if(cistrcmp(pair->attr, "ns") == 0){
|
|
rp = nsrr(entry, pair);
|
|
} else if(cistrcmp(pair->attr, "soa") == 0){
|
|
rp = soarr(entry, pair);
|
|
addarea(dp, rp, pair);
|
|
} else if(cistrcmp(pair->attr, "mx") == 0){
|
|
rp = mxrr(entry, pair);
|
|
} else if(cistrcmp(pair->attr, "cname") == 0){
|
|
rp = cnamerr(entry, pair);
|
|
} else if(cistrcmp(pair->attr, "nullrr") == 0){
|
|
rp = nullrr(entry, pair);
|
|
} else if(cistrcmp(pair->attr, "txtrr") == 0){
|
|
rp = txtrr(entry, pair);
|
|
}
|
|
|
|
if(rp == 0)
|
|
return;
|
|
|
|
rp->owner = dp;
|
|
rp->db = 1;
|
|
t = look(entry, pair, "ttl");
|
|
if(t)
|
|
rp->ttl = atoi(t->val);
|
|
rrattach(rp, 0);
|
|
}
|
|
static void
|
|
dbtuple2cache(Ndbtuple *t)
|
|
{
|
|
Ndbtuple *et, *nt;
|
|
DN *dp;
|
|
|
|
for(et = t; et; et = et->entry){
|
|
if(strcmp(et->attr, "dom") == 0){
|
|
dp = dnlookup(et->val, Cin, 1);
|
|
|
|
/* first same line */
|
|
for(nt = et->line; nt != et; nt = nt->line){
|
|
dbpair2cache(dp, t, nt);
|
|
nt->ptr = 1;
|
|
}
|
|
|
|
/* then rest of entry */
|
|
for(nt = t; nt; nt = nt->entry){
|
|
if(nt->ptr == 0)
|
|
dbpair2cache(dp, t, nt);
|
|
nt->ptr = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
static void
|
|
dbfile2cache(Ndb *db)
|
|
{
|
|
Ndbtuple *t;
|
|
|
|
if(debug)
|
|
syslog(0, logfile, "rereading %s", db->file);
|
|
Bseek(&db->b, 0, 0);
|
|
while(t = ndbparse(db)){
|
|
dbtuple2cache(t);
|
|
ndbfree(t);
|
|
}
|
|
}
|
|
void
|
|
db2cache(int doit)
|
|
{
|
|
Ndb *ndb;
|
|
Dir *d;
|
|
ulong youngest, temp;
|
|
static ulong lastcheck;
|
|
static ulong lastyoungest;
|
|
|
|
/* no faster than once every 2 minutes */
|
|
if(now < lastcheck + 2*Min && !doit)
|
|
return;
|
|
|
|
refresh_areas(owned);
|
|
|
|
lock(&dblock);
|
|
|
|
if(opendatabase() < 0){
|
|
unlock(&dblock);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* file may be changing as we are reading it, so loop till
|
|
* mod times are consistent.
|
|
*
|
|
* we don't use the times in the ndb records because they may
|
|
* change outside of refreshing our cached knowledge.
|
|
*/
|
|
for(;;){
|
|
lastcheck = now;
|
|
youngest = 0;
|
|
for(ndb = db; ndb; ndb = ndb->next){
|
|
/* the dirfstat avoids walking the mount table each time */
|
|
if((d = dirfstat(Bfildes(&ndb->b))) != nil ||
|
|
(d = dirstat(ndb->file)) != nil){
|
|
temp = d->mtime; /* ulong vs int crap */
|
|
if(temp > youngest)
|
|
youngest = temp;
|
|
free(d);
|
|
}
|
|
}
|
|
if(!doit && youngest == lastyoungest){
|
|
unlock(&dblock);
|
|
return;
|
|
}
|
|
|
|
/* forget our area definition */
|
|
freearea(&owned);
|
|
freearea(&delegated);
|
|
|
|
/* reopen all the files (to get oldest for time stamp) */
|
|
for(ndb = db; ndb; ndb = ndb->next)
|
|
ndbreopen(ndb);
|
|
|
|
if(cachedb){
|
|
/* mark all db records as timed out */
|
|
dnagedb();
|
|
|
|
/* read in new entries */
|
|
for(ndb = db; ndb; ndb = ndb->next)
|
|
dbfile2cache(ndb);
|
|
|
|
/* mark as authentic anything in our domain */
|
|
dnauthdb();
|
|
|
|
/* remove old entries */
|
|
dnageall(1);
|
|
} else {
|
|
/* read all the soa's to get database defaults */
|
|
for(ndb = db; ndb; ndb = ndb->next)
|
|
dbfile2area(ndb);
|
|
}
|
|
|
|
doit = 0;
|
|
lastyoungest = youngest;
|
|
createptrs();
|
|
}
|
|
|
|
unlock(&dblock);
|
|
}
|
|
|
|
extern uchar ipaddr[IPaddrlen];
|
|
|
|
/*
|
|
* get all my xxx
|
|
*/
|
|
Ndbtuple*
|
|
lookupinfo(char *attr)
|
|
{
|
|
char buf[64];
|
|
char *a[2];
|
|
static Ndbtuple *t;
|
|
|
|
snprint(buf, sizeof buf, "%I", ipaddr);
|
|
a[0] = attr;
|
|
|
|
lock(&dblock);
|
|
if(opendatabase() < 0){
|
|
unlock(&dblock);
|
|
return nil;
|
|
}
|
|
t = ndbipinfo(db, "ip", buf, a, 1);
|
|
unlock(&dblock);
|
|
return t;
|
|
}
|
|
|
|
char *localservers = "local#dns#servers";
|
|
char *localserverprefix = "local#dns#server";
|
|
|
|
/*
|
|
* return non-zero is this is a bad delegation
|
|
*/
|
|
int
|
|
baddelegation(RR *rp, RR *nsrp, uchar *addr)
|
|
{
|
|
Ndbtuple *nt;
|
|
static Ndbtuple *t;
|
|
|
|
if(t == nil)
|
|
t = lookupinfo("dom");
|
|
if(t == nil)
|
|
return 0;
|
|
|
|
for(; rp; rp = rp->next){
|
|
if(rp->type != Tns)
|
|
continue;
|
|
|
|
/* see if delegation is looping */
|
|
if(nsrp)
|
|
if(rp->owner != nsrp->owner)
|
|
if(subsume(rp->owner->name, nsrp->owner->name) &&
|
|
strcmp(nsrp->owner->name, localservers) != 0){
|
|
syslog(0, logfile, "delegation loop %R -> %R from %I", nsrp, rp, addr);
|
|
return 1;
|
|
}
|
|
|
|
/* see if delegating to us what we don't own */
|
|
for(nt = t; nt != nil; nt = nt->entry)
|
|
if(rp->host && cistrcmp(rp->host->name, nt->val) == 0)
|
|
break;
|
|
if(nt != nil && !inmyarea(rp->owner->name)){
|
|
syslog(0, logfile, "bad delegation %R from %I", rp, addr);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
addlocaldnsserver(DN *dp, int class, char *ipaddr, int i)
|
|
{
|
|
DN *nsdp;
|
|
RR *rp;
|
|
char buf[32];
|
|
|
|
/* ns record for name server, make up an impossible name */
|
|
rp = rralloc(Tns);
|
|
snprint(buf, sizeof(buf), "%s%d", localserverprefix, i);
|
|
nsdp = dnlookup(buf, class, 1);
|
|
rp->host = nsdp;
|
|
rp->owner = dp;
|
|
rp->local = 1;
|
|
rp->db = 1;
|
|
rp->ttl = 10*Min;
|
|
rrattach(rp, 1);
|
|
|
|
print("dns %s\n", ipaddr);
|
|
/* A record */
|
|
rp = rralloc(Ta);
|
|
rp->ip = dnlookup(ipaddr, class, 1);
|
|
rp->owner = nsdp;
|
|
rp->local = 1;
|
|
rp->db = 1;
|
|
rp->ttl = 10*Min;
|
|
rrattach(rp, 1);
|
|
}
|
|
|
|
/*
|
|
* return list of dns server addresses to use when
|
|
* acting just as a resolver.
|
|
*/
|
|
RR*
|
|
dnsservers(int class)
|
|
{
|
|
Ndbtuple *t, *nt;
|
|
RR *nsrp;
|
|
DN *dp;
|
|
char *p;
|
|
int i, n;
|
|
char *buf, *args[5];
|
|
|
|
dp = dnlookup(localservers, class, 1);
|
|
nsrp = rrlookup(dp, Tns, NOneg);
|
|
if(nsrp != nil)
|
|
return nsrp;
|
|
|
|
p = getenv("DNSSERVER");
|
|
if(p != nil){
|
|
buf = estrdup(p);
|
|
n = tokenize(buf, args, nelem(args));
|
|
for(i = 0; i < n; i++)
|
|
addlocaldnsserver(dp, class, args[i], i);
|
|
free(buf);
|
|
} else {
|
|
t = lookupinfo("@dns");
|
|
if(t == nil)
|
|
return nil;
|
|
i = 0;
|
|
for(nt = t; nt != nil; nt = nt->entry){
|
|
addlocaldnsserver(dp, class, nt->val, i);
|
|
i++;
|
|
}
|
|
ndbfree(t);
|
|
}
|
|
|
|
return rrlookup(dp, Tns, NOneg);
|
|
}
|
|
|
|
static void
|
|
addlocaldnsdomain(DN *dp, int class, char *domain)
|
|
{
|
|
RR *rp;
|
|
|
|
/* A record */
|
|
rp = rralloc(Tptr);
|
|
rp->ptr = dnlookup(domain, class, 1);
|
|
rp->owner = dp;
|
|
rp->db = 1;
|
|
rp->ttl = 10*Min;
|
|
rrattach(rp, 1);
|
|
}
|
|
|
|
/*
|
|
* return list of domains to use when resolving names without '.'s
|
|
*/
|
|
RR*
|
|
domainlist(int class)
|
|
{
|
|
Ndbtuple *t, *nt;
|
|
RR *rp;
|
|
DN *dp;
|
|
|
|
dp = dnlookup("local#dns#domains", class, 1);
|
|
rp = rrlookup(dp, Tptr, NOneg);
|
|
if(rp != nil)
|
|
return rp;
|
|
|
|
t = lookupinfo("dnsdomain");
|
|
if(t == nil)
|
|
return nil;
|
|
for(nt = t; nt != nil; nt = nt->entry)
|
|
addlocaldnsdomain(dp, class, nt->val);
|
|
ndbfree(t);
|
|
|
|
return rrlookup(dp, Tptr, NOneg);
|
|
}
|
|
|
|
char *v4ptrdom = ".in-addr.arpa";
|
|
char *v6ptrdom = ".ip6.arpa"; /* ip6.int deprecated, rfc 3152 */
|
|
|
|
char *attribs[] = {
|
|
"ipmask",
|
|
0
|
|
};
|
|
|
|
/*
|
|
* create ptrs that are in our areas
|
|
*/
|
|
static void
|
|
createptrs(void)
|
|
{
|
|
int len, dlen, n;
|
|
Area *s;
|
|
char *f[40];
|
|
char buf[Domlen+1];
|
|
uchar net[IPaddrlen];
|
|
uchar mask[IPaddrlen];
|
|
char ipa[48];
|
|
Ndbtuple *t, *nt;
|
|
|
|
dlen = strlen(v4ptrdom);
|
|
for(s = owned; s; s = s->next){
|
|
len = strlen(s->soarr->owner->name);
|
|
if(len <= dlen)
|
|
continue;
|
|
if(cistrcmp(s->soarr->owner->name+len-dlen, v4ptrdom) != 0)
|
|
continue;
|
|
|
|
/* get mask and net value */
|
|
strncpy(buf, s->soarr->owner->name, sizeof(buf));
|
|
buf[sizeof(buf)-1] = 0;
|
|
n = getfields(buf, f, nelem(f), 0, ".");
|
|
memset(mask, 0xff, IPaddrlen);
|
|
ipmove(net, v4prefix);
|
|
switch(n){
|
|
case 3: /* /8 */
|
|
net[IPv4off] = atoi(f[0]);
|
|
mask[IPv4off+1] = 0;
|
|
mask[IPv4off+2] = 0;
|
|
mask[IPv4off+3] = 0;
|
|
break;
|
|
case 4: /* /16 */
|
|
net[IPv4off] = atoi(f[1]);
|
|
net[IPv4off+1] = atoi(f[0]);
|
|
mask[IPv4off+2] = 0;
|
|
mask[IPv4off+3] = 0;
|
|
break;
|
|
case 5: /* /24 */
|
|
net[IPv4off] = atoi(f[2]);
|
|
net[IPv4off+1] = atoi(f[1]);
|
|
net[IPv4off+2] = atoi(f[0]);
|
|
mask[IPv4off+3] = 0;
|
|
break;
|
|
case 6: /* rfc2317 */
|
|
net[IPv4off] = atoi(f[3]);
|
|
net[IPv4off+1] = atoi(f[2]);
|
|
net[IPv4off+2] = atoi(f[1]);
|
|
net[IPv4off+3] = atoi(f[0]);
|
|
sprint(ipa, "%I", net);
|
|
t = ndbipinfo(db, "ip", ipa, attribs, 1);
|
|
if(t == nil) /* could be a reverse with no forward */
|
|
continue;
|
|
nt = look(t, t, "ipmask");
|
|
if(nt == nil){ /* we're confused */
|
|
ndbfree(t);
|
|
continue;
|
|
}
|
|
parseipmask(mask, nt->val);
|
|
n = 5;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
/* go through all domain entries looking for RR's in this network and create ptrs */
|
|
dnptr(net, mask, s->soarr->owner->name, 6-n, 0);
|
|
}
|
|
}
|