1 module socks5d.client;
2 
3 import socks5d.packets;
4 import socks5d.driver;
5 import socks5d.factory : f, logger, ConnectionImpl;
6 import socks5d.server;
7 import socks5d.auth;
8 
9 class Client
10 {
11     package:
12         uint         id;
13 
14     protected:
15         Connection   conn;
16         AuthManager authManager;
17 
18     public:
19         this(Connection conn, uint id, AuthManager authManager)
20         {
21             assert(authManager !is null);
22 
23             this.id = id;
24             this.authManager = authManager;
25             this.conn = conn;
26         }
27 
28         final void run()
29         {
30             logger.diagnostic("[%d] New client accepted: %s", id, conn.remoteAddress);
31             scope (exit) conn.close();
32 
33             try {
34                 if (authManager.authenticate(this)) {
35                     Connection targetConn = handshake();
36                     scope (exit) {
37                         targetConn.close();
38                     }
39 
40                     conn.duplexPipe(targetConn, id);
41                 }
42 
43             } catch (SocksException e) {
44                 logger.error("Error: %s", e.msg);
45             }
46 
47             logger.debugN("[%d] End of session", id);
48         }
49 
50     package:
51         void send(P)(ref P packet)
52         if (isSocks5OutgoingPacket!P)
53         {
54             logger.debugV("[%d] send: %s", id, packet.printFields);
55             packet.send(conn);
56         }
57 
58         void receive(P)(ref P packet)
59         if (isSocks5IncomingPacket!P)
60         {
61             packet.receive(conn);
62             logger.debugV("[%d] recv: %s", id, packet.printFields);
63         }
64 
65         Connection handshake()
66         {
67             import std.socket : InternetAddress;
68 
69             RequestPacket requestPacket = { connID: id };
70             ResponsePacket responsePacket = { connID: id };
71 
72             try {
73                 receive(requestPacket);
74             } catch (RequestException e) {
75                 logger.warning("[%d] Error: %s", id, e.msg);
76                 responsePacket.replyCode = e.replyCode;
77                 send(responsePacket);
78 
79                 throw e;
80             }
81 
82             logger.debugV("[%d] Connecting to %s:%d", id, requestPacket.getHost(), requestPacket.getPort());
83 
84             Connection targetConn = f.connection();
85             targetConn.connect(new InternetAddress(requestPacket.getHost(), requestPacket.getPort()));
86 
87             responsePacket.addressType = AddressType.IPV4;
88             responsePacket.setBindAddress(
89                 targetConn.localAddress.addr,
90                 targetConn.localAddress.port
91             );
92 
93             send(responsePacket);
94 
95             return targetConn;
96         }
97 }