feat: generic BIP terminal control (Simulator BIP pill) (v3.6.0)
BIP was only reachable through the SCP81 listener; cards that use TCP for
other purposes (an applet's OPEN CHANNEL, a push trigger without HTTP OTA)
now get a generic control surface, independent of SCP81.
Server:
- /api/bip/control starts a plain BIP session in three modes: sink
(default; a local TcpDumpServer that accepts the card's channels and only
logs conn/sink-rx/conn-close, never answering; port 0 = ephemeral, the
bound port is reported), passthru (dial the OPEN CHANNEL destination, TCP
client only) and redirect (fixed host:port).
- /api/bip/status returns {owner, bip, listener}; /api/bip/log and
/api/bip/log-clear share the BIP event log with /api/scp81/log.
- The session state is shared with the SCP81 listener and only one session
runs at a time: _bip_session_stop() stops whichever control started it
and the control responses report it as `replaced` (both directions,
SCP81 <-> BIP). State renamed _SCP81_MODE/_TARGET/_LISTENER/_LINK_EVENTS
-> _BIP_* plus _BIP_OWNER.
- TcpDumpServer logs conn-close and counts accepted connections; stop()
joins the accept thread so the port is really free on restart.
- The new control endpoints are blocked during test-script runs.
PWA:
- New Simulator pill BIP (after TR Config): mode select with notes,
Host/Port rules, Start/Stop with a "replaced" notice, status line (mode,
bound address, owner, channels), a Channels box and the shared BIP log
(reuses the SCP81 log renderer, now showing `peer`).
- The SCP81 status line marks a session owned by the BIP pill.
Help EN/RU 8.10, docs/api.md, AGENTS; CAT_TP/UDP recorded as not
implemented.
Tests: tests/test_bip.py (9) and frontend/tests/bip.test.js (6).
600 frontend / 482 Python green; version 3.6.0; sw simple-v273.
This commit is contained in:
+180
-76
@@ -31,7 +31,7 @@ from osmocom.tlv import BER_TLV_IE
|
||||
from osmocom.utils import rpad
|
||||
|
||||
|
||||
VERSION = '3.5.19'
|
||||
VERSION = '3.6.0'
|
||||
|
||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||
|
||||
@@ -1312,15 +1312,20 @@ PLI_QUALIFIER_NAMES = {
|
||||
_PLI_DATA = {q: '' for q in PLI_QUALIFIER_NAMES}
|
||||
|
||||
_BIP = httpota.BipTerminal()
|
||||
_SCP81_LISTENER = None
|
||||
# Active listener mode: 'dump' | 'tls' | 'redirect' | 'passthru'.
|
||||
# 'redirect' pins one target and has no listener object - the BIP channels
|
||||
# connect straight to the configured external platform (TLS terminated
|
||||
# there). 'passthru' has neither listener nor target: each channel dials the
|
||||
# destination the card requests in OPEN CHANNEL. Mode/target are tracked here
|
||||
# for the status API.
|
||||
_SCP81_MODE = None
|
||||
_SCP81_TARGET = None
|
||||
# Active BIP session, shared by the SCP81 listener and the Simulator's
|
||||
# generic BIP pill - only one can run at a time:
|
||||
# 'tls' | 'dump' SCP81 PSK TLS / capture listener
|
||||
# 'sink' generic BIP local sink (accept + log, never answers)
|
||||
# 'redirect' pinned target, no listener object: every channel connects
|
||||
# to the configured platform (TLS terminated there)
|
||||
# 'passthru' no listener and no target: each channel dials the
|
||||
# destination the card requests in OPEN CHANNEL
|
||||
# _BIP_OWNER records which control started it ('scp81' | 'bip') so both
|
||||
# status views can show who owns the terminal.
|
||||
_BIP_LISTENER = None
|
||||
_BIP_MODE = None
|
||||
_BIP_TARGET = None
|
||||
_BIP_OWNER = None
|
||||
# PSK table of the TLS listener: identity -> key (memory only, never logged or
|
||||
# persisted; the PWA sends it from the card presets at listener start).
|
||||
# _SCP81_PSK_LEGACY keeps a single-key start (psk_hex [+ psk_identity]) so an
|
||||
@@ -2057,32 +2062,34 @@ def _handle_bip_command(scc, cmd_num, cmd_type, cmd_qual, raw, dev_src, dev_dst)
|
||||
return None
|
||||
|
||||
|
||||
def _scp81_listener_status():
|
||||
if not _SCP81_LISTENER:
|
||||
if _SCP81_MODE == 'redirect' and _SCP81_TARGET:
|
||||
return {'mode': 'redirect', 'host': _SCP81_TARGET[0],
|
||||
'port': _SCP81_TARGET[1],
|
||||
'target': '%s:%d' % _SCP81_TARGET}
|
||||
if _SCP81_MODE == 'passthru':
|
||||
def _bip_listener_status():
|
||||
if not _BIP_LISTENER:
|
||||
if _BIP_MODE == 'redirect' and _BIP_TARGET:
|
||||
return {'mode': 'redirect', 'host': _BIP_TARGET[0],
|
||||
'port': _BIP_TARGET[1],
|
||||
'target': '%s:%d' % _BIP_TARGET}
|
||||
if _BIP_MODE == 'passthru':
|
||||
# No listener and no pinned target: every channel dials the
|
||||
# destination the card requests (per-channel targets in the
|
||||
# BIP status).
|
||||
return {'mode': 'passthru'}
|
||||
return None
|
||||
if isinstance(_SCP81_LISTENER, scp81.PskTlsServer):
|
||||
return {'mode': 'tls', 'host': _SCP81_LISTENER.host, 'port': _SCP81_LISTENER.port,
|
||||
'psk_identities': _SCP81_LISTENER.psk_identities,
|
||||
'psk_wildcard': _SCP81_LISTENER.wildcard_psk is not None,
|
||||
'identity_seen': _SCP81_LISTENER.identity_seen,
|
||||
'identity_matched': _SCP81_LISTENER.identity_matched,
|
||||
'version_seen': _SCP81_LISTENER.version_seen,
|
||||
'cipher_seen': _SCP81_LISTENER.cipher_seen,
|
||||
'chunked': _SCP81_LISTENER.chunked,
|
||||
'chunk_size': _SCP81_LISTENER.chunk_size,
|
||||
'compact_headers': _SCP81_LISTENER.compact_headers,
|
||||
'tls_version': _SCP81_LISTENER.tls_version,
|
||||
'cipher': _SCP81_LISTENER.cipher}
|
||||
return {'mode': 'dump', 'host': _SCP81_LISTENER.host, 'port': _SCP81_LISTENER.port}
|
||||
if isinstance(_BIP_LISTENER, scp81.PskTlsServer):
|
||||
return {'mode': 'tls', 'host': _BIP_LISTENER.host, 'port': _BIP_LISTENER.port,
|
||||
'psk_identities': _BIP_LISTENER.psk_identities,
|
||||
'psk_wildcard': _BIP_LISTENER.wildcard_psk is not None,
|
||||
'identity_seen': _BIP_LISTENER.identity_seen,
|
||||
'identity_matched': _BIP_LISTENER.identity_matched,
|
||||
'version_seen': _BIP_LISTENER.version_seen,
|
||||
'cipher_seen': _BIP_LISTENER.cipher_seen,
|
||||
'chunked': _BIP_LISTENER.chunked,
|
||||
'chunk_size': _BIP_LISTENER.chunk_size,
|
||||
'compact_headers': _BIP_LISTENER.compact_headers,
|
||||
'tls_version': _BIP_LISTENER.tls_version,
|
||||
'cipher': _BIP_LISTENER.cipher}
|
||||
return {'mode': _BIP_MODE or 'dump', 'host': _BIP_LISTENER.host,
|
||||
'port': _BIP_LISTENER.port,
|
||||
'connections': _BIP_LISTENER.accepted}
|
||||
|
||||
|
||||
def _bip_data_available(ch):
|
||||
@@ -2206,7 +2213,7 @@ _SCP81_CHUNKED = False
|
||||
# Send automatic Channel status (link dropped) events to the card. Suppress
|
||||
# while testing flows where the terminal closes the connection on purpose:
|
||||
# the card must drain the buffered response and resume on a new connection.
|
||||
_SCP81_LINK_EVENTS = True
|
||||
_BIP_LINK_EVENTS = True
|
||||
|
||||
|
||||
def _ber_len_bytes(n):
|
||||
@@ -2511,14 +2518,14 @@ def _scp81_update_psk_map(body):
|
||||
return {'ok': False, 'error': err}
|
||||
if not table:
|
||||
return {'ok': False, 'error': 'no usable PSK entries (identity + key required)'}
|
||||
if not isinstance(_SCP81_LISTENER, scp81.PskTlsServer):
|
||||
if not isinstance(_BIP_LISTENER, scp81.PskTlsServer):
|
||||
return {'ok': False, 'error': 'PSK TLS listener is not running'}
|
||||
_SCP81_LISTENER.set_psk_map(table)
|
||||
_BIP_LISTENER.set_psk_map(table)
|
||||
_SCP81_PSKS = dict(table)
|
||||
_SCP81_PSK_LEGACY = None
|
||||
_BIP.log('tls-psk-map', identities=sorted(table))
|
||||
return {'ok': True, 'identities': sorted(table),
|
||||
'listener': _scp81_listener_status()}
|
||||
'listener': _bip_listener_status()}
|
||||
|
||||
|
||||
def _scp81_gen_install(body):
|
||||
@@ -2573,33 +2580,113 @@ def _cap_info_body(body):
|
||||
'load_file_bytes': len(loadfile_data) // 2, 'memory': info}
|
||||
|
||||
|
||||
def _bip_session_stop():
|
||||
"""Stop the active BIP session, whichever control started it."""
|
||||
global _BIP_LISTENER, _BIP_MODE, _BIP_TARGET, _BIP_OWNER
|
||||
replaced = None
|
||||
if _BIP_OWNER:
|
||||
replaced = {'owner': _BIP_OWNER, 'mode': _BIP_MODE}
|
||||
if _BIP_LISTENER:
|
||||
_BIP_LISTENER.stop()
|
||||
_BIP_LISTENER = None
|
||||
_BIP_MODE = None
|
||||
_BIP_TARGET = None
|
||||
_BIP_OWNER = None
|
||||
_BIP.disable()
|
||||
return replaced
|
||||
|
||||
|
||||
def _bip_status_body():
|
||||
return {'owner': _BIP_OWNER, 'bip': _BIP.status(),
|
||||
'listener': _bip_listener_status()}
|
||||
|
||||
|
||||
def _bip_control(body):
|
||||
"""Generic BIP terminal control (Simulator -> BIP pill).
|
||||
|
||||
'sink' (default) accepts the card's channels on a local listener and only
|
||||
logs what arrives; 'redirect' forwards every channel to a fixed target;
|
||||
'passthru' dials the destination from the card's OPEN CHANNEL. Starting
|
||||
replaces any running BIP session (an SCP81 listener included) - one BIP
|
||||
session at a time; the response reports what was replaced."""
|
||||
global _BIP_LISTENER, _BIP_MODE, _BIP_TARGET, _BIP_OWNER, _BIP_LINK_EVENTS
|
||||
body = body or {}
|
||||
action = body.get('action', 'start')
|
||||
if action == 'stop':
|
||||
replaced = _bip_session_stop()
|
||||
resp = _bip_status_body()
|
||||
resp.update({'ok': True, 'replaced': replaced})
|
||||
return resp
|
||||
mode = body.get('mode', 'sink')
|
||||
if mode not in ('passthru', 'redirect', 'sink'):
|
||||
return {'ok': False, 'error': 'unsupported mode: %s' % mode}
|
||||
host = (body.get('host') or '').strip() or '127.0.0.1'
|
||||
raw_port = body.get('port')
|
||||
if mode == 'redirect':
|
||||
if not body.get('host') or raw_port in (None, ''):
|
||||
return {'ok': False,
|
||||
'error': 'redirect mode requires the target host and port'}
|
||||
try:
|
||||
port = int(raw_port)
|
||||
except (TypeError, ValueError):
|
||||
return {'ok': False, 'error': 'port is not a number'}
|
||||
if not 0 < port <= 0xFFFF:
|
||||
return {'ok': False, 'error': 'port must be 1-65535'}
|
||||
elif mode == 'sink':
|
||||
try:
|
||||
port = int(raw_port) if raw_port not in (None, '') else 0
|
||||
except (TypeError, ValueError):
|
||||
return {'ok': False, 'error': 'port is not a number'}
|
||||
if not 0 <= port <= 0xFFFF:
|
||||
return {'ok': False, 'error': 'port must be 0-65535 (0 = ephemeral)'}
|
||||
replaced = _bip_session_stop()
|
||||
_BIP_LINK_EVENTS = bool(body.get('link_events', True))
|
||||
_BIP.on_data = _bip_data_available
|
||||
if mode == 'passthru':
|
||||
_BIP_MODE = 'passthru'
|
||||
_BIP_TARGET = None
|
||||
_BIP.enable(mode='passthru')
|
||||
elif mode == 'redirect':
|
||||
_BIP_MODE = 'redirect'
|
||||
_BIP_TARGET = (host, port)
|
||||
_BIP.enable(host, port, mode='redirect')
|
||||
else:
|
||||
_BIP_LISTENER = httpota.TcpDumpServer(
|
||||
host, port,
|
||||
on_rx=lambda peer, data: _BIP.log('sink-rx', peer=peer,
|
||||
bytes=len(data),
|
||||
hex=data.hex().upper()[:2000]),
|
||||
on_log=lambda kind, **fields: _BIP.log(kind, **fields))
|
||||
_BIP_MODE = 'sink'
|
||||
_BIP_TARGET = (_BIP_LISTENER.host, _BIP_LISTENER.port)
|
||||
_BIP.enable(_BIP_LISTENER.host, _BIP_LISTENER.port, mode='redirect')
|
||||
_BIP_OWNER = 'bip'
|
||||
resp = _bip_status_body()
|
||||
resp.update({'ok': True, 'replaced': replaced})
|
||||
return resp
|
||||
|
||||
|
||||
def _scp81_bip_control(body):
|
||||
global _SCP81_LISTENER, _SCP81_PSKS, _SCP81_PSK_LEGACY
|
||||
global _SCP81_MODE, _SCP81_TARGET
|
||||
global _BIP_LISTENER, _SCP81_PSKS, _SCP81_PSK_LEGACY
|
||||
global _BIP_MODE, _BIP_TARGET, _BIP_OWNER
|
||||
global _SCP81_SCRIPT_TEMPLATE, _SCP81_SCRIPT_CR_TAG, _SCP81_NEXT_URI
|
||||
global _SCP81_LINK_EVENTS, _SCP81_TARGETED_APP
|
||||
global _BIP_LINK_EVENTS, _SCP81_TARGETED_APP
|
||||
global _SCP81_CHUNKED
|
||||
body = body or {}
|
||||
action = body.get('action', 'start')
|
||||
if action == 'stop':
|
||||
if _SCP81_LISTENER:
|
||||
_SCP81_LISTENER.stop()
|
||||
_SCP81_LISTENER = None
|
||||
_SCP81_MODE = None
|
||||
_SCP81_TARGET = None
|
||||
_BIP.disable()
|
||||
return {'ok': True, 'bip': _BIP.status(), 'listener': None}
|
||||
replaced = _bip_session_stop()
|
||||
resp = _bip_status_body()
|
||||
resp.update({'ok': True, 'replaced': replaced})
|
||||
return resp
|
||||
host = body.get('host') or '127.0.0.1'
|
||||
port = body.get('port')
|
||||
port = int(port) if port not in (None, '') else 8443
|
||||
mode = body.get('mode', 'dump')
|
||||
if _SCP81_LISTENER:
|
||||
_SCP81_LISTENER.stop()
|
||||
_SCP81_LISTENER = None
|
||||
_BIP.disable()
|
||||
replaced = _bip_session_stop()
|
||||
# Channel status events (TS 102 223 7.5.11) apply to every mode: the
|
||||
# terminal reports BIP link changes it detects outside proactive commands.
|
||||
_SCP81_LINK_EVENTS = bool(body.get('link_events', True))
|
||||
_BIP_LINK_EVENTS = bool(body.get('link_events', True))
|
||||
if mode == 'redirect':
|
||||
# No local listener: the card's BIP channels are redirected straight
|
||||
# to the configured target (e.g. a production HTTP OTA server), which
|
||||
@@ -2608,22 +2695,24 @@ def _scp81_bip_control(body):
|
||||
if not body.get('host') or body.get('port') in (None, ''):
|
||||
return {'ok': False,
|
||||
'error': 'redirect mode requires the target host and port'}
|
||||
_SCP81_MODE = 'redirect'
|
||||
_SCP81_TARGET = (host, port)
|
||||
_BIP_MODE = 'redirect'
|
||||
_BIP_TARGET = (host, port)
|
||||
_BIP_OWNER = 'scp81'
|
||||
_BIP.on_data = _bip_data_available
|
||||
_BIP.enable(host, port, mode='redirect')
|
||||
return {'ok': True, 'bip': _BIP.status(),
|
||||
'listener': _scp81_listener_status()}
|
||||
return {'ok': True, 'replaced': replaced, 'bip': _BIP.status(),
|
||||
'listener': _bip_listener_status()}
|
||||
if mode == 'passthru':
|
||||
# No local listener and no pinned target: every BIP channel dials the
|
||||
# destination the card requests in OPEN CHANNEL (Other address +
|
||||
# Transport level port, TCP client only). Host and port are unused.
|
||||
_SCP81_MODE = 'passthru'
|
||||
_SCP81_TARGET = None
|
||||
_BIP_MODE = 'passthru'
|
||||
_BIP_TARGET = None
|
||||
_BIP_OWNER = 'scp81'
|
||||
_BIP.on_data = _bip_data_available
|
||||
_BIP.enable(mode='passthru')
|
||||
return {'ok': True, 'bip': _BIP.status(),
|
||||
'listener': _scp81_listener_status()}
|
||||
return {'ok': True, 'replaced': replaced, 'bip': _BIP.status(),
|
||||
'listener': _bip_listener_status()}
|
||||
if mode == 'tls':
|
||||
raw_map = body.get('psk_map')
|
||||
table, err = _parse_psk_map(raw_map)
|
||||
@@ -2683,7 +2772,7 @@ def _scp81_bip_control(body):
|
||||
_SCP81_CHUNKED = bool(body.get('chunked', True))
|
||||
cs = body.get('chunk_size')
|
||||
chunk_size = int(cs) if cs not in (None, '') else 0
|
||||
_SCP81_LISTENER = scp81.PskTlsServer(
|
||||
_BIP_LISTENER = scp81.PskTlsServer(
|
||||
host, port, psk, identity=identity, psk_map=(table or None),
|
||||
responder=_scp81_script_responder,
|
||||
chunked=bool(body.get('chunked', True)),
|
||||
@@ -2695,30 +2784,34 @@ def _scp81_bip_control(body):
|
||||
conn_header=(body.get('conn_header') or 'none'),
|
||||
answer_delay=(body.get('answer_delay') or 0),
|
||||
on_log=lambda kind, **fields: _BIP.log(kind, **fields))
|
||||
_SCP81_PSKS = dict(_SCP81_LISTENER.psk_map)
|
||||
_SCP81_PSKS = dict(_BIP_LISTENER.psk_map)
|
||||
_SCP81_PSK_LEGACY = None if table else (psk, identity)
|
||||
_SCP81_MODE = 'tls'
|
||||
_SCP81_TARGET = (_SCP81_LISTENER.host, _SCP81_LISTENER.port)
|
||||
_BIP_MODE = 'tls'
|
||||
_BIP_TARGET = (_BIP_LISTENER.host, _BIP_LISTENER.port)
|
||||
_BIP_OWNER = 'scp81'
|
||||
_BIP.on_data = _bip_data_available
|
||||
_BIP.enable(host, _SCP81_LISTENER.port, mode='redirect')
|
||||
return {'ok': True, 'bip': _BIP.status(), 'listener': _scp81_listener_status(),
|
||||
_BIP.enable(host, _BIP_LISTENER.port, mode='redirect')
|
||||
return {'ok': True, 'replaced': replaced, 'bip': _BIP.status(),
|
||||
'listener': _bip_listener_status(),
|
||||
'script': list(_SCP81_SCRIPT_BASE),
|
||||
'script_kind': _SCP81_SCRIPT_KIND,
|
||||
'script_template': _SCP81_SCRIPT_TEMPLATE,
|
||||
'cr_tag': _SCP81_SCRIPT_CR_TAG, 'link_events': _SCP81_LINK_EVENTS,
|
||||
'cr_tag': _SCP81_SCRIPT_CR_TAG, 'link_events': _BIP_LINK_EVENTS,
|
||||
'targeted_app': _SCP81_TARGETED_APP,
|
||||
'chunked': _SCP81_CHUNKED}
|
||||
if mode != 'dump':
|
||||
return {'ok': False, 'error': 'unsupported mode: %s' % mode}
|
||||
_BIP.on_data = _bip_data_available
|
||||
_SCP81_LISTENER = httpota.TcpDumpServer(
|
||||
_BIP_LISTENER = httpota.TcpDumpServer(
|
||||
host, port,
|
||||
on_rx=lambda peer, data: _BIP.log('dump-rx', peer=peer, bytes=len(data), hex=data.hex().upper()[:2000]),
|
||||
on_log=lambda kind, **fields: _BIP.log(kind, **fields))
|
||||
_SCP81_MODE = 'dump'
|
||||
_SCP81_TARGET = (_SCP81_LISTENER.host, _SCP81_LISTENER.port)
|
||||
_BIP.enable(host, _SCP81_LISTENER.port, mode='redirect')
|
||||
return {'ok': True, 'bip': _BIP.status(), 'listener': _scp81_listener_status()}
|
||||
_BIP_MODE = 'dump'
|
||||
_BIP_TARGET = (_BIP_LISTENER.host, _BIP_LISTENER.port)
|
||||
_BIP_OWNER = 'scp81'
|
||||
_BIP.enable(host, _BIP_LISTENER.port, mode='redirect')
|
||||
return {'ok': True, 'replaced': replaced, 'bip': _BIP.status(),
|
||||
'listener': _bip_listener_status()}
|
||||
|
||||
|
||||
def _build_tr(scc, cmd_num, cmd_type, dev_src, dev_dst, cmd_qual):
|
||||
@@ -3499,7 +3592,7 @@ def _bip_flush_channel_events(scc):
|
||||
global _FLUSHING_CHANNEL_EVENTS
|
||||
if _FLUSHING_CHANNEL_EVENTS:
|
||||
return
|
||||
if not _SCP81_LINK_EVENTS:
|
||||
if not _BIP_LINK_EVENTS:
|
||||
_BIP.take_pending_events()
|
||||
return
|
||||
ev_list = getattr(_server_ref, 'event_list', None) or []
|
||||
@@ -4150,6 +4243,7 @@ _TEST_BLOCKED_PATHS = frozenset([
|
||||
'/api/esim/profiles', '/api/esim/notifications', '/api/esim/profile',
|
||||
'/api/scp81/bip', '/api/scp81/queue', '/api/scp81/psk-map',
|
||||
'/api/scp81/gen-install', '/api/scp81/log-clear',
|
||||
'/api/bip/control', '/api/bip/log-clear',
|
||||
])
|
||||
_TEST_COMMAND_TYPES = {name.upper(): code for code, name in PROACTIVE_TYPE_NAMES.items()}
|
||||
|
||||
@@ -4996,12 +5090,13 @@ class PysimHandler(BaseHTTPRequestHandler):
|
||||
'pending_type': self.server.stk_pending['type'] if self.server.stk_pending else None}
|
||||
self._send_json(resp)
|
||||
self._log_resp(resp)
|
||||
elif self.path == '/api/scp81/status':
|
||||
elif self.path in ('/api/scp81/status', '/api/bip/status'):
|
||||
self._log_req()
|
||||
resp = {'bip': _BIP.status(), 'listener': _scp81_listener_status()}
|
||||
resp = _bip_status_body()
|
||||
self._send_json(resp)
|
||||
self._log_resp(resp)
|
||||
elif self.path == '/api/scp81/log' or self.path.startswith('/api/scp81/log?'):
|
||||
elif (self.path == '/api/scp81/log' or self.path.startswith('/api/scp81/log?')
|
||||
or self.path == '/api/bip/log' or self.path.startswith('/api/bip/log?')):
|
||||
self._log_req()
|
||||
after = 0
|
||||
if '?' in self.path:
|
||||
@@ -6003,6 +6098,15 @@ class PysimHandler(BaseHTTPRequestHandler):
|
||||
resp = {'ok': False, 'error': str(e)}
|
||||
self._send_json(resp)
|
||||
self._log_resp(resp)
|
||||
elif self.path == '/api/bip/control':
|
||||
body = self._read_body()
|
||||
self._log_req(body)
|
||||
try:
|
||||
resp = _bip_control(body)
|
||||
except Exception as e:
|
||||
resp = {'ok': False, 'error': str(e)}
|
||||
self._send_json(resp)
|
||||
self._log_resp(resp)
|
||||
elif self.path == '/api/scp81/psk-map':
|
||||
body = self._read_body()
|
||||
self._log_req(_redact_psk_fields(body))
|
||||
@@ -6019,7 +6123,7 @@ class PysimHandler(BaseHTTPRequestHandler):
|
||||
apdus = [a for a in apdus if a]
|
||||
if not apdus:
|
||||
resp = {'ok': False, 'error': 'no apdus given'}
|
||||
elif _SCP81_LISTENER is None:
|
||||
elif _BIP_LISTENER is None:
|
||||
resp = {'ok': False, 'error': 'SCP81 listener is not running'}
|
||||
else:
|
||||
queued = _scp81_queue_script(
|
||||
@@ -6049,7 +6153,7 @@ class PysimHandler(BaseHTTPRequestHandler):
|
||||
resp = {'ok': False, 'error': str(e)}
|
||||
self._send_json(resp)
|
||||
self._log_resp(resp)
|
||||
elif self.path == '/api/scp81/log-clear':
|
||||
elif self.path in ('/api/scp81/log-clear', '/api/bip/log-clear'):
|
||||
body = self._read_body()
|
||||
self._log_req(body)
|
||||
_BIP.clear_log()
|
||||
|
||||
Reference in New Issue
Block a user