/*      $NetBSD: ns_samedomain.c,v 1.1.1.2 2012/09/09 16:08:03 christos Exp $   */

/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995,1999 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef lint
static const char rcsid[] = "Id: ns_samedomain.c,v 1.6 2005/04/27 04:56:40 sra Exp ";
#endif

#include "port_before.h"

#include <sys/types.h>
#include <arpa/nameser.h>
#include <errno.h>
#include <string.h>

#include "port_after.h"

/*%
*      Check whether a name belongs to a domain.
*
* Inputs:
*\li   a - the domain whose ancestory is being verified
*\li   b - the potential ancestor we're checking against
*
* Return:
*\li   boolean - is a at or below b?
*
* Notes:
*\li   Trailing dots are first removed from name and domain.
*      Always compare complete subdomains, not only whether the
*      domain name is the trailing string of the given name.
*
*\li   "host.foobar.top" lies in "foobar.top" and in "top" and in ""
*      but NOT in "bar.top"
*/

int
ns_samedomain(const char *a, const char *b) {
       size_t la, lb;
       int diff, i, escaped;
       const char *cp;

       la = strlen(a);
       lb = strlen(b);

       /* Ignore a trailing label separator (i.e. an unescaped dot) in 'a'. */
       if (la != 0U && a[la - 1] == '.') {
               escaped = 0;
               /* Note this loop doesn't get executed if la==1. */
               for (i = la - 2; i >= 0; i--)
                       if (a[i] == '\\') {
                               if (escaped)
                                       escaped = 0;
                               else
                                       escaped = 1;
                       } else
                               break;
               if (!escaped)
                       la--;
       }

       /* Ignore a trailing label separator (i.e. an unescaped dot) in 'b'. */
       if (lb != 0U && b[lb - 1] == '.') {
               escaped = 0;
               /* note this loop doesn't get executed if lb==1 */
               for (i = lb - 2; i >= 0; i--)
                       if (b[i] == '\\') {
                               if (escaped)
                                       escaped = 0;
                               else
                                       escaped = 1;
                       } else
                               break;
               if (!escaped)
                       lb--;
       }

       /* lb == 0 means 'b' is the root domain, so 'a' must be in 'b'. */
       if (lb == 0U)
               return (1);

       /* 'b' longer than 'a' means 'a' can't be in 'b'. */
       if (lb > la)
               return (0);

       /* 'a' and 'b' being equal at this point indicates sameness. */
       if (lb == la)
               return (strncasecmp(a, b, lb) == 0);

       /* Ok, we know la > lb. */

       diff = la - lb;

       /*
        * If 'a' is only 1 character longer than 'b', then it can't be
        * a subdomain of 'b' (because of the need for the '.' label
        * separator).
        */
       if (diff < 2)
               return (0);

       /*
        * If the character before the last 'lb' characters of 'b'
        * isn't '.', then it can't be a match (this lets us avoid
        * having "foobar.com" match "bar.com").
        */
       if (a[diff - 1] != '.')
               return (0);

       /*
        * We're not sure about that '.', however.  It could be escaped
        * and thus not a really a label separator.
        */
       escaped = 0;
       for (i = diff - 2; i >= 0; i--)
               if (a[i] == '\\') {
                       if (escaped)
                               escaped = 0;
                       else
                               escaped = 1;
               } else
                       break;
       if (escaped)
               return (0);

       /* Now compare aligned trailing substring. */
       cp = a + diff;
       return (strncasecmp(cp, b, lb) == 0);
}

/*%
*      is "a" a subdomain of "b"?
*/
int
ns_subdomain(const char *a, const char *b) {
       return (ns_samename(a, b) != 1 && ns_samedomain(a, b));
}

/*%
*      make a canonical copy of domain name "src"
*
* notes:
* \code
*      foo -> foo.
*      foo. -> foo.
*      foo.. -> foo.
*      foo\. -> foo\..
*      foo\\. -> foo\\.
* \endcode
*/

int
ns_makecanon(const char *src, char *dst, size_t dstsize) {
       size_t n = strlen(src);

       if (n + sizeof "." > dstsize) {                 /*%< Note: sizeof == 2 */
               errno = EMSGSIZE;
               return (-1);
       }
       strcpy(dst, src);
       while (n >= 1U && dst[n - 1] == '.')            /*%< Ends in "." */
               if (n >= 2U && dst[n - 2] == '\\' &&    /*%< Ends in "\." */
                   (n < 3U || dst[n - 3] != '\\'))     /*%< But not "\\." */
                       break;
               else
                       dst[--n] = '\0';
       dst[n++] = '.';
       dst[n] = '\0';
       return (0);
}

/*%
*      determine whether domain name "a" is the same as domain name "b"
*
* return:
*\li   -1 on error
*\li   0 if names differ
*\li   1 if names are the same
*/

int
ns_samename(const char *a, const char *b) {
       char ta[NS_MAXDNAME], tb[NS_MAXDNAME];

       if (ns_makecanon(a, ta, sizeof ta) < 0 ||
           ns_makecanon(b, tb, sizeof tb) < 0)
               return (-1);
       if (strcasecmp(ta, tb) == 0)
               return (1);
       else
               return (0);
}

/*! \file */