Commit 98946bcb authored by Dawit Alemayehu's avatar Dawit Alemayehu
Browse files

KIO proxy overhaul Part I:

Features:
- Switched the PAC file parser from KJS to QtScript.
- Implemented the Microsoft IPv6 PAC extensions.
- Implemented support for returning SOCKS proxy information (only SOCKS5).
- Added a new dbus call, proxiesForUrl, that returns all the proxy addresses
  for a given url.
- Added a new kpactest file to test the new MS IPv6 extension functions.

Fixes:
- Fixed the DateRange function by adding back two missing months in the static
  months array.
- Fixed the DNS related functions by fixing how the lookup works through
  the following modification to the KIO DNS caching code:
    * Added a function, lookupCachedHostInfoFor, for quering the KIO cache for
      information without causing a DNS query.
    * Added a function, cacheLookup, for inserting a DNS lookup result into
      the KIO cache.
- Fixed myIpAddress by using QNetworkInterface, though this fix is still far
  from ideal since it grabs the first suitable interface on multi-homed systems.

REVIEW: 100971
BUG: 153973
BUG: 267844
FIXED-IN: 4.7
DIGEST: KIO Proxy overhaul for KDE 4.7
parent dbe53461
......@@ -53,6 +53,8 @@ namespace KIO
HostInfoAgentPrivate(int cacheSize = 100);
virtual ~HostInfoAgentPrivate() {};
void lookupHost(const QString& hostName, QObject* receiver, const char* member);
QHostInfo lookupCachedHostInfoFor(const QString& hostName);
void cacheLookup(const QHostInfo&);
void setCacheSize(int s) { dnsCache.setMaxCost(s); }
void setTTL(int _ttl) { ttl = _ttl; }
private slots:
......@@ -117,6 +119,16 @@ void HostInfo::lookupHost(const QString& hostName, QObject* receiver,
hostInfoAgentPrivate->lookupHost(hostName, receiver, member);
}
QHostInfo HostInfo::lookupCachedHostInfoFor(const QString& hostName)
{
return hostInfoAgentPrivate->lookupCachedHostInfoFor(hostName);
}
void HostInfo::cacheLookup(const QHostInfo& info)
{
hostInfoAgentPrivate->cacheLookup(info);
}
void HostInfo::prefetchHost(const QString& hostName)
{
hostInfoAgentPrivate->lookupHost(hostName, 0, 0);
......@@ -181,6 +193,26 @@ void HostInfoAgentPrivate::lookupHost(const QString& hostName,
query->start(hostName);
}
QHostInfo HostInfoAgentPrivate::lookupCachedHostInfoFor(const QString& hostName)
{
QPair<QHostInfo, QTime>* info = dnsCache.object(hostName);
if (info && info->second.addSecs(ttl) >= QTime::currentTime())
return info->first;
return QHostInfo();
}
void HostInfoAgentPrivate::cacheLookup(const QHostInfo& info)
{
if (info.hostName().isEmpty())
return;
if (info.error() != QHostInfo::NoError)
return;
dnsCache.insert(info.hostName(), new QPair<QHostInfo, QTime>(info, QTime::currentTime()));
}
void HostInfoAgentPrivate::queryFinished(const QHostInfo& info)
{
Query* query = static_cast<Query* >(sender());
......
......@@ -25,11 +25,15 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
#include <QtCore/QObject>
#include "kio_export.h"
class QHostInfo;
namespace KIO
{
namespace HostInfo
{
KIO_EXPORT void lookupHost(const QString& hostName, QObject* receiver, const char* member);
KIO_EXPORT QHostInfo lookupCachedHostInfoFor(const QString& hostName);
KIO_EXPORT void cacheLookup(const QHostInfo& info);
// used by khtml's DNS prefetching feature
KIO_EXPORT void prefetchHost(const QString& hostName);
......
......@@ -15,7 +15,7 @@ set(kded_proxyscout_PART_SRCS
kde4_add_plugin(kded_proxyscout ${kded_proxyscout_PART_SRCS})
target_link_libraries(kded_proxyscout ${KDE4_KDECORE_LIBS} kio kjs )
target_link_libraries(kded_proxyscout ${QT_QTSCRIPT_LIBRARY} ${KDE4_KDECORE_LIBS} kio)
# this needs -lresolv e.g on Slackware, but not on FreeBSD
if (HAVE_RESOLV_LIBRARY)
......@@ -27,7 +27,7 @@ install(TARGETS kded_proxyscout DESTINATION ${PLUGIN_INSTALL_DIR} )
########### next target ###############
set(kpac_dhcp_helper_SRCS kpac_dhcp_helper.c )
set(kpac_dhcp_helper_SRCS kpac_dhcp_helper.c)
kde4_add_executable(kpac_dhcp_helper NOGUI ${kpac_dhcp_helper_SRCS})
......@@ -48,6 +48,3 @@ install(TARGETS kpac_dhcp_helper DESTINATION ${LIBEXEC_INSTALL_DIR} )
install( FILES proxyscout.notifyrc DESTINATION ${DATA_INSTALL_DIR}/proxyscout )
install( FILES proxyscout.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kded )
/* This file is part of the KDE Libraries
Copyright (c) 2001 Malte Starostik <malte@kde.org>
Copyright (c) 2011 Dawit Alemayehu <adawit@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
......
......@@ -7,11 +7,11 @@
function FindProxyForURL( url, host )
{
var result = "PROXY http://" + myIPAddress();
var result = "PROXY http://" + myIpAddress();
// plainhost1
if ( !isPlainHostName( "foo" ) )
result += "/plainhost1=failed"
result += "/plainhost1=failed";
// plainhost2
if ( isPlainHostName( "foo.bar" ) )
result += "/plainhost2=failed";
......
/*
This is a script to test the Microsoft IPv6 extension to PAC only. If you wanted
to test the original PAC specification, use 'kpactest.pac'.
To use it,set this script as proxy config file and run:
qdbus org.kde,kded /modules/proxyscout proxyForUrl http://blah (URL doesn't matter)
If everything succeeds, the output you get will be http://<your IP>/. If not,
you would get http://<your IP>/<test-result> where <test-result> contains the
tests that "failed". You can lookup the failed test name in the comments below
and file a report in the KDE bug tracking system if you feel the problem is in
the implementation.
Please note that since myIpAddressEx returns a semi-colon delimited list of all the
valid ip address for your machine matches, including the IPv6 representations for
the same network interface, you should always see at least two http://<your IP>/
in the result.
*/
function FindProxyForURLEx( url, host )
{
var result = "";
// isResolvableEx1
// on failure make sure you can resolve www.kde.org correctly :-)
if ( !isResolvableEx( "www.kde.org" ) )
result += "/isResolvableEx1=failed";
// isResolvableEx2
// on failure make sure dummy.invalid doesn't resolve :-)
if ( isResolvableEx( "dummy.invalid" ) )
result += "/isResolvableEx2=failed";
// isInNetEx1
// on failure check if localhost resolves to 127.0.0.1 as it should
if ( isInNetEx( "localhost", "1.2.3.4/8" ) )
result += "/isInNetEx1=failed";
// isinnetEx2
if ( isInNetEx( "1.2.3.4", "1.2.3.5/32" ) )
result += "/isInNetEx2=failed";
// isinnet3
if ( !isInNetEx( "1.2.3.4", "1.2.3.5/24" ) )
result += "/isInNetEx3=failed";
// dnsResolveEx1
// on failure check if localhost resolves to 127.0.0.1 as it should
if ( dnsResolveEx( "localhost" ).indexOf("127.0.0.1") == -1 )
result += "/dnsResolveEx1=failed";
// sortIpAddressList
var sorted = sortIpAddressList("2001:4898:28:3:201:2ff:feea:fc14;157.59.139.22;fe80::5efe:157.59.139.2");
if ( sorted != "fe80::5efe:157.59.139.2;2001:4898:28:3:201:2ff:feea:fc14;157.59.139.22" )
result += "/sortIpAddressList=failed";
var output = new Array();
var items = myIpAddressEx().split(";");
for (var i = 0; i < items.length; ++i) {
var entry = "PROXY http://" + items[i] + result;
output[i] = entry;
}
return output.join(';');
}
/*
Copyright (c) 2003 Malte Starostik <malte@kde.org>
Copyright (c) 2011 Dawit Alemayehu <adawit@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
......@@ -17,20 +18,22 @@
Boston, MA 02110-1301, USA.
*/
#include "proxyscout.h"
#include <cstdlib>
#include <ctime>
#include "discovery.h"
#include "script.h"
#include <kdebug.h>
#include <klocale.h>
#include <knotification.h>
#include <kprotocolmanager.h>
#include <kpluginfactory.h>
#include <kpluginloader.h>
#include <QtDBus/QtDBus>
#include "proxyscout.moc"
#include "discovery.h"
#include "script.h"
#include <cstdlib>
#include <ctime>
K_PLUGIN_FACTORY(ProxyScoutFactory,
registerPlugin<KPAC::ProxyScout>();
......@@ -40,8 +43,8 @@ K_EXPORT_PLUGIN(ProxyScoutFactory("KProxyScoutd"))
namespace KPAC
{
ProxyScout::QueuedRequest::QueuedRequest( const QDBusMessage &reply, const KUrl& u )
: transaction( reply ), url( u )
ProxyScout::QueuedRequest::QueuedRequest( const QDBusMessage &reply, const KUrl& u, bool sendall )
: transaction( reply ), url( u ), sendAll(sendall)
{
}
......@@ -50,7 +53,8 @@ namespace KPAC
m_componentData("proxyscout"),
m_downloader( 0 ),
m_script( 0 ),
m_suspendTime( 0 )
m_suspendTime( 0 ),
m_debugArea (KDebug::registerArea("proxyscout"))
{
}
......@@ -59,28 +63,62 @@ namespace KPAC
delete m_script;
}
QStringList ProxyScout::proxiesForUrl( const QString& checkUrl, const QDBusMessage &msg )
{
KUrl url(checkUrl);
if (m_suspendTime) {
if ( std::time( 0 ) - m_suspendTime < 300 ) {
return QStringList (QLatin1String("DIRECT"));
}
m_suspendTime = 0;
}
// Never use a proxy for the script itself
if (m_downloader && url.equals(m_downloader->scriptUrl(), KUrl::CompareWithoutTrailingSlash)) {
return QStringList (QLatin1String("DIRECT"));
}
if (m_script) {
return handleRequest(url);
}
if (m_downloader || startDownload()) {
msg.setDelayedReply(true);
m_requestQueue.append( QueuedRequest( msg, url, true ) );
return QStringList(); // return value will be ignored
}
return QStringList(QLatin1String("DIRECT"));
}
QString ProxyScout::proxyForUrl( const QString& checkUrl, const QDBusMessage &msg )
{
KUrl url( checkUrl );
KUrl url(checkUrl);
if ( m_suspendTime )
{
if ( std::time( 0 ) - m_suspendTime < 300 ) return "DIRECT";
if (m_suspendTime) {
if ( std::time( 0 ) - m_suspendTime < 300 ) {
return QLatin1String("DIRECT");
}
m_suspendTime = 0;
}
// Never use a proxy for the script itself
if ( m_downloader && url.equals( m_downloader->scriptUrl(), KUrl::CompareWithoutTrailingSlash ) ) return "DIRECT";
if (m_downloader && url.equals(m_downloader->scriptUrl(), KUrl::CompareWithoutTrailingSlash)) {
return QLatin1String("DIRECT");
}
if ( m_script ) return handleRequest( url );
if (m_script) {
return handleRequest(url).first();
}
if ( m_downloader || startDownload() )
{
if (m_downloader || startDownload()) {
msg.setDelayedReply(true);
m_requestQueue.append( QueuedRequest( msg, url ) );
return QString(); // return value will be ignored
}
else return "DIRECT";
return QLatin1String("DIRECT");
}
void ProxyScout::blackListProxy( const QString& proxy )
......@@ -113,91 +151,127 @@ namespace KPAC
default:
return false;
}
connect( m_downloader, SIGNAL( result( bool ) ),
SLOT( downloadResult( bool ) ) );
connect(m_downloader, SIGNAL(result(bool)), SLOT(downloadResult(bool)));
return true;
}
void ProxyScout::downloadResult( bool success )
{
if ( success )
if ( success ) {
try
{
m_script = new Script( m_downloader->script() );
}
catch ( const Script::Error& e )
{
kWarning() << "Error:" << e.message();
KNotification *notify= new KNotification ( "script-error" );
notify->setText( i18n("The proxy configuration script is invalid:\n%1" , e.message() ) );
notify->setComponentData(m_componentData);
notify->sendEvent();
success = false;
}
else
{
KNotification *notify = new KNotification ("download-error");
notify->setText( m_downloader->error() );
notify->setComponentData(m_componentData);
notify->sendEvent();
} else {
KNotification *notify = new KNotification ("download-error");
notify->setText( m_downloader->error() );
notify->setComponentData(m_componentData);
notify->sendEvent();
}
for ( RequestQueue::Iterator it = m_requestQueue.begin();
it != m_requestQueue.end(); ++it )
{
if ( success )
QDBusConnection::sessionBus().send( ( *it ).transaction.createReply( handleRequest( ( *it ).url ) ) );
else
QDBusConnection::sessionBus().send( ( *it ).transaction.createReply( QString( "DIRECT" ) ) );
if (success) {
for (RequestQueue::Iterator it = m_requestQueue.begin(), itEnd = m_requestQueue.end(); it != itEnd; ++it) {
if ((*it).sendAll) {
const QVariant result (handleRequest((*it).url));
QDBusConnection::sessionBus().send((*it).transaction.createReply(result));
} else {
const QVariant result (handleRequest((*it).url).first());
QDBusConnection::sessionBus().send((*it).transaction.createReply(result));
}
}
} else {
for (RequestQueue::Iterator it = m_requestQueue.begin(), itEnd = m_requestQueue.end(); it != itEnd; ++it) {
QDBusConnection::sessionBus().send((*it).transaction.createReply(QString::fromLatin1("DIRECT")));
}
}
m_requestQueue.clear();
m_downloader->deleteLater();
m_downloader = 0;
// Suppress further attempts for 5 minutes
if ( !success ) m_suspendTime = std::time( 0 );
if ( !success ) {
m_suspendTime = std::time( 0 );
}
}
QString ProxyScout::handleRequest( const KUrl& url )
QStringList ProxyScout::handleRequest( const KUrl& url )
{
try
{
QString result = m_script->evaluate( url );
const QStringList proxies = result.split( ';', QString::SkipEmptyParts );
for ( QStringList::ConstIterator it = proxies.begin();
it != proxies.end(); ++it )
{
QString proxy = ( *it ).trimmed();
if ( proxy.startsWith( QLatin1String( "PROXY" ) ) )
{
KUrl proxyURL( proxy = proxy.mid( 5 ).trimmed() );
// If the URL is invalid or the URL is valid but in opaque
// format which indicates a port number being present in
// this particular case, simply calling setProtocol() on
// it trashes the whole URL.
int len = proxyURL.protocol().length();
if ( !proxyURL.isValid() || proxy.indexOf( ":/", len ) != len )
proxy.prepend("http://");
if ( !m_blackList.contains( proxy ) )
return proxy;
if ( std::time( 0 ) - m_blackList[ proxy ] > 1800 ) // 30 minutes
{
// black listing expired
m_blackList.remove( proxy );
return proxy;
QStringList proxyList;
const QString result = m_script->evaluate(url).trimmed();
const QStringList proxies = result.split(QLatin1Char(';'), QString::SkipEmptyParts);
Q_FOREACH(const QString& proxy, proxies) {
QString mode, address;
const int keyIndex = proxy.indexOf(QLatin1Char(' '));
if (keyIndex == -1) {
address = proxy;
} else {
mode = proxy.left(keyIndex);
address = proxy.mid(keyIndex+1).trimmed();
}
const bool isProxy = (mode.compare(QLatin1String("PROXY"), Qt::CaseInsensitive) == 0);
const bool isSocks = (mode.compare(QLatin1String("SOCKS"), Qt::CaseInsensitive) == 0 ||
mode.compare(QLatin1String("SOCKS5"), Qt::CaseInsensitive) == 0);
if (!isProxy && !isSocks) {
continue;
}
KUrl proxyURL(address);
const int len = proxyURL.protocol().length();
// If the URL is invalid or the URL is valid but in opaque
// format, which indicates a port number being present, simply
// calling setProtocol() on it trashes the whole URL.
if (!proxyURL.isValid() || address.indexOf(QLatin1String(":/"), len) != len) {
const QString protocol = QLatin1String((isProxy ? "http://": "socks://"));
proxyURL = address.prepend(protocol);
if (!proxyURL.isValid()) {
continue;
}
}
else return "DIRECT";
if ( !m_blackList.contains( address ) ) {
proxyList << address;
} else if ( std::time( 0 ) - m_blackList[ address ] > 1800 ) { // 30 minutes
// black listing expired
m_blackList.remove( address );
proxyList << address;
}
}
if (!proxyList.isEmpty()) {
kDebug(m_debugArea) << proxyList;
return proxyList;
}
// FIXME: blacklist
}
catch ( const Script::Error& e )
{
KNotification *n=new KNotification( "evaluation-error" );
n->setText( i18n( "The proxy configuration script returned an error:\n%1" , e.message() ) );
n->setComponentData(m_componentData);
n->sendEvent();
kError() << e.message();
KNotification *n=new KNotification( "evaluation-error" );
n->setText( i18n( "The proxy configuration script returned an error:\n%1" , e.message() ) );
n->setComponentData(m_componentData);
n->sendEvent();
}
return "DIRECT";
return QStringList (QLatin1String("DIRECT"));
}
}
#include "proxyscout.moc"
// vim: ts=4 sw=4 et
......@@ -46,6 +46,7 @@ namespace KPAC
public Q_SLOTS:
Q_SCRIPTABLE QString proxyForUrl( const QString& checkUrl, const QDBusMessage & );
Q_SCRIPTABLE QStringList proxiesForUrl( const QString& checkUrl, const QDBusMessage & );
Q_SCRIPTABLE Q_NOREPLY void blackListProxy( const QString& proxy );
Q_SCRIPTABLE Q_NOREPLY void reset();
......@@ -54,7 +55,7 @@ namespace KPAC
private:
bool startDownload();
QString handleRequest( const KUrl& url );
QStringList handleRequest( const KUrl& url );
KComponentData m_componentData;
Downloader* m_downloader;
......@@ -63,10 +64,11 @@ namespace KPAC
struct QueuedRequest
{
QueuedRequest() {}
QueuedRequest( const QDBusMessage&, const KUrl& );
QueuedRequest( const QDBusMessage&, const KUrl&, bool sendall = false);
QDBusMessage transaction;
KUrl url;
bool sendAll;
};
typedef QList< QueuedRequest > RequestQueue;
RequestQueue m_requestQueue;
......@@ -74,6 +76,7 @@ namespace KPAC
typedef QMap< QString, time_t > BlackList;
BlackList m_blackList;
time_t m_suspendTime;
int m_debugArea;
};
}
......
This diff is collapsed.
/*
Copyright (c) 2003 Malte Starostik <malte@kde.org>
Copyright (c) 2011 Dawit Alemayehu <adawit@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
......@@ -23,9 +24,8 @@
#include <QtCore/QString>
#include <kjs/interpreter.h>
class KUrl;
class QScriptEngine;
namespace KPAC
{
......@@ -48,7 +48,7 @@ namespace KPAC
QString evaluate( const KUrl& );
private:
KJS::Interpreter* m_interpreter;
QScriptEngine* m_engine;
};
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment