mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-08-17 21:25:50 +02:00
Compare commits
668
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37ffb1d394 | ||
|
|
87603429a1 | ||
|
|
cc6375e909 | ||
|
|
d1e812a4af | ||
|
|
7ac31bc1d5 | ||
|
|
37c3bef544 | ||
|
|
7ca106cc8c | ||
|
|
45abfa8720 | ||
|
|
ce2067ec6b | ||
|
|
0b04be414e | ||
|
|
c90fd40a5e | ||
|
|
bea2b17277 | ||
|
|
dee84257a1 | ||
|
|
b33f5f66ec | ||
|
|
a46c9197c4 | ||
|
|
bfc64ade3c | ||
|
|
900142fb79 | ||
|
|
5aa65f431b | ||
|
|
f8655d2f5b | ||
|
|
a3ff369cfd | ||
|
|
c76429ab57 | ||
|
|
0a9116f856 | ||
|
|
9f4a9249c6 | ||
|
|
8b860e8f12 | ||
|
|
c41e1a09ab | ||
|
|
59c1ec5a95 | ||
|
|
0b39c4232f | ||
|
|
bcb13a174b | ||
|
|
d525f6ab69 | ||
|
|
f89c4c4774 | ||
|
|
5326670faf | ||
|
|
244a09bfa3 | ||
|
|
2b7969bc84 | ||
|
|
f3d116387d | ||
|
|
e6c5255466 | ||
|
|
6e9d214816 | ||
|
|
c1ae463da5 | ||
|
|
865852d3f7 | ||
|
|
4eb2bdf2b1 | ||
|
|
062ad0eb69 | ||
|
|
ca89f7290c | ||
|
|
33ae9fd843 | ||
|
|
4c76f1c003 | ||
|
|
ccdc0e3c53 | ||
|
|
9cb69874f6 | ||
|
|
12aae4fd6f | ||
|
|
1bdf2b2b6a | ||
|
|
159c7e4d6c | ||
|
|
38d57b0f80 | ||
|
|
666b066bf6 | ||
|
|
d714dfe8d8 | ||
|
|
340a6185d2 | ||
|
|
d00c045084 | ||
|
|
d090698555 | ||
|
|
11f61b6b26 | ||
|
|
4b00c5f76b | ||
|
|
31ffc5a6a9 | ||
|
|
aa1b565b41 | ||
|
|
3d3b875a70 | ||
|
|
3d42b1fffa | ||
|
|
3e8ccb027f | ||
|
|
05643589cf | ||
|
|
b616c4f833 | ||
|
|
cb4985f527 | ||
|
|
098f932df8 | ||
|
|
4ac7881829 | ||
|
|
bcf49eaf96 | ||
|
|
9e96c6e3aa | ||
|
|
9a9f012352 | ||
|
|
386ff5debe | ||
|
|
d7ee842525 | ||
|
|
ea1f00ebad | ||
|
|
b1ed30d5f4 | ||
|
|
aa0f26e3fa | ||
|
|
9e21fb7fac | ||
|
|
8b919a3780 | ||
|
|
32f6afbb48 | ||
|
|
576fe7d4c8 | ||
|
|
27f450e8fa | ||
|
|
ecad71c519 | ||
|
|
8726449421 | ||
|
|
fcf7df71d6 | ||
|
|
4ea644f5a2 | ||
|
|
e25608d1be | ||
|
|
d1b2324429 | ||
|
|
be6bbb470a | ||
|
|
59e74885b7 | ||
|
|
2b6c544f69 | ||
|
|
632234b5e4 | ||
|
|
8bc54c4380 | ||
|
|
bbbbb3c533 | ||
|
|
75cf6d0f86 | ||
|
|
c653b8424b | ||
|
|
9548455690 | ||
|
|
bbf446f1a1 | ||
|
|
9fa2964d95 | ||
|
|
d0eb5cad47 | ||
|
|
e9d742c16d | ||
|
|
2547a0a9bb | ||
|
|
3bdd807610 | ||
|
|
7fc590cda5 | ||
|
|
9525cdb01b | ||
|
|
21a64ef247 | ||
|
|
5cfd778608 | ||
|
|
3973cd8f26 | ||
|
|
c12bf8692c | ||
|
|
327e1f4b10 | ||
|
|
847107403f | ||
|
|
d469081eda | ||
|
|
345e5e82d3 | ||
|
|
c6541a2def | ||
|
|
3708097870 | ||
|
|
69af0f10e9 | ||
|
|
747dc4e3ab | ||
|
|
6c7416128c | ||
|
|
515b892024 | ||
|
|
3af6fd7980 | ||
|
|
0d01be50bc | ||
|
|
8a32f9003b | ||
|
|
7bf176eeba | ||
|
|
c6af78bfd3 | ||
|
|
8eb31df3d0 | ||
|
|
7176710b7f | ||
|
|
1b1694a959 | ||
|
|
6cebdd2baf | ||
|
|
60d8b26ba1 | ||
|
|
83a017d3d1 | ||
|
|
18b09fe9c1 | ||
|
|
f59b1a96b1 | ||
|
|
8fb41f5c00 | ||
|
|
64959c8097 | ||
|
|
2a6846f30c | ||
|
|
00f301d35f | ||
|
|
f4ad52dffd | ||
|
|
a50b796a40 | ||
|
|
e5c52a7032 | ||
|
|
a0d9d48d8f | ||
|
|
1275afa125 | ||
|
|
371b9f08ef | ||
|
|
265c199445 | ||
|
|
bf659c8362 | ||
|
|
3dc4715109 | ||
|
|
b3dd2b5b98 | ||
|
|
5a1e91af35 | ||
|
|
a21136cf66 | ||
|
|
ebb63767cd | ||
|
|
07efa0f4ea | ||
|
|
1b8d87e202 | ||
|
|
1e37e00a27 | ||
|
|
628c970ecc | ||
|
|
f59885053a | ||
|
|
dbaa5194a6 | ||
|
|
5b7a514119 | ||
|
|
b6696c1342 | ||
|
|
bfb4e20f43 | ||
|
|
c73c662fce | ||
|
|
45afaf3e08 | ||
|
|
c04451cc58 | ||
|
|
6d721dd6bd | ||
|
|
408a0cfd31 | ||
|
|
939fabd809 | ||
|
|
3ddabf26f3 | ||
|
|
e2277ca9d1 | ||
|
|
351ef6e252 | ||
|
|
620c85ee32 | ||
|
|
7d242d400f | ||
|
|
cb659ab13b | ||
|
|
74909b66b4 | ||
|
|
896e4fea2a | ||
|
|
3385bcc255 | ||
|
|
5e1193f19d | ||
|
|
2e5c620f2c | ||
|
|
13a68aac89 | ||
|
|
d4fadf55a8 | ||
|
|
a2b261f8b2 | ||
|
|
556d5407e2 | ||
|
|
66dcc95a78 | ||
|
|
16734a0e04 | ||
|
|
91138fc32a | ||
|
|
cfceb6487d | ||
|
|
de0f903d37 | ||
|
|
67bec33e20 | ||
|
|
46e50e861f | ||
|
|
b479dfd33d | ||
|
|
cf4b56d883 | ||
|
|
c77be35670 | ||
|
|
763431c059 | ||
|
|
089261572a | ||
|
|
055ebe3d0b | ||
|
|
33bc592a83 | ||
|
|
076fb3bc53 | ||
|
|
10316f091f | ||
|
|
a14c4fa6e5 | ||
|
|
9d6df1964b | ||
|
|
897bb0c0e6 | ||
|
|
50b728aa08 | ||
|
|
25c1232d22 | ||
|
|
5814e0f5b6 | ||
|
|
3ca6d2e5a2 | ||
|
|
d4151219a2 | ||
|
|
e0d36e6561 | ||
|
|
7a1ba7785d | ||
|
|
370236cb65 | ||
|
|
91972c229d | ||
|
|
144978d633 | ||
|
|
62fc91f7f1 | ||
|
|
636f742ef0 | ||
|
|
f0b77ed022 | ||
|
|
0b3c133dd3 | ||
|
|
757eff88f5 | ||
|
|
3137c88a43 | ||
|
|
08e0563483 | ||
|
|
9157324788 | ||
|
|
f918da13d2 | ||
|
|
e8e30550df | ||
|
|
5dbc854a48 | ||
|
|
af57e5163d | ||
|
|
56b91454ba | ||
|
|
121cb15d67 | ||
|
|
265eedb0d3 | ||
|
|
26429c9457 | ||
|
|
72904a3366 | ||
|
|
ff1a7ec42e | ||
|
|
05cd134de9 | ||
|
|
960da40475 | ||
|
|
01cabbebc1 | ||
|
|
a66e16cb31 | ||
|
|
49d295755d | ||
|
|
b858018446 | ||
|
|
71a535a6f2 | ||
|
|
28a388b517 | ||
|
|
51d548563c | ||
|
|
21cd9936d8 | ||
|
|
e474ca2619 | ||
|
|
f809bfa145 | ||
|
|
4993c646e7 | ||
|
|
53fed5315c | ||
|
|
21895f6013 | ||
|
|
82ce090a35 | ||
|
|
fcbc34bd7f | ||
|
|
0720031ce0 | ||
|
|
23db41c292 | ||
|
|
d60b281055 | ||
|
|
a7cfe2bf65 | ||
|
|
d9cd60a41c | ||
|
|
a39a7704af | ||
|
|
a61d10f715 | ||
|
|
a4e4f424d1 | ||
|
|
bcec1a7593 | ||
|
|
38396319c7 | ||
|
|
ee874fab01 | ||
|
|
8cbb13f251 | ||
|
|
87fe8871b0 | ||
|
|
55f7bbc051 | ||
|
|
8e39c9c1cb | ||
|
|
7549bddfda | ||
|
|
44f5800da9 | ||
|
|
4cf4659a1d | ||
|
|
080d9d6540 | ||
|
|
77b79fde5d | ||
|
|
1e4e8cb68a | ||
|
|
0d8ce6f075 | ||
|
|
23ac4b02e3 | ||
|
|
8b93a85a7d | ||
|
|
4fe0d98ff4 | ||
|
|
a30026a7d8 | ||
|
|
9d068a12f5 | ||
|
|
144d35120c | ||
|
|
014cc821ba | ||
|
|
428524a8c8 | ||
|
|
2d49dd1494 | ||
|
|
995b01a9b7 | ||
|
|
665a98e47b | ||
|
|
fe106d38b2 | ||
|
|
75ac5e0fe0 | ||
|
|
d452ba59b5 | ||
|
|
c2c5b5522c | ||
|
|
34d75b1994 | ||
|
|
b61f24e8d3 | ||
|
|
072142cfa2 | ||
|
|
afe9b3e83c | ||
|
|
e4ef5a6156 | ||
|
|
ad041b9b8e | ||
|
|
45dd63d7c8 | ||
|
|
8ab18cb0f7 | ||
|
|
4e2ca85842 | ||
|
|
71d292580b | ||
|
|
d4b612e48b | ||
|
|
3d7ad2de3c | ||
|
|
24cb32dc6b | ||
|
|
4263f2a692 | ||
|
|
8459b5f55f | ||
|
|
6acc20abf4 | ||
|
|
498039108b | ||
|
|
d165625c4d | ||
|
|
af6cbba475 | ||
|
|
14f657933c | ||
|
|
1808c93e15 | ||
|
|
8078891a28 | ||
|
|
9a102eed3e | ||
|
|
02c493bd2d | ||
|
|
e2f8a1cb82 | ||
|
|
78a3c4e93d | ||
|
|
5eb7ff35ca | ||
|
|
4e1eab891b | ||
|
|
31a3035c60 | ||
|
|
aaab8cb352 | ||
|
|
fc0dcf12c3 | ||
|
|
4664c66f06 | ||
|
|
56f3cd33ec | ||
|
|
a64adc395f | ||
|
|
66f896ddbf | ||
|
|
ee5ee20071 | ||
|
|
fc3fc7a1f3 | ||
|
|
7be0e5b814 | ||
|
|
d4cfee1f00 | ||
|
|
f7059107b3 | ||
|
|
7a528cdf53 | ||
|
|
f90f5a2583 | ||
|
|
76ee7a352a | ||
|
|
148e8a69c9 | ||
|
|
39e4ba0e78 | ||
|
|
afda1a8bcc | ||
|
|
092a325270 | ||
|
|
cc6322ac0f | ||
|
|
2487f435f6 | ||
|
|
423e1a95a8 | ||
|
|
8ffff3c0a3 | ||
|
|
39e01091e7 | ||
|
|
8527bd4aff | ||
|
|
1dec098624 | ||
|
|
283f2a5edb | ||
|
|
0db56d8a0d | ||
|
|
065d2ad96f | ||
|
|
d7f2306161 | ||
|
|
66c8bd4ad1 | ||
|
|
725929734e | ||
|
|
60f51c1197 | ||
|
|
85abd17e90 | ||
|
|
ef69508048 | ||
|
|
97854e6bc5 | ||
|
|
01af814844 | ||
|
|
6de373e04c | ||
|
|
4a6d702edf | ||
|
|
0bad5716d4 | ||
|
|
55fe583de8 | ||
|
|
55aefcad19 | ||
|
|
fa07056f79 | ||
|
|
5990dde29b | ||
|
|
f9074eb440 | ||
|
|
54318b8c35 | ||
|
|
3bbe7a2e0b | ||
|
|
0f56fd3042 | ||
|
|
0d3ecd25df | ||
|
|
96d4800692 | ||
|
|
d13686227c | ||
|
|
e60174a000 | ||
|
|
556b4efb8a | ||
|
|
cf3d92a7a7 | ||
|
|
cf19737ee6 | ||
|
|
3dca36a649 | ||
|
|
aab6da9362 | ||
|
|
8d472c8cf8 | ||
|
|
baf8056ba5 | ||
|
|
17631b6fd8 | ||
|
|
1b3eb34cd1 | ||
|
|
6d3f7cc4e0 | ||
|
|
3f94669a88 | ||
|
|
395f328d4a | ||
|
|
4ba9161e75 | ||
|
|
8638d6c019 | ||
|
|
95296a2528 | ||
|
|
9dd64e357f | ||
|
|
8ef2cb4705 | ||
|
|
9b55215eb6 | ||
|
|
9f104a42d0 | ||
|
|
21e851b0fb | ||
|
|
227ae5b7a4 | ||
|
|
9739014da1 | ||
|
|
c49eefba59 | ||
|
|
c6bbc4ea23 | ||
|
|
f7d8e3f4bd | ||
|
|
dd7404f467 | ||
|
|
30a1d557bd | ||
|
|
a377c6a9d5 | ||
|
|
f47e78c543 | ||
|
|
f9fd2676f9 | ||
|
|
c858743222 | ||
|
|
9ab79dc0dc | ||
|
|
ffed368bfc | ||
|
|
5abd6c8da1 | ||
|
|
d44b91ec12 | ||
|
|
399a165cc3 | ||
|
|
97ebee32d0 | ||
|
|
29f4a9ab78 | ||
|
|
b6a93eae31 | ||
|
|
3fced109b9 | ||
|
|
77e7e73a32 | ||
|
|
adb8eda081 | ||
|
|
0cc33e29e7 | ||
|
|
9e9d7ba0a7 | ||
|
|
74530fdb64 | ||
|
|
2f7bd77250 | ||
|
|
dccc665658 | ||
|
|
2139a8572c | ||
|
|
e65070ce6f | ||
|
|
b9bf0872c2 | ||
|
|
2904445bd2 | ||
|
|
f17580e460 | ||
|
|
0f80e5e78a | ||
|
|
a8bf62f962 | ||
|
|
234defa371 | ||
|
|
20ee8c10f5 | ||
|
|
06f41d9d84 | ||
|
|
f0cf79146c | ||
|
|
7eeb7cc78c | ||
|
|
46e703c989 | ||
|
|
d26b183ba6 | ||
|
|
ff8c2ed1bc | ||
|
|
8359474330 | ||
|
|
64627b1a63 | ||
|
|
87700fea40 | ||
|
|
bbe801da93 | ||
|
|
f35ae11ff4 | ||
|
|
677d67c29c | ||
|
|
6adf9e1459 | ||
|
|
85af59c734 | ||
|
|
30478d3e17 | ||
|
|
288f022dd6 | ||
|
|
8a359e134b | ||
|
|
c9447139c8 | ||
|
|
822422cbfb | ||
|
|
34da89510c | ||
|
|
c5ef04b7b4 | ||
|
|
372b4a1c12 | ||
|
|
3e103c2983 | ||
|
|
6b51790054 | ||
|
|
9be7d20ac6 | ||
|
|
d0da5bc14c | ||
|
|
28dfc5fc39 | ||
|
|
bc6617cf11 | ||
|
|
2b95664a66 | ||
|
|
acb8698a3c | ||
|
|
7a8fa9e3de | ||
|
|
d90c162edf | ||
|
|
130a5f2dee | ||
|
|
39b2a21a81 | ||
|
|
c00d1e02cd | ||
|
|
c2390d030d | ||
|
|
5979bc061f | ||
|
|
1dc9abfb79 | ||
|
|
9071852837 | ||
|
|
2f4be547bd | ||
|
|
cc8dfa660d | ||
|
|
99dfad38b2 | ||
|
|
fa368eff78 | ||
|
|
fb1bdb8ee0 | ||
|
|
9c0b217980 | ||
|
|
bc7d3dc192 | ||
|
|
104a7fb3ea | ||
|
|
6591aab69f | ||
|
|
49fd904db2 | ||
|
|
57511c1114 | ||
|
|
bb1bf0505e | ||
|
|
0253676384 | ||
|
|
0627d1a43e | ||
|
|
f3cfe6ed9c | ||
|
|
b018f6d9f7 | ||
|
|
7b8218b2a2 | ||
|
|
1b403f6ee1 | ||
|
|
8a7bb2c4f6 | ||
|
|
b2710818d4 | ||
|
|
0866b8e60b | ||
|
|
8b3c553f23 | ||
|
|
db6178e670 | ||
|
|
dd7ffb54ee | ||
|
|
a0cc6dfc5a | ||
|
|
ffee03db49 | ||
|
|
f3cdeb500c | ||
|
|
a345ffccfa | ||
|
|
c9d58a02d7 | ||
|
|
a561a26a80 | ||
|
|
45e6964a92 | ||
|
|
94bf5d116f | ||
|
|
94f8dd838c | ||
|
|
ed224e1d64 | ||
|
|
00165cbe65 | ||
|
|
06890aaf8e | ||
|
|
954a73f8c6 | ||
|
|
5d8fe6ec49 | ||
|
|
d7f47f4de6 | ||
|
|
49d73f5f56 | ||
|
|
1dd130be61 | ||
|
|
58c34b2dda | ||
|
|
8bde1c7cb3 | ||
|
|
0dcf1d71dc | ||
|
|
09e18ffdbb | ||
|
|
04edbb68b7 | ||
|
|
2884751446 | ||
|
|
c8982075c5 | ||
|
|
0c8d24b653 | ||
|
|
d2b3e464ac | ||
|
|
f3525b2081 | ||
|
|
849026931b | ||
|
|
da073cfc3e | ||
|
|
caaad8b810 | ||
|
|
0fe0251c8d | ||
|
|
9be27227a1 | ||
|
|
a9a47f8b41 | ||
|
|
46f7dc4d08 | ||
|
|
0e5b4105d1 | ||
|
|
9b31997a4c | ||
|
|
98552115d0 | ||
|
|
d58327876f | ||
|
|
8b048a8229 | ||
|
|
967793aad3 | ||
|
|
6ffe218762 | ||
|
|
da8ae735ce | ||
|
|
8cdb815df6 | ||
|
|
e48ecb0e15 | ||
|
|
635c65f4ed | ||
|
|
2b4ef794b9 | ||
|
|
0e1b3fb3e0 | ||
|
|
be483a3bfb | ||
|
|
6ef599255d | ||
|
|
c8f2bf0333 | ||
|
|
e5196b25ae | ||
|
|
193892e52d | ||
|
|
a6c543362d | ||
|
|
32396dc677 | ||
|
|
ed57c4de4c | ||
|
|
bfe329e34d | ||
|
|
9e30587b1e | ||
|
|
cf9837bf7f | ||
|
|
0bdfbccbdc | ||
|
|
3bb9aaafca | ||
|
|
7bfaf07981 | ||
|
|
2e28288b1d | ||
|
|
452b7cc069 | ||
|
|
4d194ac2fa | ||
|
|
f041511951 | ||
|
|
0e4bda4c55 | ||
|
|
e56a3a60ef | ||
|
|
faa7ad14c6 | ||
|
|
c87fe76e47 | ||
|
|
a18af6d1e1 | ||
|
|
3adebc8721 | ||
|
|
1a4fadaf00 | ||
|
|
b26f289ccc | ||
|
|
0e000a8587 | ||
|
|
84ae5c8e79 | ||
|
|
05f9d35f00 | ||
|
|
c7ad292438 | ||
|
|
b63db13e72 | ||
|
|
6ee18c6824 | ||
|
|
e8f4ac9b1a | ||
|
|
5e420a8275 | ||
|
|
bb3a1178b6 | ||
|
|
306db56f1f | ||
|
|
95b0a129e2 | ||
|
|
c4e1a83be5 | ||
|
|
fec9e1d262 | ||
|
|
c854c57c6f | ||
|
|
ee6e142192 | ||
|
|
ac74d176cc | ||
|
|
8343901c0f | ||
|
|
b7745a2e53 | ||
|
|
82cab3f855 | ||
|
|
6ce08cf242 | ||
|
|
877038b6d9 | ||
|
|
a2227517ed | ||
|
|
04a896098e | ||
|
|
7e84653485 | ||
|
|
71723c6276 | ||
|
|
219d0c70dd | ||
|
|
85c88707ca | ||
|
|
e7d64c7eac | ||
|
|
d7d8e117ef | ||
|
|
d9e9a31562 | ||
|
|
5d594b301f | ||
|
|
648a776493 | ||
|
|
8404701a09 | ||
|
|
4b065a443c | ||
|
|
0b04728785 | ||
|
|
76a31aacd3 | ||
|
|
179273a13a | ||
|
|
7f8b84d0e5 | ||
|
|
2b08436c4c | ||
|
|
5cc518f114 | ||
|
|
1315edc5e9 | ||
|
|
97f5d3d28e | ||
|
|
ad78119e19 | ||
|
|
2756924a44 | ||
|
|
3fd809d537 | ||
|
|
22b8057cca | ||
|
|
fe6f28ddc5 | ||
|
|
1e4dc049eb | ||
|
|
1a4d24816b | ||
|
|
1f47306af9 | ||
|
|
f1a3c9b646 | ||
|
|
a67e62dd40 | ||
|
|
1b6d254224 | ||
|
|
a0e442eb32 | ||
|
|
7eaceaab2f | ||
|
|
2ba3ff9f7d | ||
|
|
e7cb0f27cc | ||
|
|
317938efba | ||
|
|
d6ce1f426d | ||
|
|
9f97c4ac44 | ||
|
|
6a5f5e652a | ||
|
|
61b42edaef | ||
|
|
060d4740c2 | ||
|
|
5a198e0b4c | ||
|
|
abf85e9270 | ||
|
|
ab4b9aa250 | ||
|
|
a2f143cfcc | ||
|
|
e297886386 | ||
|
|
cdf334528f | ||
|
|
1f9ad9e66e | ||
|
|
75073cefea | ||
|
|
9374c1465a | ||
|
|
a2311972e7 | ||
|
|
11a764a82f | ||
|
|
f187e1da7e | ||
|
|
2dbc83d5ae | ||
|
|
fcac316d63 | ||
|
|
43a2640a9c | ||
|
|
8892d4b63c | ||
|
|
1c33240c16 | ||
|
|
b97a63aa95 | ||
|
|
2ef042ef0e | ||
|
|
185c6f76b1 | ||
|
|
87fe7013a0 | ||
|
|
77019eeb5e | ||
|
|
1152a4c5e8 | ||
|
|
83f8775d5a | ||
|
|
d9907b118f | ||
|
|
6ccdaf41e5 | ||
|
|
eb4ba518f1 | ||
|
|
6a355b37fe | ||
|
|
e12d274954 | ||
|
|
9ade3e4811 | ||
|
|
2c2eaecc6c | ||
|
|
16906c53e6 | ||
|
|
b6d9b4bfcc | ||
|
|
6bb12a5776 | ||
|
|
bce6c3405b | ||
|
|
aa0c26ddec | ||
|
|
a1cbd214a5 | ||
|
|
f64778a893 | ||
|
|
9d3b5fa30d | ||
|
|
99590f93bd | ||
|
|
b72563dcb7 | ||
|
|
6b0cf42b32 | ||
|
|
8df2959959 | ||
|
|
1cda904545 | ||
|
|
416129dedb | ||
|
|
b9c417481b | ||
|
|
eda6528c14 | ||
|
|
fc8657c421 | ||
|
|
5f50f5d2b4 | ||
|
|
31f803201d | ||
|
|
ff975871ab | ||
|
|
99a9914605 | ||
|
|
435fbd1794 | ||
|
|
fe09abe4e6 | ||
|
|
e900e44f9f | ||
|
|
bfbac6bb36 |
+159
-62
@@ -1,9 +1,15 @@
|
||||
# Copyright 2007 NLnet Labs
|
||||
# See the file LICENSE for the license
|
||||
#
|
||||
# Standard installation pathnames
|
||||
|
||||
QUIET=yes
|
||||
debug_enabled=@debug_enabled@
|
||||
ifeq "$(QUIET)" ""
|
||||
ifeq ($(debug_enabled),yes)
|
||||
QUIET=yes
|
||||
else
|
||||
QUIET=no
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq "$(QUIET)" "yes"
|
||||
Q=@
|
||||
INFO=@echo
|
||||
@@ -18,6 +24,7 @@ srcdir=@srcdir@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
bindir=@bindir@
|
||||
sbindir=@sbindir@
|
||||
mandir=@mandir@
|
||||
libdir=@libdir@
|
||||
# datarootdir is here to please some checkers, use datadir.
|
||||
@@ -29,16 +36,19 @@ libtool=@libtool@
|
||||
ldnsdir=@ldnsdir@
|
||||
staticexe=@staticexe@
|
||||
configfile=@ub_conf_file@
|
||||
UNBOUND_RUN_DIR=@UNBOUND_RUN_DIR@
|
||||
CHECKLOCK_SRC=@CHECKLOCK_SRC@
|
||||
|
||||
YACC=@YACC@
|
||||
LEX=@LEX@
|
||||
CC=@CC@
|
||||
CPPFLAGS=-I$(srcdir) @CPPFLAGS@ @DEFS@
|
||||
CPPFLAGS=-I$(srcdir) -I. @CPPFLAGS@ @DEFS@
|
||||
CFLAGS=@CFLAGS@
|
||||
LDFLAGS=@LDFLAGS@
|
||||
LIBS=@LIBS@
|
||||
LIBOBJS=@LIBOBJS@
|
||||
RUNTIME_PATH=@RUNTIME_PATH@
|
||||
DEPFLAG=@DEPFLAG@
|
||||
DATE=$(shell date +%Y%m%d)
|
||||
LIBTOOL=$(libtool)
|
||||
ifeq "$(QUIET)" "yes"
|
||||
@@ -57,58 +67,85 @@ COMMON_SRC=$(patsubst $(srcdir)/%,%, $(wildcard $(srcdir)/services/*.c \
|
||||
$(srcdir)/services/cache/*.c $(srcdir)/util/*.c \
|
||||
$(srcdir)/util/data/*.c $(srcdir)/util/storage/*.c \
|
||||
$(srcdir)/iterator/*.c $(srcdir)/validator/*.c)) \
|
||||
util/configparser.c util/configlexer.c testcode/checklocks.c
|
||||
COMMON_OBJ=$(addprefix $(BUILD),$(COMMON_SRC:.c=.o))
|
||||
COMPAT_OBJ=$(addprefix $(BUILD)compat/,$(LIBOBJS))
|
||||
util/configparser.c util/configlexer.c $(CHECKLOCK_SRC)
|
||||
COMMON_OBJ=$(addprefix $(BUILD),$(COMMON_SRC:.c=.lo))
|
||||
COMPAT_OBJ=$(addprefix $(BUILD)compat/,$(LIBOBJS:.o=.lo))
|
||||
UNITTEST_SRC=$(patsubst $(srcdir)/%,%, \
|
||||
$(wildcard $(srcdir)/testcode/unit*.c)) \
|
||||
testcode/readhex.c testcode/ldns-testpkts.c checkconf/worker_cb.c \
|
||||
testcode/readhex.c testcode/ldns-testpkts.c smallapp/worker_cb.c \
|
||||
$(COMMON_SRC)
|
||||
UNITTEST_OBJ=$(addprefix $(BUILD),$(UNITTEST_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
UNITTEST_OBJ=$(addprefix $(BUILD),$(UNITTEST_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
DAEMON_SRC=$(patsubst $(srcdir)/%,%, $(wildcard $(srcdir)/daemon/*.c)) \
|
||||
$(COMMON_SRC)
|
||||
DAEMON_OBJ=$(addprefix $(BUILD),$(DAEMON_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
CHECKCONF_SRC=checkconf/unbound-checkconf.c checkconf/worker_cb.c $(COMMON_SRC)
|
||||
CHECKCONF_OBJ=$(addprefix $(BUILD),$(CHECKCONF_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
DAEMON_OBJ=$(addprefix $(BUILD),$(DAEMON_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
CHECKCONF_SRC=smallapp/unbound-checkconf.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
CHECKCONF_OBJ=$(addprefix $(BUILD),$(CHECKCONF_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
CONTROL_SRC=smallapp/unbound-control.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
CONTROL_OBJ=$(addprefix $(BUILD),$(CONTROL_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
HOST_SRC=smallapp/unbound-host.c
|
||||
HOST_OBJ=$(addprefix $(BUILD),$(HOST_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
TESTBOUND_SRC=testcode/testbound.c testcode/ldns-testpkts.c \
|
||||
daemon/worker.c daemon/daemon.c daemon/stats.c testcode/replay.c \
|
||||
testcode/fake_event.c $(filter-out util/netevent.c \
|
||||
daemon/worker.c daemon/acl_list.c daemon/daemon.c daemon/stats.c \
|
||||
testcode/replay.c testcode/fake_event.c $(filter-out util/netevent.c \
|
||||
services/listen_dnsport.c services/outside_network.c, $(COMMON_SRC))
|
||||
TESTBOUND_OBJ=$(addprefix $(BUILD),$(TESTBOUND_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
LOCKVERIFY_SRC=testcode/lock_verify.c checkconf/worker_cb.c $(COMMON_SRC)
|
||||
LOCKVERIFY_OBJ=$(addprefix $(BUILD),$(LOCKVERIFY_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
PKTVIEW_SRC=testcode/pktview.c testcode/readhex.c checkconf/worker_cb.c \
|
||||
TESTBOUND_OBJ=$(addprefix $(BUILD),$(TESTBOUND_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
LOCKVERIFY_SRC=testcode/lock_verify.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
LOCKVERIFY_OBJ=$(addprefix $(BUILD),$(LOCKVERIFY_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
PKTVIEW_SRC=testcode/pktview.c testcode/readhex.c smallapp/worker_cb.c \
|
||||
$(COMMON_SRC)
|
||||
PKTVIEW_OBJ=$(addprefix $(BUILD),$(PKTVIEW_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
SIGNIT_SRC=testcode/signit.c checkconf/worker_cb.c $(COMMON_SRC)
|
||||
SIGNIT_OBJ=$(addprefix $(BUILD),$(SIGNIT_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
MEMSTATS_SRC=testcode/memstats.c checkconf/worker_cb.c $(COMMON_SRC)
|
||||
MEMSTATS_OBJ=$(addprefix $(BUILD),$(MEMSTATS_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) \
|
||||
PKTVIEW_OBJ=$(addprefix $(BUILD),$(PKTVIEW_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
SIGNIT_SRC=testcode/signit.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
SIGNIT_OBJ=$(addprefix $(BUILD),$(SIGNIT_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
MEMSTATS_SRC=testcode/memstats.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
MEMSTATS_OBJ=$(addprefix $(BUILD),$(MEMSTATS_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
ASYNCLOOK_SRC=testcode/asynclook.c
|
||||
ASYNCLOOK_OBJ=$(addprefix $(BUILD),$(ASYNCLOOK_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
STREAMTCP_SRC=testcode/streamtcp.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
STREAMTCP_OBJ=$(addprefix $(BUILD),$(STREAMTCP_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
PERF_SRC=testcode/perf.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
PERF_OBJ=$(addprefix $(BUILD),$(PERF_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
DELAYER_SRC=testcode/delayer.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
DELAYER_OBJ=$(addprefix $(BUILD),$(DELAYER_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
HARVEST_SRC=testcode/harvest.c
|
||||
HARVEST_OBJ=$(addprefix $(BUILD),$(HARVEST_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
LIBUNBOUND_SRC=$(patsubst $(srcdir)/%,%, \
|
||||
$(wildcard $(srcdir)/libunbound/*.c) $(COMMON_SRC))
|
||||
LIBUNBOUND_OBJ=$(addprefix $(BUILD),$(LIBUNBOUND_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
ALL_SRC=$(sort $(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) \
|
||||
$(TESTBOUND_SRC) $(LOCKVERIFY_SRC) $(PKTVIEW_SRC) $(SIGNIT_SRC) \
|
||||
$(MEMSTATS_SRC) $(CHECKCONF_SRC)
|
||||
ALL_OBJ=$(addprefix $(BUILD),$(ALL_SRC:.c=.o) \
|
||||
$(addprefix compat/,$(LIBOBJS))) $(COMPAT_OBJ)
|
||||
$(MEMSTATS_SRC) $(CHECKCONF_SRC) $(LIBUNBOUND_SRC) $(HOST_SRC) \
|
||||
$(ASYNCLOOK_SRC) $(STREAMTCP_SRC) $(PERF_SRC) $(DELAYER_SRC) \
|
||||
$(HARVEST_SRC) $(CONTROL_SRC))
|
||||
ALL_OBJ=$(addprefix $(BUILD),$(ALL_SRC:.c=.lo) \
|
||||
$(addprefix compat/,$(LIBOBJS:.o=.lo))) $(COMPAT_OBJ)
|
||||
|
||||
COMPILE=$(LIBTOOL) --tag=CC --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS)
|
||||
LINK=$(LIBTOOL) --tag=CC --mode=link $(CC) $(staticexe) $(RUNTIME_PATH) $(CFLAGS) $(LDFLAGS)
|
||||
LINK_LIB=$(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) $(LDFLAGS) -release $(VERSION)
|
||||
LINK_LIB=$(LIBTOOL) --tag=CC --mode=link $(CC) $(RUNTIME_PATH) $(CFLAGS) $(LDFLAGS) $(staticexe) -version-number @LIBUNBOUND_CURRENT@:@LIBUNBOUND_REVISION@:@LIBUNBOUND_AGE@ -no-undefined
|
||||
|
||||
.PHONY: clean realclean doc lint all install uninstall tests test download_ldns strip
|
||||
.PHONY: clean realclean doc lint all install uninstall tests test download_ldns strip lib
|
||||
|
||||
$(BUILD)%.o: $(srcdir)/%.c
|
||||
$(BUILD)%.lo: $(srcdir)/%.c
|
||||
$(INFO) Build $<
|
||||
@if test ! -z "$(ldnsdir)" -a ! -e $(ldnsdir)/include/ldns/ldns.h; \
|
||||
then (cd $(ldnsdir); $(MAKE) copy-headers); fi
|
||||
@if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
@-if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
$Q$(COMPILE) -c $< -o $@
|
||||
|
||||
all: $(COMMON_OBJ) unbound unbound-checkconf
|
||||
all: $(COMMON_OBJ) unbound unbound-checkconf lib unbound-host unbound-control unbound-control-setup
|
||||
|
||||
tests: all unittest testbound lock-verify pktview signit memstats
|
||||
TEST_BIN=asynclook delayer harvest lock-verify memstats perf pktview signit \
|
||||
streamtcp testbound unittest
|
||||
tests: all $(TEST_BIN)
|
||||
|
||||
test: tests
|
||||
bash testcode/do-tests.sh
|
||||
if test -x "`which bash`"; then bash testcode/do-tests.sh; else sh testcode/do-tests.sh; fi
|
||||
|
||||
lib: libunbound.la
|
||||
|
||||
libunbound.la: $(LIBUNBOUND_OBJ)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK_LIB) --export-symbols $(srcdir)/libunbound/ubsyms.def -o $@ $(sort $(LIBUNBOUND_OBJ)) -rpath $(libdir) $(LIBS)
|
||||
|
||||
ifeq ($(patsubst ldns-src%,ldns-src,$(ldnsdir)),ldns-src)
|
||||
ldnslib=$(ldnsdir)/lib/libldns.a
|
||||
@@ -121,19 +158,27 @@ endif
|
||||
|
||||
unbound: $(DAEMON_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(DAEMON_OBJ)) $(LIBS)
|
||||
$Q$(LINK) -o $@ $(sort $(DAEMON_OBJ)) -lssl $(LIBS)
|
||||
|
||||
unbound-checkconf: $(CHECKCONF_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(CHECKCONF_OBJ)) $(LIBS)
|
||||
|
||||
unbound-control: $(CONTROL_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(CONTROL_OBJ)) -lssl $(LIBS)
|
||||
|
||||
unbound-host: $(HOST_OBJ) libunbound.la $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(HOST_OBJ)) -L. -L.libs -lunbound $(LIBS)
|
||||
|
||||
unittest: $(UNITTEST_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(UNITTEST_OBJ)) $(LIBS)
|
||||
|
||||
testbound: $(TESTBOUND_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(TESTBOUND_OBJ)) $(LIBS)
|
||||
$Q$(LINK) -o $@ $(sort $(TESTBOUND_OBJ)) -lssl $(LIBS)
|
||||
|
||||
lock-verify: $(LOCKVERIFY_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
@@ -151,6 +196,31 @@ memstats: $(MEMSTATS_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(MEMSTATS_OBJ)) $(LIBS)
|
||||
|
||||
asynclook: $(ASYNCLOOK_OBJ) $(ldnslib) libunbound.la
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(ASYNCLOOK_OBJ)) $(LIBS) -L. -L.libs -lunbound
|
||||
|
||||
streamtcp: $(STREAMTCP_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(STREAMTCP_OBJ)) $(LIBS)
|
||||
|
||||
perf: $(PERF_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(PERF_OBJ)) $(LIBS)
|
||||
|
||||
delayer: $(DELAYER_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(DELAYER_OBJ)) $(LIBS)
|
||||
|
||||
harvest: $(HARVEST_OBJ) $(ldnslib) libunbound.la
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(HARVEST_OBJ)) $(LIBS) -L. -L.libs -lunbound
|
||||
|
||||
unbound-control-setup: $(srcdir)/smallapp/unbound-control-setup.sh
|
||||
$(INFO) Sed $@
|
||||
$Qsed -e 's:^DESTDIR=.*$$:DESTDIR=$(UNBOUND_RUN_DIR):' < $< > $@
|
||||
$Q-chmod +x $@
|
||||
|
||||
#testcode/ldns-testpkts.c: $(ldnsdir)/examples/ldns-testpkts.c \
|
||||
# $(ldnsdir)/examples/ldns-testpkts.h
|
||||
# cp $(ldnsdir)/examples/ldns-testpkts.c testcode/ldns-testpkts.c
|
||||
@@ -162,30 +232,32 @@ util/configlexer.c: $(srcdir)/util/configlexer.lex util/configparser.h
|
||||
ifeq "$(strip $(LEX))" ":"
|
||||
$Qecho "rebuild lexer, but no lex program, skipped"
|
||||
else
|
||||
@if test ! -d util; then $(INSTALL) -d util; fi
|
||||
@-if test ! -d util; then $(INSTALL) -d util; fi
|
||||
$Qecho "#include \"util/configyyrename.h\"" > $@
|
||||
$Q$(LEX) -t $< >> $@
|
||||
endif
|
||||
|
||||
util/configparser.c util/configparser.h: $(srcdir)/util/configparser.y
|
||||
$(INFO) Yacc $<
|
||||
@if test ! -d util; then $(INSTALL) -d util; fi
|
||||
@-if test ! -d util; then $(INSTALL) -d util; fi
|
||||
$Q$(YACC) -d -o util/configparser.c $<
|
||||
|
||||
clean:
|
||||
rm -f *.o *.d *.lo *~ tags
|
||||
rm -f unbound unbound-checkconf unbound-host unbound-control unbound-control-setup libunbound.la
|
||||
rm -rf autom4te.cache .libs build doc/html
|
||||
|
||||
realclean: clean
|
||||
rm -f config.status config.log config.h.in config.h
|
||||
rm -f configure config.sub config.guess ltmain.sh aclocal.m4 libtool
|
||||
rm -f util/configlexer.c util/configparser.c util/configparser.h
|
||||
rm -f $(TEST_BIN)
|
||||
if test -d ldns-src; then rm -rf ldns-src; fi
|
||||
rm -f Makefile
|
||||
|
||||
$(BUILD)%.lint: $(srcdir)/%.c
|
||||
$(INFO) Lint $<
|
||||
@if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
@-if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
$Q$(LINT) $(LINTFLAGS) -I. -I$(srcdir) -I$(ldnsdir)/include $<
|
||||
$Qtouch $@
|
||||
|
||||
@@ -195,50 +267,75 @@ tags: $(srcdir)/*.[ch] $(srcdir)/*/*.[ch]
|
||||
ctags -f $(srcdir)/tags $(srcdir)/*.[ch] $(srcdir)/*/*.[ch]
|
||||
|
||||
doc:
|
||||
ifdef doxygen
|
||||
$(doxygen) $(srcdir)/doc/unbound.doxygen
|
||||
endif
|
||||
if test -n "$(doxygen)"; then \
|
||||
$(doxygen) $(srcdir)/doc/unbound.doxygen; fi
|
||||
|
||||
strip:
|
||||
strip unbound
|
||||
strip unbound-checkconf
|
||||
strip unbound-control
|
||||
strip unbound-host
|
||||
|
||||
install:
|
||||
$(INSTALL) -d $(bindir)
|
||||
$(INSTALL) -d $(mandir)
|
||||
$(INSTALL) -d $(mandir)/man8
|
||||
$(INSTALL) -d $(mandir)/man5
|
||||
$(LIBTOOL) --mode=install cp unbound $(bindir)/unbound
|
||||
$(LIBTOOL) --mode=install cp unbound-checkconf $(bindir)/unbound-checkconf
|
||||
$(INSTALL) -c -m 644 $(srcdir)/doc/unbound.8 $(mandir)/man8
|
||||
$(INSTALL) -c -m 644 $(srcdir)/doc/unbound-checkconf.8 $(mandir)/man8
|
||||
$(INSTALL) -c -m 644 $(srcdir)/doc/unbound.conf.5 $(mandir)/man5
|
||||
if test ! -e $(configfile); then $(INSTALL) -d `dirname $(configfile)`; $(INSTALL) -c -m 644 $(srcdir)/doc/example.conf $(configfile); fi
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(sbindir)
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(mandir)
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man8
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man5
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man3
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man1
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(libdir)
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(includedir)
|
||||
$(LIBTOOL) --mode=install cp unbound $(DESTDIR)$(sbindir)/unbound
|
||||
$(LIBTOOL) --mode=install cp unbound-checkconf $(DESTDIR)$(sbindir)/unbound-checkconf
|
||||
$(LIBTOOL) --mode=install cp unbound-control $(DESTDIR)$(sbindir)/unbound-control
|
||||
$(LIBTOOL) --mode=install cp unbound-host $(DESTDIR)$(sbindir)/unbound-host
|
||||
$(INSTALL) -c -m 644 doc/unbound.8 $(DESTDIR)$(mandir)/man8
|
||||
$(INSTALL) -c -m 644 doc/unbound-checkconf.8 $(DESTDIR)$(mandir)/man8
|
||||
$(INSTALL) -c -m 644 doc/unbound-control.8 $(DESTDIR)$(mandir)/man8
|
||||
$(INSTALL) -c -m 644 doc/unbound.conf.5 $(DESTDIR)$(mandir)/man5
|
||||
$(INSTALL) -c -m 644 $(srcdir)/doc/unbound-host.1 $(DESTDIR)$(mandir)/man1
|
||||
$(INSTALL) -c -m 644 doc/libunbound.3 $(DESTDIR)$(mandir)/man3
|
||||
$(INSTALL) -c -m 755 unbound-control-setup $(DESTDIR)$(sbindir)/unbound-control-setup
|
||||
if test ! -e $(DESTDIR)$(configfile); then $(INSTALL) -d `dirname $(DESTDIR)$(configfile)`; $(INSTALL) -c -m 644 doc/example.conf $(DESTDIR)$(configfile); fi
|
||||
$(LIBTOOL) --mode=install cp $(srcdir)/libunbound/unbound.h $(DESTDIR)$(includedir)/unbound.h
|
||||
$(LIBTOOL) --mode=install cp libunbound.la $(DESTDIR)$(libdir)
|
||||
$(LIBTOOL) --mode=finish $(DESTDIR)$(libdir)
|
||||
|
||||
uninstall:
|
||||
rm -f -- $(bindir)/unbound $(bindir)/unbound-checkconf
|
||||
rm -f -- $(mandir)/man8/unbound.8 $(mandir)/man8/unbound-checkconf.8 $(mandir)/man5/unbound.conf.5
|
||||
rm -f -- $(DESTDIR)$(sbindir)/unbound $(DESTDIR)$(sbindir)/unbound-checkconf $(DESTDIR)$(sbindir)/unbound-host $(DESTDIR)$(sbindir)/unbound-control $(DESTDIR)$(sbindir)/unbound-control-setup
|
||||
rm -f -- $(DESTDIR)$(mandir)/man8/unbound.8 $(DESTDIR)$(mandir)/man8/unbound-checkconf.8 $(DESTDIR)$(mandir)/man5/unbound.conf.5 $(DESTDIR)$(mandir)/man8/unbound-control.8
|
||||
rm -f -- $(DESTDIR)$(mandir)/man1/unbound-host.1 $(DESTDIR)$(mandir)/man3/libunbound.3
|
||||
rm -f -- $(DESTDIR)$(includedir)/unbound.h
|
||||
$(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/libunbound.la
|
||||
@echo
|
||||
@echo "You still need to remove `dirname $(configfile)` , $(configfile) by hand"
|
||||
@echo "You still need to remove "`dirname $(DESTDIR)$(configfile)`" , $(DESTDIR)$(configfile) by hand"
|
||||
|
||||
download_ldns:
|
||||
svn export https://www.nlnetlabs.nl/ldns/svn/trunk/makedist.sh ldns_makedist.sh
|
||||
./ldns_makedist.sh -s -d https://www.nlnetlabs.nl/ldns/svn/trunk
|
||||
mv ldns-*_pre_*.tar.gz ldns-src.tar.gz
|
||||
rm ldns-*_pre_*.tar.gz.sha1 ldns_makedist.sh
|
||||
svn export https://www.nlnetlabs.nl/svn/ldns/trunk/makedist.sh ldns_makedist.sh
|
||||
./ldns_makedist.sh -s -d https://www.nlnetlabs.nl/svn/ldns/trunk
|
||||
mv ldns-[0-9]*.tar.gz ldns-src.tar.gz
|
||||
rm ldns-*.tar.gz.sha1 ldns_makedist.sh
|
||||
|
||||
iana_update:
|
||||
curl -o port-numbers.tmp http://www.iana.org/assignments/port-numbers
|
||||
awk '/Unassigned|Reserved/ {next;} { match($$0, "[0-9]+/udp"); if (RLENGTH > 0) print substr($$0, RSTART, RLENGTH - 4) ","}' port-numbers.tmp | sort -nu > portslist.tmp
|
||||
cat portslist.tmp > util/iana_ports.inc
|
||||
rm -f port-numbers.tmp portslist.tmp
|
||||
|
||||
# Automatic dependencies.
|
||||
$(BUILD)%.d: $(srcdir)/%.c
|
||||
$(INFO) Depend $<
|
||||
@if test ! -z "$(ldnsdir)" -a ! -e $(ldnsdir)/include/ldns/ldns.h; \
|
||||
then (cd $(ldnsdir); $(MAKE) copy-headers); fi
|
||||
@if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
$Q$(SHELL) -ec '$(CC) -MM $(CPPFLAGS) $(CFLAGS) $< \
|
||||
| sed '\''s!\(.*\)\.o[ :]*!$(dir $@)\1.o $@ : !g'\'' > $@; \
|
||||
@-if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
$Q$(SHELL) -ec '$(CC) $(DEPFLAG) $(CPPFLAGS) $(CFLAGS) $< \
|
||||
| sed '\''s!\(.*\)\.o[ :]*!$(dir $@)\1.lo $@ : !g'\'' > $@; \
|
||||
[ -s $@ ] || rm -f $@'
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(MAKECMDGOALS),realclean)
|
||||
ifeq ($(debug_enabled),yes)
|
||||
-include $(addprefix $(BUILD),$(ALL_SRC:.c=.d))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
Unbound README
|
||||
* ./configure && make && make install
|
||||
* on BSDs and Solaris use gmake (GNU make).
|
||||
* If it complains that no libevent can be found; libevent is useful when
|
||||
using many (10000) outgoing ports. By default 16 ports are used and the
|
||||
builtin alternative is equally capable.
|
||||
* You can use libevent if you want. libevent is useful when using
|
||||
many (10000) outgoing ports. By default 16 ports are used and the
|
||||
builtin alternative is equally capable and a little faster.
|
||||
* More detailed README, README.svn, README.tests in doc directory
|
||||
* manual pages can be found in doc directory, and are installed, unbound(8).
|
||||
* example configuration file doc/example.conf
|
||||
|
||||
Vendored
+370
-198
File diff suppressed because it is too large
Load Diff
@@ -1,198 +0,0 @@
|
||||
/*
|
||||
* checkconf/unbound-checkconf.c - config file checker for unbound.conf file.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* The config checker checks for syntax and other errors in the unbound.conf
|
||||
* file, and can be used to check for errors before the server is started
|
||||
* or sigHUPped.
|
||||
* Exit status 1 means an error.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/module.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/regional.h"
|
||||
#include "iterator/iterator.h"
|
||||
#include "validator/validator.h"
|
||||
#include <pwd.h>
|
||||
|
||||
/** Give checkconf usage, and exit (1). */
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
printf("Usage: unbound-checkconf [file]\n");
|
||||
printf(" Checks unbound configuration file for errors.\n");
|
||||
printf("file if omitted %s is used.\n", CONFIGFILE);
|
||||
printf("-h show this usage help.\n");
|
||||
printf("Version %s\n", PACKAGE_VERSION);
|
||||
printf("BSD licensed, see LICENSE in source package for details.\n");
|
||||
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** check if module works with config */
|
||||
static void
|
||||
check_mod(struct config_file* cfg, struct module_func_block* fb)
|
||||
{
|
||||
struct module_env env;
|
||||
memset(&env, 0, sizeof(env));
|
||||
env.cfg = cfg;
|
||||
env.scratch = regional_create();
|
||||
env.scratch_buffer = ldns_buffer_new(BUFSIZ);
|
||||
if(!env.scratch || !env.scratch_buffer)
|
||||
fatal_exit("out of memory");
|
||||
if(!(*fb->init)(&env, 0)) {
|
||||
fatal_exit("bad config for %s module", fb->name);
|
||||
}
|
||||
(*fb->deinit)(&env, 0);
|
||||
ldns_buffer_free(env.scratch_buffer);
|
||||
regional_destroy(env.scratch);
|
||||
}
|
||||
|
||||
/** check configuration for errors */
|
||||
static void
|
||||
morechecks(struct config_file* cfg)
|
||||
{
|
||||
int i;
|
||||
struct sockaddr_storage a;
|
||||
socklen_t alen;
|
||||
for(i=0; i<cfg->num_ifs; i++) {
|
||||
if(!ipstrtoaddr(cfg->ifs[i], UNBOUND_DNS_PORT, &a, &alen)) {
|
||||
fatal_exit("cannot parse interface specified as '%s'",
|
||||
cfg->ifs[i]);
|
||||
}
|
||||
}
|
||||
for(i=0; i<cfg->num_out_ifs; i++) {
|
||||
if(!ipstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT,
|
||||
&a, &alen)) {
|
||||
fatal_exit("cannot parse outgoing-interface "
|
||||
"specified as '%s'", cfg->out_ifs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if(cfg->verbosity < 0)
|
||||
fatal_exit("verbosity value < 0");
|
||||
if(cfg->num_threads < 0 || cfg->num_threads > 10000)
|
||||
fatal_exit("num_threads value weird");
|
||||
if(!cfg->do_ip4 && !cfg->do_ip6)
|
||||
fatal_exit("ip4 and ip6 are both disabled, pointless");
|
||||
if(!cfg->do_udp && !cfg->do_tcp)
|
||||
fatal_exit("udp and tcp are both disabled, pointless");
|
||||
|
||||
if(cfg->chrootdir && strncmp(cfg->chrootdir, cfg->directory,
|
||||
strlen(cfg->chrootdir)) != 0)
|
||||
fatal_exit("working directory %s not in chrootdir %s",
|
||||
cfg->directory, cfg->chrootdir);
|
||||
if(cfg->chrootdir && cfg->pidfile && cfg->pidfile[0] &&
|
||||
strncmp(cfg->chrootdir, cfg->pidfile,
|
||||
strlen(cfg->chrootdir)) != 0)
|
||||
fatal_exit("pid file %s not in chrootdir %s",
|
||||
cfg->pidfile, cfg->chrootdir);
|
||||
if(cfg->chrootdir && cfg->logfile && cfg->logfile[0] &&
|
||||
strncmp(cfg->chrootdir, cfg->logfile,
|
||||
strlen(cfg->chrootdir)) != 0)
|
||||
fatal_exit("log file %s not in chrootdir %s",
|
||||
cfg->logfile, cfg->chrootdir);
|
||||
|
||||
if(strcmp(cfg->module_conf, "iterator") != 0 &&
|
||||
strcmp(cfg->module_conf, "validator iterator") != 0) {
|
||||
fatal_exit("module conf %s is not known to work",
|
||||
cfg->module_conf);
|
||||
}
|
||||
|
||||
if(cfg->username && cfg->username[0]) {
|
||||
struct passwd *pwd;
|
||||
if((pwd = getpwnam(cfg->username)) == NULL)
|
||||
fatal_exit("user '%s' does not exist.", cfg->username);
|
||||
endpwent();
|
||||
}
|
||||
}
|
||||
|
||||
/** check config file */
|
||||
static void
|
||||
checkconf(char* cfgfile)
|
||||
{
|
||||
struct config_file* cfg = config_create();
|
||||
if(!cfg)
|
||||
fatal_exit("out of memory");
|
||||
if(!config_read(cfg, cfgfile)) {
|
||||
/* config_read prints messages to stderr */
|
||||
config_delete(cfg);
|
||||
exit(1);
|
||||
}
|
||||
morechecks(cfg);
|
||||
check_mod(cfg, iter_get_funcblock());
|
||||
check_mod(cfg, val_get_funcblock());
|
||||
config_delete(cfg);
|
||||
printf("unbound-checkconf: no errors in %s\n", cfgfile);
|
||||
}
|
||||
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern int optind;
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern char* optarg;
|
||||
|
||||
/** Main routine for checkconf */
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int c;
|
||||
char* f;
|
||||
log_ident_set("unbound-checkconf");
|
||||
log_init(NULL, 0, NULL);
|
||||
checklock_start();
|
||||
/* parse the options */
|
||||
while( (c=getopt(argc, argv, "h")) != -1) {
|
||||
switch(c) {
|
||||
case '?':
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if(argc != 0 && argc != 1)
|
||||
usage();
|
||||
if(argc == 1)
|
||||
f = argv[0];
|
||||
else f = CONFIGFILE;
|
||||
checkconf(f);
|
||||
checklock_stop();
|
||||
return 0;
|
||||
}
|
||||
@@ -149,23 +149,23 @@ struct addrinfo {
|
||||
#ifdef getaddrinfo
|
||||
# undef getaddrinfo
|
||||
#endif
|
||||
#define getaddrinfo(a,b,c,d) (ssh_getaddrinfo(a,b,c,d))
|
||||
#define getaddrinfo(a,b,c,d) (getaddrinfo_unbound(a,b,c,d))
|
||||
int getaddrinfo(const char *, const char *,
|
||||
const struct addrinfo *, struct addrinfo **);
|
||||
#endif /* !HAVE_GETADDRINFO */
|
||||
|
||||
#if !defined(HAVE_GAI_STRERROR) && !defined(HAVE_CONST_GAI_STRERROR_PROTO)
|
||||
#define gai_strerror(a) (ssh_gai_strerror(a))
|
||||
#define gai_strerror(a) (gai_strerror_unbound(a))
|
||||
char *gai_strerror(int);
|
||||
#endif /* !HAVE_GAI_STRERROR */
|
||||
|
||||
#ifndef HAVE_FREEADDRINFO
|
||||
#define freeaddrinfo(a) (ssh_freeaddrinfo(a))
|
||||
#define freeaddrinfo(a) (freeaddrinfo_unbound(a))
|
||||
void freeaddrinfo(struct addrinfo *);
|
||||
#endif /* !HAVE_FREEADDRINFO */
|
||||
|
||||
#ifndef HAVE_GETNAMEINFO
|
||||
#define getnameinfo(a,b,c,d,e,f,g) (ssh_getnameinfo(a,b,c,d,e,f,g))
|
||||
#define getnameinfo(a,b,c,d,e,f,g) (getnameinfo_unbound(a,b,c,d,e,f,g))
|
||||
int getnameinfo(const struct sockaddr *, size_t, char *, size_t,
|
||||
char *, size_t, int);
|
||||
#endif /* !HAVE_GETNAMEINFO */
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Taken from FreeBSD src / lib / libc / stdtime / localtime.c 1.43 revision.
|
||||
* localtime.c 7.78.
|
||||
* tzfile.h 1.8
|
||||
* adapted to be replacement gmtime_r.
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_TIME_H
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#define MONSPERYEAR 12
|
||||
#define DAYSPERNYEAR 365
|
||||
#define DAYSPERLYEAR 366
|
||||
#define SECSPERMIN 60
|
||||
#define SECSPERHOUR (60*60)
|
||||
#define SECSPERDAY (24*60*60)
|
||||
#define DAYSPERWEEK 7
|
||||
#define TM_SUNDAY 0
|
||||
#define TM_MONDAY 1
|
||||
#define TM_TUESDAY 2
|
||||
#define TM_WEDNESDAY 3
|
||||
#define TM_THURSDAY 4
|
||||
#define TM_FRIDAY 5
|
||||
#define TM_SATURDAY 6
|
||||
|
||||
#define TM_YEAR_BASE 1900
|
||||
|
||||
#define EPOCH_YEAR 1970
|
||||
#define EPOCH_WDAY TM_THURSDAY
|
||||
|
||||
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
|
||||
|
||||
static const int mon_lengths[2][MONSPERYEAR] = {
|
||||
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
|
||||
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
|
||||
};
|
||||
|
||||
static const int year_lengths[2] = {
|
||||
DAYSPERNYEAR, DAYSPERLYEAR
|
||||
};
|
||||
|
||||
static void
|
||||
timesub(timep, offset, tmp)
|
||||
const time_t * const timep;
|
||||
const long offset;
|
||||
struct tm * const tmp;
|
||||
{
|
||||
long days;
|
||||
long rem;
|
||||
long y;
|
||||
int yleap;
|
||||
const int * ip;
|
||||
|
||||
days = *timep / SECSPERDAY;
|
||||
rem = *timep % SECSPERDAY;
|
||||
rem += (offset);
|
||||
while (rem < 0) {
|
||||
rem += SECSPERDAY;
|
||||
--days;
|
||||
}
|
||||
while (rem >= SECSPERDAY) {
|
||||
rem -= SECSPERDAY;
|
||||
++days;
|
||||
}
|
||||
tmp->tm_hour = (int) (rem / SECSPERHOUR);
|
||||
rem = rem % SECSPERHOUR;
|
||||
tmp->tm_min = (int) (rem / SECSPERMIN);
|
||||
/*
|
||||
** A positive leap second requires a special
|
||||
** representation. This uses "... ??:59:60" et seq.
|
||||
*/
|
||||
tmp->tm_sec = (int) (rem % SECSPERMIN) ;
|
||||
tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
|
||||
if (tmp->tm_wday < 0)
|
||||
tmp->tm_wday += DAYSPERWEEK;
|
||||
y = EPOCH_YEAR;
|
||||
#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
|
||||
while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
|
||||
long newy;
|
||||
|
||||
newy = y + days / DAYSPERNYEAR;
|
||||
if (days < 0)
|
||||
--newy;
|
||||
days -= (newy - y) * DAYSPERNYEAR +
|
||||
LEAPS_THRU_END_OF(newy - 1) -
|
||||
LEAPS_THRU_END_OF(y - 1);
|
||||
y = newy;
|
||||
}
|
||||
tmp->tm_year = y - TM_YEAR_BASE;
|
||||
tmp->tm_yday = (int) days;
|
||||
ip = mon_lengths[yleap];
|
||||
for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
|
||||
days = days - (long) ip[tmp->tm_mon];
|
||||
tmp->tm_mday = (int) (days + 1);
|
||||
tmp->tm_isdst = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-entrant version of gmtime.
|
||||
*/
|
||||
struct tm * gmtime_r(const time_t* timep, struct tm *tm)
|
||||
{
|
||||
timesub(timep, 0L, tm);
|
||||
return tm;
|
||||
}
|
||||
@@ -56,8 +56,12 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
|
||||
#if 0
|
||||
|
||||
@@ -23,8 +23,12 @@
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
@@ -62,7 +66,11 @@ inet_ntop(int af, const void *src, char *dst, size_t size)
|
||||
case AF_INET6:
|
||||
return (inet_ntop6(src, dst, size));
|
||||
default:
|
||||
#ifdef EAFNOSUPPORT
|
||||
errno = EAFNOSUPPORT;
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
#endif
|
||||
return (NULL);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
|
||||
@@ -70,7 +70,11 @@ inet_pton(af, src, dst)
|
||||
case AF_INET6:
|
||||
return (inet_pton6(src, dst));
|
||||
default:
|
||||
#ifdef EAFNOSUPPORT
|
||||
errno = EAFNOSUPPORT;
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
#endif
|
||||
return (-1);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
|
||||
+5
-5
@@ -528,7 +528,7 @@ static long double abs_val (long double value)
|
||||
return result;
|
||||
}
|
||||
|
||||
static long double pow10 (int exp)
|
||||
static long double compat_pow10 (int exp)
|
||||
{
|
||||
long double result = 1;
|
||||
|
||||
@@ -541,7 +541,7 @@ static long double pow10 (int exp)
|
||||
return result;
|
||||
}
|
||||
|
||||
static long round (long double value)
|
||||
static long compat_round (long double value)
|
||||
{
|
||||
long intpart;
|
||||
|
||||
@@ -602,12 +602,12 @@ static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
|
||||
/* We "cheat" by converting the fractional part to integer by
|
||||
* multiplying by a factor of 10
|
||||
*/
|
||||
fracpart = round ((pow10 (max)) * (ufvalue - intpart));
|
||||
fracpart = compat_round ((compat_pow10 (max)) * (ufvalue - intpart));
|
||||
|
||||
if (fracpart >= pow10 (max))
|
||||
if (fracpart >= compat_pow10 (max))
|
||||
{
|
||||
intpart++;
|
||||
fracpart -= pow10 (max);
|
||||
fracpart -= compat_pow10 (max);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SNPRINTF
|
||||
|
||||
Vendored
+25
-9
@@ -4,7 +4,7 @@
|
||||
# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
|
||||
# Inc.
|
||||
|
||||
timestamp='2006-07-02'
|
||||
timestamp='2007-05-17'
|
||||
|
||||
# This file is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
@@ -161,6 +161,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
||||
arm*) machine=arm-unknown ;;
|
||||
sh3el) machine=shl-unknown ;;
|
||||
sh3eb) machine=sh-unknown ;;
|
||||
sh5el) machine=sh5le-unknown ;;
|
||||
*) machine=${UNAME_MACHINE_ARCH}-unknown ;;
|
||||
esac
|
||||
# The Operating System including object format, if it has switched
|
||||
@@ -329,7 +330,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
||||
sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
|
||||
echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
|
||||
exit ;;
|
||||
i86pc:SunOS:5.*:*)
|
||||
i86pc:SunOS:5.*:* | ix86xen:SunOS:5.*:*)
|
||||
echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
|
||||
exit ;;
|
||||
sun4*:SunOS:6*:*)
|
||||
@@ -780,7 +781,7 @@ EOF
|
||||
i*:CYGWIN*:*)
|
||||
echo ${UNAME_MACHINE}-pc-cygwin
|
||||
exit ;;
|
||||
i*:MINGW*:*)
|
||||
*:MINGW*:*)
|
||||
echo ${UNAME_MACHINE}-pc-mingw32
|
||||
exit ;;
|
||||
i*:windows32*:*)
|
||||
@@ -790,12 +791,15 @@ EOF
|
||||
i*:PW*:*)
|
||||
echo ${UNAME_MACHINE}-pc-pw32
|
||||
exit ;;
|
||||
x86:Interix*:[3456]*)
|
||||
echo i586-pc-interix${UNAME_RELEASE}
|
||||
exit ;;
|
||||
EM64T:Interix*:[3456]*)
|
||||
echo x86_64-unknown-interix${UNAME_RELEASE}
|
||||
exit ;;
|
||||
*:Interix*:[3456]*)
|
||||
case ${UNAME_MACHINE} in
|
||||
x86)
|
||||
echo i586-pc-interix${UNAME_RELEASE}
|
||||
exit ;;
|
||||
EM64T | authenticamd)
|
||||
echo x86_64-unknown-interix${UNAME_RELEASE}
|
||||
exit ;;
|
||||
esac ;;
|
||||
[345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
|
||||
echo i${UNAME_MACHINE}-pc-mks
|
||||
exit ;;
|
||||
@@ -950,6 +954,9 @@ EOF
|
||||
x86_64:Linux:*:*)
|
||||
echo x86_64-unknown-linux-gnu
|
||||
exit ;;
|
||||
xtensa:Linux:*:*)
|
||||
echo xtensa-unknown-linux-gnu
|
||||
exit ;;
|
||||
i*86:Linux:*:*)
|
||||
# The BFD linker knows what the default object file format is, so
|
||||
# first see if it will tell us. cd to the root directory to prevent
|
||||
@@ -1208,6 +1215,15 @@ EOF
|
||||
SX-6:SUPER-UX:*:*)
|
||||
echo sx6-nec-superux${UNAME_RELEASE}
|
||||
exit ;;
|
||||
SX-7:SUPER-UX:*:*)
|
||||
echo sx7-nec-superux${UNAME_RELEASE}
|
||||
exit ;;
|
||||
SX-8:SUPER-UX:*:*)
|
||||
echo sx8-nec-superux${UNAME_RELEASE}
|
||||
exit ;;
|
||||
SX-8R:SUPER-UX:*:*)
|
||||
echo sx8r-nec-superux${UNAME_RELEASE}
|
||||
exit ;;
|
||||
Power*:Rhapsody:*:*)
|
||||
echo powerpc-apple-rhapsody${UNAME_RELEASE}
|
||||
exit ;;
|
||||
|
||||
+202
-12
@@ -1,8 +1,15 @@
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Directory to chroot to */
|
||||
#undef CHROOT_DIR
|
||||
|
||||
/* Pathname to the Unbound configuration file */
|
||||
#undef CONFIGFILE
|
||||
|
||||
/* Define this if on macOSX10.4-darwin8 and setreuid and setregid do not work
|
||||
*/
|
||||
#undef DARWIN_BROKEN_SETREUID
|
||||
|
||||
/* Define if you want to use debug lock checking (slow). */
|
||||
#undef ENABLE_LOCK_CHECKS
|
||||
|
||||
@@ -18,30 +25,69 @@
|
||||
/* Define to 1 if your system has a working `chown' function. */
|
||||
#undef HAVE_CHOWN
|
||||
|
||||
/* Define to 1 if you have the `chroot' function. */
|
||||
#undef HAVE_CHROOT
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#undef HAVE_DLFCN_H
|
||||
|
||||
/* Define to 1 if you have the `event_base_free' function. */
|
||||
#undef HAVE_EVENT_BASE_FREE
|
||||
|
||||
/* Define to 1 if you have the `event_base_get_method' function. */
|
||||
#undef HAVE_EVENT_BASE_GET_METHOD
|
||||
|
||||
/* Define to 1 if you have the `event_base_once' function. */
|
||||
#undef HAVE_EVENT_BASE_ONCE
|
||||
|
||||
/* Define to 1 if you have the <event.h> header file. */
|
||||
#undef HAVE_EVENT_H
|
||||
|
||||
/* Define to 1 if you have the `EVP_sha1' function. */
|
||||
#undef HAVE_EVP_SHA1
|
||||
|
||||
/* Define to 1 if you have the `EVP_sha256' function. */
|
||||
#undef HAVE_EVP_SHA256
|
||||
|
||||
/* Define to 1 if you have the `EVP_sha512' function. */
|
||||
#undef HAVE_EVP_SHA512
|
||||
|
||||
/* Define to 1 if you have the `ev_loop' function. */
|
||||
#undef HAVE_EV_LOOP
|
||||
|
||||
/* Define to 1 if you have the `fcntl' function. */
|
||||
#undef HAVE_FCNTL
|
||||
|
||||
/* Define to 1 if you have the `fork' function. */
|
||||
#undef HAVE_FORK
|
||||
|
||||
/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
|
||||
#undef HAVE_FSEEKO
|
||||
|
||||
/* Define to 1 if you have the `getaddrinfo' function. */
|
||||
/* Whether getaddrinfo is available */
|
||||
#undef HAVE_GETADDRINFO
|
||||
|
||||
/* Define to 1 if you have the <getopt.h> header file. */
|
||||
#undef HAVE_GETOPT_H
|
||||
|
||||
/* Define to 1 if you have the `getpwnam' function. */
|
||||
#undef HAVE_GETPWNAM
|
||||
|
||||
/* Define to 1 if you have the `getrlimit' function. */
|
||||
#undef HAVE_GETRLIMIT
|
||||
|
||||
/* Define to 1 if you have the `glob' function. */
|
||||
#undef HAVE_GLOB
|
||||
|
||||
/* Define to 1 if you have the <glob.h> header file. */
|
||||
#undef HAVE_GLOB_H
|
||||
|
||||
/* Define to 1 if you have the `gmtime_r' function. */
|
||||
#undef HAVE_GMTIME_R
|
||||
|
||||
/* If you have HMAC_CTX_init */
|
||||
#undef HAVE_HMAC_CTX_INIT
|
||||
|
||||
/* Define to 1 if you have the `inet_aton' function. */
|
||||
#undef HAVE_INET_ATON
|
||||
|
||||
@@ -54,17 +100,14 @@
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the `crypto' library (-lcrypto). */
|
||||
#undef HAVE_LIBCRYPTO
|
||||
/* if the function 'ioctlsocket' is available */
|
||||
#undef HAVE_IOCTLSOCKET
|
||||
|
||||
/* Define to 1 if you have the `nsl' library (-lnsl). */
|
||||
#undef HAVE_LIBNSL
|
||||
/* Define to 1 if you have the `kill' function. */
|
||||
#undef HAVE_KILL
|
||||
|
||||
/* Define to 1 if you have the `rt' library (-lrt). */
|
||||
#undef HAVE_LIBRT
|
||||
|
||||
/* Define to 1 if you have the `socket' library (-lsocket). */
|
||||
#undef HAVE_LIBSOCKET
|
||||
/* Define to 1 if you have the `ldns' library (-lldns). */
|
||||
#undef HAVE_LIBLDNS
|
||||
|
||||
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
|
||||
to 0 otherwise. */
|
||||
@@ -76,12 +119,18 @@
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define to 1 if you have the <netdb.h> header file. */
|
||||
#undef HAVE_NETDB_H
|
||||
|
||||
/* Define to 1 if you have the <netinet/in.h> header file. */
|
||||
#undef HAVE_NETINET_IN_H
|
||||
|
||||
/* Define to 1 if you have the <openssl/err.h> header file. */
|
||||
#undef HAVE_OPENSSL_ERR_H
|
||||
|
||||
/* Define to 1 if you have the <openssl/rand.h> header file. */
|
||||
#undef HAVE_OPENSSL_RAND_H
|
||||
|
||||
/* Define to 1 if you have the <openssl/ssl.h> header file. */
|
||||
#undef HAVE_OPENSSL_SSL_H
|
||||
|
||||
@@ -94,12 +143,51 @@
|
||||
/* Define to 1 if the system has the type `pthread_spinlock_t'. */
|
||||
#undef HAVE_PTHREAD_SPINLOCK_T
|
||||
|
||||
/* Define to 1 if you have the <pwd.h> header file. */
|
||||
#undef HAVE_PWD_H
|
||||
|
||||
/* Define to 1 if you have the `random' function. */
|
||||
#undef HAVE_RANDOM
|
||||
|
||||
/* Define to 1 if you have the `recvmsg' function. */
|
||||
#undef HAVE_RECVMSG
|
||||
|
||||
/* Define to 1 if you have the `sbrk' function. */
|
||||
#undef HAVE_SBRK
|
||||
|
||||
/* Define to 1 if you have the `sendmsg' function. */
|
||||
#undef HAVE_SENDMSG
|
||||
|
||||
/* Define to 1 if you have the `setregid' function. */
|
||||
#undef HAVE_SETREGID
|
||||
|
||||
/* Define to 1 if you have the `setresgid' function. */
|
||||
#undef HAVE_SETRESGID
|
||||
|
||||
/* Define to 1 if you have the `setresuid' function. */
|
||||
#undef HAVE_SETRESUID
|
||||
|
||||
/* Define to 1 if you have the `setreuid' function. */
|
||||
#undef HAVE_SETREUID
|
||||
|
||||
/* Define to 1 if you have the `setsid' function. */
|
||||
#undef HAVE_SETSID
|
||||
|
||||
/* Define to 1 if you have the `sigprocmask' function. */
|
||||
#undef HAVE_SIGPROCMASK
|
||||
|
||||
/* Define to 1 if you have the `sleep' function. */
|
||||
#undef HAVE_SLEEP
|
||||
|
||||
/* Define to 1 if you have the `snprintf' function. */
|
||||
#undef HAVE_SNPRINTF
|
||||
|
||||
/* Using Solaris threads */
|
||||
#undef HAVE_SOLARIS_THREADS
|
||||
|
||||
/* Define to 1 if you have the `srandom' function. */
|
||||
#undef HAVE_SRANDOM
|
||||
|
||||
/* Define if you have the SSL libraries installed. */
|
||||
#undef HAVE_SSL
|
||||
|
||||
@@ -130,6 +218,9 @@
|
||||
/* Define to 1 if you have the <sys/param.h> header file. */
|
||||
#undef HAVE_SYS_PARAM_H
|
||||
|
||||
/* Define to 1 if you have the <sys/resource.h> header file. */
|
||||
#undef HAVE_SYS_RESOURCE_H
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#undef HAVE_SYS_SOCKET_H
|
||||
|
||||
@@ -142,27 +233,58 @@
|
||||
/* Define to 1 if you have the <sys/uio.h> header file. */
|
||||
#undef HAVE_SYS_UIO_H
|
||||
|
||||
/* Define to 1 if you have the <sys/wait.h> header file. */
|
||||
#undef HAVE_SYS_WAIT_H
|
||||
|
||||
/* Define to 1 if you have the <time.h> header file. */
|
||||
#undef HAVE_TIME_H
|
||||
|
||||
/* Define to 1 if you have the `tzset' function. */
|
||||
#undef HAVE_TZSET
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to 1 if you have the `usleep' function. */
|
||||
#undef HAVE_USLEEP
|
||||
|
||||
/* Define to 1 if you have the `vfork' function. */
|
||||
#undef HAVE_VFORK
|
||||
|
||||
/* Define to 1 if you have the <vfork.h> header file. */
|
||||
#undef HAVE_VFORK_H
|
||||
|
||||
/* Define to 1 if you have the <windows.h> header file. */
|
||||
#undef HAVE_WINDOWS_H
|
||||
|
||||
/* Using Windows threads */
|
||||
#undef HAVE_WINDOWS_THREADS
|
||||
|
||||
/* Define to 1 if you have the <winsock2.h> header file. */
|
||||
#undef HAVE_WINSOCK2_H
|
||||
|
||||
/* Define to 1 if `fork' works. */
|
||||
#undef HAVE_WORKING_FORK
|
||||
|
||||
/* Define to 1 if `vfork' works. */
|
||||
#undef HAVE_WORKING_VFORK
|
||||
|
||||
/* Define to 1 if you have the `writev' function. */
|
||||
#undef HAVE_WRITEV
|
||||
|
||||
/* Define to 1 if you have the <ws2tcpip.h> header file. */
|
||||
#undef HAVE_WS2TCPIP_H
|
||||
|
||||
/* Define to the maximum message length to pass to syslog. */
|
||||
#undef MAXSYSLOGMSGLEN
|
||||
|
||||
/* Define if mkdir has one argument. */
|
||||
#undef MKDIR_HAS_ONE_ARG
|
||||
|
||||
/* Define if the network stack does not fully support nonblocking io (causes
|
||||
lower performance). */
|
||||
#undef NONBLOCKING_IS_BROKEN
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
@@ -178,6 +300,9 @@
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* default pidfile location */
|
||||
#undef PIDFILE
|
||||
|
||||
/* Define to necessary symbol if this constant uses a non-standard name on
|
||||
your system. */
|
||||
#undef PTHREAD_CREATE_JOINABLE
|
||||
@@ -185,18 +310,30 @@
|
||||
/* Define as the return type of signal handlers (`int' or `void'). */
|
||||
#undef RETSIGTYPE
|
||||
|
||||
/* Directory to chdir to */
|
||||
#undef RUN_DIR
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Define to 'int' or type of struct msghdr.msg_iovlen. */
|
||||
#undef TYPE_MSGIOVLEN
|
||||
/* default username */
|
||||
#undef UB_USERNAME
|
||||
|
||||
/* use statistics for allocs and frees, for debug use */
|
||||
#undef UNBOUND_ALLOC_STATS
|
||||
|
||||
/* define this to enable debug checks. */
|
||||
#undef UNBOUND_DEBUG
|
||||
|
||||
/* Define if you want to use internal select based events */
|
||||
#undef USE_MINI_EVENT
|
||||
|
||||
/* Whether the windows socket API is used */
|
||||
#undef USE_WINSOCK
|
||||
|
||||
/* the version of the windows API enabled */
|
||||
#undef WINVER
|
||||
|
||||
/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
|
||||
`char[]'. */
|
||||
#undef YYTEXT_POINTER
|
||||
@@ -256,6 +393,9 @@
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
#undef pid_t
|
||||
|
||||
/* Define to 'int' if not defined */
|
||||
#undef rlim_t
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
#undef size_t
|
||||
|
||||
@@ -287,6 +427,10 @@
|
||||
#undef vfork
|
||||
|
||||
|
||||
#ifndef UNBOUND_DEBUG
|
||||
# define NDEBUG
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@@ -323,6 +467,14 @@
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WINSOCK2_H
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WS2TCPIP_H
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
@@ -331,6 +483,10 @@
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENSSL_RAND_H
|
||||
#include <openssl/rand.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ATTR_FORMAT
|
||||
# define ATTR_FORMAT(archetype, string_index, first_to_check) \
|
||||
__attribute__ ((format (archetype, string_index, first_to_check)))
|
||||
@@ -359,6 +515,10 @@
|
||||
#define RAND_MAX 2147483647
|
||||
#endif
|
||||
|
||||
#ifndef MAXHOSTNAMELEN
|
||||
#define MAXHOSTNAMELEN 256
|
||||
#endif
|
||||
|
||||
#ifndef IPV6_MIN_MTU
|
||||
#define IPV6_MIN_MTU 1280
|
||||
#endif /* IPV6_MIN_MTU */
|
||||
@@ -376,29 +536,59 @@
|
||||
#define MAXINCLUDES 10
|
||||
|
||||
#ifndef HAVE_SNPRINTF
|
||||
#define snprintf snprintf_unbound
|
||||
#define vsnprintf vsnprintf_unbound
|
||||
#include <stdarg.h>
|
||||
int snprintf (char *str, size_t count, const char *fmt, ...);
|
||||
int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
|
||||
#endif /* HAVE_SNPRINTF */
|
||||
#ifndef HAVE_INET_PTON
|
||||
#define inet_pton inet_pton_unbound
|
||||
int inet_pton(int af, const char* src, void* dst);
|
||||
#endif /* HAVE_INET_PTON */
|
||||
#ifndef HAVE_INET_NTOP
|
||||
#define inet_ntop inet_ntop_unbound
|
||||
const char *inet_ntop(int af, const void *src, char *dst, size_t size);
|
||||
#endif
|
||||
#ifndef HAVE_INET_ATON
|
||||
#define inet_aton inet_aton_unbound
|
||||
int inet_aton(const char *cp, struct in_addr *addr);
|
||||
#endif
|
||||
#ifndef HAVE_MEMMOVE
|
||||
#define memmove memmove_unbound
|
||||
void *memmove(void *dest, const void *src, size_t n);
|
||||
#endif
|
||||
#ifndef HAVE_STRLCPY
|
||||
#define strlcpy strlcpy_unbound
|
||||
size_t strlcpy(char *dst, const char *src, size_t siz);
|
||||
#endif
|
||||
#ifndef HAVE_GMTIME_R
|
||||
#define gmtime_r gmtime_r_unbound
|
||||
struct tm *gmtime_r(const time_t *timep, struct tm *result);
|
||||
#endif
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
struct sockaddr_storage;
|
||||
#include "compat/fake-rfc2553.h"
|
||||
#endif
|
||||
#ifndef HAVE_SLEEP
|
||||
#define sleep(x) Sleep((x)*1000) /* on win32 */
|
||||
#endif /* HAVE_SLEEP */
|
||||
#ifndef HAVE_USLEEP
|
||||
#define usleep(x) Sleep((x)/1000 + 1) /* on win32 */
|
||||
#endif /* HAVE_USLEEP */
|
||||
#ifndef HAVE_RANDOM
|
||||
#define random rand /* on win32, for tests only (bad random) */
|
||||
#endif /* HAVE_RANDOM */
|
||||
#ifndef HAVE_SRANDOM
|
||||
#define srandom(x) srand(x) /* on win32, for tests only (bad random) */
|
||||
#endif /* HAVE_SRANDOM */
|
||||
|
||||
/* detect if we need to cast to unsigned int for FD_SET to avoid warnings */
|
||||
#ifdef HAVE_WINSOCK2_H
|
||||
#define FD_SET_T (u_int)
|
||||
#else
|
||||
#define FD_SET_T
|
||||
#endif
|
||||
|
||||
#include "ldns/ldns.h"
|
||||
|
||||
|
||||
Vendored
+15
-5
@@ -4,7 +4,7 @@
|
||||
# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
|
||||
# Inc.
|
||||
|
||||
timestamp='2006-09-20'
|
||||
timestamp='2007-04-29'
|
||||
|
||||
# This file is (in principle) common to ALL GNU software.
|
||||
# The presence of a machine in this file suggests that SOME GNU software
|
||||
@@ -245,12 +245,12 @@ case $basic_machine in
|
||||
| bfin \
|
||||
| c4x | clipper \
|
||||
| d10v | d30v | dlx | dsp16xx \
|
||||
| fr30 | frv \
|
||||
| fido | fr30 | frv \
|
||||
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
|
||||
| i370 | i860 | i960 | ia64 \
|
||||
| ip2k | iq2000 \
|
||||
| m32c | m32r | m32rle | m68000 | m68k | m88k \
|
||||
| maxq | mb | microblaze | mcore \
|
||||
| maxq | mb | microblaze | mcore | mep \
|
||||
| mips | mipsbe | mipseb | mipsel | mipsle \
|
||||
| mips16 \
|
||||
| mips64 | mips64el \
|
||||
@@ -324,7 +324,7 @@ case $basic_machine in
|
||||
| clipper-* | craynv-* | cydra-* \
|
||||
| d10v-* | d30v-* | dlx-* \
|
||||
| elxsi-* \
|
||||
| f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
|
||||
| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
|
||||
| h8300-* | h8500-* \
|
||||
| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
|
||||
| i*86-* | i860-* | i960-* | ia64-* \
|
||||
@@ -683,6 +683,10 @@ case $basic_machine in
|
||||
basic_machine=i386-pc
|
||||
os=-mingw32
|
||||
;;
|
||||
mingw32ce)
|
||||
basic_machine=arm-unknown
|
||||
os=-mingw32ce
|
||||
;;
|
||||
miniframe)
|
||||
basic_machine=m68000-convergent
|
||||
;;
|
||||
@@ -925,6 +929,9 @@ case $basic_machine in
|
||||
basic_machine=sh-hitachi
|
||||
os=-hms
|
||||
;;
|
||||
sh5el)
|
||||
basic_machine=sh5le-unknown
|
||||
;;
|
||||
sh64)
|
||||
basic_machine=sh64-unknown
|
||||
;;
|
||||
@@ -1219,7 +1226,7 @@ case $os in
|
||||
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
|
||||
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
|
||||
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
|
||||
| -skyos* | -haiku* | -rdos* | -toppers*)
|
||||
| -skyos* | -haiku* | -rdos* | -toppers* | -drops*)
|
||||
# Remember, each alternative MUST END IN *, to match a version number.
|
||||
;;
|
||||
-qnx*)
|
||||
@@ -1414,6 +1421,9 @@ case $basic_machine in
|
||||
m68*-cisco)
|
||||
os=-aout
|
||||
;;
|
||||
mep-*)
|
||||
os=-elf
|
||||
;;
|
||||
mips*-cisco)
|
||||
os=-elf
|
||||
;;
|
||||
|
||||
+632
-132
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
These files are contributed to unbound, and are not part of the official
|
||||
distribution but may be helpful.
|
||||
|
||||
* rc_d_unbound: FreeBSD compatible /etc/rc.d script.
|
||||
* parseunbound.pl: perl script to run from cron that parses statistics from
|
||||
the log file and stores them.
|
||||
* unbound.spec and unbound.init: RPM specfile and Linux rc.d initfile.
|
||||
* update-anchor.sh: shell script that uses unbound-host to update a set
|
||||
of trust anchor files. Run from cron twice a month.
|
||||
* unbound_munin_ : plugin for munin statistics report
|
||||
* selinux: the .fc and .te files for SElinux protection of the unbound daemon
|
||||
@@ -0,0 +1,140 @@
|
||||
#!/usr/local/bin/perl -w
|
||||
#
|
||||
# Script to parse the output from the unbound namedaemon.
|
||||
# Unbound supports a threading model, and outputs a multiline log-blob for
|
||||
# every thread.
|
||||
#
|
||||
# This script should parse all threads of the once, and store it
|
||||
# in a local cached file for speedy results when queried lots.
|
||||
#
|
||||
use strict;
|
||||
use POSIX qw(SEEK_END);
|
||||
use Storable;
|
||||
use FileHandle;
|
||||
use Carp qw(croak carp);
|
||||
use constant UNBOUND_CACHE => "/var/tmp/unbound-cache.stor";
|
||||
|
||||
my $run_from_cron = @ARGV && $ARGV[0] eq "--cron" && shift;
|
||||
my $DEBUG = -t STDERR;
|
||||
|
||||
# NB. VERY IMPORTANTES: set this when running this script.
|
||||
my $numthreads = 4;
|
||||
|
||||
### if cache exists, read it in. and is newer than 3 minutes
|
||||
if ( -r UNBOUND_CACHE ) {
|
||||
my $result = retrieve(UNBOUND_CACHE);
|
||||
if (-M _ < 3/24/60 && !$run_from_cron ) {
|
||||
print STDERR "Cached results:\n" if $DEBUG;
|
||||
print join("\n", @$result), "\n";
|
||||
exit;
|
||||
}
|
||||
}
|
||||
my $logfile = shift or die "Usage: parseunbound.pl --cron unboundlogfile";
|
||||
my $in = new FileHandle $logfile or die "Cannot open $logfile: $!\n";
|
||||
|
||||
# there is a special key 'thread' that indicates the thread. its not used, but returned anyway.
|
||||
my @records = ('thread', 'queries', 'cachehits', 'recursions', 'recursionavg',
|
||||
'outstandingmax', 'outstandingavg', 'outstandingexc',
|
||||
'median25', 'median50', 'median75',
|
||||
'us_0', 'us_1', 'us_2', 'us_4', 'us_8', 'us_16', 'us_32',
|
||||
'us_64', 'us_128', 'us_256', 'us_512', 'us_1024', 'us_2048',
|
||||
'us_4096', 'us_8192', 'us_16384', 'us_32768', 'us_65536',
|
||||
'us_131072', 'us_262144', 'us_524288', 's_1', 's_2', 's_4',
|
||||
's_8', 's_16', 's_32', 's_64', 's_128', 's_256', 's_512');
|
||||
# Stats hash containing one or more keys. for every thread, 1 key.
|
||||
my %allstats = (); # key="$threadid", stats={key => value}
|
||||
my %startstats = (); # when we got a queries entry for this thread
|
||||
my %donestats = (); # same, but only when we got a histogram entry for it
|
||||
# stats hash contains name/value pairs of the actual numbers for that thread.
|
||||
my $offset = 0;
|
||||
my $inthread=0;
|
||||
my $inpid;
|
||||
|
||||
# We should continue looping untill we meet these conditions:
|
||||
# a) more total queries than the previous run (which defaults to 0) AND
|
||||
# b) parsed all $numthreads threads in the log.
|
||||
my $numqueries = $previousresult ? $previousresult->[1] : 0;
|
||||
|
||||
# Main loop
|
||||
while ( scalar keys %startstats < $numthreads || scalar keys %donestats < $numthreads) {
|
||||
$offset += 10000;
|
||||
if ( $offset > -s $logfile or $offset > 10_000_000 ) {
|
||||
die "Cannot find stats in $logfile\n";
|
||||
}
|
||||
$in->seek(-$offset, SEEK_END) or croak "cannot seek $logfile: $!\n";
|
||||
|
||||
for my $line ( <$in> ) {
|
||||
chomp($line);
|
||||
|
||||
#[1208777234] unbound[6705:0]
|
||||
if ($line =~ m/^\[\d+\] unbound\[\d+:(\d+)\]/) {
|
||||
$inthread = $1;
|
||||
if ($inthread + 1 > $numthreads) {
|
||||
die "Hey. lazy. change \$numthreads in this script to ($inthread)\n";
|
||||
}
|
||||
}
|
||||
# this line doesn't contain a pid:thread. skip.
|
||||
else {
|
||||
next;
|
||||
}
|
||||
|
||||
if ( $line =~ m/info: server stats for thread \d+: (\d+) queries, (\d+) answers from cache, (\d+) recursions/ ) {
|
||||
$startstats{$inthread} = 1;
|
||||
$allstats{$inthread}->{thread} = $inthread;
|
||||
$allstats{$inthread}->{queries} = $1;
|
||||
$allstats{$inthread}->{cachehits} = $2;
|
||||
$allstats{$inthread}->{recursions} = $3;
|
||||
}
|
||||
elsif ( $line =~ m/info: server stats for thread (\d+): requestlist max (\d+) avg ([0-9\.]+) exceeded (\d+)/ ) {
|
||||
$allstats{$inthread}->{outstandingmax} = $2;
|
||||
$allstats{$inthread}->{outstandingavg} = int($3); # This is a float; rrdtool only handles ints.
|
||||
$allstats{$inthread}->{outstandingexc} = $4;
|
||||
}
|
||||
elsif ( $line =~ m/info: average recursion processing time ([0-9\.]+) sec/ ) {
|
||||
$allstats{$inthread}->{recursionavg} = int($1 * 1000); # change sec to milisec.
|
||||
}
|
||||
elsif ( $line =~ m/info: histogram of recursion processing times/ ) {
|
||||
next;
|
||||
}
|
||||
elsif ( $line =~ m/info: \[25%\]=([0-9\.]+) median\[50%\]=([0-9\.]+) \[75%\]=([0-9\.]+)/ ) {
|
||||
$allstats{$inthread}->{median25} = int($1 * 1000000); # change seconds to usec
|
||||
$allstats{$inthread}->{median50} = int($2 * 1000000);
|
||||
$allstats{$inthread}->{median75} = int($3 * 1000000);
|
||||
}
|
||||
elsif ( $line =~ m/info: lower\(secs\) upper\(secs\) recursions/ ) {
|
||||
# since after this line we're unsure if we get these numbers
|
||||
# at all, we sould consider this marker as the end of the
|
||||
# block. Chances that we're parsing a file halfway written
|
||||
# at this stage are small. Bold statement.
|
||||
$donestats{$inthread} = 1;
|
||||
next;
|
||||
}
|
||||
elsif ( $line =~ m/info:\s+(\d+)\.(\d+)\s+(\d+)\.(\d+)\s+(\d+)/ ) {
|
||||
my ($froms, $fromus, $toms, $tous, $counter) = ($1, $2, $3, $4, $5);
|
||||
my $prefix = '';
|
||||
if ($froms > 0) {
|
||||
$allstats{$inthread}->{'s_' . int($froms)} = $counter;
|
||||
} else {
|
||||
$allstats{$inthread}->{'us_' . int($fromus)} = $counter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my @result;
|
||||
# loop on the records we want to store
|
||||
for my $key ( @records ) {
|
||||
my $sum = 0;
|
||||
# these are the different threads parsed
|
||||
foreach my $thread ( 0 .. $numthreads - 1 ) {
|
||||
$sum += ($allstats{$thread}->{$key} || 0);
|
||||
}
|
||||
print STDERR "$key = " . $sum . "\n" if $DEBUG;
|
||||
push @result, $sum;
|
||||
}
|
||||
print join("\n", @result), "\n";
|
||||
store \@result, UNBOUND_CACHE;
|
||||
|
||||
if ($DEBUG) {
|
||||
print STDERR "Threads: " . (scalar keys %allstats) . "\n";
|
||||
}
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# unbound freebsd startup rc.d script, modified from the named script.
|
||||
# uses the default unbound installation path and pidfile location.
|
||||
# copy this to /etc/rc.d/unbound
|
||||
# and put unbound_enable="YES" into rc.conf
|
||||
#
|
||||
|
||||
# PROVIDE: unbound
|
||||
# REQUIRE: SERVERS cleanvar
|
||||
# KEYWORD: shutdown
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name="unbound"
|
||||
rcvar=`set_rcvar`
|
||||
|
||||
load_rc_config $name
|
||||
|
||||
command="/usr/local/sbin/unbound"
|
||||
pidfile=${unbound_pidfile:-"/usr/local/etc/unbound/unbound.pid"}
|
||||
command_args=${unbound_flags:-"-c /usr/local/etc/unbound/unbound.conf"}
|
||||
extra_commands="reload"
|
||||
|
||||
run_rc_command "$1"
|
||||
@@ -0,0 +1,4 @@
|
||||
/etc/unbound(/.*)? system_u:object_r:unbound_conf_t:s0
|
||||
/etc/rc\.d/init\.d/unbound -- system_u:object_r:unbound_initrc_exec_t:s0
|
||||
/usr/sbin/unbound -- system_u:object_r:unbound_exec_t:s0
|
||||
/var/run/unbound(/.*)? system_u:object_r:unbound_var_run_t:s0
|
||||
@@ -0,0 +1,42 @@
|
||||
policy_module(unbound, 0.1.0)
|
||||
|
||||
type unbound_t;
|
||||
type unbound_conf_t;
|
||||
type unbound_exec_t;
|
||||
type unbound_initrc_exec_t;
|
||||
type unbound_var_run_t;
|
||||
|
||||
init_daemon_domain(unbound_t, unbound_exec_t)
|
||||
init_script_file(unbound_initrc_exec_t)
|
||||
|
||||
role system_r types unbound_t;
|
||||
|
||||
# XXX
|
||||
# unbound-{checkconf,control} are not protected. Do we need protect them?
|
||||
|
||||
# Unbound daemon
|
||||
|
||||
auth_use_nsswitch(unbound_t)
|
||||
dev_read_urand(unbound_t)
|
||||
corenet_all_recvfrom_unlabeled(unbound_t)
|
||||
corenet_tcp_bind_all_nodes(unbound_t)
|
||||
corenet_tcp_bind_dns_port(unbound_t)
|
||||
corenet_tcp_bind_rndc_port(unbound_t)
|
||||
corenet_udp_bind_all_nodes(unbound_t)
|
||||
corenet_udp_bind_all_unreserved_ports(unbound_t)
|
||||
corenet_udp_bind_dns_port(unbound_t)
|
||||
files_read_etc_files(unbound_t)
|
||||
files_pid_file(unbound_var_run_t)
|
||||
files_type(unbound_conf_t)
|
||||
libs_use_ld_so(unbound_t)
|
||||
libs_use_shared_libs(unbound_t)
|
||||
logging_send_syslog_msg(unbound_t)
|
||||
manage_files_pattern(unbound_t, unbound_var_run_t, unbound_var_run_t)
|
||||
miscfiles_read_localization(unbound_t)
|
||||
read_files_pattern(unbound_t, unbound_conf_t, unbound_conf_t)
|
||||
|
||||
allow unbound_t self:capability { setuid chown net_bind_service setgid dac_override };
|
||||
allow unbound_t self:tcp_socket create_stream_socket_perms;
|
||||
allow unbound_t self:udp_socket create_socket_perms;
|
||||
|
||||
###################################################
|
||||
@@ -0,0 +1,139 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# unbound This shell script takes care of starting and stopping
|
||||
# unbound (DNS server).
|
||||
#
|
||||
# chkconfig: - 14 86
|
||||
# description: unbound is a Domain Name Server (DNS) \
|
||||
# that is used to resolve host names to IP addresses.
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: $named unbound
|
||||
# Required-Start: $network $local_fs
|
||||
# Required-Stop: $network $local_fs
|
||||
# Should-Start: $syslog
|
||||
# Should-Stop: $syslog
|
||||
# Short-Description: unbound recursive Domain Name Server.
|
||||
# Description: unbound is a Domain Name Server (DNS)
|
||||
# that is used to resolve host names to IP addresses.
|
||||
### END INIT INFO
|
||||
|
||||
# Source function library.
|
||||
. /etc/rc.d/init.d/functions
|
||||
|
||||
exec="/usr/sbin/unbound"
|
||||
prog="unbound"
|
||||
config="/var/unbound/unbound.conf"
|
||||
pidfile="/var/unbound/unbound.pid"
|
||||
rootdir="/var/unbound"
|
||||
|
||||
[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
|
||||
|
||||
lockfile=/var/lock/subsys/$prog
|
||||
|
||||
start() {
|
||||
[ -x $exec ] || exit 5
|
||||
[ -f $config ] || exit 6
|
||||
echo -n $"Starting $prog: "
|
||||
|
||||
# setup root jail
|
||||
if [ -s /etc/localtime ]; then
|
||||
[ -d ${rootdir}/etc ] || mkdir -p ${rootdir}/etc ;
|
||||
if [ ! -e ${rootdir}/etc/localtime ] || /usr/bin/cmp -s /etc/localtime ${rootdir}/etc/localtime; then
|
||||
cp -fp /etc/localtime ${rootdir}/etc/localtime
|
||||
fi;
|
||||
fi;
|
||||
if [ -s /etc/resolv.conf ]; then
|
||||
[ -d ${rootdir}/etc ] || mkdir -p ${rootdir}/etc ;
|
||||
if [ ! -e ${rootdir}/etc/resolv.conf ] || /usr/bin/cmp -s /etc/resolv.conf ${rootdir}/etc/resolv.conf; then
|
||||
cp -fp /etc/resolv.conf ${rootdir}/etc/resolv.conf
|
||||
fi;
|
||||
fi;
|
||||
if ! egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}'/dev/log' /proc/mounts; then
|
||||
[ -d ${rootdir}/dev ] || mkdir -p ${rootdir}/dev ;
|
||||
[ -e ${rootdir}/dev/log ] || touch ${rootdir}/dev/log
|
||||
mount --bind -n /dev/log ${rootdir}/dev/log >/dev/null 2>&1;
|
||||
fi;
|
||||
if ! egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}'/dev/random' /proc/mounts; then
|
||||
[ -d ${rootdir}/dev ] || mkdir -p ${rootdir}/dev ;
|
||||
[ -e ${rootdir}/dev/random ] || touch ${rootdir}/dev/random
|
||||
mount --bind -n /dev/random ${rootdir}/dev/random >/dev/null 2>&1;
|
||||
fi;
|
||||
|
||||
# if not running, start it up here
|
||||
daemon $exec
|
||||
retval=$?
|
||||
echo
|
||||
[ $retval -eq 0 ] && touch $lockfile
|
||||
return $retval
|
||||
}
|
||||
|
||||
stop() {
|
||||
echo -n $"Stopping $prog: "
|
||||
# stop it here, often "killproc $prog"
|
||||
killproc -p $pidfile $prog
|
||||
retval=$?
|
||||
echo
|
||||
[ $retval -eq 0 ] && rm -f $lockfile
|
||||
if egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}'/dev/log' /proc/mounts; then
|
||||
umount ${rootdir}/dev/log >/dev/null 2>&1
|
||||
fi;
|
||||
if egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}'/dev/random' /proc/mounts; then
|
||||
umount ${rootdir}/dev/random >/dev/null 2>&1
|
||||
fi;
|
||||
return $retval
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
reload() {
|
||||
kill -HUP `cat $pidfile`
|
||||
}
|
||||
|
||||
force_reload() {
|
||||
restart
|
||||
}
|
||||
|
||||
rh_status() {
|
||||
# run checks to determine if the service is running or use generic status
|
||||
status -p $pidfile $prog
|
||||
}
|
||||
|
||||
rh_status_q() {
|
||||
rh_status -p $pidfile >/dev/null 2>&1
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
rh_status_q && exit 0
|
||||
$1
|
||||
;;
|
||||
stop)
|
||||
rh_status_q || exit 0
|
||||
$1
|
||||
;;
|
||||
restart)
|
||||
$1
|
||||
;;
|
||||
reload)
|
||||
rh_status_q || exit 7
|
||||
$1
|
||||
;;
|
||||
force-reload)
|
||||
force_reload
|
||||
;;
|
||||
status)
|
||||
rh_status
|
||||
;;
|
||||
condrestart|try-restart)
|
||||
rh_status_q || exit 0
|
||||
restart
|
||||
;;
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
|
||||
exit 2
|
||||
esac
|
||||
exit $?
|
||||
@@ -0,0 +1,119 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# unbound This shell script takes care of starting and stopping
|
||||
# unbound (DNS server).
|
||||
#
|
||||
# chkconfig: - 14 86
|
||||
# description: unbound is a Domain Name Server (DNS) \
|
||||
# that is used to resolve host names to IP addresses.
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: unbound
|
||||
# Required-Start: $network $local_fs
|
||||
# Required-Stop: $network $local_fs
|
||||
# Should-Start: $syslog
|
||||
# Should-Stop: $syslog
|
||||
# Short-Description: unbound recursive Domain Name Server.
|
||||
# Description: unbound is a Domain Name Server (DNS)
|
||||
# that is used to resolve host names to IP addresses.
|
||||
### END INIT INFO
|
||||
|
||||
# Source function library.
|
||||
. /etc/rc.d/init.d/functions
|
||||
|
||||
exec="/usr/sbin/unbound"
|
||||
config="/var/lib/unbound/unbound.conf"
|
||||
rootdir="/var/lib/unbound"
|
||||
pidfile="/var/run/unbound/unbound.pid"
|
||||
|
||||
[ -e /etc/sysconfig/unbound ] && . /etc/sysconfig/unbound
|
||||
|
||||
lockfile=/var/lock/subsys/unbound
|
||||
|
||||
start() {
|
||||
[ -x $exec ] || exit 5
|
||||
[ -f $config ] || exit 6
|
||||
echo -n $"Starting unbound: "
|
||||
|
||||
if [ ! -e ${rootdir}/etc/resolv.conf ] || /usr/bin/cmp -s /etc/resolv.conf ${rootdir}/etc/resolv.conf; then
|
||||
cp -fp /etc/resolv.conf ${rootdir}/etc/resolv.conf
|
||||
fi;
|
||||
if [ ! -e ${rootdir}/etc/localtime ] || /usr/bin/cmp -s /etc/localtime ${rootdir}/etc/localtime; then
|
||||
cp -fp /etc/localtime ${rootdir}/etc/localtime
|
||||
fi;
|
||||
mount --bind -n /dev/log ${rootdir}/dev/log >/dev/null 2>&1;
|
||||
mount --bind -n /dev/random ${rootdir}/dev/random >/dev/null 2>&1;
|
||||
mount --bind -n /var/run/unbound ${rootdir}/var/run/unbound >/dev/null 2>&1;
|
||||
|
||||
# if not running, start it up here
|
||||
daemon $exec
|
||||
retval=$?
|
||||
[ $retval -eq 0 ] && touch $lockfile
|
||||
echo
|
||||
}
|
||||
|
||||
stop() {
|
||||
echo -n $"Stopping unbound: "
|
||||
# stop it here, often "killproc unbound"
|
||||
killproc -p $pidfile unbound
|
||||
retval=$?
|
||||
[ $retval -eq 0 ] && rm -f $lockfile
|
||||
for mountfile in /dev/log /dev/random /etc/localtime /etc/resolv.conf /var/run/unbound
|
||||
do
|
||||
if egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}''${mountfile}'' /proc/mounts; then
|
||||
umount ${rootdir}$mountfile >/dev/null 2>&1
|
||||
fi;
|
||||
done
|
||||
echo
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
reload() {
|
||||
kill -HUP `cat $pidfile`
|
||||
}
|
||||
|
||||
force_reload() {
|
||||
restart
|
||||
}
|
||||
|
||||
rh_status() {
|
||||
# run checks to determine if the service is running or use generic status
|
||||
status -p $pidfile unbound
|
||||
}
|
||||
|
||||
rh_status_q() {
|
||||
rh_status -p $pidfile >/dev/null 2>&1
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart)
|
||||
restart
|
||||
;;
|
||||
reload)
|
||||
reload
|
||||
;;
|
||||
force-reload)
|
||||
force_reload
|
||||
;;
|
||||
status)
|
||||
rh_status
|
||||
;;
|
||||
condrestart|try-restart)
|
||||
rh_status_q || exit 0
|
||||
restart
|
||||
;;
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
|
||||
exit 2
|
||||
esac
|
||||
exit $?
|
||||
@@ -0,0 +1,103 @@
|
||||
Summary: Validating, recursive, and caching DNS resolver
|
||||
Name: unbound
|
||||
Version: 1.0.1
|
||||
Release: 1%{?dist}
|
||||
License: BSD
|
||||
Url: http://www.nlnetlabs.nl/unbound/
|
||||
Source: http://www.unbound.net/downloads/%{name}-%{version}.tar.gz
|
||||
#Source1: unbound.init
|
||||
Group: System Environment/Daemons
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
BuildRequires: flex, openssl-devel
|
||||
|
||||
%description
|
||||
Unbound is a validating, recursive, and caching DNS resolver.
|
||||
|
||||
The C implementation of Unbound is developed and maintained by NLnet
|
||||
Labs. It is based on ideas and algorithms taken from a java prototype
|
||||
developed by Verisign labs, Nominet, Kirei and ep.net.
|
||||
|
||||
Unbound is designed as a set of modular components, so that also
|
||||
DNSSEC (secure DNS) validation and stub-resolvers (that do not run
|
||||
as a server, but are linked into an application) are easily possible.
|
||||
|
||||
The source code is under a BSD License.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
# configure with /var/unbound/unbound.conf so that all default chroot,
|
||||
# pidfile and config file are in /var/unbound, ready for chroot jail set up.
|
||||
#
|
||||
# This is a build using libldns builtin version, the resulting binaries
|
||||
# do not require libldns and this package does not have version dependencies.
|
||||
# Could be smaller using a dependency on libldns (use --with-ldns=).
|
||||
%configure --with-conf-file=%{_localstatedir}/%{name}/unbound.conf --disable-rpath
|
||||
|
||||
%build
|
||||
#%{__make} %{?_smp_mflags}
|
||||
make
|
||||
|
||||
%install
|
||||
rm -rf %{buildroot}
|
||||
%{__make} DESTDIR=%{buildroot} install
|
||||
install -d 0700 %{buildroot}%{_localstatedir}/%{name}
|
||||
install -d 0755 %{buildroot}%{_initrddir}
|
||||
install -m 0755 contrib/unbound.init %{buildroot}%{_initrddir}/unbound
|
||||
# add symbolic link from /etc/unbound.conf -> /var/unbound/unbound.conf
|
||||
ln -s %{_localstatedir}/unbound/unbound.conf %{buildroot}%{_sysconfdir}/unbound.conf
|
||||
# remove static library from install (fedora packaging guidelines)
|
||||
rm -f %{buildroot}%{_libdir}/libunbound.a %{buildroot}%{_libdir}/libunbound.la
|
||||
|
||||
%clean
|
||||
rm -rf ${RPM_BUILD_ROOT}
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
%doc doc/README doc/CREDITS doc/LICENSE doc/FEATURES
|
||||
%attr(0755,root,root) %{_initrddir}/%{name}
|
||||
%attr(0700,%{name},%{name}) %dir %{_localstatedir}/%{name}
|
||||
%attr(0644,%{name},%{name}) %config(noreplace) %{_localstatedir}/%{name}/unbound.conf
|
||||
%attr(0644,%{name},%{name}) %config(noreplace) %{_sysconfdir}/unbound.conf
|
||||
%{_sbindir}/*
|
||||
%{_mandir}/*/*
|
||||
%{_includedir}/*
|
||||
%{_libdir}/libunbound*
|
||||
|
||||
%pre
|
||||
getent group unbound >/dev/null || groupadd -r unbound
|
||||
getent passwd unbound >/dev/null || \
|
||||
useradd -r -g unbound -d /var/unbound -s /sbin/nologin \
|
||||
-c "unbound name daemon" unbound
|
||||
exit 0
|
||||
|
||||
%post
|
||||
# This adds the proper /etc/rc*.d links for the script
|
||||
/sbin/chkconfig --add %{name}
|
||||
|
||||
%preun
|
||||
if [ $1 -eq 0 ]; then
|
||||
/sbin/service %{name} stop >/dev/null 2>&1
|
||||
/sbin/chkconfig --del %{name}
|
||||
# remove root jail
|
||||
rm -f /var/unbound/dev/log /var/unbound/dev/random /var/unbound/etc/localtime /var/unbound/etc/resolv.conf >/dev/null 2>&1
|
||||
rmdir /var/unbound/dev >/dev/null 2>&1 || :
|
||||
rmdir /var/unbound/etc >/dev/null 2>&1 || :
|
||||
rmdir /var/unbound >/dev/null 2>&1 || :
|
||||
fi
|
||||
|
||||
%postun
|
||||
if [ "$1" -ge "1" ]; then
|
||||
/sbin/service %{name} condrestart >/dev/null 2>&1 || :
|
||||
fi
|
||||
|
||||
%changelog
|
||||
* Thu May 22 2008 Wouter Wijngaards <wouter@nlnetlabs.nl> - 1.0.0
|
||||
- contrib changes from Patrick Vande Walle.
|
||||
|
||||
* Thu Apr 25 2008 Wouter Wijngaards <wouter@nlnetlabs.nl> - 0.12
|
||||
- Using parts from ports collection entry by Jaap Akkerhuis.
|
||||
- Using Fedoraproject wiki guidelines.
|
||||
|
||||
* Wed Apr 23 2008 Wouter Wijngaards <wouter@nlnetlabs.nl> - 0.11
|
||||
- Initial version.
|
||||
@@ -0,0 +1,185 @@
|
||||
Summary: Validating, recursive, and caching DNS(SEC) resolver
|
||||
Name: unbound
|
||||
Version: 1.1.0
|
||||
Release: 1%{?dist}
|
||||
License: BSD
|
||||
Url: http://www.nlnetlabs.nl/unbound/
|
||||
Source: http://www.unbound.net/downloads/%{name}-%{version}.tar.gz
|
||||
Source1: unbound.init
|
||||
Source2: unbound.conf
|
||||
Group: System Environment/Daemons
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
BuildRequires: flex, openssl-devel, ldns-devel >= 1.3.0, libevent-devel
|
||||
Requires(post): chkconfig
|
||||
Requires(preun): chkconfig
|
||||
Requires(preun): initscripts
|
||||
Requires(postun): initscripts
|
||||
Requires: ldns >= 1.3.0
|
||||
Requires(pre): shadow-utils
|
||||
# Is this obsolete?
|
||||
#Provides: caching-nameserver
|
||||
|
||||
%description
|
||||
Unbound is a validating, recursive, and caching DNS(SEC) resolver.
|
||||
|
||||
The C implementation of Unbound is developed and maintained by NLnet
|
||||
Labs. It is based on ideas and algorithms taken from a java prototype
|
||||
developed by Verisign labs, Nominet, Kirei and ep.net.
|
||||
|
||||
Unbound is designed as a set of modular components, so that also
|
||||
DNSSEC (secure DNS) validation and stub-resolvers (that do not run
|
||||
as a server, but are linked into an application) are easily possible.
|
||||
|
||||
%package devel
|
||||
Summary: Development package that includes the unbound header files
|
||||
Group: Development/Libraries
|
||||
Requires: %{name}-libs = %{version}-%{release}, openssl-devel, ldns-devel
|
||||
Requires: libevent-devel
|
||||
|
||||
%description devel
|
||||
The devel package contains the unbound library and the include files
|
||||
|
||||
%package libs
|
||||
Summary: Libraries used by the unbound server and client applications
|
||||
Group: Applications/System
|
||||
Requires(post): /sbin/ldconfig
|
||||
Requires(postun): /sbin/ldconfig
|
||||
|
||||
%description libs
|
||||
Contains libraries used by the unbound server and client applications
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
%configure --with-ldns= --with-libevent --with-pthreads --with-ssl \
|
||||
--disable-rpath --enable-debug --disable-static \
|
||||
--with-run-dir=%{_localstatedir}/lib/%{name}\
|
||||
--with-conf-file=%{_localstatedir}/lib/%{name}/unbound.conf \
|
||||
--with-pidfile=%{_localstatedir}/run/%{name}/%{name}.pid
|
||||
%{__make} CFLAGS="$RPM_OPT_FLAGS -D_GNU_SOURCE" QUIET=no %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
rm -rf %{buildroot}
|
||||
%{__make} DESTDIR=%{buildroot} install
|
||||
install -d 0755 %{buildroot}%{_localstatedir}/lib/%{name}
|
||||
install -d 0755 %{buildroot}%{_initrddir}
|
||||
#install -m 0755 contrib/unbound.init %{buildroot}%{_initrddir}/unbound
|
||||
install -m 0755 %{SOURCE1} %{buildroot}%{_initrddir}/unbound
|
||||
#overwrite stock unbound.conf with our own
|
||||
install -m 0755 %{SOURCE2} %{buildroot}%{_localstatedir}/lib/%{name}
|
||||
|
||||
# add symbolic link from /etc/unbound.conf -> /var/unbound/unbound.conf
|
||||
|
||||
( cd %{buildroot}%{_sysconfdir}/ ; ln -s ..%{_localstatedir}/lib/unbound/unbound.conf )
|
||||
# remove static library from install (fedora packaging guidelines)
|
||||
rm -rf %{buildroot}%{_libdir}/*.la
|
||||
|
||||
# The chroot needs /dev/log, /dev/random, /etc/resolv.conf and /etc/localtime
|
||||
# but the init script uses mount --bind, so just create empty files
|
||||
mkdir -p %{buildroot}%{_localstatedir}/lib/unbound/etc \
|
||||
%{buildroot}%{_localstatedir}/lib/unbound/dev
|
||||
echo "Used for mount --bind in initscript" > %{buildroot}%{_localstatedir}/lib/unbound/etc/resolv.conf
|
||||
echo "Used for mount --bind in initscript" > %{buildroot}%{_localstatedir}/lib/unbound/etc/localtime
|
||||
echo "Used for mount --bind in initscript" > %{buildroot}%{_localstatedir}/lib/unbound/dev/log
|
||||
echo "Used for mount --bind in initscript" > %{buildroot}%{_localstatedir}/lib/unbound/dev/random
|
||||
mkdir -p %{buildroot}%{_localstatedir}/lib/unbound/var/run/unbound
|
||||
mkdir -p %{buildroot}%{_localstatedir}/run/unbound
|
||||
|
||||
%clean
|
||||
rm -rf ${RPM_BUILD_ROOT}
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
%doc doc/README doc/CREDITS doc/LICENSE doc/FEATURES
|
||||
%attr(0755,root,root) %{_initrddir}/%{name}
|
||||
# the chroot env
|
||||
%attr(0755,root,root) %dir %{_localstatedir}/lib/%{name}
|
||||
%attr(0755,unbound,unbound) %dir %{_localstatedir}/run/%{name}
|
||||
%attr(0755,root,root) %dir %{_localstatedir}/lib/%{name}/dev
|
||||
%attr(0755,root,root) %dir %{_localstatedir}/lib/%{name}/etc
|
||||
%attr(0755,root,root) %dir %{_localstatedir}/lib/%{name}/var
|
||||
%attr(0755,root,root) %dir %{_localstatedir}/lib/%{name}/var/run
|
||||
%attr(0755,root,root) %dir %{_localstatedir}/lib/%{name}/var/run/unbound
|
||||
%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/unbound.conf
|
||||
%attr(0644,root,root) %config(noreplace) %{_localstatedir}/lib/%{name}/unbound.conf
|
||||
%attr(0644,root,root) %{_localstatedir}/lib/%{name}/dev/*
|
||||
%attr(0644,root,root) %{_localstatedir}/lib/%{name}/etc/*
|
||||
%{_sbindir}/*
|
||||
%{_mandir}/*/*
|
||||
|
||||
%files devel
|
||||
%defattr(-,root,root,-)
|
||||
%{_libdir}/libunbound.so
|
||||
%{_includedir}/unbound.h
|
||||
%doc README
|
||||
|
||||
%files libs
|
||||
%defattr(-,root,root,-)
|
||||
%{_libdir}/libunbound.so.*
|
||||
%doc doc/README doc/LICENSE
|
||||
|
||||
%pre
|
||||
getent group unbound >/dev/null || groupadd -r unbound
|
||||
getent passwd unbound >/dev/null || \
|
||||
useradd -r -g unbound -d %{_localstatedir}/lib/%{name} -s /sbin/nologin \
|
||||
-c "Unbound DNS resolver" unbound
|
||||
exit 0
|
||||
|
||||
%post
|
||||
/sbin/chkconfig --add %{name}
|
||||
|
||||
%post libs -p /sbin/ldconfig
|
||||
|
||||
|
||||
%preun
|
||||
if [ $1 -eq 0 ]; then
|
||||
/sbin/service %{name} stop >/dev/null 2>&1
|
||||
/sbin/chkconfig --del %{name}
|
||||
fi
|
||||
|
||||
%postun
|
||||
if [ "$1" -ge "1" ]; then
|
||||
/sbin/service %{name} condrestart >/dev/null 2>&1 || :
|
||||
fi
|
||||
|
||||
%postun libs -p /sbin/ldconfig
|
||||
|
||||
%changelog
|
||||
* Wed Oct 22 2008 Paul Wouters <paul@xelerance.com> - 1.0.2-5
|
||||
- Only call ldconfig in -libs package
|
||||
- Move configure into build section
|
||||
- devel subpackage should only depend on libs subpackage
|
||||
|
||||
* Tue Oct 21 2008 Paul Wouters <paul@xelerance.com> - 1.0.2-4
|
||||
- Fix CFLAGS getting lost in build
|
||||
- Don't enable interface-automatic:yes because that
|
||||
causes unbound to listen on 0.0.0.0 instead of 127.0.0.1
|
||||
|
||||
* Sun Oct 19 2008 Paul Wouters <paul@xelerance.com> - 1.0.2-3
|
||||
- Split off unbound-libs, make build verbose
|
||||
|
||||
* Thu Oct 9 2008 Paul Wouters <paul@xelerance.com> - 1.0.2-2
|
||||
- FSB compliance, chroot fixes, initscript fixes
|
||||
|
||||
* Thu Sep 11 2008 Paul Wouters <paul@xelerance.com> - 1.0.2-1
|
||||
- Upgraded to 1.0.2
|
||||
|
||||
* Wed Jul 16 2008 Paul Wouters <paul@xelerance.com> - 1.0.1-1
|
||||
- upgraded to new release
|
||||
|
||||
* Wed May 21 2008 Paul Wouters <paul@xelerance.com> - 1.0.0-2
|
||||
- Build against ldns-1.3.0
|
||||
|
||||
* Wed May 21 2008 Paul Wouters <paul@xelerance.com> - 1.0.0-1
|
||||
- Split of -devel package, fixed dependancies, make rpmlint happy
|
||||
|
||||
* Thu Apr 25 2008 Wouter Wijngaards <wouter@nlnetlabs.nl> - 0.12
|
||||
- Using parts from ports collection entry by Jaap Akkerhuis.
|
||||
- Using Fedoraproject wiki guidelines.
|
||||
|
||||
* Wed Apr 23 2008 Wouter Wijngaards <wouter@nlnetlabs.nl> - 0.11
|
||||
- Initial version.
|
||||
|
||||
|
||||
|
||||
Executable
+528
@@ -0,0 +1,528 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# plugin for munin to monitor usage of unbound servers.
|
||||
#
|
||||
# (C) 2008 W.C.A. Wijngaards. BSD Licensed.
|
||||
#
|
||||
# To install; enable statistics and unbound-control in unbound.conf
|
||||
# server: extended-statistics: yes
|
||||
# statistics-cumulative: no
|
||||
# statistics-interval: 0
|
||||
# remote-control: control-enable: yes
|
||||
# Run the command unbound-control-setup to generate the key files.
|
||||
#
|
||||
# Environment variables for this script
|
||||
# statefile - where to put temporary statefile.
|
||||
# unbound_conf - where the unbound.conf file is located.
|
||||
# unbound_control - where to find unbound-control executable.
|
||||
# spoof_warn - what level to warn about spoofing
|
||||
# spoof_crit - what level to crit about spoofing
|
||||
#
|
||||
# You can set them in your munin/plugin-conf.d/plugins.conf file
|
||||
# with:
|
||||
# [unbound*]
|
||||
# user root
|
||||
# env.statefile /usr/local/var/munin/plugin-state/unbound-state
|
||||
# env.unbound_conf /usr/local/etc/unbound/unbound.conf
|
||||
# env.unbound_control /usr/local/sbin/unbound-control
|
||||
# env.spoof_warn 1000
|
||||
# env.spoof_crit 100000
|
||||
#
|
||||
# This plugin can create different graphs depending on what name
|
||||
# you link it as (with ln -s) into the plugins directory
|
||||
# You can link it multiple times.
|
||||
# If you are only a casual user, the _hits and _by_type are most interesting,
|
||||
# possibly followed by _by_rcode.
|
||||
#
|
||||
# unbound_munin_hits - base volume, cache hits, unwanted traffic
|
||||
# unbound_munin_queue - to monitor the internal requestlist
|
||||
# unbound_munin_memory - memory usage
|
||||
# unbound_munin_by_type - incoming queries by type
|
||||
# unbound_munin_by_class - incoming queries by class
|
||||
# unbound_munin_by_opcode - incoming queries by opcode
|
||||
# unbound_munin_by_rcode - answers by rcode, validation status
|
||||
# unbound_munin_by_flags - incoming queries by flags
|
||||
# unbound_munin_histogram - histogram of query resolving times
|
||||
#
|
||||
# Magic markers - optional - used by installation scripts and
|
||||
# munin-config:
|
||||
#
|
||||
#%# family=contrib
|
||||
#%# capabilities=autoconf suggest
|
||||
|
||||
# POD documentation
|
||||
: <<=cut
|
||||
=head1 NAME
|
||||
|
||||
unbound_munin_ - Munin plugin to monitor the Unbound DNS resolver.
|
||||
|
||||
=head1 APPLICABLE SYSTEMS
|
||||
|
||||
System with unbound daemon.
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
[unbound*]
|
||||
user root
|
||||
env.statefile /usr/local/var/munin/plugin-state/unbound-state
|
||||
env.unbound_conf /usr/local/etc/unbound/unbound.conf
|
||||
env.unbound_control /usr/local/sbin/unbound-control
|
||||
env.spoof_warn 1000
|
||||
env.spoof_crit 100000
|
||||
|
||||
Use the .env settings to override the defaults.
|
||||
|
||||
=head1 USAGE
|
||||
|
||||
Can be used to present different graphs. Use ln -s for that name in
|
||||
the plugins directory to enable the graph.
|
||||
unbound_munin_hits - base volume, cache hits, unwanted traffic
|
||||
unbound_munin_queue - to monitor the internal requestlist
|
||||
unbound_munin_memory - memory usage
|
||||
unbound_munin_by_type - incoming queries by type
|
||||
unbound_munin_by_class - incoming queries by class
|
||||
unbound_munin_by_opcode - incoming queries by opcode
|
||||
unbound_munin_by_rcode - answers by rcode, validation status
|
||||
unbound_munin_by_flags - incoming queries by flags
|
||||
unbound_munin_histogram - histogram of query resolving times
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Copyright 2008 W.C.A. Wijngaards
|
||||
|
||||
=head1 LICENSE
|
||||
|
||||
BSD
|
||||
|
||||
=cut
|
||||
|
||||
state=${statefile:-/usr/local/var/munin/plugin-state/unbound-state}
|
||||
conf=${unbound_conf:-/usr/local/etc/unbound/unbound.conf}
|
||||
ctrl=${unbound_control:-/usr/local/sbin/unbound-control}
|
||||
warn=${spoof_warn:-1000}
|
||||
crit=${spoof_crit:-100000}
|
||||
lock=$state.lock
|
||||
|
||||
# number of seconds between polling attempts.
|
||||
# makes the statefile hang around for at least this many seconds,
|
||||
# so that multiple links of this script can share the results.
|
||||
lee=55
|
||||
|
||||
# to keep things within 19 characters
|
||||
ABBREV="-e s/total/t/ -e s/thread/t/ -e s/num/n/ -e s/query/q/ -e s/answer/a/ -e s/unwanted/u/ -e s/requestlist/ql/ -e s/type/t/ -e s/class/c/ -e s/opcode/o/ -e s/rcode/r/ -e s/edns/e/ -e s/mem/m/ -e s/cache/c/ -e s/mod/m/"
|
||||
|
||||
# get value from $1 into return variable $value
|
||||
get_value ( ) {
|
||||
value="`grep '^'$1'=' $state | sed -e 's/^.*=//'`"
|
||||
if test "$value"x = ""x; then
|
||||
value="0"
|
||||
fi
|
||||
}
|
||||
|
||||
# download the state from the unbound server.
|
||||
get_state ( ) {
|
||||
# obtain lock for fetching the state
|
||||
# because there is a race condition in fetching and writing to file
|
||||
i=0
|
||||
while test ! -f $lock || test "`cat $lock`" != $$; do
|
||||
while test -f $lock; do
|
||||
# wait
|
||||
i=`expr $i + 1`
|
||||
if test $i -gt 1000; then
|
||||
sleep 1;
|
||||
fi
|
||||
if test $i -gt 1500; then
|
||||
echo "error locking $lock" "=" `cat $lock`
|
||||
rm -f $lock
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
# try to get it
|
||||
echo $$ >$lock
|
||||
done
|
||||
# do not refetch if the file exists and only LEE seconds old
|
||||
if test -f $state; then
|
||||
now=`date +%s`
|
||||
get_value "time.now"
|
||||
value="`echo $value | sed -e 's/\..*$//'`"
|
||||
if test $now -lt `expr $value + $lee`; then
|
||||
rm -f $lock
|
||||
return
|
||||
fi
|
||||
fi
|
||||
$ctrl -c $conf stats > $state
|
||||
if test $? -ne 0; then
|
||||
echo "error retrieving data from unbound server"
|
||||
rm -f $lock
|
||||
exit 1
|
||||
fi
|
||||
rm -f $lock
|
||||
}
|
||||
|
||||
if test "$1" = "autoconf" ; then
|
||||
if test ! -f $conf; then
|
||||
echo no "($conf does not exist)"
|
||||
exit 1
|
||||
fi
|
||||
if test ! -d `dirname $state`; then
|
||||
echo no "($state directory does not exist)"
|
||||
exit 1
|
||||
fi
|
||||
echo yes
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test "$1" = "suggest" ; then
|
||||
echo "hits"
|
||||
echo "queue"
|
||||
echo "memory"
|
||||
echo "by_type"
|
||||
echo "by_class"
|
||||
echo "by_opcode"
|
||||
echo "by_rcode"
|
||||
echo "by_flags"
|
||||
echo "histogram"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# determine my type, by name
|
||||
id=`echo $0 | sed -e 's/^.*unbound_munin_//'`
|
||||
if test "$id"x = ""x; then
|
||||
# some default to keep people sane.
|
||||
id="hits"
|
||||
fi
|
||||
|
||||
# if $1 exists in statefile, config is echoed with label $2
|
||||
exist_config ( ) {
|
||||
mn=`echo $1 | sed $ABBREV | tr . _`
|
||||
if grep '^'$1'=' $state >/dev/null 2>&1; then
|
||||
echo "$mn.label $2"
|
||||
echo "$mn.min 0"
|
||||
fi
|
||||
}
|
||||
|
||||
# print label and min 0 for a name $1 in unbound format
|
||||
p_config ( ) {
|
||||
mn=`echo $1 | sed $ABBREV | tr . _`
|
||||
echo $mn.label "$2"
|
||||
echo $mn.min 0
|
||||
}
|
||||
|
||||
if test "$1" = "config" ; then
|
||||
if test ! -f $state; then
|
||||
get_state
|
||||
fi
|
||||
case $id in
|
||||
hits)
|
||||
echo "graph_title Unbound DNS traffic and cache hits"
|
||||
echo "graph_args --base 1000 -l 0"
|
||||
echo "graph_vlabel queries / second"
|
||||
echo "graph_category DNS"
|
||||
for x in thread0.num.queries thread1.num.queries \
|
||||
thread2.num.queries thread3.num.queries thread4.num.queries \
|
||||
thread5.num.queries thread6.num.queries thread7.num.queries; do
|
||||
exist_config $x "queries handled by `basename $x .num.queries`"
|
||||
done
|
||||
p_config "total.num.queries" "total queries from clients"
|
||||
p_config "total.num.cachehits" "cache hits"
|
||||
p_config "num.query.tcp" "TCP queries"
|
||||
p_config "num.query.ipv6" "IPv6 queries"
|
||||
p_config "unwanted.queries" "queries that failed acl"
|
||||
p_config "unwanted.replies" "unwanted or unsolicited replies"
|
||||
echo "u_replies.warning $warn"
|
||||
echo "u_replies.critical $crit"
|
||||
echo "graph_info DNS queries to the recursive resolver. The unwanted replies could be innocent duplicate packets, late replies, or spoof threats."
|
||||
;;
|
||||
queue)
|
||||
echo "graph_title Unbound requestlist size"
|
||||
echo "graph_args --base 1000 -l 0"
|
||||
echo "graph_vlabel number of queries"
|
||||
echo "graph_category DNS"
|
||||
p_config "total.requestlist.avg" "Average size of queue on insert"
|
||||
p_config "total.requestlist.max" "Max size of queue (in 5 min)"
|
||||
p_config "total.requestlist.overwritten" "Number of queries replaced by new ones"
|
||||
p_config "total.requestlist.exceeded" "Number of queries dropped due to lack of space"
|
||||
echo "graph_info The queries that did not hit the cache and need recursion service take up space in the requestlist. If there are too many queries, first queries get overwritten, and at last resort dropped."
|
||||
;;
|
||||
memory)
|
||||
echo "graph_title Unbound memory usage"
|
||||
echo "graph_args --base 1024 -l 0"
|
||||
echo "graph_vlabel memory used in bytes"
|
||||
echo "graph_category DNS"
|
||||
p_config "mem.total.sbrk" "Total memory"
|
||||
p_config "mem.cache.rrset" "RRset cache memory"
|
||||
p_config "mem.cache.message" "Message cache memory"
|
||||
p_config "mem.mod.iterator" "Iterator module memory"
|
||||
p_config "mem.mod.validator" "Validator module and key cache memory"
|
||||
echo "graph_info The memory used by unbound."
|
||||
;;
|
||||
by_type)
|
||||
echo "graph_title Unbound DNS queries by type"
|
||||
echo "graph_args --base 1000 -l 0"
|
||||
echo "graph_vlabel queries / second"
|
||||
echo "graph_category DNS"
|
||||
for x in `grep "^num.query.type" $state`; do
|
||||
nm=`echo $x | sed -e 's/=.*$//'`
|
||||
tp=`echo $nm | sed -e s/num.query.type.//`
|
||||
p_config "$nm" "$tp"
|
||||
done
|
||||
echo "graph_info queries by DNS RR type queried for"
|
||||
;;
|
||||
by_class)
|
||||
echo "graph_title Unbound DNS queries by class"
|
||||
echo "graph_args --base 1000 -l 0"
|
||||
echo "graph_vlabel queries / second"
|
||||
echo "graph_category DNS"
|
||||
for x in `grep "^num.query.class" $state`; do
|
||||
nm=`echo $x | sed -e 's/=.*$//'`
|
||||
tp=`echo $nm | sed -e s/num.query.class.//`
|
||||
p_config "$nm" "$tp"
|
||||
done
|
||||
echo "graph_info queries by DNS RR class queried for."
|
||||
;;
|
||||
by_opcode)
|
||||
echo "graph_title Unbound DNS queries by opcode"
|
||||
echo "graph_args --base 1000 -l 0"
|
||||
echo "graph_vlabel queries / second"
|
||||
echo "graph_category DNS"
|
||||
for x in `grep "^num.query.opcode" $state`; do
|
||||
nm=`echo $x | sed -e 's/=.*$//'`
|
||||
tp=`echo $nm | sed -e s/num.query.opcode.//`
|
||||
p_config "$nm" "$tp"
|
||||
done
|
||||
echo "graph_info queries by opcode in the query packet."
|
||||
;;
|
||||
by_rcode)
|
||||
echo "graph_title Unbound DNS answers by return code"
|
||||
echo "graph_args --base 1000 -l 0"
|
||||
echo "graph_vlabel answer packets / second"
|
||||
echo "graph_category DNS"
|
||||
for x in `grep "^num.answer.rcode" $state`; do
|
||||
nm=`echo $x | sed -e 's/=.*$//'`
|
||||
tp=`echo $nm | sed -e s/num.answer.rcode.//`
|
||||
p_config "$nm" "$tp"
|
||||
done
|
||||
p_config "num.answer.secure" "answer secure"
|
||||
p_config "num.answer.bogus" "answer bogus"
|
||||
p_config "num.rrset.bogus" "num rrsets marked bogus"
|
||||
echo "graph_info answers sorted by return value. rrsets bogus is the number of rrsets marked bogus per second by the validator"
|
||||
;;
|
||||
by_flags)
|
||||
echo "graph_title Unbound DNS incoming queries by flags"
|
||||
echo "graph_args --base 1000 -l 0"
|
||||
echo "graph_vlabel queries / second"
|
||||
echo "graph_category DNS"
|
||||
p_config "num.query.flags.QR" "QR (query reply) flag"
|
||||
p_config "num.query.flags.AA" "AA (auth answer) flag"
|
||||
p_config "num.query.flags.TC" "TC (truncated) flag"
|
||||
p_config "num.query.flags.RD" "RD (recursion desired) flag"
|
||||
p_config "num.query.flags.RA" "RA (rec avail) flag"
|
||||
p_config "num.query.flags.Z" "Z (zero) flag"
|
||||
p_config "num.query.flags.AD" "AD (auth data) flag"
|
||||
p_config "num.query.flags.CD" "CD (check disabled) flag"
|
||||
p_config "num.query.edns.present" "EDNS OPT present"
|
||||
p_config "num.query.edns.DO" "DO (DNSSEC OK) flag"
|
||||
echo "graph_info This graphs plots the flags inside incoming queries. For example, if QR, AA, TC, RA, Z flags are set, the query can be rejected. RD, AD, CD and DO are legitimately set by some software."
|
||||
;;
|
||||
histogram)
|
||||
echo "graph_title Unbound DNS histogram of reply time"
|
||||
echo "graph_args --base 1000 -l 0"
|
||||
echo "graph_vlabel queries / second"
|
||||
echo "graph_category DNS"
|
||||
echo hcache.label "cache hits"
|
||||
echo hcache.min 0
|
||||
echo hcache.draw AREA
|
||||
echo hcache.colour 999999
|
||||
echo h64ms.label "0 msec - 66 msec"
|
||||
echo h64ms.min 0
|
||||
echo h64ms.draw STACK
|
||||
echo h64ms.colour 0000FF
|
||||
echo h128ms.label "66 msec - 131 msec"
|
||||
echo h128ms.min 0
|
||||
echo h128ms.colour 1F00DF
|
||||
echo h128ms.draw STACK
|
||||
echo h256ms.label "131 msec - 262 msec"
|
||||
echo h256ms.min 0
|
||||
echo h256ms.draw STACK
|
||||
echo h256ms.colour 3F00BF
|
||||
echo h512ms.label "262 msec - 524 msec"
|
||||
echo h512ms.min 0
|
||||
echo h512ms.draw STACK
|
||||
echo h512ms.colour 5F009F
|
||||
echo h1s.label "524 msec - 1 sec"
|
||||
echo h1s.min 0
|
||||
echo h1s.draw STACK
|
||||
echo h1s.colour 7F007F
|
||||
echo h2s.label "1 sec - 2 sec"
|
||||
echo h2s.min 0
|
||||
echo h2s.draw STACK
|
||||
echo h2s.colour 9F005F
|
||||
echo h4s.label "2 sec - 4 sec"
|
||||
echo h4s.min 0
|
||||
echo h4s.draw STACK
|
||||
echo h4s.colour BF003F
|
||||
echo h8s.label "4 sec - 8 sec"
|
||||
echo h8s.min 0
|
||||
echo h8s.draw STACK
|
||||
echo h8s.colour DF001F
|
||||
echo h16s.label "8 sec - ..."
|
||||
echo h16s.min 0
|
||||
echo h16s.draw STACK
|
||||
echo h16s.colour FF0000
|
||||
echo "graph_info Histogram of the reply times for queries."
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# do the stats itself
|
||||
get_state
|
||||
|
||||
# get the time elapsed
|
||||
get_value "time.elapsed"
|
||||
if test $value = 0 || test $value = "0.000000"; then
|
||||
echo "error: time elapsed 0 or could not retrieve data"
|
||||
exit 1
|
||||
fi
|
||||
elapsed="$value"
|
||||
|
||||
# print value for $1 / elapsed
|
||||
print_qps ( ) {
|
||||
mn=`echo $1 | sed $ABBREV | tr . _`
|
||||
get_value $1
|
||||
echo "$mn.value" `echo scale=6';' $value / $elapsed | bc `
|
||||
}
|
||||
|
||||
# print qps if line already found in $2
|
||||
print_qps_line ( ) {
|
||||
mn=`echo $1 | sed $ABBREV | tr . _`
|
||||
value="`echo $2 | sed -e 's/^.*=//'`"
|
||||
echo "$mn.value" `echo scale=6';' $value / $elapsed | bc `
|
||||
}
|
||||
|
||||
# print value for $1
|
||||
print_value ( ) {
|
||||
mn=`echo $1 | sed $ABBREV | tr . _`
|
||||
get_value $1
|
||||
echo "$mn.value" $value
|
||||
}
|
||||
|
||||
case $id in
|
||||
hits)
|
||||
for x in thread0.num.queries thread1.num.queries thread2.num.queries \
|
||||
thread3.num.queries thread4.num.queries thread5.num.queries \
|
||||
thread6.num.queries thread7.num.queries total.num.queries \
|
||||
total.num.cachehits num.query.tcp num.query.ipv6 \
|
||||
unwanted.queries unwanted.replies; do
|
||||
if grep "^"$x"=" $state >/dev/null 2>&1; then
|
||||
print_qps $x
|
||||
fi
|
||||
done
|
||||
;;
|
||||
queue)
|
||||
for x in total.requestlist.avg total.requestlist.max \
|
||||
total.requestlist.overwritten total.requestlist.exceeded; do
|
||||
print_value $x
|
||||
done
|
||||
;;
|
||||
memory)
|
||||
for x in mem.total.sbrk mem.cache.rrset mem.cache.message \
|
||||
mem.mod.iterator mem.mod.validator; do
|
||||
print_value $x
|
||||
done
|
||||
;;
|
||||
by_type)
|
||||
for x in `grep "^num.query.type" $state`; do
|
||||
nm=`echo $x | sed -e 's/=.*$//'`
|
||||
print_qps_line $nm $x
|
||||
done
|
||||
;;
|
||||
by_class)
|
||||
for x in `grep "^num.query.class" $state`; do
|
||||
nm=`echo $x | sed -e 's/=.*$//'`
|
||||
print_qps_line $nm $x
|
||||
done
|
||||
;;
|
||||
by_opcode)
|
||||
for x in `grep "^num.query.opcode" $state`; do
|
||||
nm=`echo $x | sed -e 's/=.*$//'`
|
||||
print_qps_line $nm $x
|
||||
done
|
||||
;;
|
||||
by_rcode)
|
||||
for x in `grep "^num.answer.rcode" $state`; do
|
||||
nm=`echo $x | sed -e 's/=.*$//'`
|
||||
print_qps_line $nm $x
|
||||
done
|
||||
print_qps "num.answer.secure"
|
||||
print_qps "num.answer.bogus"
|
||||
print_qps "num.rrset.bogus"
|
||||
;;
|
||||
by_flags)
|
||||
for x in num.query.flags.QR num.query.flags.AA num.query.flags.TC num.query.flags.RD num.query.flags.RA num.query.flags.Z num.query.flags.AD num.query.flags.CD num.query.edns.present num.query.edns.DO; do
|
||||
print_qps $x
|
||||
done
|
||||
;;
|
||||
histogram)
|
||||
get_value total.num.cachehits
|
||||
echo hcache.value `echo scale=6';' $value / $elapsed | bc `
|
||||
r=0
|
||||
for x in histogram.000000.000000.to.000000.000001 \
|
||||
histogram.000000.000001.to.000000.000002 \
|
||||
histogram.000000.000002.to.000000.000004 \
|
||||
histogram.000000.000004.to.000000.000008 \
|
||||
histogram.000000.000008.to.000000.000016 \
|
||||
histogram.000000.000016.to.000000.000032 \
|
||||
histogram.000000.000032.to.000000.000064 \
|
||||
histogram.000000.000064.to.000000.000128 \
|
||||
histogram.000000.000128.to.000000.000256 \
|
||||
histogram.000000.000256.to.000000.000512 \
|
||||
histogram.000000.000512.to.000000.001024 \
|
||||
histogram.000000.001024.to.000000.002048 \
|
||||
histogram.000000.002048.to.000000.004096 \
|
||||
histogram.000000.004096.to.000000.008192 \
|
||||
histogram.000000.008192.to.000000.016384 \
|
||||
histogram.000000.016384.to.000000.032768 \
|
||||
histogram.000000.032768.to.000000.065536; do
|
||||
get_value $x
|
||||
r=`expr $r + $value`
|
||||
done
|
||||
echo h64ms.value `echo scale=6';' $r / $elapsed | bc `
|
||||
get_value histogram.000000.065536.to.000000.131072
|
||||
echo h128ms.value `echo scale=6';' $value / $elapsed | bc `
|
||||
get_value histogram.000000.131072.to.000000.262144
|
||||
echo h256ms.value `echo scale=6';' $value / $elapsed | bc `
|
||||
get_value histogram.000000.262144.to.000000.524288
|
||||
echo h512ms.value `echo scale=6';' $value / $elapsed | bc `
|
||||
get_value histogram.000000.524288.to.000001.000000
|
||||
echo h1s.value `echo scale=6';' $value / $elapsed | bc `
|
||||
get_value histogram.000001.000000.to.000002.000000
|
||||
echo h2s.value `echo scale=6';' $value / $elapsed | bc `
|
||||
get_value histogram.000002.000000.to.000004.000000
|
||||
echo h4s.value `echo scale=6';' $value / $elapsed | bc `
|
||||
get_value histogram.000004.000000.to.000008.000000
|
||||
echo h8s.value `echo scale=6';' $value / $elapsed | bc `
|
||||
r=0
|
||||
for x in histogram.000008.000000.to.000016.000000 \
|
||||
histogram.000016.000000.to.000032.000000 \
|
||||
histogram.000032.000000.to.000064.000000 \
|
||||
histogram.000064.000000.to.000128.000000 \
|
||||
histogram.000128.000000.to.000256.000000 \
|
||||
histogram.000256.000000.to.000512.000000 \
|
||||
histogram.000512.000000.to.001024.000000 \
|
||||
histogram.001024.000000.to.002048.000000 \
|
||||
histogram.002048.000000.to.004096.000000 \
|
||||
histogram.004096.000000.to.008192.000000 \
|
||||
histogram.008192.000000.to.016384.000000 \
|
||||
histogram.016384.000000.to.032768.000000 \
|
||||
histogram.032768.000000.to.065536.000000 \
|
||||
histogram.065536.000000.to.131072.000000 \
|
||||
histogram.131072.000000.to.262144.000000 \
|
||||
histogram.262144.000000.to.524288.000000; do
|
||||
get_value $x
|
||||
r=`expr $r + $value`
|
||||
done
|
||||
echo h16s.value `echo scale=6';' $r / $elapsed | bc `
|
||||
;;
|
||||
esac
|
||||
Executable
+145
@@ -0,0 +1,145 @@
|
||||
#!/bin/sh
|
||||
# update-anchor.sh, update a trust anchor.
|
||||
# Copyright 2008, W.C.A. Wijngaards
|
||||
# This file is BSD licensed, see doc/LICENSE.
|
||||
|
||||
# which validating lookup to use.
|
||||
ubhost=unbound-host
|
||||
|
||||
usage ( )
|
||||
{
|
||||
echo "usage: update-anchor [-b] <zone name> <trust anchor file>"
|
||||
echo " performs an update of trust anchor file"
|
||||
echo " the trust anchor file is overwritten with the latest keys"
|
||||
echo " the trust anchor file should contain only keys for one zone"
|
||||
echo " -b causes keyfile to be made in bind format."
|
||||
echo " without -b the file is made in unbound format."
|
||||
echo " "
|
||||
echo "alternate:"
|
||||
echo " update-anchor [-b] -d directory"
|
||||
echo " update all <zone>.anchor files in the directory."
|
||||
echo " "
|
||||
echo " name the files br.anchor se.anchor ..., and include them in"
|
||||
echo " the validating resolver config file."
|
||||
echo " put keys for the root in a file with the name root.anchor."
|
||||
echo ""
|
||||
echo "Exit code 0 means anchors updated, 1 no changes, others are errors."
|
||||
exit 2
|
||||
}
|
||||
|
||||
if test $# -eq 0; then
|
||||
usage
|
||||
fi
|
||||
bindformat="no"
|
||||
filearg='-f'
|
||||
if test X"$1" = "X-b"; then
|
||||
shift
|
||||
bindformat="yes"
|
||||
filearg='-F'
|
||||
fi
|
||||
if test $# -ne 2; then
|
||||
echo "arguments wrong."
|
||||
usage
|
||||
fi
|
||||
|
||||
do_update ( ) {
|
||||
# arguments: <zonename> <keyfile>
|
||||
zonename="$1"
|
||||
keyfile="$2"
|
||||
tmp2=$tmpfile.2
|
||||
|
||||
tmpfile="/tmp/update-anchor.$$"
|
||||
$ubhost -v $filearg "$keyfile" -t DNSKEY "$zonename" >$tmpfile
|
||||
if test $? -ne 0; then
|
||||
rm -f $tmpfile
|
||||
echo "Error: Could not update zone $zonename anchor file $keyfile"
|
||||
echo "Cause: $ubhost lookup failed"
|
||||
echo " (Is the domain decommissioned? Is connectivity lost?)"
|
||||
return 2
|
||||
fi
|
||||
|
||||
# has the lookup been DNSSEC validated?
|
||||
if grep '(secure)$' $tmpfile >/dev/null 2>&1; then
|
||||
:
|
||||
else
|
||||
rm -f $tmpfile
|
||||
echo "Error: Could not update zone $zonename anchor file $keyfile"
|
||||
echo "Cause: result of lookup was not secure"
|
||||
echo " (keys too far out of date? domain changed ownership?)"
|
||||
return 3
|
||||
fi
|
||||
|
||||
if test $bindformat = "yes"; then
|
||||
# are there any KSK keys on board?
|
||||
echo 'trusted-keys {' > "$tmp2"
|
||||
if grep ' has DNSKEY record 257' $tmpfile >/dev/null 2>&1; then
|
||||
# store KSK keys in anchor file
|
||||
grep '(secure)$' $tmpfile | \
|
||||
grep ' has DNSKEY record 257' | \
|
||||
sed -e 's/ (secure)$/";/' | \
|
||||
sed -e 's/ has DNSKEY record \([0-9]*\) \([0-9]*\) \([0-9]*\) /. \1 \2 \3 "/' | \
|
||||
sed -e 's/^\.\././' | sort >> "$tmp2"
|
||||
else
|
||||
# store all keys in the anchor file
|
||||
grep '(secure)$' $tmpfile | \
|
||||
sed -e 's/ (secure)$/";/' | \
|
||||
sed -e 's/ has DNSKEY record \([0-9]*\) \([0-9]*\) \([0-9]*\) /. \1 \2 \3 "/' | \
|
||||
sed -e 's/^\.\././' | sort >> "$tmp2"
|
||||
fi
|
||||
echo '};' >> "$tmp2"
|
||||
else #not bindformat
|
||||
# are there any KSK keys on board?
|
||||
if grep ' has DNSKEY record 257' $tmpfile >/dev/null 2>&1; then
|
||||
# store KSK keys in anchor file
|
||||
grep '(secure)$' $tmpfile | \
|
||||
grep ' has DNSKEY record 257' | \
|
||||
sed -e 's/ (secure)$//' | \
|
||||
sed -e 's/ has DNSKEY record /. IN DNSKEY /' | \
|
||||
sed -e 's/^\.\././' | sort > "$tmp2"
|
||||
else
|
||||
# store all keys in the anchor file
|
||||
grep '(secure)$' $tmpfile | \
|
||||
sed -e 's/ (secure)$//' | \
|
||||
sed -e 's/ has DNSKEY record /. IN DNSKEY /' | \
|
||||
sed -e 's/^\.\././' | sort > "$tmp2"
|
||||
fi
|
||||
fi # endif-bindformat
|
||||
|
||||
# copy over if changed
|
||||
diff $tmp2 $keyfile >/dev/null 2>&1
|
||||
if test $? -eq 1; then # 0 means no change, 2 means trouble.
|
||||
cat $tmp2 > $keyfile
|
||||
no_updated=0
|
||||
echo "$zonename key file $keyfile updated."
|
||||
else
|
||||
echo "$zonename key file $keyfile unchanged."
|
||||
fi
|
||||
|
||||
rm -f $tmpfile $tmp2
|
||||
}
|
||||
|
||||
no_updated=1
|
||||
if test X"$1" = "X-d"; then
|
||||
tdir="$2"
|
||||
echo "start updating in $2"
|
||||
for x in $tdir/*.anchor; do
|
||||
if test `basename "$x"` = "root.anchor"; then
|
||||
zname="."
|
||||
else
|
||||
zname=`basename "$x" .anchor`
|
||||
fi
|
||||
do_update "$zname" "$x"
|
||||
done
|
||||
echo "done updating in $2"
|
||||
else
|
||||
# regular invocation
|
||||
if test X"$1" = "X."; then
|
||||
zname="$1"
|
||||
else
|
||||
# strip trailing dot from zone name
|
||||
zname="`echo $1 | sed -e 's/\.$//'`"
|
||||
fi
|
||||
kfile="$2"
|
||||
do_update $zname $kfile
|
||||
fi
|
||||
exit $no_updated
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* daemon/acl_list.h - client access control storage for the server.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file helps the server keep out queries from outside sources, that
|
||||
* should not be answered.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "daemon/acl_list.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/net_help.h"
|
||||
|
||||
struct acl_list*
|
||||
acl_list_create()
|
||||
{
|
||||
struct acl_list* acl = (struct acl_list*)calloc(1,
|
||||
sizeof(struct acl_list));
|
||||
if(!acl)
|
||||
return NULL;
|
||||
acl->region = regional_create();
|
||||
if(!acl->region) {
|
||||
acl_list_delete(acl);
|
||||
return NULL;
|
||||
}
|
||||
return acl;
|
||||
}
|
||||
|
||||
void
|
||||
acl_list_delete(struct acl_list* acl)
|
||||
{
|
||||
if(!acl)
|
||||
return;
|
||||
regional_destroy(acl->region);
|
||||
free(acl);
|
||||
}
|
||||
|
||||
/** insert new address into acl_list structure */
|
||||
static int
|
||||
acl_list_insert(struct acl_list* acl, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, int net, enum acl_access control,
|
||||
int complain_duplicates)
|
||||
{
|
||||
struct acl_addr* node = regional_alloc(acl->region,
|
||||
sizeof(struct acl_addr));
|
||||
if(!node)
|
||||
return 0;
|
||||
node->control = control;
|
||||
if(!addr_tree_insert(&acl->tree, &node->node, addr, addrlen, net)) {
|
||||
if(complain_duplicates)
|
||||
verbose(VERB_QUERY, "duplicate acl address ignored.");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** apply acl_list string */
|
||||
static int
|
||||
acl_list_str_cfg(struct acl_list* acl, const char* str, const char* s2,
|
||||
int complain_duplicates)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
int net;
|
||||
socklen_t addrlen;
|
||||
enum acl_access control;
|
||||
if(strcmp(s2, "allow") == 0)
|
||||
control = acl_allow;
|
||||
else if(strcmp(s2, "deny") == 0)
|
||||
control = acl_deny;
|
||||
else if(strcmp(s2, "refuse") == 0)
|
||||
control = acl_refuse;
|
||||
else if(strcmp(s2, "allow_snoop") == 0)
|
||||
control = acl_allow_snoop;
|
||||
else {
|
||||
log_err("access control type %s unknown", str);
|
||||
return 0;
|
||||
}
|
||||
if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
|
||||
log_err("cannot parse access control: %s %s", str, s2);
|
||||
return 0;
|
||||
}
|
||||
if(!acl_list_insert(acl, &addr, addrlen, net, control,
|
||||
complain_duplicates)) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** read acl_list config */
|
||||
static int
|
||||
read_acl_list(struct acl_list* acl, struct config_file* cfg)
|
||||
{
|
||||
struct config_str2list* p;
|
||||
for(p = cfg->acls; p; p = p->next) {
|
||||
log_assert(p->str && p->str2);
|
||||
if(!acl_list_str_cfg(acl, p->str, p->str2, 1))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg)
|
||||
{
|
||||
regional_free_all(acl->region);
|
||||
addr_tree_init(&acl->tree);
|
||||
if(!read_acl_list(acl, cfg))
|
||||
return 0;
|
||||
/* insert defaults, with '0' to ignore them if they are duplicates */
|
||||
if(!acl_list_str_cfg(acl, "0.0.0.0/0", "refuse", 0))
|
||||
return 0;
|
||||
if(!acl_list_str_cfg(acl, "127.0.0.0/8", "allow", 0))
|
||||
return 0;
|
||||
if(cfg->do_ip6) {
|
||||
if(!acl_list_str_cfg(acl, "::0/0", "refuse", 0))
|
||||
return 0;
|
||||
if(!acl_list_str_cfg(acl, "::1", "allow", 0))
|
||||
return 0;
|
||||
if(!acl_list_str_cfg(acl, "::ffff:127.0.0.1", "allow", 0))
|
||||
return 0;
|
||||
}
|
||||
addr_tree_init_parents(&acl->tree);
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum acl_access
|
||||
acl_list_lookup(struct acl_list* acl, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
struct acl_addr* r = (struct acl_addr*)addr_tree_lookup(&acl->tree,
|
||||
addr, addrlen);
|
||||
if(r) return r->control;
|
||||
return acl_deny;
|
||||
}
|
||||
|
||||
size_t
|
||||
acl_list_get_mem(struct acl_list* acl)
|
||||
{
|
||||
if(!acl) return 0;
|
||||
return sizeof(*acl) + regional_get_mem(acl->region);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* daemon/acl_list.h - client access control storage for the server.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file keeps track of the list of clients that are allowed to
|
||||
* access the server.
|
||||
*/
|
||||
|
||||
#ifndef DAEMON_ACL_LIST_H
|
||||
#define DAEMON_ACL_LIST_H
|
||||
#include "util/storage/dnstree.h"
|
||||
struct config_file;
|
||||
struct regional;
|
||||
|
||||
/**
|
||||
* Enumeration of access control options for an address range.
|
||||
* Allow or deny access.
|
||||
*/
|
||||
enum acl_access {
|
||||
/** disallow any access whatsoever, drop it */
|
||||
acl_deny = 0,
|
||||
/** disallow access, send a polite 'REFUSED' reply */
|
||||
acl_refuse,
|
||||
/** allow full access for recursion (+RD) queries */
|
||||
acl_allow,
|
||||
/** allow full access for all queries, recursion and cache snooping */
|
||||
acl_allow_snoop
|
||||
};
|
||||
|
||||
/**
|
||||
* Access control storage structure
|
||||
*/
|
||||
struct acl_list {
|
||||
/** regional for allocation */
|
||||
struct regional* region;
|
||||
/**
|
||||
* Tree of the addresses that are allowed/blocked.
|
||||
* contents of type acl_addr.
|
||||
*/
|
||||
rbtree_t tree;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* An address span with access control information
|
||||
*/
|
||||
struct acl_addr {
|
||||
/** node in address tree */
|
||||
struct addr_tree_node node;
|
||||
/** access control on this netblock */
|
||||
enum acl_access control;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create acl structure
|
||||
* @return new structure or NULL on error.
|
||||
*/
|
||||
struct acl_list* acl_list_create();
|
||||
|
||||
/**
|
||||
* Delete acl structure.
|
||||
* @param acl: to delete.
|
||||
*/
|
||||
void acl_list_delete(struct acl_list* acl);
|
||||
|
||||
/**
|
||||
* Process access control config.
|
||||
* @param acl: where to store.
|
||||
* @param cfg: config options.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
int acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Lookup address to see its access control status.
|
||||
* @param acl: structure for address storage.
|
||||
* @param addr: address to check
|
||||
* @param addrlen: length of addr.
|
||||
* @return: what to do with message from this address.
|
||||
*/
|
||||
enum acl_access acl_list_lookup(struct acl_list* acl,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen);
|
||||
|
||||
/**
|
||||
* Get memory used by acl structure.
|
||||
* @param acl: structure for address storage.
|
||||
* @return bytes in use.
|
||||
*/
|
||||
size_t acl_list_get_mem(struct acl_list* acl);
|
||||
|
||||
#endif /* DAEMON_ACL_LIST_H */
|
||||
@@ -0,0 +1,856 @@
|
||||
/*
|
||||
* daemon/cachedump.c - dump the cache to text format.
|
||||
*
|
||||
* Copyright (c) 2008, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to read and write the cache(s)
|
||||
* to text format.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "daemon/cachedump.h"
|
||||
#include "daemon/remote.h"
|
||||
#include "daemon/worker.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "iterator/iter_utils.h"
|
||||
|
||||
/** convert to ldns rr */
|
||||
static ldns_rr*
|
||||
to_rr(struct ub_packed_rrset_key* k, struct packed_rrset_data* d,
|
||||
uint32_t now, size_t i, uint16_t type)
|
||||
{
|
||||
ldns_rr* rr = ldns_rr_new();
|
||||
ldns_rdf* rdf;
|
||||
ldns_status status;
|
||||
size_t pos;
|
||||
log_assert(i < d->count + d->rrsig_count);
|
||||
if(!rr) {
|
||||
return NULL;
|
||||
}
|
||||
ldns_rr_set_type(rr, type);
|
||||
ldns_rr_set_class(rr, ntohs(k->rk.rrset_class));
|
||||
if(d->rr_ttl[i] < now)
|
||||
ldns_rr_set_ttl(rr, 0);
|
||||
else ldns_rr_set_ttl(rr, d->rr_ttl[i] - now);
|
||||
pos = 0;
|
||||
status = ldns_wire2dname(&rdf, k->rk.dname, k->rk.dname_len, &pos);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
/* we drop detailed error in status */
|
||||
ldns_rr_free(rr);
|
||||
return NULL;
|
||||
}
|
||||
ldns_rr_set_owner(rr, rdf);
|
||||
pos = 0;
|
||||
status = ldns_wire2rdf(rr, d->rr_data[i], d->rr_len[i], &pos);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
/* we drop detailed error in status */
|
||||
ldns_rr_free(rr);
|
||||
return NULL;
|
||||
}
|
||||
return rr;
|
||||
}
|
||||
|
||||
/** dump one rrset zonefile line */
|
||||
static int
|
||||
dump_rrset_line(SSL* ssl, struct ub_packed_rrset_key* k,
|
||||
struct packed_rrset_data* d, uint32_t now, size_t i, uint16_t type)
|
||||
{
|
||||
char* s;
|
||||
ldns_rr* rr = to_rr(k, d, now, i, type);
|
||||
if(!rr) {
|
||||
return ssl_printf(ssl, "BADRR\n");
|
||||
}
|
||||
s = ldns_rr2str(rr);
|
||||
ldns_rr_free(rr);
|
||||
if(!s) {
|
||||
return ssl_printf(ssl, "BADRR\n");
|
||||
}
|
||||
if(!ssl_printf(ssl, "%s", s)) {
|
||||
free(s);
|
||||
return 0;
|
||||
}
|
||||
free(s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** dump rrset key and data info */
|
||||
static int
|
||||
dump_rrset(SSL* ssl, struct ub_packed_rrset_key* k,
|
||||
struct packed_rrset_data* d, uint32_t now)
|
||||
{
|
||||
size_t i;
|
||||
/* rd lock held by caller */
|
||||
if(!k || !d) return 1;
|
||||
if(d->ttl < now) return 1; /* expired */
|
||||
|
||||
/* meta line */
|
||||
if(!ssl_printf(ssl, ";rrset%s %u %u %u %d %d\n",
|
||||
(k->rk.flags & PACKED_RRSET_NSEC_AT_APEX)?" nsec_apex":"",
|
||||
(unsigned)(d->ttl - now),
|
||||
(unsigned)d->count, (unsigned)d->rrsig_count,
|
||||
(int)d->trust, (int)d->security
|
||||
))
|
||||
return 0;
|
||||
for(i=0; i<d->count; i++) {
|
||||
if(!dump_rrset_line(ssl, k, d, now, i, ntohs(k->rk.type)))
|
||||
return 0;
|
||||
}
|
||||
for(i=0; i<d->rrsig_count; i++) {
|
||||
if(!dump_rrset_line(ssl, k, d, now, i+d->count,
|
||||
LDNS_RR_TYPE_RRSIG))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** dump lruhash rrset cache */
|
||||
static int
|
||||
dump_rrset_lruhash(SSL* ssl, struct lruhash* h, uint32_t now)
|
||||
{
|
||||
struct lruhash_entry* e;
|
||||
/* lruhash already locked by caller */
|
||||
/* walk in order of lru; best first */
|
||||
for(e=h->lru_start; e; e = e->lru_next) {
|
||||
lock_rw_rdlock(&e->lock);
|
||||
if(!dump_rrset(ssl, (struct ub_packed_rrset_key*)e->key,
|
||||
(struct packed_rrset_data*)e->data, now)) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 0;
|
||||
}
|
||||
lock_rw_unlock(&e->lock);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** dump rrset cache */
|
||||
static int
|
||||
dump_rrset_cache(SSL* ssl, struct worker* worker)
|
||||
{
|
||||
struct rrset_cache* r = worker->env.rrset_cache;
|
||||
size_t slab;
|
||||
if(!ssl_printf(ssl, "START_RRSET_CACHE\n")) return 0;
|
||||
for(slab=0; slab<r->table.size; slab++) {
|
||||
lock_quick_lock(&r->table.array[slab]->lock);
|
||||
if(!dump_rrset_lruhash(ssl, r->table.array[slab],
|
||||
*worker->env.now)) {
|
||||
lock_quick_unlock(&r->table.array[slab]->lock);
|
||||
return 0;
|
||||
}
|
||||
lock_quick_unlock(&r->table.array[slab]->lock);
|
||||
}
|
||||
return ssl_printf(ssl, "END_RRSET_CACHE\n");
|
||||
}
|
||||
|
||||
/** dump message to rrset reference */
|
||||
static int
|
||||
dump_msg_ref(SSL* ssl, struct ub_packed_rrset_key* k)
|
||||
{
|
||||
ldns_rdf* rdf;
|
||||
ldns_status status;
|
||||
size_t pos;
|
||||
char* nm, *tp, *cl;
|
||||
|
||||
pos = 0;
|
||||
status = ldns_wire2dname(&rdf, k->rk.dname, k->rk.dname_len, &pos);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
return ssl_printf(ssl, "BADREF\n");
|
||||
}
|
||||
nm = ldns_rdf2str(rdf);
|
||||
ldns_rdf_deep_free(rdf);
|
||||
tp = ldns_rr_type2str(ntohs(k->rk.type));
|
||||
cl = ldns_rr_class2str(ntohs(k->rk.rrset_class));
|
||||
if(!nm || !cl || !tp) {
|
||||
free(nm);
|
||||
free(tp);
|
||||
free(cl);
|
||||
return ssl_printf(ssl, "BADREF\n");
|
||||
}
|
||||
if(!ssl_printf(ssl, "%s %s %s %d\n", nm, cl, tp, (int)k->rk.flags)) {
|
||||
free(nm);
|
||||
free(tp);
|
||||
free(cl);
|
||||
return 0;
|
||||
}
|
||||
free(nm);
|
||||
free(tp);
|
||||
free(cl);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** dump message entry */
|
||||
static int
|
||||
dump_msg(SSL* ssl, struct query_info* k, struct reply_info* d,
|
||||
uint32_t now)
|
||||
{
|
||||
size_t i;
|
||||
char* nm, *tp, *cl;
|
||||
ldns_rdf* rdf;
|
||||
ldns_status status;
|
||||
size_t pos;
|
||||
if(!k || !d) return 1;
|
||||
if(d->ttl < now) return 1; /* expired */
|
||||
|
||||
pos = 0;
|
||||
status = ldns_wire2dname(&rdf, k->qname, k->qname_len, &pos);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
return 1; /* skip this entry */
|
||||
}
|
||||
nm = ldns_rdf2str(rdf);
|
||||
ldns_rdf_deep_free(rdf);
|
||||
tp = ldns_rr_type2str(k->qtype);
|
||||
cl = ldns_rr_class2str(k->qclass);
|
||||
if(!nm || !tp || !cl) {
|
||||
free(nm);
|
||||
free(tp);
|
||||
free(cl);
|
||||
return 1; /* skip this entry */
|
||||
}
|
||||
if(!rrset_array_lock(d->ref, d->rrset_count, now)) {
|
||||
/* rrsets have timed out or do not exist */
|
||||
free(nm);
|
||||
free(tp);
|
||||
free(cl);
|
||||
return 1; /* skip this entry */
|
||||
}
|
||||
|
||||
/* meta line */
|
||||
if(!ssl_printf(ssl, "msg %s %s %s %d %d %u %d %u %u %u\n",
|
||||
nm, cl, tp,
|
||||
(int)d->flags, (int)d->qdcount,
|
||||
(unsigned)(d->ttl-now), (int)d->security,
|
||||
(unsigned)d->an_numrrsets,
|
||||
(unsigned)d->ns_numrrsets,
|
||||
(unsigned)d->ar_numrrsets)) {
|
||||
free(nm);
|
||||
free(tp);
|
||||
free(cl);
|
||||
rrset_array_unlock(d->ref, d->rrset_count);
|
||||
return 0;
|
||||
}
|
||||
free(nm);
|
||||
free(tp);
|
||||
free(cl);
|
||||
|
||||
for(i=0; i<d->rrset_count; i++) {
|
||||
if(!dump_msg_ref(ssl, d->rrsets[i])) {
|
||||
rrset_array_unlock(d->ref, d->rrset_count);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
rrset_array_unlock(d->ref, d->rrset_count);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** copy msg to worker pad */
|
||||
static int
|
||||
copy_msg(struct regional* region, struct lruhash_entry* e,
|
||||
struct query_info** k, struct reply_info** d)
|
||||
{
|
||||
struct reply_info* rep = (struct reply_info*)e->data;
|
||||
*d = (struct reply_info*)regional_alloc_init(region, e->data,
|
||||
sizeof(struct reply_info) +
|
||||
sizeof(struct rrset_ref) * (rep->rrset_count-1) +
|
||||
sizeof(struct ub_packed_rrset_key*) * rep->rrset_count);
|
||||
if(!*d)
|
||||
return 0;
|
||||
(*d)->rrsets = (struct ub_packed_rrset_key**)(
|
||||
(uint8_t*)(&((*d)->ref[0])) +
|
||||
sizeof(struct rrset_ref) * rep->rrset_count);
|
||||
*k = (struct query_info*)regional_alloc_init(region,
|
||||
e->key, sizeof(struct query_info));
|
||||
if(!*k)
|
||||
return 0;
|
||||
(*k)->qname = regional_alloc_init(region,
|
||||
(*k)->qname, (*k)->qname_len);
|
||||
return (*k)->qname != NULL;
|
||||
}
|
||||
|
||||
/** dump lruhash msg cache */
|
||||
static int
|
||||
dump_msg_lruhash(SSL* ssl, struct worker* worker, struct lruhash* h)
|
||||
{
|
||||
struct lruhash_entry* e;
|
||||
struct query_info* k;
|
||||
struct reply_info* d;
|
||||
|
||||
/* lruhash already locked by caller */
|
||||
/* walk in order of lru; best first */
|
||||
for(e=h->lru_start; e; e = e->lru_next) {
|
||||
regional_free_all(worker->scratchpad);
|
||||
lock_rw_rdlock(&e->lock);
|
||||
/* make copy of rrset in worker buffer */
|
||||
if(!copy_msg(worker->scratchpad, e, &k, &d)) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 0;
|
||||
}
|
||||
lock_rw_unlock(&e->lock);
|
||||
/* release lock so we can lookup the rrset references
|
||||
* in the rrset cache */
|
||||
if(!dump_msg(ssl, k, d, *worker->env.now)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** dump msg cache */
|
||||
static int
|
||||
dump_msg_cache(SSL* ssl, struct worker* worker)
|
||||
{
|
||||
struct slabhash* sh = worker->env.msg_cache;
|
||||
size_t slab;
|
||||
if(!ssl_printf(ssl, "START_MSG_CACHE\n")) return 0;
|
||||
for(slab=0; slab<sh->size; slab++) {
|
||||
lock_quick_lock(&sh->array[slab]->lock);
|
||||
if(!dump_msg_lruhash(ssl, worker, sh->array[slab])) {
|
||||
lock_quick_unlock(&sh->array[slab]->lock);
|
||||
return 0;
|
||||
}
|
||||
lock_quick_unlock(&sh->array[slab]->lock);
|
||||
}
|
||||
return ssl_printf(ssl, "END_MSG_CACHE\n");
|
||||
}
|
||||
|
||||
int
|
||||
dump_cache(SSL* ssl, struct worker* worker)
|
||||
{
|
||||
if(!dump_rrset_cache(ssl, worker))
|
||||
return 0;
|
||||
if(!dump_msg_cache(ssl, worker))
|
||||
return 0;
|
||||
return ssl_printf(ssl, "EOF\n");
|
||||
}
|
||||
|
||||
/** read a line from ssl into buffer */
|
||||
static int
|
||||
ssl_read_buf(SSL* ssl, ldns_buffer* buf)
|
||||
{
|
||||
return ssl_read_line(ssl, (char*)ldns_buffer_begin(buf),
|
||||
ldns_buffer_capacity(buf));
|
||||
}
|
||||
|
||||
/** check fixed text on line */
|
||||
static int
|
||||
read_fixed(SSL* ssl, ldns_buffer* buf, const char* str)
|
||||
{
|
||||
if(!ssl_read_buf(ssl, buf)) return 0;
|
||||
return (strcmp((char*)ldns_buffer_begin(buf), str) == 0);
|
||||
}
|
||||
|
||||
/** load an RR into rrset */
|
||||
static int
|
||||
load_rr(SSL* ssl, ldns_buffer* buf, struct regional* region,
|
||||
struct ub_packed_rrset_key* rk, struct packed_rrset_data* d,
|
||||
unsigned int i, int is_rrsig, int* go_on, uint32_t now)
|
||||
{
|
||||
ldns_rr* rr;
|
||||
ldns_status status;
|
||||
|
||||
/* read the line */
|
||||
if(!ssl_read_buf(ssl, buf))
|
||||
return 0;
|
||||
if(strncmp((char*)ldns_buffer_begin(buf), "BADRR\n", 6) == 0) {
|
||||
*go_on = 0;
|
||||
return 1;
|
||||
}
|
||||
log_info("rd %s", (char*)ldns_buffer_begin(buf));
|
||||
status = ldns_rr_new_frm_str(&rr, (char*)ldns_buffer_begin(buf),
|
||||
LDNS_DEFAULT_TTL, NULL, NULL);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
(void)ssl_printf(ssl, "error cannot parse rr :%s: %s\n",
|
||||
ldns_get_errorstr_by_id(status),
|
||||
(char*)ldns_buffer_begin(buf));
|
||||
return 0;
|
||||
}
|
||||
if(is_rrsig && ldns_rr_get_type(rr) != LDNS_RR_TYPE_RRSIG) {
|
||||
(void)ssl_printf(ssl, "error expected rrsig but got %s\n",
|
||||
(char*)ldns_buffer_begin(buf));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* convert ldns rr into packed_rr */
|
||||
d->rr_ttl[i] = ldns_rr_ttl(rr) + now;
|
||||
ldns_buffer_clear(buf);
|
||||
ldns_buffer_skip(buf, 2);
|
||||
status = ldns_rr_rdata2buffer_wire(buf, rr);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
(void)ssl_printf(ssl, "error cannot rr2wire :%s\n",
|
||||
ldns_get_errorstr_by_id(status));
|
||||
ldns_rr_free(rr);
|
||||
return 0;
|
||||
}
|
||||
ldns_buffer_flip(buf);
|
||||
ldns_buffer_write_u16_at(buf, 0, ldns_buffer_limit(buf) - 2);
|
||||
|
||||
d->rr_len[i] = ldns_buffer_limit(buf);
|
||||
d->rr_data[i] = (uint8_t*)regional_alloc_init(region,
|
||||
ldns_buffer_begin(buf), ldns_buffer_limit(buf));
|
||||
if(!d->rr_data[i]) {
|
||||
ldns_rr_free(rr);
|
||||
(void)ssl_printf(ssl, "error out of memory\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if first entry, fill the key structure */
|
||||
if(i==0) {
|
||||
rk->rk.type = htons(ldns_rr_get_type(rr));
|
||||
rk->rk.rrset_class = htons(ldns_rr_get_class(rr));
|
||||
ldns_buffer_clear(buf);
|
||||
status = ldns_dname2buffer_wire(buf, ldns_rr_owner(rr));
|
||||
ldns_buffer_flip(buf);
|
||||
rk->rk.dname_len = ldns_buffer_limit(buf);
|
||||
rk->rk.dname = regional_alloc_init(region,
|
||||
ldns_buffer_begin(buf), ldns_buffer_limit(buf));
|
||||
if(!rk->rk.dname) {
|
||||
(void)ssl_printf(ssl, "error out of memory\n");
|
||||
ldns_rr_free(rr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ldns_rr_free(rr);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** move entry into cache */
|
||||
static int
|
||||
move_into_cache(SSL* ssl, struct ub_packed_rrset_key* k,
|
||||
struct packed_rrset_data* d, struct worker* worker)
|
||||
{
|
||||
struct ub_packed_rrset_key* ak;
|
||||
struct packed_rrset_data* ad;
|
||||
size_t s, i, num = d->count + d->rrsig_count;
|
||||
struct rrset_ref ref;
|
||||
uint8_t* p;
|
||||
|
||||
ak = alloc_special_obtain(&worker->alloc);
|
||||
if(!ak) {
|
||||
(void)ssl_printf(ssl, "error out of memory\n");
|
||||
return 0;
|
||||
}
|
||||
ak->entry.data = NULL;
|
||||
ak->rk = k->rk;
|
||||
ak->entry.hash = rrset_key_hash(&k->rk);
|
||||
ak->rk.dname = (uint8_t*)memdup(k->rk.dname, k->rk.dname_len);
|
||||
if(!ak->rk.dname) {
|
||||
(void)ssl_printf(ssl, "error out of memory\n");
|
||||
ub_packed_rrset_parsedelete(ak, &worker->alloc);
|
||||
return 0;
|
||||
}
|
||||
s = sizeof(*ad) + (sizeof(size_t) + sizeof(uint8_t*) +
|
||||
sizeof(uint32_t))* num;
|
||||
for(i=0; i<num; i++)
|
||||
s += d->rr_len[i];
|
||||
ad = (struct packed_rrset_data*)malloc(s);
|
||||
if(!ad) {
|
||||
ub_packed_rrset_parsedelete(ak, &worker->alloc);
|
||||
return 0;
|
||||
}
|
||||
p = (uint8_t*)ad;
|
||||
memmove(p, d, sizeof(*ad));
|
||||
p += sizeof(*ad);
|
||||
memmove(p, &d->rr_len[0], sizeof(size_t)*num);
|
||||
p += sizeof(size_t)*num;
|
||||
memmove(p, &d->rr_data[0], sizeof(uint8_t*)*num);
|
||||
p += sizeof(uint8_t*)*num;
|
||||
memmove(p, &d->rr_ttl[0], sizeof(uint32_t)*num);
|
||||
p += sizeof(uint32_t)*num;
|
||||
for(i=0; i<num; i++) {
|
||||
memmove(p, d->rr_data[i], d->rr_len[i]);
|
||||
p += d->rr_len[i];
|
||||
}
|
||||
packed_rrset_ptr_fixup(ad);
|
||||
|
||||
ak->entry.data = ad;
|
||||
|
||||
ref.key = ak;
|
||||
ref.id = ak->id;
|
||||
(void)rrset_cache_update(worker->env.rrset_cache, &ref,
|
||||
&worker->alloc, *worker->env.now);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** load an rrset entry */
|
||||
static int
|
||||
load_rrset(SSL* ssl, ldns_buffer* buf, struct worker* worker)
|
||||
{
|
||||
char* s = (char*)ldns_buffer_begin(buf);
|
||||
struct regional* region = worker->scratchpad;
|
||||
struct ub_packed_rrset_key* rk;
|
||||
struct packed_rrset_data* d;
|
||||
unsigned int ttl, rr_count, rrsig_count, trust, security;
|
||||
unsigned int i;
|
||||
int go_on = 1;
|
||||
regional_free_all(region);
|
||||
|
||||
rk = (struct ub_packed_rrset_key*)regional_alloc_zero(region,
|
||||
sizeof(*rk));
|
||||
d = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(*d));
|
||||
if(!rk || !d) {
|
||||
(void) ssl_printf(ssl, "error out of memory\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(strncmp(s, ";rrset", 6) != 0) {
|
||||
(void)ssl_printf(ssl, "error expected ';rrset' but got %s\n",
|
||||
s);
|
||||
return 0;
|
||||
}
|
||||
s += 6;
|
||||
if(strncmp(s, " nsec_apex", 10) == 0) {
|
||||
s += 10;
|
||||
rk->rk.flags |= PACKED_RRSET_NSEC_AT_APEX;
|
||||
}
|
||||
if(sscanf(s, " %u %u %u %u %u", &ttl, &rr_count, &rrsig_count,
|
||||
&trust, &security) != 5) {
|
||||
(void)ssl_printf(ssl, "error bad rrset spec %s\n", s);
|
||||
return 0;
|
||||
}
|
||||
if(rr_count == 0 && rrsig_count == 0)
|
||||
return 0;
|
||||
d->count = (size_t)rr_count;
|
||||
d->rrsig_count = (size_t)rrsig_count;
|
||||
d->security = (enum sec_status)security;
|
||||
d->trust = (enum rrset_trust)trust;
|
||||
d->ttl = (uint32_t)ttl + *worker->env.now;
|
||||
|
||||
d->rr_len = regional_alloc_zero(region,
|
||||
sizeof(size_t)*(d->count+d->rrsig_count));
|
||||
d->rr_ttl = regional_alloc_zero(region,
|
||||
sizeof(uint32_t)*(d->count+d->rrsig_count));
|
||||
d->rr_data = regional_alloc_zero(region,
|
||||
sizeof(uint8_t*)*(d->count+d->rrsig_count));
|
||||
if(!d->rr_len || !d->rr_ttl || !d->rr_data) {
|
||||
(void) ssl_printf(ssl, "error out of memory\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read the rr's themselves */
|
||||
for(i=0; i<rr_count; i++) {
|
||||
if(!load_rr(ssl, buf, region, rk, d, i, 0,
|
||||
&go_on, *worker->env.now)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
for(i=0; i<rrsig_count; i++) {
|
||||
if(!load_rr(ssl, buf, region, rk, d, i+rr_count, 1,
|
||||
&go_on, *worker->env.now)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if(!go_on) {
|
||||
/* skip this entry */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return move_into_cache(ssl, rk, d, worker);
|
||||
}
|
||||
|
||||
/** load rrset cache */
|
||||
static int
|
||||
load_rrset_cache(SSL* ssl, struct worker* worker)
|
||||
{
|
||||
ldns_buffer* buf = worker->env.scratch_buffer;
|
||||
if(!read_fixed(ssl, buf, "START_RRSET_CACHE")) return 0;
|
||||
while(ssl_read_buf(ssl, buf) &&
|
||||
strcmp((char*)ldns_buffer_begin(buf), "END_RRSET_CACHE")!=0) {
|
||||
if(!load_rrset(ssl, buf, worker))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** read qinfo from next three words */
|
||||
static char*
|
||||
load_qinfo(char* str, struct query_info* qinfo, ldns_buffer* buf,
|
||||
struct regional* region, SSL* ssl)
|
||||
{
|
||||
/* s is part of the buf */
|
||||
char* s = str;
|
||||
ldns_rr* rr;
|
||||
ldns_status status;
|
||||
|
||||
/* skip three words */
|
||||
s = strchr(str, ' ');
|
||||
if(s) s = strchr(s+1, ' ');
|
||||
if(s) s = strchr(s+1, ' ');
|
||||
if(!s) {
|
||||
(void)ssl_printf(ssl, "error line too short, %s\n", str);
|
||||
return NULL;
|
||||
}
|
||||
s[0] = 0;
|
||||
s++;
|
||||
|
||||
/* parse them */
|
||||
status = ldns_rr_new_question_frm_str(&rr, str, NULL, NULL);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
(void)ssl_printf(ssl, "error cannot parse: %s %s\n",
|
||||
ldns_get_errorstr_by_id(status), str);
|
||||
return NULL;
|
||||
}
|
||||
qinfo->qtype = ldns_rr_get_type(rr);
|
||||
qinfo->qclass = ldns_rr_get_class(rr);
|
||||
ldns_buffer_clear(buf);
|
||||
status = ldns_dname2buffer_wire(buf, ldns_rr_owner(rr));
|
||||
ldns_rr_free(rr);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
(void)ssl_printf(ssl, "error cannot dname2wire: %s\n",
|
||||
ldns_get_errorstr_by_id(status));
|
||||
return NULL;
|
||||
}
|
||||
ldns_buffer_flip(buf);
|
||||
qinfo->qname_len = ldns_buffer_limit(buf);
|
||||
qinfo->qname = (uint8_t*)regional_alloc_init(region,
|
||||
ldns_buffer_begin(buf), ldns_buffer_limit(buf));
|
||||
if(!qinfo->qname) {
|
||||
(void)ssl_printf(ssl, "error out of memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/** load a msg rrset reference */
|
||||
static int
|
||||
load_ref(SSL* ssl, ldns_buffer* buf, struct worker* worker,
|
||||
struct regional *region, struct ub_packed_rrset_key** rrset,
|
||||
int* go_on)
|
||||
{
|
||||
char* s = (char*)ldns_buffer_begin(buf);
|
||||
struct query_info qinfo;
|
||||
unsigned int flags;
|
||||
struct ub_packed_rrset_key* k;
|
||||
|
||||
/* read line */
|
||||
if(!ssl_read_buf(ssl, buf))
|
||||
return 0;
|
||||
if(strncmp(s, "BADREF", 6) == 0) {
|
||||
*go_on = 0; /* its bad, skip it and skip message */
|
||||
return 1;
|
||||
}
|
||||
|
||||
s = load_qinfo(s, &qinfo, buf, region, ssl);
|
||||
if(!s) {
|
||||
return 0;
|
||||
}
|
||||
if(sscanf(s, " %u", &flags) != 1) {
|
||||
(void)ssl_printf(ssl, "error cannot parse flags: %s\n", s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* lookup in cache */
|
||||
k = rrset_cache_lookup(worker->env.rrset_cache, qinfo.qname,
|
||||
qinfo.qname_len, qinfo.qtype, qinfo.qclass,
|
||||
(uint32_t)flags, *worker->env.now, 0);
|
||||
if(!k) {
|
||||
/* not found or expired */
|
||||
*go_on = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* store in result */
|
||||
*rrset = packed_rrset_copy_region(k, region, *worker->env.now);
|
||||
lock_rw_unlock(&k->entry.lock);
|
||||
|
||||
return (*rrset != NULL);
|
||||
}
|
||||
|
||||
/** load a msg entry */
|
||||
static int
|
||||
load_msg(SSL* ssl, ldns_buffer* buf, struct worker* worker)
|
||||
{
|
||||
struct regional* region = worker->scratchpad;
|
||||
struct query_info qinf;
|
||||
struct reply_info rep;
|
||||
char* s = (char*)ldns_buffer_begin(buf);
|
||||
unsigned int flags, qdcount, ttl, security, an, ns, ar;
|
||||
size_t i;
|
||||
int go_on = 1;
|
||||
|
||||
regional_free_all(region);
|
||||
|
||||
if(strncmp(s, "msg ", 4) != 0) {
|
||||
(void)ssl_printf(ssl, "error expected msg but got %s\n", s);
|
||||
return 0;
|
||||
}
|
||||
s += 4;
|
||||
s = load_qinfo(s, &qinf, buf, region, ssl);
|
||||
if(!s) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read remainder of line */
|
||||
if(sscanf(s, " %u %u %u %u %u %u %u", &flags, &qdcount, &ttl,
|
||||
&security, &an, &ns, &ar) != 7) {
|
||||
(void)ssl_printf(ssl, "error cannot parse numbers: %s\n", s);
|
||||
return 0;
|
||||
}
|
||||
rep.flags = (uint16_t)flags;
|
||||
rep.qdcount = (uint16_t)qdcount;
|
||||
rep.ttl = (uint32_t)ttl;
|
||||
rep.security = (enum sec_status)security;
|
||||
rep.an_numrrsets = (size_t)an;
|
||||
rep.ns_numrrsets = (size_t)ns;
|
||||
rep.ar_numrrsets = (size_t)ar;
|
||||
rep.rrset_count = (size_t)an+(size_t)ns+(size_t)ar;
|
||||
rep.rrsets = (struct ub_packed_rrset_key**)regional_alloc_zero(
|
||||
region, sizeof(struct ub_packed_rrset_key*)*rep.rrset_count);
|
||||
|
||||
/* fill repinfo with references */
|
||||
for(i=0; i<rep.rrset_count; i++) {
|
||||
if(!load_ref(ssl, buf, worker, region, &rep.rrsets[i],
|
||||
&go_on)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(!go_on)
|
||||
return 1; /* skip this one, not all references satisfied */
|
||||
|
||||
if(!dns_cache_store(&worker->env, &qinf, &rep, 0)) {
|
||||
(void)ssl_printf(ssl, "error out of memory\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** load msg cache */
|
||||
static int
|
||||
load_msg_cache(SSL* ssl, struct worker* worker)
|
||||
{
|
||||
ldns_buffer* buf = worker->env.scratch_buffer;
|
||||
if(!read_fixed(ssl, buf, "START_MSG_CACHE")) return 0;
|
||||
while(ssl_read_buf(ssl, buf) &&
|
||||
strcmp((char*)ldns_buffer_begin(buf), "END_MSG_CACHE")!=0) {
|
||||
if(!load_msg(ssl, buf, worker))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
load_cache(SSL* ssl, struct worker* worker)
|
||||
{
|
||||
if(!load_rrset_cache(ssl, worker))
|
||||
return 0;
|
||||
if(!load_msg_cache(ssl, worker))
|
||||
return 0;
|
||||
return read_fixed(ssl, worker->env.scratch_buffer, "EOF");
|
||||
}
|
||||
|
||||
int print_deleg_lookup(SSL* ssl, struct worker* worker, uint8_t* nm,
|
||||
size_t nmlen, int ATTR_UNUSED(nmlabs))
|
||||
{
|
||||
/* deep links into the iterator module */
|
||||
struct delegpt* dp;
|
||||
struct dns_msg* msg;
|
||||
struct regional* region = worker->scratchpad;
|
||||
char b[260];
|
||||
struct query_info qinfo;
|
||||
size_t i, n_ns, n_miss, n_addr, n_res, n_avail;
|
||||
regional_free_all(region);
|
||||
qinfo.qname = nm;
|
||||
qinfo.qname_len = nmlen;
|
||||
qinfo.qtype = LDNS_RR_TYPE_A;
|
||||
qinfo.qclass = LDNS_RR_CLASS_IN;
|
||||
|
||||
dname_str(nm, b);
|
||||
if(!ssl_printf(ssl, "The following name servers are used for lookup "
|
||||
"of %s\n", b))
|
||||
return 0;
|
||||
|
||||
while(1) {
|
||||
dp = dns_cache_find_delegation(&worker->env, nm, nmlen,
|
||||
qinfo.qtype, qinfo.qclass, region, &msg,
|
||||
*worker->env.now);
|
||||
if(!dp) {
|
||||
return ssl_printf(ssl, "no delegation from "
|
||||
"cache; goes to configured roots\n");
|
||||
}
|
||||
/* print the dp */
|
||||
for(i=0; i<msg->rep->rrset_count; i++) {
|
||||
struct ub_packed_rrset_key* k = msg->rep->rrsets[i];
|
||||
struct packed_rrset_data* d =
|
||||
(struct packed_rrset_data*)k->entry.data;
|
||||
if(d->security == sec_status_bogus) {
|
||||
if(!ssl_printf(ssl, "Address is BOGUS:\n"))
|
||||
return 0;
|
||||
}
|
||||
if(!dump_rrset(ssl, k, d, 0))
|
||||
return 0;
|
||||
}
|
||||
delegpt_count_ns(dp, &n_ns, &n_miss);
|
||||
delegpt_count_addr(dp, &n_addr, &n_res, &n_avail);
|
||||
/* since dp has not been used by iterator, all are available*/
|
||||
if(!ssl_printf(ssl, "Delegation with %d names, of which %d "
|
||||
"have no addresses in cache.\n"
|
||||
"It provides %d IP addresses. %s\n",
|
||||
(int)n_ns, (int)n_miss, (int)n_addr,
|
||||
(dp->bogus?"It is BOGUS":"") ))
|
||||
return 0;
|
||||
/* go up? */
|
||||
if(iter_dp_is_useless(&qinfo, BIT_RD, dp)) {
|
||||
if(!ssl_printf(ssl, "cache delegation was "
|
||||
"useless (no IP addresses)\n"))
|
||||
return 0;
|
||||
if(dname_is_root(nm)) {
|
||||
/* goes to root config */
|
||||
return ssl_printf(ssl, "no delegation from "
|
||||
"cache; goes to configured roots\n");
|
||||
} else {
|
||||
/* useless, goes up */
|
||||
nm = dp->name;
|
||||
nmlen = dp->namelen;
|
||||
dname_remove_label(&nm, &nmlen);
|
||||
dname_str(nm, b);
|
||||
if(!ssl_printf(ssl, "going up, lookup %s\n", b))
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* daemon/cachedump.h - dump the cache to text format.
|
||||
*
|
||||
* Copyright (c) 2008, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to read and write the cache(s)
|
||||
* to text format.
|
||||
*
|
||||
* The format of the file is as follows:
|
||||
* [RRset cache]
|
||||
* [Message cache]
|
||||
* EOF -- fixed string "EOF" before end of the file.
|
||||
*
|
||||
* The RRset cache is:
|
||||
* START_RRSET_CACHE
|
||||
* [rrset]*
|
||||
* END_RRSET_CACHE
|
||||
*
|
||||
* rrset is:
|
||||
* ;rrset [nsec_apex] TTL rr_count rrsig_count trust security
|
||||
* resource records, one per line, in zonefile format
|
||||
* rrsig records, one per line, in zonefile format
|
||||
* If the text conversion fails, BADRR is printed on the line.
|
||||
*
|
||||
* The Message cache is:
|
||||
* START_MSG_CACHE
|
||||
* [msg]*
|
||||
* END_MSG_CACHE
|
||||
*
|
||||
* msg is:
|
||||
* msg name class type flags qdcount ttl security an ns ar
|
||||
* list of rrset references, one per line. If conversion fails, BADREF
|
||||
* reference is:
|
||||
* name class type flags
|
||||
*
|
||||
* Expired cache entries are not printed.
|
||||
*/
|
||||
|
||||
#ifndef DAEMON_DUMPCACHE_H
|
||||
#define DAEMON_DUMPCACHE_H
|
||||
struct worker;
|
||||
|
||||
/**
|
||||
* Dump cache(s) to text
|
||||
* @param ssl: to print to
|
||||
* @param worker: worker that is available (buffers, etc) and has
|
||||
* ptrs to the caches.
|
||||
* @return false on ssl print error.
|
||||
*/
|
||||
int dump_cache(SSL* ssl, struct worker* worker);
|
||||
|
||||
/**
|
||||
* Load cache(s) from text
|
||||
* @param ssl: to read from
|
||||
* @param worker: worker that is available (buffers, etc) and has
|
||||
* ptrs to the caches.
|
||||
* @return false on ssl error.
|
||||
*/
|
||||
int load_cache(SSL* ssl, struct worker* worker);
|
||||
|
||||
/**
|
||||
* Print the delegation used to lookup for this name.
|
||||
* @param ssl: to read from
|
||||
* @param worker: worker that is available (buffers, etc) and has
|
||||
* ptrs to the caches.
|
||||
* @param nm: name to lookup
|
||||
* @param nmlen: length of name.
|
||||
* @param nmlabs: labels in name.
|
||||
* @return false on ssl error.
|
||||
*/
|
||||
int print_deleg_lookup(SSL* ssl, struct worker* worker, uint8_t* nm,
|
||||
size_t nmlen, int nmlabs);
|
||||
|
||||
#endif /* DAEMON_DUMPCACHE_H */
|
||||
+175
-148
@@ -42,6 +42,8 @@
|
||||
#include "config.h"
|
||||
#include "daemon/daemon.h"
|
||||
#include "daemon/worker.h"
|
||||
#include "daemon/remote.h"
|
||||
#include "daemon/acl_list.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/data/msgreply.h"
|
||||
@@ -49,10 +51,11 @@
|
||||
#include "services/listen_dnsport.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "services/localzone.h"
|
||||
#include "services/modstack.h"
|
||||
#include "util/module.h"
|
||||
#include "iterator/iterator.h"
|
||||
#include "validator/validator.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "util/random.h"
|
||||
#include "util/tube.h"
|
||||
#include <signal.h>
|
||||
|
||||
/** How many quit requests happened. */
|
||||
@@ -75,13 +78,24 @@ static RETSIGTYPE record_sigh(int sig)
|
||||
switch(sig)
|
||||
{
|
||||
case SIGTERM:
|
||||
#ifdef SIGQUIT
|
||||
case SIGQUIT:
|
||||
#endif
|
||||
#ifdef SIGBREAK
|
||||
case SIGBREAK:
|
||||
#endif
|
||||
case SIGINT:
|
||||
sig_record_quit++;
|
||||
break;
|
||||
#ifdef SIGHUP
|
||||
case SIGHUP:
|
||||
sig_record_reload++;
|
||||
break;
|
||||
#endif
|
||||
#ifdef SIGPIPE
|
||||
case SIGPIPE:
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
log_err("ignoring signal %d", sig);
|
||||
}
|
||||
@@ -95,9 +109,20 @@ static void
|
||||
signal_handling_record()
|
||||
{
|
||||
if( signal(SIGTERM, record_sigh) == SIG_ERR ||
|
||||
#ifdef SIGQUIT
|
||||
signal(SIGQUIT, record_sigh) == SIG_ERR ||
|
||||
signal(SIGINT, record_sigh) == SIG_ERR ||
|
||||
signal(SIGHUP, record_sigh) == SIG_ERR)
|
||||
#endif
|
||||
#ifdef SIGBREAK
|
||||
signal(SIGBREAK, record_sigh) == SIG_ERR ||
|
||||
#endif
|
||||
#ifdef SIGHUP
|
||||
signal(SIGHUP, record_sigh) == SIG_ERR ||
|
||||
#endif
|
||||
#ifdef SIGPIPE
|
||||
signal(SIGPIPE, SIG_IGN) == SIG_ERR ||
|
||||
#endif
|
||||
signal(SIGINT, record_sigh) == SIG_ERR
|
||||
)
|
||||
log_err("install sighandler: %s", strerror(errno));
|
||||
}
|
||||
|
||||
@@ -108,8 +133,10 @@ signal_handling_record()
|
||||
static void
|
||||
signal_handling_playback(struct worker* wrk)
|
||||
{
|
||||
#ifdef SIGHUP
|
||||
if(sig_record_reload)
|
||||
worker_sighandler(SIGHUP, wrk);
|
||||
#endif
|
||||
if(sig_record_quit)
|
||||
worker_sighandler(SIGTERM, wrk);
|
||||
sig_record_quit = 0;
|
||||
@@ -121,19 +148,46 @@ daemon_init()
|
||||
{
|
||||
struct daemon* daemon = (struct daemon*)calloc(1,
|
||||
sizeof(struct daemon));
|
||||
#ifdef USE_WINSOCK
|
||||
int r;
|
||||
WSADATA wsa_data;
|
||||
#endif
|
||||
if(!daemon)
|
||||
return NULL;
|
||||
#ifdef USE_WINSOCK
|
||||
r = WSAStartup(MAKEWORD(2,2), &wsa_data);
|
||||
if(r != 0) {
|
||||
fatal_exit("could not init winsock. WSAStartup: %s",
|
||||
wsa_strerror(r));
|
||||
}
|
||||
#endif /* USE_WINSOCK */
|
||||
signal_handling_record();
|
||||
checklock_start();
|
||||
ERR_load_crypto_strings();
|
||||
ERR_load_SSL_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
(void)SSL_library_init();
|
||||
#ifdef HAVE_TZSET
|
||||
/* init timezone info while we are not chrooted yet */
|
||||
tzset();
|
||||
#endif
|
||||
daemon->need_to_exit = 0;
|
||||
daemon->num_modules = 0;
|
||||
modstack_init(&daemon->mods);
|
||||
if(!(daemon->env = (struct module_env*)calloc(1,
|
||||
sizeof(*daemon->env)))) {
|
||||
free(daemon);
|
||||
return NULL;
|
||||
}
|
||||
alloc_init(&daemon->superalloc, NULL, 0);
|
||||
daemon->acl = acl_list_create();
|
||||
if(!daemon->acl) {
|
||||
free(daemon->env);
|
||||
free(daemon);
|
||||
return NULL;
|
||||
}
|
||||
if(gettimeofday(&daemon->time_boot, NULL) < 0)
|
||||
log_err("gettimeofday: %s", strerror(errno));
|
||||
daemon->time_last_stat = daemon->time_boot;
|
||||
return daemon;
|
||||
}
|
||||
|
||||
@@ -141,164 +195,111 @@ int
|
||||
daemon_open_shared_ports(struct daemon* daemon)
|
||||
{
|
||||
log_assert(daemon);
|
||||
if(daemon->cfg->port == daemon->listening_port)
|
||||
return 1;
|
||||
listening_ports_free(daemon->ports);
|
||||
if(!(daemon->ports=listening_ports_open(daemon->cfg)))
|
||||
return 0;
|
||||
daemon->listening_port = daemon->cfg->port;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** count number of modules (words) in the string */
|
||||
static int
|
||||
count_modules(const char* s)
|
||||
{
|
||||
int num = 0;
|
||||
if(!s)
|
||||
return 0;
|
||||
while(*s) {
|
||||
/* skip whitespace */
|
||||
while(*s && isspace((int)*s))
|
||||
s++;
|
||||
if(*s && !isspace((int)*s)) {
|
||||
/* skip identifier */
|
||||
num++;
|
||||
while(*s && !isspace((int)*s))
|
||||
s++;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get funcblock for module name
|
||||
* @param str: string with module name. Advanced to next value on success.
|
||||
* @return funcblock or NULL on error.
|
||||
*/
|
||||
static struct module_func_block*
|
||||
daemon_module_factory(const char** str)
|
||||
{
|
||||
/* these are the modules available */
|
||||
int num = 2;
|
||||
const char* names[] = {"iterator", "validator", NULL};
|
||||
struct module_func_block* (*fb[])(void) =
|
||||
{&iter_get_funcblock, &val_get_funcblock, NULL};
|
||||
|
||||
int i;
|
||||
const char* s = *str;
|
||||
while(*s && isspace((int)*s))
|
||||
s++;
|
||||
for(i=0; i<num; i++) {
|
||||
if(strncmp(names[i], s, strlen(names[i])) == 0) {
|
||||
s += strlen(names[i]);
|
||||
*str = s;
|
||||
return (*fb[i])();
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read config file module settings and set up the modfunc block
|
||||
* @param daemon: the daemon.
|
||||
* @return false on error
|
||||
*/
|
||||
static int
|
||||
daemon_config_modules(struct daemon* daemon)
|
||||
{
|
||||
const char* str = daemon->cfg->module_conf;
|
||||
int i;
|
||||
verbose(VERB_DETAIL, "module config: \"%s\"", str);
|
||||
daemon->num_modules = count_modules(str);
|
||||
if(daemon->num_modules == 0) {
|
||||
log_err("error: no modules specified");
|
||||
return 0;
|
||||
}
|
||||
if(daemon->num_modules > MAX_MODULE) {
|
||||
log_err("error: too many modules (%d max %d)",
|
||||
daemon->num_modules, MAX_MODULE);
|
||||
return 0;
|
||||
}
|
||||
daemon->modfunc = (struct module_func_block**)calloc((size_t)
|
||||
daemon->num_modules, sizeof(struct module_func_block*));
|
||||
if(!daemon->modfunc) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
for(i=0; i<daemon->num_modules; i++) {
|
||||
daemon->modfunc[i] = daemon_module_factory(&str);
|
||||
if(!daemon->modfunc[i]) {
|
||||
log_err("Unknown value for first module in: '%s'",
|
||||
str);
|
||||
if(daemon->cfg->port != daemon->listening_port) {
|
||||
listening_ports_free(daemon->ports);
|
||||
if(!(daemon->ports=listening_ports_open(daemon->cfg)))
|
||||
return 0;
|
||||
}
|
||||
daemon->listening_port = daemon->cfg->port;
|
||||
}
|
||||
if(!daemon->cfg->remote_control_enable && daemon->rc_port) {
|
||||
listening_ports_free(daemon->rc_ports);
|
||||
daemon->rc_ports = NULL;
|
||||
daemon->rc_port = 0;
|
||||
}
|
||||
if(daemon->cfg->remote_control_enable &&
|
||||
daemon->cfg->control_port != daemon->rc_port) {
|
||||
listening_ports_free(daemon->rc_ports);
|
||||
if(!(daemon->rc_ports=daemon_remote_open_ports(daemon->cfg)))
|
||||
return 0;
|
||||
daemon->rc_port = daemon->cfg->control_port;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Desetup the modules, deinit, delete.
|
||||
* @param daemon: the daemon.
|
||||
*/
|
||||
static void
|
||||
daemon_desetup_modules(struct daemon* daemon)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<daemon->num_modules; i++) {
|
||||
log_assert(fptr_whitelist_mod_deinit(
|
||||
daemon->modfunc[i]->deinit));
|
||||
(*daemon->modfunc[i]->deinit)(daemon->env, i);
|
||||
}
|
||||
daemon->num_modules = 0;
|
||||
free(daemon->modfunc);
|
||||
daemon->modfunc = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup modules. Assigns ids and calls module_init.
|
||||
* Setup modules. setup module stack.
|
||||
* @param daemon: the daemon
|
||||
*/
|
||||
static void daemon_setup_modules(struct daemon* daemon)
|
||||
{
|
||||
int i;
|
||||
if(daemon->num_modules != 0)
|
||||
daemon_desetup_modules(daemon);
|
||||
/* fixed setup of the modules */
|
||||
if(!daemon_config_modules(daemon)) {
|
||||
fatal_exit("failed to setup modules");
|
||||
}
|
||||
daemon->env->cfg = daemon->cfg;
|
||||
daemon->env->alloc = &daemon->superalloc;
|
||||
daemon->env->worker = NULL;
|
||||
daemon->env->need_to_validate = 0; /* set by module init below */
|
||||
for(i=0; i<daemon->num_modules; i++) {
|
||||
log_info("init module %d: %s", i, daemon->modfunc[i]->name);
|
||||
log_assert(fptr_whitelist_mod_init(daemon->modfunc[i]->init));
|
||||
if(!(*daemon->modfunc[i]->init)(daemon->env, i)) {
|
||||
fatal_exit("module init for module %s failed",
|
||||
daemon->modfunc[i]->name);
|
||||
if(!modstack_setup(&daemon->mods, daemon->cfg->module_conf,
|
||||
daemon->env)) {
|
||||
fatal_exit("failed to setup modules");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain allowed port numbers, concatenate the list, and shuffle them
|
||||
* (ready to be handed out to threads).
|
||||
* @param daemon: the daemon. Uses rand and cfg.
|
||||
* @param shufport: the portlist output.
|
||||
* @return number of ports available.
|
||||
*/
|
||||
int daemon_get_shufport(struct daemon* daemon, int* shufport)
|
||||
{
|
||||
int i, n, k, temp;
|
||||
int avail = 0;
|
||||
for(i=0; i<65536; i++) {
|
||||
if(daemon->cfg->outgoing_avail_ports[i]) {
|
||||
shufport[avail++] = daemon->cfg->
|
||||
outgoing_avail_ports[i];
|
||||
}
|
||||
}
|
||||
if(avail == 0)
|
||||
fatal_exit("no ports are permitted for UDP, add "
|
||||
"with outgoing-port-permit");
|
||||
/* Knuth shuffle */
|
||||
n = avail;
|
||||
while(--n > 0) {
|
||||
k = ub_random(daemon->rand) % (n+1); /* 0<= k<= n */
|
||||
temp = shufport[k];
|
||||
shufport[k] = shufport[n];
|
||||
shufport[n] = temp;
|
||||
}
|
||||
return avail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate empty worker structures. With backptr and thread-number,
|
||||
* from 0..numthread initialised. Used as user arguments to new threads.
|
||||
* Creates the daemon random generator if it does not exist yet.
|
||||
* The random generator stays existing between reloads with a unique state.
|
||||
* @param daemon: the daemon with (new) config settings.
|
||||
*/
|
||||
static void
|
||||
daemon_create_workers(struct daemon* daemon)
|
||||
{
|
||||
int i;
|
||||
int i, numport;
|
||||
int* shufport;
|
||||
log_assert(daemon && daemon->cfg);
|
||||
if(!daemon->rand) {
|
||||
unsigned int seed = (unsigned int)time(NULL) ^
|
||||
(unsigned int)getpid() ^ 0x438;
|
||||
daemon->rand = ub_initstate(seed, NULL);
|
||||
if(!daemon->rand)
|
||||
fatal_exit("could not init random generator");
|
||||
}
|
||||
shufport = (int*)calloc(65536, sizeof(int));
|
||||
if(!shufport)
|
||||
fatal_exit("out of memory during daemon init");
|
||||
numport = daemon_get_shufport(daemon, shufport);
|
||||
verbose(VERB_ALGO, "total of %d outgoing ports available", numport);
|
||||
|
||||
daemon->num = daemon->cfg->num_threads;
|
||||
daemon->workers = (struct worker**)calloc((size_t)daemon->num,
|
||||
sizeof(struct worker*));
|
||||
for(i=0; i<daemon->num; i++) {
|
||||
if(!(daemon->workers[i] = worker_create(daemon, i)))
|
||||
if(!(daemon->workers[i] = worker_create(daemon, i,
|
||||
shufport+numport*i/daemon->num,
|
||||
numport*(i+1)/daemon->num - numport*i/daemon->num)))
|
||||
/* the above is not ports/numthr, due to rounding */
|
||||
fatal_exit("could not create worker");
|
||||
}
|
||||
free(shufport);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -311,13 +312,13 @@ void close_other_pipes(struct daemon* daemon, int thr)
|
||||
int i;
|
||||
for(i=0; i<daemon->num; i++)
|
||||
if(i!=thr) {
|
||||
if(daemon->workers[i]->cmd_send_fd != -1) {
|
||||
close(daemon->workers[i]->cmd_send_fd);
|
||||
daemon->workers[i]->cmd_send_fd = -1;
|
||||
}
|
||||
if(daemon->workers[i]->cmd_recv_fd != -1) {
|
||||
close(daemon->workers[i]->cmd_recv_fd);
|
||||
daemon->workers[i]->cmd_recv_fd = -1;
|
||||
if(i==0) {
|
||||
/* only close read part, need to write stats */
|
||||
tube_close_read(daemon->workers[i]->cmd);
|
||||
} else {
|
||||
/* complete close channel to others */
|
||||
tube_delete(daemon->workers[i]->cmd);
|
||||
daemon->workers[i]->cmd = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -333,10 +334,9 @@ thread_start(void* arg)
|
||||
struct worker* worker = (struct worker*)arg;
|
||||
log_thread_set(&worker->thread_num);
|
||||
ub_thread_blocksigs();
|
||||
#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS)
|
||||
#ifdef THREADS_DISABLED
|
||||
/* close pipe ends used by main */
|
||||
close(worker->cmd_send_fd);
|
||||
worker->cmd_send_fd = -1;
|
||||
tube_close_write(worker->cmd);
|
||||
close_other_pipes(worker->daemon, worker->thread_num);
|
||||
#endif
|
||||
if(!worker_init(worker, worker->daemon->cfg, worker->daemon->ports, 0))
|
||||
@@ -360,10 +360,9 @@ daemon_start_others(struct daemon* daemon)
|
||||
for(i=1; i<daemon->num; i++) {
|
||||
ub_thread_create(&daemon->workers[i]->thr_id,
|
||||
thread_start, daemon->workers[i]);
|
||||
#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS)
|
||||
#ifdef THREADS_DISABLED
|
||||
/* close pipe end of child */
|
||||
close(daemon->workers[i]->cmd_recv_fd);
|
||||
daemon->workers[i]->cmd_recv_fd = -1;
|
||||
tube_close_read(daemon->workers[i]->cmd);
|
||||
#endif /* no threads */
|
||||
}
|
||||
}
|
||||
@@ -381,8 +380,7 @@ daemon_stop_others(struct daemon* daemon)
|
||||
/* skip i=0, is this thread */
|
||||
/* use i=0 buffer for sending cmds; because we are #0 */
|
||||
for(i=1; i<daemon->num; i++) {
|
||||
worker_send_cmd(daemon->workers[i],
|
||||
daemon->workers[0]->front->udp_buff, worker_cmd_quit);
|
||||
worker_send_cmd(daemon->workers[i], worker_cmd_quit);
|
||||
}
|
||||
/* wait for them to quit */
|
||||
for(i=1; i<daemon->num; i++) {
|
||||
@@ -397,6 +395,12 @@ void
|
||||
daemon_fork(struct daemon* daemon)
|
||||
{
|
||||
log_assert(daemon);
|
||||
if(!acl_list_apply_cfg(daemon->acl, daemon->cfg))
|
||||
fatal_exit("Could not setup access control list");
|
||||
if(!(daemon->local_zones = local_zones_create()))
|
||||
fatal_exit("Could not create local zones: out of memory");
|
||||
if(!local_zones_apply_cfg(daemon->local_zones, daemon->cfg))
|
||||
fatal_exit("Could not set up local zones");
|
||||
|
||||
/* setup modules */
|
||||
daemon_setup_modules(daemon);
|
||||
@@ -405,6 +409,12 @@ daemon_fork(struct daemon* daemon)
|
||||
* them to the newly created threads.
|
||||
*/
|
||||
daemon_create_workers(daemon);
|
||||
|
||||
#ifdef HAVE_EV_LOOP
|
||||
/* in libev the first inited base gets signals */
|
||||
if(!worker_init(daemon->workers[0], daemon->cfg, daemon->ports, 1))
|
||||
fatal_exit("Could not initialize main thread");
|
||||
#endif
|
||||
|
||||
/* Now create the threads and init the workers.
|
||||
* By the way, this is thread #0 (the main thread).
|
||||
@@ -412,15 +422,19 @@ daemon_fork(struct daemon* daemon)
|
||||
daemon_start_others(daemon);
|
||||
|
||||
/* Special handling for the main thread. This is the thread
|
||||
* that handles signals.
|
||||
* that handles signals and remote control.
|
||||
*/
|
||||
#ifndef HAVE_EV_LOOP
|
||||
/* libevent has the last inited base get signals (or any base) */
|
||||
if(!worker_init(daemon->workers[0], daemon->cfg, daemon->ports, 1))
|
||||
fatal_exit("Could not initialize main thread");
|
||||
#endif
|
||||
signal_handling_playback(daemon->workers[0]);
|
||||
|
||||
/* Start resolver service on main thread. */
|
||||
log_info("start of service (%s).", PACKAGE_STRING);
|
||||
worker_work(daemon->workers[0]);
|
||||
log_info("service stopped (%s).", PACKAGE_STRING);
|
||||
|
||||
/* we exited! a signal happened! Stop other threads */
|
||||
daemon_stop_others(daemon);
|
||||
@@ -443,6 +457,8 @@ daemon_cleanup(struct daemon* daemon)
|
||||
* The infra cache is kept, the timing and edns info is still valid */
|
||||
slabhash_clear(&daemon->env->rrset_cache->table);
|
||||
slabhash_clear(daemon->env->msg_cache);
|
||||
local_zones_delete(daemon->local_zones);
|
||||
daemon->local_zones = NULL;
|
||||
/* key cache is cleared by module desetup during next daemon_init() */
|
||||
for(i=0; i<daemon->num; i++)
|
||||
worker_delete(daemon->workers[i]);
|
||||
@@ -457,14 +473,18 @@ daemon_delete(struct daemon* daemon)
|
||||
{
|
||||
if(!daemon)
|
||||
return;
|
||||
daemon_desetup_modules(daemon);
|
||||
modstack_desetup(&daemon->mods, daemon->env);
|
||||
listening_ports_free(daemon->ports);
|
||||
listening_ports_free(daemon->rc_ports);
|
||||
if(daemon->env) {
|
||||
slabhash_delete(daemon->env->msg_cache);
|
||||
rrset_cache_delete(daemon->env->rrset_cache);
|
||||
infra_delete(daemon->env->infra_cache);
|
||||
}
|
||||
ub_randfree(daemon->rand);
|
||||
alloc_clear(&daemon->superalloc);
|
||||
acl_list_delete(daemon->acl);
|
||||
free(daemon->chroot);
|
||||
free(daemon->pidfile);
|
||||
free(daemon->env);
|
||||
free(daemon);
|
||||
@@ -475,5 +495,12 @@ daemon_delete(struct daemon* daemon)
|
||||
CRYPTO_cleanup_all_ex_data(); /* safe, no more threads right now */
|
||||
ERR_remove_state(0);
|
||||
ERR_free_strings();
|
||||
RAND_cleanup();
|
||||
checklock_stop();
|
||||
#ifdef USE_WINSOCK
|
||||
if(WSACleanup() != 0) {
|
||||
log_err("Could not WSACleanup: %s",
|
||||
wsa_strerror(WSAGetLastError()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+22
-4
@@ -44,12 +44,16 @@
|
||||
|
||||
#include "util/locks.h"
|
||||
#include "util/alloc.h"
|
||||
#include "services/modstack.h"
|
||||
struct config_file;
|
||||
struct worker;
|
||||
struct listen_port;
|
||||
struct slabhash;
|
||||
struct module_env;
|
||||
struct rrset_cache;
|
||||
struct acl_list;
|
||||
struct local_zones;
|
||||
struct ub_randstate;
|
||||
|
||||
/**
|
||||
* Structure holding worker list.
|
||||
@@ -58,26 +62,40 @@ struct rrset_cache;
|
||||
struct daemon {
|
||||
/** The config settings */
|
||||
struct config_file* cfg;
|
||||
/** the chroot dir in use, NULL if none */
|
||||
char* chroot;
|
||||
/** pidfile that is used */
|
||||
char* pidfile;
|
||||
/** port number that has ports opened. */
|
||||
int listening_port;
|
||||
/** listening ports, opened, to be shared by threads */
|
||||
struct listen_port* ports;
|
||||
/** port number fore remote that has ports opened. */
|
||||
int rc_port;
|
||||
/** listening ports for remote control */
|
||||
struct listen_port* rc_ports;
|
||||
/** num threads allocated */
|
||||
int num;
|
||||
/** the worker entries */
|
||||
struct worker** workers;
|
||||
/** do we need to exit unbound (or is it only a reload?) */
|
||||
int need_to_exit;
|
||||
/** master random table ; used for port div between threads on reload*/
|
||||
struct ub_randstate* rand;
|
||||
/** master allocation cache */
|
||||
struct alloc_cache superalloc;
|
||||
/** the module environment master value, copied and changed by threads*/
|
||||
struct module_env* env;
|
||||
/** number of modules active, ids from 0 to num-1. */
|
||||
int num_modules;
|
||||
/** the module callbacks, array of num_modules length */
|
||||
struct module_func_block** modfunc;
|
||||
/** stack of module callbacks */
|
||||
struct module_stack mods;
|
||||
/** access control, which client IPs are allowed to connect */
|
||||
struct acl_list* acl;
|
||||
/** local authority zones */
|
||||
struct local_zones* local_zones;
|
||||
/** last time of statistics printout */
|
||||
struct timeval time_last_stat;
|
||||
/** time when daemon started */
|
||||
struct timeval time_boot;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+1427
File diff suppressed because it is too large
Load Diff
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* daemon/remote.h - remote control for the unbound daemon.
|
||||
*
|
||||
* Copyright (c) 2008, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains the remote control functionality for the daemon.
|
||||
* The remote control can be performed using either the commandline
|
||||
* unbound-control tool, or a SSLv3/TLS capable web browser.
|
||||
* The channel is secured using SSLv3 or TLSv1, and certificates.
|
||||
* Both the server and the client(control tool) have their own keys.
|
||||
*/
|
||||
|
||||
#ifndef DAEMON_REMOTE_H
|
||||
#define DAEMON_REMOTE_H
|
||||
struct config_file;
|
||||
struct listen_list;
|
||||
struct listen_port;
|
||||
struct worker;
|
||||
struct comm_reply;
|
||||
struct comm_point;
|
||||
struct daemon_remote;
|
||||
|
||||
/** number of seconds timeout on incoming remote control handshake */
|
||||
#define REMOTE_CONTROL_TCP_TIMEOUT 120
|
||||
|
||||
/**
|
||||
* a busy control command connection, SSL state
|
||||
*/
|
||||
struct rc_state {
|
||||
/** the next item in list */
|
||||
struct rc_state* next;
|
||||
/** the commpoint */
|
||||
struct comm_point* c;
|
||||
/** in the handshake part */
|
||||
enum { rc_none, rc_hs_read, rc_hs_write } shake_state;
|
||||
/** the ssl state */
|
||||
SSL* ssl;
|
||||
/** the rc this is part of */
|
||||
struct daemon_remote* rc;
|
||||
};
|
||||
|
||||
/**
|
||||
* The remote control tool state.
|
||||
* The state is only created for the first thread, other threads
|
||||
* are called from this thread. Only the first threads listens to
|
||||
* the control port. The other threads do not, but are called on the
|
||||
* command channel(pipe) from the first thread.
|
||||
*/
|
||||
struct daemon_remote {
|
||||
/** the worker for this remote control */
|
||||
struct worker* worker;
|
||||
/** commpoints for accepting remote control connections */
|
||||
struct listen_list* accept_list;
|
||||
/** number of active commpoints that are handling remote control */
|
||||
int active;
|
||||
/** max active commpoints */
|
||||
int max_active;
|
||||
/** current commpoints busy; should be a short list, malloced */
|
||||
struct rc_state* busy_list;
|
||||
/** the SSL context for creating new SSL streams */
|
||||
SSL_CTX* ctx;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create new remote control state for the daemon.
|
||||
* @param worker: worker with communication base. and links to command channels.
|
||||
* @return new state, or NULL on failure.
|
||||
*/
|
||||
struct daemon_remote* daemon_remote_create(struct worker* worker);
|
||||
|
||||
/**
|
||||
* remote control state to delete.
|
||||
* @param rc: state to delete.
|
||||
*/
|
||||
void daemon_remote_delete(struct daemon_remote* rc);
|
||||
|
||||
/**
|
||||
* Open and create listening ports for remote control.
|
||||
* @param cfg: config options.
|
||||
* @return list of ports or NULL on failure.
|
||||
* can be freed with listening_ports_free().
|
||||
*/
|
||||
struct listen_port* daemon_remote_open_ports(struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Setup comm points for accepting remote control connections.
|
||||
* @param rc: state
|
||||
* @param ports: already opened ports.
|
||||
* @return false on error.
|
||||
*/
|
||||
int daemon_remote_open_accept(struct daemon_remote* rc,
|
||||
struct listen_port* ports);
|
||||
|
||||
/**
|
||||
* Handle nonthreaded remote cmd execution.
|
||||
* @param worker: this worker (the remote worker).
|
||||
*/
|
||||
void daemon_remote_exec(struct worker* worker);
|
||||
|
||||
/** handle remote control accept callbacks */
|
||||
int remote_accept_callback(struct comm_point*, void*, int, struct comm_reply*);
|
||||
|
||||
/** handle remote control data callbacks */
|
||||
int remote_control_callback(struct comm_point*, void*, int, struct comm_reply*);
|
||||
|
||||
/**
|
||||
* Print fixed line of text over ssl connection in blocking mode
|
||||
* @param ssl: print to
|
||||
* @param text: the text.
|
||||
* @return false on connection failure.
|
||||
*/
|
||||
int ssl_print_text(SSL* ssl, const char* text);
|
||||
|
||||
/**
|
||||
* printf style printing to the ssl connection
|
||||
* @param ssl: the SSL connection to print to. Blocking.
|
||||
* @param format: printf style format string.
|
||||
* @return success or false on a network failure.
|
||||
*/
|
||||
int ssl_printf(SSL* ssl, const char* format, ...)
|
||||
ATTR_FORMAT(printf, 2, 3);
|
||||
|
||||
/**
|
||||
* Read until \n is encountered
|
||||
* If SSL signals EOF, the string up to then is returned (without \n).
|
||||
* @param ssl: the SSL connection to read from. blocking.
|
||||
* @param buf: buffer to read to.
|
||||
* @param max: size of buffer.
|
||||
* @return false on connection failure.
|
||||
*/
|
||||
int ssl_read_line(SSL* ssl, char* buf, size_t max);
|
||||
|
||||
#endif /* DAEMON_REMOTE_H */
|
||||
+222
-6
@@ -42,11 +42,33 @@
|
||||
#include "config.h"
|
||||
#include "daemon/stats.h"
|
||||
#include "daemon/worker.h"
|
||||
#include "daemon/daemon.h"
|
||||
#include "services/mesh.h"
|
||||
#include "services/outside_network.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/tube.h"
|
||||
#include "util/timehist.h"
|
||||
#include "util/net_help.h"
|
||||
#include "validator/validator.h"
|
||||
|
||||
void server_stats_init(struct server_stats* stats)
|
||||
/** add timers and the values do not overflow or become negative */
|
||||
static void
|
||||
timeval_add(struct timeval* d, const struct timeval* add)
|
||||
{
|
||||
memset(stats, 0, sizeof(stats));
|
||||
#ifndef S_SPLINT_S
|
||||
d->tv_sec += add->tv_sec;
|
||||
d->tv_usec += add->tv_usec;
|
||||
while(d->tv_usec > 1000000 ) {
|
||||
d->tv_usec -= 1000000;
|
||||
d->tv_sec++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void server_stats_init(struct server_stats* stats, struct config_file* cfg)
|
||||
{
|
||||
memset(stats, 0, sizeof(*stats));
|
||||
stats->extended = cfg->stat_extended;
|
||||
}
|
||||
|
||||
void server_stats_querymiss(struct server_stats* stats, struct worker* worker)
|
||||
@@ -57,16 +79,210 @@ void server_stats_querymiss(struct server_stats* stats, struct worker* worker)
|
||||
stats->max_query_list_size = worker->env.mesh->all.count;
|
||||
}
|
||||
|
||||
void server_stats_log(struct server_stats* stats, int threadnum)
|
||||
void server_stats_log(struct server_stats* stats, struct worker* worker,
|
||||
int threadnum)
|
||||
{
|
||||
log_info("server stats for thread %d: %u queries, %u from cache",
|
||||
log_info("server stats for thread %d: %u queries, "
|
||||
"%u answers from cache, %u recursions",
|
||||
threadnum, (unsigned)stats->num_queries,
|
||||
(unsigned)(stats->num_queries -
|
||||
stats->num_queries_missed_cache));
|
||||
stats->num_queries_missed_cache),
|
||||
(unsigned)stats->num_queries_missed_cache);
|
||||
log_info("server stats for thread %d: requestlist max %u avg %g "
|
||||
"exceeded %u", threadnum, (unsigned)stats->max_query_list_size,
|
||||
stats->num_queries_missed_cache?
|
||||
(double)stats->sum_query_list_size/
|
||||
stats->num_queries_missed_cache : 0.0,
|
||||
(unsigned)stats->num_query_list_exceeded);
|
||||
(unsigned)worker->env.mesh->stats_dropped);
|
||||
}
|
||||
|
||||
/** get rrsets bogus number from validator */
|
||||
static size_t
|
||||
get_rrset_bogus(struct worker* worker)
|
||||
{
|
||||
int m = modstack_find(&worker->env.mesh->mods, "validator");
|
||||
struct val_env* ve;
|
||||
size_t r;
|
||||
if(m == -1)
|
||||
return 0;
|
||||
ve = (struct val_env*)worker->env.modinfo[m];
|
||||
lock_basic_lock(&ve->bogus_lock);
|
||||
r = ve->num_rrset_bogus;
|
||||
if(!worker->env.cfg->stat_cumulative)
|
||||
ve->num_rrset_bogus = 0;
|
||||
lock_basic_unlock(&ve->bogus_lock);
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
server_stats_compile(struct worker* worker, struct stats_info* s)
|
||||
{
|
||||
int i;
|
||||
|
||||
s->svr = worker->stats;
|
||||
s->mesh_num_states = worker->env.mesh->all.count;
|
||||
s->mesh_num_reply_states = worker->env.mesh->num_reply_states;
|
||||
s->mesh_jostled = worker->env.mesh->stats_jostled;
|
||||
s->mesh_dropped = worker->env.mesh->stats_dropped;
|
||||
s->mesh_replies_sent = worker->env.mesh->replies_sent;
|
||||
s->mesh_replies_sum_wait = worker->env.mesh->replies_sum_wait;
|
||||
s->mesh_time_median = timehist_quartile(worker->env.mesh->histogram,
|
||||
0.50);
|
||||
|
||||
/* add in the values from the mesh */
|
||||
s->svr.ans_secure += worker->env.mesh->ans_secure;
|
||||
s->svr.ans_bogus += worker->env.mesh->ans_bogus;
|
||||
s->svr.ans_rcode_nodata += worker->env.mesh->ans_nodata;
|
||||
for(i=0; i<16; i++)
|
||||
s->svr.ans_rcode[i] += worker->env.mesh->ans_rcode[i];
|
||||
timehist_export(worker->env.mesh->histogram, s->svr.hist,
|
||||
NUM_BUCKETS_HIST);
|
||||
/* values from outside network */
|
||||
s->svr.unwanted_replies = worker->back->unwanted_replies;
|
||||
|
||||
/* get and reset validator rrset bogus number */
|
||||
s->svr.rrset_bogus = get_rrset_bogus(worker);
|
||||
|
||||
if(!worker->env.cfg->stat_cumulative) {
|
||||
server_stats_init(&worker->stats, worker->env.cfg);
|
||||
mesh_stats_clear(worker->env.mesh);
|
||||
worker->back->unwanted_replies = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void server_stats_obtain(struct worker* worker, struct worker* who,
|
||||
struct stats_info* s)
|
||||
{
|
||||
uint8_t *reply = NULL;
|
||||
uint32_t len = 0;
|
||||
if(worker == who) {
|
||||
/* just fill it in */
|
||||
server_stats_compile(worker, s);
|
||||
return;
|
||||
}
|
||||
/* communicate over tube */
|
||||
verbose(VERB_ALGO, "write stats cmd");
|
||||
worker_send_cmd(who, worker_cmd_stats);
|
||||
verbose(VERB_ALGO, "wait for stats reply");
|
||||
if(!tube_read_msg(worker->cmd, &reply, &len, 0))
|
||||
fatal_exit("failed to read stats over cmd channel");
|
||||
if(len != (uint32_t)sizeof(*s))
|
||||
fatal_exit("stats on cmd channel wrong length %d %d",
|
||||
(int)len, (int)sizeof(*s));
|
||||
memcpy(s, reply, (size_t)len);
|
||||
free(reply);
|
||||
}
|
||||
|
||||
void server_stats_reply(struct worker* worker)
|
||||
{
|
||||
struct stats_info s;
|
||||
server_stats_compile(worker, &s);
|
||||
verbose(VERB_ALGO, "write stats replymsg");
|
||||
if(!tube_write_msg(worker->daemon->workers[0]->cmd,
|
||||
(uint8_t*)&s, sizeof(s), 0))
|
||||
fatal_exit("could not write stat values over cmd channel");
|
||||
}
|
||||
|
||||
void server_stats_add(struct stats_info* total, struct stats_info* a)
|
||||
{
|
||||
total->svr.num_queries += a->svr.num_queries;
|
||||
total->svr.num_queries_missed_cache += a->svr.num_queries_missed_cache;
|
||||
total->svr.sum_query_list_size += a->svr.sum_query_list_size;
|
||||
/* the max size reached is upped to higher of both */
|
||||
if(a->svr.max_query_list_size > total->svr.max_query_list_size)
|
||||
total->svr.max_query_list_size = a->svr.max_query_list_size;
|
||||
|
||||
if(a->svr.extended) {
|
||||
int i;
|
||||
total->svr.qtype_big += a->svr.qtype_big;
|
||||
total->svr.qclass_big += a->svr.qclass_big;
|
||||
total->svr.qtcp += a->svr.qtcp;
|
||||
total->svr.qipv6 += a->svr.qipv6;
|
||||
total->svr.qbit_QR += a->svr.qbit_QR;
|
||||
total->svr.qbit_AA += a->svr.qbit_AA;
|
||||
total->svr.qbit_TC += a->svr.qbit_TC;
|
||||
total->svr.qbit_RD += a->svr.qbit_RD;
|
||||
total->svr.qbit_RA += a->svr.qbit_RA;
|
||||
total->svr.qbit_Z += a->svr.qbit_Z;
|
||||
total->svr.qbit_AD += a->svr.qbit_AD;
|
||||
total->svr.qbit_CD += a->svr.qbit_CD;
|
||||
total->svr.qEDNS += a->svr.qEDNS;
|
||||
total->svr.qEDNS_DO += a->svr.qEDNS_DO;
|
||||
total->svr.ans_rcode_nodata += a->svr.ans_rcode_nodata;
|
||||
total->svr.ans_secure += a->svr.ans_secure;
|
||||
total->svr.ans_bogus += a->svr.ans_bogus;
|
||||
total->svr.rrset_bogus += a->svr.rrset_bogus;
|
||||
total->svr.unwanted_replies += a->svr.unwanted_replies;
|
||||
total->svr.unwanted_queries += a->svr.unwanted_queries;
|
||||
for(i=0; i<STATS_QTYPE_NUM; i++)
|
||||
total->svr.qtype[i] += a->svr.qtype[i];
|
||||
for(i=0; i<STATS_QCLASS_NUM; i++)
|
||||
total->svr.qclass[i] += a->svr.qclass[i];
|
||||
for(i=0; i<STATS_OPCODE_NUM; i++)
|
||||
total->svr.qopcode[i] += a->svr.qopcode[i];
|
||||
for(i=0; i<STATS_RCODE_NUM; i++)
|
||||
total->svr.ans_rcode[i] += a->svr.ans_rcode[i];
|
||||
for(i=0; i<NUM_BUCKETS_HIST; i++)
|
||||
total->svr.hist[i] += a->svr.hist[i];
|
||||
}
|
||||
|
||||
total->mesh_num_states += a->mesh_num_states;
|
||||
total->mesh_num_reply_states += a->mesh_num_reply_states;
|
||||
total->mesh_jostled += a->mesh_jostled;
|
||||
total->mesh_dropped += a->mesh_dropped;
|
||||
total->mesh_replies_sent += a->mesh_replies_sent;
|
||||
timeval_add(&total->mesh_replies_sum_wait, &a->mesh_replies_sum_wait);
|
||||
/* the medians are averaged together, this is not as accurate as
|
||||
* taking the median over all of the data, but is good and fast
|
||||
* added up here, division later*/
|
||||
total->mesh_time_median += a->mesh_time_median;
|
||||
}
|
||||
|
||||
void server_stats_insquery(struct server_stats* stats, struct comm_point* c,
|
||||
uint16_t qtype, uint16_t qclass, struct edns_data* edns,
|
||||
struct comm_reply* repinfo)
|
||||
{
|
||||
uint16_t flags = ldns_buffer_read_u16_at(c->buffer, 2);
|
||||
if(qtype < STATS_QTYPE_NUM)
|
||||
stats->qtype[qtype]++;
|
||||
else stats->qtype_big++;
|
||||
if(qclass < STATS_QCLASS_NUM)
|
||||
stats->qclass[qclass]++;
|
||||
else stats->qclass_big++;
|
||||
stats->qopcode[ LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) ]++;
|
||||
if(c->type != comm_udp)
|
||||
stats->qtcp++;
|
||||
if(repinfo && addr_is_ip6(&repinfo->addr, repinfo->addrlen))
|
||||
stats->qipv6++;
|
||||
if( (flags&BIT_QR) )
|
||||
stats->qbit_QR++;
|
||||
if( (flags&BIT_AA) )
|
||||
stats->qbit_AA++;
|
||||
if( (flags&BIT_TC) )
|
||||
stats->qbit_TC++;
|
||||
if( (flags&BIT_RD) )
|
||||
stats->qbit_RD++;
|
||||
if( (flags&BIT_RA) )
|
||||
stats->qbit_RA++;
|
||||
if( (flags&BIT_Z) )
|
||||
stats->qbit_Z++;
|
||||
if( (flags&BIT_AD) )
|
||||
stats->qbit_AD++;
|
||||
if( (flags&BIT_CD) )
|
||||
stats->qbit_CD++;
|
||||
if(edns->edns_present) {
|
||||
stats->qEDNS++;
|
||||
if( (edns->bits & EDNS_DO) )
|
||||
stats->qEDNS_DO++;
|
||||
}
|
||||
}
|
||||
|
||||
void server_stats_insrcode(struct server_stats* stats, ldns_buffer* buf)
|
||||
{
|
||||
if(stats->extended && ldns_buffer_limit(buf) != 0) {
|
||||
int r = (int)LDNS_RCODE_WIRE( ldns_buffer_begin(buf) );
|
||||
stats->ans_rcode[r] ++;
|
||||
if(r == 0 && LDNS_ANCOUNT( ldns_buffer_begin(buf) ) == 0)
|
||||
stats->ans_rcode_nodata ++;
|
||||
}
|
||||
}
|
||||
|
||||
+150
-4
@@ -42,7 +42,21 @@
|
||||
|
||||
#ifndef DAEMON_STATS_H
|
||||
#define DAEMON_STATS_H
|
||||
#include "util/timehist.h"
|
||||
struct worker;
|
||||
struct config_file;
|
||||
struct comm_point;
|
||||
struct comm_reply;
|
||||
struct edns_data;
|
||||
|
||||
/** number of qtype that is stored for in array */
|
||||
#define STATS_QTYPE_NUM 256
|
||||
/** number of qclass that is stored for in array */
|
||||
#define STATS_QCLASS_NUM 256
|
||||
/** number of rcodes in stats */
|
||||
#define STATS_RCODE_NUM 16
|
||||
/** number of opcodes in stats */
|
||||
#define STATS_OPCODE_NUM 16
|
||||
|
||||
/** per worker statistics */
|
||||
struct server_stats {
|
||||
@@ -58,20 +72,152 @@ struct server_stats {
|
||||
size_t sum_query_list_size;
|
||||
/** max value of query list size reached. */
|
||||
size_t max_query_list_size;
|
||||
/** number of times that the query_list_size was insufficient */
|
||||
size_t num_query_list_exceeded;
|
||||
|
||||
/** Extended stats below (bool) */
|
||||
int extended;
|
||||
|
||||
/** qtype stats */
|
||||
size_t qtype[STATS_QTYPE_NUM];
|
||||
/** bigger qtype values not in array */
|
||||
size_t qtype_big;
|
||||
/** qclass stats */
|
||||
size_t qclass[STATS_QCLASS_NUM];
|
||||
/** bigger qclass values not in array */
|
||||
size_t qclass_big;
|
||||
/** query opcodes */
|
||||
size_t qopcode[STATS_OPCODE_NUM];
|
||||
/** number of queries over TCP */
|
||||
size_t qtcp;
|
||||
/** number of queries over IPv6 */
|
||||
size_t qipv6;
|
||||
/** number of queries with QR bit */
|
||||
size_t qbit_QR;
|
||||
/** number of queries with AA bit */
|
||||
size_t qbit_AA;
|
||||
/** number of queries with TC bit */
|
||||
size_t qbit_TC;
|
||||
/** number of queries with RD bit */
|
||||
size_t qbit_RD;
|
||||
/** number of queries with RA bit */
|
||||
size_t qbit_RA;
|
||||
/** number of queries with Z bit */
|
||||
size_t qbit_Z;
|
||||
/** number of queries with AD bit */
|
||||
size_t qbit_AD;
|
||||
/** number of queries with CD bit */
|
||||
size_t qbit_CD;
|
||||
/** number of queries with EDNS OPT record */
|
||||
size_t qEDNS;
|
||||
/** number of queries with EDNS with DO flag */
|
||||
size_t qEDNS_DO;
|
||||
/** answer rcodes */
|
||||
size_t ans_rcode[STATS_RCODE_NUM];
|
||||
/** answers with pseudo rcode 'nodata' */
|
||||
size_t ans_rcode_nodata;
|
||||
/** answers that were secure (AD) */
|
||||
size_t ans_secure;
|
||||
/** answers that were bogus (withheld as SERVFAIL) */
|
||||
size_t ans_bogus;
|
||||
/** rrsets marked bogus by validator */
|
||||
size_t rrset_bogus;
|
||||
/** unwanted traffic received on server-facing ports */
|
||||
size_t unwanted_replies;
|
||||
/** unwanted traffic received on client-facing ports */
|
||||
size_t unwanted_queries;
|
||||
|
||||
/** histogram data exported to array
|
||||
* if the array is the same size, no data is lost, and
|
||||
* if all histograms are same size (is so by default) then
|
||||
* adding up works well. */
|
||||
size_t hist[NUM_BUCKETS_HIST];
|
||||
};
|
||||
|
||||
/**
|
||||
* Statistics to send over the control pipe when asked
|
||||
* This struct is made to be memcpied, sent in binary.
|
||||
*/
|
||||
struct stats_info {
|
||||
/** the thread stats */
|
||||
struct server_stats svr;
|
||||
|
||||
/** mesh stats: current number of states */
|
||||
size_t mesh_num_states;
|
||||
/** mesh stats: current number of reply (user) states */
|
||||
size_t mesh_num_reply_states;
|
||||
/** mesh stats: number of reply states overwritten with a new one */
|
||||
size_t mesh_jostled;
|
||||
/** mesh stats: number of incoming queries dropped */
|
||||
size_t mesh_dropped;
|
||||
/** mesh stats: replies sent */
|
||||
size_t mesh_replies_sent;
|
||||
/** mesh stats: sum of waiting times for the replies */
|
||||
struct timeval mesh_replies_sum_wait;
|
||||
/** mesh stats: median of waiting times for replies (in sec) */
|
||||
double mesh_time_median;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize server stats to 0.
|
||||
* @param stats: what to init (this is alloced by the caller).
|
||||
* @param cfg: with extended statistics option.
|
||||
*/
|
||||
void server_stats_init(struct server_stats* stats);
|
||||
void server_stats_init(struct server_stats* stats, struct config_file* cfg);
|
||||
|
||||
/** add query if it missed the cache */
|
||||
void server_stats_querymiss(struct server_stats* stats, struct worker* worker);
|
||||
|
||||
/** display the stats to the log */
|
||||
void server_stats_log(struct server_stats* stats, int threadnum);
|
||||
void server_stats_log(struct server_stats* stats, struct worker* worker,
|
||||
int threadnum);
|
||||
|
||||
/**
|
||||
* Obtain the stats info for a given thread. Uses pipe to communicate.
|
||||
* @param worker: the worker that is executing (the first worker).
|
||||
* @param who: on who to get the statistics info.
|
||||
* @param s: the stats block to fill in.
|
||||
*/
|
||||
void server_stats_obtain(struct worker* worker, struct worker* who,
|
||||
struct stats_info* s);
|
||||
|
||||
/**
|
||||
* Compile stats into structure for this thread worker.
|
||||
* Also clears the statistics counters (if that is set by config file).
|
||||
* @param worker: the worker to compile stats for, also the executing worker.
|
||||
* @param s: stats block.
|
||||
*/
|
||||
void server_stats_compile(struct worker* worker, struct stats_info* s);
|
||||
|
||||
/**
|
||||
* Send stats over comm tube in reply to query cmd
|
||||
* @param worker: this worker.
|
||||
*/
|
||||
void server_stats_reply(struct worker* worker);
|
||||
|
||||
/**
|
||||
* Addup stat blocks.
|
||||
* @param total: sum of the two entries.
|
||||
* @param a: to add to it.
|
||||
*/
|
||||
void server_stats_add(struct stats_info* total, struct stats_info* a);
|
||||
|
||||
/**
|
||||
* Add stats for this query
|
||||
* @param stats: the stats
|
||||
* @param c: commpoint with type and buffer.
|
||||
* @param qtype: query type
|
||||
* @param qclass: query class
|
||||
* @param edns: edns record
|
||||
* @param repinfo: reply info with remote address
|
||||
*/
|
||||
void server_stats_insquery(struct server_stats* stats, struct comm_point* c,
|
||||
uint16_t qtype, uint16_t qclass, struct edns_data* edns,
|
||||
struct comm_reply* repinfo);
|
||||
|
||||
/**
|
||||
* Add rcode for this query.
|
||||
* @param stats: the stats
|
||||
* @param buf: buffer with rcode. If buffer is length0: not counted.
|
||||
*/
|
||||
void server_stats_insrcode(struct server_stats* stats, ldns_buffer* buf);
|
||||
|
||||
#endif /* DAEMON_STATS_H */
|
||||
|
||||
+254
-77
@@ -52,8 +52,24 @@
|
||||
#include "util/module.h"
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <openssl/crypto.h>
|
||||
#ifdef HAVE_PWD_H
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_RESOURCE_H
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_MINI_EVENT
|
||||
# ifdef USE_WINSOCK
|
||||
# include "util/winsock_event.h"
|
||||
# else
|
||||
# include "util/mini_event.h"
|
||||
# endif
|
||||
#else
|
||||
# include <event.h>
|
||||
#endif
|
||||
|
||||
/** global debug value to keep track of heap memory allocation */
|
||||
void* unbound_start_brk = 0;
|
||||
@@ -69,6 +85,9 @@ static void usage()
|
||||
printf("-d do not fork into the background.\n");
|
||||
printf("-v verbose (more times to increase verbosity)\n");
|
||||
printf("Version %s\n", PACKAGE_VERSION);
|
||||
printf("libevent %s, libldns %s, %s\n",
|
||||
event_get_version(), ldns_version(),
|
||||
SSLeay_version(SSLEAY_VERSION));
|
||||
printf("BSD licensed, see LICENSE in source package for details.\n");
|
||||
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
|
||||
}
|
||||
@@ -77,23 +96,30 @@ static void usage()
|
||||
static void
|
||||
checkrlimits(struct config_file* cfg)
|
||||
{
|
||||
int list = ((cfg->do_ip4?1:0) + (cfg->do_ip6?1:0)) *
|
||||
((cfg->do_udp?1:0) + (cfg->do_tcp?1 +
|
||||
#ifdef HAVE_GETRLIMIT
|
||||
/* list has number of ports to listen to, ifs number addresses */
|
||||
int list = ((cfg->do_udp?1:0) + (cfg->do_tcp?1 +
|
||||
(int)cfg->incoming_num_tcp:0));
|
||||
size_t ifs = (size_t)(cfg->num_ifs==0?1:cfg->num_ifs);
|
||||
size_t listen_num = list*ifs;
|
||||
size_t out_ifs = (size_t)(cfg->num_out_ifs==0?1:cfg->num_out_ifs);
|
||||
size_t outnum = cfg->outgoing_num_ports*out_ifs + cfg->outgoing_num_tcp;
|
||||
size_t listen_ifs = (size_t)(cfg->num_ifs==0?
|
||||
((cfg->do_ip4 && !cfg->if_automatic?1:0) +
|
||||
(cfg->do_ip6?1:0)):cfg->num_ifs);
|
||||
size_t listen_num = list*listen_ifs;
|
||||
size_t outudpnum = (size_t)cfg->outgoing_num_ports;
|
||||
size_t outtcpnum = cfg->outgoing_num_tcp;
|
||||
size_t misc = 4; /* logfile, pidfile, stdout... */
|
||||
size_t perthread = listen_num + outnum + 2/*cmdpipe*/ + 2/*libevent*/
|
||||
+ misc;
|
||||
size_t perthread_noudp = listen_num + outtcpnum +
|
||||
2/*cmdpipe*/ + 2/*libevent*/ + misc;
|
||||
size_t perthread = perthread_noudp + outudpnum;
|
||||
|
||||
#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS)
|
||||
int numthread = 1; /* it forks */
|
||||
#else
|
||||
int numthread = cfg->num_threads;
|
||||
#endif
|
||||
size_t total = numthread * perthread + misc;
|
||||
size_t avail;
|
||||
struct rlimit rlim;
|
||||
|
||||
if(getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
|
||||
log_warn("getrlimit: %s", strerror(errno));
|
||||
return;
|
||||
@@ -101,14 +127,46 @@ checkrlimits(struct config_file* cfg)
|
||||
if(rlim.rlim_cur == (rlim_t)RLIM_INFINITY)
|
||||
return;
|
||||
if((size_t)rlim.rlim_cur < total) {
|
||||
log_err("Not enough sockets available. Increase "
|
||||
"ulimit(open files).");
|
||||
log_err("or decrease number of threads, outgoing num ports, "
|
||||
"outgoing num tcp or number of interfaces");
|
||||
log_err("estimate %u fds high mark, %u available",
|
||||
(unsigned)total, (unsigned)rlim.rlim_cur);
|
||||
fatal_exit("Not enough file descriptors available");
|
||||
avail = (size_t)rlim.rlim_cur;
|
||||
rlim.rlim_cur = (rlim_t)(total + 10);
|
||||
rlim.rlim_max = (rlim_t)(total + 10);
|
||||
if(setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
|
||||
log_warn("setrlimit: %s", strerror(errno));
|
||||
log_warn("cannot increase max open fds from %u to %u",
|
||||
(unsigned)avail, (unsigned)total+10);
|
||||
/* check that calculation below does not underflow,
|
||||
* with 15 as margin */
|
||||
if(numthread*perthread_noudp+15 > avail)
|
||||
fatal_exit("too much tcp. not enough fds.");
|
||||
cfg->outgoing_num_ports = (int)((avail
|
||||
- numthread*perthread_noudp
|
||||
- 10 /* safety margin */) /numthread);
|
||||
log_warn("continuing with less udp ports: %u",
|
||||
cfg->outgoing_num_ports);
|
||||
log_warn("increase ulimit or decrease threads, ports in config to remove this warning");
|
||||
return;
|
||||
}
|
||||
log_warn("increased limit(open files) from %u to %u",
|
||||
(unsigned)avail, (unsigned)total+10);
|
||||
}
|
||||
if(total > 1024 &&
|
||||
strncmp(event_get_version(), "mini-event", 10) == 0) {
|
||||
log_err("too many file descriptors requested. The builtin"
|
||||
"mini-event cannot handle more than 1024. Config "
|
||||
"for less fds or compile with libevent");
|
||||
fatal_exit("configuration needs too many file descriptors");
|
||||
}
|
||||
if(perthread > 64 &&
|
||||
strncmp(event_get_version(), "winsock-event", 13) == 0) {
|
||||
log_err("too many file descriptors requested. The winsock"
|
||||
" event handler cannot handle more than 64 per "
|
||||
" thread. Config for less fds or compile with "
|
||||
" libevent");
|
||||
fatal_exit("configuration needs too many file descriptors");
|
||||
}
|
||||
#else
|
||||
(void)cfg;
|
||||
#endif /* HAVE_GETRLIMIT */
|
||||
}
|
||||
|
||||
/** set verbosity, check rlimits, cache settings */
|
||||
@@ -141,6 +199,7 @@ apply_settings(struct daemon* daemon, struct config_file* cfg,
|
||||
checkrlimits(cfg);
|
||||
}
|
||||
|
||||
#ifdef HAVE_KILL
|
||||
/** Read existing pid from pidfile.
|
||||
* @param file: file name of pid file.
|
||||
* @return: the pid from the file or -1 if none.
|
||||
@@ -208,46 +267,47 @@ writepid (const char* pidfile, pid_t pid)
|
||||
|
||||
/**
|
||||
* check old pid file.
|
||||
* @param cfg: the config settings
|
||||
* @param pidfile: the file name of the pid file.
|
||||
* @param inchroot: if pidfile is inchroot and we can thus expect to
|
||||
* be able to delete it.
|
||||
*/
|
||||
static void
|
||||
checkoldpid(struct config_file* cfg)
|
||||
checkoldpid(char* pidfile, int inchroot)
|
||||
{
|
||||
pid_t old;
|
||||
char* file = cfg->pidfile;
|
||||
if(cfg->chrootdir && cfg->chrootdir[0] &&
|
||||
strncmp(file, cfg->chrootdir, strlen(cfg->chrootdir))==0) {
|
||||
file += strlen(cfg->chrootdir);
|
||||
}
|
||||
if((old = readpid(file)) != -1) {
|
||||
if((old = readpid(pidfile)) != -1) {
|
||||
/* see if it is still alive */
|
||||
if(kill(old, 0) == 0 || errno == EPERM)
|
||||
log_warn("unbound is already running as pid %u.",
|
||||
(unsigned)old);
|
||||
else log_warn("did not exit gracefully last time (%u)",
|
||||
else if(inchroot)
|
||||
log_warn("did not exit gracefully last time (%u)",
|
||||
(unsigned)old);
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_KILL */
|
||||
|
||||
/** detach from command line */
|
||||
static void
|
||||
detach(struct config_file* cfg)
|
||||
detach(void)
|
||||
{
|
||||
#ifdef HAVE_WORKING_FORK
|
||||
int fd;
|
||||
/* Take off... */
|
||||
switch (fork()) {
|
||||
case 0:
|
||||
break;
|
||||
case -1:
|
||||
unlink(cfg->pidfile);
|
||||
fatal_exit("fork failed: %s", strerror(errno));
|
||||
default:
|
||||
/* exit interactive session */
|
||||
exit(0);
|
||||
}
|
||||
/* detach */
|
||||
#ifdef HAVE_SETSID
|
||||
if(setsid() == -1)
|
||||
fatal_exit("setsid() failed: %s", strerror(errno));
|
||||
#endif
|
||||
if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
|
||||
(void)dup2(fd, STDIN_FILENO);
|
||||
(void)dup2(fd, STDOUT_FILENO);
|
||||
@@ -255,27 +315,22 @@ detach(struct config_file* cfg)
|
||||
if (fd > 2)
|
||||
(void)close(fd);
|
||||
}
|
||||
#endif /* HAVE_WORKING_FORK */
|
||||
}
|
||||
|
||||
/** daemonize, drop user priviliges and chroot if needed */
|
||||
static void
|
||||
do_chroot(struct daemon* daemon, struct config_file* cfg, int debug_mode)
|
||||
perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
|
||||
const char** cfgfile)
|
||||
{
|
||||
#ifdef HAVE_GETPWNAM
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
/* initialize, but not to 0 (root) */
|
||||
memset(&uid, -12, sizeof(uid));
|
||||
memset(&gid, -12, sizeof(gid));
|
||||
memset(&uid, 112, sizeof(uid));
|
||||
memset(&gid, 112, sizeof(gid));
|
||||
log_assert(cfg);
|
||||
|
||||
/* daemonize last to be able to print error to user */
|
||||
if(cfg->directory && cfg->directory[0]) {
|
||||
if(chdir(cfg->directory)) {
|
||||
fatal_exit("Could not chdir to %s: %s",
|
||||
cfg->directory, strerror(errno));
|
||||
}
|
||||
verbose(VERB_DETAIL, "chdir to %s", cfg->directory);
|
||||
}
|
||||
if(cfg->username && cfg->username[0]) {
|
||||
struct passwd *pwd;
|
||||
if((pwd = getpwnam(cfg->username)) == NULL)
|
||||
@@ -284,41 +339,144 @@ do_chroot(struct daemon* daemon, struct config_file* cfg, int debug_mode)
|
||||
gid = pwd->pw_gid;
|
||||
endpwent();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* init syslog (as root) if needed, before daemonize, otherwise
|
||||
* a fork error could not be printed since daemonize closed stderr.*/
|
||||
if(cfg->use_syslog) {
|
||||
log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
|
||||
}
|
||||
/* if using a logfile, we cannot open it because the logfile would
|
||||
* be created with the wrong permissions, we cannot chown it because
|
||||
* we cannot chown system logfiles, so we do not open at all.
|
||||
* So, using a logfile, the user does not see errors unless -d is
|
||||
* given to unbound on the commandline. */
|
||||
|
||||
#ifdef HAVE_KILL
|
||||
/* check old pid file before forking */
|
||||
if(cfg->pidfile && cfg->pidfile[0]) {
|
||||
/* calculate position of pidfile */
|
||||
if(cfg->pidfile[0] == '/')
|
||||
daemon->pidfile = strdup(cfg->pidfile);
|
||||
else daemon->pidfile = fname_after_chroot(cfg->pidfile,
|
||||
cfg, 1);
|
||||
if(!daemon->pidfile)
|
||||
fatal_exit("pidfile alloc: out of memory");
|
||||
checkoldpid(daemon->pidfile,
|
||||
/* true if pidfile is inside chrootdir, or nochroot */
|
||||
!(cfg->chrootdir && cfg->chrootdir[0]) ||
|
||||
(cfg->chrootdir && cfg->chrootdir[0] &&
|
||||
strncmp(daemon->pidfile, cfg->chrootdir,
|
||||
strlen(cfg->chrootdir))==0));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* daemonize because pid is needed by the writepid func */
|
||||
if(!debug_mode && cfg->do_daemonize) {
|
||||
detach();
|
||||
}
|
||||
|
||||
/* write new pidfile (while still root, so can be outside chroot) */
|
||||
#ifdef HAVE_KILL
|
||||
if(cfg->pidfile && cfg->pidfile[0]) {
|
||||
writepid(daemon->pidfile, getpid());
|
||||
if(!(cfg->chrootdir && cfg->chrootdir[0]) ||
|
||||
(cfg->chrootdir && cfg->chrootdir[0] &&
|
||||
strncmp(daemon->pidfile, cfg->chrootdir,
|
||||
strlen(cfg->chrootdir))==0)) {
|
||||
/* delete of pidfile could potentially work,
|
||||
* chown to get permissions */
|
||||
if(cfg->username && cfg->username[0]) {
|
||||
if(chown(daemon->pidfile, uid, gid) == -1) {
|
||||
fatal_exit("cannot chown %u.%u %s: %s",
|
||||
(unsigned)uid, (unsigned)gid,
|
||||
daemon->pidfile, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)daemon;
|
||||
#endif
|
||||
|
||||
/* box into the chroot */
|
||||
#ifdef HAVE_CHROOT
|
||||
if(cfg->chrootdir && cfg->chrootdir[0]) {
|
||||
if(chdir(cfg->chrootdir)) {
|
||||
fatal_exit("unable to chdir to chroot %s: %s",
|
||||
cfg->chrootdir, strerror(errno));
|
||||
}
|
||||
verbose(VERB_QUERY, "chdir to %s", cfg->chrootdir);
|
||||
if(chroot(cfg->chrootdir))
|
||||
fatal_exit("unable to chroot to %s: %s",
|
||||
cfg->chrootdir, strerror(errno));
|
||||
verbose(VERB_DETAIL, "chroot to %s", cfg->chrootdir);
|
||||
verbose(VERB_QUERY, "chroot to %s", cfg->chrootdir);
|
||||
if(strncmp(*cfgfile, cfg->chrootdir,
|
||||
strlen(cfg->chrootdir)) == 0)
|
||||
(*cfgfile) += strlen(cfg->chrootdir);
|
||||
|
||||
/* adjust stored pidfile for chroot */
|
||||
if(daemon->pidfile && daemon->pidfile[0] &&
|
||||
strncmp(daemon->pidfile, cfg->chrootdir,
|
||||
strlen(cfg->chrootdir))==0) {
|
||||
char* old = daemon->pidfile;
|
||||
daemon->pidfile = strdup(old+strlen(cfg->chrootdir));
|
||||
free(old);
|
||||
if(!daemon->pidfile)
|
||||
log_err("out of memory in pidfile adjust");
|
||||
}
|
||||
daemon->chroot = strdup(cfg->chrootdir);
|
||||
if(!daemon->chroot)
|
||||
log_err("out of memory in daemon chroot dir storage");
|
||||
}
|
||||
if(cfg->username && cfg->username[0]) {
|
||||
if(setgid(gid) != 0)
|
||||
fatal_exit("unable to set group id of %s: %s",
|
||||
cfg->username, strerror(errno));
|
||||
if(setuid(uid) != 0)
|
||||
fatal_exit("unable to set user id of %s: %s",
|
||||
cfg->username, strerror(errno));
|
||||
verbose(VERB_DETAIL, "drop user privileges, run as %s",
|
||||
cfg->username);
|
||||
}
|
||||
/* check old pid file before forking */
|
||||
if(cfg->pidfile && cfg->pidfile[0]) {
|
||||
checkoldpid(cfg);
|
||||
#else
|
||||
(void)cfgfile;
|
||||
#endif
|
||||
/* change to working directory inside chroot */
|
||||
if(cfg->directory && cfg->directory[0]) {
|
||||
char* dir = cfg->directory;
|
||||
if(cfg->chrootdir && cfg->chrootdir[0] &&
|
||||
strncmp(dir, cfg->chrootdir,
|
||||
strlen(cfg->chrootdir)) == 0)
|
||||
dir += strlen(cfg->chrootdir);
|
||||
if(dir[0]) {
|
||||
if(chdir(dir)) {
|
||||
fatal_exit("Could not chdir to %s: %s",
|
||||
dir, strerror(errno));
|
||||
}
|
||||
verbose(VERB_QUERY, "chdir to %s", dir);
|
||||
}
|
||||
}
|
||||
|
||||
/* init logfile just before fork */
|
||||
log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
|
||||
if(!debug_mode && cfg->do_daemonize) {
|
||||
detach(cfg);
|
||||
}
|
||||
if(cfg->pidfile && cfg->pidfile[0]) {
|
||||
char* pf = cfg->pidfile;
|
||||
if(cfg->chrootdir && cfg->chrootdir[0] &&
|
||||
strncmp(pf, cfg->chrootdir, strlen(cfg->chrootdir))==0)
|
||||
pf += strlen(cfg->chrootdir);
|
||||
writepid(pf, getpid());
|
||||
if(!(daemon->pidfile = strdup(pf)))
|
||||
log_err("pidf: malloc failed");
|
||||
/* drop permissions after chroot, getpwnam, pidfile, syslog done*/
|
||||
#ifdef HAVE_GETPWNAM
|
||||
if(cfg->username && cfg->username[0]) {
|
||||
#ifdef HAVE_SETRESGID
|
||||
if(setresgid(gid,gid,gid) != 0)
|
||||
#elif defined(HAVE_SETREGID) && !defined(DARWIN_BROKEN_SETREUID)
|
||||
if(setregid(gid,gid) != 0)
|
||||
#else /* use setgid */
|
||||
if(setgid(gid) != 0)
|
||||
#endif /* HAVE_SETRESGID */
|
||||
fatal_exit("unable to set group id of %s: %s",
|
||||
cfg->username, strerror(errno));
|
||||
#ifdef HAVE_SETRESUID
|
||||
if(setresuid(uid,uid,uid) != 0)
|
||||
#elif defined(HAVE_SETREUID) && !defined(DARWIN_BROKEN_SETREUID)
|
||||
if(setreuid(uid,uid) != 0)
|
||||
#else /* use setuid */
|
||||
if(setuid(uid) != 0)
|
||||
#endif /* HAVE_SETRESUID */
|
||||
fatal_exit("unable to set user id of %s: %s",
|
||||
cfg->username, strerror(errno));
|
||||
verbose(VERB_QUERY, "drop user privileges, run as %s",
|
||||
cfg->username);
|
||||
}
|
||||
#endif
|
||||
/* file logging inited after chroot,chdir,setuid is done so that
|
||||
* it would succeed on SIGHUP as well */
|
||||
if(!cfg->use_syslog)
|
||||
log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -329,33 +487,41 @@ do_chroot(struct daemon* daemon, struct config_file* cfg, int debug_mode)
|
||||
* @param debug_mode: if set, do not daemonize.
|
||||
*/
|
||||
static void
|
||||
run_daemon(char* cfgfile, int cmdline_verbose, int debug_mode)
|
||||
run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode)
|
||||
{
|
||||
struct config_file* cfg = NULL;
|
||||
struct daemon* daemon = NULL;
|
||||
int done_chroot = 0;
|
||||
int done_setup = 0;
|
||||
|
||||
if(!(daemon = daemon_init()))
|
||||
fatal_exit("alloc failure");
|
||||
while(!daemon->need_to_exit) {
|
||||
if(done_chroot)
|
||||
log_info("Restart of %s.", PACKAGE_STRING);
|
||||
else log_info("Start of %s.", PACKAGE_STRING);
|
||||
if(done_setup)
|
||||
verbose(VERB_OPS, "Restart of %s.", PACKAGE_STRING);
|
||||
else verbose(VERB_OPS, "Start of %s.", PACKAGE_STRING);
|
||||
|
||||
/* config stuff */
|
||||
if(!(cfg = config_create()))
|
||||
fatal_exit("Could not alloc config defaults");
|
||||
if(!config_read(cfg, cfgfile))
|
||||
fatal_exit("Could not read config file: %s", cfgfile);
|
||||
if(!config_read(cfg, cfgfile, daemon->chroot)) {
|
||||
if(errno != ENOENT)
|
||||
fatal_exit("Could not read config file: %s",
|
||||
cfgfile);
|
||||
log_warn("Continuing with default config settings");
|
||||
}
|
||||
apply_settings(daemon, cfg, cmdline_verbose);
|
||||
|
||||
/* prepare */
|
||||
if(!daemon_open_shared_ports(daemon))
|
||||
fatal_exit("could not open ports");
|
||||
if(!done_chroot) {
|
||||
do_chroot(daemon, cfg, debug_mode);
|
||||
done_chroot = 1;
|
||||
} else log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
|
||||
if(!done_setup) {
|
||||
perform_setup(daemon, cfg, debug_mode, &cfgfile);
|
||||
done_setup = 1;
|
||||
} else {
|
||||
/* reopen log after HUP to facilitate log rotation */
|
||||
if(!cfg->use_syslog)
|
||||
log_init(cfg->logfile, 0, cfg->chrootdir);
|
||||
}
|
||||
/* work */
|
||||
daemon_fork(daemon);
|
||||
|
||||
@@ -365,8 +531,17 @@ run_daemon(char* cfgfile, int cmdline_verbose, int debug_mode)
|
||||
config_delete(cfg);
|
||||
}
|
||||
verbose(VERB_ALGO, "Exit cleanup.");
|
||||
if(daemon->pidfile)
|
||||
/* this unlink may not work if the pidfile is located outside
|
||||
* of the chroot/workdir or we no longer have permissions */
|
||||
if(daemon->pidfile) {
|
||||
int fd;
|
||||
/* truncate pidfile */
|
||||
fd = open(daemon->pidfile, O_WRONLY | O_TRUNC, 0644);
|
||||
if(fd != -1)
|
||||
close(fd);
|
||||
/* delete pidfile */
|
||||
unlink(daemon->pidfile);
|
||||
}
|
||||
daemon_delete(daemon);
|
||||
}
|
||||
|
||||
@@ -385,12 +560,14 @@ int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
int c;
|
||||
char* cfgfile = CONFIGFILE;
|
||||
const char* cfgfile = CONFIGFILE;
|
||||
int cmdline_verbose = 0;
|
||||
int debug_mode = 0;
|
||||
|
||||
#ifdef HAVE_SBRK
|
||||
/* take debug snapshot of heap */
|
||||
unbound_start_brk = sbrk(0);
|
||||
#endif
|
||||
|
||||
log_init(NULL, 0, NULL);
|
||||
/* parse the options */
|
||||
|
||||
+338
-124
@@ -45,6 +45,8 @@
|
||||
#include "util/random.h"
|
||||
#include "daemon/worker.h"
|
||||
#include "daemon/daemon.h"
|
||||
#include "daemon/remote.h"
|
||||
#include "daemon/acl_list.h"
|
||||
#include "util/netevent.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/module.h"
|
||||
@@ -57,15 +59,19 @@
|
||||
#include "services/cache/infra.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "services/mesh.h"
|
||||
#include "services/localzone.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "util/tube.h"
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
|
||||
/** Size of an UDP datagram */
|
||||
@@ -100,6 +106,7 @@ debug_memleak(size_t accounted, size_t heap,
|
||||
static void
|
||||
debug_total_mem(size_t calctotal)
|
||||
{
|
||||
#ifdef HAVE_SBRK
|
||||
extern void* unbound_start_brk;
|
||||
extern size_t unbound_mem_alloc, unbound_mem_freed;
|
||||
void* cur = sbrk(0);
|
||||
@@ -109,6 +116,9 @@ debug_total_mem(size_t calctotal)
|
||||
(unsigned)unbound_mem_alloc, (unsigned)unbound_mem_freed);
|
||||
debug_memleak(calctotal, (size_t)total,
|
||||
unbound_mem_alloc, unbound_mem_freed);
|
||||
#else
|
||||
(void)calctotal;
|
||||
#endif /* HAVE_SBRK */
|
||||
}
|
||||
#endif /* UNBOUND_ALLOC_STATS */
|
||||
|
||||
@@ -134,13 +144,13 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
|
||||
superac = alloc_get_mem(&worker->daemon->superalloc);
|
||||
iter = 0;
|
||||
val = 0;
|
||||
for(i=0; i<worker->env.mesh->num_modules; i++) {
|
||||
log_assert(fptr_whitelist_mod_get_mem(worker->env.mesh->
|
||||
modfunc[i]->get_mem));
|
||||
if(strcmp(worker->env.mesh->modfunc[i]->name, "validator")==0)
|
||||
val += (*worker->env.mesh->modfunc[i]->get_mem)
|
||||
for(i=0; i<worker->env.mesh->mods.num; i++) {
|
||||
fptr_ok(fptr_whitelist_mod_get_mem(worker->env.mesh->
|
||||
mods.mod[i]->get_mem));
|
||||
if(strcmp(worker->env.mesh->mods.mod[i]->name, "validator")==0)
|
||||
val += (*worker->env.mesh->mods.mod[i]->get_mem)
|
||||
(&worker->env, i);
|
||||
else iter += (*worker->env.mesh->modfunc[i]->get_mem)
|
||||
else iter += (*worker->env.mesh->mods.mod[i]->get_mem)
|
||||
(&worker->env, i);
|
||||
}
|
||||
me = sizeof(*worker) + sizeof(*worker->base) + sizeof(*worker->comsig)
|
||||
@@ -161,21 +171,33 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
|
||||
(unsigned)infra, (unsigned)iter, (unsigned)val, (unsigned)ac,
|
||||
(unsigned)superac, (unsigned)me);
|
||||
debug_total_mem(total);
|
||||
#else /* no UNBOUND_ALLOC_STATS */
|
||||
size_t val = 0;
|
||||
int i;
|
||||
if(verbosity < VERB_QUERY)
|
||||
return;
|
||||
for(i=0; i<worker->env.mesh->mods.num; i++) {
|
||||
fptr_ok(fptr_whitelist_mod_get_mem(worker->env.mesh->
|
||||
mods.mod[i]->get_mem));
|
||||
if(strcmp(worker->env.mesh->mods.mod[i]->name, "validator")==0)
|
||||
val += (*worker->env.mesh->mods.mod[i]->get_mem)
|
||||
(&worker->env, i);
|
||||
}
|
||||
verbose(VERB_QUERY, "cache memory msg=%u rrset=%u infra=%u val=%u",
|
||||
(unsigned)slabhash_get_mem(worker->env.msg_cache),
|
||||
(unsigned)slabhash_get_mem(&worker->env.rrset_cache->table),
|
||||
(unsigned)infra_get_mem(worker->env.infra_cache),
|
||||
(unsigned)val);
|
||||
#endif /* UNBOUND_ALLOC_STATS */
|
||||
}
|
||||
|
||||
void
|
||||
worker_send_cmd(struct worker* worker, ldns_buffer* buffer,
|
||||
enum worker_commands cmd)
|
||||
worker_send_cmd(struct worker* worker, enum worker_commands cmd)
|
||||
{
|
||||
ldns_buffer_clear(buffer);
|
||||
/* like DNS message, length data */
|
||||
ldns_buffer_write_u16(buffer, sizeof(uint32_t));
|
||||
ldns_buffer_write_u32(buffer, (uint32_t)cmd);
|
||||
ldns_buffer_flip(buffer);
|
||||
if(!write_socket(worker->cmd_send_fd, ldns_buffer_begin(buffer),
|
||||
ldns_buffer_limit(buffer)))
|
||||
log_err("write socket: %s", strerror(errno));
|
||||
uint32_t c = (uint32_t)htonl(cmd);
|
||||
if(!tube_write_msg(worker->cmd, (uint8_t*)&c, sizeof(c), 0)) {
|
||||
log_err("worker send cmd %d failed", (int)cmd);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
@@ -189,7 +211,7 @@ worker_handle_reply(struct comm_point* c, void* arg, int error,
|
||||
e.qsent = NULL;
|
||||
|
||||
if(error != 0) {
|
||||
mesh_report_reply(worker->env.mesh, &e, 0, reply_info);
|
||||
mesh_report_reply(worker->env.mesh, &e, reply_info, error);
|
||||
worker_mem_report(worker, NULL);
|
||||
return 0;
|
||||
}
|
||||
@@ -200,11 +222,12 @@ worker_handle_reply(struct comm_point* c, void* arg, int error,
|
||||
|| LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
|
||||
/* error becomes timeout for the module as if this reply
|
||||
* never arrived. */
|
||||
mesh_report_reply(worker->env.mesh, &e, 0, reply_info);
|
||||
mesh_report_reply(worker->env.mesh, &e, reply_info,
|
||||
NETEVENT_TIMEOUT);
|
||||
worker_mem_report(worker, NULL);
|
||||
return 0;
|
||||
}
|
||||
mesh_report_reply(worker->env.mesh, &e, 1, reply_info);
|
||||
mesh_report_reply(worker->env.mesh, &e, reply_info, NETEVENT_NOERROR);
|
||||
worker_mem_report(worker, NULL);
|
||||
return 0;
|
||||
}
|
||||
@@ -219,7 +242,7 @@ worker_handle_service_reply(struct comm_point* c, void* arg, int error,
|
||||
|
||||
verbose(VERB_ALGO, "worker svcd callback for qstate %p", e->qstate);
|
||||
if(error != 0) {
|
||||
mesh_report_reply(worker->env.mesh, e, 0, reply_info);
|
||||
mesh_report_reply(worker->env.mesh, e, reply_info, error);
|
||||
worker_mem_report(worker, sq);
|
||||
return 0;
|
||||
}
|
||||
@@ -231,11 +254,12 @@ worker_handle_service_reply(struct comm_point* c, void* arg, int error,
|
||||
/* error becomes timeout for the module as if this reply
|
||||
* never arrived. */
|
||||
verbose(VERB_ALGO, "worker: bad reply handled as timeout");
|
||||
mesh_report_reply(worker->env.mesh, e, 0, reply_info);
|
||||
mesh_report_reply(worker->env.mesh, e, reply_info,
|
||||
NETEVENT_TIMEOUT);
|
||||
worker_mem_report(worker, sq);
|
||||
return 0;
|
||||
}
|
||||
mesh_report_reply(worker->env.mesh, e, 1, reply_info);
|
||||
mesh_report_reply(worker->env.mesh, e, reply_info, NETEVENT_NOERROR);
|
||||
worker_mem_report(worker, sq);
|
||||
return 0;
|
||||
}
|
||||
@@ -248,73 +272,88 @@ static int
|
||||
worker_check_request(ldns_buffer* pkt, struct worker* worker)
|
||||
{
|
||||
if(ldns_buffer_limit(pkt) < LDNS_HEADER_SIZE) {
|
||||
verbose(VERB_DETAIL, "request too short, discarded");
|
||||
verbose(VERB_QUERY, "request too short, discarded");
|
||||
return -1;
|
||||
}
|
||||
if(ldns_buffer_limit(pkt) > NORMAL_UDP_SIZE &&
|
||||
worker->daemon->cfg->harden_large_queries) {
|
||||
verbose(VERB_DETAIL, "request too large, discarded");
|
||||
verbose(VERB_QUERY, "request too large, discarded");
|
||||
return -1;
|
||||
}
|
||||
if(LDNS_QR_WIRE(ldns_buffer_begin(pkt))) {
|
||||
verbose(VERB_DETAIL, "request has QR bit on, discarded");
|
||||
verbose(VERB_QUERY, "request has QR bit on, discarded");
|
||||
return -1;
|
||||
}
|
||||
if(LDNS_TC_WIRE(ldns_buffer_begin(pkt))) {
|
||||
LDNS_TC_CLR(ldns_buffer_begin(pkt));
|
||||
verbose(VERB_QUERY, "request bad, has TC bit on");
|
||||
return LDNS_RCODE_FORMERR;
|
||||
}
|
||||
if(LDNS_OPCODE_WIRE(ldns_buffer_begin(pkt)) != LDNS_PACKET_QUERY) {
|
||||
verbose(VERB_DETAIL, "request unknown opcode %d",
|
||||
verbose(VERB_QUERY, "request unknown opcode %d",
|
||||
LDNS_OPCODE_WIRE(ldns_buffer_begin(pkt)));
|
||||
return LDNS_RCODE_NOTIMPL;
|
||||
}
|
||||
if(LDNS_QDCOUNT(ldns_buffer_begin(pkt)) != 1) {
|
||||
verbose(VERB_DETAIL, "request wrong nr qd=%d",
|
||||
verbose(VERB_QUERY, "request wrong nr qd=%d",
|
||||
LDNS_QDCOUNT(ldns_buffer_begin(pkt)));
|
||||
return LDNS_RCODE_FORMERR;
|
||||
}
|
||||
if(LDNS_ANCOUNT(ldns_buffer_begin(pkt)) != 0) {
|
||||
verbose(VERB_DETAIL, "request wrong nr an=%d",
|
||||
verbose(VERB_QUERY, "request wrong nr an=%d",
|
||||
LDNS_ANCOUNT(ldns_buffer_begin(pkt)));
|
||||
return LDNS_RCODE_FORMERR;
|
||||
}
|
||||
if(LDNS_NSCOUNT(ldns_buffer_begin(pkt)) != 0) {
|
||||
verbose(VERB_DETAIL, "request wrong nr ns=%d",
|
||||
verbose(VERB_QUERY, "request wrong nr ns=%d",
|
||||
LDNS_NSCOUNT(ldns_buffer_begin(pkt)));
|
||||
return LDNS_RCODE_FORMERR;
|
||||
}
|
||||
if(LDNS_ARCOUNT(ldns_buffer_begin(pkt)) > 1) {
|
||||
verbose(VERB_DETAIL, "request wrong nr ar=%d",
|
||||
verbose(VERB_QUERY, "request wrong nr ar=%d",
|
||||
LDNS_ARCOUNT(ldns_buffer_begin(pkt)));
|
||||
return LDNS_RCODE_FORMERR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
worker_handle_control_cmd(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* ATTR_UNUSED(reply_info))
|
||||
void
|
||||
worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg,
|
||||
size_t len, int error, void* arg)
|
||||
{
|
||||
struct worker* worker = (struct worker*)arg;
|
||||
enum worker_commands cmd;
|
||||
if(error != NETEVENT_NOERROR) {
|
||||
free(msg);
|
||||
if(error == NETEVENT_CLOSED)
|
||||
comm_base_exit(worker->base);
|
||||
else log_info("control event: %d", error);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
if(ldns_buffer_limit(c->buffer) != sizeof(uint32_t)) {
|
||||
fatal_exit("bad control msg length %d",
|
||||
(int)ldns_buffer_limit(c->buffer));
|
||||
if(len != sizeof(uint32_t)) {
|
||||
fatal_exit("bad control msg length %d", (int)len);
|
||||
}
|
||||
cmd = ldns_buffer_read_u32(c->buffer);
|
||||
cmd = ldns_read_uint32(msg);
|
||||
free(msg);
|
||||
switch(cmd) {
|
||||
case worker_cmd_quit:
|
||||
verbose(VERB_ALGO, "got control cmd quit");
|
||||
comm_base_exit(worker->base);
|
||||
break;
|
||||
case worker_cmd_stats:
|
||||
verbose(VERB_ALGO, "got control cmd stats");
|
||||
server_stats_reply(worker);
|
||||
break;
|
||||
#ifdef THREADS_DISABLED
|
||||
case worker_cmd_remote:
|
||||
verbose(VERB_ALGO, "got control cmd remote");
|
||||
daemon_remote_exec(worker);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
log_err("bad command %d", (int)cmd);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** check if a delegation is secure */
|
||||
@@ -382,7 +421,7 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
|
||||
*/
|
||||
uint16_t udpsize = edns->udp_size;
|
||||
int secure = 0;
|
||||
uint32_t timenow = (uint32_t)time(0);
|
||||
uint32_t timenow = *worker->env.now;
|
||||
int must_validate = !(flags&BIT_CD) && worker->env.need_to_validate;
|
||||
struct dns_msg *msg = NULL;
|
||||
struct delegpt *dp;
|
||||
@@ -410,6 +449,10 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
|
||||
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
&msg->qinfo, id, flags, edns);
|
||||
regional_free_all(worker->scratchpad);
|
||||
if(worker->stats.extended) {
|
||||
worker->stats.ans_bogus++;
|
||||
worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL]++;
|
||||
}
|
||||
return 1;
|
||||
case sec_status_secure:
|
||||
/* all rrsets are secure */
|
||||
@@ -439,6 +482,10 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
|
||||
&msg->qinfo, id, flags, edns);
|
||||
}
|
||||
regional_free_all(worker->scratchpad);
|
||||
if(worker->stats.extended) {
|
||||
if(secure) worker->stats.ans_secure++;
|
||||
server_stats_insrcode(&worker->stats, repinfo->c->buffer);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -481,17 +528,16 @@ all_rrsets_secure(struct reply_info* rep) {
|
||||
|
||||
/** answer query from the cache */
|
||||
static int
|
||||
answer_from_cache(struct worker* worker, struct lruhash_entry* e, uint16_t id,
|
||||
uint16_t flags, struct comm_reply* repinfo, struct edns_data* edns)
|
||||
answer_from_cache(struct worker* worker, struct query_info* qinfo,
|
||||
struct reply_info* rep, uint16_t id, uint16_t flags,
|
||||
struct comm_reply* repinfo, struct edns_data* edns)
|
||||
{
|
||||
struct msgreply_entry* mrentry = (struct msgreply_entry*)e->key;
|
||||
struct reply_info* rep = (struct reply_info*)e->data;
|
||||
uint32_t timenow = time(0);
|
||||
uint32_t timenow = *worker->env.now;
|
||||
uint16_t udpsize = edns->udp_size;
|
||||
int secure;
|
||||
int must_validate = !(flags&BIT_CD) && worker->env.need_to_validate;
|
||||
/* see if it is possible */
|
||||
if(rep->ttl <= timenow) {
|
||||
if(rep->ttl < timenow) {
|
||||
/* the rrsets may have been updated in the meantime.
|
||||
* we will refetch the message format from the
|
||||
* authoritative server
|
||||
@@ -523,10 +569,14 @@ answer_from_cache(struct worker* worker, struct lruhash_entry* e, uint16_t id,
|
||||
edns->ext_rcode = 0;
|
||||
edns->bits &= EDNS_DO;
|
||||
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
&mrentry->key, id, flags, edns);
|
||||
qinfo, id, flags, edns);
|
||||
rrset_array_unlock_touch(worker->env.rrset_cache,
|
||||
worker->scratchpad, rep->ref, rep->rrset_count);
|
||||
regional_free_all(worker->scratchpad);
|
||||
if(worker->stats.extended) {
|
||||
worker->stats.ans_bogus ++;
|
||||
worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL] ++;
|
||||
}
|
||||
return 1;
|
||||
} else if( rep->security == sec_status_unchecked && must_validate) {
|
||||
verbose(VERB_ALGO, "Cache reply: unchecked entry needs "
|
||||
@@ -549,17 +599,21 @@ answer_from_cache(struct worker* worker, struct lruhash_entry* e, uint16_t id,
|
||||
edns->udp_size = EDNS_ADVERTISED_SIZE;
|
||||
edns->ext_rcode = 0;
|
||||
edns->bits &= EDNS_DO;
|
||||
if(!reply_info_answer_encode(&mrentry->key, rep, id, flags,
|
||||
if(!reply_info_answer_encode(qinfo, rep, id, flags,
|
||||
repinfo->c->buffer, timenow, 1, worker->scratchpad,
|
||||
udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) {
|
||||
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
&mrentry->key, id, flags, edns);
|
||||
qinfo, id, flags, edns);
|
||||
}
|
||||
/* cannot send the reply right now, because blocking network syscall
|
||||
* is bad while holding locks. */
|
||||
rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad,
|
||||
rep->ref, rep->rrset_count);
|
||||
regional_free_all(worker->scratchpad);
|
||||
if(worker->stats.extended) {
|
||||
if(secure) worker->stats.ans_secure++;
|
||||
server_stats_insrcode(&worker->stats, repinfo->c->buffer);
|
||||
}
|
||||
/* go and return this buffer to the client */
|
||||
return 1;
|
||||
}
|
||||
@@ -663,12 +717,34 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
struct lruhash_entry* e;
|
||||
struct query_info qinfo;
|
||||
struct edns_data edns;
|
||||
enum acl_access acl;
|
||||
|
||||
if(error != NETEVENT_NOERROR) {
|
||||
/* some bad tcp query DNS formats give these error calls */
|
||||
verbose(VERB_ALGO, "handle request called with err=%d", error);
|
||||
return 0;
|
||||
}
|
||||
acl = acl_list_lookup(worker->daemon->acl, &repinfo->addr,
|
||||
repinfo->addrlen);
|
||||
if(acl == acl_deny) {
|
||||
comm_point_drop_reply(repinfo);
|
||||
if(worker->stats.extended)
|
||||
worker->stats.unwanted_queries++;
|
||||
return 0;
|
||||
} else if(acl == acl_refuse) {
|
||||
ldns_buffer_set_limit(c->buffer, LDNS_HEADER_SIZE);
|
||||
ldns_buffer_write_at(c->buffer, 4,
|
||||
(uint8_t*)"\0\0\0\0\0\0\0\0", 8);
|
||||
LDNS_QR_SET(ldns_buffer_begin(c->buffer));
|
||||
LDNS_RCODE_SET(ldns_buffer_begin(c->buffer),
|
||||
LDNS_RCODE_REFUSED);
|
||||
log_addr(VERB_ALGO, "refused query from",
|
||||
&repinfo->addr, repinfo->addrlen);
|
||||
log_buf(VERB_ALGO, "refuse", c->buffer);
|
||||
if(worker->stats.extended)
|
||||
worker->stats.unwanted_queries++;
|
||||
return 1;
|
||||
}
|
||||
if((ret=worker_check_request(c->buffer, worker)) != 0) {
|
||||
verbose(VERB_ALGO, "worker check request: bad query.");
|
||||
if(ret != -1) {
|
||||
@@ -683,9 +759,11 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
/* see if query is in the cache */
|
||||
if(!query_info_parse(&qinfo, c->buffer)) {
|
||||
verbose(VERB_ALGO, "worker parse request: formerror.");
|
||||
ldns_buffer_rewind(c->buffer);
|
||||
LDNS_QR_SET(ldns_buffer_begin(c->buffer));
|
||||
LDNS_RCODE_SET(ldns_buffer_begin(c->buffer),
|
||||
LDNS_RCODE_FORMERR);
|
||||
server_stats_insrcode(&worker->stats, c->buffer);
|
||||
return 1;
|
||||
}
|
||||
if(qinfo.qtype == LDNS_RR_TYPE_AXFR ||
|
||||
@@ -694,12 +772,18 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
LDNS_QR_SET(ldns_buffer_begin(c->buffer));
|
||||
LDNS_RCODE_SET(ldns_buffer_begin(c->buffer),
|
||||
LDNS_RCODE_REFUSED);
|
||||
if(worker->stats.extended) {
|
||||
worker->stats.qtype[qinfo.qtype]++;
|
||||
server_stats_insrcode(&worker->stats, c->buffer);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if((ret=parse_edns_from_pkt(c->buffer, &edns)) != 0) {
|
||||
verbose(VERB_ALGO, "worker parse edns: formerror.");
|
||||
ldns_buffer_rewind(c->buffer);
|
||||
LDNS_QR_SET(ldns_buffer_begin(c->buffer));
|
||||
LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), ret);
|
||||
server_stats_insrcode(&worker->stats, c->buffer);
|
||||
return 1;
|
||||
}
|
||||
if(edns.edns_present && edns.edns_version != 0) {
|
||||
@@ -716,7 +800,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
}
|
||||
if(edns.edns_present && edns.udp_size < NORMAL_UDP_SIZE &&
|
||||
worker->daemon->cfg->harden_short_bufsize) {
|
||||
verbose(VERB_DETAIL, "worker request: EDNS bufsize %d ignored",
|
||||
verbose(VERB_QUERY, "worker request: EDNS bufsize %d ignored",
|
||||
(int)edns.udp_size);
|
||||
edns.udp_size = NORMAL_UDP_SIZE;
|
||||
}
|
||||
@@ -732,16 +816,44 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
ldns_buffer_flip(c->buffer);
|
||||
return 1;
|
||||
}
|
||||
if(worker->stats.extended)
|
||||
server_stats_insquery(&worker->stats, c, qinfo.qtype,
|
||||
qinfo.qclass, &edns, repinfo);
|
||||
if(c->type != comm_udp)
|
||||
edns.udp_size = 65535; /* max size for TCP replies */
|
||||
if(qinfo.qclass == LDNS_RR_CLASS_CH && answer_chaos(worker, &qinfo,
|
||||
&edns, c->buffer)) {
|
||||
server_stats_insrcode(&worker->stats, c->buffer);
|
||||
return 1;
|
||||
}
|
||||
if(local_zones_answer(worker->daemon->local_zones, &qinfo, &edns,
|
||||
c->buffer, worker->scratchpad)) {
|
||||
if(ldns_buffer_limit(c->buffer) == 0) {
|
||||
comm_point_drop_reply(repinfo);
|
||||
return 0;
|
||||
}
|
||||
server_stats_insrcode(&worker->stats, c->buffer);
|
||||
return 1;
|
||||
}
|
||||
if(!(LDNS_RD_WIRE(ldns_buffer_begin(c->buffer))) &&
|
||||
acl != acl_allow_snoop ) {
|
||||
ldns_buffer_set_limit(c->buffer, LDNS_HEADER_SIZE);
|
||||
ldns_buffer_write_at(c->buffer, 4,
|
||||
(uint8_t*)"\0\0\0\0\0\0\0\0", 8);
|
||||
LDNS_QR_SET(ldns_buffer_begin(c->buffer));
|
||||
LDNS_RCODE_SET(ldns_buffer_begin(c->buffer),
|
||||
LDNS_RCODE_REFUSED);
|
||||
ldns_buffer_flip(c->buffer);
|
||||
server_stats_insrcode(&worker->stats, c->buffer);
|
||||
log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from",
|
||||
&repinfo->addr, repinfo->addrlen);
|
||||
return 1;
|
||||
}
|
||||
h = query_info_hash(&qinfo);
|
||||
if((e=slabhash_lookup(worker->env.msg_cache, h, &qinfo, 0))) {
|
||||
/* answer from cache - we have acquired a readlock on it */
|
||||
if(answer_from_cache(worker, e,
|
||||
if(answer_from_cache(worker, &qinfo,
|
||||
(struct reply_info*)e->data,
|
||||
*(uint16_t*)ldns_buffer_begin(c->buffer),
|
||||
ldns_buffer_read_u16_at(c->buffer, 2), repinfo,
|
||||
&edns)) {
|
||||
@@ -765,10 +877,11 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
server_stats_querymiss(&worker->stats, worker);
|
||||
|
||||
/* grab a work request structure for this new request */
|
||||
if(worker->env.mesh->all.count > worker->request_size) {
|
||||
verbose(VERB_ALGO, "Too many requests active. "
|
||||
if(worker->env.mesh->num_reply_addrs>worker->request_size*16) {
|
||||
/* protect our memory usage from storing reply addresses */
|
||||
verbose(VERB_ALGO, "Too many requests queued. "
|
||||
"dropping incoming query.");
|
||||
worker->stats.num_query_list_exceeded++;
|
||||
worker->env.mesh->stats_dropped++;
|
||||
comm_point_drop_reply(repinfo);
|
||||
return 0;
|
||||
}
|
||||
@@ -788,22 +901,26 @@ worker_sighandler(int sig, void* arg)
|
||||
* in the cause for unbound to exit */
|
||||
struct worker* worker = (struct worker*)arg;
|
||||
switch(sig) {
|
||||
#ifdef SIGHUP
|
||||
case SIGHUP:
|
||||
verbose(VERB_DETAIL, "caught signal SIGHUP");
|
||||
verbose(VERB_QUERY, "caught signal SIGHUP");
|
||||
comm_base_exit(worker->base);
|
||||
break;
|
||||
#endif
|
||||
case SIGINT:
|
||||
verbose(VERB_DETAIL, "caught signal SIGINT");
|
||||
verbose(VERB_QUERY, "caught signal SIGINT");
|
||||
worker->need_to_exit = 1;
|
||||
comm_base_exit(worker->base);
|
||||
break;
|
||||
#ifdef SIGQUIT
|
||||
case SIGQUIT:
|
||||
verbose(VERB_DETAIL, "caught signal SIGQUIT");
|
||||
verbose(VERB_QUERY, "caught signal SIGQUIT");
|
||||
worker->need_to_exit = 1;
|
||||
comm_base_exit(worker->base);
|
||||
break;
|
||||
#endif
|
||||
case SIGTERM:
|
||||
verbose(VERB_DETAIL, "caught signal SIGTERM");
|
||||
verbose(VERB_QUERY, "caught signal SIGTERM");
|
||||
worker->need_to_exit = 1;
|
||||
comm_base_exit(worker->base);
|
||||
break;
|
||||
@@ -813,33 +930,52 @@ worker_sighandler(int sig, void* arg)
|
||||
}
|
||||
}
|
||||
|
||||
/** restart statistics timer for worker, if enabled */
|
||||
static void
|
||||
worker_restart_timer(struct worker* worker)
|
||||
{
|
||||
if(worker->env.cfg->stat_interval > 0) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = worker->env.cfg->stat_interval;
|
||||
tv.tv_usec = 0;
|
||||
comm_timer_set(worker->stat_timer, &tv);
|
||||
}
|
||||
}
|
||||
|
||||
void worker_stat_timer_cb(void* arg)
|
||||
{
|
||||
struct worker* worker = (struct worker*)arg;
|
||||
server_stats_log(&worker->stats, worker, worker->thread_num);
|
||||
mesh_stats(worker->env.mesh, "mesh has");
|
||||
worker_mem_report(worker, NULL);
|
||||
if(!worker->daemon->cfg->stat_cumulative) {
|
||||
server_stats_init(&worker->stats, worker->env.cfg);
|
||||
mesh_stats_clear(worker->env.mesh);
|
||||
worker->back->unwanted_replies = 0;
|
||||
}
|
||||
/* start next timer */
|
||||
worker_restart_timer(worker);
|
||||
}
|
||||
|
||||
struct worker*
|
||||
worker_create(struct daemon* daemon, int id)
|
||||
worker_create(struct daemon* daemon, int id, int* ports, int n)
|
||||
{
|
||||
struct worker* worker = (struct worker*)calloc(1,
|
||||
sizeof(struct worker));
|
||||
if(!worker)
|
||||
return NULL;
|
||||
worker->numports = n;
|
||||
worker->ports = (int*)memdup(ports, sizeof(int)*n);
|
||||
if(!worker->ports) {
|
||||
free(worker);
|
||||
return NULL;
|
||||
}
|
||||
worker->daemon = daemon;
|
||||
worker->thread_num = id;
|
||||
worker->cmd_send_fd = -1;
|
||||
worker->cmd_recv_fd = -1;
|
||||
if(id != 0) {
|
||||
int sv[2];
|
||||
/* create socketpair to communicate with worker */
|
||||
if(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
|
||||
free(worker);
|
||||
log_err("socketpair: %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
if(!fd_set_nonblock(sv[0]) || !fd_set_nonblock(sv[1])) {
|
||||
close(sv[0]);
|
||||
close(sv[1]);
|
||||
free(worker);
|
||||
return NULL;
|
||||
}
|
||||
worker->cmd_send_fd = sv[0];
|
||||
worker->cmd_recv_fd = sv[1];
|
||||
if(!(worker->cmd = tube_create())) {
|
||||
free(worker->ports);
|
||||
free(worker);
|
||||
return NULL;
|
||||
}
|
||||
return worker;
|
||||
}
|
||||
@@ -849,45 +985,56 @@ worker_init(struct worker* worker, struct config_file *cfg,
|
||||
struct listen_port* ports, int do_sigs)
|
||||
{
|
||||
unsigned int seed;
|
||||
int startport;
|
||||
worker->need_to_exit = 0;
|
||||
worker->base = comm_base_create();
|
||||
worker->base = comm_base_create(do_sigs);
|
||||
if(!worker->base) {
|
||||
log_err("could not create event handling base");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
if(do_sigs) {
|
||||
#ifdef SIGHUP
|
||||
ub_thread_sig_unblock(SIGHUP);
|
||||
#endif
|
||||
ub_thread_sig_unblock(SIGINT);
|
||||
#ifdef SIGQUIT
|
||||
ub_thread_sig_unblock(SIGQUIT);
|
||||
#endif
|
||||
ub_thread_sig_unblock(SIGTERM);
|
||||
#ifndef LIBEVENT_SIGNAL_PROBLEM
|
||||
worker->comsig = comm_signal_create(worker->base,
|
||||
worker_sighandler, worker);
|
||||
if(!worker->comsig || !comm_signal_bind(worker->comsig, SIGHUP)
|
||||
|| !comm_signal_bind(worker->comsig, SIGINT)
|
||||
if(!worker->comsig
|
||||
#ifdef SIGHUP
|
||||
|| !comm_signal_bind(worker->comsig, SIGHUP)
|
||||
#endif
|
||||
#ifdef SIGQUIT
|
||||
|| !comm_signal_bind(worker->comsig, SIGQUIT)
|
||||
#endif
|
||||
|| !comm_signal_bind(worker->comsig, SIGTERM)
|
||||
|| !comm_signal_bind(worker->comsig, SIGQUIT)) {
|
||||
|| !comm_signal_bind(worker->comsig, SIGINT)) {
|
||||
log_err("could not create signal handlers");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
#endif /* LIBEVENT_SIGNAL_PROBLEM */
|
||||
if(!(worker->rc = daemon_remote_create(worker))) {
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
if(!daemon_remote_open_accept(worker->rc,
|
||||
worker->daemon->rc_ports)) {
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
} else { /* !do_sigs */
|
||||
worker->comsig = 0;
|
||||
}
|
||||
/* init random(), large table size. */
|
||||
if(!(worker->rndstate = (struct ub_randstate*)calloc(1,
|
||||
sizeof(struct ub_randstate)))) {
|
||||
log_err("malloc rndtable failed.");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
worker->comsig = NULL;
|
||||
worker->rc = NULL;
|
||||
}
|
||||
seed = (unsigned int)time(NULL) ^ (unsigned int)getpid() ^
|
||||
(((unsigned int)worker->thread_num)<<17);
|
||||
/* shift thread_num so it does not match out pid bits */
|
||||
if(!ub_initstate(seed, worker->rndstate, RND_STATE_SIZE)) {
|
||||
if(!(worker->rndstate = ub_initstate(seed, NULL))) {
|
||||
seed = 0;
|
||||
log_err("could not init random numbers.");
|
||||
worker_delete(worker);
|
||||
@@ -902,28 +1049,31 @@ worker_init(struct worker* worker, struct config_file *cfg,
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
startport = cfg->outgoing_base_port +
|
||||
cfg->outgoing_num_ports * worker->thread_num;
|
||||
worker->back = outside_network_create(worker->base,
|
||||
cfg->msg_buffer_size, (size_t)cfg->outgoing_num_ports,
|
||||
cfg->out_ifs, cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6,
|
||||
startport, cfg->do_tcp?cfg->outgoing_num_tcp:0,
|
||||
worker->daemon->env->infra_cache, worker->rndstate);
|
||||
cfg->do_tcp?cfg->outgoing_num_tcp:0,
|
||||
worker->daemon->env->infra_cache, worker->rndstate,
|
||||
cfg->use_caps_bits_for_id, worker->ports, worker->numports,
|
||||
cfg->unwanted_threshold, &worker_alloc_cleanup, worker);
|
||||
if(!worker->back) {
|
||||
log_err("could not create outgoing sockets");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
if(worker->thread_num != 0) {
|
||||
/* start listening to commands */
|
||||
if(!(worker->cmd_com=comm_point_create_local(worker->base,
|
||||
worker->cmd_recv_fd, cfg->msg_buffer_size,
|
||||
worker_handle_control_cmd, worker))) {
|
||||
log_err("could not create control compt.");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
/* start listening to commands */
|
||||
if(!tube_setup_bg_listen(worker->cmd, worker->base,
|
||||
&worker_handle_control_cmd, worker)) {
|
||||
log_err("could not create control compt.");
|
||||
worker_delete(worker);
|
||||
return 0;
|
||||
}
|
||||
worker->stat_timer = comm_timer_create(worker->base,
|
||||
worker_stat_timer_cb, worker);
|
||||
if(!worker->stat_timer) {
|
||||
log_err("could not create statistics timer");
|
||||
}
|
||||
|
||||
/* we use the msg_buffer_size as a good estimate for what the
|
||||
* user wants for memory usage sizes */
|
||||
worker->scratchpad = regional_create_custom(cfg->msg_buffer_size);
|
||||
@@ -934,19 +1084,21 @@ worker_init(struct worker* worker, struct config_file *cfg,
|
||||
}
|
||||
worker->request_size = cfg->num_queries_per_thread;
|
||||
|
||||
server_stats_init(&worker->stats);
|
||||
server_stats_init(&worker->stats, cfg);
|
||||
alloc_init(&worker->alloc, &worker->daemon->superalloc,
|
||||
worker->thread_num);
|
||||
alloc_set_id_cleanup(&worker->alloc, &worker_alloc_cleanup, worker);
|
||||
worker->env = *worker->daemon->env;
|
||||
comm_base_timept(worker->base, &worker->env.now, &worker->env.now_tv);
|
||||
if(worker->thread_num == 0)
|
||||
log_set_time(worker->env.now);
|
||||
worker->env.worker = worker;
|
||||
worker->env.send_packet = &worker_send_packet;
|
||||
worker->env.send_query = &worker_send_query;
|
||||
worker->env.alloc = &worker->alloc;
|
||||
worker->env.rnd = worker->rndstate;
|
||||
worker->env.scratch = worker->scratchpad;
|
||||
worker->env.mesh = mesh_create(worker->daemon->num_modules,
|
||||
worker->daemon->modfunc, &worker->env);
|
||||
worker->env.mesh = mesh_create(&worker->daemon->mods, &worker->env);
|
||||
worker->env.detach_subs = &mesh_detach_subs;
|
||||
worker->env.attach_sub = &mesh_attach_sub;
|
||||
worker->env.kill_sub = &mesh_state_delete;
|
||||
@@ -957,6 +1109,12 @@ worker_init(struct worker* worker, struct config_file *cfg,
|
||||
return 0;
|
||||
}
|
||||
worker_mem_report(worker, NULL);
|
||||
/* if statistics enabled start timer */
|
||||
if(worker->env.cfg->stat_interval > 0) {
|
||||
verbose(VERB_ALGO, "set statistics interval %d secs",
|
||||
worker->env.cfg->stat_interval);
|
||||
worker_restart_timer(worker);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -971,27 +1129,24 @@ worker_delete(struct worker* worker)
|
||||
{
|
||||
if(!worker)
|
||||
return;
|
||||
if(worker->env.mesh)
|
||||
if(worker->env.mesh && verbosity >= VERB_OPS) {
|
||||
server_stats_log(&worker->stats, worker, worker->thread_num);
|
||||
mesh_stats(worker->env.mesh, "mesh has");
|
||||
server_stats_log(&worker->stats, worker->thread_num);
|
||||
worker_mem_report(worker, NULL);
|
||||
worker_mem_report(worker, NULL);
|
||||
}
|
||||
mesh_delete(worker->env.mesh);
|
||||
ldns_buffer_free(worker->env.scratch_buffer);
|
||||
listen_delete(worker->front);
|
||||
outside_network_delete(worker->back);
|
||||
comm_signal_delete(worker->comsig);
|
||||
comm_point_delete(worker->cmd_com);
|
||||
tube_delete(worker->cmd);
|
||||
comm_timer_delete(worker->stat_timer);
|
||||
daemon_remote_delete(worker->rc);
|
||||
free(worker->ports);
|
||||
if(worker->thread_num == 0)
|
||||
log_set_time(NULL);
|
||||
comm_base_delete(worker->base);
|
||||
ub_randfree(worker->rndstate);
|
||||
free(worker->rndstate);
|
||||
/* close fds after deleting commpoints, to be sure.
|
||||
Also epoll does not like closing fd before event_del */
|
||||
if(worker->cmd_send_fd != -1)
|
||||
close(worker->cmd_send_fd);
|
||||
worker->cmd_send_fd = -1;
|
||||
if(worker->cmd_recv_fd != -1)
|
||||
close(worker->cmd_recv_fd);
|
||||
worker->cmd_recv_fd = -1;
|
||||
alloc_clear(&worker->alloc);
|
||||
regional_destroy(worker->scratchpad);
|
||||
free(worker);
|
||||
@@ -1004,12 +1159,10 @@ worker_send_packet(ldns_buffer* pkt, struct sockaddr_storage* addr,
|
||||
struct worker* worker = q->env->worker;
|
||||
if(use_tcp) {
|
||||
return pending_tcp_query(worker->back, pkt, addr, addrlen,
|
||||
timeout, worker_handle_reply, q,
|
||||
worker->rndstate) != 0;
|
||||
timeout, worker_handle_reply, q) != 0;
|
||||
}
|
||||
return pending_udp_query(worker->back, pkt, addr, addrlen,
|
||||
timeout*1000, worker_handle_reply, q,
|
||||
worker->rndstate) != 0;
|
||||
timeout*1000, worker_handle_reply, q) != 0;
|
||||
}
|
||||
|
||||
/** compare outbound entry qstates */
|
||||
@@ -1052,3 +1205,64 @@ worker_alloc_cleanup(void* arg)
|
||||
slabhash_clear(&worker->env.rrset_cache->table);
|
||||
slabhash_clear(worker->env.msg_cache);
|
||||
}
|
||||
|
||||
/* --- fake callbacks for fptr_wlist to work --- */
|
||||
int libworker_send_packet(ldns_buffer* ATTR_UNUSED(pkt),
|
||||
struct sockaddr_storage* ATTR_UNUSED(addr),
|
||||
socklen_t ATTR_UNUSED(addrlen), int ATTR_UNUSED(timeout),
|
||||
struct module_qstate* ATTR_UNUSED(q), int ATTR_UNUSED(use_tcp))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct outbound_entry* libworker_send_query(uint8_t* ATTR_UNUSED(qname),
|
||||
size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
|
||||
uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags),
|
||||
int ATTR_UNUSED(dnssec), struct sockaddr_storage* ATTR_UNUSED(addr),
|
||||
socklen_t ATTR_UNUSED(addrlen), struct module_qstate* ATTR_UNUSED(q))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int libworker_handle_reply(struct comm_point* ATTR_UNUSED(c),
|
||||
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
|
||||
struct comm_reply* ATTR_UNUSED(reply_info))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int libworker_handle_service_reply(struct comm_point* ATTR_UNUSED(c),
|
||||
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
|
||||
struct comm_reply* ATTR_UNUSED(reply_info))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void libworker_handle_control_cmd(struct tube* ATTR_UNUSED(tube),
|
||||
uint8_t* ATTR_UNUSED(buffer), size_t ATTR_UNUSED(len),
|
||||
int ATTR_UNUSED(error), void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
int context_query_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int order_lock_cmp(const void* ATTR_UNUSED(e1), const void* ATTR_UNUSED(e2))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int codeline_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+33
-19
@@ -58,14 +58,17 @@ struct daemon;
|
||||
struct listen_port;
|
||||
struct ub_randstate;
|
||||
struct regional;
|
||||
|
||||
/** size of table used for random numbers. large to be more secure. */
|
||||
#define RND_STATE_SIZE 256
|
||||
struct tube;
|
||||
struct daemon_remote;
|
||||
|
||||
/** worker commands */
|
||||
enum worker_commands {
|
||||
/** make the worker quit */
|
||||
worker_cmd_quit
|
||||
worker_cmd_quit,
|
||||
/** obtain statistics */
|
||||
worker_cmd_stats,
|
||||
/** execute remote control command */
|
||||
worker_cmd_remote
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -79,20 +82,26 @@ struct worker {
|
||||
struct daemon* daemon;
|
||||
/** thread id */
|
||||
ub_thread_t thr_id;
|
||||
/** fd 0 of socketpair, write commands for worker to this one */
|
||||
int cmd_send_fd;
|
||||
/** fd 1 of socketpair, worker listens on this one */
|
||||
int cmd_recv_fd;
|
||||
/** pipe, for commands for this worker */
|
||||
struct tube* cmd;
|
||||
/** the event base this worker works with */
|
||||
struct comm_base* base;
|
||||
/** the frontside listening interface where request events come in */
|
||||
struct listen_dnsport* front;
|
||||
/** the backside outside network interface to the auth servers */
|
||||
struct outside_network* back;
|
||||
/** ports to be used by this worker. */
|
||||
int* ports;
|
||||
/** number of ports for this worker */
|
||||
int numports;
|
||||
/** the signal handler */
|
||||
struct comm_signal* comsig;
|
||||
/** commpoint to listen to commands. */
|
||||
struct comm_point* cmd_com;
|
||||
/** timer for statistics */
|
||||
struct comm_timer* stat_timer;
|
||||
/** remote control state (for first thread only) */
|
||||
struct daemon_remote* rc;
|
||||
|
||||
/** number of requests that can be handled by this worker */
|
||||
size_t request_size;
|
||||
@@ -117,9 +126,11 @@ struct worker {
|
||||
* with backpointers only. Use worker_init on it later.
|
||||
* @param daemon: the daemon that this worker thread is part of.
|
||||
* @param id: the thread number from 0.. numthreads-1.
|
||||
* @param ports: the ports it is allowed to use, array.
|
||||
* @param n: the number of ports.
|
||||
* @return: the new worker or NULL on alloc failure.
|
||||
*/
|
||||
struct worker* worker_create(struct daemon* daemon, int id);
|
||||
struct worker* worker_create(struct daemon* daemon, int id, int* ports, int n);
|
||||
|
||||
/**
|
||||
* Initialize worker.
|
||||
@@ -146,11 +157,9 @@ void worker_delete(struct worker* worker);
|
||||
/**
|
||||
* Send a command to a worker. Uses blocking writes.
|
||||
* @param worker: worker to send command to.
|
||||
* @param buffer: an empty buffer to use.
|
||||
* @param cmd: command to send.
|
||||
*/
|
||||
void worker_send_cmd(struct worker* worker, ldns_buffer* buffer,
|
||||
enum worker_commands cmd);
|
||||
void worker_send_cmd(struct worker* worker, enum worker_commands cmd);
|
||||
|
||||
/**
|
||||
* Worker signal handler function. User argument is the worker itself.
|
||||
@@ -193,14 +202,16 @@ struct outbound_entry* worker_send_query(uint8_t* qname, size_t qnamelen,
|
||||
struct module_qstate* q);
|
||||
|
||||
/**
|
||||
* process control messages from the main thread.
|
||||
* @param c: comm point to read from.
|
||||
* @param arg: worker.
|
||||
* @param error: error status of comm point.
|
||||
* @param reply_info: not used.
|
||||
* process control messages from the main thread. Frees the control
|
||||
* command message.
|
||||
* @param tube: tube control message came on.
|
||||
* @param msg: message contents. Is freed.
|
||||
* @param len: length of message.
|
||||
* @param error: if error (NETEVENT_*) happened.
|
||||
* @param arg: user argument
|
||||
*/
|
||||
int worker_handle_control_cmd(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* reply_info);
|
||||
void worker_handle_control_cmd(struct tube* tube, uint8_t* msg, size_t len,
|
||||
int error, void* arg);
|
||||
|
||||
/** handles callbacks from listening event interface */
|
||||
int worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
@@ -217,4 +228,7 @@ int worker_handle_service_reply(struct comm_point* c, void* arg, int error,
|
||||
/** cleanup the cache to remove all rrset IDs from it, arg is worker */
|
||||
void worker_alloc_cleanup(void* arg);
|
||||
|
||||
/** statistics timer callback handler */
|
||||
void worker_stat_timer_cb(void* arg);
|
||||
|
||||
#endif /* DAEMON_WORKER_H */
|
||||
|
||||
+11
-4
@@ -1,9 +1,16 @@
|
||||
Unbound was developed at NLnet Labs by Wouter Wijngaards.
|
||||
|
||||
Unbound was devised by Bill Manning, David Blacka, and Matt Larson
|
||||
from the University of California and from Verisign. The Java prototype
|
||||
was made in further cooperation with Geoff Sisson and Roy Arends from Nominet.
|
||||
Unbound was architected in January of 2004 by Jakob Schlyter of Kirei
|
||||
and Roy Arends of Nominet. VeriSign and EP.Net funded development of
|
||||
the prototype, which was built by David Blacka and Matt Larson of VeriSign.
|
||||
Late in 2006, NLnet Labs joined the effort, writing an implementation in C
|
||||
based on the existing prototype and using experience NLnet Labs gained
|
||||
during the development of NSD, an authoritative DNS server.
|
||||
|
||||
At NLnet Labs, Jelte Jansen and Mark Santcroos reviewed the unbound C sources.
|
||||
At NLnet Labs, Jelte Jansen, Mark Santcroos and Matthijs Mekking
|
||||
reviewed the unbound C sources.
|
||||
|
||||
Jakob Schlyter - for advice on secure settings, random numbers and blacklists.
|
||||
|
||||
Ondřej Surý - running coverity analysis tool on 0.9 dev version.
|
||||
Alexander Gall - multihomed, anycast testing of unbound resolver server.
|
||||
|
||||
+1323
-7
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,91 @@
|
||||
Unbound Features
|
||||
|
||||
(C) Copyright 2008, Wouter Wijngaards, NLnet Labs.
|
||||
|
||||
|
||||
This document describes the features and RFCs that unbound
|
||||
adheres to, and which ones are decided to be out of scope.
|
||||
|
||||
|
||||
Big Features
|
||||
------------
|
||||
Recursive service.
|
||||
Caching service.
|
||||
Forwarding and stub zones.
|
||||
No authoritative service.
|
||||
DNSSEC Validation options.
|
||||
EDNS0, NSEC3, Unknown-RR-types.
|
||||
|
||||
|
||||
Details
|
||||
-------
|
||||
Processing support
|
||||
RFC 1034-1035: as a recursive, caching server. Not authoritative.
|
||||
including CNAMEs, referrals, wildcards, classes, ...
|
||||
RFC 4033-4035: as a validating caching server (unbound daemon).
|
||||
as a validating stub (libunbound).
|
||||
RFC 1918.
|
||||
RFC 2181: completely, including the trust model, keeping rrsets together.
|
||||
RFC 2672: DNAME support.
|
||||
RFC 3597: Unknown RR type support.
|
||||
RFC 2671: EDNS0 support, default advertisement 4Kb size.
|
||||
RFC 5155: NSEC3, NSEC3PARAM types
|
||||
AAAA type. and IP6 dual stack support.
|
||||
type ANY queries are supported.
|
||||
RFC 2308: TTL directive, and the rest of the RFC too.
|
||||
RFC 4592: wildcards.
|
||||
|
||||
RFC 1995, 1996, 2136: not authoritative, so no AXFR, IXFR, NOTIFY or
|
||||
dynamic update services are appropriate.
|
||||
|
||||
chroot and drop-root-privileges support, default enabled in config file.
|
||||
|
||||
AD bit in query can be used to request AD bit in response (w/o using DO bit).
|
||||
CD bit in query can be used to request bogus data.
|
||||
UDP and TCP service is provided downstream.
|
||||
UDP and TCP are used to request from upstream servers.
|
||||
Multiple queries can be made over a TCP stream.
|
||||
|
||||
No TSIG support at this time.
|
||||
No SIG0 support at this time.
|
||||
No dTLS support at this time.
|
||||
This is not a DNS statistics package, but some operationally useful
|
||||
values are provided.
|
||||
TXT RRs from the Chaos class (id.server, hostname.bind, ...) supported.
|
||||
|
||||
draft-forgery-resilience: all recommendations followed.
|
||||
draft-0x20: experimental implementation (incomplete).
|
||||
implements bitwise echo of the query to support downstream 0x20.
|
||||
draft-ietf-dnsop-default-local-zones is fully supported (-04).
|
||||
It is possible to block zones or return an address for localhost.
|
||||
This is a very limited authoritative service. Defaults as in draft.
|
||||
draft-ietf-dnsop-resolver-priming(-00): can prime and can fallback to
|
||||
a safety belt list.
|
||||
draft-ietf-dnsop-dnssec-trust-anchor(-01): DS records can be configured
|
||||
as trust anchors. Also DNSKEYs are allowed, by the way.
|
||||
draft-ietf-dnsop-reflectors-are-evil: access control list for recursive
|
||||
service. In fact for all DNS service so cache snooping is halted.
|
||||
|
||||
Record type syntax support, extensive, from lib ldns.
|
||||
For these types only syntax and parsing support is needed.
|
||||
RFC 1034-1035: basic RR types.
|
||||
RFC 1183: RP, AFSDB, X25, ISDN, RT
|
||||
RFC 1706: NSAP
|
||||
RFC 2535: KEY, SIG, NXT: treated as unknown data, syntax is parsed (obsolete).
|
||||
2163: PX
|
||||
AAAA type
|
||||
1876: LOC type
|
||||
2782: SRV type
|
||||
2915: NAPTR type.
|
||||
2230: KX type.
|
||||
2538: CERT type.
|
||||
2672: DNAME type.
|
||||
OPT type
|
||||
3123: APL
|
||||
SSHFP type
|
||||
4025: IPSECKEY
|
||||
4033-4035: DS, RRSIG, NSEC, DNSKEY
|
||||
4701: DHCID
|
||||
5155: NSEC3, NSEC3PARAM
|
||||
4408: SPF
|
||||
|
||||
+46
-11
@@ -4,18 +4,16 @@ http://unbound.net
|
||||
|
||||
This software is under BSD license, see LICENSE for details.
|
||||
|
||||
* Download the latest version of this software from
|
||||
* Download the latest release version of this software from
|
||||
http://unbound.net
|
||||
or get a beta version from the svn repository at
|
||||
http://unbound.net/svn/
|
||||
|
||||
* Uses the following libraries;
|
||||
* ldns http://www.nlnetlabs.nl/ldns/ (BSD license)
|
||||
(required) can use tarball from source directory.
|
||||
* libevent http://www.monkey.org/~provos/libevent/ (BSD license)
|
||||
|
||||
* Create build environment
|
||||
* run libtoolize -c if config.sub is missing, or run glibtoolize.
|
||||
* autoreconf (autoheader && autoconf), if ./configure is missing.
|
||||
(optional) can use builtin alternative instead.
|
||||
|
||||
* Make and install: ./configure; make; make install
|
||||
* Use GNU make; default on linux, often called 'gmake' on BSD and Solaris.
|
||||
@@ -25,13 +23,15 @@ This software is under BSD license, see LICENSE for details.
|
||||
If detected on the system, it will dynamically link against it.
|
||||
* --with-libevent=/path/to/libevent
|
||||
Can be set to either the system install or the build directory.
|
||||
--with-libevent=no gives a builtin alternative implementation.
|
||||
libevent is useful when having many (thousands) of outgoing ports.
|
||||
This improves randomization and spoof resistance. For the default
|
||||
of 16 ports the builtin alternative works well.
|
||||
--with-libevent=no (default) gives a builtin alternative
|
||||
implementation. libevent is useful when having many (thousands)
|
||||
of outgoing ports. This improves randomization and spoof
|
||||
resistance. For the default of 16 ports the builtin alternative
|
||||
works well and is a little faster.
|
||||
* --without-pthreads
|
||||
This disables pthreads, and uses Solaris thr library or no threading.
|
||||
Without this option the pthreads library is detected automatically.
|
||||
This disables pthreads. Without this option the pthreads library
|
||||
is detected automatically. Use this option to disable threading
|
||||
altogether, or, on Solaris, also use --with(out)-solaris-threads.
|
||||
* --enable-static-exe
|
||||
This enables a debug option to statically link, against ldns and
|
||||
libevent libraries.
|
||||
@@ -43,6 +43,21 @@ This software is under BSD license, see LICENSE for details.
|
||||
The server periodically checks if the amount of memory used fits with
|
||||
the amount of memory it thinks it should be using, and reports
|
||||
memory usage in detail.
|
||||
* --with-conf-file=filename
|
||||
Set default location of config file,
|
||||
the default is /usr/local/etc/unbound/unbound.conf.
|
||||
* --with-pidfile=filename
|
||||
Set default location of pidfile,
|
||||
the default is /usr/local/etc/unbound/unbound.pid.
|
||||
* --with-run-dir=path
|
||||
Set default working directory,
|
||||
the default is /usr/local/etc/unbound.
|
||||
* --with-chroot-dir=path
|
||||
Set default chroot directory,
|
||||
the default is /usr/local/etc/unbound.
|
||||
* --with-username=user
|
||||
Set default user name to change to,
|
||||
the default is the "unbound" user.
|
||||
|
||||
* 'make test' attempts to run a series of tests, depending on the support
|
||||
programs that are installed.
|
||||
@@ -53,9 +68,29 @@ o If libevent is older (before 1.3c), unbound will exit instead of reload
|
||||
on sighup. On a restart 'did not exit gracefully last time' warning is
|
||||
printed. Perform ./configure --with-libevent=no or update libevent, rerun
|
||||
configure and recompile unbound to make sighup work correctly.
|
||||
It is strongly suggested to use the latest version of libevent (1.4 or more)
|
||||
on Solaris, because of fixes in the solaris event port code in libevent.
|
||||
o If you receive no answers deploying unbound on a private network.
|
||||
Check that the zone you configure as a stub zone is correct, listing working
|
||||
ip-addresses. Check that the NS data in the zone, those hostnames, are
|
||||
in fact those same, working, ip-addresses. Unbound will fetch the NS data
|
||||
from the zone and use that information in preference to the config
|
||||
information.
|
||||
o If you are not receiving the correct source IP address on replies (e.g.
|
||||
you are running a multihomed, anycast server), the interface-automatic
|
||||
option can be enabled to set socket options to achieve the correct
|
||||
source IP address on UDP replies. Listing all IP addresses explicitly in
|
||||
the config file is an alternative. The interface-automatic option uses
|
||||
non portable socket options, Linux and FreeBSD should work fine.
|
||||
o The warning 'openssl has no entropy, seeding with time', with chroot
|
||||
enabled, may be solved with a symbolic link to /dev/random from <chrootdir>.
|
||||
o On Solaris 5.10 some libtool packages from repositories do not work with
|
||||
gcc, showing errors gcc: unrecognized option `-KPIC'
|
||||
To solve this do ./configure libtool=./libtool [your options...].
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
o Unbound was written in portable C by Wouter Wijngaards (NLnet Labs).
|
||||
o Thanks to David Blacka and Matt Larson (Verisign) for the unbound-java
|
||||
prototype. Design and code from that prototype has been used to create
|
||||
this program. Such as the iterator state machine and the cache design.
|
||||
|
||||
+5
-3
@@ -1,11 +1,13 @@
|
||||
README.svn
|
||||
|
||||
For a svn checkout
|
||||
* Create build environment - see README.
|
||||
* possibly copy aclocal.m4 from your autoconf/libtool setup.
|
||||
* configure script, aclocal.m4, as well as yacc/lex output files are
|
||||
committed to the repository.
|
||||
* use --enable-debug flag for configure to enable dependency tracking and
|
||||
assertions, otherwise, use make clean; make after svn update.
|
||||
|
||||
* Note changes in the Changelog.
|
||||
* Every checking a postcommit hook is run
|
||||
* Every check-in a postcommit hook is run
|
||||
(the postcommit hook is in the svn/unbound/hooks directory).
|
||||
* generates commit email with your changes and comment.
|
||||
* compiles and runs the tests (with testcode/do-tests.sh).
|
||||
|
||||
+12
-1
@@ -3,7 +3,18 @@ README unbound tests
|
||||
There is a test setup for unbound. Use
|
||||
make test
|
||||
To make and run the tests. The results are summarized at the end.
|
||||
You can also run ./unittest by hand, if the system lacks other debug tools.
|
||||
|
||||
You need to have the following programs installed and in your PATH.
|
||||
* dig - from the bind-tools package. Used to send DNS queries.
|
||||
* splint (optional) - for lint test
|
||||
* doxygen (optional) - for doc completeness test
|
||||
* ldns-testns (optional) - from ldns examples. Used as DNS auth server.
|
||||
* xxd and nc (optional) - for (malformed) packet transmission.
|
||||
The optional programs are detected and can be omitted.
|
||||
|
||||
Without any support programs, unittest and testbound can still be used.
|
||||
(cd testdata; ../testcode/mini_tpkg.sh exe 02-unittest.tpkg)
|
||||
(cd testdata; ../testcode/mini_tpkg.sh exe 03-testbound.tpkg)
|
||||
|
||||
testdata/ contains the data for tests.
|
||||
testcode/ contains scripts and c code for the tests.
|
||||
|
||||
@@ -1,58 +1,77 @@
|
||||
TODO items.
|
||||
o use real entropy to make random (ID, port) numbers more random.
|
||||
o in production mode, do not free memory on exit. In debug mode, test leaks.
|
||||
o profile memory allocation, and if performance issues, use special memory
|
||||
allocator. For example, with caches per thread.
|
||||
o #define BIT_... different on bigendian and smallendian systems so that
|
||||
the htons on flags is not needed to send a message from the cache.
|
||||
o speed up pkt domain name decompression loop detection using counter perhaps.
|
||||
o detect OS/400 pthreads implementation that allows upgrading to writelock
|
||||
on pthreads rwlocks and use it to examine-rd before storing-wr rrset cache.
|
||||
TODO items. These are interesting todo items.
|
||||
o understand synthesized DNAMEs, so those TTL=0 packets are cached properly.
|
||||
o understand NSEC/NSEC3, aggressive negative caching, so that updates to
|
||||
NSEC/NSEC3 will result in proper negative responses.
|
||||
o fallback without EDNS if result is NOTIMPL, now only on FORMERR like in java.
|
||||
o scrubber has slow pkt_subdomain and pkt_strict_subdomain functions.
|
||||
o get serverselection algorithm out of local optimum.
|
||||
make subtargets to get rtt info for a couple of targets, like fetch-policy.
|
||||
or send out multiple queries to multiple servers.
|
||||
o donotqueryaddresses allow specification of subnets, use trie to store.
|
||||
extend default list to contain illegal ip4 and ip6 addresses.
|
||||
o memory profile; and if needed put serviced queries under qstate->region
|
||||
and special purpose region code, reuse blocks, shrink if too big.
|
||||
o check query, option to enforce presence of qdsection in noerror answers.
|
||||
o configuration option where port 53 is used for send and receive, no other
|
||||
ports are used.
|
||||
o NSEC/NSEC3 aggressive negative caching, so that updates to NSEC/NSEC3
|
||||
will result in proper negative responses.
|
||||
o (option) where port 53 is used for send and receive, no other ports are used.
|
||||
o (option) to not send replies to clients after a timeout of (say 5 secs) has
|
||||
passed, but keep task active for later retries by client.
|
||||
o private TTL feature
|
||||
o pretend-dnssec-unaware, and pretend-edns-unaware modes for debug/workshops.
|
||||
o (option) private TTL feature (always report TTL x in answers).
|
||||
o (option) pretend-dnssec-unaware, and pretend-edns-unaware modes for workshops.
|
||||
o delegpt use rbtree for ns-list, to avoid slowdown for very large NS sets.
|
||||
o be able to have different listen and query-to addresses to bind to,
|
||||
so you can listen to localhost and query-to to the internet.
|
||||
o reprime and refresh oft used data before timeout.
|
||||
o retain prime results in a overlaid roothints file.
|
||||
o store primed key data in a overlaid keyhints file (sort of like drafttimers).
|
||||
o (option) reprime and refresh oft used data before timeout.
|
||||
o (option) retain prime results in a overlaid roothints file.
|
||||
o (option) store primed key data in a overlaid keyhints file (sort of like drafttimers).
|
||||
o windows version, auto update feature, a query to check for the version.
|
||||
o autoreport of problems
|
||||
o logrotation, syslog
|
||||
o command the server with TSIG inband. get-config, clearcache,
|
||||
get stats, get memstats, get ..., reload, clear one zone from cache
|
||||
o watch for spoof nearmisses.
|
||||
o improve compression of DNS packets by first puttig uncompressible rrs, then
|
||||
compress to their rdata.
|
||||
o if one server is not responsive do not spend 75 secs on that server, but
|
||||
try other servers with lower rtt.
|
||||
o NSID rfc 5001 support.
|
||||
o timers rfc 5011 support.
|
||||
o Treat YXDOMAIN from a DNAME properly, in iterator (not throwaway), validator.
|
||||
o grab ports nonconsequtive and change the set after a while (change within
|
||||
a given range). Could be bad for OS if wrong port. unsure if it helps secure.
|
||||
o make timeout backoffs randomized (a couple percent random) to spread traffic.
|
||||
o inspect date on executable, then warn user in log if its more than 1 year.
|
||||
o proactively prime root, stubs and trust anchors, feature.
|
||||
o (option) proactively prime root, stubs and trust anchors, feature.
|
||||
early failure, faster on first query, but more traffic.
|
||||
o use privilege separation, to change privilege options during reload securely
|
||||
not needed.
|
||||
o check if for PowerDNS(2.9.21) CNAME in Answer section & rcode=NXDOMAIN needs
|
||||
to be fixed up to be rcode=NOERROR?
|
||||
o library add convenience functions for A, AAAA, PTR, getaddrinfo, libresolve.
|
||||
o library add function to validate input from app that is signed.
|
||||
o add dynamic-update requests (making a dynupd request) to libunbound api.
|
||||
o SIG(0) and TSIG.
|
||||
o support OPT record placement on recv anywhere in the additional section.
|
||||
o add local-file: config with authority features.
|
||||
o (option) to make local-data answers be secure for libunbound (default=no)
|
||||
o (option) to make chroot: copy all needed files into jail (or make jail)
|
||||
perhaps also print reminder to link /dev/random and sysloghack.
|
||||
o overhaul outside-network servicedquery to merge with udpwait and tcpwait,
|
||||
to make timers in servicedquery independent of udpwait queues.
|
||||
o check into rebinding ports for efficiency, configure time test.
|
||||
o EVP hardware crypto support.
|
||||
o option to ignore all inception and expiration dates for rrsigs.
|
||||
o option to use builtin ldns explicitly. Or stop shipping builtin tarball.
|
||||
o cleaner code; return and func statements on newline.
|
||||
o memcached module that sits before validator module; checks for memcached
|
||||
data (on local lan), stores recursion lookup. Provides one cache for
|
||||
multiple resolver machines, coherent reply content in anycast setup.
|
||||
o no openssl_add_all_algorithms, but only the ones necessary, less space.
|
||||
|
||||
*** Features features, for later
|
||||
* dTLS, TLS, look to need special port numbers, cert storage, recent libssl.
|
||||
* aggressive negative caching for NSEC, NSEC3.
|
||||
* multiple queries per question, server exploration, server selection.
|
||||
* support TSIG on queries, for validating resolver deployment.
|
||||
* retry-mode, where a bogus result triggers a retry-mode query, where a list
|
||||
of responses over a time interval is collected, and each is validated.
|
||||
or try in TCP mode. Do not 'try all servers several times', since we must
|
||||
not create packet storms with operator errors.
|
||||
* Windows port features
|
||||
o on windows version, implement that OS ancillary data capabilities for
|
||||
interface-automatic. IPPKTINFO, IP6PKTINFO for WSARecvMsg, WSASendMsg.
|
||||
o local-zone directive with authority service, full authority server
|
||||
is a non-goal.
|
||||
|
||||
o configure option to force use of builtin ldns tarball.
|
||||
o make so revoke bit keys cannot verify signatures
|
||||
o option to print UTC timestamps instead of epoch in seconds.
|
||||
o print querier IP address on high loglevels.
|
||||
|
||||
o infra and lame cache: easier size config (in Mb), show usage in graphs.
|
||||
o #226: unbound_control dump_requestlist
|
||||
o #227: unbound_control flush_stats
|
||||
o unbound_control get_stats_noreset
|
||||
o unbound_control forward [addr {addr}] | [off]
|
||||
o #231: unbound_checkconf options to output config file data.
|
||||
o config entry to denote that a zone is to be treated as unsigned (even if
|
||||
a DS exists to higher trust anchor).
|
||||
o see if we can use daemon(3), cleaner posix usage.
|
||||
o see if we can include the python bindings (contrib).
|
||||
o see if we can suport var:value (nospace) by parser.
|
||||
o remote control read ssl information while priviledged.
|
||||
o call initgroups(3) to drop secondary group permissions.
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
Specification for the unbound-control protocol.
|
||||
|
||||
Server listens on 953 TCP (localhost by default). Client connects,
|
||||
SSLv3 or TLSv1 connection setup (server selfsigned certificate,
|
||||
client has cert signed by server certificate).
|
||||
|
||||
Query and Response
|
||||
------------------
|
||||
Client sends
|
||||
UBCT [commandline] \n
|
||||
fixed string UBCT, then an ascii text line, with a command,
|
||||
some whitespace allowed. Line ends with '\n'.
|
||||
|
||||
Server executes command. And sends reply in ascii text over channel,
|
||||
closes the channel when done.
|
||||
in case of error the first line of the response is:
|
||||
error <descriptive text possible> \n
|
||||
or the remainder is data of the response, for many commands the
|
||||
response is 'ok\n'.
|
||||
|
||||
Queries and responses
|
||||
---------------------
|
||||
stop
|
||||
stops the server.
|
||||
reload
|
||||
reloads the config file, and flushes the cache.
|
||||
verbosity <new value>
|
||||
Change logging verbosity to new value.
|
||||
stats
|
||||
output is a list of [name]=[value] lines.
|
||||
clears the counters.
|
||||
dump_cache
|
||||
output is a text representation of the cache contents.
|
||||
data ends with a line 'EOF' before connection close.
|
||||
load_cache
|
||||
client sends cache contents (like from dump_cache), which is stored
|
||||
in the cache. end of data indicated with a line with 'EOF' on it.
|
||||
The data is sent after the query line.
|
||||
flush <name>
|
||||
flushes some information regarding the name from the cache.
|
||||
removes the A, AAAA, NS, SOA, CNAME, DNAME, MX, PTR, SRV, NAPTR types.
|
||||
Does not remove other types.
|
||||
flush_type <name> <RR type>
|
||||
removes rrtype entry from the cache.
|
||||
flush_zone <name>
|
||||
removes name and everything below that name from the cache.
|
||||
has to search through the cache item by item, so this is slow.
|
||||
lookup <name>
|
||||
see what servers would be queried for a lookup of the given name.
|
||||
local_zone_remove <name of local-zone entry>
|
||||
the local-zone entry is removed.
|
||||
All data from the local zone is also deleted.
|
||||
If it did not exist, nothing happens.
|
||||
local_zone <name of local zone> <type>
|
||||
As the config file entry. Adds new local zone or updates
|
||||
existing zone type.
|
||||
local_data_remove <name>
|
||||
Removes local-data (all types) name.
|
||||
local_data <resource record string>
|
||||
Add new local data record (on the rest of the line).
|
||||
local_data_add www.example.com. IN A 192.0.2.2
|
||||
if no local_zone exists for it; a transparent zone with the same
|
||||
name as the data is created.
|
||||
|
||||
|
||||
@@ -15,6 +15,17 @@ server:
|
||||
# verbosity number, 0 is least verbose. 1 is default.
|
||||
verbosity: 1
|
||||
|
||||
# print statistics to the log (for every thread) every N seconds.
|
||||
# Set to "" or 0 to disable. Default is disabled.
|
||||
# statistics-interval: 0
|
||||
|
||||
# enable cumulative statistics, without clearing them after printing.
|
||||
# statistics-cumulative: no
|
||||
|
||||
# enable extended statistics (query types, answer codes, status)
|
||||
# printed from unbound-control. default off, because of speed.
|
||||
# extended-statistics: no
|
||||
|
||||
# number of threads to create. 1 disables threading.
|
||||
# num-threads: 1
|
||||
|
||||
@@ -26,6 +37,10 @@ server:
|
||||
# interface: 192.0.2.153
|
||||
# interface: 192.0.2.154
|
||||
# interface: 2001:DB8::5
|
||||
|
||||
# enable this feature to copy the source address of queries to reply.
|
||||
# Socket options are not supported on all platforms. experimental.
|
||||
# interface-automatic: no
|
||||
|
||||
# port to answer queries from
|
||||
# port: 53
|
||||
@@ -37,16 +52,20 @@ server:
|
||||
# outgoing-interface: 2001:DB8::5
|
||||
# outgoing-interface: 2001:DB8::6
|
||||
|
||||
# unbound needs to send packets to authoritative nameservers.
|
||||
# it uses a range of ports for that.
|
||||
# the start number of the port range
|
||||
# outgoing-port: 1053
|
||||
# number of ports to allocate per thread, determines the size of the
|
||||
# port range that can be open simultaneously.
|
||||
# outgoing-range: 256
|
||||
|
||||
# permit unbound to use this port number or port range for
|
||||
# making outgoing queries, using an outgoing interface.
|
||||
# outgoing-port-permit: 32768
|
||||
|
||||
# number of port to allocate per thread, determines the size of the
|
||||
# port range. A larger port range gives more resistance to certain
|
||||
# spoof attacks, as it gets harder to guess which port is used.
|
||||
# But also takes more system resources (for open sockets).
|
||||
# outgoing-range: 16
|
||||
# deny unbound the use this of port number or port range for
|
||||
# making outgoing queries, using an outgoing interface.
|
||||
# Use this to make sure unbound does not grab a UDP port that some
|
||||
# other server on this computer needs. The default is to avoid
|
||||
# IANA-assigned port numbers.
|
||||
# outgoing-port-avoid: "3200-3208"
|
||||
|
||||
# number of outgoing simultaneous tcp buffers to hold per thread.
|
||||
# outgoing-num-tcp: 10
|
||||
@@ -59,8 +78,8 @@ server:
|
||||
# msg-buffer-size: 65552
|
||||
|
||||
# the amount of memory to use for the message cache.
|
||||
# in bytes. default is 4 Mb
|
||||
# msg-cache-size: 4194304
|
||||
# plain value in bytes or you can append k, m or G. default is "4Mb".
|
||||
# msg-cache-size: 4m
|
||||
|
||||
# the number of slabs to use for the message cache.
|
||||
# the number of slabs must be a power of 2.
|
||||
@@ -70,9 +89,12 @@ server:
|
||||
# the number of queries that a thread gets to service.
|
||||
# num-queries-per-thread: 1024
|
||||
|
||||
# if very busy, 50% queries run to completion, 50% get timeout in msec
|
||||
# jostle-timeout: 200
|
||||
|
||||
# the amount of memory to use for the RRset cache.
|
||||
# in bytes. default is 4 Mb
|
||||
# rrset-cache-size: 4194304
|
||||
# plain value in bytes or you can append k, m or G. default is "4Mb".
|
||||
# rrset-cache-size: 4m
|
||||
|
||||
# the number of slabs to use for the RRset cache.
|
||||
# the number of slabs must be a power of 2.
|
||||
@@ -81,7 +103,7 @@ server:
|
||||
|
||||
# the time to live (TTL) value cap for RRsets and messages in the
|
||||
# cache. Items are not cached for longer. In seconds.
|
||||
# cache-max-ttl: 864000
|
||||
# cache-max-ttl: 86400
|
||||
|
||||
# the time to live (TTL) value for cached roundtrip times and
|
||||
# EDNS version information for hosts. In seconds.
|
||||
@@ -99,7 +121,7 @@ server:
|
||||
# infra-cache-numhosts: 10000
|
||||
|
||||
# the maximum size of the lame zones cached per host. in bytes.
|
||||
# infra-cache-lame-size: 10240
|
||||
# infra-cache-lame-size: 10k
|
||||
|
||||
# Enable IPv4, "yes" or "no".
|
||||
# do-ip4: yes
|
||||
@@ -113,19 +135,54 @@ server:
|
||||
# Enable TCP, "yes" or "no".
|
||||
# do-tcp: yes
|
||||
|
||||
# Detach from the terminal, run in background, "yes" or "no".
|
||||
# do-daemonize: yes
|
||||
|
||||
# control which clients are allowed to make (recursive) queries
|
||||
# to this server. Specify classless netblocks with /size and action.
|
||||
# By default everything is refused, except for localhost.
|
||||
# Choose deny (drop message), refuse (polite error reply),
|
||||
# allow (recursive ok), allow_snoop (recursive and nonrecursive ok)
|
||||
# access-control: 0.0.0.0/0 refuse
|
||||
# access-control: 127.0.0.0/8 allow
|
||||
# access-control: ::0/0 refuse
|
||||
# access-control: ::1 allow
|
||||
# access-control: ::ffff:127.0.0.1 allow
|
||||
|
||||
# if given, a chroot(2) is done to the given directory.
|
||||
# i.e. you can chroot to the working directory, for example,
|
||||
# for extra security, but make sure all files are in that directory.
|
||||
# If you give "" no chroot is performed.
|
||||
# chroot: "/etc/unbound"
|
||||
#
|
||||
# If chroot is enabled, you should pass the configfile (from the
|
||||
# commandline) as a full path from the original root. After the
|
||||
# chroot has been performed the now defunct portion of the config
|
||||
# file path is removed to be able to reread the config after a reload.
|
||||
#
|
||||
# All other file paths (working dir, logfile, roothints, and
|
||||
# key files) can be specified in several ways:
|
||||
# o as an absolute path relative to the new root.
|
||||
# o as a relative path to the working directory.
|
||||
# o as an absolute path relative to the original root.
|
||||
# In the last case the path is adjusted to remove the unused portion.
|
||||
#
|
||||
# The pid file can be absolute and outside of the chroot, it is
|
||||
# written just prior to performing the chroot and dropping permissions.
|
||||
#
|
||||
# Additionally, unbound may need to access /dev/random (for entropy).
|
||||
# How to do this is specific to your OS.
|
||||
#
|
||||
# If you give "" no chroot is performed. The path must not end in a /.
|
||||
# chroot: "@UNBOUND_CHROOT_DIR@"
|
||||
|
||||
# if given, user privileges are dropped (after binding port),
|
||||
# and the given username is assumed. Default is user "unbound".
|
||||
# If you give "" no priviliges are dropped.
|
||||
# username: "unbound"
|
||||
# If you give "" no privileges are dropped.
|
||||
# username: "@UNBOUND_USERNAME@"
|
||||
|
||||
# the working directory.
|
||||
# directory: "/etc/unbound"
|
||||
# the working directory. The relative files in this config are
|
||||
# relative to this directory. If you give "" the working directory
|
||||
# is not changed.
|
||||
# directory: "@UNBOUND_RUN_DIR@"
|
||||
|
||||
# the log file, "" means log to stderr.
|
||||
# Use of this option sets use-syslog to "no".
|
||||
@@ -135,8 +192,8 @@ server:
|
||||
# log to, with identity "unbound". If yes, it overrides the logfile.
|
||||
# use-syslog: yes
|
||||
|
||||
# the pid file.
|
||||
# pidfile: "/etc/unbound/unbound.pid"
|
||||
# the pid file. Can be an absolute path outside of chroot/work dir.
|
||||
# pidfile: "@UNBOUND_PIDFILE@"
|
||||
|
||||
# file to read root hints from.
|
||||
# get one from ftp://FTP.INTERNIC.NET/domain/named.cache
|
||||
@@ -178,7 +235,40 @@ server:
|
||||
# trigger insecure mode for that zone (like without a trustanchor).
|
||||
# Default on, which insists on dnssec data for trust-anchored zones.
|
||||
# harden-dnssec-stripped: yes
|
||||
|
||||
# Harden the referral path by performing additional queries for
|
||||
# infrastructure data. Validates the replies (if possible).
|
||||
# Default off, because the lookups burden the server. Experimental
|
||||
# implementation of draft-wijngaards-dnsext-resolver-side-mitigation.
|
||||
# harden-referral-path: no
|
||||
|
||||
# Use 0x20-encoded random bits in the query to foil spoof attempts.
|
||||
# This feature is an experimental implementation of draft dns-0x20.
|
||||
# use-caps-for-id: no
|
||||
|
||||
# Enforce privacy of these addresses. Strips them away from answers.
|
||||
# It may cause DNSSEC validation to additionally mark it as bogus.
|
||||
# Protects against 'DNS Rebinding' (uses browser as network proxy).
|
||||
# Only 'private-domain' and 'local-data' names are allowed to have
|
||||
# these private addresses. No default.
|
||||
# private-address: 10.0.0.0/8
|
||||
# private-address: 172.16.0.0/12
|
||||
# private-address: 192.168.0.0/16
|
||||
# private-address: 192.254.0.0/16
|
||||
# private-address: fd00::/8
|
||||
# private-address: fe80::/10
|
||||
|
||||
# Allow the domain (and its subdomains) to contain private addresses.
|
||||
# local-data statements are allowed to contain private addresses too.
|
||||
# private-domain: "example.com"
|
||||
|
||||
# If nonzero, unwanted replies are not only reported in statistics,
|
||||
# but also a running total is kept per thread. If it reaches the
|
||||
# threshold, a warning is printed and a defensive action is taken,
|
||||
# the cache is cleared to flush potential poison out of it.
|
||||
# A suggested value is 10000000, the default is 0 (turned off).
|
||||
# unwanted-reply-threshold: 0
|
||||
|
||||
# Do not query the following addresses. No DNS queries are sent there.
|
||||
# List one address per entry. List classless netblocks with /size,
|
||||
# do-not-query-address: 127.0.0.1/8
|
||||
@@ -192,6 +282,11 @@ server:
|
||||
# separated by spaces. "iterator" or "validator iterator"
|
||||
# module-config: "validator iterator"
|
||||
|
||||
# File with DLV trusted keys. Same format as trust-anchor-file.
|
||||
# There can be only one DLV configured, it is trusted from root down.
|
||||
# Download https://secure.isc.org/ops/dlv/dlv.isc.org.key
|
||||
# dlv-anchor-file: "dlv.isc.org.key"
|
||||
|
||||
# File with trusted keys for validation. Specify more than one file
|
||||
# with several entries, one file per entry.
|
||||
# Zone file format, with DS and DNSKEY entries.
|
||||
@@ -208,7 +303,7 @@ server:
|
||||
# but has a different file format. Format is BIND-9 style format,
|
||||
# the trusted-keys { name flag proto algo "key"; }; clauses are read.
|
||||
# trusted-keys-file: ""
|
||||
|
||||
|
||||
# Override the date for validation with a specific fixed date.
|
||||
# Do not set this unless you are debugging signature inception
|
||||
# and expiration. "" or "0" turns the feature off.
|
||||
@@ -216,7 +311,7 @@ server:
|
||||
|
||||
# The time to live for bogus data, rrsets and messages. This avoids
|
||||
# some of the revalidation, until the time interval expires. in secs.
|
||||
# val-bogus-ttl: 900
|
||||
# val-bogus-ttl: 60
|
||||
|
||||
# Should additional section of secure message also be kept clean of
|
||||
# unsecure data. Useful to shield the users of this validator from
|
||||
@@ -238,22 +333,88 @@ server:
|
||||
# val-nsec3-keysize-iterations: "1024 150 2048 500 4096 2500"
|
||||
|
||||
# the amount of memory to use for the key cache.
|
||||
# in bytes. default is 4 Mb
|
||||
# key-cache-size: 4194304
|
||||
# plain value in bytes or you can append k, m or G. default is "4Mb".
|
||||
# key-cache-size: 4m
|
||||
|
||||
# the number of slabs to use for the key cache.
|
||||
# the number of slabs must be a power of 2.
|
||||
# more slabs reduce lock contention, but fragment memory usage.
|
||||
# key-cache-slabs: 4
|
||||
|
||||
# the amount of memory to use for the negative cache (used for DLV).
|
||||
# plain value in bytes or you can append k, m or G. default is "1Mb".
|
||||
# neg-cache-size: 1m
|
||||
|
||||
# a number of locally served zones can be configured.
|
||||
# local-zone: <zone> <type>
|
||||
# local-data: "<resource record string>"
|
||||
# o deny serves local data (if any), else, drops queries.
|
||||
# o refuse serves local data (if any), else, replies with error.
|
||||
# o static serves local data, else, nxdomain or nodata answer.
|
||||
# o transparent gives local data, but resolves normally for other names
|
||||
# o redirect serves the zone data for any subdomain in the zone.
|
||||
# o nodefault can be used to normally resolve AS112 zones.
|
||||
#
|
||||
# defaults are localhost address, reverse for 127.0.0.1 and ::1
|
||||
# and nxdomain for AS112 zones. If you configure one of these zones
|
||||
# the default content is omitted, or you can omit it with 'nodefault'.
|
||||
#
|
||||
# If you configure local-data without specifying local-zone, by
|
||||
# default a transparent local-zone is created for the data.
|
||||
#
|
||||
# You can add locally served data with
|
||||
# local-zone: "local." static
|
||||
# local-data: "mycomputer.local. IN A 192.0.2.51"
|
||||
# local-data: 'mytext.local TXT "content of text record"'
|
||||
#
|
||||
# You can override certain queries with
|
||||
# local-data: "adserver.example.com A 127.0.0.1"
|
||||
#
|
||||
# You can redirect a domain to a fixed address with
|
||||
# (this makes example.com, www.example.com, etc, all go to 192.0.2.3)
|
||||
# local-zone: "example.com" redirect
|
||||
# local-data: "example.com A 192.0.2.3"
|
||||
#
|
||||
# Shorthand to make PTR records, "IPv4 name" or "IPv6 name".
|
||||
# You can also add PTR records using local-data directly, but then
|
||||
# you need to do the reverse notation yourself.
|
||||
# local-data-ptr: "192.0.2.3 www.example.com"
|
||||
|
||||
# Remote control config section.
|
||||
remote-control:
|
||||
# Enable remote control with unbound-control(8) here.
|
||||
# set up the keys and certificates with unbound-control-setup.
|
||||
# control-enable: no
|
||||
|
||||
# what interfaces are listened to for remote control.
|
||||
# give 0.0.0.0 and ::0 to listen to all interfaces.
|
||||
# control-interface: 127.0.0.1
|
||||
# control-interface: ::1
|
||||
|
||||
# port number for remote control operations.
|
||||
# control-port: 953
|
||||
|
||||
# unbound server key file.
|
||||
# server-key-file: "@UNBOUND_RUN_DIR@/unbound_server.key"
|
||||
|
||||
# unbound server certificate file.
|
||||
# server-cert-file: "@UNBOUND_RUN_DIR@/unbound_server.pem"
|
||||
|
||||
# unbound-control key file.
|
||||
# control-key-file: "@UNBOUND_RUN_DIR@/unbound_control.key"
|
||||
|
||||
# unbound-control certificate file.
|
||||
# control-cert-file: "@UNBOUND_RUN_DIR@/unbound_control.pem"
|
||||
|
||||
# Stub zones.
|
||||
# Create entries like below, to make all queries for 'example.com' and
|
||||
# 'example.org' go to the given list of nameservers. list zero or more
|
||||
# nameservers by hostname or by ipaddress.
|
||||
# nameservers by hostname or by ipaddress. If you set stub-prime to yes,
|
||||
# the list is treated as priming hints (default is no).
|
||||
# stub-zone:
|
||||
# name: "example.com"
|
||||
# stub-addr: 192.0.2.68
|
||||
# stub-prime: "no"
|
||||
# stub-zone:
|
||||
# name: "example.org"
|
||||
# stub-host: ns.example.com.
|
||||
@@ -0,0 +1,335 @@
|
||||
.TH "libunbound" "3" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" libunbound.3 -- unbound library functions manual
|
||||
.\"
|
||||
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
.\"
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.SH "NAME"
|
||||
.LP
|
||||
.B libunbound,
|
||||
.B unbound.h,
|
||||
.B ub_ctx,
|
||||
.B ub_result,
|
||||
.B ub_callback_t,
|
||||
.B ub_ctx_create,
|
||||
.B ub_ctx_delete,
|
||||
.B ub_ctx_set_option,
|
||||
.B ub_ctx_config,
|
||||
.B ub_ctx_set_fwd,
|
||||
.B ub_ctx_resolvconf,
|
||||
.B ub_ctx_hosts,
|
||||
.B ub_ctx_add_ta,
|
||||
.B ub_ctx_add_ta_file,
|
||||
.B ub_ctx_trustedkeys,
|
||||
.B ub_ctx_debugout,
|
||||
.B ub_ctx_debuglevel,
|
||||
.B ub_ctx_async,
|
||||
.B ub_poll,
|
||||
.B ub_wait,
|
||||
.B ub_fd,
|
||||
.B ub_process,
|
||||
.B ub_resolve,
|
||||
.B ub_resolve_async,
|
||||
.B ub_cancel,
|
||||
.B ub_resolve_free,
|
||||
.B ub_strerror
|
||||
\- Unbound DNS validating resolver @version@ functions.
|
||||
.SH "SYNOPSIS"
|
||||
.LP
|
||||
.B #include <unbound.h>
|
||||
.LP
|
||||
\fIstruct ub_ctx *\fR
|
||||
\fBub_ctx_create\fR(\fIvoid\fR);
|
||||
.LP
|
||||
\fIvoid\fR
|
||||
\fBub_ctx_delete\fR(\fIstruct ub_ctx*\fR ctx);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_set_option\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR opt, \fIchar*\fR val);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_config\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_set_fwd\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR addr);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_resolvconf\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_hosts\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_add_ta\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR ta);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_add_ta_file\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_trustedkeys\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_debugout\fR(\fIstruct ub_ctx*\fR ctx, \fIFILE*\fR out);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_debuglevel\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR d);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_ctx_async\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR dothread);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_poll\fR(\fIstruct ub_ctx*\fR ctx);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_wait\fR(\fIstruct ub_ctx*\fR ctx);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_fd\fR(\fIstruct ub_ctx*\fR ctx);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_process\fR(\fIstruct ub_ctx*\fR ctx);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_resolve\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR name,
|
||||
.br
|
||||
\fIint\fR rrtype, \fIint\fR rrclass, \fIint*\fR secure,
|
||||
.br
|
||||
\fIint*\fR data, \fIstruct ub_result**\fR result);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_resolve_async\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR name,
|
||||
.br
|
||||
\fIint\fR rrtype, \fIint\fR rrclass, \fIvoid*\fR mydata,
|
||||
.br
|
||||
\fIub_callback_t\fR callback, \fIint*\fR async_id);
|
||||
.LP
|
||||
\fIint\fR
|
||||
\fBub_cancel\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR async_id);
|
||||
.LP
|
||||
\fIvoid\fR
|
||||
\fBub_resolve_free\fR(\fIstruct ub_result*\fR result);
|
||||
.LP
|
||||
\fIconst char *\fR
|
||||
\fBub_strerror\fR(\fIint\fR err);
|
||||
.SH "DESCRIPTION"
|
||||
.LP
|
||||
.B Unbound
|
||||
is an implementation of a DNS resolver, that does caching and
|
||||
DNSSEC validation. This is the library API, for using the \-lunbound library.
|
||||
The server daemon is described in \fIunbound\fR(8).
|
||||
The library can be used to convert hostnames to ip addresses, and back,
|
||||
and obtain other information from the DNS. The library performs public\-key
|
||||
validation of results with DNSSEC.
|
||||
.P
|
||||
The library uses a variable of type \fIstruct ub_ctx\fR to keep context
|
||||
between calls. The user must maintain it, creating it with
|
||||
.B ub_ctx_create
|
||||
and deleting it with
|
||||
.B ub_ctx_delete\fR.
|
||||
It can be created and deleted at any time. Creating it anew removes any
|
||||
previous configuration (such as trusted keys) and clears any cached results.
|
||||
.P
|
||||
The functions are thread\-safe, and a context an be used in a threaded (as
|
||||
well as in a non\-threaded) environment. Also resolution (and validation)
|
||||
can be performed blocking and non\-blocking (also called asynchronous).
|
||||
The async method returns from the call immediately, so that processing
|
||||
can go on, while the results become available later.
|
||||
.P
|
||||
The functions are discussed in turn below.
|
||||
.SH "FUNCTIONS"
|
||||
.TP
|
||||
.B ub_ctx_create
|
||||
Create a new context, initialised with defaults.
|
||||
The information from /etc/resolv.conf and /etc/hosts is not utilised
|
||||
by default. Use
|
||||
.B ub_ctx_resolvconf
|
||||
and
|
||||
.B ub_ctx_hosts
|
||||
to read them.
|
||||
.TP
|
||||
.B ub_ctx_delete
|
||||
Delete validation context and free associated resources.
|
||||
Outstanding async queries are killed and callbacks are not called for them.
|
||||
.TP
|
||||
.B ub_ctx_set_option
|
||||
A power\-user interface that lets you specify one of the options from the
|
||||
config file format, see \fIunbound.conf\fR(5). Not all options are
|
||||
relevant. For some specific options, such as adding trust anchors, special
|
||||
routines exist. Pass the option name with the trailing ':'.
|
||||
.TP
|
||||
.B ub_ctx_config
|
||||
A power\-user interface that lets you specify an unbound config file, see
|
||||
\fIunbound.conf\fR(5), which is read for configuration. Not all options are
|
||||
relevant. For some specific options, such as adding trust anchors, special
|
||||
routines exist.
|
||||
.TP
|
||||
.B ub_ctx_set_fwd
|
||||
Set machine to forward DNS queries to, the caching resolver to use.
|
||||
IP4 or IP6 address. Forwards all DNS requests to that machine, which
|
||||
is expected to run a recursive resolver. If the proxy is not
|
||||
DNSSEC capable, validation may fail. Can be called several times, in
|
||||
that case the addresses are used as backup servers.
|
||||
At this time it is only possible to set configuration before the
|
||||
first resolve is done.
|
||||
.TP
|
||||
.B ub_ctx_resolvconf
|
||||
Read list of nameservers to use from the filename given.
|
||||
Usually "/etc/resolv.conf". Uses those nameservers as caching proxies.
|
||||
If they do not support DNSSEC, validation may fail.
|
||||
Only nameservers are picked up, the searchdomain, ndots and other
|
||||
settings from \fIresolv.conf\fR(5) are ignored.
|
||||
If fname NULL is passed, "/etc/resolv.conf" is used.
|
||||
At this time it is only possible to set configuration before the
|
||||
first resolve is done.
|
||||
.TP
|
||||
.B ub_ctx_hosts
|
||||
Read list of hosts from the filename given.
|
||||
Usually "/etc/hosts". When queried for, these addresses are not marked
|
||||
DNSSEC secure. If fname NULL is passed, "/etc/hosts" is used.
|
||||
At this time it is only possible to set configuration before the
|
||||
first resolve is done.
|
||||
.TP
|
||||
.B
|
||||
ub_ctx_add_ta
|
||||
Add a trust anchor to the given context.
|
||||
At this time it is only possible to add trusted keys before the
|
||||
first resolve is done.
|
||||
The format is a string, similar to the zone-file format,
|
||||
[domainname] [type] [rdata contents]. Both DS and DNSKEY records are accepted.
|
||||
.TP
|
||||
.B ub_ctx_add_ta_file
|
||||
Add trust anchors to the given context.
|
||||
Pass name of a file with DS and DNSKEY records in zone file format.
|
||||
At this time it is only possible to add trusted keys before the
|
||||
first resolve is done.
|
||||
.TP
|
||||
.B ub_ctx_trustedkeys
|
||||
Add trust anchors to the given context.
|
||||
Pass the name of a bind-style config file with trusted-keys{}.
|
||||
At this time it is only possible to add trusted keys before the
|
||||
first resolve is done.
|
||||
.TP
|
||||
.B ub_ctx_debugout
|
||||
Set debug and error log output to the given stream. Pass NULL to disable
|
||||
output. Default is stderr. File-names or using syslog can be enabled
|
||||
using config options, this routine is for using your own stream.
|
||||
.TP
|
||||
.B ub_ctx_debuglevel
|
||||
Set debug verbosity for the context. Output is directed to stderr.
|
||||
Higher debug level gives more output.
|
||||
.TP
|
||||
.B ub_ctx_async
|
||||
Set a context behaviour for asynchronous action.
|
||||
if set to true, enables threading and a call to
|
||||
.B ub_resolve_async
|
||||
creates a thread to handle work in the background.
|
||||
If false, a process is forked to handle work in the background.
|
||||
Changes to this setting after
|
||||
.B ub_resolve_async
|
||||
calls have been made have no effect (delete and re\-create the context
|
||||
to change).
|
||||
.TP
|
||||
.B ub_poll
|
||||
Poll a context to see if it has any new results.
|
||||
Do not poll in a loop, instead extract the fd below to poll for readiness,
|
||||
and then check, or wait using the wait routine.
|
||||
Returns 0 if nothing to read, or nonzero if a result is available.
|
||||
If nonzero, call
|
||||
.B ub_process
|
||||
to do callbacks.
|
||||
.TP
|
||||
.B ub_wait
|
||||
Wait for a context to finish with results. Calls
|
||||
.B ub_process
|
||||
after the wait for you. After the wait, there are no more outstanding
|
||||
asynchronous queries.
|
||||
.TP
|
||||
.B ub_fd
|
||||
Get file descriptor. Wait for it to become readable, at this point
|
||||
answers are returned from the asynchronous validating resolver.
|
||||
Then call the \fBub_process\fR to continue processing.
|
||||
.TP
|
||||
.B ub_process
|
||||
Call this routine to continue processing results from the validating
|
||||
resolver (when the fd becomes readable).
|
||||
Will perform necessary callbacks.
|
||||
.TP
|
||||
.B ub_resolve
|
||||
Perform resolution and validation of the target name.
|
||||
The name is a domain name in a zero terminated text string.
|
||||
The rrtype and rrclass are DNS type and class codes.
|
||||
The value secure returns true if the answer validated securely.
|
||||
The value data returns true if there was data.
|
||||
The result structure is newly allocated with the resulting data.
|
||||
.TP
|
||||
.B ub_resolve_async
|
||||
Perform asynchronous resolution and validation of the target name.
|
||||
Arguments mean the same as for \fBub_resolve\fR except no
|
||||
data is returned immediately, instead a callback is called later.
|
||||
The callback receives a copy of the mydata pointer, that you can use to pass
|
||||
information to the callback. The callback type is a function pointer to
|
||||
a function declared as
|
||||
.IP
|
||||
void my_callback_function(void* my_arg, int err,
|
||||
.br
|
||||
struct ub_result* result);
|
||||
.IP
|
||||
The async_id is returned so you can (at your option) decide to track it
|
||||
and cancel the request if needed.
|
||||
.TP
|
||||
.B ub_cancel
|
||||
Cancel an async query in progress.
|
||||
.TP
|
||||
.B ub_resolve_free
|
||||
Free struct ub_result contents after use.
|
||||
.TP
|
||||
.B ub_strerror
|
||||
Convert error value from one of the unbound library functions
|
||||
to a human readable string.
|
||||
.SH "RESULT DATA STRUCTURE"
|
||||
.LP
|
||||
The result of the DNS resolution and validation is returned as
|
||||
\fIstruct ub_result\fR. The result structure contains the following entries.
|
||||
.P
|
||||
.nf
|
||||
struct ub_result {
|
||||
char* qname; /* text string, original question */
|
||||
int qtype; /* type code asked for */
|
||||
int qclass; /* class code asked for */
|
||||
char** data; /* array of rdata items, NULL terminated*/
|
||||
int* len; /* array with lengths of rdata items */
|
||||
char* canonname; /* canonical name of result */
|
||||
int rcode; /* additional error code in case of no data */
|
||||
void* answer_packet; /* full network format answer packet */
|
||||
int answer_len; /* length of packet in octets */
|
||||
int havedata; /* true if there is data */
|
||||
int nxdomain; /* true if nodata because name does not exist */
|
||||
int secure; /* true if result is secure */
|
||||
int bogus; /* true if a security failure happened */
|
||||
};
|
||||
.fi
|
||||
.P
|
||||
If both secure and bogus are false, security was not enabled for the
|
||||
domain of the query.
|
||||
.SH "RETURN VALUES"
|
||||
Many routines return an error code. The value 0 (zero) denotes no error
|
||||
happened. Other values can be passed to
|
||||
.B ub_strerror
|
||||
to obtain a readable error string.
|
||||
.B ub_strerror
|
||||
returns a zero terminated string.
|
||||
.B ub_ctx_create
|
||||
returns NULL on an error (a malloc failure).
|
||||
.B ub_poll
|
||||
returns true if some information may be available, false otherwise.
|
||||
.B ub_fd
|
||||
returns a file descriptor or -1 on error.
|
||||
.SH "SEE ALSO"
|
||||
\fIunbound.conf\fR(5),
|
||||
\fIunbound\fR(8).
|
||||
.SH "AUTHORS"
|
||||
.B Unbound
|
||||
developers are mentioned in the CREDITS file in the distribution.
|
||||
@@ -1,320 +0,0 @@
|
||||
Plan for Unbound.
|
||||
|
||||
Split into a set of boxes. Every box will take about 3 weeks to a month
|
||||
to complete. The first set of of boxes (approx 5 months) will need coding
|
||||
by a limited set of people. But after every box, a 0.x release is done,
|
||||
which is then tested and code review is done.
|
||||
|
||||
Every box:
|
||||
* implement the features
|
||||
* documentation of those features
|
||||
* test-framework for the new features
|
||||
* tests for the new features
|
||||
* speed test of this stage
|
||||
* release of 0.x version (0.x for development only)
|
||||
* a teleconference(jabber) held to discuss.
|
||||
* code review internal couple of days, external a week or so,
|
||||
while we continue the next box.
|
||||
|
||||
Roughly the boxes are as follows:
|
||||
0.0 initial setup - results in network code that forwards queries
|
||||
and returns the reply (no cache), but also testbed, svn, maillist.
|
||||
One query at a time (nonblocking IO though).
|
||||
0.1 threads - results in threaded forwarder
|
||||
0.2 LRU hashtable, results in basic caching forwarder (no DNS parse)
|
||||
0.3 First functionality - results in caching forwarder (with DNS parse,
|
||||
query compare, RR specific updates).
|
||||
0.4 Basic resolver - module layout, iterator module, scrubber module,
|
||||
results in resolver that can service multiple queries per thread.
|
||||
This stage takes longer, due to complexity in the iterator module.
|
||||
Twice as long; one box for module layout, one box for iterator module.
|
||||
0.5 Validator - validator module.
|
||||
0.6 Bigger and better - Operational useful features (config, log, memory)
|
||||
0.7 Put to a limited audience.
|
||||
gamma/alpha core functionality test release, to a small audience.
|
||||
partial functionality. For more extensive use and testing.
|
||||
0.8 Local zones feature - localzones stubzones fwdzones, no leak rfc1918.
|
||||
views support; for selective recursive service.
|
||||
0.9 Library use - resolver validator lib (and test apps)
|
||||
0.10 Corner cases - be able to resolve in the wild. Run fuzzers.
|
||||
Run as many tests as we can think of.
|
||||
Go through logs and check for long, unresolved cases
|
||||
Use profiler.
|
||||
0.11 Beta release. Run shadow for a resolver in production for several
|
||||
weeks.
|
||||
0.12 Features features
|
||||
aggressive negative caching for NSEC, NSEC3.
|
||||
multiple queries per question, server exploration, server selection.
|
||||
option to use real entropy for randomness (mix it in once in a while).
|
||||
check query, option to enforce qdsection checking (forgery-resilience).
|
||||
NSID support.
|
||||
Be able to prime roots using several queries (only NS on first).
|
||||
|
||||
For boxes 0.5-1.0 the planning is to be revised, at the 0.5 stage external
|
||||
coders are welcome. Since the project is bigger, there is room for them.
|
||||
|
||||
This is a summary of the items. Below more detailed work items are spelled
|
||||
out with a (tentative) directory structure for the project.
|
||||
|
||||
|
||||
Styleguide:
|
||||
* write working stuff. (it starts to work with no features)
|
||||
* write tests immediately for every function, every feature.
|
||||
* document as you go. (doxygen comments, manpages and readme).
|
||||
* copyright every file BSD. comments every file. clean coding in C.
|
||||
* every day discuss state of the nation for 10 minutes.
|
||||
|
||||
*** Initial setup
|
||||
* setup svn repo. Makefile with automatic dependencies and configure script.
|
||||
* link with ldns.
|
||||
* listen_dnsport and outside_network services, (unit) tests for them.
|
||||
* use libevent to listen on fds.
|
||||
* setup test infrastructure (tpkg on checkin; testbed on labs test machines).
|
||||
* daemon version that forwards queries. (listen, send) Tests for it.
|
||||
* test by having the outside_net service grab answers from a
|
||||
file instead of network, file of id priority answerpacket.
|
||||
and what query to give this answer to, highprio matches first.
|
||||
|
||||
*** Threads
|
||||
* first simple config file reading/writing and tests on config file.
|
||||
(config option is forwarder: yes/no. Cache size. That sort of thing.)
|
||||
(very simple format)
|
||||
* First simple logging (to a file).
|
||||
* Threads
|
||||
* check if pthread lib is the one to use (sys specific is faster?).
|
||||
* make config option to have threads.
|
||||
* alloc threadable.
|
||||
* locks.c
|
||||
* Tests with and without threads.
|
||||
* alloc_service. Tests for alloc service (unit tests in internal structs).
|
||||
* threading for the network services.
|
||||
* Make sure threading/libevent starts working on all test machines.
|
||||
Use configure to turn off threading/libevent/...
|
||||
-- use libevent packaged together if not in system.
|
||||
-- maybe also for pthreads/...
|
||||
* threaded forwarder version.
|
||||
* speed test of threaded version.
|
||||
|
||||
*** LRU hashtable.
|
||||
* mini msg/reply structure for LRU hashtable test, simple replay format.
|
||||
* hashtable+LRU structure. Tests on structure.
|
||||
* tests on enter/remove, finding items.
|
||||
* tests on LRU movements.
|
||||
* Test on speed of finding items.
|
||||
* slabbed hashtable+LRU structure.
|
||||
* Test locking; perhaps by having sleeps in some threads to force
|
||||
locks to contend. helgrind.
|
||||
* daemon upgraded to be a caching forwarder. So it stores all in cache.
|
||||
Replies from cache. Tests on fake-caching forwarder functionality.
|
||||
* timeout of data test
|
||||
* finding data in cache.
|
||||
* finding data not in cache.
|
||||
* lru falloff of data.
|
||||
* Speed test of fake-caching forwarder.
|
||||
|
||||
*** First functionality
|
||||
* implement dname type and unit tests on it. (all corner cases, random cases)
|
||||
* implement rrset type and tests. (all corner cases, random cases).
|
||||
* msg-reply structure. unit tests of structure.
|
||||
* Test of those rrset pointers
|
||||
* daemon upgraded to be a caching forwarder. So it stores all in cache.
|
||||
Replies from cache. Tests on caching forwarder functionality.
|
||||
* timeout of data test
|
||||
* finding data in cache.
|
||||
* finding data not in cache.
|
||||
* lru falloff of data.
|
||||
* Test update of one rrset in cached packet.
|
||||
* Speed test of caching forwarder.
|
||||
|
||||
*** Basic Resolver
|
||||
* Create module interface and module caller algorithm.
|
||||
* Daemon config to use modules. Test the module caller.
|
||||
* Create basic iterator and scrubber modules.
|
||||
* Test every state of the iterator by passing test data into
|
||||
it.
|
||||
* And scrubber.
|
||||
* Daemon config as cache(iterator).
|
||||
* Test daemon
|
||||
* Speed test.
|
||||
|
||||
*** Validator
|
||||
* Create validator
|
||||
* Test validator on various conditions. By having stored set of
|
||||
domains and RRs in those domains to return to validator.
|
||||
* Validating resolver.
|
||||
* Test resolver.
|
||||
* Speed test.
|
||||
|
||||
*** Put to a limited audience
|
||||
* The alpha/gamma core functionality, svn access to limited audience.
|
||||
* Support features and requests as they arise.
|
||||
* Provide real-world experiences.
|
||||
|
||||
*** Bigger and Better
|
||||
* Config file syntax checker program. Tests on checker.
|
||||
* Logging first class feature with config options.
|
||||
X with logfile turnover to avoid Gbs of logs.
|
||||
* use syslog optional.
|
||||
* donotqueryaddresses with trie for blocking entire netblocks.
|
||||
* Memory overhaul, special allocators for hashtable caches, and mesh qstates.
|
||||
* keep a preallocated list of region-chunks per worker thread.
|
||||
* allocate region struct and cleanup list in region itself; use
|
||||
linked list cleanup list. unit test on this. do not call region
|
||||
to avoid name-collision with nsd regions, 'regional'.
|
||||
* read root hints from file.
|
||||
* failover to next server in 1 second, instead of 100 seconds on one server.
|
||||
X failure to return answer, w. reason (donotq, noanswer servers, cannot
|
||||
find servers, validationfail w.classification, error),
|
||||
with threadno, starttime and endtime and qname/type/class, prime/qflags,
|
||||
from-clients, from-internal, has-subrequests, a nice error report,
|
||||
so that an excerpt from those times can be made from the logs.
|
||||
logfileparsing tool that makes these excerpts and emails them.
|
||||
Not done; user can change verbosity and kill -HUP.
|
||||
* clear cache as a callback from the new-rrset-id routine.
|
||||
X make overload mode work; phase 0 all ok, phase 1 some threads close ports,
|
||||
to let other threads pick up work. phase 2, all threads closed, so all open
|
||||
the ports again and drop all non-cache-reply queries.
|
||||
Keep mutexed num-overloaded-threads counter. thread incs it when it hits
|
||||
max number of user queries serviced in mesh. threads decs it when it
|
||||
falls below 90% of the max. if incs, and not all threads closed, phase 1,
|
||||
else, phase 2 start is broadcast over command pipes. if decs, open ports
|
||||
if phase 1, start servicing, phase is 0 again. Make robust against delays.
|
||||
readme: max about 1 second worth of incoming queries, 10k perhaps,
|
||||
or 1/number of seconds it takes start up of 10k.
|
||||
Not done. Implement drop when full.
|
||||
* the source includes a copy of the ldns lib for ease of building by
|
||||
new users. Detect system installed ldns, if installed ldns is OK; use
|
||||
dynamic linking against it, otherwise static linking against packaged ldns.
|
||||
* no greedy TTL algo (and test).
|
||||
* maximum TTL, cap incoming values, and config option.
|
||||
|
||||
*** Local zones feature.
|
||||
* Build in local zone features. First the total stop for1912.
|
||||
* Then 'local content' for minimal serving of localhost.localdomain,
|
||||
and so on.
|
||||
* Remember jakob's diagram. views support, selective recursive service:
|
||||
* acl for allowed recursion (RD=1), then drop or refused query.
|
||||
like 10.0.0.0/8 allow, 0.0.0.0/0 refuse, ... in-order.
|
||||
perhaps also, same list to disallow RD=0 access, like;
|
||||
allow_recursion, drop_recursion, refuse_recursion, drop_all
|
||||
* static answers for queries, fixed RRs from cfg, option
|
||||
query for that RR returns answer with that RR.
|
||||
* blacklist (return fixed nxdomain for domain and below), option
|
||||
can be used to block AS112 traffic, option to unblock a zone.
|
||||
* after checking acl, do iter: static, blacklist, forwards, recurse.
|
||||
* Forward-local-zone to NSD.
|
||||
- include in package, autoforkexec on localhost to do so.
|
||||
* forward local zone to remote server.
|
||||
* stub zones - send queries for a zone to configged nameserver.
|
||||
* test local zones
|
||||
* for speed
|
||||
* for correctness on corner cases
|
||||
* for validation
|
||||
* in case you get data on localzone in an answer (from rootserver).
|
||||
|
||||
*** Library use
|
||||
* Create library that can do:
|
||||
* resolver
|
||||
* validator
|
||||
* validating resolver.
|
||||
* Test application that links the library. (Like /usr/bin/host+validating).
|
||||
* Test it.
|
||||
|
||||
*** Corner cases
|
||||
* Try to setup corner cases of (mis)configured DNS service/websites.
|
||||
* Resolve msoft, google, yahoo, etc weird websites.
|
||||
* Try to resolve many many different queries, perhaps compared with bind.
|
||||
* create module testers, specific for the modules
|
||||
* read a file with cache contents and settings, provide fake
|
||||
environment for module-handle-state-X functions, then check
|
||||
resulting module state structure to correct answer.
|
||||
* speed test cache responses.
|
||||
* using two servers, compare answer differences between bind and unbound.
|
||||
this gives false differences due to changes in the rest of internet.
|
||||
|
||||
*** Beta release.
|
||||
* Run shadow for a resolver in production for several weeks.
|
||||
* Check logs for errors, long queries.
|
||||
* Run in valgrind, speed profiling (as production shadow).
|
||||
|
||||
*** Features features
|
||||
* aggressive negative caching for NSEC, NSEC3.
|
||||
* multiple queries per question, server exploration, server selection.
|
||||
* option to use real entropy for randomness (mix it in once in a while).
|
||||
* check query, enforce qdsection, checking for forgery-resilience.
|
||||
* NSID support.
|
||||
* support TSIG on queries, for validating resolver deployment.
|
||||
* Nicer statistics
|
||||
* private TTL, dTLS features.
|
||||
* retry-mode, where a bogus result triggers a retry-mode query, where a list
|
||||
of responses over a time interval is collected, and each is validated.
|
||||
* draft-timers, DLV features.
|
||||
|
||||
treeshrew/
|
||||
validator/ *.c *.h
|
||||
module takes qname, qtype, asks next module for answer
|
||||
and validates that answer.
|
||||
iterator/ *.c *.h
|
||||
module takes qname, qtype, iterative DNS queries
|
||||
never asks next module.
|
||||
services/
|
||||
- Routines that provide the callback services for modules.
|
||||
|
||||
alloc_service: L1, L2 alloc service
|
||||
outside_network: pending queries helpers.
|
||||
pending query structure
|
||||
listen_dnsport: listen port53 service.
|
||||
request structure
|
||||
type_caches/
|
||||
rrset_cache
|
||||
msg_cache
|
||||
rrset and msg cache check local zones.
|
||||
infra_cache
|
||||
trusted_key_cache
|
||||
util/
|
||||
- Various components from which to build the rest.
|
||||
|
||||
storage/
|
||||
rbtree: redblack tree, for L1 use.
|
||||
- copy from NSD.
|
||||
hashtable and hashfunc: for L1 use.
|
||||
locked_hashtable: for L2 use. -- not needed.
|
||||
fragment_hashtable: for L2 use.
|
||||
fragment_rbtree: for L2 use.
|
||||
slab_allocator: perhaps to support alloc service.
|
||||
|
||||
(in util/ itself)
|
||||
locks: selected lock,unlock (spinlock/mutex).
|
||||
config: reads, stores config file
|
||||
netio: register callbacks to select().
|
||||
- use libevent (!)
|
||||
- copy from NSD.
|
||||
log: error and log handling.
|
||||
module.h: module interface
|
||||
misc: time() wrapper for speed.
|
||||
|
||||
data/
|
||||
msg_reply: qname/qtype/CD/qclass/reply store.
|
||||
packed_rrset: main datatype
|
||||
dname: compare, printf, parse
|
||||
|
||||
testcode/
|
||||
main programs that do unit tests, using testdata
|
||||
testdata/
|
||||
daemon/
|
||||
unbound.c for validating caching recursive dns server.
|
||||
scheduler.c for the modules.
|
||||
|
||||
libunbound/
|
||||
app linkable. Can be configged to do whatever,
|
||||
validator, iterator, validating iterator, forwarding stub.
|
||||
libforwardbound/
|
||||
app linkable forwarding stub. Small lib.
|
||||
|
||||
ask_cachor/ *.c *.h
|
||||
module takes qname, qtype, returns answer from msgcache.
|
||||
could ask cached for answer (and wait for network, 10 ms).
|
||||
if not in cache, asks next module.
|
||||
cachord/
|
||||
main.c, simple udp proto, query or store msg in cache.
|
||||
supports option to save cache to disk (absolute time ttls).
|
||||
+70
-2
@@ -34,7 +34,7 @@ then interface with the modules to perform the necessary processing.
|
||||
|
||||
In Section 2 the origins of the Unbound project are documented. Section
|
||||
3 lists the goals, while Section 4 lists the explicit non-goals of the
|
||||
project.
|
||||
project. Section 5 discusses choices made during development.
|
||||
|
||||
|
||||
2. History
|
||||
@@ -138,7 +138,7 @@ o The method by which dnssec-lameness is detected is not secure. DNSSEC lame
|
||||
before the validator will properly verify the messages.
|
||||
|
||||
Also for zones for which no chain of trust exists, but a DS is given by the
|
||||
parent, dnssec-lameness detection enables. This delivers dnnsec to our
|
||||
parent, dnssec-lameness detection enables. This delivers dnssec to our
|
||||
clients when possible (for client validators).
|
||||
|
||||
The following issue needs to be resolved:
|
||||
@@ -175,3 +175,71 @@ o authority features.
|
||||
You can put authority data on a separate server, and set the server in
|
||||
unbound.conf as stub for those zones, this allows clients to access data
|
||||
from the server without making unbound authoritative for the zones.
|
||||
|
||||
o the access control denies queries before any other processing.
|
||||
This denies queries that are not authoritative, or version.bind, or any.
|
||||
And thus prevents cache-snooping (denied hosts cannot make non-recursive
|
||||
queries and get answers from the cache).
|
||||
|
||||
o If a client makes a query without RD bit, in the case of a returned
|
||||
message from cache which is:
|
||||
answer section: empty
|
||||
auth section: NS record present, no SOA record, no DS record,
|
||||
maybe NSEC or NSEC3 records present.
|
||||
additional: A records or other relevant records.
|
||||
A SOA record would indicate that this was a NODATA answer.
|
||||
A DS records would indicate a referral.
|
||||
Absence of NS record would indicate a NODATA answer as well.
|
||||
|
||||
Then the receiver does not know whether this was a referral
|
||||
with attempt at no-DS proof) or a nodata answer with attempt
|
||||
at no-data proof. It could be determined by attempting to prove
|
||||
either condition; and looking if only one is valid, but both
|
||||
proofs could be valid, or neither could be valid, which creates
|
||||
doubt. This case is validated by unbound as a 'referral' which
|
||||
ascertains that RRSIGs are OK (and not omitted), but does not
|
||||
check NSEC/NSEC3.
|
||||
|
||||
o Case preservation
|
||||
Unbound preserves the casing received from authority servers as best
|
||||
as possible. It compresses without case, so case can get lost there.
|
||||
The casing from the query name is used in preference to the casing
|
||||
of the authority server. This is the same as BIND. RFC4343 allows either
|
||||
behaviour.
|
||||
|
||||
o Denial of service protection
|
||||
If many queries are made, and they are made to names for which the
|
||||
authority servers do not respond, then the requestlist for unbound
|
||||
fills up fast. This results in denial of service for new queries.
|
||||
To combat this the first 50% of the requestlist can run to completion.
|
||||
The last 50% of the requestlist get (200 msec) at least and are replaced
|
||||
by newer queries when older (LIFO).
|
||||
When a new query comes in, and a place in the first 50% is available, this
|
||||
is preferred. Otherwise, it can replace older queries out of the last 50%.
|
||||
Thus, even long queries get a 50% chance to be resolved. And many 'short'
|
||||
one or two round-trip resolves can be done in the last 50% of the list.
|
||||
The timeout can be configured.
|
||||
|
||||
o EDNS fallback. Is done according to the EDNS RFC (and update draft-00).
|
||||
Unbound assumes EDNS 0 support for the first query. Then it can detect
|
||||
support (if the servers replies) or non-support (on a NOTIMPL or FORMERR).
|
||||
Some middleboxes drop EDNS 0 queries, mainly when forwarding, not when
|
||||
routing packets. To detect this, when timeouts keep happening, as the
|
||||
timeout approached 5-10 seconds, and EDNS status has not been detected yet,
|
||||
a single probe query is sent. This probe has a sub-second timeout, and
|
||||
if the server responds (quickly) without EDNS, this is cached for 15 min.
|
||||
This works very well when detecting an address that you use much - like
|
||||
a forwarder address - which is where the middleboxes need to be detected.
|
||||
Otherwise, it results in a 5 second wait time before EDNS timeout is
|
||||
detected, which is slow but it works at least.
|
||||
It minimizes the chances of a dropped query making a (DNSSEC) EDNS server
|
||||
falsely EDNS-nonsupporting, and thus DNSSEC-bogus, works well with
|
||||
middleboxes, and can detect the occasional authority that drops EDNS.
|
||||
|
||||
o 0x20 backoff.
|
||||
The draft describes to back off to the next server, and go through all
|
||||
servers several times. Unbound goes on get the full list of nameserver
|
||||
addresses, and then makes 3 * number of addresses queries.
|
||||
They are sent to a random server, but no one address more than 4 times.
|
||||
It succeeds if one has 0x20 intact, or else all are equal.
|
||||
Otherwise, servfail is returned to the client.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
.TH "unbound-checkconf" "8" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" unbound-checkconf.8 -- unbound configuration checker manual
|
||||
.\"
|
||||
@@ -6,40 +7,37 @@
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.Dd @date@
|
||||
.Dt unbound-checkconf 8
|
||||
.Sh NAME
|
||||
.SH "NAME"
|
||||
.LP
|
||||
unbound-checkconf
|
||||
.Nd Check unbound configuration file for errors.
|
||||
.Sh SYNOPSIS
|
||||
.Nm unbound-checkconf
|
||||
.Op Fl h
|
||||
.Op cfgfile
|
||||
.Sh DESCRIPTION
|
||||
.Ic Unbound-checkconf
|
||||
\- Check unbound configuration file for errors.
|
||||
.SH "SYNOPSIS"
|
||||
.B unbound-checkconf
|
||||
.RB [ \-h ]
|
||||
.IR cfgfile
|
||||
.SH "DESCRIPTION"
|
||||
.B Unbound-checkconf
|
||||
checks the configuration file for the
|
||||
.Xr unbound 8
|
||||
\fIunbound\fR(8)
|
||||
DNS resolver for syntax and other errors.
|
||||
The config file syntax is
|
||||
described in
|
||||
.Xr unbound.conf 5 .
|
||||
.Pp
|
||||
The config file syntax is described in
|
||||
\fIunbound.conf\fR(5).
|
||||
.P
|
||||
The available options are:
|
||||
.Bl -tag -width indent
|
||||
.It Fl h
|
||||
.TP
|
||||
.B \-h
|
||||
Show the version and commandline option help.
|
||||
.It cfgfile
|
||||
.TP
|
||||
.I cfgfile
|
||||
The config file to read with settings for unbound. It is checked.
|
||||
If omitted, the config file at the default location is checked.
|
||||
.Sh EXIT CODE
|
||||
.SH "EXIT CODE"
|
||||
The unbound-checkconf program exits with status code 1 on error,
|
||||
0 for a correct config file.
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width indent
|
||||
.It Pa /etc/unbound/unbound.conf
|
||||
.SH "FILES"
|
||||
.TP
|
||||
.I @ub_conf_file@
|
||||
unbound configuration file.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr unbound.conf 5 ,
|
||||
.Xr unbound 8 .
|
||||
.SH "SEE ALSO"
|
||||
\fIunbound.conf\fR(5),
|
||||
\fIunbound\fR(8).
|
||||
@@ -0,0 +1,322 @@
|
||||
.TH "unbound-control" "8" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" unbound-control.8 -- unbound remote control manual
|
||||
.\"
|
||||
.\" Copyright (c) 2008, NLnet Labs. All rights reserved.
|
||||
.\"
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.SH "NAME"
|
||||
.LP
|
||||
unbound-control
|
||||
\- Unbound remote server control utility.
|
||||
.SH "SYNOPSIS"
|
||||
.B unbound-control
|
||||
.RB [ \-h ]
|
||||
.RB [ \-c
|
||||
.IR cfgfile ]
|
||||
.RB [ \-s
|
||||
.IR server ]
|
||||
.IR command
|
||||
.SH "DESCRIPTION"
|
||||
.B Unbound-control
|
||||
performs remote administration on the \fIunbound\fR(8) DNS server.
|
||||
It reads the configuration file, contacts the unbound server over SSL
|
||||
sends the command and displays the result.
|
||||
.P
|
||||
The available options are:
|
||||
.TP
|
||||
.B \-h
|
||||
Show the version and commandline option help.
|
||||
.TP
|
||||
.B \-c \fIcfgfile
|
||||
The config file to read with settings. If not given the default
|
||||
config file @ub_conf_file@ is used.
|
||||
.TP
|
||||
.B \-s \fIserver[@port]
|
||||
IPv4 or IPv6 address of the server to contact. If not given, the
|
||||
address is read from the config file.
|
||||
.SH "COMMANDS"
|
||||
There are several commands that the server understands.
|
||||
.TP
|
||||
.B start
|
||||
Start the server. Simply execs \fIunbound\fR(8). The unbound executable
|
||||
is searched for in the \fBPATH\fR set in the environment. It is started
|
||||
with the config file specified using \fI\-c\fR or the default config file.
|
||||
.TP
|
||||
.B stop
|
||||
Stop the server. The server daemon exits.
|
||||
.TP
|
||||
.B reload
|
||||
Reload the server. This flushes the cache and reads the config file fresh.
|
||||
.TP
|
||||
.B verbosity \fInumber
|
||||
Change verbosity value for logging. Same values as \fBverbosity\fR keyword in
|
||||
\fIunbound.conf\fR(5). This new setting lasts until the server is issued
|
||||
a reload (taken from config file again), or the next verbosity control command.
|
||||
.TP
|
||||
.B stats
|
||||
Print statistics. Resets the internal counters to zero, this can be
|
||||
controlled using the \fBstatistics\-cumulative\fR config statement.
|
||||
Statistics are printed with one [name]: [value] per line.
|
||||
.TP
|
||||
.B status
|
||||
Display server status. Exit code 3 if not running (the connection to the
|
||||
port is refused), 1 on error, 0 if running.
|
||||
.TP
|
||||
.B local_zone \fIname\fR \fItype
|
||||
Add new local zone with name and type. Like \fBlocal\-zone\fR config statement.
|
||||
If the zone already exists, the type is changed to the given argument.
|
||||
.TP
|
||||
.B local_zone_remove \fIname
|
||||
Remove the local zone with the given name. Removes all local data inside
|
||||
it. If the zone does not exist, the command succeeds.
|
||||
.TP
|
||||
.B local_data \fIRR data...
|
||||
Add new local data, the given resource record. Like \fBlocal\-data\fR
|
||||
config statement, except for when no covering zone exists. In that case
|
||||
this remote control command creates a transparent zone with the same
|
||||
name as this record. This command is not good at returning detailed syntax
|
||||
errors.
|
||||
.TP
|
||||
.B local_data_remove \fIname
|
||||
Remove all RR data from local name. If the name already has no items,
|
||||
nothing happens. Often results in NXDOMAIN for the name (in a static zone),
|
||||
but if the name has become an empty nonterminal (there is still data in
|
||||
domain names below the removed name), NOERROR nodata answers are the
|
||||
result for that name.
|
||||
.TP
|
||||
.B dump_cache
|
||||
The contents of the cache is printed in a text format to stdout. You can
|
||||
redirect it to a file to store the cache in a file.
|
||||
.TP
|
||||
.B load_cache
|
||||
The contents of the cache is loaded from stdin. Uses the same format as
|
||||
dump_cache uses. Loading the cache with old, or wrong data can result
|
||||
in old or wrong data returned to clients.
|
||||
.TP
|
||||
.B lookup \fIname
|
||||
Print to stdout the name servers that would be used to look up the
|
||||
name specified.
|
||||
.TP
|
||||
.B flush \fIname
|
||||
Remove the name from the cache. Removes the types
|
||||
A, AAAA, NS, SOA, CNAME, DNAME, MX, PTR, SRV and NAPTR.
|
||||
Because that is fast to do. Other record types can be removed using
|
||||
.B flush_type
|
||||
or
|
||||
.B flush_zone\fR.
|
||||
.TP
|
||||
.B flush_type \fIname\fR \fItype
|
||||
Remove the name, type information from the cache.
|
||||
.TP
|
||||
.B flush_zone \fIname
|
||||
Remove all information at or below the name from the cache.
|
||||
The rrsets and key entries are removed so that new lookups will be performed.
|
||||
This needs to walk and inspect the entire cache, and is a slow operation.
|
||||
.SH "EXIT CODE"
|
||||
The unbound-control program exits with status code 1 on error, 0 on success.
|
||||
.SH "SET UP"
|
||||
The setup requires a self\-signed certificate and private keys for both
|
||||
the server and client. The script \fIunbound\-control\-setup\fR generates
|
||||
these in the default run directory, or with \-d in another directory.
|
||||
Run the script under the same username as you have configured in unbound.conf
|
||||
so that the daemon is permitted to read the files, for example with:
|
||||
.nf
|
||||
sudo \-u unbound unbound\-control\-setup
|
||||
.fi
|
||||
If you have not configured
|
||||
a username in unbound.conf, the keys need read permission for the user
|
||||
credentials under which the daemon is started.
|
||||
The script preserves private keys present in the directory.
|
||||
After running the script as root, turn on \fBcontrol-enable\fR in
|
||||
\fIunbound.conf\fR.
|
||||
.SH "STATISTIC COUNTERS"
|
||||
The \fIstats\fR command shows a number of statistic counters.
|
||||
.TP
|
||||
.I threadX.num.queries
|
||||
number of queries received by thread
|
||||
.TP
|
||||
.I threadX.num.cachehits
|
||||
number of queries that were successfully answered using a cache lookup
|
||||
.TP
|
||||
.I threadX.num.cachemiss
|
||||
number of queries that needed recursive processing
|
||||
.TP
|
||||
.I threadX.num.recursivereplies
|
||||
The number of replies sent to queries that needed recursive processing. Could be smaller than threadX.num.cachemiss if due to timeouts no replies were sent for some queries.
|
||||
.TP
|
||||
.I threadX.requestlist.avg
|
||||
The average number of requests in the internal recursive processing request list on insert of a new incoming recursive processing query.
|
||||
.TP
|
||||
.I threadX.requestlist.max
|
||||
Maximum size attained by the internal recursive processing request list.
|
||||
.TP
|
||||
.I threadX.requestlist.overwritten
|
||||
Number of requests in the request list that were overwritten by newer entries. This happens if there is a flood of queries that recursive processing and the server has a hard time.
|
||||
.TP
|
||||
.I threadX.requestlist.exceeded
|
||||
Queries that were dropped because the request list was full. This happens if a flood of queries need recursive processing, and the server can not keep up.
|
||||
.TP
|
||||
.I threadX.requestlist.current.all
|
||||
Current size of the request list, includes internally generated queries (such
|
||||
as priming queries and glue lookups).
|
||||
.TP
|
||||
.I threadX.requestlist.current.user
|
||||
Current size of the request list, only the requests from client queries.
|
||||
.TP
|
||||
.I threadX.recursion.time.avg
|
||||
Average time it took to answer queries that needed recursive processing. Note that queries that were answered from the cache are not in this average.
|
||||
.TP
|
||||
.I threadX.recursion.time.median
|
||||
The median of the time it took to answer queries that needed recursive
|
||||
processing. The median means that 50% of the user queries were answered in
|
||||
less than this time. Because of big outliers (usually queries to non
|
||||
responsive servers), the average can be bigger than the median. This median
|
||||
has been calculated by interpolation from a histogram.
|
||||
.TP
|
||||
.I total.num.queries
|
||||
summed over threads.
|
||||
.TP
|
||||
.I total.num.cachehits
|
||||
summed over threads.
|
||||
.TP
|
||||
.I total.num.cachemiss
|
||||
summed over threads.
|
||||
.TP
|
||||
.I total.num.recursivereplies
|
||||
summed over threads.
|
||||
.TP
|
||||
.I total.requestlist.avg
|
||||
averaged over threads.
|
||||
.TP
|
||||
.I total.requestlist.max
|
||||
the maximum of the thread requestlist.max values.
|
||||
.TP
|
||||
.I total.requestlist.overwritten
|
||||
summed over threads.
|
||||
.TP
|
||||
.I total.requestlist.exceeded
|
||||
summed over threads.
|
||||
.TP
|
||||
.I total.requestlist.current.all
|
||||
summed over threads.
|
||||
.TP
|
||||
.I total.recursion.time.median
|
||||
averaged over threads.
|
||||
.TP
|
||||
.I time.now
|
||||
current time in seconds since 1970.
|
||||
.TP
|
||||
.I time.up
|
||||
uptime since server boot in seconds.
|
||||
.TP
|
||||
.I time.elapsed
|
||||
time since last statistics printout, in seconds.
|
||||
.SH EXTENDED STATISTICS
|
||||
.TP
|
||||
.I mem.total.sbrk
|
||||
If sbrk(2) is available, an estimate of the heap size of the program in number of bytes. Close to the total memory used by the program, as reported by top and ps. Could be wrong if the OS allocates memory non\-contiguously.
|
||||
.TP
|
||||
.I mem.cache.rrset
|
||||
Memory in bytes in use by the RRset cache.
|
||||
.TP
|
||||
.I mem.cache.message
|
||||
Memory in bytes in use by the message cache.
|
||||
.TP
|
||||
.I mem.mod.iterator
|
||||
Memory in bytes in use by the iterator module.
|
||||
.TP
|
||||
.I mem.mod.validator
|
||||
Memory in bytes in use by the validator module. Includes the key cache and
|
||||
negative cache.
|
||||
.TP
|
||||
.I histogram.<sec>.<usec>.to.<sec>.<usec>
|
||||
Shows a histogram, summed over all threads. Every element counts the
|
||||
recursive queries whose reply time fit between the lower and upper bound.
|
||||
Times larger or equal to the lowerbound, and smaller than the upper bound.
|
||||
There are 40 buckets, with bucket sizes doubling.
|
||||
.TP
|
||||
.I num.query.type.A
|
||||
The total number of queries over all threads with query type A.
|
||||
Printed for the other query types as well, but only for the types for which
|
||||
queries were received, thus =0 entries are omitted for brevity.
|
||||
.TP
|
||||
.I num.query.type.other
|
||||
Number of queries with query types 256-65535.
|
||||
.TP
|
||||
.I num.query.class.IN
|
||||
The total number of queries over all threads with query class IN (internet).
|
||||
Also printed for other classes (such as CH (CHAOS) sometimes used for
|
||||
debugging), or NONE, ANY, used by dynamic update.
|
||||
num.query.class.other is printed for classes 256-65535.
|
||||
.TP
|
||||
.I num.query.opcode.QUERY
|
||||
The total number of queries over all threads with query opcode QUERY.
|
||||
Also printed for other opcodes, UPDATE, ...
|
||||
.TP
|
||||
.I num.query.tcp
|
||||
Number of queries that were made using TCP towards the unbound server.
|
||||
.TP
|
||||
.I num.query.ipv6
|
||||
Number of queries that were made using IPv6 towards the unbound server.
|
||||
.TP
|
||||
.I num.query.flags.RD
|
||||
The number of queries that had the RD flag set in the header.
|
||||
Also printed for flags QR, AA, TC, RA, Z, AD, CD.
|
||||
Note that queries with flags QR, AA or TC may have been rejected
|
||||
because of that.
|
||||
.TP
|
||||
.I num.query.edns.present
|
||||
number of queries that had an EDNS OPT record present.
|
||||
.TP
|
||||
.I num.query.edns.DO
|
||||
number of queries that had an EDNS OPT record with the DO (DNSSEC OK) bit set.
|
||||
These queries are also included in the num.query.edns.present number.
|
||||
.TP
|
||||
.I num.answer.rcode.NXDOMAIN
|
||||
The number of answers to queries, from cache or from recursion, that had the
|
||||
return code NXDOMAIN. Also printed for the other return codes.
|
||||
.TP
|
||||
.I num.answer.rcode.nodata
|
||||
The number of answers to queries that had the pseudo return code nodata.
|
||||
This means the actual return code was NOERROR, but additionally, no data was
|
||||
carried in the answer (making what is called a NOERROR/NODATA answer).
|
||||
These queries are also included in the num.answer.rcode.NOERROR number.
|
||||
Common for AAAA lookups when an A record exists, and no AAAA.
|
||||
.TP
|
||||
.I num.answer.secure
|
||||
Number of answers that were secure. The answer validated correctly.
|
||||
The AD bit might have been set in some of these answers, where the client
|
||||
signalled (with DO or AD bit in the query) that they were ready to accept
|
||||
the AD bit in the answer.
|
||||
.TP
|
||||
.I num.answer.bogus
|
||||
Number of answers that were bogus. These answers resulted in SERVFAIL
|
||||
to the client because the answer failed validation.
|
||||
.TP
|
||||
.I num.rrset.bogus
|
||||
The number of rrsets marked bogus by the validator. Increased for every
|
||||
RRset inspection that fails.
|
||||
.TP
|
||||
.I unwanted.queries
|
||||
Number of queries that were refused or dropped because they failed the
|
||||
access control settings.
|
||||
.TP
|
||||
.I unwanted.replies
|
||||
Replies that were unwanted or unsolicited. Could have been random traffic,
|
||||
delayed duplicates, very late answers, or could be spoofing attempts.
|
||||
Some low level of late answers and delayed duplicates are to be expected
|
||||
with the UDP protocol. Very high values could indicate a threat (spoofing).
|
||||
.SH "FILES"
|
||||
.TP
|
||||
.I @ub_conf_file@
|
||||
unbound configuration file.
|
||||
.TP
|
||||
.I @UNBOUND_RUN_DIR@
|
||||
directory with private keys (unbound_server.key and unbound_control.key) and
|
||||
self-signed certificates (unbound_server.pem and unbound_control.pem).
|
||||
.SH "SEE ALSO"
|
||||
\fIunbound.conf\fR(5),
|
||||
\fIunbound\fR(8).
|
||||
@@ -0,0 +1,112 @@
|
||||
.TH "unbound\-host" "1" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" unbound-host.1 -- unbound DNS lookup utility
|
||||
.\"
|
||||
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
.\"
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.SH "NAME"
|
||||
.LP
|
||||
.B unbound\-host
|
||||
\- unbound DNS lookup utility
|
||||
.SH "SYNOPSIS"
|
||||
.LP
|
||||
.B unbound\-host
|
||||
.RB [ \-vdhr46 ]
|
||||
.RB [ \-c
|
||||
.IR class ]
|
||||
.RB [ \-t
|
||||
.IR type ]
|
||||
.I hostname
|
||||
.RB [ \-y
|
||||
.IR key ]
|
||||
.RB [ \-f
|
||||
.IR keyfile ]
|
||||
.RB [ \-F
|
||||
.IR namedkeyfile ]
|
||||
.RB [ \-C
|
||||
.IR configfile ]
|
||||
.SH "DESCRIPTION"
|
||||
.LP
|
||||
.B Unbound\-host
|
||||
uses the unbound validating resolver to query for the hostname and display
|
||||
results. With the \fB\-v\fR option it displays validation
|
||||
status: secure, insecure, bogus (security failure).
|
||||
.P
|
||||
The available options are:
|
||||
.TP
|
||||
.I hostname
|
||||
This name is resolved (looked up in the DNS).
|
||||
If a IPv4 or IPv6 address is given, a reverse lookup is performed.
|
||||
.TP
|
||||
.B \-h
|
||||
Show the version and commandline option help.
|
||||
.TP
|
||||
.B \-v
|
||||
Enable verbose output and it shows validation results, on every line.
|
||||
Secure means that the NXDOMAIN (no such domain name), nodata (no such data)
|
||||
or positive data response validated correctly with one of the keys.
|
||||
Insecure means that that domain name has no security set up for it.
|
||||
Bogus (security failure) means that the response failed one or more checks,
|
||||
it is likely wrong, outdated, tampered with, or broken.
|
||||
.TP
|
||||
.B \-d
|
||||
Enable debug output to stderr. One \-d shows what the resolver and validator
|
||||
are doing and may tell you what is going on. More times, \-d \-d, gives a
|
||||
lot of output, with every packet sent and received.
|
||||
.TP
|
||||
.B \-c \fIclass
|
||||
Specify the class to lookup for, the default is IN the internet class.
|
||||
.TP
|
||||
.B \-t \fItype
|
||||
Specify the type of data to lookup. The default looks for IPv4, IPv6 and
|
||||
mail handler data, or domain name pointers for reverse queries.
|
||||
.TP
|
||||
.B \-y \fIkey
|
||||
Specify a public key to use as trust anchor. This is the base for a chain
|
||||
of trust that is built up from the trust anchor to the response, in order
|
||||
to validate the response message. Can be given as a DS or DNSKEY record.
|
||||
For example \-y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD".
|
||||
.TP
|
||||
.B \-f \fIkeyfile
|
||||
Reads keys from a file. Every line has a DS or DNSKEY record, in the format
|
||||
as for \-y. The zone file format, the same as dig and drill produce.
|
||||
.TP
|
||||
.B \-F \fInamedkeyfile
|
||||
Reads keys from a BIND\-style named.conf file. Only the trusted\-key {}; entries
|
||||
are read.
|
||||
.TP
|
||||
.B \-C \fIconfigfile
|
||||
Uses the specified unbound.conf to prime
|
||||
.IR libunbound (3).
|
||||
.TP
|
||||
.B \-r
|
||||
Read /etc/resolv.conf, and use the forward DNS servers from there (those could
|
||||
have been set by DHCP). More info in
|
||||
.IR resolv.conf (5).
|
||||
Breaks validation if those servers do not support DNSSEC.
|
||||
.TP
|
||||
.B \-4
|
||||
Use solely the IPv4 network for sending packets.
|
||||
.TP
|
||||
.B \-6
|
||||
Use solely the IPv6 network for sending packets.
|
||||
.SH "EXAMPLES"
|
||||
.LP
|
||||
Some examples of use. The keys shown below are fakes, thus a security failure
|
||||
is encountered.
|
||||
.P
|
||||
$ unbound\-host www.example.com
|
||||
.P
|
||||
$ unbound\-host \-v \-y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD" www.example.com
|
||||
.P
|
||||
$ unbound\-host \-v \-y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD" 192.0.2.153
|
||||
.SH "EXIT CODE"
|
||||
The unbound\-host program exits with status code 1 on error,
|
||||
0 on no error. The data may not be available on exit code 0, exit code 1
|
||||
means the lookup encountered a fatal error.
|
||||
.SH "SEE ALSO"
|
||||
\fIunbound.conf\fR(5),
|
||||
\fIunbound\fR(8).
|
||||
@@ -1,3 +1,4 @@
|
||||
.TH "unbound" "8" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" unbound.8 -- unbound manual
|
||||
.\"
|
||||
@@ -6,42 +7,45 @@
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.Dd @date@
|
||||
.Dt unbound 8
|
||||
.Sh NAME
|
||||
unbound
|
||||
.Nd Unbound DNS validating resolver @version@.
|
||||
.Sh SYNOPSIS
|
||||
.Nm unbound
|
||||
.Op Fl h
|
||||
.Op Fl d
|
||||
.Op Fl v
|
||||
.Op Fl c Ar cfgfile
|
||||
.Sh DESCRIPTION
|
||||
.Ic Unbound
|
||||
.SH "NAME"
|
||||
.LP
|
||||
.B unbound
|
||||
\- Unbound DNS validating resolver @version@.
|
||||
.SH "SYNOPSIS"
|
||||
.LP
|
||||
.B unbound
|
||||
.RB [ \-h ]
|
||||
.RB [ \-d ]
|
||||
.RB [ \-v ]
|
||||
.RB [ \-c
|
||||
.IR cfgfile ]
|
||||
.SH "DESCRIPTION"
|
||||
.LP
|
||||
.B Unbound
|
||||
is an implementation of a DNS resolver, that does caching and
|
||||
DNSSEC validation.
|
||||
.Pp
|
||||
.P
|
||||
The available options are:
|
||||
.Bl -tag -width indent
|
||||
.It Fl h
|
||||
.TP
|
||||
.B \-h
|
||||
Show the version and commandline option help.
|
||||
.It Fl c Ar cfgfile
|
||||
Set the config file with settings for unbound to read instead of the
|
||||
file at default location /etc/unbound/unbound.conf. The syntax is
|
||||
described in
|
||||
.Xr unbound.conf 5 .
|
||||
.It Fl d
|
||||
.TP
|
||||
.B \-c\fI cfgfile
|
||||
Set the config file with settings for unbound to read instead of reading the
|
||||
file at the default location, @ub_conf_file@. The syntax is
|
||||
described in \fIunbound.conf\fR(5).
|
||||
.TP
|
||||
.B \-d
|
||||
Debug flag, do not fork into the background, but stay attached to the
|
||||
console. This flag will also delay writing to the logfile until the
|
||||
thread-spawn time. So that most config and setup errors appear on stderr.
|
||||
.It Fl v
|
||||
thread\-spawn time. So that most config and setup errors appear on stderr.
|
||||
.TP
|
||||
.B \-v
|
||||
Increase verbosity. If given multiple times, more information is logged.
|
||||
This is in addition to the verbosity (if any) from the config file.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr unbound.conf 5 ,
|
||||
.Xr unbound-checkconf 8 .
|
||||
.Sh AUTHORS
|
||||
.Ic Unbound
|
||||
.SH "SEE ALSO"
|
||||
\fIunbound.conf\fR(5),
|
||||
\fIunbound\-checkconf\fR(8).
|
||||
.SH "AUTHORS"
|
||||
.B Unbound
|
||||
developers are mentioned in the CREDITS file in the distribution.
|
||||
@@ -1,393 +0,0 @@
|
||||
.\"
|
||||
.\" unbound.conf.5 -- unbound.conf manual
|
||||
.\"
|
||||
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
.\"
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.Dd @date@
|
||||
.Os FreeBSD
|
||||
.Dt unbound.conf 5
|
||||
.Sh NAME
|
||||
.Nm unbound.conf
|
||||
.Nd Unbound configuration file.
|
||||
.Sh SYNOPSIS
|
||||
.Nm unbound.conf
|
||||
.Sh DESCRIPTION
|
||||
.Ic unbound.conf
|
||||
is used to configure
|
||||
.Xr unbound 8 .
|
||||
The file format has attributes and values. Some attributes have attributes inside them.
|
||||
The notation is: attribute: value.
|
||||
.Pp
|
||||
Comments start with # and last to the end of line. Empty lines are
|
||||
ignored as is whitespace at the beginning of a line.
|
||||
.Pp
|
||||
The utility
|
||||
.Xr unbound-checkconf 8
|
||||
can be used to check unbound.conf prior to usage.
|
||||
.Sh EXAMPLE
|
||||
An example config file is shown below. Copy this to /etc/unbound/unbound.conf
|
||||
and start the server with:
|
||||
.nf
|
||||
$ unbound -c /etc/unbound/unbound.conf
|
||||
.fi
|
||||
Most settings are the defaults. Stop the server with:
|
||||
.nf
|
||||
$ kill `cat /etc/unbound/unbound.pid`
|
||||
.fi
|
||||
Below is a minimal config file. The source distribution contains an extensive
|
||||
example.conf file with all the options.
|
||||
.nf
|
||||
# unbound.conf(5) config file for unbound(8).
|
||||
server:
|
||||
directory: "/etc/unbound"
|
||||
username: unbound # make sure it can write to pidfile.
|
||||
chroot: "/etc/unbound"
|
||||
# logfile: "/etc/unbound/unbound.log" #uncomment to use logfile.
|
||||
pidfile: "/etc/unbound/unbound.pid"
|
||||
# verbosity: 1 # uncomment and increase to get more logging.
|
||||
.fi
|
||||
.Sh FILE FORMAT
|
||||
There must be whitespace between keywords. Attribute keywords end with a colon ':'. An attribute
|
||||
is followed by its containing attributes, or a value.
|
||||
.Pp
|
||||
Files can be included using the
|
||||
.Ic include:
|
||||
directive. It can appear anywhere, and takes a single filename as an argument.
|
||||
Processing continues as if the text from the included file was copied into
|
||||
the config file at that point.
|
||||
.Ss Server Options
|
||||
These options are part of the
|
||||
.Ic server:
|
||||
clause.
|
||||
.Bl -tag -width indent
|
||||
.It \fBverbosity:\fR <number>
|
||||
The verbosity number, level 0 means no verbosity, only errors. Level 1
|
||||
gives operational information. Level 2 gives query level information,
|
||||
output per query. Level 3 gives algorithm level information.
|
||||
Default is level 1. The verbosity can also be increased from the commandline,
|
||||
see
|
||||
.Xr unbound 8 .
|
||||
.It \fBnum-threads:\fR <number>
|
||||
The number of threads to create to serve clients. Use 1 for no threading.
|
||||
.It \fBport:\fR <port number>
|
||||
The port number, default 53, on which the server responds to queries.
|
||||
.It \fBinterface:\fR <ip address>
|
||||
Interface to use to connect to the network. This interface is listened to
|
||||
for queries from clients, and answers to clients are given from it.
|
||||
Can be given multiple times to work on several interfaces. If none are
|
||||
given the default is to listen to localhost.
|
||||
The interfaces are not changed on a reload (kill -HUP) but only on restart.
|
||||
.It \fBoutgoing-interface:\fR <ip address>
|
||||
Interface to use to connect to the network. This interface is used to send
|
||||
queries to authoritative servers and receive their replies. Can be given
|
||||
multiple times to work on several interfaces. If none are given the
|
||||
default (all) is used. You can specify the same interfaces in
|
||||
.Ic interface:
|
||||
and
|
||||
.Ic outgoing-interface:
|
||||
lines, the interfaces are then used for both purposes. Outgoing queries are
|
||||
sent via a random outgoing interface to counter spoofing.
|
||||
.It \fBoutgoing-port:\fR <port number>
|
||||
The starting port number where the outgoing query port range is allocated.
|
||||
Default is 1053.
|
||||
.It \fBoutgoing-range:\fR <number>
|
||||
Number of ports to open. This number is opened per thread for every outgoing
|
||||
query interface. Must be at least 1. Default is 16.
|
||||
Larger numbers give more protection against spoofing attempts, but need
|
||||
extra resources from the operating system.
|
||||
.It \fBoutgoing-num-tcp:\fR <number>
|
||||
Number of outgoing TCP buffers to allocate per thread. Default is 10. If set
|
||||
to 0, or if do_tcp is "no", no TCP queries to authoritative servers are done.
|
||||
.It \fBincoming-num-tcp:\fR <number>
|
||||
Number of incoming TCP buffers to allocate per thread. Default is 10. If set
|
||||
to 0, or if do_tcp is "no", no TCP queries from clients are accepted.
|
||||
.It \fBmsg-buffer-size:\fR <number>
|
||||
Number of bytes size of the message buffers. Default is 65552 bytes, enough
|
||||
for 64 Kb packets, the maximum DNS message size. No message larger than this
|
||||
can be sent or received. Can be reduced to use less memory, but some requests
|
||||
for DNS data, such as for huge resource records, will result in a SERVFAIL
|
||||
reply to the client.
|
||||
.It \fBmsg-cache-size:\fR <number>
|
||||
Number of bytes size of the message cache. Default is 4 megabytes.
|
||||
.It \fBmsg-cache-slabs:\fR <number>
|
||||
Number of slabs in the message cache. Slabs reduce lock contention by threads.
|
||||
Must be set to a power of 2. Setting (close) to the number of cpus is a
|
||||
reasonable guess.
|
||||
.It \fBnum-queries-per-thread:\fR <number>
|
||||
The number of queries that every thread will service simultaneously.
|
||||
If more queries arrive that need servicing, they are dropped. This forces
|
||||
the client to resend after a timeout; allowing the server time to work on
|
||||
the existing queries. Default 1024.
|
||||
.It \fBrrset-cache-size:\fR <number>
|
||||
Number of bytes size of the RRset cache. Default is 4 megabytes.
|
||||
.It \fBrrset-cache-slabs:\fR <number>
|
||||
Number of slabs in the RRset cache. Slabs reduce lock contention by threads.
|
||||
Must be set to a power of 2.
|
||||
.It \fBcache-max-ttl:\fR <seconds>
|
||||
Time to live maximum for RRsets and messages in the cache. Default is
|
||||
864000 seconds (10 days). If the maximum kicks in, responses to clients
|
||||
still get decrementing TTLs based on the original (larger) values.
|
||||
When the internal TTL expires, the cache item has expired.
|
||||
Can be set lower to force the resolver to query for data often, and not
|
||||
trust (very large) TTL values.
|
||||
.It \fBinfra-host-ttl:\fR <seconds>
|
||||
Time to live for entries in the host cache. The host cache contains
|
||||
roundtrip timing and EDNS support information. Default is 900.
|
||||
.It \fBinfra-lame-ttl:\fR <seconds>
|
||||
The time to live when a delegation is discovered to be lame. Default is 900.
|
||||
.It \fBinfra-cache-slabs:\fR <number>
|
||||
Number of slabs in the infrastructure cache. Slabs reduce lock contention
|
||||
by threads. Must be set to a power of 2.
|
||||
.It \fBinfra-cache-numhosts:\fR <number>
|
||||
Number of hosts for which information is cached. Default is 10000.
|
||||
.It \fBinfra-cache-lame-size:\fR <number>
|
||||
Number of bytes that the lameness cache per host is allowed to use. Default
|
||||
is 10 kb, which gives maximum storage for a couple score zones, depending on
|
||||
the lame zone name lengths.
|
||||
.It \fBdo-ip4:\fR <yes or no>
|
||||
Enable or disable whether ip4 queries are answered. Default is yes.
|
||||
.It \fBdo-ip6:\fR <yes or no>
|
||||
Enable or disable whether ip6 queries are answered. Default is yes.
|
||||
.It \fBdo-udp:\fR <yes or no>
|
||||
Enable or disable whether UDP queries are answered. Default is yes.
|
||||
.It \fBdo-tcp:\fR <yes or no>
|
||||
Enable or disable whether TCP queries are answered. Default is yes.
|
||||
.It \fBchroot:\fR <directory>
|
||||
If given a chroot is done to the given directory. The default is
|
||||
"/etc/unbound". If you give "" no chroot is performed.
|
||||
.It \fBusername:\fR <name>
|
||||
If given, after binding the port the user privileges are dropped. Default is
|
||||
"unbound". If you give username: "" no user change is performed.
|
||||
.Pp
|
||||
If this user is not capable of binding the
|
||||
port, reloads (by signal HUP) will still retain the opened ports.
|
||||
If you change the port number in the config file, and that new port number
|
||||
requires privileges, then a reload will fail; a restart is needed.
|
||||
.It \fBdirectory:\fR <directory>
|
||||
Sets the working directory for the program.
|
||||
.It \fBlogfile:\fR <filename>
|
||||
If "" is given, logging goes to stderr, or nowhere once daemonized.
|
||||
The logfile is appended to, in the following format:
|
||||
[seconds since 1970] unbound[pid:tid]: type: message.
|
||||
If this option is given, the use-syslog is option is set to "no".
|
||||
The logfile is reopened (for append) when the config file is reread, on
|
||||
SIGHUP.
|
||||
.It \fBuse-syslog:\fR <yes or no>
|
||||
Sets unbound to send log messages to the syslogd, using
|
||||
.Xr syslog 3 .
|
||||
The log facility LOG_DAEMON is used, with identity "unbound".
|
||||
The logfile setting is overridden when use-syslog is turned on.
|
||||
The default is to log to syslog.
|
||||
.It \fBpidfile:\fR <filename>
|
||||
The process id is written to the file. Default is "/etc/unbound/unbound.pid".
|
||||
So,
|
||||
.nf
|
||||
kill -HUP `cat /etc/unbound/unbound.pid`
|
||||
.fi
|
||||
triggers a reload,
|
||||
.nf
|
||||
kill -QUIT `cat /etc/unbound/unbound.pid`
|
||||
.fi
|
||||
gracefully terminates.
|
||||
.It \fBroot-hints:\fR <filename>
|
||||
Read the root hints from this file. Default is nothing, using builtin hints
|
||||
for the IN class. The file has the format of zone files, with root
|
||||
nameserver names and addresses only. The default may become outdated,
|
||||
when servers change, therefore it is good practice to use a root-hints file.
|
||||
.It \fBhide-identity:\fR <yes or no>
|
||||
If enabled id.server and hostname.bind queries are refused.
|
||||
.It \fBidentity:\fR <string>
|
||||
Set the identity to report. If set to "", the default, then the hostname
|
||||
of the server is returned.
|
||||
.It \fBhide-version:\fR <yes or no>
|
||||
If enabled version.server and version.bind queries are refused.
|
||||
.It \fBversion:\fR <string>
|
||||
Set the version to report. If set to "", the default, then the package
|
||||
version is returned.
|
||||
.It \fBtarget-fetch-policy:\fR <"list of numbers">
|
||||
Set the target fetch policy used by unbound to determine if it should fetch
|
||||
nameserver target addresses opportunistically. The policy is described per
|
||||
dependency depth.
|
||||
.Pp
|
||||
The number of values determines the maximum dependency depth
|
||||
that unbound will pursue in answering a query.
|
||||
A value of -1 means to fetch all targets opportunistically for that dependency
|
||||
depth. A value of 0 means to fetch on demand only. A positive value fetches
|
||||
that many targets opportunistically.
|
||||
.Pp
|
||||
Enclose the list between quotes ("") and put spaces between numbers.
|
||||
The default is "3 2 1 0 0". Setting all zeroes, "0 0 0 0 0" gives behaviour
|
||||
closer to that of BIND 9, while setting "-1 -1 -1 -1 -1" gives behaviour
|
||||
rumoured to be closer to that of BIND 8.
|
||||
.It \fBharden-short-bufsize:\fR <yes or no>
|
||||
Very small EDNS buffer sizes from queries are ignored. Default is off, since
|
||||
it is legal protocol wise to send these, and unbound tries to give very
|
||||
small answers to these queries, where possible.
|
||||
.It \fBharden-large-queries:\fR <yes or no>
|
||||
Very large queries are ignored. Default is off, since it is legal protocol
|
||||
wise to send these, and could be necessary for operation if TSIG or EDNS
|
||||
payload is very large.
|
||||
.It \fBharden-glue:\fR <yes or no>
|
||||
Will trust glue only if it is within the servers authority. Default is on.
|
||||
.It \fBharden-dnssec-stripped:\fR <yes or no>
|
||||
Require DNSSEC data for trust-anchored zones, if such data is absent,
|
||||
the zone becomes bogus. If turned off, and no DNSSEC data is received
|
||||
(or the DNSKEY data fails to validate), then the zone is made insecure,
|
||||
this behaves like there is no trust anchor. You could turn this off if
|
||||
you are sometimes behind an intrusive firewall (of some sort) that
|
||||
removes DNSSEC data from packets, or a zone changes from signed to
|
||||
unsigned to badly signed often. If turned off you run the risk of a
|
||||
downgrade attack that disables security for a zone. Default is on.
|
||||
.It \fBdo-not-query-address:\fR <IP address>
|
||||
Do not query the given IP address. Can be IP4 or IP6. Append /num to
|
||||
indicate a classless delegation netblock, for example like
|
||||
10.2.3.4/24 or 2001::11/64.
|
||||
.It \fBdo-not-query-localhost:\fR <yes or no>
|
||||
If yes, localhost is added to the do-not-query-address entries, both
|
||||
IP6 ::1 and IP4 127.0.0.1/8. If no, then localhost can be used to send
|
||||
queries to. Default is yes.
|
||||
.It \fBmodule-config:\fR <"module names">
|
||||
Module configuration, a list of module names separated by spaces, surround
|
||||
the string with quotes (""). The modules can be validator, iterator.
|
||||
Setting this to "iterator" will result in a non-validating server.
|
||||
Setting this to "validator iterator" will turn on DNSSEC validation.
|
||||
You must also set trust-anchors for validation to be useful.
|
||||
.It \fBtrust-anchor-file:\fR <filename>
|
||||
File with trusted keys for validation. Both DS and DNSKEY entries can appear
|
||||
in the file. The format of the file is the standard DNS Zone file format.
|
||||
Default is "", or no trust anchor file.
|
||||
.It \fBtrust-anchor:\fR <"Resource Record">
|
||||
A DS or DNSKEY RR for a key to use for validation. Multiple entries can be
|
||||
given to specify multiple trusted keys, in addition to the trust-anchor-files.
|
||||
The resource record is entered in the same format as 'dig' or 'drill' prints
|
||||
them, the same format as in the zone file. Has to be on a single line, with
|
||||
"" around it. A TTL can be specified for ease of cut and paste, but is ignored.
|
||||
A class can be specified, but class IN is default.
|
||||
.It \fBtrusted-keys-file:\fR <filename>
|
||||
File with trusted keys for validation. Specify more than one file
|
||||
with several entries, one file per entry. Like \fBtrust-anchor-file\fR
|
||||
but has a different file format. Format is BIND-9 style format,
|
||||
the trusted-keys { name flag proto algo "key"; }; clauses are read.
|
||||
.It \fBval-override-date:\fR <rrsig-style date spec>
|
||||
Default is "" or "0", which disables this debugging feature. If enabled by
|
||||
giving a RRSIG style date, that date is used for verifying RRSIG inception
|
||||
and expiration dates, instead of the current date. Do not set this unless
|
||||
you are debugging signature inception and expiration.
|
||||
.It \fBval-bogus-ttl:\fR <number>
|
||||
The time to live for bogus data. This is data that has failed validation;
|
||||
due to invalid signatures or other checks. The TTL from that data cannot be
|
||||
trusted, and this value is used instead. The value is in seconds, default 900.
|
||||
The time interval prevents repeated revalidation of bogus data.
|
||||
.It \fBval-clean-additional:\fR <yes or no>
|
||||
Instruct the validator to remove data from the additional section of secure
|
||||
messages that are not signed properly. Messages that are insecure, bogus,
|
||||
indeterminate or unchecked are not affected. Default is yes. Use this setting
|
||||
to protect the users that rely on this validator for authentication from
|
||||
protentially bad data in the additional section.
|
||||
.It \fBval-permissive-mode:\fR <yes or no>
|
||||
Instruct the validator to mark bogus messages as indeterminate. The security
|
||||
checks are performed, but if the result is bogus (failed security), the
|
||||
reply is not withheld from the client with SERVFAIL as usual. The client
|
||||
receives the bogus data. For messages that are found to be secure the AD bit
|
||||
is set in replies. Also logging is performed as for full validation.
|
||||
The default value is "no".
|
||||
.It \fBval-nsec3-keysize-iterations:\fR <"list of values">
|
||||
List of keysize and iteration count values, separated by spaces, surrounded
|
||||
by quotes. Default is "1024 150 2048 500 4096 2500". This determines the
|
||||
maximum allowed NSEC3 iteration count before a message is simply marked
|
||||
insecure instead of performing the many hashing iterations. The list must
|
||||
be in ascending order and have at least one entry. If you set it to
|
||||
"1024 65535" there is no restriction to NSEC3 iteration values.
|
||||
This table must be kept short; a very long list could cause slower operation.
|
||||
.It \fBkey-cache-size:\fR <number>
|
||||
Number of bytes size of the key cache. Default is 4 megabytes.
|
||||
.It \fBkey-cache-slabs:\fR <number>
|
||||
Number of slabs in the key cache. Slabs reduce lock contention by threads.
|
||||
Must be set to a power of 2. Setting (close) to the number of cpus is a
|
||||
reasonable guess.
|
||||
.El
|
||||
.Ss Stub Zone Options
|
||||
There may be multiple
|
||||
.Ic stub-zone:
|
||||
clauses. Each with a name: and zero or more hostnames or IP addresses.
|
||||
For the stub zone this list of nameservers is used. Class IN is assumed.
|
||||
.Bl -tag -width indent
|
||||
.It \fBname:\fR <domain name>
|
||||
Name of the stub zone.
|
||||
.It \fBstub-host:\fR <domain name>
|
||||
Name of stub zone nameserver. Is itself resolved before it is used.
|
||||
.It \fBstub-addr:\fR <IP address>
|
||||
IP address of stub zone nameserver. Can be IP 4 or IP 6.
|
||||
To use a nondefault port for DNS communication append '@' with the port number.
|
||||
.El
|
||||
.Ss Forward Zone Options
|
||||
There may be multiple
|
||||
.Ic forward-zone:
|
||||
clauses. Each with a name: and zero or more hostnames or IP addresses.
|
||||
For the forward zone this list of nameservers is used to forward the queries
|
||||
to. The servers have to handle further recursion for the query. Class IN is
|
||||
assumed. A forward-zone entry with name "." and a forward-addr target will
|
||||
forward all queries to that other server (unless it can answer from the cache).
|
||||
.Bl -tag -width indent
|
||||
.It \fBname:\fR <domain name>
|
||||
Name of the forward zone.
|
||||
.It \fBforward-host:\fR <domain name>
|
||||
Name of server to forward to. Is itself resolved before it is used.
|
||||
.It \fBforward-addr:\fR <IP address>
|
||||
IP address of server to forward to. Can be IP 4 or IP 6.
|
||||
To use a nondefault port for DNS communication append '@' with the port number.
|
||||
.El
|
||||
.Sh MEMORY CONTROL EXAMPLE
|
||||
In the example config settings below memory usage is reduced. Some service
|
||||
levels are lower, notable very large data and a high TCP load are no longer
|
||||
supported. Very large data and high TCP loads are exceptional for the DNS.
|
||||
DNSSEC validation is enabled, just add trust anchors.
|
||||
If you do not have to worry about programs using more than 1 meg of memory,
|
||||
the below example is not for you. Use the defaults to receive full service.
|
||||
.Pp
|
||||
.nf
|
||||
# example settings that reduce memory usage
|
||||
server:
|
||||
num-threads: 1
|
||||
outgoing-num-tcp: 1 # this limits TCP service, uses less buffers.
|
||||
incoming-num-tcp: 1
|
||||
outgoing-range: 1 # uses less memory, but less port randomness.
|
||||
msg-buffer-size: 8192 # note this limits service, 'no huge stuff'.
|
||||
msg-cache-size: 102400 # 100 Kb.
|
||||
msg-cache-slabs: 1
|
||||
rrset-cache-size: 102400 # 100 Kb.
|
||||
rrset-cache-slabs: 1
|
||||
infra-cache-numhosts: 200
|
||||
infra-cache-numlame: 10
|
||||
key-cache-size: 102400 # 100 Kb.
|
||||
key-cache-slabs: 1
|
||||
num-queries-per-thread: 30
|
||||
target-fetch-policy: "2 1 0 0 0 0"
|
||||
harden-large-queries: "yes"
|
||||
harden-short-bufsize: "yes"
|
||||
do-ip6: no # save a bit of memory if not used.
|
||||
.fi
|
||||
.Sh FILES
|
||||
.Bl -tag -width indent
|
||||
.It Pa /etc/unbound
|
||||
default unbound working directory and default
|
||||
.Xr chroot 2
|
||||
location.
|
||||
.It Pa unbound.conf
|
||||
unbound configuration file.
|
||||
.It Pa unbound.pid
|
||||
default unbound pidfile with process ID of the running daemon.
|
||||
.It Pa unbound.log
|
||||
unbound log file. default is to log to
|
||||
.Xr syslog 3 .
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr unbound 8 ,
|
||||
.Xr unbound-checkconf 8 .
|
||||
.Sh AUTHORS
|
||||
.Ic Unbound
|
||||
was written by NLnet Labs. Please see CREDITS file
|
||||
in the distribution for further details.
|
||||
@@ -0,0 +1,865 @@
|
||||
.TH "unbound.conf" "5" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" unbound.conf.5 -- unbound.conf manual
|
||||
.\"
|
||||
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
.\"
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.SH "NAME"
|
||||
.LP
|
||||
.B unbound.conf
|
||||
\- Unbound configuration file.
|
||||
.SH "SYNOPSIS"
|
||||
.LP
|
||||
.B unbound.conf
|
||||
.SH "DESCRIPTION"
|
||||
.LP
|
||||
.B unbound.conf
|
||||
is used to configure
|
||||
\fIunbound\fR(8).
|
||||
The file format has attributes and values. Some attributes have attributes inside them.
|
||||
The notation is: attribute: value.
|
||||
.P
|
||||
Comments start with # and last to the end of line. Empty lines are
|
||||
ignored as is whitespace at the beginning of a line.
|
||||
.P
|
||||
The utility
|
||||
\fIunbound\-checkconf\fR(8)
|
||||
can be used to check unbound.conf prior to usage.
|
||||
.SH "EXAMPLE"
|
||||
An example config file is shown below. Copy this to /etc/unbound/unbound.conf
|
||||
and start the server with:
|
||||
.P
|
||||
.nf
|
||||
$ unbound \-c /etc/unbound/unbound.conf
|
||||
.fi
|
||||
.P
|
||||
Most settings are the defaults. Stop the server with:
|
||||
.P
|
||||
.nf
|
||||
$ kill `cat /etc/unbound/unbound.pid`
|
||||
.fi
|
||||
.P
|
||||
Below is a minimal config file. The source distribution contains an extensive
|
||||
example.conf file with all the options.
|
||||
.P
|
||||
.nf
|
||||
# unbound.conf(5) config file for unbound(8).
|
||||
server:
|
||||
directory: "/etc/unbound"
|
||||
username: unbound
|
||||
# make sure unbound can access entropy from inside the chroot.
|
||||
# e.g. on linux the use these commands (on BSD, devfs(8) is used):
|
||||
# mount --bind -n /dev/random /etc/unbound/dev/random
|
||||
# and mount --bind -n /dev/log /etc/unbound/dev/log
|
||||
chroot: "/etc/unbound"
|
||||
# logfile: "/etc/unbound/unbound.log" #uncomment to use logfile.
|
||||
pidfile: "/etc/unbound/unbound.pid"
|
||||
# verbosity: 1 # uncomment and increase to get more logging.
|
||||
# listen on all interfaces, answer queries from the local subnet.
|
||||
interface: 0.0.0.0
|
||||
interface: ::0
|
||||
access\-control: 10.0.0.0/8 allow
|
||||
access\-control: 2001:DB8::/64 allow
|
||||
.fi
|
||||
.SH "FILE FORMAT"
|
||||
.LP
|
||||
There must be whitespace between keywords. Attribute keywords end with a colon ':'. An attribute
|
||||
is followed by its containing attributes, or a value.
|
||||
.P
|
||||
Files can be included using the
|
||||
.B include:
|
||||
directive. It can appear anywhere, and takes a single filename as an argument.
|
||||
Processing continues as if the text from the included file was copied into
|
||||
the config file at that point. If also using chroot, using full path names
|
||||
for the included files works, relative pathnames for the included names work
|
||||
if the directory where the daemon is started equals its chroot/working
|
||||
directory.
|
||||
.SS "Server Options"
|
||||
These options are part of the
|
||||
.B server:
|
||||
clause.
|
||||
.TP
|
||||
.B verbosity: \fI<number>
|
||||
The verbosity number, level 0 means no verbosity, only errors. Level 1
|
||||
gives operational information. Level 2 gives detailed operational
|
||||
information. Level 3 gives query level information, output per query.
|
||||
Level 4 gives algorithm level information.
|
||||
Default is level 1. The verbosity can also be increased from the commandline,
|
||||
see
|
||||
\fIunbound\fR(8).
|
||||
.TP
|
||||
.B statistics\-interval: \fI<seconds>
|
||||
The number of seconds between printing statistics to the log for every thread.
|
||||
Disable with value 0 or "". Default is disabled.
|
||||
.TP
|
||||
.B statistics\-cumulative: \fI<yes or no>
|
||||
If enabled, statistics are cumulative since starting unbound, without clearing
|
||||
the statistics counters after logging the statistics. Default is no.
|
||||
.TP
|
||||
.B extended\-statistics: \fI<yes or no>
|
||||
If enabled, extended statistics are printed from \fIunbound\-control\fR(8).
|
||||
Default is off, because keeping track of more statistics takes time.
|
||||
.TP
|
||||
.B num\-threads: \fI<number>
|
||||
The number of threads to create to serve clients. Use 1 for no threading.
|
||||
.TP
|
||||
.B port: \fI<port number>
|
||||
The port number, default 53, on which the server responds to queries.
|
||||
.TP
|
||||
.B interface: \fI<ip address>
|
||||
Interface to use to connect to the network. This interface is listened to
|
||||
for queries from clients, and answers to clients are given from it.
|
||||
Can be given multiple times to work on several interfaces. If none are
|
||||
given the default is to listen to localhost.
|
||||
The interfaces are not changed on a reload (kill \-HUP) but only on restart.
|
||||
.TP
|
||||
.B interface-automatic: \fI<yes or no>
|
||||
Detect source interface on UDP queries and copy them to replies. This
|
||||
feature is experimental, and needs support in your OS for IPv6
|
||||
(and its socket options) and IPv4 (and have source-interface socket options).
|
||||
Default value is no.
|
||||
.TP
|
||||
.B outgoing\-interface: \fI<ip address>
|
||||
Interface to use to connect to the network. This interface is used to send
|
||||
queries to authoritative servers and receive their replies. Can be given
|
||||
multiple times to work on several interfaces. If none are given the
|
||||
default (all) is used. You can specify the same interfaces in
|
||||
.B interface:
|
||||
and
|
||||
.B outgoing\-interface:
|
||||
lines, the interfaces are then used for both purposes. Outgoing queries are
|
||||
sent via a random outgoing interface to counter spoofing.
|
||||
.TP
|
||||
.B outgoing\-range: \fI<number>
|
||||
Number of ports to open. This number of file descriptors can be opened per
|
||||
thread. Must be at least 1. Default is 256. Larger numbers need extra
|
||||
resources from the operating system.
|
||||
.TP
|
||||
.B outgoing\-port\-permit: \fI<port number or range>
|
||||
Permit unbound to open this port or range of ports for use to send queries.
|
||||
A larger number of permitted outgoing ports increases resilience against
|
||||
spoofing attempts. Make sure these ports are not needed by other daemons.
|
||||
By default only ports above 1024 that have not been assigned by IANA are used.
|
||||
Give a port number or a range of the form "low-high", without spaces.
|
||||
.IP
|
||||
The \fBoutgoing\-port\-permit\fR and \fBoutgoing\-port\-avoid\fR statements
|
||||
are processed in the line order of the config file, adding the permitted ports
|
||||
and subtracting the avoided ports from the set of allowed ports. The
|
||||
processing starts with the non IANA allocated ports above 1024 in the set
|
||||
of allowed ports.
|
||||
.TP
|
||||
.B outgoing\-port\-avoid: \fI<port number or range>
|
||||
Do not permit unbound to open this port or range of ports for use to send
|
||||
queries. Use this to make sure unbound does not grab a port that another
|
||||
daemon needs. The port is avoided on all outgoing interfaces, both IP4 and IP6.
|
||||
By default only ports above 1024 that have not been assigned by IANA are used.
|
||||
Give a port number or a range of the form "low-high", without spaces.
|
||||
.TP
|
||||
.B outgoing\-num\-tcp: \fI<number>
|
||||
Number of outgoing TCP buffers to allocate per thread. Default is 10. If set
|
||||
to 0, or if do_tcp is "no", no TCP queries to authoritative servers are done.
|
||||
.TP
|
||||
.B incoming\-num\-tcp: \fI<number>
|
||||
Number of incoming TCP buffers to allocate per thread. Default is 10. If set
|
||||
to 0, or if do_tcp is "no", no TCP queries from clients are accepted.
|
||||
.TP
|
||||
.B msg\-buffer\-size: \fI<number>
|
||||
Number of bytes size of the message buffers. Default is 65552 bytes, enough
|
||||
for 64 Kb packets, the maximum DNS message size. No message larger than this
|
||||
can be sent or received. Can be reduced to use less memory, but some requests
|
||||
for DNS data, such as for huge resource records, will result in a SERVFAIL
|
||||
reply to the client.
|
||||
.TP
|
||||
.B msg\-cache\-size: \fI<number>
|
||||
Number of bytes size of the message cache. Default is 4 megabytes.
|
||||
A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, megabytes
|
||||
or gigabytes (1024*1024 bytes in a megabyte).
|
||||
.TP
|
||||
.B msg\-cache\-slabs: \fI<number>
|
||||
Number of slabs in the message cache. Slabs reduce lock contention by threads.
|
||||
Must be set to a power of 2. Setting (close) to the number of cpus is a
|
||||
reasonable guess.
|
||||
.TP
|
||||
.B num\-queries\-per\-thread: \fI<number>
|
||||
The number of queries that every thread will service simultaneously.
|
||||
If more queries arrive that need servicing, and no queries can be jostled out
|
||||
(see \fIjostle\-timeout\fR), then the queries are dropped. This forces
|
||||
the client to resend after a timeout; allowing the server time to work on
|
||||
the existing queries. Default 1024.
|
||||
.TP
|
||||
.B jostle\-timeout: \fI<msec>
|
||||
Timeout used when the server is very busy. Set to a value that usually
|
||||
results in one roundtrip to the authority servers. If too many queries
|
||||
arrive, then 50% of the queries are allowed to run to completion, and
|
||||
the other 50% are replaced with the new incoming query if they have already
|
||||
spent more than their allowed time. This protects against denial of
|
||||
service by slow queries or high query rates. Default 200 milliseconds.
|
||||
.TP
|
||||
.B rrset\-cache\-size: \fI<number>
|
||||
Number of bytes size of the RRset cache. Default is 4 megabytes.
|
||||
A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, megabytes
|
||||
or gigabytes (1024*1024 bytes in a megabyte).
|
||||
.TP
|
||||
.B rrset\-cache\-slabs: \fI<number>
|
||||
Number of slabs in the RRset cache. Slabs reduce lock contention by threads.
|
||||
Must be set to a power of 2.
|
||||
.TP
|
||||
.B cache\-max\-ttl: \fI<seconds>
|
||||
Time to live maximum for RRsets and messages in the cache. Default is
|
||||
86400 seconds (1 day). If the maximum kicks in, responses to clients
|
||||
still get decrementing TTLs based on the original (larger) values.
|
||||
When the internal TTL expires, the cache item has expired.
|
||||
Can be set lower to force the resolver to query for data often, and not
|
||||
trust (very large) TTL values.
|
||||
.TP
|
||||
.B infra\-host\-ttl: \fI<seconds>
|
||||
Time to live for entries in the host cache. The host cache contains
|
||||
roundtrip timing and EDNS support information. Default is 900.
|
||||
.TP
|
||||
.B infra\-lame\-ttl: \fI<seconds>
|
||||
The time to live when a delegation is discovered to be lame. Default is 900.
|
||||
.TP
|
||||
.B infra\-cache\-slabs: \fI<number>
|
||||
Number of slabs in the infrastructure cache. Slabs reduce lock contention
|
||||
by threads. Must be set to a power of 2.
|
||||
.TP
|
||||
.B infra\-cache\-numhosts: \fI<number>
|
||||
Number of hosts for which information is cached. Default is 10000.
|
||||
.TP
|
||||
.B infra\-cache\-lame\-size: \fI<number>
|
||||
Number of bytes that the lameness cache per host is allowed to use. Default
|
||||
is 10 kb, which gives maximum storage for a couple score zones, depending on
|
||||
the lame zone name lengths.
|
||||
.TP
|
||||
.B do\-ip4: \fI<yes or no>
|
||||
Enable or disable whether ip4 queries are answered or issued. Default is yes.
|
||||
.TP
|
||||
.B do\-ip6: \fI<yes or no>
|
||||
Enable or disable whether ip6 queries are answered or issued. Default is yes.
|
||||
If disabled, queries are not answered on IPv6, and queries are not sent on
|
||||
IPv6 to the internet nameservers.
|
||||
.TP
|
||||
.B do\-udp: \fI<yes or no>
|
||||
Enable or disable whether UDP queries are answered or issued. Default is yes.
|
||||
.TP
|
||||
.B do\-tcp: \fI<yes or no>
|
||||
Enable or disable whether TCP queries are answered or issued. Default is yes.
|
||||
.TP
|
||||
.B do\-daemonize: \fI<yes or no>
|
||||
Enable or disable whether the unbound server forks into the background as
|
||||
a daemon. Default is yes.
|
||||
.TP
|
||||
.B access\-control: \fI<IP netblock> <action>
|
||||
The netblock is given as an IP4 or IP6 address with /size appended for a
|
||||
classless network block. The action can be \fIdeny\fR, \fIrefuse\fR,
|
||||
\fIallow\fR or \fIallow_snoop\fR.
|
||||
.IP
|
||||
The action \fIdeny\fR stops queries from hosts from that netblock.
|
||||
.IP
|
||||
The action \fIrefuse\fR stops queries too, but sends a DNS rcode REFUSED
|
||||
error message back.
|
||||
.IP
|
||||
The action \fIallow\fR gives access to clients from that netblock.
|
||||
It gives only access for recursion clients (which is
|
||||
what almost all clients need). Nonrecursive queries are refused.
|
||||
.IP
|
||||
The \fIallow\fR action does allow nonrecursive queries to access the
|
||||
local\-data that is configured. The reason is that this does not involve
|
||||
the unbound server recursive lookup algorithm, and static data is served
|
||||
in the reply. This supports normal operations where nonrecursive queries
|
||||
are made for the authoritative data. For nonrecursive queries any replies
|
||||
from the dynamic cache are refused.
|
||||
.IP
|
||||
The action \fIallow_snoop\fR gives nonrecursive access too. This give
|
||||
both recursive and non recursive access. The name \fIallow_snoop\fR refers
|
||||
to cache snooping, a technique to use nonrecursive queries to examine
|
||||
the cache contents (for malicious acts). However, nonrecursive queries can
|
||||
also be a valuable debugging tool (when you want to examine the cache
|
||||
contents). In that case use \fIallow_snoop\fR for your administration host.
|
||||
.IP
|
||||
By default only localhost is \fIallow\fRed, the rest is \fIrefuse\fRd.
|
||||
The default is \fIrefuse\fRd, because that is protocol\-friendly. The DNS
|
||||
protocol is not designed to handle dropped packets due to policy, and
|
||||
dropping may result in (possibly excessive) retried queries.
|
||||
.TP
|
||||
.B chroot: \fI<directory>
|
||||
If chroot is enabled, you should pass the configfile (from the
|
||||
commandline) as a full path from the original root. After the
|
||||
chroot has been performed the now defunct portion of the config
|
||||
file path is removed to be able to reread the config after a reload.
|
||||
.IP
|
||||
All other file paths (working dir, logfile, roothints, and
|
||||
key files) can be specified in several ways:
|
||||
as an absolute path relative to the new root,
|
||||
as a relative path to the working directory, or
|
||||
as an absolute path relative to the original root.
|
||||
In the last case the path is adjusted to remove the unused portion.
|
||||
.IP
|
||||
The pidfile can be either a relative path to the working directory, or
|
||||
an absolute path relative to the original root. It is written just prior
|
||||
to chroot and dropping permissions. This allows the pidfile to be
|
||||
/var/run/unbound.pid and the chroot to be /var/unbound, for example.
|
||||
.IP
|
||||
Additionally, unbound may need to access /dev/random (for entropy)
|
||||
from inside the chroot.
|
||||
.IP
|
||||
If given a chroot is done to the given directory. The default is
|
||||
"@UNBOUND_CHROOT_DIR@". If you give "" no chroot is performed.
|
||||
.TP
|
||||
.B username: \fI<name>
|
||||
If given, after binding the port the user privileges are dropped. Default is
|
||||
"@UNBOUND_USERNAME@". If you give username: "" no user change is performed.
|
||||
.IP
|
||||
If this user is not capable of binding the
|
||||
port, reloads (by signal HUP) will still retain the opened ports.
|
||||
If you change the port number in the config file, and that new port number
|
||||
requires privileges, then a reload will fail; a restart is needed.
|
||||
.TP
|
||||
.B directory: \fI<directory>
|
||||
Sets the working directory for the program. Default is "@UNBOUND_RUN_DIR@".
|
||||
.TP
|
||||
.B logfile: \fI<filename>
|
||||
If "" is given, logging goes to stderr, or nowhere once daemonized.
|
||||
The logfile is appended to, in the following format:
|
||||
.nf
|
||||
[seconds since 1970] unbound[pid:tid]: type: message.
|
||||
.fi
|
||||
If this option is given, the use\-syslog is option is set to "no".
|
||||
The logfile is reopened (for append) when the config file is reread, on
|
||||
SIGHUP.
|
||||
.TP
|
||||
.B use\-syslog: \fI<yes or no>
|
||||
Sets unbound to send log messages to the syslogd, using
|
||||
\fIsyslog\fR(3).
|
||||
The log facility LOG_DAEMON is used, with identity "unbound".
|
||||
The logfile setting is overridden when use\-syslog is turned on.
|
||||
The default is to log to syslog.
|
||||
.TP
|
||||
.B pidfile: \fI<filename>
|
||||
The process id is written to the file. Default is "@UNBOUND_PIDFILE@".
|
||||
So,
|
||||
.nf
|
||||
kill \-HUP `cat @UNBOUND_PIDFILE@`
|
||||
.fi
|
||||
triggers a reload,
|
||||
.nf
|
||||
kill \-QUIT `cat @UNBOUND_PIDFILE@`
|
||||
.fi
|
||||
gracefully terminates.
|
||||
.TP
|
||||
.B root\-hints: \fI<filename>
|
||||
Read the root hints from this file. Default is nothing, using builtin hints
|
||||
for the IN class. The file has the format of zone files, with root
|
||||
nameserver names and addresses only. The default may become outdated,
|
||||
when servers change, therefore it is good practice to use a root\-hints file.
|
||||
.TP
|
||||
.B hide\-identity: \fI<yes or no>
|
||||
If enabled id.server and hostname.bind queries are refused.
|
||||
.TP
|
||||
.B identity: \fI<string>
|
||||
Set the identity to report. If set to "", the default, then the hostname
|
||||
of the server is returned.
|
||||
.TP
|
||||
.B hide\-version: \fI<yes or no>
|
||||
If enabled version.server and version.bind queries are refused.
|
||||
.TP
|
||||
.B version: \fI<string>
|
||||
Set the version to report. If set to "", the default, then the package
|
||||
version is returned.
|
||||
.TP
|
||||
.B target\-fetch\-policy: \fI<"list of numbers">
|
||||
Set the target fetch policy used by unbound to determine if it should fetch
|
||||
nameserver target addresses opportunistically. The policy is described per
|
||||
dependency depth.
|
||||
.IP
|
||||
The number of values determines the maximum dependency depth
|
||||
that unbound will pursue in answering a query.
|
||||
A value of \-1 means to fetch all targets opportunistically for that dependency
|
||||
depth. A value of 0 means to fetch on demand only. A positive value fetches
|
||||
that many targets opportunistically.
|
||||
.IP
|
||||
Enclose the list between quotes ("") and put spaces between numbers.
|
||||
The default is "3 2 1 0 0". Setting all zeroes, "0 0 0 0 0" gives behaviour
|
||||
closer to that of BIND 9, while setting "\-1 \-1 \-1 \-1 \-1" gives behaviour
|
||||
rumoured to be closer to that of BIND 8.
|
||||
.TP
|
||||
.B harden\-short\-bufsize: \fI<yes or no>
|
||||
Very small EDNS buffer sizes from queries are ignored. Default is off, since
|
||||
it is legal protocol wise to send these, and unbound tries to give very
|
||||
small answers to these queries, where possible.
|
||||
.TP
|
||||
.B harden\-large\-queries: \fI<yes or no>
|
||||
Very large queries are ignored. Default is off, since it is legal protocol
|
||||
wise to send these, and could be necessary for operation if TSIG or EDNS
|
||||
payload is very large.
|
||||
.TP
|
||||
.B harden\-glue: \fI<yes or no>
|
||||
Will trust glue only if it is within the servers authority. Default is on.
|
||||
.TP
|
||||
.B harden\-dnssec\-stripped: \fI<yes or no>
|
||||
Require DNSSEC data for trust\-anchored zones, if such data is absent,
|
||||
the zone becomes bogus. If turned off, and no DNSSEC data is received
|
||||
(or the DNSKEY data fails to validate), then the zone is made insecure,
|
||||
this behaves like there is no trust anchor. You could turn this off if
|
||||
you are sometimes behind an intrusive firewall (of some sort) that
|
||||
removes DNSSEC data from packets, or a zone changes from signed to
|
||||
unsigned to badly signed often. If turned off you run the risk of a
|
||||
downgrade attack that disables security for a zone. Default is on.
|
||||
.TP
|
||||
.B harden\-referral\-path: \fI<yes or no>
|
||||
Harden the referral path by performing additional queries for
|
||||
infrastructure data. Validates the replies if trust anchors are configured
|
||||
and the zones are signed. This enforces DNSSEC validation on nameserver
|
||||
NS sets and the nameserver addresses that are encountered on the referral
|
||||
path to the answer.
|
||||
Default off, because it burdens the authority servers, and it is
|
||||
not RFC standard, and could lead to performance problems because of the
|
||||
extra query load that is generated. Experimental option.
|
||||
.TP
|
||||
.B use\-caps\-for\-id: \fI<yes or no>
|
||||
Use 0x20-encoded random bits in the query to foil spoof attempts.
|
||||
This perturbs the lowercase and uppercase of query names sent to
|
||||
authority servers and checks if the reply still has the correct casing.
|
||||
Disabled by default.
|
||||
This feature is an experimental implementation of draft dns\-0x20.
|
||||
.TP
|
||||
.B private\-address: \fI<IP address or subnet>
|
||||
Give IPv4 of IPv6 addresses or classless subnets. These are addresses
|
||||
on your private network, and are not allowed to be returned for public
|
||||
internet names. Any occurence of such addresses are removed from
|
||||
DNS answers. Additionally, the DNSSEC validator may mark the answers
|
||||
bogus. This protects against so-called DNS Rebinding, where a user browser
|
||||
is turned into a network proxy, allowing remote access through the browser
|
||||
to other parts of your private network. Some names can be allowed to
|
||||
contain your private addresses, by default all the \fBlocal\-data\fR
|
||||
that you configured is allowed to, and you can specify additional
|
||||
names using \fBprivate\-domain\fR. No private addresses are enabled
|
||||
by default. We consider to enable this for the RFC1918 private IP
|
||||
address space by default in later releases. That would enable private
|
||||
addresses for 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 192.254.0.0/16
|
||||
fd00::/8 and fe80::/10, since the RFC standards say these addresses
|
||||
should not be visible on the public internet. Turning on 127.0.0.0/8
|
||||
would hinder many spamblocklists as they use that.
|
||||
.TP
|
||||
.B private\-domain: \fI<domain name>
|
||||
Allow this domain, and all its subdomains to contain private addresses.
|
||||
Give multiple times to allow multiple domain names to contain private
|
||||
addresses. Default is none.
|
||||
.TP
|
||||
.B unwanted\-reply\-threshold: \fI<number>
|
||||
If set, a total number of unwanted replies is kept track of in every thread.
|
||||
When it reaches the threshold, a defensive action is taken and a warning
|
||||
is printed to the log. The defensive action is to clear the rrset and
|
||||
message caches, hopefully flushing away any poison. A value of 10 million
|
||||
is suggested. Default is 0 (turned off).
|
||||
.TP
|
||||
.B do\-not\-query\-address: \fI<IP address>
|
||||
Do not query the given IP address. Can be IP4 or IP6. Append /num to
|
||||
indicate a classless delegation netblock, for example like
|
||||
10.2.3.4/24 or 2001::11/64.
|
||||
.TP
|
||||
.B do\-not\-query\-localhost: \fI<yes or no>
|
||||
If yes, localhost is added to the do\-not\-query\-address entries, both
|
||||
IP6 ::1 and IP4 127.0.0.1/8. If no, then localhost can be used to send
|
||||
queries to. Default is yes.
|
||||
.TP
|
||||
.B module\-config: \fI<"module names">
|
||||
Module configuration, a list of module names separated by spaces, surround
|
||||
the string with quotes (""). The modules can be validator, iterator.
|
||||
Setting this to "iterator" will result in a non\-validating server.
|
||||
Setting this to "validator iterator" will turn on DNSSEC validation.
|
||||
You must also set trust\-anchors for validation to be useful.
|
||||
.TP
|
||||
.B trust\-anchor\-file: \fI<filename>
|
||||
File with trusted keys for validation. Both DS and DNSKEY entries can appear
|
||||
in the file. The format of the file is the standard DNS Zone file format.
|
||||
Default is "", or no trust anchor file.
|
||||
.TP
|
||||
.B trust\-anchor: \fI<"Resource Record">
|
||||
A DS or DNSKEY RR for a key to use for validation. Multiple entries can be
|
||||
given to specify multiple trusted keys, in addition to the trust\-anchor\-files.
|
||||
The resource record is entered in the same format as 'dig' or 'drill' prints
|
||||
them, the same format as in the zone file. Has to be on a single line, with
|
||||
"" around it. A TTL can be specified for ease of cut and paste, but is ignored.
|
||||
A class can be specified, but class IN is default.
|
||||
.TP
|
||||
.B trusted\-keys\-file: \fI<filename>
|
||||
File with trusted keys for validation. Specify more than one file
|
||||
with several entries, one file per entry. Like \fBtrust\-anchor\-file\fR
|
||||
but has a different file format. Format is BIND\-9 style format,
|
||||
the trusted\-keys { name flag proto algo "key"; }; clauses are read.
|
||||
It is possible to use wildcards with this statement, the wildcard is
|
||||
expanded on start and on reload.
|
||||
.TP
|
||||
.B dlv\-anchor\-file: \fI<filename>
|
||||
File with trusted keys for DLV (DNSSEC Lookaside Validation). Both DS and
|
||||
DNSKEY entries can be used in the file, in the same format as for
|
||||
\fItrust\-anchor\-file:\fR statements. Only one DLV can be configured, more
|
||||
would be slow. The DLV configured is used as a root trusted DLV, this
|
||||
means that it is a lookaside for the root. Default is "", or no dlv anchor file.
|
||||
.TP
|
||||
.B dlv\-anchor: \fI<"Resource Record">
|
||||
Much like trust\-anchor, this is a DLV anchor with the DS or DNSKEY inline.
|
||||
.TP
|
||||
.B val\-override\-date: \fI<rrsig\-style date spec>
|
||||
Default is "" or "0", which disables this debugging feature. If enabled by
|
||||
giving a RRSIG style date, that date is used for verifying RRSIG inception
|
||||
and expiration dates, instead of the current date. Do not set this unless
|
||||
you are debugging signature inception and expiration.
|
||||
.TP
|
||||
.B val\-bogus\-ttl: \fI<number>
|
||||
The time to live for bogus data. This is data that has failed validation;
|
||||
due to invalid signatures or other checks. The TTL from that data cannot be
|
||||
trusted, and this value is used instead. The value is in seconds, default 60.
|
||||
The time interval prevents repeated revalidation of bogus data.
|
||||
.TP
|
||||
.B val\-clean\-additional: \fI<yes or no>
|
||||
Instruct the validator to remove data from the additional section of secure
|
||||
messages that are not signed properly. Messages that are insecure, bogus,
|
||||
indeterminate or unchecked are not affected. Default is yes. Use this setting
|
||||
to protect the users that rely on this validator for authentication from
|
||||
protentially bad data in the additional section.
|
||||
.TP
|
||||
.B val\-permissive\-mode: \fI<yes or no>
|
||||
Instruct the validator to mark bogus messages as indeterminate. The security
|
||||
checks are performed, but if the result is bogus (failed security), the
|
||||
reply is not withheld from the client with SERVFAIL as usual. The client
|
||||
receives the bogus data. For messages that are found to be secure the AD bit
|
||||
is set in replies. Also logging is performed as for full validation.
|
||||
The default value is "no".
|
||||
.TP
|
||||
.B val\-nsec3\-keysize\-iterations: \fI<"list of values">
|
||||
List of keysize and iteration count values, separated by spaces, surrounded
|
||||
by quotes. Default is "1024 150 2048 500 4096 2500". This determines the
|
||||
maximum allowed NSEC3 iteration count before a message is simply marked
|
||||
insecure instead of performing the many hashing iterations. The list must
|
||||
be in ascending order and have at least one entry. If you set it to
|
||||
"1024 65535" there is no restriction to NSEC3 iteration values.
|
||||
This table must be kept short; a very long list could cause slower operation.
|
||||
.TP
|
||||
.B key\-cache\-size: \fI<number>
|
||||
Number of bytes size of the key cache. Default is 4 megabytes.
|
||||
A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, megabytes
|
||||
or gigabytes (1024*1024 bytes in a megabyte).
|
||||
.TP
|
||||
.B key\-cache\-slabs: \fI<number>
|
||||
Number of slabs in the key cache. Slabs reduce lock contention by threads.
|
||||
Must be set to a power of 2. Setting (close) to the number of cpus is a
|
||||
reasonable guess.
|
||||
.TP
|
||||
.B neg\-cache\-size: \fI<number>
|
||||
Number of bytes size of the aggressive negative cache. Default is 1 megabyte.
|
||||
A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, megabytes
|
||||
or gigabytes (1024*1024 bytes in a megabyte).
|
||||
.TP
|
||||
.B local\-zone: \fI<zone> <type>
|
||||
Configure a local zone. The type determines the answer to give if there is
|
||||
no match from local\-data. The types are deny, refuse, static, transparent,
|
||||
redirect, nodefault, and are explained below. After that the default settings
|
||||
are listed. Use local\-data: to enter data into the local zone. Answers for
|
||||
local zones are authoritative DNS answers. By default the zones are class IN.
|
||||
.IP
|
||||
If you need more complicated authoritative data, with referrals, wildcards,
|
||||
CNAME/DNAME support, or DNSSEC authoritative service, setup a stub\-zone for
|
||||
it as detailed in the stub zone section below.
|
||||
.TP 10
|
||||
\h'5'\fIdeny\fR
|
||||
Do not send an answer, drop the query.
|
||||
If there is a match from local data, the query is answered.
|
||||
.TP 10
|
||||
\h'5'\fIrefuse\fR
|
||||
Send an error message reply, with rcode REFUSED.
|
||||
If there is a match from local data, the query is answered.
|
||||
.TP 10
|
||||
\h'5'\fIstatic\fR
|
||||
If there is a match from local data, the query is answered.
|
||||
Otherwise, the query is answered with nodata or nxdomain.
|
||||
For a negative answer a SOA is included in the answer if present
|
||||
as local\-data for the zone apex domain.
|
||||
.TP 10
|
||||
\h'5'\fItransparent\fR
|
||||
If there is a match from local data, the query is answered.
|
||||
Otherwise if the query has a different name, the query is resolved normally.
|
||||
If the query is for a name given in localdata but no such type of data is
|
||||
given in localdata, then a noerror nodata answer is returned.
|
||||
If no local\-zone is given local\-data causes a transparent zone
|
||||
to be created by default.
|
||||
.TP 10
|
||||
\h'5'\fIredirect\fR
|
||||
The query is answered from the local data for the zone name.
|
||||
There may be no local data beneath the zone name.
|
||||
This answers queries for the zone, and all subdomains of the zone
|
||||
with the local data for the zone.
|
||||
It can be used to redirect a domain to a different address, with
|
||||
local\-zone: "example.com." redirect and
|
||||
local\-data: "example.com. A 127.0.0.1"
|
||||
queries for www.example.com and www.foo.example.com are redirected.
|
||||
.TP 10
|
||||
\h'5'\fInodefault\fR
|
||||
Used to turn off default contents for AS112 zones. The other types
|
||||
also turn off default contents for the zone. The 'nodefault' option
|
||||
has no other effect than turning off default contents for the
|
||||
given zone.
|
||||
.P
|
||||
The default zones are localhost, reverse 127.0.0.1 and ::1, and the AS112
|
||||
zones. The AS112 zones are reverse DNS zones for private use and reserved
|
||||
IP addresses for which the servers on the internet cannot provide correct
|
||||
answers. They are configured by default to give nxdomain (no reverse
|
||||
information) answers. The defaults can be turned off by specifying your
|
||||
own local\-zone of that name, or using the 'nodefault' type. Below is a
|
||||
list of the default zone contents.
|
||||
.TP 10
|
||||
\h'5'\fIlocalhost\fR
|
||||
The IP4 and IP6 localhost information is given. NS and SOA records are provided
|
||||
for completeness and to satisfy some DNS update tools. Default content:
|
||||
.nf
|
||||
local\-zone: "localhost." static
|
||||
local\-data: "localhost. 10800 IN NS localhost."
|
||||
local\-data: "localhost. 10800 IN
|
||||
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
|
||||
local\-data: "localhost. 10800 IN A 127.0.0.1"
|
||||
local\-data: "localhost. 10800 IN AAAA ::1"
|
||||
.fi
|
||||
.TP 10
|
||||
\h'5'\fIreverse IPv4 loopback\fR
|
||||
Default content:
|
||||
.nf
|
||||
local\-zone: "127.in\-addr.arpa." static
|
||||
local\-data: "127.in\-addr.arpa. 10800 IN NS localhost."
|
||||
local\-data: "127.in\-addr.arpa. 10800 IN
|
||||
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
|
||||
local\-data: "1.0.0.127.in\-addr.arpa. 10800 IN
|
||||
PTR localhost."
|
||||
.fi
|
||||
.TP 10
|
||||
\h'5'\fIreverse IPv6 loopback\fR
|
||||
Default content:
|
||||
.nf
|
||||
local\-zone: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
|
||||
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa." static
|
||||
local\-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
|
||||
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN
|
||||
NS localhost."
|
||||
local\-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
|
||||
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN
|
||||
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
|
||||
local\-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
|
||||
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN
|
||||
PTR localhost."
|
||||
.fi
|
||||
.TP 10
|
||||
\h'5'\fIreverse RFC1918 local use zones\fR
|
||||
Reverse data for zones 10.in\-addr.arpa, 16.172.in\-addr.arpa to
|
||||
31.172.in\-addr.arpa, 168.192.in\-addr.arpa.
|
||||
The \fBlocal\-zone:\fR is set static and as \fBlocal\-data:\fR SOA and NS
|
||||
records are provided.
|
||||
.TP 10
|
||||
\h'5'\fIreverse RFC3330 IP4 this, link\-local, testnet and broadcast\fR
|
||||
Reverse data for zones 0.in\-addr.arpa, 254.169.in\-addr.arpa,
|
||||
2.0.192.in\-addr.arpa, 255.255.255.255.in\-addr.arpa.
|
||||
.TP 10
|
||||
\h'5'\fIreverse RFC4291 IP6 unspecified\fR
|
||||
Reverse data for zone
|
||||
.nf
|
||||
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
|
||||
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.
|
||||
.fi
|
||||
.TP 10
|
||||
\h'5'\fIreverse RFC4193 IPv6 Locally Assigned Local Addresses\fR
|
||||
Reverse data for zone D.F.ip6.arpa.
|
||||
.TP 10
|
||||
\h'5'\fIreverse RFC4291 IPv6 Link Local Addresses\fR
|
||||
Reverse data for zones 8.E.F.ip6.arpa to B.E.F.ip6.arpa.
|
||||
.TP 10
|
||||
\h'5'\fIreverse IPv6 Example Prefix\fR
|
||||
Reverse data for zone 8.B.D.0.1.0.0.2.ip6.arpa. This zone is used for
|
||||
tutorials and examples. You can remove the block on this zone with:
|
||||
.nf
|
||||
local-zone: 8.B.D.0.1.0.0.2.ip6.arpa. nodefault
|
||||
.fi
|
||||
This also works with the other default zones.
|
||||
.\" End of local-zone listing.
|
||||
.TP 5
|
||||
.B local\-data: \fI"<resource record string>"
|
||||
Configure local data, which is served in reply to queries for it.
|
||||
The query has to match exactly unless you configure the local\-zone as
|
||||
redirect. If not matched exactly, the local\-zone type determines
|
||||
further processing. If local\-data is configured that is not a subdomain of
|
||||
a local\-zone, a transparent local\-zone is configured.
|
||||
For record types such as TXT, use single quotes, as in
|
||||
local\-data: 'example. TXT "text"'.
|
||||
.IP
|
||||
If you need more complicated authoritative data, with referrals, wildcards,
|
||||
CNAME/DNAME support, or DNSSEC authoritative service, setup a stub\-zone for
|
||||
it as detailed in the stub zone section below.
|
||||
.TP 5
|
||||
.B local\-data\-ptr: \fI"IPaddr name"
|
||||
Configure local data shorthand for a PTR record with the reversed IPv4 or
|
||||
IPv6 address and the host name. For example "192.0.2.4 www.example.com".
|
||||
TTL can be inserted like this: "2001:DB8::4 7200 www.example.com"
|
||||
.SS "Remote Control Options"
|
||||
In the
|
||||
.B remote\-control:
|
||||
clause are the declarations for the remote control facility. If this is
|
||||
enabled, the \fIunbound\-control\fR(8) utility can be used to send
|
||||
commands to the running unbound server. The server uses these clauses
|
||||
to setup SSLv3 / TLSv1 security for the connection. The
|
||||
\fIunbound\-control\fR(8) utility also reads the \fBremote\-control\fR
|
||||
section for options. To setup the correct self-signed certificates use the
|
||||
\fIunbound\-control\-setup\fR(8) utility.
|
||||
.TP 5
|
||||
.B control\-enable: \fI<yes or no>
|
||||
The option is used to enable remote control, default is "no".
|
||||
If turned off, the server does not listen for control commands.
|
||||
.TP 5
|
||||
.B control\-interface: <ip address>
|
||||
Give IPv4 or IPv6 addresses to listen on for control commands.
|
||||
By default localhost (127.0.0.1 and ::1) is listened to.
|
||||
Use 0.0.0.0 and ::0 to listen to all interfaces.
|
||||
.TP 5
|
||||
.B control\-port: <port number>
|
||||
The port number to listen on for control commands, default is 953
|
||||
(that is the same port number named uses to listen to rndc).
|
||||
If you change this port number, and permissions have been dropped, a
|
||||
reload is not sufficient to open the port again, you must then restart.
|
||||
.TP 5
|
||||
.B server\-key\-file: "<private key file>"
|
||||
Path to the server private key, by default unbound_server.key.
|
||||
This file is generated by the \fIunbound\-control\-setup\fR utility.
|
||||
This file is used by the unbound server, but not by \fIunbound\-control\fR.
|
||||
.TP 5
|
||||
.B server\-cert\-file: "<certificate file.pem>"
|
||||
Path to the server self signed certificate, by default unbound_server.pem.
|
||||
This file is generated by the \fIunbound\-control\-setup\fR utility.
|
||||
This file is used by the unbound server, and also by \fIunbound\-control\fR.
|
||||
.TP 5
|
||||
.B control\-key\-file: "<private key file>"
|
||||
Path to the control client private key, by default unbound_control.key.
|
||||
This file is generated by the \fIunbound\-control\-setup\fR utility.
|
||||
This file is used by \fIunbound\-control\fR.
|
||||
.TP 5
|
||||
.B control\-cert\-file: "<certificate file.pem>"
|
||||
Path to the control client certificate, by default unbound_control.pem.
|
||||
This certificate has to be signed with the server certificate.
|
||||
This file is generated by the \fIunbound\-control\-setup\fR utility.
|
||||
This file is used by \fIunbound\-control\fR.
|
||||
.SS "Stub Zone Options"
|
||||
.LP
|
||||
There may be multiple
|
||||
.B stub\-zone:
|
||||
clauses. Each with a name: and zero or more hostnames or IP addresses.
|
||||
For the stub zone this list of nameservers is used. Class IN is assumed.
|
||||
.P
|
||||
The stub zone can be used to configure authoritative data to be used
|
||||
by the resolver that cannot be accessed using the public internet servers.
|
||||
This is useful for company\-local data or private zones. Setup an
|
||||
authoritative server on a different host (or different port). Enter a config
|
||||
entry for unbound with
|
||||
.B stub\-addr:
|
||||
<ip address of host[@port]>.
|
||||
The unbound resolver can then access the data, without referring to the
|
||||
public internet for it.
|
||||
.P
|
||||
This setup allows DNSSEC signed zones to be served by that
|
||||
authoritative server, in which case a trusted key entry with the public key
|
||||
can be put in config, so that unbound can validate the data and set the AD
|
||||
bit on replies for the private zone (authoritative servers do not set the
|
||||
AD bit). This setup makes unbound capable of answering queries for the
|
||||
private zone, and can even set the AD bit ('authentic'), but the AA
|
||||
('authoritative') bit is not set on these replies.
|
||||
.TP
|
||||
.B name: \fI<domain name>
|
||||
Name of the stub zone.
|
||||
.TP
|
||||
.B stub\-host: \fI<domain name>
|
||||
Name of stub zone nameserver. Is itself resolved before it is used.
|
||||
.TP
|
||||
.B stub\-addr: \fI<IP address>
|
||||
IP address of stub zone nameserver. Can be IP 4 or IP 6.
|
||||
To use a nondefault port for DNS communication append '@' with the port number.
|
||||
.TP
|
||||
.B stub\-prime: \fI<yes or no>
|
||||
This option is by default off. If enabled it performs NS set priming,
|
||||
which is similar to root hints, where it starts using the list of nameservers
|
||||
currently published by the zone. Thus, if the hint list is slightly outdated,
|
||||
the resolver picks up a correct list online.
|
||||
.SS "Forward Zone Options"
|
||||
.LP
|
||||
There may be multiple
|
||||
.B forward\-zone:
|
||||
clauses. Each with a name: and zero or more hostnames or IP addresses.
|
||||
For the forward zone this list of nameservers is used to forward the queries
|
||||
to. The servers have to handle further recursion for the query. Class IN is
|
||||
assumed. A forward\-zone entry with name "." and a forward\-addr target will
|
||||
forward all queries to that other server (unless it can answer from the cache).
|
||||
.TP
|
||||
.B name: \fI<domain name>
|
||||
Name of the forward zone.
|
||||
.TP
|
||||
.B forward\-host: \fI<domain name>
|
||||
Name of server to forward to. Is itself resolved before it is used.
|
||||
.TP
|
||||
.B forward\-addr: \fI<IP address>
|
||||
IP address of server to forward to. Can be IP 4 or IP 6.
|
||||
To use a nondefault port for DNS communication append '@' with the port number.
|
||||
.SH "MEMORY CONTROL EXAMPLE"
|
||||
In the example config settings below memory usage is reduced. Some service
|
||||
levels are lower, notable very large data and a high TCP load are no longer
|
||||
supported. Very large data and high TCP loads are exceptional for the DNS.
|
||||
DNSSEC validation is enabled, just add trust anchors.
|
||||
If you do not have to worry about programs using more than 3 Mb of memory,
|
||||
the below example is not for you. Use the defaults to receive full service,
|
||||
which on BSD-32bit tops out at 30-40 Mb after heavy usage.
|
||||
.P
|
||||
.nf
|
||||
# example settings that reduce memory usage
|
||||
server:
|
||||
num\-threads: 1
|
||||
outgoing\-num\-tcp: 1 # this limits TCP service, uses less buffers.
|
||||
incoming\-num\-tcp: 1
|
||||
outgoing\-range: 16 # uses less memory, but less performance.
|
||||
msg\-buffer\-size: 8192 # note this limits service, 'no huge stuff'.
|
||||
msg\-cache\-size: 100k
|
||||
msg\-cache\-slabs: 1
|
||||
rrset\-cache\-size: 100k
|
||||
rrset\-cache\-slabs: 1
|
||||
infra\-cache\-numhosts: 200
|
||||
infra\-cache\-slabs: 1
|
||||
infra\-cache\-lame\-size: 1k
|
||||
key\-cache\-size: 100k
|
||||
key\-cache\-slabs: 1
|
||||
neg\-cache\-size: 10k
|
||||
num\-queries\-per\-thread: 30
|
||||
target\-fetch\-policy: "2 1 0 0 0 0"
|
||||
harden\-large\-queries: "yes"
|
||||
harden\-short\-bufsize: "yes"
|
||||
.fi
|
||||
.SH "FILES"
|
||||
.TP
|
||||
.I @UNBOUND_RUN_DIR@
|
||||
default unbound working directory.
|
||||
.TP
|
||||
.I @UNBOUND_CHROOT_DIR@
|
||||
default
|
||||
\fIchroot\fR(2)
|
||||
location.
|
||||
.TP
|
||||
.I @ub_conf_file@
|
||||
unbound configuration file.
|
||||
.TP
|
||||
.I @UNBOUND_PIDFILE@
|
||||
default unbound pidfile with process ID of the running daemon.
|
||||
.TP
|
||||
.I unbound.log
|
||||
unbound log file. default is to log to
|
||||
\fIsyslog\fR(3).
|
||||
.SH "SEE ALSO"
|
||||
\fIunbound\fR(8),
|
||||
\fIunbound\-checkconf\fR(8).
|
||||
.SH "AUTHORS"
|
||||
.B Unbound
|
||||
was written by NLnet Labs. Please see CREDITS file
|
||||
in the distribution for further details.
|
||||
+1
-2
@@ -150,8 +150,7 @@ MULTILINE_CPP_IS_BRIEF = NO
|
||||
# will output the detailed description near the top, like JavaDoc.
|
||||
# If set to NO, the detailed description appears after the member
|
||||
# documentation.
|
||||
|
||||
DETAILS_AT_TOP = NO
|
||||
#DETAILS_AT_TOP = NO
|
||||
|
||||
# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
|
||||
# member inherits the documentation from any documented member that it
|
||||
|
||||
+78
-20
@@ -72,9 +72,12 @@ struct delegpt* delegpt_copy(struct delegpt* dp, struct regional* region)
|
||||
if(!delegpt_add_ns(copy, region, ns->name))
|
||||
return NULL;
|
||||
copy->nslist->resolved = ns->resolved;
|
||||
copy->nslist->got4 = ns->got4;
|
||||
copy->nslist->got6 = ns->got6;
|
||||
}
|
||||
for(a = dp->target_list; a; a = a->next_target) {
|
||||
if(!delegpt_add_addr(copy, region, &a->addr, a->addrlen))
|
||||
if(!delegpt_add_addr(copy, region, &a->addr, a->addrlen,
|
||||
a->bogus, 0))
|
||||
return NULL;
|
||||
}
|
||||
return copy;
|
||||
@@ -107,6 +110,8 @@ delegpt_add_ns(struct delegpt* dp, struct regional* region, uint8_t* name)
|
||||
dp->nslist = ns;
|
||||
ns->name = regional_alloc_init(region, name, ns->namelen);
|
||||
ns->resolved = 0;
|
||||
ns->got4 = 0;
|
||||
ns->got6 = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -124,25 +129,52 @@ delegpt_find_ns(struct delegpt* dp, uint8_t* name, size_t namelen)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct delegpt_addr*
|
||||
delegpt_find_addr(struct delegpt* dp, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
struct delegpt_addr* p = dp->target_list;
|
||||
while(p) {
|
||||
if(sockaddr_cmp_addr(addr, addrlen, &p->addr, p->addrlen)==0) {
|
||||
return p;
|
||||
}
|
||||
p = p->next_target;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_add_target(struct delegpt* dp, struct regional* region,
|
||||
uint8_t* name, size_t namelen, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen)
|
||||
socklen_t addrlen, int bogus, int nodup)
|
||||
{
|
||||
struct delegpt_ns* ns = delegpt_find_ns(dp, name, namelen);
|
||||
if(!ns) {
|
||||
/* ignore it */
|
||||
return 1;
|
||||
}
|
||||
ns->resolved = 1;
|
||||
return delegpt_add_addr(dp, region, addr, addrlen);
|
||||
if(addr_is_ip6(addr, addrlen))
|
||||
ns->got6 = 1;
|
||||
else ns->got4 = 1;
|
||||
if(ns->got4 && ns->got6)
|
||||
ns->resolved = 1;
|
||||
return delegpt_add_addr(dp, region, addr, addrlen, bogus, nodup);
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_add_addr(struct delegpt* dp, struct regional* region,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen)
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int bogus, int nodup)
|
||||
{
|
||||
struct delegpt_addr* a = (struct delegpt_addr*)regional_alloc(region,
|
||||
struct delegpt_addr* a;
|
||||
if(nodup) {
|
||||
if((a = delegpt_find_addr(dp, addr, addrlen))) {
|
||||
if(bogus)
|
||||
a->bogus = bogus;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
a = (struct delegpt_addr*)regional_alloc(region,
|
||||
sizeof(struct delegpt_addr));
|
||||
if(!a)
|
||||
return 0;
|
||||
@@ -154,11 +186,11 @@ delegpt_add_addr(struct delegpt* dp, struct regional* region,
|
||||
memcpy(&a->addr, addr, addrlen);
|
||||
a->addrlen = addrlen;
|
||||
a->attempts = 0;
|
||||
a->bogus = bogus;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** count NS and number missing */
|
||||
static void
|
||||
void
|
||||
delegpt_count_ns(struct delegpt* dp, size_t* numns, size_t* missing)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
@@ -171,8 +203,7 @@ delegpt_count_ns(struct delegpt* dp, size_t* numns, size_t* missing)
|
||||
}
|
||||
}
|
||||
|
||||
/** count addresses, and number in result and available lists */
|
||||
static void
|
||||
void
|
||||
delegpt_count_addr(struct delegpt* dp, size_t* numaddr, size_t* numres,
|
||||
size_t* numavail)
|
||||
{
|
||||
@@ -213,10 +244,15 @@ void delegpt_log(enum verbosity_value v, struct delegpt* dp)
|
||||
if(verbosity >= VERB_ALGO) {
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
dname_str(ns->name, buf);
|
||||
log_info(" %s%s", buf, (ns->resolved?"*":""));
|
||||
log_info(" %s %s%s%s%s", buf, (ns->resolved?"*":""),
|
||||
(ns->got4?" A":""), (ns->got6?" AAAA":""),
|
||||
(dp->bogus?" BOGUS":"") );
|
||||
}
|
||||
for(a = dp->target_list; a; a = a->next_target) {
|
||||
log_addr(VERB_ALGO, " ", &a->addr, a->addrlen);
|
||||
if(a->bogus)
|
||||
log_addr(VERB_ALGO, " BOGUS ",
|
||||
&a->addr, a->addrlen);
|
||||
else log_addr(VERB_ALGO, " ", &a->addr, a->addrlen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,10 +331,10 @@ delegpt_from_message(struct dns_msg* msg, struct regional* region)
|
||||
continue;
|
||||
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_A) {
|
||||
if(!delegpt_add_rrset_A(dp, region, s))
|
||||
if(!delegpt_add_rrset_A(dp, region, s, 0))
|
||||
return NULL;
|
||||
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_AAAA) {
|
||||
if(!delegpt_add_rrset_AAAA(dp, region, s))
|
||||
if(!delegpt_add_rrset_AAAA(dp, region, s, 0))
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -312,6 +348,8 @@ delegpt_rrset_add_ns(struct delegpt* dp, struct regional* region,
|
||||
struct packed_rrset_data* nsdata = (struct packed_rrset_data*)
|
||||
ns_rrset->entry.data;
|
||||
size_t i;
|
||||
if(nsdata->security == sec_status_bogus)
|
||||
dp->bogus = 1;
|
||||
for(i=0; i<nsdata->count; i++) {
|
||||
if(nsdata->rr_len[i] < 2+1) continue; /* len + root label */
|
||||
if(dname_valid(nsdata->rr_data[i]+2, nsdata->rr_len[i]-2) !=
|
||||
@@ -326,7 +364,7 @@ delegpt_rrset_add_ns(struct delegpt* dp, struct regional* region,
|
||||
|
||||
int
|
||||
delegpt_add_rrset_A(struct delegpt* dp, struct regional* region,
|
||||
struct ub_packed_rrset_key* ak)
|
||||
struct ub_packed_rrset_key* ak, int nodup)
|
||||
{
|
||||
struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
|
||||
size_t i;
|
||||
@@ -341,7 +379,7 @@ delegpt_add_rrset_A(struct delegpt* dp, struct regional* region,
|
||||
memmove(&sa.sin_addr, d->rr_data[i]+2, INET_SIZE);
|
||||
if(!delegpt_add_target(dp, region, ak->rk.dname,
|
||||
ak->rk.dname_len, (struct sockaddr_storage*)&sa,
|
||||
len))
|
||||
len, (d->security==sec_status_bogus), nodup))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@@ -349,7 +387,7 @@ delegpt_add_rrset_A(struct delegpt* dp, struct regional* region,
|
||||
|
||||
int
|
||||
delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region,
|
||||
struct ub_packed_rrset_key* ak)
|
||||
struct ub_packed_rrset_key* ak, int nodup)
|
||||
{
|
||||
struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
|
||||
size_t i;
|
||||
@@ -364,7 +402,7 @@ delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region,
|
||||
memmove(&sa.sin6_addr, d->rr_data[i]+2, INET6_SIZE);
|
||||
if(!delegpt_add_target(dp, region, ak->rk.dname,
|
||||
ak->rk.dname_len, (struct sockaddr_storage*)&sa,
|
||||
len))
|
||||
len, (d->security==sec_status_bogus), nodup))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@@ -379,9 +417,29 @@ delegpt_add_rrset(struct delegpt* dp, struct regional* region,
|
||||
if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_NS)
|
||||
return delegpt_rrset_add_ns(dp, region, rrset);
|
||||
else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_A)
|
||||
return delegpt_add_rrset_A(dp, region, rrset);
|
||||
return delegpt_add_rrset_A(dp, region, rrset, 0);
|
||||
else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_AAAA)
|
||||
return delegpt_add_rrset_AAAA(dp, region, rrset);
|
||||
return delegpt_add_rrset_AAAA(dp, region, rrset, 0);
|
||||
log_warn("Unknown rrset type added to delegpt");
|
||||
return 1;
|
||||
}
|
||||
|
||||
void delegpt_add_neg_msg(struct delegpt* dp, struct msgreply_entry* msg)
|
||||
{
|
||||
struct reply_info* rep = (struct reply_info*)msg->entry.data;
|
||||
if(!rep) return;
|
||||
|
||||
/* if error or no answers */
|
||||
if(FLAGS_GET_RCODE(rep->flags) != 0 || rep->an_numrrsets == 0) {
|
||||
struct delegpt_ns* ns = delegpt_find_ns(dp, msg->key.qname,
|
||||
msg->key.qname_len);
|
||||
if(ns) {
|
||||
if(msg->key.qtype == LDNS_RR_TYPE_A)
|
||||
ns->got4 = 1;
|
||||
else if(msg->key.qtype == LDNS_RR_TYPE_AAAA)
|
||||
ns->got6 = 1;
|
||||
if(ns->got4 && ns->got6)
|
||||
ns->resolved = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+52
-5
@@ -48,6 +48,7 @@ struct delegpt_ns;
|
||||
struct delegpt_addr;
|
||||
struct dns_msg;
|
||||
struct ub_packed_rrset_key;
|
||||
struct msgreply_entry;
|
||||
|
||||
/**
|
||||
* Delegation Point.
|
||||
@@ -70,6 +71,9 @@ struct delegpt {
|
||||
struct delegpt_addr* usable_list;
|
||||
/** the list of returned targets; subset of target_list */
|
||||
struct delegpt_addr* result_list;
|
||||
|
||||
/** if true, the NS RRset was bogus. All info is bad. */
|
||||
int bogus;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -84,9 +88,15 @@ struct delegpt_ns {
|
||||
size_t namelen;
|
||||
/**
|
||||
* If the name has been resolved. false if not queried for yet.
|
||||
* true if the address is known, or marked true if failed.
|
||||
* true if the A, AAAA queries have been generated.
|
||||
* marked true if those queries fail.
|
||||
* and marked true is got4 and got6 are both true.
|
||||
*/
|
||||
int resolved;
|
||||
/** if the ipv4 address is in the delegpt */
|
||||
uint8_t got4;
|
||||
/** if the ipv6 address is in the delegpt */
|
||||
uint8_t got6;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -106,6 +116,11 @@ struct delegpt_addr {
|
||||
socklen_t addrlen;
|
||||
/** number of attempts for this addr */
|
||||
int attempts;
|
||||
/** rtt stored here in the selection algorithm */
|
||||
int sel_rtt;
|
||||
/** if true, the A or AAAA RR was bogus, so this address is bad.
|
||||
* Also check the dp->bogus to see if everything is bogus. */
|
||||
int bogus;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -162,34 +177,40 @@ int delegpt_rrset_add_ns(struct delegpt* dp, struct regional* regional,
|
||||
* @param namelen: length of name.
|
||||
* @param addr: the address.
|
||||
* @param addrlen: the length of addr.
|
||||
* @param bogus: security status for the address, pass true if bogus.
|
||||
* @param nodup: if true, no address duplicates are made by this add.
|
||||
* name duplicates are always filtered.
|
||||
* @return false on error.
|
||||
*/
|
||||
int delegpt_add_target(struct delegpt* dp, struct regional* regional,
|
||||
uint8_t* name, size_t namelen, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen);
|
||||
socklen_t addrlen, int bogus, int nodup);
|
||||
|
||||
/**
|
||||
* Add A RRset to delegpt.
|
||||
* @param dp: delegation point.
|
||||
* @param regional: where to allocate the info.
|
||||
* @param rrset: RRset A to add.
|
||||
* @param nodup: if true, no duplicates are made by this add. takes time.
|
||||
* @return 0 on alloc error.
|
||||
*/
|
||||
int delegpt_add_rrset_A(struct delegpt* dp, struct regional* regional,
|
||||
struct ub_packed_rrset_key* rrset);
|
||||
struct ub_packed_rrset_key* rrset, int nodup);
|
||||
|
||||
/**
|
||||
* Add AAAA RRset to delegpt.
|
||||
* @param dp: delegation point.
|
||||
* @param regional: where to allocate the info.
|
||||
* @param rrset: RRset AAAA to add.
|
||||
* @param nodup: if true, no duplicates are made by this add. takes time.
|
||||
* @return 0 on alloc error.
|
||||
*/
|
||||
int delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* regional,
|
||||
struct ub_packed_rrset_key* rrset);
|
||||
struct ub_packed_rrset_key* rrset, int nodup);
|
||||
|
||||
/**
|
||||
* Add any RRset to delegpt.
|
||||
* Does not check for duplicates added.
|
||||
* @param dp: delegation point.
|
||||
* @param regional: where to allocate the info.
|
||||
* @param rrset: RRset to add, NS, A, AAAA.
|
||||
@@ -204,10 +225,12 @@ int delegpt_add_rrset(struct delegpt* dp, struct regional* regional,
|
||||
* @param regional: where to allocate the info.
|
||||
* @param addr: the address.
|
||||
* @param addrlen: the length of addr.
|
||||
* @param bogus: if address is bogus.
|
||||
* @param nodup: if true, no duplicates are made by this add. takes time.
|
||||
* @return false on error.
|
||||
*/
|
||||
int delegpt_add_addr(struct delegpt* dp, struct regional* regional,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen);
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int bogus, int nodup);
|
||||
|
||||
/**
|
||||
* Find NS record in name list of delegation point.
|
||||
@@ -219,6 +242,16 @@ int delegpt_add_addr(struct delegpt* dp, struct regional* regional,
|
||||
struct delegpt_ns* delegpt_find_ns(struct delegpt* dp, uint8_t* name,
|
||||
size_t namelen);
|
||||
|
||||
/**
|
||||
* Find address record in total list of delegation point.
|
||||
* @param dp: delegation point.
|
||||
* @param addr: address
|
||||
* @param addrlen: length of addr
|
||||
* @return the addr structure or NULL if not found.
|
||||
*/
|
||||
struct delegpt_addr* delegpt_find_addr(struct delegpt* dp,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen);
|
||||
|
||||
/**
|
||||
* Print the delegation point to the log. For debugging.
|
||||
* @param v: verbosity value that is needed to emit to log.
|
||||
@@ -226,6 +259,13 @@ struct delegpt_ns* delegpt_find_ns(struct delegpt* dp, uint8_t* name,
|
||||
*/
|
||||
void delegpt_log(enum verbosity_value v, struct delegpt* dp);
|
||||
|
||||
/** count NS and number missing for logging */
|
||||
void delegpt_count_ns(struct delegpt* dp, size_t* numns, size_t* missing);
|
||||
|
||||
/** count addresses, and number in result and available lists, for logging */
|
||||
void delegpt_count_addr(struct delegpt* dp, size_t* numaddr, size_t* numres,
|
||||
size_t* numavail);
|
||||
|
||||
/**
|
||||
* Add all usable targets to the result list.
|
||||
* @param dp: delegation point.
|
||||
@@ -260,4 +300,11 @@ size_t delegpt_count_missing_targets(struct delegpt* dp);
|
||||
struct delegpt* delegpt_from_message(struct dns_msg* msg,
|
||||
struct regional* regional);
|
||||
|
||||
/**
|
||||
* Add negative message to delegation point.
|
||||
* @param dp: delegation point.
|
||||
* @param msg: the message added, marks off A or AAAA from an NS entry.
|
||||
*/
|
||||
void delegpt_add_neg_msg(struct delegpt* dp, struct msgreply_entry* msg);
|
||||
|
||||
#endif /* ITERATOR_ITER_DELEGPT_H */
|
||||
|
||||
+15
-171
@@ -48,21 +48,6 @@
|
||||
#include "util/config_file.h"
|
||||
#include "util/net_help.h"
|
||||
|
||||
int
|
||||
donotq_cmp(const void* k1, const void* k2)
|
||||
{
|
||||
struct iter_donotq_addr* n1 = (struct iter_donotq_addr*)k1;
|
||||
struct iter_donotq_addr* n2 = (struct iter_donotq_addr*)k2;
|
||||
int r = sockaddr_cmp_addr(&n1->addr, n1->addrlen, &n2->addr,
|
||||
n2->addrlen);
|
||||
if(r != 0) return r;
|
||||
if(n1->net < n2->net)
|
||||
return -1;
|
||||
if(n1->net > n2->net)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct iter_donotq*
|
||||
donotq_create()
|
||||
{
|
||||
@@ -84,7 +69,6 @@ donotq_delete(struct iter_donotq* dq)
|
||||
if(!dq)
|
||||
return;
|
||||
regional_destroy(dq->region);
|
||||
free(dq->tree);
|
||||
free(dq);
|
||||
}
|
||||
|
||||
@@ -93,78 +77,28 @@ static int
|
||||
donotq_insert(struct iter_donotq* dq, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, int net)
|
||||
{
|
||||
struct iter_donotq_addr* node = regional_alloc(dq->region,
|
||||
sizeof(struct iter_donotq_addr));
|
||||
struct addr_tree_node* node = (struct addr_tree_node*)regional_alloc(
|
||||
dq->region, sizeof(*node));
|
||||
if(!node)
|
||||
return 0;
|
||||
node->node.key = node;
|
||||
memcpy(&node->addr, addr, addrlen);
|
||||
node->addrlen = addrlen;
|
||||
node->net = net;
|
||||
node->parent = NULL;
|
||||
if(!rbtree_insert(dq->tree, &node->node)) {
|
||||
verbose(VERB_DETAIL, "duplicate donotquery address ignored.");
|
||||
if(!addr_tree_insert(&dq->tree, node, addr, addrlen, net)) {
|
||||
verbose(VERB_QUERY, "duplicate donotquery address ignored.");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** make sure the netblock ends in zeroes for compare in tree */
|
||||
static void
|
||||
mask_block(int ip6, struct sockaddr_storage* addr, int net)
|
||||
{
|
||||
uint8_t mask[8] = {0x0, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f};
|
||||
int i, max;
|
||||
uint8_t* s;
|
||||
if(ip6) {
|
||||
s = (uint8_t*)&((struct sockaddr_in6*)addr)->sin6_addr;
|
||||
max = 128;
|
||||
} else {
|
||||
s = (uint8_t*)&((struct sockaddr_in*)addr)->sin_addr;
|
||||
max = 32;
|
||||
}
|
||||
if(net >= max)
|
||||
return;
|
||||
for(i=net/8+1; i<max/8; i++) {
|
||||
s[i] = 0;
|
||||
}
|
||||
s[net/8] &= mask[net&0x7];
|
||||
}
|
||||
|
||||
/** apply donotq string */
|
||||
static int
|
||||
donotq_str_cfg(struct iter_donotq* dq, const char* str)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
int net;
|
||||
char* s = NULL;
|
||||
socklen_t addrlen;
|
||||
net = (str_is_ip6(str)?128:32);
|
||||
if((s=strchr(str, '/'))) {
|
||||
if(atoi(s+1) > net) {
|
||||
log_err("netblock too large: %s", str);
|
||||
return 0;
|
||||
}
|
||||
net = atoi(s+1);
|
||||
if(net == 0 && strcmp(s+1, "0") != 0) {
|
||||
log_err("cannot parse donotquery netblock:"
|
||||
" '%s'", str);
|
||||
return 0;
|
||||
}
|
||||
if(!(s = strdup(str))) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
*strchr(s, '/') = '\0';
|
||||
}
|
||||
if(!ipstrtoaddr(s?s:str, UNBOUND_DNS_PORT, &addr, &addrlen)) {
|
||||
free(s);
|
||||
log_err("cannot parse donotquery ip address: '%s'", str);
|
||||
verbose(VERB_ALGO, "donotq: %s", str);
|
||||
if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
|
||||
log_err("cannot parse donotquery netblock: %s", str);
|
||||
return 0;
|
||||
}
|
||||
if(s) {
|
||||
free(s);
|
||||
mask_block(str_is_ip6(str), &addr, net);
|
||||
}
|
||||
if(!donotq_insert(dq, &addr, addrlen, net)) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
@@ -185,86 +119,22 @@ read_donotq(struct iter_donotq* dq, struct config_file* cfg)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** number of bits that two addrs share (are equal) */
|
||||
static int
|
||||
addr_in_common(struct sockaddr_storage* addr1, int net1,
|
||||
struct sockaddr_storage* addr2, int net2, socklen_t addrlen)
|
||||
{
|
||||
int min = (net1<net2)?net1:net2;
|
||||
int i, to;
|
||||
int match = 0;
|
||||
uint8_t* s1, *s2;
|
||||
if(addr_is_ip6(addr1, addrlen)) {
|
||||
s1 = (uint8_t*)&((struct sockaddr_in6*)addr1)->sin6_addr;
|
||||
s2 = (uint8_t*)&((struct sockaddr_in6*)addr2)->sin6_addr;
|
||||
to = 16;
|
||||
} else {
|
||||
s1 = (uint8_t*)&((struct sockaddr_in*)addr1)->sin_addr;
|
||||
s2 = (uint8_t*)&((struct sockaddr_in*)addr2)->sin_addr;
|
||||
to = 4;
|
||||
}
|
||||
/* match = bits_in_common(s1, s2, to); */
|
||||
for(i=0; i<to; i++) {
|
||||
if(s1[i] == s2[i]) {
|
||||
match += 8;
|
||||
} else {
|
||||
uint8_t z = s1[i]^s2[i];
|
||||
log_assert(z);
|
||||
while(!(z&0x80)) {
|
||||
match++;
|
||||
z<<=1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(match > min) match = min;
|
||||
return match;
|
||||
}
|
||||
|
||||
/** initialise parent pointers in the tree */
|
||||
static void
|
||||
donotq_init_parents(struct iter_donotq* donotq)
|
||||
{
|
||||
struct iter_donotq_addr* node, *prev = NULL, *p;
|
||||
int m;
|
||||
RBTREE_FOR(node, struct iter_donotq_addr*, donotq->tree) {
|
||||
node->parent = NULL;
|
||||
if(!prev || prev->addrlen != node->addrlen) {
|
||||
prev = node;
|
||||
continue;
|
||||
}
|
||||
m = addr_in_common(&prev->addr, prev->net, &node->addr,
|
||||
node->net, node->addrlen);
|
||||
/* sort order like: ::/0, 1::/2, 1::/4, ... 2::/2 */
|
||||
/* find the previous, or parent-parent-parent */
|
||||
for(p = prev; p; p = p->parent)
|
||||
if(p->net <= m) {
|
||||
/* ==: since prev matched m, this is closest*/
|
||||
/* <: prev matches more, but is not a parent,
|
||||
* this one is a (grand)parent */
|
||||
node->parent = p;
|
||||
break;
|
||||
}
|
||||
prev = node;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
donotq_apply_cfg(struct iter_donotq* dq, struct config_file* cfg)
|
||||
{
|
||||
free(dq->tree);
|
||||
dq->tree = rbtree_create(donotq_cmp);
|
||||
if(!dq->tree)
|
||||
return 0;
|
||||
regional_free_all(dq->region);
|
||||
addr_tree_init(&dq->tree);
|
||||
if(!read_donotq(dq, cfg))
|
||||
return 0;
|
||||
if(cfg->donotquery_localhost) {
|
||||
if(!donotq_str_cfg(dq, "127.0.0.0/8"))
|
||||
return 0;
|
||||
if(!donotq_str_cfg(dq, "::1"))
|
||||
return 0;
|
||||
if(cfg->do_ip6) {
|
||||
if(!donotq_str_cfg(dq, "::1"))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
donotq_init_parents(dq);
|
||||
addr_tree_init_parents(&dq->tree);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -272,33 +142,7 @@ int
|
||||
donotq_lookup(struct iter_donotq* donotq, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
/* lookup in the tree */
|
||||
rbnode_t* res = NULL;
|
||||
struct iter_donotq_addr* result;
|
||||
struct iter_donotq_addr key;
|
||||
key.node.key = &key;
|
||||
memcpy(&key.addr, addr, addrlen);
|
||||
key.addrlen = addrlen;
|
||||
key.net = (addr_is_ip6(addr, addrlen)?128:32);
|
||||
if(rbtree_find_less_equal(donotq->tree, &key, &res)) {
|
||||
/* exact */
|
||||
return 1;
|
||||
} else {
|
||||
/* smaller element (or no element) */
|
||||
int m;
|
||||
result = (struct iter_donotq_addr*)res;
|
||||
if(!result || result->addrlen != addrlen)
|
||||
return 0;
|
||||
/* count number of bits matched */
|
||||
m = addr_in_common(&result->addr, result->net, addr,
|
||||
key.net, addrlen);
|
||||
while(result) { /* go up until addr is inside netblock */
|
||||
if(result->net <= m)
|
||||
return 1;
|
||||
result = result->parent;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return addr_tree_lookup(&donotq->tree, addr, addrlen) != NULL;
|
||||
}
|
||||
|
||||
size_t
|
||||
|
||||
+5
-24
@@ -42,7 +42,7 @@
|
||||
|
||||
#ifndef ITERATOR_ITER_DONOTQ_H
|
||||
#define ITERATOR_ITER_DONOTQ_H
|
||||
#include "util/rbtree.h"
|
||||
#include "util/storage/dnstree.h"
|
||||
struct iter_env;
|
||||
struct config_file;
|
||||
struct regional;
|
||||
@@ -54,27 +54,11 @@ struct iter_donotq {
|
||||
/** regional for allocation */
|
||||
struct regional* region;
|
||||
/**
|
||||
* Tree of the single addresses that are blocked.
|
||||
* contents of type iter_donotq_addr.
|
||||
* Tree of the address spans that are blocked.
|
||||
* contents of type addr_tree_node. Each node is an address span
|
||||
* that must not be used to send queries to.
|
||||
*/
|
||||
rbtree_t* tree;
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterator donotquery address.
|
||||
* A single address that must not be used to send queries to.
|
||||
*/
|
||||
struct iter_donotq_addr {
|
||||
/** redblacktree node, key is this structure: addr and addrlen */
|
||||
rbnode_t node;
|
||||
/** address */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr */
|
||||
socklen_t addrlen;
|
||||
/** netblock size */
|
||||
int net;
|
||||
/** parent node in donotq tree that encompasses this entry */
|
||||
struct iter_donotq_addr* parent;
|
||||
rbtree_t tree;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -114,7 +98,4 @@ int donotq_lookup(struct iter_donotq* donotq, struct sockaddr_storage* addr,
|
||||
*/
|
||||
size_t donotq_get_mem(struct iter_donotq* donotq);
|
||||
|
||||
/** compare two donotq entries */
|
||||
int donotq_cmp(const void* k1, const void* k2);
|
||||
|
||||
#endif /* ITERATOR_ITER_DONOTQ_H */
|
||||
|
||||
+3
-3
@@ -203,7 +203,7 @@ read_fwds_addr(struct iter_forwards* fwd, struct config_stub* s,
|
||||
s->name, p->str);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_add_addr(dp, fwd->region, &addr, addrlen)) {
|
||||
if(!delegpt_add_addr(dp, fwd->region, &addr, addrlen, 0, 1)) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
@@ -228,8 +228,8 @@ read_forwards(struct iter_forwards* fwd, struct config_file* cfg)
|
||||
return 0;
|
||||
if(!forwards_insert(fwd, LDNS_RR_CLASS_IN, dp))
|
||||
return 0;
|
||||
verbose(VERB_DETAIL, "Forward zone server list:");
|
||||
delegpt_log(VERB_DETAIL, dp);
|
||||
verbose(VERB_QUERY, "Forward zone server list:");
|
||||
delegpt_log(VERB_QUERY, dp);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
+84
-121
@@ -48,21 +48,6 @@
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/dname.h"
|
||||
|
||||
int
|
||||
stub_cmp(const void* k1, const void* k2)
|
||||
{
|
||||
int m;
|
||||
struct iter_hints_stub* n1 = (struct iter_hints_stub*)k1;
|
||||
struct iter_hints_stub* n2 = (struct iter_hints_stub*)k2;
|
||||
if(n1->hint_class != n2->hint_class) {
|
||||
if(n1->hint_class < n2->hint_class)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
return dname_lab_cmp(n1->name, n1->namelabs, n2->name, n2->namelabs,
|
||||
&m);
|
||||
}
|
||||
|
||||
struct iter_hints*
|
||||
hints_create()
|
||||
{
|
||||
@@ -84,7 +69,6 @@ hints_delete(struct iter_hints* hints)
|
||||
if(!hints)
|
||||
return;
|
||||
regional_destroy(hints->region);
|
||||
free(hints->tree);
|
||||
free(hints);
|
||||
}
|
||||
|
||||
@@ -102,7 +86,7 @@ ah(struct delegpt* dp, struct regional* r, const char* sv, const char* ip)
|
||||
if(!delegpt_add_ns(dp, r, ldns_rdf_data(rdf)) ||
|
||||
!extstrtoaddr(ip, &addr, &addrlen) ||
|
||||
!delegpt_add_target(dp, r, ldns_rdf_data(rdf), ldns_rdf_size(rdf),
|
||||
&addr, addrlen)) {
|
||||
&addr, addrlen, 0, 1)) {
|
||||
ldns_rdf_deep_free(rdf);
|
||||
return 0;
|
||||
}
|
||||
@@ -112,7 +96,7 @@ ah(struct delegpt* dp, struct regional* r, const char* sv, const char* ip)
|
||||
|
||||
/** obtain compiletime provided root hints */
|
||||
static struct delegpt*
|
||||
compile_time_root_prime(struct regional* r)
|
||||
compile_time_root_prime(struct regional* r, int do_ip4, int do_ip6)
|
||||
{
|
||||
/* from:
|
||||
; This file is made available by InterNIC
|
||||
@@ -121,14 +105,14 @@ compile_time_root_prime(struct regional* r)
|
||||
; on server FTP.INTERNIC.NET
|
||||
; -OR- RS.INTERNIC.NET
|
||||
;
|
||||
; last update: Jan 29, 2004
|
||||
; related version of root zone: 2004012900
|
||||
; related version of root zone: 2008051300
|
||||
*/
|
||||
struct delegpt* dp = delegpt_create(r);
|
||||
if(!dp)
|
||||
return NULL;
|
||||
if(!delegpt_set_name(dp, r, (uint8_t*)"\000"))
|
||||
return NULL;
|
||||
if(do_ip4) {
|
||||
if(!ah(dp, r, "A.ROOT-SERVERS.NET.", "198.41.0.4")) return 0;
|
||||
if(!ah(dp, r, "B.ROOT-SERVERS.NET.", "192.228.79.201")) return 0;
|
||||
if(!ah(dp, r, "C.ROOT-SERVERS.NET.", "192.33.4.12")) return 0;
|
||||
@@ -142,60 +126,41 @@ compile_time_root_prime(struct regional* r)
|
||||
if(!ah(dp, r, "K.ROOT-SERVERS.NET.", "193.0.14.129")) return 0;
|
||||
if(!ah(dp, r, "L.ROOT-SERVERS.NET.", "199.7.83.42")) return 0;
|
||||
if(!ah(dp, r, "M.ROOT-SERVERS.NET.", "202.12.27.33")) return 0;
|
||||
}
|
||||
if(do_ip6) {
|
||||
if(!ah(dp, r, "A.ROOT-SERVERS.NET.", "2001:503:ba3e::2:30")) return 0;
|
||||
if(!ah(dp, r, "F.ROOT-SERVERS.NET.", "2001:500:2f::f")) return 0;
|
||||
if(!ah(dp, r, "H.ROOT-SERVERS.NET.", "2001:500:1::803f:235")) return 0;
|
||||
if(!ah(dp, r, "J.ROOT-SERVERS.NET.", "2001:503:c27::2:30")) return 0;
|
||||
if(!ah(dp, r, "K.ROOT-SERVERS.NET.", "2001:7fd::1")) return 0;
|
||||
if(!ah(dp, r, "L.ROOT-SERVERS.NET.", "2001:500:3::42")) return 0;
|
||||
if(!ah(dp, r, "M.ROOT-SERVERS.NET.", "2001:dc3::35")) return 0;
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
/** insert new hint info into hint structure */
|
||||
static int
|
||||
hints_insert(struct iter_hints* hints, uint16_t c, struct delegpt* dp)
|
||||
hints_insert(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
|
||||
int noprime)
|
||||
{
|
||||
struct iter_hints_stub* node = regional_alloc(hints->region,
|
||||
sizeof(struct iter_hints_stub));
|
||||
uint8_t* nm;
|
||||
if(!node)
|
||||
return 0;
|
||||
node->node.key = node;
|
||||
node->hint_class = c;
|
||||
node->name = regional_alloc_init(hints->region, dp->name, dp->namelen);
|
||||
if(!node->name)
|
||||
nm = regional_alloc_init(hints->region, dp->name, dp->namelen);
|
||||
if(!nm)
|
||||
return 0;
|
||||
node->namelen = dp->namelen;
|
||||
node->namelabs = dp->namelabs;
|
||||
node->dp = dp;
|
||||
if(!rbtree_insert(hints->tree, &node->node)) {
|
||||
node->noprime = (uint8_t)noprime;
|
||||
if(!name_tree_insert(&hints->tree, &node->node, nm, dp->namelen,
|
||||
dp->namelabs, c)) {
|
||||
log_err("second hints ignored.");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** initialise parent pointers in the tree */
|
||||
static void
|
||||
init_parents(struct iter_hints* hints)
|
||||
{
|
||||
struct iter_hints_stub* node, *prev = NULL, *p;
|
||||
int m;
|
||||
RBTREE_FOR(node, struct iter_hints_stub*, hints->tree) {
|
||||
node->parent = NULL;
|
||||
if(!prev || prev->hint_class != node->hint_class) {
|
||||
prev = node;
|
||||
continue;
|
||||
}
|
||||
(void)dname_lab_cmp(prev->name, prev->namelabs, node->name,
|
||||
node->namelabs, &m); /* we know prev is smaller */
|
||||
/* sort order like: . com. bla.com. zwb.com. net. */
|
||||
/* find the previous, or parent-parent-parent */
|
||||
for(p = prev; p; p = p->parent)
|
||||
/* looking for name with few labels, a parent */
|
||||
if(p->namelabs <= m) {
|
||||
/* ==: since prev matched m, this is closest*/
|
||||
/* <: prev matches more, but is not a parent,
|
||||
* this one is a (grand)parent */
|
||||
node->parent = p;
|
||||
break;
|
||||
}
|
||||
prev = node;
|
||||
}
|
||||
}
|
||||
|
||||
/** set stub name */
|
||||
static int
|
||||
read_stubs_name(struct iter_hints* hints, struct config_stub* s,
|
||||
@@ -260,7 +225,7 @@ read_stubs_addr(struct iter_hints* hints, struct config_stub* s,
|
||||
s->name, p->str);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_add_addr(dp, hints->region, &addr, addrlen)) {
|
||||
if(!delegpt_add_addr(dp, hints->region, &addr, addrlen, 0, 1)) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
@@ -283,9 +248,9 @@ read_stubs(struct iter_hints* hints, struct config_file* cfg)
|
||||
!read_stubs_host(hints, s, dp) ||
|
||||
!read_stubs_addr(hints, s, dp))
|
||||
return 0;
|
||||
if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp))
|
||||
if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, !s->isprime))
|
||||
return 0;
|
||||
delegpt_log(VERB_DETAIL, dp);
|
||||
delegpt_log(VERB_QUERY, dp);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -314,7 +279,7 @@ read_root_hints(struct iter_hints* hints, char* fname)
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
verbose(VERB_DETAIL, "Reading root hints from %s", fname);
|
||||
verbose(VERB_QUERY, "Reading root hints from %s", fname);
|
||||
while(!feof(f)) {
|
||||
status = ldns_rr_new_frm_fp_l(&rr, f,
|
||||
&default_ttl, &origin, &prev_rr, &lineno);
|
||||
@@ -325,23 +290,20 @@ read_root_hints(struct iter_hints* hints, char* fname)
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
log_err("reading root hints %s %d: %s", fname,
|
||||
lineno, ldns_get_errorstr_by_id(status));
|
||||
fclose(f);
|
||||
return 0;
|
||||
goto stop_read;
|
||||
}
|
||||
if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_NS) {
|
||||
if(!delegpt_add_ns(dp, hints->region,
|
||||
ldns_rdf_data(ldns_rr_rdf(rr, 0)))) {
|
||||
log_err("out of memory reading root hints");
|
||||
fclose(f);
|
||||
return 0;
|
||||
goto stop_read;
|
||||
}
|
||||
c = ldns_rr_get_class(rr);
|
||||
if(!dp->name) {
|
||||
if(!delegpt_set_name(dp, hints->region,
|
||||
ldns_rdf_data(ldns_rr_owner(rr)))){
|
||||
log_err("out of memory.");
|
||||
fclose(f);
|
||||
return 0;
|
||||
goto stop_read;
|
||||
}
|
||||
}
|
||||
} else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_A) {
|
||||
@@ -355,10 +317,10 @@ read_root_hints(struct iter_hints* hints, char* fname)
|
||||
if(!delegpt_add_target(dp, hints->region,
|
||||
ldns_rdf_data(ldns_rr_owner(rr)),
|
||||
ldns_rdf_size(ldns_rr_owner(rr)),
|
||||
(struct sockaddr_storage*)&sa, len)) {
|
||||
(struct sockaddr_storage*)&sa, len,
|
||||
0, 1)) {
|
||||
log_err("out of memory reading root hints");
|
||||
fclose(f);
|
||||
return 0;
|
||||
goto stop_read;
|
||||
}
|
||||
} else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA) {
|
||||
struct sockaddr_in6 sa;
|
||||
@@ -371,10 +333,10 @@ read_root_hints(struct iter_hints* hints, char* fname)
|
||||
if(!delegpt_add_target(dp, hints->region,
|
||||
ldns_rdf_data(ldns_rr_owner(rr)),
|
||||
ldns_rdf_size(ldns_rr_owner(rr)),
|
||||
(struct sockaddr_storage*)&sa, len)) {
|
||||
(struct sockaddr_storage*)&sa, len,
|
||||
0, 1)) {
|
||||
log_err("out of memory reading root hints");
|
||||
fclose(f);
|
||||
return 0;
|
||||
goto stop_read;
|
||||
}
|
||||
} else {
|
||||
log_warn("root hints %s:%d skipping type %d",
|
||||
@@ -383,16 +345,29 @@ read_root_hints(struct iter_hints* hints, char* fname)
|
||||
|
||||
ldns_rr_free(rr);
|
||||
}
|
||||
|
||||
if (origin)
|
||||
ldns_rdf_deep_free(origin);
|
||||
if (prev_rr)
|
||||
ldns_rdf_deep_free(prev_rr);
|
||||
fclose(f);
|
||||
if(!dp->name) {
|
||||
log_warn("root hints %s: no NS content", fname);
|
||||
return 1;
|
||||
}
|
||||
if(!hints_insert(hints, c, dp)) {
|
||||
if(!hints_insert(hints, c, dp, 0)) {
|
||||
return 0;
|
||||
}
|
||||
delegpt_log(VERB_DETAIL, dp);
|
||||
delegpt_log(VERB_QUERY, dp);
|
||||
return 1;
|
||||
|
||||
stop_read:
|
||||
if (origin)
|
||||
ldns_rdf_deep_free(origin);
|
||||
if (prev_rr)
|
||||
ldns_rdf_deep_free(prev_rr);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** read root hints list */
|
||||
@@ -403,7 +378,12 @@ read_root_hints_list(struct iter_hints* hints, struct config_file* cfg)
|
||||
for(p = cfg->root_hints; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
if(p->str && p->str[0]) {
|
||||
if(!read_root_hints(hints, p->str))
|
||||
char* f = p->str;
|
||||
if(cfg->chrootdir && cfg->chrootdir[0] &&
|
||||
strncmp(p->str, cfg->chrootdir,
|
||||
strlen(cfg->chrootdir)) == 0)
|
||||
f += strlen(cfg->chrootdir);
|
||||
if(!read_root_hints(hints, f))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -413,10 +393,8 @@ read_root_hints_list(struct iter_hints* hints, struct config_file* cfg)
|
||||
int
|
||||
hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg)
|
||||
{
|
||||
free(hints->tree);
|
||||
hints->tree = rbtree_create(stub_cmp);
|
||||
if(!hints->tree)
|
||||
return 0;
|
||||
regional_free_all(hints->region);
|
||||
name_tree_init(&hints->tree);
|
||||
|
||||
/* read root hints */
|
||||
if(!read_root_hints_list(hints, cfg))
|
||||
@@ -428,16 +406,16 @@ hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg)
|
||||
|
||||
/* use fallback compiletime root hints */
|
||||
if(!hints_lookup_root(hints, LDNS_RR_CLASS_IN)) {
|
||||
struct delegpt* dp = compile_time_root_prime(hints->region);
|
||||
struct delegpt* dp = compile_time_root_prime(hints->region,
|
||||
cfg->do_ip4, cfg->do_ip6);
|
||||
verbose(VERB_ALGO, "no config, using builtin root hints.");
|
||||
if(!dp)
|
||||
return 0;
|
||||
if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp))
|
||||
if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, 0))
|
||||
return 0;
|
||||
delegpt_log(VERB_DETAIL, dp);
|
||||
}
|
||||
|
||||
init_parents(hints);
|
||||
name_tree_init_parents(&hints->tree);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -445,56 +423,41 @@ struct delegpt*
|
||||
hints_lookup_root(struct iter_hints* hints, uint16_t qclass)
|
||||
{
|
||||
uint8_t rootlab = 0;
|
||||
struct iter_hints_stub key, *stub;
|
||||
key.node.key = &key;
|
||||
key.hint_class = qclass;
|
||||
key.name = &rootlab;
|
||||
key.namelen = 1;
|
||||
key.namelabs = 1;
|
||||
stub = (struct iter_hints_stub*)rbtree_search(hints->tree, &key);
|
||||
struct iter_hints_stub *stub;
|
||||
stub = (struct iter_hints_stub*)name_tree_find(&hints->tree,
|
||||
&rootlab, 1, 1, qclass);
|
||||
if(!stub)
|
||||
return NULL;
|
||||
return stub->dp;
|
||||
}
|
||||
|
||||
struct delegpt*
|
||||
struct iter_hints_stub*
|
||||
hints_lookup_stub(struct iter_hints* hints, uint8_t* qname,
|
||||
uint16_t qclass, struct delegpt* cache_dp)
|
||||
{
|
||||
size_t len;
|
||||
int labs;
|
||||
struct iter_hints_stub *r;
|
||||
|
||||
/* first lookup the stub */
|
||||
rbnode_t* res = NULL;
|
||||
struct iter_hints_stub *result;
|
||||
struct iter_hints_stub key;
|
||||
key.node.key = &key;
|
||||
key.hint_class = qclass;
|
||||
key.name = qname;
|
||||
key.namelabs = dname_count_size_labels(qname, &key.namelen);
|
||||
if(rbtree_find_less_equal(hints->tree, &key, &res)) {
|
||||
/* exact */
|
||||
result = (struct iter_hints_stub*)res;
|
||||
} else {
|
||||
/* smaller element (or no element) */
|
||||
int m;
|
||||
result = (struct iter_hints_stub*)res;
|
||||
if(!result || result->hint_class != qclass)
|
||||
return NULL;
|
||||
/* count number of labels matched */
|
||||
(void)dname_lab_cmp(result->name, result->namelabs, key.name,
|
||||
key.namelabs, &m);
|
||||
while(result) { /* go up until qname is subdomain of stub */
|
||||
if(result->namelabs <= m)
|
||||
break;
|
||||
result = result->parent;
|
||||
}
|
||||
if(!result)
|
||||
return NULL;
|
||||
}
|
||||
labs = dname_count_size_labels(qname, &len);
|
||||
r = (struct iter_hints_stub*)name_tree_lookup(&hints->tree, qname,
|
||||
len, labs, qclass);
|
||||
if(!r) return NULL;
|
||||
|
||||
/*
|
||||
* If the stub is same as the delegation we got
|
||||
* And has noprime set, we need to 'prime' to use this stub instead.
|
||||
*/
|
||||
if(r->noprime && query_dname_compare(cache_dp->name, r->dp->name)==0)
|
||||
return r; /* use this stub instead of cached dp */
|
||||
|
||||
/*
|
||||
* If our cached delegation point is above the hint, we need to prime.
|
||||
*/
|
||||
if(dname_strict_subdomain(result->dp->name, result->dp->namelabs,
|
||||
if(dname_strict_subdomain(r->dp->name, r->dp->namelabs,
|
||||
cache_dp->name, cache_dp->namelabs))
|
||||
return result->dp; /* need to prime this stub */
|
||||
return r; /* need to prime this stub */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
+8
-18
@@ -42,7 +42,7 @@
|
||||
|
||||
#ifndef ITERATOR_ITER_HINTS_H
|
||||
#define ITERATOR_ITER_HINTS_H
|
||||
#include "util/rbtree.h"
|
||||
#include "util/storage/dnstree.h"
|
||||
struct iter_env;
|
||||
struct config_file;
|
||||
struct delegpt;
|
||||
@@ -60,28 +60,21 @@ struct iter_hints {
|
||||
* a lookup on class, name will return an exact match or the closest
|
||||
* match which gives the ancestor needed.
|
||||
* contents of type iter_hints_stub. The class IN root is in here.
|
||||
* uses name_tree_node from dnstree.h.
|
||||
*/
|
||||
rbtree_t* tree;
|
||||
rbtree_t tree;
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterator hints for a particular stub.
|
||||
*/
|
||||
struct iter_hints_stub {
|
||||
/** redblacktree node, key is this structure: class and name */
|
||||
rbnode_t node;
|
||||
/** name */
|
||||
uint8_t* name;
|
||||
/** length of name */
|
||||
size_t namelen;
|
||||
/** number of labels in name */
|
||||
int namelabs;
|
||||
/** tree sorted by name, class */
|
||||
struct name_tree_node node;
|
||||
/** delegation point with hint information for this stub. */
|
||||
struct delegpt* dp;
|
||||
/** pointer to parent in stub hint tree (or NULL if none) */
|
||||
struct iter_hints_stub* parent;
|
||||
/** class of hints. host order. */
|
||||
uint16_t hint_class;
|
||||
/** does the stub need to forego priming (like on other ports) */
|
||||
uint8_t noprime;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -124,7 +117,7 @@ struct delegpt* hints_lookup_root(struct iter_hints* hints, uint16_t qclass);
|
||||
* @return: A priming delegation point if there is a stub hint that must
|
||||
* be primed, otherwise null.
|
||||
*/
|
||||
struct delegpt* hints_lookup_stub(struct iter_hints* hints,
|
||||
struct iter_hints_stub* hints_lookup_stub(struct iter_hints* hints,
|
||||
uint8_t* qname, uint16_t qclass, struct delegpt* dp);
|
||||
|
||||
/**
|
||||
@@ -134,7 +127,4 @@ struct delegpt* hints_lookup_stub(struct iter_hints* hints,
|
||||
*/
|
||||
size_t hints_get_mem(struct iter_hints* hints);
|
||||
|
||||
/** compare two hint entries */
|
||||
int stub_cmp(const void* k1, const void* k2);
|
||||
|
||||
#endif /* ITERATOR_ITER_HINTS_H */
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* iterator/iter_priv.c - iterative resolver private address and domain store
|
||||
*
|
||||
* Copyright (c) 2008, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to assist the iterator module.
|
||||
* Keep track of the private addresses and lookup fast.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "iterator/iter_priv.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/storage/dnstree.h"
|
||||
|
||||
struct iter_priv* priv_create()
|
||||
{
|
||||
struct iter_priv* priv = (struct iter_priv*)calloc(1, sizeof(*priv));
|
||||
if(!priv)
|
||||
return NULL;
|
||||
priv->region = regional_create();
|
||||
if(!priv->region) {
|
||||
priv_delete(priv);
|
||||
return NULL;
|
||||
}
|
||||
addr_tree_init(&priv->a);
|
||||
name_tree_init(&priv->n);
|
||||
return priv;
|
||||
}
|
||||
|
||||
void priv_delete(struct iter_priv* priv)
|
||||
{
|
||||
if(!priv) return;
|
||||
regional_destroy(priv->region);
|
||||
free(priv);
|
||||
}
|
||||
|
||||
/** Read private-addr declarations from config */
|
||||
static int read_addrs(struct iter_priv* priv, struct config_file* cfg)
|
||||
{
|
||||
/* parse addresses, report errors, insert into tree */
|
||||
struct config_strlist* p;
|
||||
struct addr_tree_node* n;
|
||||
struct sockaddr_storage addr;
|
||||
int net;
|
||||
socklen_t addrlen;
|
||||
|
||||
for(p = cfg->private_address; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
if(!netblockstrtoaddr(p->str, UNBOUND_DNS_PORT, &addr,
|
||||
&addrlen, &net)) {
|
||||
log_err("cannot parse private-address: %s", p->str);
|
||||
return 0;
|
||||
}
|
||||
n = (struct addr_tree_node*)regional_alloc(priv->region,
|
||||
sizeof(*n));
|
||||
if(!n) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
if(!addr_tree_insert(&priv->a, n, &addr, addrlen, net)) {
|
||||
verbose(VERB_QUERY, "ignoring duplicate "
|
||||
"private-address: %s", p->str);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Read private-domain declarations from config */
|
||||
static int read_names(struct iter_priv* priv, struct config_file* cfg)
|
||||
{
|
||||
/* parse names, report errors, insert into tree */
|
||||
struct config_strlist* p;
|
||||
struct name_tree_node* n;
|
||||
uint8_t* nm;
|
||||
size_t nm_len;
|
||||
int nm_labs;
|
||||
ldns_rdf* rdf;
|
||||
|
||||
for(p = cfg->private_domain; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
rdf = ldns_dname_new_frm_str(p->str);
|
||||
if(!rdf) {
|
||||
log_err("cannot parse private-domain: %s", p->str);
|
||||
return 0;
|
||||
}
|
||||
nm = ldns_rdf_data(rdf);
|
||||
nm_labs = dname_count_size_labels(nm, &nm_len);
|
||||
nm = (uint8_t*)regional_alloc_init(priv->region, nm, nm_len);
|
||||
ldns_rdf_deep_free(rdf);
|
||||
if(!nm) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
n = (struct name_tree_node*)regional_alloc(priv->region,
|
||||
sizeof(*n));
|
||||
if(!n) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
if(!name_tree_insert(&priv->n, n, nm, nm_len, nm_labs,
|
||||
LDNS_RR_CLASS_IN)) {
|
||||
verbose(VERB_QUERY, "ignoring duplicate "
|
||||
"private-domain: %s", p->str);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int priv_apply_cfg(struct iter_priv* priv, struct config_file* cfg)
|
||||
{
|
||||
/* empty the current contents */
|
||||
regional_free_all(priv->region);
|
||||
addr_tree_init(&priv->a);
|
||||
name_tree_init(&priv->n);
|
||||
|
||||
/* read new contents */
|
||||
if(!read_addrs(priv, cfg))
|
||||
return 0;
|
||||
if(!read_names(priv, cfg))
|
||||
return 0;
|
||||
|
||||
/* prepare for lookups */
|
||||
addr_tree_init_parents(&priv->a);
|
||||
name_tree_init_parents(&priv->n);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* See if an address is blocked.
|
||||
* @param priv: structure for address storage.
|
||||
* @param addr: address to check
|
||||
* @param addrlen: length of addr.
|
||||
* @return: true if the address must not be queried. false if unlisted.
|
||||
*/
|
||||
static int
|
||||
priv_lookup_addr(struct iter_priv* priv, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
return addr_tree_lookup(&priv->a, addr, addrlen) != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* See if a name is whitelisted.
|
||||
* @param priv: structure for address storage.
|
||||
* @param pkt: the packet (for compression ptrs).
|
||||
* @param name: name to check.
|
||||
* @param name_len: uncompressed length of the name to check.
|
||||
* @param dclass: class to check.
|
||||
* @return: true if the name is OK. false if unlisted.
|
||||
*/
|
||||
static int
|
||||
priv_lookup_name(struct iter_priv* priv, ldns_buffer* pkt,
|
||||
uint8_t* name, size_t name_len, uint16_t dclass)
|
||||
{
|
||||
size_t len;
|
||||
uint8_t decomp[256];
|
||||
int labs;
|
||||
if(name_len >= sizeof(decomp))
|
||||
return 0;
|
||||
dname_pkt_copy(pkt, decomp, name);
|
||||
labs = dname_count_size_labels(decomp, &len);
|
||||
log_assert(name_len == len);
|
||||
return name_tree_lookup(&priv->n, decomp, len, labs, dclass) != NULL;
|
||||
}
|
||||
|
||||
size_t priv_get_mem(struct iter_priv* priv)
|
||||
{
|
||||
if(!priv) return 0;
|
||||
return sizeof(*priv) + regional_get_mem(priv->region);
|
||||
}
|
||||
|
||||
int priv_rrset_bad(struct iter_priv* priv, ldns_buffer* pkt,
|
||||
struct rrset_parse* rrset)
|
||||
{
|
||||
/* see if it is a private name, that is allowed to have any */
|
||||
if(priv_lookup_name(priv, pkt, rrset->dname, rrset->dname_len,
|
||||
ntohs(rrset->rrset_class))) {
|
||||
return 0;
|
||||
} else {
|
||||
/* so its a public name, check the address */
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len;
|
||||
struct rr_parse* rr;
|
||||
if(rrset->type == LDNS_RR_TYPE_A) {
|
||||
struct sockaddr_in* sa = (struct sockaddr_in*)&addr;
|
||||
len = (socklen_t)sizeof(*sa);
|
||||
memset(sa, 0, len);
|
||||
sa->sin_family = AF_INET;
|
||||
sa->sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
|
||||
for(rr = rrset->rr_first; rr; rr = rr->next) {
|
||||
if(ldns_read_uint16(rr->ttl_data+4)
|
||||
!= INET_SIZE)
|
||||
continue;
|
||||
memmove(&sa->sin_addr, rr->ttl_data+4+2,
|
||||
INET_SIZE);
|
||||
if(priv_lookup_addr(priv, &addr, len))
|
||||
return 1;
|
||||
}
|
||||
} else if(rrset->type == LDNS_RR_TYPE_AAAA) {
|
||||
struct sockaddr_in6* sa = (struct sockaddr_in6*)&addr;
|
||||
len = (socklen_t)sizeof(*sa);
|
||||
memset(sa, 0, len);
|
||||
sa->sin6_family = AF_INET6;
|
||||
sa->sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
|
||||
for(rr = rrset->rr_first; rr; rr = rr->next) {
|
||||
if(ldns_read_uint16(rr->ttl_data+4)
|
||||
!= INET6_SIZE)
|
||||
continue;
|
||||
memmove(&sa->sin6_addr, rr->ttl_data+4+2,
|
||||
INET6_SIZE);
|
||||
if(priv_lookup_addr(priv, &addr, len))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* iterator/iter_priv.h - iterative resolver private address and domain store
|
||||
*
|
||||
* Copyright (c) 2008, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to assist the iterator module.
|
||||
* Keep track of the private addresses and lookup fast.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_PRIV_H
|
||||
#define ITERATOR_ITER_PRIV_H
|
||||
#include "util/rbtree.h"
|
||||
struct iter_env;
|
||||
struct config_file;
|
||||
struct regional;
|
||||
struct rrset_parse;
|
||||
|
||||
/**
|
||||
* Iterator priv structure
|
||||
*/
|
||||
struct iter_priv {
|
||||
/** regional for allocation */
|
||||
struct regional* region;
|
||||
/**
|
||||
* Tree of the address spans that are blocked.
|
||||
* contents of type addr_tree_node.
|
||||
* No further data need, only presence or absence.
|
||||
*/
|
||||
rbtree_t a;
|
||||
/**
|
||||
* Tree of the domains spans that are allowed to contain
|
||||
* the blocked address spans.
|
||||
* contents of type name_tree_node.
|
||||
* No further data need, only presence or absence.
|
||||
*/
|
||||
rbtree_t n;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create priv structure
|
||||
* @return new structure or NULL on error.
|
||||
*/
|
||||
struct iter_priv* priv_create();
|
||||
|
||||
/**
|
||||
* Delete priv structure.
|
||||
* @param priv: to delete.
|
||||
*/
|
||||
void priv_delete(struct iter_priv* priv);
|
||||
|
||||
/**
|
||||
* Process priv config.
|
||||
* @param priv: where to store.
|
||||
* @param cfg: config options.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
int priv_apply_cfg(struct iter_priv* priv, struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* See if rrset is bad.
|
||||
* @param priv: structure for private address storage.
|
||||
* @param pkt: packet to decompress rrset name in.
|
||||
* @param rrset: the rrset to examine, A or AAAA.
|
||||
* @return true if the rrset is bad and should be removed.
|
||||
*/
|
||||
int priv_rrset_bad(struct iter_priv* priv, ldns_buffer* pkt,
|
||||
struct rrset_parse* rrset);
|
||||
|
||||
/**
|
||||
* Get memory used by priv structure.
|
||||
* @param priv: structure for address storage.
|
||||
* @return bytes in use.
|
||||
*/
|
||||
size_t priv_get_mem(struct iter_priv* priv);
|
||||
|
||||
#endif /* ITERATOR_ITER_PRIV_H */
|
||||
@@ -104,6 +104,7 @@ response_type_from_server(int rdset,
|
||||
struct dns_msg* msg, struct query_info* request, struct delegpt* dp)
|
||||
{
|
||||
uint8_t* origzone = (uint8_t*)"\000"; /* the default */
|
||||
struct ub_packed_rrset_key* s;
|
||||
size_t origzonelen = 1;
|
||||
size_t i;
|
||||
|
||||
@@ -115,7 +116,7 @@ response_type_from_server(int rdset,
|
||||
/* make sure its not recursive when we don't want it to */
|
||||
if( (msg->rep->flags&BIT_RA) &&
|
||||
!(msg->rep->flags&BIT_AA) && !rdset)
|
||||
return RESPONSE_TYPE_LAME;
|
||||
return RESPONSE_TYPE_REC_LAME;
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
}
|
||||
|
||||
@@ -188,12 +189,10 @@ response_type_from_server(int rdset,
|
||||
}
|
||||
|
||||
/* Looking at the authority section, we just look and see if
|
||||
* there is a delegation NS set, turning it into a delegation.
|
||||
* Otherwise, we will have to conclude ANSWER (either it is
|
||||
* NOERROR/NODATA, or an non-authoritative answer). */
|
||||
* there is a SOA record, that means a NOERROR/NODATA */
|
||||
for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets +
|
||||
msg->rep->ns_numrrsets); i++) {
|
||||
struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
|
||||
s = msg->rep->rrsets[i];
|
||||
|
||||
/* The normal way of detecting NOERROR/NODATA. */
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA &&
|
||||
@@ -201,22 +200,35 @@ response_type_from_server(int rdset,
|
||||
/* we do our own recursion, thank you */
|
||||
if( (msg->rep->flags&BIT_RA) &&
|
||||
!(msg->rep->flags&BIT_AA) && !rdset)
|
||||
return RESPONSE_TYPE_LAME;
|
||||
return RESPONSE_TYPE_REC_LAME;
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
}
|
||||
}
|
||||
/* Looking at the authority section, we just look and see if
|
||||
* there is a delegation NS set, turning it into a delegation.
|
||||
* Otherwise, we will have to conclude ANSWER (either it is
|
||||
* NOERROR/NODATA, or an non-authoritative answer). */
|
||||
for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets +
|
||||
msg->rep->ns_numrrsets); i++) {
|
||||
s = msg->rep->rrsets[i];
|
||||
|
||||
/* Detect REFERRAL/LAME/ANSWER based on the relationship
|
||||
* of the NS set to the originating zone name. */
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NS) {
|
||||
/* If we are getting an NS set for the zone we
|
||||
* thought we were contacting, then it is an answer.*/
|
||||
/* FIXME: is this correct? */
|
||||
if(query_dname_compare(s->rk.dname, origzone) == 0) {
|
||||
/* see if mistakenly a recursive server was
|
||||
* deployed and is responding nonAA */
|
||||
if( (msg->rep->flags&BIT_RA) &&
|
||||
!(msg->rep->flags&BIT_AA) && !rdset)
|
||||
return RESPONSE_TYPE_LAME;
|
||||
return RESPONSE_TYPE_REC_LAME;
|
||||
/* Or if a lame server is deployed,
|
||||
* which gives ns==zone delegation from cache
|
||||
* without AA bit as well, with nodata nosoa*/
|
||||
if(msg->rep->an_numrrsets==0 &&
|
||||
!(msg->rep->flags&BIT_AA) && !rdset)
|
||||
return RESPONSE_TYPE_REC_LAME;
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
}
|
||||
/* If we are getting a referral upwards (or to
|
||||
@@ -247,6 +259,6 @@ response_type_from_server(int rdset,
|
||||
* be an entirely empty message) */
|
||||
/* check if recursive answer; saying it has empty cache */
|
||||
if( (msg->rep->flags&BIT_RA) && !(msg->rep->flags&BIT_AA) && !rdset)
|
||||
return RESPONSE_TYPE_LAME;
|
||||
return RESPONSE_TYPE_REC_LAME;
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,14 @@ enum response_type {
|
||||
* 'lame' means that this particular response indicates that
|
||||
* the nameserver knew nothing about the question.
|
||||
*/
|
||||
RESPONSE_TYPE_LAME
|
||||
RESPONSE_TYPE_LAME,
|
||||
|
||||
/**
|
||||
* Recursion lame means that the nameserver is some sort of
|
||||
* open recursor, and not authoritative for the question.
|
||||
* It may know something, but not authoritatively.
|
||||
*/
|
||||
RESPONSE_TYPE_REC_LAME
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+141
-12
@@ -41,6 +41,8 @@
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_scrub.h"
|
||||
#include "iterator/iterator.h"
|
||||
#include "iterator/iter_priv.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
@@ -60,11 +62,11 @@ static void
|
||||
remove_rrset(const char* str, ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct rrset_parse* prev, struct rrset_parse** rrset)
|
||||
{
|
||||
if(verbosity >= VERB_DETAIL
|
||||
if(verbosity >= VERB_QUERY
|
||||
&& (*rrset)->dname_len <= LDNS_MAX_DOMAINLEN) {
|
||||
uint8_t buf[LDNS_MAX_DOMAINLEN+1];
|
||||
dname_pkt_copy(pkt, buf, (*rrset)->dname);
|
||||
log_nametypeclass(VERB_DETAIL, str, buf,
|
||||
log_nametypeclass(VERB_QUERY, str, buf,
|
||||
(*rrset)->type, ntohs((*rrset)->rrset_class));
|
||||
}
|
||||
if(prev)
|
||||
@@ -99,8 +101,6 @@ has_additional(uint16_t t)
|
||||
case LDNS_RR_TYPE_NAPTR:
|
||||
/* TODO: NAPTR not supported, glue stripped off */
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -189,7 +189,11 @@ parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname,
|
||||
if(rrset->rr_count != 1) {
|
||||
verbose(VERB_ALGO, "Found CNAME rrset with "
|
||||
"size > 1: %u", (unsigned)rrset->rr_count);
|
||||
return 0;
|
||||
/* use the first CNAME! */
|
||||
rrset->rr_count = 1;
|
||||
rrset->size = rrset->rr_first->size;
|
||||
rrset->rr_last = rrset->rr_first;
|
||||
rrset->rr_first->next = NULL;
|
||||
}
|
||||
if(rrset->rr_first->size < sizeof(uint16_t)+1)
|
||||
return 0; /* CNAME rdata too small */
|
||||
@@ -316,7 +320,7 @@ scrub_normalize(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
{
|
||||
uint8_t* sname = qinfo->qname;
|
||||
size_t snamelen = qinfo->qname_len;
|
||||
struct rrset_parse* rrset, *prev;
|
||||
struct rrset_parse* rrset, *prev, *nsset=NULL;
|
||||
|
||||
if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR &&
|
||||
FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN)
|
||||
@@ -350,6 +354,10 @@ scrub_normalize(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
"too long");
|
||||
return 0;
|
||||
}
|
||||
/* internally we have CNAME'd/DNAME'd chains ending
|
||||
* in nxdomain with NOERROR rcode, change rcode
|
||||
* to reflect this (if needed) */
|
||||
FLAGS_SET_RCODE(msg->flags, LDNS_RCODE_NOERROR);
|
||||
if(nx && nx->type == LDNS_RR_TYPE_CNAME &&
|
||||
dname_pkt_compare(pkt, sname, nx->dname) == 0) {
|
||||
/* check next cname */
|
||||
@@ -392,6 +400,10 @@ scrub_normalize(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
return 0;
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
/* internally we have CNAME'd/DNAME'd chains ending
|
||||
* in nxdomain with NOERROR rcode, change rcode
|
||||
* to reflect this (if needed) */
|
||||
FLAGS_SET_RCODE(msg->flags, LDNS_RCODE_NOERROR);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -404,7 +416,10 @@ scrub_normalize(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
}
|
||||
|
||||
/* Mark the additional names from relevant rrset as OK. */
|
||||
mark_additional_rrset(pkt, msg, rrset);
|
||||
/* only for RRsets that match the query name, other ones
|
||||
* will be removed by sanitize, so no additional for them */
|
||||
if(dname_pkt_compare(pkt, qinfo->qname, rrset->dname) == 0)
|
||||
mark_additional_rrset(pkt, msg, rrset);
|
||||
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
@@ -412,6 +427,24 @@ scrub_normalize(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
|
||||
/* Mark additional names from AUTHORITY */
|
||||
while(rrset && rrset->section == LDNS_SECTION_AUTHORITY) {
|
||||
if(rrset->type==LDNS_RR_TYPE_DNAME ||
|
||||
rrset->type==LDNS_RR_TYPE_CNAME ||
|
||||
rrset->type==LDNS_RR_TYPE_A ||
|
||||
rrset->type==LDNS_RR_TYPE_AAAA) {
|
||||
remove_rrset("normalize: removing irrelevant "
|
||||
"RRset:", pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
/* only one NS set allowed in authority section */
|
||||
if(rrset->type==LDNS_RR_TYPE_NS) {
|
||||
if(nsset == NULL) {
|
||||
nsset = rrset;
|
||||
} else {
|
||||
remove_rrset("normalize: removing irrelevant "
|
||||
"RRset:", pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
mark_additional_rrset(pkt, msg, rrset);
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
@@ -435,6 +468,13 @@ scrub_normalize(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(rrset->type==LDNS_RR_TYPE_DNAME ||
|
||||
rrset->type==LDNS_RR_TYPE_CNAME ||
|
||||
rrset->type==LDNS_RR_TYPE_NS) {
|
||||
remove_rrset("normalize: removing irrelevant "
|
||||
"RRset:", pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
}
|
||||
@@ -459,7 +499,7 @@ store_rrset(ldns_buffer* pkt, struct msg_parse* msg, struct module_env* env,
|
||||
struct ub_packed_rrset_key* k;
|
||||
struct packed_rrset_data* d;
|
||||
struct rrset_ref ref;
|
||||
uint32_t now = time(NULL);
|
||||
uint32_t now = *env->now;
|
||||
|
||||
k = alloc_special_obtain(env->alloc);
|
||||
if(!k)
|
||||
@@ -478,6 +518,35 @@ store_rrset(ldns_buffer* pkt, struct msg_parse* msg, struct module_env* env,
|
||||
env->alloc, now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if right hand name in NSEC is within zone
|
||||
* @param rrset: the NSEC rrset
|
||||
* @param zonename: the zone name.
|
||||
* @return true if BAD.
|
||||
*/
|
||||
static int sanitize_nsec_is_overreach(struct rrset_parse* rrset,
|
||||
uint8_t* zonename)
|
||||
{
|
||||
struct rr_parse* rr;
|
||||
uint8_t* rhs;
|
||||
size_t len;
|
||||
log_assert(rrset->type == LDNS_RR_TYPE_NSEC);
|
||||
for(rr = rrset->rr_first; rr; rr = rr->next) {
|
||||
rhs = rr->ttl_data+4+2;
|
||||
len = ldns_read_uint16(rr->ttl_data+4);
|
||||
if(!(len=dname_valid(rhs, len))) {
|
||||
/* malformed domain name in rdata */
|
||||
return 1;
|
||||
}
|
||||
if(!dname_subdomain_c(rhs, zonename)) {
|
||||
/* overreaching */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* all NSEC RRs OK */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a response event, remove suspect RRsets from the response.
|
||||
* "Suspect" rrsets are potentially poison. Note that this routine expects
|
||||
@@ -486,18 +555,49 @@ store_rrset(ldns_buffer* pkt, struct msg_parse* msg, struct module_env* env,
|
||||
*
|
||||
* @param pkt: packet.
|
||||
* @param msg: msg to normalize.
|
||||
* @param qinfo: the question originally asked.
|
||||
* @param zonename: name of server zone.
|
||||
* @param env: module environment with config and cache.
|
||||
* @param ie: iterator environment with private address data.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
static int
|
||||
scrub_sanitize(ldns_buffer* pkt, struct msg_parse* msg, uint8_t* zonename,
|
||||
struct module_env* env)
|
||||
scrub_sanitize(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct query_info* qinfo, uint8_t* zonename, struct module_env* env,
|
||||
struct iter_env* ie)
|
||||
{
|
||||
struct rrset_parse* rrset, *prev;
|
||||
prev = NULL;
|
||||
rrset = msg->rrset_first;
|
||||
|
||||
/* the first DNAME is allowed to stay. It needs checking before
|
||||
* it can be used from the cache. After normalization, an initial
|
||||
* DNAME will have a correctly synthesized CNAME after it. */
|
||||
if(rrset && rrset->type == LDNS_RR_TYPE_DNAME &&
|
||||
rrset->section == LDNS_SECTION_ANSWER &&
|
||||
pkt_strict_sub(pkt, qinfo->qname, rrset->dname) &&
|
||||
pkt_sub(pkt, rrset->dname, zonename)) {
|
||||
prev = rrset; /* DNAME allowed to stay in answer section */
|
||||
rrset = rrset->rrset_all_next;
|
||||
}
|
||||
|
||||
/* remove all records from the answer section that are
|
||||
* not the same domain name as the query domain name.
|
||||
* The answer section should contain rrsets with the same name
|
||||
* as the question. For DNAMEs a CNAME has been synthesized.
|
||||
* Wildcards have the query name in answer section.
|
||||
* ANY queries get query name in answer section.
|
||||
* Remainders of CNAME chains are cut off and resolved by iterator. */
|
||||
while(rrset && rrset->section == LDNS_SECTION_ANSWER) {
|
||||
if(dname_pkt_compare(pkt, qinfo->qname, rrset->dname) != 0) {
|
||||
remove_rrset("sanitize: removing extraneous answer "
|
||||
"RRset:", pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
}
|
||||
|
||||
/* At this point, we brutally remove ALL rrsets that aren't
|
||||
* children of the originating zone. The idea here is that,
|
||||
* as far as we know, the server that we contacted is ONLY
|
||||
@@ -505,7 +605,21 @@ scrub_sanitize(ldns_buffer* pkt, struct msg_parse* msg, uint8_t* zonename,
|
||||
* be authoriative for any other zones, and of course, MAY
|
||||
* NOT be authoritative for some subdomains of the originating
|
||||
* zone. */
|
||||
prev = NULL;
|
||||
rrset = msg->rrset_first;
|
||||
while(rrset) {
|
||||
|
||||
/* remove private addresses */
|
||||
if( (rrset->type == LDNS_RR_TYPE_A ||
|
||||
rrset->type == LDNS_RR_TYPE_AAAA) &&
|
||||
priv_rrset_bad(ie->priv, pkt, rrset)) {
|
||||
/* set servfail, so the classification becomes
|
||||
* THROWAWAY, instead of LAME or other unwanted */
|
||||
FLAGS_SET_RCODE(msg->flags, LDNS_RCODE_SERVFAIL);
|
||||
remove_rrset("sanitize: removing public name with "
|
||||
"private address", pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* skip DNAME records -- they will always be followed by a
|
||||
* synthesized CNAME, which will be relevant.
|
||||
@@ -537,6 +651,13 @@ scrub_sanitize(ldns_buffer* pkt, struct msg_parse* msg, uint8_t* zonename,
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* check if right hand side of NSEC is within zone */
|
||||
if(rrset->type == LDNS_RR_TYPE_NSEC &&
|
||||
sanitize_nsec_is_overreach(rrset, zonename)) {
|
||||
remove_rrset("sanitize: removing overreaching NSEC "
|
||||
"RRset:", pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
}
|
||||
@@ -546,7 +667,7 @@ scrub_sanitize(ldns_buffer* pkt, struct msg_parse* msg, uint8_t* zonename,
|
||||
int
|
||||
scrub_message(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct query_info* qinfo, uint8_t* zonename, struct regional* region,
|
||||
struct module_env* env)
|
||||
struct module_env* env, struct iter_env* ie)
|
||||
{
|
||||
/* basic sanity checks */
|
||||
log_nametypeclass(VERB_ALGO, "scrub for", zonename, LDNS_RR_TYPE_NS,
|
||||
@@ -556,6 +677,14 @@ scrub_message(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
if( !(msg->flags&BIT_QR) )
|
||||
return 0;
|
||||
|
||||
/* make sure that a query is echoed back when NOERROR or NXDOMAIN */
|
||||
/* this is not required for basic operation but is a forgery
|
||||
* resistance (security) feature */
|
||||
if((FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR ||
|
||||
FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN) &&
|
||||
msg->qdcount == 0)
|
||||
return 0;
|
||||
|
||||
/* if a query is echoed back, make sure it is correct. Otherwise,
|
||||
* this may be not a reply to our query. */
|
||||
if(msg->qdcount == 1) {
|
||||
@@ -569,7 +698,7 @@ scrub_message(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
if(!scrub_normalize(pkt, msg, qinfo, region))
|
||||
return 0;
|
||||
/* delete all out-of-zone information */
|
||||
if(!scrub_sanitize(pkt, msg, zonename, env))
|
||||
if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ struct msg_parse;
|
||||
struct query_info;
|
||||
struct regional;
|
||||
struct module_env;
|
||||
struct iter_env;
|
||||
|
||||
/**
|
||||
* Cleanup the passed dns message.
|
||||
@@ -57,10 +58,11 @@ struct module_env;
|
||||
* Used to determine out of bailiwick information.
|
||||
* @param regional: where to allocate (new) parts of the message.
|
||||
* @param env: module environment with config settings and cache.
|
||||
* @param ie: iterator module environment data.
|
||||
* @return: false if the message is total waste. true if scrubbed with success.
|
||||
*/
|
||||
int scrub_message(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct query_info* qinfo, uint8_t* zonename, struct regional* regional,
|
||||
struct module_env* env);
|
||||
struct module_env* env, struct iter_env* ie);
|
||||
|
||||
#endif /* ITERATOR_ITER_SCRUB_H */
|
||||
|
||||
+199
-41
@@ -46,6 +46,7 @@
|
||||
#include "iterator/iter_fwd.h"
|
||||
#include "iterator/iter_donotq.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "iterator/iter_priv.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "services/cache/rrset.h"
|
||||
@@ -102,7 +103,7 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
|
||||
if(!read_fetch_policy(iter_env, cfg->target_fetch_policy))
|
||||
return 0;
|
||||
for(i=0; i<iter_env->max_dependency_depth+1; i++)
|
||||
verbose(VERB_DETAIL, "target fetch policy for level %d is %d",
|
||||
verbose(VERB_QUERY, "target fetch policy for level %d is %d",
|
||||
i, iter_env->target_fetch_policy[i]);
|
||||
|
||||
if(!iter_env->hints)
|
||||
@@ -123,18 +124,61 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
|
||||
log_err("Could not set donotqueryaddresses");
|
||||
return 0;
|
||||
}
|
||||
if(!iter_env->priv)
|
||||
iter_env->priv = priv_create();
|
||||
if(!iter_env->priv || !priv_apply_cfg(iter_env->priv, cfg)) {
|
||||
log_err("Could not set private addresses");
|
||||
return 0;
|
||||
}
|
||||
iter_env->supports_ipv6 = cfg->do_ip6;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** filter out unsuitable targets, return rtt or -1 */
|
||||
/** filter out unsuitable targets
|
||||
* @param iter_env: iterator environment with ipv6-support flag.
|
||||
* @param env: module environment with infra cache.
|
||||
* @param name: zone name
|
||||
* @param namelen: length of name
|
||||
* @param qtype: query type (host order).
|
||||
* @param now: current time
|
||||
* @param a: address in delegation point we are examining.
|
||||
* @return an integer that signals the target suitability.
|
||||
* as follows:
|
||||
* -1: The address should be omitted from the list.
|
||||
* Because:
|
||||
* o The address is bogus (DNSSEC validation failure).
|
||||
* o Listed as donotquery
|
||||
* o is ipv6 but no ipv6 support (in operating system).
|
||||
* o is lame
|
||||
* Otherwise, an rtt in milliseconds.
|
||||
* 0 .. USEFUL_SERVER_TOP_TIMEOUT-1
|
||||
* The roundtrip time timeout estimate. less than 2 minutes.
|
||||
* Note that util/rtt.c has a MIN_TIMEOUT of 50 msec, thus
|
||||
* values 0 .. 49 are not used, unless that is changed.
|
||||
* USEFUL_SERVER_TOP_TIMEOUT
|
||||
* This value exactly is given for unresponsive blacklisted.
|
||||
* USEFUL_SERVER_TOP_TIMEOUT ..
|
||||
* dnsseclame servers get penalty
|
||||
* USEFUL_SERVER_TOP_TIMEOUT*2 ..
|
||||
* recursion lame servers get penalty
|
||||
* UNKNOWN_SERVER_NICENESS
|
||||
* If no information is known about the server, this is
|
||||
* returned. 376 msec or so.
|
||||
*
|
||||
* When a final value is chosen that is dnsseclame ; dnsseclameness checking
|
||||
* is turned off (so we do not discard the reply).
|
||||
* When a final value is chosen that is recursionlame; RD bit is set on query.
|
||||
* Because of the numbers this means recursionlame also have dnssec lameness
|
||||
* checking turned off.
|
||||
*/
|
||||
static int
|
||||
iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env,
|
||||
uint8_t* name, size_t namelen, time_t now, struct delegpt_addr* a)
|
||||
uint8_t* name, size_t namelen, uint16_t qtype, uint32_t now,
|
||||
struct delegpt_addr* a)
|
||||
{
|
||||
int rtt;
|
||||
int lame;
|
||||
int dnsseclame;
|
||||
int rtt, lame, reclame, dnsseclame;
|
||||
if(a->bogus)
|
||||
return -1; /* address of server is bogus */
|
||||
if(donotq_lookup(iter_env->donotq, &a->addr, a->addrlen)) {
|
||||
return -1; /* server is on the donotquery list */
|
||||
}
|
||||
@@ -143,12 +187,21 @@ iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env,
|
||||
}
|
||||
/* check lameness - need zone , class info */
|
||||
if(infra_get_lame_rtt(env->infra_cache, &a->addr, a->addrlen,
|
||||
name, namelen, &lame, &dnsseclame, &rtt, now)) {
|
||||
name, namelen, qtype, &lame, &dnsseclame, &reclame,
|
||||
&rtt, now)) {
|
||||
log_addr(VERB_ALGO, "servselect", &a->addr, a->addrlen);
|
||||
verbose(VERB_ALGO, " rtt=%d%s%s%s", rtt,
|
||||
lame?" LAME":"",
|
||||
dnsseclame?" DNSSEC_LAME":"",
|
||||
reclame?" REC_LAME":"");
|
||||
if(lame)
|
||||
return -1; /* server is lame */
|
||||
else if(rtt >= USEFUL_SERVER_TOP_TIMEOUT)
|
||||
return -1; /* server is unresponsive */
|
||||
else if(dnsseclame)
|
||||
/* server is unresponsive */
|
||||
return USEFUL_SERVER_TOP_TIMEOUT;
|
||||
else if(reclame)
|
||||
return rtt+USEFUL_SERVER_TOP_TIMEOUT*2; /* nonpref */
|
||||
else if(dnsseclame )
|
||||
return rtt+USEFUL_SERVER_TOP_TIMEOUT; /* nonpref */
|
||||
else return rtt;
|
||||
}
|
||||
@@ -156,38 +209,67 @@ iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env,
|
||||
return UNKNOWN_SERVER_NICENESS;
|
||||
}
|
||||
|
||||
/** lookup RTT information, and also store fastest rtt (if any) */
|
||||
static int
|
||||
iter_fill_rtt(struct iter_env* iter_env, struct module_env* env,
|
||||
uint8_t* name, size_t namelen, uint16_t qtype, uint32_t now,
|
||||
struct delegpt* dp, int* best_rtt)
|
||||
{
|
||||
int got_it = 0;
|
||||
struct delegpt_addr* a;
|
||||
if(dp->bogus)
|
||||
return 0; /* NS bogus, all bogus, nothing found */
|
||||
for(a=dp->result_list; a; a = a->next_result) {
|
||||
a->sel_rtt = iter_filter_unsuitable(iter_env, env,
|
||||
name, namelen, qtype, now, a);
|
||||
if(a->sel_rtt != -1) {
|
||||
if(!got_it) {
|
||||
*best_rtt = a->sel_rtt;
|
||||
got_it = 1;
|
||||
} else if(a->sel_rtt < *best_rtt) {
|
||||
*best_rtt = a->sel_rtt;
|
||||
}
|
||||
}
|
||||
}
|
||||
return got_it;
|
||||
}
|
||||
|
||||
/** filter the addres list, putting best targets at front,
|
||||
* returns number of best targets (or 0, no suitable targets) */
|
||||
static int
|
||||
iter_filter_order(struct iter_env* iter_env, struct module_env* env,
|
||||
uint8_t* name, size_t namelen, time_t now, struct delegpt* dp,
|
||||
int* best_rtt)
|
||||
uint8_t* name, size_t namelen, uint16_t qtype, uint32_t now,
|
||||
struct delegpt* dp, int* selected_rtt)
|
||||
{
|
||||
int got_num = 0, got_rtt = 0, thisrtt, swap_to_front;
|
||||
int got_num = 0, low_rtt = 0, swap_to_front;
|
||||
struct delegpt_addr* a, *n, *prev=NULL;
|
||||
|
||||
/* fillup sel_rtt and find best rtt in the bunch */
|
||||
got_num = iter_fill_rtt(iter_env, env, name, namelen, qtype, now, dp,
|
||||
&low_rtt);
|
||||
if(got_num == 0)
|
||||
return 0;
|
||||
if(low_rtt >= USEFUL_SERVER_TOP_TIMEOUT &&
|
||||
delegpt_count_missing_targets(dp) > 0)
|
||||
return 0; /* we want more choice. The best choice is a bad one.
|
||||
return 0 to force the caller to fetch more */
|
||||
|
||||
got_num = 0;
|
||||
a = dp->result_list;
|
||||
while(a) {
|
||||
/* filter out unsuitable targets */
|
||||
thisrtt = iter_filter_unsuitable(iter_env, env, name, namelen,
|
||||
now, a);
|
||||
if(thisrtt == -1) {
|
||||
/* skip unsuitable targets */
|
||||
if(a->sel_rtt == -1) {
|
||||
prev = a;
|
||||
a = a->next_result;
|
||||
continue;
|
||||
}
|
||||
/* classify the server address and determine what to do */
|
||||
swap_to_front = 0;
|
||||
if(got_num == 0) {
|
||||
got_rtt = thisrtt;
|
||||
got_num = 1;
|
||||
swap_to_front = 1;
|
||||
} else if(thisrtt == got_rtt) {
|
||||
if(a->sel_rtt >= low_rtt && a->sel_rtt - low_rtt <= RTT_BAND) {
|
||||
got_num++;
|
||||
swap_to_front = 1;
|
||||
} else if(thisrtt < got_rtt) {
|
||||
got_rtt = thisrtt;
|
||||
got_num = 1; /* start back at count of 1 */
|
||||
} else if(a->sel_rtt<low_rtt && low_rtt-a->sel_rtt<=RTT_BAND) {
|
||||
got_num++;
|
||||
swap_to_front = 1;
|
||||
}
|
||||
/* swap to front if necessary, or move to next result */
|
||||
@@ -202,26 +284,43 @@ iter_filter_order(struct iter_env* iter_env, struct module_env* env,
|
||||
a = a->next_result;
|
||||
}
|
||||
}
|
||||
*best_rtt = got_rtt;
|
||||
*selected_rtt = low_rtt;
|
||||
return got_num;
|
||||
}
|
||||
|
||||
struct delegpt_addr*
|
||||
iter_server_selection(struct iter_env* iter_env,
|
||||
struct module_env* env, struct delegpt* dp,
|
||||
uint8_t* name, size_t namelen, int* dnssec_expected)
|
||||
uint8_t* name, size_t namelen, uint16_t qtype, int* dnssec_expected,
|
||||
int* chase_to_rd)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
int sel;
|
||||
int selrtt;
|
||||
struct delegpt_addr* a, *prev;
|
||||
int num = iter_filter_order(iter_env, env, name, namelen, now, dp,
|
||||
&selrtt);
|
||||
int num = iter_filter_order(iter_env, env, name, namelen, qtype,
|
||||
*env->now, dp, &selrtt);
|
||||
|
||||
if(num == 0)
|
||||
return NULL;
|
||||
if(selrtt >= USEFUL_SERVER_TOP_TIMEOUT)
|
||||
verbose(VERB_ALGO, "selrtt %d", selrtt);
|
||||
if(selrtt > USEFUL_SERVER_TOP_TIMEOUT*2) {
|
||||
verbose(VERB_ALGO, "chase to recursion lame server");
|
||||
*chase_to_rd = 1;
|
||||
}
|
||||
if(selrtt > USEFUL_SERVER_TOP_TIMEOUT) {
|
||||
verbose(VERB_ALGO, "chase to dnssec lame server");
|
||||
*dnssec_expected = 0;
|
||||
}
|
||||
if(selrtt == USEFUL_SERVER_TOP_TIMEOUT) {
|
||||
verbose(VERB_ALGO, "chase to blacklisted lame server");
|
||||
/* the best choice is a blacklisted, unresponsive server,
|
||||
* we need to throttle down our traffic towards it */
|
||||
if(ub_random(env->rnd) % 100 != 1) {
|
||||
/* 99% of the time, drop query */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if(num == 1) {
|
||||
a = dp->result_list;
|
||||
if(++a->attempts < OUTBOUND_MSG_RETRY)
|
||||
@@ -229,6 +328,7 @@ iter_server_selection(struct iter_env* iter_env,
|
||||
dp->result_list = a->next_result;
|
||||
return a;
|
||||
}
|
||||
|
||||
/* randomly select a target from the list */
|
||||
log_assert(num > 1);
|
||||
/* we do not need secure random numbers here, but
|
||||
@@ -313,7 +413,7 @@ causes_cycle(struct module_qstate* qstate, uint8_t* name, size_t namelen,
|
||||
qinf.qname_len = namelen;
|
||||
qinf.qtype = t;
|
||||
qinf.qclass = c;
|
||||
log_assert(fptr_whitelist_modenv_detect_cycle(
|
||||
fptr_ok(fptr_whitelist_modenv_detect_cycle(
|
||||
qstate->env->detect_cycle));
|
||||
return (*qstate->env->detect_cycle)(qstate, &qinf,
|
||||
(uint16_t)(BIT_RD|BIT_CD), qstate->is_priming);
|
||||
@@ -331,7 +431,7 @@ iter_mark_cycle_targets(struct module_qstate* qstate, struct delegpt* dp)
|
||||
LDNS_RR_TYPE_AAAA, qstate->qinfo.qclass) ||
|
||||
causes_cycle(qstate, ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_A, qstate->qinfo.qclass)) {
|
||||
log_nametypeclass(VERB_DETAIL, "skipping target due "
|
||||
log_nametypeclass(VERB_QUERY, "skipping target due "
|
||||
"to dependency cycle (harden-glue: no may "
|
||||
"fix some of the cycles)",
|
||||
ns->name, LDNS_RR_TYPE_A,
|
||||
@@ -342,7 +442,8 @@ iter_mark_cycle_targets(struct module_qstate* qstate, struct delegpt* dp)
|
||||
}
|
||||
|
||||
int
|
||||
iter_dp_is_useless(struct module_qstate* qstate, struct delegpt* dp)
|
||||
iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags,
|
||||
struct delegpt* dp)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
/* check:
|
||||
@@ -355,18 +456,17 @@ iter_dp_is_useless(struct module_qstate* qstate, struct delegpt* dp)
|
||||
* o the query is for one of the nameservers in dp,
|
||||
* and that nameserver is a glue-name for this dp.
|
||||
*/
|
||||
if(!(qstate->query_flags&BIT_RD))
|
||||
if(!(qflags&BIT_RD))
|
||||
return 0;
|
||||
/* either available or unused targets */
|
||||
if(dp->usable_list || dp->result_list)
|
||||
return 0;
|
||||
|
||||
/* see if query is for one of the nameservers, which is glue */
|
||||
if( (qstate->qinfo.qtype == LDNS_RR_TYPE_A ||
|
||||
qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) &&
|
||||
dname_subdomain_c(qstate->qinfo.qname, dp->name) &&
|
||||
delegpt_find_ns(dp, qstate->qinfo.qname,
|
||||
qstate->qinfo.qname_len))
|
||||
if( (qinfo->qtype == LDNS_RR_TYPE_A ||
|
||||
qinfo->qtype == LDNS_RR_TYPE_AAAA) &&
|
||||
dname_subdomain_c(qinfo->qname, dp->name) &&
|
||||
delegpt_find_ns(dp, qinfo->qname, qinfo->qname_len))
|
||||
return 1;
|
||||
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
@@ -383,14 +483,15 @@ iter_indicates_dnssec(struct module_env* env, struct delegpt* dp,
|
||||
struct dns_msg* msg, uint16_t dclass)
|
||||
{
|
||||
/* information not available, !env->anchors can be common */
|
||||
if(!env || !env->anchors || !dp || !dp->name || !msg || !msg->rep)
|
||||
if(!env || !env->anchors || !dp || !dp->name)
|
||||
return 0;
|
||||
/* a trust anchor exists with this name, RRSIGs expected */
|
||||
if(anchor_find(env->anchors, dp->name, dp->namelabs, dp->namelen,
|
||||
dclass))
|
||||
return 1;
|
||||
/* see if DS rrset was given, in AUTH section */
|
||||
if(reply_find_rrset_section_ns(msg->rep, dp->name, dp->namelen,
|
||||
if(msg && msg->rep &&
|
||||
reply_find_rrset_section_ns(msg->rep, dp->name, dp->namelen,
|
||||
LDNS_RR_TYPE_DS, dclass))
|
||||
return 1;
|
||||
return 0;
|
||||
@@ -453,3 +554,60 @@ int iter_msg_from_zone(struct dns_msg* msg, struct delegpt* dp,
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* check equality of two rrsets
|
||||
* @param k1: rrset
|
||||
* @param k2: rrset
|
||||
* @return true if equal
|
||||
*/
|
||||
static int
|
||||
rrset_equal(struct ub_packed_rrset_key* k1, struct ub_packed_rrset_key* k2)
|
||||
{
|
||||
struct packed_rrset_data* d1 = (struct packed_rrset_data*)
|
||||
k1->entry.data;
|
||||
struct packed_rrset_data* d2 = (struct packed_rrset_data*)
|
||||
k2->entry.data;
|
||||
size_t i, t;
|
||||
if(k1->rk.dname_len != k2->rk.dname_len ||
|
||||
k1->rk.flags != k2->rk.flags ||
|
||||
k1->rk.type != k2->rk.type ||
|
||||
k1->rk.rrset_class != k2->rk.rrset_class ||
|
||||
query_dname_compare(k1->rk.dname, k2->rk.dname) != 0)
|
||||
return 0;
|
||||
if(d1->ttl != d2->ttl ||
|
||||
d1->count != d2->count ||
|
||||
d1->rrsig_count != d2->rrsig_count ||
|
||||
d1->trust != d2->trust ||
|
||||
d1->security != d2->security)
|
||||
return 0;
|
||||
t = d1->count + d1->rrsig_count;
|
||||
for(i=0; i<t; i++) {
|
||||
if(d1->rr_len[i] != d2->rr_len[i] ||
|
||||
d1->rr_ttl[i] != d2->rr_ttl[i] ||
|
||||
memcmp(d1->rr_data[i], d2->rr_data[i],
|
||||
d1->rr_len[i]) != 0)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
reply_equal(struct reply_info* p, struct reply_info* q)
|
||||
{
|
||||
size_t i;
|
||||
if(p->flags != q->flags ||
|
||||
p->qdcount != q->qdcount ||
|
||||
p->ttl != q->ttl ||
|
||||
p->security != q->security ||
|
||||
p->an_numrrsets != q->an_numrrsets ||
|
||||
p->ns_numrrsets != q->ns_numrrsets ||
|
||||
p->ar_numrrsets != q->ar_numrrsets ||
|
||||
p->rrset_count != q->rrset_count)
|
||||
return 0;
|
||||
for(i=0; i<p->rrset_count; i++) {
|
||||
if(!rrset_equal(p->rrsets[i], q->rrsets[i]))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
+19
-3
@@ -74,14 +74,18 @@ int iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg);
|
||||
* @param dp: delegation point with result list.
|
||||
* @param name: zone name (for lameness check).
|
||||
* @param namelen: length of name.
|
||||
* @param qtype: query type that we want to send.
|
||||
* @param dnssec_expected: set to 0, if a known dnssec-lame server is selected
|
||||
* these are not preferred, but are used as a last resort.
|
||||
* @param chase_to_rd: set to 1 if a known recursion lame server is selected
|
||||
* these are not preferred, but are used as a last resort.
|
||||
* @return best target or NULL if no target.
|
||||
* if not null, that target is removed from the result list in the dp.
|
||||
*/
|
||||
struct delegpt_addr* iter_server_selection(struct iter_env* iter_env,
|
||||
struct module_env* env, struct delegpt* dp, uint8_t* name,
|
||||
size_t namelen, int* dnssec_expected);
|
||||
size_t namelen, uint16_t qtype, int* dnssec_expected,
|
||||
int* chase_to_rd);
|
||||
|
||||
/**
|
||||
* Allocate dns_msg from parsed msg, in regional.
|
||||
@@ -134,10 +138,12 @@ void iter_mark_cycle_targets(struct module_qstate* qstate, struct delegpt* dp);
|
||||
/**
|
||||
* See if delegation is useful or offers immediately no targets for
|
||||
* further recursion.
|
||||
* @param qstate: query state with RD flag and query name.
|
||||
* @param qinfo: query name and type
|
||||
* @param qflags: query flags with RD flag
|
||||
* @param dp: delegpt to check.
|
||||
*/
|
||||
int iter_dp_is_useless(struct module_qstate* qstate, struct delegpt* dp);
|
||||
int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags,
|
||||
struct delegpt* dp);
|
||||
|
||||
/**
|
||||
* See if delegation is expected to have DNSSEC information (RRSIGs) in
|
||||
@@ -177,4 +183,14 @@ int iter_msg_has_dnssec(struct dns_msg* msg);
|
||||
int iter_msg_from_zone(struct dns_msg* msg, struct delegpt* dp,
|
||||
enum response_type type, uint16_t dclass);
|
||||
|
||||
/**
|
||||
* Check if two replies are equal
|
||||
* For fallback procedures
|
||||
* @param p: reply one. The reply has rrset data pointers in region.
|
||||
* Does not check rrset-IDs
|
||||
* @param q: reply two
|
||||
* @return if one and two are equal.
|
||||
*/
|
||||
int reply_equal(struct reply_info* p, struct reply_info* q);
|
||||
|
||||
#endif /* ITERATOR_ITER_UTILS_H */
|
||||
|
||||
+431
-81
@@ -49,6 +49,8 @@
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "iterator/iter_resptype.h"
|
||||
#include "iterator/iter_scrub.h"
|
||||
#include "iterator/iter_priv.h"
|
||||
#include "validator/val_neg.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "util/module.h"
|
||||
@@ -58,6 +60,7 @@
|
||||
#include "util/data/dname.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "util/config_file.h"
|
||||
|
||||
int
|
||||
iter_init(struct module_env* env, int id)
|
||||
@@ -80,10 +83,11 @@ void
|
||||
iter_deinit(struct module_env* env, int id)
|
||||
{
|
||||
struct iter_env* iter_env;
|
||||
if(!env || !env->modinfo || !env->modinfo[id])
|
||||
if(!env || !env->modinfo[id])
|
||||
return;
|
||||
iter_env = (struct iter_env*)env->modinfo[id];
|
||||
free(iter_env->target_fetch_policy);
|
||||
priv_delete(iter_env->priv);
|
||||
hints_delete(iter_env->hints);
|
||||
forwards_delete(iter_env->fwds);
|
||||
donotq_delete(iter_env->donotq);
|
||||
@@ -188,7 +192,8 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
|
||||
/* not interested */
|
||||
verbose(VERB_ALGO, "subq error, but not interested");
|
||||
log_query_info(VERB_ALGO, "superq", &super->qinfo);
|
||||
delegpt_log(VERB_ALGO, super_iq->dp);
|
||||
if(super_iq->dp)
|
||||
delegpt_log(VERB_ALGO, super_iq->dp);
|
||||
log_assert(0);
|
||||
return;
|
||||
}
|
||||
@@ -215,7 +220,7 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
|
||||
static int
|
||||
error_response(struct module_qstate* qstate, int id, int rcode)
|
||||
{
|
||||
verbose(VERB_DETAIL, "return error response %s",
|
||||
verbose(VERB_QUERY, "return error response %s",
|
||||
ldns_lookup_by_id(ldns_rcodes, rcode)?
|
||||
ldns_lookup_by_id(ldns_rcodes, rcode)->name:"??");
|
||||
qstate->return_rcode = rcode;
|
||||
@@ -224,6 +229,51 @@ error_response(struct module_qstate* qstate, int id, int rcode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an error to the client and cache the error code in the
|
||||
* message cache (so per qname, qtype, qclass).
|
||||
* @param qstate: our query state
|
||||
* @param id: module id
|
||||
* @param rcode: error code (DNS errcode).
|
||||
* @return: 0 for use by caller, to make notation easy, like:
|
||||
* return error_response(..).
|
||||
*/
|
||||
static int
|
||||
error_response_cache(struct module_qstate* qstate, int id, int rcode)
|
||||
{
|
||||
/* store in cache */
|
||||
struct reply_info err;
|
||||
memset(&err, 0, sizeof(err));
|
||||
err.flags = (uint16_t)(BIT_QR | BIT_RA);
|
||||
FLAGS_SET_RCODE(err.flags, rcode);
|
||||
err.qdcount = 1;
|
||||
err.ttl = NORR_TTL;
|
||||
/* do not waste time trying to validate this servfail */
|
||||
err.security = sec_status_indeterminate;
|
||||
verbose(VERB_ALGO, "store error response in message cache");
|
||||
if(!iter_dns_store(qstate->env, &qstate->qinfo, &err, 0)) {
|
||||
log_err("error_response_cache: could not store error (nomem)");
|
||||
}
|
||||
return error_response(qstate, id, rcode);
|
||||
}
|
||||
|
||||
/** check if prepend item is duplicate item */
|
||||
static int
|
||||
prepend_is_duplicate(struct ub_packed_rrset_key** sets, size_t to,
|
||||
struct ub_packed_rrset_key* dup)
|
||||
{
|
||||
size_t i;
|
||||
for(i=0; i<to; i++) {
|
||||
if(sets[i]->rk.type == dup->rk.type &&
|
||||
sets[i]->rk.rrset_class == dup->rk.rrset_class &&
|
||||
sets[i]->rk.dname_len == dup->rk.dname_len &&
|
||||
query_dname_compare(sets[i]->rk.dname, dup->rk.dname)
|
||||
== 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** prepend the prepend list in the answer and authority section of dns_msg */
|
||||
static int
|
||||
iter_prepend(struct iter_qstate* iq, struct dns_msg* msg,
|
||||
@@ -253,6 +303,11 @@ iter_prepend(struct iter_qstate* iq, struct dns_msg* msg,
|
||||
/* AUTH section */
|
||||
num_ns = 0;
|
||||
for(p = iq->ns_prepend_list; p; p = p->next) {
|
||||
if(prepend_is_duplicate(sets+msg->rep->an_numrrsets+num_an,
|
||||
num_ns, p->rrset) || prepend_is_duplicate(
|
||||
msg->rep->rrsets+msg->rep->an_numrrsets,
|
||||
msg->rep->ns_numrrsets, p->rrset))
|
||||
continue;
|
||||
sets[msg->rep->an_numrrsets + num_an + num_ns++] = p->rrset;
|
||||
}
|
||||
memcpy(sets + num_an + msg->rep->an_numrrsets + num_ns,
|
||||
@@ -404,13 +459,16 @@ handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
* request.
|
||||
* @param subq_ret: if newly allocated, the subquerystate, or NULL if it does
|
||||
* not need initialisation.
|
||||
* @param v: if true, validation is done on the subquery.
|
||||
* @param detcyc: if true, cycle detection is done on the subquery.
|
||||
* @return false on error (malloc).
|
||||
*/
|
||||
static int
|
||||
generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
|
||||
uint16_t qclass, struct module_qstate* qstate, int id,
|
||||
struct iter_qstate* iq, enum iter_state initial_state,
|
||||
enum iter_state final_state, struct module_qstate** subq_ret)
|
||||
enum iter_state final_state, struct module_qstate** subq_ret, int v,
|
||||
int detcyc)
|
||||
{
|
||||
struct module_qstate* subq = NULL;
|
||||
struct iter_qstate* subiq = NULL;
|
||||
@@ -430,10 +488,20 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
|
||||
* the resolution chain, which might have a validator. We are
|
||||
* uninterested in validating things not on the direct resolution
|
||||
* path. */
|
||||
qflags |= BIT_CD;
|
||||
if(!v)
|
||||
qflags |= BIT_CD;
|
||||
|
||||
if(detcyc) {
|
||||
fptr_ok(fptr_whitelist_modenv_detect_cycle(
|
||||
qstate->env->detect_cycle));
|
||||
if((*qstate->env->detect_cycle)(qstate, &qinf, qflags, prime)){
|
||||
log_query_info(VERB_DETAIL, "cycle detected", &qinf);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* attach subquery, lookup existing or make a new one */
|
||||
log_assert(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
|
||||
fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
|
||||
if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, &subq)) {
|
||||
return 0;
|
||||
}
|
||||
@@ -446,7 +514,7 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
|
||||
sizeof(struct iter_qstate));
|
||||
if(!subq->minfo[id]) {
|
||||
log_err("init subq: out of memory");
|
||||
log_assert(fptr_whitelist_modenv_kill_sub(
|
||||
fptr_ok(fptr_whitelist_modenv_kill_sub(
|
||||
qstate->env->kill_sub));
|
||||
(*qstate->env->kill_sub)(subq);
|
||||
return 0;
|
||||
@@ -480,7 +548,7 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
{
|
||||
struct delegpt* dp;
|
||||
struct module_qstate* subq;
|
||||
verbose(VERB_ALGO, "priming . NS %s",
|
||||
verbose(VERB_DETAIL, "priming . %s NS",
|
||||
ldns_lookup_by_id(ldns_rr_classes, (int)qclass)?
|
||||
ldns_lookup_by_id(ldns_rr_classes, (int)qclass)->name:"??");
|
||||
dp = hints_lookup_root(ie->hints, qclass);
|
||||
@@ -498,8 +566,8 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
* the normal INIT state logic (which would cause an infloop). */
|
||||
if(!generate_sub_request((uint8_t*)"\000", 1, LDNS_RR_TYPE_NS,
|
||||
qclass, qstate, id, iq, QUERYTARGETS_STATE, PRIME_RESP_STATE,
|
||||
&subq)) {
|
||||
log_err("out of memory priming root");
|
||||
&subq, 0, 1)) {
|
||||
verbose(VERB_ALGO, "could not prime root");
|
||||
return 0;
|
||||
}
|
||||
if(subq) {
|
||||
@@ -509,6 +577,8 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
subiq->dp = dp;
|
||||
/* there should not be any target queries. */
|
||||
subiq->num_target_queries = 0;
|
||||
subiq->dnssec_expected = iter_indicates_dnssec(
|
||||
qstate->env, subiq->dp, NULL, subq->qinfo.qclass);
|
||||
}
|
||||
|
||||
/* this module stops, our submodule starts, and does the query. */
|
||||
@@ -535,12 +605,27 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
{
|
||||
/* Lookup the stub hint. This will return null if the stub doesn't
|
||||
* need to be re-primed. */
|
||||
struct delegpt* stub_dp = hints_lookup_stub(ie->hints, qname, qclass,
|
||||
iq->dp);
|
||||
struct iter_hints_stub* stub = hints_lookup_stub(ie->hints,
|
||||
qname, qclass, iq->dp);
|
||||
struct delegpt* stub_dp;
|
||||
struct module_qstate* subq;
|
||||
/* The stub (if there is one) does not need priming. */
|
||||
if(!stub_dp)
|
||||
if(!stub)
|
||||
return 0;
|
||||
stub_dp = stub->dp;
|
||||
|
||||
/* is it a noprime stub (always use) */
|
||||
if(stub->noprime) {
|
||||
iq->dp = delegpt_copy(stub_dp, qstate->region);
|
||||
if(!iq->dp) {
|
||||
log_err("out of memory priming stub");
|
||||
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
return 1; /* return 1 to make module stop, with error */
|
||||
}
|
||||
log_nametypeclass(VERB_DETAIL, "use stub", stub_dp->name,
|
||||
LDNS_RR_TYPE_NS, qclass);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Otherwise, we need to (re)prime the stub. */
|
||||
log_nametypeclass(VERB_DETAIL, "priming stub", stub_dp->name,
|
||||
@@ -550,8 +635,8 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
* redundant INIT state processing. */
|
||||
if(!generate_sub_request(stub_dp->name, stub_dp->namelen,
|
||||
LDNS_RR_TYPE_NS, qclass, qstate, id, iq,
|
||||
QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq)) {
|
||||
log_err("out of memory priming stub");
|
||||
QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0, 1)) {
|
||||
verbose(VERB_ALGO, "could not prime stub");
|
||||
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
return 1; /* return 1 to make module stop, with error */
|
||||
}
|
||||
@@ -564,7 +649,7 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
subiq->dp = delegpt_copy(stub_dp, subq->region);
|
||||
if(!subiq->dp) {
|
||||
log_err("out of memory priming stub, copydp");
|
||||
log_assert(fptr_whitelist_modenv_kill_sub(
|
||||
fptr_ok(fptr_whitelist_modenv_kill_sub(
|
||||
qstate->env->kill_sub));
|
||||
(*qstate->env->kill_sub)(subq);
|
||||
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
@@ -575,6 +660,8 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
* missing targets. */
|
||||
subiq->num_target_queries = 0;
|
||||
subiq->wait_priming_stub = 1;
|
||||
subiq->dnssec_expected = iter_indicates_dnssec(
|
||||
qstate->env, subiq->dp, NULL, subq->qinfo.qclass);
|
||||
}
|
||||
|
||||
/* this module stops, our submodule starts, and does the query. */
|
||||
@@ -582,6 +669,107 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate A and AAAA checks for glue that is in-zone for the referral
|
||||
* we just got to obtain authoritative information on the adresses.
|
||||
*
|
||||
* @param qstate: the qtstate that triggered the need to prime.
|
||||
* @param iq: iterator query state.
|
||||
* @param id: module id.
|
||||
*/
|
||||
static void
|
||||
generate_a_aaaa_check(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
int id)
|
||||
{
|
||||
struct module_qstate* subq;
|
||||
size_t i;
|
||||
struct reply_info* rep = iq->response->rep;
|
||||
struct ub_packed_rrset_key* s;
|
||||
log_assert(iq->dp);
|
||||
|
||||
/* walk through additional, and check if in-zone,
|
||||
* only relevant A, AAAA are left after scrub anyway */
|
||||
for(i=rep->an_numrrsets+rep->ns_numrrsets; i<rep->rrset_count; i++) {
|
||||
s = rep->rrsets[i];
|
||||
/* check *ALL* addresses that are transmitted in additional*/
|
||||
/* is it an address ? */
|
||||
if( !(ntohs(s->rk.type)==LDNS_RR_TYPE_A ||
|
||||
ntohs(s->rk.type)==LDNS_RR_TYPE_AAAA)) {
|
||||
continue;
|
||||
}
|
||||
/* is this query the same as the A/AAAA check for it */
|
||||
if(qstate->qinfo.qtype == ntohs(s->rk.type) &&
|
||||
qstate->qinfo.qclass == ntohs(s->rk.rrset_class) &&
|
||||
query_dname_compare(qstate->qinfo.qname,
|
||||
s->rk.dname)==0 &&
|
||||
(qstate->query_flags&BIT_RD) &&
|
||||
!(qstate->query_flags&BIT_CD))
|
||||
continue;
|
||||
|
||||
/* generate subrequest for it */
|
||||
log_nametypeclass(VERB_ALGO, "schedule addr fetch",
|
||||
s->rk.dname, ntohs(s->rk.type),
|
||||
ntohs(s->rk.rrset_class));
|
||||
if(!generate_sub_request(s->rk.dname, s->rk.dname_len,
|
||||
ntohs(s->rk.type), ntohs(s->rk.rrset_class),
|
||||
qstate, id, iq,
|
||||
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 1)) {
|
||||
verbose(VERB_ALGO, "could not generate addr check");
|
||||
return;
|
||||
}
|
||||
/* ignore subq - not need for more init */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a NS check request to obtain authoritative information
|
||||
* on an NS rrset.
|
||||
*
|
||||
* @param qstate: the qtstate that triggered the need to prime.
|
||||
* @param iq: iterator query state.
|
||||
* @param id: module id.
|
||||
*/
|
||||
static void
|
||||
generate_ns_check(struct module_qstate* qstate, struct iter_qstate* iq, int id)
|
||||
{
|
||||
struct module_qstate* subq;
|
||||
log_assert(iq->dp);
|
||||
|
||||
/* is this query the same as the nscheck? */
|
||||
if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS &&
|
||||
query_dname_compare(iq->dp->name, qstate->qinfo.qname)==0 &&
|
||||
(qstate->query_flags&BIT_RD) && !(qstate->query_flags&BIT_CD)){
|
||||
/* spawn off A, AAAA queries for in-zone glue to check */
|
||||
generate_a_aaaa_check(qstate, iq, id);
|
||||
return;
|
||||
}
|
||||
|
||||
log_nametypeclass(VERB_ALGO, "schedule ns fetch",
|
||||
iq->dp->name, LDNS_RR_TYPE_NS, iq->qchase.qclass);
|
||||
if(!generate_sub_request(iq->dp->name, iq->dp->namelen,
|
||||
LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq,
|
||||
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 1)) {
|
||||
verbose(VERB_ALGO, "could not generate ns check");
|
||||
return;
|
||||
}
|
||||
if(subq) {
|
||||
struct iter_qstate* subiq =
|
||||
(struct iter_qstate*)subq->minfo[id];
|
||||
|
||||
/* make copy to avoid use of stub dp by different qs/threads */
|
||||
/* refetch glue to start higher up the tree */
|
||||
subiq->refetch_glue = 1;
|
||||
subiq->dp = delegpt_copy(iq->dp, subq->region);
|
||||
if(!subiq->dp) {
|
||||
log_err("out of memory generating ns check, copydp");
|
||||
fptr_ok(fptr_whitelist_modenv_kill_sub(
|
||||
qstate->env->kill_sub));
|
||||
(*qstate->env->kill_sub)(subq);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See if the query needs forwarding.
|
||||
*
|
||||
@@ -639,7 +827,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
/* We enforce a maximum number of query restarts. This is primarily a
|
||||
* cheap way to prevent CNAME loops. */
|
||||
if(iq->query_restart_count > MAX_RESTART_COUNT) {
|
||||
verbose(VERB_DETAIL, "request has exceeded the maximum number"
|
||||
verbose(VERB_QUERY, "request has exceeded the maximum number"
|
||||
" of query restarts with %d", iq->query_restart_count);
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
@@ -650,7 +838,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
* of work required to answer a given query. */
|
||||
verbose(VERB_ALGO, "request has dependency depth of %d", iq->depth);
|
||||
if(iq->depth > ie->max_dependency_depth) {
|
||||
verbose(VERB_DETAIL, "request has exceeded the maximum "
|
||||
verbose(VERB_QUERY, "request has exceeded the maximum "
|
||||
"dependency depth with depth of %d", iq->depth);
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
@@ -663,6 +851,13 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
msg = dns_cache_lookup(qstate->env, iq->qchase.qname,
|
||||
iq->qchase.qname_len, iq->qchase.qtype,
|
||||
iq->qchase.qclass, qstate->region, qstate->env->scratch);
|
||||
if(!msg && qstate->env->neg_cache) {
|
||||
/* lookup in negative cache; may result in
|
||||
* NOERROR/NODATA or NXDOMAIN answers that need validation */
|
||||
msg = val_neg_getmsg(qstate->env->neg_cache, &iq->qchase,
|
||||
qstate->region, qstate->env->rrset_cache,
|
||||
qstate->env->scratch_buffer, *qstate->env->now);
|
||||
}
|
||||
if(msg) {
|
||||
/* handle positive cache response */
|
||||
enum response_type type = response_type_from_cache(msg,
|
||||
@@ -684,6 +879,8 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
iq->qchase.qname_len = slen;
|
||||
/* This *is* a query restart, even if it is a cheap
|
||||
* one. */
|
||||
iq->dp = NULL;
|
||||
iq->refetch_glue = 0;
|
||||
iq->query_restart_count++;
|
||||
return next_state(iq, INIT_REQUEST_STATE);
|
||||
}
|
||||
@@ -701,6 +898,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
log_err("alloc failure for forward dp");
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
iq->refetch_glue = 0;
|
||||
/* the request has been forwarded.
|
||||
* forwarded requests need to be immediately sent to the
|
||||
* next state, QUERYTARGETS. */
|
||||
@@ -736,7 +934,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
* cache needs to be primed for the qclass. */
|
||||
iq->dp = dns_cache_find_delegation(qstate->env, delname,
|
||||
delnamelen, iq->qchase.qtype, iq->qchase.qclass,
|
||||
qstate->region, &iq->deleg_msg, (uint32_t)time(NULL));
|
||||
qstate->region, &iq->deleg_msg, *qstate->env->now);
|
||||
|
||||
/* If the cache has returned nothing, then we have a
|
||||
* root priming situation. */
|
||||
@@ -766,10 +964,11 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
* could be useless but lead to loops (bumping into the
|
||||
* same server reply) if useless-checked.
|
||||
*/
|
||||
if(iter_dp_is_useless(qstate, iq->dp)) {
|
||||
if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags,
|
||||
iq->dp)) {
|
||||
if(dname_is_root(iq->dp->name)) {
|
||||
/* use safety belt */
|
||||
verbose(VERB_OPS, "Priming problem: NS but "
|
||||
verbose(VERB_QUERY, "Cache has root NS but "
|
||||
"no addresses. Fallback to the safety belt.");
|
||||
iq->dp = hints_lookup_root(ie->hints,
|
||||
iq->qchase.qclass);
|
||||
@@ -802,17 +1001,6 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
verbose(VERB_ALGO, "cache delegation returns delegpt");
|
||||
delegpt_log(VERB_ALGO, iq->dp);
|
||||
|
||||
/* if the cache reply dp equals a validation anchor or msg has DS,
|
||||
* then DNSSEC RRSIGs are expected in the reply */
|
||||
iq->dnssec_expected = iter_indicates_dnssec(qstate->env, iq->dp,
|
||||
iq->deleg_msg, iq->qchase.qclass);
|
||||
|
||||
/* Reset the RD flag. If this is a query restart, then the RD
|
||||
* will have been turned off. */
|
||||
if(qstate->query_flags & BIT_RD)
|
||||
iq->chase_flags |= BIT_RD;
|
||||
else iq->chase_flags &= ~BIT_RD;
|
||||
|
||||
/* Otherwise, set the current delegation point and move on to the
|
||||
* next state. */
|
||||
return next_state(iq, INIT_REQUEST_2_STATE);
|
||||
@@ -838,7 +1026,7 @@ static int
|
||||
processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
struct iter_env* ie, int id)
|
||||
{
|
||||
log_query_info(VERB_DETAIL, "resolving (init part 2): ",
|
||||
log_query_info(VERB_QUERY, "resolving (init part 2): ",
|
||||
&qstate->qinfo);
|
||||
|
||||
/* Check to see if we need to prime a stub zone. */
|
||||
@@ -865,11 +1053,16 @@ processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
static int
|
||||
processInitRequest3(struct module_qstate* qstate, struct iter_qstate* iq)
|
||||
{
|
||||
log_query_info(VERB_DETAIL, "resolving (init part 3): ",
|
||||
log_query_info(VERB_QUERY, "resolving (init part 3): ",
|
||||
&qstate->qinfo);
|
||||
/* if the cache reply dp equals a validation anchor or msg has DS,
|
||||
* then DNSSEC RRSIGs are expected in the reply */
|
||||
iq->dnssec_expected = iter_indicates_dnssec(qstate->env, iq->dp,
|
||||
iq->deleg_msg, iq->qchase.qclass);
|
||||
|
||||
/* If the RD flag wasn't set, then we just finish with the
|
||||
* cached referral as the response. */
|
||||
if(!(iq->chase_flags & BIT_RD)) {
|
||||
if(!(qstate->query_flags & BIT_RD)) {
|
||||
iq->response = iq->deleg_msg;
|
||||
if(verbosity >= VERB_ALGO)
|
||||
log_dns_msg("no RD requested, using delegation msg",
|
||||
@@ -904,7 +1097,7 @@ generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
{
|
||||
struct module_qstate* subq;
|
||||
if(!generate_sub_request(name, namelen, qtype, qclass, qstate,
|
||||
id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq))
|
||||
id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0))
|
||||
return 0;
|
||||
if(subq) {
|
||||
struct iter_qstate* subiq =
|
||||
@@ -913,9 +1106,12 @@ generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
verbose(VERB_ALGO, "refetch of target glue");
|
||||
subiq->refetch_glue = 1;
|
||||
subiq->dp = delegpt_copy(iq->dp, subq->region);
|
||||
subiq->dnssec_expected = iter_indicates_dnssec(
|
||||
qstate->env, subiq->dp, NULL,
|
||||
subq->qinfo.qclass);
|
||||
}
|
||||
}
|
||||
log_nametypeclass(VERB_DETAIL, "new target", name, qtype, qclass);
|
||||
log_nametypeclass(VERB_QUERY, "new target", name, qtype, qclass);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -972,7 +1168,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
continue;
|
||||
}
|
||||
|
||||
if(ie->supports_ipv6) {
|
||||
if(ie->supports_ipv6 && !ns->got6) {
|
||||
/* Send the AAAA request. */
|
||||
if(!generate_target_query(qstate, iq, id,
|
||||
ns->name, ns->namelen,
|
||||
@@ -981,11 +1177,13 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
query_count++;
|
||||
}
|
||||
/* Send the A request. */
|
||||
if(!generate_target_query(qstate, iq, id,
|
||||
ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_A, iq->qchase.qclass))
|
||||
return 0;
|
||||
query_count++;
|
||||
if(!ns->got4) {
|
||||
if(!generate_target_query(qstate, iq, id,
|
||||
ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_A, iq->qchase.qclass))
|
||||
return 0;
|
||||
query_count++;
|
||||
}
|
||||
|
||||
/* mark this target as in progress. */
|
||||
ns->resolved = 1;
|
||||
@@ -1029,7 +1227,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
* needs to send a query to. That is, at least one per referral,
|
||||
* more if some targets timeout or return throwaway answers. */
|
||||
|
||||
log_query_info(VERB_DETAIL, "processQueryTargets:", &qstate->qinfo);
|
||||
log_query_info(VERB_QUERY, "processQueryTargets:", &qstate->qinfo);
|
||||
verbose(VERB_ALGO, "processQueryTargets: targetqueries %d, "
|
||||
"currentqueries %d", iq->num_target_queries,
|
||||
iq->num_current_queries);
|
||||
@@ -1038,7 +1236,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
/* Make sure that we haven't run away */
|
||||
/* FIXME: is this check even necessary? */
|
||||
if(iq->referral_count > MAX_REFERRAL_COUNT) {
|
||||
verbose(VERB_DETAIL, "request has exceeded the maximum "
|
||||
verbose(VERB_QUERY, "request has exceeded the maximum "
|
||||
"number of referrrals with %d", iq->referral_count);
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
@@ -1046,7 +1244,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
/* Make sure we have a delegation point, otherwise priming failed
|
||||
* or another failure occurred */
|
||||
if(!iq->dp) {
|
||||
verbose(VERB_DETAIL, "Failed to get a delegation, giving up");
|
||||
verbose(VERB_QUERY, "Failed to get a delegation, giving up");
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
delegpt_log(VERB_ALGO, iq->dp);
|
||||
@@ -1064,11 +1262,41 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
tf_policy = ie->target_fetch_policy[iq->depth];
|
||||
}
|
||||
|
||||
/* if in 0x20 fallback get as many targets as possible */
|
||||
if(iq->caps_fallback) {
|
||||
int extra = 0;
|
||||
size_t naddr, nres, navail;
|
||||
if(!query_for_targets(qstate, iq, ie, id, -1, &extra)) {
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
iq->num_target_queries += extra;
|
||||
if(iq->num_target_queries > 0) {
|
||||
/* wait to get all targets, we want to try em */
|
||||
verbose(VERB_ALGO, "wait for all targets for fallback");
|
||||
return 0;
|
||||
}
|
||||
/* did we do enough fallback queries already? */
|
||||
delegpt_count_addr(iq->dp, &naddr, &nres, &navail);
|
||||
/* the current caps_server is the number of fallbacks sent.
|
||||
* the original query is one that matched too, so we have
|
||||
* caps_server+1 number of matching queries now */
|
||||
if(iq->caps_server+1 >= naddr*3) {
|
||||
/* we're done, process the response */
|
||||
verbose(VERB_ALGO, "0x20 fallback had %d responses "
|
||||
"match for %d wanted, done.",
|
||||
(int)iq->caps_server+1, (int)naddr*3);
|
||||
iq->caps_fallback = 0;
|
||||
iq->state = QUERY_RESP_STATE;
|
||||
return 1;
|
||||
}
|
||||
verbose(VERB_ALGO, "0x20 fallback number %d",
|
||||
(int)iq->caps_server);
|
||||
|
||||
/* if there is a policy to fetch missing targets
|
||||
* opportunistically, do it. we rely on the fact that once a
|
||||
* query (or queries) for a missing name have been issued,
|
||||
* they will not be show up again. */
|
||||
if(tf_policy != 0) {
|
||||
} else if(tf_policy != 0) {
|
||||
int extra = 0;
|
||||
verbose(VERB_ALGO, "attempt to get extra %d targets",
|
||||
tf_policy);
|
||||
@@ -1083,7 +1311,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
|
||||
/* Select the next usable target, filtering out unsuitable targets. */
|
||||
target = iter_server_selection(ie, qstate->env, iq->dp,
|
||||
iq->dp->name, iq->dp->namelen, &iq->dnssec_expected);
|
||||
iq->dp->name, iq->dp->namelen, iq->qchase.qtype,
|
||||
&iq->dnssec_expected, &iq->chase_to_rd);
|
||||
|
||||
/* If no usable target was selected... */
|
||||
if(!target) {
|
||||
@@ -1114,11 +1343,11 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
/* Since a target query might have been made, we
|
||||
* need to check again. */
|
||||
if(iq->num_target_queries == 0) {
|
||||
verbose(VERB_DETAIL, "out of query targets -- "
|
||||
verbose(VERB_QUERY, "out of query targets -- "
|
||||
"returning SERVFAIL");
|
||||
/* fail -- no more targets, no more hope
|
||||
* of targets, no hope of a response. */
|
||||
return error_response(qstate, id,
|
||||
return error_response_cache(qstate, id,
|
||||
LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
}
|
||||
@@ -1141,18 +1370,17 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
}
|
||||
|
||||
/* We have a valid target. */
|
||||
log_query_info(VERB_DETAIL, "sending query:", &iq->qchase);
|
||||
log_name_addr(VERB_DETAIL, "sending to target:", iq->dp->name,
|
||||
log_query_info(VERB_QUERY, "sending query:", &iq->qchase);
|
||||
log_name_addr(VERB_QUERY, "sending to target:", iq->dp->name,
|
||||
&target->addr, target->addrlen);
|
||||
log_assert(fptr_whitelist_modenv_send_query(qstate->env->send_query));
|
||||
fptr_ok(fptr_whitelist_modenv_send_query(qstate->env->send_query));
|
||||
outq = (*qstate->env->send_query)(
|
||||
iq->qchase.qname, iq->qchase.qname_len,
|
||||
iq->qchase.qtype, iq->qchase.qclass,
|
||||
iq->chase_flags, EDNS_DO|BIT_CD,
|
||||
iq->chase_flags | (iq->chase_to_rd?BIT_RD:0), EDNS_DO|BIT_CD,
|
||||
&target->addr, target->addrlen, qstate);
|
||||
if(!outq) {
|
||||
log_err("error sending query to auth server; skip this address");
|
||||
log_addr(0, "error for address:",
|
||||
log_addr(VERB_DETAIL, "error sending query to auth server",
|
||||
&target->addr, target->addrlen);
|
||||
return next_state(iq, QUERYTARGETS_STATE);
|
||||
}
|
||||
@@ -1163,6 +1391,19 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** find NS rrset in given list */
|
||||
static struct ub_packed_rrset_key*
|
||||
find_NS(struct reply_info* rep, size_t from, size_t to)
|
||||
{
|
||||
size_t i;
|
||||
for(i=from; i<to; i++) {
|
||||
if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS)
|
||||
return rep->rrsets[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the query response. All queries end up at this state first. This
|
||||
* process generally consists of analyzing the response and routing the
|
||||
@@ -1184,11 +1425,14 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
enum response_type type;
|
||||
iq->num_current_queries--;
|
||||
if(iq->response == NULL) {
|
||||
iq->chase_to_rd = 0;
|
||||
verbose(VERB_ALGO, "query response was timeout");
|
||||
return next_state(iq, QUERYTARGETS_STATE);
|
||||
}
|
||||
type = response_type_from_server((int)(iq->chase_flags&BIT_RD),
|
||||
type = response_type_from_server(
|
||||
(int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd),
|
||||
iq->response, &iq->qchase, iq->dp);
|
||||
iq->chase_to_rd = 0;
|
||||
if(type == RESPONSE_TYPE_REFERRAL && (iq->chase_flags&BIT_RD)) {
|
||||
/* When forwarding (RD bit is set), we handle referrals
|
||||
* differently. No queries should be sent elsewhere */
|
||||
@@ -1196,6 +1440,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
}
|
||||
if(iq->dnssec_expected && !(iq->chase_flags&BIT_RD)
|
||||
&& type != RESPONSE_TYPE_LAME
|
||||
&& type != RESPONSE_TYPE_REC_LAME
|
||||
&& type != RESPONSE_TYPE_THROWAWAY
|
||||
&& type != RESPONSE_TYPE_UNTYPED) {
|
||||
/* a possible answer, see if it is missing DNSSEC */
|
||||
@@ -1210,6 +1455,20 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
dnsseclame = 1;
|
||||
}
|
||||
}
|
||||
/* see if referral brings us close to the target */
|
||||
if(type == RESPONSE_TYPE_REFERRAL) {
|
||||
struct ub_packed_rrset_key* ns = find_NS(
|
||||
iq->response->rep, iq->response->rep->an_numrrsets,
|
||||
iq->response->rep->an_numrrsets
|
||||
+ iq->response->rep->ns_numrrsets);
|
||||
if(!ns) ns = find_NS(iq->response->rep, 0,
|
||||
iq->response->rep->an_numrrsets);
|
||||
if(!ns || !dname_strict_subdomain_c(ns->rk.dname, iq->dp->name)
|
||||
|| !dname_subdomain_c(iq->qchase.qname, ns->rk.dname)){
|
||||
verbose(VERB_ALGO, "bad referral, throwaway");
|
||||
type = RESPONSE_TYPE_THROWAWAY;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle each of the type cases */
|
||||
if(type == RESPONSE_TYPE_ANSWER) {
|
||||
@@ -1222,7 +1481,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
/* close down outstanding requests to be discarded */
|
||||
outbound_list_clear(&iq->outlist);
|
||||
iq->num_current_queries = 0;
|
||||
log_assert(fptr_whitelist_modenv_detach_subs(
|
||||
fptr_ok(fptr_whitelist_modenv_detach_subs(
|
||||
qstate->env->detach_subs));
|
||||
(*qstate->env->detach_subs)(qstate);
|
||||
iq->num_target_queries = 0;
|
||||
@@ -1232,10 +1491,27 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
* delegation point, and back to the QUERYTARGETS_STATE. */
|
||||
verbose(VERB_DETAIL, "query response was REFERRAL");
|
||||
|
||||
/* Store the referral under the current query */
|
||||
if(!iter_dns_store(qstate->env, &iq->response->qinfo,
|
||||
iq->response->rep, 1))
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
/* if hardened, only store referral if we asked for it */
|
||||
if(!qstate->env->cfg->harden_referral_path ||
|
||||
( qstate->qinfo.qtype == LDNS_RR_TYPE_NS
|
||||
&& (qstate->query_flags&BIT_RD)
|
||||
&& !(qstate->query_flags&BIT_CD)
|
||||
/* we know that all other NS rrsets are scrubbed
|
||||
* away, thus on referral only one is left.
|
||||
* see if that equals the query name... */
|
||||
&& reply_find_rrset_section_ns(iq->response->rep,
|
||||
qstate->qinfo.qname, qstate->qinfo.qname_len,
|
||||
LDNS_RR_TYPE_NS, qstate->qinfo.qclass)
|
||||
)) {
|
||||
/* Store the referral under the current query */
|
||||
if(!iter_dns_store(qstate->env, &iq->response->qinfo,
|
||||
iq->response->rep, 1))
|
||||
return error_response(qstate, id,
|
||||
LDNS_RCODE_SERVFAIL);
|
||||
if(qstate->env->neg_cache)
|
||||
val_neg_addreferral(qstate->env->neg_cache,
|
||||
iq->response->rep, iq->dp->name);
|
||||
}
|
||||
|
||||
/* Reset the event state, setting the current delegation
|
||||
* point to the referral. */
|
||||
@@ -1254,13 +1530,20 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
iq->dnssec_expected = iter_indicates_dnssec(qstate->env,
|
||||
iq->dp, iq->response, iq->qchase.qclass);
|
||||
|
||||
/* spawn off NS and addr to auth servers for the NS we just
|
||||
* got in the referral. This gets authoritative answer
|
||||
* (answer section trust level) rrset.
|
||||
* right after, we detach the subs, answer goes to cache. */
|
||||
if(qstate->env->cfg->harden_referral_path)
|
||||
generate_ns_check(qstate, iq, id);
|
||||
|
||||
/* stop current outstanding queries.
|
||||
* FIXME: should the outstanding queries be waited for and
|
||||
* handled? Say by a subquery that inherits the outbound_entry.
|
||||
*/
|
||||
outbound_list_clear(&iq->outlist);
|
||||
iq->num_current_queries = 0;
|
||||
log_assert(fptr_whitelist_modenv_detach_subs(
|
||||
fptr_ok(fptr_whitelist_modenv_detach_subs(
|
||||
qstate->env->detach_subs));
|
||||
(*qstate->env->detach_subs)(qstate);
|
||||
iq->num_target_queries = 0;
|
||||
@@ -1302,7 +1585,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
*/
|
||||
outbound_list_clear(&iq->outlist);
|
||||
iq->num_current_queries = 0;
|
||||
log_assert(fptr_whitelist_modenv_detach_subs(
|
||||
fptr_ok(fptr_whitelist_modenv_detach_subs(
|
||||
qstate->env->detach_subs));
|
||||
(*qstate->env->detach_subs)(qstate);
|
||||
iq->num_target_queries = 0;
|
||||
@@ -1313,16 +1596,37 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
/* Cache the LAMEness. */
|
||||
verbose(VERB_DETAIL, "query response was %sLAME",
|
||||
dnsseclame?"DNSSEC ":"");
|
||||
if(qstate->reply) {
|
||||
if(!dname_subdomain_c(iq->qchase.qname, iq->dp->name)) {
|
||||
log_err("mark lame: mismatch in qname and dpname");
|
||||
/* throwaway this reply below */
|
||||
} else if(qstate->reply) {
|
||||
/* need addr for lameness cache, but we may have
|
||||
* gotten this from cache, so test to be sure */
|
||||
if(!infra_set_lame(qstate->env->infra_cache,
|
||||
&qstate->reply->addr, qstate->reply->addrlen,
|
||||
iq->dp->name, iq->dp->namelen, time(NULL),
|
||||
dnsseclame))
|
||||
iq->dp->name, iq->dp->namelen,
|
||||
*qstate->env->now, dnsseclame, 0,
|
||||
iq->qchase.qtype))
|
||||
log_err("mark host lame: out of memory");
|
||||
} else log_err("%slame response from cache",
|
||||
dnsseclame?"DNSSEC ":"");
|
||||
} else if(type == RESPONSE_TYPE_REC_LAME) {
|
||||
/* Cache the LAMEness. */
|
||||
verbose(VERB_DETAIL, "query response REC_LAME: "
|
||||
"recursive but not authoritative server");
|
||||
if(!dname_subdomain_c(iq->qchase.qname, iq->dp->name)) {
|
||||
log_err("mark rec_lame: mismatch in qname and dpname");
|
||||
/* throwaway this reply below */
|
||||
} else if(qstate->reply) {
|
||||
/* need addr for lameness cache, but we may have
|
||||
* gotten this from cache, so test to be sure */
|
||||
verbose(VERB_DETAIL, "mark as REC_LAME");
|
||||
if(!infra_set_lame(qstate->env->infra_cache,
|
||||
&qstate->reply->addr, qstate->reply->addrlen,
|
||||
iq->dp->name, iq->dp->namelen,
|
||||
*qstate->env->now, 0, 1, iq->qchase.qtype))
|
||||
log_err("mark host lame: out of memory");
|
||||
}
|
||||
} else if(type == RESPONSE_TYPE_THROWAWAY) {
|
||||
/* LAME and THROWAWAY responses are handled the same way.
|
||||
* In this case, the event is just sent directly back to
|
||||
@@ -1416,6 +1720,24 @@ processPrimeResponse(struct module_qstate* qstate, int id)
|
||||
qstate->return_msg = NULL;
|
||||
}
|
||||
|
||||
/* validate the root or stub after priming (if enabled).
|
||||
* This is the same query as the prime query, but with validation.
|
||||
* Now that we are primed, the additional queries that validation
|
||||
* may need can be resolved, such as DLV. */
|
||||
if(qstate->env->cfg->harden_referral_path) {
|
||||
struct module_qstate* subq = NULL;
|
||||
log_nametypeclass(VERB_ALGO, "schedule prime validation",
|
||||
qstate->qinfo.qname, qstate->qinfo.qtype,
|
||||
qstate->qinfo.qclass);
|
||||
if(!generate_sub_request(qstate->qinfo.qname,
|
||||
qstate->qinfo.qname_len, qstate->qinfo.qtype,
|
||||
qstate->qinfo.qclass, qstate, id, iq,
|
||||
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 1)) {
|
||||
verbose(VERB_ALGO, "could not generate prime check");
|
||||
}
|
||||
generate_a_aaaa_check(qstate, iq, id);
|
||||
}
|
||||
|
||||
/* This event is finished. */
|
||||
qstate->ext_state[id] = module_finished;
|
||||
return 0;
|
||||
@@ -1505,7 +1827,7 @@ static int
|
||||
processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
int id)
|
||||
{
|
||||
log_query_info(VERB_DETAIL, "finishing processing for",
|
||||
log_query_info(VERB_QUERY, "finishing processing for",
|
||||
&qstate->qinfo);
|
||||
|
||||
if(!iq->response) {
|
||||
@@ -1548,11 +1870,6 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
}
|
||||
if(query_dname_compare(qstate->qinfo.qname,
|
||||
iq->response->qinfo.qname) == 0) {
|
||||
/* use server supplied upper/lower case */
|
||||
qstate->qinfo.qname = iq->response->qinfo.qname;
|
||||
}
|
||||
qstate->return_rcode = LDNS_RCODE_NOERROR;
|
||||
qstate->return_msg = iq->response;
|
||||
return 0;
|
||||
@@ -1666,7 +1983,8 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
if(event == module_event_noreply || event == module_event_error) {
|
||||
goto handle_it;
|
||||
}
|
||||
if(event != module_event_reply || !qstate->reply) {
|
||||
if( (event != module_event_reply && event != module_event_capsfail)
|
||||
|| !qstate->reply) {
|
||||
log_err("Bad event combined with response");
|
||||
outbound_list_remove(&iq->outlist, outbound);
|
||||
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
@@ -1697,18 +2015,50 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
|
||||
/* normalize and sanitize: easy to delete items from linked lists */
|
||||
if(!scrub_message(pkt, prs, &iq->qchase, iq->dp->name,
|
||||
qstate->env->scratch, qstate->env))
|
||||
qstate->env->scratch, qstate->env, ie))
|
||||
goto handle_it;
|
||||
|
||||
/* allocate response dns_msg in region */
|
||||
iq->response = dns_alloc_msg(pkt, prs, qstate->region);
|
||||
if(!iq->response)
|
||||
goto handle_it;
|
||||
log_name_addr(VERB_DETAIL, "incoming packet from target:", iq->dp->name,
|
||||
log_query_info(VERB_DETAIL, "response for", &qstate->qinfo);
|
||||
log_name_addr(VERB_DETAIL, "reply from", iq->dp->name,
|
||||
&qstate->reply->addr, qstate->reply->addrlen);
|
||||
if(verbosity >= VERB_ALGO)
|
||||
log_dns_msg("incoming scrubbed packet:", &iq->response->qinfo,
|
||||
iq->response->rep);
|
||||
|
||||
if(event == module_event_capsfail) {
|
||||
if(!iq->caps_fallback) {
|
||||
/* start fallback */
|
||||
iq->caps_fallback = 1;
|
||||
iq->caps_server = 0;
|
||||
iq->caps_reply = iq->response->rep;
|
||||
iq->state = QUERYTARGETS_STATE;
|
||||
iq->num_current_queries--;
|
||||
verbose(VERB_DETAIL, "Capsforid: starting fallback");
|
||||
goto handle_it;
|
||||
} else {
|
||||
/* check if reply is the same, otherwise, fail */
|
||||
if(!reply_equal(iq->response->rep, iq->caps_reply)) {
|
||||
verbose(VERB_DETAIL, "Capsforid fallback: "
|
||||
"getting different replies, failed");
|
||||
outbound_list_remove(&iq->outlist, outbound);
|
||||
(void)error_response(qstate, id,
|
||||
LDNS_RCODE_SERVFAIL);
|
||||
return;
|
||||
}
|
||||
/* continue the fallback procedure at next server */
|
||||
iq->caps_server++;
|
||||
iq->state = QUERYTARGETS_STATE;
|
||||
iq->num_current_queries--;
|
||||
verbose(VERB_DETAIL, "Capsforid: reply is equal. "
|
||||
"go to next fallback");
|
||||
goto handle_it;
|
||||
}
|
||||
}
|
||||
iq->caps_fallback = 0; /* if we were in fallback, 0x20 is OK now */
|
||||
|
||||
handle_it:
|
||||
outbound_list_remove(&iq->outlist, outbound);
|
||||
@@ -1721,12 +2071,12 @@ iter_operate(struct module_qstate* qstate, enum module_ev event, int id,
|
||||
{
|
||||
struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
|
||||
struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
|
||||
verbose(VERB_DETAIL, "iterator[module %d] operate: extstate:%s event:%s",
|
||||
verbose(VERB_QUERY, "iterator[module %d] operate: extstate:%s event:%s",
|
||||
id, strextstate(qstate->ext_state[id]), strmodulevent(event));
|
||||
if(iq) log_query_info(VERB_DETAIL, "iterator operate: query",
|
||||
if(iq) log_query_info(VERB_QUERY, "iterator operate: query",
|
||||
&qstate->qinfo);
|
||||
if(iq && qstate->qinfo.qname != iq->qchase.qname)
|
||||
log_query_info(VERB_DETAIL, "iterator operate: chased to",
|
||||
log_query_info(VERB_QUERY, "iterator operate: chased to",
|
||||
&iq->qchase);
|
||||
|
||||
/* perform iterator state machine */
|
||||
@@ -1740,11 +2090,11 @@ iter_operate(struct module_qstate* qstate, enum module_ev event, int id,
|
||||
process_request(qstate, iq, ie, id);
|
||||
return;
|
||||
}
|
||||
if(event == module_event_pass) {
|
||||
if(iq && event == module_event_pass) {
|
||||
iter_handle(qstate, iq, ie, id);
|
||||
return;
|
||||
}
|
||||
if(outbound) {
|
||||
if(iq && outbound) {
|
||||
process_response(qstate, iq, ie, id, outbound, event);
|
||||
return;
|
||||
}
|
||||
|
||||
+18
-1
@@ -50,6 +50,7 @@ struct iter_hints;
|
||||
struct iter_forwards;
|
||||
struct iter_donotq;
|
||||
struct iter_prep_list;
|
||||
struct iter_priv;
|
||||
|
||||
/** max number of query restarts. Determines max number of CNAME chain. */
|
||||
#define MAX_RESTART_COUNT 8
|
||||
@@ -65,7 +66,11 @@ struct iter_prep_list;
|
||||
*/
|
||||
#define USEFUL_SERVER_TOP_TIMEOUT 120000
|
||||
/** number of retries on outgoing queries */
|
||||
#define OUTBOUND_MSG_RETRY 4
|
||||
#define OUTBOUND_MSG_RETRY 5
|
||||
/** RTT band, within this amount from the best, servers are chosen randomly.
|
||||
* Chosen so that the UNKNOWN_SERVER_NICENESS falls within the band of a
|
||||
* fast server, this causes server exploration as a side benefit. msec. */
|
||||
#define RTT_BAND 400
|
||||
|
||||
/**
|
||||
* Global state for the iterator.
|
||||
@@ -88,6 +93,9 @@ struct iter_env {
|
||||
/** A set of inetaddrs that should never be queried. */
|
||||
struct iter_donotq* donotq;
|
||||
|
||||
/** private address space and private domains */
|
||||
struct iter_priv* priv;
|
||||
|
||||
/** The maximum dependency depth that this resolver will pursue. */
|
||||
int max_dependency_depth;
|
||||
|
||||
@@ -202,6 +210,8 @@ struct iter_qstate {
|
||||
struct query_info qchase;
|
||||
/** query flags to use when chasing the answer (i.e. RD flag) */
|
||||
uint16_t chase_flags;
|
||||
/** true if we set RD bit because of last resort recursion lame query*/
|
||||
int chase_to_rd;
|
||||
|
||||
/**
|
||||
* This is the current delegation point for an in-progress query. This
|
||||
@@ -210,6 +220,13 @@ struct iter_qstate {
|
||||
*/
|
||||
struct delegpt* dp;
|
||||
|
||||
/** state for 0x20 fallback when capsfail happens, 0 not a fallback */
|
||||
int caps_fallback;
|
||||
/** state for capsfail: current server number to try */
|
||||
size_t caps_server;
|
||||
/** state for capsfail: stored query for comparisons */
|
||||
struct reply_info* caps_reply;
|
||||
|
||||
/** Current delegation message - returned for non-RD queries */
|
||||
struct dns_msg* deleg_msg;
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* libunbound/context.c - validating context for unbound internal use
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains the validator context structure.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "libunbound/context.h"
|
||||
#include "util/module.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/net_help.h"
|
||||
#include "services/modstack.h"
|
||||
#include "services/localzone.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
|
||||
int
|
||||
context_finalize(struct ub_ctx* ctx)
|
||||
{
|
||||
struct config_file* cfg = ctx->env->cfg;
|
||||
verbosity = cfg->verbosity;
|
||||
if(ctx->logfile_override)
|
||||
log_file(ctx->log_out);
|
||||
else log_init(cfg->logfile, cfg->use_syslog, NULL);
|
||||
config_apply(cfg);
|
||||
if(!modstack_setup(&ctx->mods, cfg->module_conf, ctx->env))
|
||||
return UB_INITFAIL;
|
||||
ctx->local_zones = local_zones_create();
|
||||
if(!ctx->local_zones)
|
||||
return UB_NOMEM;
|
||||
if(!local_zones_apply_cfg(ctx->local_zones, cfg))
|
||||
return UB_INITFAIL;
|
||||
if(!ctx->env->msg_cache ||
|
||||
cfg->msg_cache_size != slabhash_get_size(ctx->env->msg_cache) ||
|
||||
cfg->msg_cache_slabs != ctx->env->msg_cache->size) {
|
||||
slabhash_delete(ctx->env->msg_cache);
|
||||
ctx->env->msg_cache = slabhash_create(cfg->msg_cache_slabs,
|
||||
HASH_DEFAULT_STARTARRAY, cfg->msg_cache_size,
|
||||
msgreply_sizefunc, query_info_compare,
|
||||
query_entry_delete, reply_info_delete, NULL);
|
||||
if(!ctx->env->msg_cache)
|
||||
return UB_NOMEM;
|
||||
}
|
||||
ctx->env->rrset_cache = rrset_cache_adjust(ctx->env->rrset_cache,
|
||||
ctx->env->cfg, ctx->env->alloc);
|
||||
if(!ctx->env->rrset_cache)
|
||||
return UB_NOMEM;
|
||||
ctx->env->infra_cache = infra_adjust(ctx->env->infra_cache, cfg);
|
||||
if(!ctx->env->infra_cache)
|
||||
return UB_NOMEM;
|
||||
ctx->finalized = 1;
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int context_query_cmp(const void* a, const void* b)
|
||||
{
|
||||
if( *(int*)a < *(int*)b )
|
||||
return -1;
|
||||
if( *(int*)a > *(int*)b )
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
context_query_delete(struct ctx_query* q)
|
||||
{
|
||||
if(!q) return;
|
||||
ub_resolve_free(q->res);
|
||||
free(q->msg);
|
||||
free(q);
|
||||
}
|
||||
|
||||
/** How many times to try to find an unused query-id-number for async */
|
||||
#define NUM_ID_TRIES 100000
|
||||
/** find next useful id number of 0 on error */
|
||||
static int
|
||||
find_id(struct ub_ctx* ctx, int* id)
|
||||
{
|
||||
size_t tries = 0;
|
||||
ctx->next_querynum++;
|
||||
while(rbtree_search(&ctx->queries, &ctx->next_querynum)) {
|
||||
ctx->next_querynum++; /* numerical wraparound is fine */
|
||||
if(tries++ > NUM_ID_TRIES)
|
||||
return 0;
|
||||
}
|
||||
*id = ctx->next_querynum;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct ctx_query*
|
||||
context_new(struct ub_ctx* ctx, char* name, int rrtype, int rrclass,
|
||||
ub_callback_t cb, void* cbarg)
|
||||
{
|
||||
struct ctx_query* q = (struct ctx_query*)calloc(1, sizeof(*q));
|
||||
if(!q) return NULL;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(!find_id(ctx, &q->querynum)) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
free(q);
|
||||
return NULL;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
q->node.key = &q->querynum;
|
||||
q->async = (cb != NULL);
|
||||
q->cb = cb;
|
||||
q->cb_arg = cbarg;
|
||||
q->res = (struct ub_result*)calloc(1, sizeof(*q->res));
|
||||
if(!q->res) {
|
||||
free(q);
|
||||
return NULL;
|
||||
}
|
||||
q->res->qname = strdup(name);
|
||||
if(!q->res->qname) {
|
||||
free(q->res);
|
||||
free(q);
|
||||
return NULL;
|
||||
}
|
||||
q->res->qtype = rrtype;
|
||||
q->res->qclass = rrclass;
|
||||
|
||||
/* add to query list */
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(q->async)
|
||||
ctx->num_async ++;
|
||||
(void)rbtree_insert(&ctx->queries, &q->node);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return q;
|
||||
}
|
||||
|
||||
struct alloc_cache*
|
||||
context_obtain_alloc(struct ub_ctx* ctx, int locking)
|
||||
{
|
||||
struct alloc_cache* a;
|
||||
int tnum = 0;
|
||||
if(locking) {
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
}
|
||||
a = ctx->alloc_list;
|
||||
if(a)
|
||||
ctx->alloc_list = a->super; /* snip off list */
|
||||
else tnum = ctx->thr_next_num++;
|
||||
if(locking) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
if(a) {
|
||||
a->super = &ctx->superalloc;
|
||||
return a;
|
||||
}
|
||||
a = (struct alloc_cache*)calloc(1, sizeof(*a));
|
||||
if(!a)
|
||||
return NULL;
|
||||
alloc_init(a, &ctx->superalloc, tnum);
|
||||
return a;
|
||||
}
|
||||
|
||||
void
|
||||
context_release_alloc(struct ub_ctx* ctx, struct alloc_cache* alloc,
|
||||
int locking)
|
||||
{
|
||||
if(!ctx || !alloc)
|
||||
return;
|
||||
if(locking) {
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
}
|
||||
alloc->super = ctx->alloc_list;
|
||||
ctx->alloc_list = alloc;
|
||||
if(locking) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
context_serialize_new_query(struct ctx_query* q, uint32_t* len)
|
||||
{
|
||||
/* format for new query is
|
||||
* o uint32 cmd
|
||||
* o uint32 id
|
||||
* o uint32 type
|
||||
* o uint32 class
|
||||
* o rest queryname (string)
|
||||
*/
|
||||
uint8_t* p;
|
||||
size_t slen = strlen(q->res->qname) + 1/*end of string*/;
|
||||
*len = sizeof(uint32_t)*4 + slen;
|
||||
p = (uint8_t*)malloc(*len);
|
||||
if(!p) return NULL;
|
||||
ldns_write_uint32(p, UB_LIBCMD_NEWQUERY);
|
||||
ldns_write_uint32(p+sizeof(uint32_t), (uint32_t)q->querynum);
|
||||
ldns_write_uint32(p+2*sizeof(uint32_t), (uint32_t)q->res->qtype);
|
||||
ldns_write_uint32(p+3*sizeof(uint32_t), (uint32_t)q->res->qclass);
|
||||
memmove(p+4*sizeof(uint32_t), q->res->qname, slen);
|
||||
return p;
|
||||
}
|
||||
|
||||
struct ctx_query*
|
||||
context_deserialize_new_query(struct ub_ctx* ctx, uint8_t* p, uint32_t len)
|
||||
{
|
||||
struct ctx_query* q = (struct ctx_query*)calloc(1, sizeof(*q));
|
||||
if(!q) return NULL;
|
||||
if(len < 4*sizeof(uint32_t)+1) {
|
||||
free(q);
|
||||
return NULL;
|
||||
}
|
||||
log_assert( ldns_read_uint32(p) == UB_LIBCMD_NEWQUERY);
|
||||
q->querynum = (int)ldns_read_uint32(p+sizeof(uint32_t));
|
||||
q->node.key = &q->querynum;
|
||||
q->async = 1;
|
||||
q->res = (struct ub_result*)calloc(1, sizeof(*q->res));
|
||||
if(!q->res) {
|
||||
free(q);
|
||||
return NULL;
|
||||
}
|
||||
q->res->qtype = (int)ldns_read_uint32(p+2*sizeof(uint32_t));
|
||||
q->res->qclass = (int)ldns_read_uint32(p+3*sizeof(uint32_t));
|
||||
q->res->qname = strdup((char*)(p+4*sizeof(uint32_t)));
|
||||
if(!q->res->qname) {
|
||||
free(q->res);
|
||||
free(q);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** add to query list */
|
||||
ctx->num_async++;
|
||||
(void)rbtree_insert(&ctx->queries, &q->node);
|
||||
return q;
|
||||
}
|
||||
|
||||
struct ctx_query*
|
||||
context_lookup_new_query(struct ub_ctx* ctx, uint8_t* p, uint32_t len)
|
||||
{
|
||||
struct ctx_query* q;
|
||||
int querynum;
|
||||
if(len < 4*sizeof(uint32_t)+1) {
|
||||
return NULL;
|
||||
}
|
||||
log_assert( ldns_read_uint32(p) == UB_LIBCMD_NEWQUERY);
|
||||
querynum = (int)ldns_read_uint32(p+sizeof(uint32_t));
|
||||
q = (struct ctx_query*)rbtree_search(&ctx->queries, &querynum);
|
||||
if(!q) {
|
||||
return NULL;
|
||||
}
|
||||
log_assert(q->async);
|
||||
return q;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
context_serialize_answer(struct ctx_query* q, int err, ldns_buffer* pkt,
|
||||
uint32_t* len)
|
||||
{
|
||||
/* answer format
|
||||
* o uint32 cmd
|
||||
* o uint32 id
|
||||
* o uint32 error_code
|
||||
* o uint32 msg_security
|
||||
* o the remainder is the answer msg from resolver lookup.
|
||||
* remainder can be length 0.
|
||||
*/
|
||||
size_t pkt_len = pkt?ldns_buffer_remaining(pkt):0;
|
||||
uint8_t* p;
|
||||
*len = sizeof(uint32_t)*4 + pkt_len;
|
||||
p = (uint8_t*)malloc(*len);
|
||||
if(!p) return NULL;
|
||||
ldns_write_uint32(p, UB_LIBCMD_ANSWER);
|
||||
ldns_write_uint32(p+sizeof(uint32_t), (uint32_t)q->querynum);
|
||||
ldns_write_uint32(p+2*sizeof(uint32_t), (uint32_t)err);
|
||||
ldns_write_uint32(p+3*sizeof(uint32_t), (uint32_t)q->msg_security);
|
||||
if(pkt_len > 0)
|
||||
memmove(p+4*sizeof(uint32_t), ldns_buffer_begin(pkt), pkt_len);
|
||||
return p;
|
||||
}
|
||||
|
||||
struct ctx_query*
|
||||
context_deserialize_answer(struct ub_ctx* ctx,
|
||||
uint8_t* p, uint32_t len, int* err)
|
||||
{
|
||||
struct ctx_query* q = NULL ;
|
||||
int id;
|
||||
if(len < 4*sizeof(uint32_t)) return NULL;
|
||||
log_assert( ldns_read_uint32(p) == UB_LIBCMD_ANSWER);
|
||||
id = (int)ldns_read_uint32(p+sizeof(uint32_t));
|
||||
q = (struct ctx_query*)rbtree_search(&ctx->queries, &id);
|
||||
if(!q) return NULL;
|
||||
*err = (int)ldns_read_uint32(p+2*sizeof(uint32_t));
|
||||
q->msg_security = ldns_read_uint32(p+3*sizeof(uint32_t));
|
||||
if(len > 4*sizeof(uint32_t)) {
|
||||
q->msg_len = len - 4*sizeof(uint32_t);
|
||||
q->msg = (uint8_t*)memdup(p+4*sizeof(uint32_t), q->msg_len);
|
||||
if(!q->msg) {
|
||||
/* pass malloc failure to the user callback */
|
||||
q->msg_len = 0;
|
||||
*err = UB_NOMEM;
|
||||
return q;
|
||||
}
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
context_serialize_cancel(struct ctx_query* q, uint32_t* len)
|
||||
{
|
||||
/* format of cancel:
|
||||
* o uint32 cmd
|
||||
* o uint32 async-id */
|
||||
uint8_t* p = (uint8_t*)malloc(2*sizeof(uint32_t));
|
||||
if(!p) return NULL;
|
||||
*len = 2*sizeof(uint32_t);
|
||||
ldns_write_uint32(p, UB_LIBCMD_CANCEL);
|
||||
ldns_write_uint32(p+sizeof(uint32_t), (uint32_t)q->querynum);
|
||||
return p;
|
||||
}
|
||||
|
||||
struct ctx_query* context_deserialize_cancel(struct ub_ctx* ctx,
|
||||
uint8_t* p, uint32_t len)
|
||||
{
|
||||
struct ctx_query* q;
|
||||
int id;
|
||||
if(len != 2*sizeof(uint32_t)) return NULL;
|
||||
log_assert( ldns_read_uint32(p) == UB_LIBCMD_CANCEL);
|
||||
id = (int)ldns_read_uint32(p+sizeof(uint32_t));
|
||||
q = (struct ctx_query*)rbtree_search(&ctx->queries, &id);
|
||||
return q;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
context_serialize_quit(uint32_t* len)
|
||||
{
|
||||
uint8_t* p = (uint8_t*)malloc(sizeof(uint32_t));
|
||||
if(!p)
|
||||
return NULL;
|
||||
*len = sizeof(uint32_t);
|
||||
ldns_write_uint32(p, UB_LIBCMD_QUIT);
|
||||
return p;
|
||||
}
|
||||
|
||||
enum ub_ctx_cmd context_serial_getcmd(uint8_t* p, uint32_t len)
|
||||
{
|
||||
uint32_t v;
|
||||
if((size_t)len < sizeof(v))
|
||||
return UB_LIBCMD_QUIT;
|
||||
v = ldns_read_uint32(p);
|
||||
return v;
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* libunbound/context.h - validating context for unbound internal use
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains the validator context structure.
|
||||
*/
|
||||
#ifndef LIBUNBOUND_CONTEXT_H
|
||||
#define LIBUNBOUND_CONTEXT_H
|
||||
#include "util/locks.h"
|
||||
#include "util/alloc.h"
|
||||
#include "util/rbtree.h"
|
||||
#include "services/modstack.h"
|
||||
#include "libunbound/unbound.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
struct libworker;
|
||||
struct tube;
|
||||
|
||||
/**
|
||||
* The context structure
|
||||
*
|
||||
* Contains two pipes for async service
|
||||
* qq : write queries to the async service pid/tid.
|
||||
* rr : read results from the async service pid/tid.
|
||||
*/
|
||||
struct ub_ctx {
|
||||
/* --- pipes --- */
|
||||
/** mutex on query write pipe */
|
||||
lock_basic_t qqpipe_lock;
|
||||
/** the query write pipe */
|
||||
struct tube* qq_pipe;
|
||||
/** mutex on result read pipe */
|
||||
lock_basic_t rrpipe_lock;
|
||||
/** the result read pipe */
|
||||
struct tube* rr_pipe;
|
||||
|
||||
/* --- shared data --- */
|
||||
/** mutex for access to env.cfg, finalized and dothread */
|
||||
lock_basic_t cfglock;
|
||||
/**
|
||||
* The context has been finalized
|
||||
* This is after config when the first resolve is done.
|
||||
* The modules are inited (module-init()) and shared caches created.
|
||||
*/
|
||||
int finalized;
|
||||
|
||||
/** is bg worker created yet ? */
|
||||
int created_bg;
|
||||
/** pid of bg worker process */
|
||||
pid_t bg_pid;
|
||||
/** tid of bg worker thread */
|
||||
ub_thread_t bg_tid;
|
||||
|
||||
/** do threading (instead of forking) for async resolution */
|
||||
int dothread;
|
||||
/** next thread number for new threads */
|
||||
int thr_next_num;
|
||||
/** if logfile is overriden */
|
||||
int logfile_override;
|
||||
/** what logfile to use instead */
|
||||
FILE* log_out;
|
||||
/**
|
||||
* List of alloc-cache-id points per threadnum for notinuse threads.
|
||||
* Simply the entire struct alloc_cache with the 'super' member used
|
||||
* to link a simply linked list. Reset super member to the superalloc
|
||||
* before use.
|
||||
*/
|
||||
struct alloc_cache* alloc_list;
|
||||
|
||||
/** shared caches, and so on */
|
||||
struct alloc_cache superalloc;
|
||||
/** module env master value */
|
||||
struct module_env* env;
|
||||
/** module stack */
|
||||
struct module_stack mods;
|
||||
/** local authority zones */
|
||||
struct local_zones* local_zones;
|
||||
/** random state used to seed new random state structures */
|
||||
struct ub_randstate* seed_rnd;
|
||||
|
||||
/** next query number (to try) to use */
|
||||
int next_querynum;
|
||||
/** number of async queries outstanding */
|
||||
size_t num_async;
|
||||
/**
|
||||
* Tree of outstanding queries. Indexed by querynum
|
||||
* Used when results come in for async to lookup.
|
||||
* Used when cancel is done for lookup (and delete).
|
||||
* Used to see if querynum is free for use.
|
||||
* Content of type ctx_query.
|
||||
*/
|
||||
rbtree_t queries;
|
||||
};
|
||||
|
||||
/**
|
||||
* The queries outstanding for the libunbound resolver.
|
||||
* These are outstanding for async resolution.
|
||||
* But also, outstanding for sync resolution by one of the threads that
|
||||
* has joined the threadpool.
|
||||
*/
|
||||
struct ctx_query {
|
||||
/** node in rbtree, must be first entry, key is ptr to the querynum */
|
||||
struct rbnode_t node;
|
||||
/** query id number, key for node */
|
||||
int querynum;
|
||||
/** was this an async query? */
|
||||
int async;
|
||||
/** was this query cancelled (for bg worker) */
|
||||
int cancelled;
|
||||
|
||||
/** for async query, the callback function */
|
||||
ub_callback_t cb;
|
||||
/** for async query, the callback user arg */
|
||||
void* cb_arg;
|
||||
|
||||
/** answer message, result from resolver lookup. */
|
||||
uint8_t* msg;
|
||||
/** resulting message length. */
|
||||
size_t msg_len;
|
||||
/** validation status on security */
|
||||
enum sec_status msg_security;
|
||||
/** store libworker that is handling this query */
|
||||
struct libworker* w;
|
||||
|
||||
/** result structure, also contains original query, type, class.
|
||||
* malloced ptr ready to hand to the client. */
|
||||
struct ub_result* res;
|
||||
};
|
||||
|
||||
/**
|
||||
* The error constants
|
||||
*/
|
||||
enum ub_ctx_err {
|
||||
/** no error */
|
||||
UB_NOERROR = 0,
|
||||
/** socket operation. Set to -1, so that if an error from _fd() is
|
||||
* passed (-1) it gives a socket error. */
|
||||
UB_SOCKET = -1,
|
||||
/** alloc failure */
|
||||
UB_NOMEM = -2,
|
||||
/** syntax error */
|
||||
UB_SYNTAX = -3,
|
||||
/** DNS service failed */
|
||||
UB_SERVFAIL = -4,
|
||||
/** fork() failed */
|
||||
UB_FORKFAIL = -5,
|
||||
/** cfg change after finalize() */
|
||||
UB_AFTERFINAL = -6,
|
||||
/** initialization failed (bad settings) */
|
||||
UB_INITFAIL = -7,
|
||||
/** error in pipe communication with async bg worker */
|
||||
UB_PIPE = -8,
|
||||
/** error reading from file (resolv.conf) */
|
||||
UB_READFILE = -9
|
||||
};
|
||||
|
||||
/**
|
||||
* Command codes for libunbound pipe.
|
||||
*
|
||||
* Serialization looks like this:
|
||||
* o length (of remainder) uint32.
|
||||
* o uint32 command code.
|
||||
* o per command format.
|
||||
*/
|
||||
enum ub_ctx_cmd {
|
||||
/** QUIT */
|
||||
UB_LIBCMD_QUIT = 0,
|
||||
/** New query, sent to bg worker */
|
||||
UB_LIBCMD_NEWQUERY,
|
||||
/** Cancel query, sent to bg worker */
|
||||
UB_LIBCMD_CANCEL,
|
||||
/** Query result, originates from bg worker */
|
||||
UB_LIBCMD_ANSWER
|
||||
};
|
||||
|
||||
/**
|
||||
* finalize a context.
|
||||
* @param ctx: context to finalize. creates shared data.
|
||||
* @return 0 if OK, or errcode.
|
||||
*/
|
||||
int context_finalize(struct ub_ctx* ctx);
|
||||
|
||||
/** compare two ctx_query elements */
|
||||
int context_query_cmp(const void* a, const void* b);
|
||||
|
||||
/**
|
||||
* delete context query
|
||||
* @param q: query to delete, including message packet and prealloc result
|
||||
*/
|
||||
void context_query_delete(struct ctx_query* q);
|
||||
|
||||
/**
|
||||
* Create new query in context, add to querynum list.
|
||||
* @param ctx: context
|
||||
* @param name: query name
|
||||
* @param rrtype: type
|
||||
* @param rrclass: class
|
||||
* @param cb: callback for async, or NULL for sync.
|
||||
* @param cbarg: user arg for async queries.
|
||||
* @return new ctx_query or NULL for malloc failure.
|
||||
*/
|
||||
struct ctx_query* context_new(struct ub_ctx* ctx, char* name, int rrtype,
|
||||
int rrclass, ub_callback_t cb, void* cbarg);
|
||||
|
||||
/**
|
||||
* Get a new alloc. Creates a new one or uses a cached one.
|
||||
* @param ctx: context
|
||||
* @param locking: if true, cfglock is locked while getting alloc.
|
||||
* @return an alloc, or NULL on mem error.
|
||||
*/
|
||||
struct alloc_cache* context_obtain_alloc(struct ub_ctx* ctx, int locking);
|
||||
|
||||
/**
|
||||
* Release an alloc. Puts it into the cache.
|
||||
* @param ctx: context
|
||||
* @param locking: if true, cfglock is locked while releasing alloc.
|
||||
* @param alloc: alloc to relinquish.
|
||||
*/
|
||||
void context_release_alloc(struct ub_ctx* ctx, struct alloc_cache* alloc,
|
||||
int locking);
|
||||
|
||||
/**
|
||||
* Serialize a context query that questions data.
|
||||
* This serializes the query name, type, ...
|
||||
* As well as command code 'new_query'.
|
||||
* @param q: context query
|
||||
* @param len: the length of the allocation is returned.
|
||||
* @return: an alloc, or NULL on mem error.
|
||||
*/
|
||||
uint8_t* context_serialize_new_query(struct ctx_query* q, uint32_t* len);
|
||||
|
||||
/**
|
||||
* Serialize a context_query result to hand back to user.
|
||||
* This serializes the query name, type, ..., and result.
|
||||
* As well as command code 'answer'.
|
||||
* @param q: context query
|
||||
* @param err: error code to pass to client.
|
||||
* @param pkt: the packet to add, can be NULL.
|
||||
* @param len: the length of the allocation is returned.
|
||||
* @return: an alloc, or NULL on mem error.
|
||||
*/
|
||||
uint8_t* context_serialize_answer(struct ctx_query* q, int err,
|
||||
ldns_buffer* pkt, uint32_t* len);
|
||||
|
||||
/**
|
||||
* Serialize a query cancellation. Serializes query async id
|
||||
* as well as command code 'cancel'
|
||||
* @param q: context query
|
||||
* @param len: the length of the allocation is returned.
|
||||
* @return: an alloc, or NULL on mem error.
|
||||
*/
|
||||
uint8_t* context_serialize_cancel(struct ctx_query* q, uint32_t* len);
|
||||
|
||||
/**
|
||||
* Serialize a 'quit' command.
|
||||
* @param len: the length of the allocation is returned.
|
||||
* @return: an alloc, or NULL on mem error.
|
||||
*/
|
||||
uint8_t* context_serialize_quit(uint32_t* len);
|
||||
|
||||
/**
|
||||
* Obtain command code from serialized buffer
|
||||
* @param p: buffer serialized.
|
||||
* @param len: length of buffer.
|
||||
* @return command code or QUIT on error.
|
||||
*/
|
||||
enum ub_ctx_cmd context_serial_getcmd(uint8_t* p, uint32_t len);
|
||||
|
||||
/**
|
||||
* Lookup query from new_query buffer.
|
||||
* @param ctx: context
|
||||
* @param p: buffer serialized.
|
||||
* @param len: length of buffer.
|
||||
* @return looked up ctx_query or NULL for malloc failure.
|
||||
*/
|
||||
struct ctx_query* context_lookup_new_query(struct ub_ctx* ctx,
|
||||
uint8_t* p, uint32_t len);
|
||||
|
||||
/**
|
||||
* Deserialize a new_query buffer.
|
||||
* @param ctx: context
|
||||
* @param p: buffer serialized.
|
||||
* @param len: length of buffer.
|
||||
* @return new ctx_query or NULL for malloc failure.
|
||||
*/
|
||||
struct ctx_query* context_deserialize_new_query(struct ub_ctx* ctx,
|
||||
uint8_t* p, uint32_t len);
|
||||
|
||||
/**
|
||||
* Deserialize an answer buffer.
|
||||
* @param ctx: context
|
||||
* @param p: buffer serialized.
|
||||
* @param len: length of buffer.
|
||||
* @param err: error code to be returned to client is passed.
|
||||
* @return ctx_query with answer added or NULL for malloc failure.
|
||||
*/
|
||||
struct ctx_query* context_deserialize_answer(struct ub_ctx* ctx,
|
||||
uint8_t* p, uint32_t len, int* err);
|
||||
|
||||
/**
|
||||
* Deserialize a cancel buffer.
|
||||
* @param ctx: context
|
||||
* @param p: buffer serialized.
|
||||
* @param len: length of buffer.
|
||||
* @return ctx_query to cancel or NULL for failure.
|
||||
*/
|
||||
struct ctx_query* context_deserialize_cancel(struct ub_ctx* ctx,
|
||||
uint8_t* p, uint32_t len);
|
||||
|
||||
#endif /* LIBUNBOUND_CONTEXT_H */
|
||||
@@ -0,0 +1,907 @@
|
||||
/*
|
||||
* unbound.c - unbound validating resolver public API implementation
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to resolve DNS queries and
|
||||
* validate the answers. Synchonously and asynchronously.
|
||||
*
|
||||
*/
|
||||
|
||||
/* include the public api first, it should be able to stand alone */
|
||||
#include "libunbound/unbound.h"
|
||||
#include "config.h"
|
||||
#include "libunbound/context.h"
|
||||
#include "libunbound/libworker.h"
|
||||
#include "util/locks.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/alloc.h"
|
||||
#include "util/module.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/log.h"
|
||||
#include "util/random.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/tube.h"
|
||||
#include "services/modstack.h"
|
||||
#include "services/localzone.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "services/cache/rrset.h"
|
||||
|
||||
struct ub_ctx*
|
||||
ub_ctx_create()
|
||||
{
|
||||
struct ub_ctx* ctx;
|
||||
unsigned int seed;
|
||||
#ifdef USE_WINSOCK
|
||||
int r;
|
||||
WSADATA wsa_data;
|
||||
#endif
|
||||
|
||||
log_init(NULL, 0, NULL); /* logs to stderr */
|
||||
log_ident_set("libunbound");
|
||||
#ifdef USE_WINSOCK
|
||||
if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) {
|
||||
log_err("could not init winsock. WSAStartup: %s",
|
||||
wsa_strerror(r));
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
verbosity = 0; /* errors only */
|
||||
checklock_start();
|
||||
ctx = (struct ub_ctx*)calloc(1, sizeof(*ctx));
|
||||
if(!ctx) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
alloc_init(&ctx->superalloc, NULL, 0);
|
||||
seed = (unsigned int)time(NULL) ^ (unsigned int)getpid();
|
||||
if(!(ctx->seed_rnd = ub_initstate(seed, NULL))) {
|
||||
seed = 0;
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
seed = 0;
|
||||
if((ctx->qq_pipe = tube_create()) == NULL) {
|
||||
int e = errno;
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
errno = e;
|
||||
return NULL;
|
||||
}
|
||||
if((ctx->rr_pipe = tube_create()) == NULL) {
|
||||
int e = errno;
|
||||
tube_delete(ctx->qq_pipe);
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
errno = e;
|
||||
return NULL;
|
||||
}
|
||||
lock_basic_init(&ctx->qqpipe_lock);
|
||||
lock_basic_init(&ctx->rrpipe_lock);
|
||||
lock_basic_init(&ctx->cfglock);
|
||||
ctx->env = (struct module_env*)calloc(1, sizeof(*ctx->env));
|
||||
if(!ctx->env) {
|
||||
tube_delete(ctx->qq_pipe);
|
||||
tube_delete(ctx->rr_pipe);
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
ctx->env->cfg = config_create_forlib();
|
||||
if(!ctx->env->cfg) {
|
||||
tube_delete(ctx->qq_pipe);
|
||||
tube_delete(ctx->rr_pipe);
|
||||
free(ctx->env);
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
ctx->env->alloc = &ctx->superalloc;
|
||||
ctx->env->worker = NULL;
|
||||
ctx->env->need_to_validate = 0;
|
||||
modstack_init(&ctx->mods);
|
||||
rbtree_init(&ctx->queries, &context_query_cmp);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/** delete q */
|
||||
static void
|
||||
delq(rbnode_t* n, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct ctx_query* q = (struct ctx_query*)n;
|
||||
context_query_delete(q);
|
||||
}
|
||||
|
||||
void
|
||||
ub_ctx_delete(struct ub_ctx* ctx)
|
||||
{
|
||||
struct alloc_cache* a, *na;
|
||||
if(!ctx) return;
|
||||
/* stop the bg thread */
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->created_bg) {
|
||||
uint8_t* msg;
|
||||
uint32_t len;
|
||||
uint32_t cmd = UB_LIBCMD_QUIT;
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
lock_basic_lock(&ctx->qqpipe_lock);
|
||||
(void)tube_write_msg(ctx->qq_pipe, (uint8_t*)&cmd,
|
||||
(uint32_t)sizeof(cmd), 0);
|
||||
lock_basic_unlock(&ctx->qqpipe_lock);
|
||||
lock_basic_lock(&ctx->rrpipe_lock);
|
||||
while(tube_read_msg(ctx->rr_pipe, &msg, &len, 0)) {
|
||||
/* discard all results except a quit confirm */
|
||||
if(context_serial_getcmd(msg, len) == UB_LIBCMD_QUIT) {
|
||||
free(msg);
|
||||
break;
|
||||
}
|
||||
free(msg);
|
||||
}
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
|
||||
/* if bg worker is a thread, wait for it to exit, so that all
|
||||
* resources are really gone. */
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->dothread) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
ub_thread_join(ctx->bg_tid);
|
||||
} else {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
}
|
||||
else {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
|
||||
|
||||
modstack_desetup(&ctx->mods, ctx->env);
|
||||
a = ctx->alloc_list;
|
||||
while(a) {
|
||||
na = a->super;
|
||||
a->super = &ctx->superalloc;
|
||||
alloc_clear(a);
|
||||
free(a);
|
||||
a = na;
|
||||
}
|
||||
local_zones_delete(ctx->local_zones);
|
||||
lock_basic_destroy(&ctx->qqpipe_lock);
|
||||
lock_basic_destroy(&ctx->rrpipe_lock);
|
||||
lock_basic_destroy(&ctx->cfglock);
|
||||
tube_delete(ctx->qq_pipe);
|
||||
tube_delete(ctx->rr_pipe);
|
||||
if(ctx->env) {
|
||||
slabhash_delete(ctx->env->msg_cache);
|
||||
rrset_cache_delete(ctx->env->rrset_cache);
|
||||
infra_delete(ctx->env->infra_cache);
|
||||
config_delete(ctx->env->cfg);
|
||||
free(ctx->env);
|
||||
}
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
alloc_clear(&ctx->superalloc);
|
||||
traverse_postorder(&ctx->queries, delq, NULL);
|
||||
free(ctx);
|
||||
#ifdef USE_WINSOCK
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_set_option(struct ub_ctx* ctx, char* opt, char* val)
|
||||
{
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
if(!config_set_option(ctx->env->cfg, opt, val)) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_SYNTAX;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_config(struct ub_ctx* ctx, char* fname)
|
||||
{
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
if(!config_read(ctx->env->cfg, fname, NULL)) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_SYNTAX;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_add_ta(struct ub_ctx* ctx, char* ta)
|
||||
{
|
||||
char* dup = strdup(ta);
|
||||
if(!dup) return UB_NOMEM;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_list, dup)) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
free(dup);
|
||||
return UB_NOMEM;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_add_ta_file(struct ub_ctx* ctx, char* fname)
|
||||
{
|
||||
char* dup = strdup(fname);
|
||||
if(!dup) return UB_NOMEM;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_file_list, dup)) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
free(dup);
|
||||
return UB_NOMEM;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_trustedkeys(struct ub_ctx* ctx, char* fname)
|
||||
{
|
||||
char* dup = strdup(fname);
|
||||
if(!dup) return UB_NOMEM;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
if(!cfg_strlist_insert(&ctx->env->cfg->trusted_keys_file_list, dup)) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
free(dup);
|
||||
return UB_NOMEM;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_debuglevel(struct ub_ctx* ctx, int d)
|
||||
{
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
verbosity = d;
|
||||
ctx->env->cfg->verbosity = d;
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int ub_ctx_debugout(struct ub_ctx* ctx, void* out)
|
||||
{
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
log_file((FILE*)out);
|
||||
ctx->logfile_override = 1;
|
||||
ctx->log_out = out;
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_async(struct ub_ctx* ctx, int dothread)
|
||||
{
|
||||
#ifdef THREADS_DISABLED
|
||||
if(dothread) /* cannot do threading */
|
||||
return UB_NOERROR;
|
||||
#endif
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
ctx->dothread = dothread;
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_poll(struct ub_ctx* ctx)
|
||||
{
|
||||
/* no need to hold lock while testing for readability. */
|
||||
return tube_poll(ctx->rr_pipe);
|
||||
}
|
||||
|
||||
int
|
||||
ub_fd(struct ub_ctx* ctx)
|
||||
{
|
||||
return tube_read_fd(ctx->rr_pipe);
|
||||
}
|
||||
|
||||
/** process answer from bg worker */
|
||||
static int
|
||||
process_answer_detail(struct ub_ctx* ctx, uint8_t* msg, uint32_t len,
|
||||
ub_callback_t* cb, void** cbarg, int* err,
|
||||
struct ub_result** res)
|
||||
{
|
||||
struct ctx_query* q;
|
||||
if(context_serial_getcmd(msg, len) != UB_LIBCMD_ANSWER) {
|
||||
log_err("error: bad data from bg worker %d",
|
||||
(int)context_serial_getcmd(msg, len));
|
||||
return 0;
|
||||
}
|
||||
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
q = context_deserialize_answer(ctx, msg, len, err);
|
||||
if(!q) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
/* probably simply the lookup that failed, i.e.
|
||||
* response returned before cancel was sent out, so noerror */
|
||||
return 1;
|
||||
}
|
||||
log_assert(q->async);
|
||||
|
||||
/* grab cb while locked */
|
||||
if(q->cancelled) {
|
||||
*cb = NULL;
|
||||
*cbarg = NULL;
|
||||
} else {
|
||||
*cb = q->cb;
|
||||
*cbarg = q->cb_arg;
|
||||
}
|
||||
if(*err) {
|
||||
*res = NULL;
|
||||
ub_resolve_free(q->res);
|
||||
} else {
|
||||
/* parse the message, extract rcode, fill result */
|
||||
ldns_buffer* buf = ldns_buffer_new(q->msg_len);
|
||||
struct regional* region = regional_create();
|
||||
*res = q->res;
|
||||
(*res)->rcode = LDNS_RCODE_SERVFAIL;
|
||||
if(region && buf) {
|
||||
ldns_buffer_clear(buf);
|
||||
ldns_buffer_write(buf, q->msg, q->msg_len);
|
||||
ldns_buffer_flip(buf);
|
||||
libworker_enter_result(*res, buf, region,
|
||||
q->msg_security);
|
||||
}
|
||||
(*res)->answer_packet = q->msg;
|
||||
(*res)->answer_len = (int)q->msg_len;
|
||||
q->msg = NULL;
|
||||
ldns_buffer_free(buf);
|
||||
regional_destroy(region);
|
||||
}
|
||||
q->res = NULL;
|
||||
/* delete the q from list */
|
||||
(void)rbtree_delete(&ctx->queries, q->node.key);
|
||||
ctx->num_async--;
|
||||
context_query_delete(q);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
|
||||
if(*cb) return 2;
|
||||
ub_resolve_free(*res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** process answer from bg worker */
|
||||
static int
|
||||
process_answer(struct ub_ctx* ctx, uint8_t* msg, uint32_t len)
|
||||
{
|
||||
int err;
|
||||
ub_callback_t cb;
|
||||
void* cbarg;
|
||||
struct ub_result* res;
|
||||
int r;
|
||||
|
||||
r = process_answer_detail(ctx, msg, len, &cb, &cbarg, &err, &res);
|
||||
|
||||
/* no locks held while calling callback, so that library is
|
||||
* re-entrant. */
|
||||
if(r == 2)
|
||||
(*cb)(cbarg, err, res);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
ub_process(struct ub_ctx* ctx)
|
||||
{
|
||||
int r;
|
||||
uint8_t* msg;
|
||||
uint32_t len;
|
||||
while(1) {
|
||||
msg = NULL;
|
||||
lock_basic_lock(&ctx->rrpipe_lock);
|
||||
r = tube_read_msg(ctx->rr_pipe, &msg, &len, 1);
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
if(r == 0)
|
||||
return UB_PIPE;
|
||||
else if(r == -1)
|
||||
break;
|
||||
if(!process_answer(ctx, msg, len)) {
|
||||
free(msg);
|
||||
return UB_PIPE;
|
||||
}
|
||||
free(msg);
|
||||
}
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_wait(struct ub_ctx* ctx)
|
||||
{
|
||||
int err;
|
||||
ub_callback_t cb;
|
||||
void* cbarg;
|
||||
struct ub_result* res;
|
||||
int r;
|
||||
uint8_t* msg;
|
||||
uint32_t len;
|
||||
/* this is basically the same loop as _process(), but with changes.
|
||||
* holds the rrpipe lock and waits with tube_wait */
|
||||
while(1) {
|
||||
lock_basic_lock(&ctx->rrpipe_lock);
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->num_async == 0) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
break;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
|
||||
/* keep rrpipe locked, while
|
||||
* o waiting for pipe readable
|
||||
* o parsing message
|
||||
* o possibly decrementing num_async
|
||||
* do callback without lock
|
||||
*/
|
||||
r = tube_wait(ctx->rr_pipe);
|
||||
if(r) {
|
||||
r = tube_read_msg(ctx->rr_pipe, &msg, &len, 1);
|
||||
if(r == 0) {
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
return UB_PIPE;
|
||||
}
|
||||
if(r == -1) {
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
continue;
|
||||
}
|
||||
r = process_answer_detail(ctx, msg, len,
|
||||
&cb, &cbarg, &err, &res);
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
free(msg);
|
||||
if(r == 0)
|
||||
return UB_PIPE;
|
||||
if(r == 2)
|
||||
(*cb)(cbarg, err, res);
|
||||
} else {
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
}
|
||||
}
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_resolve(struct ub_ctx* ctx, char* name, int rrtype,
|
||||
int rrclass, struct ub_result** result)
|
||||
{
|
||||
struct ctx_query* q;
|
||||
int r;
|
||||
*result = NULL;
|
||||
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(!ctx->finalized) {
|
||||
r = context_finalize(ctx);
|
||||
if(r) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
/* create new ctx_query and attempt to add to the list */
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
q = context_new(ctx, name, rrtype, rrclass, NULL, NULL);
|
||||
if(!q)
|
||||
return UB_NOMEM;
|
||||
/* become a resolver thread for a bit */
|
||||
|
||||
r = libworker_fg(ctx, q);
|
||||
if(r) {
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
(void)rbtree_delete(&ctx->queries, q->node.key);
|
||||
context_query_delete(q);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return r;
|
||||
}
|
||||
q->res->answer_packet = q->msg;
|
||||
q->res->answer_len = (int)q->msg_len;
|
||||
q->msg = NULL;
|
||||
*result = q->res;
|
||||
q->res = NULL;
|
||||
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
(void)rbtree_delete(&ctx->queries, q->node.key);
|
||||
context_query_delete(q);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype,
|
||||
int rrclass, void* mydata, ub_callback_t callback, int* async_id)
|
||||
{
|
||||
struct ctx_query* q;
|
||||
uint8_t* msg = NULL;
|
||||
uint32_t len = 0;
|
||||
|
||||
if(async_id)
|
||||
*async_id = 0;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(!ctx->finalized) {
|
||||
int r = context_finalize(ctx);
|
||||
if(r) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if(!ctx->created_bg) {
|
||||
int r;
|
||||
ctx->created_bg = 1;
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
r = libworker_bg(ctx);
|
||||
if(r) {
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
ctx->created_bg = 0;
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
|
||||
/* create new ctx_query and attempt to add to the list */
|
||||
q = context_new(ctx, name, rrtype, rrclass, callback, mydata);
|
||||
if(!q)
|
||||
return UB_NOMEM;
|
||||
|
||||
/* write over pipe to background worker */
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
msg = context_serialize_new_query(q, &len);
|
||||
if(!msg) {
|
||||
(void)rbtree_delete(&ctx->queries, q->node.key);
|
||||
ctx->num_async--;
|
||||
context_query_delete(q);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOMEM;
|
||||
}
|
||||
if(async_id)
|
||||
*async_id = q->querynum;
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
|
||||
lock_basic_lock(&ctx->qqpipe_lock);
|
||||
if(!tube_write_msg(ctx->qq_pipe, msg, len, 0)) {
|
||||
lock_basic_unlock(&ctx->qqpipe_lock);
|
||||
free(msg);
|
||||
return UB_PIPE;
|
||||
}
|
||||
lock_basic_unlock(&ctx->qqpipe_lock);
|
||||
free(msg);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_cancel(struct ub_ctx* ctx, int async_id)
|
||||
{
|
||||
struct ctx_query* q;
|
||||
uint8_t* msg = NULL;
|
||||
uint32_t len = 0;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
q = (struct ctx_query*)rbtree_search(&ctx->queries, &async_id);
|
||||
if(!q || !q->async) {
|
||||
/* it is not there, so nothing to do */
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
log_assert(q->async);
|
||||
q->cancelled = 1;
|
||||
|
||||
/* delete it */
|
||||
if(!ctx->dothread) { /* if forked */
|
||||
(void)rbtree_delete(&ctx->queries, q->node.key);
|
||||
ctx->num_async--;
|
||||
msg = context_serialize_cancel(q, &len);
|
||||
context_query_delete(q);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
if(!msg) {
|
||||
return UB_NOMEM;
|
||||
}
|
||||
/* send cancel to background worker */
|
||||
lock_basic_lock(&ctx->qqpipe_lock);
|
||||
if(!tube_write_msg(ctx->qq_pipe, msg, len, 0)) {
|
||||
lock_basic_unlock(&ctx->qqpipe_lock);
|
||||
free(msg);
|
||||
return UB_PIPE;
|
||||
}
|
||||
lock_basic_unlock(&ctx->qqpipe_lock);
|
||||
free(msg);
|
||||
} else {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
void
|
||||
ub_resolve_free(struct ub_result* result)
|
||||
{
|
||||
char** p;
|
||||
if(!result) return;
|
||||
free(result->qname);
|
||||
if(result->canonname != result->qname)
|
||||
free(result->canonname);
|
||||
if(result->data)
|
||||
for(p = result->data; *p; p++)
|
||||
free(*p);
|
||||
free(result->data);
|
||||
free(result->len);
|
||||
free(result->answer_packet);
|
||||
free(result);
|
||||
}
|
||||
|
||||
const char*
|
||||
ub_strerror(int err)
|
||||
{
|
||||
switch(err) {
|
||||
case UB_NOERROR: return "no error";
|
||||
case UB_SOCKET: return "socket io error";
|
||||
case UB_NOMEM: return "out of memory";
|
||||
case UB_SYNTAX: return "syntax error";
|
||||
case UB_SERVFAIL: return "server failure";
|
||||
case UB_FORKFAIL: return "could not fork";
|
||||
case UB_INITFAIL: return "initialization failure";
|
||||
case UB_AFTERFINAL: return "setting change after finalize";
|
||||
case UB_PIPE: return "error in pipe communication with async";
|
||||
case UB_READFILE: return "error reading file";
|
||||
default: return "unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_set_fwd(struct ub_ctx* ctx, char* addr)
|
||||
{
|
||||
struct sockaddr_storage storage;
|
||||
socklen_t stlen;
|
||||
struct config_stub* s;
|
||||
char* dupl;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
errno=EINVAL;
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
if(!addr) {
|
||||
/* disable fwd mode - the root stub should be first. */
|
||||
if(ctx->env->cfg->forwards &&
|
||||
strcmp(ctx->env->cfg->forwards->name, ".") == 0) {
|
||||
s = ctx->env->cfg->forwards;
|
||||
ctx->env->cfg->forwards = s->next;
|
||||
s->next = NULL;
|
||||
config_delstubs(s);
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
|
||||
/* check syntax for addr */
|
||||
if(!extstrtoaddr(addr, &storage, &stlen)) {
|
||||
errno=EINVAL;
|
||||
return UB_SYNTAX;
|
||||
}
|
||||
|
||||
/* it parses, add root stub in front of list */
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(!ctx->env->cfg->forwards ||
|
||||
strcmp(ctx->env->cfg->forwards->name, ".") != 0) {
|
||||
s = calloc(1, sizeof(*s));
|
||||
if(!s) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
errno=ENOMEM;
|
||||
return UB_NOMEM;
|
||||
}
|
||||
s->name = strdup(".");
|
||||
if(!s->name) {
|
||||
free(s);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
errno=ENOMEM;
|
||||
return UB_NOMEM;
|
||||
}
|
||||
s->next = ctx->env->cfg->forwards;
|
||||
ctx->env->cfg->forwards = s;
|
||||
} else {
|
||||
log_assert(ctx->env->cfg->forwards);
|
||||
s = ctx->env->cfg->forwards;
|
||||
}
|
||||
dupl = strdup(addr);
|
||||
if(!dupl) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
errno=ENOMEM;
|
||||
return UB_NOMEM;
|
||||
}
|
||||
if(!cfg_strlist_insert(&s->addrs, dupl)) {
|
||||
free(dupl);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
errno=ENOMEM;
|
||||
return UB_NOMEM;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_resolvconf(struct ub_ctx* ctx, char* fname)
|
||||
{
|
||||
FILE* in;
|
||||
int numserv = 0;
|
||||
char buf[1024];
|
||||
char* parse, *addr;
|
||||
int r;
|
||||
if(fname == NULL)
|
||||
fname = "/etc/resolv.conf";
|
||||
in = fopen(fname, "r");
|
||||
if(!in) {
|
||||
/* error in errno! perror(fname) */
|
||||
return UB_READFILE;
|
||||
}
|
||||
while(fgets(buf, (int)sizeof(buf), in)) {
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
parse=buf;
|
||||
while(*parse == ' ' || *parse == '\t')
|
||||
parse++;
|
||||
if(strncmp(parse, "nameserver", 10) == 0) {
|
||||
numserv++;
|
||||
parse += 10; /* skip 'nameserver' */
|
||||
/* skip whitespace */
|
||||
while(*parse == ' ' || *parse == '\t')
|
||||
parse++;
|
||||
addr = parse;
|
||||
/* skip [0-9a-fA-F.:]*, i.e. IP4 and IP6 address */
|
||||
while(isxdigit(*parse) || *parse=='.' || *parse==':')
|
||||
parse++;
|
||||
/* terminate after the address, remove newline */
|
||||
*parse = 0;
|
||||
|
||||
if((r = ub_ctx_set_fwd(ctx, addr)) != UB_NOERROR) {
|
||||
fclose(in);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(in);
|
||||
if(numserv == 0) {
|
||||
/* from resolv.conf(5) if none given, use localhost */
|
||||
return ub_ctx_set_fwd(ctx, "127.0.0.1");
|
||||
}
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
int
|
||||
ub_ctx_hosts(struct ub_ctx* ctx, char* fname)
|
||||
{
|
||||
FILE* in;
|
||||
char buf[1024], ldata[1024];
|
||||
char* parse, *addr, *name, *ins;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->finalized) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
errno=EINVAL;
|
||||
return UB_AFTERFINAL;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
if(fname == NULL)
|
||||
fname = "/etc/hosts";
|
||||
in = fopen(fname, "r");
|
||||
if(!in) {
|
||||
/* error in errno! perror(fname) */
|
||||
return UB_READFILE;
|
||||
}
|
||||
while(fgets(buf, (int)sizeof(buf), in)) {
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
parse=buf;
|
||||
while(*parse == ' ' || *parse == '\t')
|
||||
parse++;
|
||||
if(*parse == '#')
|
||||
continue; /* skip comment */
|
||||
/* format: <addr> spaces <name> spaces <name> ... */
|
||||
addr = parse;
|
||||
/* skip addr */
|
||||
while(isxdigit(*parse) || *parse == '.' || *parse == ':')
|
||||
parse++;
|
||||
if(*parse == '\n' || *parse == 0)
|
||||
continue;
|
||||
if(*parse == '%')
|
||||
continue; /* ignore macOSX fe80::1%lo0 localhost */
|
||||
if(*parse != ' ' && *parse != '\t') {
|
||||
/* must have whitespace after address */
|
||||
fclose(in);
|
||||
errno=EINVAL;
|
||||
return UB_SYNTAX;
|
||||
}
|
||||
*parse++ = 0; /* end delimiter for addr ... */
|
||||
/* go to names and add them */
|
||||
while(*parse) {
|
||||
while(*parse == ' ' || *parse == '\t' || *parse=='\n')
|
||||
parse++;
|
||||
if(*parse == 0 || *parse == '#')
|
||||
break;
|
||||
/* skip name, allows (too) many printable characters */
|
||||
name = parse;
|
||||
while('!' <= *parse && *parse <= '~')
|
||||
parse++;
|
||||
if(*parse)
|
||||
*parse++ = 0; /* end delimiter for name */
|
||||
snprintf(ldata, sizeof(ldata), "%s %s %s",
|
||||
name, str_is_ip6(addr)?"AAAA":"A", addr);
|
||||
ins = strdup(ldata);
|
||||
if(!ins) {
|
||||
/* out of memory */
|
||||
fclose(in);
|
||||
errno=ENOMEM;
|
||||
return UB_NOMEM;
|
||||
}
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(!cfg_strlist_insert(&ctx->env->cfg->local_data,
|
||||
ins)) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
fclose(in);
|
||||
free(ins);
|
||||
errno=ENOMEM;
|
||||
return UB_NOMEM;
|
||||
}
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
}
|
||||
fclose(in);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
@@ -0,0 +1,842 @@
|
||||
/*
|
||||
* libunbound/worker.c - worker thread or process that resolves
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains the worker process or thread that performs
|
||||
* the DNS resolving and validation. The worker is called by a procedure
|
||||
* and if in the background continues until exit, if in the foreground
|
||||
* returns from the procedure when done.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "libunbound/libworker.h"
|
||||
#include "libunbound/context.h"
|
||||
#include "libunbound/unbound.h"
|
||||
#include "services/outside_network.h"
|
||||
#include "services/mesh.h"
|
||||
#include "services/localzone.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "services/outbound_list.h"
|
||||
#include "util/module.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/random.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/netevent.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/tube.h"
|
||||
|
||||
/** handle new query command for bg worker */
|
||||
static void handle_newq(struct libworker* w, uint8_t* buf, uint32_t len);
|
||||
|
||||
/** delete libworker struct */
|
||||
static void
|
||||
libworker_delete(struct libworker* w)
|
||||
{
|
||||
if(!w) return;
|
||||
if(w->env) {
|
||||
mesh_delete(w->env->mesh);
|
||||
context_release_alloc(w->ctx, w->env->alloc,
|
||||
!w->is_bg || w->is_bg_thread);
|
||||
ldns_buffer_free(w->env->scratch_buffer);
|
||||
regional_destroy(w->env->scratch);
|
||||
ub_randfree(w->env->rnd);
|
||||
free(w->env);
|
||||
}
|
||||
outside_network_delete(w->back);
|
||||
comm_base_delete(w->base);
|
||||
free(w);
|
||||
}
|
||||
|
||||
/** setup fresh libworker struct */
|
||||
static struct libworker*
|
||||
libworker_setup(struct ub_ctx* ctx, int is_bg)
|
||||
{
|
||||
unsigned int seed;
|
||||
struct libworker* w = (struct libworker*)calloc(1, sizeof(*w));
|
||||
struct config_file* cfg = ctx->env->cfg;
|
||||
int* ports;
|
||||
int numports;
|
||||
if(!w) return NULL;
|
||||
w->is_bg = is_bg;
|
||||
w->ctx = ctx;
|
||||
w->env = (struct module_env*)malloc(sizeof(*w->env));
|
||||
if(!w->env) {
|
||||
free(w);
|
||||
return NULL;
|
||||
}
|
||||
*w->env = *ctx->env;
|
||||
w->env->alloc = context_obtain_alloc(ctx, !w->is_bg || w->is_bg_thread);
|
||||
if(!w->env->alloc) {
|
||||
libworker_delete(w);
|
||||
return NULL;
|
||||
}
|
||||
w->thread_num = w->env->alloc->thread_num;
|
||||
alloc_set_id_cleanup(w->env->alloc, &libworker_alloc_cleanup, w);
|
||||
if(!w->is_bg || w->is_bg_thread) {
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
}
|
||||
w->env->scratch = regional_create_custom(cfg->msg_buffer_size);
|
||||
w->env->scratch_buffer = ldns_buffer_new(cfg->msg_buffer_size);
|
||||
if(!w->is_bg || w->is_bg_thread) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
if(!w->env->scratch || !w->env->scratch_buffer) {
|
||||
libworker_delete(w);
|
||||
return NULL;
|
||||
}
|
||||
w->env->worker = (struct worker*)w;
|
||||
seed = (unsigned int)time(NULL) ^ (unsigned int)getpid() ^
|
||||
(((unsigned int)w->thread_num)<<17);
|
||||
seed ^= (unsigned int)w->env->alloc->next_id;
|
||||
if(!w->is_bg || w->is_bg_thread) {
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
}
|
||||
if(!(w->env->rnd = ub_initstate(seed, ctx->seed_rnd))) {
|
||||
if(!w->is_bg || w->is_bg_thread) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
seed = 0;
|
||||
libworker_delete(w);
|
||||
return NULL;
|
||||
}
|
||||
if(!w->is_bg || w->is_bg_thread) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
seed = 0;
|
||||
|
||||
w->base = comm_base_create(0);
|
||||
if(!w->base) {
|
||||
libworker_delete(w);
|
||||
return NULL;
|
||||
}
|
||||
if(!w->is_bg || w->is_bg_thread) {
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
}
|
||||
numports = cfg_condense_ports(cfg, &ports);
|
||||
if(numports == 0) {
|
||||
libworker_delete(w);
|
||||
return NULL;
|
||||
}
|
||||
w->back = outside_network_create(w->base, cfg->msg_buffer_size,
|
||||
(size_t)cfg->outgoing_num_ports, cfg->out_ifs,
|
||||
cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6,
|
||||
cfg->do_tcp?cfg->outgoing_num_tcp:0,
|
||||
w->env->infra_cache, w->env->rnd, cfg->use_caps_bits_for_id,
|
||||
ports, numports, cfg->unwanted_threshold,
|
||||
&libworker_alloc_cleanup, w);
|
||||
if(!w->is_bg || w->is_bg_thread) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
free(ports);
|
||||
if(!w->back) {
|
||||
libworker_delete(w);
|
||||
return NULL;
|
||||
}
|
||||
w->env->mesh = mesh_create(&ctx->mods, w->env);
|
||||
if(!w->env->mesh) {
|
||||
libworker_delete(w);
|
||||
return NULL;
|
||||
}
|
||||
w->env->send_packet = &libworker_send_packet;
|
||||
w->env->send_query = &libworker_send_query;
|
||||
w->env->detach_subs = &mesh_detach_subs;
|
||||
w->env->attach_sub = &mesh_attach_sub;
|
||||
w->env->kill_sub = &mesh_state_delete;
|
||||
w->env->detect_cycle = &mesh_detect_cycle;
|
||||
comm_base_timept(w->base, &w->env->now, &w->env->now_tv);
|
||||
return w;
|
||||
}
|
||||
|
||||
/** handle cancel command for bg worker */
|
||||
static void
|
||||
handle_cancel(struct libworker* w, uint8_t* buf, uint32_t len)
|
||||
{
|
||||
struct ctx_query* q;
|
||||
if(w->is_bg_thread) {
|
||||
lock_basic_lock(&w->ctx->cfglock);
|
||||
q = context_deserialize_cancel(w->ctx, buf, len);
|
||||
lock_basic_unlock(&w->ctx->cfglock);
|
||||
} else {
|
||||
q = context_deserialize_cancel(w->ctx, buf, len);
|
||||
}
|
||||
if(!q) {
|
||||
/* probably simply lookup failed, i.e. the message had been
|
||||
* processed and answered before the cancel arrived */
|
||||
return;
|
||||
}
|
||||
q->cancelled = 1;
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/** do control command coming into bg server */
|
||||
static void
|
||||
libworker_do_cmd(struct libworker* w, uint8_t* msg, uint32_t len)
|
||||
{
|
||||
switch(context_serial_getcmd(msg, len)) {
|
||||
default:
|
||||
case UB_LIBCMD_ANSWER:
|
||||
log_err("unknown command for bg worker %d",
|
||||
(int)context_serial_getcmd(msg, len));
|
||||
/* and fall through to quit */
|
||||
case UB_LIBCMD_QUIT:
|
||||
free(msg);
|
||||
comm_base_exit(w->base);
|
||||
break;
|
||||
case UB_LIBCMD_NEWQUERY:
|
||||
handle_newq(w, msg, len);
|
||||
break;
|
||||
case UB_LIBCMD_CANCEL:
|
||||
handle_cancel(w, msg, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** handle control command coming into server */
|
||||
void
|
||||
libworker_handle_control_cmd(struct tube* ATTR_UNUSED(tube),
|
||||
uint8_t* msg, size_t len, int err, void* arg)
|
||||
{
|
||||
struct libworker* w = (struct libworker*)arg;
|
||||
|
||||
if(err != 0) {
|
||||
free(msg);
|
||||
/* it is of no use to go on, exit */
|
||||
comm_base_exit(w->base);
|
||||
return;
|
||||
}
|
||||
libworker_do_cmd(w, msg, len); /* also frees the buf */
|
||||
}
|
||||
|
||||
/** the background thread func */
|
||||
static void*
|
||||
libworker_dobg(void* arg)
|
||||
{
|
||||
/* setup */
|
||||
uint32_t m;
|
||||
struct libworker* w = (struct libworker*)arg;
|
||||
struct ub_ctx* ctx = w->ctx;
|
||||
log_thread_set(&w->thread_num);
|
||||
#ifdef THREADS_DISABLED
|
||||
/* we are forked */
|
||||
w->is_bg_thread = 0;
|
||||
/* close non-used parts of the pipes */
|
||||
tube_close_write(ctx->qq_pipe);
|
||||
tube_close_read(ctx->rr_pipe);
|
||||
#endif
|
||||
if(!w) {
|
||||
log_err("libunbound bg worker init failed, nomem");
|
||||
return NULL;
|
||||
}
|
||||
if(!tube_setup_bg_listen(ctx->qq_pipe, w->base,
|
||||
libworker_handle_control_cmd, w)) {
|
||||
log_err("libunbound bg worker init failed, no bglisten");
|
||||
return NULL;
|
||||
}
|
||||
if(!tube_setup_bg_write(ctx->rr_pipe, w->base)) {
|
||||
log_err("libunbound bg worker init failed, no bgwrite");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* do the work */
|
||||
comm_base_dispatch(w->base);
|
||||
|
||||
/* cleanup */
|
||||
m = UB_LIBCMD_QUIT;
|
||||
tube_remove_bg_listen(w->ctx->qq_pipe);
|
||||
tube_remove_bg_write(w->ctx->rr_pipe);
|
||||
libworker_delete(w);
|
||||
(void)tube_write_msg(ctx->rr_pipe, (uint8_t*)&m,
|
||||
(uint32_t)sizeof(m), 0);
|
||||
#ifdef THREADS_DISABLED
|
||||
/* close pipes from forked process before exit */
|
||||
tube_close_read(ctx->qq_pipe);
|
||||
tube_close_write(ctx->rr_pipe);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int libworker_bg(struct ub_ctx* ctx)
|
||||
{
|
||||
struct libworker* w;
|
||||
/* fork or threadcreate */
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
if(ctx->dothread) {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
w = libworker_setup(ctx, 1);
|
||||
w->is_bg_thread = 1;
|
||||
#ifdef ENABLE_LOCK_CHECKS
|
||||
w->thread_num = 1; /* for nicer DEBUG checklocks */
|
||||
#endif
|
||||
if(!w) return UB_NOMEM;
|
||||
ub_thread_create(&ctx->bg_tid, libworker_dobg, w);
|
||||
} else {
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
#ifndef HAVE_FORK
|
||||
/* no fork on windows */
|
||||
return UB_FORKFAIL;
|
||||
#else /* HAVE_FORK */
|
||||
switch((ctx->bg_pid=fork())) {
|
||||
case 0:
|
||||
w = libworker_setup(ctx, 1);
|
||||
if(!w) fatal_exit("out of memory");
|
||||
/* close non-used parts of the pipes */
|
||||
tube_close_write(ctx->qq_pipe);
|
||||
tube_close_read(ctx->rr_pipe);
|
||||
(void)libworker_dobg(w);
|
||||
exit(0);
|
||||
break;
|
||||
case -1:
|
||||
return UB_FORKFAIL;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif /* HAVE_FORK */
|
||||
}
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
/** get msg reply struct (in temp region) */
|
||||
static struct reply_info*
|
||||
parse_reply(ldns_buffer* pkt, struct regional* region, struct query_info* qi)
|
||||
{
|
||||
struct reply_info* rep;
|
||||
struct msg_parse* msg;
|
||||
if(!(msg = regional_alloc(region, sizeof(*msg)))) {
|
||||
return NULL;
|
||||
}
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
ldns_buffer_set_position(pkt, 0);
|
||||
if(parse_packet(pkt, msg, region) != 0)
|
||||
return 0;
|
||||
if(!parse_create_msg(pkt, msg, NULL, qi, &rep, region)) {
|
||||
return 0;
|
||||
}
|
||||
return rep;
|
||||
}
|
||||
|
||||
/** insert canonname */
|
||||
static int
|
||||
fill_canon(struct ub_result* res, uint8_t* s)
|
||||
{
|
||||
char buf[255+2];
|
||||
dname_str(s, buf);
|
||||
res->canonname = strdup(buf);
|
||||
return res->canonname != 0;
|
||||
}
|
||||
|
||||
/** fill data into result */
|
||||
static int
|
||||
fill_res(struct ub_result* res, struct ub_packed_rrset_key* answer,
|
||||
uint8_t* finalcname, struct query_info* rq)
|
||||
{
|
||||
size_t i;
|
||||
struct packed_rrset_data* data;
|
||||
if(!answer) {
|
||||
if(finalcname) {
|
||||
if(!fill_canon(res, finalcname))
|
||||
return 0; /* out of memory */
|
||||
}
|
||||
res->data = (char**)calloc(1, sizeof(char*));
|
||||
res->len = (int*)calloc(1, sizeof(int));
|
||||
return (res->data && res->len);
|
||||
}
|
||||
data = (struct packed_rrset_data*)answer->entry.data;
|
||||
if(query_dname_compare(rq->qname, answer->rk.dname) != 0) {
|
||||
if(!fill_canon(res, answer->rk.dname))
|
||||
return 0; /* out of memory */
|
||||
} else res->canonname = NULL;
|
||||
res->data = (char**)calloc(data->count+1, sizeof(char*));
|
||||
res->len = (int*)calloc(data->count+1, sizeof(int));
|
||||
if(!res->data || !res->len)
|
||||
return 0; /* out of memory */
|
||||
for(i=0; i<data->count; i++) {
|
||||
/* remove rdlength from rdata */
|
||||
res->len[i] = (int)(data->rr_len[i] - 2);
|
||||
res->data[i] = memdup(data->rr_data[i]+2, (size_t)res->len[i]);
|
||||
if(!res->data[i])
|
||||
return 0; /* out of memory */
|
||||
}
|
||||
res->data[data->count] = NULL;
|
||||
res->len[data->count] = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** fill result from parsed message, on error fills servfail */
|
||||
void
|
||||
libworker_enter_result(struct ub_result* res, ldns_buffer* buf,
|
||||
struct regional* temp, enum sec_status msg_security)
|
||||
{
|
||||
struct query_info rq;
|
||||
struct reply_info* rep;
|
||||
res->rcode = LDNS_RCODE_SERVFAIL;
|
||||
rep = parse_reply(buf, temp, &rq);
|
||||
if(!rep) {
|
||||
log_err("cannot parse buf");
|
||||
return; /* error parsing buf, or out of memory */
|
||||
}
|
||||
if(!fill_res(res, reply_find_answer_rrset(&rq, rep),
|
||||
reply_find_final_cname_target(&rq, rep), &rq))
|
||||
return; /* out of memory */
|
||||
/* rcode, havedata, nxdomain, secure, bogus */
|
||||
res->rcode = (int)FLAGS_GET_RCODE(rep->flags);
|
||||
if(res->data && res->data[0])
|
||||
res->havedata = 1;
|
||||
if(res->rcode == LDNS_RCODE_NXDOMAIN)
|
||||
res->nxdomain = 1;
|
||||
if(msg_security == sec_status_secure)
|
||||
res->secure = 1;
|
||||
if(msg_security == sec_status_bogus)
|
||||
res->bogus = 1;
|
||||
}
|
||||
|
||||
/** fillup fg results */
|
||||
static void
|
||||
libworker_fillup_fg(struct ctx_query* q, int rcode, ldns_buffer* buf,
|
||||
enum sec_status s)
|
||||
{
|
||||
if(rcode != 0) {
|
||||
q->res->rcode = rcode;
|
||||
q->msg_security = s;
|
||||
return;
|
||||
}
|
||||
|
||||
q->res->rcode = LDNS_RCODE_SERVFAIL;
|
||||
q->msg_security = 0;
|
||||
q->msg = memdup(ldns_buffer_begin(buf), ldns_buffer_limit(buf));
|
||||
q->msg_len = ldns_buffer_limit(buf);
|
||||
if(!q->msg) {
|
||||
return; /* the error is in the rcode */
|
||||
}
|
||||
|
||||
/* canonname and results */
|
||||
q->msg_security = s;
|
||||
libworker_enter_result(q->res, buf, q->w->env->scratch, s);
|
||||
}
|
||||
|
||||
/** callback with fg results */
|
||||
static void
|
||||
libworker_fg_done_cb(void* arg, int rcode, ldns_buffer* buf, enum sec_status s)
|
||||
{
|
||||
struct ctx_query* q = (struct ctx_query*)arg;
|
||||
/* fg query is done; exit comm base */
|
||||
comm_base_exit(q->w->base);
|
||||
|
||||
libworker_fillup_fg(q, rcode, buf, s);
|
||||
}
|
||||
|
||||
/** setup qinfo and edns */
|
||||
static int
|
||||
setup_qinfo_edns(struct libworker* w, struct ctx_query* q,
|
||||
struct query_info* qinfo, struct edns_data* edns)
|
||||
{
|
||||
ldns_rdf* rdf;
|
||||
qinfo->qtype = (uint16_t)q->res->qtype;
|
||||
qinfo->qclass = (uint16_t)q->res->qclass;
|
||||
rdf = ldns_dname_new_frm_str(q->res->qname);
|
||||
if(!rdf) {
|
||||
return 0;
|
||||
}
|
||||
qinfo->qname = ldns_rdf_data(rdf);
|
||||
qinfo->qname_len = ldns_rdf_size(rdf);
|
||||
edns->edns_present = 1;
|
||||
edns->ext_rcode = 0;
|
||||
edns->edns_version = 0;
|
||||
edns->bits = EDNS_DO;
|
||||
if(ldns_buffer_capacity(w->back->udp_buff) < 65535)
|
||||
edns->udp_size = (uint16_t)ldns_buffer_capacity(
|
||||
w->back->udp_buff);
|
||||
else edns->udp_size = 65535;
|
||||
ldns_rdf_free(rdf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int libworker_fg(struct ub_ctx* ctx, struct ctx_query* q)
|
||||
{
|
||||
struct libworker* w = libworker_setup(ctx, 0);
|
||||
uint16_t qflags, qid;
|
||||
struct query_info qinfo;
|
||||
struct edns_data edns;
|
||||
if(!w)
|
||||
return UB_INITFAIL;
|
||||
if(!setup_qinfo_edns(w, q, &qinfo, &edns)) {
|
||||
libworker_delete(w);
|
||||
return UB_SYNTAX;
|
||||
}
|
||||
qid = 0;
|
||||
qflags = BIT_RD;
|
||||
q->w = w;
|
||||
/* see if there is a fixed answer */
|
||||
ldns_buffer_write_u16_at(w->back->udp_buff, 0, qid);
|
||||
ldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags);
|
||||
if(local_zones_answer(ctx->local_zones, &qinfo, &edns,
|
||||
w->back->udp_buff, w->env->scratch)) {
|
||||
libworker_fillup_fg(q, LDNS_RCODE_NOERROR,
|
||||
w->back->udp_buff, sec_status_insecure);
|
||||
libworker_delete(w);
|
||||
free(qinfo.qname);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
/* process new query */
|
||||
if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns,
|
||||
w->back->udp_buff, qid, libworker_fg_done_cb, q)) {
|
||||
free(qinfo.qname);
|
||||
return UB_NOMEM;
|
||||
}
|
||||
free(qinfo.qname);
|
||||
|
||||
/* wait for reply */
|
||||
comm_base_dispatch(w->base);
|
||||
|
||||
libworker_delete(w);
|
||||
return UB_NOERROR;
|
||||
}
|
||||
|
||||
/** add result to the bg worker result queue */
|
||||
static void
|
||||
add_bg_result(struct libworker* w, struct ctx_query* q, ldns_buffer* pkt,
|
||||
int err)
|
||||
{
|
||||
uint8_t* msg = NULL;
|
||||
uint32_t len = 0;
|
||||
|
||||
/* serialize and delete unneeded q */
|
||||
if(w->is_bg_thread) {
|
||||
lock_basic_lock(&w->ctx->cfglock);
|
||||
if(pkt) {
|
||||
q->msg_len = ldns_buffer_remaining(pkt);
|
||||
q->msg = memdup(ldns_buffer_begin(pkt), q->msg_len);
|
||||
if(!q->msg)
|
||||
msg = context_serialize_answer(q, UB_NOMEM,
|
||||
NULL, &len);
|
||||
else msg = context_serialize_answer(q, err,
|
||||
NULL, &len);
|
||||
} else msg = context_serialize_answer(q, err, NULL, &len);
|
||||
lock_basic_unlock(&w->ctx->cfglock);
|
||||
} else {
|
||||
msg = context_serialize_answer(q, err, pkt, &len);
|
||||
(void)rbtree_delete(&w->ctx->queries, q->node.key);
|
||||
w->ctx->num_async--;
|
||||
context_query_delete(q);
|
||||
}
|
||||
|
||||
if(!msg) {
|
||||
log_err("out of memory for async answer");
|
||||
return;
|
||||
}
|
||||
if(!tube_queue_item(w->ctx->rr_pipe, msg, len)) {
|
||||
log_err("out of memory for async answer");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/** callback with bg results */
|
||||
static void
|
||||
libworker_bg_done_cb(void* arg, int rcode, ldns_buffer* buf, enum sec_status s)
|
||||
{
|
||||
struct ctx_query* q = (struct ctx_query*)arg;
|
||||
|
||||
if(q->cancelled) {
|
||||
if(q->w->is_bg_thread) {
|
||||
/* delete it now */
|
||||
struct ub_ctx* ctx = q->w->ctx;
|
||||
lock_basic_lock(&ctx->cfglock);
|
||||
(void)rbtree_delete(&ctx->queries, q->node.key);
|
||||
ctx->num_async--;
|
||||
context_query_delete(q);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
}
|
||||
/* cancelled, do not give answer */
|
||||
return;
|
||||
}
|
||||
q->msg_security = s;
|
||||
if(rcode != 0) {
|
||||
error_encode(buf, rcode, NULL, 0, BIT_RD, NULL);
|
||||
}
|
||||
add_bg_result(q->w, q, buf, UB_NOERROR);
|
||||
}
|
||||
|
||||
|
||||
/** handle new query command for bg worker */
|
||||
static void
|
||||
handle_newq(struct libworker* w, uint8_t* buf, uint32_t len)
|
||||
{
|
||||
uint16_t qflags, qid;
|
||||
struct query_info qinfo;
|
||||
struct edns_data edns;
|
||||
struct ctx_query* q;
|
||||
if(w->is_bg_thread) {
|
||||
lock_basic_lock(&w->ctx->cfglock);
|
||||
q = context_lookup_new_query(w->ctx, buf, len);
|
||||
lock_basic_unlock(&w->ctx->cfglock);
|
||||
} else {
|
||||
q = context_deserialize_new_query(w->ctx, buf, len);
|
||||
}
|
||||
free(buf);
|
||||
if(!q) {
|
||||
log_err("failed to deserialize newq");
|
||||
return;
|
||||
}
|
||||
if(!setup_qinfo_edns(w, q, &qinfo, &edns)) {
|
||||
add_bg_result(w, q, NULL, UB_SYNTAX);
|
||||
return;
|
||||
}
|
||||
qid = 0;
|
||||
qflags = BIT_RD;
|
||||
/* see if there is a fixed answer */
|
||||
ldns_buffer_write_u16_at(w->back->udp_buff, 0, qid);
|
||||
ldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags);
|
||||
if(local_zones_answer(w->ctx->local_zones, &qinfo, &edns,
|
||||
w->back->udp_buff, w->env->scratch)) {
|
||||
q->msg_security = sec_status_insecure;
|
||||
add_bg_result(w, q, w->back->udp_buff, UB_NOERROR);
|
||||
free(qinfo.qname);
|
||||
return;
|
||||
}
|
||||
q->w = w;
|
||||
/* process new query */
|
||||
if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns,
|
||||
w->back->udp_buff, qid, libworker_bg_done_cb, q)) {
|
||||
add_bg_result(w, q, NULL, UB_NOMEM);
|
||||
}
|
||||
free(qinfo.qname);
|
||||
}
|
||||
|
||||
void libworker_alloc_cleanup(void* arg)
|
||||
{
|
||||
struct libworker* w = (struct libworker*)arg;
|
||||
slabhash_clear(&w->env->rrset_cache->table);
|
||||
slabhash_clear(w->env->msg_cache);
|
||||
}
|
||||
|
||||
int libworker_send_packet(ldns_buffer* pkt, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, int timeout, struct module_qstate* q, int use_tcp)
|
||||
{
|
||||
struct libworker* w = (struct libworker*)q->env->worker;
|
||||
if(use_tcp) {
|
||||
return pending_tcp_query(w->back, pkt, addr, addrlen,
|
||||
timeout, libworker_handle_reply, q) != 0;
|
||||
}
|
||||
return pending_udp_query(w->back, pkt, addr, addrlen,
|
||||
timeout*1000, libworker_handle_reply, q) != 0;
|
||||
}
|
||||
|
||||
/** compare outbound entry qstates */
|
||||
static int
|
||||
outbound_entry_compare(void* a, void* b)
|
||||
{
|
||||
struct outbound_entry* e1 = (struct outbound_entry*)a;
|
||||
struct outbound_entry* e2 = (struct outbound_entry*)b;
|
||||
if(e1->qstate == e2->qstate)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen,
|
||||
uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
struct module_qstate* q)
|
||||
{
|
||||
struct libworker* w = (struct libworker*)q->env->worker;
|
||||
struct outbound_entry* e = (struct outbound_entry*)regional_alloc(
|
||||
q->region, sizeof(*e));
|
||||
if(!e)
|
||||
return NULL;
|
||||
e->qstate = q;
|
||||
e->qsent = outnet_serviced_query(w->back, qname,
|
||||
qnamelen, qtype, qclass, flags, dnssec, addr, addrlen,
|
||||
libworker_handle_service_reply, e, w->back->udp_buff,
|
||||
&outbound_entry_compare);
|
||||
if(!e->qsent) {
|
||||
return NULL;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
int
|
||||
libworker_handle_reply(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* reply_info)
|
||||
{
|
||||
struct module_qstate* q = (struct module_qstate*)arg;
|
||||
struct libworker* lw = (struct libworker*)q->env->worker;
|
||||
struct outbound_entry e;
|
||||
e.qstate = q;
|
||||
e.qsent = NULL;
|
||||
|
||||
if(error != 0) {
|
||||
mesh_report_reply(lw->env->mesh, &e, reply_info, error);
|
||||
return 0;
|
||||
}
|
||||
/* sanity check. */
|
||||
if(!LDNS_QR_WIRE(ldns_buffer_begin(c->buffer))
|
||||
|| LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) !=
|
||||
LDNS_PACKET_QUERY
|
||||
|| LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
|
||||
/* error becomes timeout for the module as if this reply
|
||||
* never arrived. */
|
||||
mesh_report_reply(lw->env->mesh, &e, reply_info,
|
||||
NETEVENT_TIMEOUT);
|
||||
return 0;
|
||||
}
|
||||
mesh_report_reply(lw->env->mesh, &e, reply_info, NETEVENT_NOERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
libworker_handle_service_reply(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* reply_info)
|
||||
{
|
||||
struct outbound_entry* e = (struct outbound_entry*)arg;
|
||||
struct libworker* lw = (struct libworker*)e->qstate->env->worker;
|
||||
|
||||
if(error != 0) {
|
||||
mesh_report_reply(lw->env->mesh, e, reply_info, error);
|
||||
return 0;
|
||||
}
|
||||
/* sanity check. */
|
||||
if(!LDNS_QR_WIRE(ldns_buffer_begin(c->buffer))
|
||||
|| LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) !=
|
||||
LDNS_PACKET_QUERY
|
||||
|| LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
|
||||
/* error becomes timeout for the module as if this reply
|
||||
* never arrived. */
|
||||
mesh_report_reply(lw->env->mesh, e, reply_info,
|
||||
NETEVENT_TIMEOUT);
|
||||
return 0;
|
||||
}
|
||||
mesh_report_reply(lw->env->mesh, e, reply_info, NETEVENT_NOERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --- fake callbacks for fptr_wlist to work --- */
|
||||
void worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube),
|
||||
uint8_t* ATTR_UNUSED(buffer), size_t ATTR_UNUSED(len),
|
||||
int ATTR_UNUSED(error), void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
int worker_handle_request(struct comm_point* ATTR_UNUSED(c),
|
||||
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
|
||||
struct comm_reply* ATTR_UNUSED(repinfo))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int worker_handle_reply(struct comm_point* ATTR_UNUSED(c),
|
||||
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
|
||||
struct comm_reply* ATTR_UNUSED(reply_info))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int worker_handle_service_reply(struct comm_point* ATTR_UNUSED(c),
|
||||
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
|
||||
struct comm_reply* ATTR_UNUSED(reply_info))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int remote_accept_callback(struct comm_point* ATTR_UNUSED(c),
|
||||
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
|
||||
struct comm_reply* ATTR_UNUSED(repinfo))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int remote_control_callback(struct comm_point* ATTR_UNUSED(c),
|
||||
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
|
||||
struct comm_reply* ATTR_UNUSED(repinfo))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void worker_sighandler(int ATTR_UNUSED(sig), void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
int worker_send_packet(ldns_buffer* ATTR_UNUSED(pkt),
|
||||
struct sockaddr_storage* ATTR_UNUSED(addr),
|
||||
socklen_t ATTR_UNUSED(addrlen), int ATTR_UNUSED(timeout),
|
||||
struct module_qstate* ATTR_UNUSED(q), int ATTR_UNUSED(use_tcp))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct outbound_entry* worker_send_query(uint8_t* ATTR_UNUSED(qname),
|
||||
size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
|
||||
uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags),
|
||||
int ATTR_UNUSED(dnssec), struct sockaddr_storage* ATTR_UNUSED(addr),
|
||||
socklen_t ATTR_UNUSED(addrlen), struct module_qstate* ATTR_UNUSED(q))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
worker_alloc_cleanup(void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void worker_stat_timer_cb(void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
int order_lock_cmp(const void* ATTR_UNUSED(e1), const void* ATTR_UNUSED(e2))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
codeline_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* libunbound/worker.h - worker thread or process that resolves
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains the worker process or thread that performs
|
||||
* the DNS resolving and validation. The worker is called by a procedure
|
||||
* and if in the background continues until exit, if in the foreground
|
||||
* returns from the procedure when done.
|
||||
*/
|
||||
#ifndef LIBUNBOUND_WORKER_H
|
||||
#define LIBUNBOUND_WORKER_H
|
||||
#include "util/data/packed_rrset.h"
|
||||
struct ub_ctx;
|
||||
struct ub_result;
|
||||
struct module_env;
|
||||
struct comm_base;
|
||||
struct outside_network;
|
||||
struct ub_randstate;
|
||||
struct ctx_query;
|
||||
struct outbound_entry;
|
||||
struct module_qstate;
|
||||
struct comm_point;
|
||||
struct comm_reply;
|
||||
struct regional;
|
||||
struct tube;
|
||||
|
||||
/**
|
||||
* The library-worker status structure
|
||||
* Internal to the worker.
|
||||
*/
|
||||
struct libworker {
|
||||
/** every worker has a unique thread_num. (first in struct) */
|
||||
int thread_num;
|
||||
/** context we are operating under */
|
||||
struct ub_ctx* ctx;
|
||||
|
||||
/** is this the bg worker? */
|
||||
int is_bg;
|
||||
/** is this a bg worker that is threaded (not forked)? */
|
||||
int is_bg_thread;
|
||||
|
||||
/** copy of the module environment with worker local entries. */
|
||||
struct module_env* env;
|
||||
/** the event base this worker works with */
|
||||
struct comm_base* base;
|
||||
/** the backside outside network interface to the auth servers */
|
||||
struct outside_network* back;
|
||||
/** random() table for this worker. */
|
||||
struct ub_randstate* rndstate;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a background worker
|
||||
* @param ctx: is updated with pid/tid of the background worker.
|
||||
* a new allocation cache is obtained from ctx. It contains the
|
||||
* threadnumber and unique id for further (shared) cache insertions.
|
||||
* @return 0 if OK, else error.
|
||||
* Further communication is done via the pipes in ctx.
|
||||
*/
|
||||
int libworker_bg(struct ub_ctx* ctx);
|
||||
|
||||
/**
|
||||
* Create a foreground worker.
|
||||
* This worker will join the threadpool of resolver threads.
|
||||
* It exits when the query answer has been obtained (or error).
|
||||
* This routine blocks until the worker is finished.
|
||||
* @param ctx: new allocation cache obtained and returned to it.
|
||||
* @param q: query (result is stored in here).
|
||||
* @return 0 if finished OK, else error.
|
||||
*/
|
||||
int libworker_fg(struct ub_ctx* ctx, struct ctx_query* q);
|
||||
|
||||
/** cleanup the cache to remove all rrset IDs from it, arg is libworker */
|
||||
void libworker_alloc_cleanup(void* arg);
|
||||
|
||||
/**
|
||||
* Worker service routine to send udp messages for modules.
|
||||
* @param pkt: packet to send.
|
||||
* @param addr: where to.
|
||||
* @param addrlen: length of addr.
|
||||
* @param timeout: seconds to wait until timeout.
|
||||
* @param q: wich query state to reactivate upon return.
|
||||
* @param use_tcp: true to use TCP, false for UDP.
|
||||
* @return: false on failure (memory or socket related). no query was
|
||||
* sent.
|
||||
*/
|
||||
int libworker_send_packet(ldns_buffer* pkt, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, int timeout, struct module_qstate* q, int use_tcp);
|
||||
|
||||
/**
|
||||
* Worker service routine to send serviced queries to authoritative servers.
|
||||
* @param qname: query name. (host order)
|
||||
* @param qnamelen: length in bytes of qname, including trailing 0.
|
||||
* @param qtype: query type. (host order)
|
||||
* @param qclass: query class. (host order)
|
||||
* @param flags: host order flags word, with opcode and CD bit.
|
||||
* @param dnssec: if set, EDNS record will have DO bit set.
|
||||
* @param addr: where to.
|
||||
* @param addrlen: length of addr.
|
||||
* @param q: wich query state to reactivate upon return.
|
||||
* @return: false on failure (memory or socket related). no query was
|
||||
* sent.
|
||||
*/
|
||||
struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen,
|
||||
uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
struct module_qstate* q);
|
||||
|
||||
/** process incoming replies from the network */
|
||||
int libworker_handle_reply(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* reply_info);
|
||||
|
||||
/** process incoming serviced query replies from the network */
|
||||
int libworker_handle_service_reply(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* reply_info);
|
||||
|
||||
/** handle control command coming into server */
|
||||
void libworker_handle_control_cmd(struct tube* tube, uint8_t* msg, size_t len,
|
||||
int err, void* arg);
|
||||
|
||||
/** handle opportunity to write result back */
|
||||
void libworker_handle_result_write(struct tube* tube, uint8_t* msg, size_t len,
|
||||
int err, void* arg);
|
||||
|
||||
/**
|
||||
* fill result from parsed message, on error fills servfail
|
||||
* @param res: is clear at start, filled in at end.
|
||||
* @param buf: contains DNS message.
|
||||
* @param temp: temporary buffer for parse.
|
||||
* @param msg_security: security status of the DNS message.
|
||||
* On error, the res may contain a different status
|
||||
* (out of memory is not secure, not bogus).
|
||||
*/
|
||||
void libworker_enter_result(struct ub_result* res, ldns_buffer* buf,
|
||||
struct regional* temp, enum sec_status msg_security);
|
||||
|
||||
#endif /* LIBUNBOUND_WORKER_H */
|
||||
@@ -0,0 +1,22 @@
|
||||
ub_ctx_create
|
||||
ub_ctx_delete
|
||||
ub_ctx_set_option
|
||||
ub_ctx_config
|
||||
ub_ctx_set_fwd
|
||||
ub_ctx_resolvconf
|
||||
ub_ctx_hosts
|
||||
ub_ctx_add_ta
|
||||
ub_ctx_add_ta_file
|
||||
ub_ctx_trustedkeys
|
||||
ub_ctx_debugout
|
||||
ub_ctx_debuglevel
|
||||
ub_ctx_async
|
||||
ub_poll
|
||||
ub_wait
|
||||
ub_fd
|
||||
ub_process
|
||||
ub_resolve
|
||||
ub_resolve_async
|
||||
ub_cancel
|
||||
ub_resolve_free
|
||||
ub_strerror
|
||||
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
* unbound.h - unbound validating resolver public API
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to resolve DNS queries and
|
||||
* validate the answers. Synchonously and asynchronously.
|
||||
*
|
||||
* Several ways to use this interface from an application wishing
|
||||
* to perform (validated) DNS lookups.
|
||||
*
|
||||
* All start with
|
||||
* ctx = ub_ctx_create();
|
||||
* err = ub_ctx_add_ta(ctx, "...");
|
||||
* err = ub_ctx_add_ta(ctx, "...");
|
||||
* ... some lookups
|
||||
* ... call ub_ctx_delete(ctx); when you want to stop.
|
||||
*
|
||||
* Application not threaded. Blocking.
|
||||
* int err = ub_resolve(ctx, "www.example.com", ...
|
||||
* if(err) fprintf(stderr, "lookup error: %s\n", ub_strerror(err));
|
||||
* ... use the answer
|
||||
*
|
||||
* Application not threaded. Non-blocking ('asynchronous').
|
||||
* err = ub_resolve_async(ctx, "www.example.com", ... my_callback);
|
||||
* ... application resumes processing ...
|
||||
* ... and when either ub_poll(ctx) is true
|
||||
* ... or when the file descriptor ub_fd(ctx) is readable,
|
||||
* ... or whenever, the app calls ...
|
||||
* ub_process(ctx);
|
||||
* ... if no result is ready, the app resumes processing above,
|
||||
* ... or process() calls my_callback() with results.
|
||||
*
|
||||
* ... if the application has nothing more to do, wait for answer
|
||||
* ub_wait(ctx);
|
||||
*
|
||||
* Application threaded. Blocking.
|
||||
* Blocking, same as above. The current thread does the work.
|
||||
* Multiple threads can use the *same context*, each does work and uses
|
||||
* shared cache data from the context.
|
||||
*
|
||||
* Application threaded. Non-blocking ('asynchronous').
|
||||
* ... setup threaded-asynchronous config option
|
||||
* err = ub_ctx_async(ctx, 1);
|
||||
* ... same as async for non-threaded
|
||||
* ... the callbacks are called in the thread that calls process(ctx)
|
||||
*
|
||||
* If no threading is compiled in, the above async example uses fork(2) to
|
||||
* create a process to perform the work. The forked process exits when the
|
||||
* calling process exits, or ctx_delete() is called.
|
||||
* Otherwise, for asynchronous with threading, a worker thread is created.
|
||||
*
|
||||
* The blocking calls use shared ctx-cache when threaded. Thus
|
||||
* ub_resolve() and ub_resolve_async() && ub_wait() are
|
||||
* not the same. The first makes the current thread do the work, setting
|
||||
* up buffers, etc, to perform the work (but using shared cache data).
|
||||
* The second calls another worker thread (or process) to perform the work.
|
||||
* And no buffers need to be set up, but a context-switch happens.
|
||||
*/
|
||||
#ifndef _UB_UNBOUND_H
|
||||
#define _UB_UNBOUND_H
|
||||
|
||||
/**
|
||||
* The validation context is created to hold the resolver status,
|
||||
* validation keys and a small cache (containing messages, rrsets,
|
||||
* roundtrip times, trusted keys, lameness information).
|
||||
*
|
||||
* Its contents are internally defined.
|
||||
*/
|
||||
struct ub_ctx;
|
||||
|
||||
/**
|
||||
* The validation and resolution results.
|
||||
* Allocated by the resolver, and need to be freed by the application
|
||||
* with ub_resolve_free().
|
||||
*/
|
||||
struct ub_result {
|
||||
/** The original question, name text string. */
|
||||
char* qname;
|
||||
/** the type asked for */
|
||||
int qtype;
|
||||
/** the class asked for */
|
||||
int qclass;
|
||||
|
||||
/**
|
||||
* a list of network order DNS rdata items, terminated with a
|
||||
* NULL pointer, so that data[0] is the first result entry,
|
||||
* data[1] the second, and the last entry is NULL.
|
||||
* If there was no data, data[0] is NULL.
|
||||
*/
|
||||
char** data;
|
||||
|
||||
/** the length in bytes of the data items, len[i] for data[i] */
|
||||
int* len;
|
||||
|
||||
/**
|
||||
* canonical name for the result (the final cname).
|
||||
* zero terminated string.
|
||||
* May be NULL if no canonical name exists.
|
||||
*/
|
||||
char* canonname;
|
||||
|
||||
/**
|
||||
* DNS RCODE for the result. May contain additional error code if
|
||||
* there was no data due to an error. 0 (NOERROR) if okay.
|
||||
*/
|
||||
int rcode;
|
||||
|
||||
/**
|
||||
* The DNS answer packet. Network formatted. Can contain DNSSEC types.
|
||||
*/
|
||||
void* answer_packet;
|
||||
/** length of the answer packet in octets. */
|
||||
int answer_len;
|
||||
|
||||
/**
|
||||
* If there is any data, this is true.
|
||||
* If false, there was no data (nxdomain may be true, rcode can be set).
|
||||
*/
|
||||
int havedata;
|
||||
|
||||
/**
|
||||
* If there was no data, and the domain did not exist, this is true.
|
||||
* If it is false, and there was no data, then the domain name
|
||||
* is purported to exist, but the requested data type is not available.
|
||||
*/
|
||||
int nxdomain;
|
||||
|
||||
/**
|
||||
* True, if the result is validated securely.
|
||||
* False, if validation failed or domain queried has no security info.
|
||||
*
|
||||
* It is possible to get a result with no data (havedata is false),
|
||||
* and secure is true. This means that the non-existance of the data
|
||||
* was cryptographically proven (with signatures).
|
||||
*/
|
||||
int secure;
|
||||
|
||||
/**
|
||||
* If the result was not secure (secure==0), and this result is due
|
||||
* to a security failure, bogus is true.
|
||||
* This means the data has been actively tampered with, signatures
|
||||
* failed, expected signatures were not present, timestamps on
|
||||
* signatures were out of date and so on.
|
||||
*
|
||||
* If !secure and !bogus, this can happen if the data is not secure
|
||||
* because security is disabled for that domain name.
|
||||
* This means the data is from a domain where data is not signed.
|
||||
*/
|
||||
int bogus;
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback for results of async queries.
|
||||
* The readable function definition looks like:
|
||||
* void my_callback(void* my_arg, int err, struct ub_result* result);
|
||||
* It is called with
|
||||
* void* my_arg: your pointer to a (struct of) data of your choice,
|
||||
* or NULL.
|
||||
* int err: if 0 all is OK, otherwise an error occured and no results
|
||||
* are forthcoming.
|
||||
* struct result: pointer to more detailed result structure.
|
||||
* This structure is allocated on the heap and needs to be
|
||||
* freed with ub_resolve_free(result);
|
||||
*/
|
||||
typedef void (*ub_callback_t)(void*, int, struct ub_result*);
|
||||
|
||||
/**
|
||||
* Create a resolving and validation context.
|
||||
* The information from /etc/resolv.conf and /etc/hosts is not utilised by
|
||||
* default. Use ub_ctx_resolvconf and ub_ctx_hosts to read them.
|
||||
* @return a new context. default initialisation.
|
||||
* returns NULL on error.
|
||||
*/
|
||||
struct ub_ctx* ub_ctx_create(void);
|
||||
|
||||
/**
|
||||
* Destroy a validation context and free all its resources.
|
||||
* Outstanding async queries are killed and callbacks are not called for them.
|
||||
* @param ctx: context to delete.
|
||||
*/
|
||||
void ub_ctx_delete(struct ub_ctx* ctx);
|
||||
|
||||
/**
|
||||
* Set an option for the context.
|
||||
* @param ctx: context.
|
||||
* @param opt: option name from the unbound.conf config file format.
|
||||
* (not all settings applicable). The name includes the trailing ':'
|
||||
* for example ub_ctx_set_option("logfile:", "mylog.txt");
|
||||
* This is a power-users interface that lets you specify all sorts
|
||||
* of options.
|
||||
* For some specific options, such as adding trust anchors, special
|
||||
* routines exist.
|
||||
* @param val: value of the option.
|
||||
* @return: 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_set_option(struct ub_ctx* ctx, char* opt, char* val);
|
||||
|
||||
/**
|
||||
* setup configuration for the given context.
|
||||
* @param ctx: context.
|
||||
* @param fname: unbound config file (not all settings applicable).
|
||||
* This is a power-users interface that lets you specify all sorts
|
||||
* of options.
|
||||
* For some specific options, such as adding trust anchors, special
|
||||
* routines exist.
|
||||
* @return: 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_config(struct ub_ctx* ctx, char* fname);
|
||||
|
||||
/**
|
||||
* Set machine to forward DNS queries to, the caching resolver to use.
|
||||
* IP4 or IP6 address. Forwards all DNS requests to that machine, which
|
||||
* is expected to run a recursive resolver. If the proxy is not
|
||||
* DNSSEC-capable, validation may fail. Can be called several times, in
|
||||
* that case the addresses are used as backup servers.
|
||||
*
|
||||
* To read the list of nameservers from /etc/resolv.conf (from DHCP or so),
|
||||
* use the call ub_ctx_resolvconf.
|
||||
*
|
||||
* @param ctx: context.
|
||||
* At this time it is only possible to set configuration before the
|
||||
* first resolve is done.
|
||||
* @param addr: address, IP4 or IP6 in string format.
|
||||
* If the addr is NULL, forwarding is disabled.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_set_fwd(struct ub_ctx* ctx, char* addr);
|
||||
|
||||
/**
|
||||
* Read list of nameservers to use from the filename given.
|
||||
* Usually "/etc/resolv.conf". Uses those nameservers as caching proxies.
|
||||
* If they do not support DNSSEC, validation may fail.
|
||||
*
|
||||
* Only nameservers are picked up, the searchdomain, ndots and other
|
||||
* settings from resolv.conf(5) are ignored.
|
||||
*
|
||||
* @param ctx: context.
|
||||
* At this time it is only possible to set configuration before the
|
||||
* first resolve is done.
|
||||
* @param fname: file name string. If NULL "/etc/resolv.conf" is used.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_resolvconf(struct ub_ctx* ctx, char* fname);
|
||||
|
||||
/**
|
||||
* Read list of hosts from the filename given.
|
||||
* Usually "/etc/hosts".
|
||||
* These addresses are not flagged as DNSSEC secure when queried for.
|
||||
*
|
||||
* @param ctx: context.
|
||||
* At this time it is only possible to set configuration before the
|
||||
* first resolve is done.
|
||||
* @param fname: file name string. If NULL "/etc/hosts" is used.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_hosts(struct ub_ctx* ctx, char* fname);
|
||||
|
||||
/**
|
||||
* Add a trust anchor to the given context.
|
||||
* The trust anchor is a string, on one line, that holds a valid DNSKEY or
|
||||
* DS RR.
|
||||
* @param ctx: context.
|
||||
* At this time it is only possible to add trusted keys before the
|
||||
* first resolve is done.
|
||||
* @param ta: string, with zone-format RR on one line.
|
||||
* [domainname] [TTL optional] [type] [class optional] [rdata contents]
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_add_ta(struct ub_ctx* ctx, char* ta);
|
||||
|
||||
/**
|
||||
* Add trust anchors to the given context.
|
||||
* Pass name of a file with DS and DNSKEY records (like from dig or drill).
|
||||
* @param ctx: context.
|
||||
* At this time it is only possible to add trusted keys before the
|
||||
* first resolve is done.
|
||||
* @param fname: filename of file with keyfile with trust anchors.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_add_ta_file(struct ub_ctx* ctx, char* fname);
|
||||
|
||||
/**
|
||||
* Add trust anchors to the given context.
|
||||
* Pass the name of a bind-style config file with trusted-keys{}.
|
||||
* @param ctx: context.
|
||||
* At this time it is only possible to add trusted keys before the
|
||||
* first resolve is done.
|
||||
* @param fname: filename of file with bind-style config entries with trust
|
||||
* anchors.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_trustedkeys(struct ub_ctx* ctx, char* fname);
|
||||
|
||||
/**
|
||||
* Set debug output (and error output) to the specified stream.
|
||||
* Pass NULL to disable. Default is stderr.
|
||||
* @param ctx: context.
|
||||
* @param out: FILE* out file stream to log to.
|
||||
* Type void* to avoid stdio dependency of this header file.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_debugout(struct ub_ctx* ctx, void* out);
|
||||
|
||||
/**
|
||||
* Set debug verbosity for the context
|
||||
* Output is directed to stderr.
|
||||
* @param ctx: context.
|
||||
* @param d: debug level, 0 is off, 1 is very minimal, 2 is detailed,
|
||||
* and 3 is lots.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_debuglevel(struct ub_ctx* ctx, int d);
|
||||
|
||||
/**
|
||||
* Set a context behaviour for asynchronous action.
|
||||
* @param ctx: context.
|
||||
* @param dothread: if true, enables threading and a call to resolve_async()
|
||||
* creates a thread to handle work in the background.
|
||||
* If false, a process is forked to handle work in the background.
|
||||
* Changes to this setting after async() calls have been made have
|
||||
* no effect (delete and re-create the context to change).
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_ctx_async(struct ub_ctx* ctx, int dothread);
|
||||
|
||||
/**
|
||||
* Poll a context to see if it has any new results
|
||||
* Do not poll in a loop, instead extract the fd below to poll for readiness,
|
||||
* and then check, or wait using the wait routine.
|
||||
* @param ctx: context.
|
||||
* @return: 0 if nothing to read, or nonzero if a result is available.
|
||||
* If nonzero, call ctx_process() to do callbacks.
|
||||
*/
|
||||
int ub_poll(struct ub_ctx* ctx);
|
||||
|
||||
/**
|
||||
* Wait for a context to finish with results. Calls ub_process() after
|
||||
* the wait for you. After the wait, there are no more outstanding
|
||||
* asynchronous queries.
|
||||
* @param ctx: context.
|
||||
* @return: 0 if OK, else error.
|
||||
*/
|
||||
int ub_wait(struct ub_ctx* ctx);
|
||||
|
||||
/**
|
||||
* Get file descriptor. Wait for it to become readable, at this point
|
||||
* answers are returned from the asynchronous validating resolver.
|
||||
* Then call the ub_process to continue processing.
|
||||
* This routine works immediately after context creation, the fd
|
||||
* does not change.
|
||||
* @param ctx: context.
|
||||
* @return: -1 on error, or file descriptor to use select(2) with.
|
||||
*/
|
||||
int ub_fd(struct ub_ctx* ctx);
|
||||
|
||||
/**
|
||||
* Call this routine to continue processing results from the validating
|
||||
* resolver (when the fd becomes readable).
|
||||
* Will perform necessary callbacks.
|
||||
* @param ctx: context
|
||||
* @return: 0 if OK, else error.
|
||||
*/
|
||||
int ub_process(struct ub_ctx* ctx);
|
||||
|
||||
/**
|
||||
* Perform resolution and validation of the target name.
|
||||
* @param ctx: context.
|
||||
* The context is finalized, and can no longer accept config changes.
|
||||
* @param name: domain name in text format (a zero terminated text string).
|
||||
* @param rrtype: type of RR in host order, 1 is A (address).
|
||||
* @param rrclass: class of RR in host order, 1 is IN (for internet).
|
||||
* @param result: the result data is returned in a newly allocated result
|
||||
* structure. May be NULL on return, return value is set to an error
|
||||
* in that case (out of memory).
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_resolve(struct ub_ctx* ctx, char* name, int rrtype,
|
||||
int rrclass, struct ub_result** result);
|
||||
|
||||
/**
|
||||
* Perform resolution and validation of the target name.
|
||||
* Asynchronous, after a while, the callback will be called with your
|
||||
* data and the result.
|
||||
* @param ctx: context.
|
||||
* If no thread or process has been created yet to perform the
|
||||
* work in the background, it is created now.
|
||||
* The context is finalized, and can no longer accept config changes.
|
||||
* @param name: domain name in text format (a string).
|
||||
* @param rrtype: type of RR in host order, 1 is A.
|
||||
* @param rrclass: class of RR in host order, 1 is IN (for internet).
|
||||
* @param mydata: this data is your own data (you can pass NULL),
|
||||
* and is passed on to the callback function.
|
||||
* @param callback: this is called on completion of the resolution.
|
||||
* It is called as:
|
||||
* void callback(void* mydata, int err, struct ub_result* result)
|
||||
* with mydata: the same as passed here, you may pass NULL,
|
||||
* with err: is 0 when a result has been found.
|
||||
* with result: a newly allocated result structure.
|
||||
* The result may be NULL, in that case err is set.
|
||||
*
|
||||
* If an error happens during processing, your callback will be called
|
||||
* with error set to a nonzero value (and result==NULL).
|
||||
* @param async_id: if you pass a non-NULL value, an identifier number is
|
||||
* returned for the query as it is in progress. It can be used to
|
||||
* cancel the query.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype,
|
||||
int rrclass, void* mydata, ub_callback_t callback, int* async_id);
|
||||
|
||||
/**
|
||||
* Cancel an async query in progress.
|
||||
* Its callback will not be called.
|
||||
*
|
||||
* @param ctx: context.
|
||||
* @param async_id: which query to cancel.
|
||||
* @return 0 if OK, else error.
|
||||
*/
|
||||
int ub_cancel(struct ub_ctx* ctx, int async_id);
|
||||
|
||||
/**
|
||||
* Free storage associated with a result structure.
|
||||
* @param result: to free
|
||||
*/
|
||||
void ub_resolve_free(struct ub_result* result);
|
||||
|
||||
/**
|
||||
* Convert error value to a human readable string.
|
||||
* @param err: error code from one of the ub_val* functions.
|
||||
* @return pointer to constant text string, zero terminated.
|
||||
*/
|
||||
const char* ub_strerror(int err);
|
||||
|
||||
#endif /* _UB_UNBOUND_H */
|
||||
@@ -1,8 +1,8 @@
|
||||
# ltmain.sh - Provide generalized library-building support services.
|
||||
# NOTE: Changing this file will not affect anything until you rerun configure.
|
||||
#
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005
|
||||
# Free Software Foundation, Inc.
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006,
|
||||
# 2007 Free Software Foundation, Inc.
|
||||
# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -43,8 +43,8 @@ EXIT_FAILURE=1
|
||||
|
||||
PROGRAM=ltmain.sh
|
||||
PACKAGE=libtool
|
||||
VERSION=1.5.22
|
||||
TIMESTAMP=" (1.1220.2.365 2005/12/18 22:14:06)"
|
||||
VERSION=1.5.24
|
||||
TIMESTAMP=" (1.1220.2.456 2007/06/24 02:25:32)"
|
||||
|
||||
# Be Bourne compatible (taken from Autoconf:_AS_BOURNE_COMPATIBLE).
|
||||
if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
|
||||
@@ -57,6 +57,8 @@ if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
|
||||
else
|
||||
case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
|
||||
fi
|
||||
BIN_SH=xpg4; export BIN_SH # for Tru64
|
||||
DUALCASE=1; export DUALCASE # for MKS sh
|
||||
|
||||
# Check that we have a working $echo.
|
||||
if test "X$1" = X--no-reexec; then
|
||||
@@ -114,10 +116,10 @@ esac
|
||||
for lt_var in LANG LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
|
||||
do
|
||||
eval "if test \"\${$lt_var+set}\" = set; then
|
||||
save_$lt_var=\$$lt_var
|
||||
$lt_var=C
|
||||
export $lt_var
|
||||
fi"
|
||||
save_$lt_var=\$$lt_var
|
||||
$lt_var=C
|
||||
export $lt_var
|
||||
fi"
|
||||
done
|
||||
|
||||
# Make sure IFS has a sensible default
|
||||
@@ -206,7 +208,13 @@ func_win32_libid ()
|
||||
if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | \
|
||||
$EGREP -e 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then
|
||||
win32_nmres=`eval $NM -f posix -A $1 | \
|
||||
$SED -n -e '1,100{/ I /{s,.*,import,;p;q;};}'`
|
||||
$SED -n -e '1,100{
|
||||
/ I /{
|
||||
s,.*,import,
|
||||
p
|
||||
q
|
||||
}
|
||||
}'`
|
||||
case $win32_nmres in
|
||||
import*) win32_libid_type="x86 archive import";;
|
||||
*) win32_libid_type="x86 archive static";;
|
||||
@@ -340,11 +348,11 @@ func_extract_archives ()
|
||||
my_xlib_u=$my_xlib
|
||||
while :; do
|
||||
case " $extracted_archives " in
|
||||
*" $my_xlib_u "*)
|
||||
extracted_serial=`expr $extracted_serial + 1`
|
||||
my_xlib_u=lt$extracted_serial-$my_xlib ;;
|
||||
*) break ;;
|
||||
esac
|
||||
*" $my_xlib_u "*)
|
||||
extracted_serial=`expr $extracted_serial + 1`
|
||||
my_xlib_u=lt$extracted_serial-$my_xlib ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
extracted_archives="$extracted_archives $my_xlib_u"
|
||||
my_xdir="$my_gentop/$my_xlib_u"
|
||||
@@ -474,11 +482,12 @@ do
|
||||
;;
|
||||
|
||||
--version)
|
||||
$echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP"
|
||||
$echo
|
||||
$echo "Copyright (C) 2005 Free Software Foundation, Inc."
|
||||
$echo "This is free software; see the source for copying conditions. There is NO"
|
||||
$echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
echo "\
|
||||
$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc.
|
||||
This is free software; see the source for copying conditions. There is NO
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
exit $?
|
||||
;;
|
||||
|
||||
@@ -775,7 +784,7 @@ if test -z "$show_help"; then
|
||||
*.class) xform=class ;;
|
||||
*.cpp) xform=cpp ;;
|
||||
*.cxx) xform=cxx ;;
|
||||
*.f90) xform=f90 ;;
|
||||
*.[fF][09]?) xform=[fF][09]. ;;
|
||||
*.for) xform=for ;;
|
||||
*.java) xform=java ;;
|
||||
*.obj) xform=obj ;;
|
||||
@@ -1160,8 +1169,8 @@ EOF
|
||||
do
|
||||
case $arg in
|
||||
-all-static | -static | -static-libtool-libs)
|
||||
case $arg in
|
||||
-all-static)
|
||||
case $arg in
|
||||
-all-static)
|
||||
if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then
|
||||
$echo "$modename: warning: complete static linking is impossible in this configuration" 1>&2
|
||||
fi
|
||||
@@ -1170,19 +1179,19 @@ EOF
|
||||
fi
|
||||
prefer_static_libs=yes
|
||||
;;
|
||||
-static)
|
||||
-static)
|
||||
if test -z "$pic_flag" && test -n "$link_static_flag"; then
|
||||
dlopen_self=$dlopen_self_static
|
||||
fi
|
||||
prefer_static_libs=built
|
||||
;;
|
||||
-static-libtool-libs)
|
||||
if test -z "$pic_flag" && test -n "$link_static_flag"; then
|
||||
dlopen_self=$dlopen_self_static
|
||||
fi
|
||||
prefer_static_libs=yes
|
||||
;;
|
||||
esac
|
||||
-static-libtool-libs)
|
||||
if test -z "$pic_flag" && test -n "$link_static_flag"; then
|
||||
dlopen_self=$dlopen_self_static
|
||||
fi
|
||||
prefer_static_libs=yes
|
||||
;;
|
||||
esac
|
||||
build_libtool_libs=no
|
||||
build_old_libs=yes
|
||||
break
|
||||
@@ -1630,7 +1639,7 @@ EOF
|
||||
continue
|
||||
;;
|
||||
|
||||
-mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe)
|
||||
-mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads)
|
||||
compiler_flags="$compiler_flags $arg"
|
||||
compile_command="$compile_command $arg"
|
||||
finalize_command="$finalize_command $arg"
|
||||
@@ -1650,10 +1659,11 @@ EOF
|
||||
# -m* pass through architecture-specific compiler args for GCC
|
||||
# -m*, -t[45]*, -txscale* pass through architecture-specific
|
||||
# compiler args for GCC
|
||||
# -pg pass through profiling flag for GCC
|
||||
# -p, -pg, --coverage, -fprofile-* pass through profiling flag for GCC
|
||||
# -F/path gives path to uninstalled frameworks, gcc on darwin
|
||||
# @file GCC response files
|
||||
-64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*|-pg| \
|
||||
-t[45]*|-txscale*|@*)
|
||||
-64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
|
||||
-t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*)
|
||||
|
||||
# Unknown arguments in both finalize_command and compile_command need
|
||||
# to be aesthetically quoted because they are evaled later.
|
||||
@@ -1681,9 +1691,9 @@ EOF
|
||||
|
||||
-no-install)
|
||||
case $host in
|
||||
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*)
|
||||
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin*)
|
||||
# The PATH hackery in wrapper scripts is required on Windows
|
||||
# in order for the loader to find any dlls it needs.
|
||||
# and Darwin in order for the loader to find any dlls it needs.
|
||||
$echo "$modename: warning: \`-no-install' is ignored for $host" 1>&2
|
||||
$echo "$modename: warning: assuming \`-no-fast-install' instead" 1>&2
|
||||
fast_install=no
|
||||
@@ -2124,7 +2134,7 @@ EOF
|
||||
lib=
|
||||
found=no
|
||||
case $deplib in
|
||||
-mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe)
|
||||
-mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads)
|
||||
if test "$linkmode,$pass" = "prog,link"; then
|
||||
compile_deplibs="$deplib $compile_deplibs"
|
||||
finalize_deplibs="$deplib $finalize_deplibs"
|
||||
@@ -2520,9 +2530,9 @@ EOF
|
||||
|
||||
if test "$linkmode,$pass" = "prog,link"; then
|
||||
if test -n "$library_names" &&
|
||||
{ { test "$prefer_static_libs" = no ||
|
||||
test "$prefer_static_libs,$installed" = "built,yes"; } ||
|
||||
test -z "$old_library"; }; then
|
||||
{ { test "$prefer_static_libs" = no ||
|
||||
test "$prefer_static_libs,$installed" = "built,yes"; } ||
|
||||
test -z "$old_library"; }; then
|
||||
# We need to hardcode the library path
|
||||
if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then
|
||||
# Make sure the rpath contains only unique directories.
|
||||
@@ -3229,9 +3239,10 @@ EOF
|
||||
age="0"
|
||||
;;
|
||||
irix|nonstopux)
|
||||
current=`expr $number_major + $number_minor - 1`
|
||||
current=`expr $number_major + $number_minor`
|
||||
age="$number_minor"
|
||||
revision="$number_minor"
|
||||
lt_irix_increment=no
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
@@ -3290,7 +3301,8 @@ EOF
|
||||
versuffix="$major.$age.$revision"
|
||||
# Darwin ld doesn't like 0 for these options...
|
||||
minor_current=`expr $current + 1`
|
||||
verstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision"
|
||||
xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision"
|
||||
verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
|
||||
;;
|
||||
|
||||
freebsd-aout)
|
||||
@@ -3304,8 +3316,11 @@ EOF
|
||||
;;
|
||||
|
||||
irix | nonstopux)
|
||||
major=`expr $current - $age + 1`
|
||||
|
||||
if test "X$lt_irix_increment" = "Xno"; then
|
||||
major=`expr $current - $age`
|
||||
else
|
||||
major=`expr $current - $age + 1`
|
||||
fi
|
||||
case $version_type in
|
||||
nonstopux) verstring_prefix=nonstopux ;;
|
||||
*) verstring_prefix=sgi ;;
|
||||
@@ -3442,11 +3457,11 @@ EOF
|
||||
fi
|
||||
|
||||
# Eliminate all temporary directories.
|
||||
# for path in $notinst_path; do
|
||||
# lib_search_path=`$echo "$lib_search_path " | ${SED} -e "s% $path % %g"`
|
||||
# deplibs=`$echo "$deplibs " | ${SED} -e "s% -L$path % %g"`
|
||||
# dependency_libs=`$echo "$dependency_libs " | ${SED} -e "s% -L$path % %g"`
|
||||
# done
|
||||
#for path in $notinst_path; do
|
||||
# lib_search_path=`$echo "$lib_search_path " | ${SED} -e "s% $path % %g"`
|
||||
# deplibs=`$echo "$deplibs " | ${SED} -e "s% -L$path % %g"`
|
||||
# dependency_libs=`$echo "$dependency_libs " | ${SED} -e "s% -L$path % %g"`
|
||||
#done
|
||||
|
||||
if test -n "$xrpath"; then
|
||||
# If the user specified any rpath flags, then add them.
|
||||
@@ -3547,7 +3562,7 @@ EOF
|
||||
int main() { return 0; }
|
||||
EOF
|
||||
$rm conftest
|
||||
if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
|
||||
if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
|
||||
ldd_output=`ldd conftest`
|
||||
for i in $deplibs; do
|
||||
name=`expr $i : '-l\(.*\)'`
|
||||
@@ -3909,7 +3924,10 @@ EOF
|
||||
test -n "$hardcode_libdirs"; then
|
||||
libdir="$hardcode_libdirs"
|
||||
if test -n "$hardcode_libdir_flag_spec_ld"; then
|
||||
eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\"
|
||||
case $archive_cmds in
|
||||
*\$LD*) eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" ;;
|
||||
*) eval dep_rpath=\"$hardcode_libdir_flag_spec\" ;;
|
||||
esac
|
||||
else
|
||||
eval dep_rpath=\"$hardcode_libdir_flag_spec\"
|
||||
fi
|
||||
@@ -4275,7 +4293,7 @@ EOF
|
||||
if test -n "$convenience"; then
|
||||
if test -n "$whole_archive_flag_spec"; then
|
||||
eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\"
|
||||
reload_conv_objs=$reload_objs\ `$echo "X$tmp_whole_archive_flags" | $Xsed -e 's|,| |g'`
|
||||
reload_conv_objs=$reload_objs\ `$echo "X$tmp_whole_archive_flags" | $Xsed -e 's|,| |g'`
|
||||
else
|
||||
gentop="$output_objdir/${obj}x"
|
||||
generated="$generated $gentop"
|
||||
@@ -5295,6 +5313,8 @@ if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then
|
||||
else
|
||||
case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac
|
||||
fi
|
||||
BIN_SH=xpg4; export BIN_SH # for Tru64
|
||||
DUALCASE=1; export DUALCASE # for MKS sh
|
||||
|
||||
# The HP-UX ksh and POSIX shell print the target directory to stdout
|
||||
# if CDPATH is set.
|
||||
@@ -6391,8 +6411,10 @@ relink_command=\"$relink_command\""
|
||||
if test -f "$dir/$objdir/$dlname"; then
|
||||
dir="$dir/$objdir"
|
||||
else
|
||||
$echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2
|
||||
exit $EXIT_FAILURE
|
||||
if test ! -f "$dir/$dlname"; then
|
||||
$echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2
|
||||
exit $EXIT_FAILURE
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
|
||||
@@ -6458,12 +6480,11 @@ relink_command=\"$relink_command\""
|
||||
# Restore saved environment variables
|
||||
for lt_var in LANG LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
|
||||
do
|
||||
eval "if test \"\${save_$lt_var+set}\" = set; then
|
||||
$lt_var=\$save_$lt_var; export $lt_var
|
||||
fi"
|
||||
eval "if test \"\${save_$lt_var+set}\" = set; then
|
||||
$lt_var=\$save_$lt_var; export $lt_var
|
||||
fi"
|
||||
done
|
||||
|
||||
|
||||
# Now prepare to actually exec the command.
|
||||
exec_cmd="\$cmd$args"
|
||||
else
|
||||
|
||||
+48
-5
@@ -48,6 +48,9 @@ Generate a distribution tar file for NSD.
|
||||
-h This usage information.
|
||||
-s Build a snapshot distribution file. The current date is
|
||||
automatically appended to the current NSD version number.
|
||||
-rc <nr> Build a release candidate, the given string will be added
|
||||
to the version number
|
||||
(which will then be unbound-<version>rc<number>)
|
||||
-d SVN_root Retrieve the NSD source from the specified repository.
|
||||
Detected from svn working copy if not specified.
|
||||
-l ldnsdir Directory where ldns resides. Detected from Makefile.
|
||||
@@ -105,6 +108,7 @@ replace_all () {
|
||||
|
||||
|
||||
SNAPSHOT="no"
|
||||
RC="no"
|
||||
LDNSDIR=""
|
||||
|
||||
# Parse the command line arguments.
|
||||
@@ -124,6 +128,10 @@ while [ "$1" ]; do
|
||||
LDNSDIR="$2"
|
||||
shift
|
||||
;;
|
||||
"-rc")
|
||||
RC="$2"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
error "Unrecognized argument -- $1"
|
||||
;;
|
||||
@@ -146,7 +154,9 @@ if test -z "$LDNSDIR"; then
|
||||
# try to autodetect from Makefile (if present)
|
||||
if test -f Makefile; then
|
||||
eval `grep 'ldnsdir=' Makefile`
|
||||
LDNSDIR="$ldnsdir"
|
||||
if echo "$ldnsdir" | grep -v ldns-src/ >/dev/null 2>&1; then
|
||||
LDNSDIR="$ldnsdir"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -169,7 +179,7 @@ svn export "$SVNROOT" unbound || error_cleanup "SVN command failed"
|
||||
cd unbound || error_cleanup "Unbound not exported correctly from SVN"
|
||||
|
||||
info "Adding libtool utils (libtoolize)."
|
||||
libtoolize -c || error_cleanup "libtoolize failed"
|
||||
libtoolize -c --install || libtoolize -c || error_cleanup "Libtoolize failed."
|
||||
|
||||
info "Building configure script (autoreconf)."
|
||||
autoreconf || error_cleanup "Autoconf failed."
|
||||
@@ -182,6 +192,7 @@ flex -i -t util/configlexer.lex >> util/configlexer.c || error_cleanup "Failed
|
||||
bison -y -d -o util/configparser.c util/configparser.y || error_cleanup "Failed to create configparser"
|
||||
|
||||
# check shared code, ldns-testpkts from ldns examples, if possible.
|
||||
cd ../..
|
||||
if test ! -z "$LDNSDIR"; then
|
||||
if diff -q $LDNSDIR/examples/ldns-testpkts.c testcode/ldns-testpkts.c &&
|
||||
diff -q $LDNSDIR/examples/ldns-testpkts.h testcode/ldns-testpkts.h; then
|
||||
@@ -190,6 +201,7 @@ if test ! -z "$LDNSDIR"; then
|
||||
error_cleanup "ldns-testpkts is different in ldns and unbound"
|
||||
fi
|
||||
fi
|
||||
cd $temp_dir/unbound
|
||||
|
||||
find . -name .c-mode-rc.el -exec rm {} \;
|
||||
find . -name .cvsignore -exec rm {} \;
|
||||
@@ -201,14 +213,41 @@ version=`./configure --version | head -1 | awk '{ print $3 }'` || \
|
||||
|
||||
info "Unbound version: $version"
|
||||
|
||||
RECONFIGURE="no"
|
||||
|
||||
if [ "$RC" != "no" ]; then
|
||||
info "Building Unbound release candidate $RC."
|
||||
version2="${version}rc$RC"
|
||||
info "Version number: $version2"
|
||||
|
||||
replace_text "configure.ac" "AC_INIT(unbound, $version" "AC_INIT(unbound, $version2"
|
||||
version="$version2"
|
||||
RECONFIGURE="yes"
|
||||
fi
|
||||
|
||||
if [ "$SNAPSHOT" = "yes" ]; then
|
||||
info "Building Unbound snapshot."
|
||||
version="$version-`date +%Y%m%d`"
|
||||
info "Snapshot version number: $version"
|
||||
version2="${version}_`date +%Y%m%d`"
|
||||
info "Snapshot version number: $version2"
|
||||
|
||||
replace_text "configure.ac" "AC_INIT(unbound, $version" "AC_INIT(unbound, $version2"
|
||||
version="$version2"
|
||||
RECONFIGURE="yes"
|
||||
fi
|
||||
|
||||
if [ "$RECONFIGURE" = "yes" ]; then
|
||||
info "Rebuilding configure script (autoconf) snapshot."
|
||||
autoreconf || error_cleanup "Autoconf failed."
|
||||
rm -r autom4te* || error_cleanup "Failed to remove autoconf cache directory."
|
||||
fi
|
||||
|
||||
replace_all doc/README
|
||||
replace_all doc/unbound.8
|
||||
replace_all doc/unbound.8.in
|
||||
replace_all doc/unbound.conf.5.in
|
||||
replace_all doc/unbound-checkconf.8.in
|
||||
replace_all doc/unbound-control.8.in
|
||||
replace_all doc/unbound-host.1
|
||||
replace_all doc/libunbound.3.in
|
||||
|
||||
info "Renaming Unbound directory to unbound-$version."
|
||||
cd ..
|
||||
@@ -229,15 +268,19 @@ cleanup
|
||||
case $OSTYPE in
|
||||
linux*)
|
||||
sha=`sha1sum unbound-$version.tar.gz | awk '{ print $1 }'`
|
||||
sha256=`sha256sum unbound-$version.tar.gz | awk '{ print $1 }'`
|
||||
;;
|
||||
freebsd*)
|
||||
sha=`sha1 unbound-$version.tar.gz | awk '{ print $5 }'`
|
||||
sha256=`sha256 unbound-$version.tar.gz | awk '{ print $5 }'`
|
||||
;;
|
||||
*)
|
||||
sha=`sha1sum unbound-$version.tar.gz | awk '{ print $1 }'`
|
||||
sha256=`sha256sum unbound-$version.tar.gz | awk '{ print $1 }'`
|
||||
;;
|
||||
esac
|
||||
echo $sha > unbound-$version.tar.gz.sha1
|
||||
echo $sha256 > unbound-$version.tar.gz.sha256
|
||||
|
||||
info "Unbound distribution created successfully."
|
||||
info "SHA1sum: $sha"
|
||||
|
||||
Vendored
+104
-78
@@ -52,7 +52,7 @@
|
||||
/** store rrsets in the rrset cache.
|
||||
* @param env: module environment with caches.
|
||||
* @param rep: contains list of rrsets to store.
|
||||
* @param now: current time(NULL).
|
||||
* @param now: current time.
|
||||
*/
|
||||
static void
|
||||
store_rrsets(struct module_env* env, struct reply_info* rep, uint32_t now)
|
||||
@@ -73,7 +73,7 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
|
||||
hashvalue_t hash, struct reply_info* rep)
|
||||
{
|
||||
struct msgreply_entry* e;
|
||||
uint32_t now = time(NULL), ttl = rep->ttl;
|
||||
uint32_t ttl = rep->ttl;
|
||||
size_t i;
|
||||
|
||||
/* store RRsets */
|
||||
@@ -82,8 +82,8 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
|
||||
rep->ref[i].id = rep->rrsets[i]->id;
|
||||
}
|
||||
reply_info_sortref(rep);
|
||||
reply_info_set_ttls(rep, now);
|
||||
store_rrsets(env, rep, now);
|
||||
reply_info_set_ttls(rep, *env->now);
|
||||
store_rrsets(env, rep, *env->now);
|
||||
if(ttl == 0) {
|
||||
/* we do not store the message, but we did store the RRs,
|
||||
* which could be useful for delegation information */
|
||||
@@ -101,41 +101,6 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
|
||||
slabhash_insert(env->msg_cache, hash, &e->entry, rep, env->alloc);
|
||||
}
|
||||
|
||||
/** allocate rrset in region - no more locks needed */
|
||||
static struct ub_packed_rrset_key*
|
||||
copy_rrset(struct ub_packed_rrset_key* key, struct regional* region,
|
||||
uint32_t now)
|
||||
{
|
||||
struct ub_packed_rrset_key* ck = regional_alloc(region,
|
||||
sizeof(struct ub_packed_rrset_key));
|
||||
struct packed_rrset_data* d;
|
||||
struct packed_rrset_data* data = (struct packed_rrset_data*)
|
||||
key->entry.data;
|
||||
size_t dsize, i;
|
||||
if(!ck)
|
||||
return NULL;
|
||||
ck->id = key->id;
|
||||
memset(&ck->entry, 0, sizeof(ck->entry));
|
||||
ck->entry.hash = key->entry.hash;
|
||||
ck->entry.key = ck;
|
||||
ck->rk = key->rk;
|
||||
ck->rk.dname = regional_alloc_init(region, key->rk.dname,
|
||||
key->rk.dname_len);
|
||||
if(!ck->rk.dname)
|
||||
return NULL;
|
||||
dsize = packed_rrset_sizeof(data);
|
||||
d = (struct packed_rrset_data*)regional_alloc_init(region, data, dsize);
|
||||
if(!d)
|
||||
return NULL;
|
||||
ck->entry.data = d;
|
||||
packed_rrset_ptr_fixup(d);
|
||||
/* make TTLs relative - once per rrset */
|
||||
for(i=0; i<d->count + d->rrsig_count; i++)
|
||||
d->rr_ttl[i] -= now;
|
||||
d->ttl -= now;
|
||||
return ck;
|
||||
}
|
||||
|
||||
/** find closest NS or DNAME and returns the rrset (locked) */
|
||||
static struct ub_packed_rrset_key*
|
||||
find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen,
|
||||
@@ -171,12 +136,36 @@ addr_to_additional(struct ub_packed_rrset_key* rrset, struct regional* region,
|
||||
struct dns_msg* msg, uint32_t now)
|
||||
{
|
||||
if((msg->rep->rrsets[msg->rep->rrset_count] =
|
||||
copy_rrset(rrset, region, now))) {
|
||||
packed_rrset_copy_region(rrset, region, now))) {
|
||||
msg->rep->ar_numrrsets++;
|
||||
msg->rep->rrset_count++;
|
||||
}
|
||||
}
|
||||
|
||||
/** lookup message in message cache */
|
||||
static struct msgreply_entry*
|
||||
msg_cache_lookup(struct module_env* env, uint8_t* qname, size_t qnamelen,
|
||||
uint16_t qtype, uint16_t qclass, uint32_t now, int wr)
|
||||
{
|
||||
struct lruhash_entry* e;
|
||||
struct query_info k;
|
||||
hashvalue_t h;
|
||||
|
||||
k.qname = qname;
|
||||
k.qname_len = qnamelen;
|
||||
k.qtype = qtype;
|
||||
k.qclass = qclass;
|
||||
h = query_info_hash(&k);
|
||||
e = slabhash_lookup(env->msg_cache, h, &k, wr);
|
||||
|
||||
if(!e) return NULL;
|
||||
if( now > ((struct reply_info*)e->data)->ttl ) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return NULL;
|
||||
}
|
||||
return (struct msgreply_entry*)e->key;
|
||||
}
|
||||
|
||||
/** find and add A and AAAA records for nameservers in delegpt */
|
||||
static int
|
||||
find_add_addrs(struct module_env* env, uint16_t qclass,
|
||||
@@ -184,29 +173,44 @@ find_add_addrs(struct module_env* env, uint16_t qclass,
|
||||
struct dns_msg** msg)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
struct msgreply_entry* neg;
|
||||
struct ub_packed_rrset_key* akey;
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
akey = rrset_cache_lookup(env->rrset_cache, ns->name,
|
||||
ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0);
|
||||
if(akey) {
|
||||
if(!delegpt_add_rrset_A(dp, region, akey)) {
|
||||
if(!delegpt_add_rrset_A(dp, region, akey, 0)) {
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
return 0;
|
||||
}
|
||||
if(msg)
|
||||
addr_to_additional(akey, region, *msg, now);
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
} else {
|
||||
neg = msg_cache_lookup(env, ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_A, qclass, now, 0);
|
||||
if(neg) {
|
||||
delegpt_add_neg_msg(dp, neg);
|
||||
lock_rw_unlock(&neg->entry.lock);
|
||||
}
|
||||
}
|
||||
akey = rrset_cache_lookup(env->rrset_cache, ns->name,
|
||||
ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
|
||||
if(akey) {
|
||||
if(!delegpt_add_rrset_AAAA(dp, region, akey)) {
|
||||
if(!delegpt_add_rrset_AAAA(dp, region, akey, 0)) {
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
return 0;
|
||||
}
|
||||
if(msg)
|
||||
addr_to_additional(akey, region, *msg, now);
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
} else {
|
||||
neg = msg_cache_lookup(env, ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_AAAA, qclass, now, 0);
|
||||
if(neg) {
|
||||
delegpt_add_neg_msg(dp, neg);
|
||||
lock_rw_unlock(&neg->entry.lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
@@ -218,32 +222,47 @@ cache_fill_missing(struct module_env* env, uint16_t qclass,
|
||||
struct regional* region, struct delegpt* dp)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
struct msgreply_entry* neg;
|
||||
struct ub_packed_rrset_key* akey;
|
||||
uint32_t now = time(NULL);
|
||||
uint32_t now = *env->now;
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
if(ns->resolved)
|
||||
continue;
|
||||
akey = rrset_cache_lookup(env->rrset_cache, ns->name,
|
||||
ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0);
|
||||
if(akey) {
|
||||
if(!delegpt_add_rrset_A(dp, region, akey)) {
|
||||
if(!delegpt_add_rrset_A(dp, region, akey, 1)) {
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
return 0;
|
||||
}
|
||||
log_nametypeclass(VERB_ALGO, "found in cache",
|
||||
ns->name, LDNS_RR_TYPE_A, qclass);
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
} else {
|
||||
neg = msg_cache_lookup(env, ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_A, qclass, now, 0);
|
||||
if(neg) {
|
||||
delegpt_add_neg_msg(dp, neg);
|
||||
lock_rw_unlock(&neg->entry.lock);
|
||||
}
|
||||
}
|
||||
akey = rrset_cache_lookup(env->rrset_cache, ns->name,
|
||||
ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
|
||||
if(akey) {
|
||||
if(!delegpt_add_rrset_AAAA(dp, region, akey)) {
|
||||
if(!delegpt_add_rrset_AAAA(dp, region, akey, 1)) {
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
return 0;
|
||||
}
|
||||
log_nametypeclass(VERB_ALGO, "found in cache",
|
||||
ns->name, LDNS_RR_TYPE_AAAA, qclass);
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
} else {
|
||||
neg = msg_cache_lookup(env, ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_AAAA, qclass, now, 0);
|
||||
if(neg) {
|
||||
delegpt_add_neg_msg(dp, neg);
|
||||
lock_rw_unlock(&neg->entry.lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
@@ -271,7 +290,7 @@ find_add_ds(struct module_env* env, struct regional* region,
|
||||
if(rrset) {
|
||||
/* add it to auth section. This is the second rrset. */
|
||||
if((msg->rep->rrsets[msg->rep->rrset_count] =
|
||||
copy_rrset(rrset, region, now))) {
|
||||
packed_rrset_copy_region(rrset, region, now))) {
|
||||
msg->rep->ns_numrrsets++;
|
||||
msg->rep->rrset_count++;
|
||||
}
|
||||
@@ -279,11 +298,9 @@ find_add_ds(struct module_env* env, struct regional* region,
|
||||
}
|
||||
}
|
||||
|
||||
/** create referral message with NS and query */
|
||||
static struct dns_msg*
|
||||
create_msg(uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
struct regional* region, struct ub_packed_rrset_key* nskey,
|
||||
struct packed_rrset_data* nsdata, uint32_t now)
|
||||
struct dns_msg*
|
||||
dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype,
|
||||
uint16_t qclass, struct regional* region, size_t capacity)
|
||||
{
|
||||
struct dns_msg* msg = (struct dns_msg*)regional_alloc(region,
|
||||
sizeof(struct dns_msg));
|
||||
@@ -296,32 +313,31 @@ create_msg(uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
msg->qinfo.qtype = qtype;
|
||||
msg->qinfo.qclass = qclass;
|
||||
/* non-packed reply_info, because it needs to grow the array */
|
||||
msg->rep = (struct reply_info*)regional_alloc(region,
|
||||
msg->rep = (struct reply_info*)regional_alloc_zero(region,
|
||||
sizeof(struct reply_info)-sizeof(struct rrset_ref));
|
||||
if(!msg->rep)
|
||||
return NULL;
|
||||
memset(msg->rep, 0,
|
||||
sizeof(struct reply_info)-sizeof(struct rrset_ref));
|
||||
msg->rep->flags = BIT_QR; /* with QR, no AA */
|
||||
msg->rep->qdcount = 1;
|
||||
/* allocate the array to as much as we could need:
|
||||
* NS rrset + DS/NSEC rrset +
|
||||
* A rrset for every NS RR
|
||||
* AAAA rrset for every NS RR
|
||||
*/
|
||||
msg->rep->rrsets = (struct ub_packed_rrset_key**)
|
||||
regional_alloc(region,
|
||||
(2 + nsdata->count*2)*sizeof(struct ub_packed_rrset_key*));
|
||||
capacity*sizeof(struct ub_packed_rrset_key*));
|
||||
if(!msg->rep->rrsets)
|
||||
return NULL;
|
||||
msg->rep->rrsets[0] = copy_rrset(nskey, region, now);
|
||||
if(!msg->rep->rrsets[0])
|
||||
return NULL;
|
||||
msg->rep->ns_numrrsets++;
|
||||
msg->rep->rrset_count++;
|
||||
return msg;
|
||||
}
|
||||
|
||||
int
|
||||
dns_msg_authadd(struct dns_msg* msg, struct regional* region,
|
||||
struct ub_packed_rrset_key* rrset, uint32_t now)
|
||||
{
|
||||
if(!(msg->rep->rrsets[msg->rep->rrset_count++] =
|
||||
packed_rrset_copy_region(rrset, region, now)))
|
||||
return 0;
|
||||
msg->rep->ns_numrrsets++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct delegpt*
|
||||
dns_cache_find_delegation(struct module_env* env, uint8_t* qname,
|
||||
size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
@@ -346,9 +362,14 @@ dns_cache_find_delegation(struct module_env* env, uint8_t* qname,
|
||||
}
|
||||
/* create referral message */
|
||||
if(msg) {
|
||||
*msg = create_msg(qname, qnamelen, qtype, qclass, region,
|
||||
nskey, nsdata, now);
|
||||
if(!*msg) {
|
||||
/* allocate the array to as much as we could need:
|
||||
* NS rrset + DS/NSEC rrset +
|
||||
* A rrset for every NS RR
|
||||
* AAAA rrset for every NS RR
|
||||
*/
|
||||
*msg = dns_msg_create(qname, qnamelen, qtype, qclass, region,
|
||||
2 + nsdata->count*2);
|
||||
if(!*msg || !dns_msg_authadd(*msg, region, nskey, now)) {
|
||||
lock_rw_unlock(&nskey->entry.lock);
|
||||
log_err("find_delegation: out of memory");
|
||||
return NULL;
|
||||
@@ -414,7 +435,8 @@ tomsg(struct module_env* env, struct msgreply_entry* e, struct reply_info* r,
|
||||
if(!rrset_array_lock(r->ref, r->rrset_count, now))
|
||||
return NULL;
|
||||
for(i=0; i<msg->rep->rrset_count; i++) {
|
||||
msg->rep->rrsets[i] = copy_rrset(r->rrsets[i], region, now);
|
||||
msg->rep->rrsets[i] = packed_rrset_copy_region(r->rrsets[i],
|
||||
region, now);
|
||||
if(!msg->rep->rrsets[i]) {
|
||||
rrset_array_unlock(r->ref, r->rrset_count);
|
||||
return NULL;
|
||||
@@ -446,7 +468,7 @@ rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region,
|
||||
msg->rep->ns_numrrsets = 0;
|
||||
msg->rep->ar_numrrsets = 0;
|
||||
msg->rep->rrset_count = 1;
|
||||
msg->rep->rrsets[0] = copy_rrset(rrset, region, now);
|
||||
msg->rep->rrsets[0] = packed_rrset_copy_region(rrset, region, now);
|
||||
if(!msg->rep->rrsets[0]) /* copy CNAME */
|
||||
return NULL;
|
||||
return msg;
|
||||
@@ -465,6 +487,10 @@ synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region,
|
||||
size_t newlen, dtarglen;
|
||||
if(now > d->ttl)
|
||||
return NULL;
|
||||
/* only allow validated (with DNSSEC) DNAMEs used from cache
|
||||
* for insecure DNAMEs, query again. */
|
||||
if(d->security != sec_status_secure)
|
||||
return NULL;
|
||||
msg = gen_dns_msg(region, q, 2); /* DNAME + CNAME RRset */
|
||||
if(!msg)
|
||||
return NULL;
|
||||
@@ -476,7 +502,7 @@ synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region,
|
||||
msg->rep->ns_numrrsets = 0;
|
||||
msg->rep->ar_numrrsets = 0;
|
||||
msg->rep->rrset_count = 1;
|
||||
msg->rep->rrsets[0] = copy_rrset(rrset, region, now);
|
||||
msg->rep->rrsets[0] = packed_rrset_copy_region(rrset, region, now);
|
||||
if(!msg->rep->rrsets[0]) /* copy DNAME */
|
||||
return NULL;
|
||||
/* synth CNAME rrset */
|
||||
@@ -511,7 +537,7 @@ synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region,
|
||||
return NULL;
|
||||
ck->rk.dname_len = q->qname_len;
|
||||
ck->entry.hash = rrset_key_hash(&ck->rk);
|
||||
newd = (struct packed_rrset_data*)regional_alloc(region,
|
||||
newd = (struct packed_rrset_data*)regional_alloc_zero(region,
|
||||
sizeof(struct packed_rrset_data) + sizeof(size_t) +
|
||||
sizeof(uint8_t*) + sizeof(uint32_t) + sizeof(uint16_t)
|
||||
+ newlen);
|
||||
@@ -543,7 +569,7 @@ dns_cache_lookup(struct module_env* env,
|
||||
struct lruhash_entry* e;
|
||||
struct query_info k;
|
||||
hashvalue_t h;
|
||||
uint32_t now = (uint32_t)time(NULL);
|
||||
uint32_t now = *env->now;
|
||||
struct ub_packed_rrset_key* rrset;
|
||||
|
||||
/* lookup first, this has both NXdomains and ANSWER responses */
|
||||
@@ -591,8 +617,9 @@ dns_cache_lookup(struct module_env* env,
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
}
|
||||
|
||||
/* construct DS, DNSKEY messages from rrset cache. */
|
||||
if((qtype == LDNS_RR_TYPE_DS || qtype == LDNS_RR_TYPE_DNSKEY) &&
|
||||
/* construct DS, DNSKEY, DLV messages from rrset cache. */
|
||||
if((qtype == LDNS_RR_TYPE_DS || qtype == LDNS_RR_TYPE_DNSKEY ||
|
||||
qtype == LDNS_RR_TYPE_DLV) &&
|
||||
(rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen,
|
||||
qtype, qclass, 0, now, 0))) {
|
||||
/* if the rrset is from the additional section, and the
|
||||
@@ -630,16 +657,15 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
|
||||
if(is_referral) {
|
||||
/* store rrsets */
|
||||
struct rrset_ref ref;
|
||||
uint32_t now = time(NULL);
|
||||
size_t i;
|
||||
for(i=0; i<rep->rrset_count; i++) {
|
||||
packed_rrset_ttl_add((struct packed_rrset_data*)
|
||||
rep->rrsets[i]->entry.data, now);
|
||||
rep->rrsets[i]->entry.data, *env->now);
|
||||
ref.key = rep->rrsets[i];
|
||||
ref.id = rep->rrsets[i]->id;
|
||||
/*ignore ret: it was in the cache, ref updated */
|
||||
(void)rrset_cache_update(env->rrset_cache, &ref,
|
||||
env->alloc, now);
|
||||
env->alloc, *env->now);
|
||||
}
|
||||
free(rep);
|
||||
return 1;
|
||||
|
||||
Vendored
+25
-1
@@ -137,6 +137,30 @@ struct dns_msg* dns_cache_lookup(struct module_env* env,
|
||||
int cache_fill_missing(struct module_env* env, uint16_t qclass,
|
||||
struct regional* region, struct delegpt* dp);
|
||||
|
||||
/** Find covering DNAME */
|
||||
/**
|
||||
* Utility, create new, unpacked data structure for cache response.
|
||||
* QR bit set, no AA. Query set as indicated. Space for number of rrsets.
|
||||
* @param qname: query section name
|
||||
* @param qnamelen: len of qname
|
||||
* @param qtype: query section type
|
||||
* @param qclass: query section class
|
||||
* @param region: where to alloc.
|
||||
* @param capacity: number of rrsets space to create in the array.
|
||||
* @return new dns_msg struct or NULL on mem fail.
|
||||
*/
|
||||
struct dns_msg* dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype,
|
||||
uint16_t qclass, struct regional* region, size_t capacity);
|
||||
|
||||
/**
|
||||
* Add rrset to authority section in unpacked dns_msg message. Must have enough
|
||||
* space left, does not grow the array.
|
||||
* @param msg: msg to put it in.
|
||||
* @param region: region to alloc in
|
||||
* @param rrset: to add in authority section
|
||||
* @param now: now.
|
||||
* @return true if worked, false on fail
|
||||
*/
|
||||
int dns_msg_authadd(struct dns_msg* msg, struct regional* region,
|
||||
struct ub_packed_rrset_key* rrset, uint32_t now);
|
||||
|
||||
#endif /* SERVICES_CACHE_DNS_H */
|
||||
|
||||
Vendored
+81
-29
@@ -64,12 +64,11 @@ infra_host_compfunc(void* key1, void* key2)
|
||||
}
|
||||
|
||||
void
|
||||
infra_host_delkeyfunc(void* k, void* ATTR_UNUSED(arg), int il)
|
||||
infra_host_delkeyfunc(void* k, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct infra_host_key* key = (struct infra_host_key*)k;
|
||||
if(!key)
|
||||
return;
|
||||
if(il) { lock_rw_unlock(&key->entry.lock); }
|
||||
lock_rw_destroy(&key->entry.lock);
|
||||
free(key);
|
||||
}
|
||||
@@ -137,8 +136,18 @@ static hashvalue_t
|
||||
hash_addr(struct sockaddr_storage* addr, socklen_t addrlen)
|
||||
{
|
||||
hashvalue_t h = 0xab;
|
||||
h = hashlittle(&addrlen, sizeof(addrlen), h);
|
||||
h = hashlittle(addr, addrlen, h);
|
||||
/* select the pieces to hash, some OS have changing data inside */
|
||||
if(addr_is_ip6(addr, addrlen)) {
|
||||
struct sockaddr_in6* in6 = (struct sockaddr_in6*)addr;
|
||||
h = hashlittle(&in6->sin6_family, sizeof(in6->sin6_family), h);
|
||||
h = hashlittle(&in6->sin6_port, sizeof(in6->sin6_port), h);
|
||||
h = hashlittle(&in6->sin6_addr, INET6_SIZE, h);
|
||||
} else {
|
||||
struct sockaddr_in* in = (struct sockaddr_in*)addr;
|
||||
h = hashlittle(&in->sin_family, sizeof(in->sin_family), h);
|
||||
h = hashlittle(&in->sin_port, sizeof(in->sin_port), h);
|
||||
h = hashlittle(&in->sin_addr, INET_SIZE, h);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
@@ -159,7 +168,7 @@ infra_lookup_host_nottl(struct infra_cache* infra,
|
||||
struct infra_host_data*
|
||||
infra_lookup_host(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int wr,
|
||||
time_t timenow, struct infra_host_key** key)
|
||||
uint32_t timenow, struct infra_host_key** key)
|
||||
{
|
||||
struct infra_host_data* data;
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
@@ -187,7 +196,7 @@ infra_lookup_host(struct infra_cache* infra,
|
||||
*/
|
||||
static struct lruhash_entry*
|
||||
new_host_entry(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, time_t tm)
|
||||
socklen_t addrlen, uint32_t tm)
|
||||
{
|
||||
struct infra_host_data* data;
|
||||
struct infra_host_key* key = (struct infra_host_key*)malloc(
|
||||
@@ -209,13 +218,15 @@ new_host_entry(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
data->ttl = tm + infra->host_ttl;
|
||||
data->lameness = NULL;
|
||||
data->edns_version = 0;
|
||||
data->edns_lame_known = 0;
|
||||
rtt_init(&data->rtt);
|
||||
return &key->entry;
|
||||
}
|
||||
|
||||
int
|
||||
infra_host(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, time_t timenow, int* edns_vs, int* to)
|
||||
socklen_t addrlen, uint32_t timenow, int* edns_vs,
|
||||
uint8_t* edns_lame_known, int* to)
|
||||
{
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, 0);
|
||||
@@ -232,6 +243,7 @@ infra_host(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
rtt_init(&data->rtt);
|
||||
/* do not touch lameness, it may be valid still */
|
||||
data->edns_version = 0;
|
||||
data->edns_lame_known = 0;
|
||||
}
|
||||
}
|
||||
if(!e) {
|
||||
@@ -241,6 +253,7 @@ infra_host(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
data = (struct infra_host_data*)e->data;
|
||||
*to = rtt_timeout(&data->rtt);
|
||||
*edns_vs = data->edns_version;
|
||||
*edns_lame_known = data->edns_lame_known;
|
||||
slabhash_insert(infra->hosts, e->hash, e, data, NULL);
|
||||
return 1;
|
||||
}
|
||||
@@ -248,28 +261,29 @@ infra_host(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
data = (struct infra_host_data*)e->data;
|
||||
*to = rtt_timeout(&data->rtt);
|
||||
*edns_vs = data->edns_version;
|
||||
*edns_lame_known = data->edns_lame_known;
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** hash lameness key */
|
||||
static hashvalue_t
|
||||
hash_lameness(uint8_t* name, size_t namelen)
|
||||
hash_lameness(uint8_t* name)
|
||||
{
|
||||
return hashlittle(name, namelen, 0xab);
|
||||
return dname_query_hash(name, 0xab);
|
||||
}
|
||||
|
||||
int
|
||||
infra_lookup_lame(struct infra_host_data* host,
|
||||
uint8_t* name, size_t namelen, time_t timenow)
|
||||
uint8_t* name, size_t namelen, uint32_t timenow,
|
||||
int* dlame, int* rlame, int* alame, int* olame)
|
||||
{
|
||||
struct lruhash_entry* e;
|
||||
struct infra_lame_key k;
|
||||
struct infra_lame_data *d;
|
||||
int dl;
|
||||
if(!host->lameness)
|
||||
return 0;
|
||||
k.entry.hash = hash_lameness(name, namelen);
|
||||
k.entry.hash = hash_lameness(name);
|
||||
k.zonename = name;
|
||||
k.namelen = namelen;
|
||||
k.entry.key = (void*)&k;
|
||||
@@ -282,9 +296,12 @@ infra_lookup_lame(struct infra_host_data* host,
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 0;
|
||||
}
|
||||
dl = d->isdnsseclame;
|
||||
*dlame = d->isdnsseclame;
|
||||
*rlame = d->rec_lame;
|
||||
*alame = d->lame_type_A;
|
||||
*olame = d->lame_other;
|
||||
lock_rw_unlock(&e->lock);
|
||||
return dl?2:1;
|
||||
return *dlame || *rlame || *alame || *olame;
|
||||
}
|
||||
|
||||
size_t
|
||||
@@ -309,12 +326,11 @@ infra_lame_compfunc(void* key1, void* key2)
|
||||
}
|
||||
|
||||
void
|
||||
infra_lame_delkeyfunc(void* k, void* ATTR_UNUSED(arg), int il)
|
||||
infra_lame_delkeyfunc(void* k, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct infra_lame_key* key = (struct infra_lame_key*)k;
|
||||
if(!key)
|
||||
return;
|
||||
if(il) { lock_rw_unlock(&key->entry.lock); }
|
||||
lock_rw_destroy(&key->entry.lock);
|
||||
free(key->zonename);
|
||||
free(key);
|
||||
@@ -331,7 +347,8 @@ infra_lame_deldatafunc(void* d, void* ATTR_UNUSED(arg))
|
||||
int
|
||||
infra_set_lame(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* name, size_t namelen, time_t timenow, int dnsseclame)
|
||||
uint8_t* name, size_t namelen, uint32_t timenow, int dnsseclame,
|
||||
int reclame, uint16_t qtype)
|
||||
{
|
||||
struct infra_host_data* data;
|
||||
struct lruhash_entry* e;
|
||||
@@ -358,11 +375,14 @@ infra_set_lame(struct infra_cache* infra,
|
||||
return 0;
|
||||
}
|
||||
lock_rw_init(&k->entry.lock);
|
||||
k->entry.hash = hash_lameness(name, namelen);
|
||||
k->entry.hash = hash_lameness(name);
|
||||
k->entry.key = (void*)k;
|
||||
k->entry.data = (void*)d;
|
||||
d->ttl = timenow + infra->lame_ttl;
|
||||
d->isdnsseclame = dnsseclame;
|
||||
d->rec_lame = reclame;
|
||||
d->lame_type_A = (!dnsseclame && !reclame && qtype == LDNS_RR_TYPE_A);
|
||||
d->lame_other = (!dnsseclame && !reclame && qtype != LDNS_RR_TYPE_A);
|
||||
k->namelen = namelen;
|
||||
e = infra_lookup_host_nottl(infra, addr, addrlen, 1);
|
||||
if(!e) {
|
||||
@@ -394,7 +414,19 @@ infra_set_lame(struct infra_cache* infra,
|
||||
free(d);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* lookup existing lameness entry (if any) and merge data */
|
||||
int dlame, rlame, alame, olame;
|
||||
if(infra_lookup_lame(data, name, namelen, timenow,
|
||||
&dlame, &rlame, &alame, &olame)) {
|
||||
/* merge data into new structure */
|
||||
if(dlame) d->isdnsseclame = 1;
|
||||
if(rlame) d->rec_lame = 1;
|
||||
if(alame) d->lame_type_A = 1;
|
||||
if(olame) d->lame_other = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* inserts new entry, or updates TTL of older entry */
|
||||
lruhash_insert(data->lameness, k->entry.hash, &k->entry, d, NULL);
|
||||
|
||||
@@ -424,7 +456,7 @@ infra_update_tcp_works(struct infra_cache* infra,
|
||||
int
|
||||
infra_rtt_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int roundtrip, time_t timenow)
|
||||
int roundtrip, int orig_rtt, uint32_t timenow)
|
||||
{
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, 1);
|
||||
@@ -440,7 +472,7 @@ infra_rtt_update(struct infra_cache* infra,
|
||||
data = (struct infra_host_data*)e->data;
|
||||
data->ttl = timenow + infra->host_ttl;
|
||||
if(roundtrip == -1)
|
||||
rtt_lost(&data->rtt);
|
||||
rtt_lost(&data->rtt, orig_rtt);
|
||||
else rtt_update(&data->rtt, roundtrip);
|
||||
if(data->rtt.rto > 0)
|
||||
rto = data->rtt.rto;
|
||||
@@ -454,7 +486,7 @@ infra_rtt_update(struct infra_cache* infra,
|
||||
int
|
||||
infra_edns_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int edns_version, time_t timenow)
|
||||
int edns_version, uint32_t timenow)
|
||||
{
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, 1);
|
||||
@@ -469,6 +501,7 @@ infra_edns_update(struct infra_cache* infra,
|
||||
data = (struct infra_host_data*)e->data;
|
||||
data->ttl = timenow + infra->host_ttl;
|
||||
data->edns_version = edns_version;
|
||||
data->edns_lame_known = 1;
|
||||
|
||||
if(needtoinsert)
|
||||
slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
|
||||
@@ -479,31 +512,50 @@ infra_edns_update(struct infra_cache* infra,
|
||||
int
|
||||
infra_get_lame_rtt(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* name, size_t namelen, int* lame, int* dnsseclame,
|
||||
int* rtt, time_t timenow)
|
||||
uint8_t* name, size_t namelen, uint16_t qtype,
|
||||
int* lame, int* dnsseclame, int* reclame, int* rtt, uint32_t timenow)
|
||||
{
|
||||
struct infra_host_data* host;
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, 0);
|
||||
int lm;
|
||||
int dlm, rlm, alm, olm;
|
||||
if(!e)
|
||||
return 0;
|
||||
host = (struct infra_host_data*)e->data;
|
||||
*rtt = rtt_unclamped(&host->rtt);
|
||||
/* check lameness first, if so, ttl on host does not matter anymore */
|
||||
if((lm=infra_lookup_lame(host, name, namelen, timenow))) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
if(lm == 1) {
|
||||
if(infra_lookup_lame(host, name, namelen, timenow,
|
||||
&dlm, &rlm, &alm, &olm)) {
|
||||
if(alm && qtype == LDNS_RR_TYPE_A) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
*lame = 1;
|
||||
*dnsseclame = 0;
|
||||
} else {
|
||||
*reclame = 0;
|
||||
return 1;
|
||||
} else if(olm && qtype != LDNS_RR_TYPE_A) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
*lame = 1;
|
||||
*dnsseclame = 0;
|
||||
*reclame = 0;
|
||||
return 1;
|
||||
} else if(dlm) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
*lame = 0;
|
||||
*dnsseclame = 1;
|
||||
*reclame = 0;
|
||||
return 1;
|
||||
} else if(rlm) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
*lame = 0;
|
||||
*dnsseclame = 0;
|
||||
*reclame = 1;
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
/* no lameness for this type of query */
|
||||
}
|
||||
*lame = 0;
|
||||
*dnsseclame = 0;
|
||||
*reclame = 0;
|
||||
if(timenow > host->ttl) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 0;
|
||||
|
||||
Vendored
+39
-13
@@ -63,13 +63,17 @@ struct infra_host_key {
|
||||
*/
|
||||
struct infra_host_data {
|
||||
/** TTL value for this entry. absolute time. */
|
||||
time_t ttl;
|
||||
uint32_t ttl;
|
||||
/** round trip times for timeout calculation */
|
||||
struct rtt_info rtt;
|
||||
/** Names of the zones that are lame. NULL=no lame zones. */
|
||||
struct lruhash* lameness;
|
||||
/** edns version that the host supports, -1 means no EDNS */
|
||||
int edns_version;
|
||||
/** if the EDNS lameness is already known or not.
|
||||
* EDNS lame is when EDNS queries or replies are dropped,
|
||||
* and cause a timeout */
|
||||
uint8_t edns_lame_known;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -90,10 +94,16 @@ struct infra_lame_key {
|
||||
*/
|
||||
struct infra_lame_data {
|
||||
/** TTL of this entry. absolute time. */
|
||||
time_t ttl;
|
||||
uint32_t ttl;
|
||||
/** is the host lame (does not serve the zone authoritatively),
|
||||
* or is the host dnssec lame (does not serve DNSSEC data) */
|
||||
int isdnsseclame;
|
||||
/** is the host recursion lame (not AA, but RA) */
|
||||
int rec_lame;
|
||||
/** the host is lame (not authoritative) for A records */
|
||||
int lame_type_A;
|
||||
/** the host is lame (not authoritative) for other query types */
|
||||
int lame_other;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -151,7 +161,7 @@ struct infra_cache* infra_adjust(struct infra_cache* infra,
|
||||
*/
|
||||
struct infra_host_data* infra_lookup_host(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int wr,
|
||||
time_t timenow, struct infra_host_key** key);
|
||||
uint32_t timenow, struct infra_host_key** key);
|
||||
|
||||
/**
|
||||
* Find host information to send a packet. Creates new entry if not found.
|
||||
@@ -162,11 +172,14 @@ struct infra_host_data* infra_lookup_host(struct infra_cache* infra,
|
||||
* @param addrlen: length of addr.
|
||||
* @param timenow: what time it is now.
|
||||
* @param edns_vs: edns version it supports, is returned.
|
||||
* @param edns_lame_known: if EDNS lame (EDNS is dropped in transit) has
|
||||
* already been probed, is returned.
|
||||
* @param to: timeout to use, is returned.
|
||||
* @return: 0 on error.
|
||||
*/
|
||||
int infra_host(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, time_t timenow, int* edns_vs, int* to);
|
||||
socklen_t addrlen, uint32_t timenow, int* edns_vs,
|
||||
uint8_t* edns_lame_known, int* to);
|
||||
|
||||
/**
|
||||
* Check for lameness of this server for a particular zone.
|
||||
@@ -175,10 +188,15 @@ int infra_host(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
* @param name: domain name of zone apex.
|
||||
* @param namelen: length of domain name.
|
||||
* @param timenow: what time it is now.
|
||||
* @return: 0 if not lame or unknown or timed out, 1 if lame, 2 if dnsseclame.
|
||||
* @param dlame: if the function returns true, is set true if dnssec lame.
|
||||
* @param rlame: if the function returns true, is set true if recursion lame.
|
||||
* @param alame: if the function returns true, is set true if qtype A lame.
|
||||
* @param olame: if the function returns true, is set true if qtype other lame.
|
||||
* @return: 0 if not lame or unknown or timed out, 1 if lame
|
||||
*/
|
||||
int infra_lookup_lame(struct infra_host_data* host,
|
||||
uint8_t* name, size_t namelen, time_t timenow);
|
||||
uint8_t* name, size_t namelen, uint32_t timenow,
|
||||
int* dlame, int* rlame, int* alame, int* olame);
|
||||
|
||||
/**
|
||||
* Set a host to be lame for the given zone.
|
||||
@@ -190,11 +208,15 @@ int infra_lookup_lame(struct infra_host_data* host,
|
||||
* @param timenow: what time it is now.
|
||||
* @param dnsseclame: if true the host is set dnssec lame.
|
||||
* if false, the host is marked lame (not serving the zone).
|
||||
* @param reclame: if true host is a recursor not AA server.
|
||||
* if false, dnsseclame or marked lame.
|
||||
* @param qtype: the query type for which it is lame.
|
||||
* @return: 0 on error.
|
||||
*/
|
||||
int infra_set_lame(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* name, size_t namelen, time_t timenow, int dnsseclame);
|
||||
uint8_t* name, size_t namelen, uint32_t timenow, int dnsseclame,
|
||||
int reclame, uint16_t qtype);
|
||||
|
||||
/**
|
||||
* Update rtt information for the host.
|
||||
@@ -203,12 +225,14 @@ int infra_set_lame(struct infra_cache* infra,
|
||||
* @param addrlen: length of addr.
|
||||
* @param roundtrip: estimate of roundtrip time in milliseconds or -1 for
|
||||
* timeout.
|
||||
* @param orig_rtt: original rtt for the query that timed out (roundtrip==-1).
|
||||
* ignored if roundtrip != -1.
|
||||
* @param timenow: what time it is now.
|
||||
* @return: 0 on error. new rto otherwise.
|
||||
*/
|
||||
int infra_rtt_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int roundtrip, time_t timenow);
|
||||
int roundtrip, int orig_rtt, uint32_t timenow);
|
||||
|
||||
/**
|
||||
* Update information for the host, store that a TCP transaction works.
|
||||
@@ -230,7 +254,7 @@ void infra_update_tcp_works(struct infra_cache* infra,
|
||||
*/
|
||||
int infra_edns_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int edns_version, time_t timenow);
|
||||
int edns_version, uint32_t timenow);
|
||||
|
||||
/**
|
||||
* Get Lameness information and average RTT if host is in the cache.
|
||||
@@ -239,9 +263,11 @@ int infra_edns_update(struct infra_cache* infra,
|
||||
* @param addrlen: length of addr.
|
||||
* @param name: zone name.
|
||||
* @param namelen: zone name length.
|
||||
* @param qtype: the query to be made.
|
||||
* @param lame: if function returns true, this returns lameness of the zone.
|
||||
* @param dnsseclame: if function returns true, this returns if the zone
|
||||
* is dnssec-lame.
|
||||
* @param reclame: if function returns true, this is if it is recursion lame.
|
||||
* @param rtt: if function returns true, this returns avg rtt of the server.
|
||||
* The rtt value is unclamped and reflects recent timeouts.
|
||||
* @param timenow: what time it is now.
|
||||
@@ -249,8 +275,8 @@ int infra_edns_update(struct infra_cache* infra,
|
||||
*/
|
||||
int infra_get_lame_rtt(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* name, size_t namelen, int* lame, int* dnsseclame,
|
||||
int* rtt, time_t timenow);
|
||||
uint8_t* name, size_t namelen, uint16_t qtype,
|
||||
int* lame, int* dnsseclame, int* reclame, int* rtt, uint32_t timenow);
|
||||
|
||||
/**
|
||||
* Get memory used by the infra cache.
|
||||
@@ -267,7 +293,7 @@ size_t infra_host_sizefunc(void* k, void* d);
|
||||
int infra_host_compfunc(void* key1, void* key2);
|
||||
|
||||
/** delete key, and destroy the lock */
|
||||
void infra_host_delkeyfunc(void* k, void* arg, int il);
|
||||
void infra_host_delkeyfunc(void* k, void* arg);
|
||||
|
||||
/** delete data and destroy the lameness hashtable */
|
||||
void infra_host_deldatafunc(void* d, void* arg);
|
||||
@@ -280,7 +306,7 @@ size_t infra_lame_sizefunc(void* k, void* d);
|
||||
int infra_lame_compfunc(void* key1, void* key2);
|
||||
|
||||
/** free key, lock and zonename */
|
||||
void infra_lame_delkeyfunc(void* k, void* arg, int il);
|
||||
void infra_lame_delkeyfunc(void* k, void* arg);
|
||||
|
||||
/** free the lameness data */
|
||||
void infra_lame_deldatafunc(void* d, void* arg);
|
||||
|
||||
Vendored
+48
-10
@@ -47,6 +47,13 @@
|
||||
#include "util/regional.h"
|
||||
#include "util/alloc.h"
|
||||
|
||||
void
|
||||
rrset_markdel(void* key)
|
||||
{
|
||||
struct ub_packed_rrset_key* r = (struct ub_packed_rrset_key*)key;
|
||||
r->id = 0;
|
||||
}
|
||||
|
||||
struct rrset_cache* rrset_cache_create(struct config_file* cfg,
|
||||
struct alloc_cache* alloc)
|
||||
{
|
||||
@@ -57,6 +64,7 @@ struct rrset_cache* rrset_cache_create(struct config_file* cfg,
|
||||
struct rrset_cache *r = (struct rrset_cache*)slabhash_create(slabs,
|
||||
startarray, maxmem, ub_rrset_sizefunc, ub_rrset_compare,
|
||||
ub_rrset_key_delete, rrset_data_delete, alloc);
|
||||
slabhash_setmarkdel(&r->table, &rrset_markdel);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -65,6 +73,7 @@ void rrset_cache_delete(struct rrset_cache* r)
|
||||
if(!r)
|
||||
return;
|
||||
slabhash_delete(&r->table);
|
||||
/* slabhash delete also does free(r), since table is first in struct*/
|
||||
}
|
||||
|
||||
struct rrset_cache* rrset_cache_adjust(struct rrset_cache *r,
|
||||
@@ -115,10 +124,15 @@ need_to_update_rrset(void* nd, void* cd, uint32_t timenow, int equal)
|
||||
{
|
||||
struct packed_rrset_data* newd = (struct packed_rrset_data*)nd;
|
||||
struct packed_rrset_data* cached = (struct packed_rrset_data*)cd;
|
||||
/* o store if rrset has been validated */
|
||||
if( newd->security > cached->security) {
|
||||
/* o store if rrset has been validated
|
||||
* everything better than bogus data
|
||||
* secure is preferred */
|
||||
if( newd->security == sec_status_secure &&
|
||||
cached->security != sec_status_secure)
|
||||
return 1;
|
||||
if( cached->security == sec_status_bogus &&
|
||||
newd->security != sec_status_bogus && !equal)
|
||||
return 1;
|
||||
}
|
||||
/* o if current RRset is more trustworthy - insert it */
|
||||
if( newd->trust > cached->trust ) {
|
||||
/* if the cached rrset is bogus, and this one equal,
|
||||
@@ -300,9 +314,8 @@ rrset_array_unlock_touch(struct rrset_cache* r, struct regional* scratch,
|
||||
|
||||
void
|
||||
rrset_update_sec_status(struct rrset_cache* r,
|
||||
struct ub_packed_rrset_key* rrset)
|
||||
struct ub_packed_rrset_key* rrset, uint32_t now)
|
||||
{
|
||||
uint32_t now = (uint32_t)time(0);
|
||||
struct packed_rrset_data* updata =
|
||||
(struct packed_rrset_data*)rrset->entry.data;
|
||||
struct lruhash_entry* e;
|
||||
@@ -321,18 +334,21 @@ rrset_update_sec_status(struct rrset_cache* r,
|
||||
}
|
||||
/* update the cached rrset */
|
||||
if(updata->security > cachedata->security) {
|
||||
cachedata->trust = updata->trust;
|
||||
size_t i;
|
||||
if(updata->trust > cachedata->trust)
|
||||
cachedata->trust = updata->trust;
|
||||
cachedata->security = updata->security;
|
||||
cachedata->ttl = updata->ttl + now;
|
||||
for(i=0; i<cachedata->count+cachedata->rrsig_count; i++)
|
||||
cachedata->rr_ttl[i] = updata->rr_ttl[i]+now;
|
||||
}
|
||||
lock_rw_unlock(&e->lock);
|
||||
}
|
||||
|
||||
void
|
||||
rrset_check_sec_status(struct rrset_cache* r,
|
||||
struct ub_packed_rrset_key* rrset)
|
||||
struct ub_packed_rrset_key* rrset, uint32_t now)
|
||||
{
|
||||
uint32_t now = (uint32_t)time(0);
|
||||
struct packed_rrset_data* updata =
|
||||
(struct packed_rrset_data*)rrset->entry.data;
|
||||
struct lruhash_entry* e;
|
||||
@@ -351,9 +367,31 @@ rrset_check_sec_status(struct rrset_cache* r,
|
||||
}
|
||||
if(cachedata->security > updata->security) {
|
||||
updata->security = cachedata->security;
|
||||
if(cachedata->security == sec_status_bogus)
|
||||
if(cachedata->security == sec_status_bogus) {
|
||||
size_t i;
|
||||
updata->ttl = cachedata->ttl - now;
|
||||
updata->trust = cachedata->trust;
|
||||
for(i=0; i<cachedata->count+cachedata->rrsig_count; i++)
|
||||
if(cachedata->rr_ttl[i] < now)
|
||||
updata->rr_ttl[i] = 0;
|
||||
else updata->rr_ttl[i] =
|
||||
cachedata->rr_ttl[i]-now;
|
||||
}
|
||||
if(cachedata->trust > updata->trust)
|
||||
updata->trust = cachedata->trust;
|
||||
}
|
||||
lock_rw_unlock(&e->lock);
|
||||
}
|
||||
|
||||
void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen,
|
||||
uint16_t type, uint16_t dclass, uint32_t flags)
|
||||
{
|
||||
struct ub_packed_rrset_key key;
|
||||
key.entry.key = &key;
|
||||
key.rk.dname = nm;
|
||||
key.rk.dname_len = nmlen;
|
||||
key.rk.rrset_class = htons(dclass);
|
||||
key.rk.type = htons(type);
|
||||
key.rk.flags = flags;
|
||||
key.entry.hash = rrset_key_hash(&key.rk);
|
||||
slabhash_remove(&r->table, key.entry.hash, &key);
|
||||
}
|
||||
|
||||
Vendored
+19
-2
@@ -192,9 +192,10 @@ void rrset_array_unlock_touch(struct rrset_cache* r, struct regional* scratch,
|
||||
* @param r: the rrset cache.
|
||||
* @param rrset: which rrset to attempt to update. This rrset is left
|
||||
* untouched. The rrset in the cache is updated in-place.
|
||||
* @param now: current time.
|
||||
*/
|
||||
void rrset_update_sec_status(struct rrset_cache* r,
|
||||
struct ub_packed_rrset_key* rrset);
|
||||
struct ub_packed_rrset_key* rrset, uint32_t now);
|
||||
|
||||
/**
|
||||
* Looks up security status of an rrset. Looks up the rrset.
|
||||
@@ -203,8 +204,24 @@ void rrset_update_sec_status(struct rrset_cache* r,
|
||||
* @param r: the rrset cache.
|
||||
* @param rrset: This rrset may change security status due to the cache.
|
||||
* But its status will only improve, towards secure.
|
||||
* @param now: current time.
|
||||
*/
|
||||
void rrset_check_sec_status(struct rrset_cache* r,
|
||||
struct ub_packed_rrset_key* rrset);
|
||||
struct ub_packed_rrset_key* rrset, uint32_t now);
|
||||
|
||||
/**
|
||||
* Remove an rrset from the cache, by name and type and flags
|
||||
* @param r: rrset cache
|
||||
* @param nm: name of rrset
|
||||
* @param nmlen: length of name
|
||||
* @param type: type of rrset
|
||||
* @param dclass: class of rrset, host order
|
||||
* @param flags: flags of rrset, host order
|
||||
*/
|
||||
void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen,
|
||||
uint16_t type, uint16_t dclass, uint32_t flags);
|
||||
|
||||
/** mark rrset to be deleted, set id=0 */
|
||||
void rrset_markdel(void* key);
|
||||
|
||||
#endif /* SERVICES_CACHE_RRSET_H */
|
||||
|
||||
+266
-63
@@ -49,7 +49,9 @@
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
|
||||
/** number of queued TCP connections for listen() */
|
||||
@@ -86,24 +88,54 @@ verbose_print_addr(struct addrinfo *addr)
|
||||
}
|
||||
|
||||
int
|
||||
create_udp_sock(struct addrinfo *addr)
|
||||
create_udp_sock(int family, int socktype, struct sockaddr* addr,
|
||||
socklen_t addrlen, int v6only, int* inuse, int* noproto)
|
||||
{
|
||||
int s;
|
||||
# if defined(IPV6_V6ONLY)
|
||||
# if defined(IPV6_USE_MIN_MTU)
|
||||
int on=1;
|
||||
# else
|
||||
(void)v6only;
|
||||
# endif
|
||||
verbose_print_addr(addr);
|
||||
if((s = socket(addr->ai_family, addr->ai_socktype, 0)) == -1) {
|
||||
if((s = socket(family, socktype, 0)) == -1) {
|
||||
*inuse = 0;
|
||||
#ifndef USE_WINSOCK
|
||||
if(errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
|
||||
*noproto = 1;
|
||||
return -1;
|
||||
}
|
||||
log_err("can't create socket: %s", strerror(errno));
|
||||
#else
|
||||
if(WSAGetLastError() == WSAEAFNOSUPPORT ||
|
||||
WSAGetLastError() == WSAEPROTONOSUPPORT) {
|
||||
*noproto = 1;
|
||||
return -1;
|
||||
}
|
||||
log_err("can't create socket: %s",
|
||||
wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
*noproto = 0;
|
||||
return -1;
|
||||
}
|
||||
if(addr->ai_family == AF_INET6) {
|
||||
if(family == AF_INET6) {
|
||||
# if defined(IPV6_V6ONLY)
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
&on, (socklen_t)sizeof(on)) < 0) {
|
||||
log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
if(v6only) {
|
||||
int val=(v6only==2)?0:1;
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
(void*)&val, (socklen_t)sizeof(val)) < 0) {
|
||||
#ifndef USE_WINSOCK
|
||||
log_err("setsockopt(..., IPV6_V6ONLY"
|
||||
", ...) failed: %s", strerror(errno));
|
||||
#else
|
||||
log_err("setsockopt(..., IPV6_V6ONLY"
|
||||
", ...) failed: %s",
|
||||
wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
close(s);
|
||||
*noproto = 0;
|
||||
*inuse = 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
# if defined(IPV6_USE_MIN_MTU)
|
||||
@@ -116,72 +148,123 @@ create_udp_sock(struct addrinfo *addr)
|
||||
* network stack supports IPV6_USE_MIN_MTU.
|
||||
*/
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
|
||||
&on, (socklen_t)sizeof(on)) < 0) {
|
||||
(void*)&on, (socklen_t)sizeof(on)) < 0) {
|
||||
#ifndef USE_WINSOCK
|
||||
log_err("setsockopt(..., IPV6_USE_MIN_MTU, "
|
||||
"...) failed: %s", strerror(errno));
|
||||
#else
|
||||
log_err("setsockopt(..., IPV6_USE_MIN_MTU, "
|
||||
"...) failed: %s",
|
||||
wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
close(s);
|
||||
*noproto = 0;
|
||||
*inuse = 0;
|
||||
return -1;
|
||||
}
|
||||
# endif
|
||||
}
|
||||
if(bind(s, (struct sockaddr*)addr->ai_addr, addr->ai_addrlen) != 0) {
|
||||
log_err("can't bind socket: %s", strerror(errno));
|
||||
if(bind(s, (struct sockaddr*)addr, addrlen) != 0) {
|
||||
*noproto = 0;
|
||||
#ifndef USE_WINSOCK
|
||||
#ifdef EADDRINUSE
|
||||
*inuse = (errno == EADDRINUSE);
|
||||
if(errno != EADDRINUSE)
|
||||
log_err("can't bind socket: %s", strerror(errno));
|
||||
#endif
|
||||
#else /* USE_WINSOCK */
|
||||
if(WSAGetLastError() != WSAEADDRINUSE &&
|
||||
WSAGetLastError() != WSAEADDRNOTAVAIL)
|
||||
log_err("can't bind socket: %s",
|
||||
wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
if(!fd_set_nonblock(s))
|
||||
if(!fd_set_nonblock(s)) {
|
||||
*noproto = 0;
|
||||
*inuse = 0;
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and bind TCP listening socket
|
||||
* @param addr: address info ready to make socket.
|
||||
* @return: the socket. -1 on error.
|
||||
*/
|
||||
static int
|
||||
create_tcp_accept_sock(struct addrinfo *addr)
|
||||
int
|
||||
create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto)
|
||||
{
|
||||
int s, flag;
|
||||
int s;
|
||||
#if defined(SO_REUSEADDR) || defined(IPV6_V6ONLY)
|
||||
int on = 1;
|
||||
#endif /* SO_REUSEADDR || IPV6_V6ONLY */
|
||||
verbose_print_addr(addr);
|
||||
*noproto = 0;
|
||||
if((s = socket(addr->ai_family, addr->ai_socktype, 0)) == -1) {
|
||||
#ifndef USE_WINSOCK
|
||||
if(errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
|
||||
*noproto = 1;
|
||||
return -1;
|
||||
}
|
||||
log_err("can't create socket: %s", strerror(errno));
|
||||
#else
|
||||
if(WSAGetLastError() == WSAEAFNOSUPPORT ||
|
||||
WSAGetLastError() == WSAEPROTONOSUPPORT) {
|
||||
*noproto = 1;
|
||||
return -1;
|
||||
}
|
||||
log_err("can't create socket: %s",
|
||||
wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
#ifdef SO_REUSEADDR
|
||||
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on,
|
||||
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
|
||||
(socklen_t)sizeof(on)) < 0) {
|
||||
#ifndef USE_WINSOCK
|
||||
log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
|
||||
strerror(errno));
|
||||
#else
|
||||
log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
|
||||
wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
#endif /* SO_REUSEADDR */
|
||||
#if defined(IPV6_V6ONLY)
|
||||
if(addr->ai_family == AF_INET6) {
|
||||
if(addr->ai_family == AF_INET6 && v6only) {
|
||||
if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
&on, (socklen_t)sizeof(on)) < 0) {
|
||||
(void*)&on, (socklen_t)sizeof(on)) < 0) {
|
||||
#ifndef USE_WINSOCK
|
||||
log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s",
|
||||
strerror(errno));
|
||||
#else
|
||||
log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s",
|
||||
wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)v6only;
|
||||
#endif /* IPV6_V6ONLY */
|
||||
if(bind(s, (struct sockaddr*)addr->ai_addr, addr->ai_addrlen) != 0) {
|
||||
if(bind(s, addr->ai_addr, addr->ai_addrlen) != 0) {
|
||||
#ifndef USE_WINSOCK
|
||||
log_err("can't bind socket: %s", strerror(errno));
|
||||
#else
|
||||
log_err("can't bind socket: %s",
|
||||
wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
if((flag = fcntl(s, F_GETFL)) == -1) {
|
||||
log_err("can't fcntl F_GETFL: %s", strerror(errno));
|
||||
flag = 0;
|
||||
}
|
||||
flag |= O_NONBLOCK;
|
||||
if(fcntl(s, F_SETFL, flag) == -1) {
|
||||
log_err("can't fcntl F_SETFL: %s", strerror(errno));
|
||||
if(!fd_set_nonblock(s)) {
|
||||
return -1;
|
||||
}
|
||||
if(listen(s, TCP_BACKLOG) == -1) {
|
||||
#ifndef USE_WINSOCK
|
||||
log_err("can't listen: %s", strerror(errno));
|
||||
#else
|
||||
log_err("can't listen: %s", wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
return s;
|
||||
@@ -192,20 +275,39 @@ create_tcp_accept_sock(struct addrinfo *addr)
|
||||
*/
|
||||
static int
|
||||
make_sock(int stype, const char* ifname, const char* port,
|
||||
struct addrinfo *hints)
|
||||
struct addrinfo *hints, int v6only, int* noip6)
|
||||
{
|
||||
struct addrinfo *res = NULL;
|
||||
int r, s;
|
||||
int r, s, inuse, noproto;
|
||||
hints->ai_socktype = stype;
|
||||
*noip6 = 0;
|
||||
if((r=getaddrinfo(ifname, port, hints, &res)) != 0 || !res) {
|
||||
log_err("node %s:%s getaddrinfo: %s %s",
|
||||
ifname?ifname:"default", port, gai_strerror(r),
|
||||
r==EAI_SYSTEM?(char*)strerror(errno):"");
|
||||
#ifdef EAI_SYSTEM
|
||||
r==EAI_SYSTEM?(char*)strerror(errno):""
|
||||
#else
|
||||
""
|
||||
#endif
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
if(stype == SOCK_DGRAM)
|
||||
s = create_udp_sock(res);
|
||||
else s = create_tcp_accept_sock(res);
|
||||
if(stype == SOCK_DGRAM) {
|
||||
verbose_print_addr(res);
|
||||
s = create_udp_sock(res->ai_family, res->ai_socktype,
|
||||
(struct sockaddr*)res->ai_addr,
|
||||
res->ai_addrlen, v6only, &inuse, &noproto);
|
||||
if(s == -1 && inuse) {
|
||||
log_err("bind: address already in use");
|
||||
} else if(s == -1 && noproto && hints->ai_family == AF_INET6){
|
||||
*noip6 = 1;
|
||||
}
|
||||
} else {
|
||||
s = create_tcp_accept_sock(res, v6only, &noproto);
|
||||
if(s == -1 && noproto && hints->ai_family == AF_INET6){
|
||||
*noip6 = 1;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(res);
|
||||
return s;
|
||||
}
|
||||
@@ -214,11 +316,11 @@ make_sock(int stype, const char* ifname, const char* port,
|
||||
* Add port to open ports list.
|
||||
* @param list: list head. changed.
|
||||
* @param s: fd.
|
||||
* @param is_udp: if fd is UDP.
|
||||
* @param ftype: if fd is UDP.
|
||||
* @return false on failure. list in unchanged then.
|
||||
*/
|
||||
static int
|
||||
port_insert(struct listen_port** list, int s, int is_udp)
|
||||
port_insert(struct listen_port** list, int s, enum listen_type ftype)
|
||||
{
|
||||
struct listen_port* item = (struct listen_port*)malloc(
|
||||
sizeof(struct listen_port));
|
||||
@@ -226,14 +328,71 @@ port_insert(struct listen_port** list, int s, int is_udp)
|
||||
return 0;
|
||||
item->next = *list;
|
||||
item->fd = s;
|
||||
item->is_udp = is_udp;
|
||||
item->ftype = ftype;
|
||||
*list = item;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** set fd to receive source address packet info */
|
||||
static int
|
||||
set_recvpktinfo(int s, int family)
|
||||
{
|
||||
#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) || defined(IP_RECVDSTADDR) || defined(IP_PKTINFO)
|
||||
int on = 1;
|
||||
#else
|
||||
(void)s;
|
||||
#endif
|
||||
if(family == AF_INET6) {
|
||||
# ifdef IPV6_RECVPKTINFO
|
||||
if(setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
|
||||
(void*)&on, (socklen_t)sizeof(on)) < 0) {
|
||||
log_err("setsockopt(..., IPV6_RECVPKTINFO, ...) failed: %s",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
# elif defined(IPV6_PKTINFO)
|
||||
if(setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO,
|
||||
(void*)&on, (socklen_t)sizeof(on)) < 0) {
|
||||
log_err("setsockopt(..., IPV6_PKTINFO, ...) failed: %s",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
# else
|
||||
log_err("no IPV6_RECVPKTINFO and no IPV6_PKTINFO option, please "
|
||||
"disable interface-automatic in config");
|
||||
return 0;
|
||||
# endif /* defined IPV6_RECVPKTINFO */
|
||||
|
||||
} else if(family == AF_INET) {
|
||||
# ifdef IP_RECVDSTADDR
|
||||
if(setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR,
|
||||
(void*)&on, (socklen_t)sizeof(on)) < 0) {
|
||||
log_err("setsockopt(..., IP_RECVDSTADDR, ...) failed: %s",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
# elif defined(IP_PKTINFO)
|
||||
if(setsockopt(s, IPPROTO_IP, IP_PKTINFO,
|
||||
(void*)&on, (socklen_t)sizeof(on)) < 0) {
|
||||
log_err("setsockopt(..., IP_PKTINFO, ...) failed: %s",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
# else
|
||||
log_err("no IP_RECVDSTADDR or IP_PKTINFO option, please disable "
|
||||
"interface-automatic in config");
|
||||
return 0;
|
||||
# endif /* IP_PKTINFO */
|
||||
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for ports_open. Creates one interface (or NULL for default).
|
||||
* @param ifname: The interface ip address.
|
||||
* @param do_auto: use automatic interface detection.
|
||||
* If enabled, then ifname must be the wildcard name.
|
||||
* @param do_udp: if udp should be used.
|
||||
* @param do_tcp: if udp should be used.
|
||||
* @param hints: for getaddrinfo. family and flags have to be set by caller.
|
||||
@@ -242,25 +401,53 @@ port_insert(struct listen_port** list, int s, int is_udp)
|
||||
* @return: returns false on error.
|
||||
*/
|
||||
static int
|
||||
ports_create_if(const char* ifname, int do_udp, int do_tcp,
|
||||
ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
|
||||
struct addrinfo *hints, const char* port, struct listen_port** list)
|
||||
{
|
||||
int s;
|
||||
int s, noip6=0;
|
||||
if(!do_udp && !do_tcp)
|
||||
return 0;
|
||||
if(do_udp) {
|
||||
if((s = make_sock(SOCK_DGRAM, ifname, port, hints)) == -1)
|
||||
if(do_auto) {
|
||||
if((s = make_sock(SOCK_DGRAM, ifname, port, hints, 1,
|
||||
&noip6)) == -1) {
|
||||
if(noip6) {
|
||||
log_warn("IPv6 protocol not available");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
if(!port_insert(list, s, 1)) {
|
||||
}
|
||||
/* getting source addr packet info is highly non-portable */
|
||||
if(!set_recvpktinfo(s, hints->ai_family))
|
||||
return 0;
|
||||
if(!port_insert(list, s, listen_type_udpancil)) {
|
||||
close(s);
|
||||
return 0;
|
||||
}
|
||||
} else if(do_udp) {
|
||||
/* regular udp socket */
|
||||
if((s = make_sock(SOCK_DGRAM, ifname, port, hints, 1,
|
||||
&noip6)) == -1) {
|
||||
if(noip6) {
|
||||
log_warn("IPv6 protocol not available");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if(!port_insert(list, s, listen_type_udp)) {
|
||||
close(s);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if(do_tcp) {
|
||||
if((s = make_sock(SOCK_STREAM, ifname, port, hints)) == -1) {
|
||||
if((s = make_sock(SOCK_STREAM, ifname, port, hints, 1,
|
||||
&noip6)) == -1) {
|
||||
if(noip6) {
|
||||
/*log_warn("IPv6 protocol not available");*/
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if(!port_insert(list, s, 0)) {
|
||||
if(!port_insert(list, s, listen_type_tcp)) {
|
||||
close(s);
|
||||
return 0;
|
||||
}
|
||||
@@ -306,11 +493,15 @@ listen_create(struct comm_base* base, struct listen_port* ports,
|
||||
/* create comm points as needed */
|
||||
while(ports) {
|
||||
struct comm_point* cp = NULL;
|
||||
if(ports->is_udp)
|
||||
if(ports->ftype == listen_type_udp)
|
||||
cp = comm_point_create_udp(base, ports->fd,
|
||||
front->udp_buff, cb, cb_arg);
|
||||
else cp = comm_point_create_tcp(base, ports->fd,
|
||||
else if(ports->ftype == listen_type_tcp)
|
||||
cp = comm_point_create_tcp(base, ports->fd,
|
||||
tcp_accept_count, bufsize, cb, cb_arg);
|
||||
else if(ports->ftype == listen_type_udpancil)
|
||||
cp = comm_point_create_udp_ancil(base, ports->fd,
|
||||
front->udp_buff, cb, cb_arg);
|
||||
if(!cp) {
|
||||
log_err("can't create commpoint");
|
||||
listen_delete(front);
|
||||
@@ -334,19 +525,24 @@ listen_create(struct comm_base* base, struct listen_port* ports,
|
||||
return front;
|
||||
}
|
||||
|
||||
void
|
||||
listen_delete(struct listen_dnsport* front)
|
||||
void
|
||||
listen_list_delete(struct listen_list* list)
|
||||
{
|
||||
struct listen_list *p, *pn;
|
||||
if(!front)
|
||||
return;
|
||||
p = front->cps;
|
||||
struct listen_list *p = list, *pn;
|
||||
while(p) {
|
||||
pn = p->next;
|
||||
comm_point_delete(p->com);
|
||||
free(p);
|
||||
p = pn;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
listen_delete(struct listen_dnsport* front)
|
||||
{
|
||||
if(!front)
|
||||
return;
|
||||
listen_list_delete(front->cps);
|
||||
ldns_buffer_free(front->udp_buff);
|
||||
free(front);
|
||||
}
|
||||
@@ -383,12 +579,13 @@ listening_ports_open(struct config_file* cfg)
|
||||
struct listen_port* list = NULL;
|
||||
struct addrinfo hints;
|
||||
int i, do_ip4, do_ip6;
|
||||
int do_tcp;
|
||||
int do_tcp, do_auto;
|
||||
char portbuf[32];
|
||||
snprintf(portbuf, sizeof(portbuf), "%d", cfg->port);
|
||||
do_ip4 = cfg->do_ip4;
|
||||
do_ip6 = cfg->do_ip6;
|
||||
do_tcp = cfg->do_tcp;
|
||||
do_auto = cfg->if_automatic && cfg->do_udp;
|
||||
if(cfg->incoming_num_tcp == 0)
|
||||
do_tcp = 0;
|
||||
|
||||
@@ -405,11 +602,16 @@ listening_ports_open(struct config_file* cfg)
|
||||
if(!do_ip4 && !do_ip6) {
|
||||
return NULL;
|
||||
}
|
||||
if(do_auto && (!do_ip4 || !do_ip6)) {
|
||||
log_warn("interface_automatic option does not work when either do-ip4 or do-ip6 is not enabled. Disabling option.");
|
||||
do_auto = 0;
|
||||
}
|
||||
/* create ip4 and ip6 ports so that return addresses are nice. */
|
||||
if(cfg->num_ifs == 0) {
|
||||
if(do_auto || cfg->num_ifs == 0) {
|
||||
if(do_ip6) {
|
||||
hints.ai_family = AF_INET6;
|
||||
if(!ports_create_if("::1", cfg->do_udp, do_tcp,
|
||||
if(!ports_create_if(do_auto?"::0":"::1",
|
||||
do_auto, cfg->do_udp, do_tcp,
|
||||
&hints, portbuf, &list)) {
|
||||
listening_ports_free(list);
|
||||
return NULL;
|
||||
@@ -417,7 +619,8 @@ listening_ports_open(struct config_file* cfg)
|
||||
}
|
||||
if(do_ip4) {
|
||||
hints.ai_family = AF_INET;
|
||||
if(!ports_create_if("127.0.0.1", cfg->do_udp, do_tcp,
|
||||
if(!ports_create_if(do_auto?"0.0.0.0":"127.0.0.1",
|
||||
do_auto, cfg->do_udp, do_tcp,
|
||||
&hints, portbuf, &list)) {
|
||||
listening_ports_free(list);
|
||||
return NULL;
|
||||
@@ -428,7 +631,7 @@ listening_ports_open(struct config_file* cfg)
|
||||
if(!do_ip6)
|
||||
continue;
|
||||
hints.ai_family = AF_INET6;
|
||||
if(!ports_create_if(cfg->ifs[i], cfg->do_udp,
|
||||
if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp,
|
||||
do_tcp, &hints, portbuf, &list)) {
|
||||
listening_ports_free(list);
|
||||
return NULL;
|
||||
@@ -437,7 +640,7 @@ listening_ports_open(struct config_file* cfg)
|
||||
if(!do_ip4)
|
||||
continue;
|
||||
hints.ai_family = AF_INET;
|
||||
if(!ports_create_if(cfg->ifs[i], cfg->do_udp,
|
||||
if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp,
|
||||
do_tcp, &hints, portbuf, &list)) {
|
||||
listening_ports_free(list);
|
||||
return NULL;
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
#include "config.h"
|
||||
#include "util/netevent.h"
|
||||
struct listen_list;
|
||||
struct addrinfo;
|
||||
struct config_file;
|
||||
struct addrinfo;
|
||||
|
||||
/**
|
||||
* Listening for queries structure.
|
||||
@@ -74,6 +74,18 @@ struct listen_list {
|
||||
struct comm_point* com;
|
||||
};
|
||||
|
||||
/**
|
||||
* type of ports
|
||||
*/
|
||||
enum listen_type {
|
||||
/** udp type */
|
||||
listen_type_udp,
|
||||
/** tcp type */
|
||||
listen_type_tcp,
|
||||
/** udp ipv6 (v4mapped) for use with ancillary data */
|
||||
listen_type_udpancil
|
||||
};
|
||||
|
||||
/**
|
||||
* Single linked list to store shared ports that have been
|
||||
* opened for use by all threads.
|
||||
@@ -84,7 +96,7 @@ struct listen_port {
|
||||
/** file descriptor, open and ready for use */
|
||||
int fd;
|
||||
/** type of file descriptor, udp or tcp */
|
||||
int is_udp;
|
||||
enum listen_type ftype;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -144,6 +156,13 @@ void listen_resume(struct listen_dnsport* listen);
|
||||
*/
|
||||
void listen_delete(struct listen_dnsport* listen);
|
||||
|
||||
/**
|
||||
* delete listen_list of commpoints. Calls commpointdelete() on items.
|
||||
* This may close the fds or not depending on flags.
|
||||
* @param list: to delete.
|
||||
*/
|
||||
void listen_list_delete(struct listen_list* list);
|
||||
|
||||
/**
|
||||
* get memory size used by the listening structs
|
||||
* @param listen: listening structure.
|
||||
@@ -153,9 +172,27 @@ size_t listen_get_mem(struct listen_dnsport* listen);
|
||||
|
||||
/**
|
||||
* Create and bind nonblocking UDP socket
|
||||
* @param addr: address info ready to make socket.
|
||||
* @param family: for socket call.
|
||||
* @param socktype: for socket call.
|
||||
* @param addr: for bind call.
|
||||
* @param addrlen: for bind call.
|
||||
* @param v6only: if enabled, IP6 sockets get IP6ONLY option set.
|
||||
* if enabled with value 2 IP6ONLY option is disabled.
|
||||
* @param inuse: on error, this is set true if the port was in use.
|
||||
* @param noproto: on error, this is set true if cause is that the
|
||||
IPv6 proto (family) is not available.
|
||||
* @return: the socket. -1 on error.
|
||||
*/
|
||||
int create_udp_sock(struct addrinfo* addr);
|
||||
int create_udp_sock(int family, int socktype, struct sockaddr* addr,
|
||||
socklen_t addrlen, int v6only, int* inuse, int* noproto);
|
||||
|
||||
/**
|
||||
* Create and bind TCP listening socket
|
||||
* @param addr: address info ready to make socket.
|
||||
* @param v6only: enable ip6 only flag on ip6 sockets.
|
||||
* @param noproto: if error caused by lack of protocol support.
|
||||
* @return: the socket. -1 on error.
|
||||
*/
|
||||
int create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto);
|
||||
|
||||
#endif /* LISTEN_DNSPORT_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* services/localzone.h - local zones authority service.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to enable local zone authority service.
|
||||
*/
|
||||
|
||||
#ifndef SERVICES_LOCALZONE_H
|
||||
#define SERVICES_LOCALZONE_H
|
||||
#include "util/rbtree.h"
|
||||
#include "util/locks.h"
|
||||
struct ub_packed_rrset_key;
|
||||
struct regional;
|
||||
struct config_file;
|
||||
struct edns_data;
|
||||
struct query_info;
|
||||
|
||||
/**
|
||||
* Local zone type
|
||||
* This type determines processing for queries that did not match
|
||||
* local-data directly.
|
||||
*/
|
||||
enum localzone_type {
|
||||
/** drop query */
|
||||
local_zone_deny = 0,
|
||||
/** answer with error */
|
||||
local_zone_refuse,
|
||||
/** answer nxdomain or nodata */
|
||||
local_zone_static,
|
||||
/** resolve normally */
|
||||
local_zone_transparent,
|
||||
/** answer with data at zone apex */
|
||||
local_zone_redirect,
|
||||
/** remove default AS112 blocking contents for zone
|
||||
* nodefault is used in config not during service. */
|
||||
local_zone_nodefault
|
||||
};
|
||||
|
||||
/**
|
||||
* Authoritative local zones storage, shared.
|
||||
*/
|
||||
struct local_zones {
|
||||
/** lock on the localzone tree */
|
||||
lock_quick_t lock;
|
||||
/** rbtree of struct local_zone */
|
||||
rbtree_t ztree;
|
||||
};
|
||||
|
||||
/**
|
||||
* Local zone. A locally served authoritative zone.
|
||||
*/
|
||||
struct local_zone {
|
||||
/** rbtree node, key is name and class */
|
||||
rbnode_t node;
|
||||
/** parent zone, if any. */
|
||||
struct local_zone* parent;
|
||||
|
||||
/** zone name, in uncompressed wireformat */
|
||||
uint8_t* name;
|
||||
/** length of zone name */
|
||||
size_t namelen;
|
||||
/** number of labels in zone name */
|
||||
int namelabs;
|
||||
/** the class of this zone.
|
||||
* uses 'dclass' to not conflict with c++ keyword class. */
|
||||
uint16_t dclass;
|
||||
|
||||
/** lock on the data in the structure
|
||||
* For the node, parent, name, namelen, namelabs, dclass, you
|
||||
* need to also hold the zones_tree lock to change them (or to
|
||||
* delete this zone) */
|
||||
lock_rw_t lock;
|
||||
|
||||
/** how to process zone */
|
||||
enum localzone_type type;
|
||||
|
||||
/** in this region the zone's data is allocated.
|
||||
* the struct local_zone itself is malloced. */
|
||||
struct regional* region;
|
||||
/** local data for this zone
|
||||
* rbtree of struct local_data */
|
||||
rbtree_t data;
|
||||
/** if data contains zone apex SOA data, this is a ptr to it. */
|
||||
struct ub_packed_rrset_key* soa;
|
||||
};
|
||||
|
||||
/**
|
||||
* Local data. One domain name, and the RRs to go with it.
|
||||
*/
|
||||
struct local_data {
|
||||
/** rbtree node, key is name only */
|
||||
rbnode_t node;
|
||||
/** domain name */
|
||||
uint8_t* name;
|
||||
/** length of name */
|
||||
size_t namelen;
|
||||
/** number of labels in name */
|
||||
int namelabs;
|
||||
/** the data rrsets, with different types, linked list.
|
||||
* If this list is NULL, the node is an empty non-terminal. */
|
||||
struct local_rrset* rrsets;
|
||||
};
|
||||
|
||||
/**
|
||||
* A local data RRset
|
||||
*/
|
||||
struct local_rrset {
|
||||
/** next in list */
|
||||
struct local_rrset* next;
|
||||
/** RRset data item */
|
||||
struct ub_packed_rrset_key* rrset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create local zones storage
|
||||
* @return new struct or NULL on error.
|
||||
*/
|
||||
struct local_zones* local_zones_create();
|
||||
|
||||
/**
|
||||
* Delete local zones storage
|
||||
* @param zones: to delete.
|
||||
*/
|
||||
void local_zones_delete(struct local_zones* zones);
|
||||
|
||||
/**
|
||||
* Apply config settings; setup the local authoritative data.
|
||||
* Takes care of locking.
|
||||
* @param zones: is set up.
|
||||
* @param cfg: config data.
|
||||
* @return false on error.
|
||||
*/
|
||||
int local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Compare two local_zone entries in rbtree. Sort hierarchical but not
|
||||
* canonical
|
||||
* @param z1: zone 1
|
||||
* @param z2: zone 2
|
||||
* @return: -1, 0, +1 comparison value.
|
||||
*/
|
||||
int local_zone_cmp(const void* z1, const void* z2);
|
||||
|
||||
/**
|
||||
* Compare two local_data entries in rbtree. Sort canonical.
|
||||
* @param d1: data 1
|
||||
* @param d2: data 2
|
||||
* @return: -1, 0, +1 comparison value.
|
||||
*/
|
||||
int local_data_cmp(const void* d1, const void* d2);
|
||||
|
||||
/**
|
||||
* Delete one zone
|
||||
* @param z: to delete.
|
||||
*/
|
||||
void local_zone_delete(struct local_zone* z);
|
||||
|
||||
/**
|
||||
* Lookup zone that contains the given name, class.
|
||||
* User must lock the tree or result zone.
|
||||
* @param zones: the zones tree
|
||||
* @param name: dname to lookup
|
||||
* @param len: length of name.
|
||||
* @param labs: labelcount of name.
|
||||
* @param dclass: class to lookup.
|
||||
* @return closest local_zone or NULL if no covering zone is found.
|
||||
*/
|
||||
struct local_zone* local_zones_lookup(struct local_zones* zones,
|
||||
uint8_t* name, size_t len, int labs, uint16_t dclass);
|
||||
|
||||
/**
|
||||
* Debug helper. Print all zones
|
||||
* Takes care of locking.
|
||||
* @param zones: the zones tree
|
||||
*/
|
||||
void local_zones_print(struct local_zones* zones);
|
||||
|
||||
/**
|
||||
* Answer authoritatively for local zones.
|
||||
* Takes care of locking.
|
||||
* @param zones: the stored zones (shared, read only).
|
||||
* @param qinfo: query info (parsed).
|
||||
* @param edns: edns info (parsed).
|
||||
* @param buf: buffer with query ID and flags, also for reply.
|
||||
* @param temp: temporary storage region.
|
||||
* @return true if answer is in buffer. false if query is not answered
|
||||
* by authority data. If the reply should be dropped altogether, the return
|
||||
* value is true, but the buffer is cleared (empty).
|
||||
*/
|
||||
int local_zones_answer(struct local_zones* zones, struct query_info* qinfo,
|
||||
struct edns_data* edns, ldns_buffer* buf, struct regional* temp);
|
||||
|
||||
/**
|
||||
* Parse the string into localzone type.
|
||||
*
|
||||
* @param str: string to parse
|
||||
* @param t: local zone type returned here.
|
||||
* @return 0 on parse error.
|
||||
*/
|
||||
int local_zone_str2type(const char* str, enum localzone_type* t);
|
||||
|
||||
/**
|
||||
* Find zone that with exactly given name, class.
|
||||
* User must lock the tree or result zone.
|
||||
* @param zones: the zones tree
|
||||
* @param name: dname to lookup
|
||||
* @param len: length of name.
|
||||
* @param labs: labelcount of name.
|
||||
* @param dclass: class to lookup.
|
||||
* @return the exact local_zone or NULL.
|
||||
*/
|
||||
struct local_zone* local_zones_find(struct local_zones* zones,
|
||||
uint8_t* name, size_t len, int labs, uint16_t dclass);
|
||||
|
||||
/**
|
||||
* Add a new zone. Caller must hold the zones lock.
|
||||
* Adjusts the other zones as well (parent pointers) after insertion.
|
||||
* The zone must NOT exist (returns NULL and logs error).
|
||||
* @param zones: the zones tree
|
||||
* @param name: dname to add
|
||||
* @param len: length of name.
|
||||
* @param labs: labelcount of name.
|
||||
* @param dclass: class to add.
|
||||
* @param tp: type.
|
||||
* @return local_zone or NULL on error, caller must printout memory error.
|
||||
*/
|
||||
struct local_zone* local_zones_add_zone(struct local_zones* zones,
|
||||
uint8_t* name, size_t len, int labs, uint16_t dclass,
|
||||
enum localzone_type tp);
|
||||
|
||||
/**
|
||||
* Delete a zone. Caller must hold the zones lock.
|
||||
* Adjusts the other zones as well (parent pointers) after insertion.
|
||||
* @param zones: the zones tree
|
||||
* @param zone: the zone to delete from tree. Also deletes zone from memory.
|
||||
*/
|
||||
void local_zones_del_zone(struct local_zones* zones, struct local_zone* zone);
|
||||
|
||||
/**
|
||||
* Add RR data into the localzone data.
|
||||
* Looks up the zone, if no covering zone, a transparent zone with the
|
||||
* name of the RR is created.
|
||||
* @param zones: the zones tree. Not locked by caller.
|
||||
* @param rr: string with on RR.
|
||||
* @param buf: buffer for scratch.
|
||||
* @return false on failure.
|
||||
*/
|
||||
int local_zones_add_RR(struct local_zones* zones, const char* rr,
|
||||
ldns_buffer* buf);
|
||||
|
||||
/**
|
||||
* Remove data from domain name in the tree.
|
||||
* All types are removed. No effect if zone or name does not exist.
|
||||
* @param zones: zones tree.
|
||||
* @param name: dname to remove
|
||||
* @param len: length of name.
|
||||
* @param labs: labelcount of name.
|
||||
* @param dclass: class to remove.
|
||||
*/
|
||||
void local_zones_del_data(struct local_zones* zones,
|
||||
uint8_t* name, size_t len, int labs, uint16_t dclass);
|
||||
|
||||
#endif /* SERVICES_LOCALZONE_H */
|
||||
+398
-100
@@ -54,6 +54,71 @@
|
||||
#include "util/timehist.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "util/alloc.h"
|
||||
#include "util/config_file.h"
|
||||
|
||||
/** subtract timers and the values do not overflow or become negative */
|
||||
static void
|
||||
timeval_subtract(struct timeval* d, const struct timeval* end, const struct timeval* start)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
time_t end_usec = end->tv_usec;;
|
||||
d->tv_sec = end->tv_sec - start->tv_sec;
|
||||
while(end_usec < start->tv_usec) {
|
||||
end_usec += 1000000;
|
||||
d->tv_sec--;
|
||||
}
|
||||
d->tv_usec = end_usec - start->tv_usec;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** add timers and the values do not overflow or become negative */
|
||||
static void
|
||||
timeval_add(struct timeval* d, const struct timeval* add)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
d->tv_sec += add->tv_sec;
|
||||
d->tv_usec += add->tv_usec;
|
||||
while(d->tv_usec > 1000000 ) {
|
||||
d->tv_usec -= 1000000;
|
||||
d->tv_sec++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** divide sum of timers to get average */
|
||||
static void
|
||||
timeval_divide(struct timeval* avg, const struct timeval* sum, size_t d)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
size_t leftover;
|
||||
if(d == 0) {
|
||||
avg->tv_sec = 0;
|
||||
avg->tv_usec = 0;
|
||||
return;
|
||||
}
|
||||
avg->tv_sec = sum->tv_sec / d;
|
||||
avg->tv_usec = sum->tv_usec / d;
|
||||
/* handle fraction from seconds divide */
|
||||
leftover = sum->tv_sec - avg->tv_sec*d;
|
||||
avg->tv_usec += (leftover*1000000)/d;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** histogram compare of time values */
|
||||
static int
|
||||
timeval_smaller(const struct timeval* x, const struct timeval* y)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
if(x->tv_sec < y->tv_sec)
|
||||
return 1;
|
||||
else if(x->tv_sec == y->tv_sec) {
|
||||
if(x->tv_usec <= y->tv_usec)
|
||||
return 1;
|
||||
else return 0;
|
||||
}
|
||||
else return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
mesh_state_compare(const void* ap, const void* bp)
|
||||
@@ -88,8 +153,7 @@ mesh_state_ref_compare(const void* ap, const void* bp)
|
||||
}
|
||||
|
||||
struct mesh_area*
|
||||
mesh_create(int num_modules, struct module_func_block** modfunc,
|
||||
struct module_env* env)
|
||||
mesh_create(struct module_stack* stack, struct module_env* env)
|
||||
{
|
||||
struct mesh_area* mesh = calloc(1, sizeof(struct mesh_area));
|
||||
if(!mesh) {
|
||||
@@ -102,14 +166,23 @@ mesh_create(int num_modules, struct module_func_block** modfunc,
|
||||
log_err("mesh area alloc: out of memory");
|
||||
return NULL;
|
||||
}
|
||||
mesh->num_modules = num_modules;
|
||||
mesh->modfunc = modfunc;
|
||||
mesh->mods = *stack;
|
||||
mesh->env = env;
|
||||
rbtree_init(&mesh->run, &mesh_state_compare);
|
||||
rbtree_init(&mesh->all, &mesh_state_compare);
|
||||
mesh->num_reply_addrs = 0;
|
||||
mesh->num_reply_states = 0;
|
||||
mesh->num_detached_states = 0;
|
||||
mesh->num_forever_states = 0;
|
||||
mesh->stats_jostled = 0;
|
||||
mesh->stats_dropped = 0;
|
||||
mesh->max_reply_states = env->cfg->num_queries_per_thread;
|
||||
mesh->max_forever_states = (mesh->max_reply_states+1)/2;
|
||||
#ifndef S_SPLINT_S
|
||||
mesh->jostle_max.tv_sec = (time_t)(env->cfg->jostle_time / 1000);
|
||||
mesh->jostle_max.tv_usec = (time_t)((env->cfg->jostle_time % 1000)
|
||||
*1000);
|
||||
#endif
|
||||
return mesh;
|
||||
}
|
||||
|
||||
@@ -132,6 +205,40 @@ mesh_delete(struct mesh_area* mesh)
|
||||
free(mesh);
|
||||
}
|
||||
|
||||
int mesh_make_new_space(struct mesh_area* mesh)
|
||||
{
|
||||
struct mesh_state* m = mesh->jostle_last;
|
||||
/* free space is available */
|
||||
if(mesh->num_reply_states < mesh->max_reply_states)
|
||||
return 1;
|
||||
/* try to kick out a jostle-list item */
|
||||
if(m && m->reply_list && m->list_select == mesh_jostle_list) {
|
||||
/* how old is it? */
|
||||
struct timeval age;
|
||||
timeval_subtract(&age, mesh->env->now_tv,
|
||||
&m->reply_list->start_time);
|
||||
if(timeval_smaller(&mesh->jostle_max, &age)) {
|
||||
/* its a goner */
|
||||
log_nametypeclass(VERB_ALGO, "query jostled out to "
|
||||
"make space for a new one",
|
||||
m->s.qinfo.qname, m->s.qinfo.qtype,
|
||||
m->s.qinfo.qclass);
|
||||
/* notify supers */
|
||||
if(m->super_set.count > 0) {
|
||||
verbose(VERB_ALGO, "notify supers of failure");
|
||||
m->s.return_msg = NULL;
|
||||
m->s.return_rcode = LDNS_RCODE_SERVFAIL;
|
||||
mesh_walk_supers(mesh, m);
|
||||
}
|
||||
mesh->stats_jostled ++;
|
||||
mesh_state_delete(&m->s);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* no space for new item */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
uint16_t qflags, struct edns_data* edns, struct comm_reply* rep,
|
||||
uint16_t qid)
|
||||
@@ -140,6 +247,16 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
int was_detached = 0;
|
||||
int was_noreply = 0;
|
||||
int added = 0;
|
||||
/* does this create a new reply state? */
|
||||
if(!s || s->list_select == mesh_no_list) {
|
||||
if(!mesh_make_new_space(mesh)) {
|
||||
verbose(VERB_ALGO, "Too many queries. dropping "
|
||||
"incoming query.");
|
||||
comm_point_drop_reply(rep);
|
||||
mesh->stats_dropped ++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* see if it already exists, if not, create one */
|
||||
if(!s) {
|
||||
struct rbnode_t* n;
|
||||
@@ -157,12 +274,12 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
mesh->num_detached_states++;
|
||||
added = 1;
|
||||
}
|
||||
if(!s->reply_list && s->super_set.count == 0)
|
||||
if(!s->reply_list && !s->cb_list && s->super_set.count == 0)
|
||||
was_detached = 1;
|
||||
if(!s->reply_list)
|
||||
if(!s->reply_list && !s->cb_list)
|
||||
was_noreply = 1;
|
||||
/* add reply to s */
|
||||
if(!mesh_state_add_reply(s, edns, rep, qid, qflags)) {
|
||||
if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo->qname)) {
|
||||
log_err("mesh_new_client: out of memory; SERVFAIL");
|
||||
error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
qinfo, qid, qflags, edns);
|
||||
@@ -180,16 +297,82 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
mesh->num_reply_states ++;
|
||||
}
|
||||
mesh->num_reply_addrs++;
|
||||
if(s->list_select == mesh_no_list) {
|
||||
/* move to either the forever or the jostle_list */
|
||||
if(mesh->num_forever_states < mesh->max_forever_states) {
|
||||
mesh->num_forever_states ++;
|
||||
mesh_list_insert(s, &mesh->forever_first,
|
||||
&mesh->forever_last);
|
||||
s->list_select = mesh_forever_list;
|
||||
} else {
|
||||
mesh_list_insert(s, &mesh->jostle_first,
|
||||
&mesh->jostle_last);
|
||||
s->list_select = mesh_jostle_list;
|
||||
}
|
||||
}
|
||||
if(added)
|
||||
mesh_run(mesh, s, module_event_new, NULL);
|
||||
}
|
||||
|
||||
void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
|
||||
int is_ok, struct comm_reply* reply)
|
||||
int
|
||||
mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
uint16_t qflags, struct edns_data* edns, ldns_buffer* buf,
|
||||
uint16_t qid, mesh_cb_func_t cb, void* cb_arg)
|
||||
{
|
||||
struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags, 0);
|
||||
int was_detached = 0;
|
||||
int was_noreply = 0;
|
||||
int added = 0;
|
||||
/* there are no limits on the number of callbacks */
|
||||
|
||||
/* see if it already exists, if not, create one */
|
||||
if(!s) {
|
||||
struct rbnode_t* n;
|
||||
s = mesh_state_create(mesh->env,qinfo, qflags, 0);
|
||||
if(!s) {
|
||||
return 0;
|
||||
}
|
||||
n = rbtree_insert(&mesh->all, &s->node);
|
||||
log_assert(n != NULL);
|
||||
/* set detached (it is now) */
|
||||
mesh->num_detached_states++;
|
||||
added = 1;
|
||||
}
|
||||
if(!s->reply_list && !s->cb_list && s->super_set.count == 0)
|
||||
was_detached = 1;
|
||||
if(!s->reply_list && !s->cb_list)
|
||||
was_noreply = 1;
|
||||
/* add reply to s */
|
||||
if(!mesh_state_add_cb(s, edns, buf, cb, cb_arg, qid, qflags)) {
|
||||
if(added)
|
||||
mesh_state_delete(&s->s);
|
||||
return 0;
|
||||
}
|
||||
/* update statistics */
|
||||
if(was_detached) {
|
||||
log_assert(mesh->num_detached_states > 0);
|
||||
mesh->num_detached_states--;
|
||||
}
|
||||
if(was_noreply) {
|
||||
mesh->num_reply_states ++;
|
||||
}
|
||||
mesh->num_reply_addrs++;
|
||||
if(added)
|
||||
mesh_run(mesh, s, module_event_new, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
|
||||
struct comm_reply* reply, int what)
|
||||
{
|
||||
enum module_ev event = module_event_reply;
|
||||
e->qstate->reply = reply;
|
||||
mesh_run(mesh, e->qstate->mesh_info,
|
||||
is_ok?module_event_reply:module_event_noreply, e);
|
||||
if(what != NETEVENT_NOERROR) {
|
||||
event = module_event_noreply;
|
||||
if(what == NETEVENT_CAPSFAIL)
|
||||
event = module_event_capsfail;
|
||||
}
|
||||
mesh_run(mesh, e->qstate->mesh_info, event, e);
|
||||
}
|
||||
|
||||
struct mesh_state*
|
||||
@@ -213,6 +396,8 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
|
||||
mstate->node.key = mstate;
|
||||
mstate->run_node.key = mstate;
|
||||
mstate->reply_list = NULL;
|
||||
mstate->list_select = mesh_no_list;
|
||||
mstate->replies_sent = 0;
|
||||
rbtree_init(&mstate->super_set, &mesh_state_ref_compare);
|
||||
rbtree_init(&mstate->sub_set, &mesh_state_ref_compare);
|
||||
mstate->num_activated = 0;
|
||||
@@ -237,7 +422,7 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
|
||||
mstate->s.env = env;
|
||||
mstate->s.mesh_info = mstate;
|
||||
/* init modules */
|
||||
for(i=0; i<env->mesh->num_modules; i++) {
|
||||
for(i=0; i<env->mesh->mods.num; i++) {
|
||||
mstate->s.minfo[i] = NULL;
|
||||
mstate->s.ext_state[i] = module_state_initial;
|
||||
}
|
||||
@@ -251,11 +436,19 @@ mesh_state_cleanup(struct mesh_state* mstate)
|
||||
int i;
|
||||
if(!mstate)
|
||||
return;
|
||||
/* drop unsent replies */
|
||||
if(!mstate->replies_sent) {
|
||||
struct mesh_reply* rep;
|
||||
for(rep=mstate->reply_list; rep; rep=rep->next) {
|
||||
comm_point_drop_reply(&rep->query_reply);
|
||||
}
|
||||
}
|
||||
|
||||
/* de-init modules */
|
||||
mesh = mstate->s.env->mesh;
|
||||
for(i=0; i<mesh->num_modules; i++) {
|
||||
log_assert(fptr_whitelist_mod_clear(mesh->modfunc[i]->clear));
|
||||
(*mesh->modfunc[i]->clear)(&mstate->s, i);
|
||||
for(i=0; i<mesh->mods.num; i++) {
|
||||
fptr_ok(fptr_whitelist_mod_clear(mesh->mods.mod[i]->clear));
|
||||
(*mesh->mods.mod[i]->clear)(&mstate->s, i);
|
||||
mstate->s.minfo[i] = NULL;
|
||||
mstate->s.ext_state[i] = module_finished;
|
||||
}
|
||||
@@ -273,11 +466,20 @@ mesh_state_delete(struct module_qstate* qstate)
|
||||
mstate = qstate->mesh_info;
|
||||
mesh = mstate->s.env->mesh;
|
||||
mesh_detach_subs(&mstate->s);
|
||||
if(!mstate->reply_list && mstate->super_set.count == 0) {
|
||||
if(mstate->list_select == mesh_forever_list) {
|
||||
mesh->num_forever_states --;
|
||||
mesh_list_remove(mstate, &mesh->forever_first,
|
||||
&mesh->forever_last);
|
||||
} else if(mstate->list_select == mesh_jostle_list) {
|
||||
mesh_list_remove(mstate, &mesh->jostle_first,
|
||||
&mesh->jostle_last);
|
||||
}
|
||||
if(!mstate->reply_list && !mstate->cb_list
|
||||
&& mstate->super_set.count == 0) {
|
||||
log_assert(mesh->num_detached_states > 0);
|
||||
mesh->num_detached_states--;
|
||||
}
|
||||
if(mstate->reply_list) {
|
||||
if(mstate->reply_list || mstate->cb_list) {
|
||||
log_assert(mesh->num_reply_states > 0);
|
||||
mesh->num_reply_states--;
|
||||
}
|
||||
@@ -301,7 +503,8 @@ void mesh_detach_subs(struct module_qstate* qstate)
|
||||
RBTREE_FOR(ref, struct mesh_state_ref*, &qstate->mesh_info->sub_set) {
|
||||
n = rbtree_delete(&ref->s->super_set, &lookup);
|
||||
log_assert(n != NULL); /* must have been present */
|
||||
if(!ref->s->reply_list && ref->s->super_set.count == 0) {
|
||||
if(!ref->s->reply_list && !ref->s->cb_list
|
||||
&& ref->s->super_set.count == 0) {
|
||||
mesh->num_detached_states++;
|
||||
log_assert(mesh->num_detached_states +
|
||||
mesh->num_reply_states <= mesh->all.count);
|
||||
@@ -336,7 +539,7 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
|
||||
*newq = NULL;
|
||||
if(!mesh_state_attachment(qstate->mesh_info, sub))
|
||||
return 0;
|
||||
if(!sub->reply_list && sub->super_set.count == 1) {
|
||||
if(!sub->reply_list && !sub->cb_list && sub->super_set.count == 1) {
|
||||
/* it used to be detached, before this one got added */
|
||||
log_assert(mesh->num_detached_states > 0);
|
||||
mesh->num_detached_states--;
|
||||
@@ -368,51 +571,47 @@ int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** subtract timers and the values do not overflow or become negative */
|
||||
/**
|
||||
* callback results to mesh cb entry
|
||||
* @param m: mesh state to send it for.
|
||||
* @param rcode: if not 0, error code.
|
||||
* @param rep: reply to send (or NULL if rcode is set).
|
||||
* @param r: callback entry
|
||||
*/
|
||||
static void
|
||||
timeval_subtract(struct timeval* d, struct timeval* end, struct timeval* start)
|
||||
mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
struct mesh_cb* r)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
d->tv_sec = end->tv_sec - start->tv_sec;
|
||||
while(end->tv_usec < start->tv_usec) {
|
||||
end->tv_usec += 1000000;
|
||||
d->tv_sec--;
|
||||
int secure;
|
||||
/* bogus messages are not made into servfail, sec_status passed
|
||||
* to the callback function */
|
||||
if(rep && rep->security == sec_status_secure)
|
||||
secure = 1;
|
||||
else secure = 0;
|
||||
if(!rep && rcode == LDNS_RCODE_NOERROR)
|
||||
rcode = LDNS_RCODE_SERVFAIL;
|
||||
/* send the reply */
|
||||
if(rcode) {
|
||||
(*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked);
|
||||
} else {
|
||||
size_t udp_size = r->edns.udp_size;
|
||||
ldns_buffer_clear(r->buf);
|
||||
r->edns.edns_version = EDNS_ADVERTISED_VERSION;
|
||||
r->edns.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
r->edns.ext_rcode = 0;
|
||||
r->edns.bits &= EDNS_DO;
|
||||
if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
|
||||
r->qflags, r->buf, 0, 1,
|
||||
m->s.env->scratch, udp_size, &r->edns,
|
||||
(int)(r->edns.bits & EDNS_DO), secure))
|
||||
{
|
||||
(*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf,
|
||||
sec_status_unchecked);
|
||||
}
|
||||
else (*r->cb)(r->cb_arg, LDNS_RCODE_NOERROR, r->buf,
|
||||
rep->security);
|
||||
}
|
||||
d->tv_usec = end->tv_usec - start->tv_usec;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** add timers and the values do not overflow or become negative */
|
||||
static void
|
||||
timeval_add(struct timeval* d, struct timeval* add)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
d->tv_sec += add->tv_sec;
|
||||
d->tv_usec += add->tv_usec;
|
||||
while(d->tv_usec > 1000000 ) {
|
||||
d->tv_usec -= 1000000;
|
||||
d->tv_sec++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** divide sum of timers to get average */
|
||||
static void
|
||||
timeval_divide(struct timeval* avg, struct timeval* sum, size_t d)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
size_t leftover;
|
||||
if(d == 0) {
|
||||
avg->tv_sec = 0;
|
||||
avg->tv_usec = 0;
|
||||
return;
|
||||
}
|
||||
avg->tv_sec = sum->tv_sec / d;
|
||||
avg->tv_usec = sum->tv_usec / d;
|
||||
/* handle fraction from seconds divide */
|
||||
leftover = sum->tv_sec - avg->tv_sec*d;
|
||||
avg->tv_usec += (leftover*1000000)/d;
|
||||
#endif
|
||||
m->s.env->mesh->num_reply_addrs--;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -421,23 +620,43 @@ timeval_divide(struct timeval* avg, struct timeval* sum, size_t d)
|
||||
* @param rcode: if not 0, error code.
|
||||
* @param rep: reply to send (or NULL if rcode is set).
|
||||
* @param r: reply entry
|
||||
* @param prev: previous reply, already has its answer encoded in buffer.
|
||||
*/
|
||||
static void
|
||||
mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
struct mesh_reply* r)
|
||||
struct mesh_reply* r, struct mesh_reply* prev)
|
||||
{
|
||||
struct timeval end_time;
|
||||
struct timeval duration;
|
||||
int secure;
|
||||
/* examine security status */
|
||||
if(m->s.env->need_to_validate && !(r->qflags&BIT_CD) && rep &&
|
||||
rep->security <= sec_status_bogus) {
|
||||
rcode = LDNS_RCODE_SERVFAIL;
|
||||
if(m->s.env->cfg->stat_extended)
|
||||
m->s.env->mesh->ans_bogus++;
|
||||
}
|
||||
if(rep && rep->security == sec_status_secure)
|
||||
secure = 1;
|
||||
else secure = 0;
|
||||
if(!rep && rcode == LDNS_RCODE_NOERROR)
|
||||
rcode = LDNS_RCODE_SERVFAIL;
|
||||
/* send the reply */
|
||||
if(rcode) {
|
||||
if(prev && prev->qflags == r->qflags &&
|
||||
prev->edns.edns_present == r->edns.edns_present &&
|
||||
prev->edns.bits == r->edns.bits &&
|
||||
prev->edns.udp_size == r->edns.udp_size) {
|
||||
/* if the previous reply is identical to this one, fix ID */
|
||||
if(prev->query_reply.c->buffer != r->query_reply.c->buffer)
|
||||
ldns_buffer_copy(r->query_reply.c->buffer,
|
||||
prev->query_reply.c->buffer);
|
||||
ldns_buffer_write_at(r->query_reply.c->buffer, 0,
|
||||
&r->qid, sizeof(uint16_t));
|
||||
ldns_buffer_write_at(r->query_reply.c->buffer, 12,
|
||||
r->qname, m->s.qinfo.qname_len);
|
||||
comm_point_send_reply(&r->query_reply);
|
||||
} else if(rcode) {
|
||||
m->s.qinfo.qname = r->qname;
|
||||
error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo,
|
||||
r->qid, r->qflags, &r->edns);
|
||||
comm_point_send_reply(&r->query_reply);
|
||||
@@ -447,6 +666,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
r->edns.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
r->edns.ext_rcode = 0;
|
||||
r->edns.bits &= EDNS_DO;
|
||||
m->s.qinfo.qname = r->qname;
|
||||
if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
|
||||
r->qflags, r->query_reply.c->buffer, 0, 1,
|
||||
m->s.env->scratch, udp_size, &r->edns,
|
||||
@@ -460,27 +680,38 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
}
|
||||
/* account */
|
||||
m->s.env->mesh->num_reply_addrs--;
|
||||
if(gettimeofday(&end_time, NULL) < 0) {
|
||||
log_err("gettimeofday: %s", strerror(errno));
|
||||
return;
|
||||
} else {
|
||||
struct timeval duration;
|
||||
timeval_subtract(&duration, &end_time, &r->start_time);
|
||||
verbose(VERB_ALGO, "query took %d.%6.6d sec",
|
||||
(int)duration.tv_sec, (int)duration.tv_usec);
|
||||
m->s.env->mesh->replies_sent++;
|
||||
timeval_add(&m->s.env->mesh->replies_sum_wait, &duration);
|
||||
timehist_insert(m->s.env->mesh->histogram, &duration);
|
||||
end_time = *m->s.env->now_tv;
|
||||
timeval_subtract(&duration, &end_time, &r->start_time);
|
||||
verbose(VERB_ALGO, "query took %d.%6.6d sec",
|
||||
(int)duration.tv_sec, (int)duration.tv_usec);
|
||||
m->s.env->mesh->replies_sent++;
|
||||
timeval_add(&m->s.env->mesh->replies_sum_wait, &duration);
|
||||
timehist_insert(m->s.env->mesh->histogram, &duration);
|
||||
if(m->s.env->cfg->stat_extended) {
|
||||
uint16_t rc = FLAGS_GET_RCODE(ldns_buffer_read_u16_at(r->
|
||||
query_reply.c->buffer, 2));
|
||||
if(secure) m->s.env->mesh->ans_secure++;
|
||||
m->s.env->mesh->ans_rcode[ rc ] ++;
|
||||
if(rc == 0 && LDNS_ANCOUNT(ldns_buffer_begin(r->
|
||||
query_reply.c->buffer)) == 0)
|
||||
m->s.env->mesh->ans_nodata++;
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_query_done(struct mesh_state* mstate)
|
||||
{
|
||||
struct mesh_reply* r;
|
||||
struct mesh_reply* prev = NULL;
|
||||
struct mesh_cb* c;
|
||||
struct reply_info* rep = (mstate->s.return_msg?
|
||||
mstate->s.return_msg->rep:NULL);
|
||||
for(r = mstate->reply_list; r; r = r->next) {
|
||||
mesh_send_reply(mstate, mstate->s.return_rcode, rep, r);
|
||||
mesh_send_reply(mstate, mstate->s.return_rcode, rep, r, prev);
|
||||
prev = r;
|
||||
}
|
||||
mstate->replies_sent = 1;
|
||||
for(c = mstate->cb_list; c; c = c->next) {
|
||||
mesh_do_callback(mstate, mstate->s.return_rcode, rep, c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,9 +723,9 @@ void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate)
|
||||
/* make super runnable */
|
||||
(void)rbtree_insert(&mesh->run, &ref->s->run_node);
|
||||
/* callback the function to inform super of result */
|
||||
log_assert(fptr_whitelist_mod_inform_super(
|
||||
mesh->modfunc[ref->s->s.curmod]->inform_super));
|
||||
(*mesh->modfunc[ref->s->s.curmod]->inform_super)(&mstate->s,
|
||||
fptr_ok(fptr_whitelist_mod_inform_super(
|
||||
mesh->mods.mod[ref->s->s.curmod]->inform_super));
|
||||
(*mesh->mods.mod[ref->s->s.curmod]->inform_super)(&mstate->s,
|
||||
ref->s->s.curmod, &ref->s->s);
|
||||
}
|
||||
}
|
||||
@@ -514,8 +745,28 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh,
|
||||
return result;
|
||||
}
|
||||
|
||||
int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
|
||||
ldns_buffer* buf, mesh_cb_func_t cb, void* cb_arg,
|
||||
uint16_t qid, uint16_t qflags)
|
||||
{
|
||||
struct mesh_cb* r = regional_alloc(s->s.region,
|
||||
sizeof(struct mesh_cb));
|
||||
if(!r)
|
||||
return 0;
|
||||
r->buf = buf;
|
||||
r->cb = cb;
|
||||
r->cb_arg = cb_arg;
|
||||
r->edns = *edns;
|
||||
r->qid = qid;
|
||||
r->qflags = qflags;
|
||||
r->next = s->cb_list;
|
||||
s->cb_list = r;
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
|
||||
struct comm_reply* rep, uint16_t qid, uint16_t qflags)
|
||||
struct comm_reply* rep, uint16_t qid, uint16_t qflags, uint8_t* qname)
|
||||
{
|
||||
struct mesh_reply* r = regional_alloc(s->s.region,
|
||||
sizeof(struct mesh_reply));
|
||||
@@ -525,11 +776,12 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
|
||||
r->edns = *edns;
|
||||
r->qid = qid;
|
||||
r->qflags = qflags;
|
||||
if(gettimeofday(&r->start_time, NULL) < 0) {
|
||||
log_err("addrep: gettimeofday: %s", strerror(errno));
|
||||
memset(&r->start_time, 0, sizeof(r->start_time));
|
||||
}
|
||||
r->start_time = *s->s.env->now_tv;
|
||||
r->next = s->reply_list;
|
||||
r->qname = regional_alloc_init(s->s.region, qname,
|
||||
s->s.qinfo.qname_len);
|
||||
if(!r->qname)
|
||||
return 0;
|
||||
s->reply_list = r;
|
||||
return 1;
|
||||
|
||||
@@ -558,16 +810,16 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate,
|
||||
if(mstate->num_activated > MESH_MAX_ACTIVATION) {
|
||||
/* module is looping. Stop it. */
|
||||
log_err("internal error: looping module stopped");
|
||||
log_query_info(VERB_DETAIL, "pass error for qstate",
|
||||
log_query_info(VERB_QUERY, "pass error for qstate",
|
||||
&mstate->s.qinfo);
|
||||
s = module_error;
|
||||
}
|
||||
if(s == module_wait_module) {
|
||||
/* start next module */
|
||||
mstate->s.curmod++;
|
||||
if(mesh->num_modules == mstate->s.curmod) {
|
||||
if(mesh->mods.num == mstate->s.curmod) {
|
||||
log_err("Cannot pass to next module; at last module");
|
||||
log_query_info(VERB_DETAIL, "pass error for qstate",
|
||||
log_query_info(VERB_QUERY, "pass error for qstate",
|
||||
&mstate->s.qinfo);
|
||||
mstate->s.curmod--;
|
||||
return mesh_continue(mesh, mstate, module_error, ev);
|
||||
@@ -601,9 +853,9 @@ void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate,
|
||||
verbose(VERB_ALGO, "mesh_run: start");
|
||||
while(mstate) {
|
||||
/* run the module */
|
||||
log_assert(fptr_whitelist_mod_operate(
|
||||
mesh->modfunc[mstate->s.curmod]->operate));
|
||||
(*mesh->modfunc[mstate->s.curmod]->operate)
|
||||
fptr_ok(fptr_whitelist_mod_operate(
|
||||
mesh->mods.mod[mstate->s.curmod]->operate));
|
||||
(*mesh->mods.mod[mstate->s.curmod]->operate)
|
||||
(&mstate->s, ev, mstate->s.curmod, e);
|
||||
|
||||
/* examine results */
|
||||
@@ -611,7 +863,7 @@ void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate,
|
||||
regional_free_all(mstate->s.env->scratch);
|
||||
s = mstate->s.ext_state[mstate->s.curmod];
|
||||
verbose(VERB_ALGO, "mesh_run: %s module exit state is %s",
|
||||
mesh->modfunc[mstate->s.curmod]->name, strextstate(s));
|
||||
mesh->mods.mod[mstate->s.curmod]->name, strextstate(s));
|
||||
e = NULL;
|
||||
if(mesh_continue(mesh, mstate, s, &ev))
|
||||
continue;
|
||||
@@ -637,13 +889,15 @@ mesh_log_list(struct mesh_area* mesh)
|
||||
struct mesh_state* m;
|
||||
int num = 0;
|
||||
RBTREE_FOR(m, struct mesh_state*, &mesh->all) {
|
||||
snprintf(buf, sizeof(buf), "%d%s%s%s%s%s mod%d %s",
|
||||
snprintf(buf, sizeof(buf), "%d%s%s%s%s%s mod%d %s%s",
|
||||
num++, (m->s.is_priming)?"p":"", /* prime */
|
||||
(m->s.query_flags&BIT_RD)?"RD":"",
|
||||
(m->s.query_flags&BIT_CD)?"CD":"",
|
||||
(m->super_set.count==0)?"d":"", /* detached */
|
||||
(m->sub_set.count!=0)?"c":"", /* children */
|
||||
m->s.curmod, (m->reply_list)?"hr":"nr"); /*hasreply*/
|
||||
m->s.curmod, (m->reply_list)?"rep":"", /*hasreply*/
|
||||
(m->cb_list)?"cb":"" /* callbacks */
|
||||
);
|
||||
log_query_info(VERB_ALGO, buf, &m->s.qinfo);
|
||||
}
|
||||
}
|
||||
@@ -651,23 +905,44 @@ mesh_log_list(struct mesh_area* mesh)
|
||||
void
|
||||
mesh_stats(struct mesh_area* mesh, const char* str)
|
||||
{
|
||||
log_info("%s %u states (%u with reply, %u detached), "
|
||||
"%u waiting replies", str, (unsigned)mesh->all.count,
|
||||
verbose(VERB_DETAIL, "%s %u recursion states (%u with reply, "
|
||||
"%u detached), %u waiting replies, %u recursion replies "
|
||||
"sent, %d replies dropped, %d states jostled out",
|
||||
str, (unsigned)mesh->all.count,
|
||||
(unsigned)mesh->num_reply_states,
|
||||
(unsigned)mesh->num_detached_states,
|
||||
(unsigned)mesh->num_reply_addrs);
|
||||
(unsigned)mesh->num_reply_addrs,
|
||||
(unsigned)mesh->replies_sent,
|
||||
(unsigned)mesh->stats_dropped,
|
||||
(unsigned)mesh->stats_jostled);
|
||||
if(mesh->replies_sent > 0) {
|
||||
struct timeval avg;
|
||||
timeval_divide(&avg, &mesh->replies_sum_wait,
|
||||
mesh->replies_sent);
|
||||
log_info("sent %u replies, with average wait "
|
||||
"of %d.%6.6d sec", (unsigned)mesh->replies_sent,
|
||||
(int)avg.tv_sec, (int)avg.tv_usec);
|
||||
log_info("histogram of reply wait times");
|
||||
timehist_log(mesh->histogram);
|
||||
log_info("average recursion processing time "
|
||||
"%d.%6.6d sec", (int)avg.tv_sec, (int)avg.tv_usec);
|
||||
log_info("histogram of recursion processing times");
|
||||
timehist_log(mesh->histogram, "recursions");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mesh_stats_clear(struct mesh_area* mesh)
|
||||
{
|
||||
if(!mesh)
|
||||
return;
|
||||
mesh->replies_sent = 0;
|
||||
mesh->replies_sum_wait.tv_sec = 0;
|
||||
mesh->replies_sum_wait.tv_usec = 0;
|
||||
mesh->stats_jostled = 0;
|
||||
mesh->stats_dropped = 0;
|
||||
timehist_clear(mesh->histogram);
|
||||
mesh->ans_secure = 0;
|
||||
mesh->ans_bogus = 0;
|
||||
memset(&mesh->ans_rcode[0], 0, sizeof(size_t)*16);
|
||||
mesh->ans_nodata = 0;
|
||||
}
|
||||
|
||||
size_t
|
||||
mesh_get_mem(struct mesh_area* mesh)
|
||||
{
|
||||
@@ -706,3 +981,26 @@ mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo,
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mesh_list_insert(struct mesh_state* m, struct mesh_state** fp,
|
||||
struct mesh_state** lp)
|
||||
{
|
||||
/* insert as last element */
|
||||
m->prev = *lp;
|
||||
m->next = NULL;
|
||||
if(*lp)
|
||||
(*lp)->next = m;
|
||||
else *fp = m;
|
||||
*lp = m;
|
||||
}
|
||||
|
||||
void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp,
|
||||
struct mesh_state** lp)
|
||||
{
|
||||
if(m->next)
|
||||
m->next->prev = m->prev;
|
||||
else *lp = m->prev;
|
||||
if(m->prev)
|
||||
m->prev->next = m->next;
|
||||
else *fp = m->next;
|
||||
}
|
||||
|
||||
+154
-12
@@ -50,8 +50,10 @@
|
||||
#include "util/netevent.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/module.h"
|
||||
#include "services/modstack.h"
|
||||
struct mesh_state;
|
||||
struct mesh_reply;
|
||||
struct mesh_cb;
|
||||
struct query_info;
|
||||
struct reply_info;
|
||||
struct outbound_entry;
|
||||
@@ -67,10 +69,8 @@ struct timehist;
|
||||
* Mesh of query states
|
||||
*/
|
||||
struct mesh_area {
|
||||
/** the number of modules */
|
||||
int num_modules;
|
||||
/** the module callbacks, array of num_modules length (ref only) */
|
||||
struct module_func_block** modfunc;
|
||||
/** active module stack */
|
||||
struct module_stack mods;
|
||||
/** environment for new states */
|
||||
struct module_env* env;
|
||||
|
||||
@@ -89,13 +89,46 @@ struct mesh_area {
|
||||
* an empty set of super-states, thus are 'toplevel' or detached
|
||||
* internal opportunistic queries */
|
||||
size_t num_detached_states;
|
||||
/** number of reply states in the forever list */
|
||||
size_t num_forever_states;
|
||||
|
||||
/** max total number of reply states to have */
|
||||
size_t max_reply_states;
|
||||
/** max forever number of reply states to have */
|
||||
size_t max_forever_states;
|
||||
|
||||
/** stats, cumulative number of reply states jostled out */
|
||||
size_t stats_jostled;
|
||||
/** stats, cumulative number of incoming client msgs dropped */
|
||||
size_t stats_dropped;
|
||||
/** number of replies sent */
|
||||
size_t replies_sent;
|
||||
/** sum of waiting times for the replies */
|
||||
struct timeval replies_sum_wait;
|
||||
/** histogram of time values */
|
||||
struct timehist* histogram;
|
||||
/** (extended stats) secure replies */
|
||||
size_t ans_secure;
|
||||
/** (extended stats) bogus replies */
|
||||
size_t ans_bogus;
|
||||
/** (extended stats) rcodes in replies */
|
||||
size_t ans_rcode[16];
|
||||
/** (extended stats) rcode nodata in replies */
|
||||
size_t ans_nodata;
|
||||
|
||||
/** double linked list of the run-to-completion query states.
|
||||
* These are query states with a reply */
|
||||
struct mesh_state* forever_first;
|
||||
/** last entry in run forever list */
|
||||
struct mesh_state* forever_last;
|
||||
|
||||
/** double linked list of the query states that can be jostled out
|
||||
* by new queries if too old. These are query states with a reply */
|
||||
struct mesh_state* jostle_first;
|
||||
/** last entry in jostle list - this is the entry that is newest */
|
||||
struct mesh_state* jostle_last;
|
||||
/** timeout for jostling. if age is lower, it does not get jostled. */
|
||||
struct timeval jostle_max;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -117,6 +150,8 @@ struct mesh_state {
|
||||
struct module_qstate s;
|
||||
/** the list of replies to clients for the results */
|
||||
struct mesh_reply* reply_list;
|
||||
/** the list of callbacks for the results */
|
||||
struct mesh_cb* cb_list;
|
||||
/** set of superstates (that want this state's result)
|
||||
* contains struct mesh_state_ref* */
|
||||
rbtree_t super_set;
|
||||
@@ -125,6 +160,17 @@ struct mesh_state {
|
||||
rbtree_t sub_set;
|
||||
/** number of activations for the mesh state */
|
||||
size_t num_activated;
|
||||
|
||||
/** previous in linked list for reply states */
|
||||
struct mesh_state* prev;
|
||||
/** next in linked list for reply states */
|
||||
struct mesh_state* next;
|
||||
/** if this state is in the forever list, jostle list, or neither */
|
||||
enum mesh_list_select { mesh_no_list, mesh_forever_list,
|
||||
mesh_jostle_list } list_select;
|
||||
|
||||
/** true if replies have been sent out (at end for alignment) */
|
||||
uint8_t replies_sent;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -154,20 +200,49 @@ struct mesh_reply {
|
||||
uint16_t qid;
|
||||
/** flags of query, for reply flags */
|
||||
uint16_t qflags;
|
||||
/** qname from this query. len same as mesh qinfo. */
|
||||
uint8_t* qname;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mesh result callback func.
|
||||
* called as func(cb_arg, rcode, buffer_with_reply, security);
|
||||
* */
|
||||
typedef void (*mesh_cb_func_t)(void*, int, ldns_buffer*, enum sec_status);
|
||||
|
||||
/**
|
||||
* Callback to result routine
|
||||
*/
|
||||
struct mesh_cb {
|
||||
/** next in list */
|
||||
struct mesh_cb* next;
|
||||
/** edns data from query */
|
||||
struct edns_data edns;
|
||||
/** id of query, in network byteorder. */
|
||||
uint16_t qid;
|
||||
/** flags of query, for reply flags */
|
||||
uint16_t qflags;
|
||||
/** buffer for reply */
|
||||
ldns_buffer* buf;
|
||||
|
||||
/** callback routine for results. if rcode != 0 buf has message.
|
||||
* called as cb(cb_arg, rcode, buf);
|
||||
*/
|
||||
mesh_cb_func_t cb;
|
||||
/** user arg for callback */
|
||||
void* cb_arg;
|
||||
};
|
||||
|
||||
/* ------------------- Functions for worker -------------------- */
|
||||
|
||||
/**
|
||||
* Allocate mesh, to empty.
|
||||
* @param num_modules: number of modules that are present.
|
||||
* @param modfunc: array passed (alloced and deleted by caller), that has
|
||||
* num_modules function callbacks for the modules.
|
||||
* @param stack: module stack to activate, copied (as readonly reference).
|
||||
* @param env: environment for new queries.
|
||||
* @return mesh: the new mesh or NULL on error.
|
||||
*/
|
||||
struct mesh_area* mesh_create(int num_modules,
|
||||
struct module_func_block** modfunc, struct module_env* env);
|
||||
struct mesh_area* mesh_create(struct module_stack* stack,
|
||||
struct module_env* env);
|
||||
|
||||
/**
|
||||
* Delete mesh, and all query states and replies in it.
|
||||
@@ -191,6 +266,25 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
uint16_t qflags, struct edns_data* edns, struct comm_reply* rep,
|
||||
uint16_t qid);
|
||||
|
||||
/**
|
||||
* New query with callback. Create new query state if needed, and
|
||||
* add mesh_cb to it.
|
||||
* Will run the mesh area queries to process if a new query state is created.
|
||||
*
|
||||
* @param mesh: the mesh.
|
||||
* @param qinfo: query from client.
|
||||
* @param qflags: flags from client query.
|
||||
* @param edns: edns data from client query.
|
||||
* @param buf: buffer for reply contents.
|
||||
* @param qid: query id to reply with.
|
||||
* @param cb: callback function.
|
||||
* @param cb_arg: callback user arg.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
uint16_t qflags, struct edns_data* edns, ldns_buffer* buf,
|
||||
uint16_t qid, mesh_cb_func_t cb, void* cb_arg);
|
||||
|
||||
/**
|
||||
* Handle new event from the wire. A serviced query has returned.
|
||||
* The query state will be made runnable, and the mesh_area will process
|
||||
@@ -198,11 +292,11 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
|
||||
*
|
||||
* @param mesh: the query mesh.
|
||||
* @param e: outbound entry, with query state to run and reply pointer.
|
||||
* @param is_ok: if true, reply is OK, otherwise a timeout happened.
|
||||
* @param reply: the comm point reply info.
|
||||
* @param what: NETEVENT_* error code (if not 0, what is wrong, TIMEOUT).
|
||||
*/
|
||||
void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
|
||||
int is_ok, struct comm_reply* reply);
|
||||
struct comm_reply* reply, int what);
|
||||
|
||||
/* ------------------- Functions for module environment --------------- */
|
||||
|
||||
@@ -327,10 +421,27 @@ int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub);
|
||||
* @param rep: comm point reply info.
|
||||
* @param qid: ID of reply.
|
||||
* @param qflags: original query flags.
|
||||
* @param qname: original query name.
|
||||
* @return: 0 on alloc error.
|
||||
*/
|
||||
int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
|
||||
struct comm_reply* rep, uint16_t qid, uint16_t qflags);
|
||||
struct comm_reply* rep, uint16_t qid, uint16_t qflags, uint8_t* qname);
|
||||
|
||||
/**
|
||||
* Create new callback structure and attach it to a mesh state.
|
||||
* Does not update stat items in mesh area.
|
||||
* @param s: the mesh state.
|
||||
* @param edns: edns data for reply (bufsize).
|
||||
* @param buf: buffer for reply
|
||||
* @param cb: callback to call with results.
|
||||
* @param cb_arg: callback user arg.
|
||||
* @param qid: ID of reply.
|
||||
* @param qflags: original query flags.
|
||||
* @return: 0 on alloc error.
|
||||
*/
|
||||
int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
|
||||
ldns_buffer* buf, mesh_cb_func_t cb, void* cb_arg, uint16_t qid,
|
||||
uint16_t qflags);
|
||||
|
||||
/**
|
||||
* Run the mesh. Run all runnable mesh states. Which can create new
|
||||
@@ -351,6 +462,12 @@ void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate,
|
||||
*/
|
||||
void mesh_stats(struct mesh_area* mesh, const char* str);
|
||||
|
||||
/**
|
||||
* Clear the stats that the mesh keeps (number of queries serviced)
|
||||
* @param mesh: the mesh
|
||||
*/
|
||||
void mesh_stats_clear(struct mesh_area* mesh);
|
||||
|
||||
/**
|
||||
* Print all the states in the mesh to the log.
|
||||
* @param mesh: the mesh to print all states of.
|
||||
@@ -384,4 +501,29 @@ int mesh_state_compare(const void* ap, const void* bp);
|
||||
/** compare two mesh references */
|
||||
int mesh_state_ref_compare(const void* ap, const void* bp);
|
||||
|
||||
/**
|
||||
* Make space for another recursion state for a reply in the mesh
|
||||
* @param mesh: mesh area
|
||||
* @return false if no space is available.
|
||||
*/
|
||||
int mesh_make_new_space(struct mesh_area* mesh);
|
||||
|
||||
/**
|
||||
* Insert mesh state into a double linked list. Inserted at end.
|
||||
* @param m: mesh state.
|
||||
* @param fp: pointer to the first-elem-pointer of the list.
|
||||
* @param lp: pointer to the last-elem-pointer of the list.
|
||||
*/
|
||||
void mesh_list_insert(struct mesh_state* m, struct mesh_state** fp,
|
||||
struct mesh_state** lp);
|
||||
|
||||
/**
|
||||
* Remove mesh state from a double linked list. Remove from any position.
|
||||
* @param m: mesh state.
|
||||
* @param fp: pointer to the first-elem-pointer of the list.
|
||||
* @param lp: pointer to the last-elem-pointer of the list.
|
||||
*/
|
||||
void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp,
|
||||
struct mesh_state** lp);
|
||||
|
||||
#endif /* SERVICES_MESH_H */
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* services/modstack.c - stack of modules
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to help maintain a stack of modules.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "services/modstack.h"
|
||||
#include "util/module.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "iterator/iterator.h"
|
||||
#include "validator/validator.h"
|
||||
|
||||
/** count number of modules (words) in the string */
|
||||
static int
|
||||
count_modules(const char* s)
|
||||
{
|
||||
int num = 0;
|
||||
if(!s)
|
||||
return 0;
|
||||
while(*s) {
|
||||
/* skip whitespace */
|
||||
while(*s && isspace((int)*s))
|
||||
s++;
|
||||
if(*s && !isspace((int)*s)) {
|
||||
/* skip identifier */
|
||||
num++;
|
||||
while(*s && !isspace((int)*s))
|
||||
s++;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
void
|
||||
modstack_init(struct module_stack* stack)
|
||||
{
|
||||
stack->num = 0;
|
||||
stack->mod = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
modstack_config(struct module_stack* stack, const char* module_conf)
|
||||
{
|
||||
int i;
|
||||
verbose(VERB_QUERY, "module config: \"%s\"", module_conf);
|
||||
stack->num = count_modules(module_conf);
|
||||
if(stack->num == 0) {
|
||||
log_err("error: no modules specified");
|
||||
return 0;
|
||||
}
|
||||
if(stack->num > MAX_MODULE) {
|
||||
log_err("error: too many modules (%d max %d)",
|
||||
stack->num, MAX_MODULE);
|
||||
return 0;
|
||||
}
|
||||
stack->mod = (struct module_func_block**)calloc((size_t)
|
||||
stack->num, sizeof(struct module_func_block*));
|
||||
if(!stack->mod) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
for(i=0; i<stack->num; i++) {
|
||||
stack->mod[i] = module_factory(&module_conf);
|
||||
if(!stack->mod[i]) {
|
||||
log_err("Unknown value for next module: '%s'",
|
||||
module_conf);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct
|
||||
module_func_block* module_factory(const char** str)
|
||||
{
|
||||
/* these are the modules available */
|
||||
int num = 2;
|
||||
const char* names[] = {"iterator", "validator", NULL};
|
||||
struct module_func_block* (*fb[])(void) =
|
||||
{&iter_get_funcblock, &val_get_funcblock, NULL};
|
||||
|
||||
int i;
|
||||
const char* s = *str;
|
||||
while(*s && isspace((int)*s))
|
||||
s++;
|
||||
for(i=0; i<num; i++) {
|
||||
if(strncmp(names[i], s, strlen(names[i])) == 0) {
|
||||
s += strlen(names[i]);
|
||||
*str = s;
|
||||
return (*fb[i])();
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
modstack_setup(struct module_stack* stack, const char* module_conf,
|
||||
struct module_env* env)
|
||||
{
|
||||
int i;
|
||||
if(stack->num != 0)
|
||||
modstack_desetup(stack, env);
|
||||
/* fixed setup of the modules */
|
||||
if(!modstack_config(stack, module_conf)) {
|
||||
return 0;
|
||||
}
|
||||
env->need_to_validate = 0; /* set by module init below */
|
||||
for(i=0; i<stack->num; i++) {
|
||||
verbose(VERB_OPS, "init module %d: %s",
|
||||
i, stack->mod[i]->name);
|
||||
fptr_ok(fptr_whitelist_mod_init(stack->mod[i]->init));
|
||||
if(!(*stack->mod[i]->init)(env, i)) {
|
||||
log_err("module init for module %s failed",
|
||||
stack->mod[i]->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
modstack_desetup(struct module_stack* stack, struct module_env* env)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<stack->num; i++) {
|
||||
fptr_ok(fptr_whitelist_mod_deinit(stack->mod[i]->deinit));
|
||||
(*stack->mod[i]->deinit)(env, i);
|
||||
}
|
||||
stack->num = 0;
|
||||
free(stack->mod);
|
||||
stack->mod = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
modstack_find(struct module_stack* stack, const char* name)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<stack->num; i++) {
|
||||
if(strcmp(stack->mod[i]->name, name) == 0)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* services/modstack.h - stack of modules
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains functions to help maintain a stack of modules.
|
||||
*/
|
||||
|
||||
#ifndef SERVICES_MODSTACK_H
|
||||
#define SERVICES_MODSTACK_H
|
||||
struct module_func_block;
|
||||
struct module_env;
|
||||
|
||||
/**
|
||||
* Stack of modules.
|
||||
*/
|
||||
struct module_stack {
|
||||
/** the number of modules */
|
||||
int num;
|
||||
/** the module callbacks, array of num_modules length (ref only) */
|
||||
struct module_func_block** mod;
|
||||
};
|
||||
|
||||
/**
|
||||
* Init a stack of modules
|
||||
* @param stack: initialised as empty.
|
||||
*/
|
||||
void modstack_init(struct module_stack* stack);
|
||||
|
||||
/**
|
||||
* Read config file module settings and set up the modfunc block
|
||||
* @param stack: the stack of modules (empty before call).
|
||||
* @param module_conf: string what modules to insert.
|
||||
* @return false on error
|
||||
*/
|
||||
int modstack_config(struct module_stack* stack, const char* module_conf);
|
||||
|
||||
/**
|
||||
* Get funcblock for module name
|
||||
* @param str: string with module name. Advanced to next value on success.
|
||||
* The string is assumed whitespace separated list of module names.
|
||||
* @return funcblock or NULL on error.
|
||||
*/
|
||||
struct module_func_block* module_factory(const char** str);
|
||||
|
||||
/**
|
||||
* Setup modules. Assigns ids and calls module_init.
|
||||
* @param stack: if not empty beforehand, it will be desetup()ed.
|
||||
* It is then modstack_configged().
|
||||
* @param module_conf: string what modules to insert.
|
||||
* @param env: module environment which is inited by the modules.
|
||||
* environment should have a superalloc, cfg,
|
||||
* env.need_to_validate is set by the modules.
|
||||
* @return on false a module init failed.
|
||||
*/
|
||||
int modstack_setup(struct module_stack* stack, const char* module_conf,
|
||||
struct module_env* env);
|
||||
|
||||
/**
|
||||
* Desetup the modules, deinit, delete.
|
||||
* @param stack: made empty.
|
||||
* @param env: module env for module deinit() calls.
|
||||
*/
|
||||
void modstack_desetup(struct module_stack* stack, struct module_env* env);
|
||||
|
||||
/**
|
||||
* Find index of module by name.
|
||||
* @param stack: to look in
|
||||
* @param name: the name to look for
|
||||
* @return -1 on failure, otherwise index number.
|
||||
*/
|
||||
int modstack_find(struct module_stack* stack, const char* name);
|
||||
|
||||
#endif /* SERVICES_MODSTACK_H */
|
||||
+690
-279
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user