/*------------------------------------------------------------------------ * parseoidlist - read a list of oid's from the input line *------------------------------------------------------------------------ */ LOCAL int parseoidlist( struct req_desc *rqdp, char **word) { struct snbentry *bl; while (**word != NULLCH) { if ((bl = parseoid(word)) == (struct snbentry *) SYSERR) { snfreebl(&rqdp->bindlf); /* Canberra */ return SYSERR; } if (rqdp->bindlf == NULLCH) rqdp->bindlf = rqdp->bindle = bl; else { bl->sb_prev = rqdp->bindle; rqdp->bindle = rqdp->bindle->sb_next = bl; } } return OK; }
/*------------------------------------------------------------------------ * snparse - convert the ASN.1-encoded SNMP packet into internal form * N.B. the binding list rqdp->bindlf must either be NULL or a valid * binding list, in which case I'll free it up. *------------------------------------------------------------------------ */ int snparse(struct req_desc *rqdp, u_char *snmppack, int len) { struct snbentry *bl, *lastbl = 0; register u_char *packp; int totpacklen = 0, commlen = 0; int pdulen, totbindlen = 0; u_int lenlen; int varbindlen = 0; u_char *packendp; packp = snmppack; packendp = snmppack + len; /* sequence operator and total packet length */ if (*packp++ != ASN1_SEQ || (totpacklen = a1readlen(packp, &lenlen)) < 0) { return SYSERR; } packp += lenlen; /* verify total length, version, community */ if (packendp != packp + totpacklen || *packp++ != ASN1_INT || *packp++ != SVERS_LEN || *packp++ != SVERSION || *packp++ != ASN1_OCTSTR || (commlen = a1readlen(packp, &lenlen)) < 0) { return SYSERR; } packp += lenlen; if (strncmp((char *)packp, SCOMM_STR, commlen) != 0) { return SYSERR; } packp += commlen; /* PDU type and length */ if (*packp == PDU_TRAP) return SYSERR; rqdp->pdutype_pos = packp - snmppack; rqdp->reqtype = *packp++; if ((pdulen = a1readlen(packp, &lenlen)) < 0) { return SYSERR; } packp += lenlen; /* verify PDU length */ if (packendp != packp + pdulen) { return SYSERR; } /* request id */ if (*packp++ != ASN1_INT || (rqdp->reqidlen = a1readlen(packp, &lenlen)) < 0) { return SYSERR; } packp += lenlen; memcpy(rqdp->reqid, packp, rqdp->reqidlen); packp += rqdp->reqidlen; /* error status */ if (*packp++ != ASN1_INT || *packp++ != 1) { return SYSERR; } rqdp->err_stat = *packp; rqdp->err_stat_pos = packp++ - snmppack; /* error index */ if (*packp++ != ASN1_INT || *packp++ != 1) { return SYSERR; } rqdp->err_idx = *packp; rqdp->err_idx_pos = packp++ - snmppack; /* sequence of variable bindings */ if (*packp++ != ASN1_SEQ || (totbindlen = a1readlen(packp, &lenlen)) < 0) { return SYSERR; } packp += lenlen; /* canberra bug fix - rqdp might already have old dynamic memory */ /* attached, free before overwriting pointers */ snfreebl(&rqdp->bindlf); /* verify variable bindings length */ if (packendp != packp + totbindlen) return SYSERR; /* build doubly-linked bindings list; fill in only sb_a1str's */ rqdp->bindlf = rqdp->bindle = (struct snbentry *) NULL; do { bl = (struct snbentry *) getmem(sizeof(struct snbentry)); bl->sb_next = 0; bl->sb_prev = 0; if (rqdp->bindlf) { lastbl->sb_next = bl; bl->sb_prev = lastbl; lastbl = bl; } else lastbl = rqdp->bindlf = bl; bl->sb_a1str = packp; bl->sb_a1dynstr = FALSE; /* not dynamically allocated */ if (*packp++ != ASN1_SEQ || (varbindlen = a1readlen(packp, &lenlen)) < 0) { return SYSERR; } packp += lenlen; bl->sb_a1slen = varbindlen; packp += varbindlen; } while (packp < packendp); /* check that the entire packet has now been parsed */ if (packp != packendp) { return SYSERR; } rqdp->bindle = lastbl; return OK; }
/*------------------------------------------------------------------------ * snmpd - open the SNMP port and handle incoming queries *------------------------------------------------------------------------ */ int snmpd() { int snmpdev, len; struct xgram *query; struct req_desc rqd; char str[128]; IPaddr from; sninit(); query = (struct xgram *) getmem(sizeof (struct xgram)); /* open the SNMP server port */ if ((snmpdev = xinu_open(UDP, ANYFPORT, SNMPPORT)) == SYSERR) return SYSERR; while (TRUE) { /* * In this mode, give read the size of xgram, it returns * number of bytes of *data* in xgram. */ len = xinu_read(snmpdev, query, sizeof(struct xgram)); blkcopy(from, query->xg_fip, IP_ALEN); xinu_sprintf(str, "\n snmpd: from %u.%u.%u.%u", BYTE(from, 0), BYTE(from, 1), BYTE(from, 2), BYTE(from, 3)); xinu_write(SNMPLOG, str, xinu_strlen(str)); /* parse the packet into the request desc. structure */ if (snparse(&rqd, query->xg_data, len) == SYSERR) { snfreebl(rqd.bindlf); continue; } /* convert ASN.1 representations to internal forms */ if (sna2b(&rqd) == SYSERR) { snfreebl(rqd.bindlf); continue; } if (snrslv(&rqd) == SYSERR) { len = mksnmp(&rqd, query->xg_data, PDU_RESP); /* above call frees string memory */ query->xg_data[rqd.pdutype_pos] = PDU_RESP; query->xg_data[rqd.err_stat_pos] = rqd.err_stat; query->xg_data[rqd.err_idx_pos] = rqd.err_idx; if (xinu_write(snmpdev, query, len) == SYSERR) return SYSERR; snfreebl(rqd.bindlf); continue; } len = mksnmp(&rqd, query->xg_data, PDU_RESP); if (len == SYSERR) { query->xg_data[rqd.pdutype_pos] = PDU_RESP; query->xg_data[rqd.err_stat_pos] = rqd.err_stat; query->xg_data[rqd.err_idx_pos] = rqd.err_idx; if (xinu_write(snmpdev, query, len) == SYSERR) return SYSERR; snfreebl(rqd.bindlf); continue; } if (xinu_write(snmpdev, query, len) == SYSERR) return SYSERR; snfreebl(rqd.bindlf); } }
/*------------------------------------------------------------------------ * sendquery - parse the input line and send the query. Input has * one of the following forms: * [object-name]+ * next [object-name]+ * set [object-name type value]+ *------------------------------------------------------------------------ */ LOCAL int sendquery( int stdout, char *server) { struct req_desc rqd; struct snbentry *bl; char *word; int repl; initgetword(buf); rqd.reqtype = PDU_GET; /* by default */ rqd.bindle = rqd.bindlf = NULLCH; getword(&word); if (*word == '\0') return OK; if (strequ(word, "next")) { rqd.reqtype = PDU_GETN; getword(&word); if (parseoidlist(&rqd, &word) == SYSERR) { fprintf(stdout, "unknown variable\n"); return SYSERR; } if ((bl = rqd.bindlf) == (void *)NULLPTR) { if (lastoidset) { /* no oids so use last one */ rqd.bindlf = rqd.bindle = bl = getnewbl(); bl->sb_oid.len = lastobjid.len; memcpy(bl->sb_oid.id, lastobjid.id, lastobjid.len*2); SVTYPE(bl) = ASN1_NULL; } else { fprintf(stdout, "bad syntax\n"); return SYSERR; } } } else if (strequ(word, "set")) { rqd.reqtype = PDU_SET; if (parseset(&rqd, stdout) == SYSERR) return SYSERR; } else if (parseoidlist(&rqd, &word) == SYSERR) { fprintf(stdout, "unknown variable\n"); return SYSERR; } repl = snclient(&rqd, server, stdout); switch (repl) { case SCL_OK: if (rqd.err_stat == SNMP_OK) snmpprint(stdout, rqd.bindlf); else snerr(stdout, &rqd); break; case SCL_OPENF: fprintf(stdout, "snmp: open failed\n"); break; case SCL_WRITEF: fprintf(stdout, "snmp: write failed\n"); break; case SCL_NORESP: fprintf(stdout, "snmp: No response from server %s\n", server); break; case SCL_READF: fprintf(stdout, "snmp: read failed\n"); break; case SCL_BADRESP: fprintf(stdout, "snmp: received bad response\n"); break; } /* save this object for use with the "next" operation */ lastobjid.len = rqd.bindlf->sb_oid.len; memcpy(lastobjid.id, rqd.bindlf->sb_oid.id, rqd.bindlf->sb_oid.len*2); lastoidset = TRUE; snfreebl(&rqd.bindlf); return OK; }