diff --git a/docs/adig.1 b/docs/adig.1 index 6760bbef..38947166 100644 --- a/docs/adig.1 +++ b/docs/adig.1 @@ -44,12 +44,13 @@ 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, - stayopen - don't close the communication sockets, and - usevc - always use TCP. + primary - use the first server, + stayopen - don't close the communication sockets, and + usevc - always use TCP. .TP \fB\-h\fR, \fB\-?\fR Display this help and exit. @@ -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: diff --git a/include/ares.h b/include/ares.h index ee8e87e2..19b8faf5 100644 --- a/include/ares.h +++ b/include/ares.h @@ -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; diff --git a/include/ares_dns_record.h b/include/ares_dns_record.h index e840bf8f..7a74ef17 100644 --- a/include/ares_dns_record.h +++ b/include/ares_dns_record.h @@ -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. diff --git a/src/lib/ares__buf.c b/src/lib/ares__buf.c index 0af1d668..1e928258 100644 --- a/src/lib/ares__buf.c +++ b/src/lib/ares__buf.c @@ -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]); diff --git a/src/lib/ares__buf.h b/src/lib/ares__buf.h index ec77f9d4..9acb3213 100644 --- a/src/lib/ares__buf.h +++ b/src/lib/ares__buf.h @@ -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 diff --git a/src/lib/ares__hosts_file.c b/src/lib/ares__hosts_file.c index 83705232..20ae0a98 100644 --- a/src/lib/ares__hosts_file.c +++ b/src/lib/ares__hosts_file.c @@ -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; diff --git a/src/lib/ares_dns_mapping.c b/src/lib/ares_dns_mapping.c index 9726dbbc..ab8f52ef 100644 --- a/src/lib/ares_dns_mapping.c +++ b/src/lib/ares_dns_mapping.c @@ -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"; +} diff --git a/src/lib/ares_dns_record.c b/src/lib/ares_dns_record.c index af726774..e847e1f9 100644 --- a/src/lib/ares_dns_record.c +++ b/src/lib/ares_dns_record.c @@ -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; +} diff --git a/src/lib/ares_gethostbyaddr.c b/src/lib/ares_gethostbyaddr.c index 5df2aeff..c678d660 100644 --- a/src/lib/ares_gethostbyaddr.c +++ b/src/lib/ares_gethostbyaddr.c @@ -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); - } -} diff --git a/src/lib/ares_math.c b/src/lib/ares_math.c index 3c067e5d..41225012 100644 --- a/src/lib/ares_math.c +++ b/src/lib/ares_math.c @@ -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; +} diff --git a/src/lib/ares_private.h b/src/lib/ares_private.h index 0bbb68e5..9121e070 100644 --- a/src/lib/ares_private.h +++ b/src/lib/ares_private.h @@ -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 */ diff --git a/src/lib/ares_process.c b/src/lib/ares_process.c index 3f350450..a9e12a4e 100644 --- a/src/lib/ares_process.c +++ b/src/lib/ares_process.c @@ -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: diff --git a/src/lib/ares_query.c b/src/lib/ares_query.c index 88f3c7b8..bb64d9a9 100644 --- a/src/lib/ares_query.c +++ b/src/lib/ares_query.c @@ -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; } diff --git a/src/lib/ares_send.c b/src/lib/ares_send.c index aff65eac..eefdfe46 100644 --- a/src/lib/ares_send.c +++ b/src/lib/ares_send.c @@ -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); } diff --git a/src/tools/adig.c b/src/tools/adig.c index 2c64c986..e7be1b1c 100644 --- a/src/tools/adig.c +++ b/src/tools/adig.c @@ -24,7 +24,6 @@ * * SPDX-License-Identifier: MIT */ - #include "ares_setup.h" #ifdef HAVE_NETINET_IN_H @@ -45,7 +44,6 @@ #include "ares.h" #include "ares_dns.h" -#include "ares_getopt.h" #ifndef HAVE_STRDUP # include "ares_strdup.h" @@ -62,17 +60,30 @@ # define strncasecmp(p1, p2, n) ares_strncasecmp(p1, p2, n) #endif +#include "ares_getopt.h" + #ifdef WATT32 # undef WIN32 /* Redefined in MingW headers */ #endif -struct nv { +typedef struct { + ares_bool_t is_help; + struct ares_options options; + int optmask; + ares_dns_class_t qclass; + ares_dns_rec_type_t qtype; + int args_processed; + char *servers; + char error[256]; +} adig_config_t; + +typedef struct { const char *name; int value; -}; +} nv_t; -static const struct nv flags[] = { +static const nv_t configflags[] = { {"usevc", ARES_FLAG_USEVC }, { "primary", ARES_FLAG_PRIMARY }, { "igntc", ARES_FLAG_IGNTC }, @@ -80,995 +91,843 @@ static const struct nv flags[] = { { "stayopen", ARES_FLAG_STAYOPEN }, { "noaliases", ARES_FLAG_NOALIASES} }; -static const int nflags = sizeof(flags) / sizeof(flags[0]); - -static const struct nv classes[] = { - {"IN", C_IN }, - { "CHAOS", C_CHAOS}, - { "HS", C_HS }, - { "ANY", C_ANY } -}; -static const int nclasses = sizeof(classes) / sizeof(classes[0]); - -static const struct nv types[] = { - {"A", T_A }, - { "NS", T_NS }, - { "MD", T_MD }, - { "MF", T_MF }, - { "CNAME", T_CNAME }, - { "SOA", T_SOA }, - { "MB", T_MB }, - { "MG", T_MG }, - { "MR", T_MR }, - { "NULL", T_NULL }, - { "WKS", T_WKS }, - { "PTR", T_PTR }, - { "HINFO", T_HINFO }, - { "MINFO", T_MINFO }, - { "MX", T_MX }, - { "TXT", T_TXT }, - { "RP", T_RP }, - { "AFSDB", T_AFSDB }, - { "X25", T_X25 }, - { "ISDN", T_ISDN }, - { "RT", T_RT }, - { "NSAP", T_NSAP }, - { "NSAP_PTR", T_NSAP_PTR}, - { "SIG", T_SIG }, - { "KEY", T_KEY }, - { "PX", T_PX }, - { "GPOS", T_GPOS }, - { "AAAA", T_AAAA }, - { "LOC", T_LOC }, - { "SRV", T_SRV }, - { "AXFR", T_AXFR }, - { "MAILB", T_MAILB }, - { "MAILA", T_MAILA }, - { "NAPTR", T_NAPTR }, - { "DS", T_DS }, - { "SSHFP", T_SSHFP }, - { "RRSIG", T_RRSIG }, - { "NSEC", T_NSEC }, - { "DNSKEY", T_DNSKEY }, - { "CAA", T_CAA }, - { "URI", T_URI }, - { "ANY", T_ANY } -}; -static const int ntypes = sizeof(types) / sizeof(types[0]); - -static const char *opcodes[] = { - "QUERY", "IQUERY", "STATUS", "(reserved)", "NOTIFY", "(unknown)", - "(unknown)", "(unknown)", "(unknown)", "UPDATEA", "UPDATED", "UPDATEDA", - "UPDATEM", "UPDATEMA", "ZONEINIT", "ZONEREF" -}; +static const size_t nconfigflags = sizeof(configflags) / sizeof(*configflags); -static const char *rcodes[] = { - "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP", "REFUSED", - "(unknown)", "(unknown)", "(unknown)", "(unknown)", "(unknown)", "(unknown)", - "(unknown)", "(unknown)", "(unknown)", "NOCHANGE" -}; - -static void callback(void *arg, int status, int timeouts, unsigned char *abuf, - int alen); -static const unsigned char *display_question(const unsigned char *aptr, - const unsigned char *abuf, - int alen); -static const unsigned char *display_rr(const unsigned char *aptr, - const unsigned char *abuf, int alen); -static int convert_query(char **name, int use_bitstring); -static const char *type_name(int type); -static const char *class_name(int dnsclass); -static void usage(void); -static void destroy_addr_list(struct ares_addr_node *head); -static void append_addr_list(struct ares_addr_node **head, - struct ares_addr_node *node); -static void print_help_info_adig(void); - -static size_t ares_strcpy(char *dest, const char *src, size_t dest_size) +static int lookup_flag(const nv_t *nv, size_t num_nv, const char *name) { - size_t len = 0; + size_t i; - if (dest == NULL || dest_size == 0) { + if (name == NULL) { return 0; } - if (src != NULL) { - len = strlen(src); + for (i = 0; i < num_nv; i++) { + if (strcasecmp(nv[i].name, name) == 0) { + return nv[i].value; + } } - if (len >= dest_size) { - len = dest_size - 1; - } + return 0; +} - if (len) { - memcpy(dest, src, len); - } +static void free_config(adig_config_t *config) +{ + free(config->servers); + memset(config, 0, sizeof(*config)); +} - dest[len] = 0; - return len; +static void print_help(void) +{ + printf("adig version %s\n\n", ares_version(NULL)); + printf( + "usage: adig [-h] [-d] [-f flag] [[-s server] ...] [-T|U port] [-c class]\n" + " [-t type] name ...\n\n" + " -h : Display this help and exit.\n" + " -d : Print some extra debugging output.\n" + " -f flag : Add a behavior control flag. Possible values are\n" + " igntc - do not retry a truncated query as TCP, just\n" + " return the truncated answer\n" + " noaliases - don't honor the HOSTALIASES environment\n" + " variable\n" + " norecurse - don't query upstream servers recursively\n" + " primary - use the first server\n" + " stayopen - don't close the communication sockets\n" + " usevc - use TCP only\n" + " -s server : Connect to the specified DNS server, instead of the\n" + " system's default one(s). Servers are tried in round-robin,\n" + " if the previous one failed.\n" + " -T port : Connect to the specified TCP port of DNS server.\n" + " -U port : Connect to the specified UDP port of DNS server.\n" + " -c class : Set the query class. Possible values for class are:\n" + " ANY, CHAOS, HS and IN (default)\n" + " -t type : Query records of the specified type. Possible values for\n" + " type are:\n" + " A (default), AAAA, ANY, CNAME, HINFO, MX, NAPTR, NS, PTR,\n" + " SOA, SRV, TXT, TLSA, URI, CAA, SVCB, HTTPS\n\n"); } -int main(int argc, char **argv) +static ares_bool_t read_cmdline(int argc, const char **argv, adig_config_t *config) { - ares_channel_t *channel; - int c; - int i; - int optmask = ARES_OPT_FLAGS; - int dnsclass = C_IN; - int type = T_A; - int status; - int nfds; - int count; - int use_ptr_helper = 0; - struct ares_options options; - struct hostent *hostent; - fd_set read_fds; - fd_set write_fds; - struct timeval *tvp; - struct timeval tv; - struct ares_addr_node *srvr; - struct ares_addr_node *servers = NULL; + ares_getopt_state_t state; + ares_getopt_init(&state, argc, argv); -#ifdef USE_WINSOCK - WORD wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK); - WSADATA wsaData; - WSAStartup(wVersionRequested, &wsaData); -#endif + while (1) { + int c; + int f; - status = ares_library_init(ARES_LIB_INIT_ALL); - if (status != ARES_SUCCESS) { - fprintf(stderr, "ares_library_init: %s\n", ares_strerror(status)); - return 1; - } + c = ares_getopt(&state, "dh?f:s:c:t:T:U:"); + if (c == -1) + break; - options.flags = ARES_FLAG_NOCHECKRESP; - options.servers = NULL; - options.nservers = 0; - while ((c = ares_getopt(argc, argv, "dh?f:s:c:t:T:U:x")) != -1) { switch (c) { case 'd': #ifdef WATT32 dbug_init(); #endif break; + case 'h': - print_help_info_adig(); - break; case '?': - print_help_info_adig(); - break; + config->is_help = ARES_TRUE; + return ARES_TRUE; + case 'f': - /* Add a flag. */ - for (i = 0; i < nflags; i++) { - if (strcmp(flags[i].name, optarg) == 0) { - break; - } - } - if (i < nflags) { - options.flags |= flags[i].value; - } else { - usage(); + f = lookup_flag(configflags, nconfigflags, state.optarg); + if (f == 0) { + snprintf(config->error, sizeof(config->error), "flag %s unknown", + state.optarg); } + + config->options.flags |= f; + config->optmask |= ARES_OPT_FLAGS; break; case 's': - /* User-specified name servers override default ones. */ - srvr = malloc(sizeof(struct ares_addr_node)); - if (!srvr) { - fprintf(stderr, "Out of memory!\n"); - destroy_addr_list(servers); - return 1; + if (state.optarg == NULL) { + snprintf(config->error, sizeof(config->error), "%s", + "missing servers"); + return ARES_FALSE; } - append_addr_list(&servers, srvr); - if (ares_inet_pton(AF_INET, optarg, &srvr->addr.addr4) > 0) { - srvr->family = AF_INET; - } else if (ares_inet_pton(AF_INET6, optarg, &srvr->addr.addr6) > 0) { - srvr->family = AF_INET6; - } else { - hostent = gethostbyname(optarg); - if (!hostent) { - fprintf(stderr, "adig: server %s not found.\n", optarg); - destroy_addr_list(servers); - return 1; - } - switch (hostent->h_addrtype) { - case AF_INET: - srvr->family = AF_INET; - memcpy(&srvr->addr.addr4, hostent->h_addr, - sizeof(srvr->addr.addr4)); - break; - case AF_INET6: - srvr->family = AF_INET6; - memcpy(&srvr->addr.addr6, hostent->h_addr, - sizeof(srvr->addr.addr6)); - break; - default: - fprintf(stderr, "adig: server %s unsupported address family.\n", - optarg); - destroy_addr_list(servers); - return 1; - } + if (config->servers) { + free(config->servers); } - /* Notice that calling ares_init_options() without servers in the - * options struct and with ARES_OPT_SERVERS set simultaneously in - * the options mask, results in an initialization with no servers. - * When alternative name servers have been specified these are set - * later calling ares_set_servers() overriding any existing server - * configuration. To prevent initial configuration with default - * servers that will be discarded later, ARES_OPT_SERVERS is set. - * If this flag is not set here the result shall be the same but - * ares_init_options() will do needless work. */ - optmask |= ARES_OPT_SERVERS; + config->servers = strdup(state.optarg); break; case 'c': - /* Set the query class. */ - for (i = 0; i < nclasses; i++) { - if (strcasecmp(classes[i].name, optarg) == 0) { - break; - } - } - if (i < nclasses) { - dnsclass = classes[i].value; - } else { - usage(); + if (!ares_dns_class_fromstr(&config->qclass, state.optarg)) { + snprintf(config->error, sizeof(config->error), "unrecognied class %s", + state.optarg); + return ARES_FALSE; } break; case 't': - /* Set the query type. */ - for (i = 0; i < ntypes; i++) { - if (strcasecmp(types[i].name, optarg) == 0) { - break; - } - } - if (i < ntypes) { - type = types[i].value; - } else { - usage(); + if (!ares_dns_rec_type_fromstr(&config->qtype, state.optarg)) { + snprintf(config->error, sizeof(config->error), "unrecognied type %s", + state.optarg); + return ARES_FALSE; } break; case 'T': /* Set the TCP port number. */ - if (!ISDIGIT(*optarg)) { - usage(); + if (!isdigit(*state.optarg)) { + snprintf(config->error, sizeof(config->error), "invalid port number"); + return ARES_FALSE; } - options.tcp_port = (unsigned short)strtol(optarg, NULL, 0); - options.flags |= ARES_FLAG_USEVC; - optmask |= ARES_OPT_TCP_PORT; + config->options.tcp_port = (unsigned short)strtol(state.optarg, NULL, 0); + config->options.flags |= ARES_FLAG_USEVC; + config->optmask |= ARES_OPT_TCP_PORT; break; case 'U': /* Set the UDP port number. */ - if (!ISDIGIT(*optarg)) { - usage(); + if (!isdigit(*state.optarg)) { + snprintf(config->error, sizeof(config->error), "invalid port number"); + return ARES_FALSE; } - options.udp_port = (unsigned short)strtol(optarg, NULL, 0); - optmask |= ARES_OPT_UDP_PORT; - break; - - case 'x': - use_ptr_helper++; + config->options.udp_port = (unsigned short)strtol(state.optarg, NULL, 0); + config->optmask |= ARES_OPT_UDP_PORT; break; } } - argc -= optind; - argv += optind; - if (argc == 0) { - usage(); - } - status = ares_init_options(&channel, &options, optmask); + config->args_processed = state.optind; - if (status != ARES_SUCCESS) { - fprintf(stderr, "ares_init_options: %s\n", ares_strerror(status)); - return 1; + if (config->args_processed >= argc) { + snprintf(config->error, sizeof(config->error), "missing query name"); + return ARES_FALSE; } - if (servers) { - status = ares_set_servers(channel, servers); - destroy_addr_list(servers); - if (status != ARES_SUCCESS) { - fprintf(stderr, "ares_init_options: %s\n", ares_strerror(status)); - return 1; - } - } - - /* Initiate the queries, one per command-line argument. If there is - * only one query to do, supply NULL as the callback argument; - * otherwise, supply the query name as an argument so we can - * distinguish responses for the user when printing them out. - */ - for (i = 1; *argv; i++, argv++) { - char *query = *argv; - - if (type == T_PTR && dnsclass == C_IN && use_ptr_helper) { - if (!convert_query(&query, use_ptr_helper >= 2)) { - continue; - } - } + return ARES_TRUE; +} - ares_query(channel, query, dnsclass, type, callback, - i < argc - 1 ? (void *)query : NULL); +static void print_flags(ares_dns_flags_t flags) +{ + if (flags & ARES_FLAG_QR) { + printf(" qr"); } + if (flags & ARES_FLAG_AA) { + printf(" aa"); + } + if (flags & ARES_FLAG_TC) { + printf(" tc"); + } + if (flags & ARES_FLAG_RD) { + printf(" rd"); + } + if (flags & ARES_FLAG_RA) { + printf(" ra"); + } + if (flags & ARES_FLAG_AD) { + printf(" ad"); + } + if (flags & ARES_FLAG_CD) { + printf(" cd"); + } +} - /* Wait for all queries to complete. */ - for (;;) { - FD_ZERO(&read_fds); - FD_ZERO(&write_fds); - nfds = ares_fds(channel, &read_fds, &write_fds); - if (nfds == 0) { - break; - } - tvp = ares_timeout(channel, NULL, &tv); - if (tvp == NULL) { - break; +static void print_header(const ares_dns_record_t *dnsrec) +{ + printf(";; ->>HEADER<<- opcode: %s, status: %s, id: %u\n", + ares_dns_opcode_tostr(ares_dns_record_get_opcode(dnsrec)), + ares_dns_rcode_tostr(ares_dns_record_get_rcode(dnsrec)), + ares_dns_record_get_id(dnsrec)); + printf(";; flags:"); + print_flags(ares_dns_record_get_flags(dnsrec)); + printf("; QUERY: %u, ANSWER: %u, AUTHORITY: %u, ADDITIONAL: %u\n\n", + (unsigned int)ares_dns_record_query_cnt(dnsrec), + (unsigned int)ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER), + (unsigned int)ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_AUTHORITY), + (unsigned int)ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ADDITIONAL)); +} + +static void print_question(const ares_dns_record_t *dnsrec) +{ + size_t i; + printf(";; QUESTION SECTION:\n"); + for (i = 0; i < ares_dns_record_query_cnt(dnsrec); i++) { + const char *name; + ares_dns_rec_type_t qtype; + ares_dns_class_t qclass; + size_t len; + ares_dns_record_query_get(dnsrec, i, &name, &qtype, &qclass); + len = strlen(name); + printf(";%s.\t", name); + if (len + 1 < 24) { + printf("\t"); } - count = select(nfds, &read_fds, &write_fds, NULL, tvp); - if (count < 0 && (status = SOCKERRNO) != EINVAL) { - printf("select fail: %d", status); - return 1; + if (len + 1 < 16) { + printf("\t"); } - ares_process(channel, &read_fds, &write_fds); + printf("%s\t%s\n", ares_dns_class_tostr(qclass), + ares_dns_rec_type_tostr(qtype)); } + printf("\n"); +} - ares_destroy(channel); +static void print_opt_none(const unsigned char *val, size_t val_len) +{ + (void)val; + if (val_len != 0) { + printf("INVALID!"); + } +} - ares_library_cleanup(); +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); + } +} -#ifdef USE_WINSOCK - WSACleanup(); -#endif +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] = ""; - return 0; + ares_inet_ntop(AF_INET6, val + i, buf, sizeof(buf)); + if (i != 0) { + printf(","); + } + printf("%s", buf); + } } -static void callback(void *arg, int status, int timeouts, unsigned char *abuf, - int alen) +static void print_opt_u8_list(const unsigned char *val, size_t val_len) { - char *name = (char *)arg; - int id; - int qr; - int opcode; - int aa; - int tc; - int rd; - int ra; - int rcode; - unsigned int qdcount; - unsigned int ancount; - unsigned int nscount; - unsigned int arcount; - unsigned int i; - const unsigned char *aptr; + size_t i; - (void)timeouts; - - /* Display the query name if given. */ - if (name) { - printf("Answer for query %s:\n", name); + for (i = 0; i < val_len; i++) { + if (i != 0) { + printf(","); + } + printf("%u", (unsigned int)val[i]); } +} - /* Display an error message if there was an error, but only stop if - * we actually didn't get an answer buffer. - */ - if (status != ARES_SUCCESS) { - printf("%s\n", ares_strerror(status)); - if (!abuf) { - return; +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); } +} - /* Won't happen, but check anyway, for safety. */ - if (alen < HFIXEDSZ) { +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; - /* Parse the answer header. */ - id = DNS_HEADER_QID(abuf); - qr = DNS_HEADER_QR(abuf); - opcode = DNS_HEADER_OPCODE(abuf); - aa = DNS_HEADER_AA(abuf); - tc = DNS_HEADER_TC(abuf); - rd = DNS_HEADER_RD(abuf); - ra = DNS_HEADER_RA(abuf); - rcode = DNS_HEADER_RCODE(abuf); - qdcount = DNS_HEADER_QDCOUNT(abuf); - ancount = DNS_HEADER_ANCOUNT(abuf); - nscount = DNS_HEADER_NSCOUNT(abuf); - arcount = DNS_HEADER_ARCOUNT(abuf); - - /* Display the answer header. */ - printf("id: %d\n", id); - printf("flags: %s%s%s%s%s\n", qr ? "qr " : "", aa ? "aa " : "", - tc ? "tc " : "", rd ? "rd " : "", ra ? "ra " : ""); - printf("opcode: %s\n", opcodes[opcode]); - printf("rcode: %s\n", rcodes[rcode]); - - /* Display the questions. */ - printf("Questions:\n"); - aptr = abuf + HFIXEDSZ; - for (i = 0; i < qdcount; i++) { - aptr = display_question(aptr, abuf, alen); - if (aptr == NULL) { - return; + 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); } +} - /* Display the answers. */ - printf("Answers:\n"); - for (i = 0; i < ancount; i++) { - aptr = display_rr(aptr, abuf, alen); - if (aptr == NULL) { - return; - } - } +static void print_opt_str_list(const unsigned char *val, size_t val_len) +{ + size_t cnt = 0; - /* Display the NS records. */ - printf("NS records:\n"); - for (i = 0; i < nscount; i++) { - aptr = display_rr(aptr, abuf, alen); - if (aptr == NULL) { - return; + printf("\""); + while (val_len) { + long read_len = 0; + unsigned char *str = NULL; + ares_status_t status; + + if (cnt) { + printf(","); } - } - /* Display the additional records. */ - printf("Additional records:\n"); - for (i = 0; i < arcount; i++) { - aptr = display_rr(aptr, abuf, alen); - if (aptr == NULL) { - return; + 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 const unsigned char *display_question(const unsigned char *aptr, - const unsigned char *abuf, - int alen) +static void print_opt_name(const unsigned char *val, size_t val_len) { - char *name; - int type; - int dnsclass; - int status; - long len; - - /* Parse the question name. */ - status = ares_expand_name(aptr, abuf, alen, &name, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - aptr += len; - - /* Make sure there's enough data after the name for the fixed part - * of the question. - */ - if (aptr + QFIXEDSZ > abuf + alen) { - ares_free_string(name); - return NULL; - } + char *str = NULL; + long read_len = 0; - /* Parse the question type and class. */ - type = DNS_QUESTION_TYPE(aptr); - dnsclass = DNS_QUESTION_CLASS(aptr); - aptr += QFIXEDSZ; - - /* Display the question, in a format sort of similar to how we will - * display RRs. - */ - printf("\t%-15s.\t", name); - if (dnsclass != C_IN) { - printf("\t%s", class_name(dnsclass)); + if (ares_expand_name(val, val, (int)val_len, &str, &read_len) != + ARES_SUCCESS) { + printf("INVALID!"); + return; } - printf("\t%s\n", type_name(type)); - ares_free_string(name); - return aptr; + + printf("%s.", str); + ares_free_string(str); } -static const unsigned char *display_rr(const unsigned char *aptr, - const unsigned char *abuf, int alen) +static void print_opt_bin(const unsigned char *val, size_t val_len) { - const unsigned char *p; - int type; - int dnsclass; - unsigned int ttl; - int dlen; - int status; - int i; - long len; - int vlen; - char addr[46]; - - union { - unsigned char *as_uchar; - char *as_char; - } name; - - /* Parse the RR name. */ - status = ares_expand_name(aptr, abuf, alen, &name.as_char, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - aptr += len; - - /* Make sure there is enough data after the RR name for the fixed - * part of the RR. - */ - if (aptr + RRFIXEDSZ > abuf + alen) { - ares_free_string(name.as_char); - return NULL; + size_t i; + + for (i = 0; i < val_len; i++) { + printf("%02x", (unsigned int)val[i]); } +} - /* Parse the fixed part of the RR, and advance to the RR data - * field. */ - type = DNS_RR_TYPE(aptr); - dnsclass = DNS_RR_CLASS(aptr); - ttl = DNS_RR_TTL(aptr); - dlen = DNS_RR_LEN(aptr); - aptr += RRFIXEDSZ; - if (aptr + dlen > abuf + alen) { - ares_free_string(name.as_char); - return NULL; +static ares_bool_t adig_isprint(int ch) +{ + if (ch >= 0x20 && ch <= 0x7E) { + return ARES_TRUE; } + return ARES_FALSE; +} - /* Display the RR name, class, and type. */ - printf("\t%-15s.\t%d", name.as_char, ttl); - if (dnsclass != C_IN) { - printf("\t%s", class_name(dnsclass)); +static void print_opt_binp(const unsigned char *val, size_t val_len) +{ + size_t i; + printf("\""); + for (i = 0; i < val_len; i++) { + if (adig_isprint(val[i])) { + printf("%c", val[i]); + } else { + printf("\\%03d", val[i]); + } } - printf("\t%s", type_name(type)); - ares_free_string(name.as_char); - - /* Display the RR data. Don't touch aptr. */ - switch (type) { - case T_CNAME: - case T_MB: - case T_MD: - case T_MF: - case T_MG: - case T_MR: - case T_NS: - case T_PTR: - /* For these types, the RR data is just a domain name. */ - status = ares_expand_name(aptr, abuf, alen, &name.as_char, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf("\t%s.", name.as_char); - ares_free_string(name.as_char); - break; + printf("\""); +} - case T_HINFO: - /* The RR data is two length-counted character strings. */ - p = aptr; - len = *p; - if (p + len + 1 > aptr + dlen) { - return NULL; - } - status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf("\t%s", name.as_char); - ares_free_string(name.as_char); - p += len; - len = *p; - if (p + len + 1 > aptr + dlen) { - return NULL; - } - status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf("\t%s", name.as_char); - ares_free_string(name.as_char); - break; +static void print_opts(const ares_dns_rr_t *rr, ares_dns_rr_key_t key) +{ + size_t i; - case T_MINFO: - /* The RR data is two domain names. */ - p = aptr; - status = ares_expand_name(p, abuf, alen, &name.as_char, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf("\t%s.", name.as_char); - ares_free_string(name.as_char); - p += len; - status = ares_expand_name(p, abuf, alen, &name.as_char, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf("\t%s.", name.as_char); - ares_free_string(name.as_char); - break; + for (i = 0; i < ares_dns_rr_get_opt_cnt(rr, key); i++) { + size_t val_len = 0; + const unsigned char *val = NULL; + unsigned short opt; + const char *name; - case T_MX: - /* The RR data is two bytes giving a preference ordering, and - * then a domain name. - */ - if (dlen < 2) { - return NULL; - } - printf("\t%d", (int)DNS__16BIT(aptr)); - status = ares_expand_name(aptr + 2, abuf, alen, &name.as_char, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf("\t%s.", name.as_char); - ares_free_string(name.as_char); - break; + if (i != 0) { + printf(" "); + } - case T_SOA: - /* The RR data is two domain names and then five four-byte - * numbers giving the serial number and some timeouts. - */ - p = aptr; - status = ares_expand_name(p, abuf, alen, &name.as_char, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf("\t%s.\n", name.as_char); - ares_free_string(name.as_char); - p += len; - status = ares_expand_name(p, abuf, alen, &name.as_char, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf("\t\t\t\t\t\t%s.\n", name.as_char); - ares_free_string(name.as_char); - p += len; - if (p + 20 > aptr + dlen) { - return NULL; - } - printf("\t\t\t\t\t\t( %u %u %u %u %u )", DNS__32BIT(p), DNS__32BIT(p + 4), - DNS__32BIT(p + 8), DNS__32BIT(p + 12), DNS__32BIT(p + 16)); - break; + opt = ares_dns_rr_get_opt(rr, key, i, &val, &val_len); + name = ares_dns_opt_get_name(key, opt); + if (name == NULL) { + printf("key%u", (unsigned int)opt); + } else { + printf("%s", name); + } + if (val_len == 0) { + return; + } - case T_TXT: - /* The RR data is one or more length-counted character - * strings. */ - p = aptr; - while (p < aptr + dlen) { - len = *p; - if (p + len + 1 > aptr + dlen) { - return NULL; - } - status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf("\t%s", name.as_char); - ares_free_string(name.as_char); - p += len; - } - break; + 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; + } + } +} - case T_CAA: +static void print_addr(const ares_dns_rr_t *rr, ares_dns_rr_key_t key) +{ + const struct in_addr *addr = ares_dns_rr_get_addr(rr, key); + char buf[256] = ""; - p = aptr; + ares_inet_ntop(AF_INET, addr, buf, sizeof(buf)); + printf("%s", buf); +} - /* Flags */ - printf(" %u", (int)*p); - p += 1; +static void print_addr6(const ares_dns_rr_t *rr, ares_dns_rr_key_t key) +{ + const struct ares_in6_addr *addr = ares_dns_rr_get_addr6(rr, key); + char buf[256] = ""; - /* Remainder of record */ - vlen = (int)dlen - ((char)*p) - 2; + ares_inet_ntop(AF_INET6, addr, buf, sizeof(buf)); + printf("%s", buf); +} - /* The Property identifier, one of: - - "issue", - - "iodef", or - - "issuewild" */ - status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf(" %s", name.as_char); - ares_free_string(name.as_char); - p += len; +static void print_u8(const ares_dns_rr_t *rr, ares_dns_rr_key_t key) +{ + unsigned char u8 = ares_dns_rr_get_u8(rr, key); + printf("%u", (unsigned int)u8); +} - if (p + vlen > abuf + alen) { - return NULL; - } +static void print_u16(const ares_dns_rr_t *rr, ares_dns_rr_key_t key) +{ + unsigned short u16 = ares_dns_rr_get_u16(rr, key); + printf("%u", (unsigned int)u16); +} - /* A sequence of octets representing the Property Value */ - printf(" %.*s", vlen, p); - break; +static void print_u32(const ares_dns_rr_t *rr, ares_dns_rr_key_t key) +{ + unsigned int u32 = ares_dns_rr_get_u32(rr, key); + printf("%u", u32); +} - case T_A: - /* The RR data is a four-byte Internet address. */ - if (dlen != 4) { - return NULL; - } - printf("\t%s", ares_inet_ntop(AF_INET, aptr, addr, sizeof(addr))); - break; +static void print_name(const ares_dns_rr_t *rr, ares_dns_rr_key_t key) +{ + const char *str = ares_dns_rr_get_str(rr, key); + printf("%s.", str); +} - case T_AAAA: - /* The RR data is a 16-byte IPv6 address. */ - if (dlen != 16) { - return NULL; - } - printf("\t%s", ares_inet_ntop(AF_INET6, aptr, addr, sizeof(addr))); - break; +static void print_str(const ares_dns_rr_t *rr, ares_dns_rr_key_t key) +{ + const char *str = ares_dns_rr_get_str(rr, key); + printf("\"%s\"", str); +} - case T_WKS: - /* Not implemented yet */ - break; +static void print_bin(const ares_dns_rr_t *rr, ares_dns_rr_key_t key) +{ + size_t len = 0; + const unsigned char *binp = ares_dns_rr_get_bin(rr, key, &len); + print_opt_bin(binp, len); +} - case T_SRV: - /* The RR data is three two-byte numbers representing the - * priority, weight, and port, followed by a domain name. - */ +static void print_binp(const ares_dns_rr_t *rr, ares_dns_rr_key_t key) +{ + size_t len; + const unsigned char *binp = ares_dns_rr_get_bin(rr, key, &len); - printf("\t%d", (int)DNS__16BIT(aptr)); - printf(" %d", (int)DNS__16BIT(aptr + 2)); - printf(" %d", (int)DNS__16BIT(aptr + 4)); + print_opt_binp(binp, len); +} - status = ares_expand_name(aptr + 6, abuf, alen, &name.as_char, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf("\t%s.", name.as_char); - ares_free_string(name.as_char); - break; +static void print_rr(const ares_dns_rr_t *rr) +{ + const char *name = ares_dns_rr_get_name(rr); + size_t len = strlen(name); + size_t keys_cnt = 0; + ares_dns_rec_type_t rtype = ares_dns_rr_get_type(rr); + const ares_dns_rr_key_t *keys = ares_dns_rr_get_keys(rtype, &keys_cnt); + size_t i; + + printf("%s.\t", name); + if (len < 24) { + printf("\t"); + } + + printf("%u\t%s\t%s\t", ares_dns_rr_get_ttl(rr), + ares_dns_class_tostr(ares_dns_rr_get_class(rr)), + ares_dns_rec_type_tostr(rtype)); + + /* Output params here */ + for (i = 0; i < keys_cnt; i++) { + ares_dns_datatype_t datatype = ares_dns_rr_key_datatype(keys[i]); + if (i != 0) { + printf(" "); + } + + switch (datatype) { + case ARES_DATATYPE_INADDR: + print_addr(rr, keys[i]); + break; + case ARES_DATATYPE_INADDR6: + print_addr6(rr, keys[i]); + break; + case ARES_DATATYPE_U8: + print_u8(rr, keys[i]); + break; + case ARES_DATATYPE_U16: + print_u16(rr, keys[i]); + break; + case ARES_DATATYPE_U32: + print_u32(rr, keys[i]); + break; + case ARES_DATATYPE_NAME: + print_name(rr, keys[i]); + break; + case ARES_DATATYPE_STR: + print_str(rr, keys[i]); + break; + case ARES_DATATYPE_BIN: + print_bin(rr, keys[i]); + break; + case ARES_DATATYPE_BINP: + print_binp(rr, keys[i]); + break; + case ARES_DATATYPE_OPT: + print_opts(rr, keys[i]); + break; + } + } - case T_URI: - /* The RR data is two two-byte numbers representing the - * priority and weight, followed by a target. - */ + printf("\n"); +} - printf("\t%d ", (int)DNS__16BIT(aptr)); - printf("%d \t\t", (int)DNS__16BIT(aptr + 2)); - p = aptr + 4; - for (i = 0; i < dlen - 4; ++i) { - printf("%c", p[i]); - } - break; +static const ares_dns_rr_t *has_opt(ares_dns_record_t *dnsrec, + ares_dns_section_t section) +{ + size_t i; + for (i = 0; i < ares_dns_record_rr_cnt(dnsrec, section); i++) { + const ares_dns_rr_t *rr = ares_dns_record_rr_get(dnsrec, section, i); + if (ares_dns_rr_get_type(rr) == ARES_REC_TYPE_OPT) { + return rr; + } + } + return NULL; +} - case T_NAPTR: +static void print_section(ares_dns_record_t *dnsrec, ares_dns_section_t section) +{ + size_t i; - printf("\t%d", (int)DNS__16BIT(aptr)); /* order */ - printf(" %d\n", (int)DNS__16BIT(aptr + 2)); /* preference */ + if (ares_dns_record_rr_cnt(dnsrec, section) == 0 || + (ares_dns_record_rr_cnt(dnsrec, section) == 1 && + has_opt(dnsrec, section) != NULL)) { + return; + } - p = aptr + 4; - status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf("\t\t\t\t\t\t%s\n", name.as_char); - ares_free_string(name.as_char); - p += len; + printf(";; %s SECTION:\n", ares_dns_section_tostr(section)); + for (i = 0; i < ares_dns_record_rr_cnt(dnsrec, section); i++) { + const ares_dns_rr_t *rr = ares_dns_record_rr_get(dnsrec, section, i); + if (ares_dns_rr_get_type(rr) == ARES_REC_TYPE_OPT) { + continue; + } + print_rr(rr); + } + printf("\n"); +} - status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf("\t\t\t\t\t\t%s\n", name.as_char); - ares_free_string(name.as_char); - p += len; +static void print_opt_psuedosection(ares_dns_record_t *dnsrec) +{ + const ares_dns_rr_t *rr = has_opt(dnsrec, ARES_SECTION_ADDITIONAL); + if (rr == NULL) { + return; + } - status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf("\t\t\t\t\t\t%s\n", name.as_char); - ares_free_string(name.as_char); - p += len; + printf(";; OPT PSEUDOSECTION:\n"); + printf("; EDNS: version: %u, flags: %u; udp: %u\t", + (unsigned int)ares_dns_rr_get_u8(rr, ARES_RR_OPT_VERSION), + (unsigned int)ares_dns_rr_get_u16(rr, ARES_RR_OPT_FLAGS), + (unsigned int)ares_dns_rr_get_u16(rr, ARES_RR_OPT_UDP_SIZE)); - status = ares_expand_name(p, abuf, alen, &name.as_char, &len); - if (status != ARES_SUCCESS) { - return NULL; - } - printf("\t\t\t\t\t\t%s", name.as_char); - ares_free_string(name.as_char); - break; + printf("\n"); +} - case T_DS: - case T_SSHFP: - case T_RRSIG: - case T_NSEC: - case T_DNSKEY: - printf("\t[RR type parsing unavailable]"); - break; +static void callback(void *arg, int status, int timeouts, unsigned char *abuf, + int alen) +{ + ares_dns_record_t *dnsrec = NULL; + (void)arg; + (void)timeouts; - default: - printf("\t[Unknown RR; cannot parse]"); - break; + printf(";; Got answer:"); + if (status != ARES_SUCCESS) { + printf(" %s", ares_strerror(status)); } printf("\n"); - return aptr + dlen; + if (abuf == NULL || alen == 0) { + return; + } + + status = (int)ares_dns_parse(abuf, (size_t)alen, 0, &dnsrec); + if (status != ARES_SUCCESS) { + fprintf(stderr, ";; FAILED TO PARSE DNS PACKET: %s\n", + ares_strerror(status)); + return; + } + + print_header(dnsrec); + print_opt_psuedosection(dnsrec); + print_question(dnsrec); + print_section(dnsrec, ARES_SECTION_ANSWER); + print_section(dnsrec, ARES_SECTION_ADDITIONAL); + print_section(dnsrec, ARES_SECTION_AUTHORITY); + + printf(";; MSG SIZE rcvd: %d\n\n", alen); + ares_dns_record_destroy(dnsrec); } -/* - * With the '-x' (or '-xx') and '-t PTR' options, convert a query for an - * address into a more useful 'T_PTR' type question. - * Like with an input 'query': - * "a.b.c.d" -> "d.c.b.a".in-addr.arpa" for an IPv4 address. - * "a.b.c....x.y.z" -> "z.y.x....c.d.e.IP6.ARPA" for an IPv6 address. - * - * An example from 'dig -x PTR 2001:470:1:1b9::31': - * - * QUESTION SECTION: - * 1.3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.b.1.0.1.0.0.0.0.7.4.0.1.0.0.2.IP6.ARPA. IN - * PTR - * - * ANSWER SECTION: - * 1.3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.b.1.0.1.0.0.0.0.7.4.0.1.0.0.2.IP6.ARPA. - * 254148 IN PTR ipv6.cybernode.com. - * - * If 'use_bitstring == 1', try to use the more compact RFC-2673 bitstring - * format. Thus the above 'dig' query should become: - * [x13000000000000009b10100007401002].IP6.ARPA. IN PTR - */ -static int convert_query(char **name_p, int use_bitstring) +static ares_status_t enqueue_query(ares_channel_t *channel, + const adig_config_t *config, + const char *name) { -#ifndef MAX_IP6_RR -# define MAX_IP6_RR (16 * sizeof(".x.x") + sizeof(".IP6.ARPA") + 1) -#endif + ares_dns_record_t *dnsrec = NULL; + ares_dns_rr_t *rr = NULL; + ares_status_t status; + unsigned char *buf = NULL; + size_t buf_len = 0; + unsigned short flags = 0; + char *nametemp = NULL; -#ifdef HAVE_INET_PTON -# define ACCEPTED_RETVAL4 1 -# define ACCEPTED_RETVAL6 1 -#else -# define ACCEPTED_RETVAL4 32 -# define ACCEPTED_RETVAL6 128 -#endif + if (!(config->options.flags & ARES_FLAG_NORECURSE)) { + flags |= ARES_FLAG_RD; + } - static char new_name[MAX_IP6_RR]; - static const char hex_chars[] = "0123456789ABCDEF"; - - union { - struct in_addr addr4; - struct ares_in6_addr addr6; - } addr; - - if (ares_inet_pton(AF_INET, *name_p, &addr.addr4) == 1) { - unsigned long laddr = ntohl(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(new_name, sizeof(new_name), "%lu.%lu.%lu.%lu.in-addr.arpa", a4, a3, - a2, a1); - *name_p = new_name; - return (1); + status = ares_dns_record_create(&dnsrec, 0, flags, ARES_OPCODE_QUERY, + ARES_RCODE_NOERROR); + if (status != ARES_SUCCESS) { + goto done; } - if (ares_inet_pton(AF_INET6, *name_p, &addr.addr6) == 1) { - char *c = new_name; - const unsigned char *ip = (const unsigned char *)&addr.addr6; - int max_i = (int)sizeof(addr.addr6) - 1; - int i; - int hi; - int lo; - - /* Use the more compact RFC-2673 notation? - * Currently doesn't work or unsupported by the DNS-servers I've tested - * against. - */ - if (use_bitstring) { - *c++ = '\\'; - *c++ = '['; - *c++ = 'x'; - for (i = max_i; i >= 0; i--) { - hi = ip[i] >> 4; - lo = ip[i] & 15; - *c++ = hex_chars[lo]; - *c++ = hex_chars[hi]; - } - ares_strcpy(c, "].IP6.ARPA", sizeof(new_name) - strlen(c)); - } else { - for (i = max_i; i >= 0; i--) { - hi = ip[i] >> 4; - lo = ip[i] & 15; - *c++ = hex_chars[lo]; - *c++ = '.'; - *c++ = hex_chars[hi]; - *c++ = '.'; - } - ares_strcpy(c, "IP6.ARPA", sizeof(new_name) - strlen(c)); + /* If it is a PTR record, convert from ip address into in-arpa form + * automatically */ + if (config->qtype == ARES_REC_TYPE_PTR) { + struct ares_addr addr; + size_t len; + addr.family = AF_UNSPEC; + + if (ares_dns_pton(name, &addr, &len) != NULL) { + nametemp = ares_dns_addr_to_ptr(&addr); + name = nametemp; } - *name_p = new_name; - return (1); } - printf("Address %s was not legal for this query.\n", *name_p); - return (0); -} -static const char *type_name(int type) -{ - int i; + status = + ares_dns_record_query_add(dnsrec, name, config->qtype, config->qclass); + if (status != ARES_SUCCESS) { + goto done; + } - for (i = 0; i < ntypes; i++) { - if (types[i].value == type) { - return types[i].name; - } + status = ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL, "", + ARES_REC_TYPE_OPT, ARES_CLASS_IN, 0); + if (status != ARES_SUCCESS) { + goto done; + } + ares_dns_rr_set_u16(rr, ARES_RR_OPT_UDP_SIZE, 1280); + ares_dns_rr_set_u8(rr, ARES_RR_OPT_VERSION, 0); + + status = ares_dns_write(dnsrec, &buf, &buf_len); + if (status != ARES_SUCCESS) { + goto done; } - return "(unknown)"; + + ares_send(channel, buf, (int)buf_len, callback, NULL); + ares_free_string(buf); + +done: + ares_free_string(nametemp); + ares_dns_record_destroy(dnsrec); + return status; } -static const char *class_name(int dnsclass) +int main(int argc, char **argv) { - int i; + ares_channel_t *channel = NULL; + ares_status_t status; + adig_config_t config; + int i; + int rv = 0; - for (i = 0; i < nclasses; i++) { - if (classes[i].value == dnsclass) { - return classes[i].name; +#ifdef USE_WINSOCK + WORD wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK); + WSADATA wsaData; + WSAStartup(wVersionRequested, &wsaData); +#endif + + status = (ares_status_t)ares_library_init(ARES_LIB_INIT_ALL); + if (status != ARES_SUCCESS) { + fprintf(stderr, "ares_library_init: %s\n", ares_strerror((int)status)); + return 1; + } + + memset(&config, 0, sizeof(config)); + config.qclass = ARES_CLASS_IN; + config.qtype = ARES_REC_TYPE_A; + if (!read_cmdline(argc, (const char **)argv, &config)) { + printf("%s\n", config.error); + print_help(); + rv = 1; + goto done; + } + + if (config.is_help) { + print_help(); + goto done; + } + + status = + (ares_status_t)ares_init_options(&channel, &config.options, config.optmask); + if (status != ARES_SUCCESS) { + fprintf(stderr, "ares_init_options: %s\n", ares_strerror((int)status)); + rv = 1; + goto done; + } + + if (config.servers) { + status = (ares_status_t)ares_set_servers_ports_csv(channel, config.servers); + if (status != ARES_SUCCESS) { + fprintf(stderr, "ares_set_servers_ports_csv: %s\n", + ares_strerror((int)status)); + rv = 1; + goto done; } } - return "(unknown)"; -} -static void usage(void) -{ - fprintf(stderr, "usage: adig [-h] [-d] [-f flag] [-s server] [-c class] " - "[-t type] [-T|U port] [-x|-xx] name ...\n"); - exit(1); -} + /* Enqueue a query for each separate name */ + for (i = config.args_processed; i < argc; i++) { + status = enqueue_query(channel, &config, argv[i]); + if (status != ARES_SUCCESS) { + fprintf(stderr, "Failed to create query for %s: %s\n", argv[i], + ares_strerror((int)status)); + rv = 1; + goto done; + } + } -static void destroy_addr_list(struct ares_addr_node *head) -{ - while (head) { - struct ares_addr_node *detached = head; - head = head->next; - free(detached); + /* Debug */ + printf("\n; <<>> c-ares DiG %s <<>>", ares_version(NULL)); + for (i = config.args_processed; i < argc; i++) { + printf(" %s", argv[i]); } -} + printf("\n"); -static void append_addr_list(struct ares_addr_node **head, - struct ares_addr_node *node) -{ - struct ares_addr_node *last; - node->next = NULL; - if (*head) { - last = *head; - while (last->next) { - last = last->next; + while (1) { + fd_set read_fds; + fd_set write_fds; + int nfds; + struct timeval tv; + struct timeval *tvp; + int count; + + FD_ZERO(&read_fds); + FD_ZERO(&write_fds); + memset(&tv, 0, sizeof(tv)); + + nfds = ares_fds(channel, &read_fds, &write_fds); + if (nfds == 0) { + break; + } + tvp = ares_timeout(channel, NULL, &tv); + if (tvp == NULL) { + break; + } + count = select(nfds, &read_fds, &write_fds, NULL, tvp); + if (count < 0) { +#ifdef USE_WINSOCK + int err = WSAGetLastError(); +#else + int err = errno; +#endif + if (err != EAGAIN && err != EINTR) { + fprintf(stderr, "select fail: %d", err); + rv = 1; + goto done; + } } - last->next = node; - } else { - *head = node; + ares_process(channel, &read_fds, &write_fds); } -} -/* Information from the man page. Formatting taken from man -h */ -static void print_help_info_adig(void) -{ - printf("adig, version %s\n\n", ARES_VERSION_STR); - printf( - "usage: adig [-h] [-d] [-f flag] [[-s server] ...] [-T|U port] [-c class] " - "[-t type] [-x|-xx] name ...\n\n" - " h : Display this help and exit.\n" - " d : Print some extra debugging output.\n\n" - " f flag : Add a behavior control flag. Possible values are\n" - " igntc - ignore to query in TCP to get truncated UDP " - "answer,\n" - " noaliases - don't honor the HOSTALIASES environment " - "variable,\n" - " norecurse - don't query upstream servers recursively,\n" - " primary - use the first server,\n" - " stayopen - don't close the communication sockets, and\n" - " usevc - use TCP only.\n" - " s server : Connect to the specified DNS server, instead of the system's " - "default one(s).\n" - " Servers are tried in round-robin, if the previous one " - "failed.\n" - " T port : Connect to the specified TCP port of DNS server.\n" - " U port : Connect to the specified UDP port of DNS server.\n" - " c class : Set the query class. Possible values for class are ANY, " - "CHAOS, HS and IN (default)\n" - " t type : Query records of the specified type.\n" - " Possible values for type are A (default), AAAA, AFSDB, ANY, " - "AXFR,\n" - " CNAME, GPOS, HINFO, ISDN, KEY, LOC, MAILA, MAILB, MB, MD, " - "MF, MG,\n" - " MINFO, MR, MX, NAPTR, NS, NSAP, NSAP_PTR, NULL, PTR, PX, " - "RP, RT,\n" - " SIG, SOA, SRV, TXT, URI, WKS and X25.\n\n" - " -x : For a '-t PTR a.b.c.d' lookup, query for 'd.c.b.a.in-addr.arpa.'\n" - " -xx : As above, but for IPv6, compact the format into a bitstring like\n" - " '[xabcdef00000000000000000000000000].IP6.ARPA.'\n"); - exit(0); +done: + free_config(&config); + ares_destroy(channel); + ares_library_cleanup(); + +#ifdef USE_WINSOCK + WSACleanup(); +#endif + return rv; } diff --git a/src/tools/ahost.c b/src/tools/ahost.c index 7f133969..1f293fc6 100644 --- a/src/tools/ahost.c +++ b/src/tools/ahost.c @@ -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(); } diff --git a/src/tools/ares_getopt.c b/src/tools/ares_getopt.c index b49606fa..6f147832 100644 --- a/src/tools/ares_getopt.c +++ b/src/tools/ares_getopt.c @@ -50,77 +50,79 @@ #include #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 */ + 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 */ } diff --git a/src/tools/ares_getopt.h b/src/tools/ares_getopt.h index b80da752..d25d0b93 100644 --- a/src/tools/ares_getopt.h +++ b/src/tools/ares_getopt.h @@ -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 */ diff --git a/test/ares-test-internal.cc b/test/ares-test-internal.cc index fe55110c..ef26d463 100644 --- a/test/ares-test-internal.cc +++ b/test/ares-test-internal.cc @@ -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,