• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright(c) 2014-2018 Tim Ruehsen
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  *
22  * This file is part of the test suite of libpsl.
23  *
24  * Test psl_is_public_suffix() for all entries in public_suffix_list.dat
25  *
26  * Changelog
27  * 19.03.2014  Tim Ruehsen  created
28  *
29  */
30 
31 #if HAVE_CONFIG_H
32 # include <config.h>
33 #endif
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39 #ifdef HAVE_ALLOCA_H
40 #	include <alloca.h>
41 #endif
42 
43 #include <libpsl.h>
44 
45 static int
46 	ok,
47 	failed;
48 #ifdef HAVE_CLOCK_GETTIME
49 	static struct timespec ts1, ts2;
50 #endif
51 
_isspace_ascii(const char c)52 static int _isspace_ascii(const char c)
53 {
54 	return c == ' ' || c == '\t' || c == '\r' || c == '\n';
55 }
56 
_type_string(int type)57 static const char *_type_string(int type)
58 {
59 	switch (type) {
60 	case PSL_TYPE_ANY: return "PSL_TYPE_ANY";
61 	case PSL_TYPE_PRIVATE: return "PSL_TYPE_PRIVATE";
62 	case PSL_TYPE_ICANN: return "PSL_TYPE_ICANN";
63 	case PSL_TYPE_ANY|PSL_TYPE_NO_STAR_RULE: return "PSL_TYPE_ANY|PSL_TYPE_NO_STAR_RULE";
64 	case PSL_TYPE_PRIVATE|PSL_TYPE_NO_STAR_RULE: return "PSL_TYPE_PRIVATE|PSL_TYPE_NO_STAR_RULE";
65 	case PSL_TYPE_ICANN|PSL_TYPE_NO_STAR_RULE: return "PSL_TYPE_ICANN|PSL_TYPE_NO_STAR_RULE";
66 	default: return "Unsupported type";
67 	}
68 }
69 
test_ps(const psl_ctx_t * psl,const char * domain,int type,int expected)70 static void test_ps(const psl_ctx_t *psl, const char *domain, int type, int expected)
71 {
72 	int result;
73 
74 	if ((result = psl_is_public_suffix2(psl, domain, type)) != expected) {
75 		failed++;
76 		printf("psl_is_public_suffix2(%s, %s)=%d (expected %d)\n", domain, _type_string(type), result, expected);
77 	} else ok++;
78 }
79 
80 /* section: either PSL_TYPE_PRIVATE or PSL_TYPE_ICANN */
test_type_any(const psl_ctx_t * psl,const char * domain,int type,int expected)81 static void test_type_any(const psl_ctx_t *psl, const char *domain, int type, int expected)
82 {
83 	int wildcard = (*domain == '.');
84 	int tld = !(strchr(domain + wildcard, '.'));
85 
86 	test_ps(psl, domain, type, expected);
87 	test_ps(psl, domain, type|PSL_TYPE_NO_STAR_RULE, expected);
88 	test_ps(psl, domain, PSL_TYPE_ANY, expected);
89 	test_ps(psl, domain, PSL_TYPE_ANY|PSL_TYPE_NO_STAR_RULE, expected);
90 
91 	if (type == PSL_TYPE_PRIVATE) {
92 		if (tld) {
93 			test_ps(psl, domain, PSL_TYPE_ICANN, 1);
94 			test_ps(psl, domain, PSL_TYPE_ICANN|PSL_TYPE_NO_STAR_RULE, 0);
95 		} else {
96 			test_ps(psl, domain, PSL_TYPE_ICANN, 0);
97 			test_ps(psl, domain, PSL_TYPE_ICANN|PSL_TYPE_NO_STAR_RULE, 0);
98 		}
99 	} else if (type == PSL_TYPE_ICANN) {
100 		if (tld) {
101 			test_ps(psl, domain, PSL_TYPE_PRIVATE, 1);
102 			test_ps(psl, domain, PSL_TYPE_PRIVATE|PSL_TYPE_NO_STAR_RULE, 0);
103 		} else {
104 			test_ps(psl, domain, PSL_TYPE_PRIVATE, 0);
105 			test_ps(psl, domain, PSL_TYPE_PRIVATE|PSL_TYPE_NO_STAR_RULE, 0);
106 		}
107 	}
108 }
109 
test_psl_entry(const psl_ctx_t * psl,const char * domain,int type)110 static void test_psl_entry(const psl_ctx_t *psl, const char *domain, int type)
111 {
112 	if (*domain == '!') { /* an exception to a wildcard, e.g. !www.ck (wildcard is *.ck) */
113 		test_type_any(psl, domain + 1, type, 0); /* the exception itself is not a PS */
114 
115 		if ((domain = strchr(domain, '.')))
116 			test_type_any(psl, domain, type, 1); /* the related wildcard domain is a PS */
117 
118 	} else if (*domain == '*') { /* a wildcard, e.g. *.ck or *.platform.sh */
119 		/* '*.platform.sh' -> 'y.x.platform.sh' */
120 		size_t len = strlen(domain);
121 		char *xdomain = alloca(len + 3);
122 
123 		memcpy(xdomain, "y.x", 3);
124 		memcpy(xdomain + 3, domain + 1, len);
125 
126 		test_type_any(psl, domain + 1, type, 1); /* the domain without wildcard is a PS */
127 		test_type_any(psl, xdomain + 2, type, 1); /* random wildcard-matching domain is a PS... */
128 		test_type_any(psl, xdomain, type, 0); /* ... but sub domain is not */
129 
130 	} else {
131 		test_type_any(psl, domain, type, 1); /* Any normal PSL entry */
132 	}
133 }
134 
test_psl(void)135 static void test_psl(void)
136 {
137 	FILE *fp;
138 	psl_ctx_t *psl, *psl3, *psl4, *psl5;
139 	const psl_ctx_t *psl2;
140 	int type = 0;
141 	char buf[256], *linep, *p;
142 
143 	psl = psl_load_file(PSL_FILE); /* PSL_FILE can be set by ./configure --with-psl-file=[PATH] */
144 	printf("loaded %d suffixes and %d exceptions\n", psl_suffix_count(psl), psl_suffix_exception_count(psl));
145 
146 	psl2 = psl_builtin();
147 	printf("builtin PSL has %d suffixes and %d exceptions\n", psl_suffix_count(psl2), psl_suffix_exception_count(psl2));
148 
149 	if (!(psl3 = psl_load_file(PSL_DAFSA))) {
150 		fprintf(stderr, "Failed to load 'psl.dafsa'\n");
151 		failed++;
152 	}
153 
154 	if (!(psl4 = psl_load_file(PSL_ASCII_DAFSA))) {
155 		fprintf(stderr, "Failed to load 'psl_ascii.dafsa'\n");
156 		failed++;
157 	}
158 
159 	psl5 = psl_latest("psl.dafsa");
160 
161 	if ((fp = fopen(PSL_FILE, "r"))) {
162 #ifdef HAVE_CLOCK_GETTIME
163 		clock_gettime(CLOCK_REALTIME, &ts1);
164 #endif
165 
166 		while ((linep = fgets(buf, sizeof(buf), fp))) {
167 			while (_isspace_ascii(*linep)) linep++; /* ignore leading whitespace */
168 			if (!*linep) continue; /* skip empty lines */
169 
170 			if (*linep == '/' && linep[1] == '/') {
171 				if (!type) {
172 					if (strstr(linep + 2, "===BEGIN ICANN DOMAINS==="))
173 						type = PSL_TYPE_ICANN;
174 					else if (!type && strstr(linep + 2, "===BEGIN PRIVATE DOMAINS==="))
175 						type = PSL_TYPE_PRIVATE;
176 				}
177 				else if (type == PSL_TYPE_ICANN && strstr(linep + 2, "===END ICANN DOMAINS==="))
178 					type = 0;
179 				else if (type == PSL_TYPE_PRIVATE && strstr(linep + 2, "===END PRIVATE DOMAINS==="))
180 					type = 0;
181 
182 				continue; /* skip comments */
183 			}
184 
185 			/* parse suffix rule */
186 			for (p = linep; *linep && !_isspace_ascii(*linep);) linep++;
187 			*linep = 0;
188 
189 			test_psl_entry(psl, p, type);
190 
191 			if (psl2)
192 				test_psl_entry(psl2, p, type);
193 
194 			if (psl3)
195 				test_psl_entry(psl3, p, type);
196 
197 			if (psl4)
198 				test_psl_entry(psl4, p, type);
199 
200 			if (psl5)
201 				test_psl_entry(psl5, p, type);
202 		}
203 
204 #ifdef HAVE_CLOCK_GETTIME
205 		clock_gettime(CLOCK_REALTIME, &ts2);
206 #endif
207 		fclose(fp);
208 	} else {
209 		printf("Failed to open %s\n", PSL_FILE);
210 		failed++;
211 	}
212 
213 	psl_free(psl5);
214 	psl_free(psl4);
215 	psl_free(psl3);
216 	psl_free((psl_ctx_t *)psl2);
217 	psl_free(psl);
218 }
219 
main(int argc,const char * const * argv)220 int main(int argc, const char * const *argv)
221 {
222 #ifdef HAVE_CLOCK_GETTIME
223 	long ns;
224 #endif
225 
226 	/* if VALGRIND testing is enabled, we have to call ourselves with valgrind checking */
227 	if (argc == 1) {
228 		const char *valgrind = getenv("TESTS_VALGRIND");
229 
230 		if (valgrind && *valgrind) {
231 			size_t cmdsize = strlen(valgrind) + strlen(argv[0]) + 32;
232 			char *cmd = alloca(cmdsize);
233 
234 			snprintf(cmd, cmdsize, "TESTS_VALGRIND="" %s %s", valgrind, argv[0]);
235 			return system(cmd) != 0;
236 		}
237 	}
238 
239 	test_psl();
240 
241 	if (failed) {
242 		printf("Summary: %d out of %d tests failed\n", failed, ok + failed);
243 		return 1;
244 	}
245 
246 #ifdef HAVE_CLOCK_GETTIME
247 	if (ts1.tv_sec == ts2.tv_sec)
248 		ns = ts2.tv_nsec - ts1.tv_nsec;
249 	else if (ts1.tv_sec == ts2.tv_sec - 1)
250 		ns = 1000000000L - (ts2.tv_nsec - ts1.tv_nsec);
251 	else
252 		ns = 0; /* let's assume something is wrong and skip outputting measured time */
253 
254 	if (ns)
255 		printf("Summary: All %d tests passed in %ld.%06ld ms\n", ok, ns / 1000000, ns % 1000000000);
256 	else
257 		printf("Summary: All %d tests passed\n", ok);
258 #else
259 	printf("Summary: All %d tests passed\n", ok);
260 #endif
261 
262 	return 0;
263 }
264