параметр сокета SO_EXCLUSIVEADDRUSE
Параметр SO_EXCLUSIVEADDRUSE сокета предотвращает принудительное привязку других сокетов к одному и тому же адресу и порту.
Синтаксис
Параметр SO_EXCLUSIVEADDRUSE предотвращает принудительное привязку других сокетов к одному и тому же адресу и порту, что поддерживается параметром сокета SO_REUSEADDR. Такое повторное использование может быть выполнено вредоносными приложениями, чтобы нарушить работу приложения. Параметр SO_EXCLUSIVEADDRUSE очень полезен для системных служб, требующих высокого уровня доступности.
В следующем примере кода показано задание этого параметра.
#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;
}
Для получения какого-либо эффекта необходимо задать параметр SO_EXCLUSIVADDRUSE перед вызовом функции bind (это также относится к параметру SO_REUSEADDR). В таблице 1 перечислены последствия установки параметра SO_EXCLUSIVEADDRUSE. Подстановочный знак указывает привязку к адресу с подстановочными знаками, например 0.0.0.0 для IPv4 и :: для IPv6. Специфичный указывает на привязку к определенному интерфейсу, например привязку IP-адреса, назначенного адаптеру. Specific2 указывает на привязку к определенному адресу, отличному от адреса, привязанного к в конкретном случае.
Примечание
Случай Specific2 применим только в том случае, если первая привязка выполняется с определенным адресом; Для случая, когда первый сокет привязан к подстановочным знакам, запись Для Конкретного охватывает все конкретные варианты адресов.
Например, рассмотрим компьютер с двумя интерфейсами IP: 10.0.0.1 и 10.99.99.99. Если первая привязка к 10.0.0.1 и порт 5150 с набором параметров SO_EXCLUSIVEADDRUSE, то вторая привязка к 10.99.99.99 и порту 5150 с любым параметром или без них будет выполнена успешно. Однако если первый сокет привязан к адресу с подстановочными знаками (0.0.0.0) и порту 5150 с заданным SO_EXCLUSIVEADDRUSE, любая последующая привязка к тому же порту (независимо от IP-адреса) завершится ошибкой WSAEADDRINUSE (10048) или WSAEACCESS (10013) в зависимости от того, какие параметры были заданы для второго сокета привязки.
Поведение привязки с помощью различных параметров
Вторая привязка
Первая привязка
SO_EXCLUSIVEADDRUSE
Нет параметров или SO_REUSEADDR
Подстановочный знак
Конкретных
Подстановочный знак
Конкретных
${ROWSPAN3}$SO_EXCLUSIVEADDRUSE${REMOVE}$
Подстановочный знак
Fail (10048)
Fail (10048)
Fail (10048)
Fail (10048)
Конкретных
Fail (10048)
Fail (10048)
Fail (10048)
Fail (10048)
Конкретный2
Недоступно
Успешное завершение
Недоступно
Успешное завершение
${ROWSPAN3}$Нет параметров${REMOVE}$
Подстановочный знак
Fail (10048)
Fail (10048)
Fail (10048)
Fail (10048)
Конкретных
Fail (10048)
Fail (10048)
Fail (10048)
Fail (10048)
Конкретный2
Недоступно
Успешное завершение
Недоступно
Успешное завершение
${ROWSPAN3}$SO_REUSEADDR${REMOVE}$
Подстановочный знак
Сбой (10013)
Сбой (10013)
Успех*
Успешное завершение
Конкретных
Сбой (10013)
Сбой (10013)
Успешное завершение
Успех*
Конкретный2
Недоступно
Успешное завершение
Недоступно
Успешное завершение
* Поведение не определено в том, какой сокет будет получать пакеты.
В случае, когда первая привязка не задает параметров или SO_REUSEADDR, а вторая привязка выполняет SO_REUSEADDR, второй сокет обогнал порт и поведение в отношении того, какой сокет будет получать пакеты, не определено. SO_EXCLUSIVEADDRUSE была введена для решения этой ситуации.
Сокет с заданным SO_EXCLUSIVEADDRUSE нельзя всегда повторно использовать сразу после закрытия сокета. Например, если прослушивающий сокет с установленным монопольным флагом принимает соединение, после которого прослушивающий сокет закрывается, другой сокет не может привязаться к тому же порту, что и первый сокет прослушивания с монопольным флагом, пока принятое соединение не будет активно.
Эта ситуация может быть довольно сложной; Несмотря на то, что сокет был закрыт, базовый транспорт не может завершить подключение. Даже после закрытия сокета система должна отправить все буферизированные данные, передать корректное отключение однорангового узла и дождаться корректного отключения от однорангового узла. Поэтому возможно, что базовый транспорт никогда не разблокирует подключение, например, когда одноранговый узел объявляет окно нулевого размера или другие подобные атаки. В предыдущем примере прослушивающий сокет был закрыт после принятия клиентского подключения. Теперь, даже если клиентское подключение закрыто, порт по-прежнему может не использоваться повторно, если клиентское подключение остается в активном состоянии из-за непризнанных данных и т. д.
Чтобы избежать этой ситуации, приложения должны обеспечить корректное завершение работы: вызовите завершение работы с флагом SD_SEND, а затем подождите в цикле recv , пока не будут возвращены ноль байтов. Это позволяет избежать проблем, связанных с повторным использованием портов, гарантирует, что все данные были получены одноранговым элементом, а также гарантирует, что все его данные были успешно получены.
Параметр SO_LINGER может быть установлен для сокета, чтобы предотвратить переход порта в одно из активных состояний ожидания. однако делать это не рекомендуется, так как это может привести к нежелательным последствиям, так как это может привести к сбросу подключения. Например, если данные получены, но еще не подтверждены одноранговым элементом, а локальный компьютер закрывает сокет с заданным SO_LINGER, соединение сбрасывается, а одноранговый узел удаляет непризнанные данные. Кроме того, выбрать подходящее время, чтобы задержиться трудно; Слишком малое значение приводит к большому числу прерванных подключений, в то время как большое время ожидания может сделать систему уязвимой для атак типа "отказ в обслуживании", установив много подключений и, таким образом, застопорив многочисленные потоки приложений.
Примечание
Сокет, использующий параметр SO_EXCLUSIVEADDRUSE, должен быть должным образом выключен перед закрытием. Если связанную службу потребуется перезапустить, это может привести к атаке типа "отказ в обслуживании".
Требования
Требование | Значение |
---|---|
Минимальная версия клиента |
Windows 2000 Professional [только классические приложения] |
Минимальная версия сервера |
Windows 2000 Server [только классические приложения] |
Заголовок |
|