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