firegex 2.5.3__tar.gz → 3.1.0__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 (31) hide show
  1. firegex-3.1.0/PKG-INFO +168 -0
  2. firegex-3.1.0/README.md +140 -0
  3. {firegex-2.5.3 → firegex-3.1.0}/fgex +0 -1
  4. firegex-3.1.0/firegex/__init__.py +5 -0
  5. {firegex-2.5.3 → firegex-3.1.0}/firegex/__main__.py +0 -1
  6. firegex-3.1.0/firegex/cli.py +76 -0
  7. firegex-3.1.0/firegex/nfproxy/__init__.py +39 -0
  8. firegex-3.1.0/firegex/nfproxy/internals/__init__.py +169 -0
  9. firegex-3.1.0/firegex/nfproxy/internals/data.py +165 -0
  10. firegex-3.1.0/firegex/nfproxy/internals/exceptions.py +16 -0
  11. firegex-3.1.0/firegex/nfproxy/internals/models.py +49 -0
  12. firegex-3.1.0/firegex/nfproxy/models/__init__.py +31 -0
  13. firegex-3.1.0/firegex/nfproxy/models/http.py +422 -0
  14. firegex-3.1.0/firegex/nfproxy/models/tcp.py +87 -0
  15. firegex-3.1.0/firegex/nfproxy/proxysim/__init__.py +303 -0
  16. firegex-3.1.0/firegex.egg-info/PKG-INFO +168 -0
  17. firegex-3.1.0/firegex.egg-info/SOURCES.txt +22 -0
  18. firegex-3.1.0/firegex.egg-info/requires.txt +6 -0
  19. firegex-3.1.0/requirements.txt +6 -0
  20. {firegex-2.5.3 → firegex-3.1.0}/setup.py +1 -1
  21. firegex-2.5.3/PKG-INFO +0 -37
  22. firegex-2.5.3/README.md +0 -3
  23. firegex-2.5.3/firegex/__init__.py +0 -7
  24. firegex-2.5.3/firegex.egg-info/PKG-INFO +0 -37
  25. firegex-2.5.3/firegex.egg-info/SOURCES.txt +0 -12
  26. firegex-2.5.3/firegex.egg-info/requires.txt +0 -12
  27. firegex-2.5.3/requirements.txt +0 -14
  28. {firegex-2.5.3 → firegex-3.1.0}/MANIFEST.in +0 -0
  29. {firegex-2.5.3 → firegex-3.1.0}/firegex.egg-info/dependency_links.txt +0 -0
  30. {firegex-2.5.3 → firegex-3.1.0}/firegex.egg-info/top_level.txt +0 -0
  31. {firegex-2.5.3 → firegex-3.1.0}/setup.cfg +0 -0
firegex-3.1.0/PKG-INFO ADDED
@@ -0,0 +1,168 @@
1
+ Metadata-Version: 2.2
2
+ Name: firegex
3
+ Version: 3.1.0
4
+ Summary: Firegex client
5
+ Home-page: https://github.com/pwnzer0tt1/firegex
6
+ Author: Pwnzer0tt1
7
+ Author-email: pwnzer0tt1@poliba.it
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: typer==0.15.2
14
+ Requires-Dist: pydantic>=2
15
+ Requires-Dist: typing-extensions>=4.7.1
16
+ Requires-Dist: watchfiles
17
+ Requires-Dist: fgex
18
+ Requires-Dist: pyllhttp
19
+ Dynamic: author
20
+ Dynamic: author-email
21
+ Dynamic: classifier
22
+ Dynamic: description
23
+ Dynamic: description-content-type
24
+ Dynamic: home-page
25
+ Dynamic: requires-dist
26
+ Dynamic: requires-python
27
+ Dynamic: summary
28
+
29
+ # Firegex Python Library and CLI
30
+
31
+ This is the Python library for Firegex. It is used to get additional features of Firegex and use the feature of the command `fgex`.
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install -U firegex
37
+ ```
38
+
39
+ fgex is an alias of firegex. You can use `fgex` instead of `firegex`.
40
+
41
+ ## Command line usage:
42
+
43
+ ```text
44
+ ➤ fgex nfproxy -h
45
+
46
+ Usage: fgex nfproxy [OPTIONS] FILTER_FILE ADDRESS PORT
47
+
48
+ Run an nfproxy simulation
49
+
50
+ ╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
51
+ │ * filter_file TEXT The path to the filter file [default: None] [required] │
52
+ │ * address TEXT The address of the target to proxy [default: None] [required] │
53
+ │ * port INTEGER The port of the target to proxy [default: None] [required] │
54
+ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
55
+ ╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
56
+ │ --proto [tcp|http] The protocol to proxy [default: tcp] │
57
+ │ --from-address TEXT The address of the local server [default: None] │
58
+ │ --from-port INTEGER The port of the local server [default: 7474] │
59
+ │ -6 Use IPv6 for the connection │
60
+ │ --help -h Show this message and exit. │
61
+ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
62
+ ```
63
+
64
+ ## Library usage:
65
+
66
+ ## NfProxy decorator
67
+
68
+ ```python
69
+ from firegex.nfproxy import pyfilter
70
+ ```
71
+ This decorator is used to create a filter for the nfproxy.
72
+ Example:
73
+ ```python
74
+ @pyfilter
75
+ def my_filter(raw_packet: RawPacket): #Logging filter
76
+ print(raw_packet.data)
77
+ ```
78
+
79
+ ## Data handlers
80
+
81
+ ### RawPacket
82
+ ```python
83
+ from firegex.nfproxy import RawPacket
84
+ ```
85
+ This handler will be called every time arrives a packet from the network. It will receive a RawPacket object with the following properties:
86
+ - is_input: bool - It's true if the packet is an input packet, false if it's an output packet
87
+ - is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
88
+ - is_tcp: bool - It's true if the packet is a tcp packet, false if it's an udp packet
89
+ - data: bytes - The data of the packet assembled and sorted from TCP
90
+ - l4_size: int - The size of the layer 4 data
91
+ - raw_packet_header_len: int - The size of the original packet header
92
+ - l4_data: bytes - The layer 4 payload of the packet
93
+ - raw_packet: bytes - The raw packet with IP and TCP headers
94
+
95
+ ### TCPInputStream
96
+ Alias: TCPClientStream
97
+ ```python
98
+ from firegex.nfproxy import TCPInputStream
99
+ ```
100
+ This handler will be called every time a TCP stream is assembled in input. It will receive a TCPInputStream object with the following properties:
101
+ - data: bytes - The data of the packets assembled and sorted from TCP
102
+ - is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
103
+ - total_stream_size: int - The size of the stream
104
+
105
+ ### TCPOutputStream
106
+ Alias: TCPServerStream
107
+ ```python
108
+ from firegex.nfproxy import TCPOutputStream
109
+ ```
110
+ This handler will be called every time a TCP stream is assembled in output. It will receive a TCPOutputStream object with the following properties:
111
+ - data: bytes - The data of the packets assembled and sorted from TCP
112
+ - is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
113
+ - total_stream_size: int - The size of the stream
114
+
115
+ ### HttpRequest
116
+ ```python
117
+ from firegex.nfproxy import HttpRequest
118
+ ```
119
+ This handler will be called twice: one for the request headers and one for the request body. It will receive a HttpRequest object with the following properties:
120
+ - method: bytes - The method of the request
121
+ - url: str - The url of the request
122
+ - headers: dict - The headers of the request
123
+ - user_agent: str - The user agent of the request
124
+ - content_encoding: str - The content encoding of the request
125
+ - body: bytes - The body of the request
126
+ - headers_complete: bool - It's true if the headers are complete
127
+ - message_complete: bool - It's true if the message is complete
128
+ - http_version: str - The http version of the request
129
+ - keep_alive: bool - It's true if the request should keep alive
130
+ - should_upgrade: bool - It's true if the request should upgrade
131
+ - content_length: int - The content length of the request
132
+ - get_header(header: str, default=None): str - Get a header from the request without caring about the case
133
+ - total_size: int - The total size of the stream
134
+ - stream: bytes - The stream of the request
135
+
136
+ ### HttpRequestHeader
137
+ ```python
138
+ from firegex.nfproxy import HttpRequestHeader
139
+ ```
140
+ This handler will be called only when the request headers are complete. It will receive a HttpRequestHeader object with the same properties as HttpRequest.
141
+
142
+ ### HttpResponse
143
+ ```python
144
+ from firegex.nfproxy import HttpResponse
145
+ ```
146
+ This handler will be called twice: one for the response headers and one for the response body. It will receive a HttpResponse object with the following properties:
147
+ - status_code: int - The status code of the response
148
+ - url: str - The url of the response
149
+ - headers: dict - The headers of the response
150
+ - user_agent: str - The user agent of the response
151
+ - content_encoding: str - The content encoding of the response
152
+ - body: bytes - The body of the response
153
+ - headers_complete: bool - It's true if the headers are complete
154
+ - message_complete: bool - It's true if the message is complete
155
+ - http_version: str - The http version of the response
156
+ - keep_alive: bool - It's true if the response should keep alive
157
+ - should_upgrade: bool - It's true if the response should upgrade
158
+ - content_length: int - The content length of the response
159
+ - get_header(header: str, default=None): str - Get a header from the response without caring about the case
160
+ - total_size: int - The total size of the stream
161
+ - stream: bytes - The stream of the response
162
+
163
+ ### HttpResponseHeader
164
+ ```python
165
+ from firegex.nfproxy import HttpResponseHeader
166
+ ```
167
+ This handler will be called only when the response headers are complete. It will receive a HttpResponseHeader object with the same properties as HttpResponse.
168
+
@@ -0,0 +1,140 @@
1
+ # Firegex Python Library and CLI
2
+
3
+ This is the Python library for Firegex. It is used to get additional features of Firegex and use the feature of the command `fgex`.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install -U firegex
9
+ ```
10
+
11
+ fgex is an alias of firegex. You can use `fgex` instead of `firegex`.
12
+
13
+ ## Command line usage:
14
+
15
+ ```text
16
+ ➤ fgex nfproxy -h
17
+
18
+ Usage: fgex nfproxy [OPTIONS] FILTER_FILE ADDRESS PORT
19
+
20
+ Run an nfproxy simulation
21
+
22
+ ╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
23
+ │ * filter_file TEXT The path to the filter file [default: None] [required] │
24
+ │ * address TEXT The address of the target to proxy [default: None] [required] │
25
+ │ * port INTEGER The port of the target to proxy [default: None] [required] │
26
+ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
27
+ ╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
28
+ │ --proto [tcp|http] The protocol to proxy [default: tcp] │
29
+ │ --from-address TEXT The address of the local server [default: None] │
30
+ │ --from-port INTEGER The port of the local server [default: 7474] │
31
+ │ -6 Use IPv6 for the connection │
32
+ │ --help -h Show this message and exit. │
33
+ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
34
+ ```
35
+
36
+ ## Library usage:
37
+
38
+ ## NfProxy decorator
39
+
40
+ ```python
41
+ from firegex.nfproxy import pyfilter
42
+ ```
43
+ This decorator is used to create a filter for the nfproxy.
44
+ Example:
45
+ ```python
46
+ @pyfilter
47
+ def my_filter(raw_packet: RawPacket): #Logging filter
48
+ print(raw_packet.data)
49
+ ```
50
+
51
+ ## Data handlers
52
+
53
+ ### RawPacket
54
+ ```python
55
+ from firegex.nfproxy import RawPacket
56
+ ```
57
+ This handler will be called every time arrives a packet from the network. It will receive a RawPacket object with the following properties:
58
+ - is_input: bool - It's true if the packet is an input packet, false if it's an output packet
59
+ - is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
60
+ - is_tcp: bool - It's true if the packet is a tcp packet, false if it's an udp packet
61
+ - data: bytes - The data of the packet assembled and sorted from TCP
62
+ - l4_size: int - The size of the layer 4 data
63
+ - raw_packet_header_len: int - The size of the original packet header
64
+ - l4_data: bytes - The layer 4 payload of the packet
65
+ - raw_packet: bytes - The raw packet with IP and TCP headers
66
+
67
+ ### TCPInputStream
68
+ Alias: TCPClientStream
69
+ ```python
70
+ from firegex.nfproxy import TCPInputStream
71
+ ```
72
+ This handler will be called every time a TCP stream is assembled in input. It will receive a TCPInputStream object with the following properties:
73
+ - data: bytes - The data of the packets assembled and sorted from TCP
74
+ - is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
75
+ - total_stream_size: int - The size of the stream
76
+
77
+ ### TCPOutputStream
78
+ Alias: TCPServerStream
79
+ ```python
80
+ from firegex.nfproxy import TCPOutputStream
81
+ ```
82
+ This handler will be called every time a TCP stream is assembled in output. It will receive a TCPOutputStream object with the following properties:
83
+ - data: bytes - The data of the packets assembled and sorted from TCP
84
+ - is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
85
+ - total_stream_size: int - The size of the stream
86
+
87
+ ### HttpRequest
88
+ ```python
89
+ from firegex.nfproxy import HttpRequest
90
+ ```
91
+ This handler will be called twice: one for the request headers and one for the request body. It will receive a HttpRequest object with the following properties:
92
+ - method: bytes - The method of the request
93
+ - url: str - The url of the request
94
+ - headers: dict - The headers of the request
95
+ - user_agent: str - The user agent of the request
96
+ - content_encoding: str - The content encoding of the request
97
+ - body: bytes - The body of the request
98
+ - headers_complete: bool - It's true if the headers are complete
99
+ - message_complete: bool - It's true if the message is complete
100
+ - http_version: str - The http version of the request
101
+ - keep_alive: bool - It's true if the request should keep alive
102
+ - should_upgrade: bool - It's true if the request should upgrade
103
+ - content_length: int - The content length of the request
104
+ - get_header(header: str, default=None): str - Get a header from the request without caring about the case
105
+ - total_size: int - The total size of the stream
106
+ - stream: bytes - The stream of the request
107
+
108
+ ### HttpRequestHeader
109
+ ```python
110
+ from firegex.nfproxy import HttpRequestHeader
111
+ ```
112
+ This handler will be called only when the request headers are complete. It will receive a HttpRequestHeader object with the same properties as HttpRequest.
113
+
114
+ ### HttpResponse
115
+ ```python
116
+ from firegex.nfproxy import HttpResponse
117
+ ```
118
+ This handler will be called twice: one for the response headers and one for the response body. It will receive a HttpResponse object with the following properties:
119
+ - status_code: int - The status code of the response
120
+ - url: str - The url of the response
121
+ - headers: dict - The headers of the response
122
+ - user_agent: str - The user agent of the response
123
+ - content_encoding: str - The content encoding of the response
124
+ - body: bytes - The body of the response
125
+ - headers_complete: bool - It's true if the headers are complete
126
+ - message_complete: bool - It's true if the message is complete
127
+ - http_version: str - The http version of the response
128
+ - keep_alive: bool - It's true if the response should keep alive
129
+ - should_upgrade: bool - It's true if the response should upgrade
130
+ - content_length: int - The content length of the response
131
+ - get_header(header: str, default=None): str - Get a header from the response without caring about the case
132
+ - total_size: int - The total size of the stream
133
+ - stream: bytes - The stream of the response
134
+
135
+ ### HttpResponseHeader
136
+ ```python
137
+ from firegex.nfproxy import HttpResponseHeader
138
+ ```
139
+ This handler will be called only when the response headers are complete. It will receive a HttpResponseHeader object with the same properties as HttpResponse.
140
+
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
- # TODO implement cli start function
4
3
  from firegex.cli import run
5
4
 
6
5
  if __name__ == "__main__":
@@ -0,0 +1,5 @@
1
+
2
+ __version__ = "3.1.0" if "{" not in "3.1.0" else "0.0.0"
3
+
4
+ #Exported functions
5
+ __all__ = []
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
- # TODO implement cli start function
4
3
  from firegex.cli import run
5
4
 
6
5
  if __name__ == "__main__":
@@ -0,0 +1,76 @@
1
+
2
+ #!/usr/bin/env python3
3
+
4
+ import typer
5
+ from rich import print
6
+ from rich.markup import escape
7
+ from typer import Exit
8
+ from firegex import __version__
9
+ from firegex.nfproxy.proxysim import run_proxy_simulation
10
+ from firegex.nfproxy.models import Protocols
11
+ import os
12
+ import socket
13
+
14
+ app = typer.Typer(
15
+ no_args_is_help=True,
16
+ context_settings={"help_option_names": ["-h", "--help"]}
17
+ )
18
+
19
+ def close_cli(code:int=1):
20
+ raise Exit(code)
21
+
22
+ DEV_MODE = __version__ == "0.0.0"
23
+
24
+ def test_connection(host, port, use_ipv6=False):
25
+ family = socket.AF_INET6 if use_ipv6 else socket.AF_INET
26
+ sock = socket.socket(family, socket.SOCK_STREAM)
27
+
28
+ try:
29
+ sock.settimeout(3)
30
+ sock.connect((host, port))
31
+ return True
32
+ except Exception:
33
+ return False
34
+ finally:
35
+ sock.close()
36
+
37
+ @app.command(help="Run an nfproxy simulation")
38
+ def nfproxy(
39
+ filter_file: str = typer.Argument(..., help="The path to the filter file"),
40
+ address: str = typer.Argument(..., help="The address of the target to proxy"),
41
+ port: int = typer.Argument(..., help="The port of the target to proxy"),
42
+
43
+ proto: Protocols = typer.Option(Protocols.TCP.value, help="The protocol to proxy"),
44
+ from_address: str = typer.Option(None, help="The address of the local server"),
45
+ from_port: int = typer.Option(7474, help="The port of the local server"),
46
+ ipv6: bool = typer.Option(False, "-6", help="Use IPv6 for the connection"),
47
+ ):
48
+ if from_address is None:
49
+ from_address = "::1" if ipv6 else "127.0.0.1"
50
+ if not os.path.isfile(filter_file):
51
+ print(f"[bold red]'{escape(os.path.abspath(filter_file))}' not found[/]")
52
+ close_cli()
53
+ if not test_connection(address, port, ipv6):
54
+ print(f"[bold red]Can't connect to {escape(address)}:{port}[/]")
55
+ close_cli()
56
+ run_proxy_simulation(filter_file, proto.value, address, port, from_address, from_port, ipv6)
57
+
58
+ def version_callback(verison: bool):
59
+ if verison:
60
+ print(__version__, "Development Mode" if DEV_MODE else "Release")
61
+ raise typer.Exit()
62
+
63
+ @app.callback()
64
+ def main(
65
+ verison: bool = typer.Option(False, "--version", "-v", help="Show the version of the client", callback=version_callback),
66
+ ):
67
+ pass
68
+
69
+ def run():
70
+ try:
71
+ app()
72
+ except KeyboardInterrupt:
73
+ print("[bold yellow]Operation cancelled[/]")
74
+
75
+ if __name__ == "__main__":
76
+ run()
@@ -0,0 +1,39 @@
1
+ import functools
2
+ from firegex.nfproxy.models import RawPacket, TCPInputStream, TCPOutputStream, TCPClientStream, TCPServerStream
3
+ from firegex.nfproxy.internals.models import Action, FullStreamAction
4
+
5
+ ACCEPT = Action.ACCEPT
6
+ DROP = Action.DROP
7
+ REJECT = Action.REJECT
8
+ UNSTABLE_MANGLE = Action.MANGLE
9
+
10
+ def pyfilter(func):
11
+ """
12
+ Decorator to mark functions that will be used in the proxy.
13
+ Stores the function reference in a global registry.
14
+ """
15
+ if not hasattr(pyfilter, "registry"):
16
+ pyfilter.registry = set()
17
+
18
+ pyfilter.registry.add(func.__name__)
19
+
20
+ @functools.wraps(func)
21
+ def wrapper(*args, **kwargs):
22
+ return func(*args, **kwargs)
23
+
24
+ return wrapper
25
+
26
+ def get_pyfilters():
27
+ """Returns the list of functions marked with @pyfilter."""
28
+ return list(pyfilter.registry)
29
+
30
+ def clear_pyfilter_registry():
31
+ """Clears the pyfilter registry."""
32
+ if hasattr(pyfilter, "registry"):
33
+ pyfilter.registry.clear()
34
+
35
+ __all__ = [
36
+ "ACCEPT", "DROP", "REJECT", "UNSTABLE_MANGLE"
37
+ "Action", "FullStreamAction", "pyfilter",
38
+ "RawPacket", "TCPInputStream", "TCPOutputStream", "TCPClientStream", "TCPServerStream"
39
+ ]
@@ -0,0 +1,169 @@
1
+ from inspect import signature
2
+ from firegex.nfproxy.internals.models import Action, FullStreamAction
3
+ from firegex.nfproxy.internals.models import FilterHandler, PacketHandlerResult
4
+ import functools
5
+ from firegex.nfproxy.internals.data import DataStreamCtx
6
+ from firegex.nfproxy.internals.exceptions import NotReadyToRun, StreamFullReject, DropPacket, RejectConnection, StreamFullDrop
7
+ from firegex.nfproxy.internals.data import RawPacket
8
+
9
+ def context_call(glob, func, *args, **kargs):
10
+ glob["__firegex_tmp_args"] = args
11
+ glob["__firegex_tmp_kargs"] = kargs
12
+ glob["__firege_tmp_call"] = func
13
+ res = eval("__firege_tmp_call(*__firegex_tmp_args, **__firegex_tmp_kargs)", glob, glob)
14
+ if "__firegex_tmp_args" in glob.keys():
15
+ del glob["__firegex_tmp_args"]
16
+ if "__firegex_tmp_kargs" in glob.keys():
17
+ del glob["__firegex_tmp_kargs"]
18
+ if "__firege_tmp_call" in glob.keys():
19
+ del glob["__firege_tmp_call"]
20
+ return res
21
+
22
+ def generate_filter_structure(filters: list[str], proto:str, glob:dict) -> list[FilterHandler]:
23
+ from firegex.nfproxy.models import type_annotations_associations
24
+ if proto not in type_annotations_associations.keys():
25
+ raise Exception("Invalid protocol")
26
+ res = []
27
+ valid_annotation_type = type_annotations_associations[proto]
28
+ def add_func_to_list(func):
29
+ if not callable(func):
30
+ raise Exception(f"{func} is not a function")
31
+ sig = signature(func)
32
+ params_function = {}
33
+
34
+ for k, v in sig.parameters.items():
35
+ if v.annotation in valid_annotation_type.keys():
36
+ params_function[v.annotation] = valid_annotation_type[v.annotation]
37
+ else:
38
+ raise Exception(f"Invalid type annotation {v.annotation} for function {func.__name__}")
39
+
40
+ res.append(
41
+ FilterHandler(
42
+ func=func,
43
+ name=func.__name__,
44
+ params=params_function,
45
+ proto=proto
46
+ )
47
+ )
48
+
49
+ for filter in filters:
50
+ if not isinstance(filter, str):
51
+ raise Exception("Invalid filter list: must be a list of strings")
52
+ if filter in glob.keys():
53
+ add_func_to_list(glob[filter])
54
+ else:
55
+ raise Exception(f"Filter {filter} not found")
56
+ return res
57
+
58
+ def get_filters_info(code:str, proto:str) -> list[FilterHandler]:
59
+ glob = {}
60
+ exec("import firegex.nfproxy", glob, glob)
61
+ exec("firegex.nfproxy.clear_pyfilter_registry()", glob, glob)
62
+ exec(code, glob, glob)
63
+ filters = eval("firegex.nfproxy.get_pyfilters()", glob, glob)
64
+ try:
65
+ return generate_filter_structure(filters, proto, glob)
66
+ finally:
67
+ exec("firegex.nfproxy.clear_pyfilter_registry()", glob, glob)
68
+
69
+
70
+ def get_filter_names(code:str, proto:str) -> list[str]:
71
+ return [ele.name for ele in get_filters_info(code, proto)]
72
+
73
+ def handle_packet(glob: dict) -> None:
74
+ internal_data = DataStreamCtx(glob)
75
+
76
+ cache_call = {} # Cache of the data handler calls
77
+ cache_call[RawPacket] = internal_data.current_pkt
78
+
79
+ result = PacketHandlerResult(glob)
80
+
81
+ for filter in internal_data.filter_call_info:
82
+ final_params = []
83
+ skip_call = False
84
+ for data_type, data_func in filter.params.items():
85
+ if data_type not in cache_call.keys():
86
+ try:
87
+ cache_call[data_type] = data_func(internal_data)
88
+ except NotReadyToRun:
89
+ cache_call[data_type] = None
90
+ skip_call = True
91
+ break
92
+ except StreamFullDrop:
93
+ result.action = Action.DROP
94
+ result.matched_by = "@MAX_STREAM_SIZE_REACHED"
95
+ return result.set_result()
96
+ except StreamFullReject:
97
+ result.action = Action.REJECT
98
+ result.matched_by = "@MAX_STREAM_SIZE_REACHED"
99
+ return result.set_result()
100
+ except DropPacket:
101
+ result.action = Action.DROP
102
+ result.matched_by = filter.name
103
+ return result.set_result()
104
+ except RejectConnection:
105
+ result.action = Action.REJECT
106
+ result.matched_by = filter.name
107
+ return result.set_result()
108
+ if cache_call[data_type] is None:
109
+ skip_call = True
110
+ break
111
+ final_params.append(cache_call[data_type])
112
+
113
+ if skip_call:
114
+ continue
115
+
116
+ # Create an iterator with all the calls to be done
117
+ def try_to_call(params:list):
118
+ is_base_call = True
119
+ for i in range(len(params)):
120
+ if isinstance(params[i], list):
121
+ new_params = params.copy()
122
+ for ele in params[i]:
123
+ new_params[i] = ele
124
+ for ele in try_to_call(new_params):
125
+ yield ele
126
+ is_base_call = False
127
+ break
128
+ if is_base_call:
129
+ yield context_call(glob, filter.func, *params)
130
+
131
+ for res in try_to_call(final_params):
132
+ if res is None:
133
+ continue #ACCEPTED
134
+ if not isinstance(res, Action):
135
+ raise Exception(f"Invalid return type {type(res)} for function {filter.name}")
136
+ if res == Action.MANGLE:
137
+ result.matched_by = filter.name
138
+ result.mangled_packet = internal_data.current_pkt.raw_packet
139
+ result.action = Action.MANGLE
140
+ elif res != Action.ACCEPT:
141
+ result.matched_by = filter.name
142
+ result.action = res
143
+ result.mangled_packet = None
144
+ return result.set_result()
145
+
146
+ return result.set_result() # Will be MANGLE or ACCEPT
147
+
148
+
149
+ def compile(glob:dict) -> None:
150
+ internal_data = DataStreamCtx(glob, init_pkt=False)
151
+
152
+ glob["print"] = functools.partial(print, flush = True)
153
+
154
+ filters = glob["__firegex_pyfilter_enabled"]
155
+ proto = glob["__firegex_proto"]
156
+
157
+ internal_data.filter_call_info = generate_filter_structure(filters, proto, glob)
158
+
159
+ if "FGEX_STREAM_MAX_SIZE" in glob and int(glob["FGEX_STREAM_MAX_SIZE"]) > 0:
160
+ internal_data.stream_max_size = int(glob["FGEX_STREAM_MAX_SIZE"])
161
+
162
+ if "FGEX_FULL_STREAM_ACTION" in glob and isinstance(glob["FGEX_FULL_STREAM_ACTION"], FullStreamAction):
163
+ internal_data.full_stream_action = glob["FGEX_FULL_STREAM_ACTION"]
164
+
165
+ if "FGEX_INVALID_ENCODING_ACTION" in glob and isinstance(glob["FGEX_INVALID_ENCODING_ACTION"], Action):
166
+ internal_data.invalid_encoding_action = glob["FGEX_INVALID_ENCODING_ACTION"]
167
+
168
+ PacketHandlerResult(glob).reset_result()
169
+