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