/*
* Copyright 2010 Andreas Färber <andreas.faerber@web.de>
* All rights reserved. Distributed under the terms of the MIT License.
*/
/*
* NOTE This is a cleanroom TCP implementation with some known issues.
* Protection Against Wrapping Sequence (PAWS) needs to be added.
* Congestion control needs to be implemented (slow start, recv. window size).
* The use of *Packets needs to be re-evaluated in the context of TCP;
* probably a singly-linked list of received data chunks is more efficient.
* Debug output should be tuned for better aspect oriented tracing.
* While Little Endian systems have been considered, this still needs testing.
*/
#include <boot/net/TCP.h>
#include <stdio.h>
#include <KernelExport.h>
#include <boot/net/ChainBuffer.h>
#include <boot/net/NetStack.h>
#include "real_time_clock.h"
//#define TRACE_TCP
//#define TRACE_TCP_RANDOMNESS
//#define TRACE_TCP_CHECKSUM
//#define TRACE_TCP_QUEUE
#ifdef TRACE_TCP
# define TRACE(x, ...) dprintf(x, ## __VA_ARGS__)
#else
# define TRACE(x, ...) ;
#endif
#ifdef TRACE_TCP_RANDOMNESS
# define TRACE_PORT(x, ...) dprintf(x, ## __VA_ARGS__)
#else
# define TRACE_PORT(x, ...) ;
#endif
#if defined(TRACE_TCP_CHECKSUM)
# define TRACE_CHECKSUM(x, ...) dprintf(x, ## __VA_ARGS__)
#else
# define TRACE_CHECKSUM(x, ...) ;
#endif
#if defined(TRACE_TCP_QUEUE)
# define TRACE_QUEUE(x, ...) dprintf(x, ## __VA_ARGS__)
#else
# define TRACE_QUEUE(x, ...) ;
#endif
static unsigned int
_rand32(void)
{
static unsigned int next = 0;
if (next == 0)
next = real_time_clock_usecs() / 1000000;
next = (next >> 1) ^ (unsigned int)((0 - (next & 1U)) & 0xd0000001U);
// characteristic polynomial: x^32 + x^31 + x^29 + x + 1
return next;
}
static unsigned short
_rand14(void)
{
// TODO: Find suitable generator polynomial.
return _rand32() & 0x3fff;
}
TCPPacket::TCPPacket()
:
fData(NULL),
fNext(NULL)
{
}
TCPPacket::~TCPPacket()
{
free(fData);
}
status_t
TCPPacket::SetTo(const void* data, size_t size, ip_addr_t sourceAddress,
uint16 sourcePort, ip_addr_t destinationAddress, uint16 destinationPort,
uint32 sequenceNumber, uint32 acknowledgmentNumber, uint8 flags)
{
if (data == NULL && size > 0)
return B_BAD_VALUE;
if (size > 0) {
fData = malloc(size);
if (fData == NULL)
return B_NO_MEMORY;
memcpy(fData, data, size);
} else
fData = NULL;
fSize = size;
fSourceAddress = sourceAddress;
fSourcePort = sourcePort;
fDestinationAddress = destinationAddress;
fDestinationPort = destinationPort;
fSequenceNumber = sequenceNumber;
fAcknowledgmentNumber = acknowledgmentNumber;
fFlags = flags;
return B_OK;
}
ip_addr_t
TCPPacket::SourceAddress() const
{
return fSourceAddress;
}
ip_addr_t
TCPPacket::DestinationAddress() const
{
return fDestinationAddress;
}
uint16
TCPPacket::SourcePort() const
{
return fSourcePort;
}
uint16
TCPPacket::DestinationPort() const
{
return fDestinationPort;
}
uint32
TCPPacket::SequenceNumber() const
{
return fSequenceNumber;
}
uint32
TCPPacket::AcknowledgmentNumber() const
{
return fAcknowledgmentNumber;
}
bool
TCPPacket::ProvidesSequenceNumber(uint32 sequenceNumber) const
{
// TODO PAWS
return fSequenceNumber <= sequenceNumber
&& fSequenceNumber + fSize > sequenceNumber;
}
TCPPacket*
TCPPacket::Next() const
{
return fNext;
}
void
TCPPacket::SetNext(TCPPacket* packet)
{
fNext = packet;
}
TCPSocket::TCPSocket()
:
fTCPService(NetStack::Default()->GetTCPService()),
fAddress(INADDR_ANY),
fPort(0),
fSequenceNumber(0),
fFirstPacket(NULL),
fLastPacket(NULL),
fFirstSentPacket(NULL),
fLastSentPacket(NULL),
fState(TCP_SOCKET_STATE_INITIAL),
fRemoteState(TCP_SOCKET_STATE_INITIAL)
{
}
TCPSocket::~TCPSocket()
{
if (fTCPService != NULL && fPort != 0)
fTCPService->UnbindSocket(this);
}
uint16
TCPSocket::WindowSize() const
{
// TODO A large window size leads to read timeouts
// due to resends occuring too late.
#if 0
size_t windowSize = 0xffff;
for (TCPPacket* packet = fFirstPacket;
packet != NULL && windowSize > packet->DataSize();
packet = packet->Next())
windowSize -= packet->DataSize();
return windowSize;
#else
return 4096;
#endif
}
status_t
TCPSocket::Connect(ip_addr_t address, uint16 port)
{
fRemoteAddress = address;
fRemotePort = port;
fSequenceNumber = _rand32();
fPort = 0xC000 + (_rand14() & ~0xc000);
TRACE_PORT("TCPSocket::Connect(): connecting from port %u\n", fPort);
fAcknowledgeNumber = 0;
fNextSequence = 0;
status_t error = fTCPService->BindSocket(this);
if (error != B_OK)
return error;
// send SYN
TCPPacket* packet = new(nothrow) TCPPacket();
if (packet == NULL)
return B_NO_MEMORY;
error = packet->SetTo(NULL, 0, fAddress, fPort, address, port,
fSequenceNumber, fAcknowledgeNumber, TCP_SYN);
if (error != B_OK) {
delete packet;
return error;
}
error = _Send(packet);
if (error != B_OK)
return error;
fState = TCP_SOCKET_STATE_SYN_SENT;
fSequenceNumber++;
TRACE("SYN sent\n");
// receive SYN-ACK
error = _WaitForState(TCP_SOCKET_STATE_OPEN, 1000000LL);
if (error != B_OK) {
TRACE("no SYN-ACK received\n");
return error;
}
TRACE("SYN-ACK received\n");
return B_OK;
}
status_t
TCPSocket::Close()
{
// send FIN
TCPPacket* packet = new(nothrow) TCPPacket();
if (packet == NULL)
return B_NO_MEMORY;
status_t error = packet->SetTo(NULL, 0, fAddress, fPort, fRemoteAddress,
fRemotePort, fSequenceNumber, fAcknowledgeNumber, TCP_FIN | TCP_ACK);
if (error != B_OK) {
delete packet;
return error;
}
error = _Send(packet);
if (error != B_OK)
return error;
fState = TCP_SOCKET_STATE_FIN_SENT;
TRACE("FIN sent\n");
error = _WaitForState(TCP_SOCKET_STATE_CLOSED, 1000000LL);
if (error != B_OK)
return error;
return B_OK;
}
status_t
TCPSocket::Read(void* buffer, size_t bufferSize, size_t* bytesRead,
bigtime_t timeout)
{
TRACE("TCPSocket::Read(): size = %lu\n", bufferSize);
if (bytesRead == NULL)
return B_BAD_VALUE;
*bytesRead = 0;
TCPPacket* packet = NULL;
bigtime_t startTime = system_time();
do {
fTCPService->ProcessIncomingPackets();
//_ResendQueue();
packet = _PeekPacket();
if (packet == NULL && fRemoteState != TCP_SOCKET_STATE_OPEN)
return B_ERROR;
if (packet == NULL && timeout > 0LL)
_Ack();
} while (packet == NULL && system_time() - startTime < timeout);
if (packet == NULL) {
#ifdef TRACE_TCP_QUEUE
_DumpQueue();
#endif
return (timeout == 0) ? B_WOULD_BLOCK : B_TIMED_OUT;
}
uint32 packetOffset = fNextSequence - packet->SequenceNumber();
size_t readBytes = packet->DataSize() - packetOffset;
if (readBytes > bufferSize)
readBytes = bufferSize;
if (buffer != NULL)
memcpy(buffer, (uint8*)packet->Data() + packetOffset, readBytes);
*bytesRead = readBytes;
if (!packet->ProvidesSequenceNumber(fNextSequence + readBytes)) {
_DequeuePacket();
delete packet;
packet = NULL;
}
fNextSequence += readBytes;
if (packet == NULL && *bytesRead < bufferSize) {
do {
if (buffer != NULL)
buffer = (uint8*)buffer + readBytes;
bufferSize -= readBytes;
fTCPService->ProcessIncomingPackets();
packet = _PeekPacket();
if (packet == NULL && fRemoteState != TCP_SOCKET_STATE_OPEN)
break;
readBytes = 0;
if (packet == NULL) {
_Ack();
continue;
}
readBytes = packet->DataSize();
if (readBytes > bufferSize)
readBytes = bufferSize;
if (buffer != NULL)
memcpy(buffer, packet->Data(), readBytes);
*bytesRead += readBytes;
if (readBytes == packet->DataSize()) {
_DequeuePacket();
delete packet;
}
fNextSequence += readBytes;
} while (readBytes < bufferSize &&
system_time() - startTime < timeout);
#ifdef TRACE_TCP_QUEUE
if (readBytes < bufferSize) {
TRACE_QUEUE("TCP: Unable to deliver more data!\n");
_DumpQueue();
}
#endif
}
return B_OK;
}
status_t
TCPSocket::Write(const void* buffer, size_t bufferSize)
{
if (buffer == NULL || bufferSize == 0)
return B_BAD_VALUE;
// TODO: Check for MTU and create multiple packets if necessary.
TCPPacket* packet = new(nothrow) TCPPacket();
if (packet == NULL)
return B_NO_MEMORY;
status_t error = packet->SetTo(buffer, bufferSize, fAddress, fPort,
fRemoteAddress, fRemotePort, fSequenceNumber, fAcknowledgeNumber,
TCP_ACK);
if (error != B_OK) {
delete packet;
return error;
}
return _Send(packet);
}
void
TCPSocket::Acknowledge(uint32 number)
{
TRACE("TCPSocket::Acknowledge(): %lu\n", number);
// dequeue packets
for (TCPPacket* packet = fFirstSentPacket; packet != NULL;
packet = fFirstSentPacket) {
if (packet->SequenceNumber() >= number)
return;
fFirstSentPacket = packet->Next();
delete packet;
}
fLastSentPacket = NULL;
}
void
TCPSocket::ProcessPacket(TCPPacket* packet)
{
TRACE("TCPSocket::ProcessPacket()\n");
if ((packet->Flags() & TCP_FIN) != 0) {
fRemoteState = TCP_SOCKET_STATE_FIN_SENT;
TRACE("FIN received\n");
_Ack();
}
if (fState == TCP_SOCKET_STATE_SYN_SENT) {
if ((packet->Flags() & TCP_SYN) != 0
&& (packet->Flags() & TCP_ACK) != 0) {
fNextSequence = fAcknowledgeNumber = packet->SequenceNumber() + 1;
fRemoteState = TCP_SOCKET_STATE_SYN_SENT;
delete packet;
_Ack();
fState = fRemoteState = TCP_SOCKET_STATE_OPEN;
return;
}
} else if (fState == TCP_SOCKET_STATE_OPEN) {
} else if (fState == TCP_SOCKET_STATE_FIN_SENT) {
if ((packet->Flags() & TCP_ACK) != 0) {
TRACE("FIN-ACK received\n");
if (fRemoteState == TCP_SOCKET_STATE_FIN_SENT)
fState = TCP_SOCKET_STATE_CLOSED;
}
}
if (packet->DataSize() == 0) {
TRACE("TCPSocket::ProcessPacket(): not queuing due to lack of data\n");
delete packet;
return;
}
// For now rather protect us against being flooded with packets already
// acknowledged. "If it's important, they'll send it again."
// TODO PAWS
if (packet->SequenceNumber() < fAcknowledgeNumber) {
TRACE_QUEUE("TCPSocket::ProcessPacket(): not queuing due to wraparound\n");
delete packet;
return;
}
if (fLastPacket == NULL) {
// no packets enqueued
TRACE("TCPSocket::ProcessPacket(): first in queue\n");
packet->SetNext(NULL);
fFirstPacket = fLastPacket = packet;
} else if (fLastPacket->SequenceNumber() < packet->SequenceNumber()) {
// enqueue in back
TRACE("TCPSocket::ProcessPacket(): enqueue in back\n");
packet->SetNext(NULL);
fLastPacket->SetNext(packet);
fLastPacket = packet;
} else if (fFirstPacket->SequenceNumber() > packet->SequenceNumber()) {
// enqueue in front
TRACE("TCPSocket::ProcessPacket(): enqueue in front\n");
TRACE_QUEUE("TCP: Enqueuing %lx - %lx in front! (next is %lx)\n",
packet->SequenceNumber(),
packet->SequenceNumber() + packet->DataSize() - 1,
fNextSequence);
packet->SetNext(fFirstPacket);
fFirstPacket = packet;
} else if (fFirstPacket->SequenceNumber() == packet->SequenceNumber()) {
TRACE_QUEUE("%s(): dropping due to identical first packet\n", __func__);
delete packet;
return;
} else {
// enqueue in middle
TRACE("TCPSocket::ProcessPacket(): enqueue in middle\n");
for (TCPPacket* queuedPacket = fFirstPacket; queuedPacket != NULL;
queuedPacket = queuedPacket->Next()) {
if (queuedPacket->SequenceNumber() == packet->SequenceNumber()) {
TRACE_QUEUE("TCPSocket::EnqueuePacket(): TCP packet dropped\n");
// we may be waiting for a previous packet
delete packet;
return;
}
if (queuedPacket->Next()->SequenceNumber()
> packet->SequenceNumber()) {
packet->SetNext(queuedPacket->Next());
queuedPacket->SetNext(packet);
break;
}
}
}
while (packet != NULL && packet->SequenceNumber() == fAcknowledgeNumber) {
fAcknowledgeNumber = packet->SequenceNumber() + packet->DataSize();
packet = packet->Next();
}
}
TCPPacket*
TCPSocket::_PeekPacket()
{
TRACE("TCPSocket::_PeekPacket(): fNextSequence = %lu\n", fNextSequence);
for (TCPPacket* packet = fFirstPacket; packet != NULL;
packet = packet->Next()) {
if (packet->ProvidesSequenceNumber(fNextSequence))
return packet;
}
return NULL;
}
TCPPacket*
TCPSocket::_DequeuePacket()
{
//TRACE("TCPSocket::DequeuePacket()\n");
if (fFirstPacket == NULL)
return NULL;
if (fFirstPacket->ProvidesSequenceNumber(fNextSequence)) {
TCPPacket* packet = fFirstPacket;
fFirstPacket = packet->Next();
if (fFirstPacket == NULL)
fLastPacket = NULL;
packet->SetNext(NULL);
TRACE("TCP: Dequeuing %lx - %lx from front.\n",
packet->SequenceNumber(),
packet->SequenceNumber() + packet->DataSize() - 1);
return packet;
}
for (TCPPacket* packet = fFirstPacket;
packet != NULL && packet->Next() != NULL;
packet = packet->Next()) {
if (packet->Next()->ProvidesSequenceNumber(fNextSequence)) {
TCPPacket* nextPacket = packet->Next();
packet->SetNext(nextPacket->Next());
if (fLastPacket == nextPacket)
fLastPacket = packet;
TRACE("TCP: Dequeuing %lx - %lx.\n",
nextPacket->SequenceNumber(),
nextPacket->SequenceNumber() + nextPacket->DataSize() - 1);
return nextPacket;
}
}
TRACE_QUEUE("dequeue failed!\n");
return NULL;
}
status_t
TCPSocket::_Send(TCPPacket* packet, bool enqueue)
{
ChainBuffer buffer((void*)packet->Data(), packet->DataSize());
status_t error = fTCPService->Send(fPort, fRemoteAddress, fRemotePort,
packet->SequenceNumber(), fAcknowledgeNumber, packet->Flags(),
WindowSize(), &buffer);
if (error != B_OK)
return error;
if (packet->SequenceNumber() == fSequenceNumber)
fSequenceNumber += packet->DataSize();
if (enqueue)
_EnqueueOutgoingPacket(packet);
return B_OK;
}
status_t
TCPSocket::_ResendQueue()
{
TRACE("resending queue\n");
for (TCPPacket* packet = fFirstSentPacket; packet != NULL;
packet = packet->Next()) {
ChainBuffer buffer((void*)packet->Data(), packet->DataSize());
status_t error = fTCPService->Send(fPort, fRemoteAddress, fRemotePort,
packet->SequenceNumber(), fAcknowledgeNumber, packet->Flags(),
WindowSize(), &buffer);
if (error != B_OK)
return error;
}
return B_OK;
}
void
TCPSocket::_EnqueueOutgoingPacket(TCPPacket* packet)
{
if (fLastSentPacket != NULL) {
fLastSentPacket->SetNext(packet);
fLastSentPacket = packet;
} else {
fFirstSentPacket = fLastSentPacket = packet;
}
}
#ifdef TRACE_TCP_QUEUE
inline void
TCPSocket::_DumpQueue()
{
TRACE_QUEUE("TCP: waiting for %lx (ack'ed %lx)\n", fNextSequence, fAcknowledgeNumber);
if (fFirstPacket == NULL)
TRACE_QUEUE("TCP: Queue is empty.\n");
else {
for (TCPPacket* packet = fFirstPacket; packet != NULL;
packet = packet->Next()) {
TRACE_QUEUE("TCP: Queue: %lx\n", packet->SequenceNumber());
}
}
if (fFirstSentPacket != NULL)
TRACE_QUEUE("TCP: Send queue is non-empty.\n");
else
TRACE_QUEUE("TCP: Send queue is empty.\n");
}
#endif
status_t
TCPSocket::_Ack()
{
TCPPacket* packet = new(nothrow) TCPPacket();
if (packet == NULL)
return B_NO_MEMORY;
status_t error = packet->SetTo(NULL, 0, fAddress, fPort, fRemoteAddress,
fRemotePort, fSequenceNumber, fAcknowledgeNumber, TCP_ACK);
if (error != B_OK) {
delete packet;
return error;
}
error = _Send(packet, false);
delete packet;
if (error != B_OK)
return error;
return B_OK;
}
status_t
TCPSocket::_WaitForState(TCPSocketState state, bigtime_t timeout)
{
if (fTCPService == NULL)
return B_NO_INIT;
bigtime_t startTime = system_time();
do {
fTCPService->ProcessIncomingPackets();
if (fState == state)
return B_OK;
} while (system_time() - startTime < timeout);
return timeout == 0 ? B_WOULD_BLOCK : B_TIMED_OUT;
}
TCPService::TCPService(IPService* ipService)
:
IPSubService(kTCPServiceName),
fIPService(ipService)
{
}
TCPService::~TCPService()
{
if (fIPService != NULL)
fIPService->UnregisterIPSubService(this);
}
status_t
TCPService::Init()
{
if (fIPService == NULL)
return B_BAD_VALUE;
if (!fIPService->RegisterIPSubService(this))
return B_NO_MEMORY;
return B_OK;
}
uint8
TCPService::IPProtocol() const
{
return IPPROTO_TCP;
}
void
TCPService::HandleIPPacket(IPService* ipService, ip_addr_t sourceIP,
ip_addr_t destinationIP, const void* data, size_t size)
{
TRACE("TCPService::HandleIPPacket(): source = %08lx, "
"destination = %08lx, %lu - %lu bytes\n", sourceIP, destinationIP,
size, sizeof(tcp_header));
if (data == NULL || size < sizeof(tcp_header))
return;
const tcp_header* header = (const tcp_header*)data;
uint16 chksum = _ChecksumData(data, size, sourceIP, destinationIP);
if (chksum != 0) {
TRACE_CHECKSUM("TCPService::HandleIPPacket(): invalid checksum "
"(%04x vs. %04x), padding %lu\n",
header->checksum, chksum, size % 2);
return;
}
uint16 source = ntohs(header->source);
uint16 destination = ntohs(header->destination);
uint32 sequenceNumber = ntohl(header->seqNumber);
uint32 ackedNumber = ntohl(header->ackNumber);
TRACE("\tsource = %u, dest = %u, seq = %lu, ack = %lu, dataOffset = %u, "
"flags %s %s %s %s\n", source, destination, sequenceNumber,
ackedNumber, header->dataOffset,
(header->flags & TCP_ACK) != 0 ? "ACK" : "",
(header->flags & TCP_SYN) != 0 ? "SYN" : "",
(header->flags & TCP_FIN) != 0 ? "FIN" : "",
(header->flags & TCP_RST) != 0 ? "RST" : "");
if (header->dataOffset > 5) {
uint8* option = (uint8*)data + sizeof(tcp_header);
while ((uint32*)option < (uint32*)data + header->dataOffset) {
uint8 optionKind = option[0];
if (optionKind == 0)
break;
uint8 optionLength = 1;
if (optionKind > 1) {
optionLength = option[1];
TRACE("\tTCP option kind %u, length %u\n",
optionKind, optionLength);
if (optionKind == 2)
TRACE("\tTCP MSS = %04hu\n", *(uint16_t*)&option[2]);
}
option += optionLength;
}
}
TCPSocket* socket = _FindSocket(destinationIP, destination);
if (socket == NULL) {
// TODO If SYN, answer with RST?
TRACE("TCPService::HandleIPPacket(): no socket\n");
return;
}
if ((header->flags & TCP_ACK) != 0) {
socket->Acknowledge(ackedNumber);
}
TCPPacket* packet = new(nothrow) TCPPacket();
if (packet == NULL)
return;
status_t error = packet->SetTo((uint32*)data + header->dataOffset,
size - header->dataOffset * 4, sourceIP, source, destinationIP,
destination, sequenceNumber, ackedNumber, header->flags);
if (error == B_OK)
socket->ProcessPacket(packet);
else
delete packet;
}
status_t
TCPService::Send(uint16 sourcePort, ip_addr_t destinationAddress,
uint16 destinationPort, uint32 sequenceNumber,
uint32 acknowledgmentNumber, uint8 flags, uint16 windowSize,
ChainBuffer* buffer)
{
TRACE("TCPService::Send(): seq = %lu, ack = %lu\n",
sequenceNumber, acknowledgmentNumber);
if (fIPService == NULL)
return B_NO_INIT;
if (buffer == NULL)
return B_BAD_VALUE;
tcp_header header;
ChainBuffer headerBuffer(&header, sizeof(header), buffer);
memset(&header, 0, sizeof(header));
header.source = htons(sourcePort);
header.destination = htons(destinationPort);
header.seqNumber = htonl(sequenceNumber);
header.ackNumber = htonl(acknowledgmentNumber);
header.dataOffset = 5;
header.flags = flags;
header.window = htons(windowSize);
header.checksum = 0;
header.checksum = htons(_ChecksumBuffer(&headerBuffer,
fIPService->IPAddress(), destinationAddress,
headerBuffer.TotalSize()));
return fIPService->Send(destinationAddress, IPPROTO_TCP, &headerBuffer);
}
void
TCPService::ProcessIncomingPackets()
{
if (fIPService != NULL)
fIPService->ProcessIncomingPackets();
}
status_t
TCPService::BindSocket(TCPSocket* socket)
{
if (socket == NULL)
return B_BAD_VALUE;
if (_FindSocket(socket->Address(), socket->Port()) != NULL)
return EADDRINUSE;
return fSockets.Add(socket);
}
void
TCPService::UnbindSocket(TCPSocket* socket)
{
fSockets.Remove(socket);
}
uint16
TCPService::_ChecksumBuffer(ChainBuffer* buffer, ip_addr_t source,
ip_addr_t destination, uint16 length)
{
struct pseudo_header {
ip_addr_t source;
ip_addr_t destination;
uint8 pad;
uint8 protocol;
uint16 length;
} __attribute__ ((__packed__));
pseudo_header header = {
htonl(source),
htonl(destination),
0,
IPPROTO_TCP,
htons(length)
};
ChainBuffer headerBuffer(&header, sizeof(header), buffer);
uint16 checksum = ip_checksum(&headerBuffer);
headerBuffer.DetachNext();
return checksum;
}
uint16
TCPService::_ChecksumData(const void* data, uint16 length, ip_addr_t source,
ip_addr_t destination)
{
ChainBuffer buffer((void*)data, length);
return _ChecksumBuffer(&buffer, source, destination, length);
}
TCPSocket*
TCPService::_FindSocket(ip_addr_t address, uint16 port)
{
for (int i = 0; i < fSockets.Count(); i++) {
TCPSocket* socket = fSockets.ElementAt(i);
// TODO Remove socket->Address() INADDR_ANY check once the socket is
// aware of both its IP addresses (local one is INADDR_ANY for now).
if ((address == INADDR_ANY || socket->Address() == INADDR_ANY
|| socket->Address() == address)
&& socket->Port() == port) {
return socket;
}
}
return NULL;
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fRemoteAddress, fRemotePort, fAcknowledgeNumber, fNextSequence.