view usr/src/cmd/agents/snmp/agent/agent.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the 
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"@(#)agent.c	1.25	05/06/12 SMI"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


#include "impl.h"
#include "error.h"
#include "trace.h"
#include "snmp.h"
#include "pdu.h"
#include "request.h"
#include "trap.h"

#include "node.h"
#include "access.h"

static int ssa_mem_free = 1; /* on */

#if defined(_LP64)
#define	COLUMN_OFFSET(x)	(x) * 2
#else
#define	COLUMN_OFFSET(x)	(x)
#endif

#define OCTET_STRING 2

/***** LOCAL VARIABLES *****/

/*
 *	The idea of these cache variables is to avoid to call
 *	the same *(entry->get) function for variables contained
 *	in a single SNMP PDU.
 *
 *	There is still a problem in this mechanism.
 *	When you query a whole table row by row and when
 *	you reached the end of the table, the *(entry->get)
 *	will be called several times:
 *	- for the first column, *(entry->get) will be called
 *	  once to find that the end of the table is reached
 *	  (==> the knowledge that we reached the last row is cached)
 *	  and then *(entry->get) will be called on the first row of
 *	  the same table (==> the first row is cached).
 *	- Same behaviour for all the remaining columns
 *
 *	Possible solutions:
 *	- two caches but this implies the copy of the cached
 *	  structures + all theis pointers
 *	- only one cache + caching the knowledge that we reached the last
 *	  row ???
 *
 */

static Entry *cache_input_entry = NULL;
static Subid cache_input_index[MAX_OID_LEN];
static void *cache_output_pointer = NULL;
static int cache_output_snmp_error = -1;
static Subid cache_output_index[MAX_OID_LEN];


/***** LOCAL FUNCTIONS *****/

static int agent_get_next(SNMP_pdu *pdu, char *error_label);
static int agent_get_next_loop(SNMP_variable *variable, Node *node, Oid *suffix);
static int agent_get(SNMP_pdu *pdu, char *error_label);
static int agent_set(int pass, SNMP_pdu *pdu, char *error_label);


/****************************************************************/

/* returns:						*/
/*	0 in case of success (the pdu should be sent	*/
/*	  back to its originator even if an SNMP error	*/
/*	  was detected)					*/
/*	-1 in case of failure (no pdu should be sent	*/
/*	  back)						*/

int agent_process(Address *address, SNMP_pdu *pdu)
{
	int snmpEnableAuthTraps = FALSE;
	Manager *mngr;


	if(pdu == NULL)
	{
		error("BUG: agent_process(): pdu is NULL");
		return -1;
	}


	/* check host */
	if(is_valid_manager(address,&mngr) == NULL)
	{
		error("agent_process(): unauthorized manager (%s)",
			ip_address_string(&(address->sin_addr)));

		snmpEnableAuthTraps = request_snmpEnableAuthTraps(error_label);
		switch(snmpEnableAuthTraps)
		{
			case TRUE:
				if(trap_send_to_all_destinators(NULL,
					SNMP_TRAP_AUTHFAIL, 0,
					NULL, error_label))
				{
					error("trap_send_to_all_destinators() failed: %s\n",
						error_label);
				}
				break;

			case FALSE:
			default:
				break;
		}

		return -1;
	}

	/* if mngr == NULL -> allow requests from any hosts */

	/* check pdu type */
	if(pdu->type != GETNEXT_REQ_MSG
		&& (pdu->type != GET_REQ_MSG)
		&& (pdu->type != SET_REQ_MSG) )
	{
		error("agent_process(): bad PDU type (0x%x)", pdu->type);
		return -1;
	}


	/* check host */
	if(!is_valid_community(pdu->community, pdu->type,mngr))
	{
		/*
		 * Earlier, the community name is displayed here
		 * in this error message. But since these error
		 * messages are readable by all users, it is not advisible
		 * to display community names in the error messages.
		 */
		error("agent_process() : bad community from %s",
			ip_address_string(&(address->sin_addr)));

		snmpEnableAuthTraps = request_snmpEnableAuthTraps(error_label);
		switch(snmpEnableAuthTraps)
		{
			case TRUE:
				if(trap_send_to_all_destinators(NULL,
					SNMP_TRAP_AUTHFAIL, 0,
					NULL, error_label))
				{
					error("trap_send_to_all_destinators() failed: %s\n",
						error_label);
				}
				break;

			case FALSE:
			default:
				break;
		}

		return -1;
	}


	if(cache_input_entry != NULL && cache_output_pointer != NULL)
		if(ssa_mem_free != 0 && cache_input_entry->dealloc != NULL){
				(*(cache_input_entry->dealloc))(cache_output_pointer);
				cache_output_pointer = NULL;
		}

	cache_input_entry = NULL;

	switch(pdu->type)
	{
		case GETNEXT_REQ_MSG:
			if(agent_get_next(pdu, error_label))
			{
				error("agent_get_next() failed: %s", error_label);
				return -1;
			}
			return 0;

		case GET_REQ_MSG:
			if(agent_get(pdu, error_label))
			{
				error("agent_get() failed: %s", error_label);
				return -1;
			}
			return 0;

		case SET_REQ_MSG:
			switch(agent_set(FIRST_PASS, pdu, error_label))
			{
				case 0:
					switch(agent_set(SECOND_PASS, pdu, error_label))
					{
						case 0:
						case 1:
							return 0;

						case -1:
							error("agent_set(SECOND_PASS) failed: %s",
								error_label);
							return -1;
					}

					/* never reached */
					break;

				case 1:
					return 0;

				case -1:
					error("agent_set(FIRST_PASS) failed: %s",
						error_label);
					return -1;
			}
	}

	/* never reached */
	return -1;
}


/****************************************************************/

/* returns:						*/
/*	0 in case of success (the pdu should be sent	*/
/*	  back to its originator even if an SNMP error	*/
/*	  was detected)					*/
/*	-1 in case of failure (no pdu should be sent	*/
/*	  back)						*/

static int agent_get_next(SNMP_pdu *pdu, char *error_label)
{
	SNMP_variable *variable;
	Node *node;
	Oid suffix;
	int index = 1;
	int snmp_error;


	error_label[0] = '\0';

	pdu->type = GET_RSP_MSG;

	for(variable = pdu->first_variable; variable; variable = variable->next_variable)
	{
		node = node_find(NEXT_ENTRY, &(variable->name), &suffix);
		if(node == NULL)
		{
			pdu->error_status = SNMP_ERR_NOSUCHNAME;
			pdu->error_index = index;
			return 0;
		}
		/* we should not forget to free suffix.subids */

		if(trace_level > 0)
		{
			trace("!! getnext(): processing the variable %s\n\n",
				node->label);
		}

		if(variable->type != NULLOBJ)
		{
			error("ASN.1 type (0x%x) is not NULL for node %s",
				variable->type, node->label);
			variable->type = NULLOBJ;
		}

		if(variable->val.string)
		{
			error("val is not NULL for node %s",
				node->label);
			free(variable->val.string);
			variable->val.string = NULL;
		}

		if(variable->val_len)
		{
			error("val_len is not 0 for node %s",
				node->label);
			variable->val_len = 0;
		}

		snmp_error = agent_get_next_loop(variable, node, &suffix);

		if(snmp_error != SNMP_ERR_NOERROR)
		{
			pdu->error_status = snmp_error;
			pdu->error_index = index;
			return 0;
		}

		index++;
	}

	return 0;
}


/* This function will free suffix->subids	*/
/* It returns a positive snmp_error code.	*/
static int
agent_get_next_loop(SNMP_variable *variable, Node *node, Oid *suffix)
{
	Object *object;
	Column *column;
	Entry *entry;
	Integer integer = 0;
	Integer *integer_ptr;
	String string = { NULL, 0 };
	String *string_ptr;
	Oid oid = { NULL, 0 };
	Oid *oid_ptr;
	char *pointer;
	int snmp_error;
        Subid index[MAX_OID_LEN];
        Index *pIndex;
        int index_len;

	/* create index struct */
	IndexType index_obj;
	int index_buffer[256];

        int was_cached ;
        int i;
	int get_entry;

	bzero(index, sizeof(Subid) * MAX_OID_LEN);
	bzero(&index_obj, sizeof(index_obj));
	bzero(index_buffer, sizeof(index_buffer));
        index_obj.value = index_buffer;

	if(node == NULL)
	{
		if(trace_level > 0)
		{
			trace("!! End of MIB\n\n");
		}

		SSAOidZero(suffix);
		return SNMP_ERR_NOSUCHNAME;
	}

	if(trace_level > 0)
		trace("!! Trying %s with suffix %s\n\n",
			node->label, SSAOidString(suffix));

	switch(node->type)
	{
		case OBJECT:
			object = node->data.object;

			switch(suffix->len)
			{
				case 0:
					if( !(object->access & READ_FLAG) )
						return SNMP_ERR_NOSUCHNAME;
						
					switch(object->asn1_type)
					{
						case INTEGER:
						case COUNTER:
						case GAUGE:
						case TIMETICKS:
							snmp_error = (*(object->get))(&integer);
							break;

						case OBJID:
							snmp_error = (*(object->get))(&oid);
							if(snmp_error != SNMP_ERR_NOERROR  &&
							   ssa_mem_free != 0){
								if(object->dealloc != NULL)
								  (*(object->dealloc))(&oid);
							}
						break;

						case STRING:
						case IPADDRESS:
						case OPAQUE:
							snmp_error = (*(object->get))(&string);
							if(snmp_error != SNMP_ERR_NOERROR  &&
							   ssa_mem_free != 0){
								if(object->dealloc != NULL)
								  (*(object->dealloc))(&string);
							}
							break;
					}

					if(snmp_error != SNMP_ERR_NOERROR)
					{
						if(snmp_error < 0)
						{
							error("the get() method of %s returned %d",
								node->label,
								snmp_error);
							snmp_error = SNMP_ERR_GENERR;
						}
						return snmp_error;
					}

					/* variable->name */
					SSAOidZero(&(variable->name));
					variable->name.subids = (Subid *) malloc((object->name.len + 1) *
							(int32_t)sizeof(Subid));
					(void)memcpy(variable->name.subids, object->name.subids,
						object->name.len * (int32_t)sizeof(Subid));
					variable->name.subids[object->name.len] = 0;
					variable->name.len = object->name.len + 1;

					/* variable->type */
					variable->type = object->asn1_type;

					/* variable->val, variable->val_len */
					switch(object->asn1_type)
					{
						case INTEGER:
						case COUNTER:
						case GAUGE:
						case TIMETICKS:
							variable->val.integer = (Integer *) malloc(sizeof(Integer));
							*(variable->val.integer) = integer;
							variable->val_len = sizeof(Integer);
							break;

						case OBJID:
							variable->val.objid = (Subid *) malloc(oid.len *
								(int32_t)sizeof(Subid));
							(void)memcpy(variable->val.objid, oid.subids, oid.len *
								(int32_t)sizeof(Subid));
							variable->val_len = oid.len *
								(int32_t)sizeof(Subid);
							if(ssa_mem_free !=0 && object->dealloc != NULL)
								  (*(object->dealloc))(&oid);
							break;

						case STRING:
						case IPADDRESS:
						case OPAQUE:
							variable->val.string = (u_char *) malloc(string.len);
							(void)memcpy(variable->val.string, string.chars, string.len);
							variable->val_len = string.len;
							if(ssa_mem_free != 0 && object->dealloc != NULL)
								  (*(object->dealloc))(&string);
							break;

					}

					return SNMP_ERR_NOERROR;


				case 1:
					if(suffix->subids[0] != 0)
					{
						SSAOidZero(suffix);
						return SNMP_ERR_NOSUCHNAME;
					}
					SSAOidZero(suffix);

					return agent_get_next_loop(variable, node->next, suffix);

				default:
					SSAOidZero(suffix);
					return SNMP_ERR_NOSUCHNAME;
			}


		case COLUMN:
			column = node->data.column;
			entry = column->entry;
                        pIndex = entry->first_index;

			if( !(column->access & READ_FLAG) )
			{
				SSAOidZero(suffix);
				return SNMP_ERR_NOSUCHNAME;
			}

			if (entry->n_indexs < 0 || entry->n_indexs > MAX_OID_LEN) { 
					SSAOidZero(suffix);
					return SNMP_ERR_NOSUCHNAME;
			}

                       index_len = 0;
                       for (pIndex=entry->first_index; pIndex; pIndex=pIndex->next_index)
                       {
			   if (pIndex->index_type == OCTET_STRING)
                               index_len = index_len + (pIndex->index_len)+1; /* add extra suffix for str len */
                           else
                               index_len = index_len +1;    /* one subid per index */
                       }     
                       index_obj.len = index_len;

                       for (i=0; i < suffix->len; i++)
                       {
                                index[i] = suffix->subids[i];
                                index_obj.value[i] = suffix->subids[i];
                       }

                       for (i = suffix->len; i < index_len; i++)   /* Zero out remainder of suffixes */
                       {
                                index[i] = 0;
                                index_obj.value[i] = 0;
                       }

                       for (i=0; i < index_len; i++)
                       {
                            if ((cache_input_entry == entry) && (cache_input_index[i] == index[i]))
                                 was_cached = 1;
                            else
                            {
                                 was_cached = 0;
                                 break;
                            }

                       }

                       if (was_cached)
                       {
                            pointer = cache_output_pointer;
                            snmp_error = cache_output_snmp_error;
                            for (i=0;i < index_len; i++)
                                index[i]= cache_output_index[i];
                       }
                       else
                       {
                            if (cache_input_entry != NULL && cache_output_pointer != NULL)
                                if (ssa_mem_free!=0 && cache_input_entry->dealloc != NULL)
                                {
                                    (*(cache_input_entry->dealloc))(cache_output_pointer);
                                    cache_output_pointer = NULL;
                                }
                            cache_input_entry= entry;
                            for (i=0; i < index_len; i++)
                                 cache_input_index[i] = index[i];
			
			    if (suffix->len == 0)
				get_entry = FIRST_ENTRY;
			    else
				get_entry = NEXT_ENTRY;

                            snmp_error = (*(entry->get)) (get_entry, &pointer,&index_obj);

                            for (i=0; i< index_len; i++)
                                 index[i] = index_obj.value[i];
                            cache_output_pointer = pointer;
                            cache_output_snmp_error = snmp_error;
                            for (i=0; i < index_len; i++)
                                 cache_output_index[i] = index[i];
                       }

                       if (suffix->len !=0)
                           SSAOidZero(suffix);

                       if(pointer == NULL)
                       {
                          if(snmp_error == END_OF_TABLE)
                          {
                               if(trace_level > 0)
                               {
                                   trace("!! End of table %s\n\n",
                                          node->parent->parent->label);
                               }
                               return agent_get_next_loop(variable, node->next, suffix);
                          }

                                if(snmp_error < 0)
                                {
                                      error("the get() method of %s returned %d",
                                             node->parent->label,
                                             snmp_error);
                                      snmp_error = SNMP_ERR_GENERR;
                                }
                                return snmp_error;
                       }

			/* variable->name */
		       SSAOidZero(&(variable->name));
                       variable->name.subids = (Subid *) malloc((column->name.len + index_len) * sizeof(Subid));
                       memcpy(variable->name.subids, column->name.subids, column->name.len * sizeof(Subid));
                       for (i=0; i < index_len; i++)
                            variable->name.subids[column->name.len + i] = index[i];
                       variable->name.len = column->name.len + index_len;

			/* variable->type */
			variable->type = column->asn1_type;

			/* variable->val, variable->val_len */
			switch(column->asn1_type)
			{
				case INTEGER:
				case COUNTER:
				case GAUGE:
				case TIMETICKS:
					integer_ptr = (Integer *) (pointer +
						COLUMN_OFFSET(column->offset));
					variable->val.integer = (Integer *) malloc(sizeof(Integer));
					*(variable->val.integer) = *integer_ptr;
					variable->val_len = sizeof(Integer);
					break;

				case OBJID:
					oid_ptr = (Oid *) (pointer +
						COLUMN_OFFSET(column->offset));
					/* fix the null subid */
					if(oid_ptr->subids == NULL){
					  variable->val.objid = NULL;
					}else{
					variable->val.objid = (Subid *) malloc(oid_ptr->len *
						(int32_t)sizeof(Subid));
					(void)memcpy(variable->val.objid, oid_ptr->subids, oid_ptr->len * 
						(int32_t)sizeof(Subid));
					}
					variable->val_len = oid_ptr->len * 
						(int32_t)sizeof(Subid);
					break;

				case STRING:
				case IPADDRESS:
				case OPAQUE:
					string_ptr = (String *) (pointer +
						COLUMN_OFFSET(column->offset));
					if(string_ptr->chars == NULL){
					  variable->val.string =(u_char*)NULL;
					}else{
					variable->val.string = (u_char *) malloc(string_ptr->len);
					(void)memcpy(variable->val.string, string_ptr->chars, string_ptr->len);
					}
					variable->val_len = string_ptr->len;
					break;
			}


			return SNMP_ERR_NOERROR;


		case NODE:
			return agent_get_next_loop(variable, node->next, suffix);
	}

	/* never reached */
	return -1;
}

	
/****************************************************************/

/* returns:						*/
/*	0 in case of success (the pdu should be sent	*/
/*	  back to its originator even if an SNMP error	*/
/*	  was detected)					*/
/*	-1 in case of failure (no pdu should be sent	*/
/*	  back)						*/

static int agent_get(SNMP_pdu *pdu, char *error_label)
{
	SNMP_variable *variable;
	Node *node;
	Object *object;
	Column *column;
	Entry *entry=NULL;
	Oid suffix;
	int index_err = 1;
	Integer integer;
	Oid oid;
	String string;
	Integer *integer_ptr;
	Oid *oid_ptr;
	String *string_ptr;
	int snmp_error;
	char *pointer=NULL;
        Subid index[MAX_OID_LEN];

	/* create index struct */
	IndexType index_obj;
	int index_buffer[256];

        int was_cached;
        int i;
        Index *pIndex;
        int index_len;

	index_obj.len = 0;
	index_obj.type = 0;
	index_obj.value = index_buffer; 


	error_label[0] = '\0';

	pdu->type = GET_RSP_MSG;

	for(variable = pdu->first_variable; variable; variable = variable->next_variable)
	{
		node = node_find(EXACT_ENTRY, &(variable->name), &suffix);
		if(node == NULL)
		{
			pdu->error_status = SNMP_ERR_NOSUCHNAME;
			pdu->error_index = index_err;
			return 0;
		}
		/* we should not forget to free suffix.subids */

		if(trace_level > 0)
		{
			trace("!! get(): processing the variable %s\n\n",
				node->label);
		}

		if(variable->type != NULLOBJ)
		{
			error("agent_get(): ASN.1 type (0x%x) is not NULL for node %s",
					variable->type,
					node->label);
			variable->type = NULLOBJ;
		}

		if(variable->val.string)
		{
			error("agent_get(): val is not NULL for node %s", node->label);
			free(variable->val.string);
			variable->val.string = NULL;
		}

		if(variable->val_len)
		{
			error("agent_get(): val_len is not 0 for node %s", node->label);
			variable->val_len = 0;
		}

		switch(node->type)
		{
			case OBJECT:
				object = node->data.object;

				if( (suffix.len != 1)
					|| (suffix.subids[0] != 0) )
				{
					pdu->error_status = SNMP_ERR_NOSUCHNAME;
					pdu->error_index = index_err;
					SSAOidZero(&suffix);
					return 0;
				}

				if( !(object->access & READ_FLAG) )
				{
					pdu->error_status = SNMP_ERR_NOSUCHNAME;
					pdu->error_index = index_err;
					SSAOidZero(&suffix);
					return 0;
				}

				switch(object->asn1_type)
				{
					case INTEGER:
					case COUNTER:
					case GAUGE:
					case TIMETICKS:
						snmp_error = (*(object->get))(&integer);
						break;

					case OBJID:
						snmp_error = (*(object->get))(&oid);
                                                if(snmp_error != SNMP_ERR_NOERROR  &&
                                                   ssa_mem_free != 0){
                                                       if(object->dealloc != NULL)
                                                         (*(object->dealloc))(&oid);
						}
						break;

					case STRING:
					case IPADDRESS:
					case OPAQUE:
						snmp_error = (*(object->get))(&string);
                                                if(snmp_error != SNMP_ERR_NOERROR  &&
                                                   ssa_mem_free != 0){
                                                       if(object->dealloc != NULL)
                                                         (*(object->dealloc))(&string);
						}
						break;
				}

				if(snmp_error != SNMP_ERR_NOERROR)
				{
					if(snmp_error < 0)
					{
						error("the get() method of %s returned %d",
							node->label,
							snmp_error);
						snmp_error = SNMP_ERR_GENERR;
					}
					pdu->error_status = snmp_error;
					pdu->error_index = index_err;
					SSAOidZero(&suffix);
					return 0;
				}

				/* variable->name */

				/* variable->type */
				variable->type = object->asn1_type;

				/* variable->val, variable->val_len */
				switch(object->asn1_type)
				{
					case INTEGER:
					case COUNTER:
					case GAUGE:
					case TIMETICKS:
						variable->val.integer = (Integer *) malloc(sizeof(Integer));
						*(variable->val.integer) = integer;
						variable->val_len = sizeof(Integer);
						break;

					case OBJID:
						variable->val.objid = (Subid *) malloc(oid.len * 
							(int32_t)sizeof(Subid));
						(void)memcpy(variable->val.objid, oid.subids, oid.len *
							(int32_t)sizeof(Subid));
						variable->val_len = oid.len * 
							(int32_t)sizeof(Subid);
                                                  if (ssa_mem_free != 0){
                                                       if(object->dealloc != NULL)
                                                         (*(object->dealloc))(&oid);
						}
						break;

					case STRING:
					case IPADDRESS:
					case OPAQUE:
						variable->val.string = (u_char *) malloc(string.len);
						(void)memcpy(variable->val.string, string.chars, string.len);
						variable->val_len = string.len;
                                                /*if(snmp_error != SNMP_ERR_NOERROR  &&*/
                                                  if (ssa_mem_free != 0){
                                                       if(object->dealloc != NULL)
                                                         (*(object->dealloc))(&string);
						}
						break;
				}


				break;


			case COLUMN:
				column = node->data.column;
				entry = column->entry;
                                pIndex=entry->first_index;

				if( !(column->access & READ_FLAG) )
				{
					pdu->error_status = SNMP_ERR_NOSUCHNAME;
					pdu->error_index = index_err;
					SSAOidZero(&suffix);
					return 0;
				}
                               
                                if (suffix.subids == NULL) {
					pdu->error_status = SNMP_ERR_NOSUCHNAME;
                                        pdu->error_index = index_err;
                                        SSAOidZero(&suffix);
                                        return 0;
                                }

			        index_len = 0;
                                for (pIndex=entry->first_index; pIndex; pIndex=pIndex->next_index)
                       		{
                           	    if (pIndex->index_type == OCTET_STRING)
                               		index_len = index_len + (pIndex->index_len)+1; /* add extra suffix for str len */
                                    else
                                       index_len = index_len +1;    /* one subid per index */
                                }

 
                  	        for (i=0; i < index_len; i++)
                                {
                                        index[i] = suffix.subids[i];
                                        index_obj.value[i] = suffix.subids[i];
                                }
                                index_obj.len = index_len;
                                for (i=0; i < index_len; i++)
                                {
                                     if( (cache_input_entry == entry) && (cache_input_index[i] == index[i]) )
                                         was_cached = 1;
                                     else
                                     {
                                         was_cached = 0;
                                         break;
                                     }
                                }
                                if (was_cached)
                                {
                                     pointer = cache_output_pointer;
                                     snmp_error = cache_output_snmp_error;
                                }
                                else
                                {
                                     if(cache_input_entry != NULL && cache_output_pointer != NULL)
                                        if(ssa_mem_free != 0 && cache_input_entry->dealloc != NULL)
                                        {
                                              (*(cache_input_entry->dealloc))(cache_output_pointer);
                                              cache_output_pointer = NULL;
                                        }
                                              cache_input_entry = entry;
                                              for (i=0; i < index_len; i++)
                                                   cache_input_index[i] = index[i];
                                              snmp_error = (*(entry->get))(EXACT_ENTRY,&pointer,&index_obj);
                                              cache_output_pointer = pointer;
                                              cache_output_snmp_error = snmp_error;
                                }


				if(pointer == NULL)
				{
					if(snmp_error < 0)
					{
						error("the get() method of %s returned %d",
							node->parent->label,
							snmp_error);
						snmp_error = SNMP_ERR_GENERR;
					}
					
					pdu->error_status = snmp_error;
					pdu->error_index = index_err;
					SSAOidZero(&suffix);
					return 0;
				}

				/* variable->type */
				variable->type = column->asn1_type;

				/* variable->val, variable->val_len */
				switch(column->asn1_type)
				{
					case INTEGER:
					case COUNTER:
					case GAUGE:
					case TIMETICKS:
						integer_ptr = (Integer *) (pointer +
							COLUMN_OFFSET(column->offset));
						variable->val.integer = (Integer *) malloc(sizeof(Integer));
						*(variable->val.integer) = *integer_ptr;
						variable->val_len = sizeof(Integer);
						break;

					case OBJID:
						oid_ptr = (Oid *) (pointer +
							COLUMN_OFFSET(column->offset));
						variable->val.objid = (Subid *) malloc(oid_ptr->len *
							(int32_t)sizeof(Subid));
						(void)memcpy(variable->val.objid, oid_ptr->subids, oid_ptr->len *
							(int32_t)sizeof(Subid));
						variable->val_len = oid_ptr->len * 
							(int32_t)sizeof(Subid);
						break;

					case STRING:
					case IPADDRESS:
					case OPAQUE:
						string_ptr = (String *) (pointer +
							COLUMN_OFFSET(column->offset));
						variable->val.string = (u_char *) malloc(string_ptr->len);
						(void)memcpy(variable->val.string, string_ptr->chars, string_ptr->len);
						variable->val_len = string_ptr->len;
						break;
				}

				break;


			case NODE:
				pdu->error_status = SNMP_ERR_NOSUCHNAME;
				pdu->error_index = index_err;
				SSAOidZero(&suffix);
				return 0;
		}

		SSAOidZero(&suffix);

		index_err++;
	}
	/* Moved this down from the column loop because the cache must be
	   freed only when the required columns in the row are all read.
	   - Added pointer check because pointer will point to nothing
	   if this is a non-column get. 
	   - Added setting cache_output_pointer to NULL because if it is not
	   NULL, the next iteration of agent_process will try to delete it */
	/* Bug fix 4127458 . Added check to see if entry has been initialized */
	if(ssa_mem_free != 0 && entry != NULL && entry->dealloc != NULL && pointer != NULL){
	/* remember to turn off the caching */
	(*(entry->dealloc))(pointer);
	pointer = cache_output_pointer = NULL;
				}

	return 0;
}


/****************************************************************/

/* returns							*/
/*	0	in case of success. If we are in the		*/
/*		FIRST_PASS, we should go to the second pass.	*/
/*	-1	in case of failure. If we are in the		*/
/*		FIRST_PASS, we should not go to the		*/
/*		second pass and no pdu should be sent back.	*/
/*	1	If we are in the FIRST_PASS, we should not go	*/
/*		to the second pass but a pdu			*/
/*		with an SNMP error should be sent back to its	*/
/*		originator					*/

static int agent_set(int pass, SNMP_pdu *pdu, char *error_label)
{
	SNMP_variable *variable;
	Node *node;
	Object *object;
	Column *column;
	Entry *entry;
	Oid suffix;
	int index = 1;
	Integer integer = 0;
	Oid oid = { NULL, 0 };
	String string = { NULL, 0 };
	int snmp_error;
        int i;

	/* create index struct */
	IndexType index_obj;
	int index_buffer[256];

	index_obj.len = 0;
	index_obj.value = index_buffer; 

	error_label[0] = '\0';

	pdu->type = GET_RSP_MSG;

	for(variable = pdu->first_variable; variable; variable = variable->next_variable)
	{
		node = node_find(EXACT_ENTRY, &(variable->name), &suffix);
		if(node == NULL)
		{
			pdu->error_status = SNMP_ERR_NOSUCHNAME;
			pdu->error_index = index;
			return 1;
		}
		/* we should not forget to free suffix.subids */

		if(trace_level > 0)
		{
			trace("!! set(%s): processing the variable %s\n\n",
				(pass == FIRST_PASS)? "FIRST_PASS": "SECOND_PASS",
				node->label);
		}

/*
		if(variable->val.string == NULL)
		{
			(void)sprintf(error_label, "val.string is NULL for node %s",
				node->label);
			SSAOidZero(&suffix);
			return -1;
		}

		if(variable->val_len == 0)
		{
			(void)sprintf(error_label, "val_len is 0 for node %s",
				node->label);
			SSAOidZero(&suffix);
			return -1;
		}
*/

		switch(node->type)
		{
			case OBJECT:
				object = node->data.object;

				/* check the ASN.1 type */
				if(variable->type != object->asn1_type)
				{
					(void)sprintf(error_label, "wrong ASN.1 type (0x%x) for node %s",
						variable->type, node->label);
					SSAOidZero(&suffix);
					return -1;
				}

				/* check the suffix */
				if( (suffix.len != 1)
					|| (suffix.subids[0] != 0) )
				{
					pdu->error_status = SNMP_ERR_NOSUCHNAME;
					pdu->error_index = index;
					SSAOidZero(&suffix);
					return 1;
				}

				/* check the access */
				if( !(object->access & WRITE_FLAG) )
				{
					pdu->error_status = SNMP_ERR_READONLY;
					pdu->error_index = index;
					SSAOidZero(&suffix);
					return 1;
				}

				/* check the value length */
				switch(object->asn1_type)
				{
					case INTEGER:
					case COUNTER:
					case GAUGE:
					case TIMETICKS:
					case IPADDRESS:
						if(variable->val_len != 4)
						{
							(void)sprintf(error_label, "val_len is not 4 (%d) for node %s",
								variable->val_len, node->label);
							SSAOidZero(&suffix);
							return -1;
						}

						if(variable->val.integer == NULL)
						{
							(void)sprintf(error_label, "val.integer is NULL for node %s",
								node->label);
							SSAOidZero(&suffix);
							return -1;
						}

						break;
				}

				/* in case of enumerated integer, check the value */
				if( (object->asn1_type == INTEGER)
					&& (object->first_enum != NULL) )
				{
					Enum *enums;


					integer = *(variable->val.integer);

					for(enums = object->first_enum; enums; enums = enums->next_enum)
					{
						if(enums->value == integer)
						{
							break;
						}
					}

					if(enums == NULL)
					{
						pdu->error_status = SNMP_ERR_BADVALUE;
						pdu->error_index = index;
						SSAOidZero(&suffix);
						return 1;
					}
				}

				switch(object->asn1_type)
				{
					case INTEGER:
					case COUNTER:
					case GAUGE:
					case TIMETICKS:
						integer = *(variable->val.integer);

						snmp_error = (*(object->set))(pass, &integer);
						break;

					case OBJID:
						if(SSAOidInit(&oid, variable->val.objid,
							variable->val_len / (int32_t)sizeof(Subid),
							error_label)) {
							SSAOidZero(&suffix);
							return -1;
						}

						snmp_error = (*(object->set))(pass, &oid);
						SSAOidZero(&oid);
						break;

					case STRING:
					case IPADDRESS:
					case OPAQUE:
						if(SSAStringInit(&string, variable->val.string,
							variable->val_len, error_label)) {
							SSAOidZero(&suffix);
							return -1;
						}

						snmp_error = (*(object->set))(pass, &string);
						SSAStringZero(&string);
						break;
				}

				if(snmp_error != SNMP_ERR_NOERROR)
				{
					if(snmp_error < 0)
					{
						error("the set(%s) method of %s returned %d",
							(pass == FIRST_PASS)? "FIRST_PASS": "SECOND_PASS",
							node->label, snmp_error);
						snmp_error = SNMP_ERR_GENERR;
					}
					pdu->error_status = snmp_error;
					pdu->error_index = index;
					SSAOidZero(&suffix);
					return 1;
				}

				break;


			case COLUMN:
				column = node->data.column;
				entry = column->entry;

				/* check the ASN.1 type */
				if(variable->type != column->asn1_type)
				{
					(void)sprintf(error_label, "wrong ASN.1 type (0x%x) for node %s",
						variable->type, node->label);
					SSAOidZero(&suffix);
					return -1;
				}

				/* check the suffix */
				if (suffix.subids == NULL)
				{
					pdu->error_status = SNMP_ERR_NOSUCHNAME;
					pdu->error_index = index;
					SSAOidZero(&suffix);
					return 1;
				}

				/* check the access */
				if( !(column->access & WRITE_FLAG) )
				{
					pdu->error_status = SNMP_ERR_READONLY;
					pdu->error_index = index;
					SSAOidZero(&suffix);
					return 1;
				}

				/* check the value length */
				switch(column->asn1_type)
				{
					case INTEGER:
					case COUNTER:
					case GAUGE:
					case TIMETICKS:
					case IPADDRESS:
						if(variable->val_len != 4)
						{
							(void)sprintf(error_label, "val_len is not 4 (%d) for node %s",
								variable->val_len, node->label);
							SSAOidZero(&suffix);
							return -1;
						}

						if(variable->val.integer == NULL)
						{
							(void)sprintf(error_label, "val.integer is NULL for node %s",
								node->label);
							SSAOidZero(&suffix);
							return -1;
						}

						break;
				}

				/* in case of enumerated integer, check the value */
				if( (column->asn1_type == INTEGER)
					&& (column->first_enum != NULL) )
				{
					Enum *enums;


					integer = *(variable->val.integer);

					for(enums = column->first_enum; enums; enums = enums->next_enum)
					{
						if(enums->value == integer)
						{
							break;
						}
					}

					if(enums == NULL)
					{
						pdu->error_status = SNMP_ERR_BADVALUE;
						pdu->error_index = index;
						SSAOidZero(&suffix);
						return 1;
					}
				}

				switch(column->asn1_type)
				{
					case INTEGER:
					case COUNTER:
					case GAUGE:
					case TIMETICKS:
						integer = *(variable->val.integer);

						break;

					case OBJID:
						if(SSAOidInit(&oid, variable->val.objid,
							variable->val_len / (int32_t)sizeof(Subid), error_label)) {
							SSAOidZero(&suffix);
							return -1;
						}

						break;

					case STRING:
					case IPADDRESS:
					case OPAQUE:
						if(SSAStringInit(&string, variable->val.string, variable->val_len, error_label))
						{
							SSAOidZero(&suffix);
							return -1;
						}

						break;
				}

				index_obj.len = suffix.len;
				for (i = 0; i < suffix.len; i++)
                                {
                                    index_obj.value[i] = suffix.subids[i];
                                }

						
						switch(column->asn1_type)
						{
							case INTEGER:
							case COUNTER:
							case GAUGE:
							case TIMETICKS:
								snmp_error = (*(column->set))(pass, index_obj, &integer);
								break;

							case OBJID:
								snmp_error = (*(column->set))(pass, index_obj, &oid);
								break;

							case STRING:
							case IPADDRESS:
							case OPAQUE:
								snmp_error = (*(column->set))(pass, index_obj, &string);
								break;
				}

				switch(column->asn1_type)
				{
					case OBJID:
						SSAOidZero(&oid);
						break;

					case STRING:
					case IPADDRESS:
					case OPAQUE:
						SSAStringZero(&string);
						break;
				}

				if(snmp_error != SNMP_ERR_NOERROR)
				{
					if(snmp_error < 0)
					{
						error("the set(%s) method of %s returned %d",
							(pass == FIRST_PASS)? "FIRST_PASS": "SECOND_PASS",
							node->parent->label,
							snmp_error);
							snmp_error = SNMP_ERR_GENERR;
					}
					
					pdu->error_status = snmp_error;
					pdu->error_index = index;
					SSAOidZero(&suffix);
					return 1;
				}

				break;


			case NODE:
				pdu->error_status = SNMP_ERR_NOSUCHNAME;
				pdu->error_index = index;
				SSAOidZero(&suffix);
				return 1;
		}

		SSAOidZero(&suffix);

		index++;
	}

	return 0;
}


/****************************************************************/

/* flag == 0 means turn off auto mem free */
void SSAAutoMemFree(int flag)
{
  ssa_mem_free = flag;
}