/*
* Misc. network related functions
*
* Copyright 2004-2009 Nicolas Bernard
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
/* On Linux/glibc systems, _BSD_SOURCE must be defined */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "debug.h"
#include "netmisc.h"
static bool
isloopbackip4(const struct sockaddr* addr)
{ /* all the 127.0.0.* addresses can be loopback */
unsigned long int ip = 0;
assert(((struct sockaddr_in*) addr)->sin_family == AF_INET);
ip = (((struct sockaddr_in*) addr)->sin_addr.s_addr);
if (ip % 256 == 127 && (ip >> 8) % 256 == 0 && (ip >> 16) % 256 == 0)
return true;
return false;
}
static int
isloopbackip6(const struct sockaddr* addr)
{ /* 0000:0000:0000:0000:0000:0000:0000:0001 only ? */
/** \test test this function */
assert(((struct sockaddr_in6*) addr)->sin6_family == AF_INET6);
struct in6_addr *tmp6 = &((struct sockaddr_in6*) addr)->sin6_addr;
#ifdef __KAME__
for (int i = 0; i < 3; i++) {
if (tmp6->__u6_addr.__u6_addr32[i])
return false;
}
if (tmp6->__u6_addr.__u6_addr32[3] == htonl(1))
return true;
#elif (__GLIBC__ <= 2 && __GLIBC_MINOR__ < 8)/* ~ linux 2.4 systems ? */
for (int i = 0; i < 3; i++) {
if (tmp6->in6_u.u6_addr32[i])
return false;
}
if (tmp6->in6_u.u6_addr32[3] == htonl(1))
return true;
#else /* ~ linux 2.6+ systems ? */
for (int i = 0; i < 3; i++) {
if (tmp6->__in6_u.__u6_addr32[i])
return false;
}
if (tmp6->__in6_u.__u6_addr32[3] == htonl(1))
return true;
#endif
return false;
}
int
isloopback(const struct sockaddr* addr)
{
switch (((struct sockaddr_in*) addr)->sin_family) {
case AF_INET:
return (int) isloopbackip4(addr);
case AF_INET6:
return (int) isloopbackip6(addr);
default:
return -1;
}
}
static int
isprivateip4(const struct sockaddr* addr)
{
if (((struct sockaddr_in*) addr)->sin_family != AF_INET)
return -2;
if (isloopbackip4(addr))
return 1;
unsigned long int ip = 0;
ip = (((struct sockaddr_in*) addr)->sin_addr.s_addr);
if (ip % 256 == 10)
return 2;
if ((ip % 256 == 192)
&& ((ip >> 8) % 256 == 168))
return 3;
if ((ip % 256 == 172)
&& ((ip >> 8) % 256 >= 16)
&& ((ip >> 8) % 256 <= 31))
return 4;
return 0;
}
static int
isprivateip6(const struct sockaddr* addr)
{ ///\test to test
if (((struct sockaddr_in6*) addr)->sin6_family != AF_INET6) return -3;
if (isloopbackip6(addr)) return 5;
struct in6_addr *tmp6 = &((struct sockaddr_in6*) addr)->sin6_addr;
#ifdef __KAME__
if (tmp6->__u6_addr.__u6_addr8[0] == 0xFE
&& tmp6->__u6_addr.__u6_addr8[1] & 0x80)
return 6;
#elif (__GLIBC__ <= 2 && __GLIBC_MINOR__ < 8) /* linux 2.4 ? */
if (tmp6->in6_u.u6_addr8[0] == 0xFE
&& tmp6->in6_u.u6_addr8[1] & 0x80)
return 6;
#else /* linux 2.6 ? */
if (tmp6->__in6_u.__u6_addr8[0] == 0xFE
&& tmp6->__in6_u.__u6_addr8[1] & 0x80)
return 6;
#endif
return 0;
}
int
isprivateip(const struct sockaddr* addr)
{
switch (((struct sockaddr_in*) addr)->sin_family) {
case AF_INET:
return isprivateip4(addr);
case AF_INET6:
return isprivateip6(addr);
default:
return -1;
}
}
static int
prefixeql4(const struct sockaddr_in *a, const struct sockaddr_in *b,
uint8_t upto)
{
assert(a != NULL);
assert(b != NULL);
assert(upto < 32);
uint32_t mask = 0xFFFFFFFF;
mask <<= (32 - upto);
mask >>= (32 - upto);
assert(sizeof a->sin_addr.s_addr == sizeof mask);
assert(sizeof b->sin_addr.s_addr == sizeof mask);
return ((a->sin_addr.s_addr & mask) == (b->sin_addr.s_addr & mask));
}
static int
prefixeql6(const struct sockaddr_in6 *a, const struct sockaddr_in6 *b,
uint8_t upto)
{
assert(a != NULL);
assert(b != NULL);
assert(upto < 128);
debug("not implemented");
abort(); /* \todo [IPv6] not implemented yet */
return -1;
}
/* are the address equals up to a prefix? */
int
prefixeql(const struct sockaddr* a, const struct sockaddr* b, uint8_t upto)
{
assert(a != NULL);
assert(b != NULL);
if (a->sa_family != b->sa_family)
return 0;
/* nb: returns false if both are IPv4 but one of them is mapped in an
IPv6 address as anyway the size of the prefix won't be the same */
switch (a->sa_family) {
case AF_INET: {
if (upto >= 32)
return addreql(a, b);
const struct sockaddr_in *phonya, *phonyb;
#ifndef __GLIBC__
if (a->sa_len != 16 || b->sa_len != 16)
return -1;
#endif
phonya = (const struct sockaddr_in *) a;
phonyb = (const struct sockaddr_in *) b;
return prefixeql4(phonya, phonyb, upto);
}
break;
case AF_INET6: {
if (upto >= 128)
return addreql(a, b);
const struct sockaddr_in6 *phonya, *phonyb;
#ifndef __GLIBC__
if (a->sa_len != 28 || b->sa_len != 28)
return -1;
#endif
phonya = (const struct sockaddr_in6 *) a;
phonyb = (const struct sockaddr_in6 *) b;
return prefixeql6(phonya, phonyb, upto);
}
break;
default:
return -1;
}
return 0;
}
/* look if a contains an IPv4 address mapped in an IPv6 one equal to b. */
/* an IPv4 address mapped in an IPv6 one looks like an IPv6 address where
the first 80 bits are 0, the 16 next are 1 and the last 32 ones are
the IPv4 address. */
static int
addreql64(const struct sockaddr_in6* a, const struct sockaddr_in* b)
{
assert(a != NULL);
assert(b != NULL);
assert(a->sin6_family == AF_INET6);
assert(b->sin_family == AF_INET);
#ifndef __GLIBC__
if (a->sin6_len != 28 || b->sin_len != 16)
return -1;
#endif
#ifdef __KAME__
for (int i = 0; i < 2; i++) {
if (a->sin6_addr.__u6_addr.__u6_addr32[i])
return 0;
}
if (a->sin6_addr.__u6_addr.__u6_addr16[4]
|| a->sin6_addr.__u6_addr.__u6_addr16[5] != 0xFFFF)
return 0;
if (a->sin6_addr.__u6_addr.__u6_addr32[3] == b->sin_addr.s_addr)
return true;
#elif (__GLIBC__ <= 2 && __GLIBC_MINOR__ < 8) /* linux 2.4 ? */
for (int i = 0; i < 2; i++) {
if (a->sin6_addr.in6_u.u6_addr32[i])
return 0;
}
if (a->sin6_addr.in6_u.u6_addr16[4]
|| a->sin6_addr.in6_u.u6_addr16[5] != 0xFFFF)
return 0;
if (a->sin6_addr.in6_u.u6_addr32[3] == b->sin_addr.s_addr)
return 1;
#else /* linux 2.6 ? */
for (int i = 0; i < 2; i++) {
if (a->sin6_addr.__in6_u.__u6_addr32[i])
return 0;
}
if (a->sin6_addr.__in6_u.__u6_addr16[4]
|| a->sin6_addr.__in6_u.__u6_addr16[5] != 0xFFFF)
return 0;
if (a->sin6_addr.__in6_u.__u6_addr32[3] == b->sin_addr.s_addr)
return 1;
#endif
return 0;
}
int
addreql(const struct sockaddr* a, const struct sockaddr* b)
{
assert(a != NULL);
assert(b != NULL);
if (a->sa_family == AF_INET6 && b->sa_family == AF_INET)
return addreql64((const struct sockaddr_in6*) a,
(const struct sockaddr_in*) b);
if (a->sa_family == AF_INET && b->sa_family == AF_INET6)
return addreql64((const struct sockaddr_in6*) b,
(const struct sockaddr_in*) a);
if (a->sa_family != b->sa_family)
return 0;
switch (a->sa_family) {
case AF_INET: {
const struct sockaddr_in *phonya, *phonyb;
#ifndef __GLIBC__
if (a->sa_len != 16 || b->sa_len != 16)
return -1;
#endif
phonya = (const struct sockaddr_in *) a;
phonyb = (const struct sockaddr_in *) b;
if (!memcmp(&phonya->sin_addr, &phonyb->sin_addr, 4))
return 1;
}
break;
case AF_INET6: {
const struct sockaddr_in6 *phonya, *phonyb;
#ifndef __GLIBC__
if (a->sa_len != 28 || b->sa_len != 28)
return -1;
#endif
phonya = (const struct sockaddr_in6 *) a;
phonyb = (const struct sockaddr_in6 *) b;
if (!memcmp(&phonya->sin6_addr, &phonyb->sin6_addr, 4))
return 1;
}
break;
default:
return -1;
}
return 0;
}
uint16_t
portof(const struct sockaddr* addr)
{
assert(addr != NULL);
switch (addr->sa_family) {
case AF_INET:
return ((const struct sockaddr_in *) addr)->sin_port;
case AF_INET6:
return ((const struct sockaddr_in6 *) addr)->sin6_port;
default:
return 0;
}
return 0;
}
int
porteql(const struct sockaddr* a, const struct sockaddr* b)
{
assert(a != NULL);
assert(b != NULL);
uint16_t porta = portof(a);
uint16_t portb = portof(b);
if (porta && porta == portb)
return 1;
return 0;
}
int
echoip(FILE* restrict f, const struct sockaddr* addr)
{
/* string_ip use at most 47 octets */
char buf[47]; /* Flawfinder: ignore */
if (string_ip(buf, (const struct sockaddr_storage*) addr) != 0)
return -1;
fprintf(f, "%s", buf);
return 0;
}
inline unsigned int
addrsize(const struct sockaddr* addr)
{
#ifndef __GLIBC__
return addr->sa_len;
#else
switch(addr->sa_family) {
case AF_INET:
return sizeof(struct sockaddr_in);
case AF_INET6:
return sizeof(struct sockaddr_in6);
default:
return 0;
}
#endif
}
int
read_ip(const char *string, struct sockaddr_storage *sa)
{ /** \test */
assert(string != NULL);
assert(sa != NULL);
memset(sa, 0, sizeof(struct sockaddr));
sa->ss_family = AF_INET;
int err = inet_pton(AF_INET, string,
&((struct sockaddr_in*) sa)->sin_addr);
if (err == 1)
return 0;
if (err < 0) {
stkdbg("inet_pton(AF_INET, ...) returned %d, errno: %s", err,
strerror(errno));
return -2;
}
assert(err == 0);
sa->ss_family = AF_INET6;
err = inet_pton(AF_INET6, string,
&((struct sockaddr_in6*) sa)->sin6_addr);
if (err == 1)
return 0;
if (err < 0) {
stkdbg("inet_pton(AF_INET6, ...) returned %d, errno: %s", err,
strerror(errno));
return -3;
}
return -1;
}
int
string_ip(char buffer[static 47] /* Flawfinder: ignore */,
const struct sockaddr_storage *sa)
{
assert(buffer != NULL);
assert(sa != NULL);
const char *err;
if (((struct sockaddr_in*) sa)->sin_family == AF_INET6)
err = inet_ntop(AF_INET6,
&(((struct sockaddr_in6*) sa)->sin6_addr),
buffer, 46);
else
err = inet_ntop(AF_INET,
&(((struct sockaddr_in*) sa)->sin_addr),
buffer, 46);
buffer[46] = '\0';
if (err == NULL) {
buffer[0] = '\0';
stkdbg("inet_ntop failed: %s", strerror(errno));
return -1;
}
return 0;
}
int
writen(int desc, const void* buf, unsigned int bufsize)
{
int err = 0;
uint16_t sent = 0;
int counter = 0;
if (bufsize == 0 || buf == NULL) {
debug("called writen(%d, %p, %u)", desc, buf, bufsize);
return -4;
}
do {
assert((int64_t) bufsize - sent > 0);
err = write(desc, (uint8_t*) buf + sent, bufsize - sent);
if (err == 0)
return -1;
if (err == -1) {
counter++;
if (errno == EAGAIN || errno == EINTR)
continue;
debug("write error: %s", strerror(errno));
return -2;
}
sent += err;
counter = 0;
} while (sent < bufsize && counter < 10);
if (counter >= 10)
return -3;
return 0;
}
int
readn(int desc, void* buf, unsigned int bufsize)
{
int err = 0;
uint16_t rcvd = 0;
do {
assert((int64_t) bufsize - rcvd > 0);
/* Flawfinder: ignore */
err = read(desc, (uint8_t*) buf + rcvd, bufsize - rcvd);
if (err == 0)
return -1;
if (err == -1) {
if (errno == EAGAIN || errno == EINTR)
continue;
stkdbg("read error: %s", strerror(errno));
return -2;
}
rcvd += err;
} while (rcvd < bufsize);
return 0;
}
int
readunk(int desc, void* buf, unsigned int bufsize)
{
int err = 0;
uint16_t rcvd = 0;
do {
assert((int64_t) bufsize - rcvd > 0);
/* Flawfinder: ignore */
err = read(desc, (uint8_t*) buf + rcvd, bufsize - rcvd);
if (err == 0)
return (int) rcvd;
if (err == -1) {
if (errno == EAGAIN || errno == EINTR) {
if (rcvd)
return (int) rcvd;
continue;
}
return -1;
}
rcvd += err;
} while (rcvd < bufsize && err == -1);
return (int) rcvd;
}
#if 0
// don't work yet
int
getlocaladdresses(struct sockaddr_storage **addrs, unsigned int nbif)
{
debug(" ");
#ifdef SIOCGIFCONF
if (nbif < 10)
nbif = 10;
char ifr[nbif * sizeof(struct ifreq)]; /* Flawfinder: ignore */
memset(&ifr, 0, sizeof ifr);
struct ifconf ifc;
ifc.ifc_len = sizeof ifr;
ifc.ifc_buf = ifr;
debug(" ");
int foosock = socket(AF_INET, SOCK_STREAM, 0);
if (foosock == -1) {
debug("socket failed: %s", strerror(errno));
return -1;
}
int err = listen(foosock, 1);
if (err < 0) {
close(foosock);
debug("ioctl(SIOCGIFCONF) failed: %s", strerror(errno));
return -1;
}
err = ioctl(foosock, SIOCGIFCONF, &ifc);
close(foosock);
if (err < 0) {
debug("ioctl(SIOCGIFCONF) failed: %s", strerror(errno));
return -1;
}
if (ifc.ifc_len == sizeof ifr) // probable overflow
return getlocaladdresses(addrs, nbif * 2);
debug("ifc.ifc_len: %d (was: %d)", ifc.ifc_len, sizeof ifr);
for (int i = 0; i < ifc.ifc_len / sizeof(struct ifreq); i++) {
debug("(i: %d) interface: %s:", i, ifc.ifc_req[i].ifr_name);
echoip(stderr, &ifc.ifc_req[i].ifr_addr);
fprintf(stderr, "\n");
}
return 0;
#else
return -1;
#endif /* SIOCGIFCONF */
}
#endif
#ifdef NETMISC_TESTS
int
main()
{
int err = 0;
struct sockaddr_in a;
a.sin_family = AF_INET;
err = inet_pton(AF_INET, "193.168.1.2", &a.sin_addr);
assert(err == 1);
struct sockaddr_in b;
b.sin_family = AF_INET;
err = inet_pton(AF_INET, "193.168.1.3", &b.sin_addr);
assert(err == 1);
struct sockaddr_in c;
c.sin_family = AF_INET;
err = inet_pton(AF_INET, "127.0.0.1", &c.sin_addr);
assert(err == 1);
struct sockaddr_in d;
d.sin_family = AF_INET;
err = inet_pton(AF_INET, "193.161.1.8", &d.sin_addr);
assert(err == 1);
printf("testing echoip...\n");
echoip(stdout, (struct sockaddr*) &a);
printf("\n");
echoip(stdout, (struct sockaddr*) &b);
printf("\n");
echoip(stdout, (struct sockaddr*) &c);
printf("\n");
echoip(stdout, (struct sockaddr*) &d);
printf("\n");
printf("testing isloopbackip4...\n");
if (isloopbackip4((struct sockaddr*) &a))
assert(0);
if (isloopbackip4((struct sockaddr*) &b))
assert(0);
if (!isloopbackip4((struct sockaddr*) &c))
assert(0);
if (isloopbackip4((struct sockaddr*) &d))
assert(0);
printf("testing prefixeql4...\n");
if (!prefixeql4(&a, &b, 24))
assert(0);
if (prefixeql4(&a, &c, 24))
assert(0);
if (prefixeql4(&a, &d, 24))
assert(0);
if (prefixeql4(&c, &d, 24))
assert(0);
// todo: more tests
printf("done\n");
return 0;
}
#endif /* NETMISC_TESTS */