diff options
| author | Daniil Baturin <daniil@vyos.io> | 2024-01-04 13:38:37 -0500 |
|---|---|---|
| committer | Daniil Baturin <daniil@vyos.io> | 2024-01-04 13:38:37 -0500 |
| commit | 23e0eda853a9bfa42a2fa0d50b31eea874a01a9c (patch) | |
| tree | ba5e579ed670d0275fea996b0284b3a74da78f28 /src | |
| parent | 346aedbcd2257512208195bb165cc857e2907922 (diff) | |
| download | ipaddrcheck-23e0eda853a9bfa42a2fa0d50b31eea874a01a9c.tar.gz ipaddrcheck-23e0eda853a9bfa42a2fa0d50b31eea874a01a9c.zip | |
Add --range-prefix-length option
to require the range boundaries to lie within the same subnet
of a given size
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 2 | ||||
| -rw-r--r-- | src/ipaddrcheck.c | 49 | ||||
| -rw-r--r-- | src/ipaddrcheck_functions.c | 65 | ||||
| -rw-r--r-- | src/ipaddrcheck_functions.h | 4 | ||||
| -rw-r--r-- | src/ipaddrcheck_functions.o | bin | 45080 -> 55552 bytes |
5 files changed, 105 insertions, 15 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 6098ae5..1c43bae 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -AM_CFLAGS = --pedantic -Wall -Werror -std=c99 -O2 +AM_CFLAGS = --pedantic -Wall -Werror -Wno-error=format-overflow= -std=c99 -O2 AM_LDFLAGS = ipaddrcheck_SOURCES = ipaddrcheck.c ipaddrcheck_functions.c diff --git a/src/ipaddrcheck.c b/src/ipaddrcheck.c index 230c6d6..248c248 100644 --- a/src/ipaddrcheck.c +++ b/src/ipaddrcheck.c @@ -88,6 +88,7 @@ static const struct option options[] = { "is-any-net", no_argument, NULL, 'E' }, { "is-ipv4-range", no_argument, NULL, 'F' }, { "is-ipv6-range", no_argument, NULL, 'G' }, + { "range-prefix-length", required_argument, NULL, 'H' }, { "version", no_argument, NULL, 'z' }, { "help", no_argument, NULL, '?' }, { "verbose", no_argument, NULL, 'V' }, @@ -109,6 +110,7 @@ int main(int argc, char* argv[]) int optc; /* Option character for getopt call */ int allow_loopback = NO_LOOPBACK; /* Allow IPv4 loopback in --is-valid-intf-address */ + int range_prefix_length = 0; int no_action = 0; /* Indicates the option modifies program behaviour but doesn't have its own action */ @@ -124,9 +126,9 @@ int main(int argc, char* argv[]) const char* program_name = argv[0]; /* Program name for use in messages */ - /* Parse options, convert to action codes, store in array */ + /* Parse options, convert to action codes, store in an array. */ - /* Try to allocate memory for the actions array, abort if fail */ + /* Try to allocate memory for the actions array, abort if the attempt fails. */ actions = (int*)calloc(argc, sizeof(int)); if( errno == ENOMEM ) { @@ -134,7 +136,7 @@ int main(int argc, char* argv[]) return(RESULT_INT_ERROR); } - while( (optc = getopt_long(argc, argv, "acdefghijklmnoprstuzABCDEFGV?", options, &option_index)) != -1 ) + while( (optc = getopt_long(argc, argv, "acdefghijklmnoprstuzABCDEFGHV?", options, &option_index)) != -1 ) { switch(optc) { @@ -219,6 +221,26 @@ int main(int argc, char* argv[]) case 'G': ipv6_range_check = 1; break; + case 'H': + errno = 0; + char* endptr = ""; + /* Reminder to the reader on the quirks of strtol: + * errno != 0 --- internal parse error + * endptr == optarg --- no digits found in the string + * *endptr != '\0' --- extra characters after the last digit + */ + range_prefix_length = (int)strtol(optarg, &endptr, 10); + if( (errno != 0) || (endptr == optarg) || (*endptr != '\0') ) + { + fprintf(stderr, "Error: \"%s\" is not a valid prefix length\n", optarg); + return(RESULT_INT_ERROR); + } + if( (range_prefix_length < 0) || (range_prefix_length > 128) ) + { + fprintf(stderr, "Error: \"%s\" is not a valid prefix length\n", optarg); + return(RESULT_INT_ERROR); + } + break; case 'V': verbose = 1; break; @@ -268,8 +290,13 @@ int main(int argc, char* argv[]) /* If the argument is a range, use special functions that can handle it. */ if( ipv4_range_check ) { - int result = is_ipv4_range(address_str, verbose); + if( range_prefix_length > 32 ) + { + fprintf(stderr, "Error: prefix length cannot exceed 32 for IPv4!\n"); + return(RESULT_INT_ERROR); + } + int result = is_ipv4_range(address_str, range_prefix_length, verbose); if( result == RESULT_SUCCESS ) { return(EXIT_SUCCESS); @@ -282,8 +309,13 @@ int main(int argc, char* argv[]) if( ipv6_range_check ) { - int result = is_ipv6_range(address_str, verbose); + if( range_prefix_length > 128 ) + { + fprintf(stderr, "Error: prefix length cannot exceed 32 for IPv4!\n"); + return(RESULT_INT_ERROR); + } + int result = is_ipv6_range(address_str, range_prefix_length, verbose); if( result == RESULT_SUCCESS ) { return(EXIT_SUCCESS); @@ -633,8 +665,11 @@ Address checking options:\n\ --is-ipv6-range Check if STRING is a valid IPv6 address range\n\ \n\ Behavior options:\n\ - --allow-loopback When used with --is-valid-intf-address,\n\ - makes IPv4 loopback addresses pass the check\n\ + --allow-loopback When used with --is-valid-intf-address,\n\ + makes IPv4 loopback addresses pass the check\n\ + --range-prefix-length <INT> When used with --is-ipv4-range or --is-ipv6-range,\n\ + requires the range boundaries to lie within\n\ + a prefix of given length\n\ \n\ Other options:\n\ --version Print version information and exit \n\ diff --git a/src/ipaddrcheck_functions.c b/src/ipaddrcheck_functions.c index e3e2c86..2b3cc22 100644 --- a/src/ipaddrcheck_functions.c +++ b/src/ipaddrcheck_functions.c @@ -460,7 +460,7 @@ int is_any_net(CIDR *address) * This function is patently unsafe, * whether it's safe to do what it does should be determined by its callers. */ -void split_range(char* range_str, char* left, char*right) +void split_range(char* range_str, char* left, char* right) { char* ptr = left; int length = strlen(range_str); @@ -487,6 +487,9 @@ void split_range(char* range_str, char* left, char*right) return; } +/* in6_addr fields are byte arrays, so we cannot compare them as numbers + * and needs custom comparison logic + */ int compare_ipv6(struct in6_addr *left, struct in6_addr *right) { int i = 0; @@ -501,7 +504,7 @@ int compare_ipv6(struct in6_addr *left, struct in6_addr *right) } /* Is it a valid IPv4 address range? */ -int is_ipv4_range(char* range_str, int verbose) +int is_ipv4_range(char* range_str, int prefix_length, int verbose) { int result = RESULT_SUCCESS; @@ -553,7 +556,33 @@ int is_ipv4_range(char* range_str, int verbose) if( left_in_addr->s_addr <= right_in_addr->s_addr ) { - result = RESULT_SUCCESS; + /* If non-zero prefix_length is given, + check if the right address is within the network of the first one. */ + if( prefix_length > 0 ) + { + char left_pref_str[19]; + + /* XXX: Prefix length size is checked elsewhere, so it can't be more than 2 characters (32) + and overflow cannot occur. + */ + sprintf(left_pref_str, "%s/%u", left, prefix_length); + CIDR* left_addr_with_pref = cidr_from_str(left_pref_str); + CIDR* left_net = cidr_addr_network(left_addr_with_pref); + if( cidr_contains(left_net, right_addr) == 0 ) + { + result = RESULT_SUCCESS; + } + else + { + result = RESULT_FAILURE; + } + cidr_free(left_addr_with_pref); + cidr_free(left_net); + } + else + { + result = RESULT_SUCCESS; + } } else { @@ -573,7 +602,7 @@ int is_ipv4_range(char* range_str, int verbose) } /* Is it a valid IPv6 address range? */ -int is_ipv6_range(char* range_str, int verbose) +int is_ipv6_range(char* range_str, int prefix_length, int verbose) { int result = RESULT_SUCCESS; @@ -625,7 +654,33 @@ int is_ipv6_range(char* range_str, int verbose) if( compare_ipv6(left_in6_addr, right_in6_addr) <= 0 ) { - result = RESULT_SUCCESS; + /* If non-zero prefix_length is given, + check if the right address is within the network of the first one. */ + if( prefix_length > 0 ) + { + char left_pref_str[44]; + + /* XXX: Prefix length size is checked elsewhere, so it can't be more than 3 characters (128) + and overflow cannot occur. + */ + sprintf(left_pref_str, "%s/%u", left, prefix_length); + CIDR* left_addr_with_pref = cidr_from_str(left_pref_str); + CIDR* left_net = cidr_addr_network(left_addr_with_pref); + if( cidr_contains(left_net, right_addr) == 0 ) + { + result = RESULT_SUCCESS; + } + else + { + result = RESULT_FAILURE; + } + cidr_free(left_addr_with_pref); + cidr_free(left_net); + } + else + { + result = RESULT_SUCCESS; + } } else { diff --git a/src/ipaddrcheck_functions.h b/src/ipaddrcheck_functions.h index c0d8603..9b5e55f 100644 --- a/src/ipaddrcheck_functions.h +++ b/src/ipaddrcheck_functions.h @@ -76,7 +76,7 @@ int is_ipv6_link_local(CIDR *address); int is_valid_intf_address(CIDR *address, char* address_str, int allow_loopback); int is_any_host(CIDR *address); int is_any_net(CIDR *address); -int is_ipv4_range(char* range_str, int verbose); -int is_ipv6_range(char* range_str, int verbose); +int is_ipv4_range(char* range_str, int prefix_length, int verbose); +int is_ipv6_range(char* range_str, int prefix_length, int verbose); #endif /* IPADDRCHECK_FUNCTIONS_H */ diff --git a/src/ipaddrcheck_functions.o b/src/ipaddrcheck_functions.o Binary files differindex e8e714d..a689dac 100644 --- a/src/ipaddrcheck_functions.o +++ b/src/ipaddrcheck_functions.o |
