communityWir suchen ständig neue Tutorials und Artikel! Habt ihr selbst schonmal einen Artikel verfasst und seid bereit dieses Wissen mit der Community zu teilen? Oder würdet ihr gerne einmal über ein Thema schreiben das euch besonders auf dem Herzen liegt? Dann habt ihr nun die Gelegenheit eure Arbeit zu veröffentlichen und den Ruhm dafür zu ernten. Schreibt uns einfach eine Nachricht mit dem Betreff „Community Articles“ und helft mit das Angebot an guten Artikeln zu vergrößern. Als Autor werdet ihr für den internen Bereich freigeschaltet und könnt dort eurer literarischen Ader freien Lauf lassen.

IP-Adressen mit Winsock2 PDF Drucken E-Mail
Benutzerbewertung: / 12
SchwachPerfekt 
Geschrieben von: StarShaper   
Samstag, den 10. Juni 2006 um 16:10 Uhr

Einführung

In diesem kurzen Tutorial erfahren Sie wie sich die lokale IP-Adresse samt CNAME mit C++ und Windows Sockets herausfinden lässt. Darüberhinaus lernen Sie wie sich ein Domainname mithilfe des so genannten Domain name system in die zugehörige IP-Adresse mappen lässt. In WinSock existiert eine Funktion mit dem Namen getaddrinfo um beispielsweise an die eigene IP-Adresse zu gelangen. Diese Funktion empfängt alle Details zur IP-Adresse in einer Variable vom Typ "structure addrinfo".

Der erste Schritt beim Erstellen des Programms ist sicher zu stellen, dass die Socket Library initialisiert wurde, falls diese nirgends anders schon initialisiert wurde. Im nächsten Schritt wird die Funktion getaddrinfo aufgerufen die einen Zeiger auf eine Liste mit addrinfo Strukturen zurückliefert. Dieser Zeiger kann dazu verwendet werden um die IP-Adresse in einen sockaddr_in Typ zu konvertieren. Tatsächlich ist es auch möglich die IP-Adresse ohne die Verwendung einer sockaddr_in Struktur zu erhalten. Aber mit sockaddr_in lässt sich diese Aufgabe viel sauberer und schneller erledigen.

Falls Sie bisher mit der Funktion gethostbyname gearbeitet haben, sei hiermit erwähnt das nach Angaben von Microsoft die Funktion gethostbyname für Anwendungen die auf Windows Sockets 2 aufbauen nicht mehr verwendet werden sollte. An die Stelle der alten Funktion tritt eine neue mit dem Namen getaddrinfo die Teil der POSIX Standard API ist und daher eine plattformunabhängige Verwendung zulässt. Die Funktion ist folgendermaßen deklariert.

int WSAAPI getaddrinfo(
    const char* nodename,
    const char* servname,
    const struct addrinfo* hints,
    struct addrinfo** res
);

Der erste Parameter mit der Richtung [in] ist ein Zeiger auf einen NULL-terminierten ANSI string der einen Hostnamen (node) oder aber einen numerischen string beinhaltet. Der string hat die bekannte hexadezimale Notation (127.0.0.1) und kann eine IPv4 oder auch eine IPv6 Adresse sein. Der zweite Parameter, ebenfalls [in], ist ein Zeiger auf einen NULL-terminierten ANSI string der entweder einen Servicenamen oder eine Portnummer darstellt. Der dritte Parameter hints [in] ist ein Zeiger auf eine addrinfo Struktur. Diese Struktur stellt hintergründige Informationen über den Sockettyp den der Caller, also der Aufrufer, unterstützt bereit. Der letzte Parameter namens res ist die [out] Variable die alle unsere von getaddrinfo gesammelten Resultate über den Host enthält. Es handelt sich dabei um einen Zeiger auf eine verkettete Liste mit einer oder mehreren addrinfo Strukturen. Nachfolgend sehen sie den Aufbau dieser Struktur.

typedef struct addrinfo {
    int ai_flags;
    int ai_family;
    int ai_socktype;
    int ai_protocol;
    size_t ai_addrlen;
    char* ai_canonname;
    struct sockaddr* ai_addr;
    struct addrinfo* ai_next;
} ADDRINFOA, *PADDRINFOA;
Die Tatsache das es sich bei der Variable res um einen Zeiger auf eine verkettete Liste handelt impliziert das alle Strukturen dieser Liste dynamisch zur Laufzeit von der Funktion getaddrinfo allokiert werden. Deshalb muss ein erfolgreicher Aufruf dieser Funktion mit einem abschließenden Ruf nach freeaddrinfo enden um den allokierten Speicher wieder freizugeben.
void freeaddrinfo(
    struct addrinfo* ai
);

Ein Rückgabetyp ist sinngemäß beim Freigeben von Speicher nicht erforderlich.

Das Programm

Die Funktion getaddrinfo liefert im Gegensatz zu freeaddrinfo sehr wohl einen Wert zurück. So wird 0 zurückgegeben falls sich kein Fehler ereignet hat. Eine spezifischer Fehlercode kann mit dem Aufruf von WSAGetLastError() abgerufen werden. Als Beispiel habe ich in der Hilfsfunktion PrintWSAErrorCodes eine switch Anweisung implementiert die die Details zu einigen möglichen Fehlertypen ausgibt, welche sich bei Aufruf diverser Winsock Funktionen ereignen können.

Das Programm verwendet die Funktion PrintHostInfo um die Host Informationen aufzulisten. Als Parameter werden jeweils der Hostname und der Port übernommen. Anschließend führt die Funktion getaddrinfo mit diesen Informationen einen DNS Aufruf aus und speichert das Resultat in der Variable aiList. Die Funktion selbst wird ein zweites Mal verwendet um alle lokalen IP-Adressen des lokalen Hosts aufzulisten dessen Name zuvor mithilfe der Funktion gethostname ermittelt wurde. Anhand der Tatsache das wir durch die verkettete Liste iterieren kann man erkennen das es sich bei der Variable aiList um einen Zeiger handelt. Ein Member der Struktur addrinfo ist ai_next mit dessen Hilfe es möglich ist durch die verkettete Liste zu navigieren. Vor der Ausgabe muss noch eine Konvertierung in ein entsprechend lesbares Format vorgenommen werden, da das Feld ai_addr einfach eine hexadezimale Zahl zurückgibt die in eine uns bekannte Formatierung für IPv4-Adressen konvertiert werden muss. Diese Funktion erfüllt inet_ntoa() die jedoch eine Struktur vom Typ in_addr entgegennimmt. Dies erfordert einen Cast in den entsprechend korrekten Typ.

Zusätzlich nutzen wir zu Demonstrationszwecken bei der zweiten Ausgabe der IP-Adressen eine STL-Liste. Der überladene ostream Operator << hilft dabei die Liste auszugeben. Sie können über die Kommandozeile Parameter eingeben und in der Funktion PrintHostInfo weiterverarbeiten. Der eigentliche Quellcode sieht folgendermaßen aus:

#include <iostream>
#include <string>
#include <list>
#include <winsock2.h>
// structure and function definitions for addrinfo and getaddrinfo
#include <ws2tcpip.h>
 
// Template helper class for output
template<class T>
std::ostream& operator<<(std::ostream& os, const std::list<T>& l)
{
    if(l.empty()) {
        return os << "empty";
    }
 
    for(std::list<T>::const_iterator it = l.begin(); it != l.end(); ++it) {
        os << *it << ", ";
    }
 
    return os;
}
 
void PrintWSAErrorCodes(int err_code)
{
    // Print out error code
    std::cerr << "Error Code: " << err_code << std::endl;
 
    // Get more specific details
    switch(err_code) {
        case WSAEFAULT: 
 
        // Vollständiger Code im Anhang...
 
        default:
        {
            std::cerr << "Unknown Error!\n";
        }
    }
}
 
int PrintHostInfo(const char* host, const char* port)
{
    // Buffer for the hostname
    char szHostName[256] = "";
    struct addrinfo aiHints;
    // Sores all results
    struct addrinfo *aiList = NULL;
    struct in_addr addr;
    int retVal;
 
    // Setup the hints address info structure which is passed to getaddrinfo()
    memset(&aiHints, 0, sizeof(aiHints));
    aiHints.ai_family = AF_INET;
    aiHints.ai_socktype = SOCK_STREAM;
    aiHints.ai_protocol = IPPROTO_TCP;
 
    // Get the host address info
    if((retVal = getaddrinfo(host, port, &aiHints, &aiList)) != 0) {
        PrintWSAErrorCodes(WSAGetLastError());
        return 1;
    }
 
    // Iterate through the linked list
    while(aiList != NULL) {
        // Convert structure into a proper format for inet_ntoa()
        addr.S_un = ((struct sockaddr_in *)(aiList->ai_addr))->sin_addr.S_un;
        std::cout << "IP-Addresses mapped to " << host << ": " << inet_ntoa(addr) 
                  << std::endl;
        aiList = aiList->ai_next;
    }
 
    // Freeing Address Information from Dynamic Allocation
    freeaddrinfo(aiList);
 
    // Now lets do it for localhost
    if(::gethostname(szHostName, sizeof(szHostName)) == SOCKET_ERROR) {
        PrintWSAErrorCodes(WSAGetLastError());
        return 1;
    }
 
    std::cout << "Localhost name is " << szHostName << "." << std::endl;
 
    // Get the host address info
    if((retVal = getaddrinfo(szHostName, port, &aiHints, &aiList)) != 0) {
        PrintWSAErrorCodes(WSAGetLastError());
        return 1;
    }
 
    std::list<std::string> aszIPAddresses;    // Just for fun
 
    while(aiList != NULL) {
        addr.S_un = ((struct sockaddr_in *)(aiList->ai_addr))->sin_addr.S_un;
        aszIPAddresses.push_back(inet_ntoa(addr));
        aiList = aiList->ai_next;
    }
 
    // Freeing Address Information from Dynamic Allocation
    freeaddrinfo(aiList);
 
    std::cout << "IP-Addresses mapped to localhost: ";
    std::cout << aszIPAddresses;
 
    return 0;
}
 
int main(int argc, char *argv[])
{
    WORD wVersionRequested;
    // Add 'ws2_32.lib' to your linker options
    WSADATA wsaData;
 
    // Only Winsock version 2.2 accepted
    wVersionRequested = MAKEWORD(2, 2);
 
    // The WSAStartup function initiates use of WS2_32.DLL by a process.
    if(::WSAStartup(wVersionRequested, &wsaData) != 0) {
        // Error handling
        std::cerr << "Error: Could not find a usable WinSock DLL.";
        return 255;
    }
 
    // Confirm that the WinSock DLL supports 2.2.
    // Note that if the DLL supports versions greater   
    // than 2.2 in addition to 2.2, it will still return 
    // 2.2 in wVersion since that is the version we requested.
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        std::cerr << "Error: Winsock version not supported."; 
        WSACleanup();
        return 1; 
    }
 
    // The WinSock DLL is acceptable. Proceed...
    if(argc > 2) {
        if(PrintHostInfo(argv[1], argv[2]) != 0) {
            std::cerr << "Error in function PrintHostInfo()\n";
        }
    } else {
        // i.e. www.codeplanet.eu 80
        std::cerr << "Parameters: <Host> <Port>";
    }
 
    WSACleanup();
 
    return 0;
}

Wie schon erwähnt muss zunächst die Funktion WSAStartup aufgerufen werden um die Benutzung von WS2_32.DLL durch einen anderen Prozess zu initialisieren. Der erste Parameter wVersionRequested gibt die verwendete WinSock Version an. Der obere Wert repräsentiert die Unterversion, der untere die Hauptversion. Wir verwenden die höchste verfügbare Version 2.2 von WinSock2 in dem unteren Beispiel. Der zweite Parameter lpWSAData ist ein Zeiger auf eine WSADATA Datenstruktur die dazu da ist Details der Windows Sockets Implementierung zu empfangen. Die Funktion WSAStartup gibt 0 zurück falls sich kein Fehler ereignet hat. Die WSAStartup muss die erste Windows Sockets Funktion sein die von einer Anwendung oder DLL aufgerufen wird.

int WSAStartup(
    WORD wVersionRequested,
    LPWSADATA lpWSAData
);

Im Anhang ist das Programm und der Quellcode zusammen mit der aktuellen ws2_32.lib enthalten. Normalerweise sollte sich die statische Bibliothek auch in Ihrem Visual C++ Verzeichnis befinden.

Zuletzt aktualisiert am Mittwoch, den 20. Mai 2009 um 16:15 Uhr
 
AUSWAHLMENÜ