From 23e0eda853a9bfa42a2fa0d50b31eea874a01a9c Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Thu, 4 Jan 2024 13:38:37 -0500 Subject: Add --range-prefix-length option to require the range boundaries to lie within the same subnet of a given size --- src/ipaddrcheck_functions.c | 65 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) (limited to 'src/ipaddrcheck_functions.c') 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 { -- cgit v1.2.3