fixed a long standing binding issue with spawned sockets and TCP. also fixed bind() address checking rules.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20866 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Hugo Santos 2007-04-27 15:49:00 +00:00
parent 342444bc95
commit 9cd3b980fd
4 changed files with 29 additions and 34 deletions

View File

@ -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);

View File

@ -71,6 +71,7 @@ class EndpointManager : public DoublyLinkedListLinkImpl<EndpointManager> {
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,

View File

@ -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;

View File

@ -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;