summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
authorinqrphl <32687873+inqrphl@users.noreply.github.com>2026-03-13 15:54:23 +0100
committerGitHub <noreply@github.com>2026-03-13 15:54:23 +0100
commitb9cd60ec3a2eaa8155286c6b2ee131030f7feec7 (patch)
tree8478cef56f4d3fa04bd727a21ede139ed20e5249 /plugins
parenta793000948039afa1af9792dd776e2f9cda5b538 (diff)
downloadmonitoring-plugins-b9cd60ec3a2eaa8155286c6b2ee131030f7feec7.tar.gz
add proxy argument and improve dns cache usage (#2209)
* 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. * codespell, clang-format and hints fixes * add curl version and ssl enabelement macro checks might fix rocky linux 8 compilation issues. * add proxy_resolves_hostname, determined by proxy scheme leave the functions that print out an curl_easyoption, but dont use it. organize the code slightly, print out the final CURLOPT_PROXY and proxy_resolves_hostname flag on verbose mode, add comments * remove unused handle_curl_easyoption and format_curl_easyoption functions * fix typo in the proxy argument * fix typo with proxy scheme socks5a->socks5h * improve proxy environment parsing add another argument: --no-proxy , which is used when setting CURL_NOPROXY additionally parse all_proxy, ALL_PROXY, no_proxy and NO_PROXY environment variables in the correct order. set the curlopt_proxy and curlopt_noproxy of libcurl, and additionally save them in check_curl_working_state. add function determine_hostname_resolver, uses the working state and static config. it can tokenize the no_proxy variable and check for exact matches, but cannot determine subnet matches for ip addresses yet. * document proxy cli arguments clarify and add more examples of proxy environment variables and their behavior when multiple are specified, overriden etc. add single wildcard '*' checking for no_proxy to determine_hostname_resolver, special case per curlopt_noproxy documentation * check curlopt_noproxy before accessing it * switch argument from --no-proxy to --noproxy like curl cli * check if host name is a subdomain of an noproxy item * use strdup where destination working_state.curlopt_proxy may be NULL * add disclaimer about uppercase HTTP_PROXY * add subdomain checks for each item in the no_proxy, if the target host is a subdomain proxy wont resolve it add function ip_addr_inside_cidr, use it for checking possible cidr ranges given in the no_proxy * wip tests that work on local perl http/https server * wip tests that work on the live debian image * fix subnet definition * make apache2 listen on [::1] for ipv6 tests * remove squid certificate * rewrite ip_addr_inside_cidr, split ipv4 and ipv6 parsing path and copy them to a shared buffer later on for prefix check * Adapt tests for the squid sever, disable checking return code for socks 4/5 proxies. Squid does not support it, and we do not install a capable proxy for these schemes. * specify localhost acl and allow it through the proxy. used in check_curl tests * typo in comment * move function comments to header * fix failing tests * handle case where proxy is set as empty string * removed duplicate tests, corrected wrong comments * corrected some annotations * move docker apache subdomain setup files to /tools/subdomain1 * add a newline before dying in handle_curl_option_return_code * fix the -ssl better, now does not segfault on empty --ssl argument as well. --------- Co-authored-by: Ahmet Oeztuerk <Ahmet.Oeztuerk@consol.de>
Diffstat (limited to 'plugins')
-rw-r--r--plugins/check_curl.c91
-rw-r--r--plugins/check_curl.d/check_curl_helpers.c455
-rw-r--r--plugins/check_curl.d/check_curl_helpers.h9
-rw-r--r--plugins/check_curl.d/config.h7
-rw-r--r--plugins/t/check_curl.t115
-rwxr-xr-xplugins/tests/check_curl.t135
6 files changed, 785 insertions, 27 deletions
diff --git a/plugins/check_curl.c b/plugins/check_curl.c
index d7d68de5..4a1fb647 100644
--- a/plugins/check_curl.c
+++ b/plugins/check_curl.c
@@ -874,7 +874,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
874 COOKIE_JAR, 874 COOKIE_JAR,
875 HAPROXY_PROTOCOL, 875 HAPROXY_PROTOCOL,
876 STATE_REGEX, 876 STATE_REGEX,
877 OUTPUT_FORMAT 877 OUTPUT_FORMAT,
878 NO_PROXY,
878 }; 879 };
879 880
880 static struct option longopts[] = { 881 static struct option longopts[] = {
@@ -889,6 +890,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
889 {"url", required_argument, 0, 'u'}, 890 {"url", required_argument, 0, 'u'},
890 {"port", required_argument, 0, 'p'}, 891 {"port", required_argument, 0, 'p'},
891 {"authorization", required_argument, 0, 'a'}, 892 {"authorization", required_argument, 0, 'a'},
893 {"proxy", required_argument, 0, 'x'},
894 {"noproxy", required_argument, 0, NO_PROXY},
892 {"proxy-authorization", required_argument, 0, 'b'}, 895 {"proxy-authorization", required_argument, 0, 'b'},
893 {"header-string", required_argument, 0, 'd'}, 896 {"header-string", required_argument, 0, 'd'},
894 {"string", required_argument, 0, 's'}, 897 {"string", required_argument, 0, 's'},
@@ -961,7 +964,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
961 964
962 while (true) { 965 while (true) {
963 int option_index = getopt_long( 966 int option_index = getopt_long(
964 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", 967 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",
965 longopts, &option); 968 longopts, &option);
966 if (option_index == -1 || option_index == EOF || option_index == 1) { 969 if (option_index == -1 || option_index == EOF || option_index == 1) {
967 break; 970 break;
@@ -1049,6 +1052,10 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
1049 strncpy(result.config.curl_config.user_auth, optarg, MAX_INPUT_BUFFER - 1); 1052 strncpy(result.config.curl_config.user_auth, optarg, MAX_INPUT_BUFFER - 1);
1050 result.config.curl_config.user_auth[MAX_INPUT_BUFFER - 1] = 0; 1053 result.config.curl_config.user_auth[MAX_INPUT_BUFFER - 1] = 0;
1051 break; 1054 break;
1055 case 'x': /* proxy info */
1056 strncpy(result.config.curl_config.proxy, optarg, DEFAULT_BUFFER_SIZE - 1);
1057 result.config.curl_config.proxy[DEFAULT_BUFFER_SIZE - 1] = 0;
1058 break;
1052 case 'b': /* proxy-authorization info */ 1059 case 'b': /* proxy-authorization info */
1053 strncpy(result.config.curl_config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1); 1060 strncpy(result.config.curl_config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
1054 result.config.curl_config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0; 1061 result.config.curl_config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
@@ -1344,6 +1351,10 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
1344 case HAPROXY_PROTOCOL: 1351 case HAPROXY_PROTOCOL:
1345 result.config.curl_config.haproxy_protocol = true; 1352 result.config.curl_config.haproxy_protocol = true;
1346 break; 1353 break;
1354 case NO_PROXY:
1355 strncpy(result.config.curl_config.no_proxy, optarg, DEFAULT_BUFFER_SIZE - 1);
1356 result.config.curl_config.no_proxy[DEFAULT_BUFFER_SIZE - 1] = 0;
1357 break;
1347 case '?': 1358 case '?':
1348 /* print short usage statement if args not parsable */ 1359 /* print short usage statement if args not parsable */
1349 usage5(); 1360 usage5();
@@ -1371,35 +1382,35 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) {
1371 * parameters, like -S and -C combinations */ 1382 * parameters, like -S and -C combinations */
1372 result.config.curl_config.ssl_version = CURL_SSLVERSION_DEFAULT; 1383 result.config.curl_config.ssl_version = CURL_SSLVERSION_DEFAULT;
1373 if (tls_option_optarg != NULL) { 1384 if (tls_option_optarg != NULL) {
1374 char *plus_ptr = strchr(optarg, '+'); 1385 char *plus_ptr = strchr(tls_option_optarg, '+');
1375 if (plus_ptr) { 1386 if (plus_ptr) {
1376 got_plus = true; 1387 got_plus = true;
1377 *plus_ptr = '\0'; 1388 *plus_ptr = '\0';
1378 } 1389 }
1379 1390
1380 if (optarg[0] == '2') { 1391 if (tls_option_optarg[0] == '2') {
1381 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv2; 1392 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv2;
1382 } else if (optarg[0] == '3') { 1393 } else if (tls_option_optarg[0] == '3') {
1383 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv3; 1394 result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv3;
1384 } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) { 1395 } else if (!strcmp(tls_option_optarg, "1") || !strcmp(tls_option_optarg, "1.0")) {
1385#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) 1396#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1386 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_0; 1397 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_0;
1387#else 1398#else
1388 result.config.ssl_version = CURL_SSLVERSION_DEFAULT; 1399 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1389#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ 1400#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1390 } else if (!strcmp(optarg, "1.1")) { 1401 } else if (!strcmp(tls_option_optarg, "1.1")) {
1391#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) 1402#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1392 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_1; 1403 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_1;
1393#else 1404#else
1394 result.config.ssl_version = CURL_SSLVERSION_DEFAULT; 1405 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1395#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ 1406#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1396 } else if (!strcmp(optarg, "1.2")) { 1407 } else if (!strcmp(tls_option_optarg, "1.2")) {
1397#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) 1408#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1398 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_2; 1409 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_2;
1399#else 1410#else
1400 result.config.ssl_version = CURL_SSLVERSION_DEFAULT; 1411 result.config.ssl_version = CURL_SSLVERSION_DEFAULT;
1401#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ 1412#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1402 } else if (!strcmp(optarg, "1.3")) { 1413 } else if (!strcmp(tls_option_optarg, "1.3")) {
1403#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) 1414#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
1404 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_3; 1415 result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_3;
1405#else 1416#else
@@ -1522,8 +1533,8 @@ void print_help(void) {
1522 printf(" %s\n", "-I, --IP-address=ADDRESS"); 1533 printf(" %s\n", "-I, --IP-address=ADDRESS");
1523 printf(" %s\n", 1534 printf(" %s\n",
1524 "IP address or name (use numeric address if possible to bypass DNS lookup)."); 1535 "IP address or name (use numeric address if possible to bypass DNS lookup).");
1525 printf(" %s\n", "This overwrites the network address of the target while leaving everything " 1536 printf(" %s\n",
1526 "else (HTTP headers) as they are"); 1537 "This overwrites the network address of the target while leaving everything else (HTTP headers) as they are");
1527 printf(" %s\n", "-p, --port=INTEGER"); 1538 printf(" %s\n", "-p, --port=INTEGER");
1528 printf(" %s", _("Port number (default: ")); 1539 printf(" %s", _("Port number (default: "));
1529 printf("%d)\n", HTTP_PORT); 1540 printf("%d)\n", HTTP_PORT);
@@ -1587,8 +1598,7 @@ void print_help(void) {
1587 printf(" %s\n", _("String to expect in the content")); 1598 printf(" %s\n", _("String to expect in the content"));
1588 printf(" %s\n", "-u, --url=PATH"); 1599 printf(" %s\n", "-u, --url=PATH");
1589 printf(" %s\n", _("URL to GET or POST (default: /)")); 1600 printf(" %s\n", _("URL to GET or POST (default: /)"));
1590 printf(" %s\n", _("This is the part after the address in a URL, so for " 1601 printf(" %s\n", _("This is the part after the address in a URL, so for \"https://example.com/index.html\" it would be '-u /index.html'"));
1591 "\"https://example.com/index.html\" it would be '-u /index.html'"));
1592 printf(" %s\n", "-P, --post=STRING"); 1602 printf(" %s\n", "-P, --post=STRING");
1593 printf(" %s\n", _("URL decoded http POST data")); 1603 printf(" %s\n", _("URL decoded http POST data"));
1594 printf(" %s\n", 1604 printf(" %s\n",
@@ -1614,6 +1624,18 @@ void print_help(void) {
1614 printf(" %s\n", "--state-regex=STATE"); 1624 printf(" %s\n", "--state-regex=STATE");
1615 printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of " 1625 printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of "
1616 "\"critical\",\"warning\"")); 1626 "\"critical\",\"warning\""));
1627 printf(" %s\n", "-x, --proxy=PROXY_SERVER");
1628 printf(" %s\n", _("Specify the proxy in form of <scheme>://<host(name)>:<port>"));
1629 printf(" %s\n", _("Available schemes are http, https, socks4, socks4a, socks5, socks5h"));
1630 printf(" %s\n", _("If port is not specified, libcurl defaults to 1080"));
1631 printf(" %s\n", _("This value will be set as CURLOPT_PROXY"));
1632 printf(" %s\n", "--noproxy=COMMA_SEPARATED_LIST");
1633 printf(" %s\n", _("Specify hostnames, addresses and subnets where proxy should not be used"));
1634 printf(" %s\n", _("Example usage: \"example.com,::1,1.1.1.1,localhost,192.168.0.0/16\""));
1635 printf(" %s\n", _("Do not use brackets when specifying IPv6 addresses"));
1636 printf(" %s\n", _("Special case when an item is '*' : matches all hosts/addresses "
1637 "and effectively disables proxy."));
1638 printf(" %s\n", _("This value will be set as CURLOPT_NOPROXY"));
1617 printf(" %s\n", "-a, --authorization=AUTH_PAIR"); 1639 printf(" %s\n", "-a, --authorization=AUTH_PAIR");
1618 printf(" %s\n", _("Username:password on sites with basic authentication")); 1640 printf(" %s\n", _("Username:password on sites with basic authentication"));
1619 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); 1641 printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
@@ -1722,10 +1744,39 @@ void print_help(void) {
1722#endif 1744#endif
1723 1745
1724 printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:"); 1746 printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:");
1725 printf(" %s\n", _("It is recommended to use an environment proxy like:")); 1747 printf(" %s\n", _("Proxies are specified or disabled for certain hosts/addresses using environment variables"
1726 printf(" %s\n", 1748 " or -x/--proxy and --noproxy arguments:"));
1727 _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org")); 1749 printf(" %s\n", _("Checked environment variables: all_proxy, http_proxy, https_proxy, no_proxy"));
1728 printf(" %s\n", _("legacy proxy requests in check_http style still work:")); 1750 printf(" %s\n", _("Environment variables can also be given in uppercase, but the lowercase ones will "
1751 "take predence if both are defined."));
1752 printf(" %s\n", _("The environment variables are overwritten by -x/--proxy and --noproxy arguments:"));
1753 printf(" %s\n", _("all_proxy/ALL_PROXY environment variables are read first, but protocol "
1754 "specific environment variables override them."));
1755 printf(" %s\n", _("If SSL is enabled and used, https_proxy/HTTPS_PROXY will be checked and overwrite "
1756 "http_proxy/HTTPS_PROXY."));
1757 printf(" %s\n", _("Curl accepts proxies using http, https, socks4, socks4a, socks5 and socks5h schemes."));
1758 printf(" %s\n", _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org"));
1759 printf(" %s\n", _("http_proxy=http://used.proxy.com HTTP_PROXY=http://ignored.proxy.com ./check_curl -H www.monitoring-plugins.org"));
1760 printf(" %s\n", _(" Lowercase http_proxy takes predence over uppercase HTTP_PROXY"));
1761 printf(" %s\n", _("./check_curl -H www.monitoring-plugins.org -x http://192.168.100.35:3128"));
1762 printf(" %s\n", _("http_proxy=http://unused.proxy1.com HTTP_PROXY=http://unused.proxy2.com ./check_curl "
1763 "-H www.monitoring-plugins.org --proxy http://used.proxy"));
1764 printf(" %s\n", _(" Proxy specified by --proxy overrides any proxy specified by environment variable."));
1765 printf(" %s\n", _(" Curl uses port 1080 by default as port is not specified"));
1766 printf(" %s\n", _("HTTPS_PROXY=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org --ssl"));
1767 printf(" %s\n", _(" HTTPS_PROXY is read as --ssl is toggled"));
1768 printf(" %s\n", _("./check_curl -H www.monitoring-plugins.org --proxy socks5h://192.168.122.21"));
1769 printf(" %s\n", _("./check_curl -H www.monitoring-plugins.org -x http://unused.proxy.com --noproxy '*'"));
1770 printf(" %s\n", _(" Disabled proxy for all hosts by using '*' in no_proxy ."));
1771 printf(" %s\n", _("NO_PROXY=www.monitoring-plugins.org ./check_curl -H www.monitoring-plugins.org -x http://unused.proxy.com"));
1772 printf(" %s\n", _(" Exact matches with the hostname/address work."));
1773 printf(" %s\n", _("no_proxy=192.168.178.0/24 ./check_curl -I 192.168.178.10 -x http://proxy.acme.org"));
1774 printf(" %s\n", _("no_proxy=acme.org ./check_curl -H nonpublic.internalwebapp.acme.org -x http://proxy.acme.org"));
1775 printf(" %s\n", _(" Do not use proxy when accessing internal domains/addresses, but use a default proxy when accessing public web."));
1776 printf(" %s\n", _(" IMPORTANT: Check_curl can not always determine whether itself or the proxy will "
1777 "resolve a hostname before sending a request and getting an answer."
1778 "This can lead to DNS resolvation issues if hostname is only resolvable over proxy."));
1779 printf(" %s\n", _("Legacy proxy requests in check_http style still work:"));
1729 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ " 1780 printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ "
1730 "-H www.monitoring-plugins.org")); 1781 "-H www.monitoring-plugins.org"));
1731 1782
@@ -1756,13 +1807,15 @@ void print_usage(void) {
1756 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname); 1807 printf(" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n", progname);
1757 printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate " 1808 printf(" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate "
1758 "file>] [-D]\n"); 1809 "file>] [-D]\n");
1759 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n"); 1810 printf(" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-x <proxy>]\n");
1760 printf(" [-b proxy_auth] [-f <ok|warning|critical|follow|sticky|stickyport|curl>]\n"); 1811 printf(" [-a auth] [-b proxy_auth] [-f "
1812 "<ok|warning|critical|follow|sticky|stickyport|curl>]\n");
1761 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive " 1813 printf(" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive "
1762 "regex>]\n"); 1814 "regex>]\n");
1763 printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); 1815 printf(" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1764 printf(" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n"); 1816 printf(" [-A string] [-k string] [-S <version>] [--sni] [--haproxy-protocol]\n");
1765 printf(" [-T <content-type>] [-j method]\n"); 1817 printf(" [-T <content-type>] [-j method]\n");
1818 printf(" [--noproxy=<comma separated list of hosts, IP addresses, IP CIDR subnets>\n");
1766 printf(" [--http-version=<version>] [--enable-automatic-decompression]\n"); 1819 printf(" [--http-version=<version>] [--enable-automatic-decompression]\n");
1767 printf(" [--cookie-jar=<cookie jar file>\n"); 1820 printf(" [--cookie-jar=<cookie jar file>\n");
1768 printf(" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n", progname); 1821 printf(" %s -H <vhost> | -I <IP-address> -C <warn_age>[,<crit_age>]\n", progname);
diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c
index ad31b847..4372dc0b 100644
--- a/plugins/check_curl.d/check_curl_helpers.c
+++ b/plugins/check_curl.d/check_curl_helpers.c
@@ -3,8 +3,11 @@
3#include <arpa/inet.h> 3#include <arpa/inet.h>
4#include <netinet/in.h> 4#include <netinet/in.h>
5#include <netdb.h> 5#include <netdb.h>
6#include <stdint.h>
7#include <stdio.h>
6#include <stdlib.h> 8#include <stdlib.h>
7#include <string.h> 9#include <string.h>
10#include <sys/socket.h>
8#include "../utils.h" 11#include "../utils.h"
9#include "check_curl.d/config.h" 12#include "check_curl.d/config.h"
10#include "output.h" 13#include "output.h"
@@ -116,6 +119,107 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
116 curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout), 119 curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout),
117 "CURLOPT_TIMEOUT"); 120 "CURLOPT_TIMEOUT");
118 121
122 /* set proxy */
123 /* http(s) proxy can either be given from the command line, or taken from environment variables */
124 /* socks4(a) / socks5(h) proxy should be given using the command line */
125
126 /* first source to check is the environment variables */
127 /* lower case proxy environment variables are almost always accepted, while some programs also checking
128 uppercase ones. discover both, but take the lowercase one if both are present */
129
130 /* extra information: libcurl does not discover the uppercase version HTTP_PROXY due to security reasons */
131 /* https://github.com/curl/curl/blob/d445f2d930ae701039518d695481ee53b8490521/lib/url.c#L1987 */
132
133 /* first environment variable to read is all_proxy. it can be overridden by protocol specific environment variables */
134 char *all_proxy_env, *all_proxy_uppercase_env;
135 all_proxy_env = getenv("all_proxy");
136 all_proxy_uppercase_env = getenv("ALL_PROXY");
137 if (all_proxy_env != NULL && strlen(all_proxy_env)){
138 working_state.curlopt_proxy = strdup(all_proxy_env);
139 if (all_proxy_uppercase_env != NULL && verbose >= 1) {
140 printf("* cURL ignoring environment variable 'ALL_PROXY' as 'all_proxy' is set\n");
141 }
142 } else if (all_proxy_uppercase_env != NULL && strlen(all_proxy_uppercase_env) > 0) {
143 working_state.curlopt_proxy = strdup(all_proxy_uppercase_env);
144 }
145
146 /* second environment variable to read is http_proxy. only set curlopt_proxy if ssl is not toggled */
147 char *http_proxy_env, *http_proxy_uppercase_env;
148 http_proxy_env = getenv("http_proxy");
149 http_proxy_uppercase_env = getenv("HTTP_PROXY");
150 if (!working_state.use_ssl){
151 if (http_proxy_env != NULL && strlen(http_proxy_env) > 0) {
152 working_state.curlopt_proxy = strdup(http_proxy_env);
153 if (http_proxy_uppercase_env != NULL && verbose >= 1) {
154 printf("* cURL ignoring environment variable 'HTTP_PROXY' as 'http_proxy' is set\n");
155 }
156 } else if (http_proxy_uppercase_env != NULL && strlen(http_proxy_uppercase_env) > 0) {
157 working_state.curlopt_proxy = strdup(http_proxy_uppercase_env);
158 }
159 }
160#ifdef LIBCURL_FEATURE_SSL
161 /* optionally read https_proxy environment variable and set curlopt_proxy if ssl is toggled */
162 char *https_proxy_env, *https_proxy_uppercase_env;
163 https_proxy_env = getenv("https_proxy");
164 https_proxy_uppercase_env = getenv("HTTPS_PROXY");
165 if (working_state.use_ssl) {
166 if (https_proxy_env != NULL && strlen(https_proxy_env) > 0) {
167 working_state.curlopt_proxy = strdup(https_proxy_env);
168 if (https_proxy_uppercase_env != NULL && verbose >= 1) {
169 printf("* cURL ignoring environment variable 'HTTPS_PROXY' as 'https_proxy' is set\n");
170 }
171 }
172 else if (https_proxy_uppercase_env != NULL && strlen(https_proxy_uppercase_env) >= 0) {
173 working_state.curlopt_proxy = strdup(https_proxy_uppercase_env);
174 }
175 }
176#endif /* LIBCURL_FEATURE_SSL */
177
178 /* second source to check for proxies is command line argument, overwriting the environment variables */
179 if (strlen(config.proxy) > 0) {
180 working_state.curlopt_proxy = strdup(config.proxy);
181 }
182
183 if (working_state.curlopt_proxy != NULL && strlen(working_state.curlopt_proxy)){
184 handle_curl_option_return_code(
185 curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.curlopt_proxy), "CURLOPT_PROXY");
186 if (verbose >= 1) {
187 printf("* curl CURLOPT_PROXY: %s\n", working_state.curlopt_proxy);
188 }
189 }
190
191 /* set no_proxy */
192 /* first source to check is environment variables */
193 char *no_proxy_env, *no_proxy_uppercase_env;
194 no_proxy_env = getenv("no_proxy");
195 no_proxy_uppercase_env = getenv("NO_PROXY");
196 if (no_proxy_env != NULL && strlen(no_proxy_env)){
197 working_state.curlopt_noproxy = strdup(no_proxy_env);
198 if (no_proxy_uppercase_env != NULL && verbose >= 1){
199 printf("* cURL ignoring environment variable 'NO_PROXY' as 'no_proxy' is set\n");
200 }
201 }else if (no_proxy_uppercase_env != NULL && strlen(no_proxy_uppercase_env) > 0){
202 working_state.curlopt_noproxy = strdup(no_proxy_uppercase_env);
203 }
204
205 /* second source to check for no_proxy is command line argument, overwriting the environment variables */
206 if (strlen(config.no_proxy) > 0) {
207 working_state.curlopt_noproxy = strdup(config.no_proxy);
208 }
209
210 if ( working_state.curlopt_noproxy != NULL && strlen(working_state.curlopt_noproxy)){
211 handle_curl_option_return_code(
212 curl_easy_setopt(result.curl_state.curl, CURLOPT_NOPROXY, working_state.curlopt_noproxy), "CURLOPT_NOPROXY");
213 if (verbose >= 1) {
214 printf("* curl CURLOPT_NOPROXY: %s\n", working_state.curlopt_noproxy);
215 }
216 }
217
218 int proxy_resolves_hostname = determine_hostname_resolver(working_state, config);
219 if (verbose >= 1) {
220 printf("* proxy_resolves_hostname: %d\n", proxy_resolves_hostname);
221 }
222
119 /* enable haproxy protocol */ 223 /* enable haproxy protocol */
120 if (config.haproxy_protocol) { 224 if (config.haproxy_protocol) {
121 handle_curl_option_return_code( 225 handle_curl_option_return_code(
@@ -123,11 +227,11 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
123 "CURLOPT_HAPROXYPROTOCOL"); 227 "CURLOPT_HAPROXYPROTOCOL");
124 } 228 }
125 229
126 // fill dns resolve cache to make curl connect to the given server_address instead of the 230 /* fill dns resolve cache to make curl connect to the given server_address instead of the */
127 // host_name, only required for ssl, because we use the host_name later on to make SNI happy 231 /* host_name, only required for ssl, because we use the host_name later on to make SNI happy */
128 char dnscache[DEFAULT_BUFFER_SIZE]; 232 char dnscache[DEFAULT_BUFFER_SIZE];
129 char addrstr[DEFAULT_BUFFER_SIZE / 2]; 233 char addrstr[DEFAULT_BUFFER_SIZE / 2];
130 if (working_state.use_ssl && working_state.host_name != NULL) { 234 if (working_state.use_ssl && working_state.host_name != NULL && !proxy_resolves_hostname ) {
131 char *tmp_mod_address; 235 char *tmp_mod_address;
132 236
133 /* lookup_host() requires an IPv6 address without the brackets. */ 237 /* lookup_host() requires an IPv6 address without the brackets. */
@@ -562,7 +666,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
562 666
563void handle_curl_option_return_code(CURLcode res, const char *option) { 667void handle_curl_option_return_code(CURLcode res, const char *option) {
564 if (res != CURLE_OK) { 668 if (res != CURLE_OK) {
565 die(STATE_CRITICAL, _("Error while setting cURL option '%s': cURL returned %d - %s"), 669 die(STATE_CRITICAL, _("Error while setting cURL option '%s': cURL returned %d - %s\n"),
566 option, res, curl_easy_strerror(res)); 670 option, res, curl_easy_strerror(res));
567 } 671 }
568} 672}
@@ -589,6 +693,8 @@ check_curl_working_state check_curl_working_state_init() {
589 .serverPort = HTTP_PORT, 693 .serverPort = HTTP_PORT,
590 .use_ssl = false, 694 .use_ssl = false,
591 .no_body = false, 695 .no_body = false,
696 .curlopt_proxy = NULL,
697 .curlopt_noproxy = NULL,
592 }; 698 };
593 return result; 699 return result;
594} 700}
@@ -612,6 +718,8 @@ check_curl_config check_curl_config_init() {
612 .ca_cert = NULL, 718 .ca_cert = NULL,
613 .verify_peer_and_host = false, 719 .verify_peer_and_host = false,
614 .user_agent = {'\0'}, 720 .user_agent = {'\0'},
721 .proxy = "",
722 .no_proxy = "",
615 .proxy_auth = "", 723 .proxy_auth = "",
616 .user_auth = "", 724 .user_auth = "",
617 .http_content_type = NULL, 725 .http_content_type = NULL,
@@ -1295,3 +1403,342 @@ char *fmt_url(check_curl_working_state workingState) {
1295 1403
1296 return url; 1404 return url;
1297} 1405}
1406
1407int determine_hostname_resolver(const check_curl_working_state working_state, const check_curl_static_curl_config config){
1408 char *host_name_display = "NULL";
1409 unsigned long host_name_len = 0;
1410 if( working_state.host_name){
1411 host_name_len = strlen(working_state.host_name);
1412 host_name_display = working_state.host_name;
1413 }
1414
1415 /* IPv4 or IPv6 version of the address */
1416 char *server_address_clean = strdup(working_state.server_address);
1417 /* server address might be a full length ipv6 address encapsulated in square brackets */
1418 if ((strnlen(working_state.server_address, MAX_IPV4_HOSTLENGTH) > 2) && (working_state.server_address[0] == '[') && (working_state.server_address[strlen(working_state.server_address)-1] == ']') ) {
1419 server_address_clean = strndup( working_state.server_address + 1, strlen(working_state.server_address) - 2);
1420 }
1421
1422 /* check curlopt_noproxy option first */
1423 /* https://curl.se/libcurl/c/CURLOPT_NOPROXY.html */
1424
1425 /* curlopt_noproxy is specified as a comma separated list of
1426 direct IPv4 or IPv6 addresses e.g 130.133.8.40, 2001:4860:4802:32::a ,
1427 IPv4 or IPv6 CIDR regions e.g 10.241.0.0/16 , abcd:ef01:2345::/48 ,
1428 direct hostnames e.g example.com, google.de */
1429
1430 if (working_state.curlopt_noproxy != NULL){
1431 char* curlopt_noproxy_copy = strdup( working_state.curlopt_noproxy);
1432 char* noproxy_item = strtok(curlopt_noproxy_copy, ",");
1433 while(noproxy_item != NULL){
1434 unsigned long noproxy_item_len = strlen(noproxy_item);
1435
1436 /* According to the CURLOPT_NOPROXY documentation: */
1437 /* https://curl.se/libcurl/c/CURLOPT_NOPROXY.html */
1438 /* The only wildcard available is a single * character, which matches all hosts, and effectively disables the proxy. */
1439 if ( strlen(noproxy_item) == 1 && noproxy_item[0] == '*'){
1440 if (verbose >= 1){
1441 printf("* noproxy includes '*' which disables proxy for all host name incl. : %s / server address incl. : %s\n", host_name_display , server_address_clean);
1442 }
1443 free(curlopt_noproxy_copy);
1444 free(server_address_clean);
1445 return 0;
1446 }
1447
1448 /* direct comparison with the server_address */
1449 if( server_address_clean != NULL && strlen(server_address_clean) == strlen(noproxy_item) && strcmp(server_address_clean, noproxy_item) == 0){
1450 if (verbose >= 1){
1451 printf("* server_address is in the no_proxy list: %s\n", noproxy_item);
1452 }
1453 free(curlopt_noproxy_copy);
1454 free(server_address_clean);
1455 return 0;
1456 }
1457
1458 /* direct comparison with the host_name */
1459 if( working_state.host_name != NULL && host_name_len == noproxy_item_len && strcmp(working_state.host_name, noproxy_item) == 0){
1460 if (verbose >= 1){
1461 printf("* host_name is in the no_proxy list: %s\n", noproxy_item);
1462 }
1463 free(curlopt_noproxy_copy);
1464 free(server_address_clean);
1465 return 0;
1466 }
1467
1468 /* check if hostname is a subdomain of the item, e.g www.example.com when token is example.com */
1469 /* subdomain1.acme.com will not will use a proxy if you only specify 'acme' in the noproxy */
1470 /* check if noproxy_item is a suffix */
1471 /* check if the character just before the suffix is '.' */
1472 if( working_state.host_name != NULL && host_name_len > noproxy_item_len){
1473 unsigned long suffix_start_idx = host_name_len - noproxy_item_len;
1474 if (strcmp(working_state.host_name + suffix_start_idx, noproxy_item ) == 0 && working_state.host_name[suffix_start_idx-1] == '.' ){
1475 if (verbose >= 1){
1476 printf("* host_name: %s is a subdomain of the no_proxy list item: %s\n", working_state.host_name , noproxy_item);
1477 }
1478 free(curlopt_noproxy_copy);
1479 free(server_address_clean);
1480 return 0;
1481 }
1482 }
1483
1484 // noproxy_item could be a CIDR IP range
1485 if( server_address_clean != NULL && strlen(server_address_clean)){
1486
1487 int ip_addr_inside_cidr_ret = ip_addr_inside_cidr(noproxy_item, server_address_clean);
1488
1489 switch(ip_addr_inside_cidr_ret){
1490 case 1:
1491 return 0;
1492 break;
1493 case 0:
1494 if(verbose >= 1){
1495 printf("server address: %s is not inside IP cidr: %s\n", server_address_clean, noproxy_item);
1496 }
1497 break;
1498 case -1:
1499 if(verbose >= 1){
1500 printf("could not fully determine if server address: %s is inside the IP cidr: %s\n", server_address_clean, noproxy_item);
1501 }
1502 break;
1503 }
1504 }
1505
1506 noproxy_item = strtok(NULL, ",");
1507 }
1508
1509 free(curlopt_noproxy_copy);
1510 }
1511
1512 if (working_state.curlopt_proxy != NULL){
1513 // Libcurl documentation
1514 // Setting the proxy string to "" (an empty string) explicitly disables the use of a proxy, even if there is an environment variable set for it.
1515 if ( strlen(working_state.curlopt_proxy) == 0){
1516 return 0;
1517 }
1518
1519 if ( strncmp( working_state.curlopt_proxy, "http://", 7) == 0){
1520 if (verbose >= 1){
1521 printf("* proxy scheme is http, proxy: %s resolves host: %s or server_address: %s\n", working_state.curlopt_proxy, host_name_display, server_address_clean);
1522 }
1523 free(server_address_clean);
1524 return 1;
1525 }
1526
1527 if ( strncmp( working_state.curlopt_proxy, "https://", 8) == 0){
1528 if (verbose >= 1){
1529 printf("* proxy scheme is https, proxy: %s resolves host: %s or server_address: %s\n", working_state.curlopt_proxy, host_name_display, server_address_clean);
1530 }
1531 free(server_address_clean);
1532 return 1;
1533 }
1534
1535 if ( strncmp( working_state.curlopt_proxy, "socks4://", 9) == 0){
1536 if (verbose >= 1){
1537 printf("* proxy scheme is socks, proxy: %s does not resolve host: %s or server_address: %s\n", working_state.curlopt_proxy, host_name_display, server_address_clean);
1538 }
1539 free(server_address_clean);
1540 return 0;
1541 }
1542
1543 if ( strncmp( working_state.curlopt_proxy, "socks4a://", 10) == 0){
1544 if (verbose >= 1){
1545 printf("* proxy scheme is socks4a, proxy: %s resolves host: %s or server_address: %s\n", working_state.curlopt_proxy, host_name_display, server_address_clean);
1546 }
1547 free(server_address_clean);
1548 return 1;
1549 }
1550
1551 if ( strncmp( working_state.curlopt_proxy, "socks5://", 9) == 0){
1552 if (verbose >= 1){
1553 printf("* proxy scheme is socks5, proxy: %s does not resolve host: %s or server_address: %s\n", working_state.curlopt_proxy, host_name_display, server_address_clean);
1554 }
1555 free(server_address_clean);
1556 return 0;
1557 }
1558
1559 if ( strncmp( working_state.curlopt_proxy, "socks5h://", 10) == 0){
1560 if (verbose >= 1){
1561 printf("* proxy scheme is socks5h, proxy: %s resolves host: %s or server_address: %s\n", working_state.curlopt_proxy, host_name_display, server_address_clean);
1562 }
1563 free(server_address_clean);
1564 return 1;
1565 }
1566
1567 // Libcurl documentation:
1568 // Without a scheme prefix, CURLOPT_PROXYTYPE can be used to specify which kind of proxy the string identifies.
1569 // We do not set this value
1570 // Without a scheme, it is treated as an http proxy
1571
1572 return 1;
1573 }
1574
1575 if (verbose >= 1){
1576 printf("* proxy scheme is unknown/unavailable, no proxy is assumed for host: %s or server_address: %s\n", host_name_display, server_address_clean);
1577 }
1578
1579 free(server_address_clean);
1580 return 0;
1581}
1582
1583int ip_addr_inside_cidr(const char* cidr_region_or_ip_addr, const char* target_ip){
1584 unsigned int slash_count = 0;
1585 unsigned int last_slash_idx = 0;
1586 for(size_t i = 0; i < strlen(cidr_region_or_ip_addr); i++){
1587 if(cidr_region_or_ip_addr[i] == '/'){
1588 slash_count++;
1589 last_slash_idx = (unsigned int)i;
1590 }
1591 }
1592
1593 char *cidr_ip_part = NULL;
1594 int prefix_length = 0;
1595
1596 if (slash_count == 0) {
1597 cidr_ip_part = strdup(cidr_region_or_ip_addr);
1598 if (!cidr_ip_part) return -1;
1599 } else if (slash_count == 1) {
1600 cidr_ip_part = strndup(cidr_region_or_ip_addr, last_slash_idx);
1601 if (!cidr_ip_part) return -1;
1602
1603 errno = 0;
1604 long long tmp = strtoll(cidr_region_or_ip_addr + last_slash_idx + 1, NULL, 10);
1605 if (errno == ERANGE) {
1606 if (verbose >= 1) {
1607 printf("cidr_region_or_ip: %s , could not parse subnet length\n", cidr_region_or_ip_addr);
1608 }
1609 free(cidr_ip_part);
1610 return -1;
1611 }
1612 prefix_length = (int)tmp;
1613 } else {
1614 printf("cidr_region_or_ip: %s , has %d number of '/' characters, is not a valid cidr_region or IP\n", cidr_region_or_ip_addr, slash_count);
1615 return -1;
1616 }
1617
1618 int cidr_addr_family, target_addr_family;
1619 if (strchr(cidr_ip_part, ':')){
1620 cidr_addr_family = AF_INET6;
1621 } else {
1622 cidr_addr_family = AF_INET;
1623 }
1624
1625 if (strchr(target_ip, ':')){
1626 target_addr_family = AF_INET6;
1627 } else {
1628 target_addr_family = AF_INET;
1629 }
1630
1631 if (cidr_addr_family != target_addr_family){
1632 if (verbose >= 1){
1633 printf("cidr address: %s and target ip address: %s have different address families\n", cidr_ip_part, target_ip);
1634 }
1635 free(cidr_ip_part);
1636 return 0;
1637 }
1638
1639 // If no prefix is given, treat the cidr as a single address (full-length prefix)
1640 if (slash_count == 0) {
1641 prefix_length = (cidr_addr_family == AF_INET) ? 32 : 128;
1642 }
1643
1644 int max_bits = (cidr_addr_family == AF_INET) ? 32u : 128u;
1645 if (prefix_length < 0 || prefix_length > max_bits) {
1646 if (verbose >= 1) {
1647 printf("cidr_region_or_ip: %s has invalid prefix length: %u\n", cidr_region_or_ip_addr, prefix_length);
1648 }
1649 free(cidr_ip_part);
1650 return -1;
1651 }
1652
1653 if (verbose >= 1){
1654 printf("cidr_region_or_ip: %s , has prefix length: %u\n", cidr_region_or_ip_addr, prefix_length);
1655 }
1656
1657 int inet_pton_rc;
1658 uint8_t *cidr_bytes = NULL;
1659 uint8_t *target_bytes = NULL;
1660 uint8_t cidr_buf[16];
1661 uint8_t target_buf[16];
1662 size_t total_bytes = 0;
1663
1664 if (cidr_addr_family == AF_INET) {
1665 struct in_addr cidr_ipv4;
1666 struct in_addr target_ipv4;
1667 inet_pton_rc = inet_pton(AF_INET, cidr_ip_part, &cidr_ipv4);
1668 if (inet_pton_rc != 1) {
1669 if (verbose >= 1) {
1670 printf("ip string: %s contains characters not valid for its address family: IPv4\n", cidr_ip_part);
1671 }
1672 free(cidr_ip_part);
1673 return -1;
1674 }
1675 inet_pton_rc = inet_pton(AF_INET, target_ip, &target_ipv4);
1676 if (inet_pton_rc != 1) {
1677 if (verbose >= 1) {
1678 printf("ip string: %s contains characters not valid for its address family: IPv4\n", target_ip);
1679 }
1680 free(cidr_ip_part);
1681 return -1;
1682 }
1683 // copy the addresses in network byte order to a buffer for comparison
1684 memcpy(cidr_buf, &cidr_ipv4.s_addr, 4);
1685 memcpy(target_buf, &target_ipv4.s_addr, 4);
1686 cidr_bytes = cidr_buf;
1687 target_bytes = target_buf;
1688 total_bytes = 4;
1689 } else {
1690 struct in6_addr cidr_ipv6;
1691 struct in6_addr target_ipv6;
1692 inet_pton_rc = inet_pton(AF_INET6, cidr_ip_part, &cidr_ipv6);
1693 if (inet_pton_rc != 1) {
1694 if (verbose >= 1) {
1695 printf("ip string: %s contains characters not valid for its address family: IPv6\n", cidr_ip_part);
1696 }
1697 free(cidr_ip_part);
1698 return -1;
1699 }
1700 inet_pton_rc = inet_pton(AF_INET6, target_ip, &target_ipv6);
1701 if (inet_pton_rc != 1) {
1702 if (verbose >= 1) {
1703 printf("ip string: %s contains characters not valid for its address family: IPv6\n", target_ip);
1704 }
1705 free(cidr_ip_part);
1706 return -1;
1707 }
1708 memcpy(cidr_buf, &cidr_ipv6, 16);
1709 memcpy(target_buf, &target_ipv6, 16);
1710 cidr_bytes = cidr_buf;
1711 target_bytes = target_buf;
1712 total_bytes = 16;
1713 }
1714
1715 int prefix_bytes = prefix_length / 8;
1716 int prefix_bits = prefix_length % 8;
1717
1718 if (prefix_bytes > 0) {
1719 if (memcmp(cidr_bytes, target_bytes, (size_t)prefix_bytes) != 0) {
1720 if (verbose >= 1) {
1721 printf("the first %d bytes of the cidr_region_or_ip: %s and target_ip: %s are different\n", prefix_bytes, cidr_ip_part, target_ip);
1722 }
1723 free(cidr_ip_part);
1724 return 0;
1725 }
1726 }
1727
1728 if (prefix_bits != 0) {
1729 uint8_t cidr_oct = cidr_bytes[prefix_bytes];
1730 uint8_t target_oct = target_bytes[prefix_bytes];
1731 // the mask has first prefix_bits bits 1, the rest as 0
1732 uint8_t mask = (uint8_t)(0xFFu << (8 - prefix_bits));
1733 if ((cidr_oct & mask) != (target_oct & mask)) {
1734 if (verbose >= 1) {
1735 printf("looking at the last %d bits of the prefix, cidr_region_or_ip(%s) byte is: %u and target_ip byte(%s) is: %u, applying bitmask: %02X returns different results\n", prefix_bits, cidr_ip_part, (unsigned)cidr_oct, target_ip, (unsigned)target_oct, mask);
1736 }
1737 free(cidr_ip_part);
1738 return 0;
1739 }
1740 }
1741
1742 free(cidr_ip_part);
1743 return 1;
1744}
diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h
index e77b763b..cc47bf9d 100644
--- a/plugins/check_curl.d/check_curl_helpers.h
+++ b/plugins/check_curl.d/check_curl_helpers.h
@@ -126,3 +126,12 @@ void test_file(char *path);
126mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp, 126mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp,
127 int crit_days_till_exp); 127 int crit_days_till_exp);
128char *fmt_url(check_curl_working_state workingState); 128char *fmt_url(check_curl_working_state workingState);
129
130
131/* function that will determine if the host or the proxy resolves the target hostname
132returns 0 if requester resolves the hostname locally, 1 if proxy resolves the hostname */
133int determine_hostname_resolver(const check_curl_working_state working_state, const check_curl_static_curl_config config);
134
135/* Checks if an IP is inside given CIDR region. Using /protocol_size or not specifying the prefix length performs an equality check. Supports both IPv4 and IPv6
136returns 1 if the target_ip address is inside the given cidr_region_or_ip_addr, 0 if its out. return codes < 0 mean an error has occurred. */
137int ip_addr_inside_cidr(const char* cidr_region_or_ip_addr, const char* target_ip);
diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h
index 61067d46..bcdf3010 100644
--- a/plugins/check_curl.d/config.h
+++ b/plugins/check_curl.d/config.h
@@ -48,6 +48,11 @@ typedef struct {
48 48
49 bool use_ssl; 49 bool use_ssl;
50 bool no_body; 50 bool no_body;
51
52 /* curl CURLOPT_PROXY option will be set to this value if not NULL */
53 char *curlopt_proxy;
54 /* curl CURLOPT_NOPROXY option will be set to this value if not NULL */
55 char *curlopt_noproxy;
51} check_curl_working_state; 56} check_curl_working_state;
52 57
53check_curl_working_state check_curl_working_state_init(); 58check_curl_working_state check_curl_working_state_init();
@@ -65,6 +70,8 @@ typedef struct {
65 char *client_privkey; 70 char *client_privkey;
66 char *ca_cert; 71 char *ca_cert;
67 bool verify_peer_and_host; 72 bool verify_peer_and_host;
73 char proxy[DEFAULT_BUFFER_SIZE];
74 char no_proxy[DEFAULT_BUFFER_SIZE];
68 char user_agent[DEFAULT_BUFFER_SIZE]; 75 char user_agent[DEFAULT_BUFFER_SIZE];
69 char proxy_auth[MAX_INPUT_BUFFER]; 76 char proxy_auth[MAX_INPUT_BUFFER];
70 char user_auth[MAX_INPUT_BUFFER]; 77 char user_auth[MAX_INPUT_BUFFER];
diff --git a/plugins/t/check_curl.t b/plugins/t/check_curl.t
index 2c2fafde..a8326f12 100644
--- a/plugins/t/check_curl.t
+++ b/plugins/t/check_curl.t
@@ -13,7 +13,7 @@ use vars qw($tests $has_ipv6);
13BEGIN { 13BEGIN {
14 use NPTest; 14 use NPTest;
15 $has_ipv6 = NPTest::has_ipv6(); 15 $has_ipv6 = NPTest::has_ipv6();
16 $tests = $has_ipv6 ? 55 : 53; 16 $tests = $has_ipv6 ? 57 : 92;
17 plan tests => $tests; 17 plan tests => $tests;
18} 18}
19 19
@@ -25,7 +25,13 @@ my $plugin = 'check_http';
25$plugin = 'check_curl' if $0 =~ m/check_curl/mx; 25$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
26 26
27my $host_tcp_http = getTestParameter("NP_HOST_TCP_HTTP", "A host providing the HTTP Service (a web server)", "localhost"); 27my $host_tcp_http = getTestParameter("NP_HOST_TCP_HTTP", "A host providing the HTTP Service (a web server)", "localhost");
28my $host_tcp_http_subdomain = getTestParameter("NP_HOST_TCP_HTTP_SUBDOMAIN", "A host that is served under a subdomain name", "subdomain1.localhost.com");
29my $host_tcp_http_ipv4 = getTestParameter("NP_HOST_TCP_HTTP_IPV4", "An IPv6 address providing a HTTP Service (a web server)", "127.0.0.1");
30my $host_tcp_http_ipv4_cidr_1 = getTestParameter("NP_HOST_TCP_HTTP_IPV4_CIDR_1", "A CIDR that the provided IPv4 address is in.");
31my $host_tcp_http_ipv4_cidr_2 = getTestParameter("NP_HOST_TCP_HTTP_IPV4_CIDR_2", "A CIDR that the provided IPv4 address is in.");
28my $host_tcp_http_ipv6 = getTestParameter("NP_HOST_TCP_HTTP_IPV6", "An IPv6 address providing a HTTP Service (a web server)", "::1"); 32my $host_tcp_http_ipv6 = getTestParameter("NP_HOST_TCP_HTTP_IPV6", "An IPv6 address providing a HTTP Service (a web server)", "::1");
33my $host_tcp_http_ipv6_cidr_1 = getTestParameter("NP_HOST_TCP_HTTP_IPV6_CIDR_1", "A CIDR that the provided IPv6 address is in.");
34my $host_tcp_http_ipv6_cidr_2 = getTestParameter("NP_HOST_TCP_HTTP_IPV6_CIDR_2", "A CIDR that the provided IPv6 address is in.");
29my $host_tls_http = getTestParameter("NP_HOST_TLS_HTTP", "A host providing the HTTPS Service (a tls web server)", "localhost"); 35my $host_tls_http = getTestParameter("NP_HOST_TLS_HTTP", "A host providing the HTTPS Service (a tls web server)", "localhost");
30my $host_tls_cert = getTestParameter("NP_HOST_TLS_CERT", "the common name of the certificate.", "localhost"); 36my $host_tls_cert = getTestParameter("NP_HOST_TLS_CERT", "the common name of the certificate.", "localhost");
31my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname of system not responsive to network requests", "10.0.0.1"); 37my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname of system not responsive to network requests", "10.0.0.1");
@@ -222,3 +228,110 @@ SKIP: {
222 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org --extended-perfdata" ); 228 $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org --extended-perfdata" );
223 like ( $res->output, '/\'time_connect\'=[\d\.]+/', 'Extended Performance Data Output OK' ); 229 like ( $res->output, '/\'time_connect\'=[\d\.]+/', 'Extended Performance Data Output OK' );
224} 230}
231SKIP: {
232 skip "No internet access", 2 if $internet_access eq "no";
233
234 # Proxy tests
235 # These are the proxy tests that require a working proxy server
236 # The debian container in the github workflow runs a squid proxy server at port 3128
237 # Test that dont require one, like argument/environment variable parsing are in plugins/tests/check_curl.t
238
239 # Test if proxy works
240 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
241 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used, there are no preventative measures ");
242 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
243
244 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
245 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used, there are no preventative measures ");
246 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http_ipv4 works" );
247
248 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
249 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used, there are no preventative measures ");
250 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http_ipv6 works" );
251
252 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
253 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used, there are no preventative measures ");
254 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http2 works" );
255
256 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http_subdomain --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
257 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used, there are no preventative measures ");
258 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http_subdomain works" );
259
260 $res = NPTest->testCmd( "./$plugin -H $host_tls_http --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
261 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used, there are no preventative measures ");
262 is( $res->return_code, 0, "Using proxy http://$host_tcp_proxy:$port_tcp_proxy to connect to $host_tls_http works" );
263
264 # Noproxy '*' should prevent using proxy in any setting, even if its specified
265 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http_subdomain --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy \"\*\" -v" );
266 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since noproxy has \"\*\" ");
267 is( $res->return_code, 0, "Should reach $host_tcp_http_subdomain with or without proxy." );
268
269 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy \"\*\" -v" );
270 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since noproxy has \"\*\" ");
271 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv4 with or without proxy." );
272
273 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy \"\*\" -v" );
274 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since noproxy has \"\*\" ");
275 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv6 with or without proxy." );
276
277 # Noproxy domain should prevent using proxy for subdomains of that domain
278 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http_subdomain --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http -v" );
279 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since subdomain: $host_tcp_http_subdomain is under a noproxy domain: $host_tcp_http");
280 is( $res->return_code, 0, "Should reach $host_tcp_http_subdomain with or without proxy." );
281
282 # Noproxy should prevent using IP matches if an IP is found directly
283 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv4 -v" );
284 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since IP address: $host_tcp_http_ipv4 is added into noproxy: $host_tcp_http_ipv4");
285 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv4 with or without proxy." );
286
287 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv6 -v" );
288 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since IP address: $host_tcp_http_ipv6 is added into noproxy: $host_tcp_http_ipv6");
289 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv6 with or without proxy." );
290
291 # Noproxy should prevent using IP matches if a CIDR region that contains that Ip is used directly.
292 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv4_cidr_1 -v" );
293 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since IP address: $host_tcp_http_ipv4 is inside CIDR range: $host_tcp_http_ipv4_cidr_1");
294 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv4 with or without proxy." );
295
296 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv4 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv4_cidr_2 -v" );
297 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since IP address: $host_tcp_http_ipv4 is inside CIDR range: $host_tcp_http_ipv4_cidr_2");
298 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv4 with or without proxy." );
299
300 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv6_cidr_1 -v " );
301 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since IP address: $host_tcp_http_ipv6 is inside CIDR range: $host_tcp_http_ipv6_cidr_1");
302 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv6 with or without proxy." );
303
304 $res = NPTest->testCmd( "./$plugin -I $host_tcp_http_ipv6 --proxy http://$host_tcp_proxy:$port_tcp_proxy --noproxy $host_tcp_http_ipv6_cidr_2 -v" );
305 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used since IP address: $host_tcp_http_ipv6 is inside CIDR range: $host_tcp_http_ipv6_cidr_2");
306 is( $res->return_code, 0, "Should reach $host_tcp_http_ipv6 with or without proxy." );
307
308 # Noproxy should discern over different types of proxy schemes
309 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy http://$host_tcp_proxy:$port_tcp_proxy -v" );
310 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used for resolving hostname, and is using scheme http ");
311 is( $res->return_code, 0, "Using proxy http:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
312
313 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy https://$host_tcp_proxy:$port_tcp_proxy -v" );
314 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used for resolving hostname, and is using scheme https");
315 # Squid is not configured for https
316 # is( $res->return_code, 0, "Using proxy https:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
317
318 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy socks4://$host_tcp_proxy:$port_tcp_proxy -v" );
319 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used for resolving hostname, and is using scheme socks4");
320 # Squid is not configured for socks4
321 # is( $res->return_code, 0, "Using proxy socks4:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
322
323 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy socks4a://$host_tcp_proxy:$port_tcp_proxy -v" );
324 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used for resolving hostname, and is using scheme socks4a");
325 # Squid is not configured for socks4a
326 # is( $res->return_code, 0, "Using proxy socks4a:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
327
328 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy socks5://$host_tcp_proxy:$port_tcp_proxy -v" );
329 like($res->output, qr/^\* proxy_resolves_hostname: 0/m, "proxy is not used for resolving hostname, and is using scheme socks5");
330 # Squid is not configured for socks5
331 # is( $res->return_code, 0, "Using proxy socks5:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
332
333 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http --proxy socks5h://$host_tcp_proxy:$port_tcp_proxy -v" );
334 like($res->output, qr/^\* proxy_resolves_hostname: 1/m, "proxy is used for resolving hostname, and is using scheme socks5h");
335 # Squid is not configured for socks5h
336 # is( $res->return_code, 0, "Using proxy socks5h:$host_tcp_proxy:$port_tcp_proxy to connect to $host_tcp_http works" );
337}
diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t
index 248eb4c5..94058d5b 100755
--- a/plugins/tests/check_curl.t
+++ b/plugins/tests/check_curl.t
@@ -27,8 +27,8 @@ use HTTP::Daemon::SSL;
27 27
28$ENV{'LC_TIME'} = "C"; 28$ENV{'LC_TIME'} = "C";
29 29
30my $common_tests = 95; 30my $common_tests = 111;
31my $ssl_only_tests = 8; 31my $ssl_only_tests = 12;
32# Check that all dependent modules are available 32# Check that all dependent modules are available
33eval "use HTTP::Daemon 6.01;"; 33eval "use HTTP::Daemon 6.01;";
34plan skip_all => 'HTTP::Daemon >= 6.01 required' if $@; 34plan skip_all => 'HTTP::Daemon >= 6.01 required' if $@;
@@ -41,7 +41,7 @@ my $plugin = 'check_http';
41$plugin = 'check_curl' if $0 =~ m/check_curl/mx; 41$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
42 42
43# look for libcurl version to see if some advanced checks are possible (>= 7.49.0) 43# look for libcurl version to see if some advanced checks are possible (>= 7.49.0)
44my $advanced_checks = 12; 44my $advanced_checks = 16;
45my $use_advanced_checks = 0; 45my $use_advanced_checks = 0;
46my $required_version = '7.49.0'; 46my $required_version = '7.49.0';
47my $virtual_host = 'www.somefunnyhost.com'; 47my $virtual_host = 'www.somefunnyhost.com';
@@ -410,6 +410,41 @@ SKIP: {
410 $result = NPTest->testCmd( $cmd ); 410 $result = NPTest->testCmd( $cmd );
411 is( $result->return_code, 0, $cmd); 411 is( $result->return_code, 0, $cmd);
412 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); 412 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
413
414 # curlopt proxy/noproxy parsing tests, ssl disabled
415 {
416 # Make a scope and change environment variables here, to not mess them up for other tests using environment variables
417
418 local $ENV{"http_proxy"} = 'http://proxy.example.com:8080';
419 $cmd = "$command -u /statuscode/200 -v";
420 $result = NPTest->testCmd( $cmd );
421 like( $result->output, '/.*CURLOPT_PROXY: http://proxy.example.com:8080 */', "Correctly took 'http_proxy' environment variable: ".$result->output );
422 delete($ENV{"http_proxy"});
423
424 local $ENV{"http_proxy"} = 'http://taken.proxy.example:8080';
425 local $ENV{"HTTP_PROXY"} = 'http://discarded.proxy.example:8080';
426 $cmd = "$command -u /statuscode/200 -v";
427 $result = NPTest->testCmd( $cmd );
428 like( $result->output, '/.*CURLOPT_PROXY: http://taken.proxy.example:8080 */', "Correctly took 'http_proxy' environment variable over 'HTTP_PROXY': ".$result->output );
429 delete(local $ENV{"http_proxy"});
430 delete(local $ENV{"HTTP_PROXY"});
431
432 local $ENV{"http_proxy"} = 'http://discarded1.proxy.example:8080';
433 local $ENV{"HTTP_PROXY"} = 'http://discarded2.proxy.example:8080';
434 $cmd = "$command -u /statuscode/200 -x 'http://taken.proxy.example:8080' -v";
435 $result = NPTest->testCmd( $cmd );
436 like( $result->output, '/.*CURLOPT_PROXY: http://taken.proxy.example:8080 */', "Argument -x overwrote 'http_proxy' and 'HTTP_PROXY' environment variables: ".$result->output );
437 delete(local $ENV{"http_proxy"});
438 delete(local $ENV{"HTTP_PROXY"});
439
440 local $ENV{"http_proxy"} = 'http://discarded1.proxy.example:8080';
441 local $ENV{"HTTP_PROXY"} = 'http://discarded2.proxy.example:8080';
442 $cmd = "$command -u /statuscode/200 --proxy 'http://taken.example.com:8080' -v";
443 $result = NPTest->testCmd( $cmd );
444 like( $result->output, '/.*CURLOPT_PROXY: http://taken.example.com:8080 */', "Argument --proxy overwrote 'http_proxy' and 'HTTP_PROXY' environment variables: ".$result->output );
445 delete(local $ENV{"http_proxy"});
446 delete(local $ENV{"HTTP_PROXY"});
447 }
413} 448}
414 449
415# and the same for SSL 450# and the same for SSL
@@ -432,6 +467,41 @@ SKIP: {
432 $result = NPTest->testCmd( $cmd ); 467 $result = NPTest->testCmd( $cmd );
433 is( $result->return_code, 0, $cmd); 468 is( $result->return_code, 0, $cmd);
434 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); 469 like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output );
470
471 # curlopt proxy/noproxy parsing tests, ssl enabled
472 {
473 # Make a scope and change environment variables here, to not mess them up for other tests using environment variables
474
475 local $ENV{"https_proxy"} = 'http://proxy.example.com:8080';
476 $cmd = "$command -u /statuscode/200 --ssl -v";
477 $result = NPTest->testCmd( $cmd );
478 like( $result->output, '/.*CURLOPT_PROXY: http://proxy.example.com:8080 */', "Correctly took 'https_proxy' environment variable: ".$result->output );
479 delete($ENV{"https_proxy"});
480
481 local $ENV{"https_proxy"} = 'http://taken.proxy.example:8080';
482 local $ENV{"HTTPS_PROXY"} = 'http://discarded.proxy.example:8080';
483 $cmd = "$command -u /statuscode/200 --ssl -v";
484 $result = NPTest->testCmd( $cmd );
485 like( $result->output, '/.*CURLOPT_PROXY: http://taken.proxy.example:8080 */', "Correctly took 'https_proxy' environment variable over 'HTTPS_PROXY': ".$result->output );
486 delete(local $ENV{"https_proxy"});
487 delete(local $ENV{"HTTPS_PROXY"});
488
489 local $ENV{"https_proxy"} = 'http://discarded1.proxy.example:8080';
490 local $ENV{"HTTPS_PROXY"} = 'http://discarded2.proxy.example:8080';
491 $cmd = "$command -u /statuscode/200 --ssl -x 'http://taken.example.com:8080' -v";
492 $result = NPTest->testCmd( $cmd );
493 like( $result->output, '/.*CURLOPT_PROXY: http://taken.example.com:8080 */', "Argument -x overwrote environment variables 'https_proxy' and 'HTTPS_PROXY': ".$result->output );
494 delete(local $ENV{"https_proxy"});
495 delete(local $ENV{"HTTPS_PROXY"});
496
497 local $ENV{"https_proxy"} = 'http://discarded1.proxy.example:8080';
498 local $ENV{"HTTPS_PROXY"} = 'http://discarded2.proxy.example:8080';
499 $cmd = "$command -u /statuscode/200 --ssl --proxy 'http://taken.example.com:8080' -v";
500 $result = NPTest->testCmd( $cmd );
501 like( $result->output, '/.*CURLOPT_PROXY: http://taken.example.com:8080 */', "Argument --proxy overwrote environment variables 'https_proxy' and 'HTTPS_PROXY': ".$result->output );
502 delete(local $ENV{"https_proxy"});
503 delete(local $ENV{"HTTPS_PROXY"});
504 }
435} 505}
436 506
437 507
@@ -712,4 +782,63 @@ sub run_common_tests {
712 $result = NPTest->testCmd( $cmd, 5 ); 782 $result = NPTest->testCmd( $cmd, 5 );
713 }; 783 };
714 is( $@, "", $cmd ); 784 is( $@, "", $cmd );
785
786 # curlopt proxy/noproxy parsing tests
787 {
788 # Make a scope and change environment variables here, to not mess them up for other tests using environment variables
789
790 local $ENV{"no_proxy"} = 'internal.acme.org';
791 $cmd = "$command -u /statuscode/200 -v";
792 $result = NPTest->testCmd( $cmd );
793 like( $result->output, '/.* curl CURLOPT_NOPROXY: internal.acme.org */', "Correctly took 'no_proxy' environment variable: ".$result->output );
794 delete($ENV{"no_proxy"});
795
796 local $ENV{"no_proxy"} = 'taken.acme.org';
797 local $ENV{"NO_PROXY"} = 'discarded.acme.org';
798 $cmd = "$command -u /statuscode/200 -v";
799 $result = NPTest->testCmd( $cmd );
800 is( $result->return_code, 0, $cmd);
801 like( $result->output, '/.*CURLOPT_NOPROXY: taken.acme.org*/', "Correctly took 'no_proxy' environment variable over 'NO_PROXY': ".$result->output );
802 delete(local $ENV{"no_proxy"});
803 delete(local $ENV{"NO_PROXY"});
804
805 local $ENV{"no_proxy"} = 'taken.acme.org';
806 local $ENV{"NO_PROXY"} = 'discarded.acme.org';
807 $cmd = "$command -u /statuscode/200 --noproxy 'taken.acme.org' -v";
808 $result = NPTest->testCmd( $cmd );
809 is( $result->return_code, 0, $cmd);
810 like( $result->output, '/.*CURLOPT_NOPROXY: taken.acme.org*/', "Argument --noproxy overwrote environment variables 'no_proxy' and 'NO_PROXY': ".$result->output );
811 delete(local $ENV{"no_proxy"});
812 delete(local $ENV{"NO_PROXY"});
813
814 $cmd = "$command -u /statuscode/200 --noproxy 'internal1.acme.org,internal2.acme.org,internal3.acme.org' -v";
815 $result = NPTest->testCmd( $cmd );
816 is( $result->return_code, 0, $cmd);
817 like( $result->output, '/.*CURLOPT_NOPROXY: internal1.acme.org,internal2.acme.org,internal3.acme.org*/', "Argument --noproxy read multiple noproxy domains: ".$result->output );
818
819 $cmd = "$command -u /statuscode/200 --noproxy '10.11.12.13,256.256.256.256,0.0.0.0,192.156.0.0/22,10.0.0.0/4' -v";
820 $result = NPTest->testCmd( $cmd );
821 is( $result->return_code, 0, $cmd);
822 like( $result->output, '/.*CURLOPT_NOPROXY: 10.11.12.13,256.256.256.256,0.0.0.0,192.156.0.0/22,10.0.0.0/4*/', "Argument --noproxy took multiple noproxy domains: ".$result->output );
823
824 $cmd = "$command -u /statuscode/200 --noproxy '0123:4567:89AB:CDEF:0123:4567:89AB:CDEF,0123::CDEF,0123:4567/96,[::1],::1,[1234::5678:ABCD/4]' -v";
825 $result = NPTest->testCmd( $cmd );
826 is( $result->return_code, 0, $cmd);
827 like( $result->output, '/.*CURLOPT_NOPROXY: 0123:4567:89AB:CDEF:0123:4567:89AB:CDEF,0123::CDEF,0123:4567\/96,\[::1\],::1,\[1234::5678:ABCD\/4\].*/', "Argument --noproxy took multiple noproxy domains: ".$result->output );
828
829 $cmd = "$command -u /statuscode/200 --noproxy '300.400.500.600,1.2.3,XYZD:0123::,1:2:3:4:5:6:7,1::2::3,1.1.1.1/64,::/256' -v";
830 $result = NPTest->testCmd( $cmd );
831 is( $result->return_code, 0, $cmd);
832
833 $cmd = "$command -u /statuscode/200 --proxy http://proxy.example.com:8080 --noproxy '*' -v";
834 $result = NPTest->testCmd( $cmd );
835 is( $result->return_code, 0, $cmd);
836 like( $result->output, '/.*proxy_resolves_hostname: 0.*/', "Proxy will not be used due to '*' in noproxy: ".$result->output );
837
838 $cmd = "$command -u /statuscode/200 --proxy http://proxy.example.com:8080 --noproxy '127.0.0.1' -v";
839 $result = NPTest->testCmd( $cmd );
840 is( $result->return_code, 0, $cmd);
841 like( $result->output, '/.*proxy_resolves_hostname: 0.*/', "Proxy will not be used due to '127.0.0.1' in noproxy: ".$result->output );
842 }
843
715} 844}