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

org.bbottema.javasocksproxyserver.Socks4Impl Maven / Gradle / Ivy

There is a newer version: 4.1.2
Show newest version
/*
 * Copyright (C) 2019 Benny Bottema ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.bbottema.javasocksproxyserver;

import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import static java.util.Objects.requireNonNull;
import static org.bbottema.javasocksproxyserver.Utils.getSocketInfo;

public class Socks4Impl {

	private static final Logger LOGGER = LoggerFactory.getLogger(Socks4Impl.class);

	final ProxyHandler m_Parent;
	final byte[] DST_Port = new byte[2];
	byte[] DST_Addr = new byte[4];
	byte SOCKS_Version = 0;
	byte socksCommand;

	private InetAddress m_ExtLocalIP = null;
	InetAddress m_ServerIP = null;
	int m_nServerPort = 0;
	InetAddress m_ClientIP = null;
	int m_nClientPort = 0;

	Socks4Impl(ProxyHandler Parent) {
		m_Parent = Parent;
	}

	public byte getSuccessCode() {
		return 90;
	}

	public byte getFailCode() {
		return 91;
	}

	@NotNull
	public String commName(byte code) {
		switch (code) {
			case 0x01:
				return "CONNECT";
			case 0x02:
				return "BIND";
			case 0x03:
				return "UDP Association";
			default:
				return "Unknown Command";
		}
	}

	@NotNull
	public String replyName(byte code) {
		switch (code) {
			case 0:
				return "SUCCESS";
			case 1:
				return "General SOCKS Server failure";
			case 2:
				return "Connection not allowed by ruleset";
			case 3:
				return "Network Unreachable";
			case 4:
				return "HOST Unreachable";
			case 5:
				return "Connection Refused";
			case 6:
				return "TTL Expired";
			case 7:
				return "Command not supported";
			case 8:
				return "Address Type not Supported";
			case 9:
				return "to 0xFF UnAssigned";
			case 90:
				return "Request GRANTED";
			case 91:
				return "Request REJECTED or FAILED";
			case 92:
				return "Request REJECTED - SOCKS server can't connect to Identd on the client";
			case 93:
				return "Request REJECTED - Client and Identd report diff user-ID";
			default:
				return "Unknown Command";
		}
	}

	public boolean isInvalidAddress() {
		// IP v4 Address Type
		m_ServerIP = Utils.calcInetAddress(DST_Addr);
		m_nServerPort = Utils.calcPort(DST_Port[0], DST_Port[1]);

		m_ClientIP = m_Parent.m_ClientSocket.getInetAddress();
		m_nClientPort = m_Parent.m_ClientSocket.getPort();

		return m_ServerIP == null || m_nServerPort < 0;
	}

	protected byte getByte() {
		try {
			return m_Parent.getByteFromClient();
		} catch (Exception e) {
			return 0;
		}
	}

	public void authenticate(byte SOCKS_Ver) throws Exception {
		SOCKS_Version = SOCKS_Ver;
	}

	public void getClientCommand() throws Exception {
		// Version was get in method Authenticate()
		socksCommand = getByte();

		DST_Port[0] = getByte();
		DST_Port[1] = getByte();

		for (int i = 0; i < 4; i++) {
			DST_Addr[i] = getByte();
		}

		//noinspection StatementWithEmptyBody
		while (getByte() != 0x00) {
			// keep reading bytes
		}

		if ((socksCommand < SocksConstants.SC_CONNECT) || (socksCommand > SocksConstants.SC_BIND)) {
			refuseCommand((byte) 91);
			throw new Exception("Socks 4 - Unsupported Command : " + commName(socksCommand));
		}

		if (isInvalidAddress()) {  // Gets the IP Address
			refuseCommand((byte) 92);    // Host Not Exists...
			throw new Exception("Socks 4 - Unknown Host/IP address '" + m_ServerIP.toString());
		}

		LOGGER.debug("Accepted SOCKS 4 Command: \"" + commName(socksCommand) + "\"");
	}

	public void replyCommand(byte ReplyCode) {
		LOGGER.debug("Socks 4 reply: \"" + replyName(ReplyCode) + "\"");

		byte[] REPLY = new byte[8];
		REPLY[0] = 0;
		REPLY[1] = ReplyCode;
		REPLY[2] = DST_Port[0];
		REPLY[3] = DST_Port[1];
		REPLY[4] = DST_Addr[0];
		REPLY[5] = DST_Addr[1];
		REPLY[6] = DST_Addr[2];
		REPLY[7] = DST_Addr[3];

		m_Parent.sendToClient(REPLY);
	}

	protected void refuseCommand(byte errorCode) {
		LOGGER.debug("Socks 4 - Refuse Command: \"" + replyName(errorCode) + "\"");
		replyCommand(errorCode);
	}

	public void connect() throws Exception {
		LOGGER.debug("Connecting...");
		//	Connect to the Remote Host
		try {
			m_Parent.connectToServer(m_ServerIP.getHostAddress(), m_nServerPort);
		} catch (IOException e) {
			refuseCommand(getFailCode()); // Connection Refused
			throw new Exception("Socks 4 - Can't connect to " +
					getSocketInfo(m_Parent.m_ServerSocket));
		}

		LOGGER.debug("Connected to " + getSocketInfo(m_Parent.m_ServerSocket));
		replyCommand(getSuccessCode());
	}

	public void bindReply(byte ReplyCode, InetAddress IA, int PT) {
		LOGGER.debug("Reply to Client : \"{}\"", replyName(ReplyCode));

		final byte[] REPLY = new byte[8];
		final byte[] IP = IA.getAddress();

		REPLY[0] = 0;
		REPLY[1] = ReplyCode;
		REPLY[2] = (byte) ((PT & 0xFF00) >> 8);
		REPLY[3] = (byte) (PT & 0x00FF);
		REPLY[4] = IP[0];
		REPLY[5] = IP[1];
		REPLY[6] = IP[2];
		REPLY[7] = IP[3];

		if (m_Parent.isActive()) {
			m_Parent.sendToClient(REPLY);
		} else {
			LOGGER.debug("Closed BIND Client Connection");
		}
	}

	@NotNull
	public InetAddress resolveExternalLocalIP() {
		InetAddress IP = null;

		if (m_ExtLocalIP != null) {
			Socket sct;
			try {
				sct = new Socket(m_ExtLocalIP, m_Parent.getPort());
				IP = sct.getLocalAddress();
				sct.close();
				return m_ExtLocalIP;
			} catch (IOException e) {
				LOGGER.debug("WARNING !!! THE LOCAL IP ADDRESS WAS CHANGED !");
			}
		}

		final String[] hosts = {"www.wikipedia.org", "www.google.com", "www.microsoft.com", "www.amazon.com", "www.zombo.com", "www.ebay.com"};

		final List bindExceptions = new ArrayList<>();
		for (String host : hosts) {
			try (Socket sct = new Socket(InetAddress.getByName(host), 80)) {
				IP = sct.getLocalAddress();
				break;
			} catch (Exception e) {
				bindExceptions.add(e);
			}
		}

		if (IP == null) {
			LOGGER.error("Error in BIND() - BIND reip Failed on all common hosts to determine external IP's");
			for (Exception bindException : bindExceptions) {
				LOGGER.debug(bindException.getMessage(), bindException);
			}
		}

		m_ExtLocalIP = IP;
		return requireNonNull(IP);
	}

	public void bind() throws IOException {
		int MyPort = 0;

		LOGGER.debug("Binding...");
		// Resolve External IP
		InetAddress MyIP = resolveExternalLocalIP();

		LOGGER.debug("Local IP : " + MyIP.toString());

		ServerSocket ssock = new ServerSocket(0);
		try {
			ssock.setSoTimeout(SocksConstants.DEFAULT_PROXY_TIMEOUT);
			MyPort = ssock.getLocalPort();
		} catch (IOException e) {  // MyIP == null
			LOGGER.debug("Error in BIND() - Can't BIND at any Port");
			bindReply((byte) 92, MyIP, MyPort);
			ssock.close();
			return;
		}

		LOGGER.debug("BIND at : <" + MyIP.toString() + ":" + MyPort + ">");
		bindReply((byte) 90, MyIP, MyPort);

		Socket socket = null;

		while (socket == null) {
			if (m_Parent.checkClientData() >= 0) {
				LOGGER.debug("BIND - Client connection closed");
				ssock.close();
				return;
			}

			try {
				socket = ssock.accept();
				socket.setSoTimeout(SocksConstants.DEFAULT_PROXY_TIMEOUT);
			} catch (InterruptedIOException e) {
				// ignore
			}
			Thread.yield();
		}

		m_ServerIP = socket.getInetAddress();
		m_nServerPort = socket.getPort();

		bindReply((byte) 90, socket.getInetAddress(), socket.getPort());

		m_Parent.m_ServerSocket = socket;
		m_Parent.prepareServer();

		LOGGER.debug("BIND Connection from " + getSocketInfo(m_Parent.m_ServerSocket));
		ssock.close();
	}

	public void udp() throws IOException {
		LOGGER.debug("Error - Socks 4 don't support UDP Association!");
		LOGGER.debug("Check your Software please...");
		refuseCommand((byte) 91);    // SOCKS4 don't support UDP
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy