From 90205c9a46d0e03828e7d7417da36dd23f0c54a8 Mon Sep 17 00:00:00 2001 From: TheMythologist Date: Thu, 27 Jul 2023 09:29:17 +0800 Subject: [PATCH 01/16] ci: install upx for building via pyinstaller --- .github/workflows/release.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f9f97c7..9ec59b6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,6 +24,10 @@ jobs: run: python -m pip install --user poetry - name: Install dependencies run: poetry install --no-interaction --no-root + - name: Install UPX + uses: crazy-max/ghaction-upx@v2 + with: + install-only: true - name: Build executable run: poetry run build - uses: softprops/action-gh-release@v1 From e83329d609d2afa2c27de282d5f8cb2185278960 Mon Sep 17 00:00:00 2001 From: TheMythologist Date: Sun, 30 Jul 2023 11:25:05 +0800 Subject: [PATCH 02/16] refactor: use mypy strict type checks (#32) * chore: remove unnecessary module for mypy overrides * refactor: use pathlib import instead of os * refactor: use strict mypy checks --- guardian/config/configdata.py | 21 +++++---- guardian/config/globallist.py | 14 +++--- guardian/menu/lists.py | 26 ++++++----- guardian/menu/menu.py | 72 ++++++++++++++++++------------- guardian/network/sessioninfo.py | 20 +++++---- guardian/network/sessions.py | 21 +++++---- guardian/util/crash.py | 3 +- guardian/util/dynamicblacklist.py | 17 ++++---- guardian/util/network.py | 17 ++++---- guardian/util/singleton.py | 13 ++++-- guardian/util/types.py | 1 + pyproject.toml | 3 +- 12 files changed, 135 insertions(+), 93 deletions(-) create mode 100644 guardian/util/types.py diff --git a/guardian/config/configdata.py b/guardian/config/configdata.py index b0f9a30..e808fc1 100644 --- a/guardian/config/configdata.py +++ b/guardian/config/configdata.py @@ -1,9 +1,12 @@ import json -import os -from typing import TypedDict +from pathlib import Path +from typing import Any, Literal, TypedDict, TypeVar from util.singleton import Singleton +LIST_TYPE = Literal["blacklist", "whitelist"] +T = TypeVar("T", bound=dict[str, str]) + class ConfigDataType(TypedDict): blacklist: dict[str, str] @@ -12,23 +15,23 @@ class ConfigDataType(TypedDict): class ConfigData(metaclass=Singleton): def __init__(self, data_file: str = "data.json"): - self.data_file = data_file + self.data_file = Path(data_file) self.data: ConfigDataType - if os.path.isfile(data_file): + if self.data_file.is_file(): self.load() else: self.data = {"blacklist": {}, "whitelist": {}} self.save() def load(self) -> None: - with open(self.data_file, "r") as file: + with self.data_file.open("r") as file: self.data = json.load(file) def save(self) -> None: - with open(self.data_file, "w") as file: + with self.data_file.open("w") as file: json.dump(self.data, file, indent=4) - def get(self, key: str, default=None): + def get(self, key: LIST_TYPE, default: Any | T = None) -> Any | T: """ Retrieve data from the configuration :param key: Key to find in the config data @@ -37,10 +40,10 @@ def get(self, key: str, default=None): """ return self.data.get(key, default) - def set(self, key: str, value) -> None: + def set(self, key: LIST_TYPE, value: T) -> None: """ Set data to configuration :param key: Key to store the data on the config :param value: Value to store """ - self.data[key] = value # type: ignore[literal-required] + self.data[key] = value diff --git a/guardian/config/globallist.py b/guardian/config/globallist.py index 17d07b6..e3cd510 100644 --- a/guardian/config/globallist.py +++ b/guardian/config/globallist.py @@ -1,15 +1,17 @@ -from typing import Any, Iterable +from typing import Any, Iterator, TypeVar, cast -from config.configdata import ConfigData +from config.configdata import LIST_TYPE, ConfigData from util.singleton import Singleton +T = TypeVar("T") + class GlobalList: """Stores a dictionary mapping from IP address to name. Used for whitelisting or blacklisting IP addresses """ - def __init__(self, list_name: str): + def __init__(self, list_name: LIST_TYPE): self.list_name = list_name self.config = ConfigData() # Sample self.data: {"192.168.0.1", "bad guy"} @@ -26,7 +28,7 @@ def names(self) -> list[str]: def __contains__(self, key: str) -> bool: return key in self.data - def __iter__(self) -> Iterable[tuple[str, Any]]: + def __iter__(self) -> Iterator[tuple[str, Any]]: return iter(self.data.items()) def __len__(self) -> int: @@ -35,7 +37,7 @@ def __len__(self) -> int: def add(self, ip: str, name: str) -> None: self.data[ip] = name - def get(self, ip: str, default=None): + def get(self, ip: str, default: Any | T = None) -> Any | T: return self.data.get(ip, default) def has(self, name: str) -> bool: @@ -54,7 +56,7 @@ def save(self) -> None: self.config.save() def load(self) -> None: - self.data: dict = self.config.get(self.list_name, {}) + self.data = self.config.get(self.list_name, cast(dict[str, str], {})) def reload(self) -> None: self.load() diff --git a/guardian/menu/lists.py b/guardian/menu/lists.py index 1ba32c5..752773d 100644 --- a/guardian/menu/lists.py +++ b/guardian/menu/lists.py @@ -11,7 +11,7 @@ class Lists: name_validators = {Whitelist: NameInWhitelist, Blacklist: NameInBlacklist} @staticmethod - def choose_list(): + def choose_list() -> None: while True: answer = questionary.select( "Which list do you want to edit?", @@ -27,7 +27,7 @@ def choose_list(): Lists.edit_list(answer) @staticmethod - def edit_list(global_list_type: type[Whitelist | Blacklist]): + def edit_list(global_list_type: type[Whitelist | Blacklist]) -> None: while True: answer = questionary.select( f"What do you want to do with {global_list_type.__name__}?", @@ -45,7 +45,7 @@ def edit_list(global_list_type: type[Whitelist | Blacklist]): answer(global_list_type) @staticmethod - def add(global_list_type: type[Whitelist | Blacklist]): + def add(global_list_type: type[Whitelist | Blacklist]) -> None: global_list = global_list_type() name = questionary.text( "Name", style=UI_STYLE, validate=Lists.name_validators[global_list_type] @@ -57,16 +57,16 @@ def add(global_list_type: type[Whitelist | Blacklist]): global_list.save() @staticmethod - def list(global_list_type: type[Whitelist | Blacklist]): + def list(global_list_type: type[Whitelist | Blacklist]) -> None: global_list = global_list_type() if len(global_list): - for ip, name in global_list: # type: ignore[attr-defined] + for ip, name in global_list: print(f"IP Address: {ip}\tName: {name}") else: print(f"No {global_list_type.__name__} entries") @staticmethod - def edit(global_list_type: type[Whitelist | Blacklist]): + def edit(global_list_type: type[Whitelist | Blacklist]) -> None: global_list = global_list_type() while True: name = questionary.select( @@ -75,20 +75,23 @@ def edit(global_list_type: type[Whitelist | Blacklist]): style=UI_STYLE, ).ask() ip = global_list.find(name) + if not ip: + print(f"No ip found with name {name}") + break new_name = questionary.text("Name", style=UI_STYLE, default=name).ask() new_ip = questionary.text( "IP address", style=UI_STYLE, - default=ip, # type: ignore[arg-type] + default=ip, validate=Lists.ip_validators[global_list_type], ).ask() - global_list.remove(ip) # type: ignore[arg-type] + global_list.remove(ip) global_list.add(new_ip, new_name) global_list.save() break @staticmethod - def delete(global_list_type: type[Whitelist | Blacklist]): + def delete(global_list_type: type[Whitelist | Blacklist]) -> None: global_list = global_list_type() name = questionary.select( "Which entry do you want to edit?", @@ -96,5 +99,6 @@ def delete(global_list_type: type[Whitelist | Blacklist]): style=UI_STYLE, ).ask() ip = global_list.find(name) - global_list.remove(ip) # type: ignore[arg-type] - global_list.save() + if ip: + global_list.remove(ip) + global_list.save() diff --git a/guardian/menu/menu.py b/guardian/menu/menu.py index 6b5ea09..5197aac 100644 --- a/guardian/menu/menu.py +++ b/guardian/menu/menu.py @@ -1,7 +1,11 @@ +from __future__ import annotations + import logging import time import webbrowser +from enum import Enum from multiprocessing import Pipe +from typing import Any, cast import questionary from tqdm import trange @@ -52,33 +56,40 @@ def main_menu() -> None: return @staticmethod - def confirm_session(session_type: type[AbstractPacketFilter] | str): + def confirm_session( + session_type: type[AbstractPacketFilter] | str, + ) -> Prompts.CONFIRM_SESSION_ANSWER: pretty_print(Prompts.EXPLANATIONS[session_type]) session_type = ( session_type if isinstance(session_type, str) else session_type.__name__ ) - return questionary.select( - f"Session type: {session_type}, are you sure?", - Prompts.CONFIRM_SESSION_OPTIONS, - style=UI_STYLE, - ).ask() + return cast( + Prompts.CONFIRM_SESSION_ANSWER, + questionary.select( + f"Session type: {session_type}, are you sure?", + Prompts.CONFIRM_SESSION_OPTIONS, + style=UI_STYLE, + ).ask(), + ) @staticmethod - def launch_session(session_type: type[AbstractPacketFilter], *args, **kwargs): + def launch_session( + session_type: type[AbstractPacketFilter], *args: Any, **kwargs: Any + ) -> None: answer = Menu.confirm_session(session_type) - if answer and answer == Prompts.CONFIRM_SESSION_ANSWER_YES: + if answer and answer == Prompts.CONFIRM_SESSION_ANSWER.YES: session = session_type(*args, **kwargs) Menu.context.add_filter(session) Menu.context.start_latest_filter() @staticmethod - def launch_solo_session(): + def launch_solo_session() -> None: Menu.launch_session( SoloSession, priority=Menu.context.priority, connection=Menu.child_conn ) @staticmethod - def launch_whitelisted_session(): + def launch_whitelisted_session() -> None: ip_set = set() debug_logger.debug("Validating whitelisted IPs") for ip, name in Menu.whitelist: @@ -95,7 +106,7 @@ def launch_whitelisted_session(): ) @staticmethod - def launch_blacklisted_session(): + def launch_blacklisted_session() -> None: ip_set = set() debug_logger.debug("Validating blacklisted IPs") for ip, name in Menu.blacklist: @@ -114,23 +125,23 @@ def launch_blacklisted_session(): ) @staticmethod - def launch_locked_session(): + def launch_locked_session() -> None: Menu.launch_session( LockedSession, priority=Menu.context.priority, connection=Menu.child_conn ) @staticmethod - def launch_new_session(): + def launch_new_session() -> None: answer = Menu.confirm_session("New Session") - if answer and answer == Prompts.CONFIRM_SESSION_ANSWER_YES: + if answer and answer == Prompts.CONFIRM_SESSION_ANSWER.YES: session = SoloSession(Menu.context.priority, connection=Menu.child_conn) Menu.context.add_filter(session) Menu.context.start_latest_filter() @staticmethod - def launch_auto_whitelisted_session(): + def launch_auto_whitelisted_session() -> None: answer = Menu.confirm_session("Auto-Whitelisted") - if answer and answer == Prompts.CONFIRM_SESSION_ANSWER_YES: + if answer and answer == Prompts.CONFIRM_SESSION_ANSWER.YES: print("Collecting active IPs...") ip_set = Menu.collect_active_ips() print("Checking for potential tunnels in collected IPs...") @@ -141,7 +152,7 @@ def launch_auto_whitelisted_session(): and ip not in Menu.whitelist } if potential_tunnels: - questionary.checkbox( + ip_answer = questionary.checkbox( f"WARNING! Guardian has detected {len(potential_tunnels)} IP" f"{'' if len(potential_tunnels) == 1 else 's'} in your current session that " "may be used for connection tunnelling, and may break session security if added " @@ -154,10 +165,10 @@ def launch_auto_whitelisted_session(): "direct connection, you can prevent this message from appearing by manually adding " "them to your Whitelist.\n\nSelect the potentially session-security-breaking IPs " "you wish to keep whitelisted, if any.", - choices=potential_tunnels, + choices=list(potential_tunnels), style=UI_STYLE, ).ask() - for ip in answer: + for ip in ip_answer: potential_tunnels.remove(ip) for ip in potential_tunnels: ip_set.remove(ip) @@ -170,9 +181,9 @@ def launch_auto_whitelisted_session(): Menu.context.start_latest_filter() @staticmethod - def kick_unknowns(): + def kick_unknowns() -> None: answer = Menu.confirm_session("Kick Unknowns") - if answer and answer == Prompts.CONFIRM_SESSION_ANSWER_YES: + if answer and answer == Prompts.CONFIRM_SESSION_ANSWER.YES: ip_set = set() for ip, name in Menu.whitelist: try: @@ -190,12 +201,12 @@ def kick_unknowns(): Menu.context.kill_latest_filter() @staticmethod - def kick_by_ip(): + def kick_by_ip() -> None: answer = Menu.confirm_session("Kick by IP") - if answer and answer == Prompts.CONFIRM_SESSION_ANSWER_YES: + if answer and answer == Prompts.CONFIRM_SESSION_ANSWER.YES: ip_set = Menu.collect_active_ips() choices = questionary.checkbox( - "Select IPs to kick", ip_set, style=UI_STYLE + "Select IPs to kick", list(ip_set), style=UI_STYLE ).ask() for ip in choices: ip_set.remove(ip) @@ -209,12 +220,12 @@ def kick_by_ip(): Menu.context.kill_latest_filter() @staticmethod - def open_discord(): + def open_discord() -> None: print("Opening Discord URL...") webbrowser.open(DISCORD_URL) @staticmethod - def quit(): + def quit() -> None: raise KeyboardInterrupt @staticmethod @@ -248,12 +259,13 @@ class Prompts: {"name": "Quit", "value": Menu.quit}, ] - CONFIRM_SESSION_ANSWER_YES = "y" - CONFIRM_SESSION_ANSWER_NO = "n" + class CONFIRM_SESSION_ANSWER(Enum): + YES = "y" + NO = "n" CONFIRM_SESSION_OPTIONS = [ - {"name": "Yes, start", "value": CONFIRM_SESSION_ANSWER_YES}, - {"name": "No, go back", "value": CONFIRM_SESSION_ANSWER_NO}, + {"name": "Yes, start", "value": CONFIRM_SESSION_ANSWER.YES}, + {"name": "No, go back", "value": CONFIRM_SESSION_ANSWER.NO}, ] EXPLANATIONS = { diff --git a/guardian/network/sessioninfo.py b/guardian/network/sessioninfo.py index 82cb3c6..43373f4 100644 --- a/guardian/network/sessioninfo.py +++ b/guardian/network/sessioninfo.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, cast from pydivert import packet @@ -105,10 +105,14 @@ class SessionInfo: connection_stats: Array of ConnectionStats, which contain the calculations and statistics of connections. """ - def __init__( + # TODO: Use `DictProxy` and `ListProxy` instead + # Reference: https://github.com/python/cpython/issues/107431 + def __init__( # type: ignore[no-untyped-def] self, - proxy_dict, + proxy_dict: dict[str, int], proxy_list: list[ConnectionStats], + # Unforunately multiprocessing.queue is not subscriptable here + # Reference: https://github.com/python/cpython/issues/99509#issuecomment-1629764827 proxy_queue, initial_ips: Optional[list[IPTag]] = None, ) -> None: @@ -116,7 +120,7 @@ def __init__( initial_ips = [] self.known_ips = proxy_dict - self.connection_stats: list[ConnectionStats] = proxy_list + self.connection_stats = proxy_list for ip_tag in initial_ips: self.add_con_stat_from_ip_tag(ip_tag) @@ -152,7 +156,7 @@ def add_packet(self, packet: packet.Packet, allowed: bool) -> None: # filtering thread and so processing will happen later (and almost certainly on a different thread). self.packet_queue.put((packet, allowed), block=False) - def process_item(self, block=True) -> None: + def process_item(self, block: bool = True) -> None: """ Depletes the queue of a single packet that has been added from the filtering thread. Note that by default, whatever thread calls this method *will be blocked* until there is an item in the queue. @@ -163,8 +167,8 @@ def process_item(self, block=True) -> None: packet, allowed = self.packet_queue.get(block) self.process_packet(packet, allowed) - def process_packet(self, packet: packet.Packet, allowed) -> None: - ip = packet.src_addr if packet.is_inbound else packet.dst_addr + def process_packet(self, packet: packet.Packet, allowed: bool) -> None: + ip = cast(str, packet.src_addr if packet.is_inbound else packet.dst_addr) # If we're not aware of this destination, a new ConnectionStat (and conseq. IPTag) is required. if ip not in self.known_ips: @@ -194,7 +198,7 @@ def add_con_stat_from_ip_tag(self, ip_tag: IPTag) -> None: self.known_ips[this_ip] = len(self.connection_stats) self.connection_stats.append(ConnectionStats(ip_tag)) - def get_con_stat_from_ip(self, ip): + def get_con_stat_from_ip(self, ip: str) -> ConnectionStats: """ Returns the connection stat object associated with this IP. diff --git a/guardian/network/sessions.py b/guardian/network/sessions.py index d2c718f..0ddb847 100644 --- a/guardian/network/sessions.py +++ b/guardian/network/sessions.py @@ -4,13 +4,14 @@ from abc import ABC, abstractmethod from multiprocessing import Manager, Process from multiprocessing.connection import PipeConnection -from multiprocessing.managers import DictProxy +from multiprocessing.managers import DictProxy, ListProxy from typing import Optional import pydivert from network.sessioninfo import SessionInfo from util.network import ip_in_cidr_block_set +from util.types import CIDR_BLOCK logger = logging.getLogger(__name__) debug_logger = logging.getLogger("debugger") @@ -204,7 +205,7 @@ def __init__( ips: set[str], priority: int, connection: PipeConnection, - blocks: Optional[set] = None, + blocks: Optional[set[CIDR_BLOCK]] = None, known_allowed: Optional[set[str]] = None, session_info: Optional[SessionInfo] = None, debug: bool = False, @@ -314,11 +315,15 @@ def run(self) -> None: else: filler = "Blocked" - if packet.is_inbound: - log = f"[{filler}] {src}:{packet.src_port} --> {dst}:{packet.dst_port}" - else: - log = f"[{filler}] {src}:{packet.src_port} <-- {dst}:{packet.dst_port}" - debug_logger.debug(log) + debug_logger.debug( + "[%s] %s:%s %s %s:%s", + filler, + src, + packet.src_port, + "-->" if packet.is_inbound else "<--", + dst, + packet.dst_port, + ) # Okay, so there's a couple of changes that need to be done to fix auto-whitelisting. @@ -363,7 +368,7 @@ class IPCollector: def __init__(self, priority: int, packet_count_min_threshold: int = 1): self.priority = priority self.process = Process(target=self.run, daemon=True) - self.ips = Manager().list() + self.ips: ListProxy[str] = Manager().list() self.seen_ips: DictProxy[ str, int ] = Manager().dict() # key is IP address, value is packets seen diff --git a/guardian/util/crash.py b/guardian/util/crash.py index 2ed3cff..9c5b25a 100644 --- a/guardian/util/crash.py +++ b/guardian/util/crash.py @@ -1,5 +1,6 @@ import time import traceback +from pathlib import Path from typing import Optional @@ -11,7 +12,7 @@ def crash_report( if filename is None: filename = f"crashreport_{hex(int(time.time_ns()))[2:]}.log" - with open(filename, "w") as handle: + with Path(filename).open("w") as handle: handle.write( f"Report local time: {time.asctime(time.localtime())}\nReport UTC time: {time.asctime(time.gmtime())}\n\n" ) diff --git a/guardian/util/dynamicblacklist.py b/guardian/util/dynamicblacklist.py index f2ad500..0d7de1b 100644 --- a/guardian/util/dynamicblacklist.py +++ b/guardian/util/dynamicblacklist.py @@ -1,12 +1,13 @@ import json -import os.path import re import time +from pathlib import Path import prsw import requests from util.network import construct_cidr_block_set +from util.types import CIDR_BLOCK # This file contains classes and methods to manage acquiring, parsing, and updating a possibly dynamic list of IP ranges # that Guardian needs to be aware of. Such ranges include R* / T2 official IPs, as well as IPs that can be used for @@ -138,6 +139,7 @@ def azure_file_add_timestamp(azure_file_contents: bytes, filename: str) -> bytes def parse_azure_ip_ranges(azure_file_contents: bytes) -> list[str]: + # TODO: Type the json output azure_cloud_json = json.loads(azure_file_contents) categories = azure_cloud_json["values"] arr_ranges = next( @@ -150,27 +152,26 @@ def parse_azure_ip_ranges(azure_file_contents: bytes) -> list[str]: ) if arr_ranges is None: raise ValueError("Could not find AzureCloud category in values array.") - return arr_ranges + return arr_ranges # type: ignore[no-any-return] -def get_dynamic_blacklist(backup_file: str = "db.json") -> set[tuple[int, int]]: +def get_dynamic_blacklist(backup_file: str = "db.json") -> set[CIDR_BLOCK]: # TODO: We can tell if the file has been updated by checking `changeNumber`, but that requires attempting # to download the file anyways. Ideally, we want to be able to skip trying to download all together because # the method isn't entirely reliable, and also fallback to the previously saved version if the download fails. + backup_path = Path(backup_file) try: download_link, content = get_azure_ip_ranges_download() ranges = parse_azure_ip_ranges(content) - with open(backup_file, mode="wb") as file: - file.write(azure_file_add_timestamp(content, download_link)) + backup_path.write_bytes(azure_file_add_timestamp(content, download_link)) ranges.extend(T2_EU) # add R* EU ranges ranges.extend(T2_US) # add R* US ranges except Exception as e: print("ERROR: Could not parse Azure ranges from URL. Reason: ", e) - if not os.path.isfile(backup_file): + if not Path(backup_file).is_file(): raise FileNotFoundError( f"ERROR: Could not find backup file {backup_file}." ) from e - with open(backup_file, mode="rb") as file: - ranges = parse_azure_ip_ranges(file.read()) + ranges = parse_azure_ip_ranges(backup_path.read_bytes()) return construct_cidr_block_set(ranges) diff --git a/guardian/util/network.py b/guardian/util/network.py index eb8b1ff..0fac391 100644 --- a/guardian/util/network.py +++ b/guardian/util/network.py @@ -1,9 +1,11 @@ import contextlib import socket +from typing import cast import requests from util.constants import CIDR_MASKS +from util.types import CIDR_BLOCK def get_public_ip() -> str: @@ -11,11 +13,10 @@ def get_public_ip() -> str: def get_private_ip() -> str: - soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - soc.connect(("8.8.8.8", 80)) - local_ip = soc.getsockname()[0] - soc.close() - return local_ip + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as soc: + soc.connect(("8.8.8.8", 80)) + local_ip = soc.getsockname()[0] + return cast(str, local_ip) def calculate_ip_to_int(ip: str) -> int: @@ -34,7 +35,7 @@ def calculate_ip_to_int(ip: str) -> int: # right-most bit (move one bit left), append /31, and so on. -def ip_in_cidr_block_set(ip: str, cidr_block_set: set[tuple[int, int]]) -> bool: +def ip_in_cidr_block_set(ip: str, cidr_block_set: set[CIDR_BLOCK]) -> bool: """ Essentially a reverse-search for all possible entries in cidr_block_set that would contain ip. """ @@ -45,7 +46,7 @@ def ip_in_cidr_block_set(ip: str, cidr_block_set: set[tuple[int, int]]) -> bool: return False -def cidr_to_tuple(ip_in_cidr: str) -> tuple[int, int]: +def cidr_to_tuple(ip_in_cidr: str) -> CIDR_BLOCK: """ Converts a string representing an IP in CIDR notation to two integers, the first integer represents the lowest IP in the CIDR block, @@ -59,7 +60,7 @@ def cidr_to_tuple(ip_in_cidr: str) -> tuple[int, int]: return calculate_ip_to_int(ip), suffix_int -def construct_cidr_block_set(ips_in_cidr: list[str]) -> set[tuple[int, int]]: +def construct_cidr_block_set(ips_in_cidr: list[str]) -> set[CIDR_BLOCK]: """ Construct a set of IPs in CIDR notation. This set is specifically optimised to only work with the ip_in_cidr_block_set() function. diff --git a/guardian/util/singleton.py b/guardian/util/singleton.py index f42dc7a..42cbb41 100644 --- a/guardian/util/singleton.py +++ b/guardian/util/singleton.py @@ -1,7 +1,14 @@ -class Singleton(type): - _instances: dict = {} +from __future__ import annotations - def __call__(cls, *args, **kwargs): +from typing import Any, Generic, TypeVar + +T = TypeVar("T") + + +class Singleton(type, Generic[T]): + _instances: dict[Singleton[T], T] = {} + + def __call__(cls: Singleton[T], *args: Any, **kwargs: Any) -> T: if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] diff --git a/guardian/util/types.py b/guardian/util/types.py new file mode 100644 index 0000000..c69b04c --- /dev/null +++ b/guardian/util/types.py @@ -0,0 +1 @@ +CIDR_BLOCK = tuple[int, int] diff --git a/pyproject.toml b/pyproject.toml index 99e1fff..c47e9e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,9 +42,10 @@ src_paths = ["guardian", "tests"] [tool.mypy] platform = "win32" exclude = ["speed_tests/", "tests/", "build.py"] +strict = true [[tool.mypy.overrides]] -module = ["cx_Freeze", "pydivert", "prsw"] +module = ["pydivert", "prsw"] ignore_missing_imports = true [build-system] From 43a19130a671377e673b70981421dce306c99684 Mon Sep 17 00:00:00 2001 From: TheMythologist Date: Sun, 30 Jul 2023 21:30:48 +0800 Subject: [PATCH 03/16] chore: update README.md --- README.md | 85 ++++++++++++++++++------------------------- guardian/menu/menu.py | 2 +- 2 files changed, 37 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index b34d159..2338422 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,34 @@ # Guardian -Custom firewall used for the game GTA Online (1.54 and onwards), written in Python. +Custom firewall for the game GTA Online (version 1.54 and onwards). -## How it works - -Guardian intercepts all incoming GTA traffic, and only allows specific packets through depending on the configuration. GTA service-related packets are still allowed so you can communicate with GTA servers, but other players will not be able to join your session (unless you specify their IP addresses in the whitelist configuration). +## [Download the latest version](https://github.com/TheMythologist/guardian/releases/latest) -By observing network activity while playing GTA Online, it is discovered that the "type" of packet can be determined based on the packet's payload size even though they are encrypted. Other than user-defined configuration, the only other behaviours intended to be allowed through are the session "heartbeat" and any session information requests from the "matchmaking service" which provides initial connection details to clients. +### Requirements -## [Download 3.4.1 (latest)](https://github.com/TheMythologist/guardian/releases/tag/3.4.1) +- Windows 8/10/11 or Windows Server 2012 64-bit +- Administrator Privileges ## How to use -To increase the chance of a successful session, it is recommended that you follow these instructions: - 1. Download the latest version from the [releases](https://github.com/TheMythologist/guardian/releases) -2. Unzip the zipfile -3. Run `Guardian.exe` as Administrator -4. Start a **Solo Session** with Guardian -5. Launch GTA online and enjoy 🎉 -\- If you want to let your friends in and have added their IP addresses `Whitelist`, stop the **Solo Session** and start a **Whitelisted Session**. -\- Your session should now be secure, and your friends can join you! 🎉 -6. If you don't know your friends' IPs, you'll have to stop the **Solo Session** and tell them to join as quick as possible. -\- Note that the session is vulnerable to randoms during this time. -7. Once your friends are loading into your session (they've confirmed they want to join your session and are now in the clouds), start a **Locked Session**. -\- While a session is Locked, no one will be able to join the session, but those already connecting / connected should remain. +2. Run `Guardian.exe` (you will be prompted to run as Administrator) +3. Start a **Solo Session** with Guardian +4. Launch GTA online and enjoy 🎉 + - If you want to let your friends in, add their IP addresses into the `Whitelist` and use a **Whitelisted Session** instead. +5. If you don't know your friends' IPs, you'll have to stop the **Solo Session** and tell them to join as quick as possible. + - Note that the session is vulnerable to randoms during this time. +6. Once your friends are loading into your session (they've confirmed they want to join your session and are now in the clouds), start a **Locked Session**. + - While a session is Locked, no one will be able to join the session, but those already connecting / connected should remain. Guardian *may* work in other circumstances/setups, but is less likely to produce secure sessions. +## How it works + +Guardian intercepts all incoming GTA traffic, and only allows specific packets through depending on the configuration. GTA service-related packets are still allowed so you can communicate with GTA servers, but other players will not be able to join your session (unless you specify their IP addresses in the whitelist configuration). + +By observing network activity while playing GTA Online, it is discovered that the "type" of packet can be determined based on the packet's payload size even though they are encrypted. Other than user-defined configuration, the only other behaviours intended to be allowed through are the session "heartbeat" and any session information requests from the "matchmaking service" which provides initial connection details to clients. + ## Session Types Guardian has many different kinds of sessions, each with different behaviours intended to be used under different circumstances. @@ -53,30 +54,10 @@ The most important requirement for securing a session with Guardian is that you GTA Online on PC was too crazy with modders wreaking havoc and constantly spamming text messages or emails. They could also crash sessions, leak IPs, or even scrape R* IDs to join non-public sessions to continue harrassing people. Speyedr did some research and testing, and was eventually able to get Guardian to work again, and he publicly shared it with the open-source community (check out his repository [here](https://gitlab.com/Speyedr/guardian-fastload-fix)). I then decided to fork his own project and improve on the codebase further, as well as further improvements that I think the codebase can benefit from. -- [Requirements](#requirements) - - [System](#system) - - [Packages](#packages-only-if-building-from-source) -- [Build from source](#build-from-source) -- [Miscellaneous](#miscellaneous) -- [Credits](#credits-for-this-fork) - - [Developers](#developers) -- [License](LICENSE) - -## Requirements - -### System - -- Python 3.10+ 64-bit -- Windows 8/10/11 or Windows Server 2012 64 bit -- Administrator Privileges - -### Packages *(only if building from source)* - -- View the section `tool.poetry.dependencies` in [pyproject.toml](pyproject.toml) - ## Build from source -- Install poetry. +- Install Python 3.10+ +- **(Recommended)** Use poetry. ```bash pip install poetry @@ -88,7 +69,7 @@ GTA Online on PC was too crazy with modders wreaking havoc and constantly spammi poetry install ``` -- Build the package from the top-level repo folder. +- Build the package from the top-level repo folder. The executable will be found in the `dist` directory. ```bash poetry run build @@ -108,16 +89,22 @@ GTA Online on PC was too crazy with modders wreaking havoc and constantly spammi - Getting banned by R* (unlikely to happen) - Still getting hacked/harrassed by modders despite using the tool -## Support - -- [**> Open an issue**](https://github.com/TheMythologist/guardian/issues/new) -- [**> Join Speyedr's Discord server**](https://discord.gg/6FzKCh4j4v) - -## Credits (for this fork) +## Key differences in this fork -- [**DigitalArc Studio**](https://gitlab.com/digitalarc/guardian) -- [**Speyedr**](https://gitlab.com/Speyedr/guardian-fastload-fix) +- Single executable binary + - Just double-click and run! +- Able to change session type safely and securely with 0 downtime in-between +- Smaller package size +- UI overhaul +- Better developing experience (in my opinion, anyways) ## Developers - [**TheMythologist**](https://github.com/TheMythologist) +- [**Speyedr**](https://gitlab.com/Speyedr/guardian-fastload-fix) (previous fork) +- [**DigitalArc Studio**](https://gitlab.com/digitalarc/guardian) (original fork) + +## Support + +- [**> Open an issue**](https://github.com/TheMythologist/guardian/issues/new) +- [**> Join Speyedr's Discord server**](https://discord.gg/6FzKCh4j4v) diff --git a/guardian/menu/menu.py b/guardian/menu/menu.py index 5197aac..581f60a 100644 --- a/guardian/menu/menu.py +++ b/guardian/menu/menu.py @@ -255,7 +255,7 @@ class Prompts: }, {"name": "Kick by IP", "value": Menu.kick_by_ip}, {"name": "Edit lists", "value": Lists.choose_list}, - {"name": "Discord", "value": Menu.open_discord}, + {"name": "Speyedr's Discord", "value": Menu.open_discord}, {"name": "Quit", "value": Menu.quit}, ] From 41a6beedfbd1631fc679af6d552b91246150430d Mon Sep 17 00:00:00 2001 From: TheMythologist Date: Sun, 30 Jul 2023 22:15:20 +0800 Subject: [PATCH 04/16] ci: rename release.yml to build.yml --- .github/workflows/{release.yml => build.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{release.yml => build.yml} (98%) diff --git a/.github/workflows/release.yml b/.github/workflows/build.yml similarity index 98% rename from .github/workflows/release.yml rename to .github/workflows/build.yml index 9ec59b6..674a508 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: release +name: build on: push: From 090012b60a375238819c7f74b65c0544b15153e0 Mon Sep 17 00:00:00 2001 From: TheMythologist Date: Sun, 30 Jul 2023 22:25:29 +0800 Subject: [PATCH 05/16] chore: add badges to README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 2338422..5f76207 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,11 @@ Custom firewall for the game GTA Online (version 1.54 and onwards). +[![Latest release](https://img.shields.io/github/v/release/TheMythologist/guardian)](https://github.com/TheMythologist/guardian/releases/latest) +[![CI status](https://github.com/TheMythologist/guardian/workflows/build/badge.svg)](https://github.com/TheMythologist/guardian/actions?query=branch%3Amain) +[![Downloads](https://img.shields.io/github/downloads/TheMythologist/guardian/total)](https://github.com/TheMythologist/guardian/releases) +[![License](https://img.shields.io/github/license/TheMythologist/guardian)](LICENSE) + ## [Download the latest version](https://github.com/TheMythologist/guardian/releases/latest) ### Requirements From 04b9bc23861bccb4f05aa12dff4c30880e76b25c Mon Sep 17 00:00:00 2001 From: TheMythologist Date: Mon, 31 Jul 2023 06:55:14 +0800 Subject: [PATCH 06/16] chore: update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5f76207..4c365ea 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ GTA Online on PC was too crazy with modders wreaking havoc and constantly spammi - Just double-click and run! - Able to change session type safely and securely with 0 downtime in-between - Smaller package size +- Speed improvements - UI overhaul - Better developing experience (in my opinion, anyways) From 4b65095029d4d782ba7bd5386f993fa1fb0c03d4 Mon Sep 17 00:00:00 2001 From: TheMythologist Date: Mon, 31 Jul 2023 11:17:39 +0800 Subject: [PATCH 07/16] chore: create FUNDING.yml --- .github/FUNDING.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..40524c3 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,11 @@ +github: [TheMythologist] +# patreon: +# open_collective: +# ko_fi: +# tidelift: +# community_bridge: +# liberapay: +# issuehunt: +# otechie: +# lfx_crowdfunding: +# custom: From 682650cb06b32391f3db3c6f4207634b70fe8d86 Mon Sep 17 00:00:00 2001 From: TheMythologist Date: Tue, 1 Aug 2023 15:56:22 +0800 Subject: [PATCH 08/16] ci: create dependabot.yml --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6a7695c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" From 2a48db9f6ad56b7dd7df1a51dfb91d7b27da1730 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 16:23:44 +0800 Subject: [PATCH 09/16] build(deps-dev): bump pre-commit from 2.21.0 to 3.3.3 (#37) Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.21.0 to 3.3.3. - [Release notes](https://github.com/pre-commit/pre-commit/releases) - [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md) - [Commits](https://github.com/pre-commit/pre-commit/compare/v2.21.0...v3.3.3) --- updated-dependencies: - dependency-name: pre-commit dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 10 +++++----- pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 55eee38..cb6aeb5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -450,13 +450,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "2.21.0" +version = "3.3.3" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, - {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, + {file = "pre_commit-3.3.3-py2.py3-none-any.whl", hash = "sha256:10badb65d6a38caff29703362271d7dca483d01da88f9d7e05d0b97171c136cb"}, + {file = "pre_commit-3.3.3.tar.gz", hash = "sha256:a2256f489cd913d575c145132ae196fe335da32d91a8294b7afe6622335dd023"}, ] [package.dependencies] @@ -872,4 +872,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.10.0,<3.12" -content-hash = "f543acc2998891ae4b4b7dcff6f20d30eceb72ddf36ed6ca7ba7b60a4b9cf853" +content-hash = "3ebd121169a0336ef294124bc421fd8db68f8eaf878392c8d5adc2b42eb14af3" diff --git a/pyproject.toml b/pyproject.toml index c47e9e0..459f9e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ black = "^22.12.0" isort = "^5.11.4" flake8 = "^6.0.0" pyproject-flake8 = "^6.0.0.post1" -pre-commit = "^2.21.0" +pre-commit = "^3.3.3" mypy = "^0.991" types-colorama = "^0.4.15.4" types-tqdm = "^4.64.7.11" From 96544c0432cdb3f0645f849cd73c530dc88d9b11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 16:36:59 +0800 Subject: [PATCH 10/16] build(deps-dev): bump black from 22.12.0 to 23.7.0 (#36) Bumps [black](https://github.com/psf/black) from 22.12.0 to 23.7.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/22.12.0...23.7.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 43 +++++++++++++++++++++++++++---------------- pyproject.toml | 2 +- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/poetry.lock b/poetry.lock index cb6aeb5..56fe732 100644 --- a/poetry.lock +++ b/poetry.lock @@ -13,31 +13,42 @@ files = [ [[package]] name = "black" -version = "22.12.0" +version = "23.7.0" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, - {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, - {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, - {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, - {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, - {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, - {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, - {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, - {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, - {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, - {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, - {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, + {file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"}, + {file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"}, + {file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"}, + {file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"}, + {file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"}, + {file = "black-23.7.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2"}, + {file = "black-23.7.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd"}, + {file = "black-23.7.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a"}, + {file = "black-23.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926"}, + {file = "black-23.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad"}, + {file = "black-23.7.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f"}, + {file = "black-23.7.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3"}, + {file = "black-23.7.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6"}, + {file = "black-23.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a"}, + {file = "black-23.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320"}, + {file = "black-23.7.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9"}, + {file = "black-23.7.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3"}, + {file = "black-23.7.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087"}, + {file = "black-23.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91"}, + {file = "black-23.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491"}, + {file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"}, + {file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"}, ] [package.dependencies] click = ">=8.0.0" mypy-extensions = ">=0.4.3" +packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] @@ -872,4 +883,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.10.0,<3.12" -content-hash = "3ebd121169a0336ef294124bc421fd8db68f8eaf878392c8d5adc2b42eb14af3" +content-hash = "f71586be33854b6b7b5d26c01d08d1ca6f1be925e883489fa35ac634ef7cd67d" diff --git a/pyproject.toml b/pyproject.toml index 459f9e6..9f2b874 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ requests = "^2.31.0" prsw = "^0.3.1" [tool.poetry.group.dev.dependencies] -black = "^22.12.0" +black = "^23.7.0" isort = "^5.11.4" flake8 = "^6.0.0" pyproject-flake8 = "^6.0.0.post1" From fd267a7c9e149e44b6634a62fa3ab0a67419279f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 18:37:08 +0800 Subject: [PATCH 11/16] build(deps-dev): bump mypy from 0.991 to 1.4.1 (#35) Bumps [mypy](https://github.com/python/mypy) from 0.991 to 1.4.1. - [Commits](https://github.com/python/mypy/compare/v0.991...v1.4.1) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 64 +++++++++++++++++++++++--------------------------- pyproject.toml | 2 +- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/poetry.lock b/poetry.lock index 56fe732..cee7b24 100644 --- a/poetry.lock +++ b/poetry.lock @@ -323,47 +323,43 @@ files = [ [[package]] name = "mypy" -version = "0.991" +version = "1.4.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.7" files = [ - {file = "mypy-0.991-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab"}, - {file = "mypy-0.991-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d"}, - {file = "mypy-0.991-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c8f3be99e8a8bd403caa8c03be619544bc2c77a7093685dcf308c6b109426c6"}, - {file = "mypy-0.991-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9ec663ed6c8f15f4ae9d3c04c989b744436c16d26580eaa760ae9dd5d662eb"}, - {file = "mypy-0.991-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4307270436fd7694b41f913eb09210faff27ea4979ecbcd849e57d2da2f65305"}, - {file = "mypy-0.991-cp310-cp310-win_amd64.whl", hash = "sha256:901c2c269c616e6cb0998b33d4adbb4a6af0ac4ce5cd078afd7bc95830e62c1c"}, - {file = "mypy-0.991-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d13674f3fb73805ba0c45eb6c0c3053d218aa1f7abead6e446d474529aafc372"}, - {file = "mypy-0.991-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c8cd4fb70e8584ca1ed5805cbc7c017a3d1a29fb450621089ffed3e99d1857f"}, - {file = "mypy-0.991-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:209ee89fbb0deed518605edddd234af80506aec932ad28d73c08f1400ef80a33"}, - {file = "mypy-0.991-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37bd02ebf9d10e05b00d71302d2c2e6ca333e6c2a8584a98c00e038db8121f05"}, - {file = "mypy-0.991-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:26efb2fcc6b67e4d5a55561f39176821d2adf88f2745ddc72751b7890f3194ad"}, - {file = "mypy-0.991-cp311-cp311-win_amd64.whl", hash = "sha256:3a700330b567114b673cf8ee7388e949f843b356a73b5ab22dd7cff4742a5297"}, - {file = "mypy-0.991-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1f7d1a520373e2272b10796c3ff721ea1a0712288cafaa95931e66aa15798813"}, - {file = "mypy-0.991-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:641411733b127c3e0dab94c45af15fea99e4468f99ac88b39efb1ad677da5711"}, - {file = "mypy-0.991-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3d80e36b7d7a9259b740be6d8d906221789b0d836201af4234093cae89ced0cd"}, - {file = "mypy-0.991-cp37-cp37m-win_amd64.whl", hash = "sha256:e62ebaad93be3ad1a828a11e90f0e76f15449371ffeecca4a0a0b9adc99abcef"}, - {file = "mypy-0.991-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b86ce2c1866a748c0f6faca5232059f881cda6dda2a893b9a8373353cfe3715a"}, - {file = "mypy-0.991-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac6e503823143464538efda0e8e356d871557ef60ccd38f8824a4257acc18d93"}, - {file = "mypy-0.991-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cca5adf694af539aeaa6ac633a7afe9bbd760df9d31be55ab780b77ab5ae8bf"}, - {file = "mypy-0.991-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12c56bf73cdab116df96e4ff39610b92a348cc99a1307e1da3c3768bbb5b135"}, - {file = "mypy-0.991-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:652b651d42f155033a1967739788c436491b577b6a44e4c39fb340d0ee7f0d70"}, - {file = "mypy-0.991-cp38-cp38-win_amd64.whl", hash = "sha256:4175593dc25d9da12f7de8de873a33f9b2b8bdb4e827a7cae952e5b1a342e243"}, - {file = "mypy-0.991-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98e781cd35c0acf33eb0295e8b9c55cdbef64fcb35f6d3aa2186f289bed6e80d"}, - {file = "mypy-0.991-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6d7464bac72a85cb3491c7e92b5b62f3dcccb8af26826257760a552a5e244aa5"}, - {file = "mypy-0.991-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c9166b3f81a10cdf9b49f2d594b21b31adadb3d5e9db9b834866c3258b695be3"}, - {file = "mypy-0.991-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8472f736a5bfb159a5e36740847808f6f5b659960115ff29c7cecec1741c648"}, - {file = "mypy-0.991-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e80e758243b97b618cdf22004beb09e8a2de1af481382e4d84bc52152d1c476"}, - {file = "mypy-0.991-cp39-cp39-win_amd64.whl", hash = "sha256:74e259b5c19f70d35fcc1ad3d56499065c601dfe94ff67ae48b85596b9ec1461"}, - {file = "mypy-0.991-py3-none-any.whl", hash = "sha256:de32edc9b0a7e67c2775e574cb061a537660e51210fbf6006b0b36ea695ae9bb"}, - {file = "mypy-0.991.tar.gz", hash = "sha256:3c0165ba8f354a6d9881809ef29f1a9318a236a6d81c690094c5df32107bde06"}, + {file = "mypy-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:566e72b0cd6598503e48ea610e0052d1b8168e60a46e0bfd34b3acf2d57f96a8"}, + {file = "mypy-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca637024ca67ab24a7fd6f65d280572c3794665eaf5edcc7e90a866544076878"}, + {file = "mypy-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dde1d180cd84f0624c5dcaaa89c89775550a675aff96b5848de78fb11adabcd"}, + {file = "mypy-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8c4d8e89aa7de683e2056a581ce63c46a0c41e31bd2b6d34144e2c80f5ea53dc"}, + {file = "mypy-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:bfdca17c36ae01a21274a3c387a63aa1aafe72bff976522886869ef131b937f1"}, + {file = "mypy-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7549fbf655e5825d787bbc9ecf6028731973f78088fbca3a1f4145c39ef09462"}, + {file = "mypy-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98324ec3ecf12296e6422939e54763faedbfcc502ea4a4c38502082711867258"}, + {file = "mypy-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141dedfdbfe8a04142881ff30ce6e6653c9685b354876b12e4fe6c78598b45e2"}, + {file = "mypy-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8207b7105829eca6f3d774f64a904190bb2231de91b8b186d21ffd98005f14a7"}, + {file = "mypy-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:16f0db5b641ba159eff72cff08edc3875f2b62b2fa2bc24f68c1e7a4e8232d01"}, + {file = "mypy-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:470c969bb3f9a9efcedbadcd19a74ffb34a25f8e6b0e02dae7c0e71f8372f97b"}, + {file = "mypy-1.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5952d2d18b79f7dc25e62e014fe5a23eb1a3d2bc66318df8988a01b1a037c5b"}, + {file = "mypy-1.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:190b6bab0302cec4e9e6767d3eb66085aef2a1cc98fe04936d8a42ed2ba77bb7"}, + {file = "mypy-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9d40652cc4fe33871ad3338581dca3297ff5f2213d0df345bcfbde5162abf0c9"}, + {file = "mypy-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01fd2e9f85622d981fd9063bfaef1aed6e336eaacca00892cd2d82801ab7c042"}, + {file = "mypy-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2460a58faeea905aeb1b9b36f5065f2dc9a9c6e4c992a6499a2360c6c74ceca3"}, + {file = "mypy-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2746d69a8196698146a3dbe29104f9eb6a2a4d8a27878d92169a6c0b74435b6"}, + {file = "mypy-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae704dcfaa180ff7c4cfbad23e74321a2b774f92ca77fd94ce1049175a21c97f"}, + {file = "mypy-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:43d24f6437925ce50139a310a64b2ab048cb2d3694c84c71c3f2a1626d8101dc"}, + {file = "mypy-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c482e1246726616088532b5e964e39765b6d1520791348e6c9dc3af25b233828"}, + {file = "mypy-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43b592511672017f5b1a483527fd2684347fdffc041c9ef53428c8dc530f79a3"}, + {file = "mypy-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34a9239d5b3502c17f07fd7c0b2ae6b7dd7d7f6af35fbb5072c6208e76295816"}, + {file = "mypy-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5703097c4936bbb9e9bce41478c8d08edd2865e177dc4c52be759f81ee4dd26c"}, + {file = "mypy-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e02d700ec8d9b1859790c0475df4e4092c7bf3272a4fd2c9f33d87fac4427b8f"}, + {file = "mypy-1.4.1-py3-none-any.whl", hash = "sha256:45d32cec14e7b97af848bddd97d85ea4f0db4d5a149ed9676caa4eb2f7402bb4"}, + {file = "mypy-1.4.1.tar.gz", hash = "sha256:9bbcd9ab8ea1f2e1c8031c21445b511442cc45c89951e49bbf852cbb70755b1b"}, ] [package.dependencies] -mypy-extensions = ">=0.4.3" +mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=3.10" +typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] @@ -883,4 +879,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.10.0,<3.12" -content-hash = "f71586be33854b6b7b5d26c01d08d1ca6f1be925e883489fa35ac634ef7cd67d" +content-hash = "a727dd20087616c87315d96e37809848567310be08fddc07e30e1fc62375e811" diff --git a/pyproject.toml b/pyproject.toml index 9f2b874..17c152f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ isort = "^5.11.4" flake8 = "^6.0.0" pyproject-flake8 = "^6.0.0.post1" pre-commit = "^3.3.3" -mypy = "^0.991" +mypy = "^1.4" types-colorama = "^0.4.15.4" types-tqdm = "^4.64.7.11" types-requests = "^2.28.11.8" From 80daa20745135c439abf711cbcc8075bacb7e898 Mon Sep 17 00:00:00 2001 From: TheMythologist Date: Wed, 2 Aug 2023 15:23:52 +0800 Subject: [PATCH 12/16] ci: upload Guardian executable as github workflow artifact --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 674a508..e7b0300 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,10 @@ jobs: install-only: true - name: Build executable run: poetry run build + - uses: actions/upload-artifact@v3 + with: + name: Guardian.exe + path: dist/Guardian-*.exe - uses: softprops/action-gh-release@v1 with: files: dist/Guardian-*.exe From 00d033e1e2d784ccfb7b2a68126cc1000da467fd Mon Sep 17 00:00:00 2001 From: TheMythologist Date: Wed, 2 Aug 2023 16:01:39 +0800 Subject: [PATCH 13/16] deps: update isort in deps and pre-commit configuration --- .pre-commit-config.yaml | 2 +- poetry.lock | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f5d509c..de62e71 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: pyproject-flake8 - repo: https://github.com/pycqa/isort - rev: 5.11.4 + rev: 5.12.0 hooks: - id: isort diff --git a/poetry.lock b/poetry.lock index cee7b24..e1f2464 100644 --- a/poetry.lock +++ b/poetry.lock @@ -879,4 +879,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.10.0,<3.12" -content-hash = "a727dd20087616c87315d96e37809848567310be08fddc07e30e1fc62375e811" +content-hash = "72e2e94c2e03f636971e6c986e31b9627258169b9afac8bd16f30cda94f7ac3a" diff --git a/pyproject.toml b/pyproject.toml index 17c152f..379bd55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ prsw = "^0.3.1" [tool.poetry.group.dev.dependencies] black = "^23.7.0" -isort = "^5.11.4" +isort = "^5.12.0" flake8 = "^6.0.0" pyproject-flake8 = "^6.0.0.post1" pre-commit = "^3.3.3" From 6da0cd536b58575e2e48f8b5053f5f4a3248ee69 Mon Sep 17 00:00:00 2001 From: TheMythologist Date: Wed, 2 Aug 2023 16:49:39 +0800 Subject: [PATCH 14/16] feat: display current filter in window title (#38) --- guardian/dispatcher/context.py | 4 ++-- guardian/network/sessions.py | 6 +++++- guardian/util/console.py | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 guardian/util/console.py diff --git a/guardian/dispatcher/context.py b/guardian/dispatcher/context.py index 5889857..80909fe 100644 --- a/guardian/dispatcher/context.py +++ b/guardian/dispatcher/context.py @@ -53,7 +53,7 @@ def kill_old_filters(self) -> None: # Kill other filters for priority_identifier in list(self.filters)[:-1]: debug_logger.debug("Killing %s", self.filters[priority_identifier]) - self.filters.pop(priority_identifier).stop() + self.filters.pop(priority_identifier).stop(overwrite_title=False) def start_latest_filter(self, kill_others: bool = True) -> None: self.filters[list(self.filters)[-1]].start() @@ -66,7 +66,7 @@ def kill_latest_filter(self) -> None: debug_logger.debug( "Killing latest filter %s", self.filters[latest_priority] ) - self.filters.pop(latest_priority).stop() + self.filters.pop(latest_priority).stop(overwrite_title=True) self._current_priority -= 1 def is_filter_running(self) -> bool: diff --git a/guardian/network/sessions.py b/guardian/network/sessions.py index 0ddb847..e6acae5 100644 --- a/guardian/network/sessions.py +++ b/guardian/network/sessions.py @@ -10,6 +10,7 @@ import pydivert from network.sessioninfo import SessionInfo +from util.console import get_original_console_title, set_console_title from util.network import ip_in_cidr_block_set from util.types import CIDR_BLOCK @@ -114,10 +115,13 @@ def __str__(self) -> str: def start(self) -> None: self.process.start() logger.info("Dispatched %s blocker process", self.__class__.__name__) + set_console_title(f"{get_original_console_title()} - {self.__class__.__name__}") - def stop(self) -> None: + def stop(self, overwrite_title: bool = True) -> None: self.process.terminate() logger.info("Terminated %s blocker process", self.__class__.__name__) + if overwrite_title: + set_console_title(get_original_console_title()) @abstractmethod def is_packet_allowed(self, packet: pydivert.Packet) -> bool: diff --git a/guardian/util/console.py b/guardian/util/console.py new file mode 100644 index 0000000..7739772 --- /dev/null +++ b/guardian/util/console.py @@ -0,0 +1,18 @@ +from ctypes import WinDLL, create_unicode_buffer + +kernel32 = WinDLL("kernel32", use_last_error=True) + + +def get_console_title() -> str: + BUF_SIZE = 256 + buffer = create_unicode_buffer(256) + kernel32.GetConsoleTitleW(buffer, BUF_SIZE) + return buffer.value + + +def set_console_title(title: str) -> None: + kernel32.SetConsoleTitleW(title) + + +def get_original_console_title() -> str: + return get_console_title().split(" - ")[0] From 5c79cee5eb6829980b8b52e4aa26bf2fafaeb1cb Mon Sep 17 00:00:00 2001 From: TheMythologist Date: Wed, 2 Aug 2023 17:00:30 +0800 Subject: [PATCH 15/16] feat: update heuristics for v1.66+ (#39) * feat: update heuristics for v1.66+ --- .github/workflows/build.yml | 2 +- README.md | 2 +- guardian/network/sessions.py | 40 ++++++++++++++++++++++++++++++------ 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7b0300..f6d7c7f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,7 @@ jobs: run: poetry run build - uses: actions/upload-artifact@v3 with: - name: Guardian.exe + name: Guardian path: dist/Guardian-*.exe - uses: softprops/action-gh-release@v1 with: diff --git a/README.md b/README.md index 4c365ea..6279109 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Guardian -Custom firewall for the game GTA Online (version 1.54 and onwards). +Custom firewall for the game GTA Online (version 1.66 and onwards). [![Latest release](https://img.shields.io/github/v/release/TheMythologist/guardian)](https://github.com/TheMythologist/guardian/releases/latest) [![CI status](https://github.com/TheMythologist/guardian/workflows/build/badge.svg)](https://github.com/TheMythologist/guardian/actions?query=branch%3Amain) diff --git a/guardian/network/sessions.py b/guardian/network/sessions.py index e6acae5..9fda930 100644 --- a/guardian/network/sessions.py +++ b/guardian/network/sessions.py @@ -69,13 +69,17 @@ # Interesting note: All the matchmaker requests have payload sizes that may be 16 bytes apart. -HEARTBEAT_SIZES = {12, 18} +HEARTBEAT_SIZES = {12, 18, 63} MATCHMAKING_SIZES = { - 245, - 261, - 277, - 293, + 191, + 207, + 223, + 239, } # probably a player looking to join the session. +DTLs = {0xFEFF, 0xFEFD} +KNOWNS = {0x39, 0x31, 0x29} +# RECORDS = {20, 21, 22, 23} + KNOWN_SIZES = HEARTBEAT_SIZES.union(MATCHMAKING_SIZES) @@ -196,7 +200,31 @@ def is_packet_allowed(self, packet: pydivert.Packet) -> bool: # The "special sauce" for the new filtering logic. We're using payload sizes to guess if the packet # has a behaviour we want to allow through. - return ip in self.ips or size in KNOWN_SIZES + if ip in self.ips or size in HEARTBEAT_SIZES: + return True + + wrapper = 0 if size < 5 else int.from_bytes(packet.raw[28:30], "big") + magic_byte = packet.payload[4] + + if size == wrapper: + offset = packet.payload[2] ^ magic_byte + code = 0 + alt = 0 + if offset == 17 and size >= 27: + code = int.from_bytes(packet.payload[25:27], "big") + alt = packet.payload[24] + elif offset == 49 and size >= 91: + code = int.from_bytes(packet.payload[89:91], "big") + + magic = 0 if size < 5 else int.from_bytes([magic_byte, magic_byte], "big") + if ( + code ^ magic in DTLs + or alt ^ magic_byte in KNOWNS + or code ^ magic < 0x10 + ): + return True + + return False class BlacklistSession(AbstractPacketFilter): From d066fa1c3e4e3a54f36ed136c9847b72b7de7492 Mon Sep 17 00:00:00 2001 From: TheMythologist Date: Wed, 2 Aug 2023 17:32:12 +0800 Subject: [PATCH 16/16] release: bump version to 3.5.0 --- guardian/__main__.py | 2 +- guardian/build.py | 2 +- pyproject.toml | 2 +- spec/version.txt | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/guardian/__main__.py b/guardian/__main__.py index b8f4036..39936eb 100644 --- a/guardian/__main__.py +++ b/guardian/__main__.py @@ -9,7 +9,7 @@ from util.crash import crash_report from util.printer import print_white -__version__ = "3.4.1" +__version__ = "3.5.0" logger = logging.getLogger(__name__) logger.propagate = False diff --git a/guardian/build.py b/guardian/build.py index d437977..629f402 100644 --- a/guardian/build.py +++ b/guardian/build.py @@ -1,6 +1,6 @@ import PyInstaller.__main__ -version = "3.4.1" +version = "3.5.0" def build() -> None: diff --git a/pyproject.toml b/pyproject.toml index 379bd55..0ae8974 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "Guardian" -version = "3.4.1" +version = "3.5.0" description = "Custom firewall used to create private lobbies for GTA5 Online" authors = ["TheMythologist "] license = "LGPL-3.0" diff --git a/spec/version.txt b/spec/version.txt index 4f7f0c3..f65bf33 100644 --- a/spec/version.txt +++ b/spec/version.txt @@ -2,8 +2,8 @@ VSVersionInfo( ffi=FixedFileInfo( # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) # Set not needed items to zero 0. - filevers=(3, 4, 1, 0), - prodvers=(3, 4, 1, 0), + filevers=(3, 5, 0, 0), + prodvers=(3, 5, 0, 0), # Contains a bitmask that specifies the valid bits 'flags'r mask=0x3f, # Contains a bitmask that specifies the Boolean attributes of the file. @@ -28,13 +28,13 @@ VSVersionInfo( [StringStruct('Comments', ''), StringStruct('CompanyName', ''), StringStruct('FileDescription', 'Firewall'), - StringStruct('FileVersion', '3.4.1'), + StringStruct('FileVersion', '3.5.0'), StringStruct('InternalName', 'Guardian.exe'), StringStruct('LegalCopyright', ''), StringStruct('LegalTrademarks', ''), StringStruct('OriginalFilename', 'Guardian.exe'), StringStruct('ProductName', 'Guardian'), - StringStruct('ProductVersion', '3.4.1')]) + StringStruct('ProductVersion', '3.5.0')]) ]), VarFileInfo([VarStruct('Translation', [1033, 1252])]) ]