1 /*
2  * ufdbbase.c - URLfilterDB
3  *
4  * ufdbGuard is copyrighted (C) 2005-2020 by URLfilterDB with all rights reserved.
5  *
6  * Parts of ufdbGuard are based on squidGuard.
7  * This module is NOT based on squidGuard.
8  *
9  * RCS $Id: ufdbbase.c,v 1.138 2020/08/29 15:53:49 root Exp root $
10  */
11 
12 #undef _FORTIFY_SOURCE
13 
14 #define UFDB_MALLOC_DEBUG     	 0
15 
16 
17 #include "ufdb.h"
18 #include "ufdblib.h"
19 
20 #include <stdio.h>
21 #include <string.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #if UFDB_DPDK_SUPPORT
26 #include "rte_malloc.h"
27 #endif
28 #if HAVE_MEMALIGN
29 #include <malloc.h>
30 #endif
31 #include <ctype.h>
32 #include <signal.h>
33 #include <errno.h>
34 #if HAVE_SYS_SYSCALL_H
35 #include <sys/syscall.h>
36 #endif
37 #include <sys/time.h>
38 #include <sys/types.h>
39 
40 #if UFDB_DP_DEV
41 #include "cvmx.h"
42 #include "cvmx-bootmem.h"
43 #include "cvmx-malloc.h"
44 #endif
45 
46 #if !UFDB_BARE_METAL_SUPPORT
47 #include <sys/socket.h>
48 #include <sys/stat.h>
49 #include <pwd.h>
50 #endif
51 
52 #if HAVE_PCRE_COMPILE
53 #include <pcre.h>
54 #endif
55 
56 #if HAVE_PR_SET_TRACER
57 #include <sys/prctl.h>
58 #endif
59 
60 #if !defined(UFDB_API_NO_THREADS) && UFDB_PTHREAD_SUPPORT
61 #include <pthread.h>
62 #endif
63 
64 
65 #if UFDB_DP_DEV
66 #include "dpmalloc.h"
UFDBmallocInit(ufdb_config_t * config)67 void UFDBmallocInit( ufdb_config_t * config )
68 {
69    ufdbMallocInitDPdevelopment();
70 }
71 
72 #elif UFDB_BARE_METAL_SUPPORT && __OCTEON__
73 
74 #include "dpmalloc.h"
75 UFDB_SHARED static ufdb_malloc_t malloc_wrapper = NULL;
76 UFDB_SHARED static ufdb_free_t   free_wrapper = NULL;
77 
UFDBmallocInit(ufdb_config_t * config)78 void UFDBmallocInit( ufdb_config_t * config )
79 {
80    if (config->ufdb_alloc == NULL)
81       ufdbLogFatalError( "UFDBmallocInit: ufdb_alloc is NULL" );
82    else
83       malloc_wrapper = config->ufdb_alloc;
84 
85    if (config->ufdb_free == NULL)
86       ufdbLogFatalError( "UFDBmallocInit: ufdb_free is NULL" );
87    else
88       free_wrapper = config->ufdb_free;
89 
90    if (ufdbGV.debug)
91       ufdbLogMessage( "UFDBmallocInit:  wrapper functions  malloc %p  free %p", malloc_wrapper, free_wrapper );
92 }
93 #endif
94 
95 
96 #ifdef __cplusplus
97 extern "C" {
98 #endif
99 
100 #if HAVE_SETRESUID
101 int setresuid( uid_t ruid, uid_t euid, uid_t suid );
102 int getresuid( uid_t * ruid, uid_t * euid, uid_t * suid );
103 #endif
104 
105 
106 UFDB_SHARED static unsigned long cpu_mask = 0UL;
107 #define UFDB_MAX_CPUS 64
108 
109 
110 #if defined(__GLIBC__)
111 #if (__GLIBC__ > 2) || (__GLIBC__ == 2  &&  __GLIBC_MINOR__ >= 4) || HAVE_PCRE_COMPILE
112 #define NEED_REGEXEC_MUTEX 0
113 #else
114 #define NEED_REGEXEC_MUTEX 1
115 #endif
116 #else
117 #define NEED_REGEXEC_MUTEX 0
118 #endif
119 
120 #if UFDB_REGEX_SUPPORT && NEED_REGEXEC_MUTEX
121 ufdb_mutex ufdb_regexec_mutex = ufdb_mutex_initializer;
122 #endif
123 
124 #ifdef UFDB_DEBUG
125 ufdb_mutex ufdb_malloc_mutex = ufdb_mutex_initializer;
126 #endif
127 
128 
129 #if UFDB_DP_DEV
130 UFDB_SHARED static cvmx_arena_list_t alist = NULL;
131 
ufdbMallocInitDPdevelopment(void)132 void ufdbMallocInitDPdevelopment( void )
133 {
134 #define MYMEMSIZE (700 * 1024 * 1024)
135 
136    // allocate an arena and assign it to memory allocator
137    void * mm;
138 
139    mm = cvmx_bootmem_alloc( MYMEMSIZE, 4096 );
140    if (mm == NULL)
141    {
142       ufdbLogFatalError( "initDP: cvmx_bootmem_alloc failed" );
143       exit( 1 );
144    }
145 
146    if (cvmx_add_arena( &alist, mm, MYMEMSIZE ) != 0)
147    {
148       ufdbLogFatalError( "initDP: cvmx_add_arena failed" );
149       exit( 1 );
150    }
151 }
152 #endif
153 
154 
155 UFDB_GCC_MALLOC_ATTR
ufdbMalloc(size_t elsize)156 void * ufdbMalloc( size_t elsize )
157 {
158    void * p;
159 
160 #if UFDB_MALLOC_DEBUG
161    if (ufdbGV.debug)
162       ufdbLogMessage( "      ufdbMalloc %'ld", (long) elsize );
163 #endif
164 
165 #if UFDB_DPDK_SUPPORT
166    p = rte_malloc( NULL, elsize, 0 );
167 #elif UFDB_DP_DEV
168    p = cvmx_malloc( alist, elsize );
169 #elif UFDB_BARE_METAL_SUPPORT && __OCTEON__
170    p = malloc_wrapper( (uint32_t) elsize );
171    if (ufdbGV.debug)
172       ufdbLogMessage( "ufdbMalloc: malloc_wrapper(%ld) -> %p", (long)elsize, p );
173 #else
174    p = (void *) malloc( elsize );
175 #endif
176    if (p == NULL  &&  elsize > 0)
177    {
178       ufdbGV.memoryAllocationErrors++;
179       ufdbLogFatalError( "cannot allocate %'ld bytes memory", (long) elsize );
180    }
181 
182 #if UFDB_MALLOC_DEBUG
183    if (ufdbGV.debug)
184       ufdbLogMessage( "         ufdbMalloc %'ld -> 0x%08lx", (long) elsize, (long) p );
185 #endif
186 
187    return p;
188 }
189 
190 
191 #define UFDB_HUGEPAGESIZE (2*1024*1024)
192 
ufdbMallocAligned(size_t alignment,size_t elsize)193 void * UFDB_GCC_MALLOC_ATTR ufdbMallocAligned( size_t alignment, size_t elsize )
194 {
195    void * p;
196 
197 #if UFDB_MALLOC_DEBUG
198    if (ufdbGV.debug)
199       ufdbLogMessage( "      ufdbMallocAligned %'ld, %'ld", alignment, elsize );
200 #endif
201 
202    if (alignment < UFDB_HUGEPAGESIZE  &&  (elsize > ((size_t) (UFDB_HUGEPAGESIZE * 0.9))))
203       alignment = UFDB_HUGEPAGESIZE;                 // the kernel may use a hugepage
204    else if (alignment < 4096  &&  elsize > 3600)
205       alignment = 4096;
206    else
207    {
208       alignment = (alignment + (UFDB_CACHELINE_SIZE-1)) & ~(UFDB_CACHELINE_SIZE-1);
209    }
210 
211 #if UFDB_DPDK_SUPPORT
212    p = rte_malloc( NULL, elsize, (unsigned int) alignment );
213 #elif UFDB_DP_DEV
214    p = cvmx_memalign( alist, alignment, elsize );
215 #elif UFDB_BARE_METAL_SUPPORT && __OCTEON__
216    if (ufdbGV.debug)
217       ufdbLogMessage( "ufdbMallocAligned: going to call malloc_wrapper(%ld) wrapper=%p", (long)elsize, malloc_wrapper );
218    p = malloc_wrapper( (uint32_t) elsize );
219    if (ufdbGV.debug)
220       ufdbLogMessage( "ufdbMallocAligned: malloc_wrapper(%ld) -> %p", (long)elsize, p );
221 #else
222 
223    int    retval;
224 #if HAVE_POSIX_MEMALIGN
225    p = NULL;
226    retval = posix_memalign( &p, alignment, elsize );
227 #elif HAVE_MEMALIGN
228    retval = 0;
229    p = memalign( alignment, elsize );
230 #elif HAVE_ALIGNED_ALLOC
231    retval = 0;
232    p = aligned_alloc( alignment, elsize );
233 #else
234    retval = 0;
235    p = malloc( elsize );
236 #endif
237 
238 #if UFDB_MALLOC_DEBUG
239    if (ufdbGV.debug)
240       ufdbLogMessage( "         ufdbMallocAligned(%ld):%d %'ld -> %p",
241 		      alignment, retval, elsize, p );
242 #endif
243 
244    if (retval == 0  &&  p != NULL)
245       return p;
246 
247    p = (void *) ufdbMalloc( elsize );
248 #endif
249 
250    if (p == NULL  &&  elsize > 0)
251    {
252       ufdbGV.memoryAllocationErrors++;
253       ufdbLogFatalError( "cannot allocate %'ld bytes %'ld-aligned memory", elsize, alignment );
254    }
255 
256 #if UFDB_MALLOC_DEBUG
257    if (ufdbGV.debug)
258       ufdbLogMessage( "         ufdbMallocAligned not-aligned %'ld -> %p", elsize, p );
259 #endif
260 
261    return p;
262 }
263 
264 
ufdbCalloc(size_t n,size_t num)265 void * ufdbCalloc( size_t n, size_t num )
266 {
267    void * p;
268 
269 #if UFDB_MALLOC_DEBUG
270    if (ufdbGV.debug)
271       ufdbLogMessage( "      ufdbCalloc %ld %'ld", (long) n, (long) num );
272 #endif
273 
274    p = (void *) ufdbMalloc( n * num );
275    if (p == NULL  &&  n > 0)
276       ufdbLogFatalError( "cannot allocate %ld bytes zeroed memory", (long) (n*num) );
277    else
278       memset( p, 0, n * num );
279 
280 #if UFDB_MALLOC_DEBUG
281    if (ufdbGV.debug)
282       ufdbLogMessage( "         ufdbCalloc %ld %ld  = %'ld  -> 0x%08lx",
283                       (long) n, (long) num, (long) n*num, (long) p );
284 #endif
285 
286    return p;
287 }
288 
289 
290 // realloc is only used to parse 1.2 tables and in ufdbGenTable and may be problematic on bare metal
291 #if UFDB_DP_DEV || !UFDB_BARE_METAL_SUPPORT
ufdbRealloc(void * ptr,size_t elsize)292 void * ufdbRealloc( void * ptr, size_t elsize )
293 {
294    void * p;
295 
296 #if UFDB_MALLOC_DEBUG
297    if (ufdbGV.debug)
298       ufdbLogMessage( "      ufdbRealloc 0x%08lx %'ld", (long) ptr, (long) elsize );
299 #endif
300 
301 #if UFDB_DPDK_SUPPORT
302    p = rte_realloc( ptr, (unsigned int) elsize, 0 );
303 #elif UFDB_DP_DEV
304    ufdbLogFatalError( "ufdbRealloc: realloc is not implemented on OCTEON" );
305    p = NULL;   // cvmx_realloc( alist, ptr, elsize );
306 #elif UFDB_BARE_METAL_SUPPORT && __OCTEON__
307    ufdbLogFatalError( "ufdbRealloc: realloc is not implemented on OCTEON" );
308    p = NULL;
309 #else
310    p = (void *) realloc( ptr, elsize );
311 #endif
312    if (p == NULL  &&  elsize > 0)
313    {
314       ufdbGV.memoryAllocationErrors++;
315       ufdbLogFatalError( "cannot reallocate %'ld bytes memory", (long) elsize );
316    }
317 
318 #if UFDB_MALLOC_DEBUG
319    if (ufdbGV.debug)
320       ufdbLogMessage( "         ufdbRealloc 0x%08lx %'ld  ->  %08lx", (long) ptr, (long) elsize, (long) p );
321 #endif
322 
323    return p;
324 }
325 #endif
326 
327 
ufdbFree(void * ptr)328 void ufdbFree( void * ptr )
329 {
330 #if UFDB_MALLOC_DEBUG
331    if (ufdbGV.debug)
332       ufdbLogMessage( "      ufdbFree 0x%08lx", (long) ptr );
333 #endif
334 
335    if (ptr != NULL)
336    {
337 #if UFDB_DPDK_SUPPORT
338       rte_free( ptr );
339 #elif UFDB_DP_DEV
340       cvmx_free( ptr );
341 #elif UFDB_BARE_METAL_SUPPORT && __OCTEON__
342       if (ufdbGV.debug)
343 	 ufdbLogMessage( "ufdbFree: going to call free_wrapper(%p)", ptr );
344       free_wrapper( ptr );
345 #else
346       free( ptr );
347 #endif
348 
349 #if UFDB_MALLOC_DEBUG
350       if (ufdbGV.debug)
351 	 ufdbLogMessage( "      ufdbFree 0x%08lx freed", (long) ptr );
352 #endif
353    }
354 }
355 
356 
ufdbZlibMalloc(UFDB_GCC_UNUSED void * opaque,unsigned int items,unsigned int size)357 void * ufdbZlibMalloc( UFDB_GCC_UNUSED void * opaque, unsigned int items, unsigned int size )
358 {
359 #if UFDB_BARE_METAL_SUPPORT && __OCTEON__
360    if (ufdbGV.debug)
361       ufdbLogMessage( "ufdbZlibMalloc: calling ufdbMalloc" );
362 #endif
363    return ufdbMalloc( (size_t) items * size );
364 }
365 
366 
ufdbZlibFree(UFDB_GCC_UNUSED void * opaque,void * address)367 void ufdbZlibFree( UFDB_GCC_UNUSED void * opaque, void * address )
368 {
369 #if UFDB_BARE_METAL_SUPPORT && __OCTEON__
370    if (ufdbGV.debug)
371       ufdbLogMessage( "ufdbZlibFree: calling ufdbFree" );
372 #endif
373    return ufdbFree( address );
374 }
375 
376 
ufdbStrdup(const char * s)377 char * ufdbStrdup( const char * s )
378 {
379    int size;
380 
381 #if defined(UFDB_DEBUG)
382    ufdbLogMessage( "   ufdbStrdup %-60.60s", s );
383 #endif
384 
385    size = strlen( s ) + 1;
386    return strcpy( (char *) ufdbMalloc(size), s );
387 }
388 
389 
ufdbStrStrEnd(const char * s,const char * end)390 int ufdbStrStrEnd( const char * s, const char * end )
391 {
392    int n;
393 
394    n = strlen( end );
395    while (s != NULL  &&  *s != '\0')
396    {
397       s = strstr( s, end );
398       if (s != NULL)
399       {
400          if (*(s+n) == '\0')
401 	    return 1;
402 	 else
403 	    s++;
404       }
405    }
406    return 0;
407 }
408 
409 
UFDBfgets(char * requestBuffer,int bufferSize,FILE * fp)410 char * UFDBfgets(
411    char * requestBuffer,
412    int    bufferSize,
413    FILE * fp )
414 {
415    char * b;
416    int    ch;
417    int    size;
418 
419    b = requestBuffer;
420    size = 1;
421 
422    while ((ch = getc_unlocked(fp)) != EOF)
423    {
424       *b++ = ch;
425       if (ch == '\n')
426          goto end;
427       if (++size == bufferSize)
428          goto end;
429    }
430 
431    if (b == requestBuffer  &&  (feof(fp) || ferror(fp)))
432       return NULL;
433 
434 end:
435    *b = '\0';
436    return requestBuffer;
437 }
438 
439 
UFDBfgetsNoNL(char * s,int size,FILE * stream)440 char * UFDBfgetsNoNL( char * s, int size, FILE * stream )
441 {
442    char * buf;
443    int    ch;
444 
445    buf = s;
446    while ((ch = getc_unlocked(stream)) != EOF  &&  --size > 0)
447    {
448       if (ch == '\r')
449          continue;
450       if (ch == '\n')
451          break;
452       *buf++ = ch;
453    }
454    *buf = '\0';
455    if (ch == EOF  &&  buf == s)
456       return NULL;
457 
458    /* remove trailing spaces */
459    while (buf > s)
460    {
461       if (*(buf-1) == ' ')
462       {
463          buf--;
464          *buf = '\0';
465       }
466       else
467          break;
468    }
469 
470    return s;
471 }
472 
473 
474 #if UFDB_REGEX_SUPPORT
UFDBregcomp(void * preg,const char * regex,int cflags)475 static int UFDBregcomp( void * preg, const char * regex, int cflags )
476 {
477    int     retval;
478 #if (HAVE_PCRE_COMPILE || HAVE_PCRE_COMPILE2)
479    int     pcre_options;
480    int     pcre_error;
481    const char * pcre_errstr;
482    int     pcre_erroffset;
483    pcre ** ppreg;
484    pcre *  pcre_comp_re;
485 #endif
486 
487 #if HAVE_PCRE_COMPILE2
488    pcre_options = PCRE_UTF8;
489    if (cflags & REG_ICASE)
490       pcre_options |= PCRE_CASELESS;
491    pcre_comp_re = pcre_compile2( regex, pcre_options, &pcre_error, &pcre_errstr, &pcre_erroffset, NULL );
492    retval = 0;
493    if (pcre_comp_re == NULL)
494    {
495       if (ufdbGV.debug || ufdbGV.debugRegexp)
496          ufdbLogError( "pcre_compile2: error with regular expression \"%s\": %s", regex, pcre_errstr );
497       retval = pcre_error;
498    }
499    else
500    {
501       ppreg = (pcre**) preg;
502       *ppreg = pcre_comp_re;
503    }
504 #elif HAVE_PCRE_COMPILE
505    pcre_options = PCRE_UTF8;
506    if (cflags & REG_ICASE)
507       pcre_options |= PCRE_CASELESS;
508    pcre_comp_re = pcre_compile( regex, pcre_options, &pcre_errstr, &pcre_errofsset, NULL );
509    retval = 0;
510    if (pcre_comp_re == NULL)
511    {
512       if (ufdbGV.debug || ufdbGV.debugRegexp)
513          ufdbLogError( "pcre_compile: error with regular expression \"%s\": %s", regex, pcre_errstr );
514       retval = REG_ECOLLATE;
515    }
516    else
517    {
518       ppreg = (pcre**) preg;
519       *ppreg = pcre_comp_re;
520    }
521 #else
522    retval = regcomp( (regex_t*) preg, regex, cflags );
523 #endif
524 
525    return retval;
526 }
527 
528 
529 UFDB_GCC_INLINE
UFDBregfree(void * preg)530 static void UFDBregfree( void * preg )
531 {
532 #if HAVE_PCRE_COMPILE2 || HAVE_PCRE_COMPILE
533    pcre_free( (pcre*) preg );
534 #else
535    regfree( (regex_t*) preg );
536 #endif
537 }
538 
539 
540 /* TO-DO: make a macro for UFDBregexec() */
541 UFDB_GCC_HOT
UFDBregexec(const void * preg,const char * string,size_t nmatch,regmatch_t pmatch[],int eflags)542 int UFDBregexec( const void * preg, const char * string, size_t nmatch, regmatch_t pmatch[], int eflags )
543 {
544    int retval;
545 #if HAVE_PCRE_COMPILE2 || HAVE_PCRE_COMPILE
546    int pcre_ovector[2*3];
547    int pcre_ovecsize;
548 #endif
549 
550 #if NEED_REGEXEC_MUTEX
551    ufdb_mutex_lock( &ufdb_regexec_mutex );
552 #endif
553 
554 #if (HAVE_PCRE_COMPILE2 || HAVE_PCRE_COMPILE)
555    pcre_ovecsize = nmatch * 3;
556    if (pcre_ovecsize > (int) sizeof(pcre_ovector))
557       pcre_ovecsize = sizeof(pcre_ovector);
558    retval = pcre_exec( (pcre*) preg, NULL, string, strlen(string), 0,
559                        PCRE_NO_UTF8_CHECK, pcre_ovector, pcre_ovecsize );
560    if (retval == PCRE_ERROR_NOMATCH)
561       retval = REG_NOMATCH;
562    else if (nmatch > 0)
563    {
564       /* convert pcre ovector to regmatch_t */
565       pmatch[0].rm_so = pcre_ovector[0];
566       pmatch[0].rm_eo = pcre_ovector[1];
567       if (nmatch > 1)
568       {
569          pmatch[1].rm_so = pcre_ovector[2];
570          pmatch[1].rm_eo = pcre_ovector[3];
571       }
572    }
573 #else
574    retval = regexec( (regex_t*) preg, string, nmatch, pmatch, eflags );
575 #endif
576 
577 #if NEED_REGEXEC_MUTEX
578    ufdb_mutex_unlock( &ufdb_regexec_mutex );
579 #endif
580 
581    return retval;
582 }
583 
584 
ufdbNewPatternBuffer(char * pattern,int flags)585 struct ufdbRegExp * ufdbNewPatternBuffer(
586    char * pattern,
587    int    flags )
588 {
589    struct ufdbRegExp * re;
590 #if !(HAVE_PCRE_COMPILE2  ||  HAVE_PCRE_COMPILE)
591    int i;
592 #endif
593 
594    re = (struct ufdbRegExp *) ufdbMallocAligned( 64, sizeof(struct ufdbRegExp) );
595 
596 #if !(HAVE_PCRE_COMPILE2 || HAVE_PCRE_COMPILE)  &&  !defined(UFDB_API_NO_THREADS)
597    re->next_nregex_i = 0;
598    ufdb_mutex_init( &(re->lock) );
599 #endif
600 
601 #if HAVE_PCRE_COMPILE2  ||  HAVE_PCRE_COMPILE
602    re->compiled[0] = NULL;
603    re->error = UFDBregcomp( &(re->compiled[0]), pattern, flags );
604 #else
605    re->compiled[0] = ufdbCalloc( 1, sizeof(regex_t) );
606    re->error = UFDBregcomp( re->compiled[0], pattern, flags );
607    if (!re->error)
608    {
609       for (i = 1;  i < UFDB_NREGEX;  i++)
610       {
611 	 re->compiled[i] = ufdbCalloc( 1, sizeof(regex_t) );
612 	 (void) UFDBregcomp( re->compiled[i], pattern, flags );
613       }
614    }
615 #endif
616    re->pattern = ufdbStrdup( pattern );
617    re->substitute = NULL;
618    re->flags = flags;
619    re->global = 0;
620    re->httpcode = NULL;
621    re->next = NULL;
622 
623    return re;
624 }
625 
626 
627 /*
628  *  optimise list of RE1 ... REn into one RE with (RE1)| ... |(REn)
629  */
UFDBoptimizeExprList(char * reSource,struct ufdbRegExp * reList)630 struct ufdbRegExp * UFDBoptimizeExprList(
631    char *              reSource,
632    struct ufdbRegExp * reList )
633 {
634    struct ufdbRegExp * re;
635    int    n;
636    int    totalStrlen;
637 
638    n = 0;
639    totalStrlen = 0;
640 
641    for (re = reList;  re != NULL;  re = re->next)
642    {
643       if (re->error == 0)
644       {
645          n++;
646 	 totalStrlen += strlen( re->pattern );
647       }
648    }
649 
650    if (n == 0)
651    {
652       if (ufdbGV.debugRegexp)
653 	 ufdbLogMessage( "   UFDBoptimizeExprList: %s has no REs.", reSource );
654       return NULL;
655    }
656 
657    if (ufdbGV.debug || ufdbGV.debugRegexp)
658       ufdbLogMessage( "   UFDBoptimizeExprList: %s has %d REs", reSource, n );
659    if (ufdbGV.debugRegexp > 1)
660    {
661       ufdbLogMessage( "   UFDBoptimizeExprList: %d REs of %s are not optimised due to expression debugging",
662                       n, reSource );
663       return reList;
664    }
665 
666    if (n > 1)
667    {
668       char * newpattern;
669 
670       newpattern = (char *) ufdbMalloc( totalStrlen + 3*n + 1 );
671       newpattern[0] = '\0';
672       for (re = reList;  re != NULL;  re = re->next)
673       {
674          if (re->error == 0)
675 	 {
676 #ifdef UFDB_USE_OPTIM_RE_BRACKETS
677 	    strcat( newpattern, "(" );
678 	    strcat( newpattern, re->pattern );
679 	    if (re->next == NULL)
680 	       strcat( newpattern, ")" );
681 	    else
682 	       strcat( newpattern, ")|" );
683 #else
684 	    strcat( newpattern, re->pattern );
685 	    if (re->next != NULL)
686 	       strcat( newpattern, "|" );
687 #endif
688 	 }
689       }
690       if (ufdbGV.debug > 2  ||  ufdbGV.debugRegexp)
691          ufdbLogMessage( "going to optimise complex expression of %d subexpressions and %d characters",
692                          n, totalStrlen + 1*(n-1) );
693       if (n > 500)
694          ufdbLogMessage( "WARNING: the expressionlist has %d expressions and may use many resources  *****\n"
695 			 "Note that large numbers of expressions may impact performance considerably",
696 	                 n );
697       re = ufdbNewPatternBuffer( newpattern, reList->flags );
698       if (re->error)
699       {
700          ufdbLogError( "UFDBoptimizeExprList: unable to optimise %d expressions of %s  (error %d)  *****",
701                        n, reSource, re->error );
702          if (n >= 20)
703             ufdbLogMessage( "Since the %d expressions could not be optimised into one expression, "
704                             "they will be evaluated one by one which impacts performance  *****", n );
705 	 ufdbFree( newpattern );
706 	 ufdbFreeRegExprList( re );
707 	 return reList;
708       }
709 
710       ufdbLogMessage( "the %d expressions of %s have been optimised", n, reSource );
711       if (ufdbGV.debugRegexp)
712          ufdbLogMessage( "optimised expression: %s", newpattern );
713 
714       ufdbFree( newpattern );
715       ufdbFreeRegExprList( reList );
716       return re;
717    }
718    else if (n == 1)
719    {
720       if (ufdbGV.debugRegexp || ufdbGV.debug)
721 	 ufdbLogMessage( "the expressions of %s has one RE and does not need optimisation", reSource );
722    }
723 
724    return reList;
725 }
726 
727 
728 /*
729  * initialize an expression list (read them from a file and do the regexp compilation)
730  */
UFDBloadExpressions(struct ufdbRegExp ** exprList,char * file)731 int UFDBloadExpressions(
732    struct ufdbRegExp ** exprList,
733    char *               file  )
734 {
735    FILE *               fin;
736    char *               eoln;
737    struct ufdbRegExp *  re;
738    struct ufdbRegExp *  last;
739    int                  retCode;
740    char                 line[1024];
741 
742    if (exprList == NULL)
743       return UFDB_API_ERR_NULL;
744    *exprList = NULL;
745 
746    if (file == NULL)
747       return UFDB_API_ERR_NULL;
748 
749    fin = fopen( file, "r" );
750    if (fin == NULL)
751       return UFDB_API_ERR_NOFILE;
752 
753    retCode = UFDB_API_OK;
754    last = NULL;
755    re = NULL;
756 
757    while (fgets( line, sizeof(line), fin ) != NULL)
758    {
759       if (line[0] == '#')         /* skip comments */
760          continue;
761 
762       eoln = strchr( line, '\n' );
763       if (eoln == NULL  ||  eoln == &line[0])
764          continue;	/* skip empty lines and lines without a newline */
765       else
766       {
767          if (*(eoln-1) == '\r')
768 	    eoln--;
769       }
770       *eoln = '\0';	/* get rid of the newline */
771 
772       /* URLs are folded to lowercase so we do not use REG_ICASE any more */
773       re = ufdbNewPatternBuffer( line, REG_EXTENDED | REG_NOSUB );
774       if (re->error)
775       {
776 	 ufdbLogError( "could not compile regular expression from %s (error %d) : \"%s\"",
777                        file, re->error, line );
778          retCode = UFDB_API_ERR_EXPR;
779       }
780 
781       re->next = last;
782       last = re;
783    }
784 
785    if (ufdbGV.expressionOptimisation)
786    {
787       *exprList = UFDBoptimizeExprList( file, re );
788    }
789    else
790       *exprList = re;
791 
792    fclose( fin );
793 
794    return retCode;
795 }
796 
797 
798 /*
799  * match a URL with a compiled RE.
800  * return 0 if no match, 1 if there is a match.
801  */
802 UFDB_GCC_HOT
ufdbRegExpMatch(struct ufdbRegExp * regexp,const char * str)803 int ufdbRegExpMatch(
804    struct ufdbRegExp * regexp,
805    const char *        str )
806 {
807    struct ufdbRegExp * rp;
808    int                 error;
809    int                 i;
810 
811    if (ufdbGV.debugRegexp)
812       ufdbLogMessage( "      ufdbRegExpMatch \"%s\" %s", str, regexp==NULL ? "no REs" : "RE to test" );
813 
814    for (rp = regexp;  rp != NULL;  rp = rp->next)
815    {
816       if (ufdbGV.debugRegexp)
817 	 ufdbLogMessage( "      ufdbRegExpMatch  %s  %s  error=%d", str, rp->pattern, rp->error );
818       if (rp->error)
819          continue;
820 
821 #if !(HAVE_PCRE_COMPILE2 || HAVE_PCRE_COMPILE)  &&  !defined(UFDB_API_NO_THREADS)
822       ufdb_mutex_lock( &(rp->lock) );
823       i = rp->next_nregex_i;
824       rp->next_nregex_i = (i + 1) % UFDB_NREGEX;
825       ufdb_mutex_unlock( &(rp->lock) );
826 #else
827       i = 0;
828 #endif
829 
830 #if HAVE_PCRE_COMPILE || HAVE_PCRE_COMPILE2
831       error = UFDBregexec( (pcre*) rp->compiled[i], str, 0, NULL, 0 );
832 #else
833       error = UFDBregexec( (regex_t*) rp->compiled[i], str, 0, NULL, 0 );
834 #endif
835       if (error == 0) 	/* match */
836       {
837 	 if (ufdbGV.debugRegexp)
838 	    ufdbLogMessage( "   RE match:  %s  %s", rp->pattern, str );
839          return UFDB_API_MATCH;
840       }
841       if (error != REG_NOMATCH)
842       {
843 	 if (ufdbGV.debugRegexp)
844 	    ufdbLogMessage( "   RE error %d:  %s  %s  error=%d", error, rp->pattern, str, error );
845          return UFDB_API_ERR_EXPR;
846       }
847    }
848 
849    return 0;
850 }
851 
852 
ufdbFreeRegExprList(struct ufdbRegExp * re)853 void ufdbFreeRegExprList( struct ufdbRegExp * re )
854 {
855    struct ufdbRegExp * tmp;
856    int                 i;
857 
858    while (re != NULL)
859    {
860       tmp = re->next;
861       if (!re->error)
862       {
863 	 for (i = 0;  i < UFDB_NREGEX;  i++)
864 	 {
865 	    UFDBregfree( re->compiled[i] );
866 #if !HAVE_PCRE_COMPILE  &&  !HAVE_PCRE_COMPILE2
867 	    ufdbFree( re->compiled[i] );
868 #endif
869 	 }
870       }
871       ufdbFree( re->pattern );
872       ufdbFree( re->substitute );
873       ufdbFree( re );
874       re = tmp;
875    }
876 }
877 #endif   /* UFDB_REGEX_SUPPORT */
878 
879 
ufdbResetCPUs(void)880 void ufdbResetCPUs( void )
881 {
882    cpu_mask = 0UL;
883 }
884 
885 
ufdbAPIstatusString(int api_code)886 const char * ufdbAPIstatusString( int api_code )
887 {
888    switch (api_code)
889    {
890    case UFDB_API_OK:                       return "OK";
891    case UFDB_API_ERR_NULL:                 return "ERR_NULL";
892    case UFDB_API_ERR_NOFILE:               return "ERR_NOFILE";
893    case UFDB_API_ERR_READ:                 return "ERR_READ";
894    case UFDB_API_ERR_EXPR:                 return "ERR_EXPR";
895    case UFDB_API_ERR_RANGE:                return "ERR_RANGE";
896    case UFDB_API_ERR_ERRNO:                return "ERR_ERRNO";
897    case UFDB_API_ERR_SOCKET:               return "ERR_SOCKET";
898    case UFDB_API_ERR_NOMEM:                return "ERR_NOMEM";
899    case UFDB_API_REQ_QUEUED:               return "REQ_QUEUED";
900    case UFDB_API_ERR_TUNNEL:               return "ERR_TUNNEL";
901    case UFDB_API_ERR_INVALID_CERT:         return "ERR_INVALID_CERT";
902    case UFDB_API_ERR_IP_ADDRESS:           return "ERR_IP_ADDRESS";
903    case UFDB_API_ERR_OLD_TABLE:            return "ERR_OLD_TABLE";
904    case UFDB_API_ERR_INVALID_TABLE:        return "ERR_INVALID_TABLE";
905    case UFDB_API_ERR_INVALID_KEY:          return "ERR_INVALID_KEY";
906    case UFDB_API_ERR_IS_SKYPE:             return "ERR_IS_SKYPE";
907    case UFDB_API_ERR_FULL:                 return "ERR_FULL";
908    case UFDB_API_ERR_UNKNOWN_PROTOCOL:     return "ERR_UNKNOWN_PROTOCOL";
909    case UFDB_API_ERR_IS_GTALK:             return "ERR_IS_GTALK";
910    case UFDB_API_ERR_IS_YAHOOMSG:          return "ERR_IS_YAHOOMSG";
911    case UFDB_API_ERR_IS_AIM:               return "ERR_IS_AIM";
912    case UFDB_API_ERR_IS_FBCHAT:            return "ERR_IS_FBCHAT";
913    case UFDB_API_ERR_IS_CITRIXONLINE:      return "ERR_IS_CITRIXONLINE";
914    case UFDB_API_ERR_IS_ANYDESK:           return "ERR_IS_ANYDESK";
915    case UFDB_API_ERR_IS_TEAMVIEWER:        return "ERR_IS_TEAMVIEWER";
916    case UFDB_API_ERR_CKSUM_NOT_VALID:      return "ERR_CKSUM_NOT_VALID";
917    case UFDB_API_ERR_OUTDATED:             return "ERR_OUTDATED";
918    case UFDB_API_ERR_FATAL:                return "ERR_FATAL";
919    case UFDB_API_ERR_TLS:                  return "ERR_TLS";
920    case UFDB_API_BEING_VERIFIED:           return "BEING_VERIFIED";
921    case UFDB_API_MODIFIED_FOR_SAFESEARCH:  return "MODIFIED_FOR_SAFESEARCH";
922    case -1:				   return "INTERNAL_ERROR_MINUS_ONE";
923    }
924 
925    return "INTERNAL_ERROR_UNKNOWN_CODE";
926 }
927 
928 
ufdbDBstat2string(int status)929 const char * ufdbDBstat2string( int status )
930 {
931    switch (status)
932    {
933       case UFDB_API_STATUS_DATABASE_OK:       return "up to date";
934       case UFDB_API_STATUS_DATABASE_OLD:      return "one or more tables are more than 4 days old.  Check cron job for ufdbUpdate.";
935       case UFDB_API_STATUS_DATABASE_EXPIRED:  return "one or more tables are EXPIRED.  Check licenses and cron job for ufdbUpdate.";
936    }
937    return "internal-error";
938 }
939 
940 
ufdbStatus2string(int status)941 const char * ufdbStatus2string( int status )
942 {
943    switch (status)
944    {
945       case UFDB_API_STATUS_VIRGIN:          return "virgin";
946       case UFDB_API_STATUS_STARTED_OK:      return "started";
947       case UFDB_API_STATUS_TERMINATED:      return "terminated";
948       case UFDB_API_STATUS_RELOADING:       return "reloading";
949       case UFDB_API_STATUS_RELOAD_OK:       return "reloaded";
950       case UFDB_API_STATUS_FATAL_ERROR:     return "error";
951       case UFDB_API_STATUS_ROLLING_LOGFILE: return "rolling-logfile";
952       case UFDB_API_STATUS_UPDATE: 	    return "status-update";
953       case UFDB_API_STATUS_CRASH_REPORT_UPLOADED:     	return "crash-report-uploaded";
954       case UFDB_API_STATUS_CRASH_REPORT_NOT_UPLOADED:   return "upload-crash-reports-off-prohibits-upload";
955    }
956    return "internal-error-unknown-status";
957 }
958 
959 
960 #ifndef ufdbStrncpy
961 
ufdbStrncpy(char * dest,const char * src,size_t n)962 UFDB_GCC_HOT void ufdbStrncpy( char * dest, const char * src, size_t n )
963 {
964    if (memccpy( dest, src, '\0', n ) == NULL)
965       dest[n-1] = '\0';
966 }
967 
968 #endif
969 
970 
971 #ifdef __cplusplus
972 }
973 #endif
974