atomicshop 3.1.13__py3-none-any.whl → 3.2.0__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.

Potentially problematic release.


This version of atomicshop might be problematic. Click here for more details.

atomicshop/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """Atomic Basic functions and classes to make developer life easier"""
2
2
 
3
3
  __author__ = "Den Kras"
4
- __version__ = '3.1.13'
4
+ __version__ = '3.2.0'
@@ -4,14 +4,13 @@ import queue
4
4
  import socket
5
5
 
6
6
  from ..wrappers.socketw import receiver, sender, socket_client, base
7
- from .. import websocket_parse
7
+ from .. import websocket_parse, ip_addresses
8
8
  from ..http_parse import HTTPRequestParse, HTTPResponseParse
9
9
  from ..basics import threads, tracebacks
10
10
  from ..print_api import print_api
11
11
 
12
12
  from .message import ClientMessage
13
- from .initialize_engines import assign_class_by_domain
14
- from . import config_static
13
+ from . import config_static, initialize_engines
15
14
 
16
15
 
17
16
  def thread_worker_main(
@@ -218,30 +217,45 @@ def thread_worker_main(
218
217
  custom_client_pem_certificate_path = pem_file_path
219
218
  break
220
219
 
221
- # If we're on localhost, then use external services list in order to resolve the domain:
222
- # config['tcp']['forwarding_dns_service_ipv4_list___only_for_localhost']
223
- if client_message.client_ip in base.THIS_DEVICE_IP_LIST:
220
+ # Check if the destination service is an ip address or a domain name.
221
+ if ip_addresses.is_ip_address(client_message.server_name, ip_type='ipv4'):
222
+ # If it's an ip address, connect to the ip address directly.
224
223
  service_client_instance = socket_client.SocketClient(
225
224
  service_name=client_message.server_name,
225
+ connection_ip=client_message.server_name,
226
226
  service_port=client_message.destination_port,
227
227
  tls=is_tls,
228
- dns_servers_list=[config_static.DNSServer.forwarding_dns_service_ipv4],
229
228
  logger=network_logger,
230
229
  custom_pem_client_certificate_file_path=custom_client_pem_certificate_path,
231
230
  enable_sslkeylogfile_env_to_client_ssl_context=(
232
231
  config_static.Certificates.enable_sslkeylogfile_env_to_client_ssl_context)
233
232
  )
234
- # If we're not on localhost, then connect to domain directly.
233
+ # If it's a domain name, then we'll use the DNS to resolve it.
235
234
  else:
236
- service_client_instance = socket_client.SocketClient(
237
- service_name=client_message.server_name,
238
- service_port=client_message.destination_port,
239
- tls=is_tls,
240
- logger=network_logger,
241
- custom_pem_client_certificate_file_path=custom_client_pem_certificate_path,
242
- enable_sslkeylogfile_env_to_client_ssl_context=(
243
- config_static.Certificates.enable_sslkeylogfile_env_to_client_ssl_context)
244
- )
235
+ # If we're on localhost, then use external services list in order to resolve the domain:
236
+ # config['tcp']['forwarding_dns_service_ipv4_list___only_for_localhost']
237
+ if client_message.client_ip in base.THIS_DEVICE_IP_LIST:
238
+ service_client_instance = socket_client.SocketClient(
239
+ service_name=client_message.server_name,
240
+ service_port=client_message.destination_port,
241
+ tls=is_tls,
242
+ dns_servers_list=[config_static.DNSServer.forwarding_dns_service_ipv4],
243
+ logger=network_logger,
244
+ custom_pem_client_certificate_file_path=custom_client_pem_certificate_path,
245
+ enable_sslkeylogfile_env_to_client_ssl_context=(
246
+ config_static.Certificates.enable_sslkeylogfile_env_to_client_ssl_context)
247
+ )
248
+ # If we're not on localhost, then connect to domain directly.
249
+ else:
250
+ service_client_instance = socket_client.SocketClient(
251
+ service_name=client_message.server_name,
252
+ service_port=client_message.destination_port,
253
+ tls=is_tls,
254
+ logger=network_logger,
255
+ custom_pem_client_certificate_file_path=custom_client_pem_certificate_path,
256
+ enable_sslkeylogfile_env_to_client_ssl_context=(
257
+ config_static.Certificates.enable_sslkeylogfile_env_to_client_ssl_context)
258
+ )
245
259
 
246
260
  return service_client_instance
247
261
 
@@ -336,6 +350,7 @@ def thread_worker_main(
336
350
  raise ValueError(f"Unknown side of the socket: {receiving_socket}")
337
351
 
338
352
  while True:
353
+ is_socket_closed: bool = False
339
354
  # pass the socket connect to responder.
340
355
  if side == 'Service' and client_connection_message:
341
356
  client_message = client_connection_message
@@ -343,6 +358,8 @@ def thread_worker_main(
343
358
  bytes_to_send_list: list[bytes] = create_responder_response(client_message)
344
359
  print_api(f"Got responses from connect responder, count: [{len(bytes_to_send_list)}]", logger=network_logger,
345
360
  logger_method='info')
361
+
362
+ received_raw_data = None
346
363
  else:
347
364
  client_message.reinitialize_dynamic_vars()
348
365
 
@@ -362,12 +379,6 @@ def thread_worker_main(
362
379
  # logger_method='info')
363
380
  # else:
364
381
 
365
- # TODO
366
- # if config_static.MainConfig.offline:
367
- # if side == 'Client':
368
- # # If we're in offline mode, then we'll use the offline queue to put the data for the service socket.
369
- # offline_client_service_queue.put()
370
-
371
382
  network_logger.info(
372
383
  f"Initializing Receiver for {side} cycle: {str(current_count)}")
373
384
 
@@ -395,15 +406,22 @@ def thread_worker_main(
395
406
  # the close on the opposite socket.
396
407
  record_and_statistics_write(client_message)
397
408
 
398
- if is_socket_closed:
399
- exception_or_close_in_receiving_thread = True
400
- finish_thread()
401
- return
409
+ # if is_socket_closed:
410
+ # exception_or_close_in_receiving_thread = True
411
+ # finish_thread()
412
+ # return
402
413
 
403
414
  # Now send it to requester/responder.
404
415
  if side == 'Client':
405
416
  # Send to requester.
406
417
  bytes_to_send_list: list[bytes] = create_requester_request(client_message)
418
+
419
+ # If we're in offline mode, then we'll put the request to the responder right away.
420
+ if config_static.MainConfig.offline:
421
+ print_api("Offline Mode, sending to responder directly.", logger=network_logger,
422
+ logger_method='info')
423
+ process_client_raw_data(bytes_to_send_list[0], error_message, client_message)
424
+ bytes_to_send_list = create_responder_response(client_message)
407
425
  elif side == 'Service':
408
426
  bytes_to_send_list: list[bytes] = create_responder_response(client_message)
409
427
  print_api(f"Got responses from responder, count: [{len(bytes_to_send_list)}]",
@@ -418,7 +436,8 @@ def thread_worker_main(
418
436
  client_connection_message = None
419
437
  continue
420
438
 
421
- is_socket_closed: bool = False
439
+ # is_socket_closed: bool = False
440
+ error_on_send: str = str()
422
441
  for bytes_to_send_single in bytes_to_send_list:
423
442
  client_message.reinitialize_dynamic_vars()
424
443
  client_message.timestamp = datetime.now()
@@ -428,11 +447,28 @@ def thread_worker_main(
428
447
  else:
429
448
  client_message.response_raw_bytes = bytes_to_send_single
430
449
 
431
- record_and_statistics_write(client_message)
450
+ # This records the requester or responder output, only if it is not the same as the original
451
+ # message.
452
+ if bytes_to_send_single != received_raw_data:
453
+ if side == 'Client':
454
+ client_message.action = 'client_requester'
455
+
456
+ if config_static.MainConfig.offline:
457
+ client_message.action = 'client_responder_offline'
458
+ elif side == 'Service':
459
+ client_message.action = 'service_responder'
460
+ record_and_statistics_write(client_message)
432
461
 
433
- error_on_send: str = sender.Sender(
434
- ssl_socket=sending_socket, class_message=bytes_to_send_single,
435
- logger=network_logger).send()
462
+ # If we're in offline mode, it means we're in the client thread, and we'll send the
463
+ # bytes back to the client socket.
464
+ if config_static.MainConfig.offline:
465
+ error_on_send: str = sender.Sender(
466
+ ssl_socket=receiving_socket, class_message=bytes_to_send_single,
467
+ logger=network_logger).send()
468
+ else:
469
+ error_on_send: str = sender.Sender(
470
+ ssl_socket=sending_socket, class_message=bytes_to_send_single,
471
+ logger=network_logger).send()
436
472
 
437
473
  if error_on_send:
438
474
  client_message.reinitialize_dynamic_vars()
@@ -445,11 +481,11 @@ def thread_worker_main(
445
481
 
446
482
  record_and_statistics_write(client_message)
447
483
 
448
- # If the socket was closed on message receive, then we'll break the loop only after send.
449
- if is_socket_closed or error_on_send:
450
- exception_or_close_in_receiving_thread = True
451
- finish_thread()
452
- return
484
+ # If the socket was closed on message receive, then we'll break the loop only after send.
485
+ if is_socket_closed or error_on_send:
486
+ exception_or_close_in_receiving_thread = True
487
+ finish_thread()
488
+ return
453
489
 
454
490
  # For next iteration to start in case this iteration was responsible to process connection message, we need to set it to None.
455
491
  client_connection_message = None
@@ -524,12 +560,9 @@ def thread_worker_main(
524
560
  # websocket_unmasked_frame_parser = websocket_parse.WebsocketFrameParser()
525
561
  websocket_frame_parser = websocket_parse.WebsocketFrameParser()
526
562
 
527
- # Offline queue between client and service threads.
528
- offline_client_service_queue: queue.Queue = queue.Queue()
529
-
530
563
  # Loading parser by domain, if there is no parser for current domain - general reference parser is loaded.
531
564
  # These should be outside any loop and initialized only once entering the thread.
532
- found_domain_module = assign_class_by_domain(
565
+ found_domain_module = initialize_engines.assign_class_by_domain(
533
566
  engines_list=engines_list,
534
567
  message_domain_name=server_name,
535
568
  reference_module=reference_module
@@ -556,8 +589,15 @@ def thread_worker_main(
556
589
  try:
557
590
  engine_name: str = recorder.engine_name
558
591
  client_ip, source_port = client_socket.getpeername()
559
- client_name = socket.gethostbyaddr(client_ip)[0]
560
- destination_port = client_socket.getsockname()[1]
592
+ client_name: str = socket.gethostbyaddr(client_ip)[0]
593
+ destination_port: int = client_socket.getsockname()[1]
594
+ destination_port_str: str = str(destination_port)
595
+
596
+ # If the destination port is in the on_port_connect dictionary, then we'll get the port from there.
597
+ if destination_port_str in found_domain_module.on_port_connect:
598
+ on_port_connect_value = found_domain_module.on_port_connect[destination_port_str]
599
+ _, destination_port_str = initialize_engines.get_ipv4_from_engine_on_connect_port(on_port_connect_value)
600
+ destination_port: int = int(destination_port_str)
561
601
 
562
602
  if config_static.MainConfig.offline:
563
603
  # If in offline mode, then we'll get the TCP server's input address.
@@ -599,20 +639,25 @@ def thread_worker_main(
599
639
  client_thread = threading.Thread(
600
640
  target=receive_send_start,
601
641
  args=(client_socket, service_socket_instance, client_exception_queue, None),
602
- name=f"Thread-{thread_id}-Client")
603
- client_thread.daemon = True
642
+ name=f"Thread-{thread_id}-Client",
643
+ daemon=True)
604
644
  client_thread.start()
605
645
 
606
646
  service_exception_queue: queue.Queue = queue.Queue()
607
- service_thread = threading.Thread(
608
- target=receive_send_start,
609
- args=(service_socket_instance, client_socket, service_exception_queue, client_message_connection),
610
- name=f"Thread-{thread_id}-Service")
611
- service_thread.daemon = True
612
- service_thread.start()
647
+ if not config_static.MainConfig.offline:
648
+ service_thread = threading.Thread(
649
+ target=receive_send_start,
650
+ args=(service_socket_instance, client_socket, service_exception_queue, client_message_connection),
651
+ name=f"Thread-{thread_id}-Service",
652
+ daemon=True)
653
+ service_thread.start()
613
654
 
614
655
  client_thread.join()
615
- service_thread.join()
656
+ # If we're in offline mode, then there is no service thread.
657
+ if not config_static.MainConfig.offline:
658
+ # If we're not in offline mode, then we'll wait for the service thread to finish.
659
+ # noinspection PyUnboundLocalVariable
660
+ service_thread.join()
616
661
 
617
662
  # If there was an exception in any of the threads, then we'll raise it here.
618
663
  if not client_exception_queue.empty():
@@ -81,9 +81,12 @@ class CreateModuleTemplate:
81
81
  config_lines_list.append('[engine]')
82
82
  config_lines_list.append(f'domains = [{", ".join(domains_with_quotes)}]')
83
83
  config_lines_list.append('localhost = 1\n')
84
- # config_lines_list.append(f'\n')
84
+ config_lines_list.append('[on_port_connect]')
85
+ config_lines_list.append('#5000 = "31.31.31.31"')
86
+ config_lines_list.append('#5000 = "C:\\test\\address.txt"\n')
85
87
  config_lines_list.append('[mtls]')
86
88
  config_lines_list.append('# "subdomain.domain.com" = "file_name_in_current_dir.pem"\n')
89
+ # config_lines_list.append(f'\n')
87
90
 
88
91
  config_file_path = self.new_engine_directory + os.sep + CONFIG_FILE_NAME
89
92
 
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  from pathlib import Path
3
3
 
4
+ from .. import ip_addresses
4
5
  from ..file_io import tomls
5
6
  from ..basics.classes import import_first_class_name_from_file_path
6
7
  from .engines.__reference_general import parser___reference_general, requester___reference_general, \
@@ -14,7 +15,10 @@ class ModuleCategory:
14
15
 
15
16
  self.domain_list: list = list()
16
17
  self.domain_target_dict: dict = dict()
18
+ self.port_target_dict: dict = dict()
19
+
17
20
  self.is_localhost: bool = bool()
21
+ self.on_port_connect: dict = dict()
18
22
  self.mtls: dict = dict()
19
23
 
20
24
  self.parser_file_path: str = str()
@@ -48,6 +52,9 @@ class ModuleCategory:
48
52
  self.domain_list = configuration_data['engine']['domains']
49
53
  self.is_localhost = bool(configuration_data['engine']['localhost'])
50
54
 
55
+ if 'on_port_connect' in configuration_data:
56
+ self.on_port_connect = configuration_data['on_port_connect']
57
+
51
58
  if 'mtls' in configuration_data:
52
59
  self.mtls = configuration_data['mtls']
53
60
 
@@ -75,6 +82,9 @@ class ModuleCategory:
75
82
 
76
83
  self.domain_target_dict[domain] = {'ip': None, 'port': port}
77
84
 
85
+ for port, value in self.on_port_connect.items():
86
+ self.port_target_dict[port] = {'ip': None, 'port': int(port)}
87
+
78
88
  for subdomain, file_name in self.mtls.items():
79
89
  self.mtls[subdomain] = f'{engine_directory_path}{os.sep}{file_name}'
80
90
 
@@ -100,6 +110,47 @@ class ModuleCategory:
100
110
  return 0, ''
101
111
 
102
112
 
113
+ def get_ipv4_from_engine_on_connect_port(
114
+ address_or_file_path: str
115
+ ) -> tuple[str, str] | None:
116
+ """
117
+ Function to get the IPv4 address from the engine on connect port.
118
+
119
+ :param address_or_file_path: string, "ip_address:port" or file path that was set in the engine on_port_connect.
120
+ :return: string, IPv4 address that was parsed from the 'ip_port_address'.
121
+
122
+ """
123
+
124
+ def get_ip_port_from_address(ip_port_address: str) -> tuple[str, str] | None:
125
+ """
126
+ Function to get the IP address and port from the address string.
127
+ If the address is a file path, it will return an empty string.
128
+ """
129
+ if ':' in ip_port_address:
130
+ ipv4_to_connect, port_to_connect = ip_port_address.split(':')
131
+ if ip_addresses.is_ip_address(ipv4_to_connect, ip_type='ipv4'):
132
+ return ipv4_to_connect, port_to_connect
133
+ else:
134
+ return None
135
+ else:
136
+ return None
137
+
138
+ # Try to get it as IP address.
139
+ ip_port_address_from_config = get_ip_port_from_address(address_or_file_path)
140
+
141
+ # If it is not an IP address, try to read it as a text file.
142
+ if not ip_port_address_from_config:
143
+ if os.path.isfile(address_or_file_path):
144
+ with open(address_or_file_path, 'r', encoding='utf-8') as file:
145
+ first_line = file.readline().strip()
146
+
147
+ ip_port_address_from_config = get_ip_port_from_address(first_line)
148
+ else:
149
+ return None
150
+
151
+ return ip_port_address_from_config
152
+
153
+
103
154
  def assign_class_by_domain(
104
155
  engines_list: list,
105
156
  message_domain_name: str,
@@ -133,6 +184,22 @@ def assign_class_by_domain(
133
184
  # If the domain was found in the current list of class domains, we can stop the loop
134
185
  break
135
186
 
187
+ # If the module wasn't found by the domain, check it by the port.
188
+ if not module:
189
+ # Get the list of all the ip addresses in the on_port_connect dict.
190
+ list_of_ip_addresses_per_port: list[str] = []
191
+ for port, value in function_module.on_port_connect.items():
192
+ ipv4_to_connect, _ = get_ipv4_from_engine_on_connect_port(value)
193
+ list_of_ip_addresses_per_port.append(ipv4_to_connect)
194
+
195
+ # Checking if the message domain name is in the list of ip addresses per port.
196
+ if any(x in message_domain_name for x in list_of_ip_addresses_per_port):
197
+ # Assigning module by current engine of the port
198
+ module = function_module
199
+
200
+ # If the port was found in the current list of class ports, we can stop the loop
201
+ break
202
+
136
203
  # If none of the domains were found in the engine domains list, then we'll assign reference module.
137
204
  # It's enough to check only parser, since responder and recorder also will be empty.
138
205
  # This section is also relevant if SNI came empty in the request from the client and no domain was passed by the
@@ -196,6 +196,11 @@ def startup_output(system_logger, script_version: str):
196
196
  for domain, ip_port in engine.domain_target_dict.items():
197
197
  dns_targets.append(ip_port['ip'])
198
198
  print_api.print_api(f"[*] DNS Targets: {dns_targets}", logger=system_logger)
199
+
200
+ if engine.on_port_connect:
201
+ print_api.print_api(f"[*] Connect Ports to IPs: {list(engine.on_port_connect.values())}", logger=system_logger)
202
+ print_api.print_api(f"[*] Connect Ports to IPs Targets: {list(engine.port_target_dict.values())}", logger=system_logger)
203
+
199
204
  # print_api.print_api(f"[*] TCP Listening Interfaces: {engine.tcp_listening_address_list}", logger=system_logger)
200
205
 
201
206
  if config_static.DNSServer.enable:
@@ -235,13 +240,15 @@ def get_ipv4s_for_tcp_server():
235
240
 
236
241
  # Create a list of all the domains in all the engines.
237
242
  domains_to_create_ips_for: list[str] = list()
243
+ ports_to_create_ips_for: list[str] = list()
238
244
  for engine in config_static.ENGINES_LIST:
239
245
  domains_to_create_ips_for += list(engine.domain_target_dict.keys())
246
+ ports_to_create_ips_for += list(engine.on_port_connect.keys())
240
247
 
241
248
  engine_ips: list[str] = list()
242
249
  # Check if we need the localhost ips (12.0.0.1) or external local ips (192.168.0.100).
243
250
  if config_static.ENGINES_LIST[0].is_localhost:
244
- create_ips: int = len(domains_to_create_ips_for)
251
+ create_ips: int = len(domains_to_create_ips_for) + len(ports_to_create_ips_for)
245
252
 
246
253
  # Generate the list of localhost ips.
247
254
  for i in range(create_ips):
@@ -288,7 +295,10 @@ def get_ipv4s_for_tcp_server():
288
295
  # If the domain is in the list of domains to create IPs for, add the IP to the engine.
289
296
  if domain in domains_to_create_ips_for:
290
297
  engine.domain_target_dict[domain]['ip'] = engine_ips.pop(0)
291
-
298
+ for port in engine.on_port_connect.keys():
299
+ # If the port is in the list of ports to create IPs for, add the IP to the engine.
300
+ if port in ports_to_create_ips_for:
301
+ engine.port_target_dict[port]['ip'] = engine_ips.pop(0)
292
302
 
293
303
  def mitm_server(config_file_path: str, script_version: str):
294
304
  on_exit.register_exit_handler(exit_cleanup, at_exit=False, kill_signal=False)
@@ -190,7 +190,7 @@ def stop_remove_containers_by_image_name(image_name: str):
190
190
  container_instance.stop()
191
191
  print_api(f"Removing container: [{container_instance.name}]. Short ID: [{container_instance.short_id}]")
192
192
  container_instance.remove()
193
- print_api(f"Container removed: [{container_instance.name}]. Short ID: [{container_instance.short_id}]", color='green')
193
+ print_api(f"Container removed: [{container_instance.name}]. Short ID: [{container_instance.short_id}]", color='blue')
194
194
 
195
195
 
196
196
  client = docker.from_env()
@@ -1,3 +1,4 @@
1
+ import os.path
1
2
  import threading
2
3
  import select
3
4
  from typing import Literal, Union
@@ -427,8 +428,11 @@ class SocketWrapper:
427
428
  # If engines were passed, we will use the listening addresses from the engines.
428
429
  if not self.no_engine_usage_enable:
429
430
  for engine in self.engines_list:
431
+ # Combine the domain and port dicts.
432
+ connection_dict: dict = {**engine.domain_target_dict, **engine.port_target_dict}
433
+
430
434
  # Start all the regular listening interfaces.
431
- for domain, ip_port_dict in engine.domain_target_dict.items():
435
+ for domain_or_port, ip_port_dict in connection_dict.items():
432
436
  ip_address: str = ip_port_dict['ip']
433
437
  port = int(ip_port_dict['port'])
434
438
  socket_by_port = self.create_socket_ipv4_tcp(ip_address, port)
@@ -511,10 +515,28 @@ class SocketWrapper:
511
515
 
512
516
  domain_from_engine = None
513
517
  for engine in self.engines_list:
518
+ # Get the domain to connect on this process in case on no SNI provided.
514
519
  for domain, ip_port_dict in engine.domain_target_dict.items():
515
520
  if ip_port_dict['ip'] == listening_ip:
516
521
  domain_from_engine = domain
517
522
  break
523
+ # If there was no domain found, try to find the IP address for port.
524
+ if not domain_from_engine:
525
+ for port, file_or_ip in engine.port_target_dict.items():
526
+ if file_or_ip['ip'] == listening_ip:
527
+ # Get the value from the 'on_port_connect' dictionary.
528
+ address_or_file_path: str = engine.on_port_connect[str(listening_port)]
529
+ ip_port_address_from_config: tuple = initialize_engines.get_ipv4_from_engine_on_connect_port(
530
+ address_or_file_path)
531
+ if not ip_port_address_from_config:
532
+ raise ValueError(
533
+ f"Invalid IP address or file path in 'on_port_connect' for port "
534
+ f"{listening_port}: {address_or_file_path}"
535
+ )
536
+
537
+ domain_from_engine = ip_port_address_from_config[0]
538
+
539
+ break
518
540
 
519
541
  self.logger.info(f"Requested domain setting: {domain_from_engine}")
520
542
 
@@ -598,6 +620,9 @@ class SocketWrapper:
598
620
  print_kwargs={'logger': self.logger}
599
621
  )
600
622
 
623
+ # Get the real tls version after connection is wrapped.
624
+ tls_version = ssl_client_socket.version()
625
+
601
626
  # If the 'domain_from_dns_server' is empty, it means that the 'engine_name' is not set.
602
627
  # In this case we will set the 'engine_name' to from the SNI.
603
628
  if engine_name == '':
@@ -697,8 +722,18 @@ def get_engine_name(domain: str, engine_list: list):
697
722
  :return: string, engine name.
698
723
  """
699
724
 
725
+ engine_name: str = ''
700
726
  for engine in engine_list:
727
+ # Get engine name by domain.
701
728
  if domain in engine.domain_target_dict:
702
- return engine.engine_name
729
+ engine_name = engine.engine_name
730
+
731
+ # If didn't find by domain, try to find by port.
732
+ if engine_name == '':
733
+ for port, ip_port_to_connect_value in engine.on_port_connect.items():
734
+ ipv4_to_connect, _ = initialize_engines.get_ipv4_from_engine_on_connect_port(ip_port_to_connect_value)
735
+ if ipv4_to_connect == domain:
736
+ engine_name = engine.engine_name
737
+ break
703
738
 
704
- return ''
739
+ return engine_name
@@ -34,12 +34,9 @@ def is_tls(client_socket) -> Optional[Tuple[str, str]]:
34
34
 
35
35
  first_bytes = receiver.peek_first_bytes(client_socket, bytes_amount=3)
36
36
 
37
- # Ensure we got enough data.
38
- if len(first_bytes) < 3:
39
- return None
40
-
41
- # Extract the content type and version
42
- content_type, version_major, version_minor = first_bytes
37
+ # Sometimes only one byte is available, so we need to handle that case.
38
+ # convert to a tuple of ints, add three Nones, and keep only the first 3 items.
39
+ content_type, version_major, version_minor = (tuple(first_bytes) + (None, None, None))[:3]
43
40
 
44
41
  # Map TLS content types to their string representation.
45
42
  content_type_map = {
@@ -52,19 +49,19 @@ def is_tls(client_socket) -> Optional[Tuple[str, str]]:
52
49
 
53
50
  # Map TLS version bytes to their string representation.
54
51
  version_map = {
55
- (0x03, 0x00): "SSL 3.0",
56
- (0x03, 0x01): "TLS 1.0",
57
- (0x03, 0x02): "TLS 1.1",
58
- (0x03, 0x03): "TLS 1.2/1.3"
52
+ (0x03, 0x00): "SSLv3.0",
53
+ (0x03, 0x01): "TLSv1.0",
54
+ (0x03, 0x02): "TLSv1.1",
55
+ (0x03, 0x03): "TLSv1.2/1.3"
59
56
  # Remember, you can't definitively differentiate 1.2 and 1.3 just from these bytes
60
57
  }
61
58
 
62
59
  # Get the tuple of the type and version as strings.
63
- tls_content_and_version_tuple: tuple = \
60
+ tls_content_and_version_tuple: tuple[str, str] = \
64
61
  content_type_map.get(content_type), version_map.get((version_major, version_minor))
65
62
 
66
63
  # If both parts of the tuple are not None, return the protocol type.
67
- if tls_content_and_version_tuple[0] and tls_content_and_version_tuple[1]:
64
+ if tls_content_and_version_tuple[0]:
68
65
  return tls_content_and_version_tuple
69
66
  else:
70
67
  return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 3.1.13
3
+ Version: 3.2.0
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
- atomicshop/__init__.py,sha256=m7-cUoalFlb_P0L7dq5ZKqms-k_BdzctCtwbbLtMGyQ,123
1
+ atomicshop/__init__.py,sha256=ia_QL-nkKuszLHN2XwNJas4i3cauADZSyyLAKJGWcD0,122
2
2
  atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
3
3
  atomicshop/_create_pdf_demo.py,sha256=Yi-PGZuMg0RKvQmLqVeLIZYadqEZwUm-4A9JxBl_vYA,3713
4
4
  atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
@@ -136,16 +136,16 @@ atomicshop/file_io/xmls.py,sha256=zh3SuK-dNaFq2NDNhx6ivcf4GYCfGM8M10PcEwDSpxk,21
136
136
  atomicshop/mitm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
137
137
  atomicshop/mitm/config_static.py,sha256=N3D06C_wUytwm80PQCL90ZGHLMHeQlCcI2XQQU2FZ1I,8145
138
138
  atomicshop/mitm/config_toml_editor.py,sha256=2p1CMcktWRR_NW-SmyDwylu63ad5e0-w1QPMa8ZLDBw,1635
139
- atomicshop/mitm/connection_thread_worker.py,sha256=r3yKhT96oh_vyFIi858gYrpFyqIpoDwxIMAOo2S8Jl4,29385
139
+ atomicshop/mitm/connection_thread_worker.py,sha256=E_DNvP4lbbcoeFziiG10Aa7bg7Ka09wdwV_uqaRm5rU,32554
140
140
  atomicshop/mitm/import_config.py,sha256=gCH1hV-CxBAdwjeFJu1I5ntbm5hqzcB0y0vzRPkzra0,16936
141
- atomicshop/mitm/initialize_engines.py,sha256=Pj9k3iLdoE0KAl3QWG1Z10LHUHY8WQivSPFnQLhCWrc,7385
141
+ atomicshop/mitm/initialize_engines.py,sha256=N4JVoC7PXN8GxUBXvNCXS-ZA5ag6rXqYXyZ3EyHOcLM,10318
142
142
  atomicshop/mitm/message.py,sha256=CDhhm4BTuZE7oNZCjvIZ4BuPOW4MuIzQLOg91hJaxDI,3065
143
- atomicshop/mitm/mitm_main.py,sha256=cfQLaPaUZgluatlZDRiToi14ekZbPT2KM4-AiivTs3w,30055
143
+ atomicshop/mitm/mitm_main.py,sha256=at9uxnXF_4b273HRXt9UGkERkHantRTOAvRtlYYI68E,30774
144
144
  atomicshop/mitm/recs_files.py,sha256=ZAAD0twun-FtmbSniXe3XQhIlawvANNB_HxwbHj7kwI,3151
145
145
  atomicshop/mitm/shared_functions.py,sha256=0lzeyINd44sVEfFbahJxQmz6KAMWbYrW5ou3UYfItvw,1777
146
146
  atomicshop/mitm/statistic_analyzer.py,sha256=5_sAYGX2Xunzo_pS2W5WijNCwr_BlGJbbOO462y_wN4,27533
147
147
  atomicshop/mitm/engines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
148
- atomicshop/mitm/engines/create_module_template.py,sha256=Qu7ca72BsbcPrtonJM1ok7pGlwu0u6gEDap1ht6vMw0,5577
148
+ atomicshop/mitm/engines/create_module_template.py,sha256=_1aoB-ety98szV__2jnn6cohxmrClU5mOnf6u-nq_Zc,5762
149
149
  atomicshop/mitm/engines/create_module_template_main_example.py,sha256=LeQ44Rp2Gi_KbIDY_4OMS0odkSK3zFZWra_oAka5eJY,243
150
150
  atomicshop/mitm/engines/__parent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
151
151
  atomicshop/mitm/engines/__parent/parser___parent.py,sha256=HHaCXhScl3OlPjz6eUxsDpJaZyk6BNuDMc9xCkeo2Ws,661
@@ -224,7 +224,7 @@ atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py,sha256=htzwb2ROYI8yyc8
224
224
  atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py,sha256=AEkjnqEhfCbCUcYaulv3uba5hZjTB03xm-puAINsZGM,5488
225
225
  atomicshop/wrappers/ctyping/msi_windows_installer/tables.py,sha256=tHsu0YfBgzuIk9L-PyqLgU_IzyVbCfy8L1EqelNnvWk,17674
226
226
  atomicshop/wrappers/dockerw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
227
- atomicshop/wrappers/dockerw/dockerw.py,sha256=DIhn3RTG3m7bJ-NTevoF1s1EeGHgi2y-m07oY5xJ_ls,10640
227
+ atomicshop/wrappers/dockerw/dockerw.py,sha256=GgPSvXxJj15kZ-LPiaHLl8aekof53sSP_U-vUMUe7_8,10639
228
228
  atomicshop/wrappers/dockerw/install_docker.py,sha256=7NTMxCPBesr0QcqB0RZ5YU0I8FDPtNux_mYAX28wI0I,9982
229
229
  atomicshop/wrappers/elasticsearchw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
230
230
  atomicshop/wrappers/elasticsearchw/config_basic.py,sha256=fDujtrjEjbWiYh_WQ3OcYp_8mXhXPYeKLy4wSPL5qM0,1177
@@ -331,14 +331,14 @@ atomicshop/wrappers/socketw/sender.py,sha256=aX_K8l_rHjd5AWb8bi5mt8-YTkMYVRDB6Dn
331
331
  atomicshop/wrappers/socketw/sni.py,sha256=q-F-R1KtE94g8WGrR3QHgi-otXZJUPBprEwQqnY80_A,17639
332
332
  atomicshop/wrappers/socketw/socket_client.py,sha256=AQKjm_GZLH2PO7gkFbwzIbXzUXxRFvBTc6onrgapFqs,22048
333
333
  atomicshop/wrappers/socketw/socket_server_tester.py,sha256=Qobmh4XV8ZxLUaw-eW4ESKAbeSLecCKn2OWFzMhadk0,6420
334
- atomicshop/wrappers/socketw/socket_wrapper.py,sha256=3sEz1VUar1JJU8KxocrOT09-vIh5hLpE29aTZH9TNC8,41117
335
- atomicshop/wrappers/socketw/ssl_base.py,sha256=kmiif84kMhBr5yjQW17p935sfjR5JKG0LxIwBA4iVvU,2275
334
+ atomicshop/wrappers/socketw/socket_wrapper.py,sha256=_q_QDdyQjSmc0Q0WbMDQDajqDx0_AO3IqlL78Pk4nkE,43179
335
+ atomicshop/wrappers/socketw/ssl_base.py,sha256=62-hPm7zla1rh3m_WvDnXqKH-sDUTdiRptD8STCkgdk,2313
336
336
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=WcNyaqEZ82S5-f3kzqi1nllNT2Nd2P_zg8HqCc7vW4s,4120
337
337
  atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
338
338
  atomicshop/wrappers/winregw/winreg_installed_software.py,sha256=Qzmyktvob1qp6Tjk2DjLfAqr_yXV0sgWzdMW_9kwNjY,2345
339
339
  atomicshop/wrappers/winregw/winreg_network.py,sha256=ih0BVNwByLvf9F_Lac4EdmDYYJA3PzMvmG0PieDZrsE,9905
340
- atomicshop-3.1.13.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
341
- atomicshop-3.1.13.dist-info/METADATA,sha256=5LMH6VMuu4OhGpNqI2y4YObNGBe0k0ZU-GeGaqZl-xM,10671
342
- atomicshop-3.1.13.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
343
- atomicshop-3.1.13.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
344
- atomicshop-3.1.13.dist-info/RECORD,,
340
+ atomicshop-3.2.0.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
341
+ atomicshop-3.2.0.dist-info/METADATA,sha256=VKefd8URMpY8zVcDX7iiRnpIcssdPlobeUlqhIyA7HQ,10670
342
+ atomicshop-3.2.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
343
+ atomicshop-3.2.0.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
344
+ atomicshop-3.2.0.dist-info/RECORD,,