Files
INSServer/src/main/java/org/openautonomousconnection/ins/DatabaseINSServer.java
2025-12-11 12:01:09 +01:00

170 lines
6.0 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}