OpenVPN 3 Core Library
Loading...
Searching...
No Matches
urlparse.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12#ifndef OPENVPN_HTTP_URLPARSE_H
13#define OPENVPN_HTTP_URLPARSE_H
14
15#include <string>
16
24
25namespace openvpn::URL {
26OPENVPN_EXCEPTION(url_parse_error);
27
28class Parse
29{
30 public:
32 {
33 }
34
35 Parse(const std::string &url,
36 const bool set_default_port = false,
37 const bool loose_validation = false)
38 {
39 enum State
40 {
41 Scheme,
42 PostSchemeSlash1,
43 PostSchemeSlash2,
44 StartHost,
45 Host,
46 BracketedHost,
47 PostBracketedHost,
48 Port,
49 URI,
50 };
51
52 State state = Scheme;
53 for (auto &c : url)
54 {
55 reprocess:
56 switch (state)
57 {
58 case Scheme:
59 if (c == ':')
60 state = PostSchemeSlash1;
61 else if (is_valid_scheme_char(c))
62 scheme += c;
63 else
64 throw url_parse_error("bad scheme char");
65 break;
66 case PostSchemeSlash1:
67 if (c == '/')
68 state = PostSchemeSlash2;
69 else
70 throw url_parse_error("expected '://' after scheme");
71 break;
72 case PostSchemeSlash2:
73 if (c == '/')
74 state = StartHost;
75 else
76 throw url_parse_error("expected '://' after scheme");
77 break;
78 case StartHost:
79 if (c == '[')
80 state = BracketedHost;
81 else
82 {
83 state = Host;
84 goto reprocess;
85 }
86 break;
87 case Host:
88 if (c == ':')
89 state = Port;
90 else if (c == '/')
91 {
92 state = URI;
93 goto reprocess;
94 }
95 else
96 host += c;
97 break;
98 case BracketedHost:
99 if (c == ']')
100 state = PostBracketedHost;
101 else
102 host += c;
103 break;
104 case PostBracketedHost:
105 if (c == ':')
106 {
107 state = Port;
108 break;
109 }
110 else
111 {
112 state = URI;
113 goto reprocess;
114 }
115 break;
116 case Port:
117 if (c == '/')
118 {
119 state = URI;
120 goto reprocess;
121 }
122 else
123 port += c;
124 break;
125 case URI:
126 if (!HTTP::is_valid_uri_char(c) && !loose_validation)
127 throw url_parse_error("bad URI char");
128 uri += c;
129 break;
130 }
131 }
132 if (set_default_port)
133 default_port();
134 if (uri.empty())
135 uri = "/";
136 validate();
137 }
138
139 // Note that special address types such as unix domain
140 // sockets or windows named pipes store a tag such as
141 // "unix" or "np" as the port component of an address/port
142 // tuple. Here, we move such tags into the scheme.
143 static Parse from_components(const bool https,
144 const std::string &host,
145 const std::string &port,
146 const std::string &uri)
147 {
148 Parse p;
149 p.scheme = https ? "https" : "http";
150 p.host = host;
151 if (port.size() >= 1 && !string::is_digit(port[0])) // non-INET address
152 p.scheme = port;
153 else
154 p.port = port;
155 p.uri = uri;
156 return p;
157 }
158
159 void validate() const
160 {
161 if (scheme.empty())
162 throw url_parse_error("undefined scheme");
163 if (host.empty())
164 throw url_parse_error("undefined host");
165 if (uri.empty())
166 throw url_parse_error("undefined uri");
167
168 if (!port.empty() && !HostPort::is_valid_port(port))
169 throw url_parse_error("bad port");
170 if ((scheme == "http" || scheme == "https") && !HostPort::is_valid_host(host))
171 throw url_parse_error("bad host");
172 }
173
175 {
176 if (port.empty())
177 {
178 if (scheme == "http")
179 port = "80";
180 else if (scheme == "https")
181 port = "443";
182 }
183 }
184
185 bool port_implied() const
186 {
187 return (scheme == "http" && port == "80") || (scheme == "https" && port == "443");
188 }
189
190 bool is_bracketed_host() const
191 {
192 return host.find_first_of(":/\\") != std::string::npos;
193 }
194
195 std::string bracketed_host() const
196 {
197 return '[' + host + ']';
198 }
199
200 std::string to_string() const
201 {
202 const bool bracket_host = is_bracketed_host();
203
204 std::string ret;
205 ret.reserve(256);
206 ret += scheme;
207 ret += "://";
208 if (bracket_host)
209 ret += '[';
210 ret += host;
211 if (bracket_host)
212 ret += ']';
213 if (!port.empty() && !port_implied())
214 {
215 ret += ':';
216 ret += port;
217 }
218 ret += uri;
219 return ret;
220 }
221
222 std::string format_components() const
223 {
224 return printfmt("[scheme=%r host=%r port=%r uri=%r]", scheme, host, port, uri);
225 }
226
227 // Note that special address types such as unix domain
228 // sockets or windows named pipes store a tag such as
229 // "unix" or "np" as the port component of an address/port
230 // tuple. This method returns the port number for INET
231 // addresses or a special tag for non-INET addresses.
232 // Internally, we store the tag as an alternative
233 // scheme such as "unix" or "np".
234 std::string port_for_scheme() const
235 {
236#ifdef OPENVPN_PLATFORM_WIN
237 if (scheme == "np") // named pipe
238 return scheme;
239#else
240 if (scheme == "unix") // unix domain socket
241 return scheme;
242#endif
243 if (scheme == "http" || scheme == "https")
244 return port;
245
246 throw url_parse_error("unknown scheme");
247 }
248
249 std::string scheme;
250 std::string host;
251 std::string port;
252 std::string uri;
253
254 private:
255 bool is_valid_scheme_char(const char c)
256 {
257 return (c >= 'a' && c <= 'z') || c == '_';
258 }
259};
260
261} // namespace openvpn::URL
262
263#endif
bool is_valid_scheme_char(const char c)
Definition urlparse.hpp:255
void validate() const
Definition urlparse.hpp:159
bool is_bracketed_host() const
Definition urlparse.hpp:190
std::string scheme
Definition urlparse.hpp:249
std::string to_string() const
Definition urlparse.hpp:200
static Parse from_components(const bool https, const std::string &host, const std::string &port, const std::string &uri)
Definition urlparse.hpp:143
std::string bracketed_host() const
Definition urlparse.hpp:195
std::string port_for_scheme() const
Definition urlparse.hpp:234
std::string format_components() const
Definition urlparse.hpp:222
Parse(const std::string &url, const bool set_default_port=false, const bool loose_validation=false)
Definition urlparse.hpp:35
bool port_implied() const
Definition urlparse.hpp:185
#define OPENVPN_EXCEPTION(C)
bool is_valid_uri_char(const unsigned char c)
bool is_valid_port(const unsigned int port)
Definition hostport.hpp:24
bool is_valid_host(const std::string &host)
Definition hostport.hpp:64
bool is_digit(const char c)
Definition string.hpp:284
std::string printfmt(const std::string &fmt, Args... args)
Definition format.hpp:314
proxy_autoconfig_url url
std::string ret