Merge pull request #3 from 0x1eef/ipv6

Add isinetaddr6
This commit is contained in:
0x1eef 2023-09-11 22:56:33 -03:00 committed by GitHub
commit b30d723a0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 233 additions and 4 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
test/isinetaddr
test/iscidraddr
test/isinetaddr6

View file

@ -1,5 +1,5 @@
SRCDIR = src
SRCFILES = $(SRCDIR)/isinetaddr.c $(SRCDIR)/iscidraddr.c
SRCFILES = $(SRCDIR)/isinetaddr.c $(SRCDIR)/iscidraddr.c $(SRCDIR)/isinetaddr6.c
INCDIR = include
TESTDIR = test
@ -7,6 +7,10 @@ CC = cc
CFLAGS = -fstack-protector-all -I$(INCDIR) -Wall -Wextra -pedantic
test:
@make test4
@make test6
test4:
@$(CC) $(CFLAGS) $(SRCFILES) $(TESTDIR)/isinetaddr_test.c -o $(TESTDIR)/isinetaddr
@echo -n test/isinetaddr: ''
@$(TESTDIR)/isinetaddr
@ -14,4 +18,9 @@ test:
@echo -n test/iscidraddr: ''
@$(TESTDIR)/iscidraddr
test6:
@$(CC) $(CFLAGS) $(SRCFILES) $(TESTDIR)/isinetaddr6_test.c -o $(TESTDIR)/isinetaddr6
@echo -n test/isinetaddr6: ''
@$(TESTDIR)/isinetaddr6
.PHONY: test

View file

@ -1,9 +1,10 @@
## About
isinetaddr is a simple C library that provides an interface that can
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.
be used to validate one or more IPv(<b>4</b>|<b>6</b>) 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
@ -122,6 +123,61 @@ foobar is an invalid IPv4 address
127.0.0.1/64 is an invalid IPv4 address
```
### IPv6
The following example demonstrates the `isinetaddr6` function with
both valid and invalid inputs. The `isinetaddr6` function returns 1
when the input given is valid, and otherwise returns 0.
```C
#include <isinetaddr.h>
#include <stdio.h>
#include <stdlib.h>
const char *strings[] = {
/* valid */
"::",
"::1",
"0000:0000:0000:0000:0000:0000:0000:0000",
/* invalid */
"foobar",
NULL,
"00:::0",
};
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 (isinetaddr6(str)) {
printf("%s is a valid IPv6 address\n", str);
} else {
printf("%s is an invalid IPv6 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/*.c share/isinetaddr/examples/isinetaddr6.c -o example
$ ./example
0x1eef [isinetaddr] % ./example
:: is a valid IPv6 address
::1 is a valid IPv6 address
0000:0000:0000:0000:0000:0000:0000:0000 is a valid IPv6 address
foobar is an invalid IPv6 address
(null) is an invalid IPv6 address
00:::0 is an invalid IPv6 address
```
## Sources
* [Source code (GitHub)](https://github.com/0x1eef/isinetaddr#readme)

View file

@ -1,3 +1,4 @@
#pragma once
int isinetaddr(const char *str);
int iscidraddr(const char *str);
int isinetaddr6(const char *str);

90
src/isinetaddr6.c Normal file
View file

@ -0,0 +1,90 @@
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <isinetaddr.h>
static const int MAX_DIGITLEN = 4;
static const int MAX_HEXTETS = 8;
static const int MAX_HEXDIGITS = 32;
static const int MAX_STRLEN = 40;
static const char SEP = ':';
static int has_consecutive_chars(const char *str, char c, int n);
static char* expand(const char *str, size_t strlen, char *new_str, size_t headlen);
int
isinetaddr6(const char *str)
{
int hextets = 1, digitlen = 0, hexdigits = 0;
size_t len = (str == NULL ? 0 : strnlen(str, MAX_STRLEN));
if (len == 0) {
return 0;
} else if (strncasecmp(str, "::ffff", 6) == 0) {
return isinetaddr(&str[7]);
}
for (size_t i = 0; i < len; i++) {
if (has_consecutive_chars(&str[i], SEP, 3)) {
return 0;
} else if (has_consecutive_chars(&str[i], SEP, 2)) {
char new_str[MAX_STRLEN];
return isinetaddr6(expand(str, len, new_str, i));
} else if (str[i] == SEP) {
if (digitlen < MAX_DIGITLEN) {
return 0;
} else {
digitlen = 0;
hextets++;
}
} else if (isxdigit(str[i])) {
if (digitlen == MAX_DIGITLEN) {
return 0;
} else {
digitlen++;
hexdigits++;
}
} else {
return 0;
}
}
if (hextets == MAX_HEXTETS) {
return hexdigits <= MAX_HEXDIGITS && digitlen == MAX_DIGITLEN;
} else {
return 0;
}
}
static int
has_consecutive_chars(const char *str, char c, int n)
{
for (int i = 0; i < n; i++) {
if (*str != c) {
return 0;
}
str++;
}
return 1;
}
static char*
expand(const char *str, size_t strlen, char *new_str, size_t headlen)
{
char *ptr = new_str;
size_t taillen = (strlen - headlen) - 2;
size_t bodylen = MAX_STRLEN - taillen;
size_t i = headlen + 2;
size_t j = headlen;
while (i++ < strlen) {
if (has_consecutive_chars(&str[i], SEP, 2)) {
return NULL;
}
}
memcpy(ptr, &str[0], headlen);
ptr += headlen;
while (++j < bodylen) {
*ptr++ = j % 5 == 0 ? ':' : '0';
}
memcpy(ptr, &str[headlen + 2], taillen);
return new_str;
}

72
test/isinetaddr6_test.c Normal file
View file

@ -0,0 +1,72 @@
#include <isinetaddr.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
const char *valid[] = {
/* valid IPv6 (single colon)*/
"0000:0000:0000:0000:0000:0000:0000:0001",
"2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"fe80:0000:0000:0000:0202:b3ff:fe1e:8329",
"abcd:abcd:abcd:abcd:abcd:abcd:abcd:abcd",
"1234:5678:9abc:def0:face:face:b00c:0ffe",
"2001:19f0:5401:0000:0000:ffff:1e61:face",
"dead:beef:cafe:babe:affe:8a2e:0370:7334",
/* valid IPv6 (double colon) */
"fe80::c001:a0ff:fe12:3456",
"2001:0db8::1",
"2001:abcd:ef01:2345::",
"1234::5678",
"0000:00::1111",
"000::1111:1111",
"000::1111:1111:1111",
"0000:00::1111",
"0000:0000:0000:0000:0000:0000:0000:00::",
"2001:0db8:85a3:0000:0000::8a2e:0370:7334",
"2001::5",
"::ffff:192.168.2.1",
"::FFFF:192.168.2.1",
"::",
"::1",
};
const char *invalid[] = {
/* invalid IPv6 (single colon) */
"0000:0000:0000:0000:0000:0000:0000:000Z",
"0000:0000:0000:0000:0000:0000:0000:0",
"0000:0000:0000:0000:0000:0000:0000:",
/* invalid IPv6 (double colon) */
":::", "2001:::1", "2001:::1::",
"2001::1::", "::1::",
"2001:db8:85a3::8a2e:3700:7334",
"::ffff", "::ffff:", "::ffff:00",
/* edge cases */
NULL
};
int
main(void) {
size_t len;
/* IPv6: valid */
len = sizeof(valid) / sizeof(valid[0]);
for (size_t i = 0; i < len; i++) {
if (isinetaddr6(valid[i]) != 1) {
fprintf(stderr, "assertion failed: '%s' should be valid\n", valid[i]);
abort();
}
}
/* IPv6: invalid */
len = sizeof(invalid) / sizeof(invalid[0]);
for (size_t i = 0; i < len; i++) {
if (isinetaddr6(invalid[i]) != 0) {
fprintf(stderr, "assertion failed: '%s' should NOT be valid\n", invalid[i]);
abort();
}
}
/* Done */
printf("OK\n");
return EXIT_SUCCESS;
}