blob: 46157f29211390fd3621a9e509b6e66a47367cac [file] [log] [blame]
// Copyright (C) 1999-2005 Open Source Telecom Corporation.
// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// As a special exception, you may use this file as part of a free software
// library without restriction. Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License. This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
//
// This exception applies only to the code released under the name GNU
// Common C++. If you copy code from other releases into a copy of GNU
// Common C++, as the General Public License permits, the exception does
// not apply to the code that you add in this way. To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU Common C++, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//
#include <ucommon-config.h>
#include <commoncpp/config.h>
#include <commoncpp/export.h>
#include <commoncpp/address.h>
#include <commoncpp/socket.h>
#include <fcntl.h>
#ifdef _MSWINDOWS_
#include <io.h>
#define _IOLEN64 (unsigned)
#define _IORET64 (int)
typedef int socklen_t;
#define socket_errno WSAGetLastError()
#else
#include <sys/ioctl.h>
#include <netinet/tcp.h>
#ifdef HAVE_NET_IP6_H
#include <netinet/ip6.h>
#endif
#define socket_errno errno
# ifndef O_NONBLOCK
# define O_NONBLOCK O_NDELAY
# endif
# ifdef IPPROTO_IP
# ifndef SOL_IP
# define SOL_IP IPPROTO_IP
# endif // !SOL_IP
# endif // IPPROTO_IP
#endif // !WIN32
#ifndef INADDR_LOOPBACK
#define INADDR_LOOPBACK (unsigned long)0x7f000001
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#if defined(__hpux)
#define _XOPEN_SOURCE_EXTENDED
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#ifndef _IOLEN64
#define _IOLEN64
#endif
#ifndef _IORET64
#define _IORET64
#endif
using namespace COMMONCPP_NAMESPACE;
#if defined(_MSWINDOWS_) && !defined(__MINGW32__)
socket_t Socket::dupSocket(socket_t so, enum Socket::State state)
{
if (state == Socket::STREAM)
return dup((int)so);
HANDLE pidHandle = GetCurrentProcess();
HANDLE dupHandle;
if(DuplicateHandle(pidHandle, reinterpret_cast<HANDLE>(so), pidHandle, &dupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS))
return reinterpret_cast<socket_t>(dupHandle);
return (socket_t)(-1);
}
# define DUP_SOCK(s,state) dupSocket(s,state)
#else
socket_t Socket::dupSocket(socket_t so, State state)
{
return dup(so);
}
#endif
Mutex Socket::mutex;
void Socket::setSocket(void)
{
flags.thrown = false;
flags.broadcast = false;
flags.route = true;
flags.keepalive = false;
flags.loopback = true;
flags.multicast = false;
flags.linger = false;
flags.ttl = 1;
errid = errSuccess;
errstr = NULL;
syserr = 0;
state = INITIAL;
so = INVALID_SOCKET;
}
void Socket::endSocket(void)
{
if(Socket::state == STREAM) {
state = INITIAL;
if(so != INVALID_SOCKET) {
SOCKET sosave = so;
so = INVALID_SOCKET;
release(sosave);
}
return;
}
state = INITIAL;
if(so == INVALID_SOCKET)
return;
#ifdef SO_LINGER
struct linger linger;
if(flags.linger) {
linger.l_onoff = 1;
linger.l_linger = 60;
}
else
linger.l_onoff = linger.l_linger = 0;
setsockopt(so, SOL_SOCKET, SO_LINGER, (char *)&linger,
(socklen_t)sizeof(linger));
#endif
// shutdown(so, 2);
ucommon::Socket::release();
}
Socket::Socket() : ucommon::Socket()
{
setSocket();
}
Socket::Socket(int domain, int type, int protocol) : ucommon::Socket()
{
setSocket();
so = socket(domain, type, protocol);
if(so == INVALID_SOCKET) {
error(errCreateFailed,(char *)"Could not create socket",socket_errno);
return;
}
#ifdef SO_NOSIGPIPE
int opt = 1;
setsockopt(so, SOL_SOCKET, SO_NOSIGPIPE, (char *)&opt, sizeof(opt));
#endif
state = AVAILABLE;
}
Socket::Socket(socket_t fd) : ucommon::Socket()
{
setSocket();
if (fd == INVALID_SOCKET) {
error(errCreateFailed,(char *)"Invalid socket handle passed",0);
return;
}
so = fd;
state = AVAILABLE;
}
Socket::Socket(const Socket &orig) : ucommon::Socket()
{
setSocket();
so = dupSocket(orig.so,orig.state);
if(so == INVALID_SOCKET)
error(errCopyFailed,(char *)"Could not duplicate socket handle",socket_errno);
state = orig.state;
}
Socket::~Socket()
{
endSocket();
}
Socket::Error Socket::error(Error err, const char *errs, long systemError) const
{
errid = err;
errstr = errs;
syserr = systemError;
if(!err)
return err;
if(flags.thrown)
return err;
// prevents recursive throws
flags.thrown = true;
#ifdef CCXX_EXCEPTIONS
switch(Thread::getException()) {
case Thread::throwObject:
THROW((Socket *)this);
case Thread::throwException:
if(!errs)
errs = (char *)"";
THROW(SockException(String(errs), err, systemError));
case Thread::throwNothing:
break;
}
#endif
return err;
}
#ifdef _MSWINDOWS_
Socket::Error Socket::connectError(void)
{
const char* str = "Could not connect to remote host";
switch(WSAGetLastError()) {
case WSAENETDOWN:
return error(errResourceFailure,str,socket_errno);
case WSAEINPROGRESS:
return error(errConnectBusy,str,socket_errno);
case WSAEADDRNOTAVAIL:
return error(errConnectInvalid,str,socket_errno);
case WSAECONNREFUSED:
return error(errConnectRefused,str,socket_errno);
case WSAENETUNREACH:
return error(errConnectNoRoute,str,socket_errno);
default:
return error(errConnectFailed,str,socket_errno);
}
}
#else
Socket::Error Socket::connectError(void)
{
char* str = (char *)"Could not connect to remote host";
switch(errno) {
#ifdef EHOSTUNREACH
case EHOSTUNREACH:
return error(errConnectNoRoute,str,socket_errno);
#endif
#ifdef ENETUNREACH
case ENETUNREACH:
return error(errConnectNoRoute,str,socket_errno);
#endif
case EINPROGRESS:
return error(errConnectBusy,str,socket_errno);
#ifdef EADDRNOTAVAIL
case EADDRNOTAVAIL:
return error(errConnectInvalid,str,socket_errno);
#endif
case ECONNREFUSED:
return error(errConnectRefused,str,socket_errno);
case ETIMEDOUT:
return error(errConnectTimeout,str,socket_errno);
default:
return error(errConnectFailed,str,socket_errno);
}
}
#endif
Socket::Error Socket::sendLimit(int limit)
{
#ifdef SO_SNDLOWAT
if(setsockopt(so, SOL_SOCKET, SO_SNDLOWAT, (char *)&limit, sizeof(limit)))
return errInvalidValue;
return errSuccess;
#else
return errServiceUnavailable;
#endif
}
Socket::Error Socket::receiveLimit(int limit)
{
#ifdef SO_RCVLOWAT
if(setsockopt(so, SOL_SOCKET, SO_RCVLOWAT, (char *)&limit, sizeof(limit)))
return errInvalidValue;
return errSuccess;
#else
return errServiceUnavailable;
#endif
}
Socket::Error Socket::sendTimeout(timeout_t to)
{
#ifdef SO_SNDTIMEO
struct timeval tv;
tv.tv_sec = to / 1000;
tv.tv_usec = (to % 1000) * 1000;
if(setsockopt(so, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv)))
return errInvalidValue;
return errSuccess;
#else
return errServiceUnavailable;
#endif
}
Socket::Error Socket::receiveTimeout(timeout_t to)
{
#ifdef SO_RCVTIMEO
struct timeval tv;
tv.tv_sec = to / 1000;
tv.tv_usec = (to % 1000) * 1000;
if(setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)))
return errInvalidValue;
return errSuccess;
#else
return errServiceUnavailable;
#endif
}
Socket::Error Socket::sendBuffer(unsigned bufsize)
{
#ifdef SO_SNDBUF
if(setsockopt(so, SOL_SOCKET, SO_SNDBUF, (char *)&bufsize, sizeof(bufsize)))
return errInvalidValue;
return errSuccess;
#else
return errServiceUnavailable;
#endif
}
Socket::Error Socket::bufferSize(unsigned bufsize)
{
Error err = receiveBuffer(bufsize);
if(err == errSuccess)
err = sendBuffer(bufsize);
return err;
}
Socket::Error Socket::receiveBuffer(unsigned bufsize)
{
#ifdef SO_RCVBUF
if(setsockopt(so, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof(bufsize)))
return errInvalidValue;
return errSuccess;
#else
return errServiceUnavailable;
#endif
}
Socket::Error Socket::setBroadcast(bool enable)
{
int opt = (enable ? 1 : 0);
if(setsockopt(so, SOL_SOCKET, SO_BROADCAST,
(char *)&opt, (socklen_t)sizeof(opt)))
return error(errBroadcastDenied,(char *)"Could not set socket broadcast option",socket_errno);
flags.broadcast = enable;
return errSuccess;
}
Socket::Error Socket::setMulticastByFamily(bool enable, Family family)
{
socklen_t len;
switch(family) {
#ifdef CCXX_IPV6
case IPV6:
struct sockaddr_in6 addr;
len = sizeof(addr);
if(enable == flags.multicast)
return errSuccess;
flags.multicast = enable;
if(enable)
getsockname(so, (struct sockaddr *)&addr, &len);
else
memset(&addr.sin6_addr, 0, sizeof(addr.sin6_addr));
setsockopt(so, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *)&addr.sin6_addr, sizeof(addr.sin6_addr));
return errSuccess;
#endif
case IPV4:
#ifdef IP_MULTICAST_IF
struct sockaddr_in addr4;
len = sizeof(addr4);
if(enable == flags.multicast)
return errSuccess;
flags.multicast = enable;
if(enable)
getsockname(so, (struct sockaddr *)&addr4, &len);
else
memset(&addr4.sin_addr, 0, sizeof(addr4.sin_addr));
setsockopt(so, IPPROTO_IP, IP_MULTICAST_IF, (char *)&addr4.sin_addr, sizeof(addr4.sin_addr));
return errSuccess;
#endif
default:
return error(errServiceUnavailable,(char *)"Multicast not supported");
}
}
Socket::Error Socket::setTimeToLiveByFamily(unsigned char ttl, Family fam)
{
if(!flags.multicast)
return error(errMulticastDisabled,(char *)"Multicast not enabled on socket");
switch(fam) {
#ifdef CCXX_IPV6
case IPV6:
flags.ttl = ttl;
setsockopt(so, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&ttl, sizeof(ttl));
return errSuccess;
#endif
case IPV4:
#ifdef IP_MULTICAST_TTL
flags.ttl = ttl;
setsockopt(so, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(ttl));
return errSuccess;
#endif
default:
return error(errServiceUnavailable, (char *)"Multicast not supported");
}
}
Socket::Error Socket::setLoopbackByFamily(bool enable, Family family)
{
unsigned char loop;
if(!flags.multicast)
return error(errMulticastDisabled,(char *)"Multicast not enabled on socket");
if(enable)
loop = 1;
else
loop = 0;
flags.loopback = enable;
switch(family) {
#ifdef CCXX_IPV6
case IPV6:
setsockopt(so, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&loop, sizeof(loop));
return errSuccess;
#endif
case IPV4:
#ifdef IP_MULTICAST_LOOP
setsockopt(so, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loop, sizeof(loop));
return errSuccess;
#endif
default:
return error(errServiceUnavailable,(char *)"Multicast not supported");
}
}
Socket::Error Socket::join(const IPV4Multicast &ia)
{
#ifdef IP_ADD_MEMBERSHIP
struct ip_mreq group;
struct sockaddr_in myaddr;
socklen_t len = sizeof(myaddr);
if(!flags.multicast)
return error(errMulticastDisabled,(char *)"Multicast not enabled on socket");
getsockname(so, (struct sockaddr *)&myaddr, &len);
group.imr_interface.s_addr = INADDR_ANY;
group.imr_multiaddr = ia.getAddress();
setsockopt(so, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group));
return errSuccess;
#else
return error(errServiceUnavailable,(char *)"Multicast not supported");
#endif
}
#ifdef CCXX_IPV6
Socket::Error Socket::join(const IPV6Multicast &ia)
{
#ifdef IPV6_ADD_MEMBERSHIP
struct ipv6_mreq group;
struct sockaddr_in6 myaddr;
socklen_t len = sizeof(myaddr);
if(!flags.multicast)
return error(errMulticastDisabled,(char *)"Multicast not enabled on socket");
getsockname(so, (struct sockaddr *)&myaddr, &len);
group.ipv6mr_interface = 0;
group.ipv6mr_multiaddr = ia.getAddress();
setsockopt(so, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&group, sizeof(group));
return errSuccess;
#else
return error(errServiceUnavailable,(char *)"Multicast not supported");
#endif
}
#endif
Socket::Error Socket::drop(const IPV4Multicast &ia)
{
#ifdef IP_DROP_MEMBERSHIP
struct ip_mreq group;
struct sockaddr_in myaddr;
socklen_t len = sizeof(myaddr);
if(!flags.multicast)
return error(errMulticastDisabled,(char *)"Multicast not enabled on socket");
getsockname(so, (struct sockaddr *)&myaddr, &len);
group.imr_interface.s_addr = INADDR_ANY;
group.imr_multiaddr = ia.getAddress();
setsockopt(so, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&group, sizeof(group));
return errSuccess;
#else
return error(errServiceUnavailable,(char *)"Multicast not supported");
#endif
}
#ifdef CCXX_IPV6
Socket::Error Socket::drop(const IPV6Multicast &ia)
{
#ifdef IPV6_DROP_MEMBERSHIP
struct ipv6_mreq group;
struct sockaddr_in6 myaddr;
socklen_t len = sizeof(myaddr);
if(!flags.multicast)
return error(errMulticastDisabled,(char *)"Multicast not enabled on socket");
getsockname(so, (struct sockaddr *)&myaddr, &len);
group.ipv6mr_interface = 0;
group.ipv6mr_multiaddr = ia.getAddress();
setsockopt(so, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, (char *)&group, sizeof(group));
return errSuccess;
#else
return error(errServiceUnavailable,(char *)"Multicast not supported");
#endif
}
#endif
Socket::Error Socket::setRouting(bool enable)
{
int opt = (enable ? 1 : 0);
#ifdef SO_DONTROUTE
if(setsockopt(so, SOL_SOCKET, SO_DONTROUTE,
(char *)&opt, (socklen_t)sizeof(opt)))
return error(errRoutingDenied,(char *)"Could not set dont-route socket option",socket_errno);
#endif
flags.route = enable;
return errSuccess;
}
Socket::Error Socket::setNoDelay(bool enable)
{
int opt = (enable ? 1 : 0);
if(setsockopt(so, IPPROTO_TCP, TCP_NODELAY,
(char *)&opt, (socklen_t)sizeof(opt)))
return error(errNoDelay,(char *)"Could not set tcp-nodelay socket option",socket_errno);
return errSuccess;
}
ssize_t Socket::readLine(char *str, size_t request, timeout_t timeout)
{
bool crlf = false;
bool nl = false;
size_t nleft = request - 1; // leave also space for terminator
int nstat,c;
if(request < 1)
return 0;
str[0] = 0;
while(nleft && !nl) {
if(timeout) {
if(!isPending(pendingInput, timeout)) {
error(errTimeout,(char *)"Read timeout", 0);
return -1;
}
}
nstat = ::recv(so, str, _IOLEN64 nleft, MSG_PEEK);
if(nstat <= 0) {
error(errInput,(char *)"Could not read from socket", socket_errno);
return -1;
}
// FIXME: if unique char in buffer is '\r' return "\r"
// if buffer end in \r try to read another char?
// and if timeout ??
// remember last \r
for(c=0; c < nstat; ++c) {
if(str[c] == '\n') {
if (c > 0 && str[c-1] == '\r')
crlf = true;
++c;
nl = true;
break;
}
}
nstat = ::recv(so, str, _IOLEN64 c, 0);
// TODO: correct ???
if(nstat < 0)
break;
// adjust ending \r\n in \n
if(crlf) {
--nstat;
str[nstat - 1] = '\n';
}
str += nstat;
nleft -= nstat;
}
*str = 0;
return (ssize_t)(request - nleft - 1);
}
ssize_t Socket::readData(void *Target, size_t Size, char Separator, timeout_t timeout)
{
if ((Separator == 0x0D) || (Separator == 0x0A))
return (readLine ((char *) Target, Size, timeout));
if (Size < 1)
return (0);
ssize_t nstat;
if (Separator == 0) { // Flat-out read for a number of bytes.
if (timeout) {
if (!isPending (pendingInput, timeout)) {
error(errTimeout);
return (-1);
}
}
nstat =::recv (so, (char *)Target, _IOLEN64 Size, 0);
if (nstat < 0) {
error (errInput);
return (-1);
}
return (nstat);
}
/////////////////////////////////////////////////////////////
// Otherwise, we have a special char separator to use
/////////////////////////////////////////////////////////////
bool found = false;
size_t nleft = Size;
int c;
char *str = (char *) Target;
memset (str, 0, Size);
while (nleft && !found) {
if (timeout) {
if (!isPending (pendingInput, timeout)) {
error(errTimeout);
return (-1);
}
}
nstat =::recv (so, str, _IOLEN64 nleft, MSG_PEEK);
if (nstat <= 0) {
error (errInput);
return (-1);
}
for (c = 0; (c < nstat) && !found; ++c) {
if (str[c] == Separator)
found = true;
}
memset (str, 0, nleft);
nstat =::recv (so, str, c, 0);
if (nstat < 0)
break;
str += nstat;
nleft -= nstat;
}
return (ssize_t)(Size - (ssize_t) nleft);
}
ssize_t Socket::writeData(const void *Source, size_t Size, timeout_t timeout)
{
if (Size < 1)
return (0);
ssize_t nstat;
const char *Slide = (const char *) Source;
while (true) {
if (timeout) {
if (!isPending (pendingOutput, timeout)) {
error(errOutput);
return (-1);
}
}
nstat =::send (so, Slide, _IOLEN64 Size, MSG_NOSIGNAL);
if (nstat <= 0) {
error(errOutput);
return (-1);
}
Size -= nstat;
Slide += nstat;
if (Size <= 0)
break;
}
return (nstat);
}
const char *Socket::getSystemErrorString(void) const
{
#ifdef CCXX_EXCEPTIONS
SockException e(errstr, errid, syserr);
return e.getSystemErrorString();
#else
return NULL;
#endif
}
bool Socket::isPending(Pending pending, timeout_t timeout)
{
int status = 0;
#ifdef USE_POLL
struct pollfd pfd;
pfd.fd = so;
pfd.revents = 0;
if(so == INVALID_SOCKET)
return true;
switch(pending) {
case pendingInput:
pfd.events = POLLIN;
break;
case pendingOutput:
pfd.events = POLLOUT;
break;
case pendingError:
pfd.events = POLLERR | POLLHUP;
break;
}
status = 0;
while(status < 1) {
if(timeout == TIMEOUT_INF)
status = poll(&pfd, 1, -1);
else
status = poll(&pfd, 1, timeout);
if(status < 1) {
// don't stop polling because of a simple
// signal :)
if(status == -1 && errno == EINTR)
continue;
return false;
}
}
if(pfd.revents & pfd.events)
return true;
return false;
#else
struct timeval tv;
struct timeval *tvp = &tv;
fd_set grp;
if(timeout == TIMEOUT_INF)
tvp = NULL;
else {
tv.tv_usec = (timeout % 1000) * 1000;
tv.tv_sec = timeout / 1000;
}
FD_ZERO(&grp);
SOCKET sosave = so;
if(so == INVALID_SOCKET)
return true;
FD_SET(sosave, &grp);
switch(pending) {
case pendingInput:
status = ::select((int)so + 1, &grp, NULL, NULL, tvp);
break;
case pendingOutput:
status = ::select((int)so + 1, NULL, &grp, NULL, tvp);
break;
case pendingError:
status = ::select((int)so + 1, NULL, NULL, &grp, tvp);
break;
}
if(status < 1)
return false;
if(FD_ISSET(so, &grp))
return true;
return false;
#endif
}
bool Socket::operator!() const
{
return (Socket::state == INITIAL) ? true : false;
}
Socket::operator bool() const
{
return (Socket::state == INITIAL) ? false : true;
}
Socket &Socket::operator=(const Socket &from)
{
if(so == from.so)
return *this;
if(state != INITIAL)
endSocket();
so = dupSocket(from.so,from.state);
if(so == INVALID_SOCKET) {
error(errCopyFailed,(char *)"Could not duplicate socket handle",socket_errno);
state = INITIAL;
}
else
state = from.state;
return *this;
}
bool Socket::check(Family fam)
{
SOCKET so = INVALID_SOCKET;
switch(fam) {
case IPV4:
so = socket(fam, SOCK_DGRAM, IPPROTO_UDP);
break;
#ifdef CCXX_IPV6
case IPV6:
so = socket(fam, SOCK_DGRAM, IPPROTO_UDP);
break;
#endif
default:
return false;
}
if(so == INVALID_SOCKET)
return false;
release(so);
return true;
}
IPV4Host Socket::getIPV4Sender(tpport_t *port) const
{
struct sockaddr_in from;
char buf;
socklen_t len = sizeof(from);
int rc = ::recvfrom(so, &buf, 1, MSG_PEEK,
(struct sockaddr *)&from, &len);
#ifdef _MSWINDOWS_
if(rc < 1 && WSAGetLastError() != WSAEMSGSIZE) {
if(port)
*port = 0;
memset((void*) &from, 0, sizeof(from));
error(errInput,(char *)"Could not read from socket",socket_errno);
}
#else
if(rc < 0) {
if(port)
*port = 0;
memset((void*) &from, 0, sizeof(from));
error(errInput,(char *)"Could not read from socket",socket_errno);
}
#endif
else {
if(rc < 1)
memset((void*) &from, 0, sizeof(from));
if(port)
*port = ntohs(from.sin_port);
}
return IPV4Host(from.sin_addr);
}
#ifdef CCXX_IPV6
IPV6Host Socket::getIPV6Sender(tpport_t *port) const
{
struct sockaddr_in6 from;
char buf;
socklen_t len = sizeof(from);
int rc = ::recvfrom(so, &buf, 1, MSG_PEEK,
(struct sockaddr *)&from, &len);
#ifdef _MSWINDOWS_
if(rc < 1 && WSAGetLastError() != WSAEMSGSIZE) {
if(port)
*port = 0;
memset((void*) &from, 0, sizeof(from));
error(errInput,(char *)"Could not read from socket",socket_errno);
}
#else
if(rc < 0) {
if(port)
*port = 0;
memset((void*) &from, 0, sizeof(from));
error(errInput,(char *)"Could not read from socket",socket_errno);
}
#endif
else {
if(rc < 1)
memset((void*) &from, 0, sizeof(from));
if(port)
*port = ntohs(from.sin6_port);
}
return IPV6Host(from.sin6_addr);
}
#endif
IPV4Host Socket::getIPV4Local(tpport_t *port) const
{
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
if(getsockname(so, (struct sockaddr *)&addr, &len)) {
error(errResourceFailure,(char *)"Could not get socket address",socket_errno);
if(port)
*port = 0;
memset(&addr.sin_addr, 0, sizeof(addr.sin_addr));
}
else {
if(port)
*port = ntohs(addr.sin_port);
}
return IPV4Host(addr.sin_addr);
}
#ifdef CCXX_IPV6
IPV6Host Socket::getIPV6Local(tpport_t *port) const
{
struct sockaddr_in6 addr;
socklen_t len = sizeof(addr);
if(getsockname(so, (struct sockaddr *)&addr, &len)) {
error(errResourceFailure,(char *)"Could not get socket address",socket_errno);
if(port)
*port = 0;
memset(&addr.sin6_addr, 0, sizeof(addr.sin6_addr));
}
else {
if(port)
*port = ntohs(addr.sin6_port);
}
return IPV6Host(addr.sin6_addr);
}
#endif
IPV4Host Socket::getIPV4Peer(tpport_t *port) const
{
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
if(getpeername(so, (struct sockaddr *)&addr, &len)) {
#ifndef _MSWINDOWS_
if(errno == ENOTCONN)
error(errNotConnected,(char *)"Could not get peer address",socket_errno);
else
#endif
error(errResourceFailure,(char *)"Could not get peer address",socket_errno);
if(port)
*port = 0;
memset(&addr.sin_addr, 0, sizeof(addr.sin_addr));
}
else {
if(port)
*port = ntohs(addr.sin_port);
}
return IPV4Host(addr.sin_addr);
}
#ifdef CCXX_IPV6
IPV6Host Socket::getIPV6Peer(tpport_t *port) const
{
struct sockaddr_in6 addr;
socklen_t len = sizeof(addr);
if(getpeername(so, (struct sockaddr *)&addr, &len)) {
#ifndef _MSWINDOWS_
if(errno == ENOTCONN)
error(errNotConnected,(char *)"Could not get peer address",socket_errno);
else
#endif
error(errResourceFailure,(char *)"Could not get peer address",socket_errno);
if(port)
*port = 0;
memset(&addr.sin6_addr, 0, sizeof(addr.sin6_addr));
}
else {
if(port)
*port = ntohs(addr.sin6_port);
}
return IPV6Host(addr.sin6_addr);
}
#endif
void Socket::setCompletion(bool immediate)
{
flags.completion = immediate;
#ifdef _MSWINDOWS_
unsigned long flag;
// note that this will not work on some versions of Windows for Workgroups. Tough. -- jfc
switch( immediate ) {
case false:
// this will not work if you are using WSAAsyncSelect or WSAEventSelect.
// -- perhaps I should throw an exception
flag = 1;
// ioctlsocket( so, FIONBIO, (unsigned long *) 1);
break;
case true:
flag = 0;
// ioctlsocket( so, FIONBIO, (unsigned long *) 0);
break;
}
ioctlsocket(so, FIONBIO, &flag);
#else
int fflags = fcntl(so, F_GETFL);
if(immediate) {
fflags &=~ O_NONBLOCK;
fcntl(so, F_SETFL, fflags);
}
else {
fflags |= O_NONBLOCK;
fcntl(so, F_SETFL, fflags);
}
#endif
}
Socket::Error Socket::setKeepAlive(bool enable)
{
int opt = (enable ? ~0: 0);
#if (defined(SO_KEEPALIVE) || defined(_MSWINDOWS_))
if(setsockopt(so, SOL_SOCKET, SO_KEEPALIVE,
(char *)&opt, (socklen_t)sizeof(opt)))
return error(errKeepaliveDenied,(char *)"Could not set socket keep-alive option",socket_errno);
#endif
flags.keepalive = enable;
return errSuccess;
}
Socket::Error Socket::setLinger(bool linger)
{
#ifdef SO_LINGER
flags.linger = linger;
return errSuccess;
#else
return error(errServiceUnavailable,(char *)"Socket lingering not supported");
#endif
}
Socket::Error Socket::setTypeOfService(Tos service)
{
#ifdef SOL_IP
unsigned char tos;
switch(service) {
#ifdef IPTOS_LOWDELAY
case tosLowDelay:
tos = IPTOS_LOWDELAY;
break;
#endif
#ifdef IPTOS_THROUGHPUT
case tosThroughput:
tos = IPTOS_THROUGHPUT;
break;
#endif
#ifdef IPTOS_RELIABILITY
case tosReliability:
tos = IPTOS_RELIABILITY;
break;
#endif
#ifdef IPTOS_MINCOST
case tosMinCost:
tos = IPTOS_MINCOST;
break;
#endif
default:
return error(errServiceUnavailable,(char *)"Unknown type-of-service");
}
if(setsockopt(so, SOL_IP, IP_TOS,(char *)&tos, (socklen_t)sizeof(tos)))
return error(errServiceDenied,(char *)"Could not set type-of-service",socket_errno);
return errSuccess;
#else
return error(errServiceUnavailable,(char *)"Socket type-of-service not supported",socket_errno);
#endif
}
bool Socket::isConnected(void) const
{
return (Socket::state == CONNECTED) ? true : false;
}
bool Socket::isActive(void) const
{
return (state != INITIAL) ? true : false;
}