11 Commits

Author SHA1 Message Date
Salad Dais
21d1c7ebfe v0.13.4 2023-12-07 18:47:43 +00:00
Salad Dais
996a43be5b Add option to allow insecure upstream SSL connections 2023-12-07 18:44:10 +00:00
Salad Dais
9e8127e577 Don't use asyncio.get_running_loop() 2023-12-06 20:35:55 +00:00
Salad Dais
cfcd324a11 Pin to Werkzeug under 3.0 2023-12-06 20:35:39 +00:00
Salad Dais
6872634bf4 Be more resilient when faced with no cap_data 2023-12-06 20:35:18 +00:00
Salad Dais
091090c6fd Reparent avatars correctly when recalculating linksets 2023-12-03 23:51:11 +00:00
Salad Dais
bd4fff4200 Add support for PBR / reflection probes 2023-12-03 23:50:32 +00:00
Salad Dais
52dfd0be05 v0.13.3 2023-10-10 23:23:57 +00:00
Salad Dais
60f1737115 Appease new flake8 rules 2023-10-10 23:20:43 +00:00
Salad Dais
7a5d6baf02 Make failing to load invcache non-fatal 2023-10-10 23:15:15 +00:00
Salad Dais
44a332a77b Handle failing to load an addon correctly 2023-10-10 23:14:59 +00:00
16 changed files with 74 additions and 28 deletions

View File

@@ -16,7 +16,7 @@ from hippolyzer.lib.proxy.addons import AddonManager
from hippolyzer.lib.proxy.addon_utils import BaseAddon
from hippolyzer.lib.proxy.ca_utils import setup_ca
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_proxy import create_http_proxy, HTTPFlowContext
from hippolyzer.lib.proxy.http_event_manager import MITMProxyEventManager
from hippolyzer.lib.proxy.lludp_proxy import SLSOCKS5Server
from hippolyzer.lib.base.message.message import Message
@@ -85,12 +85,12 @@ class REPLAddon(BaseAddon):
AddonManager.spawn_repl()
def run_http_proxy_process(proxy_host, http_proxy_port, flow_context: HTTPFlowContext):
def run_http_proxy_process(proxy_host, http_proxy_port, flow_context: HTTPFlowContext, ssl_insecure=False):
mitm_loop = asyncio.new_event_loop()
asyncio.set_event_loop(mitm_loop)
async def mitmproxy_loop():
mitmproxy_master = create_http_proxy(proxy_host, http_proxy_port, flow_context)
mitmproxy_master = create_http_proxy(proxy_host, http_proxy_port, flow_context, ssl_insecure=ssl_insecure)
gc.freeze()
await mitmproxy_master.run()
@@ -98,7 +98,7 @@ def run_http_proxy_process(proxy_host, http_proxy_port, flow_context: HTTPFlowCo
def start_proxy(session_manager: SessionManager, extra_addons: Optional[list] = None,
extra_addon_paths: Optional[list] = None, proxy_host=None):
extra_addon_paths: Optional[list] = None, proxy_host=None, ssl_insecure=False):
extra_addons = extra_addons or []
extra_addon_paths = extra_addon_paths or []
extra_addons.append(SelectionManagerAddon())
@@ -123,17 +123,13 @@ def start_proxy(session_manager: SessionManager, extra_addons: Optional[list] =
# TODO: argparse
if len(sys.argv) == 3:
if sys.argv[1] == "--setup-ca":
try:
mitmproxy_master = create_http_proxy(proxy_host, http_proxy_port, flow_context)
except mitmproxy.exceptions.MitmproxyException:
# Proxy already running, create the master so we don't try to bind to a port
mitmproxy_master = create_proxy_master(proxy_host, http_proxy_port, flow_context)
mitmproxy_master = create_http_proxy(proxy_host, http_proxy_port, flow_context)
setup_ca(sys.argv[2], mitmproxy_master)
return sys.exit(0)
http_proc = multiprocessing.Process(
target=run_http_proxy_process,
args=(proxy_host, http_proxy_port, flow_context),
args=(proxy_host, http_proxy_port, flow_context, ssl_insecure),
daemon=True,
)
http_proc.start()

View File

@@ -42,7 +42,7 @@ from hippolyzer.lib.base.network.transport import Direction, SocketUDPTransport
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 ProxyCapsClient
from hippolyzer.lib.proxy.http_proxy import create_proxy_master, HTTPFlowContext
from hippolyzer.lib.proxy.http_proxy import create_http_proxy, HTTPFlowContext
from hippolyzer.lib.proxy.message_logger import LLUDPMessageLogEntry, AbstractMessageLogEntry, WrappingMessageLogger, \
import_log_entries, export_log_entries
from hippolyzer.lib.proxy.region import ProxiedRegion
@@ -275,9 +275,11 @@ class MessageLogWindow(QtWidgets.QMainWindow):
self.actionOpenMessageBuilder.triggered.connect(self._openMessageBuilder)
self.actionProxyRemotelyAccessible.setChecked(self.settings.REMOTELY_ACCESSIBLE)
self.actionProxySSLInsecure.setChecked(self.settings.SSL_INSECURE)
self.actionUseViewerObjectCache.setChecked(self.settings.USE_VIEWER_OBJECT_CACHE)
self.actionRequestMissingObjects.setChecked(self.settings.AUTOMATICALLY_REQUEST_MISSING_OBJECTS)
self.actionProxyRemotelyAccessible.triggered.connect(self._setProxyRemotelyAccessible)
self.actionProxySSLInsecure.triggered.connect(self._setProxySSLInsecure)
self.actionUseViewerObjectCache.triggered.connect(self._setUseViewerObjectCache)
self.actionRequestMissingObjects.triggered.connect(self._setRequestMissingObjects)
self.actionOpenNewMessageLogWindow.triggered.connect(self._openNewMessageLogWindow)
@@ -458,7 +460,7 @@ class MessageLogWindow(QtWidgets.QMainWindow):
if clicked_btn is not yes_btn:
return
master = create_proxy_master("127.0.0.1", -1, HTTPFlowContext())
master = create_http_proxy("127.0.0.1", -1, HTTPFlowContext())
dirs = setup_ca_everywhere(master)
msg = QtWidgets.QMessageBox()
@@ -474,6 +476,12 @@ class MessageLogWindow(QtWidgets.QMainWindow):
msg.setText("Remote accessibility setting changes will take effect on next run")
msg.exec()
def _setProxySSLInsecure(self, checked: bool):
self.sessionManager.settings.SSL_INSECURE = checked
msg = QtWidgets.QMessageBox()
msg.setText("SSL security setting changes will take effect on next run")
msg.exec()
def _setUseViewerObjectCache(self, checked: bool):
self.sessionManager.settings.USE_VIEWER_OBJECT_CACHE = checked
@@ -937,6 +945,7 @@ def gui_main():
session_manager=window.sessionManager,
extra_addon_paths=window.getAddonList(),
proxy_host=http_host,
ssl_insecure=settings.SSL_INSECURE,
)

View File

@@ -245,7 +245,7 @@
<x>0</x>
<y>0</y>
<width>700</width>
<height>22</height>
<height>29</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@@ -268,6 +268,7 @@
<addaction name="actionProxyRemotelyAccessible"/>
<addaction name="actionUseViewerObjectCache"/>
<addaction name="actionRequestMissingObjects"/>
<addaction name="actionProxySSLInsecure"/>
</widget>
<addaction name="menuFile"/>
</widget>
@@ -342,6 +343,17 @@
<string>Export Log Entries</string>
</property>
</action>
<action name="actionProxySSLInsecure">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Allow Insecure SSL Connections</string>
</property>
<property name="toolTip">
<string>Allow invalid SSL certificates from upstream connections</string>
</property>
</action>
</widget>
<resources/>
<connections/>

View File

@@ -222,7 +222,7 @@ class Message:
def add_blocks(self, block_list):
# can have a list of blocks if it is multiple or variable
for block in block_list:
if type(block) == list:
if type(block) is list:
for bl in block:
self.add_block(bl)
else:

View File

@@ -1917,7 +1917,7 @@ class IntEnumSubfieldSerializer(AdapterInstanceSubfieldSerializer):
val = super().deserialize(ctx_obj, val, pod=pod)
# Don't pretend we were able to deserialize this if we
# had to fall through to the `int` case.
if pod and type(val) == int:
if pod and type(val) is int:
return UNSERIALIZABLE
return val

View File

@@ -1461,6 +1461,8 @@ class ExtraParamType(IntEnum):
RESERVED = 0x50
MESH = 0x60
EXTENDED_MESH = 0x70
RENDER_MATERIAL = 0x80
REFLECTION_PROBE = 0x90
class ExtendedMeshFlags(IntFlag):
@@ -1483,6 +1485,13 @@ class SculptTypeData:
Mirror: bool = se.bitfield_field(bits=1, adapter=se.BoolAdapter())
class ReflectionProbeFlags(IntFlag):
# use a box influence volume
BOX_VOLUME = 0x1
# render dynamic objects (avatars) into this Reflection Probe
DYNAMIC = 0x2
EXTRA_PARAM_TEMPLATES = {
ExtraParamType.FLEXIBLE: se.Template({
"Tension": se.BitField(se.U8, {"Tension": 6, "Softness1": 2}),
@@ -1514,6 +1523,15 @@ EXTRA_PARAM_TEMPLATES = {
ExtraParamType.EXTENDED_MESH: se.Template({
"Flags": se.IntFlag(ExtendedMeshFlags, se.U32),
}),
ExtraParamType.RENDER_MATERIAL: se.Collection(se.U8, se.Template({
"TEIdx": se.U8,
"TEID": se.UUID,
})),
ExtraParamType.REFLECTION_PROBE: se.Template({
"Ambiance": se.F32,
"ClipDistance": se.F32,
"Flags": se.IntFlag(ReflectionProbeFlags, se.U8),
}),
}

View File

@@ -762,7 +762,8 @@ class RegionObjectsState:
def handle_object_reparented(self, obj: Object, old_parent_id: int):
"""Recreate any links to ancestor Objects for obj due to parent changes"""
self._unparent_object(obj, old_parent_id)
self._parent_object(obj, insert_at_head=True)
# Avatars get sent to the _end_ of the child list when reparented
self._parent_object(obj, insert_at_head=obj.PCode != PCode.AVATAR)
def collect_orphans(self, parent_localid: int) -> Sequence[int]:
"""Take ownership of any orphan IDs belonging to parent_localid"""

View File

@@ -174,7 +174,10 @@ class AddonManager:
def load_addon_from_path(cls, path, reload=False, raise_exceptions=True):
path = pathlib.Path(path).absolute()
mod_name = "hippolyzer.user_addon_%s" % path.stem
cls.BASE_ADDON_SPECS.append(importlib.util.spec_from_file_location(mod_name, path))
spec = importlib.util.spec_from_file_location(mod_name, path)
if not spec:
raise ValueError(f"Unable to load {path}")
cls.BASE_ADDON_SPECS.append(spec)
addon_dir = os.path.realpath(pathlib.Path(path).parent.absolute())
if addon_dir not in sys.path:

View File

@@ -224,6 +224,11 @@ class MITMProxyEventManager:
status = flow.response.status_code
cap_data: Optional[CapData] = flow.metadata["cap_data"]
if not cap_data:
# Make sure there's always cap data attached to the flow, even if it's
# empty. Some consumers expect it to always be there, when it might not
# be if the proxy barfed while handling the request.
cap_data = flow.metadata["cap_data"] = CapData()
if status == 200 and cap_data and cap_data.cap_name == "FirestormBridge":
# Fake FirestormBridge cap based on a bridge-like response coming from

View File

@@ -236,7 +236,7 @@ class SLMITMMaster(mitmproxy.master.Master):
)
def create_proxy_master(host, port, flow_context: HTTPFlowContext): # pragma: no cover
def create_http_proxy(host, port, flow_context: HTTPFlowContext, ssl_insecure=False): # pragma: no cover
opts = mitmproxy.options.Options()
master = SLMITMMaster(flow_context, opts)
@@ -251,10 +251,6 @@ def create_proxy_master(host, port, flow_context: HTTPFlowContext): # pragma: n
ssl_verify_upstream_trusted_ca=ca_bundle,
listen_host=host,
listen_port=port,
ssl_insecure=ssl_insecure,
)
return master
def create_http_proxy(bind_host, port, flow_context: HTTPFlowContext): # pragma: no cover
master = create_proxy_master(bind_host, port, flow_context)
return master

View File

@@ -1,4 +1,5 @@
import datetime as dt
import logging
from hippolyzer.lib.base.helpers import get_mtime
from hippolyzer.lib.client.inventory_manager import InventoryManager
@@ -25,4 +26,7 @@ class ProxyInventoryManager(InventoryManager):
newest_cache = inv_cache_path
if newest_cache:
self.load_cache(newest_cache)
try:
self.load_cache(newest_cache)
except:
logging.exception("Failed to load invcache")

View File

@@ -35,3 +35,4 @@ class ProxySettings(Settings):
AUTOMATICALLY_REQUEST_MISSING_OBJECTS: bool = SettingDescriptor(False)
ADDON_SCRIPTS: List[str] = SettingDescriptor(list)
FILTERS: Dict[str, str] = SettingDescriptor(dict)
SSL_INSECURE: bool = SettingDescriptor(False)

View File

@@ -83,7 +83,7 @@ class SOCKS5Server:
try:
# UDP Associate
if cmd == 3:
loop = asyncio.get_running_loop()
loop = asyncio.get_event_loop_policy().get_event_loop()
transport, protocol = await loop.create_datagram_endpoint(
self._udp_protocol_creator(writer.get_extra_info("peername")),
local_addr=('0.0.0.0', 0))

View File

@@ -65,7 +65,7 @@ class TaskScheduler:
task.cancel()
try:
event_loop = asyncio.get_running_loop()
event_loop = asyncio.get_event_loop_policy().get_event_loop()
await_all = asyncio.gather(*(task for task_data, task in self.tasks))
event_loop.run_until_complete(await_all)
except RuntimeError:

View File

@@ -25,7 +25,7 @@ from setuptools import setup, find_packages
here = path.abspath(path.dirname(__file__))
version = '0.13.2'
version = '0.13.4'
with open(path.join(here, 'README.md')) as readme_fh:
readme = readme_fh.read()
@@ -94,6 +94,7 @@ setup(
'idna<3,>=2.5',
# 7.x will be a major change.
'mitmproxy>=8.0.0,<8.1',
'Werkzeug<3.0',
# For REPLs
'ptpython<4.0',
# JP2 codec

View File

@@ -89,7 +89,7 @@ class _MutableMultiDictTests:
d = create_instance()
s = pickle.dumps(d, protocol)
ud = pickle.loads(s)
assert type(ud) == type(d)
assert type(ud) is type(d)
assert ud == d
alternative = pickle.dumps(create_instance("werkzeug"), protocol)
assert pickle.loads(alternative) == d