summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/NPTest.cache7
-rwxr-xr-x.github/prepare_debian.sh21
-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
-rw-r--r--tools/squid.conf3
-rw-r--r--tools/subdomain1/index.php1
-rw-r--r--tools/subdomain1/subdomain1.conf22
11 files changed, 835 insertions, 31 deletions
diff --git a/.github/NPTest.cache b/.github/NPTest.cache
index 6b463e74..3f6d4da6 100644
--- a/.github/NPTest.cache
+++ b/.github/NPTest.cache
@@ -19,6 +19,13 @@
19 'NP_HOST_TCP_HPJD' => '', 19 'NP_HOST_TCP_HPJD' => '',
20 'NP_HOST_TCP_HTTP2' => 'test.monitoring-plugins.org', 20 'NP_HOST_TCP_HTTP2' => 'test.monitoring-plugins.org',
21 'NP_HOST_TCP_HTTP' => 'localhost', 21 'NP_HOST_TCP_HTTP' => 'localhost',
22 'NP_HOST_TCP_HTTP_IPV4' => '127.0.0.1',
23 'NP_HOST_TCP_HTTP_IPV4_CIDR_1' => '127.0.0.0/28',
24 'NP_HOST_TCP_HTTP_IPV4_CIDR_2' => '127.0.0.1/32',
25 'NP_HOST_TCP_HTTP_IPV6' => '::1',
26 'NP_HOST_TCP_HTTP_IPV6_CIDR_1' => '0000:0000:0000::0000:0000:0000/16',
27 'NP_HOST_TCP_HTTP_IPV6_CIDR_2' => '::1234:5678/16',
28 'NP_HOST_TCP_HTTP_SUBDOMAIN' => 'subdomain1.localhost',
22 'NP_HOST_TCP_IMAP' => 'imap.web.de', 29 'NP_HOST_TCP_IMAP' => 'imap.web.de',
23 'NP_HOST_TCP_JABBER' => 'jabber.org', 30 'NP_HOST_TCP_JABBER' => 'jabber.org',
24 'NP_HOST_TCP_LDAP' => 'localhost', 31 'NP_HOST_TCP_LDAP' => 'localhost',
diff --git a/.github/prepare_debian.sh b/.github/prepare_debian.sh
index cffe98c5..15c2286c 100755
--- a/.github/prepare_debian.sh
+++ b/.github/prepare_debian.sh
@@ -67,10 +67,10 @@ apt-get -y install perl \
67 libjson-perl 67 libjson-perl
68 68
69# remove ipv6 interface from hosts 69# remove ipv6 interface from hosts
70sed '/^::1/d' /etc/hosts > /tmp/hosts 70# sed '/^::1/d' /etc/hosts > /tmp/hosts
71cp -f /tmp/hosts /etc/hosts 71# cp -f /tmp/hosts /etc/hosts
72ip addr show 72# ip addr show
73cat /etc/hosts 73# cat /etc/hosts
74 74
75# apache 75# apache
76a2enmod ssl 76a2enmod ssl
@@ -80,6 +80,19 @@ a2ensite default-ssl
80rm /etc/ssl/certs/ssl-cert-snakeoil.pem 80rm /etc/ssl/certs/ssl-cert-snakeoil.pem
81rm /etc/ssl/private/ssl-cert-snakeoil.key 81rm /etc/ssl/private/ssl-cert-snakeoil.key
82openssl req -nodes -newkey rsa:2048 -x509 -sha256 -days 365 -nodes -keyout /etc/ssl/private/ssl-cert-snakeoil.key -out /etc/ssl/certs/ssl-cert-snakeoil.pem -subj "/C=GB/ST=London/L=London/O=Global Security/OU=IT Department/CN=$(hostname)" 82openssl req -nodes -newkey rsa:2048 -x509 -sha256 -days 365 -nodes -keyout /etc/ssl/private/ssl-cert-snakeoil.key -out /etc/ssl/certs/ssl-cert-snakeoil.pem -subj "/C=GB/ST=London/L=London/O=Global Security/OU=IT Department/CN=$(hostname)"
83# add a subdomain for testing
84cp tools/subdomain1/subdomain1.conf /etc/apache2/sites-available/
85mkdir -p /var/www/subdomain1
86cp tools/subdomain1/index.php /var/www/subdomain1/
87echo '127.0.0.1 subdomain1.localhost' >> /etc/hosts
88echo '127.0.0.1 subdomain1.localhost.com' >> /etc/hosts
89apache2ctl configtest
90a2ensite subdomain1.conf
91
92# Make it listen to both IPv4 on IPv6 on localhost
93sed -i 's/^Listen 80/Listen 0.0.0.0:80\nListen [::1]:80/' /etc/apache2/ports.conf
94sed -i 's/^[[:space:]]*Listen 443/Listen 0.0.0.0:443\nListen [::1]:443/' /etc/apache2/ports.conf
95
83service apache2 restart 96service apache2 restart
84 97
85# squid 98# squid
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}
diff --git a/tools/squid.conf b/tools/squid.conf
index bed7a583..048aa576 100644
--- a/tools/squid.conf
+++ b/tools/squid.conf
@@ -972,6 +972,8 @@
972# Example rule allowing access from your local networks. 972# Example rule allowing access from your local networks.
973# Adapt to list your (internal) IP networks from where browsing 973# Adapt to list your (internal) IP networks from where browsing
974# should be allowed 974# should be allowed
975acl localhost src 127.0.0.1/32
976acl localhost src ::1/128
975acl localnet src 10.0.0.0/8 # RFC1918 possible internal network 977acl localnet src 10.0.0.0/8 # RFC1918 possible internal network
976acl localnet src 172.16.0.0/12 # RFC1918 possible internal network 978acl localnet src 172.16.0.0/12 # RFC1918 possible internal network
977acl localnet src 192.168.0.0/16 # RFC1918 possible internal network 979acl localnet src 192.168.0.0/16 # RFC1918 possible internal network
@@ -1172,6 +1174,7 @@ http_access deny !Safe_ports
1172http_access deny CONNECT !SSL_ports 1174http_access deny CONNECT !SSL_ports
1173 1175
1174# Only allow cachemgr access from localhost 1176# Only allow cachemgr access from localhost
1177http_access allow localhost
1175http_access allow localhost manager 1178http_access allow localhost manager
1176http_access deny manager 1179http_access deny manager
1177 1180
diff --git a/tools/subdomain1/index.php b/tools/subdomain1/index.php
new file mode 100644
index 00000000..e97b19f5
--- /dev/null
+++ b/tools/subdomain1/index.php
@@ -0,0 +1 @@
Subdomain: subdomain1.localhost.com
diff --git a/tools/subdomain1/subdomain1.conf b/tools/subdomain1/subdomain1.conf
new file mode 100644
index 00000000..74521792
--- /dev/null
+++ b/tools/subdomain1/subdomain1.conf
@@ -0,0 +1,22 @@
1# This apache configuration file is used testing
2# check_curl tests use this subdomain to see if --noproxy works on subdomains of a domain.
3
4<VirtualHost *:80>
5 ServerName subdomain1.localhost.com
6 DocumentRoot /var/www/subdomain1
7
8 ErrorLog ${APACHE_LOG_DIR}/subdomain1_error.log
9 CustomLog ${APACHE_LOG_DIR}/subdomain1_access.log combined
10</VirtualHost>
11
12<VirtualHost *:443>
13 ServerName subdomain1.localhost.com
14 DocumentRoot /var/www/subdomain1
15
16 SSLEngine on
17 SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
18 SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
19
20 ErrorLog ${APACHE_LOG_DIR}/subdomain1_ssl_error.log
21 CustomLog ${APACHE_LOG_DIR}/subdomain1_ssl_access.log combined
22</VirtualHost>