firegex 2.5.3__tar.gz → 3.0.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.0.0/PKG-INFO +170 -0
  2. firegex-3.0.0/README.md +142 -0
  3. {firegex-2.5.3 → firegex-3.0.0}/fgex +0 -1
  4. firegex-3.0.0/firegex/__init__.py +5 -0
  5. {firegex-2.5.3 → firegex-3.0.0}/firegex/__main__.py +0 -1
  6. firegex-3.0.0/firegex/cli.py +76 -0
  7. firegex-3.0.0/firegex/nfproxy/__init__.py +39 -0
  8. firegex-3.0.0/firegex/nfproxy/internals/__init__.py +160 -0
  9. firegex-3.0.0/firegex/nfproxy/internals/data.py +149 -0
  10. firegex-3.0.0/firegex/nfproxy/internals/exceptions.py +15 -0
  11. firegex-3.0.0/firegex/nfproxy/internals/models.py +44 -0
  12. firegex-3.0.0/firegex/nfproxy/models/__init__.py +31 -0
  13. firegex-3.0.0/firegex/nfproxy/models/http.py +357 -0
  14. firegex-3.0.0/firegex/nfproxy/models/tcp.py +87 -0
  15. firegex-3.0.0/firegex/nfproxy/proxysim/__init__.py +303 -0
  16. firegex-3.0.0/firegex.egg-info/PKG-INFO +170 -0
  17. firegex-3.0.0/firegex.egg-info/SOURCES.txt +22 -0
  18. firegex-3.0.0/firegex.egg-info/requires.txt +6 -0
  19. firegex-3.0.0/requirements.txt +6 -0
  20. {firegex-2.5.3 → firegex-3.0.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.0.0}/MANIFEST.in +0 -0
  29. {firegex-2.5.3 → firegex-3.0.0}/firegex.egg-info/dependency_links.txt +0 -0
  30. {firegex-2.5.3 → firegex-3.0.0}/firegex.egg-info/top_level.txt +0 -0
  31. {firegex-2.5.3 → firegex-3.0.0}/setup.cfg +0 -0
firegex-3.0.0/PKG-INFO ADDED
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.2
2
+ Name: firegex
3
+ Version: 3.0.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
+ - has_begun: bool - It's true if the request has begun
126
+ - body: bytes - The body of the request
127
+ - headers_complete: bool - It's true if the headers are complete
128
+ - message_complete: bool - It's true if the message is complete
129
+ - http_version: str - The http version of the request
130
+ - keep_alive: bool - It's true if the request should keep alive
131
+ - should_upgrade: bool - It's true if the request should upgrade
132
+ - content_length: int - The content length of the request
133
+ - get_header(header: str, default=None): str - Get a header from the request without caring about the case
134
+ - total_size: int - The total size of the stream
135
+ - stream: bytes - The stream of the request
136
+
137
+ ### HttpRequestHeader
138
+ ```python
139
+ from firegex.nfproxy import HttpRequestHeader
140
+ ```
141
+ This handler will be called only when the request headers are complete. It will receive a HttpRequestHeader object with the same properties as HttpRequest.
142
+
143
+ ### HttpResponse
144
+ ```python
145
+ from firegex.nfproxy import HttpResponse
146
+ ```
147
+ 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:
148
+ - status_code: int - The status code of the response
149
+ - url: str - The url of the response
150
+ - headers: dict - The headers of the response
151
+ - user_agent: str - The user agent of the response
152
+ - content_encoding: str - The content encoding of the response
153
+ - has_begun: bool - It's true if the response has begun
154
+ - body: bytes - The body of the response
155
+ - headers_complete: bool - It's true if the headers are complete
156
+ - message_complete: bool - It's true if the message is complete
157
+ - http_version: str - The http version of the response
158
+ - keep_alive: bool - It's true if the response should keep alive
159
+ - should_upgrade: bool - It's true if the response should upgrade
160
+ - content_length: int - The content length of the response
161
+ - get_header(header: str, default=None): str - Get a header from the response without caring about the case
162
+ - total_size: int - The total size of the stream
163
+ - stream: bytes - The stream of the response
164
+
165
+ ### HttpResponseHeader
166
+ ```python
167
+ from firegex.nfproxy import HttpResponseHeader
168
+ ```
169
+ This handler will be called only when the response headers are complete. It will receive a HttpResponseHeader object with the same properties as HttpResponse.
170
+
@@ -0,0 +1,142 @@
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
+ - has_begun: bool - It's true if the request has begun
98
+ - body: bytes - The body of the request
99
+ - headers_complete: bool - It's true if the headers are complete
100
+ - message_complete: bool - It's true if the message is complete
101
+ - http_version: str - The http version of the request
102
+ - keep_alive: bool - It's true if the request should keep alive
103
+ - should_upgrade: bool - It's true if the request should upgrade
104
+ - content_length: int - The content length of the request
105
+ - get_header(header: str, default=None): str - Get a header from the request without caring about the case
106
+ - total_size: int - The total size of the stream
107
+ - stream: bytes - The stream of the request
108
+
109
+ ### HttpRequestHeader
110
+ ```python
111
+ from firegex.nfproxy import HttpRequestHeader
112
+ ```
113
+ This handler will be called only when the request headers are complete. It will receive a HttpRequestHeader object with the same properties as HttpRequest.
114
+
115
+ ### HttpResponse
116
+ ```python
117
+ from firegex.nfproxy import HttpResponse
118
+ ```
119
+ 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:
120
+ - status_code: int - The status code of the response
121
+ - url: str - The url of the response
122
+ - headers: dict - The headers of the response
123
+ - user_agent: str - The user agent of the response
124
+ - content_encoding: str - The content encoding of the response
125
+ - has_begun: bool - It's true if the response has begun
126
+ - body: bytes - The body of the response
127
+ - headers_complete: bool - It's true if the headers are complete
128
+ - message_complete: bool - It's true if the message is complete
129
+ - http_version: str - The http version of the response
130
+ - keep_alive: bool - It's true if the response should keep alive
131
+ - should_upgrade: bool - It's true if the response should upgrade
132
+ - content_length: int - The content length of the response
133
+ - get_header(header: str, default=None): str - Get a header from the response without caring about the case
134
+ - total_size: int - The total size of the stream
135
+ - stream: bytes - The stream of the response
136
+
137
+ ### HttpResponseHeader
138
+ ```python
139
+ from firegex.nfproxy import HttpResponseHeader
140
+ ```
141
+ This handler will be called only when the response headers are complete. It will receive a HttpResponseHeader object with the same properties as HttpResponse.
142
+
@@ -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.0.0" if "{" not in "3.0.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,160 @@
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
+ final_result = Action.ACCEPT
80
+ result = PacketHandlerResult(glob)
81
+
82
+ func_name = None
83
+ mangled_packet = None
84
+ for filter in internal_data.filter_call_info:
85
+ final_params = []
86
+ skip_call = False
87
+ for data_type, data_func in filter.params.items():
88
+ if data_type not in cache_call.keys():
89
+ try:
90
+ cache_call[data_type] = data_func(internal_data)
91
+ except NotReadyToRun:
92
+ cache_call[data_type] = None
93
+ skip_call = True
94
+ break
95
+ except StreamFullDrop:
96
+ result.action = Action.DROP
97
+ result.matched_by = "@MAX_STREAM_SIZE_REACHED"
98
+ return result.set_result()
99
+ except StreamFullReject:
100
+ result.action = Action.REJECT
101
+ result.matched_by = "@MAX_STREAM_SIZE_REACHED"
102
+ return result.set_result()
103
+ except DropPacket:
104
+ result.action = Action.DROP
105
+ result.matched_by = filter.name
106
+ return result.set_result()
107
+ except RejectConnection:
108
+ result.action = Action.REJECT
109
+ result.matched_by = filter.name
110
+ return result.set_result()
111
+ if cache_call[data_type] is None:
112
+ skip_call = True
113
+ break
114
+ final_params.append(cache_call[data_type])
115
+
116
+ if skip_call:
117
+ continue
118
+
119
+ res = context_call(glob, filter.func, *final_params)
120
+
121
+ if res is None:
122
+ continue #ACCEPTED
123
+ if not isinstance(res, Action):
124
+ raise Exception(f"Invalid return type {type(res)} for function {filter.name}")
125
+ if res == Action.MANGLE:
126
+ mangled_packet = internal_data.current_pkt.raw_packet
127
+ if res != Action.ACCEPT:
128
+ func_name = filter.name
129
+ final_result = res
130
+ break
131
+
132
+ result.action = final_result
133
+ result.matched_by = func_name
134
+ result.mangled_packet = mangled_packet
135
+
136
+ return result.set_result()
137
+
138
+
139
+ def compile(glob:dict) -> None:
140
+ internal_data = DataStreamCtx(glob, init_pkt=False)
141
+
142
+ glob["print"] = functools.partial(print, flush = True)
143
+
144
+ filters = glob["__firegex_pyfilter_enabled"]
145
+ proto = glob["__firegex_proto"]
146
+
147
+ internal_data.filter_call_info = generate_filter_structure(filters, proto, glob)
148
+
149
+ if "FGEX_STREAM_MAX_SIZE" in glob and int(glob["FGEX_STREAM_MAX_SIZE"]) > 0:
150
+ internal_data.stream_max_size = int(glob["FGEX_STREAM_MAX_SIZE"])
151
+ else:
152
+ internal_data.stream_max_size = 1*8e20 # 1MB default value
153
+
154
+ if "FGEX_FULL_STREAM_ACTION" in glob and isinstance(glob["FGEX_FULL_STREAM_ACTION"], FullStreamAction):
155
+ internal_data.full_stream_action = glob["FGEX_FULL_STREAM_ACTION"]
156
+ else:
157
+ internal_data.full_stream_action = FullStreamAction.FLUSH
158
+
159
+ PacketHandlerResult(glob).reset_result()
160
+