diff --git a/src/main/java/net/raphimc/viaproxy/cli/options/Options.java b/src/main/java/net/raphimc/viaproxy/cli/options/Options.java index 4f88ab4..2e206e0 100644 --- a/src/main/java/net/raphimc/viaproxy/cli/options/Options.java +++ b/src/main/java/net/raphimc/viaproxy/cli/options/Options.java @@ -29,6 +29,8 @@ import net.raphimc.viaproxy.saves.impl.accounts.Account; import net.raphimc.viaproxy.util.logging.Logger; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import static java.util.Arrays.asList; @@ -51,7 +53,8 @@ public class Options { public static boolean SRV_MODE; // Example: lenni0451.net_25565_1.8.x.viaproxy.127.0.0.1.nip.io public static boolean INTERNAL_SRV_MODE; // Example: ip\7port\7version\7mppass public static boolean LOCAL_SOCKET_AUTH; - public static String RESOURCE_PACK_URL; + public static String RESOURCE_PACK_URL; // Example: http://example.com/resourcepack.zip + public static URI PROXY_URL; // Example: type://address:port or type://username:password@address:port public static void parse(final String[] args) throws IOException { final OptionParser parser = new OptionParser(); @@ -70,6 +73,7 @@ public class Options { final OptionSpec localSocketAuth = parser.accepts("local_socket_auth", "Enable authentication over a local socket"); final OptionSpec betaCraftAuth = parser.accepts("betacraft_auth", "Use BetaCraft authentication servers for classic"); final OptionSpec resourcePackUrl = parser.acceptsAll(asList("resource_pack_url", "resource_pack", "rpu", "rp"), "URL of a resource pack which all connecting clients can optionally download").withRequiredArg().ofType(String.class); + final OptionSpec proxyUrl = parser.acceptsAll(asList("proxy_url", "proxy"), "URL of a SOCKS(4/5)/HTTP(S) proxy which will be used for TCP connections").withRequiredArg().ofType(String.class); PluginManager.EVENT_MANAGER.call(new PreOptionsParseEvent(parser)); final OptionSet options = parser.parse(args); @@ -98,6 +102,15 @@ public class Options { if (options.has(resourcePackUrl)) { RESOURCE_PACK_URL = options.valueOf(resourcePackUrl); } + if (options.has(proxyUrl)) { + try { + PROXY_URL = new URI(options.valueOf(proxyUrl)); + } catch (URISyntaxException e) { + Logger.LOGGER.error("Invalid proxy url: " + options.valueOf(proxyUrl)); + Logger.LOGGER.error("Proxy url format: type://address:port or type://username:password@address:port"); + System.exit(1); + } + } PluginManager.EVENT_MANAGER.call(new PostOptionsParseEvent(options)); } diff --git a/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java b/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java index 15f4843..41ac9e8 100644 --- a/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java +++ b/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java @@ -22,6 +22,10 @@ import com.viaversion.viaversion.connection.UserConnectionImpl; import com.viaversion.viaversion.protocol.ProtocolPipelineImpl; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; +import io.netty.handler.proxy.HttpProxyHandler; +import io.netty.handler.proxy.ProxyHandler; +import io.netty.handler.proxy.Socks4ProxyHandler; +import io.netty.handler.proxy.Socks5ProxyHandler; import net.raphimc.netminecraft.constants.MCPipeline; import net.raphimc.netminecraft.netty.connection.MinecraftChannelInitializer; import net.raphimc.netminecraft.packet.registry.PacketRegistryUtil; @@ -31,12 +35,16 @@ import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.baseprotoc import net.raphimc.viaprotocolhack.netty.VPHEncodeHandler; import net.raphimc.viaprotocolhack.netty.VPHPipeline; import net.raphimc.viaprotocolhack.util.VersionEnum; +import net.raphimc.viaproxy.cli.options.Options; import net.raphimc.viaproxy.plugins.PluginManager; import net.raphimc.viaproxy.plugins.events.Proxy2ServerChannelInitializeEvent; import net.raphimc.viaproxy.plugins.events.types.ITyped; import net.raphimc.viaproxy.protocolhack.impl.ViaProxyViaDecodeHandler; import net.raphimc.viaproxy.proxy.ProxyConnection; +import java.net.InetSocketAddress; +import java.net.URI; +import java.util.Locale; import java.util.function.Supplier; public class Proxy2ServerChannelInitializer extends MinecraftChannelInitializer { @@ -67,9 +75,35 @@ public class Proxy2ServerChannelInitializer extends MinecraftChannelInitializer channel.pipeline().addBefore(MCPipeline.SIZER_HANDLER_NAME, VPHPipeline.PRE_NETTY_DECODER_HANDLER_NAME, new PreNettyDecoder(user)); } + if (Options.PROXY_URL != null) { + channel.pipeline().addFirst("viaproxy-proxy-handler", this.getProxyHandler()); + } + if (PluginManager.EVENT_MANAGER.call(new Proxy2ServerChannelInitializeEvent(ITyped.Type.POST, channel)).isCancelled()) { channel.close(); } } + protected ProxyHandler getProxyHandler() { + final URI proxyUrl = Options.PROXY_URL; + final InetSocketAddress proxyAddress = new InetSocketAddress(proxyUrl.getHost(), proxyUrl.getPort()); + final String username = proxyUrl.getUserInfo() != null ? proxyUrl.getUserInfo().split(":")[0] : null; + final String password = proxyUrl.getUserInfo() != null && proxyUrl.getUserInfo().contains(":") ? proxyUrl.getUserInfo().split(":")[1] : null; + + switch (proxyUrl.getScheme().toUpperCase(Locale.ROOT)) { + case "HTTP": + case "HTTPS": + if (username != null && password != null) return new HttpProxyHandler(proxyAddress, username, password); + else return new HttpProxyHandler(proxyAddress); + case "SOCKS4": + if (username != null) return new Socks4ProxyHandler(proxyAddress, username); + else return new Socks4ProxyHandler(proxyAddress); + case "SOCKS5": + if (username != null && password != null) return new Socks5ProxyHandler(proxyAddress, username, password); + else return new Socks5ProxyHandler(proxyAddress); + } + + throw new IllegalArgumentException("Unknown proxy type: " + proxyUrl.getScheme()); + } + }