#include <u.h>
#include <libc.h>
#include <ip.h>
#include "a1.h"
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;
}
char *snmpatype[] = { "get", "getn", "resp", "set", "trap" };
void
Sfree(Snmp *s)
{
free(s->pdu);
free(s);
}
Snmp*
Salloc(void)
{
Snmp *s;
if((s = malloc(sizeof(*s))) == 0)
return 0;
memset(s, 0, sizeof(*s));
return s;
}
int
objidfmt(char *s, int ns, uchar *a, int na)
{
int i, n, dot;
dot = 0;
n = snprint(s, ns, "%d.%d",
a[0]/40, a[0]%40);
for(i=1; i<na; i++) {
dot = dot << 7 | (int)(a[i] & ~0x80);
if((a[i] & 0x80) == 0) {
n += snprint(s+n, ns-n,
".%d", dot);
dot = 0;
}
}
return n;
}
void
Sprint(Snmp *s)
{
int i;
char buf[Objidlen];
print("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);
for(i=0; i<s->npdu; i++){
print("\t%s ", s->pdu[i].objid);
switch(s->pdu[i].type) {
case Anull:
break;
case Aint:
print("int %d", s->pdu[i].i);
break;
case Acounter:
print("counter %d", s->pdu[i].i);
break;
case Agauge:
print("gauge %d", s->pdu[i].i);
break;
case Atimeticks:
print("timeticks %d", s->pdu[i].i);
break;
case Aoctstr:
print("octstr %.*s", utfnlen(s->pdu[i].s, s->pdu[i].len), s->pdu[i].s);
break;
case Aobjid:
objidfmt(buf, sizeof buf, (uchar*)s->pdu[i].s, s->pdu[i].len);
print("objid %s", buf);
break;
case Aipaddr:
print("ipaddr %V", (uchar*)s->pdu[i].s);
break;
default:
print("unknown type %d", s->pdu[i].type);
break;
}
print("\n");
}
}
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;
objidfmt(pdu->objid, Objidlen, 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[32];
char *f[32];
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 neg, n;
uchar *a = oa;
n = i;
neg = 0;
if(n < 0) {
neg = 1;
n = -n;
}
*--a = n;
n >>= 8;
while(n) {
*--a = n;
n >>= 8;
}
if(*a & 0x80)
*--a = 0;
if(neg) *a |= 0x80;
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("alarmed");
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;
}
|