Allow accessing Mojang with proxy

Implement #41
This commit is contained in:
yushijinhun 2019-02-05 16:31:49 +08:00
parent 90d37f9d0f
commit e8a13e7ae2
No known key found for this signature in database
GPG key ID: 5BC167F73EA558E4
5 changed files with 80 additions and 24 deletions

View file

@ -28,9 +28,12 @@ import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.instrument.Instrumentation;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.Proxy.Type;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
@ -41,6 +44,8 @@ import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import moe.yushi.authlibinjector.httpd.DefaultURLRedirector;
@ -105,6 +110,11 @@ public final class AuthlibInjector {
*/
public static final String PROP_PRINT_UNTRANSFORMED_CLASSES = "authlibinjector.printUntransformed";
/**
* The proxy to use when accessing Mojang's APIs.
*/
public static final String PROP_MOJANG_PROXY = "authlibinjector.mojang.proxy";
/**
* The side that authlib-injector runs on.
* Possible values: client, server.
@ -322,7 +332,7 @@ public final class AuthlibInjector {
List<URLFilter> filters = new ArrayList<>();
YggdrasilClient customClient = new YggdrasilClient(new CustomYggdrasilAPIProvider(config));
YggdrasilClient mojangClient = new YggdrasilClient(new MojangYggdrasilAPIProvider());
YggdrasilClient mojangClient = new YggdrasilClient(new MojangYggdrasilAPIProvider(), getMojangProxy());
if (Boolean.TRUE.equals(config.getMeta().get("feature.legacy_skin_api"))) {
Logging.CONFIG.info("Disabled local redirect for legacy skin API, as the remote Yggdrasil server supports it");
@ -336,6 +346,35 @@ public final class AuthlibInjector {
return filters;
}
private static Proxy getMojangProxy() {
String proxyString = System.getProperty(PROP_MOJANG_PROXY);
if (proxyString == null) {
return null;
}
Matcher matcher = Pattern.compile("^(?<protocol>[^:]+)://(?<host>[^/]+)+:(?<port>\\d+)$").matcher(proxyString);
if (!matcher.find()) {
Logging.LAUNCH.severe("Failed to parse proxy string: " + proxyString);
throw new InjectorInitializationException();
}
String protocol = matcher.group("protocol");
String host = matcher.group("host");
int port = Integer.parseInt(matcher.group("port"));
Proxy proxy;
switch (protocol) {
case "socks":
proxy = new Proxy(Type.SOCKS, new InetSocketAddress(host, port));
break;
default:
Logging.LAUNCH.severe("Unsupported proxy protocol: " + protocol);
throw new InjectorInitializationException();
}
Logging.LAUNCH.info("Mojang proxy set: " + proxy);
return proxy;
}
private static ClassTransformer createTransformer(YggdrasilConfiguration config) {
URLProcessor urlProcessor = new URLProcessor(createFilters(config), new DefaultURLRedirector(config));

View file

@ -21,14 +21,6 @@ public class InjectorInitializationException extends RuntimeException {
public InjectorInitializationException() {
}
public InjectorInitializationException(String message, Throwable cause) {
super(message, cause);
}
public InjectorInitializationException(String message) {
super(message);
}
public InjectorInitializationException(Throwable cause) {
super(cause);
}

View file

@ -20,7 +20,7 @@ import static java.util.Optional.empty;
import static java.util.Optional.of;
import static java.util.Optional.ofNullable;
import static moe.yushi.authlibinjector.util.IOUtils.asString;
import static moe.yushi.authlibinjector.util.IOUtils.getURL;
import static moe.yushi.authlibinjector.util.IOUtils.http;
import static moe.yushi.authlibinjector.util.IOUtils.newUncheckedIOException;
import static moe.yushi.authlibinjector.util.JsonUtils.asJsonObject;
import static moe.yushi.authlibinjector.util.JsonUtils.parseJson;
@ -81,7 +81,7 @@ public class LegacySkinAPIFilter implements URLFilter {
Logging.HTTPD.fine("Retrieving skin for " + username + " from " + url);
byte[] data;
try {
data = getURL(url);
data = http("GET", url);
} catch (IOException e) {
throw newUncheckedIOException("Failed to retrieve skin from " + url, e);
}

View file

@ -23,24 +23,43 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
public final class IOUtils {
public static final String CONTENT_TYPE_JSON = "application/json; charset=utf-8";
public static byte[] getURL(String url) throws IOException {
try (InputStream in = new URL(url).openStream()) {
private static HttpURLConnection createConnection(String url, Proxy proxy) throws IOException {
if (proxy == null) {
return (HttpURLConnection) new URL(url).openConnection();
} else {
return (HttpURLConnection) new URL(url).openConnection(proxy);
}
}
public static byte[] http(String method, String url) throws IOException {
return http(method, url, null);
}
public static byte[] http(String method, String url, Proxy proxy) throws IOException {
HttpURLConnection conn = createConnection(url, proxy);
conn.setRequestMethod(method);
try (InputStream in = conn.getInputStream()) {
return asBytes(in);
}
}
public static byte[] postURL(String url, String contentType, byte[] payload) throws IOException {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", contentType);
conn.setRequestProperty("Content-Length", String.valueOf(payload.length));
public static byte[] http(String method, String url, byte[] payload, String contentType) throws IOException {
return http(method, url, payload, contentType, null);
}
public static byte[] http(String method, String url, byte[] payload, String contentType, Proxy proxy) throws IOException {
HttpURLConnection conn = createConnection(url, proxy);
conn.setRequestMethod(method);
conn.setDoOutput(true);
conn.setFixedLengthStreamingMode(payload.length);
conn.setRequestProperty("Content-Type", contentType);
try (OutputStream out = conn.getOutputStream()) {
out.write(payload);
}

View file

@ -20,9 +20,8 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.singleton;
import static moe.yushi.authlibinjector.util.IOUtils.CONTENT_TYPE_JSON;
import static moe.yushi.authlibinjector.util.IOUtils.asString;
import static moe.yushi.authlibinjector.util.IOUtils.getURL;
import static moe.yushi.authlibinjector.util.IOUtils.http;
import static moe.yushi.authlibinjector.util.IOUtils.newUncheckedIOException;
import static moe.yushi.authlibinjector.util.IOUtils.postURL;
import static moe.yushi.authlibinjector.util.JsonUtils.asJsonArray;
import static moe.yushi.authlibinjector.util.JsonUtils.asJsonObject;
import static moe.yushi.authlibinjector.util.JsonUtils.asJsonString;
@ -31,6 +30,7 @@ import static moe.yushi.authlibinjector.util.UUIDUtils.fromUnsignedUUID;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.Proxy;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
@ -45,17 +45,23 @@ import moe.yushi.authlibinjector.yggdrasil.GameProfile.PropertyValue;
public class YggdrasilClient {
private YggdrasilAPIProvider apiProvider;
private Proxy proxy;
public YggdrasilClient(YggdrasilAPIProvider apiProvider) {
this(apiProvider, null);
}
public YggdrasilClient(YggdrasilAPIProvider apiProvider, Proxy proxy) {
this.apiProvider = apiProvider;
this.proxy = proxy;
}
public Map<String, UUID> queryUUIDs(Set<String> names) throws UncheckedIOException {
String responseText;
try {
responseText = asString(postURL(
apiProvider.queryUUIDsByNames(), CONTENT_TYPE_JSON,
JSONArray.toJSONString(names).getBytes(UTF_8)));
responseText = asString(http("POST", apiProvider.queryUUIDsByNames(),
JSONArray.toJSONString(names).getBytes(UTF_8), CONTENT_TYPE_JSON,
proxy));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
@ -82,7 +88,7 @@ public class YggdrasilClient {
}
String responseText;
try {
responseText = asString(getURL(url));
responseText = asString(http("GET", url, proxy));
} catch (IOException e) {
throw new UncheckedIOException(e);
}