| 1 | #include "aclk_proxy.h" |
| 2 | |
| 3 | #define ACLK_PROXY_ENV "env" |
| 4 | #define ACLK_PROXY_CONFIG_VAR "proxy" |
| 5 | |
| 6 | struct { |
| 7 | ACLK_PROXY_TYPE type; |
| 8 | const char *url_str; |
| 9 | } supported_proxy_types[] = { |
| 10 | { .type = PROXY_TYPE_SOCKS5, .url_str = "socks5" ACLK_PROXY_PROTO_ADDR_SEPARATOR }, |
| 11 | { .type = PROXY_TYPE_SOCKS5H, .url_str = "socks5h" ACLK_PROXY_PROTO_ADDR_SEPARATOR }, |
| 12 | { .type = PROXY_TYPE_HTTP, .url_str = "http" ACLK_PROXY_PROTO_ADDR_SEPARATOR }, |
| 13 | { .type = PROXY_TYPE_UNKNOWN, .url_str = NULL }, |
| 14 | }; |
| 15 | |
| 16 | static inline ACLK_PROXY_TYPE aclk_find_proxy(const char *string) |
| 17 | { |
| 18 | int i = 0; |
| 19 | while (supported_proxy_types[i].url_str) { |
| 20 | if (!strncmp(supported_proxy_types[i].url_str, string, strlen(supported_proxy_types[i].url_str))) |
| 21 | return supported_proxy_types[i].type; |
| 22 | i++; |
| 23 | } |
| 24 | return PROXY_TYPE_UNKNOWN; |
| 25 | } |
| 26 | |
| 27 | ACLK_PROXY_TYPE aclk_verify_proxy(const char *string) |
| 28 | { |
| 29 | if (!string) |
| 30 | return PROXY_TYPE_UNKNOWN; |
| 31 | |
| 32 | while (*string == 0x20) |
| 33 | string++; |
| 34 | |
| 35 | if (!*string) |
| 36 | return PROXY_TYPE_UNKNOWN; |
| 37 | |
| 38 | return aclk_find_proxy(string); |
| 39 | } |
| 40 | |
| 41 | // helper function to censor user&password |
| 42 | // for logging purposes |
| 43 | void safe_log_proxy_censor(char *proxy) |
| 44 | { |
| 45 | if (!proxy) |
| 46 | return; |
| 47 | |
| 48 | size_t length = strlen(proxy); |
| 49 | char *auth = proxy + length - 1; |
| 50 | char *cur; |
| 51 | |
| 52 | while ((auth >= proxy) && (*auth != '@')) |
| 53 | auth--; |
| 54 | |
| 55 | //if not found or @ is first char do nothing |
| 56 | if (auth <= proxy) |
| 57 | return; |
| 58 | |
| 59 | cur = strstr(proxy, ACLK_PROXY_PROTO_ADDR_SEPARATOR); |
| 60 | if (!cur) |
| 61 | cur = proxy; |
| 62 | else |
| 63 | cur += strlen(ACLK_PROXY_PROTO_ADDR_SEPARATOR); |
| 64 | |
| 65 | while (cur < auth) { |
| 66 | *cur = 'X'; |
| 67 | cur++; |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | static inline void safe_log_proxy_error(char *str, const char *proxy) |
| 72 | { |
| 73 | char *log = strdupz(proxy); |
| 74 | safe_log_proxy_censor(log); |
| 75 | netdata_log_error("%s Provided Value:\"%s\"", str, log); |
| 76 | freez(log); |
| 77 | } |
| 78 | |
| 79 | // helper to extract "http://host:port" from a proxy URL, skipping credentials |
| 80 | void aclk_proxy_get_display(char *buf, size_t buflen, const char *proxy, ACLK_PROXY_TYPE type) |
| 81 | { |
| 82 | const char *at = strrchr(proxy, '@'); |
| 83 | const char *host_start = at ? at + 1 : proxy; |
| 84 | const char *sep = strstr(proxy, ACLK_PROXY_PROTO_ADDR_SEPARATOR); |
| 85 | if (!at && sep) |
| 86 | host_start = sep + strlen(ACLK_PROXY_PROTO_ADDR_SEPARATOR); |
| 87 | snprintfz(buf, buflen, "%s%s", aclk_proxy_type_to_url(type), host_start); |
| 88 | } |
| 89 | |
| 90 | static const char *proxy_source = NULL; |
| 91 | |
| 92 | static inline int check_environment_proxy(const char **proxy, ACLK_PROXY_TYPE *type) |
| 93 | { |
| 94 | const char *var = "http_proxy"; |
| 95 | char *tmp = getenv(var); |
| 96 | |
| 97 | if (!tmp || !*tmp) { |
| 98 | var = "https_proxy"; |
| 99 | tmp = getenv(var); |
| 100 | if (!tmp || !*tmp) |
| 101 | return 1; |
| 102 | } |
| 103 | |
| 104 | *type = aclk_verify_proxy(tmp); |
| 105 | if (*type == PROXY_TYPE_HTTP || *type == PROXY_TYPE_SOCKS5 || *type == PROXY_TYPE_SOCKS5H) { |
| 106 | *proxy = tmp; |
| 107 | char display[512]; |
| 108 | aclk_proxy_get_display(display, sizeof(display), tmp, *type); |
| 109 | char source_buf[256]; |
| 110 | snprintfz(source_buf, sizeof(source_buf), "environment variable '%s'", var); |
| 111 | freez((void *)proxy_source); |
| 112 | proxy_source = strdupz(source_buf); |
| 113 | nd_log(NDLS_DAEMON, NDLP_INFO, |
| 114 | "ACLK: using %s proxy %s (%s, from %s)", |
| 115 | *type == PROXY_TYPE_HTTP ? "HTTP" : (*type == PROXY_TYPE_SOCKS5H ? "SOCKS5H" : "SOCKS5"), |
| 116 | display, strchr(tmp, '@') ? "with credentials" : "without credentials", proxy_source); |
| 117 | return 0; |
| 118 | } |
| 119 | |
| 120 | char buf[1024]; |
| 121 | snprintfz(buf, sizeof(buf), |
| 122 | "Environment var '%s' defined but of unknown format '%s'. " |
| 123 | "Supported syntax: 'http://[user:pass@]host:port' or 'socks5[h]://[user:pass@]host:port'.", |
| 124 | var, tmp); |
| 125 | safe_log_proxy_error(buf, tmp); |
| 126 | |
| 127 | return 1; |
| 128 | } |
| 129 | |
| 130 | const char *aclk_lws_wss_get_proxy_setting(ACLK_PROXY_TYPE *type) |
| 131 | { |
| 132 | const char *proxy = cloud_config_proxy_get(); |
| 133 | |
| 134 | *type = PROXY_DISABLED; |
| 135 | |
| 136 | if (!proxy || !*proxy || strcmp(proxy, "none") == 0) { |
| 137 | nd_log(NDLS_DAEMON, NDLP_INFO, |
| 138 | "ACLK: proxy is %s, will connect directly without proxy.", |
| 139 | (!proxy || !*proxy) ? "not configured" : "set to 'none'"); |
| 140 | freez((void *)proxy_source); |
| 141 | proxy_source = NULL; |
| 142 | return proxy; |
| 143 | } |
| 144 | |
| 145 | if (strcmp(proxy, ACLK_PROXY_ENV) == 0) { |
| 146 | if (check_environment_proxy(&proxy, type) != 0) { |
| 147 | if (cloud_config_proxy_is_explicitly_set()) |
| 148 | nd_log(NDLS_DAEMON, NDLP_WARNING, |
| 149 | "ACLK: proxy is explicitly set to 'env' but neither 'http_proxy' nor 'https_proxy'" |
| 150 | " environment variables are set. Will connect directly without proxy."); |
| 151 | |
| 152 | freez((void *)proxy_source); |
| 153 | proxy_source = NULL; |
| 154 | proxy = NULL; |
| 155 | } |
| 156 | return proxy; |
| 157 | } |
| 158 | |
| 159 | *type = aclk_verify_proxy(proxy); |
| 160 | |
| 161 | if (*type == PROXY_TYPE_UNKNOWN) { |
| 162 | *type = PROXY_DISABLED; |
| 163 | safe_log_proxy_error( |
| 164 | "Config var \"" ACLK_PROXY_CONFIG_VAR |
| 165 | "\" defined but of unknown format. Supported syntax: \"http://[user:pass@]host:port\" or \"socks5[h]://[user:pass@]host:port\".", |
| 166 | proxy); |
| 167 | freez((void *)proxy_source); |
| 168 | proxy_source = NULL; |
| 169 | } |
| 170 | else { |
| 171 | const char *src = cloud_config_proxy_source_get(); |
| 172 | freez((void *)proxy_source); |
| 173 | proxy_source = src ? strdupz(src) : NULL; |
| 174 | char display[512]; |
| 175 | aclk_proxy_get_display(display, sizeof(display), proxy, *type); |
| 176 | nd_log(NDLS_DAEMON, NDLP_INFO, |
| 177 | "ACLK: using %s proxy %s (%s, from %s)", |
| 178 | *type == PROXY_TYPE_HTTP ? "HTTP" : (*type == PROXY_TYPE_SOCKS5H ? "SOCKS5H" : "SOCKS5"), |
| 179 | display, |
| 180 | strchr(proxy, '@') ? "with credentials" : "without credentials", |
| 181 | proxy_source); |
| 182 | } |
| 183 | |
| 184 | return proxy; |
| 185 | } |
| 186 | |
| 187 | // helper function to read settings only once (static) |
| 188 | // as claiming, challenge/response and ACLK |
| 189 | // read the same thing, no need to parse again |
| 190 | const char *aclk_get_proxy(ACLK_PROXY_TYPE *return_type, bool for_logging) |
| 191 | { |
| 192 | static const char *proxy = NULL; |
| 193 | static const char *safe_proxy = NULL; |
| 194 | static ACLK_PROXY_TYPE proxy_type = PROXY_NOT_SET; |
| 195 | |
| 196 | if (proxy_type == PROXY_NOT_SET) { |
| 197 | proxy = aclk_lws_wss_get_proxy_setting(&proxy_type); |
| 198 | char *log = NULL; |
| 199 | if (proxy) { |
| 200 | log = strdupz(proxy); |
| 201 | safe_log_proxy_censor(log); |
| 202 | } |
| 203 | safe_proxy = log; |
| 204 | } |
| 205 | |
| 206 | if (return_type) |
| 207 | *return_type = proxy_type; |
| 208 | return for_logging ? safe_proxy : proxy; |
| 209 | } |
| 210 | |
| 211 | const char *aclk_get_proxy_source(void) { |
| 212 | return proxy_source; |
| 213 | } |
| 214 | |
| 215 | void aclk_proxy_get_full_display(char *buf, size_t buflen) { |
| 216 | ACLK_PROXY_TYPE proxy_type; |
| 217 | const char *proxy_str = aclk_get_proxy(&proxy_type, false); |
| 218 | |
| 219 | if (proxy_type == PROXY_DISABLED || proxy_type == PROXY_NOT_SET || !proxy_str) { |
| 220 | snprintfz(buf, buflen, "none"); |
| 221 | return; |
| 222 | } |
| 223 | |
| 224 | char host_display[256]; |
| 225 | aclk_proxy_get_display(host_display, sizeof(host_display), proxy_str, proxy_type); |
| 226 | |
| 227 | const char *source = aclk_get_proxy_source(); |
| 228 | snprintfz(buf, buflen, "%s (%s, from %s)", |
| 229 | host_display, |
| 230 | strchr(proxy_str, '@') ? "with credentials" : "without credentials", |
| 231 | source ? source : "unknown"); |
| 232 | } |