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