File size: 10,090 Bytes
b86e5bd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1b9b049
b86e5bd
 
 
 
 
 
 
 
1b9b049
b86e5bd
1b9b049
b86e5bd
 
 
 
1b9b049
b86e5bd
 
 
1b9b049
b86e5bd
 
 
 
 
 
1b9b049
b86e5bd
 
1b9b049
b86e5bd
 
 
 
 
1b9b049
b86e5bd
c77de17
b86e5bd
 
 
 
 
 
1b9b049
b86e5bd
 
 
 
 
 
 
 
1b9b049
b86e5bd
 
 
1b9b049
b86e5bd
 
 
 
 
1b9b049
b86e5bd
 
 
 
1b9b049
b86e5bd
 
 
 
 
 
 
 
 
 
 
 
 
 
1b9b049
b86e5bd
1b9b049
b86e5bd
 
 
 
 
1b9b049
b86e5bd
 
 
 
 
1b9b049
b86e5bd
1b9b049
b86e5bd
 
 
 
 
 
 
 
 
 
1b9b049
b86e5bd
 
 
 
 
 
 
 
 
1b9b049
b86e5bd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1b9b049
b86e5bd
 
 
1b9b049
b86e5bd
 
 
1b9b049
b86e5bd
 
 
 
 
 
 
 
 
1b9b049
b86e5bd
 
 
 
 
 
 
 
1b9b049
 
b86e5bd
 
 
 
 
 
 
1b9b049
b86e5bd
 
 
 
 
 
 
 
1b9b049
b86e5bd
 
 
 
 
 
 
1b9b049
b86e5bd
 
1b9b049
b86e5bd
 
 
 
 
 
 
 
 
 
 
 
 
1b9b049
b86e5bd
 
1b9b049
b86e5bd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1b9b049
b86e5bd
 
 
1b9b049
b86e5bd
 
 
1b9b049
b86e5bd
 
 
1b9b049
b86e5bd
1b9b049
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
import socket
import threading
import time
import json
import logging
from datetime import datetime, timedelta
from typing import Dict, List, Optional
from dataclasses import dataclass
import struct

@dataclass
class ConnectionInfo:
    ip: str
    port: int
    start_time: datetime
    bytes_sent: int = 0
    bytes_received: int = 0
    is_active: bool = True

class SOCKS5ProxyServer:
    def __init__(self, host='0.0.0.0', port=1080):
        self.host = host
        self.port = port
        self.server_socket = None
        self.running = False
        self.connections: Dict[str, ConnectionInfo] = {}
        self.total_connections = 0
        self.start_time = None
        self._lock = threading.Lock()
        
    def start(self):
        """Start SOCKS5 proxy server"""
        try:
            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.server_socket.bind((self.host, self.port))
            self.server_socket.listen(50)
            self.running = True
            self.start_time = datetime.now()
            
            logging.info(f"SOCKS5 Proxy Server started at {self.host}:{self.port}")
            
            # Start thread for accepting connections
            threading.Thread(target=self._accept_connections, daemon=True).start()
            
            return True
        except Exception as e:
            logging.error(f"Failed to start proxy server: {e}")
            return False
    
    def stop(self):
        """Stop SOCKS5 proxy server"""
        self.running = False
        if self.server_socket:
            try:
                self.server_socket.close()
            except:
                pass
        logging.info("SOCKS5 Proxy Server stopped")
    
    def _accept_connections(self):
        """Accept new connections"""
        while self.running:
            try:
                client_socket, client_address = self.server_socket.accept()
                client_ip, client_port = client_address
                
                # Record connection
                with self._lock:
                    self.connections[f"{client_ip}:{client_port}"] = ConnectionInfo(
                        ip=client_ip,
                        port=client_port,
                        start_time=datetime.now()
                    )
                    self.total_connections += 1
                
                # Create thread for handling connection
                threading.Thread(
                    target=self._handle_client,
                    args=(client_socket, client_ip, client_port),
                    daemon=True
                ).start()
                
            except socket.error:
                if self.running:
                    logging.error("Error accepting connection")
                break
    
    def _handle_client(self, client_socket, client_ip, client_port):
        """Handle client connection"""
        try:
            # SOCKS5 handshake
            if not self._handle_socks5_handshake(client_socket):
                return
            
            # Receive request from client
            request = client_socket.recv(1024)
            if len(request) < 10:
                return
            
            # Parse SOCKS5 request
            if request[0] != 0x05:  # Version 5
                return
            
            cmd = request[1]  # Command
            addr_type = request[3]  # Address type
            
            if cmd == 0x01:  # CONNECT
                self._handle_connect(client_socket, request, client_ip, client_port)
            elif cmd == 0x02:  # BIND
                self._handle_bind(client_socket, client_ip, client_port)
            elif cmd == 0x03:  # UDP ASSOCIATE
                self._handle_udp_associate(client_socket, client_ip, client_port)
                
        except Exception as e:
            logging.error(f"Error handling client {client_ip}:{client_port}: {e}")
        finally:
            # Close connection
            try:
                client_socket.close()
            except:
                pass
            
            # Update status
            with self._lock:
                if f"{client_ip}:{client_port}" in self.connections:
                    self.connections[f"{client_ip}:{client_port}"].is_active = False
    
    def _handle_socks5_handshake(self, client_socket):
        """Handle SOCKS5 handshake"""
        try:
            # Receive client hello
            data = client_socket.recv(1024)
            if len(data) < 2:
                return False
            
            version = data[0]
            nmethods = data[1]
            
            if version != 0x05:  # Version 5
                return False
            
            # Respond with no authentication
            response = b'\x05\x00'
            client_socket.send(response)
            return True
            
        except Exception as e:
            logging.error(f"Handshake error: {e}")
            return False
    
    def _handle_connect(self, client_socket, request, client_ip, client_port):
        """Handle SOCKS5 CONNECT command"""
        try:
            addr_type = request[3]
            dest_addr = ""
            dest_port = 0
            
            if addr_type == 0x01:  # IPv4
                dest_ip = socket.inet_ntoa(request[4:8])
                dest_port = struct.unpack('!H', request[8:10])[0]
                dest_addr = dest_ip
            elif addr_type == 0x03:  # Domain name
                domain_length = request[4]
                dest_addr = request[5:5+domain_length].decode('utf-8')
                dest_port = struct.unpack('!H', request[5+domain_length:7+domain_length])[0]
            elif addr_type == 0x04:  # IPv6
                dest_ip = socket.inet_ntop(socket.AF_INET6, request[4:20])
                dest_port = struct.unpack('!H', request[20:22])[0]
                dest_addr = dest_ip
            
            # Connect to target
            target_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            target_socket.connect((dest_addr, dest_port))
            
            # Send success response
            reply = b'\x05\x00\x00\x01' + socket.inet_aton('0.0.0.0') + b'\x00\x00'
            client_socket.send(reply)
            
            # Start data relay
            threading.Thread(
                target=self._relay_data,
                args=(client_socket, target_socket, client_ip, client_port),
                daemon=True
            ).start()
            
            logging.info(f"CONNECT: {client_ip}:{client_port} -> {dest_addr}:{dest_port}")
            
        except Exception as e:
            # Send error response
            reply = b'\x05\x05\x00\x01\x00\x00\x00\x00\x00\x00'
            try:
                client_socket.send(reply)
            except:
                pass
            logging.error(f"CONNECT error: {e}")
    
    def _handle_bind(self, client_socket, client_ip, client_port):
        """Handle SOCKS5 BIND command"""
        # BIND command implementation (for incoming connections)
        reply = b'\x05\x07\x00\x01\x00\x00\x00\x00\x00\x00'  # Command not supported
        try:
            client_socket.send(reply)
        except:
            pass
    
    def _handle_udp_associate(self, client_socket, client_ip, client_port):
        """Handle SOCKS5 UDP ASSOCIATE command"""
        # UDP ASSOCIATE implementation
        reply = b'\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00'
        try:
            client_socket.send(reply)
        except:
            pass
    
    def _relay_data(self, client_socket, target_socket, client_ip, client_port):
        """Relay data between client and target"""
        try:
            while True:
                try:
                    data = client_socket.recv(4096)
                    if not data:
                        break
                    
                    # Forward to target
                    target_socket.send(data)
                    
                    # Update statistics
                    with self._lock:
                        if f"{client_ip}:{client_port}" in self.connections:
                            conn = self.connections[f"{client_ip}:{client_port}"]
                            conn.bytes_sent += len(data)
                    
                except socket.error:
                    break
                
                try:
                    data = target_socket.recv(4096)
                    if not data:
                        break
                    
                    # Forward back to client
                    client_socket.send(data)
                    
                    # Update statistics
                    with self._lock:
                        if f"{client_ip}:{client_port}" in self.connections:
                            conn = self.connections[f"{client_ip}:{client_port}"]
                            conn.bytes_received += len(data)
                    
                except socket.error:
                    break
                    
        except Exception as e:
            logging.error(f"Relay error: {e}")
        finally:
            try:
                client_socket.close()
                target_socket.close()
            except:
                pass

class ProxyManager:
    def __init__(self, host='0.0.0.0', port=1080):
        self.proxy_server = SOCKS5ProxyServer(host, port)
        self.host = host
        self.port = port
        
    def start_server(self):
        """Start proxy server"""
        return self.proxy_server.start()
    
    def stop_server(self):
        """Stop proxy server"""
        self.proxy_server.stop()
    
    def is_running(self):
        """Check if server is running"""
        return self.proxy_server.running
    
    def get_server_info(self):
        """Get server information"""
        info = {
            "server_status": "Running" if self.proxy_server.running else "Stopped",
            "host": self