Skip to content

Commit

Permalink
test: remove TestUtil.freePorts()
Browse files Browse the repository at this point in the history
These utilities make no guarantee about the subsequent availability of a given port and are therefore unreliable
  • Loading branch information
Zmax0 committed Jan 24, 2024
1 parent 6c4e4ca commit 1e394e1
Show file tree
Hide file tree
Showing 45 changed files with 519 additions and 441 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
import com.urbanspork.common.config.ConfigHandler;

import java.net.URL;
import java.util.*;
import java.util.ArrayList;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Objects;
import java.util.ResourceBundle;

public class Resource {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,23 @@
import com.urbanspork.client.gui.Resource;
import com.urbanspork.common.config.ClientConfig;
import com.urbanspork.common.config.ServerConfig;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.util.concurrent.DefaultPromise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.TrayIcon.MessageType;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Proxy {

private static final Logger logger = LoggerFactory.getLogger(Proxy.class);

private static final ClientConfig config = Resource.config();

private static final ExecutorService executor = Executors.newSingleThreadExecutor();

private static Future<?> proxyingTask;
private static Map.Entry<ServerSocketChannel, DatagramChannel> client;

private Proxy() {}

Expand All @@ -32,29 +30,27 @@ public static void launch() {
Tray.displayMessage("Proxy is not running", "Please set up a proxy server first", MessageType.INFO);
return;
}
if (proxyingTask != null && !proxyingTask.isCancelled()) {
proxyingTask.cancel(true);
if (client != null) {
Client.close(client);
}
CompletableFuture<Map.Entry<ServerSocketChannel, DatagramChannel>> promise = new CompletableFuture<>();
executor.submit(() -> Client.launch(config, promise));
try {
client = promise.get();
String message = current.toString();
Tray.displayMessage("Proxy is running", message, MessageType.INFO);
Tray.setToolTip(message);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
String message = e.getMessage();
Tray.displayMessage("Error", message, MessageType.ERROR);
Tray.setToolTip(message);
}
proxyingTask = executor.submit(() -> {
try {
DefaultPromise<ServerSocketChannel> promise = new DefaultPromise<>() {};
Client.launch(config, promise);
promise.await();
} catch (InterruptedException e) {
logger.error("Launching proxy client launching error", e);
String message = e.getMessage();
Tray.displayMessage("Error", message, MessageType.ERROR);
Tray.setToolTip(message);
Thread.currentThread().interrupt();
}
});
String message = current.toString();
Tray.displayMessage("Proxy is running", message, MessageType.INFO);
Tray.setToolTip(message);
}

public static void exit() {
proxyingTask.cancel(true);
Client.close(client);
executor.shutdown();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,6 @@ private void itemStateChanged(ClientConfig config, List<CheckboxMenuItem> items,
return;
}
Proxy.launch();
String message = config.getCurrent().toString();
Tray.displayMessage("Proxy is running", message, MessageType.INFO);
Tray.setToolTip(message);
} else {
item.setState(true);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.urbanspork.client;

import com.urbanspork.common.channel.ChannelCloseUtils;
import com.urbanspork.common.config.ServerConfig;
import com.urbanspork.common.protocol.network.TernaryDatagramPacket;
import io.netty.channel.Channel;
Expand All @@ -23,7 +22,6 @@ public abstract class AbstractClientUDPReplayHandler<K> extends SimpleChannelInb
protected final ServerConfig config;

protected AbstractClientUDPReplayHandler(ServerConfig config) {
super(false);
this.config = config;
}

Expand All @@ -43,11 +41,12 @@ public void channelRead0(ChannelHandlerContext ctx, TernaryDatagramPacket msg) {
}

@Override
public void channelUnregistered(ChannelHandlerContext ctx) {
ctx.fireChannelUnregistered();
logger.info("Stop timer and clean binding");
public void handlerRemoved(ChannelHandlerContext ctx) {
logger.info("Stop timer and close binding");
for (Map.Entry<?, Channel> entry : binding.entrySet()) {
entry.getValue().close();
}
timer.stop();
ChannelCloseUtils.clearMap(binding);
}

private Channel getBindingChannel(Channel inboundChannel, K key) {
Expand All @@ -65,5 +64,4 @@ private Channel getBindingChannel(Channel inboundChannel, K key) {
return channel;
});
}

}
89 changes: 58 additions & 31 deletions urban-spork-client/src/com/urbanspork/client/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,66 +10,93 @@
import com.urbanspork.common.protocol.Protocols;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.Promise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

public class Client {

private static final Logger logger = LoggerFactory.getLogger(Client.class);

public static void main(String[] args) {
launch(ConfigHandler.DEFAULT.read(), new DefaultPromise<>() {});
launch(ConfigHandler.DEFAULT.read(), new CompletableFuture<>());
}

public static void launch(ClientConfig config, Promise<ServerSocketChannel> promise) {
int port = config.getPort();
ServerConfig current = config.getCurrent();
public static void launch(ClientConfig config, CompletableFuture<Map.Entry<ServerSocketChannel, DatagramChannel>> promise) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ChannelHandler udpTransportHandler;
if (Protocols.vmess == current.getProtocol()) {
udpTransportHandler = new ClientUDPOverTCPHandler(current, workerGroup);
} else {
udpTransportHandler = new ClientUDPReplayHandler(current, workerGroup);
}
new Bootstrap().group(bossGroup).channel(NioDatagramChannel.class)
.handler(new ChannelInitializer<>() {
@Override
protected void initChannel(Channel ch) {
ch.pipeline().addLast(
new DatagramPacketEncoder(),
new DatagramPacketDecoder(),
udpTransportHandler
);
}
})
.bind(port).sync();
new ServerBootstrap().group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.SO_KEEPALIVE, true) // socks5 require
.childOption(ChannelOption.TCP_NODELAY, false)
.childOption(ChannelOption.SO_LINGER, 1)
.childHandler(new ClientSocksInitializer(current, port))
.bind(port).sync().addListener((ChannelFutureListener) future -> {
logger.info("Launch client => {} ", config);
promise.setSuccess((ServerSocketChannel) future.channel());
}).channel().closeFuture().sync();
.childHandler(new ClientSocksInitializer(config.getCurrent()))
.bind(InetAddress.getLoopbackAddress(), config.getPort()).sync().addListener((ChannelFutureListener) future -> {
ServerSocketChannel tcp = (ServerSocketChannel) future.channel();
InetSocketAddress tcpLocalAddress = tcp.localAddress();
int localPort = tcpLocalAddress.getPort();
config.setPort(localPort);
DatagramChannel udp = launchUdp(bossGroup, workerGroup, config);
logger.info("Launch client => tcp{} udp{} ", tcpLocalAddress, udp.localAddress());
Map.Entry<ServerSocketChannel, DatagramChannel> client = Map.entry(tcp, udp);
promise.complete(client);
});
Map.Entry<ServerSocketChannel, DatagramChannel> client = promise.get();
CompletableFuture.allOf(
CompletableFuture.supplyAsync(() -> client.getKey().closeFuture().syncUninterruptibly()),
CompletableFuture.supplyAsync(() -> client.getValue().closeFuture().syncUninterruptibly())
).get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) {
logger.error("Launch client failed", e);
promise.setFailure(e);
promise.completeExceptionally(e);
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}

public static void close(Map.Entry<ServerSocketChannel, DatagramChannel> client) {
client.getKey().close().awaitUninterruptibly();
client.getValue().close().awaitUninterruptibly();
}

private static DatagramChannel launchUdp(EventLoopGroup bossGroup, EventLoopGroup workerGroup, ClientConfig config) throws InterruptedException {
ServerConfig current = config.getCurrent();
ChannelHandler udpTransportHandler;
if (Protocols.vmess == current.getProtocol()) {
udpTransportHandler = new ClientUDPOverTCPHandler(current, workerGroup);
} else {
udpTransportHandler = new ClientUDPReplayHandler(current, workerGroup);
}
return (DatagramChannel) new Bootstrap().group(bossGroup).channel(NioDatagramChannel.class)
.handler(new ChannelInitializer<>() {
@Override
protected void initChannel(Channel ch) {
ch.pipeline().addLast(
new DatagramPacketEncoder(),
new DatagramPacketDecoder(),
udpTransportHandler
);
}
})
.bind(InetAddress.getLoopbackAddress(), config.getPort()).sync().channel();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@
import com.urbanspork.common.channel.DefaultChannelInboundHandler;
import com.urbanspork.common.config.ServerConfig;
import com.urbanspork.common.protocol.Protocols;
import com.urbanspork.common.protocol.socks.Socks5;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.socksx.v5.DefaultSocks5CommandResponse;
import io.netty.handler.codec.socksx.v5.Socks5CommandRequest;
import io.netty.handler.codec.socksx.v5.Socks5CommandStatus;
import io.netty.handler.codec.socksx.v5.Socks5CommandType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -42,8 +49,8 @@ protected void channelRead0(ChannelHandlerContext ctx, Socks5CommandRequest requ
Channel outbound = future.channel();
outbound.pipeline().addLast(new DefaultChannelInboundHandler(ctx.channel())); // R → L
ctx.pipeline().remove(ClientSocksConnectHandler.this);
InetSocketAddress localAddress = (InetSocketAddress) inboundChannel.localAddress();
ctx.writeAndFlush(new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, request.dstAddrType(), localAddress.getHostString(), localAddress.getPort()))
Socks5CommandRequest bndRequest = Socks5.toCommandRequest(Socks5CommandType.CONNECT, (InetSocketAddress) inboundChannel.localAddress());
ctx.writeAndFlush(new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, bndRequest.dstAddrType(), bndRequest.dstAddr(), bndRequest.dstPort()))
.addListener((ChannelFutureListener) channelFuture -> ctx.pipeline().addLast(new DefaultChannelInboundHandler(outbound))); // L → R
} else {
logger.error("Connect proxy server {} failed", serverAddress);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,14 @@
public class ClientSocksInitializer extends ChannelInitializer<NioSocketChannel> {

private final ServerConfig config;
private final Integer socksPort;

public ClientSocksInitializer(ServerConfig config, Integer socksPort) {
public ClientSocksInitializer(ServerConfig config) {
this.config = config;
this.socksPort = socksPort;
}

@Override
protected void initChannel(NioSocketChannel channel) {
channel.attr(AttributeKeys.SERVER_CONFIG).set(config);
channel.attr(AttributeKeys.SOCKS_PORT).set(socksPort);
channel.pipeline()
.addLast(new SocksPortUnificationServerHandler())
.addLast(ClientSocksMessageHandler.INSTANCE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ private void channelRead0(ChannelHandlerContext ctx, Socks5CommandRequest reques
channel.writeAndFlush(new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, request.dstAddrType()));
return;
}
Integer bndPort = channel.attr(AttributeKeys.SOCKS_PORT).get();
Socks5CommandRequest bndRequest = Socks5.toCommandRequest(Socks5CommandType.UDP_ASSOCIATE, new InetSocketAddress(bndPort));
Socks5CommandRequest bndRequest = Socks5.toCommandRequest(Socks5CommandType.UDP_ASSOCIATE, (InetSocketAddress) channel.localAddress());
DefaultSocks5CommandResponse response = new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, bndRequest.dstAddrType(), bndRequest.dstAddr(), bndRequest.dstPort());
channel.writeAndFlush(response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
import com.urbanspork.common.config.ServerConfig;
import com.urbanspork.common.protocol.network.TernaryDatagramPacket;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import org.slf4j.Logger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
import com.urbanspork.common.protocol.vmess.header.RequestCommand;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,11 @@
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;

import java.util.Map;

public final class ChannelCloseUtils {

private ChannelCloseUtils() {}

public static void closeOnFlush(Channel ch) {
ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}

public static void clearMap(Map<?, Channel> map) {
for (Map.Entry<?, Channel> entry : map.entrySet()) {
entry.getValue().close();
}
map.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List<Object
}
content.skipBytes(3);
InetSocketAddress address = Address.decode(content);
out.add(new TernaryDatagramPacket(msg.replace(content.retainedDuplicate()), address));
out.add(new TernaryDatagramPacket(msg.replace(content.retainedSlice()), address));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@ public class Control {
private ServerUser user;

public Control(CipherKind kind) {
this(Dice.rollBytes(kind.keySize()), ThreadLocalRandom.current().nextLong(), ThreadLocalRandom.current().nextLong(), 0, null);
this(Dice.rollBytes(kind.keySize()), ThreadLocalRandom.current().nextLong(), ThreadLocalRandom.current().nextLong(), 0);
}

Control(byte[] salt, long clientSessionId, long serverSessionId, long packetId, ServerUser user) {
Control(byte[] salt, long clientSessionId, long serverSessionId, long packetId) {
this.salt = salt;
this.clientSessionId = clientSessionId;
this.serverSessionId = serverSessionId;
this.packetId = packetId;
this.user = user;
}

public void increasePacketId(long i) {
Expand Down
Loading

0 comments on commit 1e394e1

Please sign in to comment.