Schlagwort: Netzwerk

PHP-Funktionen in C# (noch nicht) als Programmbibliothek 👍 👎

Meine beispielhaften PHP-Funktionen in C# erfreuen sich gewisser Beliebtheit, sodass mich mittlerweile schon ein paar mal die Frage erreicht hat, ob ich diese nicht als Programmbibliothek zur Verfügung stellen möchte.

Aus technischer Sicht wäre das tatsächlich kein Problem und grundsätzlich sogar mit überschaubarem Aufwand automatisierbar. Ich habe daher auch selbst schon einmal mit dem Gedanken der Veröffentlichung als Paket gespielt, dennoch möchte ich erst einmal Abstand davon nehmen und zwar vor allem aus folgenden Gründen:
  • Design

    Die Umsetzung der Methoden (Bezeichner, Datentypen, Parameter) ist stark an PHP angelehnt, sprich ist soweit möglich identisch zu den gleichnamigen PHP-Funktionen. Diese Umsetzung widerspricht jedoch in nahezu allen Fällen in vielerlei Hinsicht den .NET-Guidelines für Programmbibliotheken und auch die Datentypen entsprechen oftmals nicht den bei C# für den jeweiligen Einsatzzweck üblichen.

  • Qualität

    Die Umsetzungen sollen einen Ansatz bzw. Tipps liefern, entsprechen jedoch in einigen Fällen nicht einer Qualität, die einen sofortigen und ungeprüften Einsatz in Produktionsumgebungen ermöglicht. Dies wäre für eine tatsächliche Veröffentlichung jedoch zwingend erforderlich. Darüber hinaus wären an einigen Stellen Optimierungen für den effizienten Einsatz in echten Projekten durchaus sinnvoll.

Dennoch stimme ich natürlich grundsätzlich zu, dass die Implementierungen einige praktische Funktionen anbieten, die in PHP durchaus üblich sind, in C# standardmäßig jedoch nicht ohne Weiteres zur Verfügung stehen. Daher werde ich an einer entsprechenden Programmbibliothek arbeiten, die ausschließlich solche Funktionalität in stabiler Qualität zur Verfügung stellt. Einen konkreten Zeitplan kann und möchte ich derzeit jedoch noch nicht nennen.

Zertifikat einer Adresse mit C# ermitteln 👍 👎

Manchmal möchte man gerne Zertifikate von Webseiten automatisiert abrufen. Im Rahmen eines Monitorings bietet es sich beispielsweise an, die Gültigkeitsdauer zu überwachen. Dazu eine beispielhafte Implementierung:
Zertifikat einer Webseite ermitteln
010203040506070809
public static X509Certificate2 GetCertificate(string hostName, int port = 443) {    using(TcpClient client = new TcpClient(hostName, port)) {        using(SslStream stream = new SslStream(client.GetStream())) {            stream.AuthenticateAsClient(hostName);
return new X509Certificate2(stream.RemoteCertificate); } }}
Die Implementierung ist dabei – wie üblich – auf das Wesentliche beschränkt; es findet keine umfassende Problembehandlung statt. Sofern die Methode in größerem Kontext zum Einsatz kommen soll, empfiehlt sich auf jeden Fall ein Blick auf die beteiligten Klassen TcpClient, SslStream und X509Certificate2 im MSDN.

Nun stehen uns diverse Informationen zur Verfügung; einige Beispiele zur Ausgabe auf der Konsole:
Methode verwenden und Informationen ausgeben
010203040506
X509Certificate2 certificate = GetCertificate("coders-online.net");
Console.WriteLine(certificate.Subject); // "CN=coders-online.net"
Console.WriteLine(certificate.NotBefore); // 30.04.2017 08:01:00Console.WriteLine(certificate.NotAfter); // 29.07.2017 08:01:00
Im Besonderen sei abschließend noch auf RemoteCertificateValidationCallback hingewiesen, mit welchem sich das Verhalten bei Zertifizierungsfehlern beeinflussen lässt.

HTTP-Statuscode einer Webseite mit C# ermitteln 👍 👎

Hin und wieder kann es – beispielsweise im Rahmen eines Monitorings - erforderlich sein, den HTTP-Statuscode einer Webseite zu ermitteln. Das lässt sich mit C# grundsätzlich bereits mit wenigen Zeilen lösen:
Hilfsmethode
01020304050607
public static HttpStatusCode GetHttpStatusCode(Uri uri) {    HttpWebRequest httpWebRequest = WebRequest.CreateHttp(uri);
using(HttpWebResponse webResponse = (HttpWebResponse) httpWebRequest.GetResponse()) { return webResponse.StatusCode; }}
In der Praxis möchte man vermutlich zumindest noch das Weiterleitungs- und Zwischenspeicherverhalten konfigurieren, daher bietet es sich an, unsere Methode entsprechend zu erweitern:
Hilfsmethode (erweitert)
010203040506070809101112131415
public static HttpStatusCode GetHttpStatusCode(    Uri uri,    bool allowAutoRedirect = false,    RequestCacheLevel requestCacheLevel = RequestCacheLevel.NoCacheNoStore) {    HttpWebRequest httpWebRequest = WebRequest.CreateHttp(uri);    {        httpWebRequest.AllowAutoRedirect = allowAutoRedirect;        httpWebRequest.CachePolicy = new RequestCachePolicy(requestCacheLevel);    }
using(HttpWebResponse webResponse = (HttpWebResponse) httpWebRequest.GetResponse()) { return webResponse.StatusCode; }}
Die Verwendung gestaltet sich nun sehr intuitiv:
Hilfsmethode verwenden
01020304
Uri uri = new Uri("https://ccc.coders-online.net/");
HttpStatusCode statusCode = GetHttpStatusCode(uri); // Found (302)statusCode = GetHttpStatusCode(uri, true); // OK (200)
Wenn ihr den Wert der HttpStatusCode-Aufzählung zu int konvertiert erhaltet ihr im Übrigen den entsprechenden numerischen Wert nach Definition des HTTP-Protokolls.

Zeitserver mit C# abfragen 👍 👎

Im Folgenden möchte ich drei Möglichkeiten vorstellen, wie man mit C# über ein Netzwerk eine Zeitangabe abrufen kann. Es gilt zu beachten, dass es sich um sehr reduzierte Beispiele handelt und beispielsweise keine umfassende Fehlerbehandlung berücksichtigt wird. Wir verwenden standardmäßig jeweils einen Server des NIST.

Time Protocol

Beim Time Protocol handelt es sich um ein sehr einfaches (und etwas betagtes) Protokoll. Der Dienst steht für gewöhnlich unter Port 37 zur Verfügung. Details zum Protokoll können in RFC 868 nachgelesen werden.

Methode zur Zeitabfrage per Time Protocol
010203040506070809101112131415161718
public static DateTime GetTime(string host = "time.nist.gov", int port = 37) {    using(TcpClient client = new TcpClient(host, port)) {        using(NetworkStream networkStream = client.GetStream()) {            // Antwort lesen            byte[] timeBuffer = new byte[4];            networkStream.Read(timeBuffer, 0, timeBuffer.Length);
// Byte-Reihenfolge ggf. umkehren if(BitConverter.IsLittleEndian) { Array.Reverse(timeBuffer); }
return (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)) .AddSeconds(BitConverter.ToUInt32(timeBuffer, 0)) .ToLocalTime(); } }}

Daytime Protocol

Beim Daytime Protocol verhält es sich ähnlich, ist jedoch textbasiert. Der Dienst steht für gewöhnlich unter Port 13 zur Verfügung. Details zum Rückgabeformat können in RFC 867 und der Dienstbeschreibung des NIST nachgelesen werden – wir werden uns auf den minimalen Part (Datum und Uhrzeit) beschränken.

Methode zur Zeitabfrage per Daytime Protocol
010203040506070809101112131415161718
public static DateTime GetDaytime(string host = "time.nist.gov", int port = 13) {    using(TcpClient client = new TcpClient(host, port)) {        using(StreamReader streamReader = new StreamReader(client.GetStream())) {            // Antwort lesen und auftrennen            string[] response = streamReader.ReadToEnd().Split(' ');
// Datum und Uhrzeit ermitteln int[] date = Array.ConvertAll(response[1].Split('-'), Int32.Parse); int[] time = Array.ConvertAll(response[2].Split(':'), Int32.Parse);
return new DateTime( CultureInfo.CurrentCulture.Calendar.ToFourDigitYear(date[0]), date[1], date[2], time[0], time[1], time[2], DateTimeKind.Utc ).ToLocalTime(); } }}

Network Time Protocol (NTP)

Beim Network Time Protocol handelt es sich um einen weit verbreiteten Standard zur Zeitsynchronisierung in Computernetzwerken über Port 123. Details zum Protokoll können in RFC 5905 nachgelesen werden – wir werden uns wieder auf einen minimalen Teil beschränken. Als Server bietet sich hier natürlich auch die PTB an.

Methode zur Zeitabfrage per NTP
010203040506070809101112131415161718192021222324252627282930313233343536
public static DateTime GetNetworkTime(string host = "time.nist.gov", int port = 123) {    using(UdpClient client = new UdpClient()) {        client.Connect(host, port);
// Anfrage senden const byte header = ( (0 << 6) // LI = 0 (keine Warnung; zur Verdeutlichung ausformuliert) | (4 << 3) // VN = 4 (Version) | (3 << 0) // Mode = 3 (Client) ); // 00_100_011
byte[] request = new byte[48];
request[0] = header;
client.Send(request, request.Length);
// Antwort lesen IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 0); byte[] response = client.Receive(ref endPoint);
// Daten ab Offset aus ggf. umgekehrter Byte-Reihenfolge ermitteln const int offset = 40;
if(BitConverter.IsLittleEndian) { Array.Reverse(response, offset, 4); Array.Reverse(response, (offset + 4), 4); }
ulong integerPart = BitConverter.ToUInt32(response, offset); ulong fractionalPart = BitConverter.ToUInt32(response, (offset + 4));
return (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)) .AddMilliseconds((integerPart * 1000) + ((fractionalPart * 1000) / UInt32.MaxValue)) .ToLocalTime(); }}
Insbesondere bei diesem letzten Beispiel möchte ich noch einmal explizit darauf hinweisen, dass es sich um eine sehr reduzierte Implementierung handelt. Die Rückgabe erfolgt bei allen Beispielen jeweils bereits in lokaler Zeit.

Kontakt für Domain-Missbrauch mit C# von abuse.net ermitteln 👍 👎

In meinem vorherigen Beitrag habe ich bereits ein paar allgemeine Gedanken und Lösungsansätze zum – leidigen – Thema Spam niedergeschrieben. In diesem Beitrag soll es, wie bereits angekündigt, darum gehen, den bereits kurz beschriebenen Dienst von abuse.net automatisiert (mit C#) anzusprechen.

Der Anbieter stellt alle wesentlichen Informationen zur Umsetzung zur Verfügung. Dort wird der Zugriff per DNS oder Whois beschrieben. Das Herunterladen der (Roh-)Daten ist jedoch nicht möglich. Beachtet bitte außerdem die Nutzungsbedingungen, so ist der kommerzielle Einsatz beispielsweise nicht ohne Weiteres gestattet.

Es werden bei den folgenden beispielhaften Implementierungen lediglich die ermittelten eMail-Adressen an sich zurückgeliefert und keine – ebenfalls verfügbaren – Kommentare, welche insbesondere Angaben zur Quelle enthalten. Des Weiteren findet keine Reduzierung von Duplikaten statt. Beides kann bei Bedarf aber selbstverständlich noch angepasst bzw. ergänzt werden. Die Methoden sind darüber hinaus variadisch und können daher mit mehreren Argumenten (oder einem Feld) aufgerufen werden.

Zugriff per Whois

Eine – auch mit .NET-Bordmitteln – sehr einfache Möglichkeit der Abfrage des für Missbrauch zuständigen Kontakts zu einer Domain ist das Whois-Protokoll. Die Abfrage erfolgt per TCP bei whois.abuse.net:43.

Zugriff per Whois
01020304050607080910111213141516171819202122
public static IEnumerable<string> GetAbuseContactList(params string[] domainList) {    using(TcpClient client = new TcpClient("whois.abuse.net", 43)) {        using(NetworkStream stream = client.GetStream()) {            byte[] request = Encoding.UTF8.GetBytes(String.Join(" ", domainList) + "\r\n");            stream.Write(request, 0, request.Length);
using(StreamReader streamReader = new StreamReader(stream)) { while(!streamReader.EndOfStream) { string response = streamReader.ReadLine();
if(response.Contains("(")) { response = response.Substring(0, response.IndexOf('(')); }
if(!String.IsNullOrWhiteSpace(response)) { yield return response.Trim(); } } } } }}

Es sei jedoch darauf hingewiesen, dass diese Methode vom Anbieter nicht (mehr) empfohlen und möglicherweise eingestellt wird. Darüber hinaus ist das Abfragen im Vergleich zu DNS um den Faktor 2-3 langsamer.

Zugriff per DNS

Eine sehr effiziente Möglichkeit der Abfrage stellt das DNS durch Auflösung des Bezeichners {domain}.contacts.abuse.net dar; die Rückgabe der Adressen erfolgt in Form von TXT-Records.

Leider stellt das .NET-Framework jedoch nur einfache Aufgaben der Adress- und Namensauflösung per DNS-Klasse zur Verfügung. Da die Details der Kommunikation – zumindest in diesem Beitrag – nicht weiter vertieft werden sollen, greifen wir auf eine externe Laufzeitbibliothek zu, die wir mit NuGet bequem per Visual Studio beziehen können. Für das folgende Beispiel verwende ich ARSoft.Tools.Net, welche sich bereits in einigen meiner Entwicklungen im Produktiveinsatz bewährt hat und zudem auch relativ einfach zu verwenden ist.

Zugriff per DNS
010203040506070809101112
public static IEnumerable<string> GetAbuseContactList(params string[] domainList) {    foreach(string domain in domainList) {        DnsMessage response = DnsClient.Default.Resolve(            DomainName.Parse(domain + ".contacts.abuse.net"),            RecordType.Txt        );
foreach(TxtRecord record in response.AnswerRecords) { yield return record.TextData; } }}
Unabhängig davon, welche der beiden Methoden ihr verwendet, gestaltet sich die Verwendung sehr einfach:
Verwendung der Methode(n)
0102030405060708
  // ["abuse@coders-online.net"]IEnumerable<string> abuseContactList = GetAbuseContactList("coders-online.net");
// ["abuse@google.com", "abuse@microsoft.de"]abuseContactList = GetAbuseContactList("google.com", "microsoft.de");
// ["abuse@facebook.com", "spamreport@spamreport.facebook.com"]abuseContactList = GetAbuseContactList("facebook.com");
Auf eine angemessene Fehlerbehandlung habe ich wie üblich zur besseren Verdeutlichung des eigentlichen Anliegens verzichtet, sollte von euch bei der Verwendung aber nicht vernachlässigt werden.

Projektverweise

Kategorien / Archiv  |  Übersicht RSS-Feed

Schlagworte

Suche