Coverage Report

Created: 2026-08-14 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
0
#define PRIV_PSL_FLAG_EXCEPTION (1<<0)
127
0
#define PRIV_PSL_FLAG_WILDCARD  (1<<1)
128
0
#define PRIV_PSL_FLAG_ICANN     (1<<2) /* entry of ICANN section */
129
0
#define PRIV_PSL_FLAG_PRIVATE   (1<<3) /* entry of PRIVATE section */
130
0
#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
0
{
195
0
  psl_vector_t *v;
196
197
0
  if (!(v = calloc(1, sizeof(psl_vector_t))))
198
0
    return NULL;
199
200
0
  if (!(v->entry = malloc(max * sizeof(psl_entry_t *)))) {
201
0
    free(v);
202
0
    return NULL;
203
0
  }
204
205
0
  v->max = max;
206
0
  v->cmp = cmp;
207
0
  return v;
208
0
}
209
210
static void vector_free(psl_vector_t **v)
211
0
{
212
0
  if (v && *v) {
213
0
    if ((*v)->entry) {
214
0
      int it;
215
216
0
      for (it = 0; it < (*v)->cur; it++)
217
0
        free((*v)->entry[it]);
218
219
0
      free((*v)->entry);
220
0
    }
221
0
    free(*v);
222
0
  }
223
0
}
224
225
static psl_entry_t *vector_get(const psl_vector_t *v, int pos)
226
0
{
227
0
  if (pos < 0 || !v || pos >= v->cur) return NULL;
228
229
0
  return v->entry[pos];
230
0
}
231
232
/* the entries must be sorted by */
233
static int vector_find(const psl_vector_t *v, const psl_entry_t *elem)
234
0
{
235
0
  if (v) {
236
0
    int l, r, m;
237
0
    int res;
238
239
    /* binary search for element (exact match) */
240
0
    for (l = 0, r = v->cur - 1; l <= r;) {
241
0
      m = (l + r) / 2;
242
0
      if ((res = v->cmp(&elem, (const psl_entry_t **)&(v->entry[m]))) > 0) l = m + 1;
243
0
      else if (res < 0) r = m - 1;
244
0
      else return m;
245
0
    }
246
0
  }
247
248
0
  return -1; /* not found */
249
0
}
250
251
static int vector_add(psl_vector_t *v, const psl_entry_t *elem)
252
0
{
253
0
  if (v) {
254
0
    void *elemp;
255
256
0
    if (!(elemp = malloc(sizeof(psl_entry_t))))
257
0
      return -1;
258
259
0
    memcpy(elemp, elem, sizeof(psl_entry_t));
260
261
0
    if (v->max == v->cur) {
262
0
      void *m = realloc(v->entry, (v->max *= 2) * sizeof(psl_entry_t *));
263
264
0
      if (m)
265
0
        v->entry = m;
266
0
      else {
267
0
        free(elemp);
268
0
        return -1;
269
0
      }
270
0
    }
271
272
0
    v->entry[v->cur++] = elemp;
273
0
    return v->cur - 1;
274
0
  }
275
276
0
  return -1;
277
0
}
278
279
static void vector_sort(psl_vector_t *v)
280
0
{
281
0
  if (v && v->cmp)
282
0
    qsort(v->entry, v->cur, sizeof(psl_vector_t **), (int(*)(const void *, const void *))v->cmp);
283
0
}
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
0
{
288
0
  int n;
289
290
0
  if ((n = s2->nlabels - s1->nlabels))
291
0
    return n; /* most labels first */
292
293
0
  if ((n = s1->length - s2->length))
294
0
    return n;  /* shorter rules first */
295
296
0
  return strncmp(s1->label ? s1->label : s1->label_buf, s2->label ? s2->label : s2->label_buf, s1->length);
297
0
}
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
0
{
302
0
  return suffix_compare(*s1, *s2);
303
0
}
304
305
static int suffix_init(psl_entry_t *suffix, const char *rule, size_t length)
306
0
{
307
0
  const char *src;
308
0
  char *dst;
309
310
0
  suffix->label = suffix->label_buf;
311
312
0
  if (length >= sizeof(suffix->label_buf) - 1) {
313
0
    suffix->nlabels = 0;
314
    /* fprintf(stderr, "Suffix rule too long (%zd, ignored): %s\n", length, rule); */
315
0
    return -1;
316
0
  }
317
318
0
  suffix->length = (unsigned char)length;
319
320
0
  suffix->nlabels = 1;
321
322
0
  for (dst = suffix->label_buf, src = rule; *src;) {
323
0
    if (*src == '.')
324
0
      suffix->nlabels++;
325
0
    *dst++ = *src++;
326
0
  }
327
0
  *dst = 0;
328
329
0
  return 0;
330
0
}
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
0
{
622
0
  return c == ' ' || c == '\t' || c == '\r' || c == '\n';
623
0
}
624
625
static int str_is_ascii(const char *s)
626
0
{
627
0
  while (*s && *((unsigned char *)s) < 128) s++;
628
629
0
  return !*s;
630
0
}
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
{
645
  const unsigned char *s = (const unsigned char *) utf8;
646
647
  while (*s) {
648
    if ((*s & 0x80) == 0) /* 0xxxxxxx ASCII char */
649
      s++;
650
    else if ((*s & 0xE0) == 0xC0) /* 110xxxxx 10xxxxxx */ {
651
      if ((s[1] & 0xC0) != 0x80)
652
        return 0;
653
      s += 2;
654
    } else if ((*s & 0xF0) == 0xE0) /* 1110xxxx 10xxxxxx 10xxxxxx */ {
655
      if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80)
656
        return 0;
657
      s += 3;
658
    } else if ((*s & 0xF8) == 0xF0) /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ {
659
      if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80 || (s[3] & 0xC0) != 0x80)
660
        return 0;
661
      s += 4;
662
    } else
663
      return 0;
664
  }
665
666
  return 1;
667
}
668
#endif
669
670
typedef void *psl_idna_t;
671
672
static psl_idna_t *psl_idna_open(void)
673
0
{
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
0
  return NULL;
679
0
}
680
681
static void psl_idna_close(psl_idna_t *idna)
682
0
{
683
0
  (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
0
}
690
691
static int psl_idna_toASCII(psl_idna_t *idna, const char *utf8, char **ascii)
692
0
{
693
0
  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
0
  int rc;
756
757
0
  (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
0
  if ((rc = idn2_lookup_u8((uint8_t *)utf8, (uint8_t **)ascii, IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL)) == IDN2_OK)
767
0
    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
  (void) idna;
792
793
  if (!utf8_is_valid(utf8)) {
794
    /* fprintf(stderr, "Invalid UTF-8 sequence not converted: '%s'\n", utf8); */
795
    return -1;
796
  }
797
798
  /* idna_to_ascii_8z() automatically converts UTF-8 to lowercase */
799
800
  if ((rc = idna_to_ascii_8z(utf8, ascii, IDNA_USE_STD3_ASCII_RULES)) == IDNA_SUCCESS) {
801
    ret = 0;
802
  } /* 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
0
  return ret;
817
0
}
818
819
static void add_punycode_if_needed(psl_idna_t *idna, psl_vector_t *v, psl_entry_t *e)
820
0
{
821
0
  char *lookupname;
822
823
0
  if (str_is_ascii(e->label_buf))
824
0
    return;
825
826
0
  if (psl_idna_toASCII(idna, e->label_buf, &lookupname) == 0) {
827
0
    if (strcmp(e->label_buf, lookupname)) {
828
0
      psl_entry_t suffix, *suffixp;
829
830
      /* fprintf(stderr, "toASCII '%s' -> '%s'\n", e->label_buf, lookupname); */
831
0
      if (suffix_init(&suffix, lookupname, strlen(lookupname)) == 0) {
832
0
        suffix.flags = e->flags;
833
0
        if ((suffixp = vector_get(v, vector_add(v, &suffix))))
834
0
          suffixp->label = suffixp->label_buf; /* set label to changed address */
835
0
      }
836
0
    } /* else ignore */
837
838
0
    free(lookupname);
839
0
  }
840
0
}
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
0
{
848
0
  psl_entry_t suffix;
849
0
  const char *p;
850
0
  char *punycode = NULL;
851
0
  size_t domain_len;
852
0
  int need_conversion = 0;
853
854
  /* this function should be called without leading dots, just make sure */
855
0
  if (*domain == '.')
856
0
    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
0
  domain_len = strlen(domain);
861
0
  if (domain_len > 0 && domain[domain_len - 1] == '.')
862
0
    domain_len--;
863
864
0
  suffix.nlabels = 1;
865
866
0
  for (p = domain; p < domain + domain_len; p++) {
867
0
    if (*p == '.') {
868
0
      if (suffix.nlabels == 255) /* weird input, avoid 8bit overflow */
869
0
        return 0;
870
0
      suffix.nlabels++;
871
0
    }
872
0
    else if (*((unsigned char *)p) >= 128)
873
0
      need_conversion = 1; /* in case domain is non-ascii we need a toASCII conversion */
874
0
  }
875
876
0
  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
0
  type &= ~PSL_TYPE_NO_STAR_RULE;
884
885
0
  if (psl->utf8 || psl == &builtin_psl)
886
0
    need_conversion = 0;
887
888
0
  if (need_conversion) {
889
0
    psl_idna_t *idna = psl_idna_open();
890
891
0
    if (psl_idna_toASCII(idna, domain, &punycode) == 0) {
892
0
      suffix.label = punycode;
893
0
      suffix.length = strlen(punycode);
894
0
    } else {
895
      /* fallback */
896
897
0
      suffix.label = domain;
898
0
      suffix.length = domain_len;
899
0
    }
900
901
0
    psl_idna_close(idna);
902
0
  } else {
903
0
    suffix.label = domain;
904
0
    suffix.length = domain_len;
905
0
  }
906
907
0
  if (psl == &builtin_psl || psl->dafsa) {
908
0
    size_t dafsa_size = psl == &builtin_psl ? sizeof(kDafsa) : psl->dafsa_size;
909
0
    const unsigned char *dafsa = psl == &builtin_psl ? kDafsa : psl->dafsa;
910
0
    int rc = LookupStringInFixedSet(dafsa, dafsa_size, suffix.label, suffix.length);
911
0
    if (rc != -1) {
912
      /* check for correct rule type */
913
0
      if (type == PSL_TYPE_ICANN && !(rc & PRIV_PSL_FLAG_ICANN))
914
0
        goto suffix_no;
915
0
      else if (type == PSL_TYPE_PRIVATE && !(rc & PRIV_PSL_FLAG_PRIVATE))
916
0
        goto suffix_no;
917
918
0
      if (rc & PRIV_PSL_FLAG_EXCEPTION)
919
0
        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
0
      goto suffix_yes;
924
0
    }
925
0
    if ((suffix.label = strchr(suffix.label, '.'))) {
926
0
      suffix.label++;
927
0
      suffix.length = strlen(suffix.label);
928
0
      suffix.nlabels--;
929
930
0
      rc = LookupStringInFixedSet(dafsa, dafsa_size, suffix.label, suffix.length);
931
0
      if (rc != -1) {
932
        /* check for correct rule type */
933
0
        if (type == PSL_TYPE_ICANN && !(rc & PRIV_PSL_FLAG_ICANN))
934
0
          goto suffix_no;
935
0
        else if (type == PSL_TYPE_PRIVATE && !(rc & PRIV_PSL_FLAG_PRIVATE))
936
0
          goto suffix_no;
937
938
0
        if (rc & PRIV_PSL_FLAG_WILDCARD)
939
0
          goto suffix_yes;
940
0
      }
941
0
    }
942
0
  } else {
943
0
    psl_entry_t *rule = vector_get(psl->suffixes, 0);
944
945
0
    if (!rule || rule->nlabels < suffix.nlabels - 1)
946
0
      goto suffix_no;
947
948
0
    rule = vector_get(psl->suffixes, vector_find(psl->suffixes, &suffix));
949
950
0
    if (rule) {
951
      /* check for correct rule type */
952
0
      if (type == PSL_TYPE_ICANN && !(rule->flags & PRIV_PSL_FLAG_ICANN))
953
0
        goto suffix_no;
954
0
      else if (type == PSL_TYPE_PRIVATE && !(rule->flags & PRIV_PSL_FLAG_PRIVATE))
955
0
        goto suffix_no;
956
957
0
      if (rule->flags & PRIV_PSL_FLAG_EXCEPTION)
958
0
        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
0
      goto suffix_yes;
963
0
    }
964
965
0
    if ((suffix.label = strchr(suffix.label, '.'))) {
966
0
      suffix.label++;
967
0
      suffix.length = strlen(suffix.label);
968
0
      suffix.nlabels--;
969
970
0
      rule = vector_get(psl->suffixes, vector_find(psl->suffixes, &suffix));
971
972
0
      if (rule) {
973
        /* check for correct rule type */
974
0
        if (type == PSL_TYPE_ICANN && !(rule->flags & PRIV_PSL_FLAG_ICANN))
975
0
          goto suffix_no;
976
0
        else if (type == PSL_TYPE_PRIVATE && !(rule->flags & PRIV_PSL_FLAG_PRIVATE))
977
0
          goto suffix_no;
978
979
0
        if (rule->flags & PRIV_PSL_FLAG_WILDCARD)
980
0
          goto suffix_yes;
981
0
      }
982
0
    }
983
0
  }
984
985
0
suffix_no:
986
0
  if (punycode)
987
0
    free(punycode);
988
0
  return 0;
989
990
0
suffix_yes:
991
0
  if (punycode)
992
0
    free(punycode);
993
0
  return 1;
994
0
}
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
0
{
1019
0
  if (!psl || !domain)
1020
0
    return 1;
1021
1022
0
  return is_public_suffix(psl, domain, PSL_TYPE_ANY);
1023
0
}
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
0
{
1184
0
  FILE *fp;
1185
0
  psl_ctx_t *psl = NULL;
1186
1187
0
  if (!fname)
1188
0
    return NULL;
1189
1190
0
  if ((fp = fopen(fname, "rb"))) {
1191
0
    psl = psl_load_fp(fp);
1192
0
    fclose(fp);
1193
0
  }
1194
1195
0
  return psl;
1196
0
}
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
0
{
1213
0
  psl_ctx_t *psl;
1214
0
  psl_entry_t suffix, *suffixp;
1215
0
  char buf[256], *linep, *p;
1216
0
  int type = 0, is_dafsa;
1217
0
  psl_idna_t *idna;
1218
1219
0
  if (!fp)
1220
0
    return NULL;
1221
1222
0
  if (!(psl = calloc(1, sizeof(psl_ctx_t))))
1223
0
    return NULL;
1224
1225
  /* read first line to allow ASCII / DAFSA detection */
1226
0
  if (!(linep = fgets(buf, sizeof(buf) - 1, fp)))
1227
0
    goto fail;
1228
1229
0
  is_dafsa = strlen(buf) == 16 && !strncmp(buf, ".DAFSA@PSL_", 11);
1230
1231
0
  if (is_dafsa) {
1232
0
    void *m;
1233
0
    size_t size = 65536, n, len = 0;
1234
0
    int version = atoi(buf + 11);
1235
1236
0
    if (version != 0)
1237
0
      goto fail;
1238
1239
0
    if (!(psl->dafsa = malloc(size)))
1240
0
      goto fail;
1241
1242
0
    memcpy(psl->dafsa, buf, len);
1243
1244
0
    while ((n = fread(psl->dafsa + len, 1, size - len, fp)) > 0) {
1245
0
      len += n;
1246
0
      if (len >= size) {
1247
0
        if (!(m = realloc(psl->dafsa, size *= 2)))
1248
0
          goto fail;
1249
0
        psl->dafsa = m;
1250
0
      }
1251
0
    }
1252
1253
    /* release unused memory */
1254
0
    if ((m = realloc(psl->dafsa, len)))
1255
0
      psl->dafsa = m;
1256
0
    else if (!len)
1257
0
      psl->dafsa = NULL; /* realloc() just free'd psl->dafsa */
1258
1259
0
    psl->dafsa_size = len;
1260
0
    psl->utf8 = !!GetUtfMode(psl->dafsa, len);
1261
1262
0
    return psl;
1263
0
  }
1264
1265
0
  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
0
  psl->suffixes = vector_alloc(8*1024, suffix_compare_array);
1273
0
  psl->utf8 = 1; /* we put UTF-8 and punycode rules in the lookup vector */
1274
1275
0
  do {
1276
0
    while (isspace_ascii(*linep)) linep++; /* ignore leading whitespace */
1277
0
    if (!*linep) continue; /* skip empty lines */
1278
1279
0
    if (*linep == '/' && linep[1] == '/') {
1280
0
      if (!type) {
1281
0
        if (strstr(linep + 2, "===BEGIN ICANN DOMAINS==="))
1282
0
          type = PRIV_PSL_FLAG_ICANN;
1283
0
        else if (!type && strstr(linep + 2, "===BEGIN PRIVATE DOMAINS==="))
1284
0
          type = PRIV_PSL_FLAG_PRIVATE;
1285
0
      }
1286
0
      else if (type == PRIV_PSL_FLAG_ICANN && strstr(linep + 2, "===END ICANN DOMAINS==="))
1287
0
        type = 0;
1288
0
      else if (type == PRIV_PSL_FLAG_PRIVATE && strstr(linep + 2, "===END PRIVATE DOMAINS==="))
1289
0
        type = 0;
1290
1291
0
      continue; /* skip comments */
1292
0
    }
1293
1294
    /* parse suffix rule */
1295
0
    for (p = linep; *linep && !isspace_ascii(*linep);) linep++;
1296
0
    *linep = 0;
1297
1298
0
    if (*p == '!') {
1299
0
      p++;
1300
0
      suffix.flags = PRIV_PSL_FLAG_EXCEPTION | type;
1301
0
      psl->nexceptions++;
1302
0
    } else if (*p == '*') {
1303
0
      if (*++p != '.') {
1304
        /* fprintf(stderr, "Unsupported kind of rule (ignored): %s\n", p - 1); */
1305
0
        continue;
1306
0
      }
1307
0
      p++;
1308
      /* wildcard *.foo.bar implicitly make foo.bar a public suffix */
1309
0
      suffix.flags = PRIV_PSL_FLAG_WILDCARD | PRIV_PSL_FLAG_PLAIN | type;
1310
0
      psl->nwildcards++;
1311
0
      psl->nsuffixes++;
1312
0
    } else {
1313
0
      suffix.flags = PRIV_PSL_FLAG_PLAIN | type;
1314
0
      psl->nsuffixes++;
1315
0
    }
1316
1317
0
    if (suffix_init(&suffix, p, linep - p) == 0) {
1318
0
      int index;
1319
1320
0
      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
0
        suffixp = vector_get(psl->suffixes, index);
1334
0
        suffixp->flags |= suffix.flags;
1335
0
      } else {
1336
        /* New entry */
1337
0
        suffixp = vector_get(psl->suffixes, vector_add(psl->suffixes, &suffix));
1338
0
      }
1339
1340
0
      if (suffixp) {
1341
0
        suffixp->label = suffixp->label_buf; /* set label to changed address */
1342
0
        add_punycode_if_needed(idna, psl->suffixes, suffixp);
1343
0
      }
1344
0
    }
1345
0
  } while ((linep = fgets(buf, sizeof(buf), fp)));
1346
1347
0
  vector_sort(psl->suffixes);
1348
1349
0
  psl_idna_close(idna);
1350
1351
0
  return psl;
1352
1353
0
fail:
1354
0
  psl_free(psl);
1355
0
  return NULL;
1356
0
}
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
0
{
1369
0
  if (psl && psl != &builtin_psl) {
1370
0
    vector_free(&psl->suffixes);
1371
0
    free(psl->dafsa);
1372
0
    free(psl);
1373
0
  }
1374
0
}
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
0
  return PACKAGE_VERSION " (+libidn2/" IDN2_VERSION ")";
1579
#elif defined(WITH_LIBIDN)
1580
  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
}