Unverified Commit 7a054c5d authored by Dennis Jenkins's avatar Dennis Jenkins Committed by GitHub

Merge pull request #433 from knowledge4igor/refactoring_in_address_handling

Refactoring in Address handling and validation
parents b28ddfd5 153d9f0c
...@@ -12,8 +12,7 @@ ...@@ -12,8 +12,7 @@
#include <limits> #include <limits>
#include <sys/socket.h> #include <sys/socket.h>
#include <netdb.h>
#include <pistache/common.h>
#ifndef _KERNEL_FASTOPEN #ifndef _KERNEL_FASTOPEN
#define _KERNEL_FASTOPEN #define _KERNEL_FASTOPEN
...@@ -65,6 +64,7 @@ private: ...@@ -65,6 +64,7 @@ private:
class Port { class Port {
public: public:
Port(uint16_t port = 0); Port(uint16_t port = 0);
explicit Port(const std::string& data);
operator uint16_t() const { return port; } operator uint16_t() const { return port; }
...@@ -123,6 +123,18 @@ private: ...@@ -123,6 +123,18 @@ private:
uint16_t h; uint16_t h;
}; };
class AddressParser {
public:
explicit AddressParser(const std::string& data);
const std::string& rawHost() const;
const std::string& rawPort() const;
int family() const;
private:
std::string host_;
std::string port_;
int family_;
};
class Address { class Address {
public: public:
Address(); Address();
......
...@@ -4,19 +4,16 @@ ...@@ -4,19 +4,16 @@
Implementation of common HTTP headers described by the RFC Implementation of common HTTP headers described by the RFC
*/ */
#include <stdexcept>
#include <iterator>
#include <limits>
#include <cstring>
#include <iostream>
#include <pistache/http_header.h> #include <pistache/http_header.h>
#include <pistache/common.h> #include <pistache/common.h>
#include <pistache/http.h> #include <pistache/http.h>
#include <pistache/stream.h> #include <pistache/stream.h>
#include <arpa/inet.h>
using namespace std; #include <stdexcept>
#include <iterator>
#include <limits>
#include <cstring>
#include <iostream>
namespace Pistache { namespace Pistache {
namespace Http { namespace Http {
...@@ -340,53 +337,19 @@ Host::Host(const std::string& data) ...@@ -340,53 +337,19 @@ Host::Host(const std::string& data)
parse(data); parse(data);
} }
void void Host::parse(const std::string& data)
Host::parse(const std::string& data) { {
unsigned long pos = data.find(']'); AddressParser parser(data);
unsigned long s_pos = data.find('['); host_ = parser.rawHost();
if (pos != std::string::npos && s_pos != std::string::npos) { const std::string& port = parser.rawPort();
//IPv6 address if (port.empty())
host_ = data.substr(s_pos, pos + 1); {
try {
in6_addr addr6;
char buff6[INET6_ADDRSTRLEN + 1] = {0, };
std::copy(&host_[0], &host_[0] + host_.size(), buff6);
inet_pton(AF_INET6, buff6, &(addr6.s6_addr16));
} catch (std::runtime_error) {
throw std::invalid_argument("Invalid IPv6 address");
}
pos++;
} else {
//IPv4 address
pos = data.find(':');
if (pos == std::string::npos) {
host_ = data;
port_ = HTTP_STANDARD_PORT;
}
host_ = data.substr(0, pos);
if (host_ == "*") {
host_ = "0.0.0.0";
}
try {
in_addr addr;
char buff[INET_ADDRSTRLEN + 1] = {0, };
std::copy(&host_[0], &host_[0] + host_.size(), buff);
inet_pton(AF_INET, buff, &(addr));
} catch (std::runtime_error) {
throw std::invalid_argument("Invalid IPv4 address");
}
}
char *end;
const std::string portPart = data.substr(pos + 1);
long port;
if (pos != std::string::npos) {
port = strtol(portPart.c_str(), &end, 10);
if (port < std::numeric_limits<uint16_t>::min()|| port > std::numeric_limits<uint16_t>::max())
throw std::invalid_argument("Invalid port");
port_ = static_cast<uint16_t>(port);
} else {
port_ = HTTP_STANDARD_PORT; port_ = HTTP_STANDARD_PORT;
} }
else
{
port_ = Port(port);
}
} }
void void
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
*/ */
#include <pistache/net.h>
#include <pistache/common.h>
#include <stdexcept> #include <stdexcept>
#include <limits> #include <limits>
#include <cstring> #include <cstring>
...@@ -12,16 +15,42 @@ ...@@ -12,16 +15,42 @@
#include <ifaddrs.h> #include <ifaddrs.h>
#include <iostream> #include <iostream>
#include <pistache/net.h> namespace Pistache {
#include <pistache/common.h>
using namespace std; namespace {
bool IsIPv4HostName(const std::string& host)
{
in_addr addr;
char buff[INET_ADDRSTRLEN + 1] = {0, };
std::copy(host.begin(), host.end(), buff);
int res = inet_pton(AF_INET, buff, &addr);
return res;
}
namespace Pistache { bool IsIPv6HostName(const std::string& host)
{
in6_addr addr6;
char buff6[INET6_ADDRSTRLEN + 1] = {0, };
std::copy(host.begin(), host.end(), buff6);
int res = inet_pton(AF_INET6, buff6, &(addr6.s6_addr16));
return res;
}
}
Port::Port(uint16_t port) Port::Port(uint16_t port)
: port(port) : port(port)
{ } {}
Port::Port(const std::string& data)
{
if (data.empty())
throw std::invalid_argument("Invalid port: empty port");
char *end = 0;
long port_num = strtol(data.c_str(), &end, 10);
if (*end != 0 || port_num < Port::min() || port_num > Port::max())
throw std::invalid_argument("Invalid port: " + data);
port = static_cast<uint16_t>(port_num);
}
bool bool
Port::isReserved() const { Port::isReserved() const {
...@@ -157,6 +186,48 @@ bool Ipv6::supported() { ...@@ -157,6 +186,48 @@ bool Ipv6::supported() {
return supportsIpv6; return supportsIpv6;
} }
AddressParser::AddressParser(const std::string& data)
{
std::size_t end_pos = data.find(']');
std::size_t start_pos = data.find('[');
if (start_pos != std::string::npos &&
end_pos != std::string::npos &&
start_pos < end_pos)
{
host_ = data.substr(start_pos, end_pos + 1);
family_ = AF_INET6;
++end_pos;
}
else
{
end_pos = data.find(':');
host_ = data.substr(0, end_pos);
family_ = AF_INET;
}
if (end_pos != std::string::npos)
{
port_ = data.substr(end_pos + 1);
if (port_.empty())
throw std::invalid_argument("Invalid port");
}
}
const std::string& AddressParser::rawHost() const
{
return host_;
}
const std::string& AddressParser::rawPort() const
{
return port_;
}
int AddressParser::family() const
{
return family_;
}
Address::Address() Address::Address()
: host_("") : host_("")
, port_(0) , port_(0)
...@@ -228,46 +299,49 @@ Address::family() const { ...@@ -228,46 +299,49 @@ Address::family() const {
return family_; return family_;
} }
void void Address::init(const std::string& addr)
Address::init(const std::string& addr) { {
unsigned long pos = addr.find(']'); AddressParser parser(addr);
unsigned long s_pos = addr.find('['); family_ = parser.family();
if (pos != std::string::npos && s_pos != std::string::npos) { if (family_ == AF_INET6)
//IPv6 address {
host_ = addr.substr(s_pos+1, pos-1); const std::string& raw_host = parser.rawHost();
family_ = AF_INET6; assert(raw_host.size() > 2);
try { host_ = addr.substr(1, raw_host.size() - 2);
in6_addr addr6;
inet_pton(AF_INET6, host_.c_str(), &(addr6.s6_addr16)); if (!IsIPv6HostName(host_))
} catch (std::runtime_error) { {
throw std::invalid_argument("Invalid IPv6 address"); throw std::invalid_argument("Invalid IPv6 address");
} }
pos++; }
} else { else if (family_ == AF_INET)
//IPv4 address {
pos = addr.find(':'); host_ = parser.rawHost();
if (pos == std::string::npos) if (host_ == "*")
throw std::invalid_argument("Invalid address"); {
host_ = addr.substr(0, pos);
family_ = AF_INET;
if (host_ == "*") {
host_ = "0.0.0.0"; host_ = "0.0.0.0";
} }
try { else if (host_ == "localhost")
in_addr addr; {
inet_pton(AF_INET, host_.c_str(), &(addr)); host_ = "127.0.0.1";
} catch (std::runtime_error) { }
if (!IsIPv4HostName(host_))
{
throw std::invalid_argument("Invalid IPv4 address"); throw std::invalid_argument("Invalid IPv4 address");
} }
} }
char *end; else
const std::string portPart = addr.substr(pos + 1); { assert(false); }
const std::string& portPart = parser.rawPort();
if (portPart.empty()) if (portPart.empty())
throw std::invalid_argument("Invalid port"); throw std::invalid_argument("Invalid port");
char *end = 0;
long port = strtol(portPart.c_str(), &end, 10); long port = strtol(portPart.c_str(), &end, 10);
if (*end != 0 || port < Port::min() || port > Port::max()) if (*end != 0 || port < Port::min() || port > Port::max())
throw std::invalid_argument("Invalid port"); throw std::invalid_argument("Invalid port");
port_ = static_cast<uint16_t>(port); port_ = Port(port);
} }
Error::Error(const char* message) Error::Error(const char* message)
......
...@@ -256,6 +256,10 @@ TEST(headers_test, host) { ...@@ -256,6 +256,10 @@ TEST(headers_test, host) {
ASSERT_EQ(host.host(), "www.w3.org"); ASSERT_EQ(host.host(), "www.w3.org");
ASSERT_EQ(host.port(), 80); ASSERT_EQ(host.port(), 80);
host.parse("www.example.com:8080");
ASSERT_EQ(host.host(), "www.example.com");
ASSERT_EQ(host.port(), 8080);
host.parse("localhost:8080"); host.parse("localhost:8080");
ASSERT_EQ(host.host(), "localhost"); ASSERT_EQ(host.host(), "localhost");
ASSERT_EQ(host.port(), 8080); ASSERT_EQ(host.port(), 8080);
......
...@@ -90,9 +90,24 @@ TEST(net_test, invalid_address) ...@@ -90,9 +90,24 @@ TEST(net_test, invalid_address)
ASSERT_THROW(Address("127.0.0.1:"), std::invalid_argument); ASSERT_THROW(Address("127.0.0.1:"), std::invalid_argument);
ASSERT_THROW(Address("127.0.0.1:-10"), std::invalid_argument); ASSERT_THROW(Address("127.0.0.1:-10"), std::invalid_argument);
/* Due to an error in GLIBC these tests don't fail as expected, further research needed */ ASSERT_THROW(Address("[GGGG:GGGG:GGGG:GGGG:GGGG:GGGG:GGGG:GGGG]:8080");, std::invalid_argument);
// ASSERT_THROW(Address("[GGGG:GGGG:GGGG:GGGG:GGGG:GGGG:GGGG:GGGG]:8080");, std::invalid_argument); ASSERT_THROW(Address("[::GGGG]:8080");, std::invalid_argument);
// ASSERT_THROW(Address("[::GGGG]:8080");, std::invalid_argument); ASSERT_THROW(Address("256.256.256.256:8080");, std::invalid_argument);
// ASSERT_THROW(Address("256.256.256.256:8080");, std::invalid_argument); ASSERT_THROW(Address("1.0.0.256:8080");, std::invalid_argument);
// ASSERT_THROW(Address("1.0.0.256:8080");, std::invalid_argument); }
TEST(net_test, address_parser)
{
AddressParser ap1("127.0.0.1:80");
ASSERT_EQ(ap1.rawHost(), "127.0.0.1");
ASSERT_EQ(ap1.rawPort(), "80");
ASSERT_EQ(ap1.family(), AF_INET);
AddressParser ap2("[2001:0DB8:AABB:CCDD:EEFF:0011:2233:4455]:8080");
ASSERT_EQ(ap2.rawHost(), "[2001:0DB8:AABB:CCDD:EEFF:0011:2233:4455]");
ASSERT_EQ(ap2.rawPort(), "8080");
ASSERT_EQ(ap2.family(), AF_INET6);
ASSERT_THROW(AddressParser("127.0.0.1:");, std::invalid_argument);
ASSERT_THROW(AddressParser("[::]:");, std::invalid_argument);
} }
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