Last active
October 9, 2020 04:34
-
-
Save klopp/9f99e56698ce30ed005474cc3ba6a0ac to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <arpa/inet.h> | |
#include <string> | |
struct cidr; | |
static bool cidr_match(const cidr& src, const cidr& net); | |
static bool addr2cidr(const char* addr, cidr& entry); | |
/* | |
src & net can be any of: | |
"IPv4" | |
"IPv6" | |
"IPv4/BITS" | |
"IPv6/BITS" | |
"IPv4/IPv4" | |
"IPv4/IPv6" | |
"IPv6/IPv4" | |
"IPv6/IPv6" | |
*/ | |
bool ip_match(const char* src, const char* net) | |
{ | |
cidr ip, dest; | |
return(addr2cidr(src, ip) && addr2cidr(net, dest) && cidr_match(ip, dest)); | |
} | |
struct cidr | |
{ | |
/* Common */ | |
int type; | |
uint32_t mask; | |
union { | |
in_addr_t net4; | |
in6_addr net6; | |
} net; | |
/* IPv6 only */ | |
int bits_incomplete; | |
int bits_whole; | |
}; | |
static bool addr2cidr(const char* addr, cidr& entry) | |
{ | |
entry.type = -1; | |
entry.mask = 0xFFFFFFFFu; | |
entry.bits_incomplete = 0; | |
entry.bits_whole = 0; | |
std::string src_ip(addr); | |
std::string::size_type slash = src_ip.find('/'); | |
if(slash != std::string::npos) { | |
src_ip.erase(slash); | |
} | |
int nettype = src_ip.find(':') == std::string::npos ? AF_INET : AF_INET6; | |
int bits = 0; | |
if(slash != std::string::npos) { | |
if(strspn(src_ip.c_str() + slash, ":.")) { | |
/* no address type check, get longest buffer */ | |
in6_addr buf; | |
bits = inet_net_pton(nettype, src_ip.c_str() + slash + 1, &buf, sizeof(buf)); | |
} | |
else { | |
bits = std::atoi(src_ip.c_str() + slash + 1); | |
} | |
if(bits < 0) { | |
return false; | |
} | |
} | |
if(nettype == AF_INET) { | |
int rc = inet_pton(AF_INET, src_ip.c_str(), &entry.net.net4); | |
if(rc == 0) { | |
/* 0 is returned if does not contain a character string | |
representing a valid network address in the specified address family */ | |
return false; | |
} | |
if(rc < 0) { | |
/* If does not contain a valid address family, -1 is returned and errno | |
is set to EAFNOSUPPORT */ | |
return false; | |
} | |
if(bits) { | |
entry.mask = htonl(0xFFFFFFFFu << (32 - bits)); | |
} | |
} | |
else { | |
int rc = inet_pton(AF_INET6, src_ip.c_str(), &entry.net.net6); | |
if(rc == 0) { | |
/* see abowe */ | |
return false; | |
} | |
if(rc < 0) { | |
/* see abowe */ | |
return false; | |
} | |
entry.bits_whole = bits >> 5; | |
entry.bits_incomplete = bits & 0x1F; | |
if(entry.bits_incomplete) { | |
entry.mask = htonl((0xFFFFFFFFu) << (32 - entry.bits_incomplete)); | |
} | |
} | |
entry.type = nettype; | |
return true; | |
} | |
static bool cidr_match(const cidr& src, const cidr& net) | |
{ | |
if(src.type != net.type) return false; | |
if(src.type == AF_INET) { | |
/* IPv4 */ | |
return !((src.net.net4 ^ net.net.net4) & net.mask); | |
} | |
/* IPv6 */ | |
#ifdef __linux__ | |
const uint32_t* a = src.net.net6.s6_addr32; | |
const uint32_t* n = net.net.net6.s6_addr32; | |
#else | |
const uint32_t* a = src.net.net6.__u6_addr.__u6_addr32; | |
const uint32_t* n = net.net.net6.__u6_addr.__u6_addr32; | |
#endif | |
if(net.bits_whole) { | |
if(memcmp(a, n, net.bits_whole << 2)) { | |
return false; | |
} | |
} | |
if(net.bits_incomplete) { | |
if((a[net.bits_whole] ^ n[net.bits_whole]) & net.mask) { | |
return false; | |
} | |
} | |
return true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment