From 0c1c70e1f60d4263b93d63ba0179a998c3fcf554 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Fri, 27 Nov 2009 09:39:41 +0100 Subject: Import ares 1.7.0 helpers --- server/Makefile.am | 11 +-- server/external/libcares.m4 | 21 ++--- server/resolv/ares/ares_data.c | 140 ++++++++++++++++++++++++++++++ server/resolv/ares/ares_data.h | 68 +++++++++++++++ server/resolv/ares/ares_parse_srv_reply.c | 88 ++++++++++--------- server/resolv/ares/ares_parse_srv_reply.h | 13 +-- server/resolv/ares/ares_parse_txt_reply.c | 119 +++++++++++++++++-------- server/resolv/ares/ares_parse_txt_reply.h | 9 +- server/resolv/async_resolv.c | 11 +-- server/resolv/async_resolv.h | 10 +-- 10 files changed, 374 insertions(+), 116 deletions(-) create mode 100644 server/resolv/ares/ares_data.c create mode 100644 server/resolv/ares/ares_data.h diff --git a/server/Makefile.am b/server/Makefile.am index fdaefea2a..9814d4f64 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -211,11 +211,11 @@ SSSD_TOOLS_OBJ = \ SSSD_RESOLV_OBJ = \ resolv/async_resolv.c -if BUILD_ARES_PARSE_SRV - SSSD_RESOLV_OBJ += resolv/ares/ares_parse_srv_reply.c -endif -if BUILD_ARES_PARSE_TXT - SSSD_RESOLV_OBJ += resolv/ares/ares_parse_txt_reply.c +if BUILD_ARES_DATA + SSSD_RESOLV_OBJ += \ + resolv/ares/ares_parse_srv_reply.c \ + resolv/ares/ares_parse_txt_reply.c \ + resolv/ares/ares_data.c endif SSSD_FAILOVER_OBJ = \ @@ -311,6 +311,7 @@ dist_noinst_HEADERS = \ resolv/async_resolv.h \ resolv/ares/ares_parse_srv_reply.h \ resolv/ares/ares_parse_txt_reply.h \ + resolv/ares/ares_data.h \ tests/common.h diff --git a/server/external/libcares.m4 b/server/external/libcares.m4 index 020a1708d..657deac59 100644 --- a/server/external/libcares.m4 +++ b/server/external/libcares.m4 @@ -7,25 +7,14 @@ AC_CHECK_HEADERS(ares.h, [AC_MSG_ERROR([c-ares header files are not installed])] ) -dnl Check if this particular version of c-ares supports parsing of SRV records +dnl Check if this particular version of c-ares supports the generic ares_free_data function AC_CHECK_LIB([cares], - [ares_parse_srv_reply], - [AC_DEFINE([HAVE_ARES_PARSE_SRV], 1, [Does c-ares support srv parsing?]) + [ares_free_data], + [AC_DEFINE([HAVE_ARES_DATA], 1, [Does c-ares have ares_free_data()?]) ], [ - ares_build_srv=1 + ares_data=1 ] ) -dnl Check if this particular version of c-ares supports parsing of TXT records -AC_CHECK_LIB([cares], - [ares_parse_txt_reply], - [AC_DEFINE([HAVE_ARES_PARSE_TXT], 1, [Does c-ares support txt parsing?]) - ], - [ - ares_build_txt=1 - ] -) - -AM_CONDITIONAL(BUILD_ARES_PARSE_SRV, test x$ares_build_srv = x1) -AM_CONDITIONAL(BUILD_ARES_PARSE_TXT, test x$ares_build_txt = x1) +AM_CONDITIONAL(BUILD_ARES_DATA, test x$ares_data = x1) diff --git a/server/resolv/ares/ares_data.c b/server/resolv/ares/ares_data.c new file mode 100644 index 000000000..1cccaa55c --- /dev/null +++ b/server/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 +#include + +#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/server/resolv/ares/ares_data.h b/server/resolv/ares/ares_data.h new file mode 100644 index 000000000..d3606314e --- /dev/null +++ b/server/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/server/resolv/ares/ares_parse_srv_reply.c b/server/resolv/ares/ares_parse_srv_reply.c index 9745fb07a..086c4dba0 100644 --- a/server/resolv/ares/ares_parse_srv_reply.c +++ b/server/resolv/ares/ares_parse_srv_reply.c @@ -51,25 +51,24 @@ #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 srv_reply **srv_out, int *nsrvreply) + struct ares_srv_reply **srv_out) { - unsigned int qdcount, ancount; - const unsigned char *aptr; - int status, i, rr_type, rr_class, rr_len; + 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 srv_reply *srv = 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. */ - if (srv_out) - *srv_out = NULL; - /* Same with *nsrvreply. */ - if (nsrvreply) - *nsrvreply = 0; + *srv_out = NULL; /* Give up if abuf doesn't have room for a header. */ if (alen < HFIXEDSZ) @@ -96,14 +95,6 @@ int _ares_parse_srv_reply (const unsigned char *abuf, int alen, } aptr += len + QFIXEDSZ; - /* Allocate srv_reply array; ancount gives an upper bound */ - srv = malloc ((ancount) * sizeof (struct srv_reply)); - if (!srv) - { - free (hostname); - return ARES_ENOMEM; - } - /* Examine each answer resource record (RR) in turn. */ for (i = 0; i < (int) ancount; i++) { @@ -134,40 +125,59 @@ int _ares_parse_srv_reply (const unsigned char *abuf, int alen, break; } - srv[i].priority = ntohs (*((const uint16_t *)aptr)); - aptr += sizeof(uint16_t); - srv[i].weight = ntohs (*((const uint16_t *)aptr)); - aptr += sizeof(uint16_t); - srv[i].port = ntohs (*((const uint16_t *)aptr)); - aptr += sizeof(uint16_t); + /* 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 (aptr, abuf, alen, &srv[i].host, &len); + status = ares_expand_name (vptr, abuf, alen, &srv_curr->host, &len); if (status != ARES_SUCCESS) break; + } - /* Move on to the next record */ - aptr += len; + /* Don't lose memory in the next iteration */ + free(rr_name); + rr_name = NULL; - /* 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) { - free (srv); - free (hostname); - free (rr_name); + if (srv_head) + _ares_free_data (srv_head); return status; } /* everything looks fine, return the data */ - *srv_out = srv; - *nsrvreply = ancount; + *srv_out = srv_head; - free (hostname); - free (rr_name); - return status; + return ARES_SUCCESS; } diff --git a/server/resolv/ares/ares_parse_srv_reply.h b/server/resolv/ares/ares_parse_srv_reply.h index caf93cd2b..29c6e08d1 100644 --- a/server/resolv/ares/ares_parse_srv_reply.h +++ b/server/resolv/ares/ares_parse_srv_reply.h @@ -21,14 +21,15 @@ #ifndef __ARES_PARSE_SRV_REPLY_H__ #define __ARES_PARSE_SRV_REPLY_H__ -struct srv_reply { - u_int16_t weight; - u_int16_t priority; - u_int16_t port; - char *host; +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 srv_reply **srv_out, int *nsrvreply); + struct ares_srv_reply **srv_out); #endif /* __ARES_PARSE_SRV_REPLY_H__ */ diff --git a/server/resolv/ares/ares_parse_txt_reply.c b/server/resolv/ares/ares_parse_txt_reply.c index feb6af23d..d710e8f98 100644 --- a/server/resolv/ares/ares_parse_txt_reply.c +++ b/server/resolv/ares/ares_parse_txt_reply.c @@ -50,21 +50,26 @@ #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 txt_reply **txt_out, int *ntxtreply) +int _ares_parse_txt_reply (const unsigned char *abuf, int alen, + struct ares_txt_reply **txt_out) { - unsigned int qdcount, ancount; + size_t substr_len, str_len; + unsigned int qdcount, ancount, i; const unsigned char *aptr; - int status, i, rr_type, rr_class, rr_len; + const unsigned char *strptr; + int status, rr_type, rr_class, rr_len; long len; char *hostname = NULL, *rr_name = NULL; - struct txt_reply *txt = NULL; + struct ares_txt_reply *txt_head = NULL; + struct ares_txt_reply *txt_last = NULL; + struct ares_txt_reply *txt_curr; - if (txt_out) - *txt_out = NULL; + /* 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) @@ -91,29 +96,21 @@ int _ares_parse_txt_reply(const unsigned char* abuf, int alen, } aptr += len + QFIXEDSZ; - /* Allocate txt_reply array; ancount gives an upper bound */ - txt = malloc ((ancount) * sizeof (struct txt_reply)); - if (!txt) - { - free (hostname); - return ARES_ENOMEM; - } - /* 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; - } + { + break; + } aptr += len; if (aptr + RRFIXEDSZ > abuf + alen) - { - status = ARES_EBADRESP; - break; - } + { + status = ARES_EBADRESP; + break; + } rr_type = DNS_RR_TYPE(aptr); rr_class = DNS_RR_CLASS(aptr); rr_len = DNS_RR_LEN(aptr); @@ -121,37 +118,87 @@ int _ares_parse_txt_reply(const unsigned char* abuf, int alen, /* Check if we are really looking at a TXT record */ if (rr_class == C_IN && rr_type == T_TXT) - { - /* Grab the TXT payload */ - txt[i].length = rr_len; - txt[i].txt = malloc(sizeof(unsigned char) * rr_len); - if (txt[i].txt == NULL) + { + /* 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; } - memcpy((void *) txt[i].txt, aptr+1, sizeof(unsigned char) * rr_len); - /* Move on to the next record */ - aptr += rr_len; + 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; } - free(hostname); - free(rr_name); + if (hostname) + free (hostname); + if (rr_name) + free (rr_name); /* clean up on error */ if (status != ARES_SUCCESS) { - free (txt); + if (txt_head) + _ares_free_data (txt_head); return status; } /* everything looks fine, return the data */ - *txt_out = txt; - *ntxtreply = ancount; - return 0; + *txt_out = txt_head; + + return ARES_SUCCESS; } diff --git a/server/resolv/ares/ares_parse_txt_reply.h b/server/resolv/ares/ares_parse_txt_reply.h index 9fd42787e..216e2c0d1 100644 --- a/server/resolv/ares/ares_parse_txt_reply.h +++ b/server/resolv/ares/ares_parse_txt_reply.h @@ -21,12 +21,13 @@ #ifndef __ARES_PARSE_TXT_REPLY_H__ #define __ARES_PARSE_TXT_REPLY_H__ -struct txt_reply { - int length; /* length of the text */ - unsigned char *txt; /* may contain nulls */ +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 txt_reply **txt_out, int *ntxtreply); + struct ares_txt_reply **txt_out); #endif /* __ARES_PARSE_TXT_REPLY_H__ */ diff --git a/server/resolv/async_resolv.c b/server/resolv/async_resolv.c index be2107a22..afad4107b 100644 --- a/server/resolv/async_resolv.c +++ b/server/resolv/async_resolv.c @@ -42,15 +42,16 @@ #include "util/dlinklist.h" #include "util/util.h" -#ifndef HAVE_ARES_PARSE_SRV +#ifndef HAVE_ARES_DATA #define ares_parse_srv_reply(abuf, alen, srv_out) \ _ares_parse_srv_reply(abuf, alen, srv_out) -#endif /* HAVE_ARES_PARSE_SRV */ - -#ifndef HAVE_ARES_PARSE_TXT #define ares_parse_txt_reply(abuf, alen, txt_out) \ _ares_parse_txt_reply(abuf, alen, txt_out) -#endif /* HAVE_ARES_PARSE_TXT */ +#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; diff --git a/server/resolv/async_resolv.h b/server/resolv/async_resolv.h index 9d080ec69..dab2fdfc4 100644 --- a/server/resolv/async_resolv.h +++ b/server/resolv/async_resolv.h @@ -29,13 +29,13 @@ #include #include -#ifndef HAVE_ARES_PARSE_TXT -#include "resolv/ares/ares_parse_txt_reply.h" -#endif /* HAVE_ARES_PARSE_TXT */ +#include "config.h" -#ifndef HAVE_ARES_PARSE_SRV +#ifndef HAVE_ARES_DATA #include "resolv/ares/ares_parse_srv_reply.h" -#endif /* HAVE_ARES_PARSE_SRV */ +#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 -- cgit