summaryrefslogtreecommitdiffstats
path: root/plugins/check_curl.d
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_curl.d')
-rw-r--r--plugins/check_curl.d/check_curl_helpers.c553
-rw-r--r--plugins/check_curl.d/check_curl_helpers.h23
-rw-r--r--plugins/check_curl.d/config.h7
3 files changed, 565 insertions, 18 deletions
diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c
index 7be781fc..80d6f4f6 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"
@@ -57,8 +60,8 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
57 result.curl_state.curl_easy_initialized = true; 60 result.curl_state.curl_easy_initialized = true;
58 61
59 if (verbose >= 1) { 62 if (verbose >= 1) {
60 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1L), 63 handle_curl_option_return_code(
61 "CURLOPT_VERBOSE"); 64 curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1L), "CURLOPT_VERBOSE");
62 } 65 }
63 66
64 /* print everything on stdout like check_http would do */ 67 /* print everything on stdout like check_http would do */
@@ -116,6 +119,112 @@ 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 */
125 /* socks4(a) / socks5(h) proxy should be given using the command line */
126
127 /* first source to check is the environment variables */
128 /* lower case proxy environment variables are almost always accepted, while some programs also
129 checking uppercase ones. discover both, but take the lowercase one if both are present */
130
131 /* extra information: libcurl does not discover the uppercase version HTTP_PROXY due to security
132 * reasons */
133 /* https://github.com/curl/curl/blob/d445f2d930ae701039518d695481ee53b8490521/lib/url.c#L1987 */
134
135 /* first environment variable to read is all_proxy. it can be overridden by protocol specific
136 * environment variables */
137 char *all_proxy_env = getenv("all_proxy");
138 char *all_proxy_uppercase_env = getenv("ALL_PROXY");
139 if (all_proxy_env != NULL && strlen(all_proxy_env)) {
140 working_state.curlopt_proxy = strdup(all_proxy_env);
141 if (all_proxy_uppercase_env != NULL && verbose >= 1) {
142 printf("* cURL ignoring environment variable 'ALL_PROXY' as 'all_proxy' is set\n");
143 }
144 } else if (all_proxy_uppercase_env != NULL && strlen(all_proxy_uppercase_env) > 0) {
145 working_state.curlopt_proxy = strdup(all_proxy_uppercase_env);
146 }
147
148 /* second environment variable to read is http_proxy. only set curlopt_proxy if ssl is not
149 * toggled */
150 char *http_proxy_env = getenv("http_proxy");
151 char *http_proxy_uppercase_env = getenv("HTTP_PROXY");
152 if (!working_state.use_ssl) {
153 if (http_proxy_env != NULL && strlen(http_proxy_env) > 0) {
154 working_state.curlopt_proxy = strdup(http_proxy_env);
155 if (http_proxy_uppercase_env != NULL && verbose >= 1) {
156 printf(
157 "* cURL ignoring environment variable 'HTTP_PROXY' as 'http_proxy' is set\n");
158 }
159 } else if (http_proxy_uppercase_env != NULL && strlen(http_proxy_uppercase_env) > 0) {
160 working_state.curlopt_proxy = strdup(http_proxy_uppercase_env);
161 }
162 }
163#ifdef LIBCURL_FEATURE_SSL
164 /* optionally read https_proxy environment variable and set curlopt_proxy if ssl is toggled */
165 char *https_proxy_env = getenv("https_proxy");
166 char *https_proxy_uppercase_env = getenv("HTTPS_PROXY");
167 if (working_state.use_ssl) {
168 if (https_proxy_env != NULL && strlen(https_proxy_env) > 0) {
169 working_state.curlopt_proxy = strdup(https_proxy_env);
170 if (https_proxy_uppercase_env != NULL && verbose >= 1) {
171 printf(
172 "* cURL ignoring environment variable 'HTTPS_PROXY' as 'https_proxy' is set\n");
173 }
174 } else if (https_proxy_uppercase_env != NULL) {
175 working_state.curlopt_proxy = strdup(https_proxy_uppercase_env);
176 }
177 }
178#endif /* LIBCURL_FEATURE_SSL */
179
180 /* second source to check for proxies is command line argument, overwriting the environment
181 * variables */
182 if (strlen(config.proxy) > 0) {
183 working_state.curlopt_proxy = strdup(config.proxy);
184 }
185
186 if (working_state.curlopt_proxy != NULL && strlen(working_state.curlopt_proxy)) {
187 handle_curl_option_return_code(
188 curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.curlopt_proxy),
189 "CURLOPT_PROXY");
190 if (verbose >= 1) {
191 printf("* curl CURLOPT_PROXY: %s\n", working_state.curlopt_proxy);
192 }
193 }
194
195 /* set no_proxy */
196 /* first source to check is environment variables */
197 char *no_proxy_env = getenv("no_proxy");
198 char *no_proxy_uppercase_env = getenv("NO_PROXY");
199 if (no_proxy_env != NULL && strlen(no_proxy_env)) {
200 working_state.curlopt_noproxy = strdup(no_proxy_env);
201 if (no_proxy_uppercase_env != NULL && verbose >= 1) {
202 printf("* cURL ignoring environment variable 'NO_PROXY' as 'no_proxy' is set\n");
203 }
204 } else if (no_proxy_uppercase_env != NULL && strlen(no_proxy_uppercase_env) > 0) {
205 working_state.curlopt_noproxy = strdup(no_proxy_uppercase_env);
206 }
207
208 /* second source to check for no_proxy is command line argument, overwriting the environment
209 * variables */
210 if (strlen(config.no_proxy) > 0) {
211 working_state.curlopt_noproxy = strdup(config.no_proxy);
212 }
213
214 if (working_state.curlopt_noproxy != NULL && strlen(working_state.curlopt_noproxy)) {
215 handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOPROXY,
216 working_state.curlopt_noproxy),
217 "CURLOPT_NOPROXY");
218 if (verbose >= 1) {
219 printf("* curl CURLOPT_NOPROXY: %s\n", working_state.curlopt_noproxy);
220 }
221 }
222
223 bool have_local_resolution = hostname_gets_resolved_locally(working_state);
224 if (verbose >= 1) {
225 printf("* have local name resolution: %s\n", (have_local_resolution ? "true": "false"));
226 }
227
119 /* enable haproxy protocol */ 228 /* enable haproxy protocol */
120 if (config.haproxy_protocol) { 229 if (config.haproxy_protocol) {
121 handle_curl_option_return_code( 230 handle_curl_option_return_code(
@@ -123,11 +232,11 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
123 "CURLOPT_HAPROXYPROTOCOL"); 232 "CURLOPT_HAPROXYPROTOCOL");
124 } 233 }
125 234
126 // fill dns resolve cache to make curl connect to the given server_address instead of the 235 /* 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 236 /* host_name, only required for ssl, because we use the host_name later on to make SNI happy */
128 char dnscache[DEFAULT_BUFFER_SIZE]; 237 char dnscache[DEFAULT_BUFFER_SIZE];
129 char addrstr[DEFAULT_BUFFER_SIZE / 2]; 238 char addrstr[DEFAULT_BUFFER_SIZE / 2];
130 if (working_state.use_ssl && working_state.host_name != NULL) { 239 if (working_state.use_ssl && working_state.host_name != NULL && !have_local_resolution) {
131 char *tmp_mod_address; 240 char *tmp_mod_address;
132 241
133 /* lookup_host() requires an IPv6 address without the brackets. */ 242 /* lookup_host() requires an IPv6 address without the brackets. */
@@ -330,11 +439,11 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
330 case CURLHELP_SSL_LIBRARY_LIBRESSL: 439 case CURLHELP_SSL_LIBRARY_LIBRESSL:
331 /* set callback to extract certificate with OpenSSL context function (works with 440 /* set callback to extract certificate with OpenSSL context function (works with
332 * OpenSSL-style libraries only!) */ 441 * OpenSSL-style libraries only!) */
333# ifdef USE_OPENSSL 442# ifdef MOPL_USE_OPENSSL
334 /* libcurl and monitoring plugins built with OpenSSL, good */ 443 /* libcurl and monitoring plugins built with OpenSSL, good */
335 add_sslctx_verify_fun = true; 444 add_sslctx_verify_fun = true;
336 is_openssl_callback = true; 445 is_openssl_callback = true;
337# endif /* USE_OPENSSL */ 446# endif /* MOPL_USE_OPENSSL */
338 /* libcurl is built with OpenSSL, monitoring plugins, so falling 447 /* libcurl is built with OpenSSL, monitoring plugins, so falling
339 * back to manually extracting certificate information */ 448 * back to manually extracting certificate information */
340 handle_curl_option_return_code( 449 handle_curl_option_return_code(
@@ -488,7 +597,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
488 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), 597 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4),
489 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); 598 "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)");
490 } 599 }
491#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) 600#if defined(LIBCURL_FEATURE_IPV6)
492 else if (config.sin_family == AF_INET6) { 601 else if (config.sin_family == AF_INET6) {
493 handle_curl_option_return_code( 602 handle_curl_option_return_code(
494 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), 603 curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6),
@@ -562,7 +671,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config,
562 671
563void handle_curl_option_return_code(CURLcode res, const char *option) { 672void handle_curl_option_return_code(CURLcode res, const char *option) {
564 if (res != CURLE_OK) { 673 if (res != CURLE_OK) {
565 die(STATE_CRITICAL, _("Error while setting cURL option '%s': cURL returned %d - %s"), 674 die(STATE_CRITICAL, _("Error while setting cURL option '%s': cURL returned %d - %s\n"),
566 option, res, curl_easy_strerror(res)); 675 option, res, curl_easy_strerror(res));
567 } 676 }
568} 677}
@@ -578,7 +687,7 @@ char *get_header_value(const struct phr_header *headers, const size_t nof_header
578 return NULL; 687 return NULL;
579} 688}
580 689
581check_curl_working_state check_curl_working_state_init() { 690check_curl_working_state check_curl_working_state_init(void) {
582 check_curl_working_state result = { 691 check_curl_working_state result = {
583 .server_address = NULL, 692 .server_address = NULL,
584 .server_url = DEFAULT_SERVER_URL, 693 .server_url = DEFAULT_SERVER_URL,
@@ -589,11 +698,13 @@ check_curl_working_state check_curl_working_state_init() {
589 .serverPort = HTTP_PORT, 698 .serverPort = HTTP_PORT,
590 .use_ssl = false, 699 .use_ssl = false,
591 .no_body = false, 700 .no_body = false,
701 .curlopt_proxy = NULL,
702 .curlopt_noproxy = NULL,
592 }; 703 };
593 return result; 704 return result;
594} 705}
595 706
596check_curl_config check_curl_config_init() { 707check_curl_config check_curl_config_init(void) {
597 check_curl_config tmp = { 708 check_curl_config tmp = {
598 .initial_config = check_curl_working_state_init(), 709 .initial_config = check_curl_working_state_init(),
599 710
@@ -612,6 +723,8 @@ check_curl_config check_curl_config_init() {
612 .ca_cert = NULL, 723 .ca_cert = NULL,
613 .verify_peer_and_host = false, 724 .verify_peer_and_host = false,
614 .user_agent = {'\0'}, 725 .user_agent = {'\0'},
726 .proxy = "",
727 .no_proxy = "",
615 .proxy_auth = "", 728 .proxy_auth = "",
616 .user_auth = "", 729 .user_auth = "",
617 .http_content_type = NULL, 730 .http_content_type = NULL,
@@ -1199,24 +1312,24 @@ mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_
1199 1312
1200#ifdef LIBCURL_FEATURE_SSL 1313#ifdef LIBCURL_FEATURE_SSL
1201 if (is_openssl_callback) { 1314 if (is_openssl_callback) {
1202# ifdef USE_OPENSSL 1315# ifdef MOPL_USE_OPENSSL
1203 /* check certificate with OpenSSL functions, curl has been built against OpenSSL 1316 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
1204 * and we actually have OpenSSL in the monitoring tools 1317 * and we actually have OpenSSL in the monitoring tools
1205 */ 1318 */
1206 return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp); 1319 return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp);
1207# else /* USE_OPENSSL */ 1320# else /* MOPL_USE_OPENSSL */
1208 xasprintf(&result.output, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL " 1321 xasprintf(&result.output, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL "
1209 "callback used and not linked against OpenSSL\n"); 1322 "callback used and not linked against OpenSSL\n");
1210 mp_set_subcheck_state(result, STATE_CRITICAL); 1323 mp_set_subcheck_state(result, STATE_CRITICAL);
1211# endif /* USE_OPENSSL */ 1324# endif /* MOPL_USE_OPENSSL */
1212 } else { 1325 } else {
1213 struct curl_slist *slist; 1326 struct curl_slist *slist;
1214 1327
1215 cert_ptr_union cert_ptr = {0}; 1328 cert_ptr_union cert_ptr = {0};
1216 cert_ptr.to_info = NULL; 1329 cert_ptr.to_info = NULL;
1217 CURLcode res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); 1330 CURLcode res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_certinfo);
1218 if (!res && cert_ptr.to_info) { 1331 if (!res && cert_ptr.to_info) {
1219# ifdef USE_OPENSSL 1332# ifdef MOPL_USE_OPENSSL
1220 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert 1333 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert
1221 * parsing We only check the first certificate and assume it's the one of 1334 * parsing We only check the first certificate and assume it's the one of
1222 * the server 1335 * the server
@@ -1262,13 +1375,13 @@ mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_
1262 1375
1263 BIO_free(cert_BIO); 1376 BIO_free(cert_BIO);
1264 return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp); 1377 return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp);
1265# else /* USE_OPENSSL */ 1378# else /* MOPL_USE_OPENSSL */
1266 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our 1379 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our
1267 * disposal, so we use the libcurl CURLINFO data 1380 * disposal, so we use the libcurl CURLINFO data
1268 */ 1381 */
1269 return net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, 1382 return net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn,
1270 days_till_exp_crit); 1383 days_till_exp_crit);
1271# endif /* USE_OPENSSL */ 1384# endif /* MOPL_USE_OPENSSL */
1272 } else { 1385 } else {
1273 xasprintf(&sc_cert_result.output, 1386 xasprintf(&sc_cert_result.output,
1274 _("Cannot retrieve certificates - cURL returned %d - %s"), res, 1387 _("Cannot retrieve certificates - cURL returned %d - %s"), res,
@@ -1295,3 +1408,407 @@ char *fmt_url(check_curl_working_state workingState) {
1295 1408
1296 return url; 1409 return url;
1297} 1410}
1411
1412bool hostname_gets_resolved_locally(const check_curl_working_state working_state) {
1413 char *host_name_display = "NULL";
1414 unsigned long host_name_len = 0;
1415 if (working_state.host_name) {
1416 host_name_len = strlen(working_state.host_name);
1417 host_name_display = working_state.host_name;
1418 }
1419
1420 /* IPv4 or IPv6 version of the address */
1421 char *server_address_clean = strdup(working_state.server_address);
1422 /* server address might be a full length ipv6 address encapsulated in square brackets */
1423 if ((strnlen(working_state.server_address, MAX_IPV4_HOSTLENGTH) > 2) &&
1424 (working_state.server_address[0] == '[') &&
1425 (working_state.server_address[strlen(working_state.server_address) - 1] == ']')) {
1426 server_address_clean =
1427 strndup(working_state.server_address + 1, strlen(working_state.server_address) - 2);
1428 }
1429
1430 /* check curlopt_noproxy option first */
1431 /* https://curl.se/libcurl/c/CURLOPT_NOPROXY.html */
1432
1433 /* curlopt_noproxy is specified as a comma separated list of
1434 direct IPv4 or IPv6 addresses e.g 130.133.8.40, 2001:4860:4802:32::a ,
1435 IPv4 or IPv6 CIDR regions e.g 10.241.0.0/16 , abcd:ef01:2345::/48 ,
1436 direct hostnames e.g example.com, google.de */
1437
1438 if (working_state.curlopt_noproxy != NULL) {
1439 char *curlopt_noproxy_copy = strdup(working_state.curlopt_noproxy);
1440 char *noproxy_item = strtok(curlopt_noproxy_copy, ",");
1441 while (noproxy_item != NULL) {
1442 unsigned long noproxy_item_len = strlen(noproxy_item);
1443
1444 /* According to the CURLOPT_NOPROXY documentation: */
1445 /* https://curl.se/libcurl/c/CURLOPT_NOPROXY.html */
1446 /* The only wildcard available is a single * character, which matches all hosts, and
1447 * effectively disables the proxy. */
1448 if (strlen(noproxy_item) == 1 && noproxy_item[0] == '*') {
1449 if (verbose >= 1) {
1450 printf("* noproxy includes '*' which disables proxy for all host name incl. : "
1451 "%s / server address incl. : %s\n",
1452 host_name_display, server_address_clean);
1453 }
1454 free(curlopt_noproxy_copy);
1455 free(server_address_clean);
1456 return true;
1457 }
1458
1459 /* direct comparison with the server_address */
1460 if (server_address_clean != NULL &&
1461 strlen(server_address_clean) == strlen(noproxy_item) &&
1462 strcmp(server_address_clean, noproxy_item) == 0) {
1463 if (verbose >= 1) {
1464 printf("* server_address is in the no_proxy list: %s\n", noproxy_item);
1465 }
1466 free(curlopt_noproxy_copy);
1467 free(server_address_clean);
1468 return true;
1469 }
1470
1471 /* direct comparison with the host_name */
1472 if (working_state.host_name != NULL && host_name_len == noproxy_item_len &&
1473 strcmp(working_state.host_name, noproxy_item) == 0) {
1474 if (verbose >= 1) {
1475 printf("* host_name is in the no_proxy list: %s\n", noproxy_item);
1476 }
1477 free(curlopt_noproxy_copy);
1478 free(server_address_clean);
1479 return true;
1480 }
1481
1482 /* check if hostname is a subdomain of the item, e.g www.example.com when token is
1483 * example.com */
1484 /* subdomain1.acme.com will not will use a proxy if you only specify 'acme' in the
1485 * noproxy */
1486 /* check if noproxy_item is a suffix */
1487 /* check if the character just before the suffix is '.' */
1488 if (working_state.host_name != NULL && host_name_len > noproxy_item_len) {
1489 unsigned long suffix_start_idx = host_name_len - noproxy_item_len;
1490 if (strcmp(working_state.host_name + suffix_start_idx, noproxy_item) == 0 &&
1491 working_state.host_name[suffix_start_idx - 1] == '.') {
1492 if (verbose >= 1) {
1493 printf("* host_name: %s is a subdomain of the no_proxy list item: %s\n",
1494 working_state.host_name, noproxy_item);
1495 }
1496 free(curlopt_noproxy_copy);
1497 free(server_address_clean);
1498 return true;
1499 }
1500 }
1501
1502 // noproxy_item could be a CIDR IP range
1503 if (server_address_clean != NULL && strlen(server_address_clean)) {
1504 ip_addr_inside ip_addr_inside_cidr_ret =
1505 ip_addr_inside_cidr(noproxy_item, server_address_clean);
1506
1507 if (ip_addr_inside_cidr_ret.error == NO_ERROR) {
1508 if (ip_addr_inside_cidr_ret.inside) {
1509 return true;
1510 } else {
1511 if (verbose >= 1) {
1512 printf("server address: %s is not inside IP cidr: %s\n",
1513 server_address_clean, noproxy_item);
1514 }
1515 }
1516 } else {
1517 if (verbose >= 1) {
1518 printf("could not fully determine if server address: %s is inside the IP "
1519 "cidr: %s\n",
1520 server_address_clean, noproxy_item);
1521 }
1522 }
1523 }
1524
1525 noproxy_item = strtok(NULL, ",");
1526 }
1527
1528 free(curlopt_noproxy_copy);
1529 }
1530
1531 if (working_state.curlopt_proxy != NULL) {
1532 // Libcurl documentation
1533 // Setting the proxy string to "" (an empty string) explicitly disables the use of a proxy,
1534 // even if there is an environment variable set for it.
1535 if (strlen(working_state.curlopt_proxy) == 0) {
1536 return true;
1537 }
1538
1539 if (strncmp(working_state.curlopt_proxy, "http://", 7) == 0) {
1540 if (verbose >= 1) {
1541 printf(
1542 "* proxy scheme is http, proxy: %s resolves host: %s or server_address: %s\n",
1543 working_state.curlopt_proxy, host_name_display, server_address_clean);
1544 }
1545 free(server_address_clean);
1546 return false;
1547 }
1548
1549 if (strncmp(working_state.curlopt_proxy, "https://", 8) == 0) {
1550 if (verbose >= 1) {
1551 printf(
1552 "* proxy scheme is https, proxy: %s resolves host: %s or server_address: %s\n",
1553 working_state.curlopt_proxy, host_name_display, server_address_clean);
1554 }
1555 free(server_address_clean);
1556 return false;
1557 }
1558
1559 if (strncmp(working_state.curlopt_proxy, "socks4://", 9) == 0) {
1560 if (verbose >= 1) {
1561 printf("* proxy scheme is socks, proxy: %s does not resolve host: %s or "
1562 "server_address: %s\n",
1563 working_state.curlopt_proxy, host_name_display, server_address_clean);
1564 }
1565 free(server_address_clean);
1566 return true;
1567 }
1568
1569 if (strncmp(working_state.curlopt_proxy, "socks4a://", 10) == 0) {
1570 if (verbose >= 1) {
1571 printf("* proxy scheme is socks4a, proxy: %s resolves host: %s or server_address: "
1572 "%s\n",
1573 working_state.curlopt_proxy, host_name_display, server_address_clean);
1574 }
1575 free(server_address_clean);
1576 return false;
1577 }
1578
1579 if (strncmp(working_state.curlopt_proxy, "socks5://", 9) == 0) {
1580 if (verbose >= 1) {
1581 printf("* proxy scheme is socks5, proxy: %s does not resolve host: %s or "
1582 "server_address: %s\n",
1583 working_state.curlopt_proxy, host_name_display, server_address_clean);
1584 }
1585 free(server_address_clean);
1586 return true;
1587 }
1588
1589 if (strncmp(working_state.curlopt_proxy, "socks5h://", 10) == 0) {
1590 if (verbose >= 1) {
1591 printf("* proxy scheme is socks5h, proxy: %s resolves host: %s or server_address: "
1592 "%s\n",
1593 working_state.curlopt_proxy, host_name_display, server_address_clean);
1594 }
1595 free(server_address_clean);
1596 return false;
1597 }
1598
1599 // Libcurl documentation:
1600 // Without a scheme prefix, CURLOPT_PROXYTYPE can be used to specify which kind of proxy the
1601 // string identifies. We do not set this value Without a scheme, it is treated as an http
1602 // proxy
1603
1604 return false;
1605 }
1606
1607 if (verbose >= 1) {
1608 printf("* proxy scheme is unknown/unavailable, no proxy is assumed for host: %s or "
1609 "server_address: %s\n",
1610 host_name_display, server_address_clean);
1611 }
1612
1613 free(server_address_clean);
1614 return 0;
1615}
1616
1617ip_addr_inside ip_addr_inside_cidr(const char *cidr_region_or_ip_addr, const char *target_ip) {
1618 unsigned int slash_count = 0;
1619 unsigned int last_slash_idx = 0;
1620 for (size_t i = 0; i < strlen(cidr_region_or_ip_addr); i++) {
1621 if (cidr_region_or_ip_addr[i] == '/') {
1622 slash_count++;
1623 last_slash_idx = (unsigned int)i;
1624 }
1625 }
1626
1627 char *cidr_ip_part = NULL;
1628 int prefix_length = 0;
1629 ip_addr_inside result = {
1630 .inside = false,
1631 .error = NO_ERROR,
1632 };
1633
1634 if (slash_count == 0) {
1635 cidr_ip_part = strdup(cidr_region_or_ip_addr);
1636 if (!cidr_ip_part) {
1637 result.error = FAILED_STRDUP;
1638 return result;
1639 }
1640 } else if (slash_count == 1) {
1641 cidr_ip_part = strndup(cidr_region_or_ip_addr, last_slash_idx);
1642 if (!cidr_ip_part) {
1643 result.error = FAILED_STRDUP;
1644 return result;
1645 }
1646
1647 errno = 0;
1648 long long tmp = strtoll(cidr_region_or_ip_addr + last_slash_idx + 1, NULL, 10);
1649 if (errno == ERANGE) {
1650 if (verbose >= 1) {
1651 printf("cidr_region_or_ip: %s , could not parse subnet length\n",
1652 cidr_region_or_ip_addr);
1653 }
1654 free(cidr_ip_part);
1655 result.error = COULD_NOT_PARSE_SUBNET_LENGTH;
1656 return result;
1657 }
1658 prefix_length = (int)tmp;
1659 } else {
1660 if (verbose >= 1) {
1661 printf("cidr_region_or_ip: %s , has %d number of '/' characters, is not a valid "
1662 "cidr_region or IP\n",
1663 cidr_region_or_ip_addr, slash_count);
1664 }
1665 result.error = CIDR_REGION_INVALID;
1666 return result;
1667 }
1668
1669 int cidr_addr_family, target_addr_family;
1670 if (strchr(cidr_ip_part, ':')) {
1671 cidr_addr_family = AF_INET6;
1672 } else {
1673 cidr_addr_family = AF_INET;
1674 }
1675
1676 if (strchr(target_ip, ':')) {
1677 target_addr_family = AF_INET6;
1678 } else {
1679 target_addr_family = AF_INET;
1680 }
1681
1682 if (cidr_addr_family != target_addr_family) {
1683 if (verbose >= 1) {
1684 printf("cidr address: %s and target ip address: %s have different address families\n",
1685 cidr_ip_part, target_ip);
1686 }
1687 free(cidr_ip_part);
1688 result.inside = false;
1689 return result;
1690 }
1691
1692 // If no prefix is given, treat the cidr as a single address (full-length prefix)
1693 if (slash_count == 0) {
1694 prefix_length = (cidr_addr_family == AF_INET) ? 32 : 128;
1695 }
1696
1697 int max_bits = (cidr_addr_family == AF_INET) ? 32u : 128u;
1698 if (prefix_length < 0 || prefix_length > max_bits) {
1699 if (verbose >= 1) {
1700 printf("cidr_region_or_ip: %s has invalid prefix length: %u\n", cidr_region_or_ip_addr,
1701 prefix_length);
1702 }
1703 free(cidr_ip_part);
1704 result.error = CIDR_REGION_INVALID_PREFIX;
1705 return result;
1706 }
1707
1708 if (verbose >= 1) {
1709 printf("cidr_region_or_ip: %s , has prefix length: %u\n", cidr_region_or_ip_addr,
1710 prefix_length);
1711 }
1712
1713 int inet_pton_rc;
1714 uint8_t *cidr_bytes = NULL;
1715 uint8_t *target_bytes = NULL;
1716 uint8_t cidr_buf[16];
1717 uint8_t target_buf[16];
1718
1719 if (cidr_addr_family == AF_INET) {
1720 struct in_addr cidr_ipv4;
1721 struct in_addr target_ipv4;
1722 inet_pton_rc = inet_pton(AF_INET, cidr_ip_part, &cidr_ipv4);
1723 if (inet_pton_rc != 1) {
1724 if (verbose >= 1) {
1725 printf("ip string: %s contains characters not valid for its address family: IPv4\n",
1726 cidr_ip_part);
1727 }
1728 free(cidr_ip_part);
1729 result.error = IP_CONTAINS_INVALID_CHARACTERS;
1730 return result;
1731 }
1732 inet_pton_rc = inet_pton(AF_INET, target_ip, &target_ipv4);
1733 if (inet_pton_rc != 1) {
1734 if (verbose >= 1) {
1735 printf("ip string: %s contains characters not valid for its address family: IPv4\n",
1736 target_ip);
1737 }
1738 free(cidr_ip_part);
1739 result.error = IP_CONTAINS_INVALID_CHARACTERS;
1740 return result;
1741 }
1742 // copy the addresses in network byte order to a buffer for comparison
1743 memcpy(cidr_buf, &cidr_ipv4.s_addr, 4);
1744 memcpy(target_buf, &target_ipv4.s_addr, 4);
1745 cidr_bytes = cidr_buf;
1746 target_bytes = target_buf;
1747 } else {
1748 struct in6_addr cidr_ipv6;
1749 struct in6_addr target_ipv6;
1750 inet_pton_rc = inet_pton(AF_INET6, cidr_ip_part, &cidr_ipv6);
1751 if (inet_pton_rc != 1) {
1752 if (verbose >= 1) {
1753 printf("ip string: %s contains characters not valid for its address family: IPv6\n",
1754 cidr_ip_part);
1755 }
1756 free(cidr_ip_part);
1757 result.error = IP_CONTAINS_INVALID_CHARACTERS;
1758 return result;
1759 }
1760 inet_pton_rc = inet_pton(AF_INET6, target_ip, &target_ipv6);
1761 if (inet_pton_rc != 1) {
1762 if (verbose >= 1) {
1763 printf("ip string: %s contains characters not valid for its address family: IPv6\n",
1764 target_ip);
1765 }
1766 free(cidr_ip_part);
1767 result.error = IP_CONTAINS_INVALID_CHARACTERS;
1768 return result;
1769 }
1770 memcpy(cidr_buf, &cidr_ipv6, 16);
1771 memcpy(target_buf, &target_ipv6, 16);
1772 cidr_bytes = cidr_buf;
1773 target_bytes = target_buf;
1774 }
1775
1776 int prefix_bytes = prefix_length / 8;
1777 int prefix_bits = prefix_length % 8;
1778
1779 if (prefix_bytes > 0) {
1780 if (memcmp(cidr_bytes, target_bytes, (size_t)prefix_bytes) != 0) {
1781 if (verbose >= 1) {
1782 printf("the first %d bytes of the cidr_region_or_ip: %s and target_ip: %s are "
1783 "different\n",
1784 prefix_bytes, cidr_ip_part, target_ip);
1785 }
1786 free(cidr_ip_part);
1787 result.inside = false;
1788 return result;
1789 }
1790 }
1791
1792 if (prefix_bits != 0) {
1793 uint8_t cidr_oct = cidr_bytes[prefix_bytes];
1794 uint8_t target_oct = target_bytes[prefix_bytes];
1795 // the mask has first prefix_bits bits 1, the rest as 0
1796 uint8_t mask = (uint8_t)(0xFFu << (8 - prefix_bits));
1797 if ((cidr_oct & mask) != (target_oct & mask)) {
1798 if (verbose >= 1) {
1799 printf("looking at the last %d bits of the prefix, cidr_region_or_ip(%s) byte is: "
1800 "%u and target_ip byte(%s) is: %u, applying bitmask: %02X returns different "
1801 "results\n",
1802 prefix_bits, cidr_ip_part, (unsigned)cidr_oct, target_ip,
1803 (unsigned)target_oct, mask);
1804 }
1805 free(cidr_ip_part);
1806 result.inside = false;
1807 return result;
1808 }
1809 }
1810
1811 free(cidr_ip_part);
1812 result.inside = true;
1813 return result;
1814}
diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h
index e77b763b..55df9bc1 100644
--- a/plugins/check_curl.d/check_curl_helpers.h
+++ b/plugins/check_curl.d/check_curl_helpers.h
@@ -126,3 +126,26 @@ 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/* determine_hostname_resolver determines if the host or the proxy resolves the target hostname
131returns RESOLVE_LOCALLY if requester resolves the hostname locally, RESOLVE_REMOTELY if proxy
132resolves the hostname */
133bool hostname_gets_resolved_locally(const check_curl_working_state working_state);
134
135/* Checks if an IP is inside given CIDR region. Using /protocol_size or not specifying the prefix
136length performs an equality check. Supports both IPv4 and IPv6 returns 1 if the target_ip address is
137inside the given cidr_region_or_ip_addr, 0 if its out. return codes < 0 mean an error has occurred.
138*/
139typedef enum {
140 NO_ERROR,
141 FAILED_STRDUP,
142 COULD_NOT_PARSE_SUBNET_LENGTH,
143 CIDR_REGION_INVALID,
144 CIDR_REGION_INVALID_PREFIX,
145 IP_CONTAINS_INVALID_CHARACTERS,
146} ip_addr_inside_error_code;
147typedef struct {
148 bool inside;
149 ip_addr_inside_error_code error;
150} ip_addr_inside;
151ip_addr_inside 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];