Rework internals to pass around `ares_dns_record_t` instead of binary data (#730)

c-ares has historically passed around raw dns packets in binary form.
Now that we have a new parser, and messages are already parsed
internally, lets pass around that parsed message rather than requiring
multiple parse attempts on the same message. Also add a new
`ares_send_dnsrec()` and `ares_query_dnsrec()` similar to
`ares_search_dnsrec()` added with PR #719 that can return the pointer to
the `ares_dns_record_t` to the caller enqueuing queries and rework
`ares_search_dnsrec()` to use `ares_send_dnsrec()` internally.

Fix By: Brad House (@bradh352)
pull/732/head
Brad House 8 months ago committed by GitHub
parent a516bbbbaf
commit e862d1facc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      docs/Makefile.inc
  2. 43
      docs/ares_dns_record.3
  3. 3
      docs/ares_dns_record_duplicate.3
  4. 3
      docs/ares_dns_record_query_set_name.3
  5. 3
      docs/ares_dns_record_query_set_type.3
  6. 90
      docs/ares_query.3
  7. 3
      docs/ares_query_dnsrec.3
  8. 28
      docs/ares_search.3
  9. 129
      docs/ares_send.3
  10. 3
      docs/ares_send_dnsrec.3
  11. 46
      include/ares.h
  12. 43
      include/ares_dns_record.h
  13. 11
      src/lib/ares__parse_into_addrinfo.c
  14. 2
      src/lib/ares_cancel.c
  15. 2
      src/lib/ares_destroy.c
  16. 14
      src/lib/ares_dns_private.h
  17. 57
      src/lib/ares_dns_record.c
  18. 25
      src/lib/ares_dns_write.c
  19. 240
      src/lib/ares_getaddrinfo.c
  20. 20
      src/lib/ares_gethostbyaddr.c
  21. 15
      src/lib/ares_parse_a_reply.c
  22. 15
      src/lib/ares_parse_aaaa_reply.c
  23. 51
      src/lib/ares_parse_ptr_reply.c
  24. 58
      src/lib/ares_private.h
  25. 47
      src/lib/ares_process.c
  26. 56
      src/lib/ares_qcache.c
  27. 150
      src/lib/ares_query.c
  28. 703
      src/lib/ares_search.c
  29. 101
      src/lib/ares_send.c

@ -20,12 +20,15 @@ MANPAGES = ares_cancel.3 \
ares_dns_rcode_tostr.3 \
ares_dns_record.3 \
ares_dns_record_create.3 \
ares_dns_record_duplicate.3 \
ares_dns_record_get_flags.3 \
ares_dns_record_get_id.3 \
ares_dns_record_get_opcode.3 \
ares_dns_record_get_rcode.3 \
ares_dns_record_destroy.3 \
ares_dns_record_query_add.3 \
ares_dns_record_query_set_name.3 \
ares_dns_record_query_set_type.3 \
ares_dns_record_query_cnt.3 \
ares_dns_record_query_get.3 \
ares_dns_record_rr_add.3 \
@ -114,6 +117,7 @@ MANPAGES = ares_cancel.3 \
ares_search.3 \
ares_search_dnsrec.3 \
ares_send.3 \
ares_send_dnsrec.3 \
ares_set_local_dev.3 \
ares_set_local_ip4.3 \
ares_set_local_ip6.3 \

@ -28,6 +28,8 @@ ares_status_t ares_dns_record_create(ares_dns_record_t **dnsrec,
ares_dns_opcode_t opcode,
ares_dns_rcode_t rcode);
ares_dns_record_t *ares_dns_record_duplicate(const ares_dns_record_t *dnsrec);
unsigned short ares_dns_record_get_id(const ares_dns_record_t *dnsrec);
unsigned short ares_dns_record_get_flags(const ares_dns_record_t *dnsrec);
@ -41,6 +43,14 @@ ares_status_t ares_dns_record_query_add(ares_dns_record_t *dnsrec,
ares_dns_rec_type_t qtype,
ares_dns_class_t qclass);
ares_status_t ares_dns_record_query_set_name(ares_dns_record_t *dnsrec,
size_t idx,
const char *name);
ares_status_t ares_dns_record_query_set_type(ares_dns_record_t *dnsrec,
size_t idx,
ares_dns_rec_type_t qtype);
size_t ares_dns_record_query_cnt(const ares_dns_record_t *dnsrec);
ares_status_t ares_dns_record_query_get(const ares_dns_record_t *dnsrec,
@ -317,6 +327,13 @@ is meant mostly for responses and is passed in the
.IR rcode
parameter and is typically \fPARES_RCODE_NOERROR\fP.
The \fIares_dns_record_duplicate(3)\fP function duplicates an existing DNS
record structure. This may be useful if needing to save a result as retrieved
from \fIares_send_dnsrec(3)\fP or \fIares_search_dnsrec(3)\fP. The structure
to be duplicated is passed in the
.IR dnsrec
parameter, and the duplicated copy is returned, or NULL on error such as
out of memory.
The \fIares_dns_record_get_id(3)\fP function is used to retrieve the DNS
message id from the DNS record provided in the
@ -350,6 +367,29 @@ parameter and the question class (typically \fIARES_CLASS_IN\fP) in the
.IR qclass
parameter.
The \fIares_dns_record_query_set_name(3)\fP function is used to modify the
question name in the DNS record provided in the
.IR dnsrec
parameter. The index of the query, which must be less than
\fIares_dns_record_query_cnt(3)\fP, is provided in the
.IR idx
parameter. The new domain name is provided in the
.IR name
parameter. Care should be taken as this will cause invalidation of any
\fIname\fP pointer retrieved from \fIares_dns_Record_query_get(3)\fP. This
function is useful if sending multiple similar queries without re-creating
the entire DNS query.
The \fIares_dns_record_query_set_type(3)\fP function is used to modify the
question type in the DNS record provided in the
.IR dnsrec
parameter. The index of the query, which must be less than
\fIares_dns_record_query_cnt(3)\fP, is provided in the
.IR idx
parameter. The new query type is provided in the
.IR qtype
parameter.
The \fIares_dns_record_query_cnt(3)\fP function is used to retrieve the number
of DNS questions in the DNS record provided in the
.IR dnsrec
@ -363,7 +403,8 @@ parameter. The index provided in the
parameter must be less than the value returned from \fIares_dns_record_query_cnt(3)\fP.
The DNS question name will be returned in the variable pointed to by the
.IR name
parameter, this may be provided as NULL if the name is not needed.
parameter, this may be provided as NULL if the name is not needed. This pointer
will be invalided by any call to \fIares_dns_record_query_set_name(3)\fP.
The DNS question type will be returned in the variable pointed to by the
.IR qtype
parameter, this may be provided as NULL if the type is not needed.

@ -0,0 +1,3 @@
.\" Copyright (C) 2023 The c-ares project and its contributors.
.\" SPDX-License-Identifier: MIT
.so man3/ares_dns_record.3

@ -0,0 +1,3 @@
.\" Copyright (C) 2023 The c-ares project and its contributors.
.\" SPDX-License-Identifier: MIT
.so man3/ares_dns_record.3

@ -0,0 +1,3 @@
.\" Copyright (C) 2023 The c-ares project and its contributors.
.\" SPDX-License-Identifier: MIT
.so man3/ares_dns_record.3

@ -9,19 +9,32 @@ ares_query \- Initiate a single-question DNS query
.nf
#include <ares.h>
typedef void (*ares_callback)(void *\fIarg\fP, int \fIstatus\fP,
int \fItimeouts\fP, unsigned char *\fIabuf\fP,
int \fIalen\fP)
typedef void (*ares_callback_dnsrec)(void *arg, ares_status_t status,
size_t timeouts,
const ares_dns_record_t *dnsrec);
ares_status_t ares_query_dnsrec(ares_channel_t *channel,
const char *name,
ares_dns_class_t dnsclass,
ares_dns_rec_type_t type,
ares_callback_dnsrec callback,
void *arg,
unsigned short *qid);
typedef void (*ares_callback)(void *arg, int status,
int timeouts, unsigned char *abuf,
int alen);
void ares_query(ares_channel_t *channel, const char *name,
int dnsclass, int type,
ares_callback callback, void *arg);
void ares_query(ares_channel_t *\fIchannel\fP, const char *\fIname\fP,
int \fIdnsclass\fP, int \fItype\fP,
ares_callback \fIcallback\fP, void *\fIarg\fP)
.fi
.SH DESCRIPTION
The
.B ares_query
function initiates a single-question DNS query on the name service
channel identified by
The \fBares_query_dnsrec(3)\fP and \fBares_query(3)\fP functions initiate a
single-question DNS query on the name service channel identified by
.IR channel .
The parameter
.I name
@ -31,27 +44,27 @@ a label must be escaped with a backslash. The parameters
.I dnsclass
and
.I type
give the class and type of the query using the values defined in
.BR <arpa/nameser.h> .
give the class and type of the query.
\fBares_query_dnsrec(3)\fP uses the ares \fBares_dns_class_t\fP and
\fBares_dns_rec_type_t\fP defined types. However, \fBares_query(3)\fP uses
the values defined in \fB<arpa/nameser.h>\fP.
When the query is complete or has failed, the ares library will invoke
.IR callback .
Completion or failure of the query may happen immediately, or may
happen during a later call to
.BR ares_process (3)
or
.BR ares_destroy (3).
.PP
Completion or failure of the query may happen immediately (even before the
return of the function call), or may happen during a later call to
\fBares_process(3)\fP or \fBares_destroy(3)\fP.
If this is called from a thread other than which the main program event loop is
running, care needs to be taken to ensure any file descriptor lists are updated
immediately within the eventloop. When the associated callback is called,
it is called with a channel lock so care must be taken to ensure any processing
is minimal to prevent DNS channel stalls.
.PP
The callback argument
.I arg
is copied from the
.B ares_query
argument
is copied from the \fBares_query_dnsrec(3)\fP or \fBares_query(3)\fP argument
.IR arg .
The callback argument
.I status
@ -73,9 +86,7 @@ The query completed but the server claims to have experienced a
failure. (This code can only occur if the
.B ARES_FLAG_NOCHECKRESP
flag was specified at channel initialization time; otherwise, such
responses are ignored at the
.BR ares_send (3)
level.)
responses are ignored at the \fBares_send_dnsrec(3)\fP level.)
.TP 19
.B ARES_ENOTFOUND
The query completed but the queried-for domain name was not found.
@ -85,18 +96,14 @@ The query completed but the server does not implement the operation
requested by the query. (This code can only occur if the
.B ARES_FLAG_NOCHECKRESP
flag was specified at channel initialization time; otherwise, such
responses are ignored at the
.BR ares_send (3)
level.)
responses are ignored at the \fBares_send_dnsrec(3)\fP level.)
.TP 19
.B ARES_EREFUSED
The query completed but the server refused the query. (This code can
only occur if the
.B ARES_FLAG_NOCHECKRESP
flag was specified at channel initialization time; otherwise, such
responses are ignored at the
.BR ares_send (3)
level.)
responses are ignored at the \fBares_send_dnsrec(3)\fP level.)
.TP 19
.B ARES_EBADNAME
The query name
@ -126,23 +133,26 @@ is being destroyed; the query will not be completed.
The query will not be completed because no DNS servers were configured on the
channel.
.PP
The callback argument
.I timeouts
reports how many times a query timed out during the execution of the
given request.
.PP
If the query completed (even if there was something wrong with it, as
indicated by some of the above error codes), the callback argument
.I dnsrec
or
.I abuf
points to a result buffer of length
.IR alen .
If the query did not complete,
.I abuf
will be NULL and
.I alen
will be 0.
will be non-NULL, otherwise they will be NULL.
.SH AVAILABILITY
\fBares_query_dnsrec(3)\fP was introduced in c-ares 1.28.0.
.SH SEE ALSO
.BR ares_process (3)
.BR ares_process (3),
.BR ares_dns_record (3)
.SH AUTHOR
Greg Hudson, MIT Information Systems
.br

@ -0,0 +1,3 @@
.\" Copyright (C) 2023 The c-ares project and its contributors.
.\" SPDX-License-Identifier: MIT
.so man3/ares_query.3

@ -9,22 +9,23 @@ ares_search \- Initiate a DNS query with domain search
.nf
#include <ares.h>
typedef void (*ares_callback)(void *\fIarg\fP, int \fIstatus\fP,
int \fItimeouts\fP, unsigned char *\fIabuf\fP,
int \fIalen\fP)
typedef void (*ares_callback_dnsrec)(void *\fIarg\fP,
ares_status_t \fIstatus\fP,
size_t \fItimeouts\fP,
const ares_dns_record_t *\fIdnsrec\fP)
const ares_dns_record_t *\fIdnsrec\fP);
void ares_search_dnsrec(ares_channel_t *\fIchannel\fP,
ares_dns_record_t *\fIdnsrec\fP,
ares_callback_dnsrec \fIcallback\fP, void *\fIarg\fP);
typedef void (*ares_callback)(void *\fIarg\fP, int \fIstatus\fP,
int \fItimeouts\fP, unsigned char *\fIabuf\fP,
int \fIalen\fP);
void ares_search(ares_channel_t *\fIchannel\fP, const char *\fIname\fP,
int \fIdnsclass\fP, int \fItype\fP,
ares_callback \fIcallback\fP, void *\fIarg\fP)
ares_callback \fIcallback\fP, void *\fIarg\fP);
void ares_search_dnsrec(ares_channel_t *\fIchannel\fP,
ares_dns_record_t *\fIdnsrec\fP,
ares_callback_dnsrec \fIcallback\fP, void *\fIarg\fP)
.fi
.SH DESCRIPTION
The
@ -167,9 +168,18 @@ object
.I dnsrec
rather than a raw buffer with length. Note that this object is read-only.
The \fIares_search_dnsrec(3)\fP function returns an \fIares_status_t\fP response
code. This may be useful to know that the query was enqueued properly. The
response code does not reflect the result of the query, just the result of the
enqueuing of the query.
.SH AVAILABILITY
\fBares_search_dnsrec(3)\fP was introduced in c-ares 1.28.0.
.SH SEE ALSO
.BR ares_process (3),
.BR ares_dns_record (3)
.SH AUTHOR
Greg Hudson, MIT Information Systems
.br

@ -9,46 +9,78 @@ ares_send \- Initiate a DNS query
.nf
#include <ares.h>
typedef void (*ares_callback)(void *\fIarg\fP, int \fIstatus\fP,
int \fItimeouts\fP, unsigned char *\fIabuf\fP,
int \fIalen\fP)
typedef void (*ares_callback_dnsrec)(void *arg, ares_status_t status,
size_t timeouts,
const ares_dns_record_t *dnsrec);
ares_status_t ares_send_dnsrec(ares_channel_t *channel,
const ares_dns_record_t *dnsrec,
ares_callback_dnsrec callback,
void *arg, unsigned short *qid);
typedef void (*ares_callback)(void *arg, int status,
int timeouts, unsigned char *abuf,
int alen);
void ares_send(ares_channel_t *channel, const unsigned char *qbuf,
int qlen, ares_callback callback, void *arg);
void ares_send(ares_channel_t *\fIchannel\fP, const unsigned char *\fIqbuf\fP,
int \fIqlen\fP, ares_callback \fIcallback\fP, void *\fIarg\fP)
.fi
.SH DESCRIPTION
The
.B ares_send
function initiates a DNS query on the name service channel identified
by
.IR channel .
The parameters
.I qbuf
The \fIares_send_dnsrec(3)\fP function initiates a DNS query formatted using the
\fIares_dns_record_t *\fP data structure created via
\fIares_dns_record_create(3)\fP in the
.IR dnsrec
parameter. The supplied callback in the
.IR callback
parameter also returns the response using a
\fIares_dns_record_t *\fP data structure.
The \fIares_send(3)\fP function similarly initiates a DNS query, but instead uses
raw binary buffers with fully formatted DNS messages passed in the request via the
.IR qbuf
and
.I qlen
give the DNS query, which should already have been formatted according
to the DNS protocol. When the query is complete or has failed, the
ares library will invoke
.IR callback .
Completion or failure of the query may happen immediately, or may
happen later as network events are processed.
.PP
.IR qlen
parameters. The supplied callback in the
.IR callback
parameter also returns the raw binary DNS response in the
.IR abuf
and
.IR alen
parameters. This method should be considered deprecated in favor of
\fIares_send_dnsrec(3)\fP.
Both functions take an initialized ares channel identified by
.IR channel .
The \fIares_send_dnsrec(3)\fP also can be supplied an optional output parameter of
.IR qid
to populate the query id as it was placed on the wire.
The \fIares_send_dnsrec(3)\fP function returns an \fIares_status_t\fP response
code. This may be useful to know that the query was enqueued properly. The
response code does not reflect the result of the query, just the result of the
enqueuing of the query.
Completion or failure of the query may happen immediately (even before the
function returning), or may happen later as network events are processed.
When the associated callback is called, it is called with a channel lock so care
must be taken to ensure any processing is minimal to prevent DNS channel stalls.
The callback may be triggered from a different thread than the one which
called \fIares_send(3)\fP.
called \fIares_send_dnsrec(3)\fP or \fIares_send(3)\fP.
For integrators running their own event loops and not using \fBARES_OPT_EVENT_THREAD\fP,
care needs to be taken to ensure any file descriptor lists are updated immediately
within the eventloop when notified.
.PP
The callback argument
.I arg
is copied from the
.B ares_send
argument
.IR arg .
.IR arg
is copied from the \fIares_send_dnsrec(3)\fP or \fIares_send(3)\fP
.IR arg
parameter.
The callback argument
.I status
indicates whether the query succeeded and, if not, how it failed. It
@ -82,43 +114,44 @@ is being destroyed; the query will not be completed.
The query will not be completed because no DNS servers were configured on the
channel.
.PP
The callback argument
.I timeouts
reports how many times a query timed out during the execution of the
given request.
.PP
If the query completed, the callback argument
.I abuf
points to a result buffer of length
.IR alen .
If the query did not complete,
.I abuf
will be NULL and
.I alen
will be 0.
.PP
.IR dnsrec
for \fIares_send_dnsrec(3)\fP or
.IR abuf
and
.IR alen
for \fIares_send(3)\fP will be non-NULL.
Unless the flag
.B ARES_FLAG_NOCHECKRESP
was set at channel initialization time,
.B ares_send
will normally ignore responses whose questions do not match the
questions in
.IR qbuf ,
as well as responses with reply codes of
was set at channel initialization time, \fIares_send_dnsrec(3)\fP and
\fIares_send(3)\fP will normally ignore responses whose questions do not match
the supplied questions, as well as responses with reply codes of
.BR SERVFAIL ,
.BR NOTIMP ,
and
.BR REFUSED .
Unlike other query functions in the ares library, however,
.B ares_send
does not inspect the header of the reply packet to determine the error
status, so a callback status of
\fIares_send_dnsrec(3)\fP and \fIares_send(3)\fP do not inspect the header of
the reply packet to determine the error status, so a callback status of
.B ARES_SUCCESS
does not reflect as much about the response as for other query
functions.
does not reflect as much about the response as for other query functions.
.SH AVAILABILITY
\fBares_send_dnsrec(3)\fP was introduced in c-ares 1.28.0.
.SH SEE ALSO
.BR ares_dns_record_create (3),
.BR ares_process (3),
.BR ares_search (3),
.BR ares_dns_record (3)
.SH AUTHOR
Greg Hudson, MIT Information Systems
.br

@ -0,0 +1,3 @@
.\" Copyright (C) 2023 The c-ares project and its contributors.
.\" SPDX-License-Identifier: MIT
.so man3/ares_send.3

@ -497,10 +497,46 @@ CARES_EXTERN void
CARES_EXTERN void ares_send(ares_channel_t *channel, const unsigned char *qbuf,
int qlen, ares_callback callback, void *arg);
/*! Send a DNS query as an ares_dns_record_t with a callback containing the
* parsed DNS record.
*
* \param[in] channel Pointer to channel on which queries will be sent.
* \param[in] dnsrec DNS Record to send
* \param[in] callback Callback function invoked on completion or failure of
* the query sequence.
* \param[in] arg Additional argument passed to the callback function.
* \param[out] qid Query ID
* \return One of the c-ares status codes.
*/
CARES_EXTERN ares_status_t ares_send_dnsrec(ares_channel_t *channel,
const ares_dns_record_t *dnsrec,
ares_callback_dnsrec callback,
void *arg, unsigned short *qid);
CARES_EXTERN void ares_query(ares_channel_t *channel, const char *name,
int dnsclass, int type, ares_callback callback,
void *arg);
/*! Perform a DNS query with a callback containing the parsed DNS record.
*
* \param[in] channel Pointer to channel on which queries will be sent.
* \param[in] name Query name
* \param[in] dnsclass DNS Class
* \param[in] type DNS Record Type
* \param[in] callback Callback function invoked on completion or failure of
* the query sequence.
* \param[in] arg Additional argument passed to the callback function.
* \param[out] qid Query ID
* \return One of the c-ares status codes.
*/
CARES_EXTERN ares_status_t ares_query_dnsrec(ares_channel_t *channel,
const char *name,
ares_dns_class_t dnsclass,
ares_dns_rec_type_t type,
ares_callback_dnsrec callback,
void *arg,
unsigned short *qid);
CARES_EXTERN void ares_search(ares_channel_t *channel, const char *name,
int dnsclass, int type, ares_callback callback,
void *arg);
@ -512,10 +548,14 @@ CARES_EXTERN void ares_search(ares_channel_t *channel, const char *name,
* \param[in] callback Callback function invoked on completion or failure of
* the query sequence.
* \param[in] arg Additional argument passed to the callback function.
* \return One of the c-ares status codes. In all cases, except
* ARES_EFORMERR due to misuse, this error code will also be sent
* to the provided callback.
*/
CARES_EXTERN void ares_search_dnsrec(ares_channel_t *channel,
ares_dns_record_t *dnsrec,
ares_callback_dnsrec callback, void *arg);
CARES_EXTERN ares_status_t ares_search_dnsrec(ares_channel_t *channel,
ares_dns_record_t *dnsrec,
ares_callback_dnsrec callback,
void *arg);
CARES_EXTERN void ares_gethostbyname(ares_channel_t *channel, const char *name,
int family, ares_host_callback callback,

@ -619,6 +619,36 @@ CARES_EXTERN ares_status_t ares_dns_record_query_add(ares_dns_record_t *dnsrec,
ares_dns_rec_type_t qtype,
ares_dns_class_t qclass);
/*! Replace the question name with a new name. This may be used when performing
* a search with aliases.
*
* Note that this will invalidate the name pointer returned from
* ares_dns_record_query_get().
*
* \param[in] dnsrec Initialized record object
* \param[in] idx Index of question (typically 0)
* \param[in] name Name to use as replacement.
* \return ARES_SUCCESS on success
*/
CARES_EXTERN ares_status_t
ares_dns_record_query_set_name(ares_dns_record_t *dnsrec,
size_t idx,
const char *name);
/*! Replace the question type with a different type. This may be used when
* needing to query more than one address class (e.g. A and AAAA)
*
* \param[in] dnsrec Initialized record object
* \param[in] idx Index of question (typically 0)
* \param[in] qtype Record Type to use as replacement.
* \return ARES_SUCCESS on success
*/
CARES_EXTERN ares_status_t
ares_dns_record_query_set_type(ares_dns_record_t *dnsrec,
size_t idx,
ares_dns_rec_type_t qtype);
/*! Get the count of queries in the DNS Record
*
* \param[in] dnsrec Initialized record object
@ -631,6 +661,8 @@ CARES_EXTERN size_t ares_dns_record_query_cnt(const ares_dns_record_t *dnsrec);
* \param[in] dnsrec Initialized record object
* \param[in] idx Index of query
* \param[out] name Optional. Returns name, may pass NULL if not desired.
* This pointer will be invalided by any call to
* ares_dns_record_query_set_name().
* \param[out] qtype Optional. Returns record type, may pass NULL.
* \param[out] qclass Optional. Returns class, may pass NULL.
* \return ARES_SUCCESS on success
@ -971,6 +1003,17 @@ CARES_EXTERN ares_status_t ares_dns_parse(const unsigned char *buf,
*/
CARES_EXTERN ares_status_t ares_dns_write(const ares_dns_record_t *dnsrec,
unsigned char **buf, size_t *buf_len);
/*! Duplicate a complete DNS message. This does not copy internal members
* (such as the ttl decrement capability).
*
* \param[in] dnsrec Pointer to initialized and filled DNS record object.
* \return duplicted DNS record object, or NULL on out of memory.
*/
CARES_EXTERN ares_dns_record_t *
ares_dns_record_duplicate(const ares_dns_record_t *dnsrec);
/*! @} */
#ifdef __cplusplus

@ -47,13 +47,12 @@
#include "ares.h"
#include "ares_private.h"
ares_status_t ares__parse_into_addrinfo(const unsigned char *abuf, size_t alen,
ares_status_t ares__parse_into_addrinfo(const ares_dns_record_t *dnsrec,
ares_bool_t cname_only_is_enodata,
unsigned short port,
struct ares_addrinfo *ai)
{
ares_status_t status;
ares_dns_record_t *dnsrec = NULL;
size_t i;
size_t ancount;
const char *hostname = NULL;
@ -63,11 +62,6 @@ ares_status_t ares__parse_into_addrinfo(const unsigned char *abuf, size_t alen,
struct ares_addrinfo_cname *cnames = NULL;
struct ares_addrinfo_node *nodes = NULL;
status = ares_dns_parse(abuf, alen, 0, &dnsrec);
if (status != ARES_SUCCESS) {
goto done;
}
/* Save question hostname */
status = ares_dns_record_query_get(dnsrec, 0, &hostname, NULL, NULL);
if (status != ARES_SUCCESS) {
@ -83,7 +77,7 @@ ares_status_t ares__parse_into_addrinfo(const unsigned char *abuf, size_t alen,
for (i = 0; i < ancount; i++) {
ares_dns_rec_type_t rtype;
const ares_dns_rr_t *rr =
ares_dns_record_rr_get(dnsrec, ARES_SECTION_ANSWER, i);
ares_dns_record_rr_get_const(dnsrec, ARES_SECTION_ANSWER, i);
if (ares_dns_rr_get_class(rr) != ARES_CLASS_IN) {
continue;
@ -177,7 +171,6 @@ ares_status_t ares__parse_into_addrinfo(const unsigned char *abuf, size_t alen,
done:
ares__freeaddrinfo_cnames(cnames);
ares__freeaddrinfo_nodes(nodes);
ares_dns_record_destroy(dnsrec);
/* compatibility */
if (status == ARES_EBADNAME) {

@ -74,7 +74,7 @@ void ares_cancel(ares_channel_t *channel)
query->node_all_queries = NULL;
/* NOTE: its possible this may enqueue new queries */
query->callback(query->arg, ARES_ECANCELLED, 0, NULL, 0);
query->callback(query->arg, ARES_ECANCELLED, 0, NULL);
ares__free_query(query);
/* See if the connection should be cleaned up */

@ -51,7 +51,7 @@ void ares_destroy(ares_channel_t *channel)
struct query *query = ares__llist_node_claim(node);
query->node_all_queries = NULL;
query->callback(query->arg, ARES_EDESTRUCTION, 0, NULL, 0);
query->callback(query->arg, ARES_EDESTRUCTION, 0, NULL);
ares__free_query(query);
node = next;

@ -78,20 +78,6 @@ ares_status_t ares_dns_record_create_query(ares_dns_record_t **dnsrec,
ares_status_t ares_dns_query_reply_tostatus(ares_dns_rcode_t rcode,
size_t ancount);
/*! Write a DNS record representing a query for a single name into a buffer.
* An alternative name can be specified to temporarily overwrite the name
* in the query. Note that this only affects the name in the question section;
* RRs are not affected.
*
* \param[in] dnsrec DNS record object to write.
* \param[in] altname Alternative name to use in the query.
* \param[out] buf Buffer to write the query into.
* \param[out] buflen Length of the buffer.
* \return ARES_SUCCESS on success, otherwise an error code.
*/
ares_status_t ares_dns_write_query_altname(ares_dns_record_t *dnsrec,
char *altname, unsigned char **buf,
size_t *buflen);
struct ares_dns_qd {
char *name;
ares_dns_rec_type_t qtype;

@ -276,6 +276,40 @@ ares_status_t ares_dns_record_query_add(ares_dns_record_t *dnsrec,
return ARES_SUCCESS;
}
ares_status_t ares_dns_record_query_set_name(ares_dns_record_t *dnsrec,
size_t idx,
const char *name)
{
char *orig_name = NULL;
if (dnsrec == NULL || idx >= dnsrec->qdcount || name == NULL) {
return ARES_EFORMERR;
}
orig_name = dnsrec->qd[idx].name;
dnsrec->qd[idx].name = ares_strdup(name);
if (dnsrec->qd[idx].name == NULL) {
dnsrec->qd[idx].name = orig_name;
return ARES_ENOMEM;
}
ares_free(orig_name);
return ARES_SUCCESS;
}
ares_status_t ares_dns_record_query_set_type(ares_dns_record_t *dnsrec,
size_t idx,
ares_dns_rec_type_t qtype)
{
if (dnsrec == NULL || idx >= dnsrec->qdcount ||
!ares_dns_rec_type_isvalid(qtype, ARES_TRUE)) {
return ARES_EFORMERR;
}
dnsrec->qd[idx].qtype = qtype;
return ARES_SUCCESS;
}
ares_status_t ares_dns_record_query_get(const ares_dns_record_t *dnsrec,
size_t idx, const char **name,
ares_dns_rec_type_t *qtype,
@ -1392,3 +1426,26 @@ done:
}
return status;
}
ares_dns_record_t *ares_dns_record_duplicate(const ares_dns_record_t *dnsrec)
{
unsigned char *data = NULL;
size_t data_len = 0;
ares_dns_record_t *out = NULL;
ares_status_t status;
if (dnsrec == NULL)
return NULL;
status = ares_dns_write(dnsrec, &data, &data_len);
if (status != ARES_SUCCESS) {
return NULL;
}
status = ares_dns_parse(data, data_len, 0, &out);
ares_free(data);
if (status != ARES_SUCCESS) {
return NULL;
}
return out;
}

@ -1054,28 +1054,3 @@ void ares_dns_record_write_ttl_decrement(ares_dns_record_t *dnsrec,
dnsrec->ttl_decrement = ttl_decrement;
}
/* Write a DNS record representing a query for a single name, but temporarily
* overwrite the name with an alternative name before doing so. This is used
* as a helper function in ares_search(). Note that this only affects the name
* in the question section; RRs are not affected.
*/
ares_status_t ares_dns_write_query_altname(ares_dns_record_t *dnsrec,
char *altname, unsigned char **buf,
size_t *buflen)
{
char *qname;
ares_status_t status;
if (ares_dns_record_query_cnt(dnsrec) != 1) {
return ARES_EBADQUERY;
}
qname = dnsrec->qd[0].name;
if (altname != NULL) {
dnsrec->qd[0].name = altname;
}
status = ares_dns_write(dnsrec, buf, buflen);
dnsrec->qd[0].name = qname;
return status;
}

@ -79,15 +79,20 @@ struct host_query {
char *lookups; /* Duplicate memory from channel because of ares_reinit() */
const char *remaining_lookups; /* types of lookup we need to perform ("fb" by
default, file and dns respectively) */
char **domains; /* duplicate from channel for ares_reinit() safety */
size_t ndomains;
/* Search order for names */
char **names;
size_t names_cnt;
size_t next_name_idx; /* next name index being attempted */
struct ares_addrinfo *ai; /* store results between lookups */
unsigned short qid_a; /* qid for A request */
unsigned short qid_aaaa; /* qid for AAAA request */
size_t remaining; /* number of DNS answers waiting for */
ares_ssize_t next_domain; /* next search domain to try */
size_t
nodata_cnt; /* Track nodata responses to possibly override final result */
size_t remaining; /* number of DNS answers waiting for */
/* Track nodata responses to possibly override final result */
size_t nodata_cnt;
};
static const struct ares_addrinfo_hints default_hints = {
@ -98,10 +103,6 @@ static const struct ares_addrinfo_hints default_hints = {
};
/* forward declarations */
static void host_callback(void *arg, int status, int timeouts,
unsigned char *abuf, int alen);
static ares_bool_t as_is_first(const struct host_query *hquery);
static ares_bool_t as_is_only(const struct host_query *hquery);
static ares_bool_t next_dns_lookup(struct host_query *hquery);
struct ares_addrinfo_cname *
@ -324,6 +325,17 @@ static ares_bool_t fake_addrinfo(const char *name, unsigned short port,
return ARES_TRUE;
}
static void hquery_free(struct host_query *hquery, ares_bool_t cleanup_ai)
{
if (cleanup_ai) {
ares_freeaddrinfo(hquery->ai);
}
ares__strsplit_free(hquery->names, hquery->names_cnt);
ares_free(hquery->name);
ares_free(hquery->lookups);
ares_free(hquery);
}
static void end_hquery(struct host_query *hquery, ares_status_t status)
{
struct ares_addrinfo_node sentinel;
@ -349,10 +361,7 @@ static void end_hquery(struct host_query *hquery, ares_status_t status)
}
hquery->callback(hquery->arg, (int)status, (int)hquery->timeouts, hquery->ai);
ares__strsplit_free(hquery->domains, hquery->ndomains);
ares_free(hquery->lookups);
ares_free(hquery->name);
ares_free(hquery);
hquery_free(hquery, ARES_FALSE);
}
ares_bool_t ares__is_localhost(const char *name)
@ -478,25 +487,23 @@ static void terminate_retries(const struct host_query *hquery,
query->no_retries = ARES_TRUE;
}
static void host_callback(void *arg, int status, int timeouts,
unsigned char *abuf, int alen)
static void host_callback(void *arg, ares_status_t status, size_t timeouts,
const ares_dns_record_t *dnsrec)
{
struct host_query *hquery = (struct host_query *)arg;
ares_status_t addinfostatus = ARES_SUCCESS;
unsigned short qid = 0;
hquery->timeouts += (size_t)timeouts;
hquery->remaining--;
if (status == ARES_SUCCESS) {
if (alen < 0) {
if (dnsrec == NULL) {
addinfostatus = ARES_EBADRESP;
} else {
addinfostatus = ares__parse_into_addrinfo(abuf, (size_t)alen, ARES_TRUE,
addinfostatus = ares__parse_into_addrinfo(dnsrec, ARES_TRUE,
hquery->port, hquery->ai);
}
if (addinfostatus == ARES_SUCCESS && alen >= HFIXEDSZ) {
qid = DNS_HEADER_QID(abuf); /* Converts to host byte order */
terminate_retries(hquery, qid);
if (addinfostatus == ARES_SUCCESS) {
terminate_retries(hquery, ares_dns_record_get_id(dnsrec));
}
}
@ -542,7 +549,6 @@ static void ares_getaddrinfo_int(ares_channel_t *channel, const char *name,
unsigned short port = 0;
int family;
struct ares_addrinfo *ai;
char *alias_name = NULL;
ares_status_t status;
if (!hints) {
@ -563,25 +569,12 @@ static void ares_getaddrinfo_int(ares_channel_t *channel, const char *name,
return;
}
/* perform HOSTALIAS resolution (technically this function does some other
* things we are going to ignore) */
status = ares__single_domain(channel, name, &alias_name);
if (status != ARES_SUCCESS) {
callback(arg, (int)status, 0, NULL);
return;
}
if (alias_name) {
name = alias_name;
}
if (service) {
if (hints->ai_flags & ARES_AI_NUMERICSERV) {
unsigned long val;
errno = 0;
val = strtoul(service, NULL, 0);
if ((val == 0 && errno != 0) || val > 65535) {
ares_free(alias_name);
callback(arg, ARES_ESERVICE, 0, NULL);
return;
}
@ -593,7 +586,6 @@ static void ares_getaddrinfo_int(ares_channel_t *channel, const char *name,
errno = 0;
val = strtoul(service, NULL, 0);
if ((val == 0 && errno != 0) || val > 65535) {
ares_free(alias_name);
callback(arg, ARES_ESERVICE, 0, NULL);
return;
}
@ -604,56 +596,21 @@ static void ares_getaddrinfo_int(ares_channel_t *channel, const char *name,
ai = ares_malloc_zero(sizeof(*ai));
if (!ai) {
ares_free(alias_name);
callback(arg, ARES_ENOMEM, 0, NULL);
return;
}
if (fake_addrinfo(name, port, hints, ai, callback, arg)) {
ares_free(alias_name);
return;
}
/* Allocate and fill in the host query structure. */
hquery = ares_malloc_zero(sizeof(*hquery));
if (!hquery) {
ares_free(alias_name);
ares_freeaddrinfo(ai);
callback(arg, ARES_ENOMEM, 0, NULL);
return;
}
memset(hquery, 0, sizeof(*hquery));
hquery->name = ares_strdup(name);
ares_free(alias_name);
if (!hquery->name) {
ares_free(hquery);
ares_freeaddrinfo(ai);
callback(arg, ARES_ENOMEM, 0, NULL);
return;
}
hquery->lookups = ares_strdup(channel->lookups);
if (!hquery->lookups) {
ares_free(hquery->name);
ares_free(hquery);
ares_freeaddrinfo(ai);
callback(arg, ARES_ENOMEM, 0, NULL);
return;
}
if (channel->ndomains) {
/* Duplicate for ares_reinit() safety */
hquery->domains =
ares__strsplit_duplicate(channel->domains, channel->ndomains);
if (hquery->domains == NULL) {
ares_free(hquery->lookups);
ares_free(hquery->name);
ares_free(hquery);
ares_freeaddrinfo(ai);
callback(arg, ARES_ENOMEM, 0, NULL);
return;
}
hquery->ndomains = channel->ndomains;
}
hquery->port = port;
hquery->channel = channel;
@ -661,9 +618,30 @@ static void ares_getaddrinfo_int(ares_channel_t *channel, const char *name,
hquery->sent_family = -1; /* nothing is sent yet */
hquery->callback = callback;
hquery->arg = arg;
hquery->remaining_lookups = hquery->lookups;
hquery->ai = ai;
hquery->next_domain = -1;
hquery->name = ares_strdup(name);
if (hquery->name == NULL) {
hquery_free(hquery, ARES_TRUE);
callback(arg, ARES_ENOMEM, 0, NULL);
return;
}
status = ares__search_name_list(channel, name, &hquery->names, &hquery->names_cnt);
if (status != ARES_SUCCESS) {
hquery_free(hquery, ARES_TRUE);
callback(arg, (int)status, 0, NULL);
return;
}
hquery->next_name_idx = 0;
hquery->lookups = ares_strdup(channel->lookups);
if (hquery->lookups == NULL) {
hquery_free(hquery, ARES_TRUE);
callback(arg, ARES_ENOMEM, 0, NULL);
return;
}
hquery->remaining_lookups = hquery->lookups;
/* Start performing lookups according to channel->lookups. */
next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */);
@ -684,93 +662,41 @@ void ares_getaddrinfo(ares_channel_t *channel, const char *name,
static ares_bool_t next_dns_lookup(struct host_query *hquery)
{
char *s = NULL;
ares_bool_t is_s_allocated = ARES_FALSE;
ares_status_t status;
/* if next_domain == -1 and as_is_first is true, try hquery->name */
if (hquery->next_domain == -1) {
if (as_is_first(hquery)) {
s = hquery->name;
}
hquery->next_domain = 0;
}
/* if as_is_first is false, try hquery->name at last */
if (!s && (size_t)hquery->next_domain == hquery->ndomains) {
if (!as_is_first(hquery)) {
s = hquery->name;
}
hquery->next_domain++;
}
if (!s && (size_t)hquery->next_domain < hquery->ndomains &&
!as_is_only(hquery)) {
status = ares__cat_domain(hquery->name,
hquery->domains[hquery->next_domain++], &s);
if (status == ARES_SUCCESS) {
is_s_allocated = ARES_TRUE;
}
}
const char *name = NULL;
if (s) {
/* NOTE: hquery may be invalidated during the call to ares_query_qid(),
* so should not be referenced after this point */
switch (hquery->hints.ai_family) {
case AF_INET:
hquery->remaining += 1;
ares_query_qid(hquery->channel, s, C_IN, T_A, host_callback, hquery,
&hquery->qid_a);
break;
case AF_INET6:
hquery->remaining += 1;
ares_query_qid(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery,
&hquery->qid_aaaa);
break;
case AF_UNSPEC:
hquery->remaining += 2;
ares_query_qid(hquery->channel, s, C_IN, T_A, host_callback, hquery,
&hquery->qid_a);
ares_query_qid(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery,
&hquery->qid_aaaa);
break;
default:
break;
}
if (is_s_allocated) {
ares_free(s);
}
return ARES_TRUE;
} else {
assert(!hquery->ai->nodes);
if (hquery->next_name_idx >= hquery->names_cnt) {
return ARES_FALSE;
}
}
static ares_bool_t as_is_first(const struct host_query *hquery)
{
const char *p;
size_t ndots = 0;
for (p = hquery->name; p && *p; p++) {
if (*p == '.') {
ndots++;
}
}
if (as_is_only(hquery)) {
/* prevent ARES_EBADNAME for valid FQDN, where ndots < channel->ndots */
return ARES_TRUE;
}
return ndots >= hquery->channel->ndots ? ARES_TRUE : ARES_FALSE;
}
name = hquery->names[hquery->next_name_idx++];
static ares_bool_t as_is_only(const struct host_query *hquery)
{
size_t nname = ares_strlen(hquery->name);
if (hquery->channel->flags & ARES_FLAG_NOSEARCH) {
return ARES_TRUE;
}
if (hquery->name != NULL && nname && hquery->name[nname - 1] == '.') {
return ARES_TRUE;
/* NOTE: hquery may be invalidated during the call to ares_query_qid(),
* so should not be referenced after this point */
switch (hquery->hints.ai_family) {
case AF_INET:
hquery->remaining += 1;
ares_query_dnsrec(hquery->channel, name, ARES_CLASS_IN,
ARES_REC_TYPE_A, host_callback, hquery,
&hquery->qid_a);
break;
case AF_INET6:
hquery->remaining += 1;
ares_query_dnsrec(hquery->channel, name, ARES_CLASS_IN,
ARES_REC_TYPE_AAAA, host_callback, hquery,
&hquery->qid_aaaa);
break;
case AF_UNSPEC:
hquery->remaining += 2;
ares_query_dnsrec(hquery->channel, name, ARES_CLASS_IN,
ARES_REC_TYPE_A, host_callback, hquery,
&hquery->qid_a);
ares_query_dnsrec(hquery->channel, name, ARES_CLASS_IN,
ARES_REC_TYPE_AAAA, host_callback, hquery,
&hquery->qid_aaaa);
break;
default:
break;
}
return ARES_FALSE;
return ARES_TRUE;
}

@ -60,8 +60,9 @@ struct addr_query {
};
static void next_lookup(struct addr_query *aquery);
static void addr_callback(void *arg, int status, int timeouts,
unsigned char *abuf, int alen);
static void addr_callback(void *arg, ares_status_t status,
size_t timeouts,
const ares_dns_record_t *dnsrec);
static void end_aquery(struct addr_query *aquery, ares_status_t status,
struct hostent *host);
static ares_status_t file_lookup(ares_channel_t *channel,
@ -138,7 +139,8 @@ static void next_lookup(struct addr_query *aquery)
return;
}
aquery->remaining_lookups = p + 1;
ares_query(aquery->channel, name, C_IN, T_PTR, addr_callback, aquery);
ares_query_dnsrec(aquery->channel, name, ARES_CLASS_IN,
ARES_REC_TYPE_PTR, addr_callback, aquery, NULL);
ares_free(name);
return;
case 'f':
@ -159,8 +161,8 @@ static void next_lookup(struct addr_query *aquery)
end_aquery(aquery, ARES_ENOTFOUND, NULL);
}
static void addr_callback(void *arg, int status, int timeouts,
unsigned char *abuf, int alen)
static void addr_callback(void *arg, ares_status_t status, size_t timeouts,
const ares_dns_record_t *dnsrec)
{
struct addr_query *aquery = (struct addr_query *)arg;
struct hostent *host;
@ -170,12 +172,12 @@ static void addr_callback(void *arg, int status, int timeouts,
if (status == ARES_SUCCESS) {
if (aquery->addr.family == AF_INET) {
addrlen = sizeof(aquery->addr.addr.addr4);
status = ares_parse_ptr_reply(abuf, alen, &aquery->addr.addr.addr4,
(int)addrlen, AF_INET, &host);
status = ares_parse_ptr_reply_dnsrec(dnsrec, &aquery->addr.addr.addr4,
(int)addrlen, AF_INET, &host);
} else {
addrlen = sizeof(aquery->addr.addr.addr6);
status = ares_parse_ptr_reply(abuf, alen, &aquery->addr.addr.addr6,
(int)addrlen, AF_INET6, &host);
status = ares_parse_ptr_reply_dnsrec(dnsrec, &aquery->addr.addr.addr6,
(int)addrlen, AF_INET6, &host);
}
end_aquery(aquery, (ares_status_t)status, host);
} else if (status == ARES_EDESTRUCTION || status == ARES_ECANCELLED) {

@ -58,7 +58,8 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen,
struct ares_addrinfo ai;
char *question_hostname = NULL;
ares_status_t status;
size_t req_naddrttls = 0;
size_t req_naddrttls = 0;
ares_dns_record_t *dnsrec = NULL;
if (alen < 0) {
return ARES_EBADRESP;
@ -71,7 +72,12 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen,
memset(&ai, 0, sizeof(ai));
status = ares__parse_into_addrinfo(abuf, (size_t)alen, 0, 0, &ai);
status = ares_dns_parse(abuf, (size_t)alen, 0, &dnsrec);
if (status != ARES_SUCCESS) {
goto fail;
}
status = ares__parse_into_addrinfo(dnsrec, 0, 0, &ai);
if (status != ARES_SUCCESS && status != ARES_ENODATA) {
goto fail;
}
@ -96,6 +102,11 @@ fail:
ares__freeaddrinfo_nodes(ai.nodes);
ares_free(ai.name);
ares_free(question_hostname);
ares_dns_record_destroy(dnsrec);
if (status == ARES_EBADNAME) {
status = ARES_EBADRESP;
}
return (int)status;
}

@ -60,7 +60,8 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
struct ares_addrinfo ai;
char *question_hostname = NULL;
ares_status_t status;
size_t req_naddrttls = 0;
size_t req_naddrttls = 0;
ares_dns_record_t *dnsrec = NULL;
if (alen < 0) {
return ARES_EBADRESP;
@ -73,7 +74,12 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
memset(&ai, 0, sizeof(ai));
status = ares__parse_into_addrinfo(abuf, (size_t)alen, 0, 0, &ai);
status = ares_dns_parse(abuf, (size_t)alen, 0, &dnsrec);
if (status != ARES_SUCCESS) {
goto fail;
}
status = ares__parse_into_addrinfo(dnsrec, 0, 0, &ai);
if (status != ARES_SUCCESS && status != ARES_ENODATA) {
goto fail;
}
@ -97,6 +103,11 @@ fail:
ares__freeaddrinfo_nodes(ai.nodes);
ares_free(question_hostname);
ares_free(ai.name);
ares_dns_record_destroy(dnsrec);
if (status == ARES_EBADNAME) {
status = ARES_EBADRESP;
}
return (int)status;
}

@ -36,33 +36,21 @@
#include "ares.h"
#include "ares_private.h"
int ares_parse_ptr_reply(const unsigned char *abuf, int alen_int,
const void *addr, int addrlen, int family,
struct hostent **host)
ares_status_t ares_parse_ptr_reply_dnsrec(const ares_dns_record_t *dnsrec,
const void *addr, int addrlen,
int family,
struct hostent **host)
{
ares_status_t status;
size_t alen;
size_t ptrcount = 0;
struct hostent *hostent = NULL;
const char *hostname = NULL;
const char *ptrname = NULL;
ares_dns_record_t *dnsrec = NULL;
size_t i;
size_t ancount;
*host = NULL;
if (alen_int < 0) {
return ARES_EBADRESP;
}
alen = (size_t)alen_int;
status = ares_dns_parse(abuf, alen, 0, &dnsrec);
if (status != ARES_SUCCESS) {
goto done;
}
/* Fetch name from query as we will use it to compare later on. Old code
* did this check, so we'll retain it. */
status = ares_dns_record_query_get(dnsrec, 0, &ptrname, NULL, NULL);
@ -114,7 +102,7 @@ int ares_parse_ptr_reply(const unsigned char *abuf, int alen_int,
/* Cycle through answers */
for (i = 0; i < ancount; i++) {
const ares_dns_rr_t *rr =
ares_dns_record_rr_get(dnsrec, ARES_SECTION_ANSWER, i);
ares_dns_record_rr_get_const(dnsrec, ARES_SECTION_ANSWER, i);
if (rr == NULL) {
/* Shouldn't be possible */
@ -195,6 +183,35 @@ done:
} else {
*host = hostent;
}
return status;
}
int ares_parse_ptr_reply(const unsigned char *abuf, int alen_int,
const void *addr, int addrlen, int family,
struct hostent **host)
{
size_t alen;
ares_dns_record_t *dnsrec = NULL;
ares_status_t status;
if (alen_int < 0) {
return ARES_EBADRESP;
}
alen = (size_t)alen_int;
status = ares_dns_parse(abuf, alen, 0, &dnsrec);
if (status != ARES_SUCCESS) {
goto done;
}
status = ares_parse_ptr_reply_dnsrec(dnsrec, addr, addrlen, family, host);
done:
ares_dns_record_destroy(dnsrec);
if (status == ARES_EBADNAME) {
status = ARES_EBADRESP;
}
return (int)status;
}

@ -209,7 +209,7 @@ struct query {
unsigned char *qbuf;
size_t qlen;
ares_callback callback;
ares_callback_dnsrec callback;
void *arg;
/* Query status */
@ -335,17 +335,32 @@ ares_bool_t ares__timedout(const struct timeval *now,
ares_status_t ares__send_query(struct query *query, struct timeval *now);
ares_status_t ares__requeue_query(struct query *query, struct timeval *now);
/* Identical to ares_query, but returns a normal ares return code like
* ARES_SUCCESS, and can be passed the qid by reference which will be
* filled in on ARES_SUCCESS */
ares_status_t ares_query_qid(ares_channel_t *channel, const char *name,
int dnsclass, int type, ares_callback callback,
void *arg, unsigned short *qid);
/* 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,
unsigned short *qid);
/*! Retrieve a list of names to use for searching. The first successful
* query in the list wins. This function also uses the HOSTSALIASES file
* as well as uses channel configuration to determine the search order.
*
* \param[in] channel initialized ares channel
* \param[in] name initial name being searched
* \param[out] names array of names to attempt, use ares__strsplit_free()
* when no longer needed.
* \param[out] names_len number of names in array
* \return ARES_SUCCESS on success, otherwise one of the other error codes.
*/
ares_status_t ares__search_name_list(const ares_channel_t *channel,
const char *name,
char ***names, size_t *names_len);
/*! Function to create callback arg for converting from ares_callback_dnsrec
* to ares_calback */
void *ares__dnsrec_convert_arg(ares_callback callback, void *arg);
/*! Callback function used to convert from the ares_callback_dnsrec prototype to
* the ares_callback prototype, by writing the result and passing that to
* the inner callback.
*/
void ares__dnsrec_convert_cb(void *arg, ares_status_t status, size_t timeouts,
const ares_dns_record_t *dnsrec);
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,
@ -401,8 +416,13 @@ ares_status_t ares__parse_sortlist(struct apattern **sortlist, size_t *nsort,
const char *str);
void ares__destroy_servers_state(ares_channel_t *channel);
ares_status_t ares__single_domain(const ares_channel_t *channel,
const char *name, char **s);
/* Returns ARES_SUCCESS if alias found, alias is set. Returns ARES_ENOTFOUND
* if not alias found. Returns other errors on critical failure like
* ARES_ENOMEM */
ares_status_t ares__lookup_hostaliases(const ares_channel_t *channel,
const char *name, char **alias);
ares_status_t ares__cat_domain(const char *name, const char *domain, char **s);
ares_status_t ares__sortaddrinfo(ares_channel_t *channel,
struct ares_addrinfo_node *ai_node);
@ -427,10 +447,14 @@ ares_status_t ares_append_ai_node(int aftype, unsigned short port,
void ares__addrinfo_cat_cnames(struct ares_addrinfo_cname **head,
struct ares_addrinfo_cname *tail);
ares_status_t ares__parse_into_addrinfo(const unsigned char *abuf, size_t alen,
ares_status_t ares__parse_into_addrinfo(const ares_dns_record_t *dnsrec,
ares_bool_t cname_only_is_enodata,
unsigned short port,
struct ares_addrinfo *ai);
ares_status_t ares_parse_ptr_reply_dnsrec(const ares_dns_record_t *dnsrec,
const void *addr, int addrlen,
int family,
struct hostent **host);
ares_status_t ares__addrinfo2hostent(const struct ares_addrinfo *ai, int family,
struct hostent **host);
@ -583,8 +607,8 @@ ares_status_t ares_qcache_insert(ares_channel_t *channel,
ares_dns_record_t *dnsrec);
ares_status_t ares_qcache_fetch(ares_channel_t *channel,
const struct timeval *now,
const unsigned char *qbuf, size_t qlen,
unsigned char **abuf, size_t *alen);
const ares_dns_record_t *dnsrec,
const ares_dns_record_t **dnsrec_resp);
ares_status_t ares__channel_threading_init(ares_channel_t *channel);
void ares__channel_threading_destroy(ares_channel_t *channel);

@ -68,8 +68,8 @@ static ares_bool_t same_questions(const ares_dns_record_t *qrec,
static ares_bool_t same_address(const struct sockaddr *sa,
const struct ares_addr *aa);
static void end_query(ares_channel_t *channel, struct query *query,
ares_status_t status, const unsigned char *abuf,
size_t alen);
ares_status_t status,
const ares_dns_record_t *dnsrec);
static void server_increment_failures(struct server_state *server)
{
@ -621,10 +621,11 @@ static ares_status_t process_answer(ares_channel_t *channel,
struct query *query;
/* Cache these as once ares__send_query() gets called, it may end up
* invalidating the connection all-together */
struct server_state *server = conn->server;
ares_dns_record_t *rdnsrec = NULL;
ares_dns_record_t *qdnsrec = NULL;
struct server_state *server = conn->server;
ares_dns_record_t *rdnsrec = NULL;
ares_dns_record_t *qdnsrec = NULL;
ares_status_t status;
ares_bool_t is_cached = ARES_FALSE;
/* Parse the response */
status = ares_dns_parse(abuf, alen, 0, &rdnsrec);
@ -648,7 +649,7 @@ static ares_status_t process_answer(ares_channel_t *channel,
/* Parse the question we sent as we use it to compare */
status = ares_dns_parse(query->qbuf, query->qlen, 0, &qdnsrec);
if (status != ARES_SUCCESS) {
end_query(channel, query, status, NULL, 0);
end_query(channel, query, status, NULL);
goto cleanup;
}
@ -674,7 +675,7 @@ static ares_status_t process_answer(ares_channel_t *channel,
ares_dns_has_opt_rr(qdnsrec) && !ares_dns_has_opt_rr(rdnsrec)) {
status = rewrite_without_edns(qdnsrec, query);
if (status != ARES_SUCCESS) {
end_query(channel, query, status, NULL, 0);
end_query(channel, query, status, NULL);
goto cleanup;
}
@ -729,16 +730,20 @@ static ares_status_t process_answer(ares_channel_t *channel,
/* If cache insertion was successful, it took ownership. We ignore
* other cache insertion failures. */
if (ares_qcache_insert(channel, now, query, rdnsrec) == ARES_SUCCESS) {
rdnsrec = NULL;
is_cached = ARES_TRUE;
}
server_set_good(server);
end_query(channel, query, ARES_SUCCESS, abuf, alen);
end_query(channel, query, ARES_SUCCESS, rdnsrec);
status = ARES_SUCCESS;
cleanup:
ares_dns_record_destroy(rdnsrec);
/* Don't cleanup the cached pointer to the dns response */
if (!is_cached) {
ares_dns_record_destroy(rdnsrec);
}
ares_dns_record_destroy(qdnsrec);
return status;
}
@ -774,7 +779,7 @@ ares_status_t ares__requeue_query(struct query *query, struct timeval *now)
query->error_status = ARES_ETIMEOUT;
}
end_query(channel, query, query->error_status, NULL, 0);
end_query(channel, query, query->error_status, NULL);
return ARES_ETIMEOUT;
}
@ -893,7 +898,7 @@ ares_status_t ares__send_query(struct query *query, struct timeval *now)
}
if (server == NULL) {
end_query(channel, query, ARES_ENOSERVER /* ? */, NULL, 0);
end_query(channel, query, ARES_ENOSERVER /* ? */, NULL);
return ARES_ENOSERVER;
}
@ -920,7 +925,7 @@ ares_status_t ares__send_query(struct query *query, struct timeval *now)
/* Anything else is not retryable, likely ENOMEM */
default:
end_query(channel, query, status, NULL, 0);
end_query(channel, query, status, NULL);
return status;
}
}
@ -931,7 +936,7 @@ ares_status_t ares__send_query(struct query *query, struct timeval *now)
status = ares__append_tcpbuf(server, query);
if (status != ARES_SUCCESS) {
end_query(channel, query, status, NULL, 0);
end_query(channel, query, status, NULL);
/* Only safe to kill connection if it was new, otherwise it should be
* cleaned up by another process later */
@ -979,7 +984,7 @@ ares_status_t ares__send_query(struct query *query, struct timeval *now)
/* Anything else is not retryable, likely ENOMEM */
default:
end_query(channel, query, status, NULL, 0);
end_query(channel, query, status, NULL);
return status;
}
node = ares__llist_node_first(server->connections);
@ -1011,7 +1016,7 @@ ares_status_t ares__send_query(struct query *query, struct timeval *now)
query->node_queries_by_timeout =
ares__slist_insert(channel->queries_by_timeout, query);
if (!query->node_queries_by_timeout) {
end_query(channel, query, ARES_ENOMEM, NULL, 0);
end_query(channel, query, ARES_ENOMEM, NULL);
/* Only safe to kill connection if it was new, otherwise it should be
* cleaned up by another process later */
if (new_connection) {
@ -1027,7 +1032,7 @@ ares_status_t ares__send_query(struct query *query, struct timeval *now)
ares__llist_insert_last(conn->queries_to_conn, query);
if (query->node_queries_to_conn == NULL) {
end_query(channel, query, ARES_ENOMEM, NULL, 0);
end_query(channel, query, ARES_ENOMEM, NULL);
/* Only safe to kill connection if it was new, otherwise it should be
* cleaned up by another process later */
if (new_connection) {
@ -1124,14 +1129,10 @@ static void ares_detach_query(struct query *query)
}
static void end_query(ares_channel_t *channel, struct query *query,
ares_status_t status, const unsigned char *abuf,
size_t alen)
ares_status_t status, const ares_dns_record_t *dnsrec)
{
/* Invoke the callback. */
query->callback(query->arg, (int)status, (int)query->timeouts,
/* due to prior design flaws, abuf isn't meant to be modified,
* but bad prototypes, ugh. Lets cast off constfor compat. */
(unsigned char *)((void *)((size_t)abuf)), (int)alen);
query->callback(query->arg, status, query->timeouts, dnsrec);
ares__free_query(query);
/* Check and notify if no other queries are enqueued on the channel. This

@ -81,6 +81,7 @@ static char *ares__qcache_calc_key(const ares_dns_record_t *dnsrec)
for (i = 0; i < ares_dns_record_query_cnt(dnsrec); i++) {
const char *name;
size_t name_len;
ares_dns_rec_type_t qtype;
ares_dns_class_t qclass;
@ -114,7 +115,15 @@ static char *ares__qcache_calc_key(const ares_dns_record_t *dnsrec)
goto fail;
}
status = ares__buf_append_str(buf, name);
/* On queries, a '.' may be appended to the name to indicate an explicit
* name lookup without performing a search. Strip this since its not part
* of a cached response. */
name_len = ares_strlen(name);
if (name_len && name[name_len-1] == '.') {
name_len--;
}
status = ares__buf_append(buf, (const unsigned char *)name, name_len);
if (status != ARES_SUCCESS) {
goto fail;
}
@ -384,20 +393,24 @@ fail:
return ARES_ENOMEM;
}
static ares_status_t ares__qcache_fetch(ares__qcache_t *qcache,
const ares_dns_record_t *dnsrec,
const struct timeval *now,
unsigned char **buf, size_t *buf_len)
ares_status_t ares_qcache_fetch(ares_channel_t *channel,
const struct timeval *now,
const ares_dns_record_t *dnsrec,
const ares_dns_record_t **dnsrec_resp)
{
char *key = NULL;
ares__qcache_entry_t *entry;
ares_status_t status;
ares_status_t status = ARES_SUCCESS;
if (qcache == NULL || dnsrec == NULL) {
if (channel == NULL || dnsrec == NULL || dnsrec_resp == NULL) {
return ARES_EFORMERR;
}
ares__qcache_expire(qcache, now);
if (channel->qcache == NULL) {
return ARES_ENOTFOUND;
}
ares__qcache_expire(channel->qcache, now);
key = ares__qcache_calc_key(dnsrec);
if (key == NULL) {
@ -405,7 +418,7 @@ static ares_status_t ares__qcache_fetch(ares__qcache_t *qcache,
goto done;
}
entry = ares__htable_strvp_get_direct(qcache->cache, key);
entry = ares__htable_strvp_get_direct(channel->qcache->cache, key);
if (entry == NULL) {
status = ARES_ENOTFOUND;
goto done;
@ -414,7 +427,7 @@ static ares_status_t ares__qcache_fetch(ares__qcache_t *qcache,
ares_dns_record_write_ttl_decrement(
entry->dnsrec, (unsigned int)(now->tv_sec - entry->insert_ts));
status = ares_dns_write(entry->dnsrec, buf, buf_len);
*dnsrec_resp = entry->dnsrec;
done:
ares_free(key);
@ -430,26 +443,3 @@ ares_status_t ares_qcache_insert(ares_channel_t *channel,
now);
}
ares_status_t ares_qcache_fetch(ares_channel_t *channel,
const struct timeval *now,
const unsigned char *qbuf, size_t qlen,
unsigned char **abuf, size_t *alen)
{
ares_status_t status;
ares_dns_record_t *dnsrec = NULL;
if (channel->qcache == NULL) {
return ARES_ENOTFOUND;
}
status = ares_dns_parse(qbuf, qlen, 0, &dnsrec);
if (status != ARES_SUCCESS) {
goto done;
}
status = ares__qcache_fetch(channel->qcache, dnsrec, now, abuf, alen);
done:
ares_dns_record_destroy(dnsrec);
return status;
}

@ -37,89 +37,121 @@
#include "ares_dns.h"
#include "ares_private.h"
struct qquery {
ares_callback callback;
void *arg;
};
typedef struct {
ares_callback_dnsrec callback;
void *arg;
} ares_query_dnsrec_arg_t;
static void qcallback(void *arg, int status, int timeouts, unsigned char *abuf,
int alen);
ares_status_t ares_query_qid(ares_channel_t *channel, const char *name,
int dnsclass, int type, ares_callback callback,
void *arg, unsigned short *qid)
static void ares_query_dnsrec_cb(void *arg, ares_status_t status,
size_t timeouts,
const ares_dns_record_t *dnsrec)
{
struct qquery *qquery;
unsigned char *qbuf;
int qlen;
int rd;
ares_status_t status;
/* Compose the query. */
rd = !(channel->flags & ARES_FLAG_NORECURSE);
status = (ares_status_t)ares_create_query(
name, dnsclass, type, 0, rd, &qbuf, &qlen,
(channel->flags & ARES_FLAG_EDNS) ? (int)channel->ednspsz : 0);
ares_query_dnsrec_arg_t *qquery = arg;
if (status != ARES_SUCCESS) {
if (qbuf != NULL) {
ares_free(qbuf);
qquery->callback(qquery->arg, status, timeouts, dnsrec);
} else {
size_t ancount;
ares_dns_rcode_t rcode;
/* Pull the response code and answer count from the packet and convert any
* errors.
*/
rcode = ares_dns_record_get_rcode(dnsrec);
ancount = ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER);
status = ares_dns_query_reply_tostatus(rcode, ancount);
qquery->callback(qquery->arg, status, timeouts, dnsrec);
}
ares_free(qquery);
}
static ares_status_t ares_query_int(ares_channel_t *channel, const char *name,
ares_dns_class_t dnsclass,
ares_dns_rec_type_t type,
ares_callback_dnsrec callback,
void *arg, unsigned short *qid)
{
ares_status_t status;
ares_dns_record_t *dnsrec = NULL;
ares_dns_flags_t flags = 0;
ares_query_dnsrec_arg_t *qquery = NULL;
if (channel == NULL || name == NULL || callback == NULL) {
status = ARES_EFORMERR;
if (callback != NULL) {
callback(arg, status, 0, NULL);
}
callback(arg, (int)status, 0, NULL, 0);
return status;
}
/* Allocate and fill in the query structure. */
qquery = ares_malloc(sizeof(struct qquery));
if (!qquery) {
ares_free_string(qbuf);
callback(arg, ARES_ENOMEM, 0, NULL, 0);
return ARES_ENOMEM;
if (!(channel->flags & ARES_FLAG_NORECURSE)) {
flags |= ARES_FLAG_RD;
}
status = ares_dns_record_create_query(&dnsrec, name,
(ares_dns_class_t)dnsclass,
(ares_dns_rec_type_t)type,
0, flags,
(size_t)(channel->flags & ARES_FLAG_EDNS)?channel->ednspsz : 0);
if (status != ARES_SUCCESS) {
callback(arg, status, 0, NULL);
return status;
}
qquery = ares_malloc(sizeof(*qquery));
if (qquery == NULL) {
status = ARES_ENOMEM;
callback(arg, status, 0, NULL);
ares_dns_record_destroy(dnsrec);
return status;
}
qquery->callback = callback;
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, qid);
ares_free_string(qbuf);
status = ares_send_dnsrec(channel, dnsrec, ares_query_dnsrec_cb, qquery, qid);
ares_dns_record_destroy(dnsrec);
return status;
}
void ares_query(ares_channel_t *channel, const char *name, int dnsclass,
int type, ares_callback callback, void *arg)
ares_status_t ares_query_dnsrec(ares_channel_t *channel, const char *name,
ares_dns_class_t dnsclass,
ares_dns_rec_type_t type,
ares_callback_dnsrec callback,
void *arg, unsigned short *qid)
{
ares_status_t status;
if (channel == NULL) {
return;
return ARES_EFORMERR;
}
ares__channel_lock(channel);
ares_query_qid(channel, name, dnsclass, type, callback, arg, NULL);
status = ares_query_int(channel, name, dnsclass, type, callback, arg, qid);
ares__channel_unlock(channel);
return status;
}
static void qcallback(void *arg, int status, int timeouts, unsigned char *abuf,
int alen)
void ares_query(ares_channel_t *channel, const char *name, int dnsclass,
int type, ares_callback callback, void *arg)
{
struct qquery *qquery = (struct qquery *)arg;
ares_dns_record_t *dnsrep = NULL;
size_t ancount;
ares_dns_rcode_t rcode;
void *carg = NULL;
if (status != ARES_SUCCESS) {
qquery->callback(qquery->arg, status, timeouts, abuf, alen);
} else {
/* Pull the response code and answer count from the packet and convert any
* errors.
*/
status = (int)ares_dns_parse(abuf, (size_t)alen, 0, &dnsrep);
if (status != ARES_SUCCESS) {
qquery->callback(qquery->arg, status, timeouts, abuf, alen);
} else {
rcode = ares_dns_record_get_rcode(dnsrep);
ancount = ares_dns_record_rr_cnt(dnsrep, ARES_SECTION_ANSWER);
ares_dns_record_destroy(dnsrep);
status = (int)ares_dns_query_reply_tostatus(rcode, ancount);
qquery->callback(qquery->arg, status, timeouts, abuf, alen);
}
if (channel == NULL) {
return;
}
ares_free(qquery);
carg = ares__dnsrec_convert_arg(callback, arg);
if (carg == NULL) {
callback(arg, ARES_ENOMEM, 0, NULL, 0);
return;
}
ares_query_dnsrec(channel, name, (ares_dns_class_t)dnsclass,
(ares_dns_rec_type_t)type, ares__dnsrec_convert_cb, carg,
NULL);
}

@ -36,205 +36,382 @@
#include "ares_dns.h"
struct search_query {
/* Arguments passed to ares_search() */
ares_channel_t *channel;
ares_callback callback;
void *arg;
/* Arguments passed to ares_search_dnsrec() */
ares_channel_t *channel;
ares_callback_dnsrec callback;
void *arg;
/* DNS record passed to ares_search(), encoded in string format */
unsigned char *buf;
size_t buflen;
/* Duplicate of DNS record passed to ares_search_dnsrec() */
ares_dns_record_t *dnsrec;
/* Duplicate of channel domains for ares_reinit() safety */
char **domains;
size_t ndomains;
/* Search order for names */
char **names;
size_t names_cnt;
/* State tracking progress through the search query */
int status_as_is; /* error status from trying as-is */
size_t next_domain; /* next search domain to try */
ares_bool_t trying_as_is; /* current query is for name as-is */
size_t next_name_idx; /* next name index being attempted */
size_t timeouts; /* number of timeouts we saw for this request */
ares_bool_t ever_got_nodata; /* did we ever get ARES_ENODATA along the way? */
};
/* Callback argument structure passed to ares__dnsrec_convert_cb(). */
struct dnsrec_convert_arg {
ares_callback_dnsrec callback;
void *arg;
};
static void squery_free(struct search_query *squery)
{
if (squery == NULL)
return;
ares__strsplit_free(squery->names, squery->names_cnt);
ares_dns_record_destroy(squery->dnsrec);
ares_free(squery);
}
static void search_callback(void *arg, int status, int timeouts,
unsigned char *abuf, int alen);
static ares_status_t ares__write_and_send_query(ares_channel_t *channel,
ares_dns_record_t *dnsrec,
char *altname,
ares_callback callback,
void *arg);
/* End a search query by invoking the user callback and freeing the
* search_query structure.
*/
static void end_squery(struct search_query *squery, ares_status_t status,
unsigned char *abuf, size_t alen);
static void ares__dnsrec_convert_cb(void *arg, int status, int timeouts,
unsigned char *abuf, int alen);
const ares_dns_record_t *dnsrec)
{
squery->callback(squery->arg, status, squery->timeouts, dnsrec);
squery_free(squery);
}
static void search_callback(void *arg, ares_status_t status, size_t timeouts,
const ares_dns_record_t *dnsrec);
static void ares_search_int(ares_channel_t *channel, ares_dns_record_t *dnsrec,
ares_callback callback, void *arg)
static ares_status_t ares_search_next(ares_channel_t *channel,
struct search_query *squery,
ares_bool_t *skip_cleanup)
{
struct search_query *squery;
const char *name;
char *s = NULL;
const char *p;
ares_status_t status;
size_t ndots;
ares_status_t status;
/* Extract the name for the search. Note that searches are only supported for
* DNS records containing a single query.
*/
if (ares_dns_record_query_cnt(dnsrec) != 1) {
callback(arg, ARES_EBADQUERY, 0, NULL, 0);
return;
*skip_cleanup = ARES_FALSE;
/* Misuse check */
if (squery->next_name_idx >= squery->names_cnt) {
return ARES_EFORMERR;
}
status = ares_dns_record_query_get(dnsrec, 0, &name, NULL, NULL);
status =
ares_dns_record_query_set_name(squery->dnsrec, 0,
squery->names[squery->next_name_idx++]);
if (status != ARES_SUCCESS) {
callback(arg, (int)status, 0, NULL, 0);
return;
return status;
}
/* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
if (ares__is_onion_domain(name)) {
callback(arg, ARES_ENOTFOUND, 0, NULL, 0);
return;
status = ares_send_dnsrec(channel, squery->dnsrec, search_callback, squery,
NULL);
if (status != ARES_EFORMERR) {
*skip_cleanup = ARES_TRUE;
}
/* If name only yields one domain to search, then we don't have
* to keep extra state, so just do an ares_send().
*/
status = ares__single_domain(channel, name, &s);
return status;
}
static void search_callback(void *arg, ares_status_t status, size_t timeouts,
const ares_dns_record_t *dnsrec)
{
struct search_query *squery = (struct search_query *)arg;
ares_channel_t *channel = squery->channel;
ares_dns_rcode_t rcode;
size_t ancount;
ares_status_t mystatus;
ares_bool_t skip_cleanup = ARES_FALSE;
squery->timeouts += (size_t)timeouts;
if (status != ARES_SUCCESS) {
callback(arg, (int)status, 0, NULL, 0);
end_squery(squery, status, dnsrec);
return;
} else if (s != NULL) {
/* We only have a single domain to search, so do it here. */
status = ares__write_and_send_query(channel, dnsrec, s, callback, arg);
ares_free(s);
if (status != ARES_SUCCESS) {
callback(arg, (int)status, 0, NULL, 0);
}
}
rcode = ares_dns_record_get_rcode(dnsrec);
ancount = ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER);
mystatus = ares_dns_query_reply_tostatus(rcode, ancount);
if (mystatus != ARES_ENODATA && mystatus != ARES_ESERVFAIL &&
mystatus != ARES_ENOTFOUND) {
end_squery(squery, mystatus, dnsrec);
return;
}
/* Allocate a search_query structure to hold the state necessary for
* doing multiple lookups.
/* If we ever get ARES_ENODATA along the way, record that; if the search
* should run to the very end and we got at least one ARES_ENODATA,
* then callers like ares_gethostbyname() may want to try a T_A search
* even if the last domain we queried for T_AAAA resource records
* returned ARES_ENOTFOUND.
*/
squery = ares_malloc_zero(sizeof(*squery));
if (!squery) {
callback(arg, ARES_ENOMEM, 0, NULL, 0);
if (mystatus == ARES_ENODATA) {
squery->ever_got_nodata = ARES_TRUE;
}
if (squery->next_name_idx < squery->names_cnt) {
mystatus = ares_search_next(channel, squery, &skip_cleanup);
if (mystatus != ARES_SUCCESS && !skip_cleanup) {
end_squery(squery, mystatus, NULL);
}
return;
}
squery->channel = channel;
/* We pass the DNS record through the search_query structure by encoding it
* into a buffer and then later decoding it back.
*/
status = ares_dns_write(dnsrec, &squery->buf, &squery->buflen);
if (status != ARES_SUCCESS) {
ares_free(squery);
callback(arg, (int)status, 0, NULL, 0);
/* We have no more domains to search, return an appropriate response. */
if (mystatus == ARES_ENOTFOUND && squery->ever_got_nodata) {
end_squery(squery, ARES_ENODATA, NULL);
return;
}
/* Duplicate domains for safety during ares_reinit() */
if (channel->ndomains) {
squery->domains =
ares__strsplit_duplicate(channel->domains, channel->ndomains);
if (squery->domains == NULL) {
ares_free(squery->buf);
ares_free(squery);
callback(arg, ARES_ENOMEM, 0, NULL, 0);
return;
end_squery(squery, mystatus, NULL);
}
/* Determine if the domain should be looked up as-is, or if it is eligible
* for search by appending domains */
static ares_bool_t ares__search_eligible(const ares_channel_t *channel,
const char *name)
{
size_t len = ares_strlen(name);
/* Name ends in '.', cannot search */
if (len && name[len-1] == '.') {
return ARES_FALSE;
}
if (channel->flags & ARES_FLAG_NOSEARCH) {
return ARES_FALSE;
}
return ARES_TRUE;
}
ares_status_t ares__search_name_list(const ares_channel_t *channel,
const char *name,
char ***names, size_t *names_len)
{
ares_status_t status;
char **list = NULL;
size_t list_len = 0;
char *alias = NULL;
size_t ndots = 0;
size_t idx = 0;
const char *p;
size_t i;
/* Perform HOSTALIASES resolution */
status = ares__lookup_hostaliases(channel, name, &alias);
if (status == ARES_SUCCESS) {
/* If hostalias succeeds, there is no searching, it is used as-is */
list_len = 1;
list = ares_malloc_zero(sizeof(*list) * list_len);
if (list == NULL) {
status = ARES_ENOMEM;
goto done;
}
squery->ndomains = channel->ndomains;
list[0] = alias;
alias = NULL;
goto done;
} else if (status != ARES_ENOTFOUND) {
goto done;
}
squery->status_as_is = -1;
squery->callback = callback;
squery->arg = arg;
squery->timeouts = 0;
squery->ever_got_nodata = ARES_FALSE;
/* See if searching is eligible at all, if not, look up as-is only */
if (!ares__search_eligible(channel, name)) {
list_len = 1;
list = ares_malloc_zero(sizeof(*list) * list_len);
if (list == NULL) {
status = ARES_ENOMEM;
goto done;
}
list[0] = ares_strdup(name);
if (list[0] == NULL) {
status = ARES_ENOMEM;
} else {
status = ARES_SUCCESS;
}
goto done;
}
/* Count the number of dots in name. */
/* Count the number of dots in name */
ndots = 0;
for (p = name; *p; p++) {
for (p = name; *p != 0; p++) {
if (*p == '.') {
ndots++;
}
}
/* If ndots is at least the channel ndots threshold (usually 1),
* then we try the name as-is first. Otherwise, we try the name
* as-is last.
*/
if (ndots >= channel->ndots || squery->ndomains == 0) {
/* Try the name as-is first. */
squery->next_domain = 0;
squery->trying_as_is = ARES_TRUE;
ares_send(channel, squery->buf, (int)squery->buflen, search_callback,
squery);
} else {
/* Try the name as-is last; start with the first search domain. */
status = ares__cat_domain(name, squery->domains[0], &s);
if (status == ARES_SUCCESS) {
squery->next_domain = 1;
squery->trying_as_is = ARES_FALSE;
status = ares__write_and_send_query(channel, dnsrec, s, search_callback,
squery);
ares_free(s);
/* Allocate an entry for each search domain, plus one for as-is */
list_len = channel->ndomains + 1;
list = ares_malloc_zero(sizeof(*list) * list_len);
if (list == NULL) {
status = ARES_ENOMEM;
goto done;
}
/* Try as-is first */
if (ndots >= channel->ndots) {
list[idx] = ares_strdup(name);
if (list[idx] == NULL) {
status = ARES_ENOMEM;
goto done;
}
/* Handle any errors. */
idx++;
}
/* Append each search suffix to the name */
for (i=0; i<channel->ndomains; i++) {
status = ares__cat_domain(name, channel->domains[i], &list[idx]);
if (status != ARES_SUCCESS) {
end_squery(squery, status, NULL, 0);
goto done;
}
idx++;
}
/* Try as-is last */
if (ndots < channel->ndots) {
list[idx] = ares_strdup(name);
if (list[idx] == NULL) {
status = ARES_ENOMEM;
goto done;
}
idx++;
}
done:
if (status == ARES_SUCCESS) {
*names = list;
*names_len = list_len;
} else {
ares__strsplit_free(list, list_len);
}
ares_free(alias);
return status;
}
/* Search for a DNS name with given class and type. Wrapper around
* ares_search_int() where the DNS record to search is first constructed.
*/
void ares_search(ares_channel_t *channel, const char *name, int dnsclass,
int type, ares_callback callback, void *arg)
static ares_status_t ares_search_int(ares_channel_t *channel,
ares_dns_record_t *dnsrec,
ares_callback_dnsrec callback, void *arg)
{
ares_status_t status;
ares_dns_record_t *dnsrec = NULL;
size_t max_udp_size;
ares_dns_flags_t rd_flag;
struct search_query *squery = NULL;
const char *name;
ares_status_t status = ARES_SUCCESS;
ares_bool_t skip_cleanup = ARES_FALSE;
if ((channel == NULL) || (name == NULL)) {
return;
/* Extract the name for the search. Note that searches are only supported for
* DNS records containing a single query.
*/
if (ares_dns_record_query_cnt(dnsrec) != 1) {
status = ARES_EBADQUERY;
goto fail;
}
rd_flag = !(channel->flags & ARES_FLAG_NORECURSE) ? ARES_FLAG_RD: 0;
max_udp_size = (channel->flags & ARES_FLAG_EDNS) ? channel->ednspsz : 0;
status = ares_dns_record_create_query(&dnsrec, name,
(ares_dns_class_t)dnsclass,
(ares_dns_rec_type_t)type,
0, rd_flag, max_udp_size);
status = ares_dns_record_query_get(dnsrec, 0, &name, NULL, NULL);
if (status != ARES_SUCCESS) {
callback(arg, (int)status, 0, NULL, 0);
return;
goto fail;
}
ares__channel_lock(channel);
ares_search_int(channel, dnsrec, callback, arg);
ares__channel_unlock(channel);
/* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
if (ares__is_onion_domain(name)) {
status = ARES_ENOTFOUND;
goto fail;
}
ares_dns_record_destroy(dnsrec);
/* Allocate a search_query structure to hold the state necessary for
* doing multiple lookups.
*/
squery = ares_malloc_zero(sizeof(*squery));
if (squery == NULL) {
status = ARES_ENOMEM;
goto fail;
}
squery->channel = channel;
/* Duplicate DNS record since, name will need to be rewritten */
squery->dnsrec = ares_dns_record_duplicate(dnsrec);
if (squery->dnsrec == NULL) {
status = ARES_ENOMEM;
goto fail;
}
squery->callback = callback;
squery->arg = arg;
squery->timeouts = 0;
squery->ever_got_nodata = ARES_FALSE;
status = ares__search_name_list(channel, name, &squery->names,
&squery->names_cnt);
if (status != ARES_SUCCESS) {
goto fail;
}
status = ares_search_next(channel, squery, &skip_cleanup);
if (status != ARES_SUCCESS) {
goto fail;
}
return status;
fail:
if (!skip_cleanup) {
squery_free(squery);
callback(arg, status, 0, NULL);
}
return status;
}
/* Search for a DNS record. Wrapper around ares_search_int(). */
void ares_search_dnsrec(ares_channel_t *channel, ares_dns_record_t *dnsrec,
ares_callback_dnsrec callback, void *arg)
/* Callback argument structure passed to ares__dnsrec_convert_cb(). */
typedef struct {
ares_callback callback;
void *arg;
} dnsrec_convert_arg_t;
/*! Function to create callback arg for converting from ares_callback_dnsrec
* to ares_calback */
void *ares__dnsrec_convert_arg(ares_callback callback, void *arg)
{
dnsrec_convert_arg_t *carg = ares_malloc_zero(sizeof(*carg));
if (carg == NULL)
return NULL;
carg->callback = callback;
carg->arg = arg;
return carg;
}
/*! Callback function used to convert from the ares_callback_dnsrec prototype to
* the ares_callback prototype, by writing the result and passing that to
* the inner callback.
*/
void ares__dnsrec_convert_cb(void *arg, ares_status_t status, size_t timeouts,
const ares_dns_record_t *dnsrec)
{
struct dnsrec_convert_arg *carg;
dnsrec_convert_arg_t *carg = arg;
unsigned char *abuf = NULL;
size_t alen = 0;
if (dnsrec != NULL) {
ares_status_t mystatus = ares_dns_write(dnsrec, &abuf, &alen);
if (mystatus != ARES_SUCCESS) {
status = mystatus;
}
}
if ((channel == NULL) || (dnsrec == NULL)) {
carg->callback(carg->arg, (int)status, (int)timeouts, abuf, (int)alen);
ares_free(abuf);
ares_free(carg);
}
/* Search for a DNS name with given class and type. Wrapper around
* ares_search_int() where the DNS record to search is first constructed.
*/
void ares_search(ares_channel_t *channel, const char *name, int dnsclass,
int type, ares_callback callback, void *arg)
{
ares_status_t status;
ares_dns_record_t *dnsrec = NULL;
size_t max_udp_size;
ares_dns_flags_t rd_flag;
void *carg = NULL;
if (channel == NULL || name == NULL) {
return;
}
@ -243,161 +420,50 @@ void ares_search_dnsrec(ares_channel_t *channel, ares_dns_record_t *dnsrec,
* convert from ares_callback_dnsrec to ares_callback. Allocate the convert
* arg structure here.
*/
carg = ares_malloc_zero(sizeof(*carg));
carg = ares__dnsrec_convert_arg(callback, arg);
if (carg == NULL) {
callback(arg, ARES_ENOMEM, 0, NULL);
callback(arg, ARES_ENOMEM, 0, NULL, 0);
return;
}
carg->callback = callback;
carg->arg = arg;
ares__channel_lock(channel);
ares_search_int(channel, dnsrec, ares__dnsrec_convert_cb, carg);
ares__channel_unlock(channel);
}
static void search_callback(void *arg, int status, int timeouts,
unsigned char *abuf, int alen)
{
struct search_query *squery = (struct search_query *)arg;
ares_channel_t *channel = squery->channel;
ares_dns_record_t *dnsrep = NULL;
ares_dns_rcode_t rcode;
size_t ancount;
ares_dns_record_t *dnsrec = NULL;
const char *name;
char *s = NULL;
ares_status_t mystatus;
squery->timeouts += (size_t)timeouts;
rd_flag = !(channel->flags & ARES_FLAG_NORECURSE) ? ARES_FLAG_RD: 0;
max_udp_size = (channel->flags & ARES_FLAG_EDNS) ? channel->ednspsz : 0;
status = ares_dns_record_create_query(&dnsrec, name,
(ares_dns_class_t)dnsclass,
(ares_dns_rec_type_t)type,
0, rd_flag, max_udp_size);
if (status != ARES_SUCCESS) {
end_squery(squery, (ares_status_t)status, abuf, (size_t)alen);
return;
}
/* Convert the rcode and ancount from the response into an ares_status_t
* value. Stop searching unless we got a non-fatal error.
*/
mystatus = ares_dns_parse(abuf, (size_t)alen, 0, &dnsrep);
if (mystatus != ARES_SUCCESS) {
end_squery(squery, mystatus, abuf, (size_t)alen);
callback(arg, (int)status, 0, NULL, 0);
ares_free(carg);
return;
}
rcode = ares_dns_record_get_rcode(dnsrep);
ancount = ares_dns_record_rr_cnt(dnsrep, ARES_SECTION_ANSWER);
ares_dns_record_destroy(dnsrep);
mystatus = ares_dns_query_reply_tostatus(rcode, ancount);
if ((mystatus != ARES_ENODATA) && (mystatus != ARES_ESERVFAIL) &&
(mystatus != ARES_ENOTFOUND)) {
end_squery(squery, mystatus, abuf, (size_t)alen);
} else {
/* Save the status if we were trying as-is. */
if (squery->trying_as_is) {
squery->status_as_is = (int)mystatus;
}
/* If we ever get ARES_ENODATA along the way, record that; if the search
* should run to the very end and we got at least one ARES_ENODATA,
* then callers like ares_gethostbyname() may want to try a T_A search
* even if the last domain we queried for T_AAAA resource records
* returned ARES_ENOTFOUND.
*/
if (mystatus == ARES_ENODATA) {
squery->ever_got_nodata = ARES_TRUE;
}
ares__channel_lock(channel);
ares_search_int(channel, dnsrec, ares__dnsrec_convert_cb, carg);
ares__channel_unlock(channel);
if (squery->next_domain < squery->ndomains) {
/* Try the next domain.
*
* First parse the encoded DNS record in the search_query structure, so
* that we can append the next domain to it.
*/
mystatus = ares_dns_parse(squery->buf, squery->buflen, 0, &dnsrec);
if (mystatus != ARES_SUCCESS) {
end_squery(squery, mystatus, NULL, 0);
} else {
/* Concatenate the name with the search domain and query using that. */
if (ares_dns_record_query_cnt(dnsrec) != 1) {
mystatus = ARES_EBADQUERY;
} else {
mystatus = ares_dns_record_query_get(dnsrec, 0, &name, NULL, NULL);
if (mystatus == ARES_SUCCESS) {
mystatus = ares__cat_domain(name,
squery->domains[squery->next_domain],
&s);
if (mystatus == ARES_SUCCESS) {
squery->trying_as_is = ARES_FALSE;
squery->next_domain++;
mystatus = ares__write_and_send_query(channel, dnsrec, s,
search_callback, arg);
ares_free(s);
}
}
}
/* Clean up the DNS record object and handle any errors. */
ares_dns_record_destroy(dnsrec);
if (mystatus != ARES_SUCCESS) {
end_squery(squery, mystatus, NULL, 0);
}
}
} else if (squery->status_as_is == -1) {
/* Try the name as-is at the end. */
squery->trying_as_is = ARES_TRUE;
ares_send(channel, squery->buf, (int)squery->buflen, search_callback,
squery);
} else {
/* We have no more domains to search, return an appropriate response. */
if (squery->status_as_is == ARES_ENOTFOUND && squery->ever_got_nodata) {
end_squery(squery, ARES_ENODATA, NULL, 0);
} else {
end_squery(squery, (ares_status_t)squery->status_as_is, NULL, 0);
}
}
}
ares_dns_record_destroy(dnsrec);
}
/* Write and send a DNS record on a channel. The DNS record must represent a
* query for a single name. An alternative name can be specified to temporarily
* overwrite the name on the DNS record before doing so. Note that this only
* affects the name in the question section; RRs are not affected.
* This is used as a helper function in ares_search().
*/
static ares_status_t ares__write_and_send_query(ares_channel_t *channel,
ares_dns_record_t *dnsrec,
char *altname,
ares_callback callback,
void *arg)
/* Search for a DNS record. Wrapper around ares_search_int(). */
ares_status_t ares_search_dnsrec(ares_channel_t *channel,
ares_dns_record_t *dnsrec,
ares_callback_dnsrec callback, void *arg)
{
ares_status_t status;
unsigned char *buf;
size_t buflen;
ares_status_t status;
status = ares_dns_write_query_altname(dnsrec, altname, &buf, &buflen);
if (status != ARES_SUCCESS) {
return status;
if (channel == NULL || dnsrec == NULL || callback == NULL) {
return ARES_EFORMERR;
}
ares_send(channel, buf, (int)buflen, callback, arg);
ares_free(buf);
return ARES_SUCCESS;
}
ares__channel_lock(channel);
status = ares_search_int(channel, dnsrec, callback, arg);
ares__channel_unlock(channel);
/* End a search query by invoking the user callback and freeing the
* search_query structure.
*/
static void end_squery(struct search_query *squery, ares_status_t status,
unsigned char *abuf, size_t alen)
{
squery->callback(squery->arg, (int)status, (int)squery->timeouts, abuf,
(int)alen);
ares__strsplit_free(squery->domains, squery->ndomains);
ares_free(squery->buf);
ares_free(squery);
return status;
}
/* Concatenate two domains. */
ares_status_t ares__cat_domain(const char *name, const char *domain, char **s)
{
@ -420,16 +486,32 @@ ares_status_t ares__cat_domain(const char *name, const char *domain, char **s)
return ARES_SUCCESS;
}
static ares_status_t ares__lookup_hostaliases(const char *name, char **alias)
ares_status_t ares__lookup_hostaliases(const ares_channel_t *channel,
const char *name, char **alias)
{
ares_status_t status = ARES_SUCCESS;
const char *hostaliases = getenv("HOSTALIASES");
const char *hostaliases = NULL;
ares__buf_t *buf = NULL;
ares__llist_t *lines = NULL;
ares__llist_node_t *node;
*alias = NULL;
if (channel == NULL || name == NULL || alias == NULL) {
return ARES_EFORMERR;
}
/* Configuration says to not perform alias lookup */
if (channel->flags & ARES_FLAG_NOALIASES) {
return ARES_ENOTFOUND;
}
/* If a domain has a '.', its not allowed to perform an alias lookup */
if (strchr(name, '.') != NULL) {
return ARES_ENOTFOUND;
}
hostaliases = getenv("HOSTALIASES");
if (hostaliases == NULL) {
status = ARES_ENOTFOUND;
goto done;
@ -514,66 +596,3 @@ done:
return status;
}
/* Determine if this name only yields one query. If it does, set *s to
* the string we should query, in an allocated buffer. If not, set *s
* to NULL.
*/
ares_status_t ares__single_domain(const ares_channel_t *channel,
const char *name, char **s)
{
size_t len = ares_strlen(name);
ares_status_t status;
*s = NULL;
/* If the name contains a trailing dot, then the single query is the name
* sans the trailing dot.
*/
if ((len > 0) && (name[len - 1] == '.')) {
*s = ares_strdup(name);
return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
}
if (!(channel->flags & ARES_FLAG_NOALIASES) && !strchr(name, '.')) {
status = ares__lookup_hostaliases(name, s);
if (status != ARES_ENOTFOUND) {
return status;
}
}
if (channel->flags & ARES_FLAG_NOSEARCH || channel->ndomains == 0) {
/* No domain search to do; just try the name as-is. */
*s = ares_strdup(name);
return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
}
*s = NULL;
return ARES_SUCCESS;
}
/* Callback function used to convert from the ares_callback prototype to the
* ares_callback_dnsrec prototype, by parsing the result and passing that to
* the inner callback.
*/
static void ares__dnsrec_convert_cb(void *arg, int status, int timeouts,
unsigned char *abuf, int alen)
{
struct dnsrec_convert_arg *carg = (struct dnsrec_convert_arg *)arg;
ares_dns_record_t *dnsrec = NULL;
ares_status_t mystatus;
if (status != ARES_SUCCESS) {
carg->callback(carg->arg, (ares_status_t)status, (size_t)timeouts, NULL);
} else {
/* Parse the result. */
mystatus = ares_dns_parse(abuf, (size_t)alen, 0, &dnsrec);
if (mystatus != ARES_SUCCESS) {
carg->callback(carg->arg, mystatus, (size_t)timeouts, NULL);
} else {
carg->callback(carg->arg, ARES_SUCCESS, (size_t)timeouts, dnsrec);
ares_dns_record_destroy(dnsrec);
}
}
ares_free(carg);
}

@ -48,52 +48,47 @@ static unsigned short generate_unique_qid(ares_channel_t *channel)
return id;
}
ares_status_t ares_send_ex(ares_channel_t *channel, const unsigned char *qbuf,
size_t qlen, ares_callback callback, void *arg,
unsigned short *qid)
static ares_status_t ares_send_dnsrec_int(ares_channel_t *channel,
const ares_dns_record_t *dnsrec,
ares_callback_dnsrec callback,
void *arg, unsigned short *qid)
{
struct query *query;
size_t packetsz;
struct timeval now = ares__tvnow();
ares_status_t status;
unsigned short id = generate_unique_qid(channel);
unsigned char *abuf = NULL;
size_t alen = 0;
struct query *query;
size_t packetsz;
struct timeval now = ares__tvnow();
ares_status_t status;
unsigned short id = generate_unique_qid(channel);
const ares_dns_record_t *dnsrec_resp = NULL;
/* Verify that the query is at least long enough to hold the header. */
if (qlen < HFIXEDSZ || qlen >= (1 << 16)) {
callback(arg, ARES_EBADQUERY, 0, NULL, 0);
return ARES_EBADQUERY;
}
if (ares__slist_len(channel->servers) == 0) {
callback(arg, ARES_ENOSERVER, 0, NULL, 0);
callback(arg, ARES_ENOSERVER, 0, NULL);
return ARES_ENOSERVER;
}
/* Check query cache */
status = ares_qcache_fetch(channel, &now, qbuf, qlen, &abuf, &alen);
status = ares_qcache_fetch(channel, &now, dnsrec, &dnsrec_resp);
if (status != ARES_ENOTFOUND) {
/* ARES_SUCCESS means we retrieved the cache, anything else is a critical
* failure, all result in termination */
callback(arg, (int)status, 0, abuf, (int)alen);
ares_free(abuf);
callback(arg, status, 0, dnsrec_resp);
return status;
}
/* Allocate space for query and allocated fields. */
query = ares_malloc(sizeof(struct query));
if (!query) {
callback(arg, ARES_ENOMEM, 0, NULL, 0);
callback(arg, ARES_ENOMEM, 0, NULL);
return ARES_ENOMEM;
}
memset(query, 0, sizeof(*query));
query->channel = channel;
query->qbuf = ares_malloc(qlen);
if (!query->qbuf) {
status = ares_dns_write(dnsrec, &query->qbuf, &query->qlen);
if (status != ARES_SUCCESS) {
ares_free(query);
callback(arg, ARES_ENOMEM, 0, NULL, 0);
return ARES_ENOMEM;
callback(arg, status, 0, NULL);
return status;
}
query->qid = id;
@ -103,8 +98,6 @@ ares_status_t ares_send_ex(ares_channel_t *channel, const unsigned char *qbuf,
/* 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. */
query->callback = callback;
@ -114,7 +107,7 @@ ares_status_t ares_send_ex(ares_channel_t *channel, const unsigned char *qbuf,
query->try_count = 0;
packetsz = (channel->flags & ARES_FLAG_EDNS) ? channel->ednspsz : PACKETSZ;
query->using_tcp = (channel->flags & ARES_FLAG_USEVC) || qlen > packetsz;
query->using_tcp = (channel->flags & ARES_FLAG_USEVC) || query->qlen > packetsz;
query->error_status = ARES_SUCCESS;
query->timeouts = 0;
@ -127,7 +120,7 @@ ares_status_t ares_send_ex(ares_channel_t *channel, const unsigned char *qbuf,
query->node_all_queries =
ares__llist_insert_last(channel->all_queries, query);
if (query->node_all_queries == NULL) {
callback(arg, ARES_ENOMEM, 0, NULL, 0);
callback(arg, ARES_ENOMEM, 0, NULL);
ares__free_query(query);
return ARES_ENOMEM;
}
@ -136,7 +129,7 @@ ares_status_t ares_send_ex(ares_channel_t *channel, const unsigned char *qbuf,
* responses quickly.
*/
if (!ares__htable_szvp_insert(channel->queries_by_qid, query->qid, query)) {
callback(arg, ARES_ENOMEM, 0, NULL, 0);
callback(arg, ARES_ENOMEM, 0, NULL);
ares__free_query(query);
return ARES_ENOMEM;
}
@ -150,18 +143,62 @@ ares_status_t ares_send_ex(ares_channel_t *channel, const unsigned char *qbuf,
return status;
}
ares_status_t ares_send_dnsrec(ares_channel_t *channel,
const ares_dns_record_t *dnsrec,
ares_callback_dnsrec callback,
void *arg, unsigned short *qid)
{
ares_status_t status;
if (channel == NULL) {
return ARES_EFORMERR;
}
ares__channel_lock(channel);
status = ares_send_dnsrec_int(channel, dnsrec, callback, arg, qid);
ares__channel_unlock(channel);
return status;
}
void ares_send(ares_channel_t *channel, const unsigned char *qbuf, int qlen,
ares_callback callback, void *arg)
{
ares_dns_record_t *dnsrec = NULL;
ares_status_t status;
void *carg = NULL;
if (channel == NULL) {
return;
}
ares__channel_lock(channel);
/* Verify that the query is at least long enough to hold the header. */
if (qlen < HFIXEDSZ || qlen >= (1 << 16)) {
callback(arg, ARES_EBADQUERY, 0, NULL, 0);
return;
}
ares_send_ex(channel, qbuf, (size_t)qlen, callback, arg, NULL);
status = ares_dns_parse(qbuf, (size_t)qlen, 0, &dnsrec);
if (status != ARES_SUCCESS) {
callback(arg, (int)status, 0, NULL, 0);
return;
}
ares__channel_unlock(channel);
carg = ares__dnsrec_convert_arg(callback, arg);
if (carg == NULL) {
status = ARES_ENOMEM;
ares_dns_record_destroy(dnsrec);
callback(arg, (int)status, 0, NULL, 0);
return;
}
ares_send_dnsrec(channel, dnsrec, ares__dnsrec_convert_cb, carg, NULL);
ares_dns_record_destroy(dnsrec);
}
size_t ares_queue_active_queries(ares_channel_t *channel)

Loading…
Cancel
Save