Heh, I hate writing docs ... so, I made a few huge manpages. If you
have someone that loves writing docs to help out, send them this way :)
I wouldn't recommend using ares_dns_record_create() as you have to jump
through a lot of hoops to do things properly, like setting up EDNS and
whatnot. Just use ares_query_dnsrec() which does the heavy lifting for you.
The worst part in all honesty is the 'option' syntax, as HTTPS/SVCB have
dynamic option/parameter records that can be attached, and can be in any
format. I extracted some code from adig for printing those. I'm not
sure if you need all the data or if you're just looking for some certain
data. The current/known params are:
/*! SVCB (and HTTPS) RR known parameters */
typedef enum {
/*! Mandatory keys in this RR (RFC 9460 Section 8) */
ARES_SVCB_PARAM_MANDATORY = 0,
/*! Additional supported protocols (RFC 9460 Section 7.1) */
ARES_SVCB_PARAM_ALPN = 1,
/*! No support for default protocol (RFC 9460 Section 7.1) */
ARES_SVCB_PARAM_NO_DEFAULT_ALPN = 2,
/*! Port for alternative endpoint (RFC 9460 Section 7.2) */
ARES_SVCB_PARAM_PORT = 3,
/*! IPv4 address hints (RFC 9460 Section 7.3) */
ARES_SVCB_PARAM_IPV4HINT = 4,
/*! RESERVED (held for Encrypted ClientHello) */
ARES_SVCB_PARAM_ECH = 5,
/*! IPv6 address hints (RFC 9460 Section 7.3) */
ARES_SVCB_PARAM_IPV6HINT = 6
} ares_svcb_param_t;
See my attached example, compile/run like:
cc -I/usr/local/include -Wall -o ares-https ares-https.c -Wl,-rpath
/usr/local/lib -lcares
./ares-https www.cloudflare.com
Result: Successful completion, timeouts: 0
HTTPS Priority: 1
HTTPS Target:
HTTPS Params:
alpn(1)="h3, h2"
ipv4hint(4)=104.16.123.96, 104.16.124.96
ipv6hint(6)=2606:4700::6810:7b60, 2606:4700::6810:7c60
-Brad
On 1/16/25 3:05 AM, Daniel Stenberg via c-ares wrote:
Hi,
I want is to create a request for a HTTPS record and get the answer or
an error. I struggle to understand how to do this. The
ares_dns_record_create is hard to follow and understand what sequence
to use and how to glue everything together.
I think the docs would benefit from being split up to document one
function per man page as God intended. With examples showing how they
can be used.
This is my initial attempt to send off the HTTPS RR request:
ares_dns_record_create(&dnsrec, 0 /* id */, 0, /* flags */
ARES_OPCODE_QUERY, ARES_RCODE_NOERROR);
ares_dns_record_query_add(dnsrec, hostname,
ARES_REC_TYPE_HTTPS, ARES_CLASS_IN);
ares_send_dnsrec((ares_channel)resolver_hgandle,
dnsrec, dnsrec_done_cb, data, NULL);
But I simply cannot figure out how the dnsrec_done_cb callback should
be written to parse the incoming reply?
What helpers should I use?
#include <stdio.h>
#include <string.h>
#include <ares.h>
static void print_opt_none(const unsigned char *val, size_t val_len)
{
(void)val;
if (val_len != 0) {
printf("INVALID!");
}
}
static void print_opt_addr_list(const unsigned char *val, size_t val_len)
{
size_t i;
if (val_len % 4 != 0) {
printf("INVALID!");
return;
}
for (i = 0; i < val_len; i += 4) {
char buf[256] = "";
ares_inet_ntop(AF_INET, val + i, buf, sizeof(buf));
if (i != 0) {
printf(", ");
}
printf("%s", buf);
}
}
static void print_opt_addr6_list(const unsigned char *val, size_t val_len)
{
size_t i;
if (val_len % 16 != 0) {
printf("INVALID!");
return;
}
for (i = 0; i < val_len; i += 16) {
char buf[256] = "";
ares_inet_ntop(AF_INET6, val + i, buf, sizeof(buf));
if (i != 0) {
printf(", ");
}
printf("%s", buf);
}
}
static void print_opt_u8_list(const unsigned char *val, size_t val_len)
{
size_t i;
for (i = 0; i < val_len; i++) {
if (i != 0) {
printf(", ");
}
printf("%u", (unsigned int)val[i]);
}
}
static void print_opt_u16_list(const unsigned char *val, size_t val_len)
{
size_t i;
if (val_len < 2 || val_len % 2 != 0) {
printf("INVALID!");
return;
}
for (i = 0; i < val_len; i += 2) {
unsigned short u16 = 0;
unsigned short c;
/* Jumping over backwards to try to avoid odd compiler warnings */
c = (unsigned short)val[i];
u16 |= (unsigned short)((c << 8) & 0xFFFF);
c = (unsigned short)val[i + 1];
u16 |= c;
if (i != 0) {
printf(", ");
}
printf("%u", (unsigned int)u16);
}
}
static void print_opt_u32_list(const unsigned char *val, size_t val_len)
{
size_t i;
if (val_len < 4 || val_len % 4 != 0) {
printf("INVALID!");
return;
}
for (i = 0; i < val_len; i += 4) {
unsigned int u32 = 0;
u32 |= (unsigned int)(val[i] << 24);
u32 |= (unsigned int)(val[i + 1] << 16);
u32 |= (unsigned int)(val[i + 2] << 8);
u32 |= (unsigned int)(val[i + 3]);
if (i != 0) {
printf(", ");
}
printf("%u", u32);
}
}
static void print_opt_str_list(const unsigned char *val, size_t val_len)
{
size_t cnt = 0;
printf("\"");
while (val_len) {
long read_len = 0;
unsigned char *str = NULL;
ares_status_t status;
if (cnt) {
printf(", ");
}
status = (ares_status_t)ares_expand_string(val, val, (int)val_len, &str,
&read_len);
if (status != ARES_SUCCESS) {
printf("INVALID");
break;
}
printf("%s", str);
ares_free_string(str);
val_len -= (size_t)read_len;
val += read_len;
cnt++;
}
printf("\"");
}
static void print_opt_name(const unsigned char *val, size_t val_len)
{
char *str = NULL;
long read_len = 0;
if (ares_expand_name(val, val, (int)val_len, &str, &read_len) !=
ARES_SUCCESS) {
printf("INVALID!");
return;
}
printf("%s.", str);
ares_free_string(str);
}
static void print_opt_bin(const unsigned char *val, size_t val_len)
{
size_t i;
for (i = 0; i < val_len; i++) {
printf("%02x", (unsigned int)val[i]);
}
}
static void print_opt(const ares_dns_rr_t *rr, ares_dns_rr_key_t key, size_t
idx)
{
size_t val_len = 0;
const unsigned char *val = NULL;
unsigned short opt;
const char *name;
opt = ares_dns_rr_get_opt(rr, key, idx, &val, &val_len);
name = ares_dns_opt_get_name(key, opt);
if (name == NULL) {
printf("\tkey%u", (unsigned int)opt);
} else {
printf("\t%s(%u)", name, (unsigned int)opt);
}
if (val_len == 0) {
return;
}
printf("=");
switch (ares_dns_opt_get_datatype(key, opt)) {
case ARES_OPT_DATATYPE_NONE:
print_opt_none(val, val_len);
break;
case ARES_OPT_DATATYPE_U8_LIST:
print_opt_u8_list(val, val_len);
break;
case ARES_OPT_DATATYPE_INADDR4_LIST:
print_opt_addr_list(val, val_len);
break;
case ARES_OPT_DATATYPE_INADDR6_LIST:
print_opt_addr6_list(val, val_len);
break;
case ARES_OPT_DATATYPE_U16:
case ARES_OPT_DATATYPE_U16_LIST:
print_opt_u16_list(val, val_len);
break;
case ARES_OPT_DATATYPE_U32:
case ARES_OPT_DATATYPE_U32_LIST:
print_opt_u32_list(val, val_len);
break;
case ARES_OPT_DATATYPE_STR_LIST:
print_opt_str_list(val, val_len);
break;
case ARES_OPT_DATATYPE_BIN:
print_opt_bin(val, val_len);
break;
case ARES_OPT_DATATYPE_NAME:
print_opt_name(val, val_len);
break;
}
printf("\n");
}
static void dnsrec_cb(void *arg, ares_status_t status, size_t timeouts,
const ares_dns_record_t *dnsrec)
{
size_t i;
const ares_dns_rr_t *rr = NULL;
(void)arg; /* Example does not use user context */
printf("Result: %s, timeouts: %zu\n", ares_strerror(status), timeouts);
if (dnsrec == NULL) {
return;
}
for (i=0; i<ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER); i++) {
size_t opt;
rr = ares_dns_record_rr_get_const(dnsrec, ARES_SECTION_ANSWER, i);
if (ares_dns_rr_get_type(rr) != ARES_REC_TYPE_HTTPS) {
continue;
}
printf("HTTPS Priority: %u\n", ares_dns_rr_get_u16(rr,
ARES_RR_HTTPS_PRIORITY));
printf("HTTPS Target: %s.\n", ares_dns_rr_get_str(rr,
ARES_RR_HTTPS_TARGET));
printf("HTTPS Params:\n");
for (opt=0; opt<ares_dns_rr_get_opt_cnt(rr, ARES_RR_HTTPS_PARAMS); opt++) {
print_opt(rr, ARES_RR_HTTPS_PARAMS, opt);
}
}
}
int main(int argc, char **argv)
{
ares_channel_t *channel = NULL;
struct ares_options options;
int optmask = 0;
ares_status_t status;
if (argc != 2) {
printf("Usage: %s domain\n", argv[0]);
return 1;
}
/* Initialize library */
ares_library_init(ARES_LIB_INIT_ALL);
if (!ares_threadsafety()) {
printf("c-ares not compiled with thread support\n");
return 1;
}
/* Enable event thread so we don't have to monitor file descriptors */
memset(&options, 0, sizeof(options));
optmask |= ARES_OPT_EVENT_THREAD;
options.evsys = ARES_EVSYS_DEFAULT;
/* Initialize channel to run queries, a single channel can accept unlimited
* queries */
status = ares_init_options(&channel, &options, optmask);
if (status != ARES_SUCCESS) {
printf("c-ares initialization issue: %s\n", ares_strerror(status));
return 1;
}
/* Perform query */
status = ares_query_dnsrec(channel, argv[1], ARES_CLASS_IN,
ARES_REC_TYPE_HTTPS, dnsrec_cb,
NULL /* user context */, NULL /* qid */);
if (status != ARES_SUCCESS) {
printf("failed to enqueue query: %s\n", ares_strerror(status));
return 1;
}
/* Wait until no more requests are left to be processed */
ares_queue_wait_empty(channel, -1);
/* Cleanup */
ares_destroy(channel);
ares_library_cleanup();
return 0;
}
--
c-ares mailing list
c-ares@lists.haxx.se
https://lists.haxx.se/mailman/listinfo/c-ares