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.
This commit is contained in:
Ahmet Oeztuerk 2025-12-16 16:06:43 +01:00
parent 0f0865c910
commit d5a5a3de6f
4 changed files with 125 additions and 7 deletions

View file

@ -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 <scheme>://<host(name)>:<port>"));
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 <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname);
printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate "
"file>] [-D]\n");
printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n");
printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-x <proxy>]\n");
printf(" [-a auth] [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n");
printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive "
"regex>]\n");
printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");

View file

@ -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,

View file

@ -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*/);

View file

@ -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];