Compare commits
2 Commits
2027806e0c
...
5e66dccc29
Author | SHA1 | Date | |
---|---|---|---|
5e66dccc29 | |||
53a88b6d99 |
22
pom.xml
22
pom.xml
@ -33,6 +33,19 @@
|
|||||||
<target>16</target>
|
<target>16</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.2.4</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
@ -50,6 +63,15 @@
|
|||||||
<version>1.16.4-R0.1-SNAPSHOT</version>
|
<version>1.16.4-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<!--<groupId>com.sshtools</groupId>
|
||||||
|
<artifactId>maverick-synergy-client</artifactId>
|
||||||
|
<version>3.1.1</version> -->
|
||||||
|
<groupId>org.apache.sshd</groupId>
|
||||||
|
<artifactId>sshd-core</artifactId>
|
||||||
|
<version>2.14.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -3,6 +3,7 @@ package fi.flexplex.connect;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -11,10 +12,13 @@ import org.bukkit.configuration.file.YamlConfiguration;
|
|||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
import fi.flexplex.connect.util.FileChangeListener;
|
import fi.flexplex.connect.util.FileChangeListener;
|
||||||
|
import fi.flexplex.connect.util.Proxy;
|
||||||
|
import fi.flexplex.connect.util.Tunnel;
|
||||||
|
|
||||||
public final class FlexConnect extends JavaPlugin {
|
public final class FlexConnect extends JavaPlugin {
|
||||||
|
|
||||||
private final Set<String> proxyAddresses = new HashSet<>();
|
private final Set<Tunnel> tunnels = new HashSet<>();
|
||||||
|
private final Set<Proxy> proxies = new HashSet<>();
|
||||||
|
|
||||||
private String token = "";
|
private String token = "";
|
||||||
private FlexPlexGraphQLApi flexPlexGraphQLApi;
|
private FlexPlexGraphQLApi flexPlexGraphQLApi;
|
||||||
@ -27,8 +31,13 @@ public final class FlexConnect extends JavaPlugin {
|
|||||||
return this.flexPlexGraphQLApi;
|
return this.flexPlexGraphQLApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getProxyAddresses() {
|
public boolean isIpAllowedToJoin(final String ip) {
|
||||||
return this.proxyAddresses;
|
for (final Proxy proxy : this.proxies) {
|
||||||
|
if (proxy.ipAllowedToJoin(ip)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -42,7 +51,11 @@ public final class FlexConnect extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GraphQL Api
|
// GraphQL Api
|
||||||
|
try {
|
||||||
this.flexPlexGraphQLApi = new FlexPlexGraphQLApi(this, "https://api.flexplex.fi/graphql");
|
this.flexPlexGraphQLApi = new FlexPlexGraphQLApi(this, "https://api.flexplex.fi/graphql");
|
||||||
|
} catch (final MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
boolean configsModified = false;
|
boolean configsModified = false;
|
||||||
|
|
||||||
@ -95,12 +108,30 @@ public final class FlexConnect extends JavaPlugin {
|
|||||||
// Update whitelists for flexplex api
|
// Update whitelists for flexplex api
|
||||||
this.flexPlexGraphQLApi.updateWhitelist();
|
this.flexPlexGraphQLApi.updateWhitelist();
|
||||||
|
|
||||||
// Load real FlexPlex proxy addresses from FlexPlex API
|
|
||||||
this.proxyAddresses.addAll(this.flexPlexGraphQLApi.getProxyAddresses());
|
|
||||||
|
|
||||||
// Load allowed proxy addresses from config
|
// Load allowed proxy addresses from config
|
||||||
this.proxyAddresses.addAll(this.getConfig().getStringList("allowedProxies"));
|
for (final String host : this.getConfig().getStringList("allowedProxies")) {
|
||||||
|
this.proxies.add(new Proxy(host));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load tunnels and proxy addresses from FlexPlex API and start tunnels
|
||||||
|
// Load proxies from FlexPlex API
|
||||||
|
this.proxies.addAll(this.flexPlexGraphQLApi.getProxies());
|
||||||
|
|
||||||
|
// Open tunnels if there is any available
|
||||||
|
for (final Proxy proxy : this.proxies) {
|
||||||
|
if (proxy.hasTunnel()) {
|
||||||
|
final Tunnel tunnel = new Tunnel(this, proxy);
|
||||||
|
this.tunnels.add(tunnel);
|
||||||
|
tunnel.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
for (final Tunnel tunnel : this.tunnels) {
|
||||||
|
tunnel.stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,13 +20,15 @@ public final class FlexConnectListener implements Listener {
|
|||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerLogin(final PlayerLoginEvent event) {
|
public void onPlayerLogin(final PlayerLoginEvent event) {
|
||||||
if (flexConnect.getProxyAddresses().contains(event.getRealAddress().getHostAddress())) {
|
final String ip = event.getRealAddress().getHostAddress();
|
||||||
|
if (this.flexConnect.isIpAllowedToJoin(ip)) {
|
||||||
// Connection is coming from allowed proxy
|
// Connection is coming from allowed proxy
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not allow connections from other proxies
|
// Do not allow connections from other proxies
|
||||||
event.disallow(PlayerLoginEvent.Result.KICK_OTHER, "Access denied");
|
event.disallow(PlayerLoginEvent.Result.KICK_OTHER, "Access denied");
|
||||||
|
flexConnect.getLogger().warning("Connecting blocked from " + ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||||
|
@ -4,6 +4,8 @@ import java.io.DataOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -14,23 +16,24 @@ import com.google.gson.JsonArray;
|
|||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
|
import fi.flexplex.connect.util.Proxy;
|
||||||
|
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
|
|
||||||
public final class FlexPlexGraphQLApi {
|
public final class FlexPlexGraphQLApi {
|
||||||
|
|
||||||
private final String graphqlEndpoint;
|
private final URL graphqlEndpoint;
|
||||||
private final FlexConnect flexConnect;
|
private final FlexConnect flexConnect;
|
||||||
|
|
||||||
public FlexPlexGraphQLApi(final FlexConnect flexConnect, final String graphQLEndpoint) {
|
public FlexPlexGraphQLApi(final FlexConnect flexConnect, final String graphQLEndpoint) throws MalformedURLException {
|
||||||
this.flexConnect = flexConnect;
|
this.flexConnect = flexConnect;
|
||||||
this.graphqlEndpoint = graphQLEndpoint;
|
this.graphqlEndpoint = URI.create(graphQLEndpoint).toURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getProxyAddresses() {
|
public Set<Proxy> getProxies() {
|
||||||
final Set<String> output = new HashSet<>();
|
final Set<Proxy> output = new HashSet<>();
|
||||||
try {
|
try {
|
||||||
final URL url = new URL(graphqlEndpoint);
|
final HttpURLConnection conn = (HttpURLConnection) graphqlEndpoint.openConnection();
|
||||||
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
|
||||||
conn.setDoOutput(true);
|
conn.setDoOutput(true);
|
||||||
conn.setDoInput(true);
|
conn.setDoInput(true);
|
||||||
conn.setConnectTimeout(10000);
|
conn.setConnectTimeout(10000);
|
||||||
@ -40,22 +43,26 @@ public final class FlexPlexGraphQLApi {
|
|||||||
|
|
||||||
final DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
|
final DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
|
||||||
|
|
||||||
wr.writeBytes("{\"query\":\"query { proxyAddresses }\"}");
|
wr.writeBytes("{\"query\":\"query{proxies(token:\\\"" + this.flexConnect.getToken() + "\\\"){host,tunnelPort,forwardingPort}}\"}");
|
||||||
wr.flush();
|
wr.flush();
|
||||||
wr.close();
|
wr.close();
|
||||||
|
|
||||||
final JsonObject response = (JsonObject) new JsonParser().parse(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
|
final JsonObject response = (JsonObject) new JsonParser().parse(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
|
||||||
|
|
||||||
if (response.get("data").getAsJsonObject().has("proxyAddresses")) {
|
if (response.get("data").getAsJsonObject().has("proxies")) {
|
||||||
final JsonArray array = response.get("data").getAsJsonObject().get("proxyAddresses").getAsJsonArray();
|
final JsonArray array = response.get("data").getAsJsonObject().get("proxies").getAsJsonArray();
|
||||||
for (int i = 0; i < array.size(); i++) {
|
for (int i = 0; i < array.size(); i++) {
|
||||||
output.add(array.get(i).getAsString());
|
final JsonObject proxy = array.get(i).getAsJsonObject();
|
||||||
|
final Proxy p = new Proxy(proxy.get("host").getAsString());
|
||||||
|
p.setForwardingPort(proxy.get("forwardingPort").getAsInt());
|
||||||
|
p.setTunnelPort(proxy.get("tunnelPort").getAsInt());
|
||||||
|
output.add(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.disconnect();
|
conn.disconnect();
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
this.flexConnect.getLogger().warning("Updating whitelist data failed, propably connection to FlexPlex API server is down.");
|
this.flexConnect.getLogger().warning("Getting proxies failed, propably connection to FlexPlex API server is down.");
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@ -82,8 +89,7 @@ public final class FlexPlexGraphQLApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final URL url = new URL(graphqlEndpoint);
|
final HttpURLConnection conn = (HttpURLConnection) graphqlEndpoint.openConnection();
|
||||||
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
|
||||||
conn.setDoOutput(true);
|
conn.setDoOutput(true);
|
||||||
conn.setDoInput(true);
|
conn.setDoInput(true);
|
||||||
conn.setConnectTimeout(10000);
|
conn.setConnectTimeout(10000);
|
||||||
|
45
src/main/java/fi/flexplex/connect/util/Proxy.java
Normal file
45
src/main/java/fi/flexplex/connect/util/Proxy.java
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package fi.flexplex.connect.util;
|
||||||
|
|
||||||
|
public final class Proxy {
|
||||||
|
|
||||||
|
private final String host;
|
||||||
|
private int tunnelPort = -1;
|
||||||
|
private int forwardingPort = -1;
|
||||||
|
|
||||||
|
public Proxy(final String host) {
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHost() {
|
||||||
|
return this.host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTunnelPort() {
|
||||||
|
return this.tunnelPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getForwardingPort() {
|
||||||
|
return this.forwardingPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTunnelPort(final int port) {
|
||||||
|
this.tunnelPort = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setForwardingPort(final int port) {
|
||||||
|
this.forwardingPort = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasTunnel() {
|
||||||
|
return this.tunnelPort != -1 && this.forwardingPort != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean ipAllowedToJoin(final String ip) {
|
||||||
|
if (this.hasTunnel()) {
|
||||||
|
return "0:0:0:0:0:0:0:1%0".equals(ip);
|
||||||
|
} else {
|
||||||
|
return ip.equals(this.host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
83
src/main/java/fi/flexplex/connect/util/Tunnel.java
Normal file
83
src/main/java/fi/flexplex/connect/util/Tunnel.java
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package fi.flexplex.connect.util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.sshd.client.SshClient;
|
||||||
|
import org.apache.sshd.client.future.ConnectFuture;
|
||||||
|
import org.apache.sshd.client.session.ClientSession;
|
||||||
|
import org.apache.sshd.client.session.forward.PortForwardingTracker;
|
||||||
|
import org.apache.sshd.common.util.net.SshdSocketAddress;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import fi.flexplex.connect.FlexConnect;
|
||||||
|
|
||||||
|
public final class Tunnel {
|
||||||
|
|
||||||
|
private final FlexConnect flexConnect;
|
||||||
|
private final Proxy proxy;
|
||||||
|
private final Thread thread;
|
||||||
|
private final SshClient client = SshClient.setUpDefaultClient();
|
||||||
|
private boolean isRunning = false;
|
||||||
|
|
||||||
|
public Tunnel(final FlexConnect flexConnect, final Proxy proxy) {
|
||||||
|
this.flexConnect = flexConnect;
|
||||||
|
this.proxy = proxy;
|
||||||
|
this.client.setForwardingFilter(new TunnelForwardingFilter());
|
||||||
|
this.thread = new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (isRunning) {
|
||||||
|
session();
|
||||||
|
flexConnect.getLogger().warning("Tunnel session ended. Trying to reconnect in 10 seconds");
|
||||||
|
try {
|
||||||
|
Thread.sleep(10_000);
|
||||||
|
} catch (final InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
this.isRunning = true;
|
||||||
|
this.thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
this.isRunning = false;
|
||||||
|
this.client.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void session() {
|
||||||
|
this.flexConnect.getLogger().info("Tunnel connecting to " + this.proxy.getHost() + ":" + this.proxy.getTunnelPort());
|
||||||
|
try {
|
||||||
|
client.start();
|
||||||
|
|
||||||
|
final ConnectFuture cf = client.connect("FlexConnect", this.proxy.getHost(), this.proxy.getTunnelPort());
|
||||||
|
final ClientSession session = cf.verify().getSession();
|
||||||
|
|
||||||
|
session.addPasswordIdentity(flexConnect.getToken());
|
||||||
|
session.auth().verify(TimeUnit.SECONDS.toMillis(5));
|
||||||
|
|
||||||
|
final SshdSocketAddress remote = new SshdSocketAddress("127.0.0.1", this.proxy.getForwardingPort());
|
||||||
|
final SshdSocketAddress local = new SshdSocketAddress(Bukkit.getPort());
|
||||||
|
|
||||||
|
try (PortForwardingTracker tracker = session.createRemotePortForwardingTracker(remote, local)) {
|
||||||
|
this.flexConnect.getLogger().info("Tunnel successfully connected " + this.proxy.getHost() + ":" + this.proxy.getTunnelPort());
|
||||||
|
while (isRunning) {
|
||||||
|
Thread.sleep(5000);
|
||||||
|
if (session.isClosed()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (final InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
} catch (final IOException e) {
|
||||||
|
this.flexConnect.getLogger().warning(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package fi.flexplex.connect.util;
|
||||||
|
|
||||||
|
import org.apache.sshd.common.session.Session;
|
||||||
|
import org.apache.sshd.common.util.net.SshdSocketAddress;
|
||||||
|
import org.apache.sshd.server.forward.ForwardingFilter;
|
||||||
|
|
||||||
|
public final class TunnelForwardingFilter implements ForwardingFilter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canForwardAgent(final Session session, final String requestType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canForwardX11(final Session session, final String requestType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canListen(final SshdSocketAddress address, final Session session) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canConnect(final Type type, final SshdSocketAddress address, final Session session) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user