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.
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 }
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.
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";
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!
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 }
``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.
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!
Michael Schilliarbeitet 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. |