snmp_api.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:25k
源码类别:

代理服务器

开发平台:

Unix_Linux

  1. /**********************************************************************
  2.  *
  3.  *           Copyright 1997 by Carnegie Mellon University
  4.  * 
  5.  *                       All Rights Reserved
  6.  * 
  7.  * Permission to use, copy, modify, and distribute this software and its
  8.  * documentation for any purpose and without fee is hereby granted,
  9.  * provided that the above copyright notice appear in all copies and that
  10.  * both that copyright notice and this permission notice appear in
  11.  * supporting documentation, and that the name of CMU not be
  12.  * used in advertising or publicity pertaining to distribution of the
  13.  * software without specific, written prior permission.
  14.  * 
  15.  * CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  16.  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
  17.  * CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
  18.  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  19.  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  20.  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  21.  * SOFTWARE.
  22.  * 
  23.  **********************************************************************/
  24. #include "config.h"
  25. #include <stdio.h>
  26. #if HAVE_UNISTD_H
  27. #include <unistd.h>
  28. #endif
  29. #if HAVE_STDLIB_H
  30. #include <stdlib.h>
  31. #endif
  32. #if HAVE_SYS_TYPES_H
  33. #include <sys/types.h>
  34. #endif
  35. #if HAVE_CTYPE_H
  36. #include <ctype.h>
  37. #endif
  38. #if HAVE_GNUMALLOC_H
  39. #include <gnumalloc.h>
  40. #elif HAVE_MALLOC_H && !defined(_SQUID_FREEBSD_) && !defined(_SQUID_NEXT_)
  41. #include <malloc.h>
  42. #endif
  43. #if HAVE_MEMORY_H
  44. #include <memory.h>
  45. #endif
  46. #ifdef HAVE_STRING_H
  47. #include <string.h>
  48. #endif
  49. #ifdef HAVE_STRINGS_H
  50. #include <strings.h>
  51. #endif
  52. #if HAVE_BSTRING_H
  53. #include <bstring.h>
  54. #endif
  55. #if HAVE_SYS_SOCKET_H
  56. #include <sys/socket.h>
  57. #endif
  58. #if HAVE_NETINET_IN_H
  59. #include <netinet/in.h>
  60. #endif
  61. #if HAVE_ARPA_INET_H
  62. #include <arpa/inet.h>
  63. #endif
  64. #if HAVE_SYS_TIME_H
  65. #include <sys/time.h>
  66. #endif
  67. #if HAVE_NETDB_H
  68. #include <netdb.h>
  69. #endif
  70. #include "asn1.h"
  71. #include "snmp.h"
  72. #include "snmp-internal.h"
  73. #include "snmp_impl.h"
  74. #include "snmp_session.h"
  75. #if 0
  76. #include "mibii.h"
  77. #include "snmp_dump.h"
  78. #endif
  79. #include "snmp_error.h"
  80. #include "snmp_vars.h"
  81. #include "snmp_pdu.h"
  82. #include "snmp_msg.h"
  83. #include "snmp_api.h"
  84. #if 0
  85. #include "snmp_client.h"
  86. #endif
  87. #include "snmp_api_error.h"
  88. #include "snmp_api_util.h"
  89. #include "util.h"
  90. extern int snmp_errno;
  91. /*#define DEBUG_API 1 */
  92. /*
  93.  * RFC 1906: Transport Mappings for SNMPv2
  94.  */
  95. oid default_enterprise[] =
  96. {1, 3, 6, 1, 4, 1, 3, 1, 1}; /* enterprises.cmu.systems.cmuSNMP */
  97. #define DEFAULT_COMMUNITY   "public"
  98. #define DEFAULT_RETRIES     4
  99. #define DEFAULT_TIMEOUT     1000000L
  100. #define DEFAULT_REMPORT     SNMP_PORT
  101. #define DEFAULT_ENTERPRISE  default_enterprise
  102. #define DEFAULT_TIME     0
  103. extern int snmp_errno;
  104. struct session_list *Sessions = NULL;
  105. #if 0
  106. /*
  107.  * Get initial request ID for all transactions.
  108.  */
  109. static int Reqid = 0;
  110. static void 
  111. init_snmp(void)
  112. {
  113.     struct timeval tv;
  114.     gettimeofday(&tv, (struct timezone *) 0);
  115.     squid_srandom(tv.tv_sec ^ tv.tv_usec);
  116.     Reqid = squid_random();
  117. }
  118. /*
  119.  * Free each element in the input request list.
  120.  */
  121. static void 
  122. free_request_list(rp)
  123.      struct request_list *rp;
  124. {
  125.     struct request_list *orp;
  126.     while (rp) {
  127. orp = rp;
  128. rp = rp->next_request;
  129. if (orp->pdu != NULL)
  130.     snmp_free_pdu(orp->pdu);
  131. xfree((char *) orp);
  132.     }
  133. }
  134. #endif
  135. /**********************************************************************/
  136. /*
  137.  * Sets up the session with the snmp_session information provided
  138.  * by the user.  Then opens and binds the necessary UDP port.
  139.  * A handle to the created session is returned (this is different than
  140.  * the pointer passed to snmp_open()).  On any error, NULL is returned
  141.  * and snmp_errno is set to the appropriate error code.
  142.  */
  143. #if 0
  144. struct snmp_session *
  145. snmp_open(struct snmp_session *session)
  146. {
  147.     struct session_list *slp;
  148.     struct snmp_internal_session *isp;
  149.     u_char *cp;
  150.     int sd;
  151.     u_int addr;
  152.     struct sockaddr_in me;
  153.     struct hostent *hp;
  154.     struct servent *servp;
  155.     if (Reqid == 0)
  156. init_snmp();
  157.     /* Copy session structure and link into list */
  158.     slp = (struct session_list *) xmalloc(sizeof(struct session_list));
  159.     if (slp == NULL) {
  160. snmp_set_api_error(SNMPERR_OS_ERR);
  161. return (NULL);
  162.     }
  163.     /* Internal session */
  164.     isp = (struct snmp_internal_session *) xmalloc(sizeof(struct snmp_internal_session));
  165.     if (isp == NULL) {
  166. xfree(slp);
  167. snmp_set_api_error(SNMPERR_OS_ERR);
  168. return (NULL);
  169.     }
  170.     slp->internal = isp;
  171.     memset((char *) isp, '', sizeof(struct snmp_internal_session));
  172.     slp->internal->sd = -1; /* mark it not set */
  173.     /* The actual session */
  174.     slp->session = (struct snmp_session *) xmalloc(sizeof(struct snmp_session));
  175.     if (slp->session == NULL) {
  176. xfree(isp);
  177. xfree(slp);
  178. snmp_set_api_error(SNMPERR_OS_ERR);
  179. return (NULL);
  180.     }
  181.     xmemcpy((char *) slp->session, (char *) session, sizeof(struct snmp_session));
  182.     session = slp->session;
  183.     /* now link it in. */
  184.     slp->next = Sessions;
  185.     Sessions = slp;
  186.     /*
  187.      * session now points to the new structure that still contains pointers to
  188.      * data allocated elsewhere.  Some of this data is copied to space malloc'd
  189.      * here, and the pointer replaced with the new one.
  190.      */
  191.     if (session->peername != NULL) {
  192. cp = (u_char *) xmalloc((unsigned) strlen(session->peername) + 1);
  193. if (cp == NULL) {
  194.     xfree(slp->session);
  195.     xfree(isp);
  196.     xfree(slp);
  197.     snmp_set_api_error(SNMPERR_OS_ERR);
  198.     return (NULL);
  199. }
  200. strcpy((char *) cp, session->peername);
  201. session->peername = (char *) cp;
  202.     }
  203.     /* Fill in defaults if necessary */
  204.     if (session->community_len != SNMP_DEFAULT_COMMUNITY_LEN) {
  205. cp = (u_char *) xmalloc((unsigned) session->community_len);
  206. if (cp)
  207.     xmemcpy((char *) cp, (char *) session->community, session->community_len);
  208.     } else {
  209. session->community_len = strlen(DEFAULT_COMMUNITY);
  210. cp = (u_char *) xmalloc((unsigned) session->community_len);
  211. if (cp)
  212.     xmemcpy((char *) cp, (char *) DEFAULT_COMMUNITY,
  213. session->community_len);
  214.     }
  215.     if (cp == NULL) {
  216. xfree(session->peername);
  217. xfree(slp->session);
  218. xfree(isp);
  219. xfree(slp);
  220. snmp_set_api_error(SNMPERR_OS_ERR);
  221. return (NULL);
  222.     }
  223.     session->community = cp; /* replace pointer with pointer to new data */
  224.     if (session->retries == SNMP_DEFAULT_RETRIES)
  225. session->retries = DEFAULT_RETRIES;
  226.     if (session->timeout == SNMP_DEFAULT_TIMEOUT)
  227. session->timeout = DEFAULT_TIMEOUT;
  228.     isp->requests = NULL;
  229.     /* Set up connections */
  230.     sd = socket(AF_INET, SOCK_DGRAM, 0);
  231.     if (sd < 0) {
  232. perror("socket");
  233. snmp_set_api_error(SNMPERR_OS_ERR);
  234. if (!snmp_close(session)) {
  235.     snmplib_debug(5, "Couldn't abort session: %s. Exitingn",
  236. api_errstring(snmp_errno));
  237.     exit(1);
  238. }
  239. return (NULL);
  240.     }
  241. #ifdef SO_BSDCOMPAT
  242.     /* Patch for Linux.  Without this, UDP packets that fail get an ICMP
  243.      * response.  Linux turns the failed ICMP response into an error message
  244.      * and return value, unlike all other OS's.
  245.      */
  246.     {
  247. int one = 1;
  248. setsockopt(sd, SOL_SOCKET, SO_BSDCOMPAT, &one, sizeof(one));
  249.     }
  250. #endif /* SO_BSDCOMPAT */
  251.     isp->sd = sd;
  252.     if (session->peername != SNMP_DEFAULT_PEERNAME) {
  253. if ((addr = inet_addr(session->peername)) != -1) {
  254.     xmemcpy((char *) &isp->addr.sin_addr, (char *) &addr,
  255. sizeof(isp->addr.sin_addr));
  256. } else {
  257.     hp = gethostbyname(session->peername);
  258.     if (hp == NULL) {
  259. snmplib_debug(3, "unknown host: %sn", session->peername);
  260. snmp_errno = SNMPERR_BAD_ADDRESS;
  261. if (!snmp_close(session)) {
  262.     snmplib_debug(3, "Couldn't abort session: %s. Exitingn",
  263. api_errstring(snmp_errno));
  264.     exit(2);
  265. }
  266. return (NULL);
  267.     } else {
  268. xmemcpy((char *) &isp->addr.sin_addr, (char *) hp->h_addr,
  269.     hp->h_length);
  270.     }
  271. }
  272. isp->addr.sin_family = AF_INET;
  273. if (session->remote_port == SNMP_DEFAULT_REMPORT) {
  274.     servp = getservbyname("snmp", "udp");
  275.     if (servp != NULL) {
  276. isp->addr.sin_port = servp->s_port;
  277.     } else {
  278. isp->addr.sin_port = htons(SNMP_PORT);
  279.     }
  280. } else {
  281.     isp->addr.sin_port = htons(session->remote_port);
  282. }
  283.     } else {
  284. isp->addr.sin_addr.s_addr = SNMP_DEFAULT_ADDRESS;
  285.     }
  286.     memset(&me, '', sizeof(me));
  287.     me.sin_family = AF_INET;
  288.     me.sin_addr.s_addr = INADDR_ANY;
  289.     me.sin_port = htons(session->local_port);
  290.     if (bind(sd, (struct sockaddr *) &me, sizeof(me)) != 0) {
  291. perror("bind");
  292. snmp_errno = SNMPERR_BAD_LOCPORT;
  293. if (!snmp_close(session)) {
  294.     snmplib_debug(3, "Couldn't abort session: %s. Exitingn",
  295. api_errstring(snmp_errno));
  296.     exit(3);
  297. }
  298. return (NULL);
  299.     }
  300.     return (session);
  301. }
  302. /*
  303.  * Close the input session.  Frees all data allocated for the session,
  304.  * dequeues any pending requests, and closes any sockets allocated for
  305.  * the session.  Returns 0 on error, 1 otherwise.
  306.  */
  307. int 
  308. snmp_close(struct snmp_session *session)
  309. {
  310.     struct session_list *slp = NULL, *oslp = NULL;
  311.     if (Sessions->session == session) { /* If first entry */
  312. slp = Sessions;
  313. Sessions = slp->next;
  314.     } else {
  315. for (slp = Sessions; slp; slp = slp->next) {
  316.     if (slp->session == session) {
  317. if (oslp) /* if we found entry that points here */
  318.     oslp->next = slp->next; /* link around this entry */
  319. break;
  320.     }
  321.     oslp = slp;
  322. }
  323.     }
  324.     /* If we found the session, free all data associated with it */
  325.     if (slp) {
  326. if (slp->session->community != NULL)
  327.     xfree((char *) slp->session->community);
  328. if (slp->session->peername != NULL)
  329.     xfree((char *) slp->session->peername);
  330. xfree((char *) slp->session);
  331. if (slp->internal->sd != -1)
  332.     close(slp->internal->sd);
  333. free_request_list(slp->internal->requests);
  334. xfree((char *) slp->internal);
  335. xfree((char *) slp);
  336.     } else {
  337. snmp_errno = SNMPERR_BAD_SESSION;
  338. return (0);
  339.     }
  340.     return (1);
  341. }
  342. #endif
  343. /*
  344.  * Takes a session and a pdu and serializes the ASN PDU into the area
  345.  * pointed to by packet.  out_length is the size of the data area available.
  346.  * Returns the length of the encoded packet in out_length.  If an error
  347.  * occurs, -1 is returned.  If all goes well, 0 is returned.
  348.  */
  349. int
  350. snmp_build(session, pdu, packet, out_length)
  351.      struct snmp_session *session;
  352.      struct snmp_pdu *pdu;
  353.      u_char *packet;
  354.      int *out_length;
  355. {
  356.     u_char *bufp;
  357.     bufp = snmp_msg_Encode(packet, out_length,
  358. session->community, session->community_len,
  359. session->Version,
  360. pdu);
  361.     snmplib_debug(8, "LIBSNMP: snmp_build():  Packet len %d (requid %d)n",
  362. *out_length, pdu->reqid);
  363.     if (bufp == NULL)
  364. return (-1);
  365.     return (0);
  366. }
  367. /*
  368.  * Parses the packet recieved on the input session, and places the data into
  369.  * the input pdu.  length is the length of the input packet.  If any errors
  370.  * are encountered, NULL is returned.  If not, the community is.
  371.  */
  372. u_char *
  373. snmp_parse(struct snmp_session * session,
  374.     struct snmp_pdu * pdu,
  375.     u_char * data,
  376.     int length)
  377. {
  378.     u_char Community[128];
  379.     u_char *bufp;
  380.     int CommunityLen = 128;
  381.     /* Decode the entire message. */
  382.     data = snmp_msg_Decode(data, &length,
  383. Community, &CommunityLen,
  384. &session->Version, pdu);
  385.     if (data == NULL)
  386. return (NULL);
  387.     bufp = (u_char *) xmalloc(CommunityLen + 1);
  388.     if (bufp == NULL)
  389. return (NULL);
  390.     strcpy((char *) bufp, (char *) Community);
  391.     return (bufp);
  392. }
  393. /*
  394.  * Sends the input pdu on the session after calling snmp_build to create
  395.  * a serialized packet.  If necessary, set some of the pdu data from the
  396.  * session defaults.  Add a request corresponding to this pdu to the list
  397.  * of outstanding requests on this session, then send the pdu.
  398.  * Returns the request id of the generated packet if applicable, otherwise 1.
  399.  * On any error, 0 is returned.
  400.  * The pdu is freed by snmp_send() unless a failure occured.
  401.  */
  402. #if 0
  403. int 
  404. snmp_send(struct snmp_session *session, struct snmp_pdu *pdu)
  405. {
  406.     struct session_list *slp;
  407.     struct snmp_internal_session *isp = NULL;
  408.     u_char packet[PACKET_LENGTH];
  409.     int length = PACKET_LENGTH;
  410.     struct request_list *rp;
  411.     struct timeval tv;
  412.     if (Reqid == 0)
  413. init_snmp();
  414.     for (slp = Sessions; slp; slp = slp->next) {
  415. if (slp->session == session) {
  416.     isp = slp->internal;
  417.     break;
  418. }
  419.     }
  420.     if (isp == NULL) {
  421. snmp_errno = SNMPERR_BAD_SESSION;
  422. return 0;
  423.     }
  424.     if (pdu->command == SNMP_PDU_GET ||
  425. pdu->command == SNMP_PDU_GETNEXT ||
  426. pdu->command == SNMP_PDU_RESPONSE ||
  427. pdu->command == SNMP_PDU_SET) {
  428. if (pdu->reqid == SNMP_DEFAULT_REQID)
  429.     pdu->reqid = ++Reqid;
  430. if (pdu->errstat == SNMP_DEFAULT_ERRSTAT)
  431.     pdu->errstat = 0;
  432. if (pdu->errindex == SNMP_DEFAULT_ERRINDEX)
  433.     pdu->errindex = 0;
  434.     } else if (pdu->command == SNMP_PDU_INFORM ||
  435.     pdu->command == SNMP_PDU_GETBULK ||
  436. pdu->command == SNMP_PDU_V2TRAP) {
  437. if (session->Version != SNMP_VERSION_2) {
  438.     snmplib_debug(3, "Cant send SNMPv2 PDU's in SNMP message.n");
  439.     snmp_errno = SNMPERR_GENERR; /* Fix this XXXXX */
  440.     return 0;
  441. }
  442. if (pdu->reqid == SNMP_DEFAULT_REQID)
  443.     pdu->reqid = ++Reqid;
  444. if (pdu->errstat == SNMP_DEFAULT_ERRSTAT)
  445.     pdu->errstat = 0;
  446. if (pdu->errindex == SNMP_DEFAULT_ERRINDEX)
  447.     pdu->errindex = 0;
  448.     } else {
  449. /* fill in trap defaults */
  450. pdu->reqid = 1; /* give a bogus non-error reqid for traps */
  451. if (pdu->enterprise_length == SNMP_DEFAULT_ENTERPRISE_LENGTH) {
  452.     pdu->enterprise = (oid *) xmalloc(sizeof(DEFAULT_ENTERPRISE));
  453.     xmemcpy((char *) pdu->enterprise, (char *) DEFAULT_ENTERPRISE,
  454. sizeof(DEFAULT_ENTERPRISE));
  455.     pdu->enterprise_length = sizeof(DEFAULT_ENTERPRISE) / sizeof(oid);
  456. }
  457. if (pdu->time == SNMP_DEFAULT_TIME)
  458.     pdu->time = DEFAULT_TIME;
  459.     }
  460.     if (pdu->address.sin_addr.s_addr == SNMP_DEFAULT_ADDRESS) {
  461. if (isp->addr.sin_addr.s_addr != SNMP_DEFAULT_ADDRESS) {
  462.     xmemcpy((char *) &pdu->address, (char *) &isp->addr,
  463. sizeof(pdu->address));
  464. } else {
  465.     snmplib_debug(3, "No remote IP address specifiedn");
  466.     snmp_errno = SNMPERR_BAD_ADDRESS;
  467.     return 0;
  468. }
  469.     }
  470.     if (snmp_build(session, pdu, packet, &length) < 0) {
  471. snmplib_debug(3, "Error building packetn");
  472. snmp_errno = SNMPERR_GENERR;
  473. return 0;
  474.     }
  475.     snmp_dump(packet, length, "sending", pdu->address.sin_addr);
  476.     gettimeofday(&tv, (struct timezone *) 0);
  477.     if (sendto(isp->sd, (char *) packet, length, 0,
  478.     (struct sockaddr *) &pdu->address, sizeof(pdu->address)) < 0) {
  479. perror("sendto");
  480. snmp_errno = SNMPERR_GENERR;
  481. return 0;
  482.     }
  483.     snmplib_debug(6, "LIBSNMP:  Sent PDU %s, Reqid %dn",
  484. snmp_pdu_type(pdu), pdu->reqid);
  485.     if (pdu->command == SNMP_PDU_GET ||
  486. pdu->command == SNMP_PDU_GETNEXT ||
  487. pdu->command == SNMP_PDU_SET ||
  488. pdu->command == SNMP_PDU_GETBULK ||
  489. pdu->command == SNMP_PDU_INFORM) {
  490. snmplib_debug(6, "LIBSNMP:  Setting up to recieve a response for reqid %dn",
  491.     pdu->reqid);
  492. /* set up to expect a response */
  493. rp = (struct request_list *) xmalloc(sizeof(struct request_list));
  494. rp->next_request = isp->requests;
  495. isp->requests = rp;
  496. rp->pdu = pdu;
  497. rp->request_id = pdu->reqid;
  498. rp->retries = 1;
  499. rp->timeout = session->timeout;
  500. rp->time = tv;
  501. tv.tv_usec += rp->timeout;
  502. tv.tv_sec += tv.tv_usec / 1000000L;
  503. tv.tv_usec %= 1000000L;
  504. rp->expire = tv;
  505.     }
  506.     return (pdu->reqid);
  507. }
  508. /*
  509.  * Checks to see if any of the fd's set in the fdset belong to
  510.  * snmp.  Each socket with it's fd set has a packet read from it
  511.  * and snmp_parse is called on the packet received.  The resulting pdu
  512.  * is passed to the callback routine for that session.  If the callback
  513.  * routine returns successfully, the pdu and it's request are deleted.
  514.  */
  515. void
  516. snmp_read(fdset)
  517.      fd_set *fdset;
  518. {
  519.     struct session_list *slp;
  520.     struct snmp_session *sp;
  521.     struct snmp_internal_session *isp;
  522.     u_char packet[PACKET_LENGTH];
  523.     struct sockaddr_in from;
  524.     int length, fromlength;
  525.     struct snmp_pdu *pdu;
  526.     struct request_list *rp, *orp;
  527.     u_char *bufp;
  528.     for (slp = Sessions; slp; slp = slp->next) {
  529. if (FD_ISSET(slp->internal->sd, fdset)) {
  530.     sp = slp->session;
  531.     isp = slp->internal;
  532.     fromlength = sizeof from;
  533.     length = recvfrom(isp->sd, (char *) packet,
  534. PACKET_LENGTH, 0, (struct sockaddr *) &from,
  535. &fromlength);
  536.     if (length == -1)
  537. perror("recvfrom");
  538.     snmp_dump(packet, length, "received", from.sin_addr);
  539.     pdu = snmp_pdu_create(0);
  540.     pdu->address = from;
  541.     pdu->reqid = 0;
  542.     /* Parse the incoming packet */
  543.     bufp = snmp_parse(sp, pdu, packet, length);
  544.     if (bufp == NULL) {
  545. snmplib_debug(3, "Mangled packetn");
  546. snmp_free_pdu(pdu);
  547. return;
  548.     }
  549.     if (sp->community)
  550. xfree(sp->community);
  551.     sp->community = bufp;
  552.     sp->community_len = strlen((char *) bufp);
  553.     snmplib_debug(6, "LIBSNMP:  Read PDU %s, ReqId %dn", snmp_pdu_type(pdu), pdu->reqid);
  554.     if (pdu->command == SNMP_PDU_RESPONSE) {
  555. for (rp = isp->requests; rp; rp = rp->next_request) {
  556.     if (rp->request_id == pdu->reqid) {
  557. snmplib_debug(6, "LIBSNMP:  ReqId %d:  Calling callbackn", pdu->reqid);
  558. if (sp->callback(RECEIVED_MESSAGE, sp,
  559. pdu->reqid, pdu,
  560. sp->callback_magic) == 1) {
  561.     /* successful, so delete request */
  562.     snmplib_debug(6, "LIBSNMP:  ReqId %d:  Success.  Removing ReqId.n", pdu->reqid);
  563.     orp = rp;
  564.     if (isp->requests == orp) {
  565. /* first in list */
  566. isp->requests = orp->next_request;
  567.     } else {
  568. for (rp = isp->requests; rp; rp = rp->next_request) {
  569.     if (rp->next_request == orp) {
  570. /* link around it */
  571. rp->next_request = orp->next_request;
  572. break;
  573.     }
  574. }
  575.     }
  576.     snmp_free_pdu(orp->pdu);
  577.     xfree((char *) orp);
  578.     /* there shouldn't be another req with the same reqid */
  579.     break;
  580. }
  581.     }
  582. }
  583.     } else if (pdu->command == SNMP_PDU_GET ||
  584.     pdu->command == SNMP_PDU_GETNEXT ||
  585.     pdu->command == TRP_REQ_MSG ||
  586.     pdu->command == SNMP_PDU_SET ||
  587.     pdu->command == SNMP_PDU_GETBULK ||
  588.     pdu->command == SNMP_PDU_INFORM ||
  589. pdu->command == SNMP_PDU_V2TRAP) {
  590. sp->callback(RECEIVED_MESSAGE, sp, pdu->reqid,
  591.     pdu, sp->callback_magic);
  592.     }
  593.     snmp_free_pdu(pdu);
  594. }
  595.     }
  596. }
  597. /*
  598.  * Returns info about what snmp requires from a select statement.
  599.  * numfds is the number of fds in the list that are significant.
  600.  * All file descriptors opened for SNMP are OR'd into the fdset.
  601.  * If activity occurs on any of these file descriptors, snmp_read
  602.  * should be called with that file descriptor set
  603.  *
  604.  * The timeout is the latest time that SNMP can wait for a timeout.  The
  605.  * select should be done with the minimum time between timeout and any other
  606.  * timeouts necessary.  This should be checked upon each invocation of select.
  607.  * If a timeout is received, snmp_timeout should be called to check if the
  608.  * timeout was for SNMP.  (snmp_timeout is idempotent)
  609.  *
  610.  * Block is 1 if the select is requested to block indefinitely, rather than time out.
  611.  * If block is input as 1, the timeout value will be treated as undefined, but it must
  612.  * be available for setting in snmp_select_info.  On return, if block is true, the value
  613.  * of timeout will be undefined.
  614.  *
  615.  * snmp_select_info returns the number of open sockets.  (i.e. The number of sessions open)
  616.  */
  617. int
  618. snmp_select_info(numfds, fdset, timeout, block)
  619.      int *numfds;
  620.      fd_set *fdset;
  621.      struct timeval *timeout;
  622.      int *block; /* should the select block until input arrives (i.e. no input) */
  623. {
  624.     struct session_list *slp;
  625.     struct snmp_internal_session *isp;
  626.     struct request_list *rp;
  627.     struct timeval now, earliest;
  628.     int active = 0, requests = 0;
  629.     timerclear(&earliest);
  630.     /*
  631.      * For each request outstanding, add it's socket to the fdset,
  632.      * and if it is the earliest timeout to expire, mark it as lowest.
  633.      */
  634.     for (slp = Sessions; slp; slp = slp->next) {
  635. active++;
  636. isp = slp->internal;
  637. if ((isp->sd + 1) > *numfds)
  638.     *numfds = (isp->sd + 1);
  639. FD_SET(isp->sd, fdset);
  640. snmplib_debug(6, "LIBSNMP:  select():  Adding port %dn", isp->sd);
  641. if (isp->requests) {
  642.     /* found another session with outstanding requests */
  643.     requests++;
  644.     for (rp = isp->requests; rp; rp = rp->next_request) {
  645. if (!timerisset(&earliest) ||
  646.     timercmp(&rp->expire, &earliest, <))
  647.     earliest = rp->expire;
  648.     }
  649. }
  650.     }
  651.     snmplib_debug(6, "LIBSNMP:  Select Info:  %d active, %d requests pending.n",
  652. active, requests);
  653.     if (requests == 0) /* if none are active, skip arithmetic */
  654. return active;
  655.     /*
  656.      * Now find out how much time until the earliest timeout.  This
  657.      * transforms earliest from an absolute time into a delta time, the
  658.      * time left until the select should timeout.
  659.      */
  660.     gettimeofday(&now, (struct timezone *) 0);
  661.     earliest.tv_sec--; /* adjust time to make arithmetic easier */
  662.     earliest.tv_usec += 1000000L;
  663.     earliest.tv_sec -= now.tv_sec;
  664.     earliest.tv_usec -= now.tv_usec;
  665.     while (earliest.tv_usec >= 1000000L) {
  666. earliest.tv_usec -= 1000000L;
  667. earliest.tv_sec += 1;
  668.     }
  669.     if (earliest.tv_sec < 0) {
  670. earliest.tv_sec = 0;
  671. earliest.tv_usec = 0;
  672.     }
  673.     /* if it was blocking before or our delta time is less, reset timeout */
  674.     if (*block == 1 || timercmp(&earliest, timeout, <)) {
  675. *timeout = earliest;
  676. *block = 0;
  677.     }
  678.     return active;
  679. }
  680. /*
  681.  * snmp_timeout should be called whenever the timeout from snmp_select_info 
  682.  * expires, but it is idempotent, so snmp_timeout can be polled (probably a 
  683.  * cpu expensive proposition).  snmp_timeout checks to see if any of the 
  684.  * sessions have an outstanding request that has timed out.  If it finds one 
  685.  * (or more), and that pdu has more retries available, a new packet is formed
  686.  * from the pdu and is resent.  If there are no more retries available, the 
  687.  * callback for the session is used to alert the user of the timeout.
  688.  */
  689. void 
  690. snmp_timeout(void)
  691. {
  692.     struct session_list *slp;
  693.     struct snmp_session *sp;
  694.     struct snmp_internal_session *isp;
  695.     struct request_list *rp, *orp, *freeme = NULL;
  696.     struct timeval now;
  697.     gettimeofday(&now, (struct timezone *) 0);
  698.     /*
  699.      * For each request outstanding, check to see if it has expired.
  700.      */
  701.     for (slp = Sessions; slp; slp = slp->next) {
  702. sp = slp->session;
  703. isp = slp->internal;
  704. orp = NULL;
  705. snmplib_debug(6, "LIBSNMP:  Checking session %sn",
  706.     (sp->peername != NULL) ? sp->peername : "<NULL>");
  707. for (rp = isp->requests; rp; rp = rp->next_request) {
  708.     snmplib_debug(6, "LIBSNMP:  Checking session request %d, expire at %u, Retry %d/%dn",
  709. rp->request_id, rp->expire.tv_sec, rp->retries, sp->retries);
  710.     if (freeme != NULL) {
  711. /* frees rp's after the for loop goes on to the next_request */
  712. xfree((char *) freeme);
  713. freeme = NULL;
  714.     }
  715.     if (timercmp(&rp->expire, &now, <)) {
  716. snmplib_debug(6, "LIBSNMP:  Expired.n");
  717. /* this timer has expired */
  718. if (rp->retries >= sp->retries) {
  719.     /* No more chances, delete this entry */
  720.     sp->callback(TIMED_OUT, sp, rp->pdu->reqid,
  721. rp->pdu, sp->callback_magic);
  722.     if (orp == NULL) {
  723. isp->requests = rp->next_request;
  724.     } else {
  725. orp->next_request = rp->next_request;
  726.     }
  727.     snmp_free_pdu(rp->pdu);
  728.     freeme = rp;
  729.     continue; /* don't update orp below */
  730. } else {
  731.     u_char packet[PACKET_LENGTH];
  732.     int length = PACKET_LENGTH;
  733.     struct timeval tv;
  734.     snmplib_debug(6, "LIBSNMP:  Retransmitting.n");
  735.     /* retransmit this pdu */
  736.     rp->retries++;
  737.     rp->timeout <<= 1;
  738.     if (snmp_build(sp, rp->pdu, packet, &length) < 0) {
  739. snmplib_debug(3, "Error building packetn");
  740.     }
  741.     snmp_dump(packet, length,
  742. "sending", rp->pdu->address.sin_addr);
  743.     gettimeofday(&tv, (struct timezone *) 0);
  744.     if (sendto(isp->sd, (char *) packet, length, 0, (struct sockaddr *) &rp->pdu->address, sizeof(rp->pdu->address)) < 0) {
  745. perror("sendto");
  746.     }
  747.     rp->time = tv;
  748.     tv.tv_usec += rp->timeout;
  749.     tv.tv_sec += tv.tv_usec / 1000000L;
  750.     tv.tv_usec %= 1000000L;
  751.     rp->expire = tv;
  752. }
  753.     }
  754.     orp = rp;
  755. }
  756. if (freeme != NULL) {
  757.     xfree((char *) freeme);
  758.     freeme = NULL;
  759. }
  760.     }
  761. }
  762. /* Print some API stats */
  763. void 
  764. snmp_api_stats(void *outP)
  765. {
  766.     struct session_list *slp;
  767.     struct request_list *rp;
  768.     struct snmp_internal_session *isp;
  769.     FILE *out = (FILE *) outP;
  770.     int active = 0;
  771.     int requests = 0;
  772.     int count = 0;
  773.     int rcount = 0;
  774.     fprintf(out, "LIBSNMP: Session List Dumpn");
  775.     fprintf(out, "LIBSNMP: ----------------------------------------n");
  776.     for (slp = Sessions; slp; slp = slp->next) {
  777. isp = slp->internal;
  778. active++;
  779. count++;
  780. fprintf(out, "LIBSNMP: %2d: Host %sn", count,
  781.     (slp->session->peername == NULL) ? "NULL" : slp->session->peername);
  782. if (isp->requests) {
  783.     /* found another session with outstanding requests */
  784.     requests++;
  785.     rcount = 0;
  786.     for (rp = isp->requests; rp; rp = rp->next_request) {
  787. rcount++;
  788. {
  789.     struct hostent *hp;
  790.     hp = gethostbyaddr((char *) &(rp->pdu->address),
  791. sizeof(u_int), AF_INET);
  792.     fprintf(out, "LIBSNMP: %2d: ReqId %d (%s) (%s)n",
  793. rcount, rp->request_id, snmp_pdu_type(rp->pdu),
  794. (hp == NULL) ? "NULL" : hp->h_name);
  795. }
  796.     }
  797. }
  798. fprintf(out, "LIBSNMP: ----------------------------------------n");
  799.     }
  800.     fprintf(out, "LIBSNMP: Session List: %d active, %d have requests pending.n",
  801. active, requests);
  802. }
  803. #endif