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