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.
- {firegex-3.5.2/firegex.egg-info → firegex-3.5.3}/PKG-INFO +1 -1
- firegex-3.5.3/firegex/__init__.py +69 -0
- {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/__init__.py +12 -3
- {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/models/__init__.py +7 -0
- {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/models/http.py +134 -8
- {firegex-3.5.2 → firegex-3.5.3/firegex.egg-info}/PKG-INFO +1 -1
- {firegex-3.5.2 → firegex-3.5.3}/setup.py +1 -1
- firegex-3.5.2/firegex/__init__.py +0 -5
- {firegex-3.5.2 → firegex-3.5.3}/MANIFEST.in +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/README.md +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/fgex +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/firegex/__main__.py +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/firegex/cli.py +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/internals/__init__.py +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/internals/data.py +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/internals/exceptions.py +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/internals/models.py +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/models/tcp.py +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/firegex/nfproxy/proxysim/__init__.py +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/firegex.egg-info/SOURCES.txt +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/firegex.egg-info/dependency_links.txt +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/firegex.egg-info/requires.txt +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/firegex.egg-info/top_level.txt +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/requirements.txt +0 -0
- {firegex-3.5.2 → firegex-3.5.3}/setup.cfg +0 -0
|
@@ -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
|
|
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
|
|
415
|
-
return self.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
@@ -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.
|
|
9
|
+
VERSION = "3.5.3"
|
|
10
10
|
|
|
11
11
|
setuptools.setup(
|
|
12
12
|
name="firegex",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|