static void tcp_start_listen( const int i ) { if( l_sockets[i].port ) { uint32 iface = l_sockets[i].iface; D(bug("[%d] binding to interface 0x%08X\r\n", i, iface)); l_sockets[i].s = _socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if(l_sockets[i].s != INVALID_SOCKET) { struct sockaddr_in to; memset( &to, 0, sizeof(to) ); to.sin_family = AF_INET; to.sin_port = htons( l_sockets[i].port ); to.sin_addr.s_addr = htonl( iface ); if( _bind ( l_sockets[i].s, (const struct sockaddr *)&to, sizeof(to) ) == 0 ) { D(bug("[%d] socket bound to port %d on interface 0x%08X\r\n", i, l_sockets[i].port, iface)); if( _listen( l_sockets[i].s, SOMAXCONN ) == SOCKET_ERROR ) { D(bug("[%d] listen() failed with error code %d\r\n", i, _WSAGetLastError())); } else { D(bug("[%d] listening to port %d\r\n", i, l_sockets[i].port)); _WSAResetEvent( l_sockets[i].ev ); if( SOCKET_ERROR == _WSAEventSelect( l_sockets[i].s, l_sockets[i].ev, FD_ACCEPT ) ) { D(bug("[%d] WSAEventSelect() failed with error code %d\r\n", i, _WSAGetLastError())); } } } else { D(bug("[%d] bind to port %d failed with error code %d\r\n", i, l_sockets[i].port, _WSAGetLastError())); } } else { D(bug("[%d] could not create a socket for port %d, error = %d\r\n", i, l_sockets[i].port, _WSAGetLastError())); } } }
static void tcp_accept_callback( const int lst ) { D(bug("[%d] tcp_accept_callback()\r\n", lst)); struct sockaddr_in to; memset( &to, 0, sizeof(to) ); to.sin_family = AF_INET; int tolen = sizeof(to); SOCKET s = _accept( l_sockets[lst].s, (struct sockaddr *)&to, &tolen ); if( s == INVALID_SOCKET ) { D(bug("[%d] connection not accepted, error code %d\r\n", lst, _WSAGetLastError())); } else { _WSAEventSelect( s, 0, 0 ); uint16 src_port = l_sockets[lst].port; uint16 dest_port = ntohs(to.sin_port); uint32 ip_dest = ntohl(to.sin_addr.s_addr); D(bug("[%d] connection accepted, local port:%d, remote %s:%d\r\n", lst, src_port, _inet_ntoa(to.sin_addr), dest_port)); if( l_sockets[lst].ip != 0 && l_sockets[lst].ip != ip_dest ) { _closesocket( s ); D(bug("[%d] authorization failure. connection closed.\r\n", lst )); } else { int t = alloc_new_socket( src_port, dest_port, ip_dest ); if( t < 0 ) { D(bug("<%d> out of slot space, connection dropped\r\n", t )); free_socket(t); } else { sockets[t].s = s; sockets[t].state = LISTEN; sockets[t].src_port = src_port; sockets[t].dest_port = dest_port; sockets[t].ip_src = macos_ip_address; sockets[t].ip_dest = ip_dest; sockets[t].seq_out = 0x00000001; sockets[t].seq_in = 0; // not known yet sockets[t].mac_ack = sockets[t].seq_out; // zero out pending bytes tcp_reply( SYN, t ); sockets[t].seq_out++; sockets[t].state = SYN_SENT; sockets[t].time_wait = GetTickCount() + SYN_FLOOD_PROTECTION_TIMEOUT; D(bug("<%d> Connect: LISTEN -> SYN_SENT\r\n", t)); _WSAResetEvent( sockets[t].ev ); if( SOCKET_ERROR == _WSAEventSelect( sockets[t].s, sockets[t].ev, FD_CLOSE ) ) { D(bug("<%d> WSAEventSelect() failed with error code %d\r\n", t, _WSAGetLastError())); } // No data from the remote host is needed until the connection is established. // So don't initiate read yet. } } } }
static bool b_send( const int t ) { int ret = _WSASend( sockets[t].s, sockets[t].buffers_write, sockets[t].buffer_count_write, &sockets[t].bytes_written, sockets[t].flags_write, &sockets[t].overlapped_write, tcp_write_completion ); bool result; if(ret == SOCKET_ERROR) { int socket_error = _WSAGetLastError(); if(socket_error == WSA_IO_PENDING) { D(bug("<%d> WSASend() i/o pending\r\n", t)); result = true; } else { D(bug("<%d> WSASend() returned %d\r\n", t, socket_error)); result = false; } } else /*if(ret == 0) */ { D(bug("<%d> WSASend() ok\r\n", t)); // Completion routine call is already scheduled. result = true; } return result; }
static void set_ttl( const int t, const uint8 ttl ) { int _ttl = ttl; // defensive programming, I know VCx if(_setsockopt( sockets[t].s, IPPROTO_IP, IP_TTL, (const char *)&_ttl, sizeof(int) ) == SOCKET_ERROR ) { D(bug("<%d> could not set ttl to %d, error=%d\r\n", t, ttl, _WSAGetLastError())); } else { D(bug("<%d> ttl set to %d.\r\n", t, ttl)); } }
static int alloc_new_socket( const uint16 src_port, const uint16 dest_port, const uint32 ip_dest ) { int t = alloc_socket(); if(t >= 0) { sockets[t].s = _socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if(sockets[t].s == INVALID_SOCKET) { free_socket( t ); t = -1; } else { sockets[t].src_port = src_port; sockets[t].dest_port = dest_port; sockets[t].from_len = sizeof(sockets[t].from); memset( &sockets[t].from, 0, sockets[t].from_len ); sockets[t].from.sin_family = AF_INET; sockets[t].from.sin_port = htons(dest_port); sockets[t].from.sin_addr.s_addr = htonl(ip_dest); struct sockaddr_in to; memset( &to, 0, sizeof(to) ); to.sin_family = AF_INET; if( _bind ( sockets[t].s, (const struct sockaddr *)&to, sizeof(to) ) == 0 ) { D(bug("<%d> socket bound\r\n", t)); } else { if( _WSAGetLastError() == WSAEINPROGRESS ) { D(bug("<%d> bind: a blocking call is in progress.\r\n", t)); } else { D(bug("<%d> bind failed with error code %d\r\n", t, _WSAGetLastError())); } free_socket( t ); t = -1; } } } return t; }
bool socket_t::b_recfrom() { bool result; int ret = WSARecvFrom(); if(ret == SOCKET_ERROR) { int socket_error = _WSAGetLastError(); if(socket_error == WSA_IO_PENDING) { D(bug("WSARecvFrom() i/o pending\r\n")); result = true; } else { D(bug("_WSAGetLastError() returned %d\r\n", socket_error)); result = false; } } else /*if(ret == 0) */ { D(bug("WSARecvFrom() ok\r\n")); // Completion routine call is already scheduled. result = true; } return result; }
static bool b_recfrom( const int t ) { bool result; if( !has_mac_read_space(t) ) { D(bug("<%d> read stalled, mac cannot accept any more data\r\n", t)); sockets[t].stream_to_mac_stalled_until = GetTickCount() + sockets[t].resend_timeout; return true; } int ret = _WSARecv( sockets[t].s, sockets[t].buffers_read, sockets[t].buffer_count_read, &sockets[t].bytes_received, &sockets[t].flags_read, &sockets[t].overlapped_read, tcp_read_completion ); if(ret == SOCKET_ERROR) { int socket_error = _WSAGetLastError(); if(socket_error == WSA_IO_PENDING) { D(bug("<%d> WSARecv() i/o pending\r\n", t)); result = true; } else { D(bug("<%d> WSARecv() returned error %d\r\n", t, socket_error)); result = false; } } else /*if(ret == 0) */ { D(bug("<%d> WSARecv() ok\r\n", t)); // Completion routine call is already scheduled. result = true; } return result; }
void write_tcp( tcp_t *tcp, int len ) { if(len < sizeof(tcp_t)) { D(bug("<%d> Too small tcp packet(%d) on unknown slot, dropped\r\n", -1, len)); return; } uint16 src_port = ntohs(tcp->src_port); uint16 dest_port = ntohs(tcp->dest_port); BOOL ok = true; BOOL handle_data = false; BOOL initiate_read = false; EnterCriticalSection( &tcp_section ); int t = find_socket( src_port, dest_port ); if(t < 0) { t = alloc_new_socket( src_port, dest_port, ntohl(tcp->ip.dest) ); ok = t >= 0; } if(ok) { D(bug("<%d> write_tcp %d bytes from port %d to port %d\r\n", t, len, src_port, dest_port)); } else { D(bug("<%d> FAILED write_tcp %d bytes from port %d to port %d\r\n", t, len, src_port, dest_port)); } if( ok && ISSET(tcp->flags,RST) ) { D(bug("<%d> RST set, resetting socket\r\n", t)); if( sockets[t].s != INVALID_SOCKET ) { D(bug("<%d> doing an extra shutdown (ie4)\r\n", t)); _shutdown( sockets[t].s, SD_BOTH ); } free_socket( t ); ok = false; } if(ok) { D(bug("<%d> State machine start = %s\r\n", t, STATENAME(sockets[t].state))); // always update receive window sockets[t].mac_window = ntohs(tcp->window); int header_len = tcp->header_len >> 2; int option_bytes = header_len - 20; char *data = (char *)tcp + sizeof(tcp_t) + option_bytes; int dlen = len - sizeof(tcp_t) - option_bytes; if( !ISSET(tcp->flags,ACK) ) { D(bug("<%d> ACK not set\r\n", t)); } if( ISSET(tcp->flags,SYN) ) { D(bug("<%d> SYN set\r\n", t)); // Note that some options are valid even if there is no SYN. // I don't care about those however. uint32 new_mss; process_options( t, (uint8 *)data - option_bytes, option_bytes, new_mss ); if(new_mss) { sockets[t].mac_mss = (int)new_mss; if( new_mss < sockets[t].buffers_read[0].len ) { sockets[t].buffers_read[0].len = new_mss; } D(bug("<%d> Max segment size set to %d\r\n", t, new_mss)); } } if( ISSET(tcp->flags,FIN) ) { D(bug("<%d> FIN set\r\n", t)); } // The sequence number Mac expects to see next time. sockets[t].mac_ack = ntohl(tcp->ack); D(bug("<%d> From Mac: Seq=%d, Ack=%d, window=%d, router Seq=%d\r\n", t, ntohl(tcp->seq), sockets[t].mac_ack, sockets[t].mac_window, sockets[t].seq_out)); if( sockets[t].stream_to_mac_stalled_until && sockets[t].mac_ack == sockets[t].seq_out && (sockets[t].state == ESTABLISHED || sockets[t].state == CLOSE_WAIT) ) { if( has_mac_read_space(t) ) { initiate_read = true; sockets[t].stream_to_mac_stalled_until = 0; D(bug("<%d> read resumed, mac can accept more data\r\n", t)); } } switch( sockets[t].state ) { case CLOSED: sockets[t].src_port = src_port; sockets[t].dest_port = dest_port; sockets[t].ip_src = ntohl(tcp->ip.src); sockets[t].ip_dest = ntohl(tcp->ip.dest); if( ISSET(tcp->flags,SYN) ) { sockets[t].seq_out = 0x00000001; sockets[t].seq_in = ntohl(tcp->seq) + 1; _WSAResetEvent( sockets[t].ev ); if( SOCKET_ERROR == _WSAEventSelect( sockets[t].s, sockets[t].ev, FD_CONNECT | FD_CLOSE ) ) { D(bug("<%d> WSAEventSelect() failed with error code %d\r\n", t, _WSAGetLastError())); } D(bug("<%d> connecting local port %d to remote %s:%d\r\n", t, src_port, _inet_ntoa(sockets[t].from.sin_addr), dest_port)); sockets[t].state = LISTEN; if( _WSAConnect( sockets[t].s, (const struct sockaddr *)&sockets[t].from, sockets[t].from_len, NULL, NULL, NULL, NULL ) == SOCKET_ERROR ) { int connect_error = _WSAGetLastError(); if( connect_error == WSAEWOULDBLOCK ) { D(bug("<%d> WSAConnect() i/o pending.\r\n", t)); } else { D(bug("<%d> WSAConnect() failed with error %d.\r\n", t, connect_error)); } } else { D(bug("<%d> WSAConnect() ok.\r\n", t)); } } else { if( ISSET(tcp->flags,FIN) ) { D(bug("<%d> No SYN but FIN on a closed socket.\r\n", t)); free_socket(t); } else { D(bug("<%d> No SYN on a closed socket. resetting.\r\n", t)); free_socket(t); } } break; case LISTEN: // handled in connect callback break; case SYN_SENT: if( ISSET(tcp->flags,SYN) && ISSET(tcp->flags,ACK) ) { sockets[t].seq_in = ntohl(tcp->seq) + 1; tcp_reply( ACK, t ); sockets[t].state = ESTABLISHED; initiate_read = true; sockets[t].accept_more_data_from_mac = true; sockets[t].time_wait = 0; } else if( ISSET(tcp->flags,SYN) ) { sockets[t].seq_in = ntohl(tcp->seq) + 1; tcp_reply( ACK|SYN, t ); sockets[t].seq_out++; sockets[t].state = SYN_RCVD; sockets[t].time_wait = 0; } else if( ISSET(tcp->flags,ACK) ) { // What was the bright idea here. D(bug("<%d> State is SYN_SENT, but got only ACK from Mac??\r\n", t)); sockets[t].state = FINWAIT_2; sockets[t].time_wait = 0; } break; case SYN_RCVD: if( ISSET(tcp->flags,ACK) ) { sockets[t].state = ESTABLISHED; handle_data = true; initiate_read = true; sockets[t].accept_more_data_from_mac = true; } break; case ESTABLISHED: if( ISSET(tcp->flags,FIN) ) { sockets[t].seq_in++; tcp_reply( ACK, t ); _shutdown( sockets[t].s, SD_SEND ); sockets[t].state = CLOSE_WAIT; } handle_data = true; break; case CLOSE_WAIT: // handled in tcp_read_completion break; case LAST_ACK: if( ISSET(tcp->flags,ACK) ) { D(bug("<%d> LAST_ACK received, socket closed\r\n", t)); free_socket( t ); } break; case FINWAIT_1: if( ISSET(tcp->flags,FIN) && ISSET(tcp->flags,ACK) ) { sockets[t].seq_in++; tcp_reply( ACK, t ); if(sockets[t].remote_closed) { _closesocket(sockets[t].s); sockets[t].s = INVALID_SOCKET; } else { _shutdown( sockets[t].s, SD_SEND ); } sockets[t].state = TIME_WAIT; sockets[t].time_wait = GetTickCount() + 2 * sockets[t].msl; } else if( ISSET(tcp->flags,FIN) ) { sockets[t].seq_in++; tcp_reply( ACK, t ); if(sockets[t].remote_closed) { _closesocket(sockets[t].s); sockets[t].s = INVALID_SOCKET; } else { _shutdown( sockets[t].s, SD_SEND ); } sockets[t].state = CLOSING; } else if( ISSET(tcp->flags,ACK) ) { sockets[t].state = FINWAIT_2; } break; case FINWAIT_2: if( ISSET(tcp->flags,FIN) ) { sockets[t].seq_in++; tcp_reply( ACK, t ); if(sockets[t].remote_closed) { _closesocket(sockets[t].s); sockets[t].s = INVALID_SOCKET; } else { _shutdown( sockets[t].s, SD_SEND ); } sockets[t].state = TIME_WAIT; sockets[t].time_wait = GetTickCount() + 2 * sockets[t].msl; } break; case CLOSING: if( ISSET(tcp->flags,ACK) ) { sockets[t].state = TIME_WAIT; sockets[t].time_wait = GetTickCount() + 2 * sockets[t].msl; } break; case TIME_WAIT: // Catching stray packets: wait MSL * 2 seconds, -> CLOSED // Timer already set since we might not get here at all. // I'm using exceptionally low MSL value (5 secs). D(bug("<%d> time wait, datagram discarded\r\n", t)); break; } // The "t" descriptor may already be freed. However, it's safe // to peek the state value inside the critical section. D(bug("<%d> State machine end = %s\r\n", t, STATENAME(sockets[t].state))); D(bug("<%d> handle_data=%d, initiate_read=%d\r\n", t, handle_data, initiate_read)); if( handle_data && dlen && sockets[t].accept_more_data_from_mac ) { if( sockets[t].seq_in != ntohl(tcp->seq) ) { D(bug("<%d> dropping duplicate datagram seq=%d, expected=%d\r\n", t, ntohl(tcp->seq), sockets[t].seq_in)); } else { set_ttl( t, tcp->ip.ttl ); struct sockaddr_in to; memset( &to, 0, sizeof(to) ); to.sin_family = AF_INET; to.sin_port = tcp->dest_port; to.sin_addr.s_addr = tcp->ip.dest; D(bug("<%d> sending %d bytes to remote host\r\n", t, dlen)); sockets[t].accept_more_data_from_mac = false; if( dlen > MAX_SEGMENT_SIZE ) { D(bug("<%d> IMPOSSIBLE: b_send() dropped %d bytes! \r\n", t, dlen-MAX_SEGMENT_SIZE)); dlen = MAX_SEGMENT_SIZE; } memcpy( sockets[t].buffers_write[0].buf, data, dlen ); sockets[t].buffers_write[0].len = dlen; sockets[t].bytes_remaining_to_send = dlen; sockets[t].bytes_to_send = dlen; bool send_now = false; if( ISSET(tcp->flags,PSH) ) { send_now = true; } else { // todo -- delayed send send_now = true; } if(send_now) { // Patch ftp server or client address if needed. int lst = 1; bool is_pasv; uint16 ftp_data_port = 0; if(ftp_is_ftp_port(sockets[t].src_port)) { // Local ftp server may be entering to passive mode. is_pasv = true; ftp_parse_port_command( sockets[t].buffers_write[0].buf, dlen, ftp_data_port, is_pasv ); } else if(ftp_is_ftp_port(sockets[t].dest_port)) { // Local ftp client may be using port command. is_pasv = false; ftp_parse_port_command( sockets[t].buffers_write[0].buf, dlen, ftp_data_port, is_pasv ); } if(ftp_data_port) { D(bug("<%d> ftp %s command detected, port %d\r\n", t, (is_pasv ? "SERVER PASV REPLY" : "CLIENT PORT"), ftp_data_port )); // Note: for security reasons, only allow incoming connection from sockets[t].ip_dest lst = alloc_listen_socket( ftp_data_port, sockets[t].ip_dest, 0/*iface*/, true ); if(lst < 0) { D(bug("<%d> no more free slots\r\n", t)); } else { // First start listening (need to know the local name later) tcp_start_listen( lst ); // When t is closed, lst must be closed too. sockets[t].child = lst; l_sockets[lst].parent = t; // Find out the local name struct sockaddr_in name; int namelen = sizeof(name); memset( &name, 0, sizeof(name) ); if( _getsockname( sockets[t].s, (struct sockaddr *)&name, &namelen ) == SOCKET_ERROR ) { D(bug("_getsockname() failed, error=%d\r\n", _WSAGetLastError() )); } ftp_modify_port_command( sockets[t].buffers_write[0].buf, dlen, MAX_SEGMENT_SIZE, ntohl(name.sin_addr.s_addr), ftp_data_port, is_pasv ); sockets[t].buffers_write[0].len = dlen; sockets[t].bytes_remaining_to_send = dlen; // Do not change "bytes_to_send" field as it is used for ack calculation } } // end of ftp patch if(!b_send(t)) { // on error, close the ftp data listening socket if one was created if(lst >= 0) { D(bug("[%d] closing listening port %d after write error\r\n", t, l_sockets[lst].port)); _closesocket( l_sockets[lst].s ); l_sockets[lst].s = INVALID_SOCKET; l_sockets[lst].port = 0; l_sockets[lst].ip = 0; l_sockets[lst].parent = -1; sockets[t].child = -1; } } } } } if(initiate_read) { if(!b_recfrom(t)) { // post icmp error message } } } LeaveCriticalSection( &tcp_section ); }
static unsigned int WINAPI tcp_listen_thread(void *arg) { WSAEVENT wait_handles[MAX_SOCKETS]; for( int i=0; i<MAX_SOCKETS; i++ ) { wait_handles[i] = l_sockets[i].ev; tcp_start_listen( i ); } while(!is_router_shutting_down) { DWORD ret = WaitForMultipleObjects( MAX_SOCKETS, wait_handles, FALSE, 200 ); if(is_router_shutting_down) break; EnterCriticalSection( &tcp_section ); if( ret >= WAIT_OBJECT_0 && ret < WAIT_OBJECT_0 + MAX_SOCKETS ) { const int lst = ret - WAIT_OBJECT_0; D(bug("[%d] connection attempt to port %d\r\n", lst, l_sockets[lst].port)); WSANETWORKEVENTS what; if( _WSAEnumNetworkEvents( l_sockets[lst].s, l_sockets[lst].ev, &what ) != SOCKET_ERROR ) { if( what.lNetworkEvents & FD_ACCEPT ) { if( what.iErrorCode[FD_ACCEPT_BIT] == 0 ) { D(bug("[%d] Connect ok\r\n", lst)); tcp_accept_callback(lst); } else { D(bug("[%d] Connect error=%d\r\n", lst, what.iErrorCode[FD_ACCEPT_BIT])); // Post icmp error } } } // close on errors too if(l_sockets[lst].once) { D(bug("[%d] once mode: closing listening socket on port %d\r\n", lst, l_sockets[lst].port)); if( _closesocket( l_sockets[lst].s ) == SOCKET_ERROR ) { int err = _WSAGetLastError(); D(bug("[%d] close error %d\r\n", lst, err)); } l_sockets[lst].s = INVALID_SOCKET; l_sockets[lst].port = 0; l_sockets[lst].ip = 0; int t = l_sockets[lst].parent; if( t >= 0 ) { sockets[t].child = -1; } l_sockets[lst].parent = -1; } _WSAResetEvent( l_sockets[lst].ev ); } LeaveCriticalSection( &tcp_section ); } return 0; }
/* - Dispatch remote close and connect events. - Expire time-waits. - Handle resend timeouts. */ static unsigned int WINAPI tcp_connect_close_thread(void *arg) { WSAEVENT wait_handles[MAX_SOCKETS]; for( int i=0; i<MAX_SOCKETS; i++ ) { wait_handles[i] = sockets[i].ev; } while(!is_router_shutting_down) { DWORD ret = WaitForMultipleObjects( MAX_SOCKETS, wait_handles, FALSE, 200 ); if(is_router_shutting_down) break; EnterCriticalSection( &tcp_section ); if( ret >= WAIT_OBJECT_0 && ret < WAIT_OBJECT_0 + MAX_SOCKETS ) { const int t = ret - WAIT_OBJECT_0; D(bug("<%d> Event %d\r\n", t, ret)); if(sockets[t].in_use) { WSANETWORKEVENTS what; if( _WSAEnumNetworkEvents( sockets[t].s, sockets[t].ev, &what ) != SOCKET_ERROR ) { if( what.lNetworkEvents & FD_CONNECT ) { if( what.iErrorCode[FD_CONNECT_BIT] == 0 ) { D(bug("<%d> Connect ok\r\n", t)); tcp_connect_callback(t); } else { D(bug("<%d> Connect error=%d\r\n", t, what.iErrorCode[FD_CONNECT_BIT])); // Post icmp error } } else if( what.lNetworkEvents & FD_CLOSE ) { if( what.iErrorCode[FD_CLOSE_BIT] == 0 ) { D(bug("<%d> graceful close, state = %s\r\n", t, STATENAME(sockets[t].state))); } else { D(bug("<%d> abortive close, state = %s, code=%d\r\n", t, STATENAME(sockets[t].state), what.iErrorCode[FD_CLOSE_BIT])); } sockets[t].remote_closed = true; } } else { int err = _WSAGetLastError(); if( err == WSAENOTSOCK ) { D(bug("<%d> WSAEnumNetworkEvents: socket is already closed\r\n", t)); } else { D(bug("<%d> WSAEnumNetworkEvents failed with error code %d, freeing slot\r\n", t, err)); free_socket( t ); } } } _WSAResetEvent( sockets[t].ev ); } else { static int interval = 5; if( !--interval ) { for( int i=0; i<MAX_SOCKETS; i++ ) { if(sockets[i].in_use) { DWORD tmw = sockets[i].time_wait; DWORD stl = sockets[i].stream_to_mac_stalled_until; if( tmw ) { if( GetTickCount() >= tmw ) { if( sockets[i].state == SYN_SENT ) { /* A very basic SYN flood protection. Note that watching SYN_SENT instead of SYN_RCVD, because the state codes are from the point of view of the Mac-Router interface, not Router-Remote. */ D(bug("<%d> SYN_SENT time-out expired\r\n", i)); } else { D(bug("<%d> TIME_WAIT expired\r\n", i)); } free_socket( i ); } } else if( stl ) { if( sockets[i].state == ESTABLISHED ) { if( GetTickCount() >= stl ) { D(bug("<%d> RESEND timeout expired\r\n", i)); sockets[i].stream_to_mac_stalled_until = GetTickCount() + sockets[i].resend_timeout; send_buffer( i, true ); } } else { sockets[i].stream_to_mac_stalled_until = 0; } } } } interval = 5; } } LeaveCriticalSection( &tcp_section ); } return 0; }