diff --git a/addon_examples/backwards.py b/addon_examples/backwards.py index fac5d60..6b84789 100644 --- a/addon_examples/backwards.py +++ b/addon_examples/backwards.py @@ -5,7 +5,7 @@ Except for backward, which makes you go left. """ from hippolyzer.lib.base.templates import AgentControlFlags -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.proxy.addon_utils import BaseAddon from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session @@ -19,7 +19,7 @@ BACK_MASK = (AgentControlFlags.AT_NEG | AgentControlFlags.NUDGE_AT_NEG) class BackwardsAddon(BaseAddon): - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): if message.name == "AgentUpdate": agent_data_block = message["AgentData"][0] flags: AgentControlFlags = agent_data_block.deserialize_var("ControlFlags") diff --git a/addon_examples/bezoscape.py b/addon_examples/bezoscape.py index e844ebd..58ce210 100644 --- a/addon_examples/bezoscape.py +++ b/addon_examples/bezoscape.py @@ -11,7 +11,7 @@ import secrets from hippolyzer.lib.base.datatypes import UUID from hippolyzer.lib.proxy.addon_utils import BaseAddon, SessionProperty -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session @@ -41,7 +41,7 @@ class BezosifyAddon(BaseAddon): # random value to XOR all CRCs with self.bezos_crc_xor = secrets.randbits(32) - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): if message.name == "ObjectUpdateCached": for block in message["ObjectData"]: # Cached only really has a CRC, this will force the cache miss. diff --git a/addon_examples/blueish_object_list.py b/addon_examples/blueish_object_list.py index 3ded8d7..eb8dcd4 100644 --- a/addon_examples/blueish_object_list.py +++ b/addon_examples/blueish_object_list.py @@ -14,15 +14,14 @@ from typing import * from PySide2 import QtCore, QtGui, QtWidgets from hippolyzer.lib.base.datatypes import Vector3 -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.base.objects import Object from hippolyzer.lib.base.ui_helpers import loadUi from hippolyzer.lib.base.templates import PCode from hippolyzer.lib.proxy.addons import AddonManager from hippolyzer.lib.proxy.addon_utils import BaseAddon, SessionProperty from hippolyzer.lib.proxy.commands import handle_command -from hippolyzer.lib.proxy.packets import Direction -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.network.transport import Direction from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session from hippolyzer.lib.proxy.task_scheduler import TaskLifeScope @@ -81,7 +80,7 @@ class BlueishObjectListGUIAddon(BaseAddon): raise def _highlight_object(self, session: Session, obj: Object): - session.main_region.circuit.send_message(ProxiedMessage( + session.main_region.circuit.send_message(Message( "ForceObjectSelect", Block("Header", ResetList=False), Block("Data", LocalID=obj.LocalID), @@ -89,7 +88,7 @@ class BlueishObjectListGUIAddon(BaseAddon): )) def _teleport_to_object(self, session: Session, obj: Object): - session.main_region.circuit.send_message(ProxiedMessage( + session.main_region.circuit.send_message(Message( "TeleportLocationRequest", Block("AgentData", AgentID=session.agent_id, SessionID=session.id), Block( diff --git a/addon_examples/counter.py b/addon_examples/counter.py index bf46cc3..8a92053 100644 --- a/addon_examples/counter.py +++ b/addon_examples/counter.py @@ -1,9 +1,9 @@ -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session -def handle_lludp_message(session: Session, region: ProxiedRegion, message: ProxiedMessage): +def handle_lludp_message(session: Session, region: ProxiedRegion, message: Message): # addon_ctx will persist across addon reloads, use for storing data that # needs to survive across calls to this function ctx = session.addon_ctx diff --git a/addon_examples/custom_meta_filter.py b/addon_examples/custom_meta_filter.py index a50c030..bb4a4a2 100644 --- a/addon_examples/custom_meta_filter.py +++ b/addon_examples/custom_meta_filter.py @@ -10,13 +10,13 @@ message with a greeting. """ from hippolyzer.lib.proxy.addon_utils import BaseAddon -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session class CustomMetaExampleAddon(BaseAddon): - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): if not message.name.startswith("ChatFrom"): return diff --git a/addon_examples/find_packet_bugs.py b/addon_examples/find_packet_bugs.py index bd28db9..77cbcfc 100644 --- a/addon_examples/find_packet_bugs.py +++ b/addon_examples/find_packet_bugs.py @@ -16,8 +16,8 @@ import random from hippolyzer.lib.base.message.msgtypes import PacketLayout from hippolyzer.lib.base.message.udpserializer import UDPMessageSerializer from hippolyzer.lib.proxy.addon_utils import BaseAddon -from hippolyzer.lib.proxy.message import ProxiedMessage -from hippolyzer.lib.proxy.packets import Direction +from hippolyzer.lib.base.message.message import Message +from hippolyzer.lib.base.network.transport import Direction from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session @@ -28,7 +28,7 @@ class PacketMutationAddon(BaseAddon): def __init__(self): self.serializer = UDPMessageSerializer() - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): # Only inbound messages, don't fiddle with the sim. if message.direction != Direction.IN: return diff --git a/addon_examples/hide_lookat.py b/addon_examples/hide_lookat.py index 7b8a2eb..5757357 100644 --- a/addon_examples/hide_lookat.py +++ b/addon_examples/hide_lookat.py @@ -3,8 +3,8 @@ Drop outgoing packets that might leak what you're looking at, similar to Firesto """ from hippolyzer.lib.base.templates import ViewerEffectType -from hippolyzer.lib.proxy.message import ProxiedMessage -from hippolyzer.lib.proxy.packets import Direction +from hippolyzer.lib.base.message.message import Message +from hippolyzer.lib.base.network.transport import Direction from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session @@ -17,7 +17,7 @@ BLOCKED_EFFECTS = ( ) -def handle_lludp_message(_session: Session, region: ProxiedRegion, msg: ProxiedMessage): +def handle_lludp_message(_session: Session, region: ProxiedRegion, msg: Message): if msg.name == "ViewerEffect" and msg.direction == Direction.OUT: new_blocks = [b for b in msg["Effect"] if b["Type"] not in BLOCKED_EFFECTS] if new_blocks: diff --git a/addon_examples/horror_animator.py b/addon_examples/horror_animator.py index 85b0fda..2938966 100644 --- a/addon_examples/horror_animator.py +++ b/addon_examples/horror_animator.py @@ -13,7 +13,7 @@ from hippolyzer.lib.base.datatypes import UUID from hippolyzer.lib.base.llanim import Animation from hippolyzer.lib.proxy.addon_utils import AssetAliasTracker, BaseAddon, GlobalProperty from hippolyzer.lib.proxy.http_flow import HippoHTTPFlow -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session, SessionManager from hippolyzer.lib.base.vfs import STATIC_VFS @@ -53,7 +53,7 @@ class HorrorAnimatorAddon(BaseAddon): # We've reloaded, so make sure assets get new aliases self.horror_anim_tracker.invalidate_aliases() - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): tracker = self.horror_anim_tracker if message.name == "AvatarAnimation": diff --git a/addon_examples/local_anim.py b/addon_examples/local_anim.py index 3b02b5f..c612916 100644 --- a/addon_examples/local_anim.py +++ b/addon_examples/local_anim.py @@ -16,12 +16,11 @@ import pathlib from typing import * from hippolyzer.lib.base.datatypes import UUID -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.proxy.addons import AddonManager from hippolyzer.lib.proxy.addon_utils import BaseAddon, SessionProperty from hippolyzer.lib.proxy.commands import handle_command from hippolyzer.lib.proxy.http_asset_repo import HTTPAssetRepo -from hippolyzer.lib.proxy.message import ProxiedMessage from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session @@ -101,7 +100,7 @@ class LocalAnimAddon(BaseAddon): anim_name: str, new_data: Optional[bytes] = None): asset_repo: HTTPAssetRepo = session.session_manager.asset_repo next_id: Optional[UUID] = None - new_msg = ProxiedMessage( + new_msg = Message( "AgentAnimation", Block( "AgentData", diff --git a/addon_examples/local_mesh.py b/addon_examples/local_mesh.py index 0cadc3a..2c2228a 100644 --- a/addon_examples/local_mesh.py +++ b/addon_examples/local_mesh.py @@ -36,7 +36,7 @@ from hippolyzer.lib.proxy.addon_utils import show_message, BaseAddon, GlobalProp from hippolyzer.lib.proxy.commands import handle_command from hippolyzer.lib.proxy.http_asset_repo import HTTPAssetRepo from hippolyzer.lib.proxy.http_flow import HippoHTTPFlow -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session, SessionManager @@ -125,7 +125,7 @@ class MeshUploadInterceptingAddon(BaseAddon): region.objects.request_objects(old_locals) show_message(f"Cleared target {old_locals}") - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): # Replace any mesh asset IDs in tracked objects with our local assets if not self.local_mesh_target_locals: return diff --git a/addon_examples/monochrome.py b/addon_examples/monochrome.py index ddfaa3f..6a78c48 100644 --- a/addon_examples/monochrome.py +++ b/addon_examples/monochrome.py @@ -30,7 +30,7 @@ from hippolyzer.lib.base.multiprocessing_utils import ParentProcessWatcher from hippolyzer.lib.base.templates import TextureEntry from hippolyzer.lib.proxy.addon_utils import AssetAliasTracker, BaseAddon, GlobalProperty, AddonProcess from hippolyzer.lib.proxy.http_flow import HippoHTTPFlow -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session, SessionManager @@ -98,7 +98,7 @@ class MonochromeAddon(BaseAddon): # Tell queue consumers to shut down self.mono_addon_shutdown_signal.set() - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): tracker = self.mono_tracker if message.name == "ObjectUpdateCached": for block in message["ObjectData"]: diff --git a/addon_examples/payday.py b/addon_examples/payday.py index 0fade1c..be334fc 100644 --- a/addon_examples/payday.py +++ b/addon_examples/payday.py @@ -3,16 +3,15 @@ Do the money dance whenever someone in the sim pays you directly """ from hippolyzer.lib.base.datatypes import UUID -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.base.templates import MoneyTransactionType, PCode, ChatType -from hippolyzer.lib.proxy.message import ProxiedMessage from hippolyzer.lib.proxy.addon_utils import send_chat, BaseAddon from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session class PaydayAddon(BaseAddon): - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): if message.name != "MoneyBalanceReply": return transaction_block = message["TransactionInfo"][0] @@ -38,7 +37,7 @@ class PaydayAddon(BaseAddon): chat_type=ChatType.SHOUT, ) # Do the traditional money dance. - session.main_region.circuit.send_message(ProxiedMessage( + session.main_region.circuit.send_message(Message( "AgentAnimation", Block("AgentData", AgentID=session.agent_id, SessionID=session.id), Block("AnimationList", AnimID=UUID("928cae18-e31d-76fd-9cc9-2f55160ff818"), StartAnim=True), diff --git a/addon_examples/recapitator.py b/addon_examples/recapitator.py index 9dc54e6..f5875da 100644 --- a/addon_examples/recapitator.py +++ b/addon_examples/recapitator.py @@ -18,14 +18,13 @@ from typing import * from hippolyzer.lib.base import llsd from hippolyzer.lib.base.datatypes import UUID -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.base.templates import AssetType, WearableType from hippolyzer.lib.base.wearables import Wearable, VISUAL_PARAMS from hippolyzer.lib.proxy.addon_utils import BaseAddon, SessionProperty, AssetAliasTracker, show_message from hippolyzer.lib.proxy.commands import handle_command from hippolyzer.lib.proxy.http_flow import HippoHTTPFlow -from hippolyzer.lib.proxy.message import ProxiedMessage -from hippolyzer.lib.proxy.packets import Direction +from hippolyzer.lib.base.network.transport import Direction from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session, SessionManager @@ -52,7 +51,7 @@ class RecapitatorAddon(BaseAddon): self.recapitating = False show_message("Recapitation disabled") - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): if not self.recapitating: return if message.direction != Direction.OUT: @@ -68,7 +67,7 @@ class RecapitatorAddon(BaseAddon): self._schedule_task(self._proxy_bodypart_upload(session, region, new_message)) return True - async def _proxy_bodypart_upload(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + async def _proxy_bodypart_upload(self, session: Session, region: ProxiedRegion, message: Message): asset_block = message["AssetBlock"] # Asset will already be in the viewer's VFS as the expected asset ID, calculate it. asset_id = session.tid_to_assetid(asset_block["TransactionID"]) @@ -117,7 +116,7 @@ class RecapitatorAddon(BaseAddon): except: logging.exception("Exception while recapitating") # Tell the viewer about the status of its original upload - region.circuit.send_message(ProxiedMessage( + region.circuit.send_message(Message( "AssetUploadComplete", Block("AssetBlock", UUID=asset_id, Type=asset_block["Type"], Success=success), direction=Direction.IN, diff --git a/addon_examples/repl.py b/addon_examples/repl.py index 1773f47..74eb661 100644 --- a/addon_examples/repl.py +++ b/addon_examples/repl.py @@ -1,12 +1,12 @@ from hippolyzer.lib.proxy.addons import AddonManager from hippolyzer.lib.proxy.addon_utils import BaseAddon -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session class REPLExampleAddon(BaseAddon): - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): if message.name == "ChatFromViewer": chat_msg = message["ChatData"]["Message"] if not chat_msg: diff --git a/addon_examples/serialization_sanity_checker.py b/addon_examples/serialization_sanity_checker.py index a6a4759..8c43b30 100644 --- a/addon_examples/serialization_sanity_checker.py +++ b/addon_examples/serialization_sanity_checker.py @@ -15,8 +15,8 @@ from hippolyzer.lib.base import serialization as se from hippolyzer.lib.base.message.udpdeserializer import UDPMessageDeserializer from hippolyzer.lib.base.message.udpserializer import UDPMessageSerializer from hippolyzer.lib.proxy.addon_utils import BaseAddon -from hippolyzer.lib.proxy.message import ProxiedMessage -from hippolyzer.lib.proxy.packets import ProxiedUDPPacket +from hippolyzer.lib.base.message.message import Message +from hippolyzer.lib.base.network.transport import UDPPacket from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import SessionManager, Session @@ -28,9 +28,9 @@ class SerializationSanityChecker(BaseAddon): self.serializer = UDPMessageSerializer() self.deserializer = UDPMessageDeserializer() - def handle_proxied_packet(self, session_manager: SessionManager, packet: ProxiedUDPPacket, + def handle_proxied_packet(self, session_manager: SessionManager, packet: UDPPacket, session: Optional[Session], region: Optional[ProxiedRegion], - message: Optional[ProxiedMessage]): + message: Optional[Message]): # Well this doesn't even parse as a message, can't do anything about it. if message is None: LOG.error(f"Received unparseable message from {packet.src_addr!r}: {packet.data!r}") @@ -63,7 +63,7 @@ class SerializationSanityChecker(BaseAddon): except: LOG.exception(f"Exception during message validation:\n{message!r}") - def _roundtrip_var_serializers(self, message: ProxiedMessage): + def _roundtrip_var_serializers(self, message: Message): for block in itertools.chain(*message.blocks.values()): for var_name in block.vars.keys(): orig_val = block[var_name] diff --git a/addon_examples/shield.py b/addon_examples/shield.py index f9c7d1f..5044416 100644 --- a/addon_examples/shield.py +++ b/addon_examples/shield.py @@ -1,8 +1,8 @@ """Block potentially bad things""" from hippolyzer.lib.base.templates import IMDialogType, XferFilePath from hippolyzer.lib.proxy.addon_utils import BaseAddon, show_message -from hippolyzer.lib.proxy.message import ProxiedMessage -from hippolyzer.lib.proxy.packets import Direction +from hippolyzer.lib.base.message.message import Message +from hippolyzer.lib.base.network.transport import Direction from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session @@ -11,7 +11,7 @@ REGULAR_IM_DIALOGS = (IMDialogType.TYPING_STOP, IMDialogType.TYPING_STOP, IMDial class ShieldAddon(BaseAddon): - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): if message.direction != Direction.IN: return if message.name in SUSPICIOUS_PACKETS: diff --git a/addon_examples/spongecase.py b/addon_examples/spongecase.py index 9b9f3c1..cd40c8e 100644 --- a/addon_examples/spongecase.py +++ b/addon_examples/spongecase.py @@ -1,6 +1,6 @@ import itertools -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session @@ -12,7 +12,7 @@ def _to_spongecase(val): return "".join(itertools.chain(*spongecased)) -def handle_lludp_message(session: Session, _region: ProxiedRegion, message: ProxiedMessage): +def handle_lludp_message(session: Session, _region: ProxiedRegion, message: Message): ctx = session.addon_ctx ctx.setdefault("spongecase", False) if message.name == "ChatFromViewer": diff --git a/addon_examples/transfer_example.py b/addon_examples/transfer_example.py index b450f4f..90c81b5 100644 --- a/addon_examples/transfer_example.py +++ b/addon_examples/transfer_example.py @@ -4,7 +4,7 @@ Example of how to request a Transfer from typing import * from hippolyzer.lib.base.legacy_inv import InventoryModel, InventoryItem -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.base.templates import ( AssetType, EstateAssetType, @@ -15,7 +15,6 @@ from hippolyzer.lib.base.templates import ( ) from hippolyzer.lib.proxy.addon_utils import BaseAddon, show_message from hippolyzer.lib.proxy.commands import handle_command -from hippolyzer.lib.proxy.message import ProxiedMessage from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session @@ -36,7 +35,7 @@ class TransferExampleAddon(BaseAddon): async def get_first_script(self, session: Session, region: ProxiedRegion): """Get the contents of the first script in the selected object""" # Ask for the object inventory so we can find a script - region.circuit.send_message(ProxiedMessage( + region.circuit.send_message(Message( 'RequestTaskInventory', Block('AgentData', AgentID=session.agent_id, SessionID=session.id), Block('InventoryData', LocalID=session.selected.object_local), diff --git a/addon_examples/turbo_object_inventory.py b/addon_examples/turbo_object_inventory.py index 206f6a1..faa2dc0 100644 --- a/addon_examples/turbo_object_inventory.py +++ b/addon_examples/turbo_object_inventory.py @@ -34,15 +34,15 @@ from typing import * from hippolyzer.lib.base.templates import XferFilePath from hippolyzer.lib.proxy.addon_utils import BaseAddon -from hippolyzer.lib.proxy.message import ProxiedMessage -from hippolyzer.lib.proxy.packets import Direction +from hippolyzer.lib.base.message.message import Message +from hippolyzer.lib.base.network.transport import Direction from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session from hippolyzer.lib.proxy.xfer_manager import Xfer class TurboObjectInventoryAddon(BaseAddon): - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): if message.direction != Direction.OUT: return if message.name != "RequestTaskInventory": @@ -54,7 +54,7 @@ class TurboObjectInventoryAddon(BaseAddon): async def _proxy_task_inventory_request( self, region: ProxiedRegion, - request_msg: ProxiedMessage + request_msg: Message ): # Keep around a dict of chunks we saw previously in case we have to restart # an Xfer due to missing chunks. We don't expect chunks to change across Xfers diff --git a/addon_examples/uploader.py b/addon_examples/uploader.py index 9085d35..cca180b 100644 --- a/addon_examples/uploader.py +++ b/addon_examples/uploader.py @@ -11,13 +11,12 @@ from typing import * import aiohttp from hippolyzer.lib.base.datatypes import UUID -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.base.templates import AssetType from hippolyzer.lib.proxy.addons import AddonManager from hippolyzer.lib.proxy.addon_utils import ais_item_to_inventory_data, show_message, BaseAddon from hippolyzer.lib.proxy.commands import handle_command, Parameter -from hippolyzer.lib.proxy.packets import Direction -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.network.transport import Direction from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session @@ -92,7 +91,7 @@ class UploaderAddon(BaseAddon): async with region.caps_client.post('FetchInventory2', llsd=ais_req_data) as resp: ais_item = (await resp.read_llsd())["items"][0] - message = ProxiedMessage( + message = Message( "UpdateCreateInventoryItem", Block( "AgentData", diff --git a/addon_examples/xfer_example.py b/addon_examples/xfer_example.py index 0ee7f5f..04a5f66 100644 --- a/addon_examples/xfer_example.py +++ b/addon_examples/xfer_example.py @@ -4,10 +4,9 @@ Example of how to request an Xfer from hippolyzer.lib.base.datatypes import UUID from hippolyzer.lib.base.legacy_inv import InventoryModel from hippolyzer.lib.base.templates import XferFilePath, AssetType, InventoryType, WearableType -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.proxy.addon_utils import BaseAddon, show_message from hippolyzer.lib.proxy.commands import handle_command -from hippolyzer.lib.proxy.message import ProxiedMessage from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session @@ -16,7 +15,7 @@ class XferExampleAddon(BaseAddon): @handle_command() async def get_mute_list(self, session: Session, region: ProxiedRegion): """Fetch the current user's mute list""" - region.circuit.send_message(ProxiedMessage( + region.circuit.send_message(Message( 'MuteListRequest', Block('AgentData', AgentID=session.agent_id, SessionID=session.id), Block("MuteData", MuteCRC=0), @@ -36,7 +35,7 @@ class XferExampleAddon(BaseAddon): @handle_command() async def get_task_inventory(self, session: Session, region: ProxiedRegion): """Get the inventory of the currently selected object""" - region.circuit.send_message(ProxiedMessage( + region.circuit.send_message(Message( 'RequestTaskInventory', # If no session is passed in we'll use the active session when the coro was created Block('AgentData', AgentID=session.agent_id, SessionID=session.id), @@ -99,7 +98,7 @@ textures 1 data=asset_data, transaction_id=transaction_id ) - region.circuit.send_message(ProxiedMessage( + region.circuit.send_message(Message( 'CreateInventoryItem', Block('AgentData', AgentID=session.agent_id, SessionID=session.id), Block( diff --git a/hippolyzer/apps/proxy.py b/hippolyzer/apps/proxy.py index ee4d735..d1e4cae 100644 --- a/hippolyzer/apps/proxy.py +++ b/hippolyzer/apps/proxy.py @@ -17,7 +17,7 @@ from hippolyzer.lib.proxy.commands import handle_command from hippolyzer.lib.proxy.http_proxy import create_http_proxy, create_proxy_master, HTTPFlowContext from hippolyzer.lib.proxy.http_event_manager import MITMProxyEventManager from hippolyzer.lib.proxy.lludp_proxy import SLSOCKS5Server -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import SessionManager, Session @@ -25,7 +25,7 @@ LOG = logging.getLogger(__name__) class SelectionManagerAddon(BaseAddon): - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): selected = session.selected if message.name == "ObjectSelect": # ObjectDeselect intentionally ignored to deal with messages that diff --git a/hippolyzer/apps/proxy_gui.py b/hippolyzer/apps/proxy_gui.py index d055fb9..4242220 100644 --- a/hippolyzer/apps/proxy_gui.py +++ b/hippolyzer/apps/proxy_gui.py @@ -25,17 +25,17 @@ from hippolyzer.lib.base import llsd from hippolyzer.lib.base.datatypes import UUID from hippolyzer.lib.base.helpers import bytes_unescape, bytes_escape, get_resource_filename from hippolyzer.lib.base.message.llsd_msg_serializer import LLSDMessageSerializer -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.base.message.msgtypes import MsgType from hippolyzer.lib.base.message.template_dict import TemplateDictionary from hippolyzer.lib.base.ui_helpers import loadUi import hippolyzer.lib.base.serialization as se +from hippolyzer.lib.base.message.message import VerbatimHumanVal, subfield_eval, SpannedString +from hippolyzer.lib.base.network.transport import Direction from hippolyzer.lib.proxy.addons import BaseInteractionManager, AddonManager from hippolyzer.lib.proxy.ca_utils import setup_ca_everywhere from hippolyzer.lib.proxy.caps_client import CapsClient from hippolyzer.lib.proxy.http_proxy import create_proxy_master, HTTPFlowContext -from hippolyzer.lib.proxy.packets import Direction -from hippolyzer.lib.proxy.message import ProxiedMessage, VerbatimHumanVal, proxy_eval, SpannedString from hippolyzer.lib.proxy.message_logger import LLUDPMessageLogEntry, AbstractMessageLogEntry from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session, SessionManager @@ -513,7 +513,7 @@ class MessageBuilderWindow(QtWidgets.QMainWindow): self.textRequest.clear() template = self.templateDict[message_name] - msg = ProxiedMessage(message_name, direction=Direction.OUT) + msg = Message(message_name, direction=Direction.OUT) for tmpl_block in template.blocks: num_blocks = tmpl_block.number or 1 @@ -609,7 +609,7 @@ class MessageBuilderWindow(QtWidgets.QMainWindow): env = self._buildEnv(session, region) # We specifically want to allow `eval()` in messages since # messages from here are trusted. - msg = ProxiedMessage.from_human_string(msg_text, replacements, env, safe=False) + msg = Message.from_human_string(msg_text, replacements, env, safe=False) if self.checkLLUDPViaCaps.isChecked(): if msg.direction == Direction.IN: region.eq_manager.queue_event( @@ -628,7 +628,7 @@ class MessageBuilderWindow(QtWidgets.QMainWindow): transport = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) region.circuit.send_message(msg, transport=transport) - def _sendEQMessage(self, session, region: Optional[ProxiedRegion], msg_text: str, replacements: dict): + def _sendEQMessage(self, session, region: Optional[ProxiedRegion], msg_text: str, _replacements: dict): if not session or not region: raise RuntimeError("Need a valid session and region to send EQ event") message_line, _, body = (x.strip() for x in msg_text.partition("\n")) @@ -696,7 +696,7 @@ class MessageBuilderWindow(QtWidgets.QMainWindow): elif directive == b"UNESCAPE": val = unescaped_contents elif directive == b"EVAL": - val = proxy_eval(contents.decode("utf8").strip(), globals_={**env, **replacements}) + val = subfield_eval(contents.decode("utf8").strip(), globals_={**env, **replacements}) val = _coerce_to_bytes(val) elif directive == b"REPL": val = _coerce_to_bytes(replacements[contents.decode("utf8").strip()]) diff --git a/hippolyzer/lib/base/message/circuit.py b/hippolyzer/lib/base/message/circuit.py new file mode 100644 index 0000000..c69694c --- /dev/null +++ b/hippolyzer/lib/base/message/circuit.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import datetime as dt +import logging +from typing import * + +from ..network.transport import AbstractUDPTransport, UDPPacket, Direction, ADDR_TUPLE +from .message import Block +from .msgtypes import PacketFlags +from .udpserializer import UDPMessageSerializer +from .message import Message + + +class Circuit: + def __init__(self, near_host: Optional[ADDR_TUPLE], far_host: ADDR_TUPLE, transport): + self.near_host: Optional[ADDR_TUPLE] = near_host + self.host: ADDR_TUPLE = far_host + self.is_alive = True + self.transport: Optional[AbstractUDPTransport] = transport + self.serializer = UDPMessageSerializer() + self.last_packet_at = dt.datetime.now() + self.packet_id_base = 0 + + def _send_prepared_message(self, message: Message, transport=None): + try: + serialized = self.serializer.serialize(message) + except: + logging.exception(f"Failed to serialize: {message.to_dict()!r}") + raise + return self.send_datagram(serialized, message.direction, transport=transport) + + def send_datagram(self, data: bytes, direction: Direction, transport=None): + self.last_packet_at = dt.datetime.now() + src_addr, dst_addr = self.host, self.near_host + if direction == Direction.OUT: + src_addr, dst_addr = self.near_host, self.host + + packet = UDPPacket(src_addr, dst_addr, data, direction) + (transport or self.transport).send_packet(packet) + return packet + + def prepare_message(self, message: Message): + if message.finalized: + raise RuntimeError(f"Trying to re-send finalized {message!r}") + message.packet_id = self.packet_id_base + self.packet_id_base += 1 + if not message.acks: + message.send_flags &= PacketFlags.ACK + message.finalized = True + + def send_message(self, message: Message, transport=None): + if self.prepare_message(message): + return self._send_prepared_message(message, transport) + + def send_acks(self, to_ack: Sequence[int], direction=Direction.OUT, packet_id=None): + logging.debug("%r acking %r" % (direction, to_ack)) + # TODO: maybe tack this onto `.acks` for next message? + message = Message('PacketAck', *[Block('Packets', ID=x) for x in to_ack]) + message.packet_id = packet_id + message.direction = direction + message.injected = True + self.send_message(message) + + def __repr__(self): + return "<%s %r : %r>" % (self.__class__.__name__, self.near_host, self.host) diff --git a/hippolyzer/lib/base/message/message.py b/hippolyzer/lib/base/message/message.py index 39cd577..f4bd32b 100644 --- a/hippolyzer/lib/base/message/message.py +++ b/hippolyzer/lib/base/message/message.py @@ -18,20 +18,51 @@ You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. """ +from __future__ import annotations +import ast +import base64 import copy import enum import itertools +import logging +import math +import os +import re +import uuid from typing import * -from .. import serialization as se -from ..datatypes import * -from .msgtypes import PacketFlags +import hippolyzer.lib.base.datatypes +from hippolyzer.lib.base.datatypes import * +import hippolyzer.lib.base.serialization as se +from hippolyzer.lib.base import llsd +from hippolyzer.lib.base.helpers import HippoPrettyPrinter +import hippolyzer.lib.base.templates as templates +from hippolyzer.lib.base.message.msgtypes import MsgBlockType, PacketFlags +from hippolyzer.lib.base.message.template import MessageTemplate +from hippolyzer.lib.base.network.transport import Direction BLOCK_DICT = Dict[str, "MsgBlockList"] VAR_TYPE = Union[TupleCoord, bytes, str, float, int, Tuple, UUID] +_TEMPLATES_MTIME = os.stat(templates.__file__).st_mtime + + +def _maybe_reload_templates(): + # Templates may be modified at runtime during development, check + # if they've changed since startup and reload if they have. + global _TEMPLATES_MTIME + templates_mtime = os.stat(templates.__file__).st_mtime + + if _TEMPLATES_MTIME is None or _TEMPLATES_MTIME < templates_mtime: + print("Reloading templates") + try: + importlib.reload(templates) # type: ignore + _TEMPLATES_MTIME = templates_mtime + except: + logging.exception("Failed to reload templates!") + class Block: """ @@ -159,9 +190,13 @@ class MsgBlockList(List["Block"]): class Message: __slots__ = ("name", "send_flags", "_packet_id", "acks", "body_boundaries", "queued", - "offset", "raw_extra", "raw_body", "deserializer", "_blocks", "finalized") + "offset", "raw_extra", "raw_body", "deserializer", "_blocks", "finalized", + "direction", "meta", "injected", "dropped") + + def __init__(self, name, *args, packet_id=None, flags=0, acks=None, direction=None): + # TODO: Do this on a timer or something. + _maybe_reload_templates() - def __init__(self, name, *args, packet_id=None, flags=0, acks=None): self.name = name self.send_flags = flags self._packet_id: Optional[int] = packet_id # aka, sequence number @@ -170,6 +205,7 @@ class Message: self.body_boundaries = (-1, -1) self.offset = 0 self.raw_extra = b"" + self.direction: Direction = direction if direction is not None else Direction.OUT # For lazy deserialization self.raw_body = None self.deserializer = None @@ -179,6 +215,9 @@ class Message: # Whether message is owned by the queue or should be sent immediately self.queued: bool = False self._blocks: BLOCK_DICT = {} + self.meta = {} + self.injected = False + self.dropped = False self.add_blocks(args) @@ -317,7 +356,7 @@ class Message: block_reprs = sep.join(x.repr(pretty=pretty) for x in itertools.chain(*self.blocks.values())) if block_reprs: block_reprs = sep + block_reprs - return f"{self.name!r}{block_reprs}" + return f"{self.name!r}{block_reprs}, direction=Direction.{self.direction.name}" def repr(self, pretty=False): self.ensure_parsed() @@ -335,6 +374,197 @@ class Message: message_copy.packet_id = None return message_copy + def to_human_string(self, replacements=None, beautify=False, + template: Optional[MessageTemplate] = None) -> SpannedString: + replacements = replacements or {} + _maybe_reload_templates() + spans: SpanDict = {} + string = "" + if self.direction is not None: + string += f'{self.direction.name} ' + string += self.name + if self.packet_id is not None: + string += f'\n# {self.packet_id}: {PacketFlags(self.send_flags)!r}' + string += f'{", DROPPED" if self.dropped else ""}{", INJECTED" if self.injected else ""}' + if self.extra: + string += f'\n# EXTRA: {self.extra!r}' + string += '\n\n' + + for block_name, block_list in self.blocks.items(): + block_suffix = "" + if template and template.get_block(block_name).block_type == MsgBlockType.MBT_VARIABLE: + block_suffix = ' # Variable' + for block_num, block in enumerate(block_list): + string += f"[{block_name}]{block_suffix}\n" + for var_name, val in block.items(): + start_len = len(string) + string += self._format_var(block, var_name, val, replacements, beautify) + end_len = len(string) + # Store the spans for each var so we can highlight specific matches + spans[(self.name, block_name, block_num, var_name)] = (start_len, end_len) + string += "\n" + spanned = SpannedString(string) + spanned.spans = spans + return spanned + + def _format_var(self, block, var_name, var_val, replacements, beautify=False): + string = "" + # Check if we have a more human-readable way to present this field + ser_key = (self.name, block.name, var_name) + serializer = se.SUBFIELD_SERIALIZERS.get(ser_key) + field_prefix = "" + if isinstance(var_val, VerbatimHumanVal): + var_data = var_val + elif isinstance(var_val, (uuid.UUID, TupleCoord)): + var_data = str(var_val) + elif isinstance(var_val, (str, bytes)) and not serializer: + var_data = self._multi_line_pformat(var_val) + else: + var_data = repr(var_val) + if serializer and beautify and not isinstance(var_val, VerbatimHumanVal): + try: + pretty_data = serializer.deserialize(block, var_val, pod=True) + if pretty_data is not se.UNSERIALIZABLE: + string += f" {var_name} =| {self._multi_line_pformat(pretty_data)}" + if serializer.AS_HEX and isinstance(var_val, int): + var_data = hex(var_val) + if serializer.ORIG_INLINE: + string += f" #{var_data}" + return string + else: + string += "\n" + # Human-readable version should be used, orig data is commented out + field_prefix = "#" + except: + logging.exception(f"Failed in subfield serializer {ser_key!r}") + if beautify: + if block.name == "AgentData": + if var_name == "AgentID" and var_val == replacements.get("AGENT_ID"): + var_data = "[[AGENT_ID]]" + elif var_name == "SessionID" and var_val == replacements.get("SESSION_ID"): + var_data = "[[SESSION_ID]]" + if "CircuitCode" in var_name or ("Code" in var_name and "Circuit" in block.name): + if var_val == replacements.get("CIRCUIT_CODE"): + var_data = "[[CIRCUIT_CODE]]" + string += f" {field_prefix}{var_name} = {var_data}" + return string + + @staticmethod + def _multi_line_pformat(val): + printer = HippoPrettyPrinter(width=100) + val = printer.pformat(val) + newstr = "" + # Now we need to rebuild this to add in the appropriate + # line continuations. + lines = list(val.splitlines()) + first_line = True + while lines: + line = lines.pop(0) + prefix = "" + suffix = "" + if first_line: + first_line = False + else: + prefix = " " + + if lines: + suffix = " \\\n" + newstr += f"{prefix}{line}{suffix}" + return newstr + + def to_summary(self): + string = "" + for block_name, block_list in self.blocks.items(): + for block in block_list: + for var_name, val in block.items(): + if block.name == "AgentData" and var_name in ("AgentID", "SessionID"): + continue + if string: + string += ", " + string += f"{var_name}={_trunc_repr(val, 10)}" + return string + + @classmethod + def from_human_string(cls, string, replacements=None, env=None, safe=True): + _maybe_reload_templates() + replacements = replacements or {} + env = env or {} + first_line = True + cur_block = None + msg = None + lines = [x.strip() for x in string.split("\n") if x.strip()] + while lines: + line = lines.pop(0) + # Ignore comment / blank lines + if re.match(r"^\s*(#.*)?$", line): + continue + + if first_line: + direction, message_name = line.split(" ", 1) + msg = cls(message_name) + msg.direction = Direction[direction.upper()] + first_line = False + continue + + if line.startswith("["): + cur_block = Block(re.search(r"\w+", line).group(0)) + msg.add_block(cur_block) + else: + expr_match = re.match(r"^\s*(\w+)\s*(=[|$]*)\s*(.*)$", line) + var_name, operator, var_val = expr_match.groups() + # Multiline, eat all the line continuations + while var_val.endswith("\\"): + var_val = var_val[:-1].rstrip() + if lines: + var_val += lines.pop(0) + + plain = operator == "=" + packed = "|" in operator + evaled = "$" in operator + + if evaled and safe: + raise ValueError("Can't use eval operator in safe mode") + + if plain: + replacement_match = re.match(r"\[\[(\w+)]]", var_val) + if replacement_match: + replacement_name = replacement_match.group(1) + var_val = replacements.get(replacement_name) + if var_val is None: + raise ValueError("Tried to use undefined replacement %s" % replacement_name) + if callable(var_val): + var_val = var_val() + # alternate way of specifying a vector or quat + elif var_val.startswith("<"): + var_val = re.sub(r"[<>]", "", var_val) + var_val = tuple(float(x) for x in var_val.split(",")) + # UUID-ish + elif re.match(r"\A\w+-\w+-.*", var_val): + var_val = UUID(var_val) + else: + var_val = ast.literal_eval(var_val) + + # Normally gross, but necessary for expressiveness in built messages + # unless a metalanguage is added. + if evaled: + var_val = subfield_eval( + var_val, + globals_={**env, **replacements}, + locals_={"block": cur_block} + ) + # Using an packer specific to this message + if packed: + if not evaled: + var_val = ast.literal_eval(var_val) + ser_key = (msg.name, cur_block.name, var_name) + serializer = se.SUBFIELD_SERIALIZERS.get(ser_key) + if not serializer: + raise KeyError(f"No subfield serializer for {ser_key!r}") + var_val = serializer.serialize(cur_block, var_val) + + cur_block[var_name] = var_val + return msg + def __repr__(self): return self.repr() @@ -342,3 +572,45 @@ class Message: if not isinstance(other, self.__class__): return NotImplemented return self.to_dict() == other.to_dict() + + +def _trunc_repr(val, max_len): + if isinstance(val, (uuid.UUID, TupleCoord)): + val = str(val) + repr_val = repr(val) + if isinstance(val, str): + repr_val = repr_val[1:-1] + if isinstance(val, bytes): + repr_val = repr_val[2:-1] + if len(repr_val) > max_len: + return repr_val[:max_len] + "…" + return repr_val + + +class VerbatimHumanVal(str): + pass + + +def _filtered_exports(mod): + return {k: getattr(mod, k) for k in mod.__all__} + + +def subfield_eval(eval_str: str, globals_=None, locals_=None): + return eval( + eval_str, + { + "llsd": llsd, + "base64": base64, + "math": math, + **_filtered_exports(hippolyzer.lib.base.datatypes), + **(globals_ or {})}, + locals_ + ) + + +TextSpan = Tuple[int, int] +SpanDict = Dict[Tuple[Union[str, int], ...], TextSpan] + + +class SpannedString(str): + spans: SpanDict = {} diff --git a/hippolyzer/lib/base/message/udpdeserializer.py b/hippolyzer/lib/base/message/udpdeserializer.py index 9b0733e..330a17e 100644 --- a/hippolyzer/lib/base/message/udpdeserializer.py +++ b/hippolyzer/lib/base/message/udpdeserializer.py @@ -64,10 +64,9 @@ def _parse_msg_num(reader: se.BufferReader): class UDPMessageDeserializer: DEFAULT_TEMPLATE = TemplateDictionary() - def __init__(self, settings=None, message_cls: Type[Message] = Message): + def __init__(self, settings=None): self.settings = settings or Settings() self.template_dict = self.DEFAULT_TEMPLATE - self.message_cls = message_cls def deserialize(self, msg_buff: bytes): msg = self._parse_message_header(msg_buff) @@ -85,7 +84,7 @@ class UDPMessageDeserializer: reader = se.BufferReader("!", data) - msg: Message = self.message_cls("Placeholder") + msg: Message = Message("Placeholder") msg.send_flags = reader.read(se.U8) msg.packet_id = reader.read(se.U32) diff --git a/hippolyzer/lib/base/network/transport.py b/hippolyzer/lib/base/network/transport.py new file mode 100644 index 0000000..144553c --- /dev/null +++ b/hippolyzer/lib/base/network/transport.py @@ -0,0 +1,71 @@ +import abc +import asyncio +import enum +import typing + + +ADDR_TUPLE = typing.Tuple[str, int] + + +class Direction(enum.Enum): + OUT = enum.auto() + IN = enum.auto() + + def __invert__(self): + if self == self.OUT: + return self.IN + return self.OUT + + +class UDPPacket: + def __init__( + self, + src_addr: typing.Optional[ADDR_TUPLE], + dst_addr: ADDR_TUPLE, + data: bytes, + direction: Direction + ): + self.src_addr = src_addr + self.dst_addr = dst_addr + self.data = data + self.direction = direction + + @property + def outgoing(self): + return self.direction == Direction.OUT + + @property + def incoming(self): + return self.direction == Direction.IN + + @property + def far_addr(self): + if self.outgoing: + return self.dst_addr + return self.src_addr + + +class AbstractUDPTransport(abc.ABC): + __slots__ = () + + @abc.abstractmethod + def send_packet(self, packet: UDPPacket) -> None: + pass + + @abc.abstractmethod + def close(self) -> None: + pass + + +class WrappingUDPTransport(AbstractUDPTransport): + def __init__(self, transport: asyncio.DatagramTransport): + super().__init__() + self.transport = transport + + def send_packet(self, packet: UDPPacket) -> None: + if not packet.outgoing: + raise ValueError(f"{self.__class__.__name__} can only send outbound packets") + self.transport.sendto(packet.data, packet.dst_addr) + + def close(self) -> None: + self.transport.close() diff --git a/hippolyzer/lib/proxy/addon_utils.py b/hippolyzer/lib/proxy/addon_utils.py index fcd2008..1052cb9 100644 --- a/hippolyzer/lib/proxy/addon_utils.py +++ b/hippolyzer/lib/proxy/addon_utils.py @@ -8,13 +8,12 @@ import warnings from typing import * from hippolyzer.lib.base.datatypes import UUID, Vector3 -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.base.objects import Object from hippolyzer.lib.proxy import addon_ctx from hippolyzer.lib.proxy.addons import AddonManager from hippolyzer.lib.proxy.http_flow import HippoHTTPFlow -from hippolyzer.lib.proxy.packets import Direction, ProxiedUDPPacket -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.network.transport import UDPPacket, Direction from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import SessionManager, Session from hippolyzer.lib.proxy.task_scheduler import TaskLifeScope @@ -58,7 +57,7 @@ def show_message(text, session=None) -> None: # `or None` so we don't use a dead weakref Proxy which are False-y session = session or addon_ctx.session.get(None) or None - message = ProxiedMessage( + message = Message( "ChatFromSimulator", Block( "ChatData", @@ -84,7 +83,7 @@ def send_chat(message: Union[bytes, str], channel=0, chat_type=ChatType.NORMAL, session = session or addon_ctx.session.get(None) or None if not session: raise RuntimeError("Tried to send chat without session") - session.main_region.circuit.send_message(ProxiedMessage( + session.main_region.circuit.send_message(Message( "ChatFromViewer", Block( "AgentData", @@ -160,7 +159,7 @@ class BaseAddon(abc.ABC): def handle_unload(self, session_manager: SessionManager): pass - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): pass def handle_http_request(self, session_manager: SessionManager, flow: HippoHTTPFlow): @@ -186,9 +185,9 @@ class BaseAddon(abc.ABC): cmd: str, options: List[str], param: str): pass - def handle_proxied_packet(self, session_manager: SessionManager, packet: ProxiedUDPPacket, + def handle_proxied_packet(self, session_manager: SessionManager, packet: UDPPacket, session: Optional[Session], region: Optional[ProxiedRegion], - message: Optional[ProxiedMessage]): + message: Optional[Message]): pass diff --git a/hippolyzer/lib/proxy/addons.py b/hippolyzer/lib/proxy/addons.py index ed30ba7..2e2f681 100644 --- a/hippolyzer/lib/proxy/addons.py +++ b/hippolyzer/lib/proxy/addons.py @@ -22,9 +22,9 @@ from hippolyzer.lib.proxy.task_scheduler import TaskLifeScope, TaskScheduler if TYPE_CHECKING: from hippolyzer.lib.proxy.commands import CommandDetails, WrappedCommandCallable from hippolyzer.lib.proxy.http_flow import HippoHTTPFlow - from hippolyzer.lib.proxy.message import ProxiedMessage + from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.proxy.objects import Object - from hippolyzer.lib.proxy.packets import ProxiedUDPPacket + from hippolyzer.lib.base.network.transport import UDPPacket from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session, SessionManager @@ -385,7 +385,7 @@ class AddonManager: LOG.error(text) @classmethod - def handle_lludp_message(cls, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(cls, session: Session, region: ProxiedRegion, message: Message): cls._reload_addons() if message.name == "ChatFromViewer" and "ChatData" in message: if message["ChatData"]["Channel"] == cls.COMMAND_CHANNEL: @@ -517,8 +517,8 @@ class AddonManager: return cls._call_all_addon_hooks("handle_region_changed", session, region) @classmethod - def handle_proxied_packet(cls, session_manager: SessionManager, packet: ProxiedUDPPacket, + def handle_proxied_packet(cls, session_manager: SessionManager, packet: UDPPacket, session: Optional[Session], region: Optional[ProxiedRegion], - message: Optional[ProxiedMessage]): + message: Optional[Message]): return cls._call_all_addon_hooks("handle_proxied_packet", session_manager, packet, session, region, message) diff --git a/hippolyzer/lib/proxy/circuit.py b/hippolyzer/lib/proxy/circuit.py index d4faa29..af3fb0e 100644 --- a/hippolyzer/lib/proxy/circuit.py +++ b/hippolyzer/lib/proxy/circuit.py @@ -1,36 +1,25 @@ from __future__ import annotations -import asyncio -import datetime as dt import logging from collections import deque from typing import * -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.circuit import Circuit +from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.base.message.msgtypes import PacketFlags -from hippolyzer.lib.base.message.udpserializer import UDPMessageSerializer -from hippolyzer.lib.proxy.packets import Direction, ProxiedUDPPacket -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.network.transport import Direction + +LLUDP_LOGGING_HOOK = Optional[Callable[[Message], Any]] -LLUDP_LOGGING_HOOK = Optional[Callable[[ProxiedMessage], Any]] - - -class ProxiedCircuit: - def __init__(self, near_host, far_host, transport, logging_hook: LLUDP_LOGGING_HOOK = None, - socks_transport: Optional[bool] = None): - self.near_host = near_host - self.host = far_host - self.is_alive = True - self.socks_transport = socks_transport - self.transport: Optional[asyncio.DatagramTransport] = transport +class ProxiedCircuit(Circuit): + def __init__(self, near_host, far_host, transport, logging_hook: LLUDP_LOGGING_HOOK = None): + super().__init__(near_host, far_host, transport) self.in_injections = InjectionTracker(0) self.out_injections = InjectionTracker(0) - self.serializer = UDPMessageSerializer() - self.last_packet_at = dt.datetime.now() self.logging_hook: LLUDP_LOGGING_HOOK = logging_hook - def _send_prepared_message(self, message: ProxiedMessage, direction, transport=None): + def _send_prepared_message(self, message: Message, transport=None): try: serialized = self.serializer.serialize(message) except: @@ -38,25 +27,14 @@ class ProxiedCircuit: raise if self.logging_hook and message.injected: self.logging_hook(message) - return self.send_datagram(serialized, direction, transport=transport) - - def send_datagram(self, data: bytes, direction: Direction, transport=None): - self.last_packet_at = dt.datetime.now() - src_addr, dst_addr = self.host, self.near_host - if direction == Direction.OUT: - src_addr, dst_addr = self.near_host, self.host - - packet = ProxiedUDPPacket(src_addr, dst_addr, data, direction) - packet_data = packet.serialize(socks_header=self.socks_transport) - (transport or self.transport).sendto(packet_data, dst_addr) - return packet + return self.send_datagram(serialized, message.direction, transport=transport) def _get_injections(self, direction: Direction): if direction == Direction.OUT: return self.out_injections, self.in_injections return self.in_injections, self.out_injections - def prepare_message(self, message: ProxiedMessage, direction=None): + def prepare_message(self, message: Message, direction=None): if message.finalized: raise RuntimeError(f"Trying to re-send finalized {message!r}") direction = direction or getattr(message, 'direction') @@ -97,12 +75,7 @@ class ProxiedCircuit: message.send_flags &= ~PacketFlags.ACK return True - def send_message(self, message: ProxiedMessage, direction=None, transport=None): - direction = direction or getattr(message, 'direction') - if self.prepare_message(message, direction): - return self._send_prepared_message(message, direction, transport) - - def _rewrite_packet_ack(self, message: ProxiedMessage, reverse_injections): + def _rewrite_packet_ack(self, message: Message, reverse_injections): new_blocks = [] for block in message["Packets"]: packet_id = block["ID"] @@ -119,14 +92,14 @@ class ProxiedCircuit: message["Packets"] = new_blocks return True - def _rewrite_start_ping_check(self, message: ProxiedMessage, fwd_injections): + def _rewrite_start_ping_check(self, message: Message, fwd_injections): orig_id = message["PingID"]["OldestUnacked"] new_id = fwd_injections.get_effective_id(orig_id) if orig_id != new_id: logging.debug("Rewrote oldest unacked %s -> %s" % (orig_id, new_id)) message["PingID"]["OldestUnacked"] = new_id - def drop_message(self, message: ProxiedMessage, orig_direction=None): + def drop_message(self, message: Message, orig_direction=None): if message.finalized: raise RuntimeError(f"Trying to drop finalized {message!r}") if message.packet_id is None: @@ -135,13 +108,12 @@ class ProxiedCircuit: fwd_injections, reverse_injections = self._get_injections(orig_direction) fwd_injections.mark_dropped(message.packet_id) - if hasattr(message, 'dropped'): - message.dropped = True + message.dropped = True message.finalized = True # Was sent reliably, tell the other end that we saw it and to shut up. if message.reliable: - self._send_acks([message.packet_id], ~orig_direction) + self.send_acks([message.packet_id], ~orig_direction) # This packet had acks for the other end, send them in a separate PacketAck effective_acks = tuple( @@ -149,20 +121,7 @@ class ProxiedCircuit: if not reverse_injections.was_injected(x) ) if effective_acks: - self._send_acks(effective_acks, orig_direction, packet_id=message.packet_id) - - def _send_acks(self, to_ack, direction, packet_id=None): - logging.debug("%r acking %r" % (direction, to_ack)) - # TODO: maybe tack this onto `.acks` for next message? - packet = ProxiedMessage('PacketAck', - *[Block('Packets', ID=x) for x in to_ack]) - packet.packet_id = packet_id - packet.injected = True - packet.direction = direction - self.send_message(packet) - - def __repr__(self): - return "<%s %r : %r>" % (self.__class__.__name__, self.near_host, self.host) + self.send_acks(effective_acks, orig_direction, packet_id=message.packet_id) class InjectionTracker: diff --git a/hippolyzer/lib/proxy/lludp_proxy.py b/hippolyzer/lib/proxy/lludp_proxy.py index 60e9fb8..397d542 100644 --- a/hippolyzer/lib/proxy/lludp_proxy.py +++ b/hippolyzer/lib/proxy/lludp_proxy.py @@ -7,8 +7,8 @@ from hippolyzer.lib.base.message.udpdeserializer import UDPMessageDeserializer from hippolyzer.lib.base.message.udpserializer import UDPMessageSerializer from hippolyzer.lib.base.settings import Settings from hippolyzer.lib.proxy.addons import AddonManager -from hippolyzer.lib.proxy.packets import ProxiedUDPPacket -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.network.transport import UDPPacket +from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session, SessionManager from hippolyzer.lib.proxy.socks_proxy import SOCKS5Server, UDPProxyProtocol @@ -35,11 +35,10 @@ class BaseLLUDPProxyProtocol(UDPProxyProtocol): self.serializer = UDPMessageSerializer() self.deserializer = UDPMessageDeserializer( settings=self.settings, - message_cls=ProxiedMessage, ) self.message_xml = MessageDotXML() - def _ensure_message_allowed(self, msg: ProxiedMessage): + def _ensure_message_allowed(self, msg: Message): if not self.message_xml.validate_udp_msg(msg.name): LOG.warning( f"Received {msg.name!r} over UDP, when it should come over the event queue. Discarding." @@ -53,8 +52,8 @@ class InterceptingLLUDPProxyProtocol(BaseLLUDPProxyProtocol): self.session_manager: SessionManager = session_manager self.session: Optional[Session] = None - def _handle_proxied_packet(self, packet: ProxiedUDPPacket): - message: Optional[ProxiedMessage] = None + def _handle_proxied_packet(self, packet: UDPPacket): + message: Optional[Message] = None region: Optional[ProxiedRegion] = None # Try to do an initial region lookup so we have it for handle_proxied_packet() if self.session: diff --git a/hippolyzer/lib/proxy/message.py b/hippolyzer/lib/proxy/message.py deleted file mode 100644 index 02e143a..0000000 --- a/hippolyzer/lib/proxy/message.py +++ /dev/null @@ -1,286 +0,0 @@ -import ast -import base64 -import importlib -import logging -import math -import os -import re -import uuid -from typing import * - -import hippolyzer.lib.base.datatypes -from hippolyzer.lib.base.datatypes import * -import hippolyzer.lib.base.serialization as se -from hippolyzer.lib.base import llsd -from hippolyzer.lib.base.helpers import HippoPrettyPrinter -from hippolyzer.lib.base.message.message import Message, Block, PacketFlags -import hippolyzer.lib.base.templates as templates -from hippolyzer.lib.base.message.msgtypes import MsgBlockType -from hippolyzer.lib.base.message.template import MessageTemplate -from hippolyzer.lib.proxy.packets import Direction - -_TEMPLATES_MTIME = os.stat(templates.__file__).st_mtime - - -def _maybe_reload_templates(): - # Templates may be modified at runtime during development, check - # if they've changed since startup and reload if they have. - global _TEMPLATES_MTIME - templates_mtime = os.stat(templates.__file__).st_mtime - - if _TEMPLATES_MTIME is None or _TEMPLATES_MTIME < templates_mtime: - print("Reloading templates") - try: - importlib.reload(templates) # type: ignore - _TEMPLATES_MTIME = templates_mtime - except: - logging.exception("Failed to reload templates!") - - -def _trunc_repr(val, max_len): - if isinstance(val, (uuid.UUID, TupleCoord)): - val = str(val) - repr_val = repr(val) - if isinstance(val, str): - repr_val = repr_val[1:-1] - if isinstance(val, bytes): - repr_val = repr_val[2:-1] - if len(repr_val) > max_len: - return repr_val[:max_len] + "…" - return repr_val - - -class VerbatimHumanVal(str): - pass - - -def _filtered_exports(mod): - return {k: getattr(mod, k) for k in mod.__all__} - - -def proxy_eval(eval_str: str, globals_=None, locals_=None): - return eval( - eval_str, - { - "llsd": llsd, - "base64": base64, - "math": math, - **_filtered_exports(hippolyzer.lib.base.datatypes), - **(globals_ or {})}, - locals_ - ) - - -TextSpan = Tuple[int, int] -SpanDict = Dict[Tuple[Union[str, int], ...], TextSpan] - - -class SpannedString(str): - spans: SpanDict = {} - - -class ProxiedMessage(Message): - __slots__ = ("meta", "injected", "dropped", "direction") - - def __init__(self, *args, direction=None, **kwargs): - super().__init__(*args, **kwargs) - self.direction = direction if direction is not None else Direction.OUT - self.meta = {} - self.injected = False - self.dropped = False - _maybe_reload_templates() - - def to_human_string(self, replacements=None, beautify=False, - template: Optional[MessageTemplate] = None) -> SpannedString: - replacements = replacements or {} - _maybe_reload_templates() - spans: SpanDict = {} - string = "" - if self.direction is not None: - string += f'{self.direction.name} ' - string += self.name - if self.packet_id is not None: - string += f'\n# {self.packet_id}: {PacketFlags(self.send_flags)!r}' - string += f'{", DROPPED" if self.dropped else ""}{", INJECTED" if self.injected else ""}' - if self.extra: - string += f'\n# EXTRA: {self.extra!r}' - string += '\n\n' - - for block_name, block_list in self.blocks.items(): - block_suffix = "" - if template and template.get_block(block_name).block_type == MsgBlockType.MBT_VARIABLE: - block_suffix = ' # Variable' - for block_num, block in enumerate(block_list): - string += f"[{block_name}]{block_suffix}\n" - for var_name, val in block.items(): - start_len = len(string) - string += self._format_var(block, var_name, val, replacements, beautify) - end_len = len(string) - # Store the spans for each var so we can highlight specific matches - spans[(self.name, block_name, block_num, var_name)] = (start_len, end_len) - string += "\n" - spanned = SpannedString(string) - spanned.spans = spans - return spanned - - def _format_var(self, block, var_name, var_val, replacements, beautify=False): - string = "" - # Check if we have a more human-readable way to present this field - ser_key = (self.name, block.name, var_name) - serializer = se.SUBFIELD_SERIALIZERS.get(ser_key) - field_prefix = "" - if isinstance(var_val, VerbatimHumanVal): - var_data = var_val - elif isinstance(var_val, (uuid.UUID, TupleCoord)): - var_data = str(var_val) - elif isinstance(var_val, (str, bytes)) and not serializer: - var_data = self._multi_line_pformat(var_val) - else: - var_data = repr(var_val) - if serializer and beautify and not isinstance(var_val, VerbatimHumanVal): - try: - pretty_data = serializer.deserialize(block, var_val, pod=True) - if pretty_data is not se.UNSERIALIZABLE: - string += f" {var_name} =| {self._multi_line_pformat(pretty_data)}" - if serializer.AS_HEX and isinstance(var_val, int): - var_data = hex(var_val) - if serializer.ORIG_INLINE: - string += f" #{var_data}" - return string - else: - string += "\n" - # Human-readable version should be used, orig data is commented out - field_prefix = "#" - except: - logging.exception(f"Failed in subfield serializer {ser_key!r}") - if beautify: - if block.name == "AgentData": - if var_name == "AgentID" and var_val == replacements.get("AGENT_ID"): - var_data = "[[AGENT_ID]]" - elif var_name == "SessionID" and var_val == replacements.get("SESSION_ID"): - var_data = "[[SESSION_ID]]" - if "CircuitCode" in var_name or ("Code" in var_name and "Circuit" in block.name): - if var_val == replacements.get("CIRCUIT_CODE"): - var_data = "[[CIRCUIT_CODE]]" - string += f" {field_prefix}{var_name} = {var_data}" - return string - - @staticmethod - def _multi_line_pformat(val): - printer = HippoPrettyPrinter(width=100) - val = printer.pformat(val) - newstr = "" - # Now we need to rebuild this to add in the appropriate - # line continuations. - lines = list(val.splitlines()) - first_line = True - while lines: - line = lines.pop(0) - prefix = "" - suffix = "" - if first_line: - first_line = False - else: - prefix = " " - - if lines: - suffix = " \\\n" - newstr += f"{prefix}{line}{suffix}" - return newstr - - def to_summary(self): - string = "" - for block_name, block_list in self.blocks.items(): - for block in block_list: - for var_name, val in block.items(): - if block.name == "AgentData" and var_name in ("AgentID", "SessionID"): - continue - if string: - string += ", " - string += f"{var_name}={_trunc_repr(val, 10)}" - return string - - @classmethod - def from_human_string(cls, string, replacements=None, env=None, safe=True): - _maybe_reload_templates() - replacements = replacements or {} - env = env or {} - first_line = True - cur_block = None - msg = None - lines = [x.strip() for x in string.split("\n") if x.strip()] - while lines: - line = lines.pop(0) - # Ignore comment / blank lines - if re.match(r"^\s*(#.*)?$", line): - continue - - if first_line: - direction, message_name = line.split(" ", 1) - msg = ProxiedMessage(message_name) - msg.direction = Direction[direction.upper()] - first_line = False - continue - - if line.startswith("["): - cur_block = Block(re.search(r"\w+", line).group(0)) - msg.add_block(cur_block) - else: - expr_match = re.match(r"^\s*(\w+)\s*(=[|$]*)\s*(.*)$", line) - var_name, operator, var_val = expr_match.groups() - # Multiline, eat all the line continuations - while var_val.endswith("\\"): - var_val = var_val[:-1].rstrip() - if lines: - var_val += lines.pop(0) - - plain = operator == "=" - packed = "|" in operator - evaled = "$" in operator - - if evaled and safe: - raise ValueError("Can't use eval operator in safe mode") - - if plain: - replacement_match = re.match(r"\[\[(\w+)]]", var_val) - if replacement_match: - replacement_name = replacement_match.group(1) - var_val = replacements.get(replacement_name) - if var_val is None: - raise ValueError("Tried to use undefined replacement %s" % replacement_name) - if callable(var_val): - var_val = var_val() - # alternate way of specifying a vector or quat - elif var_val.startswith("<"): - var_val = re.sub(r"[<>]", "", var_val) - var_val = tuple(float(x) for x in var_val.split(",")) - # UUID-ish - elif re.match(r"\A\w+-\w+-.*", var_val): - var_val = UUID(var_val) - else: - var_val = ast.literal_eval(var_val) - - # Normally gross, but necessary for expressiveness in built messages - # unless a metalanguage is added. - if evaled: - var_val = proxy_eval( - var_val, - globals_={**env, **replacements}, - locals_={"block": cur_block} - ) - # Using an packer specific to this message - if packed: - if not evaled: - var_val = ast.literal_eval(var_val) - ser_key = (msg.name, cur_block.name, var_name) - serializer = se.SUBFIELD_SERIALIZERS.get(ser_key) - if not serializer: - raise KeyError(f"No subfield serializer for {ser_key!r}") - var_val = serializer.serialize(cur_block, var_val) - - cur_block[var_name] = var_val - return msg - - def _args_repr(self, pretty=False): - base = super()._args_repr(pretty=pretty) - return f"{base}, direction=Direction.{self.direction.name}" diff --git a/hippolyzer/lib/proxy/message_logger.py b/hippolyzer/lib/proxy/message_logger.py index c8c0c6d..f1fad2b 100644 --- a/hippolyzer/lib/proxy/message_logger.py +++ b/hippolyzer/lib/proxy/message_logger.py @@ -21,7 +21,7 @@ from hippolyzer.lib.proxy.region import CapType if typing.TYPE_CHECKING: from hippolyzer.lib.proxy.http_flow import HippoHTTPFlow - from hippolyzer.lib.proxy.message import ProxiedMessage + from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session @@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__) class BaseMessageLogger: - def log_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def log_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): pass def log_http_response(self, flow: HippoHTTPFlow): @@ -62,7 +62,7 @@ class FilteringMessageLogger(BaseMessageLogger): def set_paused(self, paused: bool): self._paused = paused - def log_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def log_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): if self._paused: return self._add_log_entry(LLUDPMessageLogEntry(message, region, session)) @@ -513,8 +513,8 @@ class EQMessageLogEntry(AbstractMessageLogEntry): class LLUDPMessageLogEntry(AbstractMessageLogEntry): __slots__ = ["_message", "_name", "_direction", "_frozen_message", "_seq", "_deserializer"] - def __init__(self, message: ProxiedMessage, region, session): - self._message: ProxiedMessage = message + def __init__(self, message: Message, region, session): + self._message: Message = message self._deserializer = None self._name = message.name self._direction = message.direction diff --git a/hippolyzer/lib/proxy/namecache.py b/hippolyzer/lib/proxy/namecache.py index bfb97c6..55f69d3 100644 --- a/hippolyzer/lib/proxy/namecache.py +++ b/hippolyzer/lib/proxy/namecache.py @@ -11,7 +11,7 @@ from hippolyzer.lib.proxy.viewer_settings import iter_viewer_cache_dirs if TYPE_CHECKING: from hippolyzer.lib.proxy.http_flow import HippoHTTPFlow - from hippolyzer.lib.proxy.message import ProxiedMessage + from hippolyzer.lib.base.message.message import Message @dataclasses.dataclass @@ -42,7 +42,7 @@ class NameCache: def create_subscriptions( self, - message_handler: MessageHandler[ProxiedMessage], + message_handler: MessageHandler[Message], http_message_handler: MessageHandler[HippoHTTPFlow], ): message_handler.subscribe("UUIDNameReply", self._handle_uuid_name_reply) @@ -83,7 +83,7 @@ class NameCache: entry.display_name = vals["DisplayName"] if vals["DisplayName"] else None self._cache[uuid] = entry - def _handle_uuid_name_reply(self, msg: ProxiedMessage): + def _handle_uuid_name_reply(self, msg: Message): for block in msg.blocks["UUIDNameBlock"]: self.update(block["ID"], { "FirstName": block["FirstName"], diff --git a/hippolyzer/lib/proxy/objects.py b/hippolyzer/lib/proxy/objects.py index 8f1a834..d442a54 100644 --- a/hippolyzer/lib/proxy/objects.py +++ b/hippolyzer/lib/proxy/objects.py @@ -13,7 +13,7 @@ from typing import * from hippolyzer.lib.base import llsd from hippolyzer.lib.base.datatypes import UUID, Vector3 from hippolyzer.lib.base.helpers import proxify -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.base.objects import ( handle_to_global_pos, normalize_object_update, @@ -24,7 +24,6 @@ from hippolyzer.lib.base.objects import ( ) from hippolyzer.lib.proxy.addons import AddonManager from hippolyzer.lib.proxy.http_flow import HippoHTTPFlow -from hippolyzer.lib.proxy.message import ProxiedMessage from hippolyzer.lib.proxy.namecache import NameCache, NameCacheEntry from hippolyzer.lib.base.templates import PCode, ObjectStateSerializer from hippolyzer.lib.proxy.vocache import RegionViewerObjectCacheChain @@ -363,7 +362,7 @@ class ObjectManager: self.untrack_object(obj) self._world_objects.handle_object_gone(obj) - def handle_object_update(self, packet: ProxiedMessage): + def handle_object_update(self, packet: Message): seen_locals = [] for block in packet['ObjectData']: object_data = normalize_object_update(block, self._region.handle) @@ -377,7 +376,7 @@ class ObjectManager: self._track_new_object(obj) packet.meta["ObjectUpdateIDs"] = tuple(seen_locals) - def handle_terse_object_update(self, packet: ProxiedMessage): + def handle_terse_object_update(self, packet: Message): seen_locals = [] for block in packet['ObjectData']: object_data = normalize_terse_object_update(block, self._region.handle) @@ -397,7 +396,7 @@ class ObjectManager: packet.meta["ObjectUpdateIDs"] = tuple(seen_locals) - def handle_object_update_cached(self, packet: ProxiedMessage): + def handle_object_update_cached(self, packet: Message): seen_locals = [] for block in packet['ObjectData']: seen_locals.append(block["ID"]) @@ -425,7 +424,7 @@ class ObjectManager: self.missing_locals.add(block["ID"]) packet.meta["ObjectUpdateIDs"] = tuple(seen_locals) - def handle_object_update_compressed(self, packet: ProxiedMessage): + def handle_object_update_compressed(self, packet: Message): seen_locals = [] for block in packet['ObjectData']: object_data = normalize_object_update_compressed(block, self._region.handle) @@ -438,7 +437,7 @@ class ObjectManager: self._track_new_object(obj) packet.meta["ObjectUpdateIDs"] = tuple(seen_locals) - def _handle_object_properties_generic(self, packet: ProxiedMessage): + def _handle_object_properties_generic(self, packet: Message): seen_locals = [] for block in packet["ObjectData"]: object_properties = dict(block.items()) @@ -465,14 +464,14 @@ class ObjectManager: obj.ObjectCosts.update(object_costs) self.run_object_update_hooks(obj, {"ObjectCosts"}, UpdateType.COSTS) - def _handle_kill_object(self, packet: ProxiedMessage): + def _handle_kill_object(self, packet: Message): seen_locals = [] for block in packet["ObjectData"]: self._kill_object_by_local_id(block["ID"]) seen_locals.append(block["ID"]) packet.meta["ObjectUpdateIDs"] = tuple(seen_locals) - def _handle_coarse_location_update(self, packet: ProxiedMessage): + def _handle_coarse_location_update(self, packet: Message): # TODO: This could lead to weird situations when an avatar crosses a # region border. Might temporarily still have a CoarseLocationUpdate containing # the avatar in the old region, making the avatar appear to be in both regions. @@ -549,8 +548,8 @@ class ObjectManager: *[Block("ObjectData", ObjectLocalID=x) for x in ids_to_req[:100]], ] # Selecting causes ObjectProperties to be sent - self._region.circuit.send_message(ProxiedMessage("ObjectSelect", blocks)) - self._region.circuit.send_message(ProxiedMessage("ObjectDeselect", blocks)) + self._region.circuit.send_message(Message("ObjectSelect", blocks)) + self._region.circuit.send_message(Message("ObjectDeselect", blocks)) ids_to_req = ids_to_req[100:] futures = [] @@ -586,7 +585,7 @@ class ObjectManager: session = self._region.session() ids_to_req = local_ids while ids_to_req: - self._region.circuit.send_message(ProxiedMessage( + self._region.circuit.send_message(Message( "RequestMultipleObjects", Block("AgentData", AgentID=session.agent_id, SessionID=session.id), *[Block("ObjectData", CacheMissType=0, ID=x) for x in ids_to_req[:100]], @@ -619,7 +618,7 @@ class WorldObjectManager: message_handler.subscribe("ObjectUpdateCached", self._handle_object_update_cached) - def _wrap_region_update_handler(self, handler: Callable, message: ProxiedMessage): + def _wrap_region_update_handler(self, handler: Callable, message: Message): """ Dispatch an ObjectUpdate to a region's handler based on RegionHandle @@ -632,16 +631,16 @@ class WorldObjectManager: return return handler(region.objects, message) - def _handle_object_update(self, message: ProxiedMessage): + def _handle_object_update(self, message: Message): self._wrap_region_update_handler(ObjectManager.handle_object_update, message) - def _handle_terse_object_update(self, message: ProxiedMessage): + def _handle_terse_object_update(self, message: Message): self._wrap_region_update_handler(ObjectManager.handle_terse_object_update, message) - def _handle_object_update_compressed(self, message: ProxiedMessage): + def _handle_object_update_compressed(self, message: Message): self._wrap_region_update_handler(ObjectManager.handle_object_update_compressed, message) - def _handle_object_update_cached(self, message: ProxiedMessage): + def _handle_object_update_cached(self, message: Message): self._wrap_region_update_handler(ObjectManager.handle_object_update_cached, message) def handle_new_object(self, obj: Object): diff --git a/hippolyzer/lib/proxy/packets.py b/hippolyzer/lib/proxy/packets.py deleted file mode 100644 index 6dc4fd1..0000000 --- a/hippolyzer/lib/proxy/packets.py +++ /dev/null @@ -1,53 +0,0 @@ -import enum -import socket -import struct -import typing - - -class Direction(enum.Enum): - OUT = enum.auto() - IN = enum.auto() - - def __invert__(self): - if self == self.OUT: - return self.IN - return self.OUT - - -ADDR_TUPLE = typing.Tuple[str, int] - - -class ProxiedUDPPacket: - HEADER_STRUCT = struct.Struct("!HBB4sH") - - def __init__(self, src_addr: ADDR_TUPLE, dst_addr: ADDR_TUPLE, data: bytes, direction: Direction): - self.src_addr = src_addr - self.dst_addr = dst_addr - self.data = data - self.direction = direction - - @property - def outgoing(self): - return self.direction == Direction.OUT - - @property - def incoming(self): - return self.direction == Direction.IN - - @property - def far_addr(self): - if self.outgoing: - return self.dst_addr - return self.src_addr - - def _make_socks_header(self): - return self.HEADER_STRUCT.pack( - 0, 0, 1, socket.inet_aton(self.far_addr[0]), self.far_addr[1]) - - def serialize(self, socks_header=None): - # Decide whether we need a header based on packet direction - if socks_header is None: - socks_header = self.incoming - if not socks_header: - return self.data - return self._make_socks_header() + self.data diff --git a/hippolyzer/lib/proxy/region.py b/hippolyzer/lib/proxy/region.py index bbfa745..8bca26b 100644 --- a/hippolyzer/lib/proxy/region.py +++ b/hippolyzer/lib/proxy/region.py @@ -22,7 +22,7 @@ from hippolyzer.lib.proxy.xfer_manager import XferManager if TYPE_CHECKING: from hippolyzer.lib.proxy.sessions import Session from hippolyzer.lib.proxy.http_flow import HippoHTTPFlow - from hippolyzer.lib.proxy.message import ProxiedMessage + from hippolyzer.lib.base.message.message import Message class CapType(enum.Enum): @@ -57,7 +57,7 @@ class ProxiedRegion: if seed_cap: self._caps["Seed"] = (CapType.NORMAL, seed_cap) self.session: Optional[Callable[[], Session]] = weakref.ref(session) - self.message_handler: MessageHandler[ProxiedMessage] = MessageHandler() + self.message_handler: MessageHandler[Message] = MessageHandler() self.http_message_handler: MessageHandler[HippoHTTPFlow] = MessageHandler() self.eq_manager = EventQueueManager(self) self.caps_client = CapsClient(self) diff --git a/hippolyzer/lib/proxy/sessions.py b/hippolyzer/lib/proxy/sessions.py index db91a1f..ec6d08f 100644 --- a/hippolyzer/lib/proxy/sessions.py +++ b/hippolyzer/lib/proxy/sessions.py @@ -21,7 +21,7 @@ from hippolyzer.lib.proxy.region import ProxiedRegion, CapType if TYPE_CHECKING: from hippolyzer.lib.proxy.message_logger import BaseMessageLogger from hippolyzer.lib.proxy.http_flow import HippoHTTPFlow - from hippolyzer.lib.proxy.message import ProxiedMessage + from hippolyzer.lib.base.message.message import Message class Session: @@ -40,7 +40,7 @@ class Session: self.selected: SelectionModel = SelectionModel() self.regions: List[ProxiedRegion] = [] self.started_at = datetime.datetime.now() - self.message_handler: MessageHandler[ProxiedMessage] = MessageHandler() + self.message_handler: MessageHandler[Message] = MessageHandler() self.http_message_handler: MessageHandler[HippoHTTPFlow] = MessageHandler() self.objects = WorldObjectManager(self) self._main_region = None diff --git a/hippolyzer/lib/proxy/socks_proxy.py b/hippolyzer/lib/proxy/socks_proxy.py index 139d3e0..a79387c 100644 --- a/hippolyzer/lib/proxy/socks_proxy.py +++ b/hippolyzer/lib/proxy/socks_proxy.py @@ -6,7 +6,8 @@ import socket import struct from typing import Optional, List, Tuple -from hippolyzer.lib.proxy.packets import ProxiedUDPPacket, Direction +from hippolyzer.lib.base.network.transport import UDPPacket, Direction +from hippolyzer.lib.proxy.transport import SOCKS5UDPTransport class SOCKS5Server: @@ -145,10 +146,10 @@ class UDPProxyProtocol(asyncio.DatagramProtocol): def __init__(self, source_addr: Tuple[str, int]): self.socks_client_addr: Tuple[str, int] = source_addr self.far_to_near_map = {} - self.transport: Optional[asyncio.DatagramTransport] = None + self.transport: Optional[SOCKS5UDPTransport] = None - def connection_made(self, transport): - self.transport = transport + def connection_made(self, transport: asyncio.DatagramTransport): + self.transport = SOCKS5UDPTransport(transport) def _parse_socks_datagram(self, data): rsv, frag, address_type = struct.unpack("!HBB", data[:4]) @@ -183,7 +184,7 @@ class UDPProxyProtocol(asyncio.DatagramProtocol): # this allows us to have source and dest addr on the same IP # since we expect a send from client->far to happen first self.far_to_near_map[remote_addr] = source_addr - src_packet = ProxiedUDPPacket( + src_packet = UDPPacket( src_addr=source_addr, dst_addr=remote_addr, data=data, @@ -198,7 +199,7 @@ class UDPProxyProtocol(asyncio.DatagramProtocol): logging.warning("Got datagram from unknown host %s:%s" % source_addr) return - src_packet = ProxiedUDPPacket( + src_packet = UDPPacket( src_addr=source_addr, dst_addr=near_addr, data=data, @@ -212,7 +213,7 @@ class UDPProxyProtocol(asyncio.DatagramProtocol): raise def _handle_proxied_packet(self, packet): - self.transport.sendto(packet.serialize(), packet.dst_addr) + self.transport.send_packet(packet) def close(self): logging.info("Closing UDP transport") diff --git a/hippolyzer/lib/proxy/transfer_manager.py b/hippolyzer/lib/proxy/transfer_manager.py index 5a0ce1e..cc9ac1b 100644 --- a/hippolyzer/lib/proxy/transfer_manager.py +++ b/hippolyzer/lib/proxy/transfer_manager.py @@ -8,10 +8,9 @@ import dataclasses from typing import * from hippolyzer.lib.base.datatypes import UUID -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.base.message.message_handler import MessageHandler from hippolyzer.lib.proxy.circuit import ProxiedCircuit -from hippolyzer.lib.proxy.message import ProxiedMessage from hippolyzer.lib.base.templates import ( TransferRequestParamsBase, TransferChannelType, @@ -48,7 +47,7 @@ class Transfer: def cancelled(self) -> bool: return self._future.cancelled() - def is_our_message(self, message: ProxiedMessage): + def is_our_message(self, message: Message): if "TransferData" in message.blocks: transfer_block = message["TransferData"][0] else: @@ -72,7 +71,7 @@ class Transfer: class TransferManager: def __init__( self, - message_handler: MessageHandler[ProxiedMessage], + message_handler: MessageHandler[Message], circuit: ProxiedCircuit, agent_id: Optional[UUID] = None, session_id: Optional[UUID] = None, @@ -98,7 +97,7 @@ class TransferManager: if params_dict.get("SessionID", dataclasses.MISSING) is None: params.SessionID = self._session_id - self._circuit.send_message(ProxiedMessage( + self._circuit.send_message(Message( 'TransferRequest', Block( 'TransferInfo', @@ -121,7 +120,7 @@ class TransferManager: ) as get_msg: while not transfer.done(): try: - msg: ProxiedMessage = await asyncio.wait_for(get_msg(), 5.0) + msg: Message = await asyncio.wait_for(get_msg(), 5.0) except TimeoutError as e: transfer.set_exception(e) return @@ -139,7 +138,7 @@ class TransferManager: ConnectionAbortedError("Unknown failure") ) - def _handle_transfer_packet(self, msg: ProxiedMessage, transfer: Transfer): + def _handle_transfer_packet(self, msg: Message, transfer: Transfer): transfer_block = msg["TransferData"][0] packet_id: int = transfer_block["Packet"] packet_data = transfer_block["Data"] @@ -147,7 +146,7 @@ class TransferManager: if transfer_block["Status"] == TransferStatus.DONE and not transfer.done(): transfer.mark_done() - def _handle_transfer_info(self, msg: ProxiedMessage, transfer: Transfer): + def _handle_transfer_info(self, msg: Message, transfer: Transfer): transfer_block = msg["TransferInfo"][0] transfer.expected_size = transfer_block["Size"] # Don't re-set if we get a resend of packet 0 diff --git a/hippolyzer/lib/proxy/transport.py b/hippolyzer/lib/proxy/transport.py new file mode 100644 index 0000000..9862c85 --- /dev/null +++ b/hippolyzer/lib/proxy/transport.py @@ -0,0 +1,20 @@ +import socket +import struct + +from hippolyzer.lib.base.network.transport import WrappingUDPTransport, UDPPacket + + +class SOCKS5UDPTransport(WrappingUDPTransport): + HEADER_STRUCT = struct.Struct("!HBB4sH") + + @classmethod + def serialize(cls, packet: UDPPacket, force_socks_header: bool = False) -> bytes: + # Decide whether we need a header based on packet direction + if packet.outgoing and not force_socks_header: + return packet.data + header = cls.HEADER_STRUCT.pack( + 0, 0, 1, socket.inet_aton(packet.far_addr[0]), packet.far_addr[1]) + return header + packet.data + + def send_packet(self, packet: UDPPacket) -> None: + self.transport.sendto(self.serialize(packet), packet.dst_addr) diff --git a/hippolyzer/lib/proxy/xfer_manager.py b/hippolyzer/lib/proxy/xfer_manager.py index f7581fd..914dce0 100644 --- a/hippolyzer/lib/proxy/xfer_manager.py +++ b/hippolyzer/lib/proxy/xfer_manager.py @@ -10,12 +10,11 @@ from typing import * from hippolyzer.lib.base.datatypes import UUID, RawBytes from hippolyzer.lib.base.message.data_packer import TemplateDataPacker -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.base.message.message_handler import MessageHandler from hippolyzer.lib.base.message.msgtypes import MsgType from hippolyzer.lib.proxy.circuit import ProxiedCircuit -from hippolyzer.lib.proxy.message import ProxiedMessage -from hippolyzer.lib.proxy.packets import Direction +from hippolyzer.lib.base.network.transport import Direction from hippolyzer.lib.base.templates import XferPacket, XferFilePath, AssetType, XferError _XFER_MESSAGES = {"AbortXfer", "ConfirmXferPacket", "RequestXfer", "SendXferPacket"} @@ -94,7 +93,7 @@ class UploadStrategy(enum.IntEnum): class XferManager: def __init__( self, - message_handler: MessageHandler[ProxiedMessage], + message_handler: MessageHandler[Message], circuit: ProxiedCircuit, secure_session_id: Optional[UUID] = None, ): @@ -114,7 +113,7 @@ class XferManager: direction: Direction = Direction.OUT, ) -> Xfer: xfer_id = xfer_id if xfer_id is not None else random.getrandbits(64) - self._circuit.send_message(ProxiedMessage( + self._circuit.send_message(Message( 'RequestXfer', Block( 'XferID', @@ -139,7 +138,7 @@ class XferManager: ) as get_msg: while not xfer.done(): try: - msg: ProxiedMessage = await asyncio.wait_for(get_msg(), 5.0) + msg: Message = await asyncio.wait_for(get_msg(), 5.0) except asyncio.exceptions.TimeoutError as e: xfer.set_exception(e) return @@ -157,7 +156,7 @@ class XferManager: ConnectionAbortedError(f"Xfer failed with {xfer.error_code!r}") ) - def _handle_send_xfer_packet(self, msg: ProxiedMessage, xfer: Xfer): + def _handle_send_xfer_packet(self, msg: Message, xfer: Xfer): # Received a SendXfer for an Xfer we sent ourselves packet_id: XferPacket = msg["XferID"][0].deserialize_var("Packet") packet_data = msg["DataPacket"]["Data"] @@ -178,7 +177,7 @@ class XferManager: to_ack = range(xfer.next_ackable, ack_max) xfer.next_ackable = ack_max for ack_id in to_ack: - self._circuit.send_message(ProxiedMessage( + self._circuit.send_message(Message( "ConfirmXferPacket", Block("XferID", ID=xfer.xfer_id, Packet=ack_id), direction=xfer.direction, @@ -220,7 +219,7 @@ class XferManager: else: inline_data = data - self._circuit.send_message(ProxiedMessage( + self._circuit.send_message(Message( "AssetUploadRequest", Block( "AssetBlock", @@ -243,12 +242,12 @@ class XferManager: try: # Only need to do this if we're using the xfer upload strategy, otherwise all the # data was already sent in the AssetUploadRequest and we don't expect a RequestXfer. - def request_predicate(request_msg: ProxiedMessage): + def request_predicate(request_msg: Message): return request_msg["XferID"]["VFileID"] == asset_id if xfer is not None: await self.serve_inbound_xfer_request(xfer, request_predicate) - def complete_predicate(complete_msg: ProxiedMessage): + def complete_predicate(complete_msg: Message): return complete_msg["AssetBlock"]["UUID"] == asset_id msg = await message_handler.wait_for('AssetUploadComplete', predicate=complete_predicate) if msg["AssetBlock"]["Success"] == 1: @@ -262,7 +261,7 @@ class XferManager: async def serve_inbound_xfer_request( self, xfer: Xfer, - request_predicate: Callable[[ProxiedMessage], bool], + request_predicate: Callable[[Message], bool], wait_for_confirm: bool = True ): message_handler = self._message_handler @@ -276,7 +275,7 @@ class XferManager: chunk = xfer.chunks.pop(packet_id) # EOF if there are no chunks left packet_val = XferPacket(PacketID=packet_id, IsEOF=not bool(xfer.chunks)) - self._circuit.send_message(ProxiedMessage( + self._circuit.send_message(Message( "SendXferPacket", Block("XferID", ID=xfer.xfer_id, Packet_=packet_val), Block("DataPacket", Data=chunk), diff --git a/tests/base/test_message_wrapper.py b/tests/base/test_message_wrapper.py index cdd1551..72956b9 100644 --- a/tests/base/test_message_wrapper.py +++ b/tests/base/test_message_wrapper.py @@ -163,7 +163,7 @@ class TestMessage(unittest.TestCase): def test_repr(self): expected_repr = r"""Message('ChatFromViewer', Block('AgentData', AgentID=UUID('550e8400-e29b-41d4-a716-446655440000'), SessionID=UUID('550e8400-e29b-41d4-a716-446655440000')), - Block('ChatData', Message='Chatting\n', Type=1, Channel=0))""" + Block('ChatData', Message='Chatting\n', Type=1, Channel=0), direction=Direction.OUT)""" self.assertEqual(expected_repr, repr(self.chat_msg)) @@ -238,3 +238,59 @@ class TestMessageHandlers(unittest.IsolatedAsyncioTestCase): self.assertTrue(msg.queued) # Receiving the message unsubscribes self.assertEqual(len(foo_handlers), 0) + + +class TestMessageSubfieldSerializers(unittest.TestCase): + def setUp(self): + self.chat_msg = Message( + 'ChatFromViewer', + Block('AgentData', + AgentID=UUID('550e8400-e29b-41d4-a716-446655440000'), + SessionID=UUID('550e8400-e29b-41d4-a716-446655440000')), + Block('ChatData', Message="Chatting\n", Type=1, Channel=0)) + + def test_pretty_repr(self): + expected_repr = r"""Message('ChatFromViewer', + Block('AgentData', AgentID=UUID('550e8400-e29b-41d4-a716-446655440000'), SessionID=UUID('550e8400-e29b-41d4-a716-446655440000')), + Block('ChatData', Message='Chatting\n', Type_=ChatType.NORMAL, Channel=0), direction=Direction.OUT)""" + self.assertEqual(expected_repr, self.chat_msg.repr(pretty=True)) + + +class HumanReadableMessageTests(unittest.TestCase): + def test_basic(self): + val = """ + OUT FooMessage + [SomeBlock] + # IGNORE ME + SomeFloat = 1.0 + SomeStr = "baz" + SomeVec = <1,1,1> + [OtherBlock] + UUID = 1f4ffb55-022e-49fb-8c63-6f159aed9b24 + """ + + msg = Message.from_human_string(val) + self.assertEqual(msg.name, "FooMessage") + self.assertEqual(set(msg.blocks.keys()), {"SomeBlock", "OtherBlock"}) + self.assertSequenceEqual(msg["SomeBlock"][0]["SomeVec"], (1.0, 1.0, 1.0)) + self.assertEqual(msg["OtherBlock"][0]["UUID"], UUID("1f4ffb55-022e-49fb-8c63-6f159aed9b24")) + + def test_eval_allowed(self): + val = """ + OUT FooMessage + [SomeBlock] + evaled =$ 1+1 + """ + + msg = Message.from_human_string(val, safe=False) + self.assertEqual(msg["SomeBlock"][0]["evaled"], 2) + + def test_eval_disallowed(self): + val = """ + OUT FooMessage + [SomeBlock] + evaled =$ 1+1 + """ + + with self.assertRaises(ValueError): + Message.from_human_string(val) diff --git a/tests/proxy/__init__.py b/tests/proxy/__init__.py index b3587f3..b516b0b 100644 --- a/tests/proxy/__init__.py +++ b/tests/proxy/__init__.py @@ -4,20 +4,30 @@ import unittest from hippolyzer.lib.base.datatypes import UUID from hippolyzer.lib.base.message.udpserializer import UDPMessageSerializer +from hippolyzer.lib.base.network.transport import AbstractUDPTransport, UDPPacket, ADDR_TUPLE from hippolyzer.lib.proxy.lludp_proxy import InterceptingLLUDPProxyProtocol -from hippolyzer.lib.proxy.message import ProxiedMessage -from hippolyzer.lib.proxy.packets import ProxiedUDPPacket +from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import SessionManager +from hippolyzer.lib.proxy.transport import SOCKS5UDPTransport -class MockTransport(asyncio.DatagramTransport): +class MockTransport(AbstractUDPTransport): + def sendto(self, data: Any, addr: Optional[ADDR_TUPLE] = ...) -> None: + pass + + def abort(self) -> None: + pass + + def close(self) -> None: + pass + def __init__(self): super().__init__() self.packets: List[Tuple[bytes, Tuple[str, int]]] = [] - def sendto(self, data: Any, addr=None) -> None: - self.packets.append((data, addr)) + def send_packet(self, packet: UDPPacket) -> None: + self.packets.append((packet.data, packet.dst_addr)) class BaseProxyTest(unittest.IsolatedAsyncioTestCase): @@ -59,8 +69,8 @@ class BaseProxyTest(unittest.IsolatedAsyncioTestCase): self.session.open_circuit(self.client_addr, region.circuit_addr, self.protocol.transport) - def _msg_to_datagram(self, msg: ProxiedMessage, src, dst, direction, socks_header=True): + def _msg_to_datagram(self, msg: Message, src, dst, direction, socks_header=True): serialized = self.serializer.serialize(msg) - packet = ProxiedUDPPacket(src_addr=src, dst_addr=dst, data=serialized, - direction=direction) - return packet.serialize(socks_header=socks_header) + packet = UDPPacket(src_addr=src, dst_addr=dst, data=serialized, + direction=direction) + return SOCKS5UDPTransport.serialize(packet, force_socks_header=socks_header) diff --git a/tests/proxy/integration/test_addons.py b/tests/proxy/integration/test_addons.py index 73cfc1b..4e1052c 100644 --- a/tests/proxy/integration/test_addons.py +++ b/tests/proxy/integration/test_addons.py @@ -1,6 +1,6 @@ from __future__ import annotations -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.proxy import addon_ctx from hippolyzer.lib.proxy.addon_utils import ( BaseAddon, @@ -10,8 +10,7 @@ from hippolyzer.lib.proxy.addon_utils import ( ) from hippolyzer.lib.proxy.addons import AddonManager from hippolyzer.lib.proxy.commands import handle_command -from hippolyzer.lib.proxy.message import ProxiedMessage -from hippolyzer.lib.proxy.packets import Direction +from hippolyzer.lib.base.network.transport import Direction from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session @@ -40,7 +39,7 @@ class AddonIntegrationTests(BaseProxyTest): AddonManager.shutdown() def _fake_command(self, command: str) -> None: - msg = ProxiedMessage( + msg = Message( "ChatFromViewer", Block("AgentData", AgentID=self.session.agent_id, SessionID=self.session.id), Block("ChatData", Message=command, Channel=AddonManager.COMMAND_CHANNEL, fill_missing=True), diff --git a/tests/proxy/integration/test_lludp.py b/tests/proxy/integration/test_lludp.py index eeab618..4018012 100644 --- a/tests/proxy/integration/test_lludp.py +++ b/tests/proxy/integration/test_lludp.py @@ -9,15 +9,14 @@ from typing import * import lazy_object_proxy from hippolyzer.lib.base.datatypes import UUID -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.base.message.udpdeserializer import UDPMessageDeserializer from hippolyzer.lib.base.objects import Object import hippolyzer.lib.base.serialization as se from hippolyzer.lib.proxy.addon_utils import BaseAddon from hippolyzer.lib.proxy.addons import AddonManager -from hippolyzer.lib.proxy.message import ProxiedMessage from hippolyzer.lib.proxy.message_logger import FilteringMessageLogger, LLUDPMessageLogEntry -from hippolyzer.lib.proxy.packets import ProxiedUDPPacket, Direction +from hippolyzer.lib.base.network.transport import Direction from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session @@ -31,7 +30,7 @@ class MockAddon(BaseAddon): def handle_session_init(self, session: Session): self.events.append(("session_init", session.id)) - def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: ProxiedMessage): + def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): self.events.append(("lludp", session.id, region.circuit_addr, message.name)) if message.name == "UndoLand": # Simulate a message being taken out of the regular proxying flow @@ -53,7 +52,7 @@ class LLUDPIntegrationTests(BaseProxyTest): def setUp(self) -> None: super().setUp() self.addon = MockAddon() - self.deserializer = UDPMessageDeserializer(message_cls=ProxiedMessage) + self.deserializer = UDPMessageDeserializer() AddonManager.init([], self.session_manager, [self.addon]) def _make_objectupdate_compressed(self, localid: Optional[int] = None, handle: Optional[int] = 123): @@ -92,7 +91,7 @@ class LLUDPIntegrationTests(BaseProxyTest): async def test_session_claiming(self): # Need a UseCircuitCode to claim a pending session - msg = ProxiedMessage( + msg = Message( "UseCircuitCode", Block("CircuitCode", Code=self.circuit_code, SessionID=self.session.id, ID=self.session.agent_id), @@ -110,7 +109,7 @@ class LLUDPIntegrationTests(BaseProxyTest): async def test_bad_session_unclaimed(self): # Need a UseCircuitCode to claim a pending session - msg = ProxiedMessage( + msg = Message( "UseCircuitCode", Block("CircuitCode", Code=self.circuit_code, SessionID=UUID.random(), ID=self.session.agent_id), @@ -127,7 +126,7 @@ class LLUDPIntegrationTests(BaseProxyTest): async def test_bad_circuit_not_sent(self): # Need a UseCircuitCode to claim a pending session - msg = ProxiedMessage( + msg = Message( "UseCircuitCode", Block("CircuitCode", Code=self.circuit_code, SessionID=self.session.id, ID=self.session.agent_id), @@ -162,8 +161,6 @@ class LLUDPIntegrationTests(BaseProxyTest): data, dst_addr = packets[0] # Was being sent towards the client self.assertEqual(dst_addr, self.client_addr) - # Which means it has a SOCKS header we need to ignore - data = data[ProxiedUDPPacket.HEADER_STRUCT.size:] # The data should not have changed since we haven't injected # or dropped anything self.assertEqual(obj_update, data) @@ -218,7 +215,7 @@ class LLUDPIntegrationTests(BaseProxyTest): self._setup_default_circuit() obj_update = self._make_objectupdate_compressed(1234) self.protocol.datagram_received(obj_update, self.region_addr) - msg = self.serializer.serialize(ProxiedMessage( + msg = self.serializer.serialize(Message( "UndoLand", Block("AgentData", AgentID=UUID(), SessionID=UUID()), direction=Direction.IN, @@ -234,7 +231,7 @@ class LLUDPIntegrationTests(BaseProxyTest): message_logger = SimpleMessageLogger() self.session_manager.message_logger = message_logger self._setup_default_circuit() - msg = self.serializer.serialize(ProxiedMessage( + msg = self.serializer.serialize(Message( "UndoLand", Block("AgentData", AgentID=UUID(), SessionID=UUID()), direction=Direction.IN, @@ -256,7 +253,7 @@ class LLUDPIntegrationTests(BaseProxyTest): def test_roundtrip_objectupdatecompressed(self): msg_bytes = self._make_objectupdate_compressed() - message: ProxiedMessage = self.deserializer.deserialize(msg_bytes) + message: Message = self.deserializer.deserialize(msg_bytes) for block in itertools.chain(*message.blocks.values()): for var_name in block.vars.keys(): orig_val = block[var_name] diff --git a/tests/proxy/test_message_filter.py b/tests/proxy/test_message_filter.py index ec97546..bf513fe 100644 --- a/tests/proxy/test_message_filter.py +++ b/tests/proxy/test_message_filter.py @@ -3,12 +3,11 @@ import unittest from mitmproxy.test import tflow, tutils from hippolyzer.lib.base.datatypes import Vector3 -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message as Message from hippolyzer.lib.base.message.udpdeserializer import UDPMessageDeserializer from hippolyzer.lib.base.settings import Settings from hippolyzer.lib.proxy.http_flow import HippoHTTPFlow from hippolyzer.lib.proxy.http_proxy import SerializedCapData -from hippolyzer.lib.proxy.message import ProxiedMessage as Message from hippolyzer.lib.proxy.message_logger import LLUDPMessageLogEntry, HTTPMessageLogEntry from hippolyzer.lib.proxy.message_filter import compile_filter from hippolyzer.lib.proxy.sessions import SessionManager @@ -113,7 +112,7 @@ class MessageFilterTests(unittest.TestCase): settings = Settings() settings.ENABLE_DEFERRED_PACKET_PARSING = False settings.HANDLE_PACKETS = False - deser = UDPMessageDeserializer(settings=settings, message_cls=Message) + deser = UDPMessageDeserializer(settings=settings) update_msg = deser.deserialize(OBJECT_UPDATE) entry = LLUDPMessageLogEntry(update_msg, None, None) self.assertTrue(self._filter_matches("ObjectUpdate.ObjectData.ObjectData.Position > (88, 41, 25)", entry)) diff --git a/tests/proxy/test_messages.py b/tests/proxy/test_messages.py index cf205a2..f72cbba 100644 --- a/tests/proxy/test_messages.py +++ b/tests/proxy/test_messages.py @@ -1,12 +1,9 @@ import unittest -import uuid -from hippolyzer.lib.base.datatypes import UUID -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.base.message.msgtypes import PacketFlags from hippolyzer.lib.proxy.circuit import ProxiedCircuit, InjectionTracker -from hippolyzer.lib.proxy.packets import Direction -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.network.transport import Direction class MockedProxyCircuit(ProxiedCircuit): @@ -19,8 +16,8 @@ class MockedProxyCircuit(ProxiedCircuit): self.out_injections = InjectionTracker(0, maxlen=10) self.in_injections = InjectionTracker(0, maxlen=10) - def _send_prepared_message(self, msg: ProxiedMessage, direction, transport=None): - self.sent_simple.append((msg.packet_id, msg.name, direction, msg.injected, msg.acks)) + def _send_prepared_message(self, msg: Message, transport=None): + self.sent_simple.append((msg.packet_id, msg.name, msg.direction, msg.injected, msg.acks)) self.sent_msgs.append(msg) @@ -29,12 +26,12 @@ class PacketIDTests(unittest.TestCase): self.circuit = MockedProxyCircuit() def _send_message(self, msg, outgoing=True): - direction = Direction.OUT if outgoing else Direction.IN - return self.circuit.send_message(msg, direction) + msg.direction = Direction.OUT if outgoing else Direction.IN + return self.circuit.send_message(msg) def test_basic(self): - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=1)) - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=2)) + self._send_message(Message('ChatFromViewer', packet_id=1)) + self._send_message(Message('ChatFromViewer', packet_id=2)) self.assertSequenceEqual(self.circuit.sent_simple, ( (1, "ChatFromViewer", Direction.OUT, False, ()), @@ -42,9 +39,9 @@ class PacketIDTests(unittest.TestCase): )) def test_inject(self): - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=1)) - self._send_message(ProxiedMessage('ChatFromViewer')) - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=2)) + self._send_message(Message('ChatFromViewer', packet_id=1)) + self._send_message(Message('ChatFromViewer')) + self._send_message(Message('ChatFromViewer', packet_id=2)) self.assertSequenceEqual(self.circuit.sent_simple, ( (1, "ChatFromViewer", Direction.OUT, False, ()), @@ -53,17 +50,17 @@ class PacketIDTests(unittest.TestCase): )) def test_max_injected(self): - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=1)) + self._send_message(Message('ChatFromViewer', packet_id=1)) for _ in range(5): - self._send_message(ProxiedMessage('ChatFromViewer')) - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=2)) + self._send_message(Message('ChatFromViewer')) + self._send_message(Message('ChatFromViewer', packet_id=2)) self.assertEqual(self.circuit.out_injections.get_original_id(1), 1) self.assertEqual(self.circuit.out_injections.get_original_id(7), 2) for _ in range(7): - self._send_message(ProxiedMessage('ChatFromViewer')) - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=3)) + self._send_message(Message('ChatFromViewer')) + self._send_message(Message('ChatFromViewer', packet_id=3)) self.assertEqual(len(self.circuit.sent_simple), 15) @@ -78,10 +75,10 @@ class PacketIDTests(unittest.TestCase): self.assertEqual(self.circuit.out_injections.get_original_id(15), 3) def test_inject_hole_in_sequence(self): - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=1)) - self._send_message(ProxiedMessage('ChatFromViewer')) - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=4)) - self._send_message(ProxiedMessage('ChatFromViewer')) + self._send_message(Message('ChatFromViewer', packet_id=1)) + self._send_message(Message('ChatFromViewer')) + self._send_message(Message('ChatFromViewer', packet_id=4)) + self._send_message(Message('ChatFromViewer')) self.assertSequenceEqual(self.circuit.sent_simple, ( (1, "ChatFromViewer", Direction.OUT, False, ()), @@ -91,9 +88,9 @@ class PacketIDTests(unittest.TestCase): )) def test_inject_misordered(self): - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=2)) - self._send_message(ProxiedMessage('ChatFromViewer')) - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=1)) + self._send_message(Message('ChatFromViewer', packet_id=2)) + self._send_message(Message('ChatFromViewer')) + self._send_message(Message('ChatFromViewer', packet_id=1)) self.assertSequenceEqual(self.circuit.sent_simple, [ (2, "ChatFromViewer", Direction.OUT, False, ()), @@ -102,12 +99,12 @@ class PacketIDTests(unittest.TestCase): ]) def test_inject_multiple(self): - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=1)) - self._send_message(ProxiedMessage('ChatFromViewer')) - self._send_message(ProxiedMessage('ChatFromViewer')) - self._send_message(ProxiedMessage('ChatFromViewer')) - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=2)) - self._send_message(ProxiedMessage('ChatFromViewer')) + self._send_message(Message('ChatFromViewer', packet_id=1)) + self._send_message(Message('ChatFromViewer')) + self._send_message(Message('ChatFromViewer')) + self._send_message(Message('ChatFromViewer')) + self._send_message(Message('ChatFromViewer', packet_id=2)) + self._send_message(Message('ChatFromViewer')) self.assertSequenceEqual(self.circuit.sent_simple, [ (1, "ChatFromViewer", Direction.OUT, False, ()), @@ -119,14 +116,14 @@ class PacketIDTests(unittest.TestCase): ]) def test_packet_ack_field_converted(self): - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=1)) - self._send_message(ProxiedMessage('ChatFromViewer')) - self._send_message(ProxiedMessage('ChatFromViewer')) - self._send_message(ProxiedMessage('ChatFromViewer', flags=PacketFlags.RELIABLE)) - self._send_message(ProxiedMessage('ChatFromSimulator', packet_id=1, acks=(2, 3, 4)), outgoing=False) - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=2, flags=PacketFlags.RELIABLE)) - self._send_message(ProxiedMessage('ChatFromSimulator', packet_id=2, acks=[5]), outgoing=False) - self._send_message(ProxiedMessage('ChatFromViewer')) + self._send_message(Message('ChatFromViewer', packet_id=1)) + self._send_message(Message('ChatFromViewer')) + self._send_message(Message('ChatFromViewer')) + self._send_message(Message('ChatFromViewer', flags=PacketFlags.RELIABLE)) + self._send_message(Message('ChatFromSimulator', packet_id=1, acks=(2, 3, 4)), outgoing=False) + self._send_message(Message('ChatFromViewer', packet_id=2, flags=PacketFlags.RELIABLE)) + self._send_message(Message('ChatFromSimulator', packet_id=2, acks=[5]), outgoing=False) + self._send_message(Message('ChatFromViewer')) self.assertSequenceEqual(self.circuit.sent_simple, [ (1, "ChatFromViewer", Direction.OUT, False, ()), @@ -143,12 +140,12 @@ class PacketIDTests(unittest.TestCase): ]) def test_packet_ack_proxied_message_converted(self): - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=1)) - self._send_message(ProxiedMessage('ChatFromViewer')) - self._send_message(ProxiedMessage('ChatFromViewer')) - self._send_message(ProxiedMessage('ChatFromViewer', flags=PacketFlags.RELIABLE)) + self._send_message(Message('ChatFromViewer', packet_id=1)) + self._send_message(Message('ChatFromViewer')) + self._send_message(Message('ChatFromViewer')) + self._send_message(Message('ChatFromViewer', flags=PacketFlags.RELIABLE)) self._send_message( - ProxiedMessage( + Message( 'PacketAck', Block('Packets', ID=2), Block('Packets', ID=3), @@ -157,12 +154,12 @@ class PacketIDTests(unittest.TestCase): ), outgoing=False ) - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=2, flags=PacketFlags.RELIABLE)) + self._send_message(Message('ChatFromViewer', packet_id=2, flags=PacketFlags.RELIABLE)) self._send_message( - ProxiedMessage('PacketAck', Block('Packets', ID=5), packet_id=2), + Message('PacketAck', Block('Packets', ID=5), packet_id=2), outgoing=False ) - self._send_message(ProxiedMessage('ChatFromViewer')) + self._send_message(Message('ChatFromViewer')) self.assertSequenceEqual(self.circuit.sent_simple, [ (1, "ChatFromViewer", Direction.OUT, False, ()), @@ -180,12 +177,12 @@ class PacketIDTests(unittest.TestCase): self.assertEqual(self.circuit.sent_msgs[5]["Packets"][0]["ID"], 2) def test_drop_proxied_message(self): - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=1)) + self._send_message(Message('ChatFromViewer', packet_id=1)) self.circuit.drop_message( - ProxiedMessage('ChatFromViewer', packet_id=2, flags=PacketFlags.RELIABLE), + Message('ChatFromViewer', packet_id=2, flags=PacketFlags.RELIABLE), Direction.OUT, ) - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=3)) + self._send_message(Message('ChatFromViewer', packet_id=3)) self.assertSequenceEqual(self.circuit.sent_simple, [ (1, "ChatFromViewer", Direction.OUT, False, ()), @@ -195,12 +192,12 @@ class PacketIDTests(unittest.TestCase): self.assertEqual(self.circuit.sent_msgs[1]["Packets"][0]["ID"], 2) def test_unreliable_proxied_message(self): - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=1)) + self._send_message(Message('ChatFromViewer', packet_id=1)) self.circuit.drop_message( - ProxiedMessage('ChatFromViewer', packet_id=2), + Message('ChatFromViewer', packet_id=2), Direction.OUT, ) - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=3)) + self._send_message(Message('ChatFromViewer', packet_id=3)) self.assertSequenceEqual(self.circuit.sent_simple, [ (1, "ChatFromViewer", Direction.OUT, False, ()), @@ -208,15 +205,15 @@ class PacketIDTests(unittest.TestCase): ]) def test_dropped_proxied_message_acks_sent(self): - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=1)) - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=2)) - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=3)) - self._send_message(ProxiedMessage('ChatFromSimulator'), outgoing=False) + self._send_message(Message('ChatFromViewer', packet_id=1)) + self._send_message(Message('ChatFromViewer', packet_id=2)) + self._send_message(Message('ChatFromViewer', packet_id=3)) + self._send_message(Message('ChatFromSimulator'), outgoing=False) self.circuit.drop_message( - ProxiedMessage('ChatFromViewer', packet_id=4, acks=(4,)), + Message('ChatFromViewer', packet_id=4, acks=(4,)), Direction.OUT, ) - self._send_message(ProxiedMessage('ChatFromViewer', packet_id=5)) + self._send_message(Message('ChatFromViewer', packet_id=5)) self.assertSequenceEqual(self.circuit.sent_simple, [ (1, "ChatFromViewer", Direction.OUT, False, ()), @@ -233,76 +230,20 @@ class PacketIDTests(unittest.TestCase): self.assertEqual(self.circuit.sent_msgs[4]["Packets"][0]["ID"], 3) def test_resending_or_dropping(self): - self.circuit.send_message(ProxiedMessage('ChatFromViewer', packet_id=1)) - to_drop = ProxiedMessage('ChatFromViewer', packet_id=2, flags=PacketFlags.RELIABLE) - self.circuit.drop_message(to_drop, Direction.OUT) + self.circuit.send_message(Message('ChatFromViewer', packet_id=1)) + to_drop = Message('ChatFromViewer', packet_id=2, flags=PacketFlags.RELIABLE) + self.circuit.drop_message(to_drop) with self.assertRaises(RuntimeError): # Re-dropping the same message should raise - self.circuit.drop_message(to_drop, Direction.OUT) + self.circuit.drop_message(to_drop) # Clears finalized flag to_drop.packet_id = None - self.circuit.send_message(to_drop, Direction.OUT) + self.circuit.send_message(to_drop) with self.assertRaises(RuntimeError): - self.circuit.send_message(to_drop, Direction.OUT) + self.circuit.send_message(to_drop) self.assertSequenceEqual(self.circuit.sent_simple, [ (1, "ChatFromViewer", Direction.OUT, False, ()), (1, "PacketAck", Direction.IN, True, ()), # ended up getting the same packet ID when injected (2, "ChatFromViewer", Direction.OUT, True, ()), ]) - - -class HumanReadableMessageTests(unittest.TestCase): - def test_basic(self): - val = """ - OUT FooMessage - [SomeBlock] - # IGNORE ME - SomeFloat = 1.0 - SomeStr = "baz" - SomeVec = <1,1,1> - [OtherBlock] - UUID = 1f4ffb55-022e-49fb-8c63-6f159aed9b24 - """ - - msg = ProxiedMessage.from_human_string(val) - self.assertEqual(msg.name, "FooMessage") - self.assertEqual(set(msg.blocks.keys()), {"SomeBlock", "OtherBlock"}) - self.assertSequenceEqual(msg["SomeBlock"][0]["SomeVec"], (1.0, 1.0, 1.0)) - self.assertEqual(msg["OtherBlock"][0]["UUID"], uuid.UUID("1f4ffb55-022e-49fb-8c63-6f159aed9b24")) - - def test_eval_allowed(self): - val = """ - OUT FooMessage - [SomeBlock] - evaled =$ 1+1 - """ - - msg = ProxiedMessage.from_human_string(val, safe=False) - self.assertEqual(msg["SomeBlock"][0]["evaled"], 2) - - def test_eval_disallowed(self): - val = """ - OUT FooMessage - [SomeBlock] - evaled =$ 1+1 - """ - - with self.assertRaises(ValueError): - ProxiedMessage.from_human_string(val) - - -class TestMessageSubfieldSerializers(unittest.TestCase): - def setUp(self): - self.chat_msg = ProxiedMessage( - 'ChatFromViewer', - Block('AgentData', - AgentID=UUID('550e8400-e29b-41d4-a716-446655440000'), - SessionID=UUID('550e8400-e29b-41d4-a716-446655440000')), - Block('ChatData', Message="Chatting\n", Type=1, Channel=0)) - - def test_pretty_repr(self): - expected_repr = r"""ProxiedMessage('ChatFromViewer', - Block('AgentData', AgentID=UUID('550e8400-e29b-41d4-a716-446655440000'), SessionID=UUID('550e8400-e29b-41d4-a716-446655440000')), - Block('ChatData', Message='Chatting\n', Type_=ChatType.NORMAL, Channel=0), direction=Direction.OUT)""" - self.assertEqual(expected_repr, self.chat_msg.repr(pretty=True)) diff --git a/tests/proxy/test_object_manager.py b/tests/proxy/test_object_manager.py index e89c915..ac8e4f4 100644 --- a/tests/proxy/test_object_manager.py +++ b/tests/proxy/test_object_manager.py @@ -6,14 +6,13 @@ from typing import * from unittest import mock from hippolyzer.lib.base.datatypes import * -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message as Message from hippolyzer.lib.base.message.udpdeserializer import UDPMessageDeserializer from hippolyzer.lib.base.message.udpserializer import UDPMessageSerializer from hippolyzer.lib.base.objects import Object, normalize_object_update_compressed_data from hippolyzer.lib.base.templates import ExtraParamType from hippolyzer.lib.proxy.addons import AddonManager from hippolyzer.lib.proxy.addon_utils import BaseAddon -from hippolyzer.lib.proxy.message import ProxiedMessage as Message from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.base.templates import PCode from hippolyzer.lib.proxy.vocache import RegionViewerObjectCacheChain, RegionViewerObjectCache, ViewerObjectCacheEntry @@ -69,7 +68,7 @@ class ObjectManagerTestMixin(BaseProxyTest): self.mock_get_region_object_cache_chain.return_value = RegionViewerObjectCacheChain([]) self.object_manager = self.region.objects self.serializer = UDPMessageSerializer() - self.deserializer = UDPMessageDeserializer(message_cls=Message) + self.deserializer = UDPMessageDeserializer() self.object_addon = ObjectTrackingAddon() AddonManager.init([], None, [self.object_addon]) diff --git a/tests/proxy/test_templates.py b/tests/proxy/test_templates.py index 63d7413..6c6edc4 100644 --- a/tests/proxy/test_templates.py +++ b/tests/proxy/test_templates.py @@ -2,7 +2,7 @@ import unittest import hippolyzer.lib.base.serialization as se from hippolyzer.lib.base.datatypes import UUID -from hippolyzer.lib.proxy.message import ProxiedMessage +from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.base.templates import TextureEntrySubfieldSerializer, TEFaceBitfield EXAMPLE_TE = b"\x89UgG$\xcbC\xed\x92\x0bG\xca\xed\x15F_\x08\xe7\xb2\x98\x04\xca\x10;\x85\x94\x05Lj\x8d\xd4" \ @@ -44,7 +44,7 @@ class TemplateTests(unittest.TestCase): 'MediaFlags': {None: {'WebPage': False, 'TexGen': 'DEFAULT', '_Unused': 0}}, 'Glow': {None: 0}, 'Materials': {None: '00000000-0000-0000-0000-000000000000'}, } - msg = ProxiedMessage.from_human_string(f""" + msg = Message.from_human_string(f""" OUT ObjectImage [AgentData] AgentID = {UUID()} @@ -55,7 +55,7 @@ class TemplateTests(unittest.TestCase): TextureEntry =| {repr(pod_te)} """) # Make sure from/to_human_string doesn't change meaning - msg = ProxiedMessage.from_human_string(msg.to_human_string(beautify=True)) + msg = Message.from_human_string(msg.to_human_string(beautify=True)) spec = msg["ObjectData"][0].get_serializer("TextureEntry") deser = spec.deserialize(None, msg["ObjectData"]["TextureEntry"], pod=True) self.assertEqual(deser, pod_te) diff --git a/tests/proxy/test_xfer_transfer.py b/tests/proxy/test_xfer_transfer.py index 827217b..6ae78a1 100644 --- a/tests/proxy/test_xfer_transfer.py +++ b/tests/proxy/test_xfer_transfer.py @@ -4,7 +4,7 @@ import unittest from typing import * from hippolyzer.lib.base.datatypes import UUID -from hippolyzer.lib.base.message.message import Block +from hippolyzer.lib.base.message.message import Block, Message from hippolyzer.lib.base.message.message_handler import MessageHandler from hippolyzer.lib.base.templates import ( AssetType, @@ -16,18 +16,17 @@ from hippolyzer.lib.base.templates import ( TransferStatus, ) from hippolyzer.lib.proxy.circuit import ProxiedCircuit -from hippolyzer.lib.proxy.message import ProxiedMessage -from hippolyzer.lib.proxy.packets import Direction +from hippolyzer.lib.base.network.transport import Direction from hippolyzer.lib.proxy.transfer_manager import TransferManager, Transfer from hippolyzer.lib.proxy.xfer_manager import XferManager class MockHandlingCircuit(ProxiedCircuit): - def __init__(self, handler: MessageHandler[ProxiedMessage]): + def __init__(self, handler: MessageHandler[Message]): super().__init__(("127.0.0.1", 1), ("127.0.0.1", 2), None) self.handler = handler - def _send_prepared_message(self, message: ProxiedMessage, direction, transport=None): + def _send_prepared_message(self, message: Message, transport=None): asyncio.get_event_loop().call_soon(self.handler.handle, message) @@ -36,8 +35,8 @@ class BaseTransferTests(unittest.IsolatedAsyncioTestCase): LARGE_PAYLOAD = b"foobar" * 500 def setUp(self) -> None: - self.server_message_handler: MessageHandler[ProxiedMessage] = MessageHandler() - self.client_message_handler: MessageHandler[ProxiedMessage] = MessageHandler() + self.server_message_handler: MessageHandler[Message] = MessageHandler() + self.client_message_handler: MessageHandler[Message] = MessageHandler() # The client side should send messages to the server side's message handler # and vice-versa self.client_circuit = MockHandlingCircuit(self.server_message_handler) @@ -62,7 +61,7 @@ class XferManagerTests(BaseTransferTests): manager = XferManager(self.server_message_handler, self.server_circuit) xfer = await manager.request(vfile_id=asset_id, vfile_type=AssetType.BODYPART) self.received_bytes = xfer.reassemble_chunks() - self.server_circuit.send_message(ProxiedMessage( + self.server_circuit.send_message(Message( "AssetUploadComplete", Block("AssetBlock", UUID=asset_id, Type=asset_block["Type"], Success=True), direction=Direction.IN, @@ -102,7 +101,7 @@ class TestTransferManager(BaseTransferTests): self.assertEqual(EstateAssetType.COVENANT, params.EstateAssetType) data = self.LARGE_PAYLOAD - self.server_circuit.send_message(ProxiedMessage( + self.server_circuit.send_message(Message( 'TransferInfo', Block( 'TransferInfo', @@ -118,7 +117,7 @@ class TestTransferManager(BaseTransferTests): while True: chunk = data[:1000] data = data[1000:] - self.server_circuit.send_message(ProxiedMessage( + self.server_circuit.send_message(Message( 'TransferPacket', Block( 'TransferData',