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 free(data.proxies[i]);
136 }
137 free(data.proxies);
138 }
139 return ret;
140}
141
143{
144 factory = px_proxy_factory_new();
145 Q_CHECK_PTR(factory);
146
147 forever {
148 requestReady.acquire();
149 if (isInterruptionRequested())
150 break;
151 request->proxies = px_proxy_factory_get_proxies(factory, request->url);
152 request->replyReady.countDown();
153 }
154
155 px_proxy_factory_free(factory);
156}
157
158QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query)
159{
160 QList<QNetworkProxy> proxyList;
161
162 QUrl queryUrl;
163 QNetworkProxy::Capabilities requiredCapabilities(0);
164 switch (query.queryType()) {
165 //URL requests are directly supported by libproxy
166 case QNetworkProxyQuery::UrlRequest:
167 queryUrl = query.url();
168 break;
169 // fake URLs to get libproxy to tell us the SOCKS proxy
170 case QNetworkProxyQuery::TcpSocket:
171 if (queryUrl.scheme().isEmpty())
172 queryUrl.setScheme(QStringLiteral("tcp"));
173 queryUrl.setHost(query.peerHostName());
174 queryUrl.setPort(query.peerPort());
175 requiredCapabilities |= QNetworkProxy::TunnelingCapability;
176 break;
177 case QNetworkProxyQuery::UdpSocket:
178 if (queryUrl.scheme().isEmpty())
179 queryUrl.setScheme(QStringLiteral("udp"));
180 queryUrl.setHost(query.peerHostName());
181 queryUrl.setPort(query.peerPort());
182 requiredCapabilities |= QNetworkProxy::UdpTunnelingCapability;
183 break;
184 default:
185 proxyList.append(QNetworkProxy(QNetworkProxy::NoProxy));
186 return proxyList;
187 }
188
189 const QList<QUrl> rawProxies = libProxyWrapper()->getProxies(queryUrl);
190
191 bool haveDirectConnection = false;
192 for (const QUrl& url : rawProxies) {
193 QNetworkProxy::ProxyType type;
194 const QString scheme = url.scheme();
195 if (scheme == "http"_L1) {
196 type = QNetworkProxy::HttpProxy;
197 } else if (scheme == "socks"_L1 || scheme == "socks5"_L1) {
198 type = QNetworkProxy::Socks5Proxy;
199 } else if (scheme == "ftp"_L1) {
200 type = QNetworkProxy::FtpCachingProxy;
201 } else if (scheme == "direct"_L1) {
202 type = QNetworkProxy::NoProxy;
203 haveDirectConnection = true;
204 } else {
205 continue; //unsupported proxy type e.g. socks4
206 }
207
208 QNetworkProxy proxy(type,
209 url.host(QUrl::EncodeUnicode),
210 url.port(0),
211 url.userName(QUrl::FullyDecoded),
212 url.password(QUrl::FullyDecoded));
213
214 if ((proxy.capabilities() & requiredCapabilities) == requiredCapabilities)
215 proxyList.append(proxy);
216 }
217
218 // fallback is direct connection
219 if (proxyList.isEmpty() || !haveDirectConnection)
220 proxyList.append(QNetworkProxy(QNetworkProxy::NoProxy));
221
222 return proxyList;
223}
224
225QT_END_NAMESPACE
226
227#include "qnetworkproxy_libproxy.moc"
228
229#endif
QList< QUrl > getProxies(const QUrl &url)
static bool isThreadingNeeded()