diff --git a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp index ca191021aa..d02dd2bd7d 100644 --- a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp @@ -257,11 +257,21 @@ EndpointManager::Bind(TCPEndpoint *endpoint, const sockaddr *address) status_t -EndpointManager::_BindToAddress(TCPEndpoint *endpoint, const sockaddr *address) +EndpointManager::BindChild(TCPEndpoint *endpoint) +{ + BenaphoreLocker _(fLock); + return _Bind(endpoint, *endpoint->LocalAddress()); +} + + +status_t +EndpointManager::_BindToAddress(TCPEndpoint *endpoint, const sockaddr *_address) { TRACE(("EndpointManager::BindToAddress(%p)\n", endpoint)); - uint16 port = AddressModule()->get_port(address); + ConstSocketAddress address(AddressModule(), _address); + + uint16 port = address.Port(); // TODO this check follows very typical UNIX semantics // and generally should be improved. @@ -270,33 +280,20 @@ EndpointManager::_BindToAddress(TCPEndpoint *endpoint, const sockaddr *address) EndpointTable::Iterator portUsers = fEndpointHash.Lookup(port); - // If there is already an endpoint bound to that port, SO_REUSEADDR has to be - // specified by the new endpoint to be allowed to bind to that same port. - // Alternatively, all endpoints must have the SO_REUSEPORT option set. - if (portUsers.HasNext()) { + while (portUsers.HasNext()) { TCPEndpoint *user = portUsers.Next(); - if ((endpoint->socket->options & SO_REUSEADDR) == 0 - && ((endpoint->socket->options & SO_REUSEPORT) == 0 - || (user->socket->options & SO_REUSEPORT) == 0)) - return EADDRINUSE; - - do { - // check if this endpoint binds to a wildcard address - if (user->LocalAddress().IsEmpty(false)) { - // you cannot specialize a wildcard endpoint - you have to open - // the wildcard endpoint last - return B_PERMISSION_DENIED; - } - - if (portUsers.HasNext()) - user = portUsers.Next(); - else - break; - } while (true); + if (user->LocalAddress().IsEmpty(false) + || address.EqualTo(*user->LocalAddress(), false)) { + if ((endpoint->socket->options & SO_REUSEADDR) == 0) + return EADDRINUSE; + // TODO lock endpoint before retriving state? + if (user->State() != TIME_WAIT && user->State() != CLOSED) + return EADDRINUSE; + } } - return _Bind(endpoint, address); + return _Bind(endpoint, *address); } @@ -368,10 +365,8 @@ EndpointManager::Unbind(TCPEndpoint *endpoint) BenaphoreLocker _(fLock); - if (!fEndpointHash.Remove(endpoint)) { - if (!endpoint->fSpawned) - panic("bound endpoint %p not in hash!", endpoint); - } + if (!fEndpointHash.Remove(endpoint)) + panic("bound endpoint %p not in hash!", endpoint); fConnectionHash.Remove(endpoint); diff --git a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.h b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.h index 6b485d6e4c..152d8d359a 100644 --- a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.h +++ b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.h @@ -71,6 +71,7 @@ class EndpointManager : public DoublyLinkedListLinkImpl { status_t SetPassive(TCPEndpoint *endpoint); status_t Bind(TCPEndpoint *endpoint, const sockaddr *address); + status_t BindChild(TCPEndpoint *endpoint); status_t Unbind(TCPEndpoint *endpoint); status_t ReplyWithReset(tcp_segment_header &segment, diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp index bf19fee9b5..c66ed86a6c 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp @@ -241,8 +241,7 @@ TCPEndpoint::TCPEndpoint(net_socket *socket) fSlowStartThreshold(0), fState(CLOSED), fFlags(FLAG_OPTION_WINDOW_SCALE | FLAG_OPTION_TIMESTAMP), - fError(B_OK), - fSpawned(false) + fError(B_OK) { //gStackModule->init_timer(&fTimer, _TimeWait, this); @@ -823,7 +822,6 @@ TCPEndpoint::Spawn(TCPEndpoint *parent, tcp_segment_header &segment, fState = SYNCHRONIZE_RECEIVED; fManager = parent->fManager; - fSpawned = true; LocalAddress().SetTo(&buffer->destination); PeerAddress().SetTo(&buffer->source); @@ -831,6 +829,9 @@ TCPEndpoint::Spawn(TCPEndpoint *parent, tcp_segment_header &segment, TRACE("Spawn()"); // TODO: proper error handling! + if (fManager->BindChild(this) < B_OK) + return DROP; + if (_PrepareSendPath(*PeerAddress()) < B_OK) return DROP; diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h index bf3bca9706..e60a372857 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h +++ b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h @@ -169,8 +169,6 @@ class TCPEndpoint : public net_protocol, public ProtocolSocket { uint32 fFlags; status_t fError; - bool fSpawned; - // timer net_timer fRetransmitTimer; net_timer fPersistTimer;