#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <ber.h>
#include <err.h>
#include <errno.h>
#include <inttypes.h>
#include <netdb.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <vis.h>
#include "regress.h"
#include <stdio.h>
#define SNMP_HOSTNAME "127.0.0.1"
#define SNMP_PORT "161"
#define SNMP_R_COMMUNITY "public"
#define MIB_SNMP_V3 MIB_SNMP, 1
#define MIB_SNMP_USM MIB_SNMP_V3, 1
#define MIB_SUBAGENT_V3 MIB_SUBAGENT_SNMP, 1
#define MIB_SUBAGENT_USM MIB_SUBAGENT_V3, 1
#define SNMPMODULES 1, 3, 6, 1, 6, 3
#define SNMPUSMMIB SNMPMODULES, 15
#define USMMIBOBJECTS SNMPUSMMIB, 1
#define USMSTATS USMMIBOBJECTS, 1
#define USMSTATSUNKNOWNENGINEIDS USMSTATS, 4
#define BER_OID(...) (struct ber_oid){ {__VA_ARGS__}, \
(sizeof((uint32_t []){__VA_ARGS__}) / sizeof(uint32_t)) }
enum snmp_application {
APPLICATION_IPADDR = 0,
APPLICATION_COUNTER32 = 1,
APPLICATION_GAUGE32 = 2,
APPLICATION_UNSIGNED32 = 2,
APPLICATION_TIMETICKS = 3,
APPLICATION_OPAQUE = 4,
APPLICATION_NSAPADDR = 5,
APPLICATION_COUNTER64 = 6,
};
enum snmp_exception {
EXCEPTION_NOSUCHOBJECT = 0,
EXCEPTION_NOSUCHINSTANCE = 1,
EXCEPTION_ENDOFMIBVIEW = 2
};
enum security_model {
SM_USM = 3,
SM_TSM = 4
};
struct usm {
char engineid[32];
size_t engineidlen;
int engineboots;
int enginetime;
char username[33];
};
union securityparams {
struct usm usm;
};
#define MSGFLAG_AUTH (1 << 0)
#define MSGFLAG_PRIV (1 << 1)
#define MSG_NOAUTHNOPRIV 0
#define MSG_AUTHNOPRIV MSGFLAG_AUTH
#define MSG_AUTHPRIV (MSGFLAG_AUTH | MSGFLAG_PRIV)
#define MSGFLAG_REPORTABLE (1 << 2)
#define MSGFLAG_ALL (MSGFLAG_AUTH | MSGFLAG_PRIV | MSGFLAG_REPORTABLE)
struct headerdata {
int32_t msgid;
int32_t msgmaxsize;
int8_t msgflags;
enum security_model msgsm;
char engineid[32];
size_t engineidlen;
char contextname[256];
};
int32_t snmpv2_send(int, const char *, enum snmp_request, int32_t, int32_t,
int32_t, struct varbind *, size_t);
int32_t snmpv3_get(int, int, struct headerdata *, union securityparams *,
int32_t, struct varbind *, size_t);
int32_t snmpv3_send(int, int, struct headerdata *, union securityparams *,
enum snmp_request, int32_t, int32_t, int32_t, struct varbind *, size_t);
int32_t snmpv3_usm_send(int, int, struct headerdata *, struct usm *, int32_t,
struct varbind *, size_t);
void snmpv3_usm_discovery(int, int, struct headerdata *, struct usm *);
void snmpv3_encode(int, struct ber *, struct headerdata *,
union securityparams *, struct ber_element *);
void snmpv3_usm_encode(int, struct ber *, struct usm *);
struct ber_element *snmpv3_decode(int, void *, size_t, struct ber_element *,
struct headerdata *, union securityparams *);
void snmpv3_response_validate(int, int, struct headerdata *,
union securityparams *, int32_t, int32_t, int32_t, struct varbind *,
size_t);
void snmpv3_usm_decode(int, void *, size_t, void *, size_t, struct usm *);
struct ber_element *snmp_recv(int, int, void *buf, size_t *);
struct ber_element *snmp_pdu(enum snmp_request, int32_t, int32_t, int32_t,
struct varbind *, size_t);
struct ber_element *snmp_varbindlist(struct varbind *, size_t);
struct ber_oid *snmp_oid2ber_oid(struct oid *, struct ber_oid *);
struct ber_element *snmp_data2ber_element(enum type, union data *);
unsigned int smi_application(struct ber_element *);
char *smi_oid2string(struct ber_oid *, char *, size_t);
char *smi_print_element(struct ber_element *);
struct ber_element *v2cmps(struct ber_element *, const char *);
void snmp_pdu_validate(struct ber_element *, enum snmp_request, int32_t,
int32_t, int32_t, struct varbind *, size_t);
void
snmp_v3_usm_noauthpriv(void)
{
struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
socklen_t salen;
int snmp_s, ax_s;
uint32_t sessionid;
struct varbind varbind = {
.type = TYPE_NULL,
.name = OID_STRUCT(MIB_SNMP_USM, 1, 0),
.data.int32 = 1
};
struct headerdata hd = {
.msgid = 0,
.msgmaxsize = 0,
.msgflags = MSG_NOAUTHNOPRIV | MSGFLAG_REPORTABLE,
.msgsm = SM_USM
};
union securityparams params = {
.usm.engineidlen = 0,
.usm.engineboots = 0,
.usm.enginetime = 0,
.usm.username = "noauthpriv"
};
int32_t requestid;
char buf[1024];
size_t n;
ax_s = agentx_connect(axsocket);
sessionid = agentx_open(ax_s, 0, 0,
OID_ARG(MIB_SUBAGENT_USM, 1), __func__);
agentx_register(ax_s, sessionid, 0, 0, 127, 0,
OID_ARG(MIB_SNMP_USM, 1), 0);
salen = snmp_resolve(SOCK_DGRAM, hostname, servname, sa);
snmp_s = snmp_connect(SOCK_DGRAM, sa, salen);
requestid = snmpv3_get(snmp_s, 1000, &hd, ¶ms, 0, &varbind, 1);
n = agentx_read(ax_s, buf, sizeof(buf), 1000);
varbind.type = TYPE_INTEGER;
agentx_get_handle(__func__, buf, n, 0, sessionid, &varbind, 1);
agentx_response(ax_s, buf, 0, NOERROR, &varbind, 1);
snmpv3_response_validate(snmp_s, 1000, &hd, ¶ms, requestid, 0, 0,
&varbind, 1);
}
socklen_t
snmp_resolve(int type, const char *hostname, const char *servname, struct sockaddr *sa)
{
struct addrinfo hints, *res;
socklen_t salen;
int error;
if (hostname == NULL)
hostname = SNMP_HOSTNAME;
if (servname == NULL)
servname = SNMP_PORT;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = type;
if ((error = getaddrinfo(hostname, servname, &hints, &res)) != 0)
errx(1, "getaddrinfo(%s, %s): %s", hostname, servname,
gai_strerror(error));
memcpy(sa, res->ai_addr, res->ai_addrlen);
salen = res->ai_addrlen;
freeaddrinfo(res);
return salen;
}
int
snmp_connect(int type, struct sockaddr *sa, socklen_t salen)
{
int s;
if ((s = socket(sa->sa_family, type, 0)) == -1)
err(1, "socket");
if (connect(s, sa, salen) == -1)
err(1, "connect");
return s;
}
int32_t
snmpv2_get(int s, const char *community, int32_t requestid,
struct varbind *varbindlist, size_t nvarbind)
{
return snmpv2_send(s, community, REQUEST_GET, requestid, 0, 0,
varbindlist, nvarbind);
}
int32_t
snmpv2_getnext(int s, const char *community, int32_t requestid,
struct varbind *varbindlist, size_t nvarbind)
{
return snmpv2_send(s, community, REQUEST_GETNEXT, requestid, 0, 0,
varbindlist, nvarbind);
}
int32_t
snmpv2_getbulk(int s, const char *community, int32_t requestid, int32_t nonrep,
int32_t maxrep, struct varbind *varbindlist, size_t nvarbind)
{
return snmpv2_send(s, community, REQUEST_GETBULK, requestid, nonrep,
maxrep, varbindlist, nvarbind);
}
struct ber_element *
snmpv2_build(const char *community, enum snmp_request request,
int32_t requestid, int32_t error, int32_t index,
struct varbind *varbindlist, size_t nvarbind)
{
struct ber_element *message;
if (community == NULL)
community = SNMP_R_COMMUNITY;
message = ober_printf_elements(NULL, "{dse}", 1, community,
snmp_pdu(request, requestid, error, index, varbindlist, nvarbind));
if (message == NULL)
err(1, NULL);
return message;
}
int32_t
snmpv2_send(int s, const char *community, enum snmp_request request,
int32_t requestid, int32_t error, int32_t index,
struct varbind *varbindlist, size_t nvarbind)
{
struct ber_element *message;
struct ber ber = {};
void *buf;
ssize_t buflen, writelen;
while (requestid == 0)
requestid = arc4random();
message = snmpv2_build(community, request, requestid, error, index,
varbindlist,nvarbind);
if (ober_write_elements(&ber, message) == -1)
err(1, "ober_write_elements");
buflen = ober_get_writebuf(&ber, &buf);
if ((writelen = write(s, buf, buflen)) == -1)
err(1, "write");
if (writelen != buflen)
errx(1, "write: short write");
if (verbose) {
printf("SNMP send(%d):\n", s);
smi_debug_elements(message);
}
ober_free_elements(message);
ober_free(&ber);
return requestid;
}
void
snmpv2_response_validate(int s, int timeout, const char *community,
int32_t requestid, int32_t error, int32_t index,
struct varbind *varbindlist, size_t nvarbind)
{
struct ber_element *message, *pdu;
char buf[100000];
size_t buflen = sizeof(buf);
message = snmp_recv(s, timeout, buf, &buflen);
if (community == NULL)
community = SNMP_R_COMMUNITY;
snmp_pdu_validate(v2cmps(message, community), REQUEST_RESPONSE,
requestid, error, index, varbindlist, nvarbind);
ober_free_elements(message);
}
struct ber_element *
snmp_recv(int s, int timeout, void *buf, size_t *buflen)
{
struct pollfd pfd = {
.fd = s,
.events = POLLIN
};
struct ber ber = {};
struct ber_element *message;
ssize_t n;
size_t ntot = 0;
int ret;
again:
if ((ret = poll(&pfd, 1, timeout)) == -1)
err(1, "poll");
if (ret == 0) {
if (ntot == 0)
errx(1, "%s: timeout", __func__);
errc(1, ECANCELED, "%s: unable to decode message", __func__);
}
if ((n = read(s, buf + ntot, *buflen - ntot)) == -1)
err(1, "snmp read");
ntot += n;
ober_set_application(&ber, smi_application);
ober_set_readbuf(&ber, buf, ntot);
if ((message = ober_read_elements(&ber, NULL)) == NULL) {
if (errno == ECANCELED)
goto again;
errx(1, "%s: unable to decode message", __func__);
}
*buflen = n;
if (verbose) {
printf("SNMP received(%d):\n", s);
smi_debug_elements(message);
}
ober_free(&ber);
return message;
}
int32_t
snmpv3_get(int s, int timeout, struct headerdata *hd,
union securityparams *params, int32_t requestid,
struct varbind *varbindlist, size_t nvarbind)
{
return snmpv3_send(s, timeout, hd, params, REQUEST_GET, requestid,
0, 0, varbindlist, nvarbind);
}
int32_t
snmpv3_send(int s, int timeout, struct headerdata *hd,
union securityparams *params, enum snmp_request request, int32_t requestid,
int32_t error, int32_t index, struct varbind *varbindlist, size_t nvarbind)
{
struct ber ber;
void *buf;
ssize_t buflen, writelen;
if (hd->msgid == 0)
hd->msgid = arc4random();
if (hd->msgmaxsize == 0)
hd->msgmaxsize = 484;
if (requestid == 0)
requestid = arc4random();
if (hd->msgsm == SM_USM)
snmpv3_usm_discovery(s, timeout, hd, ¶ms->usm);
snmpv3_encode(s, &ber, hd, params, snmp_pdu(request, requestid, error,
index, varbindlist, nvarbind));
buflen = ober_get_writebuf(&ber, &buf);
if ((writelen = write(s, buf, buflen)) == -1)
err(1, "write");
if (writelen != buflen)
errx(1, "write: short write");
ober_free(&ber);
return requestid;
}
void
snmpv3_usm_discovery(int s, int timeout, struct headerdata *hd,
struct usm *params)
{
struct ber_element *message, *pdu;
struct ber_oid oid;
struct ber ber;
struct headerdata hdd;
union securityparams sp = {
.usm = *params
};
void *buf;
char rbuf[1024];
size_t rbuflen = sizeof(rbuf);
int8_t msgflags;
size_t buflen, writelen;
char oidbuf[1024];
struct varbind vb = {
.type = TYPE_COUNTER32,
.dataunknown = 1
};
hdd = *hd;
hdd.msgid = arc4random();
if (params->engineidlen == 0) {
hdd.msgflags = MSGFLAG_REPORTABLE;
sp.usm.username[0] = '\0';
} else if (hd->msgflags & MSGFLAG_AUTH && params->engineboots == 0 &&
params->enginetime == 0)
hdd.msgflags = MSGFLAG_AUTH | MSGFLAG_REPORTABLE;
else
return;
pdu = snmp_pdu(REQUEST_GET, 0, 0, 0, NULL, 0);
snmpv3_encode(s, &ber, &hdd, &sp, pdu);
buflen = ober_get_writebuf(&ber, &buf);
if ((writelen = write(s, buf, buflen)) == -1)
err(1, "write");
if (writelen != buflen)
errx(1, "write: short write");
retry:
message = snmp_recv(s, timeout, rbuf, &rbuflen);
hdd.msgflags &= ~MSGFLAG_REPORTABLE;
pdu = snmpv3_decode(s, rbuf, rbuflen, message, &hdd, &sp);
if (params->engineidlen == 0) {
vb.name = OID_STRUCT(USMSTATSUNKNOWNENGINEIDS, 0);
snmp_pdu_validate(pdu, REQUEST_REPORT, 0, 0, 0, &vb, 1);
memcpy(params->engineid, sp.usm.engineid, sp.usm.engineidlen);
params->engineidlen = sp.usm.engineidlen;
if (hd->msgflags & MSGFLAG_AUTH)
snmpv3_usm_discovery(s, timeout, hd, params);
}
ober_free(&ber);
ober_free_elements(message);
return;
}
void
snmpv3_encode(int s, struct ber *ber, struct headerdata *hd,
union securityparams *params, struct ber_element *pdu)
{
struct ber_element *message;
void *sp;
size_t splen;
switch (hd->msgsm) {
case SM_USM:
snmpv3_usm_encode(s, ber, ¶ms->usm);
break;
default:
errx(1, "%s: unsupported securityModel %d",
__func__, hd->msgsm);
}
splen = ober_get_writebuf(ber, &sp);
if ((message = ober_printf_elements(NULL, "{d{ddxd}x{xse}}", 3,
hd->msgid, hd->msgmaxsize, &hd->msgflags, sizeof(hd->msgflags),
hd->msgsm, sp, splen, hd->engineid, hd->engineidlen,
hd->contextname, pdu)) == NULL)
err(1, NULL);
ober_free(ber);
*ber = (struct ber){};
ober_set_application(ber, smi_application);
if (ober_write_elements(ber, message) == -1)
err(1, NULL);
if (verbose) {
printf("SNMP send(%d):\n", s);
smi_debug_elements(message);
}
ober_free_elements(message);
}
void
snmpv3_usm_encode(int s, struct ber *ber, struct usm *params)
{
struct ber_element *sp;
*ber = (struct ber){};
ober_set_application(ber, smi_application);
if ((sp = ober_printf_elements(NULL, "{xddxss}", params->engineid,
params->engineidlen, params->engineboots, params->enginetime,
params->username, strlen(params->username), "", "")) == NULL)
err(1, NULL);
if (ober_write_elements(ber, sp) == -1)
err(1, NULL);
if (verbose) {
printf("USM params send(%d):\n", s);
smi_debug_elements(sp);
}
ober_free_elements(sp);
}
struct ber_element *
snmpv3_decode(int s, void *buf, size_t buflen, struct ber_element *message,
struct headerdata *hd, union securityparams *sp)
{
struct ber_element *pdu;
int32_t version, msgid, msgmaxsize, sm;
char *msgflags, *spstr, *engineid, *name;
size_t msgflagslen, spstrlen, engineidlen, namelen;
int class;
unsigned int type;
if (ober_scanf_elements(message, "{d{ddxd$}x{xxe}",
&version, &msgid, &msgmaxsize, &msgflags, &msgflagslen,
&sm, &spstr, &spstrlen, &engineid, &engineidlen, &name, &namelen,
&pdu) == -1)
errx(1, "%s: ober_scanf_elements", __func__);
if (version != 3)
errx(1, "%s: invalid version", __func__);
if (msgid != hd->msgid)
errx(1, "%s: unexpected msgid", __func__);
if (msgmaxsize < 484 || msgmaxsize > 2147483647)
errx(1, "%s: invalid msgmaxsize", __func__);
if (msgflagslen != 1 || msgflags[0] != hd->msgflags)
errx(1, "%s: invalid msgflags", __func__);
if (sm != hd->msgsm)
errx(1, "%s: unexpected security model", __func__);
if (engineidlen < 5 || engineidlen > 32)
errx(1, "%s: invalid contextEngineID", __func__);
if (hd->engineidlen != 0) {
if (hd->engineidlen != engineidlen ||
memcmp(hd->engineid, engineid, engineidlen) != 0)
errx(1, "%s: unexpected engineid", __func__);
} else {
hd->engineidlen = engineidlen;
memcpy(hd->engineid, engineid, engineidlen);
}
if (namelen > 255)
errx(1, "%s: invalid ctxnamelen", __func__);
if (strcmp(hd->contextname, name) != 0)
errx(1, "%s: unexpected context", __func__);
switch (sm) {
case SM_USM:
snmpv3_usm_decode(s, buf, buflen, spstr, spstrlen, &sp->usm);
}
return pdu;
}
void
snmpv3_usm_decode(int s, void *buf, size_t buflen, void *spstr, size_t spstrlen,
struct usm *usm)
{
struct ber ber = {};
struct ber_element *sp;
char *engineid, *username, *authparams, *privparams;
size_t engineidlen, usernamelen, authparamslen, privparamslen;
int32_t engineboots, enginetime;
ober_set_application(&ber, smi_application);
ober_set_readbuf(&ber, spstr, spstrlen);
if ((sp = ober_read_elements(&ber, NULL)) == NULL)
errx(1, "%s: ober_read_elements", __func__);
if (verbose) {
printf("USM params received(%d):\n", s);
smi_debug_elements(sp);
}
if (ober_scanf_elements(sp, "{xddxxx}", &engineid, &engineidlen,
&engineboots, &enginetime, &username, &usernamelen, &authparams,
&authparamslen, &privparams, &privparamslen) == -1)
errx(1, "%s: ober_scanf_elements", __func__);
if (engineidlen < 5 || engineidlen > 32)
errx(1, "%s: invalid msgAuthoritativeEngineID", __func__);
if (engineboots < 0 || engineboots > 2147483647)
errx(1, "%s: invalid msgAuthoritativeEngineBoots", __func__);
if (enginetime < 0 || enginetime > 2147483647)
errx(1, "%s: invalid msgAuthoritativeEngineTime", __func__);
if (usernamelen < 0 || usernamelen > 32)
errx(1, "%s: invalid msgUserName", __func__);
if (usm->engineidlen == 0) {
memcpy(usm->engineid, engineid, engineidlen);
usm->engineidlen = engineidlen;
} else {
if (usm->engineidlen != engineidlen ||
memcmp(usm->engineid, engineid, engineidlen) != 0)
errx(1, "%s: unexpected engineid", __func__);
}
if (usm->engineboots == 0 && usm->enginetime == 0) {
usm->engineboots = engineboots;
usm->enginetime = enginetime;
} else {
if (usm->engineboots < engineboots)
errx(1, "%s: engineboots decremented", __func__);
else if (usm->engineboots == engineboots) {
if (enginetime < usm->enginetime - 150 ||
enginetime > usm->enginetime + 150)
errx(1, "%s: enginetime out of window",
__func__);
} else {
if (enginetime > 150)
errx(1, "%s: enginetime out of window",
__func__);
}
}
if (strcmp(username, usm->username) != 0)
errx(1, "unexpected username");
}
void
snmpv3_response_validate(int s, int timeout, struct headerdata *hd,
union securityparams *sp, int32_t requestid, int32_t error, int32_t index,
struct varbind *varbindlist, size_t nvarbind)
{
struct ber_element *message, *pdu;
char buf[1024];
size_t buflen = sizeof(buf);
hd->msgflags &= ~MSGFLAG_REPORTABLE;
message = snmp_recv(s, timeout, buf, &buflen);
pdu = snmpv3_decode(1, buf, sizeof(buf), message, hd, sp);
snmp_pdu_validate(pdu, REQUEST_RESPONSE, requestid, error, index,
varbindlist, nvarbind);
ober_free_elements(message);
}
void
snmp_timeout(int s, int timeout)
{
int ret;
struct pollfd pfd = {
.fd = s,
.events = POLLIN
};
if ((ret = poll(&pfd, 1, timeout)) == -1)
err(1, "poll");
if (ret != 0)
errx(1, "%s: unexpected snmp data", __func__);
}
struct ber_element *
snmp_pdu(enum snmp_request request, int32_t requestid, int32_t error,
int32_t index, struct varbind *varbindlist, size_t nvarbind)
{
struct ber_element *pdu;
if ((pdu = ober_printf_elements(NULL, "{tddde}", BER_CLASS_CONTEXT,
request, requestid, error, index,
snmp_varbindlist(varbindlist, nvarbind))) == NULL)
err(1, NULL);
return pdu;
}
struct ber_element *
snmp_varbindlist(struct varbind *varbindlist, size_t nvarbind)
{
struct ber_element *vblist, *prev = NULL;
struct ber_oid oid;
size_t i;
if ((vblist = prev = ober_add_sequence(NULL)) == NULL)
err(1, NULL);
for (i = 0; i < nvarbind; i++) {
if ((prev = ober_printf_elements(prev, "{Oe}",
snmp_oid2ber_oid(&varbindlist[i].name, &oid),
snmp_data2ber_element(varbindlist[i].type,
&varbindlist[i].data))) == NULL)
err(1, NULL);
}
return vblist;
}
struct ber_oid *
snmp_oid2ber_oid(struct oid *oid, struct ber_oid *boid)
{
size_t i;
for (i = 0; i < oid->n_subid; i++)
boid->bo_id[i] = oid->subid[i];
boid->bo_n = oid->n_subid;
return boid;
}
struct ber_element *
snmp_data2ber_element(enum type type, union data *data)
{
struct ber_element *elm;
struct ber_oid oid;
switch (type) {
case TYPE_INTEGER:
elm = ober_add_integer(NULL, data->int32);
break;
case TYPE_OCTETSTRING:
elm = ober_add_nstring(NULL, data->octetstring.string,
data->octetstring.len);
break;
case TYPE_NULL:
elm = ober_add_null(NULL);
break;
case TYPE_OBJECTIDENTIFIER:
elm = ober_add_oid(NULL, snmp_oid2ber_oid(&data->oid, &oid));
break;
case TYPE_IPADDRESS:
if ((elm = ober_add_nstring(NULL, data->octetstring.string,
data->octetstring.len)) == NULL)
err(1, NULL);
ober_set_header(elm, BER_CLASS_APPLICATION, APPLICATION_IPADDR);
break;
case TYPE_COUNTER32:
if ((elm = ober_add_integer(NULL, data->uint32)) == NULL)
err(1, NULL);
ober_set_header(elm, BER_CLASS_APPLICATION,
APPLICATION_COUNTER32);
break;
case TYPE_GAUGE32:
if ((elm = ober_add_integer(NULL, data->uint32)) == NULL)
err(1, NULL);
ober_set_header(elm, BER_CLASS_APPLICATION,
APPLICATION_GAUGE32);
break;
case TYPE_TIMETICKS:
if ((elm = ober_add_integer(NULL, data->uint32)) == NULL)
err(1, NULL);
ober_set_header(elm, BER_CLASS_APPLICATION,
APPLICATION_TIMETICKS);
break;
case TYPE_OPAQUE:
if ((elm = ober_add_nstring(NULL, data->octetstring.string,
data->octetstring.len)) == NULL)
err(1, NULL);
ober_set_header(elm, BER_CLASS_APPLICATION, APPLICATION_OPAQUE);
break;
case TYPE_COUNTER64:
if ((elm = ober_add_integer(NULL, data->uint64)) == NULL)
err(1, NULL);
ober_set_header(elm, BER_CLASS_APPLICATION,
APPLICATION_COUNTER64);
break;
case TYPE_NOSUCHOBJECT:
if ((elm = ober_add_null(NULL)) == NULL)
err(1, NULL);
ober_set_header(elm, BER_CLASS_CONTEXT, EXCEPTION_NOSUCHOBJECT);
break;
case TYPE_NOSUCHINSTANCE:
if ((elm = ober_add_null(NULL)) == NULL)
err(1, NULL);
ober_set_header(elm, BER_CLASS_CONTEXT,
EXCEPTION_NOSUCHINSTANCE);
break;
case TYPE_ENDOFMIBVIEW:
if ((elm = ober_add_null(NULL)) == NULL)
err(1, NULL);
ober_set_header(elm, BER_CLASS_CONTEXT, EXCEPTION_ENDOFMIBVIEW);
break;
default:
errx(1, "%s: unsupported type: %d", __func__, type);
}
if (elm == NULL)
err(1, NULL);
return elm;
}
unsigned int
smi_application(struct ber_element *elm)
{
if (elm->be_class != BER_CLASS_APPLICATION)
return -1;
switch (elm->be_type) {
case APPLICATION_IPADDR:
case APPLICATION_OPAQUE:
return BER_TYPE_OCTETSTRING;
case APPLICATION_COUNTER32:
case APPLICATION_GAUGE32:
case APPLICATION_TIMETICKS:
case APPLICATION_COUNTER64:
return BER_TYPE_INTEGER;
default:
return -1;
}
}
char *
smi_oid2string(struct ber_oid *oid, char *buf, size_t len)
{
char digit[11];
size_t i;
buf[0] = '\0';
for (i = 0; i < oid->bo_n; i++) {
snprintf(digit, sizeof(digit), "%"PRIu32, oid->bo_id[i]);
if (i > 0)
strlcat(buf, ".", len);
strlcat(buf, digit, len);
}
return buf;
}
char *
smi_print_element(struct ber_element *root)
{
char *str = NULL, *buf, *p;
long long v;
struct ber_oid o;
char strbuf[BUFSIZ];
switch (root->be_class) {
case BER_CLASS_UNIVERSAL:
switch (root->be_type) {
case BER_TYPE_INTEGER:
if (ober_get_integer(root, &v) == -1)
goto fail;
if (asprintf(&str, "%lld", v) == -1)
goto fail;
break;
case BER_TYPE_OBJECT:
if (ober_get_oid(root, &o) == -1)
goto fail;
if (asprintf(&str, "%s", smi_oid2string(&o, strbuf,
sizeof(strbuf))) == -1)
goto fail;
break;
case BER_TYPE_OCTETSTRING:
if (ober_get_string(root, &buf) == -1)
goto fail;
p = reallocarray(NULL, 4, root->be_len + 1);
if (p == NULL)
goto fail;
strvisx(p, buf, root->be_len, VIS_NL);
if (asprintf(&str, "\"%s\"", p) == -1) {
free(p);
goto fail;
}
free(p);
break;
case BER_TYPE_NULL:
if (asprintf(&str, "null") == -1)
goto fail;
break;
default:
if (asprintf(&str, "[U/%u]", root->be_type) == -1)
goto fail;
break;
}
break;
case BER_CLASS_APPLICATION:
switch (root->be_type) {
case APPLICATION_IPADDR:
if (ober_get_string(root, &buf) == -1)
goto fail;
if (asprintf(&str, "%s",
inet_ntoa(*(struct in_addr *)buf)) == -1)
goto fail;
break;
case APPLICATION_COUNTER32:
if (ober_get_integer(root, &v) == -1)
goto fail;
if (asprintf(&str, "%lld(c32)", v) == -1)
goto fail;
break;
case APPLICATION_GAUGE32:
if (ober_get_integer(root, &v) == -1)
goto fail;
if (asprintf(&str, "%lld(g32)", v) == -1)
goto fail;
break;
case APPLICATION_TIMETICKS:
if (ober_get_integer(root, &v) == -1)
goto fail;
if (asprintf(&str, "%lld.%llds", v/100, v%100) == -1)
goto fail;
break;
case APPLICATION_OPAQUE:
if (ober_get_string(root, &buf) == -1)
goto fail;
p = reallocarray(NULL, 4, root->be_len + 1);
if (p == NULL)
goto fail;
strvisx(p, buf, root->be_len, VIS_NL);
if (asprintf(&str, "\"%s\"(opaque)", p) == -1) {
free(p);
goto fail;
}
free(p);
break;
case APPLICATION_COUNTER64:
if (ober_get_integer(root, &v) == -1)
goto fail;
if (asprintf(&str, "%lld(c64)", v) == -1)
goto fail;
break;
default:
if (asprintf(&str, "[A/%u]", root->be_type) == -1)
goto fail;
break;
}
break;
case BER_CLASS_CONTEXT:
switch (root->be_type) {
case EXCEPTION_NOSUCHOBJECT:
str = strdup("noSuchObject");
break;
case EXCEPTION_NOSUCHINSTANCE:
str = strdup("noSuchInstance");
break;
case EXCEPTION_ENDOFMIBVIEW:
str = strdup("endOfMibView");
break;
default:
if (asprintf(&str, "[C/%u]", root->be_type) == -1)
goto fail;
break;
}
break;
default:
if (asprintf(&str, "[%hhu/%u]", root->be_class,
root->be_type) == -1)
goto fail;
break;
}
return (str);
fail:
free(str);
return (NULL);
}
void
smi_debug_elements(struct ber_element *root)
{
static int indent = 0;
char *value;
int constructed;
ober_calc_len(root);
switch (root->be_encoding) {
case BER_TYPE_SEQUENCE:
case BER_TYPE_SET:
constructed = root->be_encoding;
break;
default:
constructed = 0;
break;
}
fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
switch (root->be_class) {
case BER_CLASS_UNIVERSAL:
fprintf(stderr, "class: universal(%u) type: ", root->be_class);
switch (root->be_type) {
case BER_TYPE_EOC:
fprintf(stderr, "end-of-content");
break;
case BER_TYPE_INTEGER:
fprintf(stderr, "integer");
break;
case BER_TYPE_BITSTRING:
fprintf(stderr, "bit-string");
break;
case BER_TYPE_OCTETSTRING:
fprintf(stderr, "octet-string");
break;
case BER_TYPE_NULL:
fprintf(stderr, "null");
break;
case BER_TYPE_OBJECT:
fprintf(stderr, "object");
break;
case BER_TYPE_ENUMERATED:
fprintf(stderr, "enumerated");
break;
case BER_TYPE_SEQUENCE:
fprintf(stderr, "sequence");
break;
case BER_TYPE_SET:
fprintf(stderr, "set");
break;
}
break;
case BER_CLASS_APPLICATION:
fprintf(stderr, "class: application(%u) type: ",
root->be_class);
switch (root->be_type) {
case APPLICATION_IPADDR:
fprintf(stderr, "ipaddr");
break;
case APPLICATION_COUNTER32:
fprintf(stderr, "counter32");
break;
case APPLICATION_GAUGE32:
fprintf(stderr, "gauge32");
break;
case APPLICATION_TIMETICKS:
fprintf(stderr, "timeticks");
break;
case APPLICATION_OPAQUE:
fprintf(stderr, "opaque");
break;
case APPLICATION_COUNTER64:
fprintf(stderr, "counter64");
break;
}
break;
case BER_CLASS_CONTEXT:
fprintf(stderr, "class: context(%u) type: ",
root->be_class);
switch (root->be_type) {
case REQUEST_GET:
fprintf(stderr, "getreq");
break;
case REQUEST_GETNEXT:
fprintf(stderr, "getnextreq");
break;
case REQUEST_RESPONSE:
fprintf(stderr, "response");
break;
case REQUEST_SET:
fprintf(stderr, "setreq");
break;
case REQUEST_TRAP:
fprintf(stderr, "trap");
break;
case REQUEST_GETBULK:
fprintf(stderr, "getbulkreq");
break;
case REQUEST_INFORM:
fprintf(stderr, "informreq");
break;
case REQUEST_TRAPV2:
fprintf(stderr, "trapv2");
break;
case REQUEST_REPORT:
fprintf(stderr, "report");
break;
}
break;
case BER_CLASS_PRIVATE:
fprintf(stderr, "class: private(%u) type: ", root->be_class);
break;
default:
fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
break;
}
fprintf(stderr, "(%u) encoding %u ",
root->be_type, root->be_encoding);
if ((value = smi_print_element(root)) == NULL)
goto invalid;
switch (root->be_encoding) {
case BER_TYPE_INTEGER:
case BER_TYPE_ENUMERATED:
fprintf(stderr, "value %s", value);
break;
case BER_TYPE_BITSTRING:
fprintf(stderr, "hexdump %s", value);
break;
case BER_TYPE_OBJECT:
fprintf(stderr, "oid %s", value);
break;
case BER_TYPE_OCTETSTRING:
if (root->be_class == BER_CLASS_APPLICATION &&
root->be_type == APPLICATION_IPADDR) {
fprintf(stderr, "addr %s", value);
} else {
fprintf(stderr, "string %s", value);
}
break;
case BER_TYPE_NULL:
case BER_TYPE_EOC:
case BER_TYPE_SEQUENCE:
case BER_TYPE_SET:
default:
fprintf(stderr, "%s", value);
break;
}
invalid:
if (value == NULL)
fprintf(stderr, "<INVALID>");
else
free(value);
fprintf(stderr, "\n");
if (constructed)
root->be_encoding = constructed;
if (constructed && root->be_sub) {
indent += 2;
smi_debug_elements(root->be_sub);
indent -= 2;
}
if (root->be_next)
smi_debug_elements(root->be_next);
}
struct ber_element *
v2cmps(struct ber_element *message, const char *community)
{
int version;
const char *mcommunity;
struct ber_element *pdu;
if (ober_scanf_elements(message, "{dse}$", &version, &mcommunity, &pdu) == -1)
err(1, "%s: ober_scanf_elements", __func__);
if (strcmp(mcommunity, community) != 0)
errx(1, "%s: invalid community (%s/%s)", __func__, mcommunity,
community);
return pdu;
}
void
snmp_pdu_validate(struct ber_element *pdu, enum snmp_request request,
int32_t requestid, int32_t error, int32_t index, struct varbind *varbindlist,
size_t nvarbind)
{
int32_t mrequestid, merror, mindex;
int class;
unsigned int type;
struct ber_element *mvarbindlist, *varbind;
struct ber_oid moid, oid;
struct ber_element *value;
char oidbuf1[512], oidbuf2[512];
size_t i;
if (ober_scanf_elements(pdu, "t{ddd{e}$}$", &class, &type, &mrequestid,
&merror, &mindex, &varbind) == -1)
err(1, "%s: ober_scanf_elementsi", __func__);
if (class != BER_CLASS_CONTEXT || type != request)
errx(1, "%s: unexpected pdu type", __func__);
if (mrequestid != requestid)
errx(1, "%s: unexpected pdu requestid (%d/%d)",
__func__, mrequestid, requestid);
if (error != merror)
errx(1, "%s: unexpected pdu error (%d/%d)",
__func__, merror, error);
if (index != mindex)
errx(1, "%s: unepxected pdu index (%d/%d)",
__func__, mindex, index);
for (i = 0; varbind != NULL; varbind = varbind->be_next, i++) {
if (ober_scanf_elements(varbind, "{oeS$}", &moid, &value) == -1)
err(1, "%s: ober_scanf_elements", __func__);
if (i >= nvarbind)
continue;
snmp_oid2ber_oid(&varbindlist[i].name, &oid);
if (!varbindlist[i].nameunknown && ober_oid_cmp(&moid, &oid) != 0)
errx(1, "%s: unexpected oid (%s/%s)", __func__,
smi_oid2string(&moid, oidbuf1, sizeof(oidbuf1)),
smi_oid2string(&oid, oidbuf2, sizeof(oidbuf2)));
if (value->be_class == BER_CLASS_UNIVERSAL &&
value->be_type == BER_TYPE_INTEGER) {
if (!varbindlist[i].typeunknown &&
varbindlist[i].type != TYPE_INTEGER)
errx(1, "%s: unexpected value", __func__);
if (!varbindlist[i].dataunknown &&
varbindlist[i].data.int32 != value->be_numeric)
errx(1, "%s: unexpected value", __func__);
} else if (value->be_class == BER_CLASS_UNIVERSAL &&
value->be_type == BER_TYPE_OCTETSTRING) {
if (!varbindlist[i].typeunknown &&
varbindlist[i].type != TYPE_OCTETSTRING)
errx(1, "%s: unexpected value", __func__);
if (!varbindlist[i].dataunknown && (
varbindlist[i].data.octetstring.len !=
value->be_len ||
memcmp(varbindlist[i].data.octetstring.string,
value->be_val, value->be_len) != 0))
errx(1, "%s: unexpected value", __func__);
} else if (value->be_class == BER_CLASS_UNIVERSAL &&
value->be_type == BER_TYPE_NULL) {
if (!varbindlist[i].typeunknown &&
varbindlist[i].type != TYPE_NULL)
errx(1, "%s: unexpected value", __func__);
} else if (value->be_class == BER_CLASS_UNIVERSAL &&
value->be_type == BER_TYPE_OBJECT) {
if (!varbindlist[i].typeunknown &&
varbindlist[i].type != TYPE_OBJECTIDENTIFIER)
errx(1, "%s: unexpected value", __func__);
if (!varbindlist[i].dataunknown) {
if (ober_get_oid(value, &moid) == -1)
errx(1, "%s: unexpected value",
__func__);
snmp_oid2ber_oid(&varbindlist[i].data.oid,
&oid);
if (ober_oid_cmp(&oid, &moid) != 0)
errx(1, "%s: unexpected value",
__func__);
}
} else if (value->be_class == BER_CLASS_APPLICATION &&
value->be_type == APPLICATION_IPADDR) {
if (!varbindlist[i].typeunknown &&
varbindlist[i].type != TYPE_IPADDRESS)
errx(1, "%s: unexpected value", __func__);
if (value->be_len != 4)
errx(1, "%s: unexpected value", __func__);
if (!varbindlist[i].dataunknown) {
if (memcmp(value->be_val,
varbindlist[i].data.octetstring.string,
value->be_len) != 0)
errx(1, "%s: unexpected value",
__func__);
}
} else if (value->be_class == BER_CLASS_APPLICATION &&
value->be_type == APPLICATION_COUNTER32) {
if (!varbindlist[i].typeunknown &&
varbindlist[i].type != TYPE_COUNTER32)
errx(1, "%s: unexpected value", __func__);
if (!varbindlist[i].dataunknown &&
varbindlist[i].data.uint32 != value->be_numeric)
errx(1, "%s: unexpected value", __func__);
} else if (value->be_class == BER_CLASS_APPLICATION &&
value->be_type == APPLICATION_GAUGE32) {
if (!varbindlist[i].typeunknown &&
varbindlist[i].type != TYPE_GAUGE32)
errx(1, "%s: unexpected value", __func__);
if (!varbindlist[i].dataunknown &&
varbindlist[i].data.uint32 != value->be_numeric)
errx(1, "%s: unexpected value", __func__);
} else if (value->be_class == BER_CLASS_APPLICATION &&
value->be_type == APPLICATION_TIMETICKS) {
if (!varbindlist[i].typeunknown &&
varbindlist[i].type != TYPE_TIMETICKS)
errx(1, "%s: unexpected value", __func__);
if (!varbindlist[i].dataunknown &&
varbindlist[i].data.uint32 != value->be_numeric)
errx(1, "%s: unexpected value", __func__);
} else if (value->be_class == BER_CLASS_APPLICATION &&
value->be_type == APPLICATION_OPAQUE) {
if (!varbindlist[i].typeunknown &&
varbindlist[i].type != TYPE_OPAQUE)
errx(1, "%s: unexpected value", __func__);
if (!varbindlist[i].dataunknown && (
varbindlist[i].data.octetstring.len !=
value->be_len ||
memcmp(varbindlist[i].data.octetstring.string,
value->be_val, value->be_len) != 0))
errx(1, "%s: unexpected value", __func__);
} else if (value->be_class == BER_CLASS_APPLICATION &&
value->be_type == APPLICATION_COUNTER64) {
if (!varbindlist[i].typeunknown &&
varbindlist[i].type != TYPE_COUNTER64)
errx(1, "%s: unexpected value", __func__);
if (!varbindlist[i].dataunknown &&
varbindlist[i].data.uint64 != value->be_numeric)
errx(1, "%s: unexpected value", __func__);
} else if (value->be_class == BER_CLASS_CONTEXT &&
value->be_type == EXCEPTION_NOSUCHOBJECT) {
if (!varbindlist[i].typeunknown &&
varbindlist[i].type != TYPE_NOSUCHOBJECT)
errx(1, "%s: unexpected value", __func__);
} else if (value->be_class == BER_CLASS_CONTEXT &&
value->be_type == EXCEPTION_NOSUCHINSTANCE) {
if (!varbindlist[i].typeunknown &&
varbindlist[i].type != TYPE_NOSUCHINSTANCE)
errx(1, "%s: unexpected value", __func__);
} else if (value->be_class == BER_CLASS_CONTEXT &&
value->be_type == EXCEPTION_ENDOFMIBVIEW) {
if (!varbindlist[i].typeunknown &&
varbindlist[i].type != TYPE_ENDOFMIBVIEW)
errx(1, "%s: unexpected value", __func__);
} else
errx(1, "%s: unexpected value", __func__);
}
if (i != nvarbind)
errx(1, "%s: unexpected amount of varbind (%zu/%zu)", __func__,
i, nvarbind);
}