diff options
Diffstat (limited to 'tdecore/network/ksockssocketdevice.cpp')
-rw-r--r-- | tdecore/network/ksockssocketdevice.cpp | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/tdecore/network/ksockssocketdevice.cpp b/tdecore/network/ksockssocketdevice.cpp new file mode 100644 index 000000000..3432ef84f --- /dev/null +++ b/tdecore/network/ksockssocketdevice.cpp @@ -0,0 +1,487 @@ +/* -*- 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::tqreadBlock(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::tqreadBlock(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::tqwriteBlock(const char *data, TQ_ULONG len) +{ + return tqwriteBlock(data, len, KSocketAddress()); +} + +TQ_LONG KSocksSocketDevice::tqwriteBlock(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 |