Skip to content

Commit 08ca561

Browse files
Merge pull request #28 from Tecnativa/add-port-range
[IMP] Allow port ranges
2 parents 750e2c3 + 4fa93e8 commit 08ca561

File tree

3 files changed

+46
-10
lines changed

3 files changed

+46
-10
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,22 @@ Only used when [pre-resolving](#pre-resolve) is enabled.
120120
environment:
121121
PORT: "443"
122122
```
123+
- Multiple ports can be specified separated by spaces:
124+
125+
```yaml
126+
environment:
127+
PORT: "80 443 8080"
128+
```
129+
130+
- Port ranges are also supported using the `start-end` syntax:
131+
132+
```yaml
133+
environment:
134+
PORT: "21 50000-51000"
135+
```
136+
137+
This is especially useful for protocols like FTP in passive mode, where a fixed
138+
passive port range must be proxied in addition to the control port.
123139

124140
### `PRE_RESOLVE`
125141

proxy.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,32 @@
99

1010
logging.root.setLevel(logging.INFO)
1111
mode = os.environ["MODE"]
12-
ports = os.environ["PORT"].split()
1312
max_connections = os.environ.get("MAX_CONNECTIONS", 100)
1413
ip = target = os.environ["TARGET"]
1514
udp_answers = os.environ.get("UDP_ANSWERS", "1")
1615

16+
17+
def _expand_ports(port_tokens):
18+
for token in port_tokens:
19+
token = token.strip()
20+
if not token:
21+
continue
22+
if "-" in token:
23+
start, end = token.split("-", 1)
24+
start = int(start)
25+
end = int(end)
26+
if end < start:
27+
raise ValueError(f"Invalid port range: {token}")
28+
for p in range(start, end + 1):
29+
yield str(p)
30+
else:
31+
yield token
32+
33+
34+
ports = list(_expand_ports(os.environ["PORT"].split()))
35+
1736
# Resolve target if required
18-
if os.environ["PRE_RESOLVE"] == "1":
37+
if os.environ.get("PRE_RESOLVE", "0") == "1":
1938
resolver = Resolver()
2039
resolver.nameservers = os.environ["NAMESERVERS"].split()
2140
ip = random.choice([answer.address for answer in resolver.resolve(target)])
@@ -41,10 +60,11 @@ async def netcat(port):
4160
await process.wait()
4261

4362

44-
# Wait until all proxies exited, if they ever do
45-
try:
46-
loop = asyncio.get_event_loop()
47-
loop.run_until_complete(asyncio.gather(*map(netcat, ports)))
48-
finally:
49-
loop.run_until_complete(loop.shutdown_asyncgens())
50-
loop.close()
63+
async def _main():
64+
# Create tasks within a running event loop (robust on Python 3.10+)
65+
tasks = [asyncio.create_task(netcat(port)) for port in ports]
66+
await asyncio.gather(*tasks)
67+
68+
69+
if __name__ == "__main__":
70+
asyncio.run(_main())

tests/healthcheck.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ services:
3535
interval: 1s
3636
timeout: 1s
3737
retries: 0
38-
start_period: 1s
38+
start_period: 5s
3939
restart: unless-stopped
4040

4141
proxy_without_preresolve:

0 commit comments

Comments
 (0)