xref: /openbsd-src/usr.sbin/snmpd/snmpe.c (revision ffcef06798eb7b98532e76a80212f0772bebc4f6)
1 /*	$OpenBSD: snmpe.c,v 1.75 2021/09/02 05:41:02 martijn Exp $	*/
2 
3 /*
4  * Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
5  * Copyright (c) 2017 Marco Pfatschbacher <mpf@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/queue.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <sys/tree.h>
26 
27 #include <net/if.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <errno.h>
34 #include <event.h>
35 #include <fcntl.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <pwd.h>
39 
40 #include "snmpd.h"
41 #include "mib.h"
42 
43 void	 snmpe_init(struct privsep *, struct privsep_proc *, void *);
44 const char *snmpe_pdutype2string(enum snmp_pdutype);
45 int	 snmpe_parse(struct snmp_message *);
46 void	 snmpe_tryparse(int, struct snmp_message *);
47 int	 snmpe_parsevarbinds(struct snmp_message *);
48 void	 snmpe_sig_handler(int sig, short, void *);
49 int	 snmpe_bind(struct address *);
50 void	 snmpe_recvmsg(int fd, short, void *);
51 void	 snmpe_readcb(int fd, short, void *);
52 void	 snmpe_writecb(int fd, short, void *);
53 void	 snmpe_acceptcb(int fd, short, void *);
54 void	 snmpe_prepare_read(struct snmp_message *, int);
55 int	 snmpe_encode(struct snmp_message *);
56 void	 snmp_msgfree(struct snmp_message *);
57 
58 struct imsgev	*iev_parent;
59 static const struct timeval	snmpe_tcp_timeout = { 10, 0 }; /* 10s */
60 
61 struct snmp_messages snmp_messages = RB_INITIALIZER(&snmp_messages);
62 
63 static struct privsep_proc procs[] = {
64 	{ "parent",	PROC_PARENT }
65 };
66 
67 void
68 snmpe(struct privsep *ps, struct privsep_proc *p)
69 {
70 	struct snmpd		*env = ps->ps_env;
71 	struct address		*h;
72 #ifdef DEBUG
73 	char		 buf[BUFSIZ];
74 	struct oid	*oid;
75 #endif
76 
77 #ifdef DEBUG
78 	for (oid = NULL; (oid = smi_foreach(oid, 0)) != NULL;) {
79 		smi_oid2string(&oid->o_id, buf, sizeof(buf), 0);
80 		log_debug("oid %s", buf);
81 	}
82 #endif
83 
84 	/* bind SNMP UDP/TCP sockets */
85 	TAILQ_FOREACH(h, &env->sc_addresses, entry)
86 		if ((h->fd = snmpe_bind(h)) == -1)
87 			fatal("snmpe: failed to bind SNMP socket");
88 
89 	proc_run(ps, p, procs, nitems(procs), snmpe_init, NULL);
90 }
91 
92 /* ARGSUSED */
93 void
94 snmpe_init(struct privsep *ps, struct privsep_proc *p, void *arg)
95 {
96 	struct snmpd		*env = ps->ps_env;
97 	struct address		*h;
98 
99 	kr_init();
100 	timer_init();
101 	usm_generate_keys();
102 
103 	/* listen for incoming SNMP UDP/TCP messages */
104 	TAILQ_FOREACH(h, &env->sc_addresses, entry) {
105 		if (h->type == SOCK_STREAM) {
106 			if (listen(h->fd, 5) < 0)
107 				fatalx("snmpe: failed to listen on socket");
108 			event_set(&h->ev, h->fd, EV_READ, snmpe_acceptcb, h);
109 			evtimer_set(&h->evt, snmpe_acceptcb, h);
110 		} else {
111 			event_set(&h->ev, h->fd, EV_READ|EV_PERSIST,
112 			    snmpe_recvmsg, h);
113 		}
114 		event_add(&h->ev, NULL);
115 	}
116 
117 	/* no filesystem visibility */
118 	if (unveil("/", "") == -1)
119 		fatal("unveil");
120 	if (unveil(NULL, NULL) == -1)
121 		fatal("unveil");
122 
123 	log_info("snmpe %s: ready",
124 	    tohexstr(env->sc_engineid, env->sc_engineid_len));
125 	trap_init();
126 }
127 
128 void
129 snmpe_shutdown(void)
130 {
131 	struct address *h;
132 
133 	TAILQ_FOREACH(h, &snmpd_env->sc_addresses, entry) {
134 		event_del(&h->ev);
135 		if (h->type == SOCK_STREAM)
136 			event_del(&h->evt);
137 		close(h->fd);
138 	}
139 	kr_shutdown();
140 }
141 
142 int
143 snmpe_bind(struct address *addr)
144 {
145 	char	 buf[512];
146 	int	 val, s;
147 
148 	if ((s = snmpd_socket_af(&addr->ss, addr->type)) == -1)
149 		return (-1);
150 
151 	/*
152 	 * Socket options
153 	 */
154 	if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
155 		goto bad;
156 
157 	if (addr->type == SOCK_STREAM) {
158 		val = 1;
159 		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
160 		    &val, sizeof(val)) == -1)
161 			fatal("setsockopt SO_REUSEADDR");
162 	} else { /* UDP */
163 		switch (addr->ss.ss_family) {
164 		case AF_INET:
165 			val = 1;
166 			if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR,
167 			    &val, sizeof(int)) == -1) {
168 				log_warn("%s: failed to set IPv4 packet info",
169 				    __func__);
170 				goto bad;
171 			}
172 			break;
173 		case AF_INET6:
174 			val = 1;
175 			if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
176 			    &val, sizeof(int)) == -1) {
177 				log_warn("%s: failed to set IPv6 packet info",
178 				    __func__);
179 				goto bad;
180 			}
181 		}
182 	}
183 
184 	if (bind(s, (struct sockaddr *)&addr->ss, addr->ss.ss_len) == -1)
185 		goto bad;
186 
187 	if (print_host(&addr->ss, buf, sizeof(buf)) == NULL)
188 		goto bad;
189 
190 	log_info("snmpe: listening on %s %s:%d",
191 	    (addr->type == SOCK_STREAM) ? "tcp" : "udp", buf, addr->port);
192 
193 	return (s);
194 
195  bad:
196 	close(s);
197 	return (-1);
198 }
199 
200 const char *
201 snmpe_pdutype2string(enum snmp_pdutype pdutype)
202 {
203 	static char unknown[sizeof("Unknown (4294967295)")];
204 
205 	switch (pdutype) {
206 	case SNMP_C_GETREQ:
207 		return "GetRequest";
208 	case SNMP_C_GETNEXTREQ:
209 		return "GetNextRequest";
210 	case SNMP_C_GETRESP:
211 		return "Response";
212 	case SNMP_C_SETREQ:
213 		return "SetRequest";
214 	case SNMP_C_TRAP:
215 		return "Trap";
216 	case SNMP_C_GETBULKREQ:
217 		return "GetBulkRequest";
218 	case SNMP_C_INFORMREQ:
219 		return "InformRequest";
220 	case SNMP_C_TRAPV2:
221 		return "SNMPv2-Trap";
222 	case SNMP_C_REPORT:
223 		return "Report";
224 	}
225 
226 	snprintf(unknown, sizeof(unknown), "Unknown (%u)", pdutype);
227 	return unknown;
228 }
229 
230 int
231 snmpe_parse(struct snmp_message *msg)
232 {
233 	struct snmpd		*env = snmpd_env;
234 	struct snmp_stats	*stats = &env->sc_stats;
235 	struct ber_element	*a;
236 	long long		 ver, req;
237 	long long		 errval, erridx;
238 	u_int			 class;
239 	char			*comn;
240 	char			*flagstr, *ctxname;
241 	size_t			 len;
242 	struct sockaddr_storage *ss = &msg->sm_ss;
243 	struct ber_element	*root = msg->sm_req;
244 
245 	msg->sm_errstr = "invalid message";
246 
247 	do {
248 		msg->sm_transactionid = arc4random();
249 	} while (msg->sm_transactionid == 0 ||
250 	    RB_INSERT(snmp_messages, &snmp_messages, msg) != NULL);
251 
252 	if (ober_scanf_elements(root, "{ie", &ver, &a) != 0)
253 		goto parsefail;
254 
255 	/* SNMP version and community */
256 	msg->sm_version = ver;
257 	switch (msg->sm_version) {
258 	case SNMP_V1:
259 		if (!(msg->sm_aflags & ADDRESS_FLAG_SNMPV1)) {
260 			msg->sm_errstr = "SNMPv1 disabled";
261 			goto badversion;
262 		}
263 	case SNMP_V2:
264 		if (msg->sm_version == SNMP_V2 &&
265 		    !(msg->sm_aflags & ADDRESS_FLAG_SNMPV2)) {
266 			msg->sm_errstr = "SNMPv2c disabled";
267 			goto badversion;
268 		}
269 		if (ober_scanf_elements(a, "seS$", &comn, &msg->sm_pdu) != 0)
270 			goto parsefail;
271 		if (strlcpy(msg->sm_community, comn,
272 		    sizeof(msg->sm_community)) >= sizeof(msg->sm_community) ||
273 		    msg->sm_community[0] == '\0') {
274 			stats->snmp_inbadcommunitynames++;
275 			msg->sm_errstr = "invalid community name";
276 			goto fail;
277 		}
278 		break;
279 	case SNMP_V3:
280 		if (!(msg->sm_aflags & ADDRESS_FLAG_SNMPV3)) {
281 			msg->sm_errstr = "SNMPv3 disabled";
282 			goto badversion;
283 		}
284 		if (ober_scanf_elements(a, "{iisi$}e",
285 		    &msg->sm_msgid, &msg->sm_max_msg_size, &flagstr,
286 		    &msg->sm_secmodel, &a) != 0)
287 			goto parsefail;
288 
289 		msg->sm_flags = *flagstr;
290 		if ((a = usm_decode(msg, a, &msg->sm_errstr)) == NULL)
291 			goto parsefail;
292 
293 		if (MSG_SECLEVEL(msg) < env->sc_min_seclevel ||
294 		    msg->sm_secmodel != SNMP_SEC_USM) {
295 			/* XXX currently only USM supported */
296 			msg->sm_errstr = "unsupported security model";
297 			stats->snmp_usmbadseclevel++;
298 			msg->sm_usmerr = OIDVAL_usmErrSecLevel;
299 			goto parsefail;
300 		}
301 
302 		if (ober_scanf_elements(a, "{xxeS$}$",
303 		    &msg->sm_ctxengineid, &msg->sm_ctxengineid_len,
304 		    &ctxname, &len, &msg->sm_pdu) != 0)
305 			goto parsefail;
306 		if (len > SNMPD_MAXCONTEXNAMELEN)
307 			goto parsefail;
308 		memcpy(msg->sm_ctxname, ctxname, len);
309 		msg->sm_ctxname[len] = '\0';
310 		break;
311 	default:
312 		msg->sm_errstr = "unsupported snmp version";
313 badversion:
314 		stats->snmp_inbadversions++;
315 		goto fail;
316 	}
317 
318 	if (ober_scanf_elements(msg->sm_pdu, "t{e", &class, &(msg->sm_pdutype),
319 	    &a) != 0)
320 		goto parsefail;
321 
322 	/* SNMP PDU context */
323 	if (class != BER_CLASS_CONTEXT)
324 		goto parsefail;
325 
326 	switch (msg->sm_pdutype) {
327 	case SNMP_C_GETBULKREQ:
328 		if (msg->sm_version == SNMP_V1) {
329 			stats->snmp_inbadversions++;
330 			msg->sm_errstr =
331 			    "invalid request for protocol version 1";
332 			goto fail;
333 		}
334 		/* FALLTHROUGH */
335 
336 	case SNMP_C_GETREQ:
337 		stats->snmp_ingetrequests++;
338 		/* FALLTHROUGH */
339 
340 	case SNMP_C_GETNEXTREQ:
341 		if (msg->sm_pdutype == SNMP_C_GETNEXTREQ)
342 			stats->snmp_ingetnexts++;
343 		if (!(msg->sm_aflags & ADDRESS_FLAG_READ)) {
344 			msg->sm_errstr = "read requests disabled";
345 			goto fail;
346 		}
347 		if (msg->sm_version != SNMP_V3 &&
348 		    strcmp(env->sc_rdcommunity, msg->sm_community) != 0 &&
349 		    strcmp(env->sc_rwcommunity, msg->sm_community) != 0) {
350 			stats->snmp_inbadcommunitynames++;
351 			msg->sm_errstr = "wrong read community";
352 			goto fail;
353 		}
354 		break;
355 
356 	case SNMP_C_SETREQ:
357 		stats->snmp_insetrequests++;
358 		if (!(msg->sm_aflags & ADDRESS_FLAG_WRITE)) {
359 			msg->sm_errstr = "write requests disabled";
360 			goto fail;
361 		}
362 		if (msg->sm_version != SNMP_V3 &&
363 		    strcmp(env->sc_rwcommunity, msg->sm_community) != 0) {
364 			if (strcmp(env->sc_rdcommunity, msg->sm_community) != 0)
365 				stats->snmp_inbadcommunitynames++;
366 			else
367 				stats->snmp_inbadcommunityuses++;
368 			msg->sm_errstr = "wrong write community";
369 			goto fail;
370 		}
371 		break;
372 
373 	case SNMP_C_GETRESP:
374 		stats->snmp_ingetresponses++;
375 		msg->sm_errstr = "response without request";
376 		goto parsefail;
377 
378 	case SNMP_C_TRAP:
379 		if (msg->sm_version != SNMP_V1) {
380 			msg->sm_errstr = "trapv1 request on !SNMPv1 message";
381 			goto parsefail;
382 		}
383 	case SNMP_C_TRAPV2:
384 		if (msg->sm_pdutype == SNMP_C_TRAPV2 &&
385 		    !(msg->sm_version == SNMP_V2 ||
386 		    msg->sm_version == SNMP_V3)) {
387 			msg->sm_errstr = "trapv2 request on !SNMPv2C or "
388 			    "!SNMPv3 message";
389 			goto parsefail;
390 		}
391 		if (!(msg->sm_aflags & ADDRESS_FLAG_NOTIFY)) {
392 			msg->sm_errstr = "notify requests disabled";
393 			goto fail;
394 		}
395 		if (msg->sm_version == SNMP_V3) {
396 			msg->sm_errstr = "SNMPv3 doesn't support traps yet";
397 			goto fail;
398 		}
399 		if (strcmp(env->sc_trcommunity, msg->sm_community) != 0) {
400 			stats->snmp_inbadcommunitynames++;
401 			msg->sm_errstr = "wrong trap community";
402 			goto fail;
403 		}
404 		stats->snmp_intraps++;
405 		/*
406 		 * This should probably go into parsevarbinds, but that's for a
407 		 * next refactor
408 		 */
409 		if (traphandler_parse(msg) == -1)
410 			goto fail;
411 		/* Shortcircuit */
412 		return 0;
413 	default:
414 		msg->sm_errstr = "invalid context";
415 		goto parsefail;
416 	}
417 
418 	/* SNMP PDU */
419 	if (ober_scanf_elements(a, "iiie{e{}}$",
420 	    &req, &errval, &erridx, &msg->sm_pduend,
421 	    &msg->sm_varbind) != 0) {
422 		stats->snmp_silentdrops++;
423 		msg->sm_errstr = "invalid PDU";
424 		goto fail;
425 	}
426 
427 	msg->sm_request = req;
428 	msg->sm_error = errval;
429 	msg->sm_errorindex = erridx;
430 
431 	print_host(ss, msg->sm_host, sizeof(msg->sm_host));
432 	if (msg->sm_version == SNMP_V3)
433 		log_debug("%s: %s:%hd: SNMPv3 pdutype %s, flags %#x, "
434 		    "secmodel %lld, user '%s', ctx-engine %s, ctx-name '%s', "
435 		    "request %lld", __func__, msg->sm_host, msg->sm_port,
436 		    snmpe_pdutype2string(msg->sm_pdutype), msg->sm_flags,
437 		    msg->sm_secmodel, msg->sm_username,
438 		    tohexstr(msg->sm_ctxengineid, msg->sm_ctxengineid_len),
439 		    msg->sm_ctxname, msg->sm_request);
440 	else
441 		log_debug("%s: %s:%hd: SNMPv%d '%s' pdutype %s request %lld",
442 		    __func__, msg->sm_host, msg->sm_port, msg->sm_version + 1,
443 		    msg->sm_community, snmpe_pdutype2string(msg->sm_pdutype),
444 		    msg->sm_request);
445 
446 	return (0);
447 
448  parsefail:
449 	stats->snmp_inasnparseerrs++;
450  fail:
451 	print_host(ss, msg->sm_host, sizeof(msg->sm_host));
452 	log_debug("%s: %s:%hd: %s", __func__, msg->sm_host, msg->sm_port,
453 	    msg->sm_errstr);
454 	return (-1);
455 }
456 
457 int
458 snmpe_parsevarbinds(struct snmp_message *msg)
459 {
460 	struct snmp_stats	*stats = &snmpd_env->sc_stats;
461 	struct ber_element	*varbind, *value, *rvarbind = NULL;
462 	struct ber_element	*pvarbind = NULL, *end;
463 	char			 buf[BUFSIZ];
464 	struct ber_oid		 o;
465 	int			 i;
466 
467 	msg->sm_errstr = "invalid varbind element";
468 
469 	varbind = msg->sm_varbind;
470 	msg->sm_varbindresp = NULL;
471 	end = NULL;
472 
473 	for (i = 1; varbind != NULL && i < SNMPD_MAXVARBIND;
474 	    varbind = varbind->be_next, i++) {
475 		if (ober_scanf_elements(varbind, "{oeS$}", &o, &value) == -1) {
476 			stats->snmp_inasnparseerrs++;
477 			msg->sm_errstr = "invalid varbind";
478 			goto varfail;
479 		}
480 		if (o.bo_n < BER_MIN_OID_LEN || o.bo_n > BER_MAX_OID_LEN)
481 			goto varfail;
482 
483 		log_debug("%s: %s:%hd: oid %s", __func__, msg->sm_host,
484 		    msg->sm_port, smi_oid2string(&o, buf, sizeof(buf), 0));
485 
486 		/*
487 		 * XXX intotalreqvars should only be incremented after all are
488 		 * succeeded
489 		 */
490 		switch (msg->sm_pdutype) {
491 		case SNMP_C_GETNEXTREQ:
492 			if ((rvarbind = ober_add_sequence(NULL)) == NULL)
493 				goto varfail;
494 			if (mps_getnextreq(msg, rvarbind, &o) != 0) {
495 				msg->sm_error = SNMP_ERROR_NOSUCHNAME;
496 				ober_free_elements(rvarbind);
497 				goto varfail;
498 			}
499 			stats->snmp_intotalreqvars++;
500 			break;
501 		case SNMP_C_GETREQ:
502 			if ((rvarbind = ober_add_sequence(NULL)) == NULL)
503 				goto varfail;
504 			if (mps_getreq(msg, rvarbind, &o,
505 			    msg->sm_version) != 0) {
506 				msg->sm_error = SNMP_ERROR_NOSUCHNAME;
507 				ober_free_elements(rvarbind);
508 				goto varfail;
509 			}
510 			stats->snmp_intotalreqvars++;
511 			break;
512 		case SNMP_C_SETREQ:
513 			/*
514 			 * XXX A set varbind should only be committed if
515 			 * all variables are staged
516 			 */
517 			if (mps_setreq(msg, value, &o) == 0) {
518 				stats->snmp_intotalsetvars++;
519 				break;
520 			}
521 			msg->sm_error = SNMP_ERROR_READONLY;
522 			goto varfail;
523 		case SNMP_C_GETBULKREQ:
524 			rvarbind = NULL;
525 			if (mps_getbulkreq(msg, &rvarbind, &end, &o,
526 			    (i <= msg->sm_nonrepeaters)
527 			    ? 1 : msg->sm_maxrepetitions) != 0) {
528 				msg->sm_error = SNMP_ERROR_NOSUCHNAME;
529 				goto varfail;
530 			}
531 			/*
532 			 * XXX This should be the amount of returned
533 			 * vars
534 			 */
535 			stats->snmp_intotalreqvars++;
536 			break;
537 
538 		default:
539 			goto varfail;
540 		}
541 		if (rvarbind == NULL)
542 			break;
543 		if (pvarbind == NULL)
544 			msg->sm_varbindresp = rvarbind;
545 		else
546 			ober_link_elements(pvarbind, rvarbind);
547 		pvarbind = end == NULL ? rvarbind : end;
548 	}
549 
550 	msg->sm_errstr = "none";
551 	msg->sm_error = 0;
552 	msg->sm_errorindex = 0;
553 
554 	return 0;
555  varfail:
556 	log_debug("%s: %s:%hd: %s, error index %d", __func__,
557 	    msg->sm_host, msg->sm_port, msg->sm_errstr, i);
558 	if (msg->sm_error == 0)
559 		msg->sm_error = SNMP_ERROR_GENERR;
560 	msg->sm_errorindex = i;
561 	return -1;
562 }
563 
564 void
565 snmpe_acceptcb(int fd, short type, void *arg)
566 {
567 	struct address		*h = arg;
568 	struct sockaddr_storage	 ss;
569 	socklen_t		 len = sizeof(ss);
570 	struct snmp_message	*msg;
571 	int afd;
572 
573 	event_add(&h->ev, NULL);
574 	if ((type & EV_TIMEOUT))
575 		return;
576 
577 	if ((afd = accept4(fd, (struct sockaddr *)&ss, &len,
578 	    SOCK_NONBLOCK|SOCK_CLOEXEC)) < 0) {
579 		/* Pause accept if we are out of file descriptors  */
580 		if (errno == ENFILE || errno == EMFILE) {
581 			struct timeval evtpause = { 1, 0 };
582 
583 			event_del(&h->ev);
584 			evtimer_add(&h->evt, &evtpause);
585 		} else if (errno != EAGAIN && errno != EINTR)
586 			log_debug("%s: accept4", __func__);
587 		return;
588 	}
589 	if ((msg = calloc(1, sizeof(*msg))) == NULL)
590 		goto fail;
591 
592 	memcpy(&(msg->sm_ss), &ss, len);
593 	msg->sm_slen = len;
594 	msg->sm_aflags = h->flags;
595 	msg->sm_port = h->port;
596 	snmpe_prepare_read(msg, afd);
597 	return;
598 fail:
599 	free(msg);
600 	close(afd);
601 	return;
602 }
603 
604 void
605 snmpe_prepare_read(struct snmp_message *msg, int fd)
606 {
607 	msg->sm_sock = fd;
608 	msg->sm_sock_tcp = 1;
609 	event_del(&msg->sm_sockev);
610 	event_set(&msg->sm_sockev, fd, EV_READ,
611 	    snmpe_readcb, msg);
612 	event_add(&msg->sm_sockev, &snmpe_tcp_timeout);
613 }
614 
615 void
616 snmpe_tryparse(int fd, struct snmp_message *msg)
617 {
618 	struct snmp_stats	*stats = &snmpd_env->sc_stats;
619 
620 	ober_set_application(&msg->sm_ber, smi_application);
621 	ober_set_readbuf(&msg->sm_ber, msg->sm_data, msg->sm_datalen);
622 	msg->sm_req = ober_read_elements(&msg->sm_ber, NULL);
623 	if (msg->sm_req == NULL) {
624 		if (errno == ECANCELED) {
625 			/* short read; try again */
626 			snmpe_prepare_read(msg, fd);
627 			return;
628 		}
629 		goto fail;
630 	}
631 
632 	if (snmpe_parse(msg) == -1) {
633 		if (msg->sm_usmerr && MSG_REPORT(msg)) {
634 			usm_make_report(msg);
635 			snmpe_response(msg);
636 			return;
637 		} else
638 			goto fail;
639 	}
640 	stats->snmp_inpkts++;
641 
642 	snmpe_dispatchmsg(msg);
643 	return;
644  fail:
645 	snmp_msgfree(msg);
646 	close(fd);
647 }
648 
649 void
650 snmpe_readcb(int fd, short type, void *arg)
651 {
652 	struct snmp_message *msg = arg;
653 	ssize_t len;
654 
655 	if (type == EV_TIMEOUT || msg->sm_datalen >= sizeof(msg->sm_data))
656 		goto fail;
657 
658 	len = read(fd, msg->sm_data + msg->sm_datalen,
659 	    sizeof(msg->sm_data) - msg->sm_datalen);
660 	if (len <= 0) {
661 		if (errno != EAGAIN && errno != EINTR)
662 			goto fail;
663 		snmpe_prepare_read(msg, fd);
664 		return;
665 	}
666 
667 	msg->sm_datalen += (size_t)len;
668 	snmpe_tryparse(fd, msg);
669 	return;
670 
671  fail:
672 	snmp_msgfree(msg);
673 	close(fd);
674 }
675 
676 void
677 snmpe_writecb(int fd, short type, void *arg)
678 {
679 	struct snmp_stats	*stats = &snmpd_env->sc_stats;
680 	struct snmp_message	*msg = arg;
681 	struct snmp_message	*nmsg;
682 	ssize_t			 len;
683 	size_t			 reqlen;
684 	struct ber		*ber = &msg->sm_ber;
685 
686 	if (type == EV_TIMEOUT)
687 		goto fail;
688 
689 	len = ber->br_wend - ber->br_wbuf;
690 	ber->br_wptr = ber->br_wbuf;
691 
692 	log_debug("%s: write fd %d len %zd", __func__, fd, len);
693 
694 	len = write(fd, ber->br_wptr, len);
695 	if (len == -1) {
696 		if (errno == EAGAIN || errno == EINTR)
697 			return;
698 		else
699 			goto fail;
700 	}
701 
702 	ber->br_wptr += len;
703 
704 	if (ber->br_wptr < ber->br_wend) {
705 		event_del(&msg->sm_sockev);
706 		event_set(&msg->sm_sockev, msg->sm_sock, EV_WRITE,
707 		    snmpe_writecb, msg);
708 		event_add(&msg->sm_sockev, &snmpe_tcp_timeout);
709 		return;
710 	}
711 
712 	stats->snmp_outpkts++;
713 
714 	if ((nmsg = calloc(1, sizeof(*nmsg))) == NULL)
715 		goto fail;
716 	memcpy(&(nmsg->sm_ss), &(msg->sm_ss), msg->sm_slen);
717 	nmsg->sm_slen = msg->sm_slen;
718 	nmsg->sm_aflags = msg->sm_aflags;
719 	nmsg->sm_port = msg->sm_port;
720 
721 	/*
722 	 * Reuse the connection.
723 	 * In case we already read data of the next message, copy it over.
724 	 */
725 	reqlen = ober_calc_len(msg->sm_req);
726 	if (msg->sm_datalen > reqlen) {
727 		memcpy(nmsg->sm_data, msg->sm_data + reqlen,
728 		    msg->sm_datalen - reqlen);
729 		nmsg->sm_datalen = msg->sm_datalen - reqlen;
730 		snmp_msgfree(msg);
731 		snmpe_prepare_read(nmsg, fd);
732 		snmpe_tryparse(fd, nmsg);
733 	} else {
734 		snmp_msgfree(msg);
735 		snmpe_prepare_read(nmsg, fd);
736 	}
737 	return;
738 
739  fail:
740 	close(fd);
741 	snmp_msgfree(msg);
742 }
743 
744 void
745 snmpe_recvmsg(int fd, short sig, void *arg)
746 {
747 	struct address		*h = arg;
748 	struct snmp_stats	*stats = &snmpd_env->sc_stats;
749 	ssize_t			 len;
750 	struct snmp_message	*msg;
751 
752 	if ((msg = calloc(1, sizeof(*msg))) == NULL)
753 		return;
754 
755 	msg->sm_aflags = h->flags;
756 	msg->sm_sock = fd;
757 	msg->sm_slen = sizeof(msg->sm_ss);
758 	msg->sm_port = h->port;
759 	if ((len = recvfromto(fd, msg->sm_data, sizeof(msg->sm_data), 0,
760 	    (struct sockaddr *)&msg->sm_ss, &msg->sm_slen,
761 	    (struct sockaddr *)&msg->sm_local_ss, &msg->sm_local_slen)) < 1) {
762 		free(msg);
763 		return;
764 	}
765 
766 	stats->snmp_inpkts++;
767 	msg->sm_datalen = (size_t)len;
768 
769 	bzero(&msg->sm_ber, sizeof(msg->sm_ber));
770 	ober_set_application(&msg->sm_ber, smi_application);
771 	ober_set_readbuf(&msg->sm_ber, msg->sm_data, msg->sm_datalen);
772 
773 	msg->sm_req = ober_read_elements(&msg->sm_ber, NULL);
774 	if (msg->sm_req == NULL) {
775 		stats->snmp_inasnparseerrs++;
776 		snmp_msgfree(msg);
777 		return;
778 	}
779 
780 #ifdef DEBUG
781 	fprintf(stderr, "recv msg:\n");
782 	smi_debug_elements(msg->sm_req);
783 #endif
784 
785 	if (snmpe_parse(msg) == -1) {
786 		if (msg->sm_usmerr != 0 && MSG_REPORT(msg)) {
787 			usm_make_report(msg);
788 			snmpe_response(msg);
789 			return;
790 		} else {
791 			snmp_msgfree(msg);
792 			return;
793 		}
794 	}
795 
796 	snmpe_dispatchmsg(msg);
797 }
798 
799 void
800 snmpe_dispatchmsg(struct snmp_message *msg)
801 {
802 	if (msg->sm_pdutype == SNMP_C_TRAP ||
803 	    msg->sm_pdutype == SNMP_C_TRAPV2) {
804 		snmp_msgfree(msg);
805 		return;
806 	}
807 	/* dispatched to subagent */
808 	/* XXX Do proper error handling */
809 	(void) snmpe_parsevarbinds(msg);
810 
811 	/* respond directly */
812 	msg->sm_pdutype = SNMP_C_GETRESP;
813 	snmpe_response(msg);
814 }
815 
816 void
817 snmpe_response(struct snmp_message *msg)
818 {
819 	struct snmp_stats	*stats = &snmpd_env->sc_stats;
820 	u_int8_t		*ptr = NULL;
821 	ssize_t			 len;
822 
823 	if (msg->sm_varbindresp == NULL && msg->sm_pduend != NULL)
824 		msg->sm_varbindresp = ober_unlink_elements(msg->sm_pduend);
825 
826 	switch (msg->sm_error) {
827 	case SNMP_ERROR_NONE:
828 		break;
829 	case SNMP_ERROR_TOOBIG:
830 		stats->snmp_intoobigs++;
831 		break;
832 	case SNMP_ERROR_NOSUCHNAME:
833 		stats->snmp_innosuchnames++;
834 		break;
835 	case SNMP_ERROR_BADVALUE:
836 		stats->snmp_inbadvalues++;
837 		break;
838 	case SNMP_ERROR_READONLY:
839 		stats->snmp_inreadonlys++;
840 		break;
841 	case SNMP_ERROR_GENERR:
842 	default:
843 		stats->snmp_ingenerrs++;
844 		break;
845 	}
846 
847 	/* Create new SNMP packet */
848 	if (snmpe_encode(msg) < 0)
849 		goto done;
850 
851 	len = ober_write_elements(&msg->sm_ber, msg->sm_resp);
852 	if (ober_get_writebuf(&msg->sm_ber, (void *)&ptr) == -1)
853 		goto done;
854 
855 	usm_finalize_digest(msg, ptr, len);
856 	if (msg->sm_sock_tcp) {
857 		event_del(&msg->sm_sockev);
858 		event_set(&msg->sm_sockev, msg->sm_sock, EV_WRITE,
859 		    snmpe_writecb, msg);
860 		event_add(&msg->sm_sockev, &snmpe_tcp_timeout);
861 		return;
862 	} else {
863 		len = sendtofrom(msg->sm_sock, ptr, len, 0,
864 		    (struct sockaddr *)&msg->sm_ss, msg->sm_slen,
865 		    (struct sockaddr *)&msg->sm_local_ss, msg->sm_local_slen);
866 		if (len != -1)
867 			stats->snmp_outpkts++;
868 	}
869 
870  done:
871 	snmp_msgfree(msg);
872 }
873 
874 void
875 snmp_msgfree(struct snmp_message *msg)
876 {
877 	if (msg->sm_transactionid != 0)
878 		RB_REMOVE(snmp_messages, &snmp_messages, msg);
879 	event_del(&msg->sm_sockev);
880 	ober_free(&msg->sm_ber);
881 	if (msg->sm_req != NULL)
882 		ober_free_elements(msg->sm_req);
883 	if (msg->sm_resp != NULL)
884 		ober_free_elements(msg->sm_resp);
885 	free(msg);
886 }
887 
888 int
889 snmpe_encode(struct snmp_message *msg)
890 {
891 	struct ber_element	*ehdr;
892 	struct ber_element	*pdu, *epdu;
893 
894 	msg->sm_resp = ober_add_sequence(NULL);
895 	if ((ehdr = ober_add_integer(msg->sm_resp, msg->sm_version)) == NULL)
896 		return -1;
897 	if (msg->sm_version == SNMP_V3) {
898 		char	f = MSG_SECLEVEL(msg);
899 
900 		if ((ehdr = ober_printf_elements(ehdr, "{iixi}", msg->sm_msgid,
901 		    msg->sm_max_msg_size, &f, sizeof(f),
902 		    msg->sm_secmodel)) == NULL)
903 			return -1;
904 
905 		/* XXX currently only USM supported */
906 		if ((ehdr = usm_encode(msg, ehdr)) == NULL)
907 			return -1;
908 	} else {
909 		if ((ehdr = ober_add_string(ehdr, msg->sm_community)) == NULL)
910 			return -1;
911 	}
912 
913 	pdu = epdu = ober_add_sequence(NULL);
914 	if (msg->sm_version == SNMP_V3) {
915 		if ((epdu = ober_printf_elements(epdu, "xs{",
916 		    snmpd_env->sc_engineid, snmpd_env->sc_engineid_len,
917 		    msg->sm_ctxname)) == NULL) {
918 			ober_free_elements(pdu);
919 			return -1;
920 		}
921 	}
922 
923 	if (!ober_printf_elements(epdu, "tiii{e}", BER_CLASS_CONTEXT,
924 	    msg->sm_pdutype, msg->sm_request,
925 	    msg->sm_error, msg->sm_errorindex,
926 	    msg->sm_varbindresp)) {
927 		ober_free_elements(pdu);
928 		return -1;
929 	}
930 
931 	if (MSG_HAS_PRIV(msg))
932 		pdu = usm_encrypt(msg, pdu);
933 	ober_link_elements(ehdr, pdu);
934 
935 #ifdef DEBUG
936 	fprintf(stderr, "resp msg:\n");
937 	smi_debug_elements(msg->sm_resp);
938 #endif
939 	return 0;
940 }
941 
942 int
943 snmp_messagecmp(struct snmp_message *m1, struct snmp_message *m2)
944 {
945 	return (m1->sm_transactionid < m2->sm_transactionid ? -1 :
946 	    m1->sm_transactionid > m2->sm_transactionid);
947 }
948 
949 RB_GENERATE(snmp_messages, snmp_message, sm_entry, snmp_messagecmp)
950