From 5a42813c9840f986e122947b1c8c7e099468eef3 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Thu, 9 Jan 2025 15:56:29 +0100 Subject: [PATCH 01/12] read http_proxy from env. variable --- src/sentry_options.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sentry_options.c b/src/sentry_options.c index 0cad8e335..c972b06ef 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -34,6 +34,7 @@ sentry_options_new(void) opts->release = sentry__string_clone(getenv("SENTRY_RELEASE")); opts->environment = sentry__string_clone(getenv("SENTRY_ENVIRONMENT")); #endif + sentry_options_set_proxy(opts, getenv("http_proxy")); if (!opts->environment) { opts->environment = sentry__string_clone("production"); } From 9565c21f62bfe4f60546f2a59892cc539ac83b30 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Fri, 10 Jan 2025 14:24:26 +0100 Subject: [PATCH 02/12] add proxy read from env option --- include/sentry.h | 11 +++++++++++ src/sentry_core.c | 4 ++++ src/sentry_options.c | 23 ++++++++++++++++++++++- src/sentry_options.h | 5 +++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/include/sentry.h b/include/sentry.h index e50f61a75..4596c25fb 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -1013,6 +1013,17 @@ SENTRY_API void sentry_options_set_http_proxy_n( SENTRY_API const char *sentry_options_get_http_proxy( const sentry_options_t *opts); +/** + * Sets whether to read the proxy settings from the environment. + */ +SENTRY_API void sentry_options_set_read_proxy_from_environment( + sentry_options_t *opts, int val); +/** + * Returns whether to read the proxy settings from the environment. + */ +SENTRY_API int sentry_options_get_read_proxy_from_environment( + const sentry_options_t *opts); + /** * Configures the path to a file containing ssl certificates for * verification. diff --git a/src/sentry_core.c b/src/sentry_core.c index 57d30b415..ec1747523 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -145,6 +145,10 @@ sentry_init(sentry_options_t *options) } } + if (options->read_proxy_from_environment) { + sentry__set_proxy_from_environment(options); + } + uint64_t last_crash = 0; // and then we will start the backend, since it requires a valid run diff --git a/src/sentry_options.c b/src/sentry_options.c index c972b06ef..0599b885f 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -34,7 +34,6 @@ sentry_options_new(void) opts->release = sentry__string_clone(getenv("SENTRY_RELEASE")); opts->environment = sentry__string_clone(getenv("SENTRY_ENVIRONMENT")); #endif - sentry_options_set_proxy(opts, getenv("http_proxy")); if (!opts->environment) { opts->environment = sentry__string_clone("production"); } @@ -277,6 +276,28 @@ sentry_options_get_http_proxy(const sentry_options_t *opts) return sentry_options_get_proxy(opts); } +void +sentry_options_set_read_proxy_from_environment(sentry_options_t *opts, int val) +{ + opts->read_proxy_from_environment = !!val; +} + +int +sentry_options_get_read_proxy_from_environment(const sentry_options_t *opts) +{ + return opts->read_proxy_from_environment; +} + +void +sentry__set_proxy_from_environment(sentry_options_t *opts) +{ + sentry_options_set_proxy(opts, getenv("http_proxy")); + const char *https_proxy = getenv("https_proxy"); + if (https_proxy) { + sentry_options_set_proxy(opts, https_proxy); + } +} + void sentry_options_set_ca_certs(sentry_options_t *opts, const char *path) { diff --git a/src/sentry_options.h b/src/sentry_options.h index 07a2fdbe6..cb914bc54 100644 --- a/src/sentry_options.h +++ b/src/sentry_options.h @@ -49,6 +49,7 @@ typedef struct sentry_options_s { bool require_user_consent; bool symbolize_stacktraces; bool system_crash_reporter_enabled; + bool read_proxy_from_environment; sentry_attachment_t *attachments; sentry_run_t *run; @@ -79,4 +80,8 @@ typedef struct sentry_options_s { */ sentry_options_t *sentry__options_incref(sentry_options_t *options); +/** + * Sets the proxy value by reading it from the environment. + */ +void sentry__set_proxy_from_environment(sentry_options_t *opts); #endif From 3abce7614ab680cede18a7df7a9773aabc40605f Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Fri, 10 Jan 2025 14:26:07 +0100 Subject: [PATCH 03/12] update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fff8eda9d..f423de5e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ **Features**: - Add option to set debug log level. ([#1107](https://github.com/getsentry/sentry-native/pull/1107)) +- Honor `http_proxy` environment variables ([#1111](https://github.com/getsentry/sentry-native/pull/1111)) ## 0.7.17 From 32a9d6012c1a7c4fb31bdede22fa085b1e29f46c Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Fri, 10 Jan 2025 14:49:19 +0100 Subject: [PATCH 04/12] Add proxy-from-env command + fix order of init --- CONTRIBUTING.md | 1 + examples/example.c | 4 ++++ src/sentry_core.c | 8 ++++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 041d99d24..890b50992 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -148,6 +148,7 @@ The example currently supports the following commands: - `stack-overflow`: Provokes a stack-overflow. - `http-proxy`: Uses a localhost `HTTP` proxy on port 8080. - `socks5-proxy`: Uses a localhost `SOCKS5` proxy on port 1080. +- `proxy-from-env`: Reads the proxy settings from the environment variables `https_proxy` and `http_proxy` (in this order of precedence). Only on Windows using crashpad with its WER handler module: diff --git a/examples/example.c b/examples/example.c index 4065cd40b..5bc24b988 100644 --- a/examples/example.c +++ b/examples/example.c @@ -244,6 +244,10 @@ main(int argc, char **argv) sentry_options_set_proxy(options, "socks5://127.0.0.1:1080"); } + if (has_arg(argc, argv, "proxy-from-env")) { + sentry_options_set_read_proxy_from_environment(options, true); + } + sentry_init(options); if (!has_arg(argc, argv, "no-setup")) { diff --git a/src/sentry_core.c b/src/sentry_core.c index ec1747523..a85aab005 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -138,6 +138,10 @@ sentry_init(sentry_options_t *options) "the provided DSN \"%s\" is not valid", raw_dsn ? raw_dsn : ""); } + if (options->read_proxy_from_environment) { + sentry__set_proxy_from_environment(options); + } + if (transport) { if (sentry__transport_startup(transport, options) != 0) { SENTRY_WARN("failed to initialize transport"); @@ -145,10 +149,6 @@ sentry_init(sentry_options_t *options) } } - if (options->read_proxy_from_environment) { - sentry__set_proxy_from_environment(options); - } - uint64_t last_crash = 0; // and then we will start the backend, since it requires a valid run From 8081074ba498585d8f212e3e20ee6accb5246d21 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Mon, 13 Jan 2025 10:19:30 +0100 Subject: [PATCH 05/12] reorder https->http proxy fetching --- src/sentry_options.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sentry_options.c b/src/sentry_options.c index 0599b885f..3d2b76485 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -291,10 +291,11 @@ sentry_options_get_read_proxy_from_environment(const sentry_options_t *opts) void sentry__set_proxy_from_environment(sentry_options_t *opts) { - sentry_options_set_proxy(opts, getenv("http_proxy")); const char *https_proxy = getenv("https_proxy"); if (https_proxy) { sentry_options_set_proxy(opts, https_proxy); + } else { + sentry_options_set_proxy(opts, getenv("http_proxy")); } } From 0d3e09c50b824405311d7e58b17ec9d7d37c407b Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Tue, 14 Jan 2025 17:18:52 +0100 Subject: [PATCH 06/12] add proxy_auth tests --- CONTRIBUTING.md | 2 ++ examples/example.c | 8 ++++++++ tests/test_integration_http.py | 19 ++++++++++++++----- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 890b50992..3cbd175b1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -147,7 +147,9 @@ The example currently supports the following commands: - `override-sdk-name`: Changes the SDK name via the options at runtime. - `stack-overflow`: Provokes a stack-overflow. - `http-proxy`: Uses a localhost `HTTP` proxy on port 8080. +- `http-proxy-auth`: Uses a localhost `HTTP` proxy on port 8080 with `user:password` as authentication. - `socks5-proxy`: Uses a localhost `SOCKS5` proxy on port 1080. +- `socks5-proxy-auth`: Uses a localhost `SOCKS5` proxy on port 1080 with `user:password` as authentication. - `proxy-from-env`: Reads the proxy settings from the environment variables `https_proxy` and `http_proxy` (in this order of precedence). Only on Windows using crashpad with its WER handler module: diff --git a/examples/example.c b/examples/example.c index 5bc24b988..80e23f038 100644 --- a/examples/example.c +++ b/examples/example.c @@ -239,10 +239,18 @@ main(int argc, char **argv) if (has_arg(argc, argv, "http-proxy")) { sentry_options_set_proxy(options, "http://127.0.0.1:8080"); } + if (has_arg(argc, argv, "http-proxy-auth")) { + sentry_options_set_proxy( + options, "http://user:password@127.0.0.1:8080"); + } if (has_arg(argc, argv, "socks5-proxy")) { sentry_options_set_proxy(options, "socks5://127.0.0.1:1080"); } + if (has_arg(argc, argv, "socks5-proxy-auth")) { + sentry_options_set_proxy( + options, "socks5://user:password@127.0.0.1:1080"); + } if (has_arg(argc, argv, "proxy-from-env")) { sentry_options_set_read_proxy_from_environment(options, true); diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index 15543aaa0..dcfa43df0 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -625,7 +625,8 @@ def test_capture_minidump(cmake, httpserver): ], ) @pytest.mark.parametrize("proxy_status", [(["off"]), (["on"])]) -def test_capture_proxy(cmake, httpserver, run_args, proxy_status): +@pytest.mark.parametrize("proxy_auth", [(["off"]), (["on"])]) +def test_capture_proxy(cmake, httpserver, run_args, proxy_status, proxy_auth): if not shutil.which("mitmdump"): pytest.skip("mitmdump is not installed") @@ -635,12 +636,18 @@ def test_capture_proxy(cmake, httpserver, run_args, proxy_status): if proxy_status == ["on"]: # start mitmdump from terminal if run_args == ["http-proxy"]: - proxy_process = subprocess.Popen(["mitmdump"]) + proxy_command = ["mitmdump"] + if proxy_auth == ["on"]: + proxy_command.append("--proxyauth=user:password") + proxy_process = subprocess.Popen(proxy_command) time.sleep(5) # Give mitmdump some time to start if not is_proxy_running("localhost", 8080): pytest.fail("mitmdump (HTTP) did not start correctly") elif run_args == ["socks5-proxy"]: - proxy_process = subprocess.Popen(["mitmdump", "--mode", "socks5"]) + proxy_command = ["mitmdump", "--mode", "socks5"] + if proxy_auth == ["on"]: + proxy_command.append("--proxyauth=user:password") + proxy_process = subprocess.Popen(proxy_command) time.sleep(5) # Give mitmdump some time to start if not is_proxy_running("localhost", 1080): pytest.fail("mitmdump (SOCKS5) did not start correctly") @@ -651,12 +658,14 @@ def test_capture_proxy(cmake, httpserver, run_args, proxy_status): shutil.rmtree(tmp_path / ".sentry-native", ignore_errors=True) httpserver.expect_request("/api/123456/envelope/").respond_with_data("OK") - + current_run_arg = run_args[0] + if proxy_auth == ["on"]: + current_run_arg += "-auth" run( tmp_path, "sentry_example", ["log", "start-session", "capture-event"] - + run_args, # only passes if given proxy is running + + [current_run_arg], # only passes if given proxy is running check=True, env=dict(os.environ, SENTRY_DSN=make_dsn(httpserver)), ) From 81abb92808ad8283ab5b0141ea4a938dc580a96c Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Tue, 14 Jan 2025 17:51:38 +0100 Subject: [PATCH 07/12] fix CHANGELOG.md --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79c190f5d..f9f9e1094 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,18 @@ # Changelog +## Unreleased + +**Features**: +- Honor `http_proxy` environment variables. ([#1111](https://github.com/getsentry/sentry-native/pull/1111)) + ## 0.7.18 **Features**: - Add support for Xbox Series X/S. ([#1100](https://github.com/getsentry/sentry-native/pull/1100)) - Add option to set debug log level. ([#1107](https://github.com/getsentry/sentry-native/pull/1107)) -- Add `traces_sampler` ([#1108](https://github.com/getsentry/sentry-native/pull/1108)) +- Add `traces_sampler`. ([#1108](https://github.com/getsentry/sentry-native/pull/1108)) - Provide support for C++17 compilers when using the `crashpad` backend. ([#1110](https://github.com/getsentry/sentry-native/pull/1110), [crashpad#116](https://github.com/getsentry/crashpad/pull/116), [mini_chromium#1](https://github.com/getsentry/mini_chromium/pull/1)) -- Honor `http_proxy` environment variables ([#1111](https://github.com/getsentry/sentry-native/pull/1111)) ## 0.7.17 From d6edb129ea5d4a98aefee66be375fbfcae30ae9e Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Tue, 14 Jan 2025 17:52:49 +0100 Subject: [PATCH 08/12] fix CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9f9e1094..b0f2bdc04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased **Features**: + - Honor `http_proxy` environment variables. ([#1111](https://github.com/getsentry/sentry-native/pull/1111)) ## 0.7.18 From c3a75751b30031f7be63e6e7934be53f71134c07 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:15:33 +0100 Subject: [PATCH 09/12] fix test to avoid cmake build failed From fb4680811992ca1f5a8381af9e585286aae50b59 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:17:48 +0100 Subject: [PATCH 10/12] add WinHTTP proxy username and password PoC --- src/transports/sentry_transport_winhttp.c | 46 +++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/transports/sentry_transport_winhttp.c b/src/transports/sentry_transport_winhttp.c index b72751160..0ba41b6d8 100644 --- a/src/transports/sentry_transport_winhttp.c +++ b/src/transports/sentry_transport_winhttp.c @@ -17,6 +17,8 @@ typedef struct { sentry_dsn_t *dsn; wchar_t *user_agent; wchar_t *proxy; + wchar_t *proxy_username; + wchar_t *proxy_password; sentry_rate_limiter_t *ratelimiter; HINTERNET session; HINTERNET connect; @@ -55,6 +57,38 @@ sentry__winhttp_bgworker_state_free(void *_state) sentry_free(state); } +// Function to extract and set credentials TODO replace with `sentry__url_parse` +void +set_proxy_credentials(winhttp_bgworker_state_t *state, const char *proxy) +{ + const char *at_sign = strchr(proxy, '@'); + if (at_sign) { + const char *credentials = proxy + 7; // Skip "http://" + char *colon = strchr(credentials, ':'); + if (colon && colon < at_sign) { + size_t user_len = colon - credentials; + size_t pass_len = at_sign - colon - 1; + char *user = (char *)malloc(user_len + 1); + char *pass = (char *)malloc(pass_len + 1); + strncpy(user, credentials, user_len); + user[user_len] = '\0'; + strncpy(pass, colon + 1, pass_len); + pass[pass_len] = '\0'; + + // Convert user and pass to LPCWSTR + int user_wlen = MultiByteToWideChar(CP_UTF8, 0, user, -1, NULL, 0); + int pass_wlen = MultiByteToWideChar(CP_UTF8, 0, pass, -1, NULL, 0); + wchar_t *user_w = (wchar_t *)malloc(user_wlen * sizeof(wchar_t)); + wchar_t *pass_w = (wchar_t *)malloc(pass_wlen * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, user, -1, user_w, user_wlen); + MultiByteToWideChar(CP_UTF8, 0, pass, -1, pass_w, pass_wlen); + + state->proxy_user = user_w; + state->proxy_pass = pass_w; + } + } +} + static int sentry__winhttp_transport_start( const sentry_options_t *opts, void *transport_state) @@ -71,7 +105,12 @@ sentry__winhttp_transport_start( // ensure the proxy starts with `http://`, otherwise ignore it if (opts->proxy && strstr(opts->proxy, "http://") == opts->proxy) { const char *ptr = opts->proxy + 7; + const char *at_sign = strchr(ptr, '@'); const char *slash = strchr(ptr, '/'); + if (at_sign && (!slash || at_sign < slash)) { + ptr = at_sign + 1; + set_proxy_credentials(state, opts->proxy); + } if (slash) { char *copy = sentry__string_clone_n(ptr, slash - ptr); state->proxy = sentry__string_to_wstr(copy); @@ -103,6 +142,8 @@ sentry__winhttp_transport_start( SENTRY_WARN("`WinHttpOpen` failed"); return 1; } + + set_proxy_credentials(state, opts->proxy); return sentry__bgworker_start(bgworker); } @@ -209,6 +250,11 @@ sentry__winhttp_send_task(void *_envelope, void *_state) SENTRY_DEBUGF( "sending request using winhttp to \"%s\":\n%S", req->url, headers); + if (state->proxy_user && state->proxy_pass) { + WinHttpSetCredentials(state->request, WINHTTP_AUTH_TARGET_PROXY, + WINHTTP_AUTH_SCHEME_BASIC, state->proxy_user, state->proxy_pass, 0); + } + if (WinHttpSendRequest(state->request, headers, (DWORD)-1, (LPVOID)req->body, (DWORD)req->body_len, (DWORD)req->body_len, 0)) { WinHttpReceiveResponse(state->request, NULL); From aa1c8c29e1b5954c84b660df8d3ef0309684b7cf Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:24:02 +0100 Subject: [PATCH 11/12] remove double set_proxy_credentials call --- src/transports/sentry_transport_winhttp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/transports/sentry_transport_winhttp.c b/src/transports/sentry_transport_winhttp.c index 0ba41b6d8..434bf969a 100644 --- a/src/transports/sentry_transport_winhttp.c +++ b/src/transports/sentry_transport_winhttp.c @@ -143,7 +143,6 @@ sentry__winhttp_transport_start( return 1; } - set_proxy_credentials(state, opts->proxy); return sentry__bgworker_start(bgworker); } From 86b1f6c7a41548e96b116ba929c90d52c30f261b Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:51:40 +0100 Subject: [PATCH 12/12] fix typos and some mem cleaning --- src/transports/sentry_transport_winhttp.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/transports/sentry_transport_winhttp.c b/src/transports/sentry_transport_winhttp.c index 434bf969a..0cbe5d024 100644 --- a/src/transports/sentry_transport_winhttp.c +++ b/src/transports/sentry_transport_winhttp.c @@ -53,6 +53,8 @@ sentry__winhttp_bgworker_state_free(void *_state) sentry__dsn_decref(state->dsn); sentry__rate_limiter_free(state->ratelimiter); sentry_free(state->user_agent); + sentry_free(state->proxy_username); + sentry_free(state->proxy_password); sentry_free(state->proxy); sentry_free(state); } @@ -83,8 +85,11 @@ set_proxy_credentials(winhttp_bgworker_state_t *state, const char *proxy) MultiByteToWideChar(CP_UTF8, 0, user, -1, user_w, user_wlen); MultiByteToWideChar(CP_UTF8, 0, pass, -1, pass_w, pass_wlen); - state->proxy_user = user_w; - state->proxy_pass = pass_w; + state->proxy_username = user_w; + state->proxy_password = pass_w; + + sentry_free(user); + sentry_free(pass); } } } @@ -249,9 +254,10 @@ sentry__winhttp_send_task(void *_envelope, void *_state) SENTRY_DEBUGF( "sending request using winhttp to \"%s\":\n%S", req->url, headers); - if (state->proxy_user && state->proxy_pass) { + if (state->proxy_username && state->proxy_password) { WinHttpSetCredentials(state->request, WINHTTP_AUTH_TARGET_PROXY, - WINHTTP_AUTH_SCHEME_BASIC, state->proxy_user, state->proxy_pass, 0); + WINHTTP_AUTH_SCHEME_BASIC, state->proxy_username, + state->proxy_password, 0); } if (WinHttpSendRequest(state->request, headers, (DWORD)-1,