xref: /freebsd/contrib/bsnmp/lib/snmpclient.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
1 /*
2  * Copyright (c) 2001-2003
3  *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *	All rights reserved.
5  *
6  * Author: Harti Brandt <harti@freebsd.org>
7  *         Kendy Kutzner
8  *
9  * Redistribution of this software and documentation and use in source and
10  * binary forms, with or without modification, are permitted provided that
11  * the following conditions are met:
12  *
13  * 1. Redistributions of source code or documentation must retain the above
14  *    copyright notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the Institute nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
23  * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
25  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
26  * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
29  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
32  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * $Begemot: bsnmp/lib/snmpclient.c,v 1.27 2003/12/08 17:11:58 hbb Exp $
35  *
36  * Support functions for SNMP clients.
37  */
38 #include <sys/types.h>
39 #include <sys/queue.h>
40 #include <sys/socket.h>
41 #include <sys/un.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <stddef.h>
45 #include <stdarg.h>
46 #include <string.h>
47 #include <errno.h>
48 #include <unistd.h>
49 #include <fcntl.h>
50 #include <netdb.h>
51 #include <inttypes.h>
52 #include <err.h>
53 #include <limits.h>
54 
55 #include "asn1.h"
56 #include "snmp.h"
57 #include "snmpclient.h"
58 #include "snmppriv.h"
59 
60 /* global context */
61 struct snmp_client snmp_client;
62 
63 
64 /* List of all outstanding requests */
65 struct sent_pdu {
66 	int		reqid;
67 	struct snmp_pdu	*pdu;
68 	struct timeval	time;
69 	u_int		retrycount;
70 	snmp_send_cb_f	callback;
71 	void		*arg;
72 	void		*timeout_id;
73 	LIST_ENTRY(sent_pdu) entries;
74 };
75 LIST_HEAD(sent_pdu_list, sent_pdu);
76 
77 static struct sent_pdu_list sent_pdus;
78 
79 /*
80  * Prototype table entry. All C-structure produced by the table function must
81  * start with these two fields. This relies on the fact, that all TAILQ_ENTRY
82  * are compatible with each other in the sense implied by ANSI-C.
83  */
84 struct entry {
85 	TAILQ_ENTRY(entry)	link;
86 	u_int64_t		found;
87 };
88 TAILQ_HEAD(table, entry);
89 
90 /*
91  * working list entry. This list is used to hold the Index part of the
92  * table row's. The entry list and the work list parallel each other.
93  */
94 struct work {
95 	TAILQ_ENTRY(work)	link;
96 	struct asn_oid		index;
97 };
98 TAILQ_HEAD(worklist, work);
99 
100 /*
101  * Table working data
102  */
103 struct tabwork {
104 	const struct snmp_table *descr;
105 	struct table	*table;
106 	struct worklist	worklist;
107 	u_int32_t	last_change;
108 	int		first;
109 	u_int		iter;
110 	snmp_table_cb_f	callback;
111 	void		*arg;
112 	struct snmp_pdu	pdu;
113 };
114 
115 /*
116  * Set the error string
117  */
118 static void
119 seterr(const char *fmt, ...)
120 {
121 	va_list ap;
122 
123 	va_start(ap, fmt);
124 	vsnprintf(snmp_client.error, sizeof(snmp_client.error), fmt, ap);
125 	va_end(ap);
126 }
127 
128 /*
129  * Free the entire table and work list. If table is NULL only the worklist
130  * is freed.
131  */
132 static void
133 table_free(struct tabwork *work, int all)
134 {
135 	struct work *w;
136 	struct entry *e;
137 	const struct snmp_table_entry *d;
138 	u_int i;
139 
140 	while ((w = TAILQ_FIRST(&work->worklist)) != NULL) {
141 		TAILQ_REMOVE(&work->worklist, w, link);
142 		free(w);
143 	}
144 
145 	if (all == 0)
146 		return;
147 
148 	while ((e = TAILQ_FIRST(work->table)) != NULL) {
149 		for (i = 0; work->descr->entries[i].syntax != SNMP_SYNTAX_NULL;
150 		    i++) {
151 			d = &work->descr->entries[i];
152 			if (d->syntax == SNMP_SYNTAX_OCTETSTRING &&
153 			    (e->found & ((u_int64_t)1 << i)))
154 				free(*(void **)(void *)
155 				    ((u_char *)e + d->offset));
156 		}
157 		TAILQ_REMOVE(work->table, e, link);
158 		free(e);
159 	}
160 }
161 
162 /*
163  * Find the correct table entry for the given variable. If non exists,
164  * create one.
165  */
166 static struct entry *
167 table_find(struct tabwork *work, const struct asn_oid *var)
168 {
169 	struct entry *e, *e1;
170 	struct work *w, *w1;
171 	u_int i, p, j;
172 	size_t len;
173 	u_char *ptr;
174 	struct asn_oid oid;
175 
176 	/* get index */
177 	asn_slice_oid(&oid, var, work->descr->table.len + 2, var->len);
178 
179 	e = TAILQ_FIRST(work->table);
180 	w = TAILQ_FIRST(&work->worklist);
181 	while (e != NULL) {
182 		if (asn_compare_oid(&w->index, &oid) == 0)
183 			return (e);
184 		e = TAILQ_NEXT(e, link);
185 		w = TAILQ_NEXT(w, link);
186 	}
187 
188 	/* Not found create new one */
189 	if ((e = malloc(work->descr->entry_size)) == NULL) {
190 		seterr("no memory for table entry");
191 		return (NULL);
192 	}
193 	if ((w = malloc(sizeof(*w))) == NULL) {
194 		seterr("no memory for table entry");
195 		free(e);
196 		return (NULL);
197 	}
198 	w->index = oid;
199 	memset(e, 0, work->descr->entry_size);
200 
201 	/* decode index */
202 	p = work->descr->table.len + 2;
203 	for (i = 0; i < work->descr->index_size; i++) {
204 		switch (work->descr->entries[i].syntax) {
205 
206 		  case SNMP_SYNTAX_INTEGER:
207 			if (var->len < p + 1) {
208 				seterr("bad index: need integer");
209 				goto err;
210 			}
211 			if (var->subs[p] > INT32_MAX) {
212 				seterr("bad index: integer too large");
213 				goto err;
214 			}
215 			*(int32_t *)(void *)((u_char *)e +
216 			    work->descr->entries[i].offset) = var->subs[p++];
217 			break;
218 
219 		  case SNMP_SYNTAX_OCTETSTRING:
220 			if (var->len < p + 1) {
221 				seterr("bad index: need string length");
222 				goto err;
223 			}
224 			len = var->subs[p++];
225 			if (var->len < p + len) {
226 				seterr("bad index: string too short");
227 				goto err;
228 			}
229 			if ((ptr = malloc(len + 1)) == NULL) {
230 				seterr("no memory for index string");
231 				goto err;
232 			}
233 			for (j = 0; j < len; j++) {
234 				if (var->subs[p] > UCHAR_MAX) {
235 					seterr("bad index: char too large");
236 					free(ptr);
237 					goto err;
238 				}
239 				ptr[j] = var->subs[p++];
240 			}
241 			ptr[j] = '\0';
242 			*(u_char **)(void *)((u_char *)e +
243 			    work->descr->entries[i].offset) = ptr;
244 			*(size_t *)(void *)((u_char *)e +
245 			    work->descr->entries[i].offset + sizeof(u_char *))
246 			    = len;
247 			break;
248 
249 		  case SNMP_SYNTAX_OID:
250 			if (var->len < p + 1) {
251 				seterr("bad index: need oid length");
252 				goto err;
253 			}
254 			oid.len = var->subs[p++];
255 			if (var->len < p + oid.len) {
256 				seterr("bad index: oid too short");
257 				goto err;
258 			}
259 			for (j = 0; j < oid.len; j++)
260 				oid.subs[j] = var->subs[p++];
261 			*(struct asn_oid *)(void *)((u_char *)e +
262 			    work->descr->entries[i].offset) = oid;
263 			break;
264 
265 		  case SNMP_SYNTAX_IPADDRESS:
266 			if (var->len < p + 4) {
267 				seterr("bad index: need ip-address");
268 				goto err;
269 			}
270 			for (j = 0; j < 4; j++) {
271 				if (var->subs[p] > 0xff) {
272 					seterr("bad index: ipaddress too large");
273 					goto err;
274 				}
275 				((u_char *)e +
276 				    work->descr->entries[i].offset)[j] =
277 				    var->subs[p++];
278 			}
279 			break;
280 
281 		  case SNMP_SYNTAX_GAUGE:
282 			if (var->len < p + 1) {
283 				seterr("bad index: need unsigned");
284 				goto err;
285 			}
286 			if (var->subs[p] > UINT32_MAX) {
287 				seterr("bad index: unsigned too large");
288 				goto err;
289 			}
290 			*(u_int32_t *)(void *)((u_char *)e +
291 			    work->descr->entries[i].offset) = var->subs[p++];
292 			break;
293 
294 		  case SNMP_SYNTAX_COUNTER:
295 		  case SNMP_SYNTAX_TIMETICKS:
296 		  case SNMP_SYNTAX_COUNTER64:
297 		  case SNMP_SYNTAX_NULL:
298 		  case SNMP_SYNTAX_NOSUCHOBJECT:
299 		  case SNMP_SYNTAX_NOSUCHINSTANCE:
300 		  case SNMP_SYNTAX_ENDOFMIBVIEW:
301 			abort();
302 		}
303 		e->found |= (u_int64_t)1 << i;
304 	}
305 
306 	/* link into the correct place */
307 	e1 = TAILQ_FIRST(work->table);
308 	w1 = TAILQ_FIRST(&work->worklist);
309 	while (e1 != NULL) {
310 		if (asn_compare_oid(&w1->index, &w->index) > 0)
311 			break;
312 		e1 = TAILQ_NEXT(e1, link);
313 		w1 = TAILQ_NEXT(w1, link);
314 	}
315 	if (e1 == NULL) {
316 		TAILQ_INSERT_TAIL(work->table, e, link);
317 		TAILQ_INSERT_TAIL(&work->worklist, w, link);
318 	} else {
319 		TAILQ_INSERT_BEFORE(e1, e, link);
320 		TAILQ_INSERT_BEFORE(w1, w, link);
321 	}
322 
323 	return (e);
324 
325   err:
326 	/*
327 	 * Error happend. Free all octet string index parts and the entry
328 	 * itself.
329 	 */
330 	for (i = 0; i < work->descr->index_size; i++) {
331 		if (work->descr->entries[i].syntax == SNMP_SYNTAX_OCTETSTRING &&
332 		    (e->found & ((u_int64_t)1 << i)))
333 			free(*(void **)(void *)((u_char *)e +
334 			    work->descr->entries[i].offset));
335 	}
336 	free(e);
337 	free(w);
338 	return (NULL);
339 }
340 
341 /*
342  * Assign the value
343  */
344 static int
345 table_value(const struct snmp_table *descr, struct entry *e,
346     const struct snmp_value *b)
347 {
348 	u_int i;
349 	u_char *ptr;
350 
351 	for (i = descr->index_size;
352 	    descr->entries[i].syntax != SNMP_SYNTAX_NULL; i++)
353 		if (descr->entries[i].subid ==
354 		    b->var.subs[descr->table.len + 1])
355 			break;
356 	if (descr->entries[i].syntax == SNMP_SYNTAX_NULL)
357 		return (0);
358 
359 	/* check syntax */
360 	if (b->syntax != descr->entries[i].syntax) {
361 		seterr("bad syntax (%u instead of %u)", b->syntax,
362 		    descr->entries[i].syntax);
363 		return (-1);
364 	}
365 
366 	switch (b->syntax) {
367 
368 	  case SNMP_SYNTAX_INTEGER:
369 		*(int32_t *)(void *)((u_char *)e + descr->entries[i].offset) =
370 		    b->v.integer;
371 		break;
372 
373 	  case SNMP_SYNTAX_OCTETSTRING:
374 		if ((ptr = malloc(b->v.octetstring.len + 1)) == NULL) {
375 			seterr("no memory for string");
376 			return (-1);
377 		}
378 		memcpy(ptr, b->v.octetstring.octets, b->v.octetstring.len);
379 		ptr[b->v.octetstring.len] = '\0';
380 		*(u_char **)(void *)((u_char *)e + descr->entries[i].offset) =
381 		    ptr;
382 		*(size_t *)(void *)((u_char *)e + descr->entries[i].offset +
383 		    sizeof(u_char *)) = b->v.octetstring.len;
384 		break;
385 
386 	  case SNMP_SYNTAX_OID:
387 		*(struct asn_oid *)(void *)((u_char *)e + descr->entries[i].offset) =
388 		    b->v.oid;
389 		break;
390 
391 	  case SNMP_SYNTAX_IPADDRESS:
392 		memcpy((u_char *)e + descr->entries[i].offset,
393 		    b->v.ipaddress, 4);
394 		break;
395 
396 	  case SNMP_SYNTAX_COUNTER:
397 	  case SNMP_SYNTAX_GAUGE:
398 	  case SNMP_SYNTAX_TIMETICKS:
399 		*(u_int32_t *)(void *)((u_char *)e + descr->entries[i].offset) =
400 		    b->v.uint32;
401 		break;
402 
403 	  case SNMP_SYNTAX_COUNTER64:
404 		*(u_int64_t *)(void *)((u_char *)e + descr->entries[i].offset) =
405 		    b->v.counter64;
406 		break;
407 
408 	  case SNMP_SYNTAX_NULL:
409 	  case SNMP_SYNTAX_NOSUCHOBJECT:
410 	  case SNMP_SYNTAX_NOSUCHINSTANCE:
411 	  case SNMP_SYNTAX_ENDOFMIBVIEW:
412 		abort();
413 	}
414 	e->found |= (u_int64_t)1 << i;
415 
416 	return (0);
417 }
418 
419 /*
420  * Initialize the first PDU to send
421  */
422 static void
423 table_init_pdu(const struct snmp_table *descr, struct snmp_pdu *pdu)
424 {
425 	if (snmp_client.version == SNMP_V1)
426 		snmp_pdu_create(pdu, SNMP_PDU_GETNEXT);
427 	else {
428 		snmp_pdu_create(pdu, SNMP_PDU_GETBULK);
429 		pdu->error_index = 10;
430 	}
431 	if (descr->last_change.len != 0) {
432 		pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL;
433 		pdu->bindings[pdu->nbindings].var = descr->last_change;
434 		pdu->nbindings++;
435 		if (pdu->version != SNMP_V1)
436 			pdu->error_status++;
437 	}
438 	pdu->bindings[pdu->nbindings].var = descr->table;
439 	pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL;
440 	pdu->nbindings++;
441 }
442 
443 /*
444  * Return code:
445  *	0  - End Of Table
446  * 	-1 - Error
447  *	-2 - Last change changed - again
448  *	+1 - ok, continue
449  */
450 static int
451 table_check_response(struct tabwork *work, const struct snmp_pdu *resp)
452 {
453 	const struct snmp_value *b;
454 	struct entry *e;
455 
456 	if (resp->error_status != SNMP_ERR_NOERROR) {
457 		if (snmp_client.version == SNMP_V1 &&
458 		    resp->error_status == SNMP_ERR_NOSUCHNAME &&
459 		    resp->error_index ==
460 		    (work->descr->last_change.len == 0) ? 1 : 2)
461 			/* EOT */
462 			return (0);
463 		/* Error */
464 		seterr("error fetching table: status=%d index=%d",
465 		    resp->error_status, resp->error_index);
466 		return (-1);
467 	}
468 
469 	for (b = resp->bindings; b < resp->bindings + resp->nbindings; b++) {
470 		if (work->descr->last_change.len != 0 && b == resp->bindings) {
471 			if (!asn_is_suboid(&work->descr->last_change, &b->var) ||
472 			    b->var.len != work->descr->last_change.len + 1 ||
473 			    b->var.subs[work->descr->last_change.len] != 0) {
474 				seterr("last_change: bad response");
475 				return (-1);
476 			}
477 			if (b->syntax != SNMP_SYNTAX_TIMETICKS) {
478 				seterr("last_change: bad syntax %u", b->syntax);
479 				return (-1);
480 			}
481 			if (work->first) {
482 				work->last_change = b->v.uint32;
483 				work->first = 0;
484 
485 			} else if (work->last_change != b->v.uint32) {
486 				if (++work->iter >= work->descr->max_iter) {
487 					seterr("max iteration count exceeded");
488 					return (-1);
489 				}
490 				table_free(work, 1);
491 				return (-2);
492 			}
493 
494 			continue;
495 		}
496 		if (!asn_is_suboid(&work->descr->table, &b->var) ||
497 		    b->syntax == SNMP_SYNTAX_ENDOFMIBVIEW)
498 			return (0);
499 
500 		if ((e = table_find(work, &b->var)) == NULL)
501 			return (-1);
502 		if (table_value(work->descr, e, b))
503 			return (-1);
504 	}
505 	return (+1);
506 }
507 
508 /*
509  * Check table consistency
510  */
511 static int
512 table_check_cons(struct tabwork *work)
513 {
514 	struct entry *e;
515 
516 	TAILQ_FOREACH(e, work->table, link)
517 		if ((e->found & work->descr->req_mask) !=
518 		    work->descr->req_mask) {
519 			if (work->descr->last_change.len == 0) {
520 				if (++work->iter >= work->descr->max_iter) {
521 					seterr("max iteration count exceeded");
522 					return (-1);
523 				}
524 				return (-2);
525 			}
526 			seterr("inconsistency detected %llx %llx",
527 			    e->found, work->descr->req_mask);
528 			return (-1);
529 		}
530 	return (0);
531 }
532 
533 /*
534  * Fetch a table. Returns 0 if ok, -1 on errors.
535  * This is the synchronuous variant.
536  */
537 int
538 snmp_table_fetch(const struct snmp_table *descr, void *list)
539 {
540 	struct snmp_pdu resp;
541 	struct tabwork work;
542 	int ret;
543 
544 	work.descr = descr;
545 	work.table = (struct table *)list;
546 	work.iter = 0;
547 	TAILQ_INIT(work.table);
548 	TAILQ_INIT(&work.worklist);
549 	work.callback = NULL;
550 	work.arg = NULL;
551 
552   again:
553 	/*
554 	 * We come to this label when the code detects that the table
555 	 * has changed while fetching it.
556 	 */
557 	work.first = 1;
558 	work.last_change = 0;
559 	table_init_pdu(descr, &work.pdu);
560 
561 	for (;;) {
562 		if (snmp_dialog(&work.pdu, &resp)) {
563 			seterr("SNMP error: no response");
564 			table_free(&work, 1);
565 			return (-1);
566 		}
567 		if ((ret = table_check_response(&work, &resp)) == 0) {
568 			snmp_pdu_free(&resp);
569 			break;
570 		}
571 		if (ret == -1) {
572 			snmp_pdu_free(&resp);
573 			table_free(&work, 1);
574 			return (-1);
575 		}
576 		if (ret == -2) {
577 			snmp_pdu_free(&resp);
578 			goto again;
579 		}
580 
581 		work.pdu.bindings[work.pdu.nbindings - 1].var =
582 		    resp.bindings[resp.nbindings - 1].var;
583 
584 		snmp_pdu_free(&resp);
585 	}
586 
587 	if ((ret = table_check_cons(&work)) == -1) {
588 		table_free(&work, 1);
589 		return (-1);
590 	}
591 	if (ret == -2) {
592 		table_free(&work, 1);
593 		goto again;
594 	}
595 	/*
596 	 * Free index list
597 	 */
598 	table_free(&work, 0);
599 	return (0);
600 }
601 
602 /*
603  * Callback for table
604  */
605 static void
606 table_cb(struct snmp_pdu *req __unused, struct snmp_pdu *resp, void *arg)
607 {
608 	struct tabwork *work = arg;
609 	int ret;
610 
611 	if (resp == NULL) {
612 		/* timeout */
613 		seterr("no response to fetch table request");
614 		table_free(work, 1);
615 		work->callback(work->table, work->arg, -1);
616 		free(work);
617 		return;
618 	}
619 
620 	if ((ret = table_check_response(work, resp)) == 0) {
621 		/* EOT */
622 		snmp_pdu_free(resp);
623 
624 		if ((ret = table_check_cons(work)) == -1) {
625 			/* error happend */
626 			table_free(work, 1);
627 			work->callback(work->table, work->arg, -1);
628 			free(work);
629 			return;
630 		}
631 		if (ret == -2) {
632 			/* restart */
633   again:
634 			table_free(work, 1);
635 			work->first = 1;
636 			work->last_change = 0;
637 			table_init_pdu(work->descr, &work->pdu);
638 			if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) {
639 				work->callback(work->table, work->arg, -1);
640 				free(work);
641 				return;
642 			}
643 			return;
644 		}
645 		/*
646 		 * Free index list
647 		 */
648 		table_free(work, 0);
649 		work->callback(work->table, work->arg, 0);
650 		free(work);
651 		return;
652 	}
653 
654 	if (ret == -1) {
655 		/* error */
656 		snmp_pdu_free(resp);
657 		table_free(work, 1);
658 		work->callback(work->table, work->arg, -1);
659 		free(work);
660 		return;
661 	}
662 
663 	if (ret == -2) {
664 		/* again */
665 		snmp_pdu_free(resp);
666 		goto again;
667 	}
668 
669 	/* next part */
670 
671 	work->pdu.bindings[work->pdu.nbindings - 1].var =
672 	    resp->bindings[resp->nbindings - 1].var;
673 
674 	snmp_pdu_free(resp);
675 
676 	if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) {
677 		table_free(work, 1);
678 		work->callback(work->table, work->arg, -1);
679 		free(work);
680 		return;
681 	}
682 }
683 
684 int
685 snmp_table_fetch_async(const struct snmp_table *descr, void *list,
686     snmp_table_cb_f func, void *arg)
687 {
688 	struct tabwork *work;
689 
690 	if ((work = malloc(sizeof(*work))) == NULL) {
691 		seterr("%s", strerror(errno));
692 		return (-1);
693 	}
694 
695 	work->descr = descr;
696 	work->table = (struct table *)list;
697 	work->iter = 0;
698 	TAILQ_INIT(work->table);
699 	TAILQ_INIT(&work->worklist);
700 
701 	work->callback = func;
702 	work->arg = arg;
703 
704 	/*
705 	 * Start by sending the first PDU
706 	 */
707 	work->first = 1;
708 	work->last_change = 0;
709 	table_init_pdu(descr, &work->pdu);
710 
711 	if (snmp_pdu_send(&work->pdu, table_cb, work) == -1)
712 		return (-1);
713 	return (0);
714 }
715 
716 /*
717  * Append an index to an oid
718  */
719 int
720 snmp_oid_append(struct asn_oid *oid, const char *fmt, ...)
721 {
722 	va_list	va;
723 	int	size;
724 	char	*nextptr;
725 	const u_char *str;
726 	size_t	len;
727 	struct in_addr ina;
728 	int ret;
729 
730 	va_start(va, fmt);
731 
732 	size = 0;
733 
734 	ret = 0;
735 	while (*fmt != '\0') {
736 		switch (*fmt++) {
737 		  case 'i':
738 			/* just an integer more */
739 			if (oid->len + 1 > ASN_MAXOIDLEN) {
740 				warnx("%s: OID too long for integer", __func__);
741 				ret = -1;
742 				break;
743 			}
744 			oid->subs[oid->len++] = va_arg(va, asn_subid_t);
745 			break;
746 
747 		  case 'a':
748 			/* append an IP address */
749 			if (oid->len + 4 > ASN_MAXOIDLEN) {
750 				warnx("%s: OID too long for ip-addr", __func__);
751 				ret = -1;
752 				break;
753 			}
754 			ina = va_arg(va, struct in_addr);
755 			ina.s_addr = ntohl(ina.s_addr);
756 			oid->subs[oid->len++] = (ina.s_addr >> 24) & 0xff;
757 			oid->subs[oid->len++] = (ina.s_addr >> 16) & 0xff;
758 			oid->subs[oid->len++] = (ina.s_addr >> 8) & 0xff;
759 			oid->subs[oid->len++] = (ina.s_addr >> 0) & 0xff;
760 			break;
761 
762 		  case 's':
763 			/* append a null-terminated string,
764 			 * length is computed */
765 			str = (const u_char *)va_arg(va, const char *);
766 			len = strlen((const char *)str);
767 			if (oid->len + len + 1 > ASN_MAXOIDLEN) {
768 				warnx("%s: OID too long for string", __func__);
769 				ret = -1;
770 				break;
771 			}
772 			oid->subs[oid->len++] = len;
773 			while (len--)
774 				oid->subs[oid->len++] = *str++;
775 			break;
776 
777 		  case '(':
778 			/* the integer value between ( and ) is stored
779 			 * in size */
780 			size = strtol(fmt, &nextptr, 10);
781 			if (*nextptr != ')')
782 				abort();
783 			fmt = ++nextptr;
784 			break;
785 
786 		  case 'b':
787 			/* append `size` characters */
788 			str = (const u_char *)va_arg(va, const char *);
789 			if (oid->len + size > ASN_MAXOIDLEN) {
790 				warnx("%s: OID too long for string", __func__);
791 				ret = -1;
792 				break;
793 			}
794 			while (size--)
795 				oid->subs[oid->len++] = *str++;
796 			break;
797 
798 		  case 'c':
799 			/* get size and the octets from the arguments */
800 			size = va_arg(va, size_t);
801 			str = va_arg(va, const u_char *);
802 			if (oid->len + size + 1 > ASN_MAXOIDLEN) {
803 				warnx("%s: OID too long for string", __func__);
804 				ret = -1;
805 				break;
806 			}
807 			oid->subs[oid->len++] = size;
808 			while (size--)
809 				oid->subs[oid->len++] = *str++;
810 			break;
811 
812 		  default:
813 			abort();
814 		}
815 	}
816 	va_end(va);
817 	return (ret);
818 }
819 
820 /*
821  * Initialize a client structure
822  */
823 void
824 snmp_client_init(struct snmp_client *c)
825 {
826 	memset(c, 0, sizeof(*c));
827 
828 	c->version = SNMP_V2c;
829 	c->trans = SNMP_TRANS_UDP;
830 	c->chost = NULL;
831 	c->cport = NULL;
832 
833 	strcpy(c->read_community, "public");
834 	strcpy(c->write_community, "private");
835 
836 	c->timeout.tv_sec = 3;
837 	c->timeout.tv_usec = 0;
838 	c->retries = 3;
839 	c->dump_pdus = 0;
840 	c->txbuflen = c->rxbuflen = 10000;
841 
842 	c->fd = -1;
843 
844 	c->max_reqid = INT32_MAX;
845 	c->min_reqid = 0;
846 	c->next_reqid = 0;
847 }
848 
849 
850 /*
851  * Open UDP client socket
852  */
853 static int
854 open_client_udp(const char *host, const char *port)
855 {
856 	int error;
857 	char *ptr;
858 	struct addrinfo hints, *res0, *res;
859 
860 	/* copy host- and portname */
861 	if (snmp_client.chost == NULL) {
862 		if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_HOST)))
863 		    == NULL) {
864 			seterr("%s", strerror(errno));
865 			return (-1);
866 		}
867 		strcpy(snmp_client.chost, DEFAULT_HOST);
868 	}
869 	if (host != NULL) {
870 		if ((ptr = malloc(1 + strlen(host))) == NULL) {
871 			seterr("%s", strerror(errno));
872 			return (-1);
873 		}
874 		free(snmp_client.chost);
875 		snmp_client.chost = ptr;
876 		strcpy(snmp_client.chost, host);
877 	}
878 	if (snmp_client.cport == NULL) {
879 		if ((snmp_client.cport = malloc(1 + sizeof(DEFAULT_PORT)))
880 		    == NULL) {
881 			seterr("%s", strerror(errno));
882 			return (-1);
883 		}
884 		strcpy(snmp_client.cport, DEFAULT_PORT);
885 	}
886 	if (port != NULL) {
887 		if ((ptr = malloc(1 + strlen(port))) == NULL) {
888 			seterr("%s", strerror(errno));
889 			return (-1);
890 		}
891 		free(snmp_client.cport);
892 		snmp_client.cport = ptr;
893 		strcpy(snmp_client.cport, port);
894 	}
895 
896 	/* open connection */
897 	memset(&hints, 0, sizeof(hints));
898 	hints.ai_flags = AI_CANONNAME;
899 	hints.ai_family = AF_INET;
900 	hints.ai_socktype = SOCK_DGRAM;
901 	hints.ai_protocol = 0;
902 	error = getaddrinfo(snmp_client.chost, snmp_client.cport, &hints, &res0);
903 	if (error != 0) {
904 		seterr("%s: %s", snmp_client.chost, gai_strerror(error));
905 		return (-1);
906 	}
907 	res = res0;
908 	for (;;) {
909 		if ((snmp_client.fd = socket(res->ai_family, res->ai_socktype,
910 		    res->ai_protocol)) == -1) {
911 			if ((res = res->ai_next) == NULL) {
912 				seterr("%s", strerror(errno));
913 				freeaddrinfo(res0);
914 				return (-1);
915 			}
916 		} else if (connect(snmp_client.fd, res->ai_addr,
917 		    res->ai_addrlen) == -1) {
918 			if ((res = res->ai_next) == NULL) {
919 				seterr("%s", strerror(errno));
920 				freeaddrinfo(res0);
921 				return (-1);
922 			}
923 		} else
924 			break;
925 	}
926 	freeaddrinfo(res0);
927 	return (0);
928 }
929 
930 static void
931 remove_local(void)
932 {
933 	(void)remove(snmp_client.local_path);
934 }
935 
936 /*
937  * Open local socket
938  */
939 static int
940 open_client_local(const char *path)
941 {
942 	struct sockaddr_un sa;
943 	char *ptr;
944 	int stype;
945 
946 	if (snmp_client.chost == NULL) {
947 		if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_LOCAL)))
948 		    == NULL) {
949 			seterr("%s", strerror(errno));
950 			return (-1);
951 		}
952 		strcpy(snmp_client.chost, DEFAULT_LOCAL);
953 	}
954 	if (path != NULL) {
955 		if ((ptr = malloc(1 + strlen(path))) == NULL) {
956 			seterr("%s", strerror(errno));
957 			return (-1);
958 		}
959 		free(snmp_client.chost);
960 		snmp_client.chost = ptr;
961 		strcpy(snmp_client.chost, path);
962 	}
963 
964 	if (snmp_client.trans == SNMP_TRANS_LOC_DGRAM)
965 		stype = SOCK_DGRAM;
966 	else
967 		stype = SOCK_STREAM;
968 
969 	if ((snmp_client.fd = socket(PF_LOCAL, stype, 0)) == -1) {
970 		seterr("%s", strerror(errno));
971 		return (-1);
972 	}
973 
974 	snprintf(snmp_client.local_path, sizeof(snmp_client.local_path),
975 	    "%s", SNMP_LOCAL_PATH);
976 
977 	if (mktemp(snmp_client.local_path) == NULL) {
978 		seterr("%s", strerror(errno));
979 		(void)close(snmp_client.fd);
980 		snmp_client.fd = -1;
981 		return (-1);
982 	}
983 
984 	sa.sun_family = AF_LOCAL;
985 	sa.sun_len = sizeof(sa);
986 	strcpy(sa.sun_path, snmp_client.local_path);
987 
988 	if (bind(snmp_client.fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
989 		seterr("%s", strerror(errno));
990 		(void)close(snmp_client.fd);
991 		snmp_client.fd = -1;
992 		(void)remove(snmp_client.local_path);
993 		return (-1);
994 	}
995 	atexit(remove_local);
996 
997 	sa.sun_family = AF_LOCAL;
998 	sa.sun_len = offsetof(struct sockaddr_un, sun_path) +
999 	    strlen(snmp_client.chost);
1000 	strncpy(sa.sun_path, snmp_client.chost, sizeof(sa.sun_path) - 1);
1001 	sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
1002 
1003 	if (connect(snmp_client.fd, (struct sockaddr *)&sa, sa.sun_len) == -1) {
1004 		seterr("%s", strerror(errno));
1005 		(void)close(snmp_client.fd);
1006 		snmp_client.fd = -1;
1007 		(void)remove(snmp_client.local_path);
1008 		return (-1);
1009 	}
1010 	return (0);
1011 }
1012 
1013 /*
1014  * SNMP_OPEN
1015  */
1016 int
1017 snmp_open(const char *host, const char *port, const char *readcomm,
1018     const char *writecomm)
1019 {
1020 	struct timeval tout;
1021 
1022 	/* still open ? */
1023 	if (snmp_client.fd != -1) {
1024 		errno = EBUSY;
1025 		seterr("%s", strerror(errno));
1026 		return (-1);
1027 	}
1028 
1029 	/* copy community strings */
1030 	if (readcomm != NULL)
1031 		strlcpy(snmp_client.read_community, readcomm,
1032 		    sizeof(snmp_client.read_community));
1033 	if (writecomm != NULL)
1034 		strlcpy(snmp_client.write_community, writecomm,
1035 		    sizeof(snmp_client.write_community));
1036 
1037 	switch (snmp_client.trans) {
1038 
1039 	  case SNMP_TRANS_UDP:
1040 		if (open_client_udp(host, port))
1041 			return (-1);
1042 		break;
1043 
1044 	  case SNMP_TRANS_LOC_DGRAM:
1045 	  case SNMP_TRANS_LOC_STREAM:
1046 		if (open_client_local(host))
1047 			return (-1);
1048 		break;
1049 
1050 	  default:
1051 		seterr("bad transport mapping");
1052 		return (-1);
1053 	}
1054 	tout.tv_sec = 0;
1055 	tout.tv_usec = 0;
1056 	if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_SNDTIMEO,
1057 	    &tout, sizeof(struct timeval)) == -1) {
1058 		seterr("%s", strerror(errno));
1059 		(void)close(snmp_client.fd);
1060 		snmp_client.fd = -1;
1061 		if (snmp_client.local_path[0] != '\0')
1062 			(void)remove(snmp_client.local_path);
1063 		return (-1);
1064 	}
1065 
1066 	/* initialize list */
1067 	LIST_INIT(&sent_pdus);
1068 
1069 	return (0);
1070 }
1071 
1072 
1073 /*
1074  * SNMP_CLOSE
1075  *
1076  * closes connection to snmp server
1077  * - function cannot fail
1078  * - clears connection
1079  * - clears list of sent pdus
1080  *
1081  * input:
1082  *  void
1083  * return:
1084  *  void
1085  */
1086 void
1087 snmp_close(void)
1088 {
1089 	struct sent_pdu *p1;
1090 
1091 	if (snmp_client.fd != -1) {
1092 		(void)close(snmp_client.fd);
1093 		snmp_client.fd = -1;
1094 		if (snmp_client.local_path[0] != '\0')
1095 			(void)remove(snmp_client.local_path);
1096 	}
1097 	while(!LIST_EMPTY(&sent_pdus)){
1098 		p1 = LIST_FIRST(&sent_pdus);
1099 		if (p1->timeout_id != NULL)
1100 			snmp_client.timeout_stop(p1->timeout_id);
1101 		LIST_REMOVE(p1, entries);
1102 		free(p1);
1103 	}
1104 	free(snmp_client.chost);
1105 	free(snmp_client.cport);
1106 }
1107 
1108 /*
1109  * initialize a snmp_pdu structure
1110  */
1111 void
1112 snmp_pdu_create(struct snmp_pdu *pdu, u_int op)
1113 {
1114 	memset(pdu,0,sizeof(struct snmp_pdu));
1115 	if (op == SNMP_PDU_SET)
1116 		strlcpy(pdu->community, snmp_client.write_community,
1117 		    sizeof(pdu->community));
1118 	else
1119 		strlcpy(pdu->community, snmp_client.read_community,
1120 		    sizeof(pdu->community));
1121 
1122 	pdu->type = op;
1123 	pdu->version = snmp_client.version;
1124 	pdu->error_status = 0;
1125 	pdu->error_index = 0;
1126 	pdu->nbindings = 0;
1127 }
1128 
1129 /* add pairs of (struct asn_oid, enum snmp_syntax) to an existing pdu */
1130 /* added 10/04/02 by kek: check for MAX_BINDINGS */
1131 int
1132 snmp_add_binding(struct snmp_v1_pdu *pdu, ...)
1133 {
1134 	va_list ap;
1135 	const struct asn_oid *oid;
1136 	u_int ret;
1137 
1138 	va_start(ap, pdu);
1139 
1140 	ret = pdu->nbindings;
1141 	while ((oid = va_arg(ap, const struct asn_oid *)) != NULL) {
1142 		if (pdu->nbindings >= SNMP_MAX_BINDINGS){
1143 			va_end(ap);
1144 			return (-1);
1145 		}
1146 		pdu->bindings[pdu->nbindings].var = *oid;
1147 		pdu->bindings[pdu->nbindings].syntax =
1148 		    va_arg(ap, enum snmp_syntax);
1149 		pdu->nbindings++;
1150 	}
1151 	va_end(ap);
1152 	return (ret);
1153 }
1154 
1155 
1156 static int32_t
1157 snmp_next_reqid(struct snmp_client * c)
1158 {
1159 	int32_t i;
1160 
1161 	i = c->next_reqid;
1162 	if (c->next_reqid >= c->max_reqid)
1163 		c->next_reqid = c->min_reqid;
1164 	else
1165 		c->next_reqid++;
1166 	return (i);
1167 }
1168 
1169 /*
1170  * Send request and return request id.
1171  */
1172 static int32_t
1173 snmp_send_packet(struct snmp_pdu * pdu)
1174 {
1175         u_char *buf;
1176         struct asn_buf b;
1177         ssize_t ret;
1178 
1179 	if ((buf = malloc(snmp_client.txbuflen)) == NULL) {
1180 		seterr("%s", strerror(errno));
1181 		return (-1);
1182 	}
1183 
1184         pdu->request_id = snmp_next_reqid(&snmp_client);
1185 
1186         b.asn_ptr = buf;
1187         b.asn_len = snmp_client.txbuflen;
1188         if (snmp_pdu_encode(pdu, &b)) {
1189 		seterr("%s", strerror(errno));
1190 		free(buf);
1191 		return (-1);
1192 	}
1193 
1194         if (snmp_client.dump_pdus)
1195                 snmp_pdu_dump(pdu);
1196 
1197         if ((ret = send(snmp_client.fd, buf, b.asn_ptr - buf, 0)) == -1) {
1198 		seterr("%s", strerror(errno));
1199 		free(buf);
1200                 return (-1);
1201 	}
1202 	free(buf);
1203 
1204 	return pdu->request_id;
1205 }
1206 
1207 /*
1208  * to be called when a snmp request timed out
1209  */
1210 static void
1211 snmp_timeout(void * listentry_ptr)
1212 {
1213 	struct sent_pdu *listentry = listentry_ptr;
1214 
1215 #if 0
1216 	warnx("snmp request %i timed out, attempt (%i/%i)",
1217 	    listentry->reqid, listentry->retrycount, snmp_client.retries);
1218 #endif
1219 
1220 	listentry->retrycount++;
1221 	if (listentry->retrycount > snmp_client.retries) {
1222 		/* there is no answer at all */
1223 		LIST_REMOVE(listentry, entries);
1224 		listentry->callback(listentry->pdu, NULL, listentry->arg);
1225 		free(listentry);
1226 	} else {
1227 		/* try again */
1228 		/* new request with new request ID */
1229 		listentry->reqid = snmp_send_packet(listentry->pdu);
1230 		listentry->timeout_id =
1231 		    snmp_client.timeout_start(&snmp_client.timeout,
1232 		    snmp_timeout, listentry);
1233 	}
1234 }
1235 
1236 int32_t
1237 snmp_pdu_send(struct snmp_pdu *pdu, snmp_send_cb_f func, void *arg)
1238 {
1239 	struct sent_pdu *listentry;
1240 	int32_t id;
1241 
1242 	if ((listentry = malloc(sizeof(struct sent_pdu))) == NULL) {
1243 		seterr("%s", strerror(errno));
1244 		return (-1);
1245 	}
1246 
1247 	/* here we really send */
1248 	if ((id = snmp_send_packet(pdu)) == -1) {
1249 		free(listentry);
1250 		return (-1);
1251 	}
1252 
1253 	/* add entry to list of sent PDUs */
1254 	listentry->pdu = pdu;
1255 	if (gettimeofday(&listentry->time, NULL) == -1)
1256 		warn("gettimeofday() failed");
1257 
1258 	listentry->reqid = pdu->request_id;
1259 	listentry->callback = func;
1260 	listentry->arg = arg;
1261 	listentry->retrycount=1;
1262 	listentry->timeout_id =
1263 	    snmp_client.timeout_start(&snmp_client.timeout, snmp_timeout,
1264 	    listentry);
1265 
1266 	LIST_INSERT_HEAD(&sent_pdus, listentry, entries);
1267 
1268 	return (id);
1269 }
1270 
1271 /*
1272  * Receive an SNMP packet.
1273  *
1274  * tv controls how we wait for a packet: if tv is a NULL pointer,
1275  * the receive blocks forever, if tv points to a structure with all
1276  * members 0 the socket is polled, in all other cases tv specifies the
1277  * maximum time to wait for a packet.
1278  *
1279  * Return:
1280  *	-1 on errors
1281  *	0 on timeout
1282  *	+1 if packet received
1283  */
1284 static int
1285 snmp_receive_packet(struct snmp_pdu *pdu, struct timeval *tv)
1286 {
1287 	int dopoll, setpoll;
1288 	int flags;
1289 	int saved_errno;
1290 	u_char *buf;
1291 	int ret;
1292 	struct asn_buf abuf;
1293 	int32_t ip;
1294 	socklen_t optlen;
1295 
1296 	if ((buf = malloc(snmp_client.rxbuflen)) == NULL) {
1297 		seterr("%s", strerror(errno));
1298 		return (-1);
1299 	}
1300 	dopoll = setpoll = 0;
1301 	flags = 0;
1302 	if (tv != NULL) {
1303 		/* poll or timeout */
1304 		if (tv->tv_sec != 0 || tv->tv_usec != 0) {
1305 			/* wait with timeout */
1306 			if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO,
1307 			    tv, sizeof(*tv)) == -1) {
1308 				seterr("setsockopt: %s", strerror(errno));
1309 				free(buf);
1310 				return (-1);
1311 			}
1312 			optlen = sizeof(*tv);
1313 			if (getsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO,
1314 			    tv, &optlen) == -1) {
1315 				seterr("getsockopt: %s", strerror(errno));
1316 				free(buf);
1317 				return (-1);
1318 			}
1319 			/* at this point tv_sec and tv_usec may appear
1320 			 * as 0. This happens for timeouts lesser than
1321 			 * the clock granularity. The kernel rounds these to
1322 			 * 0 and this would result in a blocking receive.
1323 			 * Instead of an else we check tv_sec and tv_usec
1324 			 * again below and if this rounding happens,
1325 			 * switch to a polling receive. */
1326 		}
1327 		if (tv->tv_sec == 0 && tv->tv_usec == 0) {
1328 			/* poll */
1329 			dopoll = 1;
1330 			if ((flags = fcntl(snmp_client.fd, F_GETFL, 0)) == -1) {
1331 				seterr("fcntl: %s", strerror(errno));
1332 				free(buf);
1333 				return (-1);
1334 			}
1335 			if (!(flags & O_NONBLOCK)) {
1336 				setpoll = 1;
1337 				flags |= O_NONBLOCK;
1338 				if (fcntl(snmp_client.fd, F_SETFL, flags) == -1) {
1339 					seterr("fcntl: %s", strerror(errno));
1340 					free(buf);
1341 					return (-1);
1342 				}
1343 			}
1344 		}
1345 	}
1346 	ret = recv(snmp_client.fd, buf, snmp_client.rxbuflen, 0);
1347 	saved_errno = errno;
1348 	if (tv != NULL) {
1349 		if (dopoll) {
1350 			if (setpoll) {
1351 				flags &= ~O_NONBLOCK;
1352 				(void)fcntl(snmp_client.fd, F_SETFL, flags);
1353 			}
1354 		} else {
1355 			tv->tv_sec = 0;
1356 			tv->tv_usec = 0;
1357 			(void)setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO,
1358 			    tv, sizeof(*tv));
1359 		}
1360 	}
1361 	if (ret == -1) {
1362 		free(buf);
1363 		if (errno == EAGAIN || errno == EWOULDBLOCK)
1364 			return (0);
1365 		seterr("recv: %s", strerror(saved_errno));
1366 		return (-1);
1367 	}
1368 	if (ret == 0)
1369 		abort();
1370 
1371 	abuf.asn_ptr = buf;
1372 	abuf.asn_len = ret;
1373 
1374 	if (SNMP_CODE_OK != (ret = snmp_pdu_decode(&abuf, pdu, &ip))) {
1375 		seterr("snmp_decode_pdu: failed %d", ret);
1376 		free(buf);
1377 		return (-1);
1378 	}
1379 	free(buf);
1380 	if (snmp_client.dump_pdus)
1381 		snmp_pdu_dump(pdu);
1382 
1383 	return (+1);
1384 }
1385 
1386 static int
1387 snmp_deliver_packet(struct snmp_pdu * resp)
1388 {
1389 	struct sent_pdu *listentry;
1390 
1391 	if (resp->type != SNMP_PDU_RESPONSE) {
1392 		warn("ignoring snmp pdu %u", resp->type);
1393 		return (-1);
1394 	}
1395 
1396 	LIST_FOREACH(listentry, &sent_pdus, entries)
1397 		if (listentry->reqid == resp->request_id)
1398 			break;
1399 	if (listentry == NULL)
1400 		return (-1);
1401 
1402 	LIST_REMOVE(listentry, entries);
1403 	listentry->callback(listentry->pdu, resp, listentry->arg);
1404 
1405 	snmp_client.timeout_stop(listentry->timeout_id);
1406 
1407 	free(listentry);
1408 	return (0);
1409 }
1410 
1411 int
1412 snmp_receive(int blocking)
1413 {
1414 	int ret;
1415 
1416 	struct timeval tv;
1417 	struct snmp_pdu * resp;
1418 
1419 	memset(&tv, 0, sizeof(tv));
1420 
1421 	resp = malloc(sizeof(struct snmp_pdu));
1422 	if (resp == NULL) {
1423 		seterr("no memory for returning PDU");
1424 		return (-1) ;
1425 	}
1426 
1427 	if ((ret = snmp_receive_packet(resp, blocking ? NULL : &tv)) <= 0) {
1428 		free(resp);
1429 		return (ret);
1430 	}
1431 	ret = snmp_deliver_packet(resp);
1432 	snmp_pdu_free(resp);
1433 	free(resp);
1434 	return (ret);
1435 }
1436 
1437 
1438 /*
1439  * Check a GETNEXT response. Here we have three possible outcomes: -1 an
1440  * unexpected error happened. +1 response is ok and is within the table 0
1441  * response is ok, but is behind the table or error is NOSUCHNAME. The req
1442  * should point to a template PDU which contains the base OIDs and the
1443  * syntaxes. This is really only useful to sweep non-sparse tables.
1444  */
1445 static int
1446 ok_getnext(const struct snmp_pdu * req, const struct snmp_pdu * resp)
1447 {
1448 	u_int i;
1449 
1450 	if (resp->version != req->version) {
1451 		warnx("SNMP GETNEXT: response has wrong version");
1452 		return (-1);
1453 	}
1454 
1455 	if (resp->error_status == SNMP_ERR_NOSUCHNAME)
1456 		return (0);
1457 
1458 	if (resp->error_status != SNMP_ERR_NOERROR) {
1459 		warnx("SNMP GETNEXT: error %d", resp->error_status);
1460 		return (-1);
1461 	}
1462 	if (resp->nbindings != req->nbindings) {
1463 		warnx("SNMP GETNEXT: bad number of bindings in response");
1464 		return (-1);
1465 	}
1466 	for (i = 0; i < req->nbindings; i++) {
1467 		if (!asn_is_suboid(&req->bindings[i].var,
1468 		    &resp->bindings[i].var)) {
1469 			if (i != 0)
1470 				warnx("SNMP GETNEXT: inconsistent table "
1471 				      "response");
1472 			return (0);
1473 		}
1474 		if (resp->version != SNMP_V1 &&
1475 		    resp->bindings[i].syntax == SNMP_SYNTAX_ENDOFMIBVIEW)
1476 			return (0);
1477 
1478 		if (resp->bindings[i].syntax != req->bindings[i].syntax) {
1479 			warnx("SNMP GETNEXT: bad syntax in response");
1480 			return (0);
1481 		}
1482 	}
1483 	return (1);
1484 }
1485 
1486 /*
1487  * Check a GET response. Here we have three possible outcomes: -1 an
1488  * unexpected error happened. +1 response is ok. 0 NOSUCHNAME The req should
1489  * point to a template PDU which contains the OIDs and the syntaxes. This
1490  * is only useful for SNMPv1 or single object GETS.
1491  */
1492 static int
1493 ok_get(const struct snmp_pdu * req, const struct snmp_pdu * resp)
1494 {
1495 	u_int i;
1496 
1497 	if (resp->version != req->version) {
1498 		warnx("SNMP GET: response has wrong version");
1499 		return (-1);
1500 	}
1501 
1502 	if (resp->error_status == SNMP_ERR_NOSUCHNAME)
1503 		return (0);
1504 
1505 	if (resp->error_status != SNMP_ERR_NOERROR) {
1506 		warnx("SNMP GET: error %d", resp->error_status);
1507 		return (-1);
1508 	}
1509 
1510 	if (resp->nbindings != req->nbindings) {
1511 		warnx("SNMP GET: bad number of bindings in response");
1512 		return (-1);
1513 	}
1514 	for (i = 0; i < req->nbindings; i++) {
1515 		if (asn_compare_oid(&req->bindings[i].var,
1516 		    &resp->bindings[i].var) != 0) {
1517 			warnx("SNMP GET: bad OID in response");
1518 			return (-1);
1519 		}
1520 		if (snmp_client.version != SNMP_V1 &&
1521 		    (resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHOBJECT ||
1522 		    resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHINSTANCE))
1523 			return (0);
1524 		if (resp->bindings[i].syntax != req->bindings[i].syntax) {
1525 			warnx("SNMP GET: bad syntax in response");
1526 			return (-1);
1527 		}
1528 	}
1529 	return (1);
1530 }
1531 
1532 /*
1533  * Check the reponse to a SET PDU. We check: - the error status must be 0 -
1534  * the number of bindings must be equal in response and request - the
1535  * syntaxes must be the same in response and request - the OIDs must be the
1536  * same in response and request
1537  */
1538 static int
1539 ok_set(const struct snmp_pdu * req, const struct snmp_pdu * resp)
1540 {
1541 	u_int i;
1542 
1543 	if (resp->version != req->version) {
1544 		warnx("SNMP SET: response has wrong version");
1545 		return (-1);
1546 	}
1547 
1548 	if (resp->error_status == SNMP_ERR_NOSUCHNAME) {
1549 		warnx("SNMP SET: error %d", resp->error_status);
1550 		return (0);
1551 	}
1552 	if (resp->error_status != SNMP_ERR_NOERROR) {
1553 		warnx("SNMP SET: error %d", resp->error_status);
1554 		return (-1);
1555 	}
1556 
1557 	if (resp->nbindings != req->nbindings) {
1558 		warnx("SNMP SET: bad number of bindings in response");
1559 		return (-1);
1560 	}
1561 	for (i = 0; i < req->nbindings; i++) {
1562 		if (asn_compare_oid(&req->bindings[i].var,
1563 		    &resp->bindings[i].var) != 0) {
1564 			warnx("SNMP SET: wrong OID in response to SET");
1565 			return (-1);
1566 		}
1567 		if (resp->bindings[i].syntax != req->bindings[i].syntax) {
1568 			warnx("SNMP SET: bad syntax in response");
1569 			return (-1);
1570 		}
1571 	}
1572 	return (1);
1573 }
1574 
1575 /*
1576  * Simple checks for response PDUs against request PDUs. Return values: 1=ok,
1577  * 0=nosuchname or similar, -1=failure, -2=no response at all
1578  */
1579 int
1580 snmp_pdu_check(const struct snmp_pdu *req,
1581     const struct snmp_pdu *resp)
1582 {
1583 	if (resp == NULL)
1584 		return (-2);
1585 
1586 	switch (req->type) {
1587 
1588 	  case SNMP_PDU_GET:
1589 		return (ok_get(req, resp));
1590 
1591 	  case SNMP_PDU_SET:
1592 		return (ok_set(req, resp));
1593 
1594 	  case SNMP_PDU_GETNEXT:
1595 		return (ok_getnext(req, resp));
1596 
1597 	}
1598 	errx(1, "%s: bad pdu type %i", __func__, req->type);
1599 }
1600 
1601 int
1602 snmp_dialog(struct snmp_v1_pdu *req, struct snmp_v1_pdu *resp)
1603 {
1604         u_int i;
1605         int32_t reqid;
1606 	int ret;
1607         struct timeval tv = snmp_client.timeout;
1608 	struct timeval end;
1609 
1610         for (i = 0; i <= snmp_client.retries; i++) {
1611 		(void)gettimeofday(&end, NULL);
1612 		timeradd(&end, &snmp_client.timeout, &end);
1613                 if ((reqid = snmp_send_packet(req)) == -1)
1614 			return (-1);
1615 		for (;;) {
1616 			(void)gettimeofday(&tv, NULL);
1617 			if (timercmp(&end, &tv, <=))
1618 				break;
1619 			timersub(&end, &tv, &tv);
1620 			if ((ret = snmp_receive_packet(resp, &tv)) == 0)
1621 				/* timeout */
1622 				break;
1623 
1624 			if (ret > 0) {
1625 				if (reqid == resp->request_id)
1626 					return (0);
1627 				/* not for us */
1628 				(void)snmp_deliver_packet(resp);
1629 			}
1630 		}
1631         }
1632 	seterr("retry count exceeded");
1633         return (-1);
1634 }
1635 
1636 int
1637 snmp_client_set_host(struct snmp_client *cl, const char *h)
1638 {
1639 	char *np;
1640 
1641 	if (h == NULL) {
1642 		if (cl->chost != NULL)
1643 			free(cl->chost);
1644 		cl->chost = NULL;
1645 	} else {
1646 		if ((np = malloc(strlen(h) + 1)) == NULL)
1647 			return (-1);
1648 		strcpy(np, h);
1649 		if (cl->chost != NULL)
1650 			free(cl->chost);
1651 		cl->chost = np;
1652 	}
1653 	return (0);
1654 }
1655 
1656 int
1657 snmp_client_set_port(struct snmp_client *cl, const char *p)
1658 {
1659 	char *np;
1660 
1661 	if (p == NULL) {
1662 		if (cl->cport != NULL)
1663 			free(cl->cport);
1664 		cl->cport = NULL;
1665 	} else {
1666 		if ((np = malloc(strlen(p) + 1)) == NULL)
1667 			return (-1);
1668 		strcpy(np, p);
1669 		if (cl->cport != NULL)
1670 			free(cl->cport);
1671 		cl->cport = np;
1672 	}
1673 	return (0);
1674 }
1675