Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qnetworkproxy_libproxy.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2017 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
7
8#ifndef QT_NO_NETWORKPROXY
9
10#include <QtCore/QByteArray>
11#include <QtCore/QMutex>
12#include <QtCore/QSemaphore>
13#include <QtCore/QUrl>
14#include <QtCore/private/qlatch_p.h>
15#include <QtCore/private/qeventdispatcher_unix_p.h>
16#include <QtCore/private/qthread_p.h>
17#include <QtCore/qapplicationstatic.h>
18
19#include <proxy.h>
20#include <dlfcn.h>
21
22QT_BEGIN_NAMESPACE
23
24using namespace Qt::StringLiterals;
25
26static bool isThreadingNeeded()
27{
28 // Try to guess if the libproxy we linked to is from the libproxy project
29 // or if it is from pacrunner. Neither library is thread-safe, but the one
30 // from libproxy is worse, since it may launch JS engines that don't take
31 // kindly to being executed from multiple threads (even if at different
32 // times). The pacrunner implementation doesn't suffer from this because
33 // the JS execution is out of process, in the pacrunner daemon.
34
35 void *sym;
36
37#ifdef Q_CC_GNU
38 // Search for the mangled name of the virtual table of the pacrunner
39 // extension. Even if libproxy begins using -fvisibility=hidden, this
40 // symbol can't be hidden.
41 sym = dlsym(RTLD_DEFAULT, "_ZTVN8libproxy19pacrunner_extensionE");
42#else
43 // The default libproxy one uses libmodman for its module management and
44 // leaks symbols because it doesn't use -fvisibility=hidden (as of
45 // v0.4.15).
46 sym = dlsym(RTLD_DEFAULT, "mm_info_ignore_hostname");
47#endif
48
49 return sym != nullptr;
50}
51
53{
55public:
58
59 QList<QUrl> getProxies(const QUrl &url);
60
61private:
62 struct Data {
63 // we leave the conversion to/from QUrl to the calling thread
64 const char *url;
65 char **proxies;
66 QLatch replyReady{1};
67 };
68
69 void run() override;
70
71 pxProxyFactory *factory; // not subject to the mutex
72
73 QMutex mutex;
74 QSemaphore requestReady;
75 Data *request;
76};
77
78Q_APPLICATION_STATIC(QLibProxyWrapper, libProxyWrapper)
79
80QLibProxyWrapper::QLibProxyWrapper()
81{
82 if (isThreadingNeeded()) {
83 setEventDispatcher(new QEventDispatcherUNIX); // don't allow the Glib one
84 start();
85 } else {
86 factory = px_proxy_factory_new();
87 Q_CHECK_PTR(factory);
88 }
89}
90
92{
93 if (isRunning()) {
94 requestInterruption();
95 requestReady.release();
96 wait();
97 } else {
98 px_proxy_factory_free(factory);
99 }
100}
101
102/*
103 Gets the list of proxies from libproxy, converted to QUrl list. Apply
104 thread-safety, though its documentation says otherwise, libproxy isn't
105 thread-safe.
106*/
108{
109 QByteArray encodedUrl = url.toEncoded();
110 Data data;
111 data.url = encodedUrl.constData();
112
113 {
114 QMutexLocker locker(&mutex);
115 if (isRunning()) {
116 // threaded mode
117 // it's safe to write to request because we hold the mutex:
118 // our aux thread is blocked waiting for work and no other thread
119 // could have got here
120 request = &data;
121 requestReady.release();
122
123 // wait for the reply
124 data.replyReady.wait();
125 } else {
126 // non-threaded mode
127 data.proxies = px_proxy_factory_get_proxies(factory, data.url);
128 }
129 }
130
131 QList<QUrl> ret;
132 if (data.proxies) {
133 for (int i = 0; data.proxies[i]; i++) {
134 ret.append(QUrl::fromEncoded(data.proxies[i]));
135 }
136 px_proxy_factory_free_proxies(data.proxies);
137 }
138 return ret;
139}
140
142{
143 factory = px_proxy_factory_new();
144 Q_CHECK_PTR(factory);
145
146 forever {
147 requestReady.acquire();
148 if (isInterruptionRequested())
149 break;
150 request->proxies = px_proxy_factory_get_proxies(factory, request->url);
151 request->replyReady.countDown();
152 }
153
154 px_proxy_factory_free(factory);
155}
156
157QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query)
158{
159 QList<QNetworkProxy> proxyList;
160
161 QUrl queryUrl;
162 QNetworkProxy::Capabilities requiredCapabilities(0);
163 switch (query.queryType()) {
164 //URL requests are directly supported by libproxy
165 case QNetworkProxyQuery::UrlRequest:
166 queryUrl = query.url();
167 break;
168 // fake URLs to get libproxy to tell us the SOCKS proxy
169 case QNetworkProxyQuery::TcpSocket:
170 if (queryUrl.scheme().isEmpty())
171 queryUrl.setScheme(QStringLiteral("tcp"));
172 queryUrl.setHost(query.peerHostName());
173 queryUrl.setPort(query.peerPort());
174 requiredCapabilities |= QNetworkProxy::TunnelingCapability;
175 break;
176 case QNetworkProxyQuery::UdpSocket:
177 if (queryUrl.scheme().isEmpty())
178 queryUrl.setScheme(QStringLiteral("udp"));
179 queryUrl.setHost(query.peerHostName());
180 queryUrl.setPort(query.peerPort());
181 requiredCapabilities |= QNetworkProxy::UdpTunnelingCapability;
182 break;
183 default:
184 proxyList.append(QNetworkProxy(QNetworkProxy::NoProxy));
185 return proxyList;
186 }
187
188 const QList<QUrl> rawProxies = libProxyWrapper()->getProxies(queryUrl);
189
190 bool haveDirectConnection = false;
191 for (const QUrl& url : rawProxies) {
192 QNetworkProxy::ProxyType type;
193 const QString scheme = url.scheme();
194 if (scheme == "http"_L1) {
195 type = QNetworkProxy::HttpProxy;
196 } else if (scheme == "socks"_L1 || scheme == "socks5"_L1) {
197 type = QNetworkProxy::Socks5Proxy;
198 } else if (scheme == "ftp"_L1) {
199 type = QNetworkProxy::FtpCachingProxy;
200 } else if (scheme == "direct"_L1) {
201 type = QNetworkProxy::NoProxy;
202 haveDirectConnection = true;
203 } else {
204 continue; //unsupported proxy type e.g. socks4
205 }
206
207 QNetworkProxy proxy(type,
208 url.host(QUrl::EncodeUnicode),
209 url.port(0),
210 url.userName(QUrl::FullyDecoded),
211 url.password(QUrl::FullyDecoded));
212
213 if ((proxy.capabilities() & requiredCapabilities) == requiredCapabilities)
214 proxyList.append(proxy);
215 }
216
217 // fallback is direct connection
218 if (proxyList.isEmpty() || !haveDirectConnection)
219 proxyList.append(QNetworkProxy(QNetworkProxy::NoProxy));
220
221 return proxyList;
222}
223
224QT_END_NAMESPACE
225
226#include "qnetworkproxy_libproxy.moc"
227
228#endif
QList< QUrl > getProxies(const QUrl &url)
static bool isThreadingNeeded()