#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "asn1.h"
#include "pdu.h"
static int snmp_add_null_vars(snmp_pdu_t *, char *, int, int);
static oid *snmp_oidstr_to_oid(int, char *, int, size_t *);
static uchar_t *snmp_build_pdu(snmp_pdu_t *, uchar_t *, size_t *);
static uchar_t *snmp_build_variable(uchar_t *, size_t *, oid *, size_t,
uchar_t, void *, size_t);
static uchar_t *snmp_parse_pdu(int, uchar_t *, size_t *, snmp_pdu_t *);
static uchar_t *snmp_parse_variable(uchar_t *, size_t *, pdu_varlist_t *);
static void snmp_free_null_vars(pdu_varlist_t *);
static uchar_t *snmp_def_community = (uchar_t *)SNMP_DEF_COMMUNITY;
snmp_pdu_t *
snmp_create_pdu(int cmd, int max_reps, char *oidstrs, int n_oids, int row)
{
snmp_pdu_t *pdu;
if ((cmd != SNMP_MSG_GET) && (cmd != SNMP_MSG_GETNEXT) &&
(cmd != SNMP_MSG_GETBULK)) {
return (NULL);
}
pdu = (snmp_pdu_t *)calloc(1, sizeof (snmp_pdu_t));
if (pdu == NULL)
return (NULL);
if (cmd == SNMP_MSG_GET || cmd == SNMP_MSG_GETNEXT) {
pdu->version = SNMP_VERSION_1;
pdu->errstat = 0;
pdu->errindex = 0;
} else if (cmd == SNMP_MSG_GETBULK) {
pdu->version = SNMP_VERSION_2c;
pdu->non_repeaters = 0;
pdu->max_repetitions = max_reps ?
max_reps : SNMP_DEF_MAX_REPETITIONS;
}
pdu->command = cmd;
pdu->reqid = snmp_get_reqid();
pdu->community = snmp_def_community;
pdu->community_len = SNMP_DEF_COMMUNITY_LEN;
if (snmp_add_null_vars(pdu, oidstrs, n_oids, row) < 0) {
free((void *) pdu);
return (NULL);
}
pdu->req_pkt = NULL;
pdu->req_pktsz = 0;
pdu->reply_pkt = NULL;
pdu->reply_pktsz = 0;
return (pdu);
}
int
snmp_make_packet(snmp_pdu_t *pdu)
{
uchar_t *buf, *p;
uchar_t *msg_seq_end;
uchar_t id;
size_t bufsz = SNMP_DEF_PKTBUF_SZ;
size_t seqlen;
if ((buf = (uchar_t *)calloc(1, SNMP_DEF_PKTBUF_SZ)) == NULL)
return (-1);
id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
if ((p = asn_build_sequence(buf, &bufsz, id, 0)) == NULL) {
free((void *) buf);
return (-1);
}
msg_seq_end = p;
id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
if ((p = asn_build_int(p, &bufsz, id, pdu->version)) == NULL) {
free((void *) buf);
return (-1);
}
id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR;
p = asn_build_string(p, &bufsz, id, pdu->community, pdu->community_len);
if (p == NULL) {
free((void *) buf);
return (-1);
}
if ((p = snmp_build_pdu(pdu, p, &bufsz)) == NULL) {
free((void *) buf);
return (-1);
}
seqlen = p - msg_seq_end;
id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
(void) asn_build_sequence(buf, NULL, id, seqlen);
pdu->req_pkt = buf;
pdu->req_pktsz = p - buf;
return (0);
}
snmp_pdu_t *
snmp_parse_reply(int reqid, uchar_t *reply_pkt, size_t reply_pktsz)
{
snmp_pdu_t *reply_pdu;
uchar_t *p;
size_t msgsz = reply_pktsz;
uchar_t exp_id;
reply_pdu = (snmp_pdu_t *)calloc(1, sizeof (snmp_pdu_t));
if (reply_pdu == NULL)
return (NULL);
exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
if ((p = asn_parse_sequence(reply_pkt, &msgsz, exp_id)) == NULL) {
snmp_free_pdu(reply_pdu);
return (NULL);
}
if ((p = asn_parse_int(p, &msgsz, &reply_pdu->version)) == NULL) {
snmp_free_pdu(reply_pdu);
return (NULL);
}
if ((reply_pdu->version != SNMP_VERSION_1) &&
(reply_pdu->version != SNMP_VERSION_2c)) {
snmp_free_pdu(reply_pdu);
return (NULL);
}
p = asn_parse_string(p, &msgsz, &reply_pdu->community,
&reply_pdu->community_len);
if (p == NULL) {
snmp_free_pdu(reply_pdu);
return (NULL);
}
if ((p = snmp_parse_pdu(reqid, p, &msgsz, reply_pdu)) == NULL) {
snmp_free_pdu(reply_pdu);
return (NULL);
}
return (reply_pdu);
}
static int
snmp_add_null_vars(snmp_pdu_t *pdu, char *oidstrs, int n_oids, int row)
{
pdu_varlist_t *vp, *prev;
pdu_varlist_t *varblock_p = NULL;
char *p;
int i;
prev = NULL;
p = oidstrs;
for (i = 0; i < n_oids; i++) {
if ((vp = calloc(1, sizeof (pdu_varlist_t))) == NULL) {
snmp_free_null_vars(varblock_p);
return (-1);
} else if (i == 0) {
varblock_p = vp;
} else {
prev->nextvar = vp;
}
vp->name = snmp_oidstr_to_oid(pdu->command,
p, row, &vp->name_len);
if (vp->name == NULL) {
snmp_free_null_vars(varblock_p);
return (-1);
}
vp->val.str = NULL;
vp->val_len = 0;
vp->type = ASN_NULL;
vp->nextvar = NULL;
prev = vp;
p += strlen(p) + 1;
}
if (pdu->vars == NULL)
pdu->vars = varblock_p;
else {
for (vp = pdu->vars; vp->nextvar; vp = vp->nextvar)
;
vp->nextvar = varblock_p;
}
return (0);
}
static oid *
snmp_oidstr_to_oid(int cmd, char *oidstr, int row, size_t *n_subids)
{
int i, count;
char *p, *q;
char *oidstr_dup;
oid *objid;
if ((oidstr == NULL) || (n_subids == NULL))
return (NULL);
for (count = 1, p = oidstr; p; count++, p++) {
if ((p = strchr(p, '.')) == NULL)
break;
}
if ((cmd == SNMP_MSG_GET) || (cmd == SNMP_MSG_GETBULK && row > 0) ||
(cmd == SNMP_MSG_GETNEXT && row >= 0)) {
count++;
}
if ((oidstr_dup = strdup(oidstr)) == NULL)
return (NULL);
objid = (oid *) calloc(count, sizeof (oid));
if (objid == NULL) {
free((void *) p);
return (NULL);
}
p = oidstr_dup;
for (i = 0; i < count - 1; i++) {
if (q = strchr(p, '.'))
*q = 0;
objid[i] = (oid) strtoul(p, NULL, 10);
p = q + 1;
}
switch (cmd) {
case SNMP_MSG_GET:
objid[i] = (oid) row;
break;
case SNMP_MSG_GETBULK:
if (row > 0)
objid[i] = (oid) (row - 1);
else
objid[i] = (oid) strtoul(p, NULL, 10);
break;
case SNMP_MSG_GETNEXT:
if (row < 0)
objid[i] = (oid) strtoul(p, NULL, 10);
else
objid[i] = (oid) row;
break;
}
*n_subids = count;
free((void *) oidstr_dup);
return (objid);
}
static uchar_t *
snmp_build_pdu(snmp_pdu_t *pdu, uchar_t *buf, size_t *bufsz_p)
{
uchar_t *p;
uchar_t *pdu_seq_begin, *pdu_seq_end;
uchar_t *varlist_seq_begin, *varlist_seq_end;
uchar_t id;
size_t seqlen;
pdu_varlist_t *vp;
pdu_seq_begin = buf;
p = asn_build_sequence(buf, bufsz_p, (uchar_t)pdu->command, 0);
if (p == NULL)
return (NULL);
pdu_seq_end = p;
id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
if ((p = asn_build_int(p, bufsz_p, id, pdu->reqid)) == NULL)
return (NULL);
id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
if ((p = asn_build_int(p, bufsz_p, id, pdu->non_repeaters)) == NULL)
return (NULL);
id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
if ((p = asn_build_int(p, bufsz_p, id, pdu->max_repetitions)) == NULL)
return (NULL);
varlist_seq_begin = p;
id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
if ((p = asn_build_sequence(p, bufsz_p, id, 0)) == NULL)
return (NULL);
varlist_seq_end = p;
for (vp = pdu->vars; vp; vp = vp->nextvar) {
p = snmp_build_variable(p, bufsz_p, vp->name, vp->name_len,
vp->type, vp->val.str, vp->val_len);
if (p == NULL)
return (NULL);
}
seqlen = p - varlist_seq_end;
id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
(void) asn_build_sequence(varlist_seq_begin, NULL, id, seqlen);
seqlen = p - pdu_seq_end;
(void) asn_build_sequence(pdu_seq_begin, NULL, (uchar_t)pdu->command,
seqlen);
return (p);
}
static uchar_t *
snmp_build_variable(uchar_t *buf, size_t *bufsz_p, oid *name, size_t name_len,
uchar_t val_type, void *val, size_t val_len)
{
uchar_t *p, *varseq_end;
size_t seqlen;
uchar_t id;
id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
if ((p = asn_build_sequence(buf, bufsz_p, id, 0)) == NULL)
return (NULL);
varseq_end = p;
id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID;
if ((p = asn_build_objid(p, bufsz_p, id, name, name_len)) == NULL)
return (NULL);
id = ASN_UNIVERSAL | ASN_PRIMITIVE | val_type;
switch (val_type) {
case ASN_INTEGER:
p = asn_build_int(p, bufsz_p, id, *((int *)val));
if (p == NULL)
return (NULL);
break;
case ASN_OBJECT_ID:
p = asn_build_objid(p, bufsz_p, id, val,
val_len / sizeof (oid));
if (p == NULL)
return (NULL);
break;
case ASN_OCTET_STR:
p = asn_build_string(p, bufsz_p, id, (uchar_t *)val, val_len);
if (p == NULL)
return (NULL);
break;
case ASN_NULL:
if ((p = asn_build_null(p, bufsz_p, id)) == NULL)
return (NULL);
break;
default:
return (NULL);
}
seqlen = p - varseq_end;
id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
(void) asn_build_sequence(buf, NULL, id, seqlen);
return (p);
}
static uchar_t *
snmp_parse_pdu(int reqid, uchar_t *msg, size_t *msgsz_p, snmp_pdu_t *reply_pdu)
{
uchar_t *p;
uchar_t id, exp_id;
pdu_varlist_t *newvp, *vp = NULL;
if ((p = asn_parse_header(msg, msgsz_p, &id)) == NULL)
return (NULL);
if (id != SNMP_MSG_RESPONSE && id != SNMP_MSG_REPORT)
return (NULL);
reply_pdu->command = (int)id;
if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->reqid)) == NULL)
return (NULL);
if (reply_pdu->reqid != reqid)
return (NULL);
if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->errstat)) == NULL)
return (NULL);
if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->errindex)) == NULL)
return (NULL);
exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
if ((p = asn_parse_sequence(p, msgsz_p, exp_id)) == NULL)
return (NULL);
while (((int)*msgsz_p) > 0) {
if ((newvp = calloc(1, sizeof (pdu_varlist_t))) == NULL)
return (NULL);
if (vp == NULL)
reply_pdu->vars = newvp;
else
vp->nextvar = newvp;
vp = newvp;
if ((p = snmp_parse_variable(p, msgsz_p, vp)) == NULL)
return (NULL);
}
return (p);
}
static uchar_t *
snmp_parse_variable(uchar_t *msg, size_t *msgsz_p, pdu_varlist_t *vp)
{
uchar_t *p;
uchar_t exp_id;
exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
if ((p = asn_parse_sequence(msg, msgsz_p, exp_id)) == NULL)
return (NULL);
p = asn_parse_objid(p, msgsz_p, &vp->name, &vp->name_len);
if (p == NULL)
return (NULL);
if ((p = asn_parse_objval(p, msgsz_p, vp)) == NULL)
return (NULL);
return (p);
}
void
snmp_free_pdu(snmp_pdu_t *pdu)
{
pdu_varlist_t *vp, *nxt;
if (pdu) {
if ((pdu->community) && (pdu->community != snmp_def_community))
free((void *) pdu->community);
for (vp = pdu->vars; vp; vp = nxt) {
nxt = vp->nextvar;
if (vp->name)
free((void *) vp->name);
if (vp->val.str)
free((void *) vp->val.str);
free((void *) vp);
}
if (pdu->req_pkt)
free((void *) pdu->req_pkt);
if (pdu->reply_pkt)
free((void *) pdu->reply_pkt);
free((void *) pdu);
}
}
static void
snmp_free_null_vars(pdu_varlist_t *varblock_p)
{
pdu_varlist_t *vp, *nxt;
for (vp = varblock_p; vp; vp = nxt) {
nxt = vp->nextvar;
if (vp->name)
free(vp->name);
free(vp);
}
}