diff --git a/urban-spork-client/src/com/urbanspork/client/AbstractClientUdpRelayHandler.java b/urban-spork-client/src/com/urbanspork/client/AbstractClientUdpRelayHandler.java index 5f0b38a4..b637d24f 100644 --- a/urban-spork-client/src/com/urbanspork/client/AbstractClientUdpRelayHandler.java +++ b/urban-spork-client/src/com/urbanspork/client/AbstractClientUdpRelayHandler.java @@ -1,7 +1,7 @@ package com.urbanspork.client; import com.urbanspork.common.config.ServerConfig; -import com.urbanspork.common.transport.udp.TernaryDatagramPacket; +import com.urbanspork.common.transport.udp.DatagramPacketWrapper; import com.urbanspork.common.util.LruCache; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; @@ -12,7 +12,7 @@ import java.time.Duration; -public abstract class AbstractClientUdpRelayHandler extends SimpleChannelInboundHandler { +public abstract class AbstractClientUdpRelayHandler extends SimpleChannelInboundHandler { private static final Logger logger = LoggerFactory.getLogger(AbstractClientUdpRelayHandler.class); protected final ServerConfig config; @@ -26,25 +26,25 @@ protected AbstractClientUdpRelayHandler(ServerConfig config, Duration keepAlive) }); } - protected abstract Object convertToWrite(TernaryDatagramPacket msg); + protected abstract Object convertToWrite(DatagramPacketWrapper msg); - protected abstract K getKey(TernaryDatagramPacket msg); + protected abstract K getKey(DatagramPacketWrapper msg); protected abstract Channel newBindingChannel(Channel inboundChannel, K k); @Override - public void channelRead0(ChannelHandlerContext ctx, TernaryDatagramPacket msg) { + public void channelRead0(ChannelHandlerContext ctx, DatagramPacketWrapper msg) { DatagramPacket packet = msg.packet(); Channel inbound = ctx.channel(); Channel outbound = getBindingChannel(inbound, getKey(msg)); - logger.info("[udp][{}]{}→{}~{}→{}", config.getProtocol(), packet.sender(), inbound.localAddress(), outbound.localAddress(), msg.third()); + logger.info("[udp][{}]{}→{}~{}→{}", config.getProtocol(), packet.sender(), inbound.localAddress(), outbound.localAddress(), msg.proxy()); outbound.writeAndFlush(convertToWrite(msg)); } @Override public void handlerRemoved(ChannelHandlerContext ctx) { logger.info("Stop timer and clear binding"); - binding.clear(); + binding.release(); } private Channel getBindingChannel(Channel inboundChannel, K key) { diff --git a/urban-spork-client/src/com/urbanspork/client/ClientSocksConnectHandler.java b/urban-spork-client/src/com/urbanspork/client/ClientSocksConnectHandler.java index 923d8f78..006ec857 100644 --- a/urban-spork-client/src/com/urbanspork/client/ClientSocksConnectHandler.java +++ b/urban-spork-client/src/com/urbanspork/client/ClientSocksConnectHandler.java @@ -1,10 +1,12 @@ package com.urbanspork.client; -import com.urbanspork.client.shadowsocks.ClientTCPChannelInitializer; -import com.urbanspork.client.vmess.ClientChannelInitializer; +import com.urbanspork.client.vmess.ClientAEADCodec; import com.urbanspork.common.channel.AttributeKeys; import com.urbanspork.common.channel.ChannelCloseUtils; import com.urbanspork.common.channel.DefaultChannelInboundHandler; +import com.urbanspork.common.codec.shadowsocks.Mode; +import com.urbanspork.common.codec.shadowsocks.tcp.Context; +import com.urbanspork.common.codec.shadowsocks.tcp.TcpRelayCodec; import com.urbanspork.common.config.ServerConfig; import com.urbanspork.common.protocol.Protocol; import com.urbanspork.common.protocol.socks.Socks5; @@ -13,6 +15,7 @@ import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.socksx.v5.DefaultSocks5CommandResponse; @@ -33,17 +36,11 @@ protected void channelRead0(ChannelHandlerContext ctx, Socks5CommandRequest requ Channel inboundChannel = ctx.channel(); ServerConfig config = inboundChannel.attr(AttributeKeys.SERVER_CONFIG).get(); InetSocketAddress serverAddress = new InetSocketAddress(config.getHost(), config.getPort()); - ChannelHandler outboundHandler; - if (Protocol.vmess == config.getProtocol()) { - outboundHandler = new ClientChannelInitializer(request, config); - } else { - outboundHandler = new ClientTCPChannelInitializer(request, config); - } new Bootstrap() .group(inboundChannel.eventLoop()) .channel(inboundChannel.getClass()) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000) - .handler(outboundHandler) + .handler(getOutboundChannelHandler(request, config)) .connect(serverAddress).addListener((ChannelFutureListener) future -> { if (future.isSuccess()) { Channel outbound = future.channel(); @@ -60,4 +57,22 @@ protected void channelRead0(ChannelHandlerContext ctx, Socks5CommandRequest requ } }); } + + private static ChannelHandler getOutboundChannelHandler(Socks5CommandRequest request, ServerConfig config) { + if (Protocol.vmess == config.getProtocol()) { + return new ChannelInitializer<>() { + @Override + protected void initChannel(Channel channel) { + channel.pipeline().addLast(new ClientAEADCodec(config.getCipher(), InetSocketAddress.createUnresolved(request.dstAddr(), request.dstPort()), config.getPassword())); + } + }; + } else { + return new ChannelInitializer<>() { + @Override + protected void initChannel(Channel channel) { + channel.pipeline().addLast(new TcpRelayCodec(new Context(), config, request, Mode.Client)); + } + }; + } + } } \ No newline at end of file diff --git a/urban-spork-client/src/com/urbanspork/client/shadowsocks/ClientTCPChannelInitializer.java b/urban-spork-client/src/com/urbanspork/client/shadowsocks/ClientTCPChannelInitializer.java deleted file mode 100644 index f594c7fd..00000000 --- a/urban-spork-client/src/com/urbanspork/client/shadowsocks/ClientTCPChannelInitializer.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.urbanspork.client.shadowsocks; - -import com.urbanspork.common.codec.shadowsocks.Mode; -import com.urbanspork.common.codec.shadowsocks.tcp.Context; -import com.urbanspork.common.codec.shadowsocks.tcp.TcpRelayCodec; -import com.urbanspork.common.config.ServerConfig; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequest; - -public class ClientTCPChannelInitializer extends ChannelInitializer { - - private final Socks5CommandRequest request; - - private final ServerConfig config; - - public ClientTCPChannelInitializer(Socks5CommandRequest request, ServerConfig config) { - this.request = request; - this.config = config; - } - - @Override - public void initChannel(Channel channel) { - channel.pipeline().addLast(new TcpRelayCodec(new Context(), config, request, Mode.Client)); - } -} diff --git a/urban-spork-client/src/com/urbanspork/client/shadowsocks/ClientUDPAssociateHandler.java b/urban-spork-client/src/com/urbanspork/client/shadowsocks/ClientUDPAssociateHandler.java index e7724e96..65922cfb 100644 --- a/urban-spork-client/src/com/urbanspork/client/shadowsocks/ClientUDPAssociateHandler.java +++ b/urban-spork-client/src/com/urbanspork/client/shadowsocks/ClientUDPAssociateHandler.java @@ -34,8 +34,8 @@ private void channelRead0(ChannelHandlerContext ctx, Socks5CommandRequest reques Channel channel = ctx.channel(); ServerConfig config = channel.attr(AttributeKeys.SERVER_CONFIG).get(); if (Protocol.vmess == config.getProtocol() && !config.udpEnabled()) { - channel.writeAndFlush(new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, request.dstAddrType())); logger.error("UDP is not enabled"); + channel.writeAndFlush(new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, request.dstAddrType())); return; } if (request.dstPort() == 0) { diff --git a/urban-spork-client/src/com/urbanspork/client/shadowsocks/ClientUdpRelayHandler.java b/urban-spork-client/src/com/urbanspork/client/shadowsocks/ClientUdpRelayHandler.java index 308df217..4cba26d5 100644 --- a/urban-spork-client/src/com/urbanspork/client/shadowsocks/ClientUdpRelayHandler.java +++ b/urban-spork-client/src/com/urbanspork/client/shadowsocks/ClientUdpRelayHandler.java @@ -5,7 +5,7 @@ import com.urbanspork.common.codec.shadowsocks.Mode; import com.urbanspork.common.codec.shadowsocks.udp.UdpRelayCodec; import com.urbanspork.common.config.ServerConfig; -import com.urbanspork.common.transport.udp.TernaryDatagramPacket; +import com.urbanspork.common.transport.udp.DatagramPacketWrapper; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; @@ -33,12 +33,12 @@ public ClientUdpRelayHandler(ServerConfig config, EventLoopGroup workerGroup) { } @Override - protected Object convertToWrite(TernaryDatagramPacket msg) { - return new TernaryDatagramPacket(new DatagramPacket(msg.packet().content(), msg.third()), relay); + protected Object convertToWrite(DatagramPacketWrapper msg) { + return new DatagramPacketWrapper(new DatagramPacket(msg.packet().content(), msg.proxy()), relay); } @Override - protected InetSocketAddress getKey(TernaryDatagramPacket msg) { + protected InetSocketAddress getKey(DatagramPacketWrapper msg) { return msg.packet().sender(); } @@ -72,7 +72,7 @@ private static class InboundHandler extends SimpleChannelInboundHandler { @@ -40,11 +43,11 @@ public class ClientAEADCodec extends ByteToMessageCodec { private PayloadEncoder bodyEncoder; private PayloadDecoder bodyDecoder; - public ClientAEADCodec(CipherKind cipher, Socks5CommandRequest address, String uuid) { + public ClientAEADCodec(CipherKind cipher, InetSocketAddress address, String uuid) { this(cipher, RequestCommand.TCP, address, uuid); } - ClientAEADCodec(CipherKind cipher, RequestCommand command, Socks5CommandRequest address, String uuid) { + ClientAEADCodec(CipherKind cipher, RequestCommand command, InetSocketAddress address, String uuid) { this(RequestHeader.defaultHeader(SecurityType.valueOf(cipher), command, address, uuid), new ClientSession()); } diff --git a/urban-spork-client/src/com/urbanspork/client/vmess/ClientChannelInitializer.java b/urban-spork-client/src/com/urbanspork/client/vmess/ClientChannelInitializer.java deleted file mode 100644 index 029b2c6e..00000000 --- a/urban-spork-client/src/com/urbanspork/client/vmess/ClientChannelInitializer.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.urbanspork.client.vmess; - -import com.urbanspork.common.config.ServerConfig; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequest; - -public class ClientChannelInitializer extends ChannelInitializer { - - private final Socks5CommandRequest request; - - private final ServerConfig config; - - public ClientChannelInitializer(Socks5CommandRequest request, ServerConfig config) { - this.request = request; - this.config = config; - } - - @Override - public void initChannel(SocketChannel remoteChannel) { - remoteChannel.pipeline().addLast(new ClientAEADCodec(config.getCipher(), request, config.getPassword())); - } -} diff --git a/urban-spork-client/src/com/urbanspork/client/vmess/ClientUdpOverTCPHandler.java b/urban-spork-client/src/com/urbanspork/client/vmess/ClientUdpOverTCPHandler.java index 7001e3f2..0f08f188 100644 --- a/urban-spork-client/src/com/urbanspork/client/vmess/ClientUdpOverTCPHandler.java +++ b/urban-spork-client/src/com/urbanspork/client/vmess/ClientUdpOverTCPHandler.java @@ -3,9 +3,8 @@ import com.urbanspork.client.AbstractClientUdpRelayHandler; import com.urbanspork.common.channel.DefaultChannelInboundHandler; import com.urbanspork.common.config.ServerConfig; -import com.urbanspork.common.protocol.socks.Socks5; import com.urbanspork.common.protocol.vmess.header.RequestCommand; -import com.urbanspork.common.transport.udp.TernaryDatagramPacket; +import com.urbanspork.common.transport.udp.DatagramPacketWrapper; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; @@ -17,8 +16,6 @@ import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequest; -import io.netty.handler.codec.socksx.v5.Socks5CommandType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,13 +33,13 @@ public ClientUdpOverTCPHandler(ServerConfig config, EventLoopGroup workerGroup) } @Override - protected Object convertToWrite(TernaryDatagramPacket msg) { + protected Object convertToWrite(DatagramPacketWrapper msg) { return msg.packet().content(); } @Override - protected Key getKey(TernaryDatagramPacket msg) { - return new Key(msg.packet().sender(), msg.third() /* recipient */); + protected Key getKey(DatagramPacketWrapper msg) { + return new Key(msg.packet().sender(), msg.proxy() /* recipient */); } @Override @@ -55,8 +52,7 @@ protected Channel newBindingChannel(Channel inboundChannel, Key key) { .handler(new ChannelInitializer<>() { @Override protected void initChannel(Channel ch) { - Socks5CommandRequest request = Socks5.toCommandRequest(Socks5CommandType.CONNECT, key.recipient); - ch.pipeline().addLast(new ClientAEADCodec(config.getCipher(), RequestCommand.UDP, request, config.getPassword())); + ch.pipeline().addLast(new ClientAEADCodec(config.getCipher(), RequestCommand.UDP, key.recipient, config.getPassword())); } }) .connect(serverAddress).addListener((ChannelFutureListener) future -> { @@ -94,7 +90,7 @@ private static class InboundHandler extends SimpleChannelInboundHandler public void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) { Channel inboundChannel = ctx.channel(); logger.info("[udp][vmess]{} ← {} ~ {} ← {}", sender, inboundChannel.localAddress(), inboundChannel.remoteAddress(), recipient); - channel.writeAndFlush(new TernaryDatagramPacket(new DatagramPacket(msg, recipient), sender)); + channel.writeAndFlush(new DatagramPacketWrapper(new DatagramPacket(msg, recipient), sender)); } } } diff --git a/urban-spork-common/src/com/urbanspork/common/codec/shadowsocks/Keys.java b/urban-spork-common/src/com/urbanspork/common/codec/shadowsocks/Keys.java index 751c0421..e4db398d 100644 --- a/urban-spork-common/src/com/urbanspork/common/codec/shadowsocks/Keys.java +++ b/urban-spork-common/src/com/urbanspork/common/codec/shadowsocks/Keys.java @@ -4,10 +4,6 @@ import com.urbanspork.common.protocol.shadowsocks.aead.AEAD; import com.urbanspork.common.protocol.shadowsocks.aead2022.AEAD2022; -import java.util.Base64; -import java.util.stream.Collectors; -import java.util.stream.Stream; - public record Keys(byte[] encKey, byte[][] identityKeys) { public static Keys from(CipherKind kind, String password) { int keySize = kind.keySize(); @@ -24,11 +20,4 @@ public static Keys from(CipherKind kind, String password) { } return keys; } - - @Override - public String toString() { - return String.format("EK:%s, IK:%s", - Base64.getEncoder().encodeToString(encKey), - Stream.of(identityKeys).map(Base64.getEncoder()::encodeToString).collect(Collectors.joining("|"))); - } } \ No newline at end of file diff --git a/urban-spork-common/src/com/urbanspork/common/codec/shadowsocks/tcp/AeadCipherCodec.java b/urban-spork-common/src/com/urbanspork/common/codec/shadowsocks/tcp/AeadCipherCodec.java index 5cbd7059..ef9c1478 100644 --- a/urban-spork-common/src/com/urbanspork/common/codec/shadowsocks/tcp/AeadCipherCodec.java +++ b/urban-spork-common/src/com/urbanspork/common/codec/shadowsocks/tcp/AeadCipherCodec.java @@ -138,7 +138,9 @@ private void initAEAD2022PayloadDecoder(Session session, ByteBuf in, List { +public class UdpRelayCodec extends MessageToMessageCodec { private static final Logger logger = LoggerFactory.getLogger(UdpRelayCodec.class); private final ServerConfig config; private final AeadCipherCodec cipher; @@ -39,8 +39,8 @@ public UdpRelayCodec(ServerConfig config, Mode mode) { } @Override - protected void encode(ChannelHandlerContext ctx, TernaryDatagramPacket msg, List out) throws Exception { - InetSocketAddress proxy = msg.third(); + protected void encode(ChannelHandlerContext ctx, DatagramPacketWrapper msg, List out) throws Exception { + InetSocketAddress proxy = msg.proxy(); if (proxy == null) { throw new EncoderException("Relay address is null"); } @@ -69,7 +69,7 @@ protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List { +public class DatagramPacketEncoder extends MessageToMessageEncoder { @Override - protected void encode(ChannelHandlerContext ctx, TernaryDatagramPacket msg, List out) { + protected void encode(ChannelHandlerContext ctx, DatagramPacketWrapper msg, List out) { ByteBuf buffer = ctx.alloc().buffer(); buffer.writeBytes(new byte[]{0, 0, 0/* fragment */}); DatagramPacket data = msg.packet(); Address.encode(data.recipient(), buffer); buffer.writeBytes(data.content()); - out.add(new DatagramPacket(buffer, msg.third(), data.sender())); + out.add(new DatagramPacket(buffer, msg.proxy(), data.sender())); } } diff --git a/urban-spork-common/src/com/urbanspork/common/crypto/AES.java b/urban-spork-common/src/com/urbanspork/common/crypto/AES.java index a5f3e499..cff30c42 100644 --- a/urban-spork-common/src/com/urbanspork/common/crypto/AES.java +++ b/urban-spork-common/src/com/urbanspork/common/crypto/AES.java @@ -4,36 +4,33 @@ import org.bouncycastle.crypto.engines.AESEngine; import org.bouncycastle.crypto.params.KeyParameter; -public enum AES { +public class AES { + private AES() {} - INSTANCE(AESEngine.newInstance()); - - private final MultiBlockCipher cipher; - - AES(MultiBlockCipher cipher) { - this.cipher = cipher; - } - - public byte[] encrypt(byte[] key, byte[] in) { + public static byte[] encrypt(byte[] key, byte[] in) { + MultiBlockCipher cipher = AESEngine.newInstance(); byte[] out = new byte[cipher.getBlockSize()]; cipher.init(true, new KeyParameter(key)); cipher.processBlock(in, 0, out, 0); return out; } - public void encrypt(byte[] key, byte[] in, byte[] out) { + public static void encrypt(byte[] key, byte[] in, byte[] out) { + MultiBlockCipher cipher = AESEngine.newInstance(); cipher.init(true, new KeyParameter(key)); cipher.processBlock(in, 0, out, 0); } - public byte[] decrypt(byte[] key, byte[] in) { + public static byte[] decrypt(byte[] key, byte[] in) { + MultiBlockCipher cipher = AESEngine.newInstance(); byte[] out = new byte[cipher.getBlockSize()]; cipher.init(false, new KeyParameter(key)); cipher.processBlock(in, 0, out, 0); return out; } - public void decrypt(byte[] key, byte[] in, byte[] out) { + public static void decrypt(byte[] key, byte[] in, byte[] out) { + MultiBlockCipher cipher = AESEngine.newInstance(); cipher.init(false, new KeyParameter(key)); cipher.processBlock(in, 0, out, 0); } diff --git a/urban-spork-common/src/com/urbanspork/common/protocol/shadowsocks/aead2022/AEAD2022.java b/urban-spork-common/src/com/urbanspork/common/protocol/shadowsocks/aead2022/AEAD2022.java index bff1eff0..defb7bf0 100644 --- a/urban-spork-common/src/com/urbanspork/common/protocol/shadowsocks/aead2022/AEAD2022.java +++ b/urban-spork-common/src/com/urbanspork/common/protocol/shadowsocks/aead2022/AEAD2022.java @@ -143,7 +143,7 @@ static PayloadDecoder newPayloadDecoder(CipherMethod cipherMethod, byte[] key, b static PayloadDecoder newPayloadDecoder(CipherMethod cipherMethod, Identity identity, ServerUserManager userManager, byte[] key, byte[] salt, byte[] eih) { byte[] identitySubKey = deriveKey("shadowsocks 2022 identity subkey".getBytes(), concat(key, salt)); - byte[] userHash = AES.INSTANCE.decrypt(identitySubKey, eih); + byte[] userHash = AES.decrypt(identitySubKey, eih); if (logger.isTraceEnabled()) { logger.trace("server EIH {}, hash: {}", ByteString.valueOf(eih), ByteString.valueOf(userHash)); } @@ -180,7 +180,7 @@ static void withEih(Keys keys, byte[] salt, ByteBuf out) { private static void withEih(byte[] subKey, byte[] iPSK, ByteBuf out) { byte[] iPSKHash = Digests.blake3.hash(iPSK); byte[] iPSKPlainText = Arrays.copyOf(iPSKHash, 16); - byte[] iPSKPEncryptText = AES.INSTANCE.encrypt(subKey, iPSKPlainText); + byte[] iPSKPEncryptText = AES.encrypt(subKey, iPSKPlainText); if (logger.isTraceEnabled()) { logger.trace("client EIH:{}, hash:{}", ByteString.valueOf(iPSKPEncryptText), ByteString.valueOf(iPSKPlainText)); } @@ -239,7 +239,7 @@ static void encodePacket(UdpCipher cipher, byte[] iPSK, int eihLength, ByteBuf i byte[] header = new byte[16]; in.readBytes(header); byte[] nonce = Arrays.copyOfRange(header, 4, 16); - AES.INSTANCE.encrypt(iPSK, header, header); + AES.encrypt(iPSK, header, header); out.writeBytes(header); if (eihLength > 0) { in.readBytes(out, eihLength); @@ -252,7 +252,7 @@ static void encodePacket(UdpCipher cipher, byte[] iPSK, int eihLength, ByteBuf i static ByteBuf decodePacket(CipherKind kind, CipherMethod method, Control control, ServerUserManager userManager, byte[] key, ByteBuf in) throws InvalidCipherTextException { byte[] header = new byte[16]; in.readBytes(header); - AES.INSTANCE.decrypt(key, header, header); + AES.decrypt(key, header, header); ByteBuf headerBuffer = Unpooled.wrappedBuffer(header); long sessionId = headerBuffer.getLong(0); UdpCipher cipher; @@ -263,7 +263,7 @@ static ByteBuf decodePacket(CipherKind kind, CipherMethod method, Control contro if (logger.isTraceEnabled()) { logger.trace("server EIH {}, session_id_packet_id: {},{}", ByteString.valueOf(eih), sessionId, headerBuffer.getLong(Long.BYTES)); } - AES.INSTANCE.decrypt(key, eih, eih); + AES.decrypt(key, eih, eih); for (int i = 0; i < eih.length; i++) { eih[i] ^= header[i]; } @@ -312,7 +312,7 @@ private static void withEih(byte[] iPSK, byte[] iPSKn, byte[] sessionIdPacketId, for (int i = 0; i < 16; i++) { identityHeader[i] ^= sessionIdPacketId[i]; } - AES.INSTANCE.encrypt(iPSK, identityHeader, identityHeader); + AES.encrypt(iPSK, identityHeader, identityHeader); if (logger.isTraceEnabled()) { logger.trace("client EIH:{}, hash:{}", ByteString.valueOf(identityHeader), ByteString.valueOf(iPSKnHashPlainText)); } diff --git a/urban-spork-common/src/com/urbanspork/common/protocol/vmess/Address.java b/urban-spork-common/src/com/urbanspork/common/protocol/vmess/Address.java index 4cef730d..c12708d9 100644 --- a/urban-spork-common/src/com/urbanspork/common/protocol/vmess/Address.java +++ b/urban-spork-common/src/com/urbanspork/common/protocol/vmess/Address.java @@ -3,62 +3,58 @@ import com.urbanspork.common.protocol.vmess.header.AddressType; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.EncoderException; -import io.netty.handler.codec.socksx.v5.DefaultSocks5CommandRequest; -import io.netty.handler.codec.socksx.v5.Socks5AddressType; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequest; -import io.netty.handler.codec.socksx.v5.Socks5CommandType; -import io.netty.util.NetUtil; -import java.nio.charset.Charset; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; public interface Address { - static void writeAddressPort(ByteBuf buf, Socks5CommandRequest address) { - String dstAddr = address.dstAddr(); - if (dstAddr.isEmpty()) { + static void writeAddressPort(ByteBuf buf, InetSocketAddress address) { + if (address == null) { throw new EncoderException("Empty destination address"); } - buf.writeShort(address.dstPort()); - Socks5AddressType addressType = address.dstAddrType(); - if (Socks5AddressType.IPv4.equals(addressType)) { + buf.writeShort(address.getPort()); + InetAddress ip = address.getAddress(); + if (ip instanceof Inet4Address ipv4) { buf.writeByte(AddressType.IPV4.getValue()); - buf.writeBytes(NetUtil.createByteArrayFromIpAddressString(dstAddr)); - } else if (Socks5AddressType.IPv6.equals(addressType)) { + buf.writeBytes(ipv4.getAddress()); + } else if (ip instanceof Inet6Address ipv6) { buf.writeByte(AddressType.IPV6.getValue()); - buf.writeBytes(NetUtil.createByteArrayFromIpAddressString(dstAddr)); - } else if (Socks5AddressType.DOMAIN.equals(addressType)) { // port[2] + type[1] + domain_len[1] + domain_bytes[n] - byte[] domain = dstAddr.getBytes(); + buf.writeBytes(ipv6.getAddress()); + } else { // port[2] + type[1] + domain_len[1] + domain_bytes[n] + byte[] domain = address.getHostString().getBytes(); buf.writeByte(AddressType.DOMAIN.getValue()); buf.writeByte(domain.length); buf.writeBytes(domain); - } else { - throw new EncoderException("Unsupported addressType: " + (addressType.byteValue() & 0xFF)); } } - static Socks5CommandRequest readAddressPort(ByteBuf buf) { + static InetSocketAddress readAddressPort(ByteBuf buf) { int port = buf.readUnsignedShort(); - String hostname; - Socks5AddressType type; - switch (AddressType.valueOf(buf.readByte())) { - case IPV4 -> { - type = Socks5AddressType.IPv4; - byte[] bytes = new byte[4]; - buf.readBytes(bytes); - hostname = NetUtil.bytesToIpAddress(bytes); + AddressType addressType = AddressType.valueOf(buf.readByte()); + if (AddressType.IPV4 == addressType) { + byte[] bytes = new byte[4]; + buf.readBytes(bytes); + try { + return new InetSocketAddress(InetAddress.getByAddress(bytes), port); + } catch (UnknownHostException ignore) { + // should never be caught } - case IPV6 -> { - type = Socks5AddressType.IPv6; - byte[] bytes = new byte[16]; - buf.readBytes(bytes); - hostname = NetUtil.bytesToIpAddress(bytes); - } - default -> { - type = Socks5AddressType.DOMAIN; - int length = buf.readByte(); - hostname = buf.readCharSequence(length, Charset.defaultCharset()).toString(); + } else if (AddressType.IPV6 == addressType) { + byte[] bytes = new byte[16]; + buf.readBytes(bytes); + try { + return new InetSocketAddress(InetAddress.getByAddress(bytes), port); + } catch (UnknownHostException ignore) { + // should never be caught } } - return new DefaultSocks5CommandRequest(Socks5CommandType.CONNECT, type, hostname, port); + int length = buf.readByte(); + String hostname = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); + return InetSocketAddress.createUnresolved(hostname, port); } } diff --git a/urban-spork-common/src/com/urbanspork/common/protocol/vmess/aead/AuthID.java b/urban-spork-common/src/com/urbanspork/common/protocol/vmess/aead/AuthID.java index 6abedc4b..366f2d13 100644 --- a/urban-spork-common/src/com/urbanspork/common/protocol/vmess/aead/AuthID.java +++ b/urban-spork-common/src/com/urbanspork/common/protocol/vmess/aead/AuthID.java @@ -20,14 +20,14 @@ public static byte[] createAuthID(byte[] key, long time) { buf.writeBytes(Go.nextUnsignedInt()); long crc32 = VMess.crc32(ByteBufUtil.getBytes(buf, buf.readerIndex(), buf.writerIndex(), false)); buf.writeInt((int) crc32); - return AES.INSTANCE.encrypt(KDF.kdf16(key, KDF_SALT_AUTH_ID_ENCRYPTION_KEY), authID); + return AES.encrypt(KDF.kdf16(key, KDF_SALT_AUTH_ID_ENCRYPTION_KEY), authID); } public static byte[] match(byte[] authID, byte[][] keys) { for (byte[] key : keys) { byte[] bytes; try { - bytes = AES.INSTANCE.decrypt(KDF.kdf16(key, KDF_SALT_AUTH_ID_ENCRYPTION_KEY), authID); + bytes = AES.decrypt(KDF.kdf16(key, KDF_SALT_AUTH_ID_ENCRYPTION_KEY), authID); } catch (Exception ignore) { continue; } diff --git a/urban-spork-common/src/com/urbanspork/common/protocol/vmess/header/RequestHeader.java b/urban-spork-common/src/com/urbanspork/common/protocol/vmess/header/RequestHeader.java index d12e1cca..1b5a783e 100644 --- a/urban-spork-common/src/com/urbanspork/common/protocol/vmess/header/RequestHeader.java +++ b/urban-spork-common/src/com/urbanspork/common/protocol/vmess/header/RequestHeader.java @@ -2,10 +2,11 @@ import com.urbanspork.common.protocol.vmess.ID; import com.urbanspork.common.protocol.vmess.VMess; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequest; -public record RequestHeader(byte version, RequestCommand command, RequestOption[] option, SecurityType security, Socks5CommandRequest address, byte[] id) { - public static RequestHeader defaultHeader(SecurityType security, RequestCommand command, Socks5CommandRequest address, String uuid) { +import java.net.InetSocketAddress; + +public record RequestHeader(byte version, RequestCommand command, RequestOption[] option, SecurityType security, InetSocketAddress address, byte[] id) { + public static RequestHeader defaultHeader(SecurityType security, RequestCommand command, InetSocketAddress address, String uuid) { return new RequestHeader(VMess.VERSION, command, new RequestOption[]{RequestOption.ChunkStream, RequestOption.ChunkMasking, RequestOption.GlobalPadding, RequestOption.AuthenticatedLength}, security, address, ID.newID(uuid)); } diff --git a/urban-spork-common/src/com/urbanspork/common/transport/udp/TernaryDatagramPacket.java b/urban-spork-common/src/com/urbanspork/common/transport/udp/DatagramPacketWrapper.java similarity index 74% rename from urban-spork-common/src/com/urbanspork/common/transport/udp/TernaryDatagramPacket.java rename to urban-spork-common/src/com/urbanspork/common/transport/udp/DatagramPacketWrapper.java index 7e9d354f..a409e1e3 100644 --- a/urban-spork-common/src/com/urbanspork/common/transport/udp/TernaryDatagramPacket.java +++ b/urban-spork-common/src/com/urbanspork/common/transport/udp/DatagramPacketWrapper.java @@ -4,13 +4,13 @@ import java.net.InetSocketAddress; -public record TernaryDatagramPacket(DatagramPacket packet, InetSocketAddress third) { +public record DatagramPacketWrapper(DatagramPacket packet, InetSocketAddress proxy) { @Override public String toString() { if (packet.sender() != null) { - return String.format("%s → %s ~ %s", packet.sender(), packet.recipient(), third); + return String.format("%s → %s ~ %s", packet.sender(), packet.recipient(), proxy); } else { - return String.format("→ %s ~ %s", packet.recipient(), third); + return String.format("→ %s ~ %s", packet.recipient(), proxy); } } } diff --git a/urban-spork-common/src/com/urbanspork/common/util/LruCache.java b/urban-spork-common/src/com/urbanspork/common/util/LruCache.java index d8e70e08..0db26af3 100644 --- a/urban-spork-common/src/com/urbanspork/common/util/LruCache.java +++ b/urban-spork-common/src/com/urbanspork/common/util/LruCache.java @@ -2,8 +2,6 @@ import io.netty.util.HashedWheelTimer; import io.netty.util.Timeout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.time.Duration; import java.util.LinkedHashMap; @@ -13,7 +11,6 @@ import java.util.function.Function; public class LruCache { - private static final Logger logger = LoggerFactory.getLogger(LruCache.class); final long capacity; final Duration timeToLive; final HashedWheelTimer timer = new HashedWheelTimer(1, TimeUnit.SECONDS); @@ -24,7 +21,6 @@ protected boolean removeEldestEntry(Map.Entry> eldest) { boolean flag = size() > capacity; if (flag) { K key = eldest.getKey(); - logger.trace("remove eldest {}", key); Pair pair = eldest.getValue(); afterExpired.accept(key, pair.value); pair.timeout.cancel(); @@ -51,16 +47,14 @@ public V computeIfAbsent(K key, Function mappingFunction } public void insert(K key, V value) { - logger.trace("insert {}={}", key, value); - inner.put(key, new Pair<>(value, timer.newTimeout(timeout -> expire(key, value), TimeUnit.NANOSECONDS.convert(timeToLive), TimeUnit.NANOSECONDS))); + inner.put(key, new Pair<>(value, timer.newTimeout(timeout -> expire(key, value), timeToLive.toNanos(), TimeUnit.NANOSECONDS))); } public V get(K key) { Pair pair = inner.get(key); if (pair != null) { pair.timeout.cancel(); - pair.timeout = timer.newTimeout(timeout -> expire(key, pair.value), TimeUnit.NANOSECONDS.convert(timeToLive), TimeUnit.NANOSECONDS); - logger.trace("keep alive {}={}", key, pair.value); + pair.timeout = timer.newTimeout(timeout -> expire(key, pair.value), timeToLive.toNanos(), TimeUnit.NANOSECONDS); return pair.value; } else { return null; @@ -68,7 +62,6 @@ public V get(K key) { } public V remove(K key) { - logger.trace("remove {}", key); Pair pair = inner.remove(key); if (pair != null) { pair.timeout.cancel(); @@ -78,8 +71,7 @@ public V remove(K key) { } } - public void clear() { - logger.trace("clear"); + public void release() { for (Map.Entry> entry : inner.entrySet()) { afterExpired.accept(entry.getKey(), entry.getValue().value); } @@ -88,7 +80,6 @@ public void clear() { } private void expire(K key, V value) { - logger.trace("expire {}={}", key, value); afterExpired.accept(key, value); inner.remove(key); } diff --git a/urban-spork-server/src/com/urbanspork/server/RemoteConnectHandler.java b/urban-spork-server/src/com/urbanspork/server/RemoteConnectHandler.java index 8f630723..e3a021bf 100644 --- a/urban-spork-server/src/com/urbanspork/server/RemoteConnectHandler.java +++ b/urban-spork-server/src/com/urbanspork/server/RemoteConnectHandler.java @@ -11,7 +11,6 @@ import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelOption; import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequest; import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.Promise; import org.slf4j.Logger; @@ -39,9 +38,9 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { } private void udp(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof Socks5CommandRequest request) { + if (msg instanceof InetSocketAddress address) { ctx.pipeline().addLast( - new ServerUDPOverTCPCodec(request), + new ServerUDPOverTCPCodec(address), new ServerUDPRelayHandler(config.getPacketEncoding(), ctx.channel().eventLoop().parent().next()) ); } else { @@ -52,35 +51,37 @@ private void udp(ChannelHandlerContext ctx, Object msg) { private void tcp(ChannelHandlerContext ctx, Object msg) { Channel localChannel = ctx.channel(); if (msg instanceof InetSocketAddress address) { - connect(ctx, localChannel, new InetSocketAddress(address.getHostString(), address.getPort())); - } else if (msg instanceof Socks5CommandRequest request) { - connect(ctx, localChannel, new InetSocketAddress(request.dstAddr(), request.dstPort())); + p = ctx.executor().newPromise(); + connect(localChannel, address, p); } else { p.addListener((FutureListener) future -> { - Channel outboundChannel = future.get(); - localChannel.pipeline().addLast(new DefaultChannelInboundHandler(outboundChannel)); + if (future.isSuccess()) { + Channel remoteChannel = future.get(); + localChannel.pipeline().addLast(new DefaultChannelInboundHandler(remoteChannel)); if (!ctx.isRemoved()) { localChannel.pipeline().remove(RemoteConnectHandler.this); } - outboundChannel.writeAndFlush(msg); + remoteChannel.writeAndFlush(msg); + } else { + ctx.close(); } - ); + }); } } - private void connect(ChannelHandlerContext ctx, Channel localChannel, InetSocketAddress remoteAddress) { - p = ctx.executor().newPromise(); + private void connect(Channel localChannel, InetSocketAddress remoteAddress, Promise promise) { new Bootstrap().group(localChannel.eventLoop()) .channel(NioSocketChannel.class) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000) .handler(new DefaultChannelInboundHandler(localChannel)) .connect(remoteAddress).addListener((ChannelFutureListener) future -> { if (future.isSuccess()) { - logger.info("[tcp][{}][{}→{}]", config.getProtocol(), localChannel.localAddress(), remoteAddress); - p.setSuccess(future.channel()); + Channel remoteChannel = future.channel(); + logger.info("[tcp][{}][{}→{}]", config.getProtocol(), localChannel.localAddress(), remoteChannel.remoteAddress()); + promise.setSuccess(remoteChannel); } else { logger.error("[tcp][{}][{}→{}]", config.getProtocol(), localChannel.localAddress(), remoteAddress); - ctx.close(); + promise.setFailure(future.cause()); } }); } diff --git a/urban-spork-server/src/com/urbanspork/server/Server.java b/urban-spork-server/src/com/urbanspork/server/Server.java index ef3427a2..4bede1a9 100644 --- a/urban-spork-server/src/com/urbanspork/server/Server.java +++ b/urban-spork-server/src/com/urbanspork/server/Server.java @@ -2,6 +2,7 @@ import com.urbanspork.common.channel.ExceptionHandler; import com.urbanspork.common.codec.shadowsocks.Mode; +import com.urbanspork.common.codec.shadowsocks.tcp.Context; import com.urbanspork.common.codec.shadowsocks.udp.UdpRelayCodec; import com.urbanspork.common.config.ConfigHandler; import com.urbanspork.common.config.ServerConfig; @@ -87,10 +88,18 @@ private static Instance startup(EventLoopGroup bossGroup, EventLoopGroup workerG user.stream().map(ServerUser::from).forEach(ServerUserManager.DEFAULT::addUser); } } - ServerSocketChannel tcp = (ServerSocketChannel) new ServerBootstrap().group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .childHandler(new ServerInitializer(config)) - .bind(config.getPort()).sync().addListener(future -> logger.info("Startup tcp server => {}", config)).channel(); + Context context = Context.checkReplay(); + ServerSocketChannel tcp; + try { + tcp = (ServerSocketChannel) new ServerBootstrap().group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new ServerInitializer(config, context)) + .bind(config.getPort()).sync().addListener(future -> logger.info("Startup tcp server => {}", config)).channel() + .closeFuture().addListener(future -> context.release()).channel(); + } catch (Exception e) { + context.release(); + throw e; + } config.setPort(tcp.localAddress().getPort()); Optional udp = startupUdp(bossGroup, workerGroup, config); return new Instance(tcp, udp); diff --git a/urban-spork-server/src/com/urbanspork/server/ServerInitializer.java b/urban-spork-server/src/com/urbanspork/server/ServerInitializer.java index 49037aa1..d50471e6 100644 --- a/urban-spork-server/src/com/urbanspork/server/ServerInitializer.java +++ b/urban-spork-server/src/com/urbanspork/server/ServerInitializer.java @@ -14,10 +14,11 @@ public class ServerInitializer extends ChannelInitializer { private final ServerConfig config; - private final Context context = Context.checkReplay(); + private final Context context; - public ServerInitializer(ServerConfig config) { + public ServerInitializer(ServerConfig config, Context context) { this.config = config; + this.context = context; } @Override diff --git a/urban-spork-server/src/com/urbanspork/server/ServerUDPOverTCPCodec.java b/urban-spork-server/src/com/urbanspork/server/ServerUDPOverTCPCodec.java index 3dd634e5..8a40d355 100644 --- a/urban-spork-server/src/com/urbanspork/server/ServerUDPOverTCPCodec.java +++ b/urban-spork-server/src/com/urbanspork/server/ServerUDPOverTCPCodec.java @@ -1,31 +1,30 @@ package com.urbanspork.server; -import com.urbanspork.common.transport.udp.TernaryDatagramPacket; +import com.urbanspork.common.transport.udp.DatagramPacketWrapper; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.socket.DatagramPacket; import io.netty.handler.codec.MessageToMessageCodec; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequest; import java.net.InetSocketAddress; import java.util.List; -class ServerUDPOverTCPCodec extends MessageToMessageCodec { +class ServerUDPOverTCPCodec extends MessageToMessageCodec { - private final Socks5CommandRequest request; + private final InetSocketAddress address; - ServerUDPOverTCPCodec(Socks5CommandRequest request) { - this.request = request; + ServerUDPOverTCPCodec(InetSocketAddress address) { + this.address = address; } @Override - protected void encode(ChannelHandlerContext ctx, TernaryDatagramPacket msg, List out) { + protected void encode(ChannelHandlerContext ctx, DatagramPacketWrapper msg, List out) { out.add(msg.packet().content()); } @Override protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) { InetSocketAddress sender = (InetSocketAddress) ctx.channel().remoteAddress(); - out.add(new DatagramPacket(msg.retainedDuplicate(), new InetSocketAddress(request.dstAddr(), request.dstPort()), sender)); + out.add(new DatagramPacket(msg.retainedDuplicate(), address, sender)); } } diff --git a/urban-spork-server/src/com/urbanspork/server/ServerUDPRelayHandler.java b/urban-spork-server/src/com/urbanspork/server/ServerUDPRelayHandler.java index b301592e..0bea3610 100644 --- a/urban-spork-server/src/com/urbanspork/server/ServerUDPRelayHandler.java +++ b/urban-spork-server/src/com/urbanspork/server/ServerUDPRelayHandler.java @@ -1,7 +1,7 @@ package com.urbanspork.server; +import com.urbanspork.common.transport.udp.DatagramPacketWrapper; import com.urbanspork.common.transport.udp.PacketEncoding; -import com.urbanspork.common.transport.udp.TernaryDatagramPacket; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; @@ -103,7 +103,7 @@ protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List {}", msg.sender()); } diff --git a/urban-spork-server/src/com/urbanspork/server/vmess/ServerAEADCodec.java b/urban-spork-server/src/com/urbanspork/server/vmess/ServerAEADCodec.java index 636851cb..668a076a 100644 --- a/urban-spork-server/src/com/urbanspork/server/vmess/ServerAEADCodec.java +++ b/urban-spork-server/src/com/urbanspork/server/vmess/ServerAEADCodec.java @@ -24,9 +24,9 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageCodec; import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequest; import org.bouncycastle.crypto.InvalidCipherTextException; +import java.net.InetSocketAddress; import java.util.Arrays; import java.util.List; @@ -103,7 +103,7 @@ public void decode(ChannelHandlerContext ctx, ByteBuf in, List out) thro SecurityType security = SecurityType.valueOf((byte) (b35 & 0x0F)); decrypted.skipBytes(1); // fixed 0 RequestCommand command = new RequestCommand(decrypted.readByte()); // command - Socks5CommandRequest address = null; + InetSocketAddress address = null; if (RequestCommand.TCP.equals(command) || RequestCommand.UDP.equals(command)) { address = Address.readAddressPort(decrypted); if (RequestCommand.UDP.equals(command)) { diff --git a/urban-spork-test/src/com/urbanspork/test/client/Socks5UDPTestClient.java b/urban-spork-test/src/com/urbanspork/test/client/Socks5UDPTestClient.java index 108e1658..5c5251dd 100644 --- a/urban-spork-test/src/com/urbanspork/test/client/Socks5UDPTestClient.java +++ b/urban-spork-test/src/com/urbanspork/test/client/Socks5UDPTestClient.java @@ -5,7 +5,7 @@ import com.urbanspork.common.config.ClientConfig; import com.urbanspork.common.config.ConfigHandler; import com.urbanspork.common.protocol.socks.ClientHandshake; -import com.urbanspork.common.transport.udp.TernaryDatagramPacket; +import com.urbanspork.common.transport.udp.DatagramPacketWrapper; import com.urbanspork.test.server.udp.DelayedEchoTestServer; import com.urbanspork.test.server.udp.SimpleEchoTestServer; import io.netty.bootstrap.Bootstrap; @@ -63,11 +63,11 @@ protected void initChannel(Channel ch) { ch.pipeline().addLast( new DatagramPacketEncoder(), new DatagramPacketDecoder(), - new SimpleChannelInboundHandler(false) { + new SimpleChannelInboundHandler(false) { @Override - protected void channelRead0(ChannelHandlerContext ctx, TernaryDatagramPacket msg) { + protected void channelRead0(ChannelHandlerContext ctx, DatagramPacketWrapper msg) { ByteBuf content = msg.packet().content(); - InetSocketAddress dst = msg.third(); + InetSocketAddress dst = msg.proxy(); logger.info("Receive msg {} - {}", dst, content.readCharSequence(content.readableBytes(), StandardCharsets.UTF_8)); if (!dstAddress1.equals(dst) && !dstAddress2.equals(dst)) { logger.error("Destination address is unexpected."); @@ -97,7 +97,7 @@ protected void channelRead0(ChannelHandlerContext ctx, TernaryDatagramPacket msg private static void sendMsg(Channel channel, InetSocketAddress dstAddress, InetSocketAddress socksAddress, byte[] bytes) { DatagramPacket data = new DatagramPacket(Unpooled.copiedBuffer(bytes), dstAddress); - TernaryDatagramPacket msg = new TernaryDatagramPacket(data, socksAddress); + DatagramPacketWrapper msg = new DatagramPacketWrapper(data, socksAddress); logger.info("Send msg {}", msg); channel.writeAndFlush(msg); } diff --git a/urban-spork-test/test/com/urbanspork/client/TestClientUdpRelayHandler.java b/urban-spork-test/test/com/urbanspork/client/TestClientUdpRelayHandler.java index b73db97d..1737f597 100644 --- a/urban-spork-test/test/com/urbanspork/client/TestClientUdpRelayHandler.java +++ b/urban-spork-test/test/com/urbanspork/client/TestClientUdpRelayHandler.java @@ -2,7 +2,7 @@ import com.urbanspork.common.config.ServerConfig; import com.urbanspork.common.config.ServerConfigTestCase; -import com.urbanspork.common.transport.udp.TernaryDatagramPacket; +import com.urbanspork.common.transport.udp.DatagramPacketWrapper; import com.urbanspork.test.TestDice; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; @@ -28,7 +28,7 @@ void test() { ServerConfig config = ServerConfigTestCase.testConfig(0); inbound.pipeline().addLast(new TestClientUdpRelayHandler(config, outbound)); InetSocketAddress recipient = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); - TernaryDatagramPacket msg = new TernaryDatagramPacket(new DatagramPacket(Unpooled.wrappedBuffer(TestDice.rollString(10).getBytes()), recipient), recipient); + DatagramPacketWrapper msg = new DatagramPacketWrapper(new DatagramPacket(Unpooled.wrappedBuffer(TestDice.rollString(10).getBytes()), recipient), recipient); Assertions.assertFalse(inbound.writeInbound(msg)); LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(3)); Assertions.assertFalse(inbound.writeInbound(msg)); @@ -45,12 +45,12 @@ protected TestClientUdpRelayHandler(ServerConfig config, Channel outbound) { } @Override - protected Object convertToWrite(TernaryDatagramPacket msg) { + protected Object convertToWrite(DatagramPacketWrapper msg) { return msg; } @Override - protected String getKey(TernaryDatagramPacket msg) { + protected String getKey(DatagramPacketWrapper msg) { return msg.packet().content().toString(StandardCharsets.US_ASCII); } diff --git a/urban-spork-test/test/com/urbanspork/client/vmess/ClientAEADCodecTestCase.java b/urban-spork-test/test/com/urbanspork/client/vmess/ClientAEADCodecTestCase.java index 1fe85528..f7d59433 100644 --- a/urban-spork-test/test/com/urbanspork/client/vmess/ClientAEADCodecTestCase.java +++ b/urban-spork-test/test/com/urbanspork/client/vmess/ClientAEADCodecTestCase.java @@ -13,14 +13,11 @@ import io.netty.buffer.Unpooled; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.socksx.v5.DefaultSocks5CommandRequest; -import io.netty.handler.codec.socksx.v5.Socks5AddressType; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequest; -import io.netty.handler.codec.socksx.v5.Socks5CommandType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ThreadLocalRandom; @@ -31,7 +28,7 @@ public class ClientAEADCodecTestCase { @Test void testDecodeDifferentSession() throws Exception { String uuid = java.util.UUID.randomUUID().toString(); - Socks5CommandRequest address = new DefaultSocks5CommandRequest(Socks5CommandType.CONNECT, Socks5AddressType.DOMAIN, "www.urban-spork.com", TestDice.rollPort()); + InetSocketAddress address = InetSocketAddress.createUnresolved("www.urban-spork.com", TestDice.rollPort()); RequestHeader header = RequestHeader.defaultHeader(SecurityType.AES128_GCM, RequestCommand.TCP, address, uuid); ClientSession session1 = new ClientSession(); ClientSession session2 = SessionTestCase.another(session1); diff --git a/urban-spork-test/test/com/urbanspork/client/vmess/ClientUDPOverTCPHandlerTestCase.java b/urban-spork-test/test/com/urbanspork/client/vmess/ClientUDPOverTCPHandlerTestCase.java index c9ba973c..d3be51c6 100644 --- a/urban-spork-test/test/com/urbanspork/client/vmess/ClientUDPOverTCPHandlerTestCase.java +++ b/urban-spork-test/test/com/urbanspork/client/vmess/ClientUDPOverTCPHandlerTestCase.java @@ -4,7 +4,7 @@ import com.urbanspork.common.config.ServerConfig; import com.urbanspork.common.config.ServerConfigTestCase; import com.urbanspork.common.protocol.Protocol; -import com.urbanspork.common.transport.udp.TernaryDatagramPacket; +import com.urbanspork.common.transport.udp.DatagramPacketWrapper; import com.urbanspork.test.TestDice; import io.netty.buffer.Unpooled; import io.netty.channel.EventLoopGroup; @@ -27,7 +27,7 @@ void testConnectFailed() { config.setPassword(TestDice.rollPassword(Protocol.vmess, CipherKind.chacha20_poly1305)); EventLoopGroup executor = new NioEventLoopGroup(); EmbeddedChannel channel = new EmbeddedChannel(new ClientUdpOverTCPHandler(config, executor)); - TernaryDatagramPacket packet = new TernaryDatagramPacket(new DatagramPacket(Unpooled.EMPTY_BUFFER, new InetSocketAddress(TestDice.rollPort())), new InetSocketAddress(0)); + DatagramPacketWrapper packet = new DatagramPacketWrapper(new DatagramPacket(Unpooled.EMPTY_BUFFER, new InetSocketAddress(TestDice.rollPort())), new InetSocketAddress(0)); Assertions.assertThrows(ConnectException.class, () -> channel.writeInbound(packet)); channel.close(); } diff --git a/urban-spork-test/test/com/urbanspork/common/codec/shadowsocks/EmbeddedChannelTestCase.java b/urban-spork-test/test/com/urbanspork/common/codec/shadowsocks/EmbeddedChannelTestCase.java index 70e44696..3685a5e8 100644 --- a/urban-spork-test/test/com/urbanspork/common/codec/shadowsocks/EmbeddedChannelTestCase.java +++ b/urban-spork-test/test/com/urbanspork/common/codec/shadowsocks/EmbeddedChannelTestCase.java @@ -6,7 +6,7 @@ import com.urbanspork.common.codec.shadowsocks.udp.UdpRelayCodec; import com.urbanspork.common.config.ServerConfig; import com.urbanspork.common.protocol.Protocol; -import com.urbanspork.common.transport.udp.TernaryDatagramPacket; +import com.urbanspork.common.transport.udp.DatagramPacketWrapper; import com.urbanspork.common.util.Dice; import com.urbanspork.test.TestDice; import io.netty.buffer.ByteBuf; @@ -74,9 +74,9 @@ void testUdpRelayChannel() { String host = "192.168.255.1"; String message = TestDice.rollString(); InetSocketAddress dst = new InetSocketAddress(host, port); - TernaryDatagramPacket noRelayPacket = new TernaryDatagramPacket(new DatagramPacket(Unpooled.wrappedBuffer(message.getBytes()), dst), null); + DatagramPacketWrapper noRelayPacket = new DatagramPacketWrapper(new DatagramPacket(Unpooled.wrappedBuffer(message.getBytes()), dst), null); Assertions.assertThrows(EncoderException.class, () -> client.writeOutbound(noRelayPacket)); - client.writeOutbound(new TernaryDatagramPacket(new DatagramPacket(Unpooled.wrappedBuffer(message.getBytes()), dst), relay)); + client.writeOutbound(new DatagramPacketWrapper(new DatagramPacket(Unpooled.wrappedBuffer(message.getBytes()), dst), relay)); DatagramPacket out = client.readOutbound(); server.writeInbound(out); DatagramPacket in = server.readInbound(); @@ -101,7 +101,7 @@ void testAead2022UdpAntiReplay() { InetSocketAddress recipient = new InetSocketAddress(InetAddress.getLoopbackAddress(), 16802); InetSocketAddress porxy = new InetSocketAddress(InetAddress.getLoopbackAddress(), 16803); DatagramPacket packet = new DatagramPacket(Unpooled.wrappedBuffer(Dice.rollBytes(10)), recipient, sender); - client.writeOutbound(new TernaryDatagramPacket(packet, porxy)); + client.writeOutbound(new DatagramPacketWrapper(packet, porxy)); DatagramPacket outbound = client.readOutbound(); server.writeInbound(outbound.copy()); Assertions.assertNotNull(server.readInbound()); diff --git a/urban-spork-test/test/com/urbanspork/common/crypto/AES128TestCase.java b/urban-spork-test/test/com/urbanspork/common/crypto/AES128TestCase.java index be716c6b..fe95aee8 100644 --- a/urban-spork-test/test/com/urbanspork/common/crypto/AES128TestCase.java +++ b/urban-spork-test/test/com/urbanspork/common/crypto/AES128TestCase.java @@ -14,7 +14,7 @@ class AESTestCase { void testEncryptAndDecrypt() { byte[] key = Dice.rollBytes(16); String str = TestDice.rollString(16); - Assertions.assertEquals(str, new String(AES.INSTANCE.decrypt(key, AES.INSTANCE.encrypt(key, str.getBytes())))); + Assertions.assertEquals(str, new String(AES.decrypt(key, AES.encrypt(key, str.getBytes())))); } @Test @@ -22,15 +22,15 @@ void testEncryptAndDecryptInPlace() { byte[] key = Dice.rollBytes(16); String str = TestDice.rollString(16); byte[] in = str.getBytes(); - AES.INSTANCE.encrypt(key, in, in); + AES.encrypt(key, in, in); Assertions.assertNotEquals(str, new String(in)); - AES.INSTANCE.decrypt(key, in, in); + AES.decrypt(key, in, in); Assertions.assertEquals(str, new String(in)); } @Test void testAes128() { - byte[] decrypt = AES.INSTANCE.decrypt("4ylXkB2KedlvbLFy".getBytes(), Base64.getDecoder().decode("P1RKHzOxcv1GKRlbD5OZGA==")); + byte[] decrypt = AES.decrypt("4ylXkB2KedlvbLFy".getBytes(), Base64.getDecoder().decode("P1RKHzOxcv1GKRlbD5OZGA==")); Assertions.assertEquals("VqaYmC3G66ZuPB6J", new String(decrypt)); } } \ No newline at end of file diff --git a/urban-spork-test/test/com/urbanspork/common/protocol/socks/DatagramPacketTestCase.java b/urban-spork-test/test/com/urbanspork/common/protocol/socks/DatagramPacketTestCase.java index 6f9b1394..ca4281ad 100644 --- a/urban-spork-test/test/com/urbanspork/common/protocol/socks/DatagramPacketTestCase.java +++ b/urban-spork-test/test/com/urbanspork/common/protocol/socks/DatagramPacketTestCase.java @@ -2,7 +2,7 @@ import com.urbanspork.common.codec.socks.DatagramPacketDecoder; import com.urbanspork.common.codec.socks.DatagramPacketEncoder; -import com.urbanspork.common.transport.udp.TernaryDatagramPacket; +import com.urbanspork.common.transport.udp.DatagramPacketWrapper; import com.urbanspork.test.TestDice; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -30,7 +30,7 @@ void testCodec() { int socksPort = TestDice.rollPort(); InetSocketAddress dstAddress = new InetSocketAddress(dstPort); InetSocketAddress socksAddress = new InetSocketAddress(socksPort); - channel.writeOutbound(new TernaryDatagramPacket(new DatagramPacket(Unpooled.wrappedBuffer(str.getBytes()), dstAddress), socksAddress)); + channel.writeOutbound(new DatagramPacketWrapper(new DatagramPacket(Unpooled.wrappedBuffer(str.getBytes()), dstAddress), socksAddress)); DatagramPacket outbound = channel.readOutbound(); Assertions.assertEquals(socksAddress, outbound.recipient()); DatagramPacket outbound2 = outbound.replace(outbound.content().copy(0, 4)); @@ -38,8 +38,8 @@ void testCodec() { DatagramPacket outbound3 = outbound.replace(outbound.content().copy().setByte(2, 1)); Assertions.assertThrows(DecoderException.class, () -> channel.writeInbound(outbound3)); channel.writeInbound(outbound); - TernaryDatagramPacket inbound = channel.readInbound(); - Assertions.assertEquals(dstAddress, inbound.third()); + DatagramPacketWrapper inbound = channel.readInbound(); + Assertions.assertEquals(dstAddress, inbound.proxy()); Assertions.assertEquals(socksAddress, inbound.packet().recipient()); ByteBuf content = inbound.packet().content(); Assertions.assertEquals(str, content.readCharSequence(content.readableBytes(), StandardCharsets.UTF_8)); diff --git a/urban-spork-test/test/com/urbanspork/common/protocol/vmess/AddressTestCase.java b/urban-spork-test/test/com/urbanspork/common/protocol/vmess/AddressTestCase.java index 58b85b08..6b6c42e7 100644 --- a/urban-spork-test/test/com/urbanspork/common/protocol/vmess/AddressTestCase.java +++ b/urban-spork-test/test/com/urbanspork/common/protocol/vmess/AddressTestCase.java @@ -1,15 +1,10 @@ package com.urbanspork.common.protocol.vmess; import com.urbanspork.common.protocol.InetSocketAddressProvider; -import com.urbanspork.common.protocol.socks.Socks5; import com.urbanspork.test.TestDice; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.EncoderException; -import io.netty.handler.codec.socksx.v5.DefaultSocks5CommandRequest; -import io.netty.handler.codec.socksx.v5.Socks5AddressType; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequest; -import io.netty.handler.codec.socksx.v5.Socks5CommandType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -22,23 +17,19 @@ class AddressTestCase { @ParameterizedTest @ArgumentsSource(InetSocketAddressProvider.class) - void testAddressing(InetSocketAddress address) { - ByteBuf out = Unpooled.directBuffer(); - Address.writeAddressPort(out, Socks5.toCommandRequest(Socks5CommandType.CONNECT, address)); - Socks5CommandRequest request = Address.readAddressPort(out); - Assertions.assertEquals(address.getHostString(), request.dstAddr()); - Assertions.assertEquals(address.getPort(), request.dstPort()); + void testAddressing(InetSocketAddress in) { + ByteBuf buffer = Unpooled.buffer(); + Address.writeAddressPort(buffer, in); + InetSocketAddress out = Address.readAddressPort(buffer); + Assertions.assertEquals(in.getHostString(), out.getHostString()); + Assertions.assertEquals(in.getPort(), out.getPort()); + buffer.release(); } @Test void testWriteUnknown() { ByteBuf buf = Unpooled.buffer(); - DefaultSocks5CommandRequest request1 = new DefaultSocks5CommandRequest( - Socks5CommandType.CONNECT, Socks5AddressType.valueOf((byte) -1), "localhost", TestDice.rollPort()); - Assertions.assertThrows(EncoderException.class, () -> Address.writeAddressPort(buf, request1)); - DefaultSocks5CommandRequest request2 = new DefaultSocks5CommandRequest( - Socks5CommandType.CONNECT, Socks5AddressType.DOMAIN, "", TestDice.rollPort()); - Assertions.assertThrows(EncoderException.class, () -> Address.writeAddressPort(buf, request2)); + Assertions.assertThrows(EncoderException.class, () -> Address.writeAddressPort(buf, null)); } @Test diff --git a/urban-spork-test/test/com/urbanspork/common/util/LruCacheTestCase.java b/urban-spork-test/test/com/urbanspork/common/util/LruCacheTestCase.java index ddb56964..70b8ca22 100644 --- a/urban-spork-test/test/com/urbanspork/common/util/LruCacheTestCase.java +++ b/urban-spork-test/test/com/urbanspork/common/util/LruCacheTestCase.java @@ -1,6 +1,5 @@ package com.urbanspork.common.util; -import com.urbanspork.test.template.TraceLevelLoggerTestTemplate; import io.netty.channel.Channel; import io.netty.channel.embedded.EmbeddedChannel; import org.junit.jupiter.api.Assertions; @@ -12,7 +11,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; -class LruCacheTestCase extends TraceLevelLoggerTestTemplate { +class LruCacheTestCase { @Test void test() { Duration timeToLive = Duration.ofSeconds(3); @@ -33,12 +32,7 @@ void test() { LockSupport.parkNanos(timeToLive.plusSeconds(2).toNanos()); Assertions.assertNull(cache.get(a1)); cache.insert(a1, new EmbeddedChannel()); - cache.clear(); + cache.release(); Assertions.assertNull(cache.get(a1)); } - - @Override - protected Class loggerClass() { - return LruCache.class; - } } diff --git a/urban-spork-test/test/com/urbanspork/server/RemoteConnectHandlerTestCase.java b/urban-spork-test/test/com/urbanspork/server/RemoteConnectHandlerTestCase.java index 19e7ccf5..0fb2d998 100644 --- a/urban-spork-test/test/com/urbanspork/server/RemoteConnectHandlerTestCase.java +++ b/urban-spork-test/test/com/urbanspork/server/RemoteConnectHandlerTestCase.java @@ -2,6 +2,8 @@ import com.urbanspork.common.config.ServerConfig; import com.urbanspork.common.config.ServerConfigTestCase; +import com.urbanspork.common.util.Dice; +import io.netty.buffer.Unpooled; import io.netty.channel.embedded.EmbeddedChannel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; @@ -16,6 +18,7 @@ void testConnectFailed() { ServerConfig config = ServerConfigTestCase.testConfig(0); EmbeddedChannel channel = new EmbeddedChannel(new RemoteConnectHandler(config)); channel.writeInbound(new InetSocketAddress(0)); + channel.writeInbound(Unpooled.wrappedBuffer(Dice.rollBytes(10))); Assertions.assertFalse(channel.isActive()); } } diff --git a/urban-spork-test/test/com/urbanspork/server/vmess/ServerAEADCodecTestCase.java b/urban-spork-test/test/com/urbanspork/server/vmess/ServerAEADCodecTestCase.java index 61ff619a..c8edf9b4 100644 --- a/urban-spork-test/test/com/urbanspork/server/vmess/ServerAEADCodecTestCase.java +++ b/urban-spork-test/test/com/urbanspork/server/vmess/ServerAEADCodecTestCase.java @@ -16,20 +16,17 @@ import io.netty.buffer.Unpooled; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.socksx.v5.DefaultSocks5CommandRequest; -import io.netty.handler.codec.socksx.v5.Socks5AddressType; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequest; -import io.netty.handler.codec.socksx.v5.Socks5CommandType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.net.InetSocketAddress; + @DisplayName("VMess - Server AEAD Codec") class ServerAEADCodecTestCase { private static final String UUID = java.util.UUID.randomUUID().toString(); - private static final Socks5CommandRequest ADDRESS = new DefaultSocks5CommandRequest( - Socks5CommandType.CONNECT, Socks5AddressType.DOMAIN, "www.urban-spork.com", TestDice.rollPort()); + private static final InetSocketAddress ADDRESS = InetSocketAddress.createUnresolved("www.urban-spork.com", TestDice.rollPort()); @Test void testDecodeEmptyHeader() { diff --git a/urban-spork-test/test/com/urbanspork/test/template/UdpTestTemplate.java b/urban-spork-test/test/com/urbanspork/test/template/UdpTestTemplate.java index 7dc19eb8..8c545eed 100644 --- a/urban-spork-test/test/com/urbanspork/test/template/UdpTestTemplate.java +++ b/urban-spork-test/test/com/urbanspork/test/template/UdpTestTemplate.java @@ -3,7 +3,7 @@ import com.urbanspork.common.codec.socks.DatagramPacketDecoder; import com.urbanspork.common.codec.socks.DatagramPacketEncoder; import com.urbanspork.common.protocol.socks.ClientHandshake; -import com.urbanspork.common.transport.udp.TernaryDatagramPacket; +import com.urbanspork.common.transport.udp.DatagramPacketWrapper; import com.urbanspork.test.TestDice; import com.urbanspork.test.server.udp.DelayedEchoTestServer; import com.urbanspork.test.server.udp.SimpleEchoTestServer; @@ -49,7 +49,7 @@ public abstract class UdpTestTemplate extends TestTemplate { private final List dstAddress = new ArrayList<>(); private final EventLoopGroup group = new NioEventLoopGroup(); private Channel channel; - private Consumer consumer; + private Consumer consumer; private DatagramSocket simpleEchoTestServer; private DatagramSocket delayedEchoTestServer; @@ -103,9 +103,9 @@ protected void initChannel(Channel ch) { ch.pipeline().addLast( new DatagramPacketEncoder(), new DatagramPacketDecoder(), - new SimpleChannelInboundHandler(false) { + new SimpleChannelInboundHandler(false) { @Override - protected void channelRead0(ChannelHandlerContext ctx, TernaryDatagramPacket msg) { + protected void channelRead0(ChannelHandlerContext ctx, DatagramPacketWrapper msg) { logger.info("Receive msg {}", msg); consumer.accept(msg); } @@ -129,7 +129,7 @@ void handshakeAndSendBytes(InetSocketAddress proxyAddress, InetSocketAddress dst Assertions.assertEquals(Socks5CommandStatus.SUCCESS, response.status()); CompletableFuture promise = new CompletableFuture<>(); consumer = msg -> { - if (dstAddress.equals(msg.third())) { + if (dstAddress.equals(msg.proxy())) { promise.complete(null); } else { promise.completeExceptionally(AssertionFailureBuilder.assertionFailure().message("Not equals").build()); @@ -137,7 +137,7 @@ void handshakeAndSendBytes(InetSocketAddress proxyAddress, InetSocketAddress dst }; String str = TestDice.rollString(); DatagramPacket data = new DatagramPacket(Unpooled.copiedBuffer(str.getBytes()), dstAddress); - TernaryDatagramPacket msg = new TernaryDatagramPacket(data, proxyAddress); + DatagramPacketWrapper msg = new DatagramPacketWrapper(data, proxyAddress); logger.info("Send msg {}", msg); channel.writeAndFlush(msg); promise.get(DelayedEchoTestServer.MAX_DELAYED_SECOND + 3, TimeUnit.SECONDS);