/*
* Copyright 2011, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2016, Rene Gollent, rene@gollent.com.
* Distributed under the terms of the MIT License.
*/
#include <AbstractSocket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/poll.h>
#include <sys/time.h>
//#define TRACE_SOCKET
#ifdef TRACE_SOCKET
# define TRACE(x...) printf(x)
#else
# define TRACE(x...) ;
#endif
BAbstractSocket::BAbstractSocket()
:
fInitStatus(B_NO_INIT),
fSocket(-1),
fIsBound(false),
fIsConnected(false),
fIsListening(false)
{
}
BAbstractSocket::BAbstractSocket(const BAbstractSocket& other)
:
fInitStatus(other.fInitStatus),
fLocal(other.fLocal),
fPeer(other.fPeer),
fIsConnected(other.fIsConnected),
fIsListening(other.fIsListening)
{
fSocket = dup(other.fSocket);
if (fSocket < 0)
fInitStatus = errno;
}
BAbstractSocket::~BAbstractSocket()
{
Disconnect();
}
status_t
BAbstractSocket::InitCheck() const
{
return fInitStatus;
}
bool
BAbstractSocket::IsBound() const
{
return fIsBound;
}
bool
BAbstractSocket::IsListening() const
{
return fIsListening;
}
bool
BAbstractSocket::IsConnected() const
{
return fIsConnected;
}
status_t
BAbstractSocket::Listen(int backlog)
{
if (!fIsBound)
return B_NO_INIT;
if (listen(Socket(), backlog) != 0)
return fInitStatus = errno;
fIsListening = true;
return B_OK;
}
void
BAbstractSocket::Disconnect()
{
if (fSocket < 0)
return;
TRACE("%p: BAbstractSocket::Disconnect()\n", this);
close(fSocket);
fSocket = -1;
fIsConnected = false;
fIsBound = false;
}
status_t
BAbstractSocket::SetTimeout(bigtime_t timeout)
{
if (timeout < 0)
timeout = 0;
struct timeval tv;
tv.tv_sec = timeout / 1000000LL;
tv.tv_usec = timeout % 1000000LL;
if (setsockopt(fSocket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(timeval)) != 0
|| setsockopt(fSocket, SOL_SOCKET, SO_RCVTIMEO, &tv,
sizeof(timeval)) != 0) {
return errno;
}
return B_OK;
}
bigtime_t
BAbstractSocket::Timeout() const
{
struct timeval tv;
socklen_t size = sizeof(tv);
if (getsockopt(fSocket, SOL_SOCKET, SO_SNDTIMEO, &tv, &size) != 0)
return B_INFINITE_TIMEOUT;
return tv.tv_sec * 1000000LL + tv.tv_usec;
}
const BNetworkAddress&
BAbstractSocket::Local() const
{
return fLocal;
}
const BNetworkAddress&
BAbstractSocket::Peer() const
{
return fPeer;
}
size_t
BAbstractSocket::MaxTransmissionSize() const
{
return SSIZE_MAX;
}
status_t
BAbstractSocket::WaitForReadable(bigtime_t timeout) const
{
return _WaitFor(POLLIN, timeout);
}
status_t
BAbstractSocket::WaitForWritable(bigtime_t timeout) const
{
return _WaitFor(POLLOUT, timeout);
}
int
BAbstractSocket::Socket() const
{
return fSocket;
}
// #pragma mark - protected
status_t
BAbstractSocket::Bind(const BNetworkAddress& local, bool reuseAddr, int type)
{
fInitStatus = _OpenIfNeeded(local.Family(), type);
if (fInitStatus != B_OK)
return fInitStatus;
if (reuseAddr) {
int value = 1;
if (setsockopt(Socket(), SOL_SOCKET, SO_REUSEADDR, &value,
sizeof(value)) != 0) {
return fInitStatus = errno;
}
}
if (bind(fSocket, local, local.Length()) != 0)
return fInitStatus = errno;
fIsBound = true;
_UpdateLocalAddress();
return B_OK;
}
status_t
BAbstractSocket::Connect(const BNetworkAddress& peer, int type,
bigtime_t timeout)
{
Disconnect();
fInitStatus = _OpenIfNeeded(peer.Family(), type);
if (fInitStatus == B_OK)
fInitStatus = SetTimeout(timeout);
if (fInitStatus == B_OK && !IsBound()) {
BNetworkAddress local;
local.SetToWildcard(peer.Family());
fInitStatus = Bind(local, true);
}
if (fInitStatus != B_OK)
return fInitStatus;
BNetworkAddress normalized = peer;
if (connect(fSocket, normalized, normalized.Length()) != 0) {
TRACE("%p: connecting to %s: %s\n", this,
normalized.ToString().c_str(), strerror(errno));
return fInitStatus = errno;
}
fIsConnected = true;
fPeer = normalized;
_UpdateLocalAddress();
TRACE("%p: connected to %s (local %s)\n", this, peer.ToString().c_str(),
fLocal.ToString().c_str());
return fInitStatus = B_OK;
}
status_t
BAbstractSocket::AcceptNext(int& _acceptedSocket, BNetworkAddress& _peer)
{
sockaddr_storage source;
socklen_t sourceLength = sizeof(sockaddr_storage);
int fd = accept(fSocket, (sockaddr*)&source, &sourceLength);
if (fd < 0)
return fd;
_peer.SetTo(source);
_acceptedSocket = fd;
return B_OK;
}
// #pragma mark - private
status_t
BAbstractSocket::_OpenIfNeeded(int family, int type)
{
if (fSocket >= 0)
return B_OK;
fSocket = socket(family, type, 0);
if (fSocket < 0)
return errno;
TRACE("%p: socket opened FD %d\n", this, fSocket);
return B_OK;
}
status_t
BAbstractSocket::_UpdateLocalAddress()
{
socklen_t localLength = sizeof(sockaddr_storage);
if (getsockname(fSocket, fLocal, &localLength) != 0)
return errno;
return B_OK;
}
status_t
BAbstractSocket::_WaitFor(int flags, bigtime_t timeout) const
{
if (fInitStatus != B_OK)
return fInitStatus;
int millis = 0;
if (timeout == B_INFINITE_TIMEOUT)
millis = -1;
if (timeout > 0)
millis = timeout / 1000;
struct pollfd entry;
entry.fd = Socket();
entry.events = flags;
int result = poll(&entry, 1, millis);
if (result < 0)
return errno;
if (result == 0)
return millis > 0 ? B_TIMED_OUT : B_WOULD_BLOCK;
return B_OK;
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fIsBound.