#include <errno.h>
#ifdef HAIKU_TARGET_PLATFORM_BEOS
# include <socket.h>
#else
# include <unistd.h>
# include <netinet/in.h>
# include <sys/socket.h>
#endif
#include "Compatibility.h"
#include "DebugSupport.h"
#include "InsecureChannel.h"
#include "NetAddress.h"
#include "Utils.h"
static const int32 kMaxInterruptedTxCount = 20;
InsecureChannel::InsecureChannel(int socket)
: Channel(),
fSocket(socket),
fClosed(false)
{
if (fSocket >= 0) {
int dontBlock = 0;
setsockopt(fSocket, SOL_SOCKET, SO_NONBLOCK, &dontBlock, sizeof(int));
#ifndef HAIKU_TARGET_PLATFORM_BEOS
int txLowWater = 1;
setsockopt(fSocket, SOL_SOCKET, SO_SNDLOWAT, &txLowWater, sizeof(int));
setsockopt(fSocket, SOL_SOCKET, SO_RCVLOWAT, &txLowWater, sizeof(int));
#endif
}
}
InsecureChannel::~InsecureChannel()
{
Close();
}
void
InsecureChannel::Close()
{
fClosed = true;
safe_closesocket(fSocket);
}
status_t
InsecureChannel::Send(const void* _buffer, int32 size)
{
if (size == 0)
return B_OK;
if (!_buffer || size < 0)
return B_BAD_VALUE;
const uint8* buffer = static_cast<const uint8*>(_buffer);
int32 remainingRetries = kMaxInterruptedTxCount;
while (size > 0) {
int32 bytesSent = send(fSocket, buffer, size, 0);
if (bytesSent < 0) {
status_t error = errno;
if (fClosed || errno != B_INTERRUPTED || --remainingRetries < 0)
{
if (!fClosed) {
PRINT(("InsecureChannel::Send(): B_INTERRUPTED\n"));
}
return error;
}
bytesSent = 0;
} else if (bytesSent == 0) {
PRINT(("Connection::Send(): sent 0 bytes. Assuming "
"that the remote peer closed the connection\n"));
return B_ERROR;
} else {
size -= bytesSent;
buffer += bytesSent;
remainingRetries = kMaxInterruptedTxCount;
}
}
return B_OK;
}
status_t
InsecureChannel::Receive(void* _buffer, int32 size)
{
if (size == 0)
return B_OK;
if (!_buffer || size < 0)
return B_BAD_VALUE;
uint8* buffer = static_cast<uint8*>(_buffer);
int32 remainingRetries = kMaxInterruptedTxCount;
while (size > 0) {
int32 bytesRead = recv(fSocket, buffer, size, 0);
if (bytesRead < 0) {
status_t error = errno;
if (fClosed || error != B_INTERRUPTED || --remainingRetries < 0)
{
if (!fClosed) {
PRINT(("InsecureChannel::Receive(): B_INTERRUPTED\n"));
}
return error;
}
} else if (bytesRead == 0) {
PRINT(("Connection::Receive(): received 0 bytes. Assuming "
"that the remote peer closed the connection\n"));
return B_ERROR;
} else {
size -= bytesRead;
buffer += bytesRead;
remainingRetries = kMaxInterruptedTxCount;
}
}
return B_OK;
}
status_t
InsecureChannel::GetPeerAddress(NetAddress *address) const
{
if (!address)
return B_BAD_VALUE;
sockaddr_in addr;
socklen_t size = sizeof(sockaddr_in);
if (getpeername(fSocket, (sockaddr*)&addr, &size) < 0)
return errno;
if (addr.sin_family != AF_INET)
return B_BAD_VALUE;
address->SetAddress(addr);
return B_OK;
}