summaryrefslogtreecommitdiffstats
path: root/lib/dns/include/dns/librpz.h
blob: 8fdbe16db14b8ecd8dcc2b2de6319f22f81015d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
/*
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
 *
 * SPDX-License-Identifier: MPL-2.0
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
 */

/*
 * Define the interface from a DNS resolver to the Response Policy Zone
 * library, librpz.
 *
 * This file should be included only the interface functions between the
 * resolver and librpz to avoid name space pollution.
 *
 * Copyright (c) 2016-2017 Farsight Security, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * version 1.2.12
 */

#pragma once

#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>

#include <arpa/nameser.h>
#include <netinet/in.h>
#include <sys/types.h>

/*
 * Allow either ordinary or dlopen() linking.
 */
#ifdef LIBRPZ_INTERNAL
#define LIBDEF(t, s) extern t s;
#define LIBDEF_F(f)  LIBDEF(librpz_##f##_t, librpz_##f)
#else /* ifdef LIBRPZ_INTERNAL */
#define LIBDEF(t, s)
#define LIBDEF_F(f)
#endif /* ifdef LIBRPZ_INTERNAL */

/*
 * Response Policy Zone triggers.
 *	Comparisons of trigger precedences require
 *	LIBRPZ_TRIG_CLIENT_IP < LIBRPZ_TRIG_QNAME < LIBRPZ_TRIG_IP
 *	    < LIBRPZ_TRIG_NSDNAME < LIBRPZ_TRIG_NSIP}
 */
typedef enum {
	LIBRPZ_TRIG_BAD = 0,
	LIBRPZ_TRIG_CLIENT_IP = 1,
	LIBRPZ_TRIG_QNAME = 2,
	LIBRPZ_TRIG_IP = 3,
	LIBRPZ_TRIG_NSDNAME = 4,
	LIBRPZ_TRIG_NSIP = 5
} librpz_trig_t;
#define LIBRPZ_TRIG_SIZE 3     /* sizeof librpz_trig_t in bits */
typedef uint8_t librpz_tbit_t; /* one bit for each of the TRIGS_NUM
				* trigger types */

/*
 * Response Policy Zone Actions or policies
 */
typedef enum {
	LIBRPZ_POLICY_UNDEFINED = 0, /* an empty entry or no decision yet */
	LIBRPZ_POLICY_DELETED = 1,   /* placeholder for a deleted policy */

	LIBRPZ_POLICY_PASSTHRU = 2, /* 'passthru': do not rewrite */
	LIBRPZ_POLICY_DROP = 3,	    /* 'drop': do not respond */
	LIBRPZ_POLICY_TCP_ONLY = 4, /* 'tcp-only': answer UDP with TC=1 */
	LIBRPZ_POLICY_NXDOMAIN = 5, /* 'nxdomain': answer with NXDOMAIN */
	LIBRPZ_POLICY_NODATA = 6,   /* 'nodata': answer with ANCOUNT=0 */
	LIBRPZ_POLICY_RECORD = 7,   /* rewrite with the policy's RR */

	/* only in client configurations to override the zone */
	LIBRPZ_POLICY_GIVEN,	/* 'given': what policy record says */
	LIBRPZ_POLICY_DISABLED, /* at most log */
	LIBRPZ_POLICY_CNAME,	/* answer with 'cname x' */
} librpz_policy_t;
#define LIBRPZ_POLICY_BITS 4

/*
 * Special policies that appear as targets of CNAMEs
 * NXDOMAIN is signaled by a CNAME with a "." target.
 * NODATA is signaled by a CNAME with a "*." target.
 */
#define LIBRPZ_RPZ_PREFIX   "rpz-"
#define LIBRPZ_RPZ_PASSTHRU LIBRPZ_RPZ_PREFIX "passthru"
#define LIBRPZ_RPZ_DROP	    LIBRPZ_RPZ_PREFIX "drop"
#define LIBRPZ_RPZ_TCP_ONLY LIBRPZ_RPZ_PREFIX "tcp-only"

typedef uint16_t librpz_dznum_t; /* dnsrpzd zone # in [0,DZNUM_MAX] */
typedef uint8_t	 librpz_cznum_t; /* client zone # in [0,CZNUM_MAX] */

/*
 * CIDR block
 */
typedef struct librpz_prefix {
	union {
		struct in_addr	in;
		struct in6_addr in6;
	} addr;
	uint8_t family;
	uint8_t len;
} librpz_prefix_t;

/*
 * A domain
 */
typedef uint8_t librpz_dsize_t;
typedef struct librpz_domain {
	librpz_dsize_t size; /* of only .d */
	uint8_t	       d[0]; /* variable length wire format */
} librpz_domain_t;

/*
 * A maximal domain buffer
 */
typedef struct librpz_domain_buf {
	librpz_dsize_t size;
	uint8_t	       d[NS_MAXCDNAME];
} librpz_domain_buf_t;

/*
 * A resource record without the owner name.
 * C compilers say that sizeof(librpz_rr_t)=12 instead of 10.
 */
typedef struct {
	uint16_t type;	   /* network byte order */
	uint16_t class;	   /* network byte order */
	uint32_t ttl;	   /* network byte order */
	uint16_t rdlength; /* network byte order */
	uint8_t	 rdata[0]; /* variable length */
} librpz_rr_t;

/*
 * The database file might be mapped with different starting addresses
 * by concurrent clients (resolvers), and so all pointers are offsets.
 */
typedef uint32_t librpz_idx_t;
#define LIBRPZ_IDX_NULL 0
#define LIBRPZ_IDX_MIN	1
#define LIBRPZ_IDX_BAD	((librpz_idx_t)-1)
/**
 * Partial decoded results of a set of RPZ queries for a single DNS response
 * or iteration through the mapped file.
 */
typedef int16_t librpz_result_id_t;
typedef struct librpz_result {
	librpz_idx_t	   next_rr;
	librpz_result_id_t hit_id;  /* trigger ID from resolver */
	librpz_policy_t	   zpolicy; /* policy from zone */
	librpz_policy_t	   policy;  /* adjusted by client configuration */
	librpz_dznum_t	   dznum;   /* dnsrpzd zone number */
	librpz_cznum_t	   cznum;   /* librpz client zone number */
	librpz_trig_t	   trig : LIBRPZ_TRIG_SIZE;
	bool		   log : 1; /* log rewrite given librpz_log_level
				     * */
} librpz_result_t;

/**
 * librpz trace or log levels.
 */
typedef enum {
	LIBRPZ_LOG_FATAL = 0,  /* always print fatal errors */
	LIBRPZ_LOG_ERROR = 1,  /* errors have this level */
	LIBRPZ_LOG_TRACE1 = 2, /* big events such as dnsrpzd starts */
	LIBRPZ_LOG_TRACE2 = 3, /* smaller dnsrpzd zone transfers */
	LIBRPZ_LOG_TRACE3 = 4, /* librpz hits */
	LIBRPZ_LOG_TRACE4 = 5, /* librpz lookups */
	LIBRPZ_LOG_INVALID = 999,
} librpz_log_level_t;
typedef librpz_log_level_t(librpz_log_level_val_t)(librpz_log_level_t level);
LIBDEF_F(log_level_val)

/**
 * Logging function that can be supplied by the resolver.
 * @param level is one of librpz_log_level_t
 * @param ctx is for use by the resolver's logging system.
 *	NULL mean a context-free message.
 */
typedef void(librpz_log_fnc_t)(librpz_log_level_t level, void *ctx,
			       const char *buf);

/**
 * Point librpz logging functions to the resolver's choice.
 */
typedef void(librpz_set_log_t)(librpz_log_fnc_t *new_log, const char *prog_nm);
LIBDEF_F(set_log)

/**
 * librpz error messages are put in these buffers.
 * Use a structure instead of naked char* to let the compiler check the length.
 * A function defined with "foo(char buf[120])" can be called with
 * "char sbuf[2]; foo(sbuf)" and suffer a buffer overrun.
 */
typedef struct {
	char c[120];
} librpz_emsg_t;

#ifdef LIBRPZ_HAVE_ATTR
#define LIBRPZ_UNUSED	__attribute__((unused))
#define LIBRPZ_PF(f, l) __attribute__((format(printf, f, l)))
#define LIBRPZ_NORET	__attribute__((__noreturn__))
#else /* ifdef LIBRPZ_HAVE_ATTR */
#define LIBRPZ_UNUSED
#define LIBRPZ_PF(f, l)
#define LIBRPZ_NORET
#endif /* ifdef LIBRPZ_HAVE_ATTR */

typedef bool(librpz_parse_log_opt_t)(librpz_emsg_t *emsg, const char *arg);
LIBDEF_F(parse_log_opt)

typedef void(librpz_vpemsg_t)(librpz_emsg_t *emsg, const char *p, va_list args);
LIBDEF_F(vpemsg)
typedef void(librpz_pemsg_t)(librpz_emsg_t *emsg, const char *p, ...)
	LIBRPZ_PF(2, 3);
LIBDEF_F(pemsg)

typedef void(librpz_vlog_t)(librpz_log_level_t level, void *ctx, const char *p,
			    va_list args);
LIBDEF_F(vlog)
typedef void(librpz_log_t)(librpz_log_level_t level, void *ctx, const char *p,
			   ...) LIBRPZ_PF(3, 4);
LIBDEF_F(log)

typedef void(librpz_fatal_t)(int ex_code, const char *p, ...) LIBRPZ_PF(2, 3);
extern void
librpz_fatal(int ex_code, const char *p, ...) LIBRPZ_PF(2, 3) LIBRPZ_NORET;

typedef void(librpz_rpz_assert_t)(const char *file, unsigned line,
				  const char *p, ...) LIBRPZ_PF(3, 4);
extern void
librpz_rpz_assert(const char *file, unsigned line, const char *p, ...)
	LIBRPZ_PF(3, 4) LIBRPZ_NORET;

typedef void(librpz_rpz_vassert_t)(const char *file, uint line, const char *p,
				   va_list args);
extern void
librpz_rpz_vassert(const char *file, uint line, const char *p,
		   va_list args) LIBRPZ_NORET;

/*
 * As far as clients are concerned, all relative pointers or indexes in a
 * version of the mapped file except trie node parent pointers remain valid
 * forever.  A client must release a version so that it can be garbage
 * collected by the file system.  When dnsrpzd needs to expand the file,
 * it copies the old file to a new, larger file.  Clients can continue
 * using the old file.
 *
 * Versions can also appear in a single file.  Old nodes and trie values
 * within the file are not destroyed until all clients using the version
 * that contained the old values release the version.
 *
 * A client is marked as using version by connecting to the daemon.  It is
 * marked as using all subsequent versions.  A client releases all versions
 * by closing the connection or a range of versions by updating is slot
 * in the shared memory version table.
 *
 * As far as clients are concerned, there are the following possible librpz
 * failures:
 *	- malloc() or other fatal internal librpz problems indicated by
 *	    a failing return from a librpz function
 *	    All operations will fail until client handle is destroyed and
 *	    recreated with librpz_client_detach() and librpz_client_create().
 *	- corrupt database detected by librpz code, corrupt database detected
 *	    by dnsrpzd, or disconnection from the daemon.
 *	    Current operations will fail.
 *
 * Clients assume that the file has already been unlinked before
 *	the corrupt flag is set so that they do not race with the server
 *	over the corruption of a single file.  A client that finds the
 *	corrupt set knows that dnsrpzd has already crashed with
 *	abort() and is restarting.  The client can re-connect to dnsrpzd
 *	and retransmit its configuration, backing off as usual if anything
 *	goes wrong.
 *
 * Searches of the database by a client do not need locks against dnsrpzd or
 *	other clients, but a lock is used to protect changes to the connection
 *	by competing threads in the client.  The client provides functions
 *	to serialize the concurrent use of any single client handle.
 *	Functions that do nothing are appropriate for applications that are
 *	not "threaded" or that do not share client handles among threads.
 *	Otherwise, functions must be provided to librpz_clientcreate().
 *	Something like the following works with pthreads:
 *
 * static void
 * lock(void *mutex) { assert(pthread_mutex_lock(mutex) == 0); }
 *
 * static void
 * unlock(void *mutex) { assert(pthread_mutex_unlock(mutex) == 0); }
 *
 * static void
 * mutex_destroy(void *mutex) { assert(pthread_mutex_destroy(mutex) == 0); }
 *
 *
 *
 * At every instant, all of the data and pointers in the mapped file are valid.
 *	Changes to trie node or other data are always made so that it and
 *	all pointers in and to it remain valid for a time.  Old versions are
 *	eventually discarded.
 *
 * Dnsrpzd periodically defines a new version by setting aside all changes
 *	made since the previous version was defined.  Subsequent changes
 *	made (only!) by dnsrpzd will be part of the next version.
 *
 * To discard an old version, dnsrpzd must know that all clients have stopped
 *	using that version.  Clients do that by using part of the mapped file
 *	to tell dnsrpzd the oldest version that each client is using.
 *	Dnsrpzd assigns each connecting client an entry in the cversions array
 *	in the mapped file.  The client puts version numbers into that entry
 *	to signal to dnsrpzd which versions that can be discarded.
 *	Dnsrpzd is free, as far as that client is concerned, to discard all
 *	numerically smaller versions.  A client can disclaim all versions with
 *	the version number VERSIONS_ALL or 0.
 *
 * The race between a client changing its entry and dnsrpzd discarding a
 *	version is resolved by allowing dnsrpzd to discard all versions
 *	smaller or equal to the client's version number.  If dnsrpzd is in
 *	the midst of discarding or about to discard version N when the
 *	client asserts N, no harm is done.  The client depends only on
 *	the consistency of version N+1.
 *
 * This version mechanism depends in part on not being exercised too frequently
 *	Version numbers are 32 bits long and dnsrpzd creates new versions
 *	at most once every 30 seconds.
 */

/*
 * Lock functions for concurrent use of a single librpz_client_t client handle.
 */
typedef void(librpz_mutex_t)(void *mutex);

/*
 * List of connections to dnsrpzd daemons.
 */
typedef struct librpz_clist librpz_clist_t;

/*
 * Client's handle on dnsrpzd.
 */
typedef struct librpz_client librpz_client_t;

/**
 * Create the list of connections to the dnsrpzd daemon.
 * @param[out] emsg: error message
 * @param lock: start exclusive access to the client handle
 * @param unlock: end exclusive access to the client handle
 * @param mutex_destroy: release the lock
 * @param mutex: pointer to the lock for the client handle
 * @param log_ctx: NULL or resolver's context log messages
 */
typedef librpz_clist_t *(librpz_clist_create_t)(librpz_emsg_t  *emsg,
						librpz_mutex_t *lock,
						librpz_mutex_t *unlock,
						librpz_mutex_t *mutex_destroy,
						void *mutex, void *log_ctx);
LIBDEF_F(clist_create)

/**
 * Release the list of dnsrpzd connections.
 */
typedef void(librpz_clist_detach_t)(librpz_clist_t **clistp);
LIBDEF_F(clist_detach)

/**
 * Create a librpz client handle.
 * @param[out] emsg: error message
 * @param clist: of dnsrpzd connections
 * @param cstr: string of configuration settings separated by ';' or '\n'
 * @param use_expired: true to not ignore expired zones
 * @return client handle or NULL if the handle could not be created
 */
typedef librpz_client_t *(librpz_client_create_t)(librpz_emsg_t	 *emsg,
						  librpz_clist_t *clist,
						  const char	 *cstr,
						  bool		  use_expired);
LIBDEF_F(client_create)

/**
 * Start (if necessary) dnsrpzd and connect to it.
 * @param[out] emsg: error message
 * @param client handle
 * @param optional: true if it is ok if starting the daemon is not allowed
 */
typedef bool(librpz_connect_t)(librpz_emsg_t *emsg, librpz_client_t *client,
			       bool optional);
LIBDEF_F(connect)

/**
 * Start to destroy a librpz client handle.
 * It will not be destroyed until the last set of RPZ queries represented
 * by a librpz_rsp_t ends.
 * @param client handle to be released
 * @return false on error
 */
typedef void(librpz_client_detach_t)(librpz_client_t **clientp);
LIBDEF_F(client_detach)

/**
 * State for a set of RPZ queries for a single DNS response
 * or for listing the database.
 */
typedef struct librpz_rsp librpz_rsp_t;

/**
 * Start a set of RPZ queries for a single DNS response.
 * @param[out] emsg: error message for false return or *rspp=NULL
 * @param[out] rspp created context or NULL
 * @param[out] min_ns_dotsp: NULL or pointer to configured MIN-NS-DOTS value
 * @param client state
 * @param have_rd: RD=1 in the DNS request
 * @param have_do: DO=1 in the DNS request
 * @return false on error
 */
typedef bool(librpz_rsp_create_t)(librpz_emsg_t *emsg, librpz_rsp_t **rspp,
				  int *min_ns_dotsp, librpz_client_t *client,
				  bool have_rd, bool have_do);
LIBDEF_F(rsp_create)

/**
 * Finish RPZ work for a DNS response.
 */
typedef void(librpz_rsp_detach_t)(librpz_rsp_t **rspp);
LIBDEF_F(rsp_detach)

/**
 * Get the final, accumulated result of a set of RPZ queries.
 * Yield LIBRPZ_POLICY_UNDEFINED if
 *  - there were no hits,
 *  - there was a dispositive hit, be we have not recursed and are required
 *	to recurse so that evil DNS authorities will not know we are using RPZ
 *  - we have a hit and have recursed, but later data such as NSIP could
 *	override
 * @param[out] emsg
 * @param[out] result describes the hit
 *	or result->policy=LIBRPZ_POLICY_UNDEFINED without a hit
 * @param[out] result: current policy rewrite values
 * @param recursed: recursion has now been done even if it was not done
 *	when the hit was found
 * @param[in,out] rsp state from librpz_itr_start()
 * @return false on error
 */
typedef bool(librpz_rsp_result_t)(librpz_emsg_t *emsg, librpz_result_t *result,
				  bool recursed, const librpz_rsp_t *rsp);
LIBDEF_F(rsp_result)

/**
 * Might looking for a trigger be worthwhile?
 * @param trig: look for this type of trigger
 * @param ipv6: true if trig is LIBRPZ_TRIG_CLIENT_IP, LIBRPZ_TRIG_IP,
 *	or LIBRPZ_TRIG_NSIP and the IP address is IPv6
 * @return: true if looking could be worthwhile
 */
typedef bool(librpz_have_trig_t)(librpz_trig_t trig, bool ipv6,
				 const librpz_rsp_t *rsp);
LIBDEF_F(have_trig)

/**
 * Might looking for NSDNAME and NSIP triggers be worthwhile?
 * @return: true if looking could be worthwhile
 */
typedef bool(librpz_have_ns_trig_t)(const librpz_rsp_t *rsp);
LIBDEF_F(have_ns_trig)

/**
 * Convert the found client IP trie key to a CIDR block
 * @param[out] emsg
 * @param[out] prefix trigger
 * @param[in,out] rsp state from librpz_itr_start()
 * @return false on error
 */
typedef bool(librpz_rsp_clientip_prefix_t)(librpz_emsg_t   *emsg,
					   librpz_prefix_t *prefix,
					   librpz_rsp_t	   *rsp);
LIBDEF_F(rsp_clientip_prefix)

/**
 * Compute the owner name of the found or result trie key, usually to log it.
 * An IP address key might be returned as 8.0.0.0.127.rpz-client-ip.
 * example.com. might be a qname trigger.  example.com.rpz-nsdname. could
 * be an NSDNAME trigger.
 * @param[out] emsg
 * @param[out] owner domain
 * @param[in,out] rsp state from librpz_itr_start()
 * @return false on error
 */
typedef bool(librpz_rsp_domain_t)(librpz_emsg_t	      *emsg,
				  librpz_domain_buf_t *owner,
				  librpz_rsp_t	      *rsp);
LIBDEF_F(rsp_domain)

/**
 * Get the next RR of the LIBRPZ_POLICY_RECORD result after an initial use of
 * librpz_rsp_result() or librpz_itr_node() or after a previous use of
 * librpz_rsp_rr().  The RR is in uncompressed wire format including type,
 * class, ttl and length in network byte order.
 * @param[out] emsg
 * @param[out] typep: optional host byte order record type or ns_t_invalid (0)
 * @param[out] classp: class such as ns_c_in
 * @param[out] ttlp: TTL
 * @param[out] rrp: optional malloc() buffer containing the next RR or
 *	NULL after the last RR
 * @param[out] result: current policy rewrite values
 * @param qname: used construct a wildcard CNAME
 * @param qname_size
 * @param[in,out] rsp state from librpz_itr_start()
 * @return false on error
 */
typedef bool(librpz_rsp_rr_t)(librpz_emsg_t *emsg, uint16_t *typep,
			      uint16_t *classp, uint32_t *ttlp,
			      librpz_rr_t **rrp, librpz_result_t *result,
			      const uint8_t *qname, size_t qname_size,
			      librpz_rsp_t *rsp);
LIBDEF_F(rsp_rr)

/**
 * Get the next RR of the LIBRPZ_POLICY_RECORD result.
 * @param[out] emsg
 * @param[out] ttlp: TTL
 * @param[out] rrp: malloc() buffer with SOA RR without owner name
 * @param[out] result: current policy rewrite values
 * @param[out] origin: SOA owner name
 * @param[out] origin_size
 * @param[in,out] rsp state from librpz_itr_start()
 * @return false on error
 */
typedef bool(librpz_rsp_soa_t)(librpz_emsg_t *emsg, uint32_t *ttlp,
			       librpz_rr_t **rrp, librpz_domain_buf_t *origin,
			       librpz_result_t *result, librpz_rsp_t *rsp);
LIBDEF_F(rsp_soa)

/**
 * Get the SOA serial number for a policy zone to compare with a known value
 * to check whether a zone transfer is complete.
 */
typedef bool(librpz_soa_serial_t)(librpz_emsg_t *emsg, uint32_t *serialp,
				  const char *domain_nm, librpz_rsp_t *rsp);
LIBDEF_F(soa_serial)

/**
 * Save the current policy checking state.
 * @param[out] emsg
 * @param[in,out] rsp state from librpz_itr_start()
 * @return false on error
 */
typedef bool(librpz_rsp_push_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
LIBDEF_F(rsp_push)
#define LIBRPZ_RSP_STACK_DEPTH 3

/**
 * Restore the previous policy checking state.
 * @param[out] emsg
 * @param[out] result: NULL or restored policy rewrite values
 * @param[in,out] rsp state from librpz_itr_start()
 * @return false on error
 */
typedef bool(librpz_rsp_pop_t)(librpz_emsg_t *emsg, librpz_result_t *result,
			       librpz_rsp_t *rsp);
LIBDEF_F(rsp_pop)

/**
 * Discard the most recently save policy checking state.
 * @param[out] emsg
 * @param[out] result: NULL or restored policy rewrite values
 * @return false on error
 */
typedef bool(librpz_rsp_pop_discard_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
LIBDEF_F(rsp_pop_discard)

/**
 * Disable a zone.
 * @param[out] emsg
 * @param znum
 * @param[in,out] rsp state from librpz_itr_start()
 * @return false on error
 */
typedef bool(librpz_rsp_forget_zone_t)(librpz_emsg_t *emsg, librpz_cznum_t znum,
				       librpz_rsp_t *rsp);
LIBDEF_F(rsp_forget_zone)

/**
 * Apply RPZ to an IP address.
 * @param[out] emsg
 * @param addr: address to check
 * @param ipv6: true for 16 byte IPv6 instead of 4 byte IPv4
 * @param trig LIBRPZ_TRIG_CLIENT_IP, LIBRPZ_TRIG_IP, or LIBRPZ_TRIG_NSIP
 * @param hit_id: caller chosen
 * @param recursed: recursion has been done
 * @param[in,out] rsp state from librpz_itr_start()
 * @return false on error
 */
typedef bool(librpz_ck_ip_t)(librpz_emsg_t *emsg, const void *addr, uint family,
			     librpz_trig_t trig, librpz_result_id_t hit_id,
			     bool recursed, librpz_rsp_t *rsp);
LIBDEF_F(ck_ip)

/**
 * Apply RPZ to a wire-format domain.
 * @param[out] emsg
 * @param domain in wire format
 * @param domain_size
 * @param trig LIBRPZ_TRIG_QNAME or LIBRPZ_TRIG_NSDNAME
 * @param hit_id: caller chosen
 * @param recursed: recursion has been done
 * @param[in,out] rsp state from librpz_itr_start()
 * @return false on error
 */
typedef bool(librpz_ck_domain_t)(librpz_emsg_t *emsg, const uint8_t *domain,
				 size_t domain_size, librpz_trig_t trig,
				 librpz_result_id_t hit_id, bool recursed,
				 librpz_rsp_t *rsp);
LIBDEF_F(ck_domain)

/**
 * Ask dnsrpzd to refresh a zone.
 * @param[out] emsg error message
 * @param librpz_domain_t domain to refresh
 * @param client context
 * @return false after error
 */
typedef bool(librpz_zone_refresh_t)(librpz_emsg_t *emsg, const char *domain,
				    librpz_rsp_t *rsp);
LIBDEF_F(zone_refresh)

/**
 * Get a string describing the database
 * @param license: include the license
 * @param cfiles: include the configuration file names
 * @param listens: include the local notify IP addresses
 * @param[out] emsg error message if the result is null
 * @param client context
 * @return malloc'ed string or NULL after error
 */
typedef char *(librpz_db_info_t)(librpz_emsg_t *emsg, bool license, bool cfiles,
				 bool listens, librpz_rsp_t *rsp);
LIBDEF_F(db_info)

/**
 * Start a context for listing the nodes and/or zones in the mapped file
 * @param[out] emsg: error message for false return or *rspp=NULL
 * @param[out] rspp: created context or NULL
 * @param client context
 * @return false after error
 */
typedef bool(librpz_itr_start_t)(librpz_emsg_t *emsg, librpz_rsp_t **rspp,
				 librpz_client_t *client);
LIBDEF_F(itr_start)

/**
 * Get mapped file memory allocation statistics.
 * @param[out] emsg: error message
 * @param rsp state from librpz_itr_start()
 * @return malloc'ed string or NULL after error
 */
typedef char *(librpz_mf_stats_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
LIBDEF_F(mf_stats)

/**
 * Get versions currently used by clients.
 * @param[out] emsg: error message
 * @param[in,out] rsp: state from librpz_itr_start()
 * @return malloc'ed string or NULL after error
 */
typedef char *(librpz_vers_stats_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
LIBDEF_F(vers_stats)

/**
 * Allocate a string describing the next zone or "" after the last zone.
 * @param[out] emsg
 * @param all_zones to list all instead of only requested zones
 * @param[in,out] rsp state from librpz_rsp_start()
 * @return malloc'ed string or NULL after error
 */
typedef char *(librpz_itr_zone_t)(librpz_emsg_t *emsg, bool all_zones,
				  librpz_rsp_t *rsp);
LIBDEF_F(itr_zone)

/**
 * Describe the next trie node while dumping the database.
 * @param[out] emsg
 * @param[out] result describes node
 *	or result->policy=LIBRPZ_POLICY_UNDEFINED after the last node.
 * @param all_zones to list all instead of only requested zones
 * @param[in,out] rsp state from librpz_itr_start()
 * @return: false on error
 */
typedef bool(librpz_itr_node_t)(librpz_emsg_t *emsg, librpz_result_t *result,
				bool all_zones, librpz_rsp_t *rsp);
LIBDEF_F(itr_node)

/**
 * RPZ policy to string with a backup buffer of POLICY2STR_SIZE size
 */
typedef const char *(librpz_policy2str_t)(librpz_policy_t policy, char *buf,
					  size_t buf_size);
#define POLICY2STR_SIZE sizeof("policy xxxxxx")
LIBDEF_F(policy2str)

/**
 * Trigger type to string.
 */
typedef const char *(librpz_trig2str_t)(librpz_trig_t trig);
LIBDEF_F(trig2str)

/**
 * Convert a number of seconds to a zone file duration string
 */
typedef const char *(librpz_secs2str_t)(time_t secs, char *buf,
					size_t buf_size);
#define SECS2STR_SIZE sizeof("1234567w7d24h59m59s")
LIBDEF_F(secs2str)

/**
 * Parse a duration with 's', 'm', 'h', 'd', and 'w' units.
 */
typedef bool(librpz_str2secs_t)(librpz_emsg_t *emsg, time_t *val,
				const char *str0);
LIBDEF_F(str2secs)

/**
 * Translate selected rtypes to strings
 */
typedef const char *(librpz_rtype2str_t)(uint type, char *buf, size_t buf_size);
#define RTYPE2STR_SIZE sizeof("type xxxxx")
LIBDEF_F(rtype2str)

/**
 * Local version of ns_name_ntop() for portability.
 */
typedef int(librpz_domain_ntop_t)(const u_char *src, char *dst, size_t dstsiz);
LIBDEF_F(domain_ntop)

/**
 * Local version of ns_name_pton().
 */
typedef int(librpz_domain_pton2_t)(const char *src, u_char *dst, size_t dstsiz,
				   size_t *dstlen, bool lower);
LIBDEF_F(domain_pton2)

typedef union socku socku_t;
typedef socku_t *(librpz_mk_inet_su_t)(socku_t *su, const struct in_addr *addrp,
				       in_port_t port);
LIBDEF_F(mk_inet_su)

typedef socku_t *(librpz_mk_inet6_su_t)(socku_t		      *su,
					const struct in6_addr *addrp,
					uint32_t scope_id, in_port_t port);
LIBDEF_F(mk_inet6_su)

typedef bool(librpz_str2su_t)(socku_t *sup, const char *str);
LIBDEF_F(str2su)

typedef char *(librpz_su2str_t)(char *str, size_t str_len, const socku_t *su);
LIBDEF_F(su2str)
#define SU2STR_SIZE (INET6_ADDRSTRLEN + 1 + 6 + 1)

/**
 * default path to dnsrpzd
 */
LIBDEF(const char *, librpz_dnsrpzd_path)

#undef LIBDEF

/*
 * This is the dlopen() interface to librpz.
 */
typedef const struct {
	const char			 *dnsrpzd_path;
	const char			 *version;
	librpz_parse_log_opt_t		 *parse_log_opt;
	librpz_log_level_val_t		 *log_level_val;
	librpz_set_log_t		 *set_log;
	librpz_vpemsg_t			 *vpemsg;
	librpz_pemsg_t			 *pemsg;
	librpz_vlog_t			 *vlog;
	librpz_log_t			 *log;
	librpz_fatal_t *fatal		  LIBRPZ_NORET;
	librpz_rpz_assert_t *rpz_assert	  LIBRPZ_NORET;
	librpz_rpz_vassert_t *rpz_vassert LIBRPZ_NORET;
	librpz_clist_create_t		 *clist_create;
	librpz_clist_detach_t		 *clist_detach;
	librpz_client_create_t		 *client_create;
	librpz_connect_t		 *connect;
	librpz_client_detach_t		 *client_detach;
	librpz_rsp_create_t		 *rsp_create;
	librpz_rsp_detach_t		 *rsp_detach;
	librpz_rsp_result_t		 *rsp_result;
	librpz_have_trig_t		 *have_trig;
	librpz_have_ns_trig_t		 *have_ns_trig;
	librpz_rsp_clientip_prefix_t	 *rsp_clientip_prefix;
	librpz_rsp_domain_t		 *rsp_domain;
	librpz_rsp_rr_t			 *rsp_rr;
	librpz_rsp_soa_t		 *rsp_soa;
	librpz_soa_serial_t		 *soa_serial;
	librpz_rsp_push_t		 *rsp_push;
	librpz_rsp_pop_t		 *rsp_pop;
	librpz_rsp_pop_discard_t	 *rsp_pop_discard;
	librpz_rsp_forget_zone_t	 *rsp_forget_zone;
	librpz_ck_ip_t			 *ck_ip;
	librpz_ck_domain_t		 *ck_domain;
	librpz_zone_refresh_t		 *zone_refresh;
	librpz_db_info_t		 *db_info;
	librpz_itr_start_t		 *itr_start;
	librpz_mf_stats_t		 *mf_stats;
	librpz_vers_stats_t		 *vers_stats;
	librpz_itr_zone_t		 *itr_zone;
	librpz_itr_node_t		 *itr_node;
	librpz_policy2str_t		 *policy2str;
	librpz_trig2str_t		 *trig2str;
	librpz_secs2str_t		 *secs2str;
	librpz_str2secs_t		 *str2secs;
	librpz_rtype2str_t		 *rtype2str;
	librpz_domain_ntop_t		 *domain_ntop;
	librpz_domain_pton2_t		 *domain_pton2;
	librpz_mk_inet_su_t		 *mk_inet_su;
	librpz_mk_inet6_su_t		 *mk_inet6_su;
	librpz_str2su_t			 *str2su;
	librpz_su2str_t			 *su2str;
} librpz_0_t;
extern librpz_0_t librpz_def_0;

/*
 * Future versions can be upward compatible by defining LIBRPZ_DEF as
 * librpz_X_t.
 */
#define LIBRPZ_DEF     librpz_def_0
#define LIBRPZ_DEF_STR "librpz_def_0"

typedef librpz_0_t librpz_t;
extern librpz_t	  *librpz;

#if LIBRPZ_LIB_OPEN == 2
#include <dlfcn.h>

/**
 * link-load librpz
 * @param[out] emsg: error message
 * @param[in,out] dl_handle: NULL or pointer to new dlopen handle
 * @param[in] path: librpz.so path
 * @return address of interface structure or NULL on failure
 */
static inline librpz_t *
librpz_lib_open(librpz_emsg_t *emsg, void **dl_handle, const char *path) {
	void	 *handle;
	librpz_t *new_librpz;

	emsg->c[0] = '\0';

	/*
	 * Close a previously opened handle on librpz.so.
	 */
	if (dl_handle != NULL && *dl_handle != NULL) {
		if (dlclose(*dl_handle) != 0) {
			snprintf(emsg->c, sizeof(librpz_emsg_t),
				 "dlopen(NULL): %s", dlerror());
			return (NULL);
		}
		*dl_handle = NULL;
	}

	/*
	 * First try the main executable of the process in case it was
	 * linked to librpz.
	 * Do not worry if we cannot search the main executable of the process.
	 */
	handle = dlopen(NULL, RTLD_NOW | RTLD_LOCAL);
	if (handle != NULL) {
		new_librpz = dlsym(handle, LIBRPZ_DEF_STR);
		if (new_librpz != NULL) {
			if (dl_handle != NULL) {
				*dl_handle = handle;
			}
			return (new_librpz);
		}
		if (dlclose(handle) != 0) {
			snprintf(emsg->c, sizeof(librpz_emsg_t),
				 "dlsym(NULL, " LIBRPZ_DEF_STR "): %s",
				 dlerror());
			return (NULL);
		}
	}

	if (path == NULL || path[0] == '\0') {
		snprintf(emsg->c, sizeof(librpz_emsg_t),
			 "librpz not linked and no dlopen() path provided");
		return (NULL);
	}

	handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
	if (handle == NULL) {
		snprintf(emsg->c, sizeof(librpz_emsg_t), "dlopen(%s): %s", path,
			 dlerror());
		return (NULL);
	}
	new_librpz = dlsym(handle, LIBRPZ_DEF_STR);
	if (new_librpz != NULL) {
		if (dl_handle != NULL) {
			*dl_handle = handle;
		}
		return (new_librpz);
	}
	snprintf(emsg->c, sizeof(librpz_emsg_t),
		 "dlsym(%s, " LIBRPZ_DEF_STR "): %s", path, dlerror());
	dlclose(handle);
	return (NULL);
}
#elif defined(LIBRPZ_LIB_OPEN)
/*
 * Statically link to the librpz.so DSO on systems without dlopen()
 */
static inline librpz_t *
librpz_lib_open(librpz_emsg_t *emsg, void **dl_handle, const char *path) {
	(void)(path);

	if (dl_handle != NULL) {
		*dl_handle = NULL;
	}

#if LIBRPZ_LIB_OPEN == 1
	emsg->c[0] = '\0';
	return (&LIBRPZ_DEF);
#else  /* if LIBRPZ_LIB_OPEN == 1 */
	snprintf(emsg->c, sizeof(librpz_emsg_t),
		 "librpz not available via ./configure");
	return (NULL);
#endif /* LIBRPZ_LIB_OPEN */
}
#endif /* LIBRPZ_LIB_OPEN */