Move Circuit and Message to lib.base
Fairly invasive, but will help make lib.base useful again. No more Message / ProxiedMessage split!
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]:
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()])
|
||||
|
||||
65
hippolyzer/lib/base/message/circuit.py
Normal file
65
hippolyzer/lib/base/message/circuit.py
Normal file
@@ -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)
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
71
hippolyzer/lib/base/network/transport.py
Normal file
71
hippolyzer/lib/base/network/transport.py
Normal file
@@ -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()
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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}"
|
||||
@@ -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
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
20
hippolyzer/lib/proxy/transport.py
Normal file
20
hippolyzer/lib/proxy/transport.py
Normal file
@@ -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)
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user