-
Notifications
You must be signed in to change notification settings - Fork 2
/
resolver_posix.c
305 lines (253 loc) · 9.74 KB
/
resolver_posix.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
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <errno.h>
#include <time.h>
#include "config.h"
#include "event.h"
#include "fetch.h"
#include "log.h"
#include "execute.h"
#include "mutex.h"
#include "net_adapter.h"
#include "resolver.h"
#include "resolver_i.h"
#include "resolver_posix.h"
#include "threadpool.h"
#include "util.h"
#include "wpad_dhcp.h"
#include "wpad_dns.h"
#define WPAD_DHCP_TIMEOUT (3)
#define WPAD_EXPIRE_SECONDS (300)
typedef struct g_proxy_resolver_posix_s {
// WPAD discovered url
char *auto_config_url;
// WPAD discovery lock
void *mutex;
// PAC script
char *script;
time_t last_wpad_time;
time_t last_fetch_time;
} g_proxy_resolver_posix_s;
g_proxy_resolver_posix_s g_proxy_resolver_posix;
typedef struct proxy_resolver_posix_s {
// Last system error
int32_t error;
// Complete event
void *complete;
// Proxy list
char *list;
} proxy_resolver_posix_s;
static char *proxy_resolver_posix_wpad_discover(void) {
char *auto_config_url = NULL;
char *script = NULL;
// Check if we need to re-discover the WPAD auto config url
if (g_proxy_resolver_posix.last_wpad_time > 0 &&
g_proxy_resolver_posix.last_wpad_time + WPAD_EXPIRE_SECONDS >= time(NULL)) {
// Use cached version of WPAD auto config url
auto_config_url = g_proxy_resolver_posix.auto_config_url;
} else {
free(g_proxy_resolver_posix.auto_config_url);
g_proxy_resolver_posix.auto_config_url = NULL;
// Detect proxy auto configuration using DHCP
LOG_INFO("Discovering proxy auto config using WPAD (%s)\n", "DHCP");
auto_config_url = wpad_dhcp(WPAD_DHCP_TIMEOUT);
// Detect proxy auto configuration using DNS
if (!auto_config_url) {
LOG_INFO("Discovering proxy auto config using WPAD (%s)\n", "DNS");
script = wpad_dns(NULL);
if (script) {
g_proxy_resolver_posix.script = script;
g_proxy_resolver_posix.last_fetch_time = time(NULL);
}
}
g_proxy_resolver_posix.auto_config_url = auto_config_url;
g_proxy_resolver_posix.last_wpad_time = time(NULL);
}
// Duplicate so it can be freed the same if proxy_config_get_auto_config_url() returns a string
return auto_config_url ? strdup(auto_config_url) : NULL;
}
static char *proxy_resolver_posix_fetch_pac(const char *auto_config_url, int32_t *error) {
char *script = NULL;
// Check if the auto config url has changed
bool url_changed = false;
if (g_proxy_resolver_posix.auto_config_url)
url_changed = strcmp(g_proxy_resolver_posix.auto_config_url, auto_config_url) != 0;
else
url_changed = true;
// Check if we need to re-fetch the PAC script
if (g_proxy_resolver_posix.last_fetch_time > 0 &&
g_proxy_resolver_posix.last_fetch_time + WPAD_EXPIRE_SECONDS >= time(NULL) && !url_changed) {
// Use cached version of the PAC script
script = g_proxy_resolver_posix.script;
} else {
LOG_INFO("Fetching proxy auto config script from %s\n", auto_config_url);
script = fetch_get(auto_config_url, error);
if (!script)
LOG_ERROR("Unable to fetch proxy auto config script %s (%" PRId32 ")\n", auto_config_url, *error);
free(g_proxy_resolver_posix.script);
g_proxy_resolver_posix.script = script;
g_proxy_resolver_posix.last_fetch_time = time(NULL);
}
return script;
}
bool proxy_resolver_posix_get_proxies_for_url(void *ctx, const char *url) {
proxy_resolver_posix_s *proxy_resolver = (proxy_resolver_posix_s *)ctx;
void *proxy_execute = NULL;
char *auto_config_url = NULL;
char *proxy = NULL;
char *script = NULL;
char *scheme = NULL;
bool locked = false;
bool is_ok = false;
if (proxy_config_get_auto_discover()) {
locked = mutex_lock(g_proxy_resolver_posix.mutex);
// Discover the proxy auto config url
auto_config_url = proxy_resolver_posix_wpad_discover();
}
// Use manually specified proxy auto configuration
if (!auto_config_url)
auto_config_url = proxy_config_get_auto_config_url();
if (auto_config_url) {
// Download proxy auto config script if available
script = proxy_resolver_posix_fetch_pac(auto_config_url, &proxy_resolver->error);
locked = locked && !mutex_unlock(g_proxy_resolver_posix.mutex);
if (!script)
goto posix_done;
// Execute blocking proxy auto config script for url
proxy_execute = proxy_execute_create();
if (!proxy_execute) {
proxy_resolver->error = ENOMEM;
LOG_ERROR("Unable to allocate memory for %s (%" PRId32 ")\n", "execute object", proxy_resolver->error);
goto posix_done;
}
if (!proxy_execute_get_proxies_for_url(proxy_execute, g_proxy_resolver_posix.script, url)) {
proxy_resolver->error = proxy_execute_get_error(proxy_execute);
LOG_ERROR("Unable to get proxies for url (%" PRId32 ")\n", proxy_resolver->error);
goto posix_done;
}
// Get return value from FindProxyForURL
const char *list = proxy_execute_get_list(proxy_execute);
// Use scheme associated with the URL when determining proxy
scheme = get_url_scheme(url, "http");
if (!scheme) {
proxy_resolver->error = ENOMEM;
LOG_ERROR("Unable to allocate memory for %s (%" PRId32 ")\n", "scheme", proxy_resolver->error);
goto posix_done;
}
// Convert return value from FindProxyForURL to uri list. We use the default
// scheme corresponding to the protocol of the original request.
proxy_resolver->list = convert_proxy_list_to_uri_list(list, scheme);
} else {
// Use DIRECT connection since WPAD didn't result in a proxy auto-configuration url
proxy_resolver->list = strdup("direct://");
}
posix_done:
if (proxy_execute)
proxy_execute_delete(&proxy_execute);
if (locked)
mutex_unlock(g_proxy_resolver_posix.mutex);
is_ok = proxy_resolver->list != NULL;
event_set(proxy_resolver->complete);
free(scheme);
free(proxy);
free(auto_config_url);
return is_ok;
}
const char *proxy_resolver_posix_get_list(void *ctx) {
proxy_resolver_posix_s *proxy_resolver = (proxy_resolver_posix_s *)ctx;
if (!proxy_resolver)
return NULL;
return proxy_resolver->list;
}
int32_t proxy_resolver_posix_get_error(void *ctx) {
proxy_resolver_posix_s *proxy_resolver = (proxy_resolver_posix_s *)ctx;
return proxy_resolver->error;
}
bool proxy_resolver_posix_wait(void *ctx, int32_t timeout_ms) {
proxy_resolver_posix_s *proxy_resolver = (proxy_resolver_posix_s *)ctx;
if (!proxy_resolver)
return false;
return event_wait(proxy_resolver->complete, timeout_ms);
}
bool proxy_resolver_posix_cancel(void *ctx) {
UNUSED(ctx);
return false;
}
void *proxy_resolver_posix_create(void) {
proxy_resolver_posix_s *proxy_resolver = (proxy_resolver_posix_s *)calloc(1, sizeof(proxy_resolver_posix_s));
if (!proxy_resolver)
return NULL;
proxy_resolver->complete = event_create();
if (!proxy_resolver->complete) {
free(proxy_resolver);
return NULL;
}
return proxy_resolver;
}
bool proxy_resolver_posix_delete(void **ctx) {
if (!ctx)
return false;
proxy_resolver_posix_s *proxy_resolver = (proxy_resolver_posix_s *)*ctx;
if (!proxy_resolver)
return false;
proxy_resolver_cancel(ctx);
event_delete(&proxy_resolver->complete);
free(proxy_resolver->list);
free(proxy_resolver);
return true;
}
static void proxy_resolver_posix_wpad_startup(void *arg) {
UNUSED(arg);
mutex_lock(g_proxy_resolver_posix.mutex);
// Discover the proxy auto config url
char *auto_config_url = proxy_resolver_posix_wpad_discover();
if (auto_config_url) {
int32_t error = 0;
// Download proxy auto config script if available
proxy_resolver_posix_fetch_pac(auto_config_url, &error);
free(auto_config_url);
}
mutex_unlock(g_proxy_resolver_posix.mutex);
}
bool proxy_resolver_posix_global_init(void) {
return proxy_resolver_posix_init_ex(NULL);
}
bool proxy_resolver_posix_init_ex(void *threadpool) {
g_proxy_resolver_posix.mutex = mutex_create();
if (!g_proxy_resolver_posix.mutex)
return false;
if (!fetch_global_init() || !proxy_execute_global_init())
return proxy_resolver_posix_global_cleanup();
// Start WPAD discovery process immediately
if (threadpool && proxy_config_get_auto_discover())
threadpool_enqueue(threadpool, NULL, proxy_resolver_posix_wpad_startup);
return true;
}
bool proxy_resolver_posix_global_cleanup(void) {
free(g_proxy_resolver_posix.script);
free(g_proxy_resolver_posix.auto_config_url);
mutex_delete(&g_proxy_resolver_posix.mutex);
fetch_global_cleanup();
proxy_execute_global_cleanup();
memset(&g_proxy_resolver_posix, 0, sizeof(g_proxy_resolver_posix));
return true;
}
const proxy_resolver_i_s *proxy_resolver_posix_get_interface(void) {
static const proxy_resolver_i_s proxy_resolver_posix_i = {
proxy_resolver_posix_get_proxies_for_url,
proxy_resolver_posix_get_list,
proxy_resolver_posix_get_error,
proxy_resolver_posix_wait,
proxy_resolver_posix_cancel,
proxy_resolver_posix_create,
proxy_resolver_posix_delete,
false, // get_proxies_for_url should be spooled to another thread
false, // get_proxies_for_url does not take into account system config
proxy_resolver_posix_global_init,
proxy_resolver_posix_global_cleanup};
return &proxy_resolver_posix_i;
}