mirror of
https://github.com/tcsenpai/oproxy.git
synced 2025-06-06 19:15:28 +00:00
first commit
This commit is contained in:
commit
65e6c7e0f8
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*.log
|
||||||
|
*.log.*
|
||||||
|
__pycache__
|
||||||
|
.env
|
102
README.md
Normal file
102
README.md
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
# OProxy
|
||||||
|
|
||||||
|
*High-performance, transparent proxy that supports both TCP and UDP protocols.*
|
||||||
|
|
||||||
|
A high-performance, transparent proxy that supports both TCP and UDP protocols.
|
||||||
|
|
||||||
|
**NOTE:** This proxy has been designed with local API proxy in mind. Specifically, I used it to forward Ollama API requests to the remote Ollama server for applications that try to connect to the local Ollama server on localhost.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Transparent TCP proxying
|
||||||
|
- HTTP/HTTPS proxying without decrypting the traffic
|
||||||
|
- Optional UDP support
|
||||||
|
- Detailed logging capabilities
|
||||||
|
- Configurable through environment variables
|
||||||
|
- Support for both file and stdout logging
|
||||||
|
- Data content logging (optional)
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Python 3.7+
|
||||||
|
- python-dotenv
|
||||||
|
- socket
|
||||||
|
- threading
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Clone the repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/tcsenpai/oproxy.git
|
||||||
|
|
||||||
|
cd oproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
3. Copy the example environment file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
4. Edit the .env file with your configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PROXY_PORT=11434
|
||||||
|
TARGET_HOST=127.0.0.1
|
||||||
|
TARGET_PORT=80
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Basic TCP proxy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python src/main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable logging to file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python src/main.py --log-file=proxy.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable data logging with debug level:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python src/main.py --log-file proxy.log --log-data --log-level DEBUG
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable UDP support:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python src/main.py --enable-udp
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Command Line Arguments
|
||||||
|
|
||||||
|
- `--log-file`: Path to the log file
|
||||||
|
- `--log-data`: Enable logging of data content
|
||||||
|
- `--log-level`: Set logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
||||||
|
- `--enable-udp`: Enable UDP proxy alongside TCP
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- TCP proxy runs on the port specified in .env
|
||||||
|
- UDP proxy (if enabled) runs on PROXY_PORT + 1
|
||||||
|
- Data logging should be used carefully as it may contain sensitive information
|
||||||
|
- UDP support is experimental and runs as a daemon thread
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License
|
3
env.example
Normal file
3
env.example
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
PROXY_PORT=11434
|
||||||
|
TARGET_HOST=localhost
|
||||||
|
TARGET_PORT=11434
|
174
main.py
Normal file
174
main.py
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
import socket
|
||||||
|
import threading
|
||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
import argparse
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
# Load environment variables
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
PROXY_HOST = '0.0.0.0' # Listen on all interfaces
|
||||||
|
PROXY_PORT = int(os.getenv('PROXY_PORT', 8080))
|
||||||
|
TARGET_HOST = os.getenv('TARGET_HOST', 'localhost')
|
||||||
|
TARGET_PORT = int(os.getenv('TARGET_PORT', 80))
|
||||||
|
|
||||||
|
def setup_logging(log_file=None, log_level=logging.INFO):
|
||||||
|
# Configure logging format
|
||||||
|
log_format = '%(asctime)s - %(levelname)s - %(message)s'
|
||||||
|
|
||||||
|
# Setup basic configuration
|
||||||
|
if log_file:
|
||||||
|
logging.basicConfig(
|
||||||
|
level=log_level,
|
||||||
|
format=log_format,
|
||||||
|
handlers=[
|
||||||
|
logging.FileHandler(log_file),
|
||||||
|
logging.StreamHandler() # This will also print to stdout
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logging.basicConfig(
|
||||||
|
level=log_level,
|
||||||
|
format=log_format
|
||||||
|
)
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(description='Transparent TCP/UDP Proxy with logging capabilities')
|
||||||
|
parser.add_argument('--log-file', type=str, help='Path to the log file')
|
||||||
|
parser.add_argument('--log-data', action='store_true', help='Enable logging of data content')
|
||||||
|
parser.add_argument('--log-level',
|
||||||
|
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
|
||||||
|
default='INFO',
|
||||||
|
help='Set the logging level')
|
||||||
|
parser.add_argument('--enable-udp', action='store_true', help='Enable UDP proxy alongside TCP')
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
def handle_tcp_client(client_socket, log_data=False):
|
||||||
|
client_address = client_socket.getpeername()
|
||||||
|
logging.info(f"New connection from {client_address[0]}:{client_address[1]}")
|
||||||
|
|
||||||
|
# Connect to target server
|
||||||
|
target_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
target_socket.connect((TARGET_HOST, TARGET_PORT))
|
||||||
|
logging.info(f"Connected to target {TARGET_HOST}:{TARGET_PORT}")
|
||||||
|
|
||||||
|
def forward(source, destination, direction):
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
data = source.recv(4096)
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
if log_data:
|
||||||
|
print("[INFO] Logging data is enabled")
|
||||||
|
src = source.getpeername()
|
||||||
|
dst = destination.getpeername()
|
||||||
|
timestamp = datetime.now().isoformat()
|
||||||
|
logging.debug(f"[{direction}] {src[0]}:{src[1]} -> {dst[0]}:{dst[1]}")
|
||||||
|
logging.debug(f"Data: {data[:1024]!r}...") # Log first 1KB of data
|
||||||
|
destination.send(data)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error in {direction}: {str(e)}")
|
||||||
|
finally:
|
||||||
|
source.close()
|
||||||
|
destination.close()
|
||||||
|
logging.info(f"Connection closed ({direction})")
|
||||||
|
|
||||||
|
# Create two threads for bidirectional communication
|
||||||
|
client_to_target = threading.Thread(
|
||||||
|
target=forward,
|
||||||
|
args=(client_socket, target_socket, "CLIENT->TARGET")
|
||||||
|
)
|
||||||
|
target_to_client = threading.Thread(
|
||||||
|
target=forward,
|
||||||
|
args=(target_socket, client_socket, "TARGET->CLIENT")
|
||||||
|
)
|
||||||
|
|
||||||
|
client_to_target.start()
|
||||||
|
target_to_client.start()
|
||||||
|
|
||||||
|
def handle_udp_proxy(log_data=False):
|
||||||
|
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
udp_socket.bind((PROXY_HOST, PROXY_PORT + 1)) # Use next port for UDP
|
||||||
|
|
||||||
|
clients = defaultdict(dict)
|
||||||
|
|
||||||
|
logging.info(f"UDP proxy listening on {PROXY_HOST}:{PROXY_PORT + 1}")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
data, client_addr = udp_socket.recvfrom(4096)
|
||||||
|
if log_data:
|
||||||
|
logging.debug(f"UDP: {client_addr} -> {TARGET_HOST}:{TARGET_PORT}")
|
||||||
|
logging.debug(f"Data: {data[:1024]!r}...")
|
||||||
|
|
||||||
|
# Forward to target
|
||||||
|
target_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
target_socket.sendto(data, (TARGET_HOST, TARGET_PORT))
|
||||||
|
|
||||||
|
# Store socket for this client
|
||||||
|
clients[client_addr]['socket'] = target_socket
|
||||||
|
clients[client_addr]['target'] = (TARGET_HOST, TARGET_PORT)
|
||||||
|
|
||||||
|
# Handle response in a separate thread to not block
|
||||||
|
def handle_response(client_addr, target_socket):
|
||||||
|
try:
|
||||||
|
response, _ = target_socket.recvfrom(4096)
|
||||||
|
udp_socket.sendto(response, client_addr)
|
||||||
|
if log_data:
|
||||||
|
logging.debug(f"UDP Response: {TARGET_HOST}:{TARGET_PORT} -> {client_addr}")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"UDP Response Error: {str(e)}")
|
||||||
|
finally:
|
||||||
|
target_socket.close()
|
||||||
|
|
||||||
|
threading.Thread(target=handle_response,
|
||||||
|
args=(client_addr, target_socket)).start()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"UDP Error: {str(e)}")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Parse command line arguments
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
# Setup logging
|
||||||
|
log_level = getattr(logging, args.log_level)
|
||||||
|
setup_logging(args.log_file, log_level)
|
||||||
|
|
||||||
|
# Start TCP proxy (main functionality)
|
||||||
|
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
tcp_server.bind((PROXY_HOST, PROXY_PORT))
|
||||||
|
tcp_server.listen(100)
|
||||||
|
|
||||||
|
logging.info(f"TCP proxy listening on {PROXY_HOST}:{PROXY_PORT}")
|
||||||
|
logging.info(f"Forwarding to {TARGET_HOST}:{TARGET_PORT}")
|
||||||
|
logging.info(f"Logging level: {args.log_level}")
|
||||||
|
if args.log_file:
|
||||||
|
logging.info(f"Logging to file: {args.log_file}")
|
||||||
|
if args.log_data:
|
||||||
|
logging.info("Data logging is enabled")
|
||||||
|
|
||||||
|
# Start UDP proxy if enabled
|
||||||
|
if args.enable_udp:
|
||||||
|
udp_thread = threading.Thread(target=handle_udp_proxy,
|
||||||
|
args=(args.log_data,),
|
||||||
|
daemon=True)
|
||||||
|
udp_thread.start()
|
||||||
|
logging.info("UDP proxy enabled")
|
||||||
|
|
||||||
|
# Main TCP loop
|
||||||
|
while True:
|
||||||
|
client_socket, addr = tcp_server.accept()
|
||||||
|
proxy_thread = threading.Thread(
|
||||||
|
target=handle_tcp_client,
|
||||||
|
args=(client_socket, args.log_data)
|
||||||
|
)
|
||||||
|
proxy_thread.start()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
python-dotenv
|
69
src/main.py
Normal file
69
src/main.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import socket
|
||||||
|
import threading
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from proxy.logger import setup_logging
|
||||||
|
from proxy.tcp_handler import TCPHandler
|
||||||
|
from proxy.udp_handler import UDPHandler
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(description='Transparent TCP/UDP Proxy with logging capabilities')
|
||||||
|
parser.add_argument('--log-file', type=str, help='Path to the log file')
|
||||||
|
parser.add_argument('--log-data', action='store_true', help='Enable logging of data content')
|
||||||
|
parser.add_argument('--full-debug', action='store_true', help='Enable full data logging (entire payload)')
|
||||||
|
parser.add_argument('--log-level',
|
||||||
|
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
|
||||||
|
default='INFO',
|
||||||
|
help='Set the logging level')
|
||||||
|
parser.add_argument('--enable-udp', action='store_true', help='Enable UDP proxy alongside TCP')
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Load configuration
|
||||||
|
load_dotenv()
|
||||||
|
PROXY_HOST = '0.0.0.0'
|
||||||
|
PROXY_PORT = int(os.getenv('PROXY_PORT', 8080))
|
||||||
|
TARGET_HOST = os.getenv('TARGET_HOST', 'localhost')
|
||||||
|
TARGET_PORT = int(os.getenv('TARGET_PORT', 80))
|
||||||
|
|
||||||
|
# Parse arguments and setup logging
|
||||||
|
args = parse_args()
|
||||||
|
setup_logging(args.log_file, getattr(logging, args.log_level))
|
||||||
|
|
||||||
|
# Initialize TCP handler
|
||||||
|
tcp_handler = TCPHandler(TARGET_HOST, TARGET_PORT)
|
||||||
|
|
||||||
|
# Setup TCP server
|
||||||
|
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
tcp_server.bind((PROXY_HOST, PROXY_PORT))
|
||||||
|
tcp_server.listen(100)
|
||||||
|
|
||||||
|
logging.info(f"TCP proxy listening on {PROXY_HOST}:{PROXY_PORT}")
|
||||||
|
logging.info(f"Forwarding to {TARGET_HOST}:{TARGET_PORT}")
|
||||||
|
|
||||||
|
# Start UDP handler if enabled
|
||||||
|
if args.enable_udp:
|
||||||
|
udp_handler = UDPHandler(PROXY_HOST, PROXY_PORT, TARGET_HOST, TARGET_PORT)
|
||||||
|
udp_thread = threading.Thread(
|
||||||
|
target=udp_handler.start,
|
||||||
|
args=(args.log_data,),
|
||||||
|
daemon=True
|
||||||
|
)
|
||||||
|
udp_thread.start()
|
||||||
|
logging.info("UDP proxy enabled")
|
||||||
|
|
||||||
|
# Main TCP loop
|
||||||
|
while True:
|
||||||
|
client_socket, addr = tcp_server.accept()
|
||||||
|
proxy_thread = threading.Thread(
|
||||||
|
target=tcp_handler.handle_client,
|
||||||
|
args=(client_socket, args.log_data, args.full_debug)
|
||||||
|
)
|
||||||
|
proxy_thread.start()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
20
src/proxy/logger.py
Normal file
20
src/proxy/logger.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import logging
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
def setup_logging(log_file: Optional[str] = None, log_level: int = logging.INFO) -> None:
|
||||||
|
log_format = '%(asctime)s - %(levelname)s - %(message)s'
|
||||||
|
|
||||||
|
if log_file:
|
||||||
|
logging.basicConfig(
|
||||||
|
level=log_level,
|
||||||
|
format=log_format,
|
||||||
|
handlers=[
|
||||||
|
logging.FileHandler(log_file),
|
||||||
|
logging.StreamHandler()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logging.basicConfig(
|
||||||
|
level=log_level,
|
||||||
|
format=log_format
|
||||||
|
)
|
104
src/proxy/tcp_handler.py
Normal file
104
src/proxy/tcp_handler.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import socket
|
||||||
|
import threading
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Tuple, Optional
|
||||||
|
|
||||||
|
class TCPHandler:
|
||||||
|
def __init__(self, target_host: str, target_port: int):
|
||||||
|
self.target_host = target_host
|
||||||
|
self.target_port = target_port
|
||||||
|
|
||||||
|
def log_data_content(self, data: bytes, src: tuple, dst: tuple, direction: str, full_debug: bool = False) -> None:
|
||||||
|
try:
|
||||||
|
# Always log basic info
|
||||||
|
logging.info(f"[{direction}] {src[0]}:{src[1]} -> {dst[0]}:{dst[1]} ({len(data)} bytes)")
|
||||||
|
|
||||||
|
# Attempt to decode the data
|
||||||
|
if all(32 <= byte <= 126 or byte in (9, 10, 13) for byte in data[:100]):
|
||||||
|
decoded = data.decode('utf-8', errors='replace')
|
||||||
|
if full_debug:
|
||||||
|
# Log the entire payload with clear separators
|
||||||
|
logging.debug("="*50)
|
||||||
|
logging.debug(f"FULL DATA [{direction}] START")
|
||||||
|
logging.debug("="*50)
|
||||||
|
logging.debug(decoded)
|
||||||
|
logging.debug("="*50)
|
||||||
|
logging.debug(f"FULL DATA [{direction}] END")
|
||||||
|
logging.debug("="*50)
|
||||||
|
else:
|
||||||
|
# Log just a preview
|
||||||
|
logging.debug(f"Data preview: {decoded[:200]}...")
|
||||||
|
else:
|
||||||
|
if full_debug:
|
||||||
|
# For binary data, log the full hex dump
|
||||||
|
logging.debug("="*50)
|
||||||
|
logging.debug(f"FULL BINARY DATA [{direction}] START")
|
||||||
|
logging.debug("="*50)
|
||||||
|
logging.debug(' '.join(f'{byte:02x}' for byte in data))
|
||||||
|
logging.debug("="*50)
|
||||||
|
logging.debug(f"FULL BINARY DATA [{direction}] END")
|
||||||
|
logging.debug("="*50)
|
||||||
|
else:
|
||||||
|
logging.debug(f"Binary data: {len(data)} bytes transferred")
|
||||||
|
except Exception as decode_err:
|
||||||
|
logging.debug(f"Could not decode data: {decode_err}")
|
||||||
|
|
||||||
|
def forward(self, source: socket.socket, destination: socket.socket,
|
||||||
|
direction: str, log_data: bool, full_debug: bool = False) -> None:
|
||||||
|
total_bytes = 0
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
data = source.recv(4096)
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
total_bytes += len(data)
|
||||||
|
destination.send(data)
|
||||||
|
|
||||||
|
if log_data:
|
||||||
|
src = source.getpeername()
|
||||||
|
dst = destination.getpeername()
|
||||||
|
self.log_data_content(data, src, dst, direction, full_debug)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error in {direction}: {str(e)}")
|
||||||
|
finally:
|
||||||
|
logging.info(f"Connection closed ({direction}). Total bytes transferred: {total_bytes}")
|
||||||
|
try:
|
||||||
|
source.close()
|
||||||
|
destination.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle_client(self, client_socket: socket.socket, log_data: bool, full_debug: bool = False) -> None:
|
||||||
|
try:
|
||||||
|
client_address = client_socket.getpeername()
|
||||||
|
logging.info(f"New connection from {client_address[0]}:{client_address[1]}")
|
||||||
|
|
||||||
|
target_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
target_socket.connect((self.target_host, self.target_port))
|
||||||
|
logging.info(f"Connected to target {self.target_host}:{self.target_port}")
|
||||||
|
|
||||||
|
client_to_target = threading.Thread(
|
||||||
|
target=self.forward,
|
||||||
|
args=(client_socket, target_socket, "CLIENT->TARGET", log_data, full_debug),
|
||||||
|
name="ClientToTarget"
|
||||||
|
)
|
||||||
|
target_to_client = threading.Thread(
|
||||||
|
target=self.forward,
|
||||||
|
args=(target_socket, client_socket, "TARGET->CLIENT", log_data, full_debug),
|
||||||
|
name="TargetToClient"
|
||||||
|
)
|
||||||
|
|
||||||
|
client_to_target.start()
|
||||||
|
target_to_client.start()
|
||||||
|
|
||||||
|
client_to_target.join()
|
||||||
|
target_to_client.join()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error in handle_client: {str(e)}")
|
||||||
|
try:
|
||||||
|
client_socket.close()
|
||||||
|
except:
|
||||||
|
pass
|
50
src/proxy/udp_handler.py
Normal file
50
src/proxy/udp_handler.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import socket
|
||||||
|
import threading
|
||||||
|
import logging
|
||||||
|
from collections import defaultdict
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
class UDPHandler:
|
||||||
|
def __init__(self, proxy_host: str, proxy_port: int,
|
||||||
|
target_host: str, target_port: int):
|
||||||
|
self.proxy_host = proxy_host
|
||||||
|
self.proxy_port = proxy_port + 1 # UDP uses next port
|
||||||
|
self.target_host = target_host
|
||||||
|
self.target_port = target_port
|
||||||
|
self.clients: Dict[Any, dict] = defaultdict(dict)
|
||||||
|
|
||||||
|
def handle_response(self, client_addr: tuple, target_socket: socket.socket,
|
||||||
|
udp_socket: socket.socket, log_data: bool) -> None:
|
||||||
|
try:
|
||||||
|
response, _ = target_socket.recvfrom(4096)
|
||||||
|
udp_socket.sendto(response, client_addr)
|
||||||
|
if log_data:
|
||||||
|
logging.debug(f"UDP Response: {self.target_host}:{self.target_port} -> {client_addr}")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"UDP Response Error: {str(e)}")
|
||||||
|
finally:
|
||||||
|
target_socket.close()
|
||||||
|
|
||||||
|
def start(self, log_data: bool) -> None:
|
||||||
|
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
udp_socket.bind((self.proxy_host, self.proxy_port))
|
||||||
|
|
||||||
|
logging.info(f"UDP proxy listening on {self.proxy_host}:{self.proxy_port}")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
data, client_addr = udp_socket.recvfrom(4096)
|
||||||
|
if log_data:
|
||||||
|
logging.debug(f"UDP: {client_addr} -> {self.target_host}:{self.target_port}")
|
||||||
|
logging.debug(f"Data: {data[:1024]!r}...")
|
||||||
|
|
||||||
|
target_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
target_socket.sendto(data, (self.target_host, self.target_port))
|
||||||
|
|
||||||
|
threading.Thread(
|
||||||
|
target=self.handle_response,
|
||||||
|
args=(client_addr, target_socket, udp_socket, log_data)
|
||||||
|
).start()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"UDP Error: {str(e)}")
|
Loading…
x
Reference in New Issue
Block a user