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