Unverified Commit d55d41bf authored by Dennis Jenkins's avatar Dennis Jenkins Committed by GitHub

Merge pull request #542 from hydratim/hydratim-network-cleanup1

Some clean-up of the net primitives
parents 47037a2a e65daea3
......@@ -83,45 +83,35 @@ private:
uint16_t port;
};
class Ipv4 {
public:
Ipv4(uint8_t a, uint8_t b, uint8_t c, uint8_t d);
static Ipv4 any();
static Ipv4 loopback();
std::string toString() const;
void toNetwork(in_addr_t*) const;
class IP {
private:
uint8_t a;
uint8_t b;
uint8_t c;
uint8_t d;
};
class Ipv6 {
int port;
int family;
union {
struct sockaddr_in addr;
struct sockaddr_in6 addr6;
};
public:
Ipv6(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h);
static Ipv6 any();
static Ipv6 loopback();
IP();
IP(uint8_t a, uint8_t b, uint8_t c, uint8_t d);
IP(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h);
IP(struct sockaddr *);
static IP any();
static IP loopback();
static IP any(bool ipv6);
static IP loopback(bool ipv6);
int getFamily() const;
int getPort() const;
std::string toString() const;
void toNetwork(in6_addr*) const;
// Returns 'true' if the kernel/libc support IPV6, false if not.
void toNetwork(in_addr_t*) const;
void toNetwork(struct in6_addr*) const;
// Returns 'true' if the system has IPV6 support, false if not.
static bool supported();
private:
uint16_t a;
uint16_t b;
uint16_t c;
uint16_t d;
uint16_t e;
uint16_t f;
uint16_t g;
uint16_t h;
};
using Ipv4 = IP;
using Ipv6 = IP;
class AddressParser {
public:
......@@ -131,20 +121,21 @@ public:
bool hasColon() const;
int family() const;
private:
std::string host_;
std::string port_;
bool hasColon_ = false;
int family_ = 0;
};
class Address {
public:
Address();
Address(std::string host, Port port);
Address(std::string addr);
Address(const char* addr);
Address(Ipv4 ip, Port port);
Address(Ipv6 ip, Port port);
Address(IP ip, Port port);
Address(const Address& other) = default;
Address(Address&& other) = default;
......@@ -160,9 +151,8 @@ public:
private:
void init(const std::string& addr);
std::string host_;
IP ip_;
Port port_;
int family_ = AF_INET;
};
class Error : public std::runtime_error {
......
......@@ -70,101 +70,117 @@ Port::toString() const {
return std::to_string(port);
}
Ipv4::Ipv4(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
: a(a)
, b(b)
, c(c)
, d(d)
{ }
IP::IP() {
family = AF_INET;
addr = {0};
addr.sin_family = AF_INET;
uint8_t buff[INET_ADDRSTRLEN+1] = {0,0,0,0};
memcpy(&addr.sin_addr.s_addr, buff, INET_ADDRSTRLEN);
}
Ipv4
Ipv4::any() {
return Ipv4(0, 0, 0, 0);
IP::IP(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
family = AF_INET;
addr = {0};
addr.sin_family = AF_INET;
uint8_t buff[INET_ADDRSTRLEN+1] = {a,b,c,d};
memcpy(&addr.sin_addr.s_addr, buff, INET_ADDRSTRLEN);
}
Ipv4
Ipv4::loopback() {
return Ipv4(127, 0, 0, 1);
IP::IP(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h){
family = AF_INET6;
addr6 = { 0 };
addr6.sin6_family = AF_INET6;
uint16_t buff[9] {a,b,c,d,e,f,g,h, '\0'};
uint16_t remap[9] = {0,0,0,0,0,0,0,0, '\0'};
if ( htonl(1) != 1 ) {
for (int i = 0; i<8; i++) {
uint16_t x = buff[i];
uint16_t y = htons(x);
remap[i] = y;
}
} else {
memcpy(&remap, &buff, sizeof(remap));
}
memcpy(&addr6.sin6_addr.s6_addr16, &remap, 8*sizeof(uint16_t));
}
IP::IP(struct sockaddr* _addr) {
if (_addr->sa_family == AF_INET) {
struct sockaddr_in *in_addr = reinterpret_cast<struct sockaddr_in *>(_addr);
family = AF_INET;
port = in_addr->sin_port;
memcpy(&(addr.sin_addr.s_addr), &(in_addr->sin_addr.s_addr), sizeof(in_addr_t));
} else if (_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *in_addr = reinterpret_cast<struct sockaddr_in6 *>(_addr);
family = AF_INET6;
port = in_addr->sin6_port;
memcpy(&(addr6.sin6_addr.s6_addr16), &(in_addr->sin6_addr.s6_addr16), 8*sizeof(uint16_t));
}
}
std::string
Ipv4::toString() const {
// Use the built-in ipv4 string length from arpa/inet.h
char buff[INET_ADDRSTRLEN+1];
in_addr_t addr;
toNetwork(&addr);
// Convert the network format address into display format
inet_ntop(AF_INET, &addr, buff, INET_ADDRSTRLEN);
return std::string(buff);
IP
IP::any() {
return IP(0, 0, 0, 0);
}
void Ipv4::toNetwork(in_addr_t *addr) const {
// Bitshift the bytes into an in_addr_t (a single 32bit unsigned int);
*addr = htonl( (uint32_t)(a<<24) | (uint32_t)(b<<16) | (uint32_t)(c<<8) | (uint32_t)d );;
IP
IP::any(bool is_ipv6) {
if (is_ipv6) {
return IP(0, 0, 0, 0, 0, 0, 0, 0);
} else {
return IP(0, 0, 0, 0);
}
}
Ipv6::Ipv6(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h)
: a(a)
, b(b)
, c(c)
, d(d)
, e(e)
, f(f)
, g(g)
, h(h)
{ }
IP
IP::loopback(){
return IP(127, 0, 0, 1);
}
Ipv6
Ipv6::any() {
return Ipv6(0, 0, 0, 0, 0, 0, 0, 0);
IP
IP::loopback(bool is_ipv6) {
if (is_ipv6) {
return IP(0, 0, 0, 0, 0, 0, 0, 1);
} else {
return IP(127, 0, 0, 1);
}
}
int IP::getFamily() const {
return family;
}
Ipv6
Ipv6::loopback() {
return Ipv6(0, 0, 0, 0, 0, 0, 0, 1);
int IP::getPort() const {
return port;
}
std::string
Ipv6::toString() const {
// Use the built-in ipv6 string length from arpa/inet.h
char buff6[INET6_ADDRSTRLEN+1];
in6_addr addr;
toNetwork(&addr);
inet_ntop(AF_INET6, &addr, buff6, INET6_ADDRSTRLEN);
IP::toString() const {
char buff[INET6_ADDRSTRLEN+1];
if (family == AF_INET) {
in_addr_t addr_;
toNetwork(&addr_);
inet_ntop(AF_INET, &addr_, buff, INET_ADDRSTRLEN);
} else if (family == AF_INET6) {
struct in6_addr addr_ = in6addr_any;
toNetwork(&addr_);
inet_ntop(AF_INET6, &addr_, buff, INET6_ADDRSTRLEN);
}
return std::string(buff);
}
return std::string(buff6);
void IP::toNetwork(in_addr_t *_addr) const {
memcpy(_addr, &addr.sin_addr.s_addr, sizeof(uint32_t));
}
void Ipv6::toNetwork(in6_addr *addr6) const {
uint16_t temp_ip6[8] = {a, b, c, d, e, f, g, h};
uint16_t remap_ip6[8] = {0};
// If native endianness is little-endian swap the bytes, otherwise just copy them into the new array
if ( htonl(1) != 1 ) {
for (uint16_t i = 0; i<8; i++) {
uint16_t x = temp_ip6[i];
uint16_t y = htons(x);
remap_ip6[i] = y;
}
} else {
memcpy(remap_ip6, temp_ip6, 16);
}
// Copy the bytes into the in6_addr struct
memcpy(addr6->s6_addr16, remap_ip6, 16);
void IP::toNetwork(struct in6_addr *out) const {
memcpy(&out->s6_addr16, &(addr6.sin6_addr.s6_addr16), 8*sizeof(uint16_t));
}
bool Ipv6::supported() {
bool IP::supported() {
struct ifaddrs *ifaddr = nullptr;
struct ifaddrs *ifa = nullptr;
int family, n;
int addr_family, n;
bool supportsIpv6 = false;
if (getifaddrs(&ifaddr) == -1) {
......@@ -176,8 +192,8 @@ bool Ipv6::supported() {
continue;
}
family = ifa->ifa_addr->sa_family;
if (family == AF_INET6) {
addr_family = ifa->ifa_addr->sa_family;
if (addr_family == AF_INET6) {
supportsIpv6 = true;
continue;
}
......@@ -187,6 +203,7 @@ bool Ipv6::supported() {
return supportsIpv6;
}
AddressParser::AddressParser(const std::string& data)
{
std::size_t end_pos = data.find(']');
......@@ -245,9 +262,11 @@ int AddressParser::family() const
}
Address::Address()
: host_("")
, port_(0)
{ }
{
port_ = Port(0);
ip_ = IP::any();
}
Address::Address(std::string host, Port port)
{
......@@ -268,41 +287,25 @@ Address::Address(const char* addr)
init(std::string(addr));
}
Address::Address(Ipv4 ip, Port port)
: host_(ip.toString())
, port_(port)
, family_(AF_INET)
{ }
Address::Address(Ipv6 ip, Port port)
: host_(ip.toString())
Address::Address(IP ip, Port port)
: ip_(ip)
, port_(port)
, family_(AF_INET6)
{ }
Address
Address::fromUnix(struct sockaddr* addr) {
if (addr->sa_family == AF_INET) {
struct sockaddr_in *in_addr = reinterpret_cast<struct sockaddr_in *>(addr);
char host[INET_ADDRSTRLEN+1];
inet_ntop(AF_INET, &(in_addr->sin_addr), host, INET_ADDRSTRLEN);
int port = ntohs(in_addr->sin_port);
assert(addr);
return Address(host, port);
} else if (addr->sa_family == AF_INET6) {
struct sockaddr_in6 *in_addr = reinterpret_cast<struct sockaddr_in6 *>(addr);
char host[INET6_ADDRSTRLEN+1];
inet_ntop(AF_INET6, &(in_addr->sin6_addr), host, INET6_ADDRSTRLEN);
int port = ntohs(in_addr->sin6_port);
if ((addr->sa_family==AF_INET) or (addr->sa_family==AF_INET6)) {
IP ip = IP(addr);
Port port = Port(ip.getPort());
assert(addr);
return Address(host, port);
return Address(ip, port);
}
throw Error("Not an IP socket");
}
std::string
Address::host() const {
return host_;
return ip_.toString();
}
Port
......@@ -312,27 +315,40 @@ Address::port() const {
int
Address::family() const {
return family_;
return ip_.getFamily();
}
void Address::init(const std::string& addr)
{
AddressParser parser(addr);
family_ = parser.family();
int family_ = parser.family();
std::string host_;
if (family_ == AF_INET6)
{
const std::string& raw_host = parser.rawHost();
assert(raw_host.size() > 2);
host_ = addr.substr(1, raw_host.size() - 2);
if (!IsIPv6HostName(host_))
{
throw std::invalid_argument("Invalid IPv6 address");
}
char buff[INET6_ADDRSTRLEN+1] = {};
memcpy(buff, host_.c_str(), INET6_ADDRSTRLEN);
struct in6_addr addr;
inet_pton(AF_INET6, buff, &addr);
struct sockaddr_in6 s_addr = {0};
s_addr.sin6_family = AF_INET6;
memcpy(&(s_addr.sin6_addr.s6_addr16), &addr.s6_addr16, 8*sizeof(uint16_t));
ip_ = IP(reinterpret_cast<struct sockaddr *>(&s_addr));
}
else if (family_ == AF_INET)
{
host_ = parser.rawHost();
if (host_ == "*")
{
host_ = "0.0.0.0";
......@@ -341,8 +357,9 @@ void Address::init(const std::string& addr)
{
host_ = "127.0.0.1";
}
struct hostent *hp = ::gethostbyname(host_.c_str());
if (hp)
{
struct in_addr **addr_list;
......@@ -354,11 +371,20 @@ void Address::init(const std::string& addr)
break;
}
}
if (!IsIPv4HostName(host_))
{
throw std::invalid_argument("Invalid IPv4 address");
}
char buff[INET_ADDRSTRLEN+1] = {};
memcpy(buff, host_.c_str(), INET_ADDRSTRLEN);
struct in_addr addr;
inet_pton(AF_INET, buff, &addr);
struct sockaddr_in s_addr = {0};
s_addr.sin_family = AF_INET;
memcpy(&(s_addr.sin_addr.s_addr), &addr.s_addr, sizeof(uint32_t));
ip_ = IP(reinterpret_cast<struct sockaddr *>(&s_addr));
}
else
{ assert(false); }
......
......@@ -30,65 +30,111 @@ TEST(net_test, address_creation)
{
Address address1("127.0.0.1:8080");
ASSERT_EQ(address1.host(), "127.0.0.1");
ASSERT_EQ(address1.family(), AF_INET);
ASSERT_EQ(address1.port(), 8080);
std::string addr = "127.0.0.1";
Address address2(addr, Port(8080));
ASSERT_EQ(address2.host(), "127.0.0.1");
ASSERT_EQ(address2.family(), AF_INET);
ASSERT_EQ(address2.port(), 8080);
Address address3(Ipv4(127, 0, 0, 1), Port(8080));
ASSERT_EQ(address3.host(), "127.0.0.1");
ASSERT_EQ(address3.family(), AF_INET);
ASSERT_EQ(address3.port(), 8080);
Address address4(Ipv4::any(), Port(8080));
ASSERT_EQ(address4.host(), "0.0.0.0");
ASSERT_EQ(address4.family(), AF_INET);
ASSERT_EQ(address4.port(), 8080);
Address address5("*:8080");
ASSERT_EQ(address5.host(), "0.0.0.0");
ASSERT_EQ(address5.family(), AF_INET);
ASSERT_EQ(address5.port(), 8080);
Address address6("[::1]:8080");
ASSERT_EQ(address6.host(), "::1");
ASSERT_EQ(address6.family(), AF_INET6);
ASSERT_EQ(address6.port(), 8080);
std::string addr2 = "[::1]";
Address address7(addr2, Port(8080));
ASSERT_EQ(address7.host(), "::1");
ASSERT_EQ(address7.family(), AF_INET6);
ASSERT_EQ(address7.port(), 8080);
Address address8(Ipv6(0, 0, 0, 0, 0, 0, 0, 1), Port(8080));
ASSERT_EQ(address8.host(), "::1");
ASSERT_EQ(address8.family(), AF_INET6);
ASSERT_EQ(address8.port(), 8080);
Address address9(Ipv6::any(), Port(8080));
Address address9(Ipv6::any(true), Port(8080));
ASSERT_EQ(address9.host(), "::");
ASSERT_EQ(address9.family(), AF_INET6);
ASSERT_EQ(address9.port(), 8080);
Address address10("[::]:8080");
ASSERT_EQ(address10.host(), "::");
ASSERT_EQ(address10.family(), AF_INET6);
ASSERT_EQ(address10.port(), 8080);
Address address11("[2001:0DB8:AABB:CCDD:EEFF:0011:2233:4455]:8080");
ASSERT_EQ(address11.host(), "2001:0DB8:AABB:CCDD:EEFF:0011:2233:4455");
ASSERT_EQ(address11.host(), "2001:db8:aabb:ccdd:eeff:11:2233:4455");
ASSERT_EQ(address11.family(), AF_INET6);
ASSERT_EQ(address11.port(), 8080);
Address address12(Ipv4::loopback(), Port(8080));
ASSERT_EQ(address12.host(), "127.0.0.1");
ASSERT_EQ(address12.family(), AF_INET);
ASSERT_EQ(address12.port(), 8080);
Address address13(Ipv6::loopback(), Port(8080));
Address address13(Ipv6::loopback(true), Port(8080));
ASSERT_EQ(address13.host(), "::1");
ASSERT_EQ(address13.family(), AF_INET6);
ASSERT_EQ(address13.port(), 8080);
Address address14("127.0.0.1");
ASSERT_EQ(address14.host(), "127.0.0.1");
ASSERT_EQ(address14.family(), AF_INET);
ASSERT_EQ(address14.port(), 80);
Address address15("www.example.com");
ASSERT_EQ(address15.host(), "93.184.216.34");
ASSERT_EQ(address15.family(), AF_INET);
ASSERT_EQ(address15.port(), 80);
Address address16(IP(127, 0, 0, 1), Port(8080));
ASSERT_EQ(address16.host(), "127.0.0.1");
ASSERT_EQ(address16.family(), AF_INET);
ASSERT_EQ(address16.port(), 8080);
Address address17(IP::any(), Port(8080));
ASSERT_EQ(address17.host(), "0.0.0.0");
ASSERT_EQ(address17.family(), AF_INET);
ASSERT_EQ(address17.port(), 8080);
Address address18(IP(2, 0, 0, 0, 0, 0, 0, 1), Port(8080));
ASSERT_EQ(address18.host(), "2::1");
ASSERT_EQ(address18.family(), AF_INET6);
ASSERT_EQ(address18.port(), 8080);
Address address19(IP::any(true), Port(8080));
ASSERT_EQ(address19.host(), "::");
ASSERT_EQ(address19.family(), AF_INET6);
ASSERT_EQ(address19.port(), 8080);
Address address20(IP::loopback(true), Port(8080));
ASSERT_EQ(address20.host(), "::1");
ASSERT_EQ(address20.family(), AF_INET6);
ASSERT_EQ(address20.port(), 8080);
Address address21(IP::loopback(), Port(8080));
ASSERT_EQ(address21.host(), "127.0.0.1");
ASSERT_EQ(address21.family(), AF_INET);
ASSERT_EQ(address21.port(), 8080);
}
TEST(net_test, invalid_address)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment