All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cc.owoo.godpen.network.proxy.ProxyServer Maven / Gradle / Ivy

package cc.owoo.godpen.network.proxy;

import cc.owoo.godpen.network.InputStreamBuilder;
import cc.owoo.godpen.network.SocketUtil;
import cc.owoo.godpen.network.http.Method;
import cc.owoo.godpen.network.http.Request;
import cc.owoo.godpen.network.http.Response;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Socket5代理服务器
 * Created by nimensei
 * 2022-07-24 下午 01:58
 **/
public class ProxyServer {
    private final int port;// 端口号
    private ServerSocket serverSocket;// 服务器socket
    private OnCreateTCP onCreateTCP;// 创建Socket对象

    public ProxyServer(int port) {
        this.port = port;
    }

    /**
     * 启动
     */
    public void start() throws IOException {
        synchronized (this) {
            if (serverSocket != null)
                return;
            serverSocket = SocketUtil.server(port, this::handler, () -> serverSocket = null);
        }
    }

    /**
     * 处理连接
     *
     * @param socket 连接的socket
     */
    private void handler(Socket socket) {
        try {
            int head = socket.getInputStream().read();
            if ((head >= 'a' && head <= 'z') || (head >= 'A' && head <= 'Z'))
                http(head, socket);
            else if (head == 5)
                socket5(socket);
        } catch (IOException ignored) {
        }
        try {
            socket.close();
        } catch (IOException ignored) {
        }
    }

    /**
     * HTTP隧道代理
     *
     * @param head   头字节
     * @param client 连接的socket
     */
    private void http(int head, Socket client) throws IOException {
        InputStreamBuilder inputStreamBuilder = new InputStreamBuilder();
        inputStreamBuilder.append(head);
        inputStreamBuilder.append(client.getInputStream());
        Request request = new Request();
        request.read(inputStreamBuilder);
        inputStreamBuilder.clear();
        if (request.isInvalid())
            return;
        request.readBytes();
        String hostName = request.getHostName();
        if (hostName == null)
            return;
        int port = request.getPort();
        if (port < 1)
            port = request.getSchemeNotNull().equals("https") ? 443 : 80;
        Socket target;
        try {
            target = new Socket(hostName, port);
        } catch (IOException e) {
            new Response(400, "socket error: " + e.getMessage()).write(client.getOutputStream());
            return;
        }
        if (request.isMethod(Method.CONNECT))
            new Response(200, "Connection established")
                    .write(client.getOutputStream());
        else
            request.write(target.getOutputStream());
        SocketUtil.socketBindAndClose(client, target);
    }

    /**
     * Socket5代理
     *
     * @param socket 连接的Socket
     */
    private void socket5(Socket socket) throws IOException {
        InputStream inputStream = socket.getInputStream();
        OutputStream outputStream = socket.getOutputStream();
        int cut = inputStream.read();
        if (cut == -1)
            return;
        byte[] method = inputStream.readNBytes(cut);
        if (method[0] != 0)
            return;
        outputStream.write(new byte[]{5, 0});
        outputStream.flush();
        int ver = inputStream.read();
        int cmd = inputStream.read();
        int rsv = inputStream.read();
        int aType = inputStream.read();
        if (ver != 5 || (cmd != 1 && cmd != 3) || rsv == -1 || aType == -1)
            return;
        InetAddress inetAddress;
        byte[] addressBytes;
        switch (aType) {
            case 1 -> {
                addressBytes = inputStream.readNBytes(4);
                if (addressBytes == null || addressBytes.length != 4)
                    return;
                inetAddress = InetAddress.getByAddress(addressBytes);
            }
            case 3 -> {
                int len = inputStream.read();
                if (len == -1)
                    return;
                byte[] domain = inputStream.readNBytes(len);
                if (domain == null || domain.length != len)
                    return;
                inetAddress = InetAddress.getByName(new String(domain));
                addressBytes = inetAddress.getAddress();
                aType = addressBytes.length == 4 ? 1 : 4;
            }
            case 4 -> {
                addressBytes = inputStream.readNBytes(16);
                if (addressBytes == null || addressBytes.length != 16)
                    return;
                inetAddress = InetAddress.getByAddress(addressBytes);
            }
            default -> {
                return;
            }
        }
        int portBit1 = inputStream.read();
        int portBit2 = inputStream.read();
        if (portBit1 == -1 || portBit2 == -1)
            return;
        int port = portBit1 << 8 | portBit2;
        outputStream.write(new byte[]{(byte) ver, 0, 0, (byte) aType});
        outputStream.write(addressBytes);
        outputStream.write(portBit1);
        outputStream.write(portBit2);
        outputStream.flush();
        if (cmd == 1)
            tcp(socket, inetAddress, port);
        else
            udp(socket, inetAddress, port);
    }

    /**
     * 建立TPC代理连接
     *
     * @param client      代理客户端
     * @param inetAddress 目标地址
     * @param port        目标端口
     * @throws IOException 发生异常
     */
    private void tcp(Socket client, InetAddress inetAddress, int port) throws IOException {
        Socket socket;
        if (onCreateTCP == null)
            socket = new Socket(inetAddress, port);
        else
            socket = onCreateTCP.create(inetAddress, port);
        if (socket == null)
            return;
        SocketUtil.socketBindAndClose(client, socket);
    }

    /**
     * 建立UDP代理连接
     *
     * @param client      代理客户端
     * @param inetAddress 目标地址
     * @param port        目标端口
     * @throws IOException 发生异常
     */
    private void udp(Socket client, InetAddress inetAddress, int port) throws IOException {
        // TODO UDP代理协议
    }

    public void setOnCreateSocket(OnCreateTCP onCreateTCP) {
        this.onCreateTCP = onCreateTCP;
    }

    /**
     * 关闭服务器
     *
     * @throws IOException 关闭异常
     */
    public void close() throws IOException {
        synchronized (this) {
            if (serverSocket == null)
                return;
            serverSocket.close();
        }
    }

    /**
     * 创建TCP Socket对象
     */
    public interface OnCreateTCP {
        /**
         * 创建连接
         *
         * @param inetAddress 地址
         * @param port        端口号
         * @return 创建的Socket对象
         * @throws IOException 创建异常
         */
        Socket create(InetAddress inetAddress, int port) throws IOException;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy