/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 | 203k | { |
26 | 203k | return ch >= '0' && ch <= '9'; |
27 | 203k | } |
28 | | |
29 | | bool isDomain(const std::string& domain) |
30 | 480 | { |
31 | | #if defined(__GLIBCXX__) && (__GLIBCXX__ < 20160726) |
32 | | // GCC 4.8 does not support regex |
33 | | BOTAN_UNUSED(domain); |
34 | | return true; |
35 | | #else |
36 | 480 | std::regex re( |
37 | 480 | 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 | 480 | std::cmatch m; |
39 | 480 | return std::regex_match(domain.c_str(), m, re); |
40 | 480 | #endif |
41 | 480 | } |
42 | | |
43 | | bool isIPv4(const std::string& ip) |
44 | 650 | { |
45 | 650 | sockaddr_storage inaddr; |
46 | 650 | return !!inet_pton(AF_INET, ip.c_str(), &inaddr); |
47 | 650 | } |
48 | | |
49 | | bool isIPv6(const std::string& ip) |
50 | 59 | { |
51 | 59 | sockaddr_storage in6addr; |
52 | 59 | return !!inet_pton(AF_INET6, ip.c_str(), &in6addr); |
53 | 59 | } |
54 | | } |
55 | | |
56 | | namespace Botan { |
57 | | |
58 | | URI URI::fromDomain(const std::string& uri) |
59 | 501 | { |
60 | 501 | unsigned port = 0; |
61 | 501 | const auto port_pos = uri.find(':'); |
62 | 501 | if(port_pos != std::string::npos) |
63 | 77 | { |
64 | 77 | for(char c : uri.substr(port_pos+1)) |
65 | 911 | { |
66 | 911 | if(!isdigit(c)) |
67 | 12 | { throw Invalid_Argument("invalid"); } |
68 | 899 | port = port*10 + c - '0'; |
69 | 899 | if(port > 65535) |
70 | 8 | { throw Invalid_Argument("invalid"); } |
71 | 899 | } |
72 | 77 | } |
73 | 481 | const auto domain = uri.substr(0, port_pos); |
74 | 481 | if(isIPv4(domain)) |
75 | 1 | { throw Invalid_Argument("invalid"); } |
76 | 480 | if(!isDomain(domain)) |
77 | 368 | { throw Invalid_Argument("invalid"); } |
78 | 112 | return {Type::Domain, domain, uint16_t(port)}; |
79 | 112 | } |
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 | 332 | { |
89 | 332 | if(!isdigit(c)) |
90 | 2 | { throw Invalid_Argument("invalid"); } |
91 | 330 | port = port*10 + c - '0'; |
92 | 330 | if(port > 65535) |
93 | 5 | { throw Invalid_Argument("invalid"); } |
94 | 330 | } |
95 | 45 | } |
96 | 39 | const auto ip = uri.substr(0, port_pos); |
97 | 39 | if(!isIPv4(ip)) |
98 | 0 | { throw Invalid_Argument("invalid"); } |
99 | 39 | return { Type::IPv4, ip, uint16_t(port) }; |
100 | 39 | } |
101 | | |
102 | | URI URI::fromIPv6(const std::string& uri) |
103 | 117 | { |
104 | 117 | unsigned port = 0; |
105 | 117 | const auto port_pos = uri.find(']'); |
106 | 117 | const bool with_braces = (port_pos != std::string::npos); |
107 | 117 | if((uri[0]=='[') != with_braces) |
108 | 15 | { throw Invalid_Argument("invalid"); } |
109 | | |
110 | 102 | if(with_braces && (uri.size() > port_pos + 1)) |
111 | 82 | { |
112 | 82 | if(uri[port_pos+1]!=':') |
113 | 24 | { throw Invalid_Argument("invalid"); } |
114 | 58 | for(char c : uri.substr(port_pos+2)) |
115 | 392 | { |
116 | 392 | if(!isdigit(c)) |
117 | 13 | { throw Invalid_Argument("invalid"); } |
118 | 379 | port = port*10 + c - '0'; |
119 | 379 | if(port > 65535) |
120 | 6 | { throw Invalid_Argument("invalid"); } |
121 | 379 | } |
122 | 58 | } |
123 | 59 | const auto ip = uri.substr((with_braces ? 1 : 0), port_pos - with_braces); |
124 | 59 | if(!isIPv6(ip)) |
125 | 57 | { throw Invalid_Argument("invalid"); } |
126 | 2 | return { Type::IPv6, ip, uint16_t(port) }; |
127 | 2 | } |
128 | | |
129 | | URI URI::fromAny(const std::string& uri) |
130 | 664 | { |
131 | | |
132 | 664 | bool colon_seen=false; |
133 | 664 | bool non_number=false; |
134 | 664 | if(uri[0]=='[') |
135 | 94 | { return fromIPv6(uri); } |
136 | 570 | for(auto c : uri) |
137 | 202k | { |
138 | 202k | if(c == ':') |
139 | 168 | { |
140 | 168 | if(colon_seen) //seen two ':' |
141 | 23 | { return fromIPv6(uri); } |
142 | 145 | colon_seen = true; |
143 | 145 | } |
144 | 202k | else if(!isdigit(c) && c != '.') |
145 | 151k | { |
146 | 151k | non_number=true; |
147 | 151k | } |
148 | 202k | } |
149 | 547 | if(!non_number) |
150 | 130 | { |
151 | 130 | if(isIPv4(uri.substr(0, uri.find(':')))) |
152 | 46 | { |
153 | 46 | return fromIPv4(uri); |
154 | 46 | } |
155 | 501 | } |
156 | 501 | return fromDomain(uri); |
157 | 501 | } |
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 | | |
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 |