Blame src/psl.c

Packit 0af36a
/*
Packit 0af36a
 * Copyright(c) 2014-2018 Tim Ruehsen
Packit 0af36a
 *
Packit 0af36a
 * Permission is hereby granted, free of charge, to any person obtaining a
Packit 0af36a
 * copy of this software and associated documentation files (the "Software"),
Packit 0af36a
 * to deal in the Software without restriction, including without limitation
Packit 0af36a
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
Packit 0af36a
 * and/or sell copies of the Software, and to permit persons to whom the
Packit 0af36a
 * Software is furnished to do so, subject to the following conditions:
Packit 0af36a
 *
Packit 0af36a
 * The above copyright notice and this permission notice shall be included in
Packit 0af36a
 * all copies or substantial portions of the Software.
Packit 0af36a
 *
Packit 0af36a
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Packit 0af36a
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Packit 0af36a
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Packit 0af36a
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Packit 0af36a
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
Packit 0af36a
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
Packit 0af36a
 * DEALINGS IN THE SOFTWARE.
Packit 0af36a
 *
Packit 0af36a
 * This file is part of libpsl.
Packit 0af36a
 *
Packit 0af36a
 * Public Suffix List routines
Packit 0af36a
 *
Packit 0af36a
 * Changelog
Packit 0af36a
 * 19.03.2014  Tim Ruehsen  created from libmget/cookie.c
Packit 0af36a
 *
Packit 0af36a
 */
Packit 0af36a
Packit 0af36a
#if HAVE_CONFIG_H
Packit 0af36a
# include <config.h>
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
#if defined(__GNUC__) && defined(__GNUC_MINOR__)
Packit 0af36a
#       define _GCC_VERSION_AT_LEAST(major, minor) ((__GNUC__ > (major)) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))
Packit 0af36a
#else
Packit 0af36a
#       define _GCC_VERSION_AT_LEAST(major, minor) 0
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
#if _GCC_VERSION_AT_LEAST(2,95)
Packit 0af36a
#  define _UNUSED __attribute__ ((unused))
Packit 0af36a
#else
Packit 0af36a
#  define _UNUSED
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
#if ENABLE_NLS != 0
Packit 0af36a
#	include <libintl.h>
Packit 0af36a
#	define _(STRING) gettext(STRING)
Packit 0af36a
#else
Packit 0af36a
#	define _(STRING) STRING
Packit 0af36a
#	define ngettext(STRING1,STRING2,N) STRING2
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
#include <sys/types.h>
Packit 0af36a
#include <sys/stat.h>
Packit 0af36a
Packit 0af36a
#ifdef _WIN32
Packit 0af36a
/* This is for Windows Vista and later, for inet_pton() */
Packit 0af36a
# define _WIN32_WINNT 0x0600
Packit 0af36a
Packit 0af36a
# include <winsock2.h>
Packit 0af36a
# include <ws2tcpip.h>
Packit 0af36a
#else
Packit 0af36a
# include <sys/socket.h>
Packit 0af36a
# include <netinet/in.h>
Packit 0af36a
# include <unistd.h>
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
#include <stdio.h>
Packit 0af36a
#include <stdlib.h>
Packit 0af36a
#include <string.h>
Packit 0af36a
#include <ctype.h>
Packit 0af36a
#include <time.h>
Packit 0af36a
#include <errno.h>
Packit 0af36a
#include <limits.h> /* for UINT_MAX */
Packit 0af36a
Packit 0af36a
#ifndef _WIN32
Packit 0af36a
# include <langinfo.h>
Packit 0af36a
# include <arpa/inet.h>
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
#ifdef HAVE_ALLOCA_H
Packit 0af36a
#	include <alloca.h>
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
#ifdef WITH_LIBICU
Packit 0af36a
#	include <unicode/uversion.h>
Packit 0af36a
#	include <unicode/ustring.h>
Packit 0af36a
#	include <unicode/uidna.h>
Packit 0af36a
#	include <unicode/ucnv.h>
Packit 0af36a
#elif defined(WITH_LIBIDN2)
Packit 0af36a
#	include <iconv.h>
Packit 0af36a
#	include <idn2.h>
Packit 0af36a
#	include <unicase.h>
Packit 0af36a
#	include <unistr.h>
Packit 0af36a
#elif defined(WITH_LIBIDN)
Packit 0af36a
#	include <iconv.h>
Packit 0af36a
#	include <stringprep.h>
Packit 0af36a
#	include <idna.h>
Packit 0af36a
#	include <unicase.h>
Packit 0af36a
#	include <unistr.h>
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
#ifndef WINICONV_CONST
Packit 0af36a
#  define WINICONV_CONST
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
#include <libpsl.h>
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * SECTION:libpsl
Packit 0af36a
 * @short_description: Public Suffix List library functions
Packit 0af36a
 * @title: libpsl
Packit 0af36a
 * @stability: Stable
Packit 0af36a
 * @include: libpsl.h
Packit 0af36a
 *
Packit 0af36a
 * [Public Suffix List](https://publicsuffix.org/) library functions.
Packit 0af36a
 *
Packit 0af36a
 */
Packit 0af36a
Packit 0af36a
#define countof(a) (sizeof(a)/sizeof(*(a)))
Packit 0af36a
Packit 0af36a
#define _PSL_FLAG_EXCEPTION (1<<0)
Packit 0af36a
#define _PSL_FLAG_WILDCARD  (1<<1)
Packit 0af36a
#define _PSL_FLAG_ICANN     (1<<2) /* entry of ICANN section */
Packit 0af36a
#define _PSL_FLAG_PRIVATE   (1<<3) /* entry of PRIVATE section */
Packit 0af36a
#define _PSL_FLAG_PLAIN     (1<<4) /* just used for PSL syntax checking */
Packit 0af36a
Packit 0af36a
typedef struct {
Packit 0af36a
	char
Packit 0af36a
		label_buf[48];
Packit 0af36a
	const char *
Packit 0af36a
		label;
Packit 0af36a
	unsigned short
Packit 0af36a
		length;
Packit 0af36a
	unsigned char
Packit 0af36a
		nlabels, /* number of labels */
Packit 0af36a
		flags;
Packit 0af36a
} _psl_entry_t;
Packit 0af36a
Packit 0af36a
/* stripped down version libmget vector routines */
Packit 0af36a
typedef struct {
Packit 0af36a
	int
Packit 0af36a
		(*cmp)(const _psl_entry_t **, const _psl_entry_t **); /* comparison function */
Packit 0af36a
	_psl_entry_t
Packit 0af36a
		**entry; /* pointer to array of pointers to elements */
Packit 0af36a
	int
Packit 0af36a
		max,     /* allocated elements */
Packit 0af36a
		cur;     /* number of elements in use */
Packit 0af36a
} _psl_vector_t;
Packit 0af36a
Packit 0af36a
struct _psl_ctx_st {
Packit 0af36a
	_psl_vector_t
Packit 0af36a
		*suffixes;
Packit 0af36a
	unsigned char
Packit 0af36a
		*dafsa;
Packit 0af36a
	size_t
Packit 0af36a
		dafsa_size;
Packit 0af36a
	int
Packit 0af36a
		nsuffixes,
Packit 0af36a
		nexceptions,
Packit 0af36a
		nwildcards;
Packit 0af36a
	unsigned
Packit 0af36a
		utf8 : 1; /* 1: data contains UTF-8 + punycode encoded rules */
Packit 0af36a
};
Packit 0af36a
Packit 0af36a
/* include the PSL data generated by psl-make-dafsa */
Packit 0af36a
#if defined(BUILTIN_GENERATOR_LIBICU) || defined(BUILTIN_GENERATOR_LIBIDN2) || defined(BUILTIN_GENERATOR_LIBIDN)
Packit 0af36a
#include "suffixes_dafsa.c"
Packit 0af36a
#else
Packit 0af36a
static const unsigned char kDafsa[] = "";
Packit 0af36a
static time_t _psl_file_time = 0;
Packit 0af36a
static int _psl_nsuffixes = 0;
Packit 0af36a
static int _psl_nexceptions = 0;
Packit 0af36a
static int _psl_nwildcards = 0;
Packit 0af36a
static const char _psl_sha1_checksum[] = "";
Packit 0af36a
static const char _psl_filename[] = "";
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
/* references to these PSLs will result in lookups to built-in data */
Packit 0af36a
static const psl_ctx_t
Packit 0af36a
	_builtin_psl;
Packit 0af36a
Packit 0af36a
#ifdef PSL_DISTFILE
Packit 0af36a
static const char _psl_dist_filename[] = PSL_DISTFILE;
Packit 0af36a
#else
Packit 0af36a
static const char _psl_dist_filename[] = "";
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
static _psl_vector_t *_vector_alloc(int max, int (*cmp)(const _psl_entry_t **, const _psl_entry_t **))
Packit 0af36a
{
Packit 0af36a
	_psl_vector_t *v;
Packit 0af36a
Packit 0af36a
	if (!(v = calloc(1, sizeof(_psl_vector_t))))
Packit 0af36a
		return NULL;
Packit 0af36a
Packit 0af36a
	if (!(v->entry = malloc(max * sizeof(_psl_entry_t *)))) {
Packit 0af36a
		free(v);
Packit 0af36a
		return NULL;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	v->max = max;
Packit 0af36a
	v->cmp = cmp;
Packit 0af36a
	return v;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
static void _vector_free(_psl_vector_t **v)
Packit 0af36a
{
Packit 0af36a
	if (v && *v) {
Packit 0af36a
		if ((*v)->entry) {
Packit 0af36a
			int it;
Packit 0af36a
Packit 0af36a
			for (it = 0; it < (*v)->cur; it++)
Packit 0af36a
				free((*v)->entry[it]);
Packit 0af36a
Packit 0af36a
			free((*v)->entry);
Packit 0af36a
		}
Packit 0af36a
		free(*v);
Packit 0af36a
	}
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
static _psl_entry_t *_vector_get(const _psl_vector_t *v, int pos)
Packit 0af36a
{
Packit 0af36a
	if (pos < 0 || !v || pos >= v->cur) return NULL;
Packit 0af36a
Packit 0af36a
	return v->entry[pos];
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/* the entries must be sorted by */
Packit 0af36a
static int _vector_find(const _psl_vector_t *v, const _psl_entry_t *elem)
Packit 0af36a
{
Packit 0af36a
	if (v) {
Packit 0af36a
		int l, r, m;
Packit 0af36a
		int res;
Packit 0af36a
Packit 0af36a
		/* binary search for element (exact match) */
Packit 0af36a
		for (l = 0, r = v->cur - 1; l <= r;) {
Packit 0af36a
			m = (l + r) / 2;
Packit 0af36a
			if ((res = v->cmp(&elem, (const _psl_entry_t **)&(v->entry[m]))) > 0) l = m + 1;
Packit 0af36a
			else if (res < 0) r = m - 1;
Packit 0af36a
			else return m;
Packit 0af36a
		}
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	return -1; /* not found */
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
static int _vector_add(_psl_vector_t *v, const _psl_entry_t *elem)
Packit 0af36a
{
Packit 0af36a
	if (v) {
Packit 0af36a
		void *elemp;
Packit 0af36a
Packit 0af36a
		if (!(elemp = malloc(sizeof(_psl_entry_t))))
Packit 0af36a
			return -1;
Packit 0af36a
Packit 0af36a
		memcpy(elemp, elem, sizeof(_psl_entry_t));
Packit 0af36a
Packit 0af36a
		if (v->max == v->cur) {
Packit 0af36a
			void *m = realloc(v->entry, (v->max *= 2) * sizeof(_psl_entry_t *));
Packit 0af36a
Packit 0af36a
			if (m)
Packit 0af36a
				v->entry = m;
Packit 0af36a
			else {
Packit 0af36a
				free(elemp);
Packit 0af36a
				return -1;
Packit 0af36a
			}
Packit 0af36a
		}
Packit 0af36a
Packit 0af36a
		v->entry[v->cur++] = elemp;
Packit 0af36a
		return v->cur - 1;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	return -1;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
static void _vector_sort(_psl_vector_t *v)
Packit 0af36a
{
Packit 0af36a
	if (v && v->cmp)
Packit 0af36a
		qsort(v->entry, v->cur, sizeof(_psl_vector_t **), (int(*)(const void *, const void *))v->cmp);
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/* by this kind of sorting, we can easily see if a domain matches or not */
Packit 0af36a
static int _suffix_compare(const _psl_entry_t *s1, const _psl_entry_t *s2)
Packit 0af36a
{
Packit 0af36a
	int n;
Packit 0af36a
Packit 0af36a
	if ((n = s2->nlabels - s1->nlabels))
Packit 0af36a
		return n; /* most labels first */
Packit 0af36a
Packit 0af36a
	if ((n = s1->length - s2->length))
Packit 0af36a
		return n;  /* shorter rules first */
Packit 0af36a
Packit 0af36a
	return strcmp(s1->label ? s1->label : s1->label_buf, s2->label ? s2->label : s2->label_buf);
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/* needed to sort array of pointers, given to qsort() */
Packit 0af36a
static int _suffix_compare_array(const _psl_entry_t **s1, const _psl_entry_t **s2)
Packit 0af36a
{
Packit 0af36a
	return _suffix_compare(*s1, *s2);
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
static int _suffix_init(_psl_entry_t *suffix, const char *rule, size_t length)
Packit 0af36a
{
Packit 0af36a
	const char *src;
Packit 0af36a
	char *dst;
Packit 0af36a
Packit 0af36a
	suffix->label = suffix->label_buf;
Packit 0af36a
Packit 0af36a
	if (length >= sizeof(suffix->label_buf) - 1) {
Packit 0af36a
		suffix->nlabels = 0;
Packit 0af36a
		/* fprintf(stderr, _("Suffix rule too long (%zd, ignored): %s\n"), length, rule); */
Packit 0af36a
		return -1;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	suffix->length = (unsigned char)length;
Packit 0af36a
Packit 0af36a
	suffix->nlabels = 1;
Packit 0af36a
Packit 0af36a
	for (dst = suffix->label_buf, src = rule; *src;) {
Packit 0af36a
		if (*src == '.')
Packit 0af36a
			suffix->nlabels++;
Packit 0af36a
		*dst++ = *src++;
Packit 0af36a
	}
Packit 0af36a
	*dst = 0;
Packit 0af36a
Packit 0af36a
	return 0;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
#if !defined(WITH_LIBIDN) && !defined(WITH_LIBIDN2) && !defined(WITH_LIBICU)
Packit 0af36a
/*
Packit 0af36a
 * When configured without runtime IDNA support (./configure --disable-runtime), we need a pure ASCII
Packit 0af36a
 * representation of non-ASCII characters in labels as found in UTF-8 domain names.
Packit 0af36a
 * This is because the current DAFSA format used may only hold character values [21..127].
Packit 0af36a
 *
Packit 0af36a
  Code copied from http://www.nicemice.net/idn/punycode-spec.gz on
Packit 0af36a
  2011-01-04 with SHA-1 a966a8017f6be579d74a50a226accc7607c40133
Packit 0af36a
  labeled punycode-spec 1.0.3 (2006-Mar-24-Thu).  It is modified for
Packit 0af36a
  libpsl by Tim Rühsen.  License on the original code:
Packit 0af36a
Packit 0af36a
  punycode-spec 1.0.3 (2006-Mar-23-Thu)
Packit 0af36a
  http://www.nicemice.net/idn/
Packit 0af36a
  Adam M. Costello
Packit 0af36a
  http://www.nicemice.net/amc/
Packit 0af36a
Packit 0af36a
  B. Disclaimer and license
Packit 0af36a
Packit 0af36a
    Regarding this entire document or any portion of it (including
Packit 0af36a
    the pseudocode and C code), the author makes no guarantees and
Packit 0af36a
    is not responsible for any damage resulting from its use.  The
Packit 0af36a
    author grants irrevocable permission to anyone to use, modify,
Packit 0af36a
    and distribute it in any way that does not diminish the rights
Packit 0af36a
    of anyone else to use, modify, and distribute it, provided that
Packit 0af36a
    redistributed derivative works do not contain misleading author or
Packit 0af36a
    version information.  Derivative works need not be licensed under
Packit 0af36a
    similar terms.
Packit 0af36a
Packit 0af36a
  C. Punycode sample implementation
Packit 0af36a
Packit 0af36a
  punycode-sample.c 2.0.0 (2004-Mar-21-Sun)
Packit 0af36a
  http://www.nicemice.net/idn/
Packit 0af36a
  Adam M. Costello
Packit 0af36a
  http://www.nicemice.net/amc/
Packit 0af36a
Packit 0af36a
  This is ANSI C code (C89) implementing Punycode 1.0.x.
Packit 0af36a
 */
Packit 0af36a
enum punycode_status {
Packit 0af36a
	punycode_success = 0,
Packit 0af36a
	punycode_bad_input = 1, /* Input is invalid.                       */
Packit 0af36a
	punycode_big_output = 2, /* Output would exceed the space provided. */
Packit 0af36a
	punycode_overflow = 3 /* Wider integers needed to process input. */
Packit 0af36a
};
Packit 0af36a
Packit 0af36a
#ifdef PUNYCODE_UINT
Packit 0af36a
	typedef PUNYCODE_UINT punycode_uint;
Packit 0af36a
#elif UINT_MAX >= (1 << 26) - 1
Packit 0af36a
	typedef unsigned int punycode_uint;
Packit 0af36a
#else
Packit 0af36a
	typedef unsigned long punycode_uint;
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
/*** Bootstring parameters for Punycode ***/
Packit 0af36a
enum {
Packit 0af36a
	base = 36, tmin = 1, tmax = 26, skew = 38, damp = 700,
Packit 0af36a
	initial_bias = 72, initial_n = 0x80, delimiter = 0x2D
Packit 0af36a
};
Packit 0af36a
Packit 0af36a
static char encode_digit(punycode_uint d)
Packit 0af36a
{
Packit 0af36a
	return d + 22 + 75 * (d < 26);
Packit 0af36a
	/*  0..25 map to ASCII a..z or A..Z */
Packit 0af36a
	/* 26..35 map to ASCII 0..9         */
Packit 0af36a
}
Packit 0af36a
#define flagged(bcp) ((punycode_uint)(bcp) - 65 < 26)
Packit 0af36a
static const punycode_uint maxint = -1;
Packit 0af36a
Packit 0af36a
static punycode_uint adapt(punycode_uint delta, punycode_uint numpoints, int firsttime)
Packit 0af36a
{
Packit 0af36a
	punycode_uint k;
Packit 0af36a
Packit 0af36a
	delta = firsttime ? delta / damp : delta >> 1;
Packit 0af36a
	/* delta >> 1 is a faster way of doing delta / 2 */
Packit 0af36a
	delta += delta / numpoints;
Packit 0af36a
Packit 0af36a
	for (k = 0; delta > ((base - tmin) * tmax) / 2; k += base) {
Packit 0af36a
		delta /= base - tmin;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	return k + (base - tmin + 1) * delta / (delta + skew);
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
static enum punycode_status punycode_encode(
Packit 0af36a
	size_t input_length_orig,
Packit 0af36a
	const punycode_uint input[],
Packit 0af36a
	size_t *output_length,
Packit 0af36a
	char output[])
Packit 0af36a
{
Packit 0af36a
	punycode_uint input_length, n, delta, h, b, bias, j, m, q, k, t;
Packit 0af36a
	size_t out, max_out;
Packit 0af36a
Packit 0af36a
	/* The Punycode spec assumes that the input length is the same type */
Packit 0af36a
	/* of integer as a code point, so we need to convert the size_t to  */
Packit 0af36a
	/* a punycode_uint, which could overflow.                           */
Packit 0af36a
Packit 0af36a
	if (input_length_orig > maxint)
Packit 0af36a
		return punycode_overflow;
Packit 0af36a
Packit 0af36a
	input_length = (punycode_uint) input_length_orig;
Packit 0af36a
Packit 0af36a
	/* Initialize the state: */
Packit 0af36a
Packit 0af36a
	n = initial_n;
Packit 0af36a
	delta = 0;
Packit 0af36a
	out = 0;
Packit 0af36a
	max_out = *output_length;
Packit 0af36a
	bias = initial_bias;
Packit 0af36a
Packit 0af36a
	/* Handle the basic code points: */
Packit 0af36a
	for (j = 0; j < input_length; ++j) {
Packit 0af36a
		if (input[j] < 0x80) {
Packit 0af36a
			if (max_out - out < 2)
Packit 0af36a
				return punycode_big_output;
Packit 0af36a
			output[out++] = (char) input[j];
Packit 0af36a
		}
Packit 0af36a
		/* else if (input[j] < n) return punycode_bad_input; */
Packit 0af36a
		/* (not needed for Punycode with unsigned code points) */
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	h = b = (punycode_uint) out;
Packit 0af36a
	/* cannot overflow because out <= input_length <= maxint */
Packit 0af36a
Packit 0af36a
	/* h is the number of code points that have been handled, b is the  */
Packit 0af36a
	/* number of basic code points, and out is the number of ASCII code */
Packit 0af36a
	/* points that have been output.                                    */
Packit 0af36a
Packit 0af36a
	if (b > 0)
Packit 0af36a
		output[out++] = delimiter;
Packit 0af36a
Packit 0af36a
	/* Main encoding loop: */
Packit 0af36a
Packit 0af36a
	while (h < input_length) {
Packit 0af36a
		/* All non-basic code points < n have been     */
Packit 0af36a
		/* handled already.  Find the next larger one: */
Packit 0af36a
Packit 0af36a
		for (m = maxint, j = 0; j < input_length; ++j) {
Packit 0af36a
			/* if (basic(input[j])) continue; */
Packit 0af36a
			/* (not needed for Punycode) */
Packit 0af36a
			if (input[j] >= n && input[j] < m)
Packit 0af36a
				m = input[j];
Packit 0af36a
		}
Packit 0af36a
Packit 0af36a
		/* Increase delta enough to advance the decoder's    */
Packit 0af36a
		/* <n,i> state to <m,0>, but guard against overflow: */
Packit 0af36a
Packit 0af36a
		if (m - n > (maxint - delta) / (h + 1))
Packit 0af36a
			return punycode_overflow;
Packit 0af36a
		delta += (m - n) * (h + 1);
Packit 0af36a
		n = m;
Packit 0af36a
Packit 0af36a
		for (j = 0; j < input_length; ++j) {
Packit 0af36a
			/* Punycode does not need to check whether input[j] is basic: */
Packit 0af36a
			if (input[j] < n /* || basic(input[j]) */) {
Packit 0af36a
				if (++delta == 0)
Packit 0af36a
					return punycode_overflow;
Packit 0af36a
			}
Packit 0af36a
Packit 0af36a
			if (input[j] == n) {
Packit 0af36a
				/* Represent delta as a generalized variable-length integer: */
Packit 0af36a
Packit 0af36a
				for (q = delta, k = base;; k += base) {
Packit 0af36a
					if (out >= max_out)
Packit 0af36a
						return punycode_big_output;
Packit 0af36a
					t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */
Packit 0af36a
						k >= bias + tmax ? tmax : k - bias;
Packit 0af36a
					if (q < t)
Packit 0af36a
						break;
Packit 0af36a
					output[out++] = encode_digit(t + (q - t) % (base - t));
Packit 0af36a
					q = (q - t) / (base - t);
Packit 0af36a
				}
Packit 0af36a
Packit 0af36a
				output[out++] = encode_digit(q);
Packit 0af36a
				bias = adapt(delta, h + 1, h == b);
Packit 0af36a
				delta = 0;
Packit 0af36a
				++h;
Packit 0af36a
			}
Packit 0af36a
		}
Packit 0af36a
Packit 0af36a
		++delta, ++n;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	*output_length = out;
Packit 0af36a
	return punycode_success;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
static ssize_t _utf8_to_utf32(const char *in, size_t inlen, punycode_uint *out, size_t outlen)
Packit 0af36a
{
Packit 0af36a
	size_t n = 0;
Packit 0af36a
	const unsigned char *s = (void *)in;
Packit 0af36a
	const unsigned char *e = (void *)(in + inlen);
Packit 0af36a
Packit 0af36a
	if (!outlen)
Packit 0af36a
		return -1;
Packit 0af36a
Packit 0af36a
	outlen--;
Packit 0af36a
Packit 0af36a
	while (n < outlen) {
Packit 0af36a
		size_t inleft = e - s;
Packit 0af36a
Packit 0af36a
		if (inleft >= 1 && (*s & 0x80) == 0) { /* 0xxxxxxx ASCII char */
Packit 0af36a
			out[n++] = *s;
Packit 0af36a
			s++;
Packit 0af36a
		} else if (inleft >= 2 && (*s & 0xE0) == 0xC0) /* 110xxxxx 10xxxxxx */ {
Packit 0af36a
			if ((s[1] & 0xC0) != 0x80)
Packit 0af36a
				return -1;
Packit 0af36a
			out[n++] = ((*s & 0x1F) << 6) | (s[1] & 0x3F);
Packit 0af36a
			s += 2;
Packit 0af36a
		} else if (inleft >= 3 && (*s & 0xF0) == 0xE0) /* 1110xxxx 10xxxxxx 10xxxxxx */ {
Packit 0af36a
			if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80)
Packit 0af36a
				return -1;
Packit 0af36a
			out[n++] = ((*s & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
Packit 0af36a
			s += 3;
Packit 0af36a
		} else if (inleft >= 4 && (*s & 0xF8) == 0xF0) /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ {
Packit 0af36a
			if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80 || (s[3] & 0xC0) != 0x80)
Packit 0af36a
				return -1;
Packit 0af36a
			out[n++] = ((*s & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
Packit 0af36a
			s += 4;
Packit 0af36a
		} else if (!inleft) {
Packit 0af36a
			break;
Packit 0af36a
		} else
Packit 0af36a
			return -1;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	return n;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
static int _mem_is_ascii(const char *s, size_t n)
Packit 0af36a
{
Packit 0af36a
	for (; n; n--) /* 'while(n--)' generates unsigned integer overflow on n = 0 */
Packit 0af36a
		if (*((unsigned char *)s++) >= 128)
Packit 0af36a
			return 0;
Packit 0af36a
Packit 0af36a
	return 1;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
static int _domain_to_punycode(const char *domain, char *out, size_t outsize)
Packit 0af36a
{
Packit 0af36a
	size_t outlen = 0, labellen;
Packit 0af36a
	punycode_uint input[256];
Packit 0af36a
	const char *label, *e;
Packit 0af36a
Packit 0af36a
	for (e = label = domain; e; label = e + 1) {
Packit 0af36a
		e = strchr(label, '.');
Packit 0af36a
		labellen = e ? (size_t) (e - label) : strlen(label);
Packit 0af36a
		/* printf("s=%s inlen=%zd\n", label, labellen); */
Packit 0af36a
Packit 0af36a
		if (_mem_is_ascii(label, labellen)) {
Packit 0af36a
			if (outlen + labellen + (e != NULL) >= outsize)
Packit 0af36a
				return 1;
Packit 0af36a
Packit 0af36a
			/* printf("outlen=%zd labellen=%zd\n", outlen, labellen); */
Packit 0af36a
			memcpy(out + outlen, label, labellen);
Packit 0af36a
			outlen += labellen;
Packit 0af36a
		} else {
Packit 0af36a
			ssize_t inputlen = 0;
Packit 0af36a
Packit 0af36a
			if (outlen + labellen + (e != NULL) + 4 >= outsize)
Packit 0af36a
				return 1;
Packit 0af36a
Packit 0af36a
			if ((inputlen = _utf8_to_utf32(label, labellen, input, countof(input))) < 0)
Packit 0af36a
				return 1;
Packit 0af36a
Packit 0af36a
			memcpy(out + outlen, "xn--", 4);
Packit 0af36a
			outlen += 4;
Packit 0af36a
Packit 0af36a
			labellen = outsize - outlen;
Packit 0af36a
			/* printf("n=%zd space_left=%zd\n", n, labellen); */
Packit 0af36a
			if (punycode_encode(inputlen, input, &labellen, out + outlen))
Packit 0af36a
				return 1;
Packit 0af36a
			outlen += labellen;
Packit 0af36a
		}
Packit 0af36a
Packit 0af36a
		if (e)
Packit 0af36a
			out[outlen++] = '.';
Packit 0af36a
		out[outlen] = 0;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	return 0;
Packit 0af36a
}
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
static int _isspace_ascii(const char c)
Packit 0af36a
{
Packit 0af36a
	return c == ' ' || c == '\t' || c == '\r' || c == '\n';
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
static int _str_is_ascii(const char *s)
Packit 0af36a
{
Packit 0af36a
	while (*s && *((unsigned char *)s) < 128) s++;
Packit 0af36a
Packit 0af36a
	return !*s;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
#if defined(WITH_LIBIDN)
Packit 0af36a
/*
Packit 0af36a
 * Work around a libidn <= 1.30 vulnerability.
Packit 0af36a
 *
Packit 0af36a
 * The function checks for a valid UTF-8 character sequence before
Packit 0af36a
 * passing it to idna_to_ascii_8z().
Packit 0af36a
 *
Packit 0af36a
 * [1] https://lists.gnu.org/archive/html/help-libidn/2015-05/msg00002.html
Packit 0af36a
 * [2] https://lists.gnu.org/archive/html/bug-wget/2015-06/msg00002.html
Packit 0af36a
 * [3] https://curl.haxx.se/mail/lib-2015-06/0143.html
Packit 0af36a
 */
Packit 0af36a
static int _utf8_is_valid(const char *utf8)
Packit 0af36a
{
Packit 0af36a
	const unsigned char *s = (const unsigned char *) utf8;
Packit 0af36a
Packit 0af36a
	while (*s) {
Packit 0af36a
		if ((*s & 0x80) == 0) /* 0xxxxxxx ASCII char */
Packit 0af36a
			s++;
Packit 0af36a
		else if ((*s & 0xE0) == 0xC0) /* 110xxxxx 10xxxxxx */ {
Packit 0af36a
			if ((s[1] & 0xC0) != 0x80)
Packit 0af36a
				return 0;
Packit 0af36a
			s += 2;
Packit 0af36a
		} else if ((*s & 0xF0) == 0xE0) /* 1110xxxx 10xxxxxx 10xxxxxx */ {
Packit 0af36a
			if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80)
Packit 0af36a
				return 0;
Packit 0af36a
			s += 3;
Packit 0af36a
		} else if ((*s & 0xF8) == 0xF0) /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ {
Packit 0af36a
			if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80 || (s[3] & 0xC0) != 0x80)
Packit 0af36a
				return 0;
Packit 0af36a
			s += 4;
Packit 0af36a
		} else
Packit 0af36a
			return 0;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	return 1;
Packit 0af36a
}
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
typedef void *_psl_idna_t;
Packit 0af36a
Packit 0af36a
static _psl_idna_t *_psl_idna_open(void)
Packit 0af36a
{
Packit 0af36a
#if defined(WITH_LIBICU)
Packit 0af36a
	UErrorCode status = 0;
Packit 0af36a
	return (void *)uidna_openUTS46(UIDNA_USE_STD3_RULES | UIDNA_NONTRANSITIONAL_TO_ASCII, &status);
Packit 0af36a
#endif
Packit 0af36a
	return NULL;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
static void _psl_idna_close(_psl_idna_t *idna _UNUSED)
Packit 0af36a
{
Packit 0af36a
#if defined(WITH_LIBICU)
Packit 0af36a
	if (idna)
Packit 0af36a
		uidna_close((UIDNA *)idna);
Packit 0af36a
#endif
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
static int _psl_idna_toASCII(_psl_idna_t *idna _UNUSED, const char *utf8, char **ascii)
Packit 0af36a
{
Packit 0af36a
	int ret = -1;
Packit 0af36a
Packit 0af36a
#if defined(WITH_LIBICU)
Packit 0af36a
	/* IDNA2008 UTS#46 punycode conversion */
Packit 0af36a
	if (idna) {
Packit 0af36a
		char lookupname_buf[128] = "", *lookupname = lookupname_buf;
Packit 0af36a
		UErrorCode status = 0;
Packit 0af36a
		UIDNAInfo info = UIDNA_INFO_INITIALIZER;
Packit 0af36a
		UChar utf16_dst[128], utf16_src_buf[128];
Packit 0af36a
		UChar *utf16_src = utf16_src_buf;
Packit 0af36a
		int32_t utf16_src_length, bytes_written;
Packit 0af36a
		int32_t utf16_dst_length;
Packit 0af36a
Packit 0af36a
		u_strFromUTF8(utf16_src, countof(utf16_src_buf), &utf16_src_length, utf8, -1, &status);
Packit 0af36a
		if (!U_SUCCESS(status)) goto cleanup; /* UTF-8 to UTF-16 conversion failed */
Packit 0af36a
Packit 0af36a
		if (utf16_src_length >= (int) countof(utf16_src_buf)) {
Packit 0af36a
			utf16_src = malloc((utf16_src_length + 1) * sizeof(UChar));
Packit 0af36a
			if (!utf16_src) goto cleanup;
Packit 0af36a
Packit 0af36a
			u_strFromUTF8(utf16_src, utf16_src_length, NULL, utf8, -1, &status);
Packit 0af36a
			if (!U_SUCCESS(status)) goto cleanup; /* UTF-8 to UTF-16 conversion failed */
Packit 0af36a
Packit 0af36a
			utf16_src[utf16_src_length] = 0; /* u_strFromUTF8() doesn't 0-terminate if dest is filled up */
Packit 0af36a
		}
Packit 0af36a
Packit 0af36a
		utf16_dst_length = uidna_nameToASCII((UIDNA *)idna, utf16_src, utf16_src_length, utf16_dst, countof(utf16_dst), &info, &status);
Packit 0af36a
		if (!U_SUCCESS(status)) goto cleanup; /* to ASCII conversion failed */
Packit 0af36a
Packit 0af36a
		u_strToUTF8(lookupname, sizeof(lookupname_buf), &bytes_written, utf16_dst, utf16_dst_length, &status);
Packit 0af36a
		if (!U_SUCCESS(status)) goto cleanup; /* UTF-16 to UTF-8 conversion failed */
Packit 0af36a
Packit 0af36a
		if (bytes_written >= (int) sizeof(lookupname_buf)) {
Packit 0af36a
			lookupname = malloc(bytes_written + 1);
Packit 0af36a
			if (!lookupname) goto cleanup;
Packit 0af36a
Packit 0af36a
			u_strToUTF8(lookupname, bytes_written, NULL, utf16_dst, utf16_dst_length, &status);
Packit 0af36a
			if (!U_SUCCESS(status)) goto cleanup; /* UTF-16 to UTF-8 conversion failed */
Packit 0af36a
Packit 0af36a
			lookupname[bytes_written] = 0; /* u_strToUTF8() doesn't 0-terminate if dest is filled up */
Packit 0af36a
		} else {
Packit 0af36a
			if (!(lookupname = strdup(lookupname)))
Packit 0af36a
				goto cleanup;
Packit 0af36a
		}
Packit 0af36a
Packit 0af36a
		if (ascii) {
Packit 0af36a
			*ascii = lookupname;
Packit 0af36a
			lookupname = NULL;
Packit 0af36a
		}
Packit 0af36a
Packit 0af36a
		ret = 0;
Packit 0af36a
Packit 0af36a
cleanup:
Packit 0af36a
		if (lookupname != lookupname_buf)
Packit 0af36a
			free(lookupname);
Packit 0af36a
		if (utf16_src != utf16_src_buf)
Packit 0af36a
			free(utf16_src);
Packit 0af36a
	}
Packit 0af36a
#elif defined(WITH_LIBIDN2)
Packit 0af36a
#if IDN2_VERSION_NUMBER >= 0x00140000
Packit 0af36a
	int rc;
Packit 0af36a
Packit 0af36a
	/* IDN2_TRANSITIONAL automatically converts to lowercase
Packit 0af36a
	 * IDN2_NFC_INPUT converts to NFC before toASCII conversion
Packit 0af36a
	 * Since IDN2_TRANSITIONAL implicitly does NFC conversion, we don't need
Packit 0af36a
	 * the additional IDN2_NFC_INPUT. But just for the unlikely case that the linked
Packit 0af36a
	 * library is not matching the headers when building and it doesn't support TR46,
Packit 0af36a
	 * we provide IDN2_NFC_INPUT. */
Packit 0af36a
Packit 0af36a
	if ((rc = idn2_lookup_u8((uint8_t *)utf8, (uint8_t **)ascii, IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL)) == IDN2_OK)
Packit 0af36a
		ret = 0;
Packit 0af36a
	/* else
Packit 0af36a
		fprintf(stderr, "toASCII(%s) failed (%d): %s\n", lower, rc, idn2_strerror(rc)); */
Packit 0af36a
#else
Packit 0af36a
	int rc;
Packit 0af36a
	uint8_t *lower;
Packit 0af36a
	size_t len = u8_strlen((uint8_t *)utf8) + 1;
Packit 0af36a
Packit 0af36a
	/* we need a conversion to lowercase */
Packit 0af36a
	if (!(lower = u8_tolower((uint8_t *)utf8, len, 0, UNINORM_NFKC, NULL, &len))) {
Packit 0af36a
		/* fprintf(stderr, "u8_tolower(%s) failed (%d)\n", utf8, errno); */
Packit 0af36a
		return -1;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	if ((rc = idn2_lookup_u8(lower, (uint8_t **)ascii, 0)) == IDN2_OK) {
Packit 0af36a
		ret = 0;
Packit 0af36a
	} /* else
Packit 0af36a
		fprintf(stderr, "toASCII(%s) failed (%d): %s\n", lower, rc, idn2_strerror(rc)); */
Packit 0af36a
Packit 0af36a
	free(lower);
Packit 0af36a
#endif
Packit 0af36a
#elif defined(WITH_LIBIDN)
Packit 0af36a
	int rc;
Packit 0af36a
Packit 0af36a
	if (!_utf8_is_valid(utf8)) {
Packit 0af36a
		/* fprintf(_(stderr, "Invalid UTF-8 sequence not converted: '%s'\n"), utf8); */
Packit 0af36a
		return -1;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	/* idna_to_ascii_8z() automatically converts UTF-8 to lowercase */
Packit 0af36a
Packit 0af36a
	if ((rc = idna_to_ascii_8z(utf8, ascii, IDNA_USE_STD3_ASCII_RULES)) == IDNA_SUCCESS) {
Packit 0af36a
		ret = 0;
Packit 0af36a
	} /* else
Packit 0af36a
		fprintf(_(stderr, "toASCII failed (%d): %s\n"), rc, idna_strerror(rc)); */
Packit 0af36a
#else
Packit 0af36a
	char lookupname[128];
Packit 0af36a
Packit 0af36a
	if (_domain_to_punycode(utf8, lookupname, sizeof(lookupname)) == 0) {
Packit 0af36a
		if (ascii)
Packit 0af36a
			if ((*ascii = strdup(lookupname)))
Packit 0af36a
				ret = 0;
Packit 0af36a
	}
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
	return ret;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
static void _add_punycode_if_needed(_psl_idna_t *idna, _psl_vector_t *v, _psl_entry_t *e)
Packit 0af36a
{
Packit 0af36a
	char *lookupname;
Packit 0af36a
Packit 0af36a
	if (_str_is_ascii(e->label_buf))
Packit 0af36a
		return;
Packit 0af36a
Packit 0af36a
	if (_psl_idna_toASCII(idna, e->label_buf, &lookupname) == 0) {
Packit 0af36a
		if (strcmp(e->label_buf, lookupname)) {
Packit 0af36a
			_psl_entry_t suffix, *suffixp;
Packit 0af36a
Packit 0af36a
			/* fprintf(stderr, "toASCII '%s' -> '%s'\n", e->label_buf, lookupname); */
Packit 0af36a
			if (_suffix_init(&suffix, lookupname, strlen(lookupname)) == 0) {
Packit 0af36a
				suffix.flags = e->flags;
Packit 0af36a
				if ((suffixp = _vector_get(v, _vector_add(v, &suffix))))
Packit 0af36a
					suffixp->label = suffixp->label_buf; /* set label to changed address */
Packit 0af36a
			}
Packit 0af36a
		} /* else ignore */
Packit 0af36a
Packit 0af36a
		free(lookupname);
Packit 0af36a
	}
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/* prototypes */
Packit 0af36a
int LookupStringInFixedSet(const unsigned char* graph, size_t length, const char* key, size_t key_length);
Packit 0af36a
int GetUtfMode(const unsigned char *graph, size_t length);
Packit 0af36a
Packit 0af36a
static int _psl_is_public_suffix(const psl_ctx_t *psl, const char *domain, int type)
Packit 0af36a
{
Packit 0af36a
	_psl_entry_t suffix;
Packit 0af36a
	const char *p;
Packit 0af36a
	char *punycode = NULL;
Packit 0af36a
	int need_conversion = 0;
Packit 0af36a
Packit 0af36a
	/* this function should be called without leading dots, just make sure */
Packit 0af36a
	if (*domain == '.')
Packit 0af36a
		domain++;
Packit 0af36a
Packit 0af36a
	suffix.nlabels = 1;
Packit 0af36a
Packit 0af36a
	for (p = domain; *p; p++) {
Packit 0af36a
		if (*p == '.')
Packit 0af36a
			suffix.nlabels++;
Packit 0af36a
		else if (*((unsigned char *)p) >= 128)
Packit 0af36a
			need_conversion = 1; /* in case domain is non-ascii we need a toASCII conversion */
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	if (suffix.nlabels == 1) {
Packit 0af36a
		/* TLD, this is the prevailing '*' match. If type excludes the '*' rule, continue.
Packit 0af36a
		 */
Packit 0af36a
		if (!(type & PSL_TYPE_NO_STAR_RULE))
Packit 0af36a
			return 1;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	type &= ~PSL_TYPE_NO_STAR_RULE;
Packit 0af36a
Packit 0af36a
	if (psl->utf8 || psl == &_builtin_psl)
Packit 0af36a
		need_conversion = 0;
Packit 0af36a
Packit 0af36a
#if defined(WITH_LIBIDN) || defined(WITH_LIBIDN2) || defined(WITH_LIBICU)
Packit 0af36a
	if (psl == &_builtin_psl)
Packit 0af36a
		need_conversion = 0;
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
	if (need_conversion) {
Packit 0af36a
		_psl_idna_t *idna = _psl_idna_open();
Packit 0af36a
Packit 0af36a
		if (_psl_idna_toASCII(idna, domain, &punycode) == 0) {
Packit 0af36a
			suffix.label = punycode;
Packit 0af36a
			suffix.length = strlen(punycode);
Packit 0af36a
		} else {
Packit 0af36a
			/* fallback */
Packit 0af36a
Packit 0af36a
			suffix.label = domain;
Packit 0af36a
			suffix.length = p - suffix.label;
Packit 0af36a
		}
Packit 0af36a
Packit 0af36a
		_psl_idna_close(idna);
Packit 0af36a
	} else {
Packit 0af36a
		suffix.label = domain;
Packit 0af36a
		suffix.length = p - suffix.label;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	if (psl == &_builtin_psl || psl->dafsa) {
Packit 0af36a
		size_t dafsa_size = psl == &_builtin_psl ? sizeof(kDafsa) : psl->dafsa_size;
Packit 0af36a
		const unsigned char *dafsa = psl == &_builtin_psl ? kDafsa : psl->dafsa;
Packit 0af36a
		int rc = LookupStringInFixedSet(dafsa, dafsa_size, suffix.label, suffix.length);
Packit 0af36a
		if (rc != -1) {
Packit 0af36a
			/* check for correct rule type */
Packit 0af36a
			if (type == PSL_TYPE_ICANN && !(rc & _PSL_FLAG_ICANN))
Packit 0af36a
				goto suffix_no;
Packit 0af36a
			else if (type == PSL_TYPE_PRIVATE && !(rc & _PSL_FLAG_PRIVATE))
Packit 0af36a
				goto suffix_no;
Packit 0af36a
Packit 0af36a
			if (rc & _PSL_FLAG_EXCEPTION)
Packit 0af36a
				goto suffix_no;
Packit 0af36a
Packit 0af36a
			/* wildcard *.foo.bar implicitly make foo.bar a public suffix */
Packit 0af36a
			/* definitely a match, no matter if the found rule is a wildcard or not */
Packit 0af36a
			goto suffix_yes;
Packit 0af36a
		}
Packit 0af36a
		if ((suffix.label = strchr(suffix.label, '.'))) {
Packit 0af36a
			suffix.label++;
Packit 0af36a
			suffix.length = strlen(suffix.label);
Packit 0af36a
			suffix.nlabels--;
Packit 0af36a
Packit 0af36a
			rc = LookupStringInFixedSet(dafsa, dafsa_size, suffix.label, suffix.length);
Packit 0af36a
			if (rc != -1) {
Packit 0af36a
				/* check for correct rule type */
Packit 0af36a
				if (type == PSL_TYPE_ICANN && !(rc & _PSL_FLAG_ICANN))
Packit 0af36a
					goto suffix_no;
Packit 0af36a
				else if (type == PSL_TYPE_PRIVATE && !(rc & _PSL_FLAG_PRIVATE))
Packit 0af36a
					goto suffix_no;
Packit 0af36a
Packit 0af36a
				if (rc & _PSL_FLAG_WILDCARD)
Packit 0af36a
					goto suffix_yes;
Packit 0af36a
			}
Packit 0af36a
		}
Packit 0af36a
	} else {
Packit 0af36a
		_psl_entry_t *rule = _vector_get(psl->suffixes, 0);
Packit 0af36a
Packit 0af36a
		if (!rule || rule->nlabels < suffix.nlabels - 1)
Packit 0af36a
			goto suffix_no;
Packit 0af36a
Packit 0af36a
		rule = _vector_get(psl->suffixes, _vector_find(psl->suffixes, &suffix));
Packit 0af36a
Packit 0af36a
		if (rule) {
Packit 0af36a
			/* check for correct rule type */
Packit 0af36a
			if (type == PSL_TYPE_ICANN && !(rule->flags & _PSL_FLAG_ICANN))
Packit 0af36a
				goto suffix_no;
Packit 0af36a
			else if (type == PSL_TYPE_PRIVATE && !(rule->flags & _PSL_FLAG_PRIVATE))
Packit 0af36a
				goto suffix_no;
Packit 0af36a
Packit 0af36a
			if (rule->flags & _PSL_FLAG_EXCEPTION)
Packit 0af36a
				goto suffix_no;
Packit 0af36a
Packit 0af36a
			/* wildcard *.foo.bar implicitly make foo.bar a public suffix */
Packit 0af36a
			/* definitely a match, no matter if the found rule is a wildcard or not */
Packit 0af36a
			goto suffix_yes;
Packit 0af36a
		}
Packit 0af36a
Packit 0af36a
		if ((suffix.label = strchr(suffix.label, '.'))) {
Packit 0af36a
			int pos;
Packit 0af36a
Packit 0af36a
			suffix.label++;
Packit 0af36a
			suffix.length = strlen(suffix.label);
Packit 0af36a
			suffix.nlabels--;
Packit 0af36a
Packit 0af36a
			rule = _vector_get(psl->suffixes, (pos = _vector_find(psl->suffixes, &suffix)));
Packit 0af36a
Packit 0af36a
			if (rule) {
Packit 0af36a
				/* check for correct rule type */
Packit 0af36a
				if (type == PSL_TYPE_ICANN && !(rule->flags & _PSL_FLAG_ICANN))
Packit 0af36a
					goto suffix_no;
Packit 0af36a
				else if (type == PSL_TYPE_PRIVATE && !(rule->flags & _PSL_FLAG_PRIVATE))
Packit 0af36a
					goto suffix_no;
Packit 0af36a
Packit 0af36a
				if (rule->flags & _PSL_FLAG_WILDCARD)
Packit 0af36a
					goto suffix_yes;
Packit 0af36a
			}
Packit 0af36a
		}
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
suffix_no:
Packit 0af36a
	if (punycode)
Packit 0af36a
		free(punycode);
Packit 0af36a
	return 0;
Packit 0af36a
Packit 0af36a
suffix_yes:
Packit 0af36a
	if (punycode)
Packit 0af36a
		free(punycode);
Packit 0af36a
	return 1;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_is_public_suffix:
Packit 0af36a
 * @psl: PSL context
Packit 0af36a
 * @domain: Domain string
Packit 0af36a
 *
Packit 0af36a
 * This function checks if @domain is a public suffix by the means of the
Packit 0af36a
 * [Mozilla Public Suffix List](https://publicsuffix.org).
Packit 0af36a
 *
Packit 0af36a
 * For cookie domain checking see psl_is_cookie_domain_acceptable().
Packit 0af36a
 *
Packit 0af36a
 * International @domain names have to be either in UTF-8 (lowercase + NFKC) or in ASCII/ACE format (punycode).
Packit 0af36a
 * Other encodings likely result in incorrect return values.
Packit 0af36a
 * Use helper function psl_str_to_utf8lower() for normalization @domain.
Packit 0af36a
 *
Packit 0af36a
 * @psl is a context returned by either psl_load_file(), psl_load_fp() or
Packit 0af36a
 * psl_builtin().
Packit 0af36a
 *
Packit 0af36a
 * Returns: 1 if domain is a public suffix, 0 if not.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.1
Packit 0af36a
 */
Packit 0af36a
int psl_is_public_suffix(const psl_ctx_t *psl, const char *domain)
Packit 0af36a
{
Packit 0af36a
	if (!psl || !domain)
Packit 0af36a
		return 1;
Packit 0af36a
Packit 0af36a
	return _psl_is_public_suffix(psl, domain, PSL_TYPE_ANY);
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_is_public_suffix2:
Packit 0af36a
 * @psl: PSL context
Packit 0af36a
 * @domain: Domain string
Packit 0af36a
 * @type: Domain type
Packit 0af36a
 *
Packit 0af36a
 * This function checks if @domain is a public suffix by the means of the
Packit 0af36a
 * [Mozilla Public Suffix List](https://publicsuffix.org).
Packit 0af36a
 *
Packit 0af36a
 * @type specifies the PSL section where to perform the lookup. Valid values are
Packit 0af36a
 * %PSL_TYPE_PRIVATE, %PSL_TYPE_ICANN, %PSL_TYPE_NO_STAR_RULE, and %PSL_TYPE_ANY.
Packit 0af36a
 *
Packit 0af36a
 * %PSL_TYPE_NO_STAR_RULE switches of the 'prevailing star rule' (see
Packit 0af36a
 * [List](https://publicsuffix.org/list) under 'Algorithm' 2.).
Packit 0af36a
 * Applying the flag means that TLDs not explicitly listed in the PSL are *not* treated as public suffixes.
Packit 0af36a
 *
Packit 0af36a
 * International @domain names have to be either in UTF-8 (lowercase + NFKC) or in ASCII/ACE format (punycode).
Packit 0af36a
 * Other encodings likely result in incorrect return values.
Packit 0af36a
 * Use helper function psl_str_to_utf8lower() for normalization @domain.
Packit 0af36a
 *
Packit 0af36a
 * @psl is a context returned by either psl_load_file(), psl_load_fp() or
Packit 0af36a
 * psl_builtin().
Packit 0af36a
 *
Packit 0af36a
 * Returns: 1 if domain is a public suffix, 0 if not.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.1
Packit 0af36a
 */
Packit 0af36a
int psl_is_public_suffix2(const psl_ctx_t *psl, const char *domain, int type)
Packit 0af36a
{
Packit 0af36a
	if (!psl || !domain)
Packit 0af36a
		return 1;
Packit 0af36a
Packit 0af36a
	return _psl_is_public_suffix(psl, domain, type);
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_unregistrable_domain:
Packit 0af36a
 * @psl: PSL context
Packit 0af36a
 * @domain: Domain string
Packit 0af36a
 *
Packit 0af36a
 * This function finds the longest public suffix part of @domain by the means
Packit 0af36a
 * of the [Mozilla Public Suffix List](https://publicsuffix.org).
Packit 0af36a
 *
Packit 0af36a
 * International @domain names have to be either in UTF-8 (lowercase + NFKC) or in ASCII/ACE format (punycode).
Packit 0af36a
 * Other encodings likely result in incorrect return values.
Packit 0af36a
 * Use helper function psl_str_to_utf8lower() for normalization @domain.
Packit 0af36a
 *
Packit 0af36a
 * @psl is a context returned by either psl_load_file(), psl_load_fp() or
Packit 0af36a
 * psl_builtin().
Packit 0af36a
 *
Packit 0af36a
 * Returns: Pointer to longest public suffix part of @domain or %NULL if @domain
Packit 0af36a
 * does not contain a public suffix (or if @psl is %NULL).
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.1
Packit 0af36a
 */
Packit 0af36a
const char *psl_unregistrable_domain(const psl_ctx_t *psl, const char *domain)
Packit 0af36a
{
Packit 0af36a
	int nlabels = 0;
Packit 0af36a
	const char *p;
Packit 0af36a
Packit 0af36a
	if (!psl || !domain)
Packit 0af36a
		return NULL;
Packit 0af36a
Packit 0af36a
	/*
Packit 0af36a
	 * In the main loop we introduce a O(N^2) behavior to avoid code duplication.
Packit 0af36a
	 * To avoid nasty CPU hogging, we limit the lookup to max. 8 domain labels to the right.
Packit 0af36a
	 */
Packit 0af36a
	for (p = domain + strlen(domain) - 1; p >= domain; p--) {
Packit 0af36a
		if (*p == '.' && ++nlabels > 8) {
Packit 0af36a
			domain = p + 1;
Packit 0af36a
			break;
Packit 0af36a
		}
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	/*
Packit 0af36a
	 *  We check from left to right to catch special PSL entries like 'forgot.his.name':
Packit 0af36a
	 *   'forgot.his.name' and 'name' are in the PSL while 'his.name' is not.
Packit 0af36a
	 */
Packit 0af36a
Packit 0af36a
	while (!_psl_is_public_suffix(psl, domain, 0)) {
Packit 0af36a
		if ((domain = strchr(domain, '.')))
Packit 0af36a
			domain++;
Packit 0af36a
		else
Packit 0af36a
			break; /* prevent endless loop if psl_is_public_suffix() is broken. */
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	return domain;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_registrable_domain:
Packit 0af36a
 * @psl: PSL context
Packit 0af36a
 * @domain: Domain string
Packit 0af36a
 *
Packit 0af36a
 * This function finds the shortest private suffix part of @domain by the means
Packit 0af36a
 * of the [Mozilla Public Suffix List](https://publicsuffix.org).
Packit 0af36a
 *
Packit 0af36a
 * International @domain names have to be either in UTF-8 (lowercase + NFKC) or in ASCII/ACE format (punycode).
Packit 0af36a
 * Other encodings likely result in incorrect return values.
Packit 0af36a
 * Use helper function psl_str_to_utf8lower() for normalization @domain.
Packit 0af36a
 *
Packit 0af36a
 * @psl is a context returned by either psl_load_file(), psl_load_fp() or
Packit 0af36a
 * psl_builtin().
Packit 0af36a
 *
Packit 0af36a
 * Returns: Pointer to shortest private suffix part of @domain or %NULL if @domain
Packit 0af36a
 * does not contain a private suffix (or if @psl is %NULL).
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.1
Packit 0af36a
 */
Packit 0af36a
const char *psl_registrable_domain(const psl_ctx_t *psl, const char *domain)
Packit 0af36a
{
Packit 0af36a
	const char *p, *regdom = NULL;
Packit 0af36a
	int nlabels = 0;
Packit 0af36a
Packit 0af36a
	if (!psl || !domain || *domain == '.')
Packit 0af36a
		return NULL;
Packit 0af36a
Packit 0af36a
	/*
Packit 0af36a
	 * In the main loop we introduce a O(N^2) behavior to avoid code duplication.
Packit 0af36a
	 * To avoid nasty CPU hogging, we limit the lookup to max. 8 domain labels to the right.
Packit 0af36a
	 */
Packit 0af36a
	for (p = domain + strlen(domain) - 1; p >= domain; p--) {
Packit 0af36a
		if (*p == '.' && ++nlabels > 8) {
Packit 0af36a
			domain = p + 1;
Packit 0af36a
			break;
Packit 0af36a
		}
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	/*
Packit 0af36a
	 *  We check from left to right to catch special PSL entries like 'forgot.his.name':
Packit 0af36a
	 *   'forgot.his.name' and 'name' are in the PSL while 'his.name' is not.
Packit 0af36a
	 */
Packit 0af36a
Packit 0af36a
	while (!_psl_is_public_suffix(psl, domain, 0)) {
Packit 0af36a
		if ((p = strchr(domain, '.'))) {
Packit 0af36a
			regdom = domain;
Packit 0af36a
			domain = p + 1;
Packit 0af36a
		} else
Packit 0af36a
			break; /* prevent endless loop if psl_is_public_suffix() is broken. */
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	return regdom;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_load_file:
Packit 0af36a
 * @fname: Name of PSL file
Packit 0af36a
 *
Packit 0af36a
 * This function loads the public suffixes file named @fname.
Packit 0af36a
 * To free the allocated resources, call psl_free().
Packit 0af36a
 *
Packit 0af36a
 * The suffixes are expected to be UTF-8 encoded (lowercase + NFKC) if they are international.
Packit 0af36a
 *
Packit 0af36a
 * Returns: Pointer to a PSL context or %NULL on failure.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.1
Packit 0af36a
 */
Packit 0af36a
psl_ctx_t *psl_load_file(const char *fname)
Packit 0af36a
{
Packit 0af36a
	FILE *fp;
Packit 0af36a
	psl_ctx_t *psl = NULL;
Packit 0af36a
Packit 0af36a
	if (!fname)
Packit 0af36a
		return NULL;
Packit 0af36a
Packit 0af36a
	if ((fp = fopen(fname, "r"))) {
Packit 0af36a
		psl = psl_load_fp(fp);
Packit 0af36a
		fclose(fp);
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	return psl;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_load_fp:
Packit 0af36a
 * @fp: FILE pointer
Packit 0af36a
 *
Packit 0af36a
 * This function loads the public suffixes from a FILE pointer.
Packit 0af36a
 * To free the allocated resources, call psl_free().
Packit 0af36a
 *
Packit 0af36a
 * The suffixes are expected to be UTF-8 encoded (lowercase + NFKC) if they are international.
Packit 0af36a
 *
Packit 0af36a
 * Returns: Pointer to a PSL context or %NULL on failure.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.1
Packit 0af36a
 */
Packit 0af36a
psl_ctx_t *psl_load_fp(FILE *fp)
Packit 0af36a
{
Packit 0af36a
	psl_ctx_t *psl;
Packit 0af36a
	_psl_entry_t suffix, *suffixp;
Packit 0af36a
	char buf[256], *linep, *p;
Packit 0af36a
	int type = 0, is_dafsa;
Packit 0af36a
	_psl_idna_t *idna;
Packit 0af36a
Packit 0af36a
	if (!fp)
Packit 0af36a
		return NULL;
Packit 0af36a
Packit 0af36a
	if (!(psl = calloc(1, sizeof(psl_ctx_t))))
Packit 0af36a
		return NULL;
Packit 0af36a
Packit 0af36a
	/* read first line to allow ASCII / DAFSA detection */
Packit 0af36a
	if (!(linep = fgets(buf, sizeof(buf) - 1, fp)))
Packit 0af36a
		goto fail;
Packit 0af36a
Packit 0af36a
	is_dafsa = strlen(buf) == 16 && !strncmp(buf, ".DAFSA@PSL_", 11);
Packit 0af36a
Packit 0af36a
	if (is_dafsa) {
Packit 0af36a
		void *m;
Packit 0af36a
		size_t size = 65536, n, len = 0;
Packit 0af36a
		int version = atoi(buf + 11);
Packit 0af36a
Packit 0af36a
		if (version != 0)
Packit 0af36a
			goto fail;
Packit 0af36a
Packit 0af36a
		if (!(psl->dafsa = malloc(size)))
Packit 0af36a
			goto fail;
Packit 0af36a
Packit 0af36a
		memcpy(psl->dafsa, buf, len);
Packit 0af36a
Packit 0af36a
		while ((n = fread(psl->dafsa + len, 1, size - len, fp)) > 0) {
Packit 0af36a
			len += n;
Packit 0af36a
			if (len >= size) {
Packit 0af36a
				if (!(m = realloc(psl->dafsa, size *= 2)))
Packit 0af36a
					goto fail;
Packit 0af36a
				psl->dafsa = m;
Packit 0af36a
			}
Packit 0af36a
		}
Packit 0af36a
Packit 0af36a
		/* release unused memory */
Packit 0af36a
		if ((m = realloc(psl->dafsa, len)))
Packit 0af36a
			psl->dafsa = m;
Packit 0af36a
		else if (!len)
Packit 0af36a
			psl->dafsa = NULL; /* realloc() just free'd psl->dafsa */
Packit 0af36a
Packit 0af36a
		psl->dafsa_size = len;
Packit 0af36a
		psl->utf8 = !!GetUtfMode(psl->dafsa, len);
Packit 0af36a
Packit 0af36a
		return psl;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	idna = _psl_idna_open();
Packit 0af36a
Packit 0af36a
	/*
Packit 0af36a
	 *  as of 02.11.2012, the list at https://publicsuffix.org/list/ contains ~6000 rules and 40 exceptions.
Packit 0af36a
	 *  as of 19.02.2014, the list at https://publicsuffix.org/list/ contains ~6500 rules and 19 exceptions.
Packit 0af36a
	 */
Packit 0af36a
	psl->suffixes = _vector_alloc(8*1024, _suffix_compare_array);
Packit 0af36a
	psl->utf8 = 1; /* we put UTF-8 and punycode rules in the lookup vector */
Packit 0af36a
Packit 0af36a
	do {
Packit 0af36a
		while (_isspace_ascii(*linep)) linep++; /* ignore leading whitespace */
Packit 0af36a
		if (!*linep) continue; /* skip empty lines */
Packit 0af36a
Packit 0af36a
		if (*linep == '/' && linep[1] == '/') {
Packit 0af36a
			if (!type) {
Packit 0af36a
				if (strstr(linep + 2, "===BEGIN ICANN DOMAINS==="))
Packit 0af36a
					type = _PSL_FLAG_ICANN;
Packit 0af36a
				else if (!type && strstr(linep + 2, "===BEGIN PRIVATE DOMAINS==="))
Packit 0af36a
					type = _PSL_FLAG_PRIVATE;
Packit 0af36a
			}
Packit 0af36a
			else if (type == _PSL_FLAG_ICANN && strstr(linep + 2, "===END ICANN DOMAINS==="))
Packit 0af36a
				type = 0;
Packit 0af36a
			else if (type == _PSL_FLAG_PRIVATE && strstr(linep + 2, "===END PRIVATE DOMAINS==="))
Packit 0af36a
				type = 0;
Packit 0af36a
Packit 0af36a
			continue; /* skip comments */
Packit 0af36a
		}
Packit 0af36a
Packit 0af36a
		/* parse suffix rule */
Packit 0af36a
		for (p = linep; *linep && !_isspace_ascii(*linep);) linep++;
Packit 0af36a
		*linep = 0;
Packit 0af36a
Packit 0af36a
		if (*p == '!') {
Packit 0af36a
			p++;
Packit 0af36a
			suffix.flags = _PSL_FLAG_EXCEPTION | type;
Packit 0af36a
			psl->nexceptions++;
Packit 0af36a
		} else if (*p == '*') {
Packit 0af36a
			if (*++p != '.') {
Packit 0af36a
				/* fprintf(stderr, _("Unsupported kind of rule (ignored): %s\n"), p - 1); */
Packit 0af36a
				continue;
Packit 0af36a
			}
Packit 0af36a
			p++;
Packit 0af36a
			/* wildcard *.foo.bar implicitly make foo.bar a public suffix */
Packit 0af36a
			suffix.flags = _PSL_FLAG_WILDCARD | _PSL_FLAG_PLAIN | type;
Packit 0af36a
			psl->nwildcards++;
Packit 0af36a
			psl->nsuffixes++;
Packit 0af36a
		} else {
Packit 0af36a
			suffix.flags = _PSL_FLAG_PLAIN | type;
Packit 0af36a
			psl->nsuffixes++;
Packit 0af36a
		}
Packit 0af36a
Packit 0af36a
		if (_suffix_init(&suffix, p, linep - p) == 0) {
Packit 0af36a
			int index;
Packit 0af36a
Packit 0af36a
			if ((index = _vector_find(psl->suffixes, &suffix)) >= 0) {
Packit 0af36a
				/* Found existing entry:
Packit 0af36a
				 * Combination of exception and plain rule is ambiguous
Packit 0af36a
				 * !foo.bar
Packit 0af36a
				 * foo.bar
Packit 0af36a
				 *
Packit 0af36a
				 * Allowed:
Packit 0af36a
				 * !foo.bar + *.foo.bar
Packit 0af36a
				 * foo.bar + *.foo.bar
Packit 0af36a
				 *
Packit 0af36a
				 * We do not check here, let's do it later.
Packit 0af36a
				 */
Packit 0af36a
Packit 0af36a
				suffixp = _vector_get(psl->suffixes, index);
Packit 0af36a
				suffixp->flags |= suffix.flags;
Packit 0af36a
			} else {
Packit 0af36a
				/* New entry */
Packit 0af36a
				suffixp = _vector_get(psl->suffixes, _vector_add(psl->suffixes, &suffix));
Packit 0af36a
			}
Packit 0af36a
Packit 0af36a
			if (suffixp) {
Packit 0af36a
				suffixp->label = suffixp->label_buf; /* set label to changed address */
Packit 0af36a
				_add_punycode_if_needed(idna, psl->suffixes, suffixp);
Packit 0af36a
			}
Packit 0af36a
		}
Packit 0af36a
	} while ((linep = fgets(buf, sizeof(buf), fp)));
Packit 0af36a
Packit 0af36a
	_vector_sort(psl->suffixes);
Packit 0af36a
Packit 0af36a
	_psl_idna_close(idna);
Packit 0af36a
Packit 0af36a
	return psl;
Packit 0af36a
Packit 0af36a
fail:
Packit 0af36a
	psl_free(psl);
Packit 0af36a
	return NULL;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_free:
Packit 0af36a
 * @psl: PSL context pointer
Packit 0af36a
 *
Packit 0af36a
 * This function frees the the PSL context that has been retrieved via
Packit 0af36a
 * psl_load_fp() or psl_load_file().
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.1
Packit 0af36a
 */
Packit 0af36a
void psl_free(psl_ctx_t *psl)
Packit 0af36a
{
Packit 0af36a
	if (psl && psl != &_builtin_psl) {
Packit 0af36a
		_vector_free(&psl->suffixes);
Packit 0af36a
		free(psl->dafsa);
Packit 0af36a
		free(psl);
Packit 0af36a
	}
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_builtin:
Packit 0af36a
 *
Packit 0af36a
 * This function returns the PSL context that has been generated and built in at compile-time.
Packit 0af36a
 * You don't have to free the returned context explicitly.
Packit 0af36a
 *
Packit 0af36a
 * The builtin data also contains punycode entries, one for each international domain name.
Packit 0af36a
 *
Packit 0af36a
 * If the generation of built-in data has been disabled during compilation, %NULL will be returned.
Packit 0af36a
 * When using the builtin psl context, you can provide UTF-8 (lowercase + NFKC) or ASCII/ACE (punycode)
Packit 0af36a
 * representations of domains to functions like psl_is_public_suffix().
Packit 0af36a
 *
Packit 0af36a
 * Returns: Pointer to the built in PSL data or NULL if this data is not available.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.1
Packit 0af36a
 */
Packit 0af36a
const psl_ctx_t *psl_builtin(void)
Packit 0af36a
{
Packit 0af36a
#if defined(BUILTIN_GENERATOR_LIBICU) || defined(BUILTIN_GENERATOR_LIBIDN2) || defined(BUILTIN_GENERATOR_LIBIDN)
Packit 0af36a
	return &_builtin_psl;
Packit 0af36a
#else
Packit 0af36a
	return NULL;
Packit 0af36a
#endif
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_suffix_count:
Packit 0af36a
 * @psl: PSL context pointer
Packit 0af36a
 *
Packit 0af36a
 * This function returns number of public suffixes maintained by @psl.
Packit 0af36a
 * The number of exceptions within the Public Suffix List are not included.
Packit 0af36a
 *
Packit 0af36a
 * If the information is not available, the return value is -1 (since 0.19).
Packit 0af36a
 * This is the case with DAFSA blobs or if @psl is NULL.
Packit 0af36a
 *
Packit 0af36a
 * Returns: Number of public suffixes entries in PSL context or -1 if this information is not available.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.1
Packit 0af36a
 */
Packit 0af36a
int psl_suffix_count(const psl_ctx_t *psl)
Packit 0af36a
{
Packit 0af36a
	if (psl == &_builtin_psl)
Packit 0af36a
		return _psl_nsuffixes;
Packit 0af36a
	else if (psl)
Packit 0af36a
		return psl->dafsa ? -1 : psl->nsuffixes;
Packit 0af36a
	else
Packit 0af36a
		return -1;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_suffix_exception_count:
Packit 0af36a
 * @psl: PSL context pointer
Packit 0af36a
 *
Packit 0af36a
 * This function returns number of public suffix exceptions maintained by @psl.
Packit 0af36a
 *
Packit 0af36a
 * If the information is not available, the return value is -1 (since 0.19).
Packit 0af36a
 * This is the case with DAFSA blobs or if @psl is NULL.
Packit 0af36a
 *
Packit 0af36a
 * Returns: Number of public suffix exceptions in PSL context or -1 if this information is not available.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.1
Packit 0af36a
 */
Packit 0af36a
int psl_suffix_exception_count(const psl_ctx_t *psl)
Packit 0af36a
{
Packit 0af36a
	if (psl == &_builtin_psl)
Packit 0af36a
		return _psl_nexceptions;
Packit 0af36a
	else if (psl)
Packit 0af36a
		return psl->dafsa ? -1 : psl->nexceptions;
Packit 0af36a
	else
Packit 0af36a
		return -1;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_suffix_wildcard_count:
Packit 0af36a
 * @psl: PSL context pointer
Packit 0af36a
 *
Packit 0af36a
 * This function returns number of public suffix wildcards maintained by @psl.
Packit 0af36a
 *
Packit 0af36a
 * If the information is not available, the return value is -1 (since 0.19).
Packit 0af36a
 * This is the case with DAFSA blobs or if @psl is NULL.
Packit 0af36a
 *
Packit 0af36a
 * Returns: Number of public suffix wildcards in PSL context or -1 if this information is not available.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.10.0
Packit 0af36a
 */
Packit 0af36a
int psl_suffix_wildcard_count(const psl_ctx_t *psl)
Packit 0af36a
{
Packit 0af36a
	if (psl == &_builtin_psl)
Packit 0af36a
		return _psl_nwildcards;
Packit 0af36a
	else if (psl)
Packit 0af36a
		return psl->dafsa ? -1 : psl->nwildcards;
Packit 0af36a
	else
Packit 0af36a
		return -1;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_builtin_file_time:
Packit 0af36a
 *
Packit 0af36a
 * This function returns the mtime of the Public Suffix List file that has been built in.
Packit 0af36a
 *
Packit 0af36a
 * If the generation of built-in data has been disabled during compilation, 0 will be returned.
Packit 0af36a
 *
Packit 0af36a
 * Returns: time_t value or 0.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.1
Packit 0af36a
 */
Packit 0af36a
time_t psl_builtin_file_time(void)
Packit 0af36a
{
Packit 0af36a
	return _psl_file_time;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_builtin_sha1sum:
Packit 0af36a
 *
Packit 0af36a
 * This function returns the SHA1 checksum of the Public Suffix List file that has been built in.
Packit 0af36a
 * The returned string is in lowercase hex encoding, e.g. "2af1e9e3044eda0678bb05949d7cca2f769901d8".
Packit 0af36a
 *
Packit 0af36a
 * If the generation of built-in data has been disabled during compilation, an empty string will be returned.
Packit 0af36a
 *
Packit 0af36a
 * Returns: String containing SHA1 checksum or an empty string.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.1
Packit 0af36a
 */
Packit 0af36a
const char *psl_builtin_sha1sum(void)
Packit 0af36a
{
Packit 0af36a
	return _psl_sha1_checksum;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_builtin_filename:
Packit 0af36a
 *
Packit 0af36a
 * This function returns the file name of the Public Suffix List file that has been built in.
Packit 0af36a
 *
Packit 0af36a
 * If the generation of built-in data has been disabled during compilation, an empty string will be returned.
Packit 0af36a
 *
Packit 0af36a
 * Returns: String containing the PSL file name or an empty string.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.1
Packit 0af36a
 */
Packit 0af36a
const char *psl_builtin_filename(void)
Packit 0af36a
{
Packit 0af36a
	return _psl_filename;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_builtin_outdated:
Packit 0af36a
 *
Packit 0af36a
 * This function checks if the built-in data is older than the file it has been created from.
Packit 0af36a
 * If it is, it might be a good idea for the application to reload the PSL.
Packit 0af36a
 * The mtime is taken as reference.
Packit 0af36a
 *
Packit 0af36a
 * If the PSL file does not exist, it is assumed that the built-in data is not outdated.
Packit 0af36a
 *
Packit 0af36a
 * Returns: 1 if the built-in is outdated, 0 otherwise.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.10.0
Packit 0af36a
 */
Packit 0af36a
int psl_builtin_outdated(void)
Packit 0af36a
{
Packit 0af36a
	struct stat st;
Packit 0af36a
Packit 0af36a
	if (stat(_psl_filename, &st) == 0 && st.st_mtime > _psl_file_time)
Packit 0af36a
		return 1;
Packit 0af36a
Packit 0af36a
	return 0;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_dist_filename:
Packit 0af36a
 *
Packit 0af36a
 * This function returns the file name of the distribution/system PSL data file.
Packit 0af36a
 * This file will be considered by psl_latest().
Packit 0af36a
 *
Packit 0af36a
 * Return the filename that is set by ./configure --with-psl-distfile, or an empty string.
Packit 0af36a
 *
Packit 0af36a
 * Returns: String containing a PSL file name or an empty string.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.16
Packit 0af36a
 */
Packit 0af36a
const char *psl_dist_filename(void)
Packit 0af36a
{
Packit 0af36a
	return _psl_dist_filename;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_get_version:
Packit 0af36a
 *
Packit 0af36a
 * Get libpsl version.
Packit 0af36a
 *
Packit 0af36a
 * Returns: String containing version of libpsl.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.2.5
Packit 0af36a
 **/
Packit 0af36a
const char *psl_get_version(void)
Packit 0af36a
{
Packit 0af36a
#ifdef WITH_LIBICU
Packit 0af36a
	return PACKAGE_VERSION " (+libicu/" U_ICU_VERSION ")";
Packit 0af36a
#elif defined(WITH_LIBIDN2)
Packit 0af36a
	return PACKAGE_VERSION " (+libidn2/" IDN2_VERSION ")";
Packit 0af36a
#elif defined(WITH_LIBIDN)
Packit 0af36a
	return PACKAGE_VERSION " (+libidn/" STRINGPREP_VERSION ")";
Packit 0af36a
#else
Packit 0af36a
	return PACKAGE_VERSION " (no IDNA support)";
Packit 0af36a
#endif
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_check_version_number:
Packit 0af36a
 * @version: Version number (hex) to check against.
Packit 0af36a
 *
Packit 0af36a
 * Check the given version number is at minimum the current library version number.
Packit 0af36a
 * The version number must be a hexadecimal number like 0x000a01 (V0.10.1).
Packit 0af36a
 *
Packit 0af36a
 * Returns: Returns the library version number if the given version number is at least
Packit 0af36a
 * the version of the library, else return 0; If the argument is 0, the function returns
Packit 0af36a
 * the library version number without performing a check.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.11.0
Packit 0af36a
 **/
Packit 0af36a
int psl_check_version_number(int version)
Packit 0af36a
{
Packit 0af36a
	if (version) {
Packit 0af36a
		int major = version >> 16;
Packit 0af36a
		int minor = (version >> 8) & 0xFF;
Packit 0af36a
		int patch = version & 0xFF;
Packit 0af36a
Packit 0af36a
		if (major < PSL_VERSION_MAJOR
Packit 0af36a
			|| (major == PSL_VERSION_MAJOR && minor < PSL_VERSION_MINOR)
Packit 0af36a
			|| (major == PSL_VERSION_MAJOR && minor == PSL_VERSION_MINOR && patch < PSL_VERSION_PATCH))
Packit 0af36a
		{
Packit 0af36a
			return 0;
Packit 0af36a
		}
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	return PSL_VERSION_NUMBER;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/* return whether hostname is an IP address or not */
Packit 0af36a
static int _isip(const char *hostname)
Packit 0af36a
{
Packit 0af36a
	struct in_addr addr;
Packit 0af36a
	struct in6_addr addr6;
Packit 0af36a
Packit 0af36a
	return inet_pton(AF_INET, hostname, &addr) || inet_pton(AF_INET6, hostname, &addr6);
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_is_cookie_domain_acceptable:
Packit 0af36a
 * @psl: PSL context pointer
Packit 0af36a
 * @hostname: The request hostname.
Packit 0af36a
 * @cookie_domain: The domain value from a cookie
Packit 0af36a
 *
Packit 0af36a
 * This helper function checks whether @cookie_domain is an acceptable cookie domain value for the request
Packit 0af36a
 * @hostname.
Packit 0af36a
 *
Packit 0af36a
 * For international domain names both, @hostname and @cookie_domain, have to be either in UTF-8 (lowercase + NFKC)
Packit 0af36a
 * or in ASCII/ACE (punycode) format. Other encodings or mixing UTF-8 and punycode likely result in incorrect return values.
Packit 0af36a
 *
Packit 0af36a
 * Use helper function psl_str_to_utf8lower() for normalization of @hostname and @cookie_domain.
Packit 0af36a
 *
Packit 0af36a
 * Examples:
Packit 0af36a
 * 1. Cookie domain 'example.com' would be acceptable for hostname 'www.example.com',
Packit 0af36a
 * but '.com' or 'com' would NOT be acceptable since 'com' is a public suffix.
Packit 0af36a
 *
Packit 0af36a
 * 2. Cookie domain 'his.name' would be acceptable for hostname 'remember.his.name',
Packit 0af36a
 *  but NOT for 'forgot.his.name' since 'forgot.his.name' is a public suffix.
Packit 0af36a
 *
Packit 0af36a
 * Returns: 1 if acceptable, 0 if not acceptable.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.1
Packit 0af36a
 */
Packit 0af36a
int psl_is_cookie_domain_acceptable(const psl_ctx_t *psl, const char *hostname, const char *cookie_domain)
Packit 0af36a
{
Packit 0af36a
	const char *p;
Packit 0af36a
	size_t hostname_length, cookie_domain_length;
Packit 0af36a
Packit 0af36a
	if (!psl || !hostname || !cookie_domain)
Packit 0af36a
		return 0;
Packit 0af36a
Packit 0af36a
	while (*cookie_domain == '.')
Packit 0af36a
		cookie_domain++;
Packit 0af36a
Packit 0af36a
	if (!strcmp(hostname, cookie_domain))
Packit 0af36a
		return 1; /* an exact match is acceptable (and pretty common) */
Packit 0af36a
Packit 0af36a
	if (_isip(hostname))
Packit 0af36a
		return 0; /* Hostname is an IP address and these must match fully (RFC 6265, 5.1.3) */
Packit 0af36a
Packit 0af36a
	cookie_domain_length = strlen(cookie_domain);
Packit 0af36a
	hostname_length = strlen(hostname);
Packit 0af36a
Packit 0af36a
	if (cookie_domain_length >= hostname_length)
Packit 0af36a
		return 0; /* cookie_domain is too long */
Packit 0af36a
Packit 0af36a
	p = hostname + hostname_length - cookie_domain_length;
Packit 0af36a
	if (!strcmp(p, cookie_domain) && p[-1] == '.') {
Packit 0af36a
		/* OK, cookie_domain matches, but it must be longer than the longest public suffix in 'hostname' */
Packit 0af36a
Packit 0af36a
		if (!(p = psl_unregistrable_domain(psl, hostname)))
Packit 0af36a
			return 1;
Packit 0af36a
Packit 0af36a
		if (cookie_domain_length > strlen(p))
Packit 0af36a
			return 1;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	return 0;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_free_string:
Packit 0af36a
 * @str: pointer to lowercase string returned by psl_str_to_utf8lower()
Packit 0af36a
 *
Packit 0af36a
 * This function free()'s the memory allocated by psl_str_to_utf8lower() when
Packit 0af36a
 * returning a lowercase string
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.19
Packit 0af36a
 */
Packit 0af36a
void psl_free_string(char *str)
Packit 0af36a
{
Packit 0af36a
	if (str)
Packit 0af36a
		free(str);
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_str_to_utf8lower:
Packit 0af36a
 * @str: string to convert
Packit 0af36a
 * @encoding: charset encoding of @str, e.g. 'iso-8859-1' or %NULL
Packit 0af36a
 * @locale: locale of @str for to lowercase conversion, e.g. 'de' or %NULL
Packit 0af36a
 * @lower: return value containing the converted string
Packit 0af36a
 *
Packit 0af36a
 * This helper function converts a string to UTF-8 lowercase + NFKC representation.
Packit 0af36a
 * Lowercase + NFKC UTF-8 is needed as input to the domain checking functions.
Packit 0af36a
 *
Packit 0af36a
 * @lower stays unchanged on error.
Packit 0af36a
 *
Packit 0af36a
 * When returning PSL_SUCCESS, the return value 'lower' must be freed after usage.
Packit 0af36a
 *
Packit 0af36a
 * Returns: psl_error_t value.
Packit 0af36a
 *   PSL_SUCCESS: Success
Packit 0af36a
 *   PSL_ERR_INVALID_ARG: @str is a %NULL value.
Packit 0af36a
 *   PSL_ERR_CONVERTER: Failed to open the unicode converter with name @encoding
Packit 0af36a
 *   PSL_ERR_TO_UTF16: Failed to convert @str to unicode
Packit 0af36a
 *   PSL_ERR_TO_LOWER: Failed to convert unicode to lowercase
Packit 0af36a
 *   PSL_ERR_TO_UTF8: Failed to convert unicode to UTF-8
Packit 0af36a
 *   PSL_ERR_NO_MEM: Failed to allocate memory
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.4
Packit 0af36a
 */
Packit 0af36a
psl_error_t psl_str_to_utf8lower(const char *str, const char *encoding _UNUSED, const char *locale _UNUSED, char **lower)
Packit 0af36a
{
Packit 0af36a
	int ret = PSL_ERR_INVALID_ARG;
Packit 0af36a
Packit 0af36a
	if (!str)
Packit 0af36a
		return PSL_ERR_INVALID_ARG;
Packit 0af36a
Packit 0af36a
	/* shortcut to avoid costly conversion */
Packit 0af36a
	if (_str_is_ascii(str)) {
Packit 0af36a
		if (lower) {
Packit 0af36a
			char *p, *tmp;
Packit 0af36a
Packit 0af36a
			if (!(tmp = strdup(str)))
Packit 0af36a
				return PSL_ERR_NO_MEM;
Packit 0af36a
Packit 0af36a
			*lower = tmp;
Packit 0af36a
Packit 0af36a
			/* convert ASCII string to lowercase */
Packit 0af36a
			for (p = *lower; *p; p++)
Packit 0af36a
				if (isupper(*p))
Packit 0af36a
					*p = tolower(*p);
Packit 0af36a
		}
Packit 0af36a
		return PSL_SUCCESS;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
#ifdef WITH_LIBICU
Packit 0af36a
	do {
Packit 0af36a
	size_t str_length = strlen(str);
Packit 0af36a
	UErrorCode status = 0;
Packit 0af36a
	UChar *utf16_dst, *utf16_lower;
Packit 0af36a
	int32_t utf16_dst_length;
Packit 0af36a
	char *utf8_lower;
Packit 0af36a
	UConverter *uconv;
Packit 0af36a
Packit 0af36a
	if (str_length < 256) {
Packit 0af36a
		/* C89 allocation */
Packit 0af36a
		utf16_dst   = alloca(sizeof(UChar) * (str_length * 2 + 1));
Packit 0af36a
		utf16_lower = alloca(sizeof(UChar) * (str_length * 2 + 1));
Packit 0af36a
		utf8_lower  = alloca(str_length * 6 + 1);
Packit 0af36a
	} else {
Packit 0af36a
		utf16_dst   = malloc(sizeof(UChar) * (str_length * 2 + 1));
Packit 0af36a
		utf16_lower = malloc(sizeof(UChar) * (str_length * 2 + 1));
Packit 0af36a
		utf8_lower  = malloc(str_length * 6 + 1);
Packit 0af36a
Packit 0af36a
		if (!utf16_dst || !utf16_lower || !utf8_lower) {
Packit 0af36a
			ret = PSL_ERR_NO_MEM;
Packit 0af36a
			goto out;
Packit 0af36a
		}
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	uconv = ucnv_open(encoding, &status);
Packit 0af36a
	if (U_SUCCESS(status)) {
Packit 0af36a
		utf16_dst_length = ucnv_toUChars(uconv, utf16_dst, str_length * 2 + 1, str, str_length, &status);
Packit 0af36a
		ucnv_close(uconv);
Packit 0af36a
Packit 0af36a
		if (U_SUCCESS(status)) {
Packit 0af36a
			int32_t utf16_lower_length = u_strToLower(utf16_lower, str_length * 2 + 1, utf16_dst, utf16_dst_length, locale, &status);
Packit 0af36a
			if (U_SUCCESS(status)) {
Packit 0af36a
				u_strToUTF8(utf8_lower, str_length * 6 + 1, NULL, utf16_lower, utf16_lower_length, &status);
Packit 0af36a
				if (U_SUCCESS(status)) {
Packit 0af36a
					ret = PSL_SUCCESS;
Packit 0af36a
					if (lower) {
Packit 0af36a
						char *tmp = strdup(utf8_lower);
Packit 0af36a
Packit 0af36a
						if (tmp)
Packit 0af36a
							*lower = tmp;
Packit 0af36a
						else
Packit 0af36a
							ret = PSL_ERR_NO_MEM;
Packit 0af36a
					}
Packit 0af36a
				} else {
Packit 0af36a
					ret = PSL_ERR_TO_UTF8;
Packit 0af36a
					/* fprintf(stderr, "Failed to convert UTF-16 to UTF-8 (status %d)\n", status); */
Packit 0af36a
				}
Packit 0af36a
			} else {
Packit 0af36a
				ret = PSL_ERR_TO_LOWER;
Packit 0af36a
				/* fprintf(stderr, "Failed to convert UTF-16 to lowercase (status %d)\n", status); */
Packit 0af36a
			}
Packit 0af36a
		} else {
Packit 0af36a
			ret = PSL_ERR_TO_UTF16;
Packit 0af36a
			/* fprintf(stderr, "Failed to convert string to UTF-16 (status %d)\n", status); */
Packit 0af36a
		}
Packit 0af36a
	} else {
Packit 0af36a
		ret = PSL_ERR_CONVERTER;
Packit 0af36a
		/* fprintf(stderr, "Failed to open converter for '%s' (status %d)\n", encoding, status); */
Packit 0af36a
	}
Packit 0af36a
out:
Packit 0af36a
	if (str_length >= 256) {
Packit 0af36a
		free(utf16_dst);
Packit 0af36a
		free(utf16_lower);
Packit 0af36a
		free(utf8_lower);
Packit 0af36a
	}
Packit 0af36a
	} while (0);
Packit 0af36a
#elif defined(WITH_LIBIDN2) || defined(WITH_LIBIDN)
Packit 0af36a
	do {
Packit 0af36a
		/* find out local charset encoding */
Packit 0af36a
		if (!encoding) {
Packit 0af36a
#ifdef HAVE_NL_LANGINFO
Packit 0af36a
			encoding = nl_langinfo(CODESET);
Packit 0af36a
#elif defined _WIN32
Packit 0af36a
			static char buf[16];
Packit 0af36a
			snprintf(buf, sizeof(buf), "CP%u", GetACP());
Packit 0af36a
			encoding = buf;
Packit 0af36a
#endif
Packit 0af36a
			if (!encoding || !*encoding)
Packit 0af36a
				encoding = "ASCII";
Packit 0af36a
		}
Packit 0af36a
Packit 0af36a
		/* convert to UTF-8 */
Packit 0af36a
		if (strcasecmp(encoding, "utf-8")) {
Packit 0af36a
			iconv_t cd = iconv_open("utf-8", encoding);
Packit 0af36a
Packit 0af36a
			if (cd != (iconv_t)-1) {
Packit 0af36a
				char *tmp = (char *)str; /* iconv won't change where str points to, but changes tmp itself */
Packit 0af36a
				size_t tmp_len = strlen(str) + 1;
Packit 0af36a
				size_t dst_len = tmp_len * 6, dst_len_tmp = dst_len;
Packit 0af36a
				char *dst = malloc(dst_len + 1), *dst_tmp = dst;
Packit 0af36a
Packit 0af36a
				if (!dst) {
Packit 0af36a
					ret = PSL_ERR_NO_MEM;
Packit 0af36a
				}
Packit 0af36a
				else if (iconv(cd, (WINICONV_CONST char **)&tmp, &tmp_len, &dst_tmp, &dst_len_tmp) != (size_t)-1
Packit 0af36a
					&& iconv(cd, NULL, NULL, &dst_tmp, &dst_len_tmp) != (size_t)-1)
Packit 0af36a
				{
Packit 0af36a
					/* start size for u8_tolower internal memory allocation.
Packit 0af36a
					 * u8_tolower() does not terminate the result string. we have 0 byte included in above tmp_len
Packit 0af36a
					 * and thus in len. */
Packit 0af36a
					size_t len = dst_len - dst_len_tmp;
Packit 0af36a
Packit 0af36a
					if ((tmp = (char *)u8_tolower((uint8_t *)dst, len, 0, UNINORM_NFKC, NULL, &len))) {
Packit 0af36a
						ret = PSL_SUCCESS;
Packit 0af36a
						if (lower) {
Packit 0af36a
							*lower = tmp;
Packit 0af36a
							tmp = NULL;
Packit 0af36a
						} else
Packit 0af36a
							free(tmp);
Packit 0af36a
					} else {
Packit 0af36a
						ret = PSL_ERR_TO_LOWER;
Packit 0af36a
						/* fprintf(stderr, "Failed to convert UTF-8 to lowercase (errno %d)\n", errno); */
Packit 0af36a
					}
Packit 0af36a
				} else {
Packit 0af36a
					ret = PSL_ERR_TO_UTF8;
Packit 0af36a
					/* fprintf(stderr, "Failed to convert '%s' string into '%s' (%d)\n", src_encoding, dst_encoding, errno); */
Packit 0af36a
				}
Packit 0af36a
Packit 0af36a
				free(dst);
Packit 0af36a
				iconv_close(cd);
Packit 0af36a
			} else {
Packit 0af36a
				ret = PSL_ERR_TO_UTF8;
Packit 0af36a
				/* fprintf(stderr, "Failed to prepare encoding '%s' into '%s' (%d)\n", src_encoding, dst_encoding, errno); */
Packit 0af36a
			}
Packit 0af36a
		} else {
Packit 0af36a
			/* we need a conversion to lowercase */
Packit 0af36a
			uint8_t *tmp;
Packit 0af36a
Packit 0af36a
			/* start size for u8_tolower internal memory allocation.
Packit 0af36a
			 * u8_tolower() does not terminate the result string, so include terminating 0 byte in len. */
Packit 0af36a
			size_t len = u8_strlen((uint8_t *)str) + 1;
Packit 0af36a
Packit 0af36a
			if ((tmp = u8_tolower((uint8_t *)str, len, 0, UNINORM_NFKC, NULL, &len))) {
Packit 0af36a
				ret = PSL_SUCCESS;
Packit 0af36a
				if (lower) {
Packit 0af36a
					*lower = (char*)tmp;
Packit 0af36a
					tmp = NULL;
Packit 0af36a
				} else
Packit 0af36a
					free(tmp);
Packit 0af36a
			} else {
Packit 0af36a
				ret = PSL_ERR_TO_LOWER;
Packit 0af36a
				/* fprintf(stderr, "Failed to convert UTF-8 to lowercase (errno %d)\n", errno); */
Packit 0af36a
			}
Packit 0af36a
		}
Packit 0af36a
Packit 0af36a
	} while (0);
Packit 0af36a
#endif
Packit 0af36a
Packit 0af36a
	return ret;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/* if file is newer than the builtin data, insert it reverse sorted by mtime */
Packit 0af36a
static int _insert_file(const char *fname, const char **psl_fname, time_t *psl_mtime, int n)
Packit 0af36a
{
Packit 0af36a
	struct stat st;
Packit 0af36a
	int it;
Packit 0af36a
Packit 0af36a
	if (fname && *fname && stat(fname, &st) == 0 && st.st_mtime > _psl_file_time) {
Packit 0af36a
		/* add file name and mtime to end of array */
Packit 0af36a
		psl_fname[n] = fname;
Packit 0af36a
		psl_mtime[n++] = st.st_mtime;
Packit 0af36a
Packit 0af36a
		/* move the new entry to it's correct position */
Packit 0af36a
		for (it = n - 2; it >= 0 && st.st_mtime > psl_mtime[it]; it--) {
Packit 0af36a
			psl_fname[it + 1] = psl_fname[it];
Packit 0af36a
			psl_mtime[it + 1] = psl_mtime[it];
Packit 0af36a
			psl_fname[it] = fname;
Packit 0af36a
			psl_mtime[it] = st.st_mtime;
Packit 0af36a
		}
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	return n;
Packit 0af36a
}
Packit 0af36a
Packit 0af36a
/**
Packit 0af36a
 * psl_latest:
Packit 0af36a
 * @fname: Name of PSL file or %NULL
Packit 0af36a
 *
Packit 0af36a
 * This function loads the the latest available PSL data from either
Packit 0af36a
 * - @fname (application specific filename, may be %NULL)
Packit 0af36a
 * - location specified during built-time (filename from ./configure --with-psl-distfile)
Packit 0af36a
 * - built-in PSL data (generated from ./configure --with-psl-file)
Packit 0af36a
 * - location of built-in data (filename from ./configure --with-psl-file)
Packit 0af36a
 *
Packit 0af36a
 * If none of the above is available, the function returns %NULL.
Packit 0af36a
 *
Packit 0af36a
 * To free the allocated resources, call psl_free().
Packit 0af36a
 *
Packit 0af36a
 * Returns: Pointer to a PSL context or %NULL on failure.
Packit 0af36a
 *
Packit 0af36a
 * Since: 0.16
Packit 0af36a
 */
Packit 0af36a
psl_ctx_t *psl_latest(const char *fname)
Packit 0af36a
{
Packit 0af36a
	psl_ctx_t *psl;
Packit 0af36a
	const char *psl_fname[3];
Packit 0af36a
	time_t psl_mtime[3];
Packit 0af36a
	int it, ntimes;
Packit 0af36a
Packit 0af36a
	psl_fname[0] = NULL; /* silence gcc 6.2 false warning */
Packit 0af36a
Packit 0af36a
	/* create array of PSL files reverse sorted by mtime (latest first) */
Packit 0af36a
	ntimes = _insert_file(fname, psl_fname, psl_mtime, 0);
Packit 0af36a
	ntimes = _insert_file(_psl_dist_filename, psl_fname, psl_mtime, ntimes);
Packit 0af36a
	ntimes = _insert_file(_psl_filename, psl_fname, psl_mtime, ntimes);
Packit 0af36a
Packit 0af36a
	/* load PSL data from the latest file, falling back to the second recent, ... */
Packit 0af36a
	for (psl = NULL, it = 0; it < ntimes; it++) {
Packit 0af36a
		if (psl_mtime[it] > _psl_file_time)
Packit 0af36a
			if ((psl = psl_load_file(psl_fname[it])))
Packit 0af36a
				break;
Packit 0af36a
	}
Packit 0af36a
Packit 0af36a
	/* if file loading failed or there is no file newer than the builtin data,
Packit 0af36a
	 * then return the builtin data. */
Packit 0af36a
	return psl ? psl : (psl_ctx_t *) psl_builtin();
Packit 0af36a
}