UPnPsdk 0.1
Universal Plug and Play +, Software Development Kit
 
Loading...
Searching...
No Matches
uri.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 *
3 * Copyright (c) 2000-2003 Intel Corporation
4 * All rights reserved.
5 * Copyright (c) 2012 France Telecom All rights reserved.
6 * Copyright (C) 2021 GPL 3 and higher by Ingo Höft, <Ingo@Hoeft-online.de>
7 * Redistribution only with this Copyright remark. Last modified: 2026-03-15
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * - Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 * - Neither name of Intel Corporation nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
29 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 ******************************************************************************/
39#include <upnp.hpp>
40#include <uri.hpp>
41#include <UPnPsdk/uri.hpp>
42#include <UPnPsdk/synclog.hpp>
43#include <UPnPsdk/addrinfo.hpp>
44
46#include <cstdio> // Needed if OpenSSL isn't compiled in.
47#include <climits>
49
51
57
58namespace {
59
72inline void copy_token(
74 const token* in,
76 const char* in_base,
78 token* out,
80 char* out_base) {
81 out->size = in->size;
82 out->buff = out_base + (in->buff - in_base);
83}
84
86} // anonymous namespace
87
88
89int create_url_list(memptr* a_url_list, URL_list* a_out) {
90 if (!a_out)
91 return 0;
92 memset(a_out, 0, sizeof(*a_out));
93 if (!a_url_list)
94 return 0;
95
96 std::string_view url_list_sv =
97 std::string_view(a_url_list->buf, a_url_list->length);
98
99 if (url_list_sv.empty() || url_list_sv == "<>")
100 return 0;
101
102 // Verify correct delimiter '<', '><', '>', and count URLs.
103 auto it{url_list_sv.begin()};
104 size_t url_count{1};
105 auto ch0 = *it;
106 for (++it; it < url_list_sv.end() - 1; it++) {
107 if (*it == '<' || (*it == '>' && *++it != '<'))
108 return UPNP_E_INVALID_URL;
109 if (*it == '<')
110 url_count++;
111 }
112 auto ch1 = *it;
113 // Check begin and end delimiter. They must be '<'...'>'.
114 // To be compatible with pUPnP we have different error messages.
115 if ((ch0 == '<' && ch1 != '>') || (ch0 != '<' && ch1 == '>'))
116 return UPNP_E_INVALID_URL;
117 if (!(ch0 == '<' && ch1 == '>'))
118 return 0;
119
120 // Allocate memory and copy the serialized urls to it.
121 auto url_list_size = url_list_sv.size(); // There is no terminating '\0'.
122 char* base_urls = static_cast<char*>(malloc(url_list_size + 1));
123 if (!base_urls)
124 return UPNP_E_OUTOF_MEMORY;
125 // copy and terminate url_list ("<url><url>\0").
126 base_urls[url_list_sv.copy(base_urls, url_list_size)] = '\0';
127 std::string_view base_urls_sv = std::string_view(base_urls, url_list_size);
128
129 // Allocate memory for the parsed urls.
130 uri_type* parsed_urls =
131 static_cast<uri_type*>(malloc(sizeof(uri_type) * url_count));
132 if (!parsed_urls) {
133 free(base_urls);
134 return UPNP_E_OUTOF_MEMORY;
135 }
136
137 // Create views into the base_urls to its components (scheme, hostport,
138 // path, etc.) and store them into the allocated buffer as members of a
139 // trivial C array.
140 size_t store_idx{}; // We have 'uri_count' for URIs correct delimited with
141 // '<url><url>' but the URIs may be wrong. These are not
142 // parsed and returned. To count only the successful
143 // parsed URIs I use this index.
144 std::string_view base_url_sv;
145 uri_type splitted_url;
146 for (size_t i{}; i < url_count; i++) {
147 size_t pos = base_urls_sv.find_first_of('>');
148 base_url_sv = base_urls_sv.substr(0, pos); // Extract current URL.
149 base_url_sv.remove_prefix(1); // Remove leading '<' from current URL.
150 base_urls_sv.remove_prefix(pos + 1); // Remove current URL from list.
151
152 // parse_uri
153 int return_code =
154 parse_uri(base_url_sv.data(), base_url_sv.size(), &splitted_url);
155
156 if (return_code == HTTP_SUCCESS &&
157 splitted_url.hostport.text.size != 0) {
158 // No errors detected, cache the parsed result. Only URLs with
159 // network addresses are considered.
160 parsed_urls[store_idx++] = splitted_url;
161
162 } else if (return_code == UPNP_E_OUTOF_MEMORY) {
163 free(base_urls);
164 free(parsed_urls);
165 return UPNP_E_OUTOF_MEMORY;
166 }
167 }
168
169 // All values are available now. Fill the destination structure.
170 a_out->parsedURLs = parsed_urls;
171 a_out->URLs = base_urls;
172 a_out->size = store_idx;
173
174 if (store_idx > INT_MAX) // Paranoia checking due to cast.
175 return UPNP_E_OUTOF_MEMORY;
176 return static_cast<int>(store_idx);
177}
178
179
181 out->URLs = nullptr;
182 out->parsedURLs = nullptr;
183 out->size = 0u;
184
185 size_t len = strlen(in->URLs) + 1;
186 out->URLs = static_cast<char*>(malloc(len));
187 if (!out->URLs)
188 return UPNP_E_OUTOF_MEMORY;
189 // out->parsedURLs = static_cast<uri_type*>(calloc(in->size,
190 // sizeof(uri_type)));
191 out->parsedURLs =
192 static_cast<uri_type*>(malloc(sizeof(uri_type) * in->size));
193 if (!out->parsedURLs) {
194 free(out->URLs);
195 return UPNP_E_OUTOF_MEMORY;
196 }
197
198 memcpy(out->URLs, in->URLs, len);
199 for (size_t i{0}; i < in->size; i++) {
200 // Having pointer within the coppied structure a deep copy is needed.
201 out->parsedURLs[i].type = in->parsedURLs[i].type;
202 copy_token(&in->parsedURLs[i].scheme, in->URLs,
203 &out->parsedURLs[i].scheme, out->URLs);
204 out->parsedURLs[i].path_type = in->parsedURLs[i].path_type;
205 copy_token(&in->parsedURLs[i].pathquery, in->URLs,
206 &out->parsedURLs[i].pathquery, out->URLs);
207 copy_token(&in->parsedURLs[i].fragment, in->URLs,
208 &out->parsedURLs[i].fragment, out->URLs);
210 &out->parsedURLs[i].hostport.text, out->URLs);
211 memcpy(&out->parsedURLs[i].hostport.IPaddress,
213 sizeof(struct sockaddr_storage));
214 }
215 out->size = in->size;
216
217 return HTTP_SUCCESS;
218}
219
220
222 if (list->URLs) {
223 free(list->URLs);
224 }
225 if (list->parsedURLs) {
226 free(list->parsedURLs);
227 }
228 list->size = (size_t)0;
229}
230
231/* This should use upnpdebug. Failing that, disable by default */
232#if defined(DEBUG_URI) || defined(DOXYGEN_RUN)
234 print_token(&in->scheme);
237 print_token(&in->fragment);
238}
239
241 size_t i = 0;
242
243 fprintf(stderr, "Token Size : %" PRIzu "\n\'", in->size);
244 for (i = 0; i < in->size; i++)
245 putchar(in->buff[i]);
246 putchar('\'');
247 putchar('\n');
248}
249#endif /* DEBUG */
250
251int token_string_casecmp(token* in1, const char* in2) {
252 size_t in2_length = strlen(in2);
253 if (in1->buff == nullptr && in2_length == 0)
254 return 0;
255 else if (in1->buff == nullptr && in2_length != 0)
256 return -1;
257 else if (in1->size < in2_length)
258 return -1;
259 else if (in1->size > in2_length)
260 return 1;
261
262 return strncasecmp(in1->buff, in2, in1->size);
263}
264
265int token_cmp(token* in1, token* in2) {
266 if (in1->size < in2->size)
267 return -1;
268 else if (in1->size > in2->size)
269 return 1;
270 else
271 return memcmp(in1->buff, in2->buff, in1->size);
272}
273
274
275char* resolve_rel_url(char* a_base_url, char* a_rel_url) {
276 std::string out_str;
277
278 do { // This loop is used one time and only to have one exit target for
279 // breaks. There are two special cases that are not direct handled by
280 // the CUri class.
281 try {
282 using STATE = UPnPsdk::CComponent::STATE;
283 if (a_base_url == nullptr && a_rel_url != nullptr) {
284 // If the base_url is a nullptr, then a copy of the rel_url is
285 // passed back.
286 UPnPsdk::CUriRef uri_refObj(a_rel_url);
287 if (uri_refObj.scheme.state() == STATE::avail) {
288 // An absolute URI is checked against DNS.
289 auto& host = uri_refObj.authority.host;
290 auto& port = uri_refObj.authority.port;
291 UPnPsdk::CAddrinfo aiObj(host.str(), port.str());
292 if (!aiObj.get_first())
293 return nullptr;
294 }
295
296 out_str = uri_refObj.str();
297 break;
298 }
299
300 if (a_base_url != nullptr && a_rel_url != nullptr) {
301 // If the rel_url is absolute (with a valid base_url), then a
302 // copy of the rel_url is passed back. I have first to check
303 // both URIs if they are absolute, means if they have a scheme.
304 UPnPsdk::CUriRef uri_relObj(a_rel_url);
305 if (uri_relObj.scheme.state() == STATE::avail) {
306 UPnPsdk::CUriRef uri_baseObj(a_base_url);
307 if (uri_baseObj.scheme.state() == STATE::avail) {
308 // The condition is given and I return the "relative"
309 // URI, that is available with an absolute scheme, but
310 // only if it is registered on DNS.
311 auto& host = uri_relObj.authority.host;
312 auto& port = uri_relObj.authority.port;
313 UPnPsdk::CAddrinfo aiObj(host.str(), port.str());
314 if (!aiObj.get_first())
315 return nullptr;
316
317 out_str = uri_relObj.str();
318 break;
319 }
320 }
321 }
322
323 // Handle the rest of relative parsing. That is covered by the CUri
324 // class.
325 UPnPsdk::CUri uriObj(a_base_url == nullptr ? "" : a_base_url);
326 uriObj = a_rel_url == nullptr ? "" : a_rel_url;
327
328 // Accept a merged target URI only if it is registered on DNS.
329 auto& host = uriObj.target.authority.host;
330 auto& port = uriObj.target.authority.port;
331 UPnPsdk::CAddrinfo aiObj(host.str(), port.str());
332 if (!aiObj.get_first())
333 return nullptr;
334
335 out_str = uriObj.str();
336
337 } catch (const std::invalid_argument& ex) {
338 UPnPsdk_LOGCATCH("MSG1046") "Catched next line...\n"
339 << ex.what() << '\n';
340 return nullptr;
341 }
342 } while (false);
343
344
345 char* out = static_cast<char*>(malloc(out_str.size() + 1));
346 if (out == nullptr)
347 return nullptr;
348 out_str.copy(out, std::string::npos);
349 out[out_str.size()] = '\0';
350
351 return out;
352}
UPnPsdk_EXTERN unsigned gIF_INDEX
Index/scope-id from the used network interface.
Definition uri.cpp:50
int copy_URL_list(URL_list *in, URL_list *out)
Copies one URL_list into another.
Definition uri.cpp:180
int token_cmp(token *in1, token *in2)
Compares two tokens.
Definition uri.cpp:265
int token_string_casecmp(token *in1, const char *in2)
Compares buffer in the token object with the buffer in in2 case insensitive.
Definition uri.cpp:251
char * resolve_rel_url(char *a_base_url, char *a_rel_url)
Resolves a relative url with a base url.
Definition uri.cpp:275
void print_uri(uri_type *in)
Function useful in debugging for printing a parsed uri.
Definition uri.cpp:233
void print_token(token *in)
Function useful in debugging for printing a token.
Definition uri.cpp:240
int create_url_list(memptr *a_url_list, URL_list *a_out)
Function to parse the Callback header value in subscription requests.
Definition uri.cpp:89
void free_URL_list(URL_list *list)
Frees the memory associated with a URL_list.
Definition uri.cpp:221
char * URLs
Definition uri.hpp:53
uri_type * parsedURLs
Definition uri.hpp:57
size_t size
Number of urls (not characters).
Definition uri.hpp:52
Represents a list of URLs as in the "callback" header of SUBSCRIBE message in GENA.
Definition uri.hpp:51
Manage Uniform Resource Identifier (URI) as specified with RFC 3986.
token fragment
Member variable.
Definition uri.hpp:71
token scheme
Member variable.
Definition uri.hpp:68
@ Relative
The URI is relative, means it hasn't a 'scheme' component.
@ Absolute
The URI is absolute, means it has a 'scheme' component.
hostport_type hostport
Member variable.
Definition uri.hpp:72
size_t size
Size of the buffer.
Definition uri.hpp:50
@ ABS_PATH
The 'path' component begins with a '/'.
@ REL_PATH
The 'path' component doesn't begin with a '/'.
pathType path_type
Member variable.
Definition uri.hpp:69
const char * buff
Buffer.
Definition uri.hpp:49
token pathquery
Member variable.
Definition uri.hpp:70
sockaddr_storage IPaddress
Network socket address.
Definition uri.hpp:58
constexpr int HTTP_SUCCESS
Yet another success code.
Definition uri.hpp:21
uriType type
Member variable.
Definition uri.hpp:67
token text
Pointing to the full host:port string representation.
Definition uri.hpp:57
Represents a URI used in parse_uri and elsewhere.
Definition uri.hpp:64
Buffer used in parsing http messages, urls, etc. Generally this simply holds a pointer into a larger ...
Definition uri.hpp:48
Declaration of the Addrinfo class.
Get information from the operating system about an internet address.
Definition addrinfo.hpp:55
bool get_first()
Get the first entry of an address info from the operating system.
Definition addrinfo.cpp:156
STATE state() const
Get state of the component.
Definition uri.cpp:181
STATE
Defines the possible states of a URI component.
Definition uri.hpp:152
This is a URI reference.
Definition uri.hpp:375
CAuthority authority
Definition uri.hpp:390
CScheme scheme
Definition uri.hpp:389
std::string str() const
Get URI reference string.
Definition uri.cpp:970
Representing a URI that can be modified with a relative reference.
Definition uri.hpp:443
CUriRef target
Resulting URI of merged relative reference to the base URI.
Definition uri.hpp:448
std::string str() const
Get the resulting URI string merged with the relative reference.
Definition uri.cpp:1101
UPnPsdk_VIS int parse_uri(const char *in, size_t max, uri_type *out)
Parses a uri as defined in RFC 3986 (Uniform Resource Identifier).
Definition uri.cpp:1111
size_t length
length of memory without terminating '\0' (read-only).
Definition membuffer.hpp:54
char * buf
start of memory (read/write).
Definition membuffer.hpp:52
pointer to a chunk of memory.
Definition membuffer.hpp:50
#define UPNP_E_OUTOF_MEMORY
Not enough resources are currently available to complete the operation.
Definition messages.hpp:57
#define UPNP_E_INVALID_URL
An URL passed into the function is invalid.
Definition messages.hpp:83
void copy_token(const token *in, const char *in_base, token *out, char *out_base)
Copy the offset and size from a token to another token.
Definition uri.cpp:72
Define macro for synced logging to the console for detailed info and debug.
#define UPnPsdk_EXTERN
Prefix for a portable 'extern' declaration.