/src/botan/src/lib/utils/socket/uri.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
22 | | namespace { |
23 | | |
24 | | constexpr bool isdigit(char ch) |
25 | 213k | { |
26 | 213k | return ch >= '0' && ch <= '9'; |
27 | 213k | } |
28 | | |
29 | | bool isDomain(const std::string& domain) |
30 | 453 | { |
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 | 453 | 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 | 453 | std::cmatch m; |
39 | 453 | return std::regex_match(domain.c_str(), m, re); |
40 | 453 | #endif |
41 | 453 | } |
42 | | |
43 | | bool isIPv4(const std::string& ip) |
44 | 622 | { |
45 | 622 | sockaddr_storage inaddr; |
46 | 622 | return !!inet_pton(AF_INET, ip.c_str(), &inaddr); |
47 | 622 | } |
48 | | |
49 | | bool isIPv6(const std::string& ip) |
50 | 55 | { |
51 | 55 | sockaddr_storage in6addr; |
52 | 55 | return !!inet_pton(AF_INET6, ip.c_str(), &in6addr); |
53 | 55 | } |
54 | | } |
55 | | |
56 | | namespace Botan { |
57 | | |
58 | | URI URI::fromDomain(const std::string& uri) |
59 | 464 | { |
60 | 464 | unsigned port = 0; |
61 | 464 | const auto port_pos = uri.find(':'); |
62 | 464 | if(port_pos != std::string::npos) |
63 | 69 | { |
64 | 69 | for(char c : uri.substr(port_pos+1)) |
65 | 839 | { |
66 | 839 | if(!isdigit(c)) |
67 | 5 | { throw Invalid_Argument("invalid"); } |
68 | 834 | port = port*10 + c - '0'; |
69 | 834 | if(port > 65535) |
70 | 5 | { throw Invalid_Argument("invalid"); } |
71 | 834 | } |
72 | 69 | } |
73 | 464 | const auto domain = uri.substr(0, port_pos); |
74 | 454 | if(isIPv4(domain)) |
75 | 1 | { throw Invalid_Argument("invalid"); } |
76 | 453 | if(!isDomain(domain)) |
77 | 357 | { throw Invalid_Argument("invalid"); } |
78 | 96 | return {Type::Domain, domain, uint16_t(port)}; |
79 | 96 | } |
80 | | |
81 | | URI URI::fromIPv4(const std::string& uri) |
82 | 46 | { |
83 | 46 | unsigned port = 0; |
84 | 46 | const auto port_pos = uri.find(':'); |
85 | 46 | if(port_pos != std::string::npos) |
86 | 45 | { |
87 | 45 | for(char c : uri.substr(port_pos+1)) |
88 | 337 | { |
89 | 337 | if(!isdigit(c)) |
90 | 3 | { throw Invalid_Argument("invalid"); } |
91 | 334 | port = port*10 + c - '0'; |
92 | 334 | if(port > 65535) |
93 | 6 | { throw Invalid_Argument("invalid"); } |
94 | 334 | } |
95 | 45 | } |
96 | 46 | const auto ip = uri.substr(0, port_pos); |
97 | 37 | if(!isIPv4(ip)) |
98 | 0 | { throw Invalid_Argument("invalid"); } |
99 | 37 | return { Type::IPv4, ip, uint16_t(port) }; |
100 | 37 | } |
101 | | |
102 | | URI URI::fromIPv6(const std::string& uri) |
103 | 116 | { |
104 | 116 | unsigned port = 0; |
105 | 116 | const auto port_pos = uri.find(']'); |
106 | 116 | const bool with_braces = (port_pos != std::string::npos); |
107 | 116 | if((uri[0]=='[') != with_braces) |
108 | 17 | { throw Invalid_Argument("invalid"); } |
109 | 99 | |
110 | 99 | if(with_braces && (uri.size() > port_pos + 1)) |
111 | 81 | { |
112 | 81 | if(uri[port_pos+1]!=':') |
113 | 29 | { throw Invalid_Argument("invalid"); } |
114 | 52 | for(char c : uri.substr(port_pos+2)) |
115 | 365 | { |
116 | 365 | if(!isdigit(c)) |
117 | 12 | { throw Invalid_Argument("invalid"); } |
118 | 353 | port = port*10 + c - '0'; |
119 | 353 | if(port > 65535) |
120 | 3 | { throw Invalid_Argument("invalid"); } |
121 | 353 | } |
122 | 52 | } |
123 | 99 | const auto ip = uri.substr((with_braces ? 1 : 0), port_pos - with_braces); |
124 | 55 | if(!isIPv6(ip)) |
125 | 52 | { throw Invalid_Argument("invalid"); } |
126 | 3 | return { Type::IPv6, ip, uint16_t(port) }; |
127 | 3 | } |
128 | | |
129 | | URI URI::fromAny(const std::string& uri) |
130 | 626 | { |
131 | 626 | |
132 | 626 | bool colon_seen=false; |
133 | 626 | bool non_number=false; |
134 | 626 | if(uri[0]=='[') |
135 | 89 | { return fromIPv6(uri); } |
136 | 537 | for(auto c : uri) |
137 | 212k | { |
138 | 212k | if(c == ':') |
139 | 168 | { |
140 | 168 | if(colon_seen) //seen two ':' |
141 | 27 | { return fromIPv6(uri); } |
142 | 141 | colon_seen = true; |
143 | 141 | } |
144 | 211k | else if(!isdigit(c) && c != '.') |
145 | 178k | { |
146 | 178k | non_number=true; |
147 | 178k | } |
148 | 212k | } |
149 | 537 | if(!non_number) |
150 | 131 | { |
151 | 131 | if(isIPv4(uri.substr(0, uri.find(':')))) |
152 | 46 | { |
153 | 46 | return fromIPv4(uri); |
154 | 46 | } |
155 | 464 | } |
156 | 464 | return fromDomain(uri); |
157 | 464 | } |
158 | | |
159 | | std::string URI::to_string() const |
160 | 0 | { |
161 | 0 | if(type == Type::NotSet) |
162 | 0 | { |
163 | 0 | throw Invalid_Argument("not set"); |
164 | 0 | } |
165 | 0 | |
166 | 0 | if(port != 0) |
167 | 0 | { |
168 | 0 | if(type == Type::IPv6) |
169 | 0 | { return "[" + host + "]:" + std::to_string(port); } |
170 | 0 | return host + ":" + std::to_string(port); |
171 | 0 | } |
172 | 0 | return host; |
173 | 0 | } |
174 | | |
175 | | } |
176 | | |
177 | | #else |
178 | | |
179 | | namespace Botan { |
180 | | |
181 | | URI URI::fromDomain(const std::string&) {throw Not_Implemented("No socket support enabled in build");} |
182 | | URI URI::fromIPv4(const std::string&) {throw Not_Implemented("No socket support enabled in build");} |
183 | | URI URI::fromIPv6(const std::string&) {throw Not_Implemented("No socket support enabled in build");} |
184 | | URI URI::fromAny(const std::string&) {throw Not_Implemented("No socket support enabled in build");} |
185 | | |
186 | | } |
187 | | |
188 | | #endif |