xref: /openbsd/usr.sbin/snmpd/snmpe.c (revision 3d8817e4)
1 /*	$OpenBSD: snmpe.c,v 1.28 2010/09/20 12:32:41 martinh Exp $	*/
2 
3 /*
4  * Copyright (c) 2007, 2008 Reyk Floeter <reyk@vantronix.net>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/queue.h>
20 #include <sys/param.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 #include <vis.h>
40 
41 #include "snmpd.h"
42 
43 int	 snmpe_parse(struct sockaddr_storage *,
44 	    struct ber_element *, struct snmp_message *);
45 unsigned long
46 	 snmpe_application(struct ber_element *);
47 void	 snmpe_sig_handler(int sig, short, void *);
48 void	 snmpe_shutdown(void);
49 void	 snmpe_dispatch_parent(int, short, void *);
50 int	 snmpe_bind(struct address *);
51 void	 snmpe_recvmsg(int fd, short, void *);
52 
53 struct snmpd	*env = NULL;
54 
55 struct imsgev	*iev_parent;
56 
57 void
58 snmpe_sig_handler(int sig, short event, void *arg)
59 {
60 	switch (sig) {
61 	case SIGINT:
62 	case SIGTERM:
63 		snmpe_shutdown();
64 		break;
65 	default:
66 		fatalx("snmpe_sig_handler: unexpected signal");
67 	}
68 }
69 
70 pid_t
71 snmpe(struct snmpd *x_env, int pipe_parent2snmpe[2])
72 {
73 	pid_t		 pid;
74 	struct passwd	*pw;
75 	struct event	 ev_sigint;
76 	struct event	 ev_sigterm;
77 #ifdef DEBUG
78 	struct oid	*oid;
79 #endif
80 
81 	switch (pid = fork()) {
82 	case -1:
83 		fatal("snmpe: cannot fork");
84 	case 0:
85 		break;
86 	default:
87 		return (pid);
88 	}
89 
90 	env = x_env;
91 
92 	if (control_init(&env->sc_csock) == -1)
93 		fatalx("snmpe: control socket setup failed");
94 	if (control_init(&env->sc_rcsock) == -1)
95 		fatalx("snmpe: restricted control socket setup failed");
96 
97 	if ((env->sc_sock = snmpe_bind(&env->sc_address)) == -1)
98 		fatalx("snmpe: failed to bind SNMP UDP socket");
99 
100 	if ((pw = getpwnam(SNMPD_USER)) == NULL)
101 		fatal("snmpe: getpwnam");
102 
103 #ifndef DEBUG
104 	if (chroot(pw->pw_dir) == -1)
105 		fatal("snmpe: chroot");
106 	if (chdir("/") == -1)
107 		fatal("snmpe: chdir(\"/\")");
108 #else
109 #warning disabling privilege revocation and chroot in DEBUG mode
110 #endif
111 
112 	setproctitle("snmp engine");
113 	snmpd_process = PROC_SNMPE;
114 
115 #ifndef DEBUG
116 	if (setgroups(1, &pw->pw_gid) ||
117 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
118 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
119 		fatal("snmpe: cannot drop privileges");
120 #endif
121 
122 #ifdef DEBUG
123 	for (oid = NULL; (oid = smi_foreach(oid, 0)) != NULL;) {
124 		char	 buf[BUFSIZ];
125 		smi_oidstring(&oid->o_id, buf, sizeof(buf));
126 		log_debug("oid %s", buf);
127 	}
128 #endif
129 
130 	event_init();
131 
132 	signal_set(&ev_sigint, SIGINT, snmpe_sig_handler, NULL);
133 	signal_set(&ev_sigterm, SIGTERM, snmpe_sig_handler, NULL);
134 	signal_add(&ev_sigint, NULL);
135 	signal_add(&ev_sigterm, NULL);
136 	signal(SIGPIPE, SIG_IGN);
137 	signal(SIGHUP, SIG_IGN);
138 
139 	close(pipe_parent2snmpe[0]);
140 
141 	if ((iev_parent = calloc(1, sizeof(struct imsgev))) == NULL)
142 		fatal("snmpe");
143 
144 	imsg_init(&iev_parent->ibuf, pipe_parent2snmpe[1]);
145 	iev_parent->handler = snmpe_dispatch_parent;
146 
147 	iev_parent->events = EV_READ;
148 	event_set(&iev_parent->ev, iev_parent->ibuf.fd, iev_parent->events,
149 	    iev_parent->handler, iev_parent);
150 	event_add(&iev_parent->ev, NULL);
151 
152 	TAILQ_INIT(&ctl_conns);
153 
154 	if (control_listen(&env->sc_csock) == -1)
155 		fatalx("snmpe: control socket listen failed");
156 	if (control_listen(&env->sc_rcsock) == -1)
157 		fatalx("snmpe: restricted control socket listen failed");
158 
159 	event_set(&env->sc_ev, env->sc_sock, EV_READ|EV_PERSIST,
160 	    snmpe_recvmsg, env);
161 	event_add(&env->sc_ev, NULL);
162 
163 	kr_init();
164 	trap_init();
165 	timer_init();
166 
167 	event_dispatch();
168 
169 	snmpe_shutdown();
170 	kr_shutdown();
171 
172 	return (0);
173 }
174 
175 void
176 snmpe_shutdown(void)
177 {
178 	log_info("snmp engine exiting");
179 	_exit(0);
180 }
181 
182 void
183 snmpe_dispatch_parent(int fd, short event, void * ptr)
184 {
185 	struct imsgev	*iev;
186 	struct imsgbuf	*ibuf;
187 	struct imsg	 imsg;
188 	ssize_t		 n;
189 
190 	iev = ptr;
191 	ibuf = &iev->ibuf;
192 	switch (event) {
193 	case EV_READ:
194 		if ((n = imsg_read(ibuf)) == -1)
195 			fatal("imsg_read error");
196 		if (n == 0) {
197 			/* this pipe is dead, so remove the event handler */
198 			event_del(&iev->ev);
199 			event_loopexit(NULL);
200 			return;
201 		}
202 		break;
203 	case EV_WRITE:
204 		if (msgbuf_write(&ibuf->w) == -1)
205 			fatal("msgbuf_write");
206 		imsg_event_add(iev);
207 		return;
208 	default:
209 		fatalx("snmpe_dispatch_parent: unknown event");
210 	}
211 
212 	for (;;) {
213 		if ((n = imsg_get(ibuf, &imsg)) == -1)
214 			fatal("snmpe_dispatch_parent: imsg_read error");
215 		if (n == 0)
216 			break;
217 
218 		switch (imsg.hdr.type) {
219 		default:
220 			log_debug("snmpe_dispatch_parent: unexpected imsg %d",
221 			    imsg.hdr.type);
222 			break;
223 		}
224 		imsg_free(&imsg);
225 	}
226 	imsg_event_add(iev);
227 }
228 
229 int
230 snmpe_bind(struct address *addr)
231 {
232 	char	 buf[512];
233 	int	 s;
234 
235 	if ((s = snmpd_socket_af(&addr->ss, htons(addr->port))) == -1)
236 		return (-1);
237 
238 	/*
239 	 * Socket options
240 	 */
241 	if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
242 		goto bad;
243 
244 	if (bind(s, (struct sockaddr *)&addr->ss, addr->ss.ss_len) == -1)
245 		goto bad;
246 
247 	if (print_host(&addr->ss, buf, sizeof(buf)) == NULL)
248 		goto bad;
249 
250 	log_info("snmpe_bind: binding to address %s:%d", buf, addr->port);
251 
252 	return (s);
253 
254  bad:
255 	close(s);
256 	return (-1);
257 }
258 
259 #ifdef DEBUG
260 void
261 snmpe_debug_elements(struct ber_element *root)
262 {
263 	static int	 indent = 0;
264 	long long	 v;
265 	int		 d;
266 	char		*buf;
267 	size_t		 len;
268 	u_int		 i;
269 	int		 constructed;
270 	struct ber_oid	 o;
271 	char		 str[BUFSIZ];
272 
273 	/* calculate lengths */
274 	ber_calc_len(root);
275 
276 	switch (root->be_encoding) {
277 	case BER_TYPE_SEQUENCE:
278 	case BER_TYPE_SET:
279 		constructed = root->be_encoding;
280 		break;
281 	default:
282 		constructed = 0;
283 		break;
284 	}
285 
286 	fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
287 	switch (root->be_class) {
288 	case BER_CLASS_UNIVERSAL:
289 		fprintf(stderr, "class: universal(%u) type: ", root->be_class);
290 		switch (root->be_type) {
291 		case BER_TYPE_EOC:
292 			fprintf(stderr, "end-of-content");
293 			break;
294 		case BER_TYPE_BOOLEAN:
295 			fprintf(stderr, "boolean");
296 			break;
297 		case BER_TYPE_INTEGER:
298 			fprintf(stderr, "integer");
299 			break;
300 		case BER_TYPE_BITSTRING:
301 			fprintf(stderr, "bit-string");
302 			break;
303 		case BER_TYPE_OCTETSTRING:
304 			fprintf(stderr, "octet-string");
305 			break;
306 		case BER_TYPE_NULL:
307 			fprintf(stderr, "null");
308 			break;
309 		case BER_TYPE_OBJECT:
310 			fprintf(stderr, "object");
311 			break;
312 		case BER_TYPE_ENUMERATED:
313 			fprintf(stderr, "enumerated");
314 			break;
315 		case BER_TYPE_SEQUENCE:
316 			fprintf(stderr, "sequence");
317 			break;
318 		case BER_TYPE_SET:
319 			fprintf(stderr, "set");
320 			break;
321 		}
322 		break;
323 	case BER_CLASS_APPLICATION:
324 		fprintf(stderr, "class: application(%u) type: ",
325 		    root->be_class);
326 		switch (root->be_type) {
327 		case SNMP_T_IPADDR:
328 			fprintf(stderr, "ipaddr");
329 			break;
330 		case SNMP_T_COUNTER32:
331 			fprintf(stderr, "counter32");
332 			break;
333 		case SNMP_T_GAUGE32:
334 			fprintf(stderr, "gauge32");
335 			break;
336 		case SNMP_T_TIMETICKS:
337 			fprintf(stderr, "timeticks");
338 			break;
339 		case SNMP_T_OPAQUE:
340 			fprintf(stderr, "opaque");
341 			break;
342 		case SNMP_T_COUNTER64:
343 			fprintf(stderr, "counter64");
344 			break;
345 		}
346 		break;
347 	case BER_CLASS_CONTEXT:
348 		fprintf(stderr, "class: context(%u) type: ",
349 		    root->be_class);
350 		switch (root->be_type) {
351 		case SNMP_C_GETREQ:
352 			fprintf(stderr, "getreq");
353 			break;
354 		case SNMP_C_GETNEXTREQ:
355 			fprintf(stderr, "nextreq");
356 			break;
357 		case SNMP_C_GETRESP:
358 			fprintf(stderr, "getresp");
359 			break;
360 		case SNMP_C_SETREQ:
361 			fprintf(stderr, "setreq");
362 			break;
363 		case SNMP_C_TRAP:
364 			fprintf(stderr, "trap");
365 			break;
366 		case SNMP_C_GETBULKREQ:
367 			fprintf(stderr, "getbulkreq");
368 			break;
369 		case SNMP_C_INFORMREQ:
370 			fprintf(stderr, "informreq");
371 			break;
372 		case SNMP_C_TRAPV2:
373 			fprintf(stderr, "trapv2");
374 			break;
375 		case SNMP_C_REPORT:
376 			fprintf(stderr, "report");
377 			break;
378 		}
379 		break;
380 	case BER_CLASS_PRIVATE:
381 		fprintf(stderr, "class: private(%u) type: ", root->be_class);
382 		break;
383 	default:
384 		fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
385 		break;
386 	}
387 	fprintf(stderr, "(%lu) encoding %lu ",
388 	    root->be_type, root->be_encoding);
389 
390 	if (constructed)
391 		root->be_encoding = constructed;
392 
393 	switch (root->be_encoding) {
394 	case BER_TYPE_BOOLEAN:
395 		if (ber_get_boolean(root, &d) == -1) {
396 			fprintf(stderr, "<INVALID>\n");
397 			break;
398 		}
399 		fprintf(stderr, "%s(%d)\n", d ? "true" : "false", d);
400 		break;
401 	case BER_TYPE_INTEGER:
402 	case BER_TYPE_ENUMERATED:
403 		if (ber_get_integer(root, &v) == -1) {
404 			fprintf(stderr, "<INVALID>\n");
405 			break;
406 		}
407 		fprintf(stderr, "value %lld\n", v);
408 		break;
409 	case BER_TYPE_BITSTRING:
410 		if (ber_get_bitstring(root, (void *)&buf, &len) == -1) {
411 			fprintf(stderr, "<INVALID>\n");
412 			break;
413 		}
414 		fprintf(stderr, "hexdump ");
415 		for (i = 0; i < len; i++)
416 			fprintf(stderr, "%02x", buf[i]);
417 		fprintf(stderr, "\n");
418 		break;
419 	case BER_TYPE_OBJECT:
420 		if (ber_get_oid(root, &o) == -1) {
421 			fprintf(stderr, "<INVALID>\n");
422 			break;
423 		}
424 		fprintf(stderr, "oid %s",
425 		    smi_oidstring(&o, str, sizeof(str)));
426 		fprintf(stderr, "\n");
427 		break;
428 	case BER_TYPE_OCTETSTRING:
429 		if (ber_get_string(root, &buf) == -1) {
430 			fprintf(stderr, "<INVALID>\n");
431 			break;
432 		}
433 		if (root->be_class == BER_CLASS_APPLICATION &&
434 		    root->be_type == SNMP_T_IPADDR) {
435 			fprintf(stderr, "addr %s\n",
436 			    inet_ntoa(*(struct in_addr *)buf));
437 		} else {
438 			char *visbuf;
439 			if ((visbuf = malloc(root->be_len * 4 + 1)) == NULL)
440 				fatal("malloc");
441 			strvisx(visbuf, buf, root->be_len, 0);
442 			fprintf(stderr, "string \"%s\"\n",  visbuf);
443 			free(visbuf);
444 		}
445 		break;
446 	case BER_TYPE_NULL:	/* no payload */
447 	case BER_TYPE_EOC:
448 	case BER_TYPE_SEQUENCE:
449 	case BER_TYPE_SET:
450 	default:
451 		fprintf(stderr, "\n");
452 		break;
453 	}
454 
455 	if (constructed && root->be_sub) {
456 		indent += 2;
457 		snmpe_debug_elements(root->be_sub);
458 		indent -= 2;
459 	}
460 	if (root->be_next)
461 		snmpe_debug_elements(root->be_next);
462 }
463 #endif
464 
465 unsigned long
466 snmpe_application(struct ber_element *elm)
467 {
468 	if (elm->be_class != BER_CLASS_APPLICATION)
469 		return (BER_TYPE_OCTETSTRING);
470 
471 	switch (elm->be_type) {
472 	case SNMP_T_IPADDR:
473 		return (BER_TYPE_OCTETSTRING);
474 	case SNMP_T_COUNTER32:
475 	case SNMP_T_GAUGE32:
476 	case SNMP_T_TIMETICKS:
477 	case SNMP_T_OPAQUE:
478 	case SNMP_T_COUNTER64:
479 		return (BER_TYPE_INTEGER);
480 	default:
481 		break;
482 	}
483 	return (BER_TYPE_OCTETSTRING);
484 }
485 
486 int
487 snmpe_parse(struct sockaddr_storage *ss,
488     struct ber_element *root, struct snmp_message *msg)
489 {
490 	struct snmp_stats	*stats = &env->sc_stats;
491 	struct ber_element	*a, *b, *c, *d, *e, *f, *next, *last;
492 	const char		*errstr = "invalid message";
493 	long long		 ver, req;
494 	long long		 errval, erridx;
495 	unsigned long		 type;
496 	u_int			 class, state, i = 0, j = 0;
497 	char			*comn, buf[BUFSIZ], host[MAXHOSTNAMELEN];
498 	struct ber_oid		 o;
499 	size_t			 len;
500 
501 	bzero(msg, sizeof(*msg));
502 
503 	if (ber_scanf_elements(root, "e{ieset{e",
504 	    &msg->sm_header, &ver, &msg->sm_headerend, &comn,
505 	    &msg->sm_pdu, &class, &type, &a) != 0)
506 		goto parsefail;
507 
508 	/* SNMP version and community */
509 	switch (ver) {
510 	case SNMP_V1:
511 	case SNMP_V2:
512 		msg->sm_version = ver;
513 		break;
514 	case SNMP_V3:
515 	default:
516 		stats->snmp_inbadversions++;
517 		errstr = "bad snmp version";
518 		goto fail;
519 	}
520 
521 	/* SNMP PDU context */
522 	if (class != BER_CLASS_CONTEXT)
523 		goto parsefail;
524 	switch (type) {
525 	case SNMP_C_GETBULKREQ:
526 		if (msg->sm_version == SNMP_V1) {
527 			stats->snmp_inbadversions++;
528 			errstr = "invalid request for protocol version 1";
529 			goto fail;
530 		}
531 		/* FALLTHROUGH */
532 	case SNMP_C_GETREQ:
533 		stats->snmp_ingetrequests++;
534 		/* FALLTHROUGH */
535 	case SNMP_C_GETNEXTREQ:
536 		if (type == SNMP_C_GETNEXTREQ)
537 			stats->snmp_ingetnexts++;
538 		if (strcmp(env->sc_rdcommunity, comn) != 0 &&
539 		    strcmp(env->sc_rwcommunity, comn) != 0) {
540 			stats->snmp_inbadcommunitynames++;
541 			errstr = "wrong read community";
542 			goto fail;
543 		}
544 		msg->sm_context = type;
545 		break;
546 	case SNMP_C_SETREQ:
547 		stats->snmp_insetrequests++;
548 		if (strcmp(env->sc_rwcommunity, comn) != 0) {
549 			if (strcmp(env->sc_rdcommunity, comn) != 0)
550 				stats->snmp_inbadcommunitynames++;
551 			else
552 				stats->snmp_inbadcommunityuses++;
553 			errstr = "wrong write community";
554 			goto fail;
555 		}
556 		msg->sm_context = type;
557 		break;
558 	case SNMP_C_GETRESP:
559 		stats->snmp_ingetresponses++;
560 		errstr = "response without request";
561 		goto parsefail;
562 	case SNMP_C_TRAP:
563 	case SNMP_C_TRAPV2:
564 		if (strcmp(env->sc_trcommunity, comn) != 0) {
565 			stats->snmp_inbadcommunitynames++;
566 			errstr = "wrong trap community";
567 			goto fail;
568 		}
569 		stats->snmp_intraps++;
570 		errstr = "received trap";
571 		goto fail;
572 	default:
573 		errstr = "invalid context";
574 		goto parsefail;
575 	}
576 
577 	if (strlcpy(msg->sm_community, comn, sizeof(msg->sm_community)) >=
578 	    sizeof(msg->sm_community)) {
579 		stats->snmp_inbadcommunitynames++;
580 		errstr = "community name too long";
581 		goto fail;
582 	}
583 
584 	/* SNMP PDU */
585 	if (ber_scanf_elements(a, "iiie{et",
586 	    &req, &errval, &erridx, &msg->sm_pduend,
587 	    &msg->sm_varbind, &class, &type) != 0) {
588 		stats->snmp_silentdrops++;
589 		errstr = "invalid PDU";
590 		goto fail;
591 	}
592 	if (class != BER_CLASS_UNIVERSAL || type != BER_TYPE_SEQUENCE) {
593 		stats->snmp_silentdrops++;
594 		errstr = "invalid varbind";
595 		goto fail;
596 	}
597 
598 	msg->sm_request = req;
599 	msg->sm_error = errval;
600 	msg->sm_errorindex = erridx;
601 
602 	print_host(ss, host, sizeof(host));
603 	log_debug("snmpe_parse: %s: SNMPv%d '%s' context %d request %lld",
604 	    host, msg->sm_version + 1, msg->sm_community, msg->sm_context,
605 	    msg->sm_request);
606 
607 	errstr = "invalid varbind element";
608 	for (i = 1, a = msg->sm_varbind, last = NULL;
609 	    a != NULL && i < SNMPD_MAXVARBIND; a = next, i++) {
610 		next = a->be_next;
611 
612 		if (a->be_class != BER_CLASS_UNIVERSAL ||
613 		    a->be_type != BER_TYPE_SEQUENCE)
614 			continue;
615 		if ((b = a->be_sub) == NULL)
616 			continue;
617 		for (state = 0; state < 2 && b != NULL; b = b->be_next) {
618 			switch (state++) {
619 			case 0:
620 				if (ber_get_oid(b, &o) != 0)
621 					goto varfail;
622 				if (o.bo_n < BER_MIN_OID_LEN ||
623 				    o.bo_n > BER_MAX_OID_LEN)
624 					goto varfail;
625 				if (msg->sm_context == SNMP_C_SETREQ)
626 					stats->snmp_intotalsetvars++;
627 				else
628 					stats->snmp_intotalreqvars++;
629 				log_debug("snmpe_parse: %s: oid %s", host,
630 				    smi_oidstring(&o, buf, sizeof(buf)));
631 				break;
632 			case 1:
633 				c = d = NULL;
634 				switch (msg->sm_context) {
635 				case SNMP_C_GETNEXTREQ:
636 					c = ber_add_sequence(NULL);
637 					if ((d = mps_getnextreq(c, &o)) != NULL)
638 						break;
639 					ber_free_elements(c);
640 					c = NULL;
641 					msg->sm_error = SNMP_ERROR_NOSUCHNAME;
642 					msg->sm_errorindex = i;
643 					break;	/* ignore error */
644 				case SNMP_C_GETREQ:
645 					c = ber_add_sequence(NULL);
646 					if ((d = mps_getreq(c, &o,
647 					    msg->sm_version)) != NULL)
648 						break;
649 					msg->sm_error = SNMP_ERROR_NOSUCHNAME;
650 					ber_free_elements(c);
651 					goto varfail;
652 				case SNMP_C_SETREQ:
653 					if (mps_setreq(b, &o) == 0)
654 						break;
655 					msg->sm_error = SNMP_ERROR_READONLY;
656 					goto varfail;
657 				case SNMP_C_GETBULKREQ:
658 					j = msg->sm_maxrepetitions;
659 					msg->sm_errorindex = 0;
660 					msg->sm_error = SNMP_ERROR_NOSUCHNAME;
661 					for (d = NULL, len = 0; j > 0; j--) {
662 						e = ber_add_sequence(NULL);
663 						if (c == NULL)
664 							c = e;
665 						f = mps_getnextreq(e, &o);
666 						if (f == NULL) {
667 							ber_free_elements(e);
668 							if (d == NULL)
669 								goto varfail;
670 							break;
671 						}
672 						len += ber_calc_len(e);
673 						if (len > SNMPD_MAXVARBINDLEN) {
674 							ber_free_elements(e);
675 							break;
676 						}
677 						if (d != NULL)
678 							ber_link_elements(d, e);
679 						d = e;
680 					}
681 					msg->sm_error = 0;
682 					break;
683 				default:
684 					goto varfail;
685 				}
686 				if (c == NULL)
687 					break;
688 				if (last == NULL)
689 					msg->sm_varbindresp = c;
690 				else
691 					ber_link_elements(last, c);
692 				last = c;
693 				break;
694 			}
695 		}
696 		if (state < 2)  {
697 			log_debug("snmpe_parse: state %d", state);
698 			goto varfail;
699 		}
700 	}
701 
702 	return (0);
703  varfail:
704 	log_debug("snmpe_parse: %s: %s, error index %d", host, errstr, i);
705 	if (msg->sm_error == 0)
706 		msg->sm_error = SNMP_ERROR_GENERR;
707 	msg->sm_errorindex = i;
708 	return (0);
709  parsefail:
710 	stats->snmp_inasnparseerrs++;
711  fail:
712 	print_host(ss, host, sizeof(host));
713 	log_debug("snmpe_parse: %s: %s", host, errstr);
714 	return (-1);
715 }
716 
717 void
718 snmpe_recvmsg(int fd, short sig, void *arg)
719 {
720 	struct snmp_stats	*stats = &env->sc_stats;
721 	struct sockaddr_storage	 ss;
722 	u_int8_t		 buf[READ_BUF_SIZE], *ptr = NULL;
723 	socklen_t		 slen;
724 	ssize_t			 len;
725 	struct ber		 ber;
726 	struct ber_element	*req = NULL, *resp = NULL;
727 	struct snmp_message	 msg;
728 
729 	slen = sizeof(ss);
730 	if ((len = recvfrom(fd, buf, sizeof(buf), 0,
731 	    (struct sockaddr *)&ss, &slen)) < 1)
732 		return;
733 
734 	stats->snmp_inpkts++;
735 
736 	bzero(&ber, sizeof(ber));
737 	ber.fd = -1;
738 	ber_set_application(&ber, snmpe_application);
739 	ber_set_readbuf(&ber, buf, len);
740 
741 	req = ber_read_elements(&ber, NULL);
742 
743 	if (req == NULL) {
744 		stats->snmp_inasnparseerrs++;
745 		goto done;
746 	}
747 
748 #ifdef DEBUG
749 	snmpe_debug_elements(req);
750 #endif
751 
752 	if (snmpe_parse(&ss, req, &msg) == -1)
753 		goto done;
754 
755 	if (msg.sm_varbindresp == NULL)
756 		msg.sm_varbindresp = ber_unlink_elements(msg.sm_pduend);
757 
758 	switch (msg.sm_error) {
759 	case SNMP_ERROR_TOOBIG:
760 		stats->snmp_intoobigs++;
761 		break;
762 	case SNMP_ERROR_NOSUCHNAME:
763 		stats->snmp_innosuchnames++;
764 		break;
765 	case SNMP_ERROR_BADVALUE:
766 		stats->snmp_inbadvalues++;
767 		break;
768 	case SNMP_ERROR_READONLY:
769 		stats->snmp_inreadonlys++;
770 		break;
771 	case SNMP_ERROR_GENERR:
772 	default:
773 		stats->snmp_ingenerrs++;
774 		break;
775 	}
776 
777 	/* Create new SNMP packet */
778 	resp = ber_add_sequence(NULL);
779 	ber_printf_elements(resp, "ds{tiii{e}}.",
780 	    msg.sm_version, msg.sm_community,
781 	    BER_CLASS_CONTEXT, SNMP_C_GETRESP,
782 	    msg.sm_request, msg.sm_error, msg.sm_errorindex,
783 	    msg.sm_varbindresp);
784 
785 #ifdef DEBUG
786 	snmpe_debug_elements(resp);
787 #endif
788 
789 	len = ber_write_elements(&ber, resp);
790 	if (ber_get_writebuf(&ber, (void *)&ptr) == -1)
791 		goto done;
792 
793 	len = sendto(fd, ptr, len, 0, (struct sockaddr *)&ss, slen);
794 	if (len != -1)
795 		stats->snmp_outpkts++;
796 
797  done:
798 	ber_free(&ber);
799 	if (req != NULL)
800 		ber_free_elements(req);
801 	if (resp != NULL)
802 		ber_free_elements(resp);
803 }
804