You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/kdecore/network/ksockssocketdevice.cpp

488 lines
11 KiB

/* -*- C++ -*-
* Copyright (C) 2004 Thiago Macieira <thiago.macieira@kdemail.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <config.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
#ifdef __CYGWIN__
#undef kde_socklen_t
#define kde_socklen_t ksocklen_t
#endif
#include "kapplication.h"
#include "ksocks.h"
#include "ksocketaddress.h"
#include "kresolver.h"
#include "ksockssocketdevice.h"
using namespace KNetwork;
// constructor
// nothing to do
KSocksSocketDevice::KSocksSocketDevice(const KSocketBase* obj)
: KSocketDevice(obj)
{
}
// constructor with argument
// nothing to do
KSocksSocketDevice::KSocksSocketDevice(int fd)
: KSocketDevice(fd)
{
}
// destructor
// also nothing to do
KSocksSocketDevice::~KSocksSocketDevice()
{
}
// returns the capabilities
int KSocksSocketDevice::capabilities() const
{
return 0; // can do everything!
}
// From here on, the code is almost exactly a copy of KSocketDevice
// the differences are the use of KSocks where appropriate
bool KSocksSocketDevice::bind(const KResolverEntry& address)
{
resetError();
if (m_sockfd == -1 && !create(address))
return false; // failed creating
// we have a socket, so try and bind
if (KSocks::self()->bind(m_sockfd, address.address(), address.length()) == -1)
{
if (errno == EADDRINUSE)
setError(IO_BindError, AddressInUse);
else if (errno == EINVAL)
setError(IO_BindError, AlreadyBound);
else
// assume the address is the cause
setError(IO_BindError, NotSupported);
return false;
}
return true;
}
bool KSocksSocketDevice::listen(int backlog)
{
if (m_sockfd != -1)
{
if (KSocks::self()->listen(m_sockfd, backlog) == -1)
{
setError(IO_ListenError, NotSupported);
return false;
}
resetError();
setFlags(IO_Sequential | IO_Raw | IO_ReadWrite);
setState(IO_Open);
return true;
}
// we don't have a socket
// can't listen
setError(IO_ListenError, NotCreated);
return false;
}
bool KSocksSocketDevice::connect(const KResolverEntry& address)
{
resetError();
if (m_sockfd == -1 && !create(address))
return false; // failed creating!
int retval;
if (KSocks::self()->hasWorkingAsyncConnect())
retval = KSocks::self()->connect(m_sockfd, address.address(),
address.length());
else
{
// work around some SOCKS implementation bugs
// we will do a *synchronous* connection here!
// FIXME: KDE4, write a proper SOCKS implementation
bool isBlocking = blocking();
setBlocking(true);
retval = KSocks::self()->connect(m_sockfd, address.address(),
address.length());
setBlocking(isBlocking);
}
if (retval == -1)
{
if (errno == EISCONN)
return true; // we're already connected
else if (errno == EALREADY || errno == EINPROGRESS)
{
setError(IO_ConnectError, InProgress);
return true;
}
else if (errno == ECONNREFUSED)
setError(IO_ConnectError, ConnectionRefused);
else if (errno == ENETDOWN || errno == ENETUNREACH ||
errno == ENETRESET || errno == ECONNABORTED ||
errno == ECONNRESET || errno == EHOSTDOWN ||
errno == EHOSTUNREACH)
setError(IO_ConnectError, NetFailure);
else
setError(IO_ConnectError, NotSupported);
return false;
}
setFlags(IO_Sequential | IO_Raw | IO_ReadWrite);
setState(IO_Open);
return true; // all is well
}
KSocksSocketDevice* KSocksSocketDevice::accept()
{
if (m_sockfd == -1)
{
// can't accept without a socket
setError(IO_AcceptError, NotCreated);
return 0L;
}
struct sockaddr sa;
kde_socklen_t len = sizeof(sa);
int newfd = KSocks::self()->accept(m_sockfd, &sa, &len);
if (newfd == -1)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
setError(IO_AcceptError, WouldBlock);
else
setError(IO_AcceptError, UnknownError);
return NULL;
}
return new KSocksSocketDevice(newfd);
}
static int socks_read_common(int sockfd, char *data, TQ_ULONG maxlen, KSocketAddress* from, ssize_t &retval, bool peek = false)
{
kde_socklen_t len;
if (from)
{
from->setLength(len = 128); // arbitrary length
retval = KSocks::self()->recvfrom(sockfd, data, maxlen, peek ? MSG_PEEK : 0, from->address(), &len);
}
else
retval = KSocks::self()->recvfrom(sockfd, data, maxlen, peek ? MSG_PEEK : 0, NULL, NULL);
if (retval == -1)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
return KSocketDevice::WouldBlock;
else
return KSocketDevice::UnknownError;
}
if (from)
from->setLength(len);
return 0;
}
TQ_LONG KSocksSocketDevice::readBlock(char *data, TQ_ULONG maxlen)
{
resetError();
if (m_sockfd == -1)
return -1;
if (maxlen == 0 || data == 0L)
return 0; // can't read
ssize_t retval;
int err = socks_read_common(m_sockfd, data, maxlen, 0L, retval);
if (err)
{
setError(IO_ReadError, static_cast<SocketError>(err));
return -1;
}
return retval;
}
TQ_LONG KSocksSocketDevice::readBlock(char *data, TQ_ULONG maxlen, KSocketAddress &from)
{
resetError();
if (m_sockfd == -1)
return -1; // nothing to do here
if (data == 0L || maxlen == 0)
return 0; // user doesn't want to read
ssize_t retval;
int err = socks_read_common(m_sockfd, data, maxlen, &from, retval);
if (err)
{
setError(IO_ReadError, static_cast<SocketError>(err));
return -1;
}
return retval;
}
TQ_LONG KSocksSocketDevice::peekBlock(char *data, TQ_ULONG maxlen)
{
resetError();
if (m_sockfd == -1)
return -1;
if (maxlen == 0 || data == 0L)
return 0; // can't read
ssize_t retval;
int err = socks_read_common(m_sockfd, data, maxlen, 0L, retval, true);
if (err)
{
setError(IO_ReadError, static_cast<SocketError>(err));
return -1;
}
return retval;
}
TQ_LONG KSocksSocketDevice::peekBlock(char *data, TQ_ULONG maxlen, KSocketAddress& from)
{
resetError();
if (m_sockfd == -1)
return -1; // nothing to do here
if (data == 0L || maxlen == 0)
return 0; // user doesn't want to read
ssize_t retval;
int err = socks_read_common(m_sockfd, data, maxlen, &from, retval, true);
if (err)
{
setError(IO_ReadError, static_cast<SocketError>(err));
return -1;
}
return retval;
}
TQ_LONG KSocksSocketDevice::writeBlock(const char *data, TQ_ULONG len)
{
return writeBlock(data, len, KSocketAddress());
}
TQ_LONG KSocksSocketDevice::writeBlock(const char *data, TQ_ULONG len, const KSocketAddress& to)
{
resetError();
if (m_sockfd == -1)
return -1; // can't write to unopen socket
if (data == 0L || len == 0)
return 0; // nothing to be written
ssize_t retval = KSocks::self()->sendto(m_sockfd, data, len, 0, to.address(), to.length());
if (retval == -1)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
setError(IO_WriteError, WouldBlock);
else
setError(IO_WriteError, UnknownError);
return -1; // nothing written
}
return retval;
}
KSocketAddress KSocksSocketDevice::localAddress() const
{
if (m_sockfd == -1)
return KSocketAddress(); // not open, empty value
kde_socklen_t len;
KSocketAddress localAddress;
localAddress.setLength(len = 32); // arbitrary value
if (KSocks::self()->getsockname(m_sockfd, localAddress.address(), &len) == -1)
// error!
return KSocketAddress();
if (len <= localAddress.length())
{
// it has fit already
localAddress.setLength(len);
return localAddress;
}
// no, the socket address is actually larger than we had anticipated
// call again
localAddress.setLength(len);
if (KSocks::self()->getsockname(m_sockfd, localAddress.address(), &len) == -1)
// error!
return KSocketAddress();
return localAddress;
}
KSocketAddress KSocksSocketDevice::peerAddress() const
{
if (m_sockfd == -1)
return KSocketAddress(); // not open, empty value
kde_socklen_t len;
KSocketAddress peerAddress;
peerAddress.setLength(len = 32); // arbitrary value
if (KSocks::self()->getpeername(m_sockfd, peerAddress.address(), &len) == -1)
// error!
return KSocketAddress();
if (len <= peerAddress.length())
{
// it has fit already
peerAddress.setLength(len);
return peerAddress;
}
// no, the socket address is actually larger than we had anticipated
// call again
peerAddress.setLength(len);
if (KSocks::self()->getpeername(m_sockfd, peerAddress.address(), &len) == -1)
// error!
return KSocketAddress();
return peerAddress;
}
KSocketAddress KSocksSocketDevice::externalAddress() const
{
// return empty, indicating unknown external address
return KSocketAddress();
}
bool KSocksSocketDevice::poll(bool *input, bool *output, bool *exception,
int timeout, bool *timedout)
{
if (m_sockfd == -1)
{
setError(IO_UnspecifiedError, NotCreated);
return false;
}
resetError();
fd_set readfds, writefds, exceptfds;
fd_set *preadfds = 0L, *pwritefds = 0L, *pexceptfds = 0L;
if (input)
{
preadfds = &readfds;
FD_ZERO(preadfds);
FD_SET(m_sockfd, preadfds);
*input = false;
}
if (output)
{
pwritefds = &writefds;
FD_ZERO(pwritefds);
FD_SET(m_sockfd, pwritefds);
*output = false;
}
if (exception)
{
pexceptfds = &exceptfds;
FD_ZERO(pexceptfds);
FD_SET(m_sockfd, pexceptfds);
*exception = false;
}
int retval;
if (timeout < 0)
retval = KSocks::self()->select(m_sockfd + 1, preadfds, pwritefds, pexceptfds, 0L);
else
{
// convert the milliseconds to timeval
struct timeval tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = timeout % 1000 * 1000;
retval = select(m_sockfd + 1, preadfds, pwritefds, pexceptfds, &tv);
}
if (retval == -1)
{
setError(IO_UnspecifiedError, UnknownError);
return false;
}
if (retval == 0)
{
// timeout
if (timedout)
*timedout = true;
return true;
}
if (input && FD_ISSET(m_sockfd, preadfds))
*input = true;
if (output && FD_ISSET(m_sockfd, pwritefds))
*output = true;
if (exception && FD_ISSET(m_sockfd, pexceptfds))
*exception = true;
return true;
}
void KSocksSocketDevice::initSocks()
{
static bool init = false;
if (init)
return;
if (kapp == 0L)
return; // no KApplication, so don't initialise
// this should, however, test for KInstance
init = true;
if (KSocks::self()->hasSocks())
delete KSocketDevice::setDefaultImpl(new KSocketDeviceFactory<KSocksSocketDevice>);
}
#if 0
static bool register()
{
KSocketDevice::addNewImpl(new KSocketDeviceFactory<KSocksSocketDevice>, 0);
}
static bool register = registered();
#endif