firegex 3.5.2__tar.gz → 3.5.3__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.5.2/firegex.egg-info → firegex-3.5.3}/PKG-INFO +1 -1
  2. firegex-3.5.3/firegex/__init__.py +69 -0
  3. {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/__init__.py +12 -3
  4. {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/models/__init__.py +7 -0
  5. {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/models/http.py +134 -8
  6. {firegex-3.5.2 → firegex-3.5.3/firegex.egg-info}/PKG-INFO +1 -1
  7. {firegex-3.5.2 → firegex-3.5.3}/setup.py +1 -1
  8. firegex-3.5.2/firegex/__init__.py +0 -5
  9. {firegex-3.5.2 → firegex-3.5.3}/MANIFEST.in +0 -0
  10. {firegex-3.5.2 → firegex-3.5.3}/README.md +0 -0
  11. {firegex-3.5.2 → firegex-3.5.3}/fgex +0 -0
  12. {firegex-3.5.2 → firegex-3.5.3}/firegex/__main__.py +0 -0
  13. {firegex-3.5.2 → firegex-3.5.3}/firegex/cli.py +0 -0
  14. {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/internals/__init__.py +0 -0
  15. {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/internals/data.py +0 -0
  16. {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/internals/exceptions.py +0 -0
  17. {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/internals/models.py +0 -0
  18. {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/models/tcp.py +0 -0
  19. {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/proxysim/__init__.py +0 -0
  20. {firegex-3.5.2 → firegex-3.5.3}/firegex.egg-info/SOURCES.txt +0 -0
  21. {firegex-3.5.2 → firegex-3.5.3}/firegex.egg-info/dependency_links.txt +0 -0
  22. {firegex-3.5.2 → firegex-3.5.3}/firegex.egg-info/requires.txt +0 -0
  23. {firegex-3.5.2 → firegex-3.5.3}/firegex.egg-info/top_level.txt +0 -0
  24. {firegex-3.5.2 → firegex-3.5.3}/requirements.txt +0 -0
  25. {firegex-3.5.2 → firegex-3.5.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firegex
3
- Version: 3.5.2
3
+ Version: 3.5.3
4
4
  Summary: Firegex client
5
5
  Home-page: https://github.com/pwnzer0tt1/firegex
6
6
  Author: Pwnzer0tt1
@@ -0,0 +1,69 @@
1
+
2
+ def _get_version():
3
+ v = "3.5.3" if "{" not in "3.5.3" else "0.0.0"
4
+ if v == "0.0.0":
5
+ import os
6
+ env_v = os.getenv("FIREGEX_VERSION")
7
+ if env_v:
8
+ return env_v
9
+ try:
10
+ import re
11
+
12
+ def parse_version(ver):
13
+ ver = ver.lstrip("v")
14
+ parts = re.split(r'[^0-9]+', ver)
15
+ return tuple(int(p) if p.isdigit() else 0 for p in parts if p)
16
+
17
+ def find_git_dir(start_path):
18
+ current = start_path
19
+ while current and current != "/" and current != "":
20
+ gd = os.path.join(current, ".git")
21
+ if os.path.isdir(gd):
22
+ return gd
23
+ parent = os.path.dirname(current)
24
+ if parent == current:
25
+ break
26
+ current = parent
27
+ return None
28
+
29
+ git_dir = find_git_dir(os.path.abspath(os.path.dirname(__file__)))
30
+ if not git_dir:
31
+ return v
32
+
33
+ branch = None
34
+ with open(os.path.join(git_dir, "HEAD"), "r") as f:
35
+ head = f.read().strip()
36
+ if head.startswith("ref: refs/heads/"):
37
+ branch = head.split("ref: refs/heads/")[1]
38
+
39
+ if branch != "main":
40
+ return v
41
+
42
+ tags = []
43
+ try:
44
+ with open(os.path.join(git_dir, "packed-refs"), "r") as f:
45
+ for line in f:
46
+ if " refs/tags/" in line:
47
+ tags.append(line.strip().split(" refs/tags/")[1])
48
+ except Exception:
49
+ pass
50
+
51
+ tags_dir = os.path.join(git_dir, "refs", "tags")
52
+ try:
53
+ for root, _, files in os.walk(tags_dir):
54
+ for file in files:
55
+ tags.append(os.path.relpath(os.path.join(root, file), tags_dir).replace(os.path.sep, "/"))
56
+ except Exception:
57
+ pass
58
+
59
+ version_tags = [t for t in set(tags) if any(c.isdigit() for c in t)]
60
+ if version_tags:
61
+ return max(version_tags, key=parse_version)
62
+ except Exception:
63
+ pass
64
+ return v
65
+
66
+ __version__ = _get_version()
67
+
68
+ #Exported functions
69
+ __all__ = []
@@ -1,5 +1,13 @@
1
1
  import functools
2
- from firegex.nfproxy.models import RawPacket, TCPInputStream, TCPOutputStream, TCPClientStream, TCPServerStream
2
+ from firegex.nfproxy.models import (
3
+ RawPacket,
4
+ TCPInputStream,
5
+ TCPOutputStream,
6
+ TCPClientStream,
7
+ TCPServerStream,
8
+ HttpHistory,
9
+ HttpStreamHistory,
10
+ )
3
11
  from firegex.nfproxy.internals.models import Action, FullStreamAction
4
12
 
5
13
  ACCEPT = Action.ACCEPT
@@ -35,7 +43,8 @@ def clear_pyfilter_registry():
35
43
  pyfilter.registry.clear()
36
44
 
37
45
  __all__ = [
38
- "ACCEPT", "DROP", "REJECT", "UNSTABLE_MANGLE"
46
+ "ACCEPT", "DROP", "REJECT", "UNSTABLE_MANGLE",
39
47
  "Action", "FullStreamAction", "pyfilter",
40
- "RawPacket", "TCPInputStream", "TCPOutputStream", "TCPClientStream", "TCPServerStream"
48
+ "RawPacket", "TCPInputStream", "TCPOutputStream", "TCPClientStream", "TCPServerStream",
49
+ "HttpHistory", "HttpStreamHistory"
41
50
  ]
@@ -11,6 +11,8 @@ from firegex.nfproxy.models.http import (
11
11
  HttpResponseHeader,
12
12
  HttpFullRequest,
13
13
  HttpFullResponse,
14
+ HttpHistory,
15
+ HttpStreamHistory,
14
16
  )
15
17
  from firegex.nfproxy.internals.data import RawPacket
16
18
  from enum import Enum
@@ -31,6 +33,8 @@ type_annotations_associations = {
31
33
  HttpResponseHeader: HttpResponseHeader._fetch_packet,
32
34
  HttpFullRequest: HttpFullRequest._fetch_packet,
33
35
  HttpFullResponse: HttpFullResponse._fetch_packet,
36
+ HttpHistory: HttpHistory._fetch_packet,
37
+ HttpStreamHistory: HttpStreamHistory._fetch_packet,
34
38
  },
35
39
  }
36
40
 
@@ -52,5 +56,8 @@ __all__ = [
52
56
  "HttpResponseHeader",
53
57
  "HttpFullRequest",
54
58
  "HttpFullResponse",
59
+ "HttpHistory",
60
+ "HttpStreamHistory",
55
61
  "Protocols",
56
62
  ]
63
+
@@ -48,6 +48,7 @@ class InternalHTTPMessage:
48
48
  ws_stream: list[Frame] = field(default_factory=list) # Decoded websocket stream
49
49
  upgrading_to_h2: bool = field(default=False)
50
50
  upgrading_to_ws: bool = field(default=False)
51
+ added_to_history: bool = field(default=False)
51
52
 
52
53
 
53
54
  @dataclass
@@ -393,6 +394,61 @@ class InternalHttpResponse(InternalCallbackHandler, pyllhttp.Response):
393
394
  return False
394
395
 
395
396
 
397
+ class HttpHistory:
398
+ """
399
+ HTTP History handler for pyfilters.
400
+ Provides access to completed previous requests and responses in the current TCP stream.
401
+ """
402
+
403
+ def __init__(
404
+ self,
405
+ requests: list["HttpFullRequest"] | None = None,
406
+ responses: list["HttpFullResponse"] | None = None,
407
+ ):
408
+ self._requests = list(requests) if requests is not None else []
409
+ self._responses = list(responses) if responses is not None else []
410
+
411
+ @property
412
+ def requests(self) -> list["HttpFullRequest"]:
413
+ """List of previous completed HTTP requests"""
414
+ return self._requests.copy()
415
+
416
+ @property
417
+ def responses(self) -> list["HttpFullResponse"]:
418
+ """List of previous completed HTTP responses"""
419
+ return self._responses.copy()
420
+
421
+ @classmethod
422
+ def _fetch_packet(cls, internal_data: DataStreamCtx):
423
+ if (
424
+ internal_data.current_pkt is None
425
+ or internal_data.current_pkt.is_tcp is False
426
+ ):
427
+ raise NotReadyToRun()
428
+
429
+ for key in ("http_module", "http_full", "http_header"):
430
+ obj = internal_data.call_mem.get(f"_fetched_obj_{key}")
431
+ if obj is not None:
432
+ if isinstance(obj, list) and obj:
433
+ return [item.history for item in obj]
434
+ elif hasattr(obj, "history"):
435
+ return obj.history
436
+
437
+ req_history_deque = internal_data.data_handler_context.get(
438
+ "http_history_requests", deque()
439
+ )
440
+ resp_history_deque = internal_data.data_handler_context.get(
441
+ "http_history_responses", deque()
442
+ )
443
+ return HttpHistory(list(req_history_deque), list(resp_history_deque))
444
+
445
+ def __repr__(self):
446
+ return f"<HttpHistory requests={len(self._requests)} responses={len(self._responses)}>"
447
+
448
+
449
+ HttpStreamHistory = HttpHistory
450
+
451
+
396
452
  class InternalBasicHttpMetaClass:
397
453
  """Internal class to handle HTTP requests and responses"""
398
454
 
@@ -404,15 +460,23 @@ class InternalBasicHttpMetaClass:
404
460
  self._parser = parser
405
461
  self.raised_error = False
406
462
  self._message: InternalHTTPMessage | None = msg
463
+ self._history: HttpHistory | None = None
407
464
  self._contructor_hook()
408
465
 
409
466
  def _contructor_hook(self):
410
467
  pass
411
468
 
469
+ @property
470
+ def history(self) -> HttpHistory:
471
+ """HTTP History for the current stream connection"""
472
+ if self._history is None:
473
+ return HttpHistory([], [])
474
+ return self._history
475
+
412
476
  @property
413
477
  def total_size(self) -> int:
414
- """Total size of the stream"""
415
- return self._parser.total_size
478
+ """Total size of the message"""
479
+ return self._message.total_size
416
480
 
417
481
  @property
418
482
  def url(self) -> str | None:
@@ -462,7 +526,7 @@ class InternalBasicHttpMetaClass:
462
526
  @property
463
527
  def should_upgrade(self) -> bool:
464
528
  """If the message should upgrade"""
465
- return self._parser.should_upgrade
529
+ return self._message.should_upgrade
466
530
 
467
531
  @property
468
532
  def content_length(self) -> int | None:
@@ -493,6 +557,10 @@ class InternalBasicHttpMetaClass:
493
557
  """Get a header from the message without caring about the case"""
494
558
  return self._message.lheaders.get(header.lower(), default)
495
559
 
560
+ @classmethod
561
+ def _should_release_message_headers(cls) -> bool:
562
+ return True
563
+
496
564
  @staticmethod
497
565
  def _before_fetch_callable_checks(internal_data: DataStreamCtx) -> bool:
498
566
  raise NotImplementedError()
@@ -521,6 +589,9 @@ class InternalBasicHttpMetaClass:
521
589
  parser: InternalHttpRequest | InternalHttpResponse = ParserType()
522
590
  internal_data.data_handler_context[parser_key] = parser
523
591
 
592
+ parser.release_message_headers = cls._should_release_message_headers()
593
+
594
+
524
595
  if not internal_data.call_mem.get(
525
596
  cls._parser_class(), False
526
597
  ): # Need to parse HTTP
@@ -620,10 +691,55 @@ class InternalBasicHttpMetaClass:
620
691
 
621
692
  if messages_to_call == 0:
622
693
  raise NotReadyToRun()
623
- elif messages_to_call == 1:
624
- return cls(parser, messages_tosend[0])
625
694
 
626
- return [cls(parser, ele) for ele in messages_tosend]
695
+ raw_max = internal_data.filter_glob.get("FGEX_MAX_HISTORY_SIZE", 100)
696
+ try:
697
+ max_history = max(0, int(raw_max))
698
+ except (ValueError, TypeError):
699
+ max_history = 100
700
+
701
+ req_history_deque: deque = (
702
+ internal_data.data_handler_context.setdefault(
703
+ "http_history_requests", deque(maxlen=max_history)
704
+ )
705
+ )
706
+ if req_history_deque.maxlen != max_history:
707
+ req_history_deque = deque(req_history_deque, maxlen=max_history)
708
+ internal_data.data_handler_context["http_history_requests"] = req_history_deque
709
+
710
+ resp_history_deque: deque = (
711
+ internal_data.data_handler_context.setdefault(
712
+ "http_history_responses", deque(maxlen=max_history)
713
+ )
714
+ )
715
+ if resp_history_deque.maxlen != max_history:
716
+ resp_history_deque = deque(resp_history_deque, maxlen=max_history)
717
+ internal_data.data_handler_context["http_history_responses"] = resp_history_deque
718
+
719
+ built_instances = []
720
+ for msg in messages_tosend:
721
+ history_snapshot = HttpHistory(
722
+ list(req_history_deque), list(resp_history_deque)
723
+ )
724
+ instance = cls(parser, msg)
725
+ instance._history = history_snapshot
726
+ built_instances.append(instance)
727
+
728
+ if msg.message_complete and not msg.added_to_history:
729
+ msg.added_to_history = True
730
+ if internal_data.current_pkt.is_input:
731
+ req_history_deque.append(HttpFullRequest(parser, msg))
732
+ else:
733
+ resp_history_deque.append(HttpFullResponse(parser, msg))
734
+
735
+ if len(built_instances) == 1:
736
+ res = built_instances[0]
737
+ internal_data.call_mem[f"_fetched_obj_{cls._parser_class()}"] = res
738
+ return res
739
+
740
+ internal_data.call_mem[f"_fetched_obj_{cls._parser_class()}"] = built_instances
741
+ return built_instances
742
+
627
743
 
628
744
 
629
745
  class HttpRequest(InternalBasicHttpMetaClass):
@@ -639,7 +755,7 @@ class HttpRequest(InternalBasicHttpMetaClass):
639
755
  @property
640
756
  def method(self) -> bytes:
641
757
  """Method of the request"""
642
- return self._parser.msg.method
758
+ return self._message.method
643
759
 
644
760
  @staticmethod
645
761
  def _parser_class() -> str:
@@ -662,7 +778,8 @@ class HttpResponse(InternalBasicHttpMetaClass):
662
778
  @property
663
779
  def status_code(self) -> int:
664
780
  """Status code of the response"""
665
- return self._parser.msg.status
781
+ return self._message.status
782
+
666
783
 
667
784
  @staticmethod
668
785
  def _parser_class() -> str:
@@ -678,6 +795,10 @@ class HttpFullRequest(HttpRequest):
678
795
  This data handler will be called when the request data is complete
679
796
  """
680
797
 
798
+ @classmethod
799
+ def _should_release_message_headers(cls) -> bool:
800
+ return False
801
+
681
802
  def _contructor_hook(self):
682
803
  self._parser.release_message_headers = False
683
804
 
@@ -695,6 +816,10 @@ class HttpFullResponse(HttpResponse):
695
816
  This data handler will be called when the response data is complete
696
817
  """
697
818
 
819
+ @classmethod
820
+ def _should_release_message_headers(cls) -> bool:
821
+ return False
822
+
698
823
  def _contructor_hook(self):
699
824
  self._parser.release_message_headers = False
700
825
 
@@ -706,6 +831,7 @@ class HttpFullResponse(HttpResponse):
706
831
  return f"<HttpFullResponse 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}>"
707
832
 
708
833
 
834
+
709
835
  class HttpRequestHeader(HttpRequest):
710
836
  """
711
837
  HTTP Request Header handler
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firegex
3
- Version: 3.5.2
3
+ Version: 3.5.3
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.5.2"
9
+ VERSION = "3.5.3"
10
10
 
11
11
  setuptools.setup(
12
12
  name="firegex",
@@ -1,5 +0,0 @@
1
-
2
- __version__ = "3.5.2" if "{" not in "3.5.2" 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