commit
b30d723a0d
6 changed files with 233 additions and 4 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
test/isinetaddr
|
||||
test/iscidraddr
|
||||
test/isinetaddr6
|
||||
|
|
11
Makefile
11
Makefile
|
@ -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
|
||||
|
|
62
README.md
62
README.md
|
@ -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)
|
||||
|
|
|
@ -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
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