blob: 45267fd5ccf5ff2b72bafac30030421b75f8184c [file] [log] [blame]
/* 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.
*/
/** @defgroup library The Net-SNMP library
* @{
*/
/*
* snmp_api.c - API for access to snmp.
*/
#include <net-snmp/net-snmp-config.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
# ifdef WIN32
# include <sys/timeb.h>
# else
# include <sys/time.h>
# endif
# 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_WINSOCK_H
#include <winsock.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>
#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>
static void _init_snmp(void);
#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.
*/
#define MAX_PACKET_LENGTH (0x7fffffff)
#ifndef NETSNMP_STREAM_QUEUE_LEN
#define NETSNMP_STREAM_QUEUE_LEN 5
#endif
#ifndef BSD4_3
#define BSD4_2
#endif
#ifndef FD_SET
typedef long fd_mask;
#define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */
#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
#define FD_ZERO(p) memset((p), 0, sizeof(*(p)))
#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 1000000L
#define DEFAULT_REMPORT SNMP_PORT
#define DEFAULT_ENTERPRISE default_enterprise
#define DEFAULT_TIME 0
/*
* don't set higher than 0x7fffffff, and I doubt it should be that high
* * = 4 gig snmp messages max
*/
#define MAXIMUM_PACKET_SIZE 0x7fffffff
/*
* 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;
size_t packet_len, packet_size;
};
/*
* The list of active/open sessions.
*/
struct session_list {
struct session_list *next;
netsnmp_session *session;
netsnmp_transport *transport;
struct snmp_internal_session *internal;
};
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 */
};
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.
*/
int snmp_build(u_char ** pkt, size_t * pkt_len,
size_t * offset, netsnmp_session * pss,
netsnmp_pdu *pdu);
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_probe_pdu(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);
int snmp_get_errno(void);
void snmp_synch_reset(netsnmp_session * notused);
void snmp_synch_setup(netsnmp_session * notused);
#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_RESPONSE:
return "RESPONSE";
case SNMP_MSG_SET:
return "SET";
case SNMP_MSG_GETBULK:
return "GETBULK";
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);
}
/*
* 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
*/
/*
* don't allow zero value to repeat init
*/
#ifdef SVR4
srand48(tv.tv_sec ^ tv.tv_usec);
tmpReqid = lrand48();
tmpMsgid = lrand48();
#else
srandom(tv.tv_sec ^ tv.tv_usec);
tmpReqid = random();
tmpMsgid = random();
#endif
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", "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", "ipx", "/36880");
netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_HEX_OUTPUT_LENGTH, 16);
#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->remote_port = SNMP_DEFAULT_REMPORT;
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->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);
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defCommunity",
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_COMMUNITY);
#endif
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_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_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_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_snmpv3(type);
init_snmp_alarm();
init_snmp_enum(type);
init_vacm();
read_premib_configs();
#ifndef NETSNMP_DISABLE_MIB_LOADING
netsnmp_init_mib();
#endif /* NETSNMP_DISABLE_MIB_LOADING */
read_configs();
} /* end init_snmp() */
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 */
unregister_all_config_handlers();
netsnmp_container_free_list();
clear_sec_mod();
clear_snmp_enum();
netsnmp_clear_tdomain_list();
clear_callback();
netsnmp_ds_shutdown();
clear_user_list();
netsnmp_clear_default_target();
netsnmp_clear_default_domain();
free_etimelist();
init_snmp_init_done = 0;
_init_snmp_init_done = 0;
}
/*
* 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_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
slp->next = Sessions;
Sessions = slp;
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
return (slp->session);
}
/*
* extended open
*/
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_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
slp->next = Sessions;
Sessions = slp;
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
return (slp->session);
}
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;
size_t i;
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 = (char *)malloc(strlen(in_session->peername) + 1);
if (session->peername == NULL) {
snmp_sess_close(slp);
in_session->s_snmp_errno = SNMPERR_MALLOC;
return (NULL);
}
strcpy(session->peername, in_session->peername);
}
/*
* 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 (session->securityLevel == 0)
session->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
if (in_session->securityAuthProtoLen > 0) {
session->securityAuthProto =
snmp_duplicate_objid(in_session->securityAuthProto,
in_session->securityAuthProtoLen);
if (session->securityAuthProto == NULL) {
snmp_sess_close(slp);
in_session->s_snmp_errno = SNMPERR_MALLOC;
return (NULL);
}
} else if (get_default_authtype(&i) != NULL) {
session->securityAuthProto =
snmp_duplicate_objid(get_default_authtype(NULL), i);
session->securityAuthProtoLen = i;
}
if (in_session->securityPrivProtoLen > 0) {
session->securityPrivProto =
snmp_duplicate_objid(in_session->securityPrivProto,
in_session->securityPrivProtoLen);
if (session->securityPrivProto == NULL) {
snmp_sess_close(slp);
in_session->s_snmp_errno = SNMPERR_MALLOC;
return (NULL);
}
} else if (get_default_privtype(&i) != NULL) {
session->securityPrivProto =
snmp_duplicate_objid(get_default_privtype(NULL), i);
session->securityPrivProtoLen = i;
}
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 ((in_session->securityAuthKeyLen <= 0) &&
((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_AUTHMASTERKEY)))) {
size_t buflen = sizeof(session->securityAuthKey);
u_char *tmpp = session->securityAuthKey;
session->securityAuthKeyLen = 0;
/* it will be a hex string */
if (!snmp_hex_to_binary(&tmpp, &buflen,
&session->securityAuthKeyLen, 0, cp)) {
snmp_set_detail("error parsing authentication master key");
snmp_sess_close(slp);
return NULL;
}
} else if ((in_session->securityAuthKeyLen <= 0) &&
((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_AUTHPASSPHRASE)) ||
(cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_PASSPHRASE)))) {
session->securityAuthKeyLen = USM_AUTH_KU_LEN;
if (generate_Ku(session->securityAuthProto,
session->securityAuthProtoLen,
(u_char *) cp, strlen(cp),
session->securityAuthKey,
&session->securityAuthKeyLen) != SNMPERR_SUCCESS) {
snmp_set_detail
("Error generating a key (Ku) from the supplied authentication pass phrase.");
snmp_sess_close(slp);
return NULL;
}
}
if ((in_session->securityPrivKeyLen <= 0) &&
((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_PRIVMASTERKEY)))) {
size_t buflen = sizeof(session->securityPrivKey);
u_char *tmpp = session->securityPrivKey;
session->securityPrivKeyLen = 0;
/* it will be a hex string */
if (!snmp_hex_to_binary(&tmpp, &buflen,
&session->securityPrivKeyLen, 0, cp)) {
snmp_set_detail("error parsing encryption master key");
snmp_sess_close(slp);
return NULL;
}
} else if ((in_session->securityPrivKeyLen <= 0) &&
((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_PRIVPASSPHRASE)) ||
(cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_PASSPHRASE)))) {
session->securityPrivKeyLen = USM_PRIV_KU_LEN;
if (generate_Ku(session->securityAuthProto,
session->securityAuthProtoLen,
(u_char *) cp, strlen(cp),
session->securityPrivKey,
&session->securityPrivKeyLen) != SNMPERR_SUCCESS) {
snmp_set_detail
("Error generating a key (Ku) from the supplied privacy pass phrase.");
snmp_sess_close(slp);
return NULL;
}
}
if (session->retries == SNMP_DEFAULT_RETRIES)
session->retries = DEFAULT_RETRIES;
if (session->timeout == SNMP_DEFAULT_TIMEOUT)
session->timeout = DEFAULT_TIMEOUT;
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 &&
sptr->session_open != NULL) {
/*
* security module specific inialization
*/
(*sptr->session_open) (session);
}
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;
}
/**
* 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_pdu *pdu = NULL, *response = NULL;
netsnmp_session *session;
unsigned int i;
int status;
if (slp == NULL || slp->session == NULL) {
return 0;
}
session = slp->session;
/*
* 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->flags & SNMP_FLAGS_DONT_PROBE) == SNMP_FLAGS_DONT_PROBE)
return 1;
if (session->version == SNMP_VERSION_3) {
if (session->securityEngineIDLen == 0) {
if (snmpv3_build_probe_pdu(&pdu) != 0) {
DEBUGMSGTL(("snmp_api", "unable to create probe PDU\n"));
return 0;
}
DEBUGMSGTL(("snmp_api", "probing for engineID...\n"));
session->flags |= SNMP_FLAGS_DONT_PROBE; /* prevent recursion */
status = snmp_sess_synch_response(slp, pdu, &response);
if ((response == NULL) && (status == STAT_SUCCESS)) {
status = STAT_ERROR;
}
switch (status) {
case STAT_SUCCESS:
in_session->s_snmp_errno = SNMPERR_INVALID_MSG; /* XX?? */
DEBUGMSGTL(("snmp_sess_open",
"error: expected Report as response to probe: %s (%d)\n",
snmp_errstring(response->errstat),
response->errstat));
break;
case STAT_ERROR: /* this is what we expected -> Report == STAT_ERROR */
in_session->s_snmp_errno = SNMPERR_UNKNOWN_ENG_ID;
break;
case STAT_TIMEOUT:
in_session->s_snmp_errno = SNMPERR_TIMEOUT;
default:
DEBUGMSGTL(("snmp_sess_open",
"unable to connect with remote engine: %s (%d)\n",
snmp_api_errstring(session->s_snmp_errno),
session->s_snmp_errno));
break;
}
if (slp->session->securityEngineIDLen == 0) {
DEBUGMSGTL(("snmp_api",
"unable to determine remote engine ID\n"));
/* clear the flag so that probe occurs on next inform */
session->flags &= ~SNMP_FLAGS_DONT_PROBE;
return 0;
}
in_session->s_snmp_errno = SNMPERR_SUCCESS;
if (snmp_get_do_debugging()) {
DEBUGMSGTL(("snmp_sess_open",
" probe found engineID: "));
for (i = 0; i < slp->session->securityEngineIDLen; i++)
DEBUGMSG(("snmp_sess_open", "%02x",
slp->session->securityEngineID[i]));
DEBUGMSG(("snmp_sess_open", "\n"));
}
}
/*
* if boot/time supplied set it for this engineID
*/
if (session->engineBoots || session->engineTime) {
set_enginetime(session->securityEngineID,
session->securityEngineIDLen,
session->engineBoots, session->engineTime,
TRUE);
}
if (create_user_from_session(slp->session) != SNMPERR_SUCCESS) {
in_session->s_snmp_errno = SNMPERR_UNKNOWN_USER_NAME; /* XX?? */
DEBUGMSGTL(("snmp_api",
"snmpv3_engine_probe(): failed(2) to create a new user from session\n"));
return 0;
}
}
return 1;
}
/*******************************************************************-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)
{
struct session_list *slp;
netsnmp_session *session;
char *clientaddr_save = NULL;
in_session->s_snmp_errno = 0;
in_session->s_errno = 0;
_init_snmp();
if ((slp = snmp_sess_copy(in_session)) == NULL) {
return (NULL);
}
session = slp->session;
slp->transport = NULL;
if (NULL != session->localname) {
clientaddr_save = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_CLIENT_ADDR);
netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_CLIENT_ADDR, session->localname);
}
if (session->flags & SNMP_FLAGS_STREAM_SOCKET) {
slp->transport =
netsnmp_tdomain_transport_full("snmp", session->peername,
session->local_port, "tcp", NULL);
} else {
slp->transport =
netsnmp_tdomain_transport_full("snmp", session->peername,
session->local_port, "udp", NULL);
}
if (NULL != session->localname)
netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_CLIENT_ADDR, clientaddr_save);
if (slp->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(session->peername);
snmp_sess_close(slp);
return NULL;
}
session->rcvMsgMaxSize = slp->transport->msgMaxSize;
if (!snmpv3_engineID_probe(slp, in_session)) {
snmp_sess_close(slp);
return NULL;
}
if (create_user_from_session(slp->session) != SNMPERR_SUCCESS) {
in_session->s_snmp_errno = SNMPERR_UNKNOWN_USER_NAME; /* XX?? */
DEBUGMSGTL(("snmp_api",
"_sess_open(): failed(2) to create a new user from session\n"));
snmp_sess_close(slp);
return NULL;
}
session->flags &= ~SNMP_FLAGS_DONT_PROBE;
return (void *) slp;
} /* end snmp_sess_open() */
/*
* 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_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
slp->next = Sessions;
Sessions = slp;
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
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_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
slp->next = Sessions;
Sessions = slp;
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
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;
_init_snmp();
if (transport == NULL)
return NULL;
if (in_session == NULL) {
transport->f_close(transport);
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;
slp->session->rcvMsgMaxSize = transport->msgMaxSize;
if (slp->session->version == SNMP_VERSION_3) {
DEBUGMSGTL(("snmp_sess_add",
"adding v3 session -- engineID probe now\n"));
if (!snmpv3_engineID_probe(slp, in_session)) {
DEBUGMSGTL(("snmp_sess_add", "engine ID probe failed\n"));
snmp_sess_close(slp);
return NULL;
}
if (create_user_from_session(slp->session) != SNMPERR_SUCCESS) {
in_session->s_snmp_errno = SNMPERR_UNKNOWN_USER_NAME;
DEBUGMSGTL(("snmp_api",
"snmp_sess_add(): failed(2) to create a new user from session\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;
}
/*
* create_user_from_session(netsnmp_session *session):
*
* creates a user in the usm table from the information in a session.
* If the user already exists, it is updated with the current
* information from the session
*
* Parameters:
* session -- IN: pointer to the session to use when creating the user.
*
* Returns:
* SNMPERR_SUCCESS
* SNMPERR_GENERR
*/
int
create_user_from_session(netsnmp_session * session)
{
struct usmUser *user;
int user_just_created = 0;
char *cp;
/*
* - don't create-another/copy-into user for this session by default
* - bail now (no error) if we don't have an engineID
*/
if (SNMP_FLAGS_USER_CREATED == (session->flags & SNMP_FLAGS_USER_CREATED) ||
session->securityModel != SNMP_SEC_MODEL_USM ||
session->version != SNMP_VERSION_3 ||
session->securityNameLen == 0 ||
session->securityEngineIDLen == 0)
return SNMPERR_SUCCESS;
session->flags |= SNMP_FLAGS_USER_CREATED;
/*
* now that we have the engineID, create an entry in the USM list
* for this user using the information in the session
*/
user = usm_get_user_from_list(session->securityEngineID,
session->securityEngineIDLen,
session->securityName,
usm_get_userList(), 0);
if (user == NULL) {
DEBUGMSGTL(("snmp_api", "Building user %s...\n",
session->securityName));
/*
* user doesn't exist so we create and add it
*/
user = (struct usmUser *) calloc(1, sizeof(struct usmUser));
if (user == NULL)
return SNMPERR_GENERR;
/*
* copy in the securityName
*/
if (session->securityName) {
user->name = strdup(session->securityName);
user->secName = strdup(session->securityName);
if (user->name == NULL || user->secName == NULL) {
usm_free_user(user);
return SNMPERR_GENERR;
}
}
/*
* copy in the engineID
*/
user->engineID = netsnmp_memdup(session->securityEngineID,
session->securityEngineIDLen);
if (!user->engineID) {
usm_free_user(user);
return SNMPERR_GENERR;
}
user->engineIDLen = session->securityEngineIDLen;
user_just_created = 1;
}
/*
* copy the auth protocol
*/
if (user->authProtocol == NULL && session->securityAuthProto != NULL) {
SNMP_FREE(user->authProtocol);
user->authProtocol =
snmp_duplicate_objid(session->securityAuthProto,
session->securityAuthProtoLen);
if (user->authProtocol == NULL) {
usm_free_user(user);
return SNMPERR_GENERR;
}
user->authProtocolLen = session->securityAuthProtoLen;
}
/*
* copy the priv protocol
*/
if (user->privProtocol == NULL && session->securityPrivProto != NULL) {
SNMP_FREE(user->privProtocol);
user->privProtocol =
snmp_duplicate_objid(session->securityPrivProto,
session->securityPrivProtoLen);
if (user->privProtocol == NULL) {
usm_free_user(user);
return SNMPERR_GENERR;
}
user->privProtocolLen = session->securityPrivProtoLen;
}
/*
* copy in the authentication Key. If not localized, localize it
*/
if (user->authKey == NULL) {
if (session->securityAuthLocalKey != NULL
&& session->securityAuthLocalKeyLen != 0) {
/* already localized key passed in. use it */
SNMP_FREE(user->authKey);
user->authKey = netsnmp_memdup(session->securityAuthLocalKey,
session->securityAuthLocalKeyLen);
if (!user->authKey) {
usm_free_user(user);
return SNMPERR_GENERR;
}
user->authKeyLen = session->securityAuthLocalKeyLen;
} else if (session->securityAuthKey != NULL
&& session->securityAuthKeyLen != 0) {
SNMP_FREE(user->authKey);
user->authKey = (u_char *) calloc(1, USM_LENGTH_KU_HASHBLOCK);
if (user->authKey == NULL) {
usm_free_user(user);
return SNMPERR_GENERR;
}
user->authKeyLen = USM_LENGTH_KU_HASHBLOCK;
if (generate_kul(user->authProtocol, user->authProtocolLen,
session->securityEngineID,
session->securityEngineIDLen,
session->securityAuthKey,
session->securityAuthKeyLen, user->authKey,
&user->authKeyLen) != SNMPERR_SUCCESS) {
usm_free_user(user);
return SNMPERR_GENERR;
}
} else if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_AUTHLOCALIZEDKEY))) {
size_t buflen = USM_AUTH_KU_LEN;
SNMP_FREE(user->authKey);
user->authKey = (u_char *)malloc(buflen); /* max length needed */
user->authKeyLen = 0;
/* it will be a hex string */
if (!snmp_hex_to_binary(&user->authKey, &buflen, &user->authKeyLen,
0, cp)) {
usm_free_user(user);
return SNMPERR_GENERR;
}
}
}
/*
* copy in the privacy Key. If not localized, localize it
*/
if (user->privKey == NULL) {
if (session->securityPrivLocalKey != NULL
&& session->securityPrivLocalKeyLen != 0) {
/* already localized key passed in. use it */
SNMP_FREE(user->privKey);
user->privKey = netsnmp_memdup(session->securityPrivLocalKey,
session->securityPrivLocalKeyLen);
if (!user->privKey) {
usm_free_user(user);
return SNMPERR_GENERR;
}
user->privKeyLen = session->securityPrivLocalKeyLen;
} else if (session->securityPrivKey != NULL
&& session->securityPrivKeyLen != 0) {
SNMP_FREE(user->privKey);
user->privKey = (u_char *) calloc(1, USM_LENGTH_KU_HASHBLOCK);
if (user->privKey == NULL) {
usm_free_user(user);
return SNMPERR_GENERR;
}
user->privKeyLen = USM_LENGTH_KU_HASHBLOCK;
if (generate_kul(user->authProtocol, user->authProtocolLen,
session->securityEngineID,
session->securityEngineIDLen,
session->securityPrivKey,
session->securityPrivKeyLen, user->privKey,
&user->privKeyLen) != SNMPERR_SUCCESS) {
usm_free_user(user);
return SNMPERR_GENERR;
}
} else if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_PRIVLOCALIZEDKEY))) {
size_t buflen = USM_PRIV_KU_LEN;
SNMP_FREE(user->privKey);
user->privKey = (u_char *)malloc(buflen); /* max length needed */
user->privKeyLen = 0;
/* it will be a hex string */
if (!snmp_hex_to_binary(&user->privKey, &buflen, &user->privKeyLen,
0, cp)) {
usm_free_user(user);
return SNMPERR_GENERR;
}
}
}
if (user_just_created) {
/*
* add the user into the database
*/
user->userStatus = RS_ACTIVE;
user->userStorageType = ST_READONLY;
usm_add_user(user);
}
return SNMPERR_SUCCESS;
} /* end create_user_from_session() */
/*
* 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);
/*
* 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 = 0;
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 = 0;
if (transport) {
transport->f_close(transport);
netsnmp_transport_free(transport);
}
sesp = slp->session;
slp->session = 0;
/*
* 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 int
snmpv3_build_probe_pdu(netsnmp_pdu **pdu)
{
struct usmUser *user;
/*
* create the pdu
*/
if (!pdu)
return -1;
*pdu = snmp_pdu_create(SNMP_MSG_GET);
if (!(*pdu))
return -1;
(*pdu)->version = SNMP_VERSION_3;
(*pdu)->securityName = strdup("");
(*pdu)->securityNameLen = strlen((*pdu)->securityName);
(*pdu)->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
(*pdu)->securityModel = SNMP_SEC_MODEL_USM;
/*
* create the empty user
*/
user = usm_get_user(NULL, 0, (*pdu)->securityName);
if (user == NULL) {
user = (struct usmUser *) calloc(1, sizeof(struct usmUser));
if (user == NULL) {
snmp_free_pdu(*pdu);
*pdu = (netsnmp_pdu *) NULL;
return -1;
}
user->name = strdup((*pdu)->securityName);
user->secName = strdup((*pdu)->securityName);
user->authProtocolLen = sizeof(usmNoAuthProtocol) / sizeof(oid);
user->authProtocol =
snmp_duplicate_objid(usmNoAuthProtocol, user->authProtocolLen);
user->privProtocolLen = sizeof(usmNoPrivProtocol) / sizeof(oid);
user->privProtocol =
snmp_duplicate_objid(usmNoPrivProtocol, user->privProtocolLen);
usm_add_user(user);
}
return 0;
}
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;
if (rpdu->securityEngineIDLen != pdu->securityEngineIDLen ||
memcmp(rpdu->securityEngineID, pdu->securityEngineID,
pdu->securityEngineIDLen))
return 0;
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));
/*
* Fallthrough
*/
case SNMP_MSG_GET:
case SNMP_MSG_GETNEXT:
case SNMP_MSG_SET:
case SNMP_MSG_INFORM:
if (pdu->errstat == SNMP_DEFAULT_ERRSTAT)
pdu->errstat = 0;
if (pdu->errindex == SNMP_DEFAULT_ERRINDEX)
pdu->errindex = 0;
break;
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;
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 = SNMP_SEC_MODEL_USM;
}
}
if (pdu->securityNameLen == 0 && pdu->securityName == 0) {
if (session->securityNameLen == 0) {
session->s_snmp_errno = SNMPERR_BAD_SEC_NAME;
return -1;
}
pdu->securityName = strdup(session->securityName);
if (pdu->securityName == NULL) {
session->s_snmp_errno = SNMPERR_GENERR;
return -1;
}
pdu->securityNameLen = session->securityNameLen;
}
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 (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_REVERSE_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 = session->rcvMsgMaxSize;
DEBUGDUMPHEADER("send", "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 = session->rcvMsgMaxSize;
DEBUGDUMPHEADER("send", "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 = 0;
size_t start_offset = *offset;
long version;
int rc = 0;
#endif /* support for community based SNMP */
u_char *cp;
size_t length;
session->s_snmp_errno = 0;
session->s_errno = 0;
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));
/*
* Fallthrough
*/
case SNMP_MSG_GET:
case SNMP_MSG_GETNEXT:
case SNMP_MSG_SET:
/*
* 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));
/*
* Fallthrough
*/
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;
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;
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
*/
length = *pkt_len;
/*
* 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);
pdu->community = NULL;
} 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%d message...\n",
(1 + pdu->version)));
#ifdef NETSNMP_USE_REVERSE_ASNENCODING
if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_REVERSE_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