/* * Copyright 2015-2025 the original author or authors. * * 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 sockslib.client; import sockslib.common.AddressType; import sockslib.common.ProtocolErrorException; import sockslib.common.SocksCommand; import sockslib.common.SocksException; import sockslib.utils.LogMessageBuilder; import sockslib.utils.LogMessageBuilder.MsgType; import sockslib.utils.UnsignedByte; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; /** * The class <code>GenericSocksCommandSender</code> implements {@link SocksCommandSender}. * * @author Youchao Feng * @version 1.0 * @date Mar 19, 2015 2:45:23 PM */ public class GenericSocksCommandSender implements SocksCommandSender { protected static final Logger logger = LoggerFactory.getLogger(GenericSocksCommandSender.class); /** * length of IPv4 address. */ protected static final int LENGTH_OF_IPV4 = 4; /** * length of IPv6 address. */ protected static final int LENGTH_OF_IPV6 = 16; @Override public CommandReplyMessage send(Socket socket, SocksCommand command, InetAddress address, int port, int version) throws SocksException, IOException { return send(socket, command, new InetSocketAddress(address, port), version); } @Override public CommandReplyMessage send(Socket socket, SocksCommand command, SocketAddress socketAddress, int version) throws SocksException, IOException { if (!(socketAddress instanceof InetSocketAddress)) { throw new IllegalArgumentException("Unsupported address type"); } final InputStream inputStream = socket.getInputStream(); final OutputStream outputStream = socket.getOutputStream(); final InetSocketAddress address = (InetSocketAddress) socketAddress; final byte[] bytesOfAddress = address.getAddress().getAddress(); final int ADDRESS_LENGTH = bytesOfAddress.length; final int port = address.getPort(); byte addressType = -1; byte[] bufferSent = null; if (ADDRESS_LENGTH == LENGTH_OF_IPV4) { addressType = ATYPE_IPV4; bufferSent = new byte[6 + LENGTH_OF_IPV4]; } else if (ADDRESS_LENGTH == LENGTH_OF_IPV6) { addressType = ATYPE_IPV6; bufferSent = new byte[6 + LENGTH_OF_IPV6]; } else { throw new SocksException("Address error");// TODO } bufferSent[0] = (byte) version; bufferSent[1] = (byte) command.getValue(); bufferSent[2] = RESERVED; bufferSent[3] = addressType; System.arraycopy(bytesOfAddress, 0, bufferSent, 4, ADDRESS_LENGTH);// copy address bytes bufferSent[4 + ADDRESS_LENGTH] = (byte) ((port & 0xff00) >> 8); bufferSent[5 + ADDRESS_LENGTH] = (byte) (port & 0xff); outputStream.write(bufferSent); outputStream.flush(); logger.debug("{}", LogMessageBuilder.build(bufferSent, MsgType.SEND)); return checkServerReply(inputStream); } @Override public CommandReplyMessage send(Socket socket, SocksCommand command, String host, int port, int version) throws SocksException, IOException { final InputStream inputStream = socket.getInputStream(); final OutputStream outputStream = socket.getOutputStream(); final int lengthOfHost = host.getBytes().length; final byte[] bufferSent = new byte[7 + lengthOfHost]; bufferSent[0] = (byte) version; bufferSent[1] = (byte) command.getValue(); bufferSent[2] = RESERVED; bufferSent[3] = ATYPE_DOMAINNAME; bufferSent[4] = (byte) lengthOfHost; byte[] bytesOfHost = host.getBytes(); System.arraycopy(bytesOfHost, 0, bufferSent, 5, lengthOfHost);// copy host bytes. bufferSent[5 + host.length()] = (byte) ((port & 0xff00) >> 8); bufferSent[6 + host.length()] = (byte) (port & 0xff); outputStream.write(bufferSent); outputStream.flush(); logger.debug("{}", LogMessageBuilder.build(bufferSent, MsgType.SEND)); return checkServerReply(inputStream); } @Override public CommandReplyMessage checkServerReply(InputStream inputStream) throws SocksException, IOException { byte serverReply = -1; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); int temp = 0; for (int i = 0; i < 4; i++) { temp = inputStream.read(); byteArrayOutputStream.write(temp); } byte addressType = (byte) temp; switch (addressType) { case AddressType.IPV4: for (int i = 0; i < 6; i++) { byteArrayOutputStream.write(inputStream.read()); } break; case AddressType.DOMAIN_NAME: temp = inputStream.read(); byteArrayOutputStream.write(temp); for (int i = 0; i < temp + 2; i++) { byteArrayOutputStream.write(inputStream.read()); } break; case AddressType.IPV6: for (int i = 0; i < 18; i++) { byteArrayOutputStream.write(inputStream.read()); } break; default: throw new ProtocolErrorException("Address type not support, type value: " + addressType); } byte[] receivedData = byteArrayOutputStream.toByteArray(); int length = receivedData.length; logger.debug("{}", LogMessageBuilder.build(receivedData, length, MsgType.RECEIVE)); byte[] addressBytes = null; byte[] portBytes = new byte[2]; if (receivedData[3] == AddressType.IPV4) { addressBytes = new byte[4]; System.arraycopy(receivedData, 4, addressBytes, 0, addressBytes.length); int a = UnsignedByte.toInt(addressBytes[0]); int b = UnsignedByte.toInt(addressBytes[1]); int c = UnsignedByte.toInt(addressBytes[2]); int d = UnsignedByte.toInt(addressBytes[3]); portBytes[0] = receivedData[8]; portBytes[1] = receivedData[9]; logger.debug("Server replied:Address as IPv4:{}.{}.{}.{}, port:{}", a, b, c, d, (UnsignedByte.toInt(portBytes[0]) << 8) | (UnsignedByte.toInt(portBytes[1]))); } else if (receivedData[3] == AddressType.DOMAIN_NAME) { int size = receivedData[4]; size = size & 0xFF; addressBytes = new byte[size]; System.arraycopy(receivedData, 4, addressBytes, 0, size); portBytes[0] = receivedData[4 + size]; portBytes[1] = receivedData[5 + size]; logger.debug("Server replied:Address as host:{}, port:{}", new String(addressBytes), (UnsignedByte.toInt(portBytes[0]) << 8) | (UnsignedByte.toInt(portBytes[1]))); } else if (receivedData[3] == AddressType.IPV6) { int size = receivedData[4]; size = size & 0xFF; addressBytes = new byte[16]; for (int i = 0; i < addressBytes.length; i++) { addressBytes[i] = receivedData[4 + i]; } logger.debug("Server replied:Address as IPv6:{}", new String(addressBytes)); } serverReply = receivedData[1]; if (serverReply != REP_SUCCEEDED) { throw SocksException.serverReplyException(serverReply); } logger.debug("SOCKS server response success"); byte[] receivedBytes = new byte[length]; System.arraycopy(receivedData, 0, receivedBytes, 0, length); return new CommandReplyMessage(receivedBytes); } }