#include <u.h>
#include <libc.h>
#include <ip.h>
-#include <pool.h>
#include <ctype.h>
#include "dns.h"
* figure it out.
*/
enum {
- Deftarget = 1<<30, /* effectively disable aging */
- Minage = 1<<30,
- Defagefreq = 1<<30, /* age names this often (seconds) */
-
/* these settings will trigger frequent aging */
-// Deftarget = 4000,
-// Minage = 5*60,
-// Defagefreq = 15*60, /* age names this often (seconds) */
+ Deftarget = 4000,
+ Minage = 5*Min,
+ Defagefreq = 15*Min, /* age names this often (seconds) */
};
/*
dp = emalloc(sizeof(*dp));
dp->magic = DNmagic;
dp->name = estrdup(name);
- assert(dp->name != nil);
dp->class = class;
- dp->rr = 0;
+ dp->rr = nil;
dp->referenced = now;
/* add new DN to tail of the hash list. *l points to last next ptr. */
dp->next = nil;
return dp;
}
+DN*
+idnlookup(char *name, int class, int enter)
+{
+ char dom[Domlen];
+
+ if(utf2idn(name, dom, sizeof dom) != nil)
+ name = dom;
+ return dnlookup(name, class, enter);
+}
+
+DN*
+ipalookup(uchar *ip, int class, int enter)
+{
+ char addr[64];
+
+ snprint(addr, sizeof(addr), "%I", ip);
+ return dnlookup(addr, class, enter);
+}
+
static int
rrsame(RR *rr1, RR *rr2)
{
for(dp = ht[i]; dp; dp = dp->next){
fprint(fd, "%s\n", dp->name);
for(rp = dp->rr; rp; rp = rp->next) {
- fprint(fd, "\t%R %c%c %lud/%lud\n",
+ fprint(fd, "\t%R %c%c %ld/%lud\n",
rp, rp->auth? 'A': 'U',
- rp->db? 'D': 'N', rp->expire, rp->ttl);
+ rp->db? 'D': 'N', (long)(rp->expire - now), rp->ttl);
if (rronlist(rp, rp->next))
fprint(fd, "*** duplicate:\n");
}
void
dnage(DN *dp)
{
- RR **l;
- RR *rp, *next;
+ RR **l, *rp;
ulong diff;
if (canlock(&dnlock))
abort(); /* dnage called with dnlock not held */
diff = now - dp->referenced;
- if(diff < Reserved || dp->keep)
+ if(diff < Reserved || dp->mark != 0)
return;
l = &dp->rr;
- for(rp = dp->rr; rp; rp = next){
+ while ((rp = *l) != nil){
assert(rp->magic == RRmagic && rp->cached);
- next = rp->next;
- if(!rp->db && (rp->expire < now || diff > dnvars.oldest))
+ if(!rp->db && ((long)(rp->expire - now) <= 0 || diff > dnvars.oldest))
rrdelhead(l); /* rp == *l before; *l == rp->next after */
else
l = &rp->next;
}
}
-#define MARK(dp) { if (dp) (dp)->keep = 1; }
+#define MARK(dp) { if (dp) (dp)->mark |= 2; }
/* mark a domain name and those in its RRs as never to be aged */
void
-dnagenever(DN *dp, int dolock)
+dnagenever(DN *dp)
{
RR *rp;
- if (dolock)
- lock(&dnlock);
+ lock(&dnlock);
/* mark all referenced domain names */
MARK(dp);
MARK(rp->host);
MARK(rp->rmb);
break;
+ case Tsig:
+ MARK(rp->sig->signer);
+ break;
}
}
- if (dolock)
- unlock(&dnlock);
-}
-
-/* mark all current domain names as never to be aged */
-void
-dnageallnever(void)
-{
- int i;
- DN *dp;
-
- lock(&dnlock);
-
- /* mark all referenced domain names */
- for(i = 0; i < HTLEN; i++)
- for(dp = ht[i]; dp; dp = dp->next)
- dnagenever(dp, 0);
-
unlock(&dnlock);
-
- dnslog("%ld initial domain names; target is %ld", dnvars.names, target);
- if(dnvars.names >= target)
- dnslog("more initial domain names (%ld) than target (%ld)",
- dnvars.names, target);
}
-#define REF(dp) { if (dp) (dp)->refs++; }
+#define REF(dp) { if (dp) (dp)->mark |= 1; }
/*
* periodicly sweep for old records and remove unreferenced domain names
RR *rp;
static ulong nextage;
- if(dnvars.names < target || (now < nextage && !doit)){
+ if(dnvars.names < target || ((long)(nextage - now) > 0 && !doit)){
dnvars.oldest = maxage;
return;
}
if (agefreq > dnvars.oldest / 2)
nextage = now + dnvars.oldest / 2;
else
- nextage = now + agefreq;
+ nextage = now + (ulong)agefreq;
lock(&dnlock);
/* time out all old entries (and set refs to 0) */
for(i = 0; i < HTLEN; i++)
for(dp = ht[i]; dp; dp = dp->next){
- dp->refs = 0;
+ dp->mark &= ~1;
dnage(dp);
}
REF(rp->host);
REF(rp->rmb);
break;
+ case Tsig:
+ REF(rp->sig->signer);
+ break;
}
}
for(i = 0; i < HTLEN; i++){
l = &ht[i];
for(dp = *l; dp; dp = *l){
- if(dp->rr == 0 && dp->refs == 0 && !dp->keep){
+ if(dp->rr == nil && dp->mark == 0){
assert(dp->magic == DNmagic);
*l = dp->next;
- if(dp->name)
- free(dp->name);
- dp->magic = ~dp->magic;
- dnvars.names--;
+ free(dp->name);
memset(dp, 0, sizeof *dp); /* cause trouble */
+ dp->magic = ~DNmagic;
free(dp);
+ dnvars.names--;
continue;
}
l = &dp->next;
/* time out all database entries */
for(i = 0; i < HTLEN; i++)
for(dp = ht[i]; dp; dp = dp->next) {
- dp->keep = 0;
+ dp->mark = 0;
for(rp = dp->rr; rp; rp = rp->next)
if(rp->db)
rp->expire = 0;
/*
* mark all local db records about my area as authoritative,
- * time out any others
+ * delete timed out ones
*/
void
dnauthdb(void)
ulong minttl;
Area *area;
DN *dp;
- RR *rp;
+ RR *rp, **l;
lock(&dnlock);
for(i = 0; i < HTLEN; i++)
for(dp = ht[i]; dp; dp = dp->next){
area = inmyarea(dp->name);
- for(rp = dp->rr; rp; rp = rp->next)
+ l = &dp->rr;
+ for(rp = *l; rp; rp = *l){
if(rp->db){
+ if(rp->expire == 0){
+ rrdelhead(l);
+ continue;
+ }
if(area){
minttl = area->soarr->soa->minttl;
if(rp->ttl < minttl)
rp->ttl = minttl;
rp->auth = 1;
}
- if(rp->expire == 0){
- rp->db = 0;
- dp->referenced = now-Reserved-1;
- }
}
+ l = &rp->next;
+ }
}
unlock(&dnlock);
now = time(nil);
nowns = nsec();
req->id = ++dnvars.id;
+ req->aux = nil;
unlock(&dnvars);
return rv;
void
putactivity(int recursive)
{
- static ulong lastclean;
-
if(traceactivity)
dnslog("put: %d active by pid %d",
dnvars.active, getpid());
unlock(&dnvars);
db2cache(needrefresh);
+
dnageall(0);
/* let others back in */
- lastclean = now;
needrefresh = 0;
dnvars.mutex = 0;
}
RR **l;
RR *rp;
DN *dp;
+ ulong ttl;
assert(new->magic == RRmagic && !new->cached);
-// dnslog("rrattach1: %s", new->owner->name);
- if(!new->db) {
- /*
- * try not to let responses expire before we
- * can use them to complete this query, by extending
- * past (or nearly past) expiration time.
- */
- new->expire = new->ttl > now + Min? new->ttl: now + 10*Min;
- } else
- new->expire = now + Year;
dp = new->owner;
- assert(dp->magic == DNmagic);
+ assert(dp != nil && dp->magic == DNmagic);
new->auth |= auth;
new->next = 0;
+ /*
+ * try not to let responses expire before we
+ * can use them to complete this query, by extending
+ * past (or nearly past) expiration time.
+ */
+ if(new->db)
+ ttl = Year;
+ else
+ ttl = new->ttl;
+ if(ttl <= Min)
+ ttl = 10*Min;
+ new->expire = now + ttl;
+
/*
* find first rr of the right type
*/
}
/* all things equal, pick the newer one */
else if(rp->arg0 == new->arg0 && rp->arg1 == new->arg1){
- /* new drives out old */
- if (new->ttl <= rp->ttl &&
- new->expire <= rp->expire) {
+ /* old drives out new */
+ if((long)(rp->expire - new->expire) > 0) {
rrfree(new);
return;
}
void
rrattach(RR *rp, int auth)
{
- RR *next, *tp;
+ RR *next;
DN *dp;
lock(&dnlock);
next = rp->next;
rp->next = nil;
dp = rp->owner;
-
-// dnslog("rrattach: %s", rp->owner->name);
- /* avoid any outside spoofing; leave keepers alone */
- if(cfg.cachedb && !rp->db && inmyarea(rp->owner->name)
-// || dp->keep /* TODO: make this work */
- )
+ /* avoid any outside spoofing */
+ if(cfg.cachedb && !rp->db && inmyarea(dp->name))
rrfree(rp);
- else {
- /* ameliorate the memory leak (someday delete this) */
- if (0 && rrlistlen(dp->rr) > 50 && !dp->keep) {
- dnslog("rrattach(%s): rr list too long; "
- "freeing it", dp->name);
- tp = dp->rr;
- dp->rr = nil;
- rrfreelist(tp);
- } else
- USED(dp);
+ else
rrattach1(rp, auth);
- }
}
unlock(&dnlock);
}
-/* should be called with dnlock held */
RR**
rrcopy(RR *rp, RR **last)
{
- Cert *cert;
- Key *key;
- Null *null;
RR *nrp;
SOA *soa;
+ Srv *srv;
+ Key *key;
+ Cert *cert;
Sig *sig;
+ Null *null;
Txt *t, *nt, **l;
- if (canlock(&dnlock))
- abort(); /* rrcopy called with dnlock not held */
+ assert(rp->magic == RRmagic);
nrp = rralloc(rp->type);
- setmalloctag(nrp, getcallerpc(&rp));
switch(rp->type){
- case Ttxt:
- *nrp = *rp;
- l = &nrp->txt;
- *l = nil;
- for(t = rp->txt; t != nil; t = t->next){
- nt = emalloc(sizeof(*nt));
- nt->p = estrdup(t->p);
- nt->next = nil;
- *l = nt;
- l = &nt->next;
- }
- break;
case Tsoa:
soa = nrp->soa;
*nrp = *rp;
nrp->soa = soa;
- *nrp->soa = *rp->soa;
- nrp->soa->slaves = copyserverlist(rp->soa->slaves);
+ *soa = *rp->soa;
+ soa->slaves = copyserverlist(rp->soa->slaves);
break;
case Tsrv:
+ srv = nrp->srv;
*nrp = *rp;
- nrp->srv = emalloc(sizeof *nrp->srv);
- *nrp->srv = *rp->srv;
+ nrp->srv = srv;
+ *srv = *rp->srv;
break;
case Tkey:
key = nrp->key;
key->data = emalloc(key->dlen);
memmove(key->data, rp->key->data, rp->key->dlen);
break;
- case Tsig:
- sig = nrp->sig;
- *nrp = *rp;
- nrp->sig = sig;
- *sig = *rp->sig;
- sig->data = emalloc(sig->dlen);
- memmove(sig->data, rp->sig->data, rp->sig->dlen);
- break;
case Tcert:
cert = nrp->cert;
*nrp = *rp;
cert->data = emalloc(cert->dlen);
memmove(cert->data, rp->cert->data, rp->cert->dlen);
break;
+ case Tsig:
+ sig = nrp->sig;
+ *nrp = *rp;
+ nrp->sig = sig;
+ *sig = *rp->sig;
+ sig->data = emalloc(sig->dlen);
+ memmove(sig->data, rp->sig->data, rp->sig->dlen);
+ break;
case Tnull:
null = nrp->null;
*nrp = *rp;
null->data = emalloc(null->dlen);
memmove(null->data, rp->null->data, rp->null->dlen);
break;
+ case Ttxt:
+ *nrp = *rp;
+ l = &nrp->txt;
+ *l = nil;
+ for(t = rp->txt; t != nil; t = t->next){
+ nt = emalloc(sizeof(*nt));
+ nt->p = estrdup(t->p);
+ nt->next = nil;
+ *l = nt;
+ l = &nt->next;
+ }
+ break;
default:
*nrp = *rp;
break;
}
+ nrp->pc = getcallerpc(&rp);
+ setmalloctag(nrp, nrp->pc);
nrp->cached = 0;
- nrp->next = 0;
+ nrp->next = nil;
*last = nrp;
return &nrp->next;
}
assert(dp->magic == DNmagic);
- first = 0;
+ first = nil;
last = &first;
lock(&dnlock);
assert(rp->magic == RRmagic && rp->cached);
if(rp->db)
if(rp->auth)
- if(tsame(type, rp->type)) {
+ if(tsame(type, rp->type))
last = rrcopy(rp, last);
- // setmalloctag(*last, getcallerpc(&dp));
- }
}
if(first)
goto out;
for(rp = dp->rr; rp; rp = rp->next){
if(!rp->db)
if(rp->auth)
- if(rp->ttl + 60 > now)
- if(tsame(type, rp->type)){
+ if((long)(rp->expire - now) > 0)
+ if(tsame(type, rp->type)){
if(flag == NOneg && rp->negative)
goto out;
last = rrcopy(rp, last);
/* try for a living unauthoritative network entry */
for(rp = dp->rr; rp; rp = rp->next){
if(!rp->db)
- if(rp->ttl + 60 > now)
+ if((long)(rp->expire - now) > 0)
if(tsame(type, rp->type)){
if(flag == NOneg && rp->negative)
goto out;
}
out:
- unique(first);
unlock(&dnlock);
-// dnslog("rrlookup(%s) -> %#p\t# in-core only", dp->name, first);
-// if (first)
-// setmalloctag(first, getcallerpc(&dp));
+ unique(first);
return first;
}
}
/*
- * Add resource records to a list, duplicate them if they are cached
- * RR's since these are shared. should be called with dnlock held
- * to avoid racing down the start chain.
+ * Add resource records to a list.
*/
RR*
rrcat(RR **start, RR *rp)
RR *olp, *nlp;
RR **last;
- if (canlock(&dnlock))
- abort(); /* rrcat called with dnlock not held */
/* check for duplicates */
for (olp = *start; 0 && olp; olp = olp->next)
for (nlp = rp; nlp; nlp = nlp->next)
return *start;
}
-/*
- * remove negative cache rr's from an rr list
- */
RR*
-rrremneg(RR **l)
+rrremfilter(RR **l, int (*filter)(RR*, void*), void *arg)
{
- RR **nl, *rp;
- RR *first;
+ RR *first, *rp;
+ RR **nl;
- if (canlock(&dnlock))
- abort(); /* rrremneg called with dnlock not held */
first = nil;
nl = &first;
while(*l != nil){
rp = *l;
- if(rp->negative){
+ if((*filter)(rp, arg)){
*l = rp->next;
*nl = rp;
nl = &rp->next;
*nl = nil;
} else
- l = &rp->next;
+ l = &(*l)->next;
}
return first;
}
+static int
+filterneg(RR *rp, void*)
+{
+ return rp->negative;
+}
+static int
+filtertype(RR *rp, void *arg)
+{
+ return rp->type == *((int*)arg);
+}
+static int
+filterowner(RR *rp, void *arg)
+{
+ return rp->owner == (DN*)arg;
+}
+
+/*
+ * remove negative cache rr's from an rr list
+ */
+RR*
+rrremneg(RR **l)
+{
+ return rrremfilter(l, filterneg, nil);
+}
+
/*
* remove rr's of a particular type from an rr list
*/
RR*
rrremtype(RR **l, int type)
{
- RR *first, *rp;
- RR **nl;
-
- first = nil;
- nl = &first;
- while(*l != nil){
- rp = *l;
- if(rp->type == type){
- *l = rp->next;
- *nl = rp;
- nl = &rp->next;
- *nl = nil;
- } else
- l = &(*l)->next;
- }
+ return rrremfilter(l, filtertype, &type);
+}
- return first;
+/*
+ * remove rr's of a particular owner from an rr list
+ */
+RR*
+rrremowner(RR **l, DN *owner)
+{
+ return rrremfilter(l, filterowner, owner);
}
static char *
return dn? dn->name: "<null>";
}
+static char *
+idnname(DN *dn, char *buf, int nbuf)
+{
+ char *name;
+
+ name = dnname(dn);
+ if(idn2utf(name, buf, nbuf) != nil)
+ return buf;
+ return name;
+}
+
/*
* print conversion for rr records
*/
fmtprint(&fstr, "\t%s", dnname(rp->ip));
break;
case Tptr:
-// fmtprint(&fstr, "\t%s(%lud)", dnname(rp->ptr),
-// rp->ptr? rp->ptr->ordinal: "<null>");
fmtprint(&fstr, "\t%s", dnname(rp->ptr));
break;
case Tsoa:
rravfmt(Fmt *f)
{
int rv, quote;
- char *strp;
+ char buf[Domlen], *strp;
Fmt fstr;
RR *rp;
Server *s;
if(rp->type == Tptr)
fmtprint(&fstr, "ptr=%s", dnname(rp->owner));
else
- fmtprint(&fstr, "dom=%s", dnname(rp->owner));
+ fmtprint(&fstr, "dom=%s", idnname(rp->owner, buf, sizeof(buf)));
switch(rp->type){
case Thinfo:
fmtprint(&fstr, " cpu=%s os=%s",
- dnname(rp->cpu), dnname(rp->os));
+ idnname(rp->cpu, buf, sizeof(buf)),
+ idnname(rp->os, buf, sizeof(buf)));
break;
case Tcname:
- fmtprint(&fstr, " cname=%s", dnname(rp->host));
+ fmtprint(&fstr, " cname=%s", idnname(rp->host, buf, sizeof(buf)));
break;
case Tmb:
case Tmd:
case Tmf:
- fmtprint(&fstr, " mbox=%s", dnname(rp->host));
+ fmtprint(&fstr, " mbox=%s", idnname(rp->host, buf, sizeof(buf)));
break;
case Tns:
- fmtprint(&fstr, " ns=%s", dnname(rp->host));
+ fmtprint(&fstr, " ns=%s", idnname(rp->host, buf, sizeof(buf)));
break;
case Tmg:
case Tmr:
- fmtprint(&fstr, " mbox=%s", dnname(rp->mb));
+ fmtprint(&fstr, " mbox=%s", idnname(rp->mb, buf, sizeof(buf)));
break;
case Tminfo:
fmtprint(&fstr, " mbox=%s mbox=%s",
- dnname(rp->mb), dnname(rp->rmb));
+ idnname(rp->mb, buf, sizeof(buf)),
+ idnname(rp->rmb, buf, sizeof(buf)));
break;
case Tmx:
- fmtprint(&fstr, " pref=%lud mx=%s", rp->pref, dnname(rp->host));
+ fmtprint(&fstr, " pref=%lud mx=%s", rp->pref,
+ idnname(rp->host, buf, sizeof(buf)));
break;
case Ta:
case Taaaa:
soa = rp->soa;
fmtprint(&fstr,
" ns=%s mbox=%s serial=%lud refresh=%lud retry=%lud expire=%lud ttl=%lud",
- dnname(rp->host), dnname(rp->rmb),
+ idnname(rp->host, buf, sizeof(buf)),
+ idnname(rp->rmb, buf, sizeof(buf)),
(soa? soa->serial: 0),
(soa? soa->refresh: 0), (soa? soa->retry: 0),
(soa? soa->expire: 0), (soa? soa->minttl: 0));
srv = rp->srv;
fmtprint(&fstr, " pri=%ud weight=%ud port=%ud target=%s",
(srv? srv->pri: 0), (srv? srv->weight: 0),
- rp->port, dnname(rp->host));
+ rp->port, idnname(rp->host, buf, sizeof(buf)));
break;
case Tnull:
if (rp->null == nil)
break;
case Trp:
fmtprint(&fstr, " rp=%s txt=%s",
- dnname(rp->rmb), dnname(rp->rp));
+ idnname(rp->rmb, buf, sizeof(buf)),
+ idnname(rp->rp, buf, sizeof(buf)));
break;
case Tkey:
if (rp->key == nil)
" type=%d alg=%d labels=%d ttl=%lud exp=%lud incep=%lud tag=%d signer=%s",
rp->sig->type, rp->sig->alg, rp->sig->labels,
rp->sig->ttl, rp->sig->exp, rp->sig->incep,
- rp->sig->tag, dnname(rp->sig->signer));
+ rp->sig->tag, idnname(rp->sig->signer, buf, sizeof(buf)));
break;
case Tcert:
if (rp->cert == nil)
/* limit parallelism */
procs = getactivity(req, 1);
- if (procs > stats.slavehiwat)
+ if(procs > stats.slavehiwat)
stats.slavehiwat = procs;
if(procs > Maxactive){
if(traceactivity)
}
}
-/*
- * chasing down double free's
- */
-void
-dncheck(void *p, int dolock)
-{
- int i;
- DN *dp;
- RR *rp;
-
- if(p != nil){
- dp = p;
- assert(dp->magic == DNmagic);
- }
-
- if(!testing)
- return;
-
- if(dolock)
- lock(&dnlock);
- poolcheck(mainmem);
- for(i = 0; i < HTLEN; i++)
- for(dp = ht[i]; dp; dp = dp->next){
- assert(dp != p);
- assert(dp->magic == DNmagic);
- for(rp = dp->rr; rp; rp = rp->next){
- assert(rp->magic == RRmagic);
- assert(rp->cached);
- assert(rp->owner == dp);
- /* also check for duplicate rrs */
- if (dolock && rronlist(rp, rp->next)) {
- dnslog("%R duplicates its next chain "
- "(%R); aborting", rp, rp->next);
- abort();
- }
- }
- }
- if(dolock)
- unlock(&dnlock);
-}
-
static int
rrequiv(RR *r1, RR *r2)
{
&& r1->arg1 == r2->arg1;
}
-/* called with dnlock held */
void
unique(RR *rp)
{
ilen = f->prec;
f->prec = 0;
f->flags &= ~FmtPrec;
- switch(f->r){
- case '<':
- len = (8*ilen+4)/5 + 3;
- break;
- case '[':
- len = (8*ilen+5)/6 + 4;
- break;
- case 'H':
- len = 2*ilen + 1;
- break;
- default:
- goto error;
- }
-
+ len = 2*ilen + 1;
if(len > sizeof(obuf)){
buf = malloc(len);
if(buf == nil)
/* convert */
out = buf;
- switch(f->r){
- case '<':
- rv = enc32(out, len, b, ilen);
- break;
- case '[':
- rv = enc64(out, len, b, ilen);
- break;
- case 'H':
- rv = enc16(out, len, b, ilen);
- break;
- default:
- rv = -1;
- break;
- }
+ rv = enc16(out, len, b, ilen);
if(rv < 0)
goto error;
int size;
char *p;
- size = strlen(s)+1;
- p = mallocz(size, 0);
+ size = strlen(s);
+ p = mallocz(size+1, 0);
if(p == nil)
abort();
memmove(p, s, size);
+ p[size] = 0;
setmalloctag(p, getcallerpc(&s));
return p;
}
for(rp = first; rp != nil; rp = nrp){
nrp = rp->next;
rp->next = nil;
+ dp = rp->owner;
rrattach(rp, Authoritative);
+ dnagenever(dp);
}
}
addserver(Server **l, char *name)
{
Server *s;
+ int n;
while(*l)
l = &(*l)->next;
- s = malloc(sizeof(Server)+strlen(name)+1);
+ n = strlen(name);
+ s = malloc(sizeof(Server)+n+1);
if(s == nil)
return;
s->name = (char*)(s+1);
- strcpy(s->name, name);
+ memmove(s->name, name, n);
+ s->name[n] = 0;
s->next = nil;
*l = s;
}
for(; s != nil; s = next){
next = s->next;
+ memset(s, 0, sizeof *s); /* cause trouble */
free(s);
}
}
void
rrfree(RR *rp)
{
- DN *dp;
- RR *nrp;
Txt *t;
- assert(rp->magic == RRmagic);
- assert(!rp->cached);
-
- dp = rp->owner;
- if(dp){
- assert(dp->magic == DNmagic);
- for(nrp = dp->rr; nrp; nrp = nrp->next)
- assert(nrp != rp); /* "rrfree of live rr" */
- }
+ assert(rp->magic == RRmagic && !rp->cached);
switch(rp->type){
case Tsoa:
free(rp->null);
break;
case Ttxt:
- while(rp->txt != nil){
- t = rp->txt;
+ while(t = rp->txt){
rp->txt = t->next;
free(t->p);
memset(t, 0, sizeof *t); /* cause trouble */
break;
}
- rp->magic = ~rp->magic;
memset(rp, 0, sizeof *rp); /* cause trouble */
+ rp->magic = ~RRmagic;
free(rp);
}