blob: 955dc029f58e488c964f9b621334f002ea67388e [file] [log] [blame]
/*
* master_request.c
*
* Handle passing SNMP requests to and from AgentX clients
*/
#include "config.h"
#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#define SNMP_NEED_REQUEST_LIST
#include "asn1.h"
#include "mib.h"
#include "snmp_api.h"
#include "snmp_impl.h"
#include "snmp_client.h"
#include "snmp_debug.h"
#include "snmp.h"
#include "ds_agent.h"
#include "default_store.h"
#include "agentx/protocol.h"
#include "agentx/client.h"
#include "agentx/master.h"
#include "snmp_agent.h"
#include "snmp_vars.h"
#include "var_struct.h"
#include "mibII/sysORTable.h"
extern int close_agentx_session(struct snmp_session *session, int sessid);
#define VARLIST_ITERATION 10
struct ax_variable_list {
struct agent_snmp_session *asp;
int num_vars;
int max_vars;
/* Placeholder for dynamically-resized list of variables */
struct variable_list* variables[1];
};
void
free_agentx_request(struct request_list *req)
{
if ( !req )
return;
/*
if ( req->cb_data )
free ( req->cb_data );
*/
free ( req );
}
void
free_agentx_varlist(struct ax_variable_list *vlist)
{
if ( !vlist )
return;
if ( vlist->variables )
free ( vlist->variables );
free ( vlist );
}
/*
* Remove the specified request from
* the list of outstanding requests
*/
int
remove_outstanding_request( struct agent_snmp_session *asp, int reqid )
{
struct request_list *req, *prev;
for (req = asp->outstanding_requests, prev=NULL ;
req != NULL ;
prev = req , req = req->next_request ) {
if ( req->request_id == reqid) {
if ( prev )
prev->next_request = req->next_request;
else
asp->outstanding_requests = req->next_request;
free_agentx_request( req );
return 0;
}
}
return 1;
}
/*
* Handle the response from an AgentX subagent,
* merging the answers back into the original query
*/
int
handle_agentx_response( int operation,
struct snmp_session *session,
int reqid,
struct snmp_pdu *pdu,
void *magic)
{
struct ax_variable_list *ax_vlist = (struct ax_variable_list *)magic;
struct variable_list *vbp, *next;
struct agent_snmp_session *asp = ax_vlist->asp;
struct snmp_session *ax_session;
struct request_list *req;
int i, type, index;
struct ax_variable_list *retry_vlist;
struct variable_list *vb_retry, *vbp2;
struct subtree *retry_sub;
int j;
char buf[SPRINT_MAX_LEN];
remove_outstanding_request( asp, pdu->reqid );
switch(operation) {
case TIMED_OUT:
/*
* Multiple timed out requests probably
* indicate that the subagent has died.
*/
if ( SET_SNMP_STRIKE_FLAGS( session->flags )) {
ax_session = session->subsession;
/*
* XXX - Need to send a 'close' message
* to the subagent (even though
* we don't think it's listening)
*/
(void) close_agentx_session(ax_session, session->sessid);
if ( ax_session->subsession == NULL ) {
snmp_close( ax_session );
}
}
pdu->errstat = SNMP_ERR_GENERR;
pdu->errindex = 0;
if (asp->pdu->command != SNMP_MSG_SET)
asp->mode = RESERVE2;
break;
case SEND_FAILED:
if ( SET_SNMP_STRIKE_FLAGS( session->flags )) {
(void) close_agentx_session(session, -1 );
}
pdu->errstat = SNMP_ERR_GENERR;
pdu->errindex = 0;
if (asp->pdu->command != SNMP_MSG_SET)
asp->mode = RESERVE2;
break;
case RECEIVED_MESSAGE:
/* This session is alive */
CLEAR_SNMP_STRIKE_FLAGS( session->flags );
break;
default:
return 0;
}
asp->status = pdu->errstat;
if ( pdu->errstat != AGENTX_ERR_NOERROR ) {
/*
* If the request failed, locate the
* original index of the variable resonsible
*/
if ( pdu->errindex != 0 && pdu->errindex < ax_vlist->num_vars )
asp->index = ax_vlist->variables[pdu->errindex-1]->index;
else
asp->index = 0;
}
else {
/*
* Otherwise, process successful requests
*/
DEBUGMSGTL(("agentx/master","handle_agentx_response() beginning...\n"));
for ( i = 0, vbp = pdu->variables ;
vbp && i < ax_vlist->num_vars ;
i++, vbp = vbp->next_variable ) {
if (vbp) {
DEBUGMSGTL(("agentx/master"," handle_agentx_response: processing: "));
DEBUGMSGOID(("agentx/master",vbp->name, vbp->name_length));
DEBUGMSG(("agentx/master","\n"));
if ( ds_get_boolean(DS_APPLICATION_ID, DS_AGENT_VERBOSE) ) {
sprint_variable (buf, vbp->name, vbp->name_length, vbp);
DEBUGMSGTL(("snmp_agent", " >> %s\n", buf));
}
}
if ( !asp->exact && (vbp->type == SNMP_ENDOFMIBVIEW ||
in_a_view( vbp->name, &vbp->name_length,
asp->pdu, vbp->type ))) {
/*
* Retry unfulfilled requests
*/
retry_sub = find_subtree_next( vbp->name, vbp->name_length, NULL );
if ( retry_sub ) {
(void)snmp_set_var_objid(ax_vlist->variables[i], retry_sub->name, retry_sub->namelen);
asp->index = ax_vlist->variables[i]->index;
asp->status = handle_one_var(asp, ax_vlist->variables[i]);
}
else
ax_vlist->variables[i]->type = SNMP_ENDOFMIBVIEW;
}
else {
next = ax_vlist->variables[i]->next_variable;
index = ax_vlist->variables[i]->index;
snmp_clone_var( vbp, ax_vlist->variables[i] );
ax_vlist->variables[i]->next_variable = next;
ax_vlist->variables[i]->index = index;
}
type = ax_vlist->variables[i]->type;
if ((asp->pdu->version == SNMP_VERSION_1) &&
((type == SNMP_ENDOFMIBVIEW) || (type == SNMP_NOSUCHOBJECT) || (type == SNMP_NOSUCHINSTANCE))) {
asp->index = ax_vlist->variables[i]->index;
asp->status = SNMP_ERR_NOSUCHNAME;
goto finish;
}
}
}
/*
* If we're in the middle of a SET,
* then update the state machine
*/
if (asp->pdu->command == SNMP_MSG_SET) {
switch( asp->mode ) {
case RESERVE2:
if ( asp->status == SNMP_ERR_NOERROR )
asp->mode = ACTION;
else
asp->mode = FREE;
break;
case ACTION:
if ( asp->status != SNMP_ERR_NOERROR )
asp->mode = UNDO;
break;
case COMMIT:
if ( asp->status != SNMP_ERR_NOERROR )
asp->mode = FREE;
break;
}
}
if ( asp->outstanding_requests ) {
/*
* Send out any newly delegated requests
* See 'handle_one_var' above
*/
for ( req=asp->outstanding_requests ; req ; req=req->next_request ) {
if ( req->pdu ) {
snmp_async_send(req->session, req->pdu,
req->callback, req->cb_data);
req->pdu = NULL;
}
}
}
finish:
DEBUGMSGTL(("agentx/master","handle_agentx_response() finishing...\n"));
return handle_snmp_packet(operation, session, reqid, asp->pdu, (void*)asp);
}
/*
* Return the request for this subagent transaction
* (creating a new one if necessary)
*/
struct request_list *
get_agentx_request(struct agent_snmp_session *asp,
struct snmp_session *ax_session, int transID )
{
struct request_list *req;
struct snmp_pdu *pdu;
struct ax_variable_list *vlist;
int new_size;
DEBUGMSGTL(("agentx/master","processing request...\n"));
for (req = asp->outstanding_requests ; req != NULL ; req = req->next_request ) {
if ( req->message_id == transID && req->session == ax_session) {
/*
* Check if there's room in this request for another variable.
* If not, then expand it by 'VARLIST_ITERATION' variables.
*/
vlist = (struct ax_variable_list *)req->cb_data;
if ( vlist->num_vars > vlist->max_vars ) { /* i.e. full */
DEBUGMSGTL(("agentx/master", "increasing ax_variable list...\n"));
new_size = sizeof(struct ax_variable_list) +
(vlist->max_vars + VARLIST_ITERATION) * sizeof(struct variable_list);
vlist = (struct ax_variable_list *)realloc(vlist, new_size);
if ( !vlist )
break;
vlist->max_vars += VARLIST_ITERATION;
req->cb_data = (void *)vlist;
}
return req;
}
}
/*
* No existing request found, so create a new one
*/
req = (struct request_list *)calloc( 1, sizeof(struct request_list));
new_size = sizeof(struct ax_variable_list) +
VARLIST_ITERATION * sizeof(struct variable_list);
vlist = (struct ax_variable_list *)calloc( 1, new_size);
pdu = snmp_pdu_create( 0 );
if ( req == NULL || pdu == NULL || vlist == NULL ) {
free_agentx_request( req );
snmp_free_pdu( pdu );
free_agentx_varlist( vlist );
return NULL;
}
/*
* Initialise the structures:
* pdu,
*/
pdu->version = AGENTX_VERSION_1;
pdu->reqid = snmp_get_next_transid();
pdu->transid = asp->pdu->transid;
pdu->sessid = ax_session->sessid;
switch (asp->pdu->command ) {
case SNMP_MSG_GET:
DEBUGMSGTL(("agentx/master","-> get\n"));
pdu->command = AGENTX_MSG_GET;
break;
case SNMP_MSG_GETNEXT:
case SNMP_MSG_GETBULK:
DEBUGMSGTL(("agentx/master","-> getnext/bulk\n"));
pdu->command = AGENTX_MSG_GETNEXT;
break;
case SNMP_MSG_SET:
DEBUGMSGTL(("agentx/master","-> set\n"));
switch ( asp->mode ) {
/*
* This is a provisional mapping of
* UCD SET handling into the AgentX
* protocol.
* It is by no means definitive.
*/
case RESERVE1:
case RESERVE2:
pdu->command = AGENTX_MSG_TESTSET;
break;
case ACTION:
pdu->command = AGENTX_MSG_COMMITSET;
break;
case UNDO:
pdu->command = AGENTX_MSG_UNDOSET;
break;
case FREE:
case COMMIT:
pdu->command = AGENTX_MSG_CLEANUPSET;
break;
}
break;
default:
DEBUGMSGTL(("agentx/master","-> unknown\n"));
free_agentx_request( req );
snmp_free_pdu( pdu );
free_agentx_varlist( vlist );
return NULL;
}
/* callback data, */
vlist->asp = asp;
vlist->num_vars = 0;
/* and request */
req->request_id = pdu->reqid;
req->message_id = pdu->transid;
req->callback = handle_agentx_response;
req->cb_data = vlist;
req->pdu = pdu;
req->session = ax_session;
req->next_request = asp->outstanding_requests;
asp->outstanding_requests = req;
DEBUGMSGTL(("agentx/master","processing request... DONE\n"));
return req;
}
/*
* Delegate a request to an AgentX subagent
* adding it to the list for that particular agent
*/
int
agentx_add_request( struct agent_snmp_session *asp,
struct variable_list *vbp)
{
struct snmp_pdu *pdu = asp->pdu;
struct snmp_session *ax_session;
struct request_list *request;
struct ax_variable_list *ax_vlist;
struct subtree *sub;
int sessid;
if (asp->pdu->command == SNMP_MSG_SET && asp->mode == RESERVE1 )
return AGENTX_ERR_NOERROR;
ax_session = get_session_for_oid( vbp->name, vbp->name_length );
if ( !ax_session )
return SNMP_ERR_GENERR;
sessid = ax_session->sessid;
if ( ax_session->flags & SNMP_FLAGS_SUBSESSION )
ax_session = ax_session->subsession;
request = get_agentx_request( asp, ax_session, pdu->transid );
if ( !request )
return SNMP_ERR_GENERR;
request->pdu->sessid = sessid; /* Use the registered (sub)session's ID,
not the main listening session ID */
ax_vlist = (struct ax_variable_list *)request->cb_data;
ax_vlist->variables[ ax_vlist->num_vars ] = vbp;
vbp->index = asp->index; /* Remember the variable index */
ax_vlist->num_vars++;
sub = find_subtree_previous( vbp->name, vbp->name_length, NULL );
if ( asp->exact )
snmp_pdu_add_variable( request->pdu,
vbp->name, vbp->name_length, vbp->type,
(u_char*)(vbp->val.string), vbp->val_len);
else {
snmp_pdu_add_variable( request->pdu,
vbp->name, vbp->name_length, ASN_PRIV_EXCL_RANGE,
(u_char*)sub->end, sub->end_len*sizeof(oid));
}
if ( sub->timeout > request->pdu->time ) {
request->pdu->time = sub->timeout;
request->pdu->flags |= UCD_MSG_FLAG_PDU_TIMEOUT;
}
return AGENTX_ERR_NOERROR;
}