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