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