![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
||||||||
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
|||||||
![]() |
|||||||
![]() |
|
||||||
![]() |
Daß das Internet intern mit Zahlen und nicht mit Namen funktioniert, wird jedem klar, der 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 mit Perl. Die Funktion gethostbyname 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 1 (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, so daß erst Perl's 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 1: lookup.pl |
1 #!/usr/bin/perl -w 2 3 $host = $ARGV[0] || die "usage: $0 hostname"; 4 5 my ($name, $aliases, $addrtype, $length, @addrs) = 6 gethostbyname($host); 7 8 foreach $i (@addrs) { 9 my ($a, $b, $c, $d) = unpack('C4', $i); 10 print "$a.$b.$c.$d\n"; 11 } |
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 in Listing 2, 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 2: revlookup.pl |
1 #!/usr/bin/perl -w 2 3 use Socket; 4 5 $ip_string = $ARGV[0] || die "usage: $0 ipaddr"; 6 7 $ip = inet_aton($ip_string); 8 9 $host = (gethostbyaddr($ip, AF_INET))[0]; 10 11 $host ||= "Unknown"; 12 13 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-Eintrages zum Einsatz, der die Daten einer Reihe von Mail-Vermittlungsrechnern enthält. Listing 3 (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 oder die Frau 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 an die Reihe, scheitert auch das, ist ns.sbs.de die letzte Möglichkeit, die EMail weiterzusenden.
Listing 3 (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 3: mx.pl |
1 #!/usr/bin/perl -w 2 3 use Net::DNS; 4 5 $domain = $ARGV[0] || die "usage: $0 domain"; 6 7 $resolver = new Net::DNS::Resolver; 8 9 # MX-Record abfragen 10 @mx = mx($resolver, $domain); 11 12 if (@mx) { 13 # Erfolg! Über alle Resource Records iterieren 14 foreach $rr (@mx) { 15 print $rr->preference, " ", 16 $rr->exchange, "\n"; 17 } 18 } else { 19 # Leere Liste, Fehler! 20 print "Kein MX-Record für $domain: ", 21 $resolver->errorstring, "\n"; 22 } |
"Welcher verdammte Sack hat die Domain registriert, die ich schon immer haben wollte?" (da spricht der Bayer! :-)). Dieser Frage geht 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, so daß Ausgaben auf den Socket auch gleich rausgeblasen werden. Zeile 29 verschickt schließlich 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 4: whois.pl |
1 #!/usr/bin/perl -w 2 ################################################## 3 # Internic-Record einholen: 4 # whois.pl domain-name 5 # michael@perlmeister.com 6 ################################################## 7 8 use IO::Socket; 9 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!
Infos |
[1] DNS & Bind, O'Reilly & Associates, 3. Auflage 1998, ISBN 1565925122 [2] http://www.perl.com/CPAN |
Der Autor |
Michael Schilli arbeitet als Web-Engineer für AOL/Netscape in Mountain View, Kalifornien. Er ist Autor des 1998 bei Addison-Wesley erschienenen (und 1999 für den englischsprachigen Markt als "Perl Power" herausgekommenen) Buches "GoTo Perl 5" und unter michael@perlmeister.com oder http://perlmeister.com zu erreichen. |
Copyright © 1999 Linux-Magazin Verlag