summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniil Baturin <daniil@vyos.io>2022-09-06 18:19:01 +0100
committerGitHub <noreply@github.com>2022-09-06 18:19:01 +0100
commit6fd22cc8000e4e5c034617cd02cbd432d673c446 (patch)
tree6a83556b4eb30be9274661a3f94211c6f0eed699
parentf84754f9b92bd136ae6fa01b80608d44a13bfff7 (diff)
downloadipaddrcheck-6fd22cc8000e4e5c034617cd02cbd432d673c446.tar.gz
ipaddrcheck-6fd22cc8000e4e5c034617cd02cbd432d673c446.zip
T4472: add an option for verifying IPv4 ranges (#6)
-rw-r--r--src/ipaddrcheck.c30
-rw-r--r--src/ipaddrcheck_functions.c208
-rw-r--r--src/ipaddrcheck_functions.h1
-rw-r--r--src/ipaddrcheck_functions.obin0 -> 45080 bytes
-rw-r--r--tests/check_ipaddrcheck.c9
-rwxr-xr-xtests/integration_tests.sh23
6 files changed, 175 insertions, 96 deletions
diff --git a/src/ipaddrcheck.c b/src/ipaddrcheck.c
index 92250d8..8e9c782 100644
--- a/src/ipaddrcheck.c
+++ b/src/ipaddrcheck.c
@@ -49,6 +49,8 @@
#define ALLOW_LOOPBACK 250
#define IS_ANY_HOST 260
#define IS_ANY_NET 270
+#define IS_IPV4_RANGE 280
+#define IS_IPV6_RANGE 290
#define NO_ACTION 500
static const struct option options[] =
@@ -77,6 +79,8 @@ static const struct option options[] =
{ "allow-loopback", no_argument, NULL, 'C' },
{ "is-any-host", no_argument, NULL, 'D' },
{ "is-any-net", no_argument, NULL, 'E' },
+ { "is-ipv4-range", no_argument, NULL, 'F' },
+ { "is-ipv6-range", no_argument, NULL, 'G' },
{ "version", no_argument, NULL, 'z' },
{ "help", no_argument, NULL, '?' },
{ "verbose", no_argument, NULL, 'V' },
@@ -102,6 +106,9 @@ int main(int argc, char* argv[])
int no_action = 0; /* Indicates the option modifies program behaviour
but doesn't have its own action */
+ int ipv4_range_check = 0; /* Disabled quick validity check for the argment string,
+ since it needs to be split into components first. */
+
int verbose = 0;
const char* program_name = argv[0]; /* Program name for use in messages */
@@ -117,7 +124,7 @@ int main(int argc, char* argv[])
return(RESULT_INT_ERROR);
}
- while( (optc = getopt_long(argc, argv, "acdefghijklmnoprstuzABCDEV?", options, &option_index)) != -1 )
+ while( (optc = getopt_long(argc, argv, "acdefghijklmnoprstuzABCDEFGV?", options, &option_index)) != -1 )
{
switch(optc)
{
@@ -196,6 +203,12 @@ int main(int argc, char* argv[])
case 'E':
action = IS_ANY_NET;
break;
+ case 'F':
+ ipv4_range_check = 1;
+ break;
+ case 'G':
+ action = IS_IPV6_RANGE;
+ break;
case 'V':
verbose = 1;
break;
@@ -242,8 +255,23 @@ int main(int argc, char* argv[])
return(RESULT_INT_ERROR);
}
+ if( ipv4_range_check )
+ {
+ int result = is_ipv4_range(address_str, verbose);
+
+ if( result == RESULT_SUCCESS )
+ {
+ return(EXIT_SUCCESS);
+ }
+ else
+ {
+ return(EXIT_FAILURE);
+ }
+ }
+
CIDR *address;
address = cidr_from_str(address_str);
+
int result = RESULT_SUCCESS;
/* Check if the address is valid and well-formatted at all,
diff --git a/src/ipaddrcheck_functions.c b/src/ipaddrcheck_functions.c
index d98ceda..269981f 100644
--- a/src/ipaddrcheck_functions.c
+++ b/src/ipaddrcheck_functions.c
@@ -20,6 +20,9 @@
*
*/
+#include <netinet/in.h>
+#include <assert.h>
+
#include "ipaddrcheck_functions.h"
/*
@@ -36,134 +39,59 @@
* the format was.
*/
-
-/* Does it contain double colons? This is not allowed in IPv6 addresses */
-int duplicate_double_colons(char* address_str) {
+int regex_matches(const char* regex, const char* str)
+{
int offsets[1];
pcre *re;
int rc;
const char *error;
int erroffset;
- re = pcre_compile(".*(::).*\\1", 0, &error, &erroffset, NULL);
- rc = pcre_exec(re, NULL, address_str, strlen(address_str), 0, 0, offsets, 1);
+ re = pcre_compile(regex, 0, &error, &erroffset, NULL);
+ assert(re != NULL);
+
+ rc = pcre_exec(re, NULL, str, strlen(str), 0, 0, offsets, 1);
if( rc >= 0)
{
- return(1);
+ return RESULT_SUCCESS;
}
else
{
- return(0);
+ return RESULT_FAILURE;
}
}
+
+/* Does it contain double colons? This is not allowed in IPv6 addresses */
+int duplicate_double_colons(char* address_str) {
+ return regex_matches(".*(::).*\\1", address_str);
+}
+
/* Does it look like IPv4 CIDR (e.g. 192.0.2.1/24)? */
int is_ipv4_cidr(char* address_str)
{
- int result;
-
- int offsets[1];
- pcre *re;
- int rc;
- const char *error;
- int erroffset;
-
- re = pcre_compile("^((([1-9]\\d{0,2}|0)\\.){3}([1-9]\\d{0,2}|0)\\/([1-9]\\d*|0))$",
- 0, &error, &erroffset, NULL);
- rc = pcre_exec(re, NULL, address_str, strlen(address_str), 0, 0, offsets, 1);
-
- if( rc < 0 )
- {
- result = RESULT_FAILURE;
- }
- else
- {
- result = RESULT_SUCCESS;
- }
-
- return(result);
+ return regex_matches("^((([1-9]\\d{0,2}|0)\\.){3}([1-9]\\d{0,2}|0)\\/([1-9]\\d*|0))$",
+ address_str);
}
/* Is it a single dotted decimal address? */
int is_ipv4_single(char* address_str)
{
- int result;
-
- int offsets[1];
- pcre *re;
- int rc;
- const char *error;
- int erroffset;
-
- re = pcre_compile("^((([1-9]\\d{0,2}|0)\\.){3}([1-9]\\d{0,2}|0))$",
- 0, &error, &erroffset, NULL);
- rc = pcre_exec(re, NULL, address_str, strlen(address_str), 0, 0, offsets, 1);
-
- if( rc < 0 )
- {
- result = RESULT_FAILURE;
- }
- else
- {
- result = RESULT_SUCCESS;
- }
-
- return(result);
+ return regex_matches("^((([1-9]\\d{0,2}|0)\\.){3}([1-9]\\d{0,2}|0))$",
+ address_str);
}
/* Is it an IPv6 address with prefix length? */
int is_ipv6_cidr(char* address_str)
{
- int result;
-
- int offsets[1];
- pcre *re;
- int rc;
- const char *error;
- int erroffset;
-
- re = pcre_compile("^((([0-9a-fA-F\\:])+)(\\/\\d{1,3}))$",
- 0, &error, &erroffset, NULL);
- rc = pcre_exec(re, NULL, address_str, strlen(address_str), 0, 0, offsets, 1);
-
- if( rc < 0 )
- {
- result = RESULT_FAILURE;
- }
- else
- {
- result = RESULT_SUCCESS;
- }
-
- return(result);
+ return regex_matches("^((([0-9a-fA-F\\:])+)(\\/\\d{1,3}))$", address_str);
}
/* Is it a single IPv6 address? */
int is_ipv6_single(char* address_str)
{
- int result;
-
- int offsets[1];
- pcre *re;
- int rc;
- const char *error;
- int erroffset;
-
- re = pcre_compile("^(([0-9a-fA-F\\:])+)$",
- 0, &error, &erroffset, NULL);
- rc = pcre_exec(re, NULL, address_str, strlen(address_str), 0, 0, offsets, 1);
-
- if( rc < 0 )
- {
- result = RESULT_FAILURE;
- }
- else
- {
- result = RESULT_SUCCESS;
- }
-
- return(result);
+ return regex_matches("^(([0-9a-fA-F\\:])+)$", address_str);
}
/* Is it a CIDR-formatted IPv4 or IPv6 address? */
@@ -527,3 +455,93 @@ int is_any_net(CIDR *address)
}
+int is_ipv4_range(char* range_str, int verbose)
+{
+ int result = RESULT_SUCCESS;
+
+ int regex_check_res = regex_matches("^([0-9\\.]+\\-[0-9\\.]+)$", range_str);
+
+ if( !regex_check_res )
+ {
+ if( verbose )
+ {
+ fprintf(stderr, "Malformed range %s: must be a pair of hyphen-separated IPv4 addresses\n", range_str);
+ }
+ result = RESULT_FAILURE;
+ }
+ else
+ {
+ /* Extract sub-components from the range string. */
+
+ /* Alocate memory for the components.
+ We know that an IPv4 address is always 15 characters or less, plus a null byte. */
+ char left[16];
+ char right[16];
+
+ /* Split the string at the hyphen.
+ If the regex check succeeded, we know the hyphen is there. */
+ char* ptr = left;
+ int length = strlen(range_str);
+ int pos = 0;
+ int index = 0;
+ while(pos < length)
+ {
+ if( range_str[pos] == '-' )
+ {
+ ptr[index] = '\0';
+ ptr = right;
+ index = 0;
+ }
+ else
+ {
+ ptr[index] = range_str[pos];
+ index++;
+ }
+
+ pos++;
+ }
+ ptr[index] = '\0';
+
+ if( !is_ipv4_single(left) )
+ {
+ if( verbose )
+ {
+ fprintf(stderr, "Malformed range %s: %s is not a valid IPv4 address\n", range_str, left);
+ }
+ result = RESULT_FAILURE;
+ }
+ else if( !is_ipv4_single(right) )
+ {
+ if( verbose )
+ {
+ fprintf(stderr, "Malformed range %s: %s is not a valid IPv4 address\n", range_str, right);
+ }
+ result = RESULT_FAILURE;
+ }
+ else
+ {
+ CIDR* left_addr = cidr_from_str(left);
+ CIDR* right_addr = cidr_from_str(right);
+ struct in_addr* left_in_addr = cidr_to_inaddr(left_addr, NULL);
+ struct in_addr* right_in_addr = cidr_to_inaddr(right_addr, NULL);
+
+ if( left_in_addr->s_addr < right_in_addr->s_addr )
+ {
+ result = RESULT_SUCCESS;
+ }
+ else
+ {
+ if( verbose )
+ {
+ fprintf(stderr, "Malformed IPv4 range %s: its first address is greater than the last\n", range_str);
+ }
+ result = RESULT_FAILURE;
+ }
+
+ cidr_free(left_addr);
+ cidr_free(right_addr);
+ }
+ }
+
+ return(result);
+}
diff --git a/src/ipaddrcheck_functions.h b/src/ipaddrcheck_functions.h
index 73dd246..05a19c3 100644
--- a/src/ipaddrcheck_functions.h
+++ b/src/ipaddrcheck_functions.h
@@ -76,5 +76,6 @@ 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);
#endif /* IPADDRCHECK_FUNCTIONS_H */
diff --git a/src/ipaddrcheck_functions.o b/src/ipaddrcheck_functions.o
new file mode 100644
index 0000000..e8e714d
--- /dev/null
+++ b/src/ipaddrcheck_functions.o
Binary files differ
diff --git a/tests/check_ipaddrcheck.c b/tests/check_ipaddrcheck.c
index ad2b673..feb5a41 100644
--- a/tests/check_ipaddrcheck.c
+++ b/tests/check_ipaddrcheck.c
@@ -403,6 +403,14 @@ START_TEST (test_is_any_net)
}
END_TEST
+START_TEST (test_is_ipv4_range)
+{
+ ck_assert_int_eq(is_ipv4_range("192.0.2.0-192.0.2.10", 0), RESULT_SUCCESS);
+ ck_assert_int_eq(is_ipv4_range("192.0.2.-", 0), RESULT_FAILURE);
+ ck_assert_int_eq(is_ipv4_range("192.0.2.99-192.0.2.11", 0), RESULT_FAILURE);
+}
+END_TEST
+
Suite *ipaddrcheck_suite(void)
{
@@ -433,6 +441,7 @@ Suite *ipaddrcheck_suite(void)
tcase_add_test(tc_core, test_is_valid_intf_address);
tcase_add_test(tc_core, test_is_any_host);
tcase_add_test(tc_core, test_is_any_net);
+ tcase_add_test(tc_core, test_is_ipv4_range);
suite_add_tcase(s, tc_core);
diff --git a/tests/integration_tests.sh b/tests/integration_tests.sh
index ccc7e3a..a679ea0 100755
--- a/tests/integration_tests.sh
+++ b/tests/integration_tests.sh
@@ -48,6 +48,16 @@ ipv4_cidr_negative=(
192.0.2.666/32
)
+ipv4_range_positive=(
+ 192.0.2.0-192.0.2.100
+)
+
+ipv4_range_negative=(
+ 192.0.2.-192.0.2.100
+ 192.0.2.0-
+ 192.0.2.200-192.0.2.100
+)
+
ipv6_single_positive=(
2001:0db8:0000:0000:0000:ff00:0042:8329
2001:db8:0:0:0:ff00:42:8329
@@ -251,4 +261,17 @@ done
# --is-ipv6-link-local
# --is-valid-intf-address
+# --is-ipv4-range
+for range in \
+ ${ipv4_range_positive[*]}
+do
+ assert_raises "$IPADDRCHECK --is-ipv4-range $range" 0
+done
+
+for range in \
+ ${ipv4_range_negative[*]}
+do
+ assert_raises "$IPADDRCHECK --is-ipv4-range $range" 1
+done
+
assert_end ipaddrcheck_integration