summaryrefslogtreecommitdiffstats
path: root/comm/third_party/botan/src/lib/utils/socket/uri.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/third_party/botan/src/lib/utils/socket/uri.cpp')
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/uri.cpp188
1 files changed, 188 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/utils/socket/uri.cpp b/comm/third_party/botan/src/lib/utils/socket/uri.cpp
new file mode 100644
index 0000000000..5653d97e73
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/socket/uri.cpp
@@ -0,0 +1,188 @@
+/*
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/uri.h>
+#include <botan/exceptn.h>
+
+#include <regex>
+
+#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+ #include <arpa/inet.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+#elif defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+ #include <ws2tcpip.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+
+namespace {
+
+constexpr bool isdigit(char ch)
+ {
+ return ch >= '0' && ch <= '9';
+ }
+
+bool isDomain(const std::string& domain)
+ {
+#if defined(__GLIBCXX__) && (__GLIBCXX__ < 20160726)
+ // GCC 4.8 does not support regex
+ BOTAN_UNUSED(domain);
+ return true;
+#else
+ std::regex re(
+ R"(^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$)");
+ std::cmatch m;
+ return std::regex_match(domain.c_str(), m, re);
+#endif
+ }
+
+bool isIPv4(const std::string& ip)
+ {
+ sockaddr_storage inaddr;
+ return !!inet_pton(AF_INET, ip.c_str(), &inaddr);
+ }
+
+bool isIPv6(const std::string& ip)
+ {
+ sockaddr_storage in6addr;
+ return !!inet_pton(AF_INET6, ip.c_str(), &in6addr);
+ }
+}
+
+namespace Botan {
+
+URI URI::fromDomain(const std::string& uri)
+ {
+ unsigned port = 0;
+ const auto port_pos = uri.find(':');
+ if(port_pos != std::string::npos)
+ {
+ for(char c : uri.substr(port_pos+1))
+ {
+ if(!isdigit(c))
+ { throw Invalid_Argument("invalid"); }
+ port = port*10 + c - '0';
+ if(port > 65535)
+ { throw Invalid_Argument("invalid"); }
+ }
+ }
+ const auto domain = uri.substr(0, port_pos);
+ if(isIPv4(domain))
+ { throw Invalid_Argument("invalid"); }
+ if(!isDomain(domain))
+ { throw Invalid_Argument("invalid"); }
+ return {Type::Domain, domain, uint16_t(port)};
+ }
+
+URI URI::fromIPv4(const std::string& uri)
+ {
+ unsigned port = 0;
+ const auto port_pos = uri.find(':');
+ if(port_pos != std::string::npos)
+ {
+ for(char c : uri.substr(port_pos+1))
+ {
+ if(!isdigit(c))
+ { throw Invalid_Argument("invalid"); }
+ port = port*10 + c - '0';
+ if(port > 65535)
+ { throw Invalid_Argument("invalid"); }
+ }
+ }
+ const auto ip = uri.substr(0, port_pos);
+ if(!isIPv4(ip))
+ { throw Invalid_Argument("invalid"); }
+ return { Type::IPv4, ip, uint16_t(port) };
+ }
+
+URI URI::fromIPv6(const std::string& uri)
+ {
+ unsigned port = 0;
+ const auto port_pos = uri.find(']');
+ const bool with_braces = (port_pos != std::string::npos);
+ if((uri[0]=='[') != with_braces)
+ { throw Invalid_Argument("invalid"); }
+
+ if(with_braces && (uri.size() > port_pos + 1))
+ {
+ if(uri[port_pos+1]!=':')
+ { throw Invalid_Argument("invalid"); }
+ for(char c : uri.substr(port_pos+2))
+ {
+ if(!isdigit(c))
+ { throw Invalid_Argument("invalid"); }
+ port = port*10 + c - '0';
+ if(port > 65535)
+ { throw Invalid_Argument("invalid"); }
+ }
+ }
+ const auto ip = uri.substr((with_braces ? 1 : 0), port_pos - with_braces);
+ if(!isIPv6(ip))
+ { throw Invalid_Argument("invalid"); }
+ return { Type::IPv6, ip, uint16_t(port) };
+ }
+
+URI URI::fromAny(const std::string& uri)
+ {
+
+ bool colon_seen=false;
+ bool non_number=false;
+ if(uri[0]=='[')
+ { return fromIPv6(uri); }
+ for(auto c : uri)
+ {
+ if(c == ':')
+ {
+ if(colon_seen) //seen two ':'
+ { return fromIPv6(uri); }
+ colon_seen = true;
+ }
+ else if(!isdigit(c) && c != '.')
+ {
+ non_number=true;
+ }
+ }
+ if(!non_number)
+ {
+ if(isIPv4(uri.substr(0, uri.find(':'))))
+ {
+ return fromIPv4(uri);
+ }
+ }
+ return fromDomain(uri);
+ }
+
+std::string URI::to_string() const
+ {
+ if(type == Type::NotSet)
+ {
+ throw Invalid_Argument("not set");
+ }
+
+ if(port != 0)
+ {
+ if(type == Type::IPv6)
+ { return "[" + host + "]:" + std::to_string(port); }
+ return host + ":" + std::to_string(port);
+ }
+ return host;
+ }
+
+}
+
+#else
+
+namespace Botan {
+
+URI URI::fromDomain(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
+URI URI::fromIPv4(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
+URI URI::fromIPv6(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
+URI URI::fromAny(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
+
+}
+
+#endif