7910 lines
250 KiB
C
7910 lines
250 KiB
C
|
||
/* Portions of this file are subject to the following copyright(s). See
|
||
* the Net-SNMP's COPYING file for more details and other copyrights
|
||
* that may apply:
|
||
*/
|
||
/******************************************************************
|
||
Copyright 1989, 1991, 1992 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.
|
||
******************************************************************/
|
||
/*
|
||
* Portions of this file are copyrighted by:
|
||
* Copyright Copyright 2003 Sun Microsystems, Inc. All rights reserved.
|
||
* Use is subject to license terms specified in the COPYING file
|
||
* distributed with the Net-SNMP package.
|
||
*
|
||
* Portions of this file are copyrighted by:
|
||
* Copyright (c) 2016 VMware, Inc. All rights reserved.
|
||
* Use is subject to license terms specified in the COPYING file
|
||
* distributed with the Net-SNMP package.
|
||
*/
|
||
|
||
/** @defgroup library The Net-SNMP library
|
||
* @{
|
||
*/
|
||
/*
|
||
* snmp_api.c - API for access to snmp.
|
||
*/
|
||
#include <net-snmp/net-snmp-config.h>
|
||
#include <net-snmp/net-snmp-features.h>
|
||
|
||
#include <stdio.h>
|
||
#include <ctype.h>
|
||
#if HAVE_STDLIB_H
|
||
#include <stdlib.h>
|
||
#endif
|
||
#if HAVE_STRING_H
|
||
#include <string.h>
|
||
#else
|
||
#include <strings.h>
|
||
#endif
|
||
#if HAVE_UNISTD_H
|
||
#include <unistd.h>
|
||
#endif
|
||
#include <sys/types.h>
|
||
#if HAVE_SYS_PARAM_H
|
||
#include <sys/param.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
|
||
#if HAVE_NETINET_IN_H
|
||
#include <netinet/in.h>
|
||
#endif
|
||
#if HAVE_ARPA_INET_H
|
||
#include <arpa/inet.h>
|
||
#endif
|
||
#if HAVE_SYS_SELECT_H
|
||
#include <sys/select.h>
|
||
#endif
|
||
#if HAVE_IO_H
|
||
#include <io.h>
|
||
#endif
|
||
#if HAVE_SYS_SOCKET_H
|
||
#include <sys/socket.h>
|
||
#endif
|
||
#if HAVE_SYS_UN_H
|
||
#include <sys/un.h>
|
||
#endif
|
||
#if HAVE_NETDB_H
|
||
#include <netdb.h>
|
||
#endif
|
||
#if HAVE_NET_IF_DL_H
|
||
#ifndef dynix
|
||
#include <net/if_dl.h>
|
||
#else
|
||
#include <sys/net/if_dl.h>
|
||
#endif
|
||
#endif
|
||
#include <errno.h>
|
||
|
||
#if HAVE_LOCALE_H
|
||
#include <locale.h>
|
||
#endif
|
||
|
||
#if HAVE_DMALLOC_H
|
||
#include <dmalloc.h>
|
||
#endif
|
||
|
||
#define SNMP_NEED_REQUEST_LIST
|
||
#include <net-snmp/types.h>
|
||
#include <net-snmp/output_api.h>
|
||
#include <net-snmp/config_api.h>
|
||
#include <net-snmp/utilities.h>
|
||
|
||
#include <net-snmp/library/asn1.h>
|
||
#include <net-snmp/library/snmp.h> /* for xdump & {build,parse}_var_op */
|
||
#include <net-snmp/library/snmp_api.h>
|
||
#include <net-snmp/library/snmp_client.h>
|
||
#include <net-snmp/library/parse.h>
|
||
#include <net-snmp/library/mib.h>
|
||
#include <net-snmp/library/int64.h>
|
||
#include <net-snmp/library/snmpv3.h>
|
||
#include <net-snmp/library/callback.h>
|
||
#include <net-snmp/library/container.h>
|
||
#include <net-snmp/library/snmp_secmod.h>
|
||
#include <net-snmp/library/large_fd_set.h>
|
||
#ifdef NETSNMP_SECMOD_USM
|
||
#include <net-snmp/library/snmpusm.h>
|
||
#endif
|
||
#ifdef NETSNMP_SECMOD_KSM
|
||
#include <net-snmp/library/snmpksm.h>
|
||
#endif
|
||
#include <net-snmp/library/keytools.h>
|
||
#include <net-snmp/library/lcd_time.h>
|
||
#include <net-snmp/library/snmp_alarm.h>
|
||
#include <net-snmp/library/snmp_transport.h>
|
||
#include <net-snmp/library/snmp_service.h>
|
||
#include <net-snmp/library/vacm.h>
|
||
#if defined(NETSNMP_USE_OPENSSL) && defined(HAVE_LIBSSL)
|
||
#include <openssl/ssl.h>
|
||
#include <net-snmp/library/cert_util.h>
|
||
#endif
|
||
|
||
netsnmp_feature_child_of(statistics, libnetsnmp)
|
||
netsnmp_feature_child_of(snmp_api, libnetsnmp)
|
||
netsnmp_feature_child_of(oid_is_subtree, snmp_api)
|
||
netsnmp_feature_child_of(snmpv3_probe_contextEngineID_rfc5343, snmp_api)
|
||
|
||
static void _init_snmp(void);
|
||
|
||
static int _snmp_store_needed = 0;
|
||
|
||
#include "../agent/mibgroup/agentx/protocol.h"
|
||
#include <net-snmp/library/transform_oids.h>
|
||
#ifndef timercmp
|
||
#define timercmp(tvp, uvp, cmp) \
|
||
/* CSTYLED */ \
|
||
((tvp)->tv_sec cmp (uvp)->tv_sec || \
|
||
((tvp)->tv_sec == (uvp)->tv_sec && \
|
||
/* CSTYLED */ \
|
||
(tvp)->tv_usec cmp (uvp)->tv_usec))
|
||
#endif
|
||
#ifndef timerclear
|
||
#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
|
||
#endif
|
||
|
||
/*
|
||
* Globals.
|
||
*/
|
||
#ifndef NETSNMP_STREAM_QUEUE_LEN
|
||
#define NETSNMP_STREAM_QUEUE_LEN 5
|
||
#endif
|
||
|
||
#ifndef BSD4_3
|
||
#define BSD4_2
|
||
#endif
|
||
|
||
static oid default_enterprise[] = { 1, 3, 6, 1, 4, 1, 3, 1, 1 };
|
||
/*
|
||
* enterprises.cmu.systems.cmuSNMP
|
||
*/
|
||
|
||
#define DEFAULT_COMMUNITY "public"
|
||
#define DEFAULT_RETRIES 5
|
||
#define DEFAULT_TIMEOUT ONE_SEC
|
||
#define DEFAULT_REMPORT SNMP_PORT
|
||
#define DEFAULT_ENTERPRISE default_enterprise
|
||
#define DEFAULT_TIME 0
|
||
|
||
/*
|
||
* Internal information about the state of the snmp session.
|
||
*/
|
||
struct snmp_internal_session {
|
||
netsnmp_request_list *requests; /* Info about outstanding requests */
|
||
netsnmp_request_list *requestsEnd; /* ptr to end of list */
|
||
int (*hook_pre) (netsnmp_session *, netsnmp_transport *,
|
||
void *, int);
|
||
int (*hook_parse) (netsnmp_session *, netsnmp_pdu *,
|
||
u_char *, size_t);
|
||
int (*hook_post) (netsnmp_session *, netsnmp_pdu *, int);
|
||
int (*hook_build) (netsnmp_session *, netsnmp_pdu *,
|
||
u_char *, size_t *);
|
||
int (*hook_realloc_build) (netsnmp_session *,
|
||
netsnmp_pdu *, u_char **,
|
||
size_t *, size_t *);
|
||
int (*check_packet) (u_char *, size_t);
|
||
netsnmp_pdu *(*hook_create_pdu) (netsnmp_transport *,
|
||
void *, size_t);
|
||
|
||
u_char *packet; /* curr rcv packet data (may be incomplete) */
|
||
size_t packet_len; /* length of data received so far */
|
||
size_t packet_size; /* size of buffer for packet data */
|
||
|
||
u_char *obuf; /* send packet buffer */
|
||
size_t obuf_size; /* size of buffer for packet data */
|
||
u_char *opacket; /* send packet data (within obuf) */
|
||
size_t opacket_len; /* length of data */
|
||
};
|
||
|
||
/*
|
||
* information about received packet
|
||
*/
|
||
typedef struct snmp_rcv_packet_s {
|
||
u_char *packet;
|
||
size_t packet_len;
|
||
void *opaque;
|
||
int olength;
|
||
} snmp_rcv_packet;
|
||
|
||
static const char *api_errors[-SNMPERR_MAX + 1] = {
|
||
"No error", /* SNMPERR_SUCCESS */
|
||
"Generic error", /* SNMPERR_GENERR */
|
||
"Invalid local port", /* SNMPERR_BAD_LOCPORT */
|
||
"Unknown host", /* SNMPERR_BAD_ADDRESS */
|
||
"Unknown session", /* SNMPERR_BAD_SESSION */
|
||
"Too long", /* SNMPERR_TOO_LONG */
|
||
"No socket", /* SNMPERR_NO_SOCKET */
|
||
"Cannot send V2 PDU on V1 session", /* SNMPERR_V2_IN_V1 */
|
||
"Cannot send V1 PDU on V2 session", /* SNMPERR_V1_IN_V2 */
|
||
"Bad value for non-repeaters", /* SNMPERR_BAD_REPEATERS */
|
||
"Bad value for max-repetitions", /* SNMPERR_BAD_REPETITIONS */
|
||
"Error building ASN.1 representation", /* SNMPERR_BAD_ASN1_BUILD */
|
||
"Failure in sendto", /* SNMPERR_BAD_SENDTO */
|
||
"Bad parse of ASN.1 type", /* SNMPERR_BAD_PARSE */
|
||
"Bad version specified", /* SNMPERR_BAD_VERSION */
|
||
"Bad source party specified", /* SNMPERR_BAD_SRC_PARTY */
|
||
"Bad destination party specified", /* SNMPERR_BAD_DST_PARTY */
|
||
"Bad context specified", /* SNMPERR_BAD_CONTEXT */
|
||
"Bad community specified", /* SNMPERR_BAD_COMMUNITY */
|
||
"Cannot send noAuth/Priv", /* SNMPERR_NOAUTH_DESPRIV */
|
||
"Bad ACL definition", /* SNMPERR_BAD_ACL */
|
||
"Bad Party definition", /* SNMPERR_BAD_PARTY */
|
||
"Session abort failure", /* SNMPERR_ABORT */
|
||
"Unknown PDU type", /* SNMPERR_UNKNOWN_PDU */
|
||
"Timeout", /* SNMPERR_TIMEOUT */
|
||
"Failure in recvfrom", /* SNMPERR_BAD_RECVFROM */
|
||
"Unable to determine contextEngineID", /* SNMPERR_BAD_ENG_ID */
|
||
"No securityName specified", /* SNMPERR_BAD_SEC_NAME */
|
||
"Unable to determine securityLevel", /* SNMPERR_BAD_SEC_LEVEL */
|
||
"ASN.1 parse error in message", /* SNMPERR_ASN_PARSE_ERR */
|
||
"Unknown security model in message", /* SNMPERR_UNKNOWN_SEC_MODEL */
|
||
"Invalid message (e.g. msgFlags)", /* SNMPERR_INVALID_MSG */
|
||
"Unknown engine ID", /* SNMPERR_UNKNOWN_ENG_ID */
|
||
"Unknown user name", /* SNMPERR_UNKNOWN_USER_NAME */
|
||
"Unsupported security level", /* SNMPERR_UNSUPPORTED_SEC_LEVEL */
|
||
"Authentication failure (incorrect password, community or key)", /* SNMPERR_AUTHENTICATION_FAILURE */
|
||
"Not in time window", /* SNMPERR_NOT_IN_TIME_WINDOW */
|
||
"Decryption error", /* SNMPERR_DECRYPTION_ERR */
|
||
"SCAPI general failure", /* SNMPERR_SC_GENERAL_FAILURE */
|
||
"SCAPI sub-system not configured", /* SNMPERR_SC_NOT_CONFIGURED */
|
||
"Key tools not available", /* SNMPERR_KT_NOT_AVAILABLE */
|
||
"Unknown Report message", /* SNMPERR_UNKNOWN_REPORT */
|
||
"USM generic error", /* SNMPERR_USM_GENERICERROR */
|
||
"USM unknown security name (no such user exists)", /* SNMPERR_USM_UNKNOWNSECURITYNAME */
|
||
"USM unsupported security level (this user has not been configured for that level of security)", /* SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL */
|
||
"USM encryption error", /* SNMPERR_USM_ENCRYPTIONERROR */
|
||
"USM authentication failure (incorrect password or key)", /* SNMPERR_USM_AUTHENTICATIONFAILURE */
|
||
"USM parse error", /* SNMPERR_USM_PARSEERROR */
|
||
"USM unknown engineID", /* SNMPERR_USM_UNKNOWNENGINEID */
|
||
"USM not in time window", /* SNMPERR_USM_NOTINTIMEWINDOW */
|
||
"USM decryption error", /* SNMPERR_USM_DECRYPTIONERROR */
|
||
"MIB not initialized", /* SNMPERR_NOMIB */
|
||
"Value out of range", /* SNMPERR_RANGE */
|
||
"Sub-id out of range", /* SNMPERR_MAX_SUBID */
|
||
"Bad sub-id in object identifier", /* SNMPERR_BAD_SUBID */
|
||
"Object identifier too long", /* SNMPERR_LONG_OID */
|
||
"Bad value name", /* SNMPERR_BAD_NAME */
|
||
"Bad value notation", /* SNMPERR_VALUE */
|
||
"Unknown Object Identifier", /* SNMPERR_UNKNOWN_OBJID */
|
||
"No PDU in snmp_send", /* SNMPERR_NULL_PDU */
|
||
"Missing variables in PDU", /* SNMPERR_NO_VARS */
|
||
"Bad variable type", /* SNMPERR_VAR_TYPE */
|
||
"Out of memory (malloc failure)", /* SNMPERR_MALLOC */
|
||
"Kerberos related error", /* SNMPERR_KRB5 */
|
||
"Protocol error", /* SNMPERR_PROTOCOL */
|
||
"OID not increasing", /* SNMPERR_OID_NONINCREASING */
|
||
"Context probe", /* SNMPERR_JUST_A_CONTEXT_PROBE */
|
||
"Configuration data found but the transport can't be configured", /* SNMPERR_TRANSPORT_NO_CONFIG */
|
||
"Transport configuration failed", /* SNMPERR_TRANSPORT_CONFIG_ERROR */
|
||
};
|
||
|
||
static const char *secLevelName[] = {
|
||
"BAD_SEC_LEVEL",
|
||
"noAuthNoPriv",
|
||
"authNoPriv",
|
||
"authPriv"
|
||
};
|
||
|
||
/*
|
||
* Multiple threads may changes these variables.
|
||
* Suggest using the Single API, which does not use Sessions.
|
||
*
|
||
* Reqid may need to be protected. Time will tell...
|
||
*
|
||
*/
|
||
/*
|
||
* MTCRITICAL_RESOURCE
|
||
*/
|
||
/*
|
||
* use token in comments to individually protect these resources
|
||
*/
|
||
struct session_list *Sessions = NULL; /* MT_LIB_SESSION */
|
||
static long Reqid = 0; /* MT_LIB_REQUESTID */
|
||
static long Msgid = 0; /* MT_LIB_MESSAGEID */
|
||
static long Sessid = 0; /* MT_LIB_SESSIONID */
|
||
static long Transid = 0; /* MT_LIB_TRANSID */
|
||
int snmp_errno = 0;
|
||
/*
|
||
* END MTCRITICAL_RESOURCE
|
||
*/
|
||
|
||
/*
|
||
* global error detail storage
|
||
*/
|
||
static char snmp_detail[192];
|
||
static int snmp_detail_f = 0;
|
||
|
||
/*
|
||
* Prototypes.
|
||
*/
|
||
static int snmp_parse(void *, netsnmp_session *, netsnmp_pdu *,
|
||
u_char *, size_t);
|
||
|
||
static void snmpv3_calc_msg_flags(int, int, u_char *);
|
||
static int snmpv3_verify_msg(netsnmp_request_list *, netsnmp_pdu *);
|
||
static int snmpv3_build(u_char ** pkt, size_t * pkt_len,
|
||
size_t * offset, netsnmp_session * session,
|
||
netsnmp_pdu *pdu);
|
||
static int snmp_parse_version(u_char *, size_t);
|
||
static int snmp_resend_request(struct session_list *slp,
|
||
netsnmp_request_list *rp,
|
||
int incr_retries);
|
||
static void register_default_handlers(void);
|
||
static struct session_list *snmp_sess_copy(netsnmp_session * pss);
|
||
|
||
/*
|
||
* return configured max message size for outgoing packets
|
||
*/
|
||
int
|
||
netsnmp_max_send_msg_size(void)
|
||
{
|
||
u_int max = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_MSG_SEND_MAX);
|
||
if (0 == max)
|
||
max = SNMP_MAX_PACKET_LEN;
|
||
else if (max < SNMP_MIN_MAX_LEN)
|
||
max = SNMP_MIN_MAX_LEN; /* minimum max size per SNMP specs */
|
||
else if (max > SNMP_MAX_PACKET_LEN)
|
||
max = SNMP_MAX_PACKET_LEN;
|
||
|
||
return max;
|
||
}
|
||
|
||
#ifndef HAVE_STRERROR
|
||
const char *
|
||
strerror(int err)
|
||
{
|
||
extern const char *sys_errlist[];
|
||
extern int sys_nerr;
|
||
|
||
if (err < 0 || err >= sys_nerr)
|
||
return "Unknown error";
|
||
return sys_errlist[err];
|
||
}
|
||
#endif
|
||
|
||
const char *
|
||
snmp_pdu_type(int type)
|
||
{
|
||
static char unknown[20];
|
||
switch(type) {
|
||
case SNMP_MSG_GET:
|
||
return "GET";
|
||
case SNMP_MSG_GETNEXT:
|
||
return "GETNEXT";
|
||
case SNMP_MSG_GETBULK:
|
||
return "GETBULK";
|
||
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
||
case SNMP_MSG_SET:
|
||
return "SET";
|
||
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
|
||
case SNMP_MSG_RESPONSE:
|
||
return "RESPONSE";
|
||
case SNMP_MSG_INFORM:
|
||
return "INFORM";
|
||
case SNMP_MSG_TRAP2:
|
||
return "TRAP2";
|
||
case SNMP_MSG_REPORT:
|
||
return "REPORT";
|
||
default:
|
||
snprintf(unknown, sizeof(unknown), "?0x%2X?", type);
|
||
return unknown;
|
||
}
|
||
}
|
||
|
||
#define DEBUGPRINTPDUTYPE(token, type) \
|
||
DEBUGDUMPSECTION(token, snmp_pdu_type(type))
|
||
|
||
long
|
||
snmp_get_next_reqid(void)
|
||
{
|
||
long retVal;
|
||
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_REQUESTID);
|
||
retVal = 1 + Reqid; /*MTCRITICAL_RESOURCE */
|
||
if (!retVal)
|
||
retVal = 2;
|
||
Reqid = retVal;
|
||
if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS))
|
||
retVal &= 0x7fff; /* mask to 15 bits */
|
||
else
|
||
retVal &= 0x7fffffff; /* mask to 31 bits */
|
||
|
||
if (!retVal) {
|
||
Reqid = retVal = 2;
|
||
}
|
||
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_REQUESTID);
|
||
return retVal;
|
||
}
|
||
|
||
long
|
||
snmp_get_next_msgid(void)
|
||
{
|
||
long retVal;
|
||
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_MESSAGEID);
|
||
retVal = 1 + Msgid; /*MTCRITICAL_RESOURCE */
|
||
if (!retVal)
|
||
retVal = 2;
|
||
Msgid = retVal;
|
||
if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS))
|
||
retVal &= 0x7fff; /* mask to 15 bits */
|
||
else
|
||
retVal &= 0x7fffffff; /* mask to 31 bits */
|
||
|
||
if (!retVal) {
|
||
Msgid = retVal = 2;
|
||
}
|
||
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_MESSAGEID);
|
||
return retVal;
|
||
}
|
||
|
||
long
|
||
snmp_get_next_sessid(void)
|
||
{
|
||
long retVal;
|
||
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSIONID);
|
||
retVal = 1 + Sessid; /*MTCRITICAL_RESOURCE */
|
||
if (!retVal)
|
||
retVal = 2;
|
||
Sessid = retVal;
|
||
if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS))
|
||
retVal &= 0x7fff; /* mask to 15 bits */
|
||
else
|
||
retVal &= 0x7fffffff; /* mask to 31 bits */
|
||
|
||
if (!retVal) {
|
||
Sessid = retVal = 2;
|
||
}
|
||
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSIONID);
|
||
return retVal;
|
||
}
|
||
|
||
long
|
||
snmp_get_next_transid(void)
|
||
{
|
||
long retVal;
|
||
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_TRANSID);
|
||
retVal = 1 + Transid; /*MTCRITICAL_RESOURCE */
|
||
if (!retVal)
|
||
retVal = 2;
|
||
Transid = retVal;
|
||
if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS))
|
||
retVal &= 0x7fff; /* mask to 15 bits */
|
||
else
|
||
retVal &= 0x7fffffff; /* mask to 31 bits */
|
||
|
||
if (!retVal) {
|
||
Transid = retVal = 2;
|
||
}
|
||
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_TRANSID);
|
||
return retVal;
|
||
}
|
||
|
||
void
|
||
snmp_perror(const char *prog_string)
|
||
{
|
||
const char *str;
|
||
int xerr;
|
||
xerr = snmp_errno; /*MTCRITICAL_RESOURCE */
|
||
str = snmp_api_errstring(xerr);
|
||
snmp_log(LOG_ERR, "%s: %s\n", prog_string, str);
|
||
}
|
||
|
||
void
|
||
snmp_set_detail(const char *detail_string)
|
||
{
|
||
if (detail_string != NULL) {
|
||
strlcpy((char *) snmp_detail, detail_string, sizeof(snmp_detail));
|
||
snmp_detail_f = 1;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* returns pointer to static data
|
||
*/
|
||
/*
|
||
* results not guaranteed in multi-threaded use
|
||
*/
|
||
const char *
|
||
snmp_api_errstring(int snmp_errnumber)
|
||
{
|
||
const char *msg = "";
|
||
static char msg_buf[SPRINT_MAX_LEN];
|
||
|
||
if (snmp_errnumber >= SNMPERR_MAX && snmp_errnumber <= SNMPERR_GENERR) {
|
||
msg = api_errors[-snmp_errnumber];
|
||
} else if (snmp_errnumber != SNMPERR_SUCCESS) {
|
||
msg = NULL;
|
||
}
|
||
if (!msg) {
|
||
snprintf(msg_buf, sizeof(msg_buf), "Unknown error: %d", snmp_errnumber);
|
||
msg_buf[sizeof(msg_buf)-1] = '\0';
|
||
} else if (snmp_detail_f) {
|
||
snprintf(msg_buf, sizeof(msg_buf), "%s (%s)", msg, snmp_detail);
|
||
msg_buf[sizeof(msg_buf)-1] = '\0';
|
||
snmp_detail_f = 0;
|
||
} else {
|
||
strlcpy(msg_buf, msg, sizeof(msg_buf));
|
||
}
|
||
|
||
return (msg_buf);
|
||
}
|
||
|
||
/*
|
||
* snmp_error - return error data
|
||
* Inputs : address of errno, address of snmp_errno, address of string
|
||
* Caller must free the string returned after use.
|
||
*/
|
||
void
|
||
snmp_error(netsnmp_session * psess,
|
||
int *p_errno, int *p_snmp_errno, char **p_str)
|
||
{
|
||
char buf[SPRINT_MAX_LEN];
|
||
int snmp_errnumber;
|
||
|
||
if (p_errno)
|
||
*p_errno = psess->s_errno;
|
||
if (p_snmp_errno)
|
||
*p_snmp_errno = psess->s_snmp_errno;
|
||
if (p_str == NULL)
|
||
return;
|
||
|
||
strcpy(buf, "");
|
||
snmp_errnumber = psess->s_snmp_errno;
|
||
if (snmp_errnumber >= SNMPERR_MAX && snmp_errnumber <= SNMPERR_GENERR) {
|
||
if (snmp_detail_f) {
|
||
snprintf(buf, sizeof(buf), "%s (%s)", api_errors[-snmp_errnumber],
|
||
snmp_detail);
|
||
buf[sizeof(buf)-1] = '\0';
|
||
snmp_detail_f = 0;
|
||
}
|
||
else
|
||
strlcpy(buf, api_errors[-snmp_errnumber], sizeof(buf));
|
||
} else {
|
||
if (snmp_errnumber) {
|
||
snprintf(buf, sizeof(buf), "Unknown Error %d", snmp_errnumber);
|
||
buf[sizeof(buf)-1] = '\0';
|
||
}
|
||
}
|
||
|
||
/*
|
||
* append a useful system errno interpretation.
|
||
*/
|
||
if (psess->s_errno) {
|
||
const char* error = strerror(psess->s_errno);
|
||
if(error == NULL)
|
||
error = "Unknown Error";
|
||
snprintf (&buf[strlen(buf)], sizeof(buf)-strlen(buf),
|
||
" (%s)", error);
|
||
}
|
||
buf[sizeof(buf)-1] = '\0';
|
||
*p_str = strdup(buf);
|
||
}
|
||
|
||
/*
|
||
* snmp_sess_error - same as snmp_error for single session API use.
|
||
*/
|
||
void
|
||
snmp_sess_error(void *sessp, int *p_errno, int *p_snmp_errno, char **p_str)
|
||
{
|
||
struct session_list *slp = (struct session_list *) sessp;
|
||
|
||
if ((slp) && (slp->session))
|
||
snmp_error(slp->session, p_errno, p_snmp_errno, p_str);
|
||
}
|
||
|
||
/*
|
||
* netsnmp_sess_log_error(): print a error stored in a session pointer
|
||
*/
|
||
void
|
||
netsnmp_sess_log_error(int priority,
|
||
const char *prog_string, netsnmp_session * ss)
|
||
{
|
||
char *err;
|
||
snmp_error(ss, NULL, NULL, &err);
|
||
snmp_log(priority, "%s: %s\n", prog_string, err);
|
||
SNMP_FREE(err);
|
||
}
|
||
|
||
/*
|
||
* snmp_sess_perror(): print a error stored in a session pointer
|
||
*/
|
||
void
|
||
snmp_sess_perror(const char *prog_string, netsnmp_session * ss)
|
||
{
|
||
netsnmp_sess_log_error(LOG_ERR, prog_string, ss);
|
||
}
|
||
|
||
long int netsnmp_random(void)
|
||
{
|
||
#if defined(HAVE_RANDOM)
|
||
return random();
|
||
#elif defined(HAVE_LRAND48)
|
||
return lrand48();
|
||
#elif defined(HAVE_RAND)
|
||
return rand();
|
||
#else
|
||
#error "Neither random(), nor lrand48() nor rand() are available"
|
||
#endif
|
||
}
|
||
|
||
void netsnmp_srandom(unsigned int seed)
|
||
{
|
||
#if defined(HAVE_SRANDOM)
|
||
srandom(seed);
|
||
#elif defined(HAVE_SRAND48)
|
||
srand48(seed);
|
||
#elif defined(HAVE_SRAND)
|
||
srand(seed);
|
||
#else
|
||
#error "Neither srandom(), nor srand48() nor srand() are available"
|
||
#endif
|
||
}
|
||
|
||
/*
|
||
* Primordial SNMP library initialization.
|
||
* Initializes mutex locks.
|
||
* Invokes minimum required initialization for displaying MIB objects.
|
||
* Gets initial request ID for all transactions,
|
||
* and finds which port SNMP over UDP uses.
|
||
* SNMP over AppleTalk is not currently supported.
|
||
*
|
||
* Warning: no debug messages here.
|
||
*/
|
||
static char _init_snmp_init_done = 0;
|
||
static void
|
||
_init_snmp(void)
|
||
{
|
||
|
||
struct timeval tv;
|
||
long tmpReqid, tmpMsgid;
|
||
|
||
if (_init_snmp_init_done)
|
||
return;
|
||
_init_snmp_init_done = 1;
|
||
Reqid = 1;
|
||
|
||
snmp_res_init(); /* initialize the mt locking structures */
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
netsnmp_init_mib_internals();
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
netsnmp_tdomain_init();
|
||
|
||
gettimeofday(&tv, (struct timezone *) 0);
|
||
/*
|
||
* Now = tv;
|
||
*/
|
||
|
||
/*
|
||
* get pseudo-random values for request ID and message ID
|
||
*/
|
||
netsnmp_srandom((unsigned)(tv.tv_sec ^ tv.tv_usec));
|
||
tmpReqid = netsnmp_random();
|
||
tmpMsgid = netsnmp_random();
|
||
|
||
/*
|
||
* don't allow zero value to repeat init
|
||
*/
|
||
if (tmpReqid == 0)
|
||
tmpReqid = 1;
|
||
if (tmpMsgid == 0)
|
||
tmpMsgid = 1;
|
||
Reqid = tmpReqid;
|
||
Msgid = tmpMsgid;
|
||
|
||
netsnmp_register_default_domain("snmp", "udp udp6");
|
||
netsnmp_register_default_domain("snmptrap", "udp udp6");
|
||
|
||
netsnmp_register_default_target("snmp", "udp", ":161");
|
||
netsnmp_register_default_target("snmp", "tcp", ":161");
|
||
netsnmp_register_default_target("snmp", "udp6", ":161");
|
||
netsnmp_register_default_target("snmp", "tcp6", ":161");
|
||
netsnmp_register_default_target("snmp", "dtlsudp", ":10161");
|
||
netsnmp_register_default_target("snmp", "tlstcp", ":10161");
|
||
netsnmp_register_default_target("snmp", "ipx", "/36879");
|
||
|
||
netsnmp_register_default_target("snmptrap", "udp", ":162");
|
||
netsnmp_register_default_target("snmptrap", "tcp", ":162");
|
||
netsnmp_register_default_target("snmptrap", "udp6", ":162");
|
||
netsnmp_register_default_target("snmptrap", "tcp6", ":162");
|
||
netsnmp_register_default_target("snmptrap", "dtlsudp", ":10162");
|
||
netsnmp_register_default_target("snmptrap", "tlstcp", ":10162");
|
||
netsnmp_register_default_target("snmptrap", "ipx", "/36880");
|
||
|
||
netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_HEX_OUTPUT_LENGTH, 16);
|
||
netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_RETRIES,
|
||
DEFAULT_RETRIES);
|
||
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_MIB_ERRORS, 1);
|
||
|
||
#ifdef NETSNMP_USE_REVERSE_ASNENCODING
|
||
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_REVERSE_ENCODE,
|
||
NETSNMP_DEFAULT_ASNENCODING_DIRECTION);
|
||
#endif
|
||
}
|
||
|
||
/*
|
||
* Initializes the session structure.
|
||
* May perform one time minimal library initialization.
|
||
* No MIB file processing is done via this call.
|
||
*/
|
||
void
|
||
snmp_sess_init(netsnmp_session * session)
|
||
{
|
||
_init_snmp();
|
||
|
||
/*
|
||
* initialize session to default values
|
||
*/
|
||
|
||
memset(session, 0, sizeof(netsnmp_session));
|
||
session->timeout = SNMP_DEFAULT_TIMEOUT;
|
||
session->retries = SNMP_DEFAULT_RETRIES;
|
||
session->version = SNMP_DEFAULT_VERSION;
|
||
session->securityModel = SNMP_DEFAULT_SECMODEL;
|
||
session->rcvMsgMaxSize = SNMP_MAX_MSG_SIZE;
|
||
session->sndMsgMaxSize = netsnmp_max_send_msg_size();
|
||
session->flags |= SNMP_FLAGS_DONT_PROBE;
|
||
}
|
||
|
||
|
||
static void
|
||
register_default_handlers(void)
|
||
{
|
||
netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "dumpPacket",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DUMP_PACKET);
|
||
netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "reverseEncodeBER",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_REVERSE_ENCODE);
|
||
netsnmp_ds_register_config(ASN_INTEGER, "snmp", "defaultPort",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DEFAULT_PORT);
|
||
#ifndef NETSNMP_FEATURE_REMOVE_RUNTIME_DISABLE_VERSION
|
||
netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "disableSNMPv3",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_V3);
|
||
#endif /* NETSNMP_FEATURE_REMOVE_RUNTIME_DISABLE_VERSION */
|
||
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
|
||
#ifndef NETSNMP_FEATURE_REMOVE_RUNTIME_DISABLE_VERSION
|
||
#if !defined(NETSNMP_DISABLE_SNMPV1)
|
||
netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "disableSNMPv1",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_V1);
|
||
#endif
|
||
#if !defined(NETSNMP_DISABLE_SNMPV2C)
|
||
netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "disableSNMPv2c",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_V2c);
|
||
#endif
|
||
#endif /* NETSNMP_FEATURE_REMOVE_RUNTIME_DISABLE_VERSION */
|
||
netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defCommunity",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_COMMUNITY);
|
||
#endif /* !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) */
|
||
netsnmp_ds_register_premib(ASN_BOOLEAN, "snmp", "noTokenWarnings",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_NO_TOKEN_WARNINGS);
|
||
netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "noRangeCheck",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_CHECK_RANGE);
|
||
netsnmp_ds_register_premib(ASN_OCTET_STR, "snmp", "persistentDir",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PERSISTENT_DIR);
|
||
netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "tempFilePattern",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_TEMP_FILE_PATTERN);
|
||
netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "noDisplayHint",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_NO_DISPLAY_HINT);
|
||
netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "16bitIDs",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS);
|
||
netsnmp_ds_register_premib(ASN_OCTET_STR, "snmp", "clientaddr",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_CLIENT_ADDR);
|
||
netsnmp_ds_register_premib(ASN_BOOLEAN, "snmp", "clientaddrUsesPort",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_CLIENT_ADDR_USES_PORT);
|
||
netsnmp_ds_register_config(ASN_INTEGER, "snmp", "serverSendBuf",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SERVERSENDBUF);
|
||
netsnmp_ds_register_config(ASN_INTEGER, "snmp", "serverRecvBuf",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SERVERRECVBUF);
|
||
netsnmp_ds_register_config(ASN_INTEGER, "snmp", "clientSendBuf",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_CLIENTSENDBUF);
|
||
netsnmp_ds_register_config(ASN_INTEGER, "snmp", "clientRecvBuf",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_CLIENTRECVBUF);
|
||
netsnmp_ds_register_config(ASN_INTEGER, "snmp", "sendMessageMaxSize",
|
||
NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_MSG_SEND_MAX);
|
||
netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "noPersistentLoad",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD);
|
||
netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "noPersistentSave",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE);
|
||
netsnmp_ds_register_config(ASN_BOOLEAN, "snmp",
|
||
"noContextEngineIDDiscovery",
|
||
NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_NO_DISCOVERY);
|
||
netsnmp_ds_register_config(ASN_INTEGER, "snmp", "timeout",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_TIMEOUT);
|
||
netsnmp_ds_register_config(ASN_INTEGER, "snmp", "retries",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_RETRIES);
|
||
netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "outputPrecision",
|
||
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OUTPUT_PRECISION);
|
||
|
||
|
||
netsnmp_register_service_handlers();
|
||
}
|
||
|
||
static int init_snmp_init_done = 0; /* To prevent double init's. */
|
||
/**
|
||
* Calls the functions to do config file loading and mib module parsing
|
||
* in the correct order.
|
||
*
|
||
* @param type label for the config file "type"
|
||
*
|
||
* @return void
|
||
*
|
||
* @see init_agent
|
||
*/
|
||
void
|
||
init_snmp(const char *type)
|
||
{
|
||
if (init_snmp_init_done) {
|
||
return;
|
||
}
|
||
|
||
init_snmp_init_done = 1;
|
||
|
||
/*
|
||
* make the type available everywhere else
|
||
*/
|
||
if (type && !netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_APPTYPE)) {
|
||
netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_APPTYPE, type);
|
||
}
|
||
|
||
_init_snmp();
|
||
|
||
/*
|
||
* set our current locale properly to initialize isprint() type functions
|
||
*/
|
||
#ifdef HAVE_SETLOCALE
|
||
setlocale(LC_CTYPE, "");
|
||
#endif
|
||
|
||
snmp_debug_init(); /* should be done first, to turn on debugging ASAP */
|
||
netsnmp_container_init_list();
|
||
init_callbacks();
|
||
init_snmp_logging();
|
||
snmp_init_statistics();
|
||
register_mib_handlers();
|
||
register_default_handlers();
|
||
init_snmp_transport();
|
||
init_snmpv3(type);
|
||
init_snmp_alarm();
|
||
init_snmp_enum(type);
|
||
init_vacm();
|
||
#if defined(NETSNMP_USE_OPENSSL) && defined(HAVE_LIBSSL) && NETSNMP_TRANSPORT_TLSBASE_DOMAIN
|
||
netsnmp_certs_init();
|
||
#endif
|
||
#ifdef DNSSEC_LOCAL_VALIDATION
|
||
netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "dnssecWarnOnly",
|
||
NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_DNSSEC_WARN_ONLY);
|
||
#endif
|
||
|
||
read_premib_configs();
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
netsnmp_init_mib();
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
|
||
read_configs();
|
||
|
||
} /* end init_snmp() */
|
||
|
||
/**
|
||
* set a flag indicating that the persistent store needs to be saved.
|
||
*/
|
||
void
|
||
snmp_store_needed(const char *type)
|
||
{
|
||
DEBUGMSGTL(("snmp_store", "setting needed flag...\n"));
|
||
_snmp_store_needed = 1;
|
||
}
|
||
|
||
void
|
||
snmp_store_if_needed(void)
|
||
{
|
||
if (0 == _snmp_store_needed)
|
||
return;
|
||
|
||
DEBUGMSGTL(("snmp_store", "store needed...\n"));
|
||
snmp_store(netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_APPTYPE));
|
||
_snmp_store_needed = 0;
|
||
}
|
||
|
||
void
|
||
snmp_store(const char *type)
|
||
{
|
||
DEBUGMSGTL(("snmp_store", "storing stuff...\n"));
|
||
snmp_save_persistent(type);
|
||
snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA, NULL);
|
||
snmp_clean_persistent(type);
|
||
}
|
||
|
||
|
||
/**
|
||
* Shuts down the application, saving any needed persistent storage,
|
||
* and appropriate clean up.
|
||
*
|
||
* @param type Label for the config file "type" used
|
||
*
|
||
* @return void
|
||
*/
|
||
void
|
||
snmp_shutdown(const char *type)
|
||
{
|
||
snmp_store(type);
|
||
snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN, NULL);
|
||
shutdown_snmp_logging();
|
||
snmp_alarm_unregister_all();
|
||
snmp_close_sessions();
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
shutdown_mib();
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
#if defined(NETSNMP_USE_OPENSSL) && defined(HAVE_LIBSSL) && NETSNMP_TRANSPORT_TLSBASE_DOMAIN
|
||
netsnmp_certs_shutdown();
|
||
#endif
|
||
#if !defined(NETSNMP_FEATURE_REMOVE_FILTER_SOURCE)
|
||
netsnmp_transport_filter_cleanup();
|
||
#endif
|
||
unregister_all_config_handlers();
|
||
netsnmp_container_free_list();
|
||
clear_sec_mod();
|
||
clear_snmp_enum();
|
||
netsnmp_clear_tdomain_list();
|
||
clear_callback();
|
||
netsnmp_ds_shutdown();
|
||
netsnmp_clear_default_target();
|
||
netsnmp_clear_default_domain();
|
||
shutdown_secmod();
|
||
shutdown_snmp_transport();
|
||
shutdown_data_list();
|
||
snmp_debug_shutdown(); /* should be done last */
|
||
|
||
init_snmp_init_done = 0;
|
||
_init_snmp_init_done = 0;
|
||
}
|
||
|
||
/*
|
||
* inserts session into session list
|
||
*/
|
||
void snmp_session_insert(struct session_list *slp)
|
||
{
|
||
if (NULL == slp)
|
||
return;
|
||
|
||
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
slp->next = Sessions;
|
||
Sessions = slp;
|
||
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
}
|
||
|
||
/*
|
||
* Sets up the session with the snmp_session information provided by the user.
|
||
* Then opens and binds the necessary low-level transport. A handle to the
|
||
* created session is returned (this is NOT the same as the pointer passed to
|
||
* snmp_open()). On any error, NULL is returned and snmp_errno is set to the
|
||
* appropriate error code.
|
||
*/
|
||
netsnmp_session *
|
||
snmp_open(netsnmp_session *session)
|
||
{
|
||
struct session_list *slp;
|
||
slp = (struct session_list *) snmp_sess_open(session);
|
||
if (!slp) {
|
||
return NULL;
|
||
}
|
||
|
||
snmp_session_insert(slp);
|
||
|
||
return (slp->session);
|
||
}
|
||
|
||
/*
|
||
* extended open
|
||
*/
|
||
netsnmp_feature_child_of(snmp_open_ex, netsnmp_unused)
|
||
#ifndef NETSNMP_FEATURE_REMOVE_SNMP_OPEN_EX
|
||
netsnmp_session *
|
||
snmp_open_ex(netsnmp_session *session,
|
||
int (*fpre_parse) (netsnmp_session *, netsnmp_transport *,
|
||
void *, int),
|
||
int (*fparse) (netsnmp_session *, netsnmp_pdu *, u_char *,
|
||
size_t),
|
||
int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *, int),
|
||
|
||
int (*fbuild) (netsnmp_session *, netsnmp_pdu *, u_char *,
|
||
size_t *),
|
||
int (*frbuild) (netsnmp_session *, netsnmp_pdu *,
|
||
u_char **, size_t *, size_t *),
|
||
int (*fcheck) (u_char *, size_t)
|
||
)
|
||
{
|
||
struct session_list *slp;
|
||
slp = (struct session_list *) snmp_sess_open(session);
|
||
if (!slp) {
|
||
return NULL;
|
||
}
|
||
slp->internal->hook_pre = fpre_parse;
|
||
slp->internal->hook_parse = fparse;
|
||
slp->internal->hook_post = fpost_parse;
|
||
slp->internal->hook_build = fbuild;
|
||
slp->internal->hook_realloc_build = frbuild;
|
||
slp->internal->check_packet = fcheck;
|
||
|
||
snmp_session_insert(slp);
|
||
|
||
return (slp->session);
|
||
}
|
||
#endif /* NETSNMP_FEATURE_REMOVE_SNMP_OPEN_EX */
|
||
|
||
static struct session_list *
|
||
_sess_copy(netsnmp_session * in_session)
|
||
{
|
||
struct session_list *slp;
|
||
struct snmp_internal_session *isp;
|
||
netsnmp_session *session;
|
||
struct snmp_secmod_def *sptr;
|
||
char *cp;
|
||
u_char *ucp;
|
||
|
||
in_session->s_snmp_errno = 0;
|
||
in_session->s_errno = 0;
|
||
|
||
/*
|
||
* Copy session structure and link into list
|
||
*/
|
||
slp = (struct session_list *) calloc(1, sizeof(struct session_list));
|
||
if (slp == NULL) {
|
||
in_session->s_snmp_errno = SNMPERR_MALLOC;
|
||
return (NULL);
|
||
}
|
||
|
||
slp->transport = NULL;
|
||
|
||
isp = (struct snmp_internal_session *)calloc(1, sizeof(struct snmp_internal_session));
|
||
|
||
if (isp == NULL) {
|
||
snmp_sess_close(slp);
|
||
in_session->s_snmp_errno = SNMPERR_MALLOC;
|
||
return (NULL);
|
||
}
|
||
|
||
slp->internal = isp;
|
||
slp->session = (netsnmp_session *)malloc(sizeof(netsnmp_session));
|
||
if (slp->session == NULL) {
|
||
snmp_sess_close(slp);
|
||
in_session->s_snmp_errno = SNMPERR_MALLOC;
|
||
return (NULL);
|
||
}
|
||
memmove(slp->session, in_session, sizeof(netsnmp_session));
|
||
session = slp->session;
|
||
|
||
/*
|
||
* zero out pointers so if we have to free the session we wont free mem
|
||
* owned by in_session
|
||
*/
|
||
session->localname = NULL;
|
||
session->peername = NULL;
|
||
session->community = NULL;
|
||
session->contextEngineID = NULL;
|
||
session->contextName = NULL;
|
||
session->securityEngineID = NULL;
|
||
session->securityName = NULL;
|
||
session->securityAuthProto = NULL;
|
||
session->securityPrivProto = NULL;
|
||
/*
|
||
* session now points to the new structure that still contains pointers to
|
||
* data allocated elsewhere. Some of this data is copied to space malloc'd
|
||
* here, and the pointer replaced with the new one.
|
||
*/
|
||
|
||
if (in_session->peername != NULL) {
|
||
session->peername =
|
||
netsnmp_strdup_and_null((u_char*)in_session->peername,
|
||
strlen(in_session->peername));
|
||
if (session->peername == NULL) {
|
||
snmp_sess_close(slp);
|
||
in_session->s_snmp_errno = SNMPERR_MALLOC;
|
||
return (NULL);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Fill in defaults if necessary
|
||
*/
|
||
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
|
||
if (in_session->community_len != SNMP_DEFAULT_COMMUNITY_LEN) {
|
||
ucp = (u_char *) malloc(in_session->community_len);
|
||
if (ucp != NULL)
|
||
memmove(ucp, in_session->community, in_session->community_len);
|
||
} else {
|
||
if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_COMMUNITY)) != NULL) {
|
||
session->community_len = strlen(cp);
|
||
ucp = (u_char *) malloc(session->community_len);
|
||
if (ucp)
|
||
memmove(ucp, cp, session->community_len);
|
||
} else {
|
||
#ifdef NETSNMP_NO_ZEROLENGTH_COMMUNITY
|
||
session->community_len = strlen(DEFAULT_COMMUNITY);
|
||
ucp = (u_char *) malloc(session->community_len);
|
||
if (ucp)
|
||
memmove(ucp, DEFAULT_COMMUNITY, session->community_len);
|
||
#else
|
||
ucp = (u_char *) strdup("");
|
||
#endif
|
||
}
|
||
}
|
||
|
||
if (ucp == NULL) {
|
||
snmp_sess_close(slp);
|
||
in_session->s_snmp_errno = SNMPERR_MALLOC;
|
||
return (NULL);
|
||
}
|
||
session->community = ucp; /* replace pointer with pointer to new data */
|
||
#endif
|
||
|
||
if (session->securityLevel <= 0) {
|
||
session->securityLevel =
|
||
netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECLEVEL);
|
||
}
|
||
|
||
if (in_session->securityEngineIDLen > 0) {
|
||
ucp = (u_char *) malloc(in_session->securityEngineIDLen);
|
||
if (ucp == NULL) {
|
||
snmp_sess_close(slp);
|
||
in_session->s_snmp_errno = SNMPERR_MALLOC;
|
||
return (NULL);
|
||
}
|
||
memmove(ucp, in_session->securityEngineID,
|
||
in_session->securityEngineIDLen);
|
||
session->securityEngineID = ucp;
|
||
|
||
}
|
||
|
||
if (in_session->contextEngineIDLen > 0) {
|
||
ucp = (u_char *) malloc(in_session->contextEngineIDLen);
|
||
if (ucp == NULL) {
|
||
snmp_sess_close(slp);
|
||
in_session->s_snmp_errno = SNMPERR_MALLOC;
|
||
return (NULL);
|
||
}
|
||
memmove(ucp, in_session->contextEngineID,
|
||
in_session->contextEngineIDLen);
|
||
session->contextEngineID = ucp;
|
||
} else if (in_session->securityEngineIDLen > 0) {
|
||
/*
|
||
* default contextEngineID to securityEngineIDLen if defined
|
||
*/
|
||
ucp = (u_char *) malloc(in_session->securityEngineIDLen);
|
||
if (ucp == NULL) {
|
||
snmp_sess_close(slp);
|
||
in_session->s_snmp_errno = SNMPERR_MALLOC;
|
||
return (NULL);
|
||
}
|
||
memmove(ucp, in_session->securityEngineID,
|
||
in_session->securityEngineIDLen);
|
||
session->contextEngineID = ucp;
|
||
session->contextEngineIDLen = in_session->securityEngineIDLen;
|
||
}
|
||
|
||
if (in_session->contextName) {
|
||
session->contextName = strdup(in_session->contextName);
|
||
if (session->contextName == NULL) {
|
||
snmp_sess_close(slp);
|
||
return (NULL);
|
||
}
|
||
session->contextNameLen = in_session->contextNameLen;
|
||
} else {
|
||
if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_CONTEXT)) != NULL)
|
||
cp = strdup(cp);
|
||
else
|
||
cp = strdup(SNMP_DEFAULT_CONTEXT);
|
||
if (cp == NULL) {
|
||
snmp_sess_close(slp);
|
||
return (NULL);
|
||
}
|
||
session->contextName = cp;
|
||
session->contextNameLen = strlen(cp);
|
||
}
|
||
|
||
if (in_session->securityName) {
|
||
session->securityName = strdup(in_session->securityName);
|
||
if (session->securityName == NULL) {
|
||
snmp_sess_close(slp);
|
||
return (NULL);
|
||
}
|
||
} else if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_SECNAME)) != NULL) {
|
||
cp = strdup(cp);
|
||
if (cp == NULL) {
|
||
snmp_sess_close(slp);
|
||
return (NULL);
|
||
}
|
||
session->securityName = cp;
|
||
session->securityNameLen = strlen(cp);
|
||
}
|
||
|
||
if (session->retries == SNMP_DEFAULT_RETRIES) {
|
||
int retry = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_RETRIES);
|
||
if (retry < 0)
|
||
session->retries = DEFAULT_RETRIES;
|
||
else
|
||
session->retries = retry;
|
||
}
|
||
if (session->timeout == SNMP_DEFAULT_TIMEOUT) {
|
||
int timeout = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_TIMEOUT);
|
||
if (timeout <= 0)
|
||
session->timeout = DEFAULT_TIMEOUT;
|
||
else
|
||
session->timeout = timeout * ONE_SEC;
|
||
}
|
||
session->sessid = snmp_get_next_sessid();
|
||
|
||
snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SESSION_INIT,
|
||
session);
|
||
|
||
if ((sptr = find_sec_mod(session->securityModel)) != NULL) {
|
||
/*
|
||
* security module specific copying
|
||
*/
|
||
if (sptr->session_setup) {
|
||
int ret = (*sptr->session_setup) (in_session, session);
|
||
if (ret != SNMPERR_SUCCESS) {
|
||
snmp_sess_close(slp);
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* security module specific opening
|
||
*/
|
||
if (sptr->session_open) {
|
||
int ret = (*sptr->session_open) (session);
|
||
if (ret != SNMPERR_SUCCESS) {
|
||
snmp_sess_close(slp);
|
||
return NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Anything below this point should only be done if the transport
|
||
had no say in the matter */
|
||
if (session->securityLevel == 0)
|
||
session->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
|
||
|
||
return (slp);
|
||
}
|
||
|
||
static struct session_list *
|
||
snmp_sess_copy(netsnmp_session * pss)
|
||
{
|
||
struct session_list *psl;
|
||
psl = _sess_copy(pss);
|
||
if (!psl) {
|
||
if (!pss->s_snmp_errno) {
|
||
pss->s_snmp_errno = SNMPERR_GENERR;
|
||
}
|
||
SET_SNMP_ERROR(pss->s_snmp_errno);
|
||
}
|
||
return psl;
|
||
}
|
||
|
||
#ifndef NETSNMP_FEATURE_REMOVE_SNMPV3_PROBE_CONTEXTENGINEID_RFC5343
|
||
/**
|
||
* probe for engineID using RFC 5343 probing mechanisms
|
||
*
|
||
* Designed to be a callback for within a security model's probe_engineid hook.
|
||
* Since it's likely multiple security models won't have engineIDs to
|
||
* probe for then this function is a callback likely to be used by
|
||
* multiple future security models. E.G. both SSH and DTLS.
|
||
*/
|
||
int
|
||
snmpv3_probe_contextEngineID_rfc5343(void *slp, netsnmp_session *session) {
|
||
netsnmp_pdu *pdu = NULL, *response = NULL;
|
||
static oid snmpEngineIDoid[] = { 1,3,6,1,6,3,10,2,1,1,0};
|
||
static size_t snmpEngineIDoid_len = 11;
|
||
|
||
static char probeEngineID[] = { (char)0x80, 0, 0, 0, 6 };
|
||
static size_t probeEngineID_len = sizeof(probeEngineID);
|
||
|
||
int status;
|
||
|
||
pdu = snmp_pdu_create(SNMP_MSG_GET);
|
||
if (!pdu)
|
||
return SNMP_ERR_GENERR;
|
||
pdu->version = SNMP_VERSION_3;
|
||
/* don't require a securityName */
|
||
if (session->securityName) {
|
||
pdu->securityName = strdup(session->securityName);
|
||
pdu->securityNameLen = strlen(pdu->securityName);
|
||
}
|
||
pdu->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
|
||
pdu->securityModel = session->securityModel;
|
||
pdu->contextEngineID = netsnmp_memdup(probeEngineID, probeEngineID_len);
|
||
if (!pdu->contextEngineID) {
|
||
snmp_log(LOG_ERR, "failed to clone memory for rfc5343 probe\n");
|
||
snmp_free_pdu(pdu);
|
||
return SNMP_ERR_GENERR;
|
||
}
|
||
pdu->contextEngineIDLen = probeEngineID_len;
|
||
|
||
snmp_add_null_var(pdu, snmpEngineIDoid, snmpEngineIDoid_len);
|
||
|
||
DEBUGMSGTL(("snmp_api", "probing for engineID using rfc5343 methods...\n"));
|
||
session->flags |= SNMP_FLAGS_DONT_PROBE; /* prevent recursion */
|
||
status = snmp_sess_synch_response(slp, pdu, &response);
|
||
|
||
if ((response == NULL) || (status != STAT_SUCCESS)) {
|
||
snmp_log(LOG_ERR, "failed rfc5343 contextEngineID probing\n");
|
||
return SNMP_ERR_GENERR;
|
||
}
|
||
|
||
/* check that the response makes sense */
|
||
if (NULL != response->variables &&
|
||
NULL != response->variables->name &&
|
||
snmp_oid_compare(response->variables->name,
|
||
response->variables->name_length,
|
||
snmpEngineIDoid, snmpEngineIDoid_len) == 0 &&
|
||
ASN_OCTET_STR == response->variables->type &&
|
||
NULL != response->variables->val.string &&
|
||
response->variables->val_len > 0) {
|
||
session->contextEngineID =
|
||
netsnmp_memdup(response->variables->val.string,
|
||
response->variables->val_len);
|
||
if (!session->contextEngineID) {
|
||
snmp_log(LOG_ERR, "failed rfc5343 contextEngineID probing: memory allocation failed\n");
|
||
return SNMP_ERR_GENERR;
|
||
}
|
||
|
||
/* technically there likely isn't a securityEngineID but just
|
||
in case anyone goes looking we might as well have one */
|
||
session->securityEngineID =
|
||
netsnmp_memdup(response->variables->val.string,
|
||
response->variables->val_len);
|
||
if (!session->securityEngineID) {
|
||
snmp_log(LOG_ERR, "failed rfc5343 securityEngineID probing: memory allocation failed\n");
|
||
return SNMP_ERR_GENERR;
|
||
}
|
||
|
||
session->securityEngineIDLen = session->contextEngineIDLen =
|
||
response->variables->val_len;
|
||
|
||
if (snmp_get_do_debugging()) {
|
||
size_t i;
|
||
DEBUGMSGTL(("snmp_sess_open",
|
||
" probe found engineID: "));
|
||
for (i = 0; i < session->securityEngineIDLen; i++)
|
||
DEBUGMSG(("snmp_sess_open", "%02x",
|
||
session->securityEngineID[i]));
|
||
DEBUGMSG(("snmp_sess_open", "\n"));
|
||
}
|
||
}
|
||
return SNMPERR_SUCCESS;
|
||
}
|
||
#endif /* NETSNMP_FEATURE_REMOVE_SNMPV3_PROBE_CONTEXTENGINEID_RFC5343 */
|
||
|
||
|
||
/**
|
||
* probe for peer engineID
|
||
*
|
||
* @param slp session list pointer.
|
||
* @param in_session session for errors
|
||
*
|
||
* @note
|
||
* - called by _sess_open(), snmp_sess_add_ex()
|
||
* - in_session is the user supplied session provided to those functions.
|
||
* - the first session in slp should the internal allocated copy of in_session
|
||
*
|
||
* @return 0 : error
|
||
* @return 1 : ok
|
||
*
|
||
*/
|
||
int
|
||
snmpv3_engineID_probe(struct session_list *slp,
|
||
netsnmp_session * in_session)
|
||
{
|
||
netsnmp_session *session;
|
||
int status;
|
||
struct snmp_secmod_def *sptr = NULL;
|
||
|
||
if (slp == NULL || slp->session == NULL) {
|
||
return 0;
|
||
}
|
||
|
||
session = slp->session;
|
||
netsnmp_assert_or_return(session != NULL, 0);
|
||
sptr = find_sec_mod(session->securityModel);
|
||
|
||
/*
|
||
* If we are opening a V3 session and we don't know engineID we must probe
|
||
* it -- this must be done after the session is created and inserted in the
|
||
* list so that the response can handled correctly.
|
||
*/
|
||
|
||
if (session->version == SNMP_VERSION_3 &&
|
||
(0 == (session->flags & SNMP_FLAGS_DONT_PROBE))) {
|
||
if (NULL != sptr && NULL != sptr->probe_engineid) {
|
||
DEBUGMSGTL(("snmp_api", "probing for engineID using security model callback...\n"));
|
||
/* security model specific mechanism of determining engineID */
|
||
status = (*sptr->probe_engineid) (slp, in_session);
|
||
if (status != SNMPERR_SUCCESS)
|
||
return 0;
|
||
} else {
|
||
/* XXX: default to the default RFC5343 contextEngineID Probe? */
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* see if there is a hook to call now that we're done probing for an
|
||
* engineID
|
||
*/
|
||
if (sptr && sptr->post_probe_engineid) {
|
||
status = (*sptr->post_probe_engineid)(slp, in_session);
|
||
if (status != SNMPERR_SUCCESS)
|
||
return 0;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
/*******************************************************************-o-******
|
||
* netsnmp_sess_config_transport
|
||
*
|
||
* Parameters:
|
||
* *in_session
|
||
* *in_transport
|
||
*
|
||
* Returns:
|
||
* SNMPERR_SUCCESS - Yay
|
||
* SNMPERR_GENERR - Generic Error
|
||
* SNMPERR_TRANSPORT_CONFIG_ERROR - Transport rejected config
|
||
* SNMPERR_TRANSPORT_NO_CONFIG - Transport can't config
|
||
*/
|
||
int
|
||
netsnmp_sess_config_transport(netsnmp_container *transport_configuration,
|
||
netsnmp_transport *transport)
|
||
{
|
||
/* Optional supplimental transport configuration information and
|
||
final call to actually open the transport */
|
||
if (transport_configuration) {
|
||
DEBUGMSGTL(("snmp_sess", "configuring transport\n"));
|
||
if (transport->f_config) {
|
||
netsnmp_iterator *iter;
|
||
netsnmp_transport_config *config_data;
|
||
int ret = 0;
|
||
|
||
iter = CONTAINER_ITERATOR(transport_configuration);
|
||
if (NULL == iter) {
|
||
return SNMPERR_GENERR;
|
||
}
|
||
|
||
for(config_data = (netsnmp_transport_config*)ITERATOR_FIRST(iter); config_data;
|
||
config_data = (netsnmp_transport_config*)ITERATOR_NEXT(iter)) {
|
||
ret = transport->f_config(transport, config_data->key,
|
||
config_data->value);
|
||
if (ret)
|
||
break;
|
||
}
|
||
ITERATOR_RELEASE(iter);
|
||
if (ret)
|
||
return SNMPERR_TRANSPORT_CONFIG_ERROR;
|
||
} else {
|
||
return SNMPERR_TRANSPORT_NO_CONFIG;
|
||
}
|
||
}
|
||
return SNMPERR_SUCCESS;
|
||
}
|
||
|
||
|
||
/**
|
||
* Copies configuration from the session and calls f_open
|
||
* This function copies any configuration stored in the session
|
||
* pointer to the transport if it has a f_config pointer and then
|
||
* calls the transport's f_open function to actually open the
|
||
* connection.
|
||
*
|
||
* @param in_session A pointer to the session that config information is in.
|
||
* @param transport A pointer to the transport to config/open.
|
||
*
|
||
* @return SNMPERR_SUCCESS : on success
|
||
*/
|
||
|
||
/*******************************************************************-o-******
|
||
* netsnmp_sess_config_transport
|
||
*
|
||
* Parameters:
|
||
* *in_session
|
||
* *in_transport
|
||
*
|
||
* Returns:
|
||
* SNMPERR_SUCCESS - Yay
|
||
* SNMPERR_GENERR - Generic Error
|
||
* SNMPERR_TRANSPORT_CONFIG_ERROR - Transport rejected config
|
||
* SNMPERR_TRANSPORT_NO_CONFIG - Transport can't config
|
||
*/
|
||
int
|
||
netsnmp_sess_config_and_open_transport(netsnmp_session *in_session,
|
||
netsnmp_transport *transport)
|
||
{
|
||
int rc;
|
||
|
||
DEBUGMSGTL(("snmp_sess", "opening transport: %x\n", transport->flags & NETSNMP_TRANSPORT_FLAG_OPENED));
|
||
|
||
/* don't double open */
|
||
if (transport->flags & NETSNMP_TRANSPORT_FLAG_OPENED)
|
||
return SNMPERR_SUCCESS;
|
||
|
||
if ((rc = netsnmp_sess_config_transport(in_session->transport_configuration,
|
||
transport)) != SNMPERR_SUCCESS) {
|
||
in_session->s_snmp_errno = rc;
|
||
in_session->s_errno = 0;
|
||
return rc;
|
||
}
|
||
|
||
if (transport->f_open)
|
||
transport = transport->f_open(transport);
|
||
|
||
if (transport == NULL) {
|
||
DEBUGMSGTL(("snmp_sess", "couldn't interpret peername\n"));
|
||
in_session->s_snmp_errno = SNMPERR_BAD_ADDRESS;
|
||
in_session->s_errno = errno;
|
||
snmp_set_detail(in_session->peername);
|
||
return SNMPERR_BAD_ADDRESS;
|
||
}
|
||
|
||
/** if transport has a max size, make sure session is the same (or less) */
|
||
if (in_session->rcvMsgMaxSize > transport->msgMaxSize) {
|
||
DEBUGMSGTL(("snmp_sess",
|
||
"limiting session rcv size to transport max\n"));
|
||
in_session->rcvMsgMaxSize = transport->msgMaxSize;
|
||
}
|
||
|
||
if (in_session->sndMsgMaxSize > transport->msgMaxSize) {
|
||
DEBUGMSGTL(("snmp_sess",
|
||
"limiting session snd size to transport max\n"));
|
||
in_session->sndMsgMaxSize = transport->msgMaxSize;
|
||
}
|
||
|
||
transport->flags |= NETSNMP_TRANSPORT_FLAG_OPENED;
|
||
DEBUGMSGTL(("snmp_sess", "done opening transport: %x\n", transport->flags & NETSNMP_TRANSPORT_FLAG_OPENED));
|
||
return SNMPERR_SUCCESS;
|
||
}
|
||
|
||
/*******************************************************************-o-******
|
||
* snmp_sess_open
|
||
*
|
||
* Parameters:
|
||
* *in_session
|
||
*
|
||
* Returns:
|
||
* Pointer to a session in the session list -OR- FIX -- right?
|
||
* NULL on failure.
|
||
*
|
||
* The "spin-free" version of snmp_open.
|
||
*/
|
||
static void *
|
||
_sess_open(netsnmp_session * in_session)
|
||
{
|
||
netsnmp_transport *transport = NULL;
|
||
int rc;
|
||
|
||
in_session->s_snmp_errno = 0;
|
||
in_session->s_errno = 0;
|
||
|
||
_init_snmp();
|
||
|
||
{
|
||
char *clientaddr_save = NULL;
|
||
|
||
if (NULL != in_session->localname) {
|
||
clientaddr_save =
|
||
netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_CLIENT_ADDR);
|
||
if (clientaddr_save)
|
||
clientaddr_save = strdup(clientaddr_save);
|
||
|
||
netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_CLIENT_ADDR,
|
||
in_session->localname);
|
||
}
|
||
|
||
if (in_session->flags & SNMP_FLAGS_STREAM_SOCKET) {
|
||
transport =
|
||
netsnmp_tdomain_transport_full("snmp", in_session->peername,
|
||
in_session->local_port, "tcp,tcp6",
|
||
NULL);
|
||
} else {
|
||
transport =
|
||
netsnmp_tdomain_transport_full("snmp", in_session->peername,
|
||
in_session->local_port, "udp,udp6",
|
||
NULL);
|
||
}
|
||
|
||
if (NULL != in_session->localname)
|
||
netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_CLIENT_ADDR, clientaddr_save);
|
||
free(clientaddr_save);
|
||
}
|
||
|
||
if (transport == NULL) {
|
||
DEBUGMSGTL(("_sess_open", "couldn't interpret peername\n"));
|
||
in_session->s_snmp_errno = SNMPERR_BAD_ADDRESS;
|
||
in_session->s_errno = errno;
|
||
snmp_set_detail(in_session->peername);
|
||
return NULL;
|
||
}
|
||
|
||
/* Optional supplimental transport configuration information and
|
||
final call to actually open the transport */
|
||
if ((rc = netsnmp_sess_config_and_open_transport(in_session, transport))
|
||
!= SNMPERR_SUCCESS) {
|
||
transport = NULL;
|
||
return NULL;
|
||
}
|
||
|
||
#if defined(SO_BROADCAST) && defined(SOL_SOCKET)
|
||
if ( in_session->flags & SNMP_FLAGS_UDP_BROADCAST) {
|
||
int b = 1;
|
||
int rc;
|
||
|
||
rc = setsockopt(transport->sock, SOL_SOCKET, SO_BROADCAST,
|
||
(char *)&b, sizeof(b));
|
||
|
||
if ( rc != 0 ) {
|
||
in_session->s_snmp_errno = SNMPERR_BAD_ADDRESS; /* good as any? */
|
||
in_session->s_errno = errno;
|
||
|
||
DEBUGMSGTL(("_sess_open", "couldn't enable UDP_BROADCAST\n"));
|
||
return NULL;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
return snmp_sess_add(in_session, transport, NULL, NULL);
|
||
}
|
||
|
||
/*
|
||
* EXTENDED SESSION API ------------------------------------------
|
||
*
|
||
* snmp_sess_add_ex, snmp_sess_add, snmp_add
|
||
*
|
||
* Analogous to snmp_open family of functions, but taking a netsnmp_transport
|
||
* pointer as an extra argument. Unlike snmp_open et al. it doesn't attempt
|
||
* to interpret the in_session->peername as a transport endpoint specifier,
|
||
* but instead uses the supplied transport. JBPN
|
||
*
|
||
*/
|
||
|
||
netsnmp_session *
|
||
snmp_add(netsnmp_session * in_session,
|
||
netsnmp_transport *transport,
|
||
int (*fpre_parse) (netsnmp_session *, netsnmp_transport *, void *,
|
||
int), int (*fpost_parse) (netsnmp_session *,
|
||
netsnmp_pdu *, int))
|
||
{
|
||
struct session_list *slp;
|
||
slp = (struct session_list *) snmp_sess_add_ex(in_session, transport,
|
||
fpre_parse, NULL,
|
||
fpost_parse, NULL, NULL,
|
||
NULL, NULL);
|
||
if (slp == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
snmp_session_insert(slp);
|
||
|
||
return (slp->session);
|
||
}
|
||
|
||
netsnmp_session *
|
||
snmp_add_full(netsnmp_session * in_session,
|
||
netsnmp_transport *transport,
|
||
int (*fpre_parse) (netsnmp_session *, netsnmp_transport *,
|
||
void *, int),
|
||
int (*fparse) (netsnmp_session *, netsnmp_pdu *, u_char *,
|
||
size_t),
|
||
int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *, int),
|
||
int (*fbuild) (netsnmp_session *, netsnmp_pdu *, u_char *,
|
||
size_t *), int (*frbuild) (netsnmp_session *,
|
||
netsnmp_pdu *,
|
||
u_char **,
|
||
size_t *,
|
||
size_t *),
|
||
int (*fcheck) (u_char *, size_t),
|
||
netsnmp_pdu *(*fcreate_pdu) (netsnmp_transport *, void *,
|
||
size_t))
|
||
{
|
||
struct session_list *slp;
|
||
slp = (struct session_list *) snmp_sess_add_ex(in_session, transport,
|
||
fpre_parse, fparse,
|
||
fpost_parse, fbuild,
|
||
frbuild, fcheck,
|
||
fcreate_pdu);
|
||
if (slp == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
snmp_session_insert(slp);
|
||
|
||
return (slp->session);
|
||
}
|
||
|
||
|
||
|
||
void *
|
||
snmp_sess_add_ex(netsnmp_session * in_session,
|
||
netsnmp_transport *transport,
|
||
int (*fpre_parse) (netsnmp_session *, netsnmp_transport *,
|
||
void *, int),
|
||
int (*fparse) (netsnmp_session *, netsnmp_pdu *, u_char *,
|
||
size_t),
|
||
int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *,
|
||
int),
|
||
int (*fbuild) (netsnmp_session *, netsnmp_pdu *, u_char *,
|
||
size_t *),
|
||
int (*frbuild) (netsnmp_session *, netsnmp_pdu *,
|
||
u_char **, size_t *, size_t *),
|
||
int (*fcheck) (u_char *, size_t),
|
||
netsnmp_pdu *(*fcreate_pdu) (netsnmp_transport *, void *,
|
||
size_t))
|
||
{
|
||
struct session_list *slp;
|
||
int rc;
|
||
|
||
_init_snmp();
|
||
|
||
if (transport == NULL)
|
||
return NULL;
|
||
|
||
if (NULL != in_session && (in_session->rcvMsgMaxSize < SNMP_MIN_MAX_LEN ||
|
||
in_session->sndMsgMaxSize < SNMP_MIN_MAX_LEN)) {
|
||
DEBUGMSGTL(("snmp_sess_add",
|
||
"invalid session (msg sizes). need snmp_sess_init"));
|
||
in_session = NULL; /* force transport cleanup below */
|
||
}
|
||
|
||
if (in_session == NULL) {
|
||
transport->f_close(transport);
|
||
netsnmp_transport_free(transport);
|
||
return NULL;
|
||
}
|
||
|
||
/* if the transport hasn't been fully opened yet, open it now */
|
||
if ((rc = netsnmp_sess_config_and_open_transport(in_session, transport))
|
||
!= SNMPERR_SUCCESS) {
|
||
return NULL;
|
||
}
|
||
|
||
if (transport->f_setup_session) {
|
||
if (SNMPERR_SUCCESS !=
|
||
transport->f_setup_session(transport, in_session)) {
|
||
netsnmp_transport_free(transport);
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
|
||
DEBUGMSGTL(("snmp_sess_add", "fd %d\n", transport->sock));
|
||
|
||
|
||
if ((slp = snmp_sess_copy(in_session)) == NULL) {
|
||
transport->f_close(transport);
|
||
netsnmp_transport_free(transport);
|
||
return (NULL);
|
||
}
|
||
|
||
slp->transport = transport;
|
||
slp->internal->hook_pre = fpre_parse;
|
||
slp->internal->hook_parse = fparse;
|
||
slp->internal->hook_post = fpost_parse;
|
||
slp->internal->hook_build = fbuild;
|
||
slp->internal->hook_realloc_build = frbuild;
|
||
slp->internal->check_packet = fcheck;
|
||
slp->internal->hook_create_pdu = fcreate_pdu;
|
||
|
||
/** don't let session max exceed transport max */
|
||
if (slp->session->rcvMsgMaxSize > transport->msgMaxSize) {
|
||
DEBUGMSGTL(("snmp_sess_add",
|
||
"limiting session rcv size to transport max\n"));
|
||
slp->session->rcvMsgMaxSize = transport->msgMaxSize;
|
||
}
|
||
if (slp->session->sndMsgMaxSize > transport->msgMaxSize) {
|
||
DEBUGMSGTL(("snmp_sess_add",
|
||
"limiting session snd size to transport max\n"));
|
||
slp->session->sndMsgMaxSize = transport->msgMaxSize;
|
||
}
|
||
|
||
if (slp->session->version == SNMP_VERSION_3) {
|
||
DEBUGMSGTL(("snmp_sess_add",
|
||
"adding v3 session -- maybe engineID probe now\n"));
|
||
if (!snmpv3_engineID_probe(slp, slp->session)) {
|
||
DEBUGMSGTL(("snmp_sess_add", "engine ID probe failed\n"));
|
||
snmp_sess_close(slp);
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
slp->session->flags &= ~SNMP_FLAGS_DONT_PROBE;
|
||
|
||
return (void *) slp;
|
||
} /* end snmp_sess_add_ex() */
|
||
|
||
|
||
|
||
void *
|
||
snmp_sess_add(netsnmp_session * in_session,
|
||
netsnmp_transport *transport,
|
||
int (*fpre_parse) (netsnmp_session *, netsnmp_transport *,
|
||
void *, int),
|
||
int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *, int))
|
||
{
|
||
return snmp_sess_add_ex(in_session, transport, fpre_parse, NULL,
|
||
fpost_parse, NULL, NULL, NULL, NULL);
|
||
}
|
||
|
||
|
||
|
||
void *
|
||
snmp_sess_open(netsnmp_session * pss)
|
||
{
|
||
void *pvoid;
|
||
pvoid = _sess_open(pss);
|
||
if (!pvoid) {
|
||
SET_SNMP_ERROR(pss->s_snmp_errno);
|
||
}
|
||
return pvoid;
|
||
}
|
||
|
||
int
|
||
create_user_from_session(netsnmp_session * session) {
|
||
#ifdef NETSNMP_SECMOD_USM
|
||
return usm_create_user_from_session(session);
|
||
#else
|
||
snmp_log(LOG_ERR, "create_user_from_session called when USM wasn't compiled in");
|
||
netsnmp_assert(0 == 1);
|
||
return SNMP_ERR_GENERR;
|
||
#endif
|
||
}
|
||
|
||
|
||
/*
|
||
* Do a "deep free()" of a netsnmp_session.
|
||
*
|
||
* CAUTION: SHOULD ONLY BE USED FROM snmp_sess_close() OR SIMILAR.
|
||
* (hence it is static)
|
||
*/
|
||
|
||
static void
|
||
snmp_free_session(netsnmp_session * s)
|
||
{
|
||
if (s) {
|
||
SNMP_FREE(s->localname);
|
||
SNMP_FREE(s->peername);
|
||
SNMP_FREE(s->community);
|
||
SNMP_FREE(s->contextEngineID);
|
||
SNMP_FREE(s->contextName);
|
||
SNMP_FREE(s->securityEngineID);
|
||
SNMP_FREE(s->securityName);
|
||
SNMP_FREE(s->securityAuthProto);
|
||
SNMP_FREE(s->securityPrivProto);
|
||
SNMP_FREE(s->paramName);
|
||
#ifndef NETSNMP_NO_TRAP_STATS
|
||
SNMP_FREE(s->trap_stats);
|
||
#endif /* NETSNMP_NO_TRAP_STATS */
|
||
|
||
/*
|
||
* clear session from any callbacks
|
||
*/
|
||
netsnmp_callback_clear_client_arg(s, 0, 0);
|
||
|
||
free((char *) s);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Close the input session. Frees all data allocated for the session,
|
||
* dequeues any pending requests, and closes any sockets allocated for
|
||
* the session. Returns 0 on error, 1 otherwise.
|
||
*/
|
||
int
|
||
snmp_sess_close(void *sessp)
|
||
{
|
||
struct session_list *slp = (struct session_list *) sessp;
|
||
netsnmp_transport *transport;
|
||
struct snmp_internal_session *isp;
|
||
netsnmp_session *sesp = NULL;
|
||
struct snmp_secmod_def *sptr;
|
||
|
||
if (slp == NULL) {
|
||
return 0;
|
||
}
|
||
|
||
if (slp->session != NULL &&
|
||
(sptr = find_sec_mod(slp->session->securityModel)) != NULL &&
|
||
sptr->session_close != NULL) {
|
||
(*sptr->session_close) (slp->session);
|
||
}
|
||
|
||
isp = slp->internal;
|
||
slp->internal = NULL;
|
||
|
||
if (isp) {
|
||
netsnmp_request_list *rp, *orp;
|
||
|
||
SNMP_FREE(isp->packet);
|
||
|
||
/*
|
||
* Free each element in the input request list.
|
||
*/
|
||
rp = isp->requests;
|
||
while (rp) {
|
||
orp = rp;
|
||
rp = rp->next_request;
|
||
if (orp->callback) {
|
||
orp->callback(NETSNMP_CALLBACK_OP_TIMED_OUT,
|
||
slp->session, orp->pdu->reqid,
|
||
orp->pdu, orp->cb_data);
|
||
}
|
||
snmp_free_pdu(orp->pdu);
|
||
free((char *) orp);
|
||
}
|
||
|
||
free((char *) isp);
|
||
}
|
||
|
||
transport = slp->transport;
|
||
slp->transport = NULL;
|
||
|
||
if (transport) {
|
||
transport->f_close(transport);
|
||
netsnmp_transport_free(transport);
|
||
}
|
||
|
||
sesp = slp->session;
|
||
slp->session = NULL;
|
||
|
||
/*
|
||
* The following is necessary to avoid memory leakage when closing AgentX
|
||
* sessions that may have multiple subsessions. These hang off the main
|
||
* session at ->subsession, and chain through ->next.
|
||
*/
|
||
|
||
if (sesp != NULL && sesp->subsession != NULL) {
|
||
netsnmp_session *subsession = sesp->subsession, *tmpsub;
|
||
|
||
while (subsession != NULL) {
|
||
DEBUGMSGTL(("snmp_sess_close",
|
||
"closing session %p, subsession %p\n", sesp,
|
||
subsession));
|
||
tmpsub = subsession->next;
|
||
snmp_free_session(subsession);
|
||
subsession = tmpsub;
|
||
}
|
||
}
|
||
|
||
snmp_free_session(sesp);
|
||
free((char *) slp);
|
||
return 1;
|
||
}
|
||
|
||
int
|
||
snmp_close(netsnmp_session * session)
|
||
{
|
||
struct session_list *slp = NULL, *oslp = NULL;
|
||
|
||
{ /*MTCRITICAL_RESOURCE */
|
||
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
if (Sessions && Sessions->session == session) { /* If first entry */
|
||
slp = Sessions;
|
||
Sessions = slp->next;
|
||
} else {
|
||
for (slp = Sessions; slp; slp = slp->next) {
|
||
if (slp->session == session) {
|
||
if (oslp) /* if we found entry that points here */
|
||
oslp->next = slp->next; /* link around this entry */
|
||
break;
|
||
}
|
||
oslp = slp;
|
||
}
|
||
}
|
||
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
} /*END MTCRITICAL_RESOURCE */
|
||
if (slp == NULL) {
|
||
return 0;
|
||
}
|
||
return snmp_sess_close((void *) slp);
|
||
}
|
||
|
||
int
|
||
snmp_close_sessions(void)
|
||
{
|
||
struct session_list *slp;
|
||
|
||
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
while (Sessions) {
|
||
slp = Sessions;
|
||
Sessions = Sessions->next;
|
||
snmp_sess_close((void *) slp);
|
||
}
|
||
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
return 1;
|
||
}
|
||
|
||
static void
|
||
snmpv3_calc_msg_flags(int sec_level, int msg_command, u_char * flags)
|
||
{
|
||
*flags = 0;
|
||
if (sec_level == SNMP_SEC_LEVEL_AUTHNOPRIV)
|
||
*flags = SNMP_MSG_FLAG_AUTH_BIT;
|
||
else if (sec_level == SNMP_SEC_LEVEL_AUTHPRIV)
|
||
*flags = SNMP_MSG_FLAG_AUTH_BIT | SNMP_MSG_FLAG_PRIV_BIT;
|
||
|
||
if (SNMP_CMD_CONFIRMED(msg_command))
|
||
*flags |= SNMP_MSG_FLAG_RPRT_BIT;
|
||
|
||
return;
|
||
}
|
||
|
||
static int
|
||
snmpv3_verify_msg(netsnmp_request_list *rp, netsnmp_pdu *pdu)
|
||
{
|
||
netsnmp_pdu *rpdu;
|
||
|
||
if (!rp || !rp->pdu || !pdu)
|
||
return 0;
|
||
/*
|
||
* Reports don't have to match anything according to the spec
|
||
*/
|
||
if (pdu->command == SNMP_MSG_REPORT)
|
||
return 1;
|
||
rpdu = rp->pdu;
|
||
if (rp->request_id != pdu->reqid || rpdu->reqid != pdu->reqid)
|
||
return 0;
|
||
if (rpdu->version != pdu->version)
|
||
return 0;
|
||
if (rpdu->securityModel != pdu->securityModel)
|
||
return 0;
|
||
if (rpdu->securityLevel != pdu->securityLevel)
|
||
return 0;
|
||
|
||
if (rpdu->contextEngineIDLen != pdu->contextEngineIDLen ||
|
||
memcmp(rpdu->contextEngineID, pdu->contextEngineID,
|
||
pdu->contextEngineIDLen))
|
||
return 0;
|
||
if (rpdu->contextNameLen != pdu->contextNameLen ||
|
||
memcmp(rpdu->contextName, pdu->contextName, pdu->contextNameLen))
|
||
return 0;
|
||
|
||
/* tunneled transports don't have a securityEngineID... that's
|
||
USM specific (and maybe other future ones) */
|
||
if (pdu->securityModel == SNMP_SEC_MODEL_USM &&
|
||
(rpdu->securityEngineIDLen != pdu->securityEngineIDLen ||
|
||
memcmp(rpdu->securityEngineID, pdu->securityEngineID,
|
||
pdu->securityEngineIDLen)))
|
||
return 0;
|
||
|
||
/* the securityName must match though regardless of secmodel */
|
||
if (rpdu->securityNameLen != pdu->securityNameLen ||
|
||
memcmp(rpdu->securityName, pdu->securityName,
|
||
pdu->securityNameLen))
|
||
return 0;
|
||
return 1;
|
||
}
|
||
|
||
|
||
/*
|
||
* SNMPv3
|
||
* * Takes a session and a pdu and serializes the ASN PDU into the area
|
||
* * pointed to by packet. out_length is the size of the data area available.
|
||
* * Returns the length of the completed packet in out_length. If any errors
|
||
* * occur, -1 is returned. If all goes well, 0 is returned.
|
||
*/
|
||
static int
|
||
snmpv3_build(u_char ** pkt, size_t * pkt_len, size_t * offset,
|
||
netsnmp_session * session, netsnmp_pdu *pdu)
|
||
{
|
||
int ret;
|
||
|
||
session->s_snmp_errno = 0;
|
||
session->s_errno = 0;
|
||
|
||
/*
|
||
* do validation for PDU types
|
||
*/
|
||
switch (pdu->command) {
|
||
case SNMP_MSG_RESPONSE:
|
||
case SNMP_MSG_TRAP2:
|
||
case SNMP_MSG_REPORT:
|
||
netsnmp_assert(0 == (pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE));
|
||
/* FALL THROUGH */
|
||
case SNMP_MSG_INFORM:
|
||
#ifndef NETSNMP_NOTIFY_ONLY
|
||
case SNMP_MSG_GET:
|
||
case SNMP_MSG_GETNEXT:
|
||
#endif /* ! NETSNMP_NOTIFY_ONLY */
|
||
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
||
case SNMP_MSG_SET:
|
||
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
|
||
if (pdu->errstat == SNMP_DEFAULT_ERRSTAT)
|
||
pdu->errstat = 0;
|
||
if (pdu->errindex == SNMP_DEFAULT_ERRINDEX)
|
||
pdu->errindex = 0;
|
||
break;
|
||
|
||
#ifndef NETSNMP_NOTIFY_ONLY
|
||
case SNMP_MSG_GETBULK:
|
||
if (pdu->max_repetitions < 0) {
|
||
session->s_snmp_errno = SNMPERR_BAD_REPETITIONS;
|
||
return -1;
|
||
}
|
||
if (pdu->non_repeaters < 0) {
|
||
session->s_snmp_errno = SNMPERR_BAD_REPEATERS;
|
||
return -1;
|
||
}
|
||
break;
|
||
#endif /* ! NETSNMP_NOTIFY_ONLY */
|
||
|
||
case SNMP_MSG_TRAP:
|
||
session->s_snmp_errno = SNMPERR_V1_IN_V2;
|
||
return -1;
|
||
|
||
default:
|
||
session->s_snmp_errno = SNMPERR_UNKNOWN_PDU;
|
||
return -1;
|
||
}
|
||
|
||
/* Do we need to set the session security engineid? */
|
||
if (pdu->securityEngineIDLen == 0) {
|
||
if (session->securityEngineIDLen) {
|
||
snmpv3_clone_engineID(&pdu->securityEngineID,
|
||
&pdu->securityEngineIDLen,
|
||
session->securityEngineID,
|
||
session->securityEngineIDLen);
|
||
}
|
||
}
|
||
|
||
/* Do we need to set the session context engineid? */
|
||
if (pdu->contextEngineIDLen == 0) {
|
||
if (session->contextEngineIDLen) {
|
||
snmpv3_clone_engineID(&pdu->contextEngineID,
|
||
&pdu->contextEngineIDLen,
|
||
session->contextEngineID,
|
||
session->contextEngineIDLen);
|
||
} else if (pdu->securityEngineIDLen) {
|
||
snmpv3_clone_engineID(&pdu->contextEngineID,
|
||
&pdu->contextEngineIDLen,
|
||
pdu->securityEngineID,
|
||
pdu->securityEngineIDLen);
|
||
}
|
||
}
|
||
|
||
if (pdu->contextName == NULL) {
|
||
if (!session->contextName) {
|
||
session->s_snmp_errno = SNMPERR_BAD_CONTEXT;
|
||
return -1;
|
||
}
|
||
pdu->contextName = strdup(session->contextName);
|
||
if (pdu->contextName == NULL) {
|
||
session->s_snmp_errno = SNMPERR_GENERR;
|
||
return -1;
|
||
}
|
||
pdu->contextNameLen = session->contextNameLen;
|
||
}
|
||
if (pdu->securityModel == SNMP_DEFAULT_SECMODEL) {
|
||
pdu->securityModel = session->securityModel;
|
||
if (pdu->securityModel == SNMP_DEFAULT_SECMODEL) {
|
||
pdu->securityModel = se_find_value_in_slist("snmp_secmods", netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECMODEL));
|
||
|
||
if (pdu->securityModel <= 0) {
|
||
pdu->securityModel = SNMP_SEC_MODEL_USM;
|
||
}
|
||
}
|
||
}
|
||
if (pdu->securityNameLen == 0 && pdu->securityName == NULL) {
|
||
if (session->securityModel != SNMP_SEC_MODEL_TSM &&
|
||
session->securityNameLen == 0) {
|
||
session->s_snmp_errno = SNMPERR_BAD_SEC_NAME;
|
||
return -1;
|
||
}
|
||
if (session->securityName) {
|
||
pdu->securityName = strdup(session->securityName);
|
||
if (pdu->securityName == NULL) {
|
||
session->s_snmp_errno = SNMPERR_GENERR;
|
||
return -1;
|
||
}
|
||
pdu->securityNameLen = session->securityNameLen;
|
||
} else {
|
||
pdu->securityName = strdup("");
|
||
session->securityName = strdup("");
|
||
}
|
||
}
|
||
if (pdu->securityLevel == 0) {
|
||
if (session->securityLevel == 0) {
|
||
session->s_snmp_errno = SNMPERR_BAD_SEC_LEVEL;
|
||
return -1;
|
||
}
|
||
pdu->securityLevel = session->securityLevel;
|
||
}
|
||
DEBUGMSGTL(("snmp_build",
|
||
"Building SNMPv3 message (secName:\"%s\", secLevel:%s)...\n",
|
||
((session->securityName) ? (char *) session->securityName :
|
||
((pdu->securityName) ? (char *) pdu->securityName :
|
||
"ERROR: undefined")), secLevelName[pdu->securityLevel]));
|
||
|
||
DEBUGDUMPSECTION("send", "SNMPv3 Message");
|
||
#ifdef NETSNMP_USE_REVERSE_ASNENCODING
|
||
if (!(pdu->flags & UCD_MSG_FLAG_FORWARD_ENCODE)) {
|
||
ret = snmpv3_packet_realloc_rbuild(pkt, pkt_len, offset,
|
||
session, pdu, NULL, 0);
|
||
} else {
|
||
#endif
|
||
ret = snmpv3_packet_build(session, pdu, *pkt, pkt_len, NULL, 0);
|
||
#ifdef NETSNMP_USE_REVERSE_ASNENCODING
|
||
}
|
||
#endif
|
||
DEBUGINDENTLESS();
|
||
if (-1 != ret) {
|
||
session->s_snmp_errno = ret;
|
||
}
|
||
|
||
return ret;
|
||
|
||
} /* end snmpv3_build() */
|
||
|
||
|
||
|
||
|
||
static u_char *
|
||
snmpv3_header_build(netsnmp_session * session, netsnmp_pdu *pdu,
|
||
u_char * packet, size_t * out_length,
|
||
size_t length, u_char ** msg_hdr_e)
|
||
{
|
||
u_char *global_hdr, *global_hdr_e;
|
||
u_char *cp;
|
||
u_char msg_flags;
|
||
long max_size;
|
||
long sec_model;
|
||
u_char *pb, *pb0e;
|
||
|
||
/*
|
||
* Save current location and build SEQUENCE tag and length placeholder
|
||
* * for SNMP message sequence (actual length inserted later)
|
||
*/
|
||
cp = asn_build_sequence(packet, out_length,
|
||
(u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
|
||
length);
|
||
if (cp == NULL)
|
||
return NULL;
|
||
if (msg_hdr_e != NULL)
|
||
*msg_hdr_e = cp;
|
||
pb0e = cp;
|
||
|
||
|
||
/*
|
||
* store the version field - msgVersion
|
||
*/
|
||
DEBUGDUMPHEADER("send", "SNMP Version Number");
|
||
cp = asn_build_int(cp, out_length,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_INTEGER), (long *) &pdu->version,
|
||
sizeof(pdu->version));
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL)
|
||
return NULL;
|
||
|
||
global_hdr = cp;
|
||
/*
|
||
* msgGlobalData HeaderData
|
||
*/
|
||
DEBUGDUMPSECTION("send", "msgGlobalData");
|
||
cp = asn_build_sequence(cp, out_length,
|
||
(u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), 0);
|
||
if (cp == NULL)
|
||
return NULL;
|
||
global_hdr_e = cp;
|
||
|
||
|
||
/*
|
||
* msgID
|
||
*/
|
||
DEBUGDUMPHEADER("send", "msgID");
|
||
cp = asn_build_int(cp, out_length,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_INTEGER), &pdu->msgid,
|
||
sizeof(pdu->msgid));
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL)
|
||
return NULL;
|
||
|
||
/*
|
||
* msgMaxSize
|
||
*/
|
||
max_size = netsnmp_max_send_msg_size();
|
||
if (session->rcvMsgMaxSize < max_size)
|
||
max_size = session->rcvMsgMaxSize;
|
||
DEBUGDUMPHEADER("send:msgMaxSize1", "msgMaxSize");
|
||
cp = asn_build_int(cp, out_length,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_INTEGER), &max_size,
|
||
sizeof(max_size));
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL)
|
||
return NULL;
|
||
|
||
/*
|
||
* msgFlags
|
||
*/
|
||
snmpv3_calc_msg_flags(pdu->securityLevel, pdu->command, &msg_flags);
|
||
DEBUGDUMPHEADER("send", "msgFlags");
|
||
cp = asn_build_string(cp, out_length,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_OCTET_STR), &msg_flags,
|
||
sizeof(msg_flags));
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL)
|
||
return NULL;
|
||
|
||
/*
|
||
* msgSecurityModel
|
||
*/
|
||
sec_model = pdu->securityModel;
|
||
DEBUGDUMPHEADER("send", "msgSecurityModel");
|
||
cp = asn_build_int(cp, out_length,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_INTEGER), &sec_model,
|
||
sizeof(sec_model));
|
||
DEBUGINDENTADD(-4); /* return from global data indent */
|
||
if (cp == NULL)
|
||
return NULL;
|
||
|
||
|
||
/*
|
||
* insert actual length of globalData
|
||
*/
|
||
pb = asn_build_sequence(global_hdr, out_length,
|
||
(u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
|
||
cp - global_hdr_e);
|
||
if (pb == NULL)
|
||
return NULL;
|
||
|
||
|
||
/*
|
||
* insert the actual length of the entire packet
|
||
*/
|
||
pb = asn_build_sequence(packet, out_length,
|
||
(u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
|
||
length + (cp - pb0e));
|
||
if (pb == NULL)
|
||
return NULL;
|
||
|
||
return cp;
|
||
|
||
} /* end snmpv3_header_build() */
|
||
|
||
#ifdef NETSNMP_USE_REVERSE_ASNENCODING
|
||
|
||
int
|
||
snmpv3_header_realloc_rbuild(u_char ** pkt, size_t * pkt_len,
|
||
size_t * offset, netsnmp_session * session,
|
||
netsnmp_pdu *pdu)
|
||
{
|
||
size_t start_offset = *offset;
|
||
u_char msg_flags;
|
||
long max_size, sec_model;
|
||
int rc = 0;
|
||
|
||
/*
|
||
* msgSecurityModel.
|
||
*/
|
||
sec_model = pdu->securityModel;
|
||
DEBUGDUMPHEADER("send", "msgSecurityModel");
|
||
rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_INTEGER), &sec_model,
|
||
sizeof(sec_model));
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* msgFlags.
|
||
*/
|
||
snmpv3_calc_msg_flags(pdu->securityLevel, pdu->command, &msg_flags);
|
||
DEBUGDUMPHEADER("send", "msgFlags");
|
||
rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
|
||
| ASN_OCTET_STR), &msg_flags,
|
||
sizeof(msg_flags));
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* msgMaxSize.
|
||
*/
|
||
max_size = netsnmp_max_send_msg_size();
|
||
if (session->rcvMsgMaxSize < max_size)
|
||
max_size = session->rcvMsgMaxSize;
|
||
DEBUGDUMPHEADER("send:msgMaxSize2", "msgMaxSize");
|
||
rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_INTEGER), &max_size,
|
||
sizeof(max_size));
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* msgID.
|
||
*/
|
||
DEBUGDUMPHEADER("send", "msgID");
|
||
rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_INTEGER), &pdu->msgid,
|
||
sizeof(pdu->msgid));
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Global data sequence.
|
||
*/
|
||
rc = asn_realloc_rbuild_sequence(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_SEQUENCE |
|
||
ASN_CONSTRUCTOR),
|
||
*offset - start_offset);
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Store the version field - msgVersion.
|
||
*/
|
||
DEBUGDUMPHEADER("send", "SNMP Version Number");
|
||
rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_INTEGER),
|
||
(long *) &pdu->version,
|
||
sizeof(pdu->version));
|
||
DEBUGINDENTLESS();
|
||
return rc;
|
||
} /* end snmpv3_header_realloc_rbuild() */
|
||
#endif /* NETSNMP_USE_REVERSE_ASNENCODING */
|
||
|
||
static u_char *
|
||
snmpv3_scopedPDU_header_build(netsnmp_pdu *pdu,
|
||
u_char * packet, size_t * out_length,
|
||
u_char ** spdu_e)
|
||
{
|
||
u_char *scopedPdu, *pb;
|
||
|
||
pb = scopedPdu = packet;
|
||
pb = asn_build_sequence(pb, out_length,
|
||
(u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), 0);
|
||
if (pb == NULL)
|
||
return NULL;
|
||
if (spdu_e)
|
||
*spdu_e = pb;
|
||
|
||
DEBUGDUMPHEADER("send", "contextEngineID");
|
||
pb = asn_build_string(pb, out_length,
|
||
(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
|
||
pdu->contextEngineID, pdu->contextEngineIDLen);
|
||
DEBUGINDENTLESS();
|
||
if (pb == NULL)
|
||
return NULL;
|
||
|
||
DEBUGDUMPHEADER("send", "contextName");
|
||
pb = asn_build_string(pb, out_length,
|
||
(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
|
||
(u_char *) pdu->contextName,
|
||
pdu->contextNameLen);
|
||
DEBUGINDENTLESS();
|
||
if (pb == NULL)
|
||
return NULL;
|
||
|
||
return pb;
|
||
|
||
} /* end snmpv3_scopedPDU_header_build() */
|
||
|
||
|
||
#ifdef NETSNMP_USE_REVERSE_ASNENCODING
|
||
int
|
||
snmpv3_scopedPDU_header_realloc_rbuild(u_char ** pkt, size_t * pkt_len,
|
||
size_t * offset, netsnmp_pdu *pdu,
|
||
size_t body_len)
|
||
{
|
||
size_t start_offset = *offset;
|
||
int rc = 0;
|
||
|
||
/*
|
||
* contextName.
|
||
*/
|
||
DEBUGDUMPHEADER("send", "contextName");
|
||
rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
|
||
| ASN_OCTET_STR),
|
||
(u_char *) pdu->contextName,
|
||
pdu->contextNameLen);
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* contextEngineID.
|
||
*/
|
||
DEBUGDUMPHEADER("send", "contextEngineID");
|
||
rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
|
||
| ASN_OCTET_STR),
|
||
pdu->contextEngineID,
|
||
pdu->contextEngineIDLen);
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
|
||
rc = asn_realloc_rbuild_sequence(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_SEQUENCE |
|
||
ASN_CONSTRUCTOR),
|
||
*offset - start_offset + body_len);
|
||
|
||
return rc;
|
||
} /* end snmpv3_scopedPDU_header_realloc_rbuild() */
|
||
#endif /* NETSNMP_USE_REVERSE_ASNENCODING */
|
||
|
||
#ifdef NETSNMP_USE_REVERSE_ASNENCODING
|
||
/*
|
||
* returns 0 if success, -1 if fail, not 0 if SM build failure
|
||
*/
|
||
int
|
||
snmpv3_packet_realloc_rbuild(u_char ** pkt, size_t * pkt_len,
|
||
size_t * offset, netsnmp_session * session,
|
||
netsnmp_pdu *pdu, u_char * pdu_data,
|
||
size_t pdu_data_len)
|
||
{
|
||
u_char *scoped_pdu, *hdrbuf = NULL, *hdr = NULL;
|
||
size_t hdrbuf_len = SNMP_MAX_MSG_V3_HDRS, hdr_offset =
|
||
0, spdu_offset = 0;
|
||
size_t body_end_offset = *offset, body_len = 0;
|
||
struct snmp_secmod_def *sptr = NULL;
|
||
int rc = 0;
|
||
|
||
/*
|
||
* Build a scopedPDU structure into the packet buffer.
|
||
*/
|
||
DEBUGPRINTPDUTYPE("send", pdu->command);
|
||
if (pdu_data) {
|
||
while ((*pkt_len - *offset) < pdu_data_len) {
|
||
if (!asn_realloc(pkt, pkt_len)) {
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
*offset += pdu_data_len;
|
||
memcpy(*pkt + *pkt_len - *offset, pdu_data, pdu_data_len);
|
||
} else {
|
||
rc = snmp_pdu_realloc_rbuild(pkt, pkt_len, offset, pdu);
|
||
if (rc == 0) {
|
||
return -1;
|
||
}
|
||
}
|
||
body_len = *offset - body_end_offset;
|
||
|
||
DEBUGDUMPSECTION("send", "ScopedPdu");
|
||
rc = snmpv3_scopedPDU_header_realloc_rbuild(pkt, pkt_len, offset,
|
||
pdu, body_len);
|
||
if (rc == 0) {
|
||
return -1;
|
||
}
|
||
spdu_offset = *offset;
|
||
DEBUGINDENTADD(-4); /* Return from Scoped PDU. */
|
||
|
||
if ((hdrbuf = (u_char *) malloc(hdrbuf_len)) == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
rc = snmpv3_header_realloc_rbuild(&hdrbuf, &hdrbuf_len, &hdr_offset,
|
||
session, pdu);
|
||
if (rc == 0) {
|
||
SNMP_FREE(hdrbuf);
|
||
return -1;
|
||
}
|
||
hdr = hdrbuf + hdrbuf_len - hdr_offset;
|
||
scoped_pdu = *pkt + *pkt_len - spdu_offset;
|
||
|
||
/*
|
||
* Call the security module to possibly encrypt and authenticate the
|
||
* message---the entire message to transmitted on the wire is returned.
|
||
*/
|
||
|
||
sptr = find_sec_mod(pdu->securityModel);
|
||
DEBUGDUMPSECTION("send", "SM msgSecurityParameters");
|
||
if (sptr && sptr->encode_reverse) {
|
||
struct snmp_secmod_outgoing_params parms;
|
||
|
||
parms.msgProcModel = pdu->msgParseModel;
|
||
parms.globalData = hdr;
|
||
parms.globalDataLen = hdr_offset;
|
||
parms.maxMsgSize = SNMP_MAX_MSG_SIZE;
|
||
parms.secModel = pdu->securityModel;
|
||
parms.secEngineID = pdu->securityEngineID;
|
||
parms.secEngineIDLen = pdu->securityEngineIDLen;
|
||
parms.secName = pdu->securityName;
|
||
parms.secNameLen = pdu->securityNameLen;
|
||
parms.secLevel = pdu->securityLevel;
|
||
parms.scopedPdu = scoped_pdu;
|
||
parms.scopedPduLen = spdu_offset;
|
||
parms.secStateRef = pdu->securityStateRef;
|
||
parms.wholeMsg = pkt;
|
||
parms.wholeMsgLen = pkt_len;
|
||
parms.wholeMsgOffset = offset;
|
||
parms.session = session;
|
||
parms.pdu = pdu;
|
||
|
||
rc = (*sptr->encode_reverse) (&parms);
|
||
} else {
|
||
if (!sptr) {
|
||
snmp_log(LOG_ERR,
|
||
"no such security service available: %d\n",
|
||
pdu->securityModel);
|
||
} else if (!sptr->encode_reverse) {
|
||
snmp_log(LOG_ERR,
|
||
"security service %d doesn't support reverse encoding.\n",
|
||
pdu->securityModel);
|
||
}
|
||
rc = -1;
|
||
}
|
||
|
||
DEBUGINDENTLESS();
|
||
SNMP_FREE(hdrbuf);
|
||
return rc;
|
||
} /* end snmpv3_packet_realloc_rbuild() */
|
||
#endif /* NETSNMP_USE_REVERSE_ASNENCODING */
|
||
|
||
/*
|
||
* returns 0 if success, -1 if fail, not 0 if SM build failure
|
||
*/
|
||
int
|
||
snmpv3_packet_build(netsnmp_session * session, netsnmp_pdu *pdu,
|
||
u_char * packet, size_t * out_length,
|
||
u_char * pdu_data, size_t pdu_data_len)
|
||
{
|
||
u_char *global_data, *sec_params, *spdu_hdr_e;
|
||
size_t global_data_len, sec_params_len;
|
||
u_char spdu_buf[SNMP_MAX_MSG_SIZE];
|
||
size_t spdu_buf_len, spdu_len;
|
||
u_char *cp;
|
||
int result;
|
||
struct snmp_secmod_def *sptr;
|
||
|
||
global_data = packet;
|
||
|
||
/*
|
||
* build the headers for the packet, returned addr = start of secParams
|
||
*/
|
||
sec_params = snmpv3_header_build(session, pdu, global_data,
|
||
out_length, 0, NULL);
|
||
if (sec_params == NULL)
|
||
return -1;
|
||
global_data_len = sec_params - global_data;
|
||
sec_params_len = *out_length; /* length left in packet buf for sec_params */
|
||
|
||
|
||
/*
|
||
* build a scopedPDU structure into spdu_buf
|
||
*/
|
||
spdu_buf_len = SNMP_MAX_MSG_SIZE;
|
||
DEBUGDUMPSECTION("send", "ScopedPdu");
|
||
cp = snmpv3_scopedPDU_header_build(pdu, spdu_buf, &spdu_buf_len,
|
||
&spdu_hdr_e);
|
||
if (cp == NULL)
|
||
return -1;
|
||
|
||
/*
|
||
* build the PDU structure onto the end of spdu_buf
|
||
*/
|
||
DEBUGPRINTPDUTYPE("send", ((pdu_data) ? *pdu_data : 0x00));
|
||
if (pdu_data) {
|
||
memcpy(cp, pdu_data, pdu_data_len);
|
||
cp += pdu_data_len;
|
||
} else {
|
||
cp = snmp_pdu_build(pdu, cp, &spdu_buf_len);
|
||
if (cp == NULL)
|
||
return -1;
|
||
}
|
||
DEBUGINDENTADD(-4); /* return from Scoped PDU */
|
||
|
||
/*
|
||
* re-encode the actual ASN.1 length of the scopedPdu
|
||
*/
|
||
spdu_len = cp - spdu_hdr_e; /* length of scopedPdu minus ASN.1 headers */
|
||
spdu_buf_len = SNMP_MAX_MSG_SIZE;
|
||
if (asn_build_sequence(spdu_buf, &spdu_buf_len,
|
||
(u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
|
||
spdu_len) == NULL)
|
||
return -1;
|
||
spdu_len = cp - spdu_buf; /* the length of the entire scopedPdu */
|
||
|
||
|
||
/*
|
||
* call the security module to possibly encrypt and authenticate the
|
||
* message - the entire message to transmitted on the wire is returned
|
||
*/
|
||
cp = NULL;
|
||
*out_length = SNMP_MAX_MSG_SIZE;
|
||
DEBUGDUMPSECTION("send", "SM msgSecurityParameters");
|
||
sptr = find_sec_mod(pdu->securityModel);
|
||
if (sptr && sptr->encode_forward) {
|
||
struct snmp_secmod_outgoing_params parms;
|
||
parms.msgProcModel = pdu->msgParseModel;
|
||
parms.globalData = global_data;
|
||
parms.globalDataLen = global_data_len;
|
||
parms.maxMsgSize = SNMP_MAX_MSG_SIZE;
|
||
parms.secModel = pdu->securityModel;
|
||
parms.secEngineID = pdu->securityEngineID;
|
||
parms.secEngineIDLen = pdu->securityEngineIDLen;
|
||
parms.secName = pdu->securityName;
|
||
parms.secNameLen = pdu->securityNameLen;
|
||
parms.secLevel = pdu->securityLevel;
|
||
parms.scopedPdu = spdu_buf;
|
||
parms.scopedPduLen = spdu_len;
|
||
parms.secStateRef = pdu->securityStateRef;
|
||
parms.secParams = sec_params;
|
||
parms.secParamsLen = &sec_params_len;
|
||
parms.wholeMsg = &cp;
|
||
parms.wholeMsgLen = out_length;
|
||
parms.session = session;
|
||
parms.pdu = pdu;
|
||
result = (*sptr->encode_forward) (&parms);
|
||
} else {
|
||
if (!sptr) {
|
||
snmp_log(LOG_ERR, "no such security service available: %d\n",
|
||
pdu->securityModel);
|
||
} else if (!sptr->encode_forward) {
|
||
snmp_log(LOG_ERR,
|
||
"security service %d doesn't support forward out encoding.\n",
|
||
pdu->securityModel);
|
||
}
|
||
result = -1;
|
||
}
|
||
DEBUGINDENTLESS();
|
||
return result;
|
||
|
||
} /* end snmpv3_packet_build() */
|
||
|
||
|
||
/*
|
||
* Takes a session and a pdu and serializes the ASN PDU into the area
|
||
* pointed to by *pkt. *pkt_len is the size of the data area available.
|
||
* Returns the length of the completed packet in *offset. If any errors
|
||
* occur, -1 is returned. If all goes well, 0 is returned.
|
||
*/
|
||
|
||
static int
|
||
_snmp_build(u_char ** pkt, size_t * pkt_len, size_t * offset,
|
||
netsnmp_session * session, netsnmp_pdu *pdu)
|
||
{
|
||
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
|
||
u_char *h0e = NULL;
|
||
size_t start_offset = *offset;
|
||
long version;
|
||
int rc = 0;
|
||
size_t length;
|
||
#endif /* support for community based SNMP */
|
||
|
||
u_char *cp;
|
||
|
||
if (NETSNMP_RUNTIME_PROTOCOL_SKIP(pdu->version)) {
|
||
DEBUGMSGTL(("snmp_send", "build packet (version 0x%02x disabled)\n",
|
||
(u_int)pdu->version));
|
||
session->s_snmp_errno = SNMPERR_BAD_VERSION;
|
||
return -1;
|
||
}
|
||
|
||
session->s_snmp_errno = 0;
|
||
session->s_errno = 0;
|
||
|
||
#ifdef NETSNMP_USE_REVERSE_ASNENCODING
|
||
if ((pdu->flags & UCD_MSG_FLAG_BULK_TOOBIG) ||
|
||
(0 == netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_REVERSE_ENCODE))) {
|
||
pdu->flags |= UCD_MSG_FLAG_FORWARD_ENCODE;
|
||
}
|
||
#endif /* NETSNMP_USE_REVERSE_ASNENCODING */
|
||
|
||
if (pdu->version == SNMP_VERSION_3) {
|
||
return snmpv3_build(pkt, pkt_len, offset, session, pdu);
|
||
}
|
||
|
||
switch (pdu->command) {
|
||
case SNMP_MSG_RESPONSE:
|
||
netsnmp_assert(0 == (pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE));
|
||
#ifndef NETSNMP_NOTIFY_ONLY
|
||
/* FALL THROUGH */
|
||
case SNMP_MSG_GET:
|
||
case SNMP_MSG_GETNEXT:
|
||
/* FALL THROUGH */
|
||
#endif /* ! NETSNMP_NOTIFY_ONLY */
|
||
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
||
case SNMP_MSG_SET:
|
||
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
|
||
/*
|
||
* all versions support these PDU types
|
||
*/
|
||
/*
|
||
* initialize defaulted PDU fields
|
||
*/
|
||
|
||
if (pdu->errstat == SNMP_DEFAULT_ERRSTAT)
|
||
pdu->errstat = 0;
|
||
if (pdu->errindex == SNMP_DEFAULT_ERRINDEX)
|
||
pdu->errindex = 0;
|
||
break;
|
||
|
||
case SNMP_MSG_TRAP2:
|
||
netsnmp_assert(0 == (pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE));
|
||
/* FALL THROUGH */
|
||
case SNMP_MSG_INFORM:
|
||
#ifndef NETSNMP_DISABLE_SNMPV1
|
||
/*
|
||
* not supported in SNMPv1 and SNMPsec
|
||
*/
|
||
if (pdu->version == SNMP_VERSION_1) {
|
||
session->s_snmp_errno = SNMPERR_V2_IN_V1;
|
||
return -1;
|
||
}
|
||
#endif
|
||
if (pdu->errstat == SNMP_DEFAULT_ERRSTAT)
|
||
pdu->errstat = 0;
|
||
if (pdu->errindex == SNMP_DEFAULT_ERRINDEX)
|
||
pdu->errindex = 0;
|
||
break;
|
||
|
||
#ifndef NETSNMP_NOTIFY_ONLY
|
||
case SNMP_MSG_GETBULK:
|
||
/*
|
||
* not supported in SNMPv1 and SNMPsec
|
||
*/
|
||
#ifndef NETSNMP_DISABLE_SNMPV1
|
||
if (pdu->version == SNMP_VERSION_1) {
|
||
session->s_snmp_errno = SNMPERR_V2_IN_V1;
|
||
return -1;
|
||
}
|
||
#endif
|
||
if (pdu->max_repetitions < 0) {
|
||
session->s_snmp_errno = SNMPERR_BAD_REPETITIONS;
|
||
return -1;
|
||
}
|
||
if (pdu->non_repeaters < 0) {
|
||
session->s_snmp_errno = SNMPERR_BAD_REPEATERS;
|
||
return -1;
|
||
}
|
||
break;
|
||
#endif /* ! NETSNMP_NOTIFY_ONLY */
|
||
|
||
case SNMP_MSG_TRAP:
|
||
/*
|
||
* *only* supported in SNMPv1 and SNMPsec
|
||
*/
|
||
#ifndef NETSNMP_DISABLE_SNMPV1
|
||
if (pdu->version != SNMP_VERSION_1) {
|
||
session->s_snmp_errno = SNMPERR_V1_IN_V2;
|
||
return -1;
|
||
}
|
||
#endif
|
||
/*
|
||
* initialize defaulted Trap PDU fields
|
||
*/
|
||
pdu->reqid = 1; /* give a bogus non-error reqid for traps */
|
||
if (pdu->enterprise_length == SNMP_DEFAULT_ENTERPRISE_LENGTH) {
|
||
pdu->enterprise = (oid *) malloc(sizeof(DEFAULT_ENTERPRISE));
|
||
if (pdu->enterprise == NULL) {
|
||
session->s_snmp_errno = SNMPERR_MALLOC;
|
||
return -1;
|
||
}
|
||
memmove(pdu->enterprise, DEFAULT_ENTERPRISE,
|
||
sizeof(DEFAULT_ENTERPRISE));
|
||
pdu->enterprise_length =
|
||
sizeof(DEFAULT_ENTERPRISE) / sizeof(oid);
|
||
}
|
||
if (pdu->time == SNMP_DEFAULT_TIME)
|
||
pdu->time = DEFAULT_TIME;
|
||
/*
|
||
* don't expect a response
|
||
*/
|
||
pdu->flags &= (~UCD_MSG_FLAG_EXPECT_RESPONSE);
|
||
break;
|
||
|
||
case SNMP_MSG_REPORT: /* SNMPv3 only */
|
||
default:
|
||
session->s_snmp_errno = SNMPERR_UNKNOWN_PDU;
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* save length
|
||
*/
|
||
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
|
||
length = *pkt_len;
|
||
#endif
|
||
|
||
/*
|
||
* setup administrative fields based on version
|
||
*/
|
||
/*
|
||
* build the message wrapper and all the administrative fields
|
||
* upto the PDU sequence
|
||
* (note that actual length of message will be inserted later)
|
||
*/
|
||
switch (pdu->version) {
|
||
#ifndef NETSNMP_DISABLE_SNMPV1
|
||
case SNMP_VERSION_1:
|
||
#endif
|
||
#ifndef NETSNMP_DISABLE_SNMPV2C
|
||
case SNMP_VERSION_2c:
|
||
#endif
|
||
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
|
||
#ifdef NETSNMP_NO_ZEROLENGTH_COMMUNITY
|
||
if (pdu->community_len == 0) {
|
||
if (session->community_len == 0) {
|
||
session->s_snmp_errno = SNMPERR_BAD_COMMUNITY;
|
||
return -1;
|
||
}
|
||
pdu->community = (u_char *) malloc(session->community_len);
|
||
if (pdu->community == NULL) {
|
||
session->s_snmp_errno = SNMPERR_MALLOC;
|
||
return -1;
|
||
}
|
||
memmove(pdu->community,
|
||
session->community, session->community_len);
|
||
pdu->community_len = session->community_len;
|
||
}
|
||
#else /* !NETSNMP_NO_ZEROLENGTH_COMMUNITY */
|
||
if (pdu->community_len == 0 && pdu->command != SNMP_MSG_RESPONSE) {
|
||
/*
|
||
* copy session community exactly to pdu community
|
||
*/
|
||
if (0 == session->community_len) {
|
||
SNMP_FREE(pdu->community);
|
||
} else if (pdu->community_len == session->community_len) {
|
||
memmove(pdu->community,
|
||
session->community, session->community_len);
|
||
} else {
|
||
SNMP_FREE(pdu->community);
|
||
pdu->community = (u_char *) malloc(session->community_len);
|
||
if (pdu->community == NULL) {
|
||
session->s_snmp_errno = SNMPERR_MALLOC;
|
||
return -1;
|
||
}
|
||
memmove(pdu->community,
|
||
session->community, session->community_len);
|
||
}
|
||
pdu->community_len = session->community_len;
|
||
}
|
||
#endif /* !NETSNMP_NO_ZEROLENGTH_COMMUNITY */
|
||
|
||
DEBUGMSGTL(("snmp_send", "Building SNMPv%ld message...\n",
|
||
(1 + pdu->version)));
|
||
#ifdef NETSNMP_USE_REVERSE_ASNENCODING
|
||
if (!(pdu->flags & UCD_MSG_FLAG_FORWARD_ENCODE)) {
|
||
DEBUGPRINTPDUTYPE("send", pdu->command);
|
||
rc = snmp_pdu_realloc_rbuild(pkt, pkt_len, offset, pdu);
|
||
if (rc == 0) {
|
||
return -1;
|
||
}
|
||
|
||
DEBUGDUMPHEADER("send", "Community String");
|
||
rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL |
|
||
ASN_PRIMITIVE |
|
||
ASN_OCTET_STR),
|
||
pdu->community,
|
||
pdu->community_len);
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return -1;
|
||
}
|
||
|
||
|
||
/*
|
||
* Store the version field.
|
||
*/
|
||
DEBUGDUMPHEADER("send", "SNMP Version Number");
|
||
|
||
version = pdu->version;
|
||
rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL |
|
||
ASN_PRIMITIVE |
|
||
ASN_INTEGER),
|
||
(long *) &version,
|
||
sizeof(version));
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* Build the final sequence.
|
||
*/
|
||
#ifndef NETSNMP_DISABLE_SNMPV1
|
||
if (pdu->version == SNMP_VERSION_1) {
|
||
DEBUGDUMPSECTION("send", "SNMPv1 Message");
|
||
} else {
|
||
#endif
|
||
DEBUGDUMPSECTION("send", "SNMPv2c Message");
|
||
#ifndef NETSNMP_DISABLE_SNMPV1
|
||
}
|
||
#endif
|
||
rc = asn_realloc_rbuild_sequence(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_SEQUENCE |
|
||
ASN_CONSTRUCTOR),
|
||
*offset - start_offset);
|
||
DEBUGINDENTLESS();
|
||
|
||
if (rc == 0) {
|
||
return -1;
|
||
}
|
||
return 0;
|
||
} else {
|
||
|
||
#endif /* NETSNMP_USE_REVERSE_ASNENCODING */
|
||
/*
|
||
* Save current location and build SEQUENCE tag and length
|
||
* placeholder for SNMP message sequence
|
||
* (actual length will be inserted later)
|
||
*/
|
||
cp = asn_build_sequence(*pkt, pkt_len,
|
||
(u_char) (ASN_SEQUENCE |
|
||
ASN_CONSTRUCTOR), 0);
|
||
if (cp == NULL) {
|
||
return -1;
|
||
}
|
||
h0e = cp;
|
||
|
||
#ifndef NETSNMP_DISABLE_SNMPV1
|
||
if (pdu->version == SNMP_VERSION_1) {
|
||
DEBUGDUMPSECTION("send", "SNMPv1 Message");
|
||
} else {
|
||
#endif
|
||
DEBUGDUMPSECTION("send", "SNMPv2c Message");
|
||
#ifndef NETSNMP_DISABLE_SNMPV1
|
||
}
|
||
#endif
|
||
|
||
/*
|
||
* store the version field
|
||
*/
|
||
DEBUGDUMPHEADER("send", "SNMP Version Number");
|
||
|
||
version = pdu->version;
|
||
cp = asn_build_int(cp, pkt_len,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_INTEGER), (long *) &version,
|
||
sizeof(version));
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL)
|
||
return -1;
|
||
|
||
/*
|
||
* store the community string
|
||
*/
|
||
DEBUGDUMPHEADER("send", "Community String");
|
||
cp = asn_build_string(cp, pkt_len,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_OCTET_STR), pdu->community,
|
||
pdu->community_len);
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL)
|
||
return -1;
|
||
break;
|
||
|
||
#ifdef NETSNMP_USE_REVERSE_ASNENCODING
|
||
}
|
||
#endif /* NETSNMP_USE_REVERSE_ASNENCODING */
|
||
break;
|
||
#endif /* support for community based SNMP */
|
||
case SNMP_VERSION_2p:
|
||
case SNMP_VERSION_sec:
|
||
case SNMP_VERSION_2u:
|
||
case SNMP_VERSION_2star:
|
||
default:
|
||
session->s_snmp_errno = SNMPERR_BAD_VERSION;
|
||
return -1;
|
||
}
|
||
|
||
DEBUGPRINTPDUTYPE("send", pdu->command);
|
||
cp = snmp_pdu_build(pdu, cp, pkt_len);
|
||
DEBUGINDENTADD(-4); /* return from entire v1/v2c message */
|
||
if (cp == NULL)
|
||
return -1;
|
||
|
||
/*
|
||
* insert the actual length of the message sequence
|
||
*/
|
||
switch (pdu->version) {
|
||
#ifndef NETSNMP_DISABLE_SNMPV1
|
||
case SNMP_VERSION_1:
|
||
#endif
|
||
#ifndef NETSNMP_DISABLE_SNMPV2C
|
||
case SNMP_VERSION_2c:
|
||
#endif
|
||
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
|
||
asn_build_sequence(*pkt, &length,
|
||
(u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
|
||
cp - h0e);
|
||
break;
|
||
#endif /* support for community based SNMP */
|
||
|
||
case SNMP_VERSION_2p:
|
||
case SNMP_VERSION_sec:
|
||
case SNMP_VERSION_2u:
|
||
case SNMP_VERSION_2star:
|
||
default:
|
||
session->s_snmp_errno = SNMPERR_BAD_VERSION;
|
||
return -1;
|
||
}
|
||
*pkt_len = cp - *pkt;
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
snmp_build(u_char ** pkt, size_t * pkt_len, size_t * offset,
|
||
netsnmp_session * pss, netsnmp_pdu *pdu)
|
||
{
|
||
int rc;
|
||
rc = _snmp_build(pkt, pkt_len, offset, pss, pdu);
|
||
if (rc) {
|
||
if (!pss->s_snmp_errno) {
|
||
snmp_log(LOG_ERR, "snmp_build: unknown failure\n");
|
||
pss->s_snmp_errno = SNMPERR_BAD_ASN1_BUILD;
|
||
}
|
||
SET_SNMP_ERROR(pss->s_snmp_errno);
|
||
rc = -1;
|
||
}
|
||
return rc;
|
||
}
|
||
|
||
/*
|
||
* on error, returns NULL (likely an encoding problem).
|
||
*/
|
||
u_char *
|
||
snmp_pdu_build(netsnmp_pdu *pdu, u_char * cp, size_t * out_length)
|
||
{
|
||
u_char *h1, *h1e, *h2, *h2e, *save_ptr;
|
||
netsnmp_variable_list *vp, *save_vp = NULL;
|
||
size_t length, save_length;
|
||
|
||
length = *out_length;
|
||
/*
|
||
* Save current location and build PDU tag and length placeholder
|
||
* (actual length will be inserted later)
|
||
*/
|
||
h1 = cp;
|
||
cp = asn_build_sequence(cp, out_length, (u_char) pdu->command, 0);
|
||
if (cp == NULL)
|
||
return NULL;
|
||
h1e = cp;
|
||
|
||
/*
|
||
* store fields in the PDU preceding the variable-bindings sequence
|
||
*/
|
||
if (pdu->command != SNMP_MSG_TRAP) {
|
||
/*
|
||
* PDU is not an SNMPv1 trap
|
||
*/
|
||
|
||
DEBUGDUMPHEADER("send", "request_id");
|
||
/*
|
||
* request id
|
||
*/
|
||
cp = asn_build_int(cp, out_length,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_INTEGER), &pdu->reqid,
|
||
sizeof(pdu->reqid));
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL)
|
||
return NULL;
|
||
|
||
/*
|
||
* error status (getbulk non-repeaters)
|
||
*/
|
||
DEBUGDUMPHEADER("send", "error status");
|
||
cp = asn_build_int(cp, out_length,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_INTEGER), &pdu->errstat,
|
||
sizeof(pdu->errstat));
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL)
|
||
return NULL;
|
||
|
||
/*
|
||
* error index (getbulk max-repetitions)
|
||
*/
|
||
DEBUGDUMPHEADER("send", "error index");
|
||
cp = asn_build_int(cp, out_length,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_INTEGER), &pdu->errindex,
|
||
sizeof(pdu->errindex));
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL)
|
||
return NULL;
|
||
} else {
|
||
/*
|
||
* an SNMPv1 trap PDU
|
||
*/
|
||
|
||
/*
|
||
* enterprise
|
||
*/
|
||
DEBUGDUMPHEADER("send", "enterprise OBJID");
|
||
cp = asn_build_objid(cp, out_length,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_OBJECT_ID),
|
||
(oid *) pdu->enterprise,
|
||
pdu->enterprise_length);
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL)
|
||
return NULL;
|
||
|
||
/*
|
||
* agent-addr
|
||
*/
|
||
DEBUGDUMPHEADER("send", "agent Address");
|
||
cp = asn_build_string(cp, out_length,
|
||
(u_char) (ASN_IPADDRESS | ASN_PRIMITIVE),
|
||
(u_char *) pdu->agent_addr, 4);
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL)
|
||
return NULL;
|
||
|
||
/*
|
||
* generic trap
|
||
*/
|
||
DEBUGDUMPHEADER("send", "generic trap number");
|
||
cp = asn_build_int(cp, out_length,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_INTEGER),
|
||
(long *) &pdu->trap_type,
|
||
sizeof(pdu->trap_type));
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL)
|
||
return NULL;
|
||
|
||
/*
|
||
* specific trap
|
||
*/
|
||
DEBUGDUMPHEADER("send", "specific trap number");
|
||
cp = asn_build_int(cp, out_length,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
|
||
ASN_INTEGER),
|
||
(long *) &pdu->specific_type,
|
||
sizeof(pdu->specific_type));
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL)
|
||
return NULL;
|
||
|
||
/*
|
||
* timestamp
|
||
*/
|
||
DEBUGDUMPHEADER("send", "timestamp");
|
||
cp = asn_build_unsigned_int(cp, out_length,
|
||
(u_char) (ASN_TIMETICKS |
|
||
ASN_PRIMITIVE), &pdu->time,
|
||
sizeof(pdu->time));
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL)
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* Save current location and build SEQUENCE tag and length placeholder
|
||
* for variable-bindings sequence
|
||
* (actual length will be inserted later)
|
||
*/
|
||
h2 = cp;
|
||
cp = asn_build_sequence(cp, out_length,
|
||
(u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), 0);
|
||
if (cp == NULL)
|
||
return NULL;
|
||
h2e = cp;
|
||
|
||
/*
|
||
* Store variable-bindings
|
||
*/
|
||
DEBUGDUMPSECTION("send", "VarBindList");
|
||
for (vp = pdu->variables; vp; vp = vp->next_variable) {
|
||
/*
|
||
* if estimated getbulk response size exceeded packet max size,
|
||
* processing was stopped before bulk cache was filled and type
|
||
* was set to ASN_PRIV_STOP, indicating that the rest of the varbinds
|
||
* in the cache are empty and we can stop encoding them.
|
||
*/
|
||
if (ASN_PRIV_STOP == vp->type)
|
||
break;
|
||
|
||
/*
|
||
* save current ptr and length so that if we exceed the packet length
|
||
* encoding this varbind and this is a bulk response, we can drop
|
||
* the failed varbind (and any that follow it) and continue encoding
|
||
* the (shorter) bulk response.
|
||
*/
|
||
save_ptr = cp;
|
||
save_length = *out_length;
|
||
|
||
DEBUGDUMPSECTION("send", "VarBind");
|
||
cp = snmp_build_var_op(cp, vp->name, &vp->name_length, vp->type,
|
||
vp->val_len, (u_char *) vp->val.string,
|
||
out_length);
|
||
DEBUGINDENTLESS();
|
||
if (cp == NULL) {
|
||
if (save_vp && (pdu->flags & UCD_MSG_FLAG_BULK_TOOBIG)) {
|
||
DEBUGDUMPSECTION("send",
|
||
"VarBind would exceed packet size; dropped");
|
||
cp = save_ptr;
|
||
*out_length = save_length;
|
||
break;
|
||
} else
|
||
return NULL;
|
||
}
|
||
save_vp = vp;
|
||
}
|
||
DEBUGINDENTLESS();
|
||
|
||
/** did we run out of room? (should only happen for bulk reponses) */
|
||
if (vp && save_vp) {
|
||
save_vp->next_variable = NULL; /* truncate variable list */
|
||
/** count remaining varbinds in list, then free them */
|
||
save_vp = vp;
|
||
for(save_length = 0; save_vp; save_vp = save_vp->next_variable)
|
||
++save_length;
|
||
DEBUGMSGTL(("send", "trimmed %" NETSNMP_PRIz "d variables\n", save_length));
|
||
snmp_free_varbind(vp);
|
||
}
|
||
|
||
/*
|
||
* insert actual length of variable-bindings sequence
|
||
*/
|
||
asn_build_sequence(h2, &length,
|
||
(u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
|
||
cp - h2e);
|
||
|
||
/*
|
||
* insert actual length of PDU sequence
|
||
*/
|
||
asn_build_sequence(h1, &length, (u_char) pdu->command, cp - h1e);
|
||
|
||
return cp;
|
||
}
|
||
|
||
#ifdef NETSNMP_USE_REVERSE_ASNENCODING
|
||
/*
|
||
* On error, returns 0 (likely an encoding problem).
|
||
*/
|
||
int
|
||
snmp_pdu_realloc_rbuild(u_char ** pkt, size_t * pkt_len, size_t * offset,
|
||
netsnmp_pdu *pdu)
|
||
{
|
||
#ifndef VPCACHE_SIZE
|
||
#define VPCACHE_SIZE 50
|
||
#endif
|
||
netsnmp_variable_list *vpcache[VPCACHE_SIZE];
|
||
netsnmp_variable_list *vp, *tmpvp;
|
||
size_t start_offset = *offset;
|
||
int i, wrapped = 0, notdone, final, rc = 0;
|
||
|
||
DEBUGMSGTL(("snmp_pdu_realloc_rbuild", "starting\n"));
|
||
for (vp = pdu->variables, i = VPCACHE_SIZE - 1; vp;
|
||
vp = vp->next_variable, i--) {
|
||
/*
|
||
* if estimated getbulk response size exceeded packet max size,
|
||
* processing was stopped before bulk cache was filled and type
|
||
* was set to ASN_PRIV_STOP, indicating that the rest of the varbinds
|
||
* in the cache are empty and we can stop encoding them.
|
||
*/
|
||
if (ASN_PRIV_STOP == vp->type)
|
||
break;
|
||
if (i < 0) {
|
||
wrapped = notdone = 1;
|
||
i = VPCACHE_SIZE - 1;
|
||
DEBUGMSGTL(("snmp_pdu_realloc_rbuild", "wrapped\n"));
|
||
}
|
||
vpcache[i] = vp;
|
||
}
|
||
final = i + 1;
|
||
|
||
do {
|
||
for (i = final; i < VPCACHE_SIZE; i++) {
|
||
vp = vpcache[i];
|
||
DEBUGDUMPSECTION("send", "VarBind");
|
||
rc = snmp_realloc_rbuild_var_op(pkt, pkt_len, offset, 1,
|
||
vp->name, &vp->name_length,
|
||
vp->type,
|
||
(u_char *) vp->val.string,
|
||
vp->val_len);
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
DEBUGINDENTLESS();
|
||
if (wrapped) {
|
||
notdone = 1;
|
||
for (i = 0; i < final; i++) {
|
||
vp = vpcache[i];
|
||
DEBUGDUMPSECTION("send", "VarBind");
|
||
rc = snmp_realloc_rbuild_var_op(pkt, pkt_len, offset, 1,
|
||
vp->name, &vp->name_length,
|
||
vp->type,
|
||
(u_char *) vp->val.string,
|
||
vp->val_len);
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
if (final == 0) {
|
||
tmpvp = vpcache[VPCACHE_SIZE - 1];
|
||
} else {
|
||
tmpvp = vpcache[final - 1];
|
||
}
|
||
wrapped = 0;
|
||
|
||
for (vp = pdu->variables, i = VPCACHE_SIZE - 1;
|
||
vp && vp != tmpvp; vp = vp->next_variable, i--) {
|
||
if (i < 0) {
|
||
wrapped = 1;
|
||
i = VPCACHE_SIZE - 1;
|
||
DEBUGMSGTL(("snmp_pdu_realloc_rbuild", "wrapped\n"));
|
||
}
|
||
vpcache[i] = vp;
|
||
}
|
||
final = i + 1;
|
||
} else {
|
||
notdone = 0;
|
||
}
|
||
} while (notdone);
|
||
|
||
/*
|
||
* Save current location and build SEQUENCE tag and length placeholder for
|
||
* variable-bindings sequence (actual length will be inserted later).
|
||
*/
|
||
|
||
rc = asn_realloc_rbuild_sequence(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_SEQUENCE |
|
||
ASN_CONSTRUCTOR),
|
||
*offset - start_offset);
|
||
|
||
/*
|
||
* Store fields in the PDU preceding the variable-bindings sequence.
|
||
*/
|
||
if (pdu->command != SNMP_MSG_TRAP) {
|
||
/*
|
||
* Error index (getbulk max-repetitions).
|
||
*/
|
||
DEBUGDUMPHEADER("send", "error index");
|
||
rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
|
||
| ASN_INTEGER),
|
||
&pdu->errindex, sizeof(pdu->errindex));
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Error status (getbulk non-repeaters).
|
||
*/
|
||
DEBUGDUMPHEADER("send", "error status");
|
||
rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
|
||
| ASN_INTEGER),
|
||
&pdu->errstat, sizeof(pdu->errstat));
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Request ID.
|
||
*/
|
||
DEBUGDUMPHEADER("send", "request_id");
|
||
rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
|
||
| ASN_INTEGER), &pdu->reqid,
|
||
sizeof(pdu->reqid));
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
} else {
|
||
/*
|
||
* An SNMPv1 trap PDU.
|
||
*/
|
||
|
||
/*
|
||
* Timestamp.
|
||
*/
|
||
DEBUGDUMPHEADER("send", "timestamp");
|
||
rc = asn_realloc_rbuild_unsigned_int(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_TIMETICKS |
|
||
ASN_PRIMITIVE),
|
||
&pdu->time,
|
||
sizeof(pdu->time));
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Specific trap.
|
||
*/
|
||
DEBUGDUMPHEADER("send", "specific trap number");
|
||
rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
|
||
| ASN_INTEGER),
|
||
(long *) &pdu->specific_type,
|
||
sizeof(pdu->specific_type));
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Generic trap.
|
||
*/
|
||
DEBUGDUMPHEADER("send", "generic trap number");
|
||
rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
|
||
| ASN_INTEGER),
|
||
(long *) &pdu->trap_type,
|
||
sizeof(pdu->trap_type));
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Agent-addr.
|
||
*/
|
||
DEBUGDUMPHEADER("send", "agent Address");
|
||
rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_IPADDRESS |
|
||
ASN_PRIMITIVE),
|
||
(u_char *) pdu->agent_addr, 4);
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Enterprise.
|
||
*/
|
||
DEBUGDUMPHEADER("send", "enterprise OBJID");
|
||
rc = asn_realloc_rbuild_objid(pkt, pkt_len, offset, 1,
|
||
(u_char) (ASN_UNIVERSAL |
|
||
ASN_PRIMITIVE |
|
||
ASN_OBJECT_ID),
|
||
(oid *) pdu->enterprise,
|
||
pdu->enterprise_length);
|
||
DEBUGINDENTLESS();
|
||
if (rc == 0) {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Build the PDU sequence.
|
||
*/
|
||
rc = asn_realloc_rbuild_sequence(pkt, pkt_len, offset, 1,
|
||
(u_char) pdu->command,
|
||
*offset - start_offset);
|
||
return rc;
|
||
}
|
||
#endif /* NETSNMP_USE_REVERSE_ASNENCODING */
|
||
|
||
/*
|
||
* Parses the packet received to determine version, either directly
|
||
* from packets version field or inferred from ASN.1 construct.
|
||
*/
|
||
static int
|
||
snmp_parse_version(u_char * data, size_t length)
|
||
{
|
||
u_char type;
|
||
long version = SNMPERR_BAD_VERSION;
|
||
|
||
data = asn_parse_sequence(data, &length, &type,
|
||
(ASN_SEQUENCE | ASN_CONSTRUCTOR), "version");
|
||
if (data) {
|
||
DEBUGDUMPHEADER("recv", "SNMP Version");
|
||
data =
|
||
asn_parse_int(data, &length, &type, &version, sizeof(version));
|
||
DEBUGINDENTLESS();
|
||
if (!data || type != ASN_INTEGER) {
|
||
return SNMPERR_BAD_VERSION;
|
||
}
|
||
}
|
||
return version;
|
||
}
|
||
|
||
|
||
int
|
||
snmpv3_parse(netsnmp_pdu *pdu,
|
||
u_char * data,
|
||
size_t * length,
|
||
u_char ** after_header, netsnmp_session * sess)
|
||
{
|
||
u_char type, msg_flags;
|
||
long ver, msg_sec_model;
|
||
size_t max_size_response;
|
||
u_char tmp_buf[SNMP_MAX_MSG_SIZE];
|
||
size_t tmp_buf_len;
|
||
u_char pdu_buf[SNMP_MAX_MSG_SIZE];
|
||
u_char *mallocbuf = NULL;
|
||
size_t pdu_buf_len = SNMP_MAX_MSG_SIZE;
|
||
u_char *sec_params;
|
||
u_char *msg_data;
|
||
u_char *cp;
|
||
size_t asn_len, msg_len;
|
||
int ret, ret_val;
|
||
struct snmp_secmod_def *sptr;
|
||
|
||
|
||
msg_data = data;
|
||
msg_len = *length;
|
||
|
||
|
||
/*
|
||
* message is an ASN.1 SEQUENCE
|
||
*/
|
||
DEBUGDUMPSECTION("recv", "SNMPv3 Message");
|
||
data = asn_parse_sequence(data, length, &type,
|
||
(ASN_SEQUENCE | ASN_CONSTRUCTOR), "message");
|
||
if (data == NULL) {
|
||
/*
|
||
* error msg detail is set
|
||
*/
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
DEBUGINDENTLESS();
|
||
return SNMPERR_ASN_PARSE_ERR;
|
||
}
|
||
|
||
/*
|
||
* parse msgVersion
|
||
*/
|
||
DEBUGDUMPHEADER("recv", "SNMP Version Number");
|
||
data = asn_parse_int(data, length, &type, &ver, sizeof(ver));
|
||
DEBUGINDENTLESS();
|
||
if (data == NULL) {
|
||
ERROR_MSG("bad parse of version");
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
DEBUGINDENTLESS();
|
||
return SNMPERR_ASN_PARSE_ERR;
|
||
}
|
||
pdu->version = ver;
|
||
|
||
/*
|
||
* parse msgGlobalData sequence
|
||
*/
|
||
cp = data;
|
||
asn_len = *length;
|
||
DEBUGDUMPSECTION("recv", "msgGlobalData");
|
||
data = asn_parse_sequence(data, &asn_len, &type,
|
||
(ASN_SEQUENCE | ASN_CONSTRUCTOR),
|
||
"msgGlobalData");
|
||
if (data == NULL) {
|
||
/*
|
||
* error msg detail is set
|
||
*/
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
DEBUGINDENTADD(-4);
|
||
return SNMPERR_ASN_PARSE_ERR;
|
||
}
|
||
*length -= data - cp; /* subtract off the length of the header */
|
||
|
||
/*
|
||
* msgID
|
||
*/
|
||
DEBUGDUMPHEADER("recv", "msgID");
|
||
data =
|
||
asn_parse_int(data, length, &type, &pdu->msgid,
|
||
sizeof(pdu->msgid));
|
||
DEBUGINDENTLESS();
|
||
if (data == NULL || type != ASN_INTEGER) {
|
||
ERROR_MSG("error parsing msgID");
|
||
DEBUGINDENTADD(-4);
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
return SNMPERR_ASN_PARSE_ERR;
|
||
}
|
||
|
||
/*
|
||
* Check the msgID we received is a legal value. If not, then increment
|
||
* snmpInASNParseErrs and return the appropriate error (see RFC 2572,
|
||
* para. 7.2, section 2 -- note that a bad msgID means that the received
|
||
* message is NOT a serialiization of an SNMPv3Message, since the msgID
|
||
* field is out of bounds).
|
||
*/
|
||
|
||
if (pdu->msgid < 0 || pdu->msgid > SNMP_MAX_PACKET_LEN) {
|
||
snmp_log(LOG_ERR, "Received bad msgID (%ld %s %s).\n", pdu->msgid,
|
||
(pdu->msgid < 0) ? "<" : ">",
|
||
(pdu->msgid < 0) ? "0" : "2^31 - 1");
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
DEBUGINDENTADD(-4);
|
||
return SNMPERR_ASN_PARSE_ERR;
|
||
}
|
||
|
||
/*
|
||
* msgMaxSize
|
||
*/
|
||
DEBUGDUMPHEADER("recv:msgMaxSize", "msgMaxSize");
|
||
data = asn_parse_int(data, length, &type, &pdu->msgMaxSize,
|
||
sizeof(pdu->msgMaxSize));
|
||
DEBUGINDENTLESS();
|
||
if (data == NULL || type != ASN_INTEGER) {
|
||
ERROR_MSG("error parsing msgMaxSize");
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
DEBUGINDENTADD(-4);
|
||
return SNMPERR_ASN_PARSE_ERR;
|
||
}
|
||
|
||
/*
|
||
* Check the msgMaxSize we received is a legal value. If not, then
|
||
* increment snmpInASNParseErrs and return the appropriate error (see RFC
|
||
* 2572, para. 7.2, section 2 -- note that a bad msgMaxSize means that the
|
||
* received message is NOT a serialiization of an SNMPv3Message, since the
|
||
* msgMaxSize field is out of bounds).
|
||
*/
|
||
|
||
if (pdu->msgMaxSize < SNMP_MIN_MAX_LEN) {
|
||
snmp_log(LOG_ERR, "Received bad msgMaxSize (%lu < 484).\n",
|
||
pdu->msgMaxSize);
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
DEBUGINDENTADD(-4);
|
||
return SNMPERR_ASN_PARSE_ERR;
|
||
} else if (pdu->msgMaxSize > SNMP_MAX_PACKET_LEN) {
|
||
snmp_log(LOG_ERR, "Received bad msgMaxSize (%lu > 2^31 - 1).\n",
|
||
pdu->msgMaxSize);
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
DEBUGINDENTADD(-4);
|
||
return SNMPERR_ASN_PARSE_ERR;
|
||
} else {
|
||
DEBUGMSGTL(("snmpv3_parse:msgMaxSize", "msgMaxSize %lu received\n",
|
||
pdu->msgMaxSize));
|
||
/** don't increase max msg size if we've already got one */
|
||
if (sess->sndMsgMaxSize < pdu->msgMaxSize) {
|
||
DEBUGMSGTL(("snmpv3_parse:msgMaxSize",
|
||
"msgMaxSize greater than session max; reducing\n"));
|
||
pdu->msgMaxSize = sess->sndMsgMaxSize;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* msgFlags
|
||
*/
|
||
tmp_buf_len = SNMP_MAX_MSG_SIZE;
|
||
DEBUGDUMPHEADER("recv", "msgFlags");
|
||
data = asn_parse_string(data, length, &type, tmp_buf, &tmp_buf_len);
|
||
DEBUGINDENTLESS();
|
||
if (data == NULL || type != ASN_OCTET_STR || tmp_buf_len != 1) {
|
||
ERROR_MSG("error parsing msgFlags");
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
DEBUGINDENTADD(-4);
|
||
return SNMPERR_ASN_PARSE_ERR;
|
||
}
|
||
msg_flags = *tmp_buf;
|
||
if (msg_flags & SNMP_MSG_FLAG_RPRT_BIT)
|
||
pdu->flags |= SNMP_MSG_FLAG_RPRT_BIT;
|
||
else
|
||
pdu->flags &= (~SNMP_MSG_FLAG_RPRT_BIT);
|
||
|
||
/*
|
||
* msgSecurityModel
|
||
*/
|
||
DEBUGDUMPHEADER("recv", "msgSecurityModel");
|
||
data = asn_parse_int(data, length, &type, &msg_sec_model,
|
||
sizeof(msg_sec_model));
|
||
DEBUGINDENTADD(-4); /* return from global data indent */
|
||
if (data == NULL || type != ASN_INTEGER ||
|
||
msg_sec_model < 1 || msg_sec_model > 0x7fffffff) {
|
||
ERROR_MSG("error parsing msgSecurityModel");
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
DEBUGINDENTLESS();
|
||
return SNMPERR_ASN_PARSE_ERR;
|
||
}
|
||
sptr = find_sec_mod(msg_sec_model);
|
||
if (!sptr) {
|
||
snmp_log(LOG_WARNING, "unknown security model: %ld\n",
|
||
msg_sec_model);
|
||
snmp_increment_statistic(STAT_SNMPUNKNOWNSECURITYMODELS);
|
||
DEBUGINDENTLESS();
|
||
return SNMPERR_UNKNOWN_SEC_MODEL;
|
||
}
|
||
pdu->securityModel = msg_sec_model;
|
||
|
||
if (msg_flags & SNMP_MSG_FLAG_PRIV_BIT &&
|
||
!(msg_flags & SNMP_MSG_FLAG_AUTH_BIT)) {
|
||
ERROR_MSG("invalid message, illegal msgFlags");
|
||
snmp_increment_statistic(STAT_SNMPINVALIDMSGS);
|
||
DEBUGINDENTLESS();
|
||
return SNMPERR_INVALID_MSG;
|
||
}
|
||
pdu->securityLevel = ((msg_flags & SNMP_MSG_FLAG_AUTH_BIT)
|
||
? ((msg_flags & SNMP_MSG_FLAG_PRIV_BIT)
|
||
? SNMP_SEC_LEVEL_AUTHPRIV
|
||
: SNMP_SEC_LEVEL_AUTHNOPRIV)
|
||
: SNMP_SEC_LEVEL_NOAUTH);
|
||
/*
|
||
* end of msgGlobalData
|
||
*/
|
||
|
||
/*
|
||
* securtityParameters OCTET STRING begins after msgGlobalData
|
||
*/
|
||
sec_params = data;
|
||
pdu->contextEngineID = (u_char *) calloc(1, SNMP_MAX_ENG_SIZE);
|
||
pdu->contextEngineIDLen = SNMP_MAX_ENG_SIZE;
|
||
|
||
/*
|
||
* Note: there is no length limit on the msgAuthoritativeEngineID field,
|
||
* although we would EXPECT it to be limited to 32 (the SnmpEngineID TC
|
||
* limit). We'll use double that here to be on the safe side.
|
||
*/
|
||
|
||
pdu->securityEngineID = (u_char *) calloc(1, SNMP_MAX_ENG_SIZE * 2);
|
||
pdu->securityEngineIDLen = SNMP_MAX_ENG_SIZE * 2;
|
||
pdu->securityName = (char *) calloc(1, SNMP_MAX_SEC_NAME_SIZE);
|
||
pdu->securityNameLen = SNMP_MAX_SEC_NAME_SIZE;
|
||
|
||
if ((pdu->securityName == NULL) ||
|
||
(pdu->securityEngineID == NULL) ||
|
||
(pdu->contextEngineID == NULL)) {
|
||
return SNMPERR_MALLOC;
|
||
}
|
||
|
||
if (pdu_buf_len < msg_len
|
||
&& pdu->securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
|
||
/*
|
||
* space needed is larger than we have in the default buffer
|
||
*/
|
||
mallocbuf = (u_char *) calloc(1, msg_len);
|
||
pdu_buf_len = msg_len;
|
||
cp = mallocbuf;
|
||
} else {
|
||
memset(pdu_buf, 0, pdu_buf_len);
|
||
cp = pdu_buf;
|
||
}
|
||
|
||
DEBUGDUMPSECTION("recv", "SM msgSecurityParameters");
|
||
if (sptr->decode) {
|
||
struct snmp_secmod_incoming_params parms;
|
||
parms.msgProcModel = pdu->msgParseModel;
|
||
parms.maxMsgSize = pdu->msgMaxSize;
|
||
parms.secParams = sec_params;
|
||
parms.secModel = msg_sec_model;
|
||
parms.secLevel = pdu->securityLevel;
|
||
parms.wholeMsg = msg_data;
|
||
parms.wholeMsgLen = msg_len;
|
||
parms.secEngineID = pdu->securityEngineID;
|
||
parms.secEngineIDLen = &pdu->securityEngineIDLen;
|
||
parms.secName = pdu->securityName;
|
||
parms.secNameLen = &pdu->securityNameLen;
|
||
parms.scopedPdu = &cp;
|
||
parms.scopedPduLen = &pdu_buf_len;
|
||
parms.maxSizeResponse = &max_size_response;
|
||
parms.secStateRef = &pdu->securityStateRef;
|
||
parms.sess = sess;
|
||
parms.pdu = pdu;
|
||
parms.msg_flags = msg_flags;
|
||
ret_val = (*sptr->decode) (&parms);
|
||
} else {
|
||
SNMP_FREE(mallocbuf);
|
||
DEBUGINDENTLESS();
|
||
snmp_log(LOG_WARNING, "security service %ld can't decode packets\n",
|
||
msg_sec_model);
|
||
return (-1);
|
||
}
|
||
|
||
if (ret_val != SNMPERR_SUCCESS) {
|
||
DEBUGDUMPSECTION("recv", "ScopedPDU");
|
||
/*
|
||
* Parse as much as possible -- though I don't see the point? [jbpn].
|
||
*/
|
||
if (cp) {
|
||
cp = snmpv3_scopedPDU_parse(pdu, cp, &pdu_buf_len);
|
||
}
|
||
if (cp) {
|
||
DEBUGPRINTPDUTYPE("recv", *cp);
|
||
snmp_pdu_parse(pdu, cp, &pdu_buf_len);
|
||
DEBUGINDENTADD(-8);
|
||
} else {
|
||
DEBUGINDENTADD(-4);
|
||
}
|
||
|
||
SNMP_FREE(mallocbuf);
|
||
return ret_val;
|
||
}
|
||
|
||
/*
|
||
* parse plaintext ScopedPDU sequence
|
||
*/
|
||
*length = pdu_buf_len;
|
||
DEBUGDUMPSECTION("recv", "ScopedPDU");
|
||
data = snmpv3_scopedPDU_parse(pdu, cp, length);
|
||
if (data == NULL) {
|
||
snmp_log(LOG_WARNING, "security service %ld error parsing ScopedPDU\n",
|
||
msg_sec_model);
|
||
ERROR_MSG("error parsing PDU");
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
DEBUGINDENTADD(-4);
|
||
SNMP_FREE(mallocbuf);
|
||
return SNMPERR_ASN_PARSE_ERR;
|
||
}
|
||
|
||
/*
|
||
* parse the PDU.
|
||
*/
|
||
if (after_header != NULL) {
|
||
*after_header = data;
|
||
tmp_buf_len = *length;
|
||
}
|
||
|
||
DEBUGPRINTPDUTYPE("recv", *data);
|
||
ret = snmp_pdu_parse(pdu, data, length);
|
||
DEBUGINDENTADD(-8);
|
||
|
||
if (after_header != NULL) {
|
||
*length = tmp_buf_len;
|
||
}
|
||
|
||
if (ret != SNMPERR_SUCCESS) {
|
||
snmp_log(LOG_WARNING, "security service %ld error parsing ScopedPDU\n",
|
||
msg_sec_model);
|
||
ERROR_MSG("error parsing PDU");
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
SNMP_FREE(mallocbuf);
|
||
return SNMPERR_ASN_PARSE_ERR;
|
||
}
|
||
|
||
SNMP_FREE(mallocbuf);
|
||
return SNMPERR_SUCCESS;
|
||
} /* end snmpv3_parse() */
|
||
|
||
static void
|
||
free_securityStateRef(netsnmp_pdu* pdu)
|
||
{
|
||
struct snmp_secmod_def *sptr = find_sec_mod(pdu->securityModel);
|
||
if (sptr) {
|
||
if (sptr->pdu_free_state_ref) {
|
||
(*sptr->pdu_free_state_ref) (pdu->securityStateRef);
|
||
} else {
|
||
snmp_log(LOG_ERR,
|
||
"Security Model %d can't free state references\n",
|
||
pdu->securityModel);
|
||
}
|
||
} else {
|
||
snmp_log(LOG_ERR,
|
||
"Can't find security model to free ptr: %d\n",
|
||
pdu->securityModel);
|
||
}
|
||
pdu->securityStateRef = NULL;
|
||
}
|
||
|
||
#define ERROR_STAT_LENGTH 11
|
||
|
||
int
|
||
snmpv3_make_report(netsnmp_pdu *pdu, int error)
|
||
{
|
||
|
||
long ltmp;
|
||
static oid unknownSecurityLevel[] =
|
||
{ 1, 3, 6, 1, 6, 3, 15, 1, 1, 1, 0 };
|
||
static oid notInTimeWindow[] =
|
||
{ 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0 };
|
||
static oid unknownUserName[] =
|
||
{ 1, 3, 6, 1, 6, 3, 15, 1, 1, 3, 0 };
|
||
static oid unknownEngineID[] =
|
||
{ 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0 };
|
||
static oid wrongDigest[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 5, 0 };
|
||
static oid decryptionError[] =
|
||
{ 1, 3, 6, 1, 6, 3, 15, 1, 1, 6, 0 };
|
||
oid *err_var;
|
||
int err_var_len;
|
||
#ifndef NETSNMP_FEATURE_REMOVE_STATISTICS
|
||
int stat_ind;
|
||
#endif
|
||
|
||
switch (error) {
|
||
case SNMPERR_USM_UNKNOWNENGINEID:
|
||
#ifndef NETSNMP_FEATURE_REMOVE_STATISTICS
|
||
stat_ind = STAT_USMSTATSUNKNOWNENGINEIDS;
|
||
#endif /* !NETSNMP_FEATURE_REMOVE_STATISTICS */
|
||
err_var = unknownEngineID;
|
||
err_var_len = ERROR_STAT_LENGTH;
|
||
break;
|
||
case SNMPERR_USM_UNKNOWNSECURITYNAME:
|
||
#ifndef NETSNMP_FEATURE_REMOVE_STATISTICS
|
||
stat_ind = STAT_USMSTATSUNKNOWNUSERNAMES;
|
||
#endif /* !NETSNMP_FEATURE_REMOVE_STATISTICS */
|
||
err_var = unknownUserName;
|
||
err_var_len = ERROR_STAT_LENGTH;
|
||
break;
|
||
case SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL:
|
||
#ifndef NETSNMP_FEATURE_REMOVE_STATISTICS
|
||
stat_ind = STAT_USMSTATSUNSUPPORTEDSECLEVELS;
|
||
#endif /* !NETSNMP_FEATURE_REMOVE_STATISTICS */
|
||
err_var = unknownSecurityLevel;
|
||
err_var_len = ERROR_STAT_LENGTH;
|
||
break;
|
||
case SNMPERR_USM_AUTHENTICATIONFAILURE:
|
||
#ifndef NETSNMP_FEATURE_REMOVE_STATISTICS
|
||
stat_ind = STAT_USMSTATSWRONGDIGESTS;
|
||
#endif /* !NETSNMP_FEATURE_REMOVE_STATISTICS */
|
||
err_var = wrongDigest;
|
||
err_var_len = ERROR_STAT_LENGTH;
|
||
break;
|
||
case SNMPERR_USM_NOTINTIMEWINDOW:
|
||
#ifndef NETSNMP_FEATURE_REMOVE_STATISTICS
|
||
stat_ind = STAT_USMSTATSNOTINTIMEWINDOWS;
|
||
#endif /* !NETSNMP_FEATURE_REMOVE_STATISTICS */
|
||
err_var = notInTimeWindow;
|
||
err_var_len = ERROR_STAT_LENGTH;
|
||
break;
|
||
case SNMPERR_USM_DECRYPTIONERROR:
|
||
#ifndef NETSNMP_FEATURE_REMOVE_STATISTICS
|
||
stat_ind = STAT_USMSTATSDECRYPTIONERRORS;
|
||
#endif /* !NETSNMP_FEATURE_REMOVE_STATISTICS */
|
||
err_var = decryptionError;
|
||
err_var_len = ERROR_STAT_LENGTH;
|
||
break;
|
||
default:
|
||
return SNMPERR_GENERR;
|
||
}
|
||
|
||
snmp_free_varbind(pdu->variables); /* free the current varbind */
|
||
|
||
pdu->variables = NULL;
|
||
SNMP_FREE(pdu->securityEngineID);
|
||
pdu->securityEngineID =
|
||
snmpv3_generate_engineID(&pdu->securityEngineIDLen);
|
||
SNMP_FREE(pdu->contextEngineID);
|
||
pdu->contextEngineID =
|
||
snmpv3_generate_engineID(&pdu->contextEngineIDLen);
|
||
pdu->command = SNMP_MSG_REPORT;
|
||
pdu->errstat = 0;
|
||
pdu->errindex = 0;
|
||
SNMP_FREE(pdu->contextName);
|
||
pdu->contextName = strdup("");
|
||
pdu->contextNameLen = strlen(pdu->contextName);
|
||
|
||
/*
|
||
* reports shouldn't cache previous data.
|
||
*/
|
||
/*
|
||
* FIX - yes they should but USM needs to follow new EoP to determine
|
||
* which cached values to use
|
||
*/
|
||
if (pdu->securityStateRef) {
|
||
free_securityStateRef(pdu);
|
||
}
|
||
|
||
if (error == SNMPERR_USM_NOTINTIMEWINDOW) {
|
||
pdu->securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
|
||
} else {
|
||
pdu->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
|
||
}
|
||
|
||
/*
|
||
* find the appropriate error counter
|
||
*/
|
||
#ifndef NETSNMP_FEATURE_REMOVE_STATISTICS
|
||
ltmp = snmp_get_statistic(stat_ind);
|
||
#else /* !NETSNMP_FEATURE_REMOVE_STATISTICS */
|
||
ltmp = 1;
|
||
#endif /* !NETSNMP_FEATURE_REMOVE_STATISTICS */
|
||
|
||
/*
|
||
* return the appropriate error counter
|
||
*/
|
||
snmp_pdu_add_variable(pdu, err_var, err_var_len,
|
||
ASN_COUNTER, & ltmp, sizeof(ltmp));
|
||
|
||
return SNMPERR_SUCCESS;
|
||
} /* end snmpv3_make_report() */
|
||
|
||
|
||
int
|
||
snmpv3_get_report_type(netsnmp_pdu *pdu)
|
||
{
|
||
static oid snmpMPDStats[] = { 1, 3, 6, 1, 6, 3, 11, 2, 1 };
|
||
static oid targetStats[] = { 1, 3, 6, 1, 6, 3, 12, 1 };
|
||
static oid usmStats[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1 };
|
||
netsnmp_variable_list *vp;
|
||
int rpt_type = SNMPERR_UNKNOWN_REPORT;
|
||
|
||
if (pdu == NULL || pdu->variables == NULL)
|
||
return rpt_type;
|
||
vp = pdu->variables;
|
||
/* MPD or USM based report statistics objects have the same length prefix
|
||
* so the actual statistics OID will have this length,
|
||
* plus one subidentifier for the scalar MIB object itself,
|
||
* and one for the instance subidentifier
|
||
*/
|
||
if (vp->name_length == REPORT_STATS_LEN + 2) {
|
||
if (memcmp(snmpMPDStats, vp->name, REPORT_STATS_LEN * sizeof(oid)) == 0) {
|
||
switch (vp->name[REPORT_STATS_LEN]) {
|
||
case REPORT_snmpUnknownSecurityModels_NUM:
|
||
rpt_type = SNMPERR_UNKNOWN_SEC_MODEL;
|
||
break;
|
||
case REPORT_snmpInvalidMsgs_NUM:
|
||
rpt_type = SNMPERR_INVALID_MSG;
|
||
break;
|
||
case REPORT_snmpUnknownPDUHandlers_NUM:
|
||
rpt_type = SNMPERR_BAD_VERSION;
|
||
break;
|
||
}
|
||
} else if (memcmp(usmStats, vp->name, REPORT_STATS_LEN * sizeof(oid)) == 0) {
|
||
switch (vp->name[REPORT_STATS_LEN]) {
|
||
case REPORT_usmStatsUnsupportedSecLevels_NUM:
|
||
rpt_type = SNMPERR_UNSUPPORTED_SEC_LEVEL;
|
||
break;
|
||
case REPORT_usmStatsNotInTimeWindows_NUM:
|
||
rpt_type = SNMPERR_NOT_IN_TIME_WINDOW;
|
||
break;
|
||
case REPORT_usmStatsUnknownUserNames_NUM:
|
||
rpt_type = SNMPERR_UNKNOWN_USER_NAME;
|
||
break;
|
||
case REPORT_usmStatsUnknownEngineIDs_NUM:
|
||
rpt_type = SNMPERR_UNKNOWN_ENG_ID;
|
||
break;
|
||
case REPORT_usmStatsWrongDigests_NUM:
|
||
rpt_type = SNMPERR_AUTHENTICATION_FAILURE;
|
||
break;
|
||
case REPORT_usmStatsDecryptionErrors_NUM:
|
||
rpt_type = SNMPERR_DECRYPTION_ERR;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
/* Context-based report statistics from the Target MIB are similar
|
||
* but the OID prefix has a different length
|
||
*/
|
||
if (vp->name_length == REPORT_STATS_LEN2 + 2) {
|
||
if (memcmp(targetStats, vp->name, REPORT_STATS_LEN2 * sizeof(oid)) == 0) {
|
||
switch (vp->name[REPORT_STATS_LEN2]) {
|
||
case REPORT_snmpUnavailableContexts_NUM:
|
||
rpt_type = SNMPERR_BAD_CONTEXT;
|
||
break;
|
||
case REPORT_snmpUnknownContexts_NUM:
|
||
rpt_type = SNMPERR_BAD_CONTEXT;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
DEBUGMSGTL(("report", "Report type: %d\n", rpt_type));
|
||
return rpt_type;
|
||
}
|
||
|
||
/*
|
||
* Parses the packet received on the input session, and places the data into
|
||
* the input pdu. length is the length of the input packet.
|
||
* If any errors are encountered, -1 or USM error is returned.
|
||
* Otherwise, a 0 is returned.
|
||
*/
|
||
static int
|
||
_snmp_parse(void *sessp,
|
||
netsnmp_session * session,
|
||
netsnmp_pdu *pdu, u_char * data, size_t length)
|
||
{
|
||
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
|
||
u_char community[COMMUNITY_MAX_LEN];
|
||
size_t community_length = COMMUNITY_MAX_LEN;
|
||
#endif
|
||
int result = -1;
|
||
|
||
static oid snmpEngineIDoid[] = { 1,3,6,1,6,3,10,2,1,1,0};
|
||
static size_t snmpEngineIDoid_len = 11;
|
||
|
||
static char ourEngineID[SNMP_SEC_PARAM_BUF_SIZE];
|
||
static size_t ourEngineID_len = sizeof(ourEngineID);
|
||
|
||
netsnmp_pdu *pdu2 = NULL;
|
||
|
||
session->s_snmp_errno = 0;
|
||
session->s_errno = 0;
|
||
|
||
/*
|
||
* Ensure all incoming PDUs have a unique means of identification
|
||
* (This is not restricted to AgentX handling,
|
||
* though that is where the need becomes visible)
|
||
*/
|
||
pdu->transid = snmp_get_next_transid();
|
||
|
||
if (session->version != SNMP_DEFAULT_VERSION) {
|
||
pdu->version = session->version;
|
||
} else {
|
||
pdu->version = snmp_parse_version(data, length);
|
||
}
|
||
|
||
switch (pdu->version) {
|
||
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
|
||
#ifndef NETSNMP_DISABLE_SNMPV1
|
||
case SNMP_VERSION_1:
|
||
#endif
|
||
#ifndef NETSNMP_DISABLE_SNMPV2C
|
||
case SNMP_VERSION_2c:
|
||
#endif
|
||
NETSNMP_RUNTIME_PROTOCOL_CHECK_V1V2(pdu->version,unsupported_version);
|
||
DEBUGMSGTL(("snmp_api", "Parsing SNMPv%ld message...\n",
|
||
(1 + pdu->version)));
|
||
|
||
/*
|
||
* authenticates message and returns length if valid
|
||
*/
|
||
#ifndef NETSNMP_DISABLE_SNMPV1
|
||
if (pdu->version == SNMP_VERSION_1) {
|
||
DEBUGDUMPSECTION("recv", "SNMPv1 message\n");
|
||
} else {
|
||
#endif
|
||
DEBUGDUMPSECTION("recv", "SNMPv2c message\n");
|
||
#ifndef NETSNMP_DISABLE_SNMPV1
|
||
}
|
||
#endif
|
||
data = snmp_comstr_parse(data, &length,
|
||
community, &community_length,
|
||
&pdu->version);
|
||
if (data == NULL)
|
||
return -1;
|
||
|
||
if (pdu->version != session->version &&
|
||
session->version != SNMP_DEFAULT_VERSION) {
|
||
session->s_snmp_errno = SNMPERR_BAD_VERSION;
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* maybe get the community string.
|
||
*/
|
||
pdu->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
|
||
pdu->securityModel =
|
||
#ifndef NETSNMP_DISABLE_SNMPV1
|
||
(pdu->version == SNMP_VERSION_1) ? SNMP_SEC_MODEL_SNMPv1 :
|
||
#endif
|
||
SNMP_SEC_MODEL_SNMPv2c;
|
||
SNMP_FREE(pdu->community);
|
||
pdu->community_len = 0;
|
||
pdu->community = (u_char *) 0;
|
||
if (community_length) {
|
||
pdu->community_len = community_length;
|
||
pdu->community = (u_char *) malloc(community_length);
|
||
if (pdu->community == NULL) {
|
||
session->s_snmp_errno = SNMPERR_MALLOC;
|
||
return -1;
|
||
}
|
||
memmove(pdu->community, community, community_length);
|
||
}
|
||
if (session->authenticator) {
|
||
data = session->authenticator(data, &length,
|
||
community, community_length);
|
||
if (data == NULL) {
|
||
session->s_snmp_errno = SNMPERR_AUTHENTICATION_FAILURE;
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
DEBUGDUMPSECTION("recv", "PDU");
|
||
result = snmp_pdu_parse(pdu, data, &length);
|
||
if (result < 0) {
|
||
/*
|
||
* This indicates a parse error.
|
||
*/
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
}
|
||
DEBUGINDENTADD(-6);
|
||
break;
|
||
#endif /* support for community based SNMP */
|
||
|
||
case SNMP_VERSION_3:
|
||
NETSNMP_RUNTIME_PROTOCOL_CHECK_V3(SNMP_VERSION_3,unsupported_version);
|
||
result = snmpv3_parse(pdu, data, &length, NULL, session);
|
||
DEBUGMSGTL(("snmp_parse",
|
||
"Parsed SNMPv3 message (secName:%s, secLevel:%s): %s\n",
|
||
pdu->securityName, secLevelName[pdu->securityLevel],
|
||
snmp_api_errstring(result)));
|
||
|
||
if (result) {
|
||
struct snmp_secmod_def *secmod =
|
||
find_sec_mod(pdu->securityModel);
|
||
if (!sessp) {
|
||
session->s_snmp_errno = result;
|
||
} else {
|
||
/*
|
||
* Call the security model to special handle any errors
|
||
*/
|
||
|
||
if (secmod && secmod->handle_report) {
|
||
struct session_list *slp = (struct session_list *) sessp;
|
||
(*secmod->handle_report)(sessp, slp->transport, session,
|
||
result, pdu);
|
||
}
|
||
}
|
||
if (pdu->securityStateRef != NULL) {
|
||
if (secmod && secmod->pdu_free_state_ref) {
|
||
secmod->pdu_free_state_ref(pdu->securityStateRef);
|
||
pdu->securityStateRef = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Implement RFC5343 here for two reasons:
|
||
1) From a security perspective it handles this otherwise
|
||
always approved request earlier. It bypasses the need
|
||
for authorization to the snmpEngineID scalar, which is
|
||
what is what RFC3415 appendix A species as ok. Note
|
||
that we haven't bypassed authentication since if there
|
||
was an authentication eror it would have been handled
|
||
above in the if(result) part at the lastet.
|
||
2) From an application point of view if we let this request
|
||
get all the way to the application, it'd require that
|
||
all application types supporting discovery also fire up
|
||
a minimal agent in order to handle just this request
|
||
which seems like overkill. Though there is no other
|
||
application types that currently need discovery (NRs
|
||
accept notifications from contextEngineIDs that derive
|
||
from the NO not the NR). Also a lame excuse for doing
|
||
it here.
|
||
3) Less important technically, but the net-snmp agent
|
||
doesn't currently handle registrations of different
|
||
engineIDs either and it would have been a lot more work
|
||
to implement there since we'd need to support that
|
||
first. :-/ Supporting multiple context engineIDs should
|
||
be done anyway, so it's not a valid excuse here.
|
||
4) There is a lot less to do if we trump the agent at this
|
||
point; IE, the agent does a lot more unnecessary
|
||
processing when the only thing that should ever be in
|
||
this context by definition is the single scalar.
|
||
*/
|
||
|
||
/* special RFC5343 engineID discovery engineID check */
|
||
if (!netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_NO_DISCOVERY) &&
|
||
SNMP_MSG_RESPONSE != pdu->command &&
|
||
NULL != pdu->contextEngineID &&
|
||
pdu->contextEngineIDLen == 5 &&
|
||
pdu->contextEngineID[0] == 0x80 &&
|
||
pdu->contextEngineID[1] == 0x00 &&
|
||
pdu->contextEngineID[2] == 0x00 &&
|
||
pdu->contextEngineID[3] == 0x00 &&
|
||
pdu->contextEngineID[4] == 0x06) {
|
||
|
||
/* define a result so it doesn't get past us at this point
|
||
and gets dropped by future parts of the stack */
|
||
result = SNMPERR_JUST_A_CONTEXT_PROBE;
|
||
|
||
DEBUGMSGTL(("snmpv3_contextid", "starting context ID discovery\n"));
|
||
/* ensure exactly one variable */
|
||
if (NULL != pdu->variables &&
|
||
NULL == pdu->variables->next_variable &&
|
||
|
||
/* if it's a GET, match it exactly */
|
||
((SNMP_MSG_GET == pdu->command &&
|
||
snmp_oid_compare(snmpEngineIDoid,
|
||
snmpEngineIDoid_len,
|
||
pdu->variables->name,
|
||
pdu->variables->name_length) == 0)
|
||
/* if it's a GETNEXT ensure it's less than the engineID oid */
|
||
||
|
||
(SNMP_MSG_GETNEXT == pdu->command &&
|
||
snmp_oid_compare(snmpEngineIDoid,
|
||
snmpEngineIDoid_len,
|
||
pdu->variables->name,
|
||
pdu->variables->name_length) > 0)
|
||
)) {
|
||
|
||
DEBUGMSGTL(("snmpv3_contextid",
|
||
" One correct variable found\n"));
|
||
|
||
/* Note: we're explictly not handling a GETBULK. Deal. */
|
||
|
||
/* set up the response */
|
||
pdu2 = snmp_clone_pdu(pdu);
|
||
|
||
/* free the current varbind */
|
||
snmp_free_varbind(pdu2->variables);
|
||
|
||
/* set the variables */
|
||
pdu2->variables = NULL;
|
||
pdu2->command = SNMP_MSG_RESPONSE;
|
||
pdu2->errstat = 0;
|
||
pdu2->errindex = 0;
|
||
|
||
ourEngineID_len =
|
||
snmpv3_get_engineID((u_char*)ourEngineID, ourEngineID_len);
|
||
if (0 != ourEngineID_len) {
|
||
|
||
DEBUGMSGTL(("snmpv3_contextid",
|
||
" responding with our engineID\n"));
|
||
|
||
snmp_pdu_add_variable(pdu2,
|
||
snmpEngineIDoid, snmpEngineIDoid_len,
|
||
ASN_OCTET_STR,
|
||
ourEngineID, ourEngineID_len);
|
||
|
||
/* send the response */
|
||
if (0 == snmp_sess_send(sessp, pdu2)) {
|
||
|
||
DEBUGMSGTL(("snmpv3_contextid",
|
||
" sent it off!\n"));
|
||
|
||
snmp_free_pdu(pdu2);
|
||
|
||
snmp_log(LOG_ERR, "sending a response to the context engineID probe failed\n");
|
||
}
|
||
} else {
|
||
snmp_log(LOG_ERR, "failed to get our own engineID!\n");
|
||
}
|
||
} else {
|
||
snmp_log(LOG_WARNING,
|
||
"received an odd context engineID probe\n");
|
||
}
|
||
}
|
||
|
||
break;
|
||
case SNMPERR_BAD_VERSION:
|
||
ERROR_MSG("error parsing snmp message version");
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
session->s_snmp_errno = SNMPERR_BAD_VERSION;
|
||
break;
|
||
|
||
unsupported_version: /* goto label */
|
||
case SNMP_VERSION_sec:
|
||
case SNMP_VERSION_2u:
|
||
case SNMP_VERSION_2star:
|
||
case SNMP_VERSION_2p:
|
||
default:
|
||
ERROR_MSG("unsupported snmp message version");
|
||
snmp_increment_statistic(STAT_SNMPINBADVERSIONS);
|
||
|
||
/*
|
||
* need better way to determine OS independent
|
||
* INT32_MAX value, for now hardcode
|
||
*/
|
||
if (pdu->version < 0 || pdu->version > 2147483647) {
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
}
|
||
session->s_snmp_errno = SNMPERR_BAD_VERSION;
|
||
break;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
static int
|
||
snmp_parse(void *sessp,
|
||
netsnmp_session * pss,
|
||
netsnmp_pdu *pdu, u_char * data, size_t length)
|
||
{
|
||
int rc;
|
||
|
||
rc = _snmp_parse(sessp, pss, pdu, data, length);
|
||
if (rc) {
|
||
if (!pss->s_snmp_errno) {
|
||
pss->s_snmp_errno = SNMPERR_BAD_PARSE;
|
||
}
|
||
SET_SNMP_ERROR(pss->s_snmp_errno);
|
||
}
|
||
|
||
return rc;
|
||
}
|
||
|
||
int
|
||
snmp_pdu_parse(netsnmp_pdu *pdu, u_char * data, size_t * length)
|
||
{
|
||
u_char type;
|
||
u_char msg_type;
|
||
u_char *var_val;
|
||
size_t len;
|
||
size_t four;
|
||
netsnmp_variable_list *vp = NULL, *vplast = NULL;
|
||
oid objid[MAX_OID_LEN];
|
||
u_char *p;
|
||
|
||
/*
|
||
* Get the PDU type
|
||
*/
|
||
data = asn_parse_header(data, length, &msg_type);
|
||
if (data == NULL)
|
||
return -1;
|
||
DEBUGMSGTL(("dumpv_recv"," Command %s\n", snmp_pdu_type(msg_type)));
|
||
pdu->command = msg_type;
|
||
pdu->flags &= (~UCD_MSG_FLAG_RESPONSE_PDU);
|
||
|
||
/*
|
||
* get the fields in the PDU preceding the variable-bindings sequence
|
||
*/
|
||
switch (pdu->command) {
|
||
case SNMP_MSG_TRAP:
|
||
/*
|
||
* enterprise
|
||
*/
|
||
pdu->enterprise_length = MAX_OID_LEN;
|
||
data = asn_parse_objid(data, length, &type, objid,
|
||
&pdu->enterprise_length);
|
||
if (data == NULL)
|
||
return -1;
|
||
pdu->enterprise =
|
||
(oid *) malloc(pdu->enterprise_length * sizeof(oid));
|
||
if (pdu->enterprise == NULL) {
|
||
return -1;
|
||
}
|
||
memmove(pdu->enterprise, objid,
|
||
pdu->enterprise_length * sizeof(oid));
|
||
|
||
/*
|
||
* agent-addr
|
||
*/
|
||
four = 4;
|
||
data = asn_parse_string(data, length, &type,
|
||
(u_char *) pdu->agent_addr, &four);
|
||
if (data == NULL)
|
||
return -1;
|
||
|
||
/*
|
||
* generic trap
|
||
*/
|
||
data = asn_parse_int(data, length, &type, (long *) &pdu->trap_type,
|
||
sizeof(pdu->trap_type));
|
||
if (data == NULL)
|
||
return -1;
|
||
/*
|
||
* specific trap
|
||
*/
|
||
data =
|
||
asn_parse_int(data, length, &type,
|
||
(long *) &pdu->specific_type,
|
||
sizeof(pdu->specific_type));
|
||
if (data == NULL)
|
||
return -1;
|
||
|
||
/*
|
||
* timestamp
|
||
*/
|
||
data = asn_parse_unsigned_int(data, length, &type, &pdu->time,
|
||
sizeof(pdu->time));
|
||
if (data == NULL)
|
||
return -1;
|
||
|
||
break;
|
||
|
||
case SNMP_MSG_RESPONSE:
|
||
case SNMP_MSG_REPORT:
|
||
pdu->flags |= UCD_MSG_FLAG_RESPONSE_PDU;
|
||
/* FALL THROUGH */
|
||
|
||
case SNMP_MSG_TRAP2:
|
||
case SNMP_MSG_INFORM:
|
||
#ifndef NETSNMP_NOTIFY_ONLY
|
||
case SNMP_MSG_GET:
|
||
case SNMP_MSG_GETNEXT:
|
||
case SNMP_MSG_GETBULK:
|
||
#endif /* ! NETSNMP_NOTIFY_ONLY */
|
||
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
||
case SNMP_MSG_SET:
|
||
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
|
||
/*
|
||
* PDU is not an SNMPv1 TRAP
|
||
*/
|
||
|
||
/*
|
||
* request id
|
||
*/
|
||
DEBUGDUMPHEADER("recv", "request_id");
|
||
data = asn_parse_int(data, length, &type, &pdu->reqid,
|
||
sizeof(pdu->reqid));
|
||
DEBUGINDENTLESS();
|
||
if (data == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* error status (getbulk non-repeaters)
|
||
*/
|
||
DEBUGDUMPHEADER("recv", "error status");
|
||
data = asn_parse_int(data, length, &type, &pdu->errstat,
|
||
sizeof(pdu->errstat));
|
||
DEBUGINDENTLESS();
|
||
if (data == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* error index (getbulk max-repetitions)
|
||
*/
|
||
DEBUGDUMPHEADER("recv", "error index");
|
||
data = asn_parse_int(data, length, &type, &pdu->errindex,
|
||
sizeof(pdu->errindex));
|
||
DEBUGINDENTLESS();
|
||
if (data == NULL) {
|
||
return -1;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
snmp_log(LOG_ERR, "Bad PDU type received: 0x%.2x\n", pdu->command);
|
||
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* get header for variable-bindings sequence
|
||
*/
|
||
DEBUGDUMPSECTION("recv", "VarBindList");
|
||
data = asn_parse_sequence(data, length, &type,
|
||
(ASN_SEQUENCE | ASN_CONSTRUCTOR),
|
||
"varbinds");
|
||
if (data == NULL)
|
||
goto fail;
|
||
|
||
/*
|
||
* get each varBind sequence
|
||
*/
|
||
while ((int) *length > 0) {
|
||
vp = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);
|
||
if (NULL == vp)
|
||
goto fail;
|
||
|
||
vp->name_length = MAX_OID_LEN;
|
||
DEBUGDUMPSECTION("recv", "VarBind");
|
||
data = snmp_parse_var_op(data, objid, &vp->name_length, &vp->type,
|
||
&vp->val_len, &var_val, length);
|
||
if (data == NULL)
|
||
goto fail;
|
||
if (snmp_set_var_objid(vp, objid, vp->name_length))
|
||
goto fail;
|
||
|
||
len = SNMP_MAX_PACKET_LEN;
|
||
DEBUGDUMPHEADER("recv", "Value");
|
||
switch ((short) vp->type) {
|
||
case ASN_INTEGER:
|
||
vp->val.integer = (long *) vp->buf;
|
||
vp->val_len = sizeof(long);
|
||
p = asn_parse_int(var_val, &len, &vp->type,
|
||
(long *) vp->val.integer,
|
||
sizeof(*vp->val.integer));
|
||
if (!p)
|
||
goto fail;
|
||
break;
|
||
case ASN_COUNTER:
|
||
case ASN_GAUGE:
|
||
case ASN_TIMETICKS:
|
||
case ASN_UINTEGER:
|
||
vp->val.integer = (long *) vp->buf;
|
||
vp->val_len = sizeof(u_long);
|
||
p = asn_parse_unsigned_int(var_val, &len, &vp->type,
|
||
(u_long *) vp->val.integer,
|
||
vp->val_len);
|
||
if (!p)
|
||
goto fail;
|
||
break;
|
||
#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
|
||
case ASN_OPAQUE_COUNTER64:
|
||
case ASN_OPAQUE_U64:
|
||
#endif /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
|
||
case ASN_COUNTER64:
|
||
vp->val.counter64 = (struct counter64 *) vp->buf;
|
||
vp->val_len = sizeof(struct counter64);
|
||
p = asn_parse_unsigned_int64(var_val, &len, &vp->type,
|
||
(struct counter64 *) vp->val.
|
||
counter64, vp->val_len);
|
||
if (!p)
|
||
goto fail;
|
||
break;
|
||
#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
|
||
case ASN_OPAQUE_FLOAT:
|
||
vp->val.floatVal = (float *) vp->buf;
|
||
vp->val_len = sizeof(float);
|
||
p = asn_parse_float(var_val, &len, &vp->type,
|
||
vp->val.floatVal, vp->val_len);
|
||
if (!p)
|
||
goto fail;
|
||
break;
|
||
case ASN_OPAQUE_DOUBLE:
|
||
vp->val.doubleVal = (double *) vp->buf;
|
||
vp->val_len = sizeof(double);
|
||
p = asn_parse_double(var_val, &len, &vp->type,
|
||
vp->val.doubleVal, vp->val_len);
|
||
if (!p)
|
||
goto fail;
|
||
break;
|
||
case ASN_OPAQUE_I64:
|
||
vp->val.counter64 = (struct counter64 *) vp->buf;
|
||
vp->val_len = sizeof(struct counter64);
|
||
p = asn_parse_signed_int64(var_val, &len, &vp->type,
|
||
(struct counter64 *) vp->val.counter64,
|
||
sizeof(*vp->val.counter64));
|
||
|
||
if (!p)
|
||
goto fail;
|
||
break;
|
||
#endif /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
|
||
case ASN_IPADDRESS:
|
||
if (vp->val_len != 4)
|
||
goto fail;
|
||
/* fallthrough */
|
||
case ASN_OCTET_STR:
|
||
case ASN_OPAQUE:
|
||
case ASN_NSAP:
|
||
if (vp->val_len < sizeof(vp->buf)) {
|
||
vp->val.string = (u_char *) vp->buf;
|
||
} else {
|
||
vp->val.string = (u_char *) malloc(vp->val_len);
|
||
}
|
||
if (vp->val.string == NULL) {
|
||
goto fail;
|
||
}
|
||
p = asn_parse_string(var_val, &len, &vp->type, vp->val.string,
|
||
&vp->val_len);
|
||
if (!p)
|
||
goto fail;
|
||
break;
|
||
case ASN_OBJECT_ID:
|
||
vp->val_len = MAX_OID_LEN;
|
||
p = asn_parse_objid(var_val, &len, &vp->type, objid, &vp->val_len);
|
||
if (!p)
|
||
goto fail;
|
||
vp->val_len *= sizeof(oid);
|
||
vp->val.objid = (oid *) malloc(vp->val_len);
|
||
if (vp->val.objid == NULL) {
|
||
goto fail;
|
||
}
|
||
memmove(vp->val.objid, objid, vp->val_len);
|
||
break;
|
||
case SNMP_NOSUCHOBJECT:
|
||
case SNMP_NOSUCHINSTANCE:
|
||
case SNMP_ENDOFMIBVIEW:
|
||
case ASN_NULL:
|
||
break;
|
||
case ASN_BIT_STR:
|
||
vp->val.bitstring = (u_char *) malloc(vp->val_len);
|
||
if (vp->val.bitstring == NULL) {
|
||
goto fail;
|
||
}
|
||
p = asn_parse_bitstring(var_val, &len, &vp->type,
|
||
vp->val.bitstring, &vp->val_len);
|
||
if (!p)
|
||
goto fail;
|
||
break;
|
||
default:
|
||
snmp_log(LOG_ERR, "bad type returned (%x)\n", vp->type);
|
||
goto fail;
|
||
break;
|
||
}
|
||
DEBUGINDENTADD(-4);
|
||
|
||
if (NULL == vplast) {
|
||
pdu->variables = vp;
|
||
} else {
|
||
vplast->next_variable = vp;
|
||
}
|
||
vplast = vp;
|
||
vp = NULL;
|
||
}
|
||
return 0;
|
||
|
||
fail:
|
||
{
|
||
const char *errstr = snmp_api_errstring(SNMPERR_SUCCESS);
|
||
DEBUGMSGTL(("recv", "error while parsing VarBindList:%s\n", errstr));
|
||
}
|
||
/** if we were parsing a var, remove it from the pdu and free it */
|
||
if (vp)
|
||
snmp_free_var(vp);
|
||
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* snmp v3 utility function to parse into the scopedPdu. stores contextName
|
||
* and contextEngineID in pdu struct. Also stores pdu->command (handy for
|
||
* Report generation).
|
||
*
|
||
* returns pointer to begining of PDU or NULL on error.
|
||
*/
|
||
u_char *
|
||
snmpv3_scopedPDU_parse(netsnmp_pdu *pdu, u_char * cp, size_t * length)
|
||
{
|
||
u_char tmp_buf[SNMP_MAX_MSG_SIZE];
|
||
size_t tmp_buf_len;
|
||
u_char type;
|
||
size_t asn_len;
|
||
u_char *data;
|
||
|
||
pdu->command = 0; /* initialize so we know if it got parsed */
|
||
asn_len = *length;
|
||
data = asn_parse_sequence(cp, &asn_len, &type,
|
||
(ASN_SEQUENCE | ASN_CONSTRUCTOR),
|
||
"plaintext scopedPDU");
|
||
if (data == NULL) {
|
||
return NULL;
|
||
}
|
||
*length -= data - cp;
|
||
|
||
/*
|
||
* contextEngineID from scopedPdu
|
||
*/
|
||
DEBUGDUMPHEADER("recv", "contextEngineID");
|
||
data = asn_parse_string(data, length, &type, pdu->contextEngineID,
|
||
&pdu->contextEngineIDLen);
|
||
DEBUGINDENTLESS();
|
||
if (data == NULL) {
|
||
ERROR_MSG("error parsing contextEngineID from scopedPdu");
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* parse contextName from scopedPdu
|
||
*/
|
||
tmp_buf_len = SNMP_MAX_CONTEXT_SIZE;
|
||
DEBUGDUMPHEADER("recv", "contextName");
|
||
data = asn_parse_string(data, length, &type, tmp_buf, &tmp_buf_len);
|
||
DEBUGINDENTLESS();
|
||
if (data == NULL) {
|
||
ERROR_MSG("error parsing contextName from scopedPdu");
|
||
return NULL;
|
||
}
|
||
|
||
if (tmp_buf_len) {
|
||
pdu->contextName = (char *) malloc(tmp_buf_len);
|
||
memmove(pdu->contextName, tmp_buf, tmp_buf_len);
|
||
pdu->contextNameLen = tmp_buf_len;
|
||
} else {
|
||
pdu->contextName = strdup("");
|
||
pdu->contextNameLen = 0;
|
||
}
|
||
if (pdu->contextName == NULL) {
|
||
ERROR_MSG("error copying contextName from scopedPdu");
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* Get the PDU type
|
||
*/
|
||
asn_len = *length;
|
||
cp = asn_parse_header(data, &asn_len, &type);
|
||
if (cp == NULL)
|
||
return NULL;
|
||
|
||
pdu->command = type;
|
||
|
||
return data;
|
||
}
|
||
|
||
|
||
/* ===========================================================================
|
||
*
|
||
* build pdu packet
|
||
*/
|
||
int
|
||
netsnmp_build_packet(struct snmp_internal_session *isp, netsnmp_session *sp,
|
||
netsnmp_pdu *pdu, u_char **pktbuf_p,
|
||
size_t *pktbuf_len_p, u_char **pkt_p, size_t *len_p)
|
||
{
|
||
size_t offset = 0;
|
||
int result;
|
||
|
||
if (isp && isp->hook_realloc_build) {
|
||
result = isp->hook_realloc_build(sp, pdu, pktbuf_p, pktbuf_len_p,
|
||
&offset);
|
||
|
||
*pkt_p = *pktbuf_p;
|
||
*len_p = offset;
|
||
} else if (isp && isp->hook_build) {
|
||
*pkt_p = *pktbuf_p;
|
||
*len_p = *pktbuf_len_p;
|
||
result = isp->hook_build(sp, pdu, *pktbuf_p, len_p);
|
||
} else {
|
||
#ifdef NETSNMP_USE_REVERSE_ASNENCODING
|
||
if (!(pdu->flags & UCD_MSG_FLAG_FORWARD_ENCODE)) {
|
||
result = snmp_build(pktbuf_p, pktbuf_len_p, &offset, sp, pdu);
|
||
*pkt_p = *pktbuf_p + *pktbuf_len_p - offset;
|
||
*len_p = offset;
|
||
} else {
|
||
#endif
|
||
*pkt_p = *pktbuf_p;
|
||
*len_p = *pktbuf_len_p;
|
||
result = snmp_build(pktbuf_p, len_p, &offset, sp, pdu);
|
||
#ifdef NETSNMP_USE_REVERSE_ASNENCODING
|
||
}
|
||
#endif
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
int
|
||
_build_initial_pdu_packet(struct session_list *slp, netsnmp_pdu *pdu, int bulk)
|
||
{
|
||
netsnmp_session *session;
|
||
struct snmp_internal_session *isp;
|
||
netsnmp_transport *transport = NULL;
|
||
u_char *pktbuf = NULL, *packet = NULL;
|
||
size_t pktbuf_len = 0, offset = 0, length = 0, orig_length = 0;
|
||
int result, orig_count = 0, curr_count = 0;
|
||
|
||
if (slp == NULL) {
|
||
return SNMPERR_GENERR;
|
||
}
|
||
session = slp->session;
|
||
|
||
isp = slp->internal;
|
||
transport = slp->transport;
|
||
if (!session || !isp || !transport) {
|
||
DEBUGMSGTL(("sess_async_send", "send fail: closing...\n"));
|
||
return SNMPERR_GENERR;
|
||
}
|
||
|
||
if (pdu == NULL) {
|
||
session->s_snmp_errno = SNMPERR_NULL_PDU;
|
||
return SNMPERR_GENERR;
|
||
}
|
||
|
||
SNMP_FREE(isp->obuf); /* should already be NULL */
|
||
|
||
session->s_snmp_errno = 0;
|
||
session->s_errno = 0;
|
||
|
||
/*
|
||
* Check/setup the version.
|
||
*/
|
||
if (pdu->version == SNMP_DEFAULT_VERSION) {
|
||
if (session->version == SNMP_DEFAULT_VERSION) {
|
||
session->s_snmp_errno = SNMPERR_BAD_VERSION;
|
||
return SNMPERR_GENERR;
|
||
}
|
||
pdu->version = session->version;
|
||
} else if (session->version == SNMP_DEFAULT_VERSION) {
|
||
/*
|
||
* It's OK
|
||
*/
|
||
} else if (pdu->version != session->version) {
|
||
/*
|
||
* ENHANCE: we should support multi-lingual sessions
|
||
*/
|
||
session->s_snmp_errno = SNMPERR_BAD_VERSION;
|
||
return SNMPERR_GENERR;
|
||
}
|
||
if (NETSNMP_RUNTIME_PROTOCOL_SKIP(pdu->version)) {
|
||
DEBUGMSGTL(("sess_async_send", "version disabled at runtime\n"));
|
||
session->s_snmp_errno = SNMPERR_BAD_VERSION;
|
||
return SNMPERR_GENERR;
|
||
}
|
||
|
||
/*
|
||
* do we expect a response?
|
||
*/
|
||
switch (pdu->command) {
|
||
|
||
case SNMP_MSG_RESPONSE:
|
||
case SNMP_MSG_TRAP:
|
||
case SNMP_MSG_TRAP2:
|
||
case SNMP_MSG_REPORT:
|
||
case AGENTX_MSG_CLEANUPSET:
|
||
case AGENTX_MSG_RESPONSE:
|
||
pdu->flags &= ~UCD_MSG_FLAG_EXPECT_RESPONSE;
|
||
break;
|
||
|
||
default:
|
||
pdu->flags |= UCD_MSG_FLAG_EXPECT_RESPONSE;
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* check to see if we need a v3 engineID probe
|
||
*/
|
||
if ((pdu->version == SNMP_VERSION_3) &&
|
||
(pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE) &&
|
||
(session->securityEngineIDLen == 0) &&
|
||
(0 == (session->flags & SNMP_FLAGS_DONT_PROBE))) {
|
||
int rc;
|
||
DEBUGMSGTL(("snmpv3_build", "delayed probe for engineID\n"));
|
||
rc = snmpv3_engineID_probe(slp, session);
|
||
if (rc == 0)
|
||
return 0; /* s_snmp_errno already set */
|
||
}
|
||
|
||
/*
|
||
* determine max packet size
|
||
*/
|
||
if (pdu->msgMaxSize == 0) {
|
||
pdu->msgMaxSize = netsnmp_max_send_msg_size();
|
||
if (pdu->msgMaxSize > transport->msgMaxSize)
|
||
pdu->msgMaxSize = transport->msgMaxSize;
|
||
if (pdu->msgMaxSize > session->sndMsgMaxSize)
|
||
pdu->msgMaxSize = session->sndMsgMaxSize;
|
||
}
|
||
netsnmp_assert(pdu->msgMaxSize > 0);
|
||
|
||
/*
|
||
* allocate initial packet buffer. Buffer will be grown as needed
|
||
* while building the packet.
|
||
*/
|
||
pktbuf_len = SNMP_MIN_MAX_LEN;
|
||
if ((pktbuf = (u_char *)malloc(pktbuf_len)) == NULL) {
|
||
DEBUGMSGTL(("sess_async_send",
|
||
"couldn't malloc initial packet buffer\n"));
|
||
session->s_snmp_errno = SNMPERR_MALLOC;
|
||
return SNMPERR_MALLOC;
|
||
}
|
||
|
||
#if TEMPORARILY_DISABLED
|
||
/*
|
||
* NULL variable are allowed in certain PDU types.
|
||
* In particular, SNMPv3 engineID probes are of this form.
|
||
* There is an internal PDU flag to indicate that this
|
||
* is acceptable, but until the construction of engineID
|
||
* probes can be amended to set this flag, we'll simply
|
||
* skip this test altogether.
|
||
*/
|
||
if (pdu->variables == NULL) {
|
||
switch (pdu->command) {
|
||
#ifndef NETSNMP_NO_WRITE_SUPPORT
|
||
case SNMP_MSG_SET:
|
||
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
|
||
case SNMP_MSG_GET:
|
||
case SNMP_MSG_GETNEXT:
|
||
case SNMP_MSG_GETBULK:
|
||
case SNMP_MSG_RESPONSE:
|
||
case SNMP_MSG_TRAP2:
|
||
case SNMP_MSG_REPORT:
|
||
case SNMP_MSG_INFORM:
|
||
session->s_snmp_errno = snmp_errno = SNMPERR_NO_VARS;
|
||
return SNMPERR_NO_VARS;
|
||
case SNMP_MSG_TRAP:
|
||
break;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
|
||
/*
|
||
* Build the message to send. If a bulk response is too big, switch to
|
||
* forward encoding and set a flag to drop varbinds to make it fit.
|
||
*/
|
||
do {
|
||
packet = pktbuf;
|
||
length = offset = 0;
|
||
result = netsnmp_build_packet(isp, session, pdu, &pktbuf, &pktbuf_len,
|
||
&packet, &length);
|
||
if (0 != result)
|
||
break;
|
||
|
||
if (orig_count) { /* 2nd pass, see how many varbinds remain */
|
||
curr_count = count_varbinds(pdu->variables);
|
||
DEBUGMSGTL(("sess_async_send", " vb count: %d -> %d\n", orig_count,
|
||
curr_count));
|
||
DEBUGMSGTL(("sess_async_send", " pdu_len: %" NETSNMP_PRIz "d -> %" NETSNMP_PRIz "d (max %ld)\n",
|
||
orig_length, length, pdu->msgMaxSize));
|
||
}
|
||
|
||
/** if length is less than max size, we're done (success). */
|
||
if (length <= pdu->msgMaxSize)
|
||
break;
|
||
|
||
/** packet too big. if this is not a bulk request, we're done (err). */
|
||
if (!bulk) {
|
||
session->s_snmp_errno = SNMPERR_TOO_LONG;
|
||
break;
|
||
}
|
||
|
||
/** rebuild bulk response with truncation and fixed size */
|
||
pdu->flags |= UCD_MSG_FLAG_FORWARD_ENCODE | UCD_MSG_FLAG_BULK_TOOBIG;
|
||
pktbuf_len = pdu->msgMaxSize;
|
||
|
||
/** save original number of vabinds & length */
|
||
if (0 == orig_count) {
|
||
curr_count = orig_count = count_varbinds(pdu->variables);
|
||
orig_length = length;
|
||
}
|
||
|
||
} while(1);
|
||
|
||
DEBUGMSGTL(("sess_async_send",
|
||
"final pktbuf_len after building packet %" NETSNMP_PRIz "u\n",
|
||
pktbuf_len));
|
||
if (curr_count != orig_count)
|
||
DEBUGMSGTL(("sess_async_send",
|
||
"sending %d of %d varbinds (-%d) from bulk response\n",
|
||
curr_count, orig_count, orig_count - curr_count));
|
||
|
||
if (length > pdu->msgMaxSize) {
|
||
DEBUGMSGTL(("sess_async_send",
|
||
"length of packet (%" NETSNMP_PRIz "u) exceeded pdu maximum (%lu)\n",
|
||
length, pdu->msgMaxSize));
|
||
netsnmp_assert(SNMPERR_TOO_LONG == session->s_snmp_errno);
|
||
}
|
||
|
||
if ((SNMPERR_TOO_LONG == session->s_snmp_errno) || (result < 0)) {
|
||
DEBUGMSGTL(("sess_async_send", "encoding failure\n"));
|
||
SNMP_FREE(pktbuf);
|
||
return SNMPERR_GENERR;
|
||
}
|
||
|
||
isp->obuf = pktbuf;
|
||
isp->obuf_size = pktbuf_len;
|
||
isp->opacket = packet;
|
||
isp->opacket_len = length;
|
||
|
||
return SNMPERR_SUCCESS;
|
||
}
|
||
|
||
/*
|
||
* These functions send PDUs using an active session:
|
||
* snmp_send - traditional API, no callback
|
||
* snmp_async_send - traditional API, with callback
|
||
* snmp_sess_send - single session API, no callback
|
||
* snmp_sess_async_send - single session API, with callback
|
||
*
|
||
* Call snmp_build to create a serialized packet (the pdu).
|
||
* If necessary, set some of the pdu data from the
|
||
* session defaults.
|
||
* If there is an expected response for this PDU,
|
||
* queue a corresponding request on the list
|
||
* of outstanding requests for this session,
|
||
* and store the callback vectors in the request.
|
||
*
|
||
* Send the pdu to the target identified by this session.
|
||
* Return on success:
|
||
* The request id of the pdu is returned, and the pdu is freed.
|
||
* Return on failure:
|
||
* Zero (0) is returned.
|
||
* The caller must call snmp_free_pdu if 0 is returned.
|
||
*/
|
||
int
|
||
snmp_send(netsnmp_session * session, netsnmp_pdu *pdu)
|
||
{
|
||
return snmp_async_send(session, pdu, NULL, NULL);
|
||
}
|
||
|
||
int
|
||
snmp_sess_send(void *sessp, netsnmp_pdu *pdu)
|
||
{
|
||
return snmp_sess_async_send(sessp, pdu, NULL, NULL);
|
||
}
|
||
|
||
int
|
||
snmp_async_send(netsnmp_session * session,
|
||
netsnmp_pdu *pdu, snmp_callback callback, void *cb_data)
|
||
{
|
||
void *sessp = snmp_sess_pointer(session);
|
||
return snmp_sess_async_send(sessp, pdu, callback, cb_data);
|
||
}
|
||
|
||
static int
|
||
_sess_async_send(void *sessp,
|
||
netsnmp_pdu *pdu, snmp_callback callback, void *cb_data)
|
||
{
|
||
struct session_list *slp = (struct session_list *) sessp;
|
||
netsnmp_session *session;
|
||
struct snmp_internal_session *isp;
|
||
netsnmp_transport *transport = NULL;
|
||
int result;
|
||
long reqid;
|
||
|
||
if (slp == NULL || NULL == slp->session || NULL ==slp->internal ||
|
||
NULL == slp->transport) {
|
||
return 0;
|
||
}
|
||
|
||
session = slp->session;
|
||
isp = slp->internal;
|
||
transport = slp->transport;
|
||
|
||
if (NULL == isp->opacket) {
|
||
result = _build_initial_pdu_packet(slp, pdu, 0);
|
||
if ((SNMPERR_SUCCESS != result) || (NULL == isp->opacket)) {
|
||
if (callback) {
|
||
switch (session->s_snmp_errno) {
|
||
/*
|
||
* some of these probably don't make sense here, but
|
||
* it's a rough first cut.
|
||
*/
|
||
case SNMPERR_BAD_ENG_ID:
|
||
case SNMPERR_BAD_SEC_LEVEL:
|
||
case SNMPERR_UNKNOWN_SEC_MODEL:
|
||
case SNMPERR_UNKNOWN_ENG_ID:
|
||
case SNMPERR_UNKNOWN_USER_NAME:
|
||
case SNMPERR_UNSUPPORTED_SEC_LEVEL:
|
||
case SNMPERR_AUTHENTICATION_FAILURE:
|
||
case SNMPERR_NOT_IN_TIME_WINDOW:
|
||
case SNMPERR_USM_GENERICERROR:
|
||
case SNMPERR_USM_UNKNOWNSECURITYNAME:
|
||
case SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL:
|
||
case SNMPERR_USM_ENCRYPTIONERROR:
|
||
case SNMPERR_USM_AUTHENTICATIONFAILURE:
|
||
case SNMPERR_USM_PARSEERROR:
|
||
case SNMPERR_USM_UNKNOWNENGINEID:
|
||
case SNMPERR_USM_NOTINTIMEWINDOW:
|
||
callback(NETSNMP_CALLBACK_OP_SEC_ERROR, session,
|
||
pdu->reqid, pdu, cb_data);
|
||
break;
|
||
case SNMPERR_TIMEOUT: /* engineID probe timed out */
|
||
callback(NETSNMP_CALLBACK_OP_TIMED_OUT, session,
|
||
pdu->reqid, pdu, cb_data);
|
||
break;
|
||
default:
|
||
callback(NETSNMP_CALLBACK_OP_SEND_FAILED, session,
|
||
pdu->reqid, pdu, cb_data);
|
||
break;
|
||
}
|
||
}
|
||
/** no packet to send?? */
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Send the message.
|
||
*/
|
||
|
||
DEBUGMSGTL(("sess_process_packet", "sending message id#%ld reqid#%ld len %"
|
||
NETSNMP_PRIz "u\n", pdu->msgid, pdu->reqid, isp->opacket_len));
|
||
result = netsnmp_transport_send(transport, isp->opacket, isp->opacket_len,
|
||
&(pdu->transport_data),
|
||
&(pdu->transport_data_length));
|
||
|
||
SNMP_FREE(isp->obuf);
|
||
isp->opacket = NULL; /* opacket was in obuf, so no free needed */
|
||
isp->opacket_len = 0;
|
||
|
||
if (result < 0) {
|
||
session->s_snmp_errno = SNMPERR_BAD_SENDTO;
|
||
session->s_errno = errno;
|
||
if (callback)
|
||
callback(NETSNMP_CALLBACK_OP_SEND_FAILED, session,
|
||
pdu->reqid, pdu, cb_data);
|
||
return 0;
|
||
}
|
||
|
||
reqid = pdu->reqid;
|
||
|
||
/*
|
||
* Bug 2387: 0 is a valid request id, so since reqid is used as a return
|
||
* code with 0 meaning an error, set reqid to 1 if there is no error. This
|
||
* does not affect the request id in the packet and fixes a memory leak
|
||
* for incoming PDUs with a request id of 0. This could cause some
|
||
* confusion if the caller is expecting the request id to match the
|
||
* return code, as the documentation states it will. Most example code
|
||
* just checks for non-zero, so hopefully this wont be an issue.
|
||
*/
|
||
if (0 == reqid && (SNMPERR_SUCCESS == session->s_snmp_errno))
|
||
++reqid;
|
||
|
||
/*
|
||
* Add to pending requests list if we expect a response.
|
||
*/
|
||
if (pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE) {
|
||
netsnmp_request_list *rp;
|
||
struct timeval tv;
|
||
|
||
rp = (netsnmp_request_list *) calloc(1,
|
||
sizeof(netsnmp_request_list));
|
||
if (rp == NULL) {
|
||
session->s_snmp_errno = SNMPERR_GENERR;
|
||
return 0;
|
||
}
|
||
|
||
netsnmp_get_monotonic_clock(&tv);
|
||
rp->pdu = pdu;
|
||
rp->request_id = pdu->reqid;
|
||
rp->message_id = pdu->msgid;
|
||
rp->callback = callback;
|
||
rp->cb_data = cb_data;
|
||
rp->retries = 0;
|
||
if (pdu->flags & UCD_MSG_FLAG_PDU_TIMEOUT) {
|
||
rp->timeout = pdu->time * 1000000L;
|
||
} else {
|
||
rp->timeout = session->timeout;
|
||
}
|
||
rp->timeM = tv;
|
||
tv.tv_usec += rp->timeout;
|
||
tv.tv_sec += tv.tv_usec / 1000000L;
|
||
tv.tv_usec %= 1000000L;
|
||
rp->expireM = tv;
|
||
|
||
/*
|
||
* XX lock should be per session !
|
||
*/
|
||
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
if (isp->requestsEnd) {
|
||
rp->next_request = isp->requestsEnd->next_request;
|
||
isp->requestsEnd->next_request = rp;
|
||
isp->requestsEnd = rp;
|
||
} else {
|
||
rp->next_request = isp->requests;
|
||
isp->requests = rp;
|
||
isp->requestsEnd = rp;
|
||
}
|
||
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
} else {
|
||
/*
|
||
* No response expected...
|
||
*/
|
||
if (reqid) {
|
||
/*
|
||
* Free v1 or v2 TRAP PDU iff no error
|
||
*/
|
||
snmp_free_pdu(pdu);
|
||
}
|
||
}
|
||
|
||
return reqid;
|
||
}
|
||
|
||
int
|
||
snmp_sess_async_send(void *sessp,
|
||
netsnmp_pdu *pdu,
|
||
snmp_callback callback, void *cb_data)
|
||
{
|
||
int rc;
|
||
|
||
if (sessp == NULL) {
|
||
snmp_errno = SNMPERR_BAD_SESSION; /*MTCRITICAL_RESOURCE */
|
||
return (0);
|
||
}
|
||
/*
|
||
* send pdu
|
||
*/
|
||
rc = _sess_async_send(sessp, pdu, callback, cb_data);
|
||
if (rc == 0) {
|
||
struct session_list *psl;
|
||
netsnmp_session *pss;
|
||
psl = (struct session_list *) sessp;
|
||
pss = psl->session;
|
||
SET_SNMP_ERROR(pss->s_snmp_errno);
|
||
}
|
||
return rc;
|
||
}
|
||
|
||
|
||
/*
|
||
* Frees the variable and any malloc'd data associated with it.
|
||
*/
|
||
void
|
||
snmp_free_var_internals(netsnmp_variable_list * var)
|
||
{
|
||
if (!var)
|
||
return;
|
||
|
||
if (var->name != var->name_loc)
|
||
SNMP_FREE(var->name);
|
||
if (var->val.string != var->buf)
|
||
SNMP_FREE(var->val.string);
|
||
if (var->data) {
|
||
if (var->dataFreeHook) {
|
||
var->dataFreeHook(var->data);
|
||
var->data = NULL;
|
||
} else {
|
||
SNMP_FREE(var->data);
|
||
}
|
||
}
|
||
}
|
||
|
||
void
|
||
snmp_free_var(netsnmp_variable_list * var)
|
||
{
|
||
snmp_free_var_internals(var);
|
||
free((char *) var);
|
||
}
|
||
|
||
void
|
||
snmp_free_varbind(netsnmp_variable_list * var)
|
||
{
|
||
netsnmp_variable_list *ptr;
|
||
while (var) {
|
||
ptr = var->next_variable;
|
||
snmp_free_var(var);
|
||
var = ptr;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Frees the pdu and any malloc'd data associated with it.
|
||
*/
|
||
void
|
||
snmp_free_pdu(netsnmp_pdu *pdu)
|
||
{
|
||
struct snmp_secmod_def *sptr;
|
||
|
||
if (!pdu)
|
||
return;
|
||
|
||
/*
|
||
* If the command field is empty, that probably indicates
|
||
* that this PDU structure has already been freed.
|
||
* Log a warning and return (rather than freeing things again)
|
||
*
|
||
* Note that this does not pick up dual-frees where the
|
||
* memory is set to random junk, which is probably more serious.
|
||
*
|
||
* rks: while this is a good idea, there are two problems.
|
||
* 1) agentx sets command to 0 in some cases
|
||
* 2) according to Wes, a bad decode of a v3 message could
|
||
* result in a 0 at this offset.
|
||
* so I'm commenting it out until a better solution is found.
|
||
* note that I'm leaving the memset, below....
|
||
*
|
||
if (pdu->command == 0) {
|
||
snmp_log(LOG_WARNING, "snmp_free_pdu probably called twice\n");
|
||
return;
|
||
}
|
||
*/
|
||
if ((sptr = find_sec_mod(pdu->securityModel)) != NULL &&
|
||
sptr->pdu_free != NULL) {
|
||
(*sptr->pdu_free) (pdu);
|
||
}
|
||
snmp_free_varbind(pdu->variables);
|
||
SNMP_FREE(pdu->enterprise);
|
||
SNMP_FREE(pdu->community);
|
||
SNMP_FREE(pdu->contextEngineID);
|
||
SNMP_FREE(pdu->securityEngineID);
|
||
SNMP_FREE(pdu->contextName);
|
||
SNMP_FREE(pdu->securityName);
|
||
SNMP_FREE(pdu->transport_data);
|
||
memset(pdu, 0, sizeof(netsnmp_pdu));
|
||
free((char *) pdu);
|
||
}
|
||
|
||
netsnmp_pdu *
|
||
snmp_create_sess_pdu(netsnmp_transport *transport, void *opaque,
|
||
size_t olength)
|
||
{
|
||
netsnmp_pdu *pdu = (netsnmp_pdu *)calloc(1, sizeof(netsnmp_pdu));
|
||
if (pdu == NULL) {
|
||
DEBUGMSGTL(("sess_process_packet", "can't malloc space for PDU\n"));
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* Save the transport-level data specific to this reception (e.g. UDP
|
||
* source address).
|
||
*/
|
||
|
||
pdu->transport_data = opaque;
|
||
pdu->transport_data_length = olength;
|
||
pdu->tDomain = transport->domain;
|
||
pdu->tDomainLen = transport->domain_length;
|
||
return pdu;
|
||
}
|
||
|
||
|
||
/*
|
||
* This function parses a packet into a PDU
|
||
*/
|
||
static netsnmp_pdu *
|
||
_sess_process_packet_parse_pdu(void *sessp, netsnmp_session * sp,
|
||
struct snmp_internal_session *isp,
|
||
netsnmp_transport *transport,
|
||
void *opaque, int olength,
|
||
u_char * packetptr, int length)
|
||
{
|
||
netsnmp_pdu *pdu;
|
||
int ret = 0;
|
||
int dump = 0, filter = 0;
|
||
|
||
debug_indent_reset();
|
||
|
||
DEBUGMSGTL(("sess_process_packet",
|
||
"session %p fd %d pkt %p length %d\n", sessp,
|
||
transport->sock, packetptr, length));
|
||
|
||
dump = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_DUMP_PACKET);
|
||
#ifndef NETSNMP_FEATURE_REMOVE_FILTER_SOURCE
|
||
filter = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_FILTER_TYPE);
|
||
#endif
|
||
if (dump || filter) {
|
||
int filtered = 0;
|
||
char *addrtxt = netsnmp_transport_peer_string(transport, opaque, olength);
|
||
snmp_log(LOG_DEBUG, "\nReceived %d byte packet from %s\n",
|
||
length, addrtxt);
|
||
|
||
if (dump)
|
||
xdump(packetptr, length, "");
|
||
|
||
#ifndef NETSNMP_FEATURE_REMOVE_FILTER_SOURCE
|
||
if (filter) {
|
||
char *sourceaddr = NULL, *c = strchr(addrtxt, '[');
|
||
const char *dropstr = NULL;
|
||
if (c) {
|
||
sourceaddr = ++c;
|
||
c = strchr(sourceaddr, ']');
|
||
if (c)
|
||
*c = 0;
|
||
filtered = netsnmp_transport_filter_check(sourceaddr);
|
||
}
|
||
if ((filter == -1) && filtered)
|
||
dropstr = "matched blacklist";
|
||
else if ((filter == 1) && !filtered)
|
||
dropstr = "didn't match whitelist";
|
||
if (dropstr) {
|
||
DEBUGMSGTL(("sess_process_packet:filter",
|
||
"packet from %s %s\n",
|
||
sourceaddr ? sourceaddr : "UNKNOWN", dropstr));
|
||
SNMP_FREE(opaque);
|
||
SNMP_FREE(addrtxt);
|
||
return NULL;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
SNMP_FREE(addrtxt);
|
||
}
|
||
|
||
/*
|
||
* Do transport-level filtering (e.g. IP-address based allow/deny).
|
||
*/
|
||
|
||
if (isp->hook_pre) {
|
||
if (isp->hook_pre(sp, transport, opaque, olength) == 0) {
|
||
DEBUGMSGTL(("sess_process_packet", "pre-parse fail\n"));
|
||
SNMP_FREE(opaque);
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
if (isp->hook_create_pdu) {
|
||
pdu = isp->hook_create_pdu(transport, opaque, olength);
|
||
} else {
|
||
pdu = snmp_create_sess_pdu(transport, opaque, olength);
|
||
}
|
||
|
||
if (pdu == NULL) {
|
||
snmp_log(LOG_ERR, "pdu failed to be created\n");
|
||
SNMP_FREE(opaque);
|
||
return NULL;
|
||
}
|
||
|
||
/* if the transport was a magic tunnel, mark the PDU as having come
|
||
through one. */
|
||
if (transport->flags & NETSNMP_TRANSPORT_FLAG_TUNNELED) {
|
||
pdu->flags |= UCD_MSG_FLAG_TUNNELED;
|
||
}
|
||
|
||
if (isp->hook_parse) {
|
||
ret = isp->hook_parse(sp, pdu, packetptr, length);
|
||
} else {
|
||
ret = snmp_parse(sessp, sp, pdu, packetptr, length);
|
||
}
|
||
|
||
DEBUGMSGTL(("sess_process_packet", "received message id#%ld reqid#%ld len "
|
||
"%u\n", pdu->msgid, pdu->reqid, length));
|
||
|
||
if (ret != SNMP_ERR_NOERROR) {
|
||
DEBUGMSGTL(("sess_process_packet", "parse fail\n"));
|
||
}
|
||
|
||
if (isp->hook_post) {
|
||
if (isp->hook_post(sp, pdu, ret) == 0) {
|
||
DEBUGMSGTL(("sess_process_packet", "post-parse fail\n"));
|
||
ret = SNMPERR_ASN_PARSE_ERR;
|
||
}
|
||
}
|
||
|
||
if (ret != SNMP_ERR_NOERROR) {
|
||
/*
|
||
* Call the security model to free any securityStateRef supplied w/ msg.
|
||
*/
|
||
if (pdu->securityStateRef != NULL) {
|
||
free_securityStateRef(pdu);
|
||
}
|
||
snmp_free_pdu(pdu);
|
||
return NULL;
|
||
}
|
||
|
||
return pdu;
|
||
}
|
||
|
||
/*
|
||
* This function processes a PDU and calls the relevant callbacks.
|
||
*/
|
||
static int
|
||
_sess_process_packet_handle_pdu(void *sessp, netsnmp_session * sp,
|
||
struct snmp_internal_session *isp,
|
||
netsnmp_transport *transport, netsnmp_pdu *pdu)
|
||
{
|
||
struct session_list *slp = (struct session_list *) sessp;
|
||
netsnmp_request_list *rp, *orp = NULL;
|
||
int handled = 0;
|
||
|
||
if (pdu->flags & UCD_MSG_FLAG_RESPONSE_PDU) {
|
||
/*
|
||
* Call USM to free any securityStateRef supplied with the message.
|
||
*/
|
||
if (pdu->securityStateRef) {
|
||
free_securityStateRef(pdu);
|
||
}
|
||
|
||
for (rp = isp->requests; rp; orp = rp, rp = rp->next_request) {
|
||
snmp_callback callback;
|
||
void *magic;
|
||
|
||
if (pdu->version == SNMP_VERSION_3) {
|
||
/*
|
||
* msgId must match for v3 messages.
|
||
*/
|
||
if (rp->message_id != pdu->msgid) {
|
||
DEBUGMSGTL(("sess_process_packet", "unmatched msg id: %ld != %ld\n",
|
||
rp->message_id, pdu->msgid));
|
||
continue;
|
||
}
|
||
|
||
/*
|
||
* Check that message fields match original, if not, no further
|
||
* processing.
|
||
*/
|
||
if (!snmpv3_verify_msg(rp, pdu)) {
|
||
break;
|
||
}
|
||
} else {
|
||
if (rp->request_id != pdu->reqid) {
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (rp->callback) {
|
||
callback = rp->callback;
|
||
magic = rp->cb_data;
|
||
} else {
|
||
callback = sp->callback;
|
||
magic = sp->callback_magic;
|
||
}
|
||
handled = 1;
|
||
|
||
/*
|
||
* MTR snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION); ?* XX lock
|
||
* should be per session !
|
||
*/
|
||
|
||
if (callback == NULL
|
||
|| callback(NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE, sp,
|
||
pdu->reqid, pdu, magic) == 1) {
|
||
if (pdu->command == SNMP_MSG_REPORT) {
|
||
if (sp->s_snmp_errno == SNMPERR_NOT_IN_TIME_WINDOW ||
|
||
snmpv3_get_report_type(pdu) ==
|
||
SNMPERR_NOT_IN_TIME_WINDOW) {
|
||
/*
|
||
* trigger immediate retry on recoverable Reports
|
||
* * (notInTimeWindow), incr_retries == TRUE to prevent
|
||
* * inifinite resend
|
||
*/
|
||
if (rp->retries <= sp->retries) {
|
||
snmp_resend_request(slp, rp, TRUE);
|
||
break;
|
||
} else {
|
||
/* We're done with retries, so no longer waiting for a response */
|
||
if (callback) {
|
||
callback(NETSNMP_CALLBACK_OP_SEC_ERROR, sp,
|
||
pdu->reqid, pdu, magic);
|
||
}
|
||
}
|
||
} else {
|
||
if (SNMPV3_IGNORE_UNAUTH_REPORTS) {
|
||
break;
|
||
} else { /* We're done with retries */
|
||
if (callback) {
|
||
callback(NETSNMP_CALLBACK_OP_SEC_ERROR, sp,
|
||
pdu->reqid, pdu, magic);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Handle engineID discovery.
|
||
*/
|
||
if (!sp->securityEngineIDLen && pdu->securityEngineIDLen) {
|
||
sp->securityEngineID =
|
||
(u_char *) malloc(pdu->securityEngineIDLen);
|
||
if (sp->securityEngineID == NULL) {
|
||
/*
|
||
* TODO FIX: recover after message callback *?
|
||
*/
|
||
snmp_log(LOG_ERR, "malloc failed handling pdu\n");
|
||
snmp_free_pdu(pdu);
|
||
return -1;
|
||
}
|
||
memcpy(sp->securityEngineID, pdu->securityEngineID,
|
||
pdu->securityEngineIDLen);
|
||
sp->securityEngineIDLen = pdu->securityEngineIDLen;
|
||
if (!sp->contextEngineIDLen) {
|
||
sp->contextEngineID =
|
||
(u_char *) malloc(pdu->
|
||
securityEngineIDLen);
|
||
if (sp->contextEngineID == NULL) {
|
||
/*
|
||
* TODO FIX: recover after message callback *?
|
||
*/
|
||
snmp_log(LOG_ERR, "malloc failed handling pdu\n");
|
||
snmp_free_pdu(pdu);
|
||
return -1;
|
||
}
|
||
memcpy(sp->contextEngineID,
|
||
pdu->securityEngineID,
|
||
pdu->securityEngineIDLen);
|
||
sp->contextEngineIDLen =
|
||
pdu->securityEngineIDLen;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Successful, so delete request.
|
||
*/
|
||
if (orp)
|
||
orp->next_request = rp->next_request;
|
||
else
|
||
isp->requests = rp->next_request;
|
||
if (isp->requestsEnd == rp)
|
||
isp->requestsEnd = orp;
|
||
snmp_free_pdu(rp->pdu);
|
||
free(rp);
|
||
/*
|
||
* There shouldn't be any more requests with the same reqid.
|
||
*/
|
||
break;
|
||
}
|
||
/*
|
||
* MTR snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION); ?* XX lock should be per session !
|
||
*/
|
||
}
|
||
} else {
|
||
if (sp->callback) {
|
||
/*
|
||
* MTR snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
*/
|
||
handled = 1;
|
||
sp->callback(NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE,
|
||
sp, pdu->reqid, pdu, sp->callback_magic);
|
||
/*
|
||
* MTR snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
*/
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Call USM to free any securityStateRef supplied with the message.
|
||
*/
|
||
if (pdu->securityStateRef && pdu->command == SNMP_MSG_TRAP2)
|
||
free_securityStateRef(pdu);
|
||
|
||
if (!handled) {
|
||
if (sp->flags & SNMP_FLAGS_SHARED_SOCKET)
|
||
return -2;
|
||
snmp_increment_statistic(STAT_SNMPUNKNOWNPDUHANDLERS);
|
||
DEBUGMSGTL(("sess_process_packet", "unhandled PDU\n"));
|
||
}
|
||
|
||
snmp_free_pdu(pdu);
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* This function processes a complete (according to asn_check_packet or the
|
||
* AgentX equivalent) packet, parsing it into a PDU and calling the relevant
|
||
* callbacks. On entry, packetptr points at the packet in the session's
|
||
* buffer and length is the length of the packet. Return codes:
|
||
* 0: pdu handled (pdu deleted)
|
||
* -1: parse error (pdu deleted)
|
||
* -2: pdu not found for shared session (pdu NOT deleted)
|
||
*/
|
||
static int
|
||
_sess_process_packet(void *sessp, netsnmp_session * sp,
|
||
struct snmp_internal_session *isp,
|
||
netsnmp_transport *transport,
|
||
void *opaque, int olength,
|
||
u_char * packetptr, int length)
|
||
{
|
||
struct session_list *slp = (struct session_list *) sessp;
|
||
netsnmp_pdu *pdu;
|
||
int rc;
|
||
|
||
pdu = _sess_process_packet_parse_pdu(sessp, sp, isp, transport, opaque,
|
||
olength, packetptr, length);
|
||
if (NULL == pdu)
|
||
return -1;
|
||
|
||
/*
|
||
* find session to process pdu. usually that will be the current session,
|
||
* but with the introduction of shared transports, another session may
|
||
* have the same socket.
|
||
*/
|
||
do {
|
||
rc = _sess_process_packet_handle_pdu(sessp, sp, isp, transport, pdu);
|
||
if (-2 != rc || !(transport->flags & NETSNMP_TRANSPORT_FLAG_SHARED))
|
||
break;
|
||
|
||
/** -2 means pdu not in request list. check other sessions */
|
||
do {
|
||
slp = slp->next;
|
||
} while (slp && slp->transport->sock != transport->sock);
|
||
if (!slp)
|
||
break; /* no more sessions with same socket */
|
||
|
||
sp = slp->session;
|
||
isp = slp->internal;
|
||
transport = slp->transport;
|
||
} while(slp);
|
||
|
||
if (-2 == rc) { /* did not find session for pdu */
|
||
snmp_increment_statistic(STAT_SNMPUNKNOWNPDUHANDLERS);
|
||
DEBUGMSGTL(("sess_process_packet", "unhandled PDU\n"));
|
||
snmp_free_pdu(pdu);
|
||
}
|
||
|
||
return rc;
|
||
}
|
||
|
||
/*
|
||
* Checks to see if any of the fd's set in the fdset belong to
|
||
* snmp. Each socket with it's fd set has a packet read from it
|
||
* and snmp_parse is called on the packet received. The resulting pdu
|
||
* is passed to the callback routine for that session. If the callback
|
||
* routine returns successfully, the pdu and it's request are deleted.
|
||
*/
|
||
void
|
||
snmp_read(fd_set * fdset)
|
||
{
|
||
netsnmp_large_fd_set lfdset;
|
||
|
||
netsnmp_large_fd_set_init(&lfdset, FD_SETSIZE);
|
||
netsnmp_copy_fd_set_to_large_fd_set(&lfdset, fdset);
|
||
snmp_read2(&lfdset);
|
||
netsnmp_large_fd_set_cleanup(&lfdset);
|
||
}
|
||
|
||
void
|
||
snmp_read2(netsnmp_large_fd_set * fdset)
|
||
{
|
||
struct session_list *slp;
|
||
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
for (slp = Sessions; slp; slp = slp->next) {
|
||
snmp_sess_read2((void *) slp, fdset);
|
||
}
|
||
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
}
|
||
|
||
/*
|
||
* accept new connections
|
||
* returns 0 if success, -1 if fail
|
||
*/
|
||
static int
|
||
_sess_read_accept(void *sessp)
|
||
{
|
||
struct session_list *slp = (struct session_list *) sessp;
|
||
netsnmp_session *sp = slp ? slp->session : NULL;
|
||
struct snmp_internal_session *isp = slp ? slp->internal : NULL;
|
||
netsnmp_transport *transport = slp ? slp->transport : NULL;
|
||
netsnmp_transport *new_transport;
|
||
struct session_list *nslp;
|
||
int data_sock;
|
||
|
||
if (NULL == sessp || NULL == sp || NULL == transport || NULL == isp ||
|
||
!(transport->flags & NETSNMP_TRANSPORT_FLAG_LISTEN))
|
||
return -1;
|
||
|
||
data_sock = transport->f_accept(transport);
|
||
if (data_sock < 0) {
|
||
sp->s_snmp_errno = SNMPERR_BAD_RECVFROM;
|
||
sp->s_errno = errno;
|
||
snmp_set_detail(strerror(errno));
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* We've successfully accepted a new stream-based connection.
|
||
* It's not too clear what should happen here if we are using the
|
||
* single-session API at this point. Basically a "session
|
||
* accepted" callback is probably needed to hand the new session
|
||
* over to the application.
|
||
*
|
||
* However, for now, as in th original snmp_api, we will ASSUME
|
||
* that we're using the traditional API, and simply add the new
|
||
* session to the list. Note we don't have to get the Session
|
||
* list lock here, because under that assumption we already hold
|
||
* it (this is also why we don't just use snmp_add).
|
||
*
|
||
* The moral of the story is: don't use listening stream-based
|
||
* transports in a multi-threaded environment because something
|
||
* will go HORRIBLY wrong (and also that SNMP/TCP is not trivial).
|
||
*
|
||
* Another open issue: what should happen to sockets that have
|
||
* been accept()ed from a listening socket when that original
|
||
* socket is closed? If they are left open, then attempting to
|
||
* re-open the listening socket will fail, which is semantically
|
||
* confusing. Perhaps there should be some kind of chaining in
|
||
* the transport structure so that they can all be closed.
|
||
* Discuss. ;-)
|
||
*/
|
||
new_transport=netsnmp_transport_copy(transport);
|
||
if (new_transport == NULL) {
|
||
sp->s_snmp_errno = SNMPERR_MALLOC;
|
||
sp->s_errno = errno;
|
||
snmp_set_detail(strerror(errno));
|
||
return -1;
|
||
}
|
||
nslp = NULL;
|
||
|
||
new_transport->sock = data_sock;
|
||
new_transport->flags &= ~NETSNMP_TRANSPORT_FLAG_LISTEN;
|
||
|
||
nslp = (struct session_list *)
|
||
snmp_sess_add_ex(sp, new_transport, isp->hook_pre, isp->hook_parse,
|
||
isp->hook_post, isp->hook_build,
|
||
isp->hook_realloc_build, isp->check_packet,
|
||
isp->hook_create_pdu);
|
||
|
||
if (nslp != NULL) {
|
||
snmp_session_insert(nslp);
|
||
/** Tell the new session about its existance if possible. */
|
||
DEBUGMSGTL(("sess_read",
|
||
"perform callback with op=CONNECT\n"));
|
||
(void)nslp->session->callback(NETSNMP_CALLBACK_OP_CONNECT,
|
||
nslp->session, 0, NULL,
|
||
sp->callback_magic);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Same as snmp_read, but works just one non-stream session.
|
||
* returns 0 if success, -1 if protocol err, -2 if no packet to process
|
||
* MTR: can't lock here and at snmp_read
|
||
* Beware recursive send maybe inside snmp_read callback function.
|
||
*/
|
||
static int
|
||
_sess_read_dgram_packet(void *sessp, netsnmp_large_fd_set * fdset,
|
||
snmp_rcv_packet *rcvp)
|
||
{
|
||
struct session_list *slp = (struct session_list *) sessp;
|
||
netsnmp_session *sp = slp ? slp->session : NULL;
|
||
struct snmp_internal_session *isp = slp ? slp->internal : NULL;
|
||
netsnmp_transport *transport = slp ? slp->transport : NULL;
|
||
|
||
if (!sp || !isp || !transport || !rcvp ) {
|
||
DEBUGMSGTL(("sess_read_packet", "missing arguments\n"));
|
||
return -2;
|
||
}
|
||
|
||
if (transport->flags & NETSNMP_TRANSPORT_FLAG_STREAM)
|
||
return -2;
|
||
|
||
if (NULL != rcvp->packet) {
|
||
snmp_log(LOG_WARNING, "overwriting existing saved packet; sess %p\n",
|
||
sp);
|
||
SNMP_FREE(rcvp->packet);
|
||
}
|
||
|
||
if ((rcvp->packet = (u_char *) malloc(SNMP_MAX_RCV_MSG_SIZE)) == NULL) {
|
||
DEBUGMSGTL(("sess_read_packet", "can't malloc %u bytes for packet\n",
|
||
SNMP_MAX_RCV_MSG_SIZE));
|
||
return -2;
|
||
}
|
||
|
||
rcvp->packet_len = netsnmp_transport_recv(transport, rcvp->packet,
|
||
SNMP_MAX_RCV_MSG_SIZE,
|
||
&rcvp->opaque, &rcvp->olength);
|
||
if (rcvp->packet_len == -1) {
|
||
sp->s_snmp_errno = SNMPERR_BAD_RECVFROM;
|
||
sp->s_errno = errno;
|
||
snmp_set_detail(strerror(errno));
|
||
SNMP_FREE(rcvp->packet);
|
||
SNMP_FREE(rcvp->opaque);
|
||
return -1;
|
||
}
|
||
|
||
/** clear so any other sess sharing this socket won't try reading again */
|
||
NETSNMP_LARGE_FD_CLR(transport->sock, fdset);
|
||
|
||
if (0 == rcvp->packet_len &&
|
||
transport->flags & NETSNMP_TRANSPORT_FLAG_EMPTY_PKT) {
|
||
/* this allows for a transport that needs to return from
|
||
* packet processing that doesn't necessarily have any
|
||
* consumable data in it. */
|
||
|
||
/* reset the flag since it's a per-message flag */
|
||
transport->flags &= (~NETSNMP_TRANSPORT_FLAG_EMPTY_PKT);
|
||
|
||
/** free packet */
|
||
SNMP_FREE(rcvp->packet);
|
||
SNMP_FREE(rcvp->opaque);
|
||
|
||
return -2;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Same as snmp_read, but works just one session.
|
||
* returns 0 if success, -1 if fail
|
||
* MTR: can't lock here and at snmp_read
|
||
* Beware recursive send maybe inside snmp_read callback function.
|
||
*/
|
||
int
|
||
_sess_read(void *sessp, netsnmp_large_fd_set * fdset)
|
||
{
|
||
struct session_list *slp = (struct session_list *) sessp;
|
||
netsnmp_session *sp = slp ? slp->session : NULL;
|
||
struct snmp_internal_session *isp = slp ? slp->internal : NULL;
|
||
netsnmp_transport *transport = slp ? slp->transport : NULL;
|
||
size_t pdulen = 0, rxbuf_len = SNMP_MAX_RCV_MSG_SIZE;
|
||
u_char *rxbuf = NULL;
|
||
int length = 0, olength = 0, rc = 0;
|
||
void *opaque = NULL;
|
||
|
||
if (NULL == slp || NULL == sp || NULL == isp || NULL == transport) {
|
||
snmp_log(LOG_ERR, "bad parameters to _sess_read\n");
|
||
return SNMPERR_GENERR;
|
||
}
|
||
|
||
/* to avoid subagent crash */
|
||
if (transport->sock < 0) {
|
||
snmp_log (LOG_INFO, "transport->sock got negative fd value %d\n",
|
||
transport->sock);
|
||
return 0;
|
||
}
|
||
|
||
if (!fdset || !(NETSNMP_LARGE_FD_ISSET(transport->sock, fdset))) {
|
||
DEBUGMSGTL(("sess_read", "not reading %d (fdset %p set %d)\n",
|
||
transport->sock, fdset,
|
||
fdset ? NETSNMP_LARGE_FD_ISSET(transport->sock, fdset)
|
||
: -9));
|
||
return 0;
|
||
}
|
||
|
||
sp->s_snmp_errno = 0;
|
||
sp->s_errno = 0;
|
||
|
||
if (transport->flags & NETSNMP_TRANSPORT_FLAG_LISTEN)
|
||
return _sess_read_accept(sessp);
|
||
|
||
if (!(transport->flags & NETSNMP_TRANSPORT_FLAG_STREAM)) {
|
||
snmp_rcv_packet rcvp;
|
||
memset(&rcvp, 0x0, sizeof(rcvp));
|
||
|
||
/** read the packet */
|
||
rc = _sess_read_dgram_packet(sessp, fdset, &rcvp);
|
||
if (-1 == rc) /* protocol error */
|
||
return -1;
|
||
else if (-2 == rc) /* no packet to process */
|
||
return 0;
|
||
|
||
rc = _sess_process_packet(sessp, sp, isp, transport,
|
||
rcvp.opaque, rcvp.olength,
|
||
rcvp.packet, rcvp.packet_len);
|
||
SNMP_FREE(rcvp.packet);
|
||
/** opaque is freed in _sess_process_packet */
|
||
return rc;
|
||
}
|
||
|
||
/** stream transport */
|
||
|
||
if (isp->packet == NULL) {
|
||
/*
|
||
* We have no saved packet. Allocate one.
|
||
*/
|
||
if ((isp->packet = (u_char *) malloc(rxbuf_len)) == NULL) {
|
||
DEBUGMSGTL(("sess_read", "can't malloc %" NETSNMP_PRIz
|
||
"u bytes for rxbuf\n", rxbuf_len));
|
||
return 0;
|
||
} else {
|
||
rxbuf = isp->packet;
|
||
isp->packet_size = rxbuf_len;
|
||
isp->packet_len = 0;
|
||
}
|
||
} else {
|
||
/*
|
||
* We have saved a partial packet from last time. Extend that, if
|
||
* necessary, and receive new data after the old data.
|
||
*/
|
||
u_char *newbuf;
|
||
|
||
if (isp->packet_size < isp->packet_len + rxbuf_len) {
|
||
newbuf =
|
||
(u_char *) realloc(isp->packet,
|
||
isp->packet_len + rxbuf_len);
|
||
if (newbuf == NULL) {
|
||
DEBUGMSGTL(("sess_read",
|
||
"can't malloc %" NETSNMP_PRIz
|
||
"u more for rxbuf (%" NETSNMP_PRIz "u tot)\n",
|
||
rxbuf_len, isp->packet_len + rxbuf_len));
|
||
return 0;
|
||
} else {
|
||
isp->packet = newbuf;
|
||
isp->packet_size = isp->packet_len + rxbuf_len;
|
||
rxbuf = isp->packet + isp->packet_len;
|
||
}
|
||
} else {
|
||
rxbuf = isp->packet + isp->packet_len;
|
||
rxbuf_len = isp->packet_size - isp->packet_len;
|
||
}
|
||
}
|
||
|
||
length = netsnmp_transport_recv(transport, rxbuf, rxbuf_len, &opaque,
|
||
&olength);
|
||
|
||
if (0 == length && transport->flags & NETSNMP_TRANSPORT_FLAG_EMPTY_PKT) {
|
||
/* this allows for a transport that needs to return from
|
||
* packet processing that doesn't necessarily have any
|
||
* consumable data in it. */
|
||
|
||
/* reset the flag since it's a per-message flag */
|
||
transport->flags &= (~NETSNMP_TRANSPORT_FLAG_EMPTY_PKT);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Remote end closed connection.
|
||
*/
|
||
if (length <= 0) {
|
||
/*
|
||
* Alert the application if possible.
|
||
*/
|
||
if (sp->callback != NULL) {
|
||
DEBUGMSGTL(("sess_read", "perform callback with op=DISCONNECT\n"));
|
||
(void) sp->callback(NETSNMP_CALLBACK_OP_DISCONNECT, sp, 0,
|
||
NULL, sp->callback_magic);
|
||
}
|
||
/*
|
||
* Close socket and mark session for deletion.
|
||
*/
|
||
DEBUGMSGTL(("sess_read", "fd %d closed\n", transport->sock));
|
||
transport->f_close(transport);
|
||
SNMP_FREE(isp->packet);
|
||
SNMP_FREE(opaque);
|
||
return -1;
|
||
}
|
||
|
||
{
|
||
u_char *pptr = isp->packet;
|
||
void *ocopy = NULL;
|
||
|
||
isp->packet_len += length;
|
||
|
||
while (isp->packet_len > 0) {
|
||
|
||
/*
|
||
* Get the total data length we're expecting (and need to wait
|
||
* for).
|
||
*/
|
||
if (isp->check_packet) {
|
||
pdulen = isp->check_packet(pptr, isp->packet_len);
|
||
} else {
|
||
pdulen = asn_check_packet(pptr, isp->packet_len);
|
||
}
|
||
|
||
DEBUGMSGTL(("sess_read",
|
||
" loop packet_len %" NETSNMP_PRIz "u, PDU length %"
|
||
NETSNMP_PRIz "u\n", isp->packet_len, pdulen));
|
||
|
||
if (pdulen > SNMP_MAX_PACKET_LEN) {
|
||
/*
|
||
* Illegal length, drop the connection.
|
||
*/
|
||
snmp_log(LOG_ERR,
|
||
"Received broken packet. Closing session.\n");
|
||
if (sp->callback != NULL) {
|
||
DEBUGMSGTL(("sess_read",
|
||
"perform callback with op=DISCONNECT\n"));
|
||
(void)sp->callback(NETSNMP_CALLBACK_OP_DISCONNECT,
|
||
sp, 0, NULL, sp->callback_magic);
|
||
}
|
||
DEBUGMSGTL(("sess_read", "fd %d closed\n", transport->sock));
|
||
transport->f_close(transport);
|
||
SNMP_FREE(opaque);
|
||
/** XXX-rks: why no SNMP_FREE(isp->packet); ?? */
|
||
return -1;
|
||
}
|
||
|
||
if (pdulen > isp->packet_len || pdulen == 0) {
|
||
/*
|
||
* We don't have a complete packet yet. If we've already
|
||
* processed a packet, break out so we'll shift this packet
|
||
* to the start of the buffer. If we're already at the
|
||
* start, simply return and wait for more data to arrive.
|
||
*/
|
||
DEBUGMSGTL(("sess_read",
|
||
"pkt not complete (need %" NETSNMP_PRIz "u got %"
|
||
NETSNMP_PRIz "u so far)\n", pdulen,
|
||
isp->packet_len));
|
||
|
||
if (pptr != isp->packet)
|
||
break; /* opaque freed for us outside of loop. */
|
||
|
||
SNMP_FREE(opaque);
|
||
return 0;
|
||
}
|
||
|
||
/* We have *at least* one complete packet in the buffer now. If
|
||
we have possibly more than one packet, we must copy the opaque
|
||
pointer because we may need to reuse it for a later packet. */
|
||
|
||
if (pdulen < isp->packet_len) {
|
||
if (olength > 0 && opaque != NULL) {
|
||
ocopy = malloc(olength);
|
||
if (ocopy != NULL) {
|
||
memcpy(ocopy, opaque, olength);
|
||
}
|
||
}
|
||
} else if (pdulen == isp->packet_len) {
|
||
/* Common case -- exactly one packet. No need to copy the
|
||
opaque pointer. */
|
||
ocopy = opaque;
|
||
opaque = NULL;
|
||
}
|
||
|
||
if ((rc = _sess_process_packet(sessp, sp, isp, transport,
|
||
ocopy, ocopy?olength:0, pptr,
|
||
pdulen))) {
|
||
/*
|
||
* Something went wrong while processing this packet -- set the
|
||
* errno.
|
||
*/
|
||
if (sp->s_snmp_errno != 0) {
|
||
SET_SNMP_ERROR(sp->s_snmp_errno);
|
||
}
|
||
}
|
||
|
||
/* ocopy has been free()d by _sess_process_packet by this point,
|
||
so set it to NULL. */
|
||
|
||
ocopy = NULL;
|
||
|
||
/* Step past the packet we've just dealt with. */
|
||
|
||
pptr += pdulen;
|
||
isp->packet_len -= pdulen;
|
||
}
|
||
|
||
/* If we had more than one packet, then we were working with copies
|
||
of the opaque pointer, so we still need to free() the opaque
|
||
pointer itself. */
|
||
|
||
SNMP_FREE(opaque);
|
||
|
||
if (isp->packet_len >= SNMP_MAX_PACKET_LEN) {
|
||
/*
|
||
* Obviously this should never happen!
|
||
*/
|
||
snmp_log(LOG_ERR,
|
||
"too large packet_len = %" NETSNMP_PRIz
|
||
"u, dropping connection %d\n",
|
||
isp->packet_len, transport->sock);
|
||
transport->f_close(transport);
|
||
/** XXX-rks: why no SNMP_FREE(isp->packet); ?? */
|
||
return -1;
|
||
} else if (isp->packet_len == 0) {
|
||
/*
|
||
* This is good: it means the packet buffer contained an integral
|
||
* number of PDUs, so we don't have to save any data for next
|
||
* time. We can free() the buffer now to keep the memory
|
||
* footprint down.
|
||
*/
|
||
SNMP_FREE(isp->packet);
|
||
isp->packet_size = 0;
|
||
isp->packet_len = 0;
|
||
return rc;
|
||
}
|
||
|
||
/*
|
||
* If we get here, then there is a partial packet of length
|
||
* isp->packet_len bytes starting at pptr left over. Move that to the
|
||
* start of the buffer, and then realloc() the buffer down to size to
|
||
* reduce the memory footprint.
|
||
*/
|
||
|
||
memmove(isp->packet, pptr, isp->packet_len);
|
||
DEBUGMSGTL(("sess_read",
|
||
"end: memmove(%p, %p, %" NETSNMP_PRIz "u); realloc(%p, %"
|
||
NETSNMP_PRIz "u)\n",
|
||
isp->packet, pptr, isp->packet_len,
|
||
isp->packet, isp->packet_len));
|
||
|
||
if ((rxbuf = (u_char *)realloc(isp->packet, isp->packet_len)) == NULL) {
|
||
/*
|
||
* I don't see why this should ever fail, but it's not a big deal.
|
||
*/
|
||
DEBUGMSGTL(("sess_read", "realloc() failed\n"));
|
||
} else {
|
||
DEBUGMSGTL(("sess_read", "realloc() okay, old buffer %p, new %p\n",
|
||
isp->packet, rxbuf));
|
||
isp->packet = rxbuf;
|
||
isp->packet_size = isp->packet_len;
|
||
}
|
||
}
|
||
|
||
return rc;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
* returns 0 if success, -1 if fail
|
||
*/
|
||
int
|
||
snmp_sess_read(void *sessp, fd_set * fdset)
|
||
{
|
||
int rc;
|
||
netsnmp_large_fd_set lfdset;
|
||
|
||
netsnmp_large_fd_set_init(&lfdset, FD_SETSIZE);
|
||
netsnmp_copy_fd_set_to_large_fd_set(&lfdset, fdset);
|
||
rc = snmp_sess_read2(sessp, &lfdset);
|
||
netsnmp_large_fd_set_cleanup(&lfdset);
|
||
return rc;
|
||
}
|
||
|
||
int
|
||
snmp_sess_read2(void *sessp, netsnmp_large_fd_set * fdset)
|
||
{
|
||
struct session_list *psl;
|
||
netsnmp_session *pss;
|
||
int rc;
|
||
|
||
rc = _sess_read(sessp, fdset);
|
||
psl = (struct session_list *) sessp;
|
||
pss = psl->session;
|
||
if (rc && pss->s_snmp_errno) {
|
||
SET_SNMP_ERROR(pss->s_snmp_errno);
|
||
}
|
||
return rc;
|
||
}
|
||
|
||
|
||
/**
|
||
* Returns info about what snmp requires from a select statement.
|
||
* numfds is the number of fds in the list that are significant.
|
||
* All file descriptors opened for SNMP are OR'd into the fdset.
|
||
* If activity occurs on any of these file descriptors, snmp_read
|
||
* should be called with that file descriptor set
|
||
*
|
||
* The timeout is the latest time that SNMP can wait for a timeout. The
|
||
* select should be done with the minimum time between timeout and any other
|
||
* timeouts necessary. This should be checked upon each invocation of select.
|
||
* If a timeout is received, snmp_timeout should be called to check if the
|
||
* timeout was for SNMP. (snmp_timeout is idempotent)
|
||
*
|
||
* The value of block indicates how the timeout value is interpreted.
|
||
* If block is true on input, the timeout value will be treated as undefined,
|
||
* but it must be available for setting in snmp_select_info. On return,
|
||
* block is set to true if the value returned for timeout is undefined;
|
||
* when block is set to false, timeout may be used as a parmeter to 'select'.
|
||
*
|
||
* snmp_select_info returns the number of open sockets. (i.e. The number of
|
||
* sessions open)
|
||
*
|
||
* @see See also snmp_sess_select_info2_flags().
|
||
*/
|
||
int
|
||
snmp_select_info(int *numfds, fd_set *fdset, struct timeval *timeout,
|
||
int *block)
|
||
{
|
||
return snmp_sess_select_info(NULL, numfds, fdset, timeout, block);
|
||
}
|
||
|
||
/**
|
||
* @see See also snmp_sess_select_info2_flags().
|
||
*/
|
||
int
|
||
snmp_select_info2(int *numfds, netsnmp_large_fd_set *fdset,
|
||
struct timeval *timeout, int *block)
|
||
{
|
||
return snmp_sess_select_info2(NULL, numfds, fdset, timeout, block);
|
||
}
|
||
|
||
/**
|
||
* @see See also snmp_sess_select_info2_flags().
|
||
*/
|
||
int
|
||
snmp_sess_select_info(void *sessp, int *numfds, fd_set *fdset,
|
||
struct timeval *timeout, int *block)
|
||
{
|
||
return snmp_sess_select_info_flags(sessp, numfds, fdset, timeout, block,
|
||
NETSNMP_SELECT_NOFLAGS);
|
||
}
|
||
|
||
/**
|
||
* @see See also snmp_sess_select_info2_flags().
|
||
*/
|
||
int
|
||
snmp_sess_select_info_flags(void *sessp, int *numfds, fd_set *fdset,
|
||
struct timeval *timeout, int *block, int flags)
|
||
{
|
||
int rc;
|
||
netsnmp_large_fd_set lfdset;
|
||
|
||
netsnmp_large_fd_set_init(&lfdset, FD_SETSIZE);
|
||
netsnmp_copy_fd_set_to_large_fd_set(&lfdset, fdset);
|
||
rc = snmp_sess_select_info2_flags(sessp, numfds, &lfdset, timeout,
|
||
block, flags);
|
||
if (netsnmp_copy_large_fd_set_to_fd_set(fdset, &lfdset) < 0) {
|
||
snmp_log(LOG_ERR,
|
||
"Use snmp_sess_select_info2() for processing"
|
||
" large file descriptors\n");
|
||
}
|
||
netsnmp_large_fd_set_cleanup(&lfdset);
|
||
return rc;
|
||
}
|
||
|
||
/**
|
||
* @see See also snmp_sess_select_info2_flags().
|
||
*/
|
||
int
|
||
snmp_sess_select_info2(void *sessp, int *numfds, netsnmp_large_fd_set *fdset,
|
||
struct timeval *timeout, int *block)
|
||
{
|
||
return snmp_sess_select_info2_flags(sessp, numfds, fdset, timeout, block,
|
||
NETSNMP_SELECT_NOFLAGS);
|
||
}
|
||
|
||
/**
|
||
* Compute/update the arguments to be passed to select().
|
||
*
|
||
* @param[in] sessp Which sessions to process: either a pointer to a
|
||
* specific session or NULL which means to process all sessions.
|
||
* @param[in,out] numfds On POSIX systems one more than the the largest file
|
||
* descriptor that is present in *fdset. On systems that use Winsock (MinGW
|
||
* and MSVC), do not use the value written into *numfds.
|
||
* @param[in,out] fdset A large file descriptor set to which all file
|
||
* descriptors will be added that are associated with one of the examined
|
||
* sessions.
|
||
* @param[in,out] timeout On input, if *block = 1, the maximum time the caller
|
||
* will block while waiting for Net-SNMP activity. On output, if this function
|
||
* has set *block to 0, the maximum time the caller is allowed to wait before
|
||
* invoking the Net-SNMP processing functions (snmp_read(), snmp_timeout()
|
||
* and run_alarms()). If this function has set *block to 1, *timeout won't
|
||
* have been modified and no alarms are active.
|
||
* @param[in,out] block On input, whether the caller prefers to block forever
|
||
* when no alarms are active. On output, 0 means that no alarms are active
|
||
* nor that there is a timeout pending for any of the processed sessions.
|
||
* @param[in] flags Either 0 or NETSNMP_SELECT_NOALARMS.
|
||
*
|
||
* @return Number of sessions processed by this function.
|
||
*
|
||
* @see See also agent_check_and_process() for an example of how to use this
|
||
* function.
|
||
*/
|
||
int
|
||
snmp_sess_select_info2_flags(void *sessp, int *numfds,
|
||
netsnmp_large_fd_set * fdset,
|
||
struct timeval *timeout, int *block, int flags)
|
||
{
|
||
struct session_list *slp, *next = NULL;
|
||
netsnmp_request_list *rp;
|
||
struct timeval now, earliest, alarm_tm;
|
||
int active = 0, requests = 0;
|
||
int next_alarm = 0;
|
||
|
||
timerclear(&earliest);
|
||
|
||
/*
|
||
* For each session examined, add its socket to the fdset,
|
||
* and if it is the earliest timeout to expire, mark it as lowest.
|
||
* If a single session is specified, do just for that session.
|
||
*/
|
||
|
||
DEBUGMSGTL(("sess_select", "for %s session%s: ",
|
||
sessp ? "single" : "all", sessp ? "" : "s"));
|
||
|
||
for (slp = sessp ? sessp : Sessions; slp; slp = next) {
|
||
next = slp->next;
|
||
|
||
if (slp->transport == NULL) {
|
||
/*
|
||
* Close in progress -- skip this one.
|
||
*/
|
||
DEBUGMSG(("sess_select", "skip "));
|
||
continue;
|
||
}
|
||
|
||
if (slp->transport->sock == -1) {
|
||
/*
|
||
* This session was marked for deletion.
|
||
*/
|
||
DEBUGMSG(("sess_select", "delete\n"));
|
||
if (sessp == NULL) {
|
||
snmp_close(slp->session);
|
||
} else {
|
||
snmp_sess_close(slp);
|
||
}
|
||
DEBUGMSGTL(("sess_select", "for %s session%s: ",
|
||
sessp ? "single" : "all", sessp ? "" : "s"));
|
||
continue;
|
||
}
|
||
|
||
DEBUGMSG(("sess_select", "%d ", slp->transport->sock));
|
||
if ((slp->transport->sock + 1) > *numfds) {
|
||
*numfds = (slp->transport->sock + 1);
|
||
}
|
||
|
||
NETSNMP_LARGE_FD_SET(slp->transport->sock, fdset);
|
||
if (slp->internal != NULL && slp->internal->requests) {
|
||
/*
|
||
* Found another session with outstanding requests.
|
||
*/
|
||
requests++;
|
||
for (rp = slp->internal->requests; rp; rp = rp->next_request) {
|
||
if (!timerisset(&earliest)
|
||
|| (timerisset(&rp->expireM)
|
||
&& timercmp(&rp->expireM, &earliest, <))) {
|
||
earliest = rp->expireM;
|
||
DEBUGMSG(("verbose:sess_select","(to in %d.%06d sec) ",
|
||
(int)earliest.tv_sec, (int)earliest.tv_usec));
|
||
}
|
||
}
|
||
}
|
||
|
||
active++;
|
||
if (sessp) {
|
||
/*
|
||
* Single session processing.
|
||
*/
|
||
break;
|
||
}
|
||
}
|
||
DEBUGMSG(("sess_select", "\n"));
|
||
|
||
netsnmp_get_monotonic_clock(&now);
|
||
|
||
if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_ALARM_DONT_USE_SIG) &&
|
||
!(flags & NETSNMP_SELECT_NOALARMS)) {
|
||
next_alarm = netsnmp_get_next_alarm_time(&alarm_tm, &now);
|
||
if (next_alarm)
|
||
DEBUGMSGT(("sess_select","next alarm at %ld.%06ld sec\n",
|
||
(long)alarm_tm.tv_sec, (long)alarm_tm.tv_usec));
|
||
}
|
||
if (next_alarm == 0 && requests == 0) {
|
||
/*
|
||
* If none are active, skip arithmetic.
|
||
*/
|
||
DEBUGMSGT(("sess_select","blocking:no session requests or alarms.\n"));
|
||
*block = 1; /* can block - timeout value is undefined if no requests */
|
||
return active;
|
||
}
|
||
|
||
if (next_alarm &&
|
||
(!timerisset(&earliest) || timercmp(&alarm_tm, &earliest, <)))
|
||
earliest = alarm_tm;
|
||
|
||
NETSNMP_TIMERSUB(&earliest, &now, &earliest);
|
||
if (earliest.tv_sec < 0) {
|
||
time_t overdue_ms = -(earliest.tv_sec * 1000 + earliest.tv_usec / 1000);
|
||
if (overdue_ms >= 10)
|
||
DEBUGMSGT(("verbose:sess_select","timer overdue by %ld ms\n",
|
||
(long) overdue_ms));
|
||
timerclear(&earliest);
|
||
} else {
|
||
DEBUGMSGT(("verbose:sess_select","timer due in %d.%06d sec\n",
|
||
(int)earliest.tv_sec, (int)earliest.tv_usec));
|
||
}
|
||
|
||
/*
|
||
* if it was blocking before or our delta time is less, reset timeout
|
||
*/
|
||
if ((*block || (timercmp(&earliest, timeout, <)))) {
|
||
DEBUGMSGT(("verbose:sess_select",
|
||
"setting timer to %d.%06d sec, clear block (was %d)\n",
|
||
(int)earliest.tv_sec, (int)earliest.tv_usec, *block));
|
||
*timeout = earliest;
|
||
*block = 0;
|
||
}
|
||
return active;
|
||
}
|
||
|
||
/*
|
||
* snmp_timeout should be called whenever the timeout from snmp_select_info
|
||
* expires, but it is idempotent, so snmp_timeout can be polled (probably a
|
||
* cpu expensive proposition). snmp_timeout checks to see if any of the
|
||
* sessions have an outstanding request that has timed out. If it finds one
|
||
* (or more), and that pdu has more retries available, a new packet is formed
|
||
* from the pdu and is resent. If there are no more retries available, the
|
||
* callback for the session is used to alert the user of the timeout.
|
||
*/
|
||
void
|
||
snmp_timeout(void)
|
||
{
|
||
struct session_list *slp;
|
||
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
for (slp = Sessions; slp; slp = slp->next) {
|
||
snmp_sess_timeout((void *) slp);
|
||
}
|
||
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
}
|
||
|
||
static int
|
||
snmp_resend_request(struct session_list *slp, netsnmp_request_list *rp,
|
||
int incr_retries)
|
||
{
|
||
struct snmp_internal_session *isp;
|
||
netsnmp_session *sp;
|
||
netsnmp_transport *transport;
|
||
u_char *pktbuf = NULL, *packet = NULL;
|
||
size_t pktbuf_len = 0, length = 0;
|
||
struct timeval tv, now;
|
||
int result = 0;
|
||
|
||
sp = slp->session;
|
||
isp = slp->internal;
|
||
transport = slp->transport;
|
||
if (!sp || !isp || !transport) {
|
||
DEBUGMSGTL(("sess_read", "resend fail: closing...\n"));
|
||
return 0;
|
||
}
|
||
|
||
if ((pktbuf = (u_char *)malloc(2048)) == NULL) {
|
||
DEBUGMSGTL(("sess_resend",
|
||
"couldn't malloc initial packet buffer\n"));
|
||
return 0;
|
||
} else {
|
||
pktbuf_len = 2048;
|
||
}
|
||
|
||
if (incr_retries) {
|
||
rp->retries++;
|
||
}
|
||
|
||
/*
|
||
* Always increment msgId for resent messages.
|
||
*/
|
||
rp->pdu->msgid = rp->message_id = snmp_get_next_msgid();
|
||
|
||
result = netsnmp_build_packet(isp, sp, rp->pdu, &pktbuf, &pktbuf_len,
|
||
&packet, &length);
|
||
if (result < 0) {
|
||
/*
|
||
* This should never happen.
|
||
*/
|
||
DEBUGMSGTL(("sess_resend", "encoding failure\n"));
|
||
SNMP_FREE(pktbuf);
|
||
return -1;
|
||
}
|
||
|
||
DEBUGMSGTL(("sess_process_packet", "resending message id#%ld reqid#%ld "
|
||
"rp_reqid#%ld rp_msgid#%ld len %" NETSNMP_PRIz "u\n",
|
||
rp->pdu->msgid, rp->pdu->reqid, rp->request_id, rp->message_id, length));
|
||
result = netsnmp_transport_send(transport, packet, length,
|
||
&(rp->pdu->transport_data),
|
||
&(rp->pdu->transport_data_length));
|
||
|
||
/*
|
||
* We are finished with the local packet buffer, if we allocated one (due
|
||
* to there being no saved packet).
|
||
*/
|
||
|
||
if (pktbuf != NULL) {
|
||
SNMP_FREE(pktbuf);
|
||
packet = NULL;
|
||
}
|
||
|
||
if (result < 0) {
|
||
sp->s_snmp_errno = SNMPERR_BAD_SENDTO;
|
||
sp->s_errno = errno;
|
||
snmp_set_detail(strerror(errno));
|
||
if (rp->callback)
|
||
rp->callback(NETSNMP_CALLBACK_OP_SEND_FAILED, sp,
|
||
rp->pdu->reqid, rp->pdu, rp->cb_data);
|
||
return -1;
|
||
} else {
|
||
netsnmp_get_monotonic_clock(&now);
|
||
tv = now;
|
||
rp->timeM = tv;
|
||
tv.tv_usec += rp->timeout;
|
||
tv.tv_sec += tv.tv_usec / 1000000L;
|
||
tv.tv_usec %= 1000000L;
|
||
rp->expireM = tv;
|
||
if (rp->callback)
|
||
rp->callback(NETSNMP_CALLBACK_OP_RESEND, sp,
|
||
rp->pdu->reqid, rp->pdu, rp->cb_data);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
|
||
void
|
||
snmp_sess_timeout(void *sessp)
|
||
{
|
||
struct session_list *slp = (struct session_list *) sessp;
|
||
netsnmp_session *sp;
|
||
struct snmp_internal_session *isp;
|
||
netsnmp_request_list *rp, *orp = NULL, *freeme = NULL;
|
||
struct timeval now;
|
||
snmp_callback callback;
|
||
void *magic;
|
||
struct snmp_secmod_def *sptr;
|
||
|
||
sp = slp->session;
|
||
isp = slp->internal;
|
||
if (!sp || !isp) {
|
||
DEBUGMSGTL(("sess_read", "timeout fail: closing...\n"));
|
||
return;
|
||
}
|
||
|
||
netsnmp_get_monotonic_clock(&now);
|
||
|
||
/*
|
||
* For each request outstanding, check to see if it has expired.
|
||
*/
|
||
for (rp = isp->requests; rp; rp = rp->next_request) {
|
||
if (freeme != NULL) {
|
||
/*
|
||
* frees rp's after the for loop goes on to the next_request
|
||
*/
|
||
free((char *) freeme);
|
||
freeme = NULL;
|
||
}
|
||
|
||
if ((timercmp(&rp->expireM, &now, <))) {
|
||
if ((sptr = find_sec_mod(rp->pdu->securityModel)) != NULL &&
|
||
sptr->pdu_timeout != NULL) {
|
||
/*
|
||
* call security model if it needs to know about this
|
||
*/
|
||
(*sptr->pdu_timeout) (rp->pdu);
|
||
}
|
||
|
||
/*
|
||
* this timer has expired
|
||
*/
|
||
if (rp->retries >= sp->retries) {
|
||
if (rp->callback) {
|
||
callback = rp->callback;
|
||
magic = rp->cb_data;
|
||
} else {
|
||
callback = sp->callback;
|
||
magic = sp->callback_magic;
|
||
}
|
||
|
||
/*
|
||
* No more chances, delete this entry
|
||
*/
|
||
if (callback) {
|
||
callback(NETSNMP_CALLBACK_OP_TIMED_OUT, sp,
|
||
rp->pdu->reqid, rp->pdu, magic);
|
||
}
|
||
if (orp)
|
||
orp->next_request = rp->next_request;
|
||
else
|
||
isp->requests = rp->next_request;
|
||
if (isp->requestsEnd == rp)
|
||
isp->requestsEnd = orp;
|
||
snmp_free_pdu(rp->pdu);
|
||
freeme = rp;
|
||
continue; /* don't update orp below */
|
||
} else {
|
||
if (snmp_resend_request(slp, rp, TRUE)) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
orp = rp;
|
||
}
|
||
|
||
if (freeme != NULL) {
|
||
free((char *) freeme);
|
||
freeme = NULL;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* lexicographical compare two object identifiers.
|
||
* * Returns -1 if name1 < name2,
|
||
* * 0 if name1 = name2,
|
||
* * 1 if name1 > name2
|
||
* *
|
||
* * Caution: this method is called often by
|
||
* * command responder applications (ie, agent).
|
||
*/
|
||
int
|
||
snmp_oid_ncompare(const oid * in_name1,
|
||
size_t len1,
|
||
const oid * in_name2, size_t len2, size_t max_len)
|
||
{
|
||
register int len;
|
||
register const oid *name1 = in_name1;
|
||
register const oid *name2 = in_name2;
|
||
size_t min_len;
|
||
|
||
/*
|
||
* len = minimum of len1 and len2
|
||
*/
|
||
if (len1 < len2)
|
||
min_len = len1;
|
||
else
|
||
min_len = len2;
|
||
|
||
if (min_len > max_len)
|
||
min_len = max_len;
|
||
|
||
len = min_len;
|
||
|
||
/*
|
||
* find first non-matching OID
|
||
*/
|
||
while (len-- > 0) {
|
||
/*
|
||
* these must be done in seperate comparisons, since
|
||
* subtracting them and using that result has problems with
|
||
* subids > 2^31.
|
||
*/
|
||
if (*(name1) != *(name2)) {
|
||
if (*(name1) < *(name2))
|
||
return -1;
|
||
return 1;
|
||
}
|
||
name1++;
|
||
name2++;
|
||
}
|
||
|
||
if (min_len != max_len) {
|
||
/*
|
||
* both OIDs equal up to length of shorter OID
|
||
*/
|
||
if (len1 < len2)
|
||
return -1;
|
||
if (len2 < len1)
|
||
return 1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/** lexicographical compare two object identifiers.
|
||
*
|
||
* Caution: this method is called often by
|
||
* command responder applications (ie, agent).
|
||
*
|
||
* @return -1 if name1 < name2, 0 if name1 = name2, 1 if name1 > name2
|
||
*/
|
||
int
|
||
snmp_oid_compare(const oid * in_name1,
|
||
size_t len1, const oid * in_name2, size_t len2)
|
||
{
|
||
register int len;
|
||
register const oid *name1 = in_name1;
|
||
register const oid *name2 = in_name2;
|
||
|
||
/*
|
||
* len = minimum of len1 and len2
|
||
*/
|
||
if (len1 < len2)
|
||
len = len1;
|
||
else
|
||
len = len2;
|
||
/*
|
||
* find first non-matching OID
|
||
*/
|
||
while (len-- > 0) {
|
||
/*
|
||
* these must be done in seperate comparisons, since
|
||
* subtracting them and using that result has problems with
|
||
* subids > 2^31.
|
||
*/
|
||
if (*(name1) != *(name2)) {
|
||
if (*(name1) < *(name2))
|
||
return -1;
|
||
return 1;
|
||
}
|
||
name1++;
|
||
name2++;
|
||
}
|
||
/*
|
||
* both OIDs equal up to length of shorter OID
|
||
*/
|
||
if (len1 < len2)
|
||
return -1;
|
||
if (len2 < len1)
|
||
return 1;
|
||
return 0;
|
||
}
|
||
|
||
/** lexicographical compare two object identifiers and return the point where they differ
|
||
*
|
||
* Caution: this method is called often by
|
||
* command responder applications (ie, agent).
|
||
*
|
||
* @return -1 if name1 < name2, 0 if name1 = name2, 1 if name1 > name2 and offpt = len where name1 != name2
|
||
*/
|
||
int
|
||
netsnmp_oid_compare_ll(const oid * in_name1,
|
||
size_t len1, const oid * in_name2, size_t len2,
|
||
size_t *offpt)
|
||
{
|
||
register int len;
|
||
register const oid *name1 = in_name1;
|
||
register const oid *name2 = in_name2;
|
||
int initlen;
|
||
|
||
/*
|
||
* len = minimum of len1 and len2
|
||
*/
|
||
if (len1 < len2)
|
||
initlen = len = len1;
|
||
else
|
||
initlen = len = len2;
|
||
/*
|
||
* find first non-matching OID
|
||
*/
|
||
while (len-- > 0) {
|
||
/*
|
||
* these must be done in seperate comparisons, since
|
||
* subtracting them and using that result has problems with
|
||
* subids > 2^31.
|
||
*/
|
||
if (*(name1) != *(name2)) {
|
||
*offpt = initlen - len;
|
||
if (*(name1) < *(name2))
|
||
return -1;
|
||
return 1;
|
||
}
|
||
name1++;
|
||
name2++;
|
||
}
|
||
/*
|
||
* both OIDs equal up to length of shorter OID
|
||
*/
|
||
*offpt = initlen - len;
|
||
if (len1 < len2)
|
||
return -1;
|
||
if (len2 < len1)
|
||
return 1;
|
||
return 0;
|
||
}
|
||
|
||
/** Compares 2 OIDs to determine if they are equal up until the shortest length.
|
||
* @param in_name1 A pointer to the first oid.
|
||
* @param len1 length of the first OID (in segments, not bytes)
|
||
* @param in_name2 A pointer to the second oid.
|
||
* @param len2 length of the second OID (in segments, not bytes)
|
||
* @return 0 if they are equal, 1 if in_name1 is > in_name2, or -1 if <.
|
||
*/
|
||
int
|
||
snmp_oidtree_compare(const oid * in_name1,
|
||
size_t len1, const oid * in_name2, size_t len2)
|
||
{
|
||
int len = ((len1 < len2) ? len1 : len2);
|
||
|
||
return (snmp_oid_compare(in_name1, len, in_name2, len));
|
||
}
|
||
|
||
int
|
||
snmp_oidsubtree_compare(const oid * in_name1,
|
||
size_t len1, const oid * in_name2, size_t len2)
|
||
{
|
||
int len = ((len1 < len2) ? len1 : len2);
|
||
|
||
return (snmp_oid_compare(in_name1, len1, in_name2, len));
|
||
}
|
||
|
||
/** Compares 2 OIDs to determine if they are exactly equal.
|
||
* This should be faster than doing a snmp_oid_compare for different
|
||
* length OIDs, since the length is checked first and if != returns
|
||
* immediately. Might be very slighly faster if lengths are ==.
|
||
* @param in_name1 A pointer to the first oid.
|
||
* @param len1 length of the first OID (in segments, not bytes)
|
||
* @param in_name2 A pointer to the second oid.
|
||
* @param len2 length of the second OID (in segments, not bytes)
|
||
* @return 0 if they are equal, 1 if they are not.
|
||
*/
|
||
int
|
||
netsnmp_oid_equals(const oid * in_name1,
|
||
size_t len1, const oid * in_name2, size_t len2)
|
||
{
|
||
register const oid *name1 = in_name1;
|
||
register const oid *name2 = in_name2;
|
||
register int len = len1;
|
||
|
||
/*
|
||
* len = minimum of len1 and len2
|
||
*/
|
||
if (len1 != len2)
|
||
return 1;
|
||
/*
|
||
* Handle 'null' OIDs
|
||
*/
|
||
if (len1 == 0)
|
||
return 0; /* Two null OIDs are (trivially) the same */
|
||
if (!name1 || !name2)
|
||
return 1; /* Otherwise something's wrong, so report a non-match */
|
||
/*
|
||
* find first non-matching OID
|
||
*/
|
||
while (len-- > 0) {
|
||
/*
|
||
* these must be done in seperate comparisons, since
|
||
* subtracting them and using that result has problems with
|
||
* subids > 2^31.
|
||
*/
|
||
if (*(name1++) != *(name2++))
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
#ifndef NETSNMP_FEATURE_REMOVE_OID_IS_SUBTREE
|
||
/** Identical to netsnmp_oid_equals, except only the length up to len1 is compared.
|
||
* Functionally, this determines if in_name2 is equal or a subtree of in_name1
|
||
* @param in_name1 A pointer to the first oid.
|
||
* @param len1 length of the first OID (in segments, not bytes)
|
||
* @param in_name2 A pointer to the second oid.
|
||
* @param len2 length of the second OID (in segments, not bytes)
|
||
* @return 0 if one is a common prefix of the other.
|
||
*/
|
||
int
|
||
netsnmp_oid_is_subtree(const oid * in_name1,
|
||
size_t len1, const oid * in_name2, size_t len2)
|
||
{
|
||
if (len1 > len2)
|
||
return 1;
|
||
|
||
if (memcmp(in_name1, in_name2, len1 * sizeof(oid)))
|
||
return 1;
|
||
|
||
return 0;
|
||
}
|
||
#endif /* NETSNMP_FEATURE_REMOVE_OID_IS_SUBTREE */
|
||
|
||
/** Given two OIDs, determine the common prefix to them both.
|
||
* @param in_name1 A pointer to the first oid.
|
||
* @param len1 Length of the first oid.
|
||
* @param in_name2 A pointer to the second oid.
|
||
* @param len2 Length of the second oid.
|
||
* @return length of common prefix
|
||
* 0 if no common prefix, -1 on error.
|
||
*/
|
||
int
|
||
netsnmp_oid_find_prefix(const oid * in_name1, size_t len1,
|
||
const oid * in_name2, size_t len2)
|
||
{
|
||
int i;
|
||
size_t min_size;
|
||
|
||
if (!in_name1 || !in_name2 || !len1 || !len2)
|
||
return -1;
|
||
|
||
if (in_name1[0] != in_name2[0])
|
||
return 0; /* No match */
|
||
min_size = SNMP_MIN(len1, len2);
|
||
for(i = 0; i < (int)min_size; i++) {
|
||
if (in_name1[i] != in_name2[i])
|
||
return i; /* '<27>' is the first differing subidentifier
|
||
So the common prefix is 0..(i-1), of length i */
|
||
}
|
||
return min_size; /* The shorter OID is a prefix of the longer, and
|
||
hence is precisely the common prefix of the two.
|
||
Return its length. */
|
||
}
|
||
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
static int _check_range(struct tree *tp, long ltmp, int *resptr,
|
||
const char *errmsg)
|
||
{
|
||
char *cp = NULL;
|
||
char *temp = NULL;
|
||
int temp_len = 0;
|
||
int check = !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_DONT_CHECK_RANGE);
|
||
|
||
if (check && tp && tp->ranges) {
|
||
struct range_list *rp = tp->ranges;
|
||
while (rp) {
|
||
if (rp->low <= ltmp && ltmp <= rp->high) break;
|
||
/* Allow four digits per range value */
|
||
temp_len += ((rp->low != rp->high) ? 27 : 15 );
|
||
rp = rp->next;
|
||
}
|
||
if (!rp) {
|
||
*resptr = SNMPERR_RANGE;
|
||
temp = (char *)malloc( temp_len+strlen(errmsg)+7);
|
||
if ( temp ) {
|
||
/* Append the Display Hint range information to the error message */
|
||
sprintf( temp, "%s :: {", errmsg );
|
||
cp = temp+(strlen(temp));
|
||
for ( rp = tp->ranges; rp; rp=rp->next ) {
|
||
if ( rp->low != rp->high )
|
||
sprintf( cp, "(%d..%d), ", rp->low, rp->high );
|
||
else
|
||
sprintf( cp, "(%d), ", rp->low );
|
||
cp += strlen(cp);
|
||
}
|
||
*(cp-2) = '}'; /* Replace the final comma with a '}' */
|
||
*(cp-1) = 0;
|
||
snmp_set_detail(temp);
|
||
free(temp);
|
||
}
|
||
return 0;
|
||
}
|
||
}
|
||
free(temp);
|
||
return 1;
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
|
||
/*
|
||
* Add a variable with the requested name to the end of the list of
|
||
* variables for this pdu.
|
||
*/
|
||
netsnmp_variable_list *
|
||
snmp_pdu_add_variable(netsnmp_pdu *pdu,
|
||
const oid * name,
|
||
size_t name_length,
|
||
u_char type, const void * value, size_t len)
|
||
{
|
||
return snmp_varlist_add_variable(&pdu->variables, name, name_length,
|
||
type, value, len);
|
||
}
|
||
|
||
/*
|
||
* Add a variable with the requested name to the end of the list of
|
||
* variables for this pdu.
|
||
*/
|
||
netsnmp_variable_list *
|
||
snmp_varlist_add_variable(netsnmp_variable_list ** varlist,
|
||
const oid * name,
|
||
size_t name_length,
|
||
u_char type, const void * value, size_t len)
|
||
{
|
||
netsnmp_variable_list *vars, *vtmp;
|
||
int rc;
|
||
|
||
if (varlist == NULL)
|
||
return NULL;
|
||
|
||
vars = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);
|
||
if (vars == NULL)
|
||
return NULL;
|
||
|
||
vars->type = type;
|
||
|
||
rc = snmp_set_var_value( vars, value, len );
|
||
if (( 0 != rc ) ||
|
||
(name != NULL && snmp_set_var_objid(vars, name, name_length))) {
|
||
snmp_free_var(vars);
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* put only qualified variable onto varlist
|
||
*/
|
||
if (*varlist == NULL) {
|
||
*varlist = vars;
|
||
} else {
|
||
for (vtmp = *varlist; vtmp->next_variable;
|
||
vtmp = vtmp->next_variable);
|
||
|
||
vtmp->next_variable = vars;
|
||
}
|
||
|
||
return vars;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
* Add a variable with the requested name to the end of the list of
|
||
* variables for this pdu.
|
||
* Returns:
|
||
* may set these error types :
|
||
* SNMPERR_RANGE - type, value, or length not found or out of range
|
||
* SNMPERR_VALUE - value is not correct
|
||
* SNMPERR_VAR_TYPE - type is not correct
|
||
* SNMPERR_BAD_NAME - name is not found
|
||
*
|
||
* returns 0 if success, error if failure.
|
||
*/
|
||
int
|
||
snmp_add_var(netsnmp_pdu *pdu,
|
||
const oid * name, size_t name_length, char type, const char *value)
|
||
{
|
||
char *st;
|
||
const char *cp;
|
||
char *ecp, *vp;
|
||
int result = SNMPERR_SUCCESS;
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
int check = !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_DONT_CHECK_RANGE);
|
||
int do_hint = !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
|
||
NETSNMP_DS_LIB_NO_DISPLAY_HINT);
|
||
u_char *hintptr;
|
||
struct tree *tp;
|
||
struct enum_list *ep;
|
||
int itmp;
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
u_char *buf = NULL;
|
||
const u_char *buf_ptr = NULL;
|
||
size_t buf_len = 0, value_len = 0, tint;
|
||
in_addr_t atmp;
|
||
long ltmp;
|
||
#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
|
||
double dtmp;
|
||
float ftmp;
|
||
#endif /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
|
||
struct counter64 c64tmp;
|
||
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
tp = get_tree(name, name_length, get_tree_head());
|
||
if (!tp || !tp->type || tp->type > TYPE_SIMPLE_LAST) {
|
||
check = 0;
|
||
}
|
||
if (!(tp && tp->hint))
|
||
do_hint = 0;
|
||
|
||
if (tp && type == '=') {
|
||
/*
|
||
* generic assignment - let the tree node decide value format
|
||
*/
|
||
switch (tp->type) {
|
||
case TYPE_INTEGER:
|
||
case TYPE_INTEGER32:
|
||
type = 'i';
|
||
break;
|
||
case TYPE_GAUGE:
|
||
case TYPE_UNSIGNED32:
|
||
type = 'u';
|
||
break;
|
||
case TYPE_UINTEGER:
|
||
type = '3';
|
||
break;
|
||
case TYPE_COUNTER:
|
||
type = 'c';
|
||
break;
|
||
case TYPE_COUNTER64:
|
||
type = 'C';
|
||
break;
|
||
case TYPE_TIMETICKS:
|
||
type = 't';
|
||
break;
|
||
case TYPE_OCTETSTR:
|
||
type = 's';
|
||
break;
|
||
case TYPE_BITSTRING:
|
||
type = 'b';
|
||
break;
|
||
case TYPE_IPADDR:
|
||
type = 'a';
|
||
break;
|
||
case TYPE_OBJID:
|
||
type = 'o';
|
||
break;
|
||
}
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
|
||
switch (type) {
|
||
case 'i':
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
if (check && tp->type != TYPE_INTEGER
|
||
&& tp->type != TYPE_INTEGER32) {
|
||
value = "INTEGER";
|
||
result = SNMPERR_VALUE;
|
||
goto type_error;
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
if (!*value)
|
||
goto fail;
|
||
ltmp = strtol(value, &ecp, 10);
|
||
if (*ecp) {
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
ep = tp ? tp->enums : NULL;
|
||
while (ep) {
|
||
if (strcmp(value, ep->label) == 0) {
|
||
ltmp = ep->value;
|
||
break;
|
||
}
|
||
ep = ep->next;
|
||
}
|
||
if (!ep) {
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
result = SNMPERR_RANGE; /* ?? or SNMPERR_VALUE; */
|
||
snmp_set_detail(value);
|
||
break;
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
}
|
||
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
if (!_check_range(tp, ltmp, &result, value))
|
||
break;
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_INTEGER,
|
||
<mp, sizeof(ltmp));
|
||
break;
|
||
|
||
case 'u':
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
if (check && tp->type != TYPE_GAUGE && tp->type != TYPE_UNSIGNED32) {
|
||
value = "Unsigned32";
|
||
result = SNMPERR_VALUE;
|
||
goto type_error;
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
ltmp = strtoul(value, &ecp, 10);
|
||
if (*value && !*ecp)
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_UNSIGNED,
|
||
<mp, sizeof(ltmp));
|
||
else
|
||
goto fail;
|
||
break;
|
||
|
||
case '3':
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
if (check && tp->type != TYPE_UINTEGER) {
|
||
value = "UInteger32";
|
||
result = SNMPERR_VALUE;
|
||
goto type_error;
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
ltmp = strtoul(value, &ecp, 10);
|
||
if (*value && !*ecp)
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_UINTEGER,
|
||
<mp, sizeof(ltmp));
|
||
else
|
||
goto fail;
|
||
break;
|
||
|
||
case 'c':
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
if (check && tp->type != TYPE_COUNTER) {
|
||
value = "Counter32";
|
||
result = SNMPERR_VALUE;
|
||
goto type_error;
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
ltmp = strtoul(value, &ecp, 10);
|
||
if (*value && !*ecp)
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_COUNTER,
|
||
<mp, sizeof(ltmp));
|
||
else
|
||
goto fail;
|
||
break;
|
||
|
||
case 'C':
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
if (check && tp->type != TYPE_COUNTER64) {
|
||
value = "Counter64";
|
||
result = SNMPERR_VALUE;
|
||
goto type_error;
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
if (read64(&c64tmp, value))
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_COUNTER64,
|
||
&c64tmp, sizeof(c64tmp));
|
||
else
|
||
goto fail;
|
||
break;
|
||
|
||
case 't':
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
if (check && tp->type != TYPE_TIMETICKS) {
|
||
value = "Timeticks";
|
||
result = SNMPERR_VALUE;
|
||
goto type_error;
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
ltmp = strtoul(value, &ecp, 10);
|
||
if (*value && !*ecp)
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_TIMETICKS,
|
||
<mp, sizeof(long));
|
||
else
|
||
goto fail;
|
||
break;
|
||
|
||
case 'a':
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
if (check && tp->type != TYPE_IPADDR) {
|
||
value = "IpAddress";
|
||
result = SNMPERR_VALUE;
|
||
goto type_error;
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
atmp = inet_addr(value);
|
||
if (atmp != (in_addr_t) -1 || !strcmp(value, "255.255.255.255"))
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_IPADDRESS,
|
||
&atmp, sizeof(atmp));
|
||
else
|
||
goto fail;
|
||
break;
|
||
|
||
case 'o':
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
if (check && tp->type != TYPE_OBJID) {
|
||
value = "OBJECT IDENTIFIER";
|
||
result = SNMPERR_VALUE;
|
||
goto type_error;
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
if ((buf = (u_char *)malloc(sizeof(oid) * MAX_OID_LEN)) == NULL) {
|
||
result = SNMPERR_MALLOC;
|
||
} else {
|
||
tint = MAX_OID_LEN;
|
||
if (snmp_parse_oid(value, (oid *) buf, &tint)) {
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_OBJECT_ID,
|
||
buf, sizeof(oid) * tint);
|
||
} else {
|
||
result = snmp_errno; /*MTCRITICAL_RESOURCE */
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 's':
|
||
case 'x':
|
||
case 'd':
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
if (check && tp->type != TYPE_OCTETSTR && tp->type != TYPE_BITSTRING) {
|
||
value = "OCTET STRING";
|
||
result = SNMPERR_VALUE;
|
||
goto type_error;
|
||
}
|
||
if ('s' == type && do_hint && !parse_octet_hint(tp->hint, value, &hintptr, &itmp)) {
|
||
if (_check_range(tp, itmp, &result, "Value does not match DISPLAY-HINT")) {
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_OCTET_STR,
|
||
hintptr, itmp);
|
||
}
|
||
SNMP_FREE(hintptr);
|
||
hintptr = buf;
|
||
break;
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
if (type == 'd') {
|
||
if (!snmp_decimal_to_binary
|
||
(&buf, &buf_len, &value_len, 1, value)) {
|
||
result = SNMPERR_VALUE;
|
||
snmp_set_detail(value);
|
||
break;
|
||
}
|
||
buf_ptr = buf;
|
||
} else if (type == 'x') {
|
||
if (!snmp_hex_to_binary(&buf, &buf_len, &value_len, 1, value)) {
|
||
result = SNMPERR_VALUE;
|
||
snmp_set_detail(value);
|
||
break;
|
||
}
|
||
buf_ptr = buf;
|
||
} else if (type == 's') {
|
||
buf_ptr = (const u_char *)value;
|
||
value_len = strlen(value);
|
||
}
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
if (!_check_range(tp, value_len, &result, "Bad string length"))
|
||
break;
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_OCTET_STR,
|
||
buf_ptr, value_len);
|
||
break;
|
||
|
||
case 'n':
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_NULL, NULL, 0);
|
||
break;
|
||
|
||
case 'b':
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
if (check && (tp->type != TYPE_BITSTRING || !tp->enums)) {
|
||
value = "BITS";
|
||
result = SNMPERR_VALUE;
|
||
goto type_error;
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
tint = 0;
|
||
if ((buf = (u_char *) malloc(256)) == NULL) {
|
||
result = SNMPERR_MALLOC;
|
||
break;
|
||
} else {
|
||
buf_len = 256;
|
||
memset(buf, 0, buf_len);
|
||
}
|
||
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
for (ep = tp ? tp->enums : NULL; ep; ep = ep->next) {
|
||
if (ep->value / 8 >= (int) tint) {
|
||
tint = ep->value / 8 + 1;
|
||
}
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
|
||
vp = strdup(value);
|
||
if (!vp) {
|
||
SNMP_FREE(buf);
|
||
goto fail;
|
||
}
|
||
for (cp = strtok_r(vp, " ,\t", &st); cp; cp = strtok_r(NULL, " ,\t", &st)) {
|
||
int ix, bit;
|
||
|
||
ltmp = strtoul(cp, &ecp, 0);
|
||
if (*ecp != 0) {
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
for (ep = tp ? tp->enums : NULL; ep != NULL; ep = ep->next) {
|
||
if (strcmp(ep->label, cp) == 0) {
|
||
break;
|
||
}
|
||
}
|
||
if (ep != NULL) {
|
||
ltmp = ep->value;
|
||
} else {
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
result = SNMPERR_RANGE; /* ?? or SNMPERR_VALUE; */
|
||
snmp_set_detail(cp);
|
||
SNMP_FREE(buf);
|
||
SNMP_FREE(vp);
|
||
goto out;
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
}
|
||
|
||
ix = ltmp / 8;
|
||
if (ix >= (int) tint) {
|
||
tint = ix + 1;
|
||
}
|
||
if (ix >= (int)buf_len && !snmp_realloc(&buf, &buf_len)) {
|
||
result = SNMPERR_MALLOC;
|
||
break;
|
||
}
|
||
bit = 0x80 >> ltmp % 8;
|
||
buf[ix] |= bit;
|
||
|
||
}
|
||
SNMP_FREE(vp);
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_OCTET_STR,
|
||
buf, tint);
|
||
break;
|
||
|
||
#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
|
||
case 'U':
|
||
if (read64(&c64tmp, value))
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_OPAQUE_U64,
|
||
&c64tmp, sizeof(c64tmp));
|
||
else
|
||
goto fail;
|
||
break;
|
||
|
||
case 'I':
|
||
if (read64(&c64tmp, value))
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_OPAQUE_I64,
|
||
&c64tmp, sizeof(c64tmp));
|
||
else
|
||
goto fail;
|
||
break;
|
||
|
||
case 'F':
|
||
if (sscanf(value, "%f", &ftmp) == 1)
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_OPAQUE_FLOAT,
|
||
&ftmp, sizeof(ftmp));
|
||
else
|
||
goto fail;
|
||
break;
|
||
|
||
case 'D':
|
||
if (sscanf(value, "%lf", &dtmp) == 1)
|
||
snmp_pdu_add_variable(pdu, name, name_length, ASN_OPAQUE_DOUBLE,
|
||
&dtmp, sizeof(dtmp));
|
||
else
|
||
goto fail;
|
||
break;
|
||
#endif /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
|
||
|
||
default:
|
||
result = SNMPERR_VAR_TYPE;
|
||
buf = (u_char *)calloc(1, 4);
|
||
if (buf != NULL) {
|
||
sprintf((char *)buf, "\"%c\"", type);
|
||
snmp_set_detail((char *)buf);
|
||
}
|
||
break;
|
||
}
|
||
|
||
SNMP_FREE(buf);
|
||
SET_SNMP_ERROR(result);
|
||
return result;
|
||
|
||
#ifndef NETSNMP_DISABLE_MIB_LOADING
|
||
type_error:
|
||
{
|
||
char error_msg[256];
|
||
char undef_msg[32];
|
||
const char *var_type;
|
||
switch (tp->type) {
|
||
case TYPE_OBJID:
|
||
var_type = "OBJECT IDENTIFIER";
|
||
break;
|
||
case TYPE_OCTETSTR:
|
||
var_type = "OCTET STRING";
|
||
break;
|
||
case TYPE_INTEGER:
|
||
var_type = "INTEGER";
|
||
break;
|
||
case TYPE_NETADDR:
|
||
var_type = "NetworkAddress";
|
||
break;
|
||
case TYPE_IPADDR:
|
||
var_type = "IpAddress";
|
||
break;
|
||
case TYPE_COUNTER:
|
||
var_type = "Counter32";
|
||
break;
|
||
case TYPE_GAUGE:
|
||
var_type = "Gauge32";
|
||
break;
|
||
case TYPE_TIMETICKS:
|
||
var_type = "Timeticks";
|
||
break;
|
||
case TYPE_OPAQUE:
|
||
var_type = "Opaque";
|
||
break;
|
||
case TYPE_NULL:
|
||
var_type = "Null";
|
||
break;
|
||
case TYPE_COUNTER64:
|
||
var_type = "Counter64";
|
||
break;
|
||
case TYPE_BITSTRING:
|
||
var_type = "BITS";
|
||
break;
|
||
case TYPE_NSAPADDRESS:
|
||
var_type = "NsapAddress";
|
||
break;
|
||
case TYPE_UINTEGER:
|
||
var_type = "UInteger";
|
||
break;
|
||
case TYPE_UNSIGNED32:
|
||
var_type = "Unsigned32";
|
||
break;
|
||
case TYPE_INTEGER32:
|
||
var_type = "Integer32";
|
||
break;
|
||
default:
|
||
sprintf(undef_msg, "TYPE_%d", tp->type);
|
||
var_type = undef_msg;
|
||
}
|
||
snprintf(error_msg, sizeof(error_msg),
|
||
"Type of attribute is %s, not %s", var_type, value);
|
||
error_msg[ sizeof(error_msg)-1 ] = 0;
|
||
result = SNMPERR_VAR_TYPE;
|
||
snmp_set_detail(error_msg);
|
||
goto out;
|
||
}
|
||
#endif /* NETSNMP_DISABLE_MIB_LOADING */
|
||
fail:
|
||
result = SNMPERR_VALUE;
|
||
snmp_set_detail(value);
|
||
out:
|
||
SET_SNMP_ERROR(result);
|
||
return result;
|
||
}
|
||
|
||
/*
|
||
* returns NULL or internal pointer to session
|
||
* use this pointer for the other snmp_sess* routines,
|
||
* which guarantee action will occur ONLY for this given session.
|
||
*/
|
||
void *
|
||
snmp_sess_pointer(netsnmp_session * session)
|
||
{
|
||
struct session_list *slp;
|
||
|
||
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
for (slp = Sessions; slp; slp = slp->next) {
|
||
if (slp->session == session) {
|
||
break;
|
||
}
|
||
}
|
||
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
|
||
if (slp == NULL) {
|
||
snmp_errno = SNMPERR_BAD_SESSION; /*MTCRITICAL_RESOURCE */
|
||
return (NULL);
|
||
}
|
||
return ((void *) slp);
|
||
}
|
||
|
||
/*
|
||
* Input : an opaque pointer, returned by snmp_sess_open.
|
||
* returns NULL or pointer to session.
|
||
*/
|
||
netsnmp_session *
|
||
snmp_sess_session(void *sessp)
|
||
{
|
||
struct session_list *slp = (struct session_list *) sessp;
|
||
if (slp == NULL)
|
||
return (NULL);
|
||
return (slp->session);
|
||
}
|
||
|
||
/**
|
||
* Look up a session that already may have been closed.
|
||
*
|
||
* @param sessp Opaque pointer, returned by snmp_sess_open.
|
||
*
|
||
* @return Pointer to session upon success or NULL upon failure.
|
||
*
|
||
* @see snmp_sess_session()
|
||
*/
|
||
netsnmp_session *
|
||
snmp_sess_session_lookup(void *sessp)
|
||
{
|
||
struct session_list *slp;
|
||
|
||
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
for (slp = Sessions; slp; slp = slp->next) {
|
||
if (slp == sessp) {
|
||
break;
|
||
}
|
||
}
|
||
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
|
||
return (netsnmp_session *)slp;
|
||
}
|
||
|
||
|
||
/*
|
||
* returns NULL or internal pointer to session
|
||
* use this pointer for the other snmp_sess* routines,
|
||
* which guarantee action will occur ONLY for this given session.
|
||
*/
|
||
netsnmp_session *
|
||
snmp_sess_lookup_by_name(const char *paramName)
|
||
{
|
||
struct session_list *slp;
|
||
|
||
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
for (slp = Sessions; slp; slp = slp->next) {
|
||
if (NULL == slp->session->paramName)
|
||
continue;
|
||
if (strcmp(paramName, slp->session->paramName) == 0)
|
||
break;
|
||
}
|
||
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
|
||
|
||
if (slp == NULL)
|
||
return NULL;
|
||
|
||
return slp->session;
|
||
}
|
||
|
||
|
||
/*
|
||
* snmp_sess_transport: takes an opaque pointer (as returned by
|
||
* snmp_sess_open or snmp_sess_pointer) and returns the corresponding
|
||
* netsnmp_transport pointer (or NULL if the opaque pointer does not correspond
|
||
* to an active internal session).
|
||
*/
|
||
|
||
netsnmp_transport *
|
||
snmp_sess_transport(void *sessp)
|
||
{
|
||
struct session_list *slp = (struct session_list *) sessp;
|
||
if (slp == NULL) {
|
||
return NULL;
|
||
} else {
|
||
return slp->transport;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
* snmp_sess_transport_set: set the transport pointer for the opaque
|
||
* session pointer sp.
|
||
*/
|
||
|
||
void
|
||
snmp_sess_transport_set(void *sp, netsnmp_transport *t)
|
||
{
|
||
struct session_list *slp = (struct session_list *) sp;
|
||
if (slp != NULL) {
|
||
slp->transport = t;
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* snmp_duplicate_objid: duplicates (mallocs) an objid based on the
|
||
* input objid
|
||
*/
|
||
oid *
|
||
snmp_duplicate_objid(const oid * objToCopy, size_t objToCopyLen)
|
||
{
|
||
oid *returnOid;
|
||
if (objToCopy != NULL && objToCopyLen != 0) {
|
||
returnOid = (oid *) malloc(objToCopyLen * sizeof(oid));
|
||
if (returnOid) {
|
||
memcpy(returnOid, objToCopy, objToCopyLen * sizeof(oid));
|
||
}
|
||
} else
|
||
returnOid = NULL;
|
||
return returnOid;
|
||
}
|
||
|
||
#ifndef NETSNMP_FEATURE_REMOVE_STATISTICS
|
||
/*
|
||
* generic statistics counter functions
|
||
*/
|
||
static u_int statistics[NETSNMP_STAT_MAX_STATS];
|
||
|
||
u_int
|
||
snmp_increment_statistic(int which)
|
||
{
|
||
if (which >= 0 && which < NETSNMP_STAT_MAX_STATS) {
|
||
statistics[which]++;
|
||
return statistics[which];
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
u_int
|
||
snmp_increment_statistic_by(int which, int count)
|
||
{
|
||
if (which >= 0 && which < NETSNMP_STAT_MAX_STATS) {
|
||
statistics[which] += count;
|
||
return statistics[which];
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
u_int
|
||
snmp_get_statistic(int which)
|
||
{
|
||
if (which >= 0 && which < NETSNMP_STAT_MAX_STATS)
|
||
return statistics[which];
|
||
return 0;
|
||
}
|
||
|
||
void
|
||
snmp_init_statistics(void)
|
||
{
|
||
memset(statistics, 0, sizeof(statistics));
|
||
}
|
||
#endif /* NETSNMP_FEATURE_REMOVE_STATISTICS */
|
||
/** @} */
|