/* * 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 */