Improve BizTalk endpoint address detection
This commit is contained in:
+6
-2
@@ -249,8 +249,12 @@ Katalog stehen. Dynamische Send Ports, lokale Dateipfade, SMTP-Empfängerlisten
|
||||
und Named Pipes sowie relative HTTP-/WCF-Receive-Listener werden als erwartbar
|
||||
nicht socket-prüfbar behandelt und erzeugen kein `UNKNOWN`. WCF-SQL-Adressen
|
||||
im Format `mssql://server[:port]/instanz/datenbank` werden auf TCP 1433 oder
|
||||
den expliziten Port reduziert. Echte Auflösungslücken bleiben sichtbar und
|
||||
nennen Artefakt, Adapter und Grund, aber niemals die vollständige URI.
|
||||
den expliziten Port reduziert. Schemafreie `host:port`-Angaben werden vor der
|
||||
allgemeinen URI-Auswertung erkannt. Bei FTP und SFTP werden zusätzlich Angaben
|
||||
der Form `[benutzer@]host[:port]/pfad` auf Host und expliziten beziehungsweise
|
||||
adaptertypischen Standardport reduziert. Echte Auflösungslücken bleiben
|
||||
sichtbar und nennen Artefakt, Adapter und Grund, aber niemals die vollständige
|
||||
URI.
|
||||
|
||||
Der lokale Katalog
|
||||
`%ProgramData%\BizTalkCheckmkPulse\data\endpoints.xml` wird beim ersten
|
||||
|
||||
@@ -119,6 +119,9 @@ Der Provider nutzt für die Endpoint-Discovery keine neue WMI-Klasse. Er liest
|
||||
reiner Host/Port-Test: HTTP(S), SFTP, FTP, WCF/net.tcp, WCF-SQL/mssql und UNC
|
||||
werden per TCP geprüft; explizite `udp://`-Ziele per UDP-Datagramm. Es werden
|
||||
keine HTTP-Requests, Anmeldungen oder fachlichen Nachrichten gesendet.
|
||||
Auch schemafreie `host:port`-Angaben sowie FTP-/SFTP-Adressen der Form
|
||||
`[benutzer@]host[:port]/pfad` werden erkannt; Benutzer und Pfad werden dabei
|
||||
nicht in den Endpoint-Katalog übernommen.
|
||||
|
||||
Nicht jede aktive BizTalk-Adresse ist selbst ein Socket-Ziel. Dynamische Send
|
||||
Ports, lokale Pfade, SMTP-Empfänger, Named Pipes und relative HTTP-/WCF-
|
||||
|
||||
@@ -183,7 +183,8 @@ Zweck:
|
||||
- Reduziert die Adresse auf Host/Port und führt keinen fachlichen
|
||||
HTTP-, FTP-, SFTP-, WCF- oder BizTalk-Aufruf aus.
|
||||
- Erkennt unter anderem Standard-URIs, UNC, MSMQ, `net.tcp`, WCF-SQL-
|
||||
`mssql`-URIs und lokale HTTP-/WCF-Wildcard-Listener.
|
||||
`mssql`-URIs, schemafreie `host:port`-Angaben, FTP-/SFTP-Ziele mit optionalen
|
||||
Benutzerinformationen sowie lokale HTTP-/WCF-Wildcard-Listener.
|
||||
- Behandelt Adressen ohne eigenständiges Socket-Ziel (zum Beispiel lokale
|
||||
Pfade, SMTP-Empfänger, Named Pipes und relative Receive-Listener) als
|
||||
erwartbar ausgenommen und nicht als Abdeckungsfehler.
|
||||
|
||||
@@ -65,6 +65,14 @@ geschrieben. Der Katalog liegt im bereits ACL-geschützten `data`-Verzeichnis;
|
||||
nur Provider und Administratoren dürfen ihn ändern. `LocalSystem` liest nur
|
||||
den separaten, validierten Checkmk-Snapshot.
|
||||
|
||||
Die automatische Auflösung akzeptiert neben absoluten URIs auch `host:port`
|
||||
und bei FTP/SFTP die schemafreie Form `[benutzer@]host[:port]/pfad`. Die
|
||||
Reihenfolge ist absichtlich eindeutig: `host:port` wird vor der allgemeinen
|
||||
.NET-URI-Auswertung erkannt, damit der Host nicht fälschlich als URI-Schema
|
||||
gedeutet wird. Benutzerinformationen und Pfad bleiben ausschließlich im
|
||||
flüchtigen WMI-Kandidaten und werden nicht persistiert. Eckige URI-Klammern
|
||||
um IPv6-Adressen werden vor der Socket-Probe entfernt.
|
||||
|
||||
## Manuelle Korrektur eines automatisch nicht auflösbaren Endpoints
|
||||
|
||||
Vor einer manuellen Änderung den Scheduled Task kurz stoppen und die Datei
|
||||
|
||||
@@ -48,7 +48,16 @@ namespace BizTalkCheckmkPulse
|
||||
return Unresolved("Aktive statische Transportadresse ist leer.");
|
||||
}
|
||||
|
||||
if (LooksLikeEmailRecipients(address) || Contains(candidate.AdapterName, "SMTP"))
|
||||
if (Contains(candidate.AdapterName, "SMTP"))
|
||||
{
|
||||
return Expected("SMTP-Empfaenger beziehungsweise Adapteradresse enthaelt nicht den SMTP-Server.");
|
||||
}
|
||||
|
||||
// SFTP-/FTP-Adressen duerfen Benutzerinformationen enthalten (user@host).
|
||||
// Deshalb erst nach der adapterspezifischen Aufloesung als Empfaengerliste einstufen.
|
||||
if (LooksLikeEmailRecipients(address)
|
||||
&& !Contains(candidate.AdapterName, "SFTP")
|
||||
&& !Contains(candidate.AdapterName, "FTP"))
|
||||
{
|
||||
return Expected("SMTP-Empfaenger beziehungsweise Adapteradresse enthaelt nicht den SMTP-Server.");
|
||||
}
|
||||
@@ -144,7 +153,9 @@ namespace BizTalkCheckmkPulse
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LooksLikeEmailRecipients(address))
|
||||
if (LooksLikeEmailRecipients(address)
|
||||
&& !Contains(adapterName, "SFTP")
|
||||
&& !Contains(adapterName, "FTP"))
|
||||
{
|
||||
reason = "SMTP-Empfaengerliste ist kein pruefbarer Netzwerk-Endpunkt.";
|
||||
return false;
|
||||
@@ -171,6 +182,28 @@ namespace BizTalkCheckmkPulse
|
||||
return Validate(host, port, out reason);
|
||||
}
|
||||
|
||||
// Host:Port muss vor Uri.TryCreate ausgewertet werden: .NET interpretiert
|
||||
// beispielsweise "server:2222/path" sonst als URI mit dem Schema "server".
|
||||
var match = HostPortPattern.Match(address);
|
||||
if (match.Success)
|
||||
{
|
||||
host = match.Groups["host"].Value.Trim('[', ']');
|
||||
if (!int.TryParse(match.Groups["port"].Value, out port))
|
||||
{
|
||||
port = 0;
|
||||
}
|
||||
|
||||
return Validate(host, port, out reason);
|
||||
}
|
||||
|
||||
// BizTalk-FTP-/SFTP-Konfigurationen kommen auch ohne Schema vor. Durch
|
||||
// die temporaere URI-Normalisierung werden user@host, IPv6 und Ports
|
||||
// sauber getrennt; Benutzername und Pfad gelangen nicht in den Katalog.
|
||||
if (TryResolveAdapterUri(address, adapterName, out host, out port))
|
||||
{
|
||||
return Validate(host, port, out reason);
|
||||
}
|
||||
|
||||
Uri uri;
|
||||
if (Uri.TryCreate(address, UriKind.Absolute, out uri)
|
||||
&& !string.IsNullOrWhiteSpace(uri.Scheme))
|
||||
@@ -184,7 +217,7 @@ namespace BizTalkCheckmkPulse
|
||||
return false;
|
||||
}
|
||||
|
||||
host = uri.Host;
|
||||
host = NormalizeHost(uri.Host);
|
||||
port = 445;
|
||||
return Validate(host, port, out reason);
|
||||
}
|
||||
@@ -195,7 +228,7 @@ namespace BizTalkCheckmkPulse
|
||||
return false;
|
||||
}
|
||||
|
||||
host = uri.Host;
|
||||
host = NormalizeHost(uri.Host);
|
||||
protocol = scheme == "udp" ? "UDP" : "TCP";
|
||||
port = ResolvePort(uri, scheme);
|
||||
if (port <= 0 && HasExplicitPort(address))
|
||||
@@ -212,18 +245,6 @@ namespace BizTalkCheckmkPulse
|
||||
return Validate(host, port, out reason);
|
||||
}
|
||||
|
||||
var match = HostPortPattern.Match(address);
|
||||
if (match.Success)
|
||||
{
|
||||
host = match.Groups["host"].Value.Trim('[', ']');
|
||||
if (!int.TryParse(match.Groups["port"].Value, out port))
|
||||
{
|
||||
port = 0;
|
||||
}
|
||||
|
||||
return Validate(host, port, out reason);
|
||||
}
|
||||
|
||||
if (Contains(adapterName, "SFTP"))
|
||||
{
|
||||
host = ExtractBareAdapterHost(address);
|
||||
@@ -318,6 +339,33 @@ namespace BizTalkCheckmkPulse
|
||||
return separator < 0 ? value : value.Substring(0, separator);
|
||||
}
|
||||
|
||||
private static bool TryResolveAdapterUri(string address, string adapterName, out string host, out int port)
|
||||
{
|
||||
host = string.Empty;
|
||||
port = 0;
|
||||
var scheme = Contains(adapterName, "SFTP")
|
||||
? "sftp"
|
||||
: Contains(adapterName, "FTP") ? "ftp" : string.Empty;
|
||||
if (scheme.Length == 0 || address.IndexOf("://", StringComparison.Ordinal) >= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Uri uri;
|
||||
var normalized = scheme + "://" + address.TrimStart('/');
|
||||
if (!Uri.TryCreate(normalized, UriKind.Absolute, out uri)
|
||||
|| string.IsNullOrWhiteSpace(uri.Host))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
host = NormalizeHost(uri.Host);
|
||||
port = uri.IsDefaultPort || uri.Port <= 0
|
||||
? scheme == "sftp" ? 22 : 21
|
||||
: uri.Port;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Erkennt Adapter, deren relative Receive-Adresse eine lokale Listenerbindung statt eines Remoteziels beschreibt.
|
||||
/// </summary>
|
||||
@@ -385,10 +433,16 @@ namespace BizTalkCheckmkPulse
|
||||
&& value.IndexOf(fragment, StringComparison.OrdinalIgnoreCase) >= 0;
|
||||
}
|
||||
|
||||
private static string NormalizeHost(string host)
|
||||
{
|
||||
return (host ?? string.Empty).Trim().Trim('[', ']');
|
||||
}
|
||||
|
||||
private static bool Validate(string host, int port, out string reason)
|
||||
{
|
||||
host = (host ?? string.Empty).Trim();
|
||||
if (host.Length == 0 || host.IndexOfAny(new[] { ' ', '\t', '\r', '\n', '*', '%' }) >= 0)
|
||||
host = NormalizeHost(host);
|
||||
if (host.Length == 0
|
||||
|| host.IndexOfAny(new[] { ' ', '\t', '\r', '\n', '*', '%', '/', '\\', '@', ';', '?', '#' }) >= 0)
|
||||
{
|
||||
reason = "Host ist leer oder enthaelt nicht aufgeloeste Platzhalter.";
|
||||
return false;
|
||||
|
||||
@@ -405,6 +405,31 @@ namespace BizTalkCheckmkPulse.Tests
|
||||
Assert(EndpointAddressParser.TryCreate(candidate, out endpoint, out reason), "net.tcp URI should resolve: " + reason);
|
||||
AssertEqual(808, endpoint.Port, "net.tcp default port");
|
||||
|
||||
candidate.Address = "transfer.example.test:2222/outbound";
|
||||
candidate.AdapterName = "Custom Adapter";
|
||||
Assert(EndpointAddressParser.TryCreate(candidate, out endpoint, out reason), "plain host:port should resolve before URI parsing: " + reason);
|
||||
AssertEqual("transfer.example.test", endpoint.Host, "plain host:port host");
|
||||
AssertEqual(2222, endpoint.Port, "plain host:port port");
|
||||
|
||||
candidate.Address = "integration-user@sftp.example.test/inbound";
|
||||
candidate.AdapterName = "SFTP";
|
||||
Assert(EndpointAddressParser.TryCreate(candidate, out endpoint, out reason), "schema-less SFTP URI with user info should resolve: " + reason);
|
||||
AssertEqual("sftp.example.test", endpoint.Host, "schema-less SFTP host");
|
||||
AssertEqual(22, endpoint.Port, "schema-less SFTP default port");
|
||||
Assert(endpoint.Host.IndexOf("integration-user", StringComparison.OrdinalIgnoreCase) < 0, "SFTP user info must not enter catalog host");
|
||||
|
||||
candidate.Address = "ftp-user@ftp.example.test:2121/drop";
|
||||
candidate.AdapterName = "FTP";
|
||||
Assert(EndpointAddressParser.TryCreate(candidate, out endpoint, out reason), "schema-less FTP URI with user info should resolve: " + reason);
|
||||
AssertEqual("ftp.example.test", endpoint.Host, "schema-less FTP host");
|
||||
AssertEqual(2121, endpoint.Port, "schema-less FTP explicit port");
|
||||
|
||||
candidate.Address = "sftp://[2001:db8::10]:2222/inbound";
|
||||
candidate.AdapterName = "SFTP";
|
||||
Assert(EndpointAddressParser.TryCreate(candidate, out endpoint, out reason), "bracketed IPv6 URI should resolve: " + reason);
|
||||
AssertEqual("2001:db8::10", endpoint.Host, "IPv6 catalog host must not retain URI brackets");
|
||||
AssertEqual(2222, endpoint.Port, "IPv6 explicit port");
|
||||
|
||||
candidate.Address = "oracledb://oracle01/ORDERS/Dedicated";
|
||||
candidate.AdapterName = "WCF-OracleDB";
|
||||
Assert(EndpointAddressParser.TryCreate(candidate, out endpoint, out reason), "direct Oracle DB URI should resolve: " + reason);
|
||||
|
||||
Reference in New Issue
Block a user