Coverage Report

Created: 2026-08-28 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libpsl/src/psl.c
Line
Count
Source
1
/*
2
 * SPDX-License-Identifier: MIT
3
 *
4
 * See the LICENSE file in the root directory for details and copyrights.
5
 *
6
 * This file is part of libpsl.
7
 *
8
 * Public Suffix List routines
9
 *
10
 */
11
12
#if HAVE_CONFIG_H
13
# include <config.h>
14
#endif
15
16
#if defined(__GNUC__) && defined(__GNUC_MINOR__)
17
#       define GCC_VERSION_AT_LEAST(major, minor) ((__GNUC__ > (major)) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))
18
#else
19
#       define GCC_VERSION_AT_LEAST(major, minor) 0
20
#endif
21
22
/* Must be defined before <sys/stat.h> */
23
#if defined(_MSC_VER) || defined(__MINGW32__)
24
# define USE_WIN32_LARGE_FILES
25
# ifdef __MINGW32__
26
#   ifndef _FILE_OFFSET_BITS
27
#    define _FILE_OFFSET_BITS 64
28
#   endif
29
# endif
30
#endif
31
32
#include <sys/types.h>
33
#include <sys/stat.h>
34
35
#if defined(_WIN32) && (defined(WITH_LIBIDN2) || defined(WITH_LIBIDN))
36
# ifndef WIN32_LEAN_AND_MEAN
37
# define WIN32_LEAN_AND_MEAN
38
# endif
39
# include <windows.h> /* for GetACP() */
40
#endif
41
42
#if defined(_WIN32)
43
# ifdef USE_WIN32_LARGE_FILES
44
#  define struct_stat  struct _stati64
45
#  define func_sys_stat _stati64
46
# else
47
#  define struct_stat  struct _stat
48
#  define func_sys_stat _stat
49
# endif
50
#endif
51
52
#ifndef struct_stat
53
0
# define struct_stat   struct stat
54
0
# define func_sys_stat stat
55
#endif
56
57
#if defined(_MSC_VER) && ! defined(ssize_t)
58
# include <basetsd.h>
59
typedef SSIZE_T ssize_t;
60
#endif
61
62
#include <stdio.h>
63
#include <stdlib.h>
64
#include <string.h>
65
#include <ctype.h>
66
#include <time.h>
67
#include <errno.h>
68
#include <limits.h> /* for UINT_MAX */
69
70
#ifdef HAVE_NL_LANGINFO
71
# include <langinfo.h>
72
#endif
73
74
#ifdef _WIN32
75
# include <malloc.h>
76
#endif
77
78
#ifdef WITH_LIBICU
79
# include <unicode/uversion.h>
80
# include <unicode/ustring.h>
81
# include <unicode/uidna.h>
82
# include <unicode/ucnv.h>
83
#elif defined(WITH_LIBICUCORE)
84
# include <iconv.h>
85
# include <unicode/uversion.h>
86
# include <unicode/ustring.h>
87
# include <unicode/uidna.h>
88
#elif defined(WITH_LIBICU_WIN)
89
# include <icu.h>
90
#elif defined(WITH_LIBIDN2)
91
# include <iconv.h>
92
# include <idn2.h>
93
# include <unicase.h>
94
# include <unistr.h>
95
#elif defined(WITH_LIBIDN)
96
# include <iconv.h>
97
# include <stringprep.h>
98
# include <idna.h>
99
# include <unicase.h>
100
# include <unistr.h>
101
#endif
102
103
#ifdef WINICONV_CONST
104
#  define ICONV_CONST WINICONV_CONST
105
#endif
106
#ifndef ICONV_CONST
107
#  define ICONV_CONST
108
#endif
109
110
111
#include <libpsl.h>
112
113
/**
114
 * SECTION:libpsl
115
 * @short_description: Public Suffix List library functions
116
 * @title: libpsl
117
 * @stability: Stable
118
 * @include: libpsl.h
119
 *
120
 * [Public Suffix List](https://publicsuffix.org/) library functions.
121
 *
122
 */
123
124
#define countof(a) (sizeof(a)/sizeof(*(a)))
125
126
848
#define PRIV_PSL_FLAG_EXCEPTION (1<<0)
127
496
#define PRIV_PSL_FLAG_WILDCARD  (1<<1)
128
2.87k
#define PRIV_PSL_FLAG_ICANN     (1<<2) /* entry of ICANN section */
129
2.31k
#define PRIV_PSL_FLAG_PRIVATE   (1<<3) /* entry of PRIVATE section */
130
4.79M
#define PRIV_PSL_FLAG_PLAIN     (1<<4) /* just used for PSL syntax checking */
131
132
typedef struct {
133
  char
134
    label_buf[128];
135
  const char *
136
    label;
137
  unsigned short
138
    length;
139
  unsigned char
140
    nlabels, /* number of labels */
141
    flags;
142
} psl_entry_t;
143
144
/* stripped down version libmget vector routines */
145
typedef struct {
146
  int
147
    (*cmp)(const psl_entry_t **, const psl_entry_t **); /* comparison function */
148
  psl_entry_t
149
    **entry; /* pointer to array of pointers to elements */
150
  int
151
    max,     /* allocated elements */
152
    cur;     /* number of elements in use */
153
} psl_vector_t;
154
155
struct psl_ctx_st {
156
  psl_vector_t
157
    *suffixes;
158
  unsigned char
159
    *dafsa;
160
  size_t
161
    dafsa_size;
162
  int
163
    nsuffixes,
164
    nexceptions,
165
    nwildcards;
166
  unsigned
167
    utf8 : 1; /* 1: data contains UTF-8 + punycode encoded rules */
168
};
169
170
/* include the PSL data generated by psl-make-dafsa */
171
#ifdef ENABLE_BUILTIN
172
#include "suffixes_dafsa.h"
173
#else
174
static const unsigned char kDafsa[] = "";
175
static time_t _psl_file_time = 0;
176
static int _psl_nsuffixes = 0;
177
static int _psl_nexceptions = 0;
178
static int _psl_nwildcards = 0;
179
static const char _psl_sha1_checksum[] = "";
180
static const char _psl_filename[] = "";
181
#endif
182
183
/* references to these PSLs will result in lookups to built-in data */
184
static const psl_ctx_t
185
  builtin_psl;
186
187
#ifdef PSL_DISTFILE
188
static const char _psl_dist_filename[] = PSL_DISTFILE;
189
#else
190
static const char _psl_dist_filename[] = "";
191
#endif
192
193
static psl_vector_t *vector_alloc(int max, int (*cmp)(const psl_entry_t **, const psl_entry_t **))
194
2.74k
{
195
2.74k
  psl_vector_t *v;
196
197
2.74k
  if (!(v = calloc(1, sizeof(psl_vector_t))))
198
0
    return NULL;
199
200
2.74k
  if (!(v->entry = malloc(max * sizeof(psl_entry_t *)))) {
201
0
    free(v);
202
0
    return NULL;
203
0
  }
204
205
2.74k
  v->max = max;
206
2.74k
  v->cmp = cmp;
207
2.74k
  return v;
208
2.74k
}
209
210
static void vector_free(psl_vector_t **v)
211
6.01k
{
212
6.01k
  if (v && *v) {
213
2.74k
    if ((*v)->entry) {
214
2.74k
      int it;
215
216
7.92M
      for (it = 0; it < (*v)->cur; it++)
217
7.92M
        free((*v)->entry[it]);
218
219
2.74k
      free((*v)->entry);
220
2.74k
    }
221
2.74k
    free(*v);
222
2.74k
  }
223
6.01k
}
224
225
static psl_entry_t *vector_get(const psl_vector_t *v, int pos)
226
9.05M
{
227
9.05M
  if (pos < 0 || !v || pos >= v->cur) return NULL;
228
229
9.05M
  return v->entry[pos];
230
9.05M
}
231
232
/* the entries must be sorted by */
233
static int vector_find(const psl_vector_t *v, const psl_entry_t *elem)
234
4.79M
{
235
4.79M
  if (v) {
236
4.79M
    int l, r, m;
237
4.79M
    int res;
238
239
    /* binary search for element (exact match) */
240
68.7M
    for (l = 0, r = v->cur - 1; l <= r;) {
241
65.0M
      m = (l + r) / 2;
242
65.0M
      if ((res = v->cmp(&elem, (const psl_entry_t **)&(v->entry[m]))) > 0) l = m + 1;
243
47.3M
      else if (res < 0) r = m - 1;
244
1.12M
      else return m;
245
65.0M
    }
246
4.79M
  }
247
248
3.67M
  return -1; /* not found */
249
4.79M
}
250
251
static int vector_add(psl_vector_t *v, const psl_entry_t *elem)
252
7.92M
{
253
7.92M
  if (v) {
254
7.92M
    void *elemp;
255
256
7.92M
    if (!(elemp = malloc(sizeof(psl_entry_t))))
257
0
      return -1;
258
259
7.92M
    memcpy(elemp, elem, sizeof(psl_entry_t));
260
261
7.92M
    if (v->max == v->cur) {
262
206
      void *m = realloc(v->entry, (v->max *= 2) * sizeof(psl_entry_t *));
263
264
206
      if (m)
265
206
        v->entry = m;
266
0
      else {
267
0
        free(elemp);
268
0
        return -1;
269
0
      }
270
206
    }
271
272
7.92M
    v->entry[v->cur++] = elemp;
273
7.92M
    return v->cur - 1;
274
7.92M
  }
275
276
0
  return -1;
277
7.92M
}
278
279
static void vector_sort(psl_vector_t *v)
280
2.74k
{
281
2.74k
  if (v && v->cmp)
282
2.74k
    qsort(v->entry, v->cur, sizeof(psl_vector_t **), (int(*)(const void *, const void *))v->cmp);
283
2.74k
}
284
285
/* by this kind of sorting, we can easily see if a domain matches or not */
286
static int suffix_compare(const psl_entry_t *s1, const psl_entry_t *s2)
287
192M
{
288
192M
  int n;
289
290
192M
  if ((n = s2->nlabels - s1->nlabels))
291
2.20M
    return n; /* most labels first */
292
293
190M
  if ((n = s1->length - s2->length))
294
59.6M
    return n;  /* shorter rules first */
295
296
130M
  return strncmp(s1->label ? s1->label : s1->label_buf, s2->label ? s2->label : s2->label_buf, s1->length);
297
190M
}
298
299
/* needed to sort array of pointers, given to qsort() */
300
static int suffix_compare_array(const psl_entry_t **s1, const psl_entry_t **s2)
301
192M
{
302
192M
  return suffix_compare(*s1, *s2);
303
192M
}
304
305
static int suffix_init(psl_entry_t *suffix, const char *rule, size_t length)
306
9.04M
{
307
9.04M
  const char *src;
308
9.04M
  char *dst;
309
310
9.04M
  suffix->label = suffix->label_buf;
311
312
9.04M
  if (length >= sizeof(suffix->label_buf) - 1) {
313
432
    suffix->nlabels = 0;
314
    /* fprintf(stderr, "Suffix rule too long (%zd, ignored): %s\n", length, rule); */
315
432
    return -1;
316
432
  }
317
318
9.04M
  suffix->length = (unsigned char)length;
319
320
9.04M
  suffix->nlabels = 1;
321
322
48.3M
  for (dst = suffix->label_buf, src = rule; *src;) {
323
39.2M
    if (*src == '.')
324
249k
      suffix->nlabels++;
325
39.2M
    *dst++ = *src++;
326
39.2M
  }
327
9.04M
  *dst = 0;
328
329
9.04M
  return 0;
330
9.04M
}
331
332
static char *psl_strdup(const char *s)
333
0
{
334
0
  char *p = malloc(strlen(s) + 1);
335
0
  if (!p)
336
0
    return NULL;
337
0
  return strcpy(p, s);
338
0
}
339
340
#if !defined(WITH_LIBIDN) && !defined(WITH_LIBIDN2) && !defined(WITH_LIBICU) && !defined(WITH_LIBICUCORE) && !defined(WITH_LIBICU_WIN)
341
/*
342
 * When configured without runtime IDNA support (./configure --disable-runtime), we need a pure ASCII
343
 * representation of non-ASCII characters in labels as found in UTF-8 domain names.
344
 * This is because the current DAFSA format used may only hold character values [21..127].
345
 *
346
  Code copied from http://www.nicemice.net/idn/punycode-spec.gz on
347
  2011-01-04 with SHA-1 a966a8017f6be579d74a50a226accc7607c40133
348
  labeled punycode-spec 1.0.3 (2006-Mar-24-Thu).  It is modified for
349
  libpsl by Tim Rühsen.  License on the original code:
350
351
  punycode-spec 1.0.3 (2006-Mar-23-Thu)
352
  http://www.nicemice.net/idn/
353
  Adam M. Costello
354
  http://www.nicemice.net/amc/
355
356
  B. Disclaimer and license
357
358
    Regarding this entire document or any portion of it (including
359
    the pseudocode and C code), the author makes no guarantees and
360
    is not responsible for any damage resulting from its use.  The
361
    author grants irrevocable permission to anyone to use, modify,
362
    and distribute it in any way that does not diminish the rights
363
    of anyone else to use, modify, and distribute it, provided that
364
    redistributed derivative works do not contain misleading author or
365
    version information.  Derivative works need not be licensed under
366
    similar terms.
367
368
  C. Punycode sample implementation
369
370
  punycode-sample.c 2.0.0 (2004-Mar-21-Sun)
371
  http://www.nicemice.net/idn/
372
  Adam M. Costello
373
  http://www.nicemice.net/amc/
374
375
  This is ANSI C code (C89) implementing Punycode 1.0.x.
376
 */
377
enum punycode_status {
378
  punycode_success = 0,
379
  punycode_bad_input = 1, /* Input is invalid.                       */
380
  punycode_big_output = 2, /* Output would exceed the space provided. */
381
  punycode_overflow = 3 /* Wider integers needed to process input. */
382
};
383
384
#ifdef PUNYCODE_UINT
385
  typedef PUNYCODE_UINT punycode_uint;
386
#elif UINT_MAX >= (1 << 26) - 1
387
  typedef unsigned int punycode_uint;
388
#else
389
  typedef unsigned long punycode_uint;
390
#endif
391
392
/*** Bootstring parameters for Punycode ***/
393
enum {
394
  base = 36, tmin = 1, tmax = 26, skew = 38, damp = 700,
395
  initial_bias = 72, initial_n = 0x80, delimiter = 0x2D
396
};
397
398
static char encode_digit(punycode_uint d)
399
{
400
  return d + 22 + 75 * (d < 26);
401
  /*  0..25 map to ASCII a..z or A..Z */
402
  /* 26..35 map to ASCII 0..9         */
403
}
404
#define flagged(bcp) ((punycode_uint)(bcp) - 65 < 26)
405
static const punycode_uint maxint = -1;
406
407
static punycode_uint adapt(punycode_uint delta, punycode_uint numpoints, int firsttime)
408
{
409
  punycode_uint k;
410
411
  delta = firsttime ? delta / damp : delta >> 1;
412
  /* delta >> 1 is a faster way of doing delta / 2 */
413
  delta += delta / numpoints;
414
415
  for (k = 0; delta > ((base - tmin) * tmax) / 2; k += base) {
416
    delta /= base - tmin;
417
  }
418
419
  return k + (base - tmin + 1) * delta / (delta + skew);
420
}
421
422
static enum punycode_status punycode_encode(
423
  size_t input_length_orig,
424
  const punycode_uint input[],
425
  size_t *output_length,
426
  char output[])
427
{
428
  punycode_uint input_length, n, delta, h, b, bias, j, m, q, k, t;
429
  size_t out, max_out;
430
431
  /* The Punycode spec assumes that the input length is the same type */
432
  /* of integer as a code point, so we need to convert the size_t to  */
433
  /* a punycode_uint, which could overflow.                           */
434
435
  if (input_length_orig > maxint)
436
    return punycode_overflow;
437
438
  input_length = (punycode_uint) input_length_orig;
439
440
  /* Initialize the state: */
441
442
  n = initial_n;
443
  delta = 0;
444
  out = 0;
445
  max_out = *output_length;
446
  bias = initial_bias;
447
448
  /* Handle the basic code points: */
449
  for (j = 0; j < input_length; ++j) {
450
    if (input[j] < 0x80) {
451
      if (max_out - out < 2)
452
        return punycode_big_output;
453
      output[out++] = (char) input[j];
454
    }
455
    /* else if (input[j] < n) return punycode_bad_input; */
456
    /* (not needed for Punycode with unsigned code points) */
457
  }
458
459
  h = b = (punycode_uint) out;
460
  /* cannot overflow because out <= input_length <= maxint */
461
462
  /* h is the number of code points that have been handled, b is the  */
463
  /* number of basic code points, and out is the number of ASCII code */
464
  /* points that have been output.                                    */
465
466
  if (b > 0)
467
    output[out++] = delimiter;
468
469
  /* Main encoding loop: */
470
471
  while (h < input_length) {
472
    /* All non-basic code points < n have been     */
473
    /* handled already.  Find the next larger one: */
474
475
    for (m = maxint, j = 0; j < input_length; ++j) {
476
      /* if (basic(input[j])) continue; */
477
      /* (not needed for Punycode) */
478
      if (input[j] >= n && input[j] < m)
479
        m = input[j];
480
    }
481
482
    /* Increase delta enough to advance the decoder's    */
483
    /* <n,i> state to <m,0>, but guard against overflow: */
484
485
    if (m - n > (maxint - delta) / (h + 1))
486
      return punycode_overflow;
487
    delta += (m - n) * (h + 1);
488
    n = m;
489
490
    for (j = 0; j < input_length; ++j) {
491
      /* Punycode does not need to check whether input[j] is basic: */
492
      if (input[j] < n /* || basic(input[j]) */) {
493
        if (++delta == 0)
494
          return punycode_overflow;
495
      }
496
497
      if (input[j] == n) {
498
        /* Represent delta as a generalized variable-length integer: */
499
500
        for (q = delta, k = base;; k += base) {
501
          if (out >= max_out)
502
            return punycode_big_output;
503
          t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */
504
            k >= bias + tmax ? tmax : k - bias;
505
          if (q < t)
506
            break;
507
          output[out++] = encode_digit(t + (q - t) % (base - t));
508
          q = (q - t) / (base - t);
509
        }
510
511
        output[out++] = encode_digit(q);
512
        bias = adapt(delta, h + 1, h == b);
513
        delta = 0;
514
        ++h;
515
      }
516
    }
517
518
    ++delta, ++n;
519
  }
520
521
  *output_length = out;
522
  return punycode_success;
523
}
524
525
static ssize_t utf8_to_utf32(const char *in, size_t inlen, punycode_uint *out, size_t outlen)
526
{
527
  size_t n = 0;
528
  const unsigned char *s = (void *)in;
529
  const unsigned char *e = (void *)(in + inlen);
530
531
  if (!outlen)
532
    return -1;
533
534
  outlen--;
535
536
  while (n < outlen) {
537
    size_t inleft = e - s;
538
539
    if (inleft >= 1 && (*s & 0x80) == 0) { /* 0xxxxxxx ASCII char */
540
      out[n++] = *s;
541
      s++;
542
    } else if (inleft >= 2 && (*s & 0xE0) == 0xC0) /* 110xxxxx 10xxxxxx */ {
543
      if ((s[1] & 0xC0) != 0x80)
544
        return -1;
545
      out[n++] = ((*s & 0x1F) << 6) | (s[1] & 0x3F);
546
      s += 2;
547
    } else if (inleft >= 3 && (*s & 0xF0) == 0xE0) /* 1110xxxx 10xxxxxx 10xxxxxx */ {
548
      if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80)
549
        return -1;
550
      out[n++] = ((*s & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
551
      s += 3;
552
    } else if (inleft >= 4 && (*s & 0xF8) == 0xF0) /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ {
553
      if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80 || (s[3] & 0xC0) != 0x80)
554
        return -1;
555
      out[n++] = ((*s & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
556
      s += 4;
557
    } else if (!inleft) {
558
      break;
559
    } else
560
      return -1;
561
  }
562
563
  return n;
564
}
565
566
static int mem_is_ascii(const char *s, size_t n)
567
{
568
  for (; n; n--) /* 'while(n--)' generates unsigned integer overflow on n = 0 */
569
    if (*((unsigned char *)s++) >= 128)
570
      return 0;
571
572
  return 1;
573
}
574
575
static int domain_to_punycode(const char *domain, char *out, size_t outsize)
576
{
577
  size_t outlen = 0, labellen;
578
  punycode_uint input[256];
579
  const char *label, *e;
580
581
  for (e = label = domain; e;) {
582
    e = strchr(label, '.');
583
    labellen = e ? (size_t) (e - label) : strlen(label);
584
585
    if (mem_is_ascii(label, labellen)) {
586
      if (outlen + labellen + (e != NULL) >= outsize)
587
        return 1;
588
589
      memcpy(out + outlen, label, labellen);
590
      outlen += labellen;
591
    } else {
592
      ssize_t inputlen = 0;
593
594
      if (outlen + labellen + (e != NULL) + 4 >= outsize)
595
        return 1;
596
597
      if ((inputlen = utf8_to_utf32(label, labellen, input, countof(input))) < 0)
598
        return 1;
599
600
      memcpy(out + outlen, "xn--", 4);
601
      outlen += 4;
602
603
      labellen = outsize - outlen - (e != NULL) - 1; /* -1 to leave space for the trailing \0 */
604
      if (punycode_encode(inputlen, input, &labellen, out + outlen))
605
        return 1;
606
      outlen += labellen;
607
    }
608
609
    if (e) {
610
      label = e + 1;
611
      out[outlen++] = '.';
612
    }
613
    out[outlen] = 0;
614
  }
615
616
  return 0;
617
}
618
#endif
619
620
static int isspace_ascii(const char c)
621
19.7M
{
622
19.7M
  return c == ' ' || c == '\t' || c == '\r' || c == '\n';
623
19.7M
}
624
625
static int str_is_ascii(const char *s)
626
4.79M
{
627
5.02M
  while (*s && *((unsigned char *)s) < 128) s++;
628
629
4.79M
  return !*s;
630
4.79M
}
631
632
#if defined(WITH_LIBIDN)
633
/*
634
 * Work around a libidn <= 1.30 vulnerability.
635
 *
636
 * The function checks for a valid UTF-8 character sequence before
637
 * passing it to idna_to_ascii_8z().
638
 *
639
 * [1] https://lists.gnu.org/archive/html/help-libidn/2015-05/msg00002.html
640
 * [2] https://lists.gnu.org/archive/html/bug-wget/2015-06/msg00002.html
641
 * [3] https://curl.haxx.se/mail/lib-2015-06/0143.html
642
 */
643
static int utf8_is_valid(const char *utf8)
644
4.75M
{
645
4.75M
  const unsigned char *s = (const unsigned char *) utf8;
646
647
9.41M
  while (*s) {
648
5.13M
    if ((*s & 0x80) == 0) /* 0xxxxxxx ASCII char */
649
323k
      s++;
650
4.81M
    else if ((*s & 0xE0) == 0xC0) /* 110xxxxx 10xxxxxx */ {
651
4.36M
      if ((s[1] & 0xC0) != 0x80)
652
159k
        return 0;
653
4.20M
      s += 2;
654
4.20M
    } else if ((*s & 0xF0) == 0xE0) /* 1110xxxx 10xxxxxx 10xxxxxx */ {
655
135k
      if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80)
656
8.62k
        return 0;
657
126k
      s += 3;
658
312k
    } else if ((*s & 0xF8) == 0xF0) /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ {
659
31.5k
      if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80 || (s[3] & 0xC0) != 0x80)
660
26.7k
        return 0;
661
4.85k
      s += 4;
662
4.85k
    } else
663
280k
      return 0;
664
5.13M
  }
665
666
4.28M
  return 1;
667
4.75M
}
668
#endif
669
670
typedef void *psl_idna_t;
671
672
static psl_idna_t *psl_idna_open(void)
673
2.80k
{
674
#if defined(WITH_LIBICU) || defined(WITH_LIBICUCORE) || defined(WITH_LIBICU_WIN)
675
  UErrorCode status = 0;
676
  return (void *)uidna_openUTS46(UIDNA_USE_STD3_RULES | UIDNA_NONTRANSITIONAL_TO_ASCII, &status);
677
#endif
678
2.80k
  return NULL;
679
2.80k
}
680
681
static void psl_idna_close(psl_idna_t *idna)
682
2.80k
{
683
2.80k
  (void) idna;
684
685
#if defined(WITH_LIBICU) || defined(WITH_LIBICUCORE) || defined(WITH_LIBICU_WIN)
686
  if (idna)
687
    uidna_close((UIDNA *)idna);
688
#endif
689
2.80k
}
690
691
static int psl_idna_toASCII(psl_idna_t *idna, const char *utf8, char **ascii)
692
4.75M
{
693
4.75M
  int ret = -1;
694
695
#if defined(WITH_LIBICU) || defined(WITH_LIBICUCORE) || defined(WITH_LIBICU_WIN)
696
  (void) idna;
697
698
  /* IDNA2008 UTS#46 punycode conversion */
699
  if (idna) {
700
    char lookupname_buf[128] = "", *lookupname = lookupname_buf;
701
    UErrorCode status = 0;
702
    UIDNAInfo info = UIDNA_INFO_INITIALIZER;
703
    UChar utf16_dst[128], utf16_src_buf[128];
704
    UChar *utf16_src = utf16_src_buf;
705
    int32_t utf16_src_length, bytes_written;
706
    int32_t utf16_dst_length;
707
708
    u_strFromUTF8(utf16_src, countof(utf16_src_buf), &utf16_src_length, utf8, -1, &status);
709
    if (!U_SUCCESS(status)) goto cleanup; /* UTF-8 to UTF-16 conversion failed */
710
711
    if (utf16_src_length >= (int) countof(utf16_src_buf)) {
712
      utf16_src = malloc((utf16_src_length + 1) * sizeof(UChar));
713
      if (!utf16_src) goto cleanup;
714
715
      u_strFromUTF8(utf16_src, utf16_src_length, NULL, utf8, -1, &status);
716
      if (!U_SUCCESS(status)) goto cleanup; /* UTF-8 to UTF-16 conversion failed */
717
718
      utf16_src[utf16_src_length] = 0; /* u_strFromUTF8() doesn't 0-terminate if dest is filled up */
719
    }
720
721
    utf16_dst_length = uidna_nameToASCII((UIDNA *)idna, utf16_src, utf16_src_length, utf16_dst, countof(utf16_dst), &info, &status);
722
    if (!U_SUCCESS(status)) goto cleanup; /* to ASCII conversion failed */
723
724
    u_strToUTF8(lookupname, sizeof(lookupname_buf), &bytes_written, utf16_dst, utf16_dst_length, &status);
725
    if (!U_SUCCESS(status)) goto cleanup; /* UTF-16 to UTF-8 conversion failed */
726
727
    if (bytes_written >= (int) sizeof(lookupname_buf)) {
728
      lookupname = malloc(bytes_written + 1);
729
      if (!lookupname) goto cleanup;
730
731
      u_strToUTF8(lookupname, bytes_written, NULL, utf16_dst, utf16_dst_length, &status);
732
      if (!U_SUCCESS(status)) goto cleanup; /* UTF-16 to UTF-8 conversion failed */
733
734
      lookupname[bytes_written] = 0; /* u_strToUTF8() doesn't 0-terminate if dest is filled up */
735
    } else {
736
      if (!(lookupname = psl_strdup(lookupname)))
737
        goto cleanup;
738
    }
739
740
    if (ascii) {
741
      *ascii = lookupname;
742
      lookupname = NULL;
743
    }
744
745
    ret = 0;
746
747
cleanup:
748
    if (lookupname != lookupname_buf)
749
      free(lookupname);
750
    if (utf16_src != utf16_src_buf)
751
      free(utf16_src);
752
  }
753
#elif defined(WITH_LIBIDN2)
754
#if IDN2_VERSION_NUMBER >= 0x00140000
755
  int rc;
756
757
  (void) idna;
758
759
  /* IDN2_TRANSITIONAL automatically converts to lowercase
760
   * IDN2_NFC_INPUT converts to NFC before toASCII conversion
761
   * Since IDN2_TRANSITIONAL implicitly does NFC conversion, we don't need
762
   * the additional IDN2_NFC_INPUT. But just for the unlikely case that the linked
763
   * library is not matching the headers when building and it doesn't support TR46,
764
   * we provide IDN2_NFC_INPUT. */
765
766
  if ((rc = idn2_lookup_u8((uint8_t *)utf8, (uint8_t **)ascii, IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL)) == IDN2_OK)
767
    ret = 0;
768
  /* else
769
    fprintf(stderr, "toASCII(%s) failed (%d): %s\n", lower, rc, idn2_strerror(rc)); */
770
#else
771
  int rc;
772
  uint8_t *lower;
773
  size_t len = u8_strlen((uint8_t *)utf8) + 1;
774
775
  /* we need a conversion to lowercase */
776
  if (!(lower = u8_tolower((uint8_t *)utf8, len, 0, UNINORM_NFKC, NULL, &len))) {
777
    /* fprintf(stderr, "u8_tolower(%s) failed (%d)\n", utf8, errno); */
778
    return -1;
779
  }
780
781
  if ((rc = idn2_lookup_u8(lower, (uint8_t **)ascii, 0)) == IDN2_OK) {
782
    ret = 0;
783
  } /* else
784
    fprintf(stderr, "toASCII(%s) failed (%d): %s\n", lower, rc, idn2_strerror(rc)); */
785
786
  free(lower);
787
#endif
788
#elif defined(WITH_LIBIDN)
789
  int rc;
790
791
4.75M
  (void) idna;
792
793
4.75M
  if (!utf8_is_valid(utf8)) {
794
    /* fprintf(stderr, "Invalid UTF-8 sequence not converted: '%s'\n", utf8); */
795
475k
    return -1;
796
475k
  }
797
798
  /* idna_to_ascii_8z() automatically converts UTF-8 to lowercase */
799
800
4.28M
  if ((rc = idna_to_ascii_8z(utf8, ascii, IDNA_USE_STD3_ASCII_RULES)) == IDNA_SUCCESS) {
801
4.25M
    ret = 0;
802
4.25M
  } /* else
803
    fprintf(stderr, "toASCII failed (%d): %s\n", rc, idna_strerror(rc)); */
804
#else
805
  char lookupname[128];
806
807
  (void) idna;
808
809
  if (domain_to_punycode(utf8, lookupname, sizeof(lookupname)) == 0) {
810
    if (ascii)
811
      if ((*ascii = psl_strdup(lookupname)))
812
        ret = 0;
813
  }
814
#endif
815
816
4.28M
  return ret;
817
4.75M
}
818
819
static void add_punycode_if_needed(psl_idna_t *idna, psl_vector_t *v, psl_entry_t *e)
820
4.79M
{
821
4.79M
  char *lookupname;
822
823
4.79M
  if (str_is_ascii(e->label_buf))
824
34.7k
    return;
825
826
4.75M
  if (psl_idna_toASCII(idna, e->label_buf, &lookupname) == 0) {
827
4.25M
    if (strcmp(e->label_buf, lookupname)) {
828
4.25M
      psl_entry_t suffix, *suffixp;
829
830
      /* fprintf(stderr, "toASCII '%s' -> '%s'\n", e->label_buf, lookupname); */
831
4.25M
      if (suffix_init(&suffix, lookupname, strlen(lookupname)) == 0) {
832
4.25M
        suffix.flags = e->flags;
833
4.25M
        if ((suffixp = vector_get(v, vector_add(v, &suffix))))
834
4.25M
          suffixp->label = suffixp->label_buf; /* set label to changed address */
835
4.25M
      }
836
4.25M
    } /* else ignore */
837
838
4.25M
    free(lookupname);
839
4.25M
  }
840
4.75M
}
841
842
/* prototypes */
843
int LookupStringInFixedSet(const unsigned char* graph, size_t length, const char* key, size_t key_length);
844
int GetUtfMode(const unsigned char *graph, size_t length);
845
846
static int is_public_suffix(const psl_ctx_t *psl, const char *domain, int type)
847
2.97k
{
848
2.97k
  psl_entry_t suffix;
849
2.97k
  const char *p;
850
2.97k
  char *punycode = NULL;
851
2.97k
  size_t domain_len;
852
2.97k
  int need_conversion = 0;
853
854
  /* this function should be called without leading dots, just make sure */
855
2.97k
  if (*domain == '.')
856
2.97k
    domain++;
857
858
  /* a single leading dot needs to be handled here, so that e.g.,
859
   * co.uk and co.uk. are both detected as publicsuffix */
860
2.97k
  domain_len = strlen(domain);
861
2.97k
  if (domain_len > 0 && domain[domain_len - 1] == '.')
862
0
    domain_len--;
863
864
2.97k
  suffix.nlabels = 1;
865
866
20.8k
  for (p = domain; p < domain + domain_len; p++) {
867
17.8k
    if (*p == '.') {
868
2.97k
      if (suffix.nlabels == 255) /* weird input, avoid 8bit overflow */
869
0
        return 0;
870
2.97k
      suffix.nlabels++;
871
2.97k
    }
872
14.8k
    else if (*((unsigned char *)p) >= 128)
873
5.95k
      need_conversion = 1; /* in case domain is non-ascii we need a toASCII conversion */
874
17.8k
  }
875
876
2.97k
  if (suffix.nlabels == 1) {
877
    /* TLD, this is the prevailing '*' match. If type excludes the '*' rule, continue.
878
     */
879
0
    if (!(type & PSL_TYPE_NO_STAR_RULE))
880
0
      return 1;
881
0
  }
882
883
2.97k
  type &= ~PSL_TYPE_NO_STAR_RULE;
884
885
2.97k
  if (psl->utf8 || psl == &builtin_psl)
886
2.91k
    need_conversion = 0;
887
888
2.97k
  if (need_conversion) {
889
58
    psl_idna_t *idna = psl_idna_open();
890
891
58
    if (psl_idna_toASCII(idna, domain, &punycode) == 0) {
892
58
      suffix.label = punycode;
893
58
      suffix.length = strlen(punycode);
894
58
    } else {
895
      /* fallback */
896
897
0
      suffix.label = domain;
898
0
      suffix.length = domain_len;
899
0
    }
900
901
58
    psl_idna_close(idna);
902
2.91k
  } else {
903
2.91k
    suffix.label = domain;
904
2.91k
    suffix.length = domain_len;
905
2.91k
  }
906
907
2.97k
  if (psl == &builtin_psl || psl->dafsa) {
908
229
    size_t dafsa_size = psl == &builtin_psl ? sizeof(kDafsa) : psl->dafsa_size;
909
229
    const unsigned char *dafsa = psl == &builtin_psl ? kDafsa : psl->dafsa;
910
229
    int rc = LookupStringInFixedSet(dafsa, dafsa_size, suffix.label, suffix.length);
911
229
    if (rc != -1) {
912
      /* check for correct rule type */
913
7
      if (type == PSL_TYPE_ICANN && !(rc & PRIV_PSL_FLAG_ICANN))
914
0
        goto suffix_no;
915
7
      else if (type == PSL_TYPE_PRIVATE && !(rc & PRIV_PSL_FLAG_PRIVATE))
916
0
        goto suffix_no;
917
918
7
      if (rc & PRIV_PSL_FLAG_EXCEPTION)
919
3
        goto suffix_no;
920
921
      /* wildcard *.foo.bar implicitly make foo.bar a public suffix */
922
      /* definitely a match, no matter if the found rule is a wildcard or not */
923
4
      goto suffix_yes;
924
7
    }
925
222
    if ((suffix.label = strchr(suffix.label, '.'))) {
926
222
      suffix.label++;
927
222
      suffix.length = strlen(suffix.label);
928
222
      suffix.nlabels--;
929
930
222
      rc = LookupStringInFixedSet(dafsa, dafsa_size, suffix.label, suffix.length);
931
222
      if (rc != -1) {
932
        /* check for correct rule type */
933
7
        if (type == PSL_TYPE_ICANN && !(rc & PRIV_PSL_FLAG_ICANN))
934
0
          goto suffix_no;
935
7
        else if (type == PSL_TYPE_PRIVATE && !(rc & PRIV_PSL_FLAG_PRIVATE))
936
0
          goto suffix_no;
937
938
7
        if (rc & PRIV_PSL_FLAG_WILDCARD)
939
3
          goto suffix_yes;
940
7
      }
941
222
    }
942
2.74k
  } else {
943
2.74k
    psl_entry_t *rule = vector_get(psl->suffixes, 0);
944
945
2.74k
    if (!rule || rule->nlabels < suffix.nlabels - 1)
946
113
      goto suffix_no;
947
948
2.63k
    rule = vector_get(psl->suffixes, vector_find(psl->suffixes, &suffix));
949
950
2.63k
    if (rule) {
951
      /* check for correct rule type */
952
47
      if (type == PSL_TYPE_ICANN && !(rule->flags & PRIV_PSL_FLAG_ICANN))
953
0
        goto suffix_no;
954
47
      else if (type == PSL_TYPE_PRIVATE && !(rule->flags & PRIV_PSL_FLAG_PRIVATE))
955
0
        goto suffix_no;
956
957
47
      if (rule->flags & PRIV_PSL_FLAG_EXCEPTION)
958
1
        goto suffix_no;
959
960
      /* wildcard *.foo.bar implicitly make foo.bar a public suffix */
961
      /* definitely a match, no matter if the found rule is a wildcard or not */
962
46
      goto suffix_yes;
963
47
    }
964
965
2.58k
    if ((suffix.label = strchr(suffix.label, '.'))) {
966
2.58k
      suffix.label++;
967
2.58k
      suffix.length = strlen(suffix.label);
968
2.58k
      suffix.nlabels--;
969
970
2.58k
      rule = vector_get(psl->suffixes, vector_find(psl->suffixes, &suffix));
971
972
2.58k
      if (rule) {
973
        /* check for correct rule type */
974
14
        if (type == PSL_TYPE_ICANN && !(rule->flags & PRIV_PSL_FLAG_ICANN))
975
0
          goto suffix_no;
976
14
        else if (type == PSL_TYPE_PRIVATE && !(rule->flags & PRIV_PSL_FLAG_PRIVATE))
977
0
          goto suffix_no;
978
979
14
        if (rule->flags & PRIV_PSL_FLAG_WILDCARD)
980
1
          goto suffix_yes;
981
14
      }
982
2.58k
    }
983
2.58k
  }
984
985
2.92k
suffix_no:
986
2.92k
  if (punycode)
987
55
    free(punycode);
988
2.92k
  return 0;
989
990
54
suffix_yes:
991
54
  if (punycode)
992
3
    free(punycode);
993
54
  return 1;
994
2.97k
}
995
996
/**
997
 * psl_is_public_suffix:
998
 * @psl: PSL context
999
 * @domain: Domain string
1000
 *
1001
 * This function checks if @domain is a public suffix by the means of the
1002
 * [Mozilla Public Suffix List](https://publicsuffix.org).
1003
 *
1004
 * For cookie domain checking see psl_is_cookie_domain_acceptable().
1005
 *
1006
 * International @domain names have to be either in UTF-8 (lowercase + NFKC) or in ASCII/ACE format (punycode).
1007
 * Other encodings likely result in incorrect return values.
1008
 * Use helper function psl_str_to_utf8lower() for normalization @domain.
1009
 *
1010
 * @psl is a context returned by either psl_load_file(), psl_load_fp() or
1011
 * psl_builtin().
1012
 *
1013
 * Returns: 1 if domain is a public suffix, 0 if not.
1014
 *
1015
 * Since: 0.1
1016
 */
1017
int psl_is_public_suffix(const psl_ctx_t *psl, const char *domain)
1018
6.01k
{
1019
6.01k
  if (!psl || !domain)
1020
3.03k
    return 1;
1021
1022
2.97k
  return is_public_suffix(psl, domain, PSL_TYPE_ANY);
1023
6.01k
}
1024
1025
/**
1026
 * psl_is_public_suffix2:
1027
 * @psl: PSL context
1028
 * @domain: Domain string
1029
 * @type: Domain type
1030
 *
1031
 * This function checks if @domain is a public suffix by the means of the
1032
 * [Mozilla Public Suffix List](https://publicsuffix.org).
1033
 *
1034
 * @type specifies the PSL section where to perform the lookup. Valid values are
1035
 * %PSL_TYPE_PRIVATE, %PSL_TYPE_ICANN, %PSL_TYPE_NO_STAR_RULE, and %PSL_TYPE_ANY.
1036
 *
1037
 * %PSL_TYPE_NO_STAR_RULE switches of the 'prevailing star rule' (see
1038
 * [List](https://publicsuffix.org/list) under 'Algorithm' 2.).
1039
 * Applying the flag means that TLDs not explicitly listed in the PSL are *not* treated as public suffixes.
1040
 *
1041
 * International @domain names have to be either in UTF-8 (lowercase + NFKC) or in ASCII/ACE format (punycode).
1042
 * Other encodings likely result in incorrect return values.
1043
 * Use helper function psl_str_to_utf8lower() for normalization @domain.
1044
 *
1045
 * @psl is a context returned by either psl_load_file(), psl_load_fp() or
1046
 * psl_builtin().
1047
 *
1048
 * Returns: 1 if domain is a public suffix, 0 if not.
1049
 *
1050
 * Since: 0.1
1051
 */
1052
int psl_is_public_suffix2(const psl_ctx_t *psl, const char *domain, int type)
1053
0
{
1054
0
  if (!psl || !domain)
1055
0
    return 1;
1056
1057
0
  return is_public_suffix(psl, domain, type);
1058
0
}
1059
1060
/**
1061
 * psl_unregistrable_domain:
1062
 * @psl: PSL context
1063
 * @domain: Domain string
1064
 *
1065
 * This function finds the longest public suffix part of @domain by the means
1066
 * of the [Mozilla Public Suffix List](https://publicsuffix.org).
1067
 *
1068
 * International @domain names have to be either in UTF-8 (lowercase + NFKC) or in ASCII/ACE format (punycode).
1069
 * Other encodings likely result in incorrect return values.
1070
 * Use helper function psl_str_to_utf8lower() for normalization @domain.
1071
 *
1072
 * @psl is a context returned by either psl_load_file(), psl_load_fp() or
1073
 * psl_builtin().
1074
 *
1075
 * Returns: Pointer to longest public suffix part of @domain or %NULL if @domain
1076
 * does not contain a public suffix (or if @psl is %NULL).
1077
 *
1078
 * Since: 0.1
1079
 */
1080
const char *psl_unregistrable_domain(const psl_ctx_t *psl, const char *domain)
1081
0
{
1082
0
  int nlabels = 0;
1083
0
  const char *p;
1084
1085
0
  if (!psl || !domain)
1086
0
    return NULL;
1087
1088
  /*
1089
   * In the main loop we introduce a O(N^2) behavior to avoid code duplication.
1090
   * To avoid nasty CPU hogging, we limit the lookup to max. 8 domain labels to the right.
1091
   */
1092
0
  for (p = domain + strlen(domain) - 1; p >= domain; p--) {
1093
0
    if (*p == '.' && ++nlabels > 8) {
1094
0
      domain = p + 1;
1095
0
      break;
1096
0
    }
1097
0
  }
1098
1099
  /*
1100
   *  We check from left to right to catch special PSL entries like 'forgot.his.name':
1101
   *   'forgot.his.name' and 'name' are in the PSL while 'his.name' is not.
1102
   */
1103
1104
0
  while (!is_public_suffix(psl, domain, 0)) {
1105
0
    if ((domain = strchr(domain, '.')))
1106
0
      domain++;
1107
0
    else
1108
0
      break; /* prevent endless loop if is_public_suffix() is broken. */
1109
0
  }
1110
1111
0
  return domain;
1112
0
}
1113
1114
/**
1115
 * psl_registrable_domain:
1116
 * @psl: PSL context
1117
 * @domain: Domain string
1118
 *
1119
 * This function finds the shortest private suffix part of @domain by the means
1120
 * of the [Mozilla Public Suffix List](https://publicsuffix.org).
1121
 *
1122
 * International @domain names have to be either in UTF-8 (lowercase + NFKC) or in ASCII/ACE format (punycode).
1123
 * Other encodings likely result in incorrect return values.
1124
 * Use helper function psl_str_to_utf8lower() for normalization @domain.
1125
 *
1126
 * @psl is a context returned by either psl_load_file(), psl_load_fp() or
1127
 * psl_builtin().
1128
 *
1129
 * Returns: Pointer to shortest private suffix part of @domain or %NULL if @domain
1130
 * does not contain a private suffix (or if @psl is %NULL).
1131
 *
1132
 * Since: 0.1
1133
 */
1134
const char *psl_registrable_domain(const psl_ctx_t *psl, const char *domain)
1135
0
{
1136
0
  const char *p, *regdom = NULL;
1137
0
  int nlabels = 0;
1138
1139
0
  if (!psl || !domain || *domain == '.')
1140
0
    return NULL;
1141
1142
  /*
1143
   * In the main loop we introduce a O(N^2) behavior to avoid code duplication.
1144
   * To avoid nasty CPU hogging, we limit the lookup to max. 8 domain labels to the right.
1145
   */
1146
0
  for (p = domain + strlen(domain) - 1; p >= domain; p--) {
1147
0
    if (*p == '.' && ++nlabels > 8) {
1148
0
      domain = p + 1;
1149
0
      break;
1150
0
    }
1151
0
  }
1152
1153
  /*
1154
   *  We check from left to right to catch special PSL entries like 'forgot.his.name':
1155
   *   'forgot.his.name' and 'name' are in the PSL while 'his.name' is not.
1156
   */
1157
1158
0
  while (!is_public_suffix(psl, domain, 0)) {
1159
0
    if ((p = strchr(domain, '.'))) {
1160
0
      regdom = domain;
1161
0
      domain = p + 1;
1162
0
    } else
1163
0
      break; /* prevent endless loop if is_public_suffix() is broken. */
1164
0
  }
1165
1166
0
  return regdom;
1167
0
}
1168
1169
/**
1170
 * psl_load_file:
1171
 * @fname: Name of PSL file
1172
 *
1173
 * This function loads the public suffixes file named @fname.
1174
 * To free the allocated resources, call psl_free().
1175
 *
1176
 * The suffixes are expected to be UTF-8 encoded (lowercase + NFKC) if they are international.
1177
 *
1178
 * Returns: Pointer to a PSL context or %NULL on failure.
1179
 *
1180
 * Since: 0.1
1181
 */
1182
psl_ctx_t *psl_load_file(const char *fname)
1183
3.00k
{
1184
3.00k
  FILE *fp;
1185
3.00k
  psl_ctx_t *psl = NULL;
1186
1187
3.00k
  if (!fname)
1188
0
    return NULL;
1189
1190
3.00k
  if ((fp = fopen(fname, "rb"))) {
1191
3.00k
    psl = psl_load_fp(fp);
1192
3.00k
    fclose(fp);
1193
3.00k
  }
1194
1195
3.00k
  return psl;
1196
3.00k
}
1197
1198
/**
1199
 * psl_load_fp:
1200
 * @fp: %FILE pointer
1201
 *
1202
 * This function loads the public suffixes from a %FILE pointer.
1203
 * To free the allocated resources, call psl_free().
1204
 *
1205
 * The suffixes are expected to be UTF-8 encoded (lowercase + NFKC) if they are international.
1206
 *
1207
 * Returns: Pointer to a PSL context or %NULL on failure.
1208
 *
1209
 * Since: 0.1
1210
 */
1211
psl_ctx_t *psl_load_fp(FILE *fp)
1212
6.01k
{
1213
6.01k
  psl_ctx_t *psl;
1214
6.01k
  psl_entry_t suffix, *suffixp;
1215
6.01k
  char buf[256], *linep, *p;
1216
6.01k
  int type = 0, is_dafsa;
1217
6.01k
  psl_idna_t *idna;
1218
1219
6.01k
  if (!fp)
1220
0
    return NULL;
1221
1222
6.01k
  if (!(psl = calloc(1, sizeof(psl_ctx_t))))
1223
0
    return NULL;
1224
1225
  /* read first line to allow ASCII / DAFSA detection */
1226
6.01k
  if (!(linep = fgets(buf, sizeof(buf) - 1, fp)))
1227
3.00k
    goto fail;
1228
1229
3.00k
  is_dafsa = strlen(buf) == 16 && !strncmp(buf, ".DAFSA@PSL_", 11);
1230
1231
3.00k
  if (is_dafsa) {
1232
258
    void *m;
1233
258
    size_t size = 65536, n, len = 0;
1234
258
    int version = atoi(buf + 11);
1235
1236
258
    if (version != 0)
1237
28
      goto fail;
1238
1239
230
    if (!(psl->dafsa = malloc(size)))
1240
0
      goto fail;
1241
1242
230
    memcpy(psl->dafsa, buf, len);
1243
1244
486
    while ((n = fread(psl->dafsa + len, 1, size - len, fp)) > 0) {
1245
256
      len += n;
1246
256
      if (len >= size) {
1247
31
        if (!(m = realloc(psl->dafsa, size *= 2)))
1248
0
          goto fail;
1249
31
        psl->dafsa = m;
1250
31
      }
1251
256
    }
1252
1253
    /* release unused memory */
1254
230
    if ((m = realloc(psl->dafsa, len)))
1255
229
      psl->dafsa = m;
1256
1
    else if (!len)
1257
1
      psl->dafsa = NULL; /* realloc() just free'd psl->dafsa */
1258
1259
230
    psl->dafsa_size = len;
1260
230
    psl->utf8 = !!GetUtfMode(psl->dafsa, len);
1261
1262
230
    return psl;
1263
230
  }
1264
1265
2.74k
  idna = psl_idna_open();
1266
1267
  /*
1268
   *  as of 02.11.2012, the list at https://publicsuffix.org/list/ contains ~6000 rules and 40 exceptions.
1269
   *  as of 19.02.2014, the list at https://publicsuffix.org/list/ contains ~6500 rules and 19 exceptions.
1270
   *  as of 07.10.2018, the list at https://publicsuffix.org/list/ contains ~8600 rules and 8 exceptions.
1271
   */
1272
2.74k
  psl->suffixes = vector_alloc(8*1024, suffix_compare_array);
1273
2.74k
  psl->utf8 = 1; /* we put UTF-8 and punycode rules in the lookup vector */
1274
1275
4.86M
  do {
1276
4.93M
    while (isspace_ascii(*linep)) linep++; /* ignore leading whitespace */
1277
4.86M
    if (!*linep) continue; /* skip empty lines */
1278
1279
4.79M
    if (*linep == '/' && linep[1] == '/') {
1280
2.22k
      if (!type) {
1281
941
        if (strstr(linep + 2, "===BEGIN ICANN DOMAINS==="))
1282
305
          type = PRIV_PSL_FLAG_ICANN;
1283
636
        else if (!type && strstr(linep + 2, "===BEGIN PRIVATE DOMAINS==="))
1284
290
          type = PRIV_PSL_FLAG_PRIVATE;
1285
941
      }
1286
1.28k
      else if (type == PRIV_PSL_FLAG_ICANN && strstr(linep + 2, "===END ICANN DOMAINS==="))
1287
273
        type = 0;
1288
1.01k
      else if (type == PRIV_PSL_FLAG_PRIVATE && strstr(linep + 2, "===END PRIVATE DOMAINS==="))
1289
260
        type = 0;
1290
1291
2.22k
      continue; /* skip comments */
1292
2.22k
    }
1293
1294
    /* parse suffix rule */
1295
14.7M
    for (p = linep; *linep && !isspace_ascii(*linep);) linep++;
1296
4.79M
    *linep = 0;
1297
1298
4.79M
    if (*p == '!') {
1299
794
      p++;
1300
794
      suffix.flags = PRIV_PSL_FLAG_EXCEPTION | type;
1301
794
      psl->nexceptions++;
1302
4.79M
    } else if (*p == '*') {
1303
730
      if (*++p != '.') {
1304
        /* fprintf(stderr, "Unsupported kind of rule (ignored): %s\n", p - 1); */
1305
255
        continue;
1306
255
      }
1307
475
      p++;
1308
      /* wildcard *.foo.bar implicitly make foo.bar a public suffix */
1309
475
      suffix.flags = PRIV_PSL_FLAG_WILDCARD | PRIV_PSL_FLAG_PLAIN | type;
1310
475
      psl->nwildcards++;
1311
475
      psl->nsuffixes++;
1312
4.79M
    } else {
1313
4.79M
      suffix.flags = PRIV_PSL_FLAG_PLAIN | type;
1314
4.79M
      psl->nsuffixes++;
1315
4.79M
    }
1316
1317
4.79M
    if (suffix_init(&suffix, p, linep - p) == 0) {
1318
4.79M
      int index;
1319
1320
4.79M
      if ((index = vector_find(psl->suffixes, &suffix)) >= 0) {
1321
        /* Found existing entry:
1322
         * Combination of exception and plain rule is ambiguous
1323
         * !foo.bar
1324
         * foo.bar
1325
         *
1326
         * Allowed:
1327
         * !foo.bar + *.foo.bar
1328
         * foo.bar + *.foo.bar
1329
         *
1330
         * We do not check here, let's do it later.
1331
         */
1332
1333
1.12M
        suffixp = vector_get(psl->suffixes, index);
1334
1.12M
        suffixp->flags |= suffix.flags;
1335
3.66M
      } else {
1336
        /* New entry */
1337
3.66M
        suffixp = vector_get(psl->suffixes, vector_add(psl->suffixes, &suffix));
1338
3.66M
      }
1339
1340
4.79M
      if (suffixp) {
1341
4.79M
        suffixp->label = suffixp->label_buf; /* set label to changed address */
1342
4.79M
        add_punycode_if_needed(idna, psl->suffixes, suffixp);
1343
4.79M
      }
1344
4.79M
    }
1345
4.86M
  } while ((linep = fgets(buf, sizeof(buf), fp)));
1346
1347
2.74k
  vector_sort(psl->suffixes);
1348
1349
2.74k
  psl_idna_close(idna);
1350
1351
2.74k
  return psl;
1352
1353
3.03k
fail:
1354
3.03k
  psl_free(psl);
1355
3.03k
  return NULL;
1356
3.00k
}
1357
1358
/**
1359
 * psl_free:
1360
 * @psl: PSL context pointer
1361
 *
1362
 * This function frees the the PSL context that has been retrieved via
1363
 * psl_load_fp() or psl_load_file().
1364
 *
1365
 * Since: 0.1
1366
 */
1367
void psl_free(psl_ctx_t *psl)
1368
6.03k
{
1369
6.03k
  if (psl && psl != &builtin_psl) {
1370
6.01k
    vector_free(&psl->suffixes);
1371
6.01k
    free(psl->dafsa);
1372
6.01k
    free(psl);
1373
6.01k
  }
1374
6.03k
}
1375
1376
/**
1377
 * psl_builtin:
1378
 *
1379
 * This function returns the PSL context that has been generated and built in at compile-time.
1380
 * You don't have to free the returned context explicitly.
1381
 *
1382
 * The builtin data also contains punycode entries, one for each international domain name.
1383
 *
1384
 * If the generation of built-in data has been disabled during compilation, %NULL will be returned.
1385
 * When using the builtin psl context, you can provide UTF-8 (lowercase + NFKC) or ASCII/ACE (punycode)
1386
 * representations of domains to functions like psl_is_public_suffix().
1387
 *
1388
 * Returns: Pointer to the built in PSL data or %NULL if this data is not available.
1389
 *
1390
 * Since: 0.1
1391
 */
1392
const psl_ctx_t *psl_builtin(void)
1393
0
{
1394
0
#ifdef ENABLE_BUILTIN
1395
0
  return &builtin_psl;
1396
#else
1397
  return NULL;
1398
#endif
1399
0
}
1400
1401
/**
1402
 * psl_suffix_count:
1403
 * @psl: PSL context pointer
1404
 *
1405
 * This function returns number of public suffixes maintained by @psl.
1406
 * The number of exceptions within the Public Suffix List are not included.
1407
 *
1408
 * If the information is not available, the return value is -1 (since 0.19).
1409
 * This is the case with DAFSA blobs or if @psl is %NULL.
1410
 *
1411
 * Returns: Number of public suffixes entries in PSL context or -1 if this information is not available.
1412
 *
1413
 * Since: 0.1
1414
 */
1415
int psl_suffix_count(const psl_ctx_t *psl)
1416
0
{
1417
0
  if (psl == &builtin_psl)
1418
0
    return _psl_nsuffixes;
1419
0
  else if (psl)
1420
0
    return psl->dafsa ? -1 : psl->nsuffixes;
1421
0
  else
1422
0
    return -1;
1423
0
}
1424
1425
/**
1426
 * psl_suffix_exception_count:
1427
 * @psl: PSL context pointer
1428
 *
1429
 * This function returns number of public suffix exceptions maintained by @psl.
1430
 *
1431
 * If the information is not available, the return value is -1 (since 0.19).
1432
 * This is the case with DAFSA blobs or if @psl is %NULL.
1433
 *
1434
 * Returns: Number of public suffix exceptions in PSL context or -1 if this information is not available.
1435
 *
1436
 * Since: 0.1
1437
 */
1438
int psl_suffix_exception_count(const psl_ctx_t *psl)
1439
0
{
1440
0
  if (psl == &builtin_psl)
1441
0
    return _psl_nexceptions;
1442
0
  else if (psl)
1443
0
    return psl->dafsa ? -1 : psl->nexceptions;
1444
0
  else
1445
0
    return -1;
1446
0
}
1447
1448
/**
1449
 * psl_suffix_wildcard_count:
1450
 * @psl: PSL context pointer
1451
 *
1452
 * This function returns number of public suffix wildcards maintained by @psl.
1453
 *
1454
 * If the information is not available, the return value is -1 (since 0.19).
1455
 * This is the case with DAFSA blobs or if @psl is %NULL.
1456
 *
1457
 * Returns: Number of public suffix wildcards in PSL context or -1 if this information is not available.
1458
 *
1459
 * Since: 0.10.0
1460
 */
1461
int psl_suffix_wildcard_count(const psl_ctx_t *psl)
1462
0
{
1463
0
  if (psl == &builtin_psl)
1464
0
    return _psl_nwildcards;
1465
0
  else if (psl)
1466
0
    return psl->dafsa ? -1 : psl->nwildcards;
1467
0
  else
1468
0
    return -1;
1469
0
}
1470
1471
/**
1472
 * psl_builtin_file_time:
1473
 *
1474
 * This function returns the mtime of the Public Suffix List file that has been built in.
1475
 *
1476
 * If the generation of built-in data has been disabled during compilation, 0 will be returned.
1477
 *
1478
 * Returns: time_t value or 0.
1479
 *
1480
 * Since: 0.1
1481
 */
1482
time_t psl_builtin_file_time(void)
1483
0
{
1484
0
  return _psl_file_time;
1485
0
}
1486
1487
/**
1488
 * psl_builtin_sha1sum:
1489
 *
1490
 * This function returns the SHA1 checksum of the Public Suffix List file that has been built in.
1491
 * The returned string is in lowercase hex encoding, e.g. "2af1e9e3044eda0678bb05949d7cca2f769901d8".
1492
 *
1493
 * If the generation of built-in data has been disabled during compilation, an empty string will be returned.
1494
 *
1495
 * Returns: String containing SHA1 checksum or an empty string.
1496
 *
1497
 * Since: 0.1
1498
 */
1499
const char *psl_builtin_sha1sum(void)
1500
0
{
1501
0
  return _psl_sha1_checksum;
1502
0
}
1503
1504
/**
1505
 * psl_builtin_filename:
1506
 *
1507
 * This function returns the file name of the Public Suffix List file that has been built in.
1508
 *
1509
 * If the generation of built-in data has been disabled during compilation, an empty string will be returned.
1510
 *
1511
 * Returns: String containing the PSL file name or an empty string.
1512
 *
1513
 * Since: 0.1
1514
 */
1515
const char *psl_builtin_filename(void)
1516
0
{
1517
0
  return _psl_filename;
1518
0
}
1519
1520
/**
1521
 * psl_builtin_outdated:
1522
 *
1523
 * This function checks if the built-in data is older than the file it has been created from.
1524
 * If it is, it might be a good idea for the application to reload the PSL.
1525
 * The mtime is taken as reference.
1526
 *
1527
 * If the PSL file does not exist, it is assumed that the built-in data is not outdated.
1528
 *
1529
 * Returns: 1 if the built-in is outdated, 0 otherwise.
1530
 *
1531
 * Since: 0.10.0
1532
 */
1533
int psl_builtin_outdated(void)
1534
0
{
1535
0
  struct_stat st;
1536
1537
0
  if (func_sys_stat(_psl_filename, &st) == 0 && st.st_mtime > _psl_file_time)
1538
0
    return 1;
1539
1540
0
  return 0;
1541
0
}
1542
1543
/**
1544
 * psl_dist_filename:
1545
 *
1546
 * This function returns the file name of the distribution/system PSL data file.
1547
 * This file will be considered by psl_latest().
1548
 *
1549
 * Return the filename that is set by ./configure --with-psl-distfile, or an empty string.
1550
 *
1551
 * Returns: String containing a PSL file name or an empty string.
1552
 *
1553
 * Since: 0.16
1554
 */
1555
const char *psl_dist_filename(void)
1556
0
{
1557
0
  return _psl_dist_filename;
1558
0
}
1559
1560
/**
1561
 * psl_get_version:
1562
 *
1563
 * Get libpsl version.
1564
 *
1565
 * Returns: String containing version of libpsl.
1566
 *
1567
 * Since: 0.2.5
1568
 **/
1569
const char *psl_get_version(void)
1570
0
{
1571
#ifdef WITH_LIBICU
1572
  return PACKAGE_VERSION " (+libicu/" U_ICU_VERSION ")";
1573
#elif defined(WITH_LIBICUCORE)
1574
  return PACKAGE_VERSION " (+libicucore/" U_ICU_VERSION ")";
1575
#elif defined(WITH_LIBICU_WIN)
1576
  return PACKAGE_VERSION " (+icu.lib/Windows)";
1577
#elif defined(WITH_LIBIDN2)
1578
  return PACKAGE_VERSION " (+libidn2/" IDN2_VERSION ")";
1579
#elif defined(WITH_LIBIDN)
1580
0
  return PACKAGE_VERSION " (+libidn/" STRINGPREP_VERSION ")";
1581
#else
1582
  return PACKAGE_VERSION " (no IDNA support)";
1583
#endif
1584
0
}
1585
1586
/**
1587
 * psl_check_version_number:
1588
 * @version: Version number (hex) to check against.
1589
 *
1590
 * Check the given version number is at minimum the current library version number.
1591
 * The version number must be a hexadecimal number like 0x000a01 (V0.10.1).
1592
 *
1593
 * Returns: Returns the library version number if the given version number is at least
1594
 * the version of the library, else return 0; If the argument is 0, the function returns
1595
 * the library version number without performing a check.
1596
 *
1597
 * Since: 0.11.0
1598
 **/
1599
int psl_check_version_number(int version)
1600
0
{
1601
0
  if (version) {
1602
0
    int major = version >> 16;
1603
0
    int minor = (version >> 8) & 0xFF;
1604
0
    int patch = version & 0xFF;
1605
1606
0
    if (major < PSL_VERSION_MAJOR
1607
0
      || (major == PSL_VERSION_MAJOR && minor < PSL_VERSION_MINOR)
1608
0
      || (major == PSL_VERSION_MAJOR && minor == PSL_VERSION_MINOR && patch < PSL_VERSION_PATCH))
1609
0
    {
1610
0
      return 0;
1611
0
    }
1612
0
  }
1613
1614
0
  return PSL_VERSION_NUMBER;
1615
0
}
1616
/*
1617
 * Return true if 'src' is a valid dotted quad, else false.
1618
 * Assume that characters '0'..'9' have consecutive byte values.
1619
 * credit:
1620
 *    inspired by Paul Vixie
1621
 */
1622
static int is_ip4(const char *s)
1623
0
{
1624
0
  int i, n;
1625
0
  unsigned char c;
1626
1627
0
  for (i = 0; i < 4; i++) {
1628
0
    if (!(c = *s++) || c < '0' || c > '9')
1629
0
      return 0;
1630
1631
0
    n = c - '0';
1632
0
    if ((c = *s++) && c >= '0' && c <= '9') {
1633
0
      n = n * 10 + c - '0';
1634
0
      if ((c = *s++) && c >= '0' && c <= '9') {
1635
0
        n = n * 10 + c - '0';
1636
0
        if ((c = *s++) && c >= '0' && c <= '9') {
1637
0
          n = n * 10 + c - '0';
1638
0
          c = *s++;
1639
0
        }
1640
0
      }
1641
0
    }
1642
1643
0
    if (n > 255)
1644
0
      return 0;
1645
1646
0
    if (i < 3 && c != '.')
1647
0
      return 0;
1648
0
  }
1649
1650
0
  return !c;
1651
0
}
1652
1653
static int hexval(unsigned c)
1654
0
{
1655
0
  if (c - '0' < 10) return c - '0';
1656
0
  c |= 32;
1657
0
  if (c - 'a' < 6) return c - 'a' + 10;
1658
0
  return -1;
1659
0
}
1660
1661
/*
1662
 * Original code taken from musl inet_pton(),
1663
 *   which has a standard MIT license (https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT).
1664
 * Amended and simplified to out needs.
1665
 */
1666
static int is_ip6(const char *s)
1667
0
{
1668
0
  int i, j, n, d, brk = -1, need_v4 = 0;
1669
1670
0
  if (*s == ':' && *++s != ':') return 0;
1671
1672
0
  for (i = 0; ; i++) {
1673
0
    if (s[0] == ':' && brk < 0) {
1674
0
      brk = i;
1675
0
      if (!*++s) break;
1676
0
      continue;
1677
0
    }
1678
0
    for (n = j = 0; j < 4 && (d = hexval(s[j])) >= 0; j++)
1679
0
      n = n * 16 + d;
1680
0
    if (j == 0) return 0;
1681
0
    if (!s[j] && (brk >= 0 || i == 7)) break;
1682
0
    if (i == 7) return 0;
1683
0
    if (s[j] != ':') {
1684
0
      if (s[j] != '.' || (i < 6 && brk < 0)) return 0;
1685
0
      need_v4 = 1;
1686
0
      i++;
1687
0
      break;
1688
0
    }
1689
0
    s += j + 1;
1690
0
  }
1691
1692
0
  if (need_v4 && !is_ip4(s)) return 0;
1693
0
  return 1;
1694
0
}
1695
1696
/* return whether hostname is an IP address or not */
1697
static int isip(const char *hostname)
1698
0
{
1699
0
  return is_ip4(hostname) || is_ip6(hostname);
1700
0
}
1701
1702
/**
1703
 * psl_is_cookie_domain_acceptable:
1704
 * @psl: PSL context pointer
1705
 * @hostname: The request hostname.
1706
 * @cookie_domain: The domain value from a cookie
1707
 *
1708
 * This helper function checks whether @cookie_domain is an acceptable cookie domain value for the request
1709
 * @hostname.
1710
 *
1711
 * For international domain names both, @hostname and @cookie_domain, have to be either in UTF-8 (lowercase + NFKC)
1712
 * or in ASCII/ACE (punycode) format. Other encodings or mixing UTF-8 and punycode likely result in incorrect return values.
1713
 *
1714
 * Use helper function psl_str_to_utf8lower() for normalization of @hostname and @cookie_domain.
1715
 *
1716
 * Hint for Windows users:
1717
 * Please make sure the calling application has called WSAStartup() before calling psl_is_cookie_domain_acceptable().
1718
 *
1719
 * Examples:
1720
 * 1. Cookie domain 'example.com' would be acceptable for hostname 'www.example.com',
1721
 * but '.com' or 'com' would NOT be acceptable since 'com' is a public suffix.
1722
 *
1723
 * 2. Cookie domain 'his.name' would be acceptable for hostname 'remember.his.name',
1724
 *  but NOT for 'forgot.his.name' since 'forgot.his.name' is a public suffix.
1725
 *
1726
 * Returns: 1 if acceptable, 0 if not acceptable.
1727
 *
1728
 * Since: 0.1
1729
 */
1730
int psl_is_cookie_domain_acceptable(const psl_ctx_t *psl, const char *hostname, const char *cookie_domain)
1731
0
{
1732
0
  const char *p;
1733
0
  size_t hostname_length, cookie_domain_length;
1734
1735
0
  if (!psl || !hostname || !cookie_domain)
1736
0
    return 0;
1737
1738
0
  while (*cookie_domain == '.')
1739
0
    cookie_domain++;
1740
1741
0
  if (!strcmp(hostname, cookie_domain))
1742
0
    return 1; /* an exact match is acceptable (and pretty common) */
1743
1744
0
  if (isip(hostname))
1745
0
    return 0; /* Hostname is an IP address and these must match fully (RFC 6265, 5.1.3) */
1746
1747
0
  cookie_domain_length = strlen(cookie_domain);
1748
0
  hostname_length = strlen(hostname);
1749
1750
0
  if (cookie_domain_length >= hostname_length)
1751
0
    return 0; /* cookie_domain is too long */
1752
1753
0
  p = hostname + hostname_length - cookie_domain_length;
1754
0
  if (!strcmp(p, cookie_domain) && p[-1] == '.') {
1755
    /* OK, cookie_domain matches, but it must be longer than the longest public suffix in 'hostname' */
1756
1757
0
    if (!(p = psl_unregistrable_domain(psl, hostname)))
1758
0
      return 1;
1759
1760
0
    if (cookie_domain_length > strlen(p))
1761
0
      return 1;
1762
0
  }
1763
1764
0
  return 0;
1765
0
}
1766
1767
/**
1768
 * psl_free_string:
1769
 * @str: pointer to lowercase string returned by psl_str_to_utf8lower()
1770
 *
1771
 * This function free()'s the memory allocated by psl_str_to_utf8lower() when
1772
 * returning a lowercase string
1773
 *
1774
 * Since: 0.19
1775
 */
1776
void psl_free_string(char *str)
1777
0
{
1778
0
  if (str)
1779
0
    free(str);
1780
0
}
1781
1782
#if defined(WITH_LIBIDN2) || defined(WITH_LIBIDN) || defined(WITH_LIBICUCORE)
1783
/* Avoid using strcasecmp() or _stricmp() */
1784
0
static int isUTF8(const char *s) {
1785
0
  return (s[0] == 'u' || s[0] == 'U')
1786
0
    && (s[1] == 't' || s[1] == 'T')
1787
0
    && (s[2] == 'f' || s[2] == 'F')
1788
0
    && s[3] == '-' && s[4] == 0;
1789
0
}
1790
1791
static char *idn_u8_tolower(const char *buf, size_t len, const char *locale)
1792
0
{
1793
#if defined(WITH_LIBICUCORE)
1794
  if (len > INT_MAX)
1795
    return NULL;
1796
1797
  int32_t src_len = (int32_t)len;
1798
  if (src_len > 0 && buf[src_len - 1] == 0)
1799
    src_len--;
1800
1801
  UErrorCode status = U_ZERO_ERROR;
1802
  int32_t utf16_src_len;
1803
  u_strFromUTF8(NULL, 0, &utf16_src_len, buf, src_len, &status);
1804
  if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
1805
    return NULL;
1806
1807
  UChar *utf16_src = malloc((size_t)utf16_src_len * sizeof(UChar));
1808
  if (!utf16_src)
1809
    return NULL;
1810
1811
  status = U_ZERO_ERROR;
1812
  u_strFromUTF8(utf16_src, utf16_src_len, NULL, buf, src_len, &status);
1813
  if (U_FAILURE(status)) {
1814
    free(utf16_src);
1815
    return NULL;
1816
  }
1817
1818
  status = U_ZERO_ERROR;
1819
  int32_t utf16_lower_len = u_strToLower(NULL, 0, utf16_src, utf16_src_len, locale, &status);
1820
  if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
1821
    free(utf16_src);
1822
    return NULL;
1823
  }
1824
1825
  UChar *utf16_lower = malloc((size_t)utf16_lower_len * sizeof(UChar));
1826
  if (!utf16_lower) {
1827
    free(utf16_src);
1828
    return NULL;
1829
  }
1830
1831
  status = U_ZERO_ERROR;
1832
  u_strToLower(utf16_lower, utf16_lower_len, utf16_src, utf16_src_len, locale, &status);
1833
  free(utf16_src);
1834
  if (U_FAILURE(status)) {
1835
    free(utf16_lower);
1836
    return NULL;
1837
  }
1838
1839
  status = U_ZERO_ERROR;
1840
  int32_t utf8_lower_len;
1841
  u_strToUTF8(NULL, 0, &utf8_lower_len, utf16_lower, utf16_lower_len, &status);
1842
  if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
1843
    free(utf16_lower);
1844
    return NULL;
1845
  }
1846
1847
  char *result = malloc((size_t)utf8_lower_len + 1);
1848
  if (!result) {
1849
    free(utf16_lower);
1850
    return NULL;
1851
  }
1852
1853
  status = U_ZERO_ERROR;
1854
  u_strToUTF8(result, utf8_lower_len + 1, NULL, utf16_lower, utf16_lower_len, &status);
1855
  free(utf16_lower);
1856
  if (U_FAILURE(status)) {
1857
    free(result);
1858
    result = NULL;
1859
  }
1860
1861
  return result;
1862
#else
1863
0
  (void) locale;
1864
1865
0
  return (char *)u8_tolower((uint8_t *)buf, len, 0, UNINORM_NFKC, NULL, &len);
1866
0
#endif
1867
0
}
1868
#endif
1869
1870
/**
1871
 * psl_str_to_utf8lower:
1872
 * @str: string to convert
1873
 * @encoding: charset encoding of @str, e.g. 'iso-8859-1' or %NULL
1874
 * @locale: locale of @str for to lowercase conversion, e.g. 'de' or %NULL
1875
 * @lower: return value containing the converted string
1876
 *
1877
 * This helper function converts a string to UTF-8 lowercase + NFKC representation.
1878
 * Lowercase + NFKC UTF-8 is needed as input to the domain checking functions.
1879
 *
1880
 * @lower stays unchanged on error.
1881
 *
1882
 * When returning PSL_SUCCESS, the return value 'lower' must be freed after usage.
1883
 *
1884
 * Returns: psl_error_t value.
1885
 *   PSL_SUCCESS: Success
1886
 *   PSL_ERR_INVALID_ARG: @str is a %NULL value.
1887
 *   PSL_ERR_CONVERTER: Failed to open the unicode converter with name @encoding
1888
 *   PSL_ERR_TO_UTF16: Failed to convert @str to unicode
1889
 *   PSL_ERR_TO_LOWER: Failed to convert unicode to lowercase
1890
 *   PSL_ERR_TO_UTF8: Failed to convert unicode to UTF-8
1891
 *   PSL_ERR_NO_MEM: Failed to allocate memory
1892
 *
1893
 * Since: 0.4
1894
 */
1895
psl_error_t psl_str_to_utf8lower(const char *str, const char *encoding, const char *locale, char **lower)
1896
0
{
1897
0
  int ret = PSL_ERR_INVALID_ARG;
1898
1899
0
  (void) encoding;
1900
0
  (void) locale;
1901
1902
0
  if (!str)
1903
0
    return PSL_ERR_INVALID_ARG;
1904
1905
  /* shortcut to avoid costly conversion */
1906
0
  if (str_is_ascii(str)) {
1907
0
    if (lower) {
1908
0
      char *p, *tmp;
1909
1910
0
      if (!(tmp = psl_strdup(str)))
1911
0
        return PSL_ERR_NO_MEM;
1912
1913
0
      *lower = tmp;
1914
1915
      /* convert ASCII string to lowercase */
1916
0
      for (p = *lower; *p; p++)
1917
0
        if (isupper(*p))
1918
0
          *p = tolower(*p);
1919
0
    }
1920
0
    return PSL_SUCCESS;
1921
0
  }
1922
1923
#if defined(WITH_LIBICU) || defined(WITH_LIBICU_WIN)
1924
#define STACK_STRLENGTH 256
1925
  do {
1926
  UErrorCode status = 0;
1927
  UChar *utf16_dst, *utf16_lower;
1928
  char *utf8_lower;
1929
  int32_t utf16_dst_length, utf16_dst_size, utf16_lower_size, utf8_lower_size;
1930
  UConverter *uconv;
1931
  UChar utf16_dst_buf[STACK_STRLENGTH * 2 + 1];
1932
  UChar utf16_lower_buf[STACK_STRLENGTH * 2 + 1];
1933
  char utf8_lower_buf[STACK_STRLENGTH * 6 + 1];
1934
  size_t str_length = strlen(str);
1935
1936
  if (str_length <= STACK_STRLENGTH) {
1937
    utf16_dst_size = countof(utf16_dst_buf);
1938
    utf16_lower_size = countof(utf16_lower_buf);
1939
    utf8_lower_size = countof(utf8_lower_buf);
1940
    utf16_dst   = utf16_dst_buf;
1941
    utf16_lower = utf16_lower_buf;
1942
    utf8_lower  = utf8_lower_buf;
1943
  } else {
1944
    utf16_dst_size = utf16_lower_size = str_length * 2 + 1;
1945
    utf8_lower_size = str_length * 6 + 1;
1946
    utf16_dst   = malloc(sizeof(UChar) * utf16_dst_size);
1947
    utf16_lower = malloc(sizeof(UChar) * utf16_lower_size);
1948
    utf8_lower  = malloc(sizeof(char) * utf8_lower_size);
1949
1950
    if (!utf16_dst || !utf16_lower || !utf8_lower) {
1951
      ret = PSL_ERR_NO_MEM;
1952
      goto out;
1953
    }
1954
  }
1955
1956
  uconv = ucnv_open(encoding, &status);
1957
  if (U_SUCCESS(status)) {
1958
    utf16_dst_length = ucnv_toUChars(uconv, utf16_dst, utf16_dst_size, str, str_length, &status);
1959
    ucnv_close(uconv);
1960
1961
    if (U_SUCCESS(status)) {
1962
      int32_t utf16_lower_length = u_strToLower(utf16_lower, utf16_lower_size, utf16_dst, utf16_dst_length, locale, &status);
1963
      if (U_SUCCESS(status)) {
1964
        u_strToUTF8(utf8_lower, utf8_lower_size, NULL, utf16_lower, utf16_lower_length, &status);
1965
        if (U_SUCCESS(status)) {
1966
          ret = PSL_SUCCESS;
1967
          if (lower) {
1968
            char *tmp = psl_strdup(utf8_lower);
1969
1970
            if (tmp)
1971
              *lower = tmp;
1972
            else
1973
              ret = PSL_ERR_NO_MEM;
1974
          }
1975
        } else {
1976
          ret = PSL_ERR_TO_UTF8;
1977
          /* fprintf(stderr, "Failed to convert UTF-16 to UTF-8 (status %d)\n", status); */
1978
        }
1979
      } else {
1980
        ret = PSL_ERR_TO_LOWER;
1981
        /* fprintf(stderr, "Failed to convert UTF-16 to lowercase (status %d)\n", status); */
1982
      }
1983
    } else {
1984
      ret = PSL_ERR_TO_UTF16;
1985
      /* fprintf(stderr, "Failed to convert string to UTF-16 (status %d)\n", status); */
1986
    }
1987
  } else {
1988
    ret = PSL_ERR_CONVERTER;
1989
    /* fprintf(stderr, "Failed to open converter for '%s' (status %d)\n", encoding, status); */
1990
  }
1991
out:
1992
  if (utf16_dst != utf16_dst_buf)
1993
    free(utf16_dst);
1994
  if (utf16_lower != utf16_lower_buf)
1995
    free(utf16_lower);
1996
  if (utf8_lower != utf8_lower_buf)
1997
    free(utf8_lower);
1998
1999
  } while (0);
2000
#elif defined(WITH_LIBIDN2) || defined(WITH_LIBIDN) || defined(WITH_LIBICUCORE)
2001
0
  do {
2002
    /* find out local charset encoding */
2003
0
    if (!encoding) {
2004
0
#ifdef HAVE_NL_LANGINFO
2005
0
      encoding = nl_langinfo(CODESET);
2006
#elif defined _WIN32
2007
      static char buf[16];
2008
      snprintf(buf, sizeof(buf), "CP%u", GetACP());
2009
      encoding = buf;
2010
#endif
2011
0
      if (!encoding || !*encoding)
2012
0
        encoding = "ASCII";
2013
0
    }
2014
2015
    /* convert to UTF-8 */
2016
0
    if (!isUTF8(encoding)) {
2017
0
      iconv_t cd = iconv_open("utf-8", encoding);
2018
2019
0
      if (cd != (iconv_t)-1) {
2020
0
        char *tmp = (char *)str; /* iconv won't change where str points to, but changes tmp itself */
2021
0
        size_t tmp_len = strlen(str) + 1;
2022
0
        size_t dst_len = tmp_len * 6, dst_len_tmp = dst_len;
2023
0
        char *dst = malloc(dst_len + 1), *dst_tmp = dst;
2024
2025
0
        if (!dst) {
2026
0
          ret = PSL_ERR_NO_MEM;
2027
0
        }
2028
0
        else if (iconv(cd, (ICONV_CONST char **)&tmp, &tmp_len, &dst_tmp, &dst_len_tmp) != (size_t)-1
2029
0
          && iconv(cd, NULL, NULL, &dst_tmp, &dst_len_tmp) != (size_t)-1)
2030
0
        {
2031
          /* start size for u8_tolower internal memory allocation.
2032
           * u8_tolower() does not terminate the result string. we have 0 byte included in above tmp_len
2033
           * and thus in len. */
2034
0
          size_t len = dst_len - dst_len_tmp;
2035
2036
0
          if ((tmp = idn_u8_tolower(dst, len, locale))) {
2037
0
            ret = PSL_SUCCESS;
2038
0
            if (lower) {
2039
0
              *lower = tmp;
2040
0
              tmp = NULL;
2041
0
            } else
2042
0
              free(tmp);
2043
0
          } else {
2044
0
            ret = PSL_ERR_TO_LOWER;
2045
            /* fprintf(stderr, "Failed to convert UTF-8 to lowercase (errno %d)\n", errno); */
2046
0
          }
2047
0
        } else {
2048
0
          ret = PSL_ERR_TO_UTF8;
2049
          /* fprintf(stderr, "Failed to convert '%s' string into '%s' (%d)\n", src_encoding, dst_encoding, errno); */
2050
0
        }
2051
2052
0
        free(dst);
2053
0
        iconv_close(cd);
2054
0
      } else {
2055
0
        ret = PSL_ERR_TO_UTF8;
2056
        /* fprintf(stderr, "Failed to prepare encoding '%s' into '%s' (%d)\n", src_encoding, dst_encoding, errno); */
2057
0
      }
2058
0
    } else {
2059
      /* we need a conversion to lowercase */
2060
0
      char *tmp;
2061
2062
      /* start size for u8_tolower internal memory allocation.
2063
       * u8_tolower() does not terminate the result string, so include terminating 0 byte in len. */
2064
0
      size_t len = strlen(str) + 1;
2065
2066
0
      if ((tmp = idn_u8_tolower(str, len, locale))) {
2067
0
        ret = PSL_SUCCESS;
2068
0
        if (lower) {
2069
0
          *lower = tmp;
2070
0
          tmp = NULL;
2071
0
        } else
2072
0
          free(tmp);
2073
0
      } else {
2074
0
        ret = PSL_ERR_TO_LOWER;
2075
        /* fprintf(stderr, "Failed to convert UTF-8 to lowercase (errno %d)\n", errno); */
2076
0
      }
2077
0
    }
2078
2079
0
  } while (0);
2080
0
#endif
2081
2082
0
  return ret;
2083
0
}
2084
2085
/* if file is newer than the builtin data, insert it reverse sorted by mtime */
2086
static int insert_file(const char *fname, const char **psl_fname, time_t *psl_mtime, int n)
2087
0
{
2088
0
  struct_stat st;
2089
0
  int it;
2090
2091
0
  if (fname && *fname && func_sys_stat(fname, &st) == 0 && st.st_mtime > _psl_file_time) {
2092
    /* add file name and mtime to end of array */
2093
0
    psl_fname[n] = fname;
2094
0
    psl_mtime[n++] = st.st_mtime;
2095
2096
    /* move the new entry to it's correct position */
2097
0
    for (it = n - 2; it >= 0 && st.st_mtime > psl_mtime[it]; it--) {
2098
0
      psl_fname[it + 1] = psl_fname[it];
2099
0
      psl_mtime[it + 1] = psl_mtime[it];
2100
0
      psl_fname[it] = fname;
2101
0
      psl_mtime[it] = st.st_mtime;
2102
0
    }
2103
0
  }
2104
2105
0
  return n;
2106
0
}
2107
2108
/**
2109
 * psl_latest:
2110
 * @fname: Name of PSL file or %NULL
2111
 *
2112
 * This function loads the the latest available PSL data from either
2113
 * - @fname (application specific filename, may be %NULL)
2114
 * - location specified during built-time (filename from ./configure --with-psl-distfile)
2115
 * - built-in PSL data (generated from ./configure --with-psl-file)
2116
 * - location of built-in data (filename from ./configure --with-psl-file)
2117
 *
2118
 * If none of the above is available, the function returns %NULL.
2119
 *
2120
 * To free the allocated resources, call psl_free().
2121
 *
2122
 * Returns: Pointer to a PSL context or %NULL on failure.
2123
 *
2124
 * Since: 0.16
2125
 */
2126
psl_ctx_t *psl_latest(const char *fname)
2127
0
{
2128
0
  psl_ctx_t *psl;
2129
0
  const char *psl_fname[3];
2130
0
  time_t psl_mtime[3];
2131
0
  int it, ntimes;
2132
2133
0
  psl_fname[0] = NULL; /* silence gcc 6.2 false warning */
2134
2135
  /* create array of PSL files reverse sorted by mtime (latest first) */
2136
0
  ntimes = insert_file(fname, psl_fname, psl_mtime, 0);
2137
0
  ntimes = insert_file(_psl_dist_filename, psl_fname, psl_mtime, ntimes);
2138
0
  ntimes = insert_file(_psl_filename, psl_fname, psl_mtime, ntimes);
2139
2140
  /* load PSL data from the latest file, falling back to the second recent, ... */
2141
0
  for (psl = NULL, it = 0; it < ntimes; it++) {
2142
0
    if (psl_mtime[it] > _psl_file_time)
2143
0
      if ((psl = psl_load_file(psl_fname[it])))
2144
0
        break;
2145
0
  }
2146
2147
  /* if file loading failed or there is no file newer than the builtin data,
2148
   * then return the builtin data. */
2149
0
  return psl ? psl : (psl_ctx_t *) psl_builtin();
2150
0
}