home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / ip / manage / snmp / cmu-snmp1.0 / snmplib / snmp_api.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-09-19  |  28.1 KB  |  934 lines

  1. /***********************************************************
  2.     Copyright 1989 by Carnegie Mellon University
  3.  
  4.                       All Rights Reserved
  5.  
  6. Permission to use, copy, modify, and distribute this software and its 
  7. documentation for any purpose and without fee is hereby granted, 
  8. provided that the above copyright notice appear in all copies and that
  9. both that copyright notice and this permission notice appear in 
  10. supporting documentation, and that the name of CMU not be
  11. used in advertising or publicity pertaining to distribution of the
  12. software without specific, written prior permission.  
  13.  
  14. CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  15. ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
  16. CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
  17. ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  18. WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  19. ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  20. SOFTWARE.
  21. ******************************************************************/
  22. /*
  23.  * snmp_api.c - API for access to snmp.
  24.  */
  25.  
  26. #include <stdio.h>
  27. #include <sys/types.h>
  28. #include <sys/param.h>
  29. #include <sys/time.h>
  30. #include <netinet/in.h>
  31. #include <sys/socket.h>
  32. #include <netdb.h>
  33. #include "asn1.h"
  34. #include "snmp.h"
  35. #include "snmp_impl.h"
  36. #include "snmp_api.h"
  37.  
  38. #define PACKET_LENGTH    1500
  39.  
  40. #ifndef BSD4_3
  41. #define BSD4_2
  42. #endif
  43.  
  44. #ifndef BSD4_3
  45.  
  46. typedef long    fd_mask;
  47. #define NFDBITS    (sizeof(fd_mask) * NBBY)    /* bits per mask */
  48.  
  49. #define    FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
  50. #define    FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
  51. #define    FD_ISSET(n, p)    ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
  52. #define FD_ZERO(p)    bzero((char *)(p), sizeof(*(p)))
  53. #endif
  54.  
  55. oid default_enterprise[] = {1, 3, 6, 1, 4, 1, 3, 1, 1}; /* enterprises.cmu.systems.cmuSNMP */
  56.  
  57. #define DEFAULT_COMMUNITY   "public"
  58. #define DEFAULT_RETRIES        4
  59. #define DEFAULT_TIMEOUT        1000000L
  60. #define DEFAULT_REMPORT        SNMP_PORT
  61. #define DEFAULT_ENTERPRISE  default_enterprise
  62. #define DEFAULT_TIME        0
  63.  
  64. /*
  65.  * Internal information about the state of the snmp session.
  66.  */
  67. struct snmp_internal_session {
  68.     int        sd;        /* socket descriptor for this connection */
  69.     ipaddr  addr;    /* address of connected peer */
  70.     struct request_list *requests;/* Info about outstanding requests */
  71. };
  72.  
  73. /*
  74.  * A list of all the outstanding requests for a particular session.
  75.  */
  76. struct request_list {
  77.     struct request_list *next_request;
  78.     u_long  request_id;    /* request id */
  79.     int        retries;    /* Number of retries */
  80.     u_long timeout;    /* length to wait for timeout */
  81.     struct timeval time; /* Time this request was made */
  82.     struct timeval expire;  /* time this request is due to expire */
  83.     struct snmp_pdu *pdu;   /* The pdu for this request (saved so it can be retransmitted */
  84. };
  85.  
  86. /*
  87.  * The list of active/open sessions.
  88.  */
  89. struct session_list {
  90.     struct session_list *next;
  91.     struct snmp_session *session;
  92.     struct snmp_internal_session *internal;
  93. };
  94.  
  95. struct session_list *Sessions = NULL;
  96.  
  97. u_long Reqid = 0;
  98. int snmp_errno = 0;
  99.  
  100. char *api_errors[4] = {
  101.     "Unknown session",
  102.     "Unknown host",
  103.     "Invalid local port",
  104.     "Unknown Error"
  105. };
  106.  
  107. static char *
  108. api_errstring(snmp_errnumber)
  109.     int    snmp_errnumber;
  110. {
  111.     if (snmp_errnumber <= SNMPERR_BAD_SESSION && snmp_errnumber >= SNMPERR_GENERR){
  112.     return api_errors[snmp_errnumber + 4];
  113.     } else {
  114.     return "Unknown Error";
  115.     }
  116. }
  117.  
  118.  
  119. /*
  120.  * Gets initial request ID for all transactions.
  121.  */
  122. static
  123. init_snmp(){
  124.     struct timeval tv;
  125.  
  126.     gettimeofday(&tv, (struct timezone *)0);
  127.     srandom(tv.tv_sec ^ tv.tv_usec);
  128.     Reqid = random();
  129. }
  130.  
  131. /*
  132.  * Sets up the session with the snmp_session information provided
  133.  * by the user.  Then opens and binds the necessary UDP port.
  134.  * A handle to the created session is returned (this is different than
  135.  * the pointer passed to snmp_open()).  On any error, NULL is returned
  136.  * and snmp_errno is set to the appropriate error code.
  137.  */
  138. struct snmp_session *
  139. snmp_open(session)
  140.     struct snmp_session *session;
  141. {
  142.     struct session_list *slp;
  143.     struct snmp_internal_session *isp;
  144.     u_char *cp;
  145.     int sd;
  146.     u_long addr;
  147.     struct sockaddr_in    me;
  148.     struct hostent *hp;
  149.     struct servent *servp;
  150.  
  151.  
  152.     if (Reqid == 0)
  153.     init_snmp();
  154.  
  155.     /* Copy session structure and link into list */
  156.     slp = (struct session_list *)malloc(sizeof(struct session_list));
  157.     slp->internal = isp = (struct snmp_internal_session *)malloc(sizeof(struct snmp_internal_session));
  158.     bzero((char *)isp, sizeof(struct snmp_internal_session));
  159.     slp->internal->sd = -1; /* mark it not set */
  160.     slp->session = (struct snmp_session *)malloc(sizeof(struct snmp_session));
  161.     bcopy((char *)session, (char *)slp->session, sizeof(struct snmp_session));
  162.     session = slp->session;
  163.     /* now link it in. */
  164.     slp->next = Sessions;
  165.     Sessions = slp;
  166.     /*
  167.      * session now points to the new structure that still contains pointers to
  168.      * data allocated elsewhere.  Some of this data is copied to space malloc'd
  169.      * here, and the pointer replaced with the new one.
  170.      */
  171.  
  172.     if (session->peername != NULL){
  173.     cp = (u_char *)malloc((unsigned)strlen(session->peername) + 1);
  174.     strcpy((char *)cp, session->peername);
  175.     session->peername = (char *)cp;
  176.     }
  177.  
  178.     /* Fill in defaults if necessary */
  179.     if (session->community_len != SNMP_DEFAULT_COMMUNITY_LEN){
  180.     cp = (u_char *)malloc((unsigned)session->community_len);
  181.     bcopy((char *)session->community, (char *)cp, session->community_len);
  182.     } else {
  183.     session->community_len = strlen(DEFAULT_COMMUNITY);
  184.     cp = (u_char *)malloc((unsigned)session->community_len);
  185.     bcopy((char *)DEFAULT_COMMUNITY, (char *)cp, session->community_len);
  186.     }
  187.     session->community = cp;    /* replace pointer with pointer to new data */
  188.  
  189.     if (session->retries == SNMP_DEFAULT_RETRIES)
  190.     session->retries = DEFAULT_RETRIES;
  191.     if (session->timeout == SNMP_DEFAULT_TIMEOUT)
  192.     session->timeout = DEFAULT_TIMEOUT;
  193.     isp->requests = NULL;
  194.  
  195.     /* Set up connections */
  196.     sd = socket(AF_INET, SOCK_DGRAM, 0);
  197.     if (sd < 0){
  198.     perror("socket");
  199.     snmp_errno = SNMPERR_GENERR;
  200.     if (!snmp_close(session)){
  201.         fprintf(stderr, "Couldn't abort session: %s. Exiting\n", api_errstring(snmp_errno));
  202.         exit(1);
  203.     }
  204.     return 0;
  205.     }
  206.     isp->sd = sd;
  207.     if (session->peername != SNMP_DEFAULT_PEERNAME){
  208.     if ((addr = inet_addr(session->peername)) != -1){
  209.         bcopy((char *)&addr, (char *)&isp->addr.sin_addr, sizeof(isp->addr.sin_addr));
  210.     } else {
  211.         hp = gethostbyname(session->peername);
  212.         if (hp == NULL){
  213.         fprintf(stderr, "unknown host: %s\n", session->peername);
  214.         snmp_errno = SNMPERR_BAD_ADDRESS;
  215.         if (!snmp_close(session)){
  216.             fprintf(stderr, "Couldn't abort session: %s. Exiting\n", api_errstring(snmp_errno));
  217.             exit(2);
  218.         }
  219.         return 0;
  220.         } else {
  221.         bcopy((char *)hp->h_addr, (char *)&isp->addr.sin_addr, hp->h_length);
  222.         }
  223.     }
  224.     isp->addr.sin_family = AF_INET;
  225.     if (session->remote_port == SNMP_DEFAULT_REMPORT){
  226.         servp = getservbyname("snmp", "udp");
  227.         if (servp != NULL){
  228.         isp->addr.sin_port = servp->s_port;
  229.         } else {
  230.         isp->addr.sin_port = htons(SNMP_PORT);
  231.         }
  232.     } else {
  233.         isp->addr.sin_port = htons(session->remote_port);
  234.     }
  235.     } else {
  236.     isp->addr.sin_addr.s_addr = SNMP_DEFAULT_ADDRESS;
  237.     }
  238.  
  239.     me.sin_family = AF_INET;
  240.     me.sin_addr.s_addr = INADDR_ANY;
  241.     me.sin_port = htons(session->local_port);
  242.     if (bind(sd, (struct sockaddr *)&me, sizeof(me)) != 0){
  243.     perror("bind");
  244.     snmp_errno = SNMPERR_BAD_LOCPORT;
  245.     if (!snmp_close(session)){
  246.         fprintf(stderr, "Couldn't abort session: %s. Exiting\n", api_errstring(snmp_errno));
  247.         exit(3);
  248.     }
  249.     return 0;
  250.     }
  251.     return session;
  252. }
  253.  
  254.  
  255. /*
  256.  * Free each element in the input request list.
  257.  */
  258. static
  259. free_request_list(rp)
  260.     struct request_list *rp;
  261. {
  262.     struct request_list *orp;
  263.  
  264.     while(rp){
  265.     orp = rp;
  266.     rp = rp->next_request;
  267.     if (orp->pdu != NULL)
  268.         snmp_free_pdu(orp->pdu);
  269.     free((char *)orp);
  270.     }
  271. }
  272.  
  273. /*
  274.  * Close the input session.  Frees all data allocated for the session,
  275.  * dequeues any pending requests, and closes any sockets allocated for
  276.  * the session.  Returns 0 on error, 1 otherwise.
  277.  */
  278. int 
  279. snmp_close(session)
  280.     struct snmp_session *session;
  281. {
  282.     struct session_list *slp = NULL, *oslp = NULL;
  283.  
  284.     if (Sessions->session == session){    /* If first entry */
  285.     slp = Sessions;
  286.     Sessions = slp->next;
  287.     } else {
  288.     for(slp = Sessions; slp; slp = slp->next){
  289.         if (slp->session == session){
  290.         if (oslp)   /* if we found entry that points here */
  291.             oslp->next = slp->next;    /* link around this entry */
  292.         break;
  293.         }
  294.         oslp = slp;
  295.     }
  296.     }
  297.     /* If we found the session, free all data associated with it */
  298.     if (slp){
  299.     if (slp->session->community != NULL)
  300.         free((char *)slp->session->community);
  301.     if(slp->session->peername != NULL)
  302.         free((char *)slp->session->peername);
  303.     free((char *)slp->session);
  304.     if (slp->internal->sd != -1)
  305.         close(slp->internal->sd);
  306.     free_request_list(slp->internal->requests);
  307.     free((char *)slp->internal);
  308.     free((char *)slp);
  309.     } else {
  310.     snmp_errno = SNMPERR_BAD_SESSION;
  311.     return 0;
  312.     }
  313.     return 1;
  314. }
  315.  
  316. /*
  317.  * Takes a session and a pdu and serializes the ASN PDU into the area
  318.  * pointed to by packet.  out_length is the size of the data area available.
  319.  * Returns the length of the completed packet in out_length.  If any errors
  320.  * occur, -1 is returned.  If all goes well, 0 is returned.
  321.  */
  322. static int
  323. snmp_build(session, pdu, packet, out_length)
  324.     struct snmp_session    *session;
  325.     struct snmp_pdu    *pdu;
  326.     register u_char    *packet;
  327.     int            *out_length;
  328. {
  329.     u_char  buf[PACKET_LENGTH];
  330.     register u_char  *cp;
  331.     struct variable_list *vp;
  332.     int        length;
  333.     long    zero = 0;
  334.     int        totallength;
  335.  
  336.     length = *out_length;
  337.     cp = packet;
  338.     for(vp = pdu->variables; vp; vp = vp->next_variable){
  339.     cp = snmp_build_var_op(cp, vp->name, &vp->name_length, vp->type, vp->val_len, (u_char *)vp->val.string, &length);
  340.     if (cp == NULL)
  341.         return -1;
  342.     }
  343.     totallength = cp - packet;
  344.  
  345.     length = PACKET_LENGTH;
  346.     cp = asn_build_header(buf, &length, (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), totallength);
  347.     if (cp == NULL)
  348.     return -1;
  349.     bcopy((char *)packet, (char *)cp, totallength);
  350.     totallength += cp - buf;
  351.  
  352.     length = *out_length;
  353.     if (pdu->command != TRP_REQ_MSG){
  354.     /* request id */
  355.     cp = asn_build_int(packet, &length,
  356.         (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
  357.         (long *)&pdu->reqid, sizeof(pdu->reqid));
  358.     if (cp == NULL)
  359.         return -1;
  360.     /* error status */
  361.     cp = asn_build_int(cp, &length,
  362.         (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
  363.         (long *)&pdu->errstat, sizeof(pdu->errstat));
  364.     if (cp == NULL)
  365.         return -1;
  366.     /* error index */
  367.     cp = asn_build_int(cp, &length,
  368.         (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
  369.         (long *)&pdu->errindex, sizeof(pdu->errindex));
  370.     if (cp == NULL)
  371.         return -1;
  372.     } else {    /* this is a trap message */
  373.     /* enterprise */
  374.     cp = asn_build_objid(packet, &length,
  375.         (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
  376.         (oid *)pdu->enterprise, pdu->enterprise_length);
  377.     if (cp == NULL)
  378.         return -1;
  379.     /* agent-addr */
  380.     cp = asn_build_string(cp, &length,
  381.         (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
  382.         (u_char *)&pdu->agent_addr.sin_addr.s_addr, sizeof(pdu->agent_addr.sin_addr.s_addr));
  383.     if (cp == NULL)
  384.         return -1;
  385.     /* generic trap */
  386.     cp = asn_build_int(cp, &length,
  387.         (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
  388.         (long *)&pdu->trap_type, sizeof(pdu->trap_type));
  389.     if (cp == NULL)
  390.         return -1;
  391.     /* specific trap */
  392.     cp = asn_build_int(cp, &length,
  393.         (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
  394.         (long *)&pdu->specific_type, sizeof(pdu->specific_type));
  395.     if (cp == NULL)
  396.         return -1;
  397.     /* timestamp  */
  398.     cp = asn_build_int(cp, &length,
  399.         (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
  400.         (long *)&pdu->time, sizeof(pdu->time));
  401.     if (cp == NULL)
  402.         return -1;
  403.     }
  404.     if (length < totallength)
  405.     return -1;
  406.     bcopy((char *)buf, (char *)cp, totallength);
  407.     totallength += cp - packet;
  408.  
  409.     length = PACKET_LENGTH;
  410.     cp = asn_build_header(buf, &length, (u_char)pdu->command, totallength);
  411.     if (cp == NULL)
  412.     return -1;
  413.     if (length < totallength)
  414.     return -1;
  415.     bcopy((char *)packet, (char *)cp, totallength);
  416.     totallength += cp - buf;
  417.  
  418.     length = *out_length;
  419.     cp = snmp_auth_build(packet, &length, session->community, &session->community_len, &zero, totallength);
  420.     if (cp == NULL)
  421.     return -1;
  422.     if ((*out_length - (cp - packet)) < totallength)
  423.     return -1;
  424.     bcopy((char *)buf, (char *)cp, totallength);
  425.     totallength += cp - packet;
  426.     *out_length = totallength;
  427.     return 0;
  428. }
  429.  
  430. /*
  431.  * Parses the packet recieved on the input session, and places the data into
  432.  * the input pdu.  length is the length of the input packet.  If any errors
  433.  * are encountered, -1 is returned.  Otherwise, a 0 is returned.
  434.  */
  435. static int
  436. snmp_parse(session, pdu, data, length)
  437.     struct snmp_session *session;
  438.     struct snmp_pdu *pdu;
  439.     u_char  *data;
  440.     int        length;
  441. {
  442.     u_char  msg_type;
  443.     u_char  type;
  444.     u_char  *var_val;
  445.     long    version;
  446.     int        len, four;
  447.     u_char community[128];
  448.     int community_length = 128;
  449.     struct variable_list *vp;
  450.     oid        objid[MAX_NAME_LEN], *op;
  451.  
  452.     /* authenticates message and returns length if valid */
  453.     data = snmp_auth_parse(data, &length, community, &community_length, &version);
  454.     if (data == NULL)
  455.     return -1;
  456.     if (version != SNMP_VERSION_1){
  457.     fprintf(stderr, "Wrong version: %d\n", version);
  458.     fprintf(stderr, "Continuing anyway\n");
  459.     }
  460.     if (session->authenticator){
  461.     data = session->authenticator(data, &length, community, community_length);
  462.     if (data == NULL)
  463.         return 0;
  464.     }
  465.     data = asn_parse_header(data, &length, &msg_type);
  466.     if (data == NULL)
  467.     return -1;
  468.     pdu->command = msg_type;
  469.     if (pdu->command != TRP_REQ_MSG){
  470.     data = asn_parse_int(data, &length, &type, (long *)&pdu->reqid, sizeof(pdu->reqid));
  471.     if (data == NULL)
  472.         return -1;
  473.     data = asn_parse_int(data, &length, &type, (long *)&pdu->errstat, sizeof(pdu->errstat));
  474.     if (data == NULL)
  475.         return -1;
  476.     data = asn_parse_int(data, &length, &type, (long *)&pdu->errindex, sizeof(pdu->errindex));
  477.     if (data == NULL)
  478.         return -1;
  479.     } else {
  480.     pdu->enterprise_length = MAX_NAME_LEN;
  481.     data = asn_parse_objid(data, &length, &type, objid, &pdu->enterprise_length);
  482.     if (data == NULL)
  483.         return -1;
  484.     pdu->enterprise = (oid *)malloc(pdu->enterprise_length * sizeof(oid));
  485.     bcopy((char *)objid, (char *)pdu->enterprise, pdu->enterprise_length * sizeof(oid));
  486.  
  487.     four = 4;
  488.     data = asn_parse_string(data, &length, &type, (u_char *)&pdu->agent_addr.sin_addr.s_addr, &four);
  489.     if (data == NULL)
  490.         return -1;
  491.     data = asn_parse_int(data, &length, &type, (long *)&pdu->trap_type, sizeof(pdu->trap_type));
  492.     if (data == NULL)
  493.         return -1;
  494.     data = asn_parse_int(data, &length, &type, (long *)&pdu->specific_type, sizeof(pdu->specific_type));
  495.     if (data == NULL)
  496.         return -1;
  497.     data = asn_parse_int(data, &length, &type, (long *)&pdu->time, sizeof(pdu->time));
  498.     if (data == NULL)
  499.         return -1;
  500.     }
  501.     data = asn_parse_header(data, &length, &type);
  502.     if (data == NULL)
  503.     return -1;
  504.     if (type != (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR))
  505.     return -1;
  506.     while((int)length > 0){
  507.     if (pdu->variables == NULL){
  508.         pdu->variables = vp = (struct variable_list *)malloc(sizeof(struct variable_list));
  509.     } else {
  510.         vp->next_variable = (struct variable_list *)malloc(sizeof(struct variable_list));
  511.         vp = vp->next_variable;
  512.     }
  513.     vp->next_variable = NULL;
  514.     vp->val.string = NULL;
  515.     vp->name = NULL;
  516.     vp->name_length = MAX_NAME_LEN;
  517.     data = snmp_parse_var_op(data, objid, &vp->name_length, &vp->type, &vp->val_len, &var_val, (int *)&length);
  518.     if (data == NULL)
  519.         return -1;
  520.     op = (oid *)malloc((unsigned)vp->name_length * sizeof(oid));
  521.     bcopy((char *)objid, (char *)op, vp->name_length * sizeof(oid));
  522.     vp->name = op;
  523.  
  524.     len = PACKET_LENGTH;
  525.     switch((short)vp->type){
  526.         case ASN_INTEGER:
  527.         case COUNTER:
  528.         case GAUGE:
  529.         case TIMETICKS:
  530.         vp->val.integer = (long *)malloc(sizeof(long));
  531.         vp->val_len = sizeof(long);
  532.         asn_parse_int(var_val, &len, &vp->type, (long *)vp->val.integer, sizeof(vp->val.integer));
  533.         break;
  534.         case ASN_OCTET_STR:
  535.         case IPADDRESS:
  536.         case OPAQUE:
  537.         vp->val.string = (u_char *)malloc((unsigned)vp->val_len);
  538.         asn_parse_string(var_val, &len, &vp->type, vp->val.string, &vp->val_len);
  539.         break;
  540.         case ASN_OBJECT_ID:
  541.         vp->val_len = MAX_NAME_LEN;
  542.         asn_parse_objid(var_val, &len, &vp->type, objid, &vp->val_len);
  543.         vp->val_len *= sizeof(oid);
  544.         vp->val.objid = (oid *)malloc((unsigned)vp->val_len);
  545.         bcopy((char *)objid, (char *)vp->val.objid, vp->val_len);
  546.         break;
  547.         case ASN_NULL:
  548.         break;
  549.         default:
  550.         fprintf(stderr, "bad type returned (%x)\n", vp->type);
  551.         break;
  552.     }
  553.     }
  554.     return 0;
  555. }
  556.  
  557. /*
  558.  * Sends the input pdu on the session after calling snmp_build to create
  559.  * a serialized packet.  If necessary, set some of the pdu data from the
  560.  * session defaults.  Add a request corresponding to this pdu to the list
  561.  * of outstanding requests on this session, then send the pdu.
  562.  * Returns the request id of the generated packet if applicable, otherwise 1.
  563.  * On any error, 0 is returned.
  564.  * The pdu is freed by snmp_send() unless a failure occured.
  565.  */
  566. int
  567. snmp_send(session, pdu)
  568.     struct snmp_session *session;
  569.     struct snmp_pdu    *pdu;
  570. {
  571.     struct session_list *slp;
  572.     struct snmp_internal_session *isp = NULL;
  573.     u_char  packet[PACKET_LENGTH];
  574.     int length = PACKET_LENGTH;
  575.     struct request_list *rp;
  576.     struct timeval tv;
  577.  
  578.     for(slp = Sessions; slp; slp = slp->next){
  579.     if (slp->session == session){
  580.         isp = slp->internal;
  581.         break;
  582.     }
  583.     }
  584.     if (isp == NULL){
  585.     snmp_errno = SNMPERR_BAD_SESSION;
  586.     return 0;
  587.     }
  588.     if (pdu->command == GET_REQ_MSG || pdu->command == GETNEXT_REQ_MSG
  589.     || pdu->command == GET_RSP_MSG || pdu->command == SET_REQ_MSG){
  590.     if (pdu->reqid == SNMP_DEFAULT_REQID)
  591.         pdu->reqid = ++Reqid;
  592.     if (pdu->errstat == SNMP_DEFAULT_ERRSTAT)
  593.         pdu->errstat = 0;
  594.     if (pdu->errindex == SNMP_DEFAULT_ERRINDEX)
  595.         pdu->errindex = 0;
  596.     } else {
  597.     /* fill in trap defaults */
  598.     pdu->reqid = 1;    /* give a bogus non-error reqid for traps */
  599.     if (pdu->enterprise_length == SNMP_DEFAULT_ENTERPRISE_LENGTH){
  600.         pdu->enterprise = (oid *)malloc(sizeof(DEFAULT_ENTERPRISE));
  601.         bcopy((char *)DEFAULT_ENTERPRISE, (char *)pdu->enterprise, sizeof(DEFAULT_ENTERPRISE));
  602.         pdu->enterprise_length = sizeof(DEFAULT_ENTERPRISE)/sizeof(oid);
  603.     }
  604.     if (pdu->time == SNMP_DEFAULT_TIME)
  605.         pdu->time = DEFAULT_TIME;
  606.     }
  607.     if (pdu->address.sin_addr.s_addr == SNMP_DEFAULT_ADDRESS){
  608.     if (isp->addr.sin_addr.s_addr != SNMP_DEFAULT_ADDRESS){
  609.         bcopy((char *)&isp->addr, (char *)&pdu->address, sizeof(pdu->address));
  610.     } else {
  611.         fprintf(stderr, "No remote IP address specified\n");
  612.         snmp_errno = SNMPERR_BAD_ADDRESS;
  613.         return 0;
  614.     }
  615.     }
  616.     
  617.  
  618.     if (snmp_build(session, pdu, packet, &length) < 0){
  619.     fprintf(stderr, "Error building packet\n");
  620.     snmp_errno = SNMPERR_GENERR;
  621.     return 0;
  622.     }
  623.     if (snmp_dump_packet){
  624.     int count;
  625.  
  626.     for(count = 0; count < length; count++){
  627.         printf("%02X ", packet[count]);
  628.         if ((count % 16) == 15)
  629.         printf("\n");
  630.     }
  631.     printf("\n\n");
  632.     }
  633.  
  634.     gettimeofday(&tv, (struct timezone *)0);
  635.     if (sendto(isp->sd, (char *)packet, length, 0, (struct sockaddr *)&pdu->address, sizeof(pdu->address)) < 0){
  636.     perror("sendto");
  637.     snmp_errno = SNMPERR_GENERR;
  638.     return 0;
  639.     }
  640.     if (pdu->command == GET_REQ_MSG || pdu->command == GETNEXT_REQ_MSG || pdu->command == SET_REQ_MSG){
  641.     /* set up to expect a response */
  642.     rp = (struct request_list *)malloc(sizeof(struct request_list));
  643.     rp->next_request = isp->requests;
  644.     isp->requests = rp;
  645.     rp->pdu = pdu;
  646.     rp->request_id = pdu->reqid;
  647.  
  648.     rp->retries = 1;
  649.     rp->timeout = session->timeout;
  650.     rp->time = tv;
  651.     tv.tv_usec += rp->timeout;
  652.     tv.tv_sec += tv.tv_usec / 1000000L;
  653.     tv.tv_usec %= 1000000L;
  654.     rp->expire = tv;
  655.     }
  656.     return pdu->reqid;
  657. }
  658.  
  659. /*
  660.  * Frees the pdu and any malloc'd data associated with it.
  661.  */
  662. void
  663. snmp_free_pdu(pdu)
  664.     struct snmp_pdu *pdu;
  665. {
  666.     struct variable_list *vp, *ovp;
  667.  
  668.     vp = pdu->variables;
  669.     while(vp){
  670.     if (vp->name)
  671.         free((char *)vp->name);
  672.     if (vp->val.string)
  673.         free((char *)vp->val.string);
  674.     ovp = vp;
  675.     vp = vp->next_variable;
  676.     free((char *)ovp);
  677.     }
  678.     if (pdu->enterprise)
  679.     free((char *)pdu->enterprise);
  680.     free((char *)pdu);
  681. }
  682.  
  683.  
  684. /*
  685.  * Checks to see if any of the fd's set in the fdset belong to
  686.  * snmp.  Each socket with it's fd set has a packet read from it
  687.  * and snmp_parse is called on the packet received.  The resulting pdu
  688.  * is passed to the callback routine for that session.  If the callback
  689.  * routine returns successfully, the pdu and it's request are deleted.
  690.  */
  691. void
  692. snmp_read(fdset)
  693.     fd_set  *fdset;
  694. {
  695.     struct session_list *slp;
  696.     struct snmp_session *sp;
  697.     struct snmp_internal_session *isp;
  698.     u_char packet[PACKET_LENGTH];
  699.     struct sockaddr_in    from;
  700.     int length, fromlength;
  701.     struct snmp_pdu *pdu;
  702.     struct request_list *rp, *orp;
  703.  
  704.     for(slp = Sessions; slp; slp = slp->next){
  705.     if (FD_ISSET(slp->internal->sd, fdset)){
  706.         sp = slp->session;
  707.         isp = slp->internal;
  708.         fromlength = sizeof from;
  709.         length = recvfrom(isp->sd, (char *)packet, PACKET_LENGTH, 0, (struct sockaddr *)&from, &fromlength);
  710.         if (length == -1)
  711.         perror("recvfrom");
  712.         if (snmp_dump_packet){
  713.         int count;
  714.  
  715.         printf("recieved %d bytes from %s:\n", length, inet_ntoa(from.sin_addr));
  716.         for(count = 0; count < length; count++){
  717.             printf("%02X ", packet[count]);
  718.             if ((count % 16) == 15)
  719.             printf("\n");
  720.         }
  721.         printf("\n\n");
  722.         }
  723.  
  724.         pdu = (struct snmp_pdu *)malloc(sizeof(struct snmp_pdu));
  725.         pdu->address = from;
  726.         pdu->reqid = 0;
  727.         pdu->variables = NULL;
  728.         pdu->enterprise = NULL;
  729.         pdu->enterprise_length = 0;
  730.         if (snmp_parse(sp, pdu, packet, length) != SNMP_ERR_NOERROR){
  731.         fprintf(stderr, "Mangled packet\n");
  732.         snmp_free_pdu(pdu);
  733.         return;
  734.         }
  735.  
  736.         if (pdu->command == GET_RSP_MSG){
  737.         for(rp = isp->requests; rp; rp = rp->next_request){
  738.             if (rp->request_id == pdu->reqid){
  739.             if (sp->callback(RECEIVED_MESSAGE, sp, pdu->reqid, pdu, sp->callback_magic) == 1){
  740.                 /* successful, so delete request */
  741.                 orp = rp;
  742.                 if (isp->requests == orp){
  743.                 /* first in list */
  744.                 isp->requests = orp->next_request;
  745.                 } else {
  746.                 for(rp = isp->requests; rp; rp = rp->next_request){
  747.                     if (rp->next_request == orp){
  748.                     rp->next_request = orp->next_request;    /* link around it */
  749.                     break;
  750.                     }
  751.                 }
  752.                 }
  753.                 snmp_free_pdu(orp->pdu);
  754.                 free((char *)orp);
  755.                 break;  /* there shouldn't be any more request with the same reqid */
  756.             }
  757.             }
  758.         }
  759.         } else if (pdu->command == GET_REQ_MSG || pdu->command == GETNEXT_REQ_MSG
  760.             || pdu->command == TRP_REQ_MSG || pdu->command == SET_REQ_MSG){
  761.         sp->callback(RECEIVED_MESSAGE, sp, pdu->reqid, pdu, sp->callback_magic);
  762.         }
  763.         snmp_free_pdu(pdu);
  764.     }
  765.     }
  766. }
  767.  
  768. /*
  769.  * Returns info about what snmp requires from a select statement.
  770.  * numfds is the number of fds in the list that are significant.
  771.  * All file descriptors opened for SNMP are OR'd into the fdset.
  772.  * If activity occurs on any of these file descriptors, snmp_read
  773.  * should be called with that file descriptor set
  774.  *
  775.  * The timeout is the latest time that SNMP can wait for a timeout.  The
  776.  * select should be done with the minimum time between timeout and any other
  777.  * timeouts necessary.  This should be checked upon each invocation of select.
  778.  * If a timeout is received, snmp_timeout should be called to check if the
  779.  * timeout was for SNMP.  (snmp_timeout is idempotent)
  780.  *
  781.  * Block is 1 if the select is requested to block indefinitely, rather than time out.
  782.  * If block is input as 1, the timeout value will be treated as undefined, but it must
  783.  * be available for setting in snmp_select_info.  On return, if block is true, the value
  784.  * of timeout will be undefined.
  785.  *
  786.  * snmp_select_info returns the number of open sockets.  (i.e. The number of sessions open)
  787.  */
  788. int
  789. snmp_select_info(numfds, fdset, timeout, block)
  790.     int        *numfds;
  791.     fd_set  *fdset;
  792.     struct timeval *timeout;
  793.     int        *block; /* should the select block until input arrives (i.e. no input) */
  794. {
  795.     struct session_list *slp;
  796.     struct snmp_internal_session *isp;
  797.     struct request_list *rp;
  798.     struct timeval now, earliest;
  799.     int active = 0, requests = 0;
  800.  
  801.     timerclear(&earliest);
  802.     /*
  803.      * For each request outstanding, add it's socket to the fdset,
  804.      * and if it is the earliest timeout to expire, mark it as lowest.
  805.      */
  806.     for(slp = Sessions; slp; slp = slp->next){
  807.     active++;
  808.     isp = slp->internal;
  809.     if ((isp->sd + 1) > *numfds)
  810.         *numfds = (isp->sd + 1);
  811.     FD_SET(isp->sd, fdset);
  812.     if (isp->requests){
  813.         /* found another session with outstanding requests */
  814.         requests++;
  815.         for(rp = isp->requests; rp; rp = rp->next_request){
  816.         if (!timerisset(&earliest) || timercmp(&rp->expire, &earliest, <))
  817.             earliest = rp->expire;
  818.         }
  819.     }
  820.     }
  821.     if (requests == 0)    /* if none are active, skip arithmetic */
  822.     return active;
  823.  
  824.     /*
  825.      * Now find out how much time until the earliest timeout.  This
  826.      * transforms earliest from an absolute time into a delta time, the
  827.      * time left until the select should timeout.
  828.      */
  829.     gettimeofday(&now, (struct timezone *)0);
  830.     earliest.tv_sec--;    /* adjust time to make arithmetic easier */
  831.     earliest.tv_usec += 1000000L;
  832.     earliest.tv_sec -= now.tv_sec;
  833.     earliest.tv_usec -= now.tv_usec;
  834.     while (earliest.tv_usec >= 1000000L){
  835.     earliest.tv_usec -= 1000000L;
  836.     earliest.tv_sec += 1;
  837.     }
  838.     if (earliest.tv_sec < 0){
  839.     earliest.tv_sec = 0;
  840.     earliest.tv_usec = 0;
  841.     }
  842.  
  843.     /* if it was blocking before or our delta time is less, reset timeout */
  844.     if (*block == 1 || timercmp(&earliest, timeout, <)){
  845.     *timeout = earliest;
  846.     *block = 0;
  847.     }
  848.     return active;
  849. }
  850.  
  851. /*
  852.  * snmp_timeout should be called whenever the timeout from snmp_select_info expires,
  853.  * but it is idempotent, so snmp_timeout can be polled (probably a cpu expensive
  854.  * proposition).  snmp_timeout checks to see if any of the sessions have an
  855.  * outstanding request that has timed out.  If it finds one (or more), and that
  856.  * pdu has more retries available, a new packet is formed from the pdu and is
  857.  * resent.  If there are no more retries available, the callback for the session
  858.  * is used to alert the user of the timeout.
  859.  */
  860. void
  861. snmp_timeout(){
  862.     struct session_list *slp;
  863.     struct snmp_session *sp;
  864.     struct snmp_internal_session *isp;
  865.     struct request_list *rp, *orp, *freeme = NULL;
  866.     struct timeval now;
  867.  
  868.     gettimeofday(&now, (struct timezone *)0);
  869.     /*
  870.      * For each request outstanding, check to see if it has expired.
  871.      */
  872.     for(slp = Sessions; slp; slp = slp->next){
  873.     sp = slp->session;
  874.     isp = slp->internal;
  875.     orp = NULL;
  876.     for(rp = isp->requests; rp; rp = rp->next_request){
  877.         if (freeme != NULL){    /* frees rp's after the for loop goes on to the next_request */
  878.         free((char *)freeme);
  879.         freeme = NULL;
  880.         }
  881.         if (timercmp(&rp->expire, &now, <)){
  882.         /* this timer has expired */
  883.         if (rp->retries >= sp->retries){
  884.             /* No more chances, delete this entry */
  885.             sp->callback(TIMED_OUT, sp, rp->pdu->reqid, rp->pdu, sp->callback_magic);
  886.             if (orp == NULL){
  887.             isp->requests = rp->next_request;
  888.             } else {
  889.             orp->next_request = rp->next_request;
  890.             }
  891.             snmp_free_pdu(rp->pdu);
  892.             freeme = rp;
  893.             continue;    /* don't update orp below */
  894.         } else {
  895.             u_char  packet[PACKET_LENGTH];
  896.             int length = PACKET_LENGTH;
  897.             struct timeval tv;
  898.  
  899.             /* retransmit this pdu */
  900.             rp->retries++;
  901.             rp->timeout <<= 1;
  902.             if (snmp_build(sp, rp->pdu, packet, &length) < 0){
  903.             fprintf(stderr, "Error building packet\n");
  904.             }
  905.             if (snmp_dump_packet){
  906.             int count;
  907.  
  908.             for(count = 0; count < length; count++){
  909.                 printf("%02X ", packet[count]);
  910.                 if ((count % 16) == 15)
  911.                 printf("\n");
  912.             }
  913.             printf("\n\n");
  914.             }
  915.             gettimeofday(&tv, (struct timezone *)0);
  916.             if (sendto(isp->sd, (char *)packet, length, 0, (struct sockaddr *)&rp->pdu->address, sizeof(rp->pdu->address)) < 0){
  917.             perror("sendto");
  918.             }
  919.             rp->time = tv;
  920.             tv.tv_usec += rp->timeout;
  921.             tv.tv_sec += tv.tv_usec / 1000000L;
  922.             tv.tv_usec %= 1000000L;
  923.             rp->expire = tv;
  924.         }
  925.         }
  926.         orp = rp;
  927.     }
  928.     if (freeme != NULL){
  929.         free((char *)freeme);
  930.         freeme = NULL;
  931.     }
  932.     }
  933. }
  934.