Compare commits
2 Commits
1.0.1-BETA
...
classic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3198f7ab9f | ||
|
|
7c02af118a |
6
.idea/GitLink.xml
generated
6
.idea/GitLink.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<?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>
|
|
||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -8,7 +8,7 @@
|
|||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" default="true" project-jdk-name="25" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_23" default="true" project-jdk-name="23" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
70
LICENSE
70
LICENSE
@@ -1,2 +1,68 @@
|
|||||||
Please read the license here: https://open-autonomous-connection.org/license.html
|
Open Autonomous Public License (OAPL) v1.0
|
||||||
Download all third parties licenses here: https://open-autonomous-connection.org/assets/licenses.zip
|
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.
|
||||||
36
README.MD
36
README.MD
@@ -1,24 +1,18 @@
|
|||||||
# Open Autonomous Connection INS
|
# Open Autonomous Connection DNS-System
|
||||||
|
|
||||||
This is the INS for our Open Autonomous Connection project.<br />
|
> [!IMPORTANT]
|
||||||
Feel free to join our Discord.
|
> 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 />
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
## License Notice
|
# Bugs/Problems
|
||||||
|
- Update check does not work
|
||||||
This project (OAC) is licensed under
|
# In progress
|
||||||
the [Open Autonomous Public License (OAPL)](https://open-autonomous-connection.org/license.html).
|
# TODO
|
||||||
|
- Subdomains
|
||||||
**Third-party components:**
|
|
||||||
<br />
|
|
||||||
Download all license here: https://open-autonomous-connection.org/assets/licenses.zip
|
|
||||||
- *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
216
database.sql
@@ -1,216 +0,0 @@
|
|||||||
-- 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 */;
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
<?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.1-BETA.0.3</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>
|
|
||||||
<annotationProcessorPaths>
|
|
||||||
<path>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<version>1.18.42</version>
|
|
||||||
</path>
|
|
||||||
</annotationProcessorPaths>
|
|
||||||
<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>
|
|
||||||
<properties>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
<maven.compiler.target>25</maven.compiler.target>
|
|
||||||
<maven.compiler.source>25</maven.compiler.source>
|
|
||||||
</properties>
|
|
||||||
</project>
|
|
||||||
11
frontend/auth/index.php
Normal file
11
frontend/auth/index.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<!--
|
||||||
|
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');
|
||||||
|
?>
|
||||||
69
frontend/auth/login.php
Normal file
69
frontend/auth/login.php
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<!--
|
||||||
|
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
|
||||||
|
?>
|
||||||
67
frontend/auth/register.php
Normal file
67
frontend/auth/register.php
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<!--
|
||||||
|
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>
|
||||||
17
frontend/config.php
Normal file
17
frontend/config.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!--
|
||||||
|
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";
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -1,389 +0,0 @@
|
|||||||
package ins.frontend;
|
|
||||||
|
|
||||||
import ins.frontend.utils.RegistrarDao;
|
|
||||||
import ins.frontend.utils.WebApp;
|
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketFlags;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
|
||||||
import org.openautonomousconnection.webserver.api.Route;
|
|
||||||
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 org.openautonomousconnection.webserver.utils.MergedRequestParams;
|
|
||||||
import org.openautonomousconnection.webserver.utils.WebUrlUtil;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* INS registrar dashboard (TLN / InfoName / Records) for protocol v1.0.1 resource packets.
|
|
||||||
*
|
|
||||||
* <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>
|
|
||||||
*/
|
|
||||||
@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 WebResourceResponsePacket handle(WebPageContext ctx) throws Exception {
|
|
||||||
WebApp.init();
|
|
||||||
|
|
||||||
if (ctx.session == null || !ctx.session.isValid() || ctx.session.getUser() == null) {
|
|
||||||
return plain(ctx, 401, "Authentication required (session).");
|
|
||||||
}
|
|
||||||
|
|
||||||
int userId;
|
|
||||||
try {
|
|
||||||
userId = Integer.parseInt(ctx.session.getUser());
|
|
||||||
} catch (Exception e) {
|
|
||||||
return plain(ctx, 401, "Invalid session user.");
|
|
||||||
}
|
|
||||||
|
|
||||||
RegistrarDao dao = WebApp.get().dao();
|
|
||||||
|
|
||||||
// Build target for param merging: "/path?query"
|
|
||||||
String rawTarget = WebUrlUtil.extractPathAndQuery(ctx.request.getUrl());
|
|
||||||
if (rawTarget == null) rawTarget = "/dashboard.html";
|
|
||||||
|
|
||||||
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(ctx, 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.");
|
|
||||||
|
|
||||||
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 WebResourceResponsePacket render(WebPageContext ctx, 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 html(ctx, 200, 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 WebResourceResponsePacket plain(WebPageContext ctx, int code, String text) {
|
|
||||||
byte[] body = (text == null ? "" : text).getBytes(StandardCharsets.UTF_8);
|
|
||||||
Map<String, String> headers = HeaderMaps.mutable();
|
|
||||||
headers.put("content-length", String.valueOf(body.length));
|
|
||||||
return new WebResourceResponsePacket(outHeader(ctx), code, "text/plain; charset=utf-8", headers, body, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private WebResourceResponsePacket html(WebPageContext ctx, int code, String html) {
|
|
||||||
byte[] body = Html.utf8(html);
|
|
||||||
Map<String, String> headers = HeaderMaps.mutable();
|
|
||||||
headers.put("content-length", String.valueOf(body.length));
|
|
||||||
return new WebResourceResponsePacket(outHeader(ctx), code, "text/html; charset=utf-8", headers, body, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private WebPacketHeader outHeader(WebPageContext ctx) {
|
|
||||||
WebPacketHeader in = (ctx != null && ctx.request != null) ? ctx.request.getHeader() : null;
|
|
||||||
if (in == null) {
|
|
||||||
return new WebPacketHeader(0, 0, 0, 0, WebPacketFlags.RESOURCE, System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
return new WebPacketHeader(
|
|
||||||
in.getRequestId(),
|
|
||||||
in.getTabId(),
|
|
||||||
in.getPageId(),
|
|
||||||
in.getFrameId(),
|
|
||||||
in.getFlags() | WebPacketFlags.RESOURCE,
|
|
||||||
System.currentTimeMillis()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
197
frontend/dashboard.php
Normal file
197
frontend/dashboard.php
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
<!--
|
||||||
|
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>
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package ins.frontend;
|
|
||||||
|
|
||||||
import ins.frontend.utils.WebApp;
|
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketFlags;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
|
||||||
import org.openautonomousconnection.webserver.api.Route;
|
|
||||||
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.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Landing page for INS registrar frontend (v1.0.1).
|
|
||||||
*/
|
|
||||||
@Route(path = "/index.html")
|
|
||||||
public final class index implements WebPage {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WebResourceResponsePacket 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>
|
|
||||||
""");
|
|
||||||
|
|
||||||
byte[] body = Html.utf8(html);
|
|
||||||
Map<String, String> headers = HeaderMaps.mutable();
|
|
||||||
headers.put("content-length", String.valueOf(body.length));
|
|
||||||
|
|
||||||
return new WebResourceResponsePacket(outHeader(ctx), 200, "text/html; charset=utf-8", headers, body, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private WebPacketHeader outHeader(WebPageContext ctx) {
|
|
||||||
WebPacketHeader in = (ctx != null && ctx.request != null) ? ctx.request.getHeader() : null;
|
|
||||||
if (in == null) {
|
|
||||||
return new WebPacketHeader(0, 0, 0, 0, WebPacketFlags.RESOURCE, System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
return new WebPacketHeader(
|
|
||||||
in.getRequestId(),
|
|
||||||
in.getTabId(),
|
|
||||||
in.getPageId(),
|
|
||||||
in.getFrameId(),
|
|
||||||
in.getFlags() | WebPacketFlags.RESOURCE,
|
|
||||||
System.currentTimeMillis()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
40
frontend/index.php
Normal file
40
frontend/index.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<!--
|
||||||
|
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>
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
package ins.frontend;
|
|
||||||
|
|
||||||
import ins.frontend.utils.WebApp;
|
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketFlags;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
|
||||||
import org.openautonomousconnection.webserver.api.Route;
|
|
||||||
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.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Info page for INS registrar frontend (v1.0.1).
|
|
||||||
*/
|
|
||||||
@Route(path = "/info.html")
|
|
||||||
public final class info implements WebPage {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WebResourceResponsePacket 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>
|
|
||||||
""");
|
|
||||||
|
|
||||||
byte[] body = Html.utf8(html);
|
|
||||||
Map<String, String> headers = HeaderMaps.mutable();
|
|
||||||
headers.put("content-length", String.valueOf(body.length));
|
|
||||||
|
|
||||||
return new WebResourceResponsePacket(outHeader(ctx), 200, "text/html; charset=utf-8", headers, body, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private WebPacketHeader outHeader(WebPageContext ctx) {
|
|
||||||
WebPacketHeader in = (ctx != null && ctx.request != null) ? ctx.request.getHeader() : null;
|
|
||||||
if (in == null) {
|
|
||||||
return new WebPacketHeader(0, 0, 0, 0, WebPacketFlags.RESOURCE, System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
return new WebPacketHeader(
|
|
||||||
in.getRequestId(),
|
|
||||||
in.getTabId(),
|
|
||||||
in.getPageId(),
|
|
||||||
in.getFrameId(),
|
|
||||||
in.getFlags() | WebPacketFlags.RESOURCE,
|
|
||||||
System.currentTimeMillis()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,266 +0,0 @@
|
|||||||
package ins.frontend;
|
|
||||||
|
|
||||||
import ins.frontend.utils.UserDao;
|
|
||||||
import ins.frontend.utils.WebApp;
|
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket;
|
|
||||||
import org.openautonomousconnection.protocol.side.web.ProtocolWebServer;
|
|
||||||
import org.openautonomousconnection.protocol.side.web.managers.SessionManager;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketFlags;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
|
||||||
import org.openautonomousconnection.webserver.api.Route;
|
|
||||||
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.lang.reflect.Method;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Login page (v1.0.1).
|
|
||||||
*/
|
|
||||||
@Route(path = "/login.html")
|
|
||||||
public final class login implements WebPage {
|
|
||||||
|
|
||||||
private enum ReqMethod { GET, POST, OTHER }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WebResourceResponsePacket handle(WebPageContext ctx) throws Exception {
|
|
||||||
WebApp.init();
|
|
||||||
|
|
||||||
// If a valid session already exists -> go dashboard (keep session)
|
|
||||||
if (ctx.session != null && ctx.session.isValid() && ctx.session.getUser() != null) {
|
|
||||||
return redirect302(ctx, "dashboard.html", ctx.session.getSessionId());
|
|
||||||
}
|
|
||||||
|
|
||||||
ReqMethod method = detectMethod(ctx);
|
|
||||||
if (method == ReqMethod.GET) {
|
|
||||||
return ok(ctx, renderForm(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method != ReqMethod.POST) {
|
|
||||||
return text(ctx, 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(ctx, 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(ctx, renderForm("Missing username/password."));
|
|
||||||
}
|
|
||||||
|
|
||||||
String lookupUsername = username.trim();
|
|
||||||
|
|
||||||
UserDao.UserRow user = WebApp.get().users().findByUsername(lookupUsername).orElse(null);
|
|
||||||
if (user == null) {
|
|
||||||
return ok(ctx, renderForm("Invalid credentials."));
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean okPw = WebApp.get().passwordHasher().verify(password, user.passwordEncoded());
|
|
||||||
if (!okPw) {
|
|
||||||
return ok(ctx, 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(ctx, "dashboard.html", session);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ReqMethod detectMethod(WebPageContext ctx) {
|
|
||||||
// Preferred: request.getMethod() via reflection (avoids depending on specific enum package).
|
|
||||||
try {
|
|
||||||
Method m = ctx.request.getClass().getMethod("getMethod");
|
|
||||||
Object v = m.invoke(ctx.request);
|
|
||||||
if (v != null) {
|
|
||||||
String s = String.valueOf(v).trim().toUpperCase(Locale.ROOT);
|
|
||||||
if ("GET".equals(s)) return ReqMethod.GET;
|
|
||||||
if ("POST".equals(s)) return ReqMethod.POST;
|
|
||||||
return ReqMethod.OTHER;
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
// Fall back below.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: treat presence of body as POST-like.
|
|
||||||
byte[] body = ctx.request.getBody();
|
|
||||||
if (body != null && body.length > 0) return ReqMethod.POST;
|
|
||||||
return ReqMethod.GET;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WebResourceResponsePacket ok(WebPageContext ctx, String html) {
|
|
||||||
byte[] body = Html.utf8(html);
|
|
||||||
Map<String, String> headers = HeaderMaps.mutable();
|
|
||||||
headers.put("content-length", String.valueOf(body.length));
|
|
||||||
return new WebResourceResponsePacket(outHeader(ctx), 200, "text/html; charset=utf-8", headers, body, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WebResourceResponsePacket text(WebPageContext ctx, int code, String msg) {
|
|
||||||
byte[] body = (msg == null ? "" : msg).getBytes(StandardCharsets.UTF_8);
|
|
||||||
Map<String, String> headers = HeaderMaps.mutable();
|
|
||||||
headers.put("content-length", String.valueOf(body.length));
|
|
||||||
return new WebResourceResponsePacket(outHeader(ctx), code, "text/plain; charset=utf-8", headers, body, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WebResourceResponsePacket redirect302(WebPageContext ctx, String location, String session) {
|
|
||||||
Map<String, String> headers = HeaderMaps.mutable();
|
|
||||||
headers.put("Location", location);
|
|
||||||
if (session != null && !session.isBlank()) {
|
|
||||||
headers.put("Set-Cookie", "session=" + session + "; Path=/; HttpOnly; SameSite=Lax");
|
|
||||||
// optional fallback for stacks that read a direct header:
|
|
||||||
headers.put("session", session);
|
|
||||||
}
|
|
||||||
return new WebResourceResponsePacket(outHeader(ctx), 302, "text/plain; charset=utf-8", headers, new byte[0], null);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(c = 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(s.charAt(i)).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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WebPacketHeader outHeader(WebPageContext ctx) {
|
|
||||||
WebPacketHeader in = (ctx != null && ctx.request != null) ? ctx.request.getHeader() : null;
|
|
||||||
if (in == null) {
|
|
||||||
return new WebPacketHeader(0, 0, 0, 0, WebPacketFlags.RESOURCE, System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
return new WebPacketHeader(
|
|
||||||
in.getRequestId(),
|
|
||||||
in.getTabId(),
|
|
||||||
in.getPageId(),
|
|
||||||
in.getFrameId(),
|
|
||||||
in.getFlags() | WebPacketFlags.RESOURCE,
|
|
||||||
System.currentTimeMillis()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
package ins.frontend;
|
|
||||||
|
|
||||||
import ins.frontend.utils.RegistrationService;
|
|
||||||
import ins.frontend.utils.WebApp;
|
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket;
|
|
||||||
import org.openautonomousconnection.protocol.side.web.ProtocolWebServer;
|
|
||||||
import org.openautonomousconnection.protocol.side.web.managers.SessionManager;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketFlags;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
|
|
||||||
import org.openautonomousconnection.webserver.api.Route;
|
|
||||||
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.lang.reflect.Method;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register page (v1.0.1).
|
|
||||||
*/
|
|
||||||
@Route(path = "/register.html")
|
|
||||||
public final class register implements WebPage {
|
|
||||||
|
|
||||||
private enum ReqMethod { GET, POST, OTHER }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WebResourceResponsePacket handle(WebPageContext ctx) throws Exception {
|
|
||||||
WebApp.init();
|
|
||||||
|
|
||||||
// If a valid session already exists -> go dashboard (keep session)
|
|
||||||
if (ctx.session != null && ctx.session.isValid() && ctx.session.getUser() != null) {
|
|
||||||
return redirect302(ctx, "dashboard.html", ctx.session.getSessionId());
|
|
||||||
}
|
|
||||||
|
|
||||||
ReqMethod method = detectMethod(ctx);
|
|
||||||
if (method == ReqMethod.GET) {
|
|
||||||
return ok(ctx, renderForm(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method != ReqMethod.POST) {
|
|
||||||
return text(ctx, 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(ctx, 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(ctx, renderForm(r.error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(ctx, "dashboard.html", session);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ReqMethod detectMethod(WebPageContext ctx) {
|
|
||||||
try {
|
|
||||||
Method m = ctx.request.getClass().getMethod("getMethod");
|
|
||||||
Object v = m.invoke(ctx.request);
|
|
||||||
if (v != null) {
|
|
||||||
String s = String.valueOf(v).trim().toUpperCase(Locale.ROOT);
|
|
||||||
if ("GET".equals(s)) return ReqMethod.GET;
|
|
||||||
if ("POST".equals(s)) return ReqMethod.POST;
|
|
||||||
return ReqMethod.OTHER;
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] body = ctx.request.getBody();
|
|
||||||
if (body != null && body.length > 0) return ReqMethod.POST;
|
|
||||||
return ReqMethod.GET;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WebResourceResponsePacket ok(WebPageContext ctx, String html) {
|
|
||||||
byte[] body = Html.utf8(html);
|
|
||||||
Map<String, String> headers = HeaderMaps.mutable();
|
|
||||||
headers.put("content-length", String.valueOf(body.length));
|
|
||||||
return new WebResourceResponsePacket(outHeader(ctx), 200, "text/html; charset=utf-8", headers, body, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WebResourceResponsePacket text(WebPageContext ctx, int code, String msg) {
|
|
||||||
byte[] body = (msg == null ? "" : msg).getBytes(StandardCharsets.UTF_8);
|
|
||||||
Map<String, String> headers = HeaderMaps.mutable();
|
|
||||||
headers.put("content-length", String.valueOf(body.length));
|
|
||||||
return new WebResourceResponsePacket(outHeader(ctx), code, "text/plain; charset=utf-8", headers, body, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WebResourceResponsePacket redirect302(WebPageContext ctx, String location, String session) {
|
|
||||||
Map<String, String> headers = HeaderMaps.mutable();
|
|
||||||
headers.put("Location", location);
|
|
||||||
if (session != null && !session.isBlank()) {
|
|
||||||
headers.put("Set-Cookie", "session=" + session + "; Path=/; HttpOnly; SameSite=Lax");
|
|
||||||
headers.put("session", session);
|
|
||||||
}
|
|
||||||
return new WebResourceResponsePacket(outHeader(ctx), 302, "text/plain; charset=utf-8", headers, new byte[0], null);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(c = 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(s.charAt(i)).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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WebPacketHeader outHeader(WebPageContext ctx) {
|
|
||||||
WebPacketHeader in = (ctx != null && ctx.request != null) ? ctx.request.getHeader() : null;
|
|
||||||
if (in == null) {
|
|
||||||
return new WebPacketHeader(0, 0, 0, 0, WebPacketFlags.RESOURCE, System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
return new WebPacketHeader(
|
|
||||||
in.getRequestId(),
|
|
||||||
in.getTabId(),
|
|
||||||
in.getPageId(),
|
|
||||||
in.getFrameId(),
|
|
||||||
in.getFlags() | WebPacketFlags.RESOURCE,
|
|
||||||
System.currentTimeMillis()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,490 +0,0 @@
|
|||||||
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) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
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) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
17
frontend/utils/connection.php
Normal file
17
frontend/utils/connection.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!--
|
||||||
|
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";
|
||||||
|
?>
|
||||||
449
frontend/utils/functions.php
Normal file
449
frontend/utils/functions.php
Normal file
@@ -0,0 +1,449 @@
|
|||||||
|
<!--
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
11
frontend/utils/index.php
Normal file
11
frontend/utils/index.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<!--
|
||||||
|
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');
|
||||||
|
?>
|
||||||
194
open_autonomous_connection.sql
Normal file
194
open_autonomous_connection.sql
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
-- 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 */;
|
||||||
98
pom.xml
98
pom.xml
@@ -1,22 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
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/xsd/maven-4.0.0.xsd">
|
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>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>org.openautonomousconnection</groupId>
|
<groupId>org.openautonomousconnection</groupId>
|
||||||
<artifactId>INSServer</artifactId>
|
<artifactId>INSServer</artifactId>
|
||||||
<version>1.0.1-BETA.0.3</version>
|
<version>1.0-CLASSIC</version>
|
||||||
<organization>
|
<organization>
|
||||||
<name>Open Autonomous Connection</name>
|
<name>Open Autonomous Connection</name>
|
||||||
<url>https://open-autonomous-connection.org/</url>
|
<url>https://open-autonomous-connection.org/</url>
|
||||||
</organization>
|
</organization>
|
||||||
<url>https://open-autonomous-connection.org/</url>
|
<url>https://open-autonomous-connection.org/</url>
|
||||||
<description>The default INS-Server</description>
|
<description>The default DNS-Server</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>25</maven.compiler.source>
|
<maven.compiler.source>23</maven.compiler.source>
|
||||||
<maven.compiler.target>25</maven.compiler.target>
|
<maven.compiler.target>23</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@@ -50,8 +50,9 @@
|
|||||||
|
|
||||||
<licenses>
|
<licenses>
|
||||||
<license>
|
<license>
|
||||||
<name>Open Autonomous Public License (OAPL)</name>
|
<name>Open Autonomous Public License</name>
|
||||||
<url>https://open-autonomous-connection.org/license.html</url>
|
<url>https://open-autonomous-connection.org/license.html</url>
|
||||||
|
<distribution>repo</distribution>
|
||||||
</license>
|
</license>
|
||||||
</licenses>
|
</licenses>
|
||||||
|
|
||||||
@@ -68,86 +69,13 @@
|
|||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openautonomousconnection</groupId>
|
<groupId>org.openautonomousconnection</groupId>
|
||||||
<artifactId>Protocol</artifactId>
|
<artifactId>protocol</artifactId>
|
||||||
<version>1.0.1-BETA.0.6</version>
|
<version>1.0.0-CLASSIC</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mariadb.jdbc</groupId>
|
<groupId>mysql</groupId>
|
||||||
<artifactId>mariadb-java-client</artifactId>
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
<version>3.5.7</version>
|
<version>5.1.39</version>
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<version>1.18.42</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</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>
|
|
||||||
<annotationProcessorPaths>
|
|
||||||
<path>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<version>1.18.42</version>
|
|
||||||
</path>
|
|
||||||
</annotationProcessorPaths>
|
|
||||||
<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>
|
</project>
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
109
src/main/java/org/openautonomousconnection/dns/Main.java
Normal file
109
src/main/java/org/openautonomousconnection/dns/Main.java
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/main/java/org/openautonomousconnection/dns/Server.java
Normal file
56
src/main/java/org/openautonomousconnection/dns/Server.java
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
116
src/main/java/org/openautonomousconnection/dns/utils/Config.java
Normal file
116
src/main/java/org/openautonomousconnection/dns/utils/Config.java
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,346 +0,0 @@
|
|||||||
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().getProtocolValues().logger.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().getProtocolValues().logger.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().getProtocolValues().logger.warn("CNAME loop detected for " + canonical + " type=" + requestedType);
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (depth > MAX_CNAME_DEPTH) {
|
|
||||||
getProtocolBridge().getProtocolValues().logger.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().getProtocolValues().logger.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
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().getProtocolValues().logger.error("Command '" + event.getName() + argsBuilder.toString() + "' not found!");
|
|
||||||
}
|
|
||||||
|
|
||||||
@dev.unlegitdqrk.unlegitlibrary.event.Listener
|
|
||||||
public void onMissingCommandPermission(CommandExecutorMissingPermissionEvent event) {
|
|
||||||
Main.getProtocolBridge().getProtocolValues().logger.error("You do not have enough permissions to execute this command!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
package org.openautonomousconnection.insserver;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.addon.AddonLoader;
|
|
||||||
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 dev.unlegitdqrk.unlegitlibrary.utils.Logger;
|
|
||||||
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 {
|
|
||||||
if (!new File("logs").exists()) new File("logs").mkdir();
|
|
||||||
Logger logger = new Logger(new File("logs"), false, true);
|
|
||||||
|
|
||||||
values = new ProtocolValues();
|
|
||||||
values.packetHandler = new PacketHandler();
|
|
||||||
values.eventManager = new EventManager();
|
|
||||||
values.protocolVersion = ProtocolVersion.PV_1_0_1_BETA;
|
|
||||||
values.eventManager.registerListener(new Listener());
|
|
||||||
values.logger = logger;
|
|
||||||
|
|
||||||
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);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2
src/main/resources/META-INF/MANIFEST.MF
Normal file
2
src/main/resources/META-INF/MANIFEST.MF
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Manifest-Version: 1.0
|
||||||
|
Main-Class: org.openautonomousconnection.dns.Main
|
||||||
1
src/main/resources/version.txt
Normal file
1
src/main/resources/version.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
1.0
|
||||||
Reference in New Issue
Block a user