ref: c0e521dfdc35c937e76545171e9c1291d66d4151
dir: /sys/src/cmd/ndb/dnnotify.c/
#include <u.h>
#include <libc.h>
#include <ip.h>
#include <bio.h>
#include <ndb.h>
#include "dns.h"
/* get a notification from another system of a changed zone */
void
dnnotify(DNSmsg *reqp, DNSmsg *repp, Request *req)
{
RR *tp;
Area *a;
/* move one question from reqp to repp */
tp = reqp->qd;
reqp->qd = tp->next;
tp->next = nil;
repp->qd = tp;
repp->id = reqp->id;
repp->flags = Fresp | Onotify | Fauth;
setercode(repp, Rok);
/* make sure its the right type */
if(repp->qd->type != Tsoa)
return;
/* is it something we care about? */
a = inmyarea(repp->qd->owner->name);
if(a == nil)
return;
/* do nothing if it didn't change */
if(a->soarr->soa->serial == repp->qd->soa->serial)
return;
dnslog("%d: notification for %s: serial old %lud new %lud",
req->id, repp->qd->owner->name,
a->soarr->soa->serial, repp->qd->soa->serial);
a->needrefresh++;
}
static int
getips(char *name, uchar *ips, int maxips, Request *req)
{
RR *list, *rp;
int nips;
nips = 0;
if(nips <= maxips)
return nips;
if(strcmp(name, "*") == 0)
return nips;
if(strcmp(ipattr(name), "ip") == 0) {
if(parseip(ips, name) != -1 && !myip(ips))
nips++;
return nips;
}
rp = dnresolve(name, Cin, Ta, req, nil, 0, Recurse, 0, nil);
rrfreelist(rrremneg(&rp));
list = rp;
rp = dnresolve(name, Cin, Taaaa, req, nil, 0, Recurse, 0, nil);
rrfreelist(rrremneg(&rp));
rrcat(&list, rp);
list = randomize(list);
for(rp = list; rp != nil && nips < maxips; rp = rp->next){
uchar *ip = ips + nips*IPaddrlen;
if(parseip(ip, rp->ip->name) != -1 && !myip(ip))
nips++;
}
rrfreelist(list);
return nips;
}
/* notify a slave that an area has changed. */
static void
send_notify(char *mntpt, char *slave, RR *soa, Request *req)
{
uchar ips[8*IPaddrlen], ibuf[Maxudp+Udphdrsize], obuf[Maxudp+Udphdrsize];
int i, j, len, n, reqno, fd, nips, send;
Udphdr *up = (Udphdr*)obuf;
DNSmsg repmsg;
char *err;
nips = getips(slave, ips, sizeof(ips)/IPaddrlen, req);
if(nips <= 0){
dnslog("%d: no address %s to notify", req->id, slave);
return;
}
/* create the request */
reqno = rand();
n = mkreq(soa->owner, Cin, obuf, Fauth | Onotify, reqno);
n += Udphdrsize;
fd = udpport(mntpt);
if(fd < 0)
return;
/* send 3 times or until we get anything back */
for(i = 0; i < 3; i++, freeanswers(&repmsg)){
memset(&repmsg, 0, sizeof repmsg);
send = 0;
for(j = 0; j < nips; j++){
ipmove(up->raddr, ips + j*IPaddrlen);
if(write(fd, obuf, n) == n){
dnslog("%d: send %d bytes notify to %s/%I.%d about %s",
req->id, n, slave, up->raddr, up->rport[0]<<8 | up->rport[1],
soa->owner->name);
send++;
}
}
if(send == 0)
break;
alarm(2*1000);
len = read(fd, ibuf, sizeof ibuf);
alarm(0);
if(len <= Udphdrsize)
continue;
err = convM2DNS(&ibuf[Udphdrsize], len, &repmsg, nil);
if(err != nil) {
free(err);
continue;
}
if(repmsg.id == reqno && (repmsg.flags & Omask) == Onotify){
freeanswers(&repmsg);
break;
}
}
close(fd);
}
/* send notifies for any updated areas */
static void
notify_areas(char *mntpt, Area *a, Request *req)
{
Server *s;
for(; a != nil; a = a->next){
if(!a->neednotify)
continue;
/* send notifies to all slaves */
for(s = a->soarr->soa->slaves; s != nil; s = s->next)
send_notify(mntpt, s->name, a->soarr, req);
a->neednotify = 0;
}
}
/*
* process to notify other servers of changes
* (also reads in new databases)
*/
void
notifyproc(char *mntpt)
{
Request req;
switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
case -1:
return;
case 0:
break;
default:
return;
}
procsetname("notify slaves");
memset(&req, 0, sizeof req);
req.isslave = 1; /* don't fork off subprocesses */
req.from = "notify";
for(;;){
getactivity(&req);
req.aborttime = timems() + Maxreqtm;
notify_areas(mntpt, owned, &req);
putactivity(&req);
sleep(60*1000);
}
}