Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-12-24 15:20:35 +01:00
Velocity Dump WIP Part 2
Dieser Commit ist enthalten in:
Ursprung
6331e1af3e
Commit
140eaaf5ab
@ -5,7 +5,11 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.command.SimpleCommand;
|
||||
import com.velocitypowered.api.permission.Tristate;
|
||||
@ -17,6 +21,12 @@ import com.velocitypowered.api.util.ProxyVersion;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.util.InformationUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@ -320,9 +330,18 @@ public class VelocityCommand implements SimpleCommand {
|
||||
servers.add(iter.getServerInfo().getName(),
|
||||
InformationUtils.collectServerInfo(iter));
|
||||
}
|
||||
JsonArray connectOrder = new JsonArray();
|
||||
List<String> attemptedConnectionOrder = ImmutableList.copyOf(
|
||||
server.getConfiguration().getAttemptConnectionOrder());
|
||||
for (int i = 0; i < attemptedConnectionOrder.size(); i++) {
|
||||
connectOrder.add(attemptedConnectionOrder.get(i));
|
||||
}
|
||||
|
||||
JsonObject proxyConfig = InformationUtils.collectProxyConfig(server.getConfiguration());
|
||||
proxyConfig.add("servers", servers);
|
||||
proxyConfig.add("connectOrder", connectOrder);
|
||||
proxyConfig.add("forcedHosts",
|
||||
InformationUtils.collectForcedHosts(server.getConfiguration()));
|
||||
|
||||
JsonObject dump = new JsonObject();
|
||||
dump.add("versionInfo", InformationUtils.collectProxyInfo(server.getVersion()));
|
||||
@ -330,7 +349,90 @@ public class VelocityCommand implements SimpleCommand {
|
||||
dump.add("config", proxyConfig);
|
||||
dump.add("plugins", InformationUtils.collectPluginInfo(server));
|
||||
|
||||
// TODO: Finish
|
||||
source.sendMessage(Component.text().content("Uploading gathered information...").build());
|
||||
|
||||
HttpURLConnection upload = null;
|
||||
try {
|
||||
upload = (HttpURLConnection) new URL("https://dump.velocitypowered.com/documents")
|
||||
.openConnection();
|
||||
} catch (IOException e1) {
|
||||
// Couldn't open connection;
|
||||
source.sendMessage(
|
||||
Component.text()
|
||||
.content("Failed to open a connection!")
|
||||
.color(NamedTextColor.RED).build());
|
||||
return;
|
||||
}
|
||||
upload.setRequestProperty("Content-Type", "text/plain");
|
||||
upload.addRequestProperty(
|
||||
"User-Agent", server.getVersion().getName() + "/"
|
||||
+ server.getVersion().getVersion());
|
||||
try {
|
||||
upload.setRequestMethod("POST");
|
||||
upload.setDoOutput(true);
|
||||
|
||||
OutputStream uploadStream = upload.getOutputStream();
|
||||
uploadStream.write(
|
||||
InformationUtils.toHumanReadableString(dump).getBytes(StandardCharsets.UTF_8));
|
||||
uploadStream.close();
|
||||
} catch (IOException e2) {
|
||||
// Couldn't POST the Data
|
||||
source.sendMessage(
|
||||
Component.text()
|
||||
.content("Couldn't upload the data!")
|
||||
.color(NamedTextColor.RED).build());
|
||||
return;
|
||||
}
|
||||
String rawResponse = null;
|
||||
try {
|
||||
rawResponse = CharStreams.toString(
|
||||
new InputStreamReader(upload.getInputStream(), StandardCharsets.UTF_8));
|
||||
upload.getInputStream().close();
|
||||
} catch (IOException e3) {
|
||||
// Couldn't read response
|
||||
source.sendMessage(
|
||||
Component.text()
|
||||
.content("Invalid server response received!")
|
||||
.color(NamedTextColor.RED).build());
|
||||
}
|
||||
JsonObject returned = null;
|
||||
try {
|
||||
returned = InformationUtils.parseString(rawResponse);
|
||||
if (returned == null || !returned.has("key")) {
|
||||
throw new JsonParseException("Invalid json response");
|
||||
}
|
||||
} catch (JsonSyntaxException e4) {
|
||||
// Mangled json
|
||||
source.sendMessage(
|
||||
Component.text()
|
||||
.content("Server responded with invalid data!")
|
||||
.color(NamedTextColor.RED).build());
|
||||
return;
|
||||
} catch (JsonParseException e5) {
|
||||
// Backend error?
|
||||
source.sendMessage(
|
||||
Component.text()
|
||||
.content("Data was uploaded successfully but couldn't be posted")
|
||||
.color(NamedTextColor.RED).build());
|
||||
return;
|
||||
}
|
||||
TextComponent response = Component.text()
|
||||
.content("Created an anonymised report containing useful information about")
|
||||
.append(Component.newline()
|
||||
.append(
|
||||
Component.text("this proxy. If a developer requested it"
|
||||
+ ", you may share the"))
|
||||
.append(Component.newline())
|
||||
.append(Component.text("following link with them:"))
|
||||
.append(Component.newline())
|
||||
.append(Component.text("https://dump.velocitypowered.com/"
|
||||
+ returned.get("key").getAsString() + ".json")
|
||||
.color(NamedTextColor.GREEN)))
|
||||
.append(Component.newline())
|
||||
.append(Component.text("Note: This link is only valid for a few days"))
|
||||
.build();
|
||||
source.sendMessage(response);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.velocitypowered.proxy.util;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
@ -16,9 +17,15 @@ import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.util.ProxyVersion;
|
||||
|
||||
import io.netty.channel.unix.DomainSocketAddress;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import joptsimple.internal.Strings;
|
||||
|
||||
public enum InformationUtils {
|
||||
@ -84,6 +91,75 @@ public enum InformationUtils {
|
||||
return envInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link JsonObject} containing information about the
|
||||
* forced hosts of the {@link ProxyConfig} instance.
|
||||
*
|
||||
* @return {@link JsonArray} containing forced hosts
|
||||
*/
|
||||
public static JsonObject collectForcedHosts(ProxyConfig config) {
|
||||
JsonObject forcedHosts = new JsonObject();
|
||||
Map<String, List<String>> allForcedHosts = ImmutableMap.copyOf(
|
||||
config.getForcedHosts());
|
||||
for (Map.Entry<String, List<String>> entry : allForcedHosts.entrySet()) {
|
||||
JsonArray host = new JsonArray();
|
||||
for (int i = 0; i < entry.getValue().size(); i++) {
|
||||
host.add(entry.getValue().get(i));
|
||||
}
|
||||
forcedHosts.add(entry.getKey(), host);
|
||||
}
|
||||
return forcedHosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonymises or redacts a given {@link InetAddress}
|
||||
* public address bits.
|
||||
*
|
||||
* @param address The address to redact
|
||||
* @return {@link String} address with public parts redacted
|
||||
*/
|
||||
public static String anonymizeInetAddress(InetAddress address) {
|
||||
if (address instanceof Inet4Address) {
|
||||
Inet4Address v4 = (Inet4Address) address;
|
||||
if (v4.isAnyLocalAddress() || v4.isLoopbackAddress()
|
||||
|| v4.isLinkLocalAddress()
|
||||
|| v4.isSiteLocalAddress()) {
|
||||
return address.getHostAddress();
|
||||
} else {
|
||||
byte[] addr = v4.getAddress();
|
||||
return (addr[0] & 0xff) + "." + (addr[1] & 0xff) + ".XXX.XXX";
|
||||
}
|
||||
} else if (address instanceof Inet6Address) {
|
||||
Inet6Address v6 = (Inet6Address) address;
|
||||
if (v6.isAnyLocalAddress() || v6.isLoopbackAddress()
|
||||
|| v6.isSiteLocalAddress()
|
||||
|| v6.isSiteLocalAddress()) {
|
||||
return address.getHostAddress();
|
||||
} else {
|
||||
String[] bits = v6.getHostAddress().split(":");
|
||||
String ret = "";
|
||||
boolean flag = false;
|
||||
for (int iter = 0; iter < bits.length; iter++) {
|
||||
if (flag) {
|
||||
ret += ":X";
|
||||
continue;
|
||||
}
|
||||
if (!bits[iter].equals("0")) {
|
||||
if (iter == 0) {
|
||||
ret = bits[iter];
|
||||
} else {
|
||||
ret = "::" + bits[iter];
|
||||
}
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
return address.getHostAddress();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link JsonObject} containing most relevant
|
||||
* information of the {@link RegisteredServer} for diagnosis.
|
||||
@ -97,18 +173,22 @@ public enum InformationUtils {
|
||||
SocketAddress address = server.getServerInfo().getAddress();
|
||||
if (address instanceof InetSocketAddress) {
|
||||
InetSocketAddress iaddr = (InetSocketAddress) address;
|
||||
info.addProperty("socketType", "EventLoop");
|
||||
info.addProperty("socketType", "EventLoop/NIO");
|
||||
info.addProperty("unresolved", iaddr.isUnresolved());
|
||||
// Greetings form Netty 4aa10db9
|
||||
info.addProperty("host", iaddr.getHostString());
|
||||
if (iaddr.isUnresolved()) {
|
||||
// Greetings form Netty 4aa10db9
|
||||
info.addProperty("host", iaddr.getHostString());
|
||||
} else {
|
||||
info.addProperty("host", anonymizeInetAddress(iaddr.getAddress()));
|
||||
}
|
||||
info.addProperty("port", iaddr.getPort());
|
||||
} else if (address instanceof DomainSocketAddress) {
|
||||
DomainSocketAddress daddr = (DomainSocketAddress) address;
|
||||
info.addProperty("socketType", "Unix/Epoll");
|
||||
info.addProperty("host", daddr.path());
|
||||
info.addProperty("path", daddr.path());
|
||||
} else {
|
||||
info.addProperty("socketType", "Unknown/Generic");
|
||||
info.addProperty("host", address.toString());
|
||||
info.addProperty("info", address.toString());
|
||||
}
|
||||
return info;
|
||||
}
|
||||
@ -135,6 +215,26 @@ public enum InformationUtils {
|
||||
return (JsonObject) serializeObject(config, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a human-readable String from a {@link JsonElement}.
|
||||
*
|
||||
* @param json the {@link JsonElement} object
|
||||
* @return the human-readable String
|
||||
*/
|
||||
public static String toHumanReadableString(JsonElement json) {
|
||||
return GSON_WITHOUT_EXCLUDES.toJson(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link JsonObject} from a String.
|
||||
*
|
||||
* @param toParse the String to parse
|
||||
* @return {@link JsonObject} object
|
||||
*/
|
||||
public static JsonObject parseString(String toParse) {
|
||||
return GSON_WITHOUT_EXCLUDES.fromJson(toParse, JsonObject.class);
|
||||
}
|
||||
|
||||
private static JsonElement serializeObject(Object toSerialize, boolean withExcludes) {
|
||||
return JsonParser.parseString(
|
||||
withExcludes ? GSON_WITH_EXCLUDES.toJson(toSerialize) :
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren