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