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