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