From d725ce0d79bc6aeb148b2ca103076005ea7b60fe Mon Sep 17 00:00:00 2001 From: Steveice10 Date: Wed, 26 Feb 2020 18:11:23 -0800 Subject: [PATCH] Implement proper NIO proxy support and remove synchronous support from send/disconnect. --- pom.xml | 2 +- .../steveice10/packetlib/ProxyInfo.java | 106 ++++++++++++++++++ .../github/steveice10/packetlib/Session.java | 30 +++-- .../steveice10/packetlib/packet/Packet.java | 6 +- .../packetlib/tcp/ProxyOioChannelFactory.java | 20 ---- .../packetlib/tcp/TcpClientSession.java | 65 +++++++---- .../steveice10/packetlib/tcp/TcpSession.java | 38 ++----- .../packetlib/tcp/TcpSessionFactory.java | 7 +- 8 files changed, 178 insertions(+), 96 deletions(-) create mode 100644 src/main/java/com/github/steveice10/packetlib/ProxyInfo.java delete mode 100644 src/main/java/com/github/steveice10/packetlib/tcp/ProxyOioChannelFactory.java diff --git a/pom.xml b/pom.xml index 31fe4166..36fa8cae 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ io.netty netty-all - 4.1.42.Final + 4.1.45.Final compile diff --git a/src/main/java/com/github/steveice10/packetlib/ProxyInfo.java b/src/main/java/com/github/steveice10/packetlib/ProxyInfo.java new file mode 100644 index 00000000..90e6544f --- /dev/null +++ b/src/main/java/com/github/steveice10/packetlib/ProxyInfo.java @@ -0,0 +1,106 @@ +package com.github.steveice10.packetlib; + +import java.net.SocketAddress; + +/** + * Information describing a network proxy. + */ +public class ProxyInfo { + private Type type; + private SocketAddress address; + private boolean authenticated; + private String username; + private String password; + + /** + * Creates a new unauthenticated ProxyInfo instance. + * + * @param type Type of proxy. + * @param address Network address of the proxy. + */ + public ProxyInfo(Type type, SocketAddress address) { + this.type = type; + this.address = address; + this.authenticated = false; + } + + /** + * Creates a new authenticated ProxyInfo instance. + * + * @param type Type of proxy. + * @param address Network address of the proxy. + * @param username Username to authenticate with. + * @param password Password to authenticate with. + */ + public ProxyInfo(Type type, SocketAddress address, String username, String password) { + this(type, address); + this.authenticated = true; + this.username = username; + this.password = password; + } + + /** + * Gets the proxy's type. + * + * @return The proxy's type. + */ + public Type getType() { + return this.type; + } + + /** + * Gets the proxy's network address. + * + * @return The proxy's network address. + */ + public SocketAddress getAddress() { + return this.address; + } + + /** + * Gets whether the proxy is authenticated with. + * + * @return Whether to authenticate with the proxy. + */ + public boolean isAuthenticated() { + return this.authenticated; + } + + /** + * Gets the proxy's authentication username. + * + * @return The username to authenticate with. + */ + public String getUsername() { + return this.username; + } + + /** + * Gets the proxy's authentication password. + * + * @return The password to authenticate with. + */ + public String getPassword() { + return this.password; + } + + /** + * Supported proxy types. + */ + public enum Type { + /** + * HTTP proxy. + */ + HTTP, + + /** + * SOCKS4 proxy. + */ + SOCKS4, + + /** + * SOCKS5 proxy. + */ + SOCKS5; + } +} diff --git a/src/main/java/com/github/steveice10/packetlib/Session.java b/src/main/java/com/github/steveice10/packetlib/Session.java index ec07bf51..45a8a06f 100644 --- a/src/main/java/com/github/steveice10/packetlib/Session.java +++ b/src/main/java/com/github/steveice10/packetlib/Session.java @@ -89,6 +89,19 @@ public interface Session { */ public T getFlag(String key); + /** + * Gets the value of the given flag as an instance of the given type. If this + * session belongs to a server, the server's flags will be checked for the flag + * as well. If no flag is found, the specified default value will be returned. + * + * @param Type of the flag. + * @param key Key of the flag. + * @param def Default value of the flag. + * @return Value of the flag. + * @throws IllegalStateException If the flag's value isn't of the required type. + */ + public T getFlag(String key, T def); + /** * Sets the value of a flag. This does not change a server's flags if this session * belongs to a server. @@ -203,14 +216,6 @@ public interface Session { */ public void disconnect(String reason); - /** - * Disconnects the session. - * - * @param reason Reason for disconnecting. - * @param wait Whether to wait for the session to be disconnected. - */ - public void disconnect(String reason, boolean wait); - /** * Disconnects the session. * @@ -218,13 +223,4 @@ public interface Session { * @param cause Throwable responsible for disconnecting. */ public void disconnect(String reason, Throwable cause); - - /** - * Disconnects the session. - * - * @param reason Reason for disconnecting. - * @param cause Throwable responsible for disconnecting. - * @param wait Whether to wait for the session to be disconnected. - */ - public void disconnect(String reason, Throwable cause, boolean wait); } diff --git a/src/main/java/com/github/steveice10/packetlib/packet/Packet.java b/src/main/java/com/github/steveice10/packetlib/packet/Packet.java index 22fa968f..139bd7e4 100644 --- a/src/main/java/com/github/steveice10/packetlib/packet/Packet.java +++ b/src/main/java/com/github/steveice10/packetlib/packet/Packet.java @@ -26,9 +26,9 @@ public interface Packet { public void write(NetOutput out) throws IOException; /** - * Gets whether the packet has handling and writing priority. - * If the result is true, the thread will wait for the packet to finish writing - * when writing and the packet will be handled immediately after reading it. + * Gets whether the packet has handling priority. + * If the result is true, the packet will be handled immediately after being + * decoded. * * @return Whether the packet has priority. */ diff --git a/src/main/java/com/github/steveice10/packetlib/tcp/ProxyOioChannelFactory.java b/src/main/java/com/github/steveice10/packetlib/tcp/ProxyOioChannelFactory.java deleted file mode 100644 index 77130135..00000000 --- a/src/main/java/com/github/steveice10/packetlib/tcp/ProxyOioChannelFactory.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.steveice10.packetlib.tcp; - -import io.netty.channel.ChannelFactory; -import io.netty.channel.socket.oio.OioSocketChannel; - -import java.net.Proxy; -import java.net.Socket; - -public class ProxyOioChannelFactory implements ChannelFactory { - private Proxy proxy; - - public ProxyOioChannelFactory(Proxy proxy) { - this.proxy = proxy; - } - - @Override - public OioSocketChannel newChannel() { - return new OioSocketChannel(new Socket(this.proxy)); - } -} diff --git a/src/main/java/com/github/steveice10/packetlib/tcp/TcpClientSession.java b/src/main/java/com/github/steveice10/packetlib/tcp/TcpClientSession.java index ae0e2223..fdbe422d 100644 --- a/src/main/java/com/github/steveice10/packetlib/tcp/TcpClientSession.java +++ b/src/main/java/com/github/steveice10/packetlib/tcp/TcpClientSession.java @@ -1,6 +1,7 @@ package com.github.steveice10.packetlib.tcp; import com.github.steveice10.packetlib.Client; +import com.github.steveice10.packetlib.ProxyInfo; import com.github.steveice10.packetlib.packet.PacketProtocol; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; @@ -10,21 +11,22 @@ import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.oio.OioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.proxy.HttpProxyHandler; +import io.netty.handler.proxy.Socks4ProxyHandler; +import io.netty.handler.proxy.Socks5ProxyHandler; import io.netty.util.concurrent.Future; import javax.naming.directory.InitialDirContext; -import java.net.Proxy; import java.util.Hashtable; public class TcpClientSession extends TcpSession { private Client client; - private Proxy proxy; + private ProxyInfo proxy; private EventLoopGroup group; - public TcpClientSession(String host, int port, PacketProtocol protocol, Client client, Proxy proxy) { + public TcpClientSession(String host, int port, PacketProtocol protocol, Client client, ProxyInfo proxy) { super(host, port, protocol); this.client = client; this.proxy = proxy; @@ -39,15 +41,10 @@ public class TcpClientSession extends TcpSession { } try { - final Bootstrap bootstrap = new Bootstrap(); - if(this.proxy != null) { - this.group = new OioEventLoopGroup(); - bootstrap.channelFactory(new ProxyOioChannelFactory(this.proxy)); - } else { - this.group = new NioEventLoopGroup(); - bootstrap.channel(NioSocketChannel.class); - } + this.group = new NioEventLoopGroup(); + final Bootstrap bootstrap = new Bootstrap(); + bootstrap.channel(NioSocketChannel.class); bootstrap.handler(new ChannelInitializer() { @Override public void initChannel(Channel channel) throws Exception { @@ -61,6 +58,37 @@ public class TcpClientSession extends TcpSession { refreshReadTimeoutHandler(channel); refreshWriteTimeoutHandler(channel); + if(proxy != null) { + switch(proxy.getType()) { + case HTTP: + if(proxy.isAuthenticated()) { + pipeline.addFirst("proxy", new HttpProxyHandler(proxy.getAddress(), proxy.getUsername(), proxy.getPassword())); + } else { + pipeline.addFirst("proxy", new HttpProxyHandler(proxy.getAddress())); + } + + break; + case SOCKS4: + if(proxy.isAuthenticated()) { + pipeline.addFirst("proxy", new Socks4ProxyHandler(proxy.getAddress(), proxy.getUsername())); + } else { + pipeline.addFirst("proxy", new Socks4ProxyHandler(proxy.getAddress())); + } + + break; + case SOCKS5: + if(proxy.isAuthenticated()) { + pipeline.addFirst("proxy", new Socks5ProxyHandler(proxy.getAddress(), proxy.getUsername(), proxy.getPassword())); + } else { + pipeline.addFirst("proxy", new Socks5ProxyHandler(proxy.getAddress())); + } + + break; + default: + throw new UnsupportedOperationException("Unsupported proxy type: " + proxy.getType()); + } + } + pipeline.addLast("encryption", new TcpPacketEncryptor(TcpClientSession.this)); pipeline.addLast("sizer", new TcpPacketSizer(TcpClientSession.this)); pipeline.addLast("codec", new TcpPacketCodec(TcpClientSession.this)); @@ -117,17 +145,10 @@ public class TcpClientSession extends TcpSession { } @Override - public void disconnect(String reason, Throwable cause, boolean wait) { - super.disconnect(reason, cause, wait); + public void disconnect(String reason, Throwable cause) { + super.disconnect(reason, cause); if(this.group != null) { - Future future = this.group.shutdownGracefully(); - if(wait) { - try { - future.await(); - } catch(InterruptedException e) { - } - } - + this.group.shutdownGracefully(); this.group = null; } } diff --git a/src/main/java/com/github/steveice10/packetlib/tcp/TcpSession.java b/src/main/java/com/github/steveice10/packetlib/tcp/TcpSession.java index 4fa23ef8..c801b341 100644 --- a/src/main/java/com/github/steveice10/packetlib/tcp/TcpSession.java +++ b/src/main/java/com/github/steveice10/packetlib/tcp/TcpSession.java @@ -104,9 +104,14 @@ public abstract class TcpSession extends SimpleChannelInboundHandler imp @SuppressWarnings("unchecked") @Override public T getFlag(String key) { + return this.getFlag(key, null); + } + + @Override + public T getFlag(String key, T def) { Object value = this.getFlags().get(key); if(value == null) { - return null; + return def; } try { @@ -214,8 +219,7 @@ public abstract class TcpSession extends SimpleChannelInboundHandler imp if(!sendingEvent.isCancelled()) { final Packet toSend = sendingEvent.getPacket(); - - ChannelFuture future = this.channel.writeAndFlush(toSend).addListener(new ChannelFutureListener() { + this.channel.writeAndFlush(toSend).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if(future.isSuccess()) { @@ -225,33 +229,16 @@ public abstract class TcpSession extends SimpleChannelInboundHandler imp } } }); - - if(toSend.isPriority()) { - try { - future.await(); - } catch(InterruptedException e) { - } - } } } @Override public void disconnect(String reason) { - this.disconnect(reason, false); - } - - @Override - public void disconnect(String reason, boolean wait) { - this.disconnect(reason, null, wait); + this.disconnect(reason, null); } @Override public void disconnect(final String reason, final Throwable cause) { - this.disconnect(reason, cause, false); - } - - @Override - public void disconnect(final String reason, final Throwable cause, boolean wait) { if(this.disconnected) { return; } @@ -265,19 +252,12 @@ public abstract class TcpSession extends SimpleChannelInboundHandler imp if(this.channel != null && this.channel.isOpen()) { this.callEvent(new DisconnectingEvent(this, reason, cause)); - ChannelFuture future = this.channel.flush().close().addListener(new ChannelFutureListener() { + this.channel.flush().close().addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { callEvent(new DisconnectedEvent(TcpSession.this, reason != null ? reason : "Connection closed.", cause)); } }); - - if(wait) { - try { - future.await(); - } catch(InterruptedException e) { - } - } } else { this.callEvent(new DisconnectedEvent(this, reason != null ? reason : "Connection closed.", cause)); } diff --git a/src/main/java/com/github/steveice10/packetlib/tcp/TcpSessionFactory.java b/src/main/java/com/github/steveice10/packetlib/tcp/TcpSessionFactory.java index d94bbb63..0fad540e 100644 --- a/src/main/java/com/github/steveice10/packetlib/tcp/TcpSessionFactory.java +++ b/src/main/java/com/github/steveice10/packetlib/tcp/TcpSessionFactory.java @@ -2,22 +2,21 @@ package com.github.steveice10.packetlib.tcp; import com.github.steveice10.packetlib.Client; import com.github.steveice10.packetlib.ConnectionListener; +import com.github.steveice10.packetlib.ProxyInfo; import com.github.steveice10.packetlib.Server; import com.github.steveice10.packetlib.Session; import com.github.steveice10.packetlib.SessionFactory; -import java.net.Proxy; - /** * A session factory used to create TCP sessions. */ public class TcpSessionFactory implements SessionFactory { - private Proxy clientProxy; + private ProxyInfo clientProxy; public TcpSessionFactory() { } - public TcpSessionFactory(Proxy clientProxy) { + public TcpSessionFactory(ProxyInfo clientProxy) { this.clientProxy = clientProxy; }