make the agent exit when the backend is in trouble at startup

parent f1b5c53b
Pipeline #1519 passed with stage
in 2 minutes and 28 seconds
......@@ -68,6 +68,9 @@ class BurpHandler(BUIbackend):
mod = __import__(module, fromlist=['Burp'])
Client = mod.Burp
self.backend = Client(conf=conf)
stats = self.backend.statistics()
if 'alive' not in stats or not stats['alive']:
raise BUIserverException('Cannot talk to burp server')
except Exception as exc:
self.logger.error('Failed loading backend {}: {}'.format(self.backend, str(exc)), exc_info=exc, stack_info=True)
sys.exit(2)
......
......@@ -161,7 +161,7 @@ class MonitorPool:
self._status_cache.clear()
self._last_status_cleanup = now
async def receive_all(self, stream: trio.StapledStream, length=1024, bsize=None):
async def receive_all(self, stream: trio.abc.Stream, length=1024, bsize=None):
buf = b''
bsize = bsize if bsize is not None else 1024
bsize = min(bsize, length)
......@@ -172,7 +172,7 @@ class MonitorPool:
if not newbuf:
# 3 successive read failure => raise exception
if tries > 3:
raise Exception('Unable to read full response')
raise IOError('Unable to read full response')
tries += 1
await trio.sleep(0.1)
continue
......@@ -228,6 +228,26 @@ class MonitorPool:
response = getattr(mon, func, '')
if func in ['batch_list_supported']:
response = json.dumps(response)
elif func == 'statistics':
tmp = []
res = {
'alive': False,
'server_version': 'unknown',
'client_version': 'unknown'
}
while not self.pool.empty():
mon = await self.pool.get()
tmp.append(mon)
if mon.alive:
res = {
'alive': True,
'server_version': getattr(mon, 'server_version', ''),
'client_version': getattr(mon, 'client_version', '')
}
break
for mon in tmp:
await self.pool.put(mon)
response = json.dumps(res)
else:
query = req['query']
cache = req.get('cache', True)
......
......@@ -103,7 +103,7 @@ class Burp(BUIbackend):
:param conf: Configuration to use
:type conf: :class:`burpui.config.BUIConfig`
:param dummy: Does not instanciate the object (used for development
:param dummy: Does not instantiate the object (used for development
purpose)
:type dummy: boolean
"""
......@@ -112,7 +112,7 @@ class Burp(BUIbackend):
self.client_version = None
self.server_version = None
super(Burp, self).__init__(server, conf)
BUIbackend.__init__(self, server, conf)
self.parser = Parser(self)
......@@ -137,17 +137,20 @@ class Burp(BUIbackend):
except:
pass
self.logger.info('burp port: {}'.format(self.port))
self.logger.info('burp host: {}'.format(self.host))
self.logger.info('burp binary: {}'.format(self.burpbin))
self.logger.info('strip binary: {}'.format(self.stripbin))
self.logger.info('burp conf cli: {}'.format(self.burpconfcli))
self.logger.info('burp conf srv: {}'.format(self.burpconfsrv))
self.logger.info('tmpdir: {}'.format(self.tmpdir))
self.logger.info('zip64: {}'.format(self.zip64))
self.logger.info('includes: {}'.format(self.includes))
self.logger.info('enforce: {}'.format(self.enforce))
self.logger.info('revoke: {}'.format(self.revoke))
self.logger.info(f'burp port: {self.port}')
self.logger.info(f'burp host: {self.host}')
self.logger.info(f'burp binary: {self.burpbin}')
self.logger.info(f'strip binary: {self.stripbin}')
self.logger.info(f'burp conf cli: {self.burpconfcli}')
self.logger.info(f'burp conf srv: {self.burpconfsrv}')
self.logger.info(f'command timeout: {self.timeout}')
self.logger.info(f'tmpdir: {self.tmpdir}')
self.logger.info(f'zip64: {self.zip64}')
self.logger.info(f'includes: {self.includes}')
self.logger.info(f'enforce: {self.enforce}')
self.logger.info(f'revoke: {self.revoke}')
self.logger.info(f'client version: {self.client_version}')
self.logger.info(f'server version: {self.server_version}')
try:
# make the connection
self.status()
......@@ -205,6 +208,14 @@ class Burp(BUIbackend):
self.logger.error('Cannot guess burp server address')
return False
def statistics(self, agent=None):
"""See :func:`burpui.misc.backend.interface.BUIbackend.statistics`"""
return {
'alive': self._test_burp_server_address(self.host),
'client_version': self.client_version,
'server_version': self.server_version
}
def status(self, query='\n', timeout=None, agent=None):
"""See :func:`burpui.misc.backend.interface.BUIbackend.status`"""
result = []
......@@ -611,7 +622,7 @@ class Burp(BUIbackend):
res['timeleft'] = -1
try:
res['percent'] = round(float(res['bytes']) / float(res['estimated_bytes']) * 100)
except Exception:
except ZeroDivisionError:
# You know... division by 0
res['percent'] = 0
return res
......
......@@ -70,16 +70,16 @@ class Burp(Burp1):
self.batch_list_supported = self.monitor.batch_list_supported
self.logger.info('burp binary: {}'.format(self.burpbin))
self.logger.info('strip binary: {}'.format(self.stripbin))
self.logger.info('burp conf cli: {}'.format(self.burpconfcli))
self.logger.info('burp conf srv: {}'.format(self.burpconfsrv))
self.logger.info('command timeout: {}'.format(self.timeout))
self.logger.info('tmpdir: {}'.format(self.tmpdir))
self.logger.info('zip64: {}'.format(self.zip64))
self.logger.info('includes: {}'.format(self.includes))
self.logger.info('enforce: {}'.format(self.enforce))
self.logger.info('revoke: {}'.format(self.revoke))
self.logger.info(f'burp binary: {self.burpbin}')
self.logger.info(f'strip binary: {self.stripbin}')
self.logger.info(f'burp conf cli: {self.burpconfcli}')
self.logger.info(f'burp conf srv: {self.burpconfsrv}')
self.logger.info(f'command timeout: {self.timeout}')
self.logger.info(f'tmpdir: {self.tmpdir}')
self.logger.info(f'zip64: {self.zip64}')
self.logger.info(f'includes: {self.includes}')
self.logger.info(f'enforce: {self.enforce}')
self.logger.info(f'revoke: {self.revoke}')
self.logger.info(f'client version: {self.client_version}')
self.logger.info(f'server version: {self.server_version}')
......@@ -123,6 +123,14 @@ class Burp(Burp1):
return hur
def statistics(self, agent=None):
"""See :func:`burpui.misc.backend.interface.BUIbackend.statistics`"""
return {
'alive': self.monitor.alive,
'server_version': self.server_version,
'client_version': self.client_version
}
def status(self, query='c:\n', timeout=None, cache=True, agent=None):
"""See :func:`burpui.misc.backend.interface.BUIbackend.status`"""
with self.plock:
......@@ -462,7 +470,7 @@ class Burp(Burp1):
ret['percent'] = round(
float(ret['bytes']) / float(ret['estimated_bytes']) * 100
)
except:
except ZeroDivisionError:
# You know... division by 0
ret['percent'] = 0
......
......@@ -244,6 +244,27 @@ class BUIbackend(object, metaclass=ABCMeta):
Utilities functions
"""
@abstractmethod
def statistics(agent=None):
"""The :func:`burpui.misc.backend.interface.BUIbackend.statistics` method should
return statistics about the current backend.
:param agent: What server to ask (only in multi-agent mode)
:type agent: str
:returns: A dict containing statistics about the backend
:rtype: dict
Example::
{
"alive": true,
"server_version": "2.1.12",
"client_version": "2.1.12"
}
"""
raise NotImplementedError("Sorry, the current Backend does not implement this method!") # pragma: no cover
@abstractmethod
def status(self, query='\n', timeout=None, agent=None):
"""The :func:`burpui.misc.backend.interface.BUIbackend.status` method is
......
......@@ -244,16 +244,24 @@ class Burp(BUIbackend):
if not method:
raise BUIserverException('Wrong method call')
r = {}
res = {}
for name, serv in self.servers.items():
func = getattr(serv, method)
try:
r[name] = func()
res[name] = func()
except BUIserverException:
r[name] = 'Unknown'
res[name] = 'Unknown'
return r
return res
def _get_statistics(self):
"""get statistics"""
res = {}
for name, serv in self.servers.items():
res[name] = serv.statistics()
return res
@implement
def get_parser(self, agent=None):
......@@ -277,6 +285,13 @@ class Burp(BUIbackend):
return self._get_version('get_server_version')
return self.servers[agent].get_server_version()
@implement
def statistics(self, agent=None):
"""See :func:`burpui.misc.backend.interface.BUIbackend.statistics"""
if not agent:
return self._get_statistics()
return self.servers[agent].statistics()
class Gsocket():
def __init__(self, host, port, ssl=False, timeout=5, notimeout=False):
......@@ -485,10 +500,12 @@ class NClient(BUIbackend):
if data['func'] == 'restore_files':
err = str(exc)
elif isinstance(exc, BUIserverException):
raise exc
raise
else:
raise BUIserverException(str(exc))
except Exception as exc:
if isinstance(exc, BUIserverException):
raise
self.logger.error('!!! {} !!!'.format(str(exc)), exc_info=exc)
raise BUIserverException(str(exc))
......
......@@ -226,6 +226,9 @@ class Burp(Burp2):
self._batch_list_supported = json.loads(trio.run(self._async_request, 'batch_list_supported'))
return self._batch_list_supported
def statistics(self, agent=None):
return json.loads(trio.run(self._async_request, 'statistics'))
async def _async_status(self, query='c:\n', timeout=None, cache=True):
async_client = Parallel(self.conf)
try:
......
......@@ -98,15 +98,15 @@ class Monitor(object):
if version < BURP_MINIMAL_VERSION and \
getattr(self.app, 'strict', True):
self.logger.critical(
'Your burp version ({}) does not fit the minimal'
' requirements: {}'.format(version, BURP_MINIMAL_VERSION)
f'Your burp version ({version}) does not fit the minimal'
f' requirements: {BURP_MINIMAL_VERSION}'
)
elif version >= BURP_MINIMAL_VERSION:
self._burp_client_ok = True
except subprocess.CalledProcessError as exp:
if getattr(self.app, 'strict', True):
self.logger.critical(
'Unable to determine your burp version: {}'.format(str(exp))
f'Unable to determine your burp version: {exp}'
)
self.client_version = version.replace('burp-', '')
......@@ -173,7 +173,7 @@ class Monitor(object):
details = ':\n'
out, _ = self.proc.communicate()
details += to_unicode(out)
raise OSError('Unable to spawn burp process{}'.format(details))
raise OSError(f'Unable to spawn burp process{details}')
_, write, _ = select([], [self.proc.stdin], [], self.timeout)
if self.proc.stdin not in write:
self._kill_burp()
......@@ -188,6 +188,10 @@ class Monitor(object):
self.proc.stdin.write(to_bytes('j:response-markers-on\n'))
self._read_proc_stdout(self.timeout, 'j:response-markers-on')
@property
def alive(self):
return self._proc_is_alive()
def _proc_is_alive(self):
"""Check if the burp client process is still alive"""
if self.proc:
......@@ -242,7 +246,7 @@ class Monitor(object):
while True:
try:
if not self._proc_is_alive():
raise Exception('process died while reading its output')
raise OSError('process died while reading its output')
read, _, _ = select([self.proc.stdout], [], [], timeout)
if self.proc.stdout not in read:
raise TimeoutError('Read operation timed out')
......@@ -275,7 +279,7 @@ class Monitor(object):
jso = cache.get('json')
break
tmp = ''
except (TimeoutError, IOError, Exception) as exp:
except (TimeoutError, IOError, OSError) as exp:
# the os throws an exception if there is no data or timeout
self.logger.warning(str(exp))
self._kill_burp()
......@@ -327,12 +331,13 @@ class Monitor(object):
return res
except TimeoutError as exp:
msg = 'Cannot send command: {}'.format(str(exp))
msg = f'Cannot send command: {exp}'
self.logger.error(msg)
self._kill_burp()
raise BUIserverException(msg)
if getattr(self.app, 'strict', True):
raise BUIserverException(msg)
except (OSError, Exception) as exp:
msg = 'Cannot launch burp process: {}'.format(str(exp))
msg = f'Cannot launch burp process: {exp}'
self.logger.error(msg)
if getattr(self.app, 'strict', True):
raise BUIserverException(msg)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment