1 /*
2  * Copyright (c) 2008, Stefan Walter
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  *     * Redistributions of source code must retain the above
10  *       copyright notice, this list of conditions and the
11  *       following disclaimer.
12  *     * Redistributions in binary form must reproduce the
13  *       above copyright notice, this list of conditions and
14  *       the following disclaimer in the documentation and/or
15  *       other materials provided with the distribution.
16  *     * The names of contributors to this software may not be
17  *       used to endorse or promote products derived from this
18  *       software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31  * DAMAGE.
32  *
33  *
34  * CONTRIBUTORS
35  *  Stef Walter <stef@memberwebs.com>
36  *
37  */
38 
39 #include "usuals.h"
40 
41 #include "async-resolver.h"
42 #include "hash.h"
43 #include "log.h"
44 #include "server-mainloop.h"
45 #include "snmp-engine.h"
46 
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <assert.h>
50 #include <errno.h>
51 #include <unistd.h>
52 #include <syslog.h>
53 #include <err.h>
54 #include <arpa/inet.h>
55 
56 #include <bsnmp/asn1.h>
57 #include <bsnmp/snmp.h>
58 #include <mib/mib-parser.h>
59 
60 struct host;
61 struct request;
62 
63 typedef uint64_t mstime;
64 
65 /* Forward declarations */
66 static void request_release (struct request *req);
67 
68 /* ------------------------------------------------------------------------------
69  * HOSTS
70  */
71 
72 struct host {
73 	/* The hash key is hostname:options:community */
74 	char key[128];
75 
76 	char *hostname;
77 	char *portnum;
78 	char *community;
79 	int version;
80 
81 	mstime interval;
82 
83 	/* Host resolving and book keeping */
84 	struct sockaddr_storage address;
85 	socklen_t address_len;
86 	mstime resolve_interval;
87 	mstime last_resolve_try;
88 	mstime last_resolved;
89 	int is_resolved;
90 	int is_resolving;
91 	int must_resolve;
92 
93 	/* Requests that are queued of this host */
94 	struct request *prepared;
95 
96 	/* Next in list of hosts */
97 	struct host *next;
98 };
99 
100 /* All hosts we've allocated */
101 static struct host *host_list = NULL;
102 
103 /* Hosts hashed by the host:version:community string */
104 static hsh_t *host_by_key = NULL;
105 
106 static void
resolve_cb(int ecode,struct addrinfo * ai,void * arg)107 resolve_cb (int ecode, struct addrinfo *ai, void *arg)
108 {
109 	struct host *host = (struct host*)arg;
110 	host->is_resolving = 0;
111 
112 	if (ecode) {
113 		log_warnx ("couldn't resolve host name: %s: %s",
114 		           host->hostname, gai_strerror (ecode));
115 		return;
116 	}
117 
118 	if (ai->ai_addrlen > sizeof (host->address)) {
119 		log_warnx ("resolved address is too long for host name: %s",
120 		           host->hostname);
121 		return;
122 	}
123 
124 	/* A successful resolve */
125 	memcpy (&host->address, ai->ai_addr, ai->ai_addrlen);
126 	host->address_len = ai->ai_addrlen;
127 	host->last_resolved = server_get_time ();
128 	host->is_resolved = 1;
129 
130 	log_debug ("resolved host: %s", host->hostname);
131 }
132 
133 static void
host_resolve(struct host * host,mstime when)134 host_resolve (struct host *host, mstime when)
135 {
136 	struct addrinfo hints;
137 
138 	if (host->is_resolving)
139 		return;
140 
141 	memset (&hints, 0, sizeof (hints));
142 	hints.ai_family = PF_UNSPEC;
143 	hints.ai_socktype = SOCK_DGRAM;
144 	hints.ai_flags = AI_NUMERICSERV;
145 
146 	/* Automatically strips port number */
147 	log_debug ("resolving host: %s", host->hostname);
148 	host->last_resolve_try = when;
149 	host->is_resolving = 0;
150 	async_resolver_queue (host->hostname, host->portnum, &hints, resolve_cb, host);
151 }
152 
153 static int
host_resolve_timer(mstime when,void * arg)154 host_resolve_timer (mstime when, void* arg)
155 {
156 	struct host *h;
157 
158 	/* Go through hosts and see which ones need resolving */
159 	for (h = host_list; h; h = h->next) {
160 
161 		/* No need to resolve? */
162 		if (!h->must_resolve)
163 			continue;
164 
165 		ASSERT (h->resolve_interval);
166 
167 		if (when - h->resolve_interval > h->last_resolve_try)
168 			host_resolve (h, when);
169 
170 		/* When the last 3 resolves have failed, set to unresolved */
171 		if (h->is_resolved && when - (h->resolve_interval * 3) > h->last_resolved) {
172 			log_debug ("host address expired, and was not resolved: %s", h->hostname);
173 			h->is_resolved = 0;
174 		}
175 	}
176 
177 	return 1;
178 }
179 
180 static void
host_update_interval(struct host * host,mstime interval)181 host_update_interval (struct host *host, mstime interval)
182 {
183 	mstime resint;
184 
185 	if (!host->must_resolve)
186 		return;
187 
188 	/* When less than three minutes, resolve once per minute */
189 	if (interval <= 180000)
190 		resint = 60000;
191 
192 	/* When between 3 and 10 minutes resolve once per cycle */
193 	else if(interval <= 600000)
194 		resint = interval;
195 
196 	/* Otherwise resolve thrice per cycle */
197 	else
198 		resint = interval / 3;
199 
200 	/* The lowest interval (since hosts can be shared by pollers) wins */
201 	if (!host->resolve_interval || host->resolve_interval > resint) {
202 		host->resolve_interval = resint;
203 		log_debug ("will resolve host '%s' every %d seconds", host->hostname, resint / 1000);
204 	}
205 }
206 
207 static struct host*
host_instance(const char * hostname,const char * portnum,const char * community,int version,mstime interval)208 host_instance (const char *hostname, const char *portnum,
209                const char *community, int version, mstime interval)
210 {
211 	struct addrinfo hints, *ai;
212 	struct host *host;
213 	char key[128];
214 	int r;
215 
216 	ASSERT (hostname);
217 
218 	if (!portnum)
219 		portnum = "161";
220 
221 	/*
222 	 * Build a lookup key. We can only combine requests for the same
223 	 * host when the version and community match.
224 	 */
225 	community = community ? community : "public";
226 	snprintf (key, sizeof(key), "%s:%d:%s", hostname, version, community);
227 	key[sizeof(key) - 1] = 0;
228 
229 	/* See if we can find an associated host */
230 	host = hsh_get (host_by_key, key, -1);
231 	if (!host) {
232 
233 		memset (&hints, 0, sizeof (hints));
234 		hints.ai_family = PF_UNSPEC;
235 		hints.ai_socktype = SOCK_DGRAM;
236 		hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST;
237 
238 		r = getaddrinfo (hostname, portnum, &hints, &ai);
239 
240 		/* Ignore error and try to resolve again later */
241 		if (r == EAI_NONAME || r == EAI_AGAIN || r == EAI_MEMORY) {
242 			ai = NULL;
243 
244 		/* Real errors */
245 		} else if (r != 0) {
246 			log_warnx ("couldn't parse host address (ignoring): %s: %s",
247 			           hostname, gai_strerror (r));
248 			return NULL;
249 
250 		/* Strango address */
251 		} else if (ai->ai_addrlen > sizeof (host->address)) {
252 			log_warnx ("parsed host address is too big (ignoring): %s", hostname);
253 			freeaddrinfo (ai);
254 			return NULL;
255 		}
256 
257 		host = calloc (1, sizeof (struct host));
258 		if (!host) {
259 			log_errorx ("out of memory");
260 			if (ai != NULL)
261 				freeaddrinfo (ai);
262 			return NULL;
263 		}
264 
265 		if (ai != NULL) {
266 			memcpy (&host->address, ai->ai_addr, ai->ai_addrlen);
267 			host->address_len = ai->ai_addrlen;
268 			freeaddrinfo (ai);
269 			host->must_resolve = 0;
270 			host->is_resolved = 1;
271 		} else {
272 			host->must_resolve = 1;
273 			host->is_resolved = 0;
274 		}
275 
276 		/* And into the hash table */
277 		memcpy (&host->key, key, sizeof (host->key));
278 		if (!hsh_set (host_by_key, host->key, -1, host)) {
279 			log_errorx ("out of memory");
280 			free (host);
281 			return NULL;
282 		}
283 
284 		/* And add it to the list */
285 		host->next = host_list;
286 		host_list = host;
287 
288 		host->version = version;
289 		host->hostname = strdup (hostname);
290 		host->portnum = strdup (portnum);
291 		host->community = strdup (community);
292 		host->resolve_interval = 0;
293 		host->last_resolved = 0;
294 		host->last_resolve_try = 0;
295 
296 		/* Start the resolving process */
297 		if (!host->is_resolved)
298 			host_resolve (host, server_get_time ());
299 	}
300 
301 	/* Update the host's resolve interval based on the poll interval requested */
302 	host_update_interval (host, interval);
303 
304 	return host;
305 }
306 
307 static void
host_initialize(void)308 host_initialize (void)
309 {
310 	/* Initialize stuff if necessary */
311 	host_by_key = hsh_create ();
312 	if (!host_by_key)
313 		err (1, "out of memory");
314 
315 	/* resolve timer goes once per second */
316 	if (server_timer (1000, host_resolve_timer, NULL) == -1)
317 		err (1, "couldn't setup resolve timer");
318 }
319 
320 static void
host_cleanup(void)321 host_cleanup (void)
322 {
323 	struct host *next, *host;
324 
325 	if (host_by_key)
326 		hsh_free (host_by_key);
327 	host_by_key = NULL;
328 
329 	for (host = host_list; host; host = next) {
330 		next = host->next;
331 		if (host->hostname)
332 			free (host->hostname);
333 		if (host->portnum)
334 			free (host->portnum);
335 		if (host->community)
336 			free (host->community);
337 		if (host->prepared)
338 			request_release (host->prepared);
339 		free (host);
340 	}
341 
342 	host_list = NULL;
343 }
344 
345 /* ------------------------------------------------------------------------------
346  * ASYNC REQUEST PROCESSING
347  */
348 
349 #define MAKE_REQUEST_ID(snmp, cb) \
350 	((((snmp) & 0xFFFFFF) << 8) | (cb & 0xFF))
351 #define REQUEST_ID_SNMP(id) \
352 	((id) >> 8)
353 #define REQUEST_ID_CB(id) \
354 	((id) & 0xFF)
355 
356 struct request
357 {
358 	/* The SNMP request identifier */
359 	uint snmp_id;
360 
361 	mstime next_send;         /* Time of the next packet send */
362 	mstime last_sent;         /* Time last sent */
363 	mstime retry_interval;    /* How long between retries */
364 	mstime when_timeout;      /* When this request times out */
365 	uint num_sent;            /* How many times we've sent */
366 
367 	struct host *host;        /* Host associated with this request */
368 
369 	/* One callback entry for each binding */
370 	struct {
371 		snmp_response func;
372 		void *arg;
373 	} callbacks[SNMP_MAX_BINDINGS];
374 
375 	/* One flag for each binding */
376 	int is_duplicate[SNMP_MAX_BINDINGS];
377 
378 	int duplicates;           /* Total number of duplicate bindings */
379 
380 	/* The actual request data */
381 	struct snmp_pdu pdu;
382 };
383 
384 struct socket
385 {
386 	int fd;                         /* The SNMP socket we're communicating on */
387 	struct sockaddr_storage addr;   /* Local address of this socket */
388 	socklen_t addr_len;             /* Length of addr */
389 	struct socket *next;            /* Linked list of socket structures */
390 };
391 
392 #define MAX_SNMP_REQUEST_ID 0x800000
393 
394 /* The number of SNMP packet retries */
395 static int snmp_retries = 3;
396 
397 /* The next request id */
398 static uint snmp_request_id = 1;
399 
400 /* The sockets we communicate on */
401 static struct socket *snmp_sockets = NULL;
402 
403 /* Since we only deal with one packet at a time, global buffer */
404 static unsigned char snmp_buffer[0x1000];
405 
406 /* Hash table of all requests being processed */
407 static hsh_t *snmp_processing = NULL;
408 
409 /* Hash table of all requests being prepared */
410 static hsh_t *snmp_preparing = NULL;
411 
412 /* A flush of prepared packets is pending */
413 static int snmp_flush_pending = 0;
414 
415 static void
request_release_all(hsh_t * hsh_req)416 request_release_all (hsh_t * hsh_req)
417 {
418 	struct request *req;
419 	hsh_index_t *i;
420 	void *val;
421 
422 	/* Go through all request packets */
423 	for (i = hsh_first (hsh_req); i; ) {
424 
425 		req = hsh_this (i, NULL, NULL);
426 		ASSERT (req);
427 
428 		/* Move to the next, as we delete below */
429 		i = hsh_next (i);
430 
431 		val = hsh_rem (hsh_req, &req->snmp_id, sizeof (req->snmp_id));
432 		ASSERT (val == req);
433 
434 		if (req->host && req->host->prepared == req)
435 			req->host->prepared = NULL;
436 
437 		/* And free the request */
438 		request_release (req);
439 	}
440 }
441 
442 static void
request_release(struct request * req)443 request_release (struct request *req)
444 {
445 	/* It should no longer be referred to any of these places */
446 	ASSERT (!hsh_get (snmp_preparing, &req->snmp_id, sizeof (req->snmp_id)));
447 	ASSERT (!hsh_get (snmp_processing, &req->snmp_id, sizeof (req->snmp_id)));
448 
449 	snmp_pdu_clear (&req->pdu);
450 	free (req);
451 }
452 
453 static void
request_send(struct request * req,mstime when)454 request_send (struct request* req, mstime when)
455 {
456 	struct socket *sock;
457 	struct asn_buf b;
458 	ssize_t ret;
459 	struct snmp_pdu *pdu, unique_pdu;
460 	int i;
461 
462 	ASSERT (snmp_sockets != NULL);
463 
464 	/* Update our bookkeeping */
465 	req->num_sent++;
466 	if (req->num_sent <= snmp_retries)
467 		req->next_send = when + req->retry_interval;
468 	else
469 		req->next_send = 0;
470 	req->last_sent = when;
471 
472 	if (!req->host->is_resolved) {
473 		if (req->num_sent <= 1)
474 			log_debug ("skipping request #%d for: %s@%s: host not resolved",
475 			           req->snmp_id, req->host->community, req->host->hostname);
476 		return;
477 	}
478 
479 	/* Select a good socket to use, based on address family */
480 	for (sock = snmp_sockets; sock; sock = sock->next) {
481 		if (sock->addr.ss_family == req->host->address.ss_family)
482 			break;
483 	}
484 
485 	if (sock == NULL) {
486 		log_warnx ("couldn't send snmp packet to: %s: %s",
487 		           req->host->hostname, "no local address of relevant protocol family");
488 		return;
489 	}
490 
491 	b.asn_ptr = snmp_buffer;
492 	b.asn_len = sizeof (snmp_buffer);
493 
494 	/* Remove any duplicates from the request */
495 	pdu = &req->pdu;
496 	if (req->duplicates > 0) {
497 		memcpy (&unique_pdu, &req->pdu, sizeof (struct snmp_pdu));
498 		unique_pdu.nbindings = 0;
499 		for (i = 0; i < req->pdu.nbindings; ++i) {
500 			if (!req->is_duplicate[i]) {
501 				unique_pdu.bindings[unique_pdu.nbindings] = req->pdu.bindings[i];
502 				unique_pdu.nbindings++;
503 			}
504 		}
505 		ASSERT (req->pdu.nbindings - req->duplicates == unique_pdu.nbindings);
506 		pdu = &unique_pdu;
507 	}
508 
509 	if (snmp_pdu_encode (pdu, &b)) {
510 		log_error("couldn't encode snmp buffer");
511 	} else {
512 		ret = sendto (sock->fd, snmp_buffer, b.asn_ptr - snmp_buffer, 0,
513 		              (struct sockaddr*)&req->host->address, req->host->address_len);
514 		if (ret == -1)
515 			log_error ("couldn't send snmp packet to: %s", req->host->hostname);
516 		else
517 			log_debug ("sent request #%d to: %s", req->snmp_id, req->host->hostname);
518 	}
519 }
520 
521 static void
request_failure(struct request * req,int code)522 request_failure (struct request *req, int code)
523 {
524 	void *val;
525 	int j;
526 	int snmp_id;
527 
528 	ASSERT (req);
529 	ASSERT (code != 0);
530 	ASSERT (hsh_get (snmp_processing, &req->snmp_id, sizeof (req->snmp_id)) == req);
531 
532 	log_debug ("failed request #%d to '%s' with code %d", req->snmp_id, req->host->hostname, code);
533 
534     /* Remember snmp_id in case req is freed by the callback */
535     snmp_id = req->snmp_id;
536 
537 	/* For each request SNMP value... */
538 	for (j = 0; j < req->pdu.nbindings; ++j) {
539 
540 		if (!req->callbacks[j].func)
541 			continue;
542 
543 		/* ... let callback know */
544 		(req->callbacks[j].func) (MAKE_REQUEST_ID (req->snmp_id, j),
545 				          code, NULL, req->callbacks[j].arg);
546 
547 		/*
548 		 * Request could have been freed by the callback, by calling the cancel
549 		 * function, check and bail if so.
550 		 */
551 		if (hsh_get (snmp_processing, &snmp_id, sizeof (snmp_id)) != req)
552 			return;
553 	}
554 
555 	/* Remove from the processing list */
556 	val = hsh_rem (snmp_processing, &req->snmp_id, sizeof (req->snmp_id));
557 	ASSERT (val == req);
558 
559 	/* And free the request */
560 	request_release (req);
561 }
562 
563 static void
request_get_dispatch(struct request * req,struct snmp_pdu * pdu)564 request_get_dispatch (struct request* req, struct snmp_pdu* pdu)
565 {
566 	struct snmp_value *pvalue;
567 	struct snmp_value *rvalue;
568 	int i, j, missed, processed;
569 	void *val;
570 
571 	ASSERT (req);
572 	ASSERT (pdu);
573 	ASSERT (req->snmp_id == pdu->request_id);
574 	ASSERT (pdu->error_status == SNMP_ERR_NOERROR);
575 	ASSERT (req->pdu.type == SNMP_PDU_GET);
576 	ASSERT (hsh_get (snmp_processing, &req->snmp_id, sizeof (req->snmp_id)) == req);
577 
578 	/*
579 	 * For SNMP GET requests we check that the values that came back
580 	 * were in fact for the same values we requested, and fix any
581 	 * ordering issues etc. See also request_prep_instance deduplication.
582 	 */
583 	missed = 0;
584 	for (j = 0; j < req->pdu.nbindings; ++j) {
585 
586 		if (!req->callbacks[j].func)
587 			continue;
588 
589 		rvalue = &(req->pdu.bindings[j]);
590 		processed = 0;
591 
592 		/* ... dig out matching value from response */
593 		for (i = 0; i < pdu->nbindings; ++i) {
594 			pvalue = &(pdu->bindings[i]);
595 
596 			if (asn_compare_oid (&(rvalue->var), &(pvalue->var)) != 0)
597 				continue;
598 
599 			(req->callbacks[j].func) (MAKE_REQUEST_ID (req->snmp_id, j),
600 			                          SNMP_ERR_NOERROR, pvalue, req->callbacks[j].arg);
601 			processed = 1;
602 
603 			/*
604 			 * Request could have been freed by the callback, by calling the cancel
605 			 * function, check and bail if so.
606 			 */
607 			if (hsh_get (snmp_processing, &req->snmp_id, sizeof (req->snmp_id)) != req)
608 				return;
609 
610 			req->callbacks[j].func = NULL;
611 			req->callbacks[j].arg = NULL;
612 			break;
613 		}
614 
615 		/* Make note that we didn't find a match for at least one binding */
616 		if (!processed && !missed) {
617 			missed = 1;
618 		}
619 	}
620 
621 	/* All done? */
622 	if (!missed)
623 		log_debug ("request #%d is complete", req->snmp_id);
624 
625     if (hsh_get (snmp_processing, &req->snmp_id, sizeof (req->snmp_id)) == req) {
626 		val = hsh_rem (snmp_processing, &req->snmp_id, sizeof (req->snmp_id));
627 		ASSERT (val == req);
628 		request_release (req);
629 	}
630 }
631 
632 static void
request_other_dispatch(struct request * req,struct snmp_pdu * pdu)633 request_other_dispatch (struct request* req, struct snmp_pdu* pdu)
634 {
635 	int snmp_id;
636 	void *val;
637 
638 	ASSERT (req);
639 	ASSERT (pdu);
640 	ASSERT (req->snmp_id == pdu->request_id);
641 	ASSERT (pdu->error_status == SNMP_ERR_NOERROR);
642 	ASSERT (req->pdu.type != SNMP_PDU_GET);
643 
644 	/* Remember snmp_id in case req is freed by the callback */
645     snmp_id = req->snmp_id;
646 
647 	/*
648 	 * For requests other than GET we just use the first value
649 	 * that was sent. See below where we limit to one binding
650 	 * per SNMP request when other than GET.
651 	 */
652 
653 	if (pdu->nbindings == 0) {
654 		log_warn ("received response from the server without any values");
655 		return;
656 	}
657 
658 	if (pdu->nbindings > 1)
659 		log_warn ("received response from the server with extra values");
660 
661 	/* Shouldn't have sent more than one binding */
662 	ASSERT (req->pdu.nbindings == 1);
663 
664 	if (req->callbacks[0].func)
665 		(req->callbacks[0].func) (MAKE_REQUEST_ID (req->snmp_id, 0), SNMP_ERR_NOERROR,
666 						          &(pdu->bindings[0]), req->callbacks[0].arg);
667 
668 	log_debug ("request #%d is complete", snmp_id);
669 
670 	/*
671 	 * Request could have been freed by the callback, by calling the cancel
672 	 * function, check and bail if so.
673 	 */
674 	if (hsh_get (snmp_processing, &req->snmp_id, sizeof (req->snmp_id)) != req)
675 		return;
676 
677 	val = hsh_rem (snmp_processing, &req->snmp_id, sizeof (req->snmp_id));
678 	ASSERT (val == req);
679 	request_release (req);
680 }
681 
682 static void
request_response(int fd,int type,void * arg)683 request_response (int fd, int type, void* arg)
684 {
685 	struct sockaddr_storage from;
686 	char hostname[MAXPATHLEN];
687 	struct snmp_pdu pdu;
688 	struct asn_buf b;
689 	struct request *req;
690 	const char *msg;
691 	socklen_t from_len;
692 	int len, ret;
693 	int ip, id;
694 
695 	/* Read in the packet */
696 	from_len = sizeof (from);
697 	len = recvfrom (fd, snmp_buffer, sizeof (snmp_buffer), 0,
698 	                (struct sockaddr*)&from, &from_len);
699 	if(len < 0) {
700 		if(errno != EAGAIN && errno != EWOULDBLOCK)
701 			log_error ("error receiving snmp packet from network");
702 		return;
703 	}
704 
705 	if (getnameinfo ((struct sockaddr*)&from, from_len,
706 	                 hostname, sizeof (hostname), NULL, 0,
707 	                 NI_NUMERICHOST) != 0)
708 		strcpy (hostname, "[UNKNOWN]");
709 
710 	/* Now parse the packet */
711 
712 	b.asn_ptr = snmp_buffer;
713 	b.asn_len = len;
714 
715 	ret = snmp_pdu_decode(&b, &pdu, &ip);
716 	if (ret != SNMP_CODE_OK) {
717 		log_warnx ("invalid snmp packet received from: %s", hostname);
718 		return;
719 	}
720 
721 	/* It needs to match something we're waiting for */
722 	id = pdu.request_id;
723 	req = hsh_get (snmp_processing, &id, sizeof (id));
724 	if(!req) {
725 		log_debug ("received extra, cancelled or delayed packet from: %s", hostname);
726 		snmp_pdu_clear (&pdu);
727 		return;
728 	}
729 
730 	if(pdu.version != req->pdu.version)
731 		log_warnx ("wrong version snmp packet from: %s", hostname);
732 
733 
734 	/* Log any errors */
735 	if(pdu.error_status == SNMP_ERR_NOERROR) {
736 		log_debug ("response to request #%d from: %s", req->snmp_id, hostname);
737 
738 		if (req->pdu.type == SNMP_PDU_GET)
739 			request_get_dispatch (req, &pdu);
740 		else
741 			request_other_dispatch (req, &pdu);
742 
743 	} else {
744 		msg = snmp_get_errmsg (pdu.error_status);
745 		if(msg)
746 			log_debug ("failure for request #%d from: %s: %s", req->snmp_id, hostname, msg);
747 		else
748 			log_debug ("failure for request #%d from: %s: %d", req->snmp_id, hostname,
749 			           pdu.error_status);
750 		request_failure (req, pdu.error_status);
751 	}
752 
753 	snmp_pdu_clear (&pdu);
754 }
755 
756 static void
request_process_all(mstime when)757 request_process_all (mstime when)
758 {
759 	struct request *req;
760 	hsh_index_t *i;
761 
762 	/* Go through all processing packets */
763 	for (i = hsh_first (snmp_processing); i; ) {
764 
765 		req = hsh_this (i, NULL, NULL);
766 		ASSERT (req);
767 
768 		/* Move to the next, as we may delete below */
769 		i = hsh_next (i);
770 
771 		if (when >= req->when_timeout)
772 			request_failure (req, -1);
773 		else if (req->next_send && when >= req->next_send)
774 			request_send (req, when);
775 	}
776 }
777 
778 static int
request_resend_timer(mstime when,void * arg)779 request_resend_timer (mstime when, void* arg)
780 {
781 	request_process_all (when);
782 	return 1;
783 }
784 
785 static void
request_flush(struct request * req,mstime when)786 request_flush (struct request *req, mstime when)
787 {
788 	void *val;
789 
790 	ASSERT (req->host->prepared == req);
791 
792 	val = hsh_rem (snmp_preparing, &req->snmp_id, sizeof (req->snmp_id));
793 	ASSERT (val == req);
794 
795 	/* Don't let us add more onto this request via the host */
796 	ASSERT (req->host->prepared == req);
797 	req->host->prepared = NULL;
798 
799 	/* Mark this packet to be sent now */
800 	req->next_send = when;
801 
802 	if (!hsh_set (snmp_processing, &req->snmp_id, sizeof (req->snmp_id), req)) {
803 		log_errorx ("out of memory, discarding packets");
804 		request_release (req);
805 	}
806 }
807 
808 static void
request_flush_all(mstime when)809 request_flush_all (mstime when)
810 {
811 	struct request *req;
812 	hsh_index_t *i;
813 
814 	/* Transfer everything to the processing table */
815 	for (i = hsh_first (snmp_preparing); i; ) {
816 		req = hsh_this (i, NULL, NULL);
817 
818 		/* Do this here, because below removes from table */
819 		i = hsh_next (i);
820 
821 		request_flush (req, when);
822 	}
823 
824 	/* Clear the preparing table */
825 	hsh_clear (snmp_preparing);
826 
827 	/* Process all packets in processing */
828 	request_process_all (when);
829 }
830 
831 
832 
833 static int
request_flush_cb(mstime when,void * arg)834 request_flush_cb (mstime when, void *arg)
835 {
836 	snmp_flush_pending = 0;
837 	request_flush_all (when);
838 	return 0; // unrepeated
839 }
840 
841 static struct request*
request_prep_instance(struct host * host,mstime interval,mstime timeout,int reqtype,struct asn_oid * oid,int * is_duplicate)842 request_prep_instance (struct host *host, mstime interval, mstime timeout,
843                        int reqtype, struct asn_oid *oid, int *is_duplicate)
844 {
845 	struct request *req;
846 	struct snmp_value *rvalue;
847 	int i;
848 
849 	*is_duplicate = 0;
850 
851 	/* See if we have one we can piggy back onto */
852 	req = host->prepared;
853 	if (req) {
854 		ASSERT (hsh_get (snmp_preparing, &req->snmp_id, sizeof (req->snmp_id)));
855 
856 		if (req->pdu.type == SNMP_PDU_GET) {
857 			/*
858 			 * Check whether oid is already waiting in the pdu so we can avoid
859 			 * asking for it twice in the same request - we know request_get_dispatch
860 			 * will find the first copy for each callback anyway
861 			 */
862 			for (i = 0; i < req->pdu.nbindings; ++i) {
863 				rvalue = &(req->pdu.bindings[i]);
864 				if (asn_compare_oid (&(rvalue->var), oid) == 0) {
865 					*is_duplicate = 1;
866 					return req;
867 				}
868 			}
869 		}
870 
871 		/* We have one we can piggy back another request onto */
872 		if (req->pdu.nbindings < SNMP_MAX_BINDINGS && req->pdu.type == reqtype)
873 			return req;
874 
875 		/* It's too full, so send it off */
876 		request_flush (req, server_get_time ());
877 		req = NULL;
878 	}
879 
880 	ASSERT (host->prepared == NULL);
881 
882 	/* Create a new request */
883 	req = calloc (1, sizeof (struct request));
884 	if (!req) {
885 		log_error ("out of memory");
886 		return NULL;
887 	}
888 
889 	/* Assign the unique id */
890 	req->snmp_id = snmp_request_id;
891 	++snmp_request_id;
892 
893 	/*
894 	 * Roll around after a decent amount of ids. Since we're using
895 	 * signed integers, and these are used in strange ways, we can't
896 	 * go above 0x800000 or so.
897 	 */
898 	if (snmp_request_id > MAX_SNMP_REQUEST_ID)
899 		snmp_request_id = 1;
900 
901 	/* Mark it down as something we want to prepare */
902 	if (!hsh_set (snmp_preparing, &req->snmp_id, sizeof (req->snmp_id), req)) {
903 		log_error ("out of memory");
904 		free (req);
905 		return NULL;
906 	}
907 
908 	/* Setup the packet */
909 	strlcpy (req->pdu.community, host->community, sizeof (req->pdu.community));
910 	req->pdu.request_id = req->snmp_id;
911 	req->pdu.version = host->version;
912 	req->pdu.type = reqtype;
913 	req->pdu.error_status = 0;
914 	req->pdu.error_index = 0;
915 	req->pdu.nbindings = 0;
916 
917 	/* Send interval is 200 ms when poll interval is below 2 seconds */
918 	req->retry_interval = (interval <= 2000) ? 200L : 600L;
919 
920 	/* Timeout is for the last packet sent, not first */
921 	req->when_timeout = server_get_time () + (req->retry_interval * ((mstime)snmp_retries)) + timeout;
922 	req->num_sent = 0;
923 
924 	/* Add it to the host */
925 	req->host = host;
926 	ASSERT (host->prepared == NULL);
927 	host->prepared = req;
928 
929 	log_debug ("preparing request #%d for: %s@%s", req->snmp_id,
930 	           req->host->community, req->host->hostname);
931 
932 	return req;
933 }
934 
935 int
snmp_engine_request(const char * hostname,const char * port,const char * community,int version,mstime interval,mstime timeout,int reqtype,struct asn_oid * oid,snmp_response func,void * arg)936 snmp_engine_request (const char *hostname, const char *port,
937                      const char *community, int version,
938                      mstime interval, mstime timeout, int reqtype,
939                      struct asn_oid *oid, snmp_response func, void *arg)
940 {
941 	struct host *host;
942 	struct request *req;
943 	int is_duplicate;
944 	int callback_id;
945 
946 	ASSERT (func);
947 
948 	/* Lookup host for request */
949 	host = host_instance (hostname, port, community, version, interval);
950 	if (!host)
951 		return 0;
952 
953 	/* Get a request with space or a new request for that host */
954 	req = request_prep_instance (host, interval, timeout, reqtype, oid, &is_duplicate);
955 	if (!req)
956 		return 0;
957 
958 	if (is_duplicate)
959 		++req->duplicates;
960 	ASSERT (req->pdu.nbindings - req->duplicates < SNMP_MAX_BINDINGS);
961 
962 	/* Add the oid to that request */
963 	callback_id = req->pdu.nbindings;
964 	req->pdu.bindings[callback_id].var = *oid;
965 	req->pdu.bindings[callback_id].syntax = SNMP_SYNTAX_NULL;
966 	req->callbacks[callback_id].func = func;
967 	req->callbacks[callback_id].arg = arg;
968 	req->is_duplicate[callback_id] = is_duplicate;
969 	req->pdu.nbindings++;
970 
971 	/* All other than GET, only get one binding */
972 	if (reqtype != SNMP_PDU_GET) {
973 		ASSERT (req->pdu.nbindings == 1);
974 		request_flush (req, server_get_time ());
975 	}
976 
977 	/* Otherwise flush on the idle callback */
978 	else if (!snmp_flush_pending) {
979 		server_timer (0, request_flush_cb, NULL);
980 		snmp_flush_pending = 1;
981 	}
982 
983 	return MAKE_REQUEST_ID (req->snmp_id, callback_id);
984 }
985 
986 void
snmp_engine_remove(int id,const char * during)987 snmp_engine_remove (int id, const char *during)
988 {
989 	struct request *req;
990 	int snmp_id, callback_id, i;
991 
992 	ASSERT (id);
993 
994 	snmp_id = REQUEST_ID_SNMP (id);
995 	callback_id = REQUEST_ID_CB (id);
996 
997 	ASSERT (snmp_id > 0 && snmp_id < MAX_SNMP_REQUEST_ID);
998 	ASSERT (callback_id >= 0 && callback_id < SNMP_MAX_BINDINGS);
999 
1000 	/* Is it being processed or prepared? */
1001 	req = hsh_get (snmp_processing, &snmp_id, sizeof (snmp_id));
1002 	if (!req) {
1003 		req = hsh_get (snmp_preparing, &snmp_id, sizeof (snmp_id));
1004 	}
1005 
1006 	if (!req)
1007 		return;
1008 
1009 	/* Remove this callback from the request */
1010 	req->callbacks[callback_id].func = NULL;
1011 	req->callbacks[callback_id].arg = NULL;
1012 
1013 	/* See if any other callbacks exist in the request */
1014 	for (i = 0; i < req->pdu.nbindings; ++i) {
1015 		if (req->callbacks[i].func)
1016 			return;
1017 	}
1018 
1019 	if (during)
1020 		log_debug ("cancelling request #%d during %s", snmp_id, during);
1021 
1022 	hsh_rem (snmp_processing, &snmp_id, sizeof (snmp_id));
1023 	hsh_rem (snmp_preparing, &snmp_id, sizeof (snmp_id));
1024 
1025 	/* If not, free the request */
1026 	if (req->host->prepared == req)
1027 		req->host->prepared = NULL;
1028 	request_release (req);
1029 }
1030 
1031 void
snmp_engine_clear(int id)1032 snmp_engine_clear (int id)
1033 {
1034 	snmp_engine_remove (id, NULL);
1035 }
1036 
1037 void
snmp_engine_cancel(int id)1038 snmp_engine_cancel (int id)
1039 {
1040 	struct request *req;
1041 	int snmp_id, callback_id;
1042 	const char *during;
1043 
1044 	ASSERT (id);
1045 
1046 	snmp_id = REQUEST_ID_SNMP (id);
1047 	callback_id = REQUEST_ID_CB (id);
1048 
1049 	ASSERT (snmp_id > 0 && snmp_id < MAX_SNMP_REQUEST_ID);
1050 	ASSERT (callback_id >= 0 && callback_id < SNMP_MAX_BINDINGS);
1051 
1052 	/* Is it being processed? */
1053 	req = hsh_get (snmp_processing, &snmp_id, sizeof (snmp_id));
1054 	if (req) {
1055 		during = "processing";
1056 
1057 	/* Is it being prepared? */
1058 	} else {
1059 		req = hsh_get (snmp_preparing, &snmp_id, sizeof (snmp_id));
1060 		if (req) {
1061 			during = "prep";
1062 			ASSERT (req->host->prepared == req);
1063 		} else {
1064 			during = NULL;
1065 		}
1066 	}
1067 
1068 	snmp_engine_remove (id, during);
1069 }
1070 
1071 void
snmp_engine_flush(void)1072 snmp_engine_flush (void)
1073 {
1074 	request_flush_all (server_get_time ());
1075 }
1076 
1077 /* -------------------------------------------------------------------------------
1078  * SYNC REQUESTS
1079  */
1080 
1081 struct sync_data {
1082 	int valid;
1083 	int code;
1084 	int id;
1085 	struct snmp_value *dest;
1086 };
1087 
1088 static void
sync_response(int req,int code,struct snmp_value * value,void * data)1089 sync_response (int req, int code, struct snmp_value *value, void *data)
1090 {
1091 	struct sync_data *sync = data;
1092 
1093 	ASSERT (req == sync->id);
1094 
1095 	sync->valid = 1;
1096 	sync->code = code;
1097 	if (value)
1098 		snmp_value_copy (sync->dest, value);
1099 
1100 	server_stop ();
1101 }
1102 
1103 int
snmp_engine_sync(const char * host,const char * port,const char * community,int version,uint64_t interval,uint64_t timeout,int reqtype,struct snmp_value * value)1104 snmp_engine_sync (const char* host, const char *port, const char* community,
1105                   int version, uint64_t interval, uint64_t timeout, int reqtype,
1106                   struct snmp_value *value)
1107 {
1108 	struct sync_data sync;
1109 
1110 	/* Can't run a sync request with the server running */
1111 	ASSERT (server_stopped());
1112 
1113 	sync.valid = 0;
1114 	sync.code = 0;
1115 	sync.dest = value;
1116 
1117 	sync.id = snmp_engine_request (host, port, community, version, interval, timeout,
1118 	                               reqtype, &value->var, sync_response, &sync);
1119 
1120 	if (!sync.id)
1121 		return -1;
1122 
1123 	snmp_engine_flush ();
1124 	server_run ();
1125 
1126 	ASSERT (sync.valid);
1127 	return sync.code;
1128 }
1129 
1130 /* -----------------------------------------------------------------------------
1131  * INIT
1132  */
1133 
1134 void
snmp_engine_init(const char ** bindaddrs,int retries)1135 snmp_engine_init (const char **bindaddrs, int retries)
1136 {
1137 	struct addrinfo hints, *ai;
1138 	struct socket *sock;
1139 	const char **p, *bindaddr;
1140 	int fd, r;
1141 
1142 	ASSERT (bindaddrs);
1143 
1144 	snmp_retries = retries;
1145 
1146 	snmp_processing = hsh_create ();
1147 	if (!snmp_processing)
1148 		err (1, "out of memory");
1149 
1150 	snmp_preparing = hsh_create ();
1151 	if (!snmp_preparing)
1152 		err (1, "out of memory");
1153 
1154 	ASSERT (snmp_sockets == NULL);
1155 
1156 	for (p = bindaddrs; p && *p; ++p) {
1157 		bindaddr = *p;
1158 
1159 		memset (&hints, 0, sizeof (hints));
1160 		hints.ai_flags = AI_PASSIVE;
1161 		hints.ai_family = PF_UNSPEC;
1162 		hints.ai_socktype = SOCK_DGRAM;
1163 		hints.ai_flags = AI_NUMERICSERV;
1164 		r = getaddrinfo (bindaddr, "0", &hints, &ai);
1165 		if (r != 0)
1166 			errx (1, "couldn't resolve bind address '%s': %s", bindaddr, gai_strerror (r));
1167 
1168 		fd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1169 		if (fd < 0) {
1170 			if (errno == EPROTONOSUPPORT ||
1171 			    errno == ENOPROTOOPT ||
1172 			    errno == ESOCKTNOSUPPORT) {
1173 				warn ("couldn't create snmp socket for '%s'", bindaddr);
1174 			} else {
1175 				err (1, "couldn't open snmp socket");
1176 			}
1177 			freeaddrinfo (ai);
1178 			continue;
1179 		}
1180 
1181 		if (bind (fd, ai->ai_addr, ai->ai_addrlen) < 0)
1182 			err (1, "couldn't listen on port '%s'", bindaddr);
1183 
1184 		if (server_watch (fd, SERVER_READ, request_response, NULL) == -1)
1185 			err (1, "couldn't watch port");
1186 
1187 		/* Stash this socket info */
1188 		sock = xcalloc (sizeof (struct socket));
1189 		sock->fd = fd;
1190 
1191 		if (ai->ai_addrlen > sizeof (sock->addr))
1192 			errx (1, "resolve address is too big");
1193 		memcpy (&sock->addr, ai->ai_addr, ai->ai_addrlen);
1194 
1195 		/* Push onto the linked list */
1196 		sock->next = snmp_sockets;
1197 		snmp_sockets = sock;
1198 
1199 		freeaddrinfo (ai);
1200 	}
1201 
1202 	if (snmp_sockets == NULL)
1203 		errx (1, "no local addresses to listen on");
1204 
1205 	/* We fire off the resend timer every 1/5 second */
1206 	if (server_timer (200, request_resend_timer, NULL) == -1)
1207 	    err(1, "couldn't setup timer");
1208 
1209 	host_initialize ();
1210 }
1211 
1212 void
snmp_engine_stop(void)1213 snmp_engine_stop (void)
1214 {
1215 	struct socket *sock;
1216 
1217 	while (snmp_sockets != NULL) {
1218 		/* Pop off the list */
1219 		sock = snmp_sockets;
1220 		snmp_sockets = sock->next;
1221 
1222 		/* And destroy */
1223 		server_unwatch (sock->fd);
1224 		close (sock->fd);
1225 		free (sock);
1226 	}
1227 
1228 	/*
1229 	 * Release all requests before freeing the either hash, request_release,
1230 	 * and thus request_release_all, expect both hashes to still exist.
1231 	 */
1232     if (snmp_preparing)
1233 		request_release_all (snmp_preparing);
1234 	if (snmp_processing)
1235 		request_release_all (snmp_processing);
1236 
1237 	/* Now we can safely free both hashes. */
1238 	if (snmp_preparing)
1239 		hsh_free (snmp_preparing);
1240 	snmp_preparing = NULL;
1241 
1242 	if (snmp_processing)
1243 		hsh_free (snmp_processing);
1244 	snmp_processing = NULL;
1245 
1246 	host_cleanup ();
1247 }
1248 
1249 int
snmp_engine_match(const struct snmp_value * value,const char * text)1250 snmp_engine_match (const struct snmp_value *value, const char *text)
1251 {
1252 	char *end;
1253 
1254 	ASSERT (value);
1255 	ASSERT (text);
1256 
1257 	switch (value->syntax) {
1258 
1259 	/* Empty string */
1260 	case SNMP_SYNTAX_NULL:
1261 	case SNMP_SYNTAX_NOSUCHOBJECT:
1262 	case SNMP_SYNTAX_NOSUCHINSTANCE:
1263 	case SNMP_SYNTAX_ENDOFMIBVIEW:
1264 		return *text == '\0';
1265 
1266 	/* Integer value */
1267 	case SNMP_SYNTAX_INTEGER:
1268 		{
1269 			int num = strtoll (text, &end, 0);
1270 			if (*end != '\0')
1271 				return 0;
1272 			return num == value->v.integer;
1273 		}
1274 
1275 	/* String of bytes */
1276 	case SNMP_SYNTAX_OCTETSTRING:
1277 		{
1278 			int len = strlen (text);
1279 			if (value->v.octetstring.len != len)
1280 				return 0;
1281 			return memcmp (value->v.octetstring.octets, text, len) == 0;
1282 		}
1283 
1284 
1285 	case SNMP_SYNTAX_OID:
1286 		{
1287 			struct asn_oid oid;
1288 			if (mib_parse (text, &oid) < 0)
1289 				return 0;
1290 			return asn_compare_oid (&oid, &value->v.oid) == 0;
1291 		}
1292 
1293 	case SNMP_SYNTAX_IPADDRESS:
1294 		{
1295 		    struct in_addr addr;
1296 		    if (!inet_aton (text, &addr))
1297 			    return 0;
1298 		    return memcmp (&addr, value->v.ipaddress, 4) == 0;
1299 		}
1300 
1301 	case SNMP_SYNTAX_COUNTER:
1302 	case SNMP_SYNTAX_GAUGE:
1303 	case SNMP_SYNTAX_TIMETICKS:
1304 		{
1305 			uint64_t sub = strtoull (text, &end, 0);
1306 			if (*end != '\0' || sub > 0xffffffff)
1307 				return 0;
1308 			return sub == value->v.uint32;
1309 		}
1310 
1311 	case SNMP_SYNTAX_COUNTER64:
1312 		{
1313 			uint64_t sub = strtoull (text, &end, 0);
1314 			if (*end != '\0' || sub > 0xffffffff)
1315 				return 0;
1316 			return sub == value->v.counter64;
1317 		}
1318 
1319 	default:
1320 		return 0;
1321 	};
1322 }
1323