#ifdef UNIX
#include <unistd.h>
#include <stdlib.h>
#ifdef HAVE_IPV6
#ifdef MACOSX
#define __APPLE_USE_RFC_3542 1
#endif #include <netinet/in.h>
#endif #include <sys/ioctl.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <ifaddrs.h>
#include <errno.h>
#include <fcntl.h>
#ifndef SIOCGIFHWADDR
#if defined(SOLARIS) || defined(IRIX)
#include <sys/sockio.h>
#include <netdb.h>
#include <sys/dlpi.h>
#include <stropts.h>
#else
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <ifaddrs.h>
#endif #endif
#endif
#ifdef WIN32
#include <Winsock2.h>
#include <Ws2def.h>
#include <WS2tcpip.h>
#include <Mswsock.h>
#include <Windows.h>
#include <Iphlpapi.h>
#include <Iptypes.h>
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
typedef UINT32 in_addr_t;
#endif
#ifndef IPV6_FLOWINFO_SEND
#define IPV6_FLOWINFO_SEND 33
#endif
#include <stdio.h>
#include <string.h>
#include "protoSocket.h"
#include "protoNet.h"
#include "protoDebug.h"
#ifdef HAVE_NETSEC
#include <net/security.h>
extern void* netsec_request;
extern int netsec_requestlen;
#endif
#ifdef SOCKLEN_T
#define socklen_t SOCKLEN_T
#else
#ifdef WIN32
#endif #endif
#include "protoDispatcher.h"
#ifdef WIN32
const ProtoSocket::Handle ProtoSocket::INVALID_HANDLE = INVALID_SOCKET;
LPFN_WSARECVMSG ProtoSocket::WSARecvMsg = NULL;
#else
const ProtoSocket::Handle ProtoSocket::INVALID_HANDLE = -1;
#endif
ProtoSocket::IPv6SupportStatus ProtoSocket::ipv6_support_status = IPV6_UNKNOWN;
ProtoSocket::ProtoSocket(ProtoSocket::Protocol theProtocol)
: domain(IPv4), protocol(theProtocol), raw_protocol(RAW), state(CLOSED),
handle(INVALID_HANDLE), port(-1), tos(0), ecn_capable(false), ip_recvdstaddr(false),
#ifdef HAVE_IPV6
flow_label(0),
#endif notifier(NULL), notify_output(false), notify_input(true), notify_exception(false),
#ifdef WIN32
input_event_handle(NULL), output_event_handle(NULL),
input_ready(false), output_ready(false), closing(false),
#endif listener(NULL), user_data(NULL)
{
}
ProtoSocket::~ProtoSocket()
{
Close();
if (NULL != listener)
{
delete listener;
listener = NULL;
}
}
bool ProtoSocket::SetBlocking(bool blocking)
{
#ifdef UNIX
if (blocking)
{
if(-1 == fcntl(handle, F_SETFL, fcntl(handle, F_GETFL, 0) & ~O_NONBLOCK))
{
PLOG(PL_ERROR, "ProtoSocket::SetBlocking() fcntl(F_SETFL(~O_NONBLOCK)) error: %s\n", GetErrorString());
return false;
}
}
else
{
if(-1 == fcntl(handle, F_SETFL, fcntl(handle, F_GETFL, 0) | O_NONBLOCK))
{
PLOG(PL_ERROR, "ProtoSocket::SetBlocking() fcntl(F_SETFL(O_NONBLOCK)) error: %s\n", GetErrorString());
return false;
}
}
#endif return true; }
bool ProtoSocket::StartInputNotification()
{
if (!notify_input)
{
notify_input = true;
notify_input = UpdateNotification();
}
return notify_input;
}
void ProtoSocket::StopInputNotification()
{
if (notify_input)
{
notify_input = false;
UpdateNotification();
}
}
bool ProtoSocket::StartOutputNotification()
{
if (!notify_output)
{
notify_output = true;
notify_output = UpdateNotification();
}
return notify_output;
}
void ProtoSocket::StopOutputNotification()
{
if (notify_output)
{
notify_output = false;
UpdateNotification();
}
}
bool ProtoSocket::StartExceptionNotification()
{
notify_exception = true;
notify_exception = UpdateNotification();
return notify_exception;
}
void ProtoSocket::StopExceptionNotification()
{
notify_exception = false;
UpdateNotification();
}
bool ProtoSocket::SetNotifier(ProtoSocket::Notifier* theNotifier)
{
if (notifier != theNotifier)
{
if (IsOpen())
{
if (NULL != notifier)
{
notifier->UpdateSocketNotification(*this, 0);
if (NULL == theNotifier)
{
if(!SetBlocking(true))
PLOG(PL_ERROR, "ProtoSocket::SetNotifier() SetBlocking(true) error\n", GetErrorString());
}
}
else
{
if(!SetBlocking(false))
{
PLOG(PL_ERROR, "ProtoSocket::SetNotifier() SetBlocking(false) error\n", GetErrorString());
return false;
}
#ifdef WIN32
input_ready = false;
output_ready = true;
#endif }
notifier = theNotifier;
if (!UpdateNotification())
{
notifier = NULL;
return false;
}
}
else
{
notifier = theNotifier;
}
}
return true;
}
ProtoAddress::Type ProtoSocket::GetAddressType()
{
switch (domain)
{
case LOCAL:
return ProtoAddress::INVALID;
case IPv4:
return ProtoAddress::IPv4;
#ifdef HAVE_IPV6
case IPv6:
return ProtoAddress::IPv6;
#endif #ifdef SIMULATE
case SIM:
return ProtoAddress::SIM;
#endif default:
return ProtoAddress::INVALID;
}
}
bool ProtoSocket::Open(UINT16 thePort,
ProtoAddress::Type addrType,
bool bindOnOpen)
{
if (IsOpen()) Close();
#ifdef HAVE_IPV6
if(addrType == ProtoAddress::IPv6)
{
if (!HostIsIPv6Capable())
{
PLOG(PL_ERROR,"ProtoSocket::Open() system not IPv6 capable?\n");
return false;
}
domain = IPv6;
}
else
#endif {
domain = IPv4;
}
int socketType = 0;
switch (protocol)
{
case UDP:
socketType = SOCK_DGRAM;
break;
case TCP:
socketType = SOCK_STREAM;
break;
case RAW:
socketType = SOCK_RAW;
break;
default:
PLOG(PL_ERROR,"ProtoSocket::Open() error: Unsupported protocol\n");
return false;
}
#ifdef WIN32
#ifdef HAVE_IPV6
int family = (IPv6 == domain) ? AF_INET6: AF_INET;
#else
int family = AF_INET;
#endif if (!ProtoAddress::Win32Startup())
{
PLOG(PL_ERROR, "ProtoSocket::Open() WSAStartup() error: %s\n", GetErrorString());
return false;
}
WSAPROTOCOL_INFO* infoPtr = NULL;
WSAPROTOCOL_INFO* protocolInfo = NULL;
DWORD buflen = 0;
WSAEnumProtocols(NULL, protocolInfo, &buflen);
if (buflen)
{
int protocolType;
switch (protocol)
{
case UDP:
protocolType = IPPROTO_UDP;
break;
case TCP:
protocolType = IPPROTO_TCP;
break;
case RAW:
switch (raw_protocol)
{
case RAW:
protocolType = IPPROTO_RAW;
break;
case UDP:
protocolType = IPPROTO_UDP;
break;
case TCP:
protocolType = IPPROTO_TCP;
break;
default:
protocolType = IPPROTO_RAW;
}
break;
}
if ((protocolInfo = (WSAPROTOCOL_INFO*) new char[buflen]))
{
int count = WSAEnumProtocols(NULL, protocolInfo, &buflen);
if (SOCKET_ERROR != count)
{
for (int i = 0; i < count; i++)
{
switch (protocol)
{
case UDP:
if ((IPPROTO_UDP == protocolInfo[i].iProtocol) &&
(0 != (XP1_SUPPORT_MULTIPOINT & protocolInfo[i].dwServiceFlags1)))
{
if ((NULL == infoPtr) ||
(0 == (XP1_QOS_SUPPORTED & protocolInfo[i].dwServiceFlags1)))
{
infoPtr = protocolInfo + i;
}
}
break;
case TCP:
if (IPPROTO_TCP == protocolInfo[i].iProtocol)
{
if ((NULL == infoPtr) ||
(0 == (XP1_QOS_SUPPORTED & protocolInfo[i].dwServiceFlags1)))
{
infoPtr = protocolInfo + i;
}
}
break;
default:
break;
}
} }
else
{
PLOG(PL_ERROR, "ProtoSocket: WSAEnumProtocols() error2!\n");
delete[] protocolInfo;
ProtoAddress::Win32Cleanup();
return false;
}
}
else
{
PLOG(PL_ERROR, "ProtoSocket: Error allocating memory!\n");
ProtoAddress::Win32Cleanup();
return false;
}
}
else
{
PLOG(PL_WARN, "ProtoSocket::Open() WSAEnumProtocols() error1!\n");
}
DWORD flags = WSA_FLAG_OVERLAPPED;
#ifndef _WIN32_WCE
if (UDP == protocol) flags |= (WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF);
#endif handle = WSASocket(family, socketType, 0, infoPtr, 0, flags);
if (NULL != protocolInfo) delete[] protocolInfo;
if (INVALID_HANDLE == handle)
{
PLOG(PL_ERROR, "ProtoSocket::Open() WSASocket() error: %s\n", GetErrorString());
ProtoAddress::Win32Cleanup();
return false;
}
if (NULL == (input_event_handle = WSACreateEvent()))
{
PLOG(PL_ERROR, "ProtoSocket::Open() WSACreateEvent() error: %s\n", GetErrorString());
Close();
return false;
}
input_ready = false;
output_ready = true;
#else
#ifdef HAVE_IPV6
int family = (IPv6 == domain) ? AF_INET6: AF_INET;
#else
int family = AF_INET;
#endif
int socketProtocol = 0; if (SOCK_RAW == socketType)
{
switch (raw_protocol)
{
case RAW:
socketProtocol = IPPROTO_RAW;
break;
case UDP:
socketProtocol = IPPROTO_UDP;
break;
case TCP:
socketProtocol = IPPROTO_TCP;
break;
default:
socketProtocol = IPPROTO_RAW;
}
}
if (INVALID_HANDLE == (handle = socket(family, socketType, socketProtocol)))
{
PLOG(PL_ERROR, "ProtoSocket::Open() socket() error: %s\n", GetErrorString());
return false;
}
#endif state = IDLE;
#ifdef NETSEC
if (net_security_setrequest(handle, 0, netsec_request, netsec_requestlen))
{
PLOG(PL_ERROR, "ProtoSocket::Open() net_security_setrequest() error: %s\n",
GetErrorString());
Close();
return false;
}
#endif
#ifdef UNIX
if(-1 == fcntl(handle, F_SETFD, FD_CLOEXEC))
PLOG(PL_ERROR, "ProtoSocket::Open() fcntl(FD_CLOEXEC) warning: %s\n", GetErrorString());
if (notifier)
{
if(-1 == fcntl(handle, F_SETFL, fcntl(handle, F_GETFL, 0) | O_NONBLOCK))
{
PLOG(PL_ERROR, "ProtoSocket::Open() fcntl(F_SETFL(O_NONBLOCK)) error: %s\n",
GetErrorString());
Close();
return false;
}
}
#endif if (bindOnOpen)
{
if (!Bind(thePort))
{
Close();
return false;
}
}
else
{
port = -1;
if (!UpdateNotification())
{
PLOG(PL_ERROR, "ProtoSocket::Open() error installing async notification\n");
Close();
return false;
}
}
if ((0 != tos) || (ecn_capable)) SetTOS(tos);
#ifdef WIN32
closing = false;
#endif ip_recvdstaddr = false; return true;
}
bool ProtoSocket::SetRawProtocol(Protocol theProtocol)
{
bool wasOpen = false;
UINT16 portSave = 0;
if (IsOpen())
{
portSave = GetPort();
Close();
wasOpen = true;
}
protocol = RAW;
raw_protocol = theProtocol;
if (wasOpen)
return Open(portSave);
else
return true;
}
bool ProtoSocket::UpdateNotification()
{
if (NULL != notifier)
{
if (IsOpen() && !SetBlocking(false))
{
PLOG(PL_ERROR, "ProtoSocket::UpdateNotification() SetBlocking() error\n");
return false;
}
int notifyFlags = NOTIFY_NONE;
if (NULL != listener)
{
switch (protocol)
{
case UDP:
case RAW:
switch (state)
{
case CLOSED:
break;
default:
if (notify_input && IsBound())
notifyFlags = NOTIFY_INPUT;
else
notifyFlags = NOTIFY_NONE;
if (notify_output)
notifyFlags |= NOTIFY_OUTPUT;
if (IsOpen() && notify_exception)
notifyFlags |= NOTIFY_EXCEPTION;
break;
}
break;
case TCP:
switch(state)
{
case CLOSED:
case IDLE:
break;
case CONNECTING:
notifyFlags = NOTIFY_OUTPUT;
break;
case LISTENING:
notifyFlags = NOTIFY_INPUT;
break;
case CONNECTED:
if (notify_input)
notifyFlags = NOTIFY_INPUT;
if (notify_output)
notifyFlags |= NOTIFY_OUTPUT;
break;
} break;
case ZMQ:
switch (state)
{
case CONNECTED:
if (notify_input)
notifyFlags = NOTIFY_INPUT;
break;
default:
break;
}
break;
default:
PLOG(PL_ERROR,"ProtoSocket::UpdateNotification Error: Unsupported protocol: %d.\n", protocol);
break;
} } return notifier->UpdateSocketNotification(*this, notifyFlags);
}
else
{
return true;
}
}
void ProtoSocket::OnNotify(NotifyFlag theFlag)
{
ProtoSocket::Event event = INVALID_EVENT;
if (NOTIFY_INPUT == theFlag)
{
switch (state)
{
case CLOSED:
break;
case IDLE:
event = RECV;
break;
case CONNECTING:
break;
case LISTENING:
event = ACCEPT;
break;
case CONNECTED:
event = RECV;
break;
}
}
else if (NOTIFY_OUTPUT == theFlag)
{
switch (state)
{
case CLOSED:
break;
case IDLE:
event = SEND;
break;
case CONNECTING:
{
#ifdef WIN32
event = CONNECT;
state = CONNECTED;
UpdateNotification();
#else
int err;
socklen_t errsize = sizeof(err);
if (getsockopt(handle, SOL_SOCKET, SO_ERROR, (char*)&err, &errsize))
{
PLOG(PL_ERROR, "ProtoSocket::OnNotify() getsockopt() error: %s\n", GetErrorString());
}
else if (0 != err)
{
PLOG(PL_DEBUG, "ProtoSocket::OnNotify() getsockopt() error: %s\n", GetErrorString());
Disconnect();
event = ERROR_; }
else
{
event = CONNECT;
state = CONNECTED;
UpdateNotification();
}
#endif break;
}
case LISTENING:
break;
case CONNECTED:
event = SEND;
break;
}
}
else if (NOTIFY_EXCEPTION == theFlag)
{
event = EXCEPTION;
}
else if (NOTIFY_ERROR == theFlag)
{
switch(state)
{
case CONNECTING:
case CONNECTED:
Disconnect();
default:
event = ProtoSocket::ERROR_;
break;
}
}
else {
switch(state)
{
case CONNECTING:
case CONNECTED:
Disconnect();
event = DISCONNECT;
break;
default:
break;
}
}
ASSERT(INVALID_EVENT != event);
if (listener) listener->on_event(*this, event);
}
bool ProtoSocket::Bind(UINT16 thePort, const ProtoAddress* localAddress)
{
if (IsBound()) Close();
if (IsOpen() && (NULL != localAddress) && (GetAddressType() != localAddress->GetType()))
{
Close(); }
if (!IsOpen())
{
ProtoAddress::Type addrType = localAddress ? localAddress->GetType() : ProtoAddress::IPv4;
if(!Open(thePort, addrType, false))
{
PLOG(PL_ERROR,"ProtoSocket::Bind() error opening socket on port %d\n",thePort);
return false;
}
}
#ifdef HAVE_IPV6
struct sockaddr_storage socketAddr;
socklen_t addrSize;
if (IPv6 == domain)
{
addrSize = sizeof(struct sockaddr_in6);
memset((char*)&socketAddr, 0, sizeof(struct sockaddr_in6));
((struct sockaddr_in6*)&socketAddr)->sin6_family = AF_INET6;
((struct sockaddr_in6*)&socketAddr)->sin6_port = htons(thePort);
((struct sockaddr_in6*)&socketAddr)->sin6_flowinfo = 0;
if (NULL != localAddress)
{
((struct sockaddr_in6*)&socketAddr)->sin6_addr =
((const struct sockaddr_in6*)(&localAddress->GetSockAddrStorage()))->sin6_addr;
}
else
{
((struct sockaddr_in6*)&socketAddr)->sin6_addr = in6addr_any;
}
}
else
{
addrSize = sizeof(struct sockaddr_in);
memset((char*)&socketAddr, 0, sizeof(struct sockaddr_in));
((struct sockaddr_in*)&socketAddr)->sin_family = AF_INET;
((struct sockaddr_in*)&socketAddr)->sin_port = htons(thePort);
if (NULL != localAddress)
{
((struct sockaddr_in*)&socketAddr)->sin_addr =
((const struct sockaddr_in*)(&localAddress->GetSockAddrStorage()))->sin_addr;
}
else
{
struct in_addr inAddr;
inAddr.s_addr = htonl(INADDR_ANY);
((struct sockaddr_in*)&socketAddr)->sin_addr = inAddr;
}
}
#else
struct sockaddr socketAddr;
socklen_t addrSize = sizeof(struct sockaddr_in);
memset((char*)&socketAddr, 0, sizeof(struct sockaddr_in));
((struct sockaddr_in*)&socketAddr)->sin_family = AF_INET;
((struct sockaddr_in*)&socketAddr)->sin_port = htons(thePort);
if (NULL != localAddress)
{
((struct sockaddr_in*)&socketAddr)->sin_addr =
((struct sockaddr_in*)(&localAddress->GetSockAddr()))->sin_addr;
}
else
{
struct in_addr inAddr;
inAddr.s_addr = htonl(INADDR_ANY);
((struct sockaddr_in*)&socketAddr)->sin_addr = inAddr;
}
#endif
#ifdef UNIX
#ifdef HAVE_IPV6
if ((IPv6 == domain) && (0 != flow_label))
((struct sockaddr_in6*)&socketAddr)->sin6_flowinfo = flow_label;
#endif #endif
#ifdef HAVE_IPV6
#ifdef WIN32
OSVERSIONINFO osvi;
BOOL osVistaOrLater;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
osVistaOrLater = osvi.dwMajorVersion > 5;
if (osVistaOrLater)
{
int ipv6only = 0;
if (setsockopt(handle, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only)) < 0)
{
PLOG(PL_ERROR, "ProtoSocket::Bind() setsockopt(!IPV6_V6ONLY) error: %s\n", GetErrorString());
return false;
}
}
#endif #endif if (bind(handle, (struct sockaddr*)&socketAddr, addrSize) < 0)
{
PLOG(PL_ERROR, "ProtoSocket::Bind(%hu) bind() error: %s\n", thePort, GetErrorString());
return false;
}
socklen_t sockLen = addrSize;
if (getsockname(handle, (struct sockaddr*)&socketAddr, &sockLen) < 0)
{
PLOG(PL_ERROR, "ProtoSocket::Bind() getsockname() error: %s\n", GetErrorString());
return false;
}
source_addr.SetSockAddr((struct sockaddr&)socketAddr);
switch(((struct sockaddr*)&socketAddr)->sa_family)
{
case AF_INET:
source_addr.SetSockAddr((struct sockaddr&)socketAddr);
port = ntohs(((struct sockaddr_in*)&socketAddr)->sin_port);
break;
#ifdef HAVE_IPV6
case AF_INET6:
source_addr.SetSockAddr((struct sockaddr&)socketAddr);
port = ntohs(((struct sockaddr_in6*)&socketAddr)->sin6_port);
break;
#endif default:
PLOG(PL_ERROR, "ProtoSocket::Bind() error: getsockname() returned unknown address type\n");
return false;
}
#ifdef WIN32
input_ready = false;
output_ready = true;
#endif return UpdateNotification();
}
bool ProtoSocket::Shutdown()
{
#ifdef WIN32
if (TCP == protocol)
#else
if (IsConnected() && TCP == protocol)
#endif {
bool notifyOutput = notify_output;
if (notifyOutput)
{
notify_output = false;
UpdateNotification();
}
#ifdef WIN32
if (SOCKET_ERROR == shutdown(handle, SD_SEND))
#else
if (0 != shutdown(handle, SHUT_WR))
#endif {
if (notifyOutput)
{
notify_output = true;
UpdateNotification();
}
PLOG(PL_ERROR, "ProtoSocket::Shutdown() error: %s\n", GetErrorString());
return false;
}
else
{
return true;
}
}
else
{
PLOG(PL_ERROR, "ProtoSocket::Shutdown() error: socket not connected\n");
return false;
}
}
void ProtoSocket::Close()
{
if (IsOpen())
{
if (IsConnected()) Disconnect();
state = CLOSED;
UpdateNotification();
#ifdef WIN32
if (NULL != input_event_handle)
{
WSACloseEvent(input_event_handle);
input_event_handle = NULL;
}
#endif if (INVALID_HANDLE != handle)
{
#ifdef WIN32
if (SOCKET_ERROR == closesocket(handle))
PLOG(PL_WARN, "ProtoSocket::Close() warning: closesocket() error: %s\n", GetErrorString());
ProtoAddress::Win32Cleanup();
input_ready = output_ready = false;
#else
close(handle);
#endif handle = INVALID_HANDLE;
}
port = -1;
}
}
bool ProtoSocket::Connect(const ProtoAddress& theAddress)
{
if (IsConnected()) Disconnect();
if (!IsOpen() && !Open(0, theAddress.GetType()))
{
PLOG(PL_ERROR, "ProtoSocket::Connect() error opening socket!\n");
return false;
}
#ifdef HAVE_IPV6
socklen_t addrSize = (IPv6 == domain) ? sizeof(sockaddr_storage) :
sizeof(struct sockaddr_in);
#else
socklen_t addrSize = sizeof(struct sockaddr_in);
#endif #ifdef WIN32
int result = connect(handle, &theAddress.GetSockAddr(), addrSize);
if (SOCKET_ERROR == result)
{
if (WSAEWOULDBLOCK != WSAGetLastError())
{
PLOG(PL_ERROR, "ProtoSocket::Connect() WSAConnect() error: (%s)\n", GetErrorString());
return false;
}
output_ready = false; state = CONNECTING;
}
#else
#ifdef HAVE_IPV6
if (flow_label && (ProtoAddress::IPv6 == theAddress.GetType()))
((struct sockaddr_in6*)(&theAddress.GetSockAddrStorage()))->sin6_flowinfo = flow_label;
#endif int result = connect(handle, &theAddress.GetSockAddr(), addrSize);
if (0 != result)
{
if (EINPROGRESS != errno)
{
PLOG(PL_ERROR, "ProtoSocket::Connect() connect() error: %s\n", GetErrorString());
return false;
}
state = CONNECTING;
}
#endif else
{
state = CONNECTED;
}
if (!UpdateNotification())
{
PLOG(PL_ERROR, "ProtoSocket::Connect() error updating notification\n");
state = IDLE;
UpdateNotification();
return false;
}
#ifdef HAVE_IPV6
struct sockaddr_in6 socketAddr;
#else
struct sockaddr socketAddr;
#endif socklen_t addrLen = sizeof(socketAddr);
if (getsockname(handle, (struct sockaddr*)&socketAddr, &addrLen) < 0)
{
PLOG(PL_ERROR, "ProtoSocket::Connect() getsockname() error: %s\n", GetErrorString());
source_addr.Invalidate();
}
else
{
switch(((struct sockaddr*)&socketAddr)->sa_family)
{
case AF_INET:
source_addr.SetSockAddr((struct sockaddr&)socketAddr);
port = ntohs(((struct sockaddr_in*)&socketAddr)->sin_port);
break;
#ifdef HAVE_IPV6
case AF_INET6:
source_addr.SetSockAddr((struct sockaddr&)socketAddr);
port = ntohs(((struct sockaddr_in6*)&socketAddr)->sin6_port);
break;
#endif #ifndef WIN32
case AF_UNIX:
source_addr.Invalidate();
port = -1;
break;
#endif default:
PLOG(PL_ERROR, "ProtoSocket::Connect() error: getsockname() returned unknown address type");
break;
} }
destination = theAddress; return true;
}
void ProtoSocket::Disconnect()
{
if (IsConnected() || IsConnecting())
{
state = IDLE;
UpdateNotification();
struct sockaddr nullAddr;
memset(&nullAddr, 0 , sizeof(struct sockaddr));
#ifdef UNIX
if (TCP != protocol)
{
nullAddr.sa_family = AF_UNSPEC;
if (connect(handle, &nullAddr, sizeof(struct sockaddr)))
{
if (EAFNOSUPPORT != errno)
PLOG(PL_WARN, "ProtoSocket::Disconnect() connect() error: %s)\n", GetErrorString());
}
}
else
{
#ifdef MACOSX
Close();
#else
nullAddr.sa_family = AF_UNSPEC;
if (connect(handle,&nullAddr,sizeof(struct sockaddr)))
{
if (EAFNOSUPPORT != errno)
PLOG(PL_WARN, "ProtoSocket::Disconnect() connect() error (%s)\n", GetErrorString());
Close();
}
#endif
}
#else
if (SOCKET_ERROR == WSAConnect(handle, &nullAddr, sizeof(struct sockaddr),
NULL, NULL, NULL, NULL))
{
Close(); }
#endif }
}
bool ProtoSocket::Listen(UINT16 thePort)
{
if (IsBound())
{
if ((0 != thePort) && (thePort != port))
{
PLOG(PL_ERROR, "ProtoSocket::Listen() error: socket bound to different port.\n");
return false;
}
}
else
{
if (!Bind(thePort))
{
PLOG(PL_ERROR, "ProtoSocket::Listen() error binding socket.\n");
return false;
}
}
if (TCP == protocol)
state = LISTENING;
else
return true; if (!UpdateNotification())
{
state = IDLE;
PLOG(PL_ERROR, "ProtoSocket::Listen() error updating notification\n");
return false;
}
#ifdef WIN32
if (SOCKET_ERROR == listen(handle, 5))
#else
if (listen(handle, 5) < 0)
#endif {
PLOG(PL_ERROR, "ProtoSocket: listen() error: %s\n", GetErrorString());
return false;
}
return true;
}
bool ProtoSocket::Accept(ProtoSocket* newSocket)
{
if (TCP != protocol)
{
PLOG(PL_ERROR, "ProtoSocket::Accept() error non-TCP socket!\n");
return false;
}
ProtoSocket& theSocket = (NULL != newSocket) ? *newSocket : *this;
if (this != &theSocket)
{
if (NULL != theSocket.listener) delete theSocket.listener;
theSocket = *this;
theSocket.listener = NULL; }
#ifdef HAVE_IPV6
struct sockaddr_in6 socketAddr;
#else
struct sockaddr socketAddr;
#endif socklen_t addrLen = sizeof(socketAddr);
#ifdef WIN32
if (this != &theSocket)
{
if (!ProtoAddress::Win32Startup())
{
PLOG(PL_ERROR, "ProtoSocket::Accept() WSAStartup() error: %s\n", GetErrorString());
return false;
}
}
Handle theHandle = WSAAccept(handle, (struct sockaddr*)&socketAddr, &addrLen, NULL, NULL);
#else
Handle theHandle = accept(handle, (struct sockaddr*)&socketAddr, &addrLen);
#endif if (INVALID_HANDLE == theHandle)
{
#ifdef WIN32
switch (WSAGetLastError())
{
case WSAEWOULDBLOCK:
if (input_ready)
{
input_ready = false;
UpdateNotification();
}
break;
default:
PLOG(PL_ERROR, "ProtoSocket::Accept() accept() error: %s\n", GetErrorString());
break;
}
#endif
PLOG(PL_ERROR, "ProtoSocket::Accept() accept() error: %s\n", GetErrorString());
if (this != &theSocket)
{
#ifdef WIN32
ProtoAddress::Win32Cleanup();
#endif theSocket.handle = INVALID_HANDLE;
theSocket.state = CLOSED;
}
return false;
}
if (LOCAL != domain)
theSocket.destination.SetSockAddr((struct sockaddr&)socketAddr);
addrLen = sizeof(socketAddr);
if (getsockname(theHandle, (struct sockaddr*)&socketAddr, &addrLen) < 0)
{
PLOG(PL_ERROR, "ProtoSocket::Accept() getsockname() error: %s\n", GetErrorString());
if (this != &theSocket)
{
#ifdef WIN32
closesocket(theHandle);
ProtoAddress::Win32Cleanup();
#endif theSocket.handle = INVALID_HANDLE;
theSocket.state = CLOSED;
}
return false;
}
switch(((struct sockaddr*)&socketAddr)->sa_family)
{
case AF_INET:
theSocket.source_addr.SetSockAddr((struct sockaddr&)socketAddr);
theSocket.port = ntohs(((struct sockaddr_in*)&socketAddr)->sin_port);
break;
#ifdef HAVE_IPV6
case AF_INET6:
theSocket.source_addr.SetSockAddr((struct sockaddr&)socketAddr);
theSocket.port = ntohs(((struct sockaddr_in6*)&socketAddr)->sin6_port);
break;
#endif #ifndef WIN32
case AF_UNIX:
theSocket.source_addr.Invalidate();
theSocket.port = -1;
break;
#endif default:
PLOG(PL_ERROR, "ProtoSocket::Accept() error: getsockname() returned unknown address type");
if (this != &theSocket)
{
#ifdef WIN32
closesocket(theHandle);
ProtoAddress::Win32Cleanup();
#endif theSocket.handle = INVALID_HANDLE;
theSocket.state = CLOSED;
}
return false;
} if (this == &theSocket)
{
state = CLOSED;
UpdateNotification();
#ifdef WIN32
closesocket(theSocket.handle);
input_ready = false; output_ready = true; #else
close(theSocket.handle);
#endif }
else
{
#ifdef WIN32
if (NULL == (theSocket.input_event_handle = WSACreateEvent()))
{
PLOG(PL_ERROR, "ProtoSocket::Accept() WSACreateEvent error: %s\n", GetErrorString());
theSocket.Close();
return false;
}
theSocket.input_ready = false; theSocket.output_ready = true; #endif if (NULL != listener)
{
if (NULL == (theSocket.listener = listener->duplicate()))
{
PLOG(PL_ERROR, "ProtoSocket::Accept() listener duplication error: %s\n", ::GetErrorString());
theSocket.Close();
return false;
}
}
if (NULL != notifier)
{
theSocket.handle = theHandle;
if(!theSocket.SetBlocking(false))
{
PLOG(PL_ERROR, "ProtoSocket::Accept() SetBlocking(false) error\n");
theSocket.Close();
return false;
}
}
} theSocket.handle = theHandle; theSocket.state = CONNECTED;
theSocket.UpdateNotification();
return true;
}
bool ProtoSocket::Send(const char* buffer,
unsigned int& numBytes)
{
if (IsConnected())
{
#ifdef WIN32
WSABUF sendBuf;
sendBuf.buf = (char*)buffer;
sendBuf.len = numBytes;
DWORD bytesSent;
if (SOCKET_ERROR == WSASend(handle, &sendBuf, 1, &bytesSent, 0, NULL, NULL))
{
numBytes = 0;
switch (WSAGetLastError())
{
case WSAEINTR:
return true;
case WSAEWOULDBLOCK:
if (output_ready)
{
output_ready = false;
UpdateNotification(); }
return true;
case WSAENETRESET:
case WSAECONNABORTED:
case WSAECONNRESET:
case WSAESHUTDOWN:
case WSAENOTCONN:
if (output_ready)
{
output_ready = false;
UpdateNotification(); }
OnNotify(NOTIFY_ERROR);
break;
default:
PLOG(PL_ERROR, "ProtoSocket::Send() WSASend() error: %s\n", GetErrorString());
break;
}
return false;
}
else
{
if (bytesSent < numBytes)
{
if (output_ready)
{
output_ready = false;
UpdateNotification(); }
}
else if (!output_ready)
{
output_ready = true;
UpdateNotification();
}
numBytes = (unsigned int)bytesSent;
return true;
}
#else
ssize_t result = send(handle, buffer, (size_t)numBytes, 0);
if (result < 0)
{
numBytes = 0;
switch (errno)
{
case EINTR:
case EAGAIN:
return true;
case ENETRESET:
case ECONNABORTED:
case ECONNRESET:
case ESHUTDOWN:
case ENOTCONN:
case EPIPE:
OnNotify(NOTIFY_ERROR);
break;
case ENOBUFS:
PLOG(PL_DEBUG, "ProtoSocket::Send() send() error: %s\n", GetErrorString());
return false;
default:
PLOG(PL_ERROR, "ProtoSocket::Send() send() error: %s\n", GetErrorString());
break;
}
return false;
}
else
{
numBytes = (unsigned int)result;
return true;
}
#endif }
else
{
PLOG(PL_ERROR, "ProtoSocket::Send() error unconnected socket\n");
numBytes = 0;
return false;
}
}
bool ProtoSocket::Recv(char* buffer,
unsigned int& numBytes)
{
#ifdef WIN32
WSABUF recvBuf;
recvBuf.buf = buffer;
recvBuf.len = numBytes;
DWORD bytesReceived;
DWORD flags = 0;
if (SOCKET_ERROR == WSARecv(handle, &recvBuf, 1, &bytesReceived, &flags, NULL, NULL))
{
numBytes = 0;
switch (WSAGetLastError())
{
case WSAEINTR:
case WSAEWOULDBLOCK:
if (input_ready)
{
input_ready = false;
UpdateNotification(); }
return true; break;
case WSAENETRESET:
case WSAECONNABORTED:
case WSAECONNRESET:
case WSAESHUTDOWN:
case WSAENOTCONN:
PLOG(PL_ERROR, "ProtoSocket::Recv() recv() error: %s\n", GetErrorString());
OnNotify(NOTIFY_ERROR);
break;
default:
PLOG(PL_ERROR, "ProtoSocket::Recv() recv() error: %s\n", GetErrorString());
break;
}
return false;
}
else
{
if ((bytesReceived < numBytes) && (TCP == protocol))
{
input_ready = false;
UpdateNotification(); }
else if (!input_ready)
{
input_ready = true;
UpdateNotification();
}
numBytes = bytesReceived;
return true;
}
#else
ssize_t result = recv(handle, buffer, numBytes, 0);
if (result < 0)
{
numBytes = 0;
switch (errno)
{
case EINTR:
case EAGAIN:
PLOG(PL_WARN, "ProtoSocket::Recv() recv() error: %s\n", GetErrorString());
return true;
break;
case ENETRESET:
case ECONNABORTED:
case ECONNRESET:
case ESHUTDOWN:
case ENOTCONN:
OnNotify(NOTIFY_ERROR);
break;
default:
PLOG(PL_ERROR, "ProtoSocket::Recv() recv() error: %s\n", GetErrorString());
break;
}
return false;
}
else
{
numBytes = (unsigned int)result;
if (0 == result) OnNotify(NOTIFY_NONE);
return true;
}
#endif }
bool ProtoSocket::SendTo(const char* buffer,
unsigned int& buflen,
const ProtoAddress& dstAddr)
{
if (!IsOpen())
{
if (!Open(0, dstAddr.GetType()))
{
PLOG(PL_ERROR, "ProtoSocket::SendTo() error: socket not open\n");
return false;
}
}
if (IsConnected())
{
unsigned int numBytes = buflen;
if (!Send(buffer, numBytes))
{
PLOG(PL_DEBUG, "ProtoSocket::SendTo() error: Send() error\n");
buflen = 0;
return false;
}
if (numBytes != buflen)
{
PLOG(PL_DEBUG, "ProtoSocket::SendTo() error: Send() incomplete\n");
buflen = 0;
return true;
}
else
{
return true;
}
}
else
{
socklen_t addrSize;
#ifdef HAVE_IPV6
if (flow_label && (ProtoAddress::IPv6 == dstAddr.GetType()))
((struct sockaddr_in6*)(&dstAddr.GetSockAddrStorage()))->sin6_flowinfo = flow_label;
if (ProtoAddress::IPv6 == dstAddr.GetType())
addrSize = sizeof(struct sockaddr_in6);
else
#endif addrSize = sizeof(struct sockaddr_in);
#ifdef WIN32
WSABUF wsaBuf;
wsaBuf.len = buflen;
wsaBuf.buf = (char*)buffer;
DWORD numBytes;
if (SOCKET_ERROR == WSASendTo(handle, &wsaBuf, 1, &numBytes, 0,
&dstAddr.GetSockAddr(), addrSize, NULL, NULL))
{
buflen = 0;
switch (WSAGetLastError())
{
case WSAEINTR:
return true;
case WSAEWOULDBLOCK:
output_ready = false;
return true;
default:
break;
}
PLOG(PL_ERROR, "ProtoSocket::SendTo() WSASendTo() error: %s\n", GetErrorString());
return false;
}
else
{
return true;
}
#else
ssize_t result = sendto(handle, buffer, (size_t)buflen, 0, &dstAddr.GetSockAddr(), addrSize);
if (result < 0)
{
buflen = 0;
switch (errno)
{
case EINTR:
case EAGAIN:
return true;
case ENOBUFS:
PLOG(PL_DEBUG, "ProtoSocket::SendTo() sendto() error: %s\n", GetErrorString());
return false;
default:
break;
}
PLOG(PL_ERROR, "ProtoSocket::SendTo() sendto() error: %s\n", GetErrorString());
return false;
}
else
{
return true;
}
#endif }
}
bool ProtoSocket::RecvFrom(char* buffer,
unsigned int& numBytes,
ProtoAddress& sourceAddr)
{
if (!IsBound())
{
PLOG(PL_ERROR, "ProtoSocket::RecvFrom() error: socket not bound\n");
numBytes = 0;
}
#ifdef HAVE_IPV6
struct sockaddr_storage sockAddr;
#else
struct sockaddr sockAddr;
#endif socklen_t addrLen = sizeof(sockAddr);
ssize_t result = recvfrom(handle, buffer, (size_t)numBytes, 0, (struct sockaddr*)&sockAddr, &addrLen);
if (result < 0)
{
numBytes = 0;
#ifdef WIN32
switch (WSAGetLastError())
{
case WSAEINTR:
case WSAEWOULDBLOCK:
if (input_ready)
{
input_ready = false;
UpdateNotification(); }
return true;
break;
default:
PLOG(PL_ERROR, "ProtoSocket::RecvFrom() recvfrom() error: %s\n", GetErrorString());
break;
}
#else
switch (errno)
{
case EINTR:
case EAGAIN:
return true;
break;
default:
PLOG(PL_ERROR, "ProtoSocket::Recv() recv() error: %s\n", GetErrorString());
break;
}
#endif return false;
}
else
{
#ifdef WIN32
if (!input_ready)
{
input_ready = true;
UpdateNotification();
}
#endif numBytes = (unsigned int)result;
sourceAddr.SetSockAddr(*((struct sockaddr*)&sockAddr));
if (!sourceAddr.IsValid())
{
PLOG(PL_ERROR, "ProtoSocket::RecvFrom() Unsupported address type!\n");
return false;
}
return true;
}
}
void ProtoSocket::EnableRecvDstAddr()
{
if (!ip_recvdstaddr)
{
int enable = 1;
#ifdef IP_RECVDSTADDR
if (setsockopt(handle, IPPROTO_IP, IP_RECVDSTADDR, (char*)&enable, sizeof(enable)) < 0)
PLOG(PL_WARN, "ProtoSocket::EnableRecvDstAddr() setsocktopt(IP_RECVDSTADDR) error: %s\n", GetErrorString());
#else
if (setsockopt(handle, IPPROTO_IP, IP_PKTINFO, (char*)&enable, sizeof(enable)) < 0)
PLOG(PL_WARN, "ProtoSocket::EnableRecvDstAddr() setsocktopt(IP_PKTINFO) error: %s\n", GetErrorString());
#endif #ifdef HAVE_IPV6
#ifdef IPV6_RECVDSTADDR
if (setsockopt(handle, IPPROTO_IPV6, IPV6_RECVDSTADDR, (char*)&enable, sizeof(enable)) < 0)
PLOG(PL_WARN, "ProtoSocket::EnableRecvDstAddr() setsocktopt(IPV6_RECVDSTADDR) error: %s\n", GetErrorString());
#else
if (setsockopt(handle, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char*)&enable, sizeof(enable)) < 0)
PLOG(PL_WARN, "ProtoSocket::EnableRecvDstAddr() setsocktopt(IPV6_PKTINFO) error: %s\n", GetErrorString());
#endif #endif ip_recvdstaddr = true;
}
#ifdef WIN32
if (NULL == WSARecvMsg)
{
GUID WSARecvMsg_GUID = WSAID_WSARECVMSG;
DWORD NumberOfBytes;
if (SOCKET_ERROR == WSAIoctl(handle, SIO_GET_EXTENSION_FUNCTION_POINTER,
&WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID),
&WSARecvMsg, sizeof WSARecvMsg,
&NumberOfBytes, NULL, NULL))
{
PLOG(PL_ERROR, "ProtoSocket::EnableRecvDstAddr() error: WSARecvMsg() not supported on this platform!\n");
WSARecvMsg = NULL;
}
}
#endif }
#ifndef WIN32
bool ProtoSocket::RecvFrom(char* buffer,
unsigned int& numBytes,
ProtoAddress& sourceAddr,
ProtoAddress& destAddr)
{
if (!IsBound())
{
PLOG(PL_ERROR, "ProtoSocket::RecvFrom() error: socket not bound\n");
numBytes = 0;
}
if (!ip_recvdstaddr) EnableRecvDstAddr();
#ifdef HAVE_IPV6
struct sockaddr_storage sockAddr;
#else
struct sockaddr sockAddr;
#endif socklen_t addrLen = sizeof(sockAddr);
char cdata[64];
struct msghdr msg; struct iovec iov[1];
iov[0].iov_base = buffer;
iov[0].iov_len = numBytes;
msg.msg_name = &sockAddr;
msg.msg_namelen = addrLen;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = cdata;
msg.msg_controllen = 64; msg.msg_flags = 0;
destAddr.Invalidate();
ssize_t result = recvmsg(handle, &msg, 0);
if (result < 0)
{
numBytes = 0;
#ifdef WIN32
switch (WSAGetLastError())
{
case WSAEINTR:
case WSAEWOULDBLOCK:
input_ready = false;
return true;
break;
default:
PLOG(PL_ERROR, "ProtoSocket::RecvFrom() recvmsg() error: %s\n", GetErrorString());
break;
}
#else
switch (errno)
{
case EINTR:
case EAGAIN:
return true;
break;
default:
PLOG(PL_ERROR, "ProtoSocket::Recv() recv() error: %s\n", GetErrorString());
break;
}
#endif return false;
}
else
{
numBytes = (unsigned int)result;
sourceAddr.SetSockAddr(*((struct sockaddr*)&sockAddr));
if (!sourceAddr.IsValid())
{
PLOG(PL_ERROR, "ProtoSocket::RecvFrom() Unsupported address type!\n");
return false;
}
for (struct cmsghdr* cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; cmptr = CMSG_NXTHDR(&msg, cmptr))
{
if (cmptr->cmsg_level == IPPROTO_IP)
{
#ifdef IP_RECVDSTADDR
if ((cmptr->cmsg_level == IPPROTO_IP) && (cmptr->cmsg_type == IP_RECVDSTADDR))
{
destAddr.SetRawHostAddress(ProtoAddress::IPv4, (char*)CMSG_DATA(cmptr), 4);
}
#else
if (cmptr->cmsg_type == IP_PKTINFO)
{
struct in_pktinfo* pktInfo = (struct in_pktinfo*)((void*)CMSG_DATA(cmptr));
destAddr.SetRawHostAddress(ProtoAddress::IPv4, (char*)(&pktInfo->ipi_addr), 4);
}
#endif }
#ifdef HAVE_IPV6
if (cmptr->cmsg_level == IPPROTO_IPV6)
{
#ifdef IPV6_RECVDSTADDR
if (cmptr->cmsg_type == IPV6_RECVDSTADDR)
{
destAddr.SetRawHostAddress(ProtoAddress::IPv6, (char*)CMSG_DATA(cmptr), 16);
}
#else
if (cmptr->cmsg_type == IPV6_PKTINFO)
{
struct in6_pktinfo* pktInfo = (struct in6_pktinfo*)((void*)CMSG_DATA(cmptr));
destAddr.SetRawHostAddress(ProtoAddress::IPv6, (char*)(&pktInfo->ipi6_addr), 16);
}
#endif }
#endif }
return true;
}
} #else
bool ProtoSocket::RecvFrom(char* buffer,
unsigned int& numBytes,
ProtoAddress& sourceAddr,
ProtoAddress& destAddr)
{
if (!IsBound())
{
PLOG(PL_ERROR, "ProtoSocket::RecvFrom() error: socket not bound\n");
numBytes = 0;
}
if (!ip_recvdstaddr) EnableRecvDstAddr(); destAddr.Invalidate();
if (NULL == WSARecvMsg)
{
PLOG(PL_WARN, "ProtoSocket::RecvFrom() warning: WSARecvMsg() not supported\n");
return RecvFrom(buffer, numBytes, sourceAddr);
}
struct sockaddr sockAddr;
socklen_t addrLen = sizeof(sockAddr);
char cdata[256];
WSABUF dbuf[1];
dbuf[0].len = numBytes;
dbuf[0].buf = buffer;
WSAMSG msg;
msg.name = &sockAddr;
msg.namelen = addrLen;
msg.lpBuffers = dbuf;
msg.dwBufferCount = 1;
msg.Control.len = 64;
msg.Control.buf = cdata;
msg.dwFlags = 0;
destAddr.Invalidate();
DWORD bytesRecvd;
if (SOCKET_ERROR == WSARecvMsg(handle, &msg, &bytesRecvd, NULL, NULL))
{
numBytes = 0;
switch (WSAGetLastError())
{
case WSAEINTR:
case WSAEWOULDBLOCK:
input_ready = false;
return true;
break;
default:
PLOG(PL_ERROR, "ProtoSocket::RecvFrom() WSARecvMsg() error: %s\n", GetErrorString());
break;
}
return false;
}
else
{
numBytes = bytesRecvd;
sourceAddr.SetSockAddr(*((struct sockaddr*)&sockAddr));
if (!sourceAddr.IsValid())
{
PLOG(PL_ERROR, "ProtoSocket::RecvFrom() error: Unsupported address type!\n");
return false;
}
for (WSACMSGHDR* cmptr = WSA_CMSG_FIRSTHDR(&msg); cmptr != NULL; cmptr = WSA_CMSG_NXTHDR(&msg, cmptr))
{
if (cmptr->cmsg_level == IPPROTO_IP)
{
#ifdef IP_RECVDSTADDR
if ((cmptr->cmsg_level == IPPROTO_IP) && (cmptr->cmsg_type == IP_RECVDSTADDR))
{
destAddr.SetRawHostAddress(ProtoAddress::IPv4, (char*)WSA_CMSG_DATA(cmptr), 4);
}
#else
if (cmptr->cmsg_type == IP_PKTINFO)
{
struct in_pktinfo* pktInfo = (struct in_pktinfo*)((void*)WSA_CMSG_DATA(cmptr));
destAddr.SetRawHostAddress(ProtoAddress::IPv4, (char*)(&pktInfo->ipi_addr), 4);
}
#endif }
#ifdef HAVE_IPV6
if (cmptr->cmsg_level == IPPROTO_IPV6)
{
#ifdef IPV6_RECVDSTADDR
if (cmptr->cmsg_type == IPV6_RECVDSTADDR)
{
destAddr.SetRawHostAddress(ProtoAddress::IPv6, (char*)WSA_CMSG_DATA(cmptr), 16);
}
#else
if (cmptr->cmsg_type == IPV6_PKTINFO)
{
struct in6_pktinfo* pktInfo = (struct in6_pktinfo*)((void*)WSA_CMSG_DATA(cmptr));
destAddr.SetRawHostAddress(ProtoAddress::IPv6, (char*)(&pktInfo->ipi6_addr), 16);
}
#endif }
#endif }
return true;
}
}
#endif
#ifdef HAVE_IPV6
#ifndef IPV6_ADD_MEMBERSHIP
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
#endif #endif
bool ProtoSocket::JoinGroup(const ProtoAddress& groupAddress,
const char* interfaceName, const ProtoAddress* sourceAddress) {
if (!IsOpen() && !Open(0, groupAddress.GetType(), false))
{
PLOG(PL_ERROR, "ProtoSocket::JoinGroup() error: unable to open socket\n");
return false;
}
#ifdef WIN32
if (NULL != sourceAddress)
{
PLOG(PL_ERROR, "ProtoSocket::JoinGroup() error: Source-specific Multicast (SSM) for WIN32 not yet supported\n");
return false;
}
bool useJoinLeaf = false;
OSVERSIONINFO vinfo;
vinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&vinfo);
if ((VER_PLATFORM_WIN32_NT == vinfo.dwPlatformId) &&
((vinfo.dwMajorVersion < 4) ||
((vinfo.dwMajorVersion == 4) && (vinfo.dwMinorVersion == 0))))
useJoinLeaf = true;
if (useJoinLeaf)
{
if (interfaceName && !SetMulticastInterface(interfaceName))
PLOG(PL_ERROR, "ProtoSocket::JoinGroup() warning: error setting socket multicast interface\n");
SOCKET result = WSAJoinLeaf(handle, &groupAddress.GetSockAddr(), sizeof(struct sockaddr),
NULL, NULL, NULL, NULL, JL_BOTH);
if (INVALID_SOCKET == result)
{
PLOG(PL_ERROR, "WSAJoinLeaf() error: %d\n", WSAGetLastError());
return false;
}
else
{
return true;
}
} #endif int result;
#ifdef HAVE_IPV6
if (ProtoAddress::IPv6 == groupAddress.GetType())
{
if (NULL != sourceAddress)
{
PLOG(PL_ERROR, "ProtoSocket::JoinGroup() error: Source-specific Multicast (SSM) for IPv6 not yet supported\n");
return false;
}
if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6*)&groupAddress.GetSockAddrStorage())->sin6_addr))
{
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr =
(in_addr_t)IN6_V4MAPPED_ADDR(&(((struct sockaddr_in6*)&groupAddress.GetSockAddrStorage())->sin6_addr));
if (NULL != interfaceName)
{
ProtoAddress interfaceAddress;
#if (defined(WIN32) && (WINVER < 0x0500)) || defined(ANDROID)
if (interfaceAddress.ResolveFromString(interfaceName))
{
mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
}
else if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#else
if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#endif {
mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
}
else
{
PLOG(PL_ERROR, "ProtoSocket::JoinGroup() invalid interface name\n");
return false;
}
}
else
{
mreq.imr_interface.s_addr = INADDR_ANY;
}
result = setsockopt(handle, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
}
else
{
struct ipv6_mreq mreq;
mreq.ipv6mr_multiaddr = ((struct sockaddr_in6*)&groupAddress.GetSockAddrStorage())->sin6_addr;
if (NULL != interfaceName)
mreq.ipv6mr_interface = GetInterfaceIndex(interfaceName);
else
mreq.ipv6mr_interface = 0;
result = setsockopt(handle, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
}
}
else
#endif {
if (NULL == sourceAddress)
{
struct ip_mreq mreq;
#ifdef HAVE_IPV6
mreq.imr_multiaddr = ((struct sockaddr_in*)&groupAddress.GetSockAddrStorage())->sin_addr;
#else
mreq.imr_multiaddr = ((struct sockaddr_in*)&groupAddress.GetSockAddr())->sin_addr;
#endif if (NULL != interfaceName)
{
ProtoAddress interfaceAddress;
#if (defined(WIN32) && (WINVER < 0x0500)) || defined(ANDROID)
if (interfaceAddress.ResolveFromString(interfaceName))
{
mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
}
else if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#else
if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#endif {
mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
}
else
{
PLOG(PL_ERROR, "ProtoSocket::JoinGroup() error: invalid interface name\n");
return false;
}
}
else
{
mreq.imr_interface.s_addr = INADDR_ANY;
}
result = setsockopt(handle, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
}
else
{
#if defined(_PROTOSOCKET_IGMPV3_SSM) && defined(IP_ADD_SOURCE_MEMBERSHIP)
struct ip_mreq_source mreq;
#ifdef HAVE_IPV6
mreq.imr_multiaddr = ((struct sockaddr_in*)&groupAddress.GetSockAddrStorage())->sin_addr;
mreq.imr_sourceaddr = ((struct sockaddr_in*)&sourceAddress->GetSockAddrStorage())->sin_addr;
#else
mreq.imr_multiaddr = ((struct sockaddr_in*)&groupAddress.GetSockAddr())->sin_addr;
mreq.imr_sourceaddr = ((struct sockaddr_in*)&sourceAddress->GetSockAddr())->sin_addr;
#endif if (NULL != interfaceName)
{
ProtoAddress interfaceAddress;
#if (defined(WIN32) && (WINVER < 0x0500)) || defined(ANDROID)
if (interfaceAddress.ResolveFromString(interfaceName))
{
mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
}
else if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#else
if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#endif {
mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
}
else
{
PLOG(PL_ERROR, "ProtoSocket::JoinGroup() invalid interface name\n");
return false;
}
}
else
{
mreq.imr_interface.s_addr = INADDR_ANY;
}
result = setsockopt(handle, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
if (result < 0)
{
PLOG(PL_ERROR, "ProtoSocket::JoinGroup() setsockopt(IP_ADD_SOURCE_MEMBERSHIP) error: %s\n", strerror( errno ));
return false;
}
return true;
#else
PLOG(PL_ERROR, "ProtoSocket:JoinGroup() error: SSM support not included in this build\n");
return false;
#endif }
}
if (result < 0)
{
PLOG(PL_ERROR, "ProtoSocket:JoinGroup() setsockopt(add membership) error: %s\n", GetErrorString());
return false;
}
return true;
}
bool ProtoSocket::LeaveGroup(const ProtoAddress& groupAddress,
const char* interfaceName,
const ProtoAddress* sourceAddress)
{
if (!IsOpen()) return true;
int result;
#ifdef HAVE_IPV6
if (ProtoAddress::IPv6 == groupAddress.GetType())
{
if (NULL != sourceAddress)
{
PLOG(PL_ERROR, "ProtoSocket::LeaveGroup() error: Source-specific Multicast (SSM) for IPv6 not yet supported\n");
return false;
}
if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6*)&groupAddress.GetSockAddrStorage())->sin6_addr))
{
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr =
(in_addr_t)IN6_V4MAPPED_ADDR(&(((struct sockaddr_in6*)&groupAddress.GetSockAddrStorage())->sin6_addr));
if (interfaceName)
{
ProtoAddress interfaceAddress;
#if (defined(WIN32) && (WINVER < 0x0500)) || defined(ANDROID)
if (interfaceAddress.ResolveFromString(interfaceName))
{
mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
}
else if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#else
if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#endif {
mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
}
else
{
PLOG(PL_ERROR, "ProtoSocket::LeaveGroup() invalid interface name\n");
return false;
}
}
else
{
mreq.imr_interface.s_addr = INADDR_ANY;
}
result = setsockopt(handle, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
}
else
{
struct ipv6_mreq mreq;
mreq.ipv6mr_multiaddr = ((struct sockaddr_in6*)&groupAddress.GetSockAddrStorage())->sin6_addr;
if (interfaceName)
mreq.ipv6mr_interface = GetInterfaceIndex(interfaceName);
else
mreq.ipv6mr_interface = 0;
result = setsockopt(handle, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
}
}
else
#endif {
if (NULL == sourceAddress)
{
struct ip_mreq mreq;
#ifdef HAVE_IPV6
mreq.imr_multiaddr = ((struct sockaddr_in*)&groupAddress.GetSockAddrStorage())->sin_addr;
#else
mreq.imr_multiaddr = ((struct sockaddr_in*)&groupAddress.GetSockAddr())->sin_addr;
#endif if (NULL != interfaceName)
{
ProtoAddress interfaceAddress;
#if (defined(WIN32) && (WINVER < 0x0500)) || defined(ANDROID)
if (interfaceAddress.ResolveFromString(interfaceName))
{
mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
}
else if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#else
if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#endif {
mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
}
else
{
PLOG(PL_ERROR, "ProtoSocket::LeaveGroup() invalid interface name\n");
return false;
}
}
else
{
mreq.imr_interface.s_addr = INADDR_ANY;
}
result = setsockopt(handle, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
}
else
{
#if defined(_PROTOSOCKET_IGMPV3_SSM) && defined(IP_DROP_SOURCE_MEMBERSHIP)
struct ip_mreq_source mreq;
#ifdef HAVE_IPV6
mreq.imr_multiaddr = ((struct sockaddr_in*)&groupAddress.GetSockAddrStorage())->sin_addr;
mreq.imr_sourceaddr = ((struct sockaddr_in*)&sourceAddress->GetSockAddrStorage())->sin_addr;
#else
mreq.imr_multiaddr = ((struct sockaddr_in*)&groupAddress.GetSockAddr())->sin_addr;
mreq.imr_sourceaddr = ((struct sockaddr_in*)&sourceAddress->GetSockAddr())->sin_addr;
#endif if (interfaceName)
{
ProtoAddress interfaceAddress;
#if (defined(WIN32) && (WINVER < 0x0500)) || defined(ANDROID)
if (interfaceAddress.ResolveFromString(interfaceName))
{
mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
}
else if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#else
if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#endif {
mreq.imr_interface.s_addr = htonl(interfaceAddress.IPv4GetAddress());
}
else
{
PLOG(PL_ERROR, "ProtoSocket::LeaveGroup() invalid interface name\n");
return false;
}
}
else
{
mreq.imr_interface.s_addr = INADDR_ANY;
}
result = setsockopt(handle, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
#else
PLOG(PL_ERROR, "ProtoSocket:LeaveGroup() error: SSM support not included in this build\n");
return false;
#endif }
}
if (result < 0)
{
PLOG(PL_ERROR, "ProtoSocket::LeaveGroup() error leaving multicast group: %s\n", GetErrorString());
return false;
}
else
{
return true;
}
}
bool ProtoSocket::SetTTL(unsigned char ttl)
{
#if defined(WIN32) && !defined(_WIN32_WCE)
DWORD dwTTL = (DWORD)ttl;
DWORD dwBytesXfer;
int optVal = ttl;
int optLen = sizeof(int);
if (WSAIoctl(handle, SIO_MULTICAST_SCOPE, &dwTTL, sizeof(dwTTL),
NULL, 0, &dwBytesXfer, NULL, NULL))
#else
int result = 0;
#ifdef HAVE_IPV6
if (IPv6 == domain)
{
#ifdef MACOSX
int hops = (int) ttl;
if (protocol != ProtoSocket::TCP)
result = setsockopt(handle, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
&hops, sizeof(hops));
if (result == 0)
result = setsockopt(handle, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
&hops, sizeof(hops));
#else
socklen_t hops = (socklen_t) ttl;
if (protocol != ProtoSocket::TCP)
result = setsockopt(handle, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
&hops, sizeof(hops));
if (result == 0)
result = setsockopt(handle, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
&hops, sizeof(hops));
#endif }
else
#endif {
#ifdef _WIN32_WCE
int hops = (int)ttl;
#else
socklen_t hops = (socklen_t)ttl;
#endif #ifdef MACOSX
if (protocol != ProtoSocket::TCP)
result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_TTL,
(char*)&hops, sizeof(hops));
if (result == 0)
result = setsockopt(handle,IPPROTO_IP, IP_TTL,
(char*)&hops, sizeof(hops));
#else
if (protocol != ProtoSocket::TCP)
result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_TTL,
(char*)&hops, sizeof(hops));
#endif
}
if (result < 0)
#endif {
PLOG(PL_ERROR, "ProtoSocket: setsockopt(IP_MULTICAST_TTL) error: %s\n", GetErrorString());
return false;
}
else
{
return true;
}
}
bool ProtoSocket::SetUnicastTTL(unsigned char ttl)
{
#if defined(WIN32) && !defined(_WIN32_WCE)
DWORD dwTTL = (DWORD)ttl;
DWORD dwBytesXfer;
int optVal = ttl;
int optLen = sizeof(int);
if (setsockopt(handle,IPPROTO_IP,IP_TTL,(char*)&optVal,optLen) == SOCKET_ERROR)
{
PLOG(PL_ERROR, "ProtoSocket: setsockopt(IP_TTL) error: %s\n", GetErrorString());
return false;
}
#else
int result = 0;
#ifdef HAVE_IPV6
if (IPv6 == domain)
{
socklen_t hops = (socklen_t) ttl;
#ifdef MACOSX
result = setsockopt(handle, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
&hops, sizeof(&hops));
#else
result = setsockopt(handle, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
&hops, sizeof(hops));
#endif }
else
#endif {
#ifdef _WIN32_WCE
int hops = (int)ttl;
#else
socklen_t hops = (socklen_t)ttl;
#endif #ifdef MACOSX
result = setsockopt(handle,IPPROTO_IP, IP_TTL,
(char*)&hops, sizeof(&hops));
#else
result = setsockopt(handle,IPPROTO_IP, IP_TTL,
(char*)&hops, sizeof(hops));
#endif
}
if (result < 0)
{
PLOG(PL_ERROR, "ProtoSocket: setsockopt(IP_MULTICAST_TTL) error: %s\n", GetErrorString());
return false;
}
#endif else
{
return true;
}
}
bool ProtoSocket::SetTOS(UINT8 theTOS)
{
if (!IsOpen())
{
tos = theTOS;
return true;
}
if (ecn_capable)
{
theTOS |= ((UINT8)ECN_ECT0); theTOS &= ~((UINT8)ECN_ECT1); }
#ifdef NEVER_EVER
int precedence = IPTOS_PREC(theTOS);
if (setsockopt(handle, SOL_SOCKET, SO_PRIORITY, (char*)&precedence, sizeof(precedence)) < 0)
{
PLOG(PL_ERROR, "ProtoSocket: setsockopt(SO_PRIORITY) error: %s\n", GetErrorString());
return false;
}
int tosBits = IPTOS_TOS(theTOS);
if (setsockopt(handle, SOL_IP, IP_TOS, (char*)&tosBits, sizeof(tosBits)) < 0)
#else
int tosBits = theTOS;
int result = -1;
#ifdef HAVE_IPV6
if (IPv6 == domain)
{
#ifdef IPV6_TCLASS
result = setsockopt(handle, IPPROTO_IPV6, IPV6_TCLASS, (char*)&tosBits, sizeof(tosBits));
if (result < 0)
#endif {
result = setsockopt(handle, IPPROTO_IPV6, IP_TOS, (char*)&tosBits, sizeof(tosBits));
if (result < 0)
PLOG(PL_ERROR,"ProtoSocket::SetTOS() Error setting IPV6 tos/tclass %s",GetErrorString());
}
SetFlowLabel(((UINT32)theTOS) << 20); }
else
#endif {
result = setsockopt(handle, IPPROTO_IP, IP_TOS, (char*)&tosBits, sizeof(tosBits));
}
#endif if (result < 0)
{
PLOG(PL_ERROR, "ProtoSocket: setsockopt(IP_TOS) error\n");
return false;
}
tos = theTOS;
return true;
}
bool ProtoSocket::SetEcnCapable(bool state)
{
if (state)
{
if (!ecn_capable)
{
ecn_capable = true;
bool result = SetTOS(tos); if (!result) ecn_capable = false;
return result;
}
}
else if (ecn_capable)
{
ecn_capable = false;
bool result = SetTOS(tos);
if (!result) ecn_capable = true;
return result;
}
return true;
}
bool ProtoSocket::SetBroadcast(bool broadcast)
{
#ifdef SO_BROADCAST
#ifdef WIN32
BOOL state = broadcast ? TRUE : FALSE;
#else
int state = broadcast ? 1 : 0;
#endif if (setsockopt(handle, SOL_SOCKET, SO_BROADCAST, (char*)&state, sizeof(state)) < 0)
{
PLOG(PL_ERROR, "ProtoSocket::SetBroadcast(): setsockopt(SO_BROADCAST) error: %s\n",
GetErrorString());
return false;
}
#endif return true;
}
bool ProtoSocket::SetFragmentation(bool enable)
{
#ifdef WIN32
DWORD df = enable ? FALSE : TRUE;
if (setsockopt(handle, IPPROTO_IP, IP_DONTFRAGMENT, (char*)&df, sizeof(df)) < 0)
{
PLOG(PL_ERROR, "ProtoSocket::SetFragmentation() setsockopt(IP_DONTFRAGMENT) error: %s\n", GetErrorString());
return false;
}
#else
#if defined(IP_MTU_DISCOVER)
int val = enable ? IP_PMTUDISC_DONT : IP_PMTUDISC_DO;
if (setsockopt(handle, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)) < 0)
{
#if defined(HAVE_IPV6) && defined(IPV6_DONTFRAG)
if (IPv6 == domain)
{
int df = enable ? 0 : 1;
if (setsockopt(handle, IPPROTO_IPV6, IPV6_DONTFRAG, &df, sizeof(df)) < 0)
{
PLOG(PL_ERROR, "ProtoSocket::SetFragmentation() setsockopt(IPV6_DONTFRAG) error: %s\n", GetErrorString());
return false;
}
}
else
#endif {
PLOG(PL_ERROR, "ProtoSocket::SetFragmentation() setsockopt(IP_MTU_DISCOVER) error: %s\n", GetErrorString());
return false;
}
}
#elif defined(IP_DONTFRAG)
int df = enable ? 0 : 1;
int result;
#if defined(HAVE_IPV6) && defined(IPV6_DONTFRAG)
if (IPv6 == domain)
result = setsockopt(handle, IPPROTO_IP, IPV6_DONTFRAG, (char*)&df, sizeof(df));
else
#endif
result = setsockopt(handle, IPPROTO_IP, IP_DONTFRAG, (char*)&df, sizeof(df));
if (result < 0)
{
PLOG(PL_ERROR, "ProtoSocket::SetFragmentation() setsockopt(IP_DONTFRAG) error: %s\n", GetErrorString());
return false;
}
#else
PLOG(PL_WARN, "ProtoSocket::SetFragmentation() warning: IP_MTU_DISCOVER or IP_DONTFRAG socket option not supported!\n");
return false;
#endif #endif return true;
}
bool ProtoSocket::SetLoopback(bool loopback)
{
ASSERT(IsOpen());
#ifdef WIN32
DWORD dwLoop = loopback ? TRUE: FALSE;
DWORD dwBytesXfer;
if (WSAIoctl(handle, SIO_MULTIPOINT_LOOPBACK , &dwLoop, sizeof(dwLoop),
NULL, 0, &dwBytesXfer, NULL, NULL))
#else
int result;
char loop = loopback ? 1 : 0;
#ifdef HAVE_IPV6
unsigned int loop6 = loopback ? 1 : 0; if (IPv6 == domain)
result = setsockopt(handle, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
(char*)&loop6, sizeof(loop6));
else
#endif result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_LOOP,
(char*)&loop, sizeof(loop));
if (result < 0)
#endif {
#ifdef WIN32
OSVERSIONINFO vinfo;
vinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&vinfo);
if ((VER_PLATFORM_WIN32_NT == vinfo.dwPlatformId) &&
((vinfo.dwMajorVersion < 4) ||
((vinfo.dwMajorVersion == 4) && (vinfo.dwMinorVersion == 0))))
return true;
#endif PLOG(PL_ERROR, "ProtoSocket: setsockopt(IP_MULTICAST_LOOP) error: %s\n", GetErrorString());
return false;
}
else
{
return true;
}
}
bool ProtoSocket::SetBindInterface(const char* ifaceName)
{
#ifdef SO_BINDTODEVICE
size_t nameSize = strlen(ifaceName) + 1; if (setsockopt(handle, SOL_SOCKET, SO_BINDTODEVICE, ifaceName, nameSize) < 0)
{
PLOG(PL_ERROR, "ProtoSocket::SetBindInterface() error: %s\n", GetErrorString());
return false;
}
return true;
#else
PLOG(PL_ERROR, "ProtoSocket::SetBindInterface() error: SO_BINDTODEVICE socket option not supported!\n", GetErrorString());
return false;
#endif }
bool ProtoSocket::SetMulticastInterface(const char* interfaceName)
{
if (interfaceName)
{
int result;
#ifdef HAVE_IPV6
if (IPv6 == domain)
{
#ifdef WIN32
PLOG(PL_ERROR, "ProtoSocket::SetMulticastInterface() not yet supported for IPv6 on WIN32?!\n");
return false;
#else
unsigned int interfaceIndex = GetInterfaceIndex(interfaceName);
result = setsockopt(handle, IPPROTO_IPV6, IPV6_MULTICAST_IF,
(char*)&interfaceIndex, sizeof(interfaceIndex));
#endif }
else
#endif {
struct in_addr localAddr;
ProtoAddress interfaceAddress;
#if (defined(WIN32) && (WINVER < 0x0500)) || defined(ANDROID)
if (interfaceAddress.ResolveFromString(interfaceName))
{
localAddr.s_addr = htonl(interfaceAddress.IPv4GetAddress());
}
else if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#else
if (GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
#endif {
localAddr.s_addr = htonl(interfaceAddress.IPv4GetAddress());
}
else
{
PLOG(PL_ERROR, "ProtoSocket::SetMulticastInterface() invalid interface name: %s\n", interfaceName);
return false;
}
result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, (char*)&localAddr,
sizeof(localAddr));
}
if (result < 0)
{
PLOG(PL_ERROR, "ProtoSocket: setsockopt(IP_MULTICAST_IF) error: %s\n", GetErrorString());
return false;
}
}
return true;
}
bool ProtoSocket::SetReuse(bool state)
{
#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT)
bool success = true;
#else
return false;
#endif
int reuse = state ? 1 : 0;
#ifdef SO_REUSEADDR
#ifdef WIN32
BOOL reuseAddr = (BOOL)reuse;
if (setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseAddr, sizeof(reuseAddr)) < 0)
#else
if (setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse)) < 0)
#endif {
PLOG(PL_ERROR, "ProtoSocket: setsockopt(REUSE_ADDR) error: %s\n", GetErrorString());
success = false;
}
#endif
#ifdef SO_REUSEPORT
#ifdef WIN32
BOOL reusePort = (BOOL)reuse;
if (setsockopt(handle, SOL_SOCKET, SO_REUSEPORT, (char*)&reusePort, sizeof(reusePort)) < 0)
#else
if (setsockopt(handle, SOL_SOCKET, SO_REUSEPORT, (char*)&reuse, sizeof(reuse)) < 0)
#endif {
PLOG(PL_ERROR, "ProtoSocket: setsockopt(SO_REUSEPORT) error: %s\n", GetErrorString());
success = false;
}
#endif return success;
}
#ifdef HAVE_IPV6
bool ProtoSocket::HostIsIPv6Capable()
{
#ifdef WIN32
if (!ProtoAddress::Win32Startup())
{
PLOG(PL_ERROR, "ProtoSocket::HostIsIPv6Capable() WSAStartup() error: %s\n", GetErrorString());
return false;
}
SOCKET handle = socket(AF_INET6, SOCK_DGRAM, 0);
closesocket(handle);
ProtoAddress::Win32Cleanup();
if(INVALID_SOCKET == handle)
return false;
else
return true;
#else
if (IPV6_UNKNOWN == ipv6_support_status)
{
ProtoAddressList addrList;
ProtoNet::GetHostAddressList(ProtoAddress::IPv6, addrList);
if (addrList.IsEmpty())
{
ipv6_support_status = IPV6_UNSUPPORTED;
}
else
{
ipv6_support_status = IPV6_SUPPORTED;
}
}
return (IPV6_SUPPORTED == ipv6_support_status);
#endif }
#endif
bool ProtoSocket::SetTxBufferSize(unsigned int bufferSize)
{
if (!IsOpen())
{
PLOG(PL_ERROR, "ProtoSocket::SetTxBufferSize() error: socket closed\n");
return false;
}
unsigned int oldBufferSize = GetTxBufferSize();
if (setsockopt(handle, SOL_SOCKET, SO_SNDBUF, (char*)&bufferSize, sizeof(bufferSize)) < 0)
{
setsockopt(handle, SOL_SOCKET, SO_SNDBUF, (char*)&oldBufferSize, sizeof(oldBufferSize));
PLOG(PL_ERROR, "ProtoSocket::SetTxBufferSize() setsockopt(SO_SNDBUF) error: %s\n",
GetErrorString());
return false;
}
return true;
}
unsigned int ProtoSocket::GetTxBufferSize()
{
if (!IsOpen()) return 0;
unsigned int txBufferSize = 0;
socklen_t len = sizeof(txBufferSize);
if (getsockopt(handle, SOL_SOCKET, SO_SNDBUF, (char*)&txBufferSize, &len) < 0)
{
PLOG(PL_ERROR, "ProtoSocket::GetTxBufferSize() getsockopt(SO_SNDBUF) error: %s\n",
GetErrorString());
return 0;
}
return ((unsigned int)txBufferSize);
}
bool ProtoSocket::SetRxBufferSize(unsigned int bufferSize)
{
if (!IsOpen())
{
PLOG(PL_ERROR, "ProtoSocket::SetRxBufferSize() error: socket closed\n");
return false;
}
unsigned int oldBufferSize = GetRxBufferSize();
if (setsockopt(handle, SOL_SOCKET, SO_RCVBUF, (char*)&bufferSize, sizeof(bufferSize)) < 0)
{
setsockopt(handle, SOL_SOCKET, SO_RCVBUF, (char*)&oldBufferSize, sizeof(oldBufferSize));
PLOG(PL_ERROR, "ProtoSocket::SetRxBufferSize() setsockopt(SO_RCVBUF) error: %s\n",
GetErrorString());
return false;
}
return true;
}
unsigned int ProtoSocket::GetRxBufferSize()
{
if (!IsOpen()) return 0;
unsigned int rxBufferSize = 0;
socklen_t len = sizeof(rxBufferSize);
if (getsockopt(handle, SOL_SOCKET, SO_RCVBUF, (char*)&rxBufferSize, &len) < 0)
{
PLOG(PL_ERROR, "ProtoSocket::GetRxBufferSize() getsockopt(SO_RCVBUF) error: %s\n",
GetErrorString());
return 0;
}
return ((unsigned int)rxBufferSize);
}
#ifdef HAVE_IPV6
bool ProtoSocket::SetFlowLabel(UINT32 label)
{
int result = 0;
#ifdef SOL_IPV6
if (label && !flow_label)
{
int on = 1;
result = setsockopt(handle, SOL_IPV6, IPV6_FLOWINFO_SEND, (char*)&on, sizeof(on));
}
else if (!label && flow_label)
{
int off = 0;
result = setsockopt(handle, SOL_IPV6, IPV6_FLOWINFO_SEND, (char*)&off, sizeof(off));
}
#endif if (0 == result)
{
if (ecn_capable)
{
label |= (((UINT32)ECN_ECT0) << 20); label &= ~(((UINT32)ECN_ECT1) << 20); }
flow_label = htonl(label);
return true;
}
else
{
PLOG(PL_ERROR, "ProtoSocket::SetFlowLabel() setsockopt(SOL_IPV6) error\n");
return false;
}
} #endif
ProtoSocket::List::List()
: head(NULL)
{
}
ProtoSocket::List::~List()
{
Destroy();
}
void ProtoSocket::List::Destroy()
{
Item* next = head;
while (next)
{
Item* current = next;
next = next->GetNext();
delete current->GetSocket();
delete current;
}
head = NULL;
}
bool ProtoSocket::List::AddSocket(ProtoSocket& theSocket)
{
Item* item = new Item(&theSocket);
if (item)
{
item->SetPrev(NULL);
item->SetNext(head);
head = item;
return true;
}
else
{
PLOG(PL_ERROR, "ProtoSocket::List::AddSocket() new Item error: %s\n", GetErrorString());
return false;
}
}
void ProtoSocket::List::RemoveSocket(ProtoSocket& theSocket)
{
Item* item = head;
while (item)
{
if (&theSocket == item->GetSocket())
{
Item* prev = item->GetPrev();
Item* next = item->GetNext();
if (prev)
prev->SetNext(next);
else
head = next;
if (next) next->SetPrev(prev);
delete item;
break;
}
item = item->GetNext();
}
}
ProtoSocket::List::Item* ProtoSocket::List::FindItem(const ProtoSocket& theSocket) const
{
Item* item = head;
while (item)
{
if (&theSocket == item->GetSocket())
return item;
else
item = item->GetNext();
}
return NULL;
}
ProtoSocket::List::Item::Item(ProtoSocket* theSocket)
: socket(theSocket), prev(NULL), next(NULL)
{
}
ProtoSocket::List::Iterator::Iterator(const ProtoSocket::List& theList)
: next(theList.head)
{
}
#include "protoNet.h"
bool ProtoSocket::GetHostAddressList(ProtoAddress::Type addrType,
ProtoAddressList& addrList)
{
return ProtoNet::GetHostAddressList(addrType, addrList);
}
bool ProtoSocket::GetInterfaceAddress(const char* ifName,
ProtoAddress::Type addrType,
ProtoAddress& theAddress,
unsigned int* ifIndex)
{
return ProtoNet::GetInterfaceAddress(ifName, addrType, theAddress, ifIndex);
}
bool ProtoSocket::GetInterfaceAddressList(const char* interfaceName,
ProtoAddress::Type addressType,
ProtoAddressList& addrList,
unsigned int* interfaceIndex)
{
return ProtoNet::GetInterfaceAddressList(interfaceName, addressType, addrList, interfaceIndex);
}
bool ProtoSocket::FindLocalAddress(ProtoAddress::Type addrType, ProtoAddress& theAddress)
{
return ProtoNet::FindLocalAddress(addrType, theAddress);
}
unsigned int ProtoSocket::GetInterfaceIndex(const char* interfaceName)
{
return ProtoNet::GetInterfaceIndex(interfaceName);
}
unsigned int ProtoSocket::GetInterfaceIndices(unsigned int* indexArray, unsigned int indexArraySize)
{
return ProtoNet::GetInterfaceIndices(indexArray, indexArraySize);
}
bool ProtoSocket::GetInterfaceName(unsigned int index, char* buffer, unsigned int buflen)
{
return (0 != ProtoNet::GetInterfaceName(index, buffer, buflen));
}
bool ProtoSocket::GetInterfaceName(const ProtoAddress& ifAddr, char* buffer, unsigned int buflen)
{
return (0 != ProtoNet::GetInterfaceName(ifAddr, buffer, buflen));
}