... verzweifelt gesucht! (Linux-Magazin, Januar 1999)

Daß das Internet intern mit Zahlen und nicht mit Namen funktioniert, wird jedem klar, den einmal

    telnet www.siemens.de

aufgerufen hat und daraufhin

    Trying 139.23.37.94...

sah. Die Umwandlung des Rechnernamens www.siemens.de in die IP-Adresse 139.23.37.94 ist keine triviale Angelegenheit. Der Domain Name Service, kurz DNS genannt, hat mit Hilfe einer verteilten Datenbank die Information eingeholt. Das geht auch in Perl: Die gethostbyname-Funktion kontaktiert einen der in der Datei /etc/resolv.conf festgelegten DNS-Server und läßt diesen den Namen nachschlagen. Dieser muß unter Umständen bei seinen Kollegen nachfragen, und irgendwann kommt das Ergebnis zurück.

Das Skript aus Listing lookup.pl zeigt, wie's geht: gethostbyname liefert im Listen-Kontext einen Wust von Elementen zurück, von denen die letzten die interessantesten sind: Das Array @addrs führt nach dem erfolgreichen Aufruf eine Reihe von Skalaren, die auch noch in wildem Binär-Format vorliegen, sodaß erst Perls unpack-Funktion die vier Bytes jeder IP-Adresse in ansprechender Form zurückliefert. So sind das Ergebnis von

    lookup.pl www.altavista.com

beispielsweise die sechs IP-Adressen

    204.74.103.37
    204.123.2.69
    204.123.2.97
    204.123.2.75
    204.123.2.107
    204.123.2.66

die alle den Zugriff auf die Altavista-Web-Server erlauben. Die Ausgabe kann übrigens bei mehreren aufeinanderfolgenden Aufrufen unterschiedlich ausfallen, wenn zur Lastverteilung ein sogenanntes DNS-Round-Robin-System im Spiel ist.

Listing lookup.pl

    01 #!/usr/bin/perl -w
    02 ##################################################
    03 # Michael Schilli, 1998 (mschilli@perlmeister.com)
    04 ##################################################
    05 
    06 $host = $ARGV[0] || die "usage: $0 hostname";
    07 
    08 my ($name, $aliases, $addrtype, $length, @addrs) = 
    09     gethostbyname($host);
    10 
    11 foreach $i (@addrs) {
    12     my ($a, $b, $c, $d) = unpack('C4', $i);
    13     print "$a.$b.$c.$d\n";
    14 }

Reverse Lookup

Manchmal kommt es vor (zum Beispiel bei der Analyse einer Webserver-Logdatei), daß zwar die IP-Adresse eines Servers vorliegt, doch lieber wäre einem der Name! Nachdem im Internet für ordnungsgemäß registrierte Server auch reverse-DNS-Einträge existieren, ist das kein Problem: Den Namen eines Rechners, dessen IP-Adresse gegeben ist, ermittelt das Skript revlookup.pl, das die IP-Adresse als Kommandozeilenparameter entgegennimmt und den Hostnamen -- falls das DNS-System ihn findet -- auf der Standardausgabe ausgibt.

    revlookup.pl 204.74.103.37

zaubert so flugs

    altavista.digital.com

daher. Die Perl-Funktion gethostbyaddr entstammt wie gethostbyname dem Unix-Betriebssystem. Sie erwartet die umzuwandelnde IP-Adresse in einer vogelwilden C-Struktur, die die Funktion inet_aton aus dem Socket-Modul schnell zusammenschustert. Als zweiten Parameter liefert das AF_INET-Macro (eine exportierte Funktion aus Socket) den nötigen Zahlenwert, um gethostbyaddr zur Rückgabe des Hostnamens zu veranlassen. Der Wert für AF_UNIX stammt, wie ich gerade aus meinem stark vergilbten ``Stevens: Unix Network Programming'', Auflage 1990 -- da war'n wir noch jung! -- entnehme, aus der Unix-Steinzeit und spezifiziert die ``Adreß-Familie'', die der angesprochene Socket verwendet, aber außer dem ``Internet-Protokoll'' ist heutzutage kaum mehr etwas anderes in Betrieb.

Listing revlookup.pl

    01 #!/usr/bin/perl -w
    02 ##################################################
    03 # Michael Schilli, 1998 (mschilli@perlmeister.com)
    04 ##################################################
    05 
    06 use Socket;
    07 
    08 $ip_string = $ARGV[0] || die "usage: $0 ipaddr";
    09 
    10 $ip = inet_aton($ip_string);
    11 
    12 $host = (gethostbyaddr($ip, AF_INET))[0];
    13 
    14 $host ||= "Unknown";
    15 
    16 print $host, "\n";

MX-Records

Email gehorcht anderen Gesetzen: An gschaftler@siemens.de verschickte Post kontaktiert nicht siemens.de -- diesen Server gibt's gar nicht. Statt dessen kommt der sogenannte MX-Record des DNS-Eintrags zum Einsatz, der die Daten einer Reihe von Mail-Vermittlungsrechnern enthält. Listing mx.pl zeigt, wie man diese Quelle anzapft, die Ausgabe von

    mx.pl siemens.de

lautet:

    50 david.siemens.de
    70 thoth.mch.sni.de
    100 ns.sbs.de

Die erste Spalte der Ausgaben zeigt die Priorität des jeweiligen Rechners an, beginnend beim Rechner mit dem kleinsten Wert, versucht der Sendmail-Dämon, die Post an den Mann zu bringen. Schlägt eine Verbindung fehl, versucht er sein Glück beim Rechner mit dem nächsthöheren Wert. Im Fall Siemens kontaktiert der Sender zunächst Port 25 auf david.siemens.de, klappt das nicht, kommt thoth.mch.sni.de dran, scheitert auch das, ist die ns.sbs.de die letzte Möglichkeit, die Email weiterzusenden.

Listing mx.pl zeigt, wie man den MX-Record eines Eintrags in der DNS-Datenbank anzapft. Das frei auf dem CPAN erhältliche Modul Net::DNS von Michael Fuhr vereinfacht den Zugriff, indem es statt schnöder Byte-Fummelei ein attraktives objektorientiertes Interface anbietet. Es arbeitet dabei mit Resolver-Objekten vom Typ Net::DNS::Resolver, die Anfragen an DNS-Server stellen und Antworten liefern. In mx.pl löst der Aufruf der mx-Funktion, die ein Resolver-Objekt und den Domain-Namen als Parameter erwartet, eine DNS-Anfrage aus. mx liefert eine Liste von Objekten vom Typ Net::DNS::RR zurück, deren Inhalte sich mit den Methoden preference (Prioritäts-Wert) und exchange (Mail-Vermittlungsrechner) hervorzaubern lassen. Bei auftretenden Fehlern ist die zurückgegebene Liste @mx leer, in diesem Fall liefert @mx in skalarem Kontext den Wert 0. Die errorstring-Methode des Resolver-Objektes gibt die dazugehörige Fehlermeldung an.

Die auf dem CPAN (http://www.perl.com/CPAN) vorrätige Distribution des Moduls Net::DNS

    authors/id/MFUHR/Net-DNS-0.12.tar.gz

wird entweder mit CPAN.pm geschnappt und automatisch installiert, oder aber manuell vom Netz gezogen, flugs entpackt und installiert:

    tar zxfv Net-DNS-0.12.tar.gz
    cd Net-DNS-0.12
    perl Makefile.PL
    make
    make install

... und schon kann's losgehen!

Listing mx.pl

    01 #!/usr/bin/perl -w
    02 ##################################################
    03 # Michael Schilli, 1998 (mschilli@perlmeister.com)
    04 ##################################################
    05 
    06 use Net::DNS;
    07 
    08 $domain = $ARGV[0] || die "usage: $0 domain";
    09 
    10 $resolver = new Net::DNS::Resolver;
    11 
    12     # MX-Record abfragen 
    13 @mx = mx($resolver, $domain);
    14 
    15 if (@mx) {
    16         # Erfolg! Über alle Resource Records iterieren
    17     foreach $rr (@mx) {
    18         print $rr->preference, " ", 
    19               $rr->exchange, "\n";
    20     }
    21 } else {
    22         # Leere Liste, Fehler!
    23     print "Kein MX-Record für $domain: ", 
    24           $resolver->errorstring, "\n";
    25 }

Whois

``Welcher verdammte Sack hat die Domain registriert, die ich schon immer haben wollte?''. Dieser Frage geht aus Perl heraus normalerweise das Net::Whois-Modul von Chip Salzenberg nach. Doch zu allem Unglück enthielt dieses zum Zeitpunkt der Erstellung dieses Artikels einen Fehler -- also machen wir's zu Fuß, wie Listing whois.pl zeigt.

Unter www.internic.net bietet die Firma InterNIC, die die Registratur von Domain-Namen durchführt, einen Service namens whois an, mit dem sich leicht ermitteln läßt, ob eine bestimmte Domain schon registriert ist und falls ja, wer dahintersteckt. Sogar die Telefonnummern und Postanschriften des Inhabers und seines Internet-Service-Providers stehen dort.

Zeile 14 ruft den DNS-Service an, um www.internic.net in eine IP-Adresse umzuwandeln, gethostbyname liefert in skalarem Kontext eine 4-Byte-Adresse zurück, die inet_ntoa in den typischen IP-String vom Format a.b.c.d umwandelt.

Zeile 19 schließlich erzeugt ein neues Objekt vom Typ IO::Socket::INET aus dem Modul IO::Socket und nimmt gleich Verbindung auf mit der angegebenen IP-Adresse unter dem Port, der dem Service whois entspricht (43) -- und zwar nach dem TCP-Protokoll.

Zeile 26 schaltet den zwischengeschalteten Puffer aus, sodaß Ausgaben auf den Socket auch gleich rausgeblasen werden. Zeile 29 schickt das Kommando

    dom domain-name

das der whois-Service erwartet, um in der Datenbank nachzuschlagen und das Ergebnis als Text zurückzuliefern. Zeile 32 liest alle Zeilen auf einen Rutsch aus.

Listing whois.pl

    01 #!/usr/bin/perl -w
    02 ##################################################
    03 # Internic-Record einholen:
    04 # whois.pl domain-name
    05 # 1998, michael@perlmeister.com
    06 ##################################################
    07 
    08 use IO::Socket;
    09 
    10 $domain = $ARGV[0] || die "usage: $0 domain";
    11 
    12 $INTERNIC = "www.internic.net";
    13 
    14 $addr = gethostbyname($INTERNIC);
    15 die "Cannot resolve $INTERNIC" unless $addr;
    16 $ip = inet_ntoa($addr);
    17 
    18     # Verbindung aufnehmen
    19 $sock = IO::Socket::INET->new(PeerAddr => $ip,
    20                               PeerPort => 'whois',
    21                               Proto    => 'tcp');
    22 
    23 $sock || die "Cannot connect to $INTERNIC: $@";
    24 
    25     # Buffer automatisch flushen
    26 $sock->autoflush;
    27 
    28     # Anfrage über Socket schicken
    29 print $sock "dom $domain\r\n";
    30 
    31     # Daten lesen und ausgeben
    32 print join('', <$sock>);

Die Ausgabe bringt es an den Tag, da ist doch glatt einer vor mir auf die Idee gekommen, diese Domain zu registrieren: Der Aufruf

    whois.pl schilli.com

zeigt

    
    Registrant:
    Schilli Transportation Services, Inc. (SCHILLI-DOM)
       4 N. Ohio St. / P.O. Box 351
       Remington, IN 47977
    
       Administrative Contact:
          Wilkinson, Louis  (LW665)  schilli@FFNI.COM
          219-261-2101
       ...
       Record last updated on 08-Jul-97.
       Record created on 26-Aug-96.
       Database last updated on 20-Nov-98 05:06:59 EST.
     
       ...

an. Man kann nicht alles haben. Macht's as guat, bis zur next'n, Leitl'n!

Literatur

[1]
DNS & Bind, O'Reilly & Associates, 3. Auflage 1998, ISBN 1565925122

Michael Schilli

arbeitet als Software-Engineer bei Yahoo! in Sunnyvale, Kalifornien. Er hat "Goto Perl 5" (deutsch) und "Perl Power" (englisch) für Addison-Wesley geschrieben und ist unter mschilli@perlmeister.com zu erreichen. Seine Homepage: http://perlmeister.com.