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