def handle_error(self, *info): exc_info = sys.exc_info() log.msg('%s: error: %s' % (self, exc_info[1])) if exc_info and not isinstance(exc_info[1], socket.error): for line in traceback.format_exception(*exc_info): log.msg(line.replace('\n', ';')) self.handle_close()
def handle_read(self): chunk = self.recv(65535) if not chunk: self.handle_close() self.__input += chunk while self.__input: msgId, contentId, msg, self.__input = protocol.prepareDataElements( self.__input, self.__secret) if msgId is None: if self.__pendingCounter > 5: log.msg( 'incomplete message pending for too long, closing connection with %s' % (self, )) self.close() return else: self.__pendingCounter += 1 return self.__pendingCounter = 0 if contentId == 0: # request self.__dataCbFun(self, msgId, msg) elif contentId == 1: # response if msgId in self.__pendingReqs: cbFun, cbCtx = self.__pendingReqs.pop(msgId) cbFun(msg, cbCtx) else: log.msg('unknown message content-id %s from %s ignored' % (contentId, self))
def handle_error(self, *info): exc_info = sys.exc_info() log.msg('connection with %s broken: %s' % (self.__remoteEndpoint, exc_info[1])) if exc_info and not isinstance(exc_info[1], socket.error): for line in traceback.format_exception(*exc_info): log.msg(line.replace('\n', ';')) self.handle_close()
def __proxyDataCbFun(self, connection, msgId, msg): if connection in self.__runningServersConnMap: trunkId = self.__runningServersConnMap[connection] elif connection in self.__runningClientsConnMap: trunkId = self.__runningClientsConnMap[connection] else: log.msg('data message from unknown connection %s ignored' % connection) return self.__dataCbFun(trunkId, msgId, msg)
def handle_connect(self): if self.isUp: return self.isUp = True if self.__announcementData: self.send(self.__announcementData) self.__announcementData = null log.msg('trunking client %s sent trunk announcement' % self) log.msg('trunk client %s is now connected' % self)
def processCommandResponse(self, pluginId, snmpEngine, pdu, **context): if pluginId not in self.__plugins: log.msg('WARNING: skipping non-existing plugin %s' % pluginId) return NEXT, pdu if 'processCommandResponse' not in self.__plugins[pluginId]: return NEXT, pdu return self.__plugins[pluginId]['processCommandResponse'](pluginId, snmpEngine, pdu, **context)
def handle_accept(self): try: sock, remoteEndpoint = self.accept() except socket.error: log.msg('%s accept() failed: %s' % (self, sys.exc_info()[1])) return log.msg('%s new connection from %s' % (self, ':'.join([str(x) for x in remoteEndpoint]))) TrunkingServer(sock, self.__localEndpoint, remoteEndpoint, self.__secret, self.__dataCbFun, self.__ctlCbFun)
def __ctlCbFun(self, connection, msg=None): if msg: trunkId = str(msg['trunk-id']) if trunkId in self.__runningServersTrunkMap or \ trunkId in self.__runningClientsTrunkMap: log.msg('duplicate trunk %s during negotiation with %s' % (trunkId, connection)) connection.close() return log.msg('registering connection %s as trunk %s' % (connection, trunkId)) self.__runningServersTrunkMap[trunkId] = connection self.__runningServersConnMap[connection] = trunkId else: if connection in self.__runningServersConnMap: trunkId = self.__runningServersConnMap[connection] else: log.msg('control message from unknown connection %s ignored' % connection) return log.msg('unregistering connection %s (trunk %s)' % (self.__runningServersTrunkMap[trunkId], trunkId)) del self.__runningServersTrunkMap[trunkId] del self.__runningServersConnMap[connection]
def handleMgmtOperation(self, snmpEngine, stateReference, contextName, PDU, acInfo): trunkReq = gCurrentRequestContext['request'] logMsg = '(SNMP request %s), matched keys: %s' % (', '.join([ x == 'snmp-pdu' and 'snmp-var-binds=%s' % prettyVarBinds(trunkReq['snmp-pdu']) or '%s=%s' % (x, isinstance(trunkReq[x], int) and trunkReq[x] or rfc1902.OctetString(trunkReq[x]).prettyPrint()) for x in trunkReq ]), ', '.join([ '%s=%s' % (k, gCurrentRequestContext[k]) for k in gCurrentRequestContext if k[-2:] == 'id' ])) usedPlugins = [] pluginIdList = gCurrentRequestContext['plugins-list'] snmpReqInfo = gCurrentRequestContext['request'].copy() for pluginId in pluginIdList: usedPlugins.append((pluginId, snmpReqInfo)) st, PDU = pluginManager.processCommandRequest( pluginId, snmpEngine, PDU, **snmpReqInfo) if st == status.BREAK: break elif st == status.DROP: log.msg('plugin %s muted request %s' % (pluginId, logMsg)) self.releaseStateInformation(stateReference) return elif st == status.RESPOND: log.msg('plugin %s forced immediate response %s' % (pluginId, logMsg)) self.sendPdu(snmpEngine, stateReference, PDU) self.releaseStateInformation(stateReference) return # pass query to trunk trunkReq['snmp-pdu'] = PDU trunkIdList = gCurrentRequestContext['trunk-id-list'] if trunkIdList is None: log.msg('no route configured %s' % logMsg) self.releaseStateInformation(stateReference) return for trunkId in trunkIdList: log.msg('received SNMP message (%s), sending through trunk %s' % (', '.join([ x == 'snmp-pdu' and 'snmp-var-binds=%s' % prettyVarBinds(trunkReq['snmp-pdu']) or '%s=%s' % (x, isinstance(trunkReq[x], int) and trunkReq[x] or rfc1902.OctetString(trunkReq[x]).prettyPrint()) for x in trunkReq ]), trunkId)) cbCtx = usedPlugins, trunkId, trunkReq, snmpEngine, stateReference trunkingManager.sendReq(trunkId, trunkReq, self.__recvCb, cbCtx)
def __init__(self, localEndpoint, secret, dataCbFun, ctlCbFun): self.__localEndpoint = localEndpoint self.__secret = secret self.__dataCbFun = dataCbFun self.__ctlCbFun = ctlCbFun asyncore.dispatcher.__init__(self) try: self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65535) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65535) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.bind(localEndpoint) self.listen(10) except socket.error: raise error.SnmpfwdError('%s socket error: %s' % (self, sys.exc_info()[1])) log.msg('%s: listening...' % self)
def __rspCbFun(snmpEngine, sendRequestHandle, errorIndication, rspPDU, cbCtx): trunkId, msgId, trunkReq = cbCtx trunkRsp = {} if errorIndication: log.msg( 'SNMP error returned for message ID %s received from trunk %s: %s' % (msgId, trunkId, errorIndication)) trunkRsp['error-indication'] = errorIndication trunkRsp['snmp-pdu'] = rspPDU log.msg( 'received SNMP %s message, sending trunk message #%s to trunk %s, original SNMP peer address %s:%s received at %s:%s, var-binds: %s' % (errorIndication and 'error' or 'response', msgId, trunkId, trunkReq['snmp-peer-address'], trunkReq['snmp-peer-port'], trunkReq['snmp-bind-address'], trunkReq['snmp-bind-port'], prettyVarBinds(rspPDU))) trunkingManager.sendRsp(trunkId, msgId, trunkRsp)
def __recvCb(self, trunkRsp, cbCtx): pluginIdList, trunkId, trunkReq, snmpEngine, stateReference = cbCtx if trunkRsp['error-indication']: log.msg( 'received trunk message through trunk %s, remote end reported error-indication %s, NOT sending response to peer address %s:%s from %s:%s' % (trunkId, trunkRsp['error-indication'], trunkReq['snmp-peer-address'], trunkReq['snmp-peer-port'], trunkReq['snmp-bind-address'], trunkReq['snmp-bind-port'])) else: PDU = trunkRsp['snmp-pdu'] for pluginId, snmpReqInfo in pluginIdList: st, PDU = pluginManager.processCommandResponse( pluginId, snmpEngine, PDU, **snmpReqInfo) if st == status.BREAK: break elif st == status.DROP: log.msg( 'received trunk message through trunk %s, snmp-var-binds=%s, plugin %s muted response' % (trunkId, prettyVarBinds(PDU), pluginId)) self.releaseStateInformation(stateReference) return log.msg( 'received trunk message through trunk %s, sending SNMP response to peer address %s:%s from %s:%s, snmp-var-binds=%s' % (trunkId, trunkReq['snmp-peer-address'], trunkReq['snmp-peer-port'], trunkReq['snmp-bind-address'], trunkReq['snmp-bind-port'], prettyVarBinds(PDU))) self.sendPdu(snmpEngine, stateReference, PDU) self.releaseStateInformation(stateReference)
def __init__(self, sock, localEndpoint, remoteEndpoint, secret, dataCbFun, ctlCbFun): self.__localEndpoint = localEndpoint self.__remoteEndpoint = remoteEndpoint self.__secret = secret self.__dataCbFun = dataCbFun self.__ctlCbFun = ctlCbFun self.__pendingReqs = {} self.__pendingCounter = 0 self.__input = null self.socket = None # asyncore strangeness asyncore.dispatcher_with_send.__init__(self, sock) try: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65535) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65535) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) except socket.error: raise error.SnmpfwdError('%s socket error: %s' % (self, sys.exc_info()[1])) else: log.msg('%s: serving new connection...' % (self, ))
def __init__(self, localEndpoint, remoteEndpoint, secret, dataCbFun): self.__localEndpoint = localEndpoint self.__remoteEndpoint = remoteEndpoint self.__secret = secret self.__dataCbFun = dataCbFun self.__pendingReqs = {} self.__pendingCounter = 0 self.__input = null self.__announcementData = null asyncore.dispatcher_with_send.__init__(self) try: self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65535) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65535) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) self.bind(localEndpoint) self.connect(remoteEndpoint) except socket.error: raise error.SnmpfwdError('%s socket error: %s' % (self, sys.exc_info()[1])) log.msg('initiated trunk client connection from %s to %s...' % (localEndpoint, remoteEndpoint))
def handle_close(self): self.isUp = False log.msg('trunk client connection with %s:%s closed' % self.__remoteEndpoint) self.close()
def dataCbFun(trunkId, msgId, msg): log.msg('message ID %s received from trunk %s' % (msgId, trunkId))
except SnmpfwdError: sys.stderr.write('%s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage)) sys.exit(-1) elif opt[0] == '--config-file': cfgFile = opt[1] log.setLogger(programName, 'stderr') try: cfgTree = cparser.Config().load(cfgFile) except SnmpfwdError: sys.stderr.write('ERROR: %s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage)) sys.exit(-1) if cfgTree.getAttrValue('program-name', '', default=None) != programName: log.msg('ERROR: config file %s does not match program name %s' % (cfgFile, programName)) sys.exit(-1) if cfgTree.getAttrValue('config-version', '', default=None) != configVersion: log.msg( 'ERROR: config file %s version is not compatible with program version %s' % (cfgFile, configVersion)) sys.exit(-1) random.seed() # # SNMPv3 CommandGenerator implementation # gCurrentRequestContext = {}
def dataCbFun(trunkId, msgId, msg): k = [ str(x) for x in (msg['snmp-engine-id'], msg['snmp-transport-domain'], msg['snmp-peer-address'] + ':' + str(msg['snmp-peer-port']), msg['snmp-bind-address'] + ':' + str(msg['snmp-bind-port']), msg['snmp-security-model'], msg['snmp-security-level'], msg['snmp-security-name'], msg['snmp-context-engine-id'], msg['snmp-context-name']) ] k.append(snmpPduTypesMap.get(msg['snmp-pdu'].tagSet, '?')) k.append('|'.join( [str(x[0]) for x in v2c.apiPDU.getVarBinds(msg['snmp-pdu'])])) k = '#'.join(k) for x, y in origCredIdList: if y.match(k): origPeerId = macro.expandMacros(x, msg) break else: origPeerId = None errorIndication = None peerIdList = routingMap.get((origPeerId, macro.expandMacros(trunkId, msg))) if not peerIdList: log.msg( 'unroutable trunk message #%s from trunk %s, orig-peer-id %s (original SNMP info: %s)' % (msgId, trunkId, origPeerId or '<none>', ', '.join([ x == 'snmp-pdu' and 'snmp-var-binds=%s' % prettyVarBinds(msg['snmp-pdu']) or '%s=%s' % (x, msg[x].prettyPrint()) for x in msg ]))) errorIndication = 'no route to SNMP peer configured' cbCtx = trunkId, msgId, msg if errorIndication: __rspCbFun(None, None, errorIndication, None, cbCtx) return for peerId in peerIdList: peerId = macro.expandMacros(peerId, msg) snmpEngine, contextEngineId, contextName, \ bindAddr, bindAddrMacro, \ peerAddr, peerAddrMacro = peerIdMap[peerId] if bindAddrMacro: bindAddr = macro.expandMacros(bindAddrMacro, msg), 0 if bindAddr: q.append(bindAddr) if peerAddrMacro: peerAddr = macro.expandMacros(peerAddrMacro, msg), 161 if peerAddr: q.append(peerAddr) log.msg( 'received trunk message #%s from trunk %s, sending SNMP message to peer ID %s, bind-address %s, peer-address %s (original SNMP info: %s)' % (msgId, trunkId, peerId, bindAddr[0] or '<default>', peerAddr[0] or '<default>', ', '.join([ x == 'snmp-pdu' and 'snmp-var-binds=%s' % prettyVarBinds(msg['snmp-pdu']) or '%s=%s' % (x, msg[x].prettyPrint()) for x in msg ]))) commandGenerator.sendPdu(snmpEngine, peerId, macro.expandMacros(contextEngineId, msg), macro.expandMacros(contextName, msg), msg['snmp-pdu'], __rspCbFun, cbCtx)
def loadPlugin(self, pluginId, pluginModuleName, pluginOptions): if pluginId in self.__plugins: raise error.SnmpfwdError('duplicate plugin ID %s' % pluginId) for pluginModulesDir in self.__path: log.msg('scanning "%s" directory for plugin modules...' % pluginModulesDir) if not os.path.exists(pluginModulesDir): log.msg('directory "%s" does not exist' % pluginModulesDir) continue mod = os.path.join(pluginModulesDir, pluginModuleName + '.py') if not os.path.exists(mod): log.msg('Variation module "%s" not found' % mod) continue ctx = { 'modulePath': mod, 'moduleContext': {}, 'moduleOptions': pluginOptions } try: if sys.version_info[0] > 2: exec(compile(open(mod).read(), mod, 'exec'), ctx) else: execfile(mod, ctx) except Exception: raise error.SnmpfwdError( 'plugin module "%s" execution failure: %s' % (mod, sys.exc_info()[1])) else: pluginModule = ctx try: if self.__progId not in pluginModule['hostProgs']: log.msg( 'ignoring plugin module "%s" (unmatched program ID)' % mod) continue if self.__apiVer not in pluginModule['apiVersions']: log.msg( 'ignoring plugin module "%s" (incompatible API version)' % mod) continue except KeyError: log.msg( 'ignoring plugin module "%s" (missing versioning info)' % mod) continue self.__plugins[pluginId] = pluginModule log.msg('plugin module "%s" loaded' % mod) break else: raise error.SnmpfwdError('plugin module "%s" not found' % pluginModuleName)
pluginManager = PluginManager(cfgTree.getAttrValue('plugin-modules-path-list', '', default=[], vector=True), progId=programName, apiVer=pluginApiVersion) for pluginCfgPath in cfgTree.getPathsToAttr('plugin-id'): pluginId = cfgTree.getAttrValue('plugin-id', *pluginCfgPath) pluginMod = cfgTree.getAttrValue('plugin-module', *pluginCfgPath) pluginOptions = cfgTree.getAttrValue('plugin-options', *pluginCfgPath, **dict(default='')) log.msg( 'configuring plugin ID %s (at %s) from module %s with options %s...' % (pluginId, '.'.join(pluginCfgPath), pluginMod, pluginOptions or '<none>')) try: pluginManager.loadPlugin(pluginId, pluginMod, pluginOptions) except SnmpfwdError: log.msg('ERROR: plugin %s not loaded: %s' % (pluginId, sys.exc_info()[1])) sys.exit(-1) for configEntryPath in cfgTree.getPathsToAttr('snmp-credentials-id'): credId = cfgTree.getAttrValue('snmp-credentials-id', *configEntryPath) configKey = [] log.msg('configuring snmp-credentials %s (at %s)...' % (credId, '.'.join(configEntryPath))) engineId = cfgTree.getAttrValue('snmp-engine-id', *configEntryPath)
method = config.get('general', 'method') if method == 'file': rotation = config.get('file', 'rotation') if rotation == 'timed': handler = handlers.TimedRotatingFileHandler( config.get('file', 'destination'), config.get('file', 'timescale'), int(config.get('file', 'interval')), int(config.get('file', 'backupcount'))) else: raise SnmpfwdError('logger: unknown rotation method' % rotation) else: raise SnmpfwdError('logger: unknown logging method' % method) msg('logger: using %s logging method' % method) logger.setLevel(logging.INFO) pdus = config.get('content', 'pdus') for pdu in pdus.split(): try: pduMap[getattr(v2c, pdu + 'PDU').tagSet] = pdu except AttributeError: raise SnmpfwdError('logger: unknown PDU %s' % pdu) else: msg('logger: PDU ACL includes %s' % pdu) handler.setFormatter( logging.Formatter(config.get('content', 'format').replace('-', '_')))
def handle_close(self): log.msg('%s: connection closed' % (self, )) self.__ctlCbFun(self) self.close()