Updated everything to new INS

This commit is contained in:
Finn
2025-12-11 12:01:09 +01:00
parent fd0c062aed
commit 6f5d355f79
11 changed files with 523 additions and 107 deletions

View File

@@ -1,31 +0,0 @@
package org.openautonomousconnection.dns;
import org.openautonomousconnection.protocol.side.dns.ConnectedProtocolClient;
import org.openautonomousconnection.protocol.versions.v1_0_0.classic.handlers.ClassicHandlerDNSServer;
import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_Domain;
import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_RequestDomain;
import org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils.Classic_ProtocolVersion;
import java.sql.SQLException;
public class ClassicHandler extends ClassicHandlerDNSServer {
@Override
public void handleMessage(ConnectedProtocolClient client, String message, Classic_ProtocolVersion protocolVersion) {
}
@Override
public Classic_Domain getDomain(Classic_RequestDomain requestDomain) throws SQLException {
return null;
}
@Override
public Classic_Domain ping(Classic_RequestDomain requestDomain) throws SQLException {
return null;
}
@Override
public void unsupportedClassicPacket(String className, Object[] content, ConnectedProtocolClient client) {
}
}

View File

@@ -1,64 +0,0 @@
package org.openautonomousconnection.dns;
import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.side.dns.ConnectedProtocolClient;
import org.openautonomousconnection.protocol.side.dns.ProtocolDNSServer;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.DNSResponseCode;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.Domain;
import java.io.File;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.util.List;
public class Server extends ProtocolDNSServer {
/**
* Constructs a ProtocolDNSServer with the specified configuration file.
*
* @throws IOException If an I/O error occurs.
* @throws CertificateException If a certificate error occurs.
*/
public Server() throws IOException, CertificateException {
super(new File("config.properties"));
}
@Override
public List<Domain> getDomains() {
return List.of();
}
@Override
public String getDomainDestination(Domain domain) {
return "";
}
@Override
public String getSubnameDestination(Domain domain, String subname) {
return "";
}
@Override
public String getTLNInfoSite(String topLevelName) {
return "";
}
@Override
public DNSResponseCode validateDomain(Domain requestedDomain) {
if (!requestedDomain.getProtocol().equalsIgnoreCase("oac")) return DNSResponseCode.RESPONSE_INVALID_REQUEST;
return DNSResponseCode.RESPONSE_DOMAIN_FULLY_NOT_EXIST;
}
@Override
public void validationPacketSendFailed(Domain domain, ConnectedProtocolClient client, Exception exception) {
ProtocolBridge.getInstance().getLogger().exception("Failed to send ValidationPacket. (" +
"Domain: " + domain.getProtocol() + "." + (domain.hasSubname() ? domain.getSubname() : "") + "." + domain.getTopLevelName() + "/" + domain.getPath() + "?" + domain.getQuery() + "#" + domain.getFragment() + ";" +
";Client: " + client.getConnectionHandler().getClientID() + ")", exception);
}
@Override
public void domainDestinationPacketFailedSend(ConnectedProtocolClient client, Domain domain, DNSResponseCode validationResponse, Exception exception) {
ProtocolBridge.getInstance().getLogger().exception("Failed to send DomainDestinationPacket. (" +
"Domain: " + domain.getProtocol() + "." + (domain.hasSubname() ? domain.getSubname() : "") + "." + domain.getTopLevelName() + "/" + domain.getPath() + "?" + domain.getQuery() + "#" + domain.getFragment() + ";" +
"Validation response: " + validationResponse + ";Client: " + client.getConnectionHandler().getClientID() + ")", exception);
}
}

View File

@@ -0,0 +1,79 @@
package org.openautonomousconnection.ins;
import org.openautonomousconnection.ins.utils.ClassicHelper;
import org.openautonomousconnection.ins.utils.ParsedDomain;
import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.side.ins.ConnectedProtocolClient;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecord;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecordType;
import org.openautonomousconnection.protocol.versions.v1_0_0.classic.handlers.ClassicHandlerINSServer;
import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_Domain;
import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_RequestDomain;
import org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils.Classic_ProtocolVersion;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class ClassicHandler extends ClassicHandlerINSServer {
private ProtocolBridge bridge() {
return Main.getProtocolBridge();
}
@Override
public void handleMessage(ConnectedProtocolClient client, String message, Classic_ProtocolVersion protocolVersion) {
bridge().getLogger().info("[ClassicHandler] Message received from ClientID " + client.getConnectionHandler().getClientID() +
" (Classic Version " + protocolVersion.version + ", Client Version: " +
client.getClientVersion().toString() + "): " + message);
}
@Override
public Classic_Domain getDomain(Classic_RequestDomain requestDomain) throws SQLException {
ParsedDomain pd = ClassicHelper.parseDomain(requestDomain);
// FIRST try A-record
List<INSRecord> a = bridge().getProtocolINSServer().resolve(pd.tln, pd.name, pd.sub, INSRecordType.A);
if (!a.isEmpty()) {
return ClassicHelper.buildClassicDomain(pd, a.get(0));
}
// If no A, try CNAME
List<INSRecord> cnameList = bridge().getProtocolINSServer().resolve(pd.tln, pd.name, pd.sub, INSRecordType.CNAME);
if (cnameList.isEmpty()) return null; // completely not found
INSRecord cname = cnameList.get(0);
// CNAME target = "www.example.net" or "app.dev"
String[] cnameParts = cname.value.split("\\.");
if (cnameParts.length < 2) return null;
String cnameTln = cnameParts[cnameParts.length - 1];
String cnameName = cnameParts[cnameParts.length - 2];
String cnameSub = cnameParts.length > 2
? String.join(".", java.util.Arrays.copyOfRange(cnameParts, 0, cnameParts.length - 2))
: null;
List<INSRecord> aAfterCname = bridge().getProtocolINSServer().resolve(cnameTln, cnameName, cnameSub, INSRecordType.A);
if (aAfterCname.isEmpty()) return null;
return ClassicHelper.buildClassicDomain(pd, aAfterCname.get(0));
}
@Override
public Classic_Domain ping(Classic_RequestDomain req) {
return new Classic_Domain(req.name, req.topLevelDomain, null, req.path, bridge());
}
@Override
public void unsupportedClassicPacket(String className, Object[] content, ConnectedProtocolClient client) {
bridge().getLogger().warn(
"[Classic UnsupportedPacket] From client " + client.getConnectionHandler().getClientID() +
": packet=" + className + " content=" + java.util.Arrays.toString(content)
);
}
}

View File

@@ -0,0 +1,169 @@
package org.openautonomousconnection.ins;
import dev.unlegitdqrk.unlegitlibrary.file.ConfigurationManager;
import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.side.ins.ProtocolINSServer;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecord;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecordType;
import java.io.File;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* Concrete INS server implementation backed by a SQL database.
* <p>
* This implementation expects a table schema similar to:
* <pre>
* CREATE TABLE ins_records (
* id BIGINT PRIMARY KEY AUTO_INCREMENT,
* tln VARCHAR(64) NOT NULL,
* name VARCHAR(255) NOT NULL,
* sub VARCHAR(255) NULL,
* type VARCHAR(16) NOT NULL, -- matches {@link INSRecordType#name()}
* value VARCHAR(1024) NOT NULL,
* priority INT NOT NULL,
* weight INT NOT NULL,
* port INT NOT NULL,
* ttl INT NOT NULL
* );
*
* CREATE TABLE ins_tln_info (
* tln VARCHAR(64) PRIMARY KEY,
* host VARCHAR(255) NOT NULL,
* port INT NOT NULL
* );
* </pre>
*/
public final class DatabaseINSServer extends ProtocolINSServer {
private final String jdbcUrl;
private final String jdbcUser;
private final String jdbcPassword;
private final ConfigurationManager configurationManager;
/**
* Creates a new database-backed INS server.
*
* @throws IOException If the base server initialization fails.
* @throws CertificateException If required certificate files are missing or invalid.
*/
public DatabaseINSServer() throws IOException, CertificateException {
super(new File("config.properties"));
configurationManager = new ConfigurationManager(new File("config.properties"));
configurationManager.loadProperties();
if (!configurationManager.isSet("db.url")) {
configurationManager.set("db.url", "jdbc:mysql://localhost:3306/DB_NAME?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC\n");
configurationManager.saveProperties();
}
if (!configurationManager.isSet("db.user")) {
configurationManager.set("db.user", "username");
configurationManager.saveProperties();
}
if (!configurationManager.isSet("db.password")) {
configurationManager.set("db.password", "password");
configurationManager.saveProperties();
}
jdbcUrl = configurationManager.getString("db.url");
jdbcUser = configurationManager.getString("db.user");
jdbcPassword = configurationManager.getString("db.password");
}
private Connection openConnection() throws SQLException {
return DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
}
/**
* Resolves records for the given INS name from the database.
* <p>
* This method does a single lookup and returns exactly the records that
* match the given query (no CNAME recursion here that is handled on a
* higher level, e.g. via {@code INSRecordTools.resolveWithCNAME(...)}).
*
* @param tln Top-level-name.
* @param name InfoName.
* @param sub Optional sub-name. May be {@code null}.
* @param type Desired record type.
*
* @return List of matching {@link INSRecord}s, or an empty list if none exist.
*/
@Override
public List<INSRecord> resolve(String tln, String name, String sub, INSRecordType type) {
List<INSRecord> result = new ArrayList<>();
String sql =
"SELECT type, value, priority, weight, port, ttl " +
"FROM ins_records " +
"WHERE tln = ? AND name = ? AND type = ? " +
"AND ( (sub IS NULL AND ? IS NULL) OR sub = ? )";
try (Connection conn = openConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, tln);
ps.setString(2, name);
ps.setString(3, type.name());
ps.setString(4, sub);
ps.setString(5, sub);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
INSRecordType dbType = INSRecordType.valueOf(rs.getString("type"));
String value = rs.getString("value");
int priority = rs.getInt("priority");
int weight = rs.getInt("weight");
int port = rs.getInt("port");
int ttl = rs.getInt("ttl");
result.add(new INSRecord(dbType, value, priority, weight, port, ttl));
}
}
} catch (SQLException ex) {
getProtocolBridge().getLogger().exception("Failed to resolve INS record from database", ex);
}
return result;
}
/**
* Resolves the TLN info site,
* which is used when a client queries {@code info.<tln>} without any sub-name.
* <p>
* The result must be returned as {@code "host:port"}.
*
* @param tln The TLN the client is asking about.
*
* @return A {@code "host:port"} string or {@code null} if no TLN info site is configured.
*/
@Override
public String resolveTLNInfoSite(String tln) {
String sql = "SELECT host, port FROM ins_tln_info WHERE tln = ?";
try (Connection conn = openConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, tln);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
String host = rs.getString("host");
int port = rs.getInt("port");
return host + ":" + port;
}
}
} catch (SQLException ex) {
getProtocolBridge().getLogger().exception("Failed to resolve TLN info site for tln=" + tln, ex);
}
return null;
}
}

View File

@@ -1,4 +1,4 @@
package org.openautonomousconnection.dns;
package org.openautonomousconnection.ins;
import dev.unlegitdqrk.unlegitlibrary.command.events.CommandExecutorMissingPermissionEvent;
import dev.unlegitdqrk.unlegitlibrary.command.events.CommandNotFoundEvent;

View File

@@ -1,16 +1,16 @@
package org.openautonomousconnection.dns;
package org.openautonomousconnection.ins;
import dev.unlegitdqrk.unlegitlibrary.command.CommandExecutor;
import dev.unlegitdqrk.unlegitlibrary.command.CommandManager;
import dev.unlegitdqrk.unlegitlibrary.command.CommandPermission;
import dev.unlegitdqrk.unlegitlibrary.utils.Logger;
import lombok.Getter;
import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.ProtocolSettings;
import org.openautonomousconnection.protocol.side.ins.ProtocolINSServer;
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
import javax.annotation.processing.Generated;
import java.io.File;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.util.Scanner;
public class Main {
@@ -18,11 +18,13 @@ public class Main {
private static final CommandExecutor commandExecutor = new CommandExecutor("DNS", PERMISSION_ALL) {};
private static CommandManager commandManager;
@Getter
private static ProtocolBridge protocolBridge;
public static void main(String[] args) throws Exception {
ProtocolSettings settings = new ProtocolSettings();
new ProtocolBridge(new Server(), settings, ProtocolVersion.PV_1_0_0_BETA, new File("logs"));
ProtocolBridge.getInstance().setClassicHandlerDNSServer(new ClassicHandler());
commandManager = new CommandManager(ProtocolBridge.getInstance().getProtocolSettings().eventManager);
protocolBridge = new ProtocolBridge(new DatabaseINSServer(), settings, ProtocolVersion.PV_1_0_0_BETA, new File("logs"));
commandManager = new CommandManager(protocolBridge.getProtocolSettings().eventManager);
Scanner scanner = new Scanner(System.in);
while (true) {

View File

@@ -0,0 +1,38 @@
package org.openautonomousconnection.ins.utils;
import org.openautonomousconnection.ins.Main;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecord;
import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_Domain;
import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_RequestDomain;
public class ClassicHelper {
public static Classic_Domain buildClassicDomain(ParsedDomain req, INSRecord rec) {
Classic_Domain info = new Classic_Domain(req.name, req.tln, req.sub, req.path, Main.getProtocolBridge());
String host = rec.value;
int port = rec.port > 0 ? rec.port : 80;
return new Classic_Domain(
req.name, req.tln,
host.contains(":") ? host : host + ":" + port,
req.path, Main.getProtocolBridge());
}
public static ParsedDomain parseDomain(Classic_RequestDomain req) {
String tln = req.topLevelDomain; // example: "net"
String full = req.name; // example: "api.v1.example"
String[] parts = full.split("\\.");
if (parts.length == 1) {
return new ParsedDomain(tln, full, null, req.path);
}
String name = parts[parts.length - 1];
String sub = parts.length > 1
? String.join(".", java.util.Arrays.copyOfRange(parts, 0, parts.length - 1))
: null;
return new ParsedDomain(tln, name, sub, req.path);
}
}

View File

@@ -0,0 +1,15 @@
package org.openautonomousconnection.ins.utils;
public final class ParsedDomain {
public final String tln;
public final String name;
public final String sub;
public final String path;
public ParsedDomain(String tln, String name, String sub, String path) {
this.tln = tln;
this.name = name;
this.sub = sub;
this.path = path;
}
}