Compare commits
1 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
9aa568eaba |
|
|
@ -24,13 +24,3 @@
|
|||
hs_err_pid*
|
||||
replay_pid*
|
||||
|
||||
**/.DS_Store
|
||||
**/*.class
|
||||
/bin/
|
||||
/.project
|
||||
/.classpath
|
||||
/.factorypath
|
||||
/.settings/
|
||||
/target/
|
||||
/.apt_generated/
|
||||
/.apt_generated_tests/
|
||||
|
|
|
|||
47
README.md
47
README.md
|
|
@ -1,46 +1,3 @@
|
|||
# PU 1: "Praktikum Service-Discovery"
|
||||
# Verteilte_Systeme
|
||||
|
||||
Dieses Projekt wurde von der **Gruppe D** entwickelt:
|
||||
**Antoniadis Dimitrios, Dexheimer Fynn, Schmitt Corinna**
|
||||
|
||||
Dieses Projekt implementiert einen Syslog-Server auf Basis von UDP gemäß dem RFC 5424 Standard. Es beinhaltet eine automatische Service-Discovery via Broadcast.
|
||||
|
||||
> **Hinweis:** Um den Server zu testen, wird ein entsprechender Client benötigt. Das System setzt die installierten Technologien **Java** und **Maven** voraus. Die Nutzung von **Just** ist optional und dient lediglich der vereinfachten Ausführung der Befehle.
|
||||
|
||||
## Projektstruktur & Klassen
|
||||
|
||||
* **`pom.xml`**: Maven-Konfigurationsdatei für Abhängigkeiten und Build-Prozess.
|
||||
* **`justfile`**: Automatisierungsskript für einfache Ausführung (`just server`).
|
||||
* **`vs.SyslogServer`**: Der Hauptdienst. Er startet zwei parallele Threads:
|
||||
* **Syslog-Service**: Empfängt Log-Nachrichten auf Port 514, validiert die Länge und trennt Metadaten optisch vom Nachrichteninhalt.
|
||||
* **Discovery-Service**: Antwortet auf Broadcast-Anfragen auf Port 8888, um Clients die Server-IP mitzuteilen.
|
||||
* **`vs.SyslogMessage`**: Kernklasse zur Erzeugung/Repräsentation standardkonformer Nachrichten (inkl. Header, Zeitstempel und Structured Data).
|
||||
* **`vs.AsciiChars` / `vs.StructuredData`**: Hilfsklassen zur Einhaltung der RFC-Spezifikationen.
|
||||
|
||||
|
||||
## Funktionsablauf (Service Discovery & Logging)
|
||||
|
||||
1. **Broadcast**: Ein Client sendet ein UDP-Paket an die Broadcast-Adresse `255.255.255.255:8888`.
|
||||
2. **Antwort**: Der Server empfängt den Suchruf und sendet ein Bestätigungs-Paket an den Client zurück.
|
||||
3. **Identifikation**: Der Client extrahiert die IP-Adresse des Servers aus dem Antwort-Paket.
|
||||
4. **Logging**: Der Client formatiert die Nachricht nach RFC 5424 und sendet sie direkt an den Server (Port 514).
|
||||
5. **Verarbeitung**: Der Server empfängt das Datenpaket, validiert die Länge, entfernt das Byte Order Mark (BOM) und trennt die RFC-Metadaten optisch vom eigentlichen Nachrichteninhalt für eine strukturierte Konsolenausgabe.
|
||||
|
||||
## Ausführung
|
||||
|
||||
Stellen Sie sicher, dass **Java** und **Maven** auf Ihrem System installiert sind. Die Installation von **Just** wird empfohlen, ist jedoch optional.
|
||||
|
||||
### 1. Konfiguration für Linux/macOS
|
||||
Da das `justfile` standardmäßig für die PowerShell optimiert ist, passen Sie bei der Nutzung von Unix-basierten Systemen bitte die erste Zeile im `justfile` an:
|
||||
* Ändern Sie `set shell := ["powershell.exe", "-c"]` zu `set shell := ["sh", "-c"]`.
|
||||
|
||||
### 2. Wichtige Befehle
|
||||
Nutzen Sie `just`, um die folgenden Aufgaben automatisiert auszuführen:
|
||||
|
||||
* **Kompilieren**: `just compile`
|
||||
* **Server starten**: `just server`
|
||||
* **Projekt aufräumen**: `just clean`
|
||||
|
||||
**Hinweise:**
|
||||
* Weitere Befehle (z. B. für Tests oder Javadoc) finden Sie direkt im `justfile`.
|
||||
* Falls Sie **Just** nicht verwenden möchten, können die entsprechenden Maven-Befehle (z. B. `mvn compile` oder `mvn exec:java...`) direkt genutzt werden.
|
||||
Bearbeitung Prüfungsvorleistung für Verteilte Systeme
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
In dieser Aufgabe soll ein Logging-System entwickelt werden, das das Syslog-Protokoll (RFC 5424) unterstützt.
|
||||
Dabei sollen abweichend von der Spezifikation in RFC 5424 die folgenden Vereinfachungen gelten:
|
||||
|
||||
- Als Transportprotokoll soll ausschließlich UDP verwendet werden.
|
||||
- TLS (Transport Layer Security) braucht nicht betrachtet werden.
|
||||
- Der Syslog-Server braucht die empfangenen Daten nur nachrichtenweise auf der Konsole (Standard-Ausgabe) ausgeben.
|
||||
Dabei sollen Informationen über den sendenden Client (insb. IP-Adresse) mitausgegeben werden.
|
||||
- Strukturierte Daten brauchen nicht berücksichtigt werden.
|
||||
|
||||
Berücksichtigen Sie unbedingt die maximale Nachrichtenlänge und prüfen Sie, dass Nachrichten, die von Clients empfangen werden auch kurz genug sind.
|
||||
|
||||
Abweichend vom Syslog-Protokoll soll der Server folgende zusätzliche Eigenschaften haben:
|
||||
|
||||
- Die IP-Adresse des Logging-Dienstes muss vom Client durch eine automatische Service Discovery ermittelt werden.
|
||||
Clients sollen die IP-Adresse des Servers also durch Broadcast-Nachrichten selbstständig ermitteln können.
|
||||
Es ist möglich, diese Anforderung unberücksichtigt zu lassen und die IP-Adresse des Servers fest zu konfigurieren. Es gibt dann aber bis zu 8 Punkte Abzug.
|
||||
|
||||
- Der Server soll zwei UDP-Sockets verwenden: einen auf dem SYSLOG-Standard-Port und den anderen auf dem Port 8888 für die Auto-Discovery.
|
||||
|
||||
- (Broadcast-) Nachrichten an 8888 sollen ähnlich wie bei DHCP beantwortet werden.
|
||||
Da der Absender aber bereits eine IP-Adresse hat, kann hier direkt eine UDP-Nachricht zurückgeschickt werden.
|
||||
Die zurückgeschickte Nachricht kann leer sein, der Client kann daraus trotzdem die Adresse des SYSLOG-Servers (seine IP-Adresse) auslesen
|
||||
und dann Log-Nachrichten an den SYSLOG-Port auf dem Host mit der so gefundenen IP-Adresse senden.
|
||||
|
||||
Entwickeln Sie den Server in Java. Ein Client (in einer beliebigen Programmiersprache) ermöglicht Ihnen das Testen, ist aber nicht abzugeben.
|
||||
Geben Sie den Quellcode des Servers in Form eines Maven- oder Eclipse-Projekts als ZIP/TAR/TGZ-Archiv verpackt ab.
|
||||
|
||||
Client und Server sollen sich in demselben Sub-Netz befinden und über IP miteinander kommunizieren.
|
||||
Zur Kommunikation dürfen nur Datagramme (UDP) verwendet werden.
|
||||
|
||||
Auf dem Ziel-System wird es nur genau ein Netzwerk geben (nicht Ethernet und Wifi mit getrennten Sub-Netzen).
|
||||
Sie können daher Broadcast-Nachrichten an 255.255.255.255 schicken.
|
||||
vs.pu01.tgz
|
||||
20
justfile
20
justfile
|
|
@ -1,20 +0,0 @@
|
|||
# Konfiguration für Windows (PowerShell).
|
||||
# Falls Sie Linux/macOS nutzen, bitte diese Zeile in ["sh", "-c"] ändern:
|
||||
set shell := ["powershell.exe", "-c"]
|
||||
|
||||
default:
|
||||
mvn clean compile test
|
||||
server: compile
|
||||
mvn exec:java "-Dexec.mainClass=vs.SyslogServer"
|
||||
client +args="": compile
|
||||
mvn exec:java "-Dexec.mainClass=vs.SyslogClient" "-Dexec.args={{args}}"
|
||||
exec class +args="": compile
|
||||
mvn exec:java "-Dexec.mainClass=vs.{{class}}" "-Dexec.args={{args}}"
|
||||
clean:
|
||||
mvn clean
|
||||
compile:
|
||||
mvn compile
|
||||
test: compile
|
||||
mvn test
|
||||
javadoc:
|
||||
mvn javadoc:javadoc
|
||||
60
pom.xml
60
pom.xml
|
|
@ -1,60 +0,0 @@
|
|||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>vs</groupId>
|
||||
<artifactId>vs.pu01</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.release>10</maven.compiler.release>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency><!-- für Unit-Tests -->
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.10.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency><!-- für Lombok -->
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency><!-- für net.jcip Annotationen -->
|
||||
<groupId>net.jcip</groupId>
|
||||
<artifactId>jcip-annotations</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin><!-- für Unit-Tests [mvn test] -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</plugin>
|
||||
<plugin><!-- [mvn compile] -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.9.0</version>
|
||||
</plugin>
|
||||
<plugin><!-- [mvn exec:java] -->
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</plugin>
|
||||
<plugin><!-- [mvn javadoc:javadoc] -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<configuration>
|
||||
<show>private</show>
|
||||
<locale>en_US</locale>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
package vs;
|
||||
|
||||
/**
|
||||
* helper class for RFC 5424 (https://datatracker.ietf.org/doc/html/rfc5424)
|
||||
* compliant log messages as immutable Java objects - representation of a subset
|
||||
* of printable strings of specific length
|
||||
*
|
||||
* @author Sandro Leuchter
|
||||
*
|
||||
*/
|
||||
public abstract class AsciiChars {
|
||||
|
||||
private final String value;
|
||||
|
||||
public String value() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
protected AsciiChars(int length, String value) {
|
||||
if (value != null) {
|
||||
if (value.length() > length) {
|
||||
throw new IllegalArgumentException(
|
||||
"Stringlänge = " + value.length() + " > " + length
|
||||
);
|
||||
}
|
||||
for (int c : value.getBytes()) {
|
||||
if (c < 33 || c > 126) {
|
||||
throw new IllegalArgumentException(
|
||||
"Stringinhalt nicht printable US-ASCII ohne Space"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (value() == null || value().length() == 0) {
|
||||
return "-";
|
||||
} else {
|
||||
return value();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class L004 extends AsciiChars {
|
||||
|
||||
public L004(String value) {
|
||||
super(4, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class L012 extends AsciiChars {
|
||||
|
||||
public L012(String value) {
|
||||
super(12, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class L032 extends AsciiChars {
|
||||
|
||||
public L032(String value) {
|
||||
super(32, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class L048 extends AsciiChars {
|
||||
|
||||
public L048(String value) {
|
||||
super(48, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class L128 extends AsciiChars {
|
||||
|
||||
public L128(String value) {
|
||||
super(128, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class L255 extends AsciiChars {
|
||||
|
||||
public L255(String value) {
|
||||
super(255, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,180 +0,0 @@
|
|||
package vs;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* helper class for RFC 5424 (https://datatracker.ietf.org/doc/html/rfc5424)
|
||||
* compliant log messages as immutable Java objects - structured data (set of
|
||||
* key/value-pairs) with some predefined sets according to the standard
|
||||
*
|
||||
* @author Sandro Leuchter
|
||||
*
|
||||
*/
|
||||
public class StructuredData {
|
||||
|
||||
public static class Element {
|
||||
|
||||
private final String name;
|
||||
private List<Param> parameters;
|
||||
|
||||
public static Element newTimeQuality(
|
||||
boolean tzKnown,
|
||||
boolean isSynced
|
||||
) {
|
||||
return newTimeQuality(tzKnown, isSynced, null);
|
||||
}
|
||||
|
||||
public static Element newTimeQuality(
|
||||
boolean tzKnown,
|
||||
boolean isSynced,
|
||||
Integer syncAccuracy
|
||||
) {
|
||||
var e = new Element("timeQuality");
|
||||
e.add(new Param("tzKnown", (tzKnown) ? "1" : "0"));
|
||||
e.add(new Param("isSynced", (isSynced) ? "1" : "0"));
|
||||
if (syncAccuracy != null && !isSynced) {
|
||||
e.add(new Param("syncAccuracy", String.valueOf(syncAccuracy)));
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
public static Element newOrigin(
|
||||
String enterpriseId,
|
||||
String software,
|
||||
String swVersion
|
||||
) {
|
||||
return newOrigin(
|
||||
new String[] {},
|
||||
enterpriseId,
|
||||
software,
|
||||
swVersion
|
||||
);
|
||||
}
|
||||
|
||||
public static Element newOrigin(
|
||||
String ip,
|
||||
String enterpriseId,
|
||||
String software,
|
||||
String swVersion
|
||||
) {
|
||||
return newOrigin(
|
||||
new String[] { ip },
|
||||
enterpriseId,
|
||||
software,
|
||||
swVersion
|
||||
);
|
||||
}
|
||||
|
||||
public static Element newOrigin(
|
||||
String[] ip,
|
||||
String enterpriseId,
|
||||
String software,
|
||||
String swVersion
|
||||
) {
|
||||
var e = new Element("origin");
|
||||
for (var p : ip) {
|
||||
e = e.add(new Param("ip", p));
|
||||
}
|
||||
if (enterpriseId != null && !enterpriseId.equals("")) {
|
||||
e = e.add(new Param("enterpriseId", enterpriseId));
|
||||
}
|
||||
if (software != null && !software.equals("")) {
|
||||
e = e.add(new Param("software", software));
|
||||
}
|
||||
if (swVersion != null && !swVersion.equals("")) {
|
||||
e = e.add(new Param("swVersion", swVersion));
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
public static Element newMeta(
|
||||
Integer sequenceId,
|
||||
Integer sysUpTime,
|
||||
String language
|
||||
) {
|
||||
var e = new Element("meta");
|
||||
if (sequenceId != null && sequenceId > 0) {
|
||||
e = e.add(
|
||||
new Param(
|
||||
"sequenceId",
|
||||
String.valueOf(sequenceId % 2147483647)
|
||||
)
|
||||
);
|
||||
}
|
||||
if (sysUpTime != null && sysUpTime >= 0) {
|
||||
e = e.add(new Param("sysUpTime", String.valueOf(sysUpTime)));
|
||||
}
|
||||
if (language != null && !language.equals("")) {
|
||||
e = e.add(new Param("language", language));
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
public Element(String name) {
|
||||
this.name = name;
|
||||
this.parameters = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Element add(Param parameter) {
|
||||
var e = new Element(this.name);
|
||||
e.parameters = this.parameters;
|
||||
e.parameters.add(parameter);
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
var str = "[" + this.name;
|
||||
for (var p : this.parameters) {
|
||||
str = str + " " + p.toString();
|
||||
}
|
||||
return str + "]";
|
||||
}
|
||||
}
|
||||
|
||||
public static class Param {
|
||||
|
||||
private final String name;
|
||||
// name: printable US-ASCII string ^[@=\]\"\s]+
|
||||
// "@" + private enterpise number "@\d+(\.\d+)*"
|
||||
private final String value;
|
||||
|
||||
public Param(String name, String value) {
|
||||
this.name = name; // 7-Bit ASCII
|
||||
this.value = value; // UTF-8
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name + "=\"" + this.value + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
private List<Element> params;
|
||||
|
||||
public StructuredData() {
|
||||
this.params = new ArrayList<>();
|
||||
}
|
||||
|
||||
public StructuredData(List<Element> params) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (this.params.size() == 0) {
|
||||
return "-";
|
||||
}
|
||||
var str = "";
|
||||
for (var p : this.params) {
|
||||
str = str + p.toString();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public StructuredData add(Element e) {
|
||||
var p = this.params;
|
||||
p.add(e);
|
||||
return new StructuredData(p);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
package vs;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class SyslogClient {
|
||||
|
||||
private static final int SYSLOG_PORT = 514;
|
||||
private static final int DISCOVERY_PORT = 8888;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
DatagramSocket socket = new DatagramSocket();
|
||||
socket.setBroadcast(true);
|
||||
|
||||
// 1. SERVICE DISCOVERY
|
||||
System.out.println("[CLIENT] Suche Syslog-Server via Broadcast...");
|
||||
byte[] discoverMsg = "DISCOVER".getBytes();
|
||||
DatagramPacket discoverPacket = new DatagramPacket(
|
||||
discoverMsg, discoverMsg.length,
|
||||
InetAddress.getByName("255.255.255.255"), DISCOVERY_PORT
|
||||
);
|
||||
socket.send(discoverPacket);
|
||||
|
||||
// Auf Antwort warten
|
||||
byte[] buffer = new byte[256];
|
||||
DatagramPacket response = new DatagramPacket(buffer, buffer.length);
|
||||
socket.receive(response);
|
||||
|
||||
InetAddress serverAddress = response.getAddress();
|
||||
System.out.println("[CLIENT] Server gefunden unter: " + serverAddress.getHostAddress());
|
||||
|
||||
// 2. NACHRICHT ERSTELLEN
|
||||
String text = (args.length > 0) ? String.join(" ", args) : "Standard-Testnachricht";
|
||||
|
||||
SyslogMessage msg = new SyslogMessage(
|
||||
SyslogMessage.Facility.USER,
|
||||
SyslogMessage.Severity.INFORMATIONAL,
|
||||
new AsciiChars.L255("mein-laptop"),
|
||||
new AsciiChars.L048("SyslogClient"),
|
||||
new AsciiChars.L128("PID1234"),
|
||||
new AsciiChars.L032("MSG-01"),
|
||||
new StructuredData().add(StructuredData.Element.newTimeQuality(true, true)),
|
||||
new SyslogMessage.TextMessage(text)
|
||||
);
|
||||
|
||||
// 3. SENDEN
|
||||
String logString = msg.toString();
|
||||
byte[] data = logString.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
DatagramPacket syslogPacket = new DatagramPacket(
|
||||
data, data.length, serverAddress, SYSLOG_PORT
|
||||
);
|
||||
|
||||
socket.send(syslogPacket);
|
||||
System.out.println("[CLIENT] Nachricht gesendet: " + logString);
|
||||
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,213 +0,0 @@
|
|||
package vs;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* RFC 5424 (https://datatracker.ietf.org/doc/html/rfc5424) compliant log
|
||||
* messages as immutable Java objects
|
||||
*
|
||||
* @author Sandro Leuchter
|
||||
*
|
||||
*/
|
||||
public class SyslogMessage implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -5895573029109990861L;
|
||||
private final Facility fac;
|
||||
private final Severity sev;
|
||||
private final AsciiChars.L255 host;
|
||||
private final AsciiChars.L048 appName;
|
||||
private final AsciiChars.L128 procId;
|
||||
private final AsciiChars.L032 msgId;
|
||||
private final StructuredData data;
|
||||
private final Message message;
|
||||
|
||||
public SyslogMessage(
|
||||
Facility fac,
|
||||
Severity sev,
|
||||
AsciiChars.L255 host,
|
||||
AsciiChars.L048 appName,
|
||||
AsciiChars.L128 procId,
|
||||
AsciiChars.L032 msgId,
|
||||
StructuredData data,
|
||||
Message message
|
||||
) {
|
||||
this.fac = fac;
|
||||
this.sev = sev;
|
||||
this.host = host;
|
||||
this.appName = appName;
|
||||
this.procId = procId;
|
||||
this.msgId = msgId;
|
||||
this.data = data;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Facility fac() {
|
||||
return this.fac;
|
||||
}
|
||||
|
||||
public Severity sev() {
|
||||
return sev;
|
||||
}
|
||||
|
||||
public AsciiChars.L255 host() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public AsciiChars.L048 appName() {
|
||||
return appName;
|
||||
}
|
||||
|
||||
public AsciiChars.L128 procId() {
|
||||
return procId;
|
||||
}
|
||||
|
||||
public AsciiChars.L032 msgId() {
|
||||
return msgId;
|
||||
}
|
||||
|
||||
public StructuredData data() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public Message message() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public static int version() {
|
||||
return VERSION;
|
||||
}
|
||||
|
||||
public static enum Facility {
|
||||
KERNEL,
|
||||
USER,
|
||||
MAIL_SYSTEM,
|
||||
SYS_DAEMON,
|
||||
SECURITY1,
|
||||
INTERNAL,
|
||||
PRINTER,
|
||||
NEWS,
|
||||
UUCP,
|
||||
CLOCK1,
|
||||
SECURITY2,
|
||||
FTP,
|
||||
NTP,
|
||||
AUDIT,
|
||||
ALERT,
|
||||
CLOCK2,
|
||||
LOCAL0,
|
||||
LOCAL1,
|
||||
LOCAL2,
|
||||
LOCAL3,
|
||||
LOCAL4,
|
||||
LOCAL5,
|
||||
LOCAL6,
|
||||
LOCAL7,
|
||||
}
|
||||
|
||||
public static enum Severity {
|
||||
EMERGENCY,
|
||||
ALERT,
|
||||
CRITICAL,
|
||||
ERROR,
|
||||
WARNING,
|
||||
NOTICE,
|
||||
INFORMATIONAL,
|
||||
DEBUG,
|
||||
}
|
||||
|
||||
public static interface Message {
|
||||
public Object message();
|
||||
|
||||
public int length();
|
||||
}
|
||||
|
||||
public static class BinaryMessage implements Message {
|
||||
|
||||
private Byte[] message;
|
||||
|
||||
public BinaryMessage(Byte[] message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object message() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return this.message.length;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TextMessage implements Message {
|
||||
|
||||
private String message; // UTF8
|
||||
|
||||
public TextMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "\u00EF\u00BB\u00BF" + message.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object message() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return this.message.length();
|
||||
}
|
||||
}
|
||||
|
||||
static final int VERSION = 1; // RFC 5424, Mar 2009
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
var prival = String.valueOf(fac().ordinal() * 8 + sev().ordinal());
|
||||
var d = "";
|
||||
if (data() != null) {
|
||||
d = " " + data();
|
||||
}
|
||||
var m = "";
|
||||
if (
|
||||
message() != null &&
|
||||
message().message() != null &&
|
||||
message().length() > 0
|
||||
) {
|
||||
m = " " + message();
|
||||
}
|
||||
var timestamp = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(
|
||||
new Date()
|
||||
);
|
||||
return (
|
||||
"<" +
|
||||
prival +
|
||||
">" +
|
||||
VERSION +
|
||||
" " +
|
||||
timestamp +
|
||||
" " +
|
||||
host().toString() +
|
||||
" " +
|
||||
appName().toString() +
|
||||
" " +
|
||||
procId().toString() +
|
||||
" " +
|
||||
msgId().toString() +
|
||||
d +
|
||||
m
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
package vs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class SyslogServer {
|
||||
|
||||
private static final int SYSLOG_PORT = 514;
|
||||
private static final int DISCOVERY_PORT = 8888;
|
||||
private static final int MAX_MESSAGE_LENGTH = 1024;
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== Syslog Server Management ===");
|
||||
|
||||
// Startet den Syslog-Server
|
||||
new Thread(() -> runSyslogServer()).start();
|
||||
|
||||
// Startet den Discovery-Server
|
||||
new Thread(() -> runDiscoveryServer()).start();
|
||||
}
|
||||
|
||||
private static void runSyslogServer() {
|
||||
try (DatagramSocket socket = new DatagramSocket(SYSLOG_PORT)) {
|
||||
byte[] buffer = new byte[2048];
|
||||
System.out.println("[INFO] Syslog-Dienst aktiv auf Port " + SYSLOG_PORT);
|
||||
|
||||
while (true) {
|
||||
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
|
||||
socket.receive(packet);
|
||||
|
||||
int length = packet.getLength();
|
||||
if (length > MAX_MESSAGE_LENGTH) {
|
||||
System.err.println("[WARN] Nachricht von " + packet.getAddress() + " zu lang. Ignoriert.");
|
||||
continue;
|
||||
}
|
||||
|
||||
String raw = new String(packet.getData(), 0, length, StandardCharsets.UTF_8);
|
||||
String clean = raw.replace("\uFEFF", "").replace("", "");
|
||||
|
||||
// Trennung von Metadaten und Inhalt
|
||||
int splitIndex = clean.lastIndexOf("]") + 1;
|
||||
String metadata = "Unbekannt";
|
||||
String messageContent = clean;
|
||||
|
||||
if (splitIndex > 0 && splitIndex < clean.length()) {
|
||||
metadata = clean.substring(0, splitIndex).trim();
|
||||
messageContent = clean.substring(splitIndex).trim();
|
||||
}
|
||||
|
||||
String separator = "==================================================";
|
||||
System.out.println("\n" + separator);
|
||||
System.out.println("NEUE SYSLOG-NACHRICHT");
|
||||
System.out.println("Quelle: " + packet.getAddress().getHostAddress() + ":" + packet.getPort());
|
||||
System.out.println("Metadaten: " + metadata);
|
||||
System.out.println("Inhalt: " + messageContent);
|
||||
System.out.println(separator);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("[ERROR] Syslog-Server Fehler: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void runDiscoveryServer() {
|
||||
try (DatagramSocket socket = new DatagramSocket(DISCOVERY_PORT)) {
|
||||
byte[] buffer = new byte[256];
|
||||
System.out.println("[INFO] Discovery-Service aktiv auf Port " + DISCOVERY_PORT);
|
||||
|
||||
while (true) {
|
||||
DatagramPacket request = new DatagramPacket(buffer, buffer.length);
|
||||
socket.receive(request);
|
||||
|
||||
DatagramPacket response = new DatagramPacket(
|
||||
new byte[0], 0, request.getAddress(), request.getPort()
|
||||
);
|
||||
socket.send(response);
|
||||
System.out.println("[DISCOVERY] Server-IP an Client mit der IP [" + request.getAddress().getHostAddress() + "] gemeldet.");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("[ERROR] Discovery-Fehler: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
package vs;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class AsciiCharsTest {
|
||||
|
||||
@Test
|
||||
void nullValue() {
|
||||
var ac = new AsciiChars.L004(null);
|
||||
assertEquals(ac.toString(), "-");
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyValue() {
|
||||
var ac = new AsciiChars.L004("");
|
||||
assertEquals(ac.toString(), "-");
|
||||
}
|
||||
|
||||
@Test
|
||||
void longValue() {
|
||||
var ac = new AsciiChars.L004("1234");
|
||||
assertEquals(ac.toString(), "1234");
|
||||
}
|
||||
|
||||
@Test
|
||||
void longerValue() {
|
||||
var thrown = assertThrows(IllegalArgumentException.class, () -> {
|
||||
new AsciiChars.L004("12345");
|
||||
});
|
||||
assertEquals("Stringlänge = 5 > 4", thrown.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void space() {
|
||||
var thrown = assertThrows(IllegalArgumentException.class, () -> {
|
||||
new AsciiChars.L004("1 1");
|
||||
});
|
||||
assertEquals(
|
||||
"Stringinhalt nicht printable US-ASCII ohne Space",
|
||||
thrown.getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void special() {
|
||||
var thrown = assertThrows(IllegalArgumentException.class, () -> {
|
||||
new AsciiChars.L004("ä");
|
||||
});
|
||||
assertEquals(
|
||||
"Stringinhalt nicht printable US-ASCII ohne Space",
|
||||
thrown.getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
package vs;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import vs.StructuredData.*;
|
||||
import vs.SyslogMessage.*;
|
||||
|
||||
class SyslogMessageTest {
|
||||
|
||||
@Test
|
||||
void testToString() {
|
||||
var m1 = new SyslogMessage(
|
||||
//
|
||||
Facility.SECURITY1, //
|
||||
Severity.CRITICAL, //
|
||||
new AsciiChars.L255("mymachine.example.com"), //
|
||||
new AsciiChars.L048("su"), //
|
||||
new AsciiChars.L128(""), //
|
||||
new AsciiChars.L032("ID47"), //
|
||||
new StructuredData() //
|
||||
.add(Element.newTimeQuality(true, true))
|
||||
.add(
|
||||
new Element("exampleSDID@32473")
|
||||
.add(new Param("iut", "3"))
|
||||
.add(new Param("eventSource", "Application"))
|
||||
.add(new Param("eventID", "1011"))
|
||||
)
|
||||
.add(
|
||||
new Element("examplePriority@32473").add(
|
||||
new Param("class", "high")
|
||||
)
|
||||
),
|
||||
new TextMessage("'su root' failed for lonvick on /dev/pts/8")
|
||||
);
|
||||
var s = m1.toString();
|
||||
assertEquals(s.substring(0, 6), "<34>1 ");
|
||||
assertEquals(
|
||||
s.substring(s.length() - 221, s.length()),
|
||||
" mymachine.example.com su - ID47 [timeQuality tzKnown=\"1\" isSynced=\"1\"][exampleSDID@32473 iut=\"3\" eventSource=\"Application\" eventID=\"1011\"][examplePriority@32473 class=\"high\"] 'su root' failed for lonvick on /dev/pts/8"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test2() {
|
||||
var m1 = new SyslogMessage(
|
||||
//
|
||||
Facility.SECURITY1, //
|
||||
Severity.CRITICAL, //
|
||||
new AsciiChars.L255("mymachine.example.com"), //
|
||||
new AsciiChars.L048("su"), //
|
||||
new AsciiChars.L128(""), //
|
||||
new AsciiChars.L032("ID47"), //
|
||||
null, //
|
||||
new TextMessage("'su root' failed for lonvick on /dev/pts/8")
|
||||
);
|
||||
var s = m1.toString();
|
||||
assertEquals(s.substring(0, 6), "<34>1 ");
|
||||
assertEquals(
|
||||
s.substring(s.length() - 78, s.length()),
|
||||
" mymachine.example.com su - ID47 'su root' failed for lonvick on /dev/pts/8"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test3() {
|
||||
var m1 = new SyslogMessage(
|
||||
//
|
||||
Facility.SECURITY1, //
|
||||
Severity.CRITICAL, //
|
||||
new AsciiChars.L255("mymachine.example.com"), //
|
||||
new AsciiChars.L048("su"), //
|
||||
new AsciiChars.L128(""), //
|
||||
new AsciiChars.L032("ID47"), //
|
||||
new StructuredData() //
|
||||
.add(Element.newTimeQuality(true, true))
|
||||
.add(
|
||||
Element.newOrigin(
|
||||
new String[] { "0.0.8.8", "8.8.8.8" },
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
)
|
||||
.add(Element.newMeta(null, 32, "de")),
|
||||
new BinaryMessage(null)
|
||||
);
|
||||
assertEquals(
|
||||
m1.data().toString(),
|
||||
"[timeQuality tzKnown=\"1\" isSynced=\"1\"][origin ip=\"0.0.8.8\" ip=\"8.8.8.8\"][meta sysUpTime=\"32\" language=\"de\"]"
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue