<返回更多

Java,Socket,实现Socket4代理服务器,客户端用Socket4代理请求

2022-09-02    古怪今人
加入收藏

前言

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();
    }

}
声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>