diff --git a/.gitignore b/.gitignore index c78abfa..d4aa077 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -test/runner +test/isinetaddr +test/iscidraddr diff --git a/Makefile b/Makefile index f0de433..862482e 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,11 @@ CC = cc CFLAGS = -fstack-protector-all -I$(INCDIR) -Wall -Wextra -pedantic test: - $(CC) $(CFLAGS) $(SRCDIR)/isinetaddr.c $(TESTDIR)/isinetaddr_test.c -o $(TESTBIN) - $(TESTBIN) + @$(CC) $(CFLAGS) $(SRCDIR)/isinetaddr.c $(TESTDIR)/isinetaddr_test.c -o $(TESTDIR)/isinetaddr + @echo -n test/isinetaddr: '' + @$(TESTDIR)/isinetaddr + @$(CC) $(CFLAGS) $(SRCDIR)/isinetaddr.c $(TESTDIR)/iscidraddr_test.c -o $(TESTDIR)/iscidraddr + @echo -n test/iscidraddr: '' + @$(TESTDIR)/isinetaddr .PHONY: test diff --git a/README.md b/README.md index 986ca11..1b23fd0 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ ## About isinetaddr is a simple C library that provides an interface that can -be used to validate one or more IPv4 addresses. The library is guided -by easy to extend [testcases](test/) that help verify safety and correctness. +be used to validate one or more IPv4 addresses (with optional support +for CIDR notation as well). The library is guided by easy to extend +[testcases](test/) that help verify safety and correctness. ## Examples @@ -58,6 +59,69 @@ foobar is an invalid IPv4 address 0.0.0.0.0 is an invalid IPv4 address ``` +### CIDR notation (IPv4) + +The `iscidraddr` function supports the same feature set as `isinetaddr`, and +in addition supports +[CIDR notation](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation). +The following example builds on the previous example: + +```C +#include +#include +#include + +const char *strings[] = { + /* valid */ + "127.0.0.1", + "1.1.1.1", + "0.0.0.0", + "127.0.0.1/8", + "127.0.0.1/16", + "127.0.0.1/32", + + /* invalid */ + "foobar", + "0.0.0.0.0", + "127.0.0.1/33", + "127.0.0.1/64" +}; + +int +main(void) +{ + const char *str; + const int i = sizeof(strings) / sizeof(strings[0]); + for (int j = 0; j < i; j++) { + str = strings[j]; + if (iscidraddr(str)) { + printf("%s is a valid IPv4 address\n", str); + } else { + printf("%s is an invalid IPv4 address\n", str); + } + } + return EXIT_SUCCESS; +} +``` + +When the above source code is compiled and run the output is +expected to be as follows: + +``` +$ cc -Iinclude src/isinetaddr.c share/isinetaddr/examples/iscidraddr.c -o example +$ ./example +127.0.0.1 is a valid IPv4 address +1.1.1.1 is a valid IPv4 address +0.0.0.0 is a valid IPv4 address +127.0.0.1/8 is a valid IPv4 address +127.0.0.1/16 is a valid IPv4 address +127.0.0.1/32 is a valid IPv4 address +foobar is an invalid IPv4 address +0.0.0.0.0 is an invalid IPv4 address +127.0.0.1/33 is an invalid IPv4 address +127.0.0.1/64 is an invalid IPv4 address +``` + ## Sources * [Source code (GitHub)](https://github.com/0x1eef/isinetaddr#readme) diff --git a/include/isinetaddr.h b/include/isinetaddr.h index df9db54..80e7696 100644 --- a/include/isinetaddr.h +++ b/include/isinetaddr.h @@ -1,2 +1,3 @@ #pragma once int isinetaddr(const char *str); +int iscidraddr(const char *str); diff --git a/share/isinetaddr/examples/iscidraddr.c b/share/isinetaddr/examples/iscidraddr.c new file mode 100644 index 0000000..8165fcd --- /dev/null +++ b/share/isinetaddr/examples/iscidraddr.c @@ -0,0 +1,34 @@ +#include +#include +#include + +const char *strings[] = { + /* valid */ + "127.0.0.1", + "1.1.1.1", + "0.0.0.0", + "127.0.0.1/8", + "127.0.0.1/16", + "127.0.0.1/32", + + /* invalid */ + "foobar", + "0.0.0.0.0", + "127.0.0.1/33", +}; + +int +main(void) +{ + const char *str; + const int i = sizeof(strings) / sizeof(strings[0]); + for (int j = 0; j < i; j++) { + str = strings[j]; + if (iscidraddr(str)) { + printf("%s is a valid IPv4 address\n", str); + } else { + printf("%s is an invalid IPv4 address\n", str); + } + } + return EXIT_SUCCESS; +} diff --git a/src/isinetaddr.c b/src/isinetaddr.c index a2582ea..8cfba71 100644 --- a/src/isinetaddr.c +++ b/src/isinetaddr.c @@ -4,7 +4,7 @@ #include #include -static int in_range(char buf[4], int buflen); +static int in_range(char buf[4], int min, int max); static void register_octet(int *octets, char *buf, int *buflen); static void register_digit(char digit, int *digits, char *buf, int *buflen); @@ -19,7 +19,7 @@ isinetaddr(const char *str) if (str[l] == '.') { if (buflen == 0) { return 0; - } else if (!in_range(buf, buflen)) { + } else if (!in_range(buf, 0, 255)) { return 0; } else { register_octet(&octets, buf, &buflen); @@ -35,24 +35,43 @@ isinetaddr(const char *str) } } if (octets == 4) { - return digits <= 12 && in_range(buf, buflen) && buflen > 0; + return buflen > 0 && digits <= 12 && in_range(buf, 0, 255); } else { return 0; } } +int +iscidraddr(const char *str) +{ + size_t offset = 0; + size_t len = (str == NULL ? 0 : strnlen(str, 20)); + + for(size_t i = 0; i < len; i++) { + if(str[i] == '/') { + offset = i; + } + } + if (offset == 0) { + return isinetaddr(str); + } else { + char addr[offset], cidr[3]; + char *c = (char*)&str[offset + 1]; + memcpy(addr, str, offset); + memcpy(cidr, c, 2); + addr[offset] = '\0'; + return isinetaddr(addr) && in_range(cidr, 0, 32); + } +} + static int -in_range(char buf[4], int buflen) +in_range(char buf[4], int min, int max) { char *err; long r; - if (buflen < 3) { - return 1; - } else { - errno = 0; - r = strtol(buf, &err, 10); - return *err == '\0' && errno == 0 && (r >= 0 && r <= 255); - } + errno = 0; + r = strtol(buf, &err, 10); + return *err == '\0' && errno == 0 && (r >= min && r <= max); } static void diff --git a/test/iscidraddr_test.c b/test/iscidraddr_test.c new file mode 100644 index 0000000..0c86b42 --- /dev/null +++ b/test/iscidraddr_test.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include + +const char *valid[] = { + "192.168.1.1", + "0.0.0.0", + "255.255.255.255", + "123.45.67.89", + "123.45.67.255", + "10.0.0.1/32" +}; + +const char *invalid[] = { + "123.45.67.891", + "192.168.500.1", + ".192.168.1.1", + "192..168.1.1", + "192.168.1.1.", + "192.168.1.1..", + "192.168.1.1a", + "555555555555555555555555555555555555", + "", + ".", + ".......", + "192.2.2.", + "...4", + "4...4", + "10.0.0.1/33", + "127.0.0.1/64", + "127.0.0.1/", + "127.0.0.1/a", + "/", + "/123.", + "127/2", + NULL +}; + +int +main(void) { + size_t len; + /* IPv4: valid */ + len = sizeof(valid) / sizeof(valid[0]); + for (size_t i = 0; i < len; i++) { + if (iscidraddr(valid[i]) != 1) { + fprintf(stderr, "assertion failed: '%s' should be valid\n", valid[i]); + abort(); + } + } + /* IPv4: invalid */ + len = sizeof(invalid) / sizeof(invalid[0]); + for (size_t i = 0; i < len; i++) { + if (iscidraddr(invalid[i]) != 0) { + fprintf(stderr, "assertion failed: '%s' should NOT be valid\n", invalid[i]); + abort(); + } + } + /* Done */ + printf("OK\n"); + return EXIT_SUCCESS; +} diff --git a/test/isinetaddr_test.c b/test/isinetaddr_test.c index d5b752c..627f7e8 100644 --- a/test/isinetaddr_test.c +++ b/test/isinetaddr_test.c @@ -8,7 +8,7 @@ const char *valid[] = { "0.0.0.0", "255.255.255.255", "123.45.67.89", - "123.45.67.255", + "123.45.67.255" }; const char *invalid[] = {