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