xref: /openbsd/regress/usr.sbin/snmpd/snmp.c (revision 1434a006)
1 #include <sys/socket.h>
2 #include <sys/types.h>
3 
4 #include <arpa/inet.h>
5 
6 #include <ber.h>
7 #include <err.h>
8 #include <errno.h>
9 #include <inttypes.h>
10 #include <netdb.h>
11 #include <poll.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <vis.h>
16 
17 #include "regress.h"
18 
19 #include <stdio.h>
20 
21 #define SNMP_HOSTNAME "127.0.0.1"
22 #define SNMP_PORT "161"
23 
24 #define SNMP_R_COMMUNITY "public"
25 
26 #define MIB_SNMP_V3 MIB_SNMP, 1
27 #define MIB_SNMP_USM MIB_SNMP_V3, 1
28 
29 #define MIB_SUBAGENT_V3 MIB_SUBAGENT_SNMP, 1
30 #define MIB_SUBAGENT_USM MIB_SUBAGENT_V3, 1
31 
32 #define SNMPMODULES 1, 3, 6, 1, 6, 3
33 #define SNMPUSMMIB SNMPMODULES, 15
34 #define USMMIBOBJECTS SNMPUSMMIB, 1
35 #define USMSTATS USMMIBOBJECTS, 1
36 #define USMSTATSUNKNOWNENGINEIDS USMSTATS, 4
37 
38 #define BER_OID(...) (struct ber_oid){ {__VA_ARGS__},	\
39     (sizeof((uint32_t []){__VA_ARGS__}) / sizeof(uint32_t)) }
40 
41 enum snmp_application {
42         APPLICATION_IPADDR = 0,
43         APPLICATION_COUNTER32 = 1,
44         APPLICATION_GAUGE32 = 2,
45         APPLICATION_UNSIGNED32 = 2,
46         APPLICATION_TIMETICKS = 3,
47         APPLICATION_OPAQUE = 4,
48         APPLICATION_NSAPADDR = 5,
49         APPLICATION_COUNTER64 = 6,
50 };
51 
52 enum snmp_exception {
53 	EXCEPTION_NOSUCHOBJECT = 0,
54 	EXCEPTION_NOSUCHINSTANCE = 1,
55 	EXCEPTION_ENDOFMIBVIEW = 2
56 };
57 
58 enum security_model {
59 	SM_USM = 3,
60 	SM_TSM = 4
61 };
62 
63 struct usm {
64 	char engineid[32];
65 	size_t engineidlen;
66 	int engineboots;
67 	int enginetime;
68 	char username[33];
69 };
70 
71 union securityparams {
72 	struct usm usm;
73 };
74 
75 #define MSGFLAG_AUTH (1 << 0)
76 #define MSGFLAG_PRIV (1 << 1)
77 #define MSG_NOAUTHNOPRIV 0
78 #define MSG_AUTHNOPRIV MSGFLAG_AUTH
79 #define MSG_AUTHPRIV (MSGFLAG_AUTH | MSGFLAG_PRIV)
80 #define MSGFLAG_REPORTABLE (1 << 2)
81 #define MSGFLAG_ALL (MSGFLAG_AUTH | MSGFLAG_PRIV | MSGFLAG_REPORTABLE)
82 struct headerdata {
83 	int32_t msgid;
84 	int32_t msgmaxsize;
85 	int8_t msgflags;
86 	enum security_model msgsm;
87 	char engineid[32];
88 	size_t engineidlen;
89 	char contextname[256];
90 };
91 
92 int32_t snmpv2_send(int, const char *, enum snmp_request, int32_t, int32_t,
93     int32_t, struct varbind *, size_t);
94 int32_t snmpv3_get(int, int, struct headerdata *, union securityparams *,
95     int32_t, struct varbind *, size_t);
96 int32_t snmpv3_send(int, int, struct headerdata *, union securityparams *,
97     enum snmp_request, int32_t, int32_t, int32_t, struct varbind *, size_t);
98 int32_t snmpv3_usm_send(int, int, struct headerdata *, struct usm *, int32_t,
99     struct varbind *, size_t);
100 void snmpv3_usm_discovery(int, int, struct headerdata *, struct usm *);
101 void snmpv3_encode(int, struct ber *, struct headerdata *,
102     union securityparams *, struct ber_element *);
103 void snmpv3_usm_encode(int, struct ber *, struct usm *);
104 struct ber_element *snmpv3_decode(int, void *, size_t, struct ber_element *,
105     struct headerdata *, union securityparams *);
106 void snmpv3_response_validate(int, int, struct headerdata *,
107     union securityparams *, int32_t, int32_t, int32_t, struct varbind *,
108     size_t);
109 void snmpv3_usm_decode(int, void *, size_t, void *, size_t, struct usm *);
110 struct ber_element *snmp_recv(int, int, void *buf, size_t *);
111 struct ber_element *snmp_pdu(enum snmp_request, int32_t, int32_t, int32_t,
112     struct varbind *, size_t);
113 struct ber_element *snmp_varbindlist(struct varbind *, size_t);
114 struct ber_oid *snmp_oid2ber_oid(struct oid *, struct ber_oid *);
115 struct ber_element *snmp_data2ber_element(enum type, union data *);
116 unsigned int smi_application(struct ber_element *);
117 char *smi_oid2string(struct ber_oid *, char *, size_t);
118 char *smi_print_element(struct ber_element *);
119 struct ber_element *v2cmps(struct ber_element *, const char *);
120 void snmp_pdu_validate(struct ber_element *, enum snmp_request, int32_t,
121     int32_t, int32_t, struct varbind *, size_t);
122 
123 void
snmp_v3_usm_noauthpriv(void)124 snmp_v3_usm_noauthpriv(void)
125 {
126 	struct sockaddr_storage ss;
127 	struct sockaddr *sa = (struct sockaddr *)&ss;
128 	socklen_t salen;
129 	int snmp_s, ax_s;
130 	uint32_t sessionid;
131 	struct varbind varbind = {
132 		.type = TYPE_NULL,
133 		.name = OID_STRUCT(MIB_SNMP_USM, 1, 0),
134 		.data.int32 = 1
135 	};
136 	struct headerdata hd = {
137 		.msgid = 0,
138 		.msgmaxsize = 0,
139 		.msgflags = MSG_NOAUTHNOPRIV | MSGFLAG_REPORTABLE,
140 		.msgsm = SM_USM
141 	};
142 	union securityparams params = {
143 		.usm.engineidlen = 0,
144 		.usm.engineboots = 0,
145 		.usm.enginetime = 0,
146 		.usm.username = "noauthpriv"
147 	};
148 	int32_t requestid;
149 	char buf[1024];
150 	size_t n;
151 
152 	ax_s = agentx_connect(axsocket);
153 	sessionid = agentx_open(ax_s, 0, 0,
154 	    OID_ARG(MIB_SUBAGENT_USM, 1), __func__);
155 	agentx_register(ax_s, sessionid, 0, 0, 127, 0,
156 	    OID_ARG(MIB_SNMP_USM, 1), 0);
157 
158 	salen = snmp_resolve(SOCK_DGRAM, hostname, servname, sa);
159 	snmp_s = snmp_connect(SOCK_DGRAM, sa, salen);
160 	requestid = snmpv3_get(snmp_s, 1000, &hd, &params, 0, &varbind, 1);
161 
162 	n = agentx_read(ax_s, buf, sizeof(buf), 1000);
163 	varbind.type = TYPE_INTEGER;
164 	agentx_get_handle(__func__, buf, n, 0, sessionid, &varbind, 1);
165 	agentx_response(ax_s, buf, 0, NOERROR, &varbind, 1);
166 
167 	snmpv3_response_validate(snmp_s, 1000, &hd, &params, requestid, 0, 0,
168 	    &varbind, 1);
169 }
170 
171 socklen_t
snmp_resolve(int type,const char * hostname,const char * servname,struct sockaddr * sa)172 snmp_resolve(int type, const char *hostname, const char *servname, struct sockaddr *sa)
173 {
174 	struct addrinfo hints, *res;
175 	socklen_t salen;
176 	int error;
177 
178 	if (hostname == NULL)
179 		hostname = SNMP_HOSTNAME;
180 	if (servname == NULL)
181 		servname = SNMP_PORT;
182 
183 	memset(&hints, 0, sizeof(hints));
184 	hints.ai_family = AF_UNSPEC;
185 	hints.ai_socktype = type;
186 
187 	if ((error = getaddrinfo(hostname, servname, &hints, &res)) != 0)
188 		errx(1, "getaddrinfo(%s, %s): %s", hostname, servname,
189 		    gai_strerror(error));
190 
191 	memcpy(sa, res->ai_addr, res->ai_addrlen);
192 	salen = res->ai_addrlen;
193 
194 	freeaddrinfo(res);
195 	return salen;
196 }
197 
198 int
snmp_connect(int type,struct sockaddr * sa,socklen_t salen)199 snmp_connect(int type, struct sockaddr *sa, socklen_t salen)
200 {
201 	int s;
202 
203 	if ((s = socket(sa->sa_family, type, 0)) == -1)
204 		err(1, "socket");
205 	if (connect(s, sa, salen) == -1)
206 		err(1, "connect");
207 	return s;
208 }
209 
210 int32_t
snmpv2_get(int s,const char * community,int32_t requestid,struct varbind * varbindlist,size_t nvarbind)211 snmpv2_get(int s, const char *community, int32_t requestid,
212     struct varbind *varbindlist, size_t nvarbind)
213 {
214 	return snmpv2_send(s, community, REQUEST_GET, requestid, 0, 0,
215 	    varbindlist, nvarbind);
216 }
217 
218 int32_t
snmpv2_getnext(int s,const char * community,int32_t requestid,struct varbind * varbindlist,size_t nvarbind)219 snmpv2_getnext(int s, const char *community, int32_t requestid,
220     struct varbind *varbindlist, size_t nvarbind)
221 {
222 	return snmpv2_send(s, community, REQUEST_GETNEXT, requestid, 0, 0,
223 	    varbindlist, nvarbind);
224 }
225 
226 int32_t
snmpv2_getbulk(int s,const char * community,int32_t requestid,int32_t nonrep,int32_t maxrep,struct varbind * varbindlist,size_t nvarbind)227 snmpv2_getbulk(int s, const char *community, int32_t requestid, int32_t nonrep,
228     int32_t maxrep, struct varbind *varbindlist, size_t nvarbind)
229 {
230 	return snmpv2_send(s, community, REQUEST_GETBULK, requestid, nonrep,
231 	    maxrep, varbindlist, nvarbind);
232 }
233 
234 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)235 snmpv2_build(const char *community, enum snmp_request request,
236     int32_t requestid, int32_t error, int32_t index,
237     struct varbind *varbindlist, size_t nvarbind)
238 {
239 	struct ber_element *message;
240 
241 	if (community == NULL)
242 		community = SNMP_R_COMMUNITY;
243 	message = ober_printf_elements(NULL, "{dse}", 1, community,
244 	    snmp_pdu(request, requestid, error, index, varbindlist, nvarbind));
245 	if (message == NULL)
246 		err(1, NULL);
247 
248 	return message;
249 }
250 
251 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)252 snmpv2_send(int s, const char *community, enum snmp_request request,
253     int32_t requestid, int32_t error, int32_t index,
254     struct varbind *varbindlist, size_t nvarbind)
255 {
256 	struct ber_element *message;
257 	struct ber ber = {};
258 	void *buf;
259 	ssize_t buflen, writelen;
260 
261 	while (requestid == 0)
262 		requestid = arc4random();
263 
264 	message = snmpv2_build(community, request, requestid, error, index,
265 	    varbindlist,nvarbind);
266 
267 	if (ober_write_elements(&ber, message) == -1)
268 		err(1, "ober_write_elements");
269 
270 	buflen = ober_get_writebuf(&ber, &buf);
271 	if ((writelen = write(s, buf, buflen)) == -1)
272 		err(1, "write");
273 	if (writelen != buflen)
274 		errx(1, "write: short write");
275 
276 	if (verbose) {
277 		printf("SNMP send(%d):\n", s);
278 		smi_debug_elements(message);
279 	}
280 	ober_free_elements(message);
281 	ober_free(&ber);
282 
283 	return requestid;
284 }
285 
286 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)287 snmpv2_response_validate(int s, int timeout, const char *community,
288     int32_t requestid, int32_t error, int32_t index,
289     struct varbind *varbindlist, size_t nvarbind)
290 {
291 	struct ber_element *message, *pdu;
292 	char buf[100000];
293 	size_t buflen = sizeof(buf);
294 
295 	message = snmp_recv(s, timeout, buf, &buflen);
296 
297 	if (community == NULL)
298 		community = SNMP_R_COMMUNITY;
299 	snmp_pdu_validate(v2cmps(message, community), REQUEST_RESPONSE,
300 	    requestid, error, index, varbindlist, nvarbind);
301 
302 	ober_free_elements(message);
303 }
304 
305 struct ber_element *
snmp_recv(int s,int timeout,void * buf,size_t * buflen)306 snmp_recv(int s, int timeout, void *buf, size_t *buflen)
307 {
308 	struct pollfd pfd = {
309 		.fd = s,
310 		.events = POLLIN
311 	};
312 	struct ber ber = {};
313 	struct ber_element *message;
314 	ssize_t n;
315 	size_t ntot = 0;
316 	int ret;
317 
318  again:
319 	if ((ret = poll(&pfd, 1, timeout)) == -1)
320 		err(1, "poll");
321 	if (ret == 0) {
322 		if (ntot == 0)
323 			errx(1, "%s: timeout", __func__);
324 		errc(1, ECANCELED, "%s: unable to decode message", __func__);
325 	}
326 	if ((n = read(s, buf + ntot, *buflen - ntot)) == -1)
327 		err(1, "snmp read");
328 	ntot += n;
329 
330 	ober_set_application(&ber, smi_application);
331 	ober_set_readbuf(&ber, buf, ntot);
332 
333 	if ((message = ober_read_elements(&ber, NULL)) == NULL) {
334 		if (errno == ECANCELED)
335 			goto again;
336 		errx(1, "%s: unable to decode message", __func__);
337 	}
338 	*buflen = n;
339 	if (verbose) {
340 		printf("SNMP received(%d):\n", s);
341 		smi_debug_elements(message);
342 	}
343 
344 	ober_free(&ber);
345 
346 	return message;
347 }
348 
349 int32_t
snmpv3_get(int s,int timeout,struct headerdata * hd,union securityparams * params,int32_t requestid,struct varbind * varbindlist,size_t nvarbind)350 snmpv3_get(int s, int timeout, struct headerdata *hd,
351     union securityparams *params, int32_t requestid,
352     struct varbind *varbindlist, size_t nvarbind)
353 {
354 	return snmpv3_send(s, timeout, hd, params, REQUEST_GET, requestid,
355 	    0, 0, varbindlist, nvarbind);
356 }
357 
358 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)359 snmpv3_send(int s, int timeout, struct headerdata *hd,
360     union securityparams *params, enum snmp_request request, int32_t requestid,
361     int32_t error, int32_t index, struct varbind *varbindlist, size_t nvarbind)
362 {
363 	struct ber ber;
364 	void *buf;
365 	ssize_t buflen, writelen;
366 
367 	if (hd->msgid == 0)
368 		hd->msgid = arc4random();
369 	if (hd->msgmaxsize == 0)
370 		hd->msgmaxsize = 484;
371 	if (requestid == 0)
372 		requestid = arc4random();
373 
374 	if (hd->msgsm == SM_USM)
375 		snmpv3_usm_discovery(s, timeout, hd, &params->usm);
376 
377 	snmpv3_encode(s, &ber, hd, params, snmp_pdu(request, requestid, error,
378 	    index, varbindlist, nvarbind));
379 
380 	buflen = ober_get_writebuf(&ber, &buf);
381 	if ((writelen = write(s, buf, buflen)) == -1)
382 		err(1, "write");
383 	if (writelen != buflen)
384 		errx(1, "write: short write");
385 
386 	ober_free(&ber);
387 
388 	return requestid;
389 }
390 
391 void
snmpv3_usm_discovery(int s,int timeout,struct headerdata * hd,struct usm * params)392 snmpv3_usm_discovery(int s, int timeout, struct headerdata *hd,
393     struct usm *params)
394 {
395 	struct ber_element *message, *pdu;
396 	struct ber_oid oid;
397 	struct ber ber;
398 	struct headerdata hdd;
399 	union securityparams sp = {
400 		.usm = *params
401 	};
402 	void *buf;
403 	char rbuf[1024];
404 	size_t rbuflen = sizeof(rbuf);
405 	int8_t msgflags;
406 	size_t buflen, writelen;
407 	char oidbuf[1024];
408 	struct varbind vb = {
409 		.type = TYPE_COUNTER32,
410 		.dataunknown = 1
411 	};
412 
413 	hdd = *hd;
414 	hdd.msgid = arc4random();
415 	if (params->engineidlen == 0) {
416 		hdd.msgflags = MSGFLAG_REPORTABLE;
417 		sp.usm.username[0] = '\0';
418 	} else if (hd->msgflags & MSGFLAG_AUTH && params->engineboots == 0 &&
419 	    params->enginetime == 0)
420 		hdd.msgflags = MSGFLAG_AUTH | MSGFLAG_REPORTABLE;
421 	else
422 		return;
423 
424 	pdu = snmp_pdu(REQUEST_GET, 0, 0, 0, NULL, 0);
425 	snmpv3_encode(s, &ber, &hdd, &sp, pdu);
426 	buflen = ober_get_writebuf(&ber, &buf);
427 	if ((writelen = write(s, buf, buflen)) == -1)
428 		err(1, "write");
429 	if (writelen != buflen)
430 		errx(1, "write: short write");
431 
432  retry:
433 	message = snmp_recv(s, timeout, rbuf, &rbuflen);
434 
435 	hdd.msgflags &= ~MSGFLAG_REPORTABLE;
436 	pdu = snmpv3_decode(s, rbuf, rbuflen, message, &hdd, &sp);
437 
438 	if (params->engineidlen == 0) {
439 		vb.name = OID_STRUCT(USMSTATSUNKNOWNENGINEIDS, 0);
440 		snmp_pdu_validate(pdu, REQUEST_REPORT, 0, 0, 0, &vb, 1);
441 		memcpy(params->engineid, sp.usm.engineid, sp.usm.engineidlen);
442 		params->engineidlen = sp.usm.engineidlen;
443 		if (hd->msgflags & MSGFLAG_AUTH)
444 			snmpv3_usm_discovery(s, timeout, hd, params);
445 	}
446 
447 	ober_free(&ber);
448 	ober_free_elements(message);
449 
450 	return;
451 }
452 
453 void
snmpv3_encode(int s,struct ber * ber,struct headerdata * hd,union securityparams * params,struct ber_element * pdu)454 snmpv3_encode(int s, struct ber *ber, struct headerdata *hd,
455     union securityparams *params, struct ber_element *pdu)
456 {
457 	struct ber_element *message;
458 	void *sp;
459 	size_t splen;
460 
461 	switch (hd->msgsm) {
462 	case SM_USM:
463 		snmpv3_usm_encode(s, ber, &params->usm);
464 		break;
465 	default:
466 		errx(1, "%s: unsupported securityModel %d",
467 		    __func__, hd->msgsm);
468 	}
469 
470 	splen = ober_get_writebuf(ber, &sp);
471 	if ((message = ober_printf_elements(NULL, "{d{ddxd}x{xse}}", 3,
472 	    hd->msgid, hd->msgmaxsize, &hd->msgflags, sizeof(hd->msgflags),
473 	    hd->msgsm, sp, splen, hd->engineid, hd->engineidlen,
474 	    hd->contextname, pdu)) == NULL)
475 		err(1, NULL);
476 	ober_free(ber);
477 	*ber = (struct ber){};
478 	ober_set_application(ber, smi_application);
479 	if (ober_write_elements(ber, message) == -1)
480 		err(1, NULL);
481 	if (verbose) {
482 		printf("SNMP send(%d):\n", s);
483 		smi_debug_elements(message);
484 	}
485 	ober_free_elements(message);
486 }
487 
488 void
snmpv3_usm_encode(int s,struct ber * ber,struct usm * params)489 snmpv3_usm_encode(int s, struct ber *ber, struct usm *params)
490 {
491 	struct ber_element *sp;
492 
493 	*ber = (struct ber){};
494 	ober_set_application(ber, smi_application);
495 	if ((sp = ober_printf_elements(NULL, "{xddxss}", params->engineid,
496 	    params->engineidlen, params->engineboots, params->enginetime,
497 	    params->username, strlen(params->username), "", "")) == NULL)
498 		err(1, NULL);
499 	if (ober_write_elements(ber, sp) == -1)
500 		err(1, NULL);
501 	if (verbose) {
502 		printf("USM params send(%d):\n", s);
503 		smi_debug_elements(sp);
504 	}
505 	ober_free_elements(sp);
506 }
507 
508 struct ber_element *
snmpv3_decode(int s,void * buf,size_t buflen,struct ber_element * message,struct headerdata * hd,union securityparams * sp)509 snmpv3_decode(int s, void *buf, size_t buflen, struct ber_element *message,
510     struct headerdata *hd, union securityparams *sp)
511 {
512 	struct ber_element *pdu;
513 	int32_t version, msgid, msgmaxsize, sm;
514 	char *msgflags, *spstr, *engineid, *name;
515 	size_t msgflagslen, spstrlen, engineidlen, namelen;
516 	int class;
517 	unsigned int type;
518 
519 	if (ober_scanf_elements(message, "{d{ddxd$}x{xxe}",
520 	    &version, &msgid, &msgmaxsize, &msgflags, &msgflagslen,
521 	    &sm, &spstr, &spstrlen, &engineid, &engineidlen, &name, &namelen,
522 	    &pdu) == -1)
523 		errx(1, "%s: ober_scanf_elements", __func__);
524 	if (version != 3)
525 		errx(1, "%s: invalid version", __func__);
526 	if (msgid != hd->msgid)
527 		errx(1, "%s: unexpected msgid", __func__);
528 	if (msgmaxsize < 484 || msgmaxsize > 2147483647)
529 		errx(1, "%s: invalid msgmaxsize", __func__);
530 	if (msgflagslen != 1 || msgflags[0] != hd->msgflags)
531 		errx(1, "%s: invalid msgflags", __func__);
532 	if (sm != hd->msgsm)
533 		errx(1, "%s: unexpected security model", __func__);
534 	if (engineidlen < 5 || engineidlen > 32)
535 		errx(1, "%s: invalid contextEngineID", __func__);
536 	if (hd->engineidlen != 0) {
537 		if (hd->engineidlen != engineidlen ||
538 		    memcmp(hd->engineid, engineid, engineidlen) != 0)
539 			errx(1, "%s: unexpected engineid", __func__);
540 	} else {
541 		hd->engineidlen = engineidlen;
542 		memcpy(hd->engineid, engineid, engineidlen);
543 	}
544 	if (namelen > 255)
545 		errx(1, "%s: invalid ctxnamelen", __func__);
546 	if (strcmp(hd->contextname, name) != 0)
547 		errx(1, "%s: unexpected context", __func__);
548 
549 	switch (sm) {
550 	case SM_USM:
551 		snmpv3_usm_decode(s, buf, buflen, spstr, spstrlen, &sp->usm);
552 	}
553 	return pdu;
554 }
555 
556 void
snmpv3_usm_decode(int s,void * buf,size_t buflen,void * spstr,size_t spstrlen,struct usm * usm)557 snmpv3_usm_decode(int s, void *buf, size_t buflen, void *spstr, size_t spstrlen,
558     struct usm *usm)
559 {
560 	struct ber ber = {};
561 	struct ber_element *sp;
562 	char *engineid, *username, *authparams, *privparams;
563 	size_t engineidlen, usernamelen, authparamslen, privparamslen;
564 	int32_t engineboots, enginetime;
565 
566 	ober_set_application(&ber, smi_application);
567 	ober_set_readbuf(&ber, spstr, spstrlen);
568 	if ((sp = ober_read_elements(&ber, NULL)) == NULL)
569 		errx(1, "%s: ober_read_elements", __func__);
570 	if (verbose) {
571 		printf("USM params received(%d):\n", s);
572 		smi_debug_elements(sp);
573 	}
574 
575 	if (ober_scanf_elements(sp, "{xddxxx}", &engineid, &engineidlen,
576 	    &engineboots, &enginetime, &username, &usernamelen, &authparams,
577 	    &authparamslen, &privparams, &privparamslen) == -1)
578 		errx(1, "%s: ober_scanf_elements", __func__);
579 
580 	if (engineidlen < 5 || engineidlen > 32)
581 		errx(1, "%s: invalid msgAuthoritativeEngineID", __func__);
582 	if (engineboots < 0 || engineboots > 2147483647)
583 		errx(1, "%s: invalid msgAuthoritativeEngineBoots", __func__);
584 	if (enginetime < 0 || enginetime > 2147483647)
585 		errx(1, "%s: invalid msgAuthoritativeEngineTime", __func__);
586 	if (usernamelen < 0 || usernamelen > 32)
587 		errx(1, "%s: invalid msgUserName", __func__);
588 
589 	if (usm->engineidlen == 0) {
590 		memcpy(usm->engineid, engineid, engineidlen);
591 		usm->engineidlen = engineidlen;
592 	} else {
593 		if (usm->engineidlen != engineidlen ||
594 		    memcmp(usm->engineid, engineid, engineidlen) != 0)
595 			errx(1, "%s: unexpected engineid", __func__);
596 	}
597 	if (usm->engineboots == 0 && usm->enginetime == 0) {
598 		usm->engineboots = engineboots;
599 		usm->enginetime = enginetime;
600 	} else {
601 		if (usm->engineboots < engineboots)
602 			errx(1, "%s: engineboots decremented", __func__);
603 		else if (usm->engineboots == engineboots) {
604 			if (enginetime < usm->enginetime - 150 ||
605 			    enginetime > usm->enginetime + 150)
606 				errx(1, "%s: enginetime out of window",
607 				    __func__);
608 		} else {
609 			if (enginetime > 150)
610 				errx(1, "%s: enginetime out of window",
611 				    __func__);
612 		}
613 	}
614 	if (strcmp(username, usm->username) != 0)
615 		errx(1, "unexpected username");
616 }
617 
618 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)619 snmpv3_response_validate(int s, int timeout, struct headerdata *hd,
620     union securityparams *sp, int32_t requestid, int32_t error, int32_t index,
621     struct varbind *varbindlist, size_t nvarbind)
622 {
623 	struct ber_element *message, *pdu;
624 	char buf[1024];
625 	size_t buflen = sizeof(buf);
626 	hd->msgflags &= ~MSGFLAG_REPORTABLE;
627 
628 	message = snmp_recv(s, timeout, buf, &buflen);
629 	pdu = snmpv3_decode(1, buf, sizeof(buf), message, hd, sp);
630 	snmp_pdu_validate(pdu, REQUEST_RESPONSE, requestid, error, index,
631 	    varbindlist, nvarbind);
632 
633 	ober_free_elements(message);
634 }
635 
636 void
snmp_timeout(int s,int timeout)637 snmp_timeout(int s, int timeout)
638 {
639 	int ret;
640 	struct pollfd pfd = {
641 		.fd = s,
642 		.events = POLLIN
643 	};
644 
645 	if ((ret = poll(&pfd, 1, timeout)) == -1)
646 		err(1, "poll");
647 	if (ret != 0)
648 		errx(1, "%s: unexpected snmp data", __func__);
649 }
650 
651 struct ber_element *
snmp_pdu(enum snmp_request request,int32_t requestid,int32_t error,int32_t index,struct varbind * varbindlist,size_t nvarbind)652 snmp_pdu(enum snmp_request request, int32_t requestid, int32_t error,
653     int32_t index, struct varbind *varbindlist, size_t nvarbind)
654 {
655 	struct ber_element *pdu;
656 
657 	if ((pdu = ober_printf_elements(NULL, "{tddde}", BER_CLASS_CONTEXT,
658 	    request, requestid, error, index,
659 	    snmp_varbindlist(varbindlist, nvarbind))) == NULL)
660 		err(1, NULL);
661 	return pdu;
662 }
663 
664 struct ber_element *
snmp_varbindlist(struct varbind * varbindlist,size_t nvarbind)665 snmp_varbindlist(struct varbind *varbindlist, size_t nvarbind)
666 {
667 	struct ber_element *vblist, *prev = NULL;
668 	struct ber_oid oid;
669 	size_t i;
670 
671 	if ((vblist = prev = ober_add_sequence(NULL)) == NULL)
672 		err(1, NULL);
673 	for (i = 0; i < nvarbind; i++) {
674 		if ((prev = ober_printf_elements(prev, "{Oe}",
675 		    snmp_oid2ber_oid(&varbindlist[i].name, &oid),
676 		    snmp_data2ber_element(varbindlist[i].type,
677 		    &varbindlist[i].data))) == NULL)
678 			err(1, NULL);
679 	}
680 
681 	return vblist;
682 }
683 
684 struct ber_oid *
snmp_oid2ber_oid(struct oid * oid,struct ber_oid * boid)685 snmp_oid2ber_oid(struct oid *oid, struct ber_oid *boid)
686 {
687 	size_t i;
688 
689 	for (i = 0; i < oid->n_subid; i++)
690 		boid->bo_id[i] = oid->subid[i];
691 	boid->bo_n = oid->n_subid;
692 
693 	return boid;
694 }
695 
696 struct ber_element *
snmp_data2ber_element(enum type type,union data * data)697 snmp_data2ber_element(enum type type, union data *data)
698 {
699 	struct ber_element *elm;
700 	struct ber_oid oid;
701 
702 	switch (type) {
703 	case TYPE_INTEGER:
704 		elm = ober_add_integer(NULL, data->int32);
705 		break;
706 	case TYPE_OCTETSTRING:
707 		elm = ober_add_nstring(NULL, data->octetstring.string,
708 		    data->octetstring.len);
709 		break;
710 	case TYPE_NULL:
711 		elm = ober_add_null(NULL);
712 		break;
713 	case TYPE_OBJECTIDENTIFIER:
714 		elm = ober_add_oid(NULL, snmp_oid2ber_oid(&data->oid, &oid));
715 		break;
716 	case TYPE_IPADDRESS:
717 		if ((elm = ober_add_nstring(NULL, data->octetstring.string,
718 		    data->octetstring.len)) == NULL)
719 			err(1, NULL);
720 		ober_set_header(elm, BER_CLASS_APPLICATION, APPLICATION_IPADDR);
721 		break;
722 	case TYPE_COUNTER32:
723 		if ((elm = ober_add_integer(NULL, data->uint32)) == NULL)
724 			err(1, NULL);
725 		ober_set_header(elm, BER_CLASS_APPLICATION,
726 		    APPLICATION_COUNTER32);
727 		break;
728 	case TYPE_GAUGE32:
729 		if ((elm = ober_add_integer(NULL, data->uint32)) == NULL)
730 			err(1, NULL);
731 		ober_set_header(elm, BER_CLASS_APPLICATION,
732 		    APPLICATION_GAUGE32);
733 		break;
734 	case TYPE_TIMETICKS:
735 		if ((elm = ober_add_integer(NULL, data->uint32)) == NULL)
736 			err(1, NULL);
737 		ober_set_header(elm, BER_CLASS_APPLICATION,
738 		    APPLICATION_TIMETICKS);
739 		break;
740 	case TYPE_OPAQUE:
741 		if ((elm = ober_add_nstring(NULL, data->octetstring.string,
742 		    data->octetstring.len)) == NULL)
743 			err(1, NULL);
744 		ober_set_header(elm, BER_CLASS_APPLICATION, APPLICATION_OPAQUE);
745 		break;
746 	case TYPE_COUNTER64:
747 		if ((elm = ober_add_integer(NULL, data->uint64)) == NULL)
748 			err(1, NULL);
749 		ober_set_header(elm, BER_CLASS_APPLICATION,
750 		    APPLICATION_COUNTER64);
751 		break;
752 	case TYPE_NOSUCHOBJECT:
753 		if ((elm = ober_add_null(NULL)) == NULL)
754 			err(1, NULL);
755 		ober_set_header(elm, BER_CLASS_CONTEXT, EXCEPTION_NOSUCHOBJECT);
756 		break;
757 	case TYPE_NOSUCHINSTANCE:
758 		if ((elm = ober_add_null(NULL)) == NULL)
759 			err(1, NULL);
760 		ober_set_header(elm, BER_CLASS_CONTEXT,
761 		    EXCEPTION_NOSUCHINSTANCE);
762 		break;
763 	case TYPE_ENDOFMIBVIEW:
764 		if ((elm = ober_add_null(NULL)) == NULL)
765 			err(1, NULL);
766 		ober_set_header(elm, BER_CLASS_CONTEXT, EXCEPTION_ENDOFMIBVIEW);
767 		break;
768 	default:
769 		errx(1, "%s: unsupported type: %d", __func__, type);
770 	}
771 	if (elm == NULL)
772 		err(1, NULL);
773 	return elm;
774 }
775 
776 unsigned int
smi_application(struct ber_element * elm)777 smi_application(struct ber_element *elm)
778 {
779 	if (elm->be_class != BER_CLASS_APPLICATION)
780 		return -1;
781 
782 	switch (elm->be_type) {
783 	case APPLICATION_IPADDR:
784 	case APPLICATION_OPAQUE:
785 		return BER_TYPE_OCTETSTRING;
786 	case APPLICATION_COUNTER32:
787 	case APPLICATION_GAUGE32:
788 	case APPLICATION_TIMETICKS:
789 	case APPLICATION_COUNTER64:
790 		return BER_TYPE_INTEGER;
791 	default:
792 		return -1;
793 	}
794 }
795 
796 char *
smi_oid2string(struct ber_oid * oid,char * buf,size_t len)797 smi_oid2string(struct ber_oid *oid, char *buf, size_t len)
798 {
799 	char digit[11];
800 	size_t i;
801 
802 	buf[0] = '\0';
803 	for (i = 0; i < oid->bo_n; i++) {
804 		snprintf(digit, sizeof(digit), "%"PRIu32, oid->bo_id[i]);
805 		if (i > 0)
806 			strlcat(buf, ".", len);
807 		strlcat(buf, digit, len);
808 	}
809 	return buf;
810 }
811 
812 char *
smi_print_element(struct ber_element * root)813 smi_print_element(struct ber_element *root)
814 {
815 	char		*str = NULL, *buf, *p;
816 	long long	 v;
817 	struct ber_oid	 o;
818 	char		 strbuf[BUFSIZ];
819 
820 	switch (root->be_class) {
821 	case BER_CLASS_UNIVERSAL:
822 		switch (root->be_type) {
823 		case BER_TYPE_INTEGER:
824 			if (ober_get_integer(root, &v) == -1)
825 				goto fail;
826 			if (asprintf(&str, "%lld", v) == -1)
827 				goto fail;
828 			break;
829 		case BER_TYPE_OBJECT:
830 			if (ober_get_oid(root, &o) == -1)
831 				goto fail;
832 			if (asprintf(&str, "%s", smi_oid2string(&o, strbuf,
833 			    sizeof(strbuf))) == -1)
834 				goto fail;
835 			break;
836 		case BER_TYPE_OCTETSTRING:
837 			if (ober_get_string(root, &buf) == -1)
838 				goto fail;
839 			p = reallocarray(NULL, 4, root->be_len + 1);
840 			if (p == NULL)
841 				goto fail;
842 			strvisx(p, buf, root->be_len, VIS_NL);
843 			if (asprintf(&str, "\"%s\"", p) == -1) {
844 				free(p);
845 				goto fail;
846 			}
847 			free(p);
848 			break;
849 		case BER_TYPE_NULL:
850 			if (asprintf(&str, "null") == -1)
851 				goto fail;
852 			break;
853 		default:
854 			/* Should not happen in a valid SNMP packet */
855 			if (asprintf(&str, "[U/%u]", root->be_type) == -1)
856 				goto fail;
857 			break;
858 		}
859 		break;
860 	case BER_CLASS_APPLICATION:
861 		switch (root->be_type) {
862 		case APPLICATION_IPADDR:
863 			if (ober_get_string(root, &buf) == -1)
864 				goto fail;
865 			if (asprintf(&str, "%s",
866 			    inet_ntoa(*(struct in_addr *)buf)) == -1)
867 					goto fail;
868 			break;
869 		case APPLICATION_COUNTER32:
870 			if (ober_get_integer(root, &v) == -1)
871 				goto fail;
872 			if (asprintf(&str, "%lld(c32)", v) == -1)
873 				goto fail;
874 			break;
875 		case APPLICATION_GAUGE32:
876 			if (ober_get_integer(root, &v) == -1)
877 				goto fail;
878 			if (asprintf(&str, "%lld(g32)", v) == -1)
879 				goto fail;
880 			break;
881 		case APPLICATION_TIMETICKS:
882 			if (ober_get_integer(root, &v) == -1)
883 				goto fail;
884 			if (asprintf(&str, "%lld.%llds", v/100, v%100) == -1)
885 				goto fail;
886 			break;
887 		case APPLICATION_OPAQUE:
888 			if (ober_get_string(root, &buf) == -1)
889 				goto fail;
890 			p = reallocarray(NULL, 4, root->be_len + 1);
891 			if (p == NULL)
892 				goto fail;
893 			strvisx(p, buf, root->be_len, VIS_NL);
894 			if (asprintf(&str, "\"%s\"(opaque)", p) == -1) {
895 				free(p);
896 				goto fail;
897 			}
898 			free(p);
899 			break;
900 		case APPLICATION_COUNTER64:
901 			if (ober_get_integer(root, &v) == -1)
902 				goto fail;
903 			if (asprintf(&str, "%lld(c64)", v) == -1)
904 				goto fail;
905 			break;
906 		default:
907 			/* Should not happen in a valid SNMP packet */
908 			if (asprintf(&str, "[A/%u]", root->be_type) == -1)
909 				goto fail;
910 			break;
911 		}
912 		break;
913 	case BER_CLASS_CONTEXT:
914 		switch (root->be_type) {
915 		case EXCEPTION_NOSUCHOBJECT:
916 			str = strdup("noSuchObject");
917 			break;
918 		case EXCEPTION_NOSUCHINSTANCE:
919 			str = strdup("noSuchInstance");
920 			break;
921 		case EXCEPTION_ENDOFMIBVIEW:
922 			str = strdup("endOfMibView");
923 			break;
924 		default:
925 			/* Should not happen in a valid SNMP packet */
926 			if (asprintf(&str, "[C/%u]", root->be_type) == -1)
927 				goto fail;
928 			break;
929 		}
930 		break;
931 	default:
932 		/* Should not happen in a valid SNMP packet */
933 		if (asprintf(&str, "[%hhu/%u]", root->be_class,
934 		    root->be_type) == -1)
935 			goto fail;
936 		break;
937 	}
938 
939 	return (str);
940 
941  fail:
942 	free(str);
943 	return (NULL);
944 }
945 
946 void
smi_debug_elements(struct ber_element * root)947 smi_debug_elements(struct ber_element *root)
948 {
949 	static int	 indent = 0;
950 	char		*value;
951 	int		 constructed;
952 
953 	/* calculate lengths */
954 	ober_calc_len(root);
955 
956 	switch (root->be_encoding) {
957 	case BER_TYPE_SEQUENCE:
958 	case BER_TYPE_SET:
959 		constructed = root->be_encoding;
960 		break;
961 	default:
962 		constructed = 0;
963 		break;
964 	}
965 
966 	fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
967 	switch (root->be_class) {
968 	case BER_CLASS_UNIVERSAL:
969 		fprintf(stderr, "class: universal(%u) type: ", root->be_class);
970 		switch (root->be_type) {
971 		case BER_TYPE_EOC:
972 			fprintf(stderr, "end-of-content");
973 			break;
974 		case BER_TYPE_INTEGER:
975 			fprintf(stderr, "integer");
976 			break;
977 		case BER_TYPE_BITSTRING:
978 			fprintf(stderr, "bit-string");
979 			break;
980 		case BER_TYPE_OCTETSTRING:
981 			fprintf(stderr, "octet-string");
982 			break;
983 		case BER_TYPE_NULL:
984 			fprintf(stderr, "null");
985 			break;
986 		case BER_TYPE_OBJECT:
987 			fprintf(stderr, "object");
988 			break;
989 		case BER_TYPE_ENUMERATED:
990 			fprintf(stderr, "enumerated");
991 			break;
992 		case BER_TYPE_SEQUENCE:
993 			fprintf(stderr, "sequence");
994 			break;
995 		case BER_TYPE_SET:
996 			fprintf(stderr, "set");
997 			break;
998 		}
999 		break;
1000 	case BER_CLASS_APPLICATION:
1001 		fprintf(stderr, "class: application(%u) type: ",
1002 		    root->be_class);
1003 		switch (root->be_type) {
1004 		case APPLICATION_IPADDR:
1005 			fprintf(stderr, "ipaddr");
1006 			break;
1007 		case APPLICATION_COUNTER32:
1008 			fprintf(stderr, "counter32");
1009 			break;
1010 		case APPLICATION_GAUGE32:
1011 			fprintf(stderr, "gauge32");
1012 			break;
1013 		case APPLICATION_TIMETICKS:
1014 			fprintf(stderr, "timeticks");
1015 			break;
1016 		case APPLICATION_OPAQUE:
1017 			fprintf(stderr, "opaque");
1018 			break;
1019 		case APPLICATION_COUNTER64:
1020 			fprintf(stderr, "counter64");
1021 			break;
1022 		}
1023 		break;
1024 	case BER_CLASS_CONTEXT:
1025 		fprintf(stderr, "class: context(%u) type: ",
1026 		    root->be_class);
1027 		switch (root->be_type) {
1028 		case REQUEST_GET:
1029 			fprintf(stderr, "getreq");
1030 			break;
1031 		case REQUEST_GETNEXT:
1032 			fprintf(stderr, "getnextreq");
1033 			break;
1034 		case REQUEST_RESPONSE:
1035 			fprintf(stderr, "response");
1036 			break;
1037 		case REQUEST_SET:
1038 			fprintf(stderr, "setreq");
1039 			break;
1040 		case REQUEST_TRAP:
1041 			fprintf(stderr, "trap");
1042 			break;
1043 		case REQUEST_GETBULK:
1044 			fprintf(stderr, "getbulkreq");
1045 			break;
1046 		case REQUEST_INFORM:
1047 			fprintf(stderr, "informreq");
1048 			break;
1049 		case REQUEST_TRAPV2:
1050 			fprintf(stderr, "trapv2");
1051 			break;
1052 		case REQUEST_REPORT:
1053 			fprintf(stderr, "report");
1054 			break;
1055 		}
1056 		break;
1057 	case BER_CLASS_PRIVATE:
1058 		fprintf(stderr, "class: private(%u) type: ", root->be_class);
1059 		break;
1060 	default:
1061 		fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
1062 		break;
1063 	}
1064 	fprintf(stderr, "(%u) encoding %u ",
1065 	    root->be_type, root->be_encoding);
1066 
1067 	if ((value = smi_print_element(root)) == NULL)
1068 		goto invalid;
1069 
1070 	switch (root->be_encoding) {
1071 	case BER_TYPE_INTEGER:
1072 	case BER_TYPE_ENUMERATED:
1073 		fprintf(stderr, "value %s", value);
1074 		break;
1075 	case BER_TYPE_BITSTRING:
1076 		fprintf(stderr, "hexdump %s", value);
1077 		break;
1078 	case BER_TYPE_OBJECT:
1079 		fprintf(stderr, "oid %s", value);
1080 		break;
1081 	case BER_TYPE_OCTETSTRING:
1082 		if (root->be_class == BER_CLASS_APPLICATION &&
1083 		    root->be_type == APPLICATION_IPADDR) {
1084 			fprintf(stderr, "addr %s", value);
1085 		} else {
1086 			fprintf(stderr, "string %s", value);
1087 		}
1088 		break;
1089 	case BER_TYPE_NULL:	/* no payload */
1090 	case BER_TYPE_EOC:
1091 	case BER_TYPE_SEQUENCE:
1092 	case BER_TYPE_SET:
1093 	default:
1094 		fprintf(stderr, "%s", value);
1095 		break;
1096 	}
1097 
1098  invalid:
1099 	if (value == NULL)
1100 		fprintf(stderr, "<INVALID>");
1101 	else
1102 		free(value);
1103 	fprintf(stderr, "\n");
1104 
1105 	if (constructed)
1106 		root->be_encoding = constructed;
1107 
1108 	if (constructed && root->be_sub) {
1109 		indent += 2;
1110 		smi_debug_elements(root->be_sub);
1111 		indent -= 2;
1112 	}
1113 	if (root->be_next)
1114 		smi_debug_elements(root->be_next);
1115 }
1116 
1117 struct ber_element *
v2cmps(struct ber_element * message,const char * community)1118 v2cmps(struct ber_element *message, const char *community)
1119 {
1120 	int version;
1121 	const char *mcommunity;
1122 	struct ber_element *pdu;
1123 
1124 	if (ober_scanf_elements(message, "{dse}$", &version, &mcommunity, &pdu) == -1)
1125 		err(1, "%s: ober_scanf_elements", __func__);
1126 
1127 	if (strcmp(mcommunity, community) != 0)
1128 		errx(1, "%s: invalid community (%s/%s)", __func__, mcommunity,
1129 		    community);
1130 
1131 	return pdu;
1132 }
1133 
1134 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)1135 snmp_pdu_validate(struct ber_element *pdu, enum snmp_request request,
1136     int32_t requestid, int32_t error, int32_t index, struct varbind *varbindlist,
1137     size_t nvarbind)
1138 {
1139 	int32_t mrequestid, merror, mindex;
1140 	int class;
1141 	unsigned int type;
1142 	struct ber_element *mvarbindlist, *varbind;
1143 	struct ber_oid moid, oid;
1144 	struct ber_element *value;
1145 	char oidbuf1[512], oidbuf2[512];
1146 	size_t i;
1147 
1148 	if (ober_scanf_elements(pdu, "t{ddd{e}$}$", &class, &type, &mrequestid,
1149 	    &merror, &mindex, &varbind) == -1)
1150 		err(1, "%s: ober_scanf_elementsi", __func__);
1151 
1152 	if (class != BER_CLASS_CONTEXT || type != request)
1153 		errx(1, "%s: unexpected pdu type", __func__);
1154 	if (mrequestid != requestid)
1155 		errx(1, "%s: unexpected pdu requestid (%d/%d)",
1156 		    __func__, mrequestid, requestid);
1157 	if (error != merror)
1158 		errx(1, "%s: unexpected pdu error (%d/%d)",
1159 		    __func__, merror, error);
1160 	if (index != mindex)
1161 		errx(1, "%s: unepxected pdu index (%d/%d)",
1162 		    __func__, mindex, index);
1163 
1164 	for (i = 0; varbind != NULL; varbind = varbind->be_next, i++) {
1165 		if (ober_scanf_elements(varbind, "{oeS$}", &moid, &value) == -1)
1166 			err(1, "%s: ober_scanf_elements", __func__);
1167 		if (i >= nvarbind)
1168 			continue;
1169 		snmp_oid2ber_oid(&varbindlist[i].name, &oid);
1170 		if (!varbindlist[i].nameunknown && ober_oid_cmp(&moid, &oid) != 0)
1171 			errx(1, "%s: unexpected oid (%s/%s)", __func__,
1172 			    smi_oid2string(&moid, oidbuf1, sizeof(oidbuf1)),
1173 			    smi_oid2string(&oid, oidbuf2, sizeof(oidbuf2)));
1174 		if (value->be_class == BER_CLASS_UNIVERSAL &&
1175 		    value->be_type == BER_TYPE_INTEGER) {
1176 			if (!varbindlist[i].typeunknown &&
1177 			    varbindlist[i].type != TYPE_INTEGER)
1178 				errx(1, "%s: unexpected value", __func__);
1179 			if (!varbindlist[i].dataunknown &&
1180 			    varbindlist[i].data.int32 != value->be_numeric)
1181 				errx(1, "%s: unexpected value", __func__);
1182 		} else if (value->be_class == BER_CLASS_UNIVERSAL &&
1183 		    value->be_type == BER_TYPE_OCTETSTRING) {
1184 			if (!varbindlist[i].typeunknown &&
1185 			    varbindlist[i].type != TYPE_OCTETSTRING)
1186 				errx(1, "%s: unexpected value", __func__);
1187 			if (!varbindlist[i].dataunknown && (
1188 			    varbindlist[i].data.octetstring.len !=
1189 			    value->be_len ||
1190 			    memcmp(varbindlist[i].data.octetstring.string,
1191 			    value->be_val, value->be_len) != 0))
1192 				errx(1, "%s: unexpected value", __func__);
1193 		} else if (value->be_class == BER_CLASS_UNIVERSAL &&
1194 		    value->be_type == BER_TYPE_NULL) {
1195 			if (!varbindlist[i].typeunknown &&
1196 			    varbindlist[i].type != TYPE_NULL)
1197 				errx(1, "%s: unexpected value", __func__);
1198 		} else if (value->be_class == BER_CLASS_UNIVERSAL &&
1199 		    value->be_type == BER_TYPE_OBJECT) {
1200 			if (!varbindlist[i].typeunknown &&
1201 			    varbindlist[i].type != TYPE_OBJECTIDENTIFIER)
1202 				errx(1, "%s: unexpected value", __func__);
1203 			if (!varbindlist[i].dataunknown) {
1204 				if (ober_get_oid(value, &moid) == -1)
1205 					errx(1, "%s: unexpected value",
1206 					    __func__);
1207 				snmp_oid2ber_oid(&varbindlist[i].data.oid,
1208 				    &oid);
1209 				if (ober_oid_cmp(&oid, &moid) != 0)
1210 					errx(1, "%s: unexpected value",
1211 					    __func__);
1212 			}
1213 		} else if (value->be_class == BER_CLASS_APPLICATION &&
1214 		    value->be_type == APPLICATION_IPADDR) {
1215 			if (!varbindlist[i].typeunknown &&
1216 			    varbindlist[i].type != TYPE_IPADDRESS)
1217 				errx(1, "%s: unexpected value", __func__);
1218 			if (value->be_len != 4)
1219 				errx(1, "%s: unexpected value", __func__);
1220 			if (!varbindlist[i].dataunknown) {
1221 				if (memcmp(value->be_val,
1222 				    varbindlist[i].data.octetstring.string,
1223 				    value->be_len) != 0)
1224 					errx(1, "%s: unexpected value",
1225 					    __func__);
1226 			}
1227 		} else if (value->be_class == BER_CLASS_APPLICATION &&
1228 		    value->be_type == APPLICATION_COUNTER32) {
1229 			if (!varbindlist[i].typeunknown &&
1230 			    varbindlist[i].type != TYPE_COUNTER32)
1231 				errx(1, "%s: unexpected value", __func__);
1232 			if (!varbindlist[i].dataunknown &&
1233 			    varbindlist[i].data.uint32 != value->be_numeric)
1234 				errx(1, "%s: unexpected value", __func__);
1235 		} else if (value->be_class == BER_CLASS_APPLICATION &&
1236 		    value->be_type == APPLICATION_GAUGE32) {
1237 			if (!varbindlist[i].typeunknown &&
1238 			    varbindlist[i].type != TYPE_GAUGE32)
1239 				errx(1, "%s: unexpected value", __func__);
1240 			if (!varbindlist[i].dataunknown &&
1241 			    varbindlist[i].data.uint32 != value->be_numeric)
1242 				errx(1, "%s: unexpected value", __func__);
1243 		} else if (value->be_class == BER_CLASS_APPLICATION &&
1244 		    value->be_type == APPLICATION_TIMETICKS) {
1245 			if (!varbindlist[i].typeunknown &&
1246 			    varbindlist[i].type != TYPE_TIMETICKS)
1247 				errx(1, "%s: unexpected value", __func__);
1248 			if (!varbindlist[i].dataunknown &&
1249 			    varbindlist[i].data.uint32 != value->be_numeric)
1250 				errx(1, "%s: unexpected value", __func__);
1251 		} else if (value->be_class == BER_CLASS_APPLICATION &&
1252 		    value->be_type == APPLICATION_OPAQUE) {
1253 			if (!varbindlist[i].typeunknown &&
1254 			    varbindlist[i].type != TYPE_OPAQUE)
1255 				errx(1, "%s: unexpected value", __func__);
1256 			if (!varbindlist[i].dataunknown && (
1257 			    varbindlist[i].data.octetstring.len !=
1258 			    value->be_len ||
1259 			    memcmp(varbindlist[i].data.octetstring.string,
1260 			    value->be_val, value->be_len) != 0))
1261 				errx(1, "%s: unexpected value", __func__);
1262 		} else if (value->be_class == BER_CLASS_APPLICATION &&
1263 		    value->be_type == APPLICATION_COUNTER64) {
1264 			if (!varbindlist[i].typeunknown &&
1265 			    varbindlist[i].type != TYPE_COUNTER64)
1266 				errx(1, "%s: unexpected value", __func__);
1267 			if (!varbindlist[i].dataunknown &&
1268 			    varbindlist[i].data.uint64 != value->be_numeric)
1269 				errx(1, "%s: unexpected value", __func__);
1270 		} else if (value->be_class == BER_CLASS_CONTEXT &&
1271 		    value->be_type == EXCEPTION_NOSUCHOBJECT) {
1272 			if (!varbindlist[i].typeunknown &&
1273 			    varbindlist[i].type != TYPE_NOSUCHOBJECT)
1274 				errx(1, "%s: unexpected value", __func__);
1275 		} else if (value->be_class == BER_CLASS_CONTEXT &&
1276 		    value->be_type == EXCEPTION_NOSUCHINSTANCE) {
1277 			if (!varbindlist[i].typeunknown &&
1278 			    varbindlist[i].type != TYPE_NOSUCHINSTANCE)
1279 				errx(1, "%s: unexpected value", __func__);
1280 		} else if (value->be_class == BER_CLASS_CONTEXT &&
1281 		    value->be_type == EXCEPTION_ENDOFMIBVIEW) {
1282 			if (!varbindlist[i].typeunknown &&
1283 			    varbindlist[i].type != TYPE_ENDOFMIBVIEW)
1284 				errx(1, "%s: unexpected value", __func__);
1285 		} else
1286 			errx(1, "%s: unexpected value", __func__);
1287 	}
1288 	if (i != nvarbind)
1289 		errx(1, "%s: unexpected amount of varbind (%zu/%zu)", __func__,
1290 		    i, nvarbind);
1291 }
1292