blob: 8e1fd0ce9464896e62db2a0ad7d62e03bf3b32f1 [file] [log] [blame]
#include <u.h>
#include <libc.h>
#include <ip.h>
#include <bio.h>
#include <ndb.h>
#include "dns.h"
typedef struct Scan Scan;
struct Scan
{
uchar *base;
uchar *p;
uchar *ep;
char *err;
};
#define NAME(x) gname(x, sp)
#define SYMBOL(x) (x = gsym(sp))
#define STRING(x) (x = gstr(sp))
#define USHORT(x) (x = gshort(sp))
#define ULONG(x) (x = glong(sp))
#define UCHAR(x) (x = gchar(sp))
#define V4ADDR(x) (x = gv4addr(sp))
#define V6ADDR(x) (x = gv6addr(sp))
#define BYTES(x, y) (y = gbytes(sp, &x, len - (sp->p - data)))
static char *toolong = "too long";
/*
* get a ushort/ulong
*/
static ushort
gchar(Scan *sp)
{
ushort x;
if(sp->err)
return 0;
if(sp->ep - sp->p < 1){
sp->err = toolong;
return 0;
}
x = sp->p[0];
sp->p += 1;
return x;
}
static ushort
gshort(Scan *sp)
{
ushort x;
if(sp->err)
return 0;
if(sp->ep - sp->p < 2){
sp->err = toolong;
return 0;
}
x = (sp->p[0]<<8) | sp->p[1];
sp->p += 2;
return x;
}
static ulong
glong(Scan *sp)
{
ulong x;
if(sp->err)
return 0;
if(sp->ep - sp->p < 4){
sp->err = toolong;
return 0;
}
x = (sp->p[0]<<24) | (sp->p[1]<<16) | (sp->p[2]<<8) | sp->p[3];
sp->p += 4;
return x;
}
/*
* get an ip address
*/
static DN*
gv4addr(Scan *sp)
{
char addr[32];
if(sp->err)
return 0;
if(sp->ep - sp->p < 4){
sp->err = toolong;
return 0;
}
snprint(addr, sizeof(addr), "%V", sp->p);
sp->p += 4;
return dnlookup(addr, Cin, 1);
}
static DN*
gv6addr(Scan *sp)
{
char addr[64];
if(sp->err)
return 0;
if(sp->ep - sp->p < IPaddrlen){
sp->err = toolong;
return 0;
}
snprint(addr, sizeof(addr), "%I", sp->p);
sp->p += IPaddrlen;
return dnlookup(addr, Cin, 1);
}
/*
* get a string. make it an internal symbol.
*/
static DN*
gsym(Scan *sp)
{
int n;
char sym[Strlen+1];
if(sp->err)
return 0;
n = *(sp->p++);
if(sp->p+n > sp->ep){
sp->err = toolong;
return 0;
}
if(n > Strlen){
sp->err = "illegal string";
return 0;
}
strncpy(sym, (char*)sp->p, n);
sym[n] = 0;
sp->p += n;
return dnlookup(sym, Csym, 1);
}
/*
* get a string. don't make it an internal symbol.
*/
static Txt*
gstr(Scan *sp)
{
int n;
char sym[Strlen+1];
Txt *t;
if(sp->err)
return 0;
n = *(sp->p++);
if(sp->p+n > sp->ep){
sp->err = toolong;
return 0;
}
if(n > Strlen){
sp->err = "illegal string";
return 0;
}
strncpy(sym, (char*)sp->p, n);
sym[n] = 0;
sp->p += n;
t = emalloc(sizeof(*t));
t->next = nil;
t->p = estrdup(sym);
return t;
}
/*
* get a sequence of bytes
*/
static int
gbytes(Scan *sp, uchar **p, int n)
{
if(sp->err)
return 0;
if(sp->p+n > sp->ep || n < 0){
sp->err = toolong;
return 0;
}
*p = emalloc(n);
memmove(*p, sp->p, n);
sp->p += n;
return n;
}
/*
* get a domain name. 'to' must point to a buffer at least Domlen+1 long.
*/
static char*
gname(char *to, Scan *sp)
{
int len, off;
int pointer;
int n;
char *tostart;
char *toend;
uchar *p;
tostart = to;
if(sp->err)
goto err;
pointer = 0;
p = sp->p;
toend = to + Domlen;
for(len = 0; *p; len += pointer ? 0 : (n+1)){
if((*p & 0xc0) == 0xc0){
/* pointer to other spot in message */
if(pointer++ > 10){
sp->err = "pointer loop";
goto err;
}
off = ((p[0]<<8) + p[1]) & 0x3ff;
p = sp->base + off;
if(p >= sp->ep){
sp->err = "bad pointer";
goto err;
}
n = 0;
continue;
}
n = *p++;
if(len + n < Domlen - 1){
if(to + n > toend){
sp->err = toolong;
goto err;
}
memmove(to, p, n);
to += n;
}
p += n;
if(*p){
if(to >= toend){
sp->err = toolong;
goto err;
}
*to++ = '.';
}
}
*to = 0;
if(pointer)
sp->p += len + 2; /* + 2 for pointer */
else
sp->p += len + 1; /* + 1 for the null domain */
return tostart;
err:
*tostart = 0;
return tostart;
}
/*
* convert the next RR from a message
*/
static RR*
convM2RR(Scan *sp)
{
RR *rp;
int type;
int class;
uchar *data;
int len;
char dname[Domlen+1];
Txt *t, **l;
retry:
NAME(dname);
USHORT(type);
USHORT(class);
rp = rralloc(type);
rp->owner = dnlookup(dname, class, 1);
rp->type = type;
ULONG(rp->ttl);
rp->ttl += now;
USHORT(len);
data = sp->p;
if(sp->p + len > sp->ep)
sp->err = toolong;
if(sp->err){
rrfree(rp);
return 0;
}
switch(type){
default:
/* unknown type, just ignore it */
sp->p = data + len;
rrfree(rp);
goto retry;
case Thinfo:
SYMBOL(rp->cpu);
SYMBOL(rp->os);
break;
case Tcname:
case Tmb:
case Tmd:
case Tmf:
case Tns:
rp->host = dnlookup(NAME(dname), Cin, 1);
break;
case Tmg:
case Tmr:
rp->mb = dnlookup(NAME(dname), Cin, 1);
break;
case Tminfo:
rp->rmb = dnlookup(NAME(dname), Cin, 1);
rp->mb = dnlookup(NAME(dname), Cin, 1);
break;
case Tmx:
USHORT(rp->pref);
rp->host = dnlookup(NAME(dname), Cin, 1);
break;
case Ta:
V4ADDR(rp->ip);
break;
case Taaaa:
V6ADDR(rp->ip);
break;
case Tptr:
rp->ptr = dnlookup(NAME(dname), Cin, 1);
break;
case Tsoa:
rp->host = dnlookup(NAME(dname), Cin, 1);
rp->rmb = dnlookup(NAME(dname), Cin, 1);
ULONG(rp->soa->serial);
ULONG(rp->soa->refresh);
ULONG(rp->soa->retry);
ULONG(rp->soa->expire);
ULONG(rp->soa->minttl);
break;
case Ttxt:
l = &rp->txt;
*l = nil;
while(sp->p-data < len){
STRING(t);
*l = t;
l = &t->next;
}
break;
case Tnull:
BYTES(rp->null->data, rp->null->dlen);
break;
case Trp:
rp->rmb = dnlookup(NAME(dname), Cin, 1);
rp->rp = dnlookup(NAME(dname), Cin, 1);
break;
case Tkey:
USHORT(rp->key->flags);
UCHAR(rp->key->proto);
UCHAR(rp->key->alg);
BYTES(rp->key->data, rp->key->dlen);
break;
case Tsig:
USHORT(rp->sig->type);
UCHAR(rp->sig->alg);
UCHAR(rp->sig->labels);
ULONG(rp->sig->ttl);
ULONG(rp->sig->exp);
ULONG(rp->sig->incep);
USHORT(rp->sig->tag);
rp->sig->signer = dnlookup(NAME(dname), Cin, 1);
BYTES(rp->sig->data, rp->sig->dlen);
break;
case Tcert:
USHORT(rp->cert->type);
USHORT(rp->cert->tag);
UCHAR(rp->cert->alg);
BYTES(rp->cert->data, rp->cert->dlen);
break;
}
if(sp->p - data != len)
sp->err = "bad RR len";
return rp;
}
/*
* convert the next question from a message
*/
static RR*
convM2Q(Scan *sp)
{
char dname[Domlen+1];
int type;
int class;
RR *rp;
NAME(dname);
USHORT(type);
USHORT(class);
if(sp->err)
return 0;
rp = rralloc(type);
rp->owner = dnlookup(dname, class, 1);
return rp;
}
static RR*
rrloop(Scan *sp, int count, int quest)
{
int i;
RR *first, *rp, **l;
if(sp->err)
return 0;
l = &first;
first = 0;
for(i = 0; i < count; i++){
rp = quest ? convM2Q(sp) : convM2RR(sp);
if(rp == 0)
break;
if(sp->err){
rrfree(rp);
break;
}
*l = rp;
l = &rp->next;
}
return first;
}
/*
* convert the next DNS from a message stream
*/
char*
convM2DNS(uchar *buf, int len, DNSmsg *m)
{
Scan scan;
Scan *sp;
char *err;
scan.base = buf;
scan.p = buf;
scan.ep = buf + len;
scan.err = 0;
sp = &scan;
memset(m, 0, sizeof(DNSmsg));
USHORT(m->id);
USHORT(m->flags);
USHORT(m->qdcount);
USHORT(m->ancount);
USHORT(m->nscount);
USHORT(m->arcount);
m->qd = rrloop(sp, m->qdcount, 1);
m->an = rrloop(sp, m->ancount, 0);
m->ns = rrloop(sp, m->nscount, 0);
err = scan.err; /* live with bad ar's */
m->ar = rrloop(sp, m->arcount, 0);
return err;
}