1# 2# This file is part of snmpclitools software. 3# 4# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com> 5# License: http://snmplabs.com/snmpclitools/license.html 6# 7import socket 8from snmpclitools.cli import base 9from pysnmp.carrier.asynsock.dgram import udp, udp6 10from snmpclitools.error import SnmpApplicationError 11from pysnmp.entity import config 12from pysnmp import error 13 14 15def getUsage(): 16 return """\ 17General communication options 18 -r RETRIES number of retries when sending request 19 -t TIMEOUT request timeout (in seconds) 20Agent address: 21 [<transport-domain>:]<transport-endpoint> 22 transport-domain: (udp|udp6) 23 transport-endpoint: (IP|IPv6|FQDN[:port]) 24""" 25 26# Scanner 27 28 29class TargetScannerMixIn: 30 def t_retries(self, s): 31 r' -r ' 32 self.rv.append(base.ConfigToken('retries')) 33 34 def t_timeout(self, s): 35 r' -t ' 36 self.rv.append(base.ConfigToken('timeout')) 37 38 def t_transport(self, s): 39 r' (udp6)|(udp) ' 40 self.rv.append(base.ConfigToken('transport', s)) 41 42# Parser 43 44 45class TargetParserMixIn: 46 def p_targetSpec(self, args): 47 ''' 48 Option ::= CommOption 49 50 CommOption ::= Retries 51 Retries ::= retries string 52 Retries ::= retries whitespace string 53 54 CommOption ::= Timeout 55 Timeout ::= timeout string 56 Timeout ::= timeout whitespace string 57 58 Agent ::= Transport semicolon Endpoint semicolon Format 59 Agent ::= Transport semicolon Endpoint 60 Agent ::= Endpoint semicolon Format 61 Agent ::= Endpoint 62 63 Transport ::= transport 64 Endpoint ::= string 65 Endpoint ::= lparen IPv6 rparen 66 IPv6 ::= string IPv6 67 IPv6 ::= semicolon IPv6 68 IPv6 ::= 69 Format ::= string 70 ''' 71# Generator 72 73if (hasattr(socket, 'has_ipv6') and socket.has_ipv6 and 74 hasattr(socket, 'getaddrinfo')): 75 _getaddrinfo = socket.getaddrinfo 76else: 77 def _getaddrinfo(a, b, c, d): 78 raise SnmpApplicationError('IPv6 not supported by the system') 79 80 81class __TargetGeneratorPassOne(base.GeneratorTemplate): 82 defPort = '161' 83 _snmpDomainMap = { 84 'udp': (udp.snmpUDPDomain, udp.UdpSocketTransport, lambda h, p: (socket.gethostbyname(h), int(p))), 85 'udp6': (udp6.snmpUDP6Domain, udp6.Udp6SocketTransport, lambda h, p: (_getaddrinfo(h, p, socket.AF_INET6, socket.SOCK_DGRAM)[0][4])) 86 } 87 _snmpDomainNameMap = { 88 2: 'udp', 89 10: 'udp6' 90 } 91 92 def n_Transport(self, cbCtx, node): 93 snmpEngine, ctx = cbCtx 94 if node[0].attr in self._snmpDomainMap: 95 (ctx['transportDomain'], 96 ctx['transportModule'], 97 ctx['addrRewriteFun']) = self._snmpDomainMap[node[0].attr] 98 else: 99 raise error.PySnmpError( 100 'Unsupported transport domain %s' % node[0].attr 101 ) 102 103 def n_Endpoint(self, cbCtx, node): 104 snmpEngine, ctx = cbCtx 105 ctx['transportAddress'] = node[0].attr 106 107 def n_IPv6(self, cbCtx, node): 108 snmpEngine, ctx = cbCtx 109 if not len(node): 110 if 'transportDomain' not in ctx: 111 (ctx['transportDomain'], 112 ctx['transportModule'], 113 ctx['addrRewriteFun']) = self._snmpDomainMap['udp6'] 114 return 115 if ctx.get('transportAddress') is None: 116 ctx['transportAddress'] = '' 117 if node[0] == 'semicolon': 118 ctx['transportAddress'] = ctx['transportAddress'] + ':' 119 else: 120 ctx['transportAddress'] = ctx['transportAddress'] + node[0].attr 121 122 def n_Format(self, cbCtx, node): 123 snmpEngine, ctx = cbCtx 124 ctx['transportFormat'] = node[0].attr 125 126 def n_Agent_exit(self, cbCtx, node): 127 snmpEngine, ctx = cbCtx 128 if 'transportDomain' not in ctx: 129 try: 130 f = _getaddrinfo(ctx['transportAddress'], 0)[0][0] 131 except: 132 f = -1 133 (ctx['transportDomain'], 134 ctx['transportModule'], 135 ctx['addrRewriteFun']) = self._snmpDomainMap[ 136 self._snmpDomainNameMap.get(f, 'udp') 137 ] 138 if 'transportFormat' in ctx: 139 ctx['transportAddress'] = ( 140 ctx['transportAddress'], ctx['transportFormat'] 141 ) 142 del ctx['transportFormat'] 143 else: 144 ctx['transportAddress'] = (ctx['transportAddress'], self.defPort) 145 146 147class __TargetGeneratorTrapPassOne(__TargetGeneratorPassOne): 148 defPort = '162' 149 150 151class __TargetGeneratorPassTwo(base.GeneratorTemplate): 152 def n_Retries(self, cbCtx, node): 153 snmpEngine, ctx = cbCtx 154 try: 155 if len(node) > 2: 156 ctx['retryCount'] = int(node[2].attr) 157 else: 158 ctx['retryCount'] = int(node[1].attr) 159 except ValueError: 160 raise error.PySnmpError('Bad retry value') 161 162 def n_Timeout(self, cbCtx, node): 163 snmpEngine, ctx = cbCtx 164 try: 165 if len(node) > 2: 166 ctx['timeout'] = int(node[2].attr) * 100 167 else: 168 ctx['timeout'] = int(node[1].attr) * 100 169 except: 170 raise error.PySnmpError('Bad timeout value') 171 172 def n_Agent_exit(self, cbCtx, node): 173 snmpEngine, ctx = cbCtx 174 ctx['addrName'] = ctx['paramsName'] 175 config.addTargetAddr( 176 snmpEngine, 177 ctx['addrName'], 178 ctx['transportDomain'], 179 ctx['addrRewriteFun'](*ctx['transportAddress']), 180 ctx['paramsName'], 181 # net-snmp defaults 182 ctx.get('timeout', 100), 183 ctx.get('retryCount', 5), 184 tagList=ctx.get('transportTag', '') 185 ) 186 config.addSocketTransport( 187 snmpEngine, 188 ctx['transportDomain'], 189 ctx['transportModule']().openClientMode() 190 ) 191 192__TargetGeneratorTrapPassTwo = __TargetGeneratorPassTwo 193 194 195def generator(cbCtx, ast): 196 snmpEngine, ctx = cbCtx 197 __TargetGeneratorPassTwo().preorder( 198 __TargetGeneratorPassOne().preorder((snmpEngine, ctx), ast), ast 199 ) 200 201 202def generatorTrap(cbCtx, ast): 203 snmpEngine, ctx = cbCtx 204 __TargetGeneratorTrapPassTwo().preorder( 205 __TargetGeneratorTrapPassOne().preorder((snmpEngine, ctx), ast), ast 206 ) 207