SO_EXCLUSIVEADDRUSE opção de soquete

A opção de soquete SO_EXCLUSIVEADDRUSE impede que outros soquetes sejam associados à força ao mesmo endereço e porta.

Sintaxe

A opção SO_EXCLUSIVEADDRUSE impede que outros soquetes sejam associados à força ao mesmo endereço e porta, uma prática habilitada pela opção de soquete SO_REUSEADDR. Essa reutilização pode ser executada por aplicativos mal-intencionados para interromper o aplicativo. A opção SO_EXCLUSIVEADDRUSE é muito útil para serviços do sistema que exigem alta disponibilidade.

O exemplo de código a seguir ilustra a configuração dessa opção.

#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;
}

Para ter qualquer efeito, a opção SO_EXCLUSIVADDRUSE deve ser definida antes que a função de associação seja chamada (isso também se aplica à opção SO_REUSEADDR). A Tabela 1 lista os efeitos da configuração da opção SO_EXCLUSIVEADDRUSE. Curinga indica associação ao endereço curinga, como 0.0.0.0 para IPv4 e :: para IPv6. Específico indica a associação a uma interface específica, como associar um endereço IP atribuído a um adaptador. Specific2 indica associação a um endereço específico diferente do endereço associado a no caso Específico.

Observação

O caso Specific2 é aplicável somente quando a primeira associação é executada com um endereço específico; para o caso em que o primeiro soquete está associado ao curinga, a entrada de Específico abrange todos os casos de endereço específicos.

 

Por exemplo, considere um computador com duas interfaces IP: 10.0.0.1 e 10.99.99.99. Se a primeira associação for a 10.0.0.1 e a porta 5150 com a opção SO_EXCLUSIVEADDRUSE definida, a segunda associação a 10.99.99.99 e a porta 5150 com qualquer ou nenhuma opção definida terá êxito. No entanto, se o primeiro soquete estiver associado ao endereço curinga (0.0.0.0) e à porta 5150 com SO_EXCLUSIVEADDRUSE definido, qualquer associação subsequente à mesma porta, independentemente do endereço IP, falhará com WSAEADDRINUSE (10048) ou WSAEACCESS (10013), dependendo de quais opções foram definidas no segundo soquete de associação.

Associar comportamento com vários conjuntos de opções

Segunda associação

Primeira associação

SO_EXCLUSIVEADDRUSE

Nenhuma opção ou SO_REUSEADDR

Curinga

Específico

Curinga

Específico

${ROWSPAN3}$SO_EXCLUSIVEADDRUSE${REMOVE}$

Curinga

Falha (10048)

Falha (10048)

Falha (10048)

Falha (10048)

Específico

Falha (10048)

Falha (10048)

Falha (10048)

Falha (10048)

Specific2

n/d

Êxito

n/d

Êxito

${ROWSPAN3}$Sem opções${REMOVE}$

Curinga

Falha (10048)

Falha (10048)

Falha (10048)

Falha (10048)

Específico

Falha (10048)

Falha (10048)

Falha (10048)

Falha (10048)

Specific2

n/d

Êxito

n/d

Êxito

${ROWSPAN3}$SO_REUSEADDR${REMOVE}$

Curinga

Falha (10013)

Falha (10013)

Sucesso*

Êxito

Específico

Falha (10013)

Falha (10013)

Êxito

Sucesso*

Specific2

n/d

Êxito

n/d

Êxito

* O comportamento é indefinido quanto a qual soquete receberá pacotes.

 

No caso em que a primeira associação não define nenhuma opção ou SO_REUSEADDR e a segunda associação executa um SO_REUSEADDR, o segundo soquete ultrapassou a porta e o comportamento em relação a qual soquete receberá pacotes é indeterminado. SO_EXCLUSIVEADDRUSE foi introduzido para resolver essa situação.

Um soquete com SO_EXCLUSIVEADDRUSE conjunto nem sempre pode ser reutilizado imediatamente após o fechamento do soquete. Por exemplo, se um soquete de escuta com o conjunto de sinalizadores exclusivo aceitar uma conexão após a qual o soquete de escuta estiver fechado, outro soquete não poderá se associar à mesma porta que o primeiro soquete de escuta com o sinalizador exclusivo até que a conexão aceita não esteja mais ativa.

Essa situação pode ser bem complicada; embora o soquete tenha sido fechado, o transporte subjacente pode não encerrar sua conexão. Mesmo depois que o soquete é fechado, o sistema deve enviar todos os dados armazenados em buffer, transmitir uma desconexão normal para o par e aguardar uma desconexão normal do par. Portanto, é possível que o transporte subjacente nunca libere a conexão, como quando o par anuncia uma janela de tamanho zero ou outros ataques desse tipo. No exemplo anterior, o soquete de escuta foi fechado depois que uma conexão de cliente foi aceita. Agora, mesmo que a conexão do cliente seja fechada, a porta ainda poderá não ser reutilizado se a conexão do cliente permanecer em um estado ativo devido a dados não reconhecidos e assim por diante.

Para evitar essa situação, os aplicativos devem garantir um desligamento normal : desligamento de chamada com o sinalizador SD_SEND e aguardar em um loop rev até que zero bytes sejam retornados. Isso evita o problema associado à reutilização da porta, garante que todos os dados foram recebidos pelo par e garante ao par que todos os seus dados foram recebidos com êxito.

A opção SO_LINGER pode ser definida em um soquete para evitar que a porta entre em um dos estados de espera ativos; no entanto, isso é desencorajado porque pode levar a efeitos indesejados, pois pode fazer com que a conexão seja redefinida. Por exemplo, se os dados tiverem sido recebidos, mas ainda não forem reconhecidos pelo par, e o computador local fechar o soquete com SO_LINGER definido, a conexão será redefinida e o par descartará os dados não reconhecidos. Além disso, escolher um tempo adequado para permanecer é difícil; um valor muito pequeno resulta em muitas conexões anuladas, enquanto um tempo limite grande pode deixar o sistema vulnerável a ataques de negação de serviço estabelecendo muitas conexões e, assim, paralisando vários threads de aplicativo.

Observação

Um soquete que está usando a opção SO_EXCLUSIVEADDRUSE deve ser desligado corretamente antes de fechá-lo. A falha ao fazer isso poderá causar um ataque de negação de serviço se o serviço associado precisar ser reiniciado.

 

Requisitos

Requisito Valor
Cliente mínimo com suporte
Windows 2000 Professional [somente aplicativos da área de trabalho]
Servidor mínimo com suporte
Windows 2000 Server [somente aplicativos da área de trabalho]
Cabeçalho
Winsock2.h