atomicshop 2.11.47__py3-none-any.whl → 3.10.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- atomicshop/__init__.py +1 -1
- atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
- atomicshop/a_mains/addons/process_list/compile.cmd +7 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
- atomicshop/a_mains/dns_gateway_setting.py +11 -0
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/github_wrapper.py +11 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/{addons/mains → a_mains}/msi_unpacker.py +3 -1
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/a_mains/update_config_toml.py +38 -0
- atomicshop/appointment_management.py +5 -3
- atomicshop/basics/ansi_escape_codes.py +3 -1
- atomicshop/basics/argparse_template.py +2 -0
- atomicshop/basics/booleans.py +27 -30
- atomicshop/basics/bytes_arrays.py +43 -0
- atomicshop/basics/classes.py +149 -1
- atomicshop/basics/dicts.py +12 -0
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/list_of_dicts.py +69 -5
- atomicshop/basics/lists.py +14 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/package_module.py +10 -0
- atomicshop/basics/strings.py +160 -7
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -4
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +12 -7
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +98 -0
- atomicshop/diff_check.py +340 -40
- atomicshop/dns.py +128 -12
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/const.py +38 -0
- atomicshop/etws/providers.py +21 -0
- atomicshop/etws/sessions.py +43 -0
- atomicshop/etws/trace.py +168 -0
- atomicshop/etws/traces/trace_dns.py +162 -0
- atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +222 -24
- atomicshop/file_io/docxs.py +35 -18
- atomicshop/file_io/file_io.py +35 -19
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +864 -293
- atomicshop/get_process_list.py +133 -0
- atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
- atomicshop/http_parse.py +149 -93
- atomicshop/ip_addresses.py +6 -1
- atomicshop/mitm/centered_settings.py +132 -0
- atomicshop/mitm/config_static.py +207 -0
- atomicshop/mitm/config_toml_editor.py +55 -0
- atomicshop/mitm/connection_thread_worker.py +875 -357
- atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
- atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
- atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
- atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
- atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
- atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
- atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
- atomicshop/mitm/engines/create_module_template.py +58 -14
- atomicshop/mitm/import_config.py +359 -139
- atomicshop/mitm/initialize_engines.py +160 -74
- atomicshop/mitm/message.py +64 -23
- atomicshop/mitm/mitm_main.py +892 -0
- atomicshop/mitm/recs_files.py +183 -0
- atomicshop/mitm/shared_functions.py +4 -10
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +257 -166
- atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
- atomicshop/monitor/change_monitor.py +96 -120
- atomicshop/monitor/checks/dns.py +139 -70
- atomicshop/monitor/checks/file.py +77 -0
- atomicshop/monitor/checks/network.py +81 -77
- atomicshop/monitor/checks/process_running.py +33 -34
- atomicshop/monitor/checks/url.py +94 -0
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +205 -0
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/permissions.py +22 -0
- atomicshop/permissions/ubuntu_permissions.py +239 -0
- atomicshop/permissions/win_permissions.py +33 -0
- atomicshop/print_api.py +24 -41
- atomicshop/process.py +63 -17
- atomicshop/process_poller/__init__.py +0 -0
- atomicshop/process_poller/pollers/__init__.py +0 -0
- atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
- atomicshop/process_poller/process_pool.py +207 -0
- atomicshop/process_poller/simple_process_pool.py +311 -0
- atomicshop/process_poller/tracer_base.py +45 -0
- atomicshop/process_poller/tracers/__init__.py +0 -0
- atomicshop/process_poller/tracers/event_log.py +46 -0
- atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/question_answer_engine.py +2 -2
- atomicshop/scheduling.py +24 -5
- atomicshop/sound.py +4 -2
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/startup/__init__.py +0 -0
- atomicshop/startup/win/__init__.py +0 -0
- atomicshop/startup/win/startup_folder.py +53 -0
- atomicshop/startup/win/task_scheduler.py +119 -0
- atomicshop/system_resource_monitor.py +61 -46
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/timer.py +30 -11
- atomicshop/urls.py +41 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +110 -25
- atomicshop/web_apis/__init__.py +0 -0
- atomicshop/web_apis/google_custom_search.py +44 -0
- atomicshop/web_apis/google_llm.py +188 -0
- atomicshop/websocket_parse.py +450 -0
- atomicshop/wrappers/certauthw/certauth.py +1 -0
- atomicshop/wrappers/cryptographyw.py +29 -8
- atomicshop/wrappers/ctyping/etw_winapi/__init__.py +0 -0
- atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
- atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +13 -9
- atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
- atomicshop/wrappers/ctyping/setup_device.py +466 -0
- atomicshop/wrappers/ctyping/win_console.py +39 -0
- atomicshop/wrappers/dockerw/dockerw.py +113 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
- atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
- atomicshop/wrappers/factw/get_file_data.py +12 -5
- atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
- atomicshop/wrappers/factw/postgresql/firmware.py +4 -6
- atomicshop/wrappers/githubw.py +583 -51
- atomicshop/wrappers/loggingw/consts.py +49 -0
- atomicshop/wrappers/loggingw/filters.py +102 -0
- atomicshop/wrappers/loggingw/formatters.py +58 -71
- atomicshop/wrappers/loggingw/handlers.py +459 -40
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +1010 -178
- atomicshop/wrappers/loggingw/reading.py +344 -19
- atomicshop/wrappers/mongodbw/__init__.py +0 -0
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
- atomicshop/wrappers/netshw.py +271 -0
- atomicshop/wrappers/playwrightw/engine.py +34 -19
- atomicshop/wrappers/playwrightw/infra.py +5 -0
- atomicshop/wrappers/playwrightw/javascript.py +7 -3
- atomicshop/wrappers/playwrightw/keyboard.py +14 -0
- atomicshop/wrappers/playwrightw/scenarios.py +172 -5
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/processes.py +81 -0
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/psutilw/psutilw.py +9 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/cert_store.py +116 -0
- atomicshop/wrappers/pywin32w/console.py +34 -0
- atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
- atomicshop/wrappers/pywin32w/winshell.py +19 -0
- atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
- atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
- atomicshop/wrappers/socketw/accepter.py +21 -7
- atomicshop/wrappers/socketw/certificator.py +216 -150
- atomicshop/wrappers/socketw/creator.py +190 -50
- atomicshop/wrappers/socketw/dns_server.py +500 -173
- atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +144 -102
- atomicshop/wrappers/socketw/sender.py +65 -35
- atomicshop/wrappers/socketw/sni.py +334 -165
- atomicshop/wrappers/socketw/socket_base.py +134 -0
- atomicshop/wrappers/socketw/socket_client.py +137 -95
- atomicshop/wrappers/socketw/socket_server_tester.py +14 -9
- atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
- atomicshop/wrappers/socketw/ssl_base.py +15 -14
- atomicshop/wrappers/socketw/statistics_csv.py +148 -17
- atomicshop/wrappers/sysmonw.py +157 -0
- atomicshop/wrappers/ubuntu_terminal.py +65 -26
- atomicshop/wrappers/win_auditw.py +189 -0
- atomicshop/wrappers/winregw/__init__.py +0 -0
- atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
- atomicshop/wrappers/winregw/winreg_network.py +232 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
- atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
- atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
- atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
- atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/addons/process_list/compile.cmd +0 -2
- atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/archiver.py +0 -34
- atomicshop/archiver/search_in_archive.py +0 -250
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -44
- atomicshop/archiver/zips.py +0 -293
- atomicshop/etw/dns_trace.py +0 -118
- atomicshop/etw/etw.py +0 -61
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -240
- atomicshop/monitor/checks/hash.py +0 -44
- atomicshop/monitor/checks/hash_checks/file.py +0 -55
- atomicshop/monitor/checks/hash_checks/url.py +0 -62
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -110
- atomicshop/process_poller.py +0 -237
- atomicshop/script_as_string_processor.py +0 -38
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -209
- atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/loggingw/checks.py +0 -20
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.11.47.dist-info/RECORD +0 -251
- /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons/mains → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
- /atomicshop/{archiver → etws}/__init__.py +0 -0
- /atomicshop/{etw → etws/traces}/__init__.py +0 -0
- /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
- /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
|
@@ -1,181 +1,350 @@
|
|
|
1
|
-
import sys
|
|
2
1
|
import ssl
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Callable, Any
|
|
3
4
|
|
|
4
|
-
from
|
|
5
|
+
from ..loggingw import loggingw
|
|
5
6
|
from ...domains import get_domain_without_first_subdomain_if_no_subdomain_return_as_is
|
|
6
7
|
from ...print_api import print_api
|
|
7
8
|
|
|
9
|
+
from . import certificator, creator
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class SNIReceivedParameters:
|
|
14
|
+
ssl_socket: ssl.SSLSocket
|
|
15
|
+
destination_name: str
|
|
16
|
+
ssl_context: ssl.SSLContext
|
|
17
|
+
|
|
8
18
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
19
|
+
class SNIDefaultCertificateCreationError(Exception):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SNISetup:
|
|
13
24
|
"""
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
:param ssl_context:
|
|
17
|
-
:param config:
|
|
18
|
-
:param dns_domain: domain that was received from DNS Server that will be used if no SNI was passed.
|
|
19
|
-
:param sni_function_name: Reference to the function that will be called when SNI is present in the request.
|
|
20
|
-
:param use_default_sni_function: If True, will use default SNI function.
|
|
21
|
-
:param use_sni_extended: If True, will use extended SNI function.
|
|
22
|
-
:param print_kwargs: dict, that contains all the arguments for 'print_api' function.
|
|
23
|
-
:return:
|
|
25
|
+
Class to handle setting up SNI related features in socket and context.
|
|
24
26
|
"""
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
ca_certificate_name: str,
|
|
30
|
+
ca_certificate_filepath: str,
|
|
31
|
+
default_server_certificate_usage: bool,
|
|
32
|
+
default_server_certificate_name: str,
|
|
33
|
+
default_server_certificate_directory: str,
|
|
34
|
+
default_certificate_domain_list: list,
|
|
35
|
+
sni_custom_callback_function: Callable[..., Any],
|
|
36
|
+
sni_use_default_callback_function: bool,
|
|
37
|
+
sni_use_default_callback_function_extended: bool,
|
|
38
|
+
sni_add_new_domains_to_default_server_certificate: bool,
|
|
39
|
+
sni_create_server_certificate_for_each_domain: bool,
|
|
40
|
+
sni_server_certificates_cache_directory: str,
|
|
41
|
+
sni_get_server_certificate_from_server_socket: bool,
|
|
42
|
+
sni_server_certificate_from_server_socket_download_directory: str,
|
|
43
|
+
custom_server_certificate_usage: bool,
|
|
44
|
+
custom_server_certificate_path: str,
|
|
45
|
+
custom_private_key_path: str,
|
|
46
|
+
forwarding_dns_service_ipv4_list___only_for_localhost: list,
|
|
47
|
+
tls: bool,
|
|
48
|
+
domain_from_dns_server: str = None,
|
|
49
|
+
skip_extension_id_list: list = None,
|
|
50
|
+
exceptions_logger: loggingw.ExceptionCsvLogger = None,
|
|
51
|
+
enable_sslkeylogfile_env_to_client_ssl_context: bool = False,
|
|
52
|
+
sslkeylog_file_path: str = None
|
|
53
|
+
):
|
|
54
|
+
self.ca_certificate_name = ca_certificate_name
|
|
55
|
+
self.ca_certificate_filepath = ca_certificate_filepath
|
|
56
|
+
self.default_server_certificate_usage = default_server_certificate_usage
|
|
57
|
+
self.default_server_certificate_name = default_server_certificate_name
|
|
58
|
+
self.default_server_certificate_directory = default_server_certificate_directory
|
|
59
|
+
self.default_certificate_domain_list = default_certificate_domain_list
|
|
60
|
+
self.sni_custom_callback_function: Callable[..., Any] = sni_custom_callback_function
|
|
61
|
+
self.sni_use_default_callback_function: bool = sni_use_default_callback_function
|
|
62
|
+
self.sni_use_default_callback_function_extended: bool = sni_use_default_callback_function_extended
|
|
63
|
+
self.sni_add_new_domains_to_default_server_certificate = sni_add_new_domains_to_default_server_certificate
|
|
64
|
+
self.sni_create_server_certificate_for_each_domain = sni_create_server_certificate_for_each_domain
|
|
65
|
+
self.sni_server_certificates_cache_directory = sni_server_certificates_cache_directory
|
|
66
|
+
self.sni_get_server_certificate_from_server_socket = sni_get_server_certificate_from_server_socket
|
|
67
|
+
self.sni_server_certificate_from_server_socket_download_directory = (
|
|
68
|
+
sni_server_certificate_from_server_socket_download_directory)
|
|
69
|
+
self.custom_server_certificate_usage = custom_server_certificate_usage
|
|
70
|
+
self.custom_server_certificate_path = custom_server_certificate_path
|
|
71
|
+
self.custom_private_key_path = custom_private_key_path
|
|
72
|
+
self.forwarding_dns_service_ipv4_list___only_for_localhost = (
|
|
73
|
+
forwarding_dns_service_ipv4_list___only_for_localhost)
|
|
74
|
+
self.domain_from_dns_server: str = domain_from_dns_server
|
|
75
|
+
self.skip_extension_id_list = skip_extension_id_list
|
|
76
|
+
self.tls = tls
|
|
77
|
+
self.exceptions_logger = exceptions_logger
|
|
78
|
+
self.certificator_instance = None
|
|
79
|
+
self.enable_sslkeylogfile_env_to_client_ssl_context: bool = enable_sslkeylogfile_env_to_client_ssl_context
|
|
80
|
+
self.sslkeylog_file_path: str = sslkeylog_file_path
|
|
81
|
+
|
|
82
|
+
def wrap_socket_with_ssl_context_server_sni_extended(
|
|
83
|
+
self,
|
|
84
|
+
socket_object,
|
|
85
|
+
print_kwargs: dict = None
|
|
86
|
+
):
|
|
25
87
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
88
|
+
# Create SSL Socket to wrap the raw socket with.
|
|
89
|
+
ssl_context: ssl.SSLContext = creator.create_ssl_context_for_server(
|
|
90
|
+
allow_legacy=True, enable_sslkeylogfile_env_to_client_ssl_context=self.enable_sslkeylogfile_env_to_client_ssl_context,
|
|
91
|
+
sslkeylog_file_path=self.sslkeylog_file_path)
|
|
92
|
+
|
|
93
|
+
self.certificator_instance = certificator.Certificator(
|
|
94
|
+
ca_certificate_name=self.ca_certificate_name,
|
|
95
|
+
ca_certificate_filepath=self.ca_certificate_filepath,
|
|
96
|
+
default_server_certificate_usage=self.default_server_certificate_usage,
|
|
97
|
+
default_server_certificate_name=self.default_server_certificate_name,
|
|
98
|
+
default_server_certificate_directory=self.default_server_certificate_directory,
|
|
99
|
+
default_certificate_domain_list=self.default_certificate_domain_list,
|
|
100
|
+
sni_server_certificates_cache_directory=self.sni_server_certificates_cache_directory,
|
|
101
|
+
sni_get_server_certificate_from_server_socket=self.sni_get_server_certificate_from_server_socket,
|
|
102
|
+
sni_server_certificate_from_server_socket_download_directory=(
|
|
103
|
+
self.sni_server_certificate_from_server_socket_download_directory),
|
|
104
|
+
custom_server_certificate_usage=self.custom_server_certificate_usage,
|
|
105
|
+
custom_server_certificate_path=self.custom_server_certificate_path,
|
|
106
|
+
custom_private_key_path=self.custom_private_key_path,
|
|
107
|
+
forwarding_dns_service_ipv4_list___only_for_localhost=(
|
|
108
|
+
self.forwarding_dns_service_ipv4_list___only_for_localhost),
|
|
109
|
+
skip_extension_id_list=self.skip_extension_id_list,
|
|
110
|
+
tls=self.tls,
|
|
111
|
+
enable_sslkeylogfile_env_to_client_ssl_context=self.enable_sslkeylogfile_env_to_client_ssl_context,
|
|
112
|
+
sslkeylog_file_path=self.sslkeylog_file_path
|
|
50
113
|
)
|
|
51
114
|
|
|
115
|
+
# Add SNI callback function to the SSL context.
|
|
116
|
+
self.add_sni_callback_function_to_ssl_context(ssl_context=ssl_context, print_kwargs=print_kwargs)
|
|
117
|
+
|
|
118
|
+
server_certificate_file_path, server_private_key_file_path = \
|
|
119
|
+
self.certificator_instance.select_server_ssl_context_certificate(print_kwargs=print_kwargs)
|
|
52
120
|
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
121
|
+
# If the user chose 'sni_create_server_certificate_for_each_domain = 1' in the configuration file,
|
|
122
|
+
# it means that 'self.server_certificate_file_path' will be empty, which is OK, since we'll inject
|
|
123
|
+
# dynamically created certificate from certs folder through SNI.
|
|
124
|
+
if server_certificate_file_path:
|
|
125
|
+
creator.load_certificate_and_key_into_server_ssl_context(
|
|
126
|
+
ssl_context, server_certificate_file_path, server_private_key_file_path,
|
|
127
|
+
print_kwargs=print_kwargs)
|
|
128
|
+
|
|
129
|
+
ssl_socket, error_message = creator.wrap_socket_with_ssl_context_server_with_error_message(
|
|
130
|
+
socket_object=socket_object, ssl_context=ssl_context, domain_from_dns_server=self.domain_from_dns_server,
|
|
131
|
+
print_kwargs=print_kwargs)
|
|
132
|
+
|
|
133
|
+
return ssl_socket, error_message
|
|
134
|
+
|
|
135
|
+
def add_sni_callback_function_to_ssl_context(
|
|
136
|
+
self,
|
|
137
|
+
ssl_context,
|
|
138
|
+
print_kwargs: dict = None
|
|
139
|
+
):
|
|
140
|
+
"""
|
|
141
|
+
Add SNI callback function reference to SSLContext object. Inplace.
|
|
142
|
+
|
|
143
|
+
:param ssl_context: SSLContext object.
|
|
144
|
+
:param print_kwargs: dict, that contains all the arguments for 'print_api' function.
|
|
145
|
+
:return:
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
# SNI - Server Name Indication: https://en.wikipedia.org/wiki/Server_Name_Indication
|
|
149
|
+
# SNI is extension to TLS protocol to tell the Server what is destination domain that the client is trying to
|
|
150
|
+
# connect. The server knowing the destination domain then can present to the client the appropriate certificate.
|
|
151
|
+
# "sni_callback" method: https://docs.python.org/3/library/ssl.html#ssl.SSLContext.sni_callback
|
|
152
|
+
# The method calls your custom function. If there is SNI present in the TLS request from the client, then the
|
|
153
|
+
# function will be called. Automatically providing 3 parameters from the system: ssl.SSLSocket, The destination
|
|
154
|
+
# server name, current ssl.SSLContext object.
|
|
155
|
+
# If you check the custom function it has all these variables mandatory, since this is what system provides and
|
|
156
|
+
# handled by the system, if SNI is existent.
|
|
157
|
+
# The function is actually called at "accept()" method of the "ssl.SSLSocket"
|
|
158
|
+
# This needs to be set only once on the listening socket
|
|
159
|
+
if self.sni_custom_callback_function:
|
|
160
|
+
# ssl_context.sni_callback = self.sni_custom_callback_function
|
|
161
|
+
ssl_context.set_servername_callback(self.sni_custom_callback_function)
|
|
162
|
+
|
|
163
|
+
if self.sni_use_default_callback_function:
|
|
164
|
+
sni_handler_instance = SNIHandler(
|
|
165
|
+
sni_use_default_callback_function_extended=self.sni_use_default_callback_function_extended,
|
|
166
|
+
sni_add_new_domains_to_default_server_certificate=self.sni_add_new_domains_to_default_server_certificate,
|
|
167
|
+
sni_create_server_certificate_for_each_domain=self.sni_create_server_certificate_for_each_domain,
|
|
168
|
+
certificator_instance=self.certificator_instance,
|
|
169
|
+
domain_from_dns_server=self.domain_from_dns_server,
|
|
170
|
+
default_certificate_domain_list=self.default_certificate_domain_list,
|
|
171
|
+
exceptions_logger=self.exceptions_logger,
|
|
172
|
+
enable_sslkeylogfile_env_to_client_ssl_context=(
|
|
173
|
+
self.certificator_instance.enable_sslkeylogfile_env_to_client_ssl_context),
|
|
174
|
+
sslkeylog_file_path=self.certificator_instance.sslkeylog_file_path)
|
|
175
|
+
ssl_context.set_servername_callback(
|
|
176
|
+
sni_handler_instance.setup_sni_callback(print_kwargs=print_kwargs))
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class SNIHandler:
|
|
59
180
|
"""
|
|
60
|
-
|
|
61
|
-
:param use_sni_extended: Use extended SNI function, besides the default one.
|
|
62
|
-
:param config:
|
|
63
|
-
:param dns_domain: domain that was received from DNS Server that will be used if no SNI was passed.
|
|
64
|
-
:param print_kwargs: dict, that contains all the arguments for 'print_api' function.
|
|
65
|
-
:return:
|
|
181
|
+
THe class is responsible for handling SNI callback execution.
|
|
66
182
|
"""
|
|
67
183
|
|
|
68
|
-
def
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
#
|
|
96
|
-
# This
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
184
|
+
def __init__(
|
|
185
|
+
self,
|
|
186
|
+
sni_use_default_callback_function_extended: bool,
|
|
187
|
+
sni_add_new_domains_to_default_server_certificate: bool,
|
|
188
|
+
sni_create_server_certificate_for_each_domain: bool,
|
|
189
|
+
certificator_instance: certificator.Certificator,
|
|
190
|
+
domain_from_dns_server: str,
|
|
191
|
+
default_certificate_domain_list: list,
|
|
192
|
+
exceptions_logger: loggingw.ExceptionCsvLogger,
|
|
193
|
+
enable_sslkeylogfile_env_to_client_ssl_context: bool,
|
|
194
|
+
sslkeylog_file_path: str
|
|
195
|
+
):
|
|
196
|
+
self.sni_use_default_callback_function_extended = sni_use_default_callback_function_extended
|
|
197
|
+
self.sni_add_new_domains_to_default_server_certificate = sni_add_new_domains_to_default_server_certificate
|
|
198
|
+
self.sni_create_server_certificate_for_each_domain = sni_create_server_certificate_for_each_domain
|
|
199
|
+
self.certificator_instance = certificator_instance
|
|
200
|
+
self.domain_from_dns_server: str = domain_from_dns_server
|
|
201
|
+
self.default_certificate_domain_list = default_certificate_domain_list
|
|
202
|
+
self.exceptions_logger = exceptions_logger
|
|
203
|
+
self.enable_sslkeylogfile_env_to_client_ssl_context: bool = enable_sslkeylogfile_env_to_client_ssl_context
|
|
204
|
+
self.sslkeylog_file_path: str = sslkeylog_file_path
|
|
205
|
+
|
|
206
|
+
# noinspection PyTypeChecker
|
|
207
|
+
self.sni_received_parameters: SNIReceivedParameters = None
|
|
208
|
+
|
|
209
|
+
# Server Name Indication (SNI) is an extension to the Transport Layer Security (TLS) computer networking protocol.
|
|
210
|
+
# Function to handle server's SSLContext's SNI callback function.
|
|
211
|
+
# This is actually called first during "accept()" method of the "ssl.SSLSocket" then comes accept itself.
|
|
212
|
+
# This happens in 'ssl.py' module in 'self._sslobj.do_handshake()' function.
|
|
213
|
+
def setup_sni_callback(
|
|
214
|
+
self,
|
|
215
|
+
print_kwargs: dict = None
|
|
216
|
+
):
|
|
217
|
+
"""
|
|
218
|
+
Setup SNI callback function.
|
|
219
|
+
:param print_kwargs: dict, that contains all the arguments for 'print_api' function.
|
|
220
|
+
:return:
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
def sni_handle(
|
|
224
|
+
sni_ssl_socket: ssl.SSLSocket,
|
|
225
|
+
sni_destination_name: str,
|
|
226
|
+
sni_ssl_context: ssl.SSLContext):
|
|
227
|
+
|
|
228
|
+
try:
|
|
229
|
+
# Set 'server_hostname' for the socket.
|
|
230
|
+
sni_ssl_socket.server_hostname = sni_destination_name
|
|
231
|
+
|
|
232
|
+
# If 'sni_execute_extended' was set to True.
|
|
233
|
+
if self.sni_use_default_callback_function_extended:
|
|
234
|
+
self.sni_received_parameters = SNIReceivedParameters(
|
|
235
|
+
ssl_socket=sni_ssl_socket,
|
|
236
|
+
destination_name=sni_destination_name,
|
|
237
|
+
ssl_context=sni_ssl_context
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
self.sni_handle_extended(print_kwargs=print_kwargs)
|
|
241
|
+
except Exception as e:
|
|
242
|
+
self.exceptions_logger.write(e)
|
|
243
|
+
|
|
244
|
+
return sni_handle
|
|
245
|
+
|
|
246
|
+
def sni_handle_extended(
|
|
247
|
+
self,
|
|
248
|
+
print_kwargs: dict = None
|
|
249
|
+
):
|
|
250
|
+
# Set 'server_hostname' for the socket.
|
|
251
|
+
self.set_socket_server_hostname(print_kwargs=print_kwargs)
|
|
252
|
+
|
|
253
|
+
# If 'sni_default_server_certificates_addons' was set to 'True' in the 'config.ini'.
|
|
254
|
+
# This section will add all the new domains that hit the server to default certificate SAN with wildcard.
|
|
255
|
+
if self.sni_add_new_domains_to_default_server_certificate:
|
|
256
|
+
self.sni_add_domain_to_default_server_certificate(print_kwargs=print_kwargs)
|
|
257
|
+
|
|
258
|
+
# If SNI server certificate creation was set to 'True', we'll create certificate for each incoming domain if
|
|
259
|
+
# non-existent in certificates cache folder.
|
|
260
|
+
if self.sni_create_server_certificate_for_each_domain:
|
|
261
|
+
self.certificator_instance.create_use_sni_server_certificate_ca_signed(
|
|
262
|
+
sni_received_parameters=self.sni_received_parameters, print_kwargs=print_kwargs)
|
|
263
|
+
|
|
264
|
+
def set_socket_server_hostname(
|
|
265
|
+
self,
|
|
266
|
+
print_kwargs: dict = None
|
|
267
|
+
):
|
|
268
|
+
service_name_from_sni = None
|
|
269
|
+
|
|
270
|
+
# Try on general settings in the SNI function.
|
|
271
|
+
try:
|
|
272
|
+
# Check if SNI was passed. If no SNI was passed.
|
|
273
|
+
if not self.sni_received_parameters.destination_name:
|
|
274
|
+
# If DNS server is enabled we'll get the domain from dns server.
|
|
275
|
+
if self.domain_from_dns_server:
|
|
276
|
+
self.sni_received_parameters.destination_name = self.domain_from_dns_server
|
|
277
|
+
print_api("SNI Passed: False", color="yellow", **(print_kwargs or {}))
|
|
278
|
+
|
|
279
|
+
message = f"SNI Handler: No SNI was passed, using domain from DNS Server: {self.domain_from_dns_server}"
|
|
280
|
+
print_api(message, color="yellow", **(print_kwargs or {}))
|
|
281
|
+
# If DNS server is disabled, the domain from dns server will be empty.
|
|
282
|
+
else:
|
|
283
|
+
print_api("SNI Passed: False", color="yellow", **(print_kwargs or {}))
|
|
284
|
+
|
|
285
|
+
message = (
|
|
286
|
+
f"SNI Handler: No SNI was passed, No domain passed from DNS Server. Service name will be 'None'.")
|
|
287
|
+
print_api(message, color="yellow", **(print_kwargs or {}))
|
|
288
|
+
|
|
289
|
+
# Setting "server_hostname" as a domain.
|
|
290
|
+
self.sni_received_parameters.ssl_socket.server_hostname = self.sni_received_parameters.destination_name
|
|
291
|
+
print_api("SNI Passed: True", **(print_kwargs or {}))
|
|
292
|
+
message = (
|
|
293
|
+
f"SNI Handler: port {self.sni_received_parameters.ssl_socket.getsockname()[1]}: "
|
|
294
|
+
f"Incoming connection for [{self.sni_received_parameters.ssl_socket.server_hostname}]")
|
|
295
|
+
print_api(message, **(print_kwargs or {}))
|
|
296
|
+
except Exception as exception_object:
|
|
297
|
+
message = f"SNI Handler: Undocumented exception general settings section: {exception_object}"
|
|
298
|
+
print_api(message, error_type=True, logger_method="error", traceback_string=True,
|
|
299
|
+
**(print_kwargs or {}))
|
|
300
|
+
pass
|
|
301
|
+
|
|
302
|
+
def sni_add_domain_to_default_server_certificate(
|
|
303
|
+
self,
|
|
304
|
+
print_kwargs: dict = None
|
|
305
|
+
):
|
|
306
|
+
# Check if incoming domain is already in the parent domains of 'domains_all_times' list.
|
|
307
|
+
if not any(x in self.sni_received_parameters.ssl_socket.server_hostname for x in
|
|
308
|
+
self.default_certificate_domain_list):
|
|
309
|
+
message = f"SNI Handler: Current domain is not in known domains list. Adding."
|
|
310
|
+
print_api(message, **(print_kwargs or {}))
|
|
311
|
+
# In the past was using 'certauth' to extract tlds, but it works only in online mode, so rewrote
|
|
312
|
+
# the function to disable online fetching of TLD snapshot.
|
|
313
|
+
# Initialize 'certauth' object.
|
|
314
|
+
# certificate_object = CertificateAuthority(certificate_ca_name, certificate_ca_filepath)
|
|
315
|
+
# Extract parent domain from the current SNI domain.
|
|
316
|
+
# parent_domain = certificate_object.get_wildcard_domain(service_name_from_sni)
|
|
317
|
+
|
|
318
|
+
# Extract parent domain from the current SNI domain.
|
|
319
|
+
parent_domain = get_domain_without_first_subdomain_if_no_subdomain_return_as_is(
|
|
320
|
+
self.sni_received_parameters.ssl_socket.server_hostname)
|
|
321
|
+
# Add the parent domain to the known domains list.
|
|
322
|
+
self.default_certificate_domain_list.append(parent_domain)
|
|
323
|
+
|
|
324
|
+
default_server_certificate_path, subject_alternate_names = \
|
|
325
|
+
self.certificator_instance.create_overwrite_default_server_certificate_ca_signed()
|
|
326
|
+
|
|
327
|
+
if default_server_certificate_path:
|
|
328
|
+
message = f"SNI Handler: Default Server Certificate was created / overwritten: " \
|
|
329
|
+
f"{default_server_certificate_path}"
|
|
330
|
+
print_api(message, **(print_kwargs or {}))
|
|
331
|
+
|
|
332
|
+
message = f"SNI Handler: Server Certificate current 'Subject Alternative Names': " \
|
|
333
|
+
f"{subject_alternate_names}"
|
|
334
|
+
print_api(message, **(print_kwargs or {}))
|
|
335
|
+
|
|
336
|
+
# Since new default certificate was created we need to create new SSLContext and add the certificate.
|
|
337
|
+
# You need to build new context and exchange the context that being inherited from the main socket,
|
|
338
|
+
# or else the context will receive previous certificate each time.
|
|
339
|
+
self.sni_received_parameters.ssl_socket.context = (
|
|
340
|
+
creator.create_server_ssl_context___load_certificate_and_key(
|
|
341
|
+
default_server_certificate_path,
|
|
342
|
+
None,
|
|
343
|
+
inherit_from=self.sni_received_parameters.ssl_socket.context,
|
|
344
|
+
enable_sslkeylogfile_env_to_client_ssl_context=self.enable_sslkeylogfile_env_to_client_ssl_context,
|
|
345
|
+
sslkeylog_file_path=self.sslkeylog_file_path
|
|
346
|
+
)
|
|
347
|
+
)
|
|
124
348
|
else:
|
|
125
|
-
message = f"
|
|
126
|
-
|
|
127
|
-
print_api(message, **print_kwargs)
|
|
128
|
-
|
|
129
|
-
# Setting "server_hostname" as a domain.
|
|
130
|
-
sni_received_dict['ssl_socket'].server_hostname = service_name_from_sni
|
|
131
|
-
message = \
|
|
132
|
-
f"SNI Handler: port {sni_received_dict['ssl_socket'].getsockname()[1]}: " \
|
|
133
|
-
f"Incoming connection for [{sni_received_dict['ssl_socket'].server_hostname}]"
|
|
134
|
-
print_api(message, **print_kwargs)
|
|
135
|
-
except Exception as exception_object:
|
|
136
|
-
message = f"SNI Handler: Undocumented exception general settings section: {exception_object}"
|
|
137
|
-
print_api(message, error_type=True, logger_method="error", traceback_string=True, oneline=True,
|
|
138
|
-
**print_kwargs)
|
|
139
|
-
pass
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def sni_add_domain_to_default_server_certificate(sni_received_dict: dict, config: dict, print_kwargs: dict = None):
|
|
143
|
-
# Check if incoming domain is already in the parent domains of 'domains_all_times' list.
|
|
144
|
-
if not any(x in sni_received_dict['ssl_socket'].server_hostname for x in
|
|
145
|
-
config['certificates']['domains_all_times']):
|
|
146
|
-
message = f"SNI Handler: Current domain is not in known domains list. Adding."
|
|
147
|
-
print_api(message, **print_kwargs)
|
|
148
|
-
# In the past was using 'certauth' to extract tlds, but it works only in online mode, so rewrote
|
|
149
|
-
# the function to disable online fetching of TLD snapshot.
|
|
150
|
-
# Initialize 'certauth' object.
|
|
151
|
-
# certificate_object = CertificateAuthority(certificate_ca_name, certificate_ca_filepath)
|
|
152
|
-
# Extract parent domain from the current SNI domain.
|
|
153
|
-
# parent_domain = certificate_object.get_wildcard_domain(service_name_from_sni)
|
|
154
|
-
|
|
155
|
-
# Extract parent domain from the current SNI domain.
|
|
156
|
-
parent_domain = get_domain_without_first_subdomain_if_no_subdomain_return_as_is(
|
|
157
|
-
sni_received_dict['ssl_socket'].server_hostname)
|
|
158
|
-
# Add the parent domain to the known domains list.
|
|
159
|
-
config['certificates']['domains_all_times'].append(parent_domain)
|
|
160
|
-
|
|
161
|
-
default_server_certificate_path, subject_alternate_names = \
|
|
162
|
-
certificator.create_overwrite_default_server_certificate_ca_signed(config=config)
|
|
163
|
-
|
|
164
|
-
if default_server_certificate_path:
|
|
165
|
-
message = f"SNI Handler: Default Server Certificate was created / overwritten: " \
|
|
166
|
-
f"{default_server_certificate_path}"
|
|
167
|
-
print_api(message, **print_kwargs)
|
|
168
|
-
|
|
169
|
-
message = f"SNI Handler: Server Certificate current 'Subject Alternative Names': " \
|
|
170
|
-
f"{subject_alternate_names}"
|
|
171
|
-
print_api(message, **print_kwargs)
|
|
172
|
-
|
|
173
|
-
# Since new default certificate was created we need to create new SSLContext and add the certificate.
|
|
174
|
-
# You need to build new context and exchange the context that being inherited from the main socket,
|
|
175
|
-
# or else the context will receive previous certificate each time.
|
|
176
|
-
sni_received_dict['ssl_socket'].context = \
|
|
177
|
-
creator.create_server_ssl_context___load_certificate_and_key(default_server_certificate_path, None)
|
|
178
|
-
else:
|
|
179
|
-
message = f"Couldn't create / overwrite Default Server Certificate: {default_server_certificate_path}"
|
|
180
|
-
print_api(message, error_type=True, logger_method="critical", **print_kwargs)
|
|
181
|
-
sys.exit()
|
|
349
|
+
message = f"Couldn't create / overwrite Default Server Certificate: {default_server_certificate_path}"
|
|
350
|
+
raise SNIDefaultCertificateCreationError(message)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import socket
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
LOCALHOST_IPV4: str = '127.0.0.1'
|
|
6
|
+
DEFAULT_IPV4: str = socket.gethostbyname(socket.gethostname())
|
|
7
|
+
THIS_DEVICE_IP_LIST: list = [LOCALHOST_IPV4, DEFAULT_IPV4]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_local_network_interfaces_ip_address(family_type: str = None, ip_only: bool = False) -> list:
|
|
11
|
+
"""
|
|
12
|
+
Return list of IP addresses of local network interfaces.
|
|
13
|
+
|
|
14
|
+
:param family_type: string, available options:
|
|
15
|
+
None: default, returns both ipv4 and ipv6 addresses.
|
|
16
|
+
"ipv4": returns only ipv4 addresses.
|
|
17
|
+
"ipv6": returns only ipv6 addresses.
|
|
18
|
+
:param ip_only: bool, if True, returns only IP addresses, if False, returns tuples with all objects.
|
|
19
|
+
:return: list.
|
|
20
|
+
"""
|
|
21
|
+
family: int = 0
|
|
22
|
+
if not family_type:
|
|
23
|
+
family = 0
|
|
24
|
+
elif family_type == "ipv4":
|
|
25
|
+
family = socket.AF_INET
|
|
26
|
+
elif family_type == "ipv6":
|
|
27
|
+
family = socket.AF_INET6
|
|
28
|
+
|
|
29
|
+
network_interfaces_tuples = list(socket.getaddrinfo(socket.gethostname(), None, family=family))
|
|
30
|
+
|
|
31
|
+
if not ip_only:
|
|
32
|
+
return network_interfaces_tuples
|
|
33
|
+
else:
|
|
34
|
+
return [i[4][0] for i in network_interfaces_tuples]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_destination_address_from_socket(socket_object):
|
|
38
|
+
"""
|
|
39
|
+
Return destination IP and port.
|
|
40
|
+
|
|
41
|
+
:param socket_object:
|
|
42
|
+
:return:
|
|
43
|
+
"""
|
|
44
|
+
# return ip_address, port
|
|
45
|
+
return socket_object.getsockname()[0], socket_object.getsockname()[1]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_source_address_from_socket(socket_object):
|
|
49
|
+
"""
|
|
50
|
+
Return source IP and port.
|
|
51
|
+
|
|
52
|
+
:param socket_object:
|
|
53
|
+
:return:
|
|
54
|
+
"""
|
|
55
|
+
# return ip_address, port
|
|
56
|
+
return socket_object.getpeername()[0], socket_object.getpeername()[1]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_source_destination(socket_object):
|
|
60
|
+
return get_source_address_from_socket(socket_object), get_destination_address_from_socket(socket_object)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def set_socket_timeout(socket_object, seconds: int = 1):
|
|
64
|
+
# Setting timeout on the socket before "accept()" drastically slows down connections.
|
|
65
|
+
socket_object.settimeout(seconds)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def get_default_ip_address() -> str:
|
|
69
|
+
"""
|
|
70
|
+
Get the default IP address of the system (in other words the interface IPv4 that is used for internet connection).
|
|
71
|
+
:return: string.
|
|
72
|
+
"""
|
|
73
|
+
return socket.gethostbyname(socket.gethostname())
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def is_socket_closed(socket_object) -> bool:
|
|
77
|
+
"""
|
|
78
|
+
Check if the socket is closed.
|
|
79
|
+
:param socket_object: socket object or ssl socket object.
|
|
80
|
+
:return: bool.
|
|
81
|
+
"""
|
|
82
|
+
try:
|
|
83
|
+
# If the socket is closed, the fileno() method will raise an exception or return -1.
|
|
84
|
+
|
|
85
|
+
if socket_object.fileno() == -1:
|
|
86
|
+
return True
|
|
87
|
+
else:
|
|
88
|
+
return False
|
|
89
|
+
except socket.error:
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def get_host_name_from_ip_address(ip_address: str) -> str:
|
|
94
|
+
"""
|
|
95
|
+
Get the host name from the IP address.
|
|
96
|
+
:param ip_address: string, IP address.
|
|
97
|
+
:return: string, host name.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
host_name, alias_list, ipaddr_list = socket.gethostbyaddr(ip_address)
|
|
101
|
+
_ = alias_list, ipaddr_list
|
|
102
|
+
|
|
103
|
+
return host_name
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def wait_for_ip_bindable(
|
|
107
|
+
ip: str,
|
|
108
|
+
port: int = 0,
|
|
109
|
+
timeout: float = 15.0,
|
|
110
|
+
interval: float = 0.5,
|
|
111
|
+
) -> None:
|
|
112
|
+
"""
|
|
113
|
+
Wait until a single IP is bindable (or timeout).
|
|
114
|
+
|
|
115
|
+
Raises TimeoutError if the IP cannot be bound within 'timeout' seconds.
|
|
116
|
+
"""
|
|
117
|
+
deadline = time.time() + timeout
|
|
118
|
+
last_err: OSError | None = None
|
|
119
|
+
|
|
120
|
+
while time.time() < deadline:
|
|
121
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
122
|
+
try:
|
|
123
|
+
s.bind((ip, port))
|
|
124
|
+
s.close()
|
|
125
|
+
return # success
|
|
126
|
+
except OSError as e:
|
|
127
|
+
last_err = e
|
|
128
|
+
s.close()
|
|
129
|
+
time.sleep(interval)
|
|
130
|
+
|
|
131
|
+
raise TimeoutError(
|
|
132
|
+
f"IP {ip} not bindable within {timeout} seconds; "
|
|
133
|
+
f"last error: {last_err}"
|
|
134
|
+
)
|