def test_multiwalk_non_containment(self): ''' Running a multiwalk should raise an exception if the agent returns OIDs which are not properly increasing. ''' from puresnmp.pdu import GetResponse OID = ObjectIdentifier.from_string # First case: Returned OIDs are the same response = Sequence( Integer(1), OctetString(b'public'), GetResponse(123, [ VarBind(oid=OID('1.2.3'), value=Integer(30)), VarBind(oid=OID('2.3.4'), value=Integer(40)), ])) with patch('puresnmp.api.raw.Transport') as mck: mck().send.side_effect = [to_bytes(response)] mck().get_request_id.return_value = 0 with self.assertRaises(FaultySNMPImplementation): list(multiwalk('::1', 'public', [ '1.2.3', '2.3.4', ])) # Second case: Returned OIDs are smaller response = Sequence( Integer(1), OctetString(b'public'), GetResponse(123, [ VarBind(oid=OID('1.2.2'), value=Integer(30)), VarBind(oid=OID('2.3.3'), value=Integer(40)), ])) with patch('puresnmp.api.raw.Transport') as mck: mck().send.side_effect = [to_bytes(response)] mck().get_request_id.return_value = 0 with self.assertRaises(FaultySNMPImplementation): list(multiwalk('::1', 'public', [ '1.2.3', '2.3.4', ]))
def test_getnext_increasing_oid_strict(self): ''' When running "getnext" we expect a different OID than the one we passed in. If not, this can cause endless-loops in the worst case. Faulty SNMP implementations may behave this way! ''' requested_oid = ObjectIdentifier(1, 2, 3, 4) response_object = Sequence( Integer(1), OctetString(b'public'), GetResponse(234, [VarBind(requested_oid, Integer(123))])) response_bytes = to_bytes(response_object) with patch('puresnmp.api.raw.send') as mck: mck.return_value = response_bytes with self.assertRaises(FaultySNMPImplementation): getnext('::1', 'private', '1.2.3.4')
def test_walk_endless_loop(self): ''' In rare cases, some devices fall into an endless loop by returning the requested OID on a "getnext" call during a "walk" operation. A SNMP client behaving according to the SNMP spec will fall into an endless loop. This test fakes such a case and revents the loop. ''' response_binds = [ VarBind(ObjectIdentifier(1, 2, 3), Integer(123)), VarBind(ObjectIdentifier(1, 2, 4), Integer(124)), VarBind(ObjectIdentifier(1, 2, 5), Integer(125)), VarBind(ObjectIdentifier(1, 2, 5), Integer(125)), # same OID VarBind(ObjectIdentifier(1, 2, 5), Integer(125)), # same OID VarBind(ObjectIdentifier(1, 2, 5), Integer(125)), # same OID ] response_packets = [ Sequence(Integer(1), OctetString(b'public'), GetResponse(234, [bind])) for bind in response_binds ] response_bytes = [to_bytes(packet) for packet in response_packets] handler = CapturingHandler() logger = getLogger('puresnmp') logger.addHandler(handler) with patch('puresnmp.api.raw.Transport') as mck: mck().send.side_effect = response_bytes mck().get_request_id.return_value = 0 result = list(walk('::1', 'private', '1.2', errors='warn')) logger.removeHandler(handler) # The last OID in the mocked responses is decreasing so we want to read # just up to that point. expected = [ VarBind(ObjectIdentifier(1, 2, 3), Integer(123)), VarBind(ObjectIdentifier(1, 2, 4), Integer(124)), VarBind(ObjectIdentifier(1, 2, 5), Integer(125)), ] self.assertEqual(result, expected) # We also want to make sure that we have a proper warning about this handler.assertContains(WARNING, r'.*1.2.5.*')
def test_walk_increasing_oid_lenient(self): ''' We want to be able to allow faulty SNMP implementations to at least try to fetch the values in a walk which are not increasing. It should read up to the values which are no longer increasing and emit a warning. ''' logger = getLogger('puresnmp') handler = CapturingHandler() logger.addHandler(handler) response_binds = [ VarBind(ObjectIdentifier(1, 2, 3), Integer(123)), VarBind(ObjectIdentifier(1, 2, 4), Integer(124)), VarBind(ObjectIdentifier(1, 2, 5), Integer(125)), VarBind(ObjectIdentifier(1, 2, 1), Integer(121)), # non-increasing ] response_packets = [ Sequence(Integer(1), OctetString(b'public'), GetResponse(234, [bind])) for bind in response_binds ] response_bytes = [to_bytes(packet) for packet in response_packets] with patch('puresnmp.api.raw.Transport') as mck: mck().send.side_effect = response_bytes mck().get_request_id.return_value = 0 result = list(walk('::1', 'private', '1.2', errors='warn')) # The last OID in the mocked responses is decreasing so we want to read # just up to that point. expected = [ VarBind(ObjectIdentifier(1, 2, 3), Integer(123)), VarBind(ObjectIdentifier(1, 2, 4), Integer(124)), VarBind(ObjectIdentifier(1, 2, 5), Integer(125)), ] self.assertEqual(result, expected) # We also want to make sure that we have a proper warning about this handler.assertContains(WARNING, r'.*1.2.1.*1.2.5.*') logger.removeHandler(handler)