diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d1e92e2e..5beca183 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,11 @@ Current - Fix: issue `#268 `_ - `Full changelog `__ +0.6.6 (04/01/2019) +------------------ + +- Fix: agent cannot start `#302 `__ + 0.6.5 (03/27/2019) ------------------ diff --git a/burpui/engines/agent.py b/burpui/engines/agent.py index 08eab79c..3fc0f444 100644 --- a/burpui/engines/agent.py +++ b/burpui/engines/agent.py @@ -13,6 +13,7 @@ import sys import ssl import json import logging +import time import trio from ..exceptions import BUIserverException @@ -38,6 +39,7 @@ BUI_DEFAULTS = { 'sslkey': '', 'backend': 'burp2', 'password': 'azerty', + 'init_wait': 15, }, } @@ -50,10 +52,11 @@ class BurpHandler(BUIbackend): foreign = BUIbackend.__abstractmethods__ BUIbackend.__abstractmethods__ = frozenset() - def __init__(self, backend='burp2', logger=None, conf=None): + def __init__(self, backend='burp2', logger=None, conf=None, init_wait=0): self.backend_name = backend self.is_async = backend == 'parallel' self.logger = logger + wait = init_wait top = __name__ if '.' in self.backend_name: @@ -70,8 +73,16 @@ class BurpHandler(BUIbackend): else: Client = mod.Burp self.backend = Client(conf=conf) - stats = self.backend.statistics() - if 'alive' not in stats or not stats['alive']: + def __backend_alive(): + stats = self.backend.statistics() + return 'alive' in stats and stats['alive'] + alive = __backend_alive() + while not alive and wait > 0: + self.logger.debug("Waiting for the backend to become alive... {}/{}".format(init_wait - wait, init_wait)) + time.sleep(1) + alive = __backend_alive() + wait -= 1 + if not alive: raise BUIserverException('Cannot talk to burp server') except Exception as exc: self.logger.error('Failed loading backend {}: {}'.format(self.backend_name, str(exc)), exc_info=exc, stack_info=True) @@ -117,9 +128,10 @@ class BUIAgent(BUIbackend): self.sslcert = self.conf.safe_get('sslcert') self.sslkey = self.conf.safe_get('sslkey') self.password = self.conf.safe_get('password') + self.init_wait = self.conf.safe_get('init_wait', 'integer') self.conf.setdefault('BUI_AGENT', True) - self.client = BurpHandler(self.backend, self.logger, self.conf) + self.client = BurpHandler(self.backend, self.logger, self.conf, self.init_wait) def _ssl_context(self): if not self.ssl: diff --git a/burpui/engines/monitor.py b/burpui/engines/monitor.py index 8b22f396..6d9b4649 100644 --- a/burpui/engines/monitor.py +++ b/burpui/engines/monitor.py @@ -206,6 +206,8 @@ class MonitorPool: 'server_version': 'unknown', 'client_version': 'unknown' } + if self.pool.empty(): + await self.fill_pool() while not self.pool.empty(): mon = await self.pool.get() tmp.append(mon) diff --git a/burpui/misc/backend/parallel.py b/burpui/misc/backend/parallel.py index 088404e1..f40fcc2d 100644 --- a/burpui/misc/backend/parallel.py +++ b/burpui/misc/backend/parallel.py @@ -224,9 +224,9 @@ class Burp(Burp2): if self.init_wait: exc = None init_mon = Parallel(conf) - for _ in range(self.init_wait): + for i in range(self.init_wait): try: - self.logger.warning('monitor not ready, waiting for it...') + self.logger.warning('monitor not ready, waiting for it... {}/{}'.format(i, self.init_wait)) trio.run(init_mon.conn) if init_mon.connected: break @@ -237,6 +237,8 @@ class Burp(Burp2): self.logger.error('monitor not ready, giving up!') raise exc del init_mon + stats = self.statistics() + if 'alive' in stats and stats['alive']: self.init_all() def init_all(self): @@ -262,12 +264,18 @@ class Burp(Burp2): def get_client_version(self, agent=None): if self._client_version is None: - self._client_version = trio.run(self._async_request, 'client_version') + try: + self._client_version = trio.run(self._async_request, 'client_version') + except BUIserverException: + return '' return self._client_version or '' def get_server_version(self, agent=None): if self._server_version is None: - self._server_version = trio.run(self._async_request, 'server_version') + try: + self._server_version = trio.run(self._async_request, 'server_version') + except BUIserverException: + return '' return self._server_version or '' async def _async_status(self, query='c:\n', timeout=None, cache=True, agent=None): @@ -285,8 +293,6 @@ class Burp(Burp2): return await async_client.request(func, *args, **kwargs) except (OSError, IOError) as exc: raise BUIserverException(str(exc)) - if not self._ready: - self.init_all() @usetriorun def status(self, query='c:\n', timeout=None, cache=True, agent=None): @@ -827,6 +833,12 @@ class Burp(Burp2): # Make every "Burp" method async class AsyncBurp(Burp): + @property + async def batch_list_supported(self): + if self._batch_list_supported is None: + self._batch_list_supported = json.loads(await self._async_request('batch_list_supported')) + return self._batch_list_supported + # this method must not be async! @implement def statistics(self, agent=None): @@ -877,6 +889,17 @@ class AsyncBurp(Burp): """ return await self._async_get_all_clients() + @implement + async def get_attr(self, name, default=None, agent=None): + """See :func:`burpui.misc.backend.interface.BUIbackend.get_attr`""" + try: + try: + return await getattr(self, name, default) + except TypeError: + return getattr(self, name, default) + except AttributeError: + return default + def __getattribute__(self, name): if name in BUIBACKEND_INTERFACE_METHODS or name in ['_guess_os']: wrap = False diff --git a/burpui/misc/backend/utils/burp2.py b/burpui/misc/backend/utils/burp2.py index b3320553..fe8c9458 100644 --- a/burpui/misc/backend/utils/burp2.py +++ b/burpui/misc/backend/utils/burp2.py @@ -129,14 +129,20 @@ class Monitor(object): @property def server_version(self): if self._server_version is None: - self.status() + try: + self.status() + except BUIserverException: + return '' return self._server_version or '' @property def alive(self): # clients may be idle for some time, in that case we may need to start them again if not self._proc_is_alive(): - self._spawn_burp() + try: + self._spawn_burp() + except OSError: + pass return self._proc_is_alive() def _exit(self): diff --git a/docs/buiagent.rst b/docs/buiagent.rst index cf36f63f..ce6a4062 100644 --- a/docs/buiagent.rst +++ b/docs/buiagent.rst @@ -113,6 +113,9 @@ section as below: version = 1 # agent password password = password + # time to wait at startup + # the burp server must be started before your agent + init_wait = 15 Each option is commented, but here is a more detailed documentation: @@ -125,6 +128,8 @@ Each option is commented, but here is a more detailed documentation: - *version*: What version of `Burp`_ this `bui-agent`_ instance manages. (see `Burp-UI versions `__ for more details) - *password*: The shared secret between the `Burp-UI`_ server and `bui-agent`_. +- *init_wait*: The time to wait for the backend to be alive (this is useful in + case you start every services at the same time). As with `Burp-UI`_, you need a specific ``[Burp]`` section. Please refer to the `Burp-UI versions `__ diff --git a/docs/upgrading.rst b/docs/upgrading.rst index e651d588..13b6dd06 100644 --- a/docs/upgrading.rst +++ b/docs/upgrading.rst @@ -31,6 +31,11 @@ v0.7.0 ``:AUTH`` suffix (so ``BASIC`` becomes ``BASIC:AUTH``, etc.). Please make sure you rename those sections accordingly so you won't be locked out. +- **Breaking** - the ``bui-agent`` will now exit when its *system* requirements + are not met at startup time (that is: the burp-server must be up and running + and the burp-client used by burp-ui must be able to reach the burp-server). + A new timeout has been added though in order for ``bui-agent`` to wait for the + burp-server to be ready. v0.6.0 ------ diff --git a/share/burpui/etc/buiagent.sample.cfg b/share/burpui/etc/buiagent.sample.cfg index 8285f880..40aa9ab4 100644 --- a/share/burpui/etc/buiagent.sample.cfg +++ b/share/burpui/etc/buiagent.sample.cfg @@ -18,6 +18,9 @@ sslkey = /var/lib/burp/ssl/server/ssl_cert-server.key backend = burp2 # agent password password = azerty +# time to wait at startup +# the burp server must be started before your agent +init_wait = 15 [Security] ## This section contains some security options. Make sure you understand the