HTTP proxy support + some cleanup (#8418)
* HTTP proxy support + some cleanup * fix unrelated compiler warnings with -Wextra * minor - log proxy setting * run changed code trough .clang-format * fix case when url ends by / * update README
Timo committed
Mar 25, 2020 at 09:17 UTC
a8e9fcb3630ebdcc92d895a6d8bf3e1c9a588a35
8 files changed
+183
-100
aclk/README.md
+16
-1
@@ -7,4 +7,19 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/aclk/README.md
7
8
# Agent-cloud link (ACLK)
9
10
-This is the agent cloud link (ACLK) information file
\ No newline at end of file
10
+
11
+## Configuration Options
12
+
13
+In `netdata.conf`:
14
+
15
+```ini
16
+[agent_cloud_link]
17
+ proxy = none
18
+```
19
+
20
+Parameter proxy can take one of the following values:
21
+
22
+- `env` - the default (try to read environment variables `http_proxy` and `socks_proxy`)
23
+- `none` - do not use any proxy (even if system configured otherwise)
24
+- `socks5[h]://[user:pass@]host:ip` - will use specified socks proxy
25
+- `http://[user:pass@]host:ip` - will use specified http proxy
aclk/aclk_common.c
+99
-41
@@ -6,16 +6,31 @@ struct {
6
ACLK_PROXY_TYPE type;
7
const char *url_str;
8
} supported_proxy_types[] = {
9
- { .type = PROXY_TYPE_SOCKS5, .url_str = "socks5" ACLK_PROXY_PROTO_ADDR_SEPARATOR },
9
+ { .type = PROXY_TYPE_SOCKS5, .url_str = "socks5" ACLK_PROXY_PROTO_ADDR_SEPARATOR },
10
{ .type = PROXY_TYPE_SOCKS5, .url_str = "socks5h" ACLK_PROXY_PROTO_ADDR_SEPARATOR },
11
+ { .type = PROXY_TYPE_HTTP, .url_str = "http" ACLK_PROXY_PROTO_ADDR_SEPARATOR },
12
{ .type = PROXY_TYPE_UNKNOWN, .url_str = NULL },
13
};
14
15
+const char *aclk_proxy_type_to_s(ACLK_PROXY_TYPE *type)
16
+{
17
+ switch (*type) {
18
+ case PROXY_DISABLED:
19
+ return "disabled";
20
+ case PROXY_TYPE_HTTP:
21
+ return "HTTP";
22
+ case PROXY_TYPE_SOCKS5:
23
+ return "SOCKS";
24
+ default:
25
+ return "Unknown";
26
+ }
27
+}
28
+
29
static inline ACLK_PROXY_TYPE aclk_find_proxy(const char *string)
30
{
31
int i = 0;
17
- while( supported_proxy_types[i].url_str ) {
18
- if(!strncmp(supported_proxy_types[i].url_str, string, strlen(supported_proxy_types[i].url_str)))
32
+ while (supported_proxy_types[i].url_str) {
33
+ if (!strncmp(supported_proxy_types[i].url_str, string, strlen(supported_proxy_types[i].url_str)))
34
return supported_proxy_types[i].type;
35
i++;
36
}
@@ -24,13 +39,13 @@ static inline ACLK_PROXY_TYPE aclk_find_proxy(const char *string)
39
40
ACLK_PROXY_TYPE aclk_verify_proxy(const char *string)
41
{
27
- if(!string)
42
+ if (!string)
43
return PROXY_TYPE_UNKNOWN;
44
30
- while(*string == 0x20)
45
+ while (*string == 0x20)
46
string++;
47
33
- if(!*string)
48
+ if (!*string)
49
return PROXY_TYPE_UNKNOWN;
50
51
return aclk_find_proxy(string);
@@ -38,112 +53,155 @@ ACLK_PROXY_TYPE aclk_verify_proxy(const char *string)
53
54
// helper function to censor user&password
55
// for logging purposes
41
-void safe_log_proxy_censor(char *proxy) {
56
+void safe_log_proxy_censor(char *proxy)
57
+{
58
size_t length = strlen(proxy);
43
- char *auth = proxy+length-1;
59
+ char *auth = proxy + length - 1;
60
char *cur;
61
46
- while( (auth >= proxy) && (*auth != '@') )
62
+ while ((auth >= proxy) && (*auth != '@'))
63
auth--;
64
65
//if not found or @ is first char do nothing
50
- if(auth<=proxy)
66
+ if (auth <= proxy)
67
return;
68
69
cur = strstr(proxy, ACLK_PROXY_PROTO_ADDR_SEPARATOR);
54
- if(!cur)
70
+ if (!cur)
71
cur = proxy;
72
else
73
cur += strlen(ACLK_PROXY_PROTO_ADDR_SEPARATOR);
74
59
- while(cur < auth) {
60
- *cur='X';
75
+ while (cur < auth) {
76
+ *cur = 'X';
77
cur++;
78
}
79
}
80
65
-static inline void safe_log_proxy_error(char *str, const char *proxy) {
81
+static inline void safe_log_proxy_error(char *str, const char *proxy)
82
+{
83
char *log = strdupz(proxy);
84
safe_log_proxy_censor(log);
85
error("%s Provided Value:\"%s\"", str, log);
86
freez(log);
87
}
88
72
-static inline int check_socks_enviroment(const char **proxy) {
89
+static inline int check_socks_enviroment(const char **proxy)
90
+{
91
char *tmp = getenv("socks_proxy");
92
75
- if(!tmp)
93
+ if (!tmp)
94
+ return 1;
95
+
96
+ if (aclk_verify_proxy(tmp) == PROXY_TYPE_SOCKS5) {
97
+ *proxy = tmp;
98
+ return 0;
99
+ }
100
+
101
+ safe_log_proxy_error(
102
+ "Environment var \"socks_proxy\" defined but of unknown format. Supported syntax: \"socks5[h]://[user:pass@]host:ip\".",
103
+ tmp);
104
+ return 1;
105
+}
106
+
107
+static inline int check_http_enviroment(const char **proxy)
108
+{
109
+ char *tmp = getenv("http_proxy");
110
+
111
+ if (!tmp)
112
return 1;
113
78
- if(aclk_verify_proxy(tmp) == PROXY_TYPE_SOCKS5) {
114
+ if (aclk_verify_proxy(tmp) == PROXY_TYPE_HTTP) {
115
*proxy = tmp;
116
return 0;
117
}
118
83
- safe_log_proxy_error("Environment var \"socks_proxy\" defined but of unknown format. Supported syntax: \"socks5[h]://[user:pass@]host:ip\".", tmp);
119
+ safe_log_proxy_error(
120
+ "Environment var \"http_proxy\" defined but of unknown format. Supported syntax: \"http[s]://[user:pass@]host:ip\".",
121
+ tmp);
122
return 1;
123
}
124
87
-const char *aclk_lws_wss_get_proxy_setting(ACLK_PROXY_TYPE *type) {
125
+const char *aclk_lws_wss_get_proxy_setting(ACLK_PROXY_TYPE *type)
126
+{
127
const char *proxy = config_get(CONFIG_SECTION_ACLK, ACLK_PROXY_CONFIG_VAR, ACLK_PROXY_ENV);
128
*type = PROXY_DISABLED;
129
91
- if(strcmp(proxy, "none") == 0)
130
+ if (strcmp(proxy, "none") == 0)
131
return proxy;
132
94
- if(strcmp(proxy, ACLK_PROXY_ENV) == 0) {
95
- if(check_socks_enviroment(&proxy) == 0)
133
+ if (strcmp(proxy, ACLK_PROXY_ENV) == 0) {
134
+ if (check_socks_enviroment(&proxy) == 0)
135
*type = PROXY_TYPE_SOCKS5;
136
+ else if (check_http_enviroment(&proxy) == 0)
137
+ *type = PROXY_TYPE_HTTP;
138
return proxy;
139
}
140
141
*type = aclk_verify_proxy(proxy);
101
- if(*type == PROXY_TYPE_UNKNOWN) {
142
+ if (*type == PROXY_TYPE_UNKNOWN) {
143
*type = PROXY_DISABLED;
103
- safe_log_proxy_error("Config var \"" ACLK_PROXY_CONFIG_VAR "\" defined but of unknown format. Supported syntax: \"socks5[h]://[user:pass@]host:ip\".", proxy);
144
+ safe_log_proxy_error(
145
+ "Config var \"" ACLK_PROXY_CONFIG_VAR
146
+ "\" defined but of unknown format. Supported syntax: \"socks5[h]://[user:pass@]host:ip\".",
147
+ proxy);
148
}
149
150
return proxy;
151
}
152
153
+// helper function to read settings only once (static)
154
+// as claiming, challenge/response and ACLK
155
+// read the same thing, no need to parse again
156
+const char *aclk_get_proxy(ACLK_PROXY_TYPE *type)
157
+{
158
+ static const char *proxy = NULL;
159
+ static ACLK_PROXY_TYPE proxy_type = PROXY_NOT_SET;
160
+
161
+ if (proxy_type == PROXY_NOT_SET)
162
+ proxy = aclk_lws_wss_get_proxy_setting(&proxy_type);
163
+
164
+ *type = proxy_type;
165
+ return proxy;
166
+}
167
+
168
int aclk_decode_base_url(char *url, char **aclk_hostname, char **aclk_port)
169
{
111
-int pos = 0;
112
- if (!strncmp("https://", url, 8))
113
- {
170
+ int pos = 0;
171
+ if (!strncmp("https://", url, 8)) {
172
pos = 8;
115
- }
116
- else if (!strncmp("http://", url, 7))
117
- {
173
+ } else if (!strncmp("http://", url, 7)) {
174
error("Cannot connect ACLK over %s -> unencrypted link is not supported", url);
175
return 1;
176
}
121
-int host_end = pos;
122
- while( url[host_end] != 0 && url[host_end] != '/' && url[host_end] != ':' )
177
+ int host_end = pos;
178
+ while (url[host_end] != 0 && url[host_end] != '/' && url[host_end] != ':')
179
host_end++;
124
- if (url[host_end] == 0)
125
- {
126
- *aclk_hostname = strdupz(url+pos);
180
+ if (url[host_end] == 0) {
181
+ *aclk_hostname = strdupz(url + pos);
182
*aclk_port = strdupz("443");
183
info("Setting ACLK target host=%s port=%s from %s", *aclk_hostname, *aclk_port, url);
184
return 0;
185
}
131
- if (url[host_end] == ':')
132
- {
186
+ if (url[host_end] == ':') {
187
*aclk_hostname = callocz(host_end - pos + 1, 1);
134
- strncpy(*aclk_hostname, url+pos, host_end - pos);
188
+ strncpy(*aclk_hostname, url + pos, host_end - pos);
189
int port_end = host_end + 1;
190
while (url[port_end] >= '0' && url[port_end] <= '9')
191
port_end++;
138
- if (port_end - host_end > 6)
139
- {
192
+ if (port_end - host_end > 6) {
193
error("Port specified in %s is invalid", url);
194
return 0;
195
}
196
*aclk_port = callocz(port_end - host_end + 1, 1);
144
- for(int i=host_end + 1; i < port_end; i++)
197
+ for (int i = host_end + 1; i < port_end; i++)
198
(*aclk_port)[i - host_end - 1] = url[i];
199
}
200
+ if (url[host_end] == '/') {
201
+ *aclk_port = strdupz("443");
202
+ *aclk_hostname = callocz(1, host_end - pos + 1);
203
+ strncpy(*aclk_hostname, url+pos, host_end - pos);
204
+ }
205
info("Setting ACLK target host=%s port=%s from %s", *aclk_hostname, *aclk_port, url);
206
return 0;
207
}
aclk/aclk_common.h
+3
@@ -11,6 +11,8 @@ typedef enum aclk_proxy_type {
11
PROXY_NOT_SET,
12
} ACLK_PROXY_TYPE;
13
14
+const char *aclk_proxy_type_to_s(ACLK_PROXY_TYPE *type);
15
+
16
#define ACLK_PROXY_PROTO_ADDR_SEPARATOR "://"
17
#define ACLK_PROXY_ENV "env"
18
#define ACLK_PROXY_CONFIG_VAR "proxy"
@@ -19,5 +21,6 @@ ACLK_PROXY_TYPE aclk_verify_proxy(const char *string);
21
const char *aclk_lws_wss_get_proxy_setting(ACLK_PROXY_TYPE *type);
22
void safe_log_proxy_censor(char *proxy);
23
int aclk_decode_base_url(char *url, char **aclk_hostname, char **aclk_port);
24
+const char *aclk_get_proxy(ACLK_PROXY_TYPE *type);
25
26
#endif //ACLK_COMMON_H
aclk/aclk_lws_https_client.c
+9
-21
@@ -19,6 +19,7 @@ struct simple_hcc_data {
19
20
static int simple_https_client_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
21
{
22
+ UNUSED(user);
23
int n;
24
char *ptr;
25
char buffer[SMALL_BUFFER];
@@ -78,7 +79,7 @@ static int simple_https_client_callback(struct lws *wsi, enum lws_callback_reaso
79
debug(D_ACLK, "LWS_CALLBACK_CLIENT_HTTP_WRITEABLE");
80
if(perconn_data && perconn_data->payload) {
81
n = strlen(perconn_data->payload);
81
- if(perconn_data->data_size < LWS_PRE + n + 1) {
82
+ if(perconn_data->data_size < (size_t)LWS_PRE + n + 1) {
83
error("Buffer given is not big enough");
84
return 1;
85
}
@@ -133,12 +134,16 @@ static const struct lws_protocols protocols[] = {
134
simple_https_client_callback,
135
0,
136
0,
137
+ 0,
138
+ 0,
139
+ 0
140
},
137
- { NULL, NULL, 0, 0 }
141
+ { NULL, NULL, 0, 0, 0, 0, 0 }
142
};
143
144
static void simple_hcc_log_divert(int level, const char *line)
145
{
146
+ UNUSED(level);
147
error("Libwebsockets: %s", line);
148
}
149
@@ -159,15 +164,7 @@ int aclk_send_https_request(char *method, char *host, char *port, char *url, cha
164
int n = 0;
165
time_t timestamp;
166
162
- //TODO -> deduplicate (aclk_lws_wss_connect)
163
- static const char *proxy = NULL;
164
- static ACLK_PROXY_TYPE proxy_type = PROXY_NOT_SET;
167
struct lws_vhost *vhost;
166
- char *log;
167
-
168
- if(proxy_type == PROXY_NOT_SET)
169
- proxy = aclk_lws_wss_get_proxy_setting(&proxy_type);
170
-
168
169
memset(&info, 0, sizeof info);
170
@@ -212,17 +209,8 @@ int aclk_send_https_request(char *method, char *host, char *port, char *url, cha
209
if(!vhost)
210
fatal("Could not find the default LWS vhost.");
211
215
- lws_set_socks(vhost, ":");
216
- lws_set_proxy(vhost, ":");
217
-
218
- if(proxy_type == PROXY_TYPE_SOCKS5) {
219
- log = strdupz(proxy);
220
- safe_log_proxy_censor(log);
221
- info("Connecting using SOCKS5 proxy:\"%s\"", log);
222
- freez(log);
223
- if(aclk_wss_set_socks(vhost, proxy))
224
- error("LWS failed to accept socks proxy.");
225
- }
212
+ //set up proxy
213
+ aclk_wss_set_proxy(vhost);
214
215
lws_client_connect_via_info(&i);
216
aclk/aclk_lws_wss_client.c
+49
-33
@@ -208,42 +208,81 @@ void aclk_lws_wss_client_destroy()
208
#endif
209
}
210
211
-int aclk_wss_set_socks(struct lws_vhost *vhost, const char *socks) {
211
+static int aclk_wss_set_socks(struct lws_vhost *vhost, const char *socks)
212
+{
213
char *proxy = strstr(socks, ACLK_PROXY_PROTO_ADDR_SEPARATOR);
214
214
- if(!proxy)
215
+ if (!proxy)
216
return -1;
217
218
proxy += strlen(ACLK_PROXY_PROTO_ADDR_SEPARATOR);
219
219
- if(!*proxy)
220
+ if (!*proxy)
221
return -1;
222
223
return lws_set_socks(vhost, proxy);
224
}
225
226
+void aclk_wss_set_proxy(struct lws_vhost *vhost)
227
+{
228
+ const char *proxy;
229
+ ACLK_PROXY_TYPE proxy_type;
230
+ char *log;
231
+
232
+ proxy = aclk_get_proxy(&proxy_type);
233
+
234
+ lws_set_socks(vhost, ":");
235
+ lws_set_proxy(vhost, ":");
236
+
237
+ if (proxy_type == PROXY_TYPE_UNKNOWN) {
238
+ error("Unknown proxy type");
239
+ return;
240
+ }
241
+
242
+ if (proxy_type == PROXY_TYPE_SOCKS5 || proxy_type == PROXY_TYPE_HTTP) {
243
+ log = strdupz(proxy);
244
+ safe_log_proxy_censor(log);
245
+ info("Connecting using %s proxy:\"%s\"", aclk_proxy_type_to_s(&proxy_type), log);
246
+ freez(log);
247
+ }
248
+ if (proxy_type == PROXY_TYPE_SOCKS5) {
249
+ if (aclk_wss_set_socks(vhost, proxy))
250
+ error("LWS failed to accept socks proxy.");
251
+ return;
252
+ }
253
+ if (proxy_type == PROXY_TYPE_HTTP) {
254
+ if (lws_set_proxy(vhost, proxy))
255
+ error("LWS failed to accept http proxy.");
256
+ return;
257
+ }
258
+ if (proxy_type != PROXY_DISABLED)
259
+ error("Unknown proxy type");
260
+}
261
+
262
// Return code indicates if connection attempt has started async.
263
int aclk_lws_wss_connect(char *host, int port)
264
{
265
struct lws_client_connect_info i;
266
struct lws_vhost *vhost;
230
- static const char *proxy = NULL;
231
- static ACLK_PROXY_TYPE proxy_type = PROXY_NOT_SET;
232
- char *log;
267
268
if (!engine_instance) {
269
return aclk_lws_wss_client_init(host, port);
270
// PROTOCOL_INIT callback will call again.
271
}
272
239
- if(proxy_type == PROXY_NOT_SET)
240
- proxy = aclk_lws_wss_get_proxy_setting(&proxy_type);
241
-
273
if (engine_instance->lws_wsi) {
274
error("Already Connected. Only one connection supported at a time.");
275
return 0;
276
}
277
278
+ memset(&i, 0, sizeof(i));
279
+ i.context = engine_instance->lws_context;
280
+ i.port = engine_instance->port;
281
+ i.address = engine_instance->host;
282
+ i.path = "/mqtt";
283
+ i.host = engine_instance->host;
284
+ i.protocol = "mqtt";
285
+
286
// from LWS docu:
287
// If option LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, no vhost is
288
// created; you're expected to create your own vhosts afterwards using
@@ -253,30 +292,7 @@ int aclk_lws_wss_connect(char *host, int port)
292
if(!vhost)
293
fatal("Could not find the default LWS vhost.");
294
256
- memset(&i, 0, sizeof(i));
257
- i.context = engine_instance->lws_context;
258
- i.port = engine_instance->port;
259
- i.address = engine_instance->host;
260
- i.path = "/mqtt";
261
- i.host = engine_instance->host;
262
- i.protocol = "mqtt";
263
-
264
- switch (proxy_type) {
265
- case PROXY_DISABLED:
266
- lws_set_socks(vhost, ":");
267
- lws_set_proxy(vhost, ":");
268
- break;
269
- case PROXY_TYPE_SOCKS5:
270
- log = strdupz(proxy);
271
- safe_log_proxy_censor(log);
272
- info("Connecting using SOCKS5 proxy:\"%s\"", log);
273
- freez(log);
274
- if(aclk_wss_set_socks(vhost, proxy))
275
- error("LWS failed to accept socks proxy.");
276
- break;
277
- default:
278
- error("The proxy could not be set. Unknown proxy type.");
279
- }
295
+ aclk_wss_set_proxy(vhost);
296
297
#ifdef ACLK_SSL_ALLOW_SELF_SIGNED
298
i.ssl_connection = LCCSCF_USE_SSL | LCCSCF_ALLOW_SELFSIGNED | LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
aclk/aclk_lws_wss_client.h
+1
-1
@@ -81,7 +81,7 @@ void aclk_lws_connection_data_received();
81
void aclk_lws_connection_closed();
82
void lws_wss_check_queues(size_t *write_len, size_t *write_len_bytes, size_t *read_len);
83
84
-int aclk_wss_set_socks(struct lws_vhost *vhost, const char *socks);
84
+void aclk_wss_set_proxy(struct lws_vhost *vhost);
85
86
#define FRAGMENT_SIZE 4096
87
#endif
claim/claim.c
+2
-2
@@ -64,9 +64,9 @@ void claim_agent(char *claiming_arguments)
64
ACLK_PROXY_TYPE proxy_type;
65
char proxy_flag[CLAIMING_PROXY_LENGTH] = "-noproxy";
66
67
- proxy_str = aclk_lws_wss_get_proxy_setting(&proxy_type);
67
+ proxy_str = aclk_get_proxy(&proxy_type);
68
69
- if(proxy_type == PROXY_TYPE_SOCKS5)
69
+ if (proxy_type == PROXY_TYPE_SOCKS5 || proxy_type == PROXY_TYPE_HTTP)
70
snprintf(proxy_flag, CLAIMING_PROXY_LENGTH, "-proxy=\"%s\"", proxy_str);
71
72
snprintfz(command_buffer,
claim/netdata-claim.sh.in
+4
-1
@@ -129,7 +129,7 @@ do
129
-hostname=*) HOSTNAME=${arg:10} ;;
130
-verbose) VERBOSE=1 ;;
131
-insecure) INSECURE=1 ;;
132
- -proxy=socks*) PROXY=${arg:7} ;;
132
+ -proxy=*) PROXY=${arg:7} ;;
133
-noproxy) NOPROXY=yes ;;
134
*) echo >&2 "Unknown argument ${arg}"
135
exit 1 ;;
@@ -148,6 +148,7 @@ echo >&2 "Base URL: $URL_BASE"
148
echo >&2 "Id: $ID"
149
echo >&2 "Rooms: $ROOMS"
150
echo >&2 "Hostname: $HOSTNAME"
151
+echo >&2 "Proxy: $PROXY"
152
153
# create the claiming directory for this user
154
if [ ! -d "${CLAIMING_DIR}" ] ; then
@@ -214,6 +215,8 @@ else
215
--body-file=\"${CLAIMING_DIR}/tmpin.txt\""
216
if [ "${NOPROXY}" = "yes" ] ; then
217
URLCOMMAND="${URLCOMMAND} --no-proxy"
218
+ elif [ "${PROXY:0:4}" = http ] ; then
219
+ URLCOMMAND="export http_proxy=${PROXY}; ${URLCOMMAND}"
220
fi
221
fi
222