SO_EXCLUSIVEADDRUSE Socketoption

Die Option SO_EXCLUSIVEADDRUSE Sockets verhindert, dass andere Sockets zwangsgebunden an die gleiche Adresse und denselben Port gebunden werden.

Syntax

Die Option SO_EXCLUSIVEADDRUSE verhindert, dass andere Sockets zwangsgebunden an die gleiche Adresse und denselben Port gebunden werden, eine Vorgehensweise, die durch die SO_REUSEADDR Socketoption aktiviert wird. Eine solche Wiederverwendung kann von schädlichen Anwendungen ausgeführt werden, um die Anwendung zu unterbrechen. Die option SO_EXCLUSIVEADDRUSE ist sehr nützlich für Systemdienste, die Hochverfügbarkeit erfordern.

Im folgenden Codebeispiel wird das Festlegen dieser Option veranschaulicht.

#ifndef UNICODE
#define UNICODE
#endif

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>             // Needed for _wtoi

#pragma comment(lib, "Ws2_32.lib")

int __cdecl wmain(int argc, wchar_t ** argv)
{
    WSADATA wsaData;
    int iResult = 0;
    int iError = 0;

    SOCKET s = INVALID_SOCKET;
    SOCKADDR_IN saLocal;
    int iOptval = 0;

    int iFamily = AF_UNSPEC;
    int iType = 0;
    int iProtocol = 0;

    int iPort = 0;

    // Validate the parameters
    if (argc != 5) {
        wprintf(L"usage: %ws <addressfamily> <type> <protocol> <port>\n", argv[0]);
        wprintf(L"    opens a socket for the specified family, type, & protocol\n");
        wprintf(L"    sets the SO_EXCLUSIVEADDRUSE socket option on the socket\n");
        wprintf(L"    then tries to bind the port specified on the command-line\n");
        wprintf(L"%ws example usage\n", argv[0]);
        wprintf(L"   %ws 0 2 17 5150\n", argv[0]);
        wprintf(L"   where AF_UNSPEC=0 SOCK_DGRAM=2 IPPROTO_UDP=17  PORT=5150\n",
                argv[0]);
        wprintf(L"   %ws 2 1 17 5150\n", argv[0]);
        wprintf(L"   where AF_INET=2 SOCK_STREAM=1 IPPROTO_TCP=6  PORT=5150\n", argv[0]);
        wprintf(L"   See the documentation for the socket function for other values\n");
        return 1;
    }

    iFamily = _wtoi(argv[1]);
    iType = _wtoi(argv[2]);
    iProtocol = _wtoi(argv[3]);

    iPort = _wtoi(argv[4]);

    if (iFamily != AF_INET && iFamily != AF_INET6) {
        wprintf(L"Address family must be either AF_INET (2) or AF_INET6 (23)\n");
        return 1;
    }

    if (iPort <= 0 || iPort >= 65535) {
        wprintf(L"Port specified must be greater than 0 and less than 65535\n");
        return 1;
    }
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        wprintf(L"WSAStartup failed with error: %d\n", iResult);
        return 1;
    }
    // Create the socket
    s = socket(iFamily, iType, iProtocol);
    if (s == INVALID_SOCKET) {
        wprintf(L"socket failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    // Set the exclusive address option
    iOptval = 1;
    iResult = setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
                         (char *) &iOptval, sizeof (iOptval));
    if (iResult == SOCKET_ERROR) {
        wprintf(L"setsockopt for SO_EXCLUSIVEADDRUSE failed with error: %ld\n",
                WSAGetLastError());
    }

    saLocal.sin_family = (ADDRESS_FAMILY) iFamily;
    saLocal.sin_port = htons( (u_short) iPort);
    saLocal.sin_addr.s_addr = htonl(INADDR_ANY);

    // Bind the socket
    iResult = bind(s, (SOCKADDR *) & saLocal, sizeof (saLocal));
    if (iResult == SOCKET_ERROR) {

        // Most errors related to setting SO_EXCLUSIVEADDRUSE
        //    will occur at bind.
        iError = WSAGetLastError();
        if (iError == WSAEACCES)
            wprintf(L"bind failed with WSAEACCES (access denied)\n");
        else
            wprintf(L"bind failed with error: %ld\n", iError);

    } else {
        wprintf(L"bind on socket with SO_EXCLUSIVEADDRUSE succeeded to port: %ld\n",
                iPort);
    }

    // cleanup
    closesocket(s);
    WSACleanup();

    return 0;
}

Um auswirkungen zu können, muss die Option SO_EXCLUSIVADDRUSE festgelegt werden, bevor die Bindfunktion aufgerufen wird (dies gilt auch für die Option SO_REUSEADDR). In Tabelle 1 sind die Auswirkungen des Festlegens der option SO_EXCLUSIVEADDRUSE aufgeführt. Ein Wildcard gibt die Bindung an die Wildcardadresse an, z. B. 0.0.0.0 für IPv4 und :: für IPv6. Specific gibt die Bindung an eine bestimmte Schnittstelle an, z. B. das Binden einer EINEM Adapter zugewiesenen IP-Adresse. Specific2 gibt die Bindung an eine andere Adresse als die Adresse an, die im spezifischen Fall an gebunden ist.

Hinweis

Der Spezifische2-Fall gilt nur, wenn die erste Bindung mit einer bestimmten Adresse ausgeführt wird. Für den Fall, in dem der erste Socket an den Wildcard gebunden ist, deckt der Eintrag für Specific alle spezifischen Adressfälle ab.

 

Betrachten Sie beispielsweise einen Computer mit zwei IP-Schnittstellen: 10.0.0.1 und 10.99.99.99. Wenn die erste Bindung an 10.0.0.1 und Port 5150 ist, wobei die option SO_EXCLUSIVEADDRUSE festgelegt ist, ist die zweite Bindung an 10.99.99.99 und Port 5150 mit oder ohne Optionssatz erfolgreich. Wenn der erste Socket jedoch an die Platzhalteradresse (0.0.0.0.0) und Port 5150 mit festgelegtem SO_EXCLUSIVEADDRUSE gebunden ist, schlägt jede nachfolgende Bindung an den gleichen Port – unabhängig von der IP-Adresse – entweder mit WSAEADDRINUSE (10048) oder WSAEACCESS (10013) fehl, je nachdem, welche Optionen für den zweiten Bindungssocket festgelegt wurden.

Bindungsverhalten mit verschiedenen festgelegten Optionen

Zweite Bindung

Erste Bindung

SO_EXCLUSIVEADDRUSE

Keine Optionen oder SO_REUSEADDR

Platzhalter

Bestimmten

Platzhalter

Bestimmten

${ROWSPAN3}$SO_EXCLUSIVEADDRUSE${REMOVE}$

Platzhalter

Fehler (10048)

Fehler (10048)

Fehler (10048)

Fehler (10048)

Bestimmten

Fehler (10048)

Fehler (10048)

Fehler (10048)

Fehler (10048)

Spezifisch2

Erfolg

Erfolg

${ROWSPAN3}$No options${REMOVE}$

Platzhalter

Fehler (10048)

Fehler (10048)

Fehler (10048)

Fehler (10048)

Bestimmten

Fehler (10048)

Fehler (10048)

Fehler (10048)

Fehler (10048)

Spezifisch2

Erfolg

Erfolg

${ROWSPAN3}$SO_REUSEADDR${REMOVE}$

Platzhalter

Fehler (10013)

Fehler (10013)

Erfolg*

Erfolg

Bestimmten

Fehler (10013)

Fehler (10013)

Erfolg

Erfolg*

Spezifisch2

Erfolg

Erfolg

* Das Verhalten ist nicht definiert, welcher Socket Pakete empfängt.

 

Wenn die erste Bindung keine Optionen oder SO_REUSEADDR festlegt und die zweite Bindung eine SO_REUSEADDR ausführt, hat der zweite Socket den Port und das Verhalten in Bezug auf den Socket, der Pakete empfangen wird, überholt. SO_EXCLUSIVEADDRUSE wurde eingeführt, um diese Situation zu beheben.

Ein Socket mit SO_EXCLUSIVEADDRUSE Satz kann nicht immer sofort nach dem Schließen des Sockets wiederverwendet werden. Wenn beispielsweise ein Abhörsocket mit dem exklusiven Flagsatz eine Verbindung akzeptiert, nach der der Abhörsocket geschlossen wird, kann ein anderer Socket nicht an denselben Port wie der erste Überwachungssocket mit dem exklusiven Flag binden, bis die akzeptierte Verbindung nicht mehr aktiv ist.

Diese Situation kann ziemlich kompliziert sein; auch wenn der Socket geschlossen wurde, wird die Verbindung vom zugrunde liegenden Transport möglicherweise nicht beendet. Selbst nachdem der Socket geschlossen wurde, muss das System alle gepufferten Daten senden, eine ordnungsgemäße Trennung an den Peer senden und auf eine ordnungsgemäße Trennung vom Peer warten. Es ist daher möglich, dass der zugrunde liegende Transport die Verbindung niemals freigeben kann, z. B. wenn der Peer ein Fenster der Größe Null angibt, oder andere solche Angriffe. Im vorherigen Beispiel wurde der Abhörsocket geschlossen, nachdem eine Clientverbindung akzeptiert wurde. Selbst wenn die Clientverbindung geschlossen ist, kann der Port immer noch nicht wiederverwendet werden, wenn die Clientverbindung aufgrund nicht bestätigter Daten in einem aktiven Zustand verbleibt usw.

Um diese Situation zu vermeiden, sollten Anwendungen ein ordnungsgemäßes Herunterfahren sicherstellen: Das Herunterfahren mit dem flag SD_SEND aufrufen und dann in einer recv-Schleife warten, bis null Bytes zurückgegeben werden. Dadurch wird das Problem im Zusammenhang mit der Portwiederverwendung vermieden, sichergestellt, dass alle Daten vom Peer empfangen wurden, und dem Peer wird sichergestellt, dass alle Seine Daten erfolgreich empfangen wurden.

Die Option SO_LINGER kann für einen Socket festgelegt werden, um zu verhindern, dass der Port in einen der aktiven Wartezustände wechselt. Es wird jedoch davon abgeraten, da dies zu unerwünschten Effekten führen kann, da dies dazu führen kann, dass die Verbindung zurückgesetzt wird. Wenn beispielsweise Daten empfangen, aber noch nicht vom Peer bestätigt wurden und der lokale Computer den Socket mit festgelegtem SO_LINGER schließt, wird die Verbindung zurückgesetzt, und der Peer verwirft die nicht bestätigten Daten. Auch die Auswahl einer geeigneten Zeit zum Verweilen ist schwierig; Ein zu kleiner Wert führt zu vielen abgebrochenen Verbindungen, während ein großes Timeout das System anfällig für Denial-of-Service-Angriffe machen kann, indem viele Verbindungen hergestellt werden und dadurch zahlreiche Anwendungsthreads blockiert werden.

Hinweis

Ein Socket, der die Option SO_EXCLUSIVEADDRUSE verwendet, muss vor dem Schließen ordnungsgemäß heruntergefahren werden. Andernfalls kann ein Denial-of-Service-Angriff ausgelöst werden, wenn der zugeordnete Dienst neu gestartet werden muss.

 

Anforderungen

Anforderung Wert
Unterstützte Mindestversion (Client)
Windows 2000 Professional [nur Desktop-Apps]
Unterstützte Mindestversion (Server)
Windows 2000 Server [nur Desktop-Apps]
Header
Winsock2.h