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