/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* tcpip720 src/tcpip/usr/samples/snmpd/dpi2/dpi_sample.c 1.2             */
/*                                                                        */
/* Licensed Materials - Property of IBM                                   */
/*                                                                        */
/* Restricted Materials of IBM                                            */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 1996,1998              */
/* All Rights Reserved                                                    */
/*                                                                        */
/* US Government Users Restricted Rights - Use, duplication or            */
/* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.      */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
static char sccsid[] = "@(#)38	1.2  src/tcpip/usr/samples/snmpd/dpi2/dpi_sample.c, snmp, tcpip720 2/17/98 17:12:31";
/*
\begin{verbatim}
*/
/* ------------------------------------------------------------------*
 * $Id:$
 * ------------------------------------------------------------------*/

/*********************************************************************/
/*                                                                   */
/* SNMP-DPI API - SNMP Distributed Protocol Interface                */
/*                Application Programming Interface                  */
/*                                                                   */
/* May 15, 1997 - Version 2.0.25                                     */
/*                                                                   */
/* Copyright    - (C) International Business Machines Corp. 1994-1997*/
/*                                                                   */
/*   Permission to use, copy, modify, and distribute this software   */
/*   and its documentation for any lawful purpose and without fee is */
/*   hereby granted, provided that this notice be retained unaltered,*/
/*   and that the names of IBM and all other contributors shall not  */
/*   be used in advertising or publicity pertaining to distribution  */
/*   of the software without specific written prior permission.      */
/*   No contributor makes any representations about the suitability  */
/*   of this software for any purpose.  It is provided "as is"       */
/*   without express or implied warranty.                            */
/*                                                                   */
/*   IBM AND ALL OTHER CONTRIBUTORS DISCLAIM ALL WARRANTIES WITH     */
/*   REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF    */
/*   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE,    */
/*   AND NON-INFRINGEMENT.                                           */
/*                                                                   */
/*   IN NO EVENT SHALL IBM OR ANY OTHER CONTRIBUTOR BE LIABLE FOR    */
/*   ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN      */
/*   CONTRACT, TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN   */
/*   CONNECTION WITH, THE USE OR PERFORMANCE OF THIS SOFTWARE.       */
/*                                                                   */
/* dpi_sample.c - a simple sample DPI subagent to show how the SNMP  */
/*                DPI API can be used to implement a public or       */
/*                private MIB.                                       */
/*                                                                   */
/* Notes:                                                            */
/* 1. This sample does not implement a real MIB but just uses an     */
/*    arbitrary subtree as a sample. We have named it dpiSimpleMIB   */
/*    and there is also a MIB writeup useable by the SMIC compiler.  */
/* 2. The code is meant to show in each segment of the code how to   */
/*    use the incoming DPI packet and how to create a DPI response   */
/*    packet. It does NOT do the proper checking on the data.        */
/* 3. Nevertheless, the code shows enough of the DPI API so that a   */
/*    programmer can see how the DPI API can be used.                */
/* 4. The code does do proper searching for the next lexicographical */
/*    OID for a GETNEXT. This is because in GETNEXT it is easy to    */
/*    make errors, and we try to show how to do it. However, we do   */
/*    not show how to do it in tables which may be indexed in quite  */
/*    a few different ways. This is something to add in the future.  */
/*    For time being, the DPI sub-agent programmer must try to do it */
/*    without a sample.                                              */
/*********************************************************************/

/* $Log:$
 *-------------------------------------------------------------------*/

#define VERSION \
   "2.0.25, May 15 1997, Compiled "__DATE__" at "__TIME__

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include <snmp_dpi.h>                     /* DPI 2.0 API definitions */

#ifdef DPI_VERY_MINIMAL_SUBAGENT          /* with VERY minimal agent */
#define DPIconnect_to_agent_SHM(a) -1;    /* there is no support for */
#define DPIdebug(a) ;                     /* these functions         */
#endif /* ndef DPI_VERY_MINIMAL_SUBAGENT */

static int handle;                        /* handle has global scope */

/*********************************************************************/
/* Our OBJECT IDENTIFIERS as defined in the dpiSimpleMIB             */
/* Your SUBAGENT OID could be different.                             */
/* We are using COUNTER64 in this sample, but please realize that    */
/* this is a SNMPv2 SMI-type, and so not all agents and managers     */
/* may support this type yet. You may want to remove it if you are   */
/* working in an SNMPv1 world.                                       */
/*********************************************************************/
#define DPI_SIMPLE_SUBAGENT   "1.3.6.1.4.1.2.2.1.5" 
#define DPI_SIMPLE_MIB        "1.3.6.1.4.1.2.2.1.5."
#define DPI_SIMPLE_INTEGER    "1.0"       /* dpiSimpleInteger.0      */
#define DPI_SIMPLE_STRING     "2.0"       /* dpiSimpleString.0       */
#define DPI_SIMPLE_COUNTER32  "3.0"       /* dpiSimpleCounter32.0    */
#define DPI_SIMPLE_COUNTER64  "4.0"       /* dpiSimpleCounter64.0    */

#define IP_LOOPBACK  "localhost" /* Common IP loopback address       */
#define SNMP_COMMUNITY  "public" /* Commonly used public community   */

/*********************************************************************/
/* these are our 4 variables dpiSimpleInteger, dpiSimpleString and   */
/* dpiSimpleCounter32 and dpiSimpleCounter64.                        */
/* We use them global throughout this file.                          */
/* For value2 (writeable) we need ptrs to save new and old values,   */
/* so that we can properly do SET/COMMIT/UNDO sequences.             */
/*********************************************************************/
static long int      value1      = 5;
#define              value2_p      cur_val_p   /* writable object    */
#define              value2_len    cur_val_len /* writable object    */
static char         *cur_val_p   = (char *)0;
static char         *new_val_p   = (char *)0;
static char         *old_val_p   = (char *)0;
static unsigned long cur_val_len = 0;
static unsigned long new_val_len = 0;
static unsigned long old_val_len = 0;
static unsigned long value3      = 1;
#ifndef EXCLUDE_SNMP_V2_SUPPORT
static snmp_dpi_u64  value4      = {0x80000000,1L};
#endif /* ndef EXCLUDE_SNMP_V2_SUPPORT */

static int            shared_mem  = 0;          /* default use TCP    */
static unsigned short timeout     = 3;          /* default timeout    */

static void do_connect_and_open(char *hostname_p, char *community_p);
static void do_register(void);
static int  do_trap(void);
static int do_get(snmp_dpi_hdr *hdr_p,  snmp_dpi_get_packet *pack_p);
static int do_set(snmp_dpi_hdr *hdr_p,  snmp_dpi_set_packet *pack_p);
static int do_next(snmp_dpi_hdr *hdr_p, snmp_dpi_next_packet *pack_p);
static int do_unreg(snmp_dpi_hdr *hdr_p,snmp_dpi_ureg_packet *pack_p);
static int do_close(snmp_dpi_hdr *hdr_p,snmp_dpi_close_packet *pack_p);

#ifndef DPI_VERY_MINIMAL_SUBAGENT         /* with VERY minimal agent */
static void usage(char *cmd_p)
{
printf("\nUsage: ");
printf("%s [-d [level]] [-h hostname] [-c community] [-shm]\n",cmd_p);
printf("\nWhere:  -d level     - debug level, default level=1\n");
printf("        -h host      - send request to specified host\n");
printf("        -c community - use specified community name\n");
printf("        -shm         - connect over shared memory, not TCP\n");
printf("\nDefaults: %s -d 0 -h %s -c %s\n",
       cmd_p, IP_LOOPBACK, SNMP_COMMUNITY);
printf("\n%s - %s\n",__FILE__, VERSION);
exit(1);
}
#else
#define usage(a) exit(1)
#endif /* ndef DPI_VERY_MINIMAL_SUBAGENT */

/*********************************************************************/
/* main line function                                                */
/*********************************************************************/
/*                                                                   */
/* First we call a function to open up a connection to the agent and */
/* to identify ourselves to the agent. That function sets the global */
/* variable "handle" which represents our connection to the agent.   */
/*                                                                   */
/* Second we call a function to register our sample MIB subtree.     */
/*                                                                   */
/* After we have registered our sample MIB subtree with the agent,   */
/* we must expect that SNMP requests for that subtree will be passed */
/* for processing by the us. The requests will arrive in the form of */
/* DPI packets on the connection that the we have established. So we */
/* go into a while loop to await DPI packets from the agent.         */
/*                                                                   */
/* The subagent cannot know in advance which kind of packet arrives  */
/* from the agent. So we await a DPI packet (forever), then we parse */
/* the packet, check the packet type and process the request based   */
/* on the DPI packet type. A call to pDPIpacket (which expects as    */
/* argument a ptr to the encoded/serialized DPI packet) returns us   */
/* a ptr to a DPI parse tree. The ptr points to a snmp_dpi_hdr       */
/* structure which looks as follows:                                 */
/*                                                                   */
/*   struct snmp_dpi_hdr {                                           */
/*     unsigned char  proto_major;                                   */
/*     unsigned char  proto_version;                                 */
/*     unsigned char  proto_release;                                 */
/*     unsigned short packet_id;                                     */
/*     unsigned char  packet_type;                                   */
/*     union {                                                       */
/*        snmp_dpi_reg_packet      *reg_p;                           */
/*        snmp_dpi_ureg_packet     *ureg_p;                          */
/*        snmp_dpi_get_packet      *get_p;                           */
/*        snmp_dpi_next_packet     *next_p;                          */
/*        snmp_dpi_bulk_packet     *bulk_p;                          */
/*        snmp_dpi_set_packet      *set_p;                           */
/*        snmp_dpi_resp_packet     *resp_p;                          */
/*        snmp_dpi_trap_packet     *trap_p;                          */
/*        snmp_dpi_open_packet     *open_p;                          */
/*        snmp_dpi_close_packet    *close_p;                         */
/*        unsigned char            *any_p;                           */
/*     } data_u;                                                     */
/*   };                                                              */
/*   typedef struct snmp_dpi_hdr    snmp_dpi_hdr;                    */
/*   #define snmp_dpi_hdr_NULL_p    ((snmp_dpi_hdr *)0)              */
/*                                                                   */
/* Given the DPI parse tree we decide how to process the DPI packet. */
/* The following code sample demonstrates the high level process of  */
/* a DPI subagent.                                                   */
/*********************************************************************/
main(int argc, char *argv[], char *envp[])
{
       unsigned char *packet_p;
       int            i     = 0;
       int            rc    = 0;
#ifndef DPI_VERY_MINIMAL_SUBAGENT         /* with VERY minimal agent */
       int            debug = 0;
#endif /* ndef DPI_VERY_MINIMAL_SUBAGENT */
       unsigned long  length;
       snmp_dpi_hdr  *hdr_p;
       char          *hostname_p  = IP_LOOPBACK;
       char          *community_p = SNMP_COMMUNITY;
       char          *cmd_p       = "";

       if (argc >= 1) cmd_p = argv[0];
    
       for (i=1; i < argc; i++) {
          if (strcmp(argv[i],"-h") == 0) {
             if (i+1 >= argc) {
                printf("Need hostname\n\n");
                usage(cmd_p);
             } /* endif */
             hostname_p = argv[++i];
#ifndef DPI_VERY_MINIMAL_SUBAGENT         /* with VERY minimal agent */
          } else if (strcmp(argv[i],"-c") == 0) {
             if (i+1 >= argc) {
                printf("Need community name\n\n");
                usage(cmd_p);
             } /* endif */
             community_p = argv[++i];
          } else if (strcmp(argv[i],"-shm") == 0) {
             shared_mem = 1;
          } else if (strcmp(argv[i],"-d") == 0) {
             if (i+1 >= argc) {
                debug = 1;
                continue;
             }
             if ((strlen(argv[i+1]) == 1) && isdigit(*argv[i+1])) {
                i++;
                debug = atoi(argv[i]);
             } else {
                debug = 1;
             } /* endif */
#endif /* ndef DPI_VERY_MINIMAL_SUBAGENT */
          } else {
             usage(cmd_p);
          } /* endif */
       } /* endfor */
    
#ifndef DPI_VERY_MINIMAL_SUBAGENT
       if (debug) {
          printf("\n%s - %s\n",__FILE__, VERSION);
          DPIdebug(debug);                /* turn on DPI dubugging   */
          timeout += 6;                   /* longer timeout please   */
       } /* endif */
#endif /* ndef DPI_VERY_MINIMAL_SUBAGENT */
    
       /* first init value2_p, our dpiSimpleString (DisplayString)   */
       /* since we treat it as display string keep terminating NULL  */
       value2_p = (char *) malloc(strlen("Initial String")+1);
       if (value2_p) {
          memcpy(value2_p,"Initial String",strlen("Initial String")+1);
          value2_len = strlen("Initial String")+1;
       } /* endif */

       do_connect_and_open(hostname_p,
                            community_p); /* connect and DPI-OPEN    */
 
       do_register();                     /* register our subtree    */
 
       do_trap();                         /* issue a trap as sample  */

       while (rc == 0) {                  /* do forever              */
         rc = DPIawait_packet_from_agent( /* wait for a DPI packet   */
                 handle,                  /* on this connection      */
                 -1,                      /* wait forever            */
                 &packet_p,               /* receives ptr to packet  */
                 &length);                /* receives packet length  */
 
         if (rc != DPI_RC_OK) exit(1);    /* If it failed, exit      */
 
         hdr_p = pDPIpacket(packet_p);    /* parse DPI packet        */
         if (hdr_p == snmp_dpi_hdr_NULL_p)/* If we fail to parse it  */
            exit(1);                      /* then exit               */

         switch(hdr_p->packet_type) {     /* handle by DPI type      */
         case SNMP_DPI_GET:
           rc = do_get(hdr_p, 
                       hdr_p->data_u.get_p);
           break;
         case SNMP_DPI_GETNEXT:
           rc = do_next(hdr_p, 
                        hdr_p->data_u.next_p);
           break;
         case SNMP_DPI_SET:
         case SNMP_DPI_COMMIT:
         case SNMP_DPI_UNDO:
           rc = do_set(hdr_p, 
                       hdr_p->data_u.set_p);
           break;
         case SNMP_DPI_CLOSE:
           rc = do_close(hdr_p, 
                         hdr_p->data_u.close_p);
           break;
         case SNMP_DPI_UNREGISTER:
           rc = do_unreg(hdr_p, 
                         hdr_p->data_u.ureg_p);
           break;
         default:
           printf("Unexpected DPI packet type %d\n",
                  hdr_p->packet_type);
           rc = -1;
         } /* endswitch */
         if (rc) exit(1);
       } /* endwhile */
 
       return(0);
} /* end of main() */

/*********************************************************************/
/* Function to setup a connection and identify ourselves to agent    */
/*********************************************************************/
/* Before a subagent can receive or send any DPI packets from/to the */
/* SNMP DPI capable agent, it must "connect" to the agent and then   */
/* identify itself to the agent.                                     */
/*                                                                   */
/* Setting up the connection is one simple call to the the function  */
/* DPIconnect_to_agent_TCP(). It expects two arguments:              */
/*  - a hostname (name or IpAdress in dot notation) that specifies   */
/*    where the agent is running. Often, the name "loopback" or      */
/*    "localhost" can be used if the subagent runs on the same       */
/*    system as the agent.                                           */
/*  - a community name which is used to obtain the dpi TCP port from */
/*    the agent. Internally that is done by sending a regular SNMP   */
/*    GET request to the agent. In an open environment, we probably  */
/*    can use the well know community name "public".                 */
/* The function returns an negative error code if an error occurs.   */
/* If the connection setup is successful, then it returns a handle   */
/* which represents the connection, and which we must use on         */
/* subsequent calls to send or await DPI packets.                    */
/*                                                                   */
/* The second step is to identify the subagent to the agent. This is */
/* done by making a DPI-OPEN packet, sending it to the agent and     */
/* then awaiting the response from the agent. The agent may accept   */
/* or deny the OPEN request.                                         */
/* Making a DPI-OPEN packet is done by calling mkDPIopen() which     */
/* expects 6 arguments:                                              */
/*  - A unique subagent identification (an Object Identifier).       */
/*  - A description. Might be the NULL string ("")                   */
/*  - Overall subagent timeout in seconds. The agent uses this value */
/*    as a timeout value for a response when it sends a request to   */
/*    the subagent. Note that the agent may have a maximum value for */
/*    this timeout that will be used if you exceed it.               */
/*  - The maximum number of varBinds per DPI packet that the         */
/*    subagent is willing (or capable) to handle.                    */
/*  - The character set we want to use for string representation.    */
/*    Often it is easiest to choose the native character set.        */
/*  - Length of a password (zero means no password)                  */
/*  - Ptr to the password (or NULL if no password)                   */
/*    It depends on the agent if subagents must specify a password   */
/*    to open up a connection.                                       */
/* The function returns a ptr to a static buffer holding the DPI     */
/* packet if success. If it fails, it returns a NULL ptr.            */
/*                                                                   */
/* Once the DPI-OPEN packet has been created, you must send it to    */
/* the agent. We can use the DPIsend_packet_to_agent() function      */
/* which expects 3 arguments:                                        */
/*  - The handle of a connection (from DPIconnect_to_agent_TCP)      */
/*  - A ptr to the DPI packet (from mkDPIopen)                       */
/*  - The length of the packet.                                      */
/*    The snmp_dpi.h include file provides a macro DPI_PACKET_LEN    */
/*    that calculates the packet length of a DPI packet.             */
/* This function returs DPI_RC_OK (value zero) if success, otherwise */
/* an appropriate DPI_RC_xxx error code as defined in snmp_dpi.h.    */
/*                                                                   */
/* Now we must wait for a response to the DPI-OPEN. To await such    */
/* a response, you call the DPIawait_packet_from_agent() function,   */
/* which expects 4 arguments:                                        */
/*  - The handle of a connection (from DPIconnect_to_agent_TCP)      */
/*  - A timeout in seconds (max time to wait for response)           */
/*  - A ptr to a ptr, which will receive a ptr to a static buffer    */
/*    containing the awaited DPI packet. If the system fails to      */
/*    receive a packet, then a NULL ptr will be stored.              */
/*  - A ptr to a long integer (32-bit) which will receive the length */
/*    of the awaited packet. On a failure it will be set to zero.    */
/* This function returs DPI_RC_OK (value zero) if success, otherwise */
/* an appropriate DPI_RC_xxx error code as defined in snmp_dpi.h.    */
/*                                                                   */
/* The last step is to ensure that we indeed got a DPI-RESPONSE      */
/* back from the agent, and if we did then we must ensure that the   */
/* agent indeed accepted us as a valid subagent. This will be shown  */
/* by the error_code field in the DPI response packet.               */
/*                                                                   */
/* The following code sample demonstrates how to setup a connection  */
/* and then "open" it by identifying yourself to the agent.          */
/*********************************************************************/
static void do_connect_and_open(char *hostname_p, char *community_p)
{
       unsigned char *packet_p;
       int            rc;
       unsigned long  length;
       snmp_dpi_hdr  *hdr_p;

       if (shared_mem) {                  /* if shared memory wanted */
          handle =                        /* then (SHM) connect to   */
             DPIconnect_to_agent_SHM(1);  /* always use 1 as queueID */
       } else {
          handle =  
                DPIconnect_to_agent_TCP(  /* (TCP) connect to agent  */
                   hostname_p,            /* on this host            */
                   community_p);          /* snmp community name     */
       } /* endif */
 
       if (handle < 0) exit(1);           /* If it failed, exit      */
 
       packet_p = mkDPIopen(              /* Make DPI-OPEN packet    */
                    DPI_SIMPLE_SUBAGENT,  /* Our identification      */
                   "Simple DPI subAgent", /* description             */
                    10L,                  /* Our overall timeout     */
                    1L,                   /* max varBinds/packet     */
                    DPI_NATIVE_CSET,      /* native character set    */
                    0L,                   /* password length         */
                    (unsigned char *)0);  /* ptr to password         */
 
       if (!packet_p) exit(1);            /* If it failed, exit      */
 
       rc  = DPIsend_packet_to_agent(     /* send OPEN packet        */
                handle,                   /* on this connection      */
                packet_p,                 /* this is the packet      */
                DPI_PACKET_LEN(packet_p));/* and this is its length  */
 
       if (rc != DPI_RC_OK) exit(1);      /* If it failed, exit      */
 
       rc  = DPIawait_packet_from_agent(  /* wait for response       */
                handle,                   /* on this connection      */
                10,                       /* timeout in seconds      */
                &packet_p,                /* receives ptr to packet  */
                &length);                 /* receives packet length  */
 
       if (rc != DPI_RC_OK) exit(1);      /* If it failed, exit      */
 
       hdr_p = pDPIpacket(packet_p);      /* parse DPI packet        */
       if (hdr_p == snmp_dpi_hdr_NULL_p)  /* If we fail to parse it  */
          exit(1);                        /* then exit               */

       if (hdr_p->packet_type != SNMP_DPI_RESPONSE) exit(1);

       rc = hdr_p->data_u.resp_p->error_code;
       if (rc != SNMP_ERROR_DPI_noError) exit(1);

} /* end of do_connect_and_open() */

/*********************************************************************/
/* Register our sample dpiSimpleMIB subtree with the agent           */
/*********************************************************************/
/*                                                                   */
/* After we have setup a connection to the agent and after we have   */
/* identified ourselves, we must register one or more MIB subtrees   */
/* for which we want to be responsible (handle all SNMP requests).   */
/*                                                                   */
/* To do so, the subagent must create a DPI-REGISTER packet and send */
/* it to the agent. The agent will then send a response to indicate  */
/* success or failure of the register request.                       */
/*                                                                   */
/* To create a DPI-REGISTER packet, the subagent uses a call to the  */
/* mkDPIregister() function, which expects these arguments:          */
/*  - A timeout value (in seconds) for this subtree. If you specify  */
/*    zero, then your overall timeout value (specified in DPI-OPEN)  */
/*    is used. Here you can specify a different value if you expect  */
/*    longer processing time for a specific subtree.                 */
/*  - A requested priority. Multiple subagents may register the same */
/*    subtree at different priorities (0 is better than 1 and so on).*/
/*    The agent considers the subagent with the best priority to be  */
/*    the active subagent for the subtree. If you specify -1, then   */
/*    you ask for the best priority available. If you specify 0,     */
/*    then you ask for a better priority than any existing subagent  */
/*    may already have.                                              */
/*  - The MIB subtree which you want to control. You must specify thi*/
/*    one with a trailing dot.                                       */
/*  - Our choice with rehard to GetBulk processing. We can ask the   */
/*    agent to pass it on or to map it into multiple GetNext packets.*/
/* The function returns a ptr to a static buffer holding the DPI     */
/* packet if success. If it fails, it returns a NULL ptr.            */
/*                                                                   */
/* Now we must send this DPI-REGISTER packet to the agent. We use    */
/* the DPIsend_packet_to_agent() function to do so, in a similar way */
/* as we did send the DPI_OPEN packet. Then we wait for a response   */
/* from the agent. Again, we use the DPIawait_packet_from_agent()    */
/* function in the same way as we awaited a response on the DPI-OPEN */
/* request. Once we have received the response, we must check the    */
/* return code to ensure that registration was successful.           */
/*                                                                   */
/* The following code sample demonstrates how to register one MIB    */
/* subtree with the agent.                                           */
/*********************************************************************/
static void do_register(void)
{
       unsigned char *packet_p;
       int            rc;
       unsigned long  length;
       snmp_dpi_hdr  *hdr_p;

       packet_p = mkDPIregister(          /* Make DPIregister packet */
                    timeout,              /* timeout in seconds      */
                    0,                    /* requested priority      */
                    DPI_SIMPLE_MIB,       /* ptr to the subtree      */
                    DPI_BULK_NO);         /* Map GetBulk into GetNext*/

       if (!packet_p) exit(1);            /* If it failed, exit      */
 
       rc  = DPIsend_packet_to_agent(     /* send REGISTER packet    */
                handle,                   /* on this connection      */
                packet_p,                 /* this is the packet      */
                DPI_PACKET_LEN(packet_p));/* and this is its length  */
 
       if (rc != DPI_RC_OK) exit(1);      /* If it failed, exit      */
 
       rc  = DPIawait_packet_from_agent(  /* wait for response       */
                handle,                   /* on this connection      */
                10,                       /* timeout in seconds      */
                &packet_p,                /* receives ptr to packet  */
                &length);                 /* receives packet length  */
 
       if (rc != DPI_RC_OK) exit(1);      /* If it failed, exit      */
 
       hdr_p = pDPIpacket(packet_p);      /* parse DPI packet        */
       if (hdr_p == snmp_dpi_hdr_NULL_p)  /* If we fail to parse it  */
          exit(1);                        /* then exit               */

       if (hdr_p->packet_type != SNMP_DPI_RESPONSE) exit(1);

       rc = hdr_p->data_u.resp_p->error_code;
       if (rc != SNMP_ERROR_DPI_noError) exit(1);
} /* end of do_register() */

/*********************************************************************/
/* Issuing a TRAP                                                    */
/*********************************************************************/
/*                                                                   */
/* A trap can be issued at any time. To do so you must create a trap */
/* packet and send it to the agent. With the TRAP, you can pass all  */
/* sorts of varBinds if you want. In this sample, we pass 2 varBinds */
/* one with integer data and one with an octet string.               */
/* You can also pass an EnterpriseID, but with DPI 2.0, the agent    */
/* will use your subagentID as the enterpriseID if you do not pass   */
/* one with the trap. In most cases that will probably be fine.      */
/*                                                                   */
/* So we must first prepare a varBind list (chain) that contains     */
/* the two variables that we want to pass along with the trap.       */
/* To do so we must prepare a chanin of 2 snmp_dpi_set_packet        */
/* structures, which looks like:                                     */
/*                                                                   */
/*   struct dpi_set_packet {                                         */
/*     char                   *object_p;       ptr to OIDstring      */
/*     char                   *group_p;        ptr to subtree        */
/*     char                   *instance_p;     ptr to rest of OID    */
/*     unsigned char           value_type;     SNMP_TYPE_xxxx        */
/*     unsigned short          value_len;      value length          */
/*     char                   *value_p;        ptr to value itself   */
/*     struct dpi_set_packet  *next_p;         ptr to next in chain  */
/*   };                                                              */
/*   typedef struct dpi_set_packet       snmp_dpi_set_packet;        */
/*   #define snmp_dpi_set_packet_NULL_p  ((snmp_dpi_set_packet *)0)  */
/*                                                                   */
/* We can use the mkDPIset() function to prepare such a structure.   */
/* This function expects the following arguments:                    */
/*  - A ptr to an existing snmp_dpi_set_packet structure if the new  */
/*    varBind must be added to an existing chain of varBinds.        */
/*    If this is the first (or the only) varBind in the chain, then  */
/*    pass the snmp_dpi_set_packet_NULL_p ptr to indicate this.      */
/*  - a ptr to the subtree that we registered.                       */
/*  - a ptr to the rest of the OID, in other words the piece that    */
/*    follows the subtree.                                           */
/*  - the value type of the value to be bound to the variable name.  */
/*    This is must be one of the SNMP_TYPE_xxx values as defined in  */
/*    the snmp_dpi.h include file.                                   */
/*  - the length of the value (for integer type values this must be  */
/*    a length of 4. So we always work with 32-bit signed or         */
/*    unsigned integers (except of course for the Counter64 type,    */
/*    for those we must point to a snmp_dpi_u64 structure and pass   */
/*    the length of that structure).                                 */
/*  - a ptr to the value.                                            */
/* Memory for the varBind is dynamically allocated and the data      */
/* itself is copied. So upon return we can dispose of our own ptrs   */
/* and allocated memory as we please. If the call is successful,     */
/* then a ptr is returned:                                           */
/*  - to a new snmp_dpi_set_packet if it is the first/only varBind   */
/*  - to the existing snmp_dpi_set_packet that we passed on the call.*/
/*    In this case, the new packed has been chained to the end of    */
/*    the varBind list.                                              */
/* If the mkDPIset() call fails, a NULL ptr is returned.             */
/*                                                                   */
/* Once we have prepared the SET-varBind data, we can create a DPI   */
/* TRAP packet. To do so we can use the mkDPItrap() function, which  */
/* expects these arguments:                                          */
/*  - the generic trap code, use 6 for enterpriseSpecific trap type. */
/*  - the specific trap type. This is a type that is defined by      */
/*    the MIB which we are implementing. So in our sample we just    */
/*    use a 1.                                                       */
/*  - a ptr to a chain of varBinds or the NULL ptr if no varBinds    */
/*    need to be passed with the trap                                */
/*  - a ptr to the enterprise OID if we ewant to use a different     */
/*    enterpriseID than the OID we used to identify ourselves as     */
/*    a subagent at DPI-OPEN time.                                   */
/*                                                                   */
/* Here follows a code sample to create an enterpriseSepcific trap   */
/* with specific type 1, and passing 2 varBinds, the first one with  */
/* our object 1, instance 0, Integer32 value; the second one with    */
/* our object 2, instance 0, Octet String. We pass no enterpriseID.  */
/*********************************************************************/
static int do_trap(void)
{
       unsigned char       *packet_p;
       int                  rc;
       snmp_dpi_set_packet *varBind_p, *set_p;

       varBind_p =                        /* init the varBind chain  */
          snmp_dpi_set_packet_NULL_p;     /* to a NULL pointer       */

       varBind_p = mkDPIset(              /* Make DPI set packet     */
                     varBind_p,           /* ptr to varBind chain    */
                     DPI_SIMPLE_MIB,      /* ptr to subtree          */
                     DPI_SIMPLE_INTEGER,  /* ptr to rest of OID      */
                     SNMP_TYPE_Integer32, /* value type Integer 32   */
                     sizeof(value1),      /* length of value         */
                     &value1);            /* ptr to value            */

       if (!varBind_p) return(-1);        /* If it failed, return    */

       set_p = mkDPIset(                     /* Make DPI set packet  */
                     varBind_p,              /* ptr to varBind chain */
                     DPI_SIMPLE_MIB,         /* ptr to subtree       */
                     DPI_SIMPLE_STRING,      /* ptr to rest of OID   */
                     SNMP_TYPE_DisplayString,/* value type           */
                     value2_len,             /* length of value      */
                     value2_p);              /* ptr to value         */

       if (!set_p) {			/* if we failed... then	     */
	  fDPIset(varBind_p);		/* free earlier varBinds     */
	  return(-1);			/* If it failed, return	     */
       }

       set_p = mkDPIset(                     /* Make DPI set packet  */
                     varBind_p,              /* ptr to varBind chain */
                     DPI_SIMPLE_MIB,         /* ptr to subtree       */
                     DPI_SIMPLE_COUNTER32,   /* ptr to rest of OID   */
                     SNMP_TYPE_Counter32,    /* value type           */
                     sizeof(value3),         /* length of value      */
                     &value3);               /* ptr to value         */

       if (!set_p) {			/* if we failed... then	     */
	  fDPIset(varBind_p);		/* free earlier varBinds     */
	  return(-1);			/* If it failed, return	     */
       }

#ifndef EXCLUDE_SNMP_V2_SUPPORT
                                             /*                *Apr23*/
       set_p = mkDPIset(                     /* Make DPI set packet  */
                     varBind_p,              /* ptr to varBind chain */
                     DPI_SIMPLE_MIB,         /* ptr to subtree       */
                     DPI_SIMPLE_COUNTER64,   /* ptr to rest of OID   */
                     SNMP_TYPE_Counter64,    /* value type           */
                     sizeof(value4),         /* length of value      */
                     &value4);               /* ptr to value         */

       if (!set_p) {			/* if we failed... then	     */
	  fDPIset(varBind_p);		/* free earlier varBinds     */
	  return(-1);			/* If it failed, return	     */
       }

#endif /* ndef EXCLUDE_SNMP_V2_SUPPORT */

       packet_p = mkDPItrap(              /* Make DPItrap packet     */
                    6,                    /* enterpriseSpecific      */
                    1,                    /* specific type = 1       */
                    varBind_p,            /* varBind data, and use   */
                    (char *)0);           /* default enterpriseID    */

       if (!packet_p) return(-1);         /* If it failed, return    */
 
       rc  = DPIsend_packet_to_agent(     /* send TRAP packet        */
                handle,                   /* on this connection      */
                packet_p,                 /* this is the packet      */
                DPI_PACKET_LEN(packet_p));/* and this is its length  */
 
       return(rc);                        /* return retcode          */
} /* end of do_trap() */

/*********************************************************************/
/* Processing a GET request                                          */
/*********************************************************************/
/*                                                                   */
/* A get request is pretty easy to process. When the DPI packet is   */
/* parsed, the snmp_dpi_hdr structure will show in the packet_type   */
/* that this is a SNMP_DPI_GET packet. In that case, the data_u field*/
/* contains a ptr to a GET-varBind, which is represented in an       */
/* snmp_dpi_get_packet structure:                                    */
/*                                                                   */
/*   struct dpi_get_packet {                                         */
/*     char                   *object_p;       ptr to OIDstring      */
/*     char                   *group_p;        ptr to subtree        */
/*     char                   *instance_p;     ptr to rest of OID    */
/*     struct dpi_get_packet  *next_p;         ptr to next in chain  */
/*   };                                                              */
/*   typedef struct dpi_get_packet       snmp_dpi_get_packet;        */
/*   #define snmp_dpi_get_packet_NULL_p  ((snmp_dpi_get_packet *)0)  */
/*                                                                   */
/* So, assuming we have registered example subtree dpiSimpleMIB      */
/* and a GET request comes in for one variable dpiSimpleInteger.0    */
/* (so that is object 1 instance 0 in our subtree), then the fields  */
/* in the snmp_dpi_get_packet would have ptrs that point to:         */
/*                                                                   */
/*   object_p    ->  "1.3.6.1.4.1.2.2.1.5.1.0"                       */
/*   group_p     ->  "1.3.6.1.4.1.2.2.1.5."                          */
/*   instance_p  ->  "1.0"                                           */
/*   next_p      ->  snmp_dpi_get_packet_NULL_p                      */
/*                                                                   */
/* If there are multiple varBinds in a GET request, then each one    */
/* is represented in a snmp_dpi_get_packet structure and all the     */
/* snmp_dpi_get_packet structures are chained via the next ptr.      */
/* As long as the next ptr is not the snmp_dpi_get_packet_NULL_p     */
/* pointer then there are more varBinds in the list.                 */
/*                                                                   */
/* Now we can analyze the varBind stucture for whatever checking we  */
/* want to do. Once we are ready to make a response that contains    */
/* the value of the variable, then we first prepare a SET-varBind    */
/* which is represented in an snmp_dpi_set_packet structure:         */
/*                                                                   */
/*   struct dpi_set_packet {                                         */
/*     char                   *object_p;       ptr to OIDstring      */
/*     char                   *group_p;        ptr to subtree        */
/*     char                   *instance_p;     ptr to rest of OID    */
/*     unsigned char           value_type;     SNMP_TYPE_xxxx        */
/*     unsigned short          value_len;      value length          */
/*     char                   *value_p;        ptr to value itself   */
/*     struct dpi_set_packet  *next_p;         ptr to next in chain  */
/*   };                                                              */
/*   typedef struct dpi_set_packet       snmp_dpi_set_packet;        */
/*   #define snmp_dpi_set_packet_NULL_p  ((snmp_dpi_set_packet *)0)  */
/*                                                                   */
/* We can use the mkDPIset() function to prepare such a structure.   */
/* This function expects the following arguments:                    */
/*  - A ptr to an existing snmp_dpi_set_packet structure if the new  */
/*    varBind must be added to an existing chain of varBinds.        */
/*    If this is the first (or the only) varBind in the chain, then  */
/*    pass the snmp_dpi_set_packet_NULL_p ptr to indicate this.      */
/*  - a ptr to the subtree that we registered.                       */
/*  - a ptr to the rest of the OID, in other words the piece that    */
/*    follows the subtree.                                           */
/*  - the value type of the value to be bound to the variable name.  */
/*    This is must be one of the SNMP_TYPE_xxx values as defined in  */
/*    the snmp_dpi.h include file.                                   */
/*  - the length of the value (for integer type values this must be  */
/*    a length of 4. So we always work with 32-bit signed or         */
/*    unsigned integers (except of course for the Counter64 type,    */
/*    for those we must point to a snmp_dpi_u64 structure and pass   */
/*    the length of that structure).                                 */
/*  - a ptr to the value.                                            */
/* Memory for the varBind is dynamically allocated and the data      */
/* itself is copied. So upon return we can dispose of our own ptrs   */
/* and allocated memory as we please. If the call is successful,     */
/* then a ptr is returned:                                           */
/*  - to a new snmp_dpi_set_packet if it is the first/only varBind   */
/*  - to the existing snmp_dpi_set_packet that we passed on the call.*/
/*    In this case, the new packed has been chained to the end of    */
/*    the varBind list.                                              */
/* If the mkDPIset() call fails, a NULL ptr is returned.             */
/*                                                                   */
/* Once we have prepared the SET-varBind data, we can create a DPI   */
/* RESPONSE packet. To do so we can use the mkDPIresponse() function */
/* which expects these arguments:                                    */
/*  - a ptr to an snmp_dpi_hdr. We should use the hdr of the parsed  */
/*    incoming packet. It is used to copy the packet_id from the     */
/*    request into the response, such that the agent can correlate   */
/*    the response to a request.                                     */
/*  - a return code (snmp error code). If success, this should be    */
/*    SNMP_ERROR_noError (value zero). If failure, it must be one    */
/*    of the SNMP_ERROR_xxxx values as defined in the snmp_dpi.h     */
/*    include file.                                                  */
/*    Note that a request for a non-existing object or instance is   */
/*    not considered an error. Instead, we must pass a value type    */
/*    of SNMP_TYPE_noSuchObject or SNMP_TYPE_noSuchInstance          */
/*    respectively.                                                  */
/*    These 2 value types have an implicit value of NULL, so we can  */
/*    pass a zero length and a NULL ptr for the value in this case.  */
/*  - The index of the varBind in error (starts counting at 1).      */
/*    Pass zero if no error occured, else pass the proper index of   */
/*    the first varBind for which an error was detected.             */
/*  - a ptr to a (chain of) snmp_dpi_set_packets (varBinds) to be    */
/*    returned as response to the GET request.                       */
/*    If an error was detected, then an snmp_dpi_set_packet_NULL_p   */
/*    ptr may be passed.                                             */
/*                                                                   */
/* Here follows a code sample to return a response. We assume that   */
/* there are no errors in the request, but proper code should do the */
/* checking for that. For instance, we return a noSuchInstance if    */
/* the instance is not exactly what we expect and a noSuchObject if  */
/* the object instanceID is greater than 3 (like 4.0). But there     */
/* might be no instanceID at all, and we should check for that too.  */
/*********************************************************************/
static int do_get(snmp_dpi_hdr *hdr_p, snmp_dpi_get_packet *pack_p)
{
       unsigned char       *packet_p;
       int                  rc;
       snmp_dpi_set_packet *varBind_p;

       varBind_p =                        /* init the varBind chain  */
          snmp_dpi_set_packet_NULL_p;     /* to a NULL pointer       */

       if (pack_p->instance_p &&
           (strcmp(pack_p->instance_p,"1.0") == 0))
       {
         varBind_p = mkDPIset(            /* Make DPI set packet     */
                     varBind_p,           /* ptr to varBind chain    */
                     pack_p->group_p,     /* ptr to subtree          */
                     pack_p->instance_p,  /* ptr to rest of OID      */
                     SNMP_TYPE_Integer32, /* value type Integer 32   */
                     sizeof(value1),      /* length of value         */
                     &value1);            /* ptr to value            */
       } else if (pack_p->instance_p &&
           (strcmp(pack_p->instance_p,"2.0") == 0))
       {
         varBind_p = mkDPIset(               /* Make DPI set packet  */
                     varBind_p,              /* ptr to varBind chain */
                     pack_p->group_p,        /* ptr to subtree       */
                     pack_p->instance_p,     /* ptr to rest of OID   */
                     SNMP_TYPE_DisplayString,/* value type           */
                     value2_len,             /* length of value      */
                     value2_p);              /* ptr to value         */
       } else if (pack_p->instance_p &&
           (strcmp(pack_p->instance_p,"3.0") == 0))
       {
         varBind_p = mkDPIset(               /* Make DPI set packet  */
                     varBind_p,              /* ptr to varBind chain */
                     pack_p->group_p,        /* ptr to subtree       */
                     pack_p->instance_p,     /* ptr to rest of OID   */
                     SNMP_TYPE_Counter32,    /* value type           */
                     sizeof(value3),         /* length of value      */
                     &value3);               /* ptr to value         */

#ifndef EXCLUDE_SNMP_V2_SUPPORT
       } else if (pack_p->instance_p &&      /*                *Apr23*/
           (strcmp(pack_p->instance_p,"4.0") == 0))
       {
         varBind_p = mkDPIset(               /* Make DPI set packet  */
                     varBind_p,              /* ptr to varBind chain */
                     pack_p->group_p,        /* ptr to subtree       */
                     pack_p->instance_p,     /* ptr to rest of OID   */
                     SNMP_TYPE_Counter64,    /* value type           */
                     sizeof(value4),         /* length of value      */
                     &value4);               /* ptr to value   *Apr23*/
       } else if (pack_p->instance_p &&
           (strcmp(pack_p->instance_p,"4")>0))
       {
#else
       } else if (pack_p->instance_p &&
           (strcmp(pack_p->instance_p,"3")>0))
       {
#endif /* ndef EXCLUDE_SNMP_V2_SUPPORT */
         varBind_p = mkDPIset(               /* Make DPI set packet  */
                    varBind_p,               /* ptr to varBind chain */
                    pack_p->group_p,         /* ptr to subtree       */
                    pack_p->instance_p,      /* ptr to rest of OID   */
                    SNMP_TYPE_noSuchObject,  /* value type           */
                    0L,                      /* length of value      */
                    (unsigned char *)0);     /* ptr to value         */
       } else {
         varBind_p = mkDPIset(               /* Make DPI set packet  */
                    varBind_p,               /* ptr to varBind chain */
                    pack_p->group_p,         /* ptr to subtree       */
                    pack_p->instance_p,      /* ptr to rest of OID   */
                    SNMP_TYPE_noSuchInstance,/* value type           */
                    0L,                      /* length of value      */
                    (unsigned char *)0);     /* ptr to value         */
       } /* endif */

       if (!varBind_p) return(-1);        /* If it failed, return    */

       packet_p = mkDPIresponse(          /* Make DPIresponse packet */
                    hdr_p,                /* ptr parsed request      */
                    SNMP_ERROR_noError,   /* all is OK, no error     */
                    0L,                   /* index is zero, no error */
                    varBind_p);           /* varBind response data   */

       if (!packet_p) return(-1);         /* If it failed, return    */
 
       rc  = DPIsend_packet_to_agent(     /* send RESPONSE packet    */
                handle,                   /* on this connection      */
                packet_p,                 /* this is the packet      */
                DPI_PACKET_LEN(packet_p));/* and this is its length  */
 
       return(rc);                        /* return retcode          */
} /* end of do_get() */

/*********************************************************************/
/* Processing a GETNEXT request                                      */
/*********************************************************************/
/*                                                                   */
/* A getnext request is more difficult to process. When a DPI packet */
/* is parsed, the snmp_dpi_hdr structure shows in the packet_type    */
/* that this is a SNMP_DPI_GETNEXT packet, and so the data_u field   */
/* contains a ptr to a GETNEXT-varBind, which is represented in an   */
/* snmp_dpi_next_packet structure:                                   */
/*                                                                   */
/* For simplicity and easier understanding we will discuss           */
/* the GETNEXT for a scalar object, which only has one instance.     */
/* For columnar objects, which may have multiple instances,          */
/* the process is a bit more complicated. We assume that a DPI       */
/* subagent programmer can handle that once he/she undestands        */
/* the basics of GETNEXT processing in a DPI subagent.               */
/*                                                                   */
/*   struct dpi_next_packet {                                        */
/*     char                   *object_p;       ptr to OIDstring      */
/*     char                   *group_p;        ptr to subtree        */
/*     char                   *instance_p;     ptr to rest of OID    */
/*     struct dpi_next_packet *next_p;         ptr to next in chain  */
/*   };                                                              */
/*   typedef struct dpi_next_packet      snmp_dpi_next_packet;       */
/*   #define snmp_dpi_next_packet_NULL_p ((snmp_dpi_next_packet *)0) */
/*                                                                   */
/* So, assuming we have registered example subtree dpiSimpleMIB      */
/* and a GETNEXT arrives for one variable dpiSimpleInteger.0         */
/* (so that is object 1 instance 0 in our subtree), then the fields  */
/* in the snmp_dpi_get_packet structure would have ptrs pointing to: */
/*                                                                   */
/*   object_p    ->  "1.3.6.1.4.1.2.2.1.5.1.0"                       */
/*   group_p     ->  "1.3.6.1.4.1.2.2.1.5."                          */
/*   instance_p  ->  "1.0"                                           */
/*   next        ->  snmp_dpi_get_packet_NULL_p                      */
/*                                                                   */
/* If there are multiple varBinds in a GETNEXT request, then each    */
/* one is represented in a snmp_dpi_get_packet structure and all     */
/* the snmp_dpi_get_packet structures are chained via the next ptr.  */
/* As long as the next ptr is not the snmp_dpi_next_packet_NULL_p    */
/* pointer then there are more varBinds in the list.                 */
/*                                                                   */
/* Now we can analyze the varBind stucture for whatever checking we  */
/* want to do. Then we must find out which OID is the one that       */
/* lexicographically follows the one in the request. And it is that  */
/* OID with its value that we must return as a response. So we must  */
/* now also set the proper OID in the response.                      */
/* Once we are ready to make a response that contains the new OID    */
/* and the value of that variable, then we must first prepare a      */
/* SET-varBind which is represented in an snmp_dpi_set_packet:       */
/*                                                                   */
/*   struct dpi_set_packet {                                         */
/*     char                   *object_p;       ptr to OIDstring      */
/*     char                   *group_p;        ptr to subtree        */
/*     char                   *instance_p;     ptr to rest of OID    */
/*     unsigned char           value_type;     SNMP_TYPE_xxxx        */
/*     unsigned short          value_len;      value length          */
/*     char                   *value_p;        ptr to value itself   */
/*     struct dpi_set_packet  *next_p;         ptr to next in chain  */
/*   };                                                              */
/*   typedef struct dpi_set_packet        snmp_dpi_set_packet;       */
/*   #define snmp_dpi_set_packet_NULL_p   ((snmp_dpi_set_packet *)0) */
/*                                                                   */
/* We can use the mkDPIset() function to prepare such a structure.   */
/* This function expects the following arguments:                    */
/*  - A ptr to an existing snmp_dpi_set_packet structure if the new  */
/*    varBind must be added to an existing chain of varBinds.        */
/*    If this is the first (or the only) varBind in the chain, then  */
/*    pass the snmp_dpi_set_packet_NULL_p ptr to indicate this.      */
/*  - a ptr to the subtree that we registered.                       */
/*  - a ptr to the rest of the OID, in other words the piece that    */
/*    follows the subtree.                                           */
/*  - the value type of the value to be bound to the variable name.  */
/*    This is must be one of the SNMP_TYPE_xxx values as defined in  */
/*    the snmp_dpi.h include file.                                   */
/*  - the length of the value (for integer type values this must be  */
/*    a length of 4. So we always work with 32-bit signed or         */
/*    unsigned integers (except of course for the Counter64 type,    */
/*    for those we must point to a snmp_dpi_u64 structure and pass   */
/*    the length of that structure).                                 */
/*  - a ptr to the value.                                            */
/* Memory for the varBind is dynamically allocated and the data      */
/* itself is copied. So upon return we can dispose of our own ptrs   */
/* and allocated memory as we please. If the call is successful,     */
/* then a ptr is returned:                                           */
/*  - to a new snmp_dpi_set_packet if it is the first/only varBind   */
/*  - to the existing snmp_dpi_set_packet that we passed on the call.*/
/*    In this case, the new packed has been chained to the end of    */
/*    the varBind list.                                              */
/* If the mkDPIset() call fails, a NULL ptr is returned.             */
/*                                                                   */
/* Once we have prepared the SET-varBind data, we can create a DPI   */
/* RESPONSE packet. To do so we can use the mkDPIresponse() function,*/
/* which expects these arguments:                                    */
/*  - a ptr to an snmp_dpi_hdr. We should use the hdr of the parsed  */
/*    incoming packet. It is used to copy the packet_id from the     */
/*    request into the response, such that the agent can correlate   */
/*    the response to a request.                                     */
/*  - a return code (snmp error code). If success, this should be    */
/*    SNMP_ERROR_noError (value zero). If failure, it must be one    */
/*    of the SNMP_ERROR_xxxx values as defined in the snmp_dpi.h     */
/*    include file.                                                  */
/*    Note that a request for a non-existing object or instance      */
/*    is not considered an error. Instead, we must pass the OID      */
/*    and value of the first OID that lexicographically follows      */
/*    the non-existing object and/or instance.                       */
/*    Also, reaching the end of our subtree (there is no NEXT OID)   */
/*    is not considered an error. In this situation we must return   */
/*    the original OID (as received in request) and a value_type     */
/*    of SNMP_TYPE_endOfMibView. This value_type has an implicit     */
/*    value of NULL, so we can pass a zero length and a NULL ptr     */
/*    for the value.                                                 */
/*  - The index of the first varBind in error (start counting at 1). */
/*    Pass zero if no error occured, else pass the proper index of   */
/*    the first varBind for which an error was detected.             */
/*  - a ptr to a (chain of) snmp_dpi_set_packet(s) (varBinds) to     */
/*    be returned as response to the GETNEXT request.                */
/*    If an error was detected, then an snmp_dpi_set_packet_NULL_p   */
/*    ptr may be passed.                                             */
/*                                                                   */
/* Here follows a code sample to return a response. We assume that   */
/* there are no errors in the request, but proper code should do     */
/* the checking for that. We do proper checking for lexicographic    */
/* next object, but we do no checking for ULONG_MAX, or making sure  */
/* that the instance ID is indeed valid (digits and dots).           */
/* If we get to the end of our dpiSimpleMIB then we must return an   */
/* endOfMibView, as defined by the SNMPv2 rules.                     */
/*********************************************************************/
static int do_next(snmp_dpi_hdr *hdr_p, snmp_dpi_next_packet *pack_p)
{
       unsigned char       *packet_p;
       int                  rc;
       unsigned long        subid;        /* subid is unsigned       */
       unsigned long        instance;     /* same with instance      */
       char                *cp;
       snmp_dpi_set_packet *varBind_p;

       varBind_p =                        /* init the varBind chain  */
          snmp_dpi_set_packet_NULL_p;     /* to a NULL pointer       */

       if (pack_p->instance_p) {          /* we have an instance ID  */
          cp = pack_p->instance_p;        /* pick up ptr             */
          subid = strtoul(cp, &cp, 10);   /* convert subid (object)  */
          if (*cp == '.') {               /* followed by a dot ?     */
             cp++;                        /* point after it if yes   */
             instance=strtoul(cp,&cp,10); /* convert real instance   */
                                          /* not that we need it, we */
             subid++;                     /* only have instance 0,   */
                                          /* so NEXT is next object  */
             instance = 0;                /* and always instance 0   */
          } else {                        /* no real instance passed */
             instance = 0;                /* so we can use 0         */
             if (subid == 0) subid++;     /* if object 0, start at 1 */
          } /* endif */
       } else {                           /* no instance ID passed   */
          subid = 1;                      /* so do first object      */
          instance = 0;                   /* instance 0 (all we have)*/
       } /* endif */

       /* we have set subid and instance such that we can basically  */
       /* process the request as a GET now. Actually, we don't even  */
       /* need instance, because all out object instances are zero.  */

       if (instance != 0) printf("Strange instance: %lu\n",instance);

       switch (subid) {
       case 1:
         varBind_p = mkDPIset(            /* Make DPI set packet     */
                     varBind_p,           /* ptr to varBind chain    */
                     pack_p->group_p,     /* ptr to subtree          */
                     DPI_SIMPLE_INTEGER,  /* ptr to rest of OID      */
                     SNMP_TYPE_Integer32, /* value type Integer 32   */
                     sizeof(value1),      /* length of value         */
                     &value1);            /* ptr to value            */
         break;
       case 2:
         varBind_p = mkDPIset(               /* Make DPI set packet  */
                     varBind_p,              /* ptr to varBind chain */
                     pack_p->group_p,        /* ptr to subtree       */
                     DPI_SIMPLE_STRING,      /* ptr to rest of OID   */
                     SNMP_TYPE_DisplayString,/* value type           */
                     value2_len,             /* length of value      */
                     value2_p);              /* ptr to value         */
         break;
       case 3:
         varBind_p = mkDPIset(               /* Make DPI set packet  */
                     varBind_p,              /* ptr to varBind chain */
                     pack_p->group_p,        /* ptr to subtree       */
                     DPI_SIMPLE_COUNTER32,   /* ptr to rest of OID   */
                     SNMP_TYPE_Counter32,    /* value type           */
                     sizeof(value3),         /* length of value      */
                     &value3);               /* ptr to value         */
         break;
#ifndef EXCLUDE_SNMP_V2_SUPPORT
       case 4:                               /*                *Apr23*/
         varBind_p = mkDPIset(               /* Make DPI set packet  */
                     varBind_p,              /* ptr to varBind chain */
                     pack_p->group_p,        /* ptr to subtree       */
                     DPI_SIMPLE_COUNTER64,   /* ptr to rest of OID   */
                     SNMP_TYPE_Counter64,    /* value type           */
                     sizeof(value4),         /* length of value      */
                     &value4);               /* ptr to value         */
         break;                              /*                *Apr23*/
#endif /* ndef EXCLUDE_SNMP_V2_SUPPORT */
       default:
         varBind_p = mkDPIset(               /* Make DPI set packet  */
                     varBind_p,              /* ptr to varBind chain */
                     pack_p->group_p,        /* ptr to subtree       */
                     pack_p->instance_p,     /* ptr to rest of OID   */
                     SNMP_TYPE_endOfMibView, /* value type           */
                     0L,                     /* length of value      */
                     (unsigned char *)0);    /* ptr to value         */
         break;
       } /* endswitch */

       if (!varBind_p) return(-1);        /* If it failed, return    */

       packet_p = mkDPIresponse(          /* Make DPIresponse packet */
                    hdr_p,                /* ptr parsed request      */
                    SNMP_ERROR_noError,   /* all is OK, no error     */
                    0L,                   /* index is zero, no error */
                    varBind_p);           /* varBind response data   */

       if (!packet_p) return(-1);         /* If it failed, return    */
 
       rc  = DPIsend_packet_to_agent(     /* send RESPONSE packet    */
                handle,                   /* on this connection      */
                packet_p,                 /* this is the packet      */
                DPI_PACKET_LEN(packet_p));/* and this is its length  */
 
       return(rc);                        /* return retcode          */
} /* end of do_next() */

/*********************************************************************/
/* Processing a SET/COMMIT/UNDO request                              */
/*********************************************************************/
/*                                                                   */
/* These 3 requests can come in one of these sequences:              */
/*    - SET, COMMIT                                                  */
/*    - SET, UNDO                                                    */
/*    - SET, COMMIT, UNDO                                            */
/* Normal sequence is SET and then COMMIT. When we receive a SET     */
/* request, we must make preparations to accept the new value        */
/* like check that it is for an existing object and instance, check  */
/* the value type and contents to be valid, allocate memory etc),    */
/* but we must not yet effectuate the change.                        */
/* If all goes well, the next request we receive will be a COMMIT    */
/* request. It is then that we must effectuate the change, but we    */
/* must then also keep enough information such that we can UNDO      */
/* the change later if we get a subsequent UNDO request. The latter  */
/* may happen if the agent discovers any errors with other           */
/* subagents while processing requests that belong to the same       */
/* original SNMP SET packet (all the varBinds in the same SNMP       */
/* request PDU must be processed "as if atomic").                    */
/*                                                                   */
/* When the DPI packet is parsed, the snmp_dpi_hdr structure shows   */
/* in the packet_type that this is an SNMP_DPI_SET, SNMP_DPI_COMMIT  */
/* SNMP_DPI_UNDO packet. In that case, the data_u field contains a   */
/* ptr to a SET-varBind, represented in an snmp_dpi_get_packet       */
/* structure (COMMIT and UNDO have same varBind data as SET upon     */
/* which they follow):                                               */
/*                                                                   */
/*   struct dpi_set_packet {                                         */
/*     char                   *object_p;       ptr to OIDstring      */
/*     char                   *group_p;        ptr to subtree        */
/*     char                   *instance_p;     ptr to rest of OID    */
/*     unsigned char           value_type;     SNMP_TYPE_xxxx        */
/*     unsigned short          value_len;      value length          */
/*     char                   *value_p;        ptr to value itself   */
/*     struct dpi_set_packet  *next_p;         ptr to next in chain  */
/*   };                                                              */
/*   typedef struct dpi_set_packet        snmp_dpi_set_packet;       */
/*   #define snmp_dpi_set_packet_NULL_p   ((snmp_dpi_set_packet *)0) */
/*                                                                   */
/* So, assuming we have registered example subtree dpiSimpleMIB      */
/* and a GET request comes in for one variable dpiSimpleString.0     */
/* (so that is object 1 instance 0 in our subtree), and also         */
/* assuming that the agent knows about our compiled dpiSimpleMIB     */
/* so that it knows this is a DisplayString as opposed to just an    */
/* arbitrary OCTET_STRING, then the ptrs in the snmp_dpi_set_packet  */
/* structure would have ptrs and values like:                        */
/*                                                                   */
/*   object_p    ->  "1.3.6.1.4.1.2.2.1.5.2.0"                       */
/*   group_p     ->  "1.3.6.1.4.1.2.2.1.5."                          */
/*   instance_p  ->  "2.0"                                           */
/*   value_type  ->  SNMP_TYPE_DisplayString                         */
/*   value_len   ->  8                                               */
/*   value_p     ->  ptr to the value to be set.                     */
/*   next_p      ->  snmp_dpi_get_packet_NULL_p                      */
/*                                                                   */
/* If there are multiple varBinds in a SET request, then each one    */
/* is represented in a snmp_dpi_set_packet structure and all the     */
/* snmp_dpi_set_packet structures are chained via the next ptr.      */
/* As long as the next ptr is not the snmp_dpi_set_packet_NULL_p     */
/* pointer then there are more varBinds in the list.                 */
/*                                                                   */
/* Now we can analyze the varBind stucture for whatever checking we  */
/* want to do. Once we are ready to make a response that contains    */
/* the value of the variable, we may prepare a new SET-varBind.      */
/* However, by definition, the response to a successful SET is       */
/* exactly the same as the SET request. So there is no need to       */
/* return any varBinds. A simple response with SNMP_ERROR_noError    */
/* and an index of zero will do. In case that there is an error,     */
/* then a simple response with the SNMP_ERROR_xxxx error code and    */
/* an index pointing to the varBind in error (counting starts at 1)  */
/* will do.                                                          */
/*                                                                   */
/* Here follows a code sample to return a response. We assume that   */
/* there are no errors in the request, but proper code should do     */
/* the checking for that. We also do not check if the varBind in     */
/* the COMMIT and/or UNDO is the same as that in the SET request.    */
/* A proper agent would make sure that that is the case, but a       */
/* proper subagent may want to verify that for itself.               */
/* We only do one simple check that this is dpiSimpleString.0 and if */
/* it is not we return a noCreation, which may not be correct.       */
/* The mainline does not even return a response.                     */
/*********************************************************************/
static int do_set(snmp_dpi_hdr *hdr_p, snmp_dpi_set_packet *pack_p)
{
       unsigned char       *packet_p;
       int                  rc;
       int                  index       = 0;
       int                  error       = SNMP_ERROR_noError;
       snmp_dpi_set_packet *varBind_p;

       varBind_p =                        /* init the varBind chain  */
          snmp_dpi_set_packet_NULL_p;     /* to a NULL pointer       */

       if (!pack_p->instance_p ||
           (strcmp(pack_p->instance_p,"2.0") != 0))
       {

          if (pack_p->instance_p &&
             (strncmp(pack_p->instance_p,"1.",2) == 0))
          {
             error = SNMP_ERROR_notWritable;
          } else if (pack_p->instance_p &&
             (strncmp(pack_p->instance_p,"2.",2) == 0))
          {
             error = SNMP_ERROR_noCreation;
          } else if (pack_p->instance_p &&
             (strncmp(pack_p->instance_p,"3.",2) == 0))
          {
             error = SNMP_ERROR_notWritable;
          } else {
             error = SNMP_ERROR_noCreation;
          } /* endif */

          packet_p = mkDPIresponse(       /* Make DPIresponse packet */
                    hdr_p,                /* ptr parsed request      */
                    error,                /* all is OK, no error     */
                    1,                    /* index is 1, 1st varBind */
                    varBind_p);           /* varBind response data   */

          if (!packet_p) return(-1);      /* If it failed, return    */
 
          rc  = DPIsend_packet_to_agent(  /* send RESPONSE packet    */
                handle,                   /* on this connection      */
                packet_p,                 /* this is the packet      */
                DPI_PACKET_LEN(packet_p));/* and this is its length  */
 
          return(rc);                     /* return retcode          */
       }

       switch (hdr_p->packet_type) {
       case SNMP_DPI_SET:
         if ((pack_p->value_type != SNMP_TYPE_DisplayString) &&
             (pack_p->value_type != SNMP_TYPE_OCTET_STRING))
         {  /* check octet string in case agent has no compiled MIB  */
            error = SNMP_ERROR_wrongType;
            break;                        /* from switch             */
         } /* endif */
         if (new_val_p) free(new_val_p);  /* free these memory areas */
         if (old_val_p) free(old_val_p);  /* if we allocated any     */
         new_val_p   = (char *)0;
         old_val_p   = (char *)0;
         new_val_len = 0;
         old_val_len = 0;

         new_val_p =                      /* allocate memory for     */
             malloc(pack_p->value_len);   /* new value to set        */
         if (new_val_p) {                 /* If success, then also   */
            memcpy(new_val_p,             /* copy new value to our   */
                   pack_p->value_p,       /* own and newly allocated */
                   pack_p->value_len);    /* memory area.            */
            new_val_len = pack_p->value_len;
         } else {                         /* Else failed to malloc,  */
            error = SNMP_ERROR_genErr;    /* so that is a genErr     */
            index = 1;                    /* at first varBind        */
         } /* endif */
         break;
       case SNMP_DPI_COMMIT:
         old_val_p = cur_val_p;           /* save old value for undo */
         cur_val_p = new_val_p;           /* make new value current  */
         new_val_p = (char *)0;           /* keep only 1 ptr around  */
         old_val_len = cur_val_len;       /* and keep lengths correct*/
         cur_val_len = new_val_len;
         new_val_len = 0;
         /* may need to convert from ASCII to native if OCTET_STRING */
         break;
       case SNMP_DPI_UNDO:
         if (new_val_p) {                 /* free allocated memory   */
            free(new_val_p);
            new_val_p   = (char *)0;
            new_val_len = 0;
         } /* endif */
         if (old_val_p) {
            if (cur_val_p) free(cur_val_p);
            cur_val_p   = old_val_p;      /* reset to old value      */
            cur_val_len = old_val_len;
            old_val_p   = (char *)0;
            old_val_len = 0;
         } /* endif */
         break;
       } /* endswitch */

       packet_p = mkDPIresponse(          /* Make DPIresponse packet */
                    hdr_p,                /* ptr parsed request      */
                    error,                /* all is OK, no error     */
                    index,                /* index is zero, no error */
                    varBind_p);           /* varBind response data   */

       if (!packet_p) return(-1);         /* If it failed, return    */
 
       rc  = DPIsend_packet_to_agent(     /* send RESPONSE packet    */
                handle,                   /* on this connection      */
                packet_p,                 /* this is the packet      */
                DPI_PACKET_LEN(packet_p));/* and this is its length  */
 
       return(rc);                        /* return retcode          */
} /* end of do_set() */
 
/*********************************************************************/
/* Function to handle a DPI UNREGISTER request                       */
/*********************************************************************/
/* An agent can send an UNREGISTER packet if some other subagent does*/
/* a register for the same subtree at a higher priority. In this case*/
/* we can decide to keep the connection opn, we may regain control   */
/* over the subtree if that higher priority registration goes away.  */
/* An agent can also send an UNREGISTER if for instance an SNMP      */
/* manager tells it to "invalidate" the subagent connection or the   */
/* registered subtree. In this case we decide to give up.            */
/*                                                                   */
/* Here is a very simple sample piece of code to handle such a packet*/
/*********************************************************************/
static int do_unreg(snmp_dpi_hdr *hdr_p, snmp_dpi_ureg_packet *pack_p)
{
       printf("DPI UNREGISTER received from agent, reason=%d\n",
               pack_p->reason_code);
       printf("    subtree=%s\n",pack_p->group_p);
       if (pack_p->reason_code == 
           SNMP_UNREGISTER_higherPriorityRegistered) 
       {
          return(0); /* keep waiting, we may regain subtree later */
       } /* endif */

       DPIdisconnect_from_agent(handle);
       return(-1); /* causes exit in main loop */
} /* end of do_unreg() */

/*********************************************************************/
/* Function to handle a DPI CLOSE request                            */
/*********************************************************************/
/* An agent can send a CLOSE packet if it encounters an error or for */
/* some other reason. It can also do so if an SNMP MANAGER tells it  */
/* to "invalidate" the subagent connection.                          */
/*                                                                   */
/* Here is a very simple sample piece of code to handle such a packet*/
/*********************************************************************/
static int do_close(snmp_dpi_hdr *hdr_p, snmp_dpi_close_packet *pack_p)
{
       printf("DPI CLOSE received from agent, reason=%d\n",
              pack_p->reason_code);

       DPIdisconnect_from_agent(handle);
       return(-1); /* causes exit in main loop */
} /* end of do_close() */

/*
\end{verbatim}
*/
