#include #include #include #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; iargs, Snmp *); for(i=0; inpdu; 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] : "", 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=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; }