From 194d2ec8202cb148bf8856e06c9248418d7debbe Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 16 Feb 2018 13:40:46 +0100 Subject: [PATCH 001/102] pysnmp-apps renamed into snmpclitools --- README.md | 4 ++-- docs/source/contents.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9489ed1f2..da4afaf4f 100644 --- a/README.md +++ b/README.md @@ -59,11 +59,11 @@ to download and install PySNMP along with its dependencies: * [PyCryptodomex](https://pycryptodome.readthedocs.io) (required only if SNMPv3 encryption is in use) * [PySMI](http://snmplabs.com/pysmi/) (required for MIB services only) -Besides the library, command-line [SNMP utilities](https://github.com/etingof/pysnmp-apps) +Besides the library, command-line [SNMP utilities](https://github.com/etingof/snmpclitools) written in pure-Python could be installed via: ```bash -$ pip install pysnmp-apps +$ pip install snmpclitools ``` and used in the very similar manner as conventional Net-SNMP tools: diff --git a/docs/source/contents.rst b/docs/source/contents.rst index 2319bbfa3..2d97af12c 100644 --- a/docs/source/contents.rst +++ b/docs/source/contents.rst @@ -24,7 +24,7 @@ examples are written for the 4.4 and later versions in mind. Older materials are still available under the obsolete section. Besides the libraries, a set of pure-Python -`command-line tools `_ +`command-line tools `_ are shipped along with the system. Those tools mimic the interface and behaviour of popular Net-SNMP snmpget/snmpset/snmpwalk utilities. They may be useful in a cross-platform situations as well as a testing From 4b21d3da293c7adc84f5f7b634bb4256397b3e84 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Mon, 9 Apr 2018 11:55:20 +0200 Subject: [PATCH 002/102] typo fix in RFC1158::snmpOutReadOnlys --- pysnmp/smi/mibs/RFC1158-MIB.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pysnmp/smi/mibs/RFC1158-MIB.py b/pysnmp/smi/mibs/RFC1158-MIB.py index 2bcdb5c20..4c1c9c6cf 100644 --- a/pysnmp/smi/mibs/RFC1158-MIB.py +++ b/pysnmp/smi/mibs/RFC1158-MIB.py @@ -17,5 +17,5 @@ snmpInBadTypes = MibScalar((1, 3, 6, 1, 2, 1, 11, 7), Counter32()).setMaxAccess("readonly") if mibBuilder.loadTexts: snmpInBadTypes.setStatus('mandatory') snmpOutReadOnlys = MibScalar((1, 3, 6, 1, 2, 1, 11, 23), Counter32()).setMaxAccess("readonly") -if mibBuilder.loadTexts: snmpInReadOnlys.setStatus('mandatory') +if mibBuilder.loadTexts: snmpOutReadOnlys.setStatus('mandatory') mibBuilder.exportSymbols("RFC1158-MIB", snmpOutReadOnlys=snmpOutReadOnlys, snmpInBadTypes=snmpInBadTypes) From 6aed418b864a90573db439c4983b601675de1816 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Mon, 9 Apr 2018 11:56:57 +0200 Subject: [PATCH 003/102] 4.4.5 --- CHANGES.txt | 5 +++++ pysnmp/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index d81161a6b..6effae80c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,9 @@ +Revision 4.4.5, released 2018-04-XX +----------------------------------- + +- Fixed typo in RFC1158 module + Revision 4.4.4, released 2018-01-03 ----------------------------------- diff --git a/pysnmp/__init__.py b/pysnmp/__init__.py index 168d8d7cb..871cc4edf 100644 --- a/pysnmp/__init__.py +++ b/pysnmp/__init__.py @@ -1,5 +1,5 @@ # http://www.python.org/dev/peps/pep-0396/ -__version__ = '4.4.4' +__version__ = '4.4.5' # backward compatibility version = tuple([int(x) for x in __version__.split('.')]) majorVersionId = version[0] From bec02e6fc1c16544d24e2050b7492e21ba9d4a29 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 13 Apr 2018 08:56:36 +0200 Subject: [PATCH 004/102] Include LICENSE in wheel --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index 2a9acf13d..13b523c63 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,5 @@ [bdist_wheel] universal = 1 + +[metadata] +license_file = LICENSE.rst From dff00bf90f705b9ce2a19ca5784a1a91ef285e80 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 13 Apr 2018 09:36:06 +0200 Subject: [PATCH 005/102] Use old Sphinx with old Python --- devel-requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devel-requirements.txt b/devel-requirements.txt index e78c2eb4b..046bf8151 100644 --- a/devel-requirements.txt +++ b/devel-requirements.txt @@ -1,3 +1,4 @@ -sphinx +Sphinx <= 1.6; python_version < '2.7' +Sphinx > 1.6; python_version >= '2.7' twisted trollius; python_version < '3.0' From c41957644565b511471cece15eabaa0c47c2ad3b Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Thu, 19 Apr 2018 01:10:24 +0200 Subject: [PATCH 006/102] fix InetAddressType rendering --- CHANGES.txt | 1 + pysnmp/smi/mibs/INET-ADDRESS-MIB.py | 4 ++-- pysnmp/smi/mibs/SNMPv2-TC.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6effae80c..0f96ddf1a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,6 +2,7 @@ Revision 4.4.5, released 2018-04-XX ----------------------------------- +- Fixed broken InetAddressType rendering caused by a pyasn1 regression - Fixed typo in RFC1158 module Revision 4.4.4, released 2018-01-03 diff --git a/pysnmp/smi/mibs/INET-ADDRESS-MIB.py b/pysnmp/smi/mibs/INET-ADDRESS-MIB.py index 46684fbc7..3ce2cd55c 100644 --- a/pysnmp/smi/mibs/INET-ADDRESS-MIB.py +++ b/pysnmp/smi/mibs/INET-ADDRESS-MIB.py @@ -80,7 +80,7 @@ def cloneFromName(cls, value, impliedFlag, parentRow, parentIndices): for parentIndex in reversed(parentIndices): if isinstance(parentIndex, InetAddressType): try: - return parentRow.setFromName(cls.typeMap[parentIndex], value, impliedFlag, parentIndices) + return parentRow.setFromName(cls.typeMap[int(parentIndex)], value, impliedFlag, parentIndices) except KeyError: pass @@ -90,7 +90,7 @@ def cloneAsName(self, impliedFlag, parentRow, parentIndices): for parentIndex in reversed(parentIndices): if isinstance(parentIndex, InetAddressType): try: - return parentRow.getAsName(self.typeMap[parentIndex].clone(self.asOctets()), impliedFlag, parentIndices) + return parentRow.getAsName(self.typeMap[int(parentIndex)].clone(str(self)), impliedFlag, parentIndices) except KeyError: pass diff --git a/pysnmp/smi/mibs/SNMPv2-TC.py b/pysnmp/smi/mibs/SNMPv2-TC.py index cad048b46..2e49352a5 100644 --- a/pysnmp/smi/mibs/SNMPv2-TC.py +++ b/pysnmp/smi/mibs/SNMPv2-TC.py @@ -259,7 +259,7 @@ def prettyIn(self, value): # override asn1 type method # how do we know if object is initialized with display-hint # formatted text? based on "text" input maybe? - if octets.isStringType(value) and not octets.isOctetsType(value): + if octets.isStringType(value): value = base.prettyIn(self, value) else: return base.prettyIn(self, value) From 1021d56e1b1472c570d395d73d1c1c73bbf7761a Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 21 Apr 2018 17:11:54 +0200 Subject: [PATCH 007/102] better InetAddressType rendering fix --- pysnmp/smi/mibs/INET-ADDRESS-MIB.py | 2 +- pysnmp/smi/mibs/SNMPv2-TC.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pysnmp/smi/mibs/INET-ADDRESS-MIB.py b/pysnmp/smi/mibs/INET-ADDRESS-MIB.py index 3ce2cd55c..5e40017c1 100644 --- a/pysnmp/smi/mibs/INET-ADDRESS-MIB.py +++ b/pysnmp/smi/mibs/INET-ADDRESS-MIB.py @@ -90,7 +90,7 @@ def cloneAsName(self, impliedFlag, parentRow, parentIndices): for parentIndex in reversed(parentIndices): if isinstance(parentIndex, InetAddressType): try: - return parentRow.getAsName(self.typeMap[int(parentIndex)].clone(str(self)), impliedFlag, parentIndices) + return parentRow.getAsName(self.typeMap[int(parentIndex)].clone(self.asOctets().decode('ascii')), impliedFlag, parentIndices) except KeyError: pass diff --git a/pysnmp/smi/mibs/SNMPv2-TC.py b/pysnmp/smi/mibs/SNMPv2-TC.py index 2e49352a5..c07e7e2c4 100644 --- a/pysnmp/smi/mibs/SNMPv2-TC.py +++ b/pysnmp/smi/mibs/SNMPv2-TC.py @@ -184,7 +184,7 @@ def prettyIn(self, value): # override asn1 type method """Implements DISPLAY-HINT parsing into base SNMP value Proper parsing seems impossible due to ambiguities. - Here we are truing to do our best, but be prepared + Here we are trying to do our best, but be prepared for failures on complicated DISPLAY-HINTs. Keep in mind that this parser only works with "text" @@ -259,7 +259,8 @@ def prettyIn(self, value): # override asn1 type method # how do we know if object is initialized with display-hint # formatted text? based on "text" input maybe? - if octets.isStringType(value): + # That boils down to `str` object on Py3 or `unicode` on Py2. + if octets.isStringType(value) and not octets.isOctetsType(value): value = base.prettyIn(self, value) else: return base.prettyIn(self, value) @@ -267,6 +268,7 @@ def prettyIn(self, value): # override asn1 type method outputValue = octets.str2octs('') runningValue = value displayHint = self.displayHint + while runningValue and displayHint: # 1 this information is totally lost, just fail explicitly if displayHint[0] == '*': From 4938e8e383d207fa83cdbfd17186acaef97e6de4 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 22 Apr 2018 02:43:45 +1000 Subject: [PATCH 008/102] asyncio.async deprecated since 3.4.4 (#143) * asyncio.async deprecated since 3.4.4 --- pysnmp/carrier/asyncio/dgram/base.py | 12 ++++++++++-- pysnmp/carrier/asyncio/dispatch.py | 10 ++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/pysnmp/carrier/asyncio/dgram/base.py b/pysnmp/carrier/asyncio/dgram/base.py index 886f8801c..a819a9c90 100644 --- a/pysnmp/carrier/asyncio/dgram/base.py +++ b/pysnmp/carrier/asyncio/dgram/base.py @@ -31,6 +31,7 @@ # THE POSSIBILITY OF SUCH DAMAGE. # import sys +import platform import traceback from pysnmp.carrier.asyncio.base import AbstractAsyncioTransport from pysnmp.carrier import error @@ -77,13 +78,20 @@ def connection_lost(self, exc): debug.logger & debug.flagIO and debug.logger('connection_lost: invoked') # AbstractAsyncioTransport API - + + python344 = platform.python_version_tuple() >= ('3', '4', '4') + def openClientMode(self, iface=None): try: c = self.loop.create_datagram_endpoint( lambda: self, local_addr=iface, family=self.sockFamily ) - self._lport = asyncio.async(c) + # Avoid deprecation warning for asyncio.async() + if python344: + self._lport = asyncio.ensure_future(c) + else: # pragma: no cover + self._lport = asyncio.async(c) + except Exception: raise error.CarrierError(';'.join(traceback.format_exception(*sys.exc_info()))) return self diff --git a/pysnmp/carrier/asyncio/dispatch.py b/pysnmp/carrier/asyncio/dispatch.py index d45fdb6b9..4fbafd1a3 100644 --- a/pysnmp/carrier/asyncio/dispatch.py +++ b/pysnmp/carrier/asyncio/dispatch.py @@ -31,6 +31,7 @@ # THE POSSIBILITY OF SUCH DAMAGE. # import sys +import platform import traceback from pysnmp.carrier.base import AbstractTransportDispatcher from pysnmp.error import PySnmpError @@ -40,6 +41,7 @@ except ImportError: import trollius as asyncio +python344 = platform.python_version_tuple() >= ('3', '4', '4') class AsyncioDispatcher(AbstractTransportDispatcher): """AsyncioDispatcher based on asyncio event loop""" @@ -66,10 +68,14 @@ def runDispatcher(self, timeout=0.0): raise except Exception: raise PySnmpError(';'.join(traceback.format_exception(*sys.exc_info()))) - + def registerTransport(self, tDomain, transport): if self.loopingcall is None and self.getTimerResolution() > 0: - self.loopingcall = asyncio.async(self.handle_timeout()) + # Avoid deprecation warning for asyncio.async() + if python344: + self.loopingcall = asyncio.ensure_future(self.handle_timeout()) + else: # pragma: no cover + self.loopingcall = asyncio.async(self.handle_timeout()) AbstractTransportDispatcher.registerTransport( self, tDomain, transport ) From 8da24e3fdbc9404255b1b1dc3dd389b4ea994ac0 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 21 Apr 2018 23:16:17 +0200 Subject: [PATCH 009/102] fix potential infinite loop in GETBULK response builder --- CHANGES.txt | 1 + pysnmp/entity/rfc3413/cmdrsp.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0f96ddf1a..57252f440 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,7 @@ Revision 4.4.5, released 2018-04-XX - Fixed broken InetAddressType rendering caused by a pyasn1 regression - Fixed typo in RFC1158 module +- Fixed possible infinite loop in GETBULK response PDU builder Revision 4.4.4, released 2018-01-03 ----------------------------------- diff --git a/pysnmp/entity/rfc3413/cmdrsp.py b/pysnmp/entity/rfc3413/cmdrsp.py index b260918b9..f69e26461 100644 --- a/pysnmp/entity/rfc3413/cmdrsp.py +++ b/pysnmp/entity/rfc3413/cmdrsp.py @@ -292,7 +292,7 @@ def handleMgmtOperation(self, snmpEngine, stateReference, R = max(len(reqVarBinds) - N, 0) if R: - M = min(M, self.maxVarBinds / R) + M = min(M, self.maxVarBinds // R) debug.logger & debug.flagApp and debug.logger('handleMgmtOperation: N %d, M %d, R %d' % (N, M, R)) From e7ca18b846d92103c8f5c9f2c086e5a4ae1b76f7 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Tue, 24 Apr 2018 11:24:06 +0200 Subject: [PATCH 010/102] VACM contextName memory leakk fix --- CHANGES.txt | 1 + pysnmp/entity/config.py | 28 ++++++++++++++++++---- pysnmp/smi/mibs/SNMP-VIEW-BASED-ACM-MIB.py | 6 ++++- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 57252f440..96a37b9dd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -5,6 +5,7 @@ Revision 4.4.5, released 2018-04-XX - Fixed broken InetAddressType rendering caused by a pyasn1 regression - Fixed typo in RFC1158 module - Fixed possible infinite loop in GETBULK response PDU builder +- Fixed memory leak in the T`config.delContext()` VACM management harness Revision 4.4.4, released 2018-01-03 ----------------------------------- diff --git a/pysnmp/entity/config.py b/pysnmp/entity/config.py index 06d6c14db..28dfccee4 100644 --- a/pysnmp/entity/config.py +++ b/pysnmp/entity/config.py @@ -389,13 +389,30 @@ def delTransport(snmpEngine, transportDomain): # VACM shortcuts -def addContext(snmpEngine, contextName): +def __cookVacmContextInfo(snmpEngine, contextName): mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder - vacmContextEntry, = mibBuilder.importSymbols('SNMP-VIEW-BASED-ACM-MIB', 'vacmContextEntry') tblIdx = vacmContextEntry.getInstIdFromIndices(contextName) + return vacmContextEntry, tblIdx + + +def addContext(snmpEngine, contextName): + vacmContextEntry, tblIdx = __cookVacmContextInfo(snmpEngine, contextName) + + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( + ((vacmContextEntry.name + (5,) + tblIdx, 'destroy'),) + ) snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( - ((vacmContextEntry.name + (1,) + tblIdx, contextName),) + ((vacmContextEntry.name + (1,) + tblIdx, contextName), + (vacmContextEntry.name + (2,) + tblIdx, 'createAndGo')) + ) + + +def delContext(snmpEngine, contextName): + vacmContextEntry, tblIdx = __cookVacmContextInfo(snmpEngine, contextName) + + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( + ((vacmContextEntry.name + (5,) + tblIdx, 'destroy'),) ) @@ -448,7 +465,7 @@ def addVacmAccess(snmpEngine, groupName, contextName, securityModel, contextName, securityModel, securityLevel) - addContext(snmpEngine, contextName) # this is leaky + addContext(snmpEngine, contextName) snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((vacmAccessEntry.name + (9,) + tblIdx, 'destroy'),) @@ -470,6 +487,9 @@ def delVacmAccess(snmpEngine, groupName, contextName, securityModel, vacmAccessEntry, tblIdx = __cookVacmAccessInfo(snmpEngine, groupName, contextName, securityModel, securityLevel) + + delContext(snmpEngine, contextName) + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((vacmAccessEntry.name + (9,) + tblIdx, 'destroy'),) ) diff --git a/pysnmp/smi/mibs/SNMP-VIEW-BASED-ACM-MIB.py b/pysnmp/smi/mibs/SNMP-VIEW-BASED-ACM-MIB.py index d8eeffd4f..8fbf962ea 100644 --- a/pysnmp/smi/mibs/SNMP-VIEW-BASED-ACM-MIB.py +++ b/pysnmp/smi/mibs/SNMP-VIEW-BASED-ACM-MIB.py @@ -34,6 +34,10 @@ vacmContextName = MibTableColumn((1, 3, 6, 1, 6, 3, 16, 1, 1, 1, 1), SnmpAdminString().subtype(subtypeSpec=ValueSizeConstraint(0, 32))).setMaxAccess("readonly") if mibBuilder.loadTexts: vacmContextName.setStatus('current') if mibBuilder.loadTexts: vacmContextName.setDescription('A human readable name identifying a particular context at a particular SNMP entity. The empty contextName (zero length) represents the default context. ') +# The RowStatus column is not present in the MIB +vacmContextStatus = MibTableColumn((1, 3, 6, 1, 6, 3, 16, 1, 1, 1, 2), RowStatus()).setMaxAccess("readcreate") +if mibBuilder.loadTexts: vacmContextStatus.setStatus('current') +if mibBuilder.loadTexts: vacmContextStatus.setDescription("The status of this conceptual row. Until instances of all corresponding columns are appropriately configured, the value of the corresponding instance of the vacmContextTableStatus column is 'notReady'. In particular, a newly created row cannot be made active until a value has been set for vacmContextName. The RowStatus TC [RFC2579] requires that this DESCRIPTION clause states under which circumstances other objects in this row can be modified: The value of this object has no effect on whether other objects in this conceptual row can be modified. ") vacmSecurityToGroupTable = MibTable((1, 3, 6, 1, 6, 3, 16, 1, 2), ) if mibBuilder.loadTexts: vacmSecurityToGroupTable.setStatus('current') if mibBuilder.loadTexts: vacmSecurityToGroupTable.setDescription('This table maps a combination of securityModel and securityName into a groupName which is used to define an access control policy for a group of principals. ') @@ -122,4 +126,4 @@ if mibBuilder.loadTexts: vacmMIBCompliance.setDescription('The compliance statement for SNMP engines which implement the SNMP View-based Access Control Model configuration MIB. ') vacmBasicGroup = ObjectGroup((1, 3, 6, 1, 6, 3, 16, 2, 2, 1)).setObjects(("SNMP-VIEW-BASED-ACM-MIB", "vacmContextName"), ("SNMP-VIEW-BASED-ACM-MIB", "vacmGroupName"), ("SNMP-VIEW-BASED-ACM-MIB", "vacmSecurityToGroupStorageType"), ("SNMP-VIEW-BASED-ACM-MIB", "vacmSecurityToGroupStatus"), ("SNMP-VIEW-BASED-ACM-MIB", "vacmAccessContextMatch"), ("SNMP-VIEW-BASED-ACM-MIB", "vacmAccessReadViewName"), ("SNMP-VIEW-BASED-ACM-MIB", "vacmAccessWriteViewName"), ("SNMP-VIEW-BASED-ACM-MIB", "vacmAccessNotifyViewName"), ("SNMP-VIEW-BASED-ACM-MIB", "vacmAccessStorageType"), ("SNMP-VIEW-BASED-ACM-MIB", "vacmAccessStatus"), ("SNMP-VIEW-BASED-ACM-MIB", "vacmViewSpinLock"), ("SNMP-VIEW-BASED-ACM-MIB", "vacmViewTreeFamilyMask"), ("SNMP-VIEW-BASED-ACM-MIB", "vacmViewTreeFamilyType"), ("SNMP-VIEW-BASED-ACM-MIB", "vacmViewTreeFamilyStorageType"), ("SNMP-VIEW-BASED-ACM-MIB", "vacmViewTreeFamilyStatus")) if mibBuilder.loadTexts: vacmBasicGroup.setDescription('A collection of objects providing for remote configuration of an SNMP engine which implements the SNMP View-based Access Control Model. ') -mibBuilder.exportSymbols("SNMP-VIEW-BASED-ACM-MIB", vacmSecurityModel=vacmSecurityModel, vacmSecurityToGroupEntry=vacmSecurityToGroupEntry, vacmMIBConformance=vacmMIBConformance, vacmAccessReadViewName=vacmAccessReadViewName, vacmViewTreeFamilyMask=vacmViewTreeFamilyMask, snmpVacmMIB=snmpVacmMIB, vacmViewTreeFamilyType=vacmViewTreeFamilyType, vacmMIBGroups=vacmMIBGroups, vacmAccessContextMatch=vacmAccessContextMatch, vacmAccessNotifyViewName=vacmAccessNotifyViewName, vacmViewSpinLock=vacmViewSpinLock, vacmAccessWriteViewName=vacmAccessWriteViewName, vacmContextTable=vacmContextTable, PYSNMP_MODULE_ID=snmpVacmMIB, vacmViewTreeFamilyViewName=vacmViewTreeFamilyViewName, vacmMIBObjects=vacmMIBObjects, vacmViewTreeFamilyStatus=vacmViewTreeFamilyStatus, vacmSecurityName=vacmSecurityName, vacmGroupName=vacmGroupName, vacmAccessSecurityLevel=vacmAccessSecurityLevel, vacmBasicGroup=vacmBasicGroup, vacmContextName=vacmContextName, vacmSecurityToGroupStatus=vacmSecurityToGroupStatus, vacmAccessEntry=vacmAccessEntry, vacmMIBViews=vacmMIBViews, vacmAccessStorageType=vacmAccessStorageType, vacmMIBCompliances=vacmMIBCompliances, vacmViewTreeFamilyEntry=vacmViewTreeFamilyEntry, vacmViewTreeFamilyTable=vacmViewTreeFamilyTable, vacmSecurityToGroupStorageType=vacmSecurityToGroupStorageType, vacmAccessTable=vacmAccessTable, vacmAccessContextPrefix=vacmAccessContextPrefix, vacmViewTreeFamilyStorageType=vacmViewTreeFamilyStorageType, vacmMIBCompliance=vacmMIBCompliance, vacmAccessSecurityModel=vacmAccessSecurityModel, vacmAccessStatus=vacmAccessStatus, vacmContextEntry=vacmContextEntry, vacmSecurityToGroupTable=vacmSecurityToGroupTable, vacmViewTreeFamilySubtree=vacmViewTreeFamilySubtree) +mibBuilder.exportSymbols("SNMP-VIEW-BASED-ACM-MIB", vacmSecurityModel=vacmSecurityModel, vacmSecurityToGroupEntry=vacmSecurityToGroupEntry, vacmMIBConformance=vacmMIBConformance, vacmAccessReadViewName=vacmAccessReadViewName, vacmViewTreeFamilyMask=vacmViewTreeFamilyMask, snmpVacmMIB=snmpVacmMIB, vacmViewTreeFamilyType=vacmViewTreeFamilyType, vacmMIBGroups=vacmMIBGroups, vacmAccessContextMatch=vacmAccessContextMatch, vacmAccessNotifyViewName=vacmAccessNotifyViewName, vacmViewSpinLock=vacmViewSpinLock, vacmAccessWriteViewName=vacmAccessWriteViewName, vacmContextTable=vacmContextTable, PYSNMP_MODULE_ID=snmpVacmMIB, vacmViewTreeFamilyViewName=vacmViewTreeFamilyViewName, vacmMIBObjects=vacmMIBObjects, vacmViewTreeFamilyStatus=vacmViewTreeFamilyStatus, vacmSecurityName=vacmSecurityName, vacmGroupName=vacmGroupName, vacmAccessSecurityLevel=vacmAccessSecurityLevel, vacmBasicGroup=vacmBasicGroup, vacmContextName=vacmContextName, vacmContextStatus=vacmContextStatus, vacmSecurityToGroupStatus=vacmSecurityToGroupStatus, vacmAccessEntry=vacmAccessEntry, vacmMIBViews=vacmMIBViews, vacmAccessStorageType=vacmAccessStorageType, vacmMIBCompliances=vacmMIBCompliances, vacmViewTreeFamilyEntry=vacmViewTreeFamilyEntry, vacmViewTreeFamilyTable=vacmViewTreeFamilyTable, vacmSecurityToGroupStorageType=vacmSecurityToGroupStorageType, vacmAccessTable=vacmAccessTable, vacmAccessContextPrefix=vacmAccessContextPrefix, vacmViewTreeFamilyStorageType=vacmViewTreeFamilyStorageType, vacmMIBCompliance=vacmMIBCompliance, vacmAccessSecurityModel=vacmAccessSecurityModel, vacmAccessStatus=vacmAccessStatus, vacmContextEntry=vacmContextEntry, vacmSecurityToGroupTable=vacmSecurityToGroupTable, vacmViewTreeFamilySubtree=vacmViewTreeFamilySubtree) From 293f4515138fa3b3a1f0f0190a321bf7456d2837 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Tue, 24 Apr 2018 12:00:20 +0200 Subject: [PATCH 011/102] VACM contextName memory leak fix followup --- pysnmp/entity/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pysnmp/entity/config.py b/pysnmp/entity/config.py index 28dfccee4..bb7be0a3b 100644 --- a/pysnmp/entity/config.py +++ b/pysnmp/entity/config.py @@ -400,7 +400,7 @@ def addContext(snmpEngine, contextName): vacmContextEntry, tblIdx = __cookVacmContextInfo(snmpEngine, contextName) snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( - ((vacmContextEntry.name + (5,) + tblIdx, 'destroy'),) + ((vacmContextEntry.name + (2,) + tblIdx, 'destroy'),) ) snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((vacmContextEntry.name + (1,) + tblIdx, contextName), @@ -412,7 +412,7 @@ def delContext(snmpEngine, contextName): vacmContextEntry, tblIdx = __cookVacmContextInfo(snmpEngine, contextName) snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( - ((vacmContextEntry.name + (5,) + tblIdx, 'destroy'),) + ((vacmContextEntry.name + (2,) + tblIdx, 'destroy'),) ) From acab4ce4dc05c414932a4885a627015a1f77a220 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Wed, 27 Jun 2018 23:49:59 -0700 Subject: [PATCH 012/102] Add Python 3.7 support (#162) Python 3.7 was just released [1]. This is a small change to enable support in pysnmp. [1] https://docs.python.org/3.7/whatsnew/3.7.html Signed-off-by: Eric Brown --- .travis.yml | 1 + README.md | 2 +- docs/source/contents.rst | 2 +- docs/source/development.rst | 2 +- setup.py | 1 + 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index eaf5f481e..04833bd35 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ python: - "3.4" - "3.5" - "3.6" + - "3.7" - "nightly" # - "pypy" # - "pypy3" diff --git a/README.md b/README.md index da4afaf4f..9c6a726bb 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Features * [PySMI](http://snmplabs.com/pysmi/) integration for dynamic MIB compilation * Built-in instrumentation exposing protocol engine operations * Python eggs and py2exe friendly -* 100% Python, works with Python 2.4 though 3.6 +* 100% Python, works with Python 2.4 though 3.7 * MT-safe (if SnmpEngine is thread-local) Features, specific to SNMPv3 model include: diff --git a/docs/source/contents.rst b/docs/source/contents.rst index 2d97af12c..c37c1a84c 100644 --- a/docs/source/contents.rst +++ b/docs/source/contents.rst @@ -17,7 +17,7 @@ multilingual capabilities, remote configuration and other features. PySNMP implementation closely follows intricate system details and features bringing most possible power and flexibility to its users. -Current PySNMP stable version is 4.4. It runs with Python 2.4 through 3.6 +Current PySNMP stable version is 4.4. It runs with Python 2.4 through 3.7 and is recommended for new applications as well as for migration from older, now obsolete, PySNMP releases. All site documentation and examples are written for the 4.4 and later versions in mind. diff --git a/docs/source/development.rst b/docs/source/development.rst index 920b0ad90..0050f943b 100644 --- a/docs/source/development.rst +++ b/docs/source/development.rst @@ -93,7 +93,7 @@ sponsoring it. Please get back to us to discuss details. Contributions to the PySNMP source code is greatly appreciated as well. We require contributed code to run with Python 2.4 through the latest -Python version (which is 3.6 at the time of this writing). Contributed +Python version (which is 3.7 at the time of this writing). Contributed code will be redistributed under the terms of the same `license `_ as PySNMP is. diff --git a/setup.py b/setup.py index 0fac0fca4..cfc4d89eb 100644 --- a/setup.py +++ b/setup.py @@ -30,6 +30,7 @@ Programming Language :: Python :: 3.4 Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 +Programming Language :: Python :: 3.7 Topic :: Communications Topic :: System :: Monitoring Topic :: System :: Networking :: Monitoring From 49927e08092c318e21879a23dcb61b2292027515 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 18 May 2018 08:19:16 +0200 Subject: [PATCH 013/102] pin twisted versions --- devel-requirements.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/devel-requirements.txt b/devel-requirements.txt index 046bf8151..d03c9630d 100644 --- a/devel-requirements.txt +++ b/devel-requirements.txt @@ -1,4 +1,10 @@ Sphinx <= 1.6; python_version < '2.7' Sphinx > 1.6; python_version >= '2.7' -twisted trollius; python_version < '3.0' +twisted < 15.4; python_version < '2.7' +twisted; python_version == '2.7' +twisted < 17.9; python_version == '3.0' +twisted < 17.9; python_version == '3.1' +twisted < 17.9; python_version == '3.2' +twisted <= 17.9; python_version == '3.3' +twisted; python_version >= '3.4' From 1cec4e7bb311d35cbbe6b8b1f7a74c83d93fd573 Mon Sep 17 00:00:00 2001 From: Aaron Spike Date: Fri, 18 May 2018 00:47:35 -0500 Subject: [PATCH 014/102] Typo "preperly" -> "properly" (#156) --- docs/source/docs/pysnmp-hlapi-tutorial.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/docs/pysnmp-hlapi-tutorial.rst b/docs/source/docs/pysnmp-hlapi-tutorial.rst index 1062a076d..22075810d 100644 --- a/docs/source/docs/pysnmp-hlapi-tutorial.rst +++ b/docs/source/docs/pysnmp-hlapi-tutorial.rst @@ -126,7 +126,7 @@ objects representing completely different instances of hardware or software being managed. This is where SNMP context could be used. -To indicate SNMP context at high-level API a preperly initialized +To indicate SNMP context at high-level API a properly initialized :py:class:`~pysnmp.hlapi.ContextData` object should be used. For this example we will use the 'empty' context (default). From 9051369318de5a014a6770bc64c310f6c67d6045 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 29 Jun 2018 09:00:14 +0200 Subject: [PATCH 015/102] reduce tests output by tailing them --- runtests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtests.sh b/runtests.sh index 22fab7e16..a435da6e1 100755 --- a/runtests.sh +++ b/runtests.sh @@ -19,7 +19,7 @@ do continue ;; *) - python "${x}" + python "${x}" | tail -50 ;; esac done \ No newline at end of file From ed7e72c6a1824239d8a94f18580dea0c2f2fb117 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 29 Jun 2018 09:06:28 +0200 Subject: [PATCH 016/102] switch Travis back to 3.7-dev while 3.7 is not avail --- .travis.yml | 60 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 04833bd35..c75af9a61 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,51 @@ - language: python -python: - - "2.6" - - "2.7" - - "3.2" - - "3.3" - - "3.4" - - "3.5" - - "3.6" - - "3.7" - - "nightly" -# - "pypy" -# - "pypy3" +cache: pip +matrix: + include: + - os: linux + dist: xenial + sudo: false + python: '2.6' + - os: linux + dist: xenial + sudo: false + python: '2.7' + - os: linux + dist: xenial + sudo: false + python: '3.2' + - os: linux + dist: xenial + sudo: false + python: '3.3' + - os: linux + dist: xenial + sudo: false + python: '3.4' + - os: linux + dist: xenial + sudo: false + python: '3.5' + - os: linux + dist: xenial + sudo: false + python: '3.6' + - os: linux + dist: xenial + sudo: false + python: '3.7-dev' + - os: linux + dist: xenial + sudo: false + python: 'nightly' +# - os: linux +# dist: xenial +# sudo: false +# python: 'pypy' +# - os: linux +# dist: xenial +# sudo: false +# python: 'pypy3' install: - pip install -r requirements.txt -r devel-requirements.txt - pip install -e . From ee4c0a53756746fb00ede7a7e0f672e85ce73c5e Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Tue, 3 Jul 2018 08:05:23 +0200 Subject: [PATCH 017/102] Fix typos --- CHANGES.txt | 12 ++++++------ docs/source/docs/snmp-design.rst | 2 +- examples/smi/manager/mib-tree-inspection.py | 4 ++-- pysnmp/smi/rfc1902.py | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 96a37b9dd..be7ab3a85 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -565,7 +565,7 @@ Revision 4.2.4, released 2013-01-30 defaulted value changed from None to () meaning no var-binds. - Attempt to convert Windows style EOL into UNIX ones in MIB source modules appeared to be unnecessary and even destructive to modules - data in some cases. So the convertion code removed altogether. + data in some cases. So the conversion code removed altogether. - Fix to isAccessAllowed() error handling at NotificationOriginator. System used to crash on access denied condition. - Fix to NotificationOriginator to make it use system uptime and trap OID @@ -826,7 +826,7 @@ Revision 4.1.16a, released 2011-03-17 configuration (LCD). + default debug.logger is now just a zero value instead of an object what saves big on frequent calls - + SNMPv2-SMI columnar indices <-> index values convertion code optimized. + + SNMPv2-SMI columnar indices <-> index values conversion code optimized. + pre-compute and re-use some of ASN.1 structures. + avoid setting PDU defaults to save on unnecessary initialization. + skip ASN.1 types verification where possible. @@ -1016,7 +1016,7 @@ Revision 4.1.9a, released 2007-11-28 effects. - Fix to rfc2576.v1ToV2c() PDU converter to perform noSuchName error code translation. -- Fixes to Notification PDU convertion code at rfc2576 in part of +- Fixes to Notification PDU conversion code at rfc2576 in part of snmpTrapOID handling. - Fix to nonRepeaters object use as sequence slicer (must be int) at cmdrsp.CommandResponderApplication @@ -1038,7 +1038,7 @@ Revision 4.1.8a, released 2007-08-14 ------------------------------------ - UNSTABLE ALPHA RELEASE. -- SMI/dispatcher timeout convertion multiplier is actually 100 (1/100 sec) +- SMI/dispatcher timeout conversion multiplier is actually 100 (1/100 sec) rather than 1/1000. This fix affects timeouts specified through SMI. - __repr__() implemented for UdpTransportTarget, CommunityData, UsmUserData in oneliner module. @@ -1089,7 +1089,7 @@ Revision 4.1.7a, released 2007-02-19 - Fix to MibViewController.getNodeName() to take MIB module name into account (SF bug #1505847). - Do explicit check for Counter32,Unsigned32,TimeTicks,Counter64 value types - in MibTableRow index convertion and in TextualConvention.prettyPrint() + in MibTableRow index conversion and in TextualConvention.prettyPrint() methods (SF bug #1506341). Handle Bits in indices as RFC2578 suggests. - Apply read-create column status to libsmi2pysnmp-generated code whenever MIB text specifies that (SF bug #1508955). @@ -1113,7 +1113,7 @@ Revision 4.1.7a, released 2007-02-19 - LCD unconfiguration functions for oneliners implemented (SF bug #1635270). - unloadModules() and unexportSymbols() implemented at MibBuilder - Notification type PDU proxy code fixed to produce symmetrical - convertion. + conversion. - Various SNMP engine-internal caches expiration implemented. - SMI-level access control now takes effect only if AC object is passed to MIB instrumentation API. diff --git a/docs/source/docs/snmp-design.rst b/docs/source/docs/snmp-design.rst index 2752a9180..c0cde7b43 100644 --- a/docs/source/docs/snmp-design.rst +++ b/docs/source/docs/snmp-design.rst @@ -227,7 +227,7 @@ engine loads those modules at runtime on demand. PySNMP MIB modules are universal -- the same module can be consumed by both managed and managing entities. -MIB convertion is performed automatically by PySNMP, but technically, +MIB conversion is performed automatically by PySNMP, but technically, it is handled by PySNMP sister project called `PySMI `_. However you can also perform said conversion by hand with PySMI's *mibdump.py* tool. diff --git a/examples/smi/manager/mib-tree-inspection.py b/examples/smi/manager/mib-tree-inspection.py index cff34b00f..58b110a60 100644 --- a/examples/smi/manager/mib-tree-inspection.py +++ b/examples/smi/manager/mib-tree-inspection.py @@ -58,10 +58,10 @@ rowNode, = mibBuilder.importSymbols(modName, symName) print(rowNode) -print('Conceptual table index value to oid convertion: '), +print('Conceptual table index value to oid conversion: '), oid = rowNode.getInstIdFromIndices('router') print(oid) -print('Conceptual table index oid to value convertion: '), +print('Conceptual table index oid to value conversion: '), print(rowNode.getIndicesFromInstId(oid)) print('MIB tree traversal') diff --git a/pysnmp/smi/rfc1902.py b/pysnmp/smi/rfc1902.py index 07cea1e54..73612ecde 100644 --- a/pysnmp/smi/rfc1902.py +++ b/pysnmp/smi/rfc1902.py @@ -506,7 +506,7 @@ class instance self.__oid += instIds self.__indices = rowNode.getIndicesFromInstId(instIds) except PyAsn1Error: - raise SmiError('Instance index %r to OID convertion failure at object %r: %s' % ( + raise SmiError('Instance index %r to OID conversion failure at object %r: %s' % ( self.__args[2:], mibNode.getLabel(), sys.exc_info()[1])) elif self.__args[2:]: # any other kind of MIB node with indices if self.__args[2:]: From f08dad88298cdb23f822dd1f5cfc073ae1d9d503 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Mon, 2 Jul 2018 23:18:56 -0700 Subject: [PATCH 018/102] Switch back to offical Python 3.7 build (#163) It appears that Python 3.7 use in Travis-CI is finally fixed. However, it requires use of xenial distribution and sudo: true. Those have now been added to the matrix. Signed-off-by: Eric Brown --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c75af9a61..99dc942d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,8 +32,8 @@ matrix: python: '3.6' - os: linux dist: xenial - sudo: false - python: '3.7-dev' + sudo: true + python: '3.7' - os: linux dist: xenial sudo: false From 06eeaf6fbfee6c638f6cd455ef99c0cd909ce740 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Wed, 4 Jul 2018 09:31:03 +0200 Subject: [PATCH 019/102] Fix scoping bug in asyncio wrapper Probably introduced by commit 2b27b49db77ff6fdad311e122da7c1305fccc095 --- pysnmp/carrier/asyncio/dgram/base.py | 10 +++++----- pysnmp/carrier/asyncio/dispatch.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pysnmp/carrier/asyncio/dgram/base.py b/pysnmp/carrier/asyncio/dgram/base.py index a819a9c90..e06e8ee5d 100644 --- a/pysnmp/carrier/asyncio/dgram/base.py +++ b/pysnmp/carrier/asyncio/dgram/base.py @@ -42,6 +42,8 @@ except ImportError: import trollius as asyncio +IS_PYTHON_344_PLUS = platform.python_version_tuple() >= ('3', '4', '4') + class DgramAsyncioProtocol(asyncio.DatagramProtocol, AbstractAsyncioTransport): """Base Asyncio datagram Transport, to be used with AsyncioDispatcher""" @@ -78,20 +80,18 @@ def connection_lost(self, exc): debug.logger & debug.flagIO and debug.logger('connection_lost: invoked') # AbstractAsyncioTransport API - - python344 = platform.python_version_tuple() >= ('3', '4', '4') - + def openClientMode(self, iface=None): try: c = self.loop.create_datagram_endpoint( lambda: self, local_addr=iface, family=self.sockFamily ) # Avoid deprecation warning for asyncio.async() - if python344: + if IS_PYTHON_344_PLUS: self._lport = asyncio.ensure_future(c) else: # pragma: no cover self._lport = asyncio.async(c) - + except Exception: raise error.CarrierError(';'.join(traceback.format_exception(*sys.exc_info()))) return self diff --git a/pysnmp/carrier/asyncio/dispatch.py b/pysnmp/carrier/asyncio/dispatch.py index 4fbafd1a3..2ba4cf4d6 100644 --- a/pysnmp/carrier/asyncio/dispatch.py +++ b/pysnmp/carrier/asyncio/dispatch.py @@ -41,7 +41,7 @@ except ImportError: import trollius as asyncio -python344 = platform.python_version_tuple() >= ('3', '4', '4') +IS_PYTHON_344_PLUS = platform.python_version_tuple() >= ('3', '4', '4') class AsyncioDispatcher(AbstractTransportDispatcher): """AsyncioDispatcher based on asyncio event loop""" @@ -72,7 +72,7 @@ def runDispatcher(self, timeout=0.0): def registerTransport(self, tDomain, transport): if self.loopingcall is None and self.getTimerResolution() > 0: # Avoid deprecation warning for asyncio.async() - if python344: + if IS_PYTHON_344_PLUS: self.loopingcall = asyncio.ensure_future(self.handle_timeout()) else: # pragma: no cover self.loopingcall = asyncio.async(self.handle_timeout()) From 23d1aa7bf5139d05c29ef27f6a5f68948c93a9df Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Thu, 5 Jul 2018 10:38:56 +0200 Subject: [PATCH 020/102] Fix var-bind initialization from ObjectIdentity at hlapi --- CHANGES.txt | 3 ++- pysnmp/hlapi/varbinds.py | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index be7ab3a85..6e234b2f6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,11 +1,12 @@ -Revision 4.4.5, released 2018-04-XX +Revision 4.4.5, released 2018-07-XX ----------------------------------- - Fixed broken InetAddressType rendering caused by a pyasn1 regression - Fixed typo in RFC1158 module - Fixed possible infinite loop in GETBULK response PDU builder - Fixed memory leak in the T`config.delContext()` VACM management harness +- Fixed var-binds initialization from `ObjectIdentity` object at `hlapi` Revision 4.4.4, released 2018-01-03 ----------------------------------- diff --git a/pysnmp/hlapi/varbinds.py b/pysnmp/hlapi/varbinds.py index ac9151a88..4d80a970e 100644 --- a/pysnmp/hlapi/varbinds.py +++ b/pysnmp/hlapi/varbinds.py @@ -29,8 +29,8 @@ def makeVarBinds(self, snmpEngine, varBinds): for varBind in varBinds: if isinstance(varBind, ObjectType): pass - elif isinstance(varBind[0], ObjectIdentity): - varBind = ObjectType(*varBind) + elif isinstance(varBind, ObjectIdentity): + varBind = ObjectType(varBind) elif isinstance(varBind[0][0], tuple): # legacy varBind = ObjectType(ObjectIdentity(varBind[0][0][0], varBind[0][0][1], *varBind[0][1:]), varBind[1]) else: @@ -57,8 +57,8 @@ def makeVarBinds(self, snmpEngine, varBinds): for varBind in varBinds: if isinstance(varBind, ObjectType): pass - elif isinstance(varBind[0], ObjectIdentity): - varBind = ObjectType(*varBind) + elif isinstance(varBind, ObjectIdentity): + varBind = ObjectType(varBind) else: varBind = ObjectType(ObjectIdentity(varBind[0]), varBind[1]) __varBinds.append(varBind.resolveWithMib(mibViewController)) From d7b86be5006dba25319be7259308a4857ffc687c Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Thu, 5 Jul 2018 10:39:53 +0200 Subject: [PATCH 021/102] Fix typo in docstring --- pysnmp/hlapi/asyncio/cmdgen.py | 8 ++++---- pysnmp/hlapi/asyncio/ntforg.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pysnmp/hlapi/asyncio/cmdgen.py b/pysnmp/hlapi/asyncio/cmdgen.py index 0e9e88a5a..e449d4771 100644 --- a/pysnmp/hlapi/asyncio/cmdgen.py +++ b/pysnmp/hlapi/asyncio/cmdgen.py @@ -57,7 +57,7 @@ def getCmd(snmpEngine, authData, transportTarget, contextData, *varBinds, **options): """Creates a generator to perform SNMP GET query. - When itereator gets advanced by :py:mod:`asyncio` main loop, + When iterator gets advanced by :py:mod:`asyncio` main loop, SNMP GET request is send (:RFC:`1905#section-4.2.1`). The iterator yields :py:class:`asyncio.Future` which gets done whenever response arrives or error occurs. @@ -162,7 +162,7 @@ def setCmd(snmpEngine, authData, transportTarget, contextData, *varBinds, **options): """Creates a generator to perform SNMP SET query. - When itereator gets advanced by :py:mod:`asyncio` main loop, + When iterator gets advanced by :py:mod:`asyncio` main loop, SNMP SET request is send (:RFC:`1905#section-4.2.5`). The iterator yields :py:class:`asyncio.Future` which gets done whenever response arrives or error occurs. @@ -267,7 +267,7 @@ def nextCmd(snmpEngine, authData, transportTarget, contextData, *varBinds, **options): """Creates a generator to perform SNMP GETNEXT query. - When itereator gets advanced by :py:mod:`asyncio` main loop, + When iterator gets advanced by :py:mod:`asyncio` main loop, SNMP GETNEXT request is send (:RFC:`1905#section-4.2.2`). The iterator yields :py:class:`asyncio.Future` which gets done whenever response arrives or error occurs. @@ -378,7 +378,7 @@ def bulkCmd(snmpEngine, authData, transportTarget, contextData, nonRepeaters, maxRepetitions, *varBinds, **options): """Creates a generator to perform SNMP GETBULK query. - When itereator gets advanced by :py:mod:`asyncio` main loop, + When iterator gets advanced by :py:mod:`asyncio` main loop, SNMP GETBULK request is send (:RFC:`1905#section-4.2.3`). The iterator yields :py:class:`asyncio.Future` which gets done whenever response arrives or error occurs. diff --git a/pysnmp/hlapi/asyncio/ntforg.py b/pysnmp/hlapi/asyncio/ntforg.py index db7b89ad8..0bd59b7e8 100644 --- a/pysnmp/hlapi/asyncio/ntforg.py +++ b/pysnmp/hlapi/asyncio/ntforg.py @@ -32,7 +32,7 @@ def sendNotification(snmpEngine, authData, transportTarget, contextData, notifyType, varBinds, **options): """Creates a generator to send SNMP notification. - When itereator gets advanced by :py:mod:`asyncio` main loop, + When iterator gets advanced by :py:mod:`asyncio` main loop, SNMP TRAP or INFORM notification is send (:RFC:`1905#section-4.2.6`). The iterator yields :py:class:`asyncio.Future` which gets done whenever response arrives or error occurs. From d70831c76f1fb60a130080040cbe720b60a54894 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Thu, 5 Jul 2018 13:39:18 +0200 Subject: [PATCH 022/102] Revert "Fix var-bind initialization from ObjectIdentity at hlapi" This reverts commit 23d1aa7bf5139d05c29ef27f6a5f68948c93a9df. --- CHANGES.txt | 3 +-- pysnmp/hlapi/varbinds.py | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6e234b2f6..be7ab3a85 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,12 +1,11 @@ -Revision 4.4.5, released 2018-07-XX +Revision 4.4.5, released 2018-04-XX ----------------------------------- - Fixed broken InetAddressType rendering caused by a pyasn1 regression - Fixed typo in RFC1158 module - Fixed possible infinite loop in GETBULK response PDU builder - Fixed memory leak in the T`config.delContext()` VACM management harness -- Fixed var-binds initialization from `ObjectIdentity` object at `hlapi` Revision 4.4.4, released 2018-01-03 ----------------------------------- diff --git a/pysnmp/hlapi/varbinds.py b/pysnmp/hlapi/varbinds.py index 4d80a970e..ac9151a88 100644 --- a/pysnmp/hlapi/varbinds.py +++ b/pysnmp/hlapi/varbinds.py @@ -29,8 +29,8 @@ def makeVarBinds(self, snmpEngine, varBinds): for varBind in varBinds: if isinstance(varBind, ObjectType): pass - elif isinstance(varBind, ObjectIdentity): - varBind = ObjectType(varBind) + elif isinstance(varBind[0], ObjectIdentity): + varBind = ObjectType(*varBind) elif isinstance(varBind[0][0], tuple): # legacy varBind = ObjectType(ObjectIdentity(varBind[0][0][0], varBind[0][0][1], *varBind[0][1:]), varBind[1]) else: @@ -57,8 +57,8 @@ def makeVarBinds(self, snmpEngine, varBinds): for varBind in varBinds: if isinstance(varBind, ObjectType): pass - elif isinstance(varBind, ObjectIdentity): - varBind = ObjectType(varBind) + elif isinstance(varBind[0], ObjectIdentity): + varBind = ObjectType(*varBind) else: varBind = ObjectType(ObjectIdentity(varBind[0]), varBind[1]) __varBinds.append(varBind.resolveWithMib(mibViewController)) From 6031836dfa0048d1de5643770175c648df840d88 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 20 Jul 2018 09:05:29 +0200 Subject: [PATCH 023/102] Fix `Bits` initialization with `namedValues` --- CHANGES.txt | 1 + pysnmp/proto/rfc1902.py | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index be7ab3a85..f57aa6a8a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,6 +6,7 @@ Revision 4.4.5, released 2018-04-XX - Fixed typo in RFC1158 module - Fixed possible infinite loop in GETBULK response PDU builder - Fixed memory leak in the T`config.delContext()` VACM management harness +- Fixed `Bits` class initialization when enumeration values are given Revision 4.4.4, released 2018-01-03 ----------------------------------- diff --git a/pysnmp/proto/rfc1902.py b/pysnmp/proto/rfc1902.py index 12237482a..3370f4788 100644 --- a/pysnmp/proto/rfc1902.py +++ b/pysnmp/proto/rfc1902.py @@ -598,11 +598,12 @@ class Bits(OctetString): """ namedValues = namedval.NamedValues() - def __init__(self, value=univ.noValue, **kwargs): - if 'namedValues' not in kwargs: - kwargs['namedValues'] = self.namedValues + def __new__(cls, *args, **kwargs): + if 'namedValues' in kwargs: + Bits = cls.withNamedBits(**kwargs.pop('namedValues')) + return Bits(*args, **kwargs) - OctetString.__init__(self, value, **kwargs) + return OctetString.__new__(cls) def prettyIn(self, bits): if not isinstance(bits, (tuple, list)): From e25f1287e0f39dd65d0ca8bc6324f8d1fc575b1e Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 31 Jan 2018 14:24:24 -0800 Subject: [PATCH 024/102] adding a basic tox handle to simplify running tests --- .gitignore | 3 +++ tox.ini | 5 +++++ 2 files changed, 8 insertions(+) create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 77f208ce7..976c4a50a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ docs/source/.templates/layout.html # Virtual envs venv* + +# Tox +.tox/ diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..5aa494ddf --- /dev/null +++ b/tox.ini @@ -0,0 +1,5 @@ +[envlist] +envlist = py{27,34,35,36} + +[testenv] +commands = {toxinidir}/runtests.sh From 5e13765c6e9a4fec4fc4010be0746fda951bc59b Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Fri, 9 Feb 2018 11:25:12 -0800 Subject: [PATCH 025/102] adding py33 to the tox auto-run environments --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 5aa494ddf..af9834376 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [envlist] -envlist = py{27,34,35,36} +envlist = py{26,27,33,34,35,36} [testenv] commands = {toxinidir}/runtests.sh From 83a8ac9e3a417cbab8b122ab49c459228fce0c61 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Tue, 31 Jul 2018 08:41:55 +0200 Subject: [PATCH 026/102] Ensure tests run through tox are using the expected Python executable (#175) Otherwise, they are likely to use the system python. --- runtests.sh | 6 ++++-- tox.ini | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/runtests.sh b/runtests.sh index a435da6e1..9143a769b 100755 --- a/runtests.sh +++ b/runtests.sh @@ -2,6 +2,8 @@ set -e +PYTHON=${1:-python} + for x in examples/hlapi/asyncore/sync/manager/cmdgen/*.py \ examples/hlapi/asyncore/sync/agent/ntforg/*.py \ examples/hlapi/asyncore/manager/cmdgen/*.py \ @@ -19,7 +21,7 @@ do continue ;; *) - python "${x}" | tail -50 + $PYTHON "${x}" | tail -50 ;; esac -done \ No newline at end of file +done diff --git a/tox.ini b/tox.ini index af9834376..88539239c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [envlist] -envlist = py{26,27,33,34,35,36} +envlist = py{26,27,33,34,35,36,37} [testenv] -commands = {toxinidir}/runtests.sh +commands = {toxinidir}/runtests.sh {envpython} From 0d7f612468d78a58c3746a8336d1353763dd315b Mon Sep 17 00:00:00 2001 From: Michal Arbet Date: Tue, 31 Jul 2018 15:48:23 +0300 Subject: [PATCH 027/102] Fix py3.7 syntax error caused by async keyword (#180) As async is the keyword since Python 3.7, let's use gettattr built-in function to call async function from asyncio. --- pysnmp/carrier/asyncio/dgram/base.py | 4 ++-- pysnmp/carrier/asyncio/dispatch.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pysnmp/carrier/asyncio/dgram/base.py b/pysnmp/carrier/asyncio/dgram/base.py index e06e8ee5d..93e584570 100644 --- a/pysnmp/carrier/asyncio/dgram/base.py +++ b/pysnmp/carrier/asyncio/dgram/base.py @@ -90,7 +90,7 @@ def openClientMode(self, iface=None): if IS_PYTHON_344_PLUS: self._lport = asyncio.ensure_future(c) else: # pragma: no cover - self._lport = asyncio.async(c) + self._lport = getattr(asyncio, 'async')(c) except Exception: raise error.CarrierError(';'.join(traceback.format_exception(*sys.exc_info()))) @@ -101,7 +101,7 @@ def openServerMode(self, iface): c = self.loop.create_datagram_endpoint( lambda: self, local_addr=iface, family=self.sockFamily ) - self._lport = asyncio.async(c) + self._lport = getattr(asyncio, 'async')(c) except Exception: raise error.CarrierError(';'.join(traceback.format_exception(*sys.exc_info()))) return self diff --git a/pysnmp/carrier/asyncio/dispatch.py b/pysnmp/carrier/asyncio/dispatch.py index 2ba4cf4d6..74f9ae10f 100644 --- a/pysnmp/carrier/asyncio/dispatch.py +++ b/pysnmp/carrier/asyncio/dispatch.py @@ -75,7 +75,7 @@ def registerTransport(self, tDomain, transport): if IS_PYTHON_344_PLUS: self.loopingcall = asyncio.ensure_future(self.handle_timeout()) else: # pragma: no cover - self.loopingcall = asyncio.async(self.handle_timeout()) + self.loopingcall = getattr(asyncio, 'async')(self.handle_timeout()) AbstractTransportDispatcher.registerTransport( self, tDomain, transport ) From 7abfa51a9993e3a79404990f844d655b3bd3ba26 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 4 Aug 2018 20:24:26 +0200 Subject: [PATCH 028/102] Fix crash on wrong SNMPv3 security model Fixed crash caused by incoming SNMPv3 message requesting SNMPv1/v2c security model --- CHANGES.txt | 2 ++ pysnmp/proto/mpmod/rfc2576.py | 2 +- pysnmp/proto/mpmod/rfc3412.py | 5 ++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f57aa6a8a..ecdd55875 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,6 +7,8 @@ Revision 4.4.5, released 2018-04-XX - Fixed possible infinite loop in GETBULK response PDU builder - Fixed memory leak in the T`config.delContext()` VACM management harness - Fixed `Bits` class initialization when enumeration values are given +- Fixed crash caused by incoming SNMPv3 message requesting SNMPv1/v2c + security model Revision 4.4.4, released 2018-01-03 ----------------------------------- diff --git a/pysnmp/proto/mpmod/rfc2576.py b/pysnmp/proto/mpmod/rfc2576.py index 93ded6da0..717110d34 100644 --- a/pysnmp/proto/mpmod/rfc2576.py +++ b/pysnmp/proto/mpmod/rfc2576.py @@ -271,7 +271,7 @@ def prepareDataElements(self, snmpEngine, transportDomain, try: try: - smHandler = snmpEngine.securityModels[int(securityModel)] + smHandler = snmpEngine.securityModels[securityModel] except KeyError: raise error.StatusInformation( diff --git a/pysnmp/proto/mpmod/rfc3412.py b/pysnmp/proto/mpmod/rfc3412.py index 5f9268a56..58f3acc08 100644 --- a/pysnmp/proto/mpmod/rfc3412.py +++ b/pysnmp/proto/mpmod/rfc3412.py @@ -40,8 +40,11 @@ class HeaderData(univ.Sequence): namedtype.NamedType('msgMaxSize', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(484, 2147483647))), namedtype.NamedType('msgFlags', univ.OctetString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, 1))), + # NOTE (etingof): constrain SNMPv3 message to only USM+ security models + # because SNMPv1/v2c seems incompatible in pysnmp implementation, not sure + # if it's intended by the SNMP standard at all... namedtype.NamedType('msgSecurityModel', - univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(1, 2147483647))) + univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(3, 2147483647))) ) From ee321e04796d0a6874ae85385a5df00aa76f8066 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 5 Aug 2018 09:52:42 +0200 Subject: [PATCH 029/102] Fix out-of-scope OID leak in hlapi table (#172) Fixed out-of-scope OIDs possibly leaking at the end of SNMP table at hlapi `nextCmd` and `bulkCmd` calls when `lexicographicMode = False`. --- CHANGES.txt | 2 ++ pysnmp/hlapi/asyncore/sync/cmdgen.py | 40 +++++++++++++++++++++------- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ecdd55875..009f6f341 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -9,6 +9,8 @@ Revision 4.4.5, released 2018-04-XX - Fixed `Bits` class initialization when enumeration values are given - Fixed crash caused by incoming SNMPv3 message requesting SNMPv1/v2c security model +- Fixed out-of-scope OIDs leaking at the end of SNMP table at hlapi + `nextCmd` and `bulkCmd` calls when `lexicographicMode = False` Revision 4.4.4, released 2018-01-03 ----------------------------------- diff --git a/pysnmp/hlapi/asyncore/sync/cmdgen.py b/pysnmp/hlapi/asyncore/sync/cmdgen.py index fb0723f4e..3868ac66b 100644 --- a/pysnmp/hlapi/asyncore/sync/cmdgen.py +++ b/pysnmp/hlapi/asyncore/sync/cmdgen.py @@ -351,6 +351,8 @@ def cbFun(snmpEngine, sendRequestHandle, totalRows = totalCalls = 0 while True: + previousVarBinds = varBinds + if varBinds: cmdgen.nextCmd(snmpEngine, authData, transportTarget, contextData, *[(x[0], Null('')) for x in varBinds], @@ -378,13 +380,22 @@ def cbFun(snmpEngine, sendRequestHandle, yield (errorIndication, errorStatus, errorIndex, varBinds) return else: + stopFlag = True + varBinds = cbCtx['varBindTable'] and cbCtx['varBindTable'][0] - for idx, varBind in enumerate(varBinds): + + for col, varBind in enumerate(varBinds): name, val = varBind - if not isinstance(val, Null): - if lexicographicMode or initialVars[idx].isPrefixOf(name): - break - else: + if isinstance(val, Null): + varBinds[col] = previousVarBinds[col][0], endOfMibView + + if not lexicographicMode and not initialVars[col].isPrefixOf(name): + varBinds[col] = previousVarBinds[col][0], endOfMibView + + if stopFlag and varBinds[col][1] is not endOfMibView: + stopFlag = False + + if stopFlag: return totalRows += 1 @@ -545,6 +556,8 @@ def cbFun(snmpEngine, sendRequestHandle, if maxRows and totalRows < maxRows: maxRepetitions = min(maxRepetitions, maxRows - totalRows) + previousVarBinds = varBinds + cmdgen.bulkCmd(snmpEngine, authData, transportTarget, contextData, nonRepeaters, maxRepetitions, *[(x[0], Null('')) for x in varBinds], @@ -583,14 +596,17 @@ def cbFun(snmpEngine, sendRequestHandle, break for col in range(len(varBindTable[row])): name, val = varBindTable[row][col] + if row: + previousVarBinds = varBindTable[row - 1] if nullVarBinds[col]: - varBindTable[row][col] = name, endOfMibView + varBindTable[row][col] = previousVarBinds[col][0], endOfMibView continue stopFlag = False if isinstance(val, Null): + varBindTable[row][col] = previousVarBinds[col][0], endOfMibView nullVarBinds[col] = True - elif not lexicographicMode and not initialVars[col].isPrefixOf(name): - varBindTable[row][col] = name, endOfMibView + if not lexicographicMode and not initialVars[col].isPrefixOf(name): + varBindTable[row][col] = previousVarBinds[col][0], endOfMibView nullVarBinds[col] = True if stopFlag: varBindTable = row and varBindTable[:row - 1] or [] @@ -607,9 +623,13 @@ def cbFun(snmpEngine, sendRequestHandle, if maxCalls and totalCalls >= maxCalls: stopFlag = True - for varBinds in varBindTable: - initialVarBinds = (yield errorIndication, errorStatus, errorIndex, varBinds) + varBinds = varBindTable and varBindTable[-1] or [] + + for varBindRow in varBindTable: + initialVarBinds = (yield errorIndication, errorStatus, errorIndex, varBindRow) if initialVarBinds: varBinds = initialVarBinds initialVars = [x[0] for x in vbProcessor.makeVarBinds(snmpEngine, varBinds)] + nullVarBinds = [False] * len(initialVars) + From 36f7584402aa8f1c3da538f2f331e980344c719a Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 5 Aug 2018 09:56:41 +0200 Subject: [PATCH 030/102] Add PySnmpError.cause attribute (#168) To convey parent exception information on re-raise --- CHANGES.txt | 1 + pysnmp/error.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 009f6f341..66a78e9d7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,6 +2,7 @@ Revision 4.4.5, released 2018-04-XX ----------------------------------- +- Added PySnmpError.cause attribute holding parent exception tuple - Fixed broken InetAddressType rendering caused by a pyasn1 regression - Fixed typo in RFC1158 module - Fixed possible infinite loop in GETBULK response PDU builder diff --git a/pysnmp/error.py b/pysnmp/error.py index 3f546c79b..b5bb35b51 100644 --- a/pysnmp/error.py +++ b/pysnmp/error.py @@ -5,6 +5,10 @@ # License: http://snmplabs.com/pysnmp/license.html # +import sys + class PySnmpError(Exception): - pass + def __init__(self, message): + self.cause = sys.exc_info() + Exception.__init__(self, '%s, caused by %s: %s' % (message, self.cause[0], self.cause[1])) From d72ad91b3a356e09479ca1b30b36e323ea0bdb8f Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 5 Aug 2018 10:24:50 +0200 Subject: [PATCH 031/102] Fix PySnmpError implementation This is a follow up fix to make PySnmpError properly overriding base Exception call signature --- pysnmp/error.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pysnmp/error.py b/pysnmp/error.py index b5bb35b51..e47f4bcb4 100644 --- a/pysnmp/error.py +++ b/pysnmp/error.py @@ -9,6 +9,15 @@ class PySnmpError(Exception): - def __init__(self, message): + def __init__(self, *args): + msg = args and str(args[0]) or '' + self.cause = sys.exc_info() - Exception.__init__(self, '%s, caused by %s: %s' % (message, self.cause[0], self.cause[1])) + + if self.cause[0]: + msg += 'caused by %s: %s' % (self.cause[0], self.cause[1]) + + if msg: + args = (msg,) + args[1:] + + Exception.__init__(self, *args) From 44ab5d3a77e2db81a78f68ec56a50aecbcf54bb8 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 5 Aug 2018 21:08:19 +0200 Subject: [PATCH 032/102] release 4.4.5 --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 66a78e9d7..089d31979 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,5 @@ -Revision 4.4.5, released 2018-04-XX +Revision 4.4.5, released 2018-08-05 ----------------------------------- - Added PySnmpError.cause attribute holding parent exception tuple From d9f5fd2efb8ef91fc7d6ab4d2b11a5f831371d53 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 5 Aug 2018 21:15:29 +0200 Subject: [PATCH 033/102] changelog typo --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 089d31979..3c799d216 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,7 +6,7 @@ Revision 4.4.5, released 2018-08-05 - Fixed broken InetAddressType rendering caused by a pyasn1 regression - Fixed typo in RFC1158 module - Fixed possible infinite loop in GETBULK response PDU builder -- Fixed memory leak in the T`config.delContext()` VACM management harness +- Fixed memory leak in the `config.delContext()` VACM management harness - Fixed `Bits` class initialization when enumeration values are given - Fixed crash caused by incoming SNMPv3 message requesting SNMPv1/v2c security model From 43a181b692b36c6d7f82dad89e4b48864beb282e Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 5 Aug 2018 21:27:08 +0200 Subject: [PATCH 034/102] Rename LICENSE.txt -> LICENSE.rst --- LICENSE.txt => LICENSE.rst | 0 README.md | 2 +- docs/source/license.rst | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename LICENSE.txt => LICENSE.rst (100%) diff --git a/LICENSE.txt b/LICENSE.rst similarity index 100% rename from LICENSE.txt rename to LICENSE.rst diff --git a/README.md b/README.md index 9c6a726bb..e1f68100b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ SNMP library for Python [![PyPI](https://img.shields.io/pypi/v/pysnmp.svg?maxAge=2592000)](https://pypi.python.org/pypi/pysnmp) [![Python Versions](https://img.shields.io/pypi/pyversions/pysnmp.svg)](https://pypi.python.org/pypi/pysnmp/) [![Build status](https://travis-ci.org/etingof/pysnmp.svg?branch=master)](https://secure.travis-ci.org/etingof/pysnmp) -[![GitHub license](https://img.shields.io/badge/license-BSD-blue.svg)](https://raw.githubusercontent.com/etingof/pysnmp/master/LICENSE.txt) +[![GitHub license](https://img.shields.io/badge/license-BSD-blue.svg)](https://raw.githubusercontent.com/etingof/pysnmp/master/LICENSE.rst) This is a pure-Python, open source and free implementation of v1/v2c/v3 SNMP engine distributed under 2-clause [BSD license](http://snmplabs.com/pysnmp/license.html). diff --git a/docs/source/license.rst b/docs/source/license.rst index 829144f8a..ffc13bd70 100644 --- a/docs/source/license.rst +++ b/docs/source/license.rst @@ -2,4 +2,4 @@ License ======= -.. include:: ../../LICENSE.txt +.. include:: ../../LICENSE.rst From 8a3727a9fc5d0c075c7cb4d1c4ced1e252d47c28 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 5 Aug 2018 22:47:36 +0200 Subject: [PATCH 035/102] Fix Python 2.4-2.5 except statement Older Pythons do not support 'except ... as...' syntax. --- pysnmp/hlapi/asyncio/cmdgen.py | 23 +++++++++++++++-------- pysnmp/hlapi/asyncio/ntforg.py | 8 ++++++-- pysnmp/hlapi/twisted/cmdgen.py | 22 ++++++++++++++-------- pysnmp/hlapi/twisted/ntforg.py | 7 +++++-- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/pysnmp/hlapi/asyncio/cmdgen.py b/pysnmp/hlapi/asyncio/cmdgen.py index e449d4771..8c69fd195 100644 --- a/pysnmp/hlapi/asyncio/cmdgen.py +++ b/pysnmp/hlapi/asyncio/cmdgen.py @@ -31,6 +31,8 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. # +import sys + from pysnmp.smi.rfc1902 import * from pysnmp.hlapi.auth import * from pysnmp.hlapi.context import * @@ -41,6 +43,7 @@ try: import asyncio + except ImportError: import trollius as asyncio @@ -137,8 +140,9 @@ def __cbFun(snmpEngine, sendRequestHandle, try: varBindsUnmade = vbProcessor.unmakeVarBinds(snmpEngine, varBinds, lookupMib) - except Exception as e: - future.set_exception(e) + except Exception: + ex = sys.exc_info()[1] + future.set_exception(ex) else: future.set_result( (errorIndication, errorStatus, errorIndex, varBindsUnmade) @@ -242,8 +246,9 @@ def __cbFun(snmpEngine, sendRequestHandle, try: varBindsUnmade = vbProcessor.unmakeVarBinds(snmpEngine, varBinds, lookupMib) - except Exception as e: - future.set_exception(e) + except Exception: + ex = sys.exc_info()[1] + future.set_exception(ex) else: future.set_result( (errorIndication, errorStatus, errorIndex, varBindsUnmade) @@ -353,8 +358,9 @@ def __cbFun(snmpEngine, sendRequestHandle, varBindTableRow, lookupMib) for varBindTableRow in varBindTable] - except Exception as e: - future.set_exception(e) + except Exception: + ex = sys.exc_info()[1] + future.set_exception(ex) else: future.set_result( (errorIndication, errorStatus, errorIndex, varBindsUnmade) @@ -493,8 +499,9 @@ def __cbFun(snmpEngine, sendRequestHandle, varBindTableRow, lookupMib) for varBindTableRow in varBindTable] - except Exception as e: - future.set_exception(e) + except Exception: + ex = sys.exc_info()[1] + future.set_exception(ex) else: future.set_result( (errorIndication, errorStatus, errorIndex, varBindsUnmade) diff --git a/pysnmp/hlapi/asyncio/ntforg.py b/pysnmp/hlapi/asyncio/ntforg.py index 0bd59b7e8..b4f6fb3a0 100644 --- a/pysnmp/hlapi/asyncio/ntforg.py +++ b/pysnmp/hlapi/asyncio/ntforg.py @@ -8,6 +8,8 @@ # Authors: Matt Hooks # Zachary Lorusso # +import sys + from pysnmp.smi.rfc1902 import * from pysnmp.hlapi.auth import * from pysnmp.hlapi.context import * @@ -18,6 +20,7 @@ try: import asyncio + except ImportError: import trollius as asyncio @@ -127,8 +130,9 @@ def __cbFun(snmpEngine, sendRequestHandle, try: varBindsUnmade = vbProcessor.unmakeVarBinds(snmpEngine, varBinds, lookupMib) - except Exception as e: - future.set_exception(e) + except Exception: + ex = sys.exc_info()[1] + future.set_exception(ex) else: future.set_result( (errorIndication, errorStatus, errorIndex, varBindsUnmade) diff --git a/pysnmp/hlapi/twisted/cmdgen.py b/pysnmp/hlapi/twisted/cmdgen.py index 944905275..9cfb98226 100644 --- a/pysnmp/hlapi/twisted/cmdgen.py +++ b/pysnmp/hlapi/twisted/cmdgen.py @@ -4,6 +4,8 @@ # Copyright (c) 2005-2018, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # +import sys + from pysnmp.smi.rfc1902 import * from pysnmp.hlapi.auth import * from pysnmp.hlapi.context import * @@ -120,8 +122,9 @@ def __cbFun(snmpEngine, sendRequestHandle, try: varBindsUnmade = vbProcessor.unmakeVarBinds(snmpEngine, varBinds, lookupMib) - except Exception as e: - deferred.errback(Failure(e)) + except Exception: + ex = sys.exc_info()[1] + deferred.errback(Failure(ex)) else: deferred.callback((errorStatus, errorIndex, varBindsUnmade)) @@ -236,8 +239,9 @@ def __cbFun(snmpEngine, sendRequestHandle, try: varBindsUnmade = vbProcessor.unmakeVarBinds(snmpEngine, varBinds, lookupMib) - except Exception as e: - deferred.errback(Failure(e)) + except Exception: + ex = sys.exc_info()[1] + deferred.errback(Failure(ex)) else: deferred.callback((errorStatus, errorIndex, varBindsUnmade)) @@ -366,8 +370,9 @@ def __cbFun(snmpEngine, sendRequestHandle, lookupMib) for varBindTableRow in varBindTable] - except Exception as e: - deferred.errback(Failure(e)) + except Exception: + ex = sys.exc_info()[1] + deferred.errback(Failure(ex)) else: deferred.callback((errorStatus, errorIndex, varBindsUnmade)) @@ -524,8 +529,9 @@ def __cbFun(snmpEngine, sendRequestHandle, lookupMib) for varBindTableRow in varBindTable] - except Exception as e: - deferred.errback(Failure(e)) + except Exception: + ex = sys.exc_info()[1] + deferred.errback(Failure(ex)) else: deferred.callback((errorStatus, errorIndex, varBindsUnmade)) diff --git a/pysnmp/hlapi/twisted/ntforg.py b/pysnmp/hlapi/twisted/ntforg.py index fbbdc0f1b..0b8f60530 100644 --- a/pysnmp/hlapi/twisted/ntforg.py +++ b/pysnmp/hlapi/twisted/ntforg.py @@ -4,6 +4,8 @@ # Copyright (c) 2005-2018, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # +import sys + from pysnmp.smi.rfc1902 import * from pysnmp.hlapi.auth import * from pysnmp.hlapi.context import * @@ -128,8 +130,9 @@ def __cbFun(snmpEngine, sendRequestHandle, try: varBindsUnmade = vbProcessor.unmakeVarBinds(snmpEngine, varBinds, lookupMib) - except Exception as e: - deferred.errback(Failure(e)) + except Exception: + ex = sys.exc_info()[1] + deferred.errback(Failure(ex)) else: deferred.callback((errorStatus, errorIndex, varBindsUnmade)) From 98e2a3e838bf851667020b54ae8d5bdab157f5ce Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Mon, 6 Aug 2018 23:01:36 +0200 Subject: [PATCH 036/102] prepare 4.4.6 --- CHANGES.txt | 5 +++++ pysnmp/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3c799d216..da779c55e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,9 @@ +Revision 4.4.6, released 2018-08-XX +----------------------------------- + +No changes so far + Revision 4.4.5, released 2018-08-05 ----------------------------------- diff --git a/pysnmp/__init__.py b/pysnmp/__init__.py index 871cc4edf..524fe0fac 100644 --- a/pysnmp/__init__.py +++ b/pysnmp/__init__.py @@ -1,5 +1,5 @@ # http://www.python.org/dev/peps/pep-0396/ -__version__ = '4.4.5' +__version__ = '4.4.6' # backward compatibility version = tuple([int(x) for x in __version__.split('.')]) majorVersionId = version[0] From be8d2caccf94ebbe00e11c739fb28a3b70d39ddc Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Mon, 6 Aug 2018 23:18:42 +0200 Subject: [PATCH 037/102] Improve package build and dependency tracking --- CHANGES.txt | 2 +- setup.py | 34 +++++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index da779c55e..1aaaff578 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,7 +2,7 @@ Revision 4.4.6, released 2018-08-XX ----------------------------------- -No changes so far +- Improved package build and dependency tracking Revision 4.4.5, released 2018-08-05 ----------------------------------- diff --git a/setup.py b/setup.py index cfc4d89eb..87e56953d 100644 --- a/setup.py +++ b/setup.py @@ -7,6 +7,7 @@ """ import sys import os +import re classifiers = """\ Development Status :: 5 - Production/Stable @@ -55,25 +56,44 @@ def howto_install_setuptools(): print("ERROR: this package requires Python 2.4 or later!") sys.exit(1) +requires = [ln.strip() for ln in open('requirements.txt').readlines()] + try: - from setuptools import setup + import setuptools + + setup, Command = setuptools.setup, setuptools.Command + + observed_version = [int(x) for x in setuptools.__version__.split('.')] + required_version = [36, 2, 0] + + # NOTE(etingof): require fresh setuptools to build proper wheels + # See also: https://hynek.me/articles/conditional-python-dependencies/ + if ('bdist_wheel' in sys.argv and + observed_version < required_version): + print("ERROR: your wheels won't come out round with setuptools %s! " + "Upgrade to %s and try again." % ( + '.'.join([str(x) for x in observed_version]), + '.'.join([str(x) for x in required_version]))) + sys.exit(1) params = { - 'install_requires': ['pyasn1>=0.2.3', 'pysmi', 'pycryptodomex'], + 'install_requires': requires, 'zip_safe': True } except ImportError: - for arg in sys.argv: - if 'egg' in arg: - howto_install_setuptools() - sys.exit(1) + if 'bdist_wheel' in sys.argv or 'bdist_egg' in sys.argv: + howto_install_setuptools() + sys.exit(1) from distutils.core import setup params = {} + if sys.version_info[:2] > (2, 4): - params['requires'] = ['pyasn1(>=0.2.3)', 'pysmi', 'pycryptodomex'] + params['requires'] = [ + re.sub(r'(.*?)([<>=!~]+)(.*)', r'\g<1>\g<2>(\g<3>)', r) for r in requires + ] doclines = [x.strip() for x in (__doc__ or '').split('\n') if x] From b0bbd37e8208da64fb1ca9d32af2d4fa4803bf4d Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Mon, 6 Aug 2018 23:22:48 +0200 Subject: [PATCH 038/102] Improve long description in Trove --- setup.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 87e56953d..0d987d5c2 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,9 @@ #!/usr/bin/env python """SNMP library for Python - SNMP v1/v2c/v3 engine and apps written in pure-Python. - Supports Manager/Agent/Proxy roles, scriptable MIBs, - asynchronous operation and multiple transports. +SNMP v1/v2c/v3 engine and Standard Applications suite written in pure-Python. +Supports Manager/Agent/Proxy roles, Manager/Agent-side MIBs, asynchronous +operation and multiple network transports. """ import sys import os @@ -95,13 +95,13 @@ def howto_install_setuptools(): re.sub(r'(.*?)([<>=!~]+)(.*)', r'\g<1>\g<2>(\g<3>)', r) for r in requires ] -doclines = [x.strip() for x in (__doc__ or '').split('\n') if x] +doclines = [x.strip() for x in (__doc__ or '').split('\n')] params.update({ 'name': 'pysnmp', 'version': open(os.path.join('pysnmp', '__init__.py')).read().split('\'')[1], 'description': doclines[0], - 'long_description': ' '.join(doclines[1:]), + 'long_description': '\n'.join(doclines[1:]), 'maintainer': 'Ilya Etingof ', 'author': 'Ilya Etingof', 'author_email': 'etingof@gmail.com', From 34836cc48453abcc3f9724b26a798b903f40e31b Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Wed, 8 Aug 2018 07:55:56 +0200 Subject: [PATCH 039/102] Recover missing LICENSE in tarball --- CHANGES.txt | 1 + MANIFEST.in | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1aaaff578..948c14275 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,6 +3,7 @@ Revision 4.4.6, released 2018-08-XX ----------------------------------- - Improved package build and dependency tracking +- Fixed missing LICENSE from the tarball distribution Revision 4.4.5, released 2018-08-05 ----------------------------------- diff --git a/MANIFEST.in b/MANIFEST.in index c72432c29..21af931bd 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include *.txt *.md *.sh +include *.rst *.txt *.md *.sh recursive-include examples *.py recursive-include docs/source *.rst *.svg *.py recursive-include docs/mibs *.txt From daa2ce01719ae53cc7b40a7b55f8878ca9dd4aa6 Mon Sep 17 00:00:00 2001 From: Ryan Van Gilder Date: Sun, 9 Sep 2018 00:29:32 -0700 Subject: [PATCH 040/102] Fix lcd.unconfigure not removing cached addr value preventing the same target being re-configured (#194) --- pysnmp/hlapi/lcd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pysnmp/hlapi/lcd.py b/pysnmp/hlapi/lcd.py index bcd69d87d..e5d18ec17 100644 --- a/pysnmp/hlapi/lcd.py +++ b/pysnmp/hlapi/lcd.py @@ -175,7 +175,7 @@ def unconfigure(self, snmpEngine, authData=None): cache['addr'][addrKey] = addrName, useCount else: config.delTargetAddr(snmpEngine, addrName) - + del cache['addr'][addrKey] addrNames.add(addrKey) if addrKey[1] in cache['tran']: From fcd4435dd8333c6994330386b815db23c4d4de26 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 9 Sep 2018 09:40:04 +0200 Subject: [PATCH 041/102] Add a CHANGELOG entry for previous fix --- CHANGES.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 948c14275..c691e96ee 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,9 @@ Revision 4.4.6, released 2018-08-XX - Improved package build and dependency tracking - Fixed missing LICENSE from the tarball distribution +- Fixed `CommandGeneratorLcdConfigurator.unconfigure()` to fully clean up + internal caches, otherwise repetitive attempts to configure the target + would fail. Revision 4.4.5, released 2018-08-05 ----------------------------------- From 2a9466dbe4da15757f888eb88200923a57ca09b0 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 9 Sep 2018 11:03:25 +0200 Subject: [PATCH 042/102] Tolerate duplicate enumerations Possible duplicate enumerations in `Bits` and `Integer` SMI types causes pyasn1 exception. This fix reduces duplicates prior to passing them to pyasn1. --- CHANGES.txt | 2 ++ pysnmp/proto/rfc1902.py | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c691e96ee..dd86b5895 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,6 +7,8 @@ Revision 4.4.6, released 2018-08-XX - Fixed `CommandGeneratorLcdConfigurator.unconfigure()` to fully clean up internal caches, otherwise repetitive attempts to configure the target would fail. +- Fix to tolerate possible duplicate enumerations in `Bits` and `Integer` + SMI types. Revision 4.4.5, released 2018-08-05 ----------------------------------- diff --git a/pysnmp/proto/rfc1902.py b/pysnmp/proto/rfc1902.py index 3370f4788..ee82ef5a8 100644 --- a/pysnmp/proto/rfc1902.py +++ b/pysnmp/proto/rfc1902.py @@ -128,12 +128,17 @@ class Integer(Integer32): @classmethod def withNamedValues(cls, **values): - """Creates a subclass with discreet named values constraint. + """Create a subclass with discreet named values constraint. + + Reduce fully duplicate enumerations along the way. """ + enums = set(cls.namedValues.items()) + enums.update(values.items()) class X(cls): - namedValues = cls.namedValues + namedval.NamedValues(*values.items()) - subtypeSpec = cls.subtypeSpec + constraint.SingleValueConstraint(*values.values()) + namedValues = namedval.NamedValues(*enums) + subtypeSpec = cls.subtypeSpec + constraint.SingleValueConstraint( + *values.values()) X.__name__ = cls.__name__ return X @@ -637,10 +642,14 @@ def prettyOut(self, value): @classmethod def withNamedBits(cls, **values): """Creates a subclass with discreet named bits constraint. + + Reduce fully duplicate enumerations along the way. """ + enums = set(cls.namedValues.items()) + enums.update(values.items()) class X(cls): - namedValues = cls.namedValues + namedval.NamedValues(*values.items()) + namedValues = namedval.NamedValues(*enums) X.__name__ = cls.__name__ return X From 51a15bdaa0d64ea1bfcce5c461ebdbe54ac701d7 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Thu, 13 Sep 2018 18:45:27 +0200 Subject: [PATCH 043/102] Tolerate non-initialised entries in SNMP community table It can happen that SNMP community table contains uninitialized entries. These entries may stop internal SNMP community table indexing which is done in rfc2576 to speed up SNMP engine operations when SNMPv1/v2c is involved. Once a bad entry gets into SNMP community table, all the rest queries would start failing. This patch ignores incomplete SNMP community table entries in the course of building indices. --- pysnmp/proto/secmod/rfc2576.py | 100 ++++++++++++++++++++++++--------- 1 file changed, 75 insertions(+), 25 deletions(-) diff --git a/pysnmp/proto/secmod/rfc2576.py b/pysnmp/proto/secmod/rfc2576.py index 69794272f..3e4b96c18 100644 --- a/pysnmp/proto/secmod/rfc2576.py +++ b/pysnmp/proto/secmod/rfc2576.py @@ -38,7 +38,7 @@ def _sec2com(self, snmpEngine, securityName, contextEngineId, contextName): nextMibNode = snmpTargetParamsSecurityName - while 1: + while True: try: nextMibNode = snmpTargetParamsSecurityName.getNextNode(nextMibNode.name) @@ -49,10 +49,18 @@ def _sec2com(self, snmpEngine, securityName, contextEngineId, contextName): mibNode = snmpTargetParamsSecurityModel.getNode(snmpTargetParamsSecurityModel.name + instId) - if mibNode.syntax not in self.__nameToModelMap: - self.__nameToModelMap[nextMibNode.syntax] = set() + try: + if mibNode.syntax not in self.__nameToModelMap: + self.__nameToModelMap[nextMibNode.syntax] = set() + + self.__nameToModelMap[nextMibNode.syntax].add(mibNode.syntax) - self.__nameToModelMap[nextMibNode.syntax].add(mibNode.syntax) + except PyAsn1Error: + debug.logger & debug.flagSM and debug.logger( + '_sec2com: table entries %r/%r hashing failed' % ( + nextMibNode.syntax, mibNode.syntax) + ) + continue self.__paramsBranchId = snmpTargetParamsSecurityName.branchVersionId @@ -72,7 +80,8 @@ def _sec2com(self, snmpEngine, securityName, contextEngineId, contextName): self.__securityMap = {} nextMibNode = snmpCommunityName - while 1: + + while True: try: nextMibNode = snmpCommunityName.getNextNode(nextMibNode.name) @@ -88,9 +97,17 @@ def _sec2com(self, snmpEngine, securityName, contextEngineId, contextName): _contextName = snmpCommunityContextName.getNode(snmpCommunityContextName.name + instId).syntax - self.__securityMap[(_securityName, - _contextEngineId, - _contextName)] = nextMibNode.syntax + try: + self.__securityMap[(_securityName, + _contextEngineId, + _contextName)] = nextMibNode.syntax + + except PyAsn1Error: + debug.logger & debug.flagSM and debug.logger( + '_sec2com: table entries %r/%r/%r hashing failed' % ( + _securityName, _contextEngineId, _contextName) + ) + continue self.__securityBranchId = snmpCommunityName.branchVersionId @@ -123,11 +140,14 @@ def _com2sec(self, snmpEngine, communityName, transportInformation): self.__transportToTagMap = {} nextMibNode = snmpTargetAddrTagList + while True: try: nextMibNode = snmpTargetAddrTagList.getNextNode(nextMibNode.name) + except NoSuchInstanceError: break + instId = nextMibNode.name[len(snmpTargetAddrTagList.name):] targetAddrTDomain = snmpTargetAddrTDomain.getNode(snmpTargetAddrTDomain.name + instId).syntax targetAddrTAddress = snmpTargetAddrTAddress.getNode(snmpTargetAddrTAddress.name + instId).syntax @@ -144,17 +164,29 @@ def _com2sec(self, snmpEngine, communityName, transportInformation): targetAddrTAddress = tuple(TransportAddressIPv6(targetAddrTAddress)) elif targetAddrTDomain[:len(unix.snmpLocalDomain)] == unix.snmpLocalDomain: targetAddrTAddress = str(targetAddrTAddress) + targetAddr = targetAddrTDomain, targetAddrTAddress targetAddrTagList = snmpTargetAddrTagList.getNode(snmpTargetAddrTagList.name + instId).syntax + if targetAddr not in self.__transportToTagMap: self.__transportToTagMap[targetAddr] = set() - if targetAddrTagList: - self.__transportToTagMap[targetAddr].update( - [SnmpTagValue(x) - for x in targetAddrTagList.asOctets().split()] + + try: + if targetAddrTagList: + self.__transportToTagMap[targetAddr].update( + [SnmpTagValue(x) + for x in targetAddrTagList.asOctets().split()] + ) + + else: + self.__transportToTagMap[targetAddr].add(self.__emptyTag) + + except PyAsn1Error: + debug.logger & debug.flagSM and debug.logger( + '_com2sec: table entries %r/%r hashing failed' % ( + targetAddr, targetAddrTagList) ) - else: - self.__transportToTagMap[targetAddr].add(self.__emptyTag) + continue self.__transportBranchId = snmpTargetAddrTAddress.branchVersionId @@ -163,6 +195,7 @@ def _com2sec(self, snmpEngine, communityName, transportInformation): snmpTargetParamsSecurityName, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols( 'SNMP-TARGET-MIB', 'snmpTargetParamsSecurityName') + if self.__paramsBranchId != snmpTargetParamsSecurityName.branchVersionId: snmpTargetParamsSecurityModel, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols( 'SNMP-TARGET-MIB', 'snmpTargetParamsSecurityModel') @@ -182,10 +215,18 @@ def _com2sec(self, snmpEngine, communityName, transportInformation): mibNode = snmpTargetParamsSecurityModel.getNode(snmpTargetParamsSecurityModel.name + instId) - if nextMibNode.syntax not in self.__nameToModelMap: - self.__nameToModelMap[nextMibNode.syntax] = set() + try: + if nextMibNode.syntax not in self.__nameToModelMap: + self.__nameToModelMap[nextMibNode.syntax] = set() + + self.__nameToModelMap[nextMibNode.syntax].add(mibNode.syntax) - self.__nameToModelMap[nextMibNode.syntax].add(mibNode.syntax) + except PyAsn1Error: + debug.logger & debug.flagSM and debug.logger( + '_com2sec: table entries %r/%r hashing failed' % ( + nextMibNode.syntax, mibNode.syntax) + ) + continue self.__paramsBranchId = snmpTargetParamsSecurityName.branchVersionId @@ -211,6 +252,7 @@ def _com2sec(self, snmpEngine, communityName, transportInformation): self.__tagAndCommunityToSecurityMap = {} nextMibNode = snmpCommunityName + while True: try: nextMibNode = snmpCommunityName.getNextNode(nextMibNode.name) @@ -231,17 +273,25 @@ def _com2sec(self, snmpEngine, communityName, transportInformation): _tagAndCommunity = transportTag, nextMibNode.syntax - if _tagAndCommunity not in self.__tagAndCommunityToSecurityMap: - self.__tagAndCommunityToSecurityMap[_tagAndCommunity] = set() + try: + if _tagAndCommunity not in self.__tagAndCommunityToSecurityMap: + self.__tagAndCommunityToSecurityMap[_tagAndCommunity] = set() - self.__tagAndCommunityToSecurityMap[_tagAndCommunity].add( - (securityName, contextEngineId, contextName) - ) + self.__tagAndCommunityToSecurityMap[_tagAndCommunity].add( + (securityName, contextEngineId, contextName) + ) + + if nextMibNode.syntax not in self.__communityToTagMap: + self.__communityToTagMap[nextMibNode.syntax] = set() - if nextMibNode.syntax not in self.__communityToTagMap: - self.__communityToTagMap[nextMibNode.syntax] = set() + self.__communityToTagMap[nextMibNode.syntax].add(transportTag) - self.__communityToTagMap[nextMibNode.syntax].add(transportTag) + except PyAsn1Error: + debug.logger & debug.flagSM and debug.logger( + '_com2sec: table entries %r/%r hashing failed' % ( + _tagAndCommunity, nextMibNode.syntax) + ) + continue self.__communityBranchId = snmpCommunityName.branchVersionId From ddf30851798e8542bd98bd294785ecb79a5c1ae0 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Thu, 13 Sep 2018 22:36:35 +0200 Subject: [PATCH 044/102] Release 4.4.6 --- CHANGES.txt | 6 +++++- pysnmp/proto/rfc1902.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index dd86b5895..96fed964b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,5 @@ -Revision 4.4.6, released 2018-08-XX +Revision 4.4.6, released 2018-09-13 ----------------------------------- - Improved package build and dependency tracking @@ -9,6 +9,10 @@ Revision 4.4.6, released 2018-08-XX would fail. - Fix to tolerate possible duplicate enumerations in `Bits` and `Integer` SMI types. +- Fix to tolerate non-initialised entries in SNMP community table. Once a + bad entry sneaked into the SNMP community table, all the subsequent + SNMP v1/v2c operations failed. The fix ignores incomplete SNMP community + table entries in the course of building indices. Revision 4.4.5, released 2018-08-05 ----------------------------------- diff --git a/pysnmp/proto/rfc1902.py b/pysnmp/proto/rfc1902.py index ee82ef5a8..3caa81d7d 100644 --- a/pysnmp/proto/rfc1902.py +++ b/pysnmp/proto/rfc1902.py @@ -605,7 +605,7 @@ class Bits(OctetString): def __new__(cls, *args, **kwargs): if 'namedValues' in kwargs: - Bits = cls.withNamedBits(**kwargs.pop('namedValues')) + Bits = cls.withNamedBits(**dict(kwargs.pop('namedValues'))) return Bits(*args, **kwargs) return OctetString.__new__(cls) From 2d91fa167e07aba77ab3206addcd009b7d2c81d3 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 14 Sep 2018 01:43:17 +0200 Subject: [PATCH 045/102] Cut for 4.4.7 --- CHANGES.txt | 5 +++++ pysnmp/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 96fed964b..fc1e9991a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,9 @@ +Revision 4.4.7, released 2018-09-XX +----------------------------------- + +No changes so far + Revision 4.4.6, released 2018-09-13 ----------------------------------- diff --git a/pysnmp/__init__.py b/pysnmp/__init__.py index 524fe0fac..a9d069292 100644 --- a/pysnmp/__init__.py +++ b/pysnmp/__init__.py @@ -1,5 +1,5 @@ # http://www.python.org/dev/peps/pep-0396/ -__version__ = '4.4.6' +__version__ = '4.4.7' # backward compatibility version = tuple([int(x) for x in __version__.split('.')]) majorVersionId = version[0] From 250deca6ed1866699ae980238592c00a423e5a67 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Thu, 20 Sep 2018 10:44:11 +0200 Subject: [PATCH 046/102] Ensure distinct transports if timeout/retries differ Fix hlapi/v3arch transport target caching to ensure transport targets are different even if just timeout/retries options differ. --- CHANGES.txt | 3 ++- pysnmp/hlapi/lcd.py | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index fc1e9991a..b815edb38 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,7 +2,8 @@ Revision 4.4.7, released 2018-09-XX ----------------------------------- -No changes so far +- Fixed hlapi/v3arch transport target caching to ensure transport targets + are different even if just timeout/retries options differ Revision 4.4.6, released 2018-09-13 ----------------------------------- diff --git a/pysnmp/hlapi/lcd.py b/pysnmp/hlapi/lcd.py index e5d18ec17..1d61c491c 100644 --- a/pysnmp/hlapi/lcd.py +++ b/pysnmp/hlapi/lcd.py @@ -94,7 +94,10 @@ def configure(self, snmpEngine, authData, transportTarget, *options): transportKey = (paramsName, transportTarget.transportDomain, transportTarget.transportAddr, - transportTarget.tagList) + transportTarget.timeout, + transportTarget.retries, + transportTarget.tagList, + transportTarget.iface) if transportKey in cache['addr']: addrName, useCount = cache['addr'][transportKey] From c7f8654c98688b7967ce0dde9fa89b67ebe4b3d7 Mon Sep 17 00:00:00 2001 From: Fabrizio Vanni Date: Wed, 26 Sep 2018 11:12:30 +0200 Subject: [PATCH 047/102] Avoid deprecation warnings for asyncio.async() in server mode (#202) This is actually needed for Python 3.7 which introduces async and await as reserved keywords, see https://docs.python.org/3/whatsnew/3.7.html --- pysnmp/carrier/asyncio/dgram/base.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pysnmp/carrier/asyncio/dgram/base.py b/pysnmp/carrier/asyncio/dgram/base.py index 93e584570..af649db66 100644 --- a/pysnmp/carrier/asyncio/dgram/base.py +++ b/pysnmp/carrier/asyncio/dgram/base.py @@ -101,7 +101,11 @@ def openServerMode(self, iface): c = self.loop.create_datagram_endpoint( lambda: self, local_addr=iface, family=self.sockFamily ) - self._lport = getattr(asyncio, 'async')(c) + # Avoid deprecation warning for asyncio.async() + if IS_PYTHON_344_PLUS: + self._lport = asyncio.ensure_future(c) + else: # pragma: no cover + self._lport = getattr(asyncio, 'async')(c) except Exception: raise error.CarrierError(';'.join(traceback.format_exception(*sys.exc_info()))) return self From 4cf3317ce9c818e3d9f8c11b662159479bf318b3 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 6 Oct 2018 09:29:29 +0200 Subject: [PATCH 048/102] Reformat `isAccessAllowed()` for clarity --- pysnmp/proto/acmod/rfc3415.py | 43 ++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/pysnmp/proto/acmod/rfc3415.py b/pysnmp/proto/acmod/rfc3415.py index 1e14ab34f..3c5afe9e4 100644 --- a/pysnmp/proto/acmod/rfc3415.py +++ b/pysnmp/proto/acmod/rfc3415.py @@ -8,14 +8,14 @@ from pysnmp.proto import errind, error from pysnmp import debug -__powOfTwoSeq = [128, 64, 32, 16, 8, 4, 2, 1] - # 3.2 class Vacm(object): """View-based Access Control Model""" accessModelID = 3 + _powOfTwoSeq = (128, 64, 32, 16, 8, 4, 2, 1) + def isAccessAllowed(self, snmpEngine, securityModel, @@ -31,25 +31,30 @@ def isAccessAllowed(self, securityModel, securityName, securityLevel, viewType, contextName, variableName)) # 3.2.1 - vacmContextEntry, = mibInstrumController.mibBuilder.importSymbols('SNMP-VIEW-BASED-ACM-MIB', 'vacmContextEntry') + vacmContextEntry, = mibInstrumController.mibBuilder.importSymbols( + 'SNMP-VIEW-BASED-ACM-MIB', 'vacmContextEntry') + tblIdx = vacmContextEntry.getInstIdFromIndices(contextName) try: - vacmContextName = vacmContextEntry.getNode( + vacmContextEntry.getNode( vacmContextEntry.name + (1,) + tblIdx ).syntax + except NoSuchInstanceError: raise error.StatusInformation(errorIndication=errind.noSuchContext) # 3.2.2 - vacmSecurityToGroupEntry, = mibInstrumController.mibBuilder.importSymbols('SNMP-VIEW-BASED-ACM-MIB', - 'vacmSecurityToGroupEntry') + vacmSecurityToGroupEntry, = mibInstrumController.mibBuilder.importSymbols( + 'SNMP-VIEW-BASED-ACM-MIB', 'vacmSecurityToGroupEntry') tblIdx = vacmSecurityToGroupEntry.getInstIdFromIndices( securityModel, securityName ) + try: vacmGroupName = vacmSecurityToGroupEntry.getNode( vacmSecurityToGroupEntry.name + (3,) + tblIdx ).syntax + except NoSuchInstanceError: raise error.StatusInformation(errorIndication=errind.noGroupName) @@ -57,6 +62,7 @@ def isAccessAllowed(self, vacmAccessEntry, = mibInstrumController.mibBuilder.importSymbols( 'SNMP-VIEW-BASED-ACM-MIB', 'vacmAccessEntry' ) + # XXX partial context name match tblIdx = vacmAccessEntry.getInstIdFromIndices( vacmGroupName, contextName, securityModel, securityLevel @@ -74,51 +80,62 @@ def isAccessAllowed(self, try: viewName = vacmAccessEntry.getNode(entryIdx).syntax + except NoSuchInstanceError: raise error.StatusInformation(errorIndication=errind.noAccessEntry) - if not len(viewName): + + if not viewName: raise error.StatusInformation(errorIndication=errind.noSuchView) # XXX split onto object & instance ? # 3.2.5a - vacmViewTreeFamilyEntry, = mibInstrumController.mibBuilder.importSymbols('SNMP-VIEW-BASED-ACM-MIB', - 'vacmViewTreeFamilyEntry') + vacmViewTreeFamilyEntry, = mibInstrumController.mibBuilder.importSymbols( + 'SNMP-VIEW-BASED-ACM-MIB', 'vacmViewTreeFamilyEntry') tblIdx = vacmViewTreeFamilyEntry.getInstIdFromIndices(viewName) # Walk over entries initialTreeName = treeName = vacmViewTreeFamilyEntry.name + (2,) + tblIdx maskName = vacmViewTreeFamilyEntry.name + (3,) + tblIdx - while 1: + + while True: vacmViewTreeFamilySubtree = vacmViewTreeFamilyEntry.getNextNode( treeName ) vacmViewTreeFamilyMask = vacmViewTreeFamilyEntry.getNextNode( maskName ) + treeName = vacmViewTreeFamilySubtree.name maskName = vacmViewTreeFamilyMask.name + if initialTreeName != treeName[:len(initialTreeName)]: # 3.2.5b raise error.StatusInformation(errorIndication=errind.notInView) + l = len(vacmViewTreeFamilySubtree.syntax) if l > len(variableName): continue + if vacmViewTreeFamilyMask.syntax: mask = [] for c in vacmViewTreeFamilyMask.syntax.asNumbers(): - mask = mask + [b & c for b in __powOfTwoSeq] + mask.extend([b & c for b in self._powOfTwoSeq]) + m = len(mask) - 1 idx = l - 1 while idx: - if idx > m or mask[idx] and \ - vacmViewTreeFamilySubtree.syntax[idx] != variableName[idx]: + if (idx > m or mask[idx] and + vacmViewTreeFamilySubtree.syntax[idx] != variableName[idx]): break idx -= 1 + if idx: continue # no match + else: # no mask if vacmViewTreeFamilySubtree.syntax != variableName[:l]: continue # no match + # 3.2.5c return error.StatusInformation(errorIndication=errind.accessAllowed) From 1315e5e9799caed8be6d2eabb8601b2b136a5238 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 6 Oct 2018 20:00:41 +0200 Subject: [PATCH 049/102] Fix Sphinx markup for USM crypto algorithm IDs --- docs/source/docs/api-reference.rst | 10 ++++-- pysnmp/entity/config.py | 2 ++ pysnmp/hlapi/__init__.py | 51 +++++++++++++++++++++++++++++- pysnmp/hlapi/auth.py | 41 ++++++++++++++++-------- 4 files changed, 88 insertions(+), 16 deletions(-) diff --git a/docs/source/docs/api-reference.rst b/docs/source/docs/api-reference.rst index 8ff7d9584..d867db46e 100644 --- a/docs/source/docs/api-reference.rst +++ b/docs/source/docs/api-reference.rst @@ -219,8 +219,7 @@ Security Model configuration for SNMP v3 systems. .. autoclass:: pysnmp.hlapi.UsmUserData(userName, authKey=None, privKey=None, authProtocol=usmNoAuthProtocol, privProtocol=usmNoPrivProtocol, securityEngineId=None) -Identification of Authentication and Privacy Protocols is done -via constant OIDs: +**Authentication protocol identifiers** .. autodata:: pysnmp.hlapi.usmNoAuthProtocol .. autodata:: pysnmp.hlapi.usmHMACMD5AuthProtocol @@ -230,6 +229,8 @@ via constant OIDs: .. autodata:: pysnmp.hlapi.usmHMAC256SHA384AuthProtocol .. autodata:: pysnmp.hlapi.usmHMAC384SHA512AuthProtocol +**Privacy (encryption) protocol identifiers** + .. autodata:: pysnmp.hlapi.usmNoPrivProtocol .. autodata:: pysnmp.hlapi.usmDESPrivProtocol .. autodata:: pysnmp.hlapi.usm3DESEDEPrivProtocol @@ -239,6 +240,11 @@ via constant OIDs: .. autodata:: pysnmp.hlapi.usmAesBlumenthalCfb192Protocol .. autodata:: pysnmp.hlapi.usmAesBlumenthalCfb256Protocol +.. note:: + + SNMP authentication and encryption keys must be at least *eight* + octets long. + Transport configuration is I/O framework specific and is described in respective sections. diff --git a/pysnmp/entity/config.py b/pysnmp/entity/config.py index bb7be0a3b..e2ca92bb6 100644 --- a/pysnmp/entity/config.py +++ b/pysnmp/entity/config.py @@ -28,7 +28,9 @@ usmHMAC192SHA256AuthProtocol = hmacsha2.HmacSha2.sha256ServiceID usmHMAC256SHA384AuthProtocol = hmacsha2.HmacSha2.sha384ServiceID usmHMAC384SHA512AuthProtocol = hmacsha2.HmacSha2.sha512ServiceID + usmNoAuthProtocol = noauth.NoAuth.serviceID +"""No authentication service""" # Privacy protocol usmDESPrivProtocol = des.Des.serviceID diff --git a/pysnmp/hlapi/__init__.py b/pysnmp/hlapi/__init__.py index f60b0ed48..7a9c63714 100644 --- a/pysnmp/hlapi/__init__.py +++ b/pysnmp/hlapi/__init__.py @@ -7,9 +7,58 @@ from pysnmp.proto.rfc1902 import * from pysnmp.proto.rfc1905 import NoSuchInstance, NoSuchObject, EndOfMibView from pysnmp.smi.rfc1902 import * -from pysnmp.hlapi.auth import * +from pysnmp.hlapi import auth from pysnmp.hlapi.context import * from pysnmp.entity.engine import * # default is synchronous asyncore-based API from pysnmp.hlapi.asyncore.sync import * + + +CommunityData = auth.CommunityData +UsmUserData = auth.UsmUserData + +usmNoAuthProtocol = auth.usmNoAuthProtocol +"""No Authentication Protocol""" + +usmHMACMD5AuthProtocol = auth.usmHMACMD5AuthProtocol +"""The HMAC-MD5-96 Digest Authentication Protocol (:RFC:`3414#section-6`)""" + +usmHMACSHAAuthProtocol = auth.usmHMACSHAAuthProtocol +"""The HMAC-SHA-96 Digest Authentication Protocol AKA SHA-1 (:RFC:`3414#section-7`)""" + +usmHMAC128SHA224AuthProtocol = auth.usmHMAC128SHA224AuthProtocol +"""The HMAC-SHA-2 Digest Authentication Protocols (:RFC:`7860`)""" + +usmHMAC192SHA256AuthProtocol = auth.usmHMAC192SHA256AuthProtocol +"""The HMAC-SHA-2 Digest Authentication Protocols (:RFC:`7860`)""" + +usmHMAC256SHA384AuthProtocol = auth.usmHMAC256SHA384AuthProtocol +"""The HMAC-SHA-2 Digest Authentication Protocols (:RFC:`7860`)""" + +usmHMAC384SHA512AuthProtocol = auth.usmHMAC384SHA512AuthProtocol +"""The HMAC-SHA-2 Digest Authentication Protocols (:RFC:`7860`)""" + +usmNoPrivProtocol = auth.usmNoPrivProtocol +"""No Privacy Protocol""" + +usmDESPrivProtocol = auth.usmDESPrivProtocol +"""The CBC-DES Symmetric Encryption Protocol (:RFC:`3414#section-8`)""" + +usm3DESEDEPrivProtocol = auth.usm3DESEDEPrivProtocol +"""The 3DES-EDE Symmetric Encryption Protocol (`draft-reeder-snmpv3-usm-3desede-00 `_)""" + +usmAesCfb128Protocol = auth.usmAesCfb128Protocol +"""The CFB128-AES-128 Symmetric Encryption Protocol (:RFC:`3826#section-3`)""" + +usmAesCfb192Protocol = auth.usmAesCfb192Protocol +"""The CFB128-AES-192 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_) with Reeder key localization""" + +usmAesCfb256Protocol = auth.usmAesCfb256Protocol +"""The CFB128-AES-256 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_) with Reeder key localization""" + +usmAesBlumenthalCfb192Protocol = auth.usmAesBlumenthalCfb192Protocol +"""The CFB128-AES-192 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_)""" + +usmAesBlumenthalCfb256Protocol = auth.usmAesBlumenthalCfb256Protocol +"""The CFB128-AES-256 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_)""" diff --git a/pysnmp/hlapi/auth.py b/pysnmp/hlapi/auth.py index e4650f4b8..20db368f4 100644 --- a/pysnmp/hlapi/auth.py +++ b/pysnmp/hlapi/auth.py @@ -151,35 +151,50 @@ def clone(self, communityIndex=None, communityName=None, securityName is None and self.securityName or securityName ) - -#: No Authentication Protocol. usmNoAuthProtocol = config.usmNoAuthProtocol -#: The HMAC-MD5-96 Digest Authentication Protocol (:RFC:`3414#section-6`) +"""No Authentication Protocol""" + usmHMACMD5AuthProtocol = config.usmHMACMD5AuthProtocol -#: The HMAC-SHA-96 Digest Authentication Protocol (:RFC:`3414#section-7`) +"""The HMAC-MD5-96 Digest Authentication Protocol (:RFC:`3414#section-6`)""" + usmHMACSHAAuthProtocol = config.usmHMACSHAAuthProtocol -#: The HMAC-SHA-2 Digest Authentication Protocols (:RFC:`7860`) +"""The HMAC-SHA-96 Digest Authentication Protocol AKA SHA-1 (:RFC:`3414#section-7`)""" + usmHMAC128SHA224AuthProtocol = config.usmHMAC128SHA224AuthProtocol +"""The HMAC-SHA-2 Digest Authentication Protocols (:RFC:`7860`)""" + usmHMAC192SHA256AuthProtocol = config.usmHMAC192SHA256AuthProtocol +"""The HMAC-SHA-2 Digest Authentication Protocols (:RFC:`7860`)""" + usmHMAC256SHA384AuthProtocol = config.usmHMAC256SHA384AuthProtocol +"""The HMAC-SHA-2 Digest Authentication Protocols (:RFC:`7860`)""" + usmHMAC384SHA512AuthProtocol = config.usmHMAC384SHA512AuthProtocol +"""The HMAC-SHA-2 Digest Authentication Protocols (:RFC:`7860`)""" -#: No Privacy Protocol. usmNoPrivProtocol = config.usmNoPrivProtocol -#: The CBC-DES Symmetric Encryption Protocol (:RFC:`3414#section-8`) +"""No Privacy Protocol""" + usmDESPrivProtocol = config.usmDESPrivProtocol -#: The 3DES-EDE Symmetric Encryption Protocol (`draft-reeder-snmpv3-usm-3desede-00 `_) +"""The CBC-DES Symmetric Encryption Protocol (:RFC:`3414#section-8`)""" + usm3DESEDEPrivProtocol = config.usm3DESEDEPrivProtocol -#: The CFB128-AES-128 Symmetric Encryption Protocol (:RFC:`3826#section-3`) +"""The 3DES-EDE Symmetric Encryption Protocol (`draft-reeder-snmpv3-usm-3desede-00 `_)""" + usmAesCfb128Protocol = config.usmAesCfb128Protocol -#: The CFB128-AES-192 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_) with Reeder key localization +"""The CFB128-AES-128 Symmetric Encryption Protocol (:RFC:`3826#section-3`)""" + usmAesCfb192Protocol = config.usmAesCfb192Protocol -#: The CFB128-AES-256 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_) with Reeder key localization +"""The CFB128-AES-192 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_) with Reeder key localization""" + usmAesCfb256Protocol = config.usmAesCfb256Protocol -#: The CFB128-AES-192 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_) +"""The CFB128-AES-256 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_) with Reeder key localization""" + usmAesBlumenthalCfb192Protocol = config.usmAesBlumenthalCfb192Protocol -#: The CFB128-AES-256 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_) +"""The CFB128-AES-192 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_)""" + usmAesBlumenthalCfb256Protocol = config.usmAesBlumenthalCfb256Protocol +"""The CFB128-AES-256 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_)""" class UsmUserData(object): From 5057a1fd59e1f1e5818f5da945677835bae38ad9 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 26 Oct 2018 08:34:26 +0200 Subject: [PATCH 050/102] Use `compile()` before `exec` of MIB modules This change attaches the file name to the stack frames what is helpful when reading traceback or debugging interactively. --- CHANGES.txt | 4 +++- pysnmp/smi/builder.py | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b815edb38..f2563d03c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,7 +1,9 @@ -Revision 4.4.7, released 2018-09-XX +Revision 4.4.7, released 2018-11-XX ----------------------------------- +- Use `compile()` before `exec`'ing MIB modules to attach filename to + the stack frames (ultimately shown in traceback/debugger) - Fixed hlapi/v3arch transport target caching to ensure transport targets are different even if just timeout/retries options differ diff --git a/pysnmp/smi/builder.py b/pysnmp/smi/builder.py index 8b81b06c6..a622d140d 100644 --- a/pysnmp/smi/builder.py +++ b/pysnmp/smi/builder.py @@ -263,7 +263,7 @@ def __init__(self): self.mibSymbols = {} self.__mibSources = [] self.__modSeen = {} - self.__modPathsSeen = {} + self.__modPathsSeen = set() self.__mibCompiler = None self.setMibSources(*sources) @@ -321,19 +321,19 @@ def loadModule(self, modName, **userCtx): debug.logger & debug.flagBld and debug.logger('loadModule: seen %s' % modPath) break else: - self.__modPathsSeen[modPath] = 1 + self.__modPathsSeen.add(modPath) debug.logger & debug.flagBld and debug.logger('loadModule: evaluating %s' % modPath) g = {'mibBuilder': self, 'userCtx': userCtx} try: - exec (modData, g) + exec(compile(modData, modPath, 'exec'), g) except Exception: del self.__modPathsSeen[modPath] raise error.MibLoadError( - 'MIB module \"%s\" load error: %s' % (modPath, traceback.format_exception(*sys.exc_info())) + 'MIB module \'%s\' load error: %s' % (modPath, traceback.format_exception(*sys.exc_info())) ) self.__modSeen[modName] = modPath From c8d699ca0a1245b1fd0c630611b9c523bf90329a Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 26 Oct 2018 08:44:06 +0200 Subject: [PATCH 051/102] Expose ASN.1 `Null` type through `rfc1902` module --- CHANGES.txt | 1 + docs/source/docs/api-reference.rst | 14 ++++++++++++++ pysnmp/proto/rfc1902.py | 27 ++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index f2563d03c..ed0d26bd2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,6 +2,7 @@ Revision 4.4.7, released 2018-11-XX ----------------------------------- +- Exposed ASN.1 `Null` type through `rfc1902` module for convenience. - Use `compile()` before `exec`'ing MIB modules to attach filename to the stack frames (ultimately shown in traceback/debugger) - Fixed hlapi/v3arch transport target caching to ensure transport targets diff --git a/docs/source/docs/api-reference.rst b/docs/source/docs/api-reference.rst index d867db46e..bc6f56930 100644 --- a/docs/source/docs/api-reference.rst +++ b/docs/source/docs/api-reference.rst @@ -340,6 +340,20 @@ data description language. PySNMP types are derived from .. toctree:: :maxdepth: 2 +.. _null: + +Null type ++++++++++ + +.. autoclass:: pysnmp.proto.rfc1902.Null(initializer) + :members: + +.. note:: + + The `NULL` type actually belongs to the base ASN.1 types. It is not defined + in :RFC:`1902#section-2` as an SNMP type. The `Null` type is exposed through + `rfc1902` module just for convenience. + .. _integer32: Integer32 type diff --git a/pysnmp/proto/rfc1902.py b/pysnmp/proto/rfc1902.py index 3caa81d7d..4bd826690 100644 --- a/pysnmp/proto/rfc1902.py +++ b/pysnmp/proto/rfc1902.py @@ -10,7 +10,32 @@ __all__ = ['Opaque', 'TimeTicks', 'Bits', 'Integer', 'OctetString', 'IpAddress', 'Counter64', 'Unsigned32', 'Gauge32', 'Integer32', - 'ObjectIdentifier', 'Counter32'] + 'ObjectIdentifier', 'Counter32', 'Null'] + + +class Null(univ.Null): + """Creates an instance of SNMP Null class. + + :py:class:`~pysnmp.proto.rfc1902.Null` type represents the absence + of value. + + Parameters + ---------- + initializer: str + Python string object. Must be an empty string. + + Raises + ------ + PyAsn1Error : + On constraint violation or bad initializer. + + Examples + -------- + >>> from pysnmp.proto.rfc1902 import * + >>> Null('') + Null('') + >>> + """ class Integer32(univ.Integer): From cd48f6e128f124ab411e267887ee9581f8b9a3af Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 3 Nov 2018 12:51:35 +0100 Subject: [PATCH 052/102] Fix hlapi LCD to include `contextName` (#217) Fixed hlapi LCD configurator to include `contextName`. Prior to this fix sending SNMPv3 TRAP with non-default `contextName` would fail. This change modifies the signature of the internal LCD methods. --- CHANGES.txt | 2 ++ pysnmp/entity/config.py | 35 +++++++++++++++++++-------------- pysnmp/hlapi/asyncio/cmdgen.py | 12 +++++++---- pysnmp/hlapi/asyncio/ntforg.py | 4 ++-- pysnmp/hlapi/asyncore/cmdgen.py | 14 +++++++++---- pysnmp/hlapi/asyncore/ntforg.py | 2 +- pysnmp/hlapi/lcd.py | 30 ++++++++++++++++------------ pysnmp/hlapi/twisted/cmdgen.py | 12 +++++++---- pysnmp/hlapi/twisted/ntforg.py | 5 ++--- 9 files changed, 70 insertions(+), 46 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ed0d26bd2..2f9166e7a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,6 +7,8 @@ Revision 4.4.7, released 2018-11-XX the stack frames (ultimately shown in traceback/debugger) - Fixed hlapi/v3arch transport target caching to ensure transport targets are different even if just timeout/retries options differ +- Fixed hlapi LCD configurator to include `contextName`. Prior to this fix + sending SNMPv3 TRAP with non-default `contextName` would fail. Revision 4.4.6, released 2018-09-13 ----------------------------------- diff --git a/pysnmp/entity/config.py b/pysnmp/entity/config.py index e2ca92bb6..4c069c72e 100644 --- a/pysnmp/entity/config.py +++ b/pysnmp/entity/config.py @@ -560,12 +560,13 @@ def addVacmUser(snmpEngine, securityModel, securityName, securityLevel, def delVacmUser(snmpEngine, securityModel, securityName, securityLevel, - readSubTree=(), writeSubTree=(), notifySubTree=()): + readSubTree=(), writeSubTree=(), notifySubTree=(), + contextName=null): (groupName, securityLevel, readView, writeView, notifyView) = __cookVacmUserInfo(snmpEngine, securityModel, securityName, securityLevel) delVacmGroup(snmpEngine, securityModel, securityName) - delVacmAccess(snmpEngine, groupName, null, securityModel, securityLevel) + delVacmAccess(snmpEngine, groupName, contextName, securityModel, securityLevel) if readSubTree: delVacmView( snmpEngine, readView, readSubTree @@ -582,36 +583,40 @@ def delVacmUser(snmpEngine, securityModel, securityName, securityLevel, # Obsolete shortcuts for add/delVacmUser() wrappers -def addRoUser(snmpEngine, securityModel, securityName, securityLevel, subTree): - addVacmUser(snmpEngine, securityModel, securityName, - securityLevel, subTree) +def addRoUser(snmpEngine, securityModel, securityName, securityLevel, + subTree, contextName=null): + addVacmUser(snmpEngine, securityModel, securityName, securityLevel, + subTree, contextName=contextName) -def delRoUser(snmpEngine, securityModel, securityName, securityLevel, subTree): +def delRoUser(snmpEngine, securityModel, securityName, securityLevel, + subTree, contextName=null): delVacmUser(snmpEngine, securityModel, securityName, securityLevel, - subTree) + subTree, contextName=contextName) -def addRwUser(snmpEngine, securityModel, securityName, securityLevel, subTree): +def addRwUser(snmpEngine, securityModel, securityName, securityLevel, + subTree, contextName=null): addVacmUser(snmpEngine, securityModel, securityName, securityLevel, - subTree, subTree) + subTree, subTree, contextName=contextName) -def delRwUser(snmpEngine, securityModel, securityName, securityLevel, subTree): +def delRwUser(snmpEngine, securityModel, securityName, securityLevel, + subTree, contextName=null): delVacmUser(snmpEngine, securityModel, securityName, securityLevel, - subTree, subTree) + subTree, subTree, contextName=contextName) def addTrapUser(snmpEngine, securityModel, securityName, - securityLevel, subTree): + securityLevel, subTree, contextName=null): addVacmUser(snmpEngine, securityModel, securityName, securityLevel, - (), (), subTree) + (), (), subTree, contextName=contextName) def delTrapUser(snmpEngine, securityModel, securityName, - securityLevel, subTree): + securityLevel, subTree, contextName=null): delVacmUser(snmpEngine, securityModel, securityName, securityLevel, - (), (), subTree) + (), (), subTree, contextName=contextName) # Notification target setup diff --git a/pysnmp/hlapi/asyncio/cmdgen.py b/pysnmp/hlapi/asyncio/cmdgen.py index 8c69fd195..46e58aa67 100644 --- a/pysnmp/hlapi/asyncio/cmdgen.py +++ b/pysnmp/hlapi/asyncio/cmdgen.py @@ -148,7 +148,8 @@ def __cbFun(snmpEngine, sendRequestHandle, (errorIndication, errorStatus, errorIndex, varBindsUnmade) ) - addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget) + addrName, paramsName = lcd.configure( + snmpEngine, authData, transportTarget, contextData.contextName) future = asyncio.Future() @@ -254,7 +255,8 @@ def __cbFun(snmpEngine, sendRequestHandle, (errorIndication, errorStatus, errorIndex, varBindsUnmade) ) - addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget) + addrName, paramsName = lcd.configure( + snmpEngine, authData, transportTarget, contextData.contextName) future = asyncio.Future() @@ -366,7 +368,8 @@ def __cbFun(snmpEngine, sendRequestHandle, (errorIndication, errorStatus, errorIndex, varBindsUnmade) ) - addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget) + addrName, paramsName = lcd.configure( + snmpEngine, authData, transportTarget, contextData.contextName) future = asyncio.Future() @@ -507,7 +510,8 @@ def __cbFun(snmpEngine, sendRequestHandle, (errorIndication, errorStatus, errorIndex, varBindsUnmade) ) - addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget) + addrName, paramsName = lcd.configure( + snmpEngine, authData, transportTarget, contextData.contextName) future = asyncio.Future() diff --git a/pysnmp/hlapi/asyncio/ntforg.py b/pysnmp/hlapi/asyncio/ntforg.py index b4f6fb3a0..be75b3a55 100644 --- a/pysnmp/hlapi/asyncio/ntforg.py +++ b/pysnmp/hlapi/asyncio/ntforg.py @@ -139,8 +139,8 @@ def __cbFun(snmpEngine, sendRequestHandle, ) notifyName = lcd.configure( - snmpEngine, authData, transportTarget, notifyType - ) + snmpEngine, authData, transportTarget, notifyType, + contextData.contextName) future = asyncio.Future() diff --git a/pysnmp/hlapi/asyncore/cmdgen.py b/pysnmp/hlapi/asyncore/cmdgen.py index 4cedfa82e..c7b8a2d3a 100644 --- a/pysnmp/hlapi/asyncore/cmdgen.py +++ b/pysnmp/hlapi/asyncore/cmdgen.py @@ -121,7 +121,8 @@ def __cbFun(snmpEngine, sendRequestHandle, snmpEngine, varBinds, lookupMib ), cbCtx) - addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget) + addrName, paramsName = lcd.configure( + snmpEngine, authData, transportTarget, contextData.contextName) return cmdgen.GetCommandGenerator().sendVarBinds( snmpEngine, addrName, contextData.contextEngineId, @@ -232,7 +233,8 @@ def __cbFun(snmpEngine, sendRequestHandle, snmpEngine, varBinds, lookupMib ), cbCtx) - addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget) + addrName, paramsName = lcd.configure( + snmpEngine, authData, transportTarget, contextData.contextName) return cmdgen.SetCommandGenerator().sendVarBinds( snmpEngine, addrName, contextData.contextEngineId, @@ -343,7 +345,9 @@ def __cbFun(snmpEngine, sendRequestHandle, errorIndication, varBindTable], cbCtx) - addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget) + addrName, paramsName = lcd.configure( + snmpEngine, authData, transportTarget, contextData.contextName) + return cmdgen.NextCommandGenerator().sendVarBinds( snmpEngine, addrName, contextData.contextEngineId, contextData.contextName, @@ -483,7 +487,9 @@ def __cbFun(snmpEngine, sendRequestHandle, [vbProcessor.unmakeVarBinds(snmpEngine, varBindTableRow, lookupMib) for varBindTableRow in varBindTable], cbCtx) - addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget) + addrName, paramsName = lcd.configure( + snmpEngine, authData, transportTarget, contextData.contextName) + return cmdgen.BulkCommandGenerator().sendVarBinds( snmpEngine, addrName, contextData.contextEngineId, contextData.contextName, nonRepeaters, maxRepetitions, diff --git a/pysnmp/hlapi/asyncore/ntforg.py b/pysnmp/hlapi/asyncore/ntforg.py index ecd418c1f..b35bdba91 100644 --- a/pysnmp/hlapi/asyncore/ntforg.py +++ b/pysnmp/hlapi/asyncore/ntforg.py @@ -131,7 +131,7 @@ def __cbFun(snmpEngine, sendRequestHandle, errorIndication, ) notifyName = lcd.configure(snmpEngine, authData, transportTarget, - notifyType) + notifyType, contextData.contextName) return ntforg.NotificationOriginator().sendVarBinds( snmpEngine, notifyName, diff --git a/pysnmp/hlapi/lcd.py b/pysnmp/hlapi/lcd.py index 1d61c491c..43bfc1016 100644 --- a/pysnmp/hlapi/lcd.py +++ b/pysnmp/hlapi/lcd.py @@ -8,6 +8,8 @@ from pysnmp import nextid, error from pysnmp.hlapi.auth import * +from pyasn1.compat.octets import null + __all__ = ['CommandGeneratorLcdConfigurator', 'NotificationOriginatorLcdConfigurator'] @@ -24,17 +26,17 @@ def _getCache(self, snmpEngine): snmpEngine.setUserContext(**{cacheId: cache}) return cache - def configure(self, snmpEngine, authData, transportTarget, *options): + def configure(self, snmpEngine, *args, **kwargs): pass - def unconfigure(self, snmpEngine, authData=None): + def unconfigure(self, snmpEngine, *args, **kwargs): pass class CommandGeneratorLcdConfigurator(AbstractLcdConfigurator): cacheKeys = ['auth', 'parm', 'tran', 'addr'] - def configure(self, snmpEngine, authData, transportTarget, *options): + def configure(self, snmpEngine, authData, transportTarget, contextName, **options): cache = self._getCache(snmpEngine) if isinstance(authData, CommunityData): if authData.communityIndex not in cache['auth']: @@ -117,7 +119,7 @@ def configure(self, snmpEngine, authData, transportTarget, *options): return addrName, paramsName - def unconfigure(self, snmpEngine, authData=None): + def unconfigure(self, snmpEngine, authData=None, contextName=null, **options): cache = self._getCache(snmpEngine) if authData: if isinstance(authData, CommunityData): @@ -198,9 +200,9 @@ class NotificationOriginatorLcdConfigurator(AbstractLcdConfigurator): cacheKeys = ['auth', 'name'] _cmdGenLcdCfg = CommandGeneratorLcdConfigurator() - def configure(self, snmpEngine, authData, transportTarget, *options): + def configure(self, snmpEngine, authData, transportTarget, notifyType, + contextName, **options): cache = self._getCache(snmpEngine) - notifyType = options and options[0] or 'trap' notifyName = None # Create matching transport tags if not given by user. Not good! @@ -211,7 +213,8 @@ def configure(self, snmpEngine, authData, transportTarget, *options): if isinstance(authData, CommunityData) and not authData.tag: authData.tag = transportTarget.tagList.split()[0] - addrName, paramsName = self._cmdGenLcdCfg.configure(snmpEngine, authData, transportTarget) + addrName, paramsName = self._cmdGenLcdCfg.configure( + snmpEngine, authData, transportTarget, contextName, **options) tagList = transportTarget.tagList.split() if not tagList: tagList = [''] @@ -230,7 +233,7 @@ def configure(self, snmpEngine, authData, transportTarget, *options): notifyType ) cache['name'][notifyNameKey] = notifyName, paramsName, 1 - authDataKey = authData.securityName, authData.securityModel + authDataKey = authData.securityName, authData.securityModel, authData.securityLevel, contextName if authDataKey in cache['auth']: authDataX, subTree, useCount = cache['auth'][authDataKey] cache['auth'][authDataKey] = authDataX, subTree, useCount + 1 @@ -238,23 +241,24 @@ def configure(self, snmpEngine, authData, transportTarget, *options): subTree = (1, 3, 6) config.addTrapUser(snmpEngine, authData.securityModel, authData.securityName, authData.securityLevel, - subTree) + subTree, contextName=contextName) cache['auth'][authDataKey] = authData, subTree, 1 return notifyName - def unconfigure(self, snmpEngine, authData=None): + def unconfigure(self, snmpEngine, authData=None, contextName=null, **options): cache = self._getCache(snmpEngine) if authData: - authDataKey = authData.securityName, authData.securityModel + authDataKey = authData.securityName, authData.securityModel, authData.securityLevel, contextName if authDataKey in cache['auth']: authDataKeys = (authDataKey,) else: raise error.PySnmpError('Unknown authData %s' % (authData,)) else: - authDataKeys = tuple(cache['auth'].keys()) + authDataKeys = tuple(cache['auth']) - addrNames, paramsNames = self._cmdGenLcdCfg.unconfigure(snmpEngine, authData) + addrNames, paramsNames = self._cmdGenLcdCfg.unconfigure( + snmpEngine, authData, contextName, **options) notifyAndParamsNames = [(cache['name'][x], x) for x in cache['name'].keys() if x[0] in paramsNames] diff --git a/pysnmp/hlapi/twisted/cmdgen.py b/pysnmp/hlapi/twisted/cmdgen.py index 9cfb98226..611de0508 100644 --- a/pysnmp/hlapi/twisted/cmdgen.py +++ b/pysnmp/hlapi/twisted/cmdgen.py @@ -129,7 +129,8 @@ def __cbFun(snmpEngine, sendRequestHandle, else: deferred.callback((errorStatus, errorIndex, varBindsUnmade)) - addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget) + addrName, paramsName = lcd.configure( + snmpEngine, authData, transportTarget, contextData.contextName) deferred = Deferred() @@ -246,7 +247,8 @@ def __cbFun(snmpEngine, sendRequestHandle, else: deferred.callback((errorStatus, errorIndex, varBindsUnmade)) - addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget) + addrName, paramsName = lcd.configure( + snmpEngine, authData, transportTarget, contextData.contextName) deferred = Deferred() @@ -377,7 +379,8 @@ def __cbFun(snmpEngine, sendRequestHandle, else: deferred.callback((errorStatus, errorIndex, varBindsUnmade)) - addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget) + addrName, paramsName = lcd.configure( + snmpEngine, authData, transportTarget, contextData.contextName) deferred = Deferred() @@ -536,7 +539,8 @@ def __cbFun(snmpEngine, sendRequestHandle, else: deferred.callback((errorStatus, errorIndex, varBindsUnmade)) - addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget) + addrName, paramsName = lcd.configure( + snmpEngine, authData, transportTarget, contextData.contextName) deferred = Deferred() diff --git a/pysnmp/hlapi/twisted/ntforg.py b/pysnmp/hlapi/twisted/ntforg.py index 0b8f60530..a33e226db 100644 --- a/pysnmp/hlapi/twisted/ntforg.py +++ b/pysnmp/hlapi/twisted/ntforg.py @@ -137,9 +137,8 @@ def __cbFun(snmpEngine, sendRequestHandle, else: deferred.callback((errorStatus, errorIndex, varBindsUnmade)) - notifyName = lcd.configure( - snmpEngine, authData, transportTarget, notifyType - ) + notifyName = lcd.configure(snmpEngine, authData, transportTarget, + notifyType, contextData.contextName) def __trapFun(deferred): deferred.callback((0, 0, [])) From 6a850eaf0144cd8a6a90805e626b3666acb7de83 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 3 Nov 2018 13:20:40 +0100 Subject: [PATCH 053/102] Disable Travis builds for py 2.6, 3.2 & 3.3 Seems like they discontinued those. --- .travis.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 99dc942d6..e2c456a0b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,22 +2,10 @@ language: python cache: pip matrix: include: - - os: linux - dist: xenial - sudo: false - python: '2.6' - os: linux dist: xenial sudo: false python: '2.7' - - os: linux - dist: xenial - sudo: false - python: '3.2' - - os: linux - dist: xenial - sudo: false - python: '3.3' - os: linux dist: xenial sudo: false From 59de44ed981c9b3645bcfecc813d68f9d963a0a0 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Tue, 25 Dec 2018 11:19:34 +0100 Subject: [PATCH 054/102] Fix possible duplicate key condition in `OrderedDict` Also, updated thr `.update` and `__init__` methods signatures to match `dict` interface. Implementation details renewed. --- CHANGES.txt | 4 ++- pysnmp/smi/indices.py | 73 ++++++++++++++++++++++--------------------- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 2f9166e7a..24ea285a3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,5 @@ -Revision 4.4.7, released 2018-11-XX +Revision 4.4.7, released 2018-12-XX ----------------------------------- - Exposed ASN.1 `Null` type through `rfc1902` module for convenience. @@ -9,6 +9,8 @@ Revision 4.4.7, released 2018-11-XX are different even if just timeout/retries options differ - Fixed hlapi LCD configurator to include `contextName`. Prior to this fix sending SNMPv3 TRAP with non-default `contextName` would fail. +- Fixed possible duplicate key occurrence in the `OrderedDict` following + a race condition Revision 4.4.6, released 2018-09-13 ----------------------------------- diff --git a/pysnmp/smi/indices.py b/pysnmp/smi/indices.py index 00d95a632..7e15a8cde 100644 --- a/pysnmp/smi/indices.py +++ b/pysnmp/smi/indices.py @@ -10,36 +10,26 @@ class OrderedDict(dict): """Ordered dictionary used for indices""" - def __init__(self, **kwargs): + def __init__(self, *args, **kwargs): self.__keys = [] self.__dirty = True super(OrderedDict, self).__init__() + if args: + self.update(*args) if kwargs: - self.update(kwargs) + self.update(**kwargs) def __setitem__(self, key, value): - if key not in self: - self.__keys.append(key) super(OrderedDict, self).__setitem__(key, value) - self.__dirty = True - - def __repr__(self): - if self.__dirty: - self.__order() - return super(OrderedDict, self).__repr__() - - def __str__(self): - if self.__dirty: - self.__order() - return super(OrderedDict, self).__str__() + if key not in self.__keys: + self.__keys.append(key) + self.__dirty = True def __delitem__(self, key): - if super(OrderedDict, self).__contains__(key): - self.__keys.remove(key) super(OrderedDict, self).__delitem__(key) - self.__dirty = True - - __delattr__ = __delitem__ + if key in self.__keys: + self.__keys.remove(key) + self.__dirty = True def clear(self): super(OrderedDict, self).clear() @@ -61,30 +51,43 @@ def items(self): self.__order() return [(k, self[k]) for k in self.__keys] - def update(self, d): - [self.__setitem__(k, v) for k, v in d.items()] + def update(self, *args, **kwargs): + if args: + iterable = args[0] + if hasattr(iterable, 'keys'): + for k in iterable: + self[k] = iterable[k] + else: + for k, v in iterable: + self[k] = v + + if kwargs: + for k in kwargs: + self[k] = kwargs[k] def sortingFun(self, keys): keys.sort() def __order(self): self.sortingFun(self.__keys) - d = {} - for k in self.__keys: - d[len(k)] = 1 - l = list(d.keys()) - l.sort(reverse=True) - self.__keysLens = tuple(l) + self.__keysLens = sorted(set(len(k) for k in self.__keys), reverse=True) self.__dirty = False def nextKey(self, key): - keys = list(self.keys()) - if key in self: + if self.__dirty: + self.__order() + + keys = self.__keys + + if key in keys: nextIdx = keys.index(key) + 1 + else: nextIdx = bisect(keys, key) + if nextIdx < len(keys): return keys[nextIdx] + else: raise KeyError(key) @@ -97,24 +100,22 @@ def getKeysLens(self): class OidOrderedDict(OrderedDict): """OID-ordered dictionary used for indices""" - def __init__(self, **kwargs): + def __init__(self, *args, **kwargs): self.__keysCache = {} - OrderedDict.__init__(self, **kwargs) + OrderedDict.__init__(self, *args, **kwargs) def __setitem__(self, key, value): + OrderedDict.__setitem__(self, key, value) if key not in self.__keysCache: if isinstance(key, tuple): self.__keysCache[key] = key else: self.__keysCache[key] = [int(x) for x in key.split('.') if x] - OrderedDict.__setitem__(self, key, value) def __delitem__(self, key): + OrderedDict.__delitem__(self, key) if key in self.__keysCache: del self.__keysCache[key] - OrderedDict.__delitem__(self, key) - - __delattr__ = __delitem__ def sortingFun(self, keys): keys.sort(key=lambda k, d=self.__keysCache: d[k]) From a2d1e7c9bb080210d09389aa312c4eab6137ba04 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Tue, 25 Dec 2018 11:30:51 +0100 Subject: [PATCH 055/102] Fix wrong `set()` member deletion in `MibBuilder` --- pysnmp/smi/builder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pysnmp/smi/builder.py b/pysnmp/smi/builder.py index a622d140d..1a1f2e8f7 100644 --- a/pysnmp/smi/builder.py +++ b/pysnmp/smi/builder.py @@ -331,7 +331,7 @@ def loadModule(self, modName, **userCtx): exec(compile(modData, modPath, 'exec'), g) except Exception: - del self.__modPathsSeen[modPath] + self.__modPathsSeen.remove(modPath) raise error.MibLoadError( 'MIB module \'%s\' load error: %s' % (modPath, traceback.format_exception(*sys.exc_info())) ) @@ -388,7 +388,7 @@ def unloadModules(self, *modNames): 'No module %s at %s' % (modName, self) ) self.unexportSymbols(modName) - del self.__modPathsSeen[self.__modSeen[modName]] + self.__modPathsSeen.remove(self.__modSeen[modName]) del self.__modSeen[modName] debug.logger & debug.flagBld and debug.logger('unloadModules: %s' % modName) From 5cc25c9b851a39955b04f0b33b37e0fc694af09f Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 29 Dec 2018 23:41:52 +0100 Subject: [PATCH 056/102] Switch Travis CI to Trusty --- .travis.yml | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index e2c456a0b..497cf2a2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,37 +3,39 @@ cache: pip matrix: include: - os: linux - dist: xenial - sudo: false + dist: trusty + python: '2.6' + - os: linux + dist: trusty python: '2.7' - os: linux - dist: xenial - sudo: false + dist: trusty + python: '3.2' + - os: linux + dist: trusty + python: '3.3' + - os: linux + dist: trusty python: '3.4' - os: linux - dist: xenial - sudo: false + dist: trusty python: '3.5' - os: linux - dist: xenial - sudo: false + dist: trusty python: '3.6' - os: linux dist: xenial sudo: true python: '3.7' - os: linux - dist: xenial - sudo: false + dist: trusty python: 'nightly' -# - os: linux -# dist: xenial -# sudo: false -# python: 'pypy' -# - os: linux -# dist: xenial -# sudo: false -# python: 'pypy3' + - os: linux + dist: trusty + python: 'pypy' + - os: linux + dist: trusty + python: 'pypy3' install: - pip install -r requirements.txt -r devel-requirements.txt - pip install -e . From 49af9176c2bb92061e38d2988fafcbf6bcd27ca6 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 29 Dec 2018 23:45:49 +0100 Subject: [PATCH 057/102] Extend copyright notice to year 2019 --- CHANGES.txt | 3 ++- LICENSE.rst | 2 +- README.md | 2 +- docs/source/conf.py | 2 +- pysnmp/cache.py | 2 +- pysnmp/carrier/asyncio/base.py | 2 +- pysnmp/carrier/asyncio/dgram/base.py | 2 +- pysnmp/carrier/asyncio/dgram/udp.py | 2 +- pysnmp/carrier/asyncio/dgram/udp6.py | 2 +- pysnmp/carrier/asyncio/dispatch.py | 2 +- pysnmp/carrier/asyncore/base.py | 2 +- pysnmp/carrier/asyncore/dgram/base.py | 2 +- pysnmp/carrier/asyncore/dgram/udp.py | 2 +- pysnmp/carrier/asyncore/dgram/udp6.py | 2 +- pysnmp/carrier/asyncore/dgram/unix.py | 2 +- pysnmp/carrier/asyncore/dispatch.py | 2 +- pysnmp/carrier/asynsock/dgram/udp.py | 2 +- pysnmp/carrier/asynsock/dgram/udp6.py | 2 +- pysnmp/carrier/asynsock/dgram/unix.py | 2 +- pysnmp/carrier/asynsock/dispatch.py | 2 +- pysnmp/carrier/base.py | 2 +- pysnmp/carrier/error.py | 2 +- pysnmp/carrier/sockfix.py | 2 +- pysnmp/carrier/sockmsg.py | 2 +- pysnmp/carrier/twisted/base.py | 2 +- pysnmp/carrier/twisted/dgram/base.py | 2 +- pysnmp/carrier/twisted/dgram/udp.py | 2 +- pysnmp/carrier/twisted/dgram/unix.py | 2 +- pysnmp/carrier/twisted/dispatch.py | 2 +- pysnmp/debug.py | 2 +- pysnmp/entity/config.py | 2 +- pysnmp/entity/engine.py | 2 +- pysnmp/entity/observer.py | 2 +- pysnmp/entity/rfc3413/cmdgen.py | 2 +- pysnmp/entity/rfc3413/cmdrsp.py | 2 +- pysnmp/entity/rfc3413/config.py | 2 +- pysnmp/entity/rfc3413/context.py | 2 +- pysnmp/entity/rfc3413/mibvar.py | 2 +- pysnmp/entity/rfc3413/ntforg.py | 2 +- pysnmp/entity/rfc3413/ntfrcv.py | 2 +- pysnmp/entity/rfc3413/oneliner/cmdgen.py | 2 +- pysnmp/entity/rfc3413/oneliner/ntforg.py | 2 +- pysnmp/error.py | 2 +- pysnmp/hlapi/__init__.py | 2 +- pysnmp/hlapi/asyncio/__init__.py | 2 +- pysnmp/hlapi/asyncio/cmdgen.py | 2 +- pysnmp/hlapi/asyncio/ntforg.py | 2 +- pysnmp/hlapi/asyncio/transport.py | 2 +- pysnmp/hlapi/asyncore/__init__.py | 2 +- pysnmp/hlapi/asyncore/cmdgen.py | 2 +- pysnmp/hlapi/asyncore/ntforg.py | 2 +- pysnmp/hlapi/asyncore/sync/__init__.py | 2 +- pysnmp/hlapi/asyncore/sync/cmdgen.py | 2 +- pysnmp/hlapi/asyncore/sync/compat/cmdgen.py | 2 +- pysnmp/hlapi/asyncore/sync/compat/ntforg.py | 2 +- pysnmp/hlapi/asyncore/sync/ntforg.py | 2 +- pysnmp/hlapi/asyncore/transport.py | 2 +- pysnmp/hlapi/auth.py | 2 +- pysnmp/hlapi/context.py | 2 +- pysnmp/hlapi/lcd.py | 2 +- pysnmp/hlapi/transport.py | 2 +- pysnmp/hlapi/twisted/cmdgen.py | 2 +- pysnmp/hlapi/twisted/ntforg.py | 2 +- pysnmp/hlapi/twisted/transport.py | 2 +- pysnmp/hlapi/varbinds.py | 2 +- pysnmp/nextid.py | 2 +- pysnmp/proto/acmod/rfc3415.py | 2 +- pysnmp/proto/acmod/void.py | 2 +- pysnmp/proto/api/__init__.py | 2 +- pysnmp/proto/api/v1.py | 2 +- pysnmp/proto/api/v2c.py | 2 +- pysnmp/proto/api/verdec.py | 2 +- pysnmp/proto/cache.py | 2 +- pysnmp/proto/errind.py | 2 +- pysnmp/proto/error.py | 2 +- pysnmp/proto/mpmod/base.py | 2 +- pysnmp/proto/mpmod/cache.py | 2 +- pysnmp/proto/mpmod/rfc2576.py | 2 +- pysnmp/proto/proxy/rfc2576.py | 2 +- pysnmp/proto/rfc1155.py | 2 +- pysnmp/proto/rfc1157.py | 2 +- pysnmp/proto/rfc1901.py | 2 +- pysnmp/proto/rfc1902.py | 2 +- pysnmp/proto/rfc1905.py | 2 +- pysnmp/proto/rfc3411.py | 2 +- pysnmp/proto/rfc3412.py | 2 +- pysnmp/proto/secmod/base.py | 2 +- pysnmp/proto/secmod/cache.py | 2 +- pysnmp/proto/secmod/eso/priv/aes192.py | 2 +- pysnmp/proto/secmod/eso/priv/aes256.py | 2 +- pysnmp/proto/secmod/eso/priv/aesbase.py | 2 +- pysnmp/proto/secmod/eso/priv/des3.py | 2 +- pysnmp/proto/secmod/rfc2576.py | 2 +- pysnmp/proto/secmod/rfc3414/__init__.py | 2 +- pysnmp/proto/secmod/rfc3414/auth/base.py | 2 +- pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py | 2 +- pysnmp/proto/secmod/rfc3414/auth/hmacsha.py | 2 +- pysnmp/proto/secmod/rfc3414/auth/noauth.py | 2 +- pysnmp/proto/secmod/rfc3414/localkey.py | 2 +- pysnmp/proto/secmod/rfc3414/priv/base.py | 2 +- pysnmp/proto/secmod/rfc3414/priv/des.py | 2 +- pysnmp/proto/secmod/rfc3414/priv/nopriv.py | 2 +- pysnmp/proto/secmod/rfc3414/service.py | 2 +- pysnmp/proto/secmod/rfc3826/priv/aes.py | 2 +- pysnmp/smi/builder.py | 2 +- pysnmp/smi/compiler.py | 2 +- pysnmp/smi/error.py | 2 +- pysnmp/smi/exval.py | 2 +- pysnmp/smi/indices.py | 2 +- pysnmp/smi/instrum.py | 2 +- pysnmp/smi/mibs/ASN1-ENUMERATION.py | 2 +- pysnmp/smi/mibs/ASN1-REFINEMENT.py | 2 +- pysnmp/smi/mibs/ASN1.py | 2 +- pysnmp/smi/mibs/INET-ADDRESS-MIB.py | 2 +- pysnmp/smi/mibs/PYSNMP-MIB.py | 2 +- pysnmp/smi/mibs/PYSNMP-SOURCE-MIB.py | 2 +- pysnmp/smi/mibs/PYSNMP-USM-MIB.py | 2 +- pysnmp/smi/mibs/RFC1158-MIB.py | 2 +- pysnmp/smi/mibs/RFC1213-MIB.py | 2 +- pysnmp/smi/mibs/SNMP-COMMUNITY-MIB.py | 2 +- pysnmp/smi/mibs/SNMP-FRAMEWORK-MIB.py | 2 +- pysnmp/smi/mibs/SNMP-MPD-MIB.py | 2 +- pysnmp/smi/mibs/SNMP-NOTIFICATION-MIB.py | 2 +- pysnmp/smi/mibs/SNMP-PROXY-MIB.py | 2 +- pysnmp/smi/mibs/SNMP-TARGET-MIB.py | 2 +- pysnmp/smi/mibs/SNMP-USER-BASED-SM-3DES-MIB.py | 2 +- pysnmp/smi/mibs/SNMP-USER-BASED-SM-MIB.py | 2 +- pysnmp/smi/mibs/SNMP-USM-AES-MIB.py | 2 +- pysnmp/smi/mibs/SNMP-VIEW-BASED-ACM-MIB.py | 2 +- pysnmp/smi/mibs/SNMPv2-CONF.py | 2 +- pysnmp/smi/mibs/SNMPv2-MIB.py | 2 +- pysnmp/smi/mibs/SNMPv2-SMI.py | 2 +- pysnmp/smi/mibs/SNMPv2-TC.py | 2 +- pysnmp/smi/mibs/SNMPv2-TM.py | 2 +- pysnmp/smi/mibs/TRANSPORT-ADDRESS-MIB.py | 2 +- pysnmp/smi/mibs/instances/__PYSNMP-USM-MIB.py | 2 +- pysnmp/smi/mibs/instances/__SNMP-FRAMEWORK-MIB.py | 2 +- pysnmp/smi/mibs/instances/__SNMP-MPD-MIB.py | 2 +- pysnmp/smi/mibs/instances/__SNMP-TARGET-MIB.py | 2 +- pysnmp/smi/mibs/instances/__SNMP-USER-BASED-SM-MIB.py | 2 +- pysnmp/smi/mibs/instances/__SNMP-VIEW-BASED-ACM-MIB.py | 2 +- pysnmp/smi/mibs/instances/__SNMPv2-MIB.py | 2 +- pysnmp/smi/rfc1902.py | 2 +- pysnmp/smi/view.py | 2 +- 144 files changed, 145 insertions(+), 144 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 24ea285a3..fb876491d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,7 +1,8 @@ -Revision 4.4.7, released 2018-12-XX +Revision 4.4.7, released 2018-12-29 ----------------------------------- +- Copyright notice extended to the year 2019 - Exposed ASN.1 `Null` type through `rfc1902` module for convenience. - Use `compile()` before `exec`'ing MIB modules to attach filename to the stack frames (ultimately shown in traceback/debugger) diff --git a/LICENSE.rst b/LICENSE.rst index 12a18b46e..4770ec3db 100644 --- a/LICENSE.rst +++ b/LICENSE.rst @@ -1,4 +1,4 @@ -Copyright (c) 2005-2018, Ilya Etingof +Copyright (c) 2005-2019, Ilya Etingof All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index e1f68100b..76bbef352 100644 --- a/README.md +++ b/README.md @@ -156,4 +156,4 @@ or try browsing pysnmp Bug reports and PRs are appreciated! ;-) -Copyright (c) 2005-2018, [Ilya Etingof](mailto:etingof@gmail.com). All rights reserved. +Copyright (c) 2005-2019, [Ilya Etingof](mailto:etingof@gmail.com). All rights reserved. diff --git a/docs/source/conf.py b/docs/source/conf.py index 5b4bb2879..10f97987e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -51,7 +51,7 @@ # General information about the project. project = u'SNMP library for Python' -copyright = u'2005-2018, Ilya Etingof ' +copyright = u'2005-2019, Ilya Etingof ' author = u'Ilya Etingof ' # The version info for the project you're documenting, acts as replacement for diff --git a/pysnmp/cache.py b/pysnmp/cache.py index 3c8d7c108..f4b8796e7 100644 --- a/pysnmp/cache.py +++ b/pysnmp/cache.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # Limited-size dictionary-like class to use for caches diff --git a/pysnmp/carrier/asyncio/base.py b/pysnmp/carrier/asyncio/base.py index 3b09b3af6..ce1b644f6 100644 --- a/pysnmp/carrier/asyncio/base.py +++ b/pysnmp/carrier/asyncio/base.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # Copyright (C) 2014, Zebra Technologies diff --git a/pysnmp/carrier/asyncio/dgram/base.py b/pysnmp/carrier/asyncio/dgram/base.py index af649db66..a845fa1e9 100644 --- a/pysnmp/carrier/asyncio/dgram/base.py +++ b/pysnmp/carrier/asyncio/dgram/base.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # Copyright (C) 2014, Zebra Technologies diff --git a/pysnmp/carrier/asyncio/dgram/udp.py b/pysnmp/carrier/asyncio/dgram/udp.py index c407eb4a9..41ada7230 100644 --- a/pysnmp/carrier/asyncio/dgram/udp.py +++ b/pysnmp/carrier/asyncio/dgram/udp.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # Copyright (C) 2014, Zebra Technologies diff --git a/pysnmp/carrier/asyncio/dgram/udp6.py b/pysnmp/carrier/asyncio/dgram/udp6.py index 12fba8424..bc03b4471 100644 --- a/pysnmp/carrier/asyncio/dgram/udp6.py +++ b/pysnmp/carrier/asyncio/dgram/udp6.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import socket diff --git a/pysnmp/carrier/asyncio/dispatch.py b/pysnmp/carrier/asyncio/dispatch.py index 74f9ae10f..e92fe95e0 100644 --- a/pysnmp/carrier/asyncio/dispatch.py +++ b/pysnmp/carrier/asyncio/dispatch.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # Copyright (C) 2014, Zebra Technologies diff --git a/pysnmp/carrier/asyncore/base.py b/pysnmp/carrier/asyncore/base.py index a1424d7d2..61c552210 100644 --- a/pysnmp/carrier/asyncore/base.py +++ b/pysnmp/carrier/asyncore/base.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import socket diff --git a/pysnmp/carrier/asyncore/dgram/base.py b/pysnmp/carrier/asyncore/dgram/base.py index 6524b8bb4..274b1eba0 100644 --- a/pysnmp/carrier/asyncore/dgram/base.py +++ b/pysnmp/carrier/asyncore/dgram/base.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import socket diff --git a/pysnmp/carrier/asyncore/dgram/udp.py b/pysnmp/carrier/asyncore/dgram/udp.py index ed9a70e51..08ae5ceba 100644 --- a/pysnmp/carrier/asyncore/dgram/udp.py +++ b/pysnmp/carrier/asyncore/dgram/udp.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from socket import AF_INET diff --git a/pysnmp/carrier/asyncore/dgram/udp6.py b/pysnmp/carrier/asyncore/dgram/udp6.py index 944048b7a..900ef3c7d 100644 --- a/pysnmp/carrier/asyncore/dgram/udp6.py +++ b/pysnmp/carrier/asyncore/dgram/udp6.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.carrier import sockfix diff --git a/pysnmp/carrier/asyncore/dgram/unix.py b/pysnmp/carrier/asyncore/dgram/unix.py index 6ab1e5fd4..15f68b2ba 100644 --- a/pysnmp/carrier/asyncore/dgram/unix.py +++ b/pysnmp/carrier/asyncore/dgram/unix.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import os diff --git a/pysnmp/carrier/asyncore/dispatch.py b/pysnmp/carrier/asyncore/dispatch.py index a88bd6739..bc72df91b 100644 --- a/pysnmp/carrier/asyncore/dispatch.py +++ b/pysnmp/carrier/asyncore/dispatch.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from time import time diff --git a/pysnmp/carrier/asynsock/dgram/udp.py b/pysnmp/carrier/asynsock/dgram/udp.py index c77184de9..ab44b28f3 100644 --- a/pysnmp/carrier/asynsock/dgram/udp.py +++ b/pysnmp/carrier/asynsock/dgram/udp.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.carrier.asyncore.dgram.udp import * diff --git a/pysnmp/carrier/asynsock/dgram/udp6.py b/pysnmp/carrier/asynsock/dgram/udp6.py index 7ac4c5550..b3a8aec02 100644 --- a/pysnmp/carrier/asynsock/dgram/udp6.py +++ b/pysnmp/carrier/asynsock/dgram/udp6.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.carrier.asyncore.dgram.udp6 import * diff --git a/pysnmp/carrier/asynsock/dgram/unix.py b/pysnmp/carrier/asynsock/dgram/unix.py index 6e75628a1..887b4edb6 100644 --- a/pysnmp/carrier/asynsock/dgram/unix.py +++ b/pysnmp/carrier/asynsock/dgram/unix.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.carrier.asyncore.dgram.unix import * diff --git a/pysnmp/carrier/asynsock/dispatch.py b/pysnmp/carrier/asynsock/dispatch.py index 120a9a70f..478d9e49e 100644 --- a/pysnmp/carrier/asynsock/dispatch.py +++ b/pysnmp/carrier/asynsock/dispatch.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.carrier.asyncore.dispatch import * diff --git a/pysnmp/carrier/base.py b/pysnmp/carrier/base.py index aedd42eb3..51a2d0b5d 100644 --- a/pysnmp/carrier/base.py +++ b/pysnmp/carrier/base.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.carrier import error diff --git a/pysnmp/carrier/error.py b/pysnmp/carrier/error.py index 601fd0eea..34d1add28 100644 --- a/pysnmp/carrier/error.py +++ b/pysnmp/carrier/error.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp import error diff --git a/pysnmp/carrier/sockfix.py b/pysnmp/carrier/sockfix.py index 01b69223e..d4ea504ef 100644 --- a/pysnmp/carrier/sockfix.py +++ b/pysnmp/carrier/sockfix.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import socket diff --git a/pysnmp/carrier/sockmsg.py b/pysnmp/carrier/sockmsg.py index 634ea1878..0d79b2c70 100644 --- a/pysnmp/carrier/sockmsg.py +++ b/pysnmp/carrier/sockmsg.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # The following routines act like sendto()/recvfrom() calls but additionally diff --git a/pysnmp/carrier/twisted/base.py b/pysnmp/carrier/twisted/base.py index f7d0f3065..d8b75bc73 100644 --- a/pysnmp/carrier/twisted/base.py +++ b/pysnmp/carrier/twisted/base.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # Copyright (C) 2008 Truelite Srl diff --git a/pysnmp/carrier/twisted/dgram/base.py b/pysnmp/carrier/twisted/dgram/base.py index 7df96d93a..4995aec44 100644 --- a/pysnmp/carrier/twisted/dgram/base.py +++ b/pysnmp/carrier/twisted/dgram/base.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/carrier/twisted/dgram/udp.py b/pysnmp/carrier/twisted/dgram/udp.py index 1eaf86361..ac0f23f19 100644 --- a/pysnmp/carrier/twisted/dgram/udp.py +++ b/pysnmp/carrier/twisted/dgram/udp.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/carrier/twisted/dgram/unix.py b/pysnmp/carrier/twisted/dgram/unix.py index 5695baadb..a256969ca 100644 --- a/pysnmp/carrier/twisted/dgram/unix.py +++ b/pysnmp/carrier/twisted/dgram/unix.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/carrier/twisted/dispatch.py b/pysnmp/carrier/twisted/dispatch.py index 5113e7b24..def16f6f0 100644 --- a/pysnmp/carrier/twisted/dispatch.py +++ b/pysnmp/carrier/twisted/dispatch.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # Copyright (C) 2008 Truelite Srl diff --git a/pysnmp/debug.py b/pysnmp/debug.py index 83502867c..715a38761 100644 --- a/pysnmp/debug.py +++ b/pysnmp/debug.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import logging diff --git a/pysnmp/entity/config.py b/pysnmp/entity/config.py index 4c069c72e..20fe63a0c 100644 --- a/pysnmp/entity/config.py +++ b/pysnmp/entity/config.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.compat.octets import null diff --git a/pysnmp/entity/engine.py b/pysnmp/entity/engine.py index 0550871b7..a299267cd 100644 --- a/pysnmp/entity/engine.py +++ b/pysnmp/entity/engine.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import os diff --git a/pysnmp/entity/observer.py b/pysnmp/entity/observer.py index a52394711..3f7bd8457 100644 --- a/pysnmp/entity/observer.py +++ b/pysnmp/entity/observer.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp import error diff --git a/pysnmp/entity/rfc3413/cmdgen.py b/pysnmp/entity/rfc3413/cmdgen.py index be7eee0d1..79eba57f0 100644 --- a/pysnmp/entity/rfc3413/cmdgen.py +++ b/pysnmp/entity/rfc3413/cmdgen.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/entity/rfc3413/cmdrsp.py b/pysnmp/entity/rfc3413/cmdrsp.py index f69e26461..3e1db4f36 100644 --- a/pysnmp/entity/rfc3413/cmdrsp.py +++ b/pysnmp/entity/rfc3413/cmdrsp.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/entity/rfc3413/config.py b/pysnmp/entity/rfc3413/config.py index 5562784cd..144aa4862 100644 --- a/pysnmp/entity/rfc3413/config.py +++ b/pysnmp/entity/rfc3413/config.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.smi.error import SmiError, NoSuchInstanceError diff --git a/pysnmp/entity/rfc3413/context.py b/pysnmp/entity/rfc3413/context.py index ec705a728..3975e6705 100644 --- a/pysnmp/entity/rfc3413/context.py +++ b/pysnmp/entity/rfc3413/context.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.type import univ diff --git a/pysnmp/entity/rfc3413/mibvar.py b/pysnmp/entity/rfc3413/mibvar.py index 02051b5c1..bfc63a06b 100644 --- a/pysnmp/entity/rfc3413/mibvar.py +++ b/pysnmp/entity/rfc3413/mibvar.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # THESE FUNCTIONS ARE OBSOLETE AND MUST NOT BE USED! diff --git a/pysnmp/entity/rfc3413/ntforg.py b/pysnmp/entity/rfc3413/ntforg.py index abf1a47f0..0065ffede 100644 --- a/pysnmp/entity/rfc3413/ntforg.py +++ b/pysnmp/entity/rfc3413/ntforg.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/entity/rfc3413/ntfrcv.py b/pysnmp/entity/rfc3413/ntfrcv.py index 841ca793d..6066f0865 100644 --- a/pysnmp/entity/rfc3413/ntfrcv.py +++ b/pysnmp/entity/rfc3413/ntfrcv.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/entity/rfc3413/oneliner/cmdgen.py b/pysnmp/entity/rfc3413/oneliner/cmdgen.py index ecbca4511..b74b7f135 100644 --- a/pysnmp/entity/rfc3413/oneliner/cmdgen.py +++ b/pysnmp/entity/rfc3413/oneliner/cmdgen.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # All code in this file belongs to obsolete, compatibility wrappers. diff --git a/pysnmp/entity/rfc3413/oneliner/ntforg.py b/pysnmp/entity/rfc3413/oneliner/ntforg.py index 8f24d84a0..0f0d2d2e7 100644 --- a/pysnmp/entity/rfc3413/oneliner/ntforg.py +++ b/pysnmp/entity/rfc3413/oneliner/ntforg.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # All code in this file belongs to obsolete, compatibility wrappers. diff --git a/pysnmp/error.py b/pysnmp/error.py index e47f4bcb4..f80046387 100644 --- a/pysnmp/error.py +++ b/pysnmp/error.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # diff --git a/pysnmp/hlapi/__init__.py b/pysnmp/hlapi/__init__.py index 7a9c63714..df0ad3136 100644 --- a/pysnmp/hlapi/__init__.py +++ b/pysnmp/hlapi/__init__.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto.rfc1902 import * diff --git a/pysnmp/hlapi/asyncio/__init__.py b/pysnmp/hlapi/asyncio/__init__.py index 24e71f7c1..b136b0846 100644 --- a/pysnmp/hlapi/asyncio/__init__.py +++ b/pysnmp/hlapi/asyncio/__init__.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto.rfc1902 import * diff --git a/pysnmp/hlapi/asyncio/cmdgen.py b/pysnmp/hlapi/asyncio/cmdgen.py index 46e58aa67..1bd5cdf47 100644 --- a/pysnmp/hlapi/asyncio/cmdgen.py +++ b/pysnmp/hlapi/asyncio/cmdgen.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # Copyright (C) 2014, Zebra Technologies diff --git a/pysnmp/hlapi/asyncio/ntforg.py b/pysnmp/hlapi/asyncio/ntforg.py index be75b3a55..4481ef6f9 100644 --- a/pysnmp/hlapi/asyncio/ntforg.py +++ b/pysnmp/hlapi/asyncio/ntforg.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # Copyright (C) 2014, Zebra Technologies diff --git a/pysnmp/hlapi/asyncio/transport.py b/pysnmp/hlapi/asyncio/transport.py index 4e7b91106..0f78f82fd 100644 --- a/pysnmp/hlapi/asyncio/transport.py +++ b/pysnmp/hlapi/asyncio/transport.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import socket diff --git a/pysnmp/hlapi/asyncore/__init__.py b/pysnmp/hlapi/asyncore/__init__.py index 303b4b842..f9bb3dd15 100644 --- a/pysnmp/hlapi/asyncore/__init__.py +++ b/pysnmp/hlapi/asyncore/__init__.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto.rfc1902 import * diff --git a/pysnmp/hlapi/asyncore/cmdgen.py b/pysnmp/hlapi/asyncore/cmdgen.py index c7b8a2d3a..284edd68c 100644 --- a/pysnmp/hlapi/asyncore/cmdgen.py +++ b/pysnmp/hlapi/asyncore/cmdgen.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.entity.rfc3413 import cmdgen diff --git a/pysnmp/hlapi/asyncore/ntforg.py b/pysnmp/hlapi/asyncore/ntforg.py index b35bdba91..a27e6a883 100644 --- a/pysnmp/hlapi/asyncore/ntforg.py +++ b/pysnmp/hlapi/asyncore/ntforg.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.smi.rfc1902 import * diff --git a/pysnmp/hlapi/asyncore/sync/__init__.py b/pysnmp/hlapi/asyncore/sync/__init__.py index f412713ac..14f6b926e 100644 --- a/pysnmp/hlapi/asyncore/sync/__init__.py +++ b/pysnmp/hlapi/asyncore/sync/__init__.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto.rfc1902 import * diff --git a/pysnmp/hlapi/asyncore/sync/cmdgen.py b/pysnmp/hlapi/asyncore/sync/cmdgen.py index 3868ac66b..e60706130 100644 --- a/pysnmp/hlapi/asyncore/sync/cmdgen.py +++ b/pysnmp/hlapi/asyncore/sync/cmdgen.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from sys import version_info diff --git a/pysnmp/hlapi/asyncore/sync/compat/cmdgen.py b/pysnmp/hlapi/asyncore/sync/compat/cmdgen.py index 066d1d9ff..8624d8572 100644 --- a/pysnmp/hlapi/asyncore/sync/compat/cmdgen.py +++ b/pysnmp/hlapi/asyncore/sync/compat/cmdgen.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # This is a Python 2.6- version of the same file at level up diff --git a/pysnmp/hlapi/asyncore/sync/compat/ntforg.py b/pysnmp/hlapi/asyncore/sync/compat/ntforg.py index 80221713c..a655b62f5 100644 --- a/pysnmp/hlapi/asyncore/sync/compat/ntforg.py +++ b/pysnmp/hlapi/asyncore/sync/compat/ntforg.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # This is a Python 2.6- version of the same file at level up diff --git a/pysnmp/hlapi/asyncore/sync/ntforg.py b/pysnmp/hlapi/asyncore/sync/ntforg.py index 6f732d756..d8029564b 100644 --- a/pysnmp/hlapi/asyncore/sync/ntforg.py +++ b/pysnmp/hlapi/asyncore/sync/ntforg.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from sys import version_info diff --git a/pysnmp/hlapi/asyncore/transport.py b/pysnmp/hlapi/asyncore/transport.py index a9b3a5736..a24f88f15 100644 --- a/pysnmp/hlapi/asyncore/transport.py +++ b/pysnmp/hlapi/asyncore/transport.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import socket diff --git a/pysnmp/hlapi/auth.py b/pysnmp/hlapi/auth.py index 20db368f4..2c729b224 100644 --- a/pysnmp/hlapi/auth.py +++ b/pysnmp/hlapi/auth.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.entity import config diff --git a/pysnmp/hlapi/context.py b/pysnmp/hlapi/context.py index 754c43cc3..73e850b22 100644 --- a/pysnmp/hlapi/context.py +++ b/pysnmp/hlapi/context.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.compat.octets import null diff --git a/pysnmp/hlapi/lcd.py b/pysnmp/hlapi/lcd.py index 43bfc1016..9a188b2e5 100644 --- a/pysnmp/hlapi/lcd.py +++ b/pysnmp/hlapi/lcd.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.entity import config diff --git a/pysnmp/hlapi/transport.py b/pysnmp/hlapi/transport.py index 44a36504b..351f01f10 100644 --- a/pysnmp/hlapi/transport.py +++ b/pysnmp/hlapi/transport.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.compat.octets import null diff --git a/pysnmp/hlapi/twisted/cmdgen.py b/pysnmp/hlapi/twisted/cmdgen.py index 611de0508..c51d0684d 100644 --- a/pysnmp/hlapi/twisted/cmdgen.py +++ b/pysnmp/hlapi/twisted/cmdgen.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/hlapi/twisted/ntforg.py b/pysnmp/hlapi/twisted/ntforg.py index a33e226db..c8324deff 100644 --- a/pysnmp/hlapi/twisted/ntforg.py +++ b/pysnmp/hlapi/twisted/ntforg.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/hlapi/twisted/transport.py b/pysnmp/hlapi/twisted/transport.py index aeea22da8..2312d68d6 100644 --- a/pysnmp/hlapi/twisted/transport.py +++ b/pysnmp/hlapi/twisted/transport.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import socket, sys diff --git a/pysnmp/hlapi/varbinds.py b/pysnmp/hlapi/varbinds.py index ac9151a88..ece7b92b9 100644 --- a/pysnmp/hlapi/varbinds.py +++ b/pysnmp/hlapi/varbinds.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.smi import view diff --git a/pysnmp/nextid.py b/pysnmp/nextid.py index 04e269d0d..f81bc9b61 100644 --- a/pysnmp/nextid.py +++ b/pysnmp/nextid.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import random diff --git a/pysnmp/proto/acmod/rfc3415.py b/pysnmp/proto/acmod/rfc3415.py index 3c5afe9e4..cf020d9f8 100644 --- a/pysnmp/proto/acmod/rfc3415.py +++ b/pysnmp/proto/acmod/rfc3415.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.smi.error import NoSuchInstanceError diff --git a/pysnmp/proto/acmod/void.py b/pysnmp/proto/acmod/void.py index 3d9533d40..ecc4c4b63 100644 --- a/pysnmp/proto/acmod/void.py +++ b/pysnmp/proto/acmod/void.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto import errind, error diff --git a/pysnmp/proto/api/__init__.py b/pysnmp/proto/api/__init__.py index 85b37bb49..d742ecc76 100644 --- a/pysnmp/proto/api/__init__.py +++ b/pysnmp/proto/api/__init__.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto.api import v1, v2c, verdec diff --git a/pysnmp/proto/api/v1.py b/pysnmp/proto/api/v1.py index 709a0b683..1f9689868 100644 --- a/pysnmp/proto/api/v1.py +++ b/pysnmp/proto/api/v1.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.type import univ diff --git a/pysnmp/proto/api/v2c.py b/pysnmp/proto/api/v2c.py index eec78e797..656c88f48 100644 --- a/pysnmp/proto/api/v2c.py +++ b/pysnmp/proto/api/v2c.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto import rfc1901, rfc1902, rfc1905 diff --git a/pysnmp/proto/api/verdec.py b/pysnmp/proto/api/verdec.py index 635c5a2d6..7076e63c1 100644 --- a/pysnmp/proto/api/verdec.py +++ b/pysnmp/proto/api/verdec.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.type import univ diff --git a/pysnmp/proto/cache.py b/pysnmp/proto/cache.py index e9218416d..9eae19c90 100644 --- a/pysnmp/proto/cache.py +++ b/pysnmp/proto/cache.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto import error diff --git a/pysnmp/proto/errind.py b/pysnmp/proto/errind.py index d660af54f..5683be8bc 100644 --- a/pysnmp/proto/errind.py +++ b/pysnmp/proto/errind.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # diff --git a/pysnmp/proto/error.py b/pysnmp/proto/error.py index eaa21f6b7..14e6b737c 100644 --- a/pysnmp/proto/error.py +++ b/pysnmp/proto/error.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.error import PyAsn1Error diff --git a/pysnmp/proto/mpmod/base.py b/pysnmp/proto/mpmod/base.py index 2ed7834c2..620ccf894 100644 --- a/pysnmp/proto/mpmod/base.py +++ b/pysnmp/proto/mpmod/base.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto.mpmod import cache diff --git a/pysnmp/proto/mpmod/cache.py b/pysnmp/proto/mpmod/cache.py index 6e9a886d3..4bc67169e 100644 --- a/pysnmp/proto/mpmod/cache.py +++ b/pysnmp/proto/mpmod/cache.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto import error diff --git a/pysnmp/proto/mpmod/rfc2576.py b/pysnmp/proto/mpmod/rfc2576.py index 717110d34..b355868f6 100644 --- a/pysnmp/proto/mpmod/rfc2576.py +++ b/pysnmp/proto/mpmod/rfc2576.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/proto/proxy/rfc2576.py b/pysnmp/proto/proxy/rfc2576.py index 6ea99b596..6b1dfcbb8 100644 --- a/pysnmp/proto/proxy/rfc2576.py +++ b/pysnmp/proto/proxy/rfc2576.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto import rfc1905, rfc3411, error diff --git a/pysnmp/proto/rfc1155.py b/pysnmp/proto/rfc1155.py index 7bd2ea0f3..8fb48e8c1 100644 --- a/pysnmp/proto/rfc1155.py +++ b/pysnmp/proto/rfc1155.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.type import univ, tag, constraint, namedtype diff --git a/pysnmp/proto/rfc1157.py b/pysnmp/proto/rfc1157.py index 603e80411..8cd354b8a 100644 --- a/pysnmp/proto/rfc1157.py +++ b/pysnmp/proto/rfc1157.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.type import univ, tag, namedtype, namedval diff --git a/pysnmp/proto/rfc1901.py b/pysnmp/proto/rfc1901.py index c4d83ad49..de912395b 100644 --- a/pysnmp/proto/rfc1901.py +++ b/pysnmp/proto/rfc1901.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.type import univ, namedtype, namedval diff --git a/pysnmp/proto/rfc1902.py b/pysnmp/proto/rfc1902.py index 4bd826690..60dd6a844 100644 --- a/pysnmp/proto/rfc1902.py +++ b/pysnmp/proto/rfc1902.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from sys import version_info diff --git a/pysnmp/proto/rfc1905.py b/pysnmp/proto/rfc1905.py index ac108617f..4a2294958 100644 --- a/pysnmp/proto/rfc1905.py +++ b/pysnmp/proto/rfc1905.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.type import univ, tag, constraint, namedtype, namedval diff --git a/pysnmp/proto/rfc3411.py b/pysnmp/proto/rfc3411.py index d45353efd..bd4d40d3f 100644 --- a/pysnmp/proto/rfc3411.py +++ b/pysnmp/proto/rfc3411.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto import rfc1157, rfc1905 diff --git a/pysnmp/proto/rfc3412.py b/pysnmp/proto/rfc3412.py index 386d1aaa6..100fff1c0 100644 --- a/pysnmp/proto/rfc3412.py +++ b/pysnmp/proto/rfc3412.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/proto/secmod/base.py b/pysnmp/proto/secmod/base.py index 58fbca197..63df39c71 100644 --- a/pysnmp/proto/secmod/base.py +++ b/pysnmp/proto/secmod/base.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto.secmod import cache diff --git a/pysnmp/proto/secmod/cache.py b/pysnmp/proto/secmod/cache.py index b6a0992f3..f5843c500 100644 --- a/pysnmp/proto/secmod/cache.py +++ b/pysnmp/proto/secmod/cache.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp import nextid diff --git a/pysnmp/proto/secmod/eso/priv/aes192.py b/pysnmp/proto/secmod/eso/priv/aes192.py index 33da51544..399a225ba 100644 --- a/pysnmp/proto/secmod/eso/priv/aes192.py +++ b/pysnmp/proto/secmod/eso/priv/aes192.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto.secmod.eso.priv import aesbase diff --git a/pysnmp/proto/secmod/eso/priv/aes256.py b/pysnmp/proto/secmod/eso/priv/aes256.py index 08d120d61..360f26c33 100644 --- a/pysnmp/proto/secmod/eso/priv/aes256.py +++ b/pysnmp/proto/secmod/eso/priv/aes256.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto.secmod.eso.priv import aesbase diff --git a/pysnmp/proto/secmod/eso/priv/aesbase.py b/pysnmp/proto/secmod/eso/priv/aesbase.py index 540ab0699..676afbbbc 100644 --- a/pysnmp/proto/secmod/eso/priv/aesbase.py +++ b/pysnmp/proto/secmod/eso/priv/aesbase.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto.secmod.rfc3826.priv import aes diff --git a/pysnmp/proto/secmod/eso/priv/des3.py b/pysnmp/proto/secmod/eso/priv/des3.py index 426df6337..6976760c5 100644 --- a/pysnmp/proto/secmod/eso/priv/des3.py +++ b/pysnmp/proto/secmod/eso/priv/des3.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import random diff --git a/pysnmp/proto/secmod/rfc2576.py b/pysnmp/proto/secmod/rfc2576.py index 3e4b96c18..4da7d495d 100644 --- a/pysnmp/proto/secmod/rfc2576.py +++ b/pysnmp/proto/secmod/rfc2576.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/proto/secmod/rfc3414/__init__.py b/pysnmp/proto/secmod/rfc3414/__init__.py index 9d06a20fb..cb2709fc7 100644 --- a/pysnmp/proto/secmod/rfc3414/__init__.py +++ b/pysnmp/proto/secmod/rfc3414/__init__.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto.secmod.rfc3414 import service diff --git a/pysnmp/proto/secmod/rfc3414/auth/base.py b/pysnmp/proto/secmod/rfc3414/auth/base.py index 496f1bcdf..3ff8778f3 100644 --- a/pysnmp/proto/secmod/rfc3414/auth/base.py +++ b/pysnmp/proto/secmod/rfc3414/auth/base.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto import errind, error diff --git a/pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py b/pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py index 387c8f395..761bf3d96 100644 --- a/pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py +++ b/pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # try: diff --git a/pysnmp/proto/secmod/rfc3414/auth/hmacsha.py b/pysnmp/proto/secmod/rfc3414/auth/hmacsha.py index d4b900f39..3efe9c496 100644 --- a/pysnmp/proto/secmod/rfc3414/auth/hmacsha.py +++ b/pysnmp/proto/secmod/rfc3414/auth/hmacsha.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # try: diff --git a/pysnmp/proto/secmod/rfc3414/auth/noauth.py b/pysnmp/proto/secmod/rfc3414/auth/noauth.py index fbd4104ba..d1dea4f56 100644 --- a/pysnmp/proto/secmod/rfc3414/auth/noauth.py +++ b/pysnmp/proto/secmod/rfc3414/auth/noauth.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto.secmod.rfc3414.auth import base diff --git a/pysnmp/proto/secmod/rfc3414/localkey.py b/pysnmp/proto/secmod/rfc3414/localkey.py index c7c730f2f..651722e4a 100644 --- a/pysnmp/proto/secmod/rfc3414/localkey.py +++ b/pysnmp/proto/secmod/rfc3414/localkey.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # try: diff --git a/pysnmp/proto/secmod/rfc3414/priv/base.py b/pysnmp/proto/secmod/rfc3414/priv/base.py index c15c973fa..a7c68a11a 100644 --- a/pysnmp/proto/secmod/rfc3414/priv/base.py +++ b/pysnmp/proto/secmod/rfc3414/priv/base.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto import error diff --git a/pysnmp/proto/secmod/rfc3414/priv/des.py b/pysnmp/proto/secmod/rfc3414/priv/des.py index b66889e21..b1d38b652 100644 --- a/pysnmp/proto/secmod/rfc3414/priv/des.py +++ b/pysnmp/proto/secmod/rfc3414/priv/des.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import random diff --git a/pysnmp/proto/secmod/rfc3414/priv/nopriv.py b/pysnmp/proto/secmod/rfc3414/priv/nopriv.py index 473cd4fba..6721153b9 100644 --- a/pysnmp/proto/secmod/rfc3414/priv/nopriv.py +++ b/pysnmp/proto/secmod/rfc3414/priv/nopriv.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto.secmod.rfc3414.priv import base diff --git a/pysnmp/proto/secmod/rfc3414/service.py b/pysnmp/proto/secmod/rfc3414/service.py index e8e363b98..545ed874c 100644 --- a/pysnmp/proto/secmod/rfc3414/service.py +++ b/pysnmp/proto/secmod/rfc3414/service.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import time diff --git a/pysnmp/proto/secmod/rfc3826/priv/aes.py b/pysnmp/proto/secmod/rfc3826/priv/aes.py index c702a4188..6f9893bcd 100644 --- a/pysnmp/proto/secmod/rfc3826/priv/aes.py +++ b/pysnmp/proto/secmod/rfc3826/priv/aes.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import random diff --git a/pysnmp/smi/builder.py b/pysnmp/smi/builder.py index 1a1f2e8f7..c13445924 100644 --- a/pysnmp/smi/builder.py +++ b/pysnmp/smi/builder.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import os diff --git a/pysnmp/smi/compiler.py b/pysnmp/smi/compiler.py index 4e4ade156..57ba9bd71 100644 --- a/pysnmp/smi/compiler.py +++ b/pysnmp/smi/compiler.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import os diff --git a/pysnmp/smi/error.py b/pysnmp/smi/error.py index 8f1c509da..4b7ba5cca 100644 --- a/pysnmp/smi/error.py +++ b/pysnmp/smi/error.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.error import PyAsn1Error diff --git a/pysnmp/smi/exval.py b/pysnmp/smi/exval.py index bc6e3d0f8..02e8cab84 100644 --- a/pysnmp/smi/exval.py +++ b/pysnmp/smi/exval.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pysnmp.proto import rfc1905 diff --git a/pysnmp/smi/indices.py b/pysnmp/smi/indices.py index 7e15a8cde..677ac97c1 100644 --- a/pysnmp/smi/indices.py +++ b/pysnmp/smi/indices.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from bisect import bisect diff --git a/pysnmp/smi/instrum.py b/pysnmp/smi/instrum.py index bd1444a9d..3c31b6313 100644 --- a/pysnmp/smi/instrum.py +++ b/pysnmp/smi/instrum.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/smi/mibs/ASN1-ENUMERATION.py b/pysnmp/smi/mibs/ASN1-ENUMERATION.py index 5eb9f80e1..6b906def4 100644 --- a/pysnmp/smi/mibs/ASN1-ENUMERATION.py +++ b/pysnmp/smi/mibs/ASN1-ENUMERATION.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.type import namedval diff --git a/pysnmp/smi/mibs/ASN1-REFINEMENT.py b/pysnmp/smi/mibs/ASN1-REFINEMENT.py index 64e20c2e3..1f5bce27d 100644 --- a/pysnmp/smi/mibs/ASN1-REFINEMENT.py +++ b/pysnmp/smi/mibs/ASN1-REFINEMENT.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.type import constraint diff --git a/pysnmp/smi/mibs/ASN1.py b/pysnmp/smi/mibs/ASN1.py index e41d3b450..4a393d3b6 100644 --- a/pysnmp/smi/mibs/ASN1.py +++ b/pysnmp/smi/mibs/ASN1.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from pyasn1.type import univ diff --git a/pysnmp/smi/mibs/INET-ADDRESS-MIB.py b/pysnmp/smi/mibs/INET-ADDRESS-MIB.py index 5e40017c1..b52f541c2 100644 --- a/pysnmp/smi/mibs/INET-ADDRESS-MIB.py +++ b/pysnmp/smi/mibs/INET-ADDRESS-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module INET-ADDRESS-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/PYSNMP-MIB.py b/pysnmp/smi/mibs/PYSNMP-MIB.py index 64915f4b8..9e0df7214 100644 --- a/pysnmp/smi/mibs/PYSNMP-MIB.py +++ b/pysnmp/smi/mibs/PYSNMP-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module PYSNMP-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/PYSNMP-SOURCE-MIB.py b/pysnmp/smi/mibs/PYSNMP-SOURCE-MIB.py index 216bcca1e..ba645a4b5 100644 --- a/pysnmp/smi/mibs/PYSNMP-SOURCE-MIB.py +++ b/pysnmp/smi/mibs/PYSNMP-SOURCE-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module PYSNMP-SOURCE-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/PYSNMP-USM-MIB.py b/pysnmp/smi/mibs/PYSNMP-USM-MIB.py index 5dac372f3..b48e3da8c 100644 --- a/pysnmp/smi/mibs/PYSNMP-USM-MIB.py +++ b/pysnmp/smi/mibs/PYSNMP-USM-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module PYSNMP-USM-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/RFC1158-MIB.py b/pysnmp/smi/mibs/RFC1158-MIB.py index 4c1c9c6cf..ef5c2c166 100644 --- a/pysnmp/smi/mibs/RFC1158-MIB.py +++ b/pysnmp/smi/mibs/RFC1158-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module RFC1158-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/RFC1213-MIB.py b/pysnmp/smi/mibs/RFC1213-MIB.py index 46ec17e07..01cd008ab 100644 --- a/pysnmp/smi/mibs/RFC1213-MIB.py +++ b/pysnmp/smi/mibs/RFC1213-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module RFC1213-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/SNMP-COMMUNITY-MIB.py b/pysnmp/smi/mibs/SNMP-COMMUNITY-MIB.py index 4e9d0aea8..0f524053d 100644 --- a/pysnmp/smi/mibs/SNMP-COMMUNITY-MIB.py +++ b/pysnmp/smi/mibs/SNMP-COMMUNITY-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module SNMP-COMMUNITY-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/SNMP-FRAMEWORK-MIB.py b/pysnmp/smi/mibs/SNMP-FRAMEWORK-MIB.py index fdaaf01a1..e7ff01f3a 100644 --- a/pysnmp/smi/mibs/SNMP-FRAMEWORK-MIB.py +++ b/pysnmp/smi/mibs/SNMP-FRAMEWORK-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module SNMP-FRAMEWORK-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/SNMP-MPD-MIB.py b/pysnmp/smi/mibs/SNMP-MPD-MIB.py index 402e69130..147981004 100644 --- a/pysnmp/smi/mibs/SNMP-MPD-MIB.py +++ b/pysnmp/smi/mibs/SNMP-MPD-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # diff --git a/pysnmp/smi/mibs/SNMP-NOTIFICATION-MIB.py b/pysnmp/smi/mibs/SNMP-NOTIFICATION-MIB.py index d71b9ae2e..87b110c51 100644 --- a/pysnmp/smi/mibs/SNMP-NOTIFICATION-MIB.py +++ b/pysnmp/smi/mibs/SNMP-NOTIFICATION-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module SNMP-NOTIFICATION-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/SNMP-PROXY-MIB.py b/pysnmp/smi/mibs/SNMP-PROXY-MIB.py index a9f5b458d..b4ac1f2db 100644 --- a/pysnmp/smi/mibs/SNMP-PROXY-MIB.py +++ b/pysnmp/smi/mibs/SNMP-PROXY-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module SNMP-PROXY-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/SNMP-TARGET-MIB.py b/pysnmp/smi/mibs/SNMP-TARGET-MIB.py index 14d0fdc45..af8439e0d 100644 --- a/pysnmp/smi/mibs/SNMP-TARGET-MIB.py +++ b/pysnmp/smi/mibs/SNMP-TARGET-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module SNMP-TARGET-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/SNMP-USER-BASED-SM-3DES-MIB.py b/pysnmp/smi/mibs/SNMP-USER-BASED-SM-3DES-MIB.py index 0a368fc92..555dd1780 100644 --- a/pysnmp/smi/mibs/SNMP-USER-BASED-SM-3DES-MIB.py +++ b/pysnmp/smi/mibs/SNMP-USER-BASED-SM-3DES-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module SNMP-USER-BASED-SM-3DES-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/SNMP-USER-BASED-SM-MIB.py b/pysnmp/smi/mibs/SNMP-USER-BASED-SM-MIB.py index 1e998f9f0..498ee9449 100644 --- a/pysnmp/smi/mibs/SNMP-USER-BASED-SM-MIB.py +++ b/pysnmp/smi/mibs/SNMP-USER-BASED-SM-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module SNMP-USER-BASED-SM-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/SNMP-USM-AES-MIB.py b/pysnmp/smi/mibs/SNMP-USM-AES-MIB.py index 83d3bdaf8..7aee7bf6c 100644 --- a/pysnmp/smi/mibs/SNMP-USM-AES-MIB.py +++ b/pysnmp/smi/mibs/SNMP-USM-AES-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module SNMP-USM-AES-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/SNMP-VIEW-BASED-ACM-MIB.py b/pysnmp/smi/mibs/SNMP-VIEW-BASED-ACM-MIB.py index 8fbf962ea..2b04e4d23 100644 --- a/pysnmp/smi/mibs/SNMP-VIEW-BASED-ACM-MIB.py +++ b/pysnmp/smi/mibs/SNMP-VIEW-BASED-ACM-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module SNMP-VIEW-BASED-ACM-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/SNMPv2-CONF.py b/pysnmp/smi/mibs/SNMPv2-CONF.py index e333336d8..c41599d58 100644 --- a/pysnmp/smi/mibs/SNMPv2-CONF.py +++ b/pysnmp/smi/mibs/SNMPv2-CONF.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module SNMPv2-CONF (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/SNMPv2-MIB.py b/pysnmp/smi/mibs/SNMPv2-MIB.py index e0d7001fa..a31d6871e 100644 --- a/pysnmp/smi/mibs/SNMPv2-MIB.py +++ b/pysnmp/smi/mibs/SNMPv2-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module SNMPv2-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/SNMPv2-SMI.py b/pysnmp/smi/mibs/SNMPv2-SMI.py index b64089306..a34755408 100644 --- a/pysnmp/smi/mibs/SNMPv2-SMI.py +++ b/pysnmp/smi/mibs/SNMPv2-SMI.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/smi/mibs/SNMPv2-TC.py b/pysnmp/smi/mibs/SNMPv2-TC.py index c07e7e2c4..176e6533f 100644 --- a/pysnmp/smi/mibs/SNMPv2-TC.py +++ b/pysnmp/smi/mibs/SNMPv2-TC.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/smi/mibs/SNMPv2-TM.py b/pysnmp/smi/mibs/SNMPv2-TM.py index 3d04c2162..67c321371 100644 --- a/pysnmp/smi/mibs/SNMPv2-TM.py +++ b/pysnmp/smi/mibs/SNMPv2-TM.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module SNMPv2-TM (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/TRANSPORT-ADDRESS-MIB.py b/pysnmp/smi/mibs/TRANSPORT-ADDRESS-MIB.py index e507358e9..38d12699a 100644 --- a/pysnmp/smi/mibs/TRANSPORT-ADDRESS-MIB.py +++ b/pysnmp/smi/mibs/TRANSPORT-ADDRESS-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # # PySNMP MIB module TRANSPORT-ADDRESS-MIB (http://snmplabs.com/pysnmp) diff --git a/pysnmp/smi/mibs/instances/__PYSNMP-USM-MIB.py b/pysnmp/smi/mibs/instances/__PYSNMP-USM-MIB.py index 1b0f118a7..2a83fc377 100644 --- a/pysnmp/smi/mibs/instances/__PYSNMP-USM-MIB.py +++ b/pysnmp/smi/mibs/instances/__PYSNMP-USM-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # MibScalarInstance, = mibBuilder.importSymbols('SNMPv2-SMI', 'MibScalarInstance') diff --git a/pysnmp/smi/mibs/instances/__SNMP-FRAMEWORK-MIB.py b/pysnmp/smi/mibs/instances/__SNMP-FRAMEWORK-MIB.py index e20a1f5e0..cfbcb9ff2 100644 --- a/pysnmp/smi/mibs/instances/__SNMP-FRAMEWORK-MIB.py +++ b/pysnmp/smi/mibs/instances/__SNMP-FRAMEWORK-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import time diff --git a/pysnmp/smi/mibs/instances/__SNMP-MPD-MIB.py b/pysnmp/smi/mibs/instances/__SNMP-MPD-MIB.py index 086dabcea..634398fe5 100644 --- a/pysnmp/smi/mibs/instances/__SNMP-MPD-MIB.py +++ b/pysnmp/smi/mibs/instances/__SNMP-MPD-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # MibScalarInstance, = mibBuilder.importSymbols('SNMPv2-SMI', 'MibScalarInstance') diff --git a/pysnmp/smi/mibs/instances/__SNMP-TARGET-MIB.py b/pysnmp/smi/mibs/instances/__SNMP-TARGET-MIB.py index fc7e0b0c7..fa9f4421c 100644 --- a/pysnmp/smi/mibs/instances/__SNMP-TARGET-MIB.py +++ b/pysnmp/smi/mibs/instances/__SNMP-TARGET-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # MibScalarInstance, = mibBuilder.importSymbols( diff --git a/pysnmp/smi/mibs/instances/__SNMP-USER-BASED-SM-MIB.py b/pysnmp/smi/mibs/instances/__SNMP-USER-BASED-SM-MIB.py index 82a1eb3b2..175cf0a0b 100644 --- a/pysnmp/smi/mibs/instances/__SNMP-USER-BASED-SM-MIB.py +++ b/pysnmp/smi/mibs/instances/__SNMP-USER-BASED-SM-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # MibScalarInstance, = mibBuilder.importSymbols('SNMPv2-SMI', 'MibScalarInstance') diff --git a/pysnmp/smi/mibs/instances/__SNMP-VIEW-BASED-ACM-MIB.py b/pysnmp/smi/mibs/instances/__SNMP-VIEW-BASED-ACM-MIB.py index ef0d6ecc4..740dcf913 100644 --- a/pysnmp/smi/mibs/instances/__SNMP-VIEW-BASED-ACM-MIB.py +++ b/pysnmp/smi/mibs/instances/__SNMP-VIEW-BASED-ACM-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # MibScalarInstance, = mibBuilder.importSymbols('SNMPv2-SMI', 'MibScalarInstance') diff --git a/pysnmp/smi/mibs/instances/__SNMPv2-MIB.py b/pysnmp/smi/mibs/instances/__SNMPv2-MIB.py index 7d9ce90fe..48d4bd991 100644 --- a/pysnmp/smi/mibs/instances/__SNMPv2-MIB.py +++ b/pysnmp/smi/mibs/instances/__SNMPv2-MIB.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # from sys import version diff --git a/pysnmp/smi/rfc1902.py b/pysnmp/smi/rfc1902.py index 73612ecde..c89900258 100644 --- a/pysnmp/smi/rfc1902.py +++ b/pysnmp/smi/rfc1902.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/smi/view.py b/pysnmp/smi/view.py index 8c5dd81c1..223e22d8d 100644 --- a/pysnmp/smi/view.py +++ b/pysnmp/smi/view.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2018, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys From d1689d72724814fefbef512f84d417be399d308d Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 30 Dec 2018 00:02:25 +0100 Subject: [PATCH 058/102] Fix undefined names in `TRANSPORT-ADDRESS-MIB.py` --- CHANGES.txt | 2 ++ pysnmp/smi/mibs/TRANSPORT-ADDRESS-MIB.py | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index fb876491d..70046378e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,6 +12,8 @@ Revision 4.4.7, released 2018-12-29 sending SNMPv3 TRAP with non-default `contextName` would fail. - Fixed possible duplicate key occurrence in the `OrderedDict` following a race condition +- Fixed undefined name references in `inet_pton`/`inet_ntop` substitute + routines for IPv6 in `TRANSPORT-ADDRESS-MIB.py` Revision 4.4.6, released 2018-09-13 ----------------------------------- diff --git a/pysnmp/smi/mibs/TRANSPORT-ADDRESS-MIB.py b/pysnmp/smi/mibs/TRANSPORT-ADDRESS-MIB.py index 38d12699a..213bb89c0 100644 --- a/pysnmp/smi/mibs/TRANSPORT-ADDRESS-MIB.py +++ b/pysnmp/smi/mibs/TRANSPORT-ADDRESS-MIB.py @@ -36,7 +36,7 @@ def inet_pton(address_family, ip_string): if address_family == socket.AF_INET: - return inet_aton(ip_string) + return socket.inet_aton(ip_string) elif address_family != socket.AF_INET6: raise socket.error( 'Unknown address family %s' % (address_family,) @@ -46,7 +46,7 @@ def inet_pton(address_family, ip_string): spaces = groups.count('') if '.' in groups[-1]: - groups[-1:] = ["%x" % x for x in struct.unpack("!HH", inet_aton(groups[-1]))] + groups[-1:] = ["%x" % x for x in struct.unpack("!HH", socket.inet_aton(groups[-1]))] if spaces == 1: idx = groups.index('') @@ -84,7 +84,7 @@ def inet_pton(address_family, ip_string): def inet_ntop(address_family, packed_ip): if address_family == socket.AF_INET: - return inet_ntop(packed_ip) + return socket.inet_ntop(packed_ip) elif address_family != socket.AF_INET6: raise socket.error( 'Unknown address family %s' % (address_family,) From 29825c1086c45672ae4f5364668da51c99e6ff8c Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 30 Dec 2018 10:27:55 +0100 Subject: [PATCH 059/102] Release 4.4.7 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 76bbef352..1ccace838 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ SNMP library for Python ----------------------- [![PyPI](https://img.shields.io/pypi/v/pysnmp.svg?maxAge=2592000)](https://pypi.python.org/pypi/pysnmp) [![Python Versions](https://img.shields.io/pypi/pyversions/pysnmp.svg)](https://pypi.python.org/pypi/pysnmp/) -[![Build status](https://travis-ci.org/etingof/pysnmp.svg?branch=master)](https://secure.travis-ci.org/etingof/pysnmp) +[![Build status](https://travis-ci.org/etingof/pysnmp.svg?branch=master)](https://travis-ci.org/etingof/pysnmp) [![GitHub license](https://img.shields.io/badge/license-BSD-blue.svg)](https://raw.githubusercontent.com/etingof/pysnmp/master/LICENSE.rst) This is a pure-Python, open source and free implementation of v1/v2c/v3 From a375c076a6d4ee3fe524a5b008913153eb04c579 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 30 Dec 2018 11:48:52 +0100 Subject: [PATCH 060/102] Prepare for 4.4.8 --- CHANGES.txt | 5 +++++ pysnmp/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 70046378e..acf760a2e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,9 @@ +Revision 4.4.8, released 2018-12-XX +----------------------------------- + +No changes so far + Revision 4.4.7, released 2018-12-29 ----------------------------------- diff --git a/pysnmp/__init__.py b/pysnmp/__init__.py index a9d069292..da4cea9c7 100644 --- a/pysnmp/__init__.py +++ b/pysnmp/__init__.py @@ -1,5 +1,5 @@ # http://www.python.org/dev/peps/pep-0396/ -__version__ = '4.4.7' +__version__ = '4.4.8' # backward compatibility version = tuple([int(x) for x in __version__.split('.')]) majorVersionId = version[0] From 475a0ba8f59c5b417ab020cf4569d61a7882bc69 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 30 Dec 2018 12:30:01 +0100 Subject: [PATCH 061/102] Fix Pythonized MIB load in source form --- CHANGES.txt | 3 ++- pysnmp/smi/builder.py | 55 ++++++++++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index acf760a2e..c46fd0936 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,7 +2,8 @@ Revision 4.4.8, released 2018-12-XX ----------------------------------- -No changes so far +- Fixed Pythonized MIB load (in the source form) - made sure to turn + it into a code object prior to its execution Revision 4.4.7, released 2018-12-29 ----------------------------------- diff --git a/pysnmp/smi/builder.py b/pysnmp/smi/builder.py index c13445924..f115c1534 100644 --- a/pysnmp/smi/builder.py +++ b/pysnmp/smi/builder.py @@ -52,7 +52,7 @@ def _uniqNames(self, files): for sfx, sfxLen, mode in self.__sfx[typ]: if f[-sfxLen:] == sfx: u[f[:-sfxLen]] = None - return tuple(u.keys()) + return tuple(u) # MibSource API follows @@ -66,6 +66,7 @@ def init(self): self.__inited = True if self.__inited is True: return self + else: return self.__inited @@ -77,52 +78,55 @@ def read(self, f): for pycSfx, pycSfxLen, pycMode in self.__sfx[imp.PY_COMPILED]: try: - pycData = self._getData(f + pycSfx, pycMode) + pycData, pycPath = self._getData(f + pycSfx, pycMode) + except IOError: why = sys.exc_info()[1] if ENOENT == -1 or why.errno == ENOENT: debug.logger & debug.flagBld and debug.logger( 'file %s access error: %s' % (f + pycSfx, why) ) + else: raise error.MibLoadError('MIB file %s access error: %s' % (f + pycSfx, why)) + else: if self.__magic == pycData[:4]: pycData = pycData[4:] pycTime = struct.unpack('= pyTime: - # noinspection PyUnboundLocalVariable return marshal.loads(pycData), pycSfx + if pyTime != -1: - # noinspection PyUnboundLocalVariable - return self._getData(f + pySfx, pyMode), pySfx + modData, pyPath = self._getData(f + pySfx, pyMode) + return compile(modData, pyPath, 'exec'), pyPath raise IOError(ENOENT, 'No suitable module found', f) @@ -194,8 +198,9 @@ def _getTimestamp(self, f): def _getData(self, f, mode=None): p = os.path.join(self._srcName, f) try: - return self.__loader.get_data(p) - except: # ZIP code seems to return all kinds of errors + return self.__loader.get_data(p), p + + except Exception: # ZIP code seems to return all kinds of errors raise IOError(ENOENT, 'No such file in ZIP archive: %s' % sys.exc_info()[1], p) @@ -220,19 +225,20 @@ def _getTimestamp(self, f): raise IOError(ENOENT, 'No such file: %s' % sys.exc_info()[1], p) def _getData(self, f, mode): - p = os.path.join(self._srcName, f) try: if f in os.listdir(self._srcName): # make FS case-sensitive + p = os.path.join(self._srcName, f) fp = open(p, mode) data = fp.read() fp.close() - return data + return data, p + except (IOError, OSError): why = sys.exc_info()[1] if why.errno != ENOENT and ENOENT != -1: raise error.MibLoadError('MIB file %s access error: %s' % (p, why)) - raise IOError(ENOENT, 'No such file: %s' % sys.exc_info()[1], p) + raise IOError(ENOENT, 'No such file: %s' % sys.exc_info()[1], f) class MibBuilder(object): @@ -306,10 +312,12 @@ def getMibPath(self): return paths def loadModule(self, modName, **userCtx): + """Load and execute MIB modules as Python code""" for mibSource in self.__mibSources: debug.logger & debug.flagBld and debug.logger('loadModule: trying %s at %s' % (modName, mibSource)) try: - modData, sfx = mibSource.read(modName) + codeObj, sfx = mibSource.read(modName) + except IOError: debug.logger & debug.flagBld and debug.logger( 'loadModule: read %s from %s failed: %s' % (modName, mibSource, sys.exc_info()[1])) @@ -320,6 +328,7 @@ def loadModule(self, modName, **userCtx): if modPath in self.__modPathsSeen: debug.logger & debug.flagBld and debug.logger('loadModule: seen %s' % modPath) break + else: self.__modPathsSeen.add(modPath) @@ -328,7 +337,7 @@ def loadModule(self, modName, **userCtx): g = {'mibBuilder': self, 'userCtx': userCtx} try: - exec(compile(modData, modPath, 'exec'), g) + exec(codeObj, g) except Exception: self.__modPathsSeen.remove(modPath) @@ -351,13 +360,15 @@ def loadModule(self, modName, **userCtx): return self def loadModules(self, *modNames, **userCtx): + """Load (optionally, compiling) pysnmp MIB modules""" # Build a list of available modules if not modNames: modNames = {} for mibSource in self.__mibSources: for modName in mibSource.listdir(): modNames[modName] = None - modNames = list(modNames.keys()) + modNames = list(modNames) + if not modNames: raise error.MibNotFoundError( 'No MIB module to load at %s' % (self,) @@ -366,6 +377,7 @@ def loadModules(self, *modNames, **userCtx): for modName in modNames: try: self.loadModule(modName, **userCtx) + except error.MibNotFoundError: if self.__mibCompiler: debug.logger & debug.flagBld and debug.logger('loadModules: calling MIB compiler for %s' % modName) @@ -374,7 +386,8 @@ def loadModules(self, *modNames, **userCtx): x in ('failed', 'missing')]) if errs: raise error.MibNotFoundError('%s compilation error(s): %s' % (modName, errs)) - # compilation suceeded, MIB might load now + + # compilation succeeded, MIB might load now self.loadModule(modName, **userCtx) return self From 4b018af15d0d4849f48bcb40f75b86a4da9a60ac Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 30 Dec 2018 14:21:41 +0100 Subject: [PATCH 062/102] Release 4.4.8 --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index c46fd0936..71a2ab87e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,5 @@ -Revision 4.4.8, released 2018-12-XX +Revision 4.4.8, released 2018-12-30 ----------------------------------- - Fixed Pythonized MIB load (in the source form) - made sure to turn From 2b3dcaff8dbc866eb84b9f5a9c4e470e97a6fcb9 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Tue, 8 Jan 2019 08:13:35 +0100 Subject: [PATCH 063/102] Prepare for 4.4.9 --- CHANGES.txt | 5 +++++ pysnmp/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 71a2ab87e..5596c0faa 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,9 @@ +Revision 4.4.9, released 2019-01-XX +----------------------------------- + +No changes yet + Revision 4.4.8, released 2018-12-30 ----------------------------------- diff --git a/pysnmp/__init__.py b/pysnmp/__init__.py index da4cea9c7..ca65ab506 100644 --- a/pysnmp/__init__.py +++ b/pysnmp/__init__.py @@ -1,5 +1,5 @@ # http://www.python.org/dev/peps/pep-0396/ -__version__ = '4.4.8' +__version__ = '4.4.9' # backward compatibility version = tuple([int(x) for x in __version__.split('.')]) majorVersionId = version[0] From fd7f6a77764a32608d35d36f7b7b4a638c536b14 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Tue, 8 Jan 2019 08:40:42 +0100 Subject: [PATCH 064/102] MIB loader ignores file and directory access errors Also fixed crash on MIB load failure in case of directory access error --- CHANGES.txt | 3 ++- pysnmp/smi/builder.py | 22 +++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 5596c0faa..4ffcf25b1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,7 +2,8 @@ Revision 4.4.9, released 2019-01-XX ----------------------------------- -No changes yet +- Made MIB loader ignoring file and directory access errors +- Fixed crash on MIB load failure in case of directory access error Revision 4.4.8, released 2018-12-30 ----------------------------------- diff --git a/pysnmp/smi/builder.py b/pysnmp/smi/builder.py index f115c1534..87c6b6832 100644 --- a/pysnmp/smi/builder.py +++ b/pysnmp/smi/builder.py @@ -44,14 +44,14 @@ def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self._srcName) def _uniqNames(self, files): - u = {} + u = set() for f in files: - if f[:9] == '__init__.': + if f.startswith('__init__.'): continue for typ in (imp.PY_SOURCE, imp.PY_COMPILED): for sfx, sfxLen, mode in self.__sfx[typ]: if f[-sfxLen:] == sfx: - u[f[:-sfxLen]] = None + u.add(f[:-sfxLen]) return tuple(u) # MibSource API follows @@ -201,7 +201,8 @@ def _getData(self, f, mode=None): return self.__loader.get_data(p), p except Exception: # ZIP code seems to return all kinds of errors - raise IOError(ENOENT, 'No such file in ZIP archive: %s' % sys.exc_info()[1], p) + why = sys.exc_info() + raise IOError(ENOENT, 'File or ZIP archive %s access error: %s' % (p, why[1])) class DirMibSource(__AbstractMibSource): @@ -213,8 +214,9 @@ def _listdir(self): try: return self._uniqNames(os.listdir(self._srcName)) except OSError: + why = sys.exc_info() debug.logger & debug.flagBld and debug.logger( - 'listdir() failed for %s: %s' % (self._srcName, sys.exc_info()[1])) + 'listdir() failed for %s: %s' % (self._srcName, why[1])) return () def _getTimestamp(self, f): @@ -225,6 +227,7 @@ def _getTimestamp(self, f): raise IOError(ENOENT, 'No such file: %s' % sys.exc_info()[1], p) def _getData(self, f, mode): + p = os.path.join(self._srcName, '*') try: if f in os.listdir(self._srcName): # make FS case-sensitive p = os.path.join(self._srcName, f) @@ -234,12 +237,13 @@ def _getData(self, f, mode): return data, p except (IOError, OSError): - why = sys.exc_info()[1] - if why.errno != ENOENT and ENOENT != -1: - raise error.MibLoadError('MIB file %s access error: %s' % (p, why)) + why = sys.exc_info() + msg = 'File or directory %s access error: %s' % (p, why[1]) - raise IOError(ENOENT, 'No such file: %s' % sys.exc_info()[1], f) + else: + msg = 'No such file or directory: %s' % p + raise IOError(ENOENT, msg) class MibBuilder(object): defaultCoreMibs = os.pathsep.join( From bded1e349662eeb634383ad46ec3e1605b317ec7 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Thu, 10 Jan 2019 23:12:06 +0100 Subject: [PATCH 065/102] Emit low-level sendmsg/recvmsg debugging When running in transparent proxy mode, log syscall parameters to aid troubleshooting --- pysnmp/carrier/asyncore/dgram/base.py | 7 +++++++ pysnmp/carrier/sockmsg.py | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/pysnmp/carrier/asyncore/dgram/base.py b/pysnmp/carrier/asyncore/dgram/base.py index 274b1eba0..4edae19b3 100644 --- a/pysnmp/carrier/asyncore/dgram/base.py +++ b/pysnmp/carrier/asyncore/dgram/base.py @@ -75,8 +75,10 @@ def enablePktInfo(self, flag=1): try: if self.socket.family in (socket.AF_INET, socket.AF_INET6): self.socket.setsockopt(socket.SOL_IP, socket.IP_PKTINFO, flag) + if self.socket.family == socket.AF_INET6: self.socket.setsockopt(socket.SOL_IPV6, socket.IPV6_RECVPKTINFO, flag) + except socket.error: raise error.CarrierError('setsockopt() for %s failed: %s' % (self.socket.family == socket.AF_INET6 and "IPV6_RECVPKTINFO" or "IP_PKTINFO", sys.exc_info()[1])) @@ -96,8 +98,10 @@ def enableTransparent(self, flag=1): self.socket.setsockopt( socket.SOL_IPV6, socket.IP_TRANSPARENT, flag ) + except socket.error: raise error.CarrierError('setsockopt() for IP_TRANSPARENT failed: %s' % sys.exc_info()[1]) + except OSError: raise error.CarrierError('IP_TRANSPARENT socket option requires superusre previleges') @@ -113,14 +117,17 @@ def sendMessage(self, outgoingMessage, transportAddress): def normalizeAddress(self, transportAddress): if not isinstance(transportAddress, self.addressType): transportAddress = self.addressType(transportAddress) + if not transportAddress.getLocalAddress(): transportAddress.setLocalAddress(self.getLocalAddress()) + return transportAddress def getLocalAddress(self): # one evil OS does not seem to support getsockname() for DGRAM sockets try: return self.socket.getsockname() + except Exception: return '0.0.0.0', 0 diff --git a/pysnmp/carrier/sockmsg.py b/pysnmp/carrier/sockmsg.py index 0d79b2c70..0b21233b7 100644 --- a/pysnmp/carrier/sockmsg.py +++ b/pysnmp/carrier/sockmsg.py @@ -17,6 +17,8 @@ # import sys +from pysnmp import debug + if sys.version_info[:2] < (3, 3): # noinspection PyUnusedLocal def getRecvFrom(addressType): @@ -69,38 +71,57 @@ class in6_pktinfo(ctypes.Structure): def getRecvFrom(addressType): + def recvfrom(s, sz): _to = None + data, ancdata, msg_flags, _from = s.recvmsg(sz, socket.CMSG_LEN(sz)) + for anc in ancdata: if anc[0] == socket.SOL_IP and anc[1] == socket.IP_PKTINFO: addr = in_pktinfo.from_buffer_copy(anc[2]) addr = ipaddress.IPv4Address(memoryview(addr.ipi_addr).tobytes()) _to = (str(addr), s.getsockname()[1]) + break + elif anc[0] == socket.SOL_IPV6 and anc[1] == socket.IPV6_PKTINFO: addr = in6_pktinfo.from_buffer_copy(anc[2]) addr = ipaddress.ip_address(memoryview(addr.ipi6_addr).tobytes()) _to = (str(addr), s.getsockname()[1]) + break + + debug.logger & debug.flagIO and debug.logger( + 'recvfrom: received %d octets from %s to %s; ' + 'iov blob %r' % (len(data), _from, _to, ancdata)) + return data, addressType(_from).setLocalAddress(_to) return recvfrom def getSendTo(addressType): + def sendto(s, _data, _to): ancdata = [] if type(_to) == addressType: addr = ipaddress.ip_address(_to.getLocalAddress()[0]) + else: addr = ipaddress.ip_address(s.getsockname()[0]) + if type(addr) == ipaddress.IPv4Address: _f = in_pktinfo() _f.ipi_spec_dst = in_addr.from_buffer_copy(addr.packed) ancdata = [(socket.SOL_IP, socket.IP_PKTINFO, memoryview(_f).tobytes())] + elif s.family == socket.AF_INET6 and type(addr) == ipaddress.IPv6Address: _f = in6_pktinfo() _f.ipi6_addr = in6_addr.from_buffer_copy(addr.packed) ancdata = [(socket.SOL_IPV6, socket.IPV6_PKTINFO, memoryview(_f).tobytes())] + + debug.logger & debug.flagIO and debug.logger( + 'sendto: sending %d octets to %s; iov blob %r' % (len(data), _to, ancdata)) + return s.sendmsg([_data], ancdata, 0, _to) return sendto From 11eb3fa691c6889a2d66ee3417d562deb6b35ec4 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Thu, 10 Jan 2019 23:27:07 +0100 Subject: [PATCH 066/102] Add `IPV6_V6ONLY` IPV6 socket option SO says this is required for proper UDP socket multi-homing. --- pysnmp/carrier/asyncore/dgram/base.py | 1 + pysnmp/carrier/sockmsg.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pysnmp/carrier/asyncore/dgram/base.py b/pysnmp/carrier/asyncore/dgram/base.py index 4edae19b3..d38f3080b 100644 --- a/pysnmp/carrier/asyncore/dgram/base.py +++ b/pysnmp/carrier/asyncore/dgram/base.py @@ -78,6 +78,7 @@ def enablePktInfo(self, flag=1): if self.socket.family == socket.AF_INET6: self.socket.setsockopt(socket.SOL_IPV6, socket.IPV6_RECVPKTINFO, flag) + self.socket.setsockopt(socket.SOL_IPV6, socket.IPV6_V6ONLY, not flag) except socket.error: raise error.CarrierError('setsockopt() for %s failed: %s' % (self.socket.family == socket.AF_INET6 and "IPV6_RECVPKTINFO" or "IP_PKTINFO", sys.exc_info()[1])) diff --git a/pysnmp/carrier/sockmsg.py b/pysnmp/carrier/sockmsg.py index 0b21233b7..97273306f 100644 --- a/pysnmp/carrier/sockmsg.py +++ b/pysnmp/carrier/sockmsg.py @@ -92,7 +92,7 @@ def recvfrom(s, sz): debug.logger & debug.flagIO and debug.logger( 'recvfrom: received %d octets from %s to %s; ' - 'iov blob %r' % (len(data), _from, _to, ancdata)) + 'iov blob %s' % (len(data), _from, _to, debug.hexdump(ancdata))) return data, addressType(_from).setLocalAddress(_to) @@ -120,7 +120,7 @@ def sendto(s, _data, _to): ancdata = [(socket.SOL_IPV6, socket.IPV6_PKTINFO, memoryview(_f).tobytes())] debug.logger & debug.flagIO and debug.logger( - 'sendto: sending %d octets to %s; iov blob %r' % (len(data), _to, ancdata)) + 'sendto: sending %d octets to %s; iov blob %s' % (len(data), _to, debug.hexdump(ancdata))) return s.sendmsg([_data], ancdata, 0, _to) From 35275000baec73639cc2c4eb1153ab94cbe58dbd Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 11 Jan 2019 08:03:07 +0100 Subject: [PATCH 067/102] Fix socket iov blob dump in debug --- pysnmp/carrier/sockmsg.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pysnmp/carrier/sockmsg.py b/pysnmp/carrier/sockmsg.py index 97273306f..909407a90 100644 --- a/pysnmp/carrier/sockmsg.py +++ b/pysnmp/carrier/sockmsg.py @@ -31,7 +31,8 @@ def getSendTo(addressType): import ctypes import ipaddress import socket - from pysnmp.carrier import sockfix, error + from pysnmp.carrier import sockfix + from pysnmp.carrier import error uint32_t = ctypes.c_uint32 in_addr_t = uint32_t @@ -92,7 +93,7 @@ def recvfrom(s, sz): debug.logger & debug.flagIO and debug.logger( 'recvfrom: received %d octets from %s to %s; ' - 'iov blob %s' % (len(data), _from, _to, debug.hexdump(ancdata))) + 'iov blob %r' % (len(data), _from, _to, ancdata)) return data, addressType(_from).setLocalAddress(_to) @@ -120,7 +121,8 @@ def sendto(s, _data, _to): ancdata = [(socket.SOL_IPV6, socket.IPV6_PKTINFO, memoryview(_f).tobytes())] debug.logger & debug.flagIO and debug.logger( - 'sendto: sending %d octets to %s; iov blob %s' % (len(data), _to, debug.hexdump(ancdata))) + 'sendto: sending %d octets to %s; ' + 'iov blob %r' % (len(data), _to, ancdata)) return s.sendmsg([_data], ancdata, 0, _to) From b8638ff65bffaef0f1959a4a5b3e7720d643bade Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 11 Jan 2019 08:04:23 +0100 Subject: [PATCH 068/102] Improve IPV6 debugging --- pysnmp/carrier/asyncore/dgram/base.py | 2 +- pysnmp/carrier/sockfix.py | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pysnmp/carrier/asyncore/dgram/base.py b/pysnmp/carrier/asyncore/dgram/base.py index d38f3080b..f72fe57c1 100644 --- a/pysnmp/carrier/asyncore/dgram/base.py +++ b/pysnmp/carrier/asyncore/dgram/base.py @@ -78,7 +78,7 @@ def enablePktInfo(self, flag=1): if self.socket.family == socket.AF_INET6: self.socket.setsockopt(socket.SOL_IPV6, socket.IPV6_RECVPKTINFO, flag) - self.socket.setsockopt(socket.SOL_IPV6, socket.IPV6_V6ONLY, not flag) + self.socket.setsockopt(socket.SOL_IPV6, socket.IPV6_V6ONLY, int(not flag)) except socket.error: raise error.CarrierError('setsockopt() for %s failed: %s' % (self.socket.family == socket.AF_INET6 and "IPV6_RECVPKTINFO" or "IP_PKTINFO", sys.exc_info()[1])) diff --git a/pysnmp/carrier/sockfix.py b/pysnmp/carrier/sockfix.py index d4ea504ef..02480bb24 100644 --- a/pysnmp/carrier/sockfix.py +++ b/pysnmp/carrier/sockfix.py @@ -6,7 +6,10 @@ # import socket -symbols = { +from pysnmp import debug + + +SYMBOLS = { 'IP_PKTINFO': 8, 'IP_TRANSPARENT': 19, 'SOL_IPV6': 41, @@ -14,6 +17,11 @@ 'IPV6_PKTINFO': 50 } -for symbol in symbols: +for symbol, value in SYMBOLS.items(): if not hasattr(socket, symbol): - setattr(socket, symbol, symbols[symbol]) + setattr(socket, symbol, value) + + debug.logger & debug.flagIO and debug.logger( + 'WARNING: the socket module on this platform misses option %s. ' + 'Assuming its value is %d.' % (symbol, value) + ) From 1f52f82d7434e709d4663d8f295a4eac948ac5bd Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 12 Jan 2019 00:03:50 +0100 Subject: [PATCH 069/102] Do not set IP_PKTINFO socket option on IPv6 socket Trying to understand why sendmsg() fails on a transparent IPv6 socket --- pysnmp/carrier/asyncore/dgram/base.py | 3 +-- pysnmp/carrier/sockmsg.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pysnmp/carrier/asyncore/dgram/base.py b/pysnmp/carrier/asyncore/dgram/base.py index f72fe57c1..3e9ea1296 100644 --- a/pysnmp/carrier/asyncore/dgram/base.py +++ b/pysnmp/carrier/asyncore/dgram/base.py @@ -73,12 +73,11 @@ def enablePktInfo(self, flag=1): raise error.CarrierError('sendmsg()/recvmsg() interface is not supported by this OS and/or Python version') try: - if self.socket.family in (socket.AF_INET, socket.AF_INET6): + if self.socket.family == socket.AF_INET: self.socket.setsockopt(socket.SOL_IP, socket.IP_PKTINFO, flag) if self.socket.family == socket.AF_INET6: self.socket.setsockopt(socket.SOL_IPV6, socket.IPV6_RECVPKTINFO, flag) - self.socket.setsockopt(socket.SOL_IPV6, socket.IPV6_V6ONLY, int(not flag)) except socket.error: raise error.CarrierError('setsockopt() for %s failed: %s' % (self.socket.family == socket.AF_INET6 and "IPV6_RECVPKTINFO" or "IP_PKTINFO", sys.exc_info()[1])) diff --git a/pysnmp/carrier/sockmsg.py b/pysnmp/carrier/sockmsg.py index 909407a90..4ff55a911 100644 --- a/pysnmp/carrier/sockmsg.py +++ b/pysnmp/carrier/sockmsg.py @@ -121,8 +121,8 @@ def sendto(s, _data, _to): ancdata = [(socket.SOL_IPV6, socket.IPV6_PKTINFO, memoryview(_f).tobytes())] debug.logger & debug.flagIO and debug.logger( - 'sendto: sending %d octets to %s; ' - 'iov blob %r' % (len(data), _to, ancdata)) + 'sendto: sending %d octets to %s; address %r; ' + 'iov blob %r' % (len(_data), _to, addr, ancdata)) return s.sendmsg([_data], ancdata, 0, _to) From fbaa6da636c20fb71e5af28eee9dba5133f0d42c Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Mon, 14 Jan 2019 23:02:01 +0100 Subject: [PATCH 070/102] Fix IPV6_TRANSPARENT socket option --- CHANGES.txt | 2 ++ pysnmp/carrier/asyncore/dgram/base.py | 2 +- pysnmp/carrier/sockfix.py | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4ffcf25b1..7f7e76180 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,8 @@ Revision 4.4.9, released 2019-01-XX - Made MIB loader ignoring file and directory access errors - Fixed crash on MIB load failure in case of directory access error +- Fixed socket transparency option (IPV6_TRANSPARENT)to make IPv6 + transparent operation functional Revision 4.4.8, released 2018-12-30 ----------------------------------- diff --git a/pysnmp/carrier/asyncore/dgram/base.py b/pysnmp/carrier/asyncore/dgram/base.py index 3e9ea1296..5b0faab2d 100644 --- a/pysnmp/carrier/asyncore/dgram/base.py +++ b/pysnmp/carrier/asyncore/dgram/base.py @@ -96,7 +96,7 @@ def enableTransparent(self, flag=1): ) if self.socket.family == socket.AF_INET6: self.socket.setsockopt( - socket.SOL_IPV6, socket.IP_TRANSPARENT, flag + socket.SOL_IPV6, socket.IPV6_TRANSPARENT, flag ) except socket.error: diff --git a/pysnmp/carrier/sockfix.py b/pysnmp/carrier/sockfix.py index 02480bb24..fab6d1aa8 100644 --- a/pysnmp/carrier/sockfix.py +++ b/pysnmp/carrier/sockfix.py @@ -14,7 +14,8 @@ 'IP_TRANSPARENT': 19, 'SOL_IPV6': 41, 'IPV6_RECVPKTINFO': 49, - 'IPV6_PKTINFO': 50 + 'IPV6_PKTINFO': 50, + 'IPV6_TRANSPARENT': 75 } for symbol, value in SYMBOLS.items(): From ed70cdf00443aed945a238f2a8b704fa8d9c255c Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 8 Feb 2019 11:16:52 +0100 Subject: [PATCH 071/102] Add missing SNMP PDU error classes Added missing SNMP PDU error classes and their handling in Command Responder --- CHANGES.txt | 1 + pysnmp/entity/rfc3413/cmdrsp.py | 39 +++++++++++++++++++++++++++++++-- pysnmp/proto/rfc1905.py | 13 ++++++----- pysnmp/smi/error.py | 31 +++++++++++++++++++++----- 4 files changed, 70 insertions(+), 14 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7f7e76180..1b5b75fdb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,6 +3,7 @@ Revision 4.4.9, released 2019-01-XX ----------------------------------- - Made MIB loader ignoring file and directory access errors +- Added missing SNMP PDU error classes and their handling in Command Responder - Fixed crash on MIB load failure in case of directory access error - Fixed socket transparency option (IPV6_TRANSPARENT)to make IPv6 transparent operation functional diff --git a/pysnmp/entity/rfc3413/cmdrsp.py b/pysnmp/entity/rfc3413/cmdrsp.py index 3e1db4f36..5cef0a14c 100644 --- a/pysnmp/entity/rfc3413/cmdrsp.py +++ b/pysnmp/entity/rfc3413/cmdrsp.py @@ -138,6 +138,7 @@ def processPdu(self, snmpEngine, messageProcessingModel, securityModel, self.handleMgmtOperation(snmpEngine, stateReference, contextName, PDU, (self.__verifyAccess, snmpEngine)) + # SNMPv2 SMI exceptions except pysnmp.smi.error.GenError: errorIndication = sys.exc_info()[1] @@ -148,38 +149,72 @@ def processPdu(self, snmpEngine, messageProcessingModel, securityModel, statusInformation['oid'] = errorIndication['oid'] statusInformation['val'] = errorIndication['val'] - # PDU-level SMI errors + # Handle PDU-level SMI errors + + except pysnmp.smi.error.TooBigError: + errorStatus, errorIndex = 'tooBig', 0 + # rfc1905: 4.2.1.3 + varBinds = [] + + # this should never bubble up, SNMP exception objects should be passed as values + except pysnmp.smi.error.NoSuchNameError: + errorStatus, errorIndex = 'noSuchName', sys.exc_info()[1]['idx'] + 1 + + except pysnmp.smi.error.BadValueError: + errorStatus, errorIndex = 'badValue', sys.exc_info()[1]['idx'] + 1 + + except pysnmp.smi.error.ReadOnlyError: + errorStatus, errorIndex = 'readOnly', sys.exc_info()[1]['idx'] + 1 + + except pysnmp.smi.error.GenError: + errorStatus, errorIndex = 'genErr', sys.exc_info()[1]['idx'] + 1 + except pysnmp.smi.error.NoAccessError: errorStatus, errorIndex = 'noAccess', sys.exc_info()[1]['idx'] + 1 + except pysnmp.smi.error.WrongTypeError: errorStatus, errorIndex = 'wrongType', sys.exc_info()[1]['idx'] + 1 + except pysnmp.smi.error.WrongLengthError: errorStatus, errorIndex = 'wrongLength', sys.exc_info()[1]['idx'] + 1 + except pysnmp.smi.error.WrongEncodingError: errorStatus, errorIndex = 'wrongEncoding', sys.exc_info()[1]['idx'] + 1 + except pysnmp.smi.error.WrongValueError: errorStatus, errorIndex = 'wrongValue', sys.exc_info()[1]['idx'] + 1 + except pysnmp.smi.error.NoCreationError: errorStatus, errorIndex = 'noCreation', sys.exc_info()[1]['idx'] + 1 + except pysnmp.smi.error.InconsistentValueError: errorStatus, errorIndex = 'inconsistentValue', sys.exc_info()[1]['idx'] + 1 + except pysnmp.smi.error.ResourceUnavailableError: errorStatus, errorIndex = 'resourceUnavailable', sys.exc_info()[1]['idx'] + 1 + except pysnmp.smi.error.CommitFailedError: errorStatus, errorIndex = 'commitFailed', sys.exc_info()[1]['idx'] + 1 + except pysnmp.smi.error.UndoFailedError: errorStatus, errorIndex = 'undoFailed', sys.exc_info()[1]['idx'] + 1 + except pysnmp.smi.error.AuthorizationError: errorStatus, errorIndex = 'authorizationError', sys.exc_info()[1]['idx'] + 1 + except pysnmp.smi.error.NotWritableError: errorStatus, errorIndex = 'notWritable', sys.exc_info()[1]['idx'] + 1 + except pysnmp.smi.error.InconsistentNameError: errorStatus, errorIndex = 'inconsistentName', sys.exc_info()[1]['idx'] + 1 + except pysnmp.smi.error.SmiError: - errorStatus, errorIndex = 'genErr', len(varBinds) and 1 or 0 + errorStatus, errorIndex = 'genErr', len(varBinds) and 1 + except pysnmp.error.PySnmpError: self.releaseStateInformation(stateReference) return + else: # successful request processor must release state info return diff --git a/pysnmp/proto/rfc1905.py b/pysnmp/proto/rfc1905.py index 4a2294958..d5da39829 100644 --- a/pysnmp/proto/rfc1905.py +++ b/pysnmp/proto/rfc1905.py @@ -84,12 +84,13 @@ class VarBindList(univ.SequenceOf): errorStatus = univ.Integer( - namedValues=namedval.NamedValues(('noError', 0), ('tooBig', 1), ('noSuchName', 2), ('badValue', 3), ('readOnly', 4), - ('genErr', 5), ('noAccess', 6), ('wrongType', 7), ('wrongLength', 8), - ('wrongEncoding', 9), ('wrongValue', 10), ('noCreation', 11), - ('inconsistentValue', 12), ('resourceUnavailable', 13), ('commitFailed', 14), - ('undoFailed', 15), ('authorizationError', 16), ('notWritable', 17), - ('inconsistentName', 18)) + namedValues=namedval.NamedValues( + ('noError', 0), ('tooBig', 1), ('noSuchName', 2), ('badValue', 3), ('readOnly', 4), + ('genErr', 5), ('noAccess', 6), ('wrongType', 7), ('wrongLength', 8), + ('wrongEncoding', 9), ('wrongValue', 10), ('noCreation', 11), + ('inconsistentValue', 12), ('resourceUnavailable', 13), ('commitFailed', 14), + ('undoFailed', 15), ('authorizationError', 16), ('notWritable', 17), + ('inconsistentName', 18)) ) diff --git a/pysnmp/smi/error.py b/pysnmp/smi/error.py index 4b7ba5cca..a16dfe2b1 100644 --- a/pysnmp/smi/error.py +++ b/pysnmp/smi/error.py @@ -43,7 +43,24 @@ def update(self, d): self.__outArgs.update(d) -# Aligned with SNMPv2 PDU error-status +# Aligned with SNMPv2 PDU error-status values + +class TooBigError(MibOperationError): + pass + + +class NoSuchNameError(MibOperationError): + pass + + +class BadValueError(MibOperationError): + pass + + +class ReadOnlyError(MibOperationError): + pass + + class GenError(MibOperationError): pass @@ -100,20 +117,22 @@ class InconsistentNameError(MibOperationError): pass -# Aligned with SNMPv2 Var-Bind exceptions -class NoSuchObjectError(MibOperationError): +# Aligned with SNMPv2 PDU exceptions or error-status values + +class NoSuchObjectError(NoSuchNameError): pass -class NoSuchInstanceError(MibOperationError): +class NoSuchInstanceError(NoSuchNameError): pass -class EndOfMibViewError(MibOperationError): +class EndOfMibViewError(NoSuchNameError): pass -# Row management +# SNMP table management exceptions + class TableRowManagement(MibOperationError): pass From c393c0c93983326cff45a660cfd32c187fed3e45 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 9 Feb 2019 16:01:53 +0100 Subject: [PATCH 072/102] Release 4.4.9 --- CHANGES.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1b5b75fdb..4de85c77a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,11 +1,11 @@ -Revision 4.4.9, released 2019-01-XX +Revision 4.4.9, released 2019-02-09 ----------------------------------- - Made MIB loader ignoring file and directory access errors - Added missing SNMP PDU error classes and their handling in Command Responder - Fixed crash on MIB load failure in case of directory access error -- Fixed socket transparency option (IPV6_TRANSPARENT)to make IPv6 +- Fixed socket transparency option (IPV6_TRANSPARENT) to make IPv6 transparent operation functional Revision 4.4.8, released 2018-12-30 From 531ccbcf782d65faaf0b88159ff1c5941d220c9b Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 9 Feb 2019 16:10:21 +0100 Subject: [PATCH 073/102] Prepare for 4.4.10 --- CHANGES.txt | 5 +++++ pysnmp/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4de85c77a..52555257f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,9 @@ +Revision 4.4.10, released 2019-02-XX +------------------------------------ + +No changes yet + Revision 4.4.9, released 2019-02-09 ----------------------------------- diff --git a/pysnmp/__init__.py b/pysnmp/__init__.py index ca65ab506..233ee90f0 100644 --- a/pysnmp/__init__.py +++ b/pysnmp/__init__.py @@ -1,5 +1,5 @@ # http://www.python.org/dev/peps/pep-0396/ -__version__ = '4.4.9' +__version__ = '4.4.10' # backward compatibility version = tuple([int(x) for x in __version__.split('.')]) majorVersionId = version[0] From 1de1055b2335d41ed46f9a6da3741aa4424adae4 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 31 Mar 2019 11:19:43 +0200 Subject: [PATCH 074/102] Respect timer resolution in asyncore main loop Fixes asyncore main loop upper bound timeout to respect currently set timer resolution. --- CHANGES.txt | 4 ++-- pysnmp/carrier/asyncore/dispatch.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 52555257f..24e490e00 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,8 +1,8 @@ -Revision 4.4.10, released 2019-02-XX +Revision 4.4.10, released 2019-04-XX ------------------------------------ -No changes yet +- Respect non-default timer resolution in asyncore main loop Revision 4.4.9, released 2019-02-09 ----------------------------------- diff --git a/pysnmp/carrier/asyncore/dispatch.py b/pysnmp/carrier/asyncore/dispatch.py index bc72df91b..c271387bc 100644 --- a/pysnmp/carrier/asyncore/dispatch.py +++ b/pysnmp/carrier/asyncore/dispatch.py @@ -42,7 +42,7 @@ def transportsAreWorking(self): def runDispatcher(self, timeout=0.0): while self.jobsArePending() or self.transportsAreWorking(): try: - loop(timeout and timeout or self.timeout, + loop(timeout or self.getTimerResolution(), use_poll=True, map=self.__sockMap, count=1) except KeyboardInterrupt: raise From 8a6dc6331cc667840166a25406057794f11fe8ec Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 31 Mar 2019 11:29:33 +0200 Subject: [PATCH 075/102] Update periodics call interval on timer resolution change Fix to updates call interval of the existing periodic dispatcher jobs on call interval change (via .setTimerResolution()) --- CHANGES.txt | 4 +++- pysnmp/carrier/base.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 24e490e00..8942df65f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,7 +2,9 @@ Revision 4.4.10, released 2019-04-XX ------------------------------------ -- Respect non-default timer resolution in asyncore main loop +- Fix to respect non-default timer resolution in asyncore main loop +- Fix to update call interval of the existing periodic dispatcher jobs + on interval change (via .setTimerResolution()) Revision 4.4.9, released 2019-02-09 ----------------------------------- diff --git a/pysnmp/carrier/base.py b/pysnmp/carrier/base.py index 51a2d0b5d..40b8d78ac 100644 --- a/pysnmp/carrier/base.py +++ b/pysnmp/carrier/base.py @@ -36,6 +36,14 @@ def __gt__(self, cbFun): def __ge__(self, cbFun): return self.__cbFun >= cbFun + @property + def interval(self): + return self.__callInterval + + @interval.setter + def interval(self, callInterval): + self.__callInterval = callInterval + class AbstractTransportDispatcher(object): def __init__(self): @@ -151,6 +159,12 @@ def getTimerResolution(self): def setTimerResolution(self, timerResolution): if timerResolution < 0.01 or timerResolution > 10: raise error.CarrierError('Impossible timer resolution') + + for timerCallable in self.__timerCallables: + if timerCallable.interval == self.__timerResolution: + # Update periodics for default resolutions + timerCallable.interval = timerResolution + self.__timerResolution = timerResolution self.__timerDelta = timerResolution * 0.05 From c3d6619176a108758e1c7f0622cb64766471b27d Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Mon, 1 Apr 2019 18:44:40 +0200 Subject: [PATCH 076/102] Replace `imp` with `importlib` is available --- CHANGES.txt | 8 +++++--- pysnmp/smi/builder.py | 44 +++++++++++++++++++++++++++---------------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8942df65f..90454a5d4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,9 +2,11 @@ Revision 4.4.10, released 2019-04-XX ------------------------------------ -- Fix to respect non-default timer resolution in asyncore main loop -- Fix to update call interval of the existing periodic dispatcher jobs - on interval change (via .setTimerResolution()) +- Rebased MIB importing code onto `importlib` because `imp` is long + deprecated +- Fixed asyncore main loop to respect non-default timer resolution +- Fixed `.setTimerResolution()` behaviour of abstract main loop dispatcher + to update call intervals of the existing periodic dispatcher jobs Revision 4.4.9, released 2019-02-09 ----------------------------------- diff --git a/pysnmp/smi/builder.py b/pysnmp/smi/builder.py index 87c6b6832..2f55b8eb5 100644 --- a/pysnmp/smi/builder.py +++ b/pysnmp/smi/builder.py @@ -6,16 +6,32 @@ # import os import sys -import imp import struct import marshal import time import traceback +try: + import importlib + + PY_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER + SOURCE_SUFFIXES = importlib.machinery.SOURCE_SUFFIXES + BYTECODE_SUFFIXES = importlib.machinery.BYTECODE_SUFFIXES + +except ImportError: + import imp + + PY_MAGIC_NUMBER = imp.get_magic() + SOURCE_SUFFIXES = [imp.PY_SOURCE] + BYTECODE_SUFFIXES = [imp.PY_COMPILED] + +PY_SUFFIXES = SOURCE_SUFFIXES + BYTECODE_SUFFIXES + try: from errno import ENOENT except ImportError: ENOENT = -1 + from pysnmp import version as pysnmp_version from pysnmp.smi import error from pysnmp import debug @@ -31,13 +47,7 @@ class __AbstractMibSource(object): def __init__(self, srcName): self._srcName = srcName - self.__magic = imp.get_magic() - self.__sfx = {} self.__inited = None - for sfx, mode, typ in imp.get_suffixes(): - if typ not in self.__sfx: - self.__sfx[typ] = [] - self.__sfx[typ].append((sfx, len(sfx), mode)) debug.logger & debug.flagBld and debug.logger('trying %s' % self) def __repr__(self): @@ -45,13 +55,13 @@ def __repr__(self): def _uniqNames(self, files): u = set() + for f in files: if f.startswith('__init__.'): continue - for typ in (imp.PY_SOURCE, imp.PY_COMPILED): - for sfx, sfxLen, mode in self.__sfx[typ]: - if f[-sfxLen:] == sfx: - u.add(f[:-sfxLen]) + + u.update(f[:-len(sfx)] for sfx in PY_SUFFIXES if f.endswith(sfx)) + return tuple(u) # MibSource API follows @@ -76,9 +86,10 @@ def listdir(self): def read(self, f): pycTime = pyTime = -1 - for pycSfx, pycSfxLen, pycMode in self.__sfx[imp.PY_COMPILED]: + for pycSfx in BYTECODE_SUFFIXES: + try: - pycData, pycPath = self._getData(f + pycSfx, pycMode) + pycData, pycPath = self._getData(f + pycSfx, 'rb') except IOError: why = sys.exc_info()[1] @@ -91,7 +102,7 @@ def read(self, f): raise error.MibLoadError('MIB file %s access error: %s' % (f + pycSfx, why)) else: - if self.__magic == pycData[:4]: + if PY_MAGIC_NUMBER == pycData[:4]: pycData = pycData[4:] pycTime = struct.unpack(' Date: Fri, 12 Apr 2019 09:01:26 +0200 Subject: [PATCH 077/102] Fix `importlib` use on Py27 Also fixes bug in `imp`-based initialization --- pysnmp/smi/builder.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/pysnmp/smi/builder.py b/pysnmp/smi/builder.py index 2f55b8eb5..a71c8a2c6 100644 --- a/pysnmp/smi/builder.py +++ b/pysnmp/smi/builder.py @@ -14,16 +14,22 @@ try: import importlib - PY_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER - SOURCE_SUFFIXES = importlib.machinery.SOURCE_SUFFIXES - BYTECODE_SUFFIXES = importlib.machinery.BYTECODE_SUFFIXES + try: + PY_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER + SOURCE_SUFFIXES = importlib.machinery.SOURCE_SUFFIXES + BYTECODE_SUFFIXES = importlib.machinery.BYTECODE_SUFFIXES + + except Exception: + raise ImportError() except ImportError: import imp PY_MAGIC_NUMBER = imp.get_magic() - SOURCE_SUFFIXES = [imp.PY_SOURCE] - BYTECODE_SUFFIXES = [imp.PY_COMPILED] + SOURCE_SUFFIXES = [s[0] for s in imp.get_suffixes() + if s[2] == imp.PY_SOURCE] + BYTECODE_SUFFIXES = [s[0] for s in imp.get_suffixes() + if s[2] == imp.PY_COMPILED] PY_SUFFIXES = SOURCE_SUFFIXES + BYTECODE_SUFFIXES From d75f2fdb4c147d29bed33bacc03466455908d2f8 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 23 Jun 2019 09:15:46 +0200 Subject: [PATCH 078/102] Fix `var-bindings` initialization Set `var-bindings` to an empty sequence by default. Otherwise it can remain a "pyasn1 schema object" failing to encode. This can happen with newer pyasn1 versions where `SequenceOf` type does not have default initializer. --- CHANGES.txt | 3 +++ pysnmp/proto/api/v1.py | 6 ++++-- pysnmp/proto/api/v2c.py | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 90454a5d4..778ed8a70 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,6 +7,9 @@ Revision 4.4.10, released 2019-04-XX - Fixed asyncore main loop to respect non-default timer resolution - Fixed `.setTimerResolution()` behaviour of abstract main loop dispatcher to update call intervals of the existing periodic dispatcher jobs +- Fixed `var-bindings` initialization to prevent pyasn1 encoder failures + with newer pyasn1 versions where `SequenceOf` type looses its default + initializer. Revision 4.4.9, released 2019-02-09 ----------------------------------- diff --git a/pysnmp/proto/api/v1.py b/pysnmp/proto/api/v1.py index 1f9689868..37882859f 100644 --- a/pysnmp/proto/api/v1.py +++ b/pysnmp/proto/api/v1.py @@ -66,7 +66,8 @@ def setDefaults(self, pdu): pdu.setComponentByPosition( 2, self._errorIndex, verifyConstraints=False, matchTags=False, matchConstraints=False ) - pdu.setComponentByPosition(3) + varBindList = pdu.setComponentByPosition(3).getComponentByPosition(3) + varBindList.clear() @staticmethod def getRequestID(pdu): @@ -170,7 +171,8 @@ def setDefaults(self, pdu): pdu.setComponentByPosition(2, self._genericTrap, verifyConstraints=False, matchTags=False, matchConstraints=False) pdu.setComponentByPosition(3, self._zeroInt, verifyConstraints=False, matchTags=False, matchConstraints=False) pdu.setComponentByPosition(4, self._zeroTime, verifyConstraints=False, matchTags=False, matchConstraints=False) - pdu.setComponentByPosition(5) + varBindList = pdu.setComponentByPosition(5).getComponentByPosition(5) + varBindList.clear() @staticmethod def getEnterprise(pdu): diff --git a/pysnmp/proto/api/v2c.py b/pysnmp/proto/api/v2c.py index 656c88f48..b21ecf366 100644 --- a/pysnmp/proto/api/v2c.py +++ b/pysnmp/proto/api/v2c.py @@ -91,7 +91,8 @@ def setDefaults(self, pdu): pdu.setComponentByPosition( 2, self._maxRepetitions, verifyConstraints=False, matchTags=False, matchConstraints=False ) - pdu.setComponentByPosition(3) + varBindList = pdu.setComponentByPosition(3).getComponentByPosition(3) + varBindList.clear() @staticmethod def getNonRepeaters(pdu): From 0228371e12b4a19a6b4942626924f8040a92f4cd Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 20 Jul 2019 00:41:49 +0200 Subject: [PATCH 079/102] Fix TRAP PDU proxy translation Fixed crash on uninitialized component serialization left out in SNMP v1 TRAP PDU to SNMPv2/3 TRAP PDU translation routine. --- CHANGES.txt | 2 ++ pysnmp/proto/proxy/rfc2576.py | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 778ed8a70..3a1efbb60 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,6 +10,8 @@ Revision 4.4.10, released 2019-04-XX - Fixed `var-bindings` initialization to prevent pyasn1 encoder failures with newer pyasn1 versions where `SequenceOf` type looses its default initializer. +- Fixed crash on uninitialized component serialization left out in + SNMP v1 TRAP PDU to SNMPv2/3 TRAP PDU proxy translation routine. Revision 4.4.9, released 2019-02-09 ----------------------------------- diff --git a/pysnmp/proto/proxy/rfc2576.py b/pysnmp/proto/proxy/rfc2576.py index 6b1dfcbb8..7b20ddc9a 100644 --- a/pysnmp/proto/proxy/rfc2576.py +++ b/pysnmp/proto/proxy/rfc2576.py @@ -142,15 +142,17 @@ def v1ToV2(v1Pdu, origV2Pdu=None, snmpTrapCommunity=''): (oid, __v1ToV2ValueMap[v1Val.tagSet].clone(v1Val)) ) - if pduType in rfc3411.responseClassPDUs: - # 4.1.2.2.1&2 + if pduType not in rfc3411.notificationClassPDUs: errorStatus = int(v1.apiPDU.getErrorStatus(v1Pdu)) errorIndex = int(v1.apiPDU.getErrorIndex(v1Pdu, muteErrors=True)) - if errorStatus == 2: # noSuchName - if origV2Pdu.tagSet == v2c.GetNextRequestPDU.tagSet: - v2VarBinds = [(o, rfc1905.endOfMibView) for o, v in v2VarBinds] - else: - v2VarBinds = [(o, rfc1905.noSuchObject) for o, v in v2VarBinds] + + if pduType in rfc3411.responseClassPDUs: + # 4.1.2.2.1&2 + if errorStatus == 2: # noSuchName + if origV2Pdu.tagSet == v2c.GetNextRequestPDU.tagSet: + v2VarBinds = [(o, rfc1905.endOfMibView) for o, v in v2VarBinds] + else: + v2VarBinds = [(o, rfc1905.noSuchObject) for o, v in v2VarBinds] # partial one-to-one mapping - 4.2.1 v2c.apiPDU.setErrorStatus(v2Pdu, errorStatus) @@ -158,13 +160,11 @@ def v1ToV2(v1Pdu, origV2Pdu=None, snmpTrapCommunity=''): # 4.1.2.1 --> no-op - elif pduType in rfc3411.confirmedClassPDUs: - v2c.apiPDU.setErrorStatus(v2Pdu, 0) - v2c.apiPDU.setErrorIndex(v2Pdu, 0) - - if pduType not in rfc3411.notificationClassPDUs: v2c.apiPDU.setRequestID(v2Pdu, int(v1.apiPDU.getRequestID(v1Pdu))) + else: + v2c.apiPDU.setDefaults(v2Pdu) + v2c.apiPDU.setVarBinds(v2Pdu, v2VarBinds) debug.logger & debug.flagPrx and debug.logger('v1ToV2: v2Pdu %s' % v2Pdu.prettyPrint()) From 9441850839bf8ebf0c8b365dcbebe22cb98b3baa Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Mon, 29 Jul 2019 09:57:45 +0200 Subject: [PATCH 080/102] Rework VACM access control function (#287) Most important changes include: * Added subtree match negation support (vacmViewTreeFamilyType) * Added subtree family mask support (vacmViewTreeFamilyMask) * Added prefix content name matching support (vacmAccessContextMatch) * Added key VACM tables caching for better lookup performance --- CHANGES.txt | 14 +- .../asyncore/agent/cmdrsp/advanced-topics.rst | 10 + .../cmdrsp/detailed-vacm-configuration.py | 117 +++++ pysnmp/entity/config.py | 58 ++- pysnmp/proto/acmod/rfc3415.py | 428 ++++++++++++++---- 5 files changed, 508 insertions(+), 119 deletions(-) create mode 100644 examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py diff --git a/CHANGES.txt b/CHANGES.txt index 3a1efbb60..37765b7b1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,7 +1,19 @@ -Revision 4.4.10, released 2019-04-XX +Revision 4.4.10, released 2019-07-XX ------------------------------------ +- Reworked VACM access control function. Most important changes include: + + * Added subtree match negation support (vacmViewTreeFamilyType) + * Added subtree family mask support (vacmViewTreeFamilyMask) + * Added prefix content name matching support (vacmAccessContextMatch) + * Added key VACM tables caching for better `isAccessAllowed` lookup + performance + + One potential incompatibility may be caused by the `addContext()` call + which now needs to be made explicitly during low-level VACM configuration + rather than be a side effect of `addVacmAccess()` call. + - Rebased MIB importing code onto `importlib` because `imp` is long deprecated - Fixed asyncore main loop to respect non-default timer resolution diff --git a/docs/source/examples/v3arch/asyncore/agent/cmdrsp/advanced-topics.rst b/docs/source/examples/v3arch/asyncore/agent/cmdrsp/advanced-topics.rst index 3b1d95dbe..38af9cb49 100644 --- a/docs/source/examples/v3arch/asyncore/agent/cmdrsp/advanced-topics.rst +++ b/docs/source/examples/v3arch/asyncore/agent/cmdrsp/advanced-topics.rst @@ -37,4 +37,14 @@ Advanced topics :download:`Download` script. +.. include:: /../../examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py + :start-after: """ + :end-before: """# + +.. literalinclude:: /../../examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py + :start-after: """# + :language: python + +:download:`Download` script. + See also: :doc:`library reference `. diff --git a/examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py b/examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py new file mode 100644 index 000000000..374d4a826 --- /dev/null +++ b/examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py @@ -0,0 +1,117 @@ +""" +Detailed VACM configuration ++++++++++++++++++++++++++++ + +Serves MIB subtrees under different conditions: + +* Respond to SNMPv2c commands +* with SNMP community "public" +* over IPv4/UDP, listening at 127.0.0.1:161 +* Serve MIB under non-default contextName `abcd` +* Allow access to `SNMPv2-MIB::system` subtree +* Although deny access to `SNMPv2-MIB::sysUpTime` by a bit mask +* Use partial context name matching (`a`) + +This example demonstrates detailed VACM configuration performed via +low-level VACM calls: `addContext`, `addVacmGroup`, `addVacmAccess` +and `addVacmView`. Each function populates one of the tables +defined in `SNMP-VIEW-BASED-ACM-MIB` and used strictly as described +in the above mentioned MIB. + +The following Net-SNMP's commands will GET a value at this Agent: + +| $ snmpget -v2c -c public 127.0.0.1 SNMPv2-MIB::sysLocation.0 + +However this command will fail: + +| $ snmpget -v2c -c public 127.0.0.1 SNMPv2-MIB::sysUpTime.0 + +This command will not reveal `SNMPv2-MIB::sysUpTime.0` among other objects: + +| $ snmpwalk -v2c -c public 127.0.0.1 SNMPv2-MIB::system +"""# +from pysnmp.entity import engine, config +from pysnmp.entity.rfc3413 import cmdrsp, context +from pysnmp.carrier.asyncore.dgram import udp + +# Create SNMP engine with autogenernated engineID and pre-bound +# to socket transport dispatcher +snmpEngine = engine.SnmpEngine() + +# Transport setup + +# UDP over IPv4 +config.addTransport( + snmpEngine, + udp.domainName, + udp.UdpTransport().openServerMode(('127.0.0.1', 1161)) +) + +# Register default MIB instrumentation controller with a new SNMP context + +contextName = 'abcd' + +snmpContext = context.SnmpContext(snmpEngine) + +snmpContext.registerContextName( + contextName, snmpEngine.msgAndPduDsp.mibInstrumController) + +# Add new SNMP community name, map it to a new security name and +# SNMP context + +securityName = 'my-area' +communityName = 'public' + +config.addV1System( + snmpEngine, securityName, communityName, + contextEngineId=snmpContext.contextEngineId, + contextName=contextName) + +# VACM configuration settings + +securityModel = 2 # SNMPv2c +securityLevel = 1 # noAuthNoPriv + +vacmGroup = 'my-group' +readViewName = 'my-read-view' + +# We will match by context name prefix +contextPrefix = contextName[:1] + +# Populate SNMP-VIEW-BASED-ACM-MIB::vacmContextTable +config.addContext(snmpEngine, contextName) + +# Populate SNMP-VIEW-BASED-ACM-MIB::vacmSecurityToGroupTable +config.addVacmGroup( + snmpEngine, vacmGroup, securityModel, securityName) + +# Populate SNMP-VIEW-BASED-ACM-MIB::vacmAccessTable +config.addVacmAccess( + snmpEngine, vacmGroup, contextPrefix, securityModel, securityLevel, + 'prefix', readViewName, '', '') + +# Populate SNMP-VIEW-BASED-ACM-MIB::vacmViewTreeFamilyTable + +# Allow the whole system subtree +config.addVacmView( + snmpEngine, readViewName, 'included', '1.3.6.1.2.1.1.1', '1.1.1.1.1.1.1.0') + +# ...but exclude one sub-branch (just one scalar OID) +config.addVacmView( + snmpEngine, readViewName, 'excluded', '1.3.6.1.2.1.1.3', '1.1.1.1.1.1.1.1') + +# Register SNMP Applications at the SNMP engine for particular SNMP context +cmdrsp.GetCommandResponder(snmpEngine, snmpContext) +cmdrsp.SetCommandResponder(snmpEngine, snmpContext) +cmdrsp.NextCommandResponder(snmpEngine, snmpContext) + +# Register an imaginary never-ending job to keep I/O dispatcher running forever +snmpEngine.transportDispatcher.jobStarted(1) + +# Run I/O dispatcher which would receive queries and send responses +try: + snmpEngine.transportDispatcher.runDispatcher() + +except Exception: + snmpEngine.transportDispatcher.closeDispatcher() + raise diff --git a/pysnmp/entity/config.py b/pysnmp/entity/config.py index 20fe63a0c..8e76bf3e6 100644 --- a/pysnmp/entity/config.py +++ b/pysnmp/entity/config.py @@ -11,6 +11,7 @@ from pysnmp.proto.secmod.rfc3826.priv import aes from pysnmp.proto.secmod.rfc7860.auth import hmacsha2 from pysnmp.proto.secmod.eso.priv import des3, aes192, aes256 +from pysnmp.proto import rfc1902 from pysnmp.proto import rfc1905 from pysnmp import error @@ -461,22 +462,20 @@ def __cookVacmAccessInfo(snmpEngine, groupName, contextName, securityModel, return vacmAccessEntry, tblIdx -def addVacmAccess(snmpEngine, groupName, contextName, securityModel, - securityLevel, prefix, readView, writeView, notifyView): - vacmAccessEntry, tblIdx = __cookVacmAccessInfo(snmpEngine, groupName, - contextName, securityModel, - securityLevel) - - addContext(snmpEngine, contextName) +def addVacmAccess(snmpEngine, groupName, contextPrefix, securityModel, + securityLevel, contextMatch, readView, writeView, notifyView): + vacmAccessEntry, tblIdx = __cookVacmAccessInfo( + snmpEngine, groupName, contextPrefix, securityModel, + securityLevel) snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((vacmAccessEntry.name + (9,) + tblIdx, 'destroy'),) ) snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( - ((vacmAccessEntry.name + (1,) + tblIdx, contextName), + ((vacmAccessEntry.name + (1,) + tblIdx, contextPrefix), (vacmAccessEntry.name + (2,) + tblIdx, securityModel), (vacmAccessEntry.name + (3,) + tblIdx, securityLevel), - (vacmAccessEntry.name + (4,) + tblIdx, prefix), + (vacmAccessEntry.name + (4,) + tblIdx, contextMatch), (vacmAccessEntry.name + (5,) + tblIdx, readView), (vacmAccessEntry.name + (6,) + tblIdx, writeView), (vacmAccessEntry.name + (7,) + tblIdx, notifyView), @@ -484,13 +483,10 @@ def addVacmAccess(snmpEngine, groupName, contextName, securityModel, ) -def delVacmAccess(snmpEngine, groupName, contextName, securityModel, +def delVacmAccess(snmpEngine, groupName, contextPrefix, securityModel, securityLevel): - vacmAccessEntry, tblIdx = __cookVacmAccessInfo(snmpEngine, groupName, - contextName, securityModel, - securityLevel) - - delContext(snmpEngine, contextName) + vacmAccessEntry, tblIdx = __cookVacmAccessInfo( + snmpEngine, groupName, contextPrefix, securityModel, securityLevel) snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((vacmAccessEntry.name + (9,) + tblIdx, 'destroy'),) @@ -507,16 +503,30 @@ def __cookVacmViewInfo(snmpEngine, viewName, subTree): return vacmViewTreeFamilyEntry, tblIdx -def addVacmView(snmpEngine, viewName, viewType, subTree, mask): - vacmViewTreeFamilyEntry, tblIdx = __cookVacmViewInfo(snmpEngine, viewName, - subTree) +def addVacmView(snmpEngine, viewName, viewType, subTree, subTreeMask): + vacmViewTreeFamilyEntry, tblIdx = __cookVacmViewInfo( + snmpEngine, viewName, subTree) + + # Allow bitmask specification in form of an OID + if '.' in subTreeMask: + subTreeMask = rfc1902.ObjectIdentifier(subTreeMask) + + if isinstance(subTreeMask, rfc1902.ObjectIdentifier): + subTreeMask = tuple(subTreeMask) + if len(subTreeMask) < len(subTree): + subTreeMask += (1,) * (len(subTree) - len(subTreeMask)) + + subTreeMask = rfc1902.OctetString.fromBinaryString( + ''.join(str(x) for x in subTreeMask)) + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((vacmViewTreeFamilyEntry.name + (6,) + tblIdx, 'destroy'),) ) + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((vacmViewTreeFamilyEntry.name + (1,) + tblIdx, viewName), (vacmViewTreeFamilyEntry.name + (2,) + tblIdx, subTree), - (vacmViewTreeFamilyEntry.name + (3,) + tblIdx, mask), + (vacmViewTreeFamilyEntry.name + (3,) + tblIdx, subTreeMask), (vacmViewTreeFamilyEntry.name + (4,) + tblIdx, viewType), (vacmViewTreeFamilyEntry.name + (6,) + tblIdx, 'createAndGo')) ) @@ -548,15 +558,16 @@ def addVacmUser(snmpEngine, securityModel, securityName, securityLevel, (groupName, securityLevel, readView, writeView, notifyView) = __cookVacmUserInfo(snmpEngine, securityModel, securityName, securityLevel) + addContext(snmpEngine, contextName) addVacmGroup(snmpEngine, groupName, securityModel, securityName) addVacmAccess(snmpEngine, groupName, contextName, securityModel, - securityLevel, 1, readView, writeView, notifyView) + securityLevel, 'exact', readView, writeView, notifyView) if readSubTree: - addVacmView(snmpEngine, readView, "included", readSubTree, null) + addVacmView(snmpEngine, readView, 'included', readSubTree, null) if writeSubTree: - addVacmView(snmpEngine, writeView, "included", writeSubTree, null) + addVacmView(snmpEngine, writeView, 'included', writeSubTree, null) if notifySubTree: - addVacmView(snmpEngine, notifyView, "included", notifySubTree, null) + addVacmView(snmpEngine, notifyView, 'included', notifySubTree, null) def delVacmUser(snmpEngine, securityModel, securityName, securityLevel, @@ -565,6 +576,7 @@ def delVacmUser(snmpEngine, securityModel, securityName, securityLevel, (groupName, securityLevel, readView, writeView, notifyView) = __cookVacmUserInfo(snmpEngine, securityModel, securityName, securityLevel) + delContext(snmpEngine, contextName) delVacmGroup(snmpEngine, securityModel, securityName) delVacmAccess(snmpEngine, groupName, contextName, securityModel, securityLevel) if readSubTree: diff --git a/pysnmp/proto/acmod/rfc3415.py b/pysnmp/proto/acmod/rfc3415.py index cf020d9f8..a1e9f6f7e 100644 --- a/pysnmp/proto/acmod/rfc3415.py +++ b/pysnmp/proto/acmod/rfc3415.py @@ -16,6 +16,117 @@ class Vacm(object): _powOfTwoSeq = (128, 64, 32, 16, 8, 4, 2, 1) + def __init__(self): + self._contextBranchId = -1 + self._groupNameBranchId = -1 + self._accessBranchId = -1 + self._viewTreeBranchId = -1 + + self._contextMap = {} + self._groupNameMap = {} + self._accessMap = {} + self._viewTreeMap = {} + + def _addAccessEntry(self, groupName, contextPrefix, securityModel, + securityLevel, prefixMatch, readView, writeView, + notifyView): + if not groupName: + return + + groups = self._accessMap + + try: + views = groups[groupName] + + except KeyError: + views = groups[groupName] = {} + + for viewType, viewName in ( + ('read', readView), ('write', writeView), + ('notify', notifyView)): + + try: + matches = views[viewType] + + except KeyError: + matches = views[viewType] = {} + + try: + contexts = matches[prefixMatch] + + except KeyError: + contexts = matches[prefixMatch] = {} + + try: + models = contexts[contextPrefix] + + except KeyError: + models = contexts[contextPrefix] = {} + + try: + levels = models[securityModel] + + except KeyError: + levels = models[securityModel] = {} + + levels[securityLevel] = viewName + + def _getFamilyViewName(self, groupName, contextName, securityModel, securityLevel, viewType): + groups = self._accessMap + + try: + views = groups[groupName] + + except KeyError: + raise error.StatusInformation(errorIndication=errind.noGroupName) + + try: + matches = views[viewType] + + except KeyError: + raise error.StatusInformation(errorIndication=errind.noAccessEntry) + + try: + # vacmAccessTable #2: exact match shortcut + return matches[1][contextName][securityModel][securityLevel] + + except KeyError: + pass + + # vacmAccessTable #2: fuzzy look-up + + candidates = [] + + for match, names in matches.items(): + + for context, models in names.items(): + + if match == 1 and contextName != context: + continue + + if match == 2 and contextName[:len(context)] != context: + continue + + for model, levels in models.items(): + for level, viewName in levels.items(): + + # priorities: + # - matching securityModel + # - exact context name match + # - longer partial match + # - highest securityLevel + rating = securityModel == model, match == 1, len(context), level + + candidates.append((rating, viewName)) + + if not candidates: + raise error.StatusInformation(errorIndication=errind.notInView) + + candidates.sort() + + rating, viewName = candidates[0] + return viewName + def isAccessAllowed(self, snmpEngine, securityModel, @@ -24,118 +135,245 @@ def isAccessAllowed(self, viewType, contextName, variableName): + mibInstrumController = snmpEngine.msgAndPduDsp.mibInstrumController debug.logger & debug.flagACL and debug.logger( - 'isAccessAllowed: securityModel %s, securityName %s, securityLevel %s, viewType %s, contextName %s for variableName %s' % ( - securityModel, securityName, securityLevel, viewType, contextName, variableName)) + 'isAccessAllowed: securityModel %s, securityName %s, ' + 'securityLevel %s, viewType %s, contextName %s for ' + 'variableName %s' % (securityModel, securityName, + securityLevel, viewType, contextName, + variableName)) - # 3.2.1 - vacmContextEntry, = mibInstrumController.mibBuilder.importSymbols( - 'SNMP-VIEW-BASED-ACM-MIB', 'vacmContextEntry') + # Rebuild contextName map if changed - tblIdx = vacmContextEntry.getInstIdFromIndices(contextName) - try: - vacmContextEntry.getNode( - vacmContextEntry.name + (1,) + tblIdx - ).syntax + vacmContextName, = mibInstrumController.mibBuilder.importSymbols( + 'SNMP-VIEW-BASED-ACM-MIB', 'vacmContextName') + + if self._contextBranchId != vacmContextName.branchVersionId: + + self._contextMap.clear() + + nextMibNode = vacmContextName + + while True: + try: + nextMibNode = vacmContextName.getNextNode(nextMibNode.name) + + except NoSuchInstanceError: + break - except NoSuchInstanceError: + self._contextMap[nextMibNode.syntax] = True + + self._contextBranchId = vacmContextName.branchVersionId + + # 3.2.1 + if contextName not in self._contextMap: raise error.StatusInformation(errorIndication=errind.noSuchContext) + # Rebuild groupName map if changed + + vacmGroupName, = mibInstrumController.mibBuilder.importSymbols( + 'SNMP-VIEW-BASED-ACM-MIB', 'vacmGroupName') + + if self._groupNameBranchId != vacmGroupName.branchVersionId: + + vacmSecurityToGroupEntry, = mibInstrumController.mibBuilder.importSymbols( + 'SNMP-VIEW-BASED-ACM-MIB', 'vacmSecurityToGroupEntry') + + self._groupNameMap.clear() + + nextMibNode = vacmGroupName + + while True: + try: + nextMibNode = vacmGroupName.getNextNode(nextMibNode.name) + + except NoSuchInstanceError: + break + + instId = nextMibNode.name[len(vacmGroupName.name):] + + indices = vacmSecurityToGroupEntry.getIndicesFromInstId(instId) + + self._groupNameMap[indices] = nextMibNode.syntax + + self._groupNameBranchId = vacmGroupName.branchVersionId + # 3.2.2 - vacmSecurityToGroupEntry, = mibInstrumController.mibBuilder.importSymbols( - 'SNMP-VIEW-BASED-ACM-MIB', 'vacmSecurityToGroupEntry') - tblIdx = vacmSecurityToGroupEntry.getInstIdFromIndices( - securityModel, securityName - ) + indices = securityModel, securityName try: - vacmGroupName = vacmSecurityToGroupEntry.getNode( - vacmSecurityToGroupEntry.name + (3,) + tblIdx - ).syntax + groupName = self._groupNameMap[indices] - except NoSuchInstanceError: + except KeyError: raise error.StatusInformation(errorIndication=errind.noGroupName) - # 3.2.3 - vacmAccessEntry, = mibInstrumController.mibBuilder.importSymbols( - 'SNMP-VIEW-BASED-ACM-MIB', 'vacmAccessEntry' - ) - - # XXX partial context name match - tblIdx = vacmAccessEntry.getInstIdFromIndices( - vacmGroupName, contextName, securityModel, securityLevel - ) - - # 3.2.4 - if viewType == 'read': - entryIdx = vacmAccessEntry.name + (5,) + tblIdx - elif viewType == 'write': - entryIdx = vacmAccessEntry.name + (6,) + tblIdx - elif viewType == 'notify': - entryIdx = vacmAccessEntry.name + (7,) + tblIdx - else: - raise error.ProtocolError('Unknown view type %s' % viewType) + # Rebuild access map if changed - try: - viewName = vacmAccessEntry.getNode(entryIdx).syntax + vacmAccessStatus, = mibInstrumController.mibBuilder.importSymbols( + 'SNMP-VIEW-BASED-ACM-MIB', 'vacmAccessStatus') - except NoSuchInstanceError: - raise error.StatusInformation(errorIndication=errind.noAccessEntry) + if self._accessBranchId != vacmAccessStatus.branchVersionId: + + (vacmAccessEntry, + vacmAccessContextPrefix, + vacmAccessSecurityModel, + vacmAccessSecurityLevel, + vacmAccessContextMatch, + vacmAccessReadViewName, + vacmAccessWriteViewName, + vacmAccessNotifyViewName) = mibInstrumController.mibBuilder.importSymbols( + 'SNMP-VIEW-BASED-ACM-MIB', + 'vacmAccessEntry', + 'vacmAccessContextPrefix', + 'vacmAccessSecurityModel', + 'vacmAccessSecurityLevel', + 'vacmAccessContextMatch', + 'vacmAccessReadViewName', + 'vacmAccessWriteViewName', + 'vacmAccessNotifyViewName') + + self._accessMap.clear() + + nextMibNode = vacmAccessStatus + + while True: + try: + nextMibNode = vacmAccessStatus.getNextNode(nextMibNode.name) + + except NoSuchInstanceError: + break + + if nextMibNode.syntax != 1: # active row + continue + + instId = nextMibNode.name[len(vacmAccessStatus.name):] + + indices = vacmAccessEntry.getIndicesFromInstId(instId) + + vacmGroupName = indices[0] + + self._addAccessEntry( + vacmGroupName, + vacmAccessContextPrefix.getNode( + vacmAccessContextPrefix.name + instId).syntax, + vacmAccessSecurityModel.getNode( + vacmAccessSecurityModel.name + instId).syntax, + vacmAccessSecurityLevel.getNode( + vacmAccessSecurityLevel.name + instId).syntax, + vacmAccessContextMatch.getNode( + vacmAccessContextMatch.name + instId).syntax, + vacmAccessReadViewName.getNode( + vacmAccessReadViewName.name + instId).syntax, + vacmAccessWriteViewName.getNode( + vacmAccessWriteViewName.name + instId).syntax, + vacmAccessNotifyViewName.getNode( + vacmAccessNotifyViewName.name + instId).syntax + ) + + self._accessBranchId = vacmAccessStatus.branchVersionId + + viewName = self._getFamilyViewName( + groupName, contextName, securityModel, securityLevel, viewType) + + # Rebuild family subtree map if changed + + vacmViewTreeFamilyViewName, = mibInstrumController.mibBuilder.importSymbols( + 'SNMP-VIEW-BASED-ACM-MIB', 'vacmViewTreeFamilyViewName') + + if self._viewTreeBranchId != vacmViewTreeFamilyViewName.branchVersionId: + + (vacmViewTreeFamilySubtree, + vacmViewTreeFamilyMask, + vacmViewTreeFamilyType) = mibInstrumController.mibBuilder.importSymbols( + 'SNMP-VIEW-BASED-ACM-MIB', + 'vacmViewTreeFamilySubtree', + 'vacmViewTreeFamilyMask', + 'vacmViewTreeFamilyType') + + self._viewTreeMap.clear() - if not viewName: - raise error.StatusInformation(errorIndication=errind.noSuchView) + powerOfTwo = [2 ** exp for exp in range(7, -1, -1)] - # XXX split onto object & instance ? + nextMibNode = vacmViewTreeFamilyViewName + + while True: + try: + nextMibNode = vacmViewTreeFamilyViewName.getNextNode( + nextMibNode.name) + + except NoSuchInstanceError: + break + + if nextMibNode.syntax not in self._viewTreeMap: + self._viewTreeMap[nextMibNode.syntax] = [] + + instId = nextMibNode.name[len(vacmViewTreeFamilyViewName.name):] + + subtree = vacmViewTreeFamilySubtree.getNode( + vacmViewTreeFamilySubtree.name + instId).syntax + + mask = vacmViewTreeFamilyMask.getNode( + vacmViewTreeFamilyMask.name + instId).syntax + + mode = vacmViewTreeFamilyType.getNode( + vacmViewTreeFamilyType.name + instId).syntax + + mask = mask.asNumbers() + maskLength = min(len(mask) * 8, len(subtree)) + + ignoredSubOids = [ + i * 8 + j for i, octet in enumerate(mask) + for j, bit in enumerate(powerOfTwo) + if not (bit & octet) and i * 8 + j < maskLength + ] + + if ignoredSubOids: + pattern = list(subtree) + + for ignoredSubOid in ignoredSubOids: + pattern[ignoredSubOid] = 0 + + subtree = subtree.clone(pattern) + + entry = subtree, ignoredSubOids, mode == 1 + + self._viewTreeMap[nextMibNode.syntax].append(entry) + + for entries in self._viewTreeMap.values(): + entries.sort(key=lambda x: (len(x[0]), x[0])) + + self._viewTreeBranchId = vacmViewTreeFamilyViewName.branchVersionId # 3.2.5a - vacmViewTreeFamilyEntry, = mibInstrumController.mibBuilder.importSymbols( - 'SNMP-VIEW-BASED-ACM-MIB', 'vacmViewTreeFamilyEntry') - tblIdx = vacmViewTreeFamilyEntry.getInstIdFromIndices(viewName) - - # Walk over entries - initialTreeName = treeName = vacmViewTreeFamilyEntry.name + (2,) + tblIdx - maskName = vacmViewTreeFamilyEntry.name + (3,) + tblIdx - - while True: - vacmViewTreeFamilySubtree = vacmViewTreeFamilyEntry.getNextNode( - treeName - ) - vacmViewTreeFamilyMask = vacmViewTreeFamilyEntry.getNextNode( - maskName - ) - - treeName = vacmViewTreeFamilySubtree.name - maskName = vacmViewTreeFamilyMask.name - - if initialTreeName != treeName[:len(initialTreeName)]: - # 3.2.5b - raise error.StatusInformation(errorIndication=errind.notInView) - - l = len(vacmViewTreeFamilySubtree.syntax) - if l > len(variableName): - continue - - if vacmViewTreeFamilyMask.syntax: - mask = [] - for c in vacmViewTreeFamilyMask.syntax.asNumbers(): - mask.extend([b & c for b in self._powOfTwoSeq]) - - m = len(mask) - 1 - idx = l - 1 - while idx: - if (idx > m or mask[idx] and - vacmViewTreeFamilySubtree.syntax[idx] != variableName[idx]): - break - idx -= 1 - - if idx: - continue # no match - - else: # no mask - if vacmViewTreeFamilySubtree.syntax != variableName[:l]: - continue # no match - - # 3.2.5c - return error.StatusInformation(errorIndication=errind.accessAllowed) + indices = viewName + + try: + entries = self._viewTreeMap[indices] + + except KeyError: + return error.StatusInformation(errorIndication=errind.notInView) + + accessAllowed = False + + for entry in entries: + subtree, ignoredSubOids, included = entry + + if ignoredSubOids: + subOids = list(variableName) + + for ignoredSubOid in ignoredSubOids: + subOids[ignoredSubOid] = 0 + + normalizedVariableName = subtree.clone(subOids) + + else: + normalizedVariableName = variableName + + if subtree.isPrefixOf(normalizedVariableName): + accessAllowed = included + + # 3.2.5c + if not accessAllowed: + raise error.StatusInformation(errorIndication=errind.notInView) From f2403842ab4af862127b2fce2799f6feff57ef4a Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Mon, 29 Jul 2019 22:52:38 +0200 Subject: [PATCH 081/102] Fix Python 2.4 compatibility @property.setter has not been invented back then --- pysnmp/carrier/base.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/pysnmp/carrier/base.py b/pysnmp/carrier/base.py index 40b8d78ac..1ab3b4547 100644 --- a/pysnmp/carrier/base.py +++ b/pysnmp/carrier/base.py @@ -4,19 +4,25 @@ # Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # +import sys + from pysnmp.carrier import error class TimerCallable(object): def __init__(self, cbFun, callInterval): self.__cbFun = cbFun - self.__callInterval = callInterval self.__nextCall = 0 + if sys.version_info > (2, 5): + self.__callInterval = callInterval + else: + self.interval = callInterval + def __call__(self, timeNow): if self.__nextCall <= timeNow: self.__cbFun(timeNow) - self.__nextCall = timeNow + self.__callInterval + self.__nextCall = timeNow + self.interval def __eq__(self, cbFun): return self.__cbFun == cbFun @@ -36,13 +42,14 @@ def __gt__(self, cbFun): def __ge__(self, cbFun): return self.__cbFun >= cbFun - @property - def interval(self): - return self.__callInterval + if sys.version_info > (2, 5): + @property + def interval(self): + return self.__callInterval - @interval.setter - def interval(self, callInterval): - self.__callInterval = callInterval + @interval.setter + def interval(self, callInterval): + self.__callInterval = callInterval class AbstractTransportDispatcher(object): From 45461219d5e6f72cf32a23abc63f126f25223798 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Mon, 29 Jul 2019 22:56:03 +0200 Subject: [PATCH 082/102] Fix Python 2.5 compatibility @property.setter has not been invented back then --- pysnmp/carrier/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pysnmp/carrier/base.py b/pysnmp/carrier/base.py index 1ab3b4547..ce06b25bf 100644 --- a/pysnmp/carrier/base.py +++ b/pysnmp/carrier/base.py @@ -14,7 +14,7 @@ def __init__(self, cbFun, callInterval): self.__cbFun = cbFun self.__nextCall = 0 - if sys.version_info > (2, 5): + if sys.version_info > (2, 6): self.__callInterval = callInterval else: self.interval = callInterval @@ -42,7 +42,7 @@ def __gt__(self, cbFun): def __ge__(self, cbFun): return self.__cbFun >= cbFun - if sys.version_info > (2, 5): + if sys.version_info > (2, 6): @property def interval(self): return self.__callInterval From 5d9a176885f93ac0298a0e0cf5deb065534d95a5 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Mon, 29 Jul 2019 23:02:34 +0200 Subject: [PATCH 083/102] Release 4.4.10 --- CHANGES.txt | 2 +- .../v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 37765b7b1..e3233ad81 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,5 @@ -Revision 4.4.10, released 2019-07-XX +Revision 4.4.10, released 2019-07-29 ------------------------------------ - Reworked VACM access control function. Most important changes include: diff --git a/examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py b/examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py index 374d4a826..7f82dde83 100644 --- a/examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py +++ b/examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py @@ -44,7 +44,7 @@ config.addTransport( snmpEngine, udp.domainName, - udp.UdpTransport().openServerMode(('127.0.0.1', 1161)) + udp.UdpTransport().openServerMode(('127.0.0.1', 161)) ) # Register default MIB instrumentation controller with a new SNMP context From 5cefaccd91dd59956e6b2f291d0b9bee749e8286 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Tue, 30 Jul 2019 08:39:30 +0200 Subject: [PATCH 084/102] Support various forms of `vacmViewTreeFamilyMask` input This fixes release 4.4.10 before it's actually released. --- pysnmp/entity/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pysnmp/entity/config.py b/pysnmp/entity/config.py index 8e76bf3e6..2a12bc5c0 100644 --- a/pysnmp/entity/config.py +++ b/pysnmp/entity/config.py @@ -508,7 +508,7 @@ def addVacmView(snmpEngine, viewName, viewType, subTree, subTreeMask): snmpEngine, viewName, subTree) # Allow bitmask specification in form of an OID - if '.' in subTreeMask: + if rfc1902.OctetString('.').asOctets() in rfc1902.OctetString(subTreeMask): subTreeMask = rfc1902.ObjectIdentifier(subTreeMask) if isinstance(subTreeMask, rfc1902.ObjectIdentifier): From 9dd4bcc9983b39424e1ecbe907b5645ee8d57f9e Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Tue, 30 Jul 2019 09:11:23 +0200 Subject: [PATCH 085/102] Make MIB objects resolution more forgiving Added optional `ignoreErrors` parameter to `ObjectType.resolveWithMib()` to control that behaviour. --- CHANGES.txt | 3 +++ pysnmp/smi/rfc1902.py | 23 ++++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e3233ad81..3fc262cda 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -16,6 +16,9 @@ Revision 4.4.10, released 2019-07-29 - Rebased MIB importing code onto `importlib` because `imp` is long deprecated +- MIB objects resolution made more forgiving to errors, added optional + `ignoreErrors` parameter to `ObjectType.resolveWithMib()` to control + that behaviour. - Fixed asyncore main loop to respect non-default timer resolution - Fixed `.setTimerResolution()` behaviour of abstract main loop dispatcher to update call intervals of the existing periodic dispatcher jobs diff --git a/pysnmp/smi/rfc1902.py b/pysnmp/smi/rfc1902.py index c89900258..9c0a50bbd 100644 --- a/pysnmp/smi/rfc1902.py +++ b/pysnmp/smi/rfc1902.py @@ -805,7 +805,7 @@ def loadMibs(self, *modNames): self.__args[0].loadMibs(*modNames) return self - def resolveWithMib(self, mibViewController): + def resolveWithMib(self, mibViewController, ignoreErrors=True): """Perform MIB variable ID and associated value conversion. Parameters @@ -813,6 +813,12 @@ def resolveWithMib(self, mibViewController): mibViewController : :py:class:`~pysnmp.smi.view.MibViewController` class instance representing MIB browsing functionality. + Other Parameters + ---------------- + ignoreErrors: :py:class:`bool` + If `True` (default), ignore MIB object name or value casting + failures if possible. + Returns ------- : :py:class:`~pysnmp.smi.rfc1902.ObjectType` @@ -851,7 +857,8 @@ class instance representing MIB browsing functionality. if not isinstance(self.__args[0].getMibNode(), (MibScalar, MibTableColumn)): - if not isinstance(self.__args[1], AbstractSimpleAsn1Item): + if (ignoreErrors and + not isinstance(self.__args[1], AbstractSimpleAsn1Item)): raise SmiError('MIB object %r is not OBJECT-TYPE (MIB not loaded?)' % (self.__args[0],)) self.__state |= self.stClean return self @@ -866,9 +873,15 @@ class instance representing MIB browsing functionality. try: self.__args[1] = self.__args[0].getMibNode().getSyntax().clone(self.__args[1]) except PyAsn1Error: - raise SmiError('MIB object %r having type %r failed to cast value %r: %s' % ( - self.__args[0].prettyPrint(), self.__args[0].getMibNode().getSyntax().__class__.__name__, self.__args[1], - sys.exc_info()[1])) + err = ('MIB object %r having type %r failed to cast value ' + '%r: %s' % (self.__args[0].prettyPrint(), + self.__args[0].getMibNode().getSyntax().__class__.__name__, + self.__args[1], + sys.exc_info()[1])) + + if (not ignoreErrors or + not isinstance(self.__args[1], AbstractSimpleAsn1Item)): + raise SmiError(err) if rfc1902.ObjectIdentifier().isSuperTypeOf(self.__args[1], matchConstraints=False): self.__args[1] = ObjectIdentity(self.__args[1]).resolveWithMib(mibViewController) From e8fa401677432e4e48d1a9c36b996ef94b043d90 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Tue, 30 Jul 2019 20:29:22 +0200 Subject: [PATCH 086/102] Make received MIB objects resolution more forgiving Previously, MIB resolution errors were ignored (whenever possible) for objects we were sending and receiving. This change tightens outgoing objects MIB compliance (send will fail), but tolerate non quite compliant objects we receive. Also, extend the same policy onto `NotificationOriginator`. --- CHANGES.txt | 6 +++--- pysnmp/hlapi/varbinds.py | 14 +++++++++----- pysnmp/smi/rfc1902.py | 16 +++++++++++----- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3fc262cda..c3a9ad2c0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -16,9 +16,9 @@ Revision 4.4.10, released 2019-07-29 - Rebased MIB importing code onto `importlib` because `imp` is long deprecated -- MIB objects resolution made more forgiving to errors, added optional - `ignoreErrors` parameter to `ObjectType.resolveWithMib()` to control - that behaviour. +- Received MIB objects resolution made more forgiving to errors, added + optional `ignoreErrors` parameter to `ObjectType.resolveWithMib()` to + control that behaviour. - Fixed asyncore main loop to respect non-default timer resolution - Fixed `.setTimerResolution()` behaviour of abstract main loop dispatcher to update call intervals of the existing periodic dispatcher jobs diff --git a/pysnmp/hlapi/varbinds.py b/pysnmp/hlapi/varbinds.py index ece7b92b9..fdffc1b33 100644 --- a/pysnmp/hlapi/varbinds.py +++ b/pysnmp/hlapi/varbinds.py @@ -36,14 +36,15 @@ def makeVarBinds(self, snmpEngine, varBinds): else: varBind = ObjectType(ObjectIdentity(varBind[0]), varBind[1]) - __varBinds.append(varBind.resolveWithMib(mibViewController)) + __varBinds.append(varBind.resolveWithMib(mibViewController, ignoreErrors=False)) return __varBinds def unmakeVarBinds(self, snmpEngine, varBinds, lookupMib=True): if lookupMib: mibViewController = self.getMibViewController(snmpEngine) - varBinds = [ObjectType(ObjectIdentity(x[0]), x[1]).resolveWithMib(mibViewController) for x in varBinds] + varBinds = [ObjectType(ObjectIdentity(x[0]), x[1]).resolveWithMib( + mibViewController) for x in varBinds] return varBinds @@ -52,7 +53,8 @@ class NotificationOriginatorVarBinds(AbstractVarBinds): def makeVarBinds(self, snmpEngine, varBinds): mibViewController = self.getMibViewController(snmpEngine) if isinstance(varBinds, NotificationType): - varBinds.resolveWithMib(mibViewController) + varBinds.resolveWithMib( + mibViewController, ignoreErrors=False) __varBinds = [] for varBind in varBinds: if isinstance(varBind, ObjectType): @@ -61,11 +63,13 @@ def makeVarBinds(self, snmpEngine, varBinds): varBind = ObjectType(*varBind) else: varBind = ObjectType(ObjectIdentity(varBind[0]), varBind[1]) - __varBinds.append(varBind.resolveWithMib(mibViewController)) + __varBinds.append(varBind.resolveWithMib( + mibViewController, ignoreErrors=False)) return __varBinds def unmakeVarBinds(self, snmpEngine, varBinds, lookupMib=False): if lookupMib: mibViewController = self.getMibViewController(snmpEngine) - varBinds = [ObjectType(ObjectIdentity(x[0]), x[1]).resolveWithMib(mibViewController) for x in varBinds] + varBinds = [ObjectType(ObjectIdentity(x[0]), x[1]).resolveWithMib( + mibViewController) for x in varBinds] return varBinds diff --git a/pysnmp/smi/rfc1902.py b/pysnmp/smi/rfc1902.py index 9c0a50bbd..ad115e7ad 100644 --- a/pysnmp/smi/rfc1902.py +++ b/pysnmp/smi/rfc1902.py @@ -1113,7 +1113,7 @@ def loadMibs(self, *modNames): def isFullyResolved(self): return self.__state & self.stClean - def resolveWithMib(self, mibViewController): + def resolveWithMib(self, mibViewController, ignoreErrors=True): """Perform MIB variable ID conversion and notification objects expansion. Parameters @@ -1121,6 +1121,12 @@ def resolveWithMib(self, mibViewController): mibViewController : :py:class:`~pysnmp.smi.view.MibViewController` class instance representing MIB browsing functionality. + Other Parameters + ---------------- + ignoreErrors: :py:class:`bool` + If `True` (default), ignore MIB object name or value casting + failures if possible. + Returns ------- : :py:class:`~pysnmp.smi.rfc1902.NotificationType` @@ -1158,7 +1164,7 @@ class instance representing MIB browsing functionality. self.__varBinds.append( ObjectType(ObjectIdentity(v2c.apiTrapPDU.snmpTrapOID), - self.__objectIdentity).resolveWithMib(mibViewController) + self.__objectIdentity).resolveWithMib(mibViewController, ignoreErrors) ) SmiNotificationType, = mibViewController.mibBuilder.importSymbols('SNMPv2-SMI', 'NotificationType') @@ -1170,11 +1176,11 @@ class instance representing MIB browsing functionality. if isinstance(mibNode, SmiNotificationType): for notificationObject in mibNode.getObjects(): objectIdentity = ObjectIdentity(*notificationObject + self.__instanceIndex).resolveWithMib( - mibViewController) + mibViewController, ignoreErrors) self.__varBinds.append( ObjectType(objectIdentity, self.__objects.get(notificationObject, rfc1905.unSpecified)).resolveWithMib( - mibViewController) + mibViewController, ignoreErrors) ) varBindsLocation[objectIdentity] = len(self.__varBinds) - 1 else: @@ -1184,7 +1190,7 @@ class instance representing MIB browsing functionality. for varBinds in self.__additionalVarBinds: if not isinstance(varBinds, ObjectType): varBinds = ObjectType(ObjectIdentity(varBinds[0]), varBinds[1]) - varBinds.resolveWithMib(mibViewController) + varBinds.resolveWithMib(mibViewController, ignoreErrors) if varBinds[0] in varBindsLocation: self.__varBinds[varBindsLocation[varBinds[0]]] = varBinds else: From a901d4f4e7e8e54eebce7f79f88c9070e3e94db2 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Thu, 1 Aug 2019 08:14:10 +0200 Subject: [PATCH 087/102] Prepare for 4.4.11 --- CHANGES.txt | 5 +++++ pysnmp/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index c3a9ad2c0..cf8e2decc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,9 @@ +Revision 4.4.11, released 2019-08-XX +------------------------------------ + +No changes yet + Revision 4.4.10, released 2019-07-29 ------------------------------------ diff --git a/pysnmp/__init__.py b/pysnmp/__init__.py index 233ee90f0..628b0652f 100644 --- a/pysnmp/__init__.py +++ b/pysnmp/__init__.py @@ -1,5 +1,5 @@ # http://www.python.org/dev/peps/pep-0396/ -__version__ = '4.4.10' +__version__ = '4.4.11' # backward compatibility version = tuple([int(x) for x in __version__.split('.')]) majorVersionId = version[0] From 8cbbaeb9fcc6a8443b5fb8669f940f3dff94484e Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 3 Aug 2019 13:40:49 +0200 Subject: [PATCH 088/102] Add USM master and localized keys configuration support (#295) Added new optional parameters to `addUsmUser()` and `hlapi.UsmUserData()` functions allowing specifying key material type being passed to the respective routines. Plain-text pass-phrase remains the default, while user can change that to `master` or `localized` types. Refer to RFC3414 for technical details on SNMP USM key localization algorithm. --- CHANGES.txt | 2 +- docs/mibs/PYSNMP-USM-MIB.txt | 15 +++ docs/source/docs/api-reference.rst | 12 +- pysnmp/entity/config.py | 65 +++++++---- pysnmp/hlapi/__init__.py | 10 ++ pysnmp/hlapi/auth.py | 105 +++++++++++++++--- pysnmp/hlapi/lcd.py | 6 +- pysnmp/smi/mibs/PYSNMP-USM-MIB.py | 5 +- pysnmp/smi/mibs/instances/__PYSNMP-USM-MIB.py | 10 +- 9 files changed, 184 insertions(+), 46 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index cf8e2decc..884600944 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,7 +2,7 @@ Revision 4.4.11, released 2019-08-XX ------------------------------------ -No changes yet +- Added SNMPv3 USM master and localized keys support to LCD configuration Revision 4.4.10, released 2019-07-29 ------------------------------------ diff --git a/docs/mibs/PYSNMP-USM-MIB.txt b/docs/mibs/PYSNMP-USM-MIB.txt index 81e112c7b..739ca08e5 100644 --- a/docs/mibs/PYSNMP-USM-MIB.txt +++ b/docs/mibs/PYSNMP-USM-MIB.txt @@ -21,6 +21,8 @@ pysnmpUsmMIB MODULE-IDENTITY DESCRIPTION "This MIB module defines objects specific to User Security Model (USM) implementation at PySNMP." + REVISION "201908300000Z" + DESCRIPTION "Added USM key types" REVISION "201707300000Z" DESCRIPTION "Extended authentication key size" REVISION "201704140000Z" @@ -56,6 +58,19 @@ pysnmpUsmDiscovery OBJECT-TYPE DEFVAL { doDiscover } ::= { pysnmpUsmCfg 2 } +pysnmpUsmKeyType OBJECT-TYPE + SYNTAX INTEGER { passphrase (0), master(1), localized(2) } + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION "When configuring USM user, the value of this enumeration + determines how the keys should be treated. The default + value "passphrase" means that given keys are plain-text + pass-phrases, "master" indicates that the keys are pre-hashed + pass-phrases, while "localized" stands for pre-hashed + pass-phrases mixed with SNMP Security Engine ID value." + DEFVAL { passphrase } + ::= { pysnmpUsmCfg 3 } + -- The usmUser Group ************************************************ pysnmpUsmUser OBJECT IDENTIFIER ::= { pysnmpUsmMIBObjects 3 } diff --git a/docs/source/docs/api-reference.rst b/docs/source/docs/api-reference.rst index bc6f56930..b32f4dd00 100644 --- a/docs/source/docs/api-reference.rst +++ b/docs/source/docs/api-reference.rst @@ -217,7 +217,7 @@ User-based The :py:class:`~pysnmp.hlapi.UsmUserData` class provides SNMPv3 User-Based Security Model configuration for SNMP v3 systems. -.. autoclass:: pysnmp.hlapi.UsmUserData(userName, authKey=None, privKey=None, authProtocol=usmNoAuthProtocol, privProtocol=usmNoPrivProtocol, securityEngineId=None) +.. autoclass:: pysnmp.hlapi.UsmUserData(userName, authKey=None, privKey=None, authProtocol=usmNoAuthProtocol, privProtocol=usmNoPrivProtocol, securityEngineId=None, authKeyType=usmKeyTypePassphrase, privKeyType=usmKeyTypePassphrase) **Authentication protocol identifiers** @@ -240,10 +240,16 @@ Security Model configuration for SNMP v3 systems. .. autodata:: pysnmp.hlapi.usmAesBlumenthalCfb192Protocol .. autodata:: pysnmp.hlapi.usmAesBlumenthalCfb256Protocol +**Key material types** + +.. autodata:: pysnmp.hlapi.usmKeyTypePassphrase +.. autodata:: pysnmp.hlapi.usmKeyTypeMaster +.. autodata:: pysnmp.hlapi.usmKeyTypeLocalized + .. note:: - SNMP authentication and encryption keys must be at least *eight* - octets long. + SNMP authentication and encryption keys must be at least *8* + and at most *32* octets long. Transport configuration is I/O framework specific and is described in respective sections. diff --git a/pysnmp/entity/config.py b/pysnmp/entity/config.py index 2a12bc5c0..9087c00f7 100644 --- a/pysnmp/entity/config.py +++ b/pysnmp/entity/config.py @@ -43,6 +43,11 @@ usmAesCfb256Protocol = aes256.Aes256.serviceID # non-standard but used by many vendors usmNoPrivProtocol = nopriv.NoPriv.serviceID +# USM key types (PYSNMP-USM-MIB::pysnmpUsmKeyType) +usmKeyTypePassphrase = 0 +usmKeyTypeMaster = 1 +usmKeyTypeLocalized = 2 + # Auth services authServices = {hmacmd5.HmacMd5.serviceID: hmacmd5.HmacMd5(), hmacsha.HmacSha.serviceID: hmacsha.HmacSha(), @@ -133,7 +138,9 @@ def addV3User(snmpEngine, userName, privProtocol=usmNoPrivProtocol, privKey=None, securityEngineId=None, securityName=None, - # deprecated parameters follow + authKeyType=usmKeyTypePassphrase, + privKeyType=usmKeyTypePassphrase, + # deprecated parameter contextEngineId=None): mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder @@ -141,6 +148,7 @@ def addV3User(snmpEngine, userName, securityName = userName if securityEngineId is None: # backward compatibility securityEngineId = contextEngineId + (snmpEngineID, usmUserEntry, tblIdx1, pysnmpUsmSecretEntry, tblIdx2) = __cookV3UserInfo(snmpEngine, userName, securityEngineId) @@ -162,40 +170,59 @@ def addV3User(snmpEngine, userName, (usmUserEntry.name + (13,) + tblIdx1, 'createAndGo')) ) - # Localize keys - if authProtocol in authServices: - hashedAuthPassphrase = authServices[authProtocol].hashPassphrase( - authKey and authKey or null + if authProtocol not in authServices: + raise error.PySnmpError('Unknown auth protocol %s' % (authProtocol,)) + + if privProtocol not in privServices: + raise error.PySnmpError('Unknown privacy protocol %s' % (privProtocol,)) + + pysnmpUsmKeyType, = mibBuilder.importSymbols('__PYSNMP-USM-MIB', 'pysnmpUsmKeyType') + + authKeyType = pysnmpUsmKeyType.syntax.clone(authKeyType) + + # Localize authentication key unless given + + masterAuthKey = localAuthKey = authKey + + if authKeyType < usmKeyTypeMaster: # master key is not given + masterAuthKey = authServices[authProtocol].hashPassphrase( + authKey or null ) + + if authKeyType < usmKeyTypeLocalized: # localized key is not given localAuthKey = authServices[authProtocol].localizeKey( - hashedAuthPassphrase, snmpEngineID + masterAuthKey, snmpEngineID ) - else: - raise error.PySnmpError('Unknown auth protocol %s' % (authProtocol,)) - if privProtocol in privServices: - hashedPrivPassphrase = privServices[privProtocol].hashPassphrase( - authProtocol, privKey and privKey or null + # Localize privacy key unless given + + masterPrivKey = localPrivKey = privKey + + privKeyType = pysnmpUsmKeyType.syntax.clone(privKeyType) + + if privKeyType < usmKeyTypeMaster: # master key is not given + masterPrivKey = privServices[privProtocol].hashPassphrase( + authProtocol, privKey or null ) + + if privKeyType < usmKeyTypeLocalized: # localized key is not given localPrivKey = privServices[privProtocol].localizeKey( - authProtocol, hashedPrivPassphrase, snmpEngineID + authProtocol, masterPrivKey, snmpEngineID ) - else: - raise error.PySnmpError('Unknown priv protocol %s' % (privProtocol,)) - # Commit localized keys + # Commit master and localized keys snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((pysnmpUsmKeyEntry.name + (1,) + tblIdx1, localAuthKey), (pysnmpUsmKeyEntry.name + (2,) + tblIdx1, localPrivKey), - (pysnmpUsmKeyEntry.name + (3,) + tblIdx1, hashedAuthPassphrase), - (pysnmpUsmKeyEntry.name + (4,) + tblIdx1, hashedPrivPassphrase)) + (pysnmpUsmKeyEntry.name + (3,) + tblIdx1, masterAuthKey), + (pysnmpUsmKeyEntry.name + (4,) + tblIdx1, masterPrivKey)) ) - # Commit passphrases - snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((pysnmpUsmSecretEntry.name + (4,) + tblIdx2, 'destroy'),) ) + + # Commit plain-text pass-phrases snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((pysnmpUsmSecretEntry.name + (1,) + tblIdx2, userName), (pysnmpUsmSecretEntry.name + (2,) + tblIdx2, authKey), diff --git a/pysnmp/hlapi/__init__.py b/pysnmp/hlapi/__init__.py index df0ad3136..8ae16aefb 100644 --- a/pysnmp/hlapi/__init__.py +++ b/pysnmp/hlapi/__init__.py @@ -62,3 +62,13 @@ usmAesBlumenthalCfb256Protocol = auth.usmAesBlumenthalCfb256Protocol """The CFB128-AES-256 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_)""" + +usmKeyTypePassphrase = auth.usmKeyTypePassphrase +"""USM key material type - plain-text pass phrase (:RFC:`3414#section-2.6`)""" + +usmKeyTypeMaster = auth.usmKeyTypeMaster +"""USM key material type - hashed pass-phrase AKA master key (:RFC:`3414#section-2.6`)""" + +usmKeyTypeLocalized = auth.usmKeyTypeLocalized +"""USM key material type - hashed pass-phrase hashed with Context SNMP Engine ID (:RFC:`3414#section-2.6`)""" + diff --git a/pysnmp/hlapi/auth.py b/pysnmp/hlapi/auth.py index 2c729b224..13120a6dc 100644 --- a/pysnmp/hlapi/auth.py +++ b/pysnmp/hlapi/auth.py @@ -34,21 +34,30 @@ class instance. Parameters ---------- - communityIndex: py:class:`str` + communityIndex: :py:class:`str`, :py:class:`~pysnmp.proto.rfc1902.OctetString` Unique index value of a row in snmpCommunityTable. If it is the only positional parameter, it is treated as a *communityName*. - communityName: py:class:`str` + + communityName: :py:class:`str`, :py:class:`~pysnmp.proto.rfc1902.OctetString` SNMP v1/v2c community string. - mpModel: py:class:`int` - SNMP version - 0 for SNMPv1 and 1 for SNMPv2c. - contextEngineId: py:class:`str` + + mpModel: :py:class:`int` + SNMP message processing model AKA SNMP version. Known SNMP versions are: + + * `0` - for SNMP v1 + * `1` - for SNMP v2c (default) + + + contextEngineId: :py:class:`str`, :py:class:`~pysnmp.proto.rfc1902.OctetString` Indicates the location of the context in which management information is accessed when using the community string specified by the above communityName. - contextName: py:class:`str` + + contextName: :py:class:`str`, :py:class:`~pysnmp.proto.rfc1902.OctetString` The context in which management information is accessed when using the above communityName. - tag: py:class:`str` + + tag: :py:class:`str` Arbitrary string that specifies a set of transport endpoints from which a command responder application will accept management requests with given *communityName* or to which @@ -196,6 +205,15 @@ def clone(self, communityIndex=None, communityName=None, usmAesBlumenthalCfb256Protocol = config.usmAesBlumenthalCfb256Protocol """The CFB128-AES-256 Symmetric Encryption Protocol (`draft-blumenthal-aes-usm-04 `_)""" +usmKeyTypePassphrase = config.usmKeyTypePassphrase +"""USM key material type - plain-text pass phrase (:RFC:`3414#section-2.6`)""" + +usmKeyTypeMaster = config.usmKeyTypeMaster +"""USM key material type - hashed pass-phrase AKA master key (:RFC:`3414#section-2.6`)""" + +usmKeyTypeLocalized = config.usmKeyTypeLocalized +"""USM key material type - hashed pass-phrase hashed with Context SNMP Engine ID (:RFC:`3414#section-2.6`)""" + class UsmUserData(object): """Creates SNMP v3 User Security Model (USM) configuration entry. @@ -212,21 +230,26 @@ class instance. Parameters ---------- - userName: py:class:`str` + userName: :py:class:`str`, :py:class:`~pysnmp.proto.rfc1902.OctetString` A human readable string representing the name of the SNMP USM user. - authKey: py:class:`str` + + Other Parameters + ---------------- + authKey: :py:class:`str`, :py:class:`~pysnmp.proto.rfc1902.OctetString` Initial value of the secret authentication key. If not set, :py:class:`~pysnmp.hlapi.usmNoAuthProtocol` is implied. If set and no *authProtocol* is specified, :py:class:`~pysnmp.hlapi.usmHMACMD5AuthProtocol` takes effect. - privKey: py:class:`str` + + privKey: :py:class:`str`, :py:class:`~pysnmp.proto.rfc1902.OctetString` Initial value of the secret encryption key. If not set, :py:class:`~pysnmp.hlapi.usmNoPrivProtocol` is implied. If set and no *privProtocol* is specified, :py:class:`~pysnmp.hlapi.usmDESPrivProtocol` takes effect. - authProtocol: py:class:`tuple` + + authProtocol: :py:class:`tuple`, :py:class:`~pysnmp.proto.rfc1902.ObjectIdentifier` An indication of whether messages sent on behalf of this USM user can be authenticated, and if so, the type of authentication protocol which is used. @@ -240,7 +263,23 @@ class instance. * :py:class:`~pysnmp.hlapi.usmHMAC192SHA256AuthProtocol` * :py:class:`~pysnmp.hlapi.usmHMAC256SHA384AuthProtocol` * :py:class:`~pysnmp.hlapi.usmHMAC384SHA512AuthProtocol` - privProtocol: py:class:`tuple` + + + securityEngineId: :py:class:`~pysnmp.proto.rfc1902.OctetString` + The snmpEngineID of the authoritative SNMP engine to which a + dateRequest message is to be sent. Will be automatically + discovered from peer if not given. + + See :RFC:`3414#section-2.5.1` for technical explanation. + + securityName: :py:class:`str`, :py:class:`~pysnmp.proto.rfc1902.OctetString` + Together with the snmpEngineID it identifies a row in the + *SNMP-USER-BASED-SM-MIB::usmUserTable* that is to be used + for securing the message. + + See :RFC:`3414#section-2.5.1` for technical explanation. + + privProtocol: :py:class:`tuple`, :py:class:`~pysnmp.proto.rfc1902.ObjectIdentifier` An indication of whether messages sent on behalf of this USM user be encrypted, and if so, the type of encryption protocol which is used. @@ -253,6 +292,29 @@ class instance. * :py:class:`~pysnmp.hlapi.usmAesCfb192Protocol` * :py:class:`~pysnmp.hlapi.usmAesCfb256Protocol` + + authKeyType: :py:class:`int` + Type of `authKey` material. See :RFC:`3414#section-2.6` for + technical explanation. + + Supported key types are: + + * :py:class:`~pysnmp.hlapi.usmKeyTypePassphrase` (default) + * :py:class:`~pysnmp.hlapi.usmKeyTypeMaster` + * :py:class:`~pysnmp.hlapi.usmKeyTypeLocalized` + + + privKeyType: :py:class:`int` + Type of `privKey` material. See :RFC:`3414#section-2.6` for + technical explanation. + + Supported key types are: + + * :py:class:`~pysnmp.hlapi.usmKeyTypePassphrase` (default) + * :py:class:`~pysnmp.hlapi.usmKeyTypeMaster` + * :py:class:`~pysnmp.hlapi.usmKeyTypeLocalized` + + Examples -------- >>> from pysnmp.hlapi import UsmUserData @@ -275,7 +337,9 @@ def __init__(self, userName, authKey=None, privKey=None, authProtocol=None, privProtocol=None, securityEngineId=None, - securityName=None): + securityName=None, + authKeyType=usmKeyTypePassphrase, + privKeyType=usmKeyTypePassphrase): self.userName = userName if securityName is None: self.securityName = userName @@ -302,24 +366,29 @@ def __init__(self, userName, self.privProtocol = privProtocol self.securityEngineId = securityEngineId + self.authKeyType = authKeyType + self.privKeyType = privKeyType def __hash__(self): raise TypeError('%s is not hashable' % self.__class__.__name__) def __repr__(self): - return '%s(userName=%r, authKey=, privKey=, authProtocol=%r, privProtocol=%r, securityEngineId=%r, securityName=%r)' % ( + return '%s(userName=%r, authKey=, privKey=, authProtocol=%r, privProtocol=%r, securityEngineId=%r, securityName=%r, authKeyType=%r, privKeyType=%r)' % ( self.__class__.__name__, self.userName, self.authProtocol, self.privProtocol, self.securityEngineId is None and '' or self.securityEngineId, - self.securityName + self.securityName, + self.authKeyType, + self.privKeyType ) def clone(self, userName=None, authKey=None, privKey=None, authProtocol=None, privProtocol=None, - securityEngineId=None, securityName=None): + securityEngineId=None, securityName=None, + authKeyType=None, privKeyType=None): return self.__class__( userName is None and self.userName or userName, authKey is None and self.authKey or authKey, @@ -327,5 +396,7 @@ def clone(self, userName=None, authProtocol is None and self.authProtocol or authProtocol, privProtocol is None and self.privProtocol or privProtocol, securityEngineId is None and self.securityEngineId or securityEngineId, - securityName=securityName is None and self.securityName or securityName + securityName is None and self.securityName or securityName, + authKeyType is None and self.authKeyType or usmKeyTypePassphrase, + privKeyType is None and self.privKeyType or usmKeyTypePassphrase ) diff --git a/pysnmp/hlapi/lcd.py b/pysnmp/hlapi/lcd.py index 9a188b2e5..4ffee3fd9 100644 --- a/pysnmp/hlapi/lcd.py +++ b/pysnmp/hlapi/lcd.py @@ -58,8 +58,10 @@ def configure(self, snmpEngine, authData, transportTarget, contextName, **option authData.userName, authData.authProtocol, authData.authKey, authData.privProtocol, authData.privKey, - authData.securityEngineId, - securityName=authData.securityName + securityEngineId=authData.securityEngineId, + securityName=authData.securityName, + authKeyType=authData.authKeyType, + privKeyType=authData.privKeyType ) cache['auth'][authDataKey] = authData else: diff --git a/pysnmp/smi/mibs/PYSNMP-USM-MIB.py b/pysnmp/smi/mibs/PYSNMP-USM-MIB.py index b48e3da8c..304c79178 100644 --- a/pysnmp/smi/mibs/PYSNMP-USM-MIB.py +++ b/pysnmp/smi/mibs/PYSNMP-USM-MIB.py @@ -34,6 +34,9 @@ pysnmpUsmDiscovery = MibScalar((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 1, 2), Integer32().subtype(subtypeSpec=ConstraintsUnion(SingleValueConstraint(0, 1))).clone(namedValues=NamedValues(("doNotDiscover", 0), ("doDiscover", 1))).clone('doDiscover')).setMaxAccess("readwrite") if mibBuilder.loadTexts: pysnmpUsmDiscovery.setStatus('current') if mibBuilder.loadTexts: pysnmpUsmDiscovery.setDescription('Whether SNMP engine would try to figure out the EngineIDs of its peers by sending discover requests.') +pysnmpUsmKeyType = MibScalar((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 1, 3), Integer32().subtype(subtypeSpec=ConstraintsUnion(SingleValueConstraint(0, 2))).clone(namedValues=NamedValues(("passphrase", 0), ("master", 1), ("localized", 2))).clone('passphrase')).setMaxAccess("not-accessible") +if mibBuilder.loadTexts: pysnmpUsmKeyType.setStatus('current') +if mibBuilder.loadTexts: pysnmpUsmKeyType.setDescription('When configuring USM user, the value of this enumeration determines how the keys should be treated. The default value "passphrase" means that given keys are plain-text pass-phrases, "master" indicates that the keys are pre-hashed pass-phrases, while "localized" stands for pre-hashed pass-phrases mixed with SNMP Security Engine ID value.') pysnmpUsmUser = MibIdentifier((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 3)) pysnmpUsmSecretTable = MibTable((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 2), ) if mibBuilder.loadTexts: pysnmpUsmSecretTable.setStatus('current') @@ -75,4 +78,4 @@ if mibBuilder.loadTexts: pysnmpUsmKeyPriv.setDescription("User's non-localized key used for encryption.") pysnmpUsmMIBCompliances = MibIdentifier((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 2, 1)) pysnmpUsmMIBGroups = MibIdentifier((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 2, 2)) -mibBuilder.exportSymbols("PYSNMP-USM-MIB", pysnmpUsmCfg=pysnmpUsmCfg, pysnmpUsmDiscoverable=pysnmpUsmDiscoverable, pysnmpUsmKeyEntry=pysnmpUsmKeyEntry, pysnmpUsmKeyTable=pysnmpUsmKeyTable, pysnmpUsmKeyPrivLocalized=pysnmpUsmKeyPrivLocalized, pysnmpUsmMIBCompliances=pysnmpUsmMIBCompliances, pysnmpUsmMIBObjects=pysnmpUsmMIBObjects, pysnmpUsmSecretTable=pysnmpUsmSecretTable, PYSNMP_MODULE_ID=pysnmpUsmMIB, pysnmpUsmSecretEntry=pysnmpUsmSecretEntry, pysnmpUsmMIBConformance=pysnmpUsmMIBConformance, pysnmpUsmUser=pysnmpUsmUser, pysnmpUsmKeyAuth=pysnmpUsmKeyAuth, pysnmpUsmSecretPrivKey=pysnmpUsmSecretPrivKey, pysnmpUsmKeyAuthLocalized=pysnmpUsmKeyAuthLocalized, pysnmpUsmMIB=pysnmpUsmMIB, pysnmpUsmDiscovery=pysnmpUsmDiscovery, pysnmpUsmSecretUserName=pysnmpUsmSecretUserName, pysnmpUsmKeyPriv=pysnmpUsmKeyPriv, pysnmpUsmSecretAuthKey=pysnmpUsmSecretAuthKey, pysnmpUsmSecretStatus=pysnmpUsmSecretStatus, pysnmpUsmMIBGroups=pysnmpUsmMIBGroups) +mibBuilder.exportSymbols("PYSNMP-USM-MIB", pysnmpUsmCfg=pysnmpUsmCfg, pysnmpUsmDiscoverable=pysnmpUsmDiscoverable, pysnmpUsmKeyType=pysnmpUsmKeyType, pysnmpUsmKeyEntry=pysnmpUsmKeyEntry, pysnmpUsmKeyTable=pysnmpUsmKeyTable, pysnmpUsmKeyPrivLocalized=pysnmpUsmKeyPrivLocalized, pysnmpUsmMIBCompliances=pysnmpUsmMIBCompliances, pysnmpUsmMIBObjects=pysnmpUsmMIBObjects, pysnmpUsmSecretTable=pysnmpUsmSecretTable, PYSNMP_MODULE_ID=pysnmpUsmMIB, pysnmpUsmSecretEntry=pysnmpUsmSecretEntry, pysnmpUsmMIBConformance=pysnmpUsmMIBConformance, pysnmpUsmUser=pysnmpUsmUser, pysnmpUsmKeyAuth=pysnmpUsmKeyAuth, pysnmpUsmSecretPrivKey=pysnmpUsmSecretPrivKey, pysnmpUsmKeyAuthLocalized=pysnmpUsmKeyAuthLocalized, pysnmpUsmMIB=pysnmpUsmMIB, pysnmpUsmDiscovery=pysnmpUsmDiscovery, pysnmpUsmSecretUserName=pysnmpUsmSecretUserName, pysnmpUsmKeyPriv=pysnmpUsmKeyPriv, pysnmpUsmSecretAuthKey=pysnmpUsmSecretAuthKey, pysnmpUsmSecretStatus=pysnmpUsmSecretStatus, pysnmpUsmMIBGroups=pysnmpUsmMIBGroups) diff --git a/pysnmp/smi/mibs/instances/__PYSNMP-USM-MIB.py b/pysnmp/smi/mibs/instances/__PYSNMP-USM-MIB.py index 2a83fc377..551e65c23 100644 --- a/pysnmp/smi/mibs/instances/__PYSNMP-USM-MIB.py +++ b/pysnmp/smi/mibs/instances/__PYSNMP-USM-MIB.py @@ -7,17 +7,21 @@ MibScalarInstance, = mibBuilder.importSymbols('SNMPv2-SMI', 'MibScalarInstance') (pysnmpUsmDiscoverable, - pysnmpUsmDiscovery) = mibBuilder.importSymbols( + pysnmpUsmDiscovery, + pysnmpUsmKeyType) = mibBuilder.importSymbols( 'PYSNMP-USM-MIB', 'pysnmpUsmDiscoverable', - 'pysnmpUsmDiscovery' + 'pysnmpUsmDiscovery', + 'pysnmpUsmKeyType' ) __pysnmpUsmDiscoverable = MibScalarInstance(pysnmpUsmDiscoverable.name, (0,), pysnmpUsmDiscoverable.syntax) __pysnmpUsmDiscovery = MibScalarInstance(pysnmpUsmDiscovery.name, (0,), pysnmpUsmDiscovery.syntax) +__pysnmpUsmKeyType = MibScalarInstance(pysnmpUsmKeyType.name, (0,), pysnmpUsmKeyType.syntax) mibBuilder.exportSymbols( "__PYSNMP-USM-MIB", pysnmpUsmDiscoverable=__pysnmpUsmDiscoverable, - pysnmpUsmDiscovery=__pysnmpUsmDiscovery + pysnmpUsmDiscovery=__pysnmpUsmDiscovery, + pysnmpUsmKeyType=__pysnmpUsmKeyType ) From cd06c6f23ae6201e72a8666dbc47be086e96d3a5 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 4 Aug 2019 10:25:24 +0200 Subject: [PATCH 089/102] Add missing enumeration to `pysnmpUemKeyType` --- pysnmp/smi/mibs/PYSNMP-USM-MIB.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pysnmp/smi/mibs/PYSNMP-USM-MIB.py b/pysnmp/smi/mibs/PYSNMP-USM-MIB.py index 304c79178..b66802daf 100644 --- a/pysnmp/smi/mibs/PYSNMP-USM-MIB.py +++ b/pysnmp/smi/mibs/PYSNMP-USM-MIB.py @@ -34,7 +34,7 @@ pysnmpUsmDiscovery = MibScalar((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 1, 2), Integer32().subtype(subtypeSpec=ConstraintsUnion(SingleValueConstraint(0, 1))).clone(namedValues=NamedValues(("doNotDiscover", 0), ("doDiscover", 1))).clone('doDiscover')).setMaxAccess("readwrite") if mibBuilder.loadTexts: pysnmpUsmDiscovery.setStatus('current') if mibBuilder.loadTexts: pysnmpUsmDiscovery.setDescription('Whether SNMP engine would try to figure out the EngineIDs of its peers by sending discover requests.') -pysnmpUsmKeyType = MibScalar((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 1, 3), Integer32().subtype(subtypeSpec=ConstraintsUnion(SingleValueConstraint(0, 2))).clone(namedValues=NamedValues(("passphrase", 0), ("master", 1), ("localized", 2))).clone('passphrase')).setMaxAccess("not-accessible") +pysnmpUsmKeyType = MibScalar((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 1, 3), Integer32().subtype(subtypeSpec=ConstraintsUnion(SingleValueConstraint(0, 1, 2))).clone(namedValues=NamedValues(("passphrase", 0), ("master", 1), ("localized", 2))).clone('passphrase')).setMaxAccess("not-accessible") if mibBuilder.loadTexts: pysnmpUsmKeyType.setStatus('current') if mibBuilder.loadTexts: pysnmpUsmKeyType.setDescription('When configuring USM user, the value of this enumeration determines how the keys should be treated. The default value "passphrase" means that given keys are plain-text pass-phrases, "master" indicates that the keys are pre-hashed pass-phrases, while "localized" stands for pre-hashed pass-phrases mixed with SNMP Security Engine ID value.') pysnmpUsmUser = MibIdentifier((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 3)) From 060428091bd7ecebb0be293252071c8654184c5f Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 4 Aug 2019 12:16:27 +0200 Subject: [PATCH 090/102] Do not store incomplete USM keys and improve USM debugging This adds details debugging on USM initial configuration process and runtime USM user cloning. Besides that, this patch eliminates storing of incomplete USM keys (in case when master/localized keys are configured directly). On top of that, this commit fixes a bug in USM configuration which did not allow the same user names to be added under different security names. --- CHANGES.txt | 3 + pysnmp/entity/config.py | 120 ++++++++++++++++++------ pysnmp/proto/secmod/rfc3414/localkey.py | 6 +- pysnmp/proto/secmod/rfc3414/service.py | 81 ++++++++++++++-- 4 files changed, 172 insertions(+), 38 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 884600944..e486cc165 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,6 +3,9 @@ Revision 4.4.11, released 2019-08-XX ------------------------------------ - Added SNMPv3 USM master and localized keys support to LCD configuration +- Improved initial and runtime USM debugging +- Fixed a bug in USM configuration which did not allow the same user names + to be added under different security names Revision 4.4.10, released 2019-07-29 ------------------------------------ diff --git a/pysnmp/entity/config.py b/pysnmp/entity/config.py index 9087c00f7..5c5b5c8b7 100644 --- a/pysnmp/entity/config.py +++ b/pysnmp/entity/config.py @@ -14,6 +14,7 @@ from pysnmp.proto import rfc1902 from pysnmp.proto import rfc1905 from pysnmp import error +from pysnmp import debug # A shortcut to popular constants @@ -91,13 +92,15 @@ def addV1System(snmpEngine, communityIndex, communityName, if contextName is None: contextName = null + securityName = securityName is not None and securityName or communityIndex + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((snmpCommunityEntry.name + (8,) + tblIdx, 'destroy'),) ) snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((snmpCommunityEntry.name + (1,) + tblIdx, communityIndex), (snmpCommunityEntry.name + (2,) + tblIdx, communityName), - (snmpCommunityEntry.name + (3,) + tblIdx, securityName is not None and securityName or communityIndex), + (snmpCommunityEntry.name + (3,) + tblIdx, securityName), (snmpCommunityEntry.name + (4,) + tblIdx, contextEngineId), (snmpCommunityEntry.name + (5,) + tblIdx, contextName), (snmpCommunityEntry.name + (6,) + tblIdx, transportTag), @@ -105,6 +108,13 @@ def addV1System(snmpEngine, communityIndex, communityName, (snmpCommunityEntry.name + (8,) + tblIdx, 'createAndGo')) ) + debug.logger & debug.flagSM and debug.logger( + 'addV1System: added new table entry ' + 'communityIndex "%s" communityName "%s" securityName "%s" ' + 'contextEngineId "%s" contextName "%s" transportTag ' + '"%s"' % (communityIndex, communityName, securityName, + contextEngineId, contextName, transportTag)) + def delV1System(snmpEngine, communityIndex): (snmpCommunityEntry, tblIdx, @@ -113,6 +123,10 @@ def delV1System(snmpEngine, communityIndex): ((snmpCommunityEntry.name + (8,) + tblIdx, 'destroy'),) ) + debug.logger & debug.flagSM and debug.logger( + 'delV1System: deleted table entry by communityIndex ' + '"%s"' % (communityIndex,)) + def __cookV3UserInfo(snmpEngine, securityName, securityEngineId): mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder @@ -120,17 +134,17 @@ def __cookV3UserInfo(snmpEngine, securityName, securityEngineId): snmpEngineID, = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID') if securityEngineId is None: - snmpEngineID = snmpEngineID.syntax + securityEngineId = snmpEngineID.syntax else: - snmpEngineID = snmpEngineID.syntax.clone(securityEngineId) + securityEngineId = snmpEngineID.syntax.clone(securityEngineId) usmUserEntry, = mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmUserEntry') - tblIdx1 = usmUserEntry.getInstIdFromIndices(snmpEngineID, securityName) + tblIdx1 = usmUserEntry.getInstIdFromIndices(securityEngineId, securityName) pysnmpUsmSecretEntry, = mibBuilder.importSymbols('PYSNMP-USM-MIB', 'pysnmpUsmSecretEntry') tblIdx2 = pysnmpUsmSecretEntry.getInstIdFromIndices(securityName) - return snmpEngineID, usmUserEntry, tblIdx1, pysnmpUsmSecretEntry, tblIdx2 + return securityEngineId, usmUserEntry, tblIdx1, pysnmpUsmSecretEntry, tblIdx2 def addV3User(snmpEngine, userName, @@ -142,15 +156,18 @@ def addV3User(snmpEngine, userName, privKeyType=usmKeyTypePassphrase, # deprecated parameter contextEngineId=None): + mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder if securityName is None: securityName = userName + if securityEngineId is None: # backward compatibility securityEngineId = contextEngineId - (snmpEngineID, usmUserEntry, tblIdx1, - pysnmpUsmSecretEntry, tblIdx2) = __cookV3UserInfo(snmpEngine, userName, securityEngineId) + (securityEngineId, usmUserEntry, tblIdx1, + pysnmpUsmSecretEntry, tblIdx2) = __cookV3UserInfo( + snmpEngine, securityName, securityEngineId) # Load augmenting table before creating new row in base one pysnmpUsmKeyEntry, = mibBuilder.importSymbols('PYSNMP-USM-MIB', 'pysnmpUsmKeyEntry') @@ -182,54 +199,93 @@ def addV3User(snmpEngine, userName, # Localize authentication key unless given - masterAuthKey = localAuthKey = authKey + masterAuthKey = localAuthKey = authKey = rfc1902.OctetString(authKey or null) - if authKeyType < usmKeyTypeMaster: # master key is not given + if authKeyType < usmKeyTypeMaster: # pass phrase is given masterAuthKey = authServices[authProtocol].hashPassphrase( authKey or null ) - if authKeyType < usmKeyTypeLocalized: # localized key is not given + if authKeyType < usmKeyTypeLocalized: # pass phrase or master key is given localAuthKey = authServices[authProtocol].localizeKey( - masterAuthKey, snmpEngineID + masterAuthKey, securityEngineId ) # Localize privacy key unless given - masterPrivKey = localPrivKey = privKey - privKeyType = pysnmpUsmKeyType.syntax.clone(privKeyType) - if privKeyType < usmKeyTypeMaster: # master key is not given + masterPrivKey = localPrivKey = privKey = rfc1902.OctetString(privKey or null) + + if privKeyType < usmKeyTypeMaster: # pass phrase is given masterPrivKey = privServices[privProtocol].hashPassphrase( authProtocol, privKey or null ) - if privKeyType < usmKeyTypeLocalized: # localized key is not given + if privKeyType < usmKeyTypeLocalized: # pass phrase or master key is given localPrivKey = privServices[privProtocol].localizeKey( - authProtocol, masterPrivKey, snmpEngineID + authProtocol, masterPrivKey, securityEngineId ) - # Commit master and localized keys + # Commit only the keys we have + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( - ((pysnmpUsmKeyEntry.name + (1,) + tblIdx1, localAuthKey), - (pysnmpUsmKeyEntry.name + (2,) + tblIdx1, localPrivKey), - (pysnmpUsmKeyEntry.name + (3,) + tblIdx1, masterAuthKey), - (pysnmpUsmKeyEntry.name + (4,) + tblIdx1, masterPrivKey)) + ((pysnmpUsmKeyEntry.name + (1,) + tblIdx1, localAuthKey),) ) + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( + ((pysnmpUsmKeyEntry.name + (2,) + tblIdx1, localPrivKey),) + ) + + if authKeyType < usmKeyTypeLocalized: + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( + ((pysnmpUsmKeyEntry.name + (3,) + tblIdx1, masterAuthKey),) + ) + + if privKeyType < usmKeyTypeLocalized: + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( + ((pysnmpUsmKeyEntry.name + (4,) + tblIdx1, masterPrivKey),) + ) + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((pysnmpUsmSecretEntry.name + (4,) + tblIdx2, 'destroy'),) ) - # Commit plain-text pass-phrases + # Commit plain-text pass-phrases if we have them + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( - ((pysnmpUsmSecretEntry.name + (1,) + tblIdx2, userName), - (pysnmpUsmSecretEntry.name + (2,) + tblIdx2, authKey), - (pysnmpUsmSecretEntry.name + (3,) + tblIdx2, privKey), - (pysnmpUsmSecretEntry.name + (4,) + tblIdx2, 'createAndGo')) + ((pysnmpUsmSecretEntry.name + (4,) + tblIdx2, 'createAndGo'),) ) + if authKeyType < usmKeyTypeMaster: + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( + ((pysnmpUsmSecretEntry.name + (1,) + tblIdx2, userName), + (pysnmpUsmSecretEntry.name + (2,) + tblIdx2, authKey)) + ) + + if privKeyType < usmKeyTypeMaster: + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( + ((pysnmpUsmSecretEntry.name + (1,) + tblIdx2, userName), + (pysnmpUsmSecretEntry.name + (3,) + tblIdx2, privKey)) + ) + + debug.logger & debug.flagSM and debug.logger( + 'addV3User: added new table entries ' + 'userName "%s" securityName "%s" authProtocol %s ' + 'privProtocol %s localAuthKey "%s" localPrivKey "%s" ' + 'masterAuthKey "%s" masterPrivKey "%s" authKey "%s" ' + 'privKey "%s" by index securityName "%s" securityEngineId ' + '"%s"' % ( + userName, securityName, authProtocol, privProtocol, + localAuthKey.prettyPrint(), + localPrivKey.prettyPrint(), + masterAuthKey.prettyPrint(), + masterPrivKey.prettyPrint(), + authKey.prettyPrint(), + privKey.prettyPrint(), + securityName, + securityEngineId and securityEngineId.prettyPrint())) + def delV3User(snmpEngine, userName, @@ -238,21 +294,31 @@ def delV3User(snmpEngine, contextEngineId=None): if securityEngineId is None: # backward compatibility securityEngineId = contextEngineId - (snmpEngineID, usmUserEntry, tblIdx1, pysnmpUsmSecretEntry, + (securityEngineId, usmUserEntry, tblIdx1, pysnmpUsmSecretEntry, tblIdx2) = __cookV3UserInfo(snmpEngine, userName, securityEngineId) + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((usmUserEntry.name + (13,) + tblIdx1, 'destroy'),) ) + snmpEngine.msgAndPduDsp.mibInstrumController.writeVars( ((pysnmpUsmSecretEntry.name + (4,) + tblIdx2, 'destroy'),) ) + debug.logger & debug.flagSM and debug.logger( + 'delV3User: deleted table entries by index ' + 'userName "%s" securityEngineId ' + '"%s"' % ( + userName, + securityEngineId.prettyPrint())) + # Drop all derived rows varBinds = initialVarBinds = ( (usmUserEntry.name + (1,), None), # usmUserEngineID (usmUserEntry.name + (2,), None), # usmUserName (usmUserEntry.name + (4,), None) # usmUserCloneFrom ) + while varBinds: varBinds = snmpEngine.msgAndPduDsp.mibInstrumController.readNextVars( varBinds diff --git a/pysnmp/proto/secmod/rfc3414/localkey.py b/pysnmp/proto/secmod/rfc3414/localkey.py index 651722e4a..95d1deb0a 100644 --- a/pysnmp/proto/secmod/rfc3414/localkey.py +++ b/pysnmp/proto/secmod/rfc3414/localkey.py @@ -37,7 +37,8 @@ def hashPassphrase(passphrase, hashFunc): ) mark = e - ringBufferLen count += 1 - return hasher.digest() + digest = hasher.digest() + return univ.OctetString(digest) def passwordToKey(passphrase, snmpEngineId, hashFunc): @@ -47,7 +48,8 @@ def passwordToKey(passphrase, snmpEngineId, hashFunc): def localizeKey(passKey, snmpEngineId, hashFunc): passKey = univ.OctetString(passKey).asOctets() # noinspection PyDeprecation,PyCallingNonCallable - return hashFunc(passKey + snmpEngineId.asOctets() + passKey).digest() + digest = hashFunc(passKey + snmpEngineId.asOctets() + passKey).digest() + return univ.OctetString(digest) # RFC3414: A.2.1 diff --git a/pysnmp/proto/secmod/rfc3414/service.py b/pysnmp/proto/secmod/rfc3414/service.py index 545ed874c..4ef7d7485 100644 --- a/pysnmp/proto/secmod/rfc3414/service.py +++ b/pysnmp/proto/secmod/rfc3414/service.py @@ -250,8 +250,27 @@ def __generateRequestOrResponseMsg(self, snmpEngine, usmUserPrivKeyLocalized = cachedSecurityData['usmUserPrivKeyLocalized'] else: usmUserPrivKeyLocalized = None + securityEngineID = snmpEngineID - debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: user info read from cache') + + debug.logger & debug.flagSM and debug.logger( + '__generateRequestOrResponseMsg: using cached USM user entry ' + 'usmUserName "%s" ' + 'usmUserSecurityName "%s" ' + 'usmUserAuthProtocol "%s" ' + 'usmUserAuthKeyLocalized "%s" ' + 'usmUserPrivProtocol "%s" ' + 'usmUserPrivKeyLocalized "%s" for ' + 'securityEngineID "%s" and securityName "%s" found by ' + 'securityStateReference "%s" ' % ( + usmUserName, usmUserSecurityName, + usmUserAuthProtocol, + usmUserAuthKeyLocalized.prettyPrint(), + usmUserPrivProtocol, + usmUserPrivKeyLocalized.prettyPrint(), + securityEngineID.prettyPrint(), + securityName, securityStateReference)) + elif securityName: # 3.1.1b try: @@ -262,7 +281,23 @@ def __generateRequestOrResponseMsg(self, snmpEngine, securityEngineID, self.__sec2usr(snmpEngine, securityName, securityEngineID) ) - debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: read user info') + + debug.logger & debug.flagSM and debug.logger( + '__generateRequestOrResponseMsg: found USM user entry ' + 'usmUserName "%s" ' + 'usmUserSecurityName "%s" ' + 'usmUserAuthProtocol "%s" ' + 'usmUserAuthKeyLocalized "%s" ' + 'usmUserPrivProtocol "%s" ' + 'usmUserPrivKeyLocalized "%s" by ' + 'securityEngineID "%s" and securityName "%s"' % ( + usmUserName, usmUserSecurityName, + usmUserAuthProtocol, + usmUserAuthKeyLocalized.prettyPrint(), + usmUserPrivProtocol, + usmUserPrivKeyLocalized.prettyPrint(), + securityEngineID.prettyPrint(), + securityName)) except NoSuchInstanceError: pysnmpUsmDiscovery, = mibBuilder.importSymbols('__PYSNMP-USM-MIB', 'pysnmpUsmDiscovery') @@ -278,7 +313,28 @@ def __generateRequestOrResponseMsg(self, snmpEngine, self.__sec2usr(snmpEngine, securityName) ) + debug.logger & debug.flagSM and debug.logger( + '__generateRequestOrResponseMsg: cloned USM user entry ' + 'usmUserName "%s" ' + 'usmUserSecurityName "%s" ' + 'usmUserAuthProtocol "%s" ' + 'usmUserAuthKeyLocalized "%s" ' + 'usmUserPrivProtocol "%s" ' + 'usmUserPrivKeyLocalized "%s" for ' + 'securityEngineID "%s" and securityName "%s"' % ( + usmUserName, usmUserSecurityName, + usmUserAuthProtocol, + usmUserAuthKeyLocalized.prettyPrint(), + usmUserPrivProtocol, + usmUserPrivKeyLocalized.prettyPrint(), + securityEngineID.prettyPrint(), securityName)) + except NoSuchInstanceError: + debug.logger & debug.flagSM and debug.logger( + '__generateRequestOrResponseMsg: failed to clone ' + 'USM user for securityEngineID "%s" securityName ' + '"%s"' % (securityEngineID, securityName)) + reportUnknownName = True if reportUnknownName: @@ -286,8 +342,6 @@ def __generateRequestOrResponseMsg(self, snmpEngine, errorIndication=errind.unknownSecurityName ) - debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: clone user info') - except PyAsn1Error: debug.logger & debug.flagSM and debug.logger( '__generateRequestOrResponseMsg: %s' % (sys.exc_info()[1],)) @@ -296,18 +350,27 @@ def __generateRequestOrResponseMsg(self, snmpEngine, raise error.StatusInformation( errorIndication=errind.invalidMsg ) + else: # empty username used for engineID discovery usmUserName = usmUserSecurityName = null usmUserAuthProtocol = noauth.NoAuth.serviceID usmUserPrivProtocol = nopriv.NoPriv.serviceID usmUserAuthKeyLocalized = usmUserPrivKeyLocalized = None - debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: use empty USM data') - # noinspection PyUnboundLocalVariable - debug.logger & debug.flagSM and debug.logger( - '__generateRequestOrResponseMsg: local usmUserName %r usmUserSecurityName %r usmUserAuthProtocol %s usmUserPrivProtocol %s securityEngineID %r securityName %r' % ( - usmUserName, usmUserSecurityName, usmUserAuthProtocol, usmUserPrivProtocol, securityEngineID, securityName)) + debug.logger & debug.flagSM and debug.logger( + '__generateRequestOrResponseMsg: using blank USM info ' + 'usmUserName "%s" ' + 'usmUserSecurityName "%s" ' + 'usmUserAuthProtocol "%s" ' + 'usmUserAuthKeyLocalized "%s" ' + 'usmUserPrivProtocol "%s" ' + 'usmUserPrivKeyLocalized "%s" for ' + 'securityEngineID "%s" and securityName "%s"' % ( + usmUserName, usmUserSecurityName, + usmUserAuthProtocol, usmUserAuthKeyLocalized, + usmUserPrivProtocol, usmUserPrivKeyLocalized, + securityEngineID.prettyPrint(), securityName)) msg = globalData From 6066897cd142b6954fa9f643780e4e488a8369a5 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 4 Aug 2019 12:40:02 +0200 Subject: [PATCH 091/102] Fix USM configuration for the case of partial keys --- pysnmp/entity/config.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/pysnmp/entity/config.py b/pysnmp/entity/config.py index 5c5b5c8b7..4ff5895e8 100644 --- a/pysnmp/entity/config.py +++ b/pysnmp/entity/config.py @@ -199,7 +199,9 @@ def addV3User(snmpEngine, userName, # Localize authentication key unless given - masterAuthKey = localAuthKey = authKey = rfc1902.OctetString(authKey or null) + authKey = authKey and rfc1902.OctetString(authKey) + + masterAuthKey = localAuthKey = authKey if authKeyType < usmKeyTypeMaster: # pass phrase is given masterAuthKey = authServices[authProtocol].hashPassphrase( @@ -215,7 +217,9 @@ def addV3User(snmpEngine, userName, privKeyType = pysnmpUsmKeyType.syntax.clone(privKeyType) - masterPrivKey = localPrivKey = privKey = rfc1902.OctetString(privKey or null) + privKey = privKey and rfc1902.OctetString(privKey) + + masterPrivKey = localPrivKey = privKey if privKeyType < usmKeyTypeMaster: # pass phrase is given masterPrivKey = privServices[privProtocol].hashPassphrase( @@ -277,14 +281,14 @@ def addV3User(snmpEngine, userName, 'privKey "%s" by index securityName "%s" securityEngineId ' '"%s"' % ( userName, securityName, authProtocol, privProtocol, - localAuthKey.prettyPrint(), - localPrivKey.prettyPrint(), - masterAuthKey.prettyPrint(), - masterPrivKey.prettyPrint(), - authKey.prettyPrint(), - privKey.prettyPrint(), + localAuthKey and localAuthKey.prettyPrint(), + localPrivKey and localPrivKey.prettyPrint(), + masterAuthKey and masterAuthKey.prettyPrint(), + masterPrivKey and masterPrivKey.prettyPrint(), + authKey and authKey.prettyPrint(), + privKey and privKey.prettyPrint(), securityName, - securityEngineId and securityEngineId.prettyPrint())) + securityEngineId.prettyPrint())) def delV3User(snmpEngine, From 89d14d560a00f2ac1ab7228f00e0edbeedb31808 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Mon, 5 Aug 2019 10:28:00 +0200 Subject: [PATCH 092/102] Fix crashing VACM debugging --- pysnmp/proto/secmod/rfc3414/service.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pysnmp/proto/secmod/rfc3414/service.py b/pysnmp/proto/secmod/rfc3414/service.py index 4ef7d7485..ab125aa70 100644 --- a/pysnmp/proto/secmod/rfc3414/service.py +++ b/pysnmp/proto/secmod/rfc3414/service.py @@ -265,9 +265,9 @@ def __generateRequestOrResponseMsg(self, snmpEngine, 'securityStateReference "%s" ' % ( usmUserName, usmUserSecurityName, usmUserAuthProtocol, - usmUserAuthKeyLocalized.prettyPrint(), + usmUserAuthKeyLocalized and usmUserAuthKeyLocalized.prettyPrint(), usmUserPrivProtocol, - usmUserPrivKeyLocalized.prettyPrint(), + usmUserPrivKeyLocalized and usmUserPrivKeyLocalized.prettyPrint(), securityEngineID.prettyPrint(), securityName, securityStateReference)) @@ -293,9 +293,9 @@ def __generateRequestOrResponseMsg(self, snmpEngine, 'securityEngineID "%s" and securityName "%s"' % ( usmUserName, usmUserSecurityName, usmUserAuthProtocol, - usmUserAuthKeyLocalized.prettyPrint(), + usmUserAuthKeyLocalized and usmUserAuthKeyLocalized.prettyPrint(), usmUserPrivProtocol, - usmUserPrivKeyLocalized.prettyPrint(), + usmUserPrivKeyLocalized and usmUserPrivKeyLocalized.prettyPrint(), securityEngineID.prettyPrint(), securityName)) @@ -324,9 +324,9 @@ def __generateRequestOrResponseMsg(self, snmpEngine, 'securityEngineID "%s" and securityName "%s"' % ( usmUserName, usmUserSecurityName, usmUserAuthProtocol, - usmUserAuthKeyLocalized.prettyPrint(), + usmUserAuthKeyLocalized and usmUserAuthKeyLocalized.prettyPrint(), usmUserPrivProtocol, - usmUserPrivKeyLocalized.prettyPrint(), + usmUserPrivKeyLocalized and usmUserPrivKeyLocalized.prettyPrint(), securityEngineID.prettyPrint(), securityName)) except NoSuchInstanceError: From c351e90391b7772c3d93e63b64d479cae5f44f6b Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Tue, 6 Aug 2019 22:56:33 +0200 Subject: [PATCH 093/102] Move most of SNMP engine discovery code to security module This SNMP engine ID discovery procedure is spread across message processing and security modules. This is weird! Anyway, this change moves SNMP message rewriting, associated with starting out SNMP discovery sequence, to security module. The motivation is to let security module making the ultimate decision whether or not SNMP engine discovery is required. For example, if localized keys are committed directly to the DB, security module may just use them without engine discovery phase. --- pysnmp/hlapi/auth.py | 8 ++++- pysnmp/proto/mpmod/rfc3412.py | 25 +++----------- pysnmp/proto/secmod/rfc3414/service.py | 46 ++++++++++++++++++++++---- 3 files changed, 50 insertions(+), 29 deletions(-) diff --git a/pysnmp/hlapi/auth.py b/pysnmp/hlapi/auth.py index 13120a6dc..e784eec8e 100644 --- a/pysnmp/hlapi/auth.py +++ b/pysnmp/hlapi/auth.py @@ -268,7 +268,9 @@ class instance. securityEngineId: :py:class:`~pysnmp.proto.rfc1902.OctetString` The snmpEngineID of the authoritative SNMP engine to which a dateRequest message is to be sent. Will be automatically - discovered from peer if not given. + discovered from peer if not given, unless localized keys + are used. In the latter case *securityEngineId* must be + specified. See :RFC:`3414#section-2.5.1` for technical explanation. @@ -304,6 +306,10 @@ class instance. * :py:class:`~pysnmp.hlapi.usmKeyTypeLocalized` + If `~pysnmp.hlapi.usmKeyTypeLocalized` is used, peer SNMP engine ID + discovery mechanism can't be leveraged for key localization, so + *securityEngineId* must be given by local configuration. + privKeyType: :py:class:`int` Type of `privKey` material. See :RFC:`3414#section-2.6` for technical explanation. diff --git a/pysnmp/proto/mpmod/rfc3412.py b/pysnmp/proto/mpmod/rfc3412.py index 58f3acc08..bfee07f92 100644 --- a/pysnmp/proto/mpmod/rfc3412.py +++ b/pysnmp/proto/mpmod/rfc3412.py @@ -203,31 +203,14 @@ def prepareOutgoingMessage(self, snmpEngine, transportDomain, # 7.1.9.a if pdu.tagSet in rfc3411.unconfirmedClassPDUs: securityEngineId = snmpEngineID + else: if peerSnmpEngineData is None: - # Force engineID discovery (rfc3414, 4) - securityEngineId = securityName = self._emptyStr - securityLevel = 1 - # Clear possible auth&priv flags - headerData.setComponentByPosition( - 2, self._msgFlags[msgFlags & 0xfc], verifyConstraints=False, matchTags=False, matchConstraints=False - ) - # XXX - scopedPDU = self.__scopedPDU - scopedPDU.setComponentByPosition( - 0, self._emptyStr, verifyConstraints=False, matchTags=False, matchConstraints=False - ) - scopedPDU.setComponentByPosition(1, contextName) - scopedPDU.setComponentByPosition(2) + debug.logger & debug.flagMP and debug.logger( + 'prepareOutgoingMessage: peer SNMP engine is not known') - # Use dead-empty PDU for engine-discovery report - emptyPdu = pdu.clone() - pMod.apiPDU.setDefaults(emptyPdu) + securityEngineId = None - scopedPDU.getComponentByPosition(2).setComponentByType( - emptyPdu.tagSet, emptyPdu, verifyConstraints=False, matchTags=False, matchConstraints=False - ) - debug.logger & debug.flagMP and debug.logger('prepareOutgoingMessage: force engineID discovery') else: securityEngineId = peerSnmpEngineData['securityEngineId'] diff --git a/pysnmp/proto/secmod/rfc3414/service.py b/pysnmp/proto/secmod/rfc3414/service.py index ab125aa70..244e9324b 100644 --- a/pysnmp/proto/secmod/rfc3414/service.py +++ b/pysnmp/proto/secmod/rfc3414/service.py @@ -13,13 +13,16 @@ from pysnmp.proto.secmod.rfc7860.auth import hmacsha2 from pysnmp.proto.secmod.eso.priv import des3, aes192, aes256 from pysnmp.smi.error import NoSuchInstanceError -from pysnmp.proto import rfc1155, errind, error +from pysnmp.proto import api, rfc1155, errind, error from pysnmp import debug from pyasn1.type import univ, namedtype, constraint from pyasn1.codec.ber import encoder, decoder, eoo from pyasn1.error import PyAsn1Error from pyasn1.compat.octets import null +# API to rfc1905 protocol objects +pMod = api.protoModules[api.protoVersion2c] + # USM security params @@ -224,6 +227,7 @@ def __generateRequestOrResponseMsg(self, snmpEngine, scopedPDU, securityStateReference): mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder snmpEngineID = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')[0].syntax + msg = globalData # 3.1.1 if securityStateReference is not None: @@ -271,7 +275,7 @@ def __generateRequestOrResponseMsg(self, snmpEngine, securityEngineID.prettyPrint(), securityName, securityStateReference)) - elif securityName: + elif securityEngineID: # 3.1.1b try: (usmUserName, usmUserSecurityName, usmUserAuthProtocol, @@ -352,14 +356,43 @@ def __generateRequestOrResponseMsg(self, snmpEngine, ) else: - # empty username used for engineID discovery + # 4. (start SNMP engine ID discovery) + securityEngineID = securityName = null + securityLevel = 1 + + scopedPDU.setComponentByPosition( + 0, null, verifyConstraints=False, + matchTags=False, matchConstraints=False) + + headerData = msg.getComponentByPosition(1) + + # Clear possible auth&priv flags + headerData.setComponentByPosition( + 2, univ.OctetString(hexValue='00'), verifyConstraints=False, + matchTags=False, matchConstraints=False + ) + + emptyPdu = scopedPDU.getComponentByPosition(2).getComponent() + + # we edit the rest of the structures in-place because they + # are ours for as long as this stack lasts, however PDU + # is more persistent and should not be touched + + emptyPdu = emptyPdu.clone() + pMod.apiPDU.setDefaults(emptyPdu) + + scopedPDU.getComponentByPosition(2).setComponentByType( + emptyPdu.tagSet, emptyPdu, verifyConstraints=False, + matchTags=False, matchConstraints=False) + usmUserName = usmUserSecurityName = null usmUserAuthProtocol = noauth.NoAuth.serviceID usmUserPrivProtocol = nopriv.NoPriv.serviceID usmUserAuthKeyLocalized = usmUserPrivKeyLocalized = None debug.logger & debug.flagSM and debug.logger( - '__generateRequestOrResponseMsg: using blank USM info ' + '__generateRequestOrResponseMsg: using blank USM info for peer ' + 'SNMP engine ID discovery ' 'usmUserName "%s" ' 'usmUserSecurityName "%s" ' 'usmUserAuthProtocol "%s" ' @@ -370,9 +403,8 @@ def __generateRequestOrResponseMsg(self, snmpEngine, usmUserName, usmUserSecurityName, usmUserAuthProtocol, usmUserAuthKeyLocalized, usmUserPrivProtocol, usmUserPrivKeyLocalized, - securityEngineID.prettyPrint(), securityName)) - - msg = globalData + securityEngineID and securityEngineID.prettyPrint(), + securityName)) # 3.1.2 if securityLevel == 3: From 6049b0cfc4b34ded72da44a8c97c56799c00127c Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 10 Aug 2019 20:08:21 +0200 Subject: [PATCH 094/102] Pin Twisted on Python < 3.5 --- devel-requirements.txt | 8 +++++++- .../v3arch/asyncore/agent/cmdrsp/multiple-usm-users.py | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/devel-requirements.txt b/devel-requirements.txt index d03c9630d..02629f5e9 100644 --- a/devel-requirements.txt +++ b/devel-requirements.txt @@ -1,10 +1,16 @@ Sphinx <= 1.6; python_version < '2.7' Sphinx > 1.6; python_version >= '2.7' trollius; python_version < '3.0' +# NOTE: Twisted < 19.2.1 has a security problem in URL handling. +# However, newer Twisted does not work on older Pythons. That's why +# we have to pin to older Twisted here. +# On the other hand, pysnmp does not use anything HTTP, however other +# dependencies can rely on that. twisted < 15.4; python_version < '2.7' twisted; python_version == '2.7' twisted < 17.9; python_version == '3.0' twisted < 17.9; python_version == '3.1' twisted < 17.9; python_version == '3.2' twisted <= 17.9; python_version == '3.3' -twisted; python_version >= '3.4' +twisted <= 17.9; python_version == '3.4' +twisted; python_version >= '3.5' diff --git a/examples/v3arch/asyncore/agent/cmdrsp/multiple-usm-users.py b/examples/v3arch/asyncore/agent/cmdrsp/multiple-usm-users.py index 3b1119dd1..9d1454ce3 100644 --- a/examples/v3arch/asyncore/agent/cmdrsp/multiple-usm-users.py +++ b/examples/v3arch/asyncore/agent/cmdrsp/multiple-usm-users.py @@ -32,7 +32,7 @@ config.addTransport( snmpEngine, udp.domainName, - udp.UdpTransport().openServerMode(('127.0.0.1', 161)) + udp.UdpTransport().openServerMode(('127.0.0.1', 1161)) ) # SNMPv3/USM setup From 3488150a26636a7d0395769bdedd8c1b183f1f62 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 11 Aug 2019 00:22:08 +0200 Subject: [PATCH 095/102] Introduce "wildcard" SNMP engine ID (#297) This change introduces "wildcard" SNMP engine ID (0x00000000). Right before deciding on firing up SNMP engine ID discovery and key localization procedure, originating SNMP engine will check for the presence of this magical engine ID (5 zeros), if it is present in LCD along with the user name being used, localized keys from that entry will be used. Does this have security implications? --- pysnmp/hlapi/auth.py | 20 +++++-- pysnmp/proto/secmod/rfc3414/service.py | 83 ++++++++++++++++++-------- 2 files changed, 73 insertions(+), 30 deletions(-) diff --git a/pysnmp/hlapi/auth.py b/pysnmp/hlapi/auth.py index e784eec8e..735d85df7 100644 --- a/pysnmp/hlapi/auth.py +++ b/pysnmp/hlapi/auth.py @@ -305,11 +305,6 @@ class instance. * :py:class:`~pysnmp.hlapi.usmKeyTypeMaster` * :py:class:`~pysnmp.hlapi.usmKeyTypeLocalized` - - If `~pysnmp.hlapi.usmKeyTypeLocalized` is used, peer SNMP engine ID - discovery mechanism can't be leveraged for key localization, so - *securityEngineId* must be given by local configuration. - privKeyType: :py:class:`int` Type of `privKey` material. See :RFC:`3414#section-2.6` for technical explanation. @@ -320,6 +315,21 @@ class instance. * :py:class:`~pysnmp.hlapi.usmKeyTypeMaster` * :py:class:`~pysnmp.hlapi.usmKeyTypeLocalized` + Notes + ----- + If `~pysnmp.hlapi.usmKeyTypeLocalized` is used when running a + non-authoritative SNMP engine, USM key localization mechanism + is not invoked. As a consequence, local SNMP engine configuration + won't get automatically populated with remote SNMP engine's + *securityEngineId*. + + Therefore peer SNMP engine's *securityEngineId* must be added + to local configuration and associated with its localized keys. + + Alternatively, the magic *securityEngineId* value of five zeros + (*0x0000000000*) can be used to refer to the localized keys that + should be used with any unknown remote SNMP engine. This feature + is specific to pysnmp. Examples -------- diff --git a/pysnmp/proto/secmod/rfc3414/service.py b/pysnmp/proto/secmod/rfc3414/service.py index 244e9324b..be2515ec5 100644 --- a/pysnmp/proto/secmod/rfc3414/service.py +++ b/pysnmp/proto/secmod/rfc3414/service.py @@ -59,6 +59,11 @@ class SnmpUSMSecurityModel(AbstractSecurityModel): aes256.Aes256.serviceID: aes256.Aes256(), # non-standard nopriv.NoPriv.serviceID: nopriv.NoPriv()} + # If this, normally impossible, SNMP engine ID is present in LCD, we will use + # its master/localized keys when preparing SNMP message towards any unknown peer + # SNMP engine + wildcardSecurityEngineId = pMod.OctetString(hexValue='0000000000') + def __init__(self): AbstractSecurityModel.__init__(self) self.__securityParametersSpec = UsmSecurityParameters() @@ -278,13 +283,25 @@ def __generateRequestOrResponseMsg(self, snmpEngine, elif securityEngineID: # 3.1.1b try: - (usmUserName, usmUserSecurityName, usmUserAuthProtocol, - usmUserAuthKeyLocalized, usmUserPrivProtocol, - usmUserPrivKeyLocalized) = self.__getUserInfo( - snmpEngine.msgAndPduDsp.mibInstrumController, - securityEngineID, - self.__sec2usr(snmpEngine, securityName, securityEngineID) - ) + try: + (usmUserName, usmUserSecurityName, usmUserAuthProtocol, + usmUserAuthKeyLocalized, usmUserPrivProtocol, + usmUserPrivKeyLocalized) = self.__getUserInfo( + snmpEngine.msgAndPduDsp.mibInstrumController, + securityEngineID, + self.__sec2usr(snmpEngine, securityName, + securityEngineID) + ) + + except NoSuchInstanceError: + (usmUserName, usmUserSecurityName, usmUserAuthProtocol, + usmUserAuthKeyLocalized, usmUserPrivProtocol, + usmUserPrivKeyLocalized) = self.__getUserInfo( + snmpEngine.msgAndPduDsp.mibInstrumController, + self.wildcardSecurityEngineId, + self.__sec2usr(snmpEngine, securityName, + self.wildcardSecurityEngineId) + ) debug.logger & debug.flagSM and debug.logger( '__generateRequestOrResponseMsg: found USM user entry ' @@ -743,28 +760,44 @@ def processIncomingMsg(self, snmpEngine, messageProcessingModel, snmpEngine.msgAndPduDsp.mibInstrumController, msgAuthoritativeEngineId, msgUserName ) - debug.logger & debug.flagSM and debug.logger('processIncomingMsg: read user info from LCD') + debug.logger & debug.flagSM and debug.logger( + 'processIncomingMsg: read user info from LCD') except NoSuchInstanceError: - debug.logger & debug.flagSM and debug.logger( - 'processIncomingMsg: unknown securityEngineID %r msgUserName %r' % ( - msgAuthoritativeEngineId, msgUserName)) + try: + (usmUserName, + usmUserSecurityName, + usmUserAuthProtocol, + usmUserAuthKeyLocalized, + usmUserPrivProtocol, + usmUserPrivKeyLocalized) = self.__getUserInfo( + snmpEngine.msgAndPduDsp.mibInstrumController, + self.wildcardSecurityEngineId, msgUserName + ) + debug.logger & debug.flagSM and debug.logger( + 'processIncomingMsg: read wildcard user info from LCD') - usmStatsUnknownUserNames, = mibBuilder.importSymbols( - '__SNMP-USER-BASED-SM-MIB', 'usmStatsUnknownUserNames') - usmStatsUnknownUserNames.syntax += 1 + except NoSuchInstanceError: - raise error.StatusInformation( - errorIndication=errind.unknownSecurityName, - oid=usmStatsUnknownUserNames.name, - val=usmStatsUnknownUserNames.syntax, - securityStateReference=securityStateReference, - securityLevel=securityLevel, - contextEngineId=contextEngineId, - contextName=contextName, - msgUserName=msgUserName, - maxSizeResponseScopedPDU=maxSizeResponseScopedPDU - ) + debug.logger & debug.flagSM and debug.logger( + 'processIncomingMsg: unknown securityEngineID %r msgUserName %r' % ( + msgAuthoritativeEngineId, msgUserName)) + + usmStatsUnknownUserNames, = mibBuilder.importSymbols( + '__SNMP-USER-BASED-SM-MIB', 'usmStatsUnknownUserNames') + usmStatsUnknownUserNames.syntax += 1 + + raise error.StatusInformation( + errorIndication=errind.unknownSecurityName, + oid=usmStatsUnknownUserNames.name, + val=usmStatsUnknownUserNames.syntax, + securityStateReference=securityStateReference, + securityLevel=securityLevel, + contextEngineId=contextEngineId, + contextName=contextName, + msgUserName=msgUserName, + maxSizeResponseScopedPDU=maxSizeResponseScopedPDU + ) except PyAsn1Error: debug.logger & debug.flagSM and debug.logger('processIncomingMsg: %s' % (sys.exc_info()[1],)) From 8a49b7dc339cae113555cbf149b66940a876df95 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 11 Aug 2019 09:49:14 +0200 Subject: [PATCH 096/102] Fix crash on AES key localization --- pysnmp/proto/secmod/eso/priv/aesbase.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pysnmp/proto/secmod/eso/priv/aesbase.py b/pysnmp/proto/secmod/eso/priv/aesbase.py index 676afbbbc..cb6287337 100644 --- a/pysnmp/proto/secmod/eso/priv/aesbase.py +++ b/pysnmp/proto/secmod/eso/priv/aesbase.py @@ -42,7 +42,8 @@ def localizeKey(self, authProtocol, privKey, snmpEngineID): # now extend this key if too short by repeating steps that includes the hashPassphrase step for count in range(1, int(ceil(self.keySize * 1.0 / len(localPrivKey)))): - localPrivKey += hashAlgo(localPrivKey).digest() + localPrivKey += localPrivKey.clone( + hashAlgo(localPrivKey.asOctets()).digest()) return localPrivKey[:self.keySize] From 13402584ceb9902ec4296e5d572a9c845a3afa53 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 11 Aug 2019 10:12:23 +0200 Subject: [PATCH 097/102] Release 4.4.11 --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index e486cc165..f9bad257e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,5 @@ -Revision 4.4.11, released 2019-08-XX +Revision 4.4.11, released 2019-08-10 ------------------------------------ - Added SNMPv3 USM master and localized keys support to LCD configuration From ba53a102ce6d071ef0faf334ea3ee5b0190e3228 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 11 Aug 2019 12:34:52 +0200 Subject: [PATCH 098/102] Prepare for 4.4.12 --- CHANGES.txt | 5 +++++ pysnmp/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index f9bad257e..1a00e66da 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,9 @@ +Revision 4.4.12, released 2019-08-XX +------------------------------------ + +No changes yet + Revision 4.4.11, released 2019-08-10 ------------------------------------ diff --git a/pysnmp/__init__.py b/pysnmp/__init__.py index 628b0652f..954ad4ed1 100644 --- a/pysnmp/__init__.py +++ b/pysnmp/__init__.py @@ -1,5 +1,5 @@ # http://www.python.org/dev/peps/pep-0396/ -__version__ = '4.4.11' +__version__ = '4.4.12' # backward compatibility version = tuple([int(x) for x in __version__.split('.')]) majorVersionId = version[0] From 5ecc8988d066e957499192a36a686ba7d35876c2 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 11 Aug 2019 12:40:03 +0200 Subject: [PATCH 099/102] Docstring fix --- pysnmp/hlapi/auth.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pysnmp/hlapi/auth.py b/pysnmp/hlapi/auth.py index 735d85df7..f66eb49b6 100644 --- a/pysnmp/hlapi/auth.py +++ b/pysnmp/hlapi/auth.py @@ -317,11 +317,11 @@ class instance. Notes ----- - If `~pysnmp.hlapi.usmKeyTypeLocalized` is used when running a - non-authoritative SNMP engine, USM key localization mechanism - is not invoked. As a consequence, local SNMP engine configuration - won't get automatically populated with remote SNMP engine's - *securityEngineId*. + If :py:class:`~pysnmp.hlapi.usmKeyTypeLocalized` is used when + running a non-authoritative SNMP engine, USM key localization + mechanism is not invoked. As a consequence, local SNMP engine + configuration won't get automatically populated with remote SNMP + engine's *securityEngineId*. Therefore peer SNMP engine's *securityEngineId* must be added to local configuration and associated with its localized keys. From a9964be80f9de7b156610f7d7f2899e9073e37f9 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Tue, 13 Aug 2019 08:30:00 +0200 Subject: [PATCH 100/102] Add examples on master&localized keys use --- .../sync/manager/cmdgen/advanced-topics.rst | 22 +++++++++ .../sync/manager/cmdgen/usm-localized-keys.py | 49 +++++++++++++++++++ .../sync/manager/cmdgen/usm-master-keys.py | 45 +++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 examples/hlapi/asyncore/sync/manager/cmdgen/usm-localized-keys.py create mode 100644 examples/hlapi/asyncore/sync/manager/cmdgen/usm-master-keys.py diff --git a/docs/source/examples/hlapi/asyncore/sync/manager/cmdgen/advanced-topics.rst b/docs/source/examples/hlapi/asyncore/sync/manager/cmdgen/advanced-topics.rst index 9854bc52e..0d64c7665 100644 --- a/docs/source/examples/hlapi/asyncore/sync/manager/cmdgen/advanced-topics.rst +++ b/docs/source/examples/hlapi/asyncore/sync/manager/cmdgen/advanced-topics.rst @@ -70,6 +70,28 @@ Advanced Command Generator :download:`Download` script. +.. include:: /../../examples/hlapi/asyncore/sync/manager/cmdgen/usm-master-keys.py + :start-after: """ + :end-before: """# + +.. literalinclude:: /../../examples/hlapi/asyncore/sync/manager/cmdgen/usm-master-keys.py + :start-after: """# + :language: python + +:download:`Download` script. + + +.. include:: /../../examples/hlapi/asyncore/sync/manager/cmdgen/usm-localized-keys.py + :start-after: """ + :end-before: """# + +.. literalinclude:: /../../examples/hlapi/asyncore/sync/manager/cmdgen/usm-localized-keys.py + :start-after: """# + :language: python + +:download:`Download` script. + + .. include:: /../../examples/hlapi/asyncore/sync/manager/cmdgen/query-agents-from-multuple-threads-over-ipv4-and-ipv6.py :start-after: """ :end-before: """# diff --git a/examples/hlapi/asyncore/sync/manager/cmdgen/usm-localized-keys.py b/examples/hlapi/asyncore/sync/manager/cmdgen/usm-localized-keys.py new file mode 100644 index 000000000..4aba20d99 --- /dev/null +++ b/examples/hlapi/asyncore/sync/manager/cmdgen/usm-localized-keys.py @@ -0,0 +1,49 @@ +""" +SNMPv3: localized auth and privacy keys ++++++++++++++++++++++++++++++++++++++++ + +Send SNMP GET request using the following options: + +* with SNMPv3, user 'usr-md5-des', MD5 authentication, DES encryption +* use localized auth and privacy keys instead of pass-phrase or master keys +* configure authoritative SNMP engine ID (0x0000000000 can be used as well) +* over IPv4/UDP +* to an Agent at demo.snmplabs.com:161 +* for SNMPv2-MIB::sysDescr.0 MIB object instance + +Functionally similar to: + +| $ snmpget -v3 -l authPriv \ + -u usr-md5-des \ + -e 0x80004fb805636c6f75644dab22cc \ + -3k 0x6b99c475259ef7976cf8d028a3381eeb \ + -3K 0x92b5ef98f0a216885e73944e58c07345 \ + demo.snmplabs.com SNMPv2-MIB::sysDescr.0 + +"""# +from pysnmp.hlapi import * + +errorIndication, errorStatus, errorIndex, varBinds = next( + getCmd(SnmpEngine(), + UsmUserData('usr-md5-des', + authKey=OctetString( + hexValue='6b99c475259ef7976cf8d028a3381eeb'), + privKey=OctetString( + hexValue='92b5ef98f0a216885e73944e58c07345'), + authKeyType=usmKeyTypeLocalized, + privKeyType=usmKeyTypeLocalized, + securityEngineId=OctetString( + hexValue='80004fb805636c6f75644dab22cc')), + UdpTransportTarget(('demo.snmplabs.com', 161)), + ContextData(), + ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))) +) + +if errorIndication: + print(errorIndication) +elif errorStatus: + print('%s at %s' % (errorStatus.prettyPrint(), + errorIndex and varBinds[int(errorIndex) - 1][0] or '?')) +else: + for varBind in varBinds: + print(' = '.join([x.prettyPrint() for x in varBind])) diff --git a/examples/hlapi/asyncore/sync/manager/cmdgen/usm-master-keys.py b/examples/hlapi/asyncore/sync/manager/cmdgen/usm-master-keys.py new file mode 100644 index 000000000..a79c6d248 --- /dev/null +++ b/examples/hlapi/asyncore/sync/manager/cmdgen/usm-master-keys.py @@ -0,0 +1,45 @@ +""" +SNMPv3: master auth and privacy keys +++++++++++++++++++++++++++++++++++++ + +Send SNMP GET request using the following options: + +* with SNMPv3, user 'usr-md5-des', MD5 authentication, DES encryption +* use master auth and privacy keys instead of pass-phrase +* over IPv4/UDP +* to an Agent at demo.snmplabs.com:161 +* for SNMPv2-MIB::sysDescr.0 MIB object instance + +Functionally similar to: + +| $ snmpget -v3 -l authPriv \ + -u usr-md5-des \ + -3m 0x1dcf59e86553b3afa5d32fd5d61bf0cf \ + -3M 0xec5ab55e93e1d85cb6846d0f23e845e0 \ + demo.snmplabs.com SNMPv2-MIB::sysDescr.0 + +"""# +from pysnmp.hlapi import * + +errorIndication, errorStatus, errorIndex, varBinds = next( + getCmd(SnmpEngine(), + UsmUserData('usr-md5-des', + authKey=OctetString( + hexValue='1dcf59e86553b3afa5d32fd5d61bf0cf'), + privKey=OctetString( + hexValue='ec5ab55e93e1d85cb6846d0f23e845e0'), + authKeyType=usmKeyTypeMaster, + privKeyType=usmKeyTypeMaster), + UdpTransportTarget(('demo.snmplabs.com', 161)), + ContextData(), + ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))) +) + +if errorIndication: + print(errorIndication) +elif errorStatus: + print('%s at %s' % (errorStatus.prettyPrint(), + errorIndex and varBinds[int(errorIndex) - 1][0] or '?')) +else: + for varBind in varBinds: + print(' = '.join([x.prettyPrint() for x in varBind])) From c5cefab2cee5ae38d093743ef83a08641385b1cc Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Tue, 24 Sep 2019 09:48:00 +0200 Subject: [PATCH 101/102] Fix broken SNMPv3 authoritative engine ID discovery Fixed a regression in SNMPv3 `msgFlag` initialization on authoritative SNMP engine ID discovery. This bug causes secure communication with peer SNMP engines to stall at SNMP engine ID discovery procedure. --- CHANGES.txt | 6 ++++-- pysnmp/proto/mpmod/rfc3412.py | 2 +- pysnmp/proto/secmod/rfc3414/service.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1a00e66da..91679c196 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,8 +1,10 @@ -Revision 4.4.12, released 2019-08-XX +Revision 4.4.12, released 2019-09-XX ------------------------------------ -No changes yet +- Fixed broken SNMPv3 `msgFlag` initialization on authoritative SNMP + engine ID discovery. This bug causes secure communication with peer + SNMP engines to stall at SNMP engine ID discovery procedure. Revision 4.4.11, released 2019-08-10 ------------------------------------ diff --git a/pysnmp/proto/mpmod/rfc3412.py b/pysnmp/proto/mpmod/rfc3412.py index bfee07f92..7c4bec98e 100644 --- a/pysnmp/proto/mpmod/rfc3412.py +++ b/pysnmp/proto/mpmod/rfc3412.py @@ -1,7 +1,7 @@ # # This file is part of pysnmp software. # -# Copyright (c) 2005-2016, Ilya Etingof +# Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pysnmp/license.html # import sys diff --git a/pysnmp/proto/secmod/rfc3414/service.py b/pysnmp/proto/secmod/rfc3414/service.py index be2515ec5..90a90307a 100644 --- a/pysnmp/proto/secmod/rfc3414/service.py +++ b/pysnmp/proto/secmod/rfc3414/service.py @@ -385,7 +385,7 @@ def __generateRequestOrResponseMsg(self, snmpEngine, # Clear possible auth&priv flags headerData.setComponentByPosition( - 2, univ.OctetString(hexValue='00'), verifyConstraints=False, + 2, univ.OctetString(hexValue='04'), verifyConstraints=False, matchTags=False, matchConstraints=False ) From 1dcc0428eafbacd46e987cec0504b12308c810df Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Tue, 24 Sep 2019 18:19:19 +0200 Subject: [PATCH 102/102] Release 4.4.12 --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 91679c196..30dc64863 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,5 @@ -Revision 4.4.12, released 2019-09-XX +Revision 4.4.12, released 2019-09-24 ------------------------------------ - Fixed broken SNMPv3 `msgFlag` initialization on authoritative SNMP