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
|
und Named Pipes sowie relative HTTP-/WCF-Receive-Listener werden als erwartbar
|
||||||
nicht socket-prüfbar behandelt und erzeugen kein `UNKNOWN`. WCF-SQL-Adressen
|
nicht socket-prüfbar behandelt und erzeugen kein `UNKNOWN`. WCF-SQL-Adressen
|
||||||
im Format `mssql://server[:port]/instanz/datenbank` werden auf TCP 1433 oder
|
im Format `mssql://server[:port]/instanz/datenbank` werden auf TCP 1433 oder
|
||||||
den expliziten Port reduziert. Echte Auflösungslücken bleiben sichtbar und
|
den expliziten Port reduziert. Schemafreie `host:port`-Angaben werden vor der
|
||||||
nennen Artefakt, Adapter und Grund, aber niemals die vollständige URI.
|
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
|
Der lokale Katalog
|
||||||
`%ProgramData%\BizTalkCheckmkPulse\data\endpoints.xml` wird beim ersten
|
`%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
|
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
|
werden per TCP geprüft; explizite `udp://`-Ziele per UDP-Datagramm. Es werden
|
||||||
keine HTTP-Requests, Anmeldungen oder fachlichen Nachrichten gesendet.
|
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
|
Nicht jede aktive BizTalk-Adresse ist selbst ein Socket-Ziel. Dynamische Send
|
||||||
Ports, lokale Pfade, SMTP-Empfänger, Named Pipes und relative HTTP-/WCF-
|
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
|
- Reduziert die Adresse auf Host/Port und führt keinen fachlichen
|
||||||
HTTP-, FTP-, SFTP-, WCF- oder BizTalk-Aufruf aus.
|
HTTP-, FTP-, SFTP-, WCF- oder BizTalk-Aufruf aus.
|
||||||
- Erkennt unter anderem Standard-URIs, UNC, MSMQ, `net.tcp`, WCF-SQL-
|
- 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
|
- Behandelt Adressen ohne eigenständiges Socket-Ziel (zum Beispiel lokale
|
||||||
Pfade, SMTP-Empfänger, Named Pipes und relative Receive-Listener) als
|
Pfade, SMTP-Empfänger, Named Pipes und relative Receive-Listener) als
|
||||||
erwartbar ausgenommen und nicht als Abdeckungsfehler.
|
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
|
nur Provider und Administratoren dürfen ihn ändern. `LocalSystem` liest nur
|
||||||
den separaten, validierten Checkmk-Snapshot.
|
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
|
## Manuelle Korrektur eines automatisch nicht auflösbaren Endpoints
|
||||||
|
|
||||||
Vor einer manuellen Änderung den Scheduled Task kurz stoppen und die Datei
|
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.");
|
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.");
|
return Expected("SMTP-Empfaenger beziehungsweise Adapteradresse enthaelt nicht den SMTP-Server.");
|
||||||
}
|
}
|
||||||
@@ -144,7 +153,9 @@ namespace BizTalkCheckmkPulse
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LooksLikeEmailRecipients(address))
|
if (LooksLikeEmailRecipients(address)
|
||||||
|
&& !Contains(adapterName, "SFTP")
|
||||||
|
&& !Contains(adapterName, "FTP"))
|
||||||
{
|
{
|
||||||
reason = "SMTP-Empfaengerliste ist kein pruefbarer Netzwerk-Endpunkt.";
|
reason = "SMTP-Empfaengerliste ist kein pruefbarer Netzwerk-Endpunkt.";
|
||||||
return false;
|
return false;
|
||||||
@@ -171,6 +182,28 @@ namespace BizTalkCheckmkPulse
|
|||||||
return Validate(host, port, out reason);
|
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;
|
Uri uri;
|
||||||
if (Uri.TryCreate(address, UriKind.Absolute, out uri)
|
if (Uri.TryCreate(address, UriKind.Absolute, out uri)
|
||||||
&& !string.IsNullOrWhiteSpace(uri.Scheme))
|
&& !string.IsNullOrWhiteSpace(uri.Scheme))
|
||||||
@@ -184,7 +217,7 @@ namespace BizTalkCheckmkPulse
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
host = uri.Host;
|
host = NormalizeHost(uri.Host);
|
||||||
port = 445;
|
port = 445;
|
||||||
return Validate(host, port, out reason);
|
return Validate(host, port, out reason);
|
||||||
}
|
}
|
||||||
@@ -195,7 +228,7 @@ namespace BizTalkCheckmkPulse
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
host = uri.Host;
|
host = NormalizeHost(uri.Host);
|
||||||
protocol = scheme == "udp" ? "UDP" : "TCP";
|
protocol = scheme == "udp" ? "UDP" : "TCP";
|
||||||
port = ResolvePort(uri, scheme);
|
port = ResolvePort(uri, scheme);
|
||||||
if (port <= 0 && HasExplicitPort(address))
|
if (port <= 0 && HasExplicitPort(address))
|
||||||
@@ -212,18 +245,6 @@ namespace BizTalkCheckmkPulse
|
|||||||
return Validate(host, port, out reason);
|
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"))
|
if (Contains(adapterName, "SFTP"))
|
||||||
{
|
{
|
||||||
host = ExtractBareAdapterHost(address);
|
host = ExtractBareAdapterHost(address);
|
||||||
@@ -318,6 +339,33 @@ namespace BizTalkCheckmkPulse
|
|||||||
return separator < 0 ? value : value.Substring(0, separator);
|
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>
|
/// <summary>
|
||||||
/// Erkennt Adapter, deren relative Receive-Adresse eine lokale Listenerbindung statt eines Remoteziels beschreibt.
|
/// Erkennt Adapter, deren relative Receive-Adresse eine lokale Listenerbindung statt eines Remoteziels beschreibt.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -385,10 +433,16 @@ namespace BizTalkCheckmkPulse
|
|||||||
&& value.IndexOf(fragment, StringComparison.OrdinalIgnoreCase) >= 0;
|
&& 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)
|
private static bool Validate(string host, int port, out string reason)
|
||||||
{
|
{
|
||||||
host = (host ?? string.Empty).Trim();
|
host = NormalizeHost(host);
|
||||||
if (host.Length == 0 || host.IndexOfAny(new[] { ' ', '\t', '\r', '\n', '*', '%' }) >= 0)
|
if (host.Length == 0
|
||||||
|
|| host.IndexOfAny(new[] { ' ', '\t', '\r', '\n', '*', '%', '/', '\\', '@', ';', '?', '#' }) >= 0)
|
||||||
{
|
{
|
||||||
reason = "Host ist leer oder enthaelt nicht aufgeloeste Platzhalter.";
|
reason = "Host ist leer oder enthaelt nicht aufgeloeste Platzhalter.";
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -405,6 +405,31 @@ namespace BizTalkCheckmkPulse.Tests
|
|||||||
Assert(EndpointAddressParser.TryCreate(candidate, out endpoint, out reason), "net.tcp URI should resolve: " + reason);
|
Assert(EndpointAddressParser.TryCreate(candidate, out endpoint, out reason), "net.tcp URI should resolve: " + reason);
|
||||||
AssertEqual(808, endpoint.Port, "net.tcp default port");
|
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.Address = "oracledb://oracle01/ORDERS/Dedicated";
|
||||||
candidate.AdapterName = "WCF-OracleDB";
|
candidate.AdapterName = "WCF-OracleDB";
|
||||||
Assert(EndpointAddressParser.TryCreate(candidate, out endpoint, out reason), "direct Oracle DB URI should resolve: " + reason);
|
Assert(EndpointAddressParser.TryCreate(candidate, out endpoint, out reason), "direct Oracle DB URI should resolve: " + reason);
|
||||||
|
|||||||
Reference in New Issue
Block a user