Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,52 +49,54 @@ public void initWidgets() {

matcher = Proxies.PROXY_PATTERN.matcher(line);
if (matcher.matches()) {
String address = matcher.group(2).replaceAll("\\b0+\\B", "");
int port = Integer.parseInt(matcher.group(3));
String address = matcher.group("address").replaceAll("\\b0+\\B", "");
int port = Integer.parseInt(matcher.group("port"));

proxy = new Proxy.Builder()
.address(address)
.port(port)
.name(matcher.group(1) != null ? matcher.group(1) : address + ":" + port)
.type(matcher.group(4) != null ? ProxyType.parse(matcher.group(4)) : ProxyType.Socks4)
.name(matcher.group("name") != null ? matcher.group("name") : address + ":" + port)
.type(matcher.group("type") != null ? ProxyType.parse(matcher.group("type")) : ProxyType.SOCKS4)
.secure(matcher.group("secure") != null)
.build();
}

matcher = Proxies.PROXY_PATTERN_WEBSHARE.matcher(line);
if (proxy == null && matcher.matches()) {
String address = matcher.group(1).replaceAll("\\b0+\\B", "");
int port = Integer.parseInt(matcher.group(2));
String address = matcher.group("address").replaceAll("\\b0+\\B", "");
int port = Integer.parseInt(matcher.group("port"));

proxy = new Proxy.Builder()
.address(address)
.port(port)
.name(address + ":" + port)
.username(matcher.group(3) != null ? matcher.group(3) : "")
.password(matcher.group(4) != null ? matcher.group(4) : "")
.type(ProxyType.Socks5)
.username(matcher.group("username") != null ? matcher.group("username") : "")
.password(matcher.group("password") != null ? matcher.group("password") : "")
.type(ProxyType.HTTP)
.secure(true)
.build();
}

matcher = Proxies.PROXY_PATTERN_URI.matcher(line);
if (proxy == null && matcher.matches()) {
String address = matcher.group("addr").replaceAll("\\b0+\\B", "");
String address = matcher.group("address").replaceAll("\\b0+\\B", "");
int port = Integer.parseInt(matcher.group("port"));

ProxyType type = ProxyType.parse(matcher.group(1));
ProxyType type = ProxyType.parse(matcher.group("type"));
boolean secure = matcher.group("secure") != null;
if (type == null) {
if (matcher.group(1) != null && matcher.group(1).equals("socks")) type = ProxyType.Socks5;
// if it has a password it's a socks5 proxy
else if (matcher.group("pass") != null) type = ProxyType.Socks5;
else type = ProxyType.Socks4;
type = ProxyType.HTTP;
secure = true;
}

proxy = new Proxy.Builder()
.address(address)
.port(port)
.name(address + ":" + port)
.username(matcher.group("user") != null ? matcher.group("user") : "")
.password(matcher.group("pass") != null ? matcher.group("pass") : "")
.username(matcher.group("username") != null ? matcher.group("username") : "")
.password(matcher.group("password") != null ? matcher.group("password") : "")
.type(type)
.secure(secure)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ private void initTable(WTable table) {
WLabel name = table.add(theme.label(proxy.name.get())).widget();
name.color = theme.textColor();

WLabel type = table.add(theme.label("(" + proxy.type.get() + ")")).widget();
WLabel type = table.add(theme.label("(" + proxy.type.get() + (proxy.secure.get() ? "S" : "") + ")")).widget();
type.color = theme.textSecondaryColor();

WHorizontalList ipList = table.add(theme.horizontalList()).expandCellX().widget();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.proxy.HttpProxyHandler;
import io.netty.handler.proxy.Socks4ProxyHandler;
import io.netty.handler.proxy.Socks5ProxyHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.timeout.TimeoutException;
import meteordevelopment.meteorclient.MeteorClient;
import meteordevelopment.meteorclient.events.packets.PacketEvent;
Expand Down Expand Up @@ -40,6 +45,8 @@

import java.net.InetSocketAddress;
import java.util.Iterator;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;

@Mixin(Connection.class)
public abstract class ConnectionMixin {
Expand Down Expand Up @@ -99,10 +106,34 @@ private static void onAddHandlers(ChannelPipeline pipeline, PacketFlow inboundDi
if (proxy == null) return;

switch (proxy.type.get()) {
case Socks4 ->
pipeline.addFirst(new Socks4ProxyHandler(new InetSocketAddress(proxy.address.get(), proxy.port.get()), proxy.username.get()));
case Socks5 ->
pipeline.addFirst(new Socks5ProxyHandler(new InetSocketAddress(proxy.address.get(), proxy.port.get()), proxy.username.get(), proxy.password.get()));
case HTTP -> pipeline.addFirst(new HttpProxyHandler(
new InetSocketAddress(proxy.address.get(), proxy.port.get()),
proxy.username.get(), proxy.password.get()
));
case SOCKS5 -> pipeline.addFirst(new Socks5ProxyHandler(
new InetSocketAddress(proxy.address.get(), proxy.port.get()),
proxy.username.get(), proxy.password.get()
));
case SOCKS4 -> pipeline.addFirst(new Socks4ProxyHandler(
new InetSocketAddress(proxy.address.get(), proxy.port.get()),
proxy.username.get()
));
}

if (proxy.secure.get()) {
try {
SslContext sslContext = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
SSLEngine engine = sslContext.newEngine(
pipeline.channel().alloc(),
proxy.address.get(),
proxy.port.get()
);
pipeline.addFirst(new SslHandler(engine));
} catch (SSLException e) {
throw new RuntimeException("Failed to create SSL context for proxy", e);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ public class Proxies extends System<Proxies> implements Iterable<Proxy> {
.build()
);

// https://regex101.com/r/gRHjnd/latest
public static final Pattern PROXY_PATTERN = Pattern.compile("^(?:([\\w\\s]+)=)?((?:0*(?:\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])(?:\\.(?!:)|)){4}):(?!0)(\\d{1,4}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])(?i:@(socks[45]))?$", Pattern.MULTILINE);
// https://regex101.com/r/QXATIS/1
public static final Pattern PROXY_PATTERN_WEBSHARE = Pattern.compile("^((?:0*(?:\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])(?:\\.(?!:)|)){4}):(?!0)(\\d{1,4}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5]):([^:]+)(?::(.+))?$", Pattern.MULTILINE);
// https://regex101.com/r/7M2LFx/1
public static final Pattern PROXY_PATTERN_URI = Pattern.compile("^(?:(socks|socks4|socks5)://)?(?:(?<user>[\\w~-]+)(:(?<pass>[\\w~-]+))?@)?(?<addr>(?:0*(?:\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])(?:\\.(?!:)|)){4}):(?!0)(?<port>\\d{1,4}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$", Pattern.MULTILINE);
// https://regex101.com/r/swN1Ya/1
public static final Pattern PROXY_PATTERN = Pattern.compile("^(?:(?<name>[\\w\\s]+)=)?(?<address>(?:0*(?:\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])(?:\\.(?!\\:)|)){4})\\:(?!0)(?<port>\\d{1,4}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])(@(?<type>http|socks[45])(?<secure>s)?)?$", Pattern.CASE_INSENSITIVE);
// https://regex101.com/r/rPqeik/1
public static final Pattern PROXY_PATTERN_WEBSHARE = Pattern.compile("^(?<address>(?:0*(?:\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])(?:\\.(?!\\:)|)){4})\\:(?!0)(?<port>\\d{1,4}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])(?:\\:(?<username>[^:]+)(?:\\:(?<password>.+))?)$", Pattern.CASE_INSENSITIVE);
// https://regex101.com/r/bGQd0X/1
public static final Pattern PROXY_PATTERN_URI = Pattern.compile("^(?:(?<type>http|socks[45])(?<secure>s)?\\:\\/\\/)?(?:(?<username>[^:]+)(\\:(?<password>[^:]+))?\\@)?(?<address>(?:0*(?:\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])(?:\\.(?!\\:)|)){4})\\:(?!0)(?<port>\\d{1,4}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$", Pattern.CASE_INSENSITIVE);

private List<Proxy> proxies = new ArrayList<>();
public boolean refreshing;
Expand All @@ -100,7 +100,7 @@ public static Proxies get() {

public boolean add(Proxy proxy) {
for (Proxy p : proxies) {
if (p.type.get().equals(proxy.type.get()) && p.address.get().equals(proxy.address.get()) && Objects.equals(p.port.get(), proxy.port.get()))
if (p.type.get().equals(proxy.type.get()) && p.secure.get().equals(proxy.secure.get()) && p.address.get().equals(proxy.address.get()) && Objects.equals(p.port.get(), proxy.port.get()))
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class Proxy implements ISerializable<Proxy> {
public final Settings settings = new Settings();
Expand All @@ -39,7 +42,14 @@ public class Proxy implements ISerializable<Proxy> {
public Setting<ProxyType> type = sgGeneral.add(new EnumSetting.Builder<ProxyType>()
.name("type")
.description("The type of proxy.")
.defaultValue(ProxyType.Socks5)
.defaultValue(ProxyType.SOCKS5)
.build()
);

public Setting<Boolean> secure = sgGeneral.add(new BoolSetting.Builder()
.name("secure")
.description("Whether the proxy is secure.")
.defaultValue(false)
.build()
);

Expand Down Expand Up @@ -78,7 +88,7 @@ public class Proxy implements ISerializable<Proxy> {
public Setting<String> password = sgOptional.add(new StringSetting.Builder()
.name("password")
.description("The password of the proxy.")
.visible(() -> type.get().equals(ProxyType.Socks5))
.visible(() -> !type.get().equals(ProxyType.SOCKS4))
.build()
);

Expand Down Expand Up @@ -138,10 +148,32 @@ public int checkStatus() {
} catch (IOException _) {
}

try {
Instant before = Instant.now();
if (isHttp()) {
status = Status.ALIVE;
latency = Duration.between(before, Instant.now()).toMillis();
return 1;
}
}
catch (SocketTimeoutException e) {
timeout = true;
}
catch (IOException ignored) {}

status = Status.DEAD;
return timeout ? 3 : 2;
}

private boolean isHttp() throws IOException {
String request = "CONNECT 0.0.0.0:80 HTTP/1.1\r\nHost: 0.0.0.0:80\r\n\r\n";

byte[] data = sendData(request.getBytes(StandardCharsets.UTF_8), 12);

if (data.length < 12) return false;
return data[0] == 'H' && data[1] == 'T' && data[2] == 'T' && data[3] == 'P' && data[4] == '/';
}

private boolean isSocks4() throws IOException {
ByteBuffer bb;
byte[] u = username.get().getBytes();
Expand Down Expand Up @@ -191,19 +223,34 @@ private boolean isSocks5() throws IOException {
}

private byte[] sendData(byte[] data, int read) throws IOException {
try (Socket s = new Socket()) {
s.setSoTimeout(Proxies.get().timeout.get());
s.connect(new InetSocketAddress(address.get(), port.get()), Proxies.get().timeout.get());
OutputStream out = s.getOutputStream();
if (secure.get()) {
SSLSocketFactory factory = (SSLSocketFactory)SSLSocketFactory.getDefault();
try (SSLSocket s = (SSLSocket)factory.createSocket()) {
s.setSoTimeout(Proxies.get().timeout.get());
s.connect(new InetSocketAddress(address.get(), port.get()), Proxies.get().timeout.get());
s.startHandshake();

OutputStream out = s.getOutputStream();
out.write(data);

out.write(data);
return s.getInputStream().readNBytes(read);
}
} else {
try (Socket s = new Socket()) {
s.setSoTimeout(Proxies.get().timeout.get());
s.connect(new InetSocketAddress(address.get(), port.get()), Proxies.get().timeout.get());

OutputStream out = s.getOutputStream();
out.write(data);

return s.getInputStream().readNBytes(read);
return s.getInputStream().readNBytes(read);
}
}
}

public static class Builder {
protected ProxyType type = ProxyType.Socks5;
protected ProxyType type = ProxyType.SOCKS5;
protected boolean secure = false;
protected String address = "";
protected int port = 0;
protected String name = "";
Expand All @@ -216,6 +263,11 @@ public Builder type(ProxyType type) {
return this;
}

public Builder secure(boolean secure) {
this.secure = secure;
return this;
}

public Builder address(String address) {
this.address = address;
return this;
Expand Down Expand Up @@ -250,6 +302,7 @@ public Proxy build() {
Proxy proxy = new Proxy();

if (!type.equals(proxy.type.getDefaultValue())) proxy.type.set(type);
if (secure != proxy.secure.getDefaultValue()) proxy.secure.set(secure);
if (!address.equals(proxy.address.getDefaultValue())) proxy.address.set(address);
if (port != proxy.port.getDefaultValue()) proxy.port.set(port);
if (!name.equals(proxy.name.getDefaultValue())) proxy.name.set(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
import org.jetbrains.annotations.Nullable;

public enum ProxyType {
Socks4,
Socks5;
HTTP,
SOCKS5,
SOCKS4;

@Nullable
public static ProxyType parse(String group) {
Expand Down