sources/network/ipaddress.cpp

changeset 186
9330b93d9946
parent 185
e83ec58cc458
child 188
5fc32e4b2a8c
--- a/sources/network/ipaddress.cpp	Wed Jan 27 14:16:58 2021 +0200
+++ b/sources/network/ipaddress.cpp	Wed Jan 27 18:55:03 2021 +0200
@@ -46,133 +46,139 @@
 typedef struct addrinfo AddrInfo;
 #endif
 
-// -----------------------------------------------------------------------------
-//
-std::string IPAddress::to_string (WithPort withport) const
+net::octet_t net::ip_octet(const net::ip_address& address, unsigned char n)
 {
-	std::string val;
+	assert(n < 4);
+	return (address.host >> ((3 - n) * 8)) & 0xFF;
+}
 
-	if (withport == WITH_PORT)
-		val = sprintf ("%u.%u.%u.%u:%u", octet (0), octet (1), octet (2), octet (3), port);
-	else
-		val = sprintf ("%u.%u.%u.%u", octet (0), octet (1), octet (2), octet (3));
+void net::ip_set_octet(net::ip_address* address, unsigned char n, net::octet_t octet)
+{
+	// TODO: make a big-endian version
+	assert(n < 4);
+	address->host &= ~(0xFF << (3 - n) * 8);
+	address->host |= octet << ((3 - n) * 8);
+}
 
+std::string net::ip_address_to_string(const net::ip_address& address)
+{
+	const auto octet = [&](unsigned char n) { return ip_octet(address, n); };
+	std::string val = sprintf ("%u.%u.%u.%u:%u", octet(0), octet(1), octet(2), octet(3), address.port);
 	return val;
 }
 
-// -----------------------------------------------------------------------------
-//
-unsigned char IPAddress::octet (int n) const
+int net::ip_compare(const net::ip_address &one, const net::ip_address &other)
 {
-	return (host >> ((3 - n) * 8)) & 0xFF;
-}
-
-// -----------------------------------------------------------------------------
-//
-void IPAddress::set_octet (int n, unsigned char oct)
-{
-	// TODO: make a big-endian version
-	host &= ~(0xFF << (3 - n) * 8);
-	host |= oct << ((3 - n) * 8);
+	if (one.host != other.host)
+	{
+		return one.host - other.host;
+	}
+	return one.port - other.port;
 }
 
-// -----------------------------------------------------------------------------
-//
-bool IPAddress::compare (const IPAddress& other) const
+bool net::ip_address::operator<(const ip_address& other) const
 {
-	for (int i = 0; i < 4; i += 1)
-	{
-		if (octet (i) != other.octet (i))
-			return false;
-	}
-
-	if (port != 0
-		and other.port != 0
-		and port != other.port)
-	{
-		return false;
-	}
-
-	return true;
+	return ip_compare(*this, other) < 0;
 }
 
-// -----------------------------------------------------------------------------
-//
-bool IPAddress::operator< (const IPAddress& other) const
+bool net::ip_address::operator==(const net::ip_address &other) const
 {
-	for (int i = 0; i < 4; i += 1)
-	{
-		if (octet (i) != other.octet (i))
-			return octet (i) < other.octet (i);
-	}
-
-	return port < other.port;
+	return this->host == other.host and this->port == other.port;
 }
 
 // -----------------------------------------------------------------------------
 //
-sockaddr_in IPAddress::to_sockaddr_in() const
+sockaddr_in net::ip_address_to_sockaddr_in(const net::ip_address& address)
 {
 	sockaddr_in claddr;
 	memset (&claddr, 0, sizeof claddr);
-	claddr.sin_addr.s_addr = htonl (host);
-	claddr.sin_port = htons (port);
+	claddr.sin_addr.s_addr = htonl(address.host);
+	claddr.sin_port = htons(address.port);
 	claddr.sin_family = AF_INET;
 	return claddr;
 }
 
-// -----------------------------------------------------------------------------
-//
-IPAddress IPAddress::from_string (std::string input)
+std::optional<unsigned short> net::ip_parse_port(const char* port_string, std::ostream& errorStream)
 {
-	unsigned int parts[4];
-	int colonpos = input.find (":");
-	std::string addressString = colonpos == -1 ? input : mid(input, 0, colonpos);
-	IPAddress value;
-
-	// Try scanf the IPv4 host first
-	if (sscanf(addressString.data(), "%u.%u.%u.%u", &parts[0], &parts[1], &parts[2], &parts[3]))
+	std::optional<long> opt = to_int(port_string);
+	if (opt.has_value())
 	{
-		for (int i = 0; i < 4; i += 1)
-			value.set_octet (i, parts[i]);
+		if (*opt >= 0 and *opt < 65536)
+		{
+			return static_cast<unsigned short>(opt.value());
+		}
+		else
+		{
+			return {};
+			errorStream << "port is not in range"s;
+		}
 	}
 	else
 	{
-		// Possibly a hostname, try resolve it
-		value = IPAddress::resolve (addressString);
+		return {};
+		errorStream << "could not parse port"s;
 	}
+}
 
-	if (colonpos != -1)
+std::optional<net::ip_address> net::ip_scan_octets(const char* address_string)
+{
+	std::optional<net::ip_address> value;
+	net::octet_t parts[4];
+	if (std::sscanf(address_string, "%hhu.%hhu.%hhu.%hhu", &parts[0], &parts[1], &parts[2], &parts[3]))
 	{
-		std::optional<long> opt = to_int(mid(input, colonpos + 1, -1).data());
-		if (opt.has_value())
-		{
-			value.port = opt.value();
-		}
+		value = net::ip_address{};
+		for (unsigned char i = 0; i < 4; i += 1)
+			ip_set_octet(&*value, i, parts[i]);
 	}
-
 	return value;
 }
 
-// -----------------------------------------------------------------------------
-//
-IPAddress IPAddress::resolve (std::string node)
+std::optional<net::ip_address> net::ip_resolve_hostname(const std::string& node, std::ostream& errorStream)
 {
 	AddrInfo hints;
 	AddrInfo* lookup;
-	memset (&hints, 0, sizeof hints);
+	memset(&hints, 0, sizeof hints);
 	hints.ai_family = AF_INET;
-
-	if (getaddrinfo(node.data(), nullptr, &hints, &lookup) != 0)
-		throw StringParseError ("unknown host " + node);
-
-	IPAddress result;
-	assert (lookup != nullptr);
-	sockaddr_in* addr = reinterpret_cast<sockaddr_in*> (lookup[0].ai_addr);
-	result.host = ntohl (addr->sin_addr.s_addr);
-	result.port = ntohs (addr->sin_port);
-	freeaddrinfo (lookup);
+	std::optional<net::ip_address> result = {};
+	if (::getaddrinfo(node.data(), nullptr, &hints, &lookup) != 0)
+	{
+		errorStream << "unknown host "s + node;
+	}
+	else
+	{
+		assert (lookup != nullptr);
+		sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(lookup[0].ai_addr);
+		result = net::ip_address{ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port)};
+		::freeaddrinfo(lookup);
+	}
 	return result;
 }
 
+std::optional<net::ip_address> net::ip_resolve(const std::string& input_string, std::ostream& errorStream)
+{
+	int colonpos = input_string.find(":");
+	std::string addressString = colonpos == -1 ? input_string : mid(input_string, 0, colonpos);
+	std::optional<net::ip_address> value;
+	// Try scanf the IPv4 host first
+	value = ip_scan_octets(addressString.data());
+	if (not value.has_value())
+	{
+		// Possibly a hostname, try resolve it
+		value = ip_resolve_hostname(addressString, errorStream);
+	}
+	if (value.has_value() and colonpos != -1)
+	{
+		std::optional<unsigned short> port_opt = ip_parse_port(&input_string.data()[colonpos + 1], errorStream);
+		if (port_opt.has_value())
+		{
+			value->port = *port_opt;
+		}
+		else
+		{
+			value.reset();
+		}
+	}
+	return value;
+}
+
 END_ZFC_NAMESPACE
\ No newline at end of file

mercurial