Blame resolv/nss_dns/dns-canon.c

Packit 6c4009
/* Copyright (C) 2004-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <netdb.h>
Packit 6c4009
#include <resolv.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
#include <arpa/nameser.h>
Packit 6c4009
#include <nsswitch.h>
Packit 6c4009
#include <resolv/resolv_context.h>
Packit 6c4009
#include <resolv/resolv-internal.h>
Packit 6c4009
Packit 6c4009
#if PACKETSZ > 65536
Packit 6c4009
# define MAXPACKET	PACKETSZ
Packit 6c4009
#else
Packit 6c4009
# define MAXPACKET	65536
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* We need this time later.  */
Packit 6c4009
typedef union querybuf
Packit 6c4009
{
Packit 6c4009
  HEADER hdr;
Packit 6c4009
  unsigned char buf[MAXPACKET];
Packit 6c4009
} querybuf;
Packit 6c4009
Packit 6c4009
Packit 6c4009
static const short int qtypes[] = { ns_t_a, ns_t_aaaa };
Packit 6c4009
#define nqtypes (sizeof (qtypes) / sizeof (qtypes[0]))
Packit 6c4009
Packit 6c4009
Packit 6c4009
enum nss_status
Packit 6c4009
_nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen,
Packit 6c4009
			 char **result,int *errnop, int *h_errnop)
Packit 6c4009
{
Packit 6c4009
  /* Just an alibi buffer, res_nquery will allocate a real buffer for
Packit 6c4009
     us.  */
Packit 6c4009
  unsigned char buf[20];
Packit 6c4009
  union
Packit 6c4009
  {
Packit 6c4009
    querybuf *buf;
Packit 6c4009
    unsigned char *ptr;
Packit 6c4009
  } ansp = { .ptr = buf };
Packit 6c4009
  enum nss_status status = NSS_STATUS_UNAVAIL;
Packit 6c4009
Packit 6c4009
  struct resolv_context *ctx = __resolv_context_get ();
Packit 6c4009
  if (ctx == NULL)
Packit 6c4009
    {
Packit 6c4009
      *errnop = errno;
Packit 6c4009
      *h_errnop = NETDB_INTERNAL;
Packit 6c4009
      return NSS_STATUS_UNAVAIL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  for (int i = 0; i < nqtypes; ++i)
Packit 6c4009
    {
Packit 6c4009
      int r = __res_context_query (ctx, name, ns_c_in, qtypes[i],
Packit 6c4009
				   buf, sizeof (buf), &ansp.ptr, NULL, NULL,
Packit 6c4009
				   NULL, NULL);
Packit 6c4009
      if (r > 0)
Packit 6c4009
	{
Packit 6c4009
	  /* We need to decode the response.  Just one question record.
Packit 6c4009
	     And if we got no answers we bail out, too.  */
Packit 6c4009
	  if (ansp.buf->hdr.qdcount != htons (1))
Packit 6c4009
	    continue;
Packit 6c4009
Packit 6c4009
	  /* Number of answers.   */
Packit 6c4009
	  unsigned int ancount = ntohs (ansp.buf->hdr.ancount);
Packit 6c4009
Packit 6c4009
	  /* Beginning and end of the buffer with query, answer, and the
Packit 6c4009
	     rest.  */
Packit 6c4009
	  unsigned char *ptr = &ansp.buf->buf[sizeof (HEADER)];
Packit 6c4009
	  unsigned char *endptr = ansp.ptr + r;
Packit 6c4009
Packit 6c4009
	  /* Skip over the query.  This is the name, type, and class.  */
Packit 6c4009
	  int s = __dn_skipname (ptr, endptr);
Packit 6c4009
	  if (s < 0)
Packit 6c4009
	    {
Packit 6c4009
	    unavail:
Packit 6c4009
	      status = NSS_STATUS_UNAVAIL;
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Skip over the name and the two 16-bit values containing type
Packit 6c4009
	     and class.  */
Packit 6c4009
	  ptr += s + 2 * sizeof (uint16_t);
Packit 6c4009
Packit 6c4009
	  while (ancount-- > 0)
Packit 6c4009
	    {
Packit 6c4009
	      /* Now the reply.  First again the name from the query,
Packit 6c4009
		 then type, class, TTL, and the length of the RDATA.
Packit 6c4009
		 We remember the name start.  */
Packit 6c4009
	      unsigned char *namestart = ptr;
Packit 6c4009
	      s = __dn_skipname (ptr, endptr);
Packit 6c4009
	      if (s < 0)
Packit 6c4009
		goto unavail;
Packit 6c4009
Packit 6c4009
	      ptr += s;
Packit 6c4009
Packit 6c4009
	      /* Check that there are enough bytes for the RR
Packit 6c4009
		 metadata.  */
Packit 6c4009
	      if (endptr - ptr < 10)
Packit 6c4009
		goto unavail;
Packit 6c4009
Packit 6c4009
	      /* Check whether type and class match.  */
Packit 6c4009
	      uint_fast16_t type;
Packit 6c4009
	      NS_GET16 (type, ptr);
Packit 6c4009
	      if (type == qtypes[i])
Packit 6c4009
		{
Packit 6c4009
		  /* We found the record.  */
Packit 6c4009
		  s = __dn_expand (ansp.buf->buf, endptr, namestart,
Packit 6c4009
				   buffer, buflen);
Packit 6c4009
		  if (s < 0)
Packit 6c4009
		    {
Packit 6c4009
		      if (errno != EMSGSIZE)
Packit 6c4009
			goto unavail;
Packit 6c4009
Packit 6c4009
		      /* The buffer is too small.  */
Packit 6c4009
		      *errnop = ERANGE;
Packit 6c4009
		      status = NSS_STATUS_TRYAGAIN;
Packit 6c4009
		      h_errno = NETDB_INTERNAL;
Packit 6c4009
		    }
Packit 6c4009
		  else
Packit 6c4009
		    {
Packit 6c4009
		      /* Success.  */
Packit 6c4009
		      *result = buffer;
Packit 6c4009
		      status = NSS_STATUS_SUCCESS;
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  goto out;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      if (type != ns_t_cname)
Packit 6c4009
		goto unavail;
Packit 6c4009
Packit 6c4009
	      if (__ns_get16 (ptr) != ns_c_in)
Packit 6c4009
		goto unavail;
Packit 6c4009
Packit 6c4009
	      /* Also skip over class and TTL.  */
Packit 6c4009
	      ptr += sizeof (uint16_t) + sizeof (uint32_t);
Packit 6c4009
Packit 6c4009
	      /* Skip over RDATA length and RDATA itself.  */
Packit 6c4009
	      uint16_t rdatalen = __ns_get16 (ptr);
Packit 6c4009
	      ptr += sizeof (uint16_t);
Packit 6c4009
	      /* Not enough room for RDATA.  */
Packit 6c4009
	      if (endptr - ptr < rdatalen)
Packit 6c4009
		goto unavail;
Packit 6c4009
	      ptr += rdatalen;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Restore original buffer before retry.  */
Packit 6c4009
      if (ansp.ptr != buf)
Packit 6c4009
	{
Packit 6c4009
	  free (ansp.ptr);
Packit 6c4009
	  ansp.ptr = buf;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
 out:
Packit 6c4009
  *h_errnop = h_errno;
Packit 6c4009
Packit 6c4009
  if (ansp.ptr != buf)
Packit 6c4009
    free (ansp.ptr);
Packit 6c4009
  __resolv_context_put (ctx);
Packit 6c4009
  return status;
Packit 6c4009
}