mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-13 09:26:11 -04:00
dns handling: srv resolving
This commit is contained in:
parent
983c3844dd
commit
04ab1b5254
36
.idea/jarRepositories.xml
generated
36
.idea/jarRepositories.xml
generated
@ -2,34 +2,34 @@
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="jitpack.io" />
|
||||
<option name="name" value="jitpack.io" />
|
||||
<option name="url" value="http://www.jitpack.io" />
|
||||
<option name="id" value="jitpack.io"/>
|
||||
<option name="name" value="jitpack.io"/>
|
||||
<option name="url" value="http://www.jitpack.io"/>
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jitpack.io" />
|
||||
<option name="name" value="jitpack.io" />
|
||||
<option name="url" value="https://www.jitpack.io" />
|
||||
<option name="id" value="jitpack.io"/>
|
||||
<option name="name" value="jitpack.io"/>
|
||||
<option name="url" value="https://www.jitpack.io"/>
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Central Repository" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||
<option name="id" value="central"/>
|
||||
<option name="name" value="Central Repository"/>
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2"/>
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
<option name="id" value="jitpack.io"/>
|
||||
<option name="name" value="jitpack.io"/>
|
||||
<option name="url" value="https://jitpack.io"/>
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jitpack.io" />
|
||||
<option name="name" value="jitpack.io" />
|
||||
<option name="url" value="https://jitpack.io" />
|
||||
<option name="id" value="central"/>
|
||||
<option name="name" value="Maven Central repository"/>
|
||||
<option name="url" value="https://repo1.maven.org/maven2"/>
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
<option name="id" value="jboss.community"/>
|
||||
<option name="name" value="JBoss Community repository"/>
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/"/>
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
10
pom.xml
10
pom.xml
@ -26,8 +26,8 @@
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
<source>14</source>
|
||||
<target>14</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
@ -54,5 +54,11 @@
|
||||
<artifactId>guava</artifactId>
|
||||
<version>29.0-jre</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/dnsjava/dnsjava -->
|
||||
<dependency>
|
||||
<groupId>dnsjava</groupId>
|
||||
<artifactId>dnsjava</artifactId>
|
||||
<version>3.2.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -21,6 +21,7 @@ import de.bixilon.minosoft.logging.Log;
|
||||
import de.bixilon.minosoft.logging.LogLevel;
|
||||
import de.bixilon.minosoft.mojang.api.MojangAccount;
|
||||
import de.bixilon.minosoft.protocol.network.Connection;
|
||||
import de.bixilon.minosoft.protocol.protocol.ConnectionReason;
|
||||
import de.bixilon.minosoft.util.FolderUtil;
|
||||
import de.bixilon.minosoft.util.OSUtil;
|
||||
import de.bixilon.minosoft.util.Util;
|
||||
@ -67,7 +68,7 @@ public class Minosoft {
|
||||
|
||||
checkClientToken();
|
||||
|
||||
Connection c = new Connection(config.getString("debug.host"), config.getInteger("debug.port"));
|
||||
Connection c = new Connection(config.getString("debug.host"));
|
||||
accountList = config.getMojangAccounts();
|
||||
if (accountList.size() == 0) {
|
||||
/*
|
||||
@ -84,7 +85,7 @@ public class Minosoft {
|
||||
Log.mojang("Could not refresh session, you will not be able to join premium servers!");
|
||||
}
|
||||
c.setPlayer(new Player(account));
|
||||
c.connect();
|
||||
c.resolve(ConnectionReason.CONNECT); // resolve dns address and connect
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,17 +32,20 @@ import de.bixilon.minosoft.protocol.packets.serverbound.login.PacketLoginStart;
|
||||
import de.bixilon.minosoft.protocol.packets.serverbound.status.PacketStatusPing;
|
||||
import de.bixilon.minosoft.protocol.packets.serverbound.status.PacketStatusRequest;
|
||||
import de.bixilon.minosoft.protocol.protocol.*;
|
||||
import de.bixilon.minosoft.util.DNSUtil;
|
||||
import de.bixilon.minosoft.util.ServerAddress;
|
||||
import org.xbill.DNS.TextParseException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Connection {
|
||||
final String host;
|
||||
final int port;
|
||||
final ArrayList<ServerAddress> addresses;
|
||||
final Network network;
|
||||
final PacketHandler handler;
|
||||
final PacketSender sender;
|
||||
final ArrayList<ClientboundPacket> handlingQueue;
|
||||
final VelocityHandler velocityHandler = new VelocityHandler(this);
|
||||
ServerAddress address;
|
||||
PluginChannelHandler pluginChannelHandler;
|
||||
Thread handleThread;
|
||||
Version version = Versions.getLowestVersionSupported(); // default
|
||||
@ -50,11 +53,16 @@ public class Connection {
|
||||
Player player;
|
||||
ConnectionState state = ConnectionState.DISCONNECTED;
|
||||
ConnectionReason reason;
|
||||
ConnectionReason nextReason;
|
||||
ConnectionPing connectionStatusPing;
|
||||
|
||||
public Connection(String host, int port) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
public Connection(String hostname) {
|
||||
try {
|
||||
addresses = DNSUtil.getServerAddresses(hostname);
|
||||
} catch (TextParseException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
network = new Network(this);
|
||||
handlingQueue = new ArrayList<>();
|
||||
handler = new PacketHandler(this);
|
||||
@ -65,29 +73,41 @@ public class Connection {
|
||||
* Sends an server ping to the server (player count, motd, ...)
|
||||
*/
|
||||
public void ping() {
|
||||
Log.info(String.format("Pinging server: %s:%d", host, port));
|
||||
Log.info(String.format("Pinging server: %s", address));
|
||||
reason = ConnectionReason.PING;
|
||||
network.connect();
|
||||
network.connect(address);
|
||||
}
|
||||
|
||||
public void resolve(ConnectionReason reason) {
|
||||
address = addresses.get(0);
|
||||
this.nextReason = reason;
|
||||
Log.info(String.format("Trying to connect to %s", address));
|
||||
resolve(address);
|
||||
}
|
||||
|
||||
public void resolve(ServerAddress address) {
|
||||
reason = ConnectionReason.DNS;
|
||||
network.connect(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to connect to the server and login
|
||||
*/
|
||||
public void connect() {
|
||||
Log.info(String.format("Connecting to server: %s:%d", host, port));
|
||||
if (reason == null) {
|
||||
Log.info(String.format("Connecting to server: %s", address));
|
||||
if (reason == null || reason == ConnectionReason.DNS) {
|
||||
// first get version, then login
|
||||
reason = ConnectionReason.GET_VERSION;
|
||||
}
|
||||
network.connect();
|
||||
network.connect(address);
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
public ServerAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
public ArrayList<ServerAddress> getAvailableAddresses() {
|
||||
return addresses;
|
||||
}
|
||||
|
||||
public ConnectionState getConnectionState() {
|
||||
@ -105,7 +125,17 @@ public class Connection {
|
||||
// connection established, starting threads and logging in
|
||||
startHandlingThread();
|
||||
ConnectionState next = ((reason == ConnectionReason.CONNECT) ? ConnectionState.LOGIN : ConnectionState.STATUS);
|
||||
network.sendPacket(new PacketHandshake(getHost(), getPort(), next, (next == ConnectionState.STATUS) ? -1 : getVersion().getProtocolVersion()));
|
||||
if (reason == ConnectionReason.DNS) {
|
||||
// valid hostname found
|
||||
if (nextReason == ConnectionReason.CONNECT) {
|
||||
// connecting, we must get the version first
|
||||
reason = ConnectionReason.GET_VERSION;
|
||||
} else {
|
||||
reason = nextReason;
|
||||
}
|
||||
Log.info(String.format("Connection to %s seems to be okay, connecting...", address));
|
||||
}
|
||||
network.sendPacket(new PacketHandshake(address, next, (next == ConnectionState.STATUS) ? -1 : getVersion().getProtocolVersion()));
|
||||
// after sending it, switch to next state
|
||||
setConnectionState(next);
|
||||
break;
|
||||
@ -128,6 +158,18 @@ public class Connection {
|
||||
// unregister all custom recipes
|
||||
Recipes.removeCustomRecipes();
|
||||
}
|
||||
break;
|
||||
case FAILED:
|
||||
// connect to next hostname, if available
|
||||
int nextIndex = addresses.indexOf(address) + 1;
|
||||
if (addresses.size() > nextIndex) {
|
||||
ServerAddress nextAddress = addresses.get(nextIndex);
|
||||
Log.warn(String.format("Could not connect to %s, trying next hostname: %s", address, nextAddress));
|
||||
this.address = nextAddress;
|
||||
resolve(address);
|
||||
}
|
||||
// else: failed
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ import de.bixilon.minosoft.protocol.packets.clientbound.interfaces.PacketCompres
|
||||
import de.bixilon.minosoft.protocol.packets.clientbound.login.PacketLoginSuccess;
|
||||
import de.bixilon.minosoft.protocol.packets.serverbound.login.PacketEncryptionResponse;
|
||||
import de.bixilon.minosoft.protocol.protocol.*;
|
||||
import de.bixilon.minosoft.util.ServerAddress;
|
||||
import de.bixilon.minosoft.util.Util;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
@ -30,7 +31,9 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Network {
|
||||
@ -51,7 +54,7 @@ public class Network {
|
||||
this.queue = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
public void connect(ServerAddress address) {
|
||||
// wait for data or send until it should disconnect
|
||||
// first send, then receive
|
||||
// something to send it, send it
|
||||
@ -61,7 +64,9 @@ public class Network {
|
||||
// Could not connect
|
||||
socketThread = new Thread(() -> {
|
||||
try {
|
||||
socket = new Socket(connection.getHost(), connection.getPort());
|
||||
socket = new Socket();
|
||||
socket.setSoTimeout(ProtocolDefinition.SOCKET_CONNECT_TIMEOUT);
|
||||
socket.connect(new InetSocketAddress(address.getHostname(), address.getPort()), ProtocolDefinition.SOCKET_CONNECT_TIMEOUT);
|
||||
connected = true;
|
||||
connection.setConnectionState(ConnectionState.HANDSHAKING);
|
||||
socket.setKeepAlive(true);
|
||||
@ -197,7 +202,9 @@ public class Network {
|
||||
connection.setConnectionState(ConnectionState.DISCONNECTED);
|
||||
} catch (IOException e) {
|
||||
// Could not connect
|
||||
connection.setConnectionState(ConnectionState.DISCONNECTED);
|
||||
if (e instanceof SocketTimeoutException) {
|
||||
connection.setConnectionState(ConnectionState.FAILED);
|
||||
}
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
@ -19,26 +19,23 @@ import de.bixilon.minosoft.protocol.packets.ServerboundPacket;
|
||||
import de.bixilon.minosoft.protocol.protocol.ConnectionState;
|
||||
import de.bixilon.minosoft.protocol.protocol.OutPacketBuffer;
|
||||
import de.bixilon.minosoft.protocol.protocol.Packets;
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
|
||||
import de.bixilon.minosoft.util.ServerAddress;
|
||||
|
||||
public class PacketHandshake implements ServerboundPacket {
|
||||
|
||||
final String address;
|
||||
final int port;
|
||||
final ServerAddress address;
|
||||
final ConnectionState nextState;
|
||||
final int version;
|
||||
|
||||
public PacketHandshake(String address, int port, ConnectionState nextState, int version) {
|
||||
public PacketHandshake(ServerAddress address, ConnectionState nextState, int version) {
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.nextState = nextState;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public PacketHandshake(String address, int version) {
|
||||
public PacketHandshake(ServerAddress address, int version) {
|
||||
this.address = address;
|
||||
this.version = version;
|
||||
this.port = ProtocolDefinition.DEFAULT_PORT;
|
||||
this.nextState = ConnectionState.STATUS;
|
||||
}
|
||||
|
||||
@ -46,14 +43,14 @@ public class PacketHandshake implements ServerboundPacket {
|
||||
public OutPacketBuffer write(Connection connection) {
|
||||
OutPacketBuffer buffer = new OutPacketBuffer(connection, Packets.Serverbound.HANDSHAKING_HANDSHAKE);
|
||||
buffer.writeVarInt((nextState == ConnectionState.STATUS ? -1 : connection.getVersion().getProtocolVersion())); // get best protocol version
|
||||
buffer.writeString(address);
|
||||
buffer.writeShort((short) port);
|
||||
buffer.writeString(address.getHostname());
|
||||
buffer.writeShort((short) address.getPort());
|
||||
buffer.writeVarInt(nextState.getId());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log() {
|
||||
Log.protocol(String.format("Sending handshake packet (host=%s, port=%d)", address, port));
|
||||
Log.protocol(String.format("Sending handshake packet (address=%s)", address));
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
package de.bixilon.minosoft.protocol.protocol;
|
||||
|
||||
public enum ConnectionReason {
|
||||
DNS,
|
||||
PING,
|
||||
GET_VERSION,
|
||||
CONNECT
|
||||
|
@ -20,7 +20,8 @@ public enum ConnectionState {
|
||||
LOGIN(2),
|
||||
PLAY(3),
|
||||
DISCONNECTING(5),
|
||||
DISCONNECTED(6);
|
||||
DISCONNECTED(6),
|
||||
FAILED(7);
|
||||
|
||||
final int id;
|
||||
|
||||
|
@ -16,6 +16,8 @@ package de.bixilon.minosoft.protocol.protocol;
|
||||
public final class ProtocolDefinition {
|
||||
public static final int STRING_MAX_LEN = 32767;
|
||||
public static final int DEFAULT_PORT = 25565;
|
||||
public static final int SOCKET_CONNECT_TIMEOUT = 5000;
|
||||
public static final int SOCKET_TIMEOUT = 30000;
|
||||
public static final int PROTOCOL_PACKET_MAX_SIZE = 2097152;
|
||||
public static final float ANGLE_CALCULATION_CONSTANT = 360.0F / 256.0F;
|
||||
|
||||
|
61
src/main/java/de/bixilon/minosoft/util/DNSUtil.java
Normal file
61
src/main/java/de/bixilon/minosoft/util/DNSUtil.java
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Codename Minosoft
|
||||
* Copyright (C) 2020 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.util;
|
||||
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
|
||||
import org.xbill.DNS.Record;
|
||||
import org.xbill.DNS.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DNSUtil {
|
||||
|
||||
public static ArrayList<ServerAddress> getServerAddresses(String hostname) throws TextParseException {
|
||||
ServerAddress fallbackAddress = getServerAddress(hostname);
|
||||
ArrayList<ServerAddress> ret = new ArrayList<>();
|
||||
if (hostname.contains(":")) {
|
||||
// port provided, skip srv check
|
||||
ret.add(fallbackAddress);
|
||||
return ret;
|
||||
}
|
||||
String query = "_minecraft._tcp." + hostname;
|
||||
Record[] records = new Lookup(query, Type.SRV).run();
|
||||
if (records == null) {
|
||||
ret.add(fallbackAddress);
|
||||
return ret;
|
||||
}
|
||||
for (Record record : records) {
|
||||
SRVRecord srvRecord = (SRVRecord) record;
|
||||
ret.add(new ServerAddress(srvRecord.getTarget().toString(true), srvRecord.getPort()));
|
||||
}
|
||||
ret.add(fallbackAddress);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static ServerAddress getServerAddress(String hostname) {
|
||||
String[] splitHostname = hostname.split(":", 2);
|
||||
if (splitHostname.length == 1) {
|
||||
return new ServerAddress(splitHostname[0], ProtocolDefinition.DEFAULT_PORT);
|
||||
}
|
||||
return new ServerAddress(splitHostname[0], Integer.parseInt(splitHostname[1]));
|
||||
|
||||
}
|
||||
|
||||
public String correctHostName(String hostname) {
|
||||
// replaces invalid chars to avoid copy and paste issues (like spaces, ...)
|
||||
hostname = hostname.replaceAll("\\s", "");
|
||||
return hostname;
|
||||
}
|
||||
|
||||
}
|
37
src/main/java/de/bixilon/minosoft/util/ServerAddress.java
Normal file
37
src/main/java/de/bixilon/minosoft/util/ServerAddress.java
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Codename Minosoft
|
||||
* Copyright (C) 2020 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.util;
|
||||
|
||||
public class ServerAddress {
|
||||
final String hostname;
|
||||
final int port;
|
||||
|
||||
public ServerAddress(String hostname, int port) {
|
||||
this.hostname = hostname;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getHostname() + ":" + getPort();
|
||||
}
|
||||
}
|
@ -17,6 +17,5 @@ account:
|
||||
|
||||
# this will be removed soon, only for debugging (pre alpha stage): some features are not implemented yet -/-
|
||||
debug:
|
||||
host: "127.0.0.1"
|
||||
port: 25565
|
||||
server: "127.0.0.1"
|
||||
version: -1
|
Loading…
x
Reference in New Issue
Block a user