From d5a5a3de6fb85e4e22388c61dc40ad1ce02aba7a Mon Sep 17 00:00:00 2001 From: Ahmet Oeztuerk Date: Tue, 16 Dec 2025 16:06:43 +0100 Subject: [PATCH] add proxy argument and improve dns cache usage add proxy argument that useing the -x and --proxy argument. add it to the static curl config struct, command usage and help outputs of the cli. parse these argument together with the environment variables like http_proxy before setting the CURLOPT_PROXY in the curl configuration option. this is required, as there is no easy way to ascertain/get what the CURLOPT_PROXY that libcurl will use. by the point it is set by libcurl, we have no control over it anymore, and need it for the other steps in the configuration. if the CURLOPT_PROXY is set, skip the DNS cache population which would set the CURLOPT_RESOLVE. this is currently not perfect however. if a proxy is set with socks4 or socks5 scheme, the host should be resolving the hostname. --- plugins/check_curl.c | 23 ++++- plugins/check_curl.d/check_curl_helpers.c | 104 +++++++++++++++++++++- plugins/check_curl.d/check_curl_helpers.h | 4 + plugins/check_curl.d/config.h | 1 + 4 files changed, 125 insertions(+), 7 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 95e45282..953fa9b2 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -889,6 +889,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { {"url", required_argument, 0, 'u'}, {"port", required_argument, 0, 'p'}, {"authorization", required_argument, 0, 'a'}, + {"proxy", required_argument, 0, 'x'}, {"proxy-authorization", required_argument, 0, 'b'}, {"header-string", required_argument, 0, 'd'}, {"string", required_argument, 0, 's'}, @@ -961,7 +962,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { while (true) { int option_index = getopt_long( - argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", + argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:x:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", longopts, &option); if (option_index == -1 || option_index == EOF || option_index == 1) { break; @@ -1049,6 +1050,10 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { strncpy(result.config.curl_config.user_auth, optarg, MAX_INPUT_BUFFER - 1); result.config.curl_config.user_auth[MAX_INPUT_BUFFER - 1] = 0; break; + case 'x': /* proxy info*/ + strncpy(result.config.curl_config.proxy, optarg, DEFAULT_BUFFER_SIZE -1); + result.config.curl_config.user_auth[DEFAULT_BUFFER_SIZE -1] = 0; + break; case 'b': /* proxy-authorization info */ strncpy(result.config.curl_config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1); result.config.curl_config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0; @@ -1614,6 +1619,11 @@ void print_help(void) { printf(" %s\n", "--state-regex=STATE"); printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of " "\"critical\",\"warning\"")); + printf(" %s\n", "-x, --proxy=PROXY_SERVER"); + printf(" %s\n", _("Specify the proxy in form of ://:")); + printf(" %s\n", _("Available schemes are http, https, socks4, socks4a, socks5, socks5a")); + printf(" %s\n", _("If port is not specified, libcurl defaults to 1080")); + printf(" %s\n", _("This value will be set as CURLOPT_PROXY")); printf(" %s\n", "-a, --authorization=AUTH_PAIR"); printf(" %s\n", _("Username:password on sites with basic authentication")); printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); @@ -1722,9 +1732,14 @@ void print_help(void) { #endif printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:"); - printf(" %s\n", _("It is recommended to use an environment proxy like:")); + printf(" %s\n", _("Proxies are defined checked using the -x or --proxy parameter:")); + printf(" %s\n", _("The environment variables are only checked -x/--proxy arguments are not set:")); + printf(" %s\n", _("Depending on the SSL enablement, either http_proxy or https_proxy environment variable is used.")); + printf(" %s\n", _("These variables can also be given in uppercase, but the lowercase ones will take predence if both are defined.")); printf(" %s\n", _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org")); + printf(" %s\n", + _("HTTPS_PROXY=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org --ssl")); printf(" %s\n", _("legacy proxy requests in check_http style still work:")); printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ " "-H www.monitoring-plugins.org")); @@ -1756,8 +1771,8 @@ void print_usage(void) { printf(" %s -H | -I [-u ] [-p ]\n", progname); printf(" [-J ] [-K ] [--ca-cert ] [-D]\n"); - printf(" [-w ] [-c ] [-t ] [-L] [-E] [-a auth]\n"); - printf(" [-b proxy_auth] [-f ]\n"); + printf(" [-w ] [-c ] [-t ] [-L] [-E] [-x ]\n"); + printf(" [-a auth] [-b proxy_auth] [-f ]\n"); printf(" [-e ] [-d string] [-s string] [-l] [-r | -R ]\n"); printf(" [-P string] [-m :] [-4|-6] [-N] [-M ]\n"); diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index ad31b847..68bd7c09 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -116,6 +116,54 @@ check_curl_configure_curl(const check_curl_static_curl_config config, curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout), "CURLOPT_TIMEOUT"); + /* set proxy */ + const struct curl_easyoption *curlopt_proxy_easyoption = curl_easy_option_by_id(CURLOPT_PROXY); + handle_curl_easyoption(curlopt_proxy_easyoption,"CURLOPT_PROXY"); + char proxy_option_str[DEFAULT_BUFFER_SIZE]; + if (verbose >= 1 && curlopt_proxy_easyoption != NULL){ + printf("* cURL Easy Option %s\n", format_curl_easyoption(curlopt_proxy_easyoption, proxy_option_str, DEFAULT_BUFFER_SIZE)); + } + /* proxy can either be given from the command line, or taken from environment variables */ + char curlopt_proxy[DEFAULT_BUFFER_SIZE]; + if (config.proxy != NULL && strlen(config.proxy) > 0 ){ + strcpy(curlopt_proxy, config.proxy); + }else{ + char *http_proxy_env; + http_proxy_env = getenv("http_proxy"); + char *http_proxy_uppercase_env; + http_proxy_uppercase_env = getenv("HTTP_PROXY"); + char *https_proxy_env; + https_proxy_env = getenv("https_proxy"); + char *https_proxy_uppercase_env; + https_proxy_uppercase_env = getenv("HTTPS_PROXY"); + /* lower case proxy environment varialbes are generally more accepted. accept both, but take the lowercase one when both are available*/ + if(working_state.use_ssl){ + if(https_proxy_env != NULL && strlen(https_proxy_env) > 0){ + strcpy(curlopt_proxy, https_proxy_env); + if(https_proxy_uppercase_env != NULL && verbose >=1){ + printf("* cURL ignoring environment variable HTTPS_PROXY as https_proxy is set\n"); + } + }else if(https_proxy_uppercase_env != NULL && strlen(https_proxy_uppercase_env) >= 0){ + strcpy(curlopt_proxy, https_proxy_uppercase_env); + } + else{ + strcpy(curlopt_proxy,""); + } + }else{ + if(http_proxy_env != NULL && strlen(http_proxy_env) > 0){ + strcpy(curlopt_proxy, http_proxy_env); + if(http_proxy_uppercase_env != NULL && verbose >=1){ + printf("* cURL ignoring environment variable HTTP_PROXY as http_proxy is set\n"); + } + }else if(http_proxy_uppercase_env != NULL && strlen(http_proxy_uppercase_env) > 0){ + strcpy(curlopt_proxy, http_proxy_uppercase_env); + }else{ + strcpy(curlopt_proxy,""); + } + } + } + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, curlopt_proxy), "CURLOPT_PROXY"); + /* enable haproxy protocol */ if (config.haproxy_protocol) { handle_curl_option_return_code( @@ -123,11 +171,13 @@ check_curl_configure_curl(const check_curl_static_curl_config config, "CURLOPT_HAPROXYPROTOCOL"); } - // fill dns resolve cache to make curl connect to the given server_address instead of the - // host_name, only required for ssl, because we use the host_name later on to make SNI happy + /* fill dns resolve cache to make curl connect to the given server_address instead of the */ + /* host_name, only required for ssl, because we use the host_name later on to make SNI happy */ + /* TODO: do not skip populating the DNS cache if the proxy scheme is socks4 or socks5.*/ + /* If the proxy should resolve the hostname, socks4h and socks5h scheme is used.*/ char dnscache[DEFAULT_BUFFER_SIZE]; char addrstr[DEFAULT_BUFFER_SIZE / 2]; - if (working_state.use_ssl && working_state.host_name != NULL) { + if (working_state.use_ssl && working_state.host_name != NULL && (curlopt_proxy != NULL && strlen(curlopt_proxy) == 0)) { char *tmp_mod_address; /* lookup_host() requires an IPv6 address without the brackets. */ @@ -567,6 +617,53 @@ void handle_curl_option_return_code(CURLcode res, const char *option) { } } +void handle_curl_easyoption(const struct curl_easyoption *option, const char *name) { + if (option == NULL){ + die(STATE_CRITICAL, _("Error while getting cURL option '%s': cURL option is null"), name); + } +} + +char *format_curl_easyoption(const struct curl_easyoption *option, char *buf, unsigned int buflen){ + if(option == NULL){ + die(STATE_CRITICAL, _("Can not print details about an empty cURL option")); + } + int offset = snprintf(buf, buflen, "name: %s flags: %s", option->name, option->flags); + switch(option->type){ + case CURLOT_LONG: + offset += snprintf(buf + offset, buflen - offset, " type: CURLOT_LONG"); + break; + case CURLOT_VALUES: + offset += snprintf(buf + offset, buflen - offset, " type: CURLOT_VALUES"); + break; + case CURLOT_OFF_T: + offset += snprintf(buf + offset, buflen - offset, " type: CURLOT_OFF_T"); + break; + case CURLOT_OBJECT: + offset += snprintf(buf + offset, buflen - offset, " type: CURLOT_OBJECT"); + break; + case CURLOT_STRING: + offset += snprintf(buf + offset, buflen - offset, " type: CURLOT_STRING"); + break; + case CURLOT_SLIST: + offset += snprintf(buf + offset, buflen - offset, " type: CURLOT_SLIST"); + break; + case CURLOT_CBPTR: + offset += snprintf(buf + offset, buflen - offset, " type: CURLOT_CBPTR"); + break; + case CURLOT_BLOB: + offset += snprintf(buf + offset, buflen - offset, " type: CURLOT_BLOB"); + break; + case CURLOT_FUNCTION: + offset += snprintf(buf + offset, buflen - offset, " type: CURLOT_FUNCTION"); + break; + default: + offset += snprintf(buf + offset, buflen - offset, " type: Unknown"); + break; + } + offset += snprintf(buf + offset, buflen - offset, " id: %d", option->id); + return buf; +} + char *get_header_value(const struct phr_header *headers, const size_t nof_headers, const char *header) { for (size_t i = 0; i < nof_headers; i++) { @@ -612,6 +709,7 @@ check_curl_config check_curl_config_init() { .ca_cert = NULL, .verify_peer_and_host = false, .user_agent = {'\0'}, + .proxy = "", .proxy_auth = "", .user_auth = "", .http_content_type = NULL, diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h index e77b763b..a5a0c5df 100644 --- a/plugins/check_curl.d/check_curl_helpers.h +++ b/plugins/check_curl.d/check_curl_helpers.h @@ -84,6 +84,10 @@ check_curl_configure_curl_wrapper check_curl_configure_curl(check_curl_static_cu void handle_curl_option_return_code(CURLcode res, const char *option); +void handle_curl_easyoption(const struct curl_easyoption *option, const char *name); + +char *format_curl_easyoption(const struct curl_easyoption *option, char *buf, unsigned int buflen); + int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf); size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 61067d46..be50c3d0 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -65,6 +65,7 @@ typedef struct { char *client_privkey; char *ca_cert; bool verify_peer_and_host; + char proxy[DEFAULT_BUFFER_SIZE]; char user_agent[DEFAULT_BUFFER_SIZE]; char proxy_auth[MAX_INPUT_BUFFER]; char user_auth[MAX_INPUT_BUFFER];