atomicshop 2.16.27__py3-none-any.whl → 2.16.29__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 +1 -1
- atomicshop/archiver/zips.py +5 -6
- atomicshop/basics/strings.py +2 -2
- atomicshop/dns.py +8 -6
- atomicshop/file_io/file_io.py +7 -7
- atomicshop/filesystem.py +6 -7
- atomicshop/http_parse.py +12 -18
- atomicshop/mitm/connection_thread_worker.py +183 -227
- atomicshop/mitm/engines/__parent/parser___parent.py +1 -4
- atomicshop/mitm/engines/__parent/recorder___parent.py +1 -1
- atomicshop/mitm/message.py +0 -17
- atomicshop/mitm/mitm_main.py +29 -27
- atomicshop/print_api.py +13 -49
- atomicshop/system_resource_monitor.py +8 -8
- atomicshop/system_resources.py +8 -8
- atomicshop/web.py +10 -10
- atomicshop/wrappers/loggingw/filters.py +23 -0
- atomicshop/wrappers/loggingw/formatters.py +12 -0
- atomicshop/wrappers/loggingw/handlers.py +30 -0
- atomicshop/wrappers/loggingw/loggingw.py +135 -8
- atomicshop/wrappers/playwrightw/engine.py +6 -7
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/socketw/dns_server.py +1 -1
- atomicshop/wrappers/socketw/sender.py +36 -27
- atomicshop/wrappers/socketw/socket_client.py +6 -5
- atomicshop/wrappers/socketw/socket_wrapper.py +45 -7
- {atomicshop-2.16.27.dist-info → atomicshop-2.16.29.dist-info}/METADATA +1 -1
- {atomicshop-2.16.27.dist-info → atomicshop-2.16.29.dist-info}/RECORD +31 -31
- {atomicshop-2.16.27.dist-info → atomicshop-2.16.29.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.16.27.dist-info → atomicshop-2.16.29.dist-info}/WHEEL +0 -0
- {atomicshop-2.16.27.dist-info → atomicshop-2.16.29.dist-info}/top_level.txt +0 -0
|
@@ -2,7 +2,7 @@ from datetime import datetime
|
|
|
2
2
|
|
|
3
3
|
from ..wrappers.socketw import receiver, sender, socket_client, base
|
|
4
4
|
from ..http_parse import HTTPRequestParse, HTTPResponseParse
|
|
5
|
-
from ..basics
|
|
5
|
+
from ..basics import threads, tracebacks
|
|
6
6
|
from ..print_api import print_api
|
|
7
7
|
|
|
8
8
|
from .message import ClientMessage
|
|
@@ -10,9 +10,8 @@ from .initialize_engines import assign_class_by_domain
|
|
|
10
10
|
from . import config_static
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
# Thread function on client connect.
|
|
14
13
|
def thread_worker_main(
|
|
15
|
-
|
|
14
|
+
client_socket,
|
|
16
15
|
process_commandline: str,
|
|
17
16
|
is_tls: bool,
|
|
18
17
|
tls_type: str,
|
|
@@ -24,7 +23,7 @@ def thread_worker_main(
|
|
|
24
23
|
reference_module
|
|
25
24
|
):
|
|
26
25
|
def output_statistics_csv_row():
|
|
27
|
-
# If there is no '.code' attribute in HTTPResponse, this means that this is not
|
|
26
|
+
# If there is no '.code' attribute in HTTPResponse, this means that this is not an HTTP message, so there is no
|
|
28
27
|
# status code.
|
|
29
28
|
try:
|
|
30
29
|
http_status_code: str = ','.join([str(x.code) for x in client_message.response_list_of_raw_decoded])
|
|
@@ -45,6 +44,15 @@ def thread_worker_main(
|
|
|
45
44
|
|
|
46
45
|
response_size_bytes = ','.join([str(len(x)) for x in client_message.response_list_of_raw_bytes])
|
|
47
46
|
|
|
47
|
+
if statistics_error_list and len(statistics_error_list) > 1:
|
|
48
|
+
error_string = '||'.join(statistics_error_list)
|
|
49
|
+
elif statistics_error_list and len(statistics_error_list) == 1:
|
|
50
|
+
error_string = statistics_error_list[0]
|
|
51
|
+
elif not statistics_error_list:
|
|
52
|
+
error_string = str()
|
|
53
|
+
else:
|
|
54
|
+
raise ValueError(f"Error in statistics error list. Values: {statistics_error_list}")
|
|
55
|
+
|
|
48
56
|
statistics_writer.write_row(
|
|
49
57
|
host=client_message.server_name,
|
|
50
58
|
tls_type=tls_type,
|
|
@@ -58,75 +66,129 @@ def thread_worker_main(
|
|
|
58
66
|
response_size_bytes=response_size_bytes,
|
|
59
67
|
recorded_file_path=client_message.recorded_file_path,
|
|
60
68
|
process_cmd=process_commandline,
|
|
61
|
-
error=
|
|
69
|
+
error=error_string
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def record_and_statistics_write():
|
|
73
|
+
# If recorder wasn't executed before, then execute it now
|
|
74
|
+
if config_static.LogRec.enable_request_response_recordings_in_logs:
|
|
75
|
+
recorded_file = recorder(
|
|
76
|
+
class_client_message=client_message, record_path=config_static.LogRec.recordings_path).record()
|
|
77
|
+
client_message.recorded_file_path = recorded_file
|
|
78
|
+
|
|
79
|
+
# Save statistics file.
|
|
80
|
+
output_statistics_csv_row()
|
|
81
|
+
|
|
82
|
+
def parse_http():
|
|
83
|
+
nonlocal error_message
|
|
84
|
+
# Parsing the raw bytes as HTTP.
|
|
85
|
+
request_decoded = HTTPRequestParse(client_message.request_raw_bytes)
|
|
86
|
+
# Getting the status of http parsing
|
|
87
|
+
request_is_http, http_parsing_reason, http_parsing_error = request_decoded.check_if_http()
|
|
88
|
+
|
|
89
|
+
# Currently, we don't care if it's HTTP. If there was no error we can continue. Just log the reason.
|
|
90
|
+
if not http_parsing_error:
|
|
91
|
+
print_api(http_parsing_reason, logger=network_logger, logger_method='info')
|
|
92
|
+
# If there was error - the request is really HTTP, but there's a problem with its structure.
|
|
93
|
+
else:
|
|
94
|
+
client_message.error = http_parsing_reason
|
|
95
|
+
error_message = (
|
|
96
|
+
f'HTTP Parse Request Error: '
|
|
97
|
+
f'The request is HTTP protocol, but there was a problem with its structure: '
|
|
98
|
+
f'{http_parsing_error}')
|
|
99
|
+
print_api(error_message, logger=network_logger, logger_method='error', color='yellow')
|
|
100
|
+
statistics_error_list.append(error_message)
|
|
101
|
+
|
|
102
|
+
# If the request is HTTP protocol.
|
|
103
|
+
if request_is_http:
|
|
104
|
+
client_message.protocol = 'HTTP'
|
|
105
|
+
network_logger.info(f"Method: {request_decoded.command}")
|
|
106
|
+
network_logger.info(f"Path: {request_decoded.path}")
|
|
107
|
+
client_message.request_raw_decoded = request_decoded
|
|
108
|
+
|
|
109
|
+
def finish_thread():
|
|
110
|
+
# At this stage there could be several times that the same socket was used to the service server - we need to
|
|
111
|
+
# close this socket as well if it still opened.
|
|
112
|
+
if service_client:
|
|
113
|
+
if service_client.socket_instance:
|
|
114
|
+
service_client.close_socket()
|
|
115
|
+
|
|
116
|
+
# If client socket is still opened - close
|
|
117
|
+
if client_socket:
|
|
118
|
+
client_socket.close()
|
|
119
|
+
network_logger.info(f"Closed client socket [{client_message.client_ip}:{client_message.source_port}]...")
|
|
120
|
+
|
|
121
|
+
network_logger.info("Thread Finished. Will continue listening on the Main thread")
|
|
122
|
+
|
|
123
|
+
# Building client message object before the loop only for any exception to occurs, since we write it to
|
|
124
|
+
# recording file in its current state.
|
|
125
|
+
client_message: ClientMessage = ClientMessage()
|
|
126
|
+
# 'recorded' boolean is needed only to write the message in case of exception in the loop or before that.
|
|
127
|
+
recorded: bool = False
|
|
128
|
+
statistics_error_list: list[str] = list()
|
|
129
|
+
|
|
130
|
+
# Only protocols that are encrypted with TLS have the server name attribute.
|
|
131
|
+
if is_tls:
|
|
132
|
+
# Get current destination domain
|
|
133
|
+
server_name = client_socket.server_hostname
|
|
134
|
+
# client_message.server_name = domain_from_dns
|
|
135
|
+
# If the protocol is not TLS, then we'll use the domain from the DNS.
|
|
136
|
+
else:
|
|
137
|
+
server_name = domain_from_dns
|
|
138
|
+
client_message.server_name = server_name
|
|
139
|
+
|
|
140
|
+
thread_id = threads.current_thread_id()
|
|
141
|
+
client_message.thread_id = thread_id
|
|
142
|
+
|
|
143
|
+
# Loading parser by domain, if there is no parser for current domain - general reference parser is loaded.
|
|
144
|
+
# These should be outside any loop and initialized only once entering the thread.
|
|
145
|
+
parser, responder, recorder = assign_class_by_domain(
|
|
146
|
+
engines_usage=config_static.TCPServer.engines_usage,
|
|
147
|
+
engines_list=engines_list,
|
|
148
|
+
message_domain_name=server_name,
|
|
149
|
+
reference_module=reference_module,
|
|
150
|
+
logger=network_logger
|
|
151
|
+
)
|
|
62
152
|
|
|
63
153
|
try:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
client_message
|
|
67
|
-
request_decoded = None
|
|
68
|
-
service_client = None
|
|
154
|
+
client_ip, source_port = client_socket.getpeername()
|
|
155
|
+
client_message.client_ip = client_ip
|
|
156
|
+
client_message.source_port = source_port
|
|
69
157
|
|
|
70
|
-
|
|
71
|
-
client_message.
|
|
72
|
-
# Get client ip and port
|
|
73
|
-
client_message.client_ip, client_message.source_port = function_client_socket_object.getpeername()
|
|
74
|
-
# Get destination port
|
|
75
|
-
client_message.destination_port = function_client_socket_object.getsockname()[1]
|
|
76
|
-
# Putting the process command line.
|
|
77
|
-
client_message.process_name = process_commandline
|
|
78
|
-
|
|
79
|
-
# Only protocols that are encrypted with TLS have the server name attribute.
|
|
80
|
-
if is_tls:
|
|
81
|
-
# Get current destination domain
|
|
82
|
-
client_message.server_name = function_client_socket_object.server_hostname
|
|
83
|
-
# client_message.server_name = domain_from_dns
|
|
84
|
-
# If the protocol is not TLS, then we'll use the domain from the DNS.
|
|
85
|
-
else:
|
|
86
|
-
client_message.server_name = domain_from_dns
|
|
87
|
-
|
|
88
|
-
network_logger.info(f"Thread Created - Client [{client_message.client_ip}:{client_message.source_port}] | "
|
|
89
|
-
f"Destination service: [{client_message.server_name}:{client_message.destination_port}]")
|
|
90
|
-
|
|
91
|
-
# Loading parser by domain, if there is no parser for current domain - general reference parser is loaded.
|
|
92
|
-
# These should be outside any loop and initialized only once entering the thread.
|
|
93
|
-
parser, responder, recorder = assign_class_by_domain(
|
|
94
|
-
engines_usage=config_static.TCPServer.engines_usage,
|
|
95
|
-
engines_list=engines_list,
|
|
96
|
-
message_domain_name=client_message.server_name,
|
|
97
|
-
reference_module=reference_module,
|
|
98
|
-
logger=network_logger
|
|
99
|
-
)
|
|
158
|
+
destination_port = client_socket.getsockname()[1]
|
|
159
|
+
client_message.destination_port = destination_port
|
|
100
160
|
|
|
101
|
-
|
|
102
|
-
|
|
161
|
+
network_logger.info(f"Thread Created - Client [{client_ip}:{source_port}] | "
|
|
162
|
+
f"Destination service: [{server_name}:{destination_port}]")
|
|
103
163
|
|
|
164
|
+
service_client = None
|
|
104
165
|
# Loop while received message is not empty, if so, close socket, since other side already closed.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
166
|
+
# noinspection PyTypeChecker
|
|
167
|
+
cycle_count: int = None
|
|
168
|
+
while True:
|
|
169
|
+
# If cycle count is None, then it's the first cycle, else it's not.
|
|
170
|
+
# The cycle_count should be added 1 in the beginning of each cycle, and not in the end, since not always
|
|
171
|
+
# the cycle will be executed till the end.
|
|
172
|
+
if cycle_count is None:
|
|
173
|
+
cycle_count = 0
|
|
174
|
+
else:
|
|
175
|
+
cycle_count += 1
|
|
176
|
+
|
|
177
|
+
recorded: bool = False
|
|
178
|
+
statistics_error_list: list[str] = list()
|
|
179
|
+
|
|
180
|
+
client_message = ClientMessage()
|
|
181
|
+
client_message.thread_id = thread_id
|
|
182
|
+
client_message.client_ip = client_ip
|
|
183
|
+
client_message.source_port = source_port
|
|
184
|
+
client_message.destination_port = destination_port
|
|
185
|
+
client_message.process_name = process_commandline
|
|
186
|
+
client_message.server_name = server_name
|
|
187
|
+
|
|
188
|
+
network_logger.info(f"Initializing Receiver on cycle: {str(cycle_count+1)}")
|
|
127
189
|
# Getting message from the client over the socket using specific class.
|
|
128
190
|
client_received_raw_data = receiver.Receiver(
|
|
129
|
-
ssl_socket=
|
|
191
|
+
ssl_socket=client_socket, logger=network_logger).receive()
|
|
130
192
|
|
|
131
193
|
# If the message is empty, then the connection was closed already by the other side,
|
|
132
194
|
# so we can close the socket as well.
|
|
@@ -137,67 +199,18 @@ def thread_worker_main(
|
|
|
137
199
|
# Getting current time of message received from client.
|
|
138
200
|
client_message.request_time_received = datetime.now()
|
|
139
201
|
|
|
140
|
-
|
|
141
|
-
# Parsing the raw bytes as HTTP.
|
|
142
|
-
try:
|
|
143
|
-
request_decoded = HTTPRequestParse(client_message.request_raw_bytes)
|
|
144
|
-
except Exception:
|
|
145
|
-
message = "There was an exception in HTTP Parsing module!"
|
|
146
|
-
print_api(
|
|
147
|
-
message, error_type=True, logger=network_logger, logger_method='critical',
|
|
148
|
-
traceback_string=True)
|
|
149
|
-
# Socket connection can be closed since we have a problem in current thread and break the loop
|
|
150
|
-
client_connection_boolean = False
|
|
151
|
-
break
|
|
152
|
-
|
|
153
|
-
# Getting the status of http parsing
|
|
154
|
-
request_is_http, http_parsing_reason, http_parsing_error = request_decoded.check_if_http()
|
|
155
|
-
|
|
156
|
-
# Currently, we don't care if it's HTTP. If there was no error we can continue. Just log the reason.
|
|
157
|
-
if not http_parsing_error:
|
|
158
|
-
network_logger.info(http_parsing_reason)
|
|
159
|
-
# If there was error - the request is really HTTP, but there's a problem with its structure.
|
|
160
|
-
# So, we'll stop the loop.
|
|
161
|
-
else:
|
|
162
|
-
client_message.error = http_parsing_reason
|
|
163
|
-
network_logger.critical(client_message.error)
|
|
164
|
-
break
|
|
202
|
+
parse_http()
|
|
165
203
|
|
|
166
|
-
#
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
network_logger.info(f"Method: {request_decoded.command}")
|
|
170
|
-
network_logger.info(f"Path: {request_decoded.path}")
|
|
171
|
-
# statistics.dict['path'] = request_decoded.path
|
|
172
|
-
client_message.request_raw_decoded = request_decoded
|
|
173
|
-
# HTTP Parsing section EOF =============================================================================
|
|
174
|
-
|
|
175
|
-
# Catching exceptions in the parser
|
|
176
|
-
try:
|
|
177
|
-
parser(client_message).parse()
|
|
178
|
-
except Exception:
|
|
179
|
-
message = "Exception in Parser"
|
|
180
|
-
print_api(
|
|
181
|
-
message, error_type=True, logger=parser.logger, logger_method='critical',
|
|
182
|
-
traceback_string=True)
|
|
183
|
-
print_api(
|
|
184
|
-
message, error_type=True, logger=network_logger, logger_method='critical',
|
|
185
|
-
traceback_string=True)
|
|
186
|
-
# At this point we can pass the exception and continue the script.
|
|
187
|
-
pass
|
|
188
|
-
# Socket connection can be closed since we have a problem in current thread and break the loop
|
|
189
|
-
client_connection_boolean = False
|
|
190
|
-
break
|
|
204
|
+
# Custom parser, should parse HTTP body or the whole message if not HTTP.
|
|
205
|
+
parser_instance = parser(client_message)
|
|
206
|
+
parser_instance.parse()
|
|
191
207
|
|
|
192
|
-
# Converting body parsed to string, since there is no strict rule for the parameter
|
|
193
|
-
#
|
|
194
|
-
|
|
195
|
-
try:
|
|
196
|
-
parser.logger.info(f"{str(client_message.request_body_parsed)[0: 100]}...")
|
|
197
|
-
except Exception:
|
|
198
|
-
pass
|
|
208
|
+
# Converting body parsed to string on logging, since there is no strict rule for the parameter
|
|
209
|
+
# to be string.
|
|
210
|
+
parser_instance.logger.info(f"{str(client_message.request_body_parsed)[0: 100]}...")
|
|
199
211
|
|
|
200
212
|
# If we're in response mode, execute responder.
|
|
213
|
+
response_raw_bytes = None
|
|
201
214
|
if config_static.TCPServer.server_response_mode:
|
|
202
215
|
# Since we're in response mode, we'll record the request anyway, after the responder did its job.
|
|
203
216
|
client_message.info = "In Server Response Mode"
|
|
@@ -206,20 +219,7 @@ def thread_worker_main(
|
|
|
206
219
|
# new entries for empty list.
|
|
207
220
|
client_message.response_list_of_raw_bytes = list()
|
|
208
221
|
# Creating response for parsed message and printing
|
|
209
|
-
|
|
210
|
-
responder.create_response(client_message)
|
|
211
|
-
except Exception:
|
|
212
|
-
message = "Exception in Responder"
|
|
213
|
-
print_api(
|
|
214
|
-
message, error_type=True, logger=responder.logger, logger_method='critical',
|
|
215
|
-
traceback_string=True)
|
|
216
|
-
print_api(
|
|
217
|
-
message, error_type=True, logger=network_logger, logger_method='critical',
|
|
218
|
-
traceback_string=True)
|
|
219
|
-
pass
|
|
220
|
-
# Socket connection can be closed since we have a problem in current thread and break the loop.
|
|
221
|
-
client_connection_boolean = False
|
|
222
|
-
break
|
|
222
|
+
responder.create_response(client_message)
|
|
223
223
|
|
|
224
224
|
# Output first 100 characters of all the responses in the list.
|
|
225
225
|
for response_raw_bytes in client_message.response_list_of_raw_bytes:
|
|
@@ -254,8 +254,11 @@ def thread_worker_main(
|
|
|
254
254
|
# be passed.
|
|
255
255
|
# If there was connection error or socket close, then "ssl_socket" of the "service_client"
|
|
256
256
|
# will be empty.
|
|
257
|
-
response_raw_bytes, client_message.error, client_message.server_ip, service_ssl_socket
|
|
258
|
-
service_client.send_receive_to_service(client_message.request_raw_bytes)
|
|
257
|
+
response_raw_bytes, client_message.error, client_message.server_ip, service_ssl_socket = (
|
|
258
|
+
service_client.send_receive_to_service(client_message.request_raw_bytes))
|
|
259
|
+
|
|
260
|
+
if client_message.error is not None:
|
|
261
|
+
statistics_error_list.append(client_message.error)
|
|
259
262
|
|
|
260
263
|
# Since we need a list for raw bytes, we'll add the 'response_raw_bytes' to our list object.
|
|
261
264
|
# But we need to re-initiate it first.
|
|
@@ -272,100 +275,53 @@ def thread_worker_main(
|
|
|
272
275
|
if not service_ssl_socket:
|
|
273
276
|
break
|
|
274
277
|
|
|
275
|
-
#
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
record_path=config_static.LogRec.recordings_path).record()
|
|
281
|
-
client_message.recorded_file_path = recorded_file
|
|
282
|
-
except Exception:
|
|
283
|
-
message = "Exception in Recorder"
|
|
284
|
-
print_api(
|
|
285
|
-
message, error_type=True, logger=recorder.logger, logger_method='critical',
|
|
286
|
-
traceback_string=True)
|
|
287
|
-
print_api(
|
|
288
|
-
message, error_type=True, logger=network_logger, logger_method='critical',
|
|
289
|
-
traceback_string=True)
|
|
290
|
-
|
|
291
|
-
function_recorded = True
|
|
292
|
-
|
|
293
|
-
# Save statistics file.
|
|
294
|
-
output_statistics_csv_row()
|
|
295
|
-
|
|
296
|
-
try:
|
|
297
|
-
# If there is a response, then send it.
|
|
298
|
-
if response_raw_bytes:
|
|
299
|
-
# Sending response/s to client no matter if in record mode or not.
|
|
300
|
-
network_logger.info(
|
|
301
|
-
f"Sending messages to client: {len(client_message.response_list_of_raw_bytes)}")
|
|
302
|
-
function_data_sent = None
|
|
303
|
-
|
|
304
|
-
# Iterate through the list of byte responses.
|
|
305
|
-
for response_raw_bytes in client_message.response_list_of_raw_bytes:
|
|
306
|
-
function_data_sent = sender.Sender(
|
|
307
|
-
ssl_socket=function_client_socket_object, class_message=response_raw_bytes,
|
|
308
|
-
logger=network_logger).send()
|
|
309
|
-
|
|
310
|
-
# If there was problem with sending data, we'll break current loop.
|
|
311
|
-
if not function_data_sent:
|
|
312
|
-
break
|
|
313
|
-
# If there is no response, close the socket.
|
|
314
|
-
else:
|
|
315
|
-
function_data_sent = None
|
|
316
|
-
network_logger.info(f"Response empty, nothing to send to client.")
|
|
317
|
-
except Exception:
|
|
318
|
-
message = "Not sending anything to the client, since there is no response available"
|
|
319
|
-
print_api(
|
|
320
|
-
message, error_type=True, logger=network_logger, logger_method='critical',
|
|
321
|
-
traceback_string=True)
|
|
322
|
-
# Pass the exception
|
|
323
|
-
pass
|
|
324
|
-
# Break the while loop
|
|
325
|
-
break
|
|
278
|
+
# If there is a response, then send it.
|
|
279
|
+
if response_raw_bytes:
|
|
280
|
+
# Sending response/s to client no matter if in record mode or not.
|
|
281
|
+
network_logger.info(
|
|
282
|
+
f"Sending messages to client: {len(client_message.response_list_of_raw_bytes)}")
|
|
326
283
|
|
|
327
|
-
|
|
328
|
-
|
|
284
|
+
# Iterate through the list of byte responses.
|
|
285
|
+
for response_raw_bytes in client_message.response_list_of_raw_bytes:
|
|
286
|
+
error_on_send: str = sender.Sender(
|
|
287
|
+
ssl_socket=client_socket, class_message=response_raw_bytes,
|
|
288
|
+
logger=network_logger).send()
|
|
289
|
+
|
|
290
|
+
# If there was problem with sending data, we'll break current loop.
|
|
291
|
+
if error_on_send:
|
|
292
|
+
statistics_error_list.append(error_on_send)
|
|
293
|
+
break
|
|
294
|
+
# If response from server came back empty, then the server has closed the connection,
|
|
295
|
+
# we will do the same.
|
|
296
|
+
else:
|
|
297
|
+
network_logger.info(f"Response empty, nothing to send to client.")
|
|
329
298
|
break
|
|
299
|
+
|
|
300
|
+
record_and_statistics_write()
|
|
301
|
+
recorded = True
|
|
330
302
|
else:
|
|
331
|
-
#
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
303
|
+
# If it's the first cycle we will record the message from the client if it came empty.
|
|
304
|
+
if cycle_count == 0:
|
|
305
|
+
record_and_statistics_write()
|
|
306
|
+
|
|
307
|
+
# In other cases, we'll just break the loop, since empty message means that the other side closed the
|
|
308
|
+
# connection.
|
|
309
|
+
recorded = True
|
|
310
|
+
break
|
|
311
|
+
|
|
312
|
+
finish_thread()
|
|
313
|
+
except Exception as e:
|
|
314
|
+
exception_message = tracebacks.get_as_string(one_line=True)
|
|
315
|
+
error_message = f'Socket Thread [{str(thread_id)}] Exception: {exception_message}'
|
|
316
|
+
print_api(error_message, logger_method='critical', logger=network_logger)
|
|
317
|
+
statistics_error_list.append(error_message)
|
|
335
318
|
|
|
336
319
|
# === At this point while loop of 'client_connection_boolean' was broken =======================================
|
|
337
320
|
# If recorder wasn't executed before, then execute it now
|
|
338
|
-
if not
|
|
339
|
-
|
|
340
|
-
try:
|
|
341
|
-
recorded_file = recorder(
|
|
342
|
-
class_client_message=client_message, record_path=config_static.LogRec.recordings_path).record()
|
|
343
|
-
client_message.recorded_file_path = recorded_file
|
|
344
|
-
except Exception:
|
|
345
|
-
message = "Exception in Recorder"
|
|
346
|
-
print_api(
|
|
347
|
-
message, error_type=True, logger=recorder.logger, logger_method='critical',
|
|
348
|
-
traceback_string=True)
|
|
349
|
-
print_api(
|
|
350
|
-
message, error_type=True, logger=network_logger, logger_method='critical',
|
|
351
|
-
traceback_string=True)
|
|
352
|
-
|
|
353
|
-
# Save statistics file.
|
|
354
|
-
output_statistics_csv_row()
|
|
321
|
+
if not recorded:
|
|
322
|
+
record_and_statistics_write()
|
|
355
323
|
|
|
356
|
-
|
|
357
|
-
# close this socket as well if it still opened.
|
|
358
|
-
if service_client:
|
|
359
|
-
if service_client.socket_instance:
|
|
360
|
-
service_client.close_socket()
|
|
324
|
+
finish_thread()
|
|
361
325
|
|
|
362
|
-
#
|
|
363
|
-
|
|
364
|
-
function_client_socket_object.close()
|
|
365
|
-
network_logger.info(f"Closed client socket [{client_message.client_ip}:{client_message.source_port}]...")
|
|
366
|
-
|
|
367
|
-
network_logger.info("Thread Finished. Will continue listening on the Main thread")
|
|
368
|
-
except Exception:
|
|
369
|
-
message = "Undocumented exception in thread worker"
|
|
370
|
-
print_api(
|
|
371
|
-
message, error_type=True, logger=network_logger, logger_method='critical', traceback_string=True)
|
|
326
|
+
# After the socket clean up, we will still raise the exception to the main thread.
|
|
327
|
+
raise e
|
|
@@ -12,7 +12,4 @@ class ParserParent:
|
|
|
12
12
|
# This is general parser, so we don't parse anything and 'request_body_parsed' gets empty byte string.
|
|
13
13
|
self.class_client_message.request_body_parsed = b''
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
self.logger.info(f"Parsed: {self.class_client_message.request_body_parsed[0: 100]}...")
|
|
17
|
-
except Exception:
|
|
18
|
-
pass
|
|
15
|
+
self.logger.info(f"Parsed: {self.class_client_message.request_body_parsed[0: 100]}...")
|
|
@@ -90,7 +90,7 @@ class RecorderParent:
|
|
|
90
90
|
record_message = get_json(self.class_client_message)
|
|
91
91
|
|
|
92
92
|
# Since we already dumped the object to dictionary string, we'll just save the object to regular file.
|
|
93
|
-
file_io.write_file(record_message, self.record_file_path, enable_long_file_path=True)
|
|
93
|
+
file_io.write_file(record_message, self.record_file_path, enable_long_file_path=True, **{'logger': self.logger})
|
|
94
94
|
|
|
95
95
|
self.logger.info(f"Recorded to file: {self.record_file_path}")
|
|
96
96
|
|
atomicshop/mitm/message.py
CHANGED
|
@@ -22,20 +22,3 @@ class ClientMessage:
|
|
|
22
22
|
self.error: str = str()
|
|
23
23
|
self.protocol: str = str()
|
|
24
24
|
self.recorded_file_path: str = str()
|
|
25
|
-
|
|
26
|
-
def reinitialize(self) -> None:
|
|
27
|
-
"""
|
|
28
|
-
'ClientMessage' is being reused, since connection is still established to the server and new requests
|
|
29
|
-
are being processed on the same socket, so we need to reinitialize variables that are being updated, like
|
|
30
|
-
lists and dictionaries. Added the rest pf the variables that are repopulated to be on the safe side.
|
|
31
|
-
:return:
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
self.request_raw_bytes = bytearray()
|
|
35
|
-
self.request_time_received = None
|
|
36
|
-
self.request_raw_decoded = None
|
|
37
|
-
self.request_body_parsed = None
|
|
38
|
-
self.response_list_of_raw_bytes = list()
|
|
39
|
-
self.request_raw_hex = None
|
|
40
|
-
self.response_list_of_raw_hex = list()
|
|
41
|
-
self.response_list_of_raw_decoded = list()
|