8#include <botan/internal/uri.h>
10#include <botan/assert.h>
11#include <botan/exceptn.h>
12#include <botan/internal/fmt.h>
13#include <botan/internal/parsing.h>
15#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
16 #include <arpa/inet.h>
17 #include <netinet/in.h>
18 #include <sys/socket.h>
19#elif defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
23#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
29bool is_domain_name(std::string_view domain) {
33 }
catch(Decoding_Error&) {
38bool is_ipv4(std::string_view ip) {
39 std::string ip_str(ip);
40 sockaddr_storage inaddr;
41 return !!inet_pton(AF_INET, ip_str.c_str(), &inaddr);
44bool is_ipv6(std::string_view ip) {
45 std::string ip_str(ip);
46 sockaddr_storage in6addr;
47 return !!inet_pton(AF_INET6, ip_str.c_str(), &in6addr);
50uint16_t parse_port_number(
const char* func_name, std::string_view uri,
size_t pos) {
51 if(pos == std::string::npos || uri.empty()) {
59 for(
char c : uri.substr(pos + 1)) {
60 size_t digit = c -
'0';
62 throw Invalid_Argument(
fmt(
"URI::{} invalid port field in {}", func_name, uri));
64 port = port * 10 + (c -
'0');
66 throw Invalid_Argument(
fmt(
"URI::{} invalid port field in {}", func_name, uri));
70 return static_cast<uint16_t
>(port);
79 const auto port_pos = uri.find(
':');
80 if(port_pos != std::string::npos) {
81 port = parse_port_number(
"from_domain", uri, port_pos);
83 const auto domain = uri.substr(0, port_pos);
85 throw Invalid_Argument(
"URI::from_domain domain name should not be IP address");
87 if(!is_domain_name(domain)) {
88 throw Invalid_Argument(
fmt(
"URI::from_domain domain name '{}' not valid", domain));
97 const auto port_pos = uri.find(
':');
98 const uint16_t
port = parse_port_number(
"from_ipv4", uri, port_pos);
99 const auto ip = uri.substr(0, port_pos);
101 throw Invalid_Argument(
"URI::from_ipv4: Invalid IPv4 specifier");
109 const auto port_pos = uri.find(
']');
110 const bool with_braces = (port_pos != std::string::npos);
111 if((uri[0] ==
'[') != with_braces) {
112 throw Invalid_Argument(
"URI::from_ipv6 Invalid IPv6 address with mismatch braces");
116 if(with_braces && (uri.size() > port_pos + 1)) {
117 if(uri[port_pos + 1] !=
':') {
118 throw Invalid_Argument(
"URI::from_ipv6 Invalid IPv6 address");
121 port = parse_port_number(
"from_ipv6", uri, port_pos + 1);
123 const auto ip = uri.substr((with_braces ? 1 : 0), port_pos - with_braces);
125 throw Invalid_Argument(
"URI::from_ipv6 URI has invalid IPv6 address");
135 }
catch(Invalid_Argument&) {}
139 }
catch(Invalid_Argument&) {}
147 return "[" + m_host +
"]:" + std::to_string(m_port);
149 return m_host +
":" + std::to_string(m_port);
#define BOTAN_ARG_CHECK(expr, msg)
static URI from_ipv4(std::string_view uri)
static URI from_domain(std::string_view uri)
static URI from_ipv6(std::string_view uri)
URI(Type type, std::string_view host, uint16_t port)
static URI from_any(std::string_view uri)
std::string to_string() const
std::string fmt(std::string_view format, const T &... args)
std::string check_and_canonicalize_dns_name(std::string_view name)