summaryrefslogtreecommitdiffstats
path: root/tdecore/network/ksockssocketdevice.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tdecore/network/ksockssocketdevice.cpp')
-rw-r--r--tdecore/network/ksockssocketdevice.cpp487
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