Created
May 28, 2015 09:49
-
-
Save cypres/050fcd418f2228fb399d to your computer and use it in GitHub Desktop.
Trim a CIDR string to make it compatible with inet_net_pton
This file contains hidden or 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
#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