/* * 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.common; import sockslib.client.Socks5DatagramSocket; import sockslib.server.Socks5Handler; import sockslib.utils.SocksUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.UnknownHostException; import java.util.Arrays; /** * The class <code>Socks5DatagramPacketHandler</code> represents a datagram packet handler. * <p> * This class can encapsulate a datagram packet or decapsulate a datagram packet to help * {@link Socks5DatagramSocket} and {@link Socks5Handler} to implement UDP ASSOCIATE. * </p> * * @author Youchao Feng * @version 1.0 * @date Mar 24, 2015 9:09:39 PM */ public class Socks5DatagramPacketHandler implements DatagramPacketEncapsulation, DatagramPacketDecapsulation { /** * Logger that subclasses also can use. */ protected static final Logger logger = LoggerFactory.getLogger(Socks5DatagramPacketHandler.class); public Socks5DatagramPacketHandler() { } @Override public DatagramPacket encapsulate(DatagramPacket packet, SocketAddress destination) throws SocksException { if (destination instanceof InetSocketAddress) { InetSocketAddress destinationAddress = (InetSocketAddress) destination; final byte[] data = packet.getData(); final InetAddress remoteServerAddress = packet.getAddress(); final byte[] addressBytes = remoteServerAddress.getAddress(); final int ADDRESS_LENGTH = remoteServerAddress.getAddress().length; final int remoteServerPort = packet.getPort(); byte[] buffer = new byte[6 + packet.getLength() + ADDRESS_LENGTH]; buffer[0] = buffer[1] = 0; // reserved byte buffer[2] = 0; // fragment byte buffer[3] = (byte) (ADDRESS_LENGTH == 4 ? AddressType.IPV4 : AddressType.IPV6); System.arraycopy(addressBytes, 0, buffer, 4, ADDRESS_LENGTH); buffer[4 + ADDRESS_LENGTH] = SocksUtil.getFirstByteFromInt(remoteServerPort); buffer[5 + ADDRESS_LENGTH] = SocksUtil.getSecondByteFromInt(remoteServerPort); System.arraycopy(data, 0, buffer, 6 + ADDRESS_LENGTH, packet.getLength()); return new DatagramPacket(buffer, buffer.length, destinationAddress.getAddress(), destinationAddress.getPort()); } else { throw new IllegalArgumentException("Only support java.net.InetSocketAddress"); } } @Override public void decapsulate(DatagramPacket packet) throws SocksException { final byte[] data = packet.getData(); if (!(data[0] == 0 && data[1] == data[0])) { // check reserved byte. throw new SocksException("SOCKS version error"); } if (data[2] != 0) { throw new SocksException("SOCKS fregment is not supported"); } InetAddress remoteServerAddress = null; int remoteServerPort = -1; byte[] originalData = null; switch (data[3]) { case AddressType.IPV4: try { remoteServerAddress = InetAddress.getByAddress(Arrays.copyOfRange(data, 4, 8)); } catch (UnknownHostException e) { logger.error(e.getMessage(), e); } remoteServerPort = SocksUtil.bytesToInt(data[8], data[9]); originalData = Arrays.copyOfRange(data, 10, packet.getLength()); break; case AddressType.IPV6: try { remoteServerAddress = InetAddress.getByAddress(Arrays.copyOfRange(data, 4, 20)); } catch (UnknownHostException e) { throw new SocksException("Unknown host"); } remoteServerPort = SocksUtil.bytesToInt(data[20], data[21]); originalData = Arrays.copyOfRange(data, 22, packet.getLength()); break; case AddressType.DOMAIN_NAME: final int DOMAIN_LENGTH = data[4]; String domainName = new String(data, 5, DOMAIN_LENGTH); try { remoteServerAddress = InetAddress.getByName(domainName); } catch (UnknownHostException e) { logger.error(e.getMessage(), e); } remoteServerPort = SocksUtil.bytesToInt(data[5 + DOMAIN_LENGTH], data[6 + DOMAIN_LENGTH]); originalData = Arrays.copyOfRange(data, 7 + DOMAIN_LENGTH, packet.getLength()); break; default: break; } packet.setAddress(remoteServerAddress); packet.setPort(remoteServerPort); packet.setData(originalData); } }