Мне удалось спровоцировать закрытие каждого кажущегося полуоткрытого TCP-соединения, написав простую программу (полный код приведен ниже), которая привязывает локальный сокет к порту, к которому сервер считает, что он уже подключен, пытается установить новое соединение, а затем закрывает соединение.
(Примечание. Если соединение установится успешно, я сделаю HTTP-запрос GET только потому, что в моем случае фантомные протоколы поддержки TCP в моем случае явно исходят от простых HTTP-серверов, и мне было интересно, какой ответ я мог бы получить. Я думаю, вызовы "send" и "recv" могут быть удалены без ущерба для способности кода достичь желаемого результата.)
В следующем коде переменная src_port_num представляет номер порта на стороне клиента (в настоящее время не используется), на который сервер отправляет пакеты "TCP keep-alive", и dst_ip_cstr это IP-адрес сервера (например, веб-сервер Akamai), а dst_port_num - это номер порта (в моем случае это обычный HTTP-сервер на порту 80).
ВНИМАНИЕ! Поделившись этим кодом, я не имею в виду, что его теория работы может быть строго объяснена пониманием спецификации протокола TCP. Я просто догадался, что утверждение о том, что заброшенный локальный порт, на который удаленная конечная точка отправляет пакеты поддержания активности TCP, и попытка установить новое подключение к той же самой удаленной конечной точке, так или иначе подтолкнет удаленную конечную точку к закрытию несвежее полуоткрытое соединение - и у меня получилось .
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
void main()
{
// Local IP and port number
char * src_ip_cstr = "10.0.2.15";
int src_port_num = 4805;
// Remote IP and port number
char * dst_ip_cstr = "23.215.100.98";
int dst_port_num = 80;
int res = 0;
WSADATA wsadata;
res = WSAStartup( MAKEWORD(2,2), (&(wsadata)) );
if (0 != res) { printf("WSAStartup() FAIL\n"); return; }
printf( "\nSRC IP:%-16s Port:%d\nDST IP:%-16s Port:%d\n\n",
src_ip_cstr, src_port_num, dst_ip_cstr, dst_port_num );
sockaddr_in src;
memset( (void*)&src, 0, sizeof(src) );
src.sin_family = AF_INET;
src.sin_addr.S_un.S_addr = inet_addr( src_ip_cstr );
src.sin_port = htons( src_port_num );
sockaddr_in dst;
memset( (void*)&dst, 0, sizeof(dst) );
dst.sin_family = AF_INET;
dst.sin_addr.S_un.S_addr = inet_addr( dst_ip_cstr );
dst.sin_port = htons( dst_port_num );
int s = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
if ((-1) == s) { printf("socket() FAIL\n"); return; }
int val = 1;
res = setsockopt( s, SOL_SOCKET, SO_REUSEADDR,
(const char*)&val, sizeof(val) );
if (0 != res) { printf("setsockopt() FAIL\n"); return; }
res = bind( s, (sockaddr*)&src, sizeof(src) );
if ((-1) == res) { printf("bind() FAIL\n"); return; }
res = connect( s, (sockaddr*)&dst, sizeof(dst) );
if ((-1) == res) { printf("connect() FAIL\n"); return; }
char req[1024];
sprintf( req, "GET / HTTP/1.1\r\nHost: %s\r\nAccept: text/html\r\n"
"Accept-Language: en-us,en\r\nAccept-Charset: US-ASCII\r\n\r\n",
dst_ip_cstr );
printf("REQUEST:\n================\n%s\n================\n\n", req );
res = send( s, (char*)&req, strlen(req), 0 );
if ((-1) == res) { printf("send() FAIL\n"); return; }
const int REPLY_SIZE = 4096;
char reply[REPLY_SIZE];
memset( (void*)&reply, 0, REPLY_SIZE );
res = recv( s, (char*)&reply, REPLY_SIZE, 0 );
if ((-1) == res) { printf("recv() FAIL\n"); return; }
printf("REPLY:\n================\n%s\n================\n\n", reply );
res = shutdown( s, SD_BOTH );
res = closesocket( s );
res = WSACleanup();
}
ОГРОМНОЕ / СТРАШНОЕ / УДИВИТЕЛЬНОЕ РАСКРЫТИЕ
Как я уже упоминал в своем первоначальном вопросе, я наблюдал эти пакеты "TCP keep-alive" с Wireshark в VirtualBox под управлением Windows XP SP3, где основной операционной системой была Windows 7.
Когда я проснулся этим утром и снова посмотрел на это явление с чашкой кофе и свежими глазами, с пакетами «TCP keep-alive», которые все еще появляются каждые 60 секунд, даже через 24 часа, я сделал смешное открытие: пакеты продолжали приходить с трех разных IP-адресов, точно с 60-секундными интервалами (но в шахматном порядке для трех IP-адресов), , даже когда я отключил кабель Ethernet от Интернета ! Мой разум был взорван!
Итак, хотя эти три IP-адреса действительно соответствовали реальным веб-серверам, к которым мой веб-браузер подключался давно, пакеты поддержки активности TCP явно происходили из какого-то локального программного компонента.
Это откровение, каким бы шокирующим оно ни было, не изменило моего мышления о ситуации: с моей точки зрения программного обеспечения на стороне клиента были TCP-соединения "на стороне сервера", которые я хотел спровоцировать на закрытие.
В VirtualBox, выбрав «Устройства» -> «Сеть» -> «Подключить сетевой адаптер» , включает или отключает виртуальный сетевой адаптер, как если бы кабель виртуального Ethernet был подключен или отключен. Переключение в отключенное состояние привело к тому, что фантомные пакеты поддержки активности TCP перестали поступать в Wireshark. Впоследствии переключение в подключенное состояние привело к тому, что пакеты поддержки активности TCP возобновили поступление в Wireshark.
В любом случае, иногда мне нужно было запустить код выше ДВАЖДЫ, чтобы успешно закрыть полуоткрытое соединение. При первом запуске кода Wireshark показывал пакет с аннотацией «[Незаметный сегмент TCP ACKed]», который является всего лишь разновидностью путаницы с газовым освещением TCP, которую я надеялся создать, ха-ха! Поскольку новая конечная точка клиента является неожиданной для удаленной конечной точки, вызов «connect» зависает на 30 секунд, прежде чем завершится с ошибкой. Для пары полуоткрытых соединений зомби / фантомов достаточно было запустить программу только один раз, чтобы вызвать также пакет RST.
Мне нужно было несколько раз изменить программу, чтобы изменить комбинацию номера локального порта, удаленного IP-адреса и номера удаленного порта, чтобы соответствовать каждому фантомному TCP-пакету поддержки активности, который я наблюдал в Wireshark.(Я оставляю реализацию удобных для пользователя параметров командной строки дорогому читателю (это вам!).) После нескольких раундов изменения и запуска программы все пакеты поддержки активности зомби были остановлены.«Молчание пакетов», можно сказать.
ЭПИЛОГ
[В смокинге с бокалом мартини в руке, задумчиво глядя на океан с палубыо яхте, в компании других хакеров] «Я так и не узнал, откуда взялись эти зомби-пакеты ... Был ли это виртуальный Ethernet-адаптер VirtualBox Host-Only Network?? Только Oracle знает!»