summaryrefslogtreecommitdiff
path: root/src/hydrilla
diff options
context:
space:
mode:
authorWojtek Kosior <koszko@koszko.org>2022-08-11 21:55:46 +0200
committerWojtek Kosior <koszko@koszko.org>2022-08-11 21:55:46 +0200
commit1960eedc813b878ab193ff84ae10054ca4e0eccd (patch)
tree465b29e8702275a75dedd712f9f415270ccddd18 /src/hydrilla
parenta38d19576c387e505cc468b20ca5b8bcf2fa4759 (diff)
downloadhaketilo-hydrilla-1960eedc813b878ab193ff84ae10054ca4e0eccd.tar.gz
haketilo-hydrilla-1960eedc813b878ab193ff84ae10054ca4e0eccd.zip
serve a stub of meta-site for proxy configuration
Diffstat (limited to 'src/hydrilla')
-rw-r--r--src/hydrilla/mitmproxy_launcher/launch.py1
-rw-r--r--src/hydrilla/proxy/addon.py39
-rw-r--r--src/hydrilla/proxy/policies/__init__.py2
-rw-r--r--src/hydrilla/proxy/policies/payload.py2
-rw-r--r--src/hydrilla/proxy/policies/web_ui.py62
-rw-r--r--src/hydrilla/proxy/state_impl/concrete_state.py9
-rw-r--r--src/hydrilla/proxy/web_ui/__init__.py7
-rw-r--r--src/hydrilla/proxy/web_ui/repos.py49
-rw-r--r--src/hydrilla/proxy/web_ui/root.py66
-rw-r--r--src/hydrilla/translations.py4
10 files changed, 221 insertions, 20 deletions
diff --git a/src/hydrilla/mitmproxy_launcher/launch.py b/src/hydrilla/mitmproxy_launcher/launch.py
index 765a9ce..59ca131 100644
--- a/src/hydrilla/mitmproxy_launcher/launch.py
+++ b/src/hydrilla/mitmproxy_launcher/launch.py
@@ -73,6 +73,7 @@ def launch(port: int, directory: str):
'-p', str(port),
'--set', f'confdir={directory_path / "mitmproxy"}',
'--set', 'upstream_cert=false',
+ '--set', 'connection_strategy=lazy',
'--set', f'haketilo_dir={directory_path}',
'--scripts', str(script_path)])
diff --git a/src/hydrilla/proxy/addon.py b/src/hydrilla/proxy/addon.py
index 06bce86..4058421 100644
--- a/src/hydrilla/proxy/addon.py
+++ b/src/hydrilla/proxy/addon.py
@@ -41,15 +41,7 @@ from threading import Lock
from pathlib import Path
from contextlib import contextmanager
-# for mitmproxy 6.*
-from mitmproxy.net import http
-if not hasattr(http, 'Headers'):
- # for mitmproxy 8.*
- from mitmproxy import http # type: ignore
-
-from mitmproxy.http import HTTPFlow
-
-from mitmproxy import addonmanager, ctx
+from mitmproxy import tls, http, addonmanager, ctx
from mitmproxy.script import concurrent
from ..exceptions import HaketiloException
@@ -127,7 +119,7 @@ class HaketiloAddon:
self.configured = True
- def try_get_policy(self, flow: HTTPFlow, fail_ok: bool = True) -> \
+ def try_get_policy(self, flow: http.HTTPFlow, fail_ok: bool = True) -> \
t.Optional[policies.Policy]:
"""...."""
with self.policies_lock:
@@ -151,16 +143,16 @@ class HaketiloAddon:
return policy
- def get_policy(self, flow: HTTPFlow) -> policies.Policy:
+ def get_policy(self, flow: http.HTTPFlow) -> policies.Policy:
return t.cast(policies.Policy, self.try_get_policy(flow, fail_ok=False))
- def forget_policy(self, flow: HTTPFlow) -> None:
+ def forget_policy(self, flow: http.HTTPFlow) -> None:
"""...."""
with self.policies_lock:
self.flow_policies.pop(id(flow), None)
@contextmanager
- def http_safe_event_handling(self, flow: HTTPFlow) -> t.Iterator:
+ def http_safe_event_handling(self, flow: http.HTTPFlow) -> t.Iterator:
"""...."""
with self.configured_lock:
assert self.configured
@@ -181,7 +173,7 @@ class HaketiloAddon:
self.forget_policy(flow)
@concurrent
- def requestheaders(self, flow: HTTPFlow) -> None:
+ def requestheaders(self, flow: http.HTTPFlow) -> None:
# TODO: don't account for mitmproxy 6 in the code
# Mitmproxy 6 causes even more strange behavior than described below.
# This cannot be easily worked around. Let's just use version 8 and
@@ -201,7 +193,7 @@ class HaketiloAddon:
flow.request.anticache()
@concurrent
- def request(self, flow: HTTPFlow) -> None:
+ def request(self, flow: http.HTTPFlow) -> None:
"""
....
"""
@@ -236,7 +228,7 @@ class HaketiloAddon:
content = result.body
)
- def responseheaders(self, flow: HTTPFlow) -> None:
+ def responseheaders(self, flow: http.HTTPFlow) -> None:
"""
......
"""
@@ -249,7 +241,7 @@ class HaketiloAddon:
flow.response.stream = True
@concurrent
- def response(self, flow: HTTPFlow) -> None:
+ def response(self, flow: http.HTTPFlow) -> None:
"""
......
"""
@@ -276,6 +268,17 @@ class HaketiloAddon:
self.forget_policy(flow)
- def error(self, flow: HTTPFlow) -> None:
+ def tls_clienthello(self, data: tls.ClientHelloData):
+ if data.context.server.address is None:
+ return
+
+ host, port = data.context.server.address
+ if (host == 'hkt.mitm.it' or host.endswith('.hkt.mitm.it')) and \
+ port == 443:
+ return
+
+ data.establish_server_tls_first = True
+
+ def error(self, flow: http.HTTPFlow) -> None:
"""...."""
self.forget_policy(flow)
diff --git a/src/hydrilla/proxy/policies/__init__.py b/src/hydrilla/proxy/policies/__init__.py
index 66e07ee..062ec56 100644
--- a/src/hydrilla/proxy/policies/__init__.py
+++ b/src/hydrilla/proxy/policies/__init__.py
@@ -13,3 +13,5 @@ from .payload_resource import PayloadResourcePolicyFactory
from .rule import RuleBlockPolicyFactory, RuleAllowPolicyFactory
from .fallback import FallbackAllowPolicy, FallbackBlockPolicy, ErrorBlockPolicy
+
+from .web_ui import WebUIPolicyFactory
diff --git a/src/hydrilla/proxy/policies/payload.py b/src/hydrilla/proxy/policies/payload.py
index ad0fa05..577c94e 100644
--- a/src/hydrilla/proxy/policies/payload.py
+++ b/src/hydrilla/proxy/policies/payload.py
@@ -292,7 +292,7 @@ class PayloadSuggestPolicy(PayloadAwarePolicy):
return http_messages.ProducedResponse(200, ((b'a', b'b'),), b'')
-@dc.dataclass(frozen=True, unsafe_hash=True) # type: ignore[misc]
+@dc.dataclass(frozen=True, unsafe_hash=True)
class PayloadPolicyFactory(PayloadAwarePolicyFactory):
"""...."""
def make_policy(self, haketilo_state: state.HaketiloState) \
diff --git a/src/hydrilla/proxy/policies/web_ui.py b/src/hydrilla/proxy/policies/web_ui.py
new file mode 100644
index 0000000..5040278
--- /dev/null
+++ b/src/hydrilla/proxy/policies/web_ui.py
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Policy for serving the web UI from within mitmproxy.
+#
+# This file is part of Hydrilla&Haketilo.
+#
+# Copyright (C) 2022 Wojtek Kosior
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+#
+# I, Wojtek Kosior, thereby promise not to sue for violation of this
+# file's license. Although I request that you do not make use this code
+# in a proprietary program, I am not going to enforce this in court.
+
+"""
+.....
+"""
+
+# Enable using with Python 3.7.
+from __future__ import annotations
+
+import dataclasses as dc
+import typing as t
+
+from ...translations import smart_gettext as _
+from .. import state
+from .. import http_messages
+from .. import web_ui
+from . import base
+
+
+@dc.dataclass(frozen=True)
+class WebUIPolicy(base.Policy):
+ """...."""
+ process_request: t.ClassVar[bool] = True
+
+ priority: t.ClassVar[base.PolicyPriority] = base.PolicyPriority._THREE
+
+ haketilo_state: state.HaketiloState
+
+ def consume_request(self, request_info: http_messages.RequestInfo) \
+ -> http_messages.ProducedResponse:
+ return web_ui.respond(request_info, self.haketilo_state)
+
+
+@dc.dataclass(frozen=True, unsafe_hash=True)
+class WebUIPolicyFactory(base.PolicyFactory):
+ """...."""
+ def make_policy(self, haketilo_state: state.HaketiloState) -> WebUIPolicy:
+ return WebUIPolicy(haketilo_state)
diff --git a/src/hydrilla/proxy/state_impl/concrete_state.py b/src/hydrilla/proxy/state_impl/concrete_state.py
index 0699bf7..bb14734 100644
--- a/src/hydrilla/proxy/state_impl/concrete_state.py
+++ b/src/hydrilla/proxy/state_impl/concrete_state.py
@@ -718,6 +718,15 @@ class ConcreteHaketiloState(base.HaketiloStateWithFields):
)
new_policy_tree = base.PolicyTree()
+
+ ui_factory = policies.WebUIPolicyFactory(builtin=True)
+ web_ui_pattern = 'http*://hkt.mitm.it/***'
+ for parsed_pattern in url_patterns.parse_pattern(web_ui_pattern):
+ new_policy_tree = new_policy_tree.register(
+ parsed_pattern,
+ ui_factory
+ )
+
new_payloads_data: dict[st.PayloadRef, st.PayloadData] = {}
for row in cursor.fetchall():
diff --git a/src/hydrilla/proxy/web_ui/__init__.py b/src/hydrilla/proxy/web_ui/__init__.py
new file mode 100644
index 0000000..ba461b1
--- /dev/null
+++ b/src/hydrilla/proxy/web_ui/__init__.py
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: CC0-1.0
+
+# Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#
+# Available under the terms of Creative Commons Zero v1.0 Universal.
+
+from .root import respond
diff --git a/src/hydrilla/proxy/web_ui/repos.py b/src/hydrilla/proxy/web_ui/repos.py
new file mode 100644
index 0000000..d20791c
--- /dev/null
+++ b/src/hydrilla/proxy/web_ui/repos.py
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Proxy web UI repos view.
+#
+# This file is part of Hydrilla&Haketilo.
+#
+# Copyright (C) 2022 Wojtek Kosior
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+#
+# I, Wojtek Kosior, thereby promise not to sue for violation of this
+# file's license. Although I request that you do not make use this code
+# in a proprietary program, I am not going to enforce this in court.
+
+"""
+.....
+"""
+
+# Enable using with Python 3.7.
+from __future__ import annotations
+
+import typing as t
+
+from ...translations import smart_gettext as _
+from .. import state as st
+from .. import http_messages
+
+def respond(
+ request: http_messages.RequestInfo,
+ rest_of_path: t.Sequence[str],
+ state: st.HaketiloState
+) -> http_messages.ProducedResponse:
+ return http_messages.ProducedResponse(
+ 404,
+ [(b'Content-Type', b'text/plain; charset=utf-8')],
+ _('api.repos_not_implemented').encode()
+ )
diff --git a/src/hydrilla/proxy/web_ui/root.py b/src/hydrilla/proxy/web_ui/root.py
new file mode 100644
index 0000000..dbb86c1
--- /dev/null
+++ b/src/hydrilla/proxy/web_ui/root.py
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Proxy web UI root.
+#
+# This file is part of Hydrilla&Haketilo.
+#
+# Copyright (C) 2022 Wojtek Kosior
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+#
+# I, Wojtek Kosior, thereby promise not to sue for violation of this
+# file's license. Although I request that you do not make use this code
+# in a proprietary program, I am not going to enforce this in court.
+
+"""
+.....
+"""
+
+# Enable using with Python 3.7.
+from __future__ import annotations
+
+import typing as t
+
+from ...translations import smart_gettext as _
+from .. import state as st
+from .. import http_messages
+
+from . import repos
+
+
+HandlerType = t.Callable[
+ [http_messages.RequestInfo, t.Sequence[str], st.HaketiloState],
+ http_messages.ProducedResponse
+]
+
+request_handlers: t.Mapping[str, HandlerType] = {
+ 'repos': repos.respond
+}
+
+def respond(
+ request_info: http_messages.RequestInfo,
+ state: st.HaketiloState
+) -> http_messages.ProducedResponse:
+ if request_info.url.path_segments == ():
+ raise NotImplementedError()
+
+ resource, *rest_of_path = request_info.url.path_segments
+
+ try:
+ handler = request_handlers[resource]
+ except KeyError:
+ raise NotImplementedError()
+
+ return handler(request_info, rest_of_path, state)
diff --git a/src/hydrilla/translations.py b/src/hydrilla/translations.py
index a963e82..84c565a 100644
--- a/src/hydrilla/translations.py
+++ b/src/hydrilla/translations.py
@@ -57,10 +57,12 @@ def select_best_locale() -> str:
use_flask = False
if use_flask:
- return flask.request.accept_languages.best_match(
+ best = flask.request.accept_languages.best_match(
supported_locales,
default=default_locale
)
+ assert best is not None
+ return best
# https://stackoverflow.com/questions/3425294/how-to-detect-the-os-default-language-in-python
# I am not going to surrender to Microbugs' nonfree, crappy OS to test it,