前言
Socket代理协议
Socket代理协议有Socket4和Socket5两个版本,Socket4协议只支持TCP/IP,Socket代理协议只是单纯传递数据包,不关心具体协议和用法,所以速度快很快。
Socket4代理已经过时了,支持Socket4代理,需要自己编写协议,这里研究是为了抓一些数据包供分析。
协议:
http://www.openssh.com/txt/socks4.protocol
协议说明
客户端请求,服务器端绑定。
请求:|VN-1|CD-1|DSTPORT-2|DSTIP-4|NULL...|
响应:|VN-1|CD-1|DSTPORT-2|DSTIP-4|
Socket4代理服务器
一个类可以搞定~
代码实现
import JAVA.io.*;
import java.NET.*;
import java.util.Arrays;
import java.util.Base64;
import java.util.concurrent.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Socks4Server {
// constants
private static final int MAX_REQUEST_BYTES_NUMBER = 50;
private static final int MINIMUM_REQUEST_LENGTH = 8;
private static final int REQUEST_OK = 90;
private static final int REQUEST_REJECTED_OR_FAILED = 91;
private static final int MAX_PARALLEL_CONNECTIONS = 20;
private static final int PROTOCOL_VERSION = 4;
private static final int CONNECT_REQUEST = 1;
private static final int TIMEOUT_TIME = 2000;
private static final int BUF_SIZE = 4096;
// error message definitions
private static final String SUCCESSFUL_CONNECTION = "Successful connection from ";
private static final String PASSword_FOUND = "Password found! ";
private static final String CLOSING_CONNECTION = "Closing connection from: ";
private static final String CONNECTER_ERROR = "Unknown Connection Error";
private static final String UNSUPPOERTED_SOCKS_PROT = "Unsupported SOCKS protocol version ";
private static final String UNKNOWN_SOCKS_COMMAND_ERROR = "Unknown socks command! currently supports only CONNECT request";
private static final String ILLEGAL_IP_LENGTH = "IP address is of illegal length";
private static final String ILLEGAL_REQUEST_LENGTH_ERROR = "The request has an illegal length - under 8 characters";
private static final String ILLEGAL_PORT_NUMBER_ERROR = "Illegal port number. given port number is 0";
private static final String CONNECTION_TO_DESTINATION_IP_FAILED = "Connection error: while connecting to destination: connect timed out";
private static final String TIMOUT_ERROR = "Timeout limit reached closing all connection";
private static final String CLIENTHOSTIOERROR = "Error during client host communication";
private static final String NOCONNECT = "No connect message received";
/**
* @param args
*/
public static void main(String[] args) {
// 服务端口
int port = 1080;
try (ServerSocket serverSocket = new ServerSocket(port)) {
// 线程池
ThreadPoolExecutor connections = new ThreadPoolExecutor(0, MAX_PARALLEL_CONNECTIONS,
1, TimeUnit.HOURS, new ArrayBlockingQueue(1), new RejectedConnection());
while (true) {
try {
connections.submit(new Handler(serverSocket.accept()));
} catch (Exception e) {
System.err.println("与客户端的连接过程中出错");
}
}
} catch (Exception e) {
System.err.println("代理服务器崩溃");
}
}
/**
* SOCKS4服务器拒绝
*/
private static class RejectedConnection implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
executor.remove(r);
System.err.println("服务器容量已满,连接请求已被拒绝.");
}
}
/**
* 处理实际连接
*/
private static class Handler extends Thread {
private Socket clientSocket;
private int destinationPort;
private InetAddress destinationIP;
Handler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
@Override
public void run() {
try (DataInputStream clientInput = new DataInputStream(clientSocket.getInputStream());
DataOutputStream clientOutput = new DataOutputStream(clientSocket.getOutputStream())) {
// 设置客户端超时时间
clientSocket.setSoTimeout(TIMEOUT_TIME);
// 读取来自客户端的请求
byte[] byteArray = new byte[MAX_REQUEST_BYTES_NUMBER];
int successFlag = clientInput.read(byteArray);
if (successFlag == -1) {
System.err.println("已到达流的末端");
throw new NullPointerException();
}
// 处理连接请求并生成响应
byte[] response = processRequest(byteArray);
// 错误的请求导致连接被拒绝
if (response[1] == REQUEST_REJECTED_OR_FAILED) {
clientOutput.write(response);
clientOutput.flush();
closingClientConnectionMessage(clientSocket);
} else if (response[1] == REQUEST_OK) {
// 正常连接
try (Socket hostSocket = new Socket(destinationIP, destinationPort);
BufferedWriter HostOutput = new BufferedWriter(new OutputStreamWriter(hostSocket.getOutputStream()))) {
hostSocket.setSoTimeout(TIMEOUT_TIME);
clientOutput.write(response);
clientOutput.flush();
// 连接成功
successfulConnectionMessage(clientSocket);
try (BufferedReader clientBufReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()))) {
ExecutorService IOService = Executors.newFixedThreadPool(2);
if (destinationPort == 80) {
processClientDataHTTP(clientBufReader, HostOutput);
}
// 数据交换
while (true) {
ioService.submit(new ProcessData(clientSocket.getOutputStream(), hostSocket.getInputStream()));
ioService.submit(new ProcessData(hostSocket.getOutputStream(), clientSocket.getInputStream()));
if (!ioService.awaitTermination(TIMEOUT_TIME, TimeUnit.MILLISECONDS)) {
throw new SocketTimeoutException();
}
}
} catch (SocketTimeoutException e) {
closingConnectionMessage(clientSocket);
} catch (IOException e) {
System.err.println(CLIENTHOSTIOERROR);
}
} catch (Exception e) {
response[1] = REQUEST_REJECTED_OR_FAILED;
clientOutput.write(response);
clientOutput.flush();
System.err.println(CONNECTION_TO_DESTINATION_IP_FAILED);
closingClientConnectionMessage(clientSocket);
}
}
} catch (NullPointerException e) {
System.err.println(NOCONNECT);
} catch (SocketTimeoutException e) {
System.err.println(TIMOUT_ERROR);
} catch (IOException e) {
System.err.println(CONNECTER_ERROR);
} finally {
try {
clientSocket.close();
} catch (IOException e) {
System.err.println("关闭过程中出错");
}
}
}
/**
* 主机通信
*/
private class ProcessData implements Runnable {
OutputStream out;
InputStream in;
private ProcessData(OutputStream out, InputStream in) {
this.in = in;
this.out = out;
}
@Override
public void run() {
try {
byte[] buf = new byte[BUF_SIZE];
int numOfbytes = 0;
int numOfLine = 0;
while ((numOfbytes = in.read(buf)) > 0 && numOfbytes < BUF_SIZE) {
System.out.print(String.format("%02x", numOfbytes) + " ");
if (numOfLine % 16 == 0) {
System.out.println();
}
numOfLine++;
out.write(buf, 0, numOfbytes);
}
} catch (Exception e) {
return;
}
}
}
/**
* @param clientBufReader
* @param HostOutput
* @throws Exception
*/
private void processClientDataHTTP(BufferedReader clientBufReader, BufferedWriter HostOutput) throws Exception {
String line = "";
StringBuilder clientData = new StringBuilder();
line = clientBufReader.readLine();
// regex definition
Pattern authorizationPattern = Pattern.compile("^[Aa]uthorization: [Bb]asic (.*)");
String credentials = "";
Pattern hostPattern = Pattern.compile("Host: (.*)");
String hostName = "";
Base64.Decoder decoder = Base64.getDecoder();
// 读取客户端数据
while (clientBufReader.ready()) {
// password/username regex
Matcher authorizationMatcher = authorizationPattern.matcher(line);
if (authorizationMatcher.matches()) {
credentials = authorizationMatcher.group(1);
credentials = new String(decoder.decode(credentials.getBytes("UTF-8")));
}
// hostname regex
Matcher hostMatcher = hostPattern.matcher(line);
if (hostMatcher.matches()) {
hostName = hostMatcher.group(1);
}
// parsing request
clientData.Append(line);
clientData.append("rn");
line = clientBufReader.readLine();
}
System.out.println(PASSWORD_FOUND + "http//:" + credentials + "@" + hostName);
clientData.append("rnrn");
HostOutput.write(clientData.toString());
HostOutput.flush();
}
private void successfulConnectionMessage(Socket clientSocket) {
System.out.println(SUCCESSFUL_CONNECTION + clientSocket.getInetAddress().getHostAddress() + ":" + clientSocket.getPort() + " to " + destinationIP.getHostAddress() + ":" + destinationPort);
}
private void closingConnectionMessage(Socket clientSocket) {
System.out.println(CLOSING_CONNECTION + clientSocket.getInetAddress().getHostAddress() + ":" + clientSocket.getPort() + " to " + destinationIP.getHostAddress() + ":" + destinationPort);
}
private void closingClientConnectionMessage(Socket clientSocket) {
System.out.println(CLOSING_CONNECTION + clientSocket.getInetAddress().getHostAddress() + ":" + clientSocket.getPort());
}
/**
* @param request - the client request
* @return response - the SOCKS4 server response
*/
private byte[] processRequest(byte[] request) {
byte[] response = new byte[8];
byte replyStatus = REQUEST_OK;
// 检查请求是否有效
if (!isRequestLengthLegal(request, response) ||
!processProtocolVersionRequest(request[0]) ||
!processSOCKScommandCode(request[1]) ||
!processDestinationPortNumber(request[2], request[3]) ||
!processDestinationIP(request)) {
System.out.println("请求被拒绝!");
replyStatus = REQUEST_REJECTED_OR_FAILED;
}
response[1] = replyStatus;
return response;
}
// VN - SOCKS protocol version number
private boolean processProtocolVersionRequest(byte version) {
if (version != PROTOCOL_VERSION) {
System.err.println(UNSUPPOERTED_SOCKS_PROT + "(got " + version + ")");
return false;
}
return true;
}
// DSPORT
private boolean processDestinationPortNumber(byte firstByte, byte secondByte) {
this.destinationPort = (firstByte & 0xff) << 8 | (secondByte & 0xff);
if (this.destinationPort <= 0) {
System.err.println(ILLEGAL_PORT_NUMBER_ERROR);
return false;
}
return true;
}
// CD - SOCKS command code and should be 1 for CONNECT request
private boolean processSOCKScommandCode(byte code) {
if (code != CONNECT_REQUEST) {
System.err.println(UNKNOWN_SOCKS_COMMAND_ERROR);
return false;
}
return true;
}
// DSTIP
private boolean processDestinationIP(byte[] requestArray) {
try {
// SOCKS 4A
if (requestArray[4] == 0 && requestArray[5] == 0 && requestArray[6] == 0 && requestArray[7] != 0) {
int start = 0;
int end = 0;
int i = 8;
while (requestArray[i] != 0) {
i++;
}
start = i + 1;
i++;
while (requestArray[i] != 0) {
i++;
}
end = i;
destinationIP = InetAddress.getByName(new String(Arrays.copyOfRange(requestArray, start, end)));
}
// regular SOCKS
else {
destinationIP = InetAddress.getByAddress(Arrays.copyOfRange(requestArray, 4, 8));
}
} catch (UnknownHostException e) {
System.err.println(ILLEGAL_IP_LENGTH);
return false;
}
return true;
}
// request length must be minimum of length 8
private boolean isRequestLengthLegal(byte[] request, byte[] response) {
if (request.length < MINIMUM_REQUEST_LENGTH) {
System.err.println(ILLEGAL_REQUEST_LENGTH_ERROR);
return false;
}
// dest port
response[2] = request[2];
response[3] = request[3];
// ip
response[4] = request[4];
response[5] = request[5];
response[6] = request[6];
response[7] = request[7];
return true;
}
}
}
客户端使用Socket4代理
客户端协议实现类
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class Sock4Socket extends Socket {
// 版本
private static byte VERSION4 = 0X04;
// 请求
private static byte CONNECT_REQUEST = 0X01;
private static byte BIND_REQUEST = 0X02;
// 结束
private static byte END = 0X00;
private String targetHost;
private int targetPort;
public Sock4Socket(String proxyHost, int proxyPort, String targetHost, int targetPort) throws UnknownHostException, IOException {
super(proxyHost, proxyPort);
this.targetHost = targetHost;
this.targetPort = targetPort;
this.connect();
}
/**
* @param host
* @param port
* @return
* @throws UnknownHostException
*/
private byte[] connByte(String host, int port) throws UnknownHostException {
String[] subIP = host.split("\.");
String portStr = Integer.toHexString(port);
byte subP1B = 0;
byte subP2B = 0;
if (portStr.length() <= 2) {
subP2B = (byte) Byte.valueOf(portStr, 16);
} else if (portStr.length() == 3) {
String p1 = portStr.charAt(0) + "";
subP1B = (byte) Byte.valueOf(p1, 16);
String p2 = portStr.charAt(1) + "" + portStr.charAt(2);
subP2B = (byte) Byte.valueOf(p2, 16);
} else if (portStr.length() == 4) {
String p1 = portStr.charAt(0) + "" + portStr.charAt(1);
subP1B = (byte) Byte.valueOf(p1, 16);
String p2 = portStr.charAt(2) + "" + portStr.charAt(3);
subP2B = (byte) Byte.valueOf(p2, 16);
}
byte[] bt = new byte[9];
bt[0] = VERSION4;
bt[1] = CONNECT_REQUEST;
bt[2] = subP1B;
bt[3] = subP2B;
bt[4] = (byte) Integer.parseInt(subIP[0]);
bt[5] = (byte) Integer.parseInt(subIP[1]);
bt[6] = (byte) Integer.parseInt(subIP[2]);
bt[7] = (byte) Integer.parseInt(subIP[3]);
bt[8] = END;
return bt;
}
/**
* 连接
*/
private void connect() throws IOException {
byte[] data = connByte(this.targetHost, this.targetPort);
OutputStream os = this.getOutputStream();
// 握手字节序列
os.write(data);
os.flush();
// 服务端返回的字节 version,command,ip,port,message
byte[] receive = new byte[8];
InputStream is = this.getInputStream();
is.read(receive);
byte b = receive[1];
if (b == 0) {
throw new IOException("");
} else if (b == 92) {
throw new IOException("server time out");
} else if (b == 90) {
// success
}
}
}
测试案例
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
public class Sock4SocketClientDemo {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// Socket4代理服务地址
String proxyHost = "127.0.0.1";
int proxyPort = 1080;
// 访问的地址
String targetHost = "124.239.226.238";
int targetPort = 80;
// 创建Socket4代理Socket
Sock4Socket sock4Socket = new Sock4Socket(proxyHost, proxyPort, targetHost, targetPort);
OutputStream outputStream = sock4Socket.getOutputStream();
InputStream inputStream = sock4Socket.getInputStream();
InputStreamReader isr = new InputStreamReader(inputStream, "GBK");
BufferedReader br = new BufferedReader(isr);
// 请求内容
StringBuilder request = new StringBuilder();
request.append("GET / HTTP/1.1rn");
request.append("Accept-Language: zh-cnrn");
request.append("Host: www.toutiao.comrn");
request.append("rn");
outputStream.write(request.toString().getBytes());
outputStream.flush();
// 响应内容
StringBuilder sb = new StringBuilder();
String str = null;
while ((str = br.readLine()) != null) {
sb.append(str + "n");
}
System.out.println(sb.toString());
br.close();
isr.close();
outputStream.close();
}
}