xref: /onnv-gate/usr/src/cmd/agents/snmp/snmplib/snmp_api.c (revision 0:68f95e015346)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1998 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/time.h>
33 #include <netinet/in.h>
34 #include <sys/socket.h>
35 #include <netdb.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <string.h>
39 
40 #include "snmp_msg.h"
41 #include "snmp_api.h"
42 #include "error.h"
43 
44 
45 /***** GLOBAL VARIABLES *****/
46 
47 int snmp_errno = 0;
48 
49 
50 /***** LOCAL CONSTANTS *****/
51 
52 #define DEFAULT_COMMUNITY	"public"
53 #define DEFAULT_RETRIES		4
54 #define DEFAULT_TIMEOUT		1000000L
55 #define DEFAULT_REMPORT		SNMP_PORT
56 #define DEFAULT_LOCPORT		0
57 #define DEFAULT_ENTERPRISE	&sun_oid
58 
59 
60 /***** LOCAL TYPES *****/
61 
62 /*
63  *	A list of all the outstanding requests
64  *	for a particular session
65  */
66 
67 typedef struct _SNMP_request_list {
68 	struct _SNMP_request_list *next_request;
69 	uint32_t request_id;	/* request id */
70 	int	predefined_id;
71 	int	retries;	/* Number of retries */
72 	uint32_t	timeout;	/* length to wait for timeout in usec */
73 	struct timeval time;	/* Time this request was made */
74 	struct timeval expire;	/* time this request is due to expire */
75 	SNMP_pdu *pdu;		/* The pdu for this request (saved so it can be retransmitted */
76 } SNMP_request_list;
77 
78 
79 /*
80  *	Internal information about the state of the snmp session
81  */
82 
83 typedef struct _SNMP_internal_session {
84 	int		sd;		/* socket descriptor for this connection */
85 	Address		address;	/* address of connected peer */
86 	SNMP_request_list *requests;	/* Info about outstanding requests */
87 } SNMP_internal_session;
88 
89 
90 /*
91  *	The list of active/open sessions.
92  */
93 
94 typedef struct _SNMP_session_list {
95 	struct _SNMP_session_list *next;
96 	SNMP_session *session;
97 	SNMP_internal_session *internal;
98 } SNMP_session_list;
99 
100 
101 /***** STATIC VARIABLES *****/
102 
103 static SNMP_session_list *first_session = NULL;
104 
105 static uint32_t static_request_id = 0;
106 
107 static char *snmp_api_errors[5] = {
108 	"System error",
109 	"Unknown session",
110 	"Unknown host",
111 	"Invalid local port",
112 	"Unknown Error"
113 };
114 
115 static char static_error_label[500] = "";
116 
117 
118 /***** STATIC FUNCTIONS *****/
119 
120 static char *api_errstring(int snmp_errnumber);
121 /*
122 static init_snmp();
123 */
124 static void free_request_list(SNMP_request_list *rp);
125 static int snmp_session_read_loop(fd_set *fdset);
126 static int snmp_session_timeout_loop();
127 
128 
129 /*******************************************************************/
130 
api_errstring(int snmp_errnumber)131 static char *api_errstring(int snmp_errnumber)
132 {
133 	if(snmp_errnumber <= SNMPERR_SYSERR && snmp_errnumber >= SNMPERR_GENERR)
134 	{
135 		return snmp_api_errors[snmp_errnumber + 5];
136 	}
137 	else
138 	{
139 		return "Unknown Error";
140 	}
141 }
142 
143 
144 /*******************************************************************/
145 
146 /*
147  *	Gets initial request ID for all transactions
148  */
149 
150 /*
151 static init_snmp()
152 {
153 	struct timeval tv;
154 
155 	(void)gettimeofday(&tv, (struct timezone *) 0);
156 	srandom(tv.tv_sec ^ tv.tv_usec);
157 	static_request_id = random();
158 }
159 */
160 
161 
162 /*******************************************************************/
163 
snmp_session_open_default(char * peername,void callback (),void * callback_magic,char * error_label)164 SNMP_session *snmp_session_open_default(char *peername, void callback(), void *callback_magic, char *error_label)
165 {
166 	return snmp_session_open(peername,
167 		NULL, SNMP_DEFAULT_RETRIES, SNMP_DEFAULT_TIMEOUT,
168 		callback, callback_magic, error_label);
169 }
170 
171 
172 /*******************************************************************/
173 
snmp_session_open(char * peername,char * community,int retries,int32_t timeout,void callback (),void * callback_magic,char * error_label)174 SNMP_session *snmp_session_open(char *peername, char *community, int retries, int32_t timeout, void callback(), void *callback_magic, char *error_label)
175 {
176 	SNMP_session_list *slp;
177 	SNMP_internal_session *isp;
178 	SNMP_session *session;
179 
180 	char *peername_dup;
181 	char *community_dup;
182 
183 	u_short remote_port = SNMP_DEFAULT_REMPORT;
184 	u_short local_port = SNMP_DEFAULT_LOCPORT;
185 
186 	struct sockaddr_in me;
187 	IPAddress ip_address;
188 
189 	error_label[0] = '\0';
190 
191 	if(peername == NULL)
192 	{
193 		sprintf(error_label, "BUG: snmp_session_open(): peername is NULL");
194 		return NULL;
195 	}
196 
197 	if(callback == NULL)
198 	{
199 		sprintf(error_label, "BUG: snmp_session_open(): callback is NULL");
200 		return NULL;
201 	}
202 
203 
204 	if(community == SNMP_DEFAULT_COMMUNITY)
205 	{
206 		community = DEFAULT_COMMUNITY;
207 	}
208 
209 	if(retries == SNMP_DEFAULT_RETRIES)
210 	{
211 		retries = DEFAULT_RETRIES;
212 	}
213 
214 	if(timeout == SNMP_DEFAULT_TIMEOUT)
215 	{
216 		timeout = DEFAULT_TIMEOUT;
217 	}
218 
219 	if(remote_port == SNMP_DEFAULT_REMPORT)
220 	{
221 		remote_port = SNMP_PORT;
222 	}
223 
224 	if(local_port == SNMP_DEFAULT_LOCPORT)
225 	{
226 		local_port = DEFAULT_LOCPORT;
227 	}
228 
229 	if(name_to_ip_address(peername, &ip_address, error_label))
230 	{
231 		snmp_errno = SNMPERR_BAD_ADDRESS;
232 		return NULL;
233 	}
234 
235 
236 	/****************************************/
237 	/* 1) allocate the different structures */
238 	/****************************************/
239 
240 	peername_dup = strdup(peername);
241 	if(peername_dup == NULL)
242 	{
243 		sprintf(error_label, ERR_MSG_ALLOC);
244 		snmp_errno = SNMPERR_GENERR;
245 		return NULL;
246 	}
247 
248 	community_dup = strdup(community);
249 	if(community_dup == NULL)
250 	{
251 		sprintf(error_label, ERR_MSG_ALLOC);
252 		snmp_errno = SNMPERR_GENERR;
253 		free(peername_dup);
254 		return NULL;
255 	}
256 
257 	slp = (SNMP_session_list *) malloc(sizeof(SNMP_session_list));
258 	if(slp == NULL)
259 	{
260 		sprintf(error_label, ERR_MSG_ALLOC);
261 		snmp_errno = SNMPERR_GENERR;
262 		free(peername_dup);
263 		free(community_dup);
264 		return NULL;
265 	}
266 	memset(slp, 0, sizeof(SNMP_session_list));
267 
268 	isp = (SNMP_internal_session *) malloc(sizeof(SNMP_internal_session));
269 	if(isp == NULL)
270 	{
271 		sprintf(error_label, ERR_MSG_ALLOC);
272 		snmp_errno = SNMPERR_GENERR;
273 		free(peername_dup);
274 		free(community_dup);
275 		free(slp);
276 		return NULL;
277 	}
278 	memset(isp, 0, sizeof(SNMP_internal_session));
279 	slp->internal = isp;
280 
281 	slp->internal->sd = -1; /* mark it not set */
282 	session = (SNMP_session *) malloc(sizeof(SNMP_session));
283 	if(session == NULL)
284 	{
285 		sprintf(error_label, ERR_MSG_ALLOC);
286 		snmp_errno = SNMPERR_GENERR;
287 		free(peername_dup);
288 		free(community_dup);
289 		free(slp);
290 		free(isp);
291 		return NULL;
292 	}
293 	memset(session, 0, sizeof(SNMP_session));
294 	slp->session = session;
295 
296 
297 	/*************************************/
298 	/* 2) now link the SNMP_session_list */
299 	/*************************************/
300 
301 	slp->next = first_session;
302 	first_session = slp;
303 
304 
305 	/***************************************/
306 	/* 3) initialize SNMP_session */
307 	/***************************************/
308 
309 	session->community = community_dup;
310 	session->retries = retries;
311 	session->timeout = timeout;
312 	session->peername = peername_dup;
313 	session->remote_port = remote_port;
314 	session->local_port = local_port;
315 	session->callback = callback;
316 	session->callback_magic = callback_magic;
317 
318 
319 	/***************************************/
320 	/* 4) initialize SNMP_internal_session */
321 	/***************************************/
322 
323 	/* Set up connections */
324 	isp->sd = socket(AF_INET, SOCK_DGRAM, 0);
325 	if(isp->sd < 0)
326 	{
327 		sprintf(error_label, ERR_MSG_SOCKET, errno_string());
328 		snmp_errno = SNMPERR_SYSERR;
329 		if(snmp_session_close(session, static_error_label))
330 		{
331 			(void)fprintf(stderr, ERR_MSG_CAN_NOT_ABORT_SESSION,
332 				static_error_label, api_errstring(snmp_errno));
333 			exit(1);
334 		}
335 		return NULL;
336 	}
337 
338 	/* initialize address */
339 	isp->address.sin_addr.s_addr = ip_address.s_addr;
340 	isp->address.sin_family = AF_INET;
341 	isp->address.sin_port = session->remote_port;	/* byte swap is done in pdu.c */
342 
343 	/* bind */
344 	me.sin_family = AF_INET;
345 	me.sin_addr.s_addr = INADDR_ANY;
346 	me.sin_port = htons(session->local_port);
347 	if(bind(isp->sd, (struct sockaddr *)&me, sizeof(me)) != 0)
348 	{
349 		sprintf(error_label, ERR_MSG_BIND, errno_string());
350 		snmp_errno = SNMPERR_BAD_LOCPORT;
351 		if(snmp_session_close(session, static_error_label))
352 		{
353 			(void)fprintf(stderr, ERR_MSG_CAN_NOT_ABORT_SESSION,
354 				static_error_label, api_errstring(snmp_errno));
355 			exit(1);
356 		}
357 		return NULL;
358 	}
359 
360 	/* request list */
361 	isp->requests = NULL;
362 	session->sd = isp->sd;
363 
364 
365 	return session;
366 }
367 
368 
369 /*******************************************************************/
370 
371 /*
372  *	Free each element in the input request list.
373  */
374 
free_request_list(SNMP_request_list * rp)375 static void free_request_list(SNMP_request_list *rp)
376 {
377 	SNMP_request_list *orp;
378 
379 
380 	while(rp)
381 	{
382 		orp = rp;
383 		rp = rp->next_request;
384 		if(orp->pdu != NULL)
385 		{
386 			snmp_pdu_free(orp->pdu);
387 		}
388 		free(orp);
389 	}
390 
391 	return;
392 }
393 
394 
395 /*******************************************************************/
396 
snmp_session_close(SNMP_session * session,char * error_label)397 int snmp_session_close(SNMP_session *session, char *error_label)
398 {
399 	SNMP_session_list *slp = NULL;
400 	SNMP_session_list *oslp = NULL;
401 
402 
403 	error_label[0] = '\0';
404 
405 	if(first_session->session == session)
406 	{
407 		/* If first entry */
408 		slp = first_session;
409 		first_session = slp->next;
410 	}
411 	else
412 	{
413 		for(slp = first_session; slp; slp = slp->next)
414 		{
415 			if(slp->session == session)
416 			{
417 				if(oslp) /* if we found entry that points here */
418 				{
419 					oslp->next = slp->next;	/* link around this entry */
420 				}
421 				break;
422 			}
423 			oslp = slp;
424 		}
425 	}
426 
427 	/* If we found the session, free all data associated with it */
428 	if(slp)
429 	{
430 		if(slp->session->community)
431 		{
432 			free(slp->session->community);
433 		}
434 		if(slp->session->peername)
435 		{
436 			free(slp->session->peername);
437 		}
438 		free(slp->session);
439 		if(slp->internal->sd != -1)
440 		{
441 			if(close(slp->internal->sd) == -1)
442 			{
443 				(void)fprintf(stderr, "close(%s) failed %s\n",
444 					slp->internal->sd, errno_string());
445 			}
446 		}
447 		free_request_list(slp->internal->requests);
448 		free((char *)slp->internal);
449 		free((char *)slp);
450 	}
451 	else
452 	{
453 		snmp_errno = SNMPERR_BAD_SESSION;
454 		return -1;
455 	}
456 
457 	return 0;
458 }
459 
460 
461 /*******************************************************************/
462 
463 /*
464  *	1) sends the input pdu on the specified session
465  *	2) if this request is a pdu, add it to the request list
466  *
467  *	Upon success, 0 is returned.
468  *	On any error, -1 is returned and error_label is set.
469  *
470  *	The pdu is freed by snmp_session_send() unless a failure occured.
471  */
472 
snmp_session_send(SNMP_session * session,int predefined_id,SNMP_pdu * pdu,char * error_label)473 int snmp_session_send(SNMP_session *session, int predefined_id, SNMP_pdu *pdu, char *error_label)
474 {
475 	SNMP_session_list *slp;
476 	SNMP_internal_session *isp = NULL;
477 	SNMP_request_list *rp;
478 	struct timeval tv;
479 
480 
481 	error_label[0] = '\0';
482 
483 	for(slp = first_session; slp; slp = slp->next)
484 	{
485 		if(slp->session == session)
486 		{
487 			isp = slp->internal;
488 			break;
489 		}
490 	}
491 	if(isp == NULL)
492 	{
493 		snmp_errno = SNMPERR_BAD_SESSION;
494 		return -1;
495 	}
496 
497 	if(pdu->community == NULL)
498 	{
499 		pdu->community = strdup(session->community);
500 		if(pdu->community == NULL)
501 		{
502 			sprintf(error_label, ERR_MSG_ALLOC);
503 			snmp_errno = SNMPERR_GENERR;
504 			return -1;
505 		}
506 	}
507 
508 	if(pdu->type == GET_REQ_MSG || pdu->type == GETNEXT_REQ_MSG
509 		|| pdu->type == GET_RSP_MSG || pdu->type == SET_REQ_MSG)
510 	{
511 		pdu->request_id = ++static_request_id;
512 	}
513 	else
514 	{
515 		pdu->request_id = 0;
516 	}
517 
518 
519 	if( (pdu->type == GET_REQ_MSG)
520 		|| (pdu->type == GETNEXT_REQ_MSG)
521 		|| (pdu->type == SET_REQ_MSG) )
522 	{
523 		/* set up to expect a response */
524 
525 		rp = (SNMP_request_list *) malloc(sizeof(SNMP_request_list));
526 		if(rp == NULL)
527 		{
528 			sprintf(error_label, ERR_MSG_ALLOC);
529 			snmp_errno = SNMPERR_GENERR;
530 			return -1;
531 		}
532 		memset(rp, 0, sizeof(SNMP_request_list));
533 	}
534 
535 	(void)gettimeofday(&tv, (struct timezone *) 0);
536 	if(snmp_pdu_send(isp->sd, &(isp->address), pdu, error_label))
537 	{
538 		snmp_errno = SNMPERR_GENERR;
539 		return -1;
540 	}
541 
542 	if( (pdu->type == GET_REQ_MSG)
543 		|| (pdu->type == GETNEXT_REQ_MSG)
544 		|| (pdu->type == SET_REQ_MSG) )
545 	{
546 		rp->next_request = isp->requests;
547 		isp->requests = rp;
548 		rp->pdu = pdu;
549 		rp->request_id = pdu->request_id;
550 
551 		rp->retries = 1;
552 
553 		rp->timeout = session->timeout;
554 		rp->predefined_id = predefined_id;
555 
556 		rp->time.tv_sec = tv.tv_sec;
557 		rp->time.tv_usec = tv.tv_usec;
558 /*
559 printf("%d NOW:    %d sec and %d usec\n",
560 	rp->retries,
561 	tv.tv_sec,
562 	tv.tv_usec);
563 */
564 
565 		tv.tv_usec += rp->timeout;
566 		tv.tv_sec += tv.tv_usec / 1000000L;
567 		tv.tv_usec %= 1000000L;
568 
569 		rp->expire.tv_sec = tv.tv_sec;
570 		rp->expire.tv_usec = tv.tv_usec;
571 /*
572 printf("%d EXPIRE: %d sec and %d usec\n\n",
573 	rp->retries,
574 	tv.tv_sec,
575 	tv.tv_usec);
576 */
577 	}
578 	else
579 	{
580 		snmp_pdu_free(pdu);
581 	}
582 
583 
584 	return 0;
585 }
586 
587 
588 /*******************************************************************/
589 
snmp_session_read(fd_set * fdset)590 void snmp_session_read(fd_set *fdset)
591 {
592 	while(snmp_session_read_loop(fdset));
593 }
594 
595 /*
596  *	We need this function because the user may close the session
597  *	in the callback and then corrupt the session list
598  */
599 
snmp_session_read_loop(fd_set * fdset)600 static int snmp_session_read_loop(fd_set *fdset)
601 {
602 	SNMP_session_list *slp;
603 	SNMP_session *sp;
604 	SNMP_internal_session *isp;
605 	SNMP_pdu *pdu;
606 	SNMP_request_list *rp, *orp;
607 
608 
609 	for(slp = first_session; slp; slp = slp->next)
610 	{
611 		if(FD_ISSET(slp->internal->sd, fdset))
612 		{
613 			Address address;
614 
615 
616 			FD_CLR(slp->internal->sd, fdset);
617 
618 			sp = slp->session;
619 			isp = slp->internal;
620 
621 			pdu = snmp_pdu_receive(isp->sd, &address, static_error_label);
622 			if(pdu == NULL)
623 			{
624 				(void)fprintf(stderr, ERR_MSG_RECEIVED_MANGLED_PACKET,
625 					static_error_label);
626 				return 0;
627 			}
628 
629 			if(pdu->type == GET_RSP_MSG)
630 			{
631 				for(rp = isp->requests; rp; rp = rp->next_request)
632 				{
633 					if(rp->request_id == pdu->request_id)
634 					{
635 						/* delete request */
636 
637 						orp = rp;
638 						if(isp->requests == orp)
639 						{
640 							/* first in list */
641 
642 							isp->requests = orp->next_request;
643 						}
644 						else
645 						{
646 							for(rp = isp->requests; rp; rp = rp->next_request)
647 							{
648 								if(rp->next_request == orp)
649 								{
650 									rp->next_request = orp->next_request; /* link around it */
651 									break;
652 								}
653 							}
654 						}
655 
656 						sp->callback(RECEIVED_MESSAGE, sp, pdu->request_id, orp->predefined_id, pdu, sp->callback_magic);
657 
658 						snmp_pdu_free(orp->pdu);
659 						free(orp);
660 
661 						/*
662 						 * Then we should return as soon as possible
663 						 * because may have closed the session and
664 						 * corrupted the pointers
665 						 */
666 
667 						break;
668 					}
669 				}
670 			}
671 			else
672 			if( (pdu->type == GET_REQ_MSG)
673 				|| (pdu->type == GETNEXT_REQ_MSG)
674 				|| (pdu->type == TRP_REQ_MSG)
675 				|| (pdu->type == SET_REQ_MSG) )
676 			{
677 				sp->callback(RECEIVED_MESSAGE, sp, pdu->request_id, 0, pdu, sp->callback_magic);
678 				/*
679 				 * Then we should return as soon as possible
680 				 * because may have closed the session and
681 				 * corrupted the pointers
682 				 */
683 			}
684 
685 			snmp_pdu_free(pdu);
686 
687 			return 1;
688 		}
689 	}
690 
691 	return 0;
692 }
693 
694 
snmp_session_read_2(int fd)695 void snmp_session_read_2(int fd)
696 {
697 	SNMP_session_list *slp;
698 	SNMP_session *sp;
699 	SNMP_internal_session *isp;
700 	SNMP_pdu *pdu;
701 	SNMP_request_list *rp, *orp;
702 
703 
704 	for(slp = first_session; slp; slp = slp->next)
705 	{
706 		if(slp->internal->sd == fd)
707 		{
708 			Address address;
709 
710 
711 			sp = slp->session;
712 			isp = slp->internal;
713 
714 			pdu = snmp_pdu_receive(isp->sd, &address, static_error_label);
715 			if(pdu == NULL)
716 			{
717 				(void)fprintf(stderr, ERR_MSG_RECEIVED_MANGLED_PACKET,
718 					static_error_label);
719 				return;
720 			}
721 
722 			if(pdu->type == GET_RSP_MSG)
723 			{
724 				for(rp = isp->requests; rp; rp = rp->next_request)
725 				{
726 					if(rp->request_id == pdu->request_id)
727 					{
728 						/* delete request */
729 						orp = rp;
730 						if(isp->requests == orp)
731 						{
732 							/* first in list */
733 
734 							isp->requests = orp->next_request;
735 						}
736 						else
737 						{
738 							for(rp = isp->requests; rp; rp = rp->next_request)
739 							{
740 								if(rp->next_request == orp)
741 								{
742 									rp->next_request = orp->next_request; /* link around it */
743 									break;
744 								}
745 							}
746 						}
747 
748 						sp->callback(RECEIVED_MESSAGE, sp, pdu->request_id, orp->predefined_id, pdu, sp->callback_magic);
749 
750 						snmp_pdu_free(orp->pdu);
751 						free(orp);
752 
753 						/*
754 						 * Then we should return as soon as possible
755 						 * because may have closed the session and
756 						 * corrupted the pointers
757 						 */
758 
759 						break;
760 					}
761 				}
762 			}
763 			else
764 			if( (pdu->type == GET_REQ_MSG)
765 				|| (pdu->type == GETNEXT_REQ_MSG)
766 				|| (pdu->type == TRP_REQ_MSG)
767 				|| (pdu->type == SET_REQ_MSG) )
768 			{
769 				sp->callback(RECEIVED_MESSAGE, sp, pdu->request_id, 0, pdu, sp->callback_magic);
770 				/*
771 				 * Then we should return as soon as possible
772 				 * because may have closed the session and
773 				 * corrupted the pointers
774 				 */
775 			}
776 
777 			snmp_pdu_free(pdu);
778 
779 			return;
780 		}
781 	}
782 
783 	return;
784 }
785 
786 
787 /*******************************************************************/
788 
snmp_session_select_info(int * numfds,fd_set * fdset,struct timeval * timeout)789 int snmp_session_select_info(int *numfds, fd_set *fdset, struct timeval *timeout)
790 {
791 	SNMP_session_list *slp;
792 	SNMP_internal_session *isp;
793 	SNMP_request_list *rp;
794 	struct timeval now, earliest;
795 	int active = 0, requests = 0;
796 
797 
798 	timerclear(&earliest);
799 
800 	/*
801 	 *	For each request outstanding, add it's socket to the fdset,
802 	 *	and if it is the earliest timeout to expire, mark it as lowest.
803 	 */
804 	for(slp = first_session; slp; slp = slp->next)
805 	{
806 		active++;
807 		isp = slp->internal;
808 		if((isp->sd + 1) > *numfds)
809 		{
810 			*numfds = (isp->sd + 1);
811 		}
812 		FD_SET(isp->sd, fdset);
813 
814 		if(isp->requests)
815 		{
816 			/* found another session with outstanding requests */
817 			for(rp = isp->requests; rp; rp = rp->next_request)
818 			{
819 				requests++;
820 				if(!timerisset(&earliest) || timercmp(&rp->expire, &earliest, <))
821 				{
822 					earliest.tv_sec = rp->expire.tv_sec;
823 					earliest.tv_usec = rp->expire.tv_usec;
824 				}
825 			}
826 		}
827 	}
828 /*
829 printf("NUM REQUESTS:     %d\n",
830 	requests);
831 */
832 
833 	if(requests == 0)
834 	{
835 		/* if none are active, skip arithmetic */
836 
837 		return 0;
838 	}
839 /*
840 printf("EARLIEST TIMEOUT: %d sec and %d usec\n\n",
841 	earliest.tv_sec,
842 	earliest.tv_usec);
843 */
844 
845 	/*
846 	 *	Now find out how much time until the earliest timeout.  This
847 	 *	transforms earliest from an absolute time into a delta time, the
848 	 *	time left until the select should timeout.
849 	 */
850 	(void)gettimeofday(&now, (struct timezone *)0);
851 	earliest.tv_sec--;	/* adjust time to make arithmetic easier */
852 	earliest.tv_usec += 1000000L;
853 	earliest.tv_sec -= now.tv_sec;
854 	earliest.tv_usec -= now.tv_usec;
855 	while(earliest.tv_usec >= 1000000L)
856 	{
857 		earliest.tv_usec -= 1000000L;
858 		earliest.tv_sec += 1;
859 	}
860 	if(earliest.tv_sec < 0)
861 	{
862 		earliest.tv_sec = 0;
863 		earliest.tv_usec = 0;
864 	}
865 	if((earliest.tv_sec == 0) && (earliest.tv_usec == 0))
866 	{
867 		earliest.tv_sec = 0;
868 		earliest.tv_usec = 1;
869 	}
870 
871 	if(timercmp(&earliest, timeout, <))
872 	{
873 		timeout->tv_sec = earliest.tv_sec;
874 		timeout->tv_usec = earliest.tv_usec;
875 	}
876 	else
877 	if((timeout->tv_sec == 0) && (timeout->tv_usec == 0))
878 	{
879 		timeout->tv_sec = earliest.tv_sec;
880 		timeout->tv_usec = earliest.tv_usec;
881 	}
882 
883 /*
884 printf("NEW TIMEOUT: %d sec and %d usec\n\n",
885 	timeout->tv_sec,
886 	timeout->tv_usec);
887 */
888 
889 	return requests;
890 }
891 
892 
snmp_session_itimeout_info(struct itimerval * itimeout)893 int snmp_session_itimeout_info(struct itimerval *itimeout)
894 {
895 	int numfds = 0;
896 	fd_set fdset;
897 
898 
899 	FD_ZERO(&fdset);
900 
901 	return snmp_session_select_info(&numfds, &fdset, &(itimeout->it_value));
902 }
903 
904 
905 /*******************************************************************/
906 
907 /*
908  *	It may remain some bugs in this function
909  *	because the user may close the session in the callback
910  *	and then corrupt the list
911  */
912 
snmp_session_timeout()913 void snmp_session_timeout()
914 {
915 	while(snmp_session_timeout_loop());
916 }
917 
snmp_session_timeout_loop()918 static int snmp_session_timeout_loop()
919 {
920 	SNMP_session_list *slp;
921 	SNMP_session *sp;
922 	SNMP_internal_session *isp;
923 	SNMP_request_list *rp, *orp;
924 	struct timeval now;
925 
926 
927 	(void)gettimeofday(&now, (struct timezone *) 0);
928 
929 	/*
930 	 *	For each request outstanding, check to see if it has expired.
931 	*/
932 
933 	for(slp = first_session; slp; slp = slp->next)
934 	{
935 		sp = slp->session;
936 		isp = slp->internal;
937 		orp = NULL;
938 		for(rp = isp->requests; rp; rp = rp->next_request)
939 		{
940 			if(timercmp(&rp->expire, &now, <))
941 			{
942 				/* this timer has expired */
943 
944 				if (rp->retries >= sp->retries)
945 				{
946 					/* No more chances, delete this entry */
947 
948 
949 					if(orp == NULL)
950 					{
951 						isp->requests = rp->next_request;
952 					}
953 					else
954 					{
955 						orp->next_request = rp->next_request;
956 					}
957 
958 					sp->callback(TIMED_OUT, sp, rp->pdu->request_id, rp->predefined_id, rp->pdu, sp->callback_magic);
959 
960 					snmp_pdu_free(rp->pdu);
961 					free(rp);
962 
963 					return 1;
964 				}
965 				else
966 				{
967 					/* retransmit this pdu */
968 
969 					struct timeval tv;
970 
971 
972 					rp->retries++;
973 					rp->timeout <<= 1;
974 
975 					(void)gettimeofday(&tv, (struct timezone *) 0);
976 					if(snmp_pdu_send(isp->sd, &(isp->address), rp->pdu, static_error_label))
977 					{
978 						(void)fprintf(stderr, "snmp_pdu_send() failed: %s\n",
979 							static_error_label);
980 					}
981 
982 					rp->time.tv_sec = tv.tv_sec;
983 					rp->time.tv_usec = tv.tv_usec;
984 /*
985 printf("%d NOW:    %d sec and %d usec\n",
986 	rp->retries,
987 	tv.tv_sec,
988 	tv.tv_usec);
989 */
990 
991 					tv.tv_usec += rp->timeout;
992 					tv.tv_sec += tv.tv_usec / 1000000L;
993 					tv.tv_usec %= 1000000L;
994 
995 					rp->expire.tv_sec = tv.tv_sec;
996 					rp->expire.tv_usec = tv.tv_usec;
997 /*
998 printf("%d EXPIRE: %d sec and %d usec\n\n",
999 	rp->retries,
1000 	tv.tv_sec,
1001 	tv.tv_usec);
1002 */
1003 				}
1004 			}
1005 			orp = rp;
1006 		}
1007 	}
1008 
1009 	return 0;
1010 }
1011 
1012 
1013 
1014