diff options
Diffstat (limited to 'plugins/check_curl.d/check_curl_helpers.c')
| -rw-r--r-- | plugins/check_curl.d/check_curl_helpers.c | 455 |
1 files changed, 451 insertions, 4 deletions
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 | ||
| 563 | void handle_curl_option_return_code(CURLcode res, const char *option) { | 667 | void 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 | |||
| 1407 | int 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 | |||
| 1583 | int 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 | } | ||
