Codebase list dnsdist / debian/1.0.0_alpha2-2 README.md
debian/1.0.0_alpha2-2

Tree @debian/1.0.0_alpha2-2 (Download .tar.gz)

README.md @debian/1.0.0_alpha2-2raw · history · blame

  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
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
dnsdist
-------
`dnsdist` is a highly DNS-, DoS- and abuse-aware loadbalancer. Its goal in
life is to route traffic to the best server, delivering top performance
to legitimate users while shunting or blocking abusive traffic.

`dnsdist` is dynamic, in the sense that its configuration can be changed at
runtime, and that its statistics can be queried from a console-like
interface.

Compiling
---------
`dnsdist` depends on boost, Lua or luajit and a pretty recent C++
compiler (g++ 4.8 or higher, clang 3.5 or higher). It can optionally use libsodium
for encrypted communications with its client.

To compile on CentOS 6 / RHEL6, use this script to install a working compiler:

```
wget -O /etc/yum.repos.d/slc6-devtoolset.repo http://linuxsoft.cern.ch/cern/devtoolset/slc6-devtoolset.repo
yum install devtoolset-2
scl enable devtoolset-2 bash
./configure
make
```

On other recent platforms, installing a Lua and the system C++ compiler should be enough. 

Packaged
--------
We build packages for `dnsdist` on our [repositories](https://repo.powerdns.com). In addition
`dnsdist` has been packaged for FreeBSD and can be found on https://freshports.org/dns/dnsdist

Examples
--------

The absolute minimum configuration:

```
# dnsdist 2001:4860:4860::8888 8.8.8.8
```

This will listen on 0.0.0.0:53 and forward queries to the two listed IP
addresses, with a sensible load balancing policy.

Here is a more complete configuration:

```
$ cat /etc/dnsdist.conf
newServer({address="2001:4860:4860::8888", qps=1})
newServer({address="2001:4860:4860::8844", qps=1})
newServer({address="2620:0:ccc::2", qps=10})
newServer({address="2620:0:ccd::2", name="dns1", qps=10})
newServer("192.168.1.2")
setServerPolicy(firstAvailable) -- first server within its QPS limit

$ dnsdist --local=0.0.0.0:5200
Marking downstream [2001:4860:4860::8888]:53 as 'up'
Marking downstream [2001:4860:4860::8844]:53 as 'up'
Marking downstream [2620:0:ccc::2]:53 as 'up'
Marking downstream [2620:0:ccd::2]:53 as 'up'
Marking downstream 192.168.1.2:53 as 'up'
Listening on 0.0.0.0:5200
> 
```

We can now send queries to port 5200, and get answers:

```
$ dig -t aaaa powerdns.com @127.0.0.1 -p 5200 +short
2001:888:2000:1d::2
```

Note that `dnsdist` offered us a prompt above, and on it we can get some
statistics:

```
> showServers()
#   Address                   State     Qps    Qlim Ord Wt    Queries   Drops Drate   Lat Pools
0   [2001:4860:4860::8888]:53    up     0.0       1   1  1          1       0   0.0   0.0
1   [2001:4860:4860::8844]:53    up     0.0       1   1  1          0       0   0.0   0.0
2   [2620:0:ccc::2]:53           up     0.0      10   1  1          0       0   0.0   0.0
3   [2620:0:ccd::2]:53           up     0.0      10   1  1          0       0   0.0   0.0
4   192.168.1.2:53               up     0.0       0   1  1          0       0   0.0   0.0
All                                     0.0                         1       0     
```

Here we also see our configuration. 5 downstream servers have been configured, of
which the first 4 have a QPS limit (of 1, 1, 10 and 10 queries per second,
respectively). The final server has no limit, which we can easily test:

```
$ for a in {0..1000}; do dig powerdns.com @127.0.0.1 -p 5200 +noall > /dev/null; done
> showServers()
#   Address                   State     Qps    Qlim Ord Wt    Queries   Drops Drate   Lat Pools
0   [2001:4860:4860::8888]:53    up     1.0       1   1  1          7       0   0.0   1.6
1   [2001:4860:4860::8844]:53    up     1.0       1   1  1          6       0   0.0   0.6
2   [2620:0:ccc::2]:53           up    10.3      10   1  1         64       0   0.0   2.4
3   [2620:0:ccd::2]:53           up    10.3      10   1  1         63       0   0.0   2.4
4   192.168.1.2:53               up   125.8       0   1  1        671       0   0.0   0.4
All                                   145.0                       811       0     
```

Note that the first 4 servers were all limited to near their configured QPS,
and that our final server was taking up most of the traffic. No queries were
dropped, and all servers remain up.

To force a server down, try:

```
> getServer(0):setDown()
> showServers()
#   Address                   State     Qps    Qlim Ord Wt    Queries   Drops Drate   Lat Pools
0   [2001:4860:4860::8888]:53  DOWN     0.0       1   1  1          8       0   0.0   0.0 
...
```

The 'DOWN' in all caps means it was forced down. A lower case 'down'
would've meant that `dnsdist` itself had concluded the server was down.
Similarly, setUp() forces a server to be up, and setAuto() returns it to the
default availability-probing.

To change the QPS for a server:
```
> getServer(0):setQPS(1000)
```

By default, the availability of a downstream server is checked by regularly
sending an A query for "a.root-servers.net.". A different query type and target
can be specified by passing, respectively, the 'checkType' and 'checkName'
parameters to `newServer`. The default behavior is to consider any valid response
with a RCODE different from ServFail as valid. If the 'mustResolve' parameter
of `newServer` is set to true, a response will only be considered valid if
its RCODE differs from NXDomain, ServFail and Refused.

```
newServer({address="192.0.2.1", checkType="AAAA", checkName="a.root-servers.net.", mustResolve=true})
```

In order to provide the downstream server with the address of the real client,
or at least the one talking to `dnsdist`, the 'useClientSubnet' parameter can be used
when declaring a new server. This parameter indicates whether an EDNS Client Subnet option
should be added to the request. If the incoming request already contains an EDNS Client Subnet value,
it will not be overriden unless setECSOverride is set to true. The source prefix-length may be
configured with:
```
> setECSSourcePrefixV4(24)
> setECSSourcePrefixV6(56)
```

TCP timeouts
------------

By default, a 2 seconds timeout is enforced on the TCP connection from the client,
meaning that a connection will be closed if the query can't be read in less than 2s
or if the answer can't be sent in less than 2s. This can be configured with:
```
> setTCPRecvTimeout(5)
> setTCPSendTimeout(5)
```

The same kind of timeouts is enforced on the TCP connections to the downstream servers.
The default value of 30s can be modified by passing the `tcpRecvTimeout` and `tcpSendTimeout`
parameters to `newServer`:
```
newServer({address="192.0.2.1", tcpRecvTimeout=10, tcpSendTimeout=10})
```

Source address
--------------

In multi-homed setups, it can be useful to be able to select the source address or the outgoing
interface used by `dnsdist` to contact a downstream server.
This can be done by using the `source` parameter:
```
newServer({address="192.0.2.1", source="192.0.2.127"})
newServer({address="192.0.2.1", source="eth1"})
newServer({address="192.0.2.1", source="192.0.2.127@eth1"})
```

The supported values for `source` are:
 * an IPv4 or IPv6 address, which must exist on the system
 * an interface name
 * an IPv4 or IPv6 address followed by '@' then an interface name

Specifying the interface name is only supported on system having IP_PKTINFO.


Configuration management
------------------------
At startup, configuration is read from the command line and the
configuration file.  The config can also be inspected and changed from the
console.  Sadly, our architecture does not allow us to serialize the running
configuration for you. However, we do try to offer the next best thing:
`delta()`.

`delta()` shows all commands entered that changed the configuration. So
adding a new downstream server with `newServer()` would show up, but
`showServers()` or even `delta()` itself would not.

It is suggested to study the output of `delta()` carefully before appending
it to your configuration file. 

```
> setACL("192.0.2.0/24")
> showACL()
192.0.2.0/24
> delta()
# Wed Dec 23 2015 15:15:35 CET
setACL("192.0.2.0/24")
> addACL("127.0.0.1/8")
> showACL()
192.0.2.0/24
127.0.0.1/8
> delta()
# Wed Dec 23 2015 15:15:35 CET
setACL("192.0.2.0/24")
# Wed Dec 23 2015 15:15:44 CET
addACL("127.0.0.1/8")
>
```


Webserver
---------
To visually interact with `dnsdist`, try adding:
```
webserver("127.0.0.1:8083", "supersecret")
```

to the configuration, and point your browser at http://127.0.0.1:8083 and
log in with any username, and that password. Enjoy!

Server pools
------------
Now for some cool stuff. Let's say we know we're getting a whole bunch of
traffic for a domain used in DoS attacks, for example 'sh43354.cn'. We can
do two things with this kind of traffic. Either we block it outright, like
this:

```
> addDomainBlock("sh43354.cn.")
```

Or we configure a server pool dedicated to receiving the nasty stuff:

```
> newServer({address="192.168.1.3", pool="abuse"})
> addPoolRule({"sh43353.cn.", "ezdns.it."}, "abuse")
```

The wonderful thing about this last solution is that it can also be used for
things where a domain might possibly be legit, but it is still causing load
on the system and slowing down the internet for everyone. With such an abuse
server, 'bad traffic' still gets a chance of an answer, but without
impacting the rest of the world (too much).

We can similarly add clients to the abuse server:

```
> addPoolRule({"192.168.12.0/24", "192.168.13.14"}, "abuse")
```

To define a pool that should receive a QPS-limited amount of traffic, do:

```
> addQPSPoolRule("com.", 10000, "gtld-cluster")
```


Both `addDomainBlock` and `addPoolRule` end up the list of Rules 
and Actions (for which see below).

Servers can be added or removed to pools with:
```
> getServer(7):addPool("abuse")
> getServer(4):rmPool("abuse")
```


Rules
-----
Rules can be inspected with `showRules()`, and can be deleted with
`rmRule()`.  Rules are evaluated in order, and this order can be changed
with `mvRule(from, to)` (see below for exact semantics).

Rules have selectors and actions. Current selectors are:
 * Source address
 * Query type
 * Query domain
 * QPS Limit total
 * QPS Limit per IP address or subnet
 * QClass (QClassRule)
 * QType (QTypeRule)
 * RegexRule on query name
 * Packet requests DNSSEC processing
 * Query received over UDP or TCP

Special rules are:
 * `AndRule{rule1, rule2}`, which only matches if all of its subrules match
 * `OrRule{rule1, rule2}`, which matches if at least one of its subrules match
 * `NotRule(rule)`, which matches if its subrule does not match

Current actions are:
 * Drop (DropAction)
 * Route to a pool (PoolAction)
 * Return with TC=1 (truncated, ie, instruction to retry with TCP)
 * Force a ServFail, NotImp or Refused answer
 * Send out a crafted response (NXDOMAIN or "real" data)
 * Delay a response by n milliseconds (DelayAction)
 * Modify query to remove RD bit

Rules can be added via:
 * addAction(DNS rule, DNS Action)
 * addAnyTCRule()
 * addDelay(DNS rule, delay in milliseconds)
 * addDisableValidationRule(DNS rule)
 * addDomainBlock(domain)
 * addDomainSpoof(domain, IPv4[, IPv6])
 * addDomainCNAMESpoof(domain, CNAME)
 * addLuaAction(DNS rule, lua function)
 * addNoRecurseRule(DNS rule)
 * addPoolRule(DNS rule, destination pool)
 * addQPSLimit(DNS rule, qps limit)
 * addQPSPoolRule(DNS rule, qps limit, destination pool)

A DNS rule can be:
 * an AllRule
 * an AndRule
 * a MaxQPSIPRule
 * a MaxQPSRule
 * a NetmaskGroupRule
 * a NotRule
 * an OrRule
 * a QClassRule
 * a QTypeRule
 * a RegexRule
 * a SuffixMatchNodeRule
 * a TCPRule

A convenience function `makeRule()` is supplied which will make a NetmaskGroupRule for you or a SuffixMatchNodeRule
depending on how you call it. `makeRule("0.0.0.0/0")` will for example match all IPv4 traffic, `makeRule{"be","nl","lu"}` will
match all Benelux DNS traffic.

More power
----------
More powerful things can be achieved by defining a function called
`blockFilter()` in the configuration file, which can decide to drop traffic
on any reason it wants. If you return 'true' from there, the query will get
blocked.

A demo on how to do this and many other things can be found on
https://github.com/powerdns/pdns/blob/master/pdns/dnsdistconf.lua and
the exact definition of `blockFilter()` is at the end of this document.

ANY or whatever to TC
---------------------
The `blockFilter()` also gets passed read/writable copy of the DNS Header,
via `dq.dh`.
If you invoke setQR(1) on that, `dnsdist` knows you turned the packet into
a response, and will send the answer directly to the original client.

If you also called setTC(1), this will tell the remote client to move to
TCP/IP, and in this way you can implement ANY-to-TCP even for downstream
servers that lack this feature.

Note that calling `addAnyTCRule()` achieves the same thing, without
involving Lua.

Rules for traffic exceeding QPS limits
--------------------------------------
Traffic that exceeds a QPS limit, in total or per IP (subnet) can be matched by a rule.

For example:

```
addDelay(MaxQPSIPRule(5, 32, 48), 100)
```

This measures traffic per IPv4 address and per /48 of IPv6, and if traffic for such 
an address (range) exceeds 5 qps, it gets delayed by 100ms.

As another example:

```
addAction(MaxQPSIPRule(5), NoRecurseAction())
```

This strips the Recursion Desired (RD) bit from any traffic per IPv4 or IPv6 /64 
that exceeds 5 qps. This means any those traffic bins is allowed to make a recursor do 'work'
for only 5 qps.

If this is not enough, try:

```
addAction(MaxQPSIPRule(5), DropAction())
-- or
addAction(MaxQPSIPRule(5), TCAction())
```

This will respectively drop traffic exceeding that 5 QPS limit per IP or range, or return it with TC=1, forcing
clients to fall back to TCP/IP.

To turn this per IP or range limit into a global limit, use MaxQPSRule(5000) instead of MaxQPSIPRule.

Lua actions in rules
--------------------
While we can pass every packet through the `blockFilter()` functions, it is also
possible to configure `dnsdist` to only hand off some packets for Lua inspection. 
If you think Lua is too slow for your query load, or if you are doing heavy processing in Lua, 
this may make sense.

To select specific packets for Lua attention, use `addLuaAction(x, func)`,
where x is either a netmask, or a domain suffix, or a table of netmasks or a
table of domain suffixes.  This is identical to how `addPoolRule()` selects.

The function should look like this:
```
function luarule(dq)
        if(dq.qtype==35) -- NAPTR
        then
                return DNSAction.Pool, "abuse" -- send to abuse pool
        else
                return DNSAction.None, ""      -- no action
        end
end
```

Valid return values for `LuaAction` functions are:
 * DNSAction.Allow: let the query pass, skipping other rules
 * DNSAction.Delay: delay the response for the specified milliseconds (UDP-only)
 * DNSAction.Drop: drop the query
 * DNSAction.HeaderModify: indicate that the query has been turned into a response
 * DNSAction.None: continue to the next rule
 * DNSAction.Nxdomain: return a response with a NXDomain rcode
 * DNSAction.Pool: use the specified pool to forward this query
 * DNSAction.Spoof: spoof the response using the supplied IPv4 (A), IPv6 (AAAA) or string (CNAME) value

DNSSEC
------
To provide DNSSEC service from a separate pool, try:
```
newServer({address="2001:888:2000:1d::2", pool="dnssec"})
newServer({address="2a01:4f8:110:4389::2", pool="dnssec"})
setDNSSECPool("dnssec")
topRule()
```

This routes all queries with a DNSSEC OK (DO) or CD bit set to on to the "dnssec" pool.
The final `topRule()` command moves this rule to the top, so it gets evaluated first.

Regular Expressions
-------------------
`RegexRule()` matches a regular expression on the query name, and it works like this:

```
addAction(RegexRule("[0-9]{5,}"), DelayAction(750)) -- milliseconds
addAction(RegexRule("[0-9]{4,}\\.cn$"), DropAction())
```

This delays any query for a domain name with 5 or more consecutive digits in it.
The second rule drops anything with more than 4 consecutive digits within a .CN domain.

Note that the query name is presented without a trailing dot to the regex.
The regex is applied case insensitively. 

Inspecting live traffic
-----------------------
This is still much in flux, but for now, try:

 * `grepq(Netmask|DNS Name|100ms [, n])`: shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms
 * `grepq({"::1", "powerdns.com", "100ms"} [, n])`: shows the last n queries and responses matching the specified client address AND range (Netmask) AND the specified DNS Name AND slower than 100ms
 * `topBandwidth(top)`: show top-`top` clients that consume the most bandwidth over length of ringbuffer
 * `topQueries(20)`: shows the top-20 queries
 * `topQueries(20,2)`: shows the top-20 two-level domain queries (so `topQueries(20,1)` only shows TLDs)
 * `topResponses(20, 2)`: top-20 servfail responses (use ,3 for NXDOMAIN)
 * `topSlow([top][, limit][, labels])`: show `top` queries slower than `limit` milliseconds, grouped by last `labels` labels

For example:
```
> grepq("127.0.0.1/24")
-11.9   127.0.0.1:52599                                 16127 nxdomain.powerdns.com.    A             RD    Question
-11.7   127.0.0.1:52599                                 16127 nxdomain.powerdns.com.    A     175.6    RD    Non-Existent domain
> grepq("powerdns.com")
-38.7   127.0.0.1:52599                                 16127 nxdomain.powerdns.com.    A             RD    Question
-38.6   127.0.0.1:52599                                 16127 nxdomain.powerdns.com.    A     175.6    RD    Non-Existent domain
```

Live histogram of latency
-------------------------
```
> showResponseLatency()
Average response latency: 78.84 msec
   msec	
   0.10	
   0.20	.
   0.40	**********************
   0.80	***********
   1.60	.
   3.20	
   6.40	.
  12.80	*
  25.60	*
  51.20	*
 102.40	**********************************************************************
 204.80	*************************
 409.60	**
 819.20	:
1638.40	.
```

Where : stands for 'half a star' and . for 'less than half a star, but
something was there'.

Per domain or subnet QPS limiting
---------------------------------
If certain domains or source addresses are generating onerous amounts of
traffic, you can put ceilings on the amount of traffic you are willing to
forward:

```
> addQPSLimit("h4xorbooter.xyz.", 10)
> addQPSLimit({"130.161.0.0/16", "145.14.0.0/16"} , 20)
> addQPSLimit({"nl.", "be."}, 1)
> showRules()
#     Matches Rule                                               Action
0           0 h4xorbooter.xyz.                                   qps limit to 10
1           0 130.161.0.0/16, 145.14.0.0/16                      qps limit to 20
2           0 nl., be.                                           qps limit to 1
```

To delete a limit (or a rule in general):
```
> rmRule(1)
> showRules()
#     Matches Rule                                               Action
0           0 h4xorbooter.xyz.                                   qps limit to 10
1           0 nl., be.                                           qps limit to 1
```

Delaying answers
----------------
Sometimes, runaway scripts will hammer your servers with back-to-back
queries.  While it is possible to drop such packets, this may paradoxically
lead to more traffic. 

An attractive middleground is to delay answers to such back-to-back queries,
causing a slowdown on the side of the source of the traffic.

To do so, use:
```
> addDelay("yourdomain.in.ua.", 500)
> addDelay({"65.55.37.0/24"}, 500)
```
This will delay responses for questions to the mentioned domain, or coming
from the configured subnet, by half a second.

Like the QPSLimits and other rules, the delaying instructions can be
inspected or edited using showRule(), rmRule(), topRule(), mvRule() etc.

Dynamic load balancing
----------------------
The default load balancing policy is called 'leastOutstanding', which means 
we pick the server with the least queries 'in the air' (and within those, the one with the lowest 'order', and within those, the one with the lowest latency). 

Another policy, 'firstAvailable', picks the first server that has not
exceeded its QPS limit gets the traffic.  

A further policy, 'wrandom' assigns queries randomly, but based on the
'weight' parameter passed to `newServer`. `whashed` is a similar weighted policy,
but assigns questions with identical hash to identical servers, allowing for
better cache concentration ('sticky queries').

If you don't like the default policies you can create your own, like this
for example:

```
counter=0
function luaroundrobin(servers, dq)
	 counter=counter+1
	 return servers[1+(counter % #servers)]
end

setServerPolicyLua("luaroundrobin", luaroundrobin)
```

Incidentally, this is similar to setting: `setServerPolicy(roundrobin)`
which uses the C++ based roundrobin policy.

Lua server policies 
-------------------
If the built in rules do not suffice to pick a server pool, full flexibility is available from Lua. For example:

```
newServer("192.168.1.2")
newServer({address="8.8.4.4", pool="numbered"})

function splitSetup(servers, dq)
         if(string.match(dq.qname:toString(), "%d"))
         then
                print("numbered pool")
                return leastOutstanding.policy(getPoolServers("numbered"), dq)
         else
                print("standard pool")
                return leastOutstanding.policy(servers, dq)
         end
end

setServerPolicyLua("splitsetup", splitSetup)
```

This will forward queries containing a number to the pool of "numbered" 
servers, and will apply the default load balancing policy to all other
queries.

Dynamic Rule Generation
-----------------------
To set dynamic rules, based on recent traffic, define a function called `maintenance()` in Lua. It will
get called every second, and from this function you can set rules to block traffic based on statistics.

As an example:

```
function maintenance()
	addDynBlocks(exceedQRate(20, 10), "Exceeded query rate", 60)
end
```

This will dynamically block all hosts that exceeded 20 queries/s as measured
over the past 10 seconds, and the dynamic block will last for 60 seconds.

Dynamic blocks in force are displayed with `showDynBlocks()` and can be cleared
with `clearDynBlocks()`. Full set of `exceed` functions is listed in the table of
all functions below.


Running it for real
-------------------
First run on the command line, and generate a key:

```
# dnsdist
> makeKey()
setKey("sepuCcHcQnSAZgNbNPCCpDWbujZ5esZJmrt/wh6ldkQ=")
```

Now add this setKey line to `dnsdistconf.lua`, and also add:

```
controlSocket("0.0.0.0") -- or add portnumber too
```

Then start `dnsdist` as a daemon, and then connect to it:
```
# dnsdist --daemon
# dnsdist --client
> 
```

Please note that, without libsodium support, 'makeKey()' will return
setKey("plaintext") and the communication between the client and the
server will not be encrypted.

ACL, who can use dnsdist
------------------------
For safety reasons, by default only private networks can use `dnsdist`, see below
how to query and change the ACL:

```
> showACL()
127.0.0.0/8
10.0.0.0/8
(...)
::1/128
fc00::/7
fe80::/10
> addACL("130.161.0.0/16")
> setACL({"::/0"}) -- resets the list to this array
> showACL()
::/0
```

Carbon/Graphite/Metronome
-------------------------
To emit metrics to Graphite, or any other software supporting the Carbon protocol, use:
```
carbonServer('ip-address-of-carbon-server', 'ourname', 30)
```

Where 'ourname' can be used to override your hostname, and '30' is the
reporting interval in seconds.  The last two arguments can be omitted.  The
latest version of [PowerDNS
Metronome](https://github.com/ahupowerdns/metronome) comes with attractive
graphs for `dnsdist` by default.

DNSCrypt
--------
`dnsdist`, when compiled with --enable-dnscrypt, can be used as a DNSCrypt server,
uncurving queries before forwarding them to downstream servers and curving responses back.
To make `dnsdist` listen to incoming DNSCrypt queries on 127.0.0.1 port 8443,
with a provider name of "2.providername", using a resolver certificate and associated key
stored respectively in the `resolver.cert` and `resolver.key` files, the `addDnsCryptBind()`
directive can be used:

```
addDNSCryptBind("127.0.0.1:8443", "2.providername", "/path/to/resolver.cert", "/path/to/resolver.key")
```

To generate the provider and resolver certificates and keys, you can simply do:

```
> generateDNSCryptProviderKeys("/path/to/providerPublic.key", "/path/to/providerPrivate.key")
Provider fingerprint is: E1D7:2108:9A59:BF8D:F101:16FA:ED5E:EA6A:9F6C:C78F:7F91:AF6B:027E:62F4:69C3:B1AA
> generateDNSCryptCertificate("/path/to/providerPrivate.key", "/path/to/resolver.cert", "/path/to/resolver.key", serial, validFrom, validUntil)
```

Ideally, the certificates and keys should be generated on an offline dedicated hardware and not on the resolver.
The resolver key should be regularly rotated and should never touch persistent storage, being stored in a tmpfs
with no swap configured.

You can display the currently configured DNSCrypt binds with:
```
> showDNSCryptBinds()
#   Address              Provider Name        Serial   Validity              P. Serial P. Validity
0   127.0.0.1:8443       2.name               14       2016-04-10 08:14:15   0         -
```

If you forgot to write down the provider fingerprint value after generating the provider keys, you can use `printDNSCryptProviderFingerprint()` to retrieve it later:
```
> printDNSCryptProviderFingerprint("/path/to/providerPublic.key")
Provider fingerprint is: E1D7:2108:9A59:BF8D:F101:16FA:ED5E:EA6A:9F6C:C78F:7F91:AF6B:027E:62F4:69C3:B1AA
```

All functions and types
-----------------------
Within `dnsdist` several core object types exist:
 * Server: generated with newServer, represents a downstream server
 * ComboAddress: represents an IP address and port
 * DNSName: represents a domain name
 * NetmaskGroup: represents a group of netmasks
 * QPSLimiter: implements a QPS-based filter
 * SuffixMatchNode: represents a group of domain suffixes for rapid testing of membership
 * DNSHeader: represents the header of a DNS packet

The existence of most of these objects can mostly be ignored, unless you
plan to write your own hooks and policies, but it helps to understand an
expressions like:

```
> getServer(0).order=12         -- set order of server 0 to 12
> getServer(0):addPool("abuse") -- add this server to the abuse pool
```
The '.' means 'order' is a data member, while the ':' meand addPool is a member function.

Here are all functions:

 * Practical
   * `shutdown()`: shut down `dnsdist`
   * quit or ^D: exit the console
   * `webserver(address, password)`: launch a webserver with stats on that address with that password
 * ACL related:
   * `addACL(netmask)`: add to the ACL set who can use this server
   * `setACL({netmask, netmask})`: replace the ACL set with these netmasks. Use `setACL({})` to reset the list, meaning no one can use us
   * `showACL()`: show our ACL set
 * Network related:
   * `addLocal(netmask, [false])`: add to addresses we listen on. Second optional parameter sets TCP/IP or not.
   * `setLocal(netmask, [false])`: reset list of addresses we listen on to this address. Second optional parameter sets TCP/IP or not.
 * Blocking related:
   * `addDomainBlock(domain)`: block queries within this domain
 * Carbon/Graphite/Metronome statistics related:
   * `carbonServer(serverIP, [ourname], [interval])`: report statistics to serverIP using our hostname, or 'ourname' if provided, every 'interval' seconds
 * Control socket related:
   * `makeKey()`: generate a new server access key, emit configuration line ready for pasting
   * `setKey(key)`: set access key to that key. 
   * `testCrypto()`: test of the crypto all works
   * `controlSocket(addr)`: open a control socket on this address / connect to this address in client mode
 * Diagnostics and statistics
   * `dumpStats()`: print all statistics we gather
   * `grepq(Netmask|DNS Name|100ms [, n])`: shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms
   * `grepq({"::1", "powerdns.com", "100ms"} [, n])`: shows the last n queries and responses matching the specified client address AND range (Netmask) AND the specified DNS Name AND slower than 100ms
   * `topQueries(n[, labels])`: show top 'n' queries, as grouped when optionally cut down to 'labels' labels
   * `topResponses(n, kind[, labels])`: show top 'n' responses with RCODE=kind (0=NO Error, 2=ServFail, 3=ServFail), as grouped when optionally cut down to 'labels' labels
   * `topSlow([top][, limit][, labels])`: show `top` queries slower than `limit` milliseconds, grouped by last `labels` labels   
   * `topBandwidth(top)`: show top-`top` clients that consume the most bandwidth over length of ringbuffer
   * `showResponseLatency()`: show a plot of the response time latency distribution
 * Logging related
   * `infolog(string)`: log at level info
   * `warnlog(string)`: log at level warning
   * `errlog(string)`: log at level error
 * Server related:
   * `newServer("ip:port")`: instantiate a new downstream server with default settings
   * `newServer({address="ip:port", qps=1000, order=1, weight=10, pool="abuse", retries=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName="a.root-servers.net.", checkType="A", mustResolve=false, useClientSubnet=true, source="address|interface name|address@interface"})`:
instantiate a server with additional parameters
   * `showServers()`: output all servers
   * `getServer(n)`: returns server with index n 
   * `getServers()`: returns a table with all defined servers
   * `rmServer(n)`: remove server with index n
   * `rmServer(server)`: remove this server object
 * Server member functions:
   * `addPool(pool)`: add this server to that pool
   * `getOutstanding()`: this *returns* the number of outstanding queries (doesn't print it!)
   * `rmPool(pool)`: remove server from that pool
   * `setQPS(n)`: set the QPS setting to n
   * `setAuto()`: set this server to automatic availability testing
   * `setDown()`: force this server to be down
   * `setUp()`: force this server to be UP
   * `isUp()`: if this server is available
 * Server member data:
   * `upStatus`: if `dnsdist` considers this server available (overridden by `setDown()` and `setUp()`)
   * `order`: order of this server in order-based server selection policies
   * `weight`: weight of this server in weighted server selection policies
 * Rule related:
   * `AllRule()`: matches all traffic
   * `AndRule()`: matches if all sub-rules matches
   * `DNSSECRule()`: matches queries with the DO flag set
   * `MaxQPSIPRule(qps, v4Mask=32, v6Mask=64)`: matches traffic exceeding the qps limit per subnet
   * `MaxQPSRule(qps)`: matches traffic not exceeding this qps limit
   * `NetmaskGroupRule()`: matches traffic from the specified network range
   * `NotRule()`: matches if the sub-rule does not match
   * `OrRule()`: matches if at least one of the sub-rules matches
   * `QClassRule(qclass)`: matches queries with the specified qclass (numeric)
   * `QTypeRule(qtype)`: matches queries with the specified qtype
   * `RegexRule(regex)`: matches the query name against the supplied regex
   * `SuffixMatchNodeRule()`: matches based on a group of domain suffixes for rapid testing of membership
   * `TCPRule(tcp)`: matches question received over TCP if `tcp` is true, over UDP otherwise
 * Rule management related:
   * `showRules()`: show all defined rules (Pool, Block, QPS, addAnyTCRule)
   * `rmRule(n)`: remove rule n
   * `mvRule(from, to)`: move rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule,
     in which case the rule will be moved to the last position.
   * `topRule()`: move the last rule to the first position
 * Built-in Actions for Rules:
   * `AllowAction()`: let these packets go through
   * `DelayAction()`: delay the response by the specified amount of milliseconds (UDP-only)
   * `DisableValidationAction()`: set the CD bit in the question, let it go through
   * `DropAction()`: drop these packets
   * `LogAction()`: Log a line for each query, to the specified file if any, to the console (require verbose) otherwise
   * `NoRecurseAction()`: strip RD bit from the question, let it go through
   * `PoolAction()`: set the packet into the specified pool
   * `QPSPoolAction()`: set the packet into the specified pool only if it does not exceed the specified QPS limits
   * `QPSAction()`: drop these packets if the QPS limits are exceeded
   * `RCodeAction()`: reply immediatly by turning the query into a response with the specified rcode
   * `SpoofAction()`: forge a response with the specified IPv4 (for an A query) or IPv6 (for an AAAA). If you specify two addresses, the first one should be an IPv4 and will be used for A, the second an IPv6 for an AAAA
   * `SpoofCNAMEAction()`: forge a response with the specified CNAME value
   * `TCAction()`: create answer to query with TC and RD bits set, to move to TCP/IP
 * Specialist rule generators
   * `addAnyTCRule()`: generate TC=1 answers to ANY queries received over UDP, moving them to TCP
   * `addDomainSpoof(domain, ip[, ip6])`: generate answers for A queries using the ip parameter (AAAA if ip is an IPv6). If ip6 is supplied, generate answers for AAAA queries too
   * `addDomainCNAMESpoof(domain, cname)`: generate CNAME answers for queries using the specified value
   * `addDisableValidationRule(domain)`: set the CD flags to 1 for all queries matching the specified domain
   * `addNoRecurseRule(domain)`: clear the RD flag for all queries matching the specified domain
   * `setDNSSECPool()`: move queries requesting DNSSEC processing to this pool
 * Policy member data:
   * `name`: the policy name
   * `policy`: the policy function
 * Pool related:
   * `addPoolRule(domain, pool)`: send queries to this domain to that pool
   * `addPoolRule({domain, domain}, pool)`: send queries to these domains to that pool
   * `addPoolRule(netmask, pool)`: send queries to this netmask to that pool
   * `addPoolRule({netmask, netmask}, pool)`: send queries to these netmasks to that pool  
   * `addQPSPoolRule(x, limit, pool)`: like `addPoolRule`, but only select at most 'limit' queries/s for this pool
   * `getPoolServers(pool)`: return servers part of this pool
 * Lua Action related:
   * `addLuaAction(x, func)`: where 'x' is all the combinations from `addPoolRule`, and func is a 
      function with parameters remote, qname, qtype, dh and len, which returns an action to be taken 
      on this packet. Good for rare packets but where you want to do a lot of processing.
 * Server selection policy related:
   * `setServerPolicy(policy)`: set server selection policy to that policy
   * `setServerPolicyLua(name, function)`: set server selection policy to one named 'name' and provided by 'function'
   * `showServerPolicy()`: show name of currently operational server selection policy
   * `newServerPolicy(name, function)`: create a policy object from a Lua function
 * Available policies:
   * `firstAvailable`: Pick first server that has not exceeded its QPS limit, ordered by the server 'order' parameter
   * `whashed`: Weighted hashed ('sticky') distribution over available servers, based on the server 'weight' parameter
   * `wrandom`: Weighted random over available servers, based on the server 'weight' parameter
   * `roundrobin`: Simple round robin over available servers
   * `leastOutstanding`: Send traffic to downstream server with least outstanding queries, with the lowest 'order', and within that the lowest recent latency
 * Shaping related:
   * `addQPSLimit(domain, n)`: limit queries within that domain to n per second
   * `addQPSLimit({domain, domain}, n)`: limit queries within those domains (together) to n per second
   * `addQPSLimit(netmask, n)`: limit queries within that netmask to n per second
   * `addQPSLimit({netmask, netmask}, n)`: limit queries within those netmasks (together) to n per second   
 * Delaying related:
   * `addDelay(domain, n)`: delay answers within that domain by n milliseconds
   * `addDelay({domain, domain}, n)`: delay answers within those domains (together) by n milliseconds
   * `addDelay(netmask, n)`: delay answers within that netmask by n milliseconds
   * `addDelay({netmask, netmask}, n)`: delay answers within those netmasks (together) by n milliseconds
 * Answer changing functions:
   * `truncateTC(bool)`: if set (default) truncate TC=1 answers so they are actually empty. Fixes an issue for PowerDNS Authoritative Server 2.9.22.
   * `fixupCase(bool)`: if set (default to no), rewrite the first qname of the question part of the answer to match the one from the query. It is only useful when you have a downstream server that messes up the case of the question qname in the answer
 * Dynamic Block related:
   * `maintenance()`: called every second by dnsdist if defined, call functions below from it
   * `clearDynBlocks()`: clear all dynamic blocks
   * `showDynBlocks()`: show dynamic blocks in force
   * `addDynBlocks(addresses, message[, seconds])`: block the set of addresses with message `msg`, for `seconds` seconds (10 by default)
   * `exceedServFails(rate, seconds)`: get set of addresses that exceed `rate` servails/s over `seconds` seconds
   * `exceedNXDOMAINs(rate, seconds)`: get set of addresses that exceed `rate` NXDOMAIN/s over `seconds` seconds
   * `exceedRespByterate(rate, seconds)`: get set of addresses that exeeded `rate` bytes/s answers over `seconds` seconds
   * `exceedQRate(rate, seconds)`: get set of address that exceed `rate` queries/s over `seconds` seconds
   * `exceedQTypeRate(type, rate, seconds)`: get set of address that exceed `rate` queries/s for queries of type `type` over `seconds` seconds
 * Advanced functions for writing your own policies and hooks
   * ComboAddress related:
     * `newCA(address)`: return a new ComboAddress
     * `getPort()`: return the port number
     * `tostring()`: return in human-friendly format
     * `toString()`: alias for `tostring()`
     * `tostringWithPort()`: return in human-friendly format, with port number
     * `toStringWithPort()`: alias for `tostringWithPort()`
   * DNSName related:
     * `newDNSName(name)`: make a DNSName based on this .-terminated name
     * member `isPartOf(dnsname)`: is this dnsname part of that dnsname
     * member `tostring()`: return as a human friendly . terminated string
     * member `toString()`: alias for `tostring()`
   * DNSQuestion related:
     * member `dh`: DNSHeader
     * member `len`: the question length
     * member `localaddr`: ComboAddress of the local bind this question was received on
     * member `qname`: DNSName of this question
     * member `qclass`: QClass (as an unsigned integer) of this question
     * member `qtype`: QType (as an unsigned integer) of this question
     * member `remoteaddr`: ComboAddress of the remote client
     * member `rcode`: RCode of this question
     * member `size`: the total size of the buffer starting at `dh`
     * member `tcp`: whether this question was received over a TCP socket
   * DNSHeader related
     * member `getRD()`: get recursion desired flag
     * member `setRD(bool)`: set recursion desired flag
     * member `setTC(bool)`: set truncation flag (TC)
     * member `setQR(bool)`: set Query Response flag (setQR(true) indicates an *answer* packet)
     * member `getCD()`: get checking disabled flag
     * member `setCD(bool)`: set checking disabled flag
   * NetmaskGroup related
     * function `newNMG()`: returns a NetmaskGroup
     * member `addMask(mask)`: adds `mask` to the NetmaskGroup
     * member `match(ComboAddress)`: checks if ComboAddress is matched by this NetmaskGroup
     * member `clear()`: clears the NetmaskGroup
     * member `size()`: returns number of netmasks in this NetmaskGroup
   * QPSLimiter related:
     * `newQPSLimiter(rate, burst)`: configure a QPS limiter with that rate and that burst capacity
     * member `check()`: check if this QPSLimiter has a token for us. If yes, you must use it.
   * SuffixMatchNode related:
     * `newSuffixMatchNode()`: returns a new SuffixMatchNode
     * member `check(DNSName)`: returns true if DNSName is matched by this group
     * member `add(DNSName)`: add this DNSName to the node
 * Tuning related:
   * `setTCPRecvTimeout(n)`: set the read timeout on TCP connections from the client, in seconds
   * `setTCPSendTimeout(n)`: set the write timeout on TCP connections from the client, in seconds
   * `setMaxTCPClientThreads(n)`: set the maximum of TCP client threads, handling TCP connections
   * `setMaxUDPOutstanding(n)`: set the maximum number of outstanding UDP queries to a given backend server. This can only be set at configuration time
 * DNSCrypt related:
   * `addDNSCryptBind("127.0.0.1:8443", "provider name", "/path/to/resolver.cert", "/path/to/resolver.key"):` listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of "provider name", using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files
   * `generateDNSCryptProviderKeys("/path/to/providerPublic.key", "/path/to/providerPrivate.key"):` generate a new provider keypair
   * `generateDNSCryptCertificate("/path/to/providerPrivate.key", "/path/to/resolver.cert", "/path/to/resolver.key", serial, validFrom, validUntil):` generate a new resolver private key and related certificate, valid from the `validFrom` timestamp until the `validUntil` one, signed with the provider private key
   * `printDNSCryptProviderFingerprint("/path/to/providerPublic.key")`: display the fingerprint of the provided resolver public key
   * `showDNSCryptBinds():`: display the currently configured DNSCrypt binds

All hooks
---------
`dnsdist` can call Lua per packet if so configured, and will do so with the following hooks:

  * `bool blockFilter(ComboAddress, DNSQuestion)`: if defined, called for every function. If this
    returns true, the packet is dropped. If false is returned, `dnsdist` will check if the DNSHeader indicates
    the packet is now a query response. If so, `dnsdist` will answer the client directly with the modified packet.
  * `server policy(candidates, DNSQuestion)`: if configured with `setServerPolicyLua()`
    gets called for every packet. Candidates is a table of potential servers to pick from, ComboAddress is the 
    address of the requestor, DNSName and qtype describe name and type of query. DNSHeader meanwhile is available for 
    your inspection.