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

Reply via email to