Add isinetaddr6
This change introduces `isinetaddr6`, a function that validates IPv6 addresses. The interface is identical to isinetaddr, where 0 is returned for an invalid address and 1 is returned for a valid address.
This commit is contained in:
parent
0dfbdd45e9
commit
f6e29e4756
5 changed files with 174 additions and 1 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
test/isinetaddr
|
test/isinetaddr
|
||||||
test/iscidraddr
|
test/iscidraddr
|
||||||
|
test/isinetaddr6
|
||||||
|
|
11
Makefile
11
Makefile
|
@ -1,5 +1,5 @@
|
||||||
SRCDIR = src
|
SRCDIR = src
|
||||||
SRCFILES = $(SRCDIR)/isinetaddr.c $(SRCDIR)/iscidraddr.c
|
SRCFILES = $(SRCDIR)/isinetaddr.c $(SRCDIR)/iscidraddr.c $(SRCDIR)/isinetaddr6.c
|
||||||
INCDIR = include
|
INCDIR = include
|
||||||
TESTDIR = test
|
TESTDIR = test
|
||||||
|
|
||||||
|
@ -7,6 +7,10 @@ CC = cc
|
||||||
CFLAGS = -fstack-protector-all -I$(INCDIR) -Wall -Wextra -pedantic
|
CFLAGS = -fstack-protector-all -I$(INCDIR) -Wall -Wextra -pedantic
|
||||||
|
|
||||||
test:
|
test:
|
||||||
|
@make test4
|
||||||
|
@make test6
|
||||||
|
|
||||||
|
test4:
|
||||||
@$(CC) $(CFLAGS) $(SRCFILES) $(TESTDIR)/isinetaddr_test.c -o $(TESTDIR)/isinetaddr
|
@$(CC) $(CFLAGS) $(SRCFILES) $(TESTDIR)/isinetaddr_test.c -o $(TESTDIR)/isinetaddr
|
||||||
@echo -n test/isinetaddr: ''
|
@echo -n test/isinetaddr: ''
|
||||||
@$(TESTDIR)/isinetaddr
|
@$(TESTDIR)/isinetaddr
|
||||||
|
@ -14,4 +18,9 @@ test:
|
||||||
@echo -n test/iscidraddr: ''
|
@echo -n test/iscidraddr: ''
|
||||||
@$(TESTDIR)/iscidraddr
|
@$(TESTDIR)/iscidraddr
|
||||||
|
|
||||||
|
test6:
|
||||||
|
@$(CC) $(CFLAGS) $(SRCFILES) $(TESTDIR)/isinetaddr6_test.c -o $(TESTDIR)/isinetaddr6
|
||||||
|
@echo -n test/isinetaddr6: ''
|
||||||
|
@$(TESTDIR)/isinetaddr6
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
int isinetaddr(const char *str);
|
int isinetaddr(const char *str);
|
||||||
int iscidraddr(const char *str);
|
int iscidraddr(const char *str);
|
||||||
|
int isinetaddr6(const char *str);
|
||||||
|
|
90
src/isinetaddr6.c
Normal file
90
src/isinetaddr6.c
Normal 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
72
test/isinetaddr6_test.c
Normal 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;
|
||||||
|
}
|
Loading…
Reference in a new issue