(root)/
glibc-2.38/
resolv/
resolv_context.c
       1  /* Temporary, thread-local resolver state.
       2     Copyright (C) 2017-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <resolv_context.h>
      20  #include <resolv_conf.h>
      21  #include <resolv-internal.h>
      22  
      23  #include <assert.h>
      24  #include <errno.h>
      25  #include <stdlib.h>
      26  #include <stdio.h>
      27  
      28  /* Currently active struct resolv_context object.  This pointer forms
      29     the start of a single-linked list, using the __next member of
      30     struct resolv_context.  This list serves two purposes:
      31  
      32     (a) A subsequent call to __resolv_context_get will only increment
      33         the reference counter and will not allocate a new object.  The
      34         _res state freshness check is skipped in this case, too.
      35  
      36     (b) The per-thread cleanup function defined by the resolver calls
      37         __resolv_context_freeres, which will deallocate all the context
      38         objects.  This avoids the need for cancellation handlers and
      39         the complexity they bring, but it requires heap allocation of
      40         the context object because the per-thread cleanup functions run
      41         only after the stack has been fully unwound (and all on-stack
      42         objects have been deallocated at this point).
      43  
      44     The TLS variable current is updated even in
      45     __resolv_context_get_override, to support case (b) above.  This does
      46     not override the per-thread resolver state (as obtained by the
      47     non-res_state function such as __resolv_context_get) in an
      48     observable way because the wrapped context is only used to
      49     implement the res_n* functions in the resolver, and those do not
      50     call back into user code which could indirectly use the per-thread
      51     resolver state.  */
      52  static __thread struct resolv_context *current attribute_tls_model_ie;
      53  
      54  /* The resolv_conf handling will gives us a ctx->conf pointer even if
      55     these fields do not match because a mismatch does not cause a loss
      56     of state (_res objects can store the full information).  This
      57     function checks to ensure that there is a full patch, to prevent
      58     overwriting a patched configuration.  */
      59  static bool
      60  replicated_configuration_matches (const struct resolv_context *ctx)
      61  {
      62    return ctx->resp->options == ctx->conf->options
      63      && ctx->resp->retrans == ctx->conf->retrans
      64      && ctx->resp->retry == ctx->conf->retry
      65      && ctx->resp->ndots == ctx->conf->ndots;
      66  }
      67  
      68  /* Initialize *RESP if RES_INIT is not yet set in RESP->options, or if
      69     res_init in some other thread requested re-initializing.  */
      70  static __attribute__ ((warn_unused_result)) bool
      71  maybe_init (struct resolv_context *ctx, bool preinit)
      72  {
      73    struct __res_state *resp = ctx->resp;
      74    if (resp->options & RES_INIT)
      75      {
      76        if (resp->options & RES_NORELOAD)
      77          /* Configuration reloading was explicitly disabled.  */
      78          return true;
      79  
      80        /* If there is no associated resolv_conf object despite the
      81           initialization, something modified *ctx->resp.  Do not
      82           override those changes.  */
      83        if (ctx->conf != NULL && replicated_configuration_matches (ctx))
      84          {
      85            struct resolv_conf *current = __resolv_conf_get_current ();
      86            if (current == NULL)
      87              return false;
      88  
      89            /* Check if the configuration changed.  */
      90            if (current != ctx->conf)
      91              {
      92                /* This call will detach the extended resolver state.  */
      93                if (resp->nscount > 0)
      94                  __res_iclose (resp, true);
      95                /* Reattach the current configuration.  */
      96                if (__resolv_conf_attach (ctx->resp, current))
      97                  {
      98                    __resolv_conf_put (ctx->conf);
      99                    /* ctx takes ownership, so we do not release current.  */
     100                    ctx->conf = current;
     101                  }
     102              }
     103            else
     104              /* No change.  Drop the reference count for current.  */
     105              __resolv_conf_put (current);
     106          }
     107        return true;
     108      }
     109  
     110    assert (ctx->conf == NULL);
     111    if (preinit)
     112      {
     113        if (!resp->retrans)
     114          resp->retrans = RES_TIMEOUT;
     115        if (!resp->retry)
     116          resp->retry = RES_DFLRETRY;
     117        resp->options = RES_DEFAULT;
     118        if (!resp->id)
     119          resp->id = res_randomid ();
     120      }
     121  
     122    if (__res_vinit (resp, preinit) < 0)
     123      return false;
     124    ctx->conf = __resolv_conf_get (ctx->resp);
     125    return true;
     126  }
     127  
     128  /* Allocate a new context object and initialize it.  The object is put
     129     on the current list.  */
     130  static struct resolv_context *
     131  context_alloc (struct __res_state *resp)
     132  {
     133    struct resolv_context *ctx = malloc (sizeof (*ctx));
     134    if (ctx == NULL)
     135      return NULL;
     136    ctx->resp = resp;
     137    ctx->conf = __resolv_conf_get (resp);
     138    ctx->__refcount = 1;
     139    ctx->__from_res = true;
     140    ctx->__next = current;
     141    current = ctx;
     142    return ctx;
     143  }
     144  
     145  /* Deallocate the context object and all the state within.   */
     146  static void
     147  context_free (struct resolv_context *ctx)
     148  {
     149    int error_code = errno;
     150    current = ctx->__next;
     151    __resolv_conf_put (ctx->conf);
     152    free (ctx);
     153    __set_errno (error_code);
     154  }
     155  
     156  /* Reuse the current context object.  */
     157  static struct resolv_context *
     158  context_reuse (void)
     159  {
     160    /* A context object created by __resolv_context_get_override cannot
     161       be reused.  */
     162    assert (current->__from_res);
     163  
     164    ++current->__refcount;
     165  
     166    /* Check for reference counter wraparound.  This can only happen if
     167       the get/put functions are not properly paired.  */
     168    assert (current->__refcount > 0);
     169  
     170    return current;
     171  }
     172  
     173  /* Backing function for the __resolv_context_get family of
     174     functions.  */
     175  static struct resolv_context *
     176  context_get (bool preinit)
     177  {
     178    if (current != NULL)
     179      return context_reuse ();
     180  
     181    struct resolv_context *ctx = context_alloc (&_res);
     182    if (ctx == NULL)
     183      return NULL;
     184    if (!maybe_init (ctx, preinit))
     185      {
     186        context_free (ctx);
     187        return NULL;
     188      }
     189    return ctx;
     190  }
     191  
     192  struct resolv_context *
     193  __resolv_context_get (void)
     194  {
     195    return context_get (false);
     196  }
     197  libc_hidden_def (__resolv_context_get)
     198  
     199  struct resolv_context *
     200  __resolv_context_get_preinit (void)
     201  {
     202    return context_get (true);
     203  }
     204  libc_hidden_def (__resolv_context_get_preinit)
     205  
     206  struct resolv_context *
     207  __resolv_context_get_override (struct __res_state *resp)
     208  {
     209    /* NB: As explained asbove, context_alloc will put the context on
     210       the current list.  */
     211    struct resolv_context *ctx = context_alloc (resp);
     212    if (ctx == NULL)
     213      return NULL;
     214  
     215    ctx->__from_res = false;
     216    return ctx;
     217  }
     218  libc_hidden_def (__resolv_context_get_override)
     219  
     220  void
     221  __resolv_context_put (struct resolv_context *ctx)
     222  {
     223    if (ctx == NULL)
     224      return;
     225  
     226    /* NB: Callers assume that this function preserves errno and
     227       h_errno.  */
     228  
     229    assert (current == ctx);
     230    assert (ctx->__refcount > 0);
     231  
     232    if (ctx->__from_res && --ctx->__refcount > 0)
     233      /* Do not pop this context yet.  */
     234      return;
     235  
     236    context_free (ctx);
     237  }
     238  libc_hidden_def (__resolv_context_put)
     239  
     240  void
     241  __resolv_context_freeres (void)
     242  {
     243    /* Deallocate the entire chain of context objects.  */
     244    struct resolv_context *ctx = current;
     245    current = NULL;
     246    while (ctx != NULL)
     247      {
     248        struct resolv_context *next = ctx->__next;
     249        context_free (ctx);
     250        ctx = next;
     251      }
     252  }