Beispiel #1
0
static void test_psl(void)
{
	/* punycode generation: idn ?? */
	/* octal code generation: echo -n "??" | od -b */
	static const struct test_data {
		const char
			*domain;
		int
			result;
	} test_data[] = {
		{ "www.example.com", 0 },
		{ "com.ar", 1 },
		{ "www.com.ar", 0 },
		{ "cc.ar.us", 1 },
		{ ".cc.ar.us", 1 },
		{ "www.cc.ar.us", 0 },
		{ "www.ck", 0 }, /* exception from *.ck */
		{ "abc.www.ck", 0 },
		{ "xxx.ck", 1 },
		{ "www.xxx.ck", 0 },
		{ "\345\225\206\346\240\207", 1 }, /* xn--czr694b or ?? */
		{ "www.\345\225\206\346\240\207", 0 },
		/* some special test follow ('name' and 'forgot.his.name' are public, but e.g. his.name is not) */
		{ "name", 1 },
		{ ".name", 1 },
		{ "his.name", 0 },
		{ ".his.name", 0 },
		{ "forgot.his.name", 1 },
		{ ".forgot.his.name", 1 },
		{ "whoever.his.name", 0 },
		{ "whoever.forgot.his.name", 0 },
		{ ".", 1 }, /* special case */
		{ "", 1 },  /* special case */
		{ NULL, 1 },  /* special case */
		{ "adfhoweirh", 1 }, /* unknown TLD */
	};
	unsigned it;
	psl_ctx_t *psl;

	psl = psl_load_file(PSL_FILE);

	printf("loaded %d suffixes and %d exceptions\n", psl_suffix_count(psl), psl_suffix_exception_count(psl));

	for (it = 0; it < countof(test_data); it++) {
		const struct test_data *t = &test_data[it];
		int result = psl_is_public_suffix(psl, t->domain);

		if (result == t->result) {
			ok++;
		} else {
			failed++;
			printf("psl_is_public_suffix(%s)=%d (expected %d)\n", t->domain, result, t->result);
		}
	}

	psl_free(psl);
}
Beispiel #2
0
int mget_cookie_db_load_psl(mget_cookie_db_t *cookie_db, const char *fname)
{
#ifdef WITH_LIBPSL
		if (fname) {
			psl_ctx_t *psl = psl_load_file(fname);

			if (psl)
				psl_free(cookie_db->psl);
			cookie_db->psl = psl;
		} else {
			psl_free(cookie_db->psl);
			cookie_db->psl = NULL;
		}

		return 0;
#else
		return -1;
#endif
}
Beispiel #3
0
static void test_psl(void)
{
	FILE *fp;
	psl_ctx_t *psl, *psl3, *psl4, *psl5;
	const psl_ctx_t *psl2;
	int type = 0;
	char buf[256], *linep, *p;

	psl = psl_load_file(PSL_FILE); /* PSL_FILE can be set by ./configure --with-psl-file=[PATH] */
	printf("loaded %d suffixes and %d exceptions\n", psl_suffix_count(psl), psl_suffix_exception_count(psl));

	psl2 = psl_builtin();
	printf("builtin PSL has %d suffixes and %d exceptions\n", psl_suffix_count(psl2), psl_suffix_exception_count(psl2));

	if (!(psl3 = psl_load_file(PSL_DAFSA))) {
		fprintf(stderr, "Failed to load 'psl.dafsa'\n");
		failed++;
	}

	if (!(psl4 = psl_load_file(PSL_ASCII_DAFSA))) {
		fprintf(stderr, "Failed to load 'psl_ascii.dafsa'\n");
		failed++;
	}

	psl5 = psl_latest("psl.dafsa");

	if ((fp = fopen(PSL_FILE, "r"))) {
#ifdef HAVE_CLOCK_GETTIME
		clock_gettime(CLOCK_REALTIME, &ts1);
#endif

		while ((linep = fgets(buf, sizeof(buf), fp))) {
			while (_isspace_ascii(*linep)) linep++; /* ignore leading whitespace */
			if (!*linep) continue; /* skip empty lines */

			if (*linep == '/' && linep[1] == '/') {
				if (!type) {
					if (strstr(linep + 2, "===BEGIN ICANN DOMAINS==="))
						type = PSL_TYPE_ICANN;
					else if (!type && strstr(linep + 2, "===BEGIN PRIVATE DOMAINS==="))
						type = PSL_TYPE_PRIVATE;
				}
				else if (type == PSL_TYPE_ICANN && strstr(linep + 2, "===END ICANN DOMAINS==="))
					type = 0;
				else if (type == PSL_TYPE_PRIVATE && strstr(linep + 2, "===END PRIVATE DOMAINS==="))
					type = 0;

				continue; /* skip comments */
			}

			/* parse suffix rule */
			for (p = linep; *linep && !_isspace_ascii(*linep);) linep++;
			*linep = 0;

			test_psl_entry(psl, p, type);

			if (psl2)
				test_psl_entry(psl2, p, type);

			if (psl3)
				test_psl_entry(psl3, p, type);

			if (psl4)
				test_psl_entry(psl4, p, type);

			if (psl5)
				test_psl_entry(psl5, p, type);
		}

#ifdef HAVE_CLOCK_GETTIME
		clock_gettime(CLOCK_REALTIME, &ts2);
#endif
		fclose(fp);
	} else {
		printf("Failed to open %s\n", PSL_FILE);
		failed++;
	}

	psl_free(psl5);
	psl_free(psl4);
	psl_free(psl3);
	psl_free((psl_ctx_t *)psl2);
	psl_free(psl);
}
Beispiel #4
0
static void test_psl(void)
{
	FILE *fp;
	psl_ctx_t *psl;
	int result;
	char buf[256], domain[64], *linep, *p;

	psl = psl_load_file(PSL_FILE); /* PSL_FILE can be set by ./configure --with-psl-file=[PATH] */

	printf("loaded %d suffixes and %d exceptions\n", psl_suffix_count(psl), psl_suffix_exception_count(psl));

	if ((fp = fopen(PSL_FILE, "r"))) {
		while ((linep = fgets(buf, sizeof(buf), fp))) {
			while (_isspace_ascii(*linep)) linep++; /* ignore leading whitespace */
			if (!*linep) continue; /* skip empty lines */

			if (*linep == '/' && linep[1] == '/')
				continue; /* skip comments */

			/* parse suffix rule */
			for (p = linep; *linep && !_isspace_ascii(*linep);) linep++;
			*linep = 0;

			if (*p == '!') { /* an exception to a wildcard, e.g. !www.ck (wildcard is *.ck) */
				if ((result = psl_is_public_suffix(psl, p + 1))) {
					failed++;
					printf("psl_is_public_suffix(%s)=%d (expected 0)\n", p, result);
				} else ok++;

				if (!(result = psl_is_public_suffix(psl, strchr(p, '.') + 1))) {
					failed++;
					printf("psl_is_public_suffix(%s)=%d (expected 1)\n", strchr(p, '.') + 1, result);
				} else ok++;
			}
			else if (*p == '*') { /* a wildcard, e.g. *.ck */
				if (!(result = psl_is_public_suffix(psl, p + 1))) {
					failed++;
					printf("psl_is_public_suffix(%s)=%d (expected 1)\n", p + 1, result);
				} else ok++;

				*p = 'x';
				if (!(result = psl_is_public_suffix(psl, p))) {
					failed++;
					printf("psl_is_public_suffix(%s)=%d (expected 1)\n", p, result);
				} else ok++;
			}
			else {
				if (!(result = psl_is_public_suffix(psl, p))) {
					failed++;
					printf("psl_is_public_suffix(%s)=%d (expected 1)\n", p, result);
				} else ok++;

				snprintf(domain, sizeof(domain), "xxxx.%s", p);
				if ((result = psl_is_public_suffix(psl, domain))) {
					failed++;
					printf("psl_is_public_suffix(%s)=%d (expected 0)\n", domain, result);
				} else ok++;
			}
		}

		fclose(fp);
	} else {
		printf("Failed to open %s\n", PSL_FILE);
		failed++;
	}

	psl_free(psl);
}
Beispiel #5
0
int main(int argc, const char **argv)
{
	FILE *fpout;
	psl_ctx_t *psl;
	int ret = 0, argpos = 1, binary = 0;

	if (argc < 3)
		usage();

	if (strcmp(argv[argpos], "--binary") == 0) {
		argpos++;
		binary = 1;
	}

	if (argc - argpos != 2)
		usage();

	if (binary) {
		if (!(psl = psl_load_file(argv[argpos])))
			return 2;

		ret = _print_psl_entries_dafsa_binary(argv[argpos + 1], psl->suffixes);

		psl_free(psl);
		return ret;
	}

#ifdef _GENERATE_BUILTIN_DATA
	if (!(psl = psl_load_file(argv[argpos])))
		return 2;

	/* look for ambiguous or double entries */
/*	if (_check_psl(psl)) {
		psl_free(psl);
		return 5;
	}
*/
	if ((fpout = fopen(argv[argpos + 1], "w"))) {
		FILE *pp;
		struct stat st;
		size_t cmdsize = 16 + strlen(argv[argpos]);
		char *cmd = alloca(cmdsize), checksum[64] = "";
		char *abs_srcfile;

		_print_psl_entries_dafsa(fpout, psl->suffixes);

		snprintf(cmd, cmdsize, "sha1sum %s", argv[argpos]);
		if ((pp = popen(cmd, "r"))) {
			if (fscanf(pp, "%63[0-9a-zA-Z]", checksum) < 1)
				*checksum = 0;
			pclose(pp);
		}

		if (stat(argv[argpos], &st) != 0)
			st.st_mtime = 0;
		fprintf(fpout, "static time_t _psl_file_time = %lu;\n", st.st_mtime);

		fprintf(fpout, "static int _psl_nsuffixes = %d;\n", psl->nsuffixes);
		fprintf(fpout, "static int _psl_nexceptions = %d;\n", psl->nexceptions);
		fprintf(fpout, "static int _psl_nwildcards = %d;\n", psl->nwildcards);
		fprintf(fpout, "static const char _psl_sha1_checksum[] = \"%s\";\n", checksum);

		/* We need an absolute path here, else psl_builtin_outdated() won't work reliable */
		/* Caveat: symbolic links are resolved by realpath() */
		if ((abs_srcfile = realpath(argv[argpos], NULL))) {
			fprintf(fpout, "static const char _psl_filename[] = \"%s\";\n", abs_srcfile);
			free(abs_srcfile);
		} else
			fprintf(fpout, "static const char _psl_filename[] = \"%s\";\n", argv[argpos]);

		if (fclose(fpout) != 0)
			ret = 4;
	} else {
		fprintf(stderr, "Failed to write open '%s'\n", argv[argpos + 1]);
		ret = 3;
	}

	psl_free(psl);
#else
	if ((fpout = fopen(argv[argpos + 1], "w"))) {
		fprintf(fpout, "static const unsigned char kDafsa[1];\n");
		fprintf(fpout, "static time_t _psl_file_time;\n");
		fprintf(fpout, "static int _psl_nsuffixes = 0;\n");
		fprintf(fpout, "static int _psl_nexceptions = 0;\n");
		fprintf(fpout, "static int _psl_nwildcards = 0;\n");
		fprintf(fpout, "static const char _psl_sha1_checksum[] = \"\";\n");
		fprintf(fpout, "static const char _psl_filename[] = \"\";\n");

		if (fclose(fpout) != 0)
			ret = 4;
	} else {
		fprintf(stderr, "Failed to write open '%s'\n", argv[argpos + 1]);
		ret = 3;
	}
#endif /* GENERATE_BUILTIN_DATA */

	return ret;
}
Beispiel #6
0
int main(int argc, const char **argv)
{
	FILE *fpout;
#ifdef _GENERATE_BUILTIN_DATA
	psl_ctx_t *psl;
#endif
	int ret = 0;

	if (argc != 3) {
		fprintf(stderr, "Usage: psl2c <infile> <outfile>\n");
		fprintf(stderr, "  <infile>  is the 'public_suffix_list.dat', lowercase UTF-8 encoded\n");
		fprintf(stderr, "  <outfile> is the the C filename to be generated from <infile>\n");
		return 1;
	}

#ifdef _GENERATE_BUILTIN_DATA
	if (!(psl = psl_load_file(argv[1])))
		return 2;

	/* look for ambigious or double entries */
/*	if (_check_psl(psl)) {
		psl_free(psl);
		return 5;
	}
*/
	if ((fpout = fopen(argv[2], "w"))) {
		FILE *pp;
		struct stat st;
		size_t cmdsize = 16 + strlen(argv[1]);
		char *cmd = alloca(cmdsize), checksum[64] = "";
		const char *source_date_epoch = NULL;

#if 0
		/* include library code did not generate punycode, so let's do it for the builtin data */
		_add_punycode_if_needed(psl->suffixes);
#endif

		_print_psl_entries(fpout, psl->suffixes, "suffixes");

		snprintf(cmd, cmdsize, "sha1sum %s", argv[1]);
		if ((pp = popen(cmd, "r"))) {
			if (fscanf(pp, "%63[0-9a-zA-Z]", checksum) < 1)
				*checksum = 0;
			pclose(pp);
		}

		if (stat(argv[1], &st) != 0)
			st.st_mtime = 0;
		fprintf(fpout, "static time_t _psl_file_time = %lu;\n", st.st_mtime);
		if ((source_date_epoch = getenv("SOURCE_DATE_EPOCH")))
			fprintf(fpout, "static time_t _psl_compile_time = %lu;\n", atol(source_date_epoch));
		else
			fprintf(fpout, "static time_t _psl_compile_time = %lu;\n", time(NULL));
		fprintf(fpout, "static int _psl_nsuffixes = %d;\n", psl->nsuffixes);
		fprintf(fpout, "static int _psl_nexceptions = %d;\n", psl->nexceptions);
		fprintf(fpout, "static int _psl_nwildcards = %d;\n", psl->nwildcards);
		fprintf(fpout, "static const char _psl_sha1_checksum[] = \"%s\";\n", checksum);
		fprintf(fpout, "static const char _psl_filename[] = \"%s\";\n", argv[1]);

		if (fclose(fpout) != 0)
			ret = 4;
	} else {
		fprintf(stderr, "Failed to write open '%s'\n", argv[2]);
		ret = 3;
	}

	psl_free(psl);
#else
	if ((fpout = fopen(argv[2], "w"))) {
		fprintf(fpout, "static _psl_entry_t suffixes[1];\n");
		fprintf(fpout, "static time_t _psl_file_time;\n");
		fprintf(fpout, "static time_t _psl_compile_time;\n");
		fprintf(fpout, "static int _psl_nsuffixes = 0;\n");
		fprintf(fpout, "static int _psl_nexceptions = 0;\n");
		fprintf(fpout, "static int _psl_nwildcards = 0;\n");
		fprintf(fpout, "static const char _psl_sha1_checksum[] = \"\";\n");
		fprintf(fpout, "static const char _psl_filename[] = \"\";\n");

		if (fclose(fpout) != 0)
			ret = 4;
	} else {
		fprintf(stderr, "Failed to write open '%s'\n", argv[2]);
		ret = 3;
	}
#endif /* GENERATE_BUILTIN_DATA */

	return ret;
}
Beispiel #7
0
int main(int argc, const char *const *argv)
{
	int mode = 1;
	const char *const *arg, *psl_file = NULL, *cookie_domain = NULL;
	psl_ctx_t *psl = (psl_ctx_t *) psl_builtin();

	/* set current locale according to the environment variables */
	#include <locale.h>
	setlocale(LC_ALL, "");

	for (arg = argv + 1; arg < argv + argc; arg++) {
		if (!strncmp(*arg, "--", 2)) {
			if (!strcmp(*arg, "--is-public-suffix"))
				mode = 1;
			else if (!strcmp(*arg, "--print-unreg-domain"))
				mode = 2;
			else if (!strcmp(*arg, "--print-reg-domain"))
				mode = 3;
			else if (!strcmp(*arg, "--print-info"))
				mode = 99;
			else if (!strcmp(*arg, "--is-cookie-domain-acceptable") && arg < argv + argc - 1) {
				mode = 4;
				cookie_domain = *(++arg);
			}
			else if (!strcmp(*arg, "--use-builtin-data")) {
				psl_free(psl);
				if (psl_file) {
					fprintf(stderr, "Dropped data from %s\n", psl_file);
					psl_file = NULL;
				}
				if (!(psl = (psl_ctx_t *) psl_builtin()))
					printf("No builtin PSL data available\n");
			}
			else if (!strcmp(*arg, "--load-psl-file") && arg < argv + argc - 1) {
				psl_free(psl);
				if (psl_file) {
					fprintf(stderr, "Dropped data from %s\n", psl_file);
					psl_file = NULL;
				}
				if (!(psl = psl_load_file(psl_file = *(++arg)))) {
					fprintf(stderr, "Failed to load PSL data from %s\n\n", psl_file);
					psl_file = NULL;
				}
			}
			else if (!strcmp(*arg, "--help")) {
				fprintf(stdout, "`psl' explores the Public Suffix List\n\n");
				usage(0, stdout);
			}
			else if (!strcmp(*arg, "--version")) {
				printf("psl %s\n", PACKAGE_VERSION);
				printf("libpsl %s\n", psl_get_version());
				printf("\n");
				printf("Copyright (C) 2014-2015 Tim Ruehsen\n");
				printf("License: MIT\n");
				exit(0);
			}
			else if (!strcmp(*arg, "--")) {
				arg++;
				break;
			}
			else {
				fprintf(stderr, "Unknown option '%s'\n", *arg);
				usage(1, stderr);
			}
		} else
			break;
	}

	if (mode != 99) {
		if (!psl) {
			fprintf(stderr, "No PSL data available - aborting\n");
			exit(2);
		}
		if (arg >= argv + argc) {
			char buf[256], *domain, *lower;
			size_t len;
			psl_error_t rc;

			/* read URLs from STDIN */
			while (fgets(buf, sizeof(buf), stdin)) {
				for (domain = buf; isspace(*domain); domain++); /* skip leading spaces */
				if (*domain == '#' || !*domain) continue; /* skip empty lines and comments */
				for (len = strlen(domain); len && isspace(domain[len - 1]); len--); /* skip trailing spaces */
				domain[len] = 0;

				if ((rc = psl_str_to_utf8lower(domain, NULL, NULL, &lower)) != PSL_SUCCESS)
					fprintf(stderr, "%s: Failed to convert to lowercase UTF-8 (%d)\n", domain, rc);
				else if (mode == 1)
					printf("%s: %d (%s)\n", domain, psl_is_public_suffix(psl, lower), lower);
				else if (mode == 2)
					printf("%s: %s\n", domain, psl_unregistrable_domain(psl, lower));
				else if (mode == 3)
					printf("%s: %s\n", domain, psl_registrable_domain(psl, lower));
				else if (mode == 4) {
					char *cookie_domain_lower;

					if ((rc = psl_str_to_utf8lower(domain, NULL, NULL, &cookie_domain_lower)) != PSL_SUCCESS)
						fprintf(stderr, "%s: Failed to convert cookie domain '%s' to lowercase UTF-8 (%d)\n", domain, cookie_domain, rc);
					else
						printf("%s: %d\n", domain, psl_is_cookie_domain_acceptable(psl, lower, cookie_domain));

					free(cookie_domain_lower);
				}

				free(lower);
			}

			psl_free(psl);
			exit(0);
		}
	}

	if (mode == 1) {
		for (; arg < argv + argc; arg++)
			printf("%s: %d\n", *arg, psl_is_public_suffix(psl, *arg));
	}
	else if (mode == 2) {
		for (; arg < argv + argc; arg++)
			printf("%s: %s\n", *arg, psl_unregistrable_domain(psl, *arg));
	}
	else if (mode == 3) {
		for (; arg < argv + argc; arg++)
			printf("%s: %s\n", *arg, psl_registrable_domain(psl, *arg));
	}
	else if (mode == 4) {
		for (; arg < argv + argc; arg++)
			printf("%s: %d\n", *arg, psl_is_cookie_domain_acceptable(psl, *arg, cookie_domain));
	}
	else if (mode == 99) {
		if (psl && psl != psl_builtin()) {
			printf("suffixes: %d\n", psl_suffix_count(psl));
			printf("exceptions: %d\n", psl_suffix_exception_count(psl));
			printf("wildcards: %d\n", psl_suffix_wildcard_count(psl));
		}

		psl_free(psl);
		psl = (psl_ctx_t *) psl_builtin();

		if (psl) {
			printf("builtin suffixes: %d\n", psl_suffix_count(psl));
			printf("builtin exceptions: %d\n", psl_suffix_exception_count(psl));
			printf("builtin wildcards: %d\n", psl_suffix_wildcard_count(psl));
			printf("builtin filename: %s\n", psl_builtin_filename());
			printf("builtin compile time: %ld (%s)\n", psl_builtin_compile_time(), time2str(psl_builtin_compile_time()));
			printf("builtin file time: %ld (%s)\n", psl_builtin_file_time(), time2str(psl_builtin_file_time()));
			printf("builtin SHA1 file hash: %s\n", psl_builtin_sha1sum());
		} else
			printf("No builtin PSL data available\n");
	}

	psl_free(psl);

	return 0;
}
Beispiel #8
0
int main(int argc, const char **argv)
{
	FILE *fpout;
#ifdef _GENERATE_BUILTIN_DATA
	psl_ctx_t *psl;
#endif
	int ret = 0;

	if (argc != 3) {
		fprintf(stderr, "Usage: psl2c <infile> <outfile>\n");
		fprintf(stderr, "  <infile>  is the 'effective_tld_names.dat' (aka Public Suffix List), lowercase UTF-8 encoded\n");
		fprintf(stderr, "  <outfile> is the the C filename to be generated from <infile>\n");
		return 1;
	}

#ifdef _GENERATE_BUILTIN_DATA
	if (!(psl = psl_load_file(argv[1])))
		return 2;

	if ((fpout = fopen(argv[2], "w"))) {
		FILE *pp;
		struct stat st;
		size_t cmdsize = 16 + strlen(argv[1]);
		char *cmd = alloca(cmdsize), checksum[64] = "";

#if 0
		/* include library code did not generate punycode, so let's do it for the builtin data */
		_add_punycode_if_needed(psl->suffixes);
		_add_punycode_if_needed(psl->suffix_exceptions);
#endif

		_print_psl_entries(fpout, psl->suffixes, "suffixes");
		_print_psl_entries(fpout, psl->suffix_exceptions, "suffix_exceptions");

		snprintf(cmd, cmdsize, "sha1sum %s", argv[1]);
		if ((pp = popen(cmd, "r"))) {
			if (fscanf(pp, "%63[0-9a-zA-Z]", checksum) < 1)
				*checksum = 0;
			pclose(pp);
		}

		if (stat(argv[1], &st) != 0)
			st.st_mtime = 0;
		fprintf(fpout, "static time_t _psl_file_time = %lu;\n", st.st_mtime);
		fprintf(fpout, "static time_t _psl_compile_time = %lu;\n", time(NULL));
		fprintf(fpout, "static const char _psl_sha1_checksum[] = \"%s\";\n", checksum);
		fprintf(fpout, "static const char _psl_filename[] = \"%s\";\n", argv[1]);

		if (fclose(fpout) != 0)
			ret = 4;
	} else {
		fprintf(stderr, "Failed to write open '%s'\n", argv[2]);
		ret = 3;
	}

	psl_free(psl);
#else
	if ((fpout = fopen(argv[2], "w"))) {
		fprintf(fpout, "static _psl_entry_t suffixes[1];\n");
		fprintf(fpout, "static _psl_entry_t suffix_exceptions[1];\n");
		fprintf(fpout, "static time_t _psl_file_time;\n");
		fprintf(fpout, "static time_t _psl_compile_time;\n");
		fprintf(fpout, "static const char _psl_sha1_checksum[] = \"\";\n");
		fprintf(fpout, "static const char _psl_filename[] = \"\";\n");

		if (fclose(fpout) != 0)
			ret = 4;
	} else {
		fprintf(stderr, "Failed to write open '%s'\n", argv[2]);
		ret = 3;
	}
#endif /* GENERATE_BUILTIN_DATA */

	return ret;
}
Beispiel #9
0
static void test_psl(void)
{
	/* punycode generation: idn ?? */
	/* octal code generation: echo -n "??" | od -b */
	static const struct test_data {
		const char
			*domain;
		int
			result;
	} test_data[] = {
		{ "www.example.com", 0 },
		{ "com.ar", 1 },
		{ "www.com.ar", 0 },
		{ "cc.ar.us", 1 },
		{ ".cc.ar.us", 1 },
		{ "www.cc.ar.us", 0 },
		{ "www.ck", 0 }, /* exception from *.ck */
		{ "abc.www.ck", 0 },
		{ "xxx.ck", 1 },
		{ "www.xxx.ck", 0 },
		{ "\345\225\206\346\240\207", 1 }, /* xn--czr694b or ?? */
		{ "www.\345\225\206\346\240\207", 0 },
		/* some special test follow ('name' and 'forgot.his.name' are public, but e.g. his.name is not) */
		{ "name", 1 },
		{ ".name", 1 },
		{ "his.name", 0 },
		{ ".his.name", 0 },
		{ "forgot.his.name", 1 },
		{ ".forgot.his.name", 1 },
		{ "whoever.his.name", 0 },
		{ "whoever.forgot.his.name", 0 },
		{ ".", 1 }, /* special case */
		{ "", 1 },  /* special case */
		{ NULL, 1 },  /* special case */
		{ "adfhoweirh", 1 }, /* unknown TLD */
	};
	unsigned it;
	int result, ver;
	psl_ctx_t *psl;

	psl = psl_load_file(PSL_FILE);

	printf("loaded %d suffixes and %d exceptions\n", psl_suffix_count(psl), psl_suffix_exception_count(psl));

	for (it = 0; it < countof(test_data); it++) {
		const struct test_data *t = &test_data[it];
		result = psl_is_public_suffix(psl, t->domain);

		if (result == t->result) {
			ok++;
		} else {
			failed++;
			printf("psl_is_public_suffix(%s)=%d (expected %d)\n", t->domain, result, t->result);
		}
	}

	/* do some checks to cover more code paths in libpsl */
	psl_is_public_suffix(NULL, "xxx");

	if ((ver = psl_check_version_number(0)) == 0) {
		printf("psl_check_version_number(0) is 0\n");
		failed++;
	} else {
		if (((result = psl_check_version_number(ver)) != ver)) {
			printf("psl_check_version_number(%06X) is %06X\n", ver, result);
			failed++;
		}

		if (((result = psl_check_version_number(ver - 1)) != 0)) {
			printf("psl_check_version_number(%06X) is %06X\n", ver - 1, result);
			failed++;
		}

		if (((result = psl_check_version_number(ver + 1)) != ver)) {
			printf("psl_check_version_number(%06X) is %06X\n", ver, result);
			failed++;
		}
	}

	psl_str_to_utf8lower("www.example.com", "utf-8", "en", NULL);
	psl_str_to_utf8lower(NULL, "utf-8", "en", NULL);

	{
		char *lower = NULL;

		psl_str_to_utf8lower("www.example.com", NULL, "de", &lower);
		free(lower); lower = NULL;

		psl_str_to_utf8lower("\374bel.de", NULL, "de", &lower);
		free(lower); lower = NULL;

		psl_str_to_utf8lower("\374bel.de", "iso-8859-1", NULL, &lower);
		free(lower); lower = NULL;

		psl_str_to_utf8lower(NULL, "utf-8", "en", &lower);
		free(lower); lower = NULL;
	}

	psl_get_version();
	psl_dist_filename();
	psl_builtin_filename();
	psl_builtin_outdated();
	psl_builtin_file_time();
	psl_builtin_sha1sum();
	psl_suffix_wildcard_count(NULL);
	psl_suffix_wildcard_count(psl);
	psl_suffix_wildcard_count(psl_builtin());
	psl_suffix_count(NULL);
	psl_suffix_exception_count(NULL);
	psl_load_file(NULL);
	psl_load_fp(NULL);
	psl_registrable_domain(NULL, "");
	psl_registrable_domain(psl, NULL);
	psl_registrable_domain(psl, "www.example.com");
	psl_unregistrable_domain(NULL, "");
	psl_unregistrable_domain(psl, NULL);
	psl_is_public_suffix2(NULL, "", PSL_TYPE_ANY);
	psl_is_public_suffix2(psl, NULL, PSL_TYPE_ANY);

	psl_free(psl);
}