#include #include #include #include #include #include <9p.h> #include #include "ber.h" #include "snmp.h" int alarmflag; int readflag; char *pduerror[] = { "noError", "tooBig", "noSuchName", "badValue", "readOnly", "genErr", "noAccess", "wrongType", "wrongLength", "wrongEncoding", "wrongValue", "noCreation", "inconsistenValue", "resourceUnavailable", "commitFailed", "undoFailed", "authorizationError", "notWritable", "inconsistentName" }; /* debug or error */ int error(int level, char *format, ... ) { va_list args; char lasterror[ERRMAX]; va_start(args, format); vsnprint(lasterror,ERRMAX, format, args); va_end(args); werrstr(lasterror); switch(level) { case 1: fprint(2,"Debug:%r\n"); return 0; break; case 2: fprint(2,"Error: %r\n"); abort(); break; } return 0; } /* creates a new snmp request id */ /* based on rsc code */ int mkreqid(void) { return (nsec() & 0x7FFE) + 1; } void freesession(Session *s) { free(s->host); free(s->port); free(s->rocomm); free(s->rwcomm); free(s); } void freevarbind(Tuple *t, int len) { int i; if ( t == nil || len < 1 ) return; for(i=0;ipdu.varbind,p->pdu.nbind); if ( p->comm != nil ) free(p->comm); free(p); } int mkwalk(Packet * p, Packet *o) { int i; p->ver = o->ver; p->comm = o->comm; p->pdu.type = GetNextRequest; p->pdu.reqid = o->pdu.reqid; p->pdu.errstat = 0; p->pdu.erridx = 0; p->pdu.nbind= o->pdu.nbind; freevarbind(p->pdu.varbind,p->pdu.nbind); p->pdu.varbind = (Tuple*)emalloc(sizeof(Tuple)*p->pdu.nbind); for(i=0;ipdu.nbind;i++){ p->pdu.varbind[i].oid = smprint("%s",o->pdu.varbind[i].oid); p->pdu.varbind[i].value = nil; } return 1; } /* The End-Of-MIB message is not always supported */ /* so we need some heuristics to do correct walks */ /* All oids of a packet must complain to be able to walk */ int canwalk(Packet *orig, Packet *p1, Packet *p2) { int origlen, oid1len; int i; if ( p1 == nil || p2 == nil ) { error(DEBUG,"canwalk(): if no packet we can walk to get one"); return 1; } for (i=0;ipdu.nbind;i++) { oid1len = strlen(p1->pdu.varbind[i].oid); origlen = strlen(orig->pdu.varbind[i].oid); if ( strncmp(p1->pdu.varbind[i].oid, p2->pdu.varbind[i].oid, oid1len) == 0 ) { error(DEBUG,"canwalk(): error: p1 and p2 are the same"); return 0; } if ( strncmp(orig->pdu.varbind[i].oid,p2->pdu.varbind[i].oid,origlen) != 0 ) { error(DEBUG,"canwalk(): error: p2 is out of branch: %s -- %s",orig->pdu.varbind[i].oid,p2->pdu.varbind[i].oid); return 0; } } /* if i am here, i can walk other packet */ return 1; } /* PDU is an IMPLICIT SEQUENCE * and that means the SEQUENCE id * is changed using one of those * specified and them should be encoded as they * should appear in the packet, ie, no more * transformations are done */ Elem mkpdu(int type, Elist *el) { Elem e; e.tag.class = type; e.tag.num = SEQUENCE; e.val.tag = VSeq; e.val.u.seqval = el; return e; } /* this is used to compose set-request message */ /* only int and octectstring */ Elem char2asn(char *data) { /* for normal requests */ if ( data == nil ) { error(DEBUG,"char2asn(): data = nil"); return Null(); } /* for set-request only octect-strings and integers */ if ( data[0] < '0' || data[0] > '9' ) return mkoctet((uchar*)data,strlen(data)); else return mkint(atoi(data)); } Elem oid2asn(char *s) { Elem e; Ints *o; char *f[MAX_ELEM]; int i, n; char *aux; aux = strdup(s); n = getfields(aux, f, MAX_ELEM, 0, "."); if ( n < 0 || atoi(f[0]) < 1 ) { free(aux); return e; } o = (Ints*) emalloc9p(sizeof(Ints)*n); o->len = n; for(i=0;idata[i] = atoi(f[i]); e = mkoid(o); error(DEBUG,"oid2asn(): oid = %s o->len = %d",s,o->len); free(aux); freeints(o); return e; } char* asn2char(Elem e) { Value v; int i; Fmt fmt; char *aux; mpint *m; fmtstrinit(&fmt); v = e.val; /* chek if i have a valid type before trying to decode it */ if ( e.tag.class > 30 ) { switch(e.tag.class) { case IpAddress: fmtprint(&fmt,"%V",v.u.octetsval->data); break; case Counter: case Gauge: case TimeTicks: case Opaque: case NsapAddress: case Counter64: case UIInteger32: m = betomp(v.u.octetsval->data, v.u.octetsval->len, nil); aux = mptoa(m,10,nil,0); fmtprint(&fmt,"%s",aux); mpfree(m); free(aux); break; case EndOfMIB: fmtprint(&fmt,"End of MIB"); break; default: fmtprint(&fmt,"unknown class type %x and val tag %x",e.tag.class,e.val.tag); break; } return fmtstrflush(&fmt); } /* TODO: complete all known types */ if ( e.tag.class == Universal ) switch(v.tag){ case VBool: fmtprint(&fmt,"BOOLEAN");break; case VInt: fmtprint(&fmt,"%d",v.u.intval); break; case VOctets: /* heuristic is need to know if it is a printable string or not */ if ( isalnum(v.u.octetsval->data[0]) ) { aux = (char*)emalloc(v.u.octetsval->len+1); memcpy(aux,v.u.octetsval->data, v.u.octetsval->len); aux[v.u.octetsval->len] = '\0'; fmtprint(&fmt,"%s",aux); free(aux); } else for(i=0;ilen;i++) fmtprint(&fmt,"%.2x",v.u.octetsval->data[i]); break; case VBigInt: fmtprint(&fmt,"BINGINT");break; case VReal: fmtprint(&fmt,"REAL");break; case VOther: fmtprint(&fmt,"OTHER"); break; case VBitString: fmtprint(&fmt,"BITSTRING");break; case VNull: fmtprint(&fmt,"NULL");break; case VEOC: fmtprint(&fmt,"EOC");break; case VObjId: for(i = 0; ilen; i++) fmtprint(&fmt,"%d%s", v.u.objidval->data[i],(i != (v.u.objidval->len-1)) ? ".":""); break; case VString: fmtprint(&fmt,"%s",v.u.stringval); break; case VSeq: fmtprint(&fmt,"SEQUENCE");break; case VSet: fmtprint(&fmt,"SET");break; default: fmtprint(&fmt,"unknown class type %x and val tag %x",e.tag.class,e.val.tag); break; } return fmtstrflush(&fmt); } /* ---- Var-Bind-List =SEQUENCE Var-Bind = SEQUENCE oid OBJECT IDENTIFIER value NULL oid OBJECT IDENTIFIER value NULL ---*/ Elem mkvarbind(Packet *p) { Elist *z=nil; int i; Elem oid; Elem value; for(i=0; i < p->pdu.nbind; i++) { value = char2asn(p->pdu.varbind[i].value); oid = oid2asn(p->pdu.varbind[i].oid); z = mkel(mkseq(mkel(oid, mkel(value, nil))), z); } return mkseq(z); } /* ---- getBulkRequest PDU ASN1 Request-ID INTEGER Non-Repeaters INTEGER Max-Repetition INTEGER Var-Bind-List =SEQUENCE ---- */ Elist * mkgetbulkreq(Packet *p) { Elist *e; e = mkel(mkpdu((int)p->pdu.type, mkel(mkint(p->pdu.reqid), mkel(mkint(p->pdu.nrep), mkel(mkint(p->pdu.maxrep), mkel(mkvarbind(p),nil))))),nil); return e; } /* ----- getNextRequest PDU ASN1 Request-ID INTEGER Error-Status INTEGER Error-Index INTEGER Var-Bind-List =SEQUENCE -----*/ Elist * mkgetnreq(Packet *p) { Elist *e; e = mkel(mkpdu((int)p->pdu.type, mkel(mkint(p->pdu.reqid), mkel(mkint(p->pdu.errstat), mkel(mkint(p->pdu.erridx), mkel(mkvarbind(p),nil))))),nil); return e; } /* ASN.1 snmp packet encoding */ /* message = SEQUECE */ /* version INTEGER */ /* community OCTECT_STRING */ /* PDU = IMPLICIT SEQUENCE */ int encpkt(Packet *p, Bytes **pbytes) { int err; Bytes *bsnmp; Elem snmp; Elem ver; Elem comm; Elist * pdu; switch(p->pdu.type){ case GetRequest: case SetRequest: case GetNextRequest: pdu = mkgetnreq(p); break; case GetBulkRequest: pdu = mkgetbulkreq(p); break; default: error(DEBUG,"encpkt(): unknown op\n");return 0; break; } if ( p->comm == nil ) { error(DEBUG,"encpkt(): community not found"); return 0; } ver = mkint(p->ver); comm = mkoctet((uchar*)p->comm,strlen(p->comm)); snmp = mkseq(mkel(ver, mkel(comm, pdu))); err = encode(snmp, &bsnmp); if ( err != ASN_OK ) { error(DEBUG,"encpkt(): cannot encode the pkt: %d %r\n",err); freevalfields(&snmp.val); return 0; } *pbytes = bsnmp; freevalfields(&snmp.val); return 1; } /* translates an asn1 stream to the pdu struct */ int dec_pdu(Elist *el, Pdu * p) { Elem *e, *last; Elist *pel; Elist *elvar = nil; Elist *elbind = nil; int tabsize, i; e = &el->hd; if( is_int(e,&p->reqid) ) { pel = el->tl; e = &pel->hd; if ( is_int(e,&p->errstat) ) { pel = pel->tl; e= &pel->hd; if ( is_int(e,&p->erridx) ) { pel = pel->tl; e = &pel->hd; } else { error(DEBUG,"dec_pdu(): reqid not found"); return 0; } } else { error(DEBUG,"dec_pdu(): errstat not found"); return 0; } }else { error(DEBUG,"dec_pdu(): erridx not found"); return 0; } p->nbind = 0; if ( is_seq(e,&elvar) ) { e = &elvar->hd; tabsize = elistlen(elvar); if ( tabsize <= 0 ) { error(DEBUG,"dec_pdu(): varbind length %d",tabsize); return 0; } p->varbind = (Tuple*)emalloc(sizeof(Tuple)*tabsize); for(i=0;ihd; pel = pel->tl; if ( last->val.tag == VObjId ) { /* store them in reverse order to correct what the ber code did */ p->varbind[tabsize-1-i].oid = asn2char(*last); p->varbind[tabsize-1-i].value = asn2char(pel->hd); p->nbind++; } else { free(p->varbind); error(DEBUG,"dec_pdu(): Error decoding var list"); return 0; } } else { free(p->varbind); error(DEBUG,"dec_pdu(): Error decoding bind list"); return 0; } elvar = elvar->tl; e = &elvar->hd; } } else { error(DEBUG,"dec_pdu(): Error decoding var list"); return 0; } return 1; } /* decode a pdu with a quick hack: */ /* the asn1 code treats the unknown objects as octetstring; */ /* so we can encode our pdu with the octet-string tag */ /* and change that tag with the sequence-type that is the correct */ /* ( pdu definition says it is IMPLICIT SEQUENCE and that is a */ /* sequence with the correspondant pdu id 0xa1..0xa8*/ int pdu2asn(Elem *epdu, Elem *e) { int err; Bytes *ocpdu; if ( encode(*epdu,&ocpdu) == ASN_OK) *ocpdu->data = 0x30; /* pdu is a sequence, so we make it that */ else { error(DEBUG,"pdu2asn(): error encoding octet-pdu: "); freebytes(ocpdu); return 0; } /* decode what we encoded, to obtain the pdu */ err = decode(ocpdu->data,ocpdu->len,e); freebytes(ocpdu); if ( err != ASN_OK ) { error(DEBUG,"pdu2asn(): error decoding pdu: "); return 0; } return 1; } int decpkt(uchar *buff, int n, Packet *p) { Bytes *bbuff, *bcomm; Elem emess, *ever, *ecomm, pdu, *epdu; Elist *elmess, *el, *elpdu; int pver; bbuff = makebytes(buff, n); if (decode(bbuff->data,bbuff->len,&emess) != ASN_OK) { error(DEBUG,"decpkt: error in ASN.1 decode"); freebytes(bbuff); return 0; } freebytes(bbuff); if( !is_seq(&emess,&elmess) ) { error(DEBUG,"decpkt: cannot recognize message"); freevalfields(&emess.val); return 0; } if ( elistlen(elmess) != 3 ) { error(DEBUG,"decpkt: cannot recognize message: n = %d",elistlen(elmess)); freevalfields(&emess.val); return 0; } ever = &elmess->hd; el = elmess->tl; ecomm = &el->hd; el = el->tl; epdu = &el->hd; if ( !is_int(ever,&pver) ) { error(DEBUG,"decpkt: error decoding version field"); freevalfields(&emess.val); return 0; } p->ver = pver; if ( !is_octetstring(ecomm,&bcomm) ) { error(DEBUG,"decpkt: error decoding community field"); freevalfields(&emess.val); return 0; } p->comm = (char*)emalloc9p(bcomm->len); memcpy(p->comm,(char*)bcomm->data,bcomm->len); p->comm[bcomm->len-1]='\0'; if ( ! pdu2asn(epdu,&pdu) ) { error(DEBUG,"decpkt: error in pdu2asn"); freevalfields(&pdu.val); freevalfields(&emess.val); return 0; } if ( !is_seq(&pdu,&elpdu) ){ error(DEBUG,"decpkt: error decoding pdu"); freevalfields(&pdu.val); freevalfields(&emess.val); return 0; } if ( ! dec_pdu(elpdu,&p->pdu) ) { error(DEBUG,"decpkt(): error decoding pdu field"); freevalfields(&emess.val); return 0; } freevalfields(&emess.val); freevalfields(&pdu.val); return 1; } /* timeout * Code from rsc */ int alarmtr(void*, char *why) { if(!readflag) return 0; if(strcmp(why, "alarm") == 0) { alarmflag++; return 1; } return 0; } int talk(Session *s, Packet *req, Packet *recv) { int n, reqid,fd; uchar recvbuf[DMAX_PKT]; Bytes * snmp = nil; if ( req->pdu.type == SetRequest ) req->comm = strdup(s->rwcomm); else req->comm = strdup(s->rocomm); if (! encpkt(req, &snmp)) { error(DEBUG,"talk(): Error encoding Packet to ASN1"); freebytes(snmp); return 0; } reqid = req->pdu.reqid; if ((fd = dial(netmkaddr(s->host, "udp", s->port), 0, 0, 0)) < 0) { error(DEBUG,"talk(): error dialing %s",s->host); freebytes(snmp); return 0; } do { n = write(fd, snmp->data, snmp->len); if (n != snmp->len) { error(DEBUG,"talk(): Error writing to %d",fd); freebytes(snmp); return 0; } freebytes(snmp); /* set alarm to exit the while when timeout reached */ alarmflag = 0; readflag = 1; threadnotify(alarmtr, 1); alarm(s->timeout*1000); do { n = read(fd, recvbuf, DMAX_PKT); if (n > 0) { if ( ! decpkt(recvbuf, n, recv) ) continue; else if ( reqid == recv->pdu.reqid ) { close(fd); alarm(0); return 1; } } } while(!alarmflag); if (alarmflag) { s->retries--; error(DEBUG,"talk(): Timeout %d s. Wil retry %d times",s->timeout,s->retries); } } while (s->retries > 0); close(fd); alarm(0); return 0; } /* from rsc */ char * dumppkt(Packet *p) { int i,e; Fmt fmt; fmtstrinit(&fmt); for(i=0;ipdu.nbind;i++) { e = p->pdu.errstat; if ( p->pdu.erridx != i+1 ) e=noError; fmtprint(&fmt,"((%s) (%s) (%s))\n", p->pdu.varbind[i].oid, p->pdu.varbind[i].value,pduerror[e]); } return fmtstrflush(&fmt); } char * doget(Session *s, Packet * req) { char *buff; Packet *recv; recv = (Packet*)emalloc(sizeof(Packet)); if ( talk(s, req, recv) ) { buff = dumppkt(recv); freepacket(recv); return buff; } else { error(DEBUG,"doget(): error talking snmp: %r"); freepacket(recv); return nil; } } char * dowalk(Session *s, Packet *orig) { Packet *recv; Packet *req; Packet *last; char *aux; Fmt fmt; fmtstrinit(&fmt); recv= (Packet*)emalloc9p(sizeof(Packet)); if ( ! talk(s, orig, recv) ) { error(DEBUG,"dowalk(): error talking snmp"); freepacket(recv); return nil; } aux = dumppkt(recv); fmtprint(&fmt,"%s", aux); free(aux); last = nil; while ( canwalk(orig,last, recv) ) { req = (Packet*)emalloc9p(sizeof(Packet)); mkwalk(req,recv); freepacket(last); last = recv; recv = (Packet*)emalloc9p(sizeof(Packet)); if ( ! talk(s, req, recv) ) { error(DEBUG,"dowalk(): error talking snmp"); freepacket(recv); return nil; } aux = dumppkt(recv); fmtprint(&fmt,"%s", aux); free(aux); freepacket(req); } freepacket(last); freepacket(recv); return fmtstrflush(&fmt); } char * dobulk(Session *s, Packet * req) { return dowalk(s, req); } char * doset(Session *s, Packet * req) { return doget(s, req); } char* dogetn(Session *s, Packet *req) { return doget(s,req); } char * dosnmp(Session *s, Packet *req) { char *buff; /* GetNextRequest is used to walk the snmp tree, */ /* while WalkRequest is used to identify the operation */ switch(req->pdu.type) { case GetRequest: buff = doget(s, req); break; case GetNextRequest: buff = dogetn(s,req); break; case WalkRequest: req->pdu.type = GetNextRequest; buff = dowalk(s,req); break; case GetBulkRequest: buff = dobulk(s, req); break; case SetRequest: buff = doset(s, req); break; default: buff = smprint("(dosnmp(): Unknown PDU type %X)\n",req->pdu.type); break; } if ( buff == nil ) buff =smprint("(%r)\n"); return buff; }