From bcb704366cb5e333a626c18c308c7e0448a8e69f Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- .../jabber/libiris/cutestuff/network/Makefile.am | 16 + .../jabber/libiris/cutestuff/network/bsocket.cpp | 394 +++++++ .../jabber/libiris/cutestuff/network/bsocket.h | 87 ++ .../libiris/cutestuff/network/httpconnect.cpp | 369 ++++++ .../jabber/libiris/cutestuff/network/httpconnect.h | 67 ++ .../jabber/libiris/cutestuff/network/httppoll.cpp | 666 +++++++++++ .../jabber/libiris/cutestuff/network/httppoll.h | 104 ++ .../jabber/libiris/cutestuff/network/ndns.cpp | 378 ++++++ .../jabber/libiris/cutestuff/network/ndns.h | 88 ++ .../jabber/libiris/cutestuff/network/servsock.cpp | 112 ++ .../jabber/libiris/cutestuff/network/servsock.h | 68 ++ .../jabber/libiris/cutestuff/network/socks.cpp | 1223 ++++++++++++++++++++ .../jabber/libiris/cutestuff/network/socks.h | 160 +++ .../libiris/cutestuff/network/srvresolver.cpp | 320 +++++ .../jabber/libiris/cutestuff/network/srvresolver.h | 65 ++ 15 files changed, 4117 insertions(+) create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/ndns.h create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/servsock.h create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/socks.h create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h (limited to 'kopete/protocols/jabber/libiris/cutestuff/network') diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am b/kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am new file mode 100644 index 00000000..5e370089 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am @@ -0,0 +1,16 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libcutestuff_network.la +INCLUDES = -I$(srcdir)/../util -I$(srcdir)/../../qca/src $(all_includes) + +libcutestuff_network_la_SOURCES = \ + bsocket.cpp \ + httpconnect.cpp \ + httppoll.cpp \ + ndns.cpp \ + servsock.cpp \ + socks.cpp \ + srvresolver.cpp + +KDE_OPTIONS = nofinal + diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp new file mode 100644 index 00000000..57e5fe66 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp @@ -0,0 +1,394 @@ +/* + * bsocket.cpp - QSocket wrapper based on Bytestream with SRV DNS support + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include"bsocket.h" + +#include +#include +#include +#include +#include"safedelete.h" +#ifndef NO_NDNS +#include"ndns.h" +#endif +#include"srvresolver.h" + +#ifdef BS_DEBUG +#include +#endif + +#define READBUFSIZE 65536 + +// CS_NAMESPACE_BEGIN + +class BSocket::Private +{ +public: + Private() + { + qsock = 0; + } + + QSocket *qsock; + int state; + +#ifndef NO_NDNS + NDns ndns; +#endif + SrvResolver srv; + QString host; + int port; + SafeDelete sd; +}; + +BSocket::BSocket(QObject *parent) +:ByteStream(parent) +{ + d = new Private; +#ifndef NO_NDNS + connect(&d->ndns, SIGNAL(resultsReady()), SLOT(ndns_done())); +#endif + connect(&d->srv, SIGNAL(resultsReady()), SLOT(srv_done())); + + reset(); +} + +BSocket::~BSocket() +{ + reset(true); + delete d; +} + +void BSocket::reset(bool clear) +{ + if(d->qsock) { + d->qsock->disconnect(this); + + if(!clear && d->qsock->isOpen()) { + // move remaining into the local queue + QByteArray block(d->qsock->bytesAvailable()); + d->qsock->readBlock(block.data(), block.size()); + appendRead(block); + } + + d->sd.deleteLater(d->qsock); + d->qsock = 0; + } + else { + if(clear) + clearReadBuffer(); + } + + if(d->srv.isBusy()) + d->srv.stop(); +#ifndef NO_NDNS + if(d->ndns.isBusy()) + d->ndns.stop(); +#endif + d->state = Idle; +} + +void BSocket::ensureSocket() +{ + if(!d->qsock) { + d->qsock = new QSocket; +#if QT_VERSION >= 0x030200 + d->qsock->setReadBufferSize(READBUFSIZE); +#endif + connect(d->qsock, SIGNAL(hostFound()), SLOT(qs_hostFound())); + connect(d->qsock, SIGNAL(connected()), SLOT(qs_connected())); + connect(d->qsock, SIGNAL(connectionClosed()), SLOT(qs_connectionClosed())); + connect(d->qsock, SIGNAL(delayedCloseFinished()), SLOT(qs_delayedCloseFinished())); + connect(d->qsock, SIGNAL(readyRead()), SLOT(qs_readyRead())); + connect(d->qsock, SIGNAL(bytesWritten(int)), SLOT(qs_bytesWritten(int))); + connect(d->qsock, SIGNAL(error(int)), SLOT(qs_error(int))); + } +} + +void BSocket::connectToHost(const QString &host, Q_UINT16 port) +{ + reset(true); + d->host = host; + d->port = port; +#ifdef NO_NDNS + d->state = Connecting; + do_connect(); +#else + d->state = HostLookup; + d->ndns.resolve(d->host); +#endif +} + +void BSocket::connectToServer(const QString &srv, const QString &type) +{ + reset(true); + d->state = HostLookup; + d->srv.resolve(srv, type, "tcp"); +} + +int BSocket::socket() const +{ + if(d->qsock) + return d->qsock->socket(); + else + return -1; +} + +void BSocket::setSocket(int s) +{ + reset(true); + ensureSocket(); + d->state = Connected; + d->qsock->setSocket(s); +} + +int BSocket::state() const +{ + return d->state; +} + +bool BSocket::isOpen() const +{ + if(d->state == Connected) + return true; + else + return false; +} + +void BSocket::close() +{ + if(d->state == Idle) + return; + + if(d->qsock) { + d->qsock->close(); + d->state = Closing; + if(d->qsock->bytesToWrite() == 0) + reset(); + } + else { + reset(); + } +} + +void BSocket::write(const QByteArray &a) +{ + if(d->state != Connected) + return; +#ifdef BS_DEBUG + QCString cs; + cs.resize(a.size()+1); + memcpy(cs.data(), a.data(), a.size()); + QString s = QString::fromUtf8(cs); + fprintf(stderr, "BSocket: writing [%d]: {%s}\n", a.size(), cs.data()); +#endif + d->qsock->writeBlock(a.data(), a.size()); +} + +QByteArray BSocket::read(int bytes) +{ + QByteArray block; + if(d->qsock) { + int max = bytesAvailable(); + if(bytes <= 0 || bytes > max) + bytes = max; + block.resize(bytes); + d->qsock->readBlock(block.data(), block.size()); + } + else + block = ByteStream::read(bytes); + +#ifdef BS_DEBUG + QCString cs; + cs.resize(block.size()+1); + memcpy(cs.data(), block.data(), block.size()); + QString s = QString::fromUtf8(cs); + fprintf(stderr, "BSocket: read [%d]: {%s}\n", block.size(), s.latin1()); +#endif + return block; +} + +int BSocket::bytesAvailable() const +{ + if(d->qsock) + return d->qsock->bytesAvailable(); + else + return ByteStream::bytesAvailable(); +} + +int BSocket::bytesToWrite() const +{ + if(!d->qsock) + return 0; + return d->qsock->bytesToWrite(); +} + +QHostAddress BSocket::address() const +{ + if(d->qsock) + return d->qsock->address(); + else + return QHostAddress(); +} + +Q_UINT16 BSocket::port() const +{ + if(d->qsock) + return d->qsock->port(); + else + return 0; +} + +QHostAddress BSocket::peerAddress() const +{ + if(d->qsock) + return d->qsock->peerAddress(); + else + return QHostAddress(); +} + +Q_UINT16 BSocket::peerPort() const +{ + if(d->qsock) + return d->qsock->port(); + else + return 0; +} + +void BSocket::srv_done() +{ + if(d->srv.failed()) { +#ifdef BS_DEBUG + fprintf(stderr, "BSocket: Error resolving hostname.\n"); +#endif + error(ErrHostNotFound); + return; + } + + d->host = d->srv.resultAddress().toString(); + d->port = d->srv.resultPort(); + do_connect(); + //QTimer::singleShot(0, this, SLOT(do_connect())); + //hostFound(); +} + +void BSocket::ndns_done() +{ +#ifndef NO_NDNS + if(d->ndns.result()) { + d->host = d->ndns.resultString(); + d->state = Connecting; + do_connect(); + //QTimer::singleShot(0, this, SLOT(do_connect())); + //hostFound(); + } + else { +#ifdef BS_DEBUG + fprintf(stderr, "BSocket: Error resolving hostname.\n"); +#endif + error(ErrHostNotFound); + } +#endif +} + +void BSocket::do_connect() +{ +#ifdef BS_DEBUG + fprintf(stderr, "BSocket: Connecting to %s:%d\n", d->host.latin1(), d->port); +#endif + ensureSocket(); + d->qsock->connectToHost(d->host, d->port); +} + +void BSocket::qs_hostFound() +{ + //SafeDeleteLock s(&d->sd); +} + +void BSocket::qs_connected() +{ + d->state = Connected; +#ifdef BS_DEBUG + fprintf(stderr, "BSocket: Connected.\n"); +#endif + SafeDeleteLock s(&d->sd); + connected(); +} + +void BSocket::qs_connectionClosed() +{ +#ifdef BS_DEBUG + fprintf(stderr, "BSocket: Connection Closed.\n"); +#endif + SafeDeleteLock s(&d->sd); + reset(); + connectionClosed(); +} + +void BSocket::qs_delayedCloseFinished() +{ +#ifdef BS_DEBUG + fprintf(stderr, "BSocket: Delayed Close Finished.\n"); +#endif + SafeDeleteLock s(&d->sd); + reset(); + delayedCloseFinished(); +} + +void BSocket::qs_readyRead() +{ + SafeDeleteLock s(&d->sd); + readyRead(); +} + +void BSocket::qs_bytesWritten(int x) +{ +#ifdef BS_DEBUG + fprintf(stderr, "BSocket: BytesWritten [%d].\n", x); +#endif + SafeDeleteLock s(&d->sd); + bytesWritten(x); +} + +void BSocket::qs_error(int x) +{ +#ifdef BS_DEBUG + fprintf(stderr, "BSocket: Error.\n"); +#endif + SafeDeleteLock s(&d->sd); + + // connection error during SRV host connect? try next + if(d->state == HostLookup && (x == QSocket::ErrConnectionRefused || x == QSocket::ErrHostNotFound)) { + d->srv.next(); + return; + } + + reset(); + if(x == QSocket::ErrConnectionRefused) + error(ErrConnectionRefused); + else if(x == QSocket::ErrHostNotFound) + error(ErrHostNotFound); + else if(x == QSocket::ErrSocketRead) + error(ErrRead); +} + +// CS_NAMESPACE_END + +#include "bsocket.moc" diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h new file mode 100644 index 00000000..bedaa54e --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h @@ -0,0 +1,87 @@ +/* + * bsocket.h - QSocket wrapper based on Bytestream with SRV DNS support + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef CS_BSOCKET_H +#define CS_BSOCKET_H + +#include +#include +#include"bytestream.h" + +// CS_NAMESPACE_BEGIN + +class BSocket : public ByteStream +{ + Q_OBJECT +public: + enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound }; + enum State { Idle, HostLookup, Connecting, Connected, Closing }; + BSocket(QObject *parent=0); + ~BSocket(); + + void connectToHost(const QString &host, Q_UINT16 port); + void connectToServer(const QString &srv, const QString &type); + int socket() const; + void setSocket(int); + int state() const; + + // from ByteStream + bool isOpen() const; + void close(); + void write(const QByteArray &); + QByteArray read(int bytes=0); + int bytesAvailable() const; + int bytesToWrite() const; + + // local + QHostAddress address() const; + Q_UINT16 port() const; + + // remote + QHostAddress peerAddress() const; + Q_UINT16 peerPort() const; + +signals: + void hostFound(); + void connected(); + +private slots: + void qs_hostFound(); + void qs_connected(); + void qs_connectionClosed(); + void qs_delayedCloseFinished(); + void qs_readyRead(); + void qs_bytesWritten(int); + void qs_error(int); + void srv_done(); + void ndns_done(); + void do_connect(); + +private: + class Private; + Private *d; + + void reset(bool clear=false); + void ensureSocket(); +}; + +// CS_NAMESPACE_END + +#endif diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp new file mode 100644 index 00000000..c194324a --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp @@ -0,0 +1,369 @@ +/* + * httpconnect.cpp - HTTP "CONNECT" proxy + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include"httpconnect.h" + +#include +#include"bsocket.h" +#include"base64.h" + +#ifdef PROX_DEBUG +#include +#endif + +// CS_NAMESPACE_BEGIN + +static QString extractLine(QByteArray *buf, bool *found) +{ + // scan for newline + int n; + for(n = 0; n < (int)buf->size()-1; ++n) { + if(buf->at(n) == '\r' && buf->at(n+1) == '\n') { + QCString cstr; + cstr.resize(n+1); + memcpy(cstr.data(), buf->data(), n); + n += 2; // hack off CR/LF + + memmove(buf->data(), buf->data() + n, buf->size() - n); + buf->resize(buf->size() - n); + QString s = QString::fromUtf8(cstr); + + if(found) + *found = true; + return s; + } + } + + if(found) + *found = false; + return ""; +} + +static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg) +{ + int n = line.find(' '); + if(n == -1) + return false; + if(proto) + *proto = line.mid(0, n); + ++n; + int n2 = line.find(' ', n); + if(n2 == -1) + return false; + if(code) + *code = line.mid(n, n2-n).toInt(); + n = n2+1; + if(msg) + *msg = line.mid(n); + return true; +} + +class HttpConnect::Private +{ +public: + Private() {} + + BSocket sock; + QString host; + int port; + QString user, pass; + QString real_host; + int real_port; + + QByteArray recvBuf; + + bool inHeader; + QStringList headerLines; + + int toWrite; + bool active; +}; + +HttpConnect::HttpConnect(QObject *parent) +:ByteStream(parent) +{ + d = new Private; + connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected())); + connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed())); + connect(&d->sock, SIGNAL(delayedCloseFinished()), SLOT(sock_delayedCloseFinished())); + connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); + connect(&d->sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int))); + connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int))); + + reset(true); +} + +HttpConnect::~HttpConnect() +{ + reset(true); + delete d; +} + +void HttpConnect::reset(bool clear) +{ + if(d->sock.state() != BSocket::Idle) + d->sock.close(); + if(clear) { + clearReadBuffer(); + d->recvBuf.resize(0); + } + d->active = false; +} + +void HttpConnect::setAuth(const QString &user, const QString &pass) +{ + d->user = user; + d->pass = pass; +} + +void HttpConnect::connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port) +{ + reset(true); + + d->host = proxyHost; + d->port = proxyPort; + d->real_host = host; + d->real_port = port; + +#ifdef PROX_DEBUG + fprintf(stderr, "HttpConnect: Connecting to %s:%d", proxyHost.latin1(), proxyPort); + if(d->user.isEmpty()) + fprintf(stderr, "\n"); + else + fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1()); +#endif + d->sock.connectToHost(d->host, d->port); +} + +bool HttpConnect::isOpen() const +{ + return d->active; +} + +void HttpConnect::close() +{ + d->sock.close(); + if(d->sock.bytesToWrite() == 0) + reset(); +} + +void HttpConnect::write(const QByteArray &buf) +{ + if(d->active) + d->sock.write(buf); +} + +QByteArray HttpConnect::read(int bytes) +{ + return ByteStream::read(bytes); +} + +int HttpConnect::bytesAvailable() const +{ + return ByteStream::bytesAvailable(); +} + +int HttpConnect::bytesToWrite() const +{ + if(d->active) + return d->sock.bytesToWrite(); + else + return 0; +} + +void HttpConnect::sock_connected() +{ +#ifdef PROX_DEBUG + fprintf(stderr, "HttpConnect: Connected\n"); +#endif + d->inHeader = true; + d->headerLines.clear(); + + // connected, now send the request + QString s; + s += QString("CONNECT ") + d->real_host + ':' + QString::number(d->real_port) + " HTTP/1.0\r\n"; + if(!d->user.isEmpty()) { + QString str = d->user + ':' + d->pass; + s += QString("Proxy-Authorization: Basic ") + Base64::encodeString(str) + "\r\n"; + } + s += "Proxy-Connection: Keep-Alive\r\n"; + s += "Pragma: no-cache\r\n"; + s += "\r\n"; + + QCString cs = s.utf8(); + QByteArray block(cs.length()); + memcpy(block.data(), cs.data(), block.size()); + d->toWrite = block.size(); + d->sock.write(block); +} + +void HttpConnect::sock_connectionClosed() +{ + if(d->active) { + reset(); + connectionClosed(); + } + else { + error(ErrProxyNeg); + } +} + +void HttpConnect::sock_delayedCloseFinished() +{ + if(d->active) { + reset(); + delayedCloseFinished(); + } +} + +void HttpConnect::sock_readyRead() +{ + QByteArray block = d->sock.read(); + + if(!d->active) { + ByteStream::appendArray(&d->recvBuf, block); + + if(d->inHeader) { + // grab available lines + while(1) { + bool found; + QString line = extractLine(&d->recvBuf, &found); + if(!found) + break; + if(line.isEmpty()) { + d->inHeader = false; + break; + } + d->headerLines += line; + } + + // done with grabbing the header? + if(!d->inHeader) { + QString str = d->headerLines.first(); + d->headerLines.remove(d->headerLines.begin()); + + QString proto; + int code; + QString msg; + if(!extractMainHeader(str, &proto, &code, &msg)) { +#ifdef PROX_DEBUG + fprintf(stderr, "HttpConnect: invalid header!\n"); +#endif + reset(true); + error(ErrProxyNeg); + return; + } + else { +#ifdef PROX_DEBUG + fprintf(stderr, "HttpConnect: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1()); + for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it) + fprintf(stderr, "HttpConnect: * [%s]\n", (*it).latin1()); +#endif + } + + if(code == 200) { // OK +#ifdef PROX_DEBUG + fprintf(stderr, "HttpConnect: << Success >>\n"); +#endif + d->active = true; + connected(); + + if(!d->recvBuf.isEmpty()) { + appendRead(d->recvBuf); + d->recvBuf.resize(0); + readyRead(); + return; + } + } + else { + int err; + QString errStr; + if(code == 407) { // Authentication failed + err = ErrProxyAuth; + errStr = tr("Authentication failed"); + } + else if(code == 404) { // Host not found + err = ErrHostNotFound; + errStr = tr("Host not found"); + } + else if(code == 403) { // Access denied + err = ErrProxyNeg; + errStr = tr("Access denied"); + } + else if(code == 503) { // Connection refused + err = ErrConnectionRefused; + errStr = tr("Connection refused"); + } + else { // invalid reply + err = ErrProxyNeg; + errStr = tr("Invalid reply"); + } + +#ifdef PROX_DEBUG + fprintf(stderr, "HttpConnect: << Error >> [%s]\n", errStr.latin1()); +#endif + reset(true); + error(err); + return; + } + } + } + } + else { + appendRead(block); + readyRead(); + return; + } +} + +void HttpConnect::sock_bytesWritten(int x) +{ + if(d->toWrite > 0) { + int size = x; + if(d->toWrite < x) + size = d->toWrite; + d->toWrite -= size; + x -= size; + } + + if(d->active && x > 0) + bytesWritten(x); +} + +void HttpConnect::sock_error(int x) +{ + if(d->active) { + reset(); + error(ErrRead); + } + else { + reset(true); + if(x == BSocket::ErrHostNotFound) + error(ErrProxyConnect); + else if(x == BSocket::ErrConnectionRefused) + error(ErrProxyConnect); + else if(x == BSocket::ErrRead) + error(ErrProxyNeg); + } +} + +// CS_NAMESPACE_END + +#include "httpconnect.moc" diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h new file mode 100644 index 00000000..38129c60 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h @@ -0,0 +1,67 @@ +/* + * httpconnect.h - HTTP "CONNECT" proxy + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef CS_HTTPCONNECT_H +#define CS_HTTPCONNECT_H + +#include"bytestream.h" + +// CS_NAMESPACE_BEGIN + +class HttpConnect : public ByteStream +{ + Q_OBJECT +public: + enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth }; + HttpConnect(QObject *parent=0); + ~HttpConnect(); + + void setAuth(const QString &user, const QString &pass=""); + void connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port); + + // from ByteStream + bool isOpen() const; + void close(); + void write(const QByteArray &); + QByteArray read(int bytes=0); + int bytesAvailable() const; + int bytesToWrite() const; + +signals: + void connected(); + +private slots: + void sock_connected(); + void sock_connectionClosed(); + void sock_delayedCloseFinished(); + void sock_readyRead(); + void sock_bytesWritten(int); + void sock_error(int); + +private: + class Private; + Private *d; + + void reset(bool clear=false); +}; + +// CS_NAMESPACE_END + +#endif diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp new file mode 100644 index 00000000..4975d0e5 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp @@ -0,0 +1,666 @@ +/* + * httppoll.cpp - HTTP polling proxy + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include"httppoll.h" + +#include +#include +#include +#include +#include +#include +#include"bsocket.h" +#include"base64.h" + +#ifdef PROX_DEBUG +#include +#endif + +#define POLL_KEYS 64 + +// CS_NAMESPACE_BEGIN + +static QByteArray randomArray(int size) +{ + QByteArray a(size); + for(int n = 0; n < size; ++n) + a[n] = (char)(256.0*rand()/(RAND_MAX+1.0)); + return a; +} + +//---------------------------------------------------------------------------- +// HttpPoll +//---------------------------------------------------------------------------- +static QString hpk(int n, const QString &s) +{ + if(n == 0) + return s; + else + return Base64::arrayToString( QCA::SHA1::hash( QCString(hpk(n - 1, s).latin1()) ) ); +} + +class HttpPoll::Private +{ +public: + Private() {} + + HttpProxyPost http; + QString host; + int port; + QString user, pass; + QString url; + bool use_proxy; + + QByteArray out; + + int state; + bool closing; + QString ident; + + QTimer *t; + + QString key[POLL_KEYS]; + int key_n; + + int polltime; +}; + +HttpPoll::HttpPoll(QObject *parent) +:ByteStream(parent) +{ + d = new Private; + + d->polltime = 30; + d->t = new QTimer; + connect(d->t, SIGNAL(timeout()), SLOT(do_sync())); + + connect(&d->http, SIGNAL(result()), SLOT(http_result())); + connect(&d->http, SIGNAL(error(int)), SLOT(http_error(int))); + + reset(true); +} + +HttpPoll::~HttpPoll() +{ + reset(true); + delete d->t; + delete d; +} + +void HttpPoll::reset(bool clear) +{ + if(d->http.isActive()) + d->http.stop(); + if(clear) + clearReadBuffer(); + clearWriteBuffer(); + d->out.resize(0); + d->state = 0; + d->closing = false; + d->t->stop(); +} + +void HttpPoll::setAuth(const QString &user, const QString &pass) +{ + d->user = user; + d->pass = pass; +} + +void HttpPoll::connectToUrl(const QString &url) +{ + connectToHost("", 0, url); +} + +void HttpPoll::connectToHost(const QString &proxyHost, int proxyPort, const QString &url) +{ + reset(true); + + // using proxy? + if(!proxyHost.isEmpty()) { + d->host = proxyHost; + d->port = proxyPort; + d->url = url; + d->use_proxy = true; + } + else { + QUrl u = url; + d->host = u.host(); + if(u.hasPort()) + d->port = u.port(); + else + d->port = 80; + d->url = u.encodedPathAndQuery(); + d->use_proxy = false; + } + + resetKey(); + bool last; + QString key = getKey(&last); + +#ifdef PROX_DEBUG + fprintf(stderr, "HttpPoll: Connecting to %s:%d [%s]", d->host.latin1(), d->port, d->url.latin1()); + if(d->user.isEmpty()) + fprintf(stderr, "\n"); + else + fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1()); +#endif + QGuardedPtr self = this; + syncStarted(); + if(!self) + return; + + d->state = 1; + d->http.setAuth(d->user, d->pass); + d->http.post(d->host, d->port, d->url, makePacket("0", key, "", QByteArray()), d->use_proxy); +} + +QByteArray HttpPoll::makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block) +{ + QString str = ident; + if(!key.isEmpty()) { + str += ';'; + str += key; + } + if(!newkey.isEmpty()) { + str += ';'; + str += newkey; + } + str += ','; + QCString cs = str.latin1(); + int len = cs.length(); + + QByteArray a(len + block.size()); + memcpy(a.data(), cs.data(), len); + memcpy(a.data() + len, block.data(), block.size()); + return a; +} + +int HttpPoll::pollInterval() const +{ + return d->polltime; +} + +void HttpPoll::setPollInterval(int seconds) +{ + d->polltime = seconds; +} + +bool HttpPoll::isOpen() const +{ + return (d->state == 2 ? true: false); +} + +void HttpPoll::close() +{ + if(d->state == 0 || d->closing) + return; + + if(bytesToWrite() == 0) + reset(); + else + d->closing = true; +} + +void HttpPoll::http_result() +{ + // check for death :) + QGuardedPtr self = this; + syncFinished(); + if(!self) + return; + + // get id and packet + QString id; + QString cookie = d->http.getHeader("Set-Cookie"); + int n = cookie.find("ID="); + if(n == -1) { + reset(); + error(ErrRead); + return; + } + n += 3; + int n2 = cookie.find(';', n); + if(n2 != -1) + id = cookie.mid(n, n2-n); + else + id = cookie.mid(n); + QByteArray block = d->http.body(); + + // session error? + if(id.right(2) == ":0") { + if(id == "0:0" && d->state == 2) { + reset(); + connectionClosed(); + return; + } + else { + reset(); + error(ErrRead); + return; + } + } + + d->ident = id; + bool justNowConnected = false; + if(d->state == 1) { + d->state = 2; + justNowConnected = true; + } + + // sync up again soon + if(bytesToWrite() > 0 || !d->closing) + d->t->start(d->polltime * 1000, true); + + // connecting + if(justNowConnected) { + connected(); + } + else { + if(!d->out.isEmpty()) { + int x = d->out.size(); + d->out.resize(0); + takeWrite(x); + bytesWritten(x); + } + } + + if(!self) + return; + + if(!block.isEmpty()) { + appendRead(block); + readyRead(); + } + + if(!self) + return; + + if(bytesToWrite() > 0) { + do_sync(); + } + else { + if(d->closing) { + reset(); + delayedCloseFinished(); + return; + } + } +} + +void HttpPoll::http_error(int x) +{ + reset(); + if(x == HttpProxyPost::ErrConnectionRefused) + error(ErrConnectionRefused); + else if(x == HttpProxyPost::ErrHostNotFound) + error(ErrHostNotFound); + else if(x == HttpProxyPost::ErrSocket) + error(ErrRead); + else if(x == HttpProxyPost::ErrProxyConnect) + error(ErrProxyConnect); + else if(x == HttpProxyPost::ErrProxyNeg) + error(ErrProxyNeg); + else if(x == HttpProxyPost::ErrProxyAuth) + error(ErrProxyAuth); +} + +int HttpPoll::tryWrite() +{ + if(!d->http.isActive()) + do_sync(); + return 0; +} + +void HttpPoll::do_sync() +{ + if(d->http.isActive()) + return; + + d->t->stop(); + d->out = takeWrite(0, false); + + bool last; + QString key = getKey(&last); + QString newkey; + if(last) { + resetKey(); + newkey = getKey(&last); + } + + QGuardedPtr self = this; + syncStarted(); + if(!self) + return; + + d->http.post(d->host, d->port, d->url, makePacket(d->ident, key, newkey, d->out), d->use_proxy); +} + +void HttpPoll::resetKey() +{ +#ifdef PROX_DEBUG + fprintf(stderr, "HttpPoll: reset key!\n"); +#endif + QByteArray a = randomArray(64); + QString str = QString::fromLatin1(a.data(), a.size()); + + d->key_n = POLL_KEYS; + for(int n = 0; n < POLL_KEYS; ++n) + d->key[n] = hpk(n+1, str); +} + +const QString & HttpPoll::getKey(bool *last) +{ + *last = false; + --(d->key_n); + if(d->key_n == 0) + *last = true; + return d->key[d->key_n]; +} + + +//---------------------------------------------------------------------------- +// HttpProxyPost +//---------------------------------------------------------------------------- +static QString extractLine(QByteArray *buf, bool *found) +{ + // scan for newline + int n; + for(n = 0; n < (int)buf->size()-1; ++n) { + if(buf->at(n) == '\r' && buf->at(n+1) == '\n') { + QCString cstr; + cstr.resize(n+1); + memcpy(cstr.data(), buf->data(), n); + n += 2; // hack off CR/LF + + memmove(buf->data(), buf->data() + n, buf->size() - n); + buf->resize(buf->size() - n); + QString s = QString::fromUtf8(cstr); + + if(found) + *found = true; + return s; + } + } + + if(found) + *found = false; + return ""; +} + +static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg) +{ + int n = line.find(' '); + if(n == -1) + return false; + if(proto) + *proto = line.mid(0, n); + ++n; + int n2 = line.find(' ', n); + if(n2 == -1) + return false; + if(code) + *code = line.mid(n, n2-n).toInt(); + n = n2+1; + if(msg) + *msg = line.mid(n); + return true; +} + +class HttpProxyPost::Private +{ +public: + Private() {} + + BSocket sock; + QByteArray postdata, recvBuf, body; + QString url; + QString user, pass; + bool inHeader; + QStringList headerLines; + bool asProxy; + QString host; +}; + +HttpProxyPost::HttpProxyPost(QObject *parent) +:QObject(parent) +{ + d = new Private; + connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected())); + connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed())); + connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); + connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int))); + reset(true); +} + +HttpProxyPost::~HttpProxyPost() +{ + reset(true); + delete d; +} + +void HttpProxyPost::reset(bool clear) +{ + if(d->sock.state() != BSocket::Idle) + d->sock.close(); + d->recvBuf.resize(0); + if(clear) + d->body.resize(0); +} + +void HttpProxyPost::setAuth(const QString &user, const QString &pass) +{ + d->user = user; + d->pass = pass; +} + +bool HttpProxyPost::isActive() const +{ + return (d->sock.state() == BSocket::Idle ? false: true); +} + +void HttpProxyPost::post(const QString &proxyHost, int proxyPort, const QString &url, const QByteArray &data, bool asProxy) +{ + reset(true); + + d->host = proxyHost; + d->url = url; + d->postdata = data; + d->asProxy = asProxy; + +#ifdef PROX_DEBUG + fprintf(stderr, "HttpProxyPost: Connecting to %s:%d", proxyHost.latin1(), proxyPort); + if(d->user.isEmpty()) + fprintf(stderr, "\n"); + else + fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1()); +#endif + d->sock.connectToHost(proxyHost, proxyPort); +} + +void HttpProxyPost::stop() +{ + reset(); +} + +QByteArray HttpProxyPost::body() const +{ + return d->body; +} + +QString HttpProxyPost::getHeader(const QString &var) const +{ + for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it) { + const QString &s = *it; + int n = s.find(": "); + if(n == -1) + continue; + QString v = s.mid(0, n); + if(v == var) + return s.mid(n+2); + } + return ""; +} + +void HttpProxyPost::sock_connected() +{ +#ifdef PROX_DEBUG + fprintf(stderr, "HttpProxyPost: Connected\n"); +#endif + d->inHeader = true; + d->headerLines.clear(); + + QUrl u = d->url; + + // connected, now send the request + QString s; + s += QString("POST ") + d->url + " HTTP/1.0\r\n"; + if(d->asProxy) { + if(!d->user.isEmpty()) { + QString str = d->user + ':' + d->pass; + s += QString("Proxy-Authorization: Basic ") + Base64::encodeString(str) + "\r\n"; + } + s += "Proxy-Connection: Keep-Alive\r\n"; + s += "Pragma: no-cache\r\n"; + s += QString("Host: ") + u.host() + "\r\n"; + } + else { + s += QString("Host: ") + d->host + "\r\n"; + } + s += "Content-Type: application/x-www-form-urlencoded\r\n"; + s += QString("Content-Length: ") + QString::number(d->postdata.size()) + "\r\n"; + s += "\r\n"; + + // write request + QCString cs = s.utf8(); + QByteArray block(cs.length()); + memcpy(block.data(), cs.data(), block.size()); + d->sock.write(block); + + // write postdata + d->sock.write(d->postdata); +} + +void HttpProxyPost::sock_connectionClosed() +{ + d->body = d->recvBuf.copy(); + reset(); + result(); +} + +void HttpProxyPost::sock_readyRead() +{ + QByteArray block = d->sock.read(); + ByteStream::appendArray(&d->recvBuf, block); + + if(d->inHeader) { + // grab available lines + while(1) { + bool found; + QString line = extractLine(&d->recvBuf, &found); + if(!found) + break; + if(line.isEmpty()) { + d->inHeader = false; + break; + } + d->headerLines += line; + } + + // done with grabbing the header? + if(!d->inHeader) { + QString str = d->headerLines.first(); + d->headerLines.remove(d->headerLines.begin()); + + QString proto; + int code; + QString msg; + if(!extractMainHeader(str, &proto, &code, &msg)) { +#ifdef PROX_DEBUG + fprintf(stderr, "HttpProxyPost: invalid header!\n"); +#endif + reset(true); + error(ErrProxyNeg); + return; + } + else { +#ifdef PROX_DEBUG + fprintf(stderr, "HttpProxyPost: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1()); + for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it) + fprintf(stderr, "HttpProxyPost: * [%s]\n", (*it).latin1()); +#endif + } + + if(code == 200) { // OK +#ifdef PROX_DEBUG + fprintf(stderr, "HttpProxyPost: << Success >>\n"); +#endif + } + else { + int err; + QString errStr; + if(code == 407) { // Authentication failed + err = ErrProxyAuth; + errStr = tr("Authentication failed"); + } + else if(code == 404) { // Host not found + err = ErrHostNotFound; + errStr = tr("Host not found"); + } + else if(code == 403) { // Access denied + err = ErrProxyNeg; + errStr = tr("Access denied"); + } + else if(code == 503) { // Connection refused + err = ErrConnectionRefused; + errStr = tr("Connection refused"); + } + else { // invalid reply + err = ErrProxyNeg; + errStr = tr("Invalid reply"); + } + +#ifdef PROX_DEBUG + fprintf(stderr, "HttpProxyPost: << Error >> [%s]\n", errStr.latin1()); +#endif + reset(true); + error(err); + return; + } + } + } +} + +void HttpProxyPost::sock_error(int x) +{ +#ifdef PROX_DEBUG + fprintf(stderr, "HttpProxyPost: socket error: %d\n", x); +#endif + reset(true); + if(x == BSocket::ErrHostNotFound) + error(ErrProxyConnect); + else if(x == BSocket::ErrConnectionRefused) + error(ErrProxyConnect); + else if(x == BSocket::ErrRead) + error(ErrProxyNeg); +} + +// CS_NAMESPACE_END + +#include "httppoll.moc" diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h new file mode 100644 index 00000000..8bbebee3 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h @@ -0,0 +1,104 @@ +/* + * httppoll.h - HTTP polling proxy + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef CS_HTTPPOLL_H +#define CS_HTTPPOLL_H + +#include"bytestream.h" + +// CS_NAMESPACE_BEGIN + +class HttpPoll : public ByteStream +{ + Q_OBJECT +public: + enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth }; + HttpPoll(QObject *parent=0); + ~HttpPoll(); + + void setAuth(const QString &user, const QString &pass=""); + void connectToUrl(const QString &url); + void connectToHost(const QString &proxyHost, int proxyPort, const QString &url); + + int pollInterval() const; + void setPollInterval(int seconds); + + // from ByteStream + bool isOpen() const; + void close(); + +signals: + void connected(); + void syncStarted(); + void syncFinished(); + +protected: + int tryWrite(); + +private slots: + void http_result(); + void http_error(int); + void do_sync(); + +private: + class Private; + Private *d; + + void reset(bool clear=false); + QByteArray makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block); + void resetKey(); + const QString & getKey(bool *); +}; + +class HttpProxyPost : public QObject +{ + Q_OBJECT +public: + enum Error { ErrConnectionRefused, ErrHostNotFound, ErrSocket, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth }; + HttpProxyPost(QObject *parent=0); + ~HttpProxyPost(); + + void setAuth(const QString &user, const QString &pass=""); + bool isActive() const; + void post(const QString &proxyHost, int proxyPort, const QString &url, const QByteArray &data, bool asProxy=true); + void stop(); + QByteArray body() const; + QString getHeader(const QString &) const; + +signals: + void result(); + void error(int); + +private slots: + void sock_connected(); + void sock_connectionClosed(); + void sock_readyRead(); + void sock_error(int); + +private: + class Private; + Private *d; + + void reset(bool clear=false); +}; + +// CS_NAMESPACE_END + +#endif diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp new file mode 100644 index 00000000..7fe60973 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp @@ -0,0 +1,378 @@ +/* + * ndns.cpp - native DNS resolution + * Copyright (C) 2001, 2002 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +//! \class NDns ndns.h +//! \brief Simple DNS resolution using native system calls +//! +//! This class is to be used when Qt's QDns is not good enough. Because QDns +//! does not use threads, it cannot make a system call asyncronously. Thus, +//! QDns tries to imitate the behavior of each platform's native behavior, and +//! generally falls short. +//! +//! NDns uses a thread to make the system call happen in the background. This +//! gives your program native DNS behavior, at the cost of requiring threads +//! to build. +//! +//! \code +//! #include "ndns.h" +//! +//! ... +//! +//! NDns dns; +//! dns.resolve("psi.affinix.com"); +//! +//! // The class will emit the resultsReady() signal when the resolution +//! // is finished. You may then retrieve the results: +//! +//! uint ip_address = dns.result(); +//! +//! // or if you want to get the IP address as a string: +//! +//! QString ip_address = dns.resultString(); +//! \endcode + +#include"ndns.h" + +#include +#include +#include +#include + +#ifdef Q_OS_UNIX +#include +#include +#include +#include +#endif + +#ifdef Q_OS_WIN32 +#include +#endif + +// CS_NAMESPACE_BEGIN + +//! \if _hide_doc_ +class NDnsWorkerEvent : public QCustomEvent +{ +public: + enum Type { WorkerEvent = QEvent::User + 100 }; + NDnsWorkerEvent(NDnsWorker *); + + NDnsWorker *worker; +}; + +class NDnsWorker : public QThread +{ +public: + NDnsWorker(QObject *, const QCString &); + + bool success; + bool cancelled; + QHostAddress addr; + +protected: + void run(); + +private: + QCString host; + QObject *par; +}; +//! \endif + +//---------------------------------------------------------------------------- +// NDnsManager +//---------------------------------------------------------------------------- +#ifndef HAVE_GETHOSTBYNAME_R +static QMutex *workerMutex = 0; +static QMutex *workerCancelled = 0; +#endif +static NDnsManager *man = 0; +bool winsock_init = false; + +class NDnsManager::Item +{ +public: + NDns *ndns; + NDnsWorker *worker; +}; + +class NDnsManager::Private +{ +public: + Item *find(const NDns *n) + { + QPtrListIterator it(list); + for(Item *i; (i = it.current()); ++it) { + if(i->ndns == n) + return i; + } + return 0; + } + + Item *find(const NDnsWorker *w) + { + QPtrListIterator it(list); + for(Item *i; (i = it.current()); ++it) { + if(i->worker == w) + return i; + } + return 0; + } + + QPtrList list; +}; + +NDnsManager::NDnsManager() +{ +#ifndef HAVE_GETHOSTBYNAME_R + workerMutex = new QMutex; + workerCancelled = new QMutex; +#endif + +#ifdef Q_OS_WIN32 + if(!winsock_init) { + winsock_init = true; + QSocketDevice *sd = new QSocketDevice; + delete sd; + } +#endif + + d = new Private; + d->list.setAutoDelete(true); + + connect(qApp, SIGNAL(aboutToQuit()), SLOT(app_aboutToQuit())); +} + +NDnsManager::~NDnsManager() +{ + delete d; + +#ifndef HAVE_GETHOSTBYNAME_R + delete workerMutex; + workerMutex = 0; + delete workerCancelled; + workerCancelled = 0; +#endif +} + +void NDnsManager::resolve(NDns *self, const QString &name) +{ + Item *i = new Item; + i->ndns = self; + i->worker = new NDnsWorker(this, name.utf8()); + d->list.append(i); + + i->worker->start(); +} + +void NDnsManager::stop(NDns *self) +{ + Item *i = d->find(self); + if(!i) + return; + // disassociate + i->ndns = 0; + +#ifndef HAVE_GETHOSTBYNAME_R + // cancel + workerCancelled->lock(); + i->worker->cancelled = true; + workerCancelled->unlock(); +#endif +} + +bool NDnsManager::isBusy(const NDns *self) const +{ + Item *i = d->find(self); + return (i ? true: false); +} + +bool NDnsManager::event(QEvent *e) +{ + if((int)e->type() == (int)NDnsWorkerEvent::WorkerEvent) { + NDnsWorkerEvent *we = static_cast(e); + we->worker->wait(); // ensure that the thread is terminated + + Item *i = d->find(we->worker); + if(!i) { + // should NOT happen + return true; + } + QHostAddress addr = i->worker->addr; + NDns *ndns = i->ndns; + delete i->worker; + d->list.removeRef(i); + + // nuke manager if no longer needed (code that follows MUST BE SAFE!) + tryDestroy(); + + // requestor still around? + if(ndns) + ndns->finished(addr); + return true; + } + return false; +} + +void NDnsManager::tryDestroy() +{ + if(d->list.isEmpty()) { + man = 0; + delete this; + } +} + +void NDnsManager::app_aboutToQuit() +{ + while(man) { + QEventLoop *e = qApp->eventLoop(); + e->processEvents(QEventLoop::WaitForMore); + } +} + + +//---------------------------------------------------------------------------- +// NDns +//---------------------------------------------------------------------------- + +//! \fn void NDns::resultsReady() +//! This signal is emitted when the DNS resolution succeeds or fails. + +//! +//! Constructs an NDns object with parent \a parent. +NDns::NDns(QObject *parent) +:QObject(parent) +{ +} + +//! +//! Destroys the object and frees allocated resources. +NDns::~NDns() +{ + stop(); +} + +//! +//! Resolves hostname \a host (eg. psi.affinix.com) +void NDns::resolve(const QString &host) +{ + stop(); + if(!man) + man = new NDnsManager; + man->resolve(this, host); +} + +//! +//! Cancels the lookup action. +//! \note This will not stop the underlying system call, which must finish before the next lookup will proceed. +void NDns::stop() +{ + if(man) + man->stop(this); +} + +//! +//! Returns the IP address as a 32-bit integer in host-byte-order. This will be 0 if the lookup failed. +//! \sa resultsReady() +uint NDns::result() const +{ + return addr.ip4Addr(); +} + +//! +//! Returns the IP address as a string. This will be an empty string if the lookup failed. +//! \sa resultsReady() +QString NDns::resultString() const +{ + return addr.toString(); +} + +//! +//! Returns TRUE if busy resolving a hostname. +bool NDns::isBusy() const +{ + if(!man) + return false; + return man->isBusy(this); +} + +void NDns::finished(const QHostAddress &a) +{ + addr = a; + resultsReady(); +} + +//---------------------------------------------------------------------------- +// NDnsWorkerEvent +//---------------------------------------------------------------------------- +NDnsWorkerEvent::NDnsWorkerEvent(NDnsWorker *p) +:QCustomEvent(WorkerEvent) +{ + worker = p; +} + +//---------------------------------------------------------------------------- +// NDnsWorker +//---------------------------------------------------------------------------- +NDnsWorker::NDnsWorker(QObject *_par, const QCString &_host) +{ + success = cancelled = false; + par = _par; + host = _host.copy(); // do we need this to avoid sharing across threads? +} + +void NDnsWorker::run() +{ + hostent *h = 0; + +#ifdef HAVE_GETHOSTBYNAME_R + hostent buf; + char char_buf[1024]; + int err; + gethostbyname_r(host.data(), &buf, char_buf, sizeof(char_buf), &h, &err); +#else + // lock for gethostbyname + QMutexLocker locker(workerMutex); + + // check for cancel + workerCancelled->lock(); + bool cancel = cancelled; + workerCancelled->unlock(); + + if(!cancel) + h = gethostbyname(host.data()); +#endif + + if(!h) { + success = false; + QApplication::postEvent(par, new NDnsWorkerEvent(this)); + return; + } + + in_addr a = *((struct in_addr *)h->h_addr_list[0]); + addr.setAddress(ntohl(a.s_addr)); + success = true; + + QApplication::postEvent(par, new NDnsWorkerEvent(this)); +} + +// CS_NAMESPACE_END + +#include "ndns.moc" diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/ndns.h b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.h new file mode 100644 index 00000000..c11d1a28 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.h @@ -0,0 +1,88 @@ +/* + * ndns.h - native DNS resolution + * Copyright (C) 2001, 2002 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef CS_NDNS_H +#define CS_NDNS_H + +#include +#include +#include +#include +#include + +// CS_NAMESPACE_BEGIN + +class NDnsWorker; +class NDnsManager; + +class NDns : public QObject +{ + Q_OBJECT +public: + NDns(QObject *parent=0); + ~NDns(); + + void resolve(const QString &); + void stop(); + bool isBusy() const; + + uint result() const; + QString resultString() const; + +signals: + void resultsReady(); + +private: + QHostAddress addr; + + friend class NDnsManager; + void finished(const QHostAddress &); +}; + +class NDnsManager : public QObject +{ + Q_OBJECT +public: + ~NDnsManager(); + class Item; + +//! \if _hide_doc_ +protected: + bool event(QEvent *); +//! \endif + +private slots: + void app_aboutToQuit(); + +private: + class Private; + Private *d; + + friend class NDns; + NDnsManager(); + void resolve(NDns *self, const QString &name); + void stop(NDns *self); + bool isBusy(const NDns *self) const; + void tryDestroy(); +}; + +// CS_NAMESPACE_END + +#endif diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp new file mode 100644 index 00000000..4aee36dc --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp @@ -0,0 +1,112 @@ +/* + * servsock.cpp - simple wrapper to QServerSocket + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include"servsock.h" + +// CS_NAMESPACE_BEGIN + +//---------------------------------------------------------------------------- +// ServSock +//---------------------------------------------------------------------------- +class ServSock::Private +{ +public: + Private() {} + + ServSockSignal *serv; +}; + +ServSock::ServSock(QObject *parent) +:QObject(parent) +{ + d = new Private; + d->serv = 0; +} + +ServSock::~ServSock() +{ + stop(); + delete d; +} + +bool ServSock::isActive() const +{ + return (d->serv ? true: false); +} + +bool ServSock::listen(Q_UINT16 port) +{ + stop(); + + d->serv = new ServSockSignal(port); + if(!d->serv->ok()) { + delete d->serv; + d->serv = 0; + return false; + } + connect(d->serv, SIGNAL(connectionReady(int)), SLOT(sss_connectionReady(int))); + + return true; +} + +void ServSock::stop() +{ + delete d->serv; + d->serv = 0; +} + +int ServSock::port() const +{ + if(d->serv) + return d->serv->port(); + else + return -1; +} + +QHostAddress ServSock::address() const +{ + if(d->serv) + return d->serv->address(); + else + return QHostAddress(); +} + +void ServSock::sss_connectionReady(int s) +{ + connectionReady(s); +} + + +//---------------------------------------------------------------------------- +// ServSockSignal +//---------------------------------------------------------------------------- +ServSockSignal::ServSockSignal(int port) +:QServerSocket(port, 16) +{ +} + +void ServSockSignal::newConnection(int x) +{ + connectionReady(x); +} + +// CS_NAMESPACE_END + +#include "servsock.moc" diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/servsock.h b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.h new file mode 100644 index 00000000..60a0c99d --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.h @@ -0,0 +1,68 @@ +/* + * servsock.h - simple wrapper to QServerSocket + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef CS_SERVSOCK_H +#define CS_SERVSOCK_H + +#include + +// CS_NAMESPACE_BEGIN + +class ServSock : public QObject +{ + Q_OBJECT +public: + ServSock(QObject *parent=0); + ~ServSock(); + + bool isActive() const; + bool listen(Q_UINT16 port); + void stop(); + int port() const; + QHostAddress address() const; + +signals: + void connectionReady(int); + +private slots: + void sss_connectionReady(int); + +private: + class Private; + Private *d; +}; + +class ServSockSignal : public QServerSocket +{ + Q_OBJECT +public: + ServSockSignal(int port); + +signals: + void connectionReady(int); + +protected: + // reimplemented + void newConnection(int); +}; + +// CS_NAMESPACE_END + +#endif diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp new file mode 100644 index 00000000..bae374f5 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp @@ -0,0 +1,1223 @@ +/* + * socks.cpp - SOCKS5 TCP proxy client/server + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include"socks.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_UNIX +#include +#include +#endif + +#ifdef Q_OS_WIN32 +#include +#endif + +#include"servsock.h" +#include"bsocket.h" + +#ifdef PROX_DEBUG +#include +#endif + +// CS_NAMESPACE_BEGIN + +//---------------------------------------------------------------------------- +// SocksUDP +//---------------------------------------------------------------------------- +static QByteArray sp_create_udp(const QString &host, Q_UINT16 port, const QByteArray &buf) +{ + // detect for IP addresses + //QHostAddress addr; + //if(addr.setAddress(host)) + // return sp_set_request(addr, port, cmd1); + + QCString h = host.utf8(); + h.truncate(255); + h = QString::fromUtf8(h).utf8(); // delete any partial characters? + int hlen = h.length(); + + int at = 0; + QByteArray a(4); + a[at++] = 0x00; // reserved + a[at++] = 0x00; // reserved + a[at++] = 0x00; // frag + a[at++] = 0x03; // address type = domain + + // host + a.resize(at+hlen+1); + a[at++] = hlen; + memcpy(a.data() + at, h.data(), hlen); + at += hlen; + + // port + a.resize(at+2); + unsigned short p = htons(port); + memcpy(a.data() + at, &p, 2); + at += 2; + + a.resize(at+buf.size()); + memcpy(a.data() + at, buf.data(), buf.size()); + + return a; +} + +struct SPS_UDP +{ + QString host; + Q_UINT16 port; + QByteArray data; +}; + +static int sp_read_udp(QByteArray *from, SPS_UDP *s) +{ + int full_len = 4; + if((int)from->size() < full_len) + return 0; + + QString host; + QHostAddress addr; + unsigned char atype = from->at(3); + + if(atype == 0x01) { + full_len += 4; + if((int)from->size() < full_len) + return 0; + Q_UINT32 ip4; + memcpy(&ip4, from->data() + 4, 4); + addr.setAddress(ntohl(ip4)); + host = addr.toString(); + } + else if(atype == 0x03) { + ++full_len; + if((int)from->size() < full_len) + return 0; + unsigned char host_len = from->at(4); + full_len += host_len; + if((int)from->size() < full_len) + return 0; + QCString cs(host_len+1); + memcpy(cs.data(), from->data() + 5, host_len); + host = QString::fromLatin1(cs); + } + else if(atype == 0x04) { + full_len += 16; + if((int)from->size() < full_len) + return 0; + Q_UINT8 a6[16]; + memcpy(a6, from->data() + 4, 16); + addr.setAddress(a6); + host = addr.toString(); + } + + full_len += 2; + if((int)from->size() < full_len) + return 0; + + Q_UINT16 p; + memcpy(&p, from->data() + full_len - 2, 2); + + s->host = host; + s->port = ntohs(p); + s->data.resize(from->size() - full_len); + memcpy(s->data.data(), from->data() + full_len, s->data.size()); + + return 1; +} + +class SocksUDP::Private +{ +public: + QSocketDevice *sd; + QSocketNotifier *sn; + SocksClient *sc; + QHostAddress routeAddr; + int routePort; + QString host; + int port; +}; + +SocksUDP::SocksUDP(SocksClient *sc, const QString &host, int port, const QHostAddress &routeAddr, int routePort) +:QObject(sc) +{ + d = new Private; + d->sc = sc; + d->sd = new QSocketDevice(QSocketDevice::Datagram); + d->sd->setBlocking(false); + d->sn = new QSocketNotifier(d->sd->socket(), QSocketNotifier::Read); + connect(d->sn, SIGNAL(activated(int)), SLOT(sn_activated(int))); + d->host = host; + d->port = port; + d->routeAddr = routeAddr; + d->routePort = routePort; +} + +SocksUDP::~SocksUDP() +{ + delete d->sn; + delete d->sd; + delete d; +} + +void SocksUDP::change(const QString &host, int port) +{ + d->host = host; + d->port = port; +} + +void SocksUDP::write(const QByteArray &data) +{ + QByteArray buf = sp_create_udp(d->host, d->port, data); + d->sd->setBlocking(true); + d->sd->writeBlock(buf.data(), buf.size(), d->routeAddr, d->routePort); + d->sd->setBlocking(false); +} + +void SocksUDP::sn_activated(int) +{ + QByteArray buf(8192); + int actual = d->sd->readBlock(buf.data(), buf.size()); + buf.resize(actual); + packetReady(buf); +} + +//---------------------------------------------------------------------------- +// SocksClient +//---------------------------------------------------------------------------- +#define REQ_CONNECT 0x01 +#define REQ_BIND 0x02 +#define REQ_UDPASSOCIATE 0x03 + +#define RET_SUCCESS 0x00 +#define RET_UNREACHABLE 0x04 +#define RET_CONNREFUSED 0x05 + +// spc = socks packet client +// sps = socks packet server +// SPCS = socks packet client struct +// SPSS = socks packet server struct + +// Version +static QByteArray spc_set_version() +{ + QByteArray ver(4); + ver[0] = 0x05; // socks version 5 + ver[1] = 0x02; // number of methods + ver[2] = 0x00; // no-auth + ver[3] = 0x02; // username + return ver; +} + +static QByteArray sps_set_version(int method) +{ + QByteArray ver(2); + ver[0] = 0x05; + ver[1] = method; + return ver; +} + +struct SPCS_VERSION +{ + unsigned char version; + QByteArray methodList; +}; + +static int spc_get_version(QByteArray *from, SPCS_VERSION *s) +{ + if(from->size() < 1) + return 0; + if(from->at(0) != 0x05) // only SOCKS5 supported + return -1; + if(from->size() < 2) + return 0; + uint num = from->at(1); + if(num > 16) // who the heck has over 16 auth methods?? + return -1; + if(from->size() < 2 + num) + return 0; + QByteArray a = ByteStream::takeArray(from, 2+num); + s->version = a[0]; + s->methodList.resize(num); + memcpy(s->methodList.data(), a.data() + 2, num); + return 1; +} + +struct SPSS_VERSION +{ + unsigned char version; + unsigned char method; +}; + +static int sps_get_version(QByteArray *from, SPSS_VERSION *s) +{ + if(from->size() < 2) + return 0; + QByteArray a = ByteStream::takeArray(from, 2); + s->version = a[0]; + s->method = a[1]; + return 1; +} + +// authUsername +static QByteArray spc_set_authUsername(const QCString &user, const QCString &pass) +{ + int len1 = user.length(); + int len2 = pass.length(); + if(len1 > 255) + len1 = 255; + if(len2 > 255) + len2 = 255; + QByteArray a(1+1+len1+1+len2); + a[0] = 0x01; // username auth version 1 + a[1] = len1; + memcpy(a.data() + 2, user.data(), len1); + a[2+len1] = len2; + memcpy(a.data() + 3 + len1, pass.data(), len2); + return a; +} + +static QByteArray sps_set_authUsername(bool success) +{ + QByteArray a(2); + a[0] = 0x01; + a[1] = success ? 0x00 : 0xff; + return a; +} + +struct SPCS_AUTHUSERNAME +{ + QString user, pass; +}; + +static int spc_get_authUsername(QByteArray *from, SPCS_AUTHUSERNAME *s) +{ + if(from->size() < 1) + return 0; + unsigned char ver = from->at(0); + if(ver != 0x01) + return -1; + if(from->size() < 2) + return 0; + unsigned char ulen = from->at(1); + if((int)from->size() < ulen + 3) + return 0; + unsigned char plen = from->at(ulen+2); + if((int)from->size() < ulen + plen + 3) + return 0; + QByteArray a = ByteStream::takeArray(from, ulen + plen + 3); + + QCString user, pass; + user.resize(ulen+1); + pass.resize(plen+1); + memcpy(user.data(), a.data()+2, ulen); + memcpy(pass.data(), a.data()+ulen+3, plen); + s->user = QString::fromUtf8(user); + s->pass = QString::fromUtf8(pass); + return 1; +} + +struct SPSS_AUTHUSERNAME +{ + unsigned char version; + bool success; +}; + +static int sps_get_authUsername(QByteArray *from, SPSS_AUTHUSERNAME *s) +{ + if(from->size() < 2) + return 0; + QByteArray a = ByteStream::takeArray(from, 2); + s->version = a[0]; + s->success = a[1] == 0 ? true: false; + return 1; +} + +// connectRequest +static QByteArray sp_set_request(const QHostAddress &addr, unsigned short port, unsigned char cmd1) +{ + int at = 0; + QByteArray a(4); + a[at++] = 0x05; // socks version 5 + a[at++] = cmd1; + a[at++] = 0x00; // reserved + if(addr.isIp4Addr()) { + a[at++] = 0x01; // address type = ipv4 + Q_UINT32 ip4 = htonl(addr.ip4Addr()); + a.resize(at+4); + memcpy(a.data() + at, &ip4, 4); + at += 4; + } + else { + a[at++] = 0x04; + Q_UINT8 a6[16]; + QStringList s6 = QStringList::split(':', addr.toString(), true); + int at = 0; + Q_UINT16 c; + bool ok; + for(QStringList::ConstIterator it = s6.begin(); it != s6.end(); ++it) { + c = (*it).toInt(&ok, 16); + a6[at++] = (c >> 8); + a6[at++] = c & 0xff; + } + a.resize(at+16); + memcpy(a.data() + at, a6, 16); + at += 16; + } + + // port + a.resize(at+2); + unsigned short p = htons(port); + memcpy(a.data() + at, &p, 2); + + return a; +} + +static QByteArray sp_set_request(const QString &host, Q_UINT16 port, unsigned char cmd1) +{ + // detect for IP addresses + QHostAddress addr; + if(addr.setAddress(host)) + return sp_set_request(addr, port, cmd1); + + QCString h = host.utf8(); + h.truncate(255); + h = QString::fromUtf8(h).utf8(); // delete any partial characters? + int hlen = h.length(); + + int at = 0; + QByteArray a(4); + a[at++] = 0x05; // socks version 5 + a[at++] = cmd1; + a[at++] = 0x00; // reserved + a[at++] = 0x03; // address type = domain + + // host + a.resize(at+hlen+1); + a[at++] = hlen; + memcpy(a.data() + at, h.data(), hlen); + at += hlen; + + // port + a.resize(at+2); + unsigned short p = htons(port); + memcpy(a.data() + at, &p, 2); + + return a; +} + +struct SPS_CONNREQ +{ + unsigned char version; + unsigned char cmd; + int address_type; + QString host; + QHostAddress addr; + Q_UINT16 port; +}; + +static int sp_get_request(QByteArray *from, SPS_CONNREQ *s) +{ + int full_len = 4; + if((int)from->size() < full_len) + return 0; + + QString host; + QHostAddress addr; + unsigned char atype = from->at(3); + + if(atype == 0x01) { + full_len += 4; + if((int)from->size() < full_len) + return 0; + Q_UINT32 ip4; + memcpy(&ip4, from->data() + 4, 4); + addr.setAddress(ntohl(ip4)); + } + else if(atype == 0x03) { + ++full_len; + if((int)from->size() < full_len) + return 0; + unsigned char host_len = from->at(4); + full_len += host_len; + if((int)from->size() < full_len) + return 0; + QCString cs(host_len+1); + memcpy(cs.data(), from->data() + 5, host_len); + host = QString::fromLatin1(cs); + } + else if(atype == 0x04) { + full_len += 16; + if((int)from->size() < full_len) + return 0; + Q_UINT8 a6[16]; + memcpy(a6, from->data() + 4, 16); + addr.setAddress(a6); + } + + full_len += 2; + if((int)from->size() < full_len) + return 0; + + QByteArray a = ByteStream::takeArray(from, full_len); + + Q_UINT16 p; + memcpy(&p, a.data() + full_len - 2, 2); + + s->version = a[0]; + s->cmd = a[1]; + s->address_type = atype; + s->host = host; + s->addr = addr; + s->port = ntohs(p); + + return 1; +} + +enum { StepVersion, StepAuth, StepRequest }; + +class SocksClient::Private +{ +public: + Private() {} + + BSocket sock; + QString host; + int port; + QString user, pass; + QString real_host; + int real_port; + + QByteArray recvBuf; + bool active; + int step; + int authMethod; + bool incoming, waiting; + + QString rhost; + int rport; + + int pending; + + bool udp; + QString udpAddr; + int udpPort; +}; + +SocksClient::SocksClient(QObject *parent) +:ByteStream(parent) +{ + init(); + + d->incoming = false; +} + +SocksClient::SocksClient(int s, QObject *parent) +:ByteStream(parent) +{ + init(); + + d->incoming = true; + d->waiting = true; + d->sock.setSocket(s); +} + +void SocksClient::init() +{ + d = new Private; + connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected())); + connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed())); + connect(&d->sock, SIGNAL(delayedCloseFinished()), SLOT(sock_delayedCloseFinished())); + connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); + connect(&d->sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int))); + connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int))); + + reset(true); +} + +SocksClient::~SocksClient() +{ + reset(true); + delete d; +} + +void SocksClient::reset(bool clear) +{ + if(d->sock.state() != BSocket::Idle) + d->sock.close(); + if(clear) + clearReadBuffer(); + d->recvBuf.resize(0); + d->active = false; + d->waiting = false; + d->udp = false; + d->pending = 0; +} + +bool SocksClient::isIncoming() const +{ + return d->incoming; +} + +void SocksClient::setAuth(const QString &user, const QString &pass) +{ + d->user = user; + d->pass = pass; +} + +void SocksClient::connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port, bool udpMode) +{ + reset(true); + + d->host = proxyHost; + d->port = proxyPort; + d->real_host = host; + d->real_port = port; + d->udp = udpMode; + +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: Connecting to %s:%d", proxyHost.latin1(), proxyPort); + if(d->user.isEmpty()) + fprintf(stderr, "\n"); + else + fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1()); +#endif + d->sock.connectToHost(d->host, d->port); +} + +bool SocksClient::isOpen() const +{ + return d->active; +} + +void SocksClient::close() +{ + d->sock.close(); + if(d->sock.bytesToWrite() == 0) + reset(); +} + +void SocksClient::writeData(const QByteArray &buf) +{ + d->pending += buf.size(); + d->sock.write(buf); +} + +void SocksClient::write(const QByteArray &buf) +{ + if(d->active && !d->udp) + d->sock.write(buf); +} + +QByteArray SocksClient::read(int bytes) +{ + return ByteStream::read(bytes); +} + +int SocksClient::bytesAvailable() const +{ + return ByteStream::bytesAvailable(); +} + +int SocksClient::bytesToWrite() const +{ + if(d->active) + return d->sock.bytesToWrite(); + else + return 0; +} + +void SocksClient::sock_connected() +{ +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: Connected\n"); +#endif + + d->step = StepVersion; + writeData(spc_set_version()); +} + +void SocksClient::sock_connectionClosed() +{ + if(d->active) { + reset(); + connectionClosed(); + } + else { + error(ErrProxyNeg); + } +} + +void SocksClient::sock_delayedCloseFinished() +{ + if(d->active) { + reset(); + delayedCloseFinished(); + } +} + +void SocksClient::sock_readyRead() +{ + QByteArray block = d->sock.read(); + + if(!d->active) { + if(d->incoming) + processIncoming(block); + else + processOutgoing(block); + } + else { + if(!d->udp) { + appendRead(block); + readyRead(); + } + } +} + +void SocksClient::processOutgoing(const QByteArray &block) +{ +#ifdef PROX_DEBUG + // show hex + fprintf(stderr, "SocksClient: client recv { "); + for(int n = 0; n < (int)block.size(); ++n) + fprintf(stderr, "%02X ", (unsigned char)block[n]); + fprintf(stderr, " } \n"); +#endif + ByteStream::appendArray(&d->recvBuf, block); + + if(d->step == StepVersion) { + SPSS_VERSION s; + int r = sps_get_version(&d->recvBuf, &s); + if(r == -1) { + reset(true); + error(ErrProxyNeg); + return; + } + else if(r == 1) { + if(s.version != 0x05 || s.method == 0xff) { +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: Method selection failed\n"); +#endif + reset(true); + error(ErrProxyNeg); + return; + } + + QString str; + if(s.method == 0x00) { + str = "None"; + d->authMethod = AuthNone; + } + else if(s.method == 0x02) { + str = "Username/Password"; + d->authMethod = AuthUsername; + } + else { +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: Server wants to use unknown method '%02x'\n", s.method); +#endif + reset(true); + error(ErrProxyNeg); + return; + } + + if(d->authMethod == AuthNone) { + // no auth, go straight to the request + do_request(); + } + else if(d->authMethod == AuthUsername) { + d->step = StepAuth; +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: Authenticating [Username] ...\n"); +#endif + writeData(spc_set_authUsername(d->user.latin1(), d->pass.latin1())); + } + } + } + if(d->step == StepAuth) { + if(d->authMethod == AuthUsername) { + SPSS_AUTHUSERNAME s; + int r = sps_get_authUsername(&d->recvBuf, &s); + if(r == -1) { + reset(true); + error(ErrProxyNeg); + return; + } + else if(r == 1) { + if(s.version != 0x01) { + reset(true); + error(ErrProxyNeg); + return; + } + if(!s.success) { + reset(true); + error(ErrProxyAuth); + return; + } + + do_request(); + } + } + } + else if(d->step == StepRequest) { + SPS_CONNREQ s; + int r = sp_get_request(&d->recvBuf, &s); + if(r == -1) { + reset(true); + error(ErrProxyNeg); + return; + } + else if(r == 1) { + if(s.cmd != RET_SUCCESS) { +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: client << Error >> [%02x]\n", s.cmd); +#endif + reset(true); + if(s.cmd == RET_UNREACHABLE) + error(ErrHostNotFound); + else if(s.cmd == RET_CONNREFUSED) + error(ErrConnectionRefused); + else + error(ErrProxyNeg); + return; + } + +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: client << Success >>\n"); +#endif + if(d->udp) { + if(s.address_type == 0x03) + d->udpAddr = s.host; + else + d->udpAddr = s.addr.toString(); + d->udpPort = s.port; + } + + d->active = true; + + QGuardedPtr self = this; + connected(); + if(!self) + return; + + if(!d->recvBuf.isEmpty()) { + appendRead(d->recvBuf); + d->recvBuf.resize(0); + readyRead(); + } + } + } +} + +void SocksClient::do_request() +{ +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: Requesting ...\n"); +#endif + d->step = StepRequest; + int cmd = d->udp ? REQ_UDPASSOCIATE : REQ_CONNECT; + QByteArray buf; + if(!d->real_host.isEmpty()) + buf = sp_set_request(d->real_host, d->real_port, cmd); + else + buf = sp_set_request(QHostAddress(), 0, cmd); + writeData(buf); +} + +void SocksClient::sock_bytesWritten(int x) +{ + int bytes = x; + if(d->pending >= bytes) { + d->pending -= bytes; + bytes = 0; + } + else { + bytes -= d->pending; + d->pending = 0; + } + if(bytes > 0) + bytesWritten(bytes); +} + +void SocksClient::sock_error(int x) +{ + if(d->active) { + reset(); + error(ErrRead); + } + else { + reset(true); + if(x == BSocket::ErrHostNotFound) + error(ErrProxyConnect); + else if(x == BSocket::ErrConnectionRefused) + error(ErrProxyConnect); + else if(x == BSocket::ErrRead) + error(ErrProxyNeg); + } +} + +void SocksClient::serve() +{ + d->waiting = false; + d->step = StepVersion; + continueIncoming(); +} + +void SocksClient::processIncoming(const QByteArray &block) +{ +#ifdef PROX_DEBUG + // show hex + fprintf(stderr, "SocksClient: server recv { "); + for(int n = 0; n < (int)block.size(); ++n) + fprintf(stderr, "%02X ", (unsigned char)block[n]); + fprintf(stderr, " } \n"); +#endif + ByteStream::appendArray(&d->recvBuf, block); + + if(!d->waiting) + continueIncoming(); +} + +void SocksClient::continueIncoming() +{ + if(d->recvBuf.isEmpty()) + return; + + if(d->step == StepVersion) { + SPCS_VERSION s; + int r = spc_get_version(&d->recvBuf, &s); + if(r == -1) { + reset(true); + error(ErrProxyNeg); + return; + } + else if(r == 1) { + if(s.version != 0x05) { + reset(true); + error(ErrProxyNeg); + return; + } + + int methods = 0; + for(int n = 0; n < (int)s.methodList.size(); ++n) { + unsigned char c = s.methodList[n]; + if(c == 0x00) + methods |= AuthNone; + else if(c == 0x02) + methods |= AuthUsername; + } + d->waiting = true; + incomingMethods(methods); + } + } + else if(d->step == StepAuth) { + SPCS_AUTHUSERNAME s; + int r = spc_get_authUsername(&d->recvBuf, &s); + if(r == -1) { + reset(true); + error(ErrProxyNeg); + return; + } + else if(r == 1) { + d->waiting = true; + incomingAuth(s.user, s.pass); + } + } + else if(d->step == StepRequest) { + SPS_CONNREQ s; + int r = sp_get_request(&d->recvBuf, &s); + if(r == -1) { + reset(true); + error(ErrProxyNeg); + return; + } + else if(r == 1) { + d->waiting = true; + if(s.cmd == REQ_CONNECT) { + if(!s.host.isEmpty()) + d->rhost = s.host; + else + d->rhost = s.addr.toString(); + d->rport = s.port; + incomingConnectRequest(d->rhost, d->rport); + } + else if(s.cmd == REQ_UDPASSOCIATE) { + incomingUDPAssociateRequest(); + } + else { + requestDeny(); + return; + } + } + } +} + +void SocksClient::chooseMethod(int method) +{ + if(d->step != StepVersion || !d->waiting) + return; + + unsigned char c; + if(method == AuthNone) { + d->step = StepRequest; + c = 0x00; + } + else { + d->step = StepAuth; + c = 0x02; + } + + // version response + d->waiting = false; + writeData(sps_set_version(c)); + continueIncoming(); +} + +void SocksClient::authGrant(bool b) +{ + if(d->step != StepAuth || !d->waiting) + return; + + if(b) + d->step = StepRequest; + + // auth response + d->waiting = false; + writeData(sps_set_authUsername(b)); + if(!b) { + reset(true); + return; + } + continueIncoming(); +} + +void SocksClient::requestDeny() +{ + if(d->step != StepRequest || !d->waiting) + return; + + // response + d->waiting = false; + writeData(sp_set_request(d->rhost, d->rport, RET_UNREACHABLE)); + reset(true); +} + +void SocksClient::grantConnect() +{ + if(d->step != StepRequest || !d->waiting) + return; + + // response + d->waiting = false; + writeData(sp_set_request(d->rhost, d->rport, RET_SUCCESS)); + d->active = true; +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: server << Success >>\n"); +#endif + + if(!d->recvBuf.isEmpty()) { + appendRead(d->recvBuf); + d->recvBuf.resize(0); + readyRead(); + } +} + +void SocksClient::grantUDPAssociate(const QString &relayHost, int relayPort) +{ + if(d->step != StepRequest || !d->waiting) + return; + + // response + d->waiting = false; + writeData(sp_set_request(relayHost, relayPort, RET_SUCCESS)); + d->udp = true; + d->active = true; +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: server << Success >>\n"); +#endif + + if(!d->recvBuf.isEmpty()) + d->recvBuf.resize(0); +} + +QHostAddress SocksClient::peerAddress() const +{ + return d->sock.peerAddress(); +} + +Q_UINT16 SocksClient::peerPort() const +{ + return d->sock.peerPort(); +} + +QString SocksClient::udpAddress() const +{ + return d->udpAddr; +} + +Q_UINT16 SocksClient::udpPort() const +{ + return d->udpPort; +} + +SocksUDP *SocksClient::createUDP(const QString &host, int port, const QHostAddress &routeAddr, int routePort) +{ + return new SocksUDP(this, host, port, routeAddr, routePort); +} + +//---------------------------------------------------------------------------- +// SocksServer +//---------------------------------------------------------------------------- +class SocksServer::Private +{ +public: + Private() {} + + ServSock serv; + QPtrList incomingConns; + QSocketDevice *sd; + QSocketNotifier *sn; +}; + +SocksServer::SocksServer(QObject *parent) +:QObject(parent) +{ + d = new Private; + d->sd = 0; + d->sn = 0; + connect(&d->serv, SIGNAL(connectionReady(int)), SLOT(connectionReady(int))); +} + +SocksServer::~SocksServer() +{ + stop(); + d->incomingConns.setAutoDelete(true); + d->incomingConns.clear(); + delete d; +} + +bool SocksServer::isActive() const +{ + return d->serv.isActive(); +} + +bool SocksServer::listen(Q_UINT16 port, bool udp) +{ + stop(); + if(!d->serv.listen(port)) + return false; + if(udp) { + d->sd = new QSocketDevice(QSocketDevice::Datagram); + d->sd->setBlocking(false); + if(!d->sd->bind(QHostAddress(), port)) { + delete d->sd; + d->sd = 0; + d->serv.stop(); + return false; + } + d->sn = new QSocketNotifier(d->sd->socket(), QSocketNotifier::Read); + connect(d->sn, SIGNAL(activated(int)), SLOT(sn_activated(int))); + } + return true; +} + +void SocksServer::stop() +{ + delete d->sn; + d->sn = 0; + delete d->sd; + d->sd = 0; + d->serv.stop(); +} + +int SocksServer::port() const +{ + return d->serv.port(); +} + +QHostAddress SocksServer::address() const +{ + return d->serv.address(); +} + +SocksClient *SocksServer::takeIncoming() +{ + if(d->incomingConns.isEmpty()) + return 0; + + SocksClient *c = d->incomingConns.getFirst(); + d->incomingConns.removeRef(c); + + // we don't care about errors anymore + disconnect(c, SIGNAL(error(int)), this, SLOT(connectionError())); + + // don't serve the connection until the event loop, to give the caller a chance to map signals + QTimer::singleShot(0, c, SLOT(serve())); + + return c; +} + +void SocksServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data) +{ + if(d->sd) { + d->sd->setBlocking(true); + d->sd->writeBlock(data.data(), data.size(), addr, port); + d->sd->setBlocking(false); + } +} + +void SocksServer::connectionReady(int s) +{ + SocksClient *c = new SocksClient(s, this); + connect(c, SIGNAL(error(int)), this, SLOT(connectionError())); + d->incomingConns.append(c); + incomingReady(); +} + +void SocksServer::connectionError() +{ + SocksClient *c = (SocksClient *)sender(); + d->incomingConns.removeRef(c); + c->deleteLater(); +} + +void SocksServer::sn_activated(int) +{ + QByteArray buf(8192); + int actual = d->sd->readBlock(buf.data(), buf.size()); + buf.resize(actual); + QHostAddress pa = d->sd->peerAddress(); + int pp = d->sd->peerPort(); + SPS_UDP s; + int r = sp_read_udp(&buf, &s); + if(r != 1) + return; + incomingUDP(s.host, s.port, pa, pp, s.data); +} + +// CS_NAMESPACE_END + +#include "socks.moc" diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/socks.h b/kopete/protocols/jabber/libiris/cutestuff/network/socks.h new file mode 100644 index 00000000..8f1e4ddc --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/socks.h @@ -0,0 +1,160 @@ +/* + * socks.h - SOCKS5 TCP proxy client/server + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef CS_SOCKS_H +#define CS_SOCKS_H + +#include"bytestream.h" + +// CS_NAMESPACE_BEGIN + +class QHostAddress; +class SocksClient; +class SocksServer; + +class SocksUDP : public QObject +{ + Q_OBJECT +public: + ~SocksUDP(); + + void change(const QString &host, int port); + void write(const QByteArray &data); + +signals: + void packetReady(const QByteArray &data); + +private slots: + void sn_activated(int); + +private: + class Private; + Private *d; + + friend class SocksClient; + SocksUDP(SocksClient *sc, const QString &host, int port, const QHostAddress &routeAddr, int routePort); +}; + +class SocksClient : public ByteStream +{ + Q_OBJECT +public: + enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth }; + enum Method { AuthNone=0x0001, AuthUsername=0x0002 }; + enum Request { ReqConnect, ReqUDPAssociate }; + SocksClient(QObject *parent=0); + SocksClient(int, QObject *parent=0); + ~SocksClient(); + + bool isIncoming() const; + + // outgoing + void setAuth(const QString &user, const QString &pass=""); + void connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port, bool udpMode=false); + + // incoming + void chooseMethod(int); + void authGrant(bool); + void requestDeny(); + void grantConnect(); + void grantUDPAssociate(const QString &relayHost, int relayPort); + + // from ByteStream + bool isOpen() const; + void close(); + void write(const QByteArray &); + QByteArray read(int bytes=0); + int bytesAvailable() const; + int bytesToWrite() const; + + // remote address + QHostAddress peerAddress() const; + Q_UINT16 peerPort() const; + + // udp + QString udpAddress() const; + Q_UINT16 udpPort() const; + SocksUDP *createUDP(const QString &host, int port, const QHostAddress &routeAddr, int routePort); + +signals: + // outgoing + void connected(); + + // incoming + void incomingMethods(int); + void incomingAuth(const QString &user, const QString &pass); + void incomingConnectRequest(const QString &host, int port); + void incomingUDPAssociateRequest(); + +private slots: + void sock_connected(); + void sock_connectionClosed(); + void sock_delayedCloseFinished(); + void sock_readyRead(); + void sock_bytesWritten(int); + void sock_error(int); + void serve(); + +private: + class Private; + Private *d; + + void init(); + void reset(bool clear=false); + void do_request(); + void processOutgoing(const QByteArray &); + void processIncoming(const QByteArray &); + void continueIncoming(); + void writeData(const QByteArray &a); +}; + +class SocksServer : public QObject +{ + Q_OBJECT +public: + SocksServer(QObject *parent=0); + ~SocksServer(); + + bool isActive() const; + bool listen(Q_UINT16 port, bool udp=false); + void stop(); + int port() const; + QHostAddress address() const; + SocksClient *takeIncoming(); + + void writeUDP(const QHostAddress &addr, int port, const QByteArray &data); + +signals: + void incomingReady(); + void incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data); + +private slots: + void connectionReady(int); + void connectionError(); + void sn_activated(int); + +private: + class Private; + Private *d; +}; + +// CS_NAMESPACE_END + +#endif diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp new file mode 100644 index 00000000..0c454c49 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp @@ -0,0 +1,320 @@ +/* + * srvresolver.cpp - class to simplify SRV lookups + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include"srvresolver.h" + +#include +#include +#include +#include"safedelete.h" + +#ifndef NO_NDNS +#include"ndns.h" +#endif + +// CS_NAMESPACE_BEGIN + +static void sortSRVList(QValueList &list) +{ + QValueList tmp = list; + list.clear(); + + while(!tmp.isEmpty()) { + QValueList::Iterator p = tmp.end(); + for(QValueList::Iterator it = tmp.begin(); it != tmp.end(); ++it) { + if(p == tmp.end()) + p = it; + else { + int a = (*it).priority; + int b = (*p).priority; + int j = (*it).weight; + int k = (*p).weight; + if(a < b || (a == b && j < k)) + p = it; + } + } + list.append(*p); + tmp.remove(p); + } +} + +class SrvResolver::Private +{ +public: + Private() {} + + QDns *qdns; +#ifndef NO_NDNS + NDns ndns; +#endif + + bool failed; + QHostAddress resultAddress; + Q_UINT16 resultPort; + + bool srvonly; + QString srv; + QValueList servers; + bool aaaa; + + QTimer t; + SafeDelete sd; +}; + +SrvResolver::SrvResolver(QObject *parent) +:QObject(parent) +{ + d = new Private; + d->qdns = 0; + +#ifndef NO_NDNS + connect(&d->ndns, SIGNAL(resultsReady()), SLOT(ndns_done())); +#endif + connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout())); + stop(); +} + +SrvResolver::~SrvResolver() +{ + stop(); + delete d; +} + +void SrvResolver::resolve(const QString &server, const QString &type, const QString &proto) +{ + stop(); + + d->failed = false; + d->srvonly = false; + d->srv = QString("_") + type + "._" + proto + '.' + server; + d->t.start(15000, true); + d->qdns = new QDns; + connect(d->qdns, SIGNAL(resultsReady()), SLOT(qdns_done())); + d->qdns->setRecordType(QDns::Srv); + d->qdns->setLabel(d->srv); +} + +void SrvResolver::resolveSrvOnly(const QString &server, const QString &type, const QString &proto) +{ + stop(); + + d->failed = false; + d->srvonly = true; + d->srv = QString("_") + type + "._" + proto + '.' + server; + d->t.start(15000, true); + d->qdns = new QDns; + connect(d->qdns, SIGNAL(resultsReady()), SLOT(qdns_done())); + d->qdns->setRecordType(QDns::Srv); + d->qdns->setLabel(d->srv); +} + +void SrvResolver::next() +{ + if(d->servers.isEmpty()) + return; + + tryNext(); +} + +void SrvResolver::stop() +{ + if(d->t.isActive()) + d->t.stop(); + if(d->qdns) { + d->qdns->disconnect(this); + d->sd.deleteLater(d->qdns); + d->qdns = 0; + } +#ifndef NO_NDNS + if(d->ndns.isBusy()) + d->ndns.stop(); +#endif + d->resultAddress = QHostAddress(); + d->resultPort = 0; + d->servers.clear(); + d->srv = ""; + d->failed = true; +} + +bool SrvResolver::isBusy() const +{ +#ifndef NO_NDNS + if(d->qdns || d->ndns.isBusy()) +#else + if(d->qdns) +#endif + return true; + else + return false; +} + +QValueList SrvResolver::servers() const +{ + return d->servers; +} + +bool SrvResolver::failed() const +{ + return d->failed; +} + +QHostAddress SrvResolver::resultAddress() const +{ + return d->resultAddress; +} + +Q_UINT16 SrvResolver::resultPort() const +{ + return d->resultPort; +} + +void SrvResolver::tryNext() +{ +#ifndef NO_NDNS + d->ndns.resolve(d->servers.first().name); +#else + d->qdns = new QDns; + connect(d->qdns, SIGNAL(resultsReady()), SLOT(ndns_done())); + if(d->aaaa) + d->qdns->setRecordType(QDns::Aaaa); // IPv6 + else + d->qdns->setRecordType(QDns::A); // IPv4 + d->qdns->setLabel(d->servers.first().name); +#endif +} + +void SrvResolver::qdns_done() +{ + if(!d->qdns) + return; + + // apparently we sometimes get this signal even though the results aren't ready + if(d->qdns->isWorking()) + return; + d->t.stop(); + + SafeDeleteLock s(&d->sd); + + // grab the server list and destroy the qdns object + QValueList list; + if(d->qdns->recordType() == QDns::Srv) + list = d->qdns->servers(); + d->qdns->disconnect(this); + d->sd.deleteLater(d->qdns); + d->qdns = 0; + + if(list.isEmpty()) { + stop(); + resultsReady(); + return; + } + sortSRVList(list); + d->servers = list; + + if(d->srvonly) + resultsReady(); + else { + // kick it off + d->aaaa = true; + tryNext(); + } +} + +void SrvResolver::ndns_done() +{ +#ifndef NO_NDNS + SafeDeleteLock s(&d->sd); + + uint r = d->ndns.result(); + int port = d->servers.first().port; + d->servers.remove(d->servers.begin()); + + if(r) { + d->resultAddress = QHostAddress(d->ndns.result()); + d->resultPort = port; + resultsReady(); + } + else { + // failed? bail if last one + if(d->servers.isEmpty()) { + stop(); + resultsReady(); + return; + } + + // otherwise try the next + tryNext(); + } +#else + if(!d->qdns) + return; + + // apparently we sometimes get this signal even though the results aren't ready + if(d->qdns->isWorking()) + return; + + SafeDeleteLock s(&d->sd); + + // grab the address list and destroy the qdns object + QValueList list; + if(d->qdns->recordType() == QDns::A || d->qdns->recordType() == QDns::Aaaa) + list = d->qdns->addresses(); + d->qdns->disconnect(this); + d->sd.deleteLater(d->qdns); + d->qdns = 0; + + if(!list.isEmpty()) { + int port = d->servers.first().port; + d->servers.remove(d->servers.begin()); + d->aaaa = true; + + d->resultAddress = list.first(); + d->resultPort = port; + resultsReady(); + } + else { + if(!d->aaaa) + d->servers.remove(d->servers.begin()); + d->aaaa = !d->aaaa; + + // failed? bail if last one + if(d->servers.isEmpty()) { + stop(); + resultsReady(); + return; + } + + // otherwise try the next + tryNext(); + } +#endif +} + +void SrvResolver::t_timeout() +{ + SafeDeleteLock s(&d->sd); + + stop(); + resultsReady(); +} + +// CS_NAMESPACE_END + +#include "srvresolver.moc" diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h new file mode 100644 index 00000000..6c9ac4f3 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h @@ -0,0 +1,65 @@ +/* + * srvresolver.h - class to simplify SRV lookups + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef CS_SRVRESOLVER_H +#define CS_SRVRESOLVER_H + +#include +#include + +// CS_NAMESPACE_BEGIN + +class SrvResolver : public QObject +{ + Q_OBJECT +public: + SrvResolver(QObject *parent=0); + ~SrvResolver(); + + void resolve(const QString &server, const QString &type, const QString &proto); + void resolveSrvOnly(const QString &server, const QString &type, const QString &proto); + void next(); + void stop(); + bool isBusy() const; + + QValueList servers() const; + + bool failed() const; + QHostAddress resultAddress() const; + Q_UINT16 resultPort() const; + +signals: + void resultsReady(); + +private slots: + void qdns_done(); + void ndns_done(); + void t_timeout(); + +private: + class Private; + Private *d; + + void tryNext(); +}; + +// CS_NAMESPACE_END + +#endif -- cgit v1.2.1