Skip to content

A White-listing QNetworkAccessManager

Saturday, 5 June 2010  |  Rich

My last blog post showed how a proxy class can be used to monitor the requests being made by a QNetworkAccessManager, and illustrated it using an example that displayed those requests graphically. This post will cover another use of proxy QNetworkAccessManagers, specifically modifying requests and responses. The example we'll use is a very simple one, we'll look at a class that will restrict a QNetworkAccessManager so that it can only access domains listed in a white-list.

Before we start, lets consider the reasons why restricting the domains a QNetworkAccessManager can access might be useful. One major use is when developing hybrid applications using QtWebkit - restricting the domains that can be accessed allows you to expose internal facilities to Javascript without allowing data to be transmited to arbitrary sites. Another use is in system's like Plasma where it would allow applets to be restricted to certain domains. The example we'll use is a mini-browser that can only access www.kde.org.

The class definition for our white-listing manager is very simple. It has a method to add domains to the whitelist, and one to test if a domain is white-listed. The only other method is the reimplementation of createRequest().

class WhiteListNetworkAccessManager : public QNetworkAccessManager
{
    Q_OBJECT

public:
    explicit WhiteListNetworkAccessManager( QObject *parent = 0 );

    bool isAllowed( const QString &domain );

public slots:
    void addDomain( const QString &domain );

protected:
    QNetworkReply *createRequest( Operation op,
                                  const QNetworkRequest &req,
                                  QIODevice *outgoingData );

private:
    QMap<QString, int> allowedDomains;
}

Looking at the two methods to manage the white-list first, we can see that they're both trivial. The first simply adds a domain to the map of allowed domains, the second tests if the map contains the specified domain as a key:

void WhiteListNetworkAccessManager::addDomain( const QString &domain )
{
    allowedDomains.insert( domain, 1 );
}

bool WhiteListNetworkAccessManager::isAllowed( const QString &domain )
{
    return (allowedDomains[domain] == 1); // Rely on Qt's default of 0 for non-existent ints
}

The only non-trivial part of this is that we rely on knowing that QMap will return a default value of 0 for maps with int based values if the key is not present.

The final method of our class is almost as trivial as these, it checks if the domain a request is being made to is on the white-list and if it is hands the request to the base-class. If however the request is to a non-allowed domain, it modifies the request to use an invalid url (one with an unsupported protocol). This causes the base-class implementation of the method to fail the request. It would be possible for us to create a response object of our own instead, but this would require quite a lot more code as we'd need to implement the full API of QNetworkReply. This might be the topic of a further example, but it's not required here.

QNetworkReply *WhiteListNetworkAccessManager::createRequest( Operation op,
                                                             const QNetworkRequest &req,
                                                             QIODevice *outgoingData )
{
    QNetworkRequest myReq( req );

    // If host is not whitelisted then kill it
    if ( !isAllowed( req.url().host() ) ) {
        myReq.setUrl( QUrl( QString("forbidden://localhost/") ) );
    }

    QNetworkReply *reply = QNetworkAccessManager::createRequest( op, myReq, outgoingData );

    return reply;
}

We've now covered the full implemtation of our class - that really is all there is to it. Using it is simply a matter of telling a QWebView to use it, and adding some domains to the white-list. An example usage is shown below:

    QWebView view;
    WhiteListNetworkAccessManager proxy;

    proxy.addDomain( QString("www.kde.org") );
    view.page()->setNetworkAccessManager( &proxy );

As usual, the full source code for this example can be found in my gitorious repository a http://gitorious.org/qt-examples/qt-examples/trees/master.