From: kostas <> Date: Fri, 21 Nov 1997 07:30:32 +0000 (+0000) Subject: More initial support for snmp X-Git-Tag: SQUID_3_0_PRE1~4505 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=25c1b8b74a6e955334c323dbbeffd6cd05b850a2;p=thirdparty%2Fsquid.git More initial support for snmp --- diff --git a/src/snmp_agent.cc b/src/snmp_agent.cc new file mode 100644 index 0000000000..3cd588c2f0 --- /dev/null +++ b/src/snmp_agent.cc @@ -0,0 +1,1010 @@ +/* + * Simple Network Management Protocol (RFC 1067). + * + */ +/*********************************************************** + Copyright 1988, 1989 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of CMU not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +******************************************************************/ + +#include "squid.h" + +#include "snmp.h" +#include "snmp_impl.h" +#include "asn1.h" +#include "snmp_api.h" +#include "snmp_client.h" +#include "snmp_config.h" + +#include "mib.h" + +#include "snmp_groupvars.h" + +void snmp_input(); +void snmp_trap(); +int create_identical(); +int parse_var_op_list(); +void setVariable(); + +#if kinetics +char version_descr[]; +oid version_id[]; +int version_id_len; +#endif + +struct pbuf *definitelyGetBuf(); +int get_community(); + +extern communityEntry *communities; +extern usecEntry *users; +extern viewEntry *views; +extern int maintenanceView; + +/* these can't be global in a multi-process router */ +u_char sid[SID_MAX_LEN + 1]; +int sidlen; +u_char *packet_end; + +struct snmp_session _session; +struct snmp_session *session = &_session; + +u_char _agentID[12] = {0}; +u_long _agentBoots; +u_long _agentStartTime; +u_long _agentSize; + + +/* fwd: */ +static int check_auth(); +static int bulk_var_op_list(); +static int goodValue(); +/* from usec.c: */ +extern void increment_stat(); +extern void create_report(); +extern void md5Digest(); +extern int parse_parameters(); + + +int +init_agent_auth() +{ + char hostname[ 256 ]; + struct hostent *hp; + FILE *f; + /* comes from snmpd.c: */ + + /* agentID is based on enterprise number and local IP address */ + /* not "settable, thus, if agentBoots=0xffffffff, then all keys should be changed */ + if (gethostname (hostname, sizeof(hostname)) != 0) { + fatal ("snmpd: cannot get hostname"); + return -1; + } + if( (hp = gethostbyname(hostname)) == NULL ) { + fatal ("snmpd: cannot determine local hostname"); + return -1; + } + _agentID[3] = 35; /* BNR private enterprise number */ + memcpy( &_agentID[4], hp->h_addr, hp->h_length ); + + if( (f = fopen( Config.Snmp.agentInfo, "r+" )) == NULL ) { + debug(49,5) ( + "init_agent_auth: Agent not installed properly, cannot open '%s'\n", + Config.Snmp.agentInfo ); + debug(49,5)("init_agent_auth: Create a empty file '%s'. This is used for\n", + snmp_agentinfo ); + debug(49,5)( "NV store of the agentBoots object.\n" ); + return -1; + } + fscanf( f, "%ld", &_agentBoots ); + _agentBoots++; + fseek( f, 0, 0 ); + fprintf( f, "%ld\n", _agentBoots ); + fclose( f ); + + + _agentStartTime = -time(NULL); + + _agentSize = SNMP_MAX_LEN; + + return 0; +} + +int +snmp_agent_parse(sn_data, length, out_sn_data, out_length, sourceip ,ireqid) +u_char *sn_data; +int length; +u_char *out_sn_data; +int *out_length; +u_long sourceip; /* possibly for authentication */ +long *ireqid; +{ + u_char msgtype, type; + long zero = 0; + long reqid, errstat, errindex, dummyindex; + u_char *out_auth, *out_header, *out_reqid; + u_char *startData = sn_data; + int startLength = length; + long version; + u_char *origsn_data = sn_data; + int origlen = length; + usecEntry *ue; + int ret = 0, packet_len; + + sidlen = SID_MAX_LEN; + sn_data = snmp_auth_parse(sn_data, &length, sid, &sidlen, &version); + if (sn_data == NULL){ + increment_stat( SNMP_STAT_ENCODING_ERRORS ); + debug(49,5)("snmp_agent_parse: bad auth encoding"); + return 0; + } + if( version != SNMP_VERSION_1 && version != SNMP_VERSION_2C && version != SNMP_VERSION_2 ) { + increment_stat( SNMP_STAT_ENCODING_ERRORS ); + debug(49,5)("snmp_agent_parse: wrong version"); +#ifdef linux + snmp_inbadversions++; +#endif + return 0; + } + + if( version == SNMP_VERSION_2C || version == SNMP_VERSION_2 ) { + if( version == SNMP_VERSION_2 ) { + ret = check_auth( session, origsn_data, origlen, sn_data - sidlen, sidlen, &ue ); + *out_length = (SNMP_MAX_LEN < session->MMS) ? SNMP_MAX_LEN : session->MMS; + session->MMS = SNMP_MAX_LEN; + + } else if( version == SNMP_VERSION_2C ) { + ret = get_community( sid ); + session->version = SNMP_VERSION_2C; + } + + if( ret < 0 ) { + increment_stat( -ret ); + if( (sn_data=asn_parse_header(sn_data, &length, &msgtype)) + && asn_parse_int(sn_data, &length, &type, &reqid, sizeof(reqid)) ) { + if( msgtype == REPORT_MSG ) return 0; + if( !(session->qoS & USEC_QOS_GENREPORT) ) return 0; + session->agentBoots = _agentBoots; + session->agentClock = _agentStartTime; + memcpy( session->agentID, _agentID, 12 ); + session->MMS = SNMP_MAX_LEN; + create_report( session, out_sn_data, out_length, -ret, reqid ); + return 1; + } else { + return 0; + } + } else if( ret > 0 ) { + increment_stat( ret ); + return 0; + } + + } else if( version == SNMP_VERSION_1 ) { + if( (ret = get_community( sid )) != 0 ) { + increment_stat(ret); + return 0; + } + session->version = SNMP_VERSION_1; + } else { + increment_stat( SNMP_STAT_ENCODING_ERRORS ); + debug(49,5)("snmp_agent_parse : wrong version"); +#ifdef linux + snmp_inbadversions++; +#endif + return 0; + } + + sn_data = asn_parse_header(sn_data, &length, &msgtype); + if (sn_data == NULL){ + increment_stat( SNMP_STAT_ENCODING_ERRORS ); + debug(49,5)("snmp_agent_parse: bad header"); + return 0; + } + +#ifdef linux + /* XXX: increment by total number of vars at correct place: */ + snmp_intotalreqvars++; + if (msgtype == GET_REQ_MSG) + snmp_ingetrequests++; + if (msgtype == GETNEXT_REQ_MSG) + snmp_ingetnexts++; + if (msgtype == SET_REQ_MSG) + snmp_insetrequests++; +#endif + + if( msgtype == GETBULK_REQ_MSG ) { + if( session->version == SNMP_VERSION_1 ) + return 0; + } else if (msgtype != GET_REQ_MSG && msgtype != GETNEXT_REQ_MSG && msgtype != SET_REQ_MSG ) { + return 0; + } + sn_data = asn_parse_int(sn_data, &length, &type, &reqid, sizeof(reqid)); + if (sn_data == NULL){ + increment_stat( SNMP_STAT_ENCODING_ERRORS ); + debug(49,5)("snmp_agent_parse: bad parse of reqid"); + return 0; + } + sn_data = asn_parse_int(sn_data, &length, &type, &errstat, sizeof(errstat)); + if (sn_data == NULL){ + increment_stat( SNMP_STAT_ENCODING_ERRORS ); + debug(49,5)("snmp_agent_parse: bad parse of errstat"); +#ifdef linux + snmp_inasnparseerrors++; +#endif + return 0; + } + sn_data = asn_parse_int(sn_data, &length, &type, &errindex, sizeof(errindex)); + if (sn_data == NULL){ + increment_stat( SNMP_STAT_ENCODING_ERRORS ); + debug(49,5)("bad parse of errindex"); + return 0; + } + /* + * Now start cobbling together what is known about the output packet. + * The final lengths are not known now, so they will have to be recomputed + * later. + */ + + /* setup for response */ + time( (time_t *) &session->agentTime ); + session->agentClock = _agentStartTime; + session->agentBoots = _agentBoots; + memcpy( session->agentID, _agentID, 12 ); + + out_auth = out_sn_data; + out_header = snmp_auth_build( out_auth, out_length, session, 1, 0 ); + if (out_header == NULL){ + debug(49,5)("snmp_agent_parse: snmp_auth_build failed"); +#ifdef linux + snmp_inasnparseerrors++; +#endif + return 0; + } + + out_reqid = asn_build_sequence(out_header, out_length, (u_char)GET_RSP_MSG, 0); + if (out_reqid == NULL){ + debug(49,5)("snmp_agent_parse; out_reqid == NULL"); + return 0; + } + + type = (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER); + /* return identical request id */ + out_sn_data = asn_build_int(out_reqid, out_length, type, &reqid, sizeof(reqid)); + if (out_sn_data == NULL){ + debug(49,5)("snmp_agent_parse; build reqid failed"); + return 0; + } + + /* assume that error status will be zero */ + out_sn_data = asn_build_int(out_sn_data, out_length, type, &zero, sizeof(zero)); + if (out_sn_data == NULL){ + debug(49,5)("snmp_agent_parse: build errstat failed"); + return 0; + } + + /* assume that error index will be zero */ + out_sn_data = asn_build_int(out_sn_data, out_length, type, &zero, sizeof(zero)); + if (out_sn_data == NULL){ + debug(49,5)("snmp_agent_parse: build errindex failed"); + return 0; + } + + if (msgtype == GETBULK_REQ_MSG) + errstat = bulk_var_op_list(sn_data, length, out_sn_data, *out_length, + errstat, errindex, &errindex ); + else + errstat = parse_var_op_list(sn_data, length, out_sn_data, *out_length, + &errindex, msgtype, RESERVE1); + + if (errstat == SNMP_ERR_NOSUCHNAME ) + { + /* see if we have forwarding turned on */ + if (Config.Snmp.localPort>0) + { + *ireqid=reqid; + return 2; + } + } + if (msgtype== SET_REQ_MSG){ + if (errstat == SNMP_ERR_NOERROR) + errstat = parse_var_op_list(sn_data, length, out_sn_data, *out_length, + &errindex, msgtype, RESERVE2); + if (errstat == SNMP_ERR_NOERROR){ + /* + * SETS require 3-4 passes through the var_op_list. The first two + * passes verify that all types, lengths, and values are valid + * and may reserve resources and the third does the set and a + * fourth executes any actions. Then the identical GET RESPONSE + * packet is returned. + * If either of the first two passes returns an error, another + * pass is made so that any reserved resources can be freed. + */ + parse_var_op_list(sn_data, length, out_sn_data, *out_length, + &dummyindex, msgtype, COMMIT); + parse_var_op_list(sn_data, length, out_sn_data, *out_length, + &dummyindex, msgtype, ACTION); + if (create_identical(startData, out_auth, startLength, 0L, 0L )){ + *out_length = packet_end - out_auth; + return 1; + } + return 0; + } else { + parse_var_op_list(sn_data, length, out_sn_data, *out_length, + &dummyindex, msgtype, FREE); + } + } + switch((short)errstat){ + case SNMP_ERR_NOERROR: + /* re-encode the headers with the real lengths */ + *out_length = packet_end - out_header; + packet_len = *out_length; + out_sn_data = asn_build_sequence(out_header, out_length, GET_RSP_MSG, + packet_end - out_reqid); + if (out_sn_data != out_reqid){ + debug(49,5)("snmp_agent_parse: internal error: header"); + return 0; + } + + *out_length = packet_end - out_auth; + out_sn_data = snmp_auth_build( out_auth, out_length, session, 1, packet_end - out_header ); + + *out_length = packet_end - out_auth; +#if 0 + /* packet_end is correct for old SNMP. This dichotomy needs + to be fixed. */ + if (session->version == SNMP_VERSION_2) + packet_end = out_auth + packet_len; +#endif + break; + case SNMP_ERR_TOOBIG: + snmp_intoobigs++; +#if notdone + if (session->version == SNMP_VERSION_2){ + create_toobig(out_auth, *out_length, reqid, pi); + break; + } /* else FALLTHRU */ +#endif + case SNMP_ERR_NOACCESS: + case SNMP_ERR_WRONGTYPE: + case SNMP_ERR_WRONGLENGTH: + case SNMP_ERR_WRONGENCODING: + case SNMP_ERR_WRONGVALUE: + case SNMP_ERR_NOCREATION: + case SNMP_ERR_INCONSISTENTVALUE: + case SNMP_ERR_RESOURCEUNAVAILABLE: + case SNMP_ERR_COMMITFAILED: + case SNMP_ERR_UNDOFAILED: + case SNMP_ERR_AUTHORIZATIONERROR: + case SNMP_ERR_NOTWRITABLE: + case SNMP_ERR_INCONSISTENTNAME: + case SNMP_ERR_NOSUCHNAME: + case SNMP_ERR_BADVALUE: + case SNMP_ERR_READONLY: + case SNMP_ERR_GENERR: + if (create_identical(startData, out_auth, startLength, errstat, + errindex)){ + *out_length = packet_end - out_auth; + return 1; + } + return 0; + default: + return 0; + } + + if( session->qoS & USEC_QOS_AUTH ) { + md5Digest( out_auth, *out_length, out_sn_data - (session->contextLen + 16), + out_sn_data - (session->contextLen + 16) ); + } + + return 1; +} + +/* + * Parse_var_op_list goes through the list of variables and retrieves each one, + * placing it's value in the output packet. In the case of a set request, + * if action is RESERVE, the value is just checked for correct type and + * value, and resources may need to be reserved. If the action is COMMIT, + * the variable is set. If the action is FREE, an error was discovered + * somewhere in the previous RESERVE pass, so any reserved resources + * should be FREE'd. + * If any error occurs, an error code is returned. + */ +int +parse_var_op_list(sn_data, length, out_sn_data, out_length, index, msgtype, action) + u_char *sn_data; + int length; + u_char *out_sn_data; + int out_length; + long *index; + int msgtype; + int action; +{ + u_char type; + oid var_name[MAX_NAME_LEN]; + int var_name_len, var_val_len; + u_char var_val_type, *var_val, statType; + u_char *statP; + int statLen; + u_short acl; + int rw, exact, err; + int (*write_method)(); + u_char *headerP, *var_list_start; + int dummyLen; + u_char *getStatPtr(); + int noSuchObject; + + if (msgtype== SET_REQ_MSG) + rw = WRITE; + else + rw = READ; + if (msgtype == GETNEXT_REQ_MSG){ + exact = FALSE; + } else { + exact = TRUE; + } + sn_data = asn_parse_header(sn_data, &length, &type); + if (sn_data == NULL){ + debug(49,5)("parse_var_op_list: not enough space for varlist"); + return PARSE_ERROR; + } + if (type != (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR)){ + debug(49,5)("parse_var_op_list: wrong type"); + return PARSE_ERROR; + } + headerP = out_sn_data; + out_sn_data = asn_build_sequence(out_sn_data, &out_length, + (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), 0); + if (out_sn_data == (u_char *)NULL){ + debug(49,5)("parse_var_op_list: not enough space in output packet"); + return BUILD_ERROR; + } + var_list_start = out_sn_data; + + *index = 1; + while((int)length > 0){ + /* parse the name, value pair */ + var_name_len = MAX_NAME_LEN; + sn_data = snmp_parse_var_op(sn_data, var_name, &var_name_len, &var_val_type, + &var_val_len, &var_val, (int *)&length); + if (sn_data == NULL) + return PARSE_ERROR; + /* now attempt to retrieve the variable on the local entity */ + statP = getStatPtr(var_name, &var_name_len, &statType, &statLen, &acl, + exact, &write_method, session->version, &noSuchObject, + msgtype==SET_REQ_MSG ? session->writeView : session->readView ); + if (session->version == SNMP_VERSION_1 && statP == NULL + && (msgtype != SET_REQ_MSG || !write_method)){ + debug(49,5)("parse_var_op_list: internal v1_error"); + return SNMP_ERR_NOSUCHNAME; + } + + /* check if this variable is read-write (in the MIB sense). */ + if( msgtype == SET_REQ_MSG && acl != RWRITE ) + return session->version == SNMP_VERSION_1 ? SNMP_ERR_NOSUCHNAME : SNMP_ERR_NOTWRITABLE; + + /* Its bogus to check here on getnexts - the whole packet shouldn't + be dumped - this should should be the loop in getStatPtr + luckily no objects are set unreadable. This can still be + useful for sets to determine which are intrinsically writable */ + + if (msgtype== SET_REQ_MSG){ + if (write_method == NULL){ + if (statP != NULL){ + /* see if the type and value is consistent with this + entity's variable */ + if (!goodValue(var_val_type, var_val_len, statType, + statLen)){ + if (session->version != SNMP_VERSION_1) + return SNMP_ERR_WRONGTYPE; /* poor approximation */ + else { + snmp_inbadvalues++; + return SNMP_ERR_BADVALUE; + } + } + /* actually do the set if necessary */ + if (action == COMMIT) + setVariable(var_val, var_val_type, var_val_len, + statP, statLen); + } else { + if (session->version != SNMP_VERSION_1) + return SNMP_ERR_NOCREATION; + else + return SNMP_ERR_NOSUCHNAME; + } + } else { + err = (*write_method)(action, var_val, var_val_type, + var_val_len, statP, var_name, + var_name_len); + + /* + * Map the SNMPv2 error codes to SNMPv1 error codes (RFC 2089). + */ + + if (session->version == SNMP_VERSION_1) { + switch (err) { + case SNMP_ERR_NOERROR: + /* keep the no-error error: */ + break; + case SNMP_ERR_WRONGVALUE: + case SNMP_ERR_WRONGENCODING: + case SNMP_ERR_WRONGTYPE: + case SNMP_ERR_WRONGLENGTH: + case SNMP_ERR_INCONSISTENTVALUE: + err = SNMP_ERR_BADVALUE; + break; + case SNMP_ERR_NOACCESS: + case SNMP_ERR_NOTWRITABLE: + case SNMP_ERR_NOCREATION: + case SNMP_ERR_INCONSISTENTNAME: + case SNMP_ERR_AUTHORIZATIONERROR: + err = SNMP_ERR_NOSUCHNAME; + break; + default: + err = SNMP_ERR_GENERR; + break; + } + } + + if (err != SNMP_ERR_NOERROR){ + if (session->version == SNMP_VERSION_1) { + snmp_inbadvalues++; + } + return err; + } + } + } else { + /* retrieve the value of the variable and place it into the + * outgoing packet */ + if (statP == NULL){ + statLen = 0; + if (exact){ + if (noSuchObject == TRUE){ + statType = SNMP_NOSUCHOBJECT; + } else { + statType = SNMP_NOSUCHINSTANCE; + } + } else { + statType = SNMP_ENDOFMIBVIEW; + } + } + out_sn_data = snmp_build_var_op(out_sn_data, var_name, &var_name_len, + statType, statLen, statP, + &out_length); + if (out_sn_data == NULL){ + return SNMP_ERR_TOOBIG; + } + } + + (*index)++; + } + if (msgtype!= SET_REQ_MSG){ + /* save a pointer to the end of the packet */ + packet_end = out_sn_data; + + /* Now rebuild header with the actual lengths */ + dummyLen = packet_end - var_list_start; + if (asn_build_sequence(headerP, &dummyLen, + (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), + dummyLen) == NULL){ + return SNMP_ERR_TOOBIG; /* bogus error ???? */ + } + } + *index = 0; + return SNMP_ERR_NOERROR; +} + +/* + * create a packet identical to the input packet, except for the error status + * and the error index which are set according to the input variables. + * Returns 1 upon success and 0 upon failure. + */ +int +create_identical(snmp_in, snmp_out, snmp_length, errstat, errindex) + u_char *snmp_in; + u_char *snmp_out; + int snmp_length; + long errstat, errindex; +{ + u_char *sn_data; + u_char type; + u_long dummy; + int length, headerLength; + u_char *headerPtr, *reqidPtr, *errstatPtr, *errindexPtr, *varListPtr; + + bcopy((char *)snmp_in, (char *)snmp_out, snmp_length); + length = snmp_length; + headerPtr = snmp_auth_parse(snmp_out, &length, sid, &sidlen, (long *)&dummy); + sid[sidlen] = 0; + if (headerPtr == NULL) + return 0; + reqidPtr = asn_parse_header(headerPtr, &length, (u_char *)&dummy); + if (reqidPtr == NULL) + return 0; + headerLength = length; + errstatPtr = asn_parse_int(reqidPtr, &length, &type, (long *)&dummy, sizeof dummy); /* request id */ + if (errstatPtr == NULL) + return 0; + errindexPtr = asn_parse_int(errstatPtr, &length, &type, (long *)&dummy, sizeof dummy); /* error status */ + if (errindexPtr == NULL) + return 0; + varListPtr = asn_parse_int(errindexPtr, &length, &type, (long *)&dummy, sizeof dummy); /* error index */ + if (varListPtr == NULL) + return 0; + + sn_data = asn_build_header(headerPtr, &headerLength, GET_RSP_MSG, headerLength); + if (sn_data != reqidPtr) + return 0; + length = snmp_length; + type = (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER); + sn_data = asn_build_int(errstatPtr, &length, type, &errstat, sizeof errstat); + if (sn_data != errindexPtr) + return 0; + sn_data = asn_build_int(errindexPtr, &length, type, &errindex, sizeof errindex); + if (sn_data != varListPtr) + return 0; + packet_end = snmp_out + snmp_length; + return 1; +} + +#ifdef KINETICS +struct pbuf * +definitelyGetBuf(){ + struct pbuf *p; + + K_PGET(PT_DATA, p); + while(p == 0){ +#ifdef notdef + if (pq->pq_head != NULL){ + K_PDEQ(SPLIMP, pq, p); + if (p) K_PFREE(p); + } else if (sendq->pq_head != NULL){ + K_PDEQ(SPLIMP, sendq, p); + if (p) K_PFREE(p); + } +#endif + K_PGET(PT_DATA, p); + } + return p; +} +#endif + +static int +check_auth( session, sn_data, length, pp, plen, ueret ) +struct snmp_session *session; +u_char *sn_data; +int length; +u_char *pp; +int plen; +usecEntry **ueret; +{ + usecEntry *ue; + Parameters params; + int ret; + + memset( session, 0, sizeof(*session) ); + + *ueret = NULL; + + session->version = SNMP_VERSION_2; + + if(( ret = parse_parameters( pp, plen, ¶ms ))) return ret; + + /* setup session record for this packet */ + /* setup before any error detection so report may be generated if required */ + session->qoS = params.qoS; + memcpy( session->userName, params.userName, params.userLen ); + session->userLen = params.userLen; + session->MMS = params.MMS; + session->contextLen = params.contextLen; + memcpy( session->contextSelector, params.contextSelector, params.contextLen ); + + /* agentID must be my agentID */ + if( memcmp( _agentID, params.agentID, 12 ) != 0 ) + return -USEC_STAT_UNKNOWN_CONTEXT_SELECTORS; + + /* only support the contextSelector of "" */ + if( params.contextLen != 0 ) return -USEC_STAT_UNKNOWN_CONTEXT_SELECTORS; + + /* lookup the user in my local configuration sn_datastore (LCD) */ + for( ue = users; ue; ue = ue->next ) { + if( ue->userLen != params.userLen ) continue; + if( memcmp( ue->userName, params.userName, params.userLen ) == 0 ) break; + } + + /* if reached end of sn_datastore, user not found */ + if( ue == NULL ) return -USEC_STAT_UNKNOWN_USERNAMES; + + + /* verify that the requested qoS is supported by the userName */ + if( (params.qoS & USEC_QOS_AUTHPRIV) > ue->qoS ) return -USEC_STAT_UNSUPPORTED_QOS; + + memcpy( session->authKey, ue->authKey, 16 ); + + /* check digest and timeliness if this is an auth message */ + if( params.qoS & USEC_QOS_AUTH ) { + int upper, lower, agentTime; + + /* check the digest */ + memcpy( params.authDigestPtr, ue->authKey, 16 ); + md5Digest( sn_data, length, ue->authKey, params.authDigestPtr ); + if( memcmp( params.authDigest, params.authDigestPtr, 16 ) != 0 ) + return -USEC_STAT_WRONG_DIGEST_VALUES; + + /* check timeliness */ + if( _agentBoots == 0xffffffff || _agentBoots != params.agentBoots ) + return -USEC_STAT_NOT_IN_WINDOWS; + + agentTime = _agentStartTime + time(NULL); + upper = agentTime + SNMP_MESSAGE_LIFETIME; + lower = agentTime - SNMP_MESSAGE_LIFETIME; + if( lower < 0 ) lower = 0; + if( params.agentTime < lower || params.agentTime > upper ) + return -USEC_STAT_NOT_IN_WINDOWS; + + session->readView = ue->authReadView; + session->writeView = ue->authWriteView; + } else { + session->readView = ue->noauthReadView; + session->writeView = ue->noauthWriteView; + } + + return 0; +} + +int get_community(sessionid) +u_char *sessionid; +{ + communityEntry *cp; + + for( cp = communities; cp; cp = cp->next ) { + if (!strcmp(cp->name, (char *)sessionid)) + break; + } + + if( cp == NULL ) { + snmp_inbadcommunitynames++; + return SNMP_STAT_V1_BAD_COMMUNITY_NAMES; + } + + memset( session, 0, sizeof(*session) ); + session->community = sessionid; + session->community_len = strlen( sessionid ); + session->readView = cp->readView; + session->writeView = cp->writeView; + + return 0; +} + +static int goodValue(inType, inLen, actualType, actualLen) + u_char inType, actualType; + int inLen, actualLen; +{ + if (inLen > actualLen) + return FALSE; + return (inType == actualType); +} + + +void +setVariable(var_val, var_val_type, var_val_len, statP, statLen) + u_char *var_val; + u_char var_val_type; + int var_val_len; + u_char *statP; + int statLen; +{ + int buffersize = 1000; + + switch(var_val_type){ + case ASN_INTEGER: + case COUNTER: + case GAUGE: + case TIMETICKS: + asn_parse_int(var_val, &buffersize, &var_val_type, (long *)statP, statLen); + break; + case ASN_OCTET_STR: + case IPADDRESS: + case OPAQUE: + asn_parse_string(var_val, &buffersize, &var_val_type, statP, &statLen); + break; + case ASN_OBJECT_ID: + asn_parse_objid(var_val, &buffersize, &var_val_type, (oid *)statP, &statLen); + break; + } +} + +struct repeater { + oid name[MAX_NAME_LEN]; + int length; +} repeaterList[20]; + + +static int +bulk_var_op_list(sn_data, length, out_sn_data, out_length, non_repeaters, max_repetitions, index) + u_char *sn_data; + int length; + u_char *out_sn_data; + int out_length; + int non_repeaters; + int max_repetitions; + long *index; +{ + u_char type; + oid var_name[MAX_NAME_LEN]; + int var_name_len, var_val_len; + u_char var_val_type, *var_val, statType; + u_char *statP; + int statLen; + u_short acl; + int (*write_method)(); + u_char *headerP, *var_list_start; + int dummyLen; + u_char *getStatPtr(); + u_char *repeaterStart, *out_sn_data_save; + int repeatCount, repeaterLength, indexStart, out_length_save; + int full = FALSE; + int noSuchObject, useful; + int repeaterIndex, repeaterCount; + struct repeater *rl; + + if (non_repeaters < 0) + non_repeaters = 0; + max_repetitions = *index; + if (max_repetitions < 0) + max_repetitions = 0; + + sn_data = asn_parse_header(sn_data, &length, &type); + if (sn_data == NULL){ + debug(49,5)("bulk_var_op_list: not enough space for varlist"); + snmp_inasnparseerrors++; + return PARSE_ERROR; + } + if (type != (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR)){ + debug(49,5)("bulk_var_op_list: wrong type"); + snmp_inasnparseerrors++; + return PARSE_ERROR; + } + headerP = out_sn_data; + out_sn_data = asn_build_sequence(out_sn_data, &out_length, + (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), 0); + if (out_sn_data == NULL){ + debug(49,5)("bulk_var_op_list: not enough space in output packet"); + return BUILD_ERROR; + } + var_list_start = out_sn_data; + + out_length -= 32; /* slop factor */ + *index = 1; + while((int)length > 0 && non_repeaters > 0){ + /* parse the name, value pair */ + + var_name_len = MAX_NAME_LEN; + sn_data = snmp_parse_var_op(sn_data, var_name, &var_name_len, &var_val_type, + &var_val_len, &var_val, (int *)&length); + if (sn_data == NULL) + return PARSE_ERROR; + /* now attempt to retrieve the variable on the local entity */ + statP = getStatPtr(var_name, &var_name_len, &statType, &statLen, &acl, + FALSE, &write_method, session->version, &noSuchObject,session->readView ); + + if (statP == NULL) + statType = SNMP_ENDOFMIBVIEW; + + /* save out_sn_data so this varbind can be removed if it goes over + the limit for this packet */ + + /* retrieve the value of the variable and place it into the outgoing packet */ + out_sn_data = snmp_build_var_op(out_sn_data, var_name, &var_name_len, + statType, statLen, statP, + &out_length); + if (out_sn_data == NULL){ + return SNMP_ERR_TOOBIG; /* ??? */ + } + (*index)++; + non_repeaters--; + } + + repeaterStart = out_sn_data; + indexStart = *index; /* index on input packet */ + + repeaterCount = 0; + rl = repeaterList; + useful = FALSE; + while((int)length > 0){ + /* parse the name, value pair */ + rl->length = MAX_NAME_LEN; + sn_data = snmp_parse_var_op(sn_data, rl->name, &rl->length, + &var_val_type, &var_val_len, &var_val, + (int *)&length); + if (sn_data == NULL) { + snmp_inasnparseerrors++; + return PARSE_ERROR; + } + /* now attempt to retrieve the variable on the local entity */ + statP = getStatPtr(rl->name, &rl->length, &statType, &statLen, + &acl, FALSE, &write_method, session->version, &noSuchObject,session->readView); + if (statP == NULL) + statType = SNMP_ENDOFMIBVIEW; + else + useful = TRUE; + + out_sn_data_save = out_sn_data; + out_length_save = out_length; + /* retrieve the value of the variable and place it into the + * outgoing packet */ + out_sn_data = snmp_build_var_op(out_sn_data, rl->name, &rl->length, + statType, statLen, statP, + &out_length); + if (out_sn_data == NULL){ + out_sn_data = out_sn_data_save; + out_length = out_length_save; + full = TRUE; + } + (*index)++; + repeaterCount++; + rl++; + } + repeaterLength = out_sn_data - repeaterStart; + if (!useful) + full = TRUE; + + for(repeatCount = 1; repeatCount < max_repetitions; repeatCount++){ + sn_data = repeaterStart; + length = repeaterLength; + *index = indexStart; + repeaterStart = out_sn_data; + useful = FALSE; + repeaterIndex = 0; + rl = repeaterList; + while((repeaterIndex++ < repeaterCount) > 0 && !full){ + /* now attempt to retrieve the variable on the local entity */ + statP = getStatPtr(rl->name, &rl->length, &statType, &statLen, + &acl, FALSE, &write_method, session->version, &noSuchObject,session->readView); + if (statP == NULL) + statType = SNMP_ENDOFMIBVIEW; + else + useful = TRUE; + + out_sn_data_save = out_sn_data; + out_length_save = out_length; + /* retrieve the value of the variable and place it into the + * Outgoing packet */ + out_sn_data = snmp_build_var_op(out_sn_data, rl->name, &rl->length, statType, statLen, statP, &out_length); + if (out_sn_data == NULL){ + out_sn_data = out_sn_data_save; + out_length = out_length_save; + full = TRUE; + repeatCount = max_repetitions; + } + (*index)++; + rl++; + } + repeaterLength = out_sn_data - repeaterStart; + if (!useful) + full = TRUE; + } + packet_end = out_sn_data; + + /* Now rebuild header with the actual lengths */ + dummyLen = out_sn_data - var_list_start; + if (asn_build_sequence(headerP, &dummyLen, (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), dummyLen) == NULL){ + return SNMP_ERR_TOOBIG; /* bogus error ???? */ + } + *index = 0; + return SNMP_ERR_NOERROR; +}