rewrite adig using new helpers (#607)

adig previously performed manual parsing of the DNS records.  Now it can focus strictly on formatting of output data for printing.  It simply iterates across the parsed DNS packet and queries for the RRs, parameters for each RR, and the datatypes for each parameter.  adig will now automatically pick up new RRs from the c-ares library due to the dynamic nature.

The adig format also now more closely resembles that of BIND's `dig` output.

A few more helpers needed to be added to the c-ares library that were missing.  There ware a couple of minor bugs and enhancements also needed.

Example:
```
./adig -t ANY www.google.com

; <<>> c-ares DiG 1.21.0 <<>> www.google.com
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: RCODE, id: 23913
;; flags: qr rd ra; QUERY: 1, ANSWER: 11, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: 0; udp: 512	
;; QUESTION SECTION:
;www.google.com.			IN	ANY

;; ANSWER SECTION:
www.google.com.		162	IN	A	142.251.107.99
www.google.com.		162	IN	A	142.251.107.105
www.google.com.		162	IN	A	142.251.107.103
www.google.com.		162	IN	A	142.251.107.147
www.google.com.		162	IN	A	142.251.107.104
www.google.com.		162	IN	A	142.251.107.106
www.google.com.		162	IN	AAAA	2607:f8b0:400c:c32::93
www.google.com.		162	IN	AAAA	2607:f8b0:400c:c32::69
www.google.com.		162	IN	AAAA	2607:f8b0:400c:c32::68
www.google.com.		162	IN	AAAA	2607:f8b0:400c:c32::6a
www.google.com.		21462	IN	HTTPS	1 . alpn="h2,h3"

;; MSG SIZE  rcvd: 276
```

Fix By: Brad House (@bradh352)
pull/614/head
Brad House 1 year ago committed by GitHub
parent de323adfd5
commit 956b7ebf68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      docs/adig.1
  2. 10
      include/ares.h
  3. 198
      include/ares_dns_record.h
  4. 33
      src/lib/ares__buf.c
  5. 20
      src/lib/ares__buf.h
  6. 10
      src/lib/ares__hosts_file.c
  7. 313
      src/lib/ares_dns_mapping.c
  8. 72
      src/lib/ares_dns_record.c
  9. 40
      src/lib/ares_gethostbyaddr.c
  10. 45
      src/lib/ares_math.c
  11. 15
      src/lib/ares_private.h
  12. 10
      src/lib/ares_process.c
  13. 24
      src/lib/ares_query.c
  14. 33
      src/lib/ares_send.c
  15. 1477
      src/tools/adig.c
  16. 16
      src/tools/ahost.c
  17. 72
      src/tools/ares_getopt.c
  18. 38
      src/tools/ares_getopt.h
  19. 21
      test/ares-test-internal.cc

@ -44,7 +44,8 @@ Print some extra debugging output.
\fB\-f\fR flag
Add a behavior control flag.
Possible values for flag are
igntc - ignore to query in TCP to get truncated UDP answer,
igntc - ignore query truncation, return answer as-is instead of retrying
via tcp.
noaliases - don't honor the HOSTALIASES environment variable,
norecurse - don't query upstream servers recursively,
primary - use the first server,
@ -61,27 +62,14 @@ Servers are tried in round-robin, if the previous one failed.
\fB\-t\fR type
Query records of specified type.
Possible values for type are
A (default), AAAA, AFSDB, ANY, AXFR, CNAME, GPOS, HINFO, ISDN, KEY, LOC, MAILA,
MAILB, MB, MD, MF, MG, MINFO, MR, MX, NAPTR, NS, NSAP, NSAP_PTR, NULL,
PTR, PX, RP, RT, SIG, SOA, SRV, TXT, URI, WKS and X25.
A (default), AAAA, ANY, AXFR, CNAME, HINFO, MX, NAPTR, NS, PTR, SOA, SRV, TXT,
URI, CAA, SVCB, and HTTPS.
.TP
\fB\-T\fR port
Connect to the specified TCP port of DNS server.
.TP
\fB\-U\fR port
Connect to the specified UDP port of DNS server.
.TP
\fB\-x\fR
For an IPv4 \fB-t PTR a.b.c.d\fR lookup, query for
.br
\fBd.c.b.a.in-addr.arpa.\fR
This more often gives correct names in the \fBANSWER\fR.
.br
For an IPv6 \fB-t PTR addr\fR lookup, query for \fBa.b.c....z.IP6.ARPA.\fR
.TP
\fB\-xx\fR
As for \fB-x\fR and an IPv6 address, compact \fBa.b.c....z.IP6.ARPA.\fR into a RFC-2673 bit-string.
This compacted \fBbit-string\fR form is not supported by many DNS-servers.
.SH "REPORTING BUGS"
Report bugs to the c-ares mailing list:

@ -506,6 +506,16 @@ struct ares_in6_addr {
} _S6_un;
};
struct ares_addr {
int family;
union {
struct in_addr addr4;
struct ares_in6_addr addr6;
} addr;
};
struct ares_addrttl {
struct in_addr ipaddr;
int ttl;

@ -115,17 +115,17 @@ typedef enum {
/*! DNS Response Codes from server */
typedef enum {
ARES_RCODE_NOERROR = 0, /*!< Success */
ARES_RCODE_FORMAT_ERROR = 1, /*!< Format error. The name server was unable
ARES_RCODE_FORMERR = 1, /*!< Format error. The name server was unable
* to interpret the query. */
ARES_RCODE_SERVER_FAILURE = 2, /*!< Server Failure. The name server was
ARES_RCODE_SERVFAIL = 2, /*!< Server Failure. The name server was
* unable to process this query due to a
* problem with the nameserver */
ARES_RCODE_NAME_ERROR = 3, /*!< Name Error. Meaningful only for
ARES_RCODE_NXDOMAIN = 3, /*!< Name Error. Meaningful only for
* responses from an authoritative name
* server, this code signifies that the
* domain name referenced in the query does
* not exist. */
ARES_RCODE_NOT_IMPLEMENTED = 4, /*!< Not implemented. The name server does
ARES_RCODE_NOTIMP = 4, /*!< Not implemented. The name server does
* not support the requested kind of
* query */
ARES_RCODE_REFUSED = 5, /*!< Refused. The name server refuses to
@ -143,6 +143,15 @@ typedef enum {
ARES_RCODE_NOTZONE = 10, /*!< RFC 2136. A name used in the Prerequisite
* or Update Section is not within the zone
* denoted by the Zone Section. */
ARES_RCODE_DSOTYPEI = 11, /*!< RFC 8409. DSO-TYPE Not implemented */
ARES_RCODE_BADSIG = 16, /*!< RFC 8945. TSIG Signature Failure */
ARES_RCODE_BADKEY = 17, /*!< RFC 8945. Key not recognized. */
ARES_RCODE_BADTIME = 18, /*!< RFC 8945. Signature out of time window. */
ARES_RCODE_BADMODE = 19, /*!< RFC 2930. Bad TKEY Mode */
ARES_RCODE_BADNAME = 20, /*!< RFC 2930. Duplicate Key Name */
ARES_RCODE_BADALG = 21, /*!< RFC 2930. Algorithm not supported */
ARES_RCODE_BADTRUNC = 22, /*!< RFC 8945. Bad Truncation */
ARES_RCODE_BADCOOKIE = 23, /*!< RVC 7973. Bad/missing Server Cookie */
} ares_dns_rcode_t;
/*! Data types used */
@ -152,13 +161,14 @@ typedef enum {
ARES_DATATYPE_U8 = 3, /*!< 8bit unsigned integer */
ARES_DATATYPE_U16 = 4, /*!< 16bit unsigned integer */
ARES_DATATYPE_U32 = 5, /*!< 32bit unsigned integer */
ARES_DATATYPE_STR = 6, /*!< Null-terminated string */
ARES_DATATYPE_BIN = 7, /*!< Binary data */
ARES_DATATYPE_BINP = 8, /*!< Officially defined as binary data, but likely
ARES_DATATYPE_NAME = 6, /*!< Null-terminated string of a domain name */
ARES_DATATYPE_STR = 7, /*!< Null-terminated string */
ARES_DATATYPE_BIN = 8, /*!< Binary data */
ARES_DATATYPE_BINP = 9, /*!< Officially defined as binary data, but likely
* printable. Guaranteed to have a NULL
* terminator for convenience (not included in
* length) */
ARES_DATATYPE_OPT = 9, /*!< Array of options. 16bit identifier, BINP
ARES_DATATYPE_OPT = 10, /*!< Array of options. 16bit identifier, BIN
* data. */
} ares_dns_datatype_t;
@ -168,13 +178,13 @@ typedef enum {
typedef enum {
/*! A Record. Address. Datatype: INADDR */
ARES_RR_A_ADDR = (ARES_REC_TYPE_A * 100) + 1,
/*! NS Record. Name. Datatype: STR */
/*! NS Record. Name. Datatype: NAME */
ARES_RR_NS_NSDNAME = (ARES_REC_TYPE_NS * 100) + 1,
/*! CNAME Record. CName. Datatype: STR */
/*! CNAME Record. CName. Datatype: NAME */
ARES_RR_CNAME_CNAME = (ARES_REC_TYPE_CNAME * 100) + 1,
/*! SOA Record. MNAME, Primary Source of Data. Datatype: STR */
/*! SOA Record. MNAME, Primary Source of Data. Datatype: NAME */
ARES_RR_SOA_MNAME = (ARES_REC_TYPE_SOA * 100) + 1,
/*! SOA Record. RNAME, Mailbox of person responsible. Datatype: STR */
/*! SOA Record. RNAME, Mailbox of person responsible. Datatype: NAME */
ARES_RR_SOA_RNAME = (ARES_REC_TYPE_SOA * 100) + 2,
/*! SOA Record. Serial, version. Datatype: U32 */
ARES_RR_SOA_SERIAL = (ARES_REC_TYPE_SOA * 100) + 3,
@ -186,7 +196,7 @@ typedef enum {
ARES_RR_SOA_EXPIRE = (ARES_REC_TYPE_SOA * 100) + 6,
/*! SOA Record. Minimum, RR TTL. Datatype: U32 */
ARES_RR_SOA_MINIMUM = (ARES_REC_TYPE_SOA * 100) + 7,
/*! PTR Record. DNAME, pointer domain. Datatype: STR */
/*! PTR Record. DNAME, pointer domain. Datatype: NAME */
ARES_RR_PTR_DNAME = (ARES_REC_TYPE_PTR * 100) + 1,
/*! HINFO Record. CPU. Datatype: STR */
ARES_RR_HINFO_CPU = (ARES_REC_TYPE_HINFO * 100) + 1,
@ -194,7 +204,7 @@ typedef enum {
ARES_RR_HINFO_OS = (ARES_REC_TYPE_HINFO * 100) + 2,
/*! MX Record. Preference. Datatype: U16 */
ARES_RR_MX_PREFERENCE = (ARES_REC_TYPE_MX * 100) + 1,
/*! MX Record. Exchange, domain. Datatype: STR */
/*! MX Record. Exchange, domain. Datatype: NAME */
ARES_RR_MX_EXCHANGE = (ARES_REC_TYPE_MX * 100) + 2,
/*! TXT Record. Data. Datatype: BINP */
ARES_RR_TXT_DATA = (ARES_REC_TYPE_TXT * 100) + 1,
@ -206,7 +216,7 @@ typedef enum {
ARES_RR_SRV_WEIGHT = (ARES_REC_TYPE_SRV * 100) + 3,
/*! SRV Record. Port. Datatype: U16 */
ARES_RR_SRV_PORT = (ARES_REC_TYPE_SRV * 100) + 4,
/*! SRV Record. Target domain. Datatype: STR */
/*! SRV Record. Target domain. Datatype: NAME */
ARES_RR_SRV_TARGET = (ARES_REC_TYPE_SRV * 100) + 5,
/*! NAPTR Record. Order. Datatype: U16 */
ARES_RR_NAPTR_ORDER = (ARES_REC_TYPE_NAPTR * 100) + 1,
@ -218,7 +228,7 @@ typedef enum {
ARES_RR_NAPTR_SERVICES = (ARES_REC_TYPE_NAPTR * 100) + 4,
/*! NAPTR Record. Regexp. Datatype: STR */
ARES_RR_NAPTR_REGEXP = (ARES_REC_TYPE_NAPTR * 100) + 5,
/*! NAPTR Record. Replacement. Datatype: STR */
/*! NAPTR Record. Replacement. Datatype: NAME */
ARES_RR_NAPTR_REPLACEMENT = (ARES_REC_TYPE_NAPTR * 100) + 6,
/*! OPT Record. UDP Size. Datatype: U16 */
ARES_RR_OPT_UDP_SIZE = (ARES_REC_TYPE_OPT * 100) + 1,
@ -240,13 +250,13 @@ typedef enum {
ARES_RR_TLSA_DATA = (ARES_REC_TYPE_TLSA * 100) + 4,
/*! SVCB Record. SvcPriority. Datatype: U16 */
ARES_RR_SVCB_PRIORITY = (ARES_REC_TYPE_SVCB * 100) + 1,
/*! SVCB Record. TargetName. Datatype: STR */
/*! SVCB Record. TargetName. Datatype: NAME */
ARES_RR_SVCB_TARGET = (ARES_REC_TYPE_SVCB * 100) + 2,
/*! SVCB Record. SvcParams. Datatype: OPT */
ARES_RR_SVCB_PARAMS = (ARES_REC_TYPE_SVCB * 100) + 3,
/*! HTTPS Record. SvcPriority. Datatype: U16 */
ARES_RR_HTTPS_PRIORITY = (ARES_REC_TYPE_HTTPS * 100) + 1,
/*! HTTPS Record. TargetName. Datatype: STR */
/*! HTTPS Record. TargetName. Datatype: NAME */
ARES_RR_HTTPS_TARGET = (ARES_REC_TYPE_HTTPS * 100) + 2,
/*! HTTPS Record. SvcParams. Datatype: OPT */
ARES_RR_HTTPS_PARAMS = (ARES_REC_TYPE_HTTPS * 100) + 3,
@ -254,7 +264,7 @@ typedef enum {
ARES_RR_URI_PRIORITY = (ARES_REC_TYPE_URI * 100) + 1,
/*! URI Record. Weight. Datatype: U16 */
ARES_RR_URI_WEIGHT = (ARES_REC_TYPE_URI * 100) + 2,
/*! URI Record. Target domain. Datatype: STR */
/*! URI Record. Target domain. Datatype: NAME */
ARES_RR_URI_TARGET = (ARES_REC_TYPE_URI * 100) + 3,
/*! CAA Record. Critical flag. Datatype: U8 */
ARES_RR_CAA_CRITICAL = (ARES_REC_TYPE_CAA * 100) + 1,
@ -298,7 +308,7 @@ typedef enum {
ARES_TLSA_MATCH_SHA512 = 2
} ares_tlsa_match_t;
/*! SVCB (and HTTPS) known parameters */
/*! SVCB (and HTTPS) RR known parameters */
typedef enum {
/*! Mandatory keys in this RR (RFC 9460 Section 8) */
ARES_SVCB_PARAM_MANDATORY = 0,
@ -316,6 +326,66 @@ typedef enum {
ARES_SVCB_PARAM_IPV6HINT = 6
} ares_svcb_param_t;
/*! OPT RR known parameters */
typedef enum {
/*! RFC 8764. Apple's DNS Long-Lived Queries Protocol */
ARES_OPT_PARAM_LLQ = 1,
/*! http://files.dns-sd.org/draft-sekar-dns-ul.txt: Update Lease */
ARES_OPT_PARAM_UL = 2,
/*! RFC 5001. Name Server Identification */
ARES_OPT_PARAM_NSID = 3,
/*! RFC 6975. DNSSEC Algorithm Understood */
ARES_OPT_PARAM_DAU = 5,
/*! RFC 6975. DS Hash Understood */
ARES_OPT_PARAM_DHU = 6,
/*! RFC 6975. NSEC3 Hash Understood */
ARES_OPT_PARAM_N3U = 7,
/*! RFC 7871. Client Subnet */
ARES_OPT_PARAM_EDNS_CLIENT_SUBNET = 8,
/*! RFC 7314. Expire Timer */
ARES_OPT_PARAM_EDNS_EXPIRE = 9,
/*! RFC 7873. Client and Server Cookies */
ARES_OPT_PARAM_COOKIE = 10,
/*! RFC 7828. TCP Keepalive timeout */
ARES_OPT_PARAM_EDNS_TCP_KEEPALIVE = 11,
/*! RFC 7830. Padding */
ARES_OPT_PARAM_PADDING = 12,
/*! RFC 7901. Chain query requests */
ARES_OPT_PARAM_CHAIN = 13,
/*! RFC 8145. Signaling Trust Anchor Knowledge in DNSSEC */
ARES_OPT_PARAM_EDNS_KEY_TAG = 14,
/*! RFC 8914. Extended ERROR code and message */
ARES_OPT_PARAM_EXTENDED_DNS_ERROR = 15,
} ares_opt_param_t;
/*! Data type for option records for keys like ARES_RR_OPT_OPTIONS and
* ARES_RR_HTTPS_PARAMS returned by ares_dns_opt_get_datatype() */
typedef enum {
/*! No value allowed for this option */
ARES_OPT_DATATYPE_NONE = 1,
/*! List of strings, each prefixed with a single octet representing the length
*/
ARES_OPT_DATATYPE_STR_LIST = 2,
/*! List of 8bit integers, concatenated */
ARES_OPT_DATATYPE_U8_LIST = 3,
/*! 16bit integer in network byte order */
ARES_OPT_DATATYPE_U16 = 4,
/*! list of 16bit integer in network byte order, concatenated. */
ARES_OPT_DATATYPE_U16_LIST = 5,
/*! 32bit integer in network byte order */
ARES_OPT_DATATYPE_U32 = 6,
/*! list 32bit integer in network byte order, concatenated */
ARES_OPT_DATATYPE_U32_LIST = 7,
/*! List of ipv4 addresses in network byte order, concatenated */
ARES_OPT_DATATYPE_INADDR4_LIST = 8,
/*! List of ipv6 addresses in network byte order, concatenated */
ARES_OPT_DATATYPE_INADDR6_LIST = 9,
/*! Binary Data */
ARES_OPT_DATATYPE_BIN = 10,
/*! DNS Domain Name Format */
ARES_OPT_DATATYPE_NAME = 11
} ares_dns_opt_datatype_t;
/*! String representation of DNS Record Type
*
* \param[in] type DNS Record Type
@ -344,6 +414,81 @@ CARES_EXTERN const char *ares_dns_opcode_tostr(ares_dns_opcode_t opcode);
*/
CARES_EXTERN const char *ares_dns_rr_key_tostr(ares_dns_rr_key_t key);
/*! String representation of DNS Resource Record section
*
* \param[in] section Section
* \return string
*/
CARES_EXTERN const char *ares_dns_section_tostr(ares_dns_section_t section);
/*! Convert DNS class name as string to ares_dns_class_t
*
* \param[out] qclass Pointer passed by reference to write class
* \param[in] str String to convert
* \return ARES_TRUE on success
*/
CARES_EXTERN ares_bool_t ares_dns_class_fromstr(ares_dns_class_t *qclass,
const char *str);
/*! Convert DNS record type as string to ares_dns_rec_type_t
*
* \param[out] qclass Pointer passed by reference to write record type
* \param[in] str String to convert
* \return ARES_TRUE on success
*/
CARES_EXTERN ares_bool_t ares_dns_rec_type_fromstr(ares_dns_rec_type_t *qtype,
const char *str);
/*! Convert DNS response code as string to from ares_dns_rcode_t
*
* \param[in] rcode Response code to convert
* \return ARES_TRUE on success
*/
CARES_EXTERN const char *ares_dns_rcode_tostr(ares_dns_rcode_t rcode);
/*! Convert any valid ip address (ipv4 or ipv6) into struct ares_addr and
* return the starting pointer of the network byte order address and the
* length of the address (4 or 16).
*
* \param[in] ipaddr ASCII string form of the ip address
* \param[in,out] addr Must set "family" member to one of AF_UNSPEC,
* AF_INET, AF_INET6 on input.
* \param[out] ptr_len Length of binary form address
* \return Pointer to start of binary address or NULL on error.
*/
CARES_EXTERN const void *ares_dns_pton(const char *ipaddr,
struct ares_addr *addr, size_t *out_len);
/*! Convert an ip address into the PTR format for in-addr.arpa or in6.arpa
*
* \param[in] addr properly filled address structure
* \return String representing PTR, use ares_free_string() to free
*/
CARES_EXTERN char *ares_dns_addr_to_ptr(const struct ares_addr *addr);
/*! The options/parameters extensions to some RRs can be somewhat opaque, this
* is a helper to return the best match for a datatype for interpreting the
* option record.
*
* \param[in] key Key associated with options/parameters
* \param[in] opt Option Key/Parameter
* \return Datatype
*/
CARES_EXTERN ares_dns_opt_datatype_t
ares_dns_opt_get_datatype(ares_dns_rr_key_t key, unsigned short opt);
/*! The options/parameters extensions to some RRs can be somewhat opaque, this
* is a helper to return the name if the option is known.
*
* \param[in] key Key associated with options/parameters
* \param[in] opt Option Key/Parameter
* \return name, or NULL if not known.
*/
CARES_EXTERN const char *ares_dns_opt_get_name(ares_dns_rr_key_t key,
unsigned short opt);
/*! Opaque data type representing a DNS RR (Resource Record) */
struct ares_dns_rr;
@ -369,7 +514,8 @@ typedef struct ares_dns_record ares_dns_record_t;
* \param[out] dnsrec Pointer passed by reference for a newly allocated
* record object. Must be ares_dns_record_destroy()'d by
* caller.
* \param[in] id DNS Query ID
* \param[in] id DNS Query ID. If structuring a new query to be sent
* with ares_send(), this value should be zero.
* \param[in] flags DNS Flags from \ares_dns_flags_t
* \param[in] opcode DNS OpCode (typically ARES_OPCODE_QUERY)
* \param[in] rcode DNS RCode
@ -583,7 +729,7 @@ CARES_EXTERN ares_status_t
const struct ares_in6_addr *addr);
/*! Set string data for specified resource record and key. Can
* only be used on keys with datatype ARES_DATATYPE_STR
* only be used on keys with datatype ARES_DATATYPE_STR or ARES_DATATYPE_NAME.
*
* \param[in] dns_rr Pointer to resource record
* \param[in] key DNS Resource Record Key
@ -680,7 +826,7 @@ CARES_EXTERN const struct ares_in6_addr *
ares_dns_rr_get_addr6(const ares_dns_rr_t *dns_rr, ares_dns_rr_key_t key);
/*! Retrieve a pointer to the string. Can only be used on keys with
* datatype ARES_DATATYPE_STR.
* datatype ARES_DATATYPE_STR and ARES_DATATYPE_NAME.
*
* \param[in] dns_rr Pointer to resource record
* \param[in] key DNS Resource Record Key
@ -748,7 +894,8 @@ CARES_EXTERN size_t ares_dns_rr_get_opt_cnt(const ares_dns_rr_t *dns_rr,
* \param[in] idx Index of option record
* \param[out] val Optional. Pointer passed by reference to hold value.
* Options may not have values. Value if returned is
* likely printable and guaranteed to be NULL terminated.
* guaranteed to be NULL terminated, however in most
* cases it is not printable.
* \param[out] val_len Optional. Pointer passed by reference to hold value
* length.
* \return option key/id on success, 65535 on misuse.
@ -764,7 +911,8 @@ CARES_EXTERN unsigned short
* \param[in] opt Option record key id (this is not the index).
* \param[out] val Optional. Pointer passed by reference to hold value.
* Options may not have values. Value if returned is
* likely printable and guaranteed to be NULL terminated.
* guaranteed to be NULL terminated, however in most cases
* it is not printable.
* \param[out] val_len Optional. Pointer passed by reference to hold value
* length.
* \return ARES_TRUE on success, ARES_FALSE on misuse.

@ -837,11 +837,40 @@ ares_status_t ares__buf_parse_dns_str(ares__buf_t *buf, size_t remaining_len,
&len, allow_multiple);
}
static ares_status_t ares__buf_append_num_hex(ares__buf_t *buf, size_t num,
size_t len)
ares_status_t ares__buf_append_num_dec(ares__buf_t *buf, size_t num, size_t len)
{
size_t i;
size_t mod;
if (len == 0) {
len = ares__count_digits(num);
}
mod = ares__pow(10, len);
for (i=len; i>0; i--) {
size_t digit = (num % mod);
ares_status_t status;
mod /= 10;
digit /= mod;
status = ares__buf_append_byte(buf, '0' + (unsigned char)(digit & 0xFF));
if (status != ARES_SUCCESS)
return status;
}
return ARES_SUCCESS;
}
ares_status_t ares__buf_append_num_hex(ares__buf_t *buf, size_t num, size_t len)
{
size_t i;
static const unsigned char hexbytes[] = "0123456789ABCDEF";
if (len == 0) {
len = ares__count_hexdigits(num);
}
for (i = len; i > 0; i--) {
ares_status_t status;
status = ares__buf_append_byte(buf, hexbytes[(num >> ((i - 1) * 4)) & 0xF]);

@ -106,6 +106,26 @@ ares_status_t ares__buf_append_be16(ares__buf_t *buf, unsigned short u16);
*/
ares_status_t ares__buf_append_be32(ares__buf_t *buf, unsigned int u32);
/*! Append a number in ASCII decimal form.
*
* \param[in] buf Initialized buffer object
* \param[in] num Number to print
* \param[in] len Length to output, use 0 for no padding
* \return ARES_SUCCESS on succeess
*/
ares_status_t ares__buf_append_num_dec(ares__buf_t *buf, size_t num,
size_t len);
/*! Append a number in ASCII hexidecimal form.
*
* \param[in] buf Initialized buffer object
* \param[in] num Number to print
* \param[in] len Length to output, use 0 for no padding
* \return ARES_SUCCESS on succeess
*/
ares_status_t ares__buf_append_num_hex(ares__buf_t *buf, size_t num,
size_t len);
/*! Sets the current buffer length. This *may* be used if there is a need to
* override a prior position in the buffer, such as if there is a length
* prefix that isn't easily predictable, and you must go back and overwrite

@ -179,8 +179,8 @@ static ares_bool_t ares__is_hostname(const char *str)
return ARES_TRUE;
}
static const void *ares__parse_ipaddr(const char *ipaddr,
struct ares_addr *addr, size_t *out_len)
const void *ares_dns_pton(const char *ipaddr, struct ares_addr *addr,
size_t *out_len)
{
const void *ptr = NULL;
size_t ptr_len = 0;
@ -225,7 +225,7 @@ static ares_bool_t ares__normalize_ipaddr(const char *ipaddr, char *out,
memset(&data, 0, sizeof(data));
data.family = AF_UNSPEC;
addr = ares__parse_ipaddr(ipaddr, &data, &addr_len);
addr = ares_dns_pton(ipaddr, &data, &addr_len);
if (addr == NULL) {
return ARES_FALSE;
}
@ -894,7 +894,7 @@ ares_status_t ares__hosts_entry_to_hostent(const ares_hosts_entry_t *entry,
memset(&addr, 0, sizeof(addr));
addr.family = family;
ptr = ares__parse_ipaddr(ipaddr, &addr, &ptr_len);
ptr = ares_dns_pton(ipaddr, &addr, &ptr_len);
if (ptr == NULL) {
continue;
}
@ -1071,7 +1071,7 @@ ares_status_t ares__hosts_entry_to_addrinfo(const ares_hosts_entry_t *entry,
memset(&addr, 0, sizeof(addr));
addr.family = family;
ptr = ares__parse_ipaddr(ipaddr, &addr, &ptr_len);
ptr = ares_dns_pton(ipaddr, &addr, &ptr_len);
if (ptr == NULL) {
continue;

@ -44,16 +44,25 @@ ares_bool_t ares_dns_rcode_isvalid(ares_dns_rcode_t rcode)
{
switch (rcode) {
case ARES_RCODE_NOERROR:
case ARES_RCODE_FORMAT_ERROR:
case ARES_RCODE_SERVER_FAILURE:
case ARES_RCODE_NAME_ERROR:
case ARES_RCODE_NOT_IMPLEMENTED:
case ARES_RCODE_FORMERR:
case ARES_RCODE_SERVFAIL:
case ARES_RCODE_NXDOMAIN:
case ARES_RCODE_NOTIMP:
case ARES_RCODE_REFUSED:
case ARES_RCODE_YXDOMAIN:
case ARES_RCODE_YXRRSET:
case ARES_RCODE_NXRRSET:
case ARES_RCODE_NOTAUTH:
case ARES_RCODE_NOTZONE:
case ARES_RCODE_DSOTYPEI:
case ARES_RCODE_BADSIG:
case ARES_RCODE_BADKEY:
case ARES_RCODE_BADTIME:
case ARES_RCODE_BADMODE:
case ARES_RCODE_BADNAME:
case ARES_RCODE_BADALG:
case ARES_RCODE_BADTRUNC:
case ARES_RCODE_BADCOOKIE:
return ARES_TRUE;
}
return ARES_FALSE;
@ -413,17 +422,19 @@ ares_dns_datatype_t ares_dns_rr_key_datatype(ares_dns_rr_key_t key)
case ARES_RR_SOA_MNAME:
case ARES_RR_SOA_RNAME:
case ARES_RR_PTR_DNAME:
case ARES_RR_HINFO_CPU:
case ARES_RR_HINFO_OS:
case ARES_RR_MX_EXCHANGE:
case ARES_RR_SRV_TARGET:
case ARES_RR_SVCB_TARGET:
case ARES_RR_HTTPS_TARGET:
case ARES_RR_NAPTR_REPLACEMENT:
case ARES_RR_URI_TARGET:
return ARES_DATATYPE_NAME;
case ARES_RR_HINFO_CPU:
case ARES_RR_HINFO_OS:
case ARES_RR_NAPTR_FLAGS:
case ARES_RR_NAPTR_SERVICES:
case ARES_RR_NAPTR_REGEXP:
case ARES_RR_NAPTR_REPLACEMENT:
case ARES_RR_URI_TARGET:
case ARES_RR_CAA_TAG:
return ARES_DATATYPE_STR;
@ -590,3 +601,289 @@ const ares_dns_rr_key_t *ares_dns_rr_get_keys(ares_dns_rec_type_t type,
return NULL;
}
ares_bool_t ares_dns_class_fromstr(ares_dns_class_t *qclass, const char *str)
{
size_t i;
static const struct {
const char *name;
ares_dns_class_t qclass;
} list[] = {
{"IN", ARES_CLASS_IN },
{ "CH", ARES_CLASS_CHAOS },
{ "HS", ARES_CLASS_HESOID},
{ "NONE", ARES_CLASS_NONE },
{ "ANY", ARES_CLASS_ANY },
{ NULL, 0 }
};
if (qclass == NULL || str == NULL) {
return ARES_FALSE;
}
for (i = 0; list[i].name != NULL; i++) {
if (strcasecmp(list[i].name, str) == 0) {
*qclass = list[i].qclass;
return ARES_TRUE;
}
}
return ARES_FALSE;
}
ares_bool_t ares_dns_rec_type_fromstr(ares_dns_rec_type_t *qtype,
const char *str)
{
size_t i;
static const struct {
const char *name;
ares_dns_rec_type_t type;
} list[] = {
{"A", ARES_REC_TYPE_A },
{ "NS", ARES_REC_TYPE_NS },
{ "CNAME", ARES_REC_TYPE_CNAME },
{ "SOA", ARES_REC_TYPE_SOA },
{ "PTR", ARES_REC_TYPE_PTR },
{ "HINFO", ARES_REC_TYPE_HINFO },
{ "MX", ARES_REC_TYPE_MX },
{ "TXT", ARES_REC_TYPE_TXT },
{ "AAAA", ARES_REC_TYPE_AAAA },
{ "SRV", ARES_REC_TYPE_SRV },
{ "NAPTR", ARES_REC_TYPE_NAPTR },
{ "OPT", ARES_REC_TYPE_OPT },
{ "TLSA", ARES_REC_TYPE_TLSA },
{ "SVCB", ARES_REC_TYPE_SVCB },
{ "HTTPS", ARES_REC_TYPE_HTTPS },
{ "ANY", ARES_REC_TYPE_ANY },
{ "URI", ARES_REC_TYPE_URI },
{ "CAA", ARES_REC_TYPE_CAA },
{ "RAW_RR", ARES_REC_TYPE_RAW_RR},
{ NULL, 0 }
};
if (qtype == NULL || str == NULL) {
return ARES_FALSE;
}
for (i = 0; list[i].name != NULL; i++) {
if (strcasecmp(list[i].name, str) == 0) {
*qtype = list[i].type;
return ARES_TRUE;
}
}
return ARES_FALSE;
}
const char *ares_dns_section_tostr(ares_dns_section_t section)
{
switch (section) {
case ARES_SECTION_ANSWER:
return "ANSWER";
case ARES_SECTION_AUTHORITY:
return "AUTHORITY";
case ARES_SECTION_ADDITIONAL:
return "ADDITIONAL";
}
return "UNKNOWN";
}
static ares_dns_opt_datatype_t ares_dns_opt_get_type_opt(unsigned short opt)
{
ares_opt_param_t param = (ares_opt_param_t)opt;
switch (param) {
case ARES_OPT_PARAM_LLQ:
/* Really it is u16 version, u16 opcode, u16 error, u64 id, u32 lease */
return ARES_OPT_DATATYPE_BIN;
case ARES_OPT_PARAM_UL:
return ARES_OPT_DATATYPE_U32;
case ARES_OPT_PARAM_NSID:
return ARES_OPT_DATATYPE_BIN;
case ARES_OPT_PARAM_DAU:
return ARES_OPT_DATATYPE_U8_LIST;
case ARES_OPT_PARAM_DHU:
return ARES_OPT_DATATYPE_U8_LIST;
case ARES_OPT_PARAM_N3U:
return ARES_OPT_DATATYPE_U8_LIST;
case ARES_OPT_PARAM_EDNS_CLIENT_SUBNET:
/* Really it is a u16 address family, u8 source prefix length,
* u8 scope prefix length, address */
return ARES_OPT_DATATYPE_BIN;
case ARES_OPT_PARAM_EDNS_EXPIRE:
return ARES_OPT_DATATYPE_U32;
case ARES_OPT_PARAM_COOKIE:
/* 8 bytes for client, 16-40 bytes for server */
return ARES_OPT_DATATYPE_BIN;
case ARES_OPT_PARAM_EDNS_TCP_KEEPALIVE:
/* Timeout in 100ms intervals */
return ARES_OPT_DATATYPE_U16;
case ARES_OPT_PARAM_PADDING:
/* Arbitrary padding */
return ARES_OPT_DATATYPE_BIN;
case ARES_OPT_PARAM_CHAIN:
return ARES_OPT_DATATYPE_NAME;
case ARES_OPT_PARAM_EDNS_KEY_TAG:
return ARES_OPT_DATATYPE_U16_LIST;
case ARES_OPT_PARAM_EXTENDED_DNS_ERROR:
/* Really 16bit code followed by textual message */
return ARES_OPT_DATATYPE_BIN;
}
return ARES_OPT_DATATYPE_BIN;
}
static ares_dns_opt_datatype_t ares_dns_opt_get_type_svcb(unsigned short opt)
{
ares_svcb_param_t param = (ares_svcb_param_t)opt;
switch (param) {
case ARES_SVCB_PARAM_NO_DEFAULT_ALPN:
return ARES_OPT_DATATYPE_NONE;
case ARES_SVCB_PARAM_ECH:
return ARES_OPT_DATATYPE_BIN;
case ARES_SVCB_PARAM_MANDATORY:
return ARES_OPT_DATATYPE_U16_LIST;
case ARES_SVCB_PARAM_ALPN:
return ARES_OPT_DATATYPE_STR_LIST;
case ARES_SVCB_PARAM_PORT:
return ARES_OPT_DATATYPE_U16;
case ARES_SVCB_PARAM_IPV4HINT:
return ARES_OPT_DATATYPE_INADDR4_LIST;
case ARES_SVCB_PARAM_IPV6HINT:
return ARES_OPT_DATATYPE_INADDR6_LIST;
}
return ARES_OPT_DATATYPE_BIN;
}
ares_dns_opt_datatype_t ares_dns_opt_get_datatype(ares_dns_rr_key_t key,
unsigned short opt)
{
switch (key) {
case ARES_RR_OPT_OPTIONS:
return ares_dns_opt_get_type_opt(opt);
case ARES_RR_SVCB_PARAMS:
case ARES_RR_HTTPS_PARAMS:
return ares_dns_opt_get_type_svcb(opt);
default:
break;
}
return ARES_OPT_DATATYPE_BIN;
}
static const char *ares_dns_opt_get_name_opt(unsigned short opt)
{
ares_opt_param_t param = (ares_opt_param_t)opt;
switch (param) {
case ARES_OPT_PARAM_LLQ:
return "LLQ";
case ARES_OPT_PARAM_UL:
return "UL";
case ARES_OPT_PARAM_NSID:
return "NSID";
case ARES_OPT_PARAM_DAU:
return "DAU";
case ARES_OPT_PARAM_DHU:
return "DHU";
case ARES_OPT_PARAM_N3U:
return "N3U";
case ARES_OPT_PARAM_EDNS_CLIENT_SUBNET:
return "edns-client-subnet";
case ARES_OPT_PARAM_EDNS_EXPIRE:
return "edns-expire";
case ARES_OPT_PARAM_COOKIE:
return "COOKIE";
case ARES_OPT_PARAM_EDNS_TCP_KEEPALIVE:
return "edns-tcp-keepalive";
case ARES_OPT_PARAM_PADDING:
return "Padding";
case ARES_OPT_PARAM_CHAIN:
return "CHAIN";
case ARES_OPT_PARAM_EDNS_KEY_TAG:
return "edns-key-tag";
case ARES_OPT_PARAM_EXTENDED_DNS_ERROR:
return "extended-dns-error";
}
return NULL;
}
static const char *ares_dns_opt_get_name_svcb(unsigned short opt)
{
ares_svcb_param_t param = (ares_svcb_param_t)opt;
switch (param) {
case ARES_SVCB_PARAM_NO_DEFAULT_ALPN:
return "no-default-alpn";
case ARES_SVCB_PARAM_ECH:
return "ech";
case ARES_SVCB_PARAM_MANDATORY:
return "mandatory";
case ARES_SVCB_PARAM_ALPN:
return "alpn";
case ARES_SVCB_PARAM_PORT:
return "port";
case ARES_SVCB_PARAM_IPV4HINT:
return "ipv4hint";
case ARES_SVCB_PARAM_IPV6HINT:
return "ipv6hint";
}
return NULL;
}
const char *ares_dns_opt_get_name(ares_dns_rr_key_t key, unsigned short opt)
{
switch (key) {
case ARES_RR_OPT_OPTIONS:
return ares_dns_opt_get_name_opt(opt);
case ARES_RR_SVCB_PARAMS:
case ARES_RR_HTTPS_PARAMS:
return ares_dns_opt_get_name_svcb(opt);
default:
break;
}
return NULL;
}
const char *ares_dns_rcode_tostr(ares_dns_rcode_t rcode)
{
switch (rcode) {
case ARES_RCODE_NOERROR:
return "NOERROR";
case ARES_RCODE_FORMERR:
return "FORMERR";
case ARES_RCODE_SERVFAIL:
return "SERVFAIL";
case ARES_RCODE_NXDOMAIN:
return "NXDOMAIN";
case ARES_RCODE_NOTIMP:
return "NOTIMP";
case ARES_RCODE_REFUSED:
return "REFUSED";
case ARES_RCODE_YXDOMAIN:
return "YXDOMAIN";
case ARES_RCODE_YXRRSET:
return "YXRRSET";
case ARES_RCODE_NXRRSET:
return "NXRRSET";
case ARES_RCODE_NOTAUTH:
return "NOTAUTH";
case ARES_RCODE_NOTZONE:
return "NOTZONE";
case ARES_RCODE_DSOTYPEI:
return "DSOTYPEI";
case ARES_RCODE_BADSIG:
return "BADSIG";
case ARES_RCODE_BADKEY:
return "BADKEY";
case ARES_RCODE_BADTIME:
return "BADTIME";
case ARES_RCODE_BADMODE:
return "BADMODE";
case ARES_RCODE_BADNAME:
return "BADNAME";
case ARES_RCODE_BADALG:
return "BADALG";
case ARES_RCODE_BADTRUNC:
return "BADTRUNC";
case ARES_RCODE_BADCOOKIE:
return "BADCOOKIE";
}
return "UNKNOWN";
}

@ -834,7 +834,8 @@ const char *ares_dns_rr_get_str(const ares_dns_rr_t *dns_rr,
{
char * const *str;
if (ares_dns_rr_key_datatype(key) != ARES_DATATYPE_STR) {
if (ares_dns_rr_key_datatype(key) != ARES_DATATYPE_STR &&
ares_dns_rr_key_datatype(key) != ARES_DATATYPE_NAME) {
return NULL;
}
@ -1090,7 +1091,8 @@ ares_status_t ares_dns_rr_set_str_own(ares_dns_rr_t *dns_rr,
{
char **str;
if (ares_dns_rr_key_datatype(key) != ARES_DATATYPE_STR) {
if (ares_dns_rr_key_datatype(key) != ARES_DATATYPE_STR &&
ares_dns_rr_key_datatype(key) != ARES_DATATYPE_NAME) {
return ARES_EFORMERR;
}
@ -1190,6 +1192,7 @@ ares_status_t ares_dns_rr_set_opt_own(ares_dns_rr_t *dns_rr,
done:
ares_free((*options)->optval[idx].val);
(*options)->optval[idx].opt = opt;
(*options)->optval[idx].val = val;
(*options)->optval[idx].val_len = val_len;
@ -1219,3 +1222,68 @@ ares_status_t ares_dns_rr_set_opt(ares_dns_rr_t *dns_rr, ares_dns_rr_key_t key,
return status;
}
char *ares_dns_addr_to_ptr(const struct ares_addr *addr)
{
ares__buf_t *buf = NULL;
const unsigned char *ptr = NULL;
size_t ptr_len = 0;
size_t i;
ares_status_t status;
static const unsigned char hexbytes[] = "0123456789abcdef";
if (addr->family != AF_INET && addr->family != AF_INET6)
goto fail;
buf = ares__buf_create();
if (buf == NULL)
goto fail;
if (addr->family == AF_INET) {
ptr = (const unsigned char *)&addr->addr.addr4;
ptr_len = 4;
} else {
ptr = (const unsigned char *)&addr->addr.addr6;
ptr_len = 16;
}
for (i=ptr_len; i>0; i--) {
if (addr->family == AF_INET) {
status = ares__buf_append_num_dec(buf, (size_t)ptr[i-1], 0);
} else {
unsigned char c;
c = ptr[i-1] & 0xF;
status = ares__buf_append_byte(buf, hexbytes[c]);
if (status != ARES_SUCCESS)
goto fail;
status = ares__buf_append_byte(buf, '.');
if (status != ARES_SUCCESS)
goto fail;
c = (ptr[i-1] >> 4) & 0xF;
status = ares__buf_append_byte(buf, hexbytes[c]);
}
if (status != ARES_SUCCESS)
goto fail;
status = ares__buf_append_byte(buf, '.');
if (status != ARES_SUCCESS)
goto fail;
}
if (addr->family == AF_INET) {
status = ares__buf_append(buf, (const unsigned char *)"in-addr.arpa", 12);
} else {
status = ares__buf_append(buf, (const unsigned char *)"ip6.arpa", 8);
}
if (status != ARES_SUCCESS)
goto fail;
return ares__buf_finish_str(buf, NULL);
fail:
ares__buf_destroy(buf);
return NULL;
}

@ -67,8 +67,6 @@ static void end_aquery(struct addr_query *aquery, ares_status_t status,
static ares_status_t file_lookup(ares_channel_t *channel,
const struct ares_addr *addr,
struct hostent **host);
static void ptr_rr_name(char *name, size_t name_size,
const struct ares_addr *addr);
void ares_gethostbyaddr(ares_channel_t *channel, const void *addr, int addrlen,
int family, ares_host_callback callback, void *arg)
@ -106,19 +104,25 @@ void ares_gethostbyaddr(ares_channel_t *channel, const void *addr, int addrlen,
next_lookup(aquery);
}
static void next_lookup(struct addr_query *aquery)
{
const char *p;
char name[128];
ares_status_t status;
struct hostent *host;
char *name;
for (p = aquery->remaining_lookups; *p; p++) {
switch (*p) {
case 'b':
ptr_rr_name(name, sizeof(name), &aquery->addr);
name = ares_dns_addr_to_ptr(&aquery->addr);
if (name == NULL) {
end_aquery(aquery, ARES_ENOMEM, NULL);
return;
}
aquery->remaining_lookups = p + 1;
ares_query(aquery->channel, name, C_IN, T_PTR, addr_callback, aquery);
ares_free(name);
return;
case 'f':
status = file_lookup(aquery->channel, &aquery->addr, &host);
@ -210,31 +214,3 @@ static ares_status_t file_lookup(ares_channel_t *channel,
return ARES_SUCCESS;
}
static void ptr_rr_name(char *name, size_t name_size,
const struct ares_addr *addr)
{
if (addr->family == AF_INET) {
unsigned long laddr = ntohl(addr->addr.addr4.s_addr);
unsigned long a1 = (laddr >> 24UL) & 0xFFUL;
unsigned long a2 = (laddr >> 16UL) & 0xFFUL;
unsigned long a3 = (laddr >> 8UL) & 0xFFUL;
unsigned long a4 = laddr & 0xFFUL;
snprintf(name, name_size, "%lu.%lu.%lu.%lu.in-addr.arpa", a4, a3, a2, a1);
} else {
const unsigned char *bytes = (const unsigned char *)&addr->addr.addr6;
/* There are too many arguments to do this in one line using
* minimally C89-compliant compilers */
snprintf(name, name_size,
"%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.",
bytes[15] & 0xf, bytes[15] >> 4, bytes[14] & 0xf, bytes[14] >> 4,
bytes[13] & 0xf, bytes[13] >> 4, bytes[12] & 0xf, bytes[12] >> 4,
bytes[11] & 0xf, bytes[11] >> 4, bytes[10] & 0xf, bytes[10] >> 4,
bytes[9] & 0xf, bytes[9] >> 4, bytes[8] & 0xf, bytes[8] >> 4);
snprintf(name + ares_strlen(name), name_size - ares_strlen(name),
"%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.arpa",
bytes[7] & 0xf, bytes[7] >> 4, bytes[6] & 0xf, bytes[6] >> 4,
bytes[5] & 0xf, bytes[5] >> 4, bytes[4] & 0xf, bytes[4] >> 4,
bytes[3] & 0xf, bytes[3] >> 4, bytes[2] & 0xf, bytes[2] >> 4,
bytes[1] & 0xf, bytes[1] >> 4, bytes[0] & 0xf, bytes[0] >> 4);
}
}

@ -65,3 +65,48 @@ size_t ares__log2(size_t n)
return tab64[(n * 0x07EDD5E59A4E28C2) >> 58];
}
/* x^y */
size_t ares__pow(size_t x, size_t y)
{
size_t res = 1;
while (y > 0) {
/* If y is odd, multiply x with result */
if (y & 1) {
res = res * x;
}
/* y must be even now */
y = y >> 1; /* y /= 2; */
x = x * x; /* x^2 */
}
return res;
}
size_t ares__count_digits(size_t n)
{
size_t digits;
for (digits = 0; n > 0; digits++) {
n /= 10;
}
if (digits == 0)
digits = 1;
return digits;
}
size_t ares__count_hexdigits(size_t n)
{
size_t digits;
for (digits = 0; n > 0; digits++) {
n /= 16;
}
if (digits == 0)
digits = 1;
return digits;
}

@ -151,14 +151,6 @@ typedef struct ares_rand_state ares_rand_state;
/********* EDNS defines section ******/
struct ares_addr {
int family;
union {
struct in_addr addr4;
struct ares_in6_addr addr6;
} addr;
};
struct query;
@ -353,7 +345,8 @@ ares_status_t ares_query_qid(ares_channel_t *channel, const char *name,
/* Identical to ares_send() except returns normal ares return codes like
* ARES_SUCCESS */
ares_status_t ares_send_ex(ares_channel_t *channel, const unsigned char *qbuf,
size_t qlen, ares_callback callback, void *arg);
size_t qlen, ares_callback callback, void *arg,
unsigned short *qid);
void ares__close_connection(struct server_connection *conn);
void ares__close_sockets(struct server_state *server);
void ares__check_cleanup_conn(const ares_channel_t *channel,
@ -558,5 +551,9 @@ ares_status_t ares__dns_name_write(ares__buf_t *buf, ares__llist_t **list,
size_t ares__round_up_pow2(size_t n);
size_t ares__log2(size_t n);
size_t ares__pow(size_t x, size_t y);
size_t ares__count_digits(size_t n);
size_t ares__count_hexdigits(size_t n);
#endif /* __ARES_PRIVATE_H */

@ -656,7 +656,7 @@ static ares_status_t process_answer(ares_channel_t *channel,
/* If we use EDNS and server answers with FORMERR without an OPT RR, the
* protocol extension is not understood by the responder. We must retry the
* query without EDNS enabled. */
if (ares_dns_record_get_rcode(rdnsrec) == ARES_RCODE_FORMAT_ERROR &&
if (ares_dns_record_get_rcode(rdnsrec) == ARES_RCODE_FORMERR &&
has_opt_rr(qdnsrec) && !has_opt_rr(rdnsrec)) {
status = rewrite_without_edns(qdnsrec, query);
if (status != ARES_SUCCESS) {
@ -686,13 +686,13 @@ static ares_status_t process_answer(ares_channel_t *channel,
*/
if (!(channel->flags & ARES_FLAG_NOCHECKRESP)) {
ares_dns_rcode_t rcode = ares_dns_record_get_rcode(rdnsrec);
if (rcode == ARES_RCODE_SERVER_FAILURE ||
rcode == ARES_RCODE_NOT_IMPLEMENTED || rcode == ARES_RCODE_REFUSED) {
if (rcode == ARES_RCODE_SERVFAIL ||
rcode == ARES_RCODE_NOTIMP || rcode == ARES_RCODE_REFUSED) {
switch (rcode) {
case ARES_RCODE_SERVER_FAILURE:
case ARES_RCODE_SERVFAIL:
query->error_status = ARES_ESERVFAIL;
break;
case ARES_RCODE_NOT_IMPLEMENTED:
case ARES_RCODE_NOTIMP:
query->error_status = ARES_ENOTIMP;
break;
case ARES_RCODE_REFUSED:

@ -45,21 +45,6 @@ struct qquery {
static void qcallback(void *arg, int status, int timeouts, unsigned char *abuf,
int alen);
/* a unique query id is generated using an rc4 key. Since the id may already
be used by a running query (as infrequent as it may be), a lookup is
performed per id generation. In practice this search should happen only
once per newly generated id
*/
static unsigned short generate_unique_id(ares_channel_t *channel)
{
unsigned short id;
do {
id = ares__generate_new_id(channel->rand_state);
} while (ares__htable_szvp_get(channel->queries_by_qid, id, NULL));
return id;
}
ares_status_t ares_query_qid(ares_channel_t *channel, const char *name,
int dnsclass, int type, ares_callback callback,
@ -70,12 +55,11 @@ ares_status_t ares_query_qid(ares_channel_t *channel, const char *name,
int qlen;
int rd;
ares_status_t status;
unsigned short id = generate_unique_id(channel);
/* Compose the query. */
rd = !(channel->flags & ARES_FLAG_NORECURSE);
status = (ares_status_t)ares_create_query(
name, dnsclass, type, id, rd, &qbuf, &qlen,
name, dnsclass, type, 0, rd, &qbuf, &qlen,
(channel->flags & ARES_FLAG_EDNS) ? (int)channel->ednspsz : 0);
if (status != ARES_SUCCESS) {
if (qbuf != NULL) {
@ -96,13 +80,9 @@ ares_status_t ares_query_qid(ares_channel_t *channel, const char *name,
qquery->arg = arg;
/* Send it off. qcallback will be called when we get an answer. */
status = ares_send_ex(channel, qbuf, (size_t)qlen, qcallback, qquery);
status = ares_send_ex(channel, qbuf, (size_t)qlen, qcallback, qquery, qid);
ares_free_string(qbuf);
if (status == ARES_SUCCESS && qid) {
*qid = id;
}
return status;
}

@ -37,12 +37,27 @@
#include "ares_dns.h"
#include "ares_private.h"
static unsigned short generate_unique_qid(ares_channel_t *channel)
{
unsigned short id;
do {
id = ares__generate_new_id(channel->rand_state);
} while (ares__htable_szvp_get(channel->queries_by_qid, id, NULL));
return id;
}
ares_status_t ares_send_ex(ares_channel_t *channel, const unsigned char *qbuf,
size_t qlen, ares_callback callback, void *arg)
size_t qlen, ares_callback callback, void *arg,
unsigned short *qid)
{
struct query *query;
size_t packetsz;
struct timeval now;
ares_status_t status;
unsigned short id = generate_unique_qid(channel);
/* Verify that the query is at least long enough to hold the header. */
if (qlen < HFIXEDSZ || qlen >= (1 << 16)) {
@ -69,12 +84,14 @@ ares_status_t ares_send_ex(ares_channel_t *channel, const unsigned char *qbuf,
return ARES_ENOMEM;
}
/* Compute the query ID. Start with no timeout. */
query->qid = DNS_HEADER_QID(qbuf);
query->qid = id;
query->timeout.tv_sec = 0;
query->timeout.tv_usec = 0;
memcpy(query->qbuf, qbuf, qlen);
/* Ignore first 2 bytes, assign our own query id */
query->qbuf[0] = (unsigned char)((id >> 8) & 0xFF);
query->qbuf[1] = (unsigned char)(id & 0xFF);
memcpy(query->qbuf + 2, qbuf + 2, qlen - 2);
query->qlen = qlen;
/* Fill in query arguments. */
@ -115,11 +132,15 @@ ares_status_t ares_send_ex(ares_channel_t *channel, const unsigned char *qbuf,
/* Perform the first query action. */
now = ares__tvnow();
return ares__send_query(query, &now);
status = ares__send_query(query, &now);
if (status == ARES_SUCCESS && qid) {
*qid = id;
}
return status;
}
void ares_send(ares_channel_t *channel, const unsigned char *qbuf, int qlen,
ares_callback callback, void *arg)
{
ares_send_ex(channel, qbuf, (size_t)qlen, callback, arg);
ares_send_ex(channel, qbuf, (size_t)qlen, callback, arg, NULL);
}

File diff suppressed because it is too large Load Diff

@ -76,6 +76,7 @@ int main(int argc, char **argv)
struct timeval tv;
struct in_addr addr4;
struct ares_in6_addr addr6;
ares_getopt_state_t state;
#ifdef USE_WINSOCK
WORD wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK);
@ -91,7 +92,8 @@ int main(int argc, char **argv)
return 1;
}
while ((c = ares_getopt(argc, argv, "dt:h?s:")) != -1) {
ares_getopt_init(&state, argc, (const char **)argv);
while ((c = ares_getopt(&state, "dt:h?s:")) != -1) {
switch (c) {
case 'd':
#ifdef WATT32
@ -103,14 +105,14 @@ int main(int argc, char **argv)
options.ndomains++;
options.domains = (char **)realloc(
options.domains, (size_t)options.ndomains * sizeof(char *));
options.domains[options.ndomains - 1] = strdup(optarg);
options.domains[options.ndomains - 1] = strdup(state.optarg);
break;
case 't':
if (!strcasecmp(optarg, "a")) {
if (!strcasecmp(state.optarg, "a")) {
addr_family = AF_INET;
} else if (!strcasecmp(optarg, "aaaa")) {
} else if (!strcasecmp(state.optarg, "aaaa")) {
addr_family = AF_INET6;
} else if (!strcasecmp(optarg, "u")) {
} else if (!strcasecmp(state.optarg, "u")) {
addr_family = AF_UNSPEC;
} else {
usage();
@ -128,8 +130,8 @@ int main(int argc, char **argv)
}
}
argc -= optind;
argv += optind;
argc -= state.optind;
argv += state.optind;
if (argc < 1) {
usage();
}

@ -50,77 +50,79 @@
#include <string.h>
#include "ares_getopt.h"
int opterr = 1; /* if error message should be printed */
int optind = 1; /* index into parent argv vector */
int optopt = 0; /* character checked for validity */
static int optreset; /* reset getopt */
char *optarg; /* argument associated with option */
#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG (char *)""
void ares_getopt_init(ares_getopt_state_t *state, int nargc, const char **nargv)
{
memset(state, 0, sizeof(*state));
state->opterr = 1;
state->optind = 1;
state->place = EMSG;
state->argc = nargc;
state->argv = nargv;
}
/*
* ares_getopt --
* Parse argc/argv argument vector.
*/
int ares_getopt(int nargc, char * const nargv[], const char *ostr)
int ares_getopt(ares_getopt_state_t *state, const char *ostr)
{
static char *place = EMSG; /* option letter processing */
char *oli; /* option letter list index */
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc || *(place = nargv[optind]) != '-') {
place = EMSG;
if (!*state->place) { /* update scanning pointer */
if (state->optind >= state->argc || *(state->place = state->argv[state->optind]) != '-') {
state->place = EMSG;
return (EOF);
}
if (place[1] && *++place == '-') { /* found "--" */
++optind;
place = EMSG;
if (state->place[1] && *++state->place == '-') { /* found "--" */
++state->optind;
state->place = EMSG;
return (EOF);
}
} /* option letter okay? */
if ((optopt = (int)*place++) == (int)':' ||
(oli = strchr(ostr, optopt)) == NULL) {
if ((state->optopt = (int)*state->place++) == (int)':' ||
(oli = strchr(ostr, state->optopt)) == NULL) {
/*
* if the user didn't specify '-' as an option,
* assume it means EOF.
*/
if (optopt == (int)'-') {
if (state->optopt == (int)'-') {
return (EOF);
}
if (!*place) {
++optind;
if (!*state->place) {
++state->optind;
}
if (opterr && *ostr != ':') {
(void)fprintf(stderr, "%s: illegal option -- %c\n", __FILE__, optopt);
if (state->opterr && *ostr != ':') {
(void)fprintf(stderr, "%s: illegal option -- %c\n", __FILE__, state->optopt);
}
return (BADCH);
}
if (*++oli != ':') { /* don't need argument */
optarg = NULL;
if (!*place) {
++optind;
state->optarg = NULL;
if (!*state->place) {
++state->optind;
}
} else { /* need an argument */
if (*place) { /* no white space */
optarg = place;
} else if (nargc <= ++optind) { /* no arg */
place = EMSG;
if (*state->place) { /* no white space */
state->optarg = state->place;
} else if (state->argc <= ++state->optind) { /* no arg */
state->place = EMSG;
if (*ostr == ':') {
return (BADARG);
}
if (opterr) {
if (state->opterr) {
(void)fprintf(stderr, "%s: option requires an argument -- %c\n",
__FILE__, optopt);
__FILE__, state->optopt);
}
return (BADCH);
} else { /* white space */
optarg = nargv[optind];
state->optarg = state->argv[state->optind];
}
place = EMSG;
++optind;
state->place = EMSG;
++state->optind;
}
return (optopt); /* dump back option letter */
return (state->optopt); /* dump back option letter */
}

@ -33,33 +33,17 @@
*/
int ares_getopt(int nargc, char * const nargv[], const char *ostr);
typedef struct {
const char *optarg; /* argument associated with option */
int optind; /* index into parent argv vector */
int opterr; /* if error message should be printed */
int optopt; /* character checked for validity */
const char *place;
int argc;
const char **argv;
} ares_getopt_state_t;
#ifdef optarg
# undef optarg
#endif
#ifdef optind
# undef optind
#endif
#ifdef opterr
# undef opterr
#endif
#ifdef optopt
# undef optopt
#endif
#ifdef optreset
# undef optreset
#endif
#define optarg ares_optarg
#define optind ares_optind
#define opterr ares_opterr
#define optopt ares_optopt
#define optreset ares_optreset
extern char *optarg;
extern int optind;
extern int opterr;
extern int optopt;
void ares_getopt_init(ares_getopt_state_t *state, int argc, const char **argv);
int ares_getopt(ares_getopt_state_t *state, const char *ostr);
#endif /* ARES_GETOPT_H */

@ -621,14 +621,19 @@ TEST_F(LibraryTest, DNSRecord) {
ares_dns_rr_set_u16(rr, ARES_RR_SVCB_PRIORITY, 1));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_SVCB_TARGET, "svc1.example.net"));
const unsigned char svcb_ipv6hint[] = "2001:db8::1";
/* IPV6 hint is a list of IPV6 addresses in network byte order, concatenated */
struct ares_addr svcb_addr;
svcb_addr.family = AF_UNSPEC;
size_t svcb_ipv6hint_len = 0;
const unsigned char *svcb_ipv6hint = (const unsigned char *)ares_dns_pton("2001:db8::1", &svcb_addr, &svcb_ipv6hint_len);
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_opt(rr, ARES_RR_SVCB_PARAMS, ARES_SVCB_PARAM_IPV6HINT,
svcb_ipv6hint, sizeof(svcb_ipv6hint)-1));
const unsigned char svcb_port[] = "1234";
svcb_ipv6hint, svcb_ipv6hint_len));
/* Port is 16bit big endian format */
unsigned short svcb_port = htons(1234);
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_opt(rr, ARES_RR_SVCB_PARAMS, ARES_SVCB_PARAM_IPV6HINT,
svcb_port, sizeof(svcb_port)-1));
ares_dns_rr_set_opt(rr, ARES_RR_SVCB_PARAMS, ARES_SVCB_PARAM_PORT,
(const unsigned char *)&svcb_port, sizeof(svcb_port)));
/* HTTPS */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL,
@ -637,10 +642,12 @@ TEST_F(LibraryTest, DNSRecord) {
ares_dns_rr_set_u16(rr, ARES_RR_HTTPS_PRIORITY, 1));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_HTTPS_TARGET, ""));
const unsigned char https_alpn[] = "h3";
/* In DNS string format which is 1 octet length indicator followed by string */
const unsigned char https_alpn[] = { 0x02, 'h', '3' };
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_opt(rr, ARES_RR_HTTPS_PARAMS, ARES_SVCB_PARAM_ALPN,
https_alpn, sizeof(https_alpn)-1));
https_alpn, sizeof(https_alpn)));
/* URI */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL,

Loading…
Cancel
Save