Skip to content

Instantly share code, notes, and snippets.

@cypres
Created May 28, 2015 09:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cypres/050fcd418f2228fb399d to your computer and use it in GitHub Desktop.
Save cypres/050fcd418f2228fb399d to your computer and use it in GitHub Desktop.
Trim a CIDR string to make it compatible with inet_net_pton
#ifdef __FreeBSD__
#include <sys/socket.h>
#endif
#include <arpa/inet.h>
#include <netinet/in.h>
#include <algorithm> // find, find_if_not, reverse_copy, for_each
#include <functional> // bind1st, std::equal_to
#include <string> // string
#include <iostream> // cout
// Trim a CIDR string so (excess) zero groups are trimmed out
// inet_net_pton is very picky about how many address groups are specified, so
// this method trims any '::' which is adjacent to the '/' char in a CIDR.
// 2001:db8::/32 is not understood by inet_net_pton, since with 32 bits it's
// supposed to be 2001:db8/32, this method fixes that, unfortunately it also
// trimmes zero groups that might be needed such as 2001:db8::/48.
std::string TrimCidr(const std::string &input) {
// Find the network sep '/' starting from the rear where it's closest
auto net_it = std::find(input.crbegin(), input.crend(), '/');
if (net_it != input.crend()) {
// Advance the iterator to skip past the '/' itself
std::advance(net_it, 1);
// If this is not a ':' char, then early return (for speed)
if (*net_it != ':') {
return input;
}
// Find location of first non ':' char
auto trim_it = std::find_if_not(net_it,
input.crend(), std::bind1st(std::equal_to<char>(),':'));
// If the address contains anything else than ':' then trim excess ':'
if (trim_it != input.crend()) {
std::string output;
output.reserve(input.size());
std::reverse_copy(trim_it, input.crend(), std::back_inserter(output));
std::reverse_copy(input.crbegin(), net_it, std::back_inserter(output));
return output;
}
}
return input;
}
int main() {
auto tests = {
"0.0.0.0/0",
"::0/0",
"2001:db8::/32", // This will fail without being trimmed
"2001:db8::/48", // This fails when trimmed
"2001:db8::ff00:42:8329",
"2a03:2880:2130:cf05:face:b00c:0:1",
"192.168.0.16/28",
"192.168/16",
"1.2.3.4",
"0::0/0", // Fails but that's okay since the correct form is ::/0
"::ffff:1.2.3.0/120",
"2001:1448:281::/48",
"::/0",
};
std::for_each(tests.begin(), tests.end(), [](std::string t) {
auto cidr = TrimCidr(t);
std::cout << t << " -> " << cidr << " - ";
int bits;
in_addr ip4;
in6_addr ip6;
bits = inet_net_pton(AF_INET, cidr.c_str(), &ip4, sizeof(ip4));
if ((bits = inet_net_pton(AF_INET, cidr.c_str(), &ip4, sizeof(ip4))) != -1) {
std::cout << "IPv4 CIDR with " << bits << " bits" << std::endl;
} else if ((bits = inet_net_pton(AF_INET6, cidr.c_str(), &ip6, sizeof(ip6))) != -1) {
std::cout << "IPv6 CIDR with " << bits << " bits (trimmed)" << std::endl;
} else if ((bits = inet_net_pton(AF_INET6, t.c_str(), &ip6, sizeof(ip6))) != -1) {
std::cout << "IPv6 CIDR with " << bits << " bits (original)" << std::endl;
} else {
std::cout << "Unknown CIDR " << std::endl;
}
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment