Plan 9 from Bell Labs’s /usr/web/sources/contrib/steve/root/sys/src/cmd/snmpfs/a1.c

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


#include <u.h>
#include <libc.h>
#include <ip.h>
#include "a1.h"

int chatty = 0;

static char *snmpatype[] = {
	"get", "getn", "resp", "set", "trap"
};

static struct {
	int id;
	char *name;
} Valtype[] = {
	{ Aseq,		"seq" },
	{ Aint,		"int" },
	{ Aoctstr,	"str" },
	{ Anull,	"null" },
	{ Aobjid,	"obj" },
	{ Aipaddr,	"ip" },
	{ Acounter,	"count" },
	{ Agauge,	"gauge" },
	{ Atimeticks,	"time" }
};

static void
chat(char *fmt, ...)
{
	va_list v;

	if (!chatty)
		return;
	va_start(v, fmt);
	vfprint(2, fmt, v);
	va_end(v);
}

int
a1readlen(uchar *a, int *llen)
{
	int len, i;
	if((a[0] & 0x80) == 0){
		*llen = 1;
		return a[0];
	}
	*llen = (*a++ & 0x7f) + 1;
	for(i=0, len=0; i<*llen-1; i++)
		len = (len << 8) | *a++;
	return len;
}

int
a1readint(uchar *a, int len)
{
	int tot, neg, tlen;

	tlen = len;
	if(len > sizeof(int)) return 0;
	tot = *a & 0x7f;
	neg = *a & 0x80;

	for(tlen--, a++; tlen > 0; tlen--, a++)
		tot = (tot << 8) | *a;
	if(neg)
		tot -= (1<<((len*8)-1));
	return tot;
}

void
Sfree(Snmp *s) 
{
	free(s->pdu);
	free(s);
}
	
Snmp*
Salloc(void)
{
	Snmp *s;
	if((s = mallocz(sizeof(Snmp), 1)) == 0)
		return nil;
	return s;
}

int
oidfmt(Fmt *f)
{
	Oid oid;
	int i, m, dot;

	dot = 0;
	oid = va_arg(f->args, Oid);
	m = fmtprint(f, "%d.%d", oid.buf[0]/40, oid.buf[0]%40);
	for(i=1; i<oid.len; i++){
		dot = dot << 7 | (int)(oid.buf[i] & ~0x80);
		if((oid.buf[i] & 0x80) == 0){
			m += fmtprint(f, ".%d", dot);
			dot = 0;
		}
	}
	return m;
}

int
Sfmt(Fmt *f)
{
	Snmp *s;
	char *name;
	int i, j, rc;

	rc = 0;
	s = va_arg(f->args, Snmp *);
	for(i=0; i<s->npdu; i++){

		name = "unknown";
		for(j = 0; j < nelem(Valtype); j++)
			if(s->pdu[i].type == Valtype[j].id){
				name = Valtype[j].name;
				break;
			}

		switch(s->pdu[i].type){
		case Anull:
			break;
		case Aint:
			rc |= fmtprint(f, "%s %d", name, s->pdu[i].i);
			break;
		case Acounter:
			rc |= fmtprint(f, "%s %d", name, s->pdu[i].i);
			break;
		case Agauge:
			rc |= fmtprint(f, "%s %d", name, s->pdu[i].i);
			break;
		case Atimeticks:
			rc |= fmtprint(f, "%s %d", name, s->pdu[i].i);
			break;
		case Aoctstr:
			rc |= fmtprint(f, "%s %.*q", name, utfnlen(s->pdu[i].s, s->pdu[i].len), s->pdu[i].s);
			break;
		case Aobjid:
			rc |= fmtprint(f, "%s %O", name, (Oid){(uchar*)s->pdu[i].s, s->pdu[i].len});
			break;
		case Aipaddr:
			rc |= fmtprint(f, "%s %V", name, (uchar*)s->pdu[i].s);
			break;
		default:
			rc |= fmtprint(f, "%s type=0x%x", name, s->pdu[i].type);
			break;
		}
		rc |= fmtprint(f, "\n");
	}
	return rc;
}

void
Sdump(Snmp *s)
{

	fprint(2, "snmp v%d %s %s req %d estat %d eindex %d\n",
		s->vers, s->private ? "private" : "public",
		0xa0 <= s->type && s->type <= 0xa4 ? snmpatype[s->type-0xa0] : "<bad type>",
		s->reqid, s->estat, s->eindex);
	fprint(2, "\t%A\n", s);
}


int
Sscan(SnmpPdu *p, char *buf)
{
	int i;
	char *a[2];

	if(tokenize(buf, a, nelem(a)) < 2){
		werrstr("missing data type");
		return -1;
	}

	for(i = 0; i < nelem(Valtype); i++)
		if(strcmp(a[0], Valtype[i].name) == 0)
			break;
	if(i >= nelem(Valtype)){
		werrstr("unknown data type");
		return -1;
	}
	p->type = Valtype[i].id;

	switch(p->type){
	case Anull:
		return 0;
	case Aint:
	case Acounter:
	case Agauge:
	case Atimeticks:
		p->i = atoi(a[1]);
		p->len = sizeof(p->i);
		break;
	case Aoctstr:
		p->len = strlen(a[1]);
		p->s = a[1];			/* just use the buffer passed in to us */
		break;
	case Aipaddr:
		v4parseip((uchar *)a[0], a[1]);	/* we abuse the type name as a temporary buffer */
		p->s = a[0];
		p->len = 4;
		break;
	case Aseq:
	case Aobjid:
		werrstr("not supported");
		return -1;
		break;
	default:
		sysfatal("Sscan: internal error type=%d\n", p->type);
	}
	return 0;
}
int
Sparse(void *va, int na, Snmp *dst)
{
	SnmpPdu *pdu;
	uchar *a = va;
	uchar *ea = a+na;
	int j, tlen, llen, npdu;

	chat("Sparse %d...", ea-a);

	if(dst == 0){
		werrstr("bad args to Sparse");
		return -1;
	}

	chat(".");
	if(*a++ != Aseq || (tlen = a1readlen(a, &llen)) < 0)
		goto die;

	chat("tlen=%d...", tlen);
	a += llen;
	if(a+tlen < ea) 
		ea = a+tlen;

	if(a > ea) goto die;

	if(*a++ != Aint) goto die;
	if((j=a1readlen(a, &llen)) < 0) goto die;
	chat("llen =%d\n", llen);
	a++;
	dst->vers = a1readint(a, j);
	a += j;

	chat("vers=%d...", dst->vers);

	if(a > ea) goto die;

	chat("nxt=%d", *a);
	if(*a++ != Aoctstr || (j=a1readlen(a, &llen)) < 0) goto die;
	a += llen;

	if(a > ea) goto diespace;

	if(strncmp((char*)a, "private", j) == 0)
		dst->private = 1;
	else
		dst->private = 0;

	chat("(%d)%s...", j, dst->private?"private":"public");

	a += j;
	chat("%x", *a);
	if(*a < 0xa0 || *a > 0xa4){
		werrstr("bad pkt type");
		goto die;
	}

	dst->type = *a;
	a++;
	chat("type=%s", snmpatype[dst->type-0xa0]);
	if((j = a1readlen(a, &llen)) < 0) goto die;
	a += llen;

	if(ea < a+j) goto die;

	if(*a++ != Aint || (j=a1readlen(a, &llen)) < 0) goto die;
	a += llen;

	dst->reqid = a1readint(a, j);
	a += j;

	if(a > ea) goto die;

	if(*a++ != Aint || *a++ != 1) goto die;
	dst->estat = *a++;

	if(*a++ != Aint || *a++ != 1) goto die;
	dst->eindex = *a++;

	if(a > ea) goto die;

	if(*a++ != Aseq || (j = a1readlen(a, &llen)) < 0) goto die;
	a += llen;

	if(ea < a+j) goto die;

	npdu = 0;
	while(a < ea && npdu < Mpdu){
		chat("pdu...");
		++npdu;
		chat("npdu=%d", npdu);
		pdu = &dst->pdu[npdu-1];
		if(*a++ != Aseq || (a1readlen(a, &llen)) < 0) goto die;
		a += llen;
		if(a > ea) goto die;

		if(*a++ != Aobjid || (j=a1readlen(a, &llen)) < 0) goto die;
		a += llen;
		if(a > ea) goto die;

		pdu->objid = smprint("%O", (Oid){a,j} );
		chat("objid %s...", pdu->objid);

		a += j;
		pdu->type = *a;
		chat("type %d...", pdu->type);
		a++;
		if(a > ea) goto die;
		if((j = a1readlen(a, &llen)) < 0) goto die;
		a += llen;
		if(a > ea) goto die;
		pdu->len = j;
		chat("len %d...", j);
		switch(pdu->type){
		case Aint:
		case Acounter:
		case Agauge:
		case Atimeticks:
			pdu->i = a1readint(a, j);
			break;
		case Aoctstr:
		case Aobjid:
		case Aipaddr:
		default:
			pdu->s = (char*)a;
			break;
		}
		a += j;
		if(a > ea) goto die;
	}
	dst->npdu = npdu;
	return 0;

diespace:
	werrstr("out of space");
die:
	chat("die...a=%d ea=%d", a-(uchar*)va, ea-(uchar*)va);
	return -1;
}

static int
wobjid(uchar *oa, char *s)
{
	uchar *a = oa;
	int i, j, dot;
	int dots[Maxoids];
	char *f[Maxoids];
	int nf;

	s = strdup(s);
	nf = getfields(s, f, 32, 0, ".");
	if(nf < 0){
		free(s);
		return 0;
	}
	for(i=0; i<nf; i++)
		dots[i] = atoi(f[i]);
	for(j=nf-1; j>=2; j--){
		dot = dots[j];
		*--a = dot & 0x7f;
		dot >>= 7;
		while(dot){
			*--a = 0x80|(dot&0x7f);
			dot >>= 7;
		}
	}
	*--a = dots[0] * 40 + dots[1];
	free(s);
	return (oa-a);
}

static int
wlen(uchar *oa, int len)
{
	uchar *a = oa;
	if(len < 128){
		*--a = len;
		return 1;
	}
	while(len){
		*--a = len;
		len >>= 8;
	}
	--a;
	*a = (oa-a-1)|0x80;
	return (oa-a);
}

static int
wint(uchar *oa, int i)
{
	int n;
	uchar *a = oa;

	n = i;
	*--a = n;
	n >>= 8;
	while(n != 0 && n != -1){
		*--a = n;
		n >>= 8;
	}

	if(i > 0 && (*a & 0x80) == 0x80) 
		*--a = 0;
	if(i < 0 && (*a & 0x80) == 0) 
		*--a = 0xff;
	return (oa-a);
}

int
Sunparse(Snmp *s, void *va, int na)
{
	uchar *atop = va;
	uchar *a = va;
	uchar *oa;
	uchar *ea = a+na;
	SnmpPdu *pdu;
	int i,l;

	/* we build the packet backwards at the end of
	 * the buffer and then memmove it to the 
	 * beginning
	 */

	a = ea;
	pdu = &s->pdu[s->npdu];
	for(i=s->npdu-1; i>=0; i--){
		oa = a;
		pdu--;
		switch(pdu->type){
		case Aint:
		case Acounter:
		case Agauge:
		case Atimeticks:
			a -= wint(a, pdu->i);
			break;
		case Aobjid:
			a -= wobjid(a, pdu->s);
			break;

		case Anull:
		default:
			/* do nothing */
			break;
		case Aipaddr:
			pdu->len = 4;
			/* fall through */
		case Aoctstr:
			a -= pdu->len;
			memcpy(a, pdu->s, pdu->len);
			break;
		}

		pdu->len = oa-a;
		/* now write len of pdu data*/
		a -= wlen(a, pdu->len);
		*--a = pdu->type;

		/* now write objid */
		a -= (l=wobjid(a, pdu->objid));
		a -= wlen(a, l);
		*--a = Aobjid;
		
		/* now write len of pdu */
		a -= wlen(a, oa-a);
		*--a = Aseq;
	}

	/* now write length of all pdus */
	a -= wlen(a, ea-a);
	*--a = Aseq;

	/* eindex */
	*--a = 0;
	*--a = 1;
	*--a = Aint;

	/* estat */
	*--a = 0;
	*--a = 1;
	*--a = Aint;

	/* request id */
	a -= (l=wint(a, s->reqid));
	a -= wlen(a, l);
	*--a = Aint;

	/* let's just toss the length of the packet so far
	 * in here, just to make sure we haven't forgotten.
	 * bit decay, you know.
	 */
	a -= wlen(a, (ea-a));
	/* DO NOT *--a = Aseq; [sic] */

	*--a = s->type;
	
	if(s->private){
		a -= 7;
		memcpy(a, "private", 7);
		a -= wlen(a, 7);
	} else {
		a -= 6;
		memcpy(a, "public", 6);
		a -= wlen(a, 6);
	}

	*--a = Aoctstr;

	a -= (l=wint(a, s->vers));
	a -= wlen(a, l);
	*--a = Aint;

	/* bit decay check */
	a -= wlen(a, ea-a);
	*--a = Aseq;

	/* done! */
	memmove(atop, a, ea-a);
	return ea-a;
}

int readflag, alarmflag;

int
alarmtr(void*, char *why)
{
	if(!readflag)
		return 0;

	if(strcmp(why, "alarm")==0){
		alarmflag++;
		return 1;
	}
	return 0;
}

int
dosnmp(int nfd, Snmp *s, Snmp *srep)
{
	int nn, n, reqid;
	static char buf[4096];
	static int reg;

	if(!reg){
		atnotify(alarmtr, 1);
		reg = 1;
	}

	if(s == 0 || srep == 0){
		werrstr("bad args to dosnmp");
		return -1;
	}

	if(s->reqid == 0)
		s->reqid = (nsec()&0x7FFE)+1;
	reqid = s->reqid;
	n = Sunparse(s, buf, sizeof(buf));

	if(n <= 0){
		werrstr("Sunparse: %r");
		return -1;
	}

	nn = write(nfd, buf, n);
	if(nn != n){
		werrstr("write failed: %r");
		return -1;
	}

	memset(srep, 0, sizeof(*srep));
	alarmflag = 0;
	readflag = 1;
	alarm(5000);
	do {
		n = read(nfd, buf, sizeof buf);
		if(n > 0){ 
			chat("sparse %d?", n);
			memset(srep, 0, sizeof(*srep));
			if(Sparse(buf, n, srep) < 0){
				print("parse error: %r\n");
				continue;
			}
			if(srep->reqid != reqid)
				print("mismatch id %d %d\n", srep->reqid, reqid);
		}
	} while(!alarmflag && n > 0 && srep->reqid != reqid);

	alarm(0);
	if(alarmflag){
		werrstr("timeout");
		return -1;
	}
	readflag = 0;
	if(srep->reqid == reqid){
		return 0;
	}
	if(n < 0)
		werrstr("read failed: %r");
	else if(n == 0)
		werrstr("read got eof");
	else
		werrstr("got reqid %d wanted %d", srep->reqid, reqid);
	return -1;
}

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].