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 @@
#include <limits>
#include <sys/socket.h>
#include <pistache/common.h>
#include <netdb.h>
#ifndef _KERNEL_FASTOPEN
#define _KERNEL_FASTOPEN
......@@ -65,6 +64,7 @@ private:
class Port {
public:
Port(uint16_t port = 0);
explicit Port(const std::string& data);
operator uint16_t() const { return port; }
......@@ -123,6 +123,18 @@ private:
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 {
public:
Address();
......
......@@ -4,19 +4,16 @@
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/common.h>
#include <pistache/http.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 Http {
......@@ -340,52 +337,18 @@ Host::Host(const std::string& data)
parse(data);
}
void
Host::parse(const std::string& data) {
unsigned long pos = data.find(']');
unsigned long s_pos = data.find('[');
if (pos != std::string::npos && s_pos != std::string::npos) {
//IPv6 address
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;
void Host::parse(const std::string& data)
{
AddressParser parser(data);
host_ = parser.rawHost();
const std::string& port = parser.rawPort();
if (port.empty())
{
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;
else
{
port_ = Port(port);
}
}
......
......@@ -3,6 +3,9 @@
*/
#include <pistache/net.h>
#include <pistache/common.h>
#include <stdexcept>
#include <limits>
#include <cstring>
......@@ -12,16 +15,42 @@
#include <ifaddrs.h>
#include <iostream>
#include <pistache/net.h>
#include <pistache/common.h>
namespace Pistache {
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)
{ }
{}
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
Port::isReserved() const {
......@@ -157,6 +186,48 @@ bool Ipv6::supported() {
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()
: host_("")
, port_(0)
......@@ -228,46 +299,49 @@ Address::family() const {
return family_;
}
void
Address::init(const std::string& addr) {
unsigned long pos = addr.find(']');
unsigned long s_pos = addr.find('[');
if (pos != std::string::npos && s_pos != std::string::npos) {
//IPv6 address
host_ = addr.substr(s_pos+1, pos-1);
family_ = AF_INET6;
try {
in6_addr addr6;
inet_pton(AF_INET6, host_.c_str(), &(addr6.s6_addr16));
} catch (std::runtime_error) {
void Address::init(const std::string& addr)
{
AddressParser parser(addr);
family_ = parser.family();
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");
}
pos++;
} else {
//IPv4 address
pos = addr.find(':');
if (pos == std::string::npos)
throw std::invalid_argument("Invalid address");
host_ = addr.substr(0, pos);
family_ = AF_INET;
if (host_ == "*") {
}
else if (family_ == AF_INET)
{
host_ = parser.rawHost();
if (host_ == "*")
{
host_ = "0.0.0.0";
}
try {
in_addr addr;
inet_pton(AF_INET, host_.c_str(), &(addr));
} catch (std::runtime_error) {
else if (host_ == "localhost")
{
host_ = "127.0.0.1";
}
if (!IsIPv4HostName(host_))
{
throw std::invalid_argument("Invalid IPv4 address");
}
}
char *end;
const std::string portPart = addr.substr(pos + 1);
else
{ assert(false); }
const std::string& portPart = parser.rawPort();
if (portPart.empty())
throw std::invalid_argument("Invalid port");
char *end = 0;
long port = strtol(portPart.c_str(), &end, 10);
if (*end != 0 || port < Port::min() || port > Port::max())
throw std::invalid_argument("Invalid port");
port_ = static_cast<uint16_t>(port);
port_ = Port(port);
}
Error::Error(const char* message)
......
......@@ -256,6 +256,10 @@ TEST(headers_test, host) {
ASSERT_EQ(host.host(), "www.w3.org");
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");
ASSERT_EQ(host.host(), "localhost");
ASSERT_EQ(host.port(), 8080);
......
......@@ -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:-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]: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("[GGGG:GGGG:GGGG:GGGG:GGGG:GGGG:GGGG: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("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