0 /* See LICENSE file for copyright and license details. */

1 module client;

2 import std.datetime.systime : SysTime, Clock;

3 import std.algorithm.mutation;

4 import std.socket;

5 import core.stdc.string;

6 import logger;

7 import std.format;

8 import core.time;

9 import proxy;

10

11 /// Connection state of the client

12 enum State { CONNECTED, GREETED, AUTHENTICATED, ESTABLISHED, FAILED }

13

14 /// Converting nano seconds to mseconds

15 const long NANOTOMS = 1_000_000_000;

16

17 /// Client of the proxy server

18 class Client {

19

20 private:

21 State state;

22 long connectionTime;

23 Proxy server;

24 ubyte[1024] confirmData;

25 int confirmDataLen;

26 string rDomain;

27 int rPort;

28 public:

29 /// Socket to the client

30 Socket socket;

31 /// Socket to the remote server

32 Socket remote;

33

34 /// Assign sockets to the server

35 this(Socket socket, Proxy proxy) {

36 this.server=proxy;

37 this.socket=socket;

38 this.remote=null;

39 connectionTime=Clock.currStdTime()/NANOTOMS;

40 state = State.CONNECTED;

41 }

42

43 ~this() {

44 if(socket !is null) {

45 socket.close();

46 socket.shutdown(SocketShutdown.BOTH);

47 }

48 if (remote !is null && remote.isAlive) {

49 try

50 {

51 Logger.error(format("Connection from %s:%d closed.", rDomain,rPort));

52 }

53 catch (SocketException)

54 {

55 Logger.error(format("Connection closed."));

56 }

57 remote.shutdown(SocketShutdown.BOTH);

58 remote.close();

59 } else {

60 Logger.error(format("Connection closed. No connection"));

61 }

62 }

63

64 /// Return true if the remote connection is established

65 bool isEstablished() {

66 return state == State.ESTABLISHED;

67 }

68

69 /// Confirm to the client that the remote connectio was established

70 void confirm() {

71 if(state == State.GREETED) {

72 sendRes(0x00,confirmData,confirmDataLen);

73 Logger.info(format("Connected to "~rDomain~":%d",rPort));

74 state = State.ESTABLISHED;

75 }

76 }

77

78 /// Return true if the client is connected to the remote address

79 bool isConnecting() {

80 if(state == State.GREETED) {

81 if(remote is null || !remote.isAlive) {

82 Logger.error("Failed to connect to that");

83 sendRes(0x05);

84 state = State.FAILED;

85 }

86 }

87 return state == State.GREETED;

88 }

89

90 /// Return true if the client should disconnect

91 bool shouldDisconnect() {

92 switch(state) {

93 case State.FAILED:

94 return true;

95 case State.ESTABLISHED:

96 if(server.getTimeout(true)<0) return false;

97 return connectionTime+server.getTimeout(true)<Clock.currStdTime()/NANOTOMS;

98 default:

99 if(server.getTimeout(false)<0) return false;

100 return connectionTime+server.getTimeout(false)<Clock.currStdTime()/NANOTOMS;

101 }

102 }

103

104 /// Send Auth data to the client

105 void sendAuth(ubyte method) {

106 auto data = new char[2];

107 data[0]=0x05;

108 data[1]=method;

109 socket.send(data[0..2]);

110 }

111

112 /// Send packet to the client

113 void sendRes(ubyte status, ubyte[] data=null, int len=0) {

114 auto res = new char[3+len];

115 res[0]=0x05;

116 res[1]=status;

117 res[2]=0x00;

118 if(len!=0)

119 memcpy(cast(void*)&res[3],cast(void*)data,len);

120 socket.send(res[0..3+len]);

121 }

122

123 /// Return true if the address is martian

124 bool isMartian(uint addr) {

125 if((3232235520<=addr&&addr<=3232301055)||

126 (167772160<=addr&&addr<=184549375)||

127 (0<=addr&&addr<=16777215)||

128 (2851995648<=addr&&addr<=2852061183)||

129 (3221225472<=addr&&addr<=3221225727)||

130 (3221225984<=addr&&addr<=3221226239)||

131 (3325256704<=addr&&addr<=3325256959)||

132 (3405803776<=addr&&addr<=3405804031)||

133 (3323068416<=addr&&addr<=3323199487)||

134 (2886729728<=addr&&addr<=2887778303)||

135 (3758096384<=addr&&addr<=4294967295)

136 ) return true;

137 return false;

138 }

139

140 /// Send data to the client

141 bool feed(ubyte[] buf, long len) {

142 if(state==State.CONNECTED) {

143 if(buf[0]!=0x05||len<buf[1]+1) return false;

144 ubyte auth=0xFF;

145 for(ubyte b=0; b<buf[1]; b++)

146 if(buf[b+2]==0) {

147 auth=0;

148 break;

149 }

150 if(auth==0xFF) {

151 sendAuth(0xFF);

152 return false;

153 }

154 switch(auth) {

155 case 0x00:

156 sendAuth(0);

157 state=State.AUTHENTICATED;

158 connectionTime=Clock.currStdTime()/NANOTOMS;

159 break;

160 case 0x02:

161 // TODO: Username/password Authentication

162 default:

163 sendAuth(0xFF);

164 return false;

165 }

166 } else if(state==State.AUTHENTICATED) {

167 if(buf[0]!=0x05||len<5) return false;

168 if(buf[1]==0x01) {

169 switch(buf[3]) {

170 case 0x01:

171 if(len<10) return false;

172 remote = new TcpSocket();

173 remote.blocking=false;

174 uint ip = *cast(uint*)reverse(cast(byte[])buf[4..8]);

175 ushort port = *cast(ushort*)reverse(cast(byte[])buf[8..10]);

176 InternetAddress addr = new InternetAddress(ip,port);

177 rPort=port;

178 if(server.isPortBlacklisted(rPort)) {

179 Logger.error(format("Port %d is blacklisted",rPort));

180 sendRes(0x08);

181 return false;

182 }

183 rDomain=addr.toAddrString();

184 if(server.isBlacklisted(addr.toAddrString())) {

185 Logger.error(format("%s is blacklisted",rDomain));

186 sendRes(0x08);

187 return false;

188 }

189 if(isMartian(addr.addr)) {

190 Logger.error(format("%s is martian",rDomain));

191 sendRes(0x08);

192 return false;

193 }

194 try {

195 remote.connect(addr);

196 state = State.GREETED;

197 confirmDataLen = 7;

198 memcpy(cast(void*)confirmData,cast(void*)buf[3..10],confirmDataLen);

199 } catch(SocketException) {

200 Logger.error(format("Failed to connect to %s:%d",rDomain,rPort));

201 }

202 break;

203 case 0x03:

204 ubyte dlen = buf[4];

205 if(len<dlen+7) return false;

206 string domain = cast(string)(buf[5..5+dlen]).dup;

207 if(server.isBlacklisted(domain)) {

208 Logger.error(format("%s is blacklisted",domain));

209 sendRes(0x08);

210 return false;

211 }

212 ushort port = *cast(ushort*)reverse(cast(byte[])buf[5+dlen..7+dlen]);

213 rPort=port;

214 if(server.isPortBlacklisted(rPort)) {

215 Logger.error(format("Port %d is blacklisted",rPort));

216 sendRes(0x08);

217 return false;

218 }

219 InternetAddress addr = new InternetAddress(domain,port);

220 if(isMartian(addr.addr)) {

221 Logger.error(format("%s is martian",domain));

222 sendRes(0x08);

223 return false;

224 }

225 rDomain=domain;

226 try {

227 remote = new TcpSocket();

228 remote.blocking=false;

229 remote.connect(addr);

230 state = State.GREETED;

231 confirmDataLen = dlen+4;

232 memcpy(cast(void*)confirmData,cast(void*)buf[3..7+dlen],dlen+4);

233 } catch(SocketException) {

234 Logger.error(format("Failed to connect to %s:%d",domain,port));

235 }

236 break;

237 default:

238 sendRes(0x08);

239 return false;

240 }

241

242 } else if(state==State.GREETED) {

243 if(remote.isAlive) {

244 state=State.ESTABLISHED;

245 } else {

246 Logger.error("Failed to connect to that");

247 sendRes(0x05);

248 return false;

249 }

250 } else {

251 sendRes(0x02);

252 return false;

253 }

254 } else if(state==State.ESTABLISHED) {

255 connectionTime=Clock.currStdTime()/NANOTOMS;

256 remote.send(buf[0..len]);

257 }

258 return true;

259 }

260

261 }

262