diff options
Diffstat (limited to 'kdecore/network/ksocketdevice.cpp')
-rw-r--r-- | kdecore/network/ksocketdevice.cpp | 886 |
1 files changed, 886 insertions, 0 deletions
diff --git a/kdecore/network/ksocketdevice.cpp b/kdecore/network/ksocketdevice.cpp new file mode 100644 index 000000000..b3004ccc2 --- /dev/null +++ b/kdecore/network/ksocketdevice.cpp @@ -0,0 +1,886 @@ +/* -*- C++ -*- + * Copyright (C) 2003,2005 Thiago Macieira <thiago.macieira@kdemail.net> + * + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <config.h> + +#include <qmap.h> + +#ifdef USE_SOLARIS +# include <sys/filio.h> +#endif +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <unistd.h> + +#ifdef HAVE_POLL +# include <sys/poll.h> +#else +# ifdef HAVE_SYS_SELECT +# include <sys/select.h> +# endif +#endif + +// Include syssocket before our local includes +#include "syssocket.h" + +#include <qmutex.h> +#include <qsocketnotifier.h> + +#include "kresolver.h" +#include "ksocketaddress.h" +#include "ksocketbase.h" +#include "ksocketdevice.h" +#include "ksockssocketdevice.h" + +using namespace KNetwork; + +class KNetwork::KSocketDevicePrivate +{ +public: + mutable QSocketNotifier *input, *output, *exception; + KSocketAddress local, peer; + int af; + + inline KSocketDevicePrivate() + { + input = output = exception = 0L; + af = 0; + } +}; + + +KSocketDevice::KSocketDevice(const KSocketBase* parent) + : m_sockfd(-1), d(new KSocketDevicePrivate) +{ + setSocketDevice(this); + if (parent) + setSocketOptions(parent->socketOptions()); +} + +KSocketDevice::KSocketDevice(int fd) + : m_sockfd(fd), d(new KSocketDevicePrivate) +{ + setState(IO_Open); + setFlags(IO_Sequential | IO_Raw | IO_ReadWrite); + setSocketDevice(this); + d->af = localAddress().family(); +} + +KSocketDevice::KSocketDevice(bool, const KSocketBase* parent) + : m_sockfd(-1), d(new KSocketDevicePrivate) +{ + // do not set parent + if (parent) + setSocketOptions(parent->socketOptions()); +} + +KSocketDevice::~KSocketDevice() +{ + close(); // deletes the notifiers + unsetSocketDevice(); // prevent double deletion + delete d; +} + +bool KSocketDevice::setSocketOptions(int opts) +{ + // must call parent + QMutexLocker locker(mutex()); + KSocketBase::setSocketOptions(opts); + + if (m_sockfd == -1) + return true; // flags are stored + + { + int fdflags = fcntl(m_sockfd, F_GETFL, 0); + if (fdflags == -1) + { + setError(IO_UnspecifiedError, UnknownError); + return false; // error + } + + if (opts & Blocking) + fdflags &= ~O_NONBLOCK; + else + fdflags |= O_NONBLOCK; + + if (fcntl(m_sockfd, F_SETFL, fdflags) == -1) + { + setError(IO_UnspecifiedError, UnknownError); + return false; // error + } + } + + { + int on = opts & AddressReuseable ? 1 : 0; + if (setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)) == -1) + { + setError(IO_UnspecifiedError, UnknownError); + return false; // error + } + } + +#if defined(IPV6_V6ONLY) && defined(AF_INET6) + if (d->af == AF_INET6) + { + // don't try this on non-IPv6 sockets, or we'll get an error + + int on = opts & IPv6Only ? 1 : 0; + if (setsockopt(m_sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&on, sizeof(on)) == -1) + { + setError(IO_UnspecifiedError, UnknownError); + return false; // error + } + } +#endif + + { + int on = opts & Broadcast ? 1 : 0; + if (setsockopt(m_sockfd, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on)) == -1) + { + setError(IO_UnspecifiedError, UnknownError); + return false; // error + } + } + + return true; // all went well +} + +bool KSocketDevice::open(int) +{ + resetError(); + return false; +} + +void KSocketDevice::close() +{ + resetError(); + if (m_sockfd != -1) + { + delete d->input; + delete d->output; + delete d->exception; + + d->input = d->output = d->exception = 0L; + + d->local.setFamily(AF_UNSPEC); + d->peer.setFamily(AF_UNSPEC); + + ::close(m_sockfd); + } + setState(0); + + m_sockfd = -1; +} + +bool KSocketDevice::create(int family, int type, int protocol) +{ + resetError(); + + if (m_sockfd != -1) + { + // it's already created! + setError(IO_SocketCreateError, AlreadyCreated); + return false; + } + + // no socket yet; we have to create it + m_sockfd = kde_socket(family, type, protocol); + + if (m_sockfd == -1) + { + setError(IO_SocketCreateError, NotSupported); + return false; + } + + d->af = family; + setSocketOptions(socketOptions()); + setState(IO_Open); + return true; // successfully created +} + +bool KSocketDevice::create(const KResolverEntry& address) +{ + return create(address.family(), address.socketType(), address.protocol()); +} + +bool KSocketDevice::bind(const KResolverEntry& address) +{ + resetError(); + + if (m_sockfd == -1 && !create(address)) + return false; // failed creating + + // we have a socket, so try and bind + if (kde_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 KSocketDevice::listen(int backlog) +{ + if (m_sockfd != -1) + { + if (kde_listen(m_sockfd, backlog) == -1) + { + setError(IO_ListenError, NotSupported); + return false; + } + + resetError(); + setFlags(IO_Sequential | IO_Raw | IO_ReadWrite); + return true; + } + + // we don't have a socket + // can't listen + setError(IO_ListenError, NotCreated); + return false; +} + +bool KSocketDevice::connect(const KResolverEntry& address) +{ + resetError(); + + if (m_sockfd == -1 && !create(address)) + return false; // failed creating! + + if (kde_connect(m_sockfd, address.address(), address.length()) == -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); + return true; // all is well +} + +KSocketDevice* KSocketDevice::accept() +{ + if (m_sockfd == -1) + { + // can't accept without a socket + setError(IO_AcceptError, NotCreated); + return 0L; + } + + struct sockaddr sa; + socklen_t len = sizeof(sa); + int newfd = kde_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 KSocketDevice(newfd); +} + +bool KSocketDevice::disconnect() +{ + resetError(); + + if (m_sockfd == -1) + return false; // can't create + + KSocketAddress address; + address.setFamily(AF_UNSPEC); + if (kde_connect(m_sockfd, address.address(), address.length()) == -1) + { + if (errno == EALREADY || errno == EINPROGRESS) + { + setError(IO_ConnectError, InProgress); + return false; + } + 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 +} + +Q_LONG KSocketDevice::bytesAvailable() const +{ + if (m_sockfd == -1) + return -1; // there's nothing to read in a closed socket + + int nchars; + if (ioctl(m_sockfd, FIONREAD, &nchars) == -1) + return -1; // error! + + return nchars; +} + +Q_LONG KSocketDevice::waitForMore(int msecs, bool *timeout) +{ + if (m_sockfd == -1) + return -1; // there won't ever be anything to read... + + bool input; + if (!poll(&input, 0, 0, msecs, timeout)) + return -1; // failed polling + + return bytesAvailable(); +} + +static int do_read_common(int sockfd, char *data, Q_ULONG maxlen, KSocketAddress* from, ssize_t &retval, bool peek = false) +{ + socklen_t len; + if (from) + { + from->setLength(len = 128); // arbitrary length + retval = ::recvfrom(sockfd, data, maxlen, peek ? MSG_PEEK : 0, from->address(), &len); + } + else + retval = ::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 (retval == 0) + return KSocketDevice::RemotelyDisconnected; + + if (from) + from->setLength(len); + return 0; +} + +Q_LONG KSocketDevice::readBlock(char *data, Q_ULONG maxlen) +{ + resetError(); + if (m_sockfd == -1) + return -1; + + if (maxlen == 0 || data == 0L) + return 0; // can't read + + ssize_t retval; + int err = do_read_common(m_sockfd, data, maxlen, 0L, retval); + + if (err) + { + setError(IO_ReadError, static_cast<SocketError>(err)); + return -1; + } + + return retval; +} + +Q_LONG KSocketDevice::readBlock(char *data, Q_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 = do_read_common(m_sockfd, data, maxlen, &from, retval); + + if (err) + { + setError(IO_ReadError, static_cast<SocketError>(err)); + return -1; + } + + return retval; +} + +Q_LONG KSocketDevice::peekBlock(char *data, Q_ULONG maxlen) +{ + resetError(); + if (m_sockfd == -1) + return -1; + + if (maxlen == 0 || data == 0L) + return 0; // can't read + + ssize_t retval; + int err = do_read_common(m_sockfd, data, maxlen, 0L, retval, true); + + if (err) + { + setError(IO_ReadError, static_cast<SocketError>(err)); + return -1; + } + + return retval; +} + +Q_LONG KSocketDevice::peekBlock(char *data, Q_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 = do_read_common(m_sockfd, data, maxlen, &from, retval, true); + + if (err) + { + setError(IO_ReadError, static_cast<SocketError>(err)); + return -1; + } + + return retval; +} + +Q_LONG KSocketDevice::writeBlock(const char *data, Q_ULONG len) +{ + return writeBlock(data, len, KSocketAddress()); +} + +Q_LONG KSocketDevice::writeBlock(const char *data, Q_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 = ::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 + } + else if (retval == 0) + setError(IO_WriteError, RemotelyDisconnected); + + return retval; +} + +KSocketAddress KSocketDevice::localAddress() const +{ + if (m_sockfd == -1) + return KSocketAddress(); // not open, empty value + + if (d->local.family() != AF_UNSPEC) + return d->local; + + socklen_t len; + KSocketAddress localAddress; + localAddress.setLength(len = 32); // arbitrary value + if (kde_getsockname(m_sockfd, localAddress.address(), &len) == -1) + // error! + return d->local = KSocketAddress(); + +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + len = localAddress.address()->sa_len; +#endif + + if (len <= localAddress.length()) + { + // it has fit already + localAddress.setLength(len); + return d->local = localAddress; + } + + // no, the socket address is actually larger than we had anticipated + // call again + localAddress.setLength(len); + if (kde_getsockname(m_sockfd, localAddress.address(), &len) == -1) + // error! + return d->local = KSocketAddress(); + + return d->local = localAddress; +} + +KSocketAddress KSocketDevice::peerAddress() const +{ + if (m_sockfd == -1) + return KSocketAddress(); // not open, empty value + + if (d->peer.family() != AF_UNSPEC) + return d->peer; + + socklen_t len; + KSocketAddress peerAddress; + peerAddress.setLength(len = 32); // arbitrary value + if (kde_getpeername(m_sockfd, peerAddress.address(), &len) == -1) + // error! + return d->peer = KSocketAddress(); + +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + len = peerAddress.address()->sa_len; +#endif + + if (len <= peerAddress.length()) + { + // it has fit already + peerAddress.setLength(len); + return d->peer = peerAddress; + } + + // no, the socket address is actually larger than we had anticipated + // call again + peerAddress.setLength(len); + if (kde_getpeername(m_sockfd, peerAddress.address(), &len) == -1) + // error! + return d->peer = KSocketAddress(); + + return d->peer = peerAddress; +} + +KSocketAddress KSocketDevice::externalAddress() const +{ + // for normal sockets, the externally visible address is the same + // as the local address + return localAddress(); +} + +QSocketNotifier* KSocketDevice::readNotifier() const +{ + if (d->input) + return d->input; + + QMutexLocker locker(mutex()); + if (d->input) + return d->input; + + if (m_sockfd == -1) + { + // socket doesn't exist; can't create notifier + return 0L; + } + + return d->input = createNotifier(QSocketNotifier::Read); +} + +QSocketNotifier* KSocketDevice::writeNotifier() const +{ + if (d->output) + return d->output; + + QMutexLocker locker(mutex()); + if (d->output) + return d->output; + + if (m_sockfd == -1) + { + // socket doesn't exist; can't create notifier + return 0L; + } + + return d->output = createNotifier(QSocketNotifier::Write); +} + +QSocketNotifier* KSocketDevice::exceptionNotifier() const +{ + if (d->exception) + return d->exception; + + QMutexLocker locker(mutex()); + if (d->exception) + return d->exception; + + if (m_sockfd == -1) + { + // socket doesn't exist; can't create notifier + return 0L; + } + + return d->exception = createNotifier(QSocketNotifier::Exception); +} + +bool KSocketDevice::poll(bool *input, bool *output, bool *exception, + int timeout, bool* timedout) +{ + if (m_sockfd == -1) + { + setError(IO_UnspecifiedError, NotCreated); + return false; + } + + resetError(); +#ifdef HAVE_POLL + struct pollfd fds; + fds.fd = m_sockfd; + fds.events = 0; + + if (input) + { + fds.events |= POLLIN; + *input = false; + } + if (output) + { + fds.events |= POLLOUT; + *output = false; + } + if (exception) + { + fds.events |= POLLPRI; + *exception = false; + } + + int retval = ::poll(&fds, 1, timeout); + if (retval == -1) + { + setError(IO_UnspecifiedError, UnknownError); + return false; + } + if (retval == 0) + { + // timeout + if (timedout) + *timedout = true; + return true; + } + + if (input && fds.revents & POLLIN) + *input = true; + if (output && fds.revents & POLLOUT) + *output = true; + if (exception && fds.revents & POLLPRI) + *exception = true; + if (timedout) + *timedout = false; + + return true; +#else + /* + * We don't have poll(2). We'll have to make do with select(2). + */ + + 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 = 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; +#endif +} + +bool KSocketDevice::poll(int timeout, bool *timedout) +{ + bool input, output, exception; + return poll(&input, &output, &exception, timeout, timedout); +} + +QSocketNotifier* KSocketDevice::createNotifier(QSocketNotifier::Type type) const +{ + if (m_sockfd == -1) + return 0L; + + return new QSocketNotifier(m_sockfd, type); +} + +namespace +{ + // simple class to avoid pointer stuff + template<class T> class ptr + { + typedef T type; + type* obj; + public: + ptr() : obj(0) + { } + + ptr(const ptr<T>& other) : obj(other.obj) + { } + + ptr(type* _obj) : obj(_obj) + { } + + ~ptr() + { } + + ptr<T>& operator=(const ptr<T>& other) + { obj = other.obj; return *this; } + + ptr<T>& operator=(T* _obj) + { obj = _obj; return *this; } + + type* operator->() const { return obj; } + + operator T*() const { return obj; } + + bool isNull() const + { return obj == 0; } + }; +} + +static KSocketDeviceFactoryBase* defaultImplFactory; +static QMutex defaultImplFactoryMutex; +typedef QMap<int, KSocketDeviceFactoryBase* > factoryMap; +static factoryMap factories; + +KSocketDevice* KSocketDevice::createDefault(KSocketBase* parent) +{ + KSocketDevice* device = dynamic_cast<KSocketDevice*>(parent); + if (device != 0L) + return device; + + KSocksSocketDevice::initSocks(); + + if (defaultImplFactory) + return defaultImplFactory->create(parent); + + // the really default + return new KSocketDevice(parent); +} + +KSocketDevice* KSocketDevice::createDefault(KSocketBase* parent, int capabilities) +{ + KSocketDevice* device = dynamic_cast<KSocketDevice*>(parent); + if (device != 0L) + return device; + + QMutexLocker locker(&defaultImplFactoryMutex); + factoryMap::ConstIterator it = factories.constBegin(); + for ( ; it != factories.constEnd(); ++it) + if ((it.key() & capabilities) == capabilities) + // found a match + return it.data()->create(parent); + + return 0L; // no default +} + +KSocketDeviceFactoryBase* +KSocketDevice::setDefaultImpl(KSocketDeviceFactoryBase* factory) +{ + QMutexLocker locker(&defaultImplFactoryMutex); + KSocketDeviceFactoryBase* old = defaultImplFactory; + defaultImplFactory = factory; + return old; +} + +void KSocketDevice::addNewImpl(KSocketDeviceFactoryBase* factory, int capabilities) +{ + QMutexLocker locker(&defaultImplFactoryMutex); + if (factories.contains(capabilities)) + delete factories[capabilities]; + factories.insert(capabilities, factory); +} + |