#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;
}
|