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