LCOV - code coverage report
Current view: top level - resolv - async_resolv.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 619 1001 61.8 %
Date: 2015-10-19 Functions: 48 60 80.0 %

          Line data    Source code
       1             : /*
       2             :    SSSD
       3             : 
       4             :    Async resolver
       5             : 
       6             :    Authors:
       7             :         Martin Nagy <mnagy@redhat.com>
       8             :         Jakub Hrozek <jhrozek@redhat.com>
       9             : 
      10             :    Copyright (C) Red Hat, Inc 2009
      11             : 
      12             :    This program is free software; you can redistribute it and/or modify
      13             :    it under the terms of the GNU General Public License as published by
      14             :    the Free Software Foundation; either version 3 of the License, or
      15             :    (at your option) any later version.
      16             : 
      17             :    This program is distributed in the hope that it will be useful,
      18             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      19             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      20             :    GNU General Public License for more details.
      21             : 
      22             :    You should have received a copy of the GNU General Public License
      23             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      24             : */
      25             : 
      26             : #include <sys/select.h>
      27             : #include <arpa/inet.h>
      28             : #include <arpa/nameser.h>
      29             : 
      30             : #include <ares.h>
      31             : #include <talloc.h>
      32             : #include <tevent.h>
      33             : 
      34             : #include <errno.h>
      35             : #include <netdb.h>
      36             : #include <stddef.h>
      37             : #include <string.h>
      38             : #include <unistd.h>
      39             : 
      40             : #include "config.h"
      41             : #include "resolv/async_resolv.h"
      42             : #include "util/dlinklist.h"
      43             : #include "util/util.h"
      44             : 
      45             : #define DNS__16BIT(p)                   (((p)[0] << 8) | (p)[1])
      46             : 
      47             : /*
      48             :  * Macro DNS__32BIT reads a network long (32 bit) given in network
      49             :  * byte order, and returns its value as an unsigned int. Copied
      50             :  * from c-ares source code.
      51             :  */
      52             : #define DNS__32BIT(p)  ((unsigned int) \
      53             :                          (((unsigned int)((unsigned char)(p)[0]) << 24U) | \
      54             :                           ((unsigned int)((unsigned char)(p)[1]) << 16U) | \
      55             :                           ((unsigned int)((unsigned char)(p)[2]) <<  8U) | \
      56             :                           ((unsigned int)((unsigned char)(p)[3]))))
      57             : 
      58             : #define DNS_HEADER_ANCOUNT(h)           DNS__16BIT((h) + 6)
      59             : #define DNS_RR_LEN(r)                   DNS__16BIT((r) + 8)
      60             : #define DNS_RR_TTL(r)                   DNS__32BIT((r) + 4)
      61             : 
      62             : #define RESOLV_TIMEOUTMS  2000
      63             : 
      64             : enum host_database default_host_dbs[] = { DB_FILES, DB_DNS, DB_SENTINEL };
      65             : 
      66             : struct fd_watch {
      67             :     struct fd_watch *prev;
      68             :     struct fd_watch *next;
      69             : 
      70             :     int fd;
      71             :     struct resolv_ctx *ctx;
      72             :     struct tevent_fd *fde;
      73             : };
      74             : 
      75             : struct resolv_ctx {
      76             :     struct tevent_context *ev_ctx;
      77             :     ares_channel channel;
      78             : 
      79             :     /* List of file descriptors that are watched by tevent. */
      80             :     struct fd_watch *fds;
      81             : 
      82             :     /* Time in milliseconds before canceling a DNS request */
      83             :     int timeout;
      84             : 
      85             :     /* The timeout watcher periodically calls ares_process_fd() to check
      86             :      * if our pending requests didn't timeout. */
      87             :     int pending_requests;
      88             :     struct tevent_timer *timeout_watcher;
      89             : };
      90             : 
      91             : struct request_watch {
      92             :     struct tevent_req *req;
      93             :     struct resolv_request *rr;
      94             : };
      95             : 
      96             : struct resolv_request {
      97             :     struct resolv_ctx *ctx;
      98             :     struct request_watch *rwatch;
      99             :     struct tevent_timer *request_timeout;
     100             : };
     101             : 
     102             : static int
     103           2 : return_code(int ares_code)
     104             : {
     105           2 :     switch (ares_code) {
     106             :     case ARES_SUCCESS:
     107           2 :         return EOK;
     108             :     case ARES_ENOMEM:
     109           0 :         return ENOMEM;
     110             :     case ARES_EFILE:
     111             :     default:
     112           0 :         return EIO;
     113             :     }
     114             : }
     115             : 
     116             : const char *
     117           0 : resolv_strerror(int ares_code)
     118             : {
     119           0 :     return ares_strerror(ares_code);
     120             : }
     121             : 
     122             : static int
     123           2 : fd_watch_destructor(struct fd_watch *f)
     124             : {
     125           2 :     DLIST_REMOVE(f->ctx->fds, f);
     126           2 :     f->fd = -1;
     127             : 
     128           2 :     return 0;
     129             : }
     130             : 
     131             : static void
     132           2 : fd_input_available(struct tevent_context *ev, struct tevent_fd *fde,
     133             :                    uint16_t flags, void *data)
     134             : {
     135           2 :     struct fd_watch *watch = talloc_get_type(data, struct fd_watch);
     136             : 
     137           2 :     if (watch->ctx->channel == NULL) {
     138           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     139             :               "Invalid ares channel - this is likely a bug\n");
     140           0 :         return;
     141             :     }
     142             : 
     143           2 :     if (flags & TEVENT_FD_READ) {
     144           2 :         ares_process_fd(watch->ctx->channel, watch->fd, ARES_SOCKET_BAD);
     145             :     }
     146           2 :     if (flags & TEVENT_FD_WRITE) {
     147           0 :         ares_process_fd(watch->ctx->channel, ARES_SOCKET_BAD, watch->fd);
     148             :     }
     149             : }
     150             : 
     151             : static void
     152             : check_fd_timeouts(struct tevent_context *ev, struct tevent_timer *te,
     153             :                   struct timeval current_time, void *private_data);
     154             : 
     155             : static void
     156           3 : add_timeout_timer(struct tevent_context *ev, struct resolv_ctx *ctx)
     157             : {
     158           3 :     struct timeval tv = { 0, 0 };
     159             :     struct timeval *tvp;
     160             : 
     161           3 :     if (ctx->timeout_watcher) {
     162           0 :         return;
     163             :     }
     164             : 
     165           3 :     tvp = ares_timeout(ctx->channel, NULL, &tv);
     166             : 
     167           3 :     if (tvp == NULL) {
     168           3 :         tvp = &tv;
     169             :     }
     170             : 
     171             :     /* Enforce a minimum of 1 second. */
     172           3 :     if (tvp->tv_sec < 1) {
     173           3 :         tv = tevent_timeval_current_ofs(1, 0);
     174             :     } else {
     175           0 :         tv = tevent_timeval_current_ofs(tvp->tv_sec, tvp->tv_usec);
     176             :     }
     177             : 
     178           3 :     ctx->timeout_watcher = tevent_add_timer(ev, ctx, tv, check_fd_timeouts,
     179             :                                             ctx);
     180           3 :     if (ctx->timeout_watcher == NULL) {
     181           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory\n");
     182             :     }
     183             : }
     184             : 
     185             : static void
     186           0 : check_fd_timeouts(struct tevent_context *ev, struct tevent_timer *te,
     187             :                   struct timeval current_time, void *private_data)
     188             : {
     189           0 :     struct resolv_ctx *ctx = talloc_get_type(private_data, struct resolv_ctx);
     190             : 
     191           0 :     DEBUG(SSSDBG_TRACE_ALL, "Checking for DNS timeouts\n");
     192             : 
     193             :     /* NULLify the timeout_watcher so we don't
     194             :      * free it in the _done() function if it
     195             :      * gets called. Now that we're already in
     196             :      * the handler, tevent will take care of
     197             :      * freeing it when it returns.
     198             :      */
     199           0 :     ctx->timeout_watcher = NULL;
     200             : 
     201           0 :     ares_process_fd(ctx->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
     202             : 
     203           0 :     if (ctx->pending_requests > 0) {
     204           0 :         add_timeout_timer(ev, ctx);
     205             :     }
     206           0 : }
     207             : 
     208             : static void
     209           0 : resolv_request_timeout(struct tevent_context *ev,
     210             :                        struct tevent_timer *te,
     211             :                        struct timeval tv, void *pvt)
     212             : {
     213             :     struct resolv_request *rreq;
     214             : 
     215           0 :     DEBUG(SSSDBG_MINOR_FAILURE, "The resolve request timed out\n");
     216             : 
     217           0 :     rreq = talloc_get_type(pvt, struct resolv_request);
     218           0 :     if (rreq->rwatch == NULL) {
     219           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "The request already completed\n");
     220           0 :         return;
     221             :     }
     222             : 
     223           0 :     tevent_req_error(rreq->rwatch->req, ETIMEDOUT);
     224           0 :     rreq->rwatch = NULL;
     225             : }
     226             : 
     227             : static int
     228           3 : request_watch_destructor(struct request_watch *rwatch)
     229             : {
     230           3 :     DEBUG(SSSDBG_TRACE_FUNC, "Deleting request watch\n");
     231           3 :     if (rwatch->rr) rwatch->rr->rwatch = NULL;
     232           3 :     return 0;
     233             : }
     234             : 
     235             : static struct resolv_request *
     236           3 : schedule_request_timeout(struct tevent_context *ev, struct resolv_ctx *ctx,
     237             :                          struct tevent_req *req)
     238             : {
     239             :     struct resolv_request *rreq;
     240             :     struct timeval tv;
     241             : 
     242           3 :     DEBUG(SSSDBG_TRACE_INTERNAL, "Scheduling a timeout of %d seconds\n",
     243             :                                   ctx->timeout);
     244           3 :     tv = tevent_timeval_current_ofs(ctx->timeout, 0);
     245             : 
     246             :     /* Intentionally allocating on ctx, because the request might go away
     247             :      * before c-ares returns */
     248           3 :     rreq = talloc(ctx, struct resolv_request);
     249           3 :     if (!rreq) {
     250           0 :         talloc_zfree(req);
     251           0 :         return NULL;
     252             :     }
     253           3 :     rreq->ctx = ctx;
     254           3 :     rreq->request_timeout = tevent_add_timer(ev, rreq, tv,
     255             :                                              resolv_request_timeout,
     256             :                                              rreq);
     257           3 :     if (rreq->request_timeout == NULL) {
     258           0 :         talloc_free(rreq);
     259           0 :         return NULL;
     260             :     }
     261             : 
     262             :     /* The watch will go away when the request finishes */
     263           3 :     rreq->rwatch = talloc(req, struct request_watch);
     264           3 :     if (!rreq->rwatch) {
     265           0 :         talloc_zfree(req);
     266           0 :         return NULL;
     267             :     }
     268             : 
     269           3 :     rreq->rwatch->req = req;
     270           3 :     rreq->rwatch->rr = rreq;
     271           3 :     talloc_set_destructor(rreq->rwatch, request_watch_destructor);
     272             : 
     273           3 :     return rreq;
     274             : }
     275             : 
     276             : static struct resolv_request *
     277           3 : schedule_timeout_watcher(struct tevent_context *ev, struct resolv_ctx *ctx,
     278             :                          struct tevent_req *req)
     279             : {
     280             :     struct resolv_request *rreq;
     281             : 
     282           3 :     rreq = schedule_request_timeout(ev, ctx, req);
     283           3 :     if (!rreq) return NULL;
     284             : 
     285           3 :     ctx->pending_requests++;
     286             : 
     287           3 :     DEBUG(SSSDBG_TRACE_INTERNAL, "Scheduling DNS timeout watcher\n");
     288           3 :     add_timeout_timer(ev, ctx);
     289           3 :     return rreq;
     290             : }
     291             : 
     292             : static void
     293           3 : unschedule_timeout_watcher(struct resolv_ctx *ctx, struct resolv_request *rreq)
     294             : {
     295             :     /* Unlink the watch if the request is still active */
     296           3 :     if (rreq->rwatch) {
     297           3 :         rreq->rwatch->rr = NULL;
     298             :     }
     299           3 :     talloc_free(rreq); /* Cancels the tevent timeout as well */
     300             : 
     301           3 :     if (ctx->pending_requests <= 0) {
     302           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Pending DNS requests mismatch\n");
     303           0 :         return;
     304             :     }
     305             : 
     306           3 :     ctx->pending_requests--;
     307           3 :     if (ctx->pending_requests == 0) {
     308           3 :         DEBUG(SSSDBG_TRACE_ALL, "Unscheduling DNS timeout watcher\n");
     309           3 :         talloc_zfree(ctx->timeout_watcher);
     310             :     }
     311             : }
     312             : 
     313             : static void fd_event_add(struct resolv_ctx *ctx, int s, int flags);
     314             : static void fd_event_close(struct resolv_ctx *ctx, int s);
     315             : 
     316             : /*
     317             :  * When ares is ready to read or write to a file descriptor, it will
     318             :  * call this callback. If both read and write are 0, it means that ares
     319             :  * will soon close the socket. We are mainly using this function to register
     320             :  * new file descriptors with tevent.
     321             :  */
     322             : static void
     323           4 : fd_event(void *data, int s, int fd_read, int fd_write)
     324             : {
     325           4 :     struct resolv_ctx *ctx = talloc_get_type(data, struct resolv_ctx);
     326             :     struct fd_watch *watch;
     327             :     int flags;
     328             : 
     329             :     /* The socket is about to get closed. */
     330           4 :     if (fd_read == 0 && fd_write == 0) {
     331           2 :         fd_event_close(ctx, s);
     332           2 :         return;
     333             :     }
     334             : 
     335           2 :     flags = fd_read ? TEVENT_FD_READ : 0;
     336           2 :     flags |= fd_write ? TEVENT_FD_WRITE : 0;
     337             : 
     338             :     /* Are we already watching this file descriptor? */
     339           2 :     watch = ctx->fds;
     340           4 :     while (watch) {
     341           0 :         if (watch->fd == s) {
     342           0 :             tevent_fd_set_flags(watch->fde, flags);
     343           0 :             return;
     344             :         }
     345           0 :         watch = watch->next;
     346             :     }
     347             : 
     348           2 :     fd_event_add(ctx, s, flags);
     349             : }
     350             : 
     351             : static void
     352           2 : fd_event_add(struct resolv_ctx *ctx, int s, int flags)
     353             : {
     354             :     struct fd_watch *watch;
     355             : 
     356             :     /* The file descriptor is new, register it with tevent. */
     357           2 :     watch = talloc(ctx, struct fd_watch);
     358           2 :     if (watch == NULL) {
     359           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     360             :               "Out of memory allocating fd_watch structure\n");
     361           0 :         return;
     362             :     }
     363           2 :     talloc_set_destructor(watch, fd_watch_destructor);
     364             : 
     365           2 :     watch->fd = s;
     366           2 :     watch->ctx = ctx;
     367             : 
     368           2 :     watch->fde = tevent_add_fd(ctx->ev_ctx, watch, s, flags,
     369             :                                fd_input_available, watch);
     370           2 :     if (watch->fde == NULL) {
     371           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_fd() failed\n");
     372           0 :         talloc_free(watch);
     373           0 :         return;
     374             :     }
     375           2 :     DLIST_ADD(ctx->fds, watch);
     376             : }
     377             : 
     378             : static void
     379           2 : fd_event_close(struct resolv_ctx *ctx, int s)
     380             : {
     381             :     struct fd_watch *watch;
     382             : 
     383             :     /* Remove the socket from list */
     384           2 :     watch = ctx->fds;
     385           4 :     while (watch) {
     386           2 :         if (watch->fd == s) {
     387           2 :             talloc_free(watch);
     388           2 :             return;
     389             :         }
     390           0 :         watch = watch->next;
     391             :     }
     392             : }
     393             : 
     394             : static int
     395          15 : resolv_ctx_destructor(struct resolv_ctx *ctx)
     396             : {
     397             :     ares_channel channel;
     398             : 
     399          15 :     if (ctx->channel == NULL) {
     400           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Ares channel already destroyed?\n");
     401           0 :         return -1;
     402             :     }
     403             : 
     404             :     /* Set ctx->channel to NULL first, so that callbacks that get
     405             :      * ARES_EDESTRUCTION won't retry. */
     406          15 :     channel = ctx->channel;
     407          15 :     ctx->channel = NULL;
     408          15 :     ares_destroy(channel);
     409             : 
     410          15 :     return 0;
     411             : }
     412             : 
     413             : static int
     414          15 : recreate_ares_channel(struct resolv_ctx *ctx)
     415             : {
     416             :     int ret;
     417             :     ares_channel new_channel;
     418             :     ares_channel old_channel;
     419             :     struct ares_options options;
     420             : 
     421          15 :     DEBUG(SSSDBG_CONF_SETTINGS, "Initializing new c-ares channel\n");
     422             :     /* FIXME: the options would contain
     423             :      * the nameservers to contact, the domains
     424             :      * to search... => get from confdb
     425             :      */
     426          15 :     options.sock_state_cb = fd_event;
     427          15 :     options.sock_state_cb_data = ctx;
     428          15 :     options.timeout = RESOLV_TIMEOUTMS;
     429             :     /* Only affects ares_gethostbyname */
     430          15 :     options.lookups = discard_const("f");
     431          15 :     options.tries = 1;
     432          15 :     ret = ares_init_options(&new_channel, &options,
     433             :                             ARES_OPT_SOCK_STATE_CB |
     434             :                             ARES_OPT_TIMEOUTMS |
     435             :                             ARES_OPT_LOOKUPS |
     436             :                             ARES_OPT_TRIES);
     437          15 :     if (ret != ARES_SUCCESS) {
     438           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to initialize ares channel: %s\n",
     439             :                   resolv_strerror(ret));
     440           0 :         return return_code(ret);
     441             :     }
     442             : 
     443          15 :     old_channel = ctx->channel;
     444          15 :     ctx->channel = new_channel;
     445          15 :     if (old_channel != NULL) {
     446           0 :         DEBUG(SSSDBG_CONF_SETTINGS, "Destroying the old c-ares channel\n");
     447           0 :         ares_destroy(old_channel);
     448             :     }
     449             : 
     450          15 :     return EOK;
     451             : }
     452             : 
     453             : int
     454          15 : resolv_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx,
     455             :             int timeout, struct resolv_ctx **ctxp)
     456             : {
     457             :     int ret;
     458             :     struct resolv_ctx *ctx;
     459             : 
     460          15 :     if (timeout < 1) {
     461           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
     462             :               "The timeout is too short, DNS operations are going to fail. "
     463             :                "This is a bug outside unit tests\n");
     464             :     }
     465             : 
     466          15 :     ctx = talloc_zero(mem_ctx, struct resolv_ctx);
     467          15 :     if (ctx == NULL)
     468           0 :         return ENOMEM;
     469             : 
     470          15 :     ctx->ev_ctx = ev_ctx;
     471          15 :     ctx->timeout = timeout;
     472             : 
     473          15 :     ret = recreate_ares_channel(ctx);
     474          15 :     if (ret != EOK) {
     475           0 :         goto done;
     476             :     }
     477             : 
     478          15 :     talloc_set_destructor(ctx, resolv_ctx_destructor);
     479             : 
     480          15 :     *ctxp = ctx;
     481          15 :     return EOK;
     482             : 
     483             : done:
     484           0 :     talloc_free(ctx);
     485           0 :     return ret;
     486             : }
     487             : 
     488             : void
     489           0 : resolv_reread_configuration(struct resolv_ctx *ctx)
     490             : {
     491           0 :     recreate_ares_channel(ctx);
     492           0 : }
     493             : 
     494             : static errno_t
     495           4 : resolv_copy_in_addr(TALLOC_CTX *mem_ctx, struct resolv_addr *ret,
     496             :                     struct ares_addrttl *attl)
     497             : {
     498           4 :     ret->ipaddr = talloc_array(mem_ctx, uint8_t, sizeof(struct in_addr));
     499           4 :     if (!ret->ipaddr) return ENOMEM;
     500             : 
     501           4 :     memcpy(ret->ipaddr, &attl->ipaddr, sizeof(struct in_addr));
     502           4 :     ret->ttl = attl->ttl;
     503             : 
     504           4 :     return EOK;
     505             : }
     506             : 
     507             : static errno_t
     508           0 : resolv_copy_in6_addr(TALLOC_CTX *mem_ctx, struct resolv_addr *ret,
     509             :                      struct ares_addr6ttl *a6ttl)
     510             : {
     511           0 :     ret->ipaddr = talloc_array(mem_ctx, uint8_t, sizeof(struct in6_addr));
     512           0 :     if (!ret->ipaddr) return ENOMEM;
     513             : 
     514           0 :     memcpy(ret->ipaddr, &a6ttl->ip6addr, sizeof(struct in6_addr));
     515           0 :     ret->ttl = a6ttl->ttl;
     516             : 
     517           0 :     return EOK;
     518             : }
     519             : 
     520             : static struct resolv_hostent *
     521           5 : resolv_copy_hostent_common(TALLOC_CTX *mem_ctx, struct hostent *src)
     522             : {
     523             :     struct resolv_hostent *ret;
     524             :     int len;
     525             :     int i;
     526             : 
     527           5 :     ret = talloc_zero(mem_ctx, struct resolv_hostent);
     528           5 :     if (ret == NULL) {
     529           0 :         return NULL;
     530             :     }
     531             : 
     532           5 :     if (src->h_name != NULL) {
     533           5 :         ret->name = talloc_strdup(ret, src->h_name);
     534           5 :         if (ret->name == NULL) {
     535           0 :             goto fail;
     536             :         }
     537             :     }
     538           5 :     if (src->h_aliases != NULL) {
     539           5 :         for (len = 0; src->h_aliases[len] != NULL; len++);
     540             : 
     541           5 :         ret->aliases = talloc_array(ret, char *, len + 1);
     542           5 :         if (ret->aliases == NULL) {
     543           0 :             goto fail;
     544             :         }
     545             : 
     546          10 :         for (i = 0; i < len; i++) {
     547           5 :             ret->aliases[i] = talloc_strdup(ret->aliases, src->h_aliases[i]);
     548           5 :             if (ret->aliases[i] == NULL) {
     549           0 :                 goto fail;
     550             :             }
     551             :         }
     552           5 :         ret->aliases[len] = NULL;
     553             :     }
     554             : 
     555           5 :     ret->family = src->h_addrtype;
     556           5 :     return ret;
     557             : 
     558             : fail:
     559           0 :     talloc_free(ret);
     560           0 :     return NULL;
     561             : }
     562             : 
     563             : struct resolv_hostent *
     564           2 : resolv_copy_hostent(TALLOC_CTX *mem_ctx, struct hostent *src)
     565             : {
     566             :     struct resolv_hostent *ret;
     567             :     int len;
     568             :     int i;
     569             : 
     570           2 :     ret = resolv_copy_hostent_common(mem_ctx, src);
     571           2 :     if (ret == NULL) {
     572           0 :         return NULL;
     573             :     }
     574             : 
     575           2 :     if (src->h_addr_list != NULL) {
     576           2 :         for (len = 0; src->h_addr_list[len] != NULL; len++);
     577             : 
     578           2 :         ret->addr_list = talloc_array(ret, struct resolv_addr *, len + 1);
     579           2 :         if (ret->addr_list == NULL) {
     580           0 :             goto fail;
     581             :         }
     582             : 
     583           5 :         for (i = 0; i < len; i++) {
     584           3 :             ret->addr_list[i] = talloc_zero(ret->addr_list,
     585             :                                             struct resolv_addr);
     586           3 :             if (ret->addr_list[i] == NULL) {
     587           0 :                 goto fail;
     588             :             }
     589             : 
     590           3 :             ret->addr_list[i]->ipaddr = talloc_memdup(ret->addr_list[i],
     591             :                                                       src->h_addr_list[i],
     592             :                                                       src->h_length);
     593           3 :             if (ret->addr_list[i]->ipaddr == NULL) {
     594           0 :                 goto fail;
     595             :             }
     596           3 :             ret->addr_list[i]->ttl = RESOLV_DEFAULT_TTL;
     597             :         }
     598           2 :         ret->addr_list[len] = NULL;
     599             :     }
     600           2 :     return ret;
     601             : 
     602             : fail:
     603           0 :     talloc_free(ret);
     604           0 :     return NULL;
     605             : }
     606             : 
     607             : struct resolv_hostent *
     608           3 : resolv_copy_hostent_ares(TALLOC_CTX *mem_ctx, struct hostent *src,
     609             :                          int family, void *ares_ttl_data,
     610             :                          int num_ares_ttl_data)
     611             : {
     612             :     struct resolv_hostent *ret;
     613             :     errno_t cret;
     614             :     int i;
     615             : 
     616           3 :     ret = resolv_copy_hostent_common(mem_ctx, src);
     617           3 :     if (ret == NULL) {
     618           0 :         return NULL;
     619             :     }
     620             : 
     621           3 :     if (num_ares_ttl_data > 0) {
     622           3 :         ret->addr_list = talloc_array(ret, struct resolv_addr *,
     623             :                                       num_ares_ttl_data + 1);
     624           3 :         if (ret->addr_list == NULL) {
     625           0 :             goto fail;
     626             :         }
     627             : 
     628           7 :         for (i = 0; i < num_ares_ttl_data; i++) {
     629           4 :             ret->addr_list[i] = talloc_zero(ret->addr_list,
     630             :                                             struct resolv_addr);
     631           4 :             if (ret->addr_list[i] == NULL) {
     632           0 :                 goto fail;
     633             :             }
     634             : 
     635           4 :             switch (family) {
     636             :             case AF_INET:
     637           8 :                 cret = resolv_copy_in_addr(ret->addr_list, ret->addr_list[i],
     638           4 :                                 &((struct ares_addrttl *) ares_ttl_data)[i]);
     639           4 :                 break;
     640             :             case AF_INET6:
     641           0 :                 cret = resolv_copy_in6_addr(ret->addr_list, ret->addr_list[i],
     642           0 :                                 &((struct ares_addr6ttl *) ares_ttl_data)[i]);
     643           0 :                 break;
     644             :             default:
     645           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
     646             :                       "Unknown address family %d\n", family);
     647           0 :                 goto fail;
     648             :             }
     649             : 
     650           4 :             if (cret != EOK) {
     651           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "Could not copy address\n");
     652           0 :                 goto fail;
     653             :             }
     654             :         }
     655           3 :         ret->addr_list[num_ares_ttl_data] = NULL;
     656             :     }
     657             : 
     658           3 :     ret->family = family;
     659           3 :     return ret;
     660             : 
     661             : fail:
     662           0 :     talloc_free(ret);
     663           0 :     return NULL;
     664             : }
     665             : 
     666             : /* =================== Resolve host name in files =========================*/
     667             : struct gethostbyname_files_state {
     668             :     struct resolv_ctx *resolv_ctx;
     669             : 
     670             :     /* Part of the query. */
     671             :     const char *name;
     672             :     int family;
     673             : 
     674             :     /* query result */
     675             :     struct resolv_hostent *rhostent;
     676             : 
     677             :     /* returned by ares. */
     678             :     int status;
     679             : };
     680             : 
     681             : /* Fake up an async interface even though files would
     682             :  * always be blocking */
     683             : static struct tevent_req *
     684           5 : resolv_gethostbyname_files_send(TALLOC_CTX *mem_ctx,
     685             :                                 struct tevent_context *ev,
     686             :                                 struct resolv_ctx *ctx,
     687             :                                 const char *name,
     688             :                                 int family)
     689             : {
     690             :     struct tevent_req *req;
     691             :     struct gethostbyname_files_state *state;
     692           5 :     struct hostent *hostent = NULL;
     693             : 
     694           5 :     req = tevent_req_create(mem_ctx, &state,
     695             :                             struct gethostbyname_files_state);
     696           5 :     if (req == NULL) {
     697           0 :         tevent_req_error(req, ENOMEM);
     698           0 :         goto done;
     699             :     }
     700             : 
     701           5 :     state->resolv_ctx = ctx;
     702           5 :     state->name = name;
     703           5 :     state->rhostent = NULL;
     704           5 :     state->family = family;
     705             : 
     706           5 :     DEBUG(SSSDBG_CONF_SETTINGS,
     707             :           "Trying to resolve %s record of '%s' in files\n",
     708             :               state->family == AF_INET ? "A" : "AAAA", state->name);
     709             : 
     710          15 :     state->status = ares_gethostbyname_file(state->resolv_ctx->channel,
     711          10 :                                             state->name, state->family,
     712             :                                             &hostent);
     713             : 
     714           5 :     if (state->status == ARES_SUCCESS) {
     715           1 :         state->rhostent = resolv_copy_hostent(state, hostent);
     716           1 :         if (state->rhostent == NULL) {
     717           0 :             tevent_req_error(req, ENOMEM);
     718           0 :             goto done;
     719             :         }
     720           4 :     } else if (state->status == ARES_ENOTFOUND ||
     721           0 :                state->status == ARES_ENODATA) {
     722             :         /* Just say we didn't find anything and let the caller decide
     723             :          * about retrying */
     724           4 :         tevent_req_error(req, ENOENT);
     725           4 :         goto done;
     726             :     } else {
     727           0 :         tevent_req_error(req, return_code(state->status));
     728           0 :         goto done;
     729             :     }
     730             : 
     731           1 :     tevent_req_done(req);
     732             : done:
     733           5 :     if (hostent) ares_free_hostent(hostent);
     734           5 :     tevent_req_post(req, ev);
     735           5 :     return req;
     736             : }
     737             : 
     738             : static errno_t
     739           5 : resolv_gethostbyname_files_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
     740             :                                 int *status, struct resolv_hostent **rhostent)
     741             : {
     742           5 :     struct gethostbyname_files_state *state = tevent_req_data(req,
     743             :                                         struct gethostbyname_files_state);
     744             : 
     745             :     /* Fill in even in case of error as status contains the
     746             :      * c-ares return code */
     747           5 :     if (status) {
     748           5 :         *status = state->status;
     749             :     }
     750           5 :     if (rhostent) {
     751           5 :         *rhostent = talloc_steal(mem_ctx, state->rhostent);
     752             :     }
     753             : 
     754           9 :     TEVENT_REQ_RETURN_ON_ERROR(req);
     755             : 
     756           1 :     return EOK;
     757             : }
     758             : 
     759             : /* ==================== Resolve host name in DNS =========================*/
     760             : struct gethostbyname_dns_state {
     761             :     struct resolv_ctx *resolv_ctx;
     762             :     struct tevent_context *ev;
     763             : 
     764             :     /* Part of the query. */
     765             :     const char *name;
     766             :     int family;
     767             : 
     768             :     /* query result */
     769             :     struct resolv_hostent *rhostent;
     770             : 
     771             :     /* These are returned by ares. */
     772             :     int status;
     773             :     int timeouts;
     774             :     int retrying;
     775             : };
     776             : 
     777             : static void
     778             : resolv_gethostbyname_dns_wakeup(struct tevent_req *subreq);
     779             : static void
     780             : resolv_gethostbyname_dns_query(struct tevent_req *req,
     781             :                                struct gethostbyname_dns_state *state);
     782             : static void
     783             : resolv_gethostbyname_dns_query_done(void *arg, int status, int timeouts,
     784             :                                     unsigned char *abuf, int alen);
     785             : static int
     786             : resolv_gethostbyname_dns_parse(struct gethostbyname_dns_state *state,
     787             :                                int status, unsigned char *abuf, int alen);
     788             : 
     789             : static struct tevent_req *
     790           2 : resolv_gethostbyname_dns_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
     791             :                               struct resolv_ctx *ctx, const char *name,
     792             :                               int family)
     793             : {
     794             :     struct tevent_req *req, *subreq;
     795             :     struct gethostbyname_dns_state *state;
     796           2 :     struct timeval tv = { 0, 0 };
     797             : 
     798           2 :     if (ctx->channel == NULL) {
     799           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     800             :               "Invalid ares channel - this is likely a bug\n");
     801           0 :         return NULL;
     802             :     }
     803             : 
     804           2 :     req = tevent_req_create(mem_ctx, &state, struct gethostbyname_dns_state);
     805           2 :     if (req == NULL) {
     806           0 :         return NULL;
     807             :     }
     808             : 
     809           2 :     state->resolv_ctx = ctx;
     810           2 :     state->ev = ev;
     811           2 :     state->name = name;
     812           2 :     state->rhostent = NULL;
     813           2 :     state->status = 0;
     814           2 :     state->timeouts = 0;
     815           2 :     state->retrying = 0;
     816           2 :     state->family = family;
     817             : 
     818             :     /* We need to have a wrapper around ares async calls, because
     819             :      * they can in some cases call it's callback immediately.
     820             :      * This would not let our caller to set a callback for req. */
     821           2 :     subreq = tevent_wakeup_send(req, ev, tv);
     822           2 :     if (subreq == NULL) {
     823           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     824             :               "Failed to add critical timer to run next operation!\n");
     825           0 :         talloc_zfree(req);
     826           0 :         return NULL;
     827             :     }
     828           2 :     tevent_req_set_callback(subreq, resolv_gethostbyname_dns_wakeup, req);
     829             : 
     830           2 :     return req;
     831             : }
     832             : 
     833             : static void
     834           2 : resolv_gethostbyname_dns_wakeup(struct tevent_req *subreq)
     835             : {
     836           2 :     struct tevent_req *req = tevent_req_callback_data(subreq,
     837             :                                                 struct tevent_req);
     838           2 :     struct gethostbyname_dns_state *state = tevent_req_data(req,
     839             :                                         struct gethostbyname_dns_state);
     840             : 
     841           2 :     if (!tevent_wakeup_recv(subreq)) {
     842           0 :         tevent_req_error(req, EIO);
     843           0 :         return;
     844             :     }
     845           2 :     talloc_zfree(subreq);
     846             : 
     847           2 :     if (state->resolv_ctx->channel == NULL) {
     848           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     849             :               "Invalid ares channel - this is likely a bug\n");
     850           0 :         tevent_req_error(req, EIO);
     851           0 :         return;
     852             :     }
     853             : 
     854           2 :     resolv_gethostbyname_dns_query(req, state);
     855             : }
     856             : 
     857             : static void
     858           2 : resolv_gethostbyname_dns_query(struct tevent_req *req,
     859             :                                struct gethostbyname_dns_state *state)
     860             : {
     861             :     struct resolv_request *rreq;
     862             : 
     863           2 :     DEBUG(SSSDBG_CONF_SETTINGS, "Trying to resolve %s record of '%s' in DNS\n",
     864             :               state->family == AF_INET ? "A" : "AAAA", state->name);
     865             : 
     866           2 :     rreq = schedule_timeout_watcher(state->ev, state->resolv_ctx, req);
     867           2 :     if (!rreq) {
     868           0 :         tevent_req_error(req, ENOMEM);
     869           0 :         return;
     870             :     }
     871             : 
     872           2 :     ares_search(state->resolv_ctx->channel,
     873             :                 state->name, ns_c_in,
     874           2 :                 (state->family == AF_INET) ? ns_t_a : ns_t_aaaa,
     875             :                 resolv_gethostbyname_dns_query_done, rreq);
     876             : }
     877             : 
     878             : static void
     879           2 : resolv_gethostbyname_dns_query_done(void *arg, int status, int timeouts,
     880             :                                     unsigned char *abuf, int alen)
     881             : {
     882             :     errno_t ret;
     883             :     struct gethostbyname_dns_state *state;
     884           2 :     struct resolv_request *rreq = talloc_get_type(arg, struct resolv_request);
     885             :     struct tevent_req *req;
     886             : 
     887             : 
     888           2 :     if (rreq->rwatch == NULL) {
     889             :         /* The tevent request was cancelled while the ares call was still in
     890             :          * progress so nobody cares about the result now. Quit. */
     891           0 :         unschedule_timeout_watcher(rreq->ctx, rreq);
     892           0 :         return;
     893             :     }
     894             : 
     895           2 :     req = rreq->rwatch->req;
     896           2 :     unschedule_timeout_watcher(rreq->ctx, rreq);
     897             : 
     898           2 :     state = tevent_req_data(req, struct gethostbyname_dns_state);
     899             : 
     900           2 :     state->status = status;
     901           2 :     state->timeouts = timeouts;
     902             : 
     903             :     /* If resolv.conf changed during processing of a request we might
     904             :      * destroy the old channel before the request has a chance to finish.
     905             :      * We must resend the request in this case */
     906           2 :     if (state->retrying == 0 && status == ARES_EDESTRUCTION
     907           0 :         && state->resolv_ctx->channel != NULL) {
     908           0 :         state->retrying = 1;
     909           0 :         resolv_gethostbyname_dns_query(req, state);
     910           0 :         return;
     911             :     }
     912             : 
     913           2 :     if (status == ARES_ENOTFOUND || status == ARES_ENODATA) {
     914             :         /* Just say we didn't find anything and let the caller decide
     915             :          * about retrying */
     916           0 :         tevent_req_error(req, ENOENT);
     917           0 :         return;
     918             :     }
     919             : 
     920           2 :     if (status != ARES_SUCCESS) {
     921             :         /* Any other error indicates a server error,
     922             :          * so don't bother trying again
     923             :          */
     924           0 :         tevent_req_error(req, return_code(status));
     925           0 :         return;
     926             :     }
     927             : 
     928           2 :     ret = resolv_gethostbyname_dns_parse(state, status, abuf, alen);
     929           2 :     if (ret != EOK) {
     930           0 :         tevent_req_error(req, ret);
     931           0 :         return;
     932             :     }
     933             : 
     934           2 :     tevent_req_done(req);
     935             : }
     936             : 
     937             : static int
     938           2 : resolv_gethostbyname_dns_parse(struct gethostbyname_dns_state *state,
     939             :                                int status, unsigned char *abuf, int alen)
     940             : {
     941             :     struct hostent *hostent;
     942             :     int naddrttls;
     943             :     errno_t ret;
     944           2 :     void *addr = NULL;
     945             : 
     946           2 :     naddrttls = DNS_HEADER_ANCOUNT(abuf);
     947             : 
     948           2 :     switch (state->family) {
     949             :         case AF_INET:
     950           2 :             DEBUG(SSSDBG_TRACE_LIBS, "Parsing an A reply\n");
     951             : 
     952           2 :             addr = talloc_array(state, struct ares_addrttl, naddrttls);
     953           2 :             if (!addr) {
     954           0 :                 ret = ENOMEM;
     955           0 :                 goto fail;
     956             :             }
     957             : 
     958           2 :             status = ares_parse_a_reply(abuf, alen, &hostent,
     959             :                                         (struct ares_addrttl *) addr,
     960             :                                         &naddrttls);
     961           2 :             break;
     962             :         case AF_INET6:
     963           0 :             DEBUG(SSSDBG_TRACE_LIBS, "Parsing an AAAA reply\n");
     964             : 
     965           0 :             addr = talloc_array(state, struct ares_addr6ttl, naddrttls);
     966           0 :             if (!addr) {
     967           0 :                 ret = ENOMEM;
     968           0 :                 goto fail;
     969             :             }
     970             : 
     971           0 :             status = ares_parse_aaaa_reply(abuf, alen, &hostent,
     972             :                                            (struct ares_addr6ttl *) addr,
     973             :                                            &naddrttls);
     974           0 :             break;
     975             :         default:
     976           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Unknown family %d\n", state->family);
     977           0 :             ret = EAFNOSUPPORT;
     978           0 :             goto fail;
     979             :     }
     980             : 
     981           2 :     if (hostent != NULL) {
     982           2 :         state->rhostent = resolv_copy_hostent_ares(state, hostent,
     983             :                                                    state->family,
     984             :                                                    addr, naddrttls);
     985           2 :         ares_free_hostent(hostent);
     986           2 :         if (state->rhostent == NULL) {
     987           0 :             ret = ENOMEM;
     988           0 :             goto fail;
     989             :         }
     990             : 
     991             :         /* The address list is NULL. This is probably a bug in
     992             :          * c-ares, but we need to handle it gracefully.
     993             :          */
     994           2 :         if (state->rhostent->addr_list == NULL) {
     995           0 :             talloc_zfree(state->rhostent);
     996           0 :             return ENOENT;
     997             :         }
     998             :     }
     999             : 
    1000           2 :     talloc_free(addr);
    1001           2 :     return return_code(status);
    1002             : 
    1003             : fail:
    1004           0 :     talloc_free(addr);
    1005           0 :     return ret;
    1006             : }
    1007             : 
    1008             : static int
    1009           2 : resolv_gethostbyname_dns_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
    1010             :                               int *status, int *timeouts,
    1011             :                               struct resolv_hostent **rhostent)
    1012             : {
    1013           2 :     struct gethostbyname_dns_state *state = tevent_req_data(req,
    1014             :                                         struct gethostbyname_dns_state);
    1015             : 
    1016             :     /* Fill in even in case of error as status contains the
    1017             :      * c-ares return code */
    1018           2 :     if (status) {
    1019           2 :         *status = state->status;
    1020             :     }
    1021           2 :     if (timeouts) {
    1022           2 :         *timeouts = state->timeouts;
    1023             :     }
    1024             : 
    1025           2 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1026             : 
    1027           2 :     if (rhostent) {
    1028           2 :         *rhostent = talloc_steal(mem_ctx, state->rhostent);
    1029             :     }
    1030             : 
    1031           2 :     return EOK;
    1032             : }
    1033             : 
    1034             : /*******************************************************************
    1035             :  * Get host by name.                                               *
    1036             :  *******************************************************************/
    1037             : 
    1038             : struct gethostbyname_state {
    1039             :     struct resolv_ctx *resolv_ctx;
    1040             :     struct tevent_context *ev;
    1041             : 
    1042             :     /* Part of the query. */
    1043             :     const char *name;
    1044             :     int family;
    1045             : 
    1046             :     /* In which order to use IPv4, or v6 */
    1047             :     enum restrict_family family_order;
    1048             : 
    1049             :     /* Known hosts databases and index to the current one */
    1050             :     enum host_database *db;
    1051             :     int dbi;
    1052             : 
    1053             :     /* These are returned by ares. The hostent struct will be freed
    1054             :      * when the user callback returns. */
    1055             :     struct resolv_hostent *rhostent;
    1056             :     int status;
    1057             :     int timeouts;
    1058             :     int retrying;
    1059             : };
    1060             : 
    1061             : static errno_t
    1062             : resolv_gethostbyname_address(TALLOC_CTX *mem_ctx, const char *address,
    1063             :                              struct resolv_hostent **_rhostent);
    1064             : static inline int
    1065             : resolv_gethostbyname_family_init(enum restrict_family family_order);
    1066             : static bool
    1067             : resolv_is_address(const char *name);
    1068             : static errno_t
    1069             : resolv_gethostbyname_step(struct tevent_req *req);
    1070             : 
    1071             : struct tevent_req *
    1072           5 : resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
    1073             :                           struct resolv_ctx *ctx, const char *name,
    1074             :                           enum restrict_family family_order,
    1075             :                           enum host_database *db)
    1076             : {
    1077             :     struct tevent_req *req;
    1078             :     struct gethostbyname_state *state;
    1079             :     errno_t ret;
    1080             : 
    1081           5 :     if (ctx->channel == NULL) {
    1082           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1083             :               "Invalid ares channel - this is likely a bug\n");
    1084           0 :         return NULL;
    1085             :     }
    1086             : 
    1087           5 :     req = tevent_req_create(mem_ctx, &state, struct gethostbyname_state);
    1088           5 :     if (req == NULL) {
    1089           0 :         return NULL;
    1090             :     }
    1091             : 
    1092           5 :     state->resolv_ctx = ctx;
    1093           5 :     state->ev = ev;
    1094           5 :     state->name = talloc_strdup(state, name);
    1095           5 :     if (state->name == NULL) {
    1096           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
    1097           0 :         goto fail;
    1098             :     }
    1099             : 
    1100           5 :     state->rhostent = NULL;
    1101           5 :     state->status = 0;
    1102           5 :     state->timeouts = 0;
    1103           5 :     state->retrying = 0;
    1104           5 :     state->family_order = family_order;
    1105           5 :     state->family = resolv_gethostbyname_family_init(state->family_order);
    1106           5 :     state->db = db;
    1107           5 :     state->dbi = 0;
    1108             : 
    1109             :     /* Do not attempt to resolve IP addresses */
    1110           5 :     if (resolv_is_address(state->name)) {
    1111           2 :         ret = resolv_gethostbyname_address(state, state->name,
    1112           2 :                                            &state->rhostent);
    1113           2 :         if (ret != EOK) {
    1114           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1115             :                   "Canot create a fake hostent structure\n");
    1116           0 :             goto fail;
    1117             :         }
    1118             : 
    1119           2 :         tevent_req_done(req);
    1120           2 :         tevent_req_post(req, ev);
    1121           2 :         return req;
    1122             :     }
    1123             : 
    1124           3 :     ret = resolv_gethostbyname_step(req);
    1125           3 :     if (ret != EOK) {
    1126           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot start the resolving\n");
    1127           0 :         goto fail;
    1128             :     }
    1129             : 
    1130           3 :     return req;
    1131             : 
    1132             : fail:
    1133           0 :     talloc_zfree(req);
    1134           0 :     return NULL;
    1135             : }
    1136             : 
    1137             : static bool
    1138           5 : resolv_is_address(const char *name)
    1139             : {
    1140             :     struct addrinfo hints;
    1141           5 :     struct addrinfo *res = NULL;
    1142             :     int ret;
    1143             : 
    1144           5 :     memset((void *) &hints, 0, sizeof(struct addrinfo));
    1145           5 :     hints.ai_family = AF_UNSPEC;
    1146           5 :     hints.ai_flags = AI_NUMERICHOST; /* No network lookups */
    1147             : 
    1148           5 :     ret = getaddrinfo(name, NULL, &hints, &res);
    1149           5 :     if (ret != 0) {
    1150           3 :         if (ret == -2) {
    1151           3 :             DEBUG(SSSDBG_TRACE_ALL,
    1152             :                   "[%s] does not look like an IP address\n", name);
    1153             :         } else {
    1154           0 :             DEBUG(SSSDBG_OP_FAILURE, "getaddrinfo failed [%d]: %s\n",
    1155             :                       ret, gai_strerror(ret));
    1156             :         }
    1157             :     } else { /* ret == 0 */
    1158           2 :         freeaddrinfo(res);
    1159             :     }
    1160             : 
    1161           5 :     return ret == 0;
    1162             : }
    1163             : 
    1164             : static errno_t
    1165           2 : resolv_gethostbyname_address(TALLOC_CTX *mem_ctx, const char *address,
    1166             :                              struct resolv_hostent **_rhostent)
    1167             : {
    1168             :     struct resolv_hostent *rhostent;
    1169             :     TALLOC_CTX *tmp_ctx;
    1170             :     errno_t ret;
    1171             :     int family;
    1172             : 
    1173           2 :     tmp_ctx = talloc_new(NULL);
    1174           2 :     if (!tmp_ctx) return ENOMEM;
    1175             : 
    1176           2 :     rhostent = talloc_zero(tmp_ctx, struct resolv_hostent);
    1177           2 :     if (!rhostent) {
    1178           0 :         ret = ENOMEM;
    1179           0 :         goto done;
    1180             :     }
    1181             : 
    1182           2 :     rhostent->name = talloc_strdup(rhostent, address);
    1183           2 :     rhostent->addr_list = talloc_array(rhostent, struct resolv_addr *, 2);
    1184             : 
    1185           4 :     if (!rhostent->name ||
    1186           2 :         !rhostent->addr_list) {
    1187           0 :         ret = ENOMEM;
    1188           0 :         goto done;
    1189             :     }
    1190             : 
    1191           2 :     rhostent->addr_list[0] = talloc_zero(rhostent->addr_list,
    1192             :                                          struct resolv_addr);
    1193           2 :     if (!rhostent->addr_list[0]) {
    1194           0 :         ret = ENOMEM;
    1195           0 :         goto done;
    1196             :     }
    1197           2 :     rhostent->addr_list[0]->ipaddr = talloc_array(rhostent->addr_list[0],
    1198             :                                                   uint8_t,
    1199             :                                                   sizeof(struct in6_addr));
    1200           2 :     if (!rhostent->addr_list[0]->ipaddr) {
    1201           0 :         ret = ENOMEM;
    1202           0 :         goto done;
    1203             :     }
    1204             : 
    1205           2 :     family = AF_INET;
    1206           2 :     ret = inet_pton(family, address,
    1207           2 :                     rhostent->addr_list[0]->ipaddr);
    1208           2 :     if (ret != 1) {
    1209           0 :         family = AF_INET6;
    1210           0 :         ret = inet_pton(family, address,
    1211           0 :                         rhostent->addr_list[0]->ipaddr);
    1212           0 :         if (ret != 1) {
    1213           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1214             :                   "Could not parse address as neither v4 nor v6\n");
    1215           0 :             ret = EINVAL;
    1216           0 :             goto done;
    1217             :         }
    1218             :     }
    1219             : 
    1220           2 :     rhostent->addr_list[0]->ttl = RESOLV_DEFAULT_TTL;
    1221           2 :     rhostent->addr_list[1] = NULL;
    1222           2 :     rhostent->family = family;
    1223           2 :     rhostent->aliases = NULL;
    1224             : 
    1225           2 :     *_rhostent = talloc_move(mem_ctx, &rhostent);
    1226           2 :     ret = EOK;
    1227             : done:
    1228           2 :     talloc_free(tmp_ctx);
    1229           2 :     return ret;
    1230             : }
    1231             : 
    1232             : static inline int
    1233           7 : resolv_gethostbyname_family_init(enum restrict_family family_order)
    1234             : {
    1235           7 :     switch(family_order) {
    1236             :         case IPV4_ONLY:
    1237             :         case IPV4_FIRST:
    1238           7 :             return AF_INET;
    1239             :         case IPV6_ONLY:
    1240             :         case IPV6_FIRST:
    1241           0 :             return AF_INET6;
    1242             :     }
    1243             : 
    1244           0 :     DEBUG(SSSDBG_CRIT_FAILURE,
    1245             :           "Unknown address family order %d\n", family_order);
    1246           0 :     return -1;
    1247             : }
    1248             : 
    1249             : static int
    1250           4 : resolv_gethostbyname_next(struct gethostbyname_state *state)
    1251             : {
    1252           8 :     if (state->family_order == IPV4_FIRST &&
    1253           4 :         state->family == AF_INET) {
    1254           2 :         state->family = AF_INET6;
    1255           2 :         return EOK;
    1256           2 :     } else if (state->family_order == IPV6_FIRST &&
    1257           0 :                state->family == AF_INET6) {
    1258           0 :         state->family = AF_INET;
    1259           0 :         return EOK;
    1260             :     } else {
    1261             :         /* No more address families for this DB, check if
    1262             :          * there is another DB to try */
    1263           2 :         DEBUG(SSSDBG_FUNC_DATA, "No more address families to retry\n");
    1264           2 :         state->dbi++;
    1265           2 :         if (state->db[state->dbi] != DB_SENTINEL) {
    1266           2 :             state->family = resolv_gethostbyname_family_init(
    1267             :                                                 state->family_order);
    1268           2 :             return EOK;
    1269             :         }
    1270             :     }
    1271             : 
    1272           0 :     DEBUG(SSSDBG_CONF_SETTINGS, "No more hosts databases to retry\n");
    1273           0 :     return ENOENT;
    1274             : }
    1275             : 
    1276             : static void
    1277             : resolv_gethostbyname_done(struct tevent_req *subreq);
    1278             : 
    1279             : static errno_t
    1280           7 : resolv_gethostbyname_step(struct tevent_req *req)
    1281             : {
    1282           7 :     struct gethostbyname_state *state = tevent_req_data(req,
    1283             :                                                 struct gethostbyname_state);
    1284             :     struct tevent_req *subreq;
    1285             : 
    1286           7 :     switch(state->db[state->dbi]) {
    1287             :         case DB_FILES:
    1288           5 :             DEBUG(SSSDBG_TRACE_INTERNAL, "Querying files\n");
    1289           5 :             subreq = resolv_gethostbyname_files_send(state, state->ev,
    1290             :                                                      state->resolv_ctx,
    1291             :                                                      state->name,
    1292             :                                                      state->family);
    1293           5 :             break;
    1294             :         case DB_DNS:
    1295           2 :             DEBUG(SSSDBG_TRACE_INTERNAL, "Querying DNS\n");
    1296           2 :             subreq = resolv_gethostbyname_dns_send(state, state->ev,
    1297             :                                                    state->resolv_ctx,
    1298             :                                                    state->name,
    1299             :                                                    state->family);
    1300           2 :             break;
    1301             :         default:
    1302           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Invalid hosts database\n");
    1303           0 :             return EINVAL;
    1304             :     }
    1305             : 
    1306           7 :     if (subreq == NULL) {
    1307           0 :         return ENOMEM;
    1308             :     }
    1309             : 
    1310           7 :     tevent_req_set_callback(subreq, resolv_gethostbyname_done, req);
    1311           7 :     return EOK;
    1312             : }
    1313             : 
    1314             : static void
    1315           7 : resolv_gethostbyname_done(struct tevent_req *subreq)
    1316             : {
    1317           7 :     struct tevent_req *req = tevent_req_callback_data(subreq,
    1318             :                                                       struct tevent_req);
    1319           7 :     struct gethostbyname_state *state = tevent_req_data(req,
    1320             :                                                 struct gethostbyname_state);
    1321             :     errno_t ret;
    1322             : 
    1323           7 :     switch(state->db[state->dbi]) {
    1324             :         case DB_FILES:
    1325           5 :             ret = resolv_gethostbyname_files_recv(subreq, state,
    1326             :                                                   &state->status,
    1327             :                                                   &state->rhostent);
    1328             :             /* files is synchronous, there can be no timeouts */
    1329           5 :             state->timeouts = 0;
    1330           5 :             break;
    1331             :         case DB_DNS:
    1332           2 :             ret = resolv_gethostbyname_dns_recv(subreq, state,
    1333             :                                                 &state->status, &state->timeouts,
    1334             :                                                 &state->rhostent);
    1335           2 :             break;
    1336             :         default:
    1337           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Invalid hosts database\n");
    1338           0 :             tevent_req_error(req, EINVAL);
    1339           0 :             return;
    1340             :     }
    1341             : 
    1342           7 :     talloc_zfree(subreq);
    1343             : 
    1344           7 :     if (ret == ENOENT) {
    1345           4 :         ret = resolv_gethostbyname_next(state);
    1346           4 :         if (ret == EOK) {
    1347           4 :             ret = resolv_gethostbyname_step(req);
    1348           4 :             if (ret != EOK) {
    1349           0 :                 tevent_req_error(req, ret);
    1350             :             }
    1351           4 :             return;
    1352             :         }
    1353             : 
    1354             :         /* No more databases and/or address families */
    1355           0 :         tevent_req_error(req, ENOENT);
    1356           0 :         return;
    1357           3 :     } else if (ret == ETIMEDOUT) {
    1358             :         /* In case we killed the request before c-ares answered */
    1359           0 :         state->status = ARES_ETIMEOUT;
    1360             :     }
    1361             : 
    1362           3 :     if (ret != EOK) {
    1363           0 :         DEBUG(SSSDBG_OP_FAILURE, "querying hosts database failed [%d]: %s\n",
    1364             :                   ret, strerror(ret));
    1365           0 :         tevent_req_error(req, ret);
    1366           0 :         return;
    1367             :     }
    1368             : 
    1369           3 :     tevent_req_done(req);
    1370             : }
    1371             : 
    1372             : int
    1373           3 : resolv_gethostbyname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
    1374             :                           int *status, int *timeouts,
    1375             :                           struct resolv_hostent **rhostent)
    1376             : {
    1377           3 :     struct gethostbyname_state *state = tevent_req_data(req, struct gethostbyname_state);
    1378             : 
    1379             :     /* Fill in even in case of error as status contains the
    1380             :      * c-ares return code */
    1381           3 :     if (status) {
    1382           3 :         *status = state->status;
    1383             :     }
    1384           3 :     if (timeouts) {
    1385           0 :         *timeouts = state->timeouts;
    1386             :     }
    1387           3 :     if (rhostent) {
    1388           3 :         *rhostent = talloc_steal(mem_ctx, state->rhostent);
    1389             :     }
    1390             : 
    1391           3 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1392             : 
    1393           3 :     return EOK;
    1394             : }
    1395             : 
    1396             : char *
    1397           2 : resolv_get_string_address_index(TALLOC_CTX *mem_ctx,
    1398             :                                 struct resolv_hostent *hostent,
    1399             :                                 unsigned int addrindex)
    1400             : {
    1401             :     char *address;
    1402             : 
    1403           2 :     if (!hostent) return NULL;
    1404             : 
    1405           2 :     address = talloc_zero_size(mem_ctx, 128);
    1406           2 :     if (address == NULL) {
    1407           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
    1408           0 :         return NULL;
    1409             :     }
    1410             : 
    1411           2 :     errno = 0;
    1412           2 :     if (inet_ntop(hostent->family, hostent->addr_list[addrindex]->ipaddr,
    1413             :                   address, 128) == NULL) {
    1414           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1415             :               "inet_ntop failed [%d][%s].\n", errno, strerror(errno));
    1416           0 :         talloc_free(address);
    1417           0 :         return NULL;
    1418             :     }
    1419             : 
    1420           2 :     return address;
    1421             : }
    1422             : 
    1423             : char *
    1424           2 : resolv_get_string_ptr_address(TALLOC_CTX *mem_ctx,
    1425             :                               int family, uint8_t *address)
    1426             : {
    1427             :     char *straddr;
    1428             : 
    1429           2 :     if (family == AF_INET6) {
    1430             :         int i;
    1431             :         char hexbyte[3];
    1432             : 
    1433           1 :         straddr = talloc_strdup(mem_ctx, "\0");
    1434           1 :         if (!straddr) {
    1435           0 :             return NULL;
    1436             :         }
    1437             : 
    1438          17 :         for (i = 15; i >= 0; i--) {
    1439          16 :             snprintf(hexbyte, 3, "%02x", address[i]);
    1440          32 :             straddr = talloc_asprintf_append(straddr, "%c.%c.",
    1441          32 :                                              hexbyte[1], hexbyte[0]);
    1442             :         }
    1443           1 :         straddr = talloc_asprintf_append(straddr, "ip6.arpa.");
    1444           1 :     } else if (family == AF_INET) {
    1445           4 :         straddr = talloc_asprintf(mem_ctx,
    1446             :                                   "%u.%u.%u.%u.in-addr.arpa.",
    1447           1 :                                   (address[3]),
    1448           1 :                                   (address[2]),
    1449           1 :                                   (address[1]),
    1450           1 :                                   (address[0]));
    1451             :     } else {
    1452           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unknown address family\n");
    1453           0 :         return NULL;
    1454             :     }
    1455             : 
    1456           2 :     return straddr;
    1457             : }
    1458             : 
    1459             : struct sockaddr_storage *
    1460           0 : resolv_get_sockaddr_address_index(TALLOC_CTX *mem_ctx,
    1461             :                                   struct resolv_hostent *hostent,
    1462             :                                   int port, int addrindex)
    1463             : {
    1464             :     struct sockaddr_storage *sockaddr;
    1465             : 
    1466           0 :     if (!hostent) return NULL;
    1467             : 
    1468           0 :     sockaddr = talloc_zero(mem_ctx, struct sockaddr_storage);
    1469           0 :     if (sockaddr == NULL) {
    1470           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
    1471           0 :         return NULL;
    1472             :     }
    1473             : 
    1474           0 :     switch(hostent->family) {
    1475             :         case AF_INET:
    1476           0 :             sockaddr->ss_family = AF_INET;
    1477           0 :             memcpy(&((struct sockaddr_in *) sockaddr)->sin_addr,
    1478           0 :                    hostent->addr_list[addrindex]->ipaddr,
    1479             :                    sizeof(struct in_addr));
    1480           0 :             ((struct sockaddr_in *) sockaddr)->sin_port = (in_port_t) htons(port);
    1481             : 
    1482           0 :             break;
    1483             :         case AF_INET6:
    1484           0 :             sockaddr->ss_family = AF_INET6;
    1485           0 :             memcpy(&((struct sockaddr_in6 *) sockaddr)->sin6_addr,
    1486           0 :                    hostent->addr_list[addrindex]->ipaddr,
    1487             :                    sizeof(struct in6_addr));
    1488           0 :             ((struct sockaddr_in6 *) sockaddr)->sin6_port = (in_port_t) htons(port);
    1489           0 :             break;
    1490             :         default:
    1491           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1492             :                   "Unknown address family %d\n", hostent->family);
    1493           0 :             return NULL;
    1494             :     }
    1495             : 
    1496           0 :     return sockaddr;
    1497             : }
    1498             : 
    1499             : /*
    1500             :  * A simple helper function that will take an array of struct ares_srv_reply that
    1501             :  * was allocated by malloc() in c-ares and copies it using talloc. The old one
    1502             :  * is freed and the talloc one is put into 'reply_list' instead.
    1503             :  */
    1504             : static int
    1505           1 : rewrite_talloc_srv_reply(TALLOC_CTX *mem_ctx, struct ares_srv_reply **reply_list)
    1506             : {
    1507           1 :     struct ares_srv_reply *ptr = NULL;
    1508           1 :     struct ares_srv_reply *new_list = NULL;
    1509           1 :     struct ares_srv_reply *old_list = *reply_list;
    1510             : 
    1511             :     /* Nothing to do, but not an error */
    1512           1 :     if (!old_list) {
    1513           0 :         return EOK;
    1514             :     }
    1515             : 
    1516             :     /* Copy the linked list */
    1517           4 :     while (old_list) {
    1518             :         /* Special case for the first node */
    1519           2 :         if (!new_list) {
    1520           1 :             new_list = talloc_zero(mem_ctx, struct ares_srv_reply);
    1521           1 :             if (new_list == NULL) {
    1522           0 :                 ares_free_data(*reply_list);
    1523           0 :                 return ENOMEM;
    1524             :             }
    1525           1 :             ptr = new_list;
    1526             :         } else {
    1527           1 :             ptr->next = talloc_zero(new_list, struct ares_srv_reply);
    1528           1 :             if (ptr->next == NULL) {
    1529           0 :                 ares_free_data(*reply_list);
    1530           0 :                 talloc_free(new_list);
    1531           0 :                 return ENOMEM;
    1532             :             }
    1533           1 :             ptr = ptr->next;
    1534             :         }
    1535             : 
    1536           2 :         ptr->weight = old_list->weight;
    1537           2 :         ptr->priority = old_list->priority;
    1538           2 :         ptr->port = old_list->port;
    1539           2 :         ptr->host = talloc_strdup(ptr, old_list->host);
    1540           2 :         if (ptr->host == NULL) {
    1541           0 :             ares_free_data(*reply_list);
    1542           0 :             talloc_free(new_list);
    1543           0 :             return ENOMEM;
    1544             :         }
    1545             : 
    1546           2 :         old_list = old_list->next;
    1547             :     }
    1548             : 
    1549             :     /* Free the old one (uses malloc). */
    1550           1 :     ares_free_data(*reply_list);
    1551             : 
    1552             :     /* And now put our own new_list in place. */
    1553           1 :     *reply_list = new_list;
    1554             : 
    1555           1 :     return EOK;
    1556             : }
    1557             : 
    1558             : /*******************************************************************
    1559             :  * Get SRV record                                                  *
    1560             :  *******************************************************************/
    1561             : 
    1562             : struct getsrv_state {
    1563             :     struct tevent_context *ev;
    1564             :     struct resolv_ctx *resolv_ctx;
    1565             :     /* the SRV query - for example _ldap._tcp.example.com */
    1566             :     const char *query;
    1567             : 
    1568             :     /* parsed data returned by ares */
    1569             :     struct ares_srv_reply *reply_list;
    1570             :     uint32_t ttl;
    1571             :     int status;
    1572             :     int timeouts;
    1573             :     int retrying;
    1574             : };
    1575             : 
    1576             : static void
    1577             : ares_getsrv_wakeup(struct tevent_req *subreq);
    1578             : static void
    1579             : resolv_getsrv_query(struct tevent_req *req,
    1580             :                     struct getsrv_state *state);
    1581             : 
    1582             : struct tevent_req *
    1583           1 : resolv_getsrv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
    1584             :                    struct resolv_ctx *ctx, const char *query)
    1585             : {
    1586             :     struct tevent_req *req, *subreq;
    1587             :     struct getsrv_state *state;
    1588           1 :     struct timeval tv = { 0, 0 };
    1589             : 
    1590           1 :     DEBUG(SSSDBG_CONF_SETTINGS,
    1591             :           "Trying to resolve SRV record of '%s'\n", query);
    1592             : 
    1593           1 :     if (ctx->channel == NULL) {
    1594           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1595             :               "Invalid ares channel - this is likely a bug\n");
    1596           0 :         return NULL;
    1597             :     }
    1598             : 
    1599           1 :     req = tevent_req_create(mem_ctx, &state, struct getsrv_state);
    1600           1 :     if (req == NULL)
    1601           0 :         return NULL;
    1602             : 
    1603           1 :     state->resolv_ctx = ctx;
    1604           1 :     state->query = query;
    1605           1 :     state->reply_list = NULL;
    1606           1 :     state->ttl = 0;
    1607           1 :     state->status = 0;
    1608           1 :     state->timeouts = 0;
    1609           1 :     state->retrying = 0;
    1610           1 :     state->ev = ev;
    1611             : 
    1612           1 :     subreq = tevent_wakeup_send(req, ev, tv);
    1613           1 :     if (subreq == NULL) {
    1614           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1615             :               "Failed to add critical timer to run next operation!\n");
    1616           0 :         talloc_zfree(req);
    1617           0 :         return NULL;
    1618             :     }
    1619           1 :     tevent_req_set_callback(subreq, ares_getsrv_wakeup, req);
    1620             : 
    1621           1 :     return req;
    1622             : }
    1623             : 
    1624             : /*
    1625             :  * Implemented based on http://tools.ietf.org/html/rfc2181#section-5
    1626             :  *
    1627             :  * Especially:
    1628             :  * 5.2. TTLs of RRs in an RRSet
    1629             :  *  Consequently the use of differing TTLs in an RRSet is hereby
    1630             :  *  deprecated, the TTLs of all RRs in an RRSet must be the same.
    1631             :  *  ...
    1632             :  *  Should an authoritative source send such a malformed RRSet, the
    1633             :  *  client should treat the RRs for all purposes as if all TTLs in the
    1634             :  *  RRSet had been set to the value of the lowest TTL in the RRSet.
    1635             :  *
    1636             :  *  On success, returns true and sets the TTL in the _ttl parameter. On
    1637             :  *  failure, returns false and _ttl is undefined.
    1638             :  */
    1639             : static bool
    1640           1 : resolv_get_ttl(const unsigned char *abuf, const int alen, uint32_t *_ttl)
    1641             : {
    1642             :     const unsigned char *aptr;
    1643             :     int ret;
    1644           1 :     char *name = NULL;
    1645             :     long len;
    1646           1 :     uint32_t ttl = 0;
    1647             :     uint32_t rr_ttl;
    1648             :     unsigned int rr_len;
    1649             :     unsigned int ancount;
    1650             :     unsigned int i;
    1651             : 
    1652             :     /* Read the number of RRs and then skip past the header */
    1653           1 :     if (alen < NS_HFIXEDSZ) {
    1654           0 :         return false;
    1655             :     }
    1656             : 
    1657           1 :     ancount = DNS_HEADER_ANCOUNT(abuf);
    1658           1 :     if (ancount == 0) {
    1659           0 :         return false;
    1660             :     }
    1661             : 
    1662           1 :     aptr = abuf + NS_HFIXEDSZ;
    1663             : 
    1664             :     /* We only care about len from the question data,
    1665             :      * so that we can move past hostname */
    1666           1 :     ret = ares_expand_name(aptr, abuf, alen, &name, &len);
    1667           1 :     ares_free_string(name);
    1668           1 :     if (ret != ARES_SUCCESS) {
    1669           0 :         return false;
    1670             :     }
    1671             : 
    1672             :     /* Skip past the question */
    1673           1 :     aptr += len + NS_QFIXEDSZ;
    1674           1 :     if (aptr > abuf + alen) {
    1675           0 :         return false;
    1676             :     }
    1677             : 
    1678             :     /* Examine each RR in turn and read the lowest TTL */
    1679           3 :     for (i = 0; i < ancount; i++) {
    1680             :         /* Decode the RR up to the data field. */
    1681           2 :         ret = ares_expand_name(aptr, abuf, alen, &name, &len);
    1682           2 :         ares_free_string(name);
    1683           2 :         if (ret != ARES_SUCCESS) {
    1684           0 :             return false;
    1685             :         }
    1686             : 
    1687           2 :         aptr += len;
    1688           2 :         if (aptr + NS_RRFIXEDSZ > abuf + alen) {
    1689           0 :             return false;
    1690             :         }
    1691             : 
    1692           2 :         rr_len = DNS_RR_LEN(aptr);
    1693           2 :         rr_ttl = DNS_RR_TTL(aptr);
    1694           2 :         if (aptr + rr_len > abuf + alen) {
    1695           0 :             return false;
    1696             :         }
    1697           2 :         aptr += NS_RRFIXEDSZ + rr_len;
    1698             : 
    1699           2 :         if (ttl > 0) {
    1700           1 :             ttl = MIN(ttl, rr_ttl);
    1701             :         } else {
    1702           1 :             ttl = rr_ttl; /* special-case for first TTL */
    1703             :         }
    1704             :     }
    1705             : 
    1706           1 :     *_ttl = ttl;
    1707           1 :     return true;
    1708             : }
    1709             : 
    1710             : static void
    1711           1 : resolv_getsrv_done(void *arg, int status, int timeouts, unsigned char *abuf, int alen)
    1712             : {
    1713           1 :     struct resolv_request *rreq = talloc_get_type(arg, struct resolv_request);
    1714             :     struct tevent_req *req;
    1715             :     struct getsrv_state *state;
    1716             :     int ret;
    1717             :     bool ok;
    1718             :     struct ares_srv_reply *reply_list;
    1719             : 
    1720           1 :     if (rreq->rwatch == NULL) {
    1721             :         /* The tevent request was cancelled while the ares call was still in
    1722             :          * progress so nobody cares about the result now. Quit. */
    1723           0 :         unschedule_timeout_watcher(rreq->ctx, rreq);
    1724           0 :         return;
    1725             :     }
    1726             : 
    1727           1 :     req = rreq->rwatch->req;
    1728           1 :     unschedule_timeout_watcher(rreq->ctx, rreq);
    1729           1 :     state = tevent_req_data(req, struct getsrv_state);
    1730             : 
    1731           1 :     if (state->retrying == 0 && status == ARES_EDESTRUCTION
    1732           0 :         && state->resolv_ctx->channel != NULL) {
    1733           0 :         state->retrying = 1;
    1734           0 :         resolv_getsrv_query(req, state);
    1735           0 :         return;
    1736             :     }
    1737             : 
    1738           1 :     state->status = status;
    1739           1 :     state->timeouts = timeouts;
    1740             : 
    1741           1 :     if (status != ARES_SUCCESS) {
    1742           0 :         ret = return_code(status);
    1743           0 :         goto fail;
    1744             :     }
    1745             : 
    1746           1 :     ret = ares_parse_srv_reply(abuf, alen, &reply_list);
    1747           1 :     if (ret != ARES_SUCCESS) {
    1748           0 :         DEBUG(SSSDBG_OP_FAILURE,
    1749             :               "SRV record parsing failed: %d: %s\n", ret, ares_strerror(ret));
    1750           0 :         ret = return_code(ret);
    1751           0 :         goto fail;
    1752             :     }
    1753           1 :     ret = rewrite_talloc_srv_reply(req, &reply_list);
    1754           1 :     if (ret != EOK) {
    1755           0 :         goto fail;
    1756             :     }
    1757           1 :     state->reply_list = reply_list;
    1758           1 :     ok = resolv_get_ttl(abuf, alen, &state->ttl);
    1759           1 :     if (ok == false) {
    1760           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "Could not read TTL, using the default..\n");
    1761           0 :         state->ttl = RESOLV_DEFAULT_SRV_TTL;
    1762             :     }
    1763           1 :     DEBUG(SSSDBG_TRACE_LIBS, "Using TTL [%"PRIu32"]\n", state->ttl);
    1764             : 
    1765           1 :     tevent_req_done(req);
    1766           1 :     return;
    1767             : 
    1768             : fail:
    1769           0 :     state->reply_list = NULL;
    1770           0 :     tevent_req_error(req, ret);
    1771             : }
    1772             : 
    1773             : int
    1774           1 : resolv_getsrv_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, int *status,
    1775             :                    int *timeouts, struct ares_srv_reply **reply_list,
    1776             :                    uint32_t *ttl)
    1777             : {
    1778           1 :     struct getsrv_state *state = tevent_req_data(req, struct getsrv_state);
    1779             : 
    1780           1 :     if (status)
    1781           1 :         *status = state->status;
    1782           1 :     if (timeouts)
    1783           0 :         *timeouts = state->timeouts;
    1784           1 :     if (reply_list)
    1785           1 :         *reply_list = talloc_steal(mem_ctx, state->reply_list);
    1786           1 :     if (ttl) {
    1787           1 :         *ttl = state->ttl;
    1788             :     }
    1789             : 
    1790           1 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1791             : 
    1792           1 :     return EOK;
    1793             : }
    1794             : 
    1795             : static void
    1796           1 : ares_getsrv_wakeup(struct tevent_req *subreq)
    1797             : {
    1798           1 :     struct tevent_req *req = tevent_req_callback_data(subreq,
    1799             :                                                 struct tevent_req);
    1800           1 :     struct getsrv_state *state = tevent_req_data(req,
    1801             :                                                 struct getsrv_state);
    1802             : 
    1803           1 :     if (!tevent_wakeup_recv(subreq)) {
    1804           0 :         return;
    1805             :     }
    1806           1 :     talloc_zfree(subreq);
    1807             : 
    1808           1 :     if (state->resolv_ctx->channel == NULL) {
    1809           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1810             :               "Invalid ares channel - this is likely a bug\n");
    1811           0 :         tevent_req_error(req, EIO);
    1812           0 :         return;
    1813             :     }
    1814             : 
    1815           1 :     return resolv_getsrv_query(req, state);
    1816             : }
    1817             : 
    1818             : static void
    1819           1 : resolv_getsrv_query(struct tevent_req *req,
    1820             :                     struct getsrv_state *state)
    1821             : {
    1822             :     struct resolv_request *rreq;
    1823             : 
    1824           1 :     rreq = schedule_timeout_watcher(state->ev, state->resolv_ctx, req);
    1825           1 :     if (!rreq) {
    1826           0 :         tevent_req_error(req, ENOMEM);
    1827           0 :         return;
    1828             :     }
    1829             : 
    1830           1 :     ares_query(state->resolv_ctx->channel, state->query,
    1831             :                ns_c_in, ns_t_srv, resolv_getsrv_done, rreq);
    1832             : }
    1833             : 
    1834             : /* TXT parsing is not used anywhere in the code yet, so we disable it
    1835             :  * for now
    1836             :  */
    1837             : #ifdef BUILD_TXT
    1838             : 
    1839             : /*
    1840             :  * A simple helper function that will take an array of struct txt_reply that
    1841             :  * was allocated by malloc() in c-ares and copies it using talloc. The old one
    1842             :  * is freed and the talloc one is put into 'reply_list' instead.
    1843             :  */
    1844             : static int
    1845           0 : rewrite_talloc_txt_reply(TALLOC_CTX *mem_ctx, struct ares_txt_reply **reply_list)
    1846             : {
    1847           0 :     struct ares_txt_reply *ptr = NULL;
    1848           0 :     struct ares_txt_reply *new_list = NULL;
    1849           0 :     struct ares_txt_reply *old_list = *reply_list;
    1850             : 
    1851             :     /* Nothing to do, but not an error */
    1852           0 :     if (!old_list) {
    1853           0 :         return EOK;
    1854             :     }
    1855             : 
    1856             :     /* Copy the linked list */
    1857           0 :     while (old_list) {
    1858             : 
    1859             :         /* Special case for the first node */
    1860           0 :         if (!new_list) {
    1861           0 :             new_list = talloc_zero(mem_ctx, struct ares_txt_reply);
    1862           0 :             if (new_list == NULL) {
    1863           0 :                 ares_free_data(*reply_list);
    1864           0 :                 talloc_free(new_list);
    1865           0 :                 return ENOMEM;
    1866             :             }
    1867           0 :             ptr = new_list;
    1868             :         } else {
    1869           0 :             ptr->next = talloc_zero(new_list, struct ares_txt_reply);
    1870           0 :             if (ptr->next == NULL) {
    1871           0 :                 ares_free_data(*reply_list);
    1872           0 :                 talloc_free(new_list);
    1873           0 :                 return ENOMEM;
    1874             :             }
    1875           0 :             ptr = ptr->next;
    1876             :         }
    1877             : 
    1878           0 :         ptr->length = old_list->length;
    1879           0 :         ptr->txt = talloc_memdup(ptr, old_list->txt,
    1880             :                                  old_list->length);
    1881           0 :         if (ptr->txt == NULL) {
    1882           0 :             ares_free_data(*reply_list);
    1883           0 :             talloc_free(new_list);
    1884           0 :             return ENOMEM;
    1885             :         }
    1886             : 
    1887           0 :         old_list = old_list->next;
    1888             :     }
    1889             : 
    1890           0 :     ares_free_data(*reply_list);
    1891             : 
    1892             :     /* And now put our own new_list in place. */
    1893           0 :     *reply_list = new_list;
    1894             : 
    1895           0 :     return EOK;
    1896             : }
    1897             : 
    1898             : /*******************************************************************
    1899             :  * Get TXT record                                                  *
    1900             :  *******************************************************************/
    1901             : 
    1902             : struct gettxt_state {
    1903             :     struct tevent_context *ev;
    1904             :     struct resolv_ctx *resolv_ctx;
    1905             :     /* the TXT query */
    1906             :     const char *query;
    1907             : 
    1908             :     /* parsed data returned by ares */
    1909             :     struct ares_txt_reply *reply_list;
    1910             :     int status;
    1911             :     int timeouts;
    1912             :     int retrying;
    1913             : };
    1914             : 
    1915             : static void
    1916             : ares_gettxt_wakeup(struct tevent_req *subreq);
    1917             : static void
    1918             : resolv_gettxt_query(struct tevent_req *req,
    1919             :                     struct gettxt_state *state);
    1920             : 
    1921             : struct tevent_req *
    1922           0 : resolv_gettxt_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
    1923             :                           struct resolv_ctx *ctx, const char *query)
    1924             : {
    1925             :     struct tevent_req *req, *subreq;
    1926             :     struct gettxt_state *state;
    1927           0 :     struct timeval tv = { 0, 0 };
    1928             : 
    1929           0 :     DEBUG(SSSDBG_CONF_SETTINGS,
    1930             :           "Trying to resolve TXT record of '%s'\n", query);
    1931             : 
    1932           0 :     if (ctx->channel == NULL) {
    1933           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1934             :               "Invalid ares channel - this is likely a bug\n");
    1935           0 :         return NULL;
    1936             :     }
    1937             : 
    1938           0 :     req = tevent_req_create(mem_ctx, &state, struct gettxt_state);
    1939           0 :     if (req == NULL)
    1940           0 :         return NULL;
    1941             : 
    1942           0 :     state->resolv_ctx = ctx;
    1943           0 :     state->query = query;
    1944           0 :     state->reply_list = NULL;
    1945           0 :     state->status = 0;
    1946           0 :     state->timeouts = 0;
    1947           0 :     state->retrying = 0;
    1948           0 :     state->ev = ev;
    1949             : 
    1950           0 :     subreq = tevent_wakeup_send(req, ev, tv);
    1951           0 :     if (subreq == NULL) {
    1952           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1953             :               "Failed to add critical timer to run next operation!\n");
    1954           0 :         talloc_zfree(req);
    1955           0 :         return NULL;
    1956             :     }
    1957           0 :     tevent_req_set_callback(subreq, ares_gettxt_wakeup, req);
    1958             : 
    1959           0 :     return req;
    1960             : }
    1961             : 
    1962             : static void
    1963           0 : resolv_gettxt_done(void *arg, int status, int timeouts, unsigned char *abuf, int alen)
    1964             : {
    1965           0 :     struct resolv_request *rreq = talloc_get_type(arg, struct resolv_request);
    1966             :     struct tevent_req *req;
    1967             :     struct gettxt_state *state;
    1968             :     int ret;
    1969             :     struct ares_txt_reply *reply_list;
    1970             : 
    1971           0 :     if (rreq->rwatch == NULL) {
    1972             :         /* The tevent request was cancelled while the ares call was still in
    1973             :          * progress so nobody cares about the result now. Quit. */
    1974           0 :         unschedule_timeout_watcher(rreq->ctx, rreq);
    1975           0 :         return;
    1976             :     }
    1977             : 
    1978           0 :     req = rreq->rwatch->req;
    1979           0 :     unschedule_timeout_watcher(rreq->ctx, rreq);
    1980           0 :     state = tevent_req_data(req, struct gettxt_state);
    1981             : 
    1982           0 :     if (state->retrying == 0 && status == ARES_EDESTRUCTION
    1983           0 :             && state->resolv_ctx->channel != NULL) {
    1984           0 :         state->retrying = 1;
    1985           0 :         ares_query(state->resolv_ctx->channel, state->query,
    1986             :                    ns_c_in, ns_t_txt, resolv_gettxt_done, req);
    1987           0 :         return;
    1988             :     }
    1989             : 
    1990           0 :     state->status = status;
    1991           0 :     state->timeouts = timeouts;
    1992             : 
    1993           0 :     if (status != ARES_SUCCESS) {
    1994           0 :         ret = return_code(status);
    1995           0 :         goto fail;
    1996             :     }
    1997             : 
    1998           0 :     ret = ares_parse_txt_reply(abuf, alen, &reply_list);
    1999           0 :     if (status != ARES_SUCCESS) {
    2000           0 :         DEBUG(SSSDBG_OP_FAILURE,
    2001             :               "TXT record parsing failed: %d: %s\n", ret, ares_strerror(ret));
    2002           0 :         ret = return_code(ret);
    2003           0 :         goto fail;
    2004             :     }
    2005           0 :     ret = rewrite_talloc_txt_reply(req, &reply_list);
    2006           0 :     if (ret != EOK) {
    2007           0 :         goto fail;
    2008             :     }
    2009           0 :     state->reply_list = reply_list;
    2010             : 
    2011           0 :     tevent_req_done(req);
    2012           0 :     return;
    2013             : 
    2014             : fail:
    2015           0 :     state->reply_list = NULL;
    2016           0 :     tevent_req_error(req, ret);
    2017             : }
    2018             : 
    2019             : int
    2020           0 : resolv_gettxt_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, int *status,
    2021             :                    int *timeouts, struct ares_txt_reply **reply_list)
    2022             : {
    2023           0 :     struct gettxt_state *state = tevent_req_data(req, struct gettxt_state);
    2024             : 
    2025           0 :     if (status)
    2026           0 :         *status = state->status;
    2027           0 :     if (timeouts)
    2028           0 :         *timeouts = state->timeouts;
    2029           0 :     if (reply_list)
    2030           0 :         *reply_list = talloc_steal(mem_ctx, state->reply_list);
    2031             : 
    2032           0 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    2033             : 
    2034           0 :     return EOK;
    2035             : }
    2036             : 
    2037             : static void
    2038           0 : ares_gettxt_wakeup(struct tevent_req *subreq)
    2039             : {
    2040           0 :     struct tevent_req *req = tevent_req_callback_data(subreq,
    2041             :                                             struct tevent_req);
    2042           0 :     struct gettxt_state *state = tevent_req_data(req,
    2043             :                                             struct gettxt_state);
    2044             : 
    2045           0 :     if (!tevent_wakeup_recv(subreq)) {
    2046           0 :         return;
    2047             :     }
    2048           0 :     talloc_zfree(subreq);
    2049             : 
    2050           0 :     if (state->resolv_ctx->channel == NULL) {
    2051           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    2052             :               "Invalid ares channel - this is likely a bug\n");
    2053           0 :         tevent_req_error(req, EIO);
    2054           0 :         return;
    2055             :     }
    2056             : 
    2057           0 :     return resolv_gettxt_query(req, state);
    2058             : }
    2059             : 
    2060             : static void
    2061           0 : resolv_gettxt_query(struct tevent_req *req,
    2062             :                     struct gettxt_state *state)
    2063             : {
    2064             :     struct resolv_request *rreq;
    2065             : 
    2066           0 :     rreq = schedule_timeout_watcher(state->ev, state->resolv_ctx, req);
    2067           0 :     if (!rreq) {
    2068           0 :         tevent_req_error(req, ENOMEM);
    2069           0 :         return;
    2070             :     }
    2071             : 
    2072           0 :     ares_query(state->resolv_ctx->channel, state->query,
    2073             :                ns_c_in, ns_t_txt, resolv_gettxt_done, rreq);
    2074             : }
    2075             : 
    2076             : #endif
    2077             : 
    2078           9 : static struct ares_srv_reply *split_reply_list(struct ares_srv_reply *list)
    2079             : {
    2080             :     struct ares_srv_reply *single_step, *double_step, *prev;
    2081             : 
    2082           9 :     if (!list) {
    2083           0 :         return NULL;
    2084             :     }
    2085             : 
    2086           9 :     prev        = list;
    2087           9 :     single_step = list->next;
    2088           9 :     double_step = single_step->next;
    2089             : 
    2090          20 :     while (double_step && double_step->next) {
    2091           2 :         prev = single_step;
    2092           2 :         single_step = single_step->next;
    2093           2 :         double_step = double_step->next->next;
    2094             :     }
    2095             : 
    2096           9 :     prev->next = NULL;
    2097           9 :     return single_step;
    2098             : }
    2099             : 
    2100           9 : static struct ares_srv_reply *merge_reply_list(struct ares_srv_reply *left,
    2101             :                                                struct ares_srv_reply *right)
    2102             : {
    2103             :     struct ares_srv_reply *l, *r;
    2104             :     struct ares_srv_reply *res, *res_start;
    2105             : 
    2106           9 :     if (!left)
    2107           0 :         return right;
    2108           9 :     if (!right)
    2109           0 :         return left;
    2110             : 
    2111           9 :     if (left->priority < right->priority) {
    2112           3 :         res_start = left;
    2113           3 :         l = left->next;
    2114           3 :         r = right;
    2115             :     } else {
    2116           6 :         res_start = right;
    2117           6 :         l = left;
    2118           6 :         r = right->next;
    2119             :     }
    2120             : 
    2121           9 :     res = res_start;
    2122             : 
    2123          24 :     while(l && r) {
    2124           6 :         if (l->priority < r->priority) {
    2125           4 :             res->next = l;
    2126           4 :             res = l;
    2127           4 :             l = l->next;
    2128             :         } else {
    2129           2 :             res->next = r;
    2130           2 :             res = r;
    2131           2 :             r = r->next;
    2132             :         }
    2133             :     }
    2134             : 
    2135           9 :     res->next = l ? l : r;
    2136             : 
    2137           9 :     return res_start;
    2138             : }
    2139             : 
    2140             : /**
    2141             :  * sort linked list of struct ares_srv_reply by priority using merge sort.
    2142             :  *
    2143             :  * Merge sort is ideal for sorting linked lists as there is no problem
    2144             :  * with absence of random access into the list. The complexity is O(n log n)
    2145             :  *
    2146             :  * For reference, see Robert Sedgewick's "Algorithms in C", Addison-Wesley,
    2147             :  * ISBN 0-201-51425
    2148             :  */
    2149          21 : static struct ares_srv_reply *reply_priority_sort(struct ares_srv_reply *list)
    2150             : {
    2151             :     struct ares_srv_reply *half;
    2152             : 
    2153          21 :     if (!list || !list->next)
    2154          12 :         return list;
    2155             : 
    2156           9 :     half = split_reply_list(list);
    2157           9 :     list = merge_reply_list(reply_priority_sort(list),
    2158             :                             reply_priority_sort(half));
    2159             : 
    2160           9 :     return list;
    2161             : }
    2162             : 
    2163           8 : static int reply_weight_rearrange(int len,
    2164             :                                   struct ares_srv_reply **start,
    2165             :                                   struct ares_srv_reply **end)
    2166             : {
    2167             :     int i;
    2168             :     int total, selected;
    2169             :     int *totals;
    2170             :     struct ares_srv_reply *r, *prev, *tmp;
    2171           8 :     struct ares_srv_reply *new_start = NULL;
    2172           8 :     struct ares_srv_reply *new_end = NULL;
    2173             :     int ret;
    2174             : 
    2175           8 :     if (len <= 1) {
    2176           4 :         return EOK;
    2177             :     }
    2178             : 
    2179           4 :     totals = talloc_array(NULL, int, len);
    2180           4 :     if (!totals) {
    2181           0 :         return ENOMEM;
    2182             :     }
    2183             : 
    2184           4 :     srand(time(NULL) * getpid());
    2185             : 
    2186             :     /* promote all servers with weight==0 to the top */
    2187           4 :     r = *(start);
    2188           4 :     prev = NULL;
    2189          16 :     while (r != NULL) {
    2190           8 :         if (r->weight == 0 && r != *start) {
    2191             :             /* remove from the old list */
    2192           4 :             prev->next = r->next;
    2193             : 
    2194             :             /* add to the head of the new list */
    2195           4 :             tmp = r;
    2196           4 :             r = r->next;
    2197             : 
    2198           4 :             tmp->next = *start;
    2199           4 :             *start = tmp;
    2200             :         } else {
    2201           4 :             prev = r;
    2202           4 :             r = r->next;
    2203             :         }
    2204             :     }
    2205           4 :     *end = prev ? prev : *start;
    2206             : 
    2207          16 :     while (*start != NULL) {
    2208             :         /* Commpute the sum of the weights of those RRs, and with each RR
    2209             :          * associate the running sum in the selected order.
    2210             :          */
    2211           8 :         total = 0;
    2212           8 :         memset(totals, -1, sizeof(int) * len);
    2213          20 :         for (i = 0, r = *start; r != NULL; r=r->next, ++i) {
    2214          12 :             totals[i] = r->weight + total;
    2215          12 :             total = totals[i];
    2216             :         }
    2217             : 
    2218             :         /* choose a  uniform random number between 0 and the sum computed
    2219             :          * (inclusive), and select the RR whose running sum value is the
    2220             :          * first in the selected order which is greater than or equal to
    2221             :          * the random number selected.
    2222             :          */
    2223           8 :         selected = (int)((total + 1) * (rand()/(RAND_MAX + 1.0)));
    2224           9 :         for (i = 0, r = *start, prev = NULL; r != NULL; r=r->next, ++i) {
    2225           9 :             if (totals[i] >= selected)
    2226           8 :                 break;
    2227             : 
    2228           1 :             prev = r;
    2229             :         }
    2230             : 
    2231           8 :         if (r == NULL || totals[i] == -1) {
    2232           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Bug: did not select any server!\n");
    2233           0 :             ret = EIO;
    2234           0 :             goto done;
    2235             :         }
    2236             : 
    2237             :         /* remove r from the old list */
    2238           8 :         if (prev) {
    2239           1 :             prev->next = r->next;
    2240             :         } else {
    2241           7 :             *start = r->next;
    2242             :         }
    2243             : 
    2244             :         /* add r to the end of the new list */
    2245           8 :         if (!new_start) {
    2246           4 :             new_start = r;
    2247           4 :             new_end = r;
    2248             :         } else {
    2249           4 :             new_end->next = r;
    2250           4 :             new_end = r;
    2251             :         }
    2252             :     }
    2253           4 :     new_end->next = NULL;
    2254             : 
    2255             :     /* return the rearranged list */
    2256           4 :     *start = new_start;
    2257           4 :     *end = new_end;
    2258           4 :     ret = EOK;
    2259             : 
    2260             : done:
    2261           4 :     talloc_free(totals);
    2262           4 :     return ret;
    2263             : }
    2264             : 
    2265             : int
    2266           3 : resolv_sort_srv_reply(struct ares_srv_reply **reply)
    2267             : {
    2268             :     int ret;
    2269             :     struct ares_srv_reply *pri_start, *pri_end, *next, *prev_end;
    2270             :     int len;
    2271             : 
    2272             :     /* RFC 2782 says: If there is precisely one SRV RR, and its Target is "."
    2273             :      * (the root domain), abort.
    2274             :      */
    2275           3 :     if (*reply && !(*reply)->next && strcmp((*reply)->host, ".") == 0) {
    2276           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    2277             :               "DNS returned only the root domain, aborting\n");
    2278           0 :         return EIO;
    2279             :     }
    2280             : 
    2281             :     /* sort the list by priority */
    2282           3 :     *reply = reply_priority_sort(*reply);
    2283             : 
    2284           3 :     pri_start = *reply;
    2285           3 :     prev_end  = NULL;
    2286             : 
    2287          14 :     while (pri_start) {
    2288           8 :         pri_end = pri_start;
    2289             : 
    2290             :         /* Find nodes with the same priority */
    2291           8 :         len = 1;
    2292          20 :         while (pri_end->next && pri_end->priority == pri_end->next->priority) {
    2293           4 :             pri_end = pri_end->next;
    2294           4 :             len++;
    2295             :         }
    2296             : 
    2297             :         /* rearrange each priority level according to the weight field */
    2298           8 :         next = pri_end->next;
    2299           8 :         pri_end->next = NULL;
    2300           8 :         ret = reply_weight_rearrange(len, &pri_start, &pri_end);
    2301           8 :         if (ret) {
    2302           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    2303             :                   "Error rearranging priority level [%d]: %s\n",
    2304             :                       ret, strerror(ret));
    2305           0 :             return ret;
    2306             :         }
    2307             : 
    2308             :         /* Hook the level back into the list */
    2309           8 :         if (prev_end) {
    2310           5 :             prev_end->next = pri_start;
    2311             :         } else {
    2312           3 :             *reply = pri_start;
    2313             :         }
    2314           8 :         pri_end->next = next;
    2315             : 
    2316             :         /* Move on to the next level */
    2317           8 :         prev_end  = pri_end;
    2318           8 :         pri_start = next;
    2319             :     }
    2320             : 
    2321           3 :     return EOK;
    2322             : }

Generated by: LCOV version 1.10