From 42fa08047bdadd3e352218c9192796b0a32cf85e Mon Sep 17 00:00:00 2001 From: Packit Service Date: Dec 03 2020 08:13:56 +0000 Subject: net-snmp-5.8 base --- diff --git a/python/LICENSE b/python/LICENSE new file mode 100644 index 0000000..4c7638f --- /dev/null +++ b/python/LICENSE @@ -0,0 +1,66 @@ +Various copyrights apply to this package, listed in various separate +parts below. Please make sure that you read all the parts. + +---- Part 1: Sparta, Inc (BSD) ----- + +Copyright (c) 2003-2010, Sparta, Inc +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Sparta, Inc nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---- Part 2: ScienceLogic, LLC (BSD) ---- + +Copyright (c) 2006, ScienceLogic, LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of ScienceLogic, LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/python/README b/python/README new file mode 100644 index 0000000..f998f8b --- /dev/null +++ b/python/README @@ -0,0 +1,365 @@ + The Python 'netsnmp' Extension Module + for the Net-SNMP Library + +Contents: + Introduction: + Availability: + Contact: + Supported Platforms: + Release Notes: + Installation: + Operational Description: + Trouble Shooting: + Acknowledgments: + License/Copyright: + +Introduction: + + This is the Python 'netsnmp' extension module. The 'netsnmp' module + provides a full featured, tri-lingual SNMP (SNMPv3, SNMPv2c, + SNMPv1) client API. The 'netsnmp' module internals rely on the + Net-SNMP toolkit library. For information on the Net-SNMP library + see the documentation provided with the Net-SNMP distribution or + the project web page available on 'Source Forge': + + http://www.net-snmp.org/ + +Availability: + + The most recent release of the Python 'netsnmp' module can be found + bundled with the latest Net-SNMP distribution available from: + + http://www.net-snmp.org/download.html + +Contact: + + The following mailing list should be consider the primary support + mechanism for this module: + + net-snmp-users@lists.sourceforge.net mail list + + (see http://www.net-snmp.org/lists/users/ to subscribe) + +Supported Platforms: + + Linux 2.x + Other UNIX/POSIX variants (untested) + MS Windows (untested) + + Let us know where it *doesn't* work, as it should on most systems + +Release Notes: + + This initial alpha release of the Python 'netsnmp' extension module + has been developed against net-snmp 5.4.pre1. + + Only syncronous, client-side functionality is implemented. + + Access to the parsed MIB database is not yet implemented. + +KNOWN BUGS: + + Too many to mention at this point + +Installation: + + Build and install the Net-SNMP package - see Net-SNMP README and + INSTALL docs. + + Unix: + + cd net-snmp/python + python setup.py build + python setup.py test (requires a locally running agent w/ config provided) + python setup.py install + + +Operational Description: + + The basic operations of the SNMP protocol are provided by this + module through an object oriented interface for modularity and ease + of use. The primary class is netsnmp.Session which encapsulates + the persistent aspects of a connection between the management + application and the managed agent. This class supplies 'get', + 'getnext', 'getbulk', 'set' and other method calls. + + A description of the fields which can be specified when instantiating an + netsnmp.Session follows: + + netsnmp.Session(=, ... ) + + DestHost - default 'localhost', hostname or ip addr of SNMP agent + Version - default '3', [1, 2 (equiv to 2c), 3] + RemotePort - default '161', allow remote UDP port to be overridden + Timeout - default '500000', micro-seconds before retry + Retries - default '3', retries before failure + RetryNoSuch - default '0', if enabled NOSUCH errors in 'get' pdus will + be repaired, removing the varbind in error, and resent - + undef will be returned for all NOSUCH varbinds, when set + to '0' this feature is disabled and the entire get request + will fail on any NOSUCH error (applies to v1 only) + UseLongNames - set to non-zero to have for 'getnext' methods + generated preferring longer Mib name convention (e.g., + system.sysDescr vs just sysDescr) + UseSprintValue - set to non-zero to have return values + for 'get' and 'getnext' methods formatted with the libraries + sprint_value function. This will result in certain data types + being returned in non-canonical format Note: values returned + with this option set may not be appropriate for 'set' + operations (see discussion of value formats in + description section) + UseEnums - set to non-zero to have integer return values + converted to enumeration identifiers if possible, + these values will also be acceptable when supplied to + 'set' operations + UseNumeric - set to non-zero to have returned by the 'get' + methods untranslated (i.e. dotted-decimal). Setting the + UseLongNames value for the session is highly recommended. + BestGuess - this setting controls how are parsed. setting + to 0 causes a regular lookup. setting to 1 causes a regular + expression match (defined as -Ib in snmpcmd). setting to 2 + causes a random access lookup (defined as -IR in snmpcmd). + ErrorStr - read-only, holds the error message assoc. w/ last request + ErrorNum - read-only, holds the snmp_err or status of last request + ErrorInd - read-only, holds the snmp_err_index when appropriate + + SNMPv1/SNMPv2c options: + Community - default 'public', SNMP community string (used for both R/W) + + SNMPv3 Options: + SecName - default 'initial', security name (v3) + SecLevel - default 'noAuthNoPriv', security level [noAuthNoPriv, + authNoPriv, authPriv] (v3) + ContextEngineId - default , context engineID, will be + probed if not supplied (v3) + Context - default '', context name (v3) + + SNMPv3 over TLS or DTLS options: + OurIdentity - The fingerprint or file name for the local X.509 + certificate to use for our identity. Run + net-snmp-cert to create and manage certificates. + TheirIdentity - The fingerprint or file name for the local X.509 + certificate to use for their identity. + TrustCert - A trusted certificate to use for validating + certificates. Typically this would be a CA + certificate. + TheirHostname - Their hostname to expect. Either "TheirIdentity" + or a trusted certificate plus a hostname is needed + to validate the server is the proper server. + + SNMPv3 with USM security Options: + SecEngineId - default , security engineID, will be probed if not + supplied (v3) + AuthProto - default 'MD5', authentication protocol [MD5, SHA] (v3) + AuthPass - default , authentication passphrase + PrivProto - default 'DES', privacy protocol [DES] (v3) + PrivPass - default , privacy passphrase (v3) + + private: + sess_ptr - internal field used to cache a created session structure + + methods: + + get() + - SNMP GET a netsnmp.VarList object must be supplied, + returns a tuple of values for each varbind in list + + getnext() + - SNMP GETNEXT, a netsnmp.VarList object must be supplied + returns retrieved value(s), VarList passed as arguments + are updated to return a list of next lexicographical + Varbind objects. returns a tuple of values for each + varbind in list + + set() + - SNMP SET, a netsnmp.VarList object must be supplied + the value field in all Varbinds must be in a canonical + format (i.e., well known format) to ensure unambiguous + translation to SNMP MIB data value (see discussion of + canonical value format description section), + returns true on success or None on error. + + getbulk(, , ) + - SNMP GETBULK, a netsnmp.VarList object must be supplied + the single next lexico instance is fetched for the first + n Varbinds in the list as defined by . + For the remaining Varbinds, the next m lexico instances + are retrieved each of the remaining Varbinds, + where m is . Returns a tuple of values + retrieved. + + walk() + - Performs multiple GETNEXT requests in order to + return a tuple of values retrieved from the MIB + below the Varbind passed in. The VarList passed + in will be updated to contain a complete set of + Varbinds created for the results of the walk. + + Note that only one varbind should be contained in the + VarList passed in. The code is structured to maybe + handle this is the the future, but right now walking + multiple trees at once is not yet supported and will + produce insufficient results. + + + Acceptable variable formats: + + netsnmp.VarList: - represents an list of Varbind objects to get or set. + takes are arguments and unspecified number of Varbinds, + or tuples which will be converted to Varbinds. + + + netsnmp.Varbind: - represents a single MIB object to get or set + implemented as Python[, , , ]. + - one of the following forms: + 1) leaf identifier (e.g., 'sysDescr') assumed to be + unique for practical purposes + 2) fully qualified identifier (e.g., + '.iso.org.dod.internet.mgmt.mib-2.system.sysDescr') + 3) fully qualified, dotted-decimal, numeric OID + (e.g., '.1.3.6.1.2.1.1.1') + - the dotted-decimal, instance identifier. for + scalar MIB objects use '0' + - the SNMP data value retrieved from or being set + to the agents MIB. for set operations the + format must be canonical to ensure unambiguous + translation. The canonical forms are as follows: + OBJECTID => dotted-decimal (e.g., .1.3.6.1.2.1.1.1) + OCTETSTR => perl scalar containing octets, + INTEGER => decimal signed integer (or enum), + NETADDR => dotted-decimal, + IPADDR => dotted-decimal, + COUNTER => decimal unsigned integer, + COUNTER64 => decimal unsigned integer, + GAUGE, => decimal unsigned integer, + UINTEGER, => decimal unsigned integer, + TICKS, => decimal unsigned integer, + OPAQUE => perl scalar containing octets, + NULL, => perl scalar containing nothing, + + - SNMP data type (see list above), this field is + populated by 'get' and 'getnext' operations. In + some cases the programmer needs to populate this + field when passing to a 'set' operation. this + field need not be supplied when the attribute + indicated by is already described in the + parsed MIB. for 'set's, if a numeric OID is used + and the object is not in the parsed MIB, + the field must be supplied + + + Python 'netsnmp' package variables and functions: + + + netsnmp.verbose - default '0', + controls warning/info output of themodule + 0 => no output, + 1 => enables warning/info + +(needs implementation) + $SNMP::debugging - default '0', controls debugging output level + within SNMP module and libsnmp + 1 => enables 'SNMP::verbose' (see above) + 2 => level 1 plus snmp_set_do_debugging(1), + 3 => level 2 plus snmp_set_dump_packet(1) + + $SNMP::dump_packet - default '0', set [non-]zero to independently set + snmp_set_dump_packet() + + Exported 'netsnmp' package utility functions: + + snmpget(, ) + - takes args of netsnmp.Session preceded by those of the + corresponding netsnmp.Session method. Returns a tuple with + Varbind values fetched, and input is updated to contain + complete Varbinds fetched. + + snmpgetnext(, ) + - takes args of netsnmp.Session preceded by those of the + corresponding netsnmp.Session method. Returns a tuple with + Varbind values fetched, and input is updated to contain + complete Varbinds fetched. + + snmpgetbulk(nonrepeaters, maxrepetitions,, ) + - takes args of netsnmp.Session preceded by those of the + corresponding netsnmp.Session method. Returns a tuple with + Varbind values fetched, and VarList is updated to contain + complete Varbinds fetched. + + snmpset(, ) + - takes args of netsnmp.Session preceded by those of the + corresponding netsnmp.Session method. returns True on success, + otherwise False. + + snmpwalk(, )) + - takes args of netsnmp.Session preceded by a Varbind or + VarList from which the 'walk' operation will start. + Returns a tuple of values retrieved from the MIB below + the Varbind passed in. If a VarList is passed in it + will be updated to contain a complete set of VarBinds + created for the results of the walk. It is not + recommended to pass in just a Varbind since you loose + the ability to examine the returned OIDs. But, if only + a Varbind is passed in it will be returned unaltered. + + Note that only one varbind should be contained in the + VarList passed in. The code is structured to maybe + handle this is the the future, but right now walking + multiple trees at once is not yet supported and will + produce insufficient results. + +Trouble Shooting: + + If problems occur there are number areas to look at to narrow down the + possibilities. + + The first step should be to test the Net-SNMP installation + independently from the Python 'netsnmp' Extension. + + Try running the apps from the Net-SNMP distribution. + + Make sure your agent (snmpd) is running and properly configured with + read-write access for the community you are using. + + Ensure that your MIBs are installed and environment variables are set + appropriately (see man mib_api) + + Be sure to ensure headers and libraries from old CMU installations are + not being used by mistake (see -NET-SNMP-PATH). + + If the problem occurs during compilation/linking check that the snmp + library being linked is actually the Net-SNMP library (there have been + name conflicts with existing snmp libs). + + Also check that the header files are correct and up to date. + + Sometimes compiling the Net-SNMP library with + 'position-independent-code' enabled is required (HPUX specifically). + + If you cannot resolve the problem you can email + net-snmp-users@lists.sourceforge.net. + + Please give sufficient information to analyze the problem (OS type, + versions for OS/python/net-SNMP/compiler, complete error output, etc.) + +Acknowledgments: + + Giovanni Marzot (the original author) + ScienceLogic, LLC sponsored the initial development of this module. + Wes Hardaker and the net-snmp-coders + + Thanks in advance to any who supply patches, suggestions and feedback. + +License: + + Please see the LICENSE file contained with this package + +Copyright: + + Copyright (c) 2006 G. S. Marzot. All rights reserved. + This program is free software; you can redistribute it and/or + modify it under the same terms as Net-SNMP itself. + + Copyright (c) 2006 SPARTA, Inc. All Rights Reserved. This + program is free software; you can redistribute it and/or modify + it under the same terms as Net-SNMP itself. diff --git a/python/netsnmp/__init__.py b/python/netsnmp/__init__.py new file mode 100644 index 0000000..1d20dac --- /dev/null +++ b/python/netsnmp/__init__.py @@ -0,0 +1 @@ +from client import * diff --git a/python/netsnmp/client.py b/python/netsnmp/client.py new file mode 100644 index 0000000..46a6e37 --- /dev/null +++ b/python/netsnmp/client.py @@ -0,0 +1,281 @@ +import re +from sys import stderr +import netsnmp +import netsnmp.client_intf + +# control verbosity of error output +verbose = 1 + +secLevelMap = {'noAuthNoPriv':1, 'authNoPriv':2, 'authPriv':3} + +def _parse_session_args(kargs): + sessArgs = { + 'Version':3, + 'DestHost':'localhost', + 'Community':'public', + 'Timeout':1000000, + 'Retries':3, + 'RemotePort':161, + 'LocalPort':0, + 'SecLevel':'noAuthNoPriv', + 'SecName':'initial', + 'PrivProto':'DEFAULT', + 'PrivPass':'', + 'AuthProto':'DEFAULT', + 'AuthPass':'', + 'ContextEngineId':'', + 'SecEngineId':'', + 'Context':'', + 'Engineboots':0, + 'Enginetime':0, + 'UseNumeric':0, + 'OurIdentity':'', + 'TheirIdentity':'', + 'TheirHostname':'', + 'TrustCert':'' + } + keys = kargs.keys() + for key in keys: + if sessArgs.has_key(key): + sessArgs[key] = kargs[key] + else: + print >>stderr, "ERROR: unknown key", key + return sessArgs + +def STR(obj): + if obj != None: + obj = str(obj) + return obj + +def obj_to_str(obj): + return type(obj).__name__ + ":" + str(obj.__dict__) + + +class Varbind(object): + def __init__(self, tag=None, iid=None, val=None, type_arg=None): + self.tag = STR(tag) + self.iid = STR(iid) + self.val = STR(val) + self.type = STR(type_arg) + # parse iid out of tag if needed + if iid is None and tag is not None: + regex = re.compile(r'^((?:\.\d+)+|(?:\w+(?:[-:]*\w+)+))\.?(.*)$') + match = regex.match(tag) + if match: + (self.tag, self.iid) = match.group(1, 2) + + def __setattr__(self, name, val): + self.__dict__[name] = STR(val) + + def __str__(self): + return obj_to_str(self) + + def print_str(self): + return self.tag, self.iid, self.val, self.type + + +class VarList(object): + def __init__(self, *vs): + self.varbinds = [] + + for var in vs: + if isinstance(var, netsnmp.client.Varbind): + self.varbinds.append(var) + else: + self.varbinds.append(Varbind(var)) + + def __len__(self): + return len(self.varbinds) + + def __getitem__(self, index): + return self.varbinds[index] + + def __setitem__(self, index, val): + if isinstance(val, netsnmp.client.Varbind): + self.varbinds[index] = val + else: + raise TypeError + + def __iter__(self): + return iter(self.varbinds) + + def __delitem__(self, index): + del self.varbinds[index] + + def __repr__(self): + return repr(self.varbinds) + + def __getslice__(self, i, j): + return self.varbinds[i:j] + + def __str__(self): + return str([str(v) for v in self.varbinds]) + + def append(self, *varlist): + for var in varlist: + if isinstance(var, netsnmp.client.Varbind): + self.varbinds.append(var) + else: + raise TypeError + + +class Session(object): + def __init__(self, **args): + self.sess_ptr = None + self.UseLongNames = 0 + self.UseNumeric = 0 + self.UseSprintValue = 0 + self.UseEnums = 0 + self.BestGuess = 0 + self.RetryNoSuch = 0 + self._clear_error() + + sess_args = _parse_session_args(args) + + for k, v in sess_args.items(): + self.__dict__[k] = v + + + # check for transports that may be tunneled + transportCheck = re.compile('^(tls|dtls|ssh)') + match = transportCheck.match(sess_args['DestHost']) + + if match: + self.sess_ptr = netsnmp.client_intf.session_tunneled( + sess_args['Version'], + sess_args['DestHost'], + sess_args['LocalPort'], + sess_args['Retries'], + sess_args['Timeout'], + sess_args['SecName'], + secLevelMap[sess_args['SecLevel']], + sess_args['ContextEngineId'], + sess_args['Context'], + sess_args['OurIdentity'], + sess_args['TheirIdentity'], + sess_args['TheirHostname'], + sess_args['TrustCert'], + ) + elif sess_args['Version'] == 3: + self.sess_ptr = netsnmp.client_intf.session_v3( + sess_args['Version'], + sess_args['DestHost'], + sess_args['LocalPort'], + sess_args['Retries'], + sess_args['Timeout'], + sess_args['SecName'], + secLevelMap[sess_args['SecLevel']], + sess_args['SecEngineId'], + sess_args['ContextEngineId'], + sess_args['Context'], + sess_args['AuthProto'], + sess_args['AuthPass'], + sess_args['PrivProto'], + sess_args['PrivPass'], + sess_args['Engineboots'], + sess_args['Enginetime']) + else: + self.sess_ptr = netsnmp.client_intf.session( + sess_args['Version'], + sess_args['Community'], + sess_args['DestHost'], + sess_args['LocalPort'], + sess_args['Retries'], + sess_args['Timeout']) + + def _clear_error(self): + self.ErrorStr = '' + self.ErrorNum = 0 + self.ErrorInd = 0 + + def get(self, varlist): + self._clear_error() + res = netsnmp.client_intf.get(self, varlist) + return res + + def set(self, varlist): + self._clear_error() + res = netsnmp.client_intf.set(self, varlist) + return res + + def getnext(self, varlist): + self._clear_error() + res = netsnmp.client_intf.getnext(self, varlist) + return res + + def getbulk(self, nonrepeaters, maxrepetitions, varlist): + self._clear_error() + if self.Version == 1: + return None + res = netsnmp.client_intf.getbulk(self, nonrepeaters, maxrepetitions, varlist) + return res + + def walk(self, varlist): + self._clear_error() + res = netsnmp.client_intf.walk(self, varlist) + return res + + def __del__(self): + res = netsnmp.client_intf.delete_session(self) + return res + + def __str__(self): + return obj_to_str(self) + +def snmpget(*args, **kargs): + sess = Session(**kargs) + var_list = VarList() + for arg in args: + if isinstance(arg, netsnmp.client.Varbind): + var_list.append(arg) + else: + var_list.append(Varbind(arg)) + res = sess.get(var_list) + return res + +def snmpset(*args, **kargs): + sess = Session(**kargs) + var_list = VarList() + for arg in args: + if isinstance(arg, netsnmp.client.Varbind): + var_list.append(arg) + else: + var_list.append(Varbind(arg)) + res = sess.set(var_list) + return res + +def snmpgetnext(*args, **kargs): + sess = Session(**kargs) + var_list = VarList() + for arg in args: + if isinstance(arg, netsnmp.client.Varbind): + var_list.append(arg) + else: + var_list.append(Varbind(arg)) + res = sess.getnext(var_list) + return res + +def snmpgetbulk(nonrepeaters, maxrepetitions, *args, **kargs): + sess = Session(**kargs) + var_list = VarList() + for arg in args: + if isinstance(arg, netsnmp.client.Varbind): + var_list.append(arg) + else: + var_list.append(Varbind(arg)) + res = sess.getbulk(nonrepeaters, maxrepetitions, var_list) + return res + +def snmpwalk(*args, **kargs): + sess = Session(**kargs) + if isinstance(args[0], netsnmp.client.VarList): + var_list = args[0] + else: + var_list = VarList() + for arg in args: + if isinstance(arg, netsnmp.client.Varbind): + var_list.append(arg) + else: + var_list.append(Varbind(arg)) + res = sess.walk(var_list) + return res diff --git a/python/netsnmp/client_intf.c b/python/netsnmp/client_intf.c new file mode 100644 index 0000000..589f24f --- /dev/null +++ b/python/netsnmp/client_intf.c @@ -0,0 +1,2502 @@ +#include + +#if PY_VERSION_HEX < 0x02050000 +typedef int Py_ssize_t; +#define PY_SSIZE_T_MAX INT_MAX +#define PY_SSIZE_T_MIN INT_MIN +#endif + +#include +#include +#include +#include +#include +#include +#include +#ifdef I_SYS_TIME +#include +#endif +#include +#include + +#ifdef HAVE_REGEX_H +#include +#endif + +#define SUCCESS 1 +#define FAILURE 0 + +#define VARBIND_TAG_F 0 +#define VARBIND_IID_F 1 +#define VARBIND_VAL_F 2 +#define VARBIND_TYPE_F 3 + +#define TYPE_UNKNOWN 0 +#define MAX_TYPE_NAME_LEN 32 +#define STR_BUF_SIZE (MAX_TYPE_NAME_LEN * MAX_OID_LEN) +#define ENG_ID_BUF_SIZE 32 + +#define NO_RETRY_NOSUCH 0 + +#define STRLEN(x) (x ? strlen(x) : 0) + + +typedef netsnmp_session SnmpSession; +typedef struct tree SnmpMibNode; +static int __is_numeric_oid (char*); +static int __is_leaf (struct tree*); +static int __translate_asn_type (int); +static int __snprint_value (char **, size_t *, + netsnmp_variable_list*, struct tree *, + int, int); +static int __sprint_num_objid (char **, size_t *, oid *, int); +static int __scan_num_objid (char *, oid *, size_t *); +static int __get_type_str (int, char *); +static int __get_label_iid (char *, char **, char **, int); +static struct tree * __tag2oid (char *, char *, oid *, int *, int *, int); +static int __concat_oid_str (oid *, int *, char *); +#define USE_NUMERIC_OIDS 0x08 +#define NON_LEAF_NAME 0x04 +#define USE_LONG_NAMES 0x02 +#define FAIL_ON_NULL_IID 0x01 +#define NO_FLAGS 0x00 + +static int _debug_level; + + +void +__libraries_init(char *appname) +{ + static int have_inited = 0; + + if (have_inited) + return; + have_inited = 1; + + netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_QUICK_PRINT, 1); + snmp_enable_stderrlog(); + init_snmp(appname); + + netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_BREAKDOWN_OIDS, 1); + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_SUFFIX_ONLY, 1); + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, + NETSNMP_OID_OUTPUT_SUFFIX); +} + +static int +__is_numeric_oid(char* oidstr) +{ + if (!oidstr) return 0; + for (; *oidstr; oidstr++) { + if (isalpha((int)*oidstr)) return 0; + } + return(1); +} + +static int +__is_leaf(struct tree* tp) +{ + char buf[MAX_TYPE_NAME_LEN]; + return (tp && (__get_type_str(tp->type,buf) || + (tp->parent && __get_type_str(tp->parent->type,buf) ))); +} + +struct type_table_entry { + uint8_t mib_type; + uint8_t asn_type; + int8_t cmp_len; + const char *name; +}; + +static const struct type_table_entry type_table[] = { + { TYPE_INTEGER32, 0, 8, "INTEGER32" }, + { TYPE_INTEGER, ASN_INTEGER, 3, "INTEGER" }, + { TYPE_UNSIGNED32, 0, 3, "UNSIGNED32" }, + /* -1 to use strcasecmp() to avoid matching COUNTER64 */ + { TYPE_COUNTER, ASN_COUNTER, -1, "COUNTER" }, + { TYPE_GAUGE, ASN_GAUGE, 3, "GAUGE" }, + { TYPE_IPADDR, ASN_IPADDRESS, 3, "IPADDR" }, + { TYPE_OCTETSTR, ASN_OCTET_STR, 3, "OCTETSTR" }, + { TYPE_TIMETICKS, ASN_TIMETICKS, 3, "TICKS" }, + { TYPE_OPAQUE, ASN_OPAQUE, 3, "OPAQUE" }, + { TYPE_OBJID, ASN_OBJECT_ID, 3, "OBJECTID" }, + { TYPE_NETADDR, 0, 3, "NETADDR" }, + { TYPE_COUNTER64, ASN_COUNTER64, 3, "COUNTER64" }, + { TYPE_NULL, ASN_NULL, 3, "NULL" }, + { TYPE_BITSTRING, ASN_BIT_STR, 3, "BITS" }, + /* historic - should not show up but it does? */ + { TYPE_UINTEGER, ASN_UINTEGER, 3, "UINTEGER" }, + { TYPE_NOTIFTYPE, 0, 3, "NOTIF" }, + { TYPE_TRAPTYPE, 0, 4, "TRAP" }, + + { SNMP_ENDOFMIBVIEW, SNMP_ENDOFMIBVIEW, 3, "ENDOFMIBVIEW" }, + { SNMP_NOSUCHOBJECT, SNMP_NOSUCHOBJECT, 7, "NOSUCHOBJECT" }, + { SNMP_NOSUCHINSTANCE, SNMP_NOSUCHINSTANCE, 7, "NOSUCHINSTANCE"}, + + { } +}; + +#ifndef NETSNMP_NO_WRITE_SUPPORT +static int +__translate_appl_type(const char *typestr) +{ + const struct type_table_entry *e; + + if (typestr == NULL || *typestr == '\0') + return TYPE_UNKNOWN; + + for (e = type_table; e->name; e++) { + if ((e->cmp_len < 0 && strcasecmp(typestr, e->name) == 0) || + (e->cmp_len > 0 && strncasecmp(typestr, e->name, e->cmp_len) == 0)) + return e->mib_type; + } + + return TYPE_UNKNOWN; +} +#endif /* NETSNMP_NO_WRITE_SUPPORT */ + +static int +__translate_asn_type(int asn_type) +{ + const struct type_table_entry *e; + + netsnmp_assert(asn_type != 0); + + for (e = type_table; e->name; e++) + if (asn_type == e->asn_type) + return e->mib_type; + + fprintf(stderr, "translate_asn_type: unhandled asn type (%d)\n", asn_type); + return TYPE_OTHER; +} + +static void *enlarge_buffer(char **buf, size_t *buf_len, size_t desired_len) +{ + void *new_buf; + + if (desired_len > *buf_len) { + new_buf = netsnmp_realloc(*buf, desired_len); + if (!new_buf) + return NULL; + *buf = new_buf; + *buf_len = desired_len; + } + return *buf; +} + +#define USE_BASIC 0 +#define USE_ENUMS 1 +#define USE_SPRINT_VALUE 2 +static int +__snprint_value(char **buf, size_t *buf_len, netsnmp_variable_list *var, + struct tree *tp, int type, int flag) +{ + size_t out_len = 0; + int len = 0; + u_char* ip; + struct enum_list *ep; + + + enlarge_buffer(buf, buf_len, 32); + (*buf)[0] = '\0'; + if (flag == USE_SPRINT_VALUE) { + if (sprint_realloc_value((u_char **)buf, buf_len, &out_len, 1, + var->name, var->name_length, var)) { + *buf_len = out_len; + len = STRLEN(*buf); + } + } else { + switch (var->type) { + case ASN_INTEGER: + if (flag == USE_ENUMS) { + for(ep = tp->enums; ep; ep = ep->next) { + if (ep->value == *var->val.integer) { + strlcpy(*buf, ep->label, *buf_len); + len = STRLEN(*buf); + break; + } + } + } + if (!len) { + snprintf(*buf, *buf_len, "%ld", *var->val.integer); + len = STRLEN(*buf); + } + break; + + case ASN_GAUGE: + case ASN_COUNTER: + case ASN_TIMETICKS: + case ASN_UINTEGER: + snprintf(*buf, *buf_len, "%lu", (unsigned long) *var->val.integer); + len = STRLEN(*buf); + break; + + case ASN_OCTET_STR: + case ASN_OPAQUE: + len = var->val_len; + enlarge_buffer(buf, buf_len, len + 1); + if (len > *buf_len - 1) + len = *buf_len - 1; + memcpy(*buf, var->val.string, len); + break; + + case ASN_IPADDRESS: + ip = (u_char*)var->val.string; + snprintf(*buf, *buf_len, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + len = STRLEN(*buf); + break; + + case ASN_NULL: + break; + + case ASN_OBJECT_ID: + __sprint_num_objid(buf, buf_len, (oid *)(var->val.objid), + var->val_len/sizeof(oid)); + len = STRLEN(*buf); + break; + + case SNMP_ENDOFMIBVIEW: + snprintf(*buf, *buf_len, "%s", "ENDOFMIBVIEW"); + break; + case SNMP_NOSUCHOBJECT: + snprintf(*buf, *buf_len, "%s", "NOSUCHOBJECT"); + break; + case SNMP_NOSUCHINSTANCE: + snprintf(*buf, *buf_len, "%s", "NOSUCHINSTANCE"); + break; + + case ASN_COUNTER64: +#ifdef OPAQUE_SPECIAL_TYPES + case ASN_OPAQUE_COUNTER64: + case ASN_OPAQUE_U64: +#endif + printU64(*buf,(struct counter64 *)var->val.counter64); + len = STRLEN(*buf); + break; + +#ifdef OPAQUE_SPECIAL_TYPES + case ASN_OPAQUE_I64: + printI64(*buf,(struct counter64 *)var->val.counter64); + len = STRLEN(*buf); + break; +#endif + + case ASN_BIT_STR: + if (sprint_realloc_bitstring((u_char **)buf, buf_len, &out_len, 1, + var, NULL, NULL, NULL)) { + *buf_len = out_len; + len = STRLEN(*buf); + } + break; +#ifdef OPAQUE_SPECIAL_TYPES + case ASN_OPAQUE_FLOAT: + if (var->val.floatVal) + snprintf(*buf, *buf_len, "%f", *var->val.floatVal); + break; + + case ASN_OPAQUE_DOUBLE: + if (var->val.doubleVal) + snprintf(*buf, *buf_len, "%f", *var->val.doubleVal); + break; +#endif + + case ASN_NSAP: + default: + fprintf(stderr,"snprint_value: asn type not handled %d\n",var->type); + } + } + return(len); +} + +static int +__sprint_num_objid(char **buf, size_t *buf_len, oid *objid, int len) +{ + char *p, *end; + int i; + + enlarge_buffer(buf, buf_len, len * 16); + p = *buf; + end = *buf + *buf_len; + (*buf)[0] = '\0'; + for (i = 0; i < len; i++) + p += snprintf(p, end - p, ".%lu", *objid++); + + return SUCCESS; +} + +static int +__scan_num_objid(char *buf, oid *objid, size_t *len) +{ + char *cp; + *len = 0; + if (*buf == '.') buf++; + cp = buf; + while (*buf) { + if (*buf++ == '.') { + sscanf(cp, "%lu", objid++); + /* *objid++ = atoi(cp); */ + (*len)++; + cp = buf; + } else { + if (isalpha((int)*buf)) { + return FAILURE; + } + } + } + sscanf(cp, "%lu", objid++); + /* *objid++ = atoi(cp); */ + (*len)++; + return SUCCESS; +} + +static int +__get_type_str(int type, char * str) +{ + const struct type_table_entry *e; + + for (e = type_table; e->name; e++) { + if (type == e->mib_type) { + strcpy(str, e->name); + return SUCCESS; + } + } + + strcpy(str, ""); + if (_debug_level) + printf("__get_type_str:FAILURE(%d)\n", type); + + return FAILURE; +} + +/* does a destructive disection of .... returning + and in seperate strings (note: will destructively + alter input string, 'name') */ +static int +__get_label_iid(char *name, char **last_label, char **iid, int flag) +{ + char *lcp; + char *icp; + int len = STRLEN(name); + int found_label = 0; + + *last_label = *iid = NULL; + + if (len == 0) return(FAILURE); + + /* Handle case where numeric oid's have been requested. The input 'name' + ** in this case should be a numeric OID -- return failure if not. + */ + if ((flag & USE_NUMERIC_OIDS)) { + if (!__is_numeric_oid(name)) + return(FAILURE); + + /* Walk backward through the string, looking for first two '.' chars */ + lcp = &(name[len]); + icp = NULL; + while (lcp > name) { + if (*lcp == '.') { + + /* If this is the first occurence of '.', note it in icp. + ** Otherwise, this must be the second occurrence, so break + ** out of the loop. + */ + if (icp == NULL) + icp = lcp; + else + break; + } + lcp --; + } + + /* Make sure we found at least a label and index. */ + if (!icp) + return(FAILURE); + + /* Push forward past leading '.' chars and separate the strings. */ + lcp ++; + *icp ++ = '\0'; + + *last_label = (flag & USE_LONG_NAMES) ? name : lcp; + *iid = icp; + + return(SUCCESS); + } + + lcp = icp = &(name[len]); + + while (lcp > name) { + if (*lcp == '.') { + if (found_label) { + lcp++; + break; + } else { + icp = lcp; + } + } + if (!found_label && isalpha((int)*lcp)) found_label = 1; + lcp--; + } + + if (!found_label || (!isdigit((int)*(icp+1)) && (flag & FAIL_ON_NULL_IID))) + return(FAILURE); + + if (flag & NON_LEAF_NAME) { /* dont know where to start instance id */ + /* put the whole thing in label */ + icp = &(name[len]); + flag |= USE_LONG_NAMES; + /* special hack in case no mib loaded - object identifiers will + * start with .iso......, in which case it is preferable + * to make the label entirely numeric (i.e., convert "iso" => "1") + */ + if (*lcp == '.' && lcp == name) { + if (!strncmp(".ccitt.",lcp,7)) { + name += 2; + *name = '.'; + *(name+1) = '0'; + } else if (!strncmp(".iso.",lcp,5)) { + name += 2; + *name = '.'; + *(name+1) = '1'; + } else if (!strncmp(".joint-iso-ccitt.",lcp,17)) { + name += 2; + *name = '.'; + *(name+1) = '2'; + } + } + } else if (*icp) { + *(icp++) = '\0'; + } + *last_label = (flag & USE_LONG_NAMES ? name : lcp); + + *iid = icp; + + return(SUCCESS); +} + +/* Convert a tag (string) to an OID array */ +/* Tag can be either a symbolic name, or an OID string */ +static struct tree * +__tag2oid(char *tag, char *iid, oid *oid_arr, int *oid_arr_len, int *type, + int best_guess) +{ + struct tree *tp = NULL; + struct tree *rtp = NULL; + oid newname[MAX_OID_LEN], *op; + size_t newname_len = 0; + + if (type) *type = TYPE_UNKNOWN; + if (oid_arr_len) *oid_arr_len = 0; + if (!tag) goto done; + + /*********************************************************/ + /* best_guess = 0 - same as no switches (read_objid) */ + /* if multiple parts, or uses find_node */ + /* if a single leaf */ + /* best_guess = 1 - same as -Ib (get_wild_node) */ + /* best_guess = 2 - same as -IR (get_node) */ + /*********************************************************/ + + /* numeric scalar (1,2) */ + /* single symbolic (1,2) */ + /* single regex (1) */ + /* partial full symbolic (2) */ + /* full symbolic (2) */ + /* module::single symbolic (2) */ + /* module::partial full symbolic (2) */ + if (best_guess == 1 || best_guess == 2) { + if (!__scan_num_objid(tag, newname, &newname_len)) { /* make sure it's not a numeric tag */ + newname_len = MAX_OID_LEN; + if (best_guess == 2) { /* Random search -IR */ + if (get_node(tag, newname, &newname_len)) { + rtp = tp = get_tree(newname, newname_len, get_tree_head()); + } + } + else if (best_guess == 1) { /* Regex search -Ib */ + clear_tree_flags(get_tree_head()); + if (get_wild_node(tag, newname, &newname_len)) { + rtp = tp = get_tree(newname, newname_len, get_tree_head()); + } + } + } + else { + rtp = tp = get_tree(newname, newname_len, get_tree_head()); + } + if (type) *type = (tp ? tp->type : TYPE_UNKNOWN); + if ((oid_arr == NULL) || (oid_arr_len == NULL)) return rtp; + memcpy(oid_arr,(char*)newname,newname_len*sizeof(oid)); + *oid_arr_len = newname_len; + } + + /* if best_guess is off and multi part tag or module::tag */ + /* numeric scalar */ + /* module::single symbolic */ + /* module::partial full symbolic */ + /* FULL symbolic OID */ + else if (strchr(tag,'.') || strchr(tag,':')) { + if (!__scan_num_objid(tag, newname, &newname_len)) { /* make sure it's not a numeric tag */ + newname_len = MAX_OID_LEN; + if (read_objid(tag, newname, &newname_len)) { /* long name */ + rtp = tp = get_tree(newname, newname_len, get_tree_head()); + } else { + /* failed to parse the OID */ + newname_len = 0; + } + } + else { + rtp = tp = get_tree(newname, newname_len, get_tree_head()); + } + if (type) *type = (tp ? tp->type : TYPE_UNKNOWN); + if ((oid_arr == NULL) || (oid_arr_len == NULL)) return rtp; + memcpy(oid_arr,(char*)newname,newname_len*sizeof(oid)); + *oid_arr_len = newname_len; + } + + /* else best_guess is off and it is a single leaf */ + /* single symbolic */ + else { + rtp = tp = find_node(tag, get_tree_head()); + if (tp) { + if (type) *type = tp->type; + if ((oid_arr == NULL) || (oid_arr_len == NULL)) return rtp; + /* code taken from get_node in snmp_client.c */ + for(op = newname + MAX_OID_LEN - 1; op >= newname; op--){ + *op = tp->subid; + tp = tp->parent; + if (tp == NULL) + break; + } + *oid_arr_len = newname + MAX_OID_LEN - op; + memcpy(oid_arr, op, *oid_arr_len * sizeof(oid)); + } else { + return(rtp); /* HACK: otherwise, concat_oid_str confuses things */ + } + } + done: + if (iid && *iid && oid_arr_len) + __concat_oid_str(oid_arr, oid_arr_len, iid); + return(rtp); +} + +/* function: __concat_oid_str + * + * This function converts a dotted-decimal string, soid_str, to an array + * of oid types and concatenates them on doid_arr begining at the index + * specified by doid_arr_len. + * + * returns : SUCCESS, FAILURE + */ +static int +__concat_oid_str(oid *doid_arr, int *doid_arr_len, char *soid_str) +{ + char *soid_buf; + char *cp; + char *st; + + if (!soid_str || !*soid_str) return SUCCESS;/* successfully added nothing */ + if (*soid_str == '.') soid_str++; + soid_buf = strdup(soid_str); + if (!soid_buf) + return FAILURE; + cp = strtok_r(soid_buf,".",&st); + while (cp) { + sscanf(cp, "%lu", &(doid_arr[(*doid_arr_len)++])); + /* doid_arr[(*doid_arr_len)++] = atoi(cp); */ + cp = strtok_r(NULL,".",&st); + } + free(soid_buf); + return(SUCCESS); +} + +#ifndef NETSNMP_NO_WRITE_SUPPORT +/* + * add a varbind to PDU + */ +static int +__add_var_val_str(netsnmp_pdu *pdu, oid *name, int name_length, char *val, + int len, int type) +{ + netsnmp_variable_list *vars; + oid oidbuf[MAX_OID_LEN]; + int ret = SUCCESS; + + if (pdu->variables == NULL){ + pdu->variables = vars = + (netsnmp_variable_list *)calloc(1,sizeof(netsnmp_variable_list)); + } else { + for(vars = pdu->variables; + vars->next_variable; + vars = vars->next_variable) + /*EXIT*/; + vars->next_variable = + (netsnmp_variable_list *)calloc(1,sizeof(netsnmp_variable_list)); + vars = vars->next_variable; + } + + vars->next_variable = NULL; + vars->name = snmp_duplicate_objid(name, name_length); + vars->name_length = name_length; + switch (type) { + case TYPE_INTEGER: + case TYPE_INTEGER32: + vars->type = ASN_INTEGER; + vars->val.integer = malloc(sizeof(long)); + if (val) + *(vars->val.integer) = strtol(val,NULL,0); + else { + ret = FAILURE; + *(vars->val.integer) = 0; + } + vars->val_len = sizeof(long); + break; + + case TYPE_GAUGE: + case TYPE_UNSIGNED32: + vars->type = ASN_GAUGE; + goto UINT; + case TYPE_COUNTER: + vars->type = ASN_COUNTER; + goto UINT; + case TYPE_TIMETICKS: + vars->type = ASN_TIMETICKS; + goto UINT; + case TYPE_UINTEGER: + vars->type = ASN_UINTEGER; +UINT: + vars->val.integer = malloc(sizeof(long)); + if (val) + sscanf(val,"%lu",vars->val.integer); + else { + ret = FAILURE; + *(vars->val.integer) = 0; + } + vars->val_len = sizeof(long); + break; + + case TYPE_OCTETSTR: + vars->type = ASN_OCTET_STR; + goto OCT; + + case TYPE_BITSTRING: + vars->type = ASN_OCTET_STR; + goto OCT; + + case TYPE_OPAQUE: + vars->type = ASN_OCTET_STR; +OCT: + vars->val.string = malloc(len); + vars->val_len = len; + if (val && len) + memcpy((char *)vars->val.string, val, len); + else { + ret = FAILURE; + vars->val.string = (u_char*)strdup(""); + vars->val_len = 0; + } + break; + + case TYPE_IPADDR: + vars->type = ASN_IPADDRESS; + { + in_addr_t addr; + + if (val) + addr = inet_addr(val); + else { + ret = FAILURE; + addr = 0; + } + vars->val.integer = netsnmp_memdup((u_char **)&addr, sizeof(addr)); + vars->val_len = sizeof(addr); + } + break; + + case TYPE_OBJID: + vars->type = ASN_OBJECT_ID; + vars->val_len = MAX_OID_LEN; + /* if (read_objid(val, oidbuf, &(vars->val_len))) { */ + /* tp = __tag2oid(val,NULL,oidbuf,&(vars->val_len),NULL,0); */ + if (!val || !snmp_parse_oid(val, oidbuf, &vars->val_len)) { + vars->val.objid = NULL; + ret = FAILURE; + } else { + vars->val.objid = snmp_duplicate_objid(oidbuf, vars->val_len); + vars->val_len *= sizeof(oid); + } + break; + + default: + vars->type = ASN_NULL; + vars->val_len = 0; + vars->val.string = NULL; + ret = FAILURE; + } + + return ret; +} +#endif /* NETSNMP_NO_WRITE_SUPPORT */ + +/* takes ss and pdu as input and updates the 'response' argument */ +/* the input 'pdu' argument will be freed */ +static int +__send_sync_pdu(netsnmp_session *ss, netsnmp_pdu *pdu, netsnmp_pdu **response, + int retry_nosuch, char *err_str, int *err_num, int *err_ind) +{ + int status = 0; + long command = pdu->command; + char *tmp_err_str; + + *err_num = 0; + *err_ind = 0; + *response = NULL; + tmp_err_str = NULL; + memset(err_str, '\0', STR_BUF_SIZE); + + if (ss == NULL) { + *err_num = 0; + *err_ind = SNMPERR_BAD_SESSION; + status = SNMPERR_BAD_SESSION; + strlcpy(err_str, snmp_api_errstring(*err_ind), STR_BUF_SIZE); + goto done; + } + +retry: + + Py_BEGIN_ALLOW_THREADS + status = snmp_sess_synch_response(ss, pdu, response); + Py_END_ALLOW_THREADS + + if ((*response == NULL) && (status == STAT_SUCCESS)) status = STAT_ERROR; + + switch (status) { + case STAT_SUCCESS: + switch ((*response)->errstat) { + case SNMP_ERR_NOERROR: + break; + + case SNMP_ERR_NOSUCHNAME: + if (retry_nosuch && (pdu = snmp_fix_pdu(*response, command))) { + if (*response) snmp_free_pdu(*response); + goto retry; + } + + /* Pv1, SNMPsec, Pv2p, v2c, v2u, v2*, and SNMPv3 PDUs */ + case SNMP_ERR_TOOBIG: + case SNMP_ERR_BADVALUE: + case SNMP_ERR_READONLY: + case SNMP_ERR_GENERR: + /* in SNMPv2p, SNMPv2c, SNMPv2u, SNMPv2*, and SNMPv3 PDUs */ + case SNMP_ERR_NOACCESS: + case SNMP_ERR_WRONGTYPE: + case SNMP_ERR_WRONGLENGTH: + case SNMP_ERR_WRONGENCODING: + case SNMP_ERR_WRONGVALUE: + case SNMP_ERR_NOCREATION: + case SNMP_ERR_INCONSISTENTVALUE: + case SNMP_ERR_RESOURCEUNAVAILABLE: + case SNMP_ERR_COMMITFAILED: + case SNMP_ERR_UNDOFAILED: + case SNMP_ERR_AUTHORIZATIONERROR: + case SNMP_ERR_NOTWRITABLE: + /* in SNMPv2c, SNMPv2u, SNMPv2*, and SNMPv3 PDUs */ + case SNMP_ERR_INCONSISTENTNAME: + default: + strlcpy(err_str, (char*)snmp_errstring((*response)->errstat), + STR_BUF_SIZE); + *err_num = (int)(*response)->errstat; + *err_ind = (*response)->errindex; + status = (*response)->errstat; + break; + } + break; + + case STAT_TIMEOUT: + case STAT_ERROR: + snmp_sess_error(ss, err_num, err_ind, &tmp_err_str); + strlcpy(err_str, tmp_err_str, STR_BUF_SIZE); + break; + + default: + strcat(err_str, "send_sync_pdu: unknown status"); + *err_num = ss->s_snmp_errno; + break; + } +done: + if (tmp_err_str) { + free(tmp_err_str); + } + if (_debug_level && *err_num) printf("XXX sync PDU: %s\n", err_str); + return(status); +} + +static PyObject * +py_netsnmp_construct_varbind(void) +{ + PyObject *module; + PyObject *dict; + PyObject *callable; + + module = PyImport_ImportModule("netsnmp"); + dict = PyModule_GetDict(module); + + callable = PyDict_GetItemString(dict, "Varbind"); + + return PyObject_CallFunction(callable, ""); +} + +static int +py_netsnmp_attr_string(PyObject *obj, char * attr_name, char **val, + Py_ssize_t *len) +{ + *val = NULL; + if (obj && attr_name && PyObject_HasAttrString(obj, attr_name)) { + PyObject *attr = PyObject_GetAttrString(obj, attr_name); + if (attr) { + int retval; + retval = PyString_AsStringAndSize(attr, val, len); + Py_DECREF(attr); + return retval; + } + } + + return -1; +} + +static long long +py_netsnmp_attr_long(PyObject *obj, char * attr_name) +{ + long long val = -1; + + if (obj && attr_name && PyObject_HasAttrString(obj, attr_name)) { + PyObject *attr = PyObject_GetAttrString(obj, attr_name); + if (attr) { + val = PyInt_AsLong(attr); + Py_DECREF(attr); + } + } + + return val; +} + +static void * +py_netsnmp_attr_void_ptr(PyObject *obj, char * attr_name) +{ + void *val = NULL; + + if (obj && attr_name && PyObject_HasAttrString(obj, attr_name)) { + PyObject *attr = PyObject_GetAttrString(obj, attr_name); + if (attr) { + val = PyLong_AsVoidPtr(attr); + Py_DECREF(attr); + } + } + + return val; +} + +static int +py_netsnmp_verbose(void) +{ + int verbose = 0; + PyObject *pkg = PyImport_ImportModule("netsnmp"); + if (pkg) { + verbose = py_netsnmp_attr_long(pkg, "verbose"); + Py_DECREF(pkg); + } + + return verbose; +} + +static int +py_netsnmp_attr_set_string(PyObject *obj, char *attr_name, + char *val, size_t len) +{ + int ret = -1; + if (obj && attr_name) { + PyObject* val_obj = (val ? + Py_BuildValue("s#", val, len) : + Py_BuildValue("")); + if (!val_obj) + return -1; + ret = PyObject_SetAttrString(obj, attr_name, val_obj); + Py_DECREF(val_obj); + } + return ret; +} + +/** + * Update python session object error attributes. + * + * Copy the error info which may have been returned from __send_sync_pdu(...) + * into the python object. This will allow the python code to determine if + * an error occured during an snmp operation. + * + * Currently there are 3 attributes we care about + * + * ErrorNum - Copy of the value of netsnmp_session.s_errno. This is the system + * errno that was generated during our last call into the net-snmp library. + * + * ErrorInd - Copy of the value of netsmp_session.s_snmp_errno. These error + * numbers are separate from the system errno's and describe SNMP errors. + * + * ErrorStr - A string describing the ErrorInd that was returned during our last + * operation. + * + * @param[in] session The python object that represents our current Session + * @param[in|out] err_str A string describing err_ind + * @param[in|out] err_num The system errno currently stored by our session + * @param[in|out] err_ind The snmp errno currently stored by our session + */ +static void +__py_netsnmp_update_session_errors(PyObject *session, char *err_str, + int err_num, int err_ind) +{ + PyObject *tmp_for_conversion; + + py_netsnmp_attr_set_string(session, "ErrorStr", err_str, STRLEN(err_str)); + + tmp_for_conversion = PyInt_FromLong(err_num); + if (!tmp_for_conversion) + return; /* nothing better to do? */ + PyObject_SetAttrString(session, "ErrorNum", tmp_for_conversion); + Py_DECREF(tmp_for_conversion); + + tmp_for_conversion = PyInt_FromLong(err_ind); + if (!tmp_for_conversion) + return; /* nothing better to do? */ + PyObject_SetAttrString(session, "ErrorInd", tmp_for_conversion); + Py_DECREF(tmp_for_conversion); +} + +static PyObject * +netsnmp_create_session(PyObject *self, PyObject *args) +{ + int version; + char *community; + char *peer; + int lport; + int retries; + int timeout; + SnmpSession session = {0}; + SnmpSession *ss = NULL; + int verbose = py_netsnmp_verbose(); + + if (!PyArg_ParseTuple(args, "issiii", &version, + &community, &peer, &lport, + &retries, &timeout)) + return NULL; + + __libraries_init("python"); + + snmp_sess_init(&session); + + session.version = -1; +#ifndef DISABLE_SNMPV1 + if (version == 1) { + session.version = SNMP_VERSION_1; + } +#endif +#ifndef DISABLE_SNMPV2C + if (version == 2) { + session.version = SNMP_VERSION_2c; + } +#endif + if (version == 3) { + session.version = SNMP_VERSION_3; + } + if (session.version == -1) { + if (verbose) + printf("error:snmp_new_session:Unsupported SNMP version (%d)\n", version); + goto end; + } + + session.community_len = STRLEN((char *)community); + session.community = (u_char *)community; + session.peername = peer; + session.local_port = lport; + session.retries = retries; /* 5 */ + session.timeout = timeout; /* 1000000L */ + session.authenticator = NULL; + + ss = snmp_sess_open(&session); + + if (ss == NULL) { + if (verbose) + printf("error:snmp_new_session: Couldn't open SNMP session"); + } + end: + return PyLong_FromVoidPtr((void *)ss); +} + +static PyObject * +netsnmp_create_session_v3(PyObject *self, PyObject *args) +{ + int version; + char *peer; + int lport; + int retries; + int timeout; + char * sec_name; + int sec_level; + char * sec_eng_id; + char * context_eng_id; + char * context; + char * auth_proto; + char * auth_pass; + char * priv_proto; + char * priv_pass; + int priv_type; + int eng_boots; + int eng_time; + SnmpSession session = {0}; + SnmpSession *ss = NULL; + int verbose = py_netsnmp_verbose(); + + if (!PyArg_ParseTuple(args, "isiiisisssssssii", &version, + &peer, &lport, &retries, &timeout, + &sec_name, &sec_level, &sec_eng_id, + &context_eng_id, &context, + &auth_proto, &auth_pass, + &priv_proto, &priv_pass, + &eng_boots, &eng_time)) + return NULL; + + __libraries_init("python"); + snmp_sess_init(&session); + + if (version == 3) { + session.version = SNMP_VERSION_3; + } else { + if (verbose) + printf("error:snmp_new_v3_session:Unsupported SNMP version (%d)\n", version); + goto end; + } + + session.peername = peer; + session.retries = retries; /* 5 */ + session.timeout = timeout; /* 1000000L */ + session.authenticator = NULL; + session.contextNameLen = STRLEN(context); + session.contextName = context; + session.securityNameLen = STRLEN(sec_name); + session.securityName = sec_name; + session.securityLevel = sec_level; + session.securityModel = USM_SEC_MODEL_NUMBER; + session.securityEngineIDLen = + hex_to_binary2((unsigned char*)sec_eng_id, STRLEN(sec_eng_id), + (char **) &session.securityEngineID); + session.contextEngineIDLen = + hex_to_binary2((unsigned char*)sec_eng_id, STRLEN(sec_eng_id), + (char **) &session.contextEngineID); + session.engineBoots = eng_boots; + session.engineTime = eng_time; + + if (!strcmp(auth_proto, "DEFAULT")) { + const oid* a = get_default_authtype(&session.securityAuthProtoLen); + session.securityAuthProto + = snmp_duplicate_objid(a, session.securityAuthProtoLen); + } else { + const oid *auth_prot; + int auth_type = usm_lookup_auth_type(auth_proto); + if (auth_type < 0) { + if (verbose) + printf("error:snmp_new_v3_session:Unsupported authentication protocol(%s)\n", auth_proto); + goto end; + } + auth_prot = sc_get_auth_oid(auth_type, &session.securityAuthProtoLen); + if (auth_prot) + session.securityAuthProto = + snmp_duplicate_objid(auth_prot, session.securityAuthProtoLen); + } + if (session.securityLevel >= SNMP_SEC_LEVEL_AUTHNOPRIV) { + if (STRLEN(auth_pass) > 0) { + session.securityAuthKeyLen = USM_AUTH_KU_LEN; + if (generate_Ku(session.securityAuthProto, + session.securityAuthProtoLen, + (u_char *)auth_pass, STRLEN(auth_pass), + session.securityAuthKey, + &session.securityAuthKeyLen) != SNMPERR_SUCCESS) { + if (verbose) + printf("error:snmp_new_v3_session:Error generating Ku from authentication password.\n"); + goto end; + } + } + } + if (!strcmp(priv_proto, "DEFAULT")) { + const oid *p = get_default_privtype(&session.securityPrivProtoLen); + session.securityPrivProto + = snmp_duplicate_objid(p, session.securityPrivProtoLen); + } else { + const oid *priv_prot; + priv_type = usm_lookup_priv_type(priv_proto); + if (priv_type < 0) { + if (verbose) + printf("error:snmp_new_v3_session:Unsupported privacy protocol(%s)\n", priv_proto); + goto end; + } + priv_prot = sc_get_priv_oid(priv_type, &session.securityPrivProtoLen); + if (priv_prot) + session.securityPrivProto = + snmp_duplicate_objid(priv_prot, session.securityPrivProtoLen); + } + + if (session.securityLevel >= SNMP_SEC_LEVEL_AUTHPRIV) { + session.securityPrivKeyLen = USM_PRIV_KU_LEN; + if (generate_Ku(session.securityAuthProto, + session.securityAuthProtoLen, + (u_char *)priv_pass, STRLEN(priv_pass), + session.securityPrivKey, + &session.securityPrivKeyLen) != SNMPERR_SUCCESS) { + if (verbose) + printf("error:v3_session: couldn't gen Ku from priv pass phrase.\n"); + goto end; + } + } + + ss = snmp_sess_open(&session); + + end: + if (ss == NULL) { + if (verbose) + printf("error:v3_session: couldn't open SNMP session(%s).\n", + snmp_api_errstring(snmp_errno)); + } + free (session.securityEngineID); + free (session.contextEngineID); + + return PyLong_FromVoidPtr((void *)ss); +} + +static PyObject * +netsnmp_create_session_tunneled(PyObject *self, PyObject *args) +{ + int version; + char *peer; + int lport; + int retries; + int timeout; + char * sec_name; + int sec_level; + char * context_eng_id; + char * context; + char * our_identity; + char * their_identity; + char * their_hostname; + char * trust_cert; + SnmpSession session = {0}; + SnmpSession *ss = NULL; + int verbose = py_netsnmp_verbose(); + + if (!PyArg_ParseTuple(args, "isiiisissssss", &version, + &peer, &lport, &retries, &timeout, + &sec_name, &sec_level, + &context_eng_id, &context, + &our_identity, &their_identity, + &their_hostname, &trust_cert)) + return NULL; + + __libraries_init("python"); + snmp_sess_init(&session); + + if (version != 3) { + session.version = SNMP_VERSION_3; + if (verbose) + printf("Using version 3 as it's the only version that supports tunneling\n"); + } + + session.peername = peer; + session.retries = retries; /* 5 */ + session.timeout = timeout; /* 1000000L */ + session.contextNameLen = STRLEN(context); + session.contextName = context; + session.securityNameLen = STRLEN(sec_name); + session.securityName = sec_name; + session.securityLevel = sec_level; + session.securityModel = NETSNMP_TSM_SECURITY_MODEL; + + /* create the transport configuration store */ + if (!session.transport_configuration) { + netsnmp_container_init_list(); + session.transport_configuration = + netsnmp_container_find("transport_configuration:fifo"); + if (!session.transport_configuration) { + fprintf(stderr, "failed to initialize the transport configuration container\n"); + return NULL; + } + + session.transport_configuration->compare = + (netsnmp_container_compare*) + netsnmp_transport_config_compare; + } + + if (our_identity && our_identity[0] != '\0') + CONTAINER_INSERT(session.transport_configuration, + netsnmp_transport_create_config("localCert", + our_identity)); + + if (their_identity && their_identity[0] != '\0') + CONTAINER_INSERT(session.transport_configuration, + netsnmp_transport_create_config("peerCert", + their_identity)); + + if (their_hostname && their_hostname[0] != '\0') + CONTAINER_INSERT(session.transport_configuration, + netsnmp_transport_create_config("their_hostname", + their_hostname)); + + if (trust_cert && trust_cert[0] != '\0') + CONTAINER_INSERT(session.transport_configuration, + netsnmp_transport_create_config("trust_cert", + trust_cert)); + + ss = snmp_sess_open(&session); + + if (!ss) + return NULL; + /* + * Note: on a 64-bit system the statement below discards the upper 32 bits of + * "ss", which is most likely a bug. + */ + return Py_BuildValue("i", (int)(uintptr_t)ss); +} + +static PyObject * +netsnmp_delete_session(PyObject *self, PyObject *args) +{ + PyObject *session; + SnmpSession *ss = NULL; + + if (!PyArg_ParseTuple(args, "O", &session)) { + return NULL; + } + + ss = (SnmpSession *)py_netsnmp_attr_void_ptr(session, "sess_ptr"); + + snmp_sess_close(ss); + return (Py_BuildValue("")); +} + + +static PyObject * +netsnmp_get(PyObject *self, PyObject *args) +{ + PyObject *session; + PyObject *varlist; + PyObject *varbind; + PyObject *val_tuple = NULL; + int varlist_len = 0; + int varlist_ind; + netsnmp_session *ss; + netsnmp_pdu *pdu, *response; + netsnmp_variable_list *vars; + struct tree *tp; + int len; + oid *oid_arr; + int oid_arr_len = MAX_OID_LEN; + int type; + char type_str[MAX_TYPE_NAME_LEN]; + u_char *str_buf = NULL; + size_t str_buf_len = 0; + size_t out_len = 0; + int buf_over = 0; + char *tag; + char *iid; + int getlabel_flag = NO_FLAGS; + int sprintval_flag = USE_BASIC; + int verbose = py_netsnmp_verbose(); + int old_format; + int best_guess; + int retry_nosuch; + int err_ind; + int err_num; + char err_str[STR_BUF_SIZE]; + char *tmpstr; + Py_ssize_t tmplen; + + oid_arr = calloc(MAX_OID_LEN, sizeof(oid)); + + if (oid_arr && args) { + + if (!PyArg_ParseTuple(args, "OO", &session, &varlist)) { + goto done; + } + + ss = (SnmpSession *)py_netsnmp_attr_void_ptr(session, "sess_ptr"); + + if (py_netsnmp_attr_string(session, "ErrorStr", &tmpstr, &tmplen) < 0) { + goto done; + } + + if (py_netsnmp_attr_long(session, "UseLongNames")) + getlabel_flag |= USE_LONG_NAMES; + if (py_netsnmp_attr_long(session, "UseNumeric")) + getlabel_flag |= USE_NUMERIC_OIDS; + if (py_netsnmp_attr_long(session, "UseEnums")) + sprintval_flag = USE_ENUMS; + if (py_netsnmp_attr_long(session, "UseSprintValue")) + sprintval_flag = USE_SPRINT_VALUE; + best_guess = py_netsnmp_attr_long(session, "BestGuess"); + retry_nosuch = py_netsnmp_attr_long(session, "RetryNoSuch"); + + pdu = snmp_pdu_create(SNMP_MSG_GET); + + if (varlist) { + PyObject *varlist_iter = PyObject_GetIter(varlist); + + while (varlist_iter && (varbind = PyIter_Next(varlist_iter))) { + if (py_netsnmp_attr_string(varbind, "tag", &tag, NULL) < 0 || + py_netsnmp_attr_string(varbind, "iid", &iid, NULL) < 0) + { + oid_arr_len = 0; + } else { + tp = __tag2oid(tag, iid, oid_arr, &oid_arr_len, NULL, best_guess); + } + + if (oid_arr_len) { + snmp_add_null_var(pdu, oid_arr, oid_arr_len); + varlist_len++; + } else { + if (verbose) + printf("error: get: unknown object ID (%s)", + (tag ? tag : "")); + snmp_free_pdu(pdu); + Py_DECREF(varbind); + goto done; + } + /* release reference when done */ + Py_DECREF(varbind); + } + + Py_DECREF(varlist_iter); + + if (PyErr_Occurred()) { + /* propagate error */ + if (verbose) + printf("error: get: unknown python error"); + snmp_free_pdu(pdu); + goto done; + } + } + + __send_sync_pdu(ss, pdu, &response, retry_nosuch, err_str, &err_num, + &err_ind); + __py_netsnmp_update_session_errors(session, err_str, err_num, err_ind); + + /* + ** Set up for numeric or full OID's, if necessary. Save the old + ** output format so that it can be restored when we finish -- this + ** is a library-wide global, and has to be set/restored for each + ** session. + */ + old_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT); + + if (py_netsnmp_attr_long(session, "UseLongNames")) { + getlabel_flag |= USE_LONG_NAMES; + + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, + NETSNMP_OID_OUTPUT_FULL); + } + /* Setting UseNumeric forces UseLongNames on so check for UseNumeric + after UseLongNames (above) to make sure the final outcome of + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT is NETSNMP_OID_OUTPUT_NUMERIC */ + if (py_netsnmp_attr_long(session, "UseNumeric")) { + getlabel_flag |= USE_LONG_NAMES; + getlabel_flag |= USE_NUMERIC_OIDS; + + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, + NETSNMP_OID_OUTPUT_NUMERIC); + } + + val_tuple = PyTuple_New(varlist_len); + /* initialize return tuple */ + for (varlist_ind = 0; varlist_ind < varlist_len; varlist_ind++) { + PyTuple_SetItem(val_tuple, varlist_ind, Py_BuildValue("")); + } + + for(vars = (response ? response->variables : NULL), varlist_ind = 0; + vars && (varlist_ind < varlist_len); + vars = vars->next_variable, varlist_ind++) { + + varbind = PySequence_GetItem(varlist, varlist_ind); + + if (PyObject_HasAttrString(varbind, "tag")) { + if (str_buf == NULL) { + str_buf = (u_char *) netsnmp_malloc(STR_BUF_SIZE); + str_buf_len = STR_BUF_SIZE; + } + *str_buf = '.'; + *(str_buf+1) = '\0'; + out_len = 0; + tp = netsnmp_sprint_realloc_objid_tree(&str_buf, &str_buf_len, + &out_len, 1, &buf_over, + vars->name,vars->name_length); + if (_debug_level) + printf("netsnmp_get:str_buf:%s:%d:%d\n", str_buf, + (int)str_buf_len, (int)out_len); + + if (__is_leaf(tp)) { + type = (tp->type ? tp->type : tp->parent->type); + getlabel_flag &= ~NON_LEAF_NAME; + if (_debug_level) printf("netsnmp_get:is_leaf:%d\n",type); + } else { + getlabel_flag |= NON_LEAF_NAME; + type = __translate_asn_type(vars->type); + if (_debug_level) printf("netsnmp_get:!is_leaf:%d\n",tp->type); + } + __get_label_iid((char *) str_buf, &tag, &iid, getlabel_flag); + + py_netsnmp_attr_set_string(varbind, "tag", tag, STRLEN(tag)); + py_netsnmp_attr_set_string(varbind, "iid", iid, STRLEN(iid)); + + __get_type_str(type, type_str); + + py_netsnmp_attr_set_string(varbind, "type", type_str, strlen(type_str)); + + len = __snprint_value((char **)&str_buf, &str_buf_len, + vars, tp, type, sprintval_flag); + str_buf[len] = '\0'; + py_netsnmp_attr_set_string(varbind, "val", (char *) str_buf, len); + + /* save in return tuple as well */ + if ((type == SNMP_ENDOFMIBVIEW) || + (type == SNMP_NOSUCHOBJECT) || + (type == SNMP_NOSUCHINSTANCE)) { + /* Translate error to None */ + PyTuple_SetItem(val_tuple, varlist_ind, + Py_BuildValue("")); + } else { + PyTuple_SetItem(val_tuple, varlist_ind, + Py_BuildValue("s#", str_buf, len)); + } + Py_DECREF(varbind); + } else { + printf("netsnmp_get: bad varbind (%d)\n", varlist_ind); + Py_XDECREF(varbind); + } + } + + /* Reset the library's behavior for numeric/symbolic OID's. */ + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, + old_format); + + if (response) snmp_free_pdu(response); + } + + done: + free(oid_arr); + if (str_buf != NULL) netsnmp_free(str_buf); + return (val_tuple ? val_tuple : Py_BuildValue("")); +} + +static PyObject * +netsnmp_getnext(PyObject *self, PyObject *args) +{ + PyObject *session; + PyObject *varlist; + PyObject *varbind; + PyObject *val_tuple = NULL; + int varlist_len = 0; + int varlist_ind; + netsnmp_session *ss; + netsnmp_pdu *pdu, *response; + netsnmp_variable_list *vars; + struct tree *tp; + int len; + oid *oid_arr; + int oid_arr_len = MAX_OID_LEN; + int type; + char type_str[MAX_TYPE_NAME_LEN]; + u_char *str_buf = NULL; + size_t str_buf_len = 0; + size_t out_len = 0; + int buf_over = 0; + char *tag; + char *iid = NULL; + int getlabel_flag = NO_FLAGS; + int sprintval_flag = USE_BASIC; + int verbose = py_netsnmp_verbose(); + int old_format; + int best_guess; + int retry_nosuch; + int err_ind; + int err_num; + char err_str[STR_BUF_SIZE]; + char *tmpstr; + Py_ssize_t tmplen; + + oid_arr = calloc(MAX_OID_LEN, sizeof(oid)); + + if (oid_arr && args) { + + if (!PyArg_ParseTuple(args, "OO", &session, &varlist)) { + goto done; + } + + ss = (SnmpSession *)py_netsnmp_attr_void_ptr(session, "sess_ptr"); + + if (py_netsnmp_attr_string(session, "ErrorStr", &tmpstr, &tmplen) < 0) { + goto done; + } + memcpy(&err_str, tmpstr, tmplen); + err_num = py_netsnmp_attr_long(session, "ErrorNum"); + err_ind = py_netsnmp_attr_long(session, "ErrorInd"); + + if (py_netsnmp_attr_long(session, "UseLongNames")) + getlabel_flag |= USE_LONG_NAMES; + if (py_netsnmp_attr_long(session, "UseNumeric")) + getlabel_flag |= USE_NUMERIC_OIDS; + if (py_netsnmp_attr_long(session, "UseEnums")) + sprintval_flag = USE_ENUMS; + if (py_netsnmp_attr_long(session, "UseSprintValue")) + sprintval_flag = USE_SPRINT_VALUE; + best_guess = py_netsnmp_attr_long(session, "BestGuess"); + retry_nosuch = py_netsnmp_attr_long(session, "RetryNoSuch"); + + pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); + + if (varlist) { + PyObject *varlist_iter = PyObject_GetIter(varlist); + + while (varlist_iter && (varbind = PyIter_Next(varlist_iter))) { + if (py_netsnmp_attr_string(varbind, "tag", &tag, NULL) < 0 || + py_netsnmp_attr_string(varbind, "iid", &iid, NULL) < 0) + { + oid_arr_len = 0; + } else { + tp = __tag2oid(tag, iid, oid_arr, &oid_arr_len, NULL, best_guess); + } + + if (_debug_level) + printf("netsnmp_getnext: filling request: %s:%s:%d:%d\n", + tag, iid, oid_arr_len,best_guess); + + if (oid_arr_len) { + snmp_add_null_var(pdu, oid_arr, oid_arr_len); + varlist_len++; + } else { + if (verbose) + printf("error: getnext: unknown object ID (%s)", + (tag ? tag : "")); + snmp_free_pdu(pdu); + Py_DECREF(varbind); + goto done; + } + /* release reference when done */ + Py_DECREF(varbind); + } + + Py_DECREF(varlist_iter); + + if (PyErr_Occurred()) { + /* propagate error */ + if (verbose) + printf("error: getnext: unknown python error"); + snmp_free_pdu(pdu); + goto done; + } + } + + __send_sync_pdu(ss, pdu, &response, retry_nosuch, err_str, &err_num, + &err_ind); + __py_netsnmp_update_session_errors(session, err_str, err_num, err_ind); + + /* + ** Set up for numeric or full OID's, if necessary. Save the old + ** output format so that it can be restored when we finish -- this + ** is a library-wide global, and has to be set/restored for each + ** session. + */ + old_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT); + + if (py_netsnmp_attr_long(session, "UseLongNames")) { + getlabel_flag |= USE_LONG_NAMES; + + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, + NETSNMP_OID_OUTPUT_FULL); + } + /* Setting UseNumeric forces UseLongNames on so check for UseNumeric + after UseLongNames (above) to make sure the final outcome of + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT is NETSNMP_OID_OUTPUT_NUMERIC */ + if (py_netsnmp_attr_long(session, "UseNumeric")) { + getlabel_flag |= USE_LONG_NAMES; + getlabel_flag |= USE_NUMERIC_OIDS; + + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, + NETSNMP_OID_OUTPUT_NUMERIC); + } + + val_tuple = PyTuple_New(varlist_len); + /* initialize return tuple */ + for (varlist_ind = 0; varlist_ind < varlist_len; varlist_ind++) { + PyTuple_SetItem(val_tuple, varlist_ind, Py_BuildValue("")); + } + + for(vars = (response ? response->variables : NULL), varlist_ind = 0; + vars && (varlist_ind < varlist_len); + vars = vars->next_variable, varlist_ind++) { + + varbind = PySequence_GetItem(varlist, varlist_ind); + + if (PyObject_HasAttrString(varbind, "tag")) { + if (str_buf == NULL) { + str_buf = (u_char *) netsnmp_malloc(STR_BUF_SIZE); + str_buf_len = STR_BUF_SIZE; + } + *str_buf = '.'; + *(str_buf+1) = '\0'; + out_len = 0; + tp = netsnmp_sprint_realloc_objid_tree(&str_buf, &str_buf_len, + &out_len, 1, &buf_over, + vars->name,vars->name_length); + + if (__is_leaf(tp)) { + type = (tp->type ? tp->type : tp->parent->type); + getlabel_flag &= ~NON_LEAF_NAME; + } else { + getlabel_flag |= NON_LEAF_NAME; + type = __translate_asn_type(vars->type); + } + + __get_label_iid((char *) str_buf, &tag, &iid, getlabel_flag); + + if (_debug_level) + printf("netsnmp_getnext: filling response: %s:%s\n", tag, iid); + + py_netsnmp_attr_set_string(varbind, "tag", tag, STRLEN(tag)); + py_netsnmp_attr_set_string(varbind, "iid", iid, STRLEN(iid)); + + __get_type_str(type, type_str); + + py_netsnmp_attr_set_string(varbind, "type", type_str, + strlen(type_str)); + + len = __snprint_value((char **)&str_buf, &str_buf_len, + vars, tp, type, sprintval_flag); + str_buf[len] = '\0'; + + py_netsnmp_attr_set_string(varbind, "val", (char *) str_buf, len); + + /* save in return tuple as well */ + if ((type == SNMP_ENDOFMIBVIEW) || + (type == SNMP_NOSUCHOBJECT) || + (type == SNMP_NOSUCHINSTANCE)) { + /* Translate error to None */ + PyTuple_SetItem(val_tuple, varlist_ind, + Py_BuildValue("")); + } else { + PyTuple_SetItem(val_tuple, varlist_ind, + Py_BuildValue("s#", str_buf, len)); + } + Py_DECREF(varbind); + } else { + printf("netsnmp_getnext: bad varbind (%d)\n", varlist_ind); + Py_XDECREF(varbind); + } + } + + /* Reset the library's behavior for numeric/symbolic OID's. */ + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, + old_format); + + if (response) snmp_free_pdu(response); + } + + done: + free(oid_arr); + if (str_buf != NULL) netsnmp_free(str_buf); + return (val_tuple ? val_tuple : Py_BuildValue("")); +} + +static PyObject * +netsnmp_walk(PyObject *self, PyObject *args) +{ + PyObject *session; + PyObject *varlist; + PyObject *varlist_iter; + PyObject *varbind; + PyObject *val_tuple = NULL; + PyObject *varbinds = NULL; + int varlist_len = 0; + int varlist_ind; + netsnmp_session *ss; + netsnmp_pdu *pdu, *response; + netsnmp_pdu *newpdu; + netsnmp_variable_list *vars, *oldvars; + struct tree *tp; + int len; + oid **oid_arr = NULL; + int *oid_arr_len = NULL; + oid **oid_arr_broken_check = NULL; + int *oid_arr_broken_check_len = NULL; + int type; + char type_str[MAX_TYPE_NAME_LEN]; + int status; + u_char *str_buf = NULL; + size_t str_buf_len = 0; + size_t out_len = 0; + int buf_over = 0; + char *tag; + char *iid = NULL; + int getlabel_flag = NO_FLAGS; + int sprintval_flag = USE_BASIC; + int verbose = py_netsnmp_verbose(); + int old_format; + int best_guess; + int retry_nosuch; + int err_ind; + int err_num; + char err_str[STR_BUF_SIZE]; + int notdone = 1; + int result_count = 0; + char *tmpstr; + Py_ssize_t tmplen; + + if (args) { + + if (!PyArg_ParseTuple(args, "OO", &session, &varlist)) { + goto done; + } + + if (!varlist) { + goto done; + } + + if ((varbinds = PyObject_GetAttrString(varlist, "varbinds")) == NULL) { + goto done; + } + ss = (SnmpSession *)py_netsnmp_attr_void_ptr(session, "sess_ptr"); + + if (py_netsnmp_attr_string(session, "ErrorStr", &tmpstr, &tmplen) < 0) { + goto done; + } + memcpy(&err_str, tmpstr, tmplen); + err_num = py_netsnmp_attr_long(session, "ErrorNum"); + err_ind = py_netsnmp_attr_long(session, "ErrorInd"); + + if (py_netsnmp_attr_long(session, "UseLongNames")) + getlabel_flag |= USE_LONG_NAMES; + if (py_netsnmp_attr_long(session, "UseNumeric")) + getlabel_flag |= USE_NUMERIC_OIDS; + if (py_netsnmp_attr_long(session, "UseEnums")) + sprintval_flag = USE_ENUMS; + if (py_netsnmp_attr_long(session, "UseSprintValue")) + sprintval_flag = USE_SPRINT_VALUE; + best_guess = py_netsnmp_attr_long(session, "BestGuess"); + retry_nosuch = py_netsnmp_attr_long(session, "RetryNoSuch"); + + pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); + + /* we need an initial count for memory allocation */ + varlist_iter = PyObject_GetIter(varlist); + varlist_len = 0; + while (varlist_iter && (varbind = PyIter_Next(varlist_iter))) { + varlist_len++; + } + Py_DECREF(varlist_iter); + + oid_arr_len = calloc(varlist_len, sizeof(int)); + oid_arr_broken_check_len = calloc(varlist_len, sizeof(int)); + + oid_arr = calloc(varlist_len, sizeof(oid *)); + oid_arr_broken_check = calloc(varlist_len, sizeof(oid *)); + + for(varlist_ind = 0; varlist_ind < varlist_len; varlist_ind++) { + + oid_arr[varlist_ind] = calloc(MAX_OID_LEN, sizeof(oid)); + oid_arr_broken_check[varlist_ind] = calloc(MAX_OID_LEN, sizeof(oid)); + + oid_arr_len[varlist_ind] = MAX_OID_LEN; + oid_arr_broken_check_len[varlist_ind] = MAX_OID_LEN; + } + + /* get the initial starting oids*/ + varlist_iter = PyObject_GetIter(varlist); + varlist_ind = 0; + while (varlist_iter && (varbind = PyIter_Next(varlist_iter))) { + + if (py_netsnmp_attr_string(varbind, "tag", &tag, NULL) < 0 || + py_netsnmp_attr_string(varbind, "iid", &iid, NULL) < 0) + { + oid_arr_len[varlist_ind] = 0; + } else { + tp = __tag2oid(tag, iid, + oid_arr[varlist_ind], &oid_arr_len[varlist_ind], + NULL, best_guess); + } + + if (_debug_level) + printf("netsnmp_walk: filling request: %s:%s:%d:%d\n", + tag, iid, oid_arr_len[varlist_ind],best_guess); + + if (oid_arr_len[varlist_ind]) { + snmp_add_null_var(pdu, oid_arr[varlist_ind], oid_arr_len[varlist_ind]); + } else { + if (verbose) + printf("error: walk: unknown object ID (%s)", + (tag ? tag : "")); + snmp_free_pdu(pdu); + Py_DECREF(varbind); + goto done; + } + /* release reference when done */ + Py_DECREF(varbind); + varlist_ind++; + } + + if (varlist_iter) + Py_DECREF(varlist_iter); + + if (PyErr_Occurred()) { + /* propagate error */ + if (verbose) + printf("error: walk: unknown python error (varlist)"); + snmp_free_pdu(pdu); + goto done; + } + + /* pre-allocate the return tuples */ + val_tuple = PyTuple_New(0); + + if (!val_tuple) { + /* propagate error */ + if (verbose) + printf("error: walk: couldn't allocate a new value tuple"); + snmp_free_pdu(pdu); + goto done; + } + + /* + ** Set up for numeric or full OID's, if necessary. Save the old + ** output format so that it can be restored when we finish -- this + ** is a library-wide global, and has to be set/restored for each + ** session. + */ + old_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT); + + if (py_netsnmp_attr_long(session, "UseLongNames")) { + getlabel_flag |= USE_LONG_NAMES; + + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, + NETSNMP_OID_OUTPUT_FULL); + } + + /* Setting UseNumeric forces UseLongNames on so check for UseNumeric + after UseLongNames (above) to make sure the final outcome of + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT is NETSNMP_OID_OUTPUT_NUMERIC */ + if (py_netsnmp_attr_long(session, "UseNumeric")) { + getlabel_flag |= USE_LONG_NAMES; + getlabel_flag |= USE_NUMERIC_OIDS; + + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, + NETSNMP_OID_OUTPUT_NUMERIC); + } + + /* delete the existing varbinds that we'll replace */ + PySequence_DelSlice(varbinds, 0, PySequence_Length(varbinds)); + + if (PyErr_Occurred()) { + /* propagate error */ + if (verbose) + printf("error: walk: deleting old varbinds failed\n"); + snmp_free_pdu(pdu); + goto done; + } + + /* save the starting OID */ + + for(vars = pdu->variables, varlist_ind = 0; + vars != NULL; + vars = vars->next_variable, varlist_ind++) { + + oid_arr_broken_check[varlist_ind] = calloc(MAX_OID_LEN, sizeof(oid)); + + oid_arr_broken_check_len[varlist_ind] = vars->name_length; + memcpy(oid_arr_broken_check[varlist_ind], + vars->name, vars->name_length * sizeof(oid)); + } + + while(notdone) { + + status = __send_sync_pdu(ss, pdu, &response, retry_nosuch, + err_str, &err_num, &err_ind); + __py_netsnmp_update_session_errors(session, err_str, err_num, err_ind); + + if (!response || !response->variables || + status != STAT_SUCCESS || + response->errstat != SNMP_ERR_NOERROR) { + notdone = 0; + } else { + newpdu = snmp_pdu_create(SNMP_MSG_GETNEXT); + + for(vars = (response ? response->variables : NULL), + varlist_ind = 0, + oldvars = (pdu ? pdu->variables : NULL); + vars && (varlist_ind < varlist_len); + vars = vars->next_variable, varlist_ind++, + oldvars = (oldvars ? oldvars->next_variable : NULL)) { + + if ((vars->name_length < oid_arr_len[varlist_ind]) || + (memcmp(oid_arr[varlist_ind], vars->name, + oid_arr_len[varlist_ind] * sizeof(oid)) != 0)) { + notdone = 0; + break; + } + + if ((vars->type == SNMP_ENDOFMIBVIEW) || + (vars->type == SNMP_NOSUCHOBJECT) || + (vars->type == SNMP_NOSUCHINSTANCE)) { + notdone = 0; + break; + } + + if (snmp_oid_compare(vars->name, vars->name_length, + oid_arr_broken_check[varlist_ind], + oid_arr_broken_check_len[varlist_ind]) <= 0) { + /* The agent responded with an illegal response + as the returning OID was lexogragically less + then or equal to the requested OID... + We need to give up here because an infite + loop will result otherwise. + + XXX: this really should be an option to + continue like the -Cc option to the snmpwalk + application. + */ + notdone = 0; + break; + } + + varbind = py_netsnmp_construct_varbind(); + + if (PyObject_HasAttrString(varbind, "tag")) { + if (str_buf == NULL) { + str_buf = (u_char *) netsnmp_malloc(STR_BUF_SIZE); + str_buf_len = STR_BUF_SIZE; + } + str_buf[0] = '.'; + str_buf[1] = '\0'; + out_len = 0; + tp = netsnmp_sprint_realloc_objid_tree(&str_buf, &str_buf_len, + &out_len, 1, &buf_over, + vars->name,vars->name_length); + + if (__is_leaf(tp)) { + type = (tp->type ? tp->type : tp->parent->type); + getlabel_flag &= ~NON_LEAF_NAME; + } else { + getlabel_flag |= NON_LEAF_NAME; + type = __translate_asn_type(vars->type); + } + + __get_label_iid((char *) str_buf, &tag, &iid, getlabel_flag); + + if (_debug_level) printf("netsnmp_walk: filling response: %s:%s\n", tag, iid); + + py_netsnmp_attr_set_string(varbind, "tag", tag, STRLEN(tag)); + py_netsnmp_attr_set_string(varbind, "iid", iid, STRLEN(iid)); + + __get_type_str(type, type_str); + + py_netsnmp_attr_set_string(varbind, "type", type_str, + strlen(type_str)); + + len = __snprint_value((char **)&str_buf, &str_buf_len, + vars, tp, type, sprintval_flag); + str_buf[len] = '\0'; + + py_netsnmp_attr_set_string(varbind, "val", (char *) str_buf, + len); + + /* push the varbind onto the return varbinds */ + PyList_Append(varbinds, varbind); + + /* save in return tuple as well */ + /* save in return tuple as well - steals ref */ + _PyTuple_Resize(&val_tuple, result_count+1); + PyTuple_SetItem(val_tuple, result_count++, + Py_BuildValue("s#", str_buf, len)); + } else { + /* Return None for this variable. */ + _PyTuple_Resize(&val_tuple, result_count+1); + PyTuple_SetItem(val_tuple, result_count++, Py_BuildValue("")); + printf("netsnmp_walk: bad varbind (%d)\n", varlist_ind); + } + Py_XDECREF(varbind); + + memcpy(oid_arr_broken_check[varlist_ind], vars->name, + sizeof(oid) * vars->name_length); + oid_arr_broken_check_len[varlist_ind] = vars->name_length; + + snmp_add_null_var(newpdu, vars->name, + vars->name_length); + } + pdu = newpdu; + } + if (response) + snmp_free_pdu(response); + } + + /* Reset the library's behavior for numeric/symbolic OID's. */ + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, + old_format); + + + if (PyErr_Occurred()) { + /* propagate error */ + if (verbose) + printf("error: walk response processing: unknown python error"); + Py_DECREF(val_tuple); + } + } + + done: + Py_XDECREF(varbinds); + free(oid_arr_len); + free(oid_arr_broken_check_len); + for(varlist_ind = 0; varlist_ind < varlist_len; varlist_ind ++) { + free(oid_arr[varlist_ind]); + free(oid_arr_broken_check[varlist_ind]); + } + free(oid_arr); + free(oid_arr_broken_check); + if (str_buf != NULL) netsnmp_free(str_buf); + return (val_tuple ? val_tuple : Py_BuildValue("")); +} + + +static PyObject * +netsnmp_getbulk(PyObject *self, PyObject *args) +{ + int nonrepeaters; + int maxrepetitions; + PyObject *session; + PyObject *varlist; + PyObject *varbinds; + PyObject *varbind; + PyObject *varbinds_iter; + PyObject *val_tuple = NULL; + int varbind_ind; + netsnmp_session *ss; + netsnmp_pdu *pdu, *response; + netsnmp_variable_list *vars; + struct tree *tp; + int len; + oid *oid_arr; + int oid_arr_len = MAX_OID_LEN; + int type; + char type_str[MAX_TYPE_NAME_LEN]; + u_char *str_buf = NULL; + size_t str_buf_len = 0; + size_t out_len = 0; + int buf_over = 0; + char *tag; + char *iid; + int getlabel_flag = NO_FLAGS; + int sprintval_flag = USE_BASIC; + int verbose = py_netsnmp_verbose(); + int old_format; + int best_guess; + int retry_nosuch; + int err_ind; + int err_num; + char err_str[STR_BUF_SIZE]; + char *tmpstr; + Py_ssize_t tmplen; + + oid_arr = calloc(MAX_OID_LEN, sizeof(oid)); + + if (oid_arr && args) { + + if (!PyArg_ParseTuple(args, "OiiO", &session, &nonrepeaters, + &maxrepetitions, &varlist)) { + goto done; + } + + if (varlist && (varbinds = PyObject_GetAttrString(varlist, "varbinds"))) { + + ss = (SnmpSession *)py_netsnmp_attr_void_ptr(session, "sess_ptr"); + + if (py_netsnmp_attr_string(session, "ErrorStr", &tmpstr, &tmplen) < 0) { + goto done; + } + memcpy(&err_str, tmpstr, tmplen); + err_num = py_netsnmp_attr_long(session, "ErrorNum"); + err_ind = py_netsnmp_attr_long(session, "ErrorInd"); + + if (py_netsnmp_attr_long(session, "UseLongNames")) + getlabel_flag |= USE_LONG_NAMES; + if (py_netsnmp_attr_long(session, "UseNumeric")) + getlabel_flag |= USE_NUMERIC_OIDS; + if (py_netsnmp_attr_long(session, "UseEnums")) + sprintval_flag = USE_ENUMS; + if (py_netsnmp_attr_long(session, "UseSprintValue")) + sprintval_flag = USE_SPRINT_VALUE; + best_guess = py_netsnmp_attr_long(session, "BestGuess"); + retry_nosuch = py_netsnmp_attr_long(session, "RetryNoSuch"); + + pdu = snmp_pdu_create(SNMP_MSG_GETBULK); + + pdu->errstat = nonrepeaters; + pdu->errindex = maxrepetitions; + + varbinds_iter = PyObject_GetIter(varbinds); + + while (varbinds_iter && (varbind = PyIter_Next(varbinds_iter))) { + if (py_netsnmp_attr_string(varbind, "tag", &tag, NULL) < 0 || + py_netsnmp_attr_string(varbind, "iid", &iid, NULL) < 0) + { + oid_arr_len = 0; + } else { + tp = __tag2oid(tag, iid, oid_arr, &oid_arr_len, NULL, best_guess); + } + + if (oid_arr_len) { + snmp_add_null_var(pdu, oid_arr, oid_arr_len); + } else { + if (verbose) + printf("error: get: unknown object ID (%s)", + (tag ? tag : "")); + snmp_free_pdu(pdu); + Py_DECREF(varbind); + goto done; + } + /* release reference when done */ + Py_DECREF(varbind); + } + + Py_DECREF(varbinds_iter); + + if (PyErr_Occurred()) { + /* propagate error */ + if (verbose) + printf("error: get: unknown python error"); + snmp_free_pdu(pdu); + goto done; + } + + __send_sync_pdu(ss, pdu, &response, retry_nosuch, err_str, &err_num, + &err_ind); + __py_netsnmp_update_session_errors(session, err_str, err_num, err_ind); + + /* + ** Set up for numeric or full OID's, if necessary. Save the old + ** output format so that it can be restored when we finish -- this + ** is a library-wide global, and has to be set/restored for each + ** session. + */ + old_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT); + + if (py_netsnmp_attr_long(session, "UseLongNames")) { + getlabel_flag |= USE_LONG_NAMES; + + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, + NETSNMP_OID_OUTPUT_FULL); + } + /* Setting UseNumeric forces UseLongNames on so check for UseNumeric + after UseLongNames (above) to make sure the final outcome of + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT is NETSNMP_OID_OUTPUT_NUMERIC */ + if (py_netsnmp_attr_long(session, "UseNumeric")) { + getlabel_flag |= USE_LONG_NAMES; + getlabel_flag |= USE_NUMERIC_OIDS; + + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, + NETSNMP_OID_OUTPUT_NUMERIC); + } + + /* create tuple in which to return results */ + val_tuple = PyTuple_New(0); + + if(response && response->variables) { + /* clear varlist to receive response varbinds*/ + PySequence_DelSlice(varbinds, 0, PySequence_Length(varbinds)); + + if (PyErr_Occurred()) { + /* propagate error */ + if (verbose) + printf("error: bulk: deleting old varbinds failed\n"); + snmp_free_pdu(pdu); + goto done; + } + + for(vars = response->variables, varbind_ind=0; + vars; + vars = vars->next_variable, varbind_ind++) { + + varbind = py_netsnmp_construct_varbind(); + + if (PyObject_HasAttrString(varbind, "tag")) { + if (str_buf == NULL) { + str_buf = (u_char *) netsnmp_malloc(STR_BUF_SIZE); + str_buf_len = STR_BUF_SIZE; + } + *str_buf = '.'; + *(str_buf+1) = '\0'; + out_len = 0; + buf_over = 0; + tp = netsnmp_sprint_realloc_objid_tree(&str_buf, &str_buf_len, + &out_len, 1, &buf_over, + vars->name,vars->name_length); + if (__is_leaf(tp)) { + type = (tp->type ? tp->type : tp->parent->type); + getlabel_flag &= ~NON_LEAF_NAME; + } else { + getlabel_flag |= NON_LEAF_NAME; + type = __translate_asn_type(vars->type); + } + + __get_label_iid((char *) str_buf, &tag, &iid, getlabel_flag); + + py_netsnmp_attr_set_string(varbind, "tag", tag, STRLEN(tag)); + py_netsnmp_attr_set_string(varbind, "iid", iid, STRLEN(iid)); + + __get_type_str(type, type_str); + + py_netsnmp_attr_set_string(varbind, "type", type_str, + strlen(type_str)); + + len = __snprint_value((char **)&str_buf, &str_buf_len, + vars, tp, type, sprintval_flag); + str_buf[len] = '\0'; + + py_netsnmp_attr_set_string(varbind, "val", (char *) str_buf, len); + + /* push varbind onto varbinds */ + PyList_Append(varbinds, varbind); + + /* save in return tuple as well - steals ref */ + _PyTuple_Resize(&val_tuple, varbind_ind+1); + PyTuple_SetItem(val_tuple, varbind_ind, + Py_BuildValue("s#", str_buf, len)); + + Py_DECREF(varbind); + + } else { + PyObject *none = Py_BuildValue(""); /* new ref */ + /* not sure why making vabind failed - should not happen*/ + PyList_Append(varbinds, none); /* increments ref */ + /* Return None for this variable. */ + PyTuple_SetItem(val_tuple, varbind_ind, none); /* steals ref */ + Py_XDECREF(varbind); + } + } + } + + /* Reset the library's behavior for numeric/symbolic OID's. */ + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, + old_format); + + if (response) snmp_free_pdu(response); + + Py_DECREF(varbinds); + + } + + if (PyErr_Occurred()) { + /* propagate error */ + if (verbose) + printf("error: getbulk response processing: unknown python error"); + if (val_tuple) + Py_DECREF(val_tuple); + val_tuple = NULL; + } + } + + done: + free(oid_arr); + if (str_buf != NULL) netsnmp_free(str_buf); + return (val_tuple ? val_tuple : Py_BuildValue("")); +} + +static PyObject * +netsnmp_set(PyObject *self, PyObject *args) +{ + PyObject *ret = NULL; +#ifndef NETSNMP_NO_WRITE_SUPPORT + PyObject *session; + PyObject *varlist; + PyObject *varbind; + netsnmp_session *ss; + netsnmp_pdu *pdu, *response; + struct tree *tp; + char *tag; + char *iid; + char *val; + char *type_str; + int len; + oid *oid_arr; + int oid_arr_len = MAX_OID_LEN; + int type; + u_char tmp_val_str[STR_BUF_SIZE]; + int use_enums; + struct enum_list *ep; + int verbose = py_netsnmp_verbose(); + int best_guess; + int status; + int err_ind; + int err_num; + char err_str[STR_BUF_SIZE]; + char *tmpstr; + Py_ssize_t tmplen; + + oid_arr = calloc(MAX_OID_LEN, sizeof(oid)); + + if (oid_arr && args) { + + if (!PyArg_ParseTuple(args, "OO", &session, &varlist)) { + goto done; + } + + ss = (SnmpSession *)py_netsnmp_attr_void_ptr(session, "sess_ptr"); + + /* PyObject_SetAttrString(); */ + if (py_netsnmp_attr_string(session, "ErrorStr", &tmpstr, &tmplen) < 0) { + goto done; + } + + use_enums = py_netsnmp_attr_long(session, "UseEnums"); + + best_guess = py_netsnmp_attr_long(session, "BestGuess"); + + pdu = snmp_pdu_create(SNMP_MSG_SET); + + if (varlist) { + PyObject *varlist_iter = PyObject_GetIter(varlist); + + while (varlist_iter && (varbind = PyIter_Next(varlist_iter))) { + if (py_netsnmp_attr_string(varbind, "tag", &tag, NULL) < 0 || + py_netsnmp_attr_string(varbind, "iid", &iid, NULL) < 0) + { + oid_arr_len = 0; + } else { + tp = __tag2oid(tag, iid, oid_arr, &oid_arr_len, &type, best_guess); + } + + if (oid_arr_len==0) { + if (verbose) + printf("error: set: unknown object ID (%s)", + (tag?tag:"")); + snmp_free_pdu(pdu); + goto done; + } + + if (type == TYPE_UNKNOWN) { + if (py_netsnmp_attr_string(varbind, "type", &type_str, NULL) < 0) { + snmp_free_pdu(pdu); + goto done; + } + type = __translate_appl_type(type_str); + if (type == TYPE_UNKNOWN) { + if (verbose) + printf("error: set: no type found for object"); + snmp_free_pdu(pdu); + goto done; + } + } + + if (py_netsnmp_attr_string(varbind, "val", &val, &tmplen) < 0) { + snmp_free_pdu(pdu); + goto done; + } + memset(tmp_val_str, 0, sizeof(tmp_val_str)); + if ( tmplen >= sizeof(tmp_val_str)) { + tmplen = sizeof(tmp_val_str)-1; + } + memcpy(tmp_val_str, val, tmplen); + if (type==TYPE_INTEGER && use_enums && tp && tp->enums) { + for(ep = tp->enums; ep; ep = ep->next) { + if (val && !strcmp(ep->label, val)) { + snprintf((char *) tmp_val_str, sizeof(tmp_val_str), "%d", + ep->value); + break; + } + } + } + len = (int)tmplen; + status = __add_var_val_str(pdu, oid_arr, oid_arr_len, + (char *) tmp_val_str, len, type); + + if (verbose && status == FAILURE) + printf("error: set: adding variable/value to PDU"); + + /* release reference when done */ + Py_DECREF(varbind); + } + + Py_DECREF(varlist_iter); + + if (PyErr_Occurred()) { + /* propagate error */ + if (verbose) + printf("error: set: unknown python error"); + snmp_free_pdu(pdu); + goto done; + } + } + + status = __send_sync_pdu(ss, pdu, &response, NO_RETRY_NOSUCH, + err_str, &err_num, &err_ind); + __py_netsnmp_update_session_errors(session, err_str, err_num, err_ind); + + if (response) snmp_free_pdu(response); + + if (status == STAT_SUCCESS) + ret = Py_BuildValue("i",1); /* success, return True */ + else + ret = Py_BuildValue("i",0); /* fail, return False */ + } + done: + Py_XDECREF(varbind); + free(oid_arr); +#endif /* NETSNMP_NO_WRITE_SUPPORT */ + return (ret ? ret : Py_BuildValue("")); +} + + +static PyMethodDef ClientMethods[] = { + {"session", netsnmp_create_session, METH_VARARGS, + "create a netsnmp session."}, + {"session_v3", netsnmp_create_session_v3, METH_VARARGS, + "create a netsnmp session."}, + {"session_tunneled", netsnmp_create_session_tunneled, METH_VARARGS, + "create a tunneled netsnmp session over tls, dtls or ssh."}, + {"delete_session", netsnmp_delete_session, METH_VARARGS, + "create a netsnmp session."}, + {"get", netsnmp_get, METH_VARARGS, + "perform an SNMP GET operation."}, + {"getnext", netsnmp_getnext, METH_VARARGS, + "perform an SNMP GETNEXT operation."}, + {"getbulk", netsnmp_getbulk, METH_VARARGS, + "perform an SNMP GETBULK operation."}, + {"set", netsnmp_set, METH_VARARGS, + "perform an SNMP SET operation."}, + {"walk", netsnmp_walk, METH_VARARGS, + "perform an SNMP WALK operation."}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +PyMODINIT_FUNC +initclient_intf(void) +{ + (void) Py_InitModule("client_intf", ClientMethods); +} + + + + + diff --git a/python/netsnmp/netsnmp-feature-definitions.h b/python/netsnmp/netsnmp-feature-definitions.h new file mode 100644 index 0000000..1f5a7f4 --- /dev/null +++ b/python/netsnmp/netsnmp-feature-definitions.h @@ -0,0 +1,7 @@ +#include +#include + +netsnmp_feature_require(enable_stderrlog) +netsnmp_feature_require(memory_malloc) +netsnmp_feature_require(memory_free) + diff --git a/python/netsnmp/tests/__init__.py b/python/netsnmp/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/python/netsnmp/tests/__init__.py diff --git a/python/netsnmp/tests/snmpd.conf b/python/netsnmp/tests/snmpd.conf new file mode 100644 index 0000000..b62e4c7 --- /dev/null +++ b/python/netsnmp/tests/snmpd.conf @@ -0,0 +1,425 @@ +############################################################################### +# +# EXAMPLE.conf: +# An example configuration file for configuring the ucd-snmp snmpd agent. +# +############################################################################### +# +# This file is intended to only be an example. If, however, you want +# to use it, it should be placed in /usr/local/etc/snmp/snmpd.conf. +# When the snmpd agent starts up, this is where it will look for it. +# +# You might be interested in generating your own snmpd.conf file using +# the "snmpconf" program (perl script) instead. It's a nice menu +# based interface to writing well commented configuration files. Try it! +# +# Note: This file is automatically generated from EXAMPLE.conf.def. +# Do NOT read the EXAMPLE.conf.def file! Instead, after you have run +# configure & make, and then make sure you read the EXAMPLE.conf file +# instead, as it will tailor itself to your configuration. + +# All lines beginning with a '#' are comments and are intended for you +# to read. All other lines are configuration commands for the agent. + +# +# PLEASE: read the snmpd.conf(5) manual page as well! +# + + +############################################################################### +# Access Control +############################################################################### + +# YOU SHOULD CHANGE THE "COMMUNITY" TOKEN BELOW TO A NEW KEYWORD ONLY +# KNOWN AT YOUR SITE. YOU *MUST* CHANGE THE NETWORK TOKEN BELOW TO +# SOMETHING REFLECTING YOUR LOCAL NETWORK ADDRESS SPACE. + +# By far, the most common question I get about the agent is "why won't +# it work?", when really it should be "how do I configure the agent to +# allow me to access it?" +# +# By default, the agent responds to the "public" community for read +# only access, if run out of the box without any configuration file in +# place. The following examples show you other ways of configuring +# the agent so that you can change the community names, and give +# yourself write access as well. +# +# The following lines change the access permissions of the agent so +# that the COMMUNITY string provides read-only access to your entire +# NETWORK (EG: 10.10.10.0/24), and read/write access to only the +# localhost (127.0.0.1, not its real ipaddress). +# +# For more information, read the FAQ as well as the snmpd.conf(5) +# manual page. + +#### +# First, map the community name (COMMUNITY) into a security name +# (local and mynetwork, depending on where the request is coming +# from): + +# sec.name source community +com2sec local localhost public +com2sec mynetwork 192.168.1.0/24 public + +#### +# Second, map the security names into group names: + +# sec.model sec.name +group MyRWGroup v1 local +group MyRWGroup v2c local +group MyRWGroup usm local +group MyRWGroup usm initial +group MyROGroup v1 mynetwork +group MyROGroup v2c mynetwork +group MyROGroup usm mynetwork + +#### +# Third, create a view for us to let the groups have rights to: + +# incl/excl subtree mask +view all included .1 80 + +#### +# Finally, grant the 2 groups access to the 1 view with different +# write permissions: + +# context sec.model sec.level match read write notif +access MyROGroup "" any noauth exact all none none +access MyRWGroup "" any noauth exact all all none + +# ----------------------------------------------------------------------------- + +rwuser initial priv +createUser initial MD5 auth_pass DES priv_pass + +############################################################################### +# System contact information +# + +# It is also possible to set the sysContact and sysLocation system +# variables through the snmpd.conf file. **PLEASE NOTE** that setting +# the value of these objects here makes these objects READ-ONLY +# (regardless of any access control settings). Any attempt to set the +# value of an object whose value is given here will fail with an error +# status of notWritable. + +# syslocation Right here, right now. +syscontact G. S. Marzot + +# Example output of snmpwalk: +# % snmpwalk -v 1 -c public localhost system +# system.sysDescr.0 = "SunOS name sun4c" +# system.sysObjectID.0 = OID: enterprises.ucdavis.ucdSnmpAgent.sunos4 +# system.sysUpTime.0 = Timeticks: (595637548) 68 days, 22:32:55 +# system.sysContact.0 = "Me " +# system.sysName.0 = "name" +# system.sysLocation.0 = "Right here, right now." +# system.sysServices.0 = 72 + + +# ----------------------------------------------------------------------------- + + +############################################################################### +# Process checks. +# +# The following are examples of how to use the agent to check for +# processes running on the host. The syntax looks something like: +# +# proc NAME [MAX=0] [MIN=0] +# +# NAME: the name of the process to check for. It must match +# exactly (ie, http will not find httpd processes). +# MAX: the maximum number allowed to be running. Defaults to 0. +# MIN: the minimum number to be running. Defaults to 0. + +# +# Examples: +# + +# Make sure mountd is running +proc mountd + +# Make sure there are no more than 4 ntalkds running, but 0 is ok too. +proc ntalkd 4 + +# Make sure at least one sendmail, but less than or equal to 10 are running. +proc sendmail 10 1 + +# A snmpwalk of the prTable would look something like this: +# +# % snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.2 +# enterprises.ucdavis.procTable.prEntry.prIndex.1 = 1 +# enterprises.ucdavis.procTable.prEntry.prIndex.2 = 2 +# enterprises.ucdavis.procTable.prEntry.prIndex.3 = 3 +# enterprises.ucdavis.procTable.prEntry.prNames.1 = "mountd" +# enterprises.ucdavis.procTable.prEntry.prNames.2 = "ntalkd" +# enterprises.ucdavis.procTable.prEntry.prNames.3 = "sendmail" +# enterprises.ucdavis.procTable.prEntry.prMin.1 = 0 +# enterprises.ucdavis.procTable.prEntry.prMin.2 = 0 +# enterprises.ucdavis.procTable.prEntry.prMin.3 = 1 +# enterprises.ucdavis.procTable.prEntry.prMax.1 = 0 +# enterprises.ucdavis.procTable.prEntry.prMax.2 = 4 +# enterprises.ucdavis.procTable.prEntry.prMax.3 = 10 +# enterprises.ucdavis.procTable.prEntry.prCount.1 = 0 +# enterprises.ucdavis.procTable.prEntry.prCount.2 = 0 +# enterprises.ucdavis.procTable.prEntry.prCount.3 = 1 +# enterprises.ucdavis.procTable.prEntry.prErrorFlag.1 = 1 +# enterprises.ucdavis.procTable.prEntry.prErrorFlag.2 = 0 +# enterprises.ucdavis.procTable.prEntry.prErrorFlag.3 = 0 +# enterprises.ucdavis.procTable.prEntry.prErrMessage.1 = "No mountd process running." +# enterprises.ucdavis.procTable.prEntry.prErrMessage.2 = "" +# enterprises.ucdavis.procTable.prEntry.prErrMessage.3 = "" +# enterprises.ucdavis.procTable.prEntry.prErrFix.1 = 0 +# enterprises.ucdavis.procTable.prEntry.prErrFix.2 = 0 +# enterprises.ucdavis.procTable.prEntry.prErrFix.3 = 0 +# +# Note that the errorFlag for mountd is set to 1 because one is not +# running (in this case an rpc.mountd is, but thats not good enough), +# and the ErrMessage tells you what's wrong. The configuration +# imposed in the snmpd.conf file is also shown. +# +# Special Case: When the min and max numbers are both 0, it assumes +# you want a max of infinity and a min of 1. +# + + +# ----------------------------------------------------------------------------- + + +############################################################################### +# Executables/scripts +# + +# +# You can also have programs run by the agent that return a single +# line of output and an exit code. Here are two examples. +# +# exec NAME PROGRAM [ARGS ...] +# +# NAME: A generic name. +# PROGRAM: The program to run. Include the path! +# ARGS: optional arguments to be passed to the program + +# a simple hello world +exec echotest /bin/echo hello world + +# Run a shell script containing: +# +# #!/bin/sh +# echo hello world +# echo hi there +# exit 35 +# +# Note: this has been specifically commented out to prevent +# accidental security holes due to someone else on your system writing +# a /tmp/shtest before you do. Uncomment to use it. +# +#exec shelltest /bin/sh /tmp/shtest + +# Then, +# % snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.8 +# enterprises.ucdavis.extTable.extEntry.extIndex.1 = 1 +# enterprises.ucdavis.extTable.extEntry.extIndex.2 = 2 +# enterprises.ucdavis.extTable.extEntry.extNames.1 = "echotest" +# enterprises.ucdavis.extTable.extEntry.extNames.2 = "shelltest" +# enterprises.ucdavis.extTable.extEntry.extCommand.1 = "/bin/echo hello world" +# enterprises.ucdavis.extTable.extEntry.extCommand.2 = "/bin/sh /tmp/shtest" +# enterprises.ucdavis.extTable.extEntry.extResult.1 = 0 +# enterprises.ucdavis.extTable.extEntry.extResult.2 = 35 +# enterprises.ucdavis.extTable.extEntry.extOutput.1 = "hello world." +# enterprises.ucdavis.extTable.extEntry.extOutput.2 = "hello world." +# enterprises.ucdavis.extTable.extEntry.extErrFix.1 = 0 +# enterprises.ucdavis.extTable.extEntry.extErrFix.2 = 0 + +# Note that the second line of the /tmp/shtest shell script is cut +# off. Also note that the exit status of 35 was returned. + +# ----------------------------------------------------------------------------- + + +############################################################################### +# disk checks +# + +# The agent can check the amount of available disk space, and make +# sure it is above a set limit. + +# disk PATH [MIN=DEFDISKMINIMUMSPACE] +# +# PATH: mount path to the disk in question. +# MIN: Disks with space below this value will have the Mib's errorFlag set. +# Default value = DEFDISKMINIMUMSPACE. + +# Check the / partition and make sure it contains at least 10 megs. + +disk / 10000 + +# % snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.9 +# enterprises.ucdavis.diskTable.dskEntry.diskIndex.1 = 0 +# enterprises.ucdavis.diskTable.dskEntry.diskPath.1 = "/" Hex: 2F +# enterprises.ucdavis.diskTable.dskEntry.diskDevice.1 = "/dev/dsk/c201d6s0" +# enterprises.ucdavis.diskTable.dskEntry.diskMinimum.1 = 10000 +# enterprises.ucdavis.diskTable.dskEntry.diskTotal.1 = 837130 +# enterprises.ucdavis.diskTable.dskEntry.diskAvail.1 = 316325 +# enterprises.ucdavis.diskTable.dskEntry.diskUsed.1 = 437092 +# enterprises.ucdavis.diskTable.dskEntry.diskPercent.1 = 58 +# enterprises.ucdavis.diskTable.dskEntry.diskErrorFlag.1 = 0 +# enterprises.ucdavis.diskTable.dskEntry.diskErrorMsg.1 = "" + +# ----------------------------------------------------------------------------- + + +############################################################################### +# load average checks +# + +# load [1MAX=DEFMAXLOADAVE] [5MAX=DEFMAXLOADAVE] [15MAX=DEFMAXLOADAVE] +# +# 1MAX: If the 1 minute load average is above this limit at query +# time, the errorFlag will be set. +# 5MAX: Similar, but for 5 min average. +# 15MAX: Similar, but for 15 min average. + +# Check for loads: +load 12 14 14 + +# % snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.10 +# enterprises.ucdavis.loadTable.laEntry.loadaveIndex.1 = 1 +# enterprises.ucdavis.loadTable.laEntry.loadaveIndex.2 = 2 +# enterprises.ucdavis.loadTable.laEntry.loadaveIndex.3 = 3 +# enterprises.ucdavis.loadTable.laEntry.loadaveNames.1 = "Load-1" +# enterprises.ucdavis.loadTable.laEntry.loadaveNames.2 = "Load-5" +# enterprises.ucdavis.loadTable.laEntry.loadaveNames.3 = "Load-15" +# enterprises.ucdavis.loadTable.laEntry.loadaveLoad.1 = "0.49" Hex: 30 2E 34 39 +# enterprises.ucdavis.loadTable.laEntry.loadaveLoad.2 = "0.31" Hex: 30 2E 33 31 +# enterprises.ucdavis.loadTable.laEntry.loadaveLoad.3 = "0.26" Hex: 30 2E 32 36 +# enterprises.ucdavis.loadTable.laEntry.loadaveConfig.1 = "12.00" +# enterprises.ucdavis.loadTable.laEntry.loadaveConfig.2 = "14.00" +# enterprises.ucdavis.loadTable.laEntry.loadaveConfig.3 = "14.00" +# enterprises.ucdavis.loadTable.laEntry.loadaveErrorFlag.1 = 0 +# enterprises.ucdavis.loadTable.laEntry.loadaveErrorFlag.2 = 0 +# enterprises.ucdavis.loadTable.laEntry.loadaveErrorFlag.3 = 0 +# enterprises.ucdavis.loadTable.laEntry.loadaveErrMessage.1 = "" +# enterprises.ucdavis.loadTable.laEntry.loadaveErrMessage.2 = "" +# enterprises.ucdavis.loadTable.laEntry.loadaveErrMessage.3 = "" + +# ----------------------------------------------------------------------------- + + +############################################################################### +# Extensible sections. +# + +# This alleviates the multiple line output problem found in the +# previous executable mib by placing each mib in its own mib table: + +# Run a shell script containing: +# +# #!/bin/sh +# echo hello world +# echo hi there +# exit 35 +# +# Note: this has been specifically commented out to prevent +# accidental security holes due to someone else on your system writing +# a /tmp/shtest before you do. Uncomment to use it. +# +# exec .1.3.6.1.4.1.2021.50 shelltest /bin/sh /tmp/shtest + +# % snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.50 +# enterprises.ucdavis.50.1.1 = 1 +# enterprises.ucdavis.50.2.1 = "shelltest" +# enterprises.ucdavis.50.3.1 = "/bin/sh /tmp/shtest" +# enterprises.ucdavis.50.100.1 = 35 +# enterprises.ucdavis.50.101.1 = "hello world." +# enterprises.ucdavis.50.101.2 = "hi there." +# enterprises.ucdavis.50.102.1 = 0 + +# Now the Output has grown to two lines, and we can see the 'hi +# there.' output as the second line from our shell script. +# +# Note that you must alter the mib.txt file to be correct if you want +# the .50.* outputs above to change to reasonable text descriptions. + +# Other ideas: +# +# exec .1.3.6.1.4.1.2021.51 ps /bin/ps +# exec .1.3.6.1.4.1.2021.52 top /usr/local/bin/top +# exec .1.3.6.1.4.1.2021.53 mailq /usr/bin/mailq + +# ----------------------------------------------------------------------------- + + +############################################################################### +# Pass through control. +# + +# Usage: +# pass MIBOID EXEC-COMMAND +# +# This will pass total control of the mib underneath the MIBOID +# portion of the mib to the EXEC-COMMAND. +# +# Note: You'll have to change the path of the passtest script to your +# source directory or install it in the given location. +# +# Example: (see the script for details) +# (commented out here since it requires that you place the +# script in the right location. (its not installed by default)) + +# pass .1.3.6.1.4.1.2021.255 /bin/sh PREFIX/local/passtest + +# % snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.255 +# enterprises.ucdavis.255.1 = "life the universe and everything" +# enterprises.ucdavis.255.2.1 = 42 +# enterprises.ucdavis.255.2.2 = OID: 42.42.42 +# enterprises.ucdavis.255.3 = Timeticks: (363136200) 42 days, 0:42:42 +# enterprises.ucdavis.255.4 = IpAddress: 127.0.0.1 +# enterprises.ucdavis.255.5 = 42 +# enterprises.ucdavis.255.6 = Gauge: 42 +# +# % snmpget -v 1 -c public localhost .1.3.6.1.4.1.2021.255.5 +# enterprises.ucdavis.255.5 = 42 +# +# % snmpset -v 1 -c public localhost .1.3.6.1.4.1.2021.255.1 s "New string" +# enterprises.ucdavis.255.1 = "New string" +# + +# For specific usage information, see the man/snmpd.conf.5 manual page +# as well as the local/passtest script used in the above example. + +############################################################################### +# Subagent control +# + +# The agent can support subagents using a number of extension mechanisms. +# From the 4.2.1 release, AgentX support is being compiled in by default. +# However, this is still experimental code, so should not be used on +# critical production systems. +# Please see the file README.agentx for more details. +# +# If having read, marked, learnt and inwardly digested this information, +# you decide that you do wish to make use of this mechanism, simply +# uncomment the following directive. +# +# master agentx +# +# I repeat - this is *NOT* regarded as suitable for front-line production +# systems, though it is probably stable enough for day-to-day use. +# Probably. +# +# No refunds will be given. + + +############################################################################### +# Further Information +# +# See the snmpd.conf manual page, and the output of "snmpd -H". +# MUCH more can be done with the snmpd.conf than is shown as an +# example here. + +certSecName 10 D020A78EAF99FCE276AA9F43063A69698E4F75D1 --rfc822 +rwuser -s tsm hardaker@wjh.hardakers.net + +trustCert D020A78EAF99FCE276AA9F43063A69698E4F75D1 diff --git a/python/netsnmp/tests/test.py b/python/netsnmp/tests/test.py new file mode 100644 index 0000000..c71812b --- /dev/null +++ b/python/netsnmp/tests/test.py @@ -0,0 +1,446 @@ +""" Runs all unit tests for the netsnmp package. """ +# Copyright (c) 2006 Andy Gross. See LICENSE.txt for details. + +import os +import unittest +import netsnmp + +def snmp_dest(**kwargs): + """Return information about how to communicate with snmpd""" + dest = { + 'Version': 1, + 'DestHost': 'localhost:' + os.environ.get("SNMP_SNMPD_PORT", 161), + 'Community': 'public', + } + for key, value in kwargs.iteritems(): + dest[key] = value + return dest + +def setup_v1(): + return netsnmp.Session(**snmp_dest()) + +def setup_v2(): + sess = netsnmp.Session(**snmp_dest(Version=2)) + sess.UseEnums = 1 + sess.UseLongNames = 1 + return sess + +def setup_v3(): + sess = netsnmp.Session(**snmp_dest(Version=3, + SecLevel='authPriv', + SecName='initial', + PrivPass='priv_pass', + AuthPass='auth_pass')) + sess.UseSprintValue = 1 + return sess + +class BasicTests(unittest.TestCase): + """Basic unit tests for the Net-SNMP Python interface""" + def test_varbind_creation(self): + var = netsnmp.Varbind('sysDescr.0') + self.assertEqual(var.tag, 'sysDescr') + self.assertEqual(var.iid, '0') + + var = netsnmp.Varbind('sysDescr', '0') + self.assertEqual(var.tag, 'sysDescr') + self.assertEqual(var.iid, '0') + + var = netsnmp.Varbind( + '.iso.org.dod.internet.mgmt.mib-2.system.sysDescr', '0') + self.assertEqual(var.tag, + '.iso.org.dod.internet.mgmt.mib-2.system.sysDescr') + self.assertEqual(var.iid, '0') + + var = netsnmp.Varbind( + '.iso.org.dod.internet.mgmt.mib-2.system.sysDescr.0') + self.assertEqual(var.tag, + '.iso.org.dod.internet.mgmt.mib-2.system.sysDescr.0') + self.assertEqual(var.iid, None) + + var = netsnmp.Varbind('.1.3.6.1.2.1.1.1.0') + self.assertEqual(var.tag, '.1.3.6.1.2.1.1.1.0') + self.assertEqual(var.iid, '') + + def test_v1_get(self): + print "\n" + print "---v1 GET tests -------------------------------------\n" + var = netsnmp.Varbind('.1.3.6.1.2.1.1.1', '0') + res = netsnmp.snmpget(var, **snmp_dest()) + + print "v1 snmpget result: ", res, "\n" + self.assertEqual(len(res), 1) + + print "v1 get var: ", var.tag, var.iid, "=", var.val, '(', var.type, ')' + self.assertEqual(var.tag, 'sysDescr') + self.assertEqual(var.iid, '0') + self.assertEqual(var.val, res[0]) + self.assertEqual(var.type, 'OCTETSTR') + + def test_v1_getnext(self): + print "\n" + print "---v1 GETNEXT tests-------------------------------------\n" + var = netsnmp.Varbind('.1.3.6.1.2.1.1.1', '0') + res = netsnmp.snmpgetnext(var, **snmp_dest()) + + print "v1 snmpgetnext result: ", res, "\n" + self.assertEqual(len(res), 1) + + print "v1 getnext var: ", var.tag, var.iid, "=", var.val, '(', var.type, ')' + self.assertTrue(var.tag is not None) + self.assertTrue(var.iid is not None) + self.assertTrue(var.val is not None) + self.assertTrue(var.type is not None) + + def test_v1_set(self): + print "\n" + print "---v1 SET tests-------------------------------------\n" + var = netsnmp.Varbind('sysLocation', '0', 'my new location') + res = netsnmp.snmpset(var, **snmp_dest()) + + print "v1 snmpset result: ", res, "\n" + self.assertEqual(res, 1) + + print "v1 set var: ", var.tag, var.iid, "=", var.val, '(', var.type, ')' + self.assertEqual(var.tag, 'sysLocation') + self.assertEqual(var.iid, '0') + self.assertEqual(var.val, 'my new location') + self.assertTrue(var.type is None) + + def test_v1_walk(self): + print "\n" + print "---v1 walk tests-------------------------------------\n" + varlist = netsnmp.VarList(netsnmp.Varbind('system')) + + print "v1 varlist walk in: " + for var in varlist: + print " ", var.tag, var.iid, "=", var.val, '(', var.type, ')' + + res = netsnmp.snmpwalk(varlist, **snmp_dest()) + print "v1 snmpwalk result: ", res, "\n" + self.assertTrue(len(res) > 0) + + for var in varlist: + print var.tag, var.iid, "=", var.val, '(', var.type, ')' + + def test_v1_walk_2(self): + print "\n" + print "---v1 walk 2-------------------------------------\n" + + print "v1 varbind walk in: " + var = netsnmp.Varbind('system') + self.assertEqual(var.tag, 'system') + self.assertEqual(var.iid, '') + self.assertEqual(var.val, None) + self.assertEqual(var.type, None) + res = netsnmp.snmpwalk(var, **snmp_dest()) + print "v1 snmpwalk result (should be = orig): ", res, "\n" + self.assertTrue(len(res) > 0) + + print var.tag, var.iid, "=", var.val, '(', var.type, ')' + self.assertEqual(var.tag, 'system') + self.assertEqual(var.iid, '') + self.assertEqual(var.val, None) + self.assertEqual(var.type, None) + + def test_v1_mv_get(self): + print "\n" + print "---v1 multi-varbind test-------------------------------------\n" + sess = setup_v1() + + varlist = netsnmp.VarList(netsnmp.Varbind('sysUpTime', 0), + netsnmp.Varbind('sysContact', 0), + netsnmp.Varbind('sysLocation', 0)) + vals = sess.get(varlist) + print "v1 sess.get result: ", vals, "\n" + self.assertTrue(len(vals) > 0) + + for var in varlist: + print var.tag, var.iid, "=", var.val, '(', var.type, ')' + + vals = sess.getnext(varlist) + print "v1 sess.getnext result: ", vals, "\n" + self.assertTrue(len(vals) > 0) + + for var in varlist: + print var.tag, var.iid, "=", var.val, '(', var.type, ')' + + varlist = netsnmp.VarList(netsnmp.Varbind('sysUpTime'), + netsnmp.Varbind('sysORLastChange'), + netsnmp.Varbind('sysORID'), + netsnmp.Varbind('sysORDescr'), + netsnmp.Varbind('sysORUpTime')) + + vals = sess.getbulk(2, 8, varlist) + print "v1 sess.getbulk result: ", vals, "\n" + self.assertEqual(vals, None) # GetBulk is not supported for v1 + + for var in varlist: + print var.tag, var.iid, "=", var.val, '(', var.type, ')' + + def test_v1_set_2(self): + print "\n" + print "---v1 set2-------------------------------------\n" + + sess = setup_v1() + varlist = netsnmp.VarList( + netsnmp.Varbind('sysLocation', '0', 'my newer location')) + res = sess.set(varlist) + print "v1 sess.set result: ", res, "\n" + + def test_v1_walk_3(self): + print "\n" + print "---v1 walk3-------------------------------------\n" + + sess = setup_v1() + varlist = netsnmp.VarList(netsnmp.Varbind('system')) + + vals = sess.walk(varlist) + print "v1 sess.walk result: ", vals, "\n" + self.assertTrue(len(vals) > 0) + + for var in varlist: + print " ", var.tag, var.iid, "=", var.val, '(', var.type, ')' + + def test_v2c_get(self): + print "\n" + print "---v2c get-------------------------------------\n" + + sess = setup_v2() + varlist = netsnmp.VarList(netsnmp.Varbind('sysUpTime', 0), + netsnmp.Varbind('sysContact', 0), + netsnmp.Varbind('sysLocation', 0)) + vals = sess.get(varlist) + print "v2 sess.get result: ", vals, "\n" + self.assertEqual(len(vals), 3) + + def test_v2c_getnext(self): + print "\n" + print "---v2c getnext-------------------------------------\n" + + sess = setup_v2() + varlist = netsnmp.VarList(netsnmp.Varbind('sysUpTime', 0), + netsnmp.Varbind('sysContact', 0), + netsnmp.Varbind('sysLocation', 0)) + for var in varlist: + print var.tag, var.iid, "=", var.val, '(', var.type, ')' + print "\n" + + vals = sess.getnext(varlist) + print "v2 sess.getnext result: ", vals, "\n" + self.assertTrue(len(vals) > 0) + + for var in varlist: + print var.tag, var.iid, "=", var.val, '(', var.type, ')' + print "\n" + + def test_v2c_getbulk(self): + print "\n" + print "---v2c getbulk-------------------------------------\n" + + sess = setup_v2() + varlist = netsnmp.VarList(netsnmp.Varbind('sysUpTime'), + netsnmp.Varbind('sysORLastChange'), + netsnmp.Varbind('sysORID'), + netsnmp.Varbind('sysORDescr'), + netsnmp.Varbind('sysORUpTime')) + + vals = sess.getbulk(2, 8, varlist) + print "v2 sess.getbulk result: ", vals, "\n" + self.assertTrue(len(vals) > 0) + + for var in varlist: + print var.tag, var.iid, "=", var.val, '(', var.type, ')' + print "\n" + + def test_v2c_set(self): + print "\n" + print "---v2c set-------------------------------------\n" + + sess = setup_v2() + + varlist = netsnmp.VarList( + netsnmp.Varbind('sysLocation', '0', 'my even newer location')) + + res = sess.set(varlist) + print "v2 sess.set result: ", res, "\n" + self.assertEqual(res, 1) + + def test_v2c_walk(self): + print "\n" + print "---v2c walk-------------------------------------\n" + + sess = setup_v2() + + varlist = netsnmp.VarList(netsnmp.Varbind('system')) + + vals = sess.walk(varlist) + print "v2 sess.walk result: ", vals, "\n" + self.assertTrue(len(vals) > 0) + + for var in varlist: + print " ", var.tag, var.iid, "=", var.val, '(', var.type, ')' + + def test_v3_get(self): + print "\n" + sess = setup_v3(); + varlist = netsnmp.VarList(netsnmp.Varbind('sysUpTime', 0), + netsnmp.Varbind('sysContact', 0), + netsnmp.Varbind('sysLocation', 0)) + print "---v3 get-------------------------------------\n" + vals = sess.get(varlist) + print "v3 sess.get result: ", vals, "\n" + self.assertTrue(len(vals) > 0) + + for var in varlist: + print var.tag, var.iid, "=", var.val, '(', var.type, ')' + print "\n" + + def test_v3_getnext(self): + print "\n" + print "---v3 getnext-------------------------------------\n" + + sess = setup_v3(); + varlist = netsnmp.VarList(netsnmp.Varbind('sysUpTime', 0), + netsnmp.Varbind('sysContact', 0), + netsnmp.Varbind('sysLocation', 0)) + vals = sess.getnext(varlist) + print "v3 sess.getnext result: ", vals, "\n" + self.assertTrue(len(vals) > 0) + + for var in varlist: + print var.tag, var.iid, "=", var.val, '(', var.type, ')' + print "\n" + + def test_v3_getbulk(self): + sess = setup_v3(); + varlist = netsnmp.VarList(netsnmp.Varbind('sysUpTime'), + netsnmp.Varbind('sysORLastChange'), + netsnmp.Varbind('sysORID'), + netsnmp.Varbind('sysORDescr'), + netsnmp.Varbind('sysORUpTime')) + + vals = sess.getbulk(2, 8, varlist) + print "v3 sess.getbulk result: ", vals, "\n" + self.assertTrue(len(vals) > 0) + + for var in varlist: + print var.tag, var.iid, "=", var.val, '(', var.type, ')' + print "\n" + + def test_v3_set(self): + print "\n" + print "---v3 set-------------------------------------\n" + + sess = setup_v3(); + varlist = netsnmp.VarList( + netsnmp.Varbind('sysLocation', '0', 'my final destination')) + res = sess.set(varlist) + print "v3 sess.set result: ", res, "\n" + self.assertEqual(res, 1) + + def test_v3_walk(self): + print "\n" + print "---v3 walk-------------------------------------\n" + sess = setup_v3(); + varlist = netsnmp.VarList(netsnmp.Varbind('system')) + + vals = sess.walk(varlist) + print "v3 sess.walk result: ", vals, "\n" + self.assertTrue(len(vals) > 0) + + for var in varlist: + print " ", var.tag, var.iid, "=", var.val, '(', var.type, ')' + + +class SetTests(unittest.TestCase): + """SNMP set tests for the Net-SNMP Python interface""" + def testFuncs(self): + """Test code""" + print "\n-------------- SET Test Start ----------------------------\n" + + var = netsnmp.Varbind('sysUpTime', '0') + res = netsnmp.snmpget(var, **snmp_dest()) + print "uptime = ", res[0] + self.assertEqual(len(res), 1) + + + var = netsnmp.Varbind('versionRestartAgent', '0', 1) + res = netsnmp.snmpset(var, **snmp_dest()) + self.assertEqual(res, 1) + + var = netsnmp.Varbind('sysUpTime', '0') + res = netsnmp.snmpget(var, **snmp_dest()) + print "uptime = ", res[0] + self.assertEqual(len(res), 1) + + var = netsnmp.Varbind('nsCacheEntry') + res = netsnmp.snmpgetnext(var, **snmp_dest()) + print "var = ", var.tag, var.iid, "=", var.val, '(', var.type, ')' + self.assertEqual(len(res), 1) + + var.val = 65 + res = netsnmp.snmpset(var, **snmp_dest()) + self.assertEqual(res, 1) + res = netsnmp.snmpget(var, **snmp_dest()) + print "var = ", var.tag, var.iid, "=", var.val, '(', var.type, ')' + self.assertEqual(len(res), 1) + self.assertEqual(res[0], '65'); + + sess = setup_v1() + + varlist = netsnmp.VarList( + netsnmp.Varbind('.1.3.6.1.6.3.12.1.2.1.2.116.101.115.116', '', '.1.3.6.1.6.1.1'), + netsnmp.Varbind('.1.3.6.1.6.3.12.1.2.1.3.116.101.115.116', '', '1234'), + netsnmp.Varbind('.1.3.6.1.6.3.12.1.2.1.9.116.101.115.116', '', 4)) + res = sess.set(varlist) + + print "res = ", res + self.assertEqual(res, 1) + + varlist = netsnmp.VarList(netsnmp.Varbind('snmpTargetAddrTDomain'), + netsnmp.Varbind('snmpTargetAddrTAddress'), + netsnmp.Varbind('snmpTargetAddrRowStatus')) + + res = sess.getnext(varlist) + self.assertEqual(len(res), 3) + self.assertEqual(varlist[0].tag, 'snmpTargetAddrTDomain') + self.assertEqual(varlist[0].iid, '116.101.115.116') + self.assertEqual(varlist[0].val, '.1.3.6.1.6.1.1') + self.assertEqual(varlist[1].tag, 'snmpTargetAddrTAddress') + self.assertEqual(varlist[1].iid, '116.101.115.116') + self.assertEqual(varlist[1].val, '1234') + self.assertEqual(varlist[2].tag, 'snmpTargetAddrRowStatus') + self.assertEqual(varlist[2].iid, '116.101.115.116') + self.assertEqual(varlist[2].val, '3') + + for var in varlist: + print var.tag, var.iid, "=", var.val, '(', var.type, ')' + print "\n" + + varlist = netsnmp.VarList( + netsnmp.Varbind('.1.3.6.1.6.3.12.1.2.1.9.116.101.115.116', '', 6)) + + res = sess.set(varlist) + + print "res = ", res + self.assertEqual(res, 1) + + varlist = netsnmp.VarList(netsnmp.Varbind('snmpTargetAddrTDomain'), + netsnmp.Varbind('snmpTargetAddrTAddress'), + netsnmp.Varbind('snmpTargetAddrRowStatus')) + + res = sess.getnext(varlist) + self.assertEqual(len(res), 3) + self.assertNotEqual(varlist[0].tag, 'snmpTargetAddrTDomain') + self.assertNotEqual(varlist[1].tag, 'snmpTargetAddrTAddress') + self.assertNotEqual(varlist[2].tag, 'snmpTargetAddrRowStatus') + + for var in varlist: + print var.tag, var.iid, "=", var.val, '(', var.type, ')' + print "\n" + + print "\n-------------- SET Test End ----------------------------\n" + + +if __name__ == '__main__': + unittest.main() diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 0000000..a62aabf --- /dev/null +++ b/python/setup.py @@ -0,0 +1,46 @@ +from distutils.core import setup, Extension +from setuptools import setup, Extension, find_packages +import os +import re +import string +import sys + +intree=0 + +args = sys.argv[:] +for arg in args: + if string.find(arg,'--basedir=') == 0: + basedir = string.split(arg,'=')[1] + sys.argv.remove(arg) + intree=1 + +if intree: + netsnmp_libs = os.popen(basedir+'/net-snmp-config --libs').read() + libdir = os.popen(basedir+'/net-snmp-config --build-lib-dirs '+basedir).read() + incdir = os.popen(basedir+'/net-snmp-config --build-includes '+basedir).read() + " " + os.popen(basedir+'/net-snmp-config --base-cflags '+basedir).read() + libs = re.findall(r"-l(\S+)", netsnmp_libs) + libdirs = re.findall(r"-L(\S+)", libdir) + incdirs = re.findall(r"-I(\S+)", incdir) +else: + netsnmp_libs = os.popen('net-snmp-config --libs').read() + libdirs = re.findall(r"-L(\S+)", netsnmp_libs) + incdirs = [] + libs = re.findall(r"-l(\S+)", netsnmp_libs) + +setup( + name="netsnmp-python", version="1.0a1", + description = 'The Net-SNMP Python Interface', + author = 'G. S. Marzot', + author_email = 'giovanni.marzot@sparta.com', + url = 'http://www.net-snmp.org', + license="BSD", + packages=find_packages(), + test_suite = "netsnmp.tests.test", + + ext_modules = [ + Extension("netsnmp.client_intf", ["netsnmp/client_intf.c"], + library_dirs=libdirs, + include_dirs=incdirs, + libraries=libs ) + ] + )