mirror of https://github.com/c-ares/c-ares.git
URI parser/writer for ares_set_servers_csv()/ares_get_servers_csv() (#882)
The DNS server format is insufficient for future configurations, such as supporting DNS over TLS (DoT) and DNS over HTTPS (DoH), as well as additional functionality such as domain-specific servers. Already, in the case where different UDP and TCP ports are used, it is impossible to represent in the current format. In order to try to use some standardized format, we are going to define our own URI schemes that should be parse-able by any URI parser. The new scheme will only be used when the configuration cannot otherwise be expressed using the current `ipaddr%iface:port` format, which is the format used as the nameserver configuration in `/etc/resolv.conf`. However, the parser `ares_set_servers_csv()` shall accept the new URI scheme format even when it is not necessary. This PR implements a URI parser and writer and hooks the basic usage into `ares_set_servers_csv()` and `ares_get_servers_csv()` as well as provides updated documentation in the relevant manpages. We will define these URI schemes: * `dns://` - Normal DNS server (UDP + TCP). We need to be careful not to conflict with query params defined in https://datatracker.ietf.org/doc/html/rfc4501 since we'd technically be extending this URI scheme. Port defaults to `53`. * `dns+tls://` - DNS over TLS. Port defaults to `853`. * `dns+https://` - DNS over HTTPS. Port defaults to `443`. We initially will define these query parameters (additional arguments may be required in the future to specify options such as TLS certificate validation rules): * `tcpport` - TCP port to use, only for `dns://` scheme. The `port` specified as part of the `authority` component of the URI will be used for both UDP and TCP by default, this option will override the TCP port. * `ipaddr` - Only for `dns+tls://` and `dns+https://`. If the `authority` component of the URI contains a hostname, this is used to specify the ip address of the hostname. If not specified, will need to use a non-secure server to perform a DNS lookup to retrieve this information. It is always recommended to have both the ip address and fully qualified domain name specified. * `hostname` - Only for `dns+tls://` and `dns+https://`. If the `authority` component of the URI contains an ip address, this is used to specify the fully qualified domain name of the server. If not specified, will need to use a non-secure server to perform a DNS reverse lookup to retrieve this information. It is always recommended to have both the ip address and fully qualified domain name specified. * `domain` - If specified, this server is a domain-specific server. Any queries for this domain will be routed to this server. Multiple servers may be tagged with the same domain. Examples: ``` dns://8.8.8.8 dns://[2001:4860:4860::8888] dns://[fe80::b542:84df:1719:65e3%en0] dns://192.168.1.1:55 dns://192.168.1.1?tcpport=1153 dns://10.0.1.1?domain=myvpn.com dns+tls://8.8.8.8?hostname=dns.google dns+tls://one.one.one.one?ipaddr=1.1.1.1 ``` NOTE: While we are defining the scheme for things like domain-specific servers, DNS over TLS and DNS over HTTPS, the underlying implementations for those features do not yet exist and therefore will result in errors if they are attempted to be used. ### Non-compliance in implementation All these could be easily implemented/fixed if desired, however any such changes would be of no use to the current c-ares usage of URIs: * Does not currently support relative references * Requires use of the authority section, blank is not allowed * The query string is interpreted to be in [application/x-www-form-urlencoded](https://en.wikipedia.org/wiki/Application/x-www-form-urlencoded) format only and will result in parse errors if it is not. This is the most common format used, however technically not valid to mandate this format is used. We could add flags in the future to treat the query string as opaque and leave it to the user to process. Or we could internally have a list of schemes that use this format. * [IDNA](https://en.wikipedia.org/wiki/Internationalized_domain_name) is not supported. * Does not support hex-encoded IPv4 addresses (this is compliant with RFC3986, but not WHATWG) Authored-By: Brad House (@bradh352)pull/886/head
parent
b3b5d0b81d
commit
9dd78e2f23
58 changed files with 3061 additions and 208 deletions
@ -0,0 +1,228 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2024 Brad House |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice (including the next |
||||
* paragraph) shall be included in all copies or substantial portions of the |
||||
* Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#include "ares_private.h" |
||||
#include "ares_htable.h" |
||||
#include "ares_htable_dict.h" |
||||
|
||||
struct ares_htable_dict { |
||||
ares_htable_t *hash; |
||||
}; |
||||
|
||||
typedef struct { |
||||
char *key; |
||||
char *val; |
||||
ares_htable_dict_t *parent; |
||||
} ares_htable_dict_bucket_t; |
||||
|
||||
void ares_htable_dict_destroy(ares_htable_dict_t *htable) |
||||
{ |
||||
if (htable == NULL) { |
||||
return; /* LCOV_EXCL_LINE: DefensiveCoding */ |
||||
} |
||||
|
||||
ares_htable_destroy(htable->hash); |
||||
ares_free(htable); |
||||
} |
||||
|
||||
static unsigned int hash_func(const void *key, unsigned int seed) |
||||
{ |
||||
return ares_htable_hash_FNV1a_casecmp(key, ares_strlen(key), seed); |
||||
} |
||||
|
||||
static const void *bucket_key(const void *bucket) |
||||
{ |
||||
const ares_htable_dict_bucket_t *arg = bucket; |
||||
return arg->key; |
||||
} |
||||
|
||||
static void bucket_free(void *bucket) |
||||
{ |
||||
ares_htable_dict_bucket_t *arg = bucket; |
||||
|
||||
ares_free(arg->key); |
||||
ares_free(arg->val); |
||||
|
||||
ares_free(arg); |
||||
} |
||||
|
||||
static ares_bool_t key_eq(const void *key1, const void *key2) |
||||
{ |
||||
return ares_strcaseeq(key1, key2); |
||||
} |
||||
|
||||
ares_htable_dict_t *ares_htable_dict_create(void) |
||||
{ |
||||
ares_htable_dict_t *htable = ares_malloc(sizeof(*htable)); |
||||
if (htable == NULL) { |
||||
goto fail; /* LCOV_EXCL_LINE: OutOfMemory */ |
||||
} |
||||
|
||||
htable->hash = ares_htable_create(hash_func, bucket_key, bucket_free, key_eq); |
||||
if (htable->hash == NULL) { |
||||
goto fail; /* LCOV_EXCL_LINE: OutOfMemory */ |
||||
} |
||||
|
||||
return htable; |
||||
|
||||
/* LCOV_EXCL_START: OutOfMemory */ |
||||
fail: |
||||
if (htable) { |
||||
ares_htable_destroy(htable->hash); |
||||
ares_free(htable); |
||||
} |
||||
return NULL; |
||||
/* LCOV_EXCL_STOP */ |
||||
} |
||||
|
||||
ares_bool_t ares_htable_dict_insert(ares_htable_dict_t *htable, const char *key, |
||||
const char *val) |
||||
{ |
||||
ares_htable_dict_bucket_t *bucket = NULL; |
||||
|
||||
if (htable == NULL || ares_strlen(key) == 0) { |
||||
goto fail; |
||||
} |
||||
|
||||
bucket = ares_malloc_zero(sizeof(*bucket)); |
||||
if (bucket == NULL) { |
||||
goto fail; |
||||
} |
||||
|
||||
bucket->parent = htable; |
||||
bucket->key = ares_strdup(key); |
||||
if (bucket->key == NULL) { |
||||
goto fail; |
||||
} |
||||
|
||||
if (val != NULL) { |
||||
bucket->val = ares_strdup(val); |
||||
if (bucket->val == NULL) { |
||||
goto fail; |
||||
} |
||||
} |
||||
|
||||
if (!ares_htable_insert(htable->hash, bucket)) { |
||||
goto fail; |
||||
} |
||||
|
||||
return ARES_TRUE; |
||||
|
||||
fail: |
||||
if (bucket) { |
||||
ares_free(bucket->val); |
||||
ares_free(bucket); |
||||
} |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
ares_bool_t ares_htable_dict_get(const ares_htable_dict_t *htable, |
||||
const char *key, const char **val) |
||||
{ |
||||
const ares_htable_dict_bucket_t *bucket = NULL; |
||||
|
||||
if (val) { |
||||
*val = NULL; |
||||
} |
||||
|
||||
if (htable == NULL) { |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
bucket = ares_htable_get(htable->hash, key); |
||||
if (bucket == NULL) { |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
if (val) { |
||||
*val = bucket->val; |
||||
} |
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
const char *ares_htable_dict_get_direct(const ares_htable_dict_t *htable, |
||||
const char *key) |
||||
{ |
||||
const char *val = NULL; |
||||
ares_htable_dict_get(htable, key, &val); |
||||
return val; |
||||
} |
||||
|
||||
ares_bool_t ares_htable_dict_remove(ares_htable_dict_t *htable, const char *key) |
||||
{ |
||||
if (htable == NULL) { |
||||
return ARES_FALSE; |
||||
} |
||||
|
||||
return ares_htable_remove(htable->hash, key); |
||||
} |
||||
|
||||
size_t ares_htable_dict_num_keys(const ares_htable_dict_t *htable) |
||||
{ |
||||
if (htable == NULL) { |
||||
return 0; |
||||
} |
||||
return ares_htable_num_keys(htable->hash); |
||||
} |
||||
|
||||
char **ares_htable_dict_keys(const ares_htable_dict_t *htable, size_t *num) |
||||
{ |
||||
const void **buckets = NULL; |
||||
size_t cnt = 0; |
||||
char **out = NULL; |
||||
size_t i; |
||||
|
||||
if (htable == NULL || num == NULL) { |
||||
return NULL; /* LCOV_EXCL_LINE: DefensiveCoding */ |
||||
} |
||||
|
||||
*num = 0; |
||||
|
||||
buckets = ares_htable_all_buckets(htable->hash, &cnt); |
||||
if (buckets == NULL || cnt == 0) { |
||||
return NULL; |
||||
} |
||||
|
||||
out = ares_malloc_zero(sizeof(*out) * cnt); |
||||
if (out == NULL) { |
||||
goto fail; /* LCOV_EXCL_LINE: OutOfMemory */ |
||||
} |
||||
|
||||
for (i = 0; i < cnt; i++) { |
||||
out[i] = ares_strdup(((const ares_htable_dict_bucket_t *)buckets[i])->key); |
||||
if (out[i] == NULL) { |
||||
goto fail; |
||||
} |
||||
} |
||||
|
||||
ares_free(buckets); |
||||
*num = cnt; |
||||
return out; |
||||
|
||||
fail: |
||||
*num = 0; |
||||
ares_free_array(out, cnt, ares_free); |
||||
return NULL; |
||||
} |
@ -0,0 +1,123 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2024 Brad House |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice (including the next |
||||
* paragraph) shall be included in all copies or substantial portions of the |
||||
* Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#ifndef __ARES__HTABLE_DICT_H |
||||
#define __ARES__HTABLE_DICT_H |
||||
|
||||
/*! \addtogroup ares_htable_dict HashTable with case-insensitive string Key and
|
||||
* string value |
||||
* |
||||
* This data structure wraps the base ares_htable data structure in order to |
||||
* split the key and value data types as string and string, respectively. |
||||
* |
||||
* Average time complexity: |
||||
* - Insert: O(1) |
||||
* - Search: O(1) |
||||
* - Delete: O(1) |
||||
* |
||||
* @{ |
||||
*/ |
||||
|
||||
struct ares_htable_dict; |
||||
|
||||
/*! Opaque data type for string key, string value hash table
|
||||
* implementation */ |
||||
typedef struct ares_htable_dict ares_htable_dict_t; |
||||
|
||||
/*! Destroy hashtable
|
||||
* |
||||
* \param[in] htable Initialized hashtable |
||||
*/ |
||||
CARES_EXTERN void ares_htable_dict_destroy(ares_htable_dict_t *htable); |
||||
|
||||
/*! Create void pointer key, string value hash table
|
||||
* |
||||
*/ |
||||
CARES_EXTERN ares_htable_dict_t *ares_htable_dict_create(void); |
||||
|
||||
/*! Insert key/value into hash table
|
||||
* |
||||
* \param[in] htable Initialized hash table |
||||
* \param[in] key key to associate with value |
||||
* \param[in] val value to store (duplicates). |
||||
* \return ARES_TRUE on success, ARES_FALSE on failure or out of memory |
||||
*/ |
||||
CARES_EXTERN ares_bool_t ares_htable_dict_insert(ares_htable_dict_t *htable, |
||||
const char *key, |
||||
const char *val); |
||||
|
||||
/*! Retrieve value from hashtable based on key
|
||||
* |
||||
* \param[in] htable Initialized hash table |
||||
* \param[in] key key to use to search |
||||
* \param[out] val Optional. Pointer to store value. |
||||
* \return ARES_TRUE on success, ARES_FALSE on failure |
||||
*/ |
||||
CARES_EXTERN ares_bool_t ares_htable_dict_get(const ares_htable_dict_t *htable, |
||||
const char *key, |
||||
const char **val); |
||||
|
||||
/*! Retrieve value from hashtable directly as return value. Caveat to this
|
||||
* function over ares_htable_dict_get() is that if a NULL value is stored |
||||
* you cannot determine if the key is not found or the value is NULL. |
||||
* |
||||
* \param[in] htable Initialized hash table |
||||
* \param[in] key key to use to search |
||||
* \return value associated with key in hashtable or NULL |
||||
*/ |
||||
CARES_EXTERN const char * |
||||
ares_htable_dict_get_direct(const ares_htable_dict_t *htable, |
||||
const char *key); |
||||
|
||||
/*! Remove a value from the hashtable by key
|
||||
* |
||||
* \param[in] htable Initialized hash table |
||||
* \param[in] key key to use to search |
||||
* \return ARES_TRUE if found, ARES_FALSE if not |
||||
*/ |
||||
CARES_EXTERN ares_bool_t ares_htable_dict_remove(ares_htable_dict_t *htable, |
||||
const char *key); |
||||
|
||||
/*! Retrieve the number of keys stored in the hash table
|
||||
* |
||||
* \param[in] htable Initialized hash table |
||||
* \return count |
||||
*/ |
||||
CARES_EXTERN size_t ares_htable_dict_num_keys(const ares_htable_dict_t *htable); |
||||
|
||||
/*! Retrieve an array of keys from the hashtable.
|
||||
* |
||||
* \param[in] htable Initialized hashtable |
||||
* \param[out] num Count of returned keys |
||||
* \return Array of keys in the hashtable. Must be free'd with |
||||
* ares_free_array(strs, num, ares_free); |
||||
*/ |
||||
CARES_EXTERN char **ares_htable_dict_keys(const ares_htable_dict_t *htable, |
||||
size_t *num); |
||||
|
||||
|
||||
/*! @} */ |
||||
|
||||
#endif /* __ARES__HTABLE_DICT_H */ |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,252 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2024 Brad House |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice (including the next |
||||
* paragraph) shall be included in all copies or substantial portions of the |
||||
* Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#ifndef __ARES_URI_H |
||||
#define __ARES_URI_H |
||||
|
||||
/*! \addtogroup ares_uri URI parser and writer implementation
|
||||
* |
||||
* This is a fairly complete URI parser and writer implementation (RFC 3986) for |
||||
* schemes which use the :// syntax. Does not currently support URIs without an
|
||||
* authority section, such as "mailto:person@example.com". |
||||
* |
||||
* Its implementation is overkill for our current needs to be able to express |
||||
* DNS server configuration, but there was really no reason not to support |
||||
* a greater subset of the specification. |
||||
* |
||||
* @{ |
||||
*/ |
||||
|
||||
|
||||
struct ares_uri; |
||||
|
||||
/*! URI object */ |
||||
typedef struct ares_uri ares_uri_t; |
||||
|
||||
/*! Create a new URI object
|
||||
* |
||||
* \return new ares_uri_t, must be freed with ares_uri_destroy() |
||||
*/ |
||||
ares_uri_t *ares_uri_create(void); |
||||
|
||||
/*! Destroy an initialized URI object
|
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
*/ |
||||
void ares_uri_destroy(ares_uri_t *uri); |
||||
|
||||
/*! Set the URI scheme. Automatically lower-cases the scheme provided.
|
||||
* Only allows Alpha, Digit, +, -, and . characters. Maximum length is |
||||
* 15 characters. This is required to be set to write a URI. |
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \param[in] scheme Scheme to set the object to use |
||||
* \return ARES_SUCCESS on success |
||||
*/ |
||||
ares_status_t ares_uri_set_scheme(ares_uri_t *uri, const char *scheme); |
||||
|
||||
/*! Retrieve the currently configured URI scheme.
|
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \return string containing URI scheme |
||||
*/ |
||||
const char *ares_uri_get_scheme(ares_uri_t *uri); |
||||
|
||||
/*! Set the username in the URI object
|
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \param[in] username Username to set. May be NULL to unset existing username. |
||||
* \return ARES_SUCCESS on success |
||||
*/ |
||||
ares_status_t ares_uri_set_username(ares_uri_t *uri, const char *username); |
||||
|
||||
/*! Retrieve the currently configured username.
|
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \return string containing username, maybe NULL if not set. |
||||
*/ |
||||
const char *ares_uri_get_username(ares_uri_t *uri); |
||||
|
||||
/*! Set the password in the URI object
|
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \param[in] password Password to set. May be NULL to unset existing password. |
||||
* \return ARES_SUCCESS on success |
||||
*/ |
||||
ares_status_t ares_uri_set_password(ares_uri_t *uri, const char *password); |
||||
|
||||
/*! Retrieve the currently configured password.
|
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \return string containing password, maybe NULL if not set. |
||||
*/ |
||||
const char *ares_uri_get_password(ares_uri_t *uri); |
||||
|
||||
/*! Set the host or ip address in the URI object. This is required to be
|
||||
* set to write a URI. The character set is strictly validated. |
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \param[in] host IPv4, IPv6, or hostname to set. |
||||
* \return ARES_SUCCESS on success |
||||
*/ |
||||
ares_status_t ares_uri_set_host(ares_uri_t *uri, const char *host); |
||||
|
||||
/*! Retrieve the currently configured host (or ip address). IPv6 addresses
|
||||
* May include a link-local scope (e.g. fe80::b542:84df:1719:65e3%en0). |
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \return string containing host, maybe NULL if not set. |
||||
*/ |
||||
const char *ares_uri_get_host(ares_uri_t *uri); |
||||
|
||||
/*! Set the port to use in the URI object. A port value of 0 will omit
|
||||
* the port from the URI when written, thus using the scheme's default. |
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \param[in] port Port to set. Use 0 to unset. |
||||
* \return ARES_SUCCESS on success |
||||
*/ |
||||
ares_status_t ares_uri_set_port(ares_uri_t *uri, unsigned short port); |
||||
|
||||
/*! Retrieve the currently configured port
|
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \return port number, or 0 if not set. |
||||
*/ |
||||
unsigned short ares_uri_get_port(ares_uri_t *uri); |
||||
|
||||
/*! Set the path in the URI object. Unsupported characters will be URI-encoded
|
||||
* when written. |
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \param[in] path Path to set. May be NULL to unset existing path. |
||||
* \return ARES_SUCCESS on success |
||||
*/ |
||||
ares_status_t ares_uri_set_path(ares_uri_t *uri, const char *path); |
||||
|
||||
/*! Retrieves the path in the URI object. If retrieved after parse, this
|
||||
* value will be URI-decoded already. |
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \return path string, or NULL if not set. |
||||
*/ |
||||
const char *ares_uri_get_path(ares_uri_t *uri); |
||||
|
||||
/*! Set a new query key/value pair. There is no set order for query keys
|
||||
* when output in the URI, they will be emitted in a random order. Keys are |
||||
* case-insensitive. Query keys and values will be automatically URI-encoded |
||||
* when written. |
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \param[in] key Query key to use, must be non-zero length. |
||||
* \param[in] val Query value to use, may be NULL. |
||||
* \return ARES_SUCCESS on success |
||||
*/ |
||||
ares_status_t ares_uri_set_query_key(ares_uri_t *uri, const char *key, |
||||
const char *val); |
||||
|
||||
/*! Delete a specific query key.
|
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \param[in] key Key to delete. |
||||
* \return ARES_SUCCESS if deleted, ARES_ENOTFOUND if not found |
||||
*/ |
||||
ares_status_t ares_uri_del_query_key(ares_uri_t *uri, const char *key); |
||||
|
||||
/*! Retrieve the value associted with a query key. Keys are case-insensitive.
|
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \param[in] key Key to retrieve. |
||||
* \return string representing value, may be NULL if either not found or |
||||
* NULL value set. There is currently no way to indicate the |
||||
* difference. |
||||
*/ |
||||
const char *ares_uri_get_query_key(ares_uri_t *uri, const char *key); |
||||
|
||||
/*! Retrieve a complete list of query keys.
|
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \param[out] num Number of keys. |
||||
* \return NULL on failure or no keys. Use |
||||
* ares_free_array(keys, num, ares_free) when done with array. |
||||
*/ |
||||
char **ares_uri_get_query_keys(ares_uri_t *uri, size_t *num); |
||||
|
||||
/*! Set the fragment in the URI object. Unsupported characters will be
|
||||
* URI-encoded when written. |
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \param[in] fragment Fragment to set. May be NULL to unset existing fragment. |
||||
* \return ARES_SUCCESS on success |
||||
*/ |
||||
ares_status_t ares_uri_set_fragment(ares_uri_t *uri, const char *fragment); |
||||
|
||||
/*! Retrieves the fragment in the URI object. If retrieved after parse, this
|
||||
* value will be URI-decoded already. |
||||
* |
||||
* \param[in] uri Initialized URI object |
||||
* \return fragment string, or NULL if not set. |
||||
*/ |
||||
const char *ares_uri_get_fragment(ares_uri_t *uri); |
||||
|
||||
/*! Parse the provided URI buffer into a new URI object.
|
||||
* |
||||
* \param[out] out Returned new URI object. free with ares_uri_destroy(). |
||||
* \param[in] buf Buffer object containing the URI |
||||
* \return ARES_SUCCESS on successful parse. On failure the 'buf' object will |
||||
* be restored to its initial state in case another parser needs to |
||||
* be attempted. |
||||
*/ |
||||
ares_status_t ares_uri_parse_buf(ares_uri_t **out, ares_buf_t *buf); |
||||
|
||||
/*! Parse the provided URI string into a new URI object.
|
||||
* |
||||
* \param[out] out Returned new URI object. free with ares_uri_destroy(). |
||||
* \param[in] uri URI string to parse |
||||
* \return ARES_SUCCESS on successful parse |
||||
*/ |
||||
ares_status_t ares_uri_parse(ares_uri_t **out, const char *uri); |
||||
|
||||
/*! Write URI object to a new string buffer. Requires at least the scheme
|
||||
* and host to be set for this to succeed. |
||||
* |
||||
* \param[out] out Returned new URI string. Free with ares_free(). |
||||
* \param[in] uri Initialized URI object. |
||||
* \return ARES_SUCCESS on successful write. |
||||
*/ |
||||
ares_status_t ares_uri_write(char **out, ares_uri_t *uri); |
||||
|
||||
/*! Write URI object to an existing ares_buf_t object. Requires at least the
|
||||
* scheme and host to be set for this to succeed. |
||||
* |
||||
* \param[in] uri Initialized URI object. |
||||
* \param[in,out] buf Destination buf object. |
||||
* \return ARES_SUCCESS on successful write. |
||||
*/ |
||||
ares_status_t ares_uri_write_buf(ares_uri_t *uri, ares_buf_t *buf); |
||||
|
||||
/*! @} */ |
||||
|
||||
#endif /* __ARES_URI_H */ |
@ -0,0 +1 @@ |
||||
https://www.example.com |
@ -0,0 +1 @@ |
||||
https://www.example.com?key= |
@ -0,0 +1 @@ |
||||
https://www.example.com#fragment |
@ -0,0 +1 @@ |
||||
https://user:password@www.example.com/path |
@ -0,0 +1 @@ |
||||
https://user:password@www.example.com/path#fragment |
@ -0,0 +1 @@ |
||||
https://user:password@www.example.com/path?key=val |
@ -0,0 +1 @@ |
||||
https://user:password@www.example.com/path?key=val#fragment |
@ -0,0 +1 @@ |
||||
HTTPS://www.example.com |
@ -0,0 +1 @@ |
||||
https://www.example.com?key=hello+world |
@ -0,0 +1 @@ |
||||
https://www.example.com?key=val%26 |
@ -0,0 +1 @@ |
||||
https://www.example.com:8443 |
@ -0,0 +1 @@ |
||||
https://www.example.com?key%26=val |
@ -0,0 +1 @@ |
||||
https://www.example.com?key=Aa2-._~/?uri20\'\(\)\*,;:@ |
@ -0,0 +1 @@ |
||||
https://www.example.com?key1=val1&key2=val2&key3=val3&key4=val4 |
@ -0,0 +1 @@ |
||||
https://www.example.com?key=%41%61%32%2D%2E%5f%7e%2F%3F%21%24%27%28%29%2a%2C%3b%3a%40 |
@ -0,0 +1 @@ |
||||
dns+tls://192.168.1.1:53 |
@ -0,0 +1 @@ |
||||
dns+tls://[fe80::1]:53 |
@ -0,0 +1 @@ |
||||
dns://[fe80::b542:84df:1719:65e3%en0] |
@ -0,0 +1 @@ |
||||
dns+tls://[fe80:00::00:1]:53 |
@ -0,0 +1 @@ |
||||
d.n+s-tls://www.example.com |
@ -0,0 +1 @@ |
||||
dns*tls://www.example.com |
@ -0,0 +1 @@ |
||||
https://user:password@www.example.com |
@ -0,0 +1 @@ |
||||
https://www.example.com?key=val%01 |
@ -0,0 +1 @@ |
||||
abcdef0123456789://www.example.com |
@ -0,0 +1 @@ |
||||
www.example.com |
@ -0,0 +1 @@ |
||||
https://www.example.com?key=val%0 |
@ -0,0 +1 @@ |
||||
https://www.example.com?key=val%AZ |
@ -0,0 +1 @@ |
||||
https://www.example.com?key=hello world |
@ -0,0 +1 @@ |
||||
https://:password@www.example.com |
@ -0,0 +1 @@ |
||||
dns+tls://[fe8G::1] |
@ -0,0 +1 @@ |
||||
https://user%25:password@www.example.com |
@ -0,0 +1 @@ |
||||
https://user:password%25@www.example.com |
@ -0,0 +1 @@ |
||||
https://user@www.example.com |
@ -0,0 +1 @@ |
||||
https://www.example.com/path |
@ -0,0 +1 @@ |
||||
https://www.example.com?key=val |
@ -0,0 +1 @@ |
||||
https://www.example.com?key |
Loading…
Reference in new issue