Plan 9 from Bell Labs’s /usr/web/sources/contrib/steve/root/sys/src/cmd/ndb/zonefresh/axfr.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
 * axfr ns domain - fetch domain from ns,
 * print on stdout in ndb format
 *
 * Steve Simon and Geoff Collyer
 */
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ip.h>
#include <ctype.h>

Biobuf *Bo;

enum {
	FQDNMAX	= 255,

	Cin	= 1,		// internet
	Ccs	= 2,		// csnet
	Cch	= 3,		// CHAOS
	Chs	= 4,		// Hesoid
	Call	= 255,

	Ta	= 1,		// a host address
	Tns	= 2,		// an authoritative name server
	Tmd	= 3,		// a mail destination (Obsolete - use MX)
	Tmf	= 4,		// a mail forwarder (Obsolete - use MX)
	Tcname	= 5,		// the canonical name for an alias
	Tsoa	= 6,		// marks the start of a zone of authority
	Tmb	= 7,		// a mailbox domain name (EXPERIMENTAL)
	Tmg	= 8,		// a mail group member (EXPERIMENTAL)
	Tmr	= 9,		// a mail rename domain name (EXPERIMENTAL)
	Tnull	= 10,		// a null RR (EXPERIMENTAL)
	Twks	= 11,		// a well known service description
	Tptr	= 12,		// a domain name pointer
	Thinfo	= 13,		// host information
	Tminfo	= 14,		// mailbox or mail list information
	Tmx	= 15,		// mail exchange
	Ttxt	= 16,		// text strings
	Trp	= 17,		// for Responsible Person
	Tafsdb	= 18,		// for AFS Data Base location
	Tx25	= 19,		// for X.25 PSDN address
	Tisdn	= 20,		// for ISDN address
	Trt	= 21,		// for Route Through
	Tnsap	= 22,		// for NSAP address, NSAP style A record
	Tnsap_ptr = 23,		// for domain name pointer, NSAP style
	Tsig	= 24,		// for security signature
	Tkey	= 25,		// for security key
	Tpx	= 26,		// X.400 mail mapping information
	Tgpos	= 27,		// Geographical Position
	Taaaa	= 28,		// IP6 Address
	Tloc	= 29,		// Location Information
	Tnxt	= 30,		// Next Domain (obsolete)
	Teid	= 31,		// Endpoint Identifier
	Tnimloc	= 32,		// Nimrod Locator
	Tsrv	= 33,		// Server Selection
	Tatma	= 34,		// ATM Address
	Tnaptr	= 35,		// Naming Authority Pointer
	Tkx	= 36,		// Key Exchanger
	Tcert	= 37,
	Ta6	= 38,
	Tdname	= 39,
	Tsink	= 40,
	Topt	= 41,
	Tapl	= 42,
	Tds	= 43,		// Delegation Signer
	Tsshfp	= 44,		// SSH Key Fingerprint
	Trrsig	= 46,
	Tnsec	= 47,
	Tdnskey	= 48,
	Tuinfo	= 100,
	Tuid	= 101,
	Tgid	= 102,
	Tunspec	= 103,
	Taddrs	= 248,
	Ttkey	= 249,
	Ttsig	= 250,		// Transaction Signature
	Tixfr	= 251,		// Incremental transfer
	Taxfr	= 252,		// transfer of an entire zone
	Tmailb	= 253,		// mailbox-related RRs (MB, MG or MR)
	Tmaila	= 254,		// mail agent RRs (Obsolete - see MX)
	Tall	= 255,		// A request for all records
	Twins	= 0xff01,	// MS WINS server
	Twinsr	= 0xff02,	// MS wins reverse lookup
};

static char *Typestr[] = {
	[Ta]		"a",
	[Tns]		"ns",
	[Tmd]		"md",
	[Tmf]		"mf",
	[Tcname]	"cname",
	[Tsoa]		"soa",
	[Tmb]		"mb",
	[Tmg]		"mg",
	[Tmr]		"mr",
	[Tnull]		"null",
	[Twks]		"wks",
	[Tptr]		"ptr",
	[Thinfo]	"hinfo",
	[Tminfo]	"minfo",
	[Tmx]		"mx",
	[Ttxt]		"txt",
	[Trp]		"rp",
	[Tafsdb]	"afsdb",
	[Tx25]		"x25",
	[Tisdn]		"isdn",
	[Trt]		"rt",
	[Tnsap]		"nsap",
	[Tnsap_ptr]	"nsap_ptr",
	[Tsig]		"sig",
	[Tkey]		"key",
	[Tpx]		"px",
	[Tgpos]		"gpos",
	[Taaaa]		"aaaa",
	[Tloc]		"loc",
	[Tnxt]		"nxt",
	[Teid]		"eid",
	[Tnimloc]	"nimloc",
	[Tsrv]		"srv",
	[Tatma]		"atma",
	[Tnaptr]	"naptr",
	[Tkx]		"kx",
	[Tcert]		"cert",
	[Ta6]		"a6",
	[Tdname]	"dname",
	[Tsink]		"sink",
	[Topt]		"opt",
	[Tapl]		"apl",
	[Tds]		"ds",
	[Tsshfp]	"sshfp",
	[Trrsig]	"rrsig",
	[Tnsec]		"nsec",
	[Tdnskey]	"dnskey",
	[Tuinfo]	"uinfo",
	[Tuid]		"uid",
	[Tgid]		"gid",
	[Tunspec]	"unspec",
	[Taddrs]	"addrs",
	[Ttkey]		"tkey",
	[Ttsig]		"tsig",
	[Tixfr]		"ixfr",
	[Taxfr]		"axfr",
	[Tmailb]	"mailb",
	[Tmaila]	"maila",
	[Tall]		"all",
};

int Debug;

void
usage(void)
{
	fprint(2, "usage: %s [-x netmtpt] [-d] nameserver domainname\n", argv0);
	exits("usage");
}

void
ding(void *u, char *msg)
{
	USED(u);

	if(strstr(msg, "alarm"))
		noted(NCONT);
	noted(NDFLT);
}

int
g8(uchar **p)
{
	return *(*p)++;
}

int
g16(uchar **p)
{
	int n;

	n  = *(*p)++ << 8;
	n |= *(*p)++;
	return n;
}

int
g32(uchar **p)
{
	int n;

	n  = *(*p)++ << 24;
	n |= *(*p)++ << 16;
	n |= *(*p)++ << 8;
	n |= *(*p)++;
	return n;
}

void
gname(uchar **p, uchar *buf, char *s)
{
	char *last = s;
	uchar *q;
	int n;

	while (n = g8(p)){
		if(n & 0xc0){
			(*p)--;
			n = g16(p);
			q = buf + (n & 0x3fff) +2;	// +2 to skip packet len
			gname(&q, buf, s);
			return;
		}
		while (**p && n--)
			*s++ = *(*p)++;
		last = s;
		*s++ = '.';
	}
	*last = 0;
}

void
skip(uchar **p, int len)
{
	if(Debug)
		Bprint(Bo, "skiped %d bytes at end of record\n", len);
	*p += len;
}

void *
gmem(uchar **p, int len)
{
	char *t, *s;

	assert(s = malloc(len));
	for (t = s; len; len--)
		*t++ = *(*p)++;
	return s;
}

void
p8(uchar **p, int n)
{
	*(*p)++ = n & 0xff;
}

void
p16(uchar **p, int n)
{
	*(*p)++ = (n >> 8) & 0xff;
	*(*p)++ = n & 0xff;
}

void
p32(uchar **p, int n)
{
	*(*p)++ = (n >> 24) & 0xff;
	*(*p)++ = (n >> 16) & 0xff;
	*(*p)++ = (n >> 8) & 0xff;
	*(*p)++ = n & 0xff;
}

void
pmem(uchar **p, void *v, int len)
{
	char *s;

	for(s = v; len; len--)
		*(*p)++ = *s++;
}

void
pname(uchar **p, char *s)
{
	uchar *len;

	while (*s){
		len = (*p)++;
		while(*s && *s != '.')
			*(*p)++ = *s++;
		*len = (*p - len) -1;
		if(*s == '.')
			s++;
	}
	*(*p)++ = 0;
}

void
xd(void *p, int l)
{
	int i;
	uchar *buf = p;

	Bprint(Bo, "\n%-4x ", 0);
	for (i = 0; i < l && i < 128; i++){
		if(isprint(buf[i]))
			Bprint(Bo, " %c ", buf[i]);
		else
			Bprint(Bo, "%02x ", buf[i]);
		if(i != 0 && (i % 16) == 0)
			Bprint(Bo, "\n%-4x ", i);
	}
	Bprint(Bo, "\n");

}

void
pr(uchar **pp, uchar *buf, int vlen, char *name, int type)
{
	int n, i, j, k;
	uchar *ad = nil;
	char *p = nil, *a[128], s[FQDNMAX +1];

	switch(type){
	case Ta:
		ad = gmem(pp, vlen);
		Bprint(Bo, "dom=%s ip=%V\n", name, ad);
		break;
	case Taaaa:
		ad = gmem(pp, vlen);
		Bprint(Bo, "dom=%s ip=%I\n", name, ad);
		break;
	case Thinfo:
		p = gmem(pp, vlen);
		Bprint(Bo, "dom=%s hinfo=\"%s\"\n", name, p);
		break;
	case Ttxt:
		do{
			n = g8(pp);
			p = gmem(pp, n);
			Bprint(Bo, "dom=%s txtrr=\"%.*s\"\n", name, n, p);
			free(p);
			p = nil;
			vlen -= n+1;
		}while(vlen > 0);
		break;
	case Trp:
		gname(pp, buf, s);
		if((p = strchr(s, '.')) != nil)
			*p = '@';
		p = nil;		// don't free p
		Bprint(Bo, "dom=%s contact=%s\n", name, s);
		break;
	case Tns:
		gname(pp, buf, s);
		Bprint(Bo, "dom=%s ns=%s\n", name, s);
		break;
	case Tptr:
		gname(pp, buf, s);
		Bprint(Bo, "dom=%s ptr=%s\n", s, name);
		break;
	case Tcname:
		gname(pp, buf, s);
		Bprint(Bo, "dom=%s cname=%s\n", name, s);
		break;
	case Tmx:
		n = g16(pp);
		gname(pp, buf, s);
		Bprint(Bo, "dom=%s mx=%s pref=%ud\n", name, s, n);
		break;
	case Tsoa:
		Bprint(Bo, "dom=%s soa= ", name);
		gname(pp, buf, s);
		Bprint(Bo, "ns=%s ", s);
		gname(pp, buf, s);
		if((p = strchr(s, '.')) != nil)
			*p = '@';
		p = nil;		// don't free p
		Bprint(Bo, "mbox=%s ", s);
	 	Bprint(Bo, "serial=%ud ", g32(pp));
	 	Bprint(Bo, "refresh=%ud ", g32(pp));
	 	Bprint(Bo, "retry=%ud ", g32(pp));
	 	Bprint(Bo, "expire=%ud ", g32(pp));
	 	Bprint(Bo, "min=%ud\n", g32(pp));
		break;
	case Tsrv:
		i = g16(pp);
		j = g16(pp);
		k = g16(pp);
		gname(pp, buf, s);

		if((n = gettokens(name, a, 3, "._")) != 3)
			sysfatal("%s, %d fields - corrupt srv record\n", name, n);
		Bprint(Bo, "srv=%s ", a[2]);
		Bprint(Bo, "service=%s ", a[0]);
	 	Bprint(Bo, "port=%ud ", k);
		Bprint(Bo, "proto=%s ", a[1]);
	 	Bprint(Bo, "priority=%ud ", i);
	 	Bprint(Bo, "weight=%ud ", j);
		Bprint(Bo, "dom=%s\n", s);
		break;
	case Tgpos:
		Bprint(Bo, "# dom=%s ", name);
		Bprint(Bo, "longitude=%d ", g16(pp));
		Bprint(Bo, "latitude=%d ", g16(pp));
		Bprint(Bo, "altitude=%d\n", g16(pp));
		break;
	case Tx25:
		p = gmem(pp, vlen);
		Bprint(Bo, "dom=%s x25=%s\n", name, p);
		break;
	case Tisdn:
		p = gmem(pp, vlen);
		Bprint(Bo, "dom=%s isdn=%s\n", name, p);
		break;
	case Twins:
		Bprint(Bo, "wins=%s ", name);
		Bprint(Bo, "replication=%s ", g32(pp)? "none": "active");
		Bprint(Bo, "timeout=%d ", g32(pp));
		Bprint(Bo, "cache=%d ", g32(pp));
		n = g32(pp);
		while(n--){
			ad = gmem(pp, 4);
			Bprint(Bo, "server=%V ", ad);
			free(ad);
			ad = nil;
		}
		Bprint(Bo, "\n");
		break;
	case Twinsr:
		Bprint(Bo, "winsr=%s ", name);
		Bprint(Bo, "replication=%s ", g32(pp)? "none": "active");
		Bprint(Bo, "timeout=%d ", g32(pp));
		Bprint(Bo, "cache=%d ", g32(pp));
		gname(pp, buf, s);
		Bprint(Bo, "domain=%s\n", s);
		break;
	default:
		if(type > 0 && type < nelem(Typestr) && Typestr[type])
			Bprint(Bo, "# %s=%s unsupported\n", Typestr[type], name);
		else{
			Bprint(Bo, "# %ux=%s unknown\n", type, name);
			xd(*pp, vlen);
		}
		skip(pp, vlen);
		break;
	}
	free(p);
	free(ad);
}

/*
 * Evade DNS poisioning, detect records that refer
 * to domains other than the one we asked about.
 */
int
mydom(char *me, char *dom)
{
	int m, d;

	m = strlen(me);
	d = strlen(dom);
	if (m > d)
		return 0;
	return cistrcmp(me, dom+(d-m)) == 0;
}

void
main(int argc, char *argv[])
{
	Biobuf bout;
	char net[64], name[FQDNMAX +1];
	uchar *p, obuf[0xff], *ibuf;
	int nsoa, nans, naut, nadd, vlen, len, id;
	int type, class, err, i, fd;

	Bo = &bout;
	Binit(&bout, 1, OWRITE);

	nsoa = 0;
	Debug = 0;
	setnetmtpt(net, sizeof(net), nil);
	ARGBEGIN{
	case 'd':
		Debug++;
		break;
	case 'x':
		setnetmtpt(net, sizeof(net), EARGF(usage()));
		break;
	}ARGEND;
	if(argc != 2)
		usage();

	fmtinstall('I', eipfmt);
	fmtinstall('V', eipfmt);

	if((fd = dial(netmkaddr(argv[0], "tcp", "dns"), 0, 0, 0)) < 0)
		sysfatal("%s can't dial - %r", argv[0]);

	id = time(nil) + getpid();

	p = obuf;
	p16(&p, 0);			// length, filled in later
	p16(&p, id);			// ID
	p16(&p, 0);			// flags
	p16(&p, 1);			// # questions
	p16(&p, 0);			// # answers
	p16(&p, 0);			// # authorities
	p16(&p, 0);			// # additional
        pname(&p, argv[1]);		// question name
	p16(&p, Taxfr);			// question type (AXFR - zone transfer)
	p16(&p, Cin);			// question class (Internet)

	len = p-obuf;

	p = obuf;
	p16(&p, len -2);		// -2 as length is non-inclusive

	if(write(fd, obuf, len) != len)
		sysfatal("write failed: %r");

	notify(ding);

	while(1){
		assert(ibuf = malloc(2));
		alarm(5000);
		if(readn(fd, ibuf, 2) != 2)
			sysfatal("failed - timeout");

		p = ibuf;
		len = g16(&p);
		assert(ibuf = realloc(ibuf, len +2));
		if(readn(fd, ibuf +2, len) != len)
			sysfatal("failed - timeout");
		alarm(0);

		if(g16(&p) != (id & 0xffff))
			sysfatal("bad packet ID");

		err = g16(&p) & 7;		// flags
		g16(&p);			// # questions
		nans = g16(&p);			// # answers
		naut = g16(&p);			// # authorities
		nadd = g16(&p);			// # additional

		gname(&p, ibuf, name);		// name requested
		type = g16(&p);			// type requested
		class = g16(&p);		// class requested

		switch(err){
		case 0:	break;			// success
		case 1:	sysfatal("bad request");
		case 2:	sysfatal("internal server failure");
		case 3:	sysfatal("resource does not exist");
		case 4:	sysfatal("request not supported");
		case 5:	sysfatal("permission denied");
		default: sysfatal("%d - unknown server error", err);
		}

		if(Debug){
			Bprint(Bo, "got ans=%d auth=%d add=%d type=%d class=%d name=%s\n",
				nans, naut, nadd, type, class, name);
			xd(ibuf, len);
		}

		for (i = 0; i < nans; i++){
			gname(&p, ibuf, name);
			type = g16(&p);		// type
			g16(&p);		// class
			g32(&p);		// TTL
			vlen = g16(&p);		// name length

			if(type == Tsoa && nsoa++ > 0)
				goto done;
			if(Debug > 1)
				Bprint(Bo, "type=%d len=%d name=%s ", type, len, name);
			if (! mydom(argv[1], name))
				Bprint(Bo, "#poison: ");
			pr(&p, ibuf, vlen, name, type);
		}
		if(Debug && (p-ibuf > len+2))
			fprint(2, "skiped %ld bytes at end of packet\n", (p-ibuf)-(len+2));
		free(ibuf);
	}
done:
	free(ibuf);
	close(fd);
	exits(0);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].