123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- #!/usr/bin/env python3
- import socket
- import select
- import struct
- import json
- import zlib
- import time
- import re
- import lib.helper
- from lib.ratelimit import rateLimit
- from lib.nodeinfo import Nodeinfo
- from lib.neighbours import Neighbours
- from lib.statistics import Statistics
- class ResponddClient:
- def __init__(self, config):
- self._config = config
- if 'rate_limit' in self._config:
- if 'rate_limit_burst' not in self._config:
- self._config['rate_limit_burst'] = 10
- self.__RateLimit = rateLimit(self._config['rate_limit'], self._config['rate_limit_burst'])
- else:
- self.__RateLimit = None
- self._nodeinfo = Nodeinfo(self._config)
- self._neighbours = Neighbours(self._config)
- self._statistics = Statistics(self._config)
- self._sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
- @staticmethod
- def joinMCAST(sock, addr, ifname):
- group = socket.inet_pton(socket.AF_INET6, addr)
- if_idx = socket.if_nametoindex(ifname)
- sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, group + struct.pack('I', if_idx))
- def start(self):
- self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, bytes(self._config['bridge'], 'UTF-8'))
- self._sock.bind(('::', self._config['port']))
- lines = lib.helper.batctlMeshif([self._config['batman'], 'if'])
- for line in lines:
- lineMatch = re.match(r'^([^:]*)', line)
- self.joinMCAST(self._sock, self._config['addr'], lineMatch.group(1))
- self.joinMCAST(self._sock, self._config['addr'], self._config['bridge'])
- while True:
- msg, sourceAddress = self._sock.recvfrom(2048)
- msgSplit = str(msg, 'UTF-8').split(' ')
- responseStruct = {}
- if msgSplit[0] == 'GET': # multi_request
- for request in msgSplit[1:]:
- responseStruct[request] = self.buildStruct(request)
- self.sendStruct(sourceAddress, responseStruct, True)
- else: # single_request
- responseStruct = self.buildStruct(msgSplit[0])
- self.sendStruct(sourceAddress, responseStruct, False)
- def buildStruct(self, responseType):
- if self.__RateLimit is not None and not self.__RateLimit.limit():
- print('rate limit reached!')
- return
- responseClass = None
- if responseType == 'statistics':
- responseClass = self._statistics
- elif responseType == 'nodeinfo':
- responseClass = self._nodeinfo
- elif responseType == 'neighbours':
- responseClass = self._neighbours
- else:
- print('unknown command: ' + responseType)
- return
- return responseClass.getStruct()
- def sendStruct(self, destAddress, responseStruct, withCompression):
- if self._config['verbose'] or self._config['dry_run']:
- print('%14.3f %35s %5d: ' % (time.time(), destAddress[0], destAddress[1]), end='')
- print(json.dumps(responseStruct, sort_keys=True, indent=4))
- responseData = bytes(json.dumps(responseStruct, separators=(',', ':')), 'UTF-8')
- if withCompression:
- encoder = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15) # The data may be decompressed using zlib and many zlib bindings using -15 as the window size parameter.
- responseData = encoder.compress(responseData)
- responseData += encoder.flush()
- # return compress(str.encode(json.dumps(ret)))[2:-4] # bug? (mesh-announce strip here)
- if not self._config['dry_run']:
- self._sock.sendto(responseData, destAddress)
|