firegex 3.2.0__tar.gz → 3.2.1__tar.gz

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.
Files changed (25) hide show
  1. {firegex-3.2.0/firegex.egg-info → firegex-3.2.1}/PKG-INFO +1 -1
  2. firegex-3.2.1/firegex/__init__.py +5 -0
  3. {firegex-3.2.0 → firegex-3.2.1}/firegex/nfproxy/internals/data.py +1 -0
  4. {firegex-3.2.0 → firegex-3.2.1}/firegex/nfproxy/models/http.py +86 -55
  5. {firegex-3.2.0 → firegex-3.2.1/firegex.egg-info}/PKG-INFO +1 -1
  6. {firegex-3.2.0 → firegex-3.2.1}/setup.py +1 -1
  7. firegex-3.2.0/firegex/__init__.py +0 -5
  8. {firegex-3.2.0 → firegex-3.2.1}/MANIFEST.in +0 -0
  9. {firegex-3.2.0 → firegex-3.2.1}/README.md +0 -0
  10. {firegex-3.2.0 → firegex-3.2.1}/fgex +0 -0
  11. {firegex-3.2.0 → firegex-3.2.1}/firegex/__main__.py +0 -0
  12. {firegex-3.2.0 → firegex-3.2.1}/firegex/cli.py +0 -0
  13. {firegex-3.2.0 → firegex-3.2.1}/firegex/nfproxy/__init__.py +0 -0
  14. {firegex-3.2.0 → firegex-3.2.1}/firegex/nfproxy/internals/__init__.py +0 -0
  15. {firegex-3.2.0 → firegex-3.2.1}/firegex/nfproxy/internals/exceptions.py +0 -0
  16. {firegex-3.2.0 → firegex-3.2.1}/firegex/nfproxy/internals/models.py +0 -0
  17. {firegex-3.2.0 → firegex-3.2.1}/firegex/nfproxy/models/__init__.py +0 -0
  18. {firegex-3.2.0 → firegex-3.2.1}/firegex/nfproxy/models/tcp.py +0 -0
  19. {firegex-3.2.0 → firegex-3.2.1}/firegex/nfproxy/proxysim/__init__.py +0 -0
  20. {firegex-3.2.0 → firegex-3.2.1}/firegex.egg-info/SOURCES.txt +0 -0
  21. {firegex-3.2.0 → firegex-3.2.1}/firegex.egg-info/dependency_links.txt +0 -0
  22. {firegex-3.2.0 → firegex-3.2.1}/firegex.egg-info/requires.txt +0 -0
  23. {firegex-3.2.0 → firegex-3.2.1}/firegex.egg-info/top_level.txt +0 -0
  24. {firegex-3.2.0 → firegex-3.2.1}/requirements.txt +0 -0
  25. {firegex-3.2.0 → firegex-3.2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: firegex
3
- Version: 3.2.0
3
+ Version: 3.2.1
4
4
  Summary: Firegex client
5
5
  Home-page: https://github.com/pwnzer0tt1/firegex
6
6
  Author: Pwnzer0tt1
@@ -0,0 +1,5 @@
1
+
2
+ __version__ = "3.2.1" if "{" not in "3.2.1" else "0.0.0"
3
+
4
+ #Exported functions
5
+ __all__ = []
@@ -106,6 +106,7 @@ class DataStreamCtx:
106
106
  self.__data = glob["__firegex_pyfilter_ctx"]
107
107
  self.filter_glob = glob
108
108
  self.current_pkt = RawPacket._fetch_packet(self) if init_pkt else None
109
+ self.call_mem = {} #A memory space valid only for the current packet handler
109
110
 
110
111
  @property
111
112
  def filter_call_info(self) -> list[FilterHandler]:
@@ -5,12 +5,12 @@ from firegex.nfproxy.internals.exceptions import StreamFullDrop, StreamFullRejec
5
5
  from firegex.nfproxy.internals.models import FullStreamAction, ExceptionAction
6
6
  from dataclasses import dataclass, field
7
7
  from collections import deque
8
- from typing import Type
9
8
  from zstd import ZSTD_uncompress
10
9
  import gzip
11
10
  import io
12
11
  import zlib
13
12
  import brotli
13
+ import traceback
14
14
  from websockets.frames import Frame
15
15
  from websockets.extensions.permessage_deflate import PerMessageDeflate
16
16
  from pyllhttp import PAUSED_H2_UPGRADE, PAUSED_UPGRADE
@@ -260,7 +260,9 @@ class InternalCallbackHandler():
260
260
  while True:
261
261
  try:
262
262
  new_frame, self.buffers._ws_packet_stream = self._parse_websocket_frame(self.buffers._ws_packet_stream)
263
- except Exception as e:
263
+ except Exception:
264
+ print("[WARNING] Websocket parsing failed, passing data to stream...", flush=True)
265
+ traceback.print_exc()
264
266
  self._ws_raised_error = True
265
267
  self.msg.stream += self.buffers._ws_packet_stream
266
268
  self.buffers._ws_packet_stream = b""
@@ -287,9 +289,11 @@ class InternalCallbackHandler():
287
289
  return ext_ws
288
290
 
289
291
  def _parse_websocket_frame(self, data: bytes) -> tuple[Frame|None, bytes]:
290
- # mask = is_input
291
292
  if self._ws_extentions is None:
292
- self._ws_extentions = self._parse_websocket_ext()
293
+ if self._is_input():
294
+ self._ws_extentions = [] # Fallback to no options
295
+ else:
296
+ self._ws_extentions = self._parse_websocket_ext() # Extentions used are choosen by the server response
293
297
  read_buffering = bytearray()
294
298
  def read_exact(n: int):
295
299
  nonlocal read_buffering
@@ -450,70 +454,89 @@ class InternalBasicHttpMetaClass:
450
454
  return self._message.lheaders.get(header.lower(), default)
451
455
 
452
456
  @staticmethod
453
- def _associated_parser_class() -> Type[InternalHttpRequest]|Type[InternalHttpResponse]:
457
+ def _before_fetch_callable_checks(internal_data: DataStreamCtx) -> bool:
454
458
  raise NotImplementedError()
455
459
 
456
460
  @staticmethod
457
- def _before_fetch_callable_checks(internal_data: DataStreamCtx):
458
- return True
461
+ def _parser_class() -> str:
462
+ raise NotImplementedError()
459
463
 
460
464
  @classmethod
461
465
  def _fetch_packet(cls, internal_data: DataStreamCtx):
462
466
  if internal_data.current_pkt is None or internal_data.current_pkt.is_tcp is False:
463
467
  raise NotReadyToRun()
464
468
 
465
- ParserType = cls._associated_parser_class()
469
+ ParserType = InternalHttpRequest if internal_data.current_pkt.is_input else InternalHttpResponse
470
+ parser_key = f"{cls._parser_class()}_{'in' if internal_data.current_pkt.is_input else 'out'}"
466
471
 
467
- parser = internal_data.data_handler_context.get(cls, None)
472
+ parser = internal_data.data_handler_context.get(parser_key, None)
468
473
  if parser is None or parser.raised_error:
469
474
  parser: InternalHttpRequest|InternalHttpResponse = ParserType()
470
- internal_data.data_handler_context[cls] = parser
475
+ internal_data.data_handler_context[parser_key] = parser
476
+
477
+ if not internal_data.call_mem.get(cls._parser_class(), False): #Need to parse HTTP
478
+ internal_data.call_mem[cls._parser_class()] = True
479
+
480
+ #Setting websocket options if needed to the client parser
481
+ if internal_data.current_pkt.is_input:
482
+ ext_opt = internal_data.data_handler_context.get(f"{cls._parser_class()}_ws_options_client")
483
+ if ext_opt is not None and parser._ws_extentions != ext_opt:
484
+ parser._ws_extentions = ext_opt
485
+
486
+ # Memory size managment
487
+ if parser.total_size+len(internal_data.current_pkt.data) > internal_data.stream_max_size:
488
+ match internal_data.full_stream_action:
489
+ case FullStreamAction.FLUSH:
490
+ # Deleting parser and re-creating it
491
+ parser.messages.clear()
492
+ parser.msg.total_size -= len(parser.msg.stream)
493
+ parser.msg.stream = b""
494
+ parser.msg.total_size -= len(parser.msg.body)
495
+ parser.msg.body = b""
496
+ print("[WARNING] Flushing stream", flush=True)
497
+ if parser.total_size+len(internal_data.current_pkt.data) > internal_data.stream_max_size:
498
+ parser.reset_data()
499
+ case FullStreamAction.REJECT:
500
+ raise StreamFullReject()
501
+ case FullStreamAction.DROP:
502
+ raise StreamFullDrop()
503
+ case FullStreamAction.ACCEPT:
504
+ raise NotReadyToRun()
471
505
 
506
+ internal_data.call_mem["headers_were_set"] = parser.msg.headers_complete #This information is usefull for building the real object
507
+
508
+ try:
509
+ parser.parse_data(internal_data.current_pkt.data)
510
+ except Exception as e:
511
+ traceback.print_exc()
512
+ match internal_data.invalid_encoding_action:
513
+ case ExceptionAction.REJECT:
514
+ raise RejectConnection()
515
+ case ExceptionAction.DROP:
516
+ raise DropPacket()
517
+ case ExceptionAction.NOACTION:
518
+ raise e
519
+ case ExceptionAction.ACCEPT:
520
+ raise NotReadyToRun()
521
+
522
+ if parser.should_upgrade and not internal_data.current_pkt.is_input:
523
+ #Creating ws_option for the client
524
+ if not internal_data.data_handler_context.get(f"{cls._parser_class()}_ws_options_client"):
525
+ ext = parser._parse_websocket_ext()
526
+ internal_data.data_handler_context[f"{cls._parser_class()}_ws_options_client"] = ext
527
+
528
+ #Once the parsers has been triggered, we can return the object if needed
472
529
  if not cls._before_fetch_callable_checks(internal_data):
473
530
  raise NotReadyToRun()
474
531
 
475
- # Memory size managment
476
- if parser.total_size+len(internal_data.current_pkt.data) > internal_data.stream_max_size:
477
- match internal_data.full_stream_action:
478
- case FullStreamAction.FLUSH:
479
- # Deleting parser and re-creating it
480
- parser.messages.clear()
481
- parser.msg.total_size -= len(parser.msg.stream)
482
- parser.msg.stream = b""
483
- parser.msg.total_size -= len(parser.msg.body)
484
- parser.msg.body = b""
485
- print("[WARNING] Flushing stream", flush=True)
486
- if parser.total_size+len(internal_data.current_pkt.data) > internal_data.stream_max_size:
487
- parser.reset_data()
488
- case FullStreamAction.REJECT:
489
- raise StreamFullReject()
490
- case FullStreamAction.DROP:
491
- raise StreamFullDrop()
492
- case FullStreamAction.ACCEPT:
493
- raise NotReadyToRun()
494
-
495
- headers_were_set = parser.msg.headers_complete
496
- try:
497
- parser.parse_data(internal_data.current_pkt.data)
498
- except Exception as e:
499
- match internal_data.invalid_encoding_action:
500
- case ExceptionAction.REJECT:
501
- raise RejectConnection()
502
- case ExceptionAction.DROP:
503
- raise DropPacket()
504
- case ExceptionAction.NOACTION:
505
- raise e
506
- case ExceptionAction.ACCEPT:
507
- raise NotReadyToRun()
508
-
509
532
  messages_tosend:list[InternalHTTPMessage] = []
510
533
  for i in range(len(parser.messages)):
511
534
  messages_tosend.append(parser.pop_message())
512
535
 
513
536
  if len(messages_tosend) > 0:
514
- headers_were_set = False # New messages completed so the current message headers were not set in this case
537
+ internal_data.call_mem["headers_were_set"] = False # New messages completed so the current message headers were not set in this case
515
538
 
516
- if not headers_were_set and parser.msg.headers_complete:
539
+ if not internal_data.call_mem["headers_were_set"] and parser.msg.headers_complete:
517
540
  messages_tosend.append(parser.msg) # Also the current message needs to be sent due to complete headers
518
541
 
519
542
  if parser._packet_to_stream():
@@ -533,10 +556,6 @@ class HttpRequest(InternalBasicHttpMetaClass):
533
556
  HTTP Request handler
534
557
  This data handler will be called twice, first with the headers complete, and second with the body complete
535
558
  """
536
-
537
- @staticmethod
538
- def _associated_parser_class() -> Type[InternalHttpRequest]:
539
- return InternalHttpRequest
540
559
 
541
560
  @staticmethod
542
561
  def _before_fetch_callable_checks(internal_data: DataStreamCtx):
@@ -546,6 +565,10 @@ class HttpRequest(InternalBasicHttpMetaClass):
546
565
  def method(self) -> bytes:
547
566
  """Method of the request"""
548
567
  return self._parser.msg.method
568
+
569
+ @staticmethod
570
+ def _parser_class() -> str:
571
+ return "full_http"
549
572
 
550
573
  def __repr__(self):
551
574
  return f"<HttpRequest method={self.method} url={self.url} headers={self.headers} body=[{0 if not self.body else len(self.body)} bytes] http_version={self.http_version} keep_alive={self.keep_alive} should_upgrade={self.should_upgrade} headers_complete={self.headers_complete} message_complete={self.message_complete} content_length={self.content_length} stream={self.stream} ws_stream={self.ws_stream}>"
@@ -556,10 +579,6 @@ class HttpResponse(InternalBasicHttpMetaClass):
556
579
  This data handler will be called twice, first with the headers complete, and second with the body complete
557
580
  """
558
581
 
559
- @staticmethod
560
- def _associated_parser_class() -> Type[InternalHttpResponse]:
561
- return InternalHttpResponse
562
-
563
582
  @staticmethod
564
583
  def _before_fetch_callable_checks(internal_data: DataStreamCtx):
565
584
  return not internal_data.current_pkt.is_input
@@ -568,6 +587,10 @@ class HttpResponse(InternalBasicHttpMetaClass):
568
587
  def status_code(self) -> int:
569
588
  """Status code of the response"""
570
589
  return self._parser.msg.status
590
+
591
+ @staticmethod
592
+ def _parser_class() -> str:
593
+ return "full_http"
571
594
 
572
595
  def __repr__(self):
573
596
  return f"<HttpResponse status_code={self.status_code} url={self.url} headers={self.headers} body=[{0 if not self.body else len(self.body)} bytes] http_version={self.http_version} keep_alive={self.keep_alive} should_upgrade={self.should_upgrade} headers_complete={self.headers_complete} message_complete={self.message_complete} content_length={self.content_length} stream={self.stream} ws_stream={self.ws_stream}>"
@@ -580,6 +603,10 @@ class HttpRequestHeader(HttpRequest):
580
603
 
581
604
  def _contructor_hook(self):
582
605
  self._parser.save_body = False
606
+
607
+ @staticmethod
608
+ def _parser_class() -> str:
609
+ return "header_http"
583
610
 
584
611
  class HttpResponseHeader(HttpResponse):
585
612
  """
@@ -588,4 +615,8 @@ class HttpResponseHeader(HttpResponse):
588
615
  """
589
616
 
590
617
  def _contructor_hook(self):
591
- self._parser.save_body = False
618
+ self._parser.save_body = False
619
+
620
+ @staticmethod
621
+ def _parser_class() -> str:
622
+ return "header_http"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: firegex
3
- Version: 3.2.0
3
+ Version: 3.2.1
4
4
  Summary: Firegex client
5
5
  Home-page: https://github.com/pwnzer0tt1/firegex
6
6
  Author: Pwnzer0tt1
@@ -6,7 +6,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
6
6
  with open('requirements.txt', 'r', encoding='utf-8') as f:
7
7
  required = [ele.strip() for ele in f.read().splitlines() if not ele.strip().startswith("#") and ele.strip() != ""]
8
8
 
9
- VERSION = "3.2.0"
9
+ VERSION = "3.2.1"
10
10
 
11
11
  setuptools.setup(
12
12
  name="firegex",
@@ -1,5 +0,0 @@
1
-
2
- __version__ = "3.2.0" if "{" not in "3.2.0" else "0.0.0"
3
-
4
- #Exported functions
5
- __all__ = []
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes