/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 | | |
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 | | |
23 | | namespace { |
24 | | |
25 | 372k | constexpr bool isdigit(char ch) { return ch >= '0' && ch <= '9'; } |
26 | | |
27 | 362 | bool isDomain(std::string_view domain) { |
28 | 362 | std::string domain_str(domain); |
29 | 362 | std::regex re( |
30 | 362 | 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])$)"); |
31 | 362 | std::cmatch m; |
32 | 362 | return std::regex_match(domain_str.c_str(), m, re); |
33 | 362 | } |
34 | | |
35 | 641 | bool isIPv4(std::string_view ip) { |
36 | 641 | std::string ip_str(ip); |
37 | 641 | sockaddr_storage inaddr; |
38 | 641 | return !!inet_pton(AF_INET, ip_str.c_str(), &inaddr); |
39 | 641 | } |
40 | | |
41 | 121 | bool isIPv6(std::string_view ip) { |
42 | 121 | std::string ip_str(ip); |
43 | 121 | sockaddr_storage in6addr; |
44 | 121 | return !!inet_pton(AF_INET6, ip_str.c_str(), &in6addr); |
45 | 121 | } |
46 | | } // namespace |
47 | | |
48 | | namespace Botan { |
49 | | |
50 | 405 | URI URI::fromDomain(std::string_view uri) { |
51 | 405 | unsigned port = 0; |
52 | 405 | const auto port_pos = uri.find(':'); |
53 | 405 | if(port_pos != std::string::npos) { |
54 | 681 | for(char c : uri.substr(port_pos + 1)) { |
55 | 681 | if(!isdigit(c)) { |
56 | 31 | throw Invalid_Argument("invalid"); |
57 | 31 | } |
58 | 650 | port = port * 10 + c - '0'; |
59 | 650 | if(port > 65535) { |
60 | 11 | throw Invalid_Argument("invalid"); |
61 | 11 | } |
62 | 650 | } |
63 | 108 | } |
64 | 363 | const auto domain = uri.substr(0, port_pos); |
65 | 363 | if(isIPv4(domain)) { |
66 | 1 | throw Invalid_Argument("invalid"); |
67 | 1 | } |
68 | 362 | if(!isDomain(domain)) { |
69 | 255 | throw Invalid_Argument("invalid"); |
70 | 255 | } |
71 | 107 | return {Type::Domain, domain, uint16_t(port)}; |
72 | 362 | } |
73 | | |
74 | 99 | URI URI::fromIPv4(std::string_view uri) { |
75 | 99 | unsigned port = 0; |
76 | 99 | const auto port_pos = uri.find(':'); |
77 | 99 | if(port_pos != std::string::npos) { |
78 | 33.2k | for(char c : uri.substr(port_pos + 1)) { |
79 | 33.2k | if(!isdigit(c)) { |
80 | 6 | throw Invalid_Argument("invalid"); |
81 | 6 | } |
82 | 33.2k | port = port * 10 + c - '0'; |
83 | 33.2k | if(port > 65535) { |
84 | 25 | throw Invalid_Argument("invalid"); |
85 | 25 | } |
86 | 33.2k | } |
87 | 94 | } |
88 | 68 | const auto ip = uri.substr(0, port_pos); |
89 | 68 | if(!isIPv4(ip)) { |
90 | 0 | throw Invalid_Argument("invalid"); |
91 | 0 | } |
92 | 68 | return {Type::IPv4, ip, uint16_t(port)}; |
93 | 68 | } |
94 | | |
95 | 182 | URI URI::fromIPv6(std::string_view uri) { |
96 | 182 | unsigned port = 0; |
97 | 182 | const auto port_pos = uri.find(']'); |
98 | 182 | const bool with_braces = (port_pos != std::string::npos); |
99 | 182 | if((uri[0] == '[') != with_braces) { |
100 | 3 | throw Invalid_Argument("invalid"); |
101 | 3 | } |
102 | | |
103 | 179 | if(with_braces && (uri.size() > port_pos + 1)) { |
104 | 109 | if(uri[port_pos + 1] != ':') { |
105 | 17 | throw Invalid_Argument("invalid"); |
106 | 17 | } |
107 | 566 | for(char c : uri.substr(port_pos + 2)) { |
108 | 566 | if(!isdigit(c)) { |
109 | 35 | throw Invalid_Argument("invalid"); |
110 | 35 | } |
111 | 531 | port = port * 10 + c - '0'; |
112 | 531 | if(port > 65535) { |
113 | 6 | throw Invalid_Argument("invalid"); |
114 | 6 | } |
115 | 531 | } |
116 | 92 | } |
117 | 121 | const auto ip = uri.substr((with_braces ? 1 : 0), port_pos - with_braces); |
118 | 121 | if(!isIPv6(ip)) { |
119 | 117 | throw Invalid_Argument("invalid"); |
120 | 117 | } |
121 | 4 | return {Type::IPv6, ip, uint16_t(port)}; |
122 | 121 | } |
123 | | |
124 | 686 | URI URI::fromAny(std::string_view uri) { |
125 | 686 | bool colon_seen = false; |
126 | 686 | bool non_number = false; |
127 | 686 | if(uri[0] == '[') { |
128 | 133 | return fromIPv6(uri); |
129 | 133 | } |
130 | 338k | for(auto c : uri) { |
131 | 338k | if(c == ':') { |
132 | 300 | if(colon_seen) //seen two ':' |
133 | 49 | { |
134 | 49 | return fromIPv6(uri); |
135 | 49 | } |
136 | 251 | colon_seen = true; |
137 | 337k | } else if(!isdigit(c) && c != '.') { |
138 | 123k | non_number = true; |
139 | 123k | } |
140 | 338k | } |
141 | 504 | if(!non_number) { |
142 | 210 | if(isIPv4(uri.substr(0, uri.find(':')))) { |
143 | 99 | return fromIPv4(uri); |
144 | 99 | } |
145 | 210 | } |
146 | 405 | return fromDomain(uri); |
147 | 504 | } |
148 | | |
149 | 0 | std::string URI::to_string() const { |
150 | 0 | if(type == Type::NotSet) { |
151 | 0 | throw Invalid_Argument("not set"); |
152 | 0 | } |
153 | | |
154 | 0 | if(port != 0) { |
155 | 0 | if(type == Type::IPv6) { |
156 | 0 | return "[" + host + "]:" + std::to_string(port); |
157 | 0 | } |
158 | 0 | return host + ":" + std::to_string(port); |
159 | 0 | } |
160 | 0 | return host; |
161 | 0 | } |
162 | | |
163 | | } // namespace Botan |
164 | | |
165 | | #else |
166 | | |
167 | | namespace Botan { |
168 | | |
169 | | URI URI::fromDomain(std::string_view) { throw Not_Implemented("No socket support enabled in build"); } |
170 | | |
171 | | URI URI::fromIPv4(std::string_view) { throw Not_Implemented("No socket support enabled in build"); } |
172 | | |
173 | | URI URI::fromIPv6(std::string_view) { throw Not_Implemented("No socket support enabled in build"); } |
174 | | |
175 | | URI URI::fromAny(std::string_view) { throw Not_Implemented("No socket support enabled in build"); } |
176 | | |
177 | | } // namespace Botan |
178 | | |
179 | | #endif |