Add LEAP / outleap support

This commit is contained in:
Salad Dais
2022-10-14 06:11:51 +00:00
parent 584d9f11e8
commit 2036e3c5b3
7 changed files with 73 additions and 3 deletions

View File

@@ -0,0 +1,43 @@
"""
Example of how to control a viewer over LEAP
Must launch the viewer with `outleap-agent` LEAP script.
See https://github.com/SaladDais/outleap/ for more info on LEAP / outleap.
"""
import outleap
from outleap.scripts.inspector import LEAPInspectorGUI
from hippolyzer.lib.proxy.addon_utils import send_chat, BaseAddon, show_message
from hippolyzer.lib.proxy.commands import handle_command
from hippolyzer.lib.proxy.region import ProxiedRegion
from hippolyzer.lib.proxy.sessions import Session
# Path found using `outleap-inspector`
FPS_PATH = outleap.UIPath("/main_view/menu_stack/status_bar_container/status/time_and_media_bg/FPSText")
class LEAPExampleAddon(BaseAddon):
@handle_command()
async def show_ui_inspector(self, session: Session, _region: ProxiedRegion):
"""Spawn a GUI for inspecting the UI state"""
if not session.leap_client:
show_message("No LEAP client connected?")
return
LEAPInspectorGUI(session.leap_client).show()
@handle_command()
async def say_fps(self, session: Session, _region: ProxiedRegion):
"""Say your current FPS in chat"""
if not session.leap_client:
show_message("No LEAP client connected?")
return
window_api = outleap.LLWindowAPI(session.leap_client)
fps = (await window_api.get_info(path=FPS_PATH))['value']
send_chat(f"LEAP says I'm running at {fps} FPS!")
addons = [LEAPExampleAddon()]

View File

@@ -9,6 +9,7 @@ from typing import Optional
import mitmproxy.ctx
import mitmproxy.exceptions
import outleap
from hippolyzer.lib.base import llsd
from hippolyzer.lib.proxy.addons import AddonManager
@@ -112,6 +113,7 @@ def start_proxy(session_manager: SessionManager, extra_addons: Optional[list] =
udp_proxy_port = session_manager.settings.SOCKS_PROXY_PORT
http_proxy_port = session_manager.settings.HTTP_PROXY_PORT
leap_port = session_manager.settings.LEAP_PORT
if proxy_host is None:
proxy_host = session_manager.settings.PROXY_BIND_ADDR
@@ -143,6 +145,10 @@ def start_proxy(session_manager: SessionManager, extra_addons: Optional[list] =
coro = asyncio.start_server(server.handle_connection, proxy_host, udp_proxy_port)
async_server = loop.run_until_complete(coro)
leap_server = outleap.LEAPBridgeServer(session_manager.leap_client_connected)
coro = asyncio.start_server(leap_server.handle_connection, proxy_host, leap_port)
async_leap_server = loop.run_until_complete(coro)
event_manager = MITMProxyEventManager(session_manager, flow_context)
loop.create_task(event_manager.run())
@@ -169,6 +175,8 @@ def start_proxy(session_manager: SessionManager, extra_addons: Optional[list] =
# Close the server
print("Closing SOCKS server")
async_server.close()
print("Shutting down LEAP server")
async_leap_server.close()
print("Shutting down addons")
AddonManager.shutdown()
print("Waiting for SOCKS server to close")

View File

@@ -157,7 +157,6 @@ class UDPMessageDeserializer:
reader.seek(current_template.get_msg_freq_num_len() + msg.offset)
for tmpl_block in current_template.blocks:
LOG.debug("Parsing %s:%s" % (msg.name, tmpl_block.name))
# EOF?
if not len(reader):
# Seems like even some "Single" blocks are optional?
@@ -180,7 +179,6 @@ class UDPMessageDeserializer:
for i in range(repeat_count):
current_block = Block(tmpl_block.name)
LOG.debug("Adding block %s" % current_block.name)
msg.add_block(current_block)
for tmpl_variable in tmpl_block.variables:

View File

@@ -9,6 +9,8 @@ import weakref
from typing import *
from weakref import ref
from outleap import LEAPClient
from hippolyzer.lib.base.datatypes import UUID
from hippolyzer.lib.base.helpers import proxify
from hippolyzer.lib.base.message.message import Message
@@ -50,6 +52,7 @@ class Session(BaseClientSession):
self.http_message_handler: MessageHandler[HippoHTTPFlow, str] = MessageHandler()
self.objects = ProxyWorldObjectManager(self, session_manager.settings, session_manager.name_cache)
self.inventory = ProxyInventoryManager(proxify(self))
self.leap_client: Optional[LEAPClient] = None
# Base path of a newview type cache directory for this session
self.cache_dir: Optional[str] = None
self._main_region = None
@@ -187,6 +190,7 @@ class SessionManager:
self.message_logger: Optional[BaseMessageLogger] = None
self.addon_ctx: Dict[str, Any] = {}
self.name_cache = ProxyNameCache()
self.pending_leap_clients: List[LEAPClient] = []
def create_session(self, login_data) -> Session:
session = Session.from_login_data(login_data, self)
@@ -203,12 +207,23 @@ class SessionManager:
if session.pending and session.id == session_id:
logging.info("Claimed %r" % session)
session.pending = False
# TODO: less crap way of tying a LEAP client to a session
while self.pending_leap_clients:
leap_client = self.pending_leap_clients.pop(-1)
# Client may have gone bad since it connected
if not leap_client.connected:
continue
logging.info("Assigned LEAP client to session")
session.leap_client = leap_client
break
return session
return None
def close_session(self, session: Session):
logging.info("Closed %r" % session)
session.objects.clear()
if session.leap_client:
session.leap_client.disconnect()
self.sessions.remove(session)
def resolve_cap(self, url: str) -> Optional["CapData"]:
@@ -218,6 +233,9 @@ class SessionManager:
return cap_data
return CapData()
async def leap_client_connected(self, leap_client: LEAPClient):
self.pending_leap_clients.append(leap_client)
@dataclasses.dataclass
class SelectionModel:

View File

@@ -25,6 +25,7 @@ class EnvSettingDescriptor(SettingDescriptor):
class ProxySettings(Settings):
SOCKS_PROXY_PORT: int = EnvSettingDescriptor(9061, "HIPPO_UDP_PORT", int)
HTTP_PROXY_PORT: int = EnvSettingDescriptor(9062, "HIPPO_HTTP_PORT", int)
LEAP_PORT: int = EnvSettingDescriptor(9063, "HIPPO_LEAP_PORT", int)
PROXY_BIND_ADDR: str = EnvSettingDescriptor("127.0.0.1", "HIPPO_BIND_HOST", str)
REMOTELY_ACCESSIBLE: bool = SettingDescriptor(False)
USE_VIEWER_OBJECT_CACHE: bool = SettingDescriptor(False)

View File

@@ -35,6 +35,7 @@ mitmproxy==8.0.0
msgpack==1.0.3
multidict==5.2.0
numpy==1.21.4
outleap~=0.4.1
parso==0.8.3
passlib==1.7.4
prompt-toolkit==3.0.23
@@ -66,4 +67,4 @@ wcwidth==0.2.5
Werkzeug==2.0.2
wsproto==1.0.0
yarl==1.7.2
zstandard==0.15.2
zstandard==0.15.2

View File

@@ -82,6 +82,7 @@ setup(
python_requires='>=3.8',
install_requires=[
'llsd<1.1.0',
'outleap<1.0',
'defusedxml',
'aiohttp<4.0.0',
'recordclass<0.15',