static void tcp_connect_callback( const int t ) { D(bug("<%d> tcp_connect_callback() start, old state = %s\r\n", t, STATENAME(sockets[t].state))); switch( sockets[t].state ) { case LISTEN: tcp_reply( SYN|ACK, t ); sockets[t].seq_out++; sockets[t].state = SYN_RCVD; D(bug("<%d> Connect: LISTEN -> SYN_RCVD\r\n", t)); break; default: break; } D(bug("<%d> tcp_connect_callback() end, new state = %s\r\n", t, STATENAME(sockets[t].state))); }
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 void CALLBACK tcp_read_completion( DWORD error, DWORD bytes_read, LPWSAOVERLAPPED lpOverlapped, DWORD flags ) { EnterCriticalSection( &tcp_section ); const int t = (int)lpOverlapped->hEvent; sockets[t].bytes_received = bytes_read; D(bug("<%d> tcp_read_completion(error=%d, bytes_read=%d)\r\n", t, error, bytes_read)); D(bug("<%d> tcp_read_completion() start, old state = %s\r\n", t, STATENAME(sockets[t].state))); if(!sockets[t].in_use) { D(bug("<%d> ignoring canceled read\r\n", t)); } else { if( error != 0 ) { D(bug("<%d> resetting after read error\r\n", t)); tcp_reply( RST, t ); free_socket(t); } else { if(bytes_read == 0) { _closesocket( sockets[t].s ); sockets[t].s = INVALID_SOCKET; } else if( bytes_read > 0) { send_buffer( t, false ); } switch( sockets[t].state ) { case SYN_RCVD: if( bytes_read == 0 ) { D(bug("<%d> Closing: SYN_RCVD -> FINWAIT_1\r\n", t)); tcp_reply( ACK|FIN, t ); sockets[t].seq_out++; sockets[t].state = FINWAIT_1; } break; case ESTABLISHED: if( bytes_read == 0 ) { D(bug("<%d> Closing: ESTABLISHED -> FINWAIT_1\r\n", t)); tcp_reply( ACK|FIN, t ); sockets[t].seq_out++; sockets[t].state = FINWAIT_1; } break; case LISTEN: 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> LISTEN -> SYN_SENT\r\n", t)); break; case CLOSE_WAIT: if( bytes_read == 0) { tcp_reply( ACK|FIN, t ); sockets[t].seq_out++; sockets[t].state = LAST_ACK; D(bug("<%d> Closing: CLOSE_WAIT -> LAST_ACK\r\n", t)); if(sockets[t].remote_closed) { // Just in case that mac gets out of sync. _closesocket(sockets[t].s); sockets[t].s = INVALID_SOCKET; } } break; default: break; } if(!is_router_shutting_down && sockets[t].s != INVALID_SOCKET) { if(sockets[t].state != LISTEN) { b_recfrom(t); } } } } LeaveCriticalSection( &tcp_section ); }
/* - 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; }
TEkrStateDisablingLPM::TEkrStateDisablingLPM(CEncryptionKeyRefresherStateFactory& aFactory) : TEncryptionKeyRefresherState(aFactory) { LOG_FUNC STATENAME("TEkrStateDisablingLPM"); }
TEkrStateIdle::TEkrStateIdle(CEncryptionKeyRefresherStateFactory& aFactory) : TEncryptionKeyRefresherState(aFactory) { LOG_FUNC STATENAME("TEkrStateIdle"); }