summaryrefslogtreecommitdiff
path: root/src/resolv
diff options
context:
space:
mode:
Diffstat (limited to 'src/resolv')
-rw-r--r--src/resolv/ares/ares_data.c140
-rw-r--r--src/resolv/ares/ares_data.h68
-rw-r--r--src/resolv/ares/ares_dns.h91
-rw-r--r--src/resolv/ares/ares_parse_srv_reply.c183
-rw-r--r--src/resolv/ares/ares_parse_srv_reply.h35
-rw-r--r--src/resolv/ares/ares_parse_txt_reply.c204
-rw-r--r--src/resolv/ares/ares_parse_txt_reply.h33
-rw-r--r--src/resolv/async_resolv.c1062
-rw-r--r--src/resolv/async_resolv.h95
9 files changed, 1911 insertions, 0 deletions
diff --git a/src/resolv/ares/ares_data.c b/src/resolv/ares/ares_data.c
new file mode 100644
index 00000000..1cccaa55
--- /dev/null
+++ b/src/resolv/ares/ares_data.c
@@ -0,0 +1,140 @@
+/* $Id: ares_data.c,v 1.2 2009-11-20 09:06:33 yangtse Exp $ */
+
+/* Copyright (C) 2009 by Daniel Stenberg
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "ares.h"
+#include "ares_data.h"
+
+/*
+** ares_free_data() - c-ares external API function.
+**
+** This function must be used by the application to free data memory that
+** has been internally allocated by some c-ares function and for which a
+** pointer has already been returned to the calling application. The list
+** of c-ares functions returning pointers that must be free'ed using this
+** function is:
+**
+** ares_parse_srv_reply()
+** ares_parse_txt_reply()
+*/
+
+void _ares_free_data(void *dataptr)
+{
+ struct ares_data *ptr;
+
+ if (!dataptr)
+ return;
+
+ ptr = (void *)((char *)dataptr - offsetof(struct ares_data, data));
+
+ if (ptr->mark != ARES_DATATYPE_MARK)
+ return;
+
+ switch (ptr->type)
+ {
+ case ARES_DATATYPE_SRV_REPLY:
+
+ if (ptr->data.srv_reply.next)
+ _ares_free_data(ptr->data.srv_reply.next);
+ if (ptr->data.srv_reply.host)
+ free(ptr->data.srv_reply.host);
+ break;
+
+ case ARES_DATATYPE_TXT_REPLY:
+
+ if (ptr->data.txt_reply.next)
+ _ares_free_data(ptr->data.txt_reply.next);
+ if (ptr->data.txt_reply.txt)
+ free(ptr->data.txt_reply.txt);
+ break;
+
+ default:
+ return;
+ }
+
+ free(ptr);
+}
+
+
+/*
+** ares_malloc_data() - c-ares internal helper function.
+**
+** This function allocates memory for a c-ares private ares_data struct
+** for the specified ares_datatype, initializes c-ares private fields
+** and zero initializes those which later might be used from the public
+** API. It returns an interior pointer which can be passed by c-ares
+** functions to the calling application, and that must be free'ed using
+** c-ares external API function ares_free_data().
+*/
+
+void *_ares_malloc_data(ares_datatype type)
+{
+ struct ares_data *ptr;
+
+ ptr = malloc(sizeof(struct ares_data));
+ if (!ptr)
+ return NULL;
+
+ switch (type)
+ {
+ case ARES_DATATYPE_SRV_REPLY:
+ ptr->data.srv_reply.next = NULL;
+ ptr->data.srv_reply.host = NULL;
+ ptr->data.srv_reply.priority = 0;
+ ptr->data.srv_reply.weight = 0;
+ ptr->data.srv_reply.port = 0;
+ break;
+
+ case ARES_DATATYPE_TXT_REPLY:
+ ptr->data.txt_reply.next = NULL;
+ ptr->data.txt_reply.txt = NULL;
+ ptr->data.txt_reply.length = 0;
+ break;
+
+ default:
+ free(ptr);
+ return NULL;
+ }
+
+ ptr->mark = ARES_DATATYPE_MARK;
+ ptr->type = type;
+
+ return &ptr->data;
+}
+
+
+/*
+** ares_get_datatype() - c-ares internal helper function.
+**
+** This function returns the ares_datatype of the data stored in a
+** private ares_data struct when given the public API pointer.
+*/
+
+ares_datatype ares_get_datatype(void * dataptr)
+{
+ struct ares_data *ptr;
+
+ ptr = (void *)((char *)dataptr - offsetof(struct ares_data, data));
+
+ if (ptr->mark == ARES_DATATYPE_MARK)
+ return ptr->type;
+
+ return ARES_DATATYPE_UNKNOWN;
+}
diff --git a/src/resolv/ares/ares_data.h b/src/resolv/ares/ares_data.h
new file mode 100644
index 00000000..d3606314
--- /dev/null
+++ b/src/resolv/ares/ares_data.h
@@ -0,0 +1,68 @@
+/* $Id: ares_data.h,v 1.2 2009-11-23 12:03:33 yangtse Exp $ */
+
+/* Copyright (C) 2009 by Daniel Stenberg
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+
+#ifndef HAVE_ARES_DATA
+#include "resolv/ares/ares_parse_txt_reply.h"
+#include "resolv/ares/ares_parse_srv_reply.h"
+#endif /* HAVE_ARES_DATA */
+
+typedef enum {
+ ARES_DATATYPE_UNKNOWN = 1, /* unknown data type - introduced in 1.7.0 */
+ ARES_DATATYPE_SRV_REPLY, /* struct ares_srv_reply - introduced in 1.7.0 */
+ ARES_DATATYPE_TXT_REPLY, /* struct ares_txt_reply - introduced in 1.7.0 */
+#if 0
+ ARES_DATATYPE_ADDR6TTL, /* struct ares_addrttl */
+ ARES_DATATYPE_ADDRTTL, /* struct ares_addr6ttl */
+ ARES_DATATYPE_HOSTENT, /* struct hostent */
+ ARES_DATATYPE_OPTIONS, /* struct ares_options */
+#endif
+ ARES_DATATYPE_LAST /* not used - introduced in 1.7.0 */
+} ares_datatype;
+
+#define ARES_DATATYPE_MARK 0xbead
+
+/*
+ * ares_data struct definition is internal to c-ares and shall not
+ * be exposed by the public API in order to allow future changes
+ * and extensions to it without breaking ABI. This will be used
+ * internally by c-ares as the container of multiple types of data
+ * dynamically allocated for which a reference will be returned
+ * to the calling application.
+ *
+ * c-ares API functions returning a pointer to c-ares internally
+ * allocated data will actually be returning an interior pointer
+ * into this ares_data struct.
+ *
+ * All this is 'invisible' to the calling application, the only
+ * requirement is that this kind of data must be free'ed by the
+ * calling application using ares_free_data() with the pointer
+ * it has received from a previous c-ares function call.
+ */
+
+struct ares_data {
+ ares_datatype type; /* Actual data type identifier. */
+ unsigned int mark; /* Private ares_data signature. */
+ union {
+ struct ares_txt_reply txt_reply;
+ struct ares_srv_reply srv_reply;
+ } data;
+};
+
+void *_ares_malloc_data(ares_datatype type);
+void _ares_free_data(void *dataptr);
+
+ares_datatype ares_get_datatype(void * dataptr);
diff --git a/src/resolv/ares/ares_dns.h b/src/resolv/ares/ares_dns.h
new file mode 100644
index 00000000..c0a9dda6
--- /dev/null
+++ b/src/resolv/ares/ares_dns.h
@@ -0,0 +1,91 @@
+/* $Id: ares_dns.h,v 1.8 2007-02-16 14:22:08 yangtse Exp $ */
+
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+
+#ifndef ARES__DNS_H
+#define ARES__DNS_H
+
+#define DNS__16BIT(p) (((p)[0] << 8) | (p)[1])
+#define DNS__32BIT(p) (((p)[0] << 24) | ((p)[1] << 16) | \
+ ((p)[2] << 8) | (p)[3])
+
+#define DNS__SET16BIT(p, v) (((p)[0] = (unsigned char)(((v) >> 8) & 0xff)), \
+ ((p)[1] = (unsigned char)((v) & 0xff)))
+#define DNS__SET32BIT(p, v) (((p)[0] = (unsigned char)(((v) >> 24) & 0xff)), \
+ ((p)[1] = (unsigned char)(((v) >> 16) & 0xff)), \
+ ((p)[2] = (unsigned char)(((v) >> 8) & 0xff)), \
+ ((p)[3] = (unsigned char)((v) & 0xff)))
+
+#if 0
+/* we cannot use this approach on systems where we can't access 16/32 bit
+ data on un-aligned addresses */
+#define DNS__16BIT(p) ntohs(*(unsigned short*)(p))
+#define DNS__32BIT(p) ntohl(*(unsigned long*)(p))
+#define DNS__SET16BIT(p, v) *(unsigned short*)(p) = htons(v)
+#define DNS__SET32BIT(p, v) *(unsigned long*)(p) = htonl(v)
+#endif
+
+/* Macros for parsing a DNS header */
+#define DNS_HEADER_QID(h) DNS__16BIT(h)
+#define DNS_HEADER_QR(h) (((h)[2] >> 7) & 0x1)
+#define DNS_HEADER_OPCODE(h) (((h)[2] >> 3) & 0xf)
+#define DNS_HEADER_AA(h) (((h)[2] >> 2) & 0x1)
+#define DNS_HEADER_TC(h) (((h)[2] >> 1) & 0x1)
+#define DNS_HEADER_RD(h) ((h)[2] & 0x1)
+#define DNS_HEADER_RA(h) (((h)[3] >> 7) & 0x1)
+#define DNS_HEADER_Z(h) (((h)[3] >> 4) & 0x7)
+#define DNS_HEADER_RCODE(h) ((h)[3] & 0xf)
+#define DNS_HEADER_QDCOUNT(h) DNS__16BIT((h) + 4)
+#define DNS_HEADER_ANCOUNT(h) DNS__16BIT((h) + 6)
+#define DNS_HEADER_NSCOUNT(h) DNS__16BIT((h) + 8)
+#define DNS_HEADER_ARCOUNT(h) DNS__16BIT((h) + 10)
+
+/* Macros for constructing a DNS header */
+#define DNS_HEADER_SET_QID(h, v) DNS__SET16BIT(h, v)
+#define DNS_HEADER_SET_QR(h, v) ((h)[2] |= (unsigned char)(((v) & 0x1) << 7))
+#define DNS_HEADER_SET_OPCODE(h, v) ((h)[2] |= (unsigned char)(((v) & 0xf) << 3))
+#define DNS_HEADER_SET_AA(h, v) ((h)[2] |= (unsigned char)(((v) & 0x1) << 2))
+#define DNS_HEADER_SET_TC(h, v) ((h)[2] |= (unsigned char)(((v) & 0x1) << 1))
+#define DNS_HEADER_SET_RD(h, v) ((h)[2] |= (unsigned char)((v) & 0x1))
+#define DNS_HEADER_SET_RA(h, v) ((h)[3] |= (unsigned char)(((v) & 0x1) << 7))
+#define DNS_HEADER_SET_Z(h, v) ((h)[3] |= (unsigned char)(((v) & 0x7) << 4))
+#define DNS_HEADER_SET_RCODE(h, v) ((h)[3] |= (unsigned char)((v) & 0xf))
+#define DNS_HEADER_SET_QDCOUNT(h, v) DNS__SET16BIT((h) + 4, v)
+#define DNS_HEADER_SET_ANCOUNT(h, v) DNS__SET16BIT((h) + 6, v)
+#define DNS_HEADER_SET_NSCOUNT(h, v) DNS__SET16BIT((h) + 8, v)
+#define DNS_HEADER_SET_ARCOUNT(h, v) DNS__SET16BIT((h) + 10, v)
+
+/* Macros for parsing the fixed part of a DNS question */
+#define DNS_QUESTION_TYPE(q) DNS__16BIT(q)
+#define DNS_QUESTION_CLASS(q) DNS__16BIT((q) + 2)
+
+/* Macros for constructing the fixed part of a DNS question */
+#define DNS_QUESTION_SET_TYPE(q, v) DNS__SET16BIT(q, v)
+#define DNS_QUESTION_SET_CLASS(q, v) DNS__SET16BIT((q) + 2, v)
+
+/* Macros for parsing the fixed part of a DNS resource record */
+#define DNS_RR_TYPE(r) DNS__16BIT(r)
+#define DNS_RR_CLASS(r) DNS__16BIT((r) + 2)
+#define DNS_RR_TTL(r) DNS__32BIT((r) + 4)
+#define DNS_RR_LEN(r) DNS__16BIT((r) + 8)
+
+/* Macros for constructing the fixed part of a DNS resource record */
+#define DNS_RR_SET_TYPE(r) DNS__SET16BIT(r, v)
+#define DNS_RR_SET_CLASS(r) DNS__SET16BIT((r) + 2, v)
+#define DNS_RR_SET_TTL(r) DNS__SET32BIT((r) + 4, v)
+#define DNS_RR_SET_LEN(r) DNS__SET16BIT((r) + 8, v)
+
+#endif /* ARES__DNS_H */
diff --git a/src/resolv/ares/ares_parse_srv_reply.c b/src/resolv/ares/ares_parse_srv_reply.c
new file mode 100644
index 00000000..086c4dba
--- /dev/null
+++ b/src/resolv/ares/ares_parse_srv_reply.c
@@ -0,0 +1,183 @@
+/*
+ SSSD
+
+ Async resolver - SRV records parsing
+
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) Red Hat, Inc 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * This code is based on other c-ares parsing licensed as follows:
+
+ * Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <arpa/nameser.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ares.h"
+/* this drags in some private macros c-ares uses */
+#include "ares_dns.h"
+#include "ares_data.h"
+
+#include "ares_parse_srv_reply.h"
+
+int _ares_parse_srv_reply (const unsigned char *abuf, int alen,
+ struct ares_srv_reply **srv_out)
+{
+ unsigned int qdcount, ancount, i;
+ const unsigned char *aptr, *vptr;
+ int status, rr_type, rr_class, rr_len;
+ long len;
+ char *hostname = NULL, *rr_name = NULL;
+ struct ares_srv_reply *srv_head = NULL;
+ struct ares_srv_reply *srv_last = NULL;
+ struct ares_srv_reply *srv_curr;
+
+ /* Set *srv_out to NULL for all failure cases. */
+ *srv_out = NULL;
+
+ /* Give up if abuf doesn't have room for a header. */
+ if (alen < HFIXEDSZ)
+ return ARES_EBADRESP;
+
+ /* Fetch the question and answer count from the header. */
+ qdcount = DNS_HEADER_QDCOUNT (abuf);
+ ancount = DNS_HEADER_ANCOUNT (abuf);
+ if (qdcount != 1)
+ return ARES_EBADRESP;
+ if (ancount == 0)
+ return ARES_ENODATA;
+
+ /* Expand the name from the question, and skip past the question. */
+ aptr = abuf + HFIXEDSZ;
+ status = ares_expand_name (aptr, abuf, alen, &hostname, &len);
+ if (status != ARES_SUCCESS)
+ return status;
+
+ if (aptr + len + QFIXEDSZ > abuf + alen)
+ {
+ free (hostname);
+ return ARES_EBADRESP;
+ }
+ aptr += len + QFIXEDSZ;
+
+ /* Examine each answer resource record (RR) in turn. */
+ for (i = 0; i < (int) ancount; i++)
+ {
+ /* Decode the RR up to the data field. */
+ status = ares_expand_name (aptr, abuf, alen, &rr_name, &len);
+ if (status != ARES_SUCCESS)
+ {
+ break;
+ }
+ aptr += len;
+ if (aptr + RRFIXEDSZ > abuf + alen)
+ {
+ status = ARES_EBADRESP;
+ break;
+ }
+ rr_type = DNS_RR_TYPE (aptr);
+ rr_class = DNS_RR_CLASS (aptr);
+ rr_len = DNS_RR_LEN (aptr);
+ aptr += RRFIXEDSZ;
+
+ /* Check if we are really looking at a SRV record */
+ if (rr_class == C_IN && rr_type == T_SRV)
+ {
+ /* parse the SRV record itself */
+ if (rr_len < 6)
+ {
+ status = ARES_EBADRESP;
+ break;
+ }
+
+ /* Allocate storage for this SRV answer appending it to the list */
+ srv_curr = _ares_malloc_data(ARES_DATATYPE_SRV_REPLY);
+ if (!srv_curr)
+ {
+ status = ARES_ENOMEM;
+ break;
+ }
+ if (srv_last)
+ {
+ srv_last->next = srv_curr;
+ }
+ else
+ {
+ srv_head = srv_curr;
+ }
+ srv_last = srv_curr;
+
+ vptr = aptr;
+ srv_curr->priority = ntohs (*((const unsigned short *)vptr));
+ vptr += sizeof(const unsigned short);
+ srv_curr->weight = ntohs (*((const unsigned short *)vptr));
+ vptr += sizeof(const unsigned short);
+ srv_curr->port = ntohs (*((const unsigned short *)vptr));
+ vptr += sizeof(const unsigned short);
+
+ status = ares_expand_name (vptr, abuf, alen, &srv_curr->host, &len);
+ if (status != ARES_SUCCESS)
+ break;
+ }
+
+ /* Don't lose memory in the next iteration */
+ free(rr_name);
+ rr_name = NULL;
+
+ /* Move on to the next record */
+ aptr += rr_len;
+ }
+
+ if (hostname)
+ free (hostname);
+ if (rr_name)
+ free (rr_name);
+
+ /* clean up on error */
+ if (status != ARES_SUCCESS)
+ {
+ if (srv_head)
+ _ares_free_data (srv_head);
+ return status;
+ }
+
+ /* everything looks fine, return the data */
+ *srv_out = srv_head;
+
+ return ARES_SUCCESS;
+}
diff --git a/src/resolv/ares/ares_parse_srv_reply.h b/src/resolv/ares/ares_parse_srv_reply.h
new file mode 100644
index 00000000..29c6e08d
--- /dev/null
+++ b/src/resolv/ares/ares_parse_srv_reply.h
@@ -0,0 +1,35 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2009 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __ARES_PARSE_SRV_REPLY_H__
+#define __ARES_PARSE_SRV_REPLY_H__
+
+struct ares_srv_reply {
+ struct ares_srv_reply *next;
+ char *host;
+ unsigned short priority;
+ unsigned short weight;
+ unsigned short port;
+};
+
+int _ares_parse_srv_reply (const unsigned char *abuf, int alen,
+ struct ares_srv_reply **srv_out);
+
+#endif /* __ARES_PARSE_SRV_REPLY_H__ */
diff --git a/src/resolv/ares/ares_parse_txt_reply.c b/src/resolv/ares/ares_parse_txt_reply.c
new file mode 100644
index 00000000..d710e8f9
--- /dev/null
+++ b/src/resolv/ares/ares_parse_txt_reply.c
@@ -0,0 +1,204 @@
+/*
+ SSSD
+
+ Async resolver - TXT records parsing
+
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) Red Hat, Inc 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * This code is based on other c-ares parsing licensed as follows:
+
+ * Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <arpa/nameser.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ares.h"
+/* this drags in some private macros c-ares uses */
+#include "ares_dns.h"
+#include "ares_data.h"
+
+#include "ares_parse_txt_reply.h"
+
+int _ares_parse_txt_reply (const unsigned char *abuf, int alen,
+ struct ares_txt_reply **txt_out)
+{
+ size_t substr_len, str_len;
+ unsigned int qdcount, ancount, i;
+ const unsigned char *aptr;
+ const unsigned char *strptr;
+ int status, rr_type, rr_class, rr_len;
+ long len;
+ char *hostname = NULL, *rr_name = NULL;
+ struct ares_txt_reply *txt_head = NULL;
+ struct ares_txt_reply *txt_last = NULL;
+ struct ares_txt_reply *txt_curr;
+
+ /* Set *txt_out to NULL for all failure cases. */
+ *txt_out = NULL;
+
+ /* Give up if abuf doesn't have room for a header. */
+ if (alen < HFIXEDSZ)
+ return ARES_EBADRESP;
+
+ /* Fetch the question and answer count from the header. */
+ qdcount = DNS_HEADER_QDCOUNT(abuf);
+ ancount = DNS_HEADER_ANCOUNT(abuf);
+ if (qdcount != 1)
+ return ARES_EBADRESP;
+ if (ancount == 0)
+ return ARES_ENODATA;
+
+ /* Expand the name from the question, and skip past the question. */
+ aptr = abuf + HFIXEDSZ;
+ status = ares_expand_name(aptr, abuf, alen, &hostname, &len);
+ if (status != ARES_SUCCESS)
+ return status;
+
+ if (aptr + len + QFIXEDSZ > abuf + alen)
+ {
+ free (hostname);
+ return ARES_EBADRESP;
+ }
+ aptr += len + QFIXEDSZ;
+
+ /* Examine each answer resource record (RR) in turn. */
+ for (i = 0; i < (int) ancount; i++)
+ {
+ /* Decode the RR up to the data field. */
+ status = ares_expand_name(aptr, abuf, alen, &rr_name, &len);
+ if (status != ARES_SUCCESS)
+ {
+ break;
+ }
+ aptr += len;
+ if (aptr + RRFIXEDSZ > abuf + alen)
+ {
+ status = ARES_EBADRESP;
+ break;
+ }
+ rr_type = DNS_RR_TYPE(aptr);
+ rr_class = DNS_RR_CLASS(aptr);
+ rr_len = DNS_RR_LEN(aptr);
+ aptr += RRFIXEDSZ;
+
+ /* Check if we are really looking at a TXT record */
+ if (rr_class == C_IN && rr_type == T_TXT)
+ {
+ /* Allocate storage for this TXT answer appending it to the list */
+ txt_curr = _ares_malloc_data(ARES_DATATYPE_TXT_REPLY);
+ if (!txt_curr)
+ {
+ status = ARES_ENOMEM;
+ break;
+ }
+ if (txt_last)
+ {
+ txt_last->next = txt_curr;
+ }
+ else
+ {
+ txt_head = txt_curr;
+ }
+ txt_last = txt_curr;
+
+ /*
+ * There may be multiple substrings in a single TXT record. Each
+ * substring may be up to 255 characters in length, with a
+ * "length byte" indicating the size of the substring payload.
+ * RDATA contains both the length-bytes and payloads of all
+ * substrings contained therein.
+ */
+
+ /* Compute total length to allow a single memory allocation */
+ strptr = aptr;
+ while (strptr < (aptr + rr_len))
+ {
+ substr_len = (unsigned char)*strptr;
+ txt_curr->length += substr_len;
+ strptr += substr_len + 1;
+ }
+
+ /* Including null byte */
+ txt_curr->txt = malloc (txt_curr->length + 1);
+ if (txt_curr->txt == NULL)
+ {
+ status = ARES_ENOMEM;
+ break;
+ }
+
+ /* Step through the list of substrings, concatenating them */
+ str_len = 0;
+ strptr = aptr;
+ while (strptr < (aptr + rr_len))
+ {
+ substr_len = (unsigned char)*strptr;
+ strptr++;
+ memcpy ((char *) txt_curr->txt + str_len, strptr, substr_len);
+ str_len += substr_len;
+ strptr += substr_len;
+ }
+ /* Make sure we NULL-terminate */
+ *((char *) txt_curr->txt + txt_curr->length) = '\0';
+ }
+
+ /* Don't lose memory in the next iteration */
+ free(rr_name);
+ rr_name = NULL;
+
+ /* Move on to the next record */
+ aptr += rr_len;
+ }
+
+ if (hostname)
+ free (hostname);
+ if (rr_name)
+ free (rr_name);
+
+ /* clean up on error */
+ if (status != ARES_SUCCESS)
+ {
+ if (txt_head)
+ _ares_free_data (txt_head);
+ return status;
+ }
+
+ /* everything looks fine, return the data */
+ *txt_out = txt_head;
+
+ return ARES_SUCCESS;
+}
diff --git a/src/resolv/ares/ares_parse_txt_reply.h b/src/resolv/ares/ares_parse_txt_reply.h
new file mode 100644
index 00000000..216e2c0d
--- /dev/null
+++ b/src/resolv/ares/ares_parse_txt_reply.h
@@ -0,0 +1,33 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2009 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __ARES_PARSE_TXT_REPLY_H__
+#define __ARES_PARSE_TXT_REPLY_H__
+
+struct ares_txt_reply {
+ struct ares_txt_reply *next;
+ unsigned char *txt;
+ size_t length; /* length excludes null termination */
+};
+
+int _ares_parse_txt_reply(const unsigned char* abuf, int alen,
+ struct ares_txt_reply **txt_out);
+
+#endif /* __ARES_PARSE_TXT_REPLY_H__ */
diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c
new file mode 100644
index 00000000..70d8d11e
--- /dev/null
+++ b/src/resolv/async_resolv.c
@@ -0,0 +1,1062 @@
+/*
+ SSSD
+
+ Async resolver
+
+ Authors:
+ Martin Nagy <mnagy@redhat.com>
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) Red Hat, Inc 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/select.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ares.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "resolv/async_resolv.h"
+#include "util/dlinklist.h"
+#include "util/util.h"
+
+#ifndef HAVE_ARES_DATA
+#define ares_parse_srv_reply(abuf, alen, srv_out) \
+ _ares_parse_srv_reply(abuf, alen, srv_out)
+#define ares_parse_txt_reply(abuf, alen, txt_out) \
+ _ares_parse_txt_reply(abuf, alen, txt_out)
+#define ares_free_data(dataptr) \
+ _ares_free_data(dataptr)
+#define ares_malloc_data(data) \
+ _ares_malloc_data(data)
+#endif /* HAVE_ARES_DATA */
+
+struct fd_watch {
+ struct fd_watch *prev;
+ struct fd_watch *next;
+
+ int fd;
+ struct resolv_ctx *ctx;
+ struct tevent_fd *fde;
+};
+
+struct resolv_ctx {
+ /* Contexts are linked so we can keep track of them and re-create
+ * the ares channels in all of them at once if we need to. */
+ struct resolv_ctx *prev;
+ struct resolv_ctx *next;
+
+ struct tevent_context *ev_ctx;
+ ares_channel channel;
+
+ /* List of file descriptors that are watched by tevent. */
+ struct fd_watch *fds;
+
+ /* Time in milliseconds before canceling a DNS request */
+ int timeout;
+
+ /* The timeout watcher periodically calls ares_process_fd() to check
+ * if our pending requests didn't timeout. */
+ int pending_requests;
+ struct tevent_timer *timeout_watcher;
+};
+
+struct resolv_ctx *context_list;
+
+static int
+return_code(int ares_code)
+{
+ switch (ares_code) {
+ case ARES_SUCCESS:
+ return EOK;
+ case ARES_ENOMEM:
+ return ENOMEM;
+ case ARES_EFILE:
+ default:
+ return EIO;
+ }
+}
+
+const char *
+resolv_strerror(int ares_code)
+{
+ return ares_strerror(ares_code);
+}
+
+static int
+fd_watch_destructor(struct fd_watch *f)
+{
+ DLIST_REMOVE(f->ctx->fds, f);
+ f->fd = -1;
+
+ return 0;
+}
+
+static void
+fd_input_available(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *data)
+{
+ struct fd_watch *watch = talloc_get_type(data, struct fd_watch);
+
+ if (watch->ctx->channel == NULL) {
+ DEBUG(1, ("Invalid ares channel - this is likely a bug\n"));
+ return;
+ }
+
+ if (flags & TEVENT_FD_READ) {
+ ares_process_fd(watch->ctx->channel, watch->fd, ARES_SOCKET_BAD);
+ }
+ if (flags & TEVENT_FD_WRITE) {
+ ares_process_fd(watch->ctx->channel, ARES_SOCKET_BAD, watch->fd);
+ }
+}
+
+static void
+check_fd_timeouts(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval current_time, void *private_data);
+
+static void
+add_timeout_timer(struct tevent_context *ev, struct resolv_ctx *ctx)
+{
+ struct timeval tv = { 0 };
+ struct timeval *tvp;
+
+ tvp = ares_timeout(ctx->channel, NULL, &tv);
+
+ if (tvp == NULL) {
+ tvp = &tv;
+ }
+
+ /* Enforce a minimum of 1 second. */
+ if (tvp->tv_sec < 1) {
+ tv = tevent_timeval_current_ofs(1, 0);
+ } else {
+ tv = tevent_timeval_current_ofs(tvp->tv_sec, tvp->tv_usec);
+ }
+
+ ctx->timeout_watcher = tevent_add_timer(ev, ctx, tv, check_fd_timeouts,
+ ctx);
+ if (ctx->timeout_watcher == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ }
+}
+
+static void
+check_fd_timeouts(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval current_time, void *private_data)
+{
+ struct resolv_ctx *ctx = talloc_get_type(private_data, struct resolv_ctx);
+
+ DEBUG(9, ("Checking for DNS timeouts\n"));
+
+ /* NULLify the timeout_watcher so we don't
+ * free it in the _done() function if it
+ * gets called. Now that we're already in
+ * the handler, tevent will take care of
+ * freeing it when it returns.
+ */
+ ctx->timeout_watcher = NULL;
+
+ ares_process_fd(ctx->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
+
+ if (ctx->pending_requests > 0) {
+ add_timeout_timer(ev, ctx);
+ }
+}
+
+static void
+schedule_timeout_watcher(struct tevent_context *ev, struct resolv_ctx *ctx)
+{
+ ctx->pending_requests++;
+ if (ctx->timeout_watcher) {
+ return;
+ }
+
+ DEBUG(9, ("Scheduling DNS timeout watcher\n"));
+ add_timeout_timer(ev, ctx);
+}
+
+static void
+unschedule_timeout_watcher(struct resolv_ctx *ctx)
+{
+ if (ctx->pending_requests <= 0) {
+ DEBUG(1, ("Pending DNS requests mismatch\n"));
+ return;
+ }
+
+ ctx->pending_requests--;
+ if (ctx->pending_requests == 0) {
+ DEBUG(9, ("Unscheduling DNS timeout watcher\n"));
+ talloc_zfree(ctx->timeout_watcher);
+ }
+}
+
+static void fd_event_add(struct resolv_ctx *ctx, int s, int flags);
+static void fd_event_close(struct resolv_ctx *ctx, int s);
+
+/*
+ * When ares is ready to read or write to a file descriptor, it will
+ * call this callback. If both read and write are 0, it means that ares
+ * will soon close the socket. We are mainly using this function to register
+ * new file descriptors with tevent.
+ */
+static void
+fd_event(void *data, int s, int fd_read, int fd_write)
+{
+ struct resolv_ctx *ctx = talloc_get_type(data, struct resolv_ctx);
+ struct fd_watch *watch;
+ int flags;
+
+ /* The socket is about to get closed. */
+ if (fd_read == 0 && fd_write == 0) {
+ fd_event_close(ctx, s);
+ return;
+ }
+
+ flags = fd_read ? TEVENT_FD_READ : 0;
+ flags |= fd_write ? TEVENT_FD_WRITE : 0;
+
+ /* Are we already watching this file descriptor? */
+ watch = ctx->fds;
+ while (watch) {
+ if (watch->fd == s) {
+ tevent_fd_set_flags(watch->fde, flags);
+ return;
+ }
+ watch = watch->next;
+ }
+
+ fd_event_add(ctx, s, flags);
+}
+
+static void
+fd_event_add(struct resolv_ctx *ctx, int s, int flags)
+{
+ struct fd_watch *watch;
+
+ /* The file descriptor is new, register it with tevent. */
+ watch = talloc(ctx, struct fd_watch);
+ if (watch == NULL) {
+ DEBUG(1, ("Out of memory allocating fd_watch structure\n"));
+ return;
+ }
+ talloc_set_destructor(watch, fd_watch_destructor);
+
+ watch->fd = s;
+ watch->ctx = ctx;
+
+ watch->fde = tevent_add_fd(ctx->ev_ctx, watch, s, flags,
+ fd_input_available, watch);
+ if (watch->fde == NULL) {
+ DEBUG(1, ("tevent_add_fd() failed\n"));
+ talloc_free(watch);
+ return;
+ }
+ DLIST_ADD(ctx->fds, watch);
+}
+
+static void
+fd_event_close(struct resolv_ctx *ctx, int s)
+{
+ struct fd_watch *watch;
+
+ /* Remove the socket from list */
+ watch = ctx->fds;
+ while (watch) {
+ if (watch->fd == s) {
+ talloc_free(watch);
+ return;
+ }
+ watch = watch->next;
+ }
+}
+
+static int
+resolv_ctx_destructor(struct resolv_ctx *ctx)
+{
+ ares_channel channel;
+
+ DLIST_REMOVE(context_list, ctx);
+
+ if (ctx->channel == NULL) {
+ DEBUG(1, ("Ares channel already destroyed?\n"));
+ return -1;
+ }
+
+ /* Set ctx->channel to NULL first, so that callbacks that get
+ * ARES_EDESTRUCTION won't retry. */
+ channel = ctx->channel;
+ ctx->channel = NULL;
+ ares_destroy(channel);
+
+ return 0;
+}
+
+static int
+recreate_ares_channel(struct resolv_ctx *ctx)
+{
+ int ret;
+ ares_channel new_channel;
+ ares_channel old_channel;
+ struct ares_options options;
+
+ DEBUG(4, ("Initializing new c-ares channel\n"));
+ /* FIXME: the options would contain
+ * the nameservers to contact, the domains
+ * to search, timeout... => get from confdb
+ */
+ options.sock_state_cb = fd_event;
+ options.sock_state_cb_data = ctx;
+ options.timeout = ctx->timeout * 1000;
+ options.tries = 1;
+ ret = ares_init_options(&new_channel, &options,
+ ARES_OPT_SOCK_STATE_CB |
+ ARES_OPT_TIMEOUTMS |
+ ARES_OPT_TRIES);
+ if (ret != ARES_SUCCESS) {
+ DEBUG(1, ("Failed to initialize ares channel: %s\n",
+ resolv_strerror(ret)));
+ return return_code(ret);
+ }
+
+ old_channel = ctx->channel;
+ ctx->channel = new_channel;
+ if (old_channel != NULL) {
+ DEBUG(4, ("Destroying the old c-ares channel\n"));
+ ares_destroy(old_channel);
+ }
+
+ return EOK;
+}
+
+int
+resolv_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx,
+ int timeout, struct resolv_ctx **ctxp)
+{
+ int ret;
+ struct resolv_ctx *ctx;
+
+ ctx = talloc_zero(mem_ctx, struct resolv_ctx);
+ if (ctx == NULL)
+ return ENOMEM;
+
+ ctx->ev_ctx = ev_ctx;
+ ctx->timeout = timeout;
+
+ ret = recreate_ares_channel(ctx);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DLIST_ADD(context_list, ctx);
+ talloc_set_destructor(ctx, resolv_ctx_destructor);
+
+ *ctxp = ctx;
+ return EOK;
+
+done:
+ talloc_free(ctx);
+ return ret;
+}
+
+void
+resolv_reread_configuration(void)
+{
+ struct resolv_ctx *ctx;
+
+ DEBUG(4, ("Recreating all c-ares channels\n"));
+ DLIST_FOR_EACH(ctx, context_list) {
+ recreate_ares_channel(ctx);
+ }
+}
+
+struct hostent *
+resolv_copy_hostent(TALLOC_CTX *mem_ctx, struct hostent *src)
+{
+ struct hostent *ret;
+ int len;
+ int i;
+
+ ret = talloc_zero(mem_ctx, struct hostent);
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ if (src->h_name != NULL) {
+ ret->h_name = talloc_strdup(ret, src->h_name);
+ if (ret->h_name == NULL) {
+ goto fail;
+ }
+ }
+ if (src->h_aliases != NULL) {
+ for (len = 0; src->h_aliases[len] != NULL; len++);
+ ret->h_aliases = talloc_size(ret, sizeof(char *) * (len + 1));
+ if (ret->h_aliases == NULL) {
+ goto fail;
+ }
+ for (i = 0; i < len; i++) {
+ ret->h_aliases[i] = talloc_strdup(ret->h_aliases, src->h_aliases[i]);
+ if (ret->h_aliases[i] == NULL) {
+ goto fail;
+ }
+ }
+ ret->h_aliases[len] = NULL;
+ }
+ ret->h_addrtype = src->h_addrtype;
+ ret->h_length = src->h_length;
+ if (src->h_addr_list != NULL) {
+ for (len = 0; src->h_addr_list[len] != NULL; len++);
+ ret->h_addr_list = talloc_size(ret, sizeof(char *) * (len + 1));
+ if (ret->h_addr_list == NULL) {
+ goto fail;
+ }
+ for (i = 0; i < len; i++) {
+ ret->h_addr_list[i] = talloc_memdup(ret->h_addr_list,
+ src->h_addr_list[i],
+ ret->h_length);
+ if (ret->h_addr_list[i] == NULL) {
+ goto fail;
+ }
+ }
+ ret->h_addr_list[len] = NULL;
+ }
+
+ return ret;
+
+fail:
+ talloc_free(ret);
+ return NULL;
+}
+
+/*******************************************************************
+ * Get host by name. *
+ *******************************************************************/
+
+struct gethostbyname_state {
+ struct resolv_ctx *resolv_ctx;
+ /* Part of the query. */
+ const char *name;
+ int family;
+ /* These are returned by ares. The hostent struct will be freed
+ * when the user callback returns. */
+ struct hostent *hostent;
+ int status;
+ int timeouts;
+ int retrying;
+};
+
+static void
+ares_gethostbyname_wakeup(struct tevent_req *req);
+
+struct tevent_req *
+resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct resolv_ctx *ctx, const char *name)
+{
+ struct tevent_req *req, *subreq;
+ struct gethostbyname_state *state;
+ struct timeval tv = { 0, 0 };
+
+ DEBUG(4, ("Trying to resolve A record of '%s'\n", name));
+
+ if (ctx->channel == NULL) {
+ DEBUG(1, ("Invalid ares channel - this is likely a bug\n"));
+ return NULL;
+ }
+
+ req = tevent_req_create(mem_ctx, &state, struct gethostbyname_state);
+ if (req == NULL)
+ return NULL;
+
+ state->resolv_ctx = ctx;
+ state->name = name;
+ state->family = AF_INET;
+ state->hostent = NULL;
+ state->status = 0;
+ state->timeouts = 0;
+ state->retrying = 0;
+
+ /* We need to have a wrapper around ares_gethostbyname(), because
+ * ares_gethostbyname() can in some cases call it's callback immediately.
+ * This would not let our caller to set a callback for req. */
+ subreq = tevent_wakeup_send(req, ev, tv);
+ if (subreq == NULL) {
+ DEBUG(1, ("Failed to add critical timer to run next operation!\n"));
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, ares_gethostbyname_wakeup, req);
+ schedule_timeout_watcher(ev, ctx);
+
+ return req;
+}
+
+static void
+resolv_gethostbyname6_done(void *arg, int status, int timeouts, struct hostent *hostent);
+
+static void
+resolv_gethostbyname_done(void *arg, int status, int timeouts, struct hostent *hostent)
+{
+ struct tevent_req *req = talloc_get_type(arg, struct tevent_req);
+ struct gethostbyname_state *state = tevent_req_data(req, struct gethostbyname_state);
+
+ if (state->retrying == 0 && status == ARES_EDESTRUCTION
+ && state->resolv_ctx->channel != NULL) {
+ state->retrying = 1;
+ ares_gethostbyname(state->resolv_ctx->channel, state->name,
+ state->family, resolv_gethostbyname_done, req);
+ return;
+ }
+
+ unschedule_timeout_watcher(state->resolv_ctx);
+
+ if (hostent != NULL) {
+ state->hostent = resolv_copy_hostent(req, hostent);
+ if (state->hostent == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ } else {
+ state->hostent = NULL;
+ }
+ state->status = status;
+ state->timeouts = timeouts;
+
+ if (status != ARES_SUCCESS) {
+ if (status == ARES_ENOTFOUND || status == ARES_ENODATA) {
+ /* IPv4 failure. Try IPv6 */
+ state->family = AF_INET6;
+ state->retrying = 0;
+ state->timeouts = 0;
+ DEBUG(4, ("Trying to resolve AAAA record of '%s'\n",
+ state->name));
+ ares_gethostbyname(state->resolv_ctx->channel, state->name,
+ state->family, resolv_gethostbyname6_done,
+ req);
+ return;
+ }
+
+ /* Any other error indicates a server error,
+ * so don't bother trying again
+ */
+ tevent_req_error(req, return_code(status));
+ }
+ else {
+ tevent_req_done(req);
+ }
+}
+
+static void
+resolv_gethostbyname6_done(void *arg, int status, int timeouts, struct hostent *hostent)
+{
+ struct tevent_req *req = talloc_get_type(arg, struct tevent_req);
+ struct gethostbyname_state *state = tevent_req_data(req, struct gethostbyname_state);
+
+ if (state->retrying == 0 && status == ARES_EDESTRUCTION) {
+ state->retrying = 1;
+ ares_gethostbyname(state->resolv_ctx->channel, state->name,
+ state->family, resolv_gethostbyname6_done, req);
+ return;
+ }
+
+ if (hostent != NULL) {
+ state->hostent = resolv_copy_hostent(req, hostent);
+ if (state->hostent == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ } else {
+ state->hostent = NULL;
+ }
+ state->status = status;
+ state->timeouts = timeouts;
+
+ if (status != ARES_SUCCESS) {
+ tevent_req_error(req, return_code(status));
+ }
+ else {
+ tevent_req_done(req);
+ }
+}
+
+int
+resolv_gethostbyname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ int *status, int *timeouts,
+ struct hostent **hostent)
+{
+ struct gethostbyname_state *state = tevent_req_data(req, struct gethostbyname_state);
+
+ /* Fill in even in case of error as status contains the
+ * c-ares return code */
+ if (status) {
+ *status = state->status;
+ }
+ if (timeouts) {
+ *timeouts = state->timeouts;
+ }
+ if (hostent) {
+ *hostent = talloc_steal(mem_ctx, state->hostent);
+ }
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+static void
+ares_gethostbyname_wakeup(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct gethostbyname_state *state = tevent_req_data(req,
+ struct gethostbyname_state);
+
+ if (!tevent_wakeup_recv(subreq)) {
+ return;
+ }
+ talloc_zfree(subreq);
+
+ if (state->resolv_ctx->channel == NULL) {
+ DEBUG(1, ("Invalid ares channel - this is likely a bug\n"));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ ares_gethostbyname(state->resolv_ctx->channel, state->name,
+ state->family, resolv_gethostbyname_done, req);
+}
+
+/* SRV and TXT parsing is not used anywhere in the code yet, so we disable it
+ * for now
+ */
+#ifdef BUILD_TXT_SRV
+
+/*
+ * A simple helper function that will take an array of struct ares_srv_reply that
+ * was allocated by malloc() in c-ares and copies it using talloc. The old one
+ * is freed and the talloc one is put into 'reply_list' instead.
+ */
+static int
+rewrite_talloc_srv_reply(TALLOC_CTX *mem_ctx, struct ares_srv_reply **reply_list)
+{
+ struct ares_srv_reply *ptr = NULL;
+ struct ares_srv_reply *new_list = NULL;
+ struct ares_srv_reply *old_list = *reply_list;
+
+ /* Nothing to do, but not an error */
+ if (!old_list) {
+ return EOK;
+ }
+
+ /* Copy the linked list */
+ while (old_list) {
+ /* Special case for the first node */
+ if (!new_list) {
+ new_list = talloc_zero(mem_ctx, struct ares_srv_reply);
+ if (new_list == NULL) {
+ ares_free_data(*reply_list);
+ return ENOMEM;
+ }
+ ptr = new_list;
+ } else {
+ ptr->next = talloc_zero(new_list, struct ares_srv_reply);
+ if (ptr->next == NULL) {
+ ares_free_data(*reply_list);
+ talloc_free(new_list);
+ return ENOMEM;
+ }
+ ptr = ptr->next;
+ }
+
+ ptr->weight = old_list->weight;
+ ptr->priority = old_list->priority;
+ ptr->port = old_list->port;
+ ptr->host = talloc_strdup(ptr, old_list->host);
+ if (ptr->host == NULL) {
+ ares_free_data(*reply_list);
+ talloc_free(new_list);
+ return ENOMEM;
+ }
+
+ old_list = old_list->next;
+ }
+
+ /* Free the old one (uses malloc). */
+ ares_free_data(*reply_list);
+
+ /* And now put our own new_list in place. */
+ *reply_list = new_list;
+
+ return EOK;
+}
+
+/*******************************************************************
+ * Get SRV record *
+ *******************************************************************/
+
+struct getsrv_state {
+ struct resolv_ctx *resolv_ctx;
+ /* the SRV query - for example _ldap._tcp.example.com */
+ const char *query;
+
+ /* parsed data returned by ares */
+ struct ares_srv_reply *reply_list;
+ int status;
+ int timeouts;
+ int retrying;
+};
+
+static void
+ares_getsrv_wakeup(struct tevent_req *subreq);
+
+struct tevent_req *
+resolv_getsrv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct resolv_ctx *ctx, const char *query)
+{
+ struct tevent_req *req, *subreq;
+ struct getsrv_state *state;
+ struct timeval tv = { 0, 0 };
+
+ DEBUG(4, ("Trying to resolve SRV record of '%s'\n", query));
+
+ if (ctx->channel == NULL) {
+ DEBUG(1, ("Invalid ares channel - this is likely a bug\n"));
+ return NULL;
+ }
+
+ req = tevent_req_create(mem_ctx, &state, struct getsrv_state);
+ if (req == NULL)
+ return NULL;
+
+ state->resolv_ctx = ctx;
+ state->query = query;
+ state->reply_list = NULL;
+ state->status = 0;
+ state->timeouts = 0;
+ state->retrying = 0;
+
+ subreq = tevent_wakeup_send(req, ev, tv);
+ if (subreq == NULL) {
+ DEBUG(1, ("Failed to add critical timer to run next operation!\n"));
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, ares_getsrv_wakeup, req);
+ schedule_timeout_watcher(ev, ctx);
+
+ return req;
+}
+
+static void
+resolv_getsrv_done(void *arg, int status, int timeouts, unsigned char *abuf, int alen)
+{
+ struct tevent_req *req = talloc_get_type(arg, struct tevent_req);
+ struct getsrv_state *state = tevent_req_data(req, struct getsrv_state);
+ int ret;
+ struct ares_srv_reply *reply_list;
+
+ if (state->retrying == 0 && status == ARES_EDESTRUCTION
+ && state->resolv_ctx->channel != NULL) {
+ state->retrying = 1;
+ ares_query(state->resolv_ctx->channel, state->query,
+ ns_c_in, ns_t_srv, resolv_getsrv_done, req);
+ return;
+ }
+
+ unschedule_timeout_watcher(state->resolv_ctx);
+
+ state->status = status;
+ state->timeouts = timeouts;
+
+ if (status != ARES_SUCCESS) {
+ tevent_req_error(req, return_code(status));
+ ret = return_code(status);
+ goto fail;
+ }
+
+ ret = ares_parse_srv_reply(abuf, alen, &reply_list);
+ if (status != ARES_SUCCESS) {
+ DEBUG(2, ("SRV record parsing failed: %d: %s\n", ret, ares_strerror(ret)));
+ ret = return_code(ret);
+ goto fail;
+ }
+ ret = rewrite_talloc_srv_reply(req, &reply_list);
+ if (ret != EOK) {
+ goto fail;
+ }
+ state->reply_list = reply_list;
+
+ tevent_req_done(req);
+ return;
+
+fail:
+ state->reply_list = NULL;
+ tevent_req_error(req, ret);
+}
+
+int
+resolv_getsrv_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, int *status,
+ int *timeouts, struct ares_srv_reply **reply_list)
+{
+ struct getsrv_state *state = tevent_req_data(req, struct getsrv_state);
+
+ if (status)
+ *status = state->status;
+ if (timeouts)
+ *timeouts = state->timeouts;
+ if (reply_list)
+ *reply_list = talloc_steal(mem_ctx, state->reply_list);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+static void
+ares_getsrv_wakeup(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct getsrv_state *state = tevent_req_data(req,
+ struct getsrv_state);
+
+ if (!tevent_wakeup_recv(subreq)) {
+ return;
+ }
+ talloc_zfree(subreq);
+
+ if (state->resolv_ctx->channel == NULL) {
+ DEBUG(1, ("Invalid ares channel - this is likely a bug\n"));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ ares_query(state->resolv_ctx->channel, state->query,
+ ns_c_in, ns_t_srv, resolv_getsrv_done, req);
+}
+
+/*
+ * A simple helper function that will take an array of struct txt_reply that
+ * was allocated by malloc() in c-ares and copies it using talloc. The old one
+ * is freed and the talloc one is put into 'reply_list' instead.
+ */
+static int
+rewrite_talloc_txt_reply(TALLOC_CTX *mem_ctx, struct ares_txt_reply **reply_list)
+{
+ struct ares_txt_reply *ptr = NULL;
+ struct ares_txt_reply *new_list = NULL;
+ struct ares_txt_reply *old_list = *reply_list;
+
+ /* Nothing to do, but not an error */
+ if (!old_list) {
+ return EOK;
+ }
+
+ /* Copy the linked list */
+ while (old_list) {
+
+ /* Special case for the first node */
+ if (!new_list) {
+ new_list = talloc_zero(mem_ctx, struct ares_txt_reply);
+ if (new_list == NULL) {
+ ares_free_data(*reply_list);
+ talloc_free(new_list);
+ return ENOMEM;
+ }
+ ptr = new_list;
+ } else {
+ ptr->next = talloc_zero(new_list, struct ares_txt_reply);
+ if (ptr->next == NULL) {
+ ares_free_data(*reply_list);
+ talloc_free(new_list);
+ return ENOMEM;
+ }
+ ptr = ptr->next;
+ }
+
+ ptr->length = old_list->length;
+ ptr->txt = talloc_memdup(ptr, old_list->txt,
+ old_list->length);
+ if (ptr->txt == NULL) {
+ ares_free_data(*reply_list);
+ talloc_free(new_list);
+ return ENOMEM;
+ }
+
+ old_list = old_list->next;
+ }
+
+ ares_free_data(*reply_list);
+
+ /* And now put our own new_list in place. */
+ *reply_list = new_list;
+
+ return EOK;
+}
+
+/*******************************************************************
+ * Get TXT record *
+ *******************************************************************/
+
+struct gettxt_state {
+ struct resolv_ctx *resolv_ctx;
+ /* the TXT query */
+ const char *query;
+
+ /* parsed data returned by ares */
+ struct ares_txt_reply *reply_list;
+ int status;
+ int timeouts;
+ int retrying;
+};
+
+static void
+ares_gettxt_wakeup(struct tevent_req *subreq);
+
+struct tevent_req *
+resolv_gettxt_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct resolv_ctx *ctx, const char *query)
+{
+ struct tevent_req *req, *subreq;
+ struct gettxt_state *state;
+ struct timeval tv = { 0, 0 };
+
+ DEBUG(4, ("Trying to resolve TXT record of '%s'\n", query));
+
+ if (ctx->channel == NULL) {
+ DEBUG(1, ("Invalid ares channel - this is likely a bug\n"));
+ return NULL;
+ }
+
+ req = tevent_req_create(mem_ctx, &state, struct gettxt_state);
+ if (req == NULL)
+ return NULL;
+
+ state->resolv_ctx = ctx;
+ state->query = query;
+ state->reply_list = NULL;
+ state->status = 0;
+ state->timeouts = 0;
+ state->retrying = 0;
+
+ subreq = tevent_wakeup_send(req, ev, tv);
+ if (subreq == NULL) {
+ DEBUG(1, ("Failed to add critical timer to run next operation!\n"));
+ talloc_zfree(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, ares_gettxt_wakeup, req);
+ schedule_timeout_watcher(ev, ctx);
+
+ return req;
+}
+
+static void
+resolv_gettxt_done(void *arg, int status, int timeouts, unsigned char *abuf, int alen)
+{
+ struct tevent_req *req = talloc_get_type(arg, struct tevent_req);
+ struct gettxt_state *state = tevent_req_data(req, struct gettxt_state);
+ int ret;
+ struct ares_txt_reply *reply_list;
+
+ if (state->retrying == 0 && status == ARES_EDESTRUCTION
+ && state->resolv_ctx->channel != NULL) {
+ state->retrying = 1;
+ ares_query(state->resolv_ctx->channel, state->query,
+ ns_c_in, ns_t_txt, resolv_gettxt_done, req);
+ return;
+ }
+
+ unschedule_timeout_watcher(state->resolv_ctx);
+
+ state->status = status;
+ state->timeouts = timeouts;
+
+ if (status != ARES_SUCCESS) {
+ ret = return_code(status);
+ goto fail;
+ }
+
+ ret = ares_parse_txt_reply(abuf, alen, &reply_list);
+ if (status != ARES_SUCCESS) {
+ DEBUG(2, ("TXT record parsing failed: %d: %s\n", ret, ares_strerror(ret)));
+ ret = return_code(ret);
+ goto fail;
+ }
+ ret = rewrite_talloc_txt_reply(req, &reply_list);
+ if (ret != EOK) {
+ goto fail;
+ }
+ state->reply_list = reply_list;
+
+ tevent_req_done(req);
+ return;
+
+fail:
+ state->reply_list = NULL;
+ tevent_req_error(req, ret);
+}
+
+int
+resolv_gettxt_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, int *status,
+ int *timeouts, struct ares_txt_reply **reply_list)
+{
+ struct gettxt_state *state = tevent_req_data(req, struct gettxt_state);
+
+ if (status)
+ *status = state->status;
+ if (timeouts)
+ *timeouts = state->timeouts;
+ if (reply_list)
+ *reply_list = talloc_steal(mem_ctx, state->reply_list);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+static void
+ares_gettxt_wakeup(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct gettxt_state *state = tevent_req_data(req,
+ struct gettxt_state);
+
+ if (!tevent_wakeup_recv(subreq)) {
+ return;
+ }
+ talloc_zfree(subreq);
+
+ if (state->resolv_ctx->channel == NULL) {
+ DEBUG(1, ("Invalid ares channel - this is likely a bug\n"));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ ares_query(state->resolv_ctx->channel, state->query,
+ ns_c_in, ns_t_txt, resolv_gettxt_done, req);
+}
+
+#endif
diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h
new file mode 100644
index 00000000..2ba6449b
--- /dev/null
+++ b/src/resolv/async_resolv.h
@@ -0,0 +1,95 @@
+/*
+ SSSD
+
+ Async resolver header
+
+ Authors:
+ Martin Nagy <mnagy@redhat.com>
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) Red Hat, Inc 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __ASYNC_RESOLV_H__
+#define __ASYNC_RESOLV_H__
+
+#include <netdb.h>
+#include <ares.h>
+
+#include "config.h"
+
+#ifndef HAVE_ARES_DATA
+#include "resolv/ares/ares_parse_srv_reply.h"
+#include "resolv/ares/ares_parse_txt_reply.h"
+#include "resolv/ares/ares_data.h"
+#endif /* HAVE_ARES_DATA */
+
+/*
+ * An opaque structure which holds context for a module using the async
+ * resolver. Is should be used as a "local-global" variable - in sssd,
+ * every backend should have its own.
+
+ * Do NOT free the context until there are any pending resolv_ calls
+ */
+struct resolv_ctx;
+
+int resolv_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx,
+ int timeout, struct resolv_ctx **ctxp);
+
+void resolv_reread_configuration(void);
+
+const char *resolv_strerror(int ares_code);
+
+struct hostent *resolv_copy_hostent(TALLOC_CTX *mem_ctx,
+ struct hostent *src);
+
+/** Get host by name **/
+struct tevent_req *resolv_gethostbyname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resolv_ctx *ctx,
+ const char *name);
+
+int resolv_gethostbyname_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ int *status,
+ int *timeouts,
+ struct hostent **hostent);
+
+/** Get SRV record **/
+struct tevent_req *resolv_getsrv_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resolv_ctx *ctx,
+ const char *query);
+
+int resolv_getsrv_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ int *status,
+ int *timeouts,
+ struct ares_srv_reply **reply_list);
+
+/** Get TXT record **/
+struct tevent_req *resolv_gettxt_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resolv_ctx *ctx,
+ const char *query);
+
+int resolv_gettxt_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ int *status,
+ int *timeouts,
+ struct ares_txt_reply **reply_list);
+
+#endif /* __ASYNC_RESOLV_H__ */