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