51 Commits
classic ... dev

Author SHA1 Message Date
UnlegitDqrk
a365b57638 Removed unused licenses 2026-02-14 19:22:18 +01:00
UnlegitDqrk
eda1b24a26 Small bug fix 2026-02-14 17:25:22 +01:00
0ab292ba72 Update frontend/dashboard.java 2026-02-11 23:01:13 +00:00
UnlegitDqrk
f2c35c1e2c Finished 2026-02-11 23:23:10 +01:00
UnlegitDqrk
64ce55ea7b Finished 2026-02-11 23:22:20 +01:00
UnlegitDqrk
9483c36a66 Added mariadb 2026-02-10 22:09:14 +01:00
UnlegitDqrk
7f2865a5d8 Added mariadb 2026-02-10 22:08:48 +01:00
9d2670ebbe Update backend/database.sql 2026-02-10 20:13:17 +00:00
f978895be8 Update backend/database.sql 2026-02-10 19:38:49 +00:00
49764f445a Add backend/database 2026-02-10 19:38:40 +00:00
911a166d48 Delete ins_backend.sql 2026-02-10 19:38:24 +00:00
UnlegitDqrk
3b7301e974 Updated Protocol 2026-02-08 21:42:09 +01:00
UnlegitDqrk
39461fc07a Updated Protocol 2026-02-08 19:05:55 +01:00
UnlegitDqrk
26c1427dc6 Updated Protocol 2026-02-08 18:28:00 +01:00
UnlegitDqrk
ee40b4785a Updated Protocol 2026-02-08 18:26:30 +01:00
UnlegitDqrk
3180cde3c9 Protocol update 2026-02-06 18:02:13 +01:00
UnlegitDqrk
0c21d04488 Protocol update 2026-02-06 18:00:32 +01:00
UnlegitDqrk
323c256050 Updated Readme 2026-02-06 17:50:19 +01:00
UnlegitDqrk
bf6a26a5c5 Updated Protocol 2026-02-06 17:39:03 +01:00
UnlegitDqrk
10aafd1218 Updated Protocol 2026-02-06 17:38:02 +01:00
UnlegitDqrk
3b962af0d3 Updated Protocol 2026-02-06 17:37:43 +01:00
Finn
fce45566fb Updated to latest Protocol Version 2026-02-01 19:06:45 +01:00
Finn
77eb54afc2 Added JavaDocs 2026-01-19 19:16:35 +01:00
Finn
c4dac2373f Added JavaDocs 2026-01-19 19:01:17 +01:00
Finn
76d16209ff Updated Protocol Version 2026-01-19 14:28:12 +01:00
Finn
3848126854 Bug fix 2026-01-18 23:13:38 +01:00
Finn
7c8efa597b Added StopCommand 2026-01-18 22:56:12 +01:00
Finn
5e7f8b7cf3 Fixed Scanner Input 2026-01-18 22:48:36 +01:00
Finn
db66ce0a21 Updated to latest Protocol Version 2026-01-18 22:02:10 +01:00
Finn
6589160632 Updated to latest Protocol Version 2026-01-18 22:01:14 +01:00
Finn
63ac0c2d89 Changed to new ProtocolVersion 2026-01-18 18:34:29 +01:00
Finn
978d30a82c Version changing 2026-01-18 14:57:04 +01:00
Finn
8cd38d18a5 Version changing 2026-01-18 14:56:41 +01:00
5f4889a3db Update LICENSE 2026-01-16 22:15:43 +00:00
70375fb286 Update pom.xml 2026-01-16 22:12:26 +00:00
Finn
c25d223e32 Bug fixes 2025-12-13 16:48:28 +01:00
Finn
b36f96b9b9 Changed DNS to INS 2025-12-13 16:04:42 +01:00
Finn
f9b53f1192 Added licenses 2025-12-13 15:53:57 +01:00
Finn
181274d818 Fixed some links 2025-12-13 14:55:41 +01:00
Finn
c7ca67176d Registered listener 2025-12-13 14:44:45 +01:00
Finn
5fec181a74 Clean up 2025-12-13 14:43:49 +01:00
Finn
887587f5de Added Build Properties 2025-12-13 14:42:51 +01:00
Finn
f74e7100aa Updated to latest protocol version 2025-12-12 21:03:10 +01:00
Finn
17cc3449d2 Added default port and implemented ProtocolSettings 2025-12-12 19:46:00 +01:00
Finn
16ea18d95f Updated Protocol Version 2025-12-12 19:23:31 +01:00
Finn
41884d9d39 Updated Version 2025-12-12 18:47:10 +01:00
Finn
5f1bc8285d Changed OAPL link 2025-12-11 12:54:59 +01:00
Finn
829a998331 Changed OAPL link 2025-12-11 12:53:51 +01:00
Finn
509ed5d1bf Changed OAPL link 2025-12-11 12:52:33 +01:00
Finn
25d6f1de21 Finished INS 2025-12-11 12:36:33 +01:00
Finn
6f5d355f79 Updated everything to new INS 2025-12-11 12:01:09 +01:00
39 changed files with 2716 additions and 1713 deletions

6
.idea/GitLink.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="uk.co.ben_gibson.git.link.SettingsState">
<option name="host" value="e0f86390-1091-4871-8aeb-f534fbc99cf0" />
</component>
</project>

69
LICENSE
View File

@@ -1,68 +1 @@
Open Autonomous Public License (OAPL) v1.0
Copyright (C) 2024-2025 Open Autonomous Connection (OAC)
Projekt-URL: https://open-autonomous-connection.org/
---
1. Nutzungsrechte
Diese Software darf sowohl für private als auch kommerzielle Zwecke genutzt werden. Die Nutzung ist unter den Bedingungen dieser Lizenz gestattet.
---
2. Verkaufsverbot
Es ist nicht gestattet, diese Software oder abgeleitete Werke davon zu verkaufen oder kommerziell zu vertreiben.
Dies umfasst auch jede Form der direkten oder indirekten Monetarisierung der Software selbst.
Es ist gestattet, die Software im Rahmen von Dienstleistungen kommerziell zu nutzen, solange der Quellcode und die Originaldateien kostenlos verfügbar bleiben und nicht gegen Entgelt verkauft oder monetarisiert werden.
Jede Form der Monetarisierung der Software selbst, wie der Verkauf oder die Lizenzierung der Software, ist untersagt.
---
3. Offenlegung des Quellcodes
Die Software ist dauerhaft quelloffen. Der vollständige Quellcode muss bei jeder Verbreitung, auch in geänderter Form, mitgeliefert oder öffentlich zugänglich gemacht werden.
Jede Version, auch veränderte, muss einen klar sichtbaren Verweis auf das Originalprojekt enthalten:
→ https://github.com/Open-Autonomous-Connection/
---
4. Weitergabe & Lizenzvererbung
Die Software darf frei kopiert, verteilt und verändert werden, unter folgenden Bedingungen:
- Die Original-Lizenz (OAPL v1.0) muss vollständig und unverändert mitgeliefert werden.
- Alle Änderungen am Quellcode müssen klar kenntlich gemacht werden (z.B. durch Kommentare oder Änderungsprotokolle).
- Abgeleitete Werke müssen unter derselben Lizenz (OAPL v1.0) veröffentlicht werden, es sei denn, die Lizenzierung erfolgt in einem Kontext,
in dem dies durch geltendes Recht oder technische Einschränkungen nicht möglich ist. In diesem Fall muss der Quellcode der Änderungen weiterhin offen und zugänglich gemacht werden.
- Es dürfen keine zusätzlichen Einschränkungen oder Bedingungen auferlegt werden, die den Bedingungen dieser Lizenz widersprechen.
---
5. Keine proprietäre Nutzung
Die Software oder ihre abgeleiteten Werke dürfen nicht in proprietäre Software integriert oder unter einer Lizenz weitergegeben werden, die die Bedingungen dieser Lizenz einschränkt oder die Offenlegung des Quellcodes unterlässt.
---
6. Keine Veränderung dieser Lizenz
Diese Lizenz darf nicht verändert oder durch andere Lizenzen ersetzt werden. Eine Modifikation oder Re-Lizenzierung ist ausdrücklich untersagt.
---
7. Haftungsausschluss ("as-is")
DIE SOFTWARE WIRD 'WIE BESEHEN' BEREITGESTELLT, OHNE AUSDRÜCKLICHE ODER IMPLIZIERTE GARANTIEN, EINSCHLIESSLICH, ABER NICHT BESCHRÄNKT AUF GARANTIEN DER MARKTGÄNGIGKEIT,
DER EIGNUNG FÜR EINEN BESTIMMTEN ZWECK UND DER NICHTVERLETZUNG VON RECHTEN DRITTER. SOWEIT ES GESCHÄFTSRECHTLICH ZULÄSSIG IST,
WIRD DIE HAFTUNG DER AUTOREN FÜR SCHÄDEN ODER VERLUSTE AUFGRUND DER NUTZUNG DER SOFTWARE AUSGESCHLOSSEN.
---
8. Salvatorische Klausel
Sollte eine Bestimmung dieser Lizenz als unwirksam, undurchsetzbar oder nicht durchsetzbar erklärt werden,
bleibt die Gültigkeit der übrigen Bestimmungen davon unberührt. In diesem Fall wird die unwirksame Klausel durch eine wirksame und durchsetzbare Bestimmung ersetzt,
die dem ursprünglichen wirtschaftlichen Zweck der unwirksamen Bestimmung am nächsten kommt.
---
Ende der Lizenzbedingungen.
Additional Notice regarding UnlegitLibrary:
UnlegitLibrary is primarily distributed under the GNU GPLv3.
For the purposes of the Open Autonomous Connection (OAC) project,
the author has also licensed UnlegitLibrary under the Open Autonomous Public License (OAPL).
Within OAC, the OAPL terms apply to UnlegitLibrary.
Please read the license here: https://open-autonomous-connection.org/license.html

View File

@@ -1,18 +1,23 @@
# Open Autonomous Connection DNS-System
# Open Autonomous Connection INS
> [!IMPORTANT]
> This is the classic version!
> Classic version is not longer maintained or supported please switch the Branch to master
> The new INS has a Backwards compatibility and is supporting the classic Version
This is the old DNS-System for our Open Autonomous Connection project.<br />
If you want to host your own DNS-System for the OAC-Web and you have some problems,<br />
feel free to join our Discord and ask for help.
<br />
This is the INS for our Open Autonomous Connection project.<br />
Feel free to join our Discord.
<br />
# Bugs/Problems
- Update check does not work
# In progress
# TODO
- Subdomains
## License Notice
This project (OAC) is licensed under
the [Open Autonomous Public License (OAPL)](https://open-autonomous-connection.org/license.html).
**Third-party components:**
- *UnlegitLibrary* is authored by the same copyright holder and is used here under a special agreement:
While [UnlegitLibrary](https://repo.unlegitdqrk.dev/UnlegitDqrk/unlegitlibrary/) is generally distributed under
the [GNU GPLv3](https://repo.unlegitdqrk.dev/UnlegitDqrk/unlegitlibrary/src/branch/master/LICENSE),
it is additionally licensed under OAPL **exclusively for the OAC project**.
Therefore, within OAC, the OAPL terms apply to UnlegitLibrary as well.
### Take a look into [Certificate Generation](https://repo.unlegitdqrk.dev/UnlegitDqrk/unlegitlibrary/src/branch/master#certificate-generation-for-networksystem).
> [!NOTE]
> All certificate registrars require the Root CA to issue a server/client certificate

216
database.sql Normal file
View File

@@ -0,0 +1,216 @@
-- phpMyAdmin SQL Dump
-- version 5.2.1deb1+deb12u1
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Generation Time: Feb 10, 2026 at 08:12 PM
-- Server version: 10.11.14-MariaDB-0+deb12u2
-- PHP Version: 8.2.29
SET
SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET
time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `oac_ins`
--
-- --------------------------------------------------------
--
-- Table structure for table `infonames`
--
CREATE TABLE `infonames`
(
`id` int(11) NOT NULL,
`info` varchar(63) NOT NULL,
`tln_id` int(11) NOT NULL,
`uid` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Table structure for table `records`
--
CREATE TABLE `records`
(
`id` int(11) NOT NULL,
`infoname_id` int(11) NOT NULL,
`subname_id` int(11) DEFAULT NULL,
`type` enum('A','AAAA','TXT','CNAME','MX','SRV','NS') NOT NULL,
`value` varchar(2048) NOT NULL,
`ttl` int(11) DEFAULT 3600,
`priority` int(11) DEFAULT NULL,
`port` int(11) DEFAULT NULL,
`weight` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Table structure for table `subnames`
--
CREATE TABLE `subnames`
(
`id` int(11) NOT NULL,
`name` varchar(63) NOT NULL,
`infoname_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Table structure for table `tln`
--
CREATE TABLE `tln`
(
`id` int(11) NOT NULL,
`name` varchar(63) NOT NULL,
`info` text DEFAULT NULL,
`owner_id` int(11) DEFAULT NULL,
`is_public` tinyint(1) DEFAULT 1,
`allow_subdomains` tinyint(1) DEFAULT 1
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Table structure for table `users`
--
CREATE TABLE `users`
(
`id` int(11) NOT NULL,
`uid` varchar(36) NOT NULL,
`username` varchar(64) NOT NULL,
`password` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `infonames`
--
ALTER TABLE `infonames`
ADD PRIMARY KEY (`id`),
ADD KEY `tln_id` (`tln_id`),
ADD KEY `uid` (`uid`);
--
-- Indexes for table `records`
--
ALTER TABLE `records`
ADD PRIMARY KEY (`id`),
ADD KEY `infoname_id` (`infoname_id`),
ADD KEY `subname_id` (`subname_id`);
--
-- Indexes for table `subnames`
--
ALTER TABLE `subnames`
ADD PRIMARY KEY (`id`),
ADD KEY `infoname_id` (`infoname_id`);
--
-- Indexes for table `tln`
--
ALTER TABLE `tln`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `name` (`name`),
ADD KEY `owner_id` (`owner_id`);
--
-- Indexes for table `users`
--
ALTER TABLE `users`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `uid` (`uid`),
ADD UNIQUE KEY `username` (`username`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `infonames`
--
ALTER TABLE `infonames`
MODIFY `id` int (11) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `records`
--
ALTER TABLE `records`
MODIFY `id` int (11) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `subnames`
--
ALTER TABLE `subnames`
MODIFY `id` int (11) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `tln`
--
ALTER TABLE `tln`
MODIFY `id` int (11) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `users`
--
ALTER TABLE `users`
MODIFY `id` int (11) NOT NULL AUTO_INCREMENT;
--
-- Constraints for dumped tables
--
--
-- Constraints for table `infonames`
--
ALTER TABLE `infonames`
ADD CONSTRAINT `infonames_ibfk_1` FOREIGN KEY (`tln_id`) REFERENCES `tln` (`id`) ON DELETE CASCADE,
ADD CONSTRAINT `infonames_ibfk_2` FOREIGN KEY (`uid`) REFERENCES `users` (`id`) ON
DELETE
CASCADE;
--
-- Constraints for table `records`
--
ALTER TABLE `records`
ADD CONSTRAINT `records_ibfk_1` FOREIGN KEY (`infoname_id`) REFERENCES `infonames` (`id`) ON DELETE CASCADE,
ADD CONSTRAINT `records_ibfk_2` FOREIGN KEY (`subname_id`) REFERENCES `subnames` (`id`) ON
DELETE
CASCADE;
--
-- Constraints for table `subnames`
--
ALTER TABLE `subnames`
ADD CONSTRAINT `subnames_ibfk_1` FOREIGN KEY (`infoname_id`) REFERENCES `infonames` (`id`) ON DELETE CASCADE;
--
-- Constraints for table `tln`
--
ALTER TABLE `tln`
ADD CONSTRAINT `tln_ibfk_1` FOREIGN KEY (`owner_id`) REFERENCES `users` (`id`) ON DELETE SET NULL;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

121
dependency-reduced-pom.xml Normal file
View File

@@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.openautonomousconnection</groupId>
<artifactId>INSServer</artifactId>
<version>1.0.0-BETA.1.0</version>
<description>The default INS-Server</description>
<url>https://open-autonomous-connection.org/</url>
<issueManagement>
<system>Issue Tracker</system>
<url>https://repo.open-autonomous-connection.org/open-autonomous-connection/INSServer/issues</url>
</issueManagement>
<developers>
<developer>
<name>UnlegitDqrk</name>
<url>https://unlegitdqrk.dev/</url>
<organization>Open Autonomous Connection</organization>
<organizationUrl>https://open-autonomous-connection.org/</organizationUrl>
<roles>
<role>Owner</role>
<role>Head Developer</role>
</roles>
</developer>
<developer>
<name>Maple</name>
<url>https://niumaple.carrd.co/</url>
<organization>Open Autonomous Connection</organization>
<organizationUrl>https://open-autonomous-connection.org/</organizationUrl>
<roles>
<role>Owner</role>
<role>Head Developer</role>
</roles>
</developer>
</developers>
<licenses>
<license>
<name>Open Autonomous Public License (OAPL)</name>
<url>https://open-autonomous-connection.org/license.html</url>
</license>
</licenses>
<organization>
<name>Open Autonomous Connection</name>
<url>https://open-autonomous-connection.org/</url>
</organization>
<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer>
<mainClass>org.openautonomousconnection.insserver.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<compilerArgs>
<arg>--add-exports</arg>
<arg>java.base/sun.security.x509=ALL-UNNAMED</arg>
<arg>--add-exports</arg>
<arg>java.base/sun.security.util=ALL-UNNAMED</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.3</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<failOnError>false</failOnError>
<failOnWarnings>false</failOnWarnings>
<doclint>none</doclint>
<locale>en_US</locale>
<encoding>UTF-8</encoding>
<docencoding>UTF-8</docencoding>
<charset>UTF-8</charset>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<snapshots />
<id>oac</id>
<url>https://repo.open-autonomous-connection.org/api/packages/open-autonomous-connection/maven</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.38</version>
<scope>provided</scope>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>23</maven.compiler.target>
<maven.compiler.source>23</maven.compiler.source>
</properties>
</project>

View File

@@ -1,11 +0,0 @@
<!--
Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
You are unauthorized to remove this copyright.
You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
See LICENSE-File if exists
-->
<?php
header('Location: index.php');
?>

View File

@@ -1,69 +0,0 @@
<!--
Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
You are unauthorized to remove this copyright.
You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
See LICENSE-File if exists
-->
<?php
session_start();
include(__DIR__ . "/../utils/connection.php");
include(__DIR__ . "/../utils/functions.php");
global $con;
$user_data = check_login($con);
if ($user_data != null) {
header('Location: dashboard.php');
die();
}
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$user = $_POST["username"];
$pass = $_POST["password"];
if (!empty($user) && !empty($pass)) {
if (!username_exists($con, $user)) echo "Username not exists.";
else {
if (login($con, $user, $pass)) {
$_SESSION['user'] = $user;
$pw = hash('sha512', $pass);
$_SESSION['pass'] = $pw;
header('Location: ../dashboard.php');
die();
} else echo "Failed to login. Wrong credentials?";
}
} else echo "Please enter username and password";
}
?>
<html>
<head>
<title>Open Autonomous Connection - Management/Login</title>
<meta name="charset" content="UTF-8" />
<meta name="author" content="Open Autonomous Connection" />
<meta name="description" content="Register here your API Key or (Top level) Domain" />
</head>
<body>
<div id="box">
<h4>Login</h4>
<form method="post">
<input type="text" name="username" placeholder="Username" />
<input type="password" name="password" placeholder="Password" />
<input type="submit" value="Login" />
</form>
<a href="auth/register.php">Register</a>
</div>
</body>
</html>
<?php
?>

View File

@@ -1,67 +0,0 @@
<!--
Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
You are unauthorized to remove this copyright.
You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
See LICENSE-File if exists
-->
<?php
session_start();
include(__DIR__ . "/../utils/connection.php");
include(__DIR__ . "/../utils/functions.php");
global $con;
$user_data = check_login($con);
if ($user_data != null) {
header('Location: dashboard.php');
die();
}
if (!accountRegisteringAllowed($con)) {
echo "No account registering allowed!";
die();
}
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$user = $_POST["username"];
$pass = $_POST["password"];
if (!empty($user) && !empty($pass)) {
if (username_exists($con, $user)) echo "Username already exists.";
else {
if (create_account($con, $user, $pass)) {
header('Location: auth/login.php');
die();
} else echo "Failed to register.";
}
} else echo "Please enter username and password";
}
?>
<html>
<head>
<title>Open Autonomous Connection - Management/Register</title>
<meta name="charset" content="UTF-8" />
<meta name="author" content="Open Autonomous Connection" />
<meta name="description" content="Register here your API Key or (Top level) Domain" />
</head>
<body>
<div id="box">
<h4>Register</h4>
<form method="post">
<input type="text" name="username" placeholder="Username" />
<input type="password" name="password" placeholder="Password" />
<input type="submit" value="Register" />
</form>
<a href="auth/login.php">Login</a>
</div>
</body>
</html>

View File

@@ -1,17 +0,0 @@
<!--
Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
You are unauthorized to remove this copyright.
You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
See LICENSE-File if exists
-->
<?php
$DATABASE_HOST = "127.0.0.1";
$DATABASE_PORT = 3306;
$DATABASE_USER = "my_user";
$DATABASE_PASSWORD = "my_pass";
$DATABASE_NAME = "my_db";
?>

370
frontend/dashboard.java Normal file
View File

@@ -0,0 +1,370 @@
package ins.frontend;
import ins.frontend.utils.RegistrarDao;
import ins.frontend.utils.WebApp;
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
import org.openautonomousconnection.protocol.side.web.ProtocolWebServer;
import org.openautonomousconnection.webserver.api.Route;
import org.openautonomousconnection.webserver.api.SessionContext;
import org.openautonomousconnection.webserver.api.WebPage;
import org.openautonomousconnection.webserver.api.WebPageContext;
import org.openautonomousconnection.webserver.utils.Html;
import org.openautonomousconnection.webserver.utils.MergedRequestParams;
import org.openautonomousconnection.webserver.utils.QuerySupport;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* INS registrar ins.frontend (TLN / InfoName / Records) with proper POST parameter parsing.
*
* <p>Supported actions (POST recommended for mutations):</p>
* <ul>
* <li>create_tln</li>
* <li>update_tln</li>
* <li>delete_tln</li>
* <li>create_infoname</li>
* <li>delete_infoname</li>
* <li>add_record</li>
* </ul>
*
* <p>Important: Listing/editing/deleting records requires DAO methods that are not part of the provided snippet.
* This page currently supports adding records only.</p>
*/
@Route(path = "dashboard.html")
public final class dashboard implements WebPage {
private static Integer normalizeNullableInt(String s) {
if (s == null) return null;
String t = s.trim();
if (t.isEmpty()) return null;
try {
return Integer.parseInt(t);
} catch (Exception ignored) {
return null;
}
}
@Override
public WebResponsePacket handle(WebPageContext ctx) throws Exception {
WebApp.init();
SessionContext session = SessionContext.from(
ctx.client,
(ProtocolWebServer) ctx.client.getServer(),
ctx.request.getHeaders()
);
if (!session.isValid() || session.getUser() == null) {
return plain(401, "Authentication required (session).");
}
int userId;
try {
userId = Integer.parseInt(session.getUser());
} catch (Exception e) {
return plain(401, "Invalid session user.");
}
RegistrarDao dao = WebApp.get().dao();
// Raw target and merged params (GET + POST).
String rawTarget = org.openautonomousconnection.webserver.utils.QuerySupport.extractRawTarget(ctx.request);
Map<String, String> headers = ctx.request.getHeaders();
byte[] body = ctx.request.getBody();
MergedRequestParams p = MergedRequestParams.from(rawTarget, headers, body);
String msg = null;
String err = null;
String action = p.getOr("action", "").trim();
if (!action.isBlank()) {
try {
ActionResult r = executeAction(action, p, userId, dao);
msg = r.msg();
err = r.err();
} catch (Exception e) {
err = "Action failed: " + safeMsg(e);
}
}
return render(userId, msg, err, dao);
}
private ActionResult executeAction(String action, MergedRequestParams p, int userId, RegistrarDao dao) throws Exception {
String a = action.trim().toLowerCase(Locale.ROOT);
if ("create_tln".equals(a)) {
String name = p.get("name");
String info = p.getOr("info", "");
boolean isPublic = p.getBool("is_public");
boolean allowSubs = p.getBool("allow_subdomains");
if (name == null || name.isBlank()) return ActionResult.err("Missing TLN name.");
int id = dao.createTln(name.trim(), info, userId, isPublic, allowSubs);
return ActionResult.ok("Created TLN id=" + id + " (" + name.trim() + ")");
}
if ("update_tln".equals(a)) {
int id = p.getInt("id", -1);
String info = p.getOr("info", "");
boolean isPublic = p.getBool("is_public");
boolean allowSubs = p.getBool("allow_subdomains");
if (id <= 0) return ActionResult.err("Invalid TLN id.");
boolean ok = dao.updateTlnOwned(id, userId, info, isPublic, allowSubs);
return ActionResult.ok(ok ? ("Updated TLN id=" + id) : "Not owner / not found.");
}
if ("delete_tln".equals(a)) {
int id = p.getInt("id", -1);
if (id <= 0) return ActionResult.err("Invalid TLN id.");
boolean ok = dao.deleteTlnOwned(id, userId);
return ActionResult.ok(ok ? ("Deleted TLN id=" + id) : "Not owner / not found.");
}
if ("create_infoname".equals(a)) {
String tlnName = p.get("tln");
String info = p.get("info");
if (tlnName == null || tlnName.isBlank() || info == null || info.isBlank()) {
return ActionResult.err("Missing tln / info.");
}
RegistrarDao.TlnRow tln = dao.findTlnByName(tlnName.trim()).orElse(null);
if (tln == null) return ActionResult.err("Unknown TLN: " + tlnName);
if (!RegistrarDao.canUseTln(tln, userId)) {
return ActionResult.err("TLN not public and not owned by you.");
}
int id = dao.createInfoName(tln, info.trim(), userId);
return ActionResult.ok("Created infoname id=" + id + " (" + info.trim() + "." + tln.name() + ")");
}
if ("delete_infoname".equals(a)) {
int id = p.getInt("id", -1);
if (id <= 0) return ActionResult.err("Invalid infoname id.");
boolean ok = dao.deleteInfoNameOwned(id, userId);
return ActionResult.ok(ok ? ("Deleted infoname id=" + id) : "Not owner / not found.");
}
if ("add_record".equals(a)) {
int infonameId = p.getInt("infoname_id", -1);
String sub = p.get("sub");
String type = p.getOr("type", "").trim().toUpperCase(Locale.ROOT);
String value = p.get("value");
int ttl = p.getInt("ttl", 3600);
Integer priority = normalizeNullableInt(p.get("priority"));
Integer port = normalizeNullableInt(p.get("port"));
Integer weight = normalizeNullableInt(p.get("weight"));
if (infonameId <= 0) return ActionResult.err("Invalid infoname_id.");
if (!dao.isOwnerOfInfoName(infonameId, userId)) return ActionResult.err("Not owner of this infoname.");
if (type.isBlank() || value == null || value.isBlank()) return ActionResult.err("Missing type/value.");
// Validate allow_subdomains against TLN of this infoname (owned list contains TLN metadata).
RegistrarDao.InfoNameRow[] owned = dao.listOwnedInfoNames(userId);
RegistrarDao.InfoNameRow row = null;
for (RegistrarDao.InfoNameRow r : owned) {
if (r.id() == infonameId) {
row = r;
break;
}
}
if (row == null) return ActionResult.err("Infoname not found in your ownership list.");
if (!RegistrarDao.canUseSubname(row.tln(), userId, sub)) {
return ActionResult.err("Subnames are not allowed for this TLN (allow_subdomains=0) unless you own the TLN.");
}
Integer subId = dao.ensureSubname(infonameId, sub);
int rid = dao.addRecord(infonameId, subId, type, value.trim(), ttl, priority, port, weight);
return ActionResult.ok("Added record id=" + rid + " type=" + type);
}
return ActionResult.err("Unknown action: " + action);
}
private WebResponsePacket render(int userId, String msg, String err, RegistrarDao dao) throws Exception {
RegistrarDao.TlnRow[] tlns = dao.listVisibleTlns(userId);
RegistrarDao.InfoNameRow[] owned = dao.listOwnedInfoNames(userId);
String tlnHtml = renderTlnSection(tlns, userId);
String infoHtml = renderInfoNamesSection(owned);
String body = """
<div class="card">
<h2>INS Registrar</h2>
%s
%s
<h3>Create TLN</h3>
<form method="post" action="dashboard.html" class="form">
<input type="hidden" name="action" value="create_tln">
<label><span class="muted">name</span><input type="text" name="name" placeholder="com" required></label>
<label><span class="muted">info</span><input type="text" name="info" required placeholder="ip[:port]"></label>
<label><span class="muted">is_public</span><input type="text" name="is_public" value="1" required></label>
<label><span class="muted">allow_subdomains</span><input type="text" name="allow_subdomains" value="1" required></label>
<button type="submit">Create TLN</button>
</form>
<h3>TLNs (public + owned)</h3>
%s
<h3>Create InfoName</h3>
<p class="muted">Allowed if TLN is public or owned by you.</p>
<form method="post" action="dashboard.html" class="form">
<input type="hidden" name="action" value="create_infoname">
<label><span class="muted">tln</span><input type="text" name="tln" placeholder="com" required></label>
<label><span class="muted">info</span><input type="text" name="info" placeholder="example" required></label>
<button type="submit">Create InfoName</button>
</form>
<h3>Add Record</h3>
<p class="muted">Subname requires allow_subdomains=1 unless you own the TLN. Root (no sub) always allowed.</p>
<form method="post" action="dashboard.html" class="form">
<input type="hidden" name="action" value="add_record">
<label><span class="muted">infoname_id</span><input type="number" name="infoname_id" min="1" required></label>
<label><span class="muted">sub (optional)</span><input type="text" name="sub" placeholder="www"></label>
<label><span class="muted">type</span><input type="text" name="type" placeholder="A/AAAA/TXT/CNAME/MX/SRV/NS" required></label>
<label><span class="muted">value</span><input type="text" name="value" required></label>
<label><span class="muted">ttl</span><input type="number" name="ttl" value="3600"></label>
<label><span class="muted">priority (MX/SRV)</span><input type="number" name="priority"></label>
<label><span class="muted">port (SRV)</span><input type="number" placeholder="Default: 1028" value="1028" name="port"></label>
<label><span class="muted">weight (SRV)</span><input type="number" name="weight"></label>
<button type="submit">Add Record</button>
</form>
<h3>Your InfoNames</h3>
%s
<div class="row">
<div class="col"><a href="index.html">Home</a></div>
</div>
</div>
""".formatted(
msg == null ? "" : "<p class='ok'>" + Html.esc(msg) + "</p>",
err == null ? "" : "<p class='err'>" + Html.esc(err) + "</p>",
tlnHtml,
infoHtml
);
String html = Html.page("INS Registrar", body);
return new WebResponsePacket(200, "text/html", new HashMap<>(), Html.utf8(html));
}
private String renderTlnSection(RegistrarDao.TlnRow[] tlns, int userId) {
if (tlns == null || tlns.length == 0) {
return "<p class='muted'>No TLNs available.</p>";
}
StringBuilder sb = new StringBuilder();
sb.append("<ul>");
for (RegistrarDao.TlnRow t : tlns) {
boolean ownedByMe = (t.ownerId() != null && t.ownerId() == userId);
sb.append("<li>")
.append("<code>").append(Html.esc(t.name())).append("</code>")
.append(" <span class='muted'>(id=").append(t.id()).append(")</span> ")
.append(ownedByMe ? "<span class='muted'>(owner)</span>" : "<span class='muted'>(public)</span>")
.append(" <span class='muted'>is_public=").append(t.isPublic() ? "1" : "0")
.append(", allow_subdomains=").append(t.allowSubdomains() ? "1" : "0")
.append("</span>");
if (ownedByMe) {
sb.append("""
<div class="row" style="margin-top:8px; gap:10px; flex-wrap:wrap;">
<form method="post" action="dashboard.html" class="form" style="margin:0;">
<input type="hidden" name="action" value="update_tln">
<input type="hidden" name="id" value="%d">
<label>
<span class="muted">info</span>
<input type="text" name="info" value="%s">
</label>
<label>
<span class="muted">is_public</span>
<input type="text" name="is_public" value="%s" style="width:70px;">
</label>
<label>
<span class="muted">allow_subdomains</span>
<input type="text" name="allow_subdomains" value="%s" style="width:70px;">
</label>
<button type="submit">Update</button>
</form>
<form method="post" action="dashboard.html" style="margin:0;">
<input type="hidden" name="action" value="delete_tln">
<input type="hidden" name="id" value="%d">
<button type="submit" class="muted">Delete</button>
</form>
</div>
""".formatted(
t.id(),
Html.esc(t.info() == null ? "" : t.info()),
t.isPublic() ? "1" : "0",
t.allowSubdomains() ? "1" : "0",
t.id()
));
}
sb.append("</li>");
}
sb.append("</ul>");
return sb.toString();
}
private String renderInfoNamesSection(RegistrarDao.InfoNameRow[] owned) {
if (owned == null || owned.length == 0) {
return "<p class='muted'>No infonames yet.</p>";
}
StringBuilder sb = new StringBuilder();
sb.append("<ul>");
for (RegistrarDao.InfoNameRow r : owned) {
sb.append("<li>")
.append("<code>")
.append(Html.esc(r.info())).append(".").append(Html.esc(r.tln().name()))
.append("</code>")
.append(" <span class='muted'>(id=").append(r.id()).append(")</span>")
.append("""
<form method="post" action="dashboard.html" style="display:inline; margin-left:10px;">
<input type="hidden" name="action" value="delete_infoname">
<input type="hidden" name="id" value="%d">
<button type="submit" class="muted">Delete</button>
</form>
""".formatted(r.id()))
.append("</li>");
}
sb.append("</ul>");
return sb.toString();
}
private WebResponsePacket plain(int code, String text) {
return new WebResponsePacket(code, "text/plain", new HashMap<>(), Html.utf8(text));
}
private String safeMsg(Exception e) {
String m = e.getMessage();
if (m == null || m.isBlank()) return e.getClass().getSimpleName();
return m;
}
private record ActionResult(String msg, String err) {
static ActionResult ok(String msg) {
return new ActionResult(msg, null);
}
static ActionResult err(String err) {
return new ActionResult(null, err);
}
}
}

View File

@@ -1,197 +0,0 @@
<!--
Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
You are unauthorized to remove this copyright.
You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
See LICENSE-File if exists
-->
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
session_start();
include(__DIR__ . "/utils/connection.php");
include(__DIR__ . "/utils/functions.php");
global $con;
$username = $_SESSION['user'];
$user_data = check_login($con);
if ($user_data == null) {
header('Location: index.php');
die();
}
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (isset($_POST['delete_domain'])) {
$name = $_POST['domain_name'];
$tld = $_POST['tld'];
$accessKey = $_POST['accessKey'];
delete_domain($con, $name, $tld, $accessKey);
} elseif (isset($_POST['delete_tld'])) {
$name = $_POST['tld_name'];
$accessKey = $_POST['accessKey'];
delete_top_level_domain($con, $name, $accessKey);
} elseif (isset($_POST['delete_apikey'])) {
$application = $_POST['application'];
$apiKey = $_POST['apiKey'];
delete_api_key($con, $username, $application, $apiKey);
} elseif (isset($_POST['delete_account'])) {
delete_account($con, $username);
logout($con);
header('Location: index.php');
die();
}
}
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (isset($_POST['create_domain'])) {
if (!domainRegisteringAllowed($con)) {
echo "No domain registering allowed!";
die();
}
$name = $_POST['domain_name'];
$tld = $_POST['tld'];
$destination = $_POST['destination'];
create_domain($con, $name, $tld, $destination, $username);
} elseif (isset($_POST['create_tld'])) {
if (!topLevelDomainRegisteringAllowed($con)) {
echo "No top level domain registering allowed!";
die();
}
$name = $_POST['tld_name'];
$infoSite = $_POST['info_site'];
create_top_level_domain($con, $name, $infoSite, $username);
} elseif (isset($_POST['create_apikey'])) {
$application = $_POST['application'];
create_api_key($con, $username, $application);
}
}
$domains = list_domains($con, $username);
$tlds = list_topleveldomains($con, $username);
$apikeys = list_apikeys($con, $username);
?>
<head>
<title>Open Autonomous Connection - Management/Dashboard</title>
<meta name="charset" content="UTF-8" />
<meta name="author" content="Open Autonomous Connection" />
<meta name="description" content="Register here your API Key or (Top level) Domain" />
</head>
<body>
<h1>Welcome, <?php echo $username; ?></h1>
<h2>Your Domains</h2>
<table border="1">
<tr>
<th>Name</th>
<th>Top Level Domain</th>
<th>Destination</th>
<th>Access Key</th>
<th>Action</th>
</tr>
<?php foreach ($domains as $domain): ?>
<tr>
<td><?php echo $domain['name']; ?></td>
<td><?php echo $domain['topleveldomain']; ?></td>
<td><?php echo $domain['destination']; ?></td>
<td><?php echo $domain['accesskey']; ?></td>
<td>
<form method="post">
<input type="hidden" name="domain_name" value="<?php echo $domain['name']; ?>">
<input type="hidden" name="tld" value="<?php echo $domain['topleveldomain']; ?>">
<input type="hidden" name="accessKey" value="<?php echo $domain['accesskey']; ?>">
<input type="submit" name="delete_domain" value="Delete">
</form>
</td>
</tr>
<?php endforeach; ?>
</table>
<h2>Your Top Level Domains</h2>
<table border="1">
<tr>
<th>Name</th>
<th>Info Site</th>
<th>Access Key</th>
<th>Action</th>
</tr>
<?php foreach ($tlds as $tld): ?>
<tr>
<td><?php echo $tld['name']; ?></td>
<td><?php echo $tld['info']; ?></td>
<td><?php echo $tld['accesskey']; ?></td>
<td>
<form method="post">
<input type="hidden" name="tld_name" value="<?php echo $tld['name']; ?>">
<input type="hidden" name="accessKey" value="<?php echo $tld['accesskey']; ?>">
<input type="submit" name="delete_tld" value="Delete">
</form>
</td>
</tr>
<?php endforeach; ?>
</table>
<h2>Your API Keys</h2>
<table border="1">
<tr>
<th>Application</th>
<th>API Key</th>
<th>Action</th>
</tr>
<?php foreach ($apikeys as $apikey): ?>
<tr>
<td><?php echo $apikey['application']; ?></td>
<td><?php echo $apikey['keyapi']; ?></td>
<td>
<form method="post">
<input type="hidden" name="application" value="<?php echo $apikey['application']; ?>">
<input type="hidden" name="apiKey" value="<?php echo $apikey['keyapi']; ?>">
<input type="submit" name="delete_apikey" value="Delete">
</form>
</td>
</tr>
<?php endforeach; ?>
</table>
<h2>Create Domain</h2>
<form method="post">
<label for="domain_name">Domain Name:</label>
<input type="text" id="domain_name" name="domain_name" required>
<label for="tld">Top Level Domain:</label>
<input type="text" id="tld" name="tld" required>
<label for="destination">Destination:</label>
<input type="text" id="destination" name="destination" required>
<input type="submit" name="create_domain" value="Create Domain">
</form>
<h2>Create Top Level Domain</h2>
<form method="post">
<label for="tld_name">TLD Name:</label>
<input type="text" id="tld_name" name="tld_name" required>
<label for="info_site">Info Site:</label>
<input type="text" id="info_site" name="info_site" required>
<input type="submit" name="create_tld" value="Create TLD">
</form>
<h2>Create API Key</h2>
<form method="post">
<label for="application">Application:</label>
<input type="text" id="application" name="application" required>
<input type="submit" name="create_apikey" value="Create API Key">
</form>
<h2>Delete Account</h2>
<form method="post">
<input type="submit" name="delete_account" value="Delete Account">
</form>
</body>
</html>

36
frontend/index.java Normal file
View File

@@ -0,0 +1,36 @@
package ins.frontend;
import ins.frontend.utils.WebApp;
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
import org.openautonomousconnection.webserver.api.Route;
import org.openautonomousconnection.webserver.api.WebPage;
import org.openautonomousconnection.webserver.api.WebPageContext;
import org.openautonomousconnection.webserver.utils.Html;
import java.util.HashMap;
/**
* Landing page for the registrar ins.frontend.
*/
@Route(path = "index.html")
public final class index implements WebPage {
@Override
public WebResponsePacket handle(WebPageContext ctx) {
WebApp.init();
String html = Html.page("OAC INS Registrar", """
<div class="card">
<h2>OAC INS Registrar</h2>
<p class="muted">What you want to do?</p>
<div class="col"><a href="info.html">Info</a></div><br />
<div class="row">
<div class="col"><a href="login.html">Login</a></div>
<div class="col"><a href="register.html">Register</a></div>
<div class="col"><a href="dashboard.html">Dashboard</a></div>
</div>
</div>
""");
return new WebResponsePacket(200, "text/html", new HashMap<>(), Html.utf8(html));
}
}

View File

@@ -1,40 +0,0 @@
<!--
Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
You are unauthorized to remove this copyright.
You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
See LICENSE-File if exists
-->
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
session_start();
include(__DIR__ . "/utils/connection.php");
include(__DIR__ . "/utils/functions.php");
global $con;
$user_data = check_login($con);
if ($user_data != null) {
header('Location: dashboard.php');
die();
}
?>
<html>
<head>
<title>Open Autonomous Connection - Management</title>
<meta name="charset" content="UTF-8" />
<meta name="author" content="Open Autonomous Connection" />
<meta name="description" content="Register here your API Key or (Top level) Domain" />
<meta name="keywords" content="domain,api,oac,registration,key,host,manager,management" />
</head>
<body>
<a href="auth/register.php">Register</a>
<a href="auth/login.php">Login</a>
</body>
</html>

49
frontend/info.java Normal file
View File

@@ -0,0 +1,49 @@
package ins.frontend;
import ins.frontend.utils.WebApp;
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
import org.openautonomousconnection.webserver.api.Route;
import org.openautonomousconnection.webserver.api.WebPage;
import org.openautonomousconnection.webserver.api.WebPageContext;
import org.openautonomousconnection.webserver.utils.Html;
import java.util.HashMap;
/**
* Landing page for the registrar ins.frontend.
*/
@Route(path = "info.html")
public final class info implements WebPage {
@Override
public WebResponsePacket handle(WebPageContext ctx) {
WebApp.init();
String html = Html.page("INS Info", """
<section class="card">
<h2>OAC Default INS Server</h2>
<p>
The <strong>Default INS Server</strong> is the official, project-operated
name service of the Open Autonomous Connection (OAC) network.
</p>
<p>
It provides a trusted reference point for resolving InfoNames
and enables initial client connections
to the OAC ecosystem.
</p>
<p>
This server is maintained by the OAC project and is intended
as the recommended entry point for public services and new clients.
</p>
<p class="muted">
Note: Alternative or private INS servers may exist, but the default INS
server represents the official and stable reference instance.
</p>
</section> """);
return new WebResponsePacket(200, "text/html", new HashMap<>(), Html.utf8(html));
}
}

231
frontend/login.java Normal file
View File

@@ -0,0 +1,231 @@
package ins.frontend;
import ins.frontend.utils.UserDao;
import ins.frontend.utils.WebApp;
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
import org.openautonomousconnection.protocol.side.web.ProtocolWebServer;
import org.openautonomousconnection.protocol.side.web.managers.SessionManager;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.WebRequestMethod;
import org.openautonomousconnection.webserver.api.Route;
import org.openautonomousconnection.webserver.api.SessionContext;
import org.openautonomousconnection.webserver.api.WebPage;
import org.openautonomousconnection.webserver.api.WebPageContext;
import org.openautonomousconnection.webserver.utils.HeaderMaps;
import org.openautonomousconnection.webserver.utils.Html;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* Login page with existing-session short-circuit.
*
* <p>Username stored in DB as plain text.</p>
*/
@Route(path = "/login.html")
public final class login implements WebPage {
private static WebResponsePacket ok(String html) {
return new WebResponsePacket(200, "text/html; charset=utf-8", HeaderMaps.mutable(), Html.utf8(html));
}
private static WebResponsePacket text(int code, String msg) {
return new WebResponsePacket(code, "text/plain; charset=utf-8", HeaderMaps.mutable(), msg.getBytes(StandardCharsets.UTF_8));
}
private static WebResponsePacket redirect302(String location, String session) {
Map<String, String> headers = HeaderMaps.mutable();
headers.put("location", location);
if (session != null && !session.isBlank()) {
headers.put("Location", "dashboard.html");
headers.put("Set-Cookie", "session=" + session + "; Path=/; HttpOnly; SameSite=Lax");
headers.put("session", session);
headers.put("cookie", session);
}
return new WebResponsePacket(302, "text/plain; charset=utf-8", headers, new byte[0]);
}
private static String renderForm(String errOrOk) {
String line = "";
if (errOrOk != null && !errOrOk.isBlank()) {
boolean ok = errOrOk.startsWith("OK:");
line = "<p class='" + (ok ? "ok" : "err") + "'>" + errOrOk + "</p>";
}
String body = """
<div class="card">
<h2>Login</h2>
%s
<form method="post" action="login.html" class="form">
<label><span class="muted">Username</span><input type="text" name="username" autocomplete="username" required></label>
<label><span class="muted">Password</span><input type="password" name="password" autocomplete="current-password" required></label>
<button type="submit">Login</button>
</form>
<div class="row" style="margin-top: 12px;">
<div class="col"><a href="register.html">Register</a></div>
<div class="col"><a href="index.html">Home</a></div>
</div>
</div>
""".formatted(line);
return Html.page("Login", body);
}
private static String resolveIp(WebPageContext ctx) {
if (ctx.client == null || ctx.client.getConnection() == null) return "";
if (ctx.client.getConnection().getTcpSocket() == null) return "";
if (ctx.client.getConnection().getTcpSocket().getInetAddress() == null) return "";
return ctx.client.getConnection().getTcpSocket().getInetAddress().getHostAddress();
}
private static String headerIgnoreCase(Map<String, String> headers, String key) {
if (headers == null || headers.isEmpty() || key == null) return null;
for (Map.Entry<String, String> e : headers.entrySet()) {
if (e.getKey() != null && e.getKey().equalsIgnoreCase(key)) return e.getValue();
}
return null;
}
private static Map<String, List<String>> parseFormUrlEncoded(byte[] body) {
if (body == null || body.length == 0) return Map.of();
String raw = new String(body, StandardCharsets.UTF_8);
Map<String, List<String>> out = new LinkedHashMap<>();
int start = 0;
while (start <= raw.length()) {
int amp = raw.indexOf('&', start);
if (amp < 0) amp = raw.length();
String pair = raw.substring(start, amp);
if (!pair.isEmpty()) {
int eq = pair.indexOf('=');
String k = (eq < 0) ? pair : pair.substring(0, eq);
String v = (eq < 0) ? "" : pair.substring(eq + 1);
String key = decodeFormToken(k);
String val = decodeFormToken(v);
if (!key.isEmpty()) out.computeIfAbsent(key, __ -> new ArrayList<>(1)).add(val);
}
start = amp + 1;
if (amp == raw.length()) break;
}
Map<String, List<String>> frozen = new LinkedHashMap<>();
for (Map.Entry<String, List<String>> e : out.entrySet()) frozen.put(e.getKey(), List.copyOf(e.getValue()));
return Map.copyOf(frozen);
}
private static String first(Map<String, List<String>> params, String key) {
if (params == null || key == null) return null;
List<String> v = params.get(key);
if (v == null || v.isEmpty()) return null;
String t = v.getFirst();
if (t == null) return null;
String s = t.trim();
return s.isEmpty() ? null : s;
}
private static String decodeFormToken(String s) {
if (s == null || s.isEmpty()) return "";
byte[] tmp = new byte[s.length()];
int n = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '+') {
tmp[n++] = (byte) ' ';
continue;
}
if (c == '%' && i + 2 < s.length()) {
int hi = hex(s.charAt(i + 1));
int lo = hex(s.charAt(i + 2));
if (hi >= 0 && lo >= 0) {
tmp[n++] = (byte) ((hi << 4) | lo);
i += 2;
continue;
}
}
byte[] b = String.valueOf(c).getBytes(StandardCharsets.UTF_8);
for (byte bb : b) tmp[n++] = bb;
}
return new String(tmp, 0, n, StandardCharsets.UTF_8);
}
private static int hex(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
return -1;
}
@Override
public WebResponsePacket handle(WebPageContext ctx) throws Exception {
WebApp.init();
// 1) If a valid session already exists -> go dashboard (keep session)
SessionContext existing = SessionContext.from(
ctx.client,
(ProtocolWebServer) ctx.client.getServer(),
ctx.request.getHeaders()
);
if (existing.isValid() && existing.getUser() != null) {
return redirect302("dashboard.html", existing.getSessionId());
}
WebRequestMethod method = ctx.request.getMethod();
if (method == null) method = WebRequestMethod.GET;
if (method == WebRequestMethod.GET) {
return ok(renderForm(null));
}
if (method != WebRequestMethod.POST) {
return text(405, "Method Not Allowed");
}
String contentType = headerIgnoreCase(ctx.request.getHeaders(), "content-type");
String ctLower = (contentType == null) ? "" : contentType.toLowerCase(Locale.ROOT);
if (!ctLower.startsWith("application/x-www-form-urlencoded")) {
return ok(renderForm("Unsupported content-type: " + Html.esc(contentType)));
}
Map<String, List<String>> form = parseFormUrlEncoded(ctx.request.getBody());
String username = first(form, "username");
String password = first(form, "password");
if (username == null || password == null) {
return ok(renderForm("Missing username/password."));
}
String lookupUsername = username.trim();
UserDao.UserRow user = WebApp.get().users().findByUsername(lookupUsername).orElse(null);
if (user == null) {
return ok(renderForm("Invalid credentials."));
}
boolean okPw = WebApp.get().passwordHasher().verify(password, user.passwordEncoded());
if (!okPw) {
return ok(renderForm("Invalid credentials."));
}
String ip = resolveIp(ctx);
String ua = headerIgnoreCase(ctx.request.getHeaders(), "user-agent");
if (ua == null) ua = "";
String session = SessionManager.create(
String.valueOf(user.id()),
ip,
ua,
(ProtocolWebServer) ctx.client.getServer()
);
return redirect302("dashboard.html", session);
}
}

221
frontend/register.java Normal file
View File

@@ -0,0 +1,221 @@
package ins.frontend;
import ins.frontend.utils.RegistrationService;
import ins.frontend.utils.WebApp;
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
import org.openautonomousconnection.protocol.side.web.ProtocolWebServer;
import org.openautonomousconnection.protocol.side.web.managers.SessionManager;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.WebRequestMethod;
import org.openautonomousconnection.webserver.api.Route;
import org.openautonomousconnection.webserver.api.SessionContext;
import org.openautonomousconnection.webserver.api.WebPage;
import org.openautonomousconnection.webserver.api.WebPageContext;
import org.openautonomousconnection.webserver.utils.HeaderMaps;
import org.openautonomousconnection.webserver.utils.Html;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* Register page with existing-session short-circuit.
*/
@Route(path = "/register.html")
public final class register implements WebPage {
private static WebResponsePacket ok(String html) {
return new WebResponsePacket(200, "text/html; charset=utf-8", HeaderMaps.mutable(), Html.utf8(html));
}
private static WebResponsePacket text(int code, String msg) {
return new WebResponsePacket(code, "text/plain; charset=utf-8", HeaderMaps.mutable(), msg.getBytes(StandardCharsets.UTF_8));
}
private static WebResponsePacket redirect302(String location, String session) {
Map<String, String> headers = HeaderMaps.mutable();
headers.put("location", location);
if (session != null && !session.isBlank()) {
headers.put("Location", "dashboard.html");
headers.put("Set-Cookie", "session=" + session + "; Path=/; HttpOnly; SameSite=Lax");
headers.put("session", session);
headers.put("cookie", session);
}
return new WebResponsePacket(302, "text/plain; charset=utf-8", headers, new byte[0]);
}
private static String renderForm(String errOrOk) {
String line = "";
if (errOrOk != null && !errOrOk.isBlank()) {
boolean ok = errOrOk.startsWith("OK:");
line = "<p class='" + (ok ? "ok" : "err") + "'>" + errOrOk + "</p>";
}
String body = """
<div class="card">
<h2>Register</h2>
%s
<form method="post" action="register.html" class="form">
<label><span class="muted">Username</span><input type="text" name="username" autocomplete="username" required></label>
<label><span class="muted">Password</span><input type="password" name="password" autocomplete="new-password" required></label>
<button type="submit">Create account</button>
</form>
<div class="row" style="margin-top: 12px;">
<div class="col"><a href="login.html">Login</a></div>
<div class="col"><a href="index.html">Home</a></div>
</div>
</div>
""".formatted(line);
return Html.page("Register", body);
}
private static String resolveIp(WebPageContext ctx) {
if (ctx.client == null || ctx.client.getConnection() == null) return "";
if (ctx.client.getConnection().getTcpSocket() == null) return "";
if (ctx.client.getConnection().getTcpSocket().getInetAddress() == null) return "";
return ctx.client.getConnection().getTcpSocket().getInetAddress().getHostAddress();
}
private static String headerIgnoreCase(Map<String, String> headers, String key) {
if (headers == null || headers.isEmpty() || key == null) return null;
for (Map.Entry<String, String> e : headers.entrySet()) {
if (e.getKey() != null && e.getKey().equalsIgnoreCase(key)) return e.getValue();
}
return null;
}
private static Map<String, List<String>> parseFormUrlEncoded(byte[] body) {
if (body == null || body.length == 0) return Map.of();
String raw = new String(body, StandardCharsets.UTF_8);
Map<String, List<String>> out = new LinkedHashMap<>();
int start = 0;
while (start <= raw.length()) {
int amp = raw.indexOf('&', start);
if (amp < 0) amp = raw.length();
String pair = raw.substring(start, amp);
if (!pair.isEmpty()) {
int eq = pair.indexOf('=');
String k = (eq < 0) ? pair : pair.substring(0, eq);
String v = (eq < 0) ? "" : pair.substring(eq + 1);
String key = decodeFormToken(k);
String val = decodeFormToken(v);
if (!key.isEmpty()) out.computeIfAbsent(key, __ -> new ArrayList<>(1)).add(val);
}
start = amp + 1;
if (amp == raw.length()) break;
}
Map<String, List<String>> frozen = new LinkedHashMap<>();
for (Map.Entry<String, List<String>> e : out.entrySet()) frozen.put(e.getKey(), List.copyOf(e.getValue()));
return Map.copyOf(frozen);
}
private static String first(Map<String, List<String>> params, String key) {
if (params == null || key == null) return null;
List<String> v = params.get(key);
if (v == null || v.isEmpty()) return null;
String t = v.getFirst();
if (t == null) return null;
String s = t.trim();
return s.isEmpty() ? null : s;
}
private static String decodeFormToken(String s) {
if (s == null || s.isEmpty()) return "";
byte[] tmp = new byte[s.length()];
int n = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '+') {
tmp[n++] = (byte) ' ';
continue;
}
if (c == '%' && i + 2 < s.length()) {
int hi = hex(s.charAt(i + 1));
int lo = hex(s.charAt(i + 2));
if (hi >= 0 && lo >= 0) {
tmp[n++] = (byte) ((hi << 4) | lo);
i += 2;
continue;
}
}
byte[] b = String.valueOf(c).getBytes(StandardCharsets.UTF_8);
for (byte bb : b) tmp[n++] = bb;
}
return new String(tmp, 0, n, StandardCharsets.UTF_8);
}
private static int hex(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
return -1;
}
@Override
public WebResponsePacket handle(WebPageContext ctx) throws Exception {
WebApp.init();
// 1) If a valid session already exists -> go dashboard (keep session)
SessionContext existing = SessionContext.from(
ctx.client,
(ProtocolWebServer) ctx.client.getServer(),
ctx.request.getHeaders()
);
if (existing.isValid() && existing.getUser() != null) {
return redirect302("dashboard.html", existing.getSessionId());
}
WebRequestMethod method = ctx.request.getMethod();
if (method == null) method = WebRequestMethod.GET;
if (method == WebRequestMethod.GET) {
return ok(renderForm(null));
}
if (method != WebRequestMethod.POST) {
return text(405, "Method Not Allowed");
}
String contentType = headerIgnoreCase(ctx.request.getHeaders(), "content-type");
String ctLower = (contentType == null) ? "" : contentType.toLowerCase(Locale.ROOT);
if (!ctLower.startsWith("application/x-www-form-urlencoded")) {
return ok(renderForm("Unsupported content-type: " + Html.esc(contentType)));
}
Map<String, List<String>> form = parseFormUrlEncoded(ctx.request.getBody());
String username = first(form, "username");
String password = first(form, "password");
RegistrationService service = new RegistrationService(WebApp.get().users(), WebApp.get().passwordHasher());
RegistrationService.Result r = service.register(username, password);
if (!r.ok()) {
return ok(renderForm(r.error()));
}
// 2) Create new session (user just registered)
String ip = resolveIp(ctx);
String ua = headerIgnoreCase(ctx.request.getHeaders(), "user-agent");
if (ua == null) ua = "";
String session = SessionManager.create(
String.valueOf(r.userId()),
ip,
ua,
(ProtocolWebServer) ctx.client.getServer()
);
return redirect302("dashboard.html", session);
}
}

View File

@@ -0,0 +1,490 @@
package ins.frontend.utils;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
* Registrar DAO for schema:
* <ul>
* <li>tln(id, name, info, owner_id, is_public, allow_subdomains)</li>
* <li>infonames(id, info, tln_id, uid)</li>
* <li>subnames(id, name, infoname_id)</li>
* <li>records(id, infoname_id, subname_id, type, value, ttl, priority, port, weight)</li>
* </ul>
*
* <p>Rules:</p>
* <ul>
* <li>Create InfoName under TLN allowed if TLN is public OR owned by user.</li>
* <li>Subnames allowed for non-owner only if allow_subdomains=1. Owner always allowed.</li>
* <li>TLN name is not editable; only info/is_public/allow_subdomains are editable (owner-only).</li>
* </ul>
*/
public final class RegistrarDao {
private final UserDao.DataSourceProvider dataSource;
/**
* Creates the DAO.
*
* @param dataSource JDBC connection provider
*/
public RegistrarDao(UserDao.DataSourceProvider dataSource) {
this.dataSource = Objects.requireNonNull(dataSource, "dataSource");
}
// ---------- TLN ----------
/**
* TLN usable for creating InfoNames if public OR owned.
*
* @param tln TLN row
* @param userId current user id
* @return allowed
*/
public static boolean canUseTln(TlnRow tln, int userId) {
if (tln == null || userId <= 0) return false;
if (tln.isPublic) return true;
return tln.ownerId != null && tln.ownerId == userId;
}
/**
* Subdomain/subname allowed if:
* <ul>
* <li>no sub requested (root) OR</li>
* <li>TLN allows subdomains OR</li>
* <li>user is TLN owner</li>
* </ul>
*
* @param tln TLN row
* @param userId current user id
* @param sub sub label (nullable)
* @return allowed
*/
public static boolean canUseSubname(TlnRow tln, int userId, String sub) {
if (tln == null || userId <= 0) return false;
boolean wantsSub = sub != null && !sub.isBlank();
if (!wantsSub) return true;
if (tln.allowSubdomains) return true;
return tln.ownerId != null && tln.ownerId == userId;
}
private static TlnRow mapTln(ResultSet rs) throws SQLException {
return new TlnRow(
rs.getInt("id"),
rs.getString("name"),
rs.getString("info"),
(Integer) rs.getObject("owner_id"),
rs.getInt("is_public") == 1,
rs.getInt("allow_subdomains") == 1
);
}
/**
* Creates a TLN.
*
* @param name tln.name (unique)
* @param info tln.info (editable, can be null/blank)
* @param ownerId owner user id (nullable in DB, pass null to create unowned TLN)
* @param isPublic tln.is_public
* @param allowSubdomains tln.allow_subdomains
* @return new tln.id
* @throws SQLException on SQL errors
*/
public int createTln(String name, String info, Integer ownerId, boolean isPublic, boolean allowSubdomains) throws SQLException {
if (name == null || name.isBlank()) throw new IllegalArgumentException("name must not be blank");
String sql = "INSERT INTO tln (name, info, owner_id, is_public, allow_subdomains) VALUES (?, ?, ?, ?, ?)";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, name.trim());
ps.setString(2, info);
if (ownerId == null) ps.setNull(3, Types.INTEGER);
else ps.setInt(3, ownerId);
ps.setInt(4, isPublic ? 1 : 0);
ps.setInt(5, allowSubdomains ? 1 : 0);
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) return rs.getInt(1);
}
}
throw new SQLException("No generated key returned for tln.id");
}
/**
* Finds TLN by name.
*
* @param name tln.name
* @return optional row
* @throws SQLException on SQL errors
*/
public Optional<TlnRow> findTlnByName(String name) throws SQLException {
if (name == null || name.isBlank()) return Optional.empty();
String sql = "SELECT id, name, info, owner_id, is_public, allow_subdomains FROM tln WHERE name = ? LIMIT 1";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1, name.trim());
try (ResultSet rs = ps.executeQuery()) {
if (!rs.next()) return Optional.empty();
return Optional.of(mapTln(rs));
}
}
}
// ---------- Permissions ----------
/**
* Lists TLNs visible to a user: owned OR public OR unowned+public.
*
* @param userId current user id
* @return rows
* @throws SQLException on SQL errors
*/
public TlnRow[] listVisibleTlns(int userId) throws SQLException {
String sql = """
SELECT id, name, info, owner_id, is_public, allow_subdomains
FROM tln
WHERE is_public = 1 OR owner_id = ?
ORDER BY name ASC
""";
List<TlnRow> out = new ArrayList<>();
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1, userId);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) out.add(mapTln(rs));
}
}
return out.toArray(new TlnRow[0]);
}
/**
* Updates TLN fields except name (owner-only).
*
* @param tlnId tln.id
* @param ownerUserId users.id (must match tln.owner_id)
* @param newInfo new info text (nullable)
* @param isPublic is_public
* @param allowSubdomains allow_subdomains
* @return true if updated, false if not owned/not found
* @throws SQLException on SQL errors
*/
public boolean updateTlnOwned(int tlnId, int ownerUserId, String newInfo, boolean isPublic, boolean allowSubdomains) throws SQLException {
if (tlnId <= 0) throw new IllegalArgumentException("tlnId must be > 0");
if (ownerUserId <= 0) throw new IllegalArgumentException("ownerUserId must be > 0");
String sql = "UPDATE tln SET info = ?, is_public = ?, allow_subdomains = ? WHERE id = ? AND owner_id = ?";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1, newInfo);
ps.setInt(2, isPublic ? 1 : 0);
ps.setInt(3, allowSubdomains ? 1 : 0);
ps.setInt(4, tlnId);
ps.setInt(5, ownerUserId);
return ps.executeUpdate() > 0;
}
}
// ---------- InfoNames ----------
/**
* Deletes TLN (owner-only).
*
* @param tlnId tln.id
* @param ownerUserId users.id
* @return true if deleted
* @throws SQLException on SQL errors
*/
public boolean deleteTlnOwned(int tlnId, int ownerUserId) throws SQLException {
if (tlnId <= 0) throw new IllegalArgumentException("tlnId must be > 0");
if (ownerUserId <= 0) throw new IllegalArgumentException("ownerUserId must be > 0");
String sql = "DELETE FROM tln WHERE id = ? AND owner_id = ?";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1, tlnId);
ps.setInt(2, ownerUserId);
return ps.executeUpdate() > 0;
}
}
/**
* Creates an InfoName under the given TLN.
*
* @param tln TLN row
* @param info infonames.info
* @param userId infonames.uid (users.id)
* @return new infonames.id
* @throws SQLException on SQL errors
*/
public int createInfoName(TlnRow tln, String info, int userId) throws SQLException {
if (tln == null) throw new IllegalArgumentException("tln must not be null");
if (userId <= 0) throw new IllegalArgumentException("userId must be > 0");
if (!canUseTln(tln, userId)) throw new SQLException("TLN not public and not owned by user.");
if (info == null || info.isBlank()) throw new IllegalArgumentException("info must not be blank");
String sql = "INSERT INTO infonames (info, tln_id, uid) VALUES (?, ?, ?)";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, info.trim());
ps.setInt(2, tln.id);
ps.setInt(3, userId);
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) return rs.getInt(1);
}
}
throw new SQLException("No generated key returned for infonames.id");
}
/**
* Checks whether a user owns the infoname.
*
* @param infonameId infonames.id
* @param userId users.id
* @return true if owned
* @throws SQLException on SQL errors
*/
public boolean isOwnerOfInfoName(int infonameId, int userId) throws SQLException {
if (infonameId <= 0 || userId <= 0) return false;
String sql = "SELECT 1 FROM infonames WHERE id = ? AND uid = ? LIMIT 1";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1, infonameId);
ps.setInt(2, userId);
try (ResultSet rs = ps.executeQuery()) {
return rs.next();
}
}
}
/**
* Deletes an InfoName (owner-only).
*
* @param infonameId infonames.id
* @param ownerUserId users.id
* @return true if deleted
* @throws SQLException on SQL errors
*/
public boolean deleteInfoNameOwned(int infonameId, int ownerUserId) throws SQLException {
if (infonameId <= 0) throw new IllegalArgumentException("infonameId must be > 0");
if (ownerUserId <= 0) throw new IllegalArgumentException("ownerUserId must be > 0");
String sql = "DELETE FROM infonames WHERE id = ? AND uid = ?";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1, infonameId);
ps.setInt(2, ownerUserId);
return ps.executeUpdate() > 0;
}
}
// ---------- Subnames + Records ----------
/**
* Lists InfoNames owned by user (joined with TLN data).
*
* @param userId users.id
* @return rows
* @throws SQLException on SQL errors
*/
public InfoNameRow[] listOwnedInfoNames(int userId) throws SQLException {
if (userId <= 0) return new InfoNameRow[0];
String sql = """
SELECT i.id AS iid, i.info AS info, i.tln_id AS tln_id,
t.name AS tln_name, t.info AS tln_info, t.owner_id AS owner_id,
t.is_public AS is_public, t.allow_subdomains AS allow_subdomains
FROM infonames i
INNER JOIN tln t ON t.id = i.tln_id
WHERE i.uid = ?
ORDER BY t.name ASC, i.info ASC
""";
List<InfoNameRow> out = new ArrayList<>();
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1, userId);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
TlnRow tln = new TlnRow(
rs.getInt("tln_id"),
rs.getString("tln_name"),
rs.getString("tln_info"),
(Integer) rs.getObject("owner_id"),
rs.getInt("is_public") == 1,
rs.getInt("allow_subdomains") == 1
);
out.add(new InfoNameRow(
rs.getInt("iid"),
rs.getString("info"),
tln
));
}
}
}
return out.toArray(new InfoNameRow[0]);
}
/**
* Ensures a subname exists for an infoname; returns subnames.id.
*
* <p>If sub is null/blank, returns null (root).</p>
*
* @param infonameId infonames.id
* @param sub sub label (nullable)
* @return subnames.id or null for root
* @throws SQLException on SQL errors
*/
public Integer ensureSubname(int infonameId, String sub) throws SQLException {
if (sub == null || sub.isBlank()) return null;
if (infonameId <= 0) throw new IllegalArgumentException("infonameId must be > 0");
String name = sub.trim();
// 1) find
String find = "SELECT id FROM subnames WHERE infoname_id = ? AND name = ? LIMIT 1";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(find)) {
ps.setInt(1, infonameId);
ps.setString(2, name);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) return rs.getInt("id");
}
}
// 2) insert
String ins = "INSERT INTO subnames (name, infoname_id) VALUES (?, ?)";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(ins, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, name);
ps.setInt(2, infonameId);
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) return rs.getInt(1);
}
}
throw new SQLException("No generated key returned for subnames.id");
}
/**
* Adds a DNS-like record.
*
* @param infonameId infonames.id
* @param subnameId subnames.id or null for root
* @param type enum type (A/AAAA/TXT/CNAME/MX/SRV/NS)
* @param value record value
* @param ttl ttl (default 3600)
* @param priority priority (MX/SRV)
* @param port port (SRV)
* @param weight weight (SRV)
* @return new records.id
* @throws SQLException on SQL errors
*/
public int addRecord(int infonameId,
Integer subnameId,
String type,
String value,
int ttl,
Integer priority,
Integer port,
Integer weight) throws SQLException {
if (infonameId <= 0) throw new IllegalArgumentException("infonameId must be > 0");
if (type == null || type.isBlank()) throw new IllegalArgumentException("type must not be blank");
if (value == null || value.isBlank()) throw new IllegalArgumentException("value must not be blank");
if (ttl <= 0) ttl = 3600;
String t = type.trim().toUpperCase();
String sql = """
INSERT INTO records (infoname_id, subname_id, type, value, ttl, priority, port, weight)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setInt(1, infonameId);
if (subnameId == null) ps.setNull(2, Types.INTEGER);
else ps.setInt(2, subnameId);
ps.setString(3, t);
ps.setString(4, value.trim());
ps.setInt(5, ttl);
if (priority == null) ps.setNull(6, Types.INTEGER);
else ps.setInt(6, priority);
if (port == null) ps.setNull(7, Types.INTEGER);
else ps.setInt(7, port);
if (weight == null) ps.setNull(8, Types.INTEGER);
else ps.setInt(8, weight);
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) return rs.getInt(1);
}
}
throw new SQLException("No generated key returned for records.id");
}
/**
* TLN row.
*/
public record TlnRow(int id, String name, String info, Integer ownerId, boolean isPublic, boolean allowSubdomains) {
}
/**
* InfoName row (includes TLN).
*/
public record InfoNameRow(int id, String info, TlnRow tln) {
}
}

View File

@@ -0,0 +1,122 @@
package ins.frontend.utils;
import org.openautonomousconnection.webserver.utils.PasswordHasher;
import java.util.Objects;
/**
* Registration service for creating users with secure password hashing and basic validation.
*/
public final class RegistrationService {
private static final int USERNAME_MIN = 5;
private static final int USERNAME_MAX = 256;
private static final int PASSWORD_MIN = 6;
private static final int PASSWORD_MAX = 256;
private final UserDao userDao;
private final PasswordHasher hasher;
/**
* Creates a registration service.
*
* @param userDao user DAO
* @param hasher password hasher
*/
public RegistrationService(UserDao userDao, PasswordHasher hasher) {
this.userDao = Objects.requireNonNull(userDao, "userDao");
this.hasher = Objects.requireNonNull(hasher, "hasher");
}
private static String normalizeUsername(String u) {
if (u == null) return null;
String t = u.trim();
return t.isEmpty() ? null : t;
}
private static String validate(String username, String password) {
if (username == null) return "Missing username.";
if (password == null) return "Missing password.";
if (username.length() < USERNAME_MIN) return "Username too short (min " + USERNAME_MIN + ").";
if (username.length() > USERNAME_MAX) return "Username too long (max " + USERNAME_MAX + ").";
// Allow only a safe subset to avoid weird edge cases in UI and future.
for (int i = 0; i < username.length(); i++) {
char c = username.charAt(i);
boolean ok = (c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9')
|| c == '_' || c == '-' || c == '.';
if (!ok) return "Username contains invalid characters.";
}
if (password.length() < PASSWORD_MIN) return "Password too short (min " + PASSWORD_MIN + ").";
if (password.length() > PASSWORD_MAX) return "Password too long.";
return null;
}
/**
* Registers a new user.
*
* @param usernameRaw raw username (from form)
* @param passwordRaw raw password (from form)
* @return result containing either userId or an error message
*/
public Result register(String usernameRaw, String passwordRaw) {
String username = normalizeUsername(usernameRaw);
String password = (passwordRaw == null) ? "" : passwordRaw;
String validationError = validate(username, password);
if (validationError != null) {
return Result.error(validationError);
}
try {
// Choose: store username as-is OR store sha256(username).
// Your schema says: username(sha256 hex or plain) -> keep it plain for now.
String usernameStored = username;
if (userDao.findByUsername(usernameStored).isPresent()) {
return Result.error("Username already exists.");
}
String passwordEncoded = hasher.hash(password);
int userId = userDao.createUserWithNewUuid(usernameStored, passwordEncoded);
return Result.ok(userId);
} catch (Exception e) {
return Result.error("Registration failed: " + e.getMessage());
}
}
/**
* Registration result.
*
* @param ok whether succeeded
* @param userId created user id, or -1
* @param error error message, or null
*/
public record Result(boolean ok, int userId, String error) {
/**
* Creates a success result.
*
* @param userId user id
* @return result
*/
public static Result ok(int userId) {
return new Result(true, userId, null);
}
/**
* Creates an error result.
*
* @param error error message
* @return result
*/
public static Result error(String error) {
return new Result(false, -1, error);
}
}
}

154
frontend/utils/UserDao.java Normal file
View File

@@ -0,0 +1,154 @@
package ins.frontend.utils;
import org.openautonomousconnection.webserver.utils.Sha256;
import java.sql.*;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
/**
* DAO for users table:
* users(id, uid(uuid string), username(sha256 hex or plain), password(pbkdf2...))
*/
public final class UserDao {
private final DataSourceProvider dataSource;
/**
* Creates a UserDao.
*
* @param dataSource connection provider
*/
public UserDao(DataSourceProvider dataSource) {
this.dataSource = Objects.requireNonNull(dataSource, "dataSource");
}
/**
* Finds a user by username.
*
* @param uid exact uid match
* @return optional row
* @throws SQLException on SQL errors
*/
public Optional<UserRow> findByUid(UUID uid) throws SQLException {
if (uid == null) return Optional.empty();
String sql = "SELECT id, uid, username, password FROM users WHERE uid = ? LIMIT 1";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1, uid.toString());
try (ResultSet rs = ps.executeQuery()) {
if (!rs.next()) return Optional.empty();
return Optional.of(new UserRow(
rs.getInt("id"),
rs.getString("uid"),
rs.getString("username"),
rs.getString("password")
));
}
}
}
/**
* Creates a new user.
*
* @param uid uuid string (36 chars)
* @param username username string (your choice: raw or sha256 hex)
* @param passwordEncoded encoded password
* @return generated users.id
* @throws SQLException on SQL errors
*/
public int createUser(String uid, String username, String passwordEncoded) throws SQLException {
if (uid == null || uid.isBlank()) throw new IllegalArgumentException("uid must not be blank");
if (username == null || username.isBlank()) throw new IllegalArgumentException("username must not be blank");
if (passwordEncoded == null || passwordEncoded.isBlank())
throw new IllegalArgumentException("passwordEncoded must not be blank");
String sql = "INSERT INTO users (uid, username, password) VALUES (?, ?, ?)";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, uid);
ps.setString(2, Sha256.hex(username));
ps.setString(3, passwordEncoded);
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) return rs.getInt(1);
}
}
throw new SQLException("No generated key returned for users.id");
}
/**
* Convenience: creates a user with a new random UUID.
*
* @param username username
* @param passwordEncoded encoded password
* @return generated users.id
* @throws SQLException on SQL errors
*/
public int createUserWithNewUuid(String username, String passwordEncoded) throws SQLException {
UUID uuid = UUID.randomUUID();
while (findByUid(uuid).isPresent()) uuid = UUID.randomUUID();
return createUser(uuid.toString(), username, passwordEncoded);
}
/**
* Finds a user by username.
*
* @param username exact username match
* @return optional row
* @throws SQLException on SQL errors
*/
public Optional<UserRow> findByUsername(String username) throws SQLException {
if (username == null || username.isBlank()) return Optional.empty();
username = Sha256.hex(username);
String sql = "SELECT id, uid, username, password FROM users WHERE username = ? LIMIT 1";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1, username);
try (ResultSet rs = ps.executeQuery()) {
if (!rs.next()) return Optional.empty();
return Optional.of(new UserRow(
rs.getInt("id"),
rs.getString("uid"),
rs.getString("username"),
rs.getString("password")
));
}
}
}
/**
* Connection provider abstraction.
*/
public interface DataSourceProvider {
/**
* @return open SQL connection
* @throws SQLException on errors
*/
Connection getConnection() throws SQLException;
}
/**
* User row.
*
* @param id users.id
* @param uid users.uid (uuid)
* @param username users.username
* @param passwordEncoded users.password
*/
public record UserRow(int id, String uid, String username, String passwordEncoded) {
}
}

113
frontend/utils/WebApp.java Normal file
View File

@@ -0,0 +1,113 @@
package ins.frontend.utils;
import dev.unlegitdqrk.unlegitlibrary.file.ConfigurationManager;
import org.openautonomousconnection.webserver.utils.PasswordHasher;
import org.openautonomousconnection.webserver.utils.Pbkdf2Sha256Hasher;
import java.io.File;
import java.io.IOException;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Objects;
/**
* Application singleton holding DAO + password hasher.
*/
public final class WebApp {
private static volatile WebApp INSTANCE;
private static boolean isInit;
private static UserDao.DataSourceProvider dsp = null;
private final UserDao userDao;
private final RegistrarDao registrarDao;
private final PasswordHasher passwordHasher;
private WebApp(UserDao userDao, RegistrarDao registrarDao, PasswordHasher passwordHasher) {
this.userDao = Objects.requireNonNull(userDao, "userDao");
this.registrarDao = Objects.requireNonNull(registrarDao, "registrarDao");
this.passwordHasher = Objects.requireNonNull(passwordHasher, "passwordHasher");
}
/**
* Initializes the singleton (call once at server startup).
*/
public static void init() {
if (isInit) return;
try {
if (!new File(new File(".").getParent(), "db.properties").exists()) {
new File(new File(".").getParent(), "db.properties").createNewFile();
}
ConfigurationManager config = new ConfigurationManager(new File(new File(".").getParent(), "db.properties"));
config.loadProperties();
if (!config.isSet("db.url")) {
config.set(
"db.url",
"jdbc:mariadb://localhost:3306/ins?useUnicode=true&characterEncoding=utf8"
);
config.saveProperties();
}
if (!config.isSet("db.user")) {
config.set("db.user", "username");
config.saveProperties();
}
if (!config.isSet("db.password")) {
config.set("db.password", "password");
config.saveProperties();
}
dsp = () -> {
try {
isInit = true;
return DriverManager.getConnection(config.getString("db.url"), config.getString("db.user"), config.getString("db.password"));
} catch (SQLException e) {
throw new SQLException("Failed to open DB connection", e);
}
};
} catch (IOException e) {
throw new RuntimeException("Failed to create config", e);
}
UserDao udao = new UserDao(dsp);
RegistrarDao rdao = new RegistrarDao(dsp);
PasswordHasher hasher = new Pbkdf2Sha256Hasher(150_000, 16, 32);
INSTANCE = new WebApp(udao, rdao, hasher);
}
/**
* Returns the initialized instance.
*
* @return app
*/
public static WebApp get() {
WebApp v = INSTANCE;
if (v == null) throw new IllegalStateException("Oac2WebApp not initialized. Call Oac2WebApp.init(...) first.");
return v;
}
/**
* @return user DAO
*/
public UserDao users() {
return userDao;
}
/**
* @return registrar DAO
*/
public RegistrarDao dao() {
return registrarDao;
}
/**
* @return password hasher
*/
public PasswordHasher passwordHasher() {
return passwordHasher;
}
}

View File

@@ -1,17 +0,0 @@
<!--
Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
You are unauthorized to remove this copyright.
You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
See LICENSE-File if exists
-->
<?php
include(__DIR__ . "/../config.php");
global $DATABASE_HOST, $DATABASE_USER, $DATABASE_PASSWORD, $DATABASE_NAME;
$con = mysqli_connect($DATABASE_HOST, $DATABASE_USER, $DATABASE_PASSWORD, $DATABASE_NAME);
if (!$con) echo "Failed to connect";
?>

View File

@@ -1,449 +0,0 @@
<!--
Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
You are unauthorized to remove this copyright.
You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
See LICENSE-File if exists
-->
<?php
$DOMAIN_PATTERN = '/^(?!-)[A-Za-z0-9-]{1,63}(?<!-)$/';
$TOP_LEVEL_DOMAIN_PATTERN = '/^[A-Za-z]{2,6}$/';
function check_login($con) {
if (isset($_SESSION["user"]) && isset($_SESSION["pass"])) {
$user = $_SESSION["user"];
$pass = $_SESSION["pass"];
if (!username_exists($con, $user)) {
logout();
return null;
}
$query = "SELECT * FROM accounts WHERE username = '$user' AND password = '$pass'";
$result = mysqli_query($con, $query);
if ($result && mysqli_num_rows($result) > 0) {
if (!login($con, $user, $pass, true)) {
logout();
return null;
}
$user_data = mysqli_fetch_assoc($result);
return $user_data && login($con, $user, $pass, true);
}
}
return null;
}
function logout() {
unset($_SESSION["user"]);
unset($_SESSION["pass"]);
}
function list_domains($con, $username) {
$domains = [];
// Get the infokeys for the domains associated with the user
$query = "SELECT infokey FROM accountinfos WHERE username = ? AND type = 'domain'";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 's', $username);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$infokeys = [];
while ($row = mysqli_fetch_assoc($result)) {
$infokeys[] = $row['infokey'];
}
// Fetch the domains based on the infokeys
if (!empty($infokeys)) {
$placeholders = implode(',', array_fill(0, count($infokeys), '?'));
$types = str_repeat('s', count($infokeys));
$query = "SELECT * FROM domains WHERE accesskey IN ($placeholders)";
$stmt = mysqli_prepare($con, $query);
// Dynamically bind the parameters
mysqli_stmt_bind_param($stmt, $types, ...$infokeys);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$domains = mysqli_fetch_all($result, MYSQLI_ASSOC);
}
return $domains;
}
function list_topleveldomains($con, $username) {
$query = "SELECT infokey FROM accountinfos WHERE username = ? AND type = 'tld'";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 's', $username);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$tlds = [];
while ($row = mysqli_fetch_assoc($result)) {
$infokey = $row['infokey'];
$query = "SELECT * FROM topleveldomains WHERE accesskey = ?";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 's', $infokey);
mysqli_stmt_execute($stmt);
$result_tld = mysqli_stmt_get_result($stmt);
$tlds = array_merge($tlds, mysqli_fetch_all($result_tld, MYSQLI_ASSOC));
}
return $tlds;
}
function list_apikeys($con, $username) {
$query = "SELECT infokey FROM accountinfos WHERE username = ? AND type = 'api'";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 's', $username);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$apikeys = [];
while ($row = mysqli_fetch_assoc($result)) {
$infokey = $row['infokey'];
$query = "SELECT * FROM apikeys WHERE keyapi = ?";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 's', $infokey);
mysqli_stmt_execute($stmt);
$result_apikey = mysqli_stmt_get_result($stmt);
$apikeys = array_merge($apikeys, mysqli_fetch_all($result_apikey, MYSQLI_ASSOC));
}
return $apikeys;
}
function create_domain($con, $name, $topLevelDomain, $destination, $username) {
if (!domainRegisteringAllowed($con)) return false;
if (domain_exists($con, $name, $topLevelDomain)) return false;
if (strlen($name) < 3 || strlen($name) > 20) return false;
if (!top_level_domain_exists($con, $topLevelDomain)) return false;
if (!is_valid_domain($name)) return false;
if (!is_valid_top_level_domain($topLevelDomain)) return false;
if (!username_exists($con, $username)) return false;
$access_key = generate_key($name . "." . $topLevelDomain . "=" . $username);
$query = "INSERT INTO domains (name, topleveldomain, destination, accesskey) VALUES (?, ?, ?, ?)";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 'ssss', $name, $topLevelDomain, $destination, $access_key);
$result = mysqli_stmt_execute($stmt);
if ($result) {
$query = "INSERT INTO accountinfos (username, infokey, type) VALUES (?, ?, 'domain')";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 'ss', $username, $access_key);
$result = mysqli_stmt_execute($stmt);
return $result;
}
return false;
}
function create_top_level_domain($con, $name, $infoSite, $username) {
if (!topLevelDomainRegisteringAllowed($con)) return false;
if (strlen($name) < 3 || strlen($name) > 10) return false;
if (top_level_domain_exists($con, $name)) return false;
if (!is_valid_top_level_domain($name)) return false;
if (!username_exists($con, $username)) return false;
$access_key = generate_key($infoSite . "." . $name . "=" . $username);
$query = "INSERT INTO topleveldomains (name, accesskey, info) VALUES (?, ?, ?)";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 'sss', $name, $access_key, $infoSite);
$result = mysqli_stmt_execute($stmt);
if ($result) {
$query = "INSERT INTO accountinfos (username, infokey, type) VALUES (?, ?, 'tld')";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 'ss', $username, $access_key);
$result = mysqli_stmt_execute($stmt);
return $result;
}
return false;
}
function create_api_key($con, $username, $application) {
if (!username_exists($con, $username)) return false;
if (has_api_key($con, $username, $application)) return false;
$currentApiKeyCount = getCurrentApiKeyCount($con, $username);
$maxApiKeyCount = maxApiKeys($con);
if ($maxApiKeyCount != -1 && $currentApiKeyCount >= $maxApiKeyCount) return false;
$apikey = generate_key($username . "=" . $application);
$query = "INSERT INTO apikeys (username, application, keyapi) VALUES (?, ?, ?)";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 'sss', $username, $application, $apikey);
$result = mysqli_stmt_execute($stmt);
if ($result) {
$query = "INSERT INTO accountinfos (username, infokey, type) VALUES (?, ?, 'api')";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 'ss', $username, $apikey);
$result = mysqli_stmt_execute($stmt);
return $result;
}
return false;
}
function getCurrentApiKeyCount($con, $username) {
$query = "SELECT COUNT(*) as count FROM apikeys WHERE username = '$username'";
$result = mysqli_query($con, $query);
if ($result && $row = mysqli_fetch_assoc($result)) {
return intval($row['count']);
}
return 0;
}
function is_valid_domain(string $name) {
global $DOMAIN_PATTERN;
return preg_match($DOMAIN_PATTERN, $name);
}
function is_valid_top_level_domain(string $topLevelDomain) {
global $TOP_LEVEL_DOMAIN_PATTERN;
return preg_match($TOP_LEVEL_DOMAIN_PATTERN, $topLevelDomain);
}
function validate_domain_access_key($con, $name, $topLevelDomain, $accessKey) {
$query = "SELECT * FROM domains WHERE name = '$name' AND topleveldomain = '$topLevelDomain' AND accesskey = '$accessKey'";
$result = mysqli_query($con, $query);
return $result && mysqli_num_rows($result) > 0;
}
function validate_top_level_domain_access_key($con, $topLevelDomain, $accessKey) {
$query = "SELECT * FROM topleveldomains WHERE name = '$topLevelDomain' AND accesskey = '$accessKey'";
$result = mysqli_query($con, $query);
return $result && mysqli_num_rows($result) > 0;
}
function domain_exists($con, $name, $topLevelDomain) {
if (strcasecmp($name, "info") == 0) return true;
$query = "SELECT * FROM domains WHERE name = '$name' AND topleveldomain = '$topLevelDomain'";
$result = mysqli_query($con, $query);
return $result && mysqli_num_rows($result) > 0;
}
function top_level_domain_exists($con, $topLevelDomain) {
if (strcasecmp($topLevelDomain, "oac") == 0) return true;
$query = "SELECT * FROM topleveldomains WHERE name = '$topLevelDomain'";
$result = mysqli_query($con, $query);
return $result && mysqli_num_rows($result) > 0;
}
function validate_api_key($con, $username, $application, $apikey) {
if (!username_exists($con, $username)) return false;
if (!has_api_key($con, $username, $application)) return false;
$query = "SELECT * FROM apikeys WHERE application = '$application' AND keyapi = '$apikey' AND username = '$username'";
$result = mysqli_query($con, $query);
return $result && mysqli_num_rows($result) > 0;
}
function has_api_key($con, $username, $application) {
if (!username_exists($con, $username)) return false;
$query = "SELECT * FROM apikeys WHERE application = '$application' AND username = '$username'";
$result = mysqli_query($con, $query);
return $result && mysqli_num_rows($result) > 0;
}
function username_exists($con, $username) {
$query = "SELECT * FROM accounts WHERE username = '$username'";
$result = mysqli_query($con, $query);
return $result && mysqli_num_rows($result) > 0;
}
function create_account($con, $username, $password) {
if (!accountRegisteringAllowed($con)) return false;
if (username_exists($con, $username)) return false;
$pw = hash('sha512', $password);
$query = "INSERT INTO accounts (username, password) VALUES (?, ?)";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 'ss', $username, $pw);
$result = mysqli_stmt_execute($stmt);
return $result;
}
function login($con, $username, $password, $sha = false) {
if (!username_exists($con, $username)) return false;
$pw = $password;
if (!$sha) $pw = hash('sha512', $password);
$query = "SELECT * FROM accounts WHERE username = '$username' AND password = '$pw'";
$result = mysqli_query($con, $query);
return $result && mysqli_num_rows($result) > 0;
}
function generate_key($based) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < 20; $i++) $randomString .= $characters[random_int(0, $charactersLength - 1)];
return hash("sha512", $based . $randomString);
}
function delete_api_key($con, $username, $application, $apiKey) {
if (!username_exists($con, $username)) return false;
if (!has_api_key($con, $username, $application)) return false;
if (!validate_api_key($con, $username, $application, $apiKey)) return false;
$query = "DELETE FROM apikeys WHERE application = ? AND keyapi = ? AND username = ?";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 'sss', $application, $apiKey, $username);
$result = mysqli_stmt_execute($stmt);
if ($result) {
$query = "DELETE FROM accountinfos WHERE username = ? AND infokey = ? AND type = 'api'";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 'ss', $username, $apiKey);
$result = mysqli_stmt_execute($stmt);
}
return $result;
}
function delete_domain($con, $name, $topLevelDomain, $accessKey) {
if (!validate_domain_access_key($con, $name, $topLevelDomain, $accessKey)) return false;
$query = "DELETE FROM domains WHERE name = ? AND topleveldomain = ? AND accesskey = ?";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 'sss', $name, $topLevelDomain, $accessKey);
$result = mysqli_stmt_execute($stmt);
if ($result) {
$query = "DELETE FROM accountinfos WHERE infokey = ?";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 's', $accessKey);
mysqli_stmt_execute($stmt);
}
return $result;
}
function delete_top_level_domain($con, $topLevelDomain, $accessKey) {
if (!validate_top_level_domain_access_key($con, $topLevelDomain, $accessKey)) return false;
$query = "DELETE FROM topleveldomains WHERE name = ? AND accesskey = ?";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 'ss', $topLevelDomain, $accessKey);
$result = mysqli_stmt_execute($stmt);
if ($result) {
$query = "DELETE FROM accountinfos WHERE infokey = ?";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 's', $accessKey);
mysqli_stmt_execute($stmt);
}
return $result;
}
function delete_account($con, $username) {
if (!username_exists($con, $username)) return false;
$query = "SELECT infokey FROM accountinfos WHERE username = ? AND type = 'domain'";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 's', $username);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
while ($row = mysqli_fetch_assoc($result)) {
$infokey = $row['infokey'];
$query = "DELETE FROM domains WHERE accesskey = ?";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 's', $infokey);
mysqli_stmt_execute($stmt);
}
$query = "SELECT infokey FROM accountinfos WHERE username = ? AND type = 'tld'";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 's', $username);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
while ($row = mysqli_fetch_assoc($result)) {
$infokey = $row['infokey'];
$query = "DELETE FROM topleveldomains WHERE accesskey = ?";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 's', $infokey);
mysqli_stmt_execute($stmt);
}
$query = "DELETE FROM apikeys WHERE username = ?";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 's', $username);
mysqli_stmt_execute($stmt);
$query = "DELETE FROM accountinfos WHERE username = ?";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 's', $username);
mysqli_stmt_execute($stmt);
$query = "DELETE FROM accounts WHERE username = ?";
$stmt = mysqli_prepare($con, $query);
mysqli_stmt_bind_param($stmt, 's', $username);
$result = mysqli_stmt_execute($stmt);
return $result;
}
function getConfigValue($con, $name) {
$query = "SELECT value FROM config WHERE name = '$name'";
$result = mysqli_query($con, $query);
if ($result && $row = mysqli_fetch_assoc($result)) {
return $row['value'];
}
return null;
}
function parseBoolean($value) {
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
function topLevelDomainRegisteringAllowed($con) {
$value = getConfigValue($con, 'allow_register_tld');
return $value !== null && parseBoolean(intval($value));
}
function domainRegisteringAllowed($con) {
$value = getConfigValue($con, 'allow_register_domain');
return $value !== null && parseBoolean(intval($value));
}
function accountRegisteringAllowed($con) {
$value = getConfigValue($con, 'allow_register_account');
return $value !== null && parseBoolean(intval($value));
}
function maxApiKeys($con) {
$value = getConfigValue($con, 'max_apikeys');
return $value !== null ? intval($value) : 0;
}
?>

View File

@@ -1,11 +0,0 @@
<!--
Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
You are unauthorized to remove this copyright.
You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
See LICENSE-File if exists
-->
<?php
header('Location: index.php');
?>

View File

@@ -1,194 +0,0 @@
-- phpMyAdmin SQL Dump
-- version 5.1.1deb5ubuntu1
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Erstellungszeit: 09. Jul 2024 um 13:40
-- Server-Version: 10.6.18-MariaDB-0ubuntu0.22.04.1
-- PHP-Version: 8.1.2-1ubuntu2.18
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Datenbank: `open_autonomous_connection`
--
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `accountinfos`
--
CREATE TABLE `accountinfos` (
`id` int(11) NOT NULL,
`username` varchar(255) NOT NULL,
`infokey` varchar(255) NOT NULL,
`type` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `accounts`
--
CREATE TABLE `accounts` (
`id` int(11) NOT NULL,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `apikeys`
--
CREATE TABLE `apikeys` (
`id` int(11) NOT NULL,
`application` varchar(255) NOT NULL,
`keyapi` varchar(255) NOT NULL,
`username` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `config`
--
CREATE TABLE `config` (
`id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`value` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Daten für Tabelle `config`
--
INSERT INTO `config` (`id`, `name`, `value`) VALUES
(1, 'allow_register_tld', '1'),
(2, 'allow_register_domain', '1'),
(3, 'allow_register_account', '1'),
(4, 'max_apikeys', '5');
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `domains`
--
CREATE TABLE `domains` (
`id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`topleveldomain` varchar(255) NOT NULL,
`destination` varchar(255) NOT NULL,
`accesskey` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `topleveldomains`
--
CREATE TABLE `topleveldomains` (
`id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`accesskey` varchar(255) NOT NULL,
`info` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Indizes der exportierten Tabellen
--
--
-- Indizes für die Tabelle `accountinfos`
--
ALTER TABLE `accountinfos`
ADD PRIMARY KEY (`id`);
--
-- Indizes für die Tabelle `accounts`
--
ALTER TABLE `accounts`
ADD PRIMARY KEY (`id`);
--
-- Indizes für die Tabelle `apikeys`
--
ALTER TABLE `apikeys`
ADD PRIMARY KEY (`id`);
--
-- Indizes für die Tabelle `config`
--
ALTER TABLE `config`
ADD PRIMARY KEY (`id`);
--
-- Indizes für die Tabelle `domains`
--
ALTER TABLE `domains`
ADD PRIMARY KEY (`id`);
--
-- Indizes für die Tabelle `topleveldomains`
--
ALTER TABLE `topleveldomains`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT für exportierte Tabellen
--
--
-- AUTO_INCREMENT für Tabelle `accountinfos`
--
ALTER TABLE `accountinfos`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=9;
--
-- AUTO_INCREMENT für Tabelle `accounts`
--
ALTER TABLE `accounts`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
--
-- AUTO_INCREMENT für Tabelle `apikeys`
--
ALTER TABLE `apikeys`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
--
-- AUTO_INCREMENT für Tabelle `config`
--
ALTER TABLE `config`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
--
-- AUTO_INCREMENT für Tabelle `domains`
--
ALTER TABLE `domains`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;
--
-- AUTO_INCREMENT für Tabelle `topleveldomains`
--
ALTER TABLE `topleveldomains`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=18;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

88
pom.xml
View File

@@ -1,18 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.openautonomousconnection</groupId>
<artifactId>INSServer</artifactId>
<version>1.0-CLASSIC</version>
<version>1.0.0-BETA.1.0</version>
<organization>
<name>Open Autonomous Connection</name>
<url>https://open-autonomous-connection.org/</url>
</organization>
<url>https://open-autonomous-connection.org/</url>
<description>The default DNS-Server</description>
<description>The default INS-Server</description>
<properties>
<maven.compiler.source>23</maven.compiler.source>
@@ -50,9 +50,8 @@
<licenses>
<license>
<name>Open Autonomous Public License</name>
<name>Open Autonomous Public License (OAPL)</name>
<url>https://open-autonomous-connection.org/license.html</url>
<distribution>repo</distribution>
</license>
</licenses>
@@ -69,13 +68,80 @@
<dependencies>
<dependency>
<groupId>org.openautonomousconnection</groupId>
<artifactId>protocol</artifactId>
<version>1.0.0-CLASSIC</version>
<artifactId>Protocol</artifactId>
<version>1.0.0-BETA.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.38</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.5.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.openautonomousconnection.insserver.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<compilerArgs>
<arg>--add-exports</arg>
<arg>java.base/sun.security.x509=ALL-UNNAMED</arg>
<arg>--add-exports</arg>
<arg>java.base/sun.security.util=ALL-UNNAMED</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.3</version>
<configuration>
<failOnError>false</failOnError>
<failOnWarnings>false</failOnWarnings>
<doclint>none</doclint>
<locale>en_US</locale>
<encoding>UTF-8</encoding>
<docencoding>UTF-8</docencoding>
<charset>UTF-8</charset>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.dns;
import org.openautonomousconnection.dns.utils.Database;
import org.openautonomousconnection.protocol.domain.Domain;
import org.openautonomousconnection.protocol.domain.RequestDomain;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class DomainManager {
public static Domain getDomain(String name, String topLevelDomain) throws SQLException {
for (Domain domain : getDomains())
if (domain.name.equalsIgnoreCase(name) && domain.topLevelDomain.equalsIgnoreCase(topLevelDomain))
return domain;
return null;
}
public static Domain getDomain(RequestDomain requestDomain) throws SQLException {
return getDomain(requestDomain.name, requestDomain.topLevelDomain);
}
public static List<Domain> getDomains() throws SQLException {
List<Domain> domains = new ArrayList<>();
ResultSet result = Database.getConnection().prepareStatement("SELECT name, topleveldomain, destination FROM domains").executeQuery();
while (result.next()) {
String name = result.getString("name");
String topLevelDomain = result.getString("topleveldomain");
String destination = result.getString("destination");
domains.add(new Domain(name, topLevelDomain, destination, ""));
}
return domains;
}
public static List<String> getTopLevelDomains() throws SQLException {
List<String> topLevelDomains = new ArrayList<>();
ResultSet result = Database.getConnection().prepareStatement("SELECT name FROM topleveldomains").executeQuery();
while (result.next()) topLevelDomains.add(result.getString("name"));
return topLevelDomains;
}
}

View File

@@ -1,109 +0,0 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.dns;
import dev.unlegitdqrk.unlegitlibrary.addon.AddonLoader;
import dev.unlegitdqrk.unlegitlibrary.addon.impl.AddonInfo;
import org.openautonomousconnection.dns.utils.Config;
import org.openautonomousconnection.dns.utils.Database;
import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.ProtocolSettings;
import org.openautonomousconnection.protocol.ProtocolVersion;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.SQLException;
import java.util.Objects;
public class Main {
public static ProtocolBridge protocolBridge;
public static AddonLoader addonLoader;
public static final File modulesFolder = new File("modules");
public static void main(String[] args) {
try {
URL oracle = new URI("https://raw.githubusercontent.com/Open-Autonomous-Connection/dns/master/src/resources/version.txt").toURL();
BufferedReader in = new BufferedReader(new InputStreamReader(oracle.openStream()));
StringBuilder version = new StringBuilder();
String inputLine;
while ((inputLine = in.readLine()) != null) version.append(inputLine);
if (!version.toString().equalsIgnoreCase(Files.readString(Path.of(Objects.requireNonNull(Main.class.getResource("../../../version.txt")).toURI())))) {
System.out.println();
System.out.println("======================================================");
System.out.println("IMPORTANT: A NEW SERVER VERSION IS PUBLISHED ON GITHUB");
System.out.println("======================================================");
System.out.println();
}
} catch (IOException | URISyntaxException exception) {
System.out.println();
System.out.println("=====================================================================");
System.out.println("IMPORTANT: SERVER VERSION CHECK COULD NOT COMPLETED! VISIT OUR GITHUB");
System.out.println(" https://github.com/Open-Autonomous-Connection ");
System.out.println("=====================================================================");
System.out.println();
}
try {
Config.init();
Database.connect();
} catch (SQLException | InstantiationException | ClassNotFoundException | IllegalAccessException | IOException exception) {
exception.printStackTrace();
return;
}
final ProtocolSettings protocolSettings = new ProtocolSettings();
protocolSettings.port = Config.getPort();
try {
protocolBridge = new ProtocolBridge(ProtocolVersion.PV_1_0_0, protocolSettings, new Server());
protocolBridge.getProtocolServer().setProtocolBridge(protocolBridge);
protocolBridge.getProtocolServer().getServer().getEventManager().registerListener(ServerEventListener.class);
protocolBridge.getProtocolServer().startServer();
System.out.println();
} catch (Exception exception) {
exception.printStackTrace();
return;
}
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
protocolBridge.getProtocolServer().stopServer();
Database.close();
} catch (SQLException | IOException exception) {
exception.printStackTrace();
}
}));
addonLoader = new AddonLoader(protocolSettings.eventManager, null);
try {
addonLoader.loadAddonsFromDirectory(modulesFolder);
addonLoader.getLoadedAddons().forEach(addon -> {
if (addon.isEnabled()) return;
AddonInfo info = addon.getAddonInfo();
System.out.println("Enabling Addon '" + info.name() + " v" + info.version() + "' by " + info.author() + "...");
addon.enable();
System.out.println("Addon '" + info.name() + " v" + info.version() + "' enabled.");
});
} catch (IOException exception) {
exception.printStackTrace();
}
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.dns;
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
import org.openautonomousconnection.dns.utils.Config;
import org.openautonomousconnection.dns.utils.Database;
import org.openautonomousconnection.protocol.domain.Domain;
import org.openautonomousconnection.protocol.side.ProtocolServer;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class Server extends ProtocolServer {
@Override
public List<Domain> getDomains() throws SQLException {
return DomainManager.getDomains();
}
@Override
public List<String> getTopLevelDomains() throws SQLException {
return DomainManager.getTopLevelDomains();
}
@Override
public void handleMessage(ConnectionHandler connectionHandler, String message) {
System.out.println("[MESSAGE] " + connectionHandler.getClientID() + ": " + message);
}
@Override
public String getInfoSite(String topLevelDomain) throws SQLException {
if (!topLevelDomainExists(topLevelDomain)) return null;
ResultSet resultSet = Database.getConnection().prepareStatement("SELECT name, info FROM topleveldomains").executeQuery();
while (resultSet.next()) if (resultSet.getString("name").equals(topLevelDomain)) return resultSet.getString("info");
return null;
}
@Override
public String getInterfaceSite() {
return Config.getInterfaceSite();
}
@Override
public String getDNSServerInfoSite() {
return Config.getInfoSite();
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.dns;
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.ConnectionHandlerConnectedEvent;
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.ConnectionHandlerDisconnectedEvent;
import org.openautonomousconnection.protocol.events.v1_0_0.DomainPacketReceivedEvent;
import org.openautonomousconnection.protocol.events.v1_0_0.PingPacketReceivedEvent;
public class ServerEventListener extends EventListener {
@Listener
public void onConnect(ConnectionHandlerConnectedEvent event) {
System.out.println("New client connected. ID: " + event.getConnectionHandler().getClientID());
System.out.println();
}
@Listener
public void onDisconnect(ConnectionHandlerDisconnectedEvent event) {
System.out.println("Client disconnected. ID: " + event.getConnectionHandler().getClientID());
System.out.println();
}
@Listener
public void onPing(PingPacketReceivedEvent event) {
System.out.println("New Ping request:");
System.out.println(" » From client id: " + event.clientID);
System.out.println(" » Request domain: " + event.requestDomain.toString());
System.out.println(" » Path: " + event.requestDomain.getPath());
System.out.println(" » Reachable: " + (event.reachable ? "Yes" : "No"));
System.out.println(" » Destination: " + (event.domain == null ? "N/A" : event.domain.parsedDestination()));
System.out.println();
}
@Listener
public void onExistCheck(DomainPacketReceivedEvent event) {
System.out.println("New Domain packet request:");
System.out.println(" » From client id: " + event.clientID);
System.out.println(" » Request domain: " + event.requestDomain.toString());
System.out.println(" » Path: " + event.requestDomain.getPath());
System.out.println(" » Exists: " + (event.domain == null ? "No" : "Yes"));
System.out.println(" » Destination: " + (event.domain == null ? "N/A" : event.domain.parsedDestination()));
System.out.println(" ");
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.dns.utils;
import org.openautonomousconnection.protocol.utils.APIInformation;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class APIManager {
public static boolean hasKey(String username, String application) throws SQLException {
PreparedStatement statement = Database.getConnection().prepareStatement("SELECT application, username FROM apikeys WHERE application = ? AND username = ?");
statement.setString(1, application.toLowerCase());
statement.setString(2, username);
ResultSet result = statement.executeQuery();
return result.next() && result.getString("application").equalsIgnoreCase(application) && result.getString("username").equalsIgnoreCase(username);
}
public static boolean validateKey(String username, String application, String apiKey) throws SQLException {
if (!hasKey(username, application)) return false;
PreparedStatement statement = Database.getConnection().prepareStatement("SELECT application, keyapi, username FROM apikeys WHERE application = ? AND keyapi = ? AND username = ?");
statement.setString(1, application.toLowerCase());
statement.setString(2, apiKey);
statement.setString(3, username);
ResultSet result = statement.executeQuery();
return result.next() && result.getString("application").equalsIgnoreCase(application) && result.getString("keyapi").equals(apiKey) && result.getString("username").equalsIgnoreCase(username);
}
public static boolean validateKey(APIInformation apiInformation) throws SQLException {
return validateKey(apiInformation.username, apiInformation.apiApplication, apiInformation.apiKey);
}
}

View File

@@ -1,116 +0,0 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.dns.utils;
import dev.unlegitdqrk.unlegitlibrary.file.ConfigurationManager;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Config {
public static boolean parseBoolean(int i) {
return i != 0;
}
public static int booleanToInt(boolean bool) {
return bool ? 1 : 0;
}
public static boolean topLevelDomainRegisteringAllowed() throws SQLException {
PreparedStatement statement = Database.getConnection().prepareStatement("SELECT value FROM config WHERE name = ?");
statement.setString(1, "allow_register_tld");
ResultSet result = statement.executeQuery();
return result.next() && parseBoolean(Integer.parseInt(result.getString("value")));
}
public static boolean domainRegisteringAllowed() throws SQLException {
PreparedStatement statement = Database.getConnection().prepareStatement("SELECT value FROM config WHERE name = ?");
statement.setString(1, "allow_register_domain");
ResultSet result = statement.executeQuery();
return result.next() && parseBoolean(Integer.parseInt(result.getString("value")));
}
public static boolean accountRegisteringAllowed() throws SQLException {
PreparedStatement statement = Database.getConnection().prepareStatement("SELECT value FROM config WHERE name = ?");
statement.setString(1, "allow_register_account");
ResultSet result = statement.executeQuery();
return result.next() && parseBoolean(Integer.parseInt(result.getString("value")));
}
public static int maxApiKeys() throws SQLException {
PreparedStatement statement = Database.getConnection().prepareStatement("SELECT value FROM config WHERE name = ?");
statement.setString(1, "max_apikeys");
ResultSet result = statement.executeQuery();
if (!result.next()) return 0;
return Integer.parseInt(result.getString("value")); // -1 = Endless
}
private static final File configFile = new File("./config.properties");
private static ConfigurationManager config;
public static void init() throws IOException {
URL whatIsMyIp = new URL("http://checkip.amazonaws.com");
BufferedReader in = new BufferedReader(new InputStreamReader(whatIsMyIp.openStream()));
String ip = in.readLine();
if (!configFile.exists()) configFile.createNewFile();
config = new ConfigurationManager(configFile);
config.loadProperties();
if (!config.isSet("port")) config.set("port", 9382);
if (!config.isSet("sites.info")) config.set("sites.info", "DNS SERVER NEED A INFO SITE!");
if (!config.isSet("sites.interface")) config.set("sites.interface", ip);
if (!config.isSet("database.host")) config.set("database.host", "127.0.0.1");
if (!config.isSet("database.port")) config.set("database.port", 3306);
if (!config.isSet("database.name")) config.set("database.name", "my_db");
if (!config.isSet("database.username")) config.set("database.username", "my_username");
if (!config.isSet("database.password")) config.set("database.password", "my_password");
config.saveProperties();
}
public static String getInfoSite() {
return config.getString("sites.info");
}
public static String getInterfaceSite() {
return config.getString("sites.interface");
}
public static int getPort() {
return config.getInt("port");
}
public static String getDatabaseHost() {
return config.getString("database.host");
}
public static int getDatabasePort() {
return config.getInt("database.port");
}
public static String getDatabaseName() {
return config.getString("database.name");
}
public static String getDatabaseUsername() {
return config.getString("database.username");
}
public static String getDatabasePassword() {
return config.getString("database.password");
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.dns.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Database {
private static Connection connection;
public static Connection getConnection() {
return connection;
}
public static void connect() throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
if (isConnected()) return;
Class.forName("com.mysql.jdbc.Driver").newInstance();
connection = DriverManager.getConnection("jdbc:mysql://" + Config.getDatabaseHost() + ":" + Config.getDatabasePort() + "/" +
Config.getDatabaseName() + "?autoReconnect=true", Config.getDatabaseUsername(), Config.getDatabasePassword());
}
public static void close() throws SQLException {
if (!isConnected()) return;
connection.close();
connection = null;
}
public static boolean isConnected() {
return connection != null;
}
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.dns.utils;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Utils {
public static String createAccessKey(String input) {
return sha256(shuffleString(sha256(input) + getAlphaNumericString(5) +
sha256(getAlphaNumericString(5)) +
sha256(getAlphaNumericString(5)) +
sha256(getAlphaNumericString(5))));
}
public static String sha256(final String base) {
try {
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
final byte[] hash = digest.digest(base.getBytes(StandardCharsets.UTF_8));
final StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
final String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static String getAlphaNumericString(int length) {
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvxyz";
StringBuilder builder = new StringBuilder(length);
for (int i = 0; i < length; i++) {
int index = (int) (chars.length() * Math.random());
builder.append(chars.charAt(index));
}
return builder.toString();
}
public static String shuffleString(String input) {
List<Character> characters = new ArrayList<>();
for (char c : input.toCharArray()) characters.add(c);
Collections.shuffle(characters);
StringBuilder shuffledString = new StringBuilder();
for (char c : characters) shuffledString.append(c);
return shuffledString.toString();
}
}

View File

@@ -0,0 +1,346 @@
package org.openautonomousconnection.insserver;
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.IOException;
import java.security.cert.CertificateException;
import java.sql.*;
import java.util.*;
/**
* Database-backed INS server.
* <p>
* This implementation resolves records from the SQL schema and performs CNAME recursion (with loop detection + depth limit).
* Returned records are deterministically sorted so that callers can safely select index 0 as the "best" record.
*/
public final class DatabaseINSServer extends ProtocolINSServer {
private final String jdbcUrl;
private final String jdbcUser;
private final String jdbcPassword;
/**
* 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(String insInfoSite, String insFrontendSite, String jdbcUrl, String jdbcUser, String jdbcPassword) throws Exception {
super(insInfoSite, insFrontendSite);
this.jdbcUrl = jdbcUrl;
this.jdbcUser = jdbcUser;
this.jdbcPassword = jdbcPassword;
}
private static int safeInt(int v) {
return v;
}
private static String safeString(String s) {
return s == null ? "" : s;
}
private Connection openConnection() throws SQLException {
return DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
}
/**
* Resolves a request for an INS record based on TLN, name, subname and record type.
* <p>
* The implementation:
* <ul>
* <li>Locates the corresponding InfoName in the SQL schema</li>
* <li>Returns all matching {@link INSRecord} entries</li>
* <li>Performs CNAME recursion (with loop detection + depth limit) when no direct records for the requested type exist</li>
* <li>Returns deterministically sorted results (so index 0 can be used as "best record")</li>
* </ul>
*
* @param tln The top-level name.
* @param name The InfoName.
* @param sub Optional subname, may be {@code null}.
* @param type The requested record type.
* @return Resolved records (possibly empty).
*/
@Override
public List<INSRecord> resolve(String tln, String name, String sub, INSRecordType type) {
try {
List<INSRecord> out = resolveInternal(tln, name, sub, type, 0, new HashSet<>());
out.sort(recordComparator(type));
return out;
} catch (SQLException ex) {
getProtocolBridge().getLogger().exception(
"INS resolve failed for " + formatName(tln, name, sub) + " type=" + type,
ex
);
return new ArrayList<>();
}
}
/**
* Resolves the TLN info site which is used when a client queries {@code info.<tln>} without any sub-name.
* <p>
* The value is read from {@code tln.info} and must be of the form {@code "host:port"}.
*
* @param tln The TLN name.
* @return The configured info target ("host:port") or {@code null} if not present.
*/
@Override
public String resolveTLNInfoSite(String tln) {
String sql = "SELECT info FROM tln WHERE name = ?";
try (Connection conn = openConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, tln);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) return rs.getString("info");
}
} catch (SQLException ex) {
getProtocolBridge().getLogger().exception("Failed to resolve TLN info site for tln=" + tln, ex);
}
return null;
}
private String formatName(String tln, String name, String sub) {
if (sub == null || sub.isEmpty()) return name + "." + tln;
return sub + "." + name + "." + tln;
}
/**
* Internal recursive resolver with CNAME handling.
*
* @param depth Current recursion depth.
* @param visited Loop detection set of canonical names (sub.name.tln).
*/
private List<INSRecord> resolveInternal(
String tln,
String name,
String sub,
INSRecordType requestedType,
int depth,
Set<String> visited
) throws SQLException {
final int MAX_CNAME_DEPTH = 16;
String canonical = formatName(tln, name, sub).toLowerCase(Locale.ROOT);
if (!visited.add(canonical)) {
// loop detected
getProtocolBridge().getLogger().warn("CNAME loop detected for " + canonical + " type=" + requestedType);
return new ArrayList<>();
}
if (depth > MAX_CNAME_DEPTH) {
getProtocolBridge().getLogger().warn("CNAME recursion limit exceeded for " + canonical + " type=" + requestedType);
return new ArrayList<>();
}
try (Connection conn = openConnection()) {
Integer tlnId = findTLNId(conn, tln);
if (tlnId == null) return new ArrayList<>();
Integer infoNameId = findInfoNameId(conn, tlnId, name);
if (infoNameId == null) return new ArrayList<>();
Integer subNameId = findSubNameId(conn, infoNameId, sub);
// 1) direct records
List<INSRecord> direct = loadRecords(conn, infoNameId, subNameId, requestedType);
direct.sort(recordComparator(requestedType));
// If the requested type is CNAME, do not recurse.
if (requestedType == INSRecordType.CNAME) return direct;
if (!direct.isEmpty()) return direct;
// 2) fallback to CNAME if no direct records exist
List<INSRecord> cnames = loadRecords(conn, infoNameId, subNameId, INSRecordType.CNAME);
cnames.sort(recordComparator(INSRecordType.CNAME));
if (cnames.isEmpty()) return new ArrayList<>();
List<INSRecord> aggregated = new ArrayList<>();
for (INSRecord cname : cnames) {
TargetName target = parseCnameTarget(cname.value);
if (target == null) {
getProtocolBridge().getLogger().warn("Invalid CNAME target '" + cname.value + "' on " + canonical);
continue;
}
// recurse on the target name to fetch the original requested type
List<INSRecord> resolvedTarget = resolveInternal(
target.tln,
target.name,
target.sub,
requestedType,
depth + 1,
visited
);
aggregated.addAll(resolvedTarget);
}
aggregated.sort(recordComparator(requestedType));
return aggregated;
} finally {
// important: visited is shared across the recursion chain on purpose
// (do not remove canonical here; loop detection should stay for the whole chain)
}
}
/**
* Deterministic ordering for returned records. The goal is stable ranking so callers can pick index 0.
*
* <p>Rules:
* <ul>
* <li>priority ASC (smaller is better)</li>
* <li>weight DESC (larger is better)</li>
* <li>port ASC (smaller is better)</li>
* <li>ttl DESC (larger is better)</li>
* <li>value ASC (case-insensitive) as final tie-breaker</li>
* </ul>
*
* @param requestedType The type being requested (kept for future type-specific tuning).
* @return Comparator for {@link INSRecord}.
*/
private Comparator<INSRecord> recordComparator(INSRecordType requestedType) {
return Comparator
.comparingInt((INSRecord r) -> safeInt(r.priority))
.thenComparingInt(r -> -safeInt(r.weight))
.thenComparingInt(r -> safeInt(r.port))
.thenComparingInt(r -> -safeInt(r.ttl))
.thenComparing(r -> safeString(r.value), String.CASE_INSENSITIVE_ORDER);
}
/**
* Loads all records of a given type for (infoname_id, subname_id).
*
* @param type May be {@code null} to load all types.
*/
private List<INSRecord> loadRecords(Connection conn, int infonameId, Integer subnameId, INSRecordType type) throws SQLException {
StringBuilder sql = new StringBuilder(
"SELECT type, value, ttl, priority, port, weight " +
"FROM records " +
"WHERE infoname_id = ? "
);
if (subnameId == null) sql.append("AND subname_id IS NULL ");
else sql.append("AND subname_id = ? ");
if (type != null) sql.append("AND type = ? ");
try (PreparedStatement ps = conn.prepareStatement(sql.toString())) {
int idx = 1;
ps.setInt(idx++, infonameId);
if (subnameId != null) ps.setInt(idx++, subnameId);
if (type != null) ps.setString(idx, type.name());
List<INSRecord> result = new ArrayList<>();
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
INSRecordType rType = INSRecordType.valueOf(rs.getString("type"));
String value = rs.getString("value");
int ttl = rs.getInt("ttl");
int priority = rs.getInt("priority");
int port = rs.getInt("port");
int weight = rs.getInt("weight");
result.add(new INSRecord(rType, value, priority, weight, port, ttl));
}
}
return result;
}
}
private Integer findTLNId(Connection conn, String tln) throws SQLException {
String sql = "SELECT id FROM tln WHERE name = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, tln);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) return rs.getInt("id");
}
}
return null;
}
private Integer findInfoNameId(Connection conn, int tlnId, String infoName) throws SQLException {
String sql = "SELECT id FROM infonames WHERE tln_id = ? AND info = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, tlnId);
ps.setString(2, infoName);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) return rs.getInt("id");
}
}
return null;
}
private Integer findSubNameId(Connection conn, int infoNameId, String sub) throws SQLException {
if (sub == null || sub.isEmpty()) return null;
String sql = "SELECT id FROM subnames WHERE infoname_id = ? AND name = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, infoNameId);
ps.setString(2, sub);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) return rs.getInt("id");
}
}
return null;
}
/**
* Parses a CNAME target string into TLN, InfoName and optional subname.
*
* @param value Raw CNAME value (e.g. "sub.app.example" or "example.net").
* @return Parsed {@link TargetName} or {@code null} if invalid.
*/
private TargetName parseCnameTarget(String value) {
if (value == null) return null;
String trimmed = value.trim();
if (trimmed.isEmpty()) return null;
String[] parts = trimmed.split("\\.");
if (parts.length < 2) return null;
String tln = parts[parts.length - 1];
String name = parts[parts.length - 2];
String sub = null;
if (parts.length > 2) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < parts.length - 2; i++) {
if (i > 0) sb.append('.');
sb.append(parts[i]);
}
sub = sb.toString();
}
return new TargetName(tln, name, sub);
}
private static final class TargetName {
final String tln;
final String name;
final String sub;
TargetName(String tln, String name, String sub) {
this.tln = tln;
this.name = name;
this.sub = sub;
}
}
}

View File

@@ -0,0 +1,20 @@
package org.openautonomousconnection.insserver;
import dev.unlegitdqrk.unlegitlibrary.command.events.CommandExecutorMissingPermissionEvent;
import dev.unlegitdqrk.unlegitlibrary.command.events.CommandNotFoundEvent;
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
public class Listener extends EventListener {
@dev.unlegitdqrk.unlegitlibrary.event.Listener
public void onCommandNotFound(CommandNotFoundEvent event) {
StringBuilder argsBuilder = new StringBuilder();
for (String arg : event.getArgs()) argsBuilder.append(arg).append(" ");
Main.getProtocolBridge().getLogger().error("Command '" + event.getName() + argsBuilder.toString() + "' not found!");
}
@dev.unlegitdqrk.unlegitlibrary.event.Listener
public void onMissingCommandPermission(CommandExecutorMissingPermissionEvent event) {
Main.getProtocolBridge().getLogger().error("You do not have enough permissions to execute this command!");
}
}

View File

@@ -0,0 +1,102 @@
package org.openautonomousconnection.insserver;
import dev.unlegitdqrk.unlegitlibrary.command.CommandExecutor;
import dev.unlegitdqrk.unlegitlibrary.command.CommandManager;
import dev.unlegitdqrk.unlegitlibrary.command.CommandPermission;
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
import dev.unlegitdqrk.unlegitlibrary.file.ConfigurationManager;
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
import lombok.Getter;
import org.openautonomousconnection.insserver.commands.StopCommand;
import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.ProtocolValues;
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
import java.io.File;
import java.util.Scanner;
public class Main {
public static final CommandPermission PERMISSION_ALL = new CommandPermission("all", 1);
private static final CommandExecutor commandExecutor = new CommandExecutor("INS", PERMISSION_ALL) {
};
@Getter
private static CommandManager commandManager;
@Getter
private static ProtocolBridge protocolBridge;
@Getter
private static ProtocolValues values;
public static void main(String[] args) throws Exception {
values = new ProtocolValues();
values.packetHandler = new PacketHandler();
values.eventManager = new EventManager();
values.eventManager.registerListener(new Listener());
if (!new File("config.properties").exists()) {
new File("config.properties").createNewFile();
}
ConfigurationManager config = new ConfigurationManager(new File("config.properties"));
config.loadProperties();
if (!config.isSet("db.url")) {
config.set(
"db.url",
"jdbc:mariadb://localhost:3306/ins?useUnicode=true&characterEncoding=utf8"
);
config.saveProperties();
}
if (!config.isSet("db.user")) {
config.set("db.user", "username");
config.saveProperties();
}
if (!config.isSet("db.password")) {
config.set("db.password", "password");
config.saveProperties();
}
if (!config.isSet("port")) {
config.set("port", 1026);
config.saveProperties();
}
if (!config.isSet("ins.info")) {
config.set("ins.info", "INS INFO SITE (HOST)");
config.saveProperties();
}
if (!config.isSet("ins.frontend")) {
config.set("ins.frontend", "INS FRONTEND SITE (HOST)");
config.saveProperties();
}
String url = config.getString("db.url");
String user = config.getString("db.user");
String password = config.getString("db.password");
int tcpPort = config.getInt("port");
String info = config.getString("ins.info");
String frontend = config.getString("ins.frontend");
DatabaseINSServer server = new DatabaseINSServer(info, frontend, url, user, password);
protocolBridge = new ProtocolBridge(server, values, ProtocolVersion.PV_1_0_0_BETA, new File("logs"));
server.start(tcpPort);
commandManager = new CommandManager(values.eventManager);
commandManager.registerCommand(StopCommand.class);
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print(commandExecutor.getName() + "> ");
String line = scanner.nextLine();
commandManager.execute(commandExecutor, line);
}
}
}

View File

@@ -0,0 +1,21 @@
package org.openautonomousconnection.insserver.commands;
import dev.unlegitdqrk.unlegitlibrary.command.Command;
import dev.unlegitdqrk.unlegitlibrary.command.CommandExecutor;
import org.openautonomousconnection.insserver.Main;
import javax.management.InstanceAlreadyExistsException;
import java.util.ArrayList;
import java.util.Collections;
public class StopCommand extends Command {
public StopCommand() throws InstanceAlreadyExistsException {
super(Main.getCommandManager(), "stop", "Stop the Server", "stop", Collections.singletonList(Main.PERMISSION_ALL), new ArrayList<>());
}
@Override
public void execute(CommandExecutor commandExecutor, String s, String[] strings) {
System.exit(0);
}
}

View File

@@ -1,2 +0,0 @@
Manifest-Version: 1.0
Main-Class: org.openautonomousconnection.dns.Main

View File

@@ -1 +0,0 @@
1.0