212 lines
4.5 KiB
C
Executable file
212 lines
4.5 KiB
C
Executable file
#include <u.h>
|
|
#include <libc.h>
|
|
#include <ip.h>
|
|
#include <bio.h>
|
|
#include <ndb.h>
|
|
#include <thread.h>
|
|
#include "dns.h"
|
|
|
|
static int udpannounce(char*);
|
|
static void reply(int, uchar*, DNSmsg*, Request*);
|
|
|
|
extern char *logfile;
|
|
|
|
typedef struct Inprogress Inprogress;
|
|
struct Inprogress
|
|
{
|
|
int inuse;
|
|
Udphdr uh;
|
|
DN *owner;
|
|
int type;
|
|
int id;
|
|
};
|
|
Inprogress inprog[Maxactive+2];
|
|
QLock inproglk;
|
|
|
|
/*
|
|
* record client id and ignore retransmissions.
|
|
* we're still single thread at this point. BUG
|
|
*/
|
|
static Inprogress*
|
|
clientrxmit(DNSmsg *req, uchar *buf)
|
|
{
|
|
Inprogress *p, *empty;
|
|
Udphdr *uh;
|
|
|
|
qlock(&inproglk);
|
|
uh = (Udphdr *)buf;
|
|
empty = 0;
|
|
for(p = inprog; p < &inprog[Maxactive]; p++){
|
|
if(p->inuse == 0){
|
|
if(empty == 0)
|
|
empty = p;
|
|
continue;
|
|
}
|
|
if(req->id == p->id)
|
|
if(req->qd->owner == p->owner)
|
|
if(req->qd->type == p->type)
|
|
if(memcmp(uh, &p->uh, Udphdrsize) == 0){
|
|
qunlock(&inproglk);
|
|
return 0;
|
|
}
|
|
}
|
|
if(empty == 0){
|
|
qunlock(&inproglk);
|
|
return 0; /* shouldn't happen - see slave() and definition of Maxactive */
|
|
}
|
|
|
|
empty->id = req->id;
|
|
empty->owner = req->qd->owner;
|
|
empty->type = req->qd->type;
|
|
memmove(&empty->uh, uh, Udphdrsize);
|
|
empty->inuse = 1;
|
|
qunlock(&inproglk);
|
|
return empty;
|
|
}
|
|
|
|
/*
|
|
* a process to act as a dns server for outside reqeusts
|
|
*/
|
|
static void
|
|
udpproc(void *v)
|
|
{
|
|
int fd, len, op;
|
|
Request req;
|
|
DNSmsg reqmsg, repmsg;
|
|
uchar buf[Udphdrsize + Maxudp + 1024];
|
|
char *err;
|
|
Inprogress *p;
|
|
char tname[32];
|
|
Udphdr *uh;
|
|
|
|
fd = (uintptr)v;
|
|
|
|
/* loop on requests */
|
|
for(;; putactivity()){
|
|
memset(&repmsg, 0, sizeof(repmsg));
|
|
memset(&reqmsg, 0, sizeof(reqmsg));
|
|
len = udpread(fd, (Udphdr*)buf, buf+Udphdrsize, sizeof(buf)-Udphdrsize);
|
|
if(len <= 0)
|
|
continue;
|
|
uh = (Udphdr*)buf;
|
|
getactivity(&req);
|
|
req.aborttime = now + 30; /* don't spend more than 30 seconds */
|
|
err = convM2DNS(&buf[Udphdrsize], len, &reqmsg);
|
|
if(err){
|
|
syslog(0, logfile, "server: input error: %s from %I", err, buf);
|
|
continue;
|
|
}
|
|
if(reqmsg.qdcount < 1){
|
|
syslog(0, logfile, "server: no questions from %I", buf);
|
|
goto freereq;
|
|
}
|
|
if(reqmsg.flags & Fresp){
|
|
syslog(0, logfile, "server: reply not request from %I", buf);
|
|
goto freereq;
|
|
}
|
|
op = reqmsg.flags & Omask;
|
|
if(op != Oquery && op != Onotify){
|
|
syslog(0, logfile, "server: op %d from %I", reqmsg.flags & Omask, buf);
|
|
goto freereq;
|
|
}
|
|
|
|
if(debug || (trace && subsume(trace, reqmsg.qd->owner->name))){
|
|
syslog(0, logfile, "%d: serve (%I/%d) %d %s %s",
|
|
req.id, buf, ((uh->rport[0])<<8)+uh->rport[1],
|
|
reqmsg.id,
|
|
reqmsg.qd->owner->name,
|
|
rrname(reqmsg.qd->type, tname, sizeof tname));
|
|
}
|
|
|
|
p = clientrxmit(&reqmsg, buf);
|
|
if(p == 0){
|
|
if(debug)
|
|
syslog(0, logfile, "%d: duplicate", req.id);
|
|
goto freereq;
|
|
}
|
|
|
|
/* loop through each question */
|
|
while(reqmsg.qd){
|
|
memset(&repmsg, 0, sizeof(repmsg));
|
|
switch(op){
|
|
case Oquery:
|
|
dnserver(&reqmsg, &repmsg, &req);
|
|
break;
|
|
case Onotify:
|
|
dnnotify(&reqmsg, &repmsg, &req);
|
|
break;
|
|
}
|
|
reply(fd, buf, &repmsg, &req);
|
|
rrfreelist(repmsg.qd);
|
|
rrfreelist(repmsg.an);
|
|
rrfreelist(repmsg.ns);
|
|
rrfreelist(repmsg.ar);
|
|
}
|
|
|
|
p->inuse = 0;
|
|
|
|
freereq:
|
|
rrfreelist(reqmsg.qd);
|
|
rrfreelist(reqmsg.an);
|
|
rrfreelist(reqmsg.ns);
|
|
rrfreelist(reqmsg.ar);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* announce on udp port
|
|
*/
|
|
static int
|
|
udpannounce(char *mntpt)
|
|
{
|
|
int fd;
|
|
char buf[40];
|
|
|
|
USED(mntpt);
|
|
|
|
snprint(buf, sizeof buf, "udp!*!%s", portname);
|
|
if((fd=announce(buf, buf)) < 0)
|
|
warning("announce %s: %r", buf);
|
|
return fd;
|
|
}
|
|
|
|
static void
|
|
reply(int fd, uchar *buf, DNSmsg *rep, Request *reqp)
|
|
{
|
|
int len;
|
|
char tname[32];
|
|
RR *rp;
|
|
|
|
if(debug || (trace && subsume(trace, rep->qd->owner->name)))
|
|
syslog(0, logfile, "%d: reply (%I/%d) %d %s %s an %R ns %R ar %R",
|
|
reqp->id, buf, ((buf[4])<<8)+buf[5],
|
|
rep->id, rep->qd->owner->name,
|
|
rrname(rep->qd->type, tname, sizeof tname), rep->an, rep->ns, rep->ar);
|
|
|
|
len = convDNS2M(rep, &buf[Udphdrsize], Maxudp);
|
|
if(len <= 0){
|
|
syslog(0, logfile, "error converting reply: %s %d", rep->qd->owner->name,
|
|
rep->qd->type);
|
|
for(rp = rep->an; rp; rp = rp->next)
|
|
syslog(0, logfile, "an %R", rp);
|
|
for(rp = rep->ns; rp; rp = rp->next)
|
|
syslog(0, logfile, "ns %R", rp);
|
|
for(rp = rep->ar; rp; rp = rp->next)
|
|
syslog(0, logfile, "ar %R", rp);
|
|
return;
|
|
}
|
|
if(udpwrite(fd, (Udphdr*)buf, buf+Udphdrsize, len) != len)
|
|
syslog(0, logfile, "error sending reply: %r");
|
|
}
|
|
|
|
void
|
|
dnudpserver(void *v)
|
|
{
|
|
int i, fd;
|
|
|
|
while((fd = udpannounce(v)) < 0)
|
|
sleep(5*1000);
|
|
for(i=0; i<Maxactive; i++)
|
|
proccreate(udpproc, (void*)(uintptr)fd, STACK);
|
|
}
|
|
|