Botan 3.4.0
Crypto and TLS for C&
uri.cpp
Go to the documentation of this file.
1/*
2* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#include <botan/internal/uri.h>
8
9#include <botan/exceptn.h>
10
11#include <regex>
12
13#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
14 #include <arpa/inet.h>
15 #include <netinet/in.h>
16 #include <sys/socket.h>
17#elif defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
18 #include <ws2tcpip.h>
19#endif
20
21#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
22
23namespace {
24
25constexpr bool isdigit(char ch) {
26 return ch >= '0' && ch <= '9';
27}
28
29bool isDomain(std::string_view domain) {
30 std::string domain_str(domain);
31 std::regex re(
32 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])$)");
33 std::cmatch m;
34 return std::regex_match(domain_str.c_str(), m, re);
35}
36
37bool isIPv4(std::string_view ip) {
38 std::string ip_str(ip);
39 sockaddr_storage inaddr;
40 return !!inet_pton(AF_INET, ip_str.c_str(), &inaddr);
41}
42
43bool isIPv6(std::string_view ip) {
44 std::string ip_str(ip);
45 sockaddr_storage in6addr;
46 return !!inet_pton(AF_INET6, ip_str.c_str(), &in6addr);
47}
48} // namespace
49
50namespace Botan {
51
52URI URI::fromDomain(std::string_view uri) {
53 unsigned port = 0;
54 const auto port_pos = uri.find(':');
55 if(port_pos != std::string::npos) {
56 for(char c : uri.substr(port_pos + 1)) {
57 if(!isdigit(c)) {
58 throw Invalid_Argument("invalid");
59 }
60 port = port * 10 + c - '0';
61 if(port > 65535) {
62 throw Invalid_Argument("invalid");
63 }
64 }
65 }
66 const auto domain = uri.substr(0, port_pos);
67 if(isIPv4(domain)) {
68 throw Invalid_Argument("invalid");
69 }
70 if(!isDomain(domain)) {
71 throw Invalid_Argument("invalid");
72 }
73 return {Type::Domain, domain, uint16_t(port)};
74}
75
76URI URI::fromIPv4(std::string_view uri) {
77 unsigned port = 0;
78 const auto port_pos = uri.find(':');
79 if(port_pos != std::string::npos) {
80 for(char c : uri.substr(port_pos + 1)) {
81 if(!isdigit(c)) {
82 throw Invalid_Argument("invalid");
83 }
84 port = port * 10 + c - '0';
85 if(port > 65535) {
86 throw Invalid_Argument("invalid");
87 }
88 }
89 }
90 const auto ip = uri.substr(0, port_pos);
91 if(!isIPv4(ip)) {
92 throw Invalid_Argument("invalid");
93 }
94 return {Type::IPv4, ip, uint16_t(port)};
95}
96
97URI URI::fromIPv6(std::string_view uri) {
98 unsigned port = 0;
99 const auto port_pos = uri.find(']');
100 const bool with_braces = (port_pos != std::string::npos);
101 if((uri[0] == '[') != with_braces) {
102 throw Invalid_Argument("invalid");
103 }
104
105 if(with_braces && (uri.size() > port_pos + 1)) {
106 if(uri[port_pos + 1] != ':') {
107 throw Invalid_Argument("invalid");
108 }
109 for(char c : uri.substr(port_pos + 2)) {
110 if(!isdigit(c)) {
111 throw Invalid_Argument("invalid");
112 }
113 port = port * 10 + c - '0';
114 if(port > 65535) {
115 throw Invalid_Argument("invalid");
116 }
117 }
118 }
119 const auto ip = uri.substr((with_braces ? 1 : 0), port_pos - with_braces);
120 if(!isIPv6(ip)) {
121 throw Invalid_Argument("invalid");
122 }
123 return {Type::IPv6, ip, uint16_t(port)};
124}
125
126URI URI::fromAny(std::string_view uri) {
127 bool colon_seen = false;
128 bool non_number = false;
129 if(uri[0] == '[') {
130 return fromIPv6(uri);
131 }
132 for(auto c : uri) {
133 if(c == ':') {
134 if(colon_seen) //seen two ':'
135 {
136 return fromIPv6(uri);
137 }
138 colon_seen = true;
139 } else if(!isdigit(c) && c != '.') {
140 non_number = true;
141 }
142 }
143 if(!non_number) {
144 if(isIPv4(uri.substr(0, uri.find(':')))) {
145 return fromIPv4(uri);
146 }
147 }
148 return fromDomain(uri);
149}
150
151std::string URI::to_string() const {
152 if(type == Type::NotSet) {
153 throw Invalid_Argument("not set");
154 }
155
156 if(port != 0) {
157 if(type == Type::IPv6) {
158 return "[" + host + "]:" + std::to_string(port);
159 }
160 return host + ":" + std::to_string(port);
161 }
162 return host;
163}
164
165} // namespace Botan
166
167#else
168
169namespace Botan {
170
171URI URI::fromDomain(std::string_view) {
172 throw Not_Implemented("No socket support enabled in build");
173}
174
175URI URI::fromIPv4(std::string_view) {
176 throw Not_Implemented("No socket support enabled in build");
177}
178
179URI URI::fromIPv6(std::string_view) {
180 throw Not_Implemented("No socket support enabled in build");
181}
182
183URI URI::fromAny(std::string_view) {
184 throw Not_Implemented("No socket support enabled in build");
185}
186
187} // namespace Botan
188
189#endif
const Type type
Definition uri.h:36
static URI fromIPv4(std::string_view uri)
Definition uri.cpp:175
const uint16_t port
Definition uri.h:38
const std::string host
Definition uri.h:37
static URI fromIPv6(std::string_view uri)
Definition uri.cpp:179
static URI fromDomain(std::string_view uri)
Definition uri.cpp:171
std::string to_string() const
static URI fromAny(std::string_view uri)
Definition uri.cpp:183