-
Notifications
You must be signed in to change notification settings - Fork 2
/
resolver.c
324 lines (283 loc) · 10.7 KB
/
resolver.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <errno.h>
#ifdef _WIN32
# include <windows.h>
# include <winsock2.h>
#endif
#include "config.h"
#include "log.h"
#include "resolver.h"
#include "resolver_i.h"
#if defined(__APPLE__)
# include "resolver_mac.h"
#elif defined(__linux__)
# include "resolver_gnome3.h"
# ifdef PROXYRES_EXECUTE
# include "resolver_posix.h"
# endif
#elif defined(_WIN32)
# if WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP
# include "resolver_winxp.h"
# include "resolver_win8.h"
# elif WINAPI_FAMILY == WINAPI_FAMILY_PC_APP
# include "resolver_winrt.h"
# endif
#endif
#include "threadpool.h"
#include "util.h"
typedef struct g_proxy_resolver_s {
// Library reference count
int32_t ref_count;
// Proxy resolver interface
const proxy_resolver_i_s *proxy_resolver_i;
// Thread pool
void *threadpool;
} g_proxy_resolver_s;
g_proxy_resolver_s g_proxy_resolver;
typedef struct proxy_resolver_s {
// Base proxy resolver instance
void *base;
// Async job
char *url;
// Proxy list from system config
char *list;
// Next proxy pointer
const char *listp;
} proxy_resolver_s;
static void proxy_resolver_get_proxies_for_url_threadpool(void *arg) {
proxy_resolver_s *proxy_resolver = (proxy_resolver_s *)arg;
if (!proxy_resolver)
return;
g_proxy_resolver.proxy_resolver_i->get_proxies_for_url(proxy_resolver->base, proxy_resolver->url);
}
static bool proxy_resolver_get_proxies_for_url_from_system_config(void *ctx, const char *url) {
proxy_resolver_s *proxy_resolver = (proxy_resolver_s *)ctx;
char *auto_config_url = NULL;
char *proxy = NULL;
char *scheme = NULL;
// Skip if auto-config url evaluation is required for proxy resolution
auto_config_url = proxy_config_get_auto_config_url();
if (auto_config_url)
goto config_done;
// Use scheme associated with the URL when determining proxy
scheme = get_url_scheme(url, "http");
if (!scheme) {
LOG_ERROR("Unable to allocate memory for scheme\n");
goto config_done;
}
// Check if manually configured proxy is specified in system config
proxy = proxy_config_get_proxy(scheme);
if (proxy) {
// Check if we need to bypass the proxy for the url
char *bypass_list = proxy_config_get_bypass_list();
bool should_bypass = should_bypass_proxy(url, bypass_list);
if (should_bypass) {
// Bypass the proxy for the url
LOG_INFO("Bypassing proxy for %s (%s)\n", url, bypass_list ? bypass_list : "null");
proxy_resolver->list = strdup("direct://");
} else {
// Construct proxy list url using scheme associated with proxy's port if available,
// otherwise continue to use scheme associated with the url.
const uint16_t proxy_port = get_host_port(proxy, strlen(proxy), 0);
const char *proxy_scheme = proxy_port ? get_port_scheme(proxy_port, scheme) : scheme;
// Use proxy from settings
proxy_resolver->list = get_url_from_host(proxy_scheme, proxy);
}
free(bypass_list);
} else if (!proxy_config_get_auto_discover()) {
// Use DIRECT connection since proxy auto-discovery is not necessary
proxy_resolver->list = strdup("direct://");
}
config_done:
free(scheme);
free(proxy);
free(auto_config_url);
return proxy_resolver->list != NULL;
}
bool proxy_resolver_get_proxies_for_url(void *ctx, const char *url) {
proxy_resolver_s *proxy_resolver = (proxy_resolver_s *)ctx;
if (!proxy_resolver || !g_proxy_resolver.proxy_resolver_i)
return false;
proxy_resolver->listp = NULL;
free(proxy_resolver->list);
proxy_resolver->list = NULL;
// Check if OS resolver already takes into account system configuration
if (!g_proxy_resolver.proxy_resolver_i->uses_system_config) {
// Check if auto-discovery is necessary
if (proxy_resolver_get_proxies_for_url_from_system_config(ctx, url)) {
// Use system proxy configuration if no auto-discovery mechanism is necessary
return true;
}
}
// Discover proxy auto-config asynchronously if supported, otherwise spool to thread pool
if (g_proxy_resolver.proxy_resolver_i->is_async)
return g_proxy_resolver.proxy_resolver_i->get_proxies_for_url(proxy_resolver->base, url);
free(proxy_resolver->url);
proxy_resolver->url = strdup(url);
return threadpool_enqueue(g_proxy_resolver.threadpool, proxy_resolver,
proxy_resolver_get_proxies_for_url_threadpool);
}
const char *proxy_resolver_get_list(void *ctx) {
proxy_resolver_s *proxy_resolver = (proxy_resolver_s *)ctx;
if (!proxy_resolver || !g_proxy_resolver.proxy_resolver_i)
return false;
if (proxy_resolver->list)
return proxy_resolver->list;
return g_proxy_resolver.proxy_resolver_i->get_list(proxy_resolver->base);
}
char *proxy_resolver_get_next_proxy(void *ctx) {
proxy_resolver_s *proxy_resolver = (proxy_resolver_s *)ctx;
if (!proxy_resolver || !g_proxy_resolver.proxy_resolver_i)
return false;
if (!proxy_resolver->listp)
return NULL;
// Get the next proxy to connect through
char *proxy = str_sep_dup(&proxy_resolver->listp, ",");
if (!proxy) {
if (proxy_resolver->list)
proxy_resolver->listp = proxy_resolver->list;
else
proxy_resolver->listp = g_proxy_resolver.proxy_resolver_i->get_list(proxy_resolver->base);
}
return proxy;
}
int32_t proxy_resolver_get_error(void *ctx) {
proxy_resolver_s *proxy_resolver = (proxy_resolver_s *)ctx;
if (!proxy_resolver || !g_proxy_resolver.proxy_resolver_i)
return -1;
return g_proxy_resolver.proxy_resolver_i->get_error(proxy_resolver->base);
}
bool proxy_resolver_wait(void *ctx, int32_t timeout_ms) {
proxy_resolver_s *proxy_resolver = (proxy_resolver_s *)ctx;
if (!proxy_resolver || !g_proxy_resolver.proxy_resolver_i)
return false;
if (proxy_resolver->list) {
proxy_resolver->listp = proxy_resolver->list;
return true;
}
if (g_proxy_resolver.proxy_resolver_i->wait(proxy_resolver->base, timeout_ms)) {
proxy_resolver->listp = g_proxy_resolver.proxy_resolver_i->get_list(proxy_resolver->base);
return true;
}
return false;
}
bool proxy_resolver_cancel(void *ctx) {
proxy_resolver_s *proxy_resolver = (proxy_resolver_s *)ctx;
if (!proxy_resolver || !g_proxy_resolver.proxy_resolver_i)
return false;
if (proxy_resolver->list)
return true;
return g_proxy_resolver.proxy_resolver_i->cancel(proxy_resolver->base);
}
void *proxy_resolver_create(void) {
if (!g_proxy_resolver.proxy_resolver_i)
return NULL;
proxy_resolver_s *proxy_resolver = calloc(1, sizeof(proxy_resolver_s));
if (!proxy_resolver)
return NULL;
proxy_resolver->base = g_proxy_resolver.proxy_resolver_i->create();
if (!proxy_resolver->base) {
free(proxy_resolver);
return NULL;
}
return proxy_resolver;
}
bool proxy_resolver_delete(void **ctx) {
if (!g_proxy_resolver.proxy_resolver_i)
return true;
if (!ctx || !*ctx)
return false;
proxy_resolver_s *proxy_resolver = (proxy_resolver_s *)*ctx;
free(proxy_resolver->url);
free(proxy_resolver->list);
g_proxy_resolver.proxy_resolver_i->delete (&proxy_resolver->base);
free(proxy_resolver);
*ctx = NULL;
return true;
}
bool proxy_resolver_global_init(void) {
if (g_proxy_resolver.ref_count > 0) {
g_proxy_resolver.ref_count++;
return true;
}
memset(&g_proxy_resolver, 0, sizeof(g_proxy_resolver_s));
#if defined(_WIN32) && (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
WSADATA WsaData = {0};
if (WSAStartup(MAKEWORD(2, 2), &WsaData) != 0) {
LOG_ERROR("Failed to initialize winsock %d\n", WSAGetLastError());
return false;
}
#endif
if (!proxy_config_global_init())
return false;
#if defined(__APPLE__)
if (proxy_resolver_mac_global_init())
g_proxy_resolver.proxy_resolver_i = proxy_resolver_mac_get_interface();
#elif defined(__linux__)
/* Does not work for manually specified proxy auto-config urls
if (proxy_resolver_gnome3_global_init())
g_proxy_resolver.proxy_resolver_i = proxy_resolver_gnome3_get_interface();*/
# ifdef PROXYRES_EXECUTE
if (!g_proxy_resolver.proxy_resolver_i)
g_proxy_resolver.proxy_resolver_i = proxy_resolver_posix_get_interface();
# endif
#elif defined(_WIN32)
# if WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP
if (proxy_resolver_win8_global_init())
g_proxy_resolver.proxy_resolver_i = proxy_resolver_win8_get_interface();
else if (proxy_resolver_winxp_global_init())
g_proxy_resolver.proxy_resolver_i = proxy_resolver_winxp_get_interface();
# elif WINAPI_FAMILY == WINAPI_FAMILY_PC_APP
if (proxy_resolver_winrt_global_init())
g_proxy_resolver.proxy_resolver_i = proxy_resolver_winrt_get_interface();
# endif
#endif
if (!g_proxy_resolver.proxy_resolver_i) {
LOG_ERROR("No proxy resolver available\n");
return false;
}
// No need to create thread pool since underlying implementation is already asynchronous
if (g_proxy_resolver.proxy_resolver_i->is_async) {
g_proxy_resolver.ref_count++;
return true;
}
// Create thread pool to handle proxy resolution requests asynchronously
g_proxy_resolver.threadpool = threadpool_create(THREADPOOL_DEFAULT_MIN_THREADS, THREADPOOL_DEFAULT_MAX_THREADS);
if (!g_proxy_resolver.threadpool) {
LOG_ERROR("Failed to create thread pool\n");
proxy_resolver_global_cleanup();
return false;
}
#if defined(__linux__) && defined(PROXYRES_EXECUTE)
// Pass threadpool to posix resolver to immediately start wpad discovery
if (g_proxy_resolver.proxy_resolver_i == proxy_resolver_posix_get_interface()) {
if (!proxy_resolver_posix_init_ex(g_proxy_resolver.threadpool)) {
LOG_ERROR("Failed to initialize posix proxy resolver\n");
proxy_resolver_global_cleanup();
return false;
}
}
#endif
g_proxy_resolver.ref_count++;
return true;
}
bool proxy_resolver_global_cleanup(void) {
if (--g_proxy_resolver.ref_count > 0)
return true;
if (g_proxy_resolver.threadpool)
threadpool_delete(&g_proxy_resolver.threadpool);
if (g_proxy_resolver.proxy_resolver_i)
g_proxy_resolver.proxy_resolver_i->global_cleanup();
memset(&g_proxy_resolver, 0, sizeof(g_proxy_resolver));
if (!proxy_config_global_cleanup())
return false;
#if defined(_WIN32) && (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
WSACleanup();
#endif
return true;
}