9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
* [9fans] ndb database checker: ndb/vrfy
@ 2010-10-28 23:14 erik quanstrom
  2010-10-29  0:21 ` erik quanstrom
  0 siblings, 1 reply; 2+ messages in thread
From: erik quanstrom @ 2010-10-28 23:14 UTC (permalink / raw)
  To: 9fans

[-- Attachment #1: Type: text/plain, Size: 670 bytes --]

a number of weeks ago, to facilitate migrating many
hundreds of machines to a new network setup, i wrote a
simple and fairly bone-headed ndb database checker,
ndb/vrfy.  it was amazingly effective, if provincial.

it catches all the classics like badly formatted eas,
duplicate eas,  mistyped keys (eg ethr and not ether)

the referenced, but not included, checkndb, checks
to see that the user was "okay" (not expired, in ndb),
but depends on local rules, kenfs and /adm/keystat,
none of which are universal.  /adm/keystat is a local
invention to allow anyone to make a rough check for
expired keys.  if you really want a copy email me privately.

- erik

[-- Attachment #2.1: Type: text/plain, Size: 312 bytes --]

from postmaster@plug:
The following attachment had content that we can't
prove to be harmless.  To avoid possible automatic
execution, we changed the content headers.
The original header was:

	Content-Type: text/troff
	Content-Disposition: attachment; filename=ndbvrfy
	Content-Transfer-Encoding: base64

[-- Attachment #2.2: ndbvrfy.suspect --]
[-- Type: application/octet-stream, Size: 971 bytes --]

.TH NDBVRFY 9
.SH NAME
ndb/vrfy, checkndb \- check ndb database
.SH SYNOPSIS
.B ndb/vrfy
[
.B -c
] [
-B -m
.I ip/mask
] [
.I file ...
]
.br
checkndb
.SH DESCRIPTION
.I Ndb/vrfy
verifies the syntax database file given
and applies a few ad-hoc heuristics.  With
the
.B -m
flag, all IP addresses in
.I file
are checked against the given
.IR ip/mask .
Normally,
.I ndb/vrfy
checks attributes against a generic list of
attributes.  with
.BR -c,
attributes suitable for use with
.IR consoledb (8)
are used.
.PP
.I Checkndb
checks all the files in
.B /lib/ndb
for reasonableness in the Coraid environment.
It is assumed that the owner's uid on the file server
match the third octet of the network.  Thus it
is assumed that
.I tor
owns
.BR /lib/ndb/10.220.1 .
and all the IP addresses in this file are within the
.B 10.220.1.0/112
network.
.SH SOURCE
.B /sys/src/cmd/ndb/vrfy.y
.br
.B /rc/bin/checkndb
.SH SEE ALSO
.IR consoledb (8),
.IR ndb (8)
.SH BUGS
Hopeless provinciality.

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [9fans] ndb database checker: ndb/vrfy
  2010-10-28 23:14 [9fans] ndb database checker: ndb/vrfy erik quanstrom
@ 2010-10-29  0:21 ` erik quanstrom
  0 siblings, 0 replies; 2+ messages in thread
From: erik quanstrom @ 2010-10-29  0:21 UTC (permalink / raw)
  To: 9fans

[-- Attachment #1: Type: text/plain, Size: 74 bytes --]

i have no idea why the yacc source wasn't included.
here it is.

- erik

[-- Attachment #2: vrfy.y --]
[-- Type: text/plain, Size: 9646 bytes --]

%{
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ip.h>

typedef struct T T;
struct T{
	T	*next;
	T	*down;
	char	*s;
	char	*t;
	int	line;
	char	*file;
};
#pragma	varargck	type	"T"	T*

Biobuf	in;
char	*file;
char	*ipmask;
int	line;
int	factotum;
T	*dblist;
T	*ll0;
T	**ll;
T	*tab0;
T	**tab;
int	fd;

extern	int	yyparse(void);
extern	void	yyerror(char*, ...);

char*
nonil(char *s)
{
	if(s == nil)
		return "";
	return s;
}

int
Tfmt(Fmt *f)
{
	int sharp;
	T *t, *t0;

	t0 = va_arg(f->args, T*);
	sharp = f->flags & FmtSharp;
	for(; t0 != nil; t0 = t0->down){
		if((t = t0) != nil)
			fmtprint(f, "%s=%s", t->s, nonil(t->t));
		while(t = t->next)
			fmtprint(f, " %s=%s", t->s, nonil(t->t));
		if(sharp == 0)
			break;
		if(t0->down != nil)
			fmtprint(f, "\n");
	}
	return 0;
}

T*
newt(char *s, char *t)
{
	T *x;
	x = malloc(sizeof *x);
	if(x == nil)
		sysfatal("malloc: %r");
	memset(x, 0, sizeof *x);
	x->s = s;
	x->t = t;
	x->line = line;
	x->file = file;
	return x;
}

static char *nakedtab[] = {
	"database",
	"soa",
	"trampok",
};

int
cknaked(char *s)
{
	int i;

	for(i = 0; i < nelem(nakedtab); i++)
		if(strcmp(nakedtab[i], s) == 0)
			return 0;
	yyerror("naked token %s", s);
	return -1;
}

%}

%union{
	T	*t;
	char	*s;
}

%type	<s>	rhs
%token	<s>	S
%start	db
%%
db	:
	| db '\n'
	| db tuples '\n'
	;
tuples:	tuple {
		*tab = ll0;
		tab = &ll0->down;

		ll0 = nil;
		ll = &ll0;
	}
	| tuple tuples
	;
tuple:	S rhs {
		T *x;

		if($2 == nil)
			cknaked($1);
		x = *ll = newt($1, $2);
		ll = &x->next;
	}
	;
rhs:		{ $$ = nil; }
	| '=' S	{ $$ = $2; }
	;
%%
void
freet(T *t)
{
	T *n, *d;

	for(; t != nil; t = d){
		d = t->down;
		for(; t != nil; t = n){
			n = t->next;
			free(t->s);
			free(t->t);
			free(t);
		}
	}
}

void
yyinit(char *f)
{
	file = f;
	line = 1;
	fd = open(file, OREAD);
	if(fd == -1)
		sysfatal("open: %r");
	if(Binit(&in, fd, OREAD) == -1)
		sysfatal("Binit: %r");

	freet(tab0);
	ll0 = nil;
	tab0 = nil;

	ll = &ll0;
	tab = &tab0;
}

void
yyerror(char *fmt, ...)
{
	char buf[256], *p, *e;
	va_list arg;
	static int nerrors;

	e = buf + sizeof buf;
	va_start(arg, fmt);
	p = seprint(buf, e, "%s:%d ", file, line);
	p = vseprint(p, e, fmt, arg);
	p = seprint(p, e, "\n");
	va_end(arg);
	write(2, buf, p - buf);
	nerrors++;
	if(nerrors > 5)
		sysfatal("too many errors");
}

void
tokerror(T *t, char *fmt, ...)
{
	char buf[256], *p, *e;
	va_list arg;
	static int nerrors;

	e = buf + sizeof buf;
	va_start(arg, fmt);
	p = seprint(buf, e, "%s:%d ", t->file, t->line);
	p = vseprint(p, e, fmt, arg);
	p = seprint(p, e, "\n");
	va_end(arg);
	write(2, buf, p - buf);
}

enum{
	Base,
	Seeneq,
};

int
yylex(void)
{
	char buf[128], *s;
	int c, i;
	Rune r;
	static int state = Base;
	static int quoting;

	if(quoting != 0){
		yyerror("bad quote");
		quoting = 0;
	}
	i = 0;
again:
	c = Bgetrune(&in);
	if(c == Beof)
		return 0;
	r = c;
	switch(r){
	case '
':
	case '\0':
		yyerror("bad character %#ux\n", r);
		goto again;
	case '#':
		for(;;){
			switch(Bgetrune(&in)){
			case Beof:
				yyerror("EOF in comment");
				return 0;
			case '\n':
				Bungetrune(&in);
				goto again;
			}
		}
	case '\n':
		if(state != Base){
			Bungetrune(&in);
			goto tok;
		}
		line++;
		switch(Bgetrune(&in)){
		case Beof:
			return '\n';
		case ' ':
		case '\t':
			goto again;
		}
		Bungetrune(&in);
		return '\n';
	case ' ':
	case '\t':
		if(state != Base){
			Bungetrune(&in);
			goto tok;
		}
		goto again;
	case '=':
		if(state != Base)
			break;
		state = Seeneq;
		return '=';
	default:
		break;
	}

	/* token */
	if(r == '"'){
		quoting = 1;
		c = Bgetrune(&in);
		r = c;
	}
	for(;;){
		if(i >= sizeof buf - UTFmax - 1){
			yyerror("token too long");
			break;
		}
		i += runetochar(buf + i, &r);
		c = Bgetrune(&in);
		if(c == Beof)
			break;
		r = c;
		switch(r){
		case '
':
		case '\0':
			yyerror("bad character %#ux\n", r);
			goto tok;
		case ' ':
		case '\t':
			if(quoting)
				break;
		case '\n':
			Bungetrune(&in);
			goto tok;
		case '=':
			if(state == Base){
				Bungetrune(&in);
				goto tok;
			}
			break;
		case '\'':
			if(factotum)
				goto quote;
			break;
		case '"':
			if(!factotum)
			if(quoting == 1){
				quoting = 0;
				goto tok;
			}
			break;
		case '#':
			yyerror("# in token");
		}
	}
tok:
	if(state == Base && i == 0)
		yyerror("name is nil");
	if(state == Seeneq)
		state = Base;
	buf[i] = 0;
	s = strdup(buf);
	if(s == nil)
		sysfatal("malloc: %r");
	yylval.s = s;
	return S;

quote:
	quoting = 1;
	for(;;){
		/* extra space for ' if quoting fails.  ndb is wierd */
		if(i >= sizeof buf - UTFmax - 1 - 1){
			yyerror("token too long");
			break;
		}
		i += runetochar(buf + i, &r);

		c = Bgetrune(&in);
		if(c == Beof)
			break;
		r = c;
		switch(r){
		case '\n':
			/* wierd!  this is actually what ndb does */
			Bungetrune(&in);
			memmove(buf, buf + 1, i);
			buf[0] = '\'';
			goto tok;
		case '\'':
			if(Bgetrune(&in) == '\'')
				break;
			Bungetrune(&in);
			goto tok;
		}
	}
	goto tok;
}

void
go(char *file)
{
	yyinit(file);
	yyparse();
	close(fd);
}

int
parseipxmask(uchar *ip, uchar *m, char *s0)
{
	uchar tmp[IPaddrlen];
	char *msk, s[4*8+4];

	snprint(s, sizeof s, "%s", s0);
	msk = strchr(s, '/');
	if(msk == nil){
		if(parseipmask(m, "/120") == -1)
			return -1;
	}else{
		if(parseipmask(m, msk) == -1)
			return -1;
		*msk = 0;
	}
	if(parseip(tmp, s) == -1)
		return -1;
	maskip(tmp, m, ip);
	return 0;
}

void
checkip(void)
{
	uchar m0[IPaddrlen], mask[IPaddrlen], ip1[IPaddrlen], m1[IPaddrlen];
	int havemsk;
	T *d, *t;

	havemsk = 0;
	if(ipmask != nil)
	if(parseipxmask(m0, mask, ipmask) == 0)
		havemsk = 1;
	for(d = tab0; d != nil; d = d->down)
		for(t = d; t != nil; t = t->next){
			if(strcmp(t->s, "ip") != 0)
				continue;
			if(parseip(ip1, t->t) == -1){
				tokerror(t, "bad ip %T", d);
				continue;
			}
			if(havemsk == 0)
				continue;
			maskip(ip1, mask, m1);
			if(ipcmp(m0, m1) != 0)
				tokerror(t, "wrong file: %I", t, ip1);
		}
}

typedef struct {
	uchar	ea[IPaddrlen];
	T	*d[10];
	int	nd;
} B;

static	B	*btab;
static	uint	nbtab;
static	uint	nbtaba;

int
addrcmp(uchar *a, uchar *b)
{
	int d;

	d = memcmp(a, b, IPaddrlen);
	if(d > 0)
		return 1;
	if(d < 0)
		return -1;
	return 0;
}

static B*
bsearch(uchar *ea)
{
	B *tab, *m;
	int n, i, d;

	tab = btab;
	for(n = nbtab; n > 0;){
		i = n/2;
		m = tab+i;
		d = addrcmp(m->ea, ea);
		if(d == 0)
			return m;
		if(d < 0){
			tab += i+1;
			n -= i+1;
		}else
			n = i;
	}
	return nil;
}

void
binsert(B *m, T *d, uchar *ea)
{
	char buf[256], *p, *e;
	int i;
	T **x;

	if(m == nil){
		if(nbtab + 1 >= nbtaba){
			nbtaba = nbtab + 1 << 1;
			btab = realloc(btab, sizeof btab[0]*nbtaba);
			if(btab == nil)
				sysfatal("realloc: %r");
		}
		for(m=btab+nbtab; m > btab && addrcmp(m[-1].ea, ea) > 0; m--)
			m[0] = m[-1];
		m->nd = 0;
		memcpy(m->ea, ea, IPaddrlen);
		nbtab++;
	}
	m->d[m->nd++] = d;
	if(m->nd > 1){
		x = m->d;
		p = buf;
		e = buf + sizeof buf;
		for(i = 0; i < m->nd - 1; i++)
			p = seprint(p, e, "%s:%d ", x[i]->file, x[i]->line);
		tokerror(d, "duplicate ea %E %s", m->ea, buf);
	}
}

void
prdb(void)
{
	int i;

	for(i = 0; i < nbtab; i++)
		print("%E\n", btab[i].ea);
}

void
checkether(void)
{
	uchar ea[IPaddrlen];
	T *d, *t;

	for(d = tab0; d != nil; d = d->down)
		for(t = d; t != nil; t = t->next){
			if(strcmp(t->s, "ether") != 0)
				continue;
			if(strspn(t->t, "0123456789abcdef") != 12)
				tokerror(t, "bad ea %s", t->t);
			parseether(ea, t->t);
			binsert(bsearch(ea), d, ea);
		}
}

static char *soaattr[] = {
	"refresh",
	"ttl",
	"ns",
	"mx",
	"pref",
	"mbox",
};

static char *cattrtab[] = {
	"clog",			/* only in consoledb */
	"console",
	"dev",
	"gid",
	"group",
	"speed",
	"uid",
	"openondemand",
};

static char *xattrtab[] = {
	"auth",
	"authdom",
	"bootf",
	"cname",
	"console",
	"database",
	"dns",
	"dnsdomain",
	"dom",
	"el",
	"ether",
	"file",
	"fs",
	"gw",
	"imap",
	"ip",
	"ipgw",
	"ipmask",
	"ipnet",
	"mb",
	"mbox",
	"mx",
	"ns",
	"pref",
	"proto",
	"refresh",
	"smtp",
	"soa",
	"sys",
	"trampok",
	"ttl",
	"txtrr",

	"pri",
	"srv",
	"weight",

	"gre",
	"il",
	"ipv4proto",
	"port",
	"protocol",
	"restricted",
	"tcp",
	"udp",

	"uid",
	"hostid"
};

char	**attrtab	= xattrtab;
int	nattrtab	= nelem(xattrtab);

void
checkattrs(void)
{
	int i;
	T *d, *t;

	for(d = tab0; d != nil; d = d->down)
		for(t = d; t != nil; t = t->next){
			for(i = 0; i < nattrtab; i++)
				if(strcmp(t->s, attrtab[i]) == 0)
					break;
			if(i == nattrtab)
				tokerror(t, "unknown attr %s", t->s);
		}
}

void
usage(void)
{
	fprint(2, "usage: ndb/vrfy [-m ip/mask]  [file ...]\n");
	exits("usage");
}

void
main(int argc, char **argv)
{
	int i;

	fmtinstall('T', Tfmt);
	fmtinstall('I', eipfmt);
	fmtinstall('M', eipfmt);
	fmtinstall('E', eipfmt);
	ARGBEGIN{
	case 'c':
		attrtab = cattrtab;
		nattrtab = nelem(cattrtab);
		break;
	case 'm':
		ipmask = EARGF(usage());
		break;
	default:
		usage();
	}ARGEND
	for(i = 0; i < argc; i++){
		go(argv[i]);
		checkip();
		checkether();
		checkattrs();
	}

	exits("");
}

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2010-10-29  0:21 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-10-28 23:14 [9fans] ndb database checker: ndb/vrfy erik quanstrom
2010-10-29  0:21 ` erik quanstrom

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).