11.10 Namen und IP-Adressen umwandeln
Zwar wissen Sie jetzt, dass im Internet alles über IP-Nummern erreichbar ist, aber wenn Sie sich mit diesen Nummern durchs WWW hangeln müssten, wäre das Internet wohl heute nicht so erfolgreich. Deshalb wird an die Nummern noch zusätzlich ein Name, bestehend aus Host- und Domainname (und eventuell einer Top-Level-Domain (TLD)), vergeben (hostname.domainname.tld; z. B. www.pronix.de).
Der Domainname ist der Name, womit Sie eine Webseite (z. B. pronix.de) im Internet ansprechen. Ein Domainname wird von hinten nach vorne aufgelöst. Somit steht das de für die Top-Level-Domain (wie der Name sagt, das, was ganz vorne steht). Die Top-Level-Domain bezeichnet die Herkunft und/oder die Zugehörigkeit (.com; .gov; .net etc.), was hier mit de für Deutschland steht. Nach der Top-Level-Domain folgt der eigentliche Domainname, der im Beispiel pronix lautet.
Der Hostname ist somit die niedrigste Instanz eines Domainnamens, da dieser ja von hinten nach vorne aufgelöst wird (aber der erste Teil in der Leseweise). Gewöhnlich lautet im Internet der Hostname www (www.pronix.de) – allerdings spricht nichts dagegen, diesen auch anders zu benennen. Es wird nämlich nur empfohlen, weil sich viele Anwender an das www gewöhnt haben. Genauso gut können Sie auch abc.pronix.de verwenden.
Die Vorteile, Namen anstatt Nummern zu verwenden, sind eindeutig:
|
Einfacher les- und verwendbar. |
|
Die Nummern können verändert werden, und der Name kann trotzdem gleich bleiben, wie dies z. B. im Fall der Umstellung von IPv4 auf IPv6 ohne weiteres möglich wäre/ist. Es müssen lediglich dem Namen entsprechende Nummern zugeteilt werden. |
|
Mehrere Nummern können auch zu einem Ziel führen. Geben Sie z. B. www.pronix.de an und die Nummer lautet 193.129.2.1 – aber die Seite ist überlaufen -, so ist es ohne Problem möglich, auf eine andere IP-Nummer zu wechseln, die zur selben Adresse führt. Also mehrere Nummern können für den gleichen Host vergeben werden (wie dies z. B. bei Webhostern der Fall ist). |
11.10.1 Name-Server
Einfach ausgedrückt ist ein Name-Server ein Rechner, der für die Umsetzung von Rechnernamen zur IP-Nummer verantwortlich ist. Im Internet z. B. ist dies vergleichbar mit einem Telefonbuch, wo der Name des Teilnehmers in die Telefonnummer aufgelöst wird. Der Dienst, der Ihnen diese Arbeit im Internet abnimmt, wird als Domain Name System (DNS) bezeichnet. Bei kleineren Netzwerken wie z. B. dem Intranet werden die lokalen IP-Nummern meistens in Form einer Tabelle in einer Datei (/etc/hosts) hinterlegt. Größere Netzwerke, wie das Internet eines ist, halten diese Daten meistens in eigenen Datenbanken. Der Name des lokalen Name-Servers finden Sie in der Datei /etc/resolv.conf. Kennt dieser Name-Server keinen Namen und die dazugehörende IP-Adresse, fragt dieser im Internet bei anderen Name-Servern nach.
11.10.2 Informationen zum Rechner im Netz – gethostbyname und gethostbyaddr
Wenn der Client den Dienst eines Servers verwenden will, muss jenem natürlich dessen IP-Adresse bekannt sein. Meistens gibt ein Endanwender aber als Adresse den Rechnernamen anstatt der IP-Adresse an, da dieser einfacher zu merken ist. Damit also ein Client aus dem Rechnernamen (z. B. www.google.de) eine IP-Adresse (216.239.59.99) erhält, wird die Funktion gethostbyname() verwendet.
#include <netdb.h>
struct hostent *gethostbyname( const char *rechnername );
Die Funktion gethostbyname() gibt bei Erfolg einen Zeiger auf struct hostent des gefundenen Rechners zurück, ansonsten bei einem Fehler NULL.
Um aus einem Rechnernamen eine IP-Adresse und weitere Informationen zu ermitteln, steht (wie bereits erwähnt) der Name-Server zur Verfügung – dieser Rechner ist für die Umsetzung zwischen Rechnernamen und IP-Nummern zuständig. Selbst auf Ihrem Rechner finden Sie, wie schon erwähnt, solche Einträge der lokalen IP-Nummern in der Datei /etc/hosts hinterlegt. Im Internet hingegen werden diese Daten in einer eigenen Datenbank gehalten. Um solche Informationen zu den einzelnen Rechnern zu erhalten, ist in der Headerdatei <netdb.h> folgende Struktur definiert:
struct hostent {
char * h_name;
char ** h_aliases;
short h_addrtype;
short h_length;
char ** h_addr_list;
};
Kurze Beschreibung der einzelnen Strukturvariablen:
Tabelle 11.3
Die Strukturvariablen in struct hostent
Strukturvariable
|
Bedeutung
|
h_name
|
Offizieller Name des Rechners im Netz
|
h_aliases
|
Ein Stringarray, worin sich eventuelle Aliasnamen befinden. Das letzte Element ist immer NULL.
|
h_addrtyp
|
Hier steht der Adresstyp, was gewöhnlich AF_INET für IPv4 oder AF_INET6 für IPv6 ist.
|
h_length
|
Hier befindet sich die Länge der nummerischen Adresse (4 oder 16) in Bytes.
|
h_addr_list
|
Hierbei handelt es sich um ein Array von Zeigern auf die Adressen von IPv4-/IPv6-Nummern in der Netzwerk-Byte-Reihenfolge (network byte order).
|
Benötigen Sie hingegen von einer IP-Adresse Informationen zu einem Rechner, können Sie die Funktion gethostbyaddr() verwenden. Diese Funktion gibt bei Erfolg ebenfalls einen Zeiger auf die Struktur hostent des gefundenen Rechners zurück oder bei einem Fehler NULL.
Hinweis Dies wird allerdings nur funktionieren, wenn es sich um einen FQDN (Fully Qualified Domain Name) handelt, da sonst die Name-Server keinen Namen aus der IP auflösen können. FQDN ist ein eindeutiger Name eines Internet-Hosts. Er setzt sich aus dem Hostnamen und dem Namen der Domain zusammen. Mithilfe des FQDN kann per DNS die IP-Adresse des Hosts ermittelt werden. Z. B.: Mit linux.pronix.de wäre linux der Hostname und pronix.de der Domainname.
|
#include <netdb.h>
struct hostent *gethostbyaddr( const char *ip_addr,
int len, int type );
Mit dem ersten Parameter geben Sie die IP-Adresse des Zielrechners an. Mit dem zweiten wird die Länge dieser Adresse angegeben, und der letzte Parameter gibt den Typ der Adresse an, der auch hier meistens AF_INET für IPv4 bzw. AF_INET6 für IPv6 ist.
Beachten Sie bitte, dass beide Funktionen (gethostbyname() und gethostbyaddr()) nur einen statischen Speicherbereich für den Rückgabewert reservieren. Jeder nachfolgende Funktionsaufruf überschreibt diesen Speicherplatz wieder. GNU bietet auch gethostbyname_r() und gethostbyaddr_r() für Thread-safe-Aktionen an.
Hinweis Die Funktionen gethostbyname() und gethostbyaddr() benutzen für ihre Anfragen den Name-Server named, die Datei /etc/hosts und den Network Information Service (NIS oder YP). Was davon und in welcher Reihenfolge benutzt wird, bestimmt die order-Zeile in der Datei /etc/host.conf Siehe dazu auch resolv. Das Standardverhalten ist, zuerst den Name-Server zu befragen und danach die Datei /etc/hosts zu durchsuchen.
|
Wenn ein Fehler auf der Suche nach einem Rechner mit den Funktionen gethostbyaddr() und gethostbyname() auftritt, sollten Sie die Funktion herror() aufrufen, die ähnlich wie perror() ist, um eine sinnvolle Fehlermeldung zu erhalten, warum ein Fehler aufgetreten ist. Die herror()-Funktion gibt die zum aktuellen Wert von h_errno gehörende Fehlermeldung auf stderr aus.
#include <netdb.h>
extern int h_errno;
void herror ( const char *string );
Die Variable h_errno kann folgende Werte annehmen:
Tabelle 11.4
Mögliche Werte der Fehlervariablen h_errno
Fehlerwert
|
Bedeutung
|
0
|
Kein Fehler aufgetreten.
|
HOST_NOT_FOUND
|
Der angegebene Rechner ist unbekannt.
|
NO_ADDRESS
|
Der angegebene Name ist gültig, aber es existiert dazu keine IP-Adresse.
|
NO_RECOVERY
|
Ein nicht behebbarer Name-Server-Fehler ist aufgetreten.
|
TRY_AGAIN
|
Beim zuständigen Name-Server ist ein vorübergehender Fehler aufgetreten. Versuchen Sie es später nochmals.
|
NO_DATA
|
Name ist keiner Adresse zugeordnet.
|
Falls also auf Ihrem System keine Funktion wie herror() vorhanden ist, können Sie mit den Fehlercodes von h_error ohne Probleme eine eigene Fehlerausgaberoutine nachimplementieren.
Das folgende Beispiel soll demonstrieren, wie Sie Informationen zu einem Rechner durch Angabe eines Rechnernamens oder einer IP-Adresse aus der Struktur hostent herausfinden können.
/* hostent.c */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
int main(int argc, char **argv) {
struct hostent *host;
struct in_addr ip, **ip_ptr;
char **ptr;
if( argc != 2 )
printf("Usage: %s (Rechnername oder IP-Adresse)\n",
*argv);
else {
/* Argument als IP-Adresse ...? */
if( inet_aton( argv[1], &ip ) != 0 )
host=gethostbyaddr( (const void *)&ip,
sizeof(ip),AF_INET );
/* Argument als Rechnername */
else
host = gethostbyname( argv[1] );
if( host == NULL ) {
herror("Konnte Rechner nicht finden");
return EXIT_FAILURE;
}
printf("Hostname : %s\n", host->h_name);
printf("Aliase :\n");
ptr=host->h_aliases;
while( *ptr != NULL ) {
printf("\t%s\n", *ptr++);
}
printf("IP-Adressen :\n");
ip_ptr = (struct in_addr **)host->h_addr_list;
while(*ip_ptr != NULL) {
printf("\t%s\n", inet_ntoa( **ip_ptr++));
}
}
return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o hostent hostent.c
$ ./hostent google.de
Hostname : google.de
Aliase :
IP-Adressen :
216.239.33.100
216.239.37.100
216.239.39.100
$ ./hostent www.pronix.de
Hostname : www.pronix.de
Aliase :
IP-Adressen :
194.150.178.34
$ ./hostent 194.150.178.34
Hostname : goliath.speedpartner.de
Aliase :
IP-Adressen :
194.150.178.34
$ ./hostent www.microsoft.com
Hostname : www.microsoft.com.nsatc.net
Aliase :
www.microsoft.com
IP-Adressen :
207.46.19.30
Hinweis Wie schon einmal erwähnt, wird die Funktion gethostbyname() vorwiegend bei Clientanwendungen verwendet, um aus der Angabe eines Rechnernamens eine IP-Adresse zu machen.
|
11.10.3 Service-Informationen – getservbyname() und getservbyport()
Eine weitere Information, die eine Clientanwendung häufig benötigt, ist die Nummer des Ports, der den gewünschten Service verwendet. Oder wissen Sie auf Anhieb zu allen Portnummern den entsprechenden Service? Natürlich könnten Sie in /etc/services nachschauen und die Nummer dann fest einkompilieren. Wie schon bei den Informationen zu den Rechnern im Netz gibt es auch hierfür eine Struktur in der Headerdatei <netdb.h>, worin die Informationen zu den entsprechenden Diensten stehen.
struct servent {
char *s_name;
char **s_aliases;
int s_port;
char *s_proto;
};
Eine kurze Beschreibung der einzelnen Strukturvariablen:
Tabelle 11.5
Die Strukturvariablen in servent und deren Bedeutung
Strukturvariable
|
Bedeutung
|
s_name
|
Offizieller Servicename
|
s_aliases
|
Ein Stringarray mit eventuellen Aliasnamen zum Service, falls vorhanden. Das letzte Element in der Liste ist NULL.
|
s_port
|
Die Portnummer zum Servicenamen
|
s_proto
|
Der Name des verwendeten Protokolls
|
Hier die Syntax zu getservbyname():
#include <netdb.h>
struct servent *getservbyname ( const char *name,
const char *proto );
Bei Angaben des Dienstes name und des Protokolls proto liefert Ihnen diese Funktion bei Erfolg eine Adresse auf die Information in struct servent. Bei einem Fehler wird NULL zurückgegeben.
Ähnlich verhält es sich mit der Funktion getservbyport() (im Vergleich mit getservbyname()). Nur liefert getservbyport() zum Dienst mit der Portnummer port (network-byte-order) und dem Protokoll proto die struct servent-Informationen zurück.
#include <netdb.h>
struct servent *getservbyport(int port, const char *proto);
Hierzu ein Beispiel, das die Funktion getservbyname() demonstriert. Da meistens Informationen abgefragt werden, die das TCP-Protokoll betreffen, könnten Sie auch das zweite Argument der Funktion getserbyname() gleich mit der Stringkonstante "tcp" belegen.
/* servent.c */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>
int main (int argc, char **argv) {
struct servent *service_info;
char **ptr;
if( argc != 3) {
printf("Usage: %s dienst protokoll\n", *argv);
return EXIT_FAILURE;
}
service_info = getservbyname( argv[1], argv[2] );
if( service_info == NULL ) {
herror("Konnte den Service nicht finden\n");
return EXIT_FAILURE;
}
printf("Service : %s\n", service_info->s_name);
printf("Port : %d\n", ntohs( service_info->s_port) );
printf("Aliase : \n");
ptr = service_info->s_aliases;
while(*ptr != NULL) {
printf("%s\n", *ptr++);
}
printf("Protokoll : %s\n", service_info->s_proto);
return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o servent servent.c
$ ./servent http tcp
Service : http
Port : 80
Aliase :
(null)
Protokoll : tcp
$ ./servent ftp tcp
Service : ftp
Port : 21
Aliase :
(null)
Protokoll : tcp
$ ./servent pop3 tcp
Service : pop3
Port : 110
Aliase :
(null)
Protokoll : tcp
Dasselbe Beispiel auch mit der Funktion getservbyport(). Als Protokoll verwenden Sie hierbei gewöhnlich TCP. Beim Beispiel werden entsprechende Informationen von den Ports 1 bis 100 (ohne den Alias) ausgegeben:
/* servent2.c */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
int main (int argc, char **argv) {
struct servent *service_info;
int port = 0;
while (port++ < 100) {
service_info = getservbyport (htons (port), "tcp");
if (service_info == NULL)
printf ("Port %d : Keine Infos\n", port);
else {
printf ("Service : %10s ", service_info->s_name);
printf ("Port : %3d ",
ntohs (service_info->s_port));
printf ("Protokoll : %5s\n", service_info->s_proto);
}
}
return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o servent2 servent2.c
$ ./servent2
Service : tcpmux Port : 1 Protokoll : tcp
Service : compressnet Port : 2 Protokoll : tcp
Service : compressnet Port : 3 Protokoll : tcp
Port 4 : Keine Infos
Service : rje Port : 5 Protokoll : tcp
Port 6 : Keine Infos
Service : echo Port : 7 Protokoll : tcp
Port 8 : Keine Infos
Service : discard Port : 9 Protokoll : tcp
Port 10 : Keine Infos
Service : systat Port : 11 Protokoll : tcp
Port 12 : Keine Infos
Service : daytime Port : 13 Protokoll : tcp
Port 14 : Keine Infos
Service : netstat Port : 15 Protokoll : tcp
...
...
Service : swift-rvf Port : 97 Protokoll : tcp
Service : tacnews Port : 98 Protokoll : tcp
Service : metagram Port : 99 Protokoll : tcp
Service : newacct Port : 100 Protokoll : tcp
|