323 lines
7.6 KiB
C
323 lines
7.6 KiB
C
#include "massip-addr.h"
|
|
#include <string.h>
|
|
|
|
|
|
/**
|
|
* Holds the output string, so that we can append to it without
|
|
* overflowing buffers. The _append_xxx() functions below append
|
|
* to this string.
|
|
*/
|
|
typedef struct stream_t {
|
|
char *buf;
|
|
size_t offset;
|
|
size_t length;
|
|
} stream_t;
|
|
|
|
/**
|
|
* Append a character to the output string. All the other _append_xxx()
|
|
* functions call this one, so this is the only one where a
|
|
* buffer-overflow can occur.
|
|
*/
|
|
static void
|
|
_append_char(stream_t *out, char c)
|
|
{
|
|
if (out->offset < out->length)
|
|
out->buf[out->offset++] = c;
|
|
|
|
/* keep the string nul terminated as we build it */
|
|
if (out->offset < out->length)
|
|
out->buf[out->offset] = '\0';
|
|
}
|
|
|
|
static void
|
|
_append_ipv6(stream_t *out, const unsigned char *ipv6)
|
|
{
|
|
static const char hex[] = "0123456789abcdef";
|
|
size_t i;
|
|
int is_ellision = 0;
|
|
|
|
/* An IPv6 address is printed as a series of 2-byte hex words
|
|
* separated by colons :, for a total of 16-bytes */
|
|
for (i = 0; i < 16; i += 2) {
|
|
unsigned n = ipv6[i] << 8 | ipv6[i + 1];
|
|
|
|
/* Handle the ellision case. A series of words with a value
|
|
* of 0 can be removed completely, replaced by an extra colon */
|
|
if (n == 0 && !is_ellision) {
|
|
is_ellision = 1;
|
|
while (i < 13 && ipv6[i + 2] == 0 && ipv6[i + 3] == 0)
|
|
i += 2;
|
|
_append_char(out, ':');
|
|
|
|
/* test for all-zero address, in which case the output
|
|
* will be "::". */
|
|
while (i == 14 && ipv6[i] == 0 && ipv6[i + 1] == 0){
|
|
i=16;
|
|
_append_char(out, ':');
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* Print the colon between numbers. Fence-post alert: only colons
|
|
* between numbers are printed, not at the beginning or end of the
|
|
* string */
|
|
if (i)
|
|
_append_char(out, ':');
|
|
|
|
/* Print the digits. Leading zeroes are not printed */
|
|
if (n >> 12)
|
|
_append_char(out, hex[(n >> 12) & 0xF]);
|
|
if (n >> 8)
|
|
_append_char(out, hex[(n >> 8) & 0xF]);
|
|
if (n >> 4)
|
|
_append_char(out, hex[(n >> 4) & 0xF]);
|
|
_append_char(out, hex[(n >> 0) & 0xF]);
|
|
}
|
|
}
|
|
|
|
struct ipaddress_formatted ipv6address_fmt(ipv6address a)
|
|
{
|
|
struct ipaddress_formatted out;
|
|
unsigned char tmp[16];
|
|
size_t i;
|
|
stream_t s;
|
|
|
|
/*
|
|
* Convert address into a sequence of bytes. Our code
|
|
* here represents an IPv6 address as two 64-bit numbers, but
|
|
* the formatting code above that we copied from a different
|
|
* project represents it as an array of bytes.
|
|
*/
|
|
for (i=0; i<16; i++) {
|
|
uint64_t x;
|
|
if (i<8)
|
|
x = a.hi;
|
|
else
|
|
x = a.lo;
|
|
x >>= (7 - (i%8)) * 8;
|
|
|
|
tmp[i] = (unsigned char)(x & 0xFF);
|
|
}
|
|
|
|
/* Call the formatting function */
|
|
s.buf = out.string;
|
|
s.offset = 0;
|
|
s.length = sizeof(out.string);
|
|
_append_ipv6(&s, tmp);
|
|
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* Append a decimal integer.
|
|
*/
|
|
static void
|
|
_append_decimal(stream_t *out, unsigned long long n)
|
|
{
|
|
char tmp[64];
|
|
size_t tmp_offset = 0;
|
|
|
|
/* Create temporary string */
|
|
while (n >= 10) {
|
|
unsigned digit = n % 10;
|
|
n /= 10;
|
|
tmp[tmp_offset++] = (char)('0' + digit);
|
|
}
|
|
|
|
/* the final digit, may be zero */
|
|
tmp[tmp_offset++] = (char)('0' + n);
|
|
|
|
/* Copy the result backwards */
|
|
while (tmp_offset)
|
|
_append_char(out, tmp[--tmp_offset]);
|
|
}
|
|
static void
|
|
_append_hex2(stream_t *out, unsigned long long n)
|
|
{
|
|
static const char hex[17] = "0123456789abcdef";
|
|
|
|
_append_char(out, hex[(n>>4)&0xF]);
|
|
_append_char(out, hex[(n>>0)&0xF]);
|
|
}
|
|
|
|
struct ipaddress_formatted ipv4address_fmt(ipv4address ip)
|
|
{
|
|
struct ipaddress_formatted out;
|
|
stream_t s;
|
|
|
|
|
|
/* Call the formatting function */
|
|
s.buf = out.string;
|
|
s.offset = 0;
|
|
s.length = sizeof(out.string);
|
|
|
|
_append_decimal(&s, (ip >> 24) & 0xFF);
|
|
_append_char(&s, '.');
|
|
_append_decimal(&s, (ip >> 16) & 0xFF);
|
|
_append_char(&s, '.');
|
|
_append_decimal(&s, (ip >> 8) & 0xFF);
|
|
_append_char(&s, '.');
|
|
_append_decimal(&s, (ip >> 0) & 0xFF);
|
|
|
|
return out;
|
|
}
|
|
|
|
struct ipaddress_formatted macaddress_fmt(macaddress_t mac)
|
|
{
|
|
struct ipaddress_formatted out;
|
|
stream_t s;
|
|
|
|
|
|
/* Call the formatting function */
|
|
s.buf = out.string;
|
|
s.offset = 0;
|
|
s.length = sizeof(out.string);
|
|
|
|
_append_hex2(&s, mac.addr[0]);
|
|
_append_char(&s, '-');
|
|
_append_hex2(&s, mac.addr[1]);
|
|
_append_char(&s, '-');
|
|
_append_hex2(&s, mac.addr[2]);
|
|
_append_char(&s, '-');
|
|
_append_hex2(&s, mac.addr[3]);
|
|
_append_char(&s, '-');
|
|
_append_hex2(&s, mac.addr[4]);
|
|
_append_char(&s, '-');
|
|
_append_hex2(&s, mac.addr[5]);
|
|
|
|
return out;
|
|
}
|
|
|
|
struct ipaddress_formatted ipaddress_fmt(ipaddress a)
|
|
{
|
|
struct ipaddress_formatted out;
|
|
stream_t s;
|
|
ipv4address ip = a.ipv4;
|
|
|
|
if (a.version == 6) {
|
|
return ipv6address_fmt(a.ipv6);
|
|
}
|
|
|
|
|
|
/* Call the formatting function */
|
|
s.buf = out.string;
|
|
s.offset = 0;
|
|
s.length = sizeof(out.string);
|
|
|
|
_append_decimal(&s, (ip >> 24) & 0xFF);
|
|
_append_char(&s, '.');
|
|
_append_decimal(&s, (ip >> 16) & 0xFF);
|
|
_append_char(&s, '.');
|
|
_append_decimal(&s, (ip >> 8) & 0xFF);
|
|
_append_char(&s, '.');
|
|
_append_decimal(&s, (ip >> 0) & 0xFF);
|
|
|
|
return out;
|
|
}
|
|
|
|
|
|
static unsigned _count_long(uint64_t number)
|
|
{
|
|
unsigned i;
|
|
unsigned count = 0;
|
|
for (i=0; i<64; i++) {
|
|
if ((number >> i) & 1)
|
|
count = i + 1;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Find the number of bits needed to hold the integer. In other words,
|
|
* the number 0x64 would need 7 bits to store it.
|
|
*
|
|
* We use this to count the size of scans. We currently only support
|
|
* scan sizes up to 63 bits.
|
|
*/
|
|
unsigned massint128_bitcount(massint128_t number)
|
|
{
|
|
if (number.hi)
|
|
return _count_long(number.hi) + 64;
|
|
else
|
|
return _count_long(number.lo);
|
|
}
|
|
|
|
ipv6address_t ipv6address_add_uint64(ipv6address_t lhs, uint64_t rhs) {
|
|
lhs.lo += rhs;
|
|
if (lhs.lo < rhs) {
|
|
lhs.hi += 1;
|
|
}
|
|
return lhs;
|
|
}
|
|
|
|
ipv6address_t ipv6address_subtract(ipv6address_t lhs, ipv6address_t rhs) {
|
|
ipv6address_t difference;
|
|
difference.hi = lhs.hi - rhs.hi;
|
|
difference.lo = lhs.lo - rhs.lo;
|
|
|
|
/* check for underflow */
|
|
if (difference.lo > lhs.lo)
|
|
difference.hi -= 1;
|
|
return difference;
|
|
}
|
|
|
|
ipv6address_t ipv6address_add(ipv6address_t lhs, ipv6address_t rhs) {
|
|
ipv6address_t sum;
|
|
sum.hi = lhs.hi + rhs.hi;
|
|
sum.lo = lhs.lo - rhs.lo;
|
|
|
|
/* check for underflow */
|
|
if (sum.lo > lhs.lo)
|
|
sum.hi += 1;
|
|
return sum;
|
|
}
|
|
|
|
|
|
int ipv6address_selftest(void)
|
|
{
|
|
int x = 0;
|
|
ipaddress ip;
|
|
struct ipaddress_formatted fmt;
|
|
|
|
ip.version = 4;
|
|
ip.ipv4 = 0x01FF00A3;
|
|
|
|
fmt = ipaddress_fmt(ip);
|
|
if (strcmp(fmt.string, "1.255.0.163") != 0)
|
|
x++;
|
|
|
|
return x;
|
|
}
|
|
|
|
int ipv6address_is_equal_prefixed(ipv6address_t lhs, ipv6address_t rhs, unsigned prefix)
|
|
{
|
|
ipv6address mask;
|
|
|
|
/* If the prefix is bad, then the answer is 'no'. */
|
|
if (prefix > 128) {
|
|
return 0;
|
|
}
|
|
|
|
/* Create the mask from the prefix */
|
|
if (prefix > 64)
|
|
mask.hi = ~0ULL;
|
|
else if (prefix == 0)
|
|
mask.hi = 0;
|
|
else
|
|
mask.hi = ~0ULL << (64 - prefix);
|
|
|
|
if (prefix > 64)
|
|
mask.lo = ~0ULL << (128 - prefix);
|
|
else
|
|
mask.lo = 0;
|
|
|
|
/* Mask off any non-zero bits from both addresses */
|
|
lhs.hi &= mask.hi;
|
|
lhs.lo &= mask.lo;
|
|
rhs.hi &= mask.hi;
|
|
rhs.lo &= mask.lo;
|
|
|
|
/* Now do a normal compare */
|
|
return ipv6address_is_equal(lhs, rhs);
|
|
}
|