mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-08-17 21:25:50 +02:00
Compare commits
620
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82e4110e09 | ||
|
|
fb57492c0a | ||
|
|
bde1ce87d5 | ||
|
|
b7a627b8cc | ||
|
|
ea7630845f | ||
|
|
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 | ||
|
|
cc21a1ca9a | ||
|
|
919b1ab1b6 | ||
|
|
ad69492495 | ||
|
|
2b2cfd7e4b | ||
|
|
84a3dbe660 | ||
|
|
d7b0d86b09 | ||
|
|
bce5467f59 | ||
|
|
a6ae1d6194 | ||
|
|
0ee5532396 | ||
|
|
7c5182d3fd | ||
|
|
d41f40b028 | ||
|
|
518e7511d7 | ||
|
|
6048ba67fa | ||
|
|
0eddfd7528 | ||
|
|
3ed2f28b76 | ||
|
|
e9277fc201 | ||
|
|
5d5f08e4fd | ||
|
|
a06131872d | ||
|
|
d2892aa5ab | ||
|
|
27c8952a2c | ||
|
|
5b966557cc | ||
|
|
734abf9323 | ||
|
|
2725ad55fc | ||
|
|
f79c387f3f | ||
|
|
2e9785cfb5 | ||
|
|
75c30382ba | ||
|
|
fe44f5918a | ||
|
|
63dc8ffde2 | ||
|
|
2bb408e2aa | ||
|
|
0eb3149af5 | ||
|
|
298e1fd688 | ||
|
|
861483ac82 | ||
|
|
f92d51b3fe | ||
|
|
633daf4bc7 | ||
|
|
b247774236 | ||
|
|
63aa7463e8 | ||
|
|
280352760c | ||
|
|
f8dcdf6e3a | ||
|
|
2885210384 | ||
|
|
253a6e0143 | ||
|
|
90df76f931 | ||
|
|
391504fd03 | ||
|
|
da79cd57d9 | ||
|
|
c8c099960a | ||
|
|
3a84f02606 | ||
|
|
086b257b1d | ||
|
|
f22716c237 | ||
|
|
c74ae3212b | ||
|
|
b40163446e | ||
|
|
ce11690aa1 | ||
|
|
6f49c2fe55 | ||
|
|
63bef95aac | ||
|
|
f768fc1148 | ||
|
|
8e54424abc | ||
|
|
c41d12bb94 | ||
|
|
268ada0d33 | ||
|
|
907ab3d99a | ||
|
|
f12875c910 | ||
|
|
482658b1cc | ||
|
|
75792c34dc | ||
|
|
be0bdf0260 | ||
|
|
08549c7610 | ||
|
|
108539c94c | ||
|
|
a4d4d0fe3a | ||
|
|
9e732da50b | ||
|
|
fe33f7c72c | ||
|
|
631ccb58bf | ||
|
|
189fafa1da | ||
|
|
4260a18fb1 | ||
|
|
79f840acd4 | ||
|
|
fed59bca84 | ||
|
|
aecdb781fc | ||
|
|
bef17e9e7c | ||
|
|
25da698bf1 | ||
|
|
3aea7c2630 | ||
|
|
d4424931f4 | ||
|
|
a5ac86f410 | ||
|
|
aed61e4d8a | ||
|
|
c46e1a1ee8 | ||
|
|
2bfe2d5fe3 | ||
|
|
75bfcb8c91 | ||
|
|
f4049b54c4 | ||
|
|
9fec5d86d3 | ||
|
|
f856eef411 | ||
|
|
24d1ed15c4 | ||
|
|
26c4e7a182 | ||
|
|
8e55996b7c | ||
|
|
2a547a35a7 | ||
|
|
9a60182c86 | ||
|
|
107a915c67 | ||
|
|
41e847df18 | ||
|
|
7ccfb10392 | ||
|
|
d4614cf1ce | ||
|
|
a740dfe195 | ||
|
|
a73089c5f0 | ||
|
|
759f666177 | ||
|
|
e7883b13f4 | ||
|
|
f0015b8919 | ||
|
|
1115d50dce | ||
|
|
dc34bb36b3 | ||
|
|
8f8627a0c3 | ||
|
|
c2f8d43f9d | ||
|
|
6a482dc7f2 | ||
|
|
bc9f482b2a | ||
|
|
8659118a5e | ||
|
|
01db268dff | ||
|
|
6cb2b251c5 | ||
|
|
41e1ffe1c4 | ||
|
|
5a6638fed0 | ||
|
|
fd3293a0ee | ||
|
|
0f7ced491e | ||
|
|
52e48a90ea | ||
|
|
2a5bcffcc2 | ||
|
|
931efbbaa0 | ||
|
|
74468b2edb | ||
|
|
f2ba9e985b | ||
|
|
553ebaf7a9 | ||
|
|
5043992159 | ||
|
|
463bd32b26 | ||
|
|
a81e19a6eb | ||
|
|
db695709e6 | ||
|
|
62e37fe3ee | ||
|
|
c6e92c19b2 | ||
|
|
9865f38f69 | ||
|
|
8959cd47a8 | ||
|
|
c968fc7581 | ||
|
|
cbd9764601 | ||
|
|
9c1f6e2df5 | ||
|
|
e8bcec4529 | ||
|
|
c4392dd54c | ||
|
|
748bfb0b0e | ||
|
|
6510d8f20a | ||
|
|
c7d15770c1 | ||
|
|
48aec18ef7 | ||
|
|
7987b687dc | ||
|
|
b22e1d4273 | ||
|
|
92198b3624 | ||
|
|
facde2ca10 | ||
|
|
d85debfae4 | ||
|
|
db452ffc16 | ||
|
|
26f9ea8942 | ||
|
|
b0b9193603 | ||
|
|
d2bf27acd7 | ||
|
|
149ff1cd5a | ||
|
|
08bf892be6 | ||
|
|
ab316be813 | ||
|
|
5134e8e1ae | ||
|
|
4736eaf80d | ||
|
|
a404373112 | ||
|
|
5f9e30aacb | ||
|
|
7b76e85956 | ||
|
|
cb3044c09d | ||
|
|
009a68a795 | ||
|
|
f52b202747 | ||
|
|
a9ea07cbb3 | ||
|
|
e384cdaf70 | ||
|
|
18a5fb1fc5 | ||
|
|
bf02cdbf5c | ||
|
|
4d473bd618 | ||
|
|
496c91c250 | ||
|
|
91786f4cc6 | ||
|
|
359d9ec426 | ||
|
|
d45774fe03 | ||
|
|
0e11cd160d | ||
|
|
bb67a404fb | ||
|
|
4016aa755e | ||
|
|
b33c39334b | ||
|
|
6fe044de03 | ||
|
|
81fe98956c | ||
|
|
ab143fd1f8 | ||
|
|
eb0deb1301 | ||
|
|
8351afe61a | ||
|
|
edbbacecf9 | ||
|
|
1e5a3fb786 | ||
|
|
7f4a12c652 | ||
|
|
49bb418691 | ||
|
|
2540d6c511 | ||
|
|
fd7af10ff6 | ||
|
|
f304a2a459 | ||
|
|
fceea2bcd5 | ||
|
|
45297772c9 | ||
|
|
a22a3b1669 | ||
|
|
341492391e | ||
|
|
de6d807e3d | ||
|
|
134db23ea8 | ||
|
|
e8a709a315 | ||
|
|
dd9da95c70 | ||
|
|
824ceffedb | ||
|
|
e839035a7a | ||
|
|
3d832f5dca | ||
|
|
459039cf08 | ||
|
|
60470b186e | ||
|
|
c988905aa4 | ||
|
|
af2b300529 | ||
|
|
0e90c03e95 | ||
|
|
f293924be8 | ||
|
|
f9cc9481e2 | ||
|
|
542b812690 | ||
|
|
674fdd5e39 | ||
|
|
c9107bfb1a | ||
|
|
5a62edfc26 | ||
|
|
87fafec48a | ||
|
|
a490e8777f | ||
|
|
3a422ba496 | ||
|
|
6890f55d17 | ||
|
|
b54a0400ab | ||
|
|
a7001366a1 | ||
|
|
a084af819c | ||
|
|
5c9f7c00ba | ||
|
|
cf4ef6fbaf | ||
|
|
9c8fd98719 | ||
|
|
272096d611 | ||
|
|
834a8fc30f | ||
|
|
0f9ae7acd8 | ||
|
|
5605f8d003 | ||
|
|
1b42a51048 | ||
|
|
3f8b0b1cfe | ||
|
|
453df0c66c | ||
|
|
cedeaa8316 | ||
|
|
9ddbb430ef | ||
|
|
568f0699dc | ||
|
|
24aad68487 | ||
|
|
e4aa70b4c5 | ||
|
|
f6371f11a4 | ||
|
|
ce290651d0 | ||
|
|
1d29f79974 | ||
|
|
927af50c81 | ||
|
|
f06b1e8ef3 | ||
|
|
c2b1ad1418 | ||
|
|
9db2d3fa78 | ||
|
|
8a290f6a16 | ||
|
|
016f27739d | ||
|
|
c49a3af953 | ||
|
|
7d07e1b9c3 | ||
|
|
f8719c1aa1 | ||
|
|
448a9f414f | ||
|
|
86723bd032 | ||
|
|
0374d468c8 | ||
|
|
2157540f35 | ||
|
|
749ee526e8 | ||
|
|
b12ae95d1d | ||
|
|
1dd6ef9f24 | ||
|
|
5166d65ea2 | ||
|
|
9ada9f1093 | ||
|
|
d5e4fa21de | ||
|
|
45f95a18af | ||
|
|
8f58908f45 | ||
|
|
bdb519c5c1 | ||
|
|
7726691af9 | ||
|
|
eb5ab1f8e6 | ||
|
|
b2639ba961 | ||
|
|
c3a5bcb8d4 | ||
|
|
ff4f04de2a | ||
|
|
b24aac895f | ||
|
|
188bfacd05 | ||
|
|
4eaa855db9 | ||
|
|
44560e40ea | ||
|
|
1d83bb84ed | ||
|
|
9ebbdbabd3 | ||
|
|
6d8e67e7f9 | ||
|
|
eb1b1e6044 | ||
|
|
d48e17e1dd | ||
|
|
669a20084c | ||
|
|
6b8f7df843 | ||
|
|
37a906991e | ||
|
|
fd3e3ed05b | ||
|
|
ce12d59957 | ||
|
|
c70c747c04 | ||
|
|
6849c1030b | ||
|
|
947bd1ab91 | ||
|
|
c9a1cdec68 | ||
|
|
dba780a00c | ||
|
|
4f8f9f2f74 | ||
|
|
630d9dbc94 | ||
|
|
c3f6bcb89a | ||
|
|
62b02dc911 | ||
|
|
2122b961d3 | ||
|
|
56916e48fb | ||
|
|
ec4878be75 | ||
|
|
ac895c6d35 | ||
|
|
39e8652152 | ||
|
|
cc94a2e202 | ||
|
|
7c219b4917 | ||
|
|
b7052aa56a | ||
|
|
dc4d8d2838 | ||
|
|
5f9edd6ca6 | ||
|
|
498a10a492 | ||
|
|
bb73083132 | ||
|
|
912999e187 | ||
|
|
185a7cd276 | ||
|
|
b2b5b1e7f9 | ||
|
|
ca9a8ae1bd | ||
|
|
78032304ca | ||
|
|
a037e0317c | ||
|
|
3eb8f9028b | ||
|
|
85bfa1bd35 | ||
|
|
9bd8d5a929 | ||
|
|
8b0bbc6691 | ||
|
|
84a1152109 | ||
|
|
ba1bda607b | ||
|
|
1460965467 | ||
|
|
522220494a | ||
|
|
d2fd438713 | ||
|
|
8277eabcaf | ||
|
|
84b0f323b5 | ||
|
|
62287e64f7 | ||
|
|
306eda3809 | ||
|
|
1d6715544a | ||
|
|
4e4fabfc40 | ||
|
|
5e48023c6c | ||
|
|
a96e895bc9 | ||
|
|
55630293b9 | ||
|
|
59501db3bd | ||
|
|
0b9807d677 | ||
|
|
9db3f47b98 | ||
|
|
c7883a9ebe | ||
|
|
ab7a6bc2b9 | ||
|
|
dc434596e4 | ||
|
|
62dd62efd0 | ||
|
|
6907d9a989 | ||
|
|
959088280f | ||
|
|
5a66104adb | ||
|
|
3d9ea4721a | ||
|
|
ee94589556 | ||
|
|
06cfef3252 | ||
|
|
489e48b3d1 | ||
|
|
479273df3c | ||
|
|
9a1813e0bf | ||
|
|
e939a7689f | ||
|
|
fcd489a12a | ||
|
|
b2deaa21ba | ||
|
|
2b3f0026fe | ||
|
|
a941253a81 | ||
|
|
510b8df4b6 | ||
|
|
a342145ead | ||
|
|
0daf4a0b43 | ||
|
|
78c8161cf4 | ||
|
|
25092accc1 | ||
|
|
c4e81d06b7 | ||
|
|
fed6a738de | ||
|
|
133b65bc67 | ||
|
|
cc9c205d88 | ||
|
|
191e347415 | ||
|
|
2573007d13 | ||
|
|
9c33f8dcf4 | ||
|
|
1c80108a5b | ||
|
|
6f9ec93b4a | ||
|
|
890a3fe0a9 | ||
|
|
53d8141a00 | ||
|
|
534c4c194e | ||
|
|
9f16769aa3 | ||
|
|
d68a8204d2 | ||
|
|
7be70582d2 | ||
|
|
5543bd63b6 | ||
|
|
1a629da2c3 | ||
|
|
605d2dd751 | ||
|
|
34c36a7616 | ||
|
|
59824635da | ||
|
|
7608c92bb7 | ||
|
|
c7b302f43a | ||
|
|
526496a860 | ||
|
|
3c1e638757 | ||
|
|
ccafa0830f | ||
|
|
330a321f13 | ||
|
|
37a8e7e370 | ||
|
|
7a6abc6a11 | ||
|
|
83defc87c6 | ||
|
|
2e352bc48c | ||
|
|
c8b71a8b1e | ||
|
|
d43a081730 | ||
|
|
99d58fb600 | ||
|
|
e8f7d9c150 | ||
|
|
162b28bea1 | ||
|
|
019816dcf1 | ||
|
|
a11cc0c530 | ||
|
|
01c703d3e6 | ||
|
|
aaec3572e4 | ||
|
|
523c1bdb06 | ||
|
|
31b8352bc6 | ||
|
|
e98df72b44 | ||
|
|
7a882802b6 | ||
|
|
a8c3d57e0e | ||
|
|
5eef2381f0 | ||
|
|
81ae42fde0 | ||
|
|
2102aa0a3a | ||
|
|
5e42374cc8 | ||
|
|
e08155e86e | ||
|
|
d15e61a753 | ||
|
|
a5e722d85f | ||
|
|
a84ed747dc | ||
|
|
0001a42efc | ||
|
|
3feea76968 | ||
|
|
ef6480b696 | ||
|
|
28f9864b53 | ||
|
|
a7d3013334 | ||
|
|
80391ee2b8 | ||
|
|
c8939acc7f | ||
|
|
b03a464d58 | ||
|
|
a793689af0 | ||
|
|
635461389c | ||
|
|
95d4dc2eb8 | ||
|
|
3e7a53c5ca | ||
|
|
d2318c50cd | ||
|
|
1065ff7c17 | ||
|
|
1a9238ca5f | ||
|
|
5def8556c6 | ||
|
|
265993017d | ||
|
|
decd94f2a8 | ||
|
|
c7a9776d91 | ||
|
|
6a08bfbb30 | ||
|
|
fa44214c61 | ||
|
|
4a2c15be58 | ||
|
|
42312206c9 | ||
|
|
472e96622c | ||
|
|
5fa0d10608 | ||
|
|
c4096b8654 | ||
|
|
d48aa65684 | ||
|
|
cc48ec587f | ||
|
|
687a99a82e | ||
|
|
66addd85e7 | ||
|
|
b461dc4111 | ||
|
|
1a90ff7b67 | ||
|
|
69bfd93616 | ||
|
|
ccb924d55f | ||
|
|
55f571825a | ||
|
|
0fa6818d48 | ||
|
|
cb0d7805e2 | ||
|
|
7a4e1baca9 | ||
|
|
e6dccd8565 | ||
|
|
62461e23be | ||
|
|
f060064e38 | ||
|
|
09420fde81 | ||
|
|
5b6e516dd1 | ||
|
|
00ebea939a | ||
|
|
fd57c88931 | ||
|
|
5f95ede1fc | ||
|
|
49bc2f887c | ||
|
|
696a07e7eb | ||
|
|
1c90125ab2 | ||
|
|
d388acddaa | ||
|
|
3aa83fe595 | ||
|
|
ced720ad99 | ||
|
|
df07a8ae65 | ||
|
|
67537b99c8 |
+174
-40
@@ -1,9 +1,13 @@
|
||||
# Copyright 2007 NLnet Labs
|
||||
# See the file LICENSE for the license
|
||||
#
|
||||
# Standard installation pathnames
|
||||
|
||||
QUIET=yes
|
||||
debug_enabled=@debug_enabled@
|
||||
ifeq ($(debug_enabled),yes)
|
||||
QUIET=yes
|
||||
else
|
||||
QUIET=no
|
||||
endif
|
||||
|
||||
ifeq "$(QUIET)" "yes"
|
||||
Q=@
|
||||
INFO=@echo
|
||||
@@ -20,18 +24,21 @@ exec_prefix=@exec_prefix@
|
||||
bindir=@bindir@
|
||||
mandir=@mandir@
|
||||
libdir=@libdir@
|
||||
# datarootdir is here to please some checkers, use datadir.
|
||||
datarootdir=@datarootdir@
|
||||
datadir=@datadir@
|
||||
includedir=@includedir@
|
||||
doxygen=@doxygen@
|
||||
libtool=@libtool@
|
||||
ldnsdir=@ldnsdir@
|
||||
staticexe=@staticexe@
|
||||
configfile=@ub_conf_file@
|
||||
|
||||
YACC=@YACC@
|
||||
LEX=@LEX@
|
||||
CC=@CC@
|
||||
CPPFLAGS=-I. @CPPFLAGS@ @DEFS@
|
||||
CFLAGS=-I. @CFLAGS@
|
||||
CPPFLAGS=-I$(srcdir) -I. @CPPFLAGS@ @DEFS@
|
||||
CFLAGS=@CFLAGS@
|
||||
LDFLAGS=@LDFLAGS@
|
||||
LIBS=@LIBS@
|
||||
LIBOBJS=@LIBOBJS@
|
||||
@@ -44,60 +51,138 @@ endif
|
||||
BUILD=build/
|
||||
|
||||
LINT=splint
|
||||
LINTFLAGS=+quiet -weak -warnposix -unrecog -Din_addr_t=uint32_t -Du_int=unsigned -Du_char=uint8_t -preproc -Drlimit=rlimit64
|
||||
LINTFLAGS=+quiet -weak -warnposix -unrecog -Din_addr_t=uint32_t -Du_int=unsigned -Du_char=uint8_t -preproc -Drlimit=rlimit64 -D__gnuc_va_list=va_list
|
||||
# compat with openssl linux edition.
|
||||
LINTFLAGS+="-DBN_ULONG=unsigned long" -Dkrb5_int32=int "-Dkrb5_ui_4=unsigned int" -DPQ_64BIT=uint64_t
|
||||
LINTFLAGS+="-DBN_ULONG=unsigned long" -Dkrb5_int32=int "-Dkrb5_ui_4=unsigned int" -DPQ_64BIT=uint64_t -DRC4_INT=unsigned
|
||||
|
||||
INSTALL=$(srcdir)/install-sh
|
||||
INSTALL=$(srcdir)/install-sh
|
||||
|
||||
COMMON_SRC=$(wildcard services/*.c util/*.c util/data/*.c util/storage/*.c) util/configparser.c util/configlexer.c testcode/checklocks.c
|
||||
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))
|
||||
UNITTEST_SRC=$(wildcard testcode/unit*.c) testcode/readhex.c $(COMMON_SRC)
|
||||
UNITTEST_SRC=$(patsubst $(srcdir)/%,%, \
|
||||
$(wildcard $(srcdir)/testcode/unit*.c)) \
|
||||
testcode/readhex.c testcode/ldns-testpkts.c smallapp/worker_cb.c \
|
||||
$(COMMON_SRC)
|
||||
UNITTEST_OBJ=$(addprefix $(BUILD),$(UNITTEST_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
DAEMON_SRC=$(wildcard daemon/*.c) $(COMMON_SRC)
|
||||
DAEMON_SRC=$(patsubst $(srcdir)/%,%, $(wildcard $(srcdir)/daemon/*.c)) \
|
||||
$(COMMON_SRC)
|
||||
DAEMON_OBJ=$(addprefix $(BUILD),$(DAEMON_SRC:.c=.o)) $(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 services/listen_dnsport.c services/outside_network.c, $(COMMON_SRC))
|
||||
CHECKCONF_SRC=smallapp/unbound-checkconf.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
CHECKCONF_OBJ=$(addprefix $(BUILD),$(CHECKCONF_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
HOST_SRC=smallapp/unbound-host.c
|
||||
HOST_OBJ=$(addprefix $(BUILD),$(HOST_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
TESTBOUND_SRC=testcode/testbound.c testcode/ldns-testpkts.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 $(COMMON_SRC)
|
||||
LOCKVERIFY_SRC=testcode/lock_verify.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
LOCKVERIFY_OBJ=$(addprefix $(BUILD),$(LOCKVERIFY_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
PKTVIEW_SRC=testcode/pktview.c testcode/readhex.c $(COMMON_SRC)
|
||||
PKTVIEW_SRC=testcode/pktview.c testcode/readhex.c smallapp/worker_cb.c \
|
||||
$(COMMON_SRC)
|
||||
PKTVIEW_OBJ=$(addprefix $(BUILD),$(PKTVIEW_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) $(TESTBOUND_SRC) $(LOCKVERIFY_SRC)
|
||||
ALL_OBJ=$(addprefix $(BUILD),$(ALL_SRC:.c=.o) $(addprefix compat/,$(LIBOBJS))) $(COMPAT_OBJ)
|
||||
SIGNIT_SRC=testcode/signit.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
SIGNIT_OBJ=$(addprefix $(BUILD),$(SIGNIT_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
MEMSTATS_SRC=testcode/memstats.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
MEMSTATS_OBJ=$(addprefix $(BUILD),$(MEMSTATS_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
ASYNCLOOK_SRC=testcode/asynclook.c
|
||||
ASYNCLOOK_OBJ=$(addprefix $(BUILD),$(ASYNCLOOK_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
STREAMTCP_SRC=testcode/streamtcp.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
STREAMTCP_OBJ=$(addprefix $(BUILD),$(STREAMTCP_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
LIBUNBOUND_SRC=$(patsubst $(srcdir)/%,%, \
|
||||
$(wildcard $(srcdir)/libunbound/*.c) $(COMMON_SRC))
|
||||
LIBUNBOUND_OBJ=$(addprefix $(BUILD),$(LIBUNBOUND_SRC:.c=.o)) $(COMPAT_OBJ)
|
||||
ALL_SRC=$(sort $(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) \
|
||||
$(TESTBOUND_SRC) $(LOCKVERIFY_SRC) $(PKTVIEW_SRC) $(SIGNIT_SRC) \
|
||||
$(MEMSTATS_SRC) $(CHECKCONF_SRC) $(LIBUNBOUND_SRC) $(HOST_SRC) \
|
||||
$(ASYNCLOOK_SRC) $(STREAMTCP_SRC))
|
||||
ALL_OBJ=$(addprefix $(BUILD),$(ALL_SRC:.c=.o) \
|
||||
$(addprefix compat/,$(LIBOBJS))) $(COMPAT_OBJ)
|
||||
|
||||
COMPILE=$(LIBTOOL) --tag=CC --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS)
|
||||
LINK=$(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) $(LDFLAGS)
|
||||
LINK_LIB=$(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) $(LDFLAGS) -release $(VERSION)
|
||||
LINK=$(LIBTOOL) --tag=CC --mode=link $(CC) $(staticexe) $(RUNTIME_PATH) $(CFLAGS) $(LDFLAGS)
|
||||
LINK_LIB=$(LIBTOOL) --tag=CC --mode=link $(CC) $(RUNTIME_PATH) $(CFLAGS) $(LDFLAGS) $(staticexe) -release $(VERSION) -no-undefined
|
||||
|
||||
.PHONY: clean realclean doc lint all install uninstall tests test download_ldns strip lib
|
||||
|
||||
$(BUILD)%.o: $(srcdir)/%.c
|
||||
$(INFO) Build $<
|
||||
@if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
@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$(COMPILE) -c $< -o $@
|
||||
|
||||
.PHONY: clean realclean doc lint all
|
||||
all: $(COMMON_OBJ) unbound unbound-checkconf lib unbound-host
|
||||
|
||||
all: $(COMMON_OBJ) unbound unittest testbound lock-verify pktview
|
||||
tests: all unittest testbound lock-verify pktview signit memstats \
|
||||
asynclook streamtcp
|
||||
|
||||
unbound: $(DAEMON_OBJ)
|
||||
test: tests
|
||||
bash testcode/do-tests.sh
|
||||
|
||||
lib: libunbound.la
|
||||
|
||||
libunbound.la: $(LIBUNBOUND_OBJ)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $^ $(LIBS)
|
||||
$Q$(LINK_LIB) --export-symbols $(srcdir)/libunbound/ubsyms.def -o $@ $(sort $(LIBUNBOUND_OBJ:.o=.lo)) -rpath $(libdir) $(LIBS)
|
||||
|
||||
unittest: $(UNITTEST_OBJ)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $^ $(LIBS)
|
||||
ifeq ($(patsubst ldns-src%,ldns-src,$(ldnsdir)),ldns-src)
|
||||
ldnslib=$(ldnsdir)/lib/libldns.a
|
||||
$(ldnslib):
|
||||
@if test ! -z "$(ldnsdir)"; \
|
||||
then (cd $(ldnsdir) && $(MAKE)); fi
|
||||
else
|
||||
ldnslib=
|
||||
endif
|
||||
|
||||
testbound: $(TESTBOUND_OBJ)
|
||||
unbound: $(DAEMON_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $^ $(LIBS)
|
||||
$Q$(LINK) -o $@ $(sort $(DAEMON_OBJ)) $(LIBS)
|
||||
|
||||
lock-verify: $(LOCKVERIFY_OBJ)
|
||||
unbound-checkconf: $(CHECKCONF_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $^ $(LIBS)
|
||||
$Q$(LINK) -o $@ $(sort $(CHECKCONF_OBJ)) $(LIBS)
|
||||
|
||||
pktview: $(PKTVIEW_OBJ)
|
||||
unbound-host: $(HOST_OBJ) libunbound.la $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $^ $(LIBS)
|
||||
$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)
|
||||
|
||||
lock-verify: $(LOCKVERIFY_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(LOCKVERIFY_OBJ)) $(LIBS)
|
||||
|
||||
pktview: $(PKTVIEW_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(PKTVIEW_OBJ)) $(LIBS)
|
||||
|
||||
signit: $(SIGNIT_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(SIGNIT_OBJ)) $(LIBS)
|
||||
|
||||
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)
|
||||
|
||||
#testcode/ldns-testpkts.c: $(ldnsdir)/examples/ldns-testpkts.c \
|
||||
# $(ldnsdir)/examples/ldns-testpkts.h
|
||||
@@ -107,13 +192,17 @@ pktview: $(PKTVIEW_OBJ)
|
||||
util/config_file.c: util/configparser.h
|
||||
util/configlexer.c: $(srcdir)/util/configlexer.lex util/configparser.h
|
||||
$(INFO) Lex $<
|
||||
@if test ! -d util; then $(INSTALL) -d util; fi
|
||||
ifeq "$(strip $(LEX))" ":"
|
||||
$Qecho "rebuild lexer, but no lex program, skipped"
|
||||
else
|
||||
@-if test ! -d util; then $(INSTALL) -d util; fi
|
||||
$Qecho "#include \"util/configyyrename.h\"" > $@
|
||||
$Q$(LEX) -i -t $< >> $@
|
||||
$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:
|
||||
@@ -124,11 +213,12 @@ 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
|
||||
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 $@
|
||||
|
||||
@@ -138,16 +228,60 @@ 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-host
|
||||
|
||||
install:
|
||||
$(INSTALL) -m 755 -d $(bindir)
|
||||
$(INSTALL) -m 755 -d $(mandir)
|
||||
$(INSTALL) -m 755 -d $(mandir)/man8
|
||||
$(INSTALL) -m 755 -d $(mandir)/man5
|
||||
$(INSTALL) -m 755 -d $(libdir)
|
||||
$(INSTALL) -m 755 -d $(includedir)
|
||||
$(LIBTOOL) --mode=install cp unbound $(bindir)/unbound
|
||||
$(LIBTOOL) --mode=install cp unbound-checkconf $(bindir)/unbound-checkconf
|
||||
$(LIBTOOL) --mode=install cp unbound-host $(bindir)/unbound-host
|
||||
$(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
|
||||
$(LIBTOOL) --mode=install cp $(srcdir)/libunbound/unbound.h $(includedir)/unbound.h
|
||||
$(LIBTOOL) --mode=install cp libunbound.la $(libdir)
|
||||
$(LIBTOOL) --mode=finish $(libdir)
|
||||
|
||||
uninstall:
|
||||
rm -f -- $(bindir)/unbound $(bindir)/unbound-checkconf $(bindir)/unbound-host
|
||||
rm -f -- $(mandir)/man8/unbound.8 $(mandir)/man8/unbound-checkconf.8 $(mandir)/man5/unbound.conf.5
|
||||
rm -f -- $(includedir)/unbound.h
|
||||
$(LIBTOOL) --mode=uninstall rm -f $(libdir)/libunbound.la
|
||||
@echo
|
||||
@echo "You still need to remove `dirname $(configfile)` , $(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
|
||||
|
||||
# Automatic dependencies.
|
||||
$(BUILD)%.d: $(srcdir)/%.c
|
||||
$(INFO) Depend $<
|
||||
@if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
@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'\'' > $@; \
|
||||
[ -s $@ ] || rm -f $@'
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(MAKECMDGOALS),realclean)
|
||||
ifeq ($(debug_enabled),yes)
|
||||
-include $(addprefix $(BUILD),$(ALL_SRC:.c=.d))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
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.
|
||||
* 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
+5
-3
@@ -1,7 +1,7 @@
|
||||
# generated automatically by aclocal 1.9.6 -*- Autoconf -*-
|
||||
# generated automatically by aclocal 1.10 -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
|
||||
# 2005 Free Software Foundation, Inc.
|
||||
# 2005, 2006 Free Software Foundation, Inc.
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
@@ -1597,7 +1597,7 @@ linux*)
|
||||
|
||||
# Append ld.so.conf contents to the search path
|
||||
if test -f /etc/ld.so.conf; then
|
||||
lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
|
||||
lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/^[ ]*//;s/#.*//;/^[^\/]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
|
||||
sys_lib_dlsearch_path_spec="/lib${libsuff} /usr/lib${libsuff} $lt_ld_extra"
|
||||
fi
|
||||
|
||||
@@ -6370,6 +6370,7 @@ do
|
||||
done
|
||||
done
|
||||
done
|
||||
IFS=$as_save_IFS
|
||||
lt_ac_max=0
|
||||
lt_ac_count=0
|
||||
# Add /usr/xpg4/bin/sed as it is typically found on Solaris
|
||||
@@ -6402,6 +6403,7 @@ for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
|
||||
done
|
||||
])
|
||||
SED=$lt_cv_path_SED
|
||||
AC_SUBST([SED])
|
||||
AC_MSG_RESULT([$SED])
|
||||
])
|
||||
|
||||
|
||||
+1500
File diff suppressed because it is too large
Load Diff
+431
@@ -0,0 +1,431 @@
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Pathname to the Unbound configuration file */
|
||||
#undef CONFIGFILE
|
||||
|
||||
/* Define if you want to use debug lock checking (slow). */
|
||||
#undef ENABLE_LOCK_CHECKS
|
||||
|
||||
/* Define to 1 if you have the <arpa/inet.h> header file. */
|
||||
#undef HAVE_ARPA_INET_H
|
||||
|
||||
/* Whether the C compiler accepts the "format" attribute */
|
||||
#undef HAVE_ATTR_FORMAT
|
||||
|
||||
/* Whether the C compiler accepts the "unused" attribute */
|
||||
#undef HAVE_ATTR_UNUSED
|
||||
|
||||
/* Define to 1 if your system has a working `chown' function. */
|
||||
#undef HAVE_CHOWN
|
||||
|
||||
/* 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_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 `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. */
|
||||
#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 `inet_aton' function. */
|
||||
#undef HAVE_INET_ATON
|
||||
|
||||
/* Define to 1 if you have the `inet_ntop' function. */
|
||||
#undef HAVE_INET_NTOP
|
||||
|
||||
/* Define to 1 if you have the `inet_pton' function. */
|
||||
#undef HAVE_INET_PTON
|
||||
|
||||
/* 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
|
||||
|
||||
/* 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. */
|
||||
#undef HAVE_MALLOC
|
||||
|
||||
/* Define to 1 if you have the `memmove' function. */
|
||||
#undef HAVE_MEMMOVE
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_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
|
||||
|
||||
/* Define if you have POSIX threads libraries and header files. */
|
||||
#undef HAVE_PTHREAD
|
||||
|
||||
/* Define to 1 if the system has the type `pthread_rwlock_t'. */
|
||||
#undef HAVE_PTHREAD_RWLOCK_T
|
||||
|
||||
/* Define to 1 if the system has the type `pthread_spinlock_t'. */
|
||||
#undef HAVE_PTHREAD_SPINLOCK_T
|
||||
|
||||
/* Define to 1 if you have the `snprintf' function. */
|
||||
#undef HAVE_SNPRINTF
|
||||
|
||||
/* Using Solaris threads */
|
||||
#undef HAVE_SOLARIS_THREADS
|
||||
|
||||
/* Define if you have the SSL libraries installed. */
|
||||
#undef HAVE_SSL
|
||||
|
||||
/* Define to 1 if you have the <stdarg.h> header file. */
|
||||
#undef HAVE_STDARG_H
|
||||
|
||||
/* Define to 1 if you have the <stdbool.h> header file. */
|
||||
#undef HAVE_STDBOOL_H
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the `strlcpy' function. */
|
||||
#undef HAVE_STRLCPY
|
||||
|
||||
/* Define to 1 if you have the <syslog.h> header file. */
|
||||
#undef HAVE_SYSLOG_H
|
||||
|
||||
/* 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/socket.h> header file. */
|
||||
#undef HAVE_SYS_SOCKET_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the <sys/uio.h> header file. */
|
||||
#undef HAVE_SYS_UIO_H
|
||||
|
||||
/* Define to 1 if you have the <time.h> header file. */
|
||||
#undef HAVE_TIME_H
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* 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 `fork' works. */
|
||||
#undef HAVE_WORKING_FORK
|
||||
|
||||
/* Define to 1 if `vfork' works. */
|
||||
#undef HAVE_WORKING_VFORK
|
||||
|
||||
/* Define to the maximum message length to pass to syslog. */
|
||||
#undef MAXSYSLOGMSGLEN
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to necessary symbol if this constant uses a non-standard name on
|
||||
your system. */
|
||||
#undef PTHREAD_CREATE_JOINABLE
|
||||
|
||||
/* Define as the return type of signal handlers (`int' or `void'). */
|
||||
#undef RETSIGTYPE
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* 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
|
||||
|
||||
/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
|
||||
`char[]'. */
|
||||
#undef YYTEXT_POINTER
|
||||
|
||||
/* Define to 1 if on AIX 3.
|
||||
System headers sometimes define this.
|
||||
We just want to avoid a redefinition error message. */
|
||||
#ifndef _ALL_SOURCE
|
||||
# undef _ALL_SOURCE
|
||||
#endif
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#undef _FILE_OFFSET_BITS
|
||||
|
||||
/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
|
||||
#undef _LARGEFILE_SOURCE
|
||||
|
||||
/* Define for large files, on AIX-style hosts. */
|
||||
#undef _LARGE_FILES
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
#undef const
|
||||
|
||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||
#undef gid_t
|
||||
|
||||
/* in_addr_t */
|
||||
#undef in_addr_t
|
||||
|
||||
/* in_port_t */
|
||||
#undef in_port_t
|
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||
#ifndef __cplusplus
|
||||
#undef inline
|
||||
#endif
|
||||
|
||||
/* Define to `short' if <sys/types.h> does not define. */
|
||||
#undef int16_t
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
#undef int32_t
|
||||
|
||||
/* Define to `long long' if <sys/types.h> does not define. */
|
||||
#undef int64_t
|
||||
|
||||
/* Define to `char' if <sys/types.h> does not define. */
|
||||
#undef int8_t
|
||||
|
||||
/* Define to rpl_malloc if the replacement function should be used. */
|
||||
#undef malloc
|
||||
|
||||
/* Define to `long int' if <sys/types.h> does not define. */
|
||||
#undef off_t
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
#undef pid_t
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
#undef size_t
|
||||
|
||||
/* Define to 'int' if not defined */
|
||||
#undef socklen_t
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
#undef ssize_t
|
||||
|
||||
/* Define to `unsigned char' if <sys/types.h> does not define. */
|
||||
#undef u_char
|
||||
|
||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||
#undef uid_t
|
||||
|
||||
/* Define to `unsigned short' if <sys/types.h> does not define. */
|
||||
#undef uint16_t
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
#undef uint32_t
|
||||
|
||||
/* Define to `unsigned long long' if <sys/types.h> does not define. */
|
||||
#undef uint64_t
|
||||
|
||||
/* Define to `unsigned char' if <sys/types.h> does not define. */
|
||||
#undef uint8_t
|
||||
|
||||
/* Define as `fork' if `vfork' does not work. */
|
||||
#undef vfork
|
||||
|
||||
|
||||
#ifndef UNBOUND_DEBUG
|
||||
# define NDEBUG
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if STDC_HEADERS
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#if HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_UIO_H
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENSSL_ERR_H
|
||||
#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)))
|
||||
#else /* !HAVE_ATTR_FORMAT */
|
||||
# define ATTR_FORMAT(archetype, string_index, first_to_check) /* empty */
|
||||
#endif /* !HAVE_ATTR_FORMAT */
|
||||
#if defined(DOXYGEN)
|
||||
# define ATTR_UNUSED(x) x
|
||||
#elif defined(__cplusplus)
|
||||
# define ATTR_UNUSED(x)
|
||||
#elif defined(HAVE_ATTR_UNUSED)
|
||||
# define ATTR_UNUSED(x) x __attribute__((unused))
|
||||
#else /* !HAVE_ATTR_UNUSED */
|
||||
# define ATTR_UNUSED(x) x
|
||||
#endif /* !HAVE_ATTR_UNUSED */
|
||||
#ifndef HAVE_FSEEKO
|
||||
#define fseeko fseek
|
||||
#define ftello ftell
|
||||
#endif /* HAVE_FSEEKO */
|
||||
#if defined(HAVE_EVENT_H) && !defined(HAVE_EVENT_BASE_ONCE) && (defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS))
|
||||
/* using version of libevent that is not threadsafe. */
|
||||
# define LIBEVENT_SIGNAL_PROBLEM 1
|
||||
#endif
|
||||
|
||||
#ifndef RAND_MAX
|
||||
#define RAND_MAX 2147483647
|
||||
#endif
|
||||
|
||||
#ifndef IPV6_MIN_MTU
|
||||
#define IPV6_MIN_MTU 1280
|
||||
#endif /* IPV6_MIN_MTU */
|
||||
|
||||
#ifndef CHECKED_INET6
|
||||
# define CHECKED_INET6
|
||||
# ifdef AF_INET6
|
||||
# define INET6
|
||||
# else
|
||||
# define AF_INET6 28
|
||||
# endif
|
||||
#endif /* CHECKED_INET6 */
|
||||
|
||||
/* maximum nesting of included files */
|
||||
#define MAXINCLUDES 10
|
||||
|
||||
#ifndef HAVE_SNPRINTF
|
||||
#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
|
||||
int inet_pton(int af, const char* src, void* dst);
|
||||
#endif /* HAVE_INET_PTON */
|
||||
#ifndef HAVE_INET_NTOP
|
||||
const char *inet_ntop(int af, const void *src, char *dst, size_t size);
|
||||
#endif
|
||||
#ifndef HAVE_INET_ATON
|
||||
int inet_aton(const char *cp, struct in_addr *addr);
|
||||
#endif
|
||||
#ifndef HAVE_MEMMOVE
|
||||
void *memmove(void *dest, const void *src, size_t n);
|
||||
#endif
|
||||
#ifndef HAVE_STRLCPY
|
||||
size_t strlcpy(char *dst, const char *src, size_t siz);
|
||||
#endif
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
struct sockaddr_storage;
|
||||
#include "compat/fake-rfc2553.h"
|
||||
#endif
|
||||
|
||||
#include "ldns/ldns.h"
|
||||
|
||||
#ifdef UNBOUND_ALLOC_STATS
|
||||
# define malloc(s) unbound_stat_malloc_log(s, __FILE__, __LINE__, __func__)
|
||||
# define calloc(n,s) unbound_stat_calloc_log(n, s, __FILE__, __LINE__, __func__)
|
||||
# define free(p) unbound_stat_free_log(p, __FILE__, __LINE__, __func__)
|
||||
# define realloc(p,s) unbound_stat_realloc_log(p, s, __FILE__, __LINE__, __func__)
|
||||
void *unbound_stat_malloc(size_t size);
|
||||
void *unbound_stat_calloc(size_t nmemb, size_t size);
|
||||
void unbound_stat_free(void *ptr);
|
||||
void *unbound_stat_realloc(void *ptr, size_t size);
|
||||
void *unbound_stat_malloc_log(size_t size, const char* file, int line,
|
||||
const char* func);
|
||||
void *unbound_stat_calloc_log(size_t nmemb, size_t size, const char* file,
|
||||
int line, const char* func);
|
||||
void unbound_stat_free_log(void *ptr, const char* file, int line,
|
||||
const char* func);
|
||||
void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
|
||||
int line, const char* func);
|
||||
#endif /* UNBOUND_ALLOC_STATS */
|
||||
|
||||
/** default port for DNS traffic. */
|
||||
#define UNBOUND_DNS_PORT 53
|
||||
|
||||
+1616
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+267
-65
@@ -2,11 +2,40 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
AC_PREREQ(2.57)
|
||||
|
||||
AC_INIT(unbound, 0.3, wouter@nlnetlabs.nl, unbound)
|
||||
AC_INIT(unbound, 0.9, unbound-bugs@nlnetlabs.nl, unbound)
|
||||
|
||||
CFLAGS=
|
||||
AC_AIX
|
||||
|
||||
dnl
|
||||
dnl By default set $sysconfdir to /etc and $localstatedir to /var
|
||||
dnl
|
||||
case "$prefix" in
|
||||
NONE)
|
||||
case "$sysconfdir" in
|
||||
'${prefix}/etc')
|
||||
sysconfdir=/etc
|
||||
;;
|
||||
esac
|
||||
case "$localstatedir" in
|
||||
'${prefix}/var')
|
||||
localstatedir=/var
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
#
|
||||
# Determine configuration file
|
||||
ub_conf_file=${sysconfdir}/unbound/unbound.conf
|
||||
AC_ARG_WITH([conf_file],
|
||||
AC_HELP_STRING([--with-conf-file=path], [Pathname to the Unbound configuration file]),
|
||||
[ub_conf_file=$withval])
|
||||
AC_SUBST(ub_conf_file)
|
||||
# the eval is to evaluate shell expansion twice, once
|
||||
# for $nsd_conf_file and once for the ${prefix} within it.
|
||||
AC_DEFINE_UNQUOTED(CONFIGFILE, ["`eval echo $ub_conf_file`"], [Pathname to the Unbound configuration file])
|
||||
|
||||
dnl routine to help check for compiler flags.
|
||||
AC_DEFUN([CHECK_COMPILER_FLAG],
|
||||
[
|
||||
@@ -90,6 +119,39 @@ CHECK_COMPILER_FLAG(O2, [CFLAGS="$CFLAGS -O2"])
|
||||
|
||||
AC_CHECK_HEADERS([getopt.h time.h],,, [AC_INCLUDES_DEFAULT])
|
||||
|
||||
CHECK_COMPILER_FLAG_NEEDED(-std=c99 -D__EXTENSIONS__ -D_BSD_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE,
|
||||
[
|
||||
#include "confdefs.h"
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/time.h>
|
||||
#ifdef HAVE_TIME_H
|
||||
#include <time.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
int test() {
|
||||
int a;
|
||||
char **opts = NULL;
|
||||
struct timeval tv;
|
||||
char *t;
|
||||
time_t time = 0;
|
||||
char *buf = NULL;
|
||||
const char* str = NULL;
|
||||
t = ctime_r(&time, buf);
|
||||
tv.tv_usec = 10;
|
||||
srandom(32);
|
||||
a = getopt(2, opts, "a");
|
||||
a = isascii(32);
|
||||
str = gai_strerror(0);
|
||||
return a;
|
||||
}
|
||||
], [CFLAGS="$CFLAGS -std=c99 -D__EXTENSIONS__ -D_BSD_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE"])
|
||||
|
||||
CHECK_COMPILER_FLAG_NEEDED(-std=c99 -D__EXTENSIONS__ -D_BSD_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE,
|
||||
[
|
||||
#include "confdefs.h"
|
||||
@@ -194,10 +256,21 @@ int test() {
|
||||
CHECK_COMPILER_FLAG(xO4, [CFLAGS="$CFLAGS -xO4"])
|
||||
CHECK_COMPILER_FLAG(xtarget=generic, [CFLAGS="$CFLAGS -xtarget=generic"])
|
||||
# flag warnings.
|
||||
CHECK_COMPILER_FLAG(W, [CFLAGS="$CFLAGS -W"])
|
||||
CHECK_COMPILER_FLAG(Wall, [CFLAGS="$CFLAGS -Wall"])
|
||||
CHECK_COMPILER_FLAG(Wextra, [CFLAGS="$CFLAGS -Wextra"])
|
||||
CHECK_COMPILER_FLAG(Wdeclaration-after-statement, [CFLAGS="$CFLAGS -Wdeclaration-after-statement"])
|
||||
AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [Enable debug warnings, asserts, makefile-dependencies]))
|
||||
debug_enabled="$enable_debug"
|
||||
AC_SUBST(debug_enabled)
|
||||
case "$enable_debug" in
|
||||
yes)
|
||||
CHECK_COMPILER_FLAG(W, [CFLAGS="$CFLAGS -W"])
|
||||
CHECK_COMPILER_FLAG(Wall, [CFLAGS="$CFLAGS -Wall"])
|
||||
CHECK_COMPILER_FLAG(Wextra, [CFLAGS="$CFLAGS -Wextra"])
|
||||
CHECK_COMPILER_FLAG(Wdeclaration-after-statement, [CFLAGS="$CFLAGS -Wdeclaration-after-statement"])
|
||||
AC_DEFINE([UNBOUND_DEBUG], [], [define this to enable debug checks.])
|
||||
;;
|
||||
no|*)
|
||||
# nothing to do.
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_C_INLINE
|
||||
|
||||
@@ -248,9 +321,18 @@ AC_CHECK_FORMAT_ATTRIBUTE
|
||||
AC_CHECK_UNUSED_ATTRIBUTE
|
||||
|
||||
if test "$srcdir" != "."; then
|
||||
CPPFLAGS="$CPPFLAGS -I$srcdir";
|
||||
CPPFLAGS="$CPPFLAGS -I$srcdir"
|
||||
fi
|
||||
|
||||
# skip these tests, we do not need them.
|
||||
AC_DEFUN([AC_PROG_F77], [:])
|
||||
AC_DEFUN([AC_PROG_FC], [:])
|
||||
AC_DEFUN([AC_PROG_CXX], [:])
|
||||
AC_DEFUN([AC_PROG_CXXCPP], [:])
|
||||
AC_DEFUN([AC_PROG_OBJC], [:])
|
||||
AC_DEFUN([AC_PROG_OBJCCPP], [:])
|
||||
AC_DEFUN([AC_LIBTOOL_CXX], [:])
|
||||
AC_DEFUN([AC_LIBTOOL_F77], [:])
|
||||
# Use libtool
|
||||
AC_CHECK_PROGS(libtool, [glibtool libtool15 libtool], [./libtool])
|
||||
AC_PATH_TOOL(AR, ar, [false])
|
||||
@@ -269,7 +351,7 @@ AC_PROG_LIBTOOL
|
||||
|
||||
# Checks for header files.
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS([stdarg.h stdbool.h netinet/in.h sys/param.h sys/socket.h sys/uio.h arpa/inet.h],,, [AC_INCLUDES_DEFAULT])
|
||||
AC_CHECK_HEADERS([stdarg.h stdbool.h netinet/in.h sys/param.h sys/socket.h sys/uio.h arpa/inet.h syslog.h],,, [AC_INCLUDES_DEFAULT])
|
||||
|
||||
# check for types
|
||||
AC_CHECK_TYPE(int8_t, char)
|
||||
@@ -313,47 +395,50 @@ AC_INCLUDES_DEFAULT
|
||||
#endif
|
||||
])
|
||||
|
||||
# Check the type of struct msghdr msg_iovlen.
|
||||
AC_MSG_CHECKING([sign of struct msghdr.msg_iovlen])
|
||||
AC_RUN_IFELSE([
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
int main()
|
||||
{
|
||||
struct msghdr h;
|
||||
h.msg_iovlen = -1;
|
||||
if(10/h.msg_iovlen == 0) return 0; /* unsigned */
|
||||
return 1; /* signed */
|
||||
}
|
||||
], [
|
||||
# if compiled and exit status 0
|
||||
cv_msgiovlen_type="unsigned" ], [
|
||||
# not compiled or bad exit status
|
||||
cv_msgiovlen_type="int" ],
|
||||
# when cross compiling
|
||||
[ cv_msgiovlen_type="int" ])
|
||||
AC_MSG_RESULT($cv_msgiovlen_type)
|
||||
AC_DEFINE_UNQUOTED([TYPE_MSGIOVLEN], $cv_msgiovlen_type, [Define to 'int' or type of struct msghdr.msg_iovlen.])
|
||||
# Check for yyunput
|
||||
AC_DEFUN([CHECK_YYUNPUT],
|
||||
[AC_MSG_CHECKING(whether lex can not generate yyunput)
|
||||
AC_CACHE_VAL(ac_cv_c_yy_unput,
|
||||
[ac_cv_c_yy_unput=no
|
||||
if $LEX --nounput -h 2>&1 | grep "do not generate" 2>&1 >/dev/null; then
|
||||
ac_cv_c_yy_unput="yes"
|
||||
else
|
||||
ac_cv_c_yy_unput="no"
|
||||
fi
|
||||
])
|
||||
|
||||
AC_MSG_RESULT($ac_cv_c_yy_unput)
|
||||
if test $ac_cv_c_yy_unput = yes; then
|
||||
LEX="$LEX --nounput"
|
||||
fi
|
||||
])dnl
|
||||
|
||||
CHECK_YYUNPUT
|
||||
|
||||
# add option to disable the evil rpath
|
||||
dnl Check whether to use rpath or not
|
||||
AC_ARG_ENABLE(rpath,
|
||||
[ --disable-rpath disable hardcoded rpath (default=enabled)],
|
||||
enable_rpath=$enableval, enable_rpath=yes)
|
||||
|
||||
# Checks for libraries.
|
||||
AC_ARG_WITH(ssl, AC_HELP_STRING([--with-ssl=pathname],
|
||||
[enable SSL (will check /usr/local/ssl
|
||||
/usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr/sfw /usr)]),[
|
||||
/usr/lib/ssl /usr/ssl /usr/pkg /usr/local /opt/local /usr/sfw /usr)]),[
|
||||
],[
|
||||
withval="yes"
|
||||
])
|
||||
if test x_$withval != x_no; then
|
||||
AC_MSG_CHECKING(for SSL)
|
||||
if test x_$withval = x_ -o x_$withval = x_yes; then
|
||||
withval="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr/sfw /usr"
|
||||
withval="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /opt/local /usr/sfw /usr"
|
||||
fi
|
||||
for dir in $withval; do
|
||||
ssldir="$dir"
|
||||
if test -f "$dir/include/openssl/ssl.h"; then
|
||||
found_ssl="yes";
|
||||
found_ssl="yes"
|
||||
AC_DEFINE_UNQUOTED([HAVE_SSL], [], [Define if you have the SSL libraries installed.])
|
||||
CPPFLAGS="$CPPFLAGS -I$ssldir/include";
|
||||
CPPFLAGS="$CPPFLAGS -I$ssldir/include"
|
||||
break;
|
||||
fi
|
||||
done
|
||||
@@ -362,8 +447,10 @@ AC_ARG_WITH(ssl, AC_HELP_STRING([--with-ssl=pathname],
|
||||
else
|
||||
AC_MSG_RESULT(found in $ssldir)
|
||||
HAVE_SSL=yes
|
||||
LDFLAGS="$LDFLAGS -L$ssldir/lib";
|
||||
RUNTIME_PATH="$RUNTIME_PATH -R$ssldir/lib"
|
||||
LDFLAGS="$LDFLAGS -L$ssldir/lib"
|
||||
if test "x$enable_rpath" = xyes; then
|
||||
RUNTIME_PATH="$RUNTIME_PATH -R$ssldir/lib"
|
||||
fi
|
||||
AC_CHECK_LIB(crypto, HMAC_CTX_init,, [
|
||||
AC_MSG_ERROR([OpenSSL found in $ssldir, but version 0.9.7 or higher is required])
|
||||
])
|
||||
@@ -372,10 +459,12 @@ AC_ARG_WITH(ssl, AC_HELP_STRING([--with-ssl=pathname],
|
||||
AC_SUBST(RUNTIME_PATH)
|
||||
fi
|
||||
AC_CHECK_HEADERS([openssl/ssl.h],,, [AC_INCLUDES_DEFAULT])
|
||||
AC_CHECK_HEADERS([openssl/err.h],,, [AC_INCLUDES_DEFAULT])
|
||||
AC_CHECK_HEADERS([openssl/rand.h],,, [AC_INCLUDES_DEFAULT])
|
||||
|
||||
# check for thread library.
|
||||
AC_ARG_WITH(pthreads, AC_HELP_STRING([--with-pthreads],
|
||||
[use pthreads library, or --without--pthreads to disable threading support.]),
|
||||
[use pthreads library, or --without-pthreads to disable threading support.]),
|
||||
[ ],[ withval="yes" ])
|
||||
ub_have_pthreads=no
|
||||
if test x_$withval != x_no; then
|
||||
@@ -386,7 +475,7 @@ if test x_$withval != x_no; then
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
CC="$PTHREAD_CC"
|
||||
ub_have_pthreads=yes
|
||||
AC_CHECK_TYPES(pthread_spinlock_t,,,[#include <pthread.h>])
|
||||
AC_CHECK_TYPES([pthread_spinlock_t, pthread_rwlock_t],,,[#include <pthread.h>])
|
||||
])
|
||||
fi
|
||||
|
||||
@@ -409,69 +498,106 @@ if test x_$withval != x_no; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# check to see if libraries are needed for these functions.
|
||||
AC_SEARCH_LIBS([inet_pton], [nsl])
|
||||
AC_SEARCH_LIBS([socket], [socket])
|
||||
# check for library used by libevent after 1.3c
|
||||
AC_SEARCH_LIBS([clock_gettime], [rt])
|
||||
|
||||
# check for libevent
|
||||
AC_ARG_WITH(libevent, AC_HELP_STRING([--with-libevent=pathname],
|
||||
[set path to libevent (will check /usr/local /usr/lib /usr/pkg /usr/sfw /usr)]),
|
||||
[set path to libevent (will check /usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr)]),
|
||||
[ ],[ withval="yes" ])
|
||||
if test x_$withval != x_no; then
|
||||
AC_MSG_CHECKING(for libevent)
|
||||
if test x_$withval = x_ -o x_$withval = x_yes; then
|
||||
withval="/usr/local /usr/lib /usr/pkg /usr/sfw /usr";
|
||||
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
|
||||
fi
|
||||
for dir in $withval; do
|
||||
thedir="$dir"
|
||||
if test -f "$dir/include/event.h"; then
|
||||
found_libevent="yes";
|
||||
CPPFLAGS="$CPPFLAGS -I$thedir/include";
|
||||
found_libevent="yes"
|
||||
CPPFLAGS="$CPPFLAGS -I$thedir/include"
|
||||
break;
|
||||
fi
|
||||
done
|
||||
if test x_$found_libevent != x_yes; then
|
||||
if test -f "$dir/event.h" -a -f "$dir/.libs/libevent.a"; then
|
||||
if test -f "$dir/event.h" -a -f "$dir/libevent.la"; then
|
||||
# libevent source directory
|
||||
AC_MSG_RESULT(found in $thedir)
|
||||
CPPFLAGS="$CPPFLAGS -I$thedir";
|
||||
LDFLAGS="$thedir/.libs/*.o $LDFLAGS";
|
||||
CPPFLAGS="$CPPFLAGS -I$thedir"
|
||||
BAK_LDFLAGS="$LDFLAGS"
|
||||
# remove evdns from linking
|
||||
mkdir build >/dev/null 2>&1
|
||||
mkdir build/libevent >/dev/null 2>&1
|
||||
mkdir build/libevent/.libs >/dev/null 2>&1
|
||||
ev_files_o=`ls $thedir/*.o | grep -v evdns\.o`
|
||||
ev_files_lo=`ls $thedir/*.lo | grep -v evdns\.lo`
|
||||
ev_files_libso=`ls $thedir/.libs/*.o | grep -v evdns\.o`
|
||||
cp $ev_files_o build/libevent
|
||||
cp $ev_files_lo build/libevent
|
||||
cp $ev_files_libso build/libevent/.libs
|
||||
LATE_LDFLAGS="build/libevent/*.lo"
|
||||
LDFLAGS="build/libevent/*.o $LDFLAGS"
|
||||
else
|
||||
AC_MSG_ERROR(Cannot find the libevent library in $withval)
|
||||
AC_MSG_ERROR([Cannot find the libevent library in $withval
|
||||
You can restart ./configure --with-libevent=no to use a builtin alternative.
|
||||
Please note that this alternative is not as capable as libevent when using
|
||||
many outgoing ports. ])
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(found in $thedir)
|
||||
LDFLAGS="$LDFLAGS -L$thedir/lib";
|
||||
RUNTIME_PATH="$RUNTIME_PATH -R$thedir/lib"
|
||||
LDFLAGS="$LDFLAGS -L$thedir/lib"
|
||||
if test "x$enable_rpath" = xyes; then
|
||||
RUNTIME_PATH="$RUNTIME_PATH -R$thedir/lib"
|
||||
fi
|
||||
fi
|
||||
AC_SUBST(RUNTIME_PATH)
|
||||
AC_SEARCH_LIBS(event_set, [event])
|
||||
AC_CHECK_HEADERS([event.h],,, [AC_INCLUDES_DEFAULT])
|
||||
AC_CHECK_FUNCS([event_base_free]) # only in libevent 1.2 and later
|
||||
AC_CHECK_FUNCS([event_base_once]) # only in libevent 1.4? and later
|
||||
if test -n "$BAK_LDFLAGS"; then
|
||||
LDFLAGS="$BAK_LDFLAGS"
|
||||
fi
|
||||
else
|
||||
AC_DEFINE(USE_MINI_EVENT, 1, [Define if you want to use internal select based events])
|
||||
fi
|
||||
|
||||
# check to see if libraries are needed for these functions.
|
||||
AC_CHECK_LIB(socket, socket)
|
||||
AC_CHECK_LIB(nsl, inet_pton)
|
||||
# set static linking if requested
|
||||
AC_SUBST(staticexe)
|
||||
staticexe=""
|
||||
AC_ARG_ENABLE(staticexe, AC_HELP_STRING([--enable-static-exe],
|
||||
[ enable to compile executables statically against event, ldns libs, for debug purposes ]),
|
||||
, )
|
||||
if test x_$enable_static_exe = x_yes; then
|
||||
staticexe="-static"
|
||||
fi
|
||||
|
||||
# set lock checking if requested
|
||||
AC_ARG_ENABLE(lock_checks, AC_HELP_STRING([--enable-lock-checks],
|
||||
[ enable to check lock and unlock calls, for debug purposes ]),
|
||||
, )
|
||||
if test x_$enable_lock_checks = x_yes; then
|
||||
AC_DEFINE(ENABLE_LOCK_CHECKS, 1, [Define if you want to use debug lock checking (slow).])
|
||||
fi
|
||||
|
||||
# set memory allocation checking if requested
|
||||
AC_ARG_ENABLE(alloc-checks, AC_HELP_STRING([--enable-alloc-checks],
|
||||
[ enable to check memory allocation, for debug purposes ]),
|
||||
, )
|
||||
if test x_$enable_alloc_checks = x_yes; then
|
||||
AC_DEFINE(UNBOUND_ALLOC_STATS, 1, [use statistics for allocs and frees, for debug use])
|
||||
else
|
||||
AC_FUNC_MALLOC
|
||||
fi
|
||||
|
||||
AC_FUNC_CHOWN
|
||||
AC_FUNC_FORK
|
||||
AC_FUNC_MALLOC
|
||||
AC_TYPE_SIGNAL
|
||||
AC_FUNC_FSEEKO
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
AC_REPLACE_FUNCS(inet_aton)
|
||||
AC_REPLACE_FUNCS(inet_pton)
|
||||
AC_REPLACE_FUNCS(inet_ntop)
|
||||
AC_REPLACE_FUNCS(snprintf)
|
||||
AC_REPLACE_FUNCS(strlcpy)
|
||||
AC_REPLACE_FUNCS(memmove)
|
||||
|
||||
AC_CHECK_FUNCS([getaddrinfo])
|
||||
if test $ac_cv_func_getaddrinfo = no; then
|
||||
AC_LIBOBJ([fake-rfc2553])
|
||||
fi
|
||||
|
||||
# check this after all other compilation checks, since the linking of the lib
|
||||
# may break checks after this.
|
||||
AC_ARG_WITH(ldns,
|
||||
@@ -483,11 +609,60 @@ AC_ARG_WITH(ldns,
|
||||
AC_SUBST(ldnsdir)
|
||||
])
|
||||
|
||||
AC_CHECK_LIB(ldns, ldns_rr_new,, [AC_MSG_ERROR([Can't find ldns library])])
|
||||
AC_CHECK_LIB(ldns, ldns_rr_new, [
|
||||
dnl ldns was found, check compat functions
|
||||
AC_CHECK_LIB(ldns, [ldns_rr_free])
|
||||
AC_REPLACE_FUNCS(inet_aton)
|
||||
AC_REPLACE_FUNCS(inet_pton)
|
||||
AC_REPLACE_FUNCS(inet_ntop)
|
||||
AC_REPLACE_FUNCS(snprintf)
|
||||
AC_REPLACE_FUNCS(strlcpy)
|
||||
AC_REPLACE_FUNCS(memmove)
|
||||
|
||||
AC_DEFINE_UNQUOTED([MAXSYSLOGMSGLEN], [512], [Define to the maximum message length to pass to syslog.])
|
||||
AC_CHECK_FUNCS([getaddrinfo])
|
||||
if test $ac_cv_func_getaddrinfo = no; then
|
||||
AC_LIBOBJ([fake-rfc2553])
|
||||
fi
|
||||
|
||||
] , [
|
||||
dnl use the builtin ldns-src.tar.gz file to build ldns.
|
||||
if test ! -f $srcdir/ldns-src.tar.gz; then
|
||||
AC_MSG_ERROR([No ldns library found and no ldns-src.tar.gz, use --with-ldns=path.])
|
||||
fi
|
||||
echo "****************************************************************"
|
||||
echo "*** ***"
|
||||
echo "*** ldns library not found (--with-ldns= to specify by hand) ***"
|
||||
echo "*** Building ldns library from package included in this one. ***"
|
||||
echo "*** ***"
|
||||
echo "****************************************************************"
|
||||
if test -d ldns-src; then rm -rf ldns-src; fi
|
||||
mkdir ldns-src; cp $srcdir/ldns-src.tar.gz ldns-src/ldns-src.tar.gz; (cd ldns-src; gzip -cd ldns-src.tar.gz | tar xf -); rm -f ldns-src/ldns-src.tar.gz
|
||||
ldnsdir=`ls -d ldns-src/ldns*`
|
||||
AC_MSG_NOTICE([Configure $ldnsdir])
|
||||
echo "(cd $ldnsdir; ./configure)"
|
||||
(cd $ldnsdir; ./configure)
|
||||
AC_MSG_NOTICE([Finished $ldnsdir configure])
|
||||
CPPFLAGS="$CPPFLAGS -I$ldnsdir/include"
|
||||
LDFLAGS="$LDFLAGS $ldnsdir/*.lo"
|
||||
AC_SUBST(ldnsdir)
|
||||
dnl use ldns compat functions
|
||||
AC_DEFINE(HAVE_INET_ATON)
|
||||
AC_DEFINE(HAVE_INET_PTON)
|
||||
AC_DEFINE(HAVE_INET_NTOP)
|
||||
AC_DEFINE(HAVE_SNPRINTF)
|
||||
AC_DEFINE(HAVE_STRLCPY)
|
||||
AC_DEFINE(HAVE_MEMMOVE)
|
||||
AC_DEFINE(HAVE_GETADDRINFO)
|
||||
])
|
||||
LDFLAGS="$LATE_LDFLAGS $LDFLAGS"
|
||||
|
||||
AC_DEFINE_UNQUOTED([MAXSYSLOGMSGLEN], [5120], [Define to the maximum message length to pass to syslog.])
|
||||
|
||||
AH_BOTTOM([
|
||||
#ifndef UNBOUND_DEBUG
|
||||
# define NDEBUG
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@@ -528,6 +703,14 @@ AH_BOTTOM([
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENSSL_ERR_H
|
||||
#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)))
|
||||
@@ -599,6 +782,25 @@ struct sockaddr_storage;
|
||||
|
||||
#include "ldns/ldns.h"
|
||||
|
||||
#ifdef UNBOUND_ALLOC_STATS
|
||||
# define malloc(s) unbound_stat_malloc_log(s, __FILE__, __LINE__, __func__)
|
||||
# define calloc(n,s) unbound_stat_calloc_log(n, s, __FILE__, __LINE__, __func__)
|
||||
# define free(p) unbound_stat_free_log(p, __FILE__, __LINE__, __func__)
|
||||
# define realloc(p,s) unbound_stat_realloc_log(p, s, __FILE__, __LINE__, __func__)
|
||||
void *unbound_stat_malloc(size_t size);
|
||||
void *unbound_stat_calloc(size_t nmemb, size_t size);
|
||||
void unbound_stat_free(void *ptr);
|
||||
void *unbound_stat_realloc(void *ptr, size_t size);
|
||||
void *unbound_stat_malloc_log(size_t size, const char* file, int line,
|
||||
const char* func);
|
||||
void *unbound_stat_calloc_log(size_t nmemb, size_t size, const char* file,
|
||||
int line, const char* func);
|
||||
void unbound_stat_free_log(void *ptr, const char* file, int line,
|
||||
const char* func);
|
||||
void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
|
||||
int line, const char* func);
|
||||
#endif /* UNBOUND_ALLOC_STATS */
|
||||
|
||||
/** default port for DNS traffic. */
|
||||
#define UNBOUND_DNS_PORT 53
|
||||
])
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
int
|
||||
acl_list_cmp(const void* k1, const void* k2)
|
||||
{
|
||||
struct acl_addr* n1 = (struct acl_addr*)k1;
|
||||
struct acl_addr* n2 = (struct acl_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 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->tree);
|
||||
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->node.key = node;
|
||||
memcpy(&node->addr, addr, addrlen);
|
||||
node->addrlen = addrlen;
|
||||
node->net = net;
|
||||
node->parent = NULL;
|
||||
node->control = control;
|
||||
if(!rbtree_insert(acl->tree, &node->node)) {
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
/** initialise parent pointers in the tree */
|
||||
static void
|
||||
acl_list_init_parents(struct acl_list* acl)
|
||||
{
|
||||
struct acl_addr* node, *prev = NULL, *p;
|
||||
int m;
|
||||
RBTREE_FOR(node, struct acl_addr*, acl->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
|
||||
acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg)
|
||||
{
|
||||
regional_free_all(acl->region);
|
||||
free(acl->tree);
|
||||
acl->tree = rbtree_create(acl_list_cmp);
|
||||
if(!acl->tree)
|
||||
return 0;
|
||||
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, "::0/0", "refuse", 0))
|
||||
return 0;
|
||||
if(!acl_list_str_cfg(acl, "127.0.0.0/8", "allow", 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;
|
||||
acl_list_init_parents(acl);
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum acl_access
|
||||
acl_list_lookup(struct acl_list* acl, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
/* lookup in the tree */
|
||||
rbnode_t* res = NULL;
|
||||
struct acl_addr* result;
|
||||
struct acl_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(acl->tree, &key, &res)) {
|
||||
/* exact */
|
||||
result = (struct acl_addr*)res;
|
||||
return result->control;
|
||||
} else {
|
||||
/* smaller element (or no element) */
|
||||
int m;
|
||||
result = (struct acl_addr*)res;
|
||||
if(!result || result->addrlen != addrlen)
|
||||
return acl_deny;
|
||||
/* 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 result->control;
|
||||
result = result->parent;
|
||||
}
|
||||
}
|
||||
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,134 @@
|
||||
/*
|
||||
* 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/rbtree.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 */
|
||||
acl_allow
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
/** redblacktree node, key is this structure: addr and addrlen, net */
|
||||
rbnode_t node;
|
||||
/** parent node in acl tree that encompasses this entry */
|
||||
struct acl_addr* parent;
|
||||
/** address */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr */
|
||||
socklen_t addrlen;
|
||||
/** netblock size */
|
||||
int net;
|
||||
/** 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);
|
||||
|
||||
/** compare two acl list entries */
|
||||
int acl_list_cmp(const void* k1, const void* k2);
|
||||
|
||||
#endif /* DAEMON_ACL_LIST_H */
|
||||
+80
-31
@@ -39,17 +39,20 @@
|
||||
* The daemon consists of global settings and a number of workers.
|
||||
*/
|
||||
|
||||
/** buffer size for network connections */
|
||||
#define BUFSZ 65552
|
||||
|
||||
#include "config.h"
|
||||
#include "daemon/daemon.h"
|
||||
#include "daemon/worker.h"
|
||||
#include "daemon/acl_list.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
#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 <signal.h>
|
||||
|
||||
/** How many quit requests happened. */
|
||||
@@ -58,10 +61,15 @@ static int sig_record_quit = 0;
|
||||
static int sig_record_reload = 0;
|
||||
|
||||
/** used when no other sighandling happens, so we don't die
|
||||
* when multiple signals in quick succession are sent to us. */
|
||||
* when multiple signals in quick succession are sent to us.
|
||||
* @param sig: signal number.
|
||||
* @return signal handler return type (void or int).
|
||||
*/
|
||||
static RETSIGTYPE record_sigh(int sig)
|
||||
{
|
||||
#ifdef LIBEVENT_SIGNAL_PROBLEM
|
||||
verbose(VERB_OPS, "quit on signal, no cleanup and statistics, "
|
||||
"because installed libevent version is not threadsafe");
|
||||
exit(0);
|
||||
#endif
|
||||
switch(sig)
|
||||
@@ -100,10 +108,10 @@ signal_handling_record()
|
||||
static void
|
||||
signal_handling_playback(struct worker* wrk)
|
||||
{
|
||||
if(sig_record_quit)
|
||||
worker_sighandler(SIGTERM, wrk);
|
||||
if(sig_record_reload)
|
||||
worker_sighandler(SIGHUP, wrk);
|
||||
if(sig_record_quit)
|
||||
worker_sighandler(SIGTERM, wrk);
|
||||
sig_record_quit = 0;
|
||||
sig_record_reload = 0;
|
||||
}
|
||||
@@ -117,25 +125,21 @@ daemon_init()
|
||||
return NULL;
|
||||
signal_handling_record();
|
||||
checklock_start();
|
||||
ERR_load_crypto_strings();
|
||||
daemon->need_to_exit = 0;
|
||||
daemon->msg_cache = slabhash_create(HASH_DEFAULT_SLABS,
|
||||
HASH_DEFAULT_STARTARRAY, HASH_DEFAULT_MAXMEM,
|
||||
msgreply_sizefunc, query_info_compare,
|
||||
query_entry_delete, reply_info_delete, NULL);
|
||||
if(!daemon->msg_cache) {
|
||||
free(daemon);
|
||||
return NULL;
|
||||
}
|
||||
daemon->rrset_cache = slabhash_create(HASH_DEFAULT_SLABS,
|
||||
HASH_DEFAULT_STARTARRAY, HASH_DEFAULT_MAXMEM,
|
||||
ub_rrset_sizefunc, ub_rrset_compare,
|
||||
ub_rrset_key_delete, rrset_data_delete, &daemon->superalloc);
|
||||
if(!daemon->rrset_cache) {
|
||||
slabhash_delete(daemon->msg_cache);
|
||||
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;
|
||||
}
|
||||
return daemon;
|
||||
}
|
||||
|
||||
@@ -152,6 +156,22 @@ daemon_open_shared_ports(struct daemon* daemon)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup modules. setup module stack.
|
||||
* @param daemon: the daemon
|
||||
*/
|
||||
static void daemon_setup_modules(struct daemon* daemon)
|
||||
{
|
||||
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 */
|
||||
if(!modstack_setup(&daemon->mods, daemon->cfg->module_conf,
|
||||
daemon->env)) {
|
||||
fatal_exit("failed to setup modules");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate empty worker structures. With backptr and thread-number,
|
||||
* from 0..numthread initialised. Used as user arguments to new threads.
|
||||
@@ -209,8 +229,7 @@ thread_start(void* arg)
|
||||
worker->cmd_send_fd = -1;
|
||||
close_other_pipes(worker->daemon, worker->thread_num);
|
||||
#endif
|
||||
if(!worker_init(worker, worker->daemon->cfg, worker->daemon->ports,
|
||||
BUFSZ, 0))
|
||||
if(!worker_init(worker, worker->daemon->cfg, worker->daemon->ports, 0))
|
||||
fatal_exit("Could not initialize thread");
|
||||
|
||||
worker_work(worker);
|
||||
@@ -255,7 +274,7 @@ daemon_stop_others(struct daemon* daemon)
|
||||
worker_send_cmd(daemon->workers[i],
|
||||
daemon->workers[0]->front->udp_buff, worker_cmd_quit);
|
||||
}
|
||||
/** wait for them to quit */
|
||||
/* wait for them to quit */
|
||||
for(i=1; i<daemon->num; i++) {
|
||||
/* join it to make sure its dead */
|
||||
verbose(VERB_ALGO, "join %d", i);
|
||||
@@ -268,6 +287,16 @@ 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);
|
||||
|
||||
/* first create all the worker structures, so we can pass
|
||||
* them to the newly created threads.
|
||||
*/
|
||||
@@ -281,21 +310,19 @@ daemon_fork(struct daemon* daemon)
|
||||
/* Special handling for the main thread. This is the thread
|
||||
* that handles signals.
|
||||
*/
|
||||
if(!worker_init(daemon->workers[0], daemon->cfg, daemon->ports,
|
||||
BUFSZ, 1))
|
||||
if(!worker_init(daemon->workers[0], daemon->cfg, daemon->ports, 1))
|
||||
fatal_exit("Could not initialize main thread");
|
||||
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);
|
||||
|
||||
if(daemon->workers[0]->need_to_restart)
|
||||
daemon->need_to_exit = 0;
|
||||
else daemon->need_to_exit = 1;
|
||||
daemon->need_to_exit = daemon->workers[0]->need_to_exit;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -307,6 +334,15 @@ daemon_cleanup(struct daemon* daemon)
|
||||
don't die on multiple reload signals for example. */
|
||||
signal_handling_record();
|
||||
log_thread_set(NULL);
|
||||
/* clean up caches because
|
||||
* a) RRset IDs will be recycled after a reload, causing collisions
|
||||
* b) validation config can change, thus rrset, msg, keycache clear
|
||||
* 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]);
|
||||
free(daemon->workers);
|
||||
@@ -320,12 +356,25 @@ daemon_delete(struct daemon* daemon)
|
||||
{
|
||||
if(!daemon)
|
||||
return;
|
||||
modstack_desetup(&daemon->mods, daemon->env);
|
||||
listening_ports_free(daemon->ports);
|
||||
slabhash_delete(daemon->msg_cache);
|
||||
slabhash_delete(daemon->rrset_cache);
|
||||
if(daemon->env) {
|
||||
slabhash_delete(daemon->env->msg_cache);
|
||||
rrset_cache_delete(daemon->env->rrset_cache);
|
||||
infra_delete(daemon->env->infra_cache);
|
||||
}
|
||||
alloc_clear(&daemon->superalloc);
|
||||
free(daemon->cwd);
|
||||
acl_list_delete(daemon->acl);
|
||||
free(daemon->pidfile);
|
||||
free(daemon->env);
|
||||
free(daemon);
|
||||
/* libcrypto cleanup */
|
||||
/* CONF_modules_unload(1); */
|
||||
EVP_cleanup();
|
||||
/* ENGINE_cleanup(); */
|
||||
CRYPTO_cleanup_all_ex_data(); /* safe, no more threads right now */
|
||||
ERR_remove_state(0);
|
||||
ERR_free_strings();
|
||||
RAND_cleanup();
|
||||
checklock_stop();
|
||||
}
|
||||
|
||||
+13
-6
@@ -44,10 +44,15 @@
|
||||
|
||||
#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;
|
||||
|
||||
/**
|
||||
* Structure holding worker list.
|
||||
@@ -56,8 +61,6 @@ struct slabhash;
|
||||
struct daemon {
|
||||
/** The config settings */
|
||||
struct config_file* cfg;
|
||||
/** current working directory */
|
||||
char* cwd;
|
||||
/** pidfile that is used */
|
||||
char* pidfile;
|
||||
/** port number that has ports opened. */
|
||||
@@ -72,10 +75,14 @@ struct daemon {
|
||||
int need_to_exit;
|
||||
/** master allocation cache */
|
||||
struct alloc_cache superalloc;
|
||||
/** the message cache, content is struct msgreply_entry* */
|
||||
struct slabhash* msg_cache;
|
||||
/** the rrset cache, content is struct ub_packed_rrset_key* */
|
||||
struct slabhash* rrset_cache;
|
||||
/** the module environment master value, copied and changed by threads*/
|
||||
struct module_env* env;
|
||||
/** 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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+11
-8
@@ -42,30 +42,33 @@
|
||||
#include "config.h"
|
||||
#include "daemon/stats.h"
|
||||
#include "daemon/worker.h"
|
||||
#include "services/mesh.h"
|
||||
|
||||
void server_stats_init(struct server_stats* stats)
|
||||
{
|
||||
memset(stats, 0, sizeof(stats));
|
||||
memset(stats, 0, sizeof(*stats));
|
||||
}
|
||||
|
||||
void server_stats_querymiss(struct server_stats* stats, struct worker* worker)
|
||||
{
|
||||
stats->num_queries_missed_cache++;
|
||||
stats->sum_query_list_size += worker->num_requests;
|
||||
if(worker->num_requests > stats->max_query_list_size)
|
||||
stats->max_query_list_size = worker->num_requests;
|
||||
stats->sum_query_list_size += worker->env.mesh->all.count;
|
||||
if(worker->env.mesh->all.count > stats->max_query_list_size)
|
||||
stats->max_query_list_size = worker->env.mesh->all.count;
|
||||
}
|
||||
|
||||
void server_stats_log(struct server_stats* stats, 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,
|
||||
(double)stats->sum_query_list_size/
|
||||
stats->num_queries_missed_cache : 0.0,
|
||||
(unsigned)stats->num_query_list_exceeded);
|
||||
}
|
||||
|
||||
+96
-56
@@ -46,21 +46,28 @@
|
||||
#include "util/config_file.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
#include "services/listen_dnsport.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/module.h"
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
/** global debug value to keep track of heap memory allocation */
|
||||
void* unbound_start_brk = 0;
|
||||
|
||||
/** print usage. */
|
||||
static void usage()
|
||||
{
|
||||
printf("usage: unbound [options]\n");
|
||||
printf("usage: unbound [options]\n");
|
||||
printf(" start unbound daemon DNS resolver.\n");
|
||||
printf("-h this help\n");
|
||||
printf("-c file config file to read, unbound.conf(5).\n");
|
||||
printf("-c file config file to read instead of %s\n", CONFIGFILE);
|
||||
printf(" file format is described in unbound.conf(5).\n");
|
||||
printf("-d do not fork into the background.\n");
|
||||
printf("-v verbose (multiple times increase verbosity)\n");
|
||||
printf("-v verbose (more times to increase verbosity)\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);
|
||||
@@ -71,10 +78,12 @@ 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 + TCP_ACCEPT_COUNT:0));
|
||||
((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 outnum = cfg->outgoing_num_ports*ifs + cfg->outgoing_num_tcp;
|
||||
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 misc = 4; /* logfile, pidfile, stdout... */
|
||||
size_t perthread = listen_num + outnum + 2/*cmdpipe*/ + 2/*libevent*/
|
||||
+ misc;
|
||||
@@ -89,6 +98,8 @@ checkrlimits(struct config_file* cfg)
|
||||
log_warn("getrlimit: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
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).");
|
||||
@@ -100,51 +111,40 @@ checkrlimits(struct config_file* cfg)
|
||||
}
|
||||
}
|
||||
|
||||
/** to changedir, logfile */
|
||||
/** set verbosity, check rlimits, cache settings */
|
||||
static void
|
||||
apply_dir(struct daemon* daemon, struct config_file* cfg, int cmdline_verbose)
|
||||
apply_settings(struct daemon* daemon, struct config_file* cfg,
|
||||
int cmdline_verbose)
|
||||
{
|
||||
/* apply if they have changed */
|
||||
daemon->cfg = cfg;
|
||||
verbosity = cmdline_verbose + cfg->verbosity;
|
||||
if(cfg->directory && cfg->directory[0]) {
|
||||
if(!daemon->cwd || strcmp(daemon->cwd, cfg->directory) != 0) {
|
||||
if(chdir(cfg->directory)) {
|
||||
log_err("Could not chdir to %s: %s",
|
||||
cfg->directory, strerror(errno));
|
||||
}
|
||||
free(daemon->cwd);
|
||||
if(!(daemon->cwd = strdup(cfg->directory)))
|
||||
log_err("cwd: malloc failed");
|
||||
}
|
||||
}
|
||||
if(cfg->msg_cache_size != slabhash_get_size(daemon->msg_cache) ||
|
||||
cfg->msg_cache_slabs != daemon->msg_cache->size) {
|
||||
slabhash_delete(daemon->msg_cache);
|
||||
daemon->msg_cache = slabhash_create(cfg->msg_cache_slabs,
|
||||
config_apply(cfg);
|
||||
if(!daemon->env->msg_cache ||
|
||||
cfg->msg_cache_size != slabhash_get_size(daemon->env->msg_cache) ||
|
||||
cfg->msg_cache_slabs != daemon->env->msg_cache->size) {
|
||||
slabhash_delete(daemon->env->msg_cache);
|
||||
daemon->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(!daemon->msg_cache) {
|
||||
fatal_exit("malloc failure updating config settings");
|
||||
}
|
||||
}
|
||||
if(cfg->rrset_cache_size != slabhash_get_size(daemon->rrset_cache) ||
|
||||
cfg->rrset_cache_slabs != daemon->rrset_cache->size) {
|
||||
slabhash_delete(daemon->rrset_cache);
|
||||
daemon->rrset_cache = slabhash_create(cfg->rrset_cache_slabs,
|
||||
HASH_DEFAULT_STARTARRAY, cfg->rrset_cache_size,
|
||||
ub_rrset_sizefunc, ub_rrset_compare,
|
||||
ub_rrset_key_delete, rrset_data_delete,
|
||||
&daemon->superalloc);
|
||||
if(!daemon->rrset_cache) {
|
||||
if(!daemon->env->msg_cache) {
|
||||
fatal_exit("malloc failure updating config settings");
|
||||
}
|
||||
}
|
||||
if((daemon->env->rrset_cache = rrset_cache_adjust(
|
||||
daemon->env->rrset_cache, cfg, &daemon->superalloc)) == 0)
|
||||
fatal_exit("malloc failure updating config settings");
|
||||
if((daemon->env->infra_cache = infra_adjust(daemon->env->infra_cache,
|
||||
cfg))==0)
|
||||
fatal_exit("malloc failure updating config settings");
|
||||
checkrlimits(cfg);
|
||||
}
|
||||
|
||||
/** Read existing pid from pidfile. */
|
||||
/** Read existing pid from pidfile.
|
||||
* @param file: file name of pid file.
|
||||
* @return: the pid from the file or -1 if none.
|
||||
*/
|
||||
static pid_t
|
||||
readpid (const char* file)
|
||||
{
|
||||
@@ -176,7 +176,8 @@ readpid (const char* file)
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid = strtol(pidbuf, &t, 10);
|
||||
pidbuf[sizeof(pidbuf)-1] = 0;
|
||||
pid = (pid_t)strtol(pidbuf, &t, 10);
|
||||
|
||||
if (*t && *t != '\n') {
|
||||
return -1;
|
||||
@@ -184,7 +185,10 @@ readpid (const char* file)
|
||||
return pid;
|
||||
}
|
||||
|
||||
/** write pid to file. */
|
||||
/** write pid to file.
|
||||
* @param pidfile: file name of pid file.
|
||||
* @param pid: pid to write to file.
|
||||
*/
|
||||
static void
|
||||
writepid (const char* pidfile, pid_t pid)
|
||||
{
|
||||
@@ -210,7 +214,12 @@ static void
|
||||
checkoldpid(struct config_file* cfg)
|
||||
{
|
||||
pid_t old;
|
||||
if((old = readpid(cfg->pidfile)) != -1) {
|
||||
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) {
|
||||
/* see if it is still alive */
|
||||
if(kill(old, 0) == 0 || errno == EPERM)
|
||||
log_warn("unbound is already running as pid %u.",
|
||||
@@ -252,35 +261,62 @@ detach(struct config_file* cfg)
|
||||
static void
|
||||
do_chroot(struct daemon* daemon, struct config_file* cfg, int debug_mode)
|
||||
{
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
/* initialize, but not to 0 (root) */
|
||||
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->chrootdir && cfg->chrootdir[0])
|
||||
if(chroot(cfg->chrootdir))
|
||||
fatal_exit("unable to chroot: %s", strerror(errno));
|
||||
if(cfg->directory && cfg->directory[0]) {
|
||||
if(chdir(cfg->directory)) {
|
||||
fatal_exit("Could not chdir to %s: %s",
|
||||
cfg->directory, strerror(errno));
|
||||
}
|
||||
verbose(VERB_QUERY, "chdir to %s", cfg->directory);
|
||||
}
|
||||
if(cfg->username && cfg->username[0]) {
|
||||
struct passwd *pwd;
|
||||
if((pwd = getpwnam(cfg->username)) == NULL)
|
||||
fatal_exit("user '%s' does not exist.", cfg->username);
|
||||
if(setgid(pwd->pw_gid) != 0)
|
||||
fatal_exit("unable to set group id: %s", strerror(errno));
|
||||
if(setuid(pwd->pw_uid) != 0)
|
||||
fatal_exit("unable to set user id: %s", strerror(errno));
|
||||
uid = pwd->pw_uid;
|
||||
gid = pwd->pw_gid;
|
||||
endpwent();
|
||||
}
|
||||
if(cfg->chrootdir && cfg->chrootdir[0]) {
|
||||
if(chroot(cfg->chrootdir))
|
||||
fatal_exit("unable to chroot to %s: %s",
|
||||
cfg->chrootdir, strerror(errno));
|
||||
verbose(VERB_QUERY, "chroot to %s", cfg->chrootdir);
|
||||
}
|
||||
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_QUERY, "drop user privileges, run as %s",
|
||||
cfg->username);
|
||||
}
|
||||
/* check old pid file before forking */
|
||||
if(cfg->pidfile && cfg->pidfile[0]) {
|
||||
checkoldpid(cfg);
|
||||
}
|
||||
|
||||
/* init logfile just before fork */
|
||||
log_init(cfg->logfile);
|
||||
log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
|
||||
if(!debug_mode && cfg->do_daemonize) {
|
||||
detach(cfg);
|
||||
}
|
||||
if(cfg->pidfile && cfg->pidfile[0]) {
|
||||
writepid(cfg->pidfile, getpid());
|
||||
if(!(daemon->pidfile = strdup(cfg->pidfile)))
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -293,7 +329,7 @@ do_chroot(struct daemon* daemon, struct config_file* cfg, int debug_mode)
|
||||
* @param debug_mode: if set, do not daemonize.
|
||||
*/
|
||||
static void
|
||||
run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode)
|
||||
run_daemon(char* cfgfile, int cmdline_verbose, int debug_mode)
|
||||
{
|
||||
struct config_file* cfg = NULL;
|
||||
struct daemon* daemon = NULL;
|
||||
@@ -303,15 +339,15 @@ run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode)
|
||||
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);
|
||||
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);
|
||||
apply_dir(daemon, cfg, cmdline_verbose);
|
||||
apply_settings(daemon, cfg, cmdline_verbose);
|
||||
|
||||
/* prepare */
|
||||
if(!daemon_open_shared_ports(daemon))
|
||||
@@ -319,7 +355,7 @@ run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode)
|
||||
if(!done_chroot) {
|
||||
do_chroot(daemon, cfg, debug_mode);
|
||||
done_chroot = 1;
|
||||
}
|
||||
} else log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
|
||||
/* work */
|
||||
daemon_fork(daemon);
|
||||
|
||||
@@ -349,11 +385,14 @@ int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
int c;
|
||||
const char* cfgfile = NULL;
|
||||
char* cfgfile = CONFIGFILE;
|
||||
int cmdline_verbose = 0;
|
||||
int debug_mode = 0;
|
||||
|
||||
log_init(NULL);
|
||||
/* take debug snapshot of heap */
|
||||
unbound_start_brk = sbrk(0);
|
||||
|
||||
log_init(NULL, 0, NULL);
|
||||
/* parse the options */
|
||||
while( (c=getopt(argc, argv, "c:dhv")) != -1) {
|
||||
switch(c) {
|
||||
@@ -383,5 +422,6 @@ main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
run_daemon(cfgfile, cmdline_verbose, debug_mode);
|
||||
log_init(NULL, 0, NULL); /* close logfile */
|
||||
return 0;
|
||||
}
|
||||
|
||||
+776
-403
File diff suppressed because it is too large
Load Diff
+73
-52
@@ -50,16 +50,14 @@
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "daemon/stats.h"
|
||||
#include "util/module.h"
|
||||
struct listen_dnsport;
|
||||
struct outside_network;
|
||||
struct config_file;
|
||||
struct daemon;
|
||||
struct listen_port;
|
||||
struct ub_randstate;
|
||||
struct region;
|
||||
|
||||
/** size of table used for random numbers. large to be more secure. */
|
||||
#define RND_STATE_SIZE 256
|
||||
struct regional;
|
||||
|
||||
/** worker commands */
|
||||
enum worker_commands {
|
||||
@@ -67,28 +65,6 @@ enum worker_commands {
|
||||
worker_cmd_quit
|
||||
};
|
||||
|
||||
/** information per query that is in processing */
|
||||
struct work_query {
|
||||
/** next query in freelist */
|
||||
struct work_query* next;
|
||||
/** the worker for this query */
|
||||
struct worker* worker;
|
||||
/** the query reply destination, packet buffer and where to send. */
|
||||
struct comm_reply query_reply;
|
||||
/** the query_info structure from the query */
|
||||
struct query_info qinfo;
|
||||
/** hash value of the query qinfo */
|
||||
hashvalue_t query_hash;
|
||||
/** next query in all-list */
|
||||
struct work_query* all_next;
|
||||
/** id of query, in network byteorder. */
|
||||
uint16_t query_id;
|
||||
/** flags uint16 from query */
|
||||
uint16_t query_flags;
|
||||
/** edns data from the query */
|
||||
struct edns_data edns;
|
||||
};
|
||||
|
||||
/**
|
||||
* Structure holding working information for unbound.
|
||||
* Holds globally visible information.
|
||||
@@ -114,31 +90,25 @@ struct worker {
|
||||
struct comm_signal* comsig;
|
||||
/** commpoint to listen to commands. */
|
||||
struct comm_point* cmd_com;
|
||||
/** timer for statistics */
|
||||
struct comm_timer* stat_timer;
|
||||
|
||||
/** number of requests currently active */
|
||||
size_t num_requests;
|
||||
/** number of requests that can be handled by this worker */
|
||||
size_t request_size;
|
||||
/** the free working queries */
|
||||
struct work_query* free_queries;
|
||||
/** list of all working queries */
|
||||
struct work_query* all_queries;
|
||||
|
||||
/** address to forward to */
|
||||
struct sockaddr_storage fwd_addr;
|
||||
/** length of fwd_addr */
|
||||
socklen_t fwd_addrlen;
|
||||
|
||||
/** random() table for this worker. */
|
||||
struct ub_randstate* rndstate;
|
||||
/** do we need to restart (instead of exit) ? */
|
||||
int need_to_restart;
|
||||
/** do we need to restart or quit (on signal) */
|
||||
int need_to_exit;
|
||||
/** allocation cache for this thread */
|
||||
struct alloc_cache alloc;
|
||||
/** per thread statistics */
|
||||
struct server_stats stats;
|
||||
/** thread scratch region */
|
||||
struct region* scratchpad;
|
||||
/** thread scratch regional */
|
||||
struct regional* scratchpad;
|
||||
|
||||
/** module environment passed to modules, changed for this thread */
|
||||
struct module_env env;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -156,12 +126,11 @@ struct worker* worker_create(struct daemon* daemon, int id);
|
||||
* @param worker: worker to initialize, created with worker_create.
|
||||
* @param cfg: configuration settings.
|
||||
* @param ports: list of shared query ports.
|
||||
* @param buffer_size: size of datagram buffer.
|
||||
* @param do_sigs: if true, worker installs signal handlers.
|
||||
* @return: false on error.
|
||||
*/
|
||||
int worker_init(struct worker* worker, struct config_file *cfg,
|
||||
struct listen_port* ports, size_t buffer_size, int do_sigs);
|
||||
struct listen_port* ports, int do_sigs);
|
||||
|
||||
/**
|
||||
* Make worker work.
|
||||
@@ -173,15 +142,6 @@ void worker_work(struct worker* worker);
|
||||
*/
|
||||
void worker_delete(struct worker* worker);
|
||||
|
||||
/**
|
||||
* Set forwarder
|
||||
* @param worker: the worker to modify.
|
||||
* @param ip: the server name.
|
||||
* @param port: port on server or NULL for default 53.
|
||||
* @return: false on error.
|
||||
*/
|
||||
int worker_set_fwd(struct worker* worker, const char* ip, int port);
|
||||
|
||||
/**
|
||||
* Send a command to a worker. Uses blocking writes.
|
||||
* @param worker: worker to send command to.
|
||||
@@ -198,4 +158,65 @@ void worker_send_cmd(struct worker* worker, ldns_buffer* buffer,
|
||||
*/
|
||||
void worker_sighandler(int sig, 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 worker_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* worker_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 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.
|
||||
*/
|
||||
int worker_handle_control_cmd(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* reply_info);
|
||||
|
||||
/** handles callbacks from listening event interface */
|
||||
int worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* repinfo);
|
||||
|
||||
/** process incoming replies from the network */
|
||||
int worker_handle_reply(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* reply_info);
|
||||
|
||||
/** process incoming serviced query replies from the network */
|
||||
int worker_handle_service_reply(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* reply_info);
|
||||
|
||||
/** 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 */
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
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.
|
||||
|
||||
At NLnet Labs, Jelte Jansen and Mark Santcroos 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.
|
||||
+1197
File diff suppressed because it is too large
Load Diff
+61
-8
@@ -4,25 +4,78 @@ http://unbound.net
|
||||
|
||||
This software is under BSD license, see LICENSE for details.
|
||||
|
||||
* Needs the following libraries
|
||||
* Download the latest 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)
|
||||
* libevent http://www.monkey.org/~provos/libevent/ (BSD license)
|
||||
|
||||
* Make and install: ./configure; make
|
||||
* Create build environment
|
||||
* run libtoolize -c if config.sub is missing, or run glibtoolize.
|
||||
* autoreconf (autoheader && autoconf), if ./configure is missing.
|
||||
|
||||
* Make and install: ./configure; make; make install
|
||||
* Use GNU make; default on linux, often called 'gmake' on BSD and Solaris.
|
||||
* --with-ldns=/path/to/ldns
|
||||
If ldns is not detected on the system, a prepackaged tarball
|
||||
of the ldns library is used to compile and statically link against.
|
||||
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 an alternative to libevent (based on select).
|
||||
--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.
|
||||
* --without-pthreads
|
||||
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.
|
||||
* --enable-lock-checks
|
||||
This enables a debug option to check lock and unlock calls. It needs
|
||||
a recent pthreads library to work.
|
||||
* --enable-alloc-checks
|
||||
This enables a debug option to check malloc (calloc, realloc, free).
|
||||
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.
|
||||
|
||||
* Create build environment
|
||||
* run libtoolize -c if config.sub is missing.
|
||||
* autoreconf (autoheader && autoconf), if ./configure is missing.
|
||||
* 'make test' attempts to run a series of tests, depending on the support
|
||||
programs that are installed.
|
||||
|
||||
Known issues
|
||||
------------
|
||||
o If libevent is older (1.3 and before), unbound will exit instead of reload
|
||||
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.
|
||||
|
||||
* mailto:wouter@nlnetlabs.nl
|
||||
Acknowledgements
|
||||
----------------
|
||||
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.
|
||||
o Other code origins are from the NSD (NLnet Labs) and LDNS (NLnet Labs)
|
||||
projects. Such as buffer, region-allocator and red-black tree code.
|
||||
o See Credits file for contributors.
|
||||
|
||||
* mailto:unbound-bugs@nlnetlabs.nl
|
||||
|
||||
+5
-2
@@ -1,6 +1,9 @@
|
||||
README unbound tests
|
||||
|
||||
There is a test setup for unbound.
|
||||
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.
|
||||
|
||||
testdata/ contains the data for tests.
|
||||
testcode/ contains scripts and c code for the tests.
|
||||
@@ -8,4 +11,4 @@ testcode/ contains scripts and c code for the tests.
|
||||
do-tests.sh : runs all the tests in the testdata directory.
|
||||
testbed.sh : compiles on a set of (user specific) hosts and runs do-tests.
|
||||
|
||||
Tests are run using tpkg.
|
||||
Tests are run using testcode/mini_tpkg.sh.
|
||||
|
||||
@@ -1,20 +1,57 @@
|
||||
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 possible optimization with delayed malloc of msgreply after parse
|
||||
(reuse parse structures and ptrs to packet in meantime).
|
||||
o possible optimization, so that precious id number resource is not depleted
|
||||
by parsing of messages. Delay malloc, as above, or try to reverse release
|
||||
special id numbers, and if you release the next_id number for the thread
|
||||
it reuses that id number.
|
||||
o use rbtree to compress domain names in messages, sorted AXFRs of 16Kb
|
||||
and so on. For current packets, even worstcase, list, is ok. Only if we
|
||||
start to send out compressed AXFRs then it becomes a problem.
|
||||
We can cap AXFR packets on 256 or 1024 domain names, for example.
|
||||
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.
|
||||
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 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 configuration 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 delegpt use rbtree for ns-list, to avoid slowdown for very large NS sets.
|
||||
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 windows version, auto update feature, a query to check for the version.
|
||||
o autoreport of problems
|
||||
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. Keep counter of nearmisses and print that
|
||||
in the stats lines, operator can determine what level is a redalert.
|
||||
o improve compression of DNS packets by first puttig uncompressible rrs, then
|
||||
compress to their rdata.
|
||||
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.
|
||||
early failure, faster on first query, but more traffic.
|
||||
o use privilege separation, to change privilege options during reload securely
|
||||
not needed.
|
||||
o On Windows use CryptGenRandom() to get random seed for arc4random.
|
||||
o Think about intermediate firewalls dropping EDNS UDP & handling that.
|
||||
detect nonEDNS middlebox by timeout on edns queries, and fallback to
|
||||
nonEDNS when appropriate.
|
||||
o library add convenience functions for A, AAAA, PTR, getaddrinfo, libresolve.
|
||||
o library add function to get signature data (or whole reply message).
|
||||
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 in an ipv6 connected only environment unbound cannot use outgoing IP6
|
||||
to send to ip4to6 mapped hosts, need ip4to6map of NS and disable
|
||||
V6ONLY socket option.
|
||||
o support multiple dns messages in a TCP query stream for the unbound server.
|
||||
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.
|
||||
|
||||
+237
-19
@@ -13,21 +13,38 @@ server:
|
||||
# whitespace is not necessary, but looks cleaner.
|
||||
|
||||
# verbosity number, 0 is least verbose. 1 is default.
|
||||
verbosity: 2
|
||||
|
||||
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
|
||||
|
||||
# number of threads to create. 1 disables threading.
|
||||
# num-threads: 1
|
||||
|
||||
# specify the interfaces to answer queries from by ip-address.
|
||||
# If you give none the default (all) interface is used.
|
||||
# The default is to listen to localhost (127.0.0.1 and ::1).
|
||||
# specify 0.0.0.0 and ::0 to bind to all available interfaces.
|
||||
# specify every interface on a new 'interface:' labelled line.
|
||||
# The listen interfaces are not changed on reload, only on restart.
|
||||
# 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 not be supported on all platforms. experimental.
|
||||
# interface-automatic: no
|
||||
|
||||
# port to answer queries from
|
||||
# port: 53
|
||||
|
||||
# specify the interfaces to send outgoing queries to authoritative
|
||||
# server from by ip-address. If none, the default (all) interface
|
||||
# is used. Specify every interface on a 'outgoing-interface:' line.
|
||||
# outgoing-interface: 192.0.2.153
|
||||
# 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
|
||||
@@ -42,9 +59,16 @@ server:
|
||||
# number of outgoing simultaneous tcp buffers to hold per thread.
|
||||
# outgoing-num-tcp: 10
|
||||
|
||||
# number of incoming simultaneous tcp buffers to hold per thread.
|
||||
# incoming-num-tcp: 10
|
||||
|
||||
# buffer size for handling DNS data. No messages larger than this
|
||||
# size can be sent or received, by UDP or TCP. In bytes.
|
||||
# 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.
|
||||
@@ -55,13 +79,35 @@ server:
|
||||
# num-queries-per-thread: 1024
|
||||
|
||||
# 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.
|
||||
# more slabs reduce lock contention, but fragment memory usage.
|
||||
# rrset-cache-slabs: 4
|
||||
|
||||
# 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: 86400
|
||||
|
||||
# the time to live (TTL) value for cached roundtrip times and
|
||||
# EDNS version information for hosts. In seconds.
|
||||
# infra-host-ttl: 900
|
||||
|
||||
# the time to live (TTL) value for cached lame delegations. In sec.
|
||||
# infra-lame-ttl: 900
|
||||
|
||||
# the number of slabs to use for the Infrastructure cache.
|
||||
# the number of slabs must be a power of 2.
|
||||
# more slabs reduce lock contention, but fragment memory usage.
|
||||
# infra-cache-slabs: 4
|
||||
|
||||
# the maximum number of hosts that are cached (roundtrip times, EDNS).
|
||||
# infra-cache-numhosts: 10000
|
||||
|
||||
# the maximum size of the lame zones cached per host. in bytes.
|
||||
# infra-cache-lame-size: 10k
|
||||
|
||||
# Enable IPv4, "yes" or "no".
|
||||
# do-ip4: yes
|
||||
@@ -75,28 +121,200 @@ server:
|
||||
# Enable TCP, "yes" or "no".
|
||||
# do-tcp: yes
|
||||
|
||||
# Set this to configure unbound to act as a forwarder. All queries are
|
||||
# sent to the remote nameserver that will resolve them.
|
||||
# Set to "" to disable forwarding, or give ip-address to enable.
|
||||
# forward-to: ""
|
||||
|
||||
# The port number to send forwarded queries to.
|
||||
# forward-to-port: 53
|
||||
|
||||
# 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.
|
||||
# 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.
|
||||
# chroot: "/some/directory"
|
||||
# If you give "" no chroot is performed.
|
||||
# chroot: "/etc/unbound"
|
||||
|
||||
# if given, user privileges are dropped (after binding port),
|
||||
# and the given username is assumed. Default is nothing "".
|
||||
# and the given username is assumed. Default is user "unbound".
|
||||
# If you give "" no privileges are dropped.
|
||||
# username: "unbound"
|
||||
|
||||
# the working directory.
|
||||
# directory: "/etc/unbound"
|
||||
|
||||
# the log file, "" means log to stderr.
|
||||
# the log file, "" means log to stderr.
|
||||
# Use of this option sets use-syslog to "no".
|
||||
# logfile: ""
|
||||
|
||||
# Log to syslog(3) if yes. The log facility LOG_DAEMON is used to
|
||||
# log to, with identity "unbound". If yes, it overrides the logfile.
|
||||
# use-syslog: yes
|
||||
|
||||
# the pid file.
|
||||
# pidfile: "unbound.pid"
|
||||
# pidfile: "/etc/unbound/unbound.pid"
|
||||
|
||||
# file to read root hints from.
|
||||
# get one from ftp://FTP.INTERNIC.NET/domain/named.cache
|
||||
# root-hints: ""
|
||||
|
||||
# enable to not answer id.server and hostname.bind queries.
|
||||
# hide-identity: no
|
||||
|
||||
# enable to not answer version.server and version.bind queries.
|
||||
# hide-version: no
|
||||
|
||||
# the identity to report. Leave "" or default to return hostname.
|
||||
# identity: ""
|
||||
|
||||
# the version to report. Leave "" or default to return package version.
|
||||
# version: ""
|
||||
|
||||
# the target fetch policy.
|
||||
# series of integers describing the policy per dependency depth.
|
||||
# The number of values in the list determines the maximum dependency
|
||||
# depth the recursor will pursue before giving up. Each integer means:
|
||||
# -1 : fetch all targets opportunistically,
|
||||
# 0: fetch on demand,
|
||||
# positive value: fetch that many targets opportunistically.
|
||||
# Enclose the list of numbers between quotes ("").
|
||||
# target-fetch-policy: "3 2 1 0 0"
|
||||
|
||||
# Harden against very small EDNS buffer sizes.
|
||||
# harden-short-bufsize: no
|
||||
|
||||
# Harden against unseemly large queries.
|
||||
# harden-large-queries: no
|
||||
|
||||
# Harden against out of zone rrsets, to avoid spoofing attempts.
|
||||
# harden-glue: yes
|
||||
|
||||
# Harden against receiving dnssec-stripped data. If you turn it
|
||||
# off, failing to validate dnskey data for a trustanchor will
|
||||
# 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
|
||||
|
||||
# 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
|
||||
# do-not-query-address: ::1
|
||||
|
||||
# if yes, the above default do-not-query-address entries are present.
|
||||
# if no, localhost can be queried (for testing and debugging).
|
||||
# do-not-query-localhost: yes
|
||||
|
||||
# module configuration of the server. A string with identifiers
|
||||
# separated by spaces. "iterator" or "validator iterator"
|
||||
# module-config: "validator iterator"
|
||||
|
||||
# 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.
|
||||
# trust-anchor-file: ""
|
||||
|
||||
# Trusted key for validation. DS or DNSKEY. specify the RR on a
|
||||
# single line, surrounded by "". TTL is ignored. class is IN default.
|
||||
# (These examples are from August 2007 and may not be valid anymore).
|
||||
# trust-anchor: "nlnetlabs.nl. DNSKEY 257 3 5 AQPzzTWMz8qSWIQlfRnPckx2BiVmkVN6LPupO3mbz7FhLSnm26n6iG9N Lby97Ji453aWZY3M5/xJBSOS2vWtco2t8C0+xeO1bc/d6ZTy32DHchpW 6rDH1vp86Ll+ha0tmwyy9QP7y2bVw5zSbFCrefk8qCUBgfHm9bHzMG1U BYtEIQ=="
|
||||
# trust-anchor: "jelte.nlnetlabs.nl. DS 42860 5 1 14D739EB566D2B1A5E216A0BA4D17FA9B038BE4A"
|
||||
|
||||
# File with trusted keys for validation. Specify more than one file
|
||||
# with several entries, one file per entry. Like trust-anchor-file
|
||||
# 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.
|
||||
# val-override-date: ""
|
||||
|
||||
# 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
|
||||
|
||||
# Should additional section of secure message also be kept clean of
|
||||
# unsecure data. Useful to shield the users of this validator from
|
||||
# potential bogus data in the additional section. All unsigned data
|
||||
# in the additional section is removed from secure messages.
|
||||
# val-clean-additional: yes
|
||||
|
||||
# Turn permissive mode on to permit bogus messages. Thus, messages
|
||||
# for which security checks failed will be returned to clients,
|
||||
# instead of SERVFAIL. It still performs the security checks, which
|
||||
# result in interesting log files and possibly the AD bit in
|
||||
# replies if the message is found secure. The default is off.
|
||||
# val-permissive-mode: no
|
||||
|
||||
# It is possible to configure NSEC3 maximum iteration counts per
|
||||
# keysize. Keep this table very short, as linear search is done.
|
||||
# A message with an NSEC3 with larger count is marked insecure.
|
||||
# List in ascending order the keysize and count values.
|
||||
# val-nsec3-keysize-iterations: "1024 150 2048 500 4096 2500"
|
||||
|
||||
# the amount of memory to use for the key cache.
|
||||
# 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
|
||||
|
||||
# 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 serves local data, else, resolves normally .
|
||||
# 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"
|
||||
|
||||
|
||||
# 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.
|
||||
# stub-zone:
|
||||
# name: "example.com"
|
||||
# stub-addr: 192.0.2.68
|
||||
# stub-zone:
|
||||
# name: "example.org"
|
||||
# stub-host: ns.example.com.
|
||||
|
||||
# Forward zones
|
||||
# Create entries like below, to make all queries for 'example.com' and
|
||||
# 'example.org' go to the given list of servers. These servers have to handle
|
||||
# recursion to other nameservers. List zero or more nameservers by hostname
|
||||
# or by ipaddress. Use an entry with name "." to forward all queries.
|
||||
# forward-zone:
|
||||
# name: "example.com"
|
||||
# forward-addr: 192.0.2.68
|
||||
# forward-addr: 192.0.2.73@5355 # forward to port 5355.
|
||||
# forward-zone:
|
||||
# name: "example.org"
|
||||
# forward-host: fwd.example.com
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
.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 */
|
||||
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.
|
||||
@@ -29,14 +29,26 @@ Roughly the boxes are as follows:
|
||||
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 - good syslog, compressed packets, port ranges,
|
||||
config file format with config checker.
|
||||
0.7 Local zones feature - localzones stubzones fwdzones, no leak rfc1918.
|
||||
0.8 Library use - resolver validator lib (and test apps)
|
||||
0.9 Corner cases - be able to resolve in the wild. Run fuzzers.
|
||||
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.
|
||||
0.10 Beta release. Run shadow for a resolver in production for several
|
||||
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.
|
||||
@@ -115,7 +127,7 @@ Styleguide:
|
||||
* Speed test of caching forwarder.
|
||||
|
||||
*** Basic Resolver
|
||||
* Create module interfacce and module caller algorithm.
|
||||
* 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
|
||||
@@ -133,29 +145,76 @@ Styleguide:
|
||||
* 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
|
||||
* DNS packet compression of output.
|
||||
* test packet compression (that it compresses.)
|
||||
* config option to compress yes/no
|
||||
* speed test with and without compression.
|
||||
* Config file syntax checker program. Tests on checker.
|
||||
* Use a huge number of random outgoing ports.
|
||||
* 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.
|
||||
* 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.
|
||||
- in package, autoforkexec on localhost to do so.
|
||||
- not included. Not necessary for localhost and AS112 service.
|
||||
* forward local zone to remote server.
|
||||
- not included. Not necessary for localhost and AS112 service.
|
||||
* stub zones - send queries for a zone to configged nameserver.
|
||||
- Can be used for complicated setups. So, run auth server on a
|
||||
different port or pc, and stub it on the resolver. Resolver is
|
||||
not auth for zones, but resolution works. This enforces the split
|
||||
of recursive and auth servers.
|
||||
* 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:
|
||||
@@ -169,6 +228,31 @@ Styleguide:
|
||||
* 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.
|
||||
* 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.
|
||||
or try in TCP mode. Do not 'try all servers several times', since we must
|
||||
not create packet storms with operator errors.
|
||||
* draft-timers, DLV features.
|
||||
|
||||
treeshrew/
|
||||
validator/ *.c *.h
|
||||
@@ -225,10 +309,10 @@ treeshrew/
|
||||
unbound.c for validating caching recursive dns server.
|
||||
scheduler.c for the modules.
|
||||
|
||||
libunbound-all/
|
||||
libunbound/
|
||||
app linkable. Can be configged to do whatever,
|
||||
validator, iterator, validating iterator, forwarding stub.
|
||||
libunbound-fwd/
|
||||
libforwardbound/
|
||||
app linkable forwarding stub. Small lib.
|
||||
|
||||
ask_cachor/ *.c *.h
|
||||
|
||||
+128
-1
@@ -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
|
||||
@@ -79,3 +79,130 @@ o An authoritative name server.
|
||||
o Too many Features.
|
||||
|
||||
|
||||
5. Choices
|
||||
----------
|
||||
o rfc2181 decourages duplicates RRs in RRsets. unbound does not create
|
||||
duplicates, but when presented with duplicates on the wire from the
|
||||
authoritative servers, does not perform duplicate removal.
|
||||
It does do some rrsig duplicate removal, in the msgparser, for dnssec qtype
|
||||
rrsig and any, because of special rrsig processing in the msgparser.
|
||||
o The harden-glue feature, when yes all out of zone glue is deleted, when
|
||||
no out of zone glue is used for further resolving, is more complicated
|
||||
than that, see below.
|
||||
Main points:
|
||||
* rfc2182 trust handling is used.
|
||||
* data is let through only in very specific cases
|
||||
* spoofability remains possible.
|
||||
Not all glue is let through (despite the name of the option). Only glue
|
||||
which is present in a delegation, of type A and AAAA, where the name is
|
||||
present in the NS record in the authority section is let through.
|
||||
The glue that is let through is stored in the cache (marked as 'from the
|
||||
additional section'). And will then be used for sending queries to. It
|
||||
will not be present in the reply to the client (if RD is off).
|
||||
A direct query for that name will attempt to get a msg into the message
|
||||
cache. Since A and AAAA queries are not synthesized by the unbound cache,
|
||||
this query will be (eventually) sent to the authoritative server and its
|
||||
answer will be put in the cache, marked as 'from the answer section' and
|
||||
thus remove the 'from the additional section' data, and this record is
|
||||
returned to the client.
|
||||
The message has a TTL smaller or equal to the TTL of the answer RR.
|
||||
If the cache memory is low; the answer RR may be dropped, and a glue
|
||||
RR may be inserted, within the message TTL time, and thus return the
|
||||
spoofed glue to a client. When the message expires, it is refetched and
|
||||
the cached RR is updated with the correct content.
|
||||
The server can be spoofed by getting it to visit a especially prepared
|
||||
domain. This domain then inserts an address for another authoritative
|
||||
server into the cache, when visiting that other domain, this address may
|
||||
then be used to send queries to. And fake answers may be returned.
|
||||
If the other domain is signed by DNSSEC, the fakes will be detected.
|
||||
|
||||
In summary, the harden glue feature presents a security risk if
|
||||
disabled. Disabling the feature leads to possible better performance
|
||||
as more glue is present for the recursive service to use. The feature
|
||||
is implemented so as to minimise the security risk, while trying to
|
||||
keep this performance gain.
|
||||
o The method by which dnssec-lameness is detected is not secure. DNSSEC lame
|
||||
is when a server has the zone in question, but lacks dnssec data, such as
|
||||
signatures. The method to detect dnssec lameness looks at nonvalidated
|
||||
data from the parent of a zone. This can be used, by spoofing the parent,
|
||||
to create a false sense of dnssec-lameness in the child, or a false sense
|
||||
or dnssec-non-lameness in the child. The first results in the server marked
|
||||
lame, and not used for 900 seconds, and the second will result in a
|
||||
validator failure (SERVFAIL again), when the query is validated later on.
|
||||
|
||||
Concluding, a spoof of the parent delegation can be used for many cases
|
||||
of denial of service. I.e. a completely different NS set could be returned,
|
||||
or the information withheld. All of these alterations can be caught by
|
||||
the validator if the parent is signed, and result in 900 seconds bogus.
|
||||
The dnssec-lameness detection is used to detect operator failures,
|
||||
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 dnssec to our
|
||||
clients when possible (for client validators).
|
||||
|
||||
The following issue needs to be resolved:
|
||||
a server that serves both a parent and child zone, where
|
||||
parent is signed, but child is not. The server must not be marked
|
||||
lame for the parent zone, because the child answer is not signed.
|
||||
Instead of a false positive, we want false negatives; failure to
|
||||
detect dnssec-lameness is less of a problem than marking honest
|
||||
servers lame. dnssec-lameness is a config error and deserves the trouble.
|
||||
So, only messages that identify the zone are used to mark the zone
|
||||
lame. The zone is identified by SOA or NS RRsets in the answer/auth.
|
||||
That includes almost all negative responses and also A, AAAA qtypes.
|
||||
That would be most responses from servers.
|
||||
For referrals, delegations that add a single label can be checked to be
|
||||
from their zone, this covers most delegation-centric zones.
|
||||
|
||||
So possibly, for complicated setups, with multiple (parent-child) zones
|
||||
on a server, dnssec-lameness detection does not work - no dnssec-lameness
|
||||
is detected. Instead the zone that is dnssec-lame becomes bogus.
|
||||
|
||||
o authority features.
|
||||
This is a recursive server, and authority features are out of scope.
|
||||
However, some authority features are expected in a recursor. Things like
|
||||
localhost, reverse lookup for 127.0.0.1, or blocking AS112 traffic.
|
||||
Also redirection of domain names with fixed data is needed by service
|
||||
providers. Limited support is added specifically to address this.
|
||||
|
||||
Adding full authority support, requires much more code, and more complex
|
||||
maintenance.
|
||||
|
||||
The limited support allows adding some static data (for localhost and so),
|
||||
and to respond with a fixed rcode (NXDOMAIN) for domains (such as AS112).
|
||||
|
||||
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 authority server is used in preference to the casing
|
||||
of the query name. This is different from BIND. RFC4343 allows either
|
||||
behaviour.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
.TH "unbound-checkconf" "8" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" unbound-checkconf.8 -- unbound configuration checker manual
|
||||
.\"
|
||||
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
.\"
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.SH "NAME"
|
||||
.LP
|
||||
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
|
||||
\fIunbound\fR(8)
|
||||
DNS resolver for syntax and other errors.
|
||||
The config file syntax is described in
|
||||
\fIunbound.conf\fR(5).
|
||||
.P
|
||||
The available options are:
|
||||
.TP
|
||||
.B \-h
|
||||
Show the version and commandline option help.
|
||||
.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"
|
||||
The unbound-checkconf program exits with status code 1 on error,
|
||||
0 for a correct config file.
|
||||
.SH "FILES"
|
||||
.TP
|
||||
.I /etc/unbound/unbound.conf
|
||||
unbound configuration file.
|
||||
.SH "SEE ALSO"
|
||||
\fIunbound.conf\fR(5),
|
||||
\fIunbound\fR(8).
|
||||
@@ -0,0 +1,93 @@
|
||||
.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 [ \-vdh ]
|
||||
.RB [ \-c
|
||||
.IR class ]
|
||||
.RB [ \-t
|
||||
.IR type ]
|
||||
.I hostname
|
||||
.RB [ \-y
|
||||
.IR key ]
|
||||
.RB [ \-f
|
||||
.IR keyfile ]
|
||||
.RB [ \-F
|
||||
.IR namedkeyfile ]
|
||||
.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.
|
||||
.SH "EXAMPLES"
|
||||
.LP
|
||||
Some examples of use.
|
||||
.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).
|
||||
+43
-70
@@ -1,78 +1,51 @@
|
||||
.ig
|
||||
unbound.8 -- unbound manual
|
||||
|
||||
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.
|
||||
|
||||
..
|
||||
.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
|
||||
.TH "unbound" "8" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" unbound.8 -- unbound manual
|
||||
.\"
|
||||
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
.\"
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.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 to read with settings for unbound. 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 the
|
||||
file at default location /etc/unbound/unbound.conf. 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.
|
||||
|
||||
.It Fl v
|
||||
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.
|
||||
.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 .
|
||||
|
||||
.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.
|
||||
|
||||
+593
-74
@@ -1,3 +1,4 @@
|
||||
.TH "unbound.conf" "5" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" unbound.conf.5 -- unbound.conf manual
|
||||
.\"
|
||||
@@ -6,120 +7,638 @@
|
||||
.\" 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
|
||||
.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
|
||||
.Xr unbound 8 .
|
||||
\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.
|
||||
|
||||
.El
|
||||
.Sh FILE FORMAT
|
||||
.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 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.
|
||||
# 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.
|
||||
|
||||
.Pp
|
||||
.P
|
||||
Files can be included using the
|
||||
.Ic include:
|
||||
.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.
|
||||
|
||||
.Ss Server Options
|
||||
There may only be one
|
||||
.Ic server:
|
||||
.SS "Server Options"
|
||||
These options are part of the
|
||||
.B server:
|
||||
clause.
|
||||
.Bl -tag -width indent
|
||||
.It \fBverbosity:\fR <number>
|
||||
.TP
|
||||
.B verbosity: \fI<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.
|
||||
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
|
||||
.Xr unbound 8 .
|
||||
.It \fBnum-threads:\fR <number>
|
||||
\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 num\-threads: \fI<number>
|
||||
The number of threads to create to serve clients. Use 1 for no threading.
|
||||
.It \fBport:\fR <port number>
|
||||
.TP
|
||||
.B port: \fI<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. Can be given multiple times to
|
||||
work on several interfaces. If none are given the default (all) is used.
|
||||
.It \fBoutgoing-port:\fR <port number>
|
||||
.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\-port: \fI<port number>
|
||||
The starting port number where the outgoing query port range is allocated.
|
||||
Default is 1053.
|
||||
.It \fBoutgoing-range:\fR <number>
|
||||
.TP
|
||||
.B outgoing\-range: \fI<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>
|
||||
.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.
|
||||
.It \fBmsg-cache-size:\fR <number>
|
||||
.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.
|
||||
.It \fBmsg-cache-slabs:\fR <number>
|
||||
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.
|
||||
.It \fBnum-queries-per-thread:\fR <number>
|
||||
.TP
|
||||
.B num\-queries\-per\-thread: \fI<number>
|
||||
The number of queries that every thread will service simultaneously.
|
||||
.It \fBrrset-cache-size:\fR <number>
|
||||
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.
|
||||
.TP
|
||||
.B rrset\-cache\-size: \fI<number>
|
||||
Number of bytes size of the RRset cache. Default is 4 megabytes.
|
||||
.It \fBrrset-cache-slabs:\fR <number>
|
||||
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.
|
||||
.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 \fBforward-to:\fR <ip address>
|
||||
If set (not "") then forwarder mode is enabled. Default is "" (disabled).
|
||||
The ip address is used to forward all DNS queries to.
|
||||
.It \fBforward-to-port:\fR <port number>
|
||||
The port on which the remote server is running that answers forwarded queries.
|
||||
Default is 53.
|
||||
.It \fBchroot:\fR <directory>
|
||||
If given a chroot is done to the given directory. The default is none ("").
|
||||
.It \fBusername:\fR <name>
|
||||
.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 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 deny, refuse or allow.
|
||||
Deny stops queries from hosts from that netblock.
|
||||
Refuse stops queries too, but sends a DNS rcode REFUSED error message back.
|
||||
Allow gives access to clients from that netblock.
|
||||
By default only localhost is allowed, the rest is refused.
|
||||
The default is refused, 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 given a chroot is done to the given directory. The default is
|
||||
"/etc/unbound". 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
|
||||
not to change user, username: "". If this user is not capable of binding the
|
||||
"unbound". 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 restart is needed.
|
||||
.It \fBdirectory:\fR <directory>
|
||||
requires privileges, then a reload will fail; a restart is needed.
|
||||
.TP
|
||||
.B directory: \fI<directory>
|
||||
Sets the working directory for the program.
|
||||
.It \fBlogfile:\fR <filename>
|
||||
.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.
|
||||
.It \fBpidfile:\fR <filename>
|
||||
The process id is written to the file. Default is "unbound.pid". So,
|
||||
kill -HUP `cat /etc/unbound/unbound.pid` will trigger a reload,
|
||||
kill -QUIT `cat /etc/unbound/unbound.pid` will gracefully terminate.
|
||||
|
||||
.Sh FILES
|
||||
.Bl -tag -width indent
|
||||
.It Pa unbound.conf
|
||||
.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 "/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.
|
||||
.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 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.
|
||||
.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 900.
|
||||
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 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, the query is resolved normally.
|
||||
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.
|
||||
.\" 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.
|
||||
.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.
|
||||
.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 1 meg of memory,
|
||||
the below example is not for you. Use the defaults to receive full service.
|
||||
.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: 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"
|
||||
.TP
|
||||
.I /etc/unbound
|
||||
default unbound working directory and default
|
||||
\fIchroot\fR(2)
|
||||
location.
|
||||
.TP
|
||||
.I unbound.conf
|
||||
unbound configuration file.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr unbound 8
|
||||
.Sh AUTHORS
|
||||
.Ic Unbound
|
||||
.TP
|
||||
.I unbound.pid
|
||||
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.
|
||||
|
||||
+71
-45
@@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.4.6
|
||||
# Doxyfile 1.5.2
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project
|
||||
@@ -14,6 +14,14 @@
|
||||
# Project related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# This tag specifies the encoding used for all characters in the config file that
|
||||
# follow. The default is UTF-8 which is also the encoding used for all text before
|
||||
# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into
|
||||
# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of
|
||||
# possible encodings.
|
||||
|
||||
# DOXYFILE_ENCODING = UTF-8
|
||||
|
||||
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
|
||||
# by quotes) that should identify the project.
|
||||
|
||||
@@ -45,24 +53,14 @@ CREATE_SUBDIRS = NO
|
||||
# documentation generated by doxygen is written. Doxygen will use this
|
||||
# information to generate all constant output in the proper language.
|
||||
# The default language is English, other supported languages are:
|
||||
# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish,
|
||||
# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese,
|
||||
# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian,
|
||||
# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish,
|
||||
# Swedish, and Ukrainian.
|
||||
# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
|
||||
# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
|
||||
# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
|
||||
# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
|
||||
# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
|
||||
|
||||
OUTPUT_LANGUAGE = English
|
||||
|
||||
# This tag can be used to specify the encoding used in the generated output.
|
||||
# The encoding is not always determined by the language that is chosen,
|
||||
# but also whether or not the output is meant for Windows or non-Windows users.
|
||||
# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
|
||||
# forces the Windows encoding (this is the default for the Windows binary),
|
||||
# whereas setting the tag to NO uses a Unix-style encoding (the default for
|
||||
# all platforms other than Windows).
|
||||
|
||||
USE_WINDOWS_ENCODING = NO
|
||||
|
||||
# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
|
||||
# include brief member descriptions after the members that are listed in
|
||||
# the file and class documentation (similar to JavaDoc).
|
||||
@@ -204,6 +202,11 @@ OPTIMIZE_OUTPUT_JAVA = NO
|
||||
|
||||
BUILTIN_STL_SUPPORT = NO
|
||||
|
||||
# If you use Microsoft's C++/CLI language, you should set this option to YES to
|
||||
# enable parsing support.
|
||||
|
||||
# CPP_CLI_SUPPORT = NO
|
||||
|
||||
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
|
||||
# tag is set to YES, then doxygen will reuse the documentation of the first
|
||||
# member in the group (if any) for the other members of the group. By default
|
||||
@@ -459,7 +462,14 @@ WARN_LOGFILE =
|
||||
# directories like "/usr/src/myproject". Separate the files or directories
|
||||
# with spaces.
|
||||
|
||||
INPUT = .
|
||||
INPUT = .
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files that
|
||||
# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default
|
||||
# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding.
|
||||
# See http://www.gnu.org/software/libiconv for the list of possible encodings.
|
||||
|
||||
# INPUT_ENCODING = UTF-8
|
||||
|
||||
# If the value of the INPUT tag contains directories, you can use the
|
||||
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
|
||||
@@ -468,7 +478,7 @@ INPUT = .
|
||||
# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
|
||||
# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
|
||||
|
||||
#FILE_PATTERNS = *.h *.c *.dox
|
||||
FILE_PATTERNS =
|
||||
|
||||
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
|
||||
# should be searched for input files as well. Possible values are YES and NO.
|
||||
@@ -480,7 +490,13 @@ RECURSIVE = YES
|
||||
# excluded from the INPUT source files. This way you can easily exclude a
|
||||
# subdirectory from a directory tree whose root is specified with the INPUT tag.
|
||||
|
||||
EXCLUDE = ./build ./compat util/configparser.c util/configparser.h util/configlexer.c util/locks.h
|
||||
EXCLUDE = ./build \
|
||||
./compat \
|
||||
util/configparser.c \
|
||||
util/configparser.h \
|
||||
util/configlexer.c \
|
||||
util/locks.h \
|
||||
./ldns-src
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
|
||||
# directories that are symbolic links (a Unix filesystem feature) are excluded
|
||||
@@ -496,6 +512,13 @@ EXCLUDE_SYMLINKS = NO
|
||||
|
||||
EXCLUDE_PATTERNS =
|
||||
|
||||
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
|
||||
# (namespaces, classes, functions, etc.) that should be excluded from the output.
|
||||
# The symbol name can be a fully qualified name, a word, or if the wildcard * is used,
|
||||
# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
|
||||
|
||||
# EXCLUDE_SYMBOLS =
|
||||
|
||||
# The EXAMPLE_PATH tag can be used to specify one or more files or
|
||||
# directories that contain example code fragments that are included (see
|
||||
# the \include command).
|
||||
@@ -581,6 +604,13 @@ REFERENCED_BY_RELATION = YES
|
||||
|
||||
REFERENCES_RELATION = YES
|
||||
|
||||
# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
|
||||
# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
|
||||
# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
|
||||
# link to the source code. Otherwise they will link to the documentstion.
|
||||
|
||||
# REFERENCES_LINK_SOURCE = YES
|
||||
|
||||
# If the USE_HTAGS tag is set to YES then the references to source code
|
||||
# will point to the HTML generated by the htags(1) tool instead of doxygen
|
||||
# built-in source browser. The htags tool is part of GNU's global source
|
||||
@@ -1078,6 +1108,14 @@ PERL_PATH = /usr/bin/perl
|
||||
|
||||
CLASS_DIAGRAMS = YES
|
||||
|
||||
# You can define message sequence charts within doxygen comments using the \msc
|
||||
# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to
|
||||
# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to
|
||||
# specify the directory where the mscgen tool resides. If left empty the tool is assumed to
|
||||
# be found in the default search path.
|
||||
|
||||
# MSCGEN_PATH =
|
||||
|
||||
# If set to YES, the inheritance and collaboration graphs will hide
|
||||
# inheritance and usage relations if the target is undocumented
|
||||
# or is not a class.
|
||||
@@ -1143,6 +1181,14 @@ INCLUDED_BY_GRAPH = YES
|
||||
|
||||
CALL_GRAPH = NO
|
||||
|
||||
# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will
|
||||
# generate a caller dependency graph for every global function or class method.
|
||||
# Note that enabling this option will significantly increase the time of a run.
|
||||
# So in most cases it will be better to enable caller graphs for selected
|
||||
# functions only using the \callergraph command.
|
||||
|
||||
# CALLER_GRAPH = NO
|
||||
|
||||
# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
|
||||
# will graphical hierarchy of all classes instead of a textual one.
|
||||
|
||||
@@ -1172,33 +1218,13 @@ DOT_PATH =
|
||||
|
||||
DOTFILE_DIRS =
|
||||
|
||||
# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
|
||||
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
|
||||
# this value, doxygen will try to truncate the graph, so that it fits within
|
||||
# the specified constraint. Beware that most browsers cannot cope with very
|
||||
# large images.
|
||||
# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
|
||||
# nodes that will be shown in the graph. If the number of nodes in a graph
|
||||
# becomes larger than this value, doxygen will truncate the graph, which is
|
||||
# visualized by representing a node as a red box. Note that doxygen will always
|
||||
# show the root nodes and its direct children regardless of this setting.
|
||||
|
||||
MAX_DOT_GRAPH_WIDTH = 1024
|
||||
|
||||
# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
|
||||
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
|
||||
# this value, doxygen will try to truncate the graph, so that it fits within
|
||||
# the specified constraint. Beware that most browsers cannot cope with very
|
||||
# large images.
|
||||
|
||||
MAX_DOT_GRAPH_HEIGHT = 1024
|
||||
|
||||
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
|
||||
# graphs generated by dot. A depth value of 3 means that only nodes reachable
|
||||
# from the root by following a path via at most 3 edges will be shown. Nodes
|
||||
# that lay further from the root node will be omitted. Note that setting this
|
||||
# option to 1 or 2 may greatly reduce the computation time needed for large
|
||||
# code bases. Also note that a graph may be further truncated if the graph's
|
||||
# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH
|
||||
# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default),
|
||||
# the graph is not depth-constrained.
|
||||
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
# DOT_GRAPH_MAX_NODES = 50
|
||||
|
||||
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
|
||||
# background. This is disabled by default, which results in a white background.
|
||||
|
||||
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
* iterator/iter_delegpt.c - delegation point with NS and address information.
|
||||
*
|
||||
* 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 implements the Delegation Point. It contains a list of name servers
|
||||
* and their addresses if known.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/net_help.h"
|
||||
|
||||
struct delegpt*
|
||||
delegpt_create(struct regional* region)
|
||||
{
|
||||
struct delegpt* dp=(struct delegpt*)regional_alloc(
|
||||
region, sizeof(*dp));
|
||||
if(!dp)
|
||||
return NULL;
|
||||
memset(dp, 0, sizeof(*dp));
|
||||
return dp;
|
||||
}
|
||||
|
||||
struct delegpt* delegpt_copy(struct delegpt* dp, struct regional* region)
|
||||
{
|
||||
struct delegpt* copy = delegpt_create(region);
|
||||
struct delegpt_ns* ns;
|
||||
struct delegpt_addr* a;
|
||||
if(!copy)
|
||||
return NULL;
|
||||
if(!delegpt_set_name(copy, region, dp->name))
|
||||
return NULL;
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
if(!delegpt_add_ns(copy, region, ns->name))
|
||||
return NULL;
|
||||
copy->nslist->resolved = ns->resolved;
|
||||
}
|
||||
for(a = dp->target_list; a; a = a->next_target) {
|
||||
if(!delegpt_add_addr(copy, region, &a->addr, a->addrlen))
|
||||
return NULL;
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_set_name(struct delegpt* dp, struct regional* region, uint8_t* name)
|
||||
{
|
||||
dp->namelabs = dname_count_size_labels(name, &dp->namelen);
|
||||
dp->name = regional_alloc_init(region, name, dp->namelen);
|
||||
return dp->name != 0;
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_add_ns(struct delegpt* dp, struct regional* region, uint8_t* name)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
size_t len;
|
||||
(void)dname_count_size_labels(name, &len);
|
||||
/* slow check for duplicates to avoid counting failures when
|
||||
* adding the same server as a dependency twice */
|
||||
if(delegpt_find_ns(dp, name, len))
|
||||
return 1;
|
||||
ns = (struct delegpt_ns*)regional_alloc(region,
|
||||
sizeof(struct delegpt_ns));
|
||||
if(!ns)
|
||||
return 0;
|
||||
ns->next = dp->nslist;
|
||||
ns->namelen = len;
|
||||
dp->nslist = ns;
|
||||
ns->name = regional_alloc_init(region, name, ns->namelen);
|
||||
ns->resolved = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct delegpt_ns*
|
||||
delegpt_find_ns(struct delegpt* dp, uint8_t* name, size_t namelen)
|
||||
{
|
||||
struct delegpt_ns* p = dp->nslist;
|
||||
while(p) {
|
||||
if(namelen == p->namelen &&
|
||||
query_dname_compare(name, p->name) == 0) {
|
||||
return p;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_add_addr(struct delegpt* dp, struct regional* region,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen)
|
||||
{
|
||||
struct delegpt_addr* a = (struct delegpt_addr*)regional_alloc(region,
|
||||
sizeof(struct delegpt_addr));
|
||||
if(!a)
|
||||
return 0;
|
||||
a->next_target = dp->target_list;
|
||||
dp->target_list = a;
|
||||
a->next_result = 0;
|
||||
a->next_usable = dp->usable_list;
|
||||
dp->usable_list = a;
|
||||
memcpy(&a->addr, addr, addrlen);
|
||||
a->addrlen = addrlen;
|
||||
a->attempts = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** count NS and number missing */
|
||||
static void
|
||||
delegpt_count_ns(struct delegpt* dp, size_t* numns, size_t* missing)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
*numns = 0;
|
||||
*missing = 0;
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
(*numns)++;
|
||||
if(!ns->resolved)
|
||||
(*missing)++;
|
||||
}
|
||||
}
|
||||
|
||||
/** count addresses, and number in result and available lists */
|
||||
static void
|
||||
delegpt_count_addr(struct delegpt* dp, size_t* numaddr, size_t* numres,
|
||||
size_t* numavail)
|
||||
{
|
||||
struct delegpt_addr* a;
|
||||
*numaddr = 0;
|
||||
*numres = 0;
|
||||
*numavail = 0;
|
||||
for(a = dp->target_list; a; a = a->next_target) {
|
||||
(*numaddr)++;
|
||||
}
|
||||
for(a = dp->result_list; a; a = a->next_result) {
|
||||
(*numres)++;
|
||||
}
|
||||
for(a = dp->usable_list; a; a = a->next_usable) {
|
||||
(*numavail)++;
|
||||
}
|
||||
}
|
||||
|
||||
void delegpt_log(enum verbosity_value v, struct delegpt* dp)
|
||||
{
|
||||
char buf[LDNS_MAX_DOMAINLEN+1];
|
||||
struct delegpt_ns* ns;
|
||||
struct delegpt_addr* a;
|
||||
size_t missing=0, numns=0, numaddr=0, numres=0, numavail=0;
|
||||
if(verbosity < v)
|
||||
return;
|
||||
dname_str(dp->name, buf);
|
||||
if(dp->nslist == NULL && dp->target_list == NULL) {
|
||||
log_info("DelegationPoint<%s>: empty", buf);
|
||||
return;
|
||||
}
|
||||
delegpt_count_ns(dp, &numns, &missing);
|
||||
delegpt_count_addr(dp, &numaddr, &numres, &numavail);
|
||||
log_info("DelegationPoint<%s>: %u names (%u missing), "
|
||||
"%u addrs (%u result, %u avail)",
|
||||
buf, (unsigned)numns, (unsigned)missing,
|
||||
(unsigned)numaddr, (unsigned)numres, (unsigned)numavail);
|
||||
if(verbosity >= VERB_ALGO) {
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
dname_str(ns->name, buf);
|
||||
log_info(" %s%s", buf, (ns->resolved?"*":""));
|
||||
}
|
||||
for(a = dp->target_list; a; a = a->next_target) {
|
||||
log_addr(VERB_ALGO, " ", &a->addr, a->addrlen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
delegpt_add_unused_targets(struct delegpt* dp)
|
||||
{
|
||||
struct delegpt_addr* usa = dp->usable_list;
|
||||
dp->usable_list = NULL;
|
||||
while(usa) {
|
||||
usa->next_result = dp->result_list;
|
||||
dp->result_list = usa;
|
||||
usa = usa->next_usable;
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
delegpt_count_missing_targets(struct delegpt* dp)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
size_t n = 0;
|
||||
for(ns = dp->nslist; ns; ns = ns->next)
|
||||
if(!ns->resolved)
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
struct delegpt*
|
||||
delegpt_from_message(struct dns_msg* msg, struct regional* region)
|
||||
{
|
||||
struct ub_packed_rrset_key* ns_rrset = NULL;
|
||||
struct delegpt* dp;
|
||||
size_t i;
|
||||
/* look for NS records in the authority section... */
|
||||
ns_rrset = find_NS(msg->rep, msg->rep->an_numrrsets,
|
||||
msg->rep->an_numrrsets+msg->rep->ns_numrrsets);
|
||||
|
||||
/* In some cases (even legitimate, perfectly legal cases), the
|
||||
* NS set for the "referral" might be in the answer section. */
|
||||
if(!ns_rrset)
|
||||
ns_rrset = find_NS(msg->rep, 0, msg->rep->an_numrrsets);
|
||||
|
||||
/* If there was no NS rrset in the authority section, then this
|
||||
* wasn't a referral message. (It might not actually be a
|
||||
* referral message anyway) */
|
||||
if(!ns_rrset)
|
||||
return NULL;
|
||||
|
||||
/* If we found any, then Yay! we have a delegation point. */
|
||||
dp = delegpt_create(region);
|
||||
if(!dp)
|
||||
return NULL;
|
||||
if(!delegpt_set_name(dp, region, ns_rrset->rk.dname))
|
||||
return NULL;
|
||||
if(!delegpt_rrset_add_ns(dp, region, ns_rrset))
|
||||
return NULL;
|
||||
|
||||
/* add glue, A and AAAA in answer and additional section */
|
||||
for(i=0; i<msg->rep->rrset_count; i++) {
|
||||
struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
|
||||
/* skip auth section. FIXME really needed?*/
|
||||
if(msg->rep->an_numrrsets <= i &&
|
||||
i < (msg->rep->an_numrrsets+msg->rep->ns_numrrsets))
|
||||
continue;
|
||||
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_A) {
|
||||
if(!delegpt_add_rrset_A(dp, region, s))
|
||||
return NULL;
|
||||
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_AAAA) {
|
||||
if(!delegpt_add_rrset_AAAA(dp, region, s))
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_rrset_add_ns(struct delegpt* dp, struct regional* region,
|
||||
struct ub_packed_rrset_key* ns_rrset)
|
||||
{
|
||||
struct packed_rrset_data* nsdata = (struct packed_rrset_data*)
|
||||
ns_rrset->entry.data;
|
||||
size_t i;
|
||||
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) !=
|
||||
(size_t)ldns_read_uint16(nsdata->rr_data[i]))
|
||||
continue; /* bad format */
|
||||
/* add rdata of NS (= wirefmt dname), skip rdatalen bytes */
|
||||
if(!delegpt_add_ns(dp, region, nsdata->rr_data[i]+2))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_add_rrset_A(struct delegpt* dp, struct regional* region,
|
||||
struct ub_packed_rrset_key* ak)
|
||||
{
|
||||
struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
|
||||
size_t i;
|
||||
struct sockaddr_in sa;
|
||||
socklen_t 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(i=0; i<d->count; i++) {
|
||||
if(d->rr_len[i] != 2 + INET_SIZE)
|
||||
continue;
|
||||
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))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region,
|
||||
struct ub_packed_rrset_key* ak)
|
||||
{
|
||||
struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
|
||||
size_t i;
|
||||
struct sockaddr_in6 sa;
|
||||
socklen_t 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(i=0; i<d->count; i++) {
|
||||
if(d->rr_len[i] != 2 + INET6_SIZE) /* rdatalen + len of IP6 */
|
||||
continue;
|
||||
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))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
delegpt_add_rrset(struct delegpt* dp, struct regional* region,
|
||||
struct ub_packed_rrset_key* rrset)
|
||||
{
|
||||
if(!rrset)
|
||||
return 1;
|
||||
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);
|
||||
else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_AAAA)
|
||||
return delegpt_add_rrset_AAAA(dp, region, rrset);
|
||||
log_warn("Unknown rrset type added to delegpt");
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* iterator/iter_delegpt.h - delegation point with NS and address information.
|
||||
*
|
||||
* 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 implements the Delegation Point. It contains a list of name servers
|
||||
* and their addresses if known.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_DELEGPT_H
|
||||
#define ITERATOR_ITER_DELEGPT_H
|
||||
#include "util/log.h"
|
||||
struct regional;
|
||||
struct delegpt_ns;
|
||||
struct delegpt_addr;
|
||||
struct dns_msg;
|
||||
struct ub_packed_rrset_key;
|
||||
|
||||
/**
|
||||
* Delegation Point.
|
||||
* For a domain name, the NS rrset, and the A and AAAA records for those.
|
||||
*/
|
||||
struct delegpt {
|
||||
/** the domain name of the delegation point. */
|
||||
uint8_t* name;
|
||||
/** length of the delegation point name */
|
||||
size_t namelen;
|
||||
/** number of labels in delegation point */
|
||||
int namelabs;
|
||||
|
||||
/** the nameservers, names from the NS RRset rdata. */
|
||||
struct delegpt_ns* nslist;
|
||||
/** the target addresses for delegation */
|
||||
struct delegpt_addr* target_list;
|
||||
/** the list of usable targets; subset of target_list
|
||||
* the items in this list are not part of the result list. */
|
||||
struct delegpt_addr* usable_list;
|
||||
/** the list of returned targets; subset of target_list */
|
||||
struct delegpt_addr* result_list;
|
||||
};
|
||||
|
||||
/**
|
||||
* Nameservers for a delegation point.
|
||||
*/
|
||||
struct delegpt_ns {
|
||||
/** next in list */
|
||||
struct delegpt_ns* next;
|
||||
/** name of nameserver */
|
||||
uint8_t* name;
|
||||
/** length of name */
|
||||
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.
|
||||
*/
|
||||
int resolved;
|
||||
};
|
||||
|
||||
/**
|
||||
* Address of target nameserver in delegation point.
|
||||
*/
|
||||
struct delegpt_addr {
|
||||
/** next delegation point in results */
|
||||
struct delegpt_addr* next_result;
|
||||
/** next delegation point in usable list */
|
||||
struct delegpt_addr* next_usable;
|
||||
/** next delegation point in all targets list */
|
||||
struct delegpt_addr* next_target;
|
||||
|
||||
/** delegation point address */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr */
|
||||
socklen_t addrlen;
|
||||
/** number of attempts for this addr */
|
||||
int attempts;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create new delegation point.
|
||||
* @param regional: where to allocate it.
|
||||
* @return new delegation point or NULL on error.
|
||||
*/
|
||||
struct delegpt* delegpt_create(struct regional* regional);
|
||||
|
||||
/**
|
||||
* Create a copy of a delegation point.
|
||||
* @param dp: delegation point to copy.
|
||||
* @param regional: where to allocate it.
|
||||
* @return new delegation point or NULL on error.
|
||||
*/
|
||||
struct delegpt* delegpt_copy(struct delegpt* dp, struct regional* regional);
|
||||
|
||||
/**
|
||||
* Set name of delegation point.
|
||||
* @param dp: delegation point.
|
||||
* @param regional: where to allocate the name copy.
|
||||
* @param name: name to use.
|
||||
* @return false on error.
|
||||
*/
|
||||
int delegpt_set_name(struct delegpt* dp, struct regional* regional,
|
||||
uint8_t* name);
|
||||
|
||||
/**
|
||||
* Add a name to the delegation point.
|
||||
* @param dp: delegation point.
|
||||
* @param regional: where to allocate the info.
|
||||
* @param name: domain name in wire format.
|
||||
* @return false on error.
|
||||
*/
|
||||
int delegpt_add_ns(struct delegpt* dp, struct regional* regional,
|
||||
uint8_t* name);
|
||||
|
||||
/**
|
||||
* Add NS rrset; calls add_ns repeatedly.
|
||||
* @param dp: delegation point.
|
||||
* @param regional: where to allocate the info.
|
||||
* @param ns_rrset: NS rrset.
|
||||
* return 0 on alloc error.
|
||||
*/
|
||||
int delegpt_rrset_add_ns(struct delegpt* dp, struct regional* regional,
|
||||
struct ub_packed_rrset_key* ns_rrset);
|
||||
|
||||
/**
|
||||
* Add target address to the delegation point.
|
||||
* @param dp: delegation point.
|
||||
* @param regional: where to allocate the info.
|
||||
* @param name: name for which target was found (must be in nslist).
|
||||
* This name is marked resolved.
|
||||
* @param namelen: length of name.
|
||||
* @param addr: the address.
|
||||
* @param addrlen: the length of addr.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Add A RRset to delegpt.
|
||||
* @param dp: delegation point.
|
||||
* @param regional: where to allocate the info.
|
||||
* @param rrset: RRset A to add.
|
||||
* @return 0 on alloc error.
|
||||
*/
|
||||
int delegpt_add_rrset_A(struct delegpt* dp, struct regional* regional,
|
||||
struct ub_packed_rrset_key* rrset);
|
||||
|
||||
/**
|
||||
* Add AAAA RRset to delegpt.
|
||||
* @param dp: delegation point.
|
||||
* @param regional: where to allocate the info.
|
||||
* @param rrset: RRset AAAA to add.
|
||||
* @return 0 on alloc error.
|
||||
*/
|
||||
int delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* regional,
|
||||
struct ub_packed_rrset_key* rrset);
|
||||
|
||||
/**
|
||||
* Add any RRset to delegpt.
|
||||
* @param dp: delegation point.
|
||||
* @param regional: where to allocate the info.
|
||||
* @param rrset: RRset to add, NS, A, AAAA.
|
||||
* @return 0 on alloc error.
|
||||
*/
|
||||
int delegpt_add_rrset(struct delegpt* dp, struct regional* regional,
|
||||
struct ub_packed_rrset_key* rrset);
|
||||
|
||||
/**
|
||||
* Add address to the delegation point. No servername is associated or checked.
|
||||
* @param dp: delegation point.
|
||||
* @param regional: where to allocate the info.
|
||||
* @param addr: the address.
|
||||
* @param addrlen: the length of addr.
|
||||
* @return false on error.
|
||||
*/
|
||||
int delegpt_add_addr(struct delegpt* dp, struct regional* regional,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen);
|
||||
|
||||
/**
|
||||
* Find NS record in name list of delegation point.
|
||||
* @param dp: delegation point.
|
||||
* @param name: name of nameserver to look for, uncompressed wireformat.
|
||||
* @param namelen: length of name.
|
||||
* @return the ns structure or NULL if not found.
|
||||
*/
|
||||
struct delegpt_ns* delegpt_find_ns(struct delegpt* dp, uint8_t* name,
|
||||
size_t namelen);
|
||||
|
||||
/**
|
||||
* Print the delegation point to the log. For debugging.
|
||||
* @param v: verbosity value that is needed to emit to log.
|
||||
* @param dp: delegation point.
|
||||
*/
|
||||
void delegpt_log(enum verbosity_value v, struct delegpt* dp);
|
||||
|
||||
/**
|
||||
* Add all usable targets to the result list.
|
||||
* @param dp: delegation point.
|
||||
*/
|
||||
void delegpt_add_unused_targets(struct delegpt* dp);
|
||||
|
||||
/**
|
||||
* Count number of missing targets. These are ns names with no resolved flag.
|
||||
* @param dp: delegation point.
|
||||
* @return number of missing targets (or 0).
|
||||
*/
|
||||
size_t delegpt_count_missing_targets(struct delegpt* dp);
|
||||
|
||||
/**
|
||||
* Create new delegation point from a dns message
|
||||
*
|
||||
* Note that this method does not actually test to see if the message is an
|
||||
* actual referral. It really is just checking to see if it can construct a
|
||||
* delegation point, so the message could be of some other type (some ANSWER
|
||||
* messages, some CNAME messages, generally.) Note that the resulting
|
||||
* DelegationPoint will contain targets for all "relevant" glue (i.e.,
|
||||
* address records whose ownernames match the target of one of the NS
|
||||
* records), so if policy dictates that some glue should be discarded beyond
|
||||
* that, discard it before calling this method. Note that this method will
|
||||
* find "glue" in either the ADDITIONAL section or the ANSWER section.
|
||||
*
|
||||
* @param msg: the dns message, referral.
|
||||
* @param regional: where to allocate delegation point.
|
||||
* @return new delegation point or NULL on alloc error, or if the
|
||||
* message was not appropriate.
|
||||
*/
|
||||
struct delegpt* delegpt_from_message(struct dns_msg* msg,
|
||||
struct regional* regional);
|
||||
|
||||
#endif /* ITERATOR_ITER_DELEGPT_H */
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* iterator/iter_donotq.c - iterative resolver donotqueryaddresses storage.
|
||||
*
|
||||
* 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 assist the iterator module.
|
||||
* The donotqueryaddresses are stored and looked up. These addresses
|
||||
* (like 127.0.0.1) must not be used to send queries to, and can be
|
||||
* discarded immediately from the server selection.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_donotq.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/log.h"
|
||||
#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()
|
||||
{
|
||||
struct iter_donotq* dq = (struct iter_donotq*)calloc(1,
|
||||
sizeof(struct iter_donotq));
|
||||
if(!dq)
|
||||
return NULL;
|
||||
dq->region = regional_create();
|
||||
if(!dq->region) {
|
||||
donotq_delete(dq);
|
||||
return NULL;
|
||||
}
|
||||
return dq;
|
||||
}
|
||||
|
||||
void
|
||||
donotq_delete(struct iter_donotq* dq)
|
||||
{
|
||||
if(!dq)
|
||||
return;
|
||||
regional_destroy(dq->region);
|
||||
free(dq->tree);
|
||||
free(dq);
|
||||
}
|
||||
|
||||
/** insert new address into donotq structure */
|
||||
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));
|
||||
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_QUERY, "duplicate donotquery address ignored.");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** apply donotq string */
|
||||
static int
|
||||
donotq_str_cfg(struct iter_donotq* dq, const char* str)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
int net;
|
||||
socklen_t addrlen;
|
||||
if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
|
||||
log_err("cannot parse donotquery netblock: %s", str);
|
||||
return 0;
|
||||
}
|
||||
if(!donotq_insert(dq, &addr, addrlen, net)) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** read donotq config */
|
||||
static int
|
||||
read_donotq(struct iter_donotq* dq, struct config_file* cfg)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
for(p = cfg->donotqueryaddrs; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
if(!donotq_str_cfg(dq, p->str))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
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;
|
||||
}
|
||||
donotq_init_parents(dq);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
size_t
|
||||
donotq_get_mem(struct iter_donotq* donotq)
|
||||
{
|
||||
if(!donotq) return 0;
|
||||
return sizeof(*donotq) + regional_get_mem(donotq->region);
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* iterator/iter_donotq.h - iterative resolver donotqueryaddresses storage.
|
||||
*
|
||||
* 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 assist the iterator module.
|
||||
* Keep track of the donotquery addresses and lookup fast.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_DONOTQ_H
|
||||
#define ITERATOR_ITER_DONOTQ_H
|
||||
#include "util/rbtree.h"
|
||||
struct iter_env;
|
||||
struct config_file;
|
||||
struct regional;
|
||||
|
||||
/**
|
||||
* Iterator donotqueryaddresses structure
|
||||
*/
|
||||
struct iter_donotq {
|
||||
/** regional for allocation */
|
||||
struct regional* region;
|
||||
/**
|
||||
* Tree of the address spans that are blocked.
|
||||
* contents of type iter_donotq_addr.
|
||||
*/
|
||||
rbtree_t* tree;
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterator donotquery address.
|
||||
* An address span that must not be used to send queries to.
|
||||
*/
|
||||
struct iter_donotq_addr {
|
||||
/** redblacktree node, key is this structure: addr and addrlen, net */
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create donotqueryaddresses structure
|
||||
* @return new structure or NULL on error.
|
||||
*/
|
||||
struct iter_donotq* donotq_create();
|
||||
|
||||
/**
|
||||
* Delete donotqueryaddresses structure.
|
||||
* @param donotq: to delete.
|
||||
*/
|
||||
void donotq_delete(struct iter_donotq* donotq);
|
||||
|
||||
/**
|
||||
* Process donotqueryaddresses config.
|
||||
* @param donotq: where to store.
|
||||
* @param cfg: config options.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
int donotq_apply_cfg(struct iter_donotq* donotq, struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* See if an address is blocked.
|
||||
* @param donotq: 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.
|
||||
*/
|
||||
int donotq_lookup(struct iter_donotq* donotq, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen);
|
||||
|
||||
/**
|
||||
* Get memory used by donotqueryaddresses structure.
|
||||
* @param donotq: structure for address storage.
|
||||
* @return bytes in use.
|
||||
*/
|
||||
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 */
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* iterator/iter_fwd.c - iterative resolver module forward zones.
|
||||
*
|
||||
* 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 assist the iterator module.
|
||||
* Keep track of forward zones and config settings.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_fwd.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/dname.h"
|
||||
|
||||
int
|
||||
fwd_cmp(const void* k1, const void* k2)
|
||||
{
|
||||
int m;
|
||||
struct iter_forward_zone* n1 = (struct iter_forward_zone*)k1;
|
||||
struct iter_forward_zone* n2 = (struct iter_forward_zone*)k2;
|
||||
if(n1->dclass != n2->dclass) {
|
||||
if(n1->dclass < n2->dclass)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
return dname_lab_cmp(n1->name, n1->namelabs, n2->name, n2->namelabs,
|
||||
&m);
|
||||
}
|
||||
|
||||
struct iter_forwards*
|
||||
forwards_create()
|
||||
{
|
||||
struct iter_forwards* fwd = (struct iter_forwards*)calloc(1,
|
||||
sizeof(struct iter_forwards));
|
||||
if(!fwd)
|
||||
return NULL;
|
||||
fwd->region = regional_create();
|
||||
if(!fwd->region) {
|
||||
forwards_delete(fwd);
|
||||
return NULL;
|
||||
}
|
||||
return fwd;
|
||||
}
|
||||
|
||||
void
|
||||
forwards_delete(struct iter_forwards* fwd)
|
||||
{
|
||||
if(!fwd)
|
||||
return;
|
||||
regional_destroy(fwd->region);
|
||||
free(fwd->tree);
|
||||
free(fwd);
|
||||
}
|
||||
|
||||
/** insert new info into forward structure */
|
||||
static int
|
||||
forwards_insert(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp)
|
||||
{
|
||||
struct iter_forward_zone* node = regional_alloc(fwd->region,
|
||||
sizeof(struct iter_forward_zone));
|
||||
if(!node)
|
||||
return 0;
|
||||
node->node.key = node;
|
||||
node->dclass = c;
|
||||
node->name = regional_alloc_init(fwd->region, dp->name, dp->namelen);
|
||||
if(!node->name)
|
||||
return 0;
|
||||
node->namelen = dp->namelen;
|
||||
node->namelabs = dp->namelabs;
|
||||
node->dp = dp;
|
||||
if(!rbtree_insert(fwd->tree, &node->node)) {
|
||||
log_err("duplicate forward zone ignored.");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** initialise parent pointers in the tree */
|
||||
static void
|
||||
fwd_init_parents(struct iter_forwards* fwd)
|
||||
{
|
||||
struct iter_forward_zone* node, *prev = NULL, *p;
|
||||
int m;
|
||||
RBTREE_FOR(node, struct iter_forward_zone*, fwd->tree) {
|
||||
node->parent = NULL;
|
||||
if(!prev || prev->dclass != node->dclass) {
|
||||
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 zone name */
|
||||
static int
|
||||
read_fwds_name(struct iter_forwards* fwd, struct config_stub* s,
|
||||
struct delegpt* dp)
|
||||
{
|
||||
ldns_rdf* rdf;
|
||||
if(!s->name) {
|
||||
log_err("forward zone without a name (use name \".\" to forward everything)");
|
||||
return 0;
|
||||
}
|
||||
rdf = ldns_dname_new_frm_str(s->name);
|
||||
if(!rdf) {
|
||||
log_err("cannot parse forward zone name %s", s->name);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_set_name(dp, fwd->region, ldns_rdf_data(rdf))) {
|
||||
ldns_rdf_deep_free(rdf);
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
ldns_rdf_deep_free(rdf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** set fwd host names */
|
||||
static int
|
||||
read_fwds_host(struct iter_forwards* fwd, struct config_stub* s,
|
||||
struct delegpt* dp)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
ldns_rdf* rdf;
|
||||
for(p = s->hosts; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
rdf = ldns_dname_new_frm_str(p->str);
|
||||
if(!rdf) {
|
||||
log_err("cannot parse forward %s server name: '%s'",
|
||||
s->name, p->str);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_add_ns(dp, fwd->region, ldns_rdf_data(rdf))) {
|
||||
ldns_rdf_deep_free(rdf);
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
ldns_rdf_deep_free(rdf);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** set fwd server addresses */
|
||||
static int
|
||||
read_fwds_addr(struct iter_forwards* fwd, struct config_stub* s,
|
||||
struct delegpt* dp)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
for(p = s->addrs; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
if(!extstrtoaddr(p->str, &addr, &addrlen)) {
|
||||
log_err("cannot parse forward %s ip address: '%s'",
|
||||
s->name, p->str);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_add_addr(dp, fwd->region, &addr, addrlen)) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** read forwards config */
|
||||
static int
|
||||
read_forwards(struct iter_forwards* fwd, struct config_file* cfg)
|
||||
{
|
||||
struct config_stub* s;
|
||||
for(s = cfg->forwards; s; s = s->next) {
|
||||
struct delegpt* dp = delegpt_create(fwd->region);
|
||||
if(!dp) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
if(!read_fwds_name(fwd, s, dp) ||
|
||||
!read_fwds_host(fwd, s, dp) ||
|
||||
!read_fwds_addr(fwd, s, dp))
|
||||
return 0;
|
||||
if(!forwards_insert(fwd, LDNS_RR_CLASS_IN, dp))
|
||||
return 0;
|
||||
verbose(VERB_QUERY, "Forward zone server list:");
|
||||
delegpt_log(VERB_QUERY, dp);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg)
|
||||
{
|
||||
free(fwd->tree);
|
||||
fwd->tree = rbtree_create(fwd_cmp);
|
||||
if(!fwd->tree)
|
||||
return 0;
|
||||
|
||||
/* read forward zones */
|
||||
if(!read_forwards(fwd, cfg))
|
||||
return 0;
|
||||
fwd_init_parents(fwd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct delegpt*
|
||||
forwards_lookup(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass)
|
||||
{
|
||||
/* lookup the forward zone in the tree */
|
||||
rbnode_t* res = NULL;
|
||||
struct iter_forward_zone *result;
|
||||
struct iter_forward_zone key;
|
||||
key.node.key = &key;
|
||||
key.dclass = qclass;
|
||||
key.name = qname;
|
||||
key.namelabs = dname_count_size_labels(qname, &key.namelen);
|
||||
if(rbtree_find_less_equal(fwd->tree, &key, &res)) {
|
||||
/* exact */
|
||||
result = (struct iter_forward_zone*)res;
|
||||
} else {
|
||||
/* smaller element (or no element) */
|
||||
int m;
|
||||
result = (struct iter_forward_zone*)res;
|
||||
if(!result || result->dclass != 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 result->dp;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t
|
||||
forwards_get_mem(struct iter_forwards* fwd)
|
||||
{
|
||||
if(!fwd)
|
||||
return 0;
|
||||
return sizeof(*fwd) + regional_get_mem(fwd->region);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* iterator/iter_fwd.h - iterative resolver module forward zones.
|
||||
*
|
||||
* 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 assist the iterator module.
|
||||
* Keep track of forward zones, and read those from config.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_FWD_H
|
||||
#define ITERATOR_ITER_FWD_H
|
||||
#include "util/rbtree.h"
|
||||
struct iter_env;
|
||||
struct config_file;
|
||||
struct delegpt;
|
||||
struct regional;
|
||||
|
||||
/**
|
||||
* Iterator forward zones structure
|
||||
*/
|
||||
struct iter_forwards {
|
||||
/** regional where forward zone server addresses are allocated */
|
||||
struct regional* region;
|
||||
/**
|
||||
* Zones are stored in this tree. Sort order is specially chosen.
|
||||
* first sorted on qtype. Then on dname in nsec-like order, so that
|
||||
* a lookup on class, name will return an exact match or the closest
|
||||
* match which gives the ancestor needed.
|
||||
* contents of type iter_forward_zone.
|
||||
*/
|
||||
rbtree_t* tree;
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterator forward servers for a particular zone.
|
||||
*/
|
||||
struct iter_forward_zone {
|
||||
/** 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;
|
||||
/** delegation point with forward server information for this zone. */
|
||||
struct delegpt* dp;
|
||||
/** pointer to parent in tree (or NULL if none) */
|
||||
struct iter_forward_zone* parent;
|
||||
/** class. host order. */
|
||||
uint16_t dclass;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create forwards
|
||||
* @return new forwards or NULL on error.
|
||||
*/
|
||||
struct iter_forwards* forwards_create();
|
||||
|
||||
/**
|
||||
* Delete forwards.
|
||||
* @param fwd: to delete.
|
||||
*/
|
||||
void forwards_delete(struct iter_forwards* fwd);
|
||||
|
||||
/**
|
||||
* Process forwards config.
|
||||
* @param fwd: where to store.
|
||||
* @param cfg: config options.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
int forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Find forward zone information
|
||||
* For this qname/qclass find forward zone information, returns delegation
|
||||
* point with server names and addresses, or NULL if no forwarding is needed.
|
||||
*
|
||||
* @param fwd: forward storage.
|
||||
* @param qname: The qname of the query.
|
||||
* @param qclass: The qclass of the query.
|
||||
* @return: A delegation point if the query has to be forwarded to that list,
|
||||
* otherwise null.
|
||||
*/
|
||||
struct delegpt* forwards_lookup(struct iter_forwards* fwd,
|
||||
uint8_t* qname, uint16_t qclass);
|
||||
|
||||
/**
|
||||
* Get memory in use by forward storage
|
||||
* @param fwd: forward storage.
|
||||
* @return bytes in use
|
||||
*/
|
||||
size_t forwards_get_mem(struct iter_forwards* fwd);
|
||||
|
||||
/** compare two fwd entries */
|
||||
int fwd_cmp(const void* k1, const void* k2);
|
||||
|
||||
#endif /* ITERATOR_ITER_FWD_H */
|
||||
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
* iterator/iter_hints.c - iterative resolver module stub and root hints.
|
||||
*
|
||||
* 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 assist the iterator module.
|
||||
* Keep track of stub and root hints, and read those from config.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_hints.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#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()
|
||||
{
|
||||
struct iter_hints* hints = (struct iter_hints*)calloc(1,
|
||||
sizeof(struct iter_hints));
|
||||
if(!hints)
|
||||
return NULL;
|
||||
hints->region = regional_create();
|
||||
if(!hints->region) {
|
||||
hints_delete(hints);
|
||||
return NULL;
|
||||
}
|
||||
return hints;
|
||||
}
|
||||
|
||||
void
|
||||
hints_delete(struct iter_hints* hints)
|
||||
{
|
||||
if(!hints)
|
||||
return;
|
||||
regional_destroy(hints->region);
|
||||
free(hints->tree);
|
||||
free(hints);
|
||||
}
|
||||
|
||||
/** add hint to delegation hints */
|
||||
static int
|
||||
ah(struct delegpt* dp, struct regional* r, const char* sv, const char* ip)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
ldns_rdf* rdf = ldns_dname_new_frm_str(sv);
|
||||
if(!rdf) {
|
||||
log_err("could not parse %s", sv);
|
||||
return 0;
|
||||
}
|
||||
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)) {
|
||||
ldns_rdf_deep_free(rdf);
|
||||
return 0;
|
||||
}
|
||||
ldns_rdf_deep_free(rdf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** obtain compiletime provided root hints */
|
||||
static struct delegpt*
|
||||
compile_time_root_prime(struct regional* r)
|
||||
{
|
||||
/* from:
|
||||
; This file is made available by InterNIC
|
||||
; under anonymous FTP as
|
||||
; file /domain/named.cache
|
||||
; on server FTP.INTERNIC.NET
|
||||
; -OR- RS.INTERNIC.NET
|
||||
;
|
||||
; last update: Jan 29, 2004
|
||||
; related version of root zone: 2004012900
|
||||
*/
|
||||
struct delegpt* dp = delegpt_create(r);
|
||||
if(!dp)
|
||||
return NULL;
|
||||
if(!delegpt_set_name(dp, r, (uint8_t*)"\000"))
|
||||
return NULL;
|
||||
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;
|
||||
if(!ah(dp, r, "D.ROOT-SERVERS.NET.", "128.8.10.90")) return 0;
|
||||
if(!ah(dp, r, "E.ROOT-SERVERS.NET.", "192.203.230.10")) return 0;
|
||||
if(!ah(dp, r, "F.ROOT-SERVERS.NET.", "192.5.5.241")) return 0;
|
||||
if(!ah(dp, r, "G.ROOT-SERVERS.NET.", "192.112.36.4")) return 0;
|
||||
if(!ah(dp, r, "H.ROOT-SERVERS.NET.", "128.63.2.53")) return 0;
|
||||
if(!ah(dp, r, "I.ROOT-SERVERS.NET.", "192.36.148.17")) return 0;
|
||||
if(!ah(dp, r, "J.ROOT-SERVERS.NET.", "192.58.128.30")) return 0;
|
||||
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;
|
||||
return dp;
|
||||
}
|
||||
|
||||
/** insert new hint info into hint structure */
|
||||
static int
|
||||
hints_insert(struct iter_hints* hints, uint16_t c, struct delegpt* dp)
|
||||
{
|
||||
struct iter_hints_stub* node = regional_alloc(hints->region,
|
||||
sizeof(struct iter_hints_stub));
|
||||
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)
|
||||
return 0;
|
||||
node->namelen = dp->namelen;
|
||||
node->namelabs = dp->namelabs;
|
||||
node->dp = dp;
|
||||
if(!rbtree_insert(hints->tree, &node->node)) {
|
||||
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,
|
||||
struct delegpt* dp)
|
||||
{
|
||||
ldns_rdf* rdf;
|
||||
if(!s->name) {
|
||||
log_err("stub zone without a name");
|
||||
return 0;
|
||||
}
|
||||
rdf = ldns_dname_new_frm_str(s->name);
|
||||
if(!rdf) {
|
||||
log_err("cannot parse stub zone name %s", s->name);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_set_name(dp, hints->region, ldns_rdf_data(rdf))) {
|
||||
ldns_rdf_deep_free(rdf);
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
ldns_rdf_deep_free(rdf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** set stub host names */
|
||||
static int
|
||||
read_stubs_host(struct iter_hints* hints, struct config_stub* s,
|
||||
struct delegpt* dp)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
ldns_rdf* rdf;
|
||||
for(p = s->hosts; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
rdf = ldns_dname_new_frm_str(p->str);
|
||||
if(!rdf) {
|
||||
log_err("cannot parse stub %s nameserver name: '%s'",
|
||||
s->name, p->str);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_add_ns(dp, hints->region, ldns_rdf_data(rdf))) {
|
||||
ldns_rdf_deep_free(rdf);
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
ldns_rdf_deep_free(rdf);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** set stub server addresses */
|
||||
static int
|
||||
read_stubs_addr(struct iter_hints* hints, struct config_stub* s,
|
||||
struct delegpt* dp)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
for(p = s->addrs; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
if(!extstrtoaddr(p->str, &addr, &addrlen)) {
|
||||
log_err("cannot parse stub %s ip address: '%s'",
|
||||
s->name, p->str);
|
||||
return 0;
|
||||
}
|
||||
if(!delegpt_add_addr(dp, hints->region, &addr, addrlen)) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** read stubs config */
|
||||
static int
|
||||
read_stubs(struct iter_hints* hints, struct config_file* cfg)
|
||||
{
|
||||
struct config_stub* s;
|
||||
for(s = cfg->stubs; s; s = s->next) {
|
||||
struct delegpt* dp = delegpt_create(hints->region);
|
||||
if(!dp) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
if(!read_stubs_name(hints, s, dp) ||
|
||||
!read_stubs_host(hints, s, dp) ||
|
||||
!read_stubs_addr(hints, s, dp))
|
||||
return 0;
|
||||
if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp))
|
||||
return 0;
|
||||
delegpt_log(VERB_QUERY, dp);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** read root hints from file */
|
||||
static int
|
||||
read_root_hints(struct iter_hints* hints, char* fname)
|
||||
{
|
||||
int lineno = 0;
|
||||
uint32_t default_ttl = 0;
|
||||
ldns_rdf* origin = NULL;
|
||||
ldns_rdf* prev_rr = NULL;
|
||||
struct delegpt* dp;
|
||||
ldns_rr* rr = NULL;
|
||||
ldns_status status;
|
||||
uint16_t c = LDNS_RR_CLASS_IN;
|
||||
FILE* f = fopen(fname, "r");
|
||||
if(!f) {
|
||||
log_err("could not read root hints %s: %s",
|
||||
fname, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
dp = delegpt_create(hints->region);
|
||||
if(!dp) {
|
||||
log_err("out of memory reading root hints");
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
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);
|
||||
if(status == LDNS_STATUS_SYNTAX_EMPTY ||
|
||||
status == LDNS_STATUS_SYNTAX_TTL ||
|
||||
status == LDNS_STATUS_SYNTAX_ORIGIN)
|
||||
continue;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
} else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_A) {
|
||||
struct sockaddr_in sa;
|
||||
socklen_t len = (socklen_t)sizeof(sa);
|
||||
memset(&sa, 0, len);
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
|
||||
memmove(&sa.sin_addr,
|
||||
ldns_rdf_data(ldns_rr_rdf(rr, 0)), INET_SIZE);
|
||||
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)) {
|
||||
log_err("out of memory reading root hints");
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
} else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA) {
|
||||
struct sockaddr_in6 sa;
|
||||
socklen_t len = (socklen_t)sizeof(sa);
|
||||
memset(&sa, 0, len);
|
||||
sa.sin6_family = AF_INET6;
|
||||
sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
|
||||
memmove(&sa.sin6_addr,
|
||||
ldns_rdf_data(ldns_rr_rdf(rr, 0)), INET6_SIZE);
|
||||
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)) {
|
||||
log_err("out of memory reading root hints");
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
log_warn("root hints %s:%d skipping type %d",
|
||||
fname, lineno, ldns_rr_get_type(rr));
|
||||
}
|
||||
|
||||
ldns_rr_free(rr);
|
||||
}
|
||||
fclose(f);
|
||||
if(!dp->name) {
|
||||
log_warn("root hints %s: no NS content", fname);
|
||||
return 1;
|
||||
}
|
||||
if(!hints_insert(hints, c, dp)) {
|
||||
return 0;
|
||||
}
|
||||
delegpt_log(VERB_QUERY, dp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** read root hints list */
|
||||
static int
|
||||
read_root_hints_list(struct iter_hints* hints, struct config_file* cfg)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
for(p = cfg->root_hints; p; p = p->next) {
|
||||
log_assert(p->str);
|
||||
if(p->str && p->str[0]) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* read root hints */
|
||||
if(!read_root_hints_list(hints, cfg))
|
||||
return 0;
|
||||
|
||||
/* read stub hints */
|
||||
if(!read_stubs(hints, cfg))
|
||||
return 0;
|
||||
|
||||
/* use fallback compiletime root hints */
|
||||
if(!hints_lookup_root(hints, LDNS_RR_CLASS_IN)) {
|
||||
struct delegpt* dp = compile_time_root_prime(hints->region);
|
||||
verbose(VERB_ALGO, "no config, using builtin root hints.");
|
||||
if(!dp)
|
||||
return 0;
|
||||
if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp))
|
||||
return 0;
|
||||
}
|
||||
|
||||
init_parents(hints);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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);
|
||||
if(!stub)
|
||||
return NULL;
|
||||
return stub->dp;
|
||||
}
|
||||
|
||||
struct delegpt*
|
||||
hints_lookup_stub(struct iter_hints* hints, uint8_t* qname,
|
||||
uint16_t qclass, struct delegpt* cache_dp)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
/*
|
||||
* If our cached delegation point is above the hint, we need to prime.
|
||||
*/
|
||||
if(dname_strict_subdomain(result->dp->name, result->dp->namelabs,
|
||||
cache_dp->name, cache_dp->namelabs))
|
||||
return result->dp; /* need to prime this stub */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t
|
||||
hints_get_mem(struct iter_hints* hints)
|
||||
{
|
||||
if(!hints) return 0;
|
||||
return sizeof(*hints) + regional_get_mem(hints->region);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* iterator/iter_hints.h - iterative resolver module stub and root hints.
|
||||
*
|
||||
* 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 assist the iterator module.
|
||||
* Keep track of stub and root hints, and read those from config.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_HINTS_H
|
||||
#define ITERATOR_ITER_HINTS_H
|
||||
#include "util/rbtree.h"
|
||||
struct iter_env;
|
||||
struct config_file;
|
||||
struct delegpt;
|
||||
struct regional;
|
||||
|
||||
/**
|
||||
* Iterator hints structure
|
||||
*/
|
||||
struct iter_hints {
|
||||
/** regional where hints are allocated */
|
||||
struct regional* region;
|
||||
/**
|
||||
* Hints are stored in this tree. Sort order is specially chosen.
|
||||
* first sorted on qtype. Then on dname in nsec-like order, so that
|
||||
* 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.
|
||||
*/
|
||||
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;
|
||||
/** 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create hints
|
||||
* @return new hints or NULL on error.
|
||||
*/
|
||||
struct iter_hints* hints_create();
|
||||
|
||||
/**
|
||||
* Delete hints.
|
||||
* @param hints: to delete.
|
||||
*/
|
||||
void hints_delete(struct iter_hints* hints);
|
||||
|
||||
/**
|
||||
* Process hints config. Sets default values for root hints if no config.
|
||||
* @param hints: where to store.
|
||||
* @param cfg: config options.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
int hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Find root hints for the given class.
|
||||
* @param hints: hint storage.
|
||||
* @param qclass: class for which root hints are requested. host order.
|
||||
* @return: NULL if no hints, or a ptr to stored hints.
|
||||
*/
|
||||
struct delegpt* hints_lookup_root(struct iter_hints* hints, uint16_t qclass);
|
||||
|
||||
/**
|
||||
* Given a qname/qclass combination, and the delegation point from the cache
|
||||
* for this qname/qclass, determine if this combination indicates that a
|
||||
* stub hint exists and must be primed.
|
||||
*
|
||||
* @param hints: hint storage.
|
||||
* @param qname: The qname that generated the delegation point.
|
||||
* @param qclass: The qclass that generated the delegation point.
|
||||
* @param dp: The cache generated delegation point.
|
||||
* @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,
|
||||
uint8_t* qname, uint16_t qclass, struct delegpt* dp);
|
||||
|
||||
/**
|
||||
* Get memory in use by hints
|
||||
* @param hints: hint storage.
|
||||
* @return bytes in use
|
||||
*/
|
||||
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,252 @@
|
||||
/*
|
||||
* iterator/iter_resptype.c - response type information and classification.
|
||||
*
|
||||
* 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 defines the response type. DNS Responses can be classified as
|
||||
* one of the response types.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_resptype.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/dname.h"
|
||||
|
||||
enum response_type
|
||||
response_type_from_cache(struct dns_msg* msg,
|
||||
struct query_info* request)
|
||||
{
|
||||
/* If the message is NXDOMAIN, then it is an ANSWER. */
|
||||
if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN)
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
|
||||
/* First we look at the answer section. This can tell us if this is
|
||||
* CNAME or positive ANSWER. */
|
||||
if(msg->rep->an_numrrsets > 0) {
|
||||
/* Now look at the answer section first. 3 states:
|
||||
* o our answer is there directly,
|
||||
* o our answer is there after a cname,
|
||||
* o or there is just a cname. */
|
||||
uint8_t* mname = request->qname;
|
||||
size_t mname_len = request->qname_len;
|
||||
size_t i;
|
||||
for(i=0; i<msg->rep->an_numrrsets; i++) {
|
||||
struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
|
||||
|
||||
/* If we have encountered an answer (before or
|
||||
* after a CNAME), then we are done! Note that
|
||||
* if qtype == CNAME then this will be noted as
|
||||
* an ANSWER before it gets treated as a CNAME,
|
||||
* as it should */
|
||||
if(ntohs(s->rk.type) == request->qtype &&
|
||||
ntohs(s->rk.rrset_class) == request->qclass &&
|
||||
query_dname_compare(mname, s->rk.dname) == 0) {
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
}
|
||||
|
||||
/* If we have encountered a CNAME, make sure that
|
||||
* it is relevant. */
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
|
||||
query_dname_compare(mname, s->rk.dname) == 0) {
|
||||
get_cname_target(s, &mname, &mname_len);
|
||||
}
|
||||
}
|
||||
|
||||
/* if we encountered a CNAME (or a bunch of CNAMEs), and
|
||||
* still got to here, then it is a CNAME response. (i.e.,
|
||||
* the CNAME chain didn't terminate in an answer rrset.) */
|
||||
if(mname != request->qname) {
|
||||
return RESPONSE_TYPE_CNAME;
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point, since we don't need to detect REFERRAL or LAME
|
||||
* messages, it can only be an ANSWER. */
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
}
|
||||
|
||||
enum response_type
|
||||
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 */
|
||||
size_t origzonelen = 1;
|
||||
size_t i;
|
||||
|
||||
if(!msg || !request)
|
||||
return RESPONSE_TYPE_THROWAWAY;
|
||||
|
||||
/* If the message is NXDOMAIN, then it answers the question. */
|
||||
if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN) {
|
||||
/* 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_ANSWER;
|
||||
}
|
||||
|
||||
/* Other response codes mean (so far) to throw the response away as
|
||||
* meaningless and move on to the next nameserver. */
|
||||
if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR)
|
||||
return RESPONSE_TYPE_THROWAWAY;
|
||||
|
||||
/* Note: TC bit has already been handled */
|
||||
|
||||
if(dp) {
|
||||
origzone = dp->name;
|
||||
origzonelen = dp->namelen;
|
||||
}
|
||||
|
||||
/* First we look at the answer section. This can tell us if this is a
|
||||
* CNAME or ANSWER or (provisional) ANSWER. */
|
||||
if(msg->rep->an_numrrsets > 0) {
|
||||
uint8_t* mname = request->qname;
|
||||
size_t mname_len = request->qname_len;
|
||||
|
||||
/* Now look at the answer section first. 3 states: our
|
||||
* answer is there directly, our answer is there after
|
||||
* a cname, or there is just a cname. */
|
||||
for(i=0; i<msg->rep->an_numrrsets; i++) {
|
||||
struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
|
||||
|
||||
/* if the answer section has NS rrset, and qtype ANY
|
||||
* and the delegation is lower, and no CNAMEs followed,
|
||||
* this is a referral where the NS went to AN section */
|
||||
if((request->qtype == LDNS_RR_TYPE_ANY ||
|
||||
request->qtype == LDNS_RR_TYPE_NS) &&
|
||||
ntohs(s->rk.type) == LDNS_RR_TYPE_NS &&
|
||||
ntohs(s->rk.rrset_class) == request->qclass &&
|
||||
dname_strict_subdomain_c(s->rk.dname,
|
||||
origzone)) {
|
||||
return RESPONSE_TYPE_REFERRAL;
|
||||
}
|
||||
|
||||
/* If we have encountered an answer (before or
|
||||
* after a CNAME), then we are done! Note that
|
||||
* if qtype == CNAME then this will be noted as an
|
||||
* ANSWER before it gets treated as a CNAME, as
|
||||
* it should. */
|
||||
if(ntohs(s->rk.type) == request->qtype &&
|
||||
ntohs(s->rk.rrset_class) == request->qclass &&
|
||||
query_dname_compare(mname, s->rk.dname) == 0) {
|
||||
if((msg->rep->flags&BIT_AA))
|
||||
return RESPONSE_TYPE_ANSWER;
|
||||
/* If the AA bit isn't on, and we've seen
|
||||
* the answer, we only provisionally say
|
||||
* 'ANSWER' -- it very well could be a
|
||||
* REFERRAL. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we have encountered a CNAME, make sure that
|
||||
* it is relevant. */
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
|
||||
query_dname_compare(mname, s->rk.dname) == 0) {
|
||||
get_cname_target(s, &mname, &mname_len);
|
||||
}
|
||||
}
|
||||
/* if we encountered a CNAME (or a bunch of CNAMEs), and
|
||||
* still got to here, then it is a CNAME response.
|
||||
* (This is regardless of the AA bit at this point) */
|
||||
if(mname != request->qname) {
|
||||
return RESPONSE_TYPE_CNAME;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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++) {
|
||||
struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
|
||||
|
||||
/* The normal way of detecting NOERROR/NODATA. */
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA &&
|
||||
dname_subdomain_c(request->qname, s->rk.dname)) {
|
||||
/* 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_ANSWER;
|
||||
}
|
||||
|
||||
/* 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_ANSWER;
|
||||
}
|
||||
/* If we are getting a referral upwards (or to
|
||||
* the same zone), then the server is 'lame'. */
|
||||
if(dname_subdomain_c(origzone, s->rk.dname)) {
|
||||
return RESPONSE_TYPE_LAME;
|
||||
}
|
||||
/* If the NS set is below the delegation point we
|
||||
* are on, and it is non-authoritative, then it is
|
||||
* a referral, otherwise it is an answer. */
|
||||
if(dname_subdomain_c(s->rk.dname, origzone)) {
|
||||
/* NOTE: I no longer remember in what case
|
||||
* we would like this to be an answer.
|
||||
* NODATA should have a SOA or nothing,
|
||||
* not an NS rrset.
|
||||
* True, referrals should not have the AA
|
||||
* bit set, but... */
|
||||
|
||||
/* if((msg->rep->flags&BIT_AA))
|
||||
return RESPONSE_TYPE_ANSWER; */
|
||||
return RESPONSE_TYPE_REFERRAL;
|
||||
}
|
||||
/* Otherwise, the NS set is irrelevant. */
|
||||
}
|
||||
}
|
||||
|
||||
/* If we've gotten this far, this is NOERROR/NODATA (which could
|
||||
* 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_ANSWER;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* iterator/iter_resptype.h - response type information and classification.
|
||||
*
|
||||
* 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 defines the response type. DNS Responses can be classified as
|
||||
* one of the response types.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_RESPTYPE_H
|
||||
#define ITERATOR_ITER_RESPTYPE_H
|
||||
struct dns_msg;
|
||||
struct query_info;
|
||||
struct delegpt;
|
||||
|
||||
/**
|
||||
* The response type is used to interpret the response.
|
||||
*/
|
||||
enum response_type {
|
||||
/**
|
||||
* 'untyped' means that the type of this response hasn't been
|
||||
* assigned.
|
||||
*/
|
||||
RESPONSE_TYPE_UNTYPED = 0,
|
||||
|
||||
/**
|
||||
* 'answer' means that the response terminates the resolution
|
||||
* process.
|
||||
*/
|
||||
RESPONSE_TYPE_ANSWER,
|
||||
|
||||
/** 'delegation' means that the response is a delegation. */
|
||||
RESPONSE_TYPE_REFERRAL,
|
||||
|
||||
/**
|
||||
* 'cname' means that the response is a cname without the final
|
||||
* answer, and thus must be restarted.
|
||||
*/
|
||||
RESPONSE_TYPE_CNAME,
|
||||
|
||||
/**
|
||||
* 'throwaway' means that this particular response should be
|
||||
* discarded and the next nameserver should be contacted
|
||||
*/
|
||||
RESPONSE_TYPE_THROWAWAY,
|
||||
|
||||
/**
|
||||
* 'lame' means that this particular response indicates that
|
||||
* the nameserver knew nothing about the question.
|
||||
*/
|
||||
RESPONSE_TYPE_LAME
|
||||
};
|
||||
|
||||
/**
|
||||
* Classifies a response message from cache based on the current request.
|
||||
* Note that this routine assumes that THROWAWAY or LAME responses will not
|
||||
* occur. Also, it will not detect REFERRAL type messages, since those are
|
||||
* (currently) automatically classified based on how they came from the
|
||||
* cache (findDelegation() instead of lookup()).
|
||||
*
|
||||
* @param msg: the message from the cache.
|
||||
* @param request: the request that generated the response.
|
||||
* @return the response type (CNAME or ANSWER).
|
||||
*/
|
||||
enum response_type response_type_from_cache(struct dns_msg* msg,
|
||||
struct query_info* request);
|
||||
|
||||
/**
|
||||
* Classifies a response message (from the wire) based on the current
|
||||
* request.
|
||||
*
|
||||
* NOTE: currently this routine uses the AA bit in the response to help
|
||||
* distinguish between some non-standard referrals and answers. It also
|
||||
* relies somewhat on the originating zone to be accurate (for lameness
|
||||
* detection, mostly).
|
||||
*
|
||||
* @param rdset: if RD bit was sent in query sent by unbound.
|
||||
* @param msg: the message from the cache.
|
||||
* @param request: the request that generated the response.
|
||||
* @param dp: The delegation point that was being queried
|
||||
* when the response was returned.
|
||||
* @return the response type (CNAME or ANSWER).
|
||||
*/
|
||||
enum response_type response_type_from_server(int rdset,
|
||||
struct dns_msg* msg, struct query_info* request, struct delegpt* dp);
|
||||
|
||||
#endif /* ITERATOR_ITER_RESPTYPE_H */
|
||||
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
* iterator/iter_scrub.c - scrubbing, normalization, sanitization of DNS msgs.
|
||||
*
|
||||
* 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 has routine(s) for cleaning up incoming DNS messages from
|
||||
* possible useless or malicious junk in it.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_scrub.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/module.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/alloc.h"
|
||||
|
||||
/** RRset flag used during scrubbing. The RRset is OK. */
|
||||
#define RRSET_SCRUB_OK 0x80
|
||||
|
||||
/** remove rrset, update loop variables */
|
||||
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_QUERY
|
||||
&& (*rrset)->dname_len <= LDNS_MAX_DOMAINLEN) {
|
||||
uint8_t buf[LDNS_MAX_DOMAINLEN+1];
|
||||
dname_pkt_copy(pkt, buf, (*rrset)->dname);
|
||||
log_nametypeclass(VERB_QUERY, str, buf,
|
||||
(*rrset)->type, ntohs((*rrset)->rrset_class));
|
||||
}
|
||||
if(prev)
|
||||
prev->rrset_all_next = (*rrset)->rrset_all_next;
|
||||
else msg->rrset_first = (*rrset)->rrset_all_next;
|
||||
if(msg->rrset_last == *rrset)
|
||||
msg->rrset_last = prev;
|
||||
msg->rrset_count --;
|
||||
switch((*rrset)->section) {
|
||||
case LDNS_SECTION_ANSWER: msg->an_rrsets--; break;
|
||||
case LDNS_SECTION_AUTHORITY: msg->ns_rrsets--; break;
|
||||
case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets--; break;
|
||||
default: log_assert(0);
|
||||
}
|
||||
msgparse_bucket_remove(msg, *rrset);
|
||||
*rrset = (*rrset)->rrset_all_next;
|
||||
}
|
||||
|
||||
/** return true if rr type has additional names in it */
|
||||
static int
|
||||
has_additional(uint16_t t)
|
||||
{
|
||||
switch(t) {
|
||||
case LDNS_RR_TYPE_MB:
|
||||
case LDNS_RR_TYPE_MD:
|
||||
case LDNS_RR_TYPE_MF:
|
||||
case LDNS_RR_TYPE_NS:
|
||||
case LDNS_RR_TYPE_MX:
|
||||
case LDNS_RR_TYPE_KX:
|
||||
case LDNS_RR_TYPE_SRV:
|
||||
return 1;
|
||||
case LDNS_RR_TYPE_NAPTR:
|
||||
/* TODO: NAPTR not supported, glue stripped off */
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** get additional name from rrset RR, return false if no name present */
|
||||
static int
|
||||
get_additional_name(struct rrset_parse* rrset, struct rr_parse* rr,
|
||||
uint8_t** nm, size_t* nmlen, ldns_buffer* pkt)
|
||||
{
|
||||
size_t offset = 0;
|
||||
size_t len, oldpos;
|
||||
switch(rrset->type) {
|
||||
case LDNS_RR_TYPE_MB:
|
||||
case LDNS_RR_TYPE_MD:
|
||||
case LDNS_RR_TYPE_MF:
|
||||
case LDNS_RR_TYPE_NS:
|
||||
offset = 0;
|
||||
break;
|
||||
case LDNS_RR_TYPE_MX:
|
||||
case LDNS_RR_TYPE_KX:
|
||||
offset = 2;
|
||||
break;
|
||||
case LDNS_RR_TYPE_SRV:
|
||||
offset = 6;
|
||||
break;
|
||||
case LDNS_RR_TYPE_NAPTR:
|
||||
/* TODO: NAPTR not supported, glue stripped off */
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
len = ldns_read_uint16(rr->ttl_data+sizeof(uint32_t));
|
||||
if(len < offset+1)
|
||||
return 0; /* rdata field too small */
|
||||
*nm = rr->ttl_data+sizeof(uint32_t)+sizeof(uint16_t)+offset;
|
||||
oldpos = ldns_buffer_position(pkt);
|
||||
ldns_buffer_set_position(pkt, (size_t)(*nm - ldns_buffer_begin(pkt)));
|
||||
*nmlen = pkt_dname_len(pkt);
|
||||
ldns_buffer_set_position(pkt, oldpos);
|
||||
if(*nmlen == 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Place mark on rrsets in additional section they are OK */
|
||||
static void
|
||||
mark_additional_rrset(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct rrset_parse* rrset)
|
||||
{
|
||||
/* Mark A and AAAA for NS as appropriate additional section info. */
|
||||
uint8_t* nm = NULL;
|
||||
size_t nmlen = 0;
|
||||
struct rr_parse* rr;
|
||||
|
||||
if(!has_additional(rrset->type))
|
||||
return;
|
||||
for(rr = rrset->rr_first; rr; rr = rr->next) {
|
||||
if(get_additional_name(rrset, rr, &nm, &nmlen, pkt)) {
|
||||
/* mark A */
|
||||
hashvalue_t h = pkt_hash_rrset(pkt, nm, LDNS_RR_TYPE_A,
|
||||
rrset->rrset_class, 0);
|
||||
struct rrset_parse* r = msgparse_hashtable_lookup(
|
||||
msg, pkt, h, 0, nm, nmlen,
|
||||
LDNS_RR_TYPE_A, rrset->rrset_class);
|
||||
if(r && r->section == LDNS_SECTION_ADDITIONAL) {
|
||||
r->flags |= RRSET_SCRUB_OK;
|
||||
}
|
||||
|
||||
/* mark AAAA */
|
||||
h = pkt_hash_rrset(pkt, nm, LDNS_RR_TYPE_AAAA,
|
||||
rrset->rrset_class, 0);
|
||||
r = msgparse_hashtable_lookup(msg, pkt, h, 0, nm,
|
||||
nmlen, LDNS_RR_TYPE_AAAA, rrset->rrset_class);
|
||||
if(r && r->section == LDNS_SECTION_ADDITIONAL) {
|
||||
r->flags |= RRSET_SCRUB_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Get target name of a CNAME */
|
||||
static int
|
||||
parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname,
|
||||
size_t* snamelen)
|
||||
{
|
||||
if(rrset->rr_count != 1) {
|
||||
verbose(VERB_ALGO, "Found CNAME rrset with "
|
||||
"size > 1: %u", (unsigned)rrset->rr_count);
|
||||
return 0;
|
||||
}
|
||||
if(rrset->rr_first->size < sizeof(uint16_t)+1)
|
||||
return 0; /* CNAME rdata too small */
|
||||
*sname = rrset->rr_first->ttl_data + sizeof(uint32_t)
|
||||
+ sizeof(uint16_t); /* skip ttl, rdatalen */
|
||||
*snamelen = rrset->rr_first->size - sizeof(uint16_t);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Synthesize CNAME from DNAME, false if too long */
|
||||
static int
|
||||
synth_cname(uint8_t* qname, size_t qnamelen, struct rrset_parse* dname_rrset,
|
||||
uint8_t* alias, size_t* aliaslen, ldns_buffer* pkt)
|
||||
{
|
||||
/* we already know that sname is a strict subdomain of DNAME owner */
|
||||
uint8_t* dtarg = NULL;
|
||||
size_t dtarglen;
|
||||
if(!parse_get_cname_target(dname_rrset, &dtarg, &dtarglen))
|
||||
return 0;
|
||||
log_assert(qnamelen > dname_rrset->dname_len);
|
||||
/* DNAME from com. to net. with qname example.com. -> example.net. */
|
||||
/* so: \3com\0 to \3net\0 and qname \7example\3com\0 */
|
||||
*aliaslen = qnamelen + dtarglen - dname_rrset->dname_len;
|
||||
if(*aliaslen > LDNS_MAX_DOMAINLEN)
|
||||
return 0; /* should have been RCODE YXDOMAIN */
|
||||
/* decompress dnames into buffer, we know it fits */
|
||||
dname_pkt_copy(pkt, alias, qname);
|
||||
dname_pkt_copy(pkt, alias+(qnamelen-dname_rrset->dname_len), dtarg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** synthesize a CNAME rrset */
|
||||
static struct rrset_parse*
|
||||
synth_cname_rrset(uint8_t** sname, size_t* snamelen, uint8_t* alias,
|
||||
size_t aliaslen, struct regional* region, struct msg_parse* msg,
|
||||
struct rrset_parse* rrset, struct rrset_parse* prev,
|
||||
struct rrset_parse* nx, ldns_buffer* pkt)
|
||||
{
|
||||
struct rrset_parse* cn = (struct rrset_parse*)regional_alloc(region,
|
||||
sizeof(struct rrset_parse));
|
||||
if(!cn)
|
||||
return NULL;
|
||||
memset(cn, 0, sizeof(*cn));
|
||||
cn->rr_first = (struct rr_parse*)regional_alloc(region,
|
||||
sizeof(struct rr_parse));
|
||||
if(!cn->rr_first)
|
||||
return NULL;
|
||||
cn->rr_last = cn->rr_first;
|
||||
/* CNAME from sname to alias */
|
||||
cn->dname = (uint8_t*)regional_alloc(region, *snamelen);
|
||||
if(!cn->dname)
|
||||
return NULL;
|
||||
dname_pkt_copy(pkt, cn->dname, *sname);
|
||||
cn->dname_len = *snamelen;
|
||||
cn->type = LDNS_RR_TYPE_CNAME;
|
||||
cn->section = rrset->section;
|
||||
cn->rrset_class = rrset->rrset_class;
|
||||
cn->rr_count = 1;
|
||||
cn->size = sizeof(uint16_t) + aliaslen;
|
||||
cn->hash=pkt_hash_rrset(pkt, cn->dname, cn->type, cn->rrset_class, 0);
|
||||
/* allocate TTL + rdatalen + uncompressed dname */
|
||||
memset(cn->rr_first, 0, sizeof(struct rr_parse));
|
||||
cn->rr_first->outside_packet = 1;
|
||||
cn->rr_first->ttl_data = (uint8_t*)regional_alloc(region,
|
||||
sizeof(uint32_t)+sizeof(uint16_t)+aliaslen);
|
||||
if(!cn->rr_first->ttl_data)
|
||||
return NULL;
|
||||
ldns_write_uint32(cn->rr_first->ttl_data, 0); /* TTL = 0 */
|
||||
ldns_write_uint16(cn->rr_first->ttl_data+4, aliaslen);
|
||||
memmove(cn->rr_first->ttl_data+6, alias, aliaslen);
|
||||
cn->rr_first->size = sizeof(uint16_t)+aliaslen;
|
||||
|
||||
/* link it in */
|
||||
cn->rrset_all_next = nx;
|
||||
if(prev)
|
||||
prev->rrset_all_next = cn;
|
||||
else msg->rrset_first = cn;
|
||||
if(nx == NULL)
|
||||
msg->rrset_last = cn;
|
||||
msg->rrset_count ++;
|
||||
msg->an_rrsets++;
|
||||
/* it is not inserted in the msg hashtable. */
|
||||
|
||||
*sname = cn->rr_first->ttl_data + sizeof(uint32_t)+sizeof(uint16_t);
|
||||
*snamelen = aliaslen;
|
||||
return cn;
|
||||
}
|
||||
|
||||
/** check if DNAME applies to a name */
|
||||
static int
|
||||
pkt_strict_sub(ldns_buffer* pkt, uint8_t* sname, uint8_t* dr)
|
||||
{
|
||||
uint8_t buf1[LDNS_MAX_DOMAINLEN+1];
|
||||
uint8_t buf2[LDNS_MAX_DOMAINLEN+1];
|
||||
/* decompress names */
|
||||
dname_pkt_copy(pkt, buf1, sname);
|
||||
dname_pkt_copy(pkt, buf2, dr);
|
||||
return dname_strict_subdomain_c(buf1, buf2);
|
||||
}
|
||||
|
||||
/** check subdomain with decompression */
|
||||
static int
|
||||
pkt_sub(ldns_buffer* pkt, uint8_t* comprname, uint8_t* zone)
|
||||
{
|
||||
uint8_t buf[LDNS_MAX_DOMAINLEN+1];
|
||||
dname_pkt_copy(pkt, buf, comprname);
|
||||
return dname_subdomain_c(buf, zone);
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine normalizes a response. This includes removing "irrelevant"
|
||||
* records from the answer and additional sections and (re)synthesizing
|
||||
* CNAMEs from DNAMEs, if present.
|
||||
*
|
||||
* @param pkt: packet.
|
||||
* @param msg: msg to normalize.
|
||||
* @param qinfo: original query.
|
||||
* @param region: where to allocate synthesized CNAMEs.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
static int
|
||||
scrub_normalize(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct query_info* qinfo, struct regional* region)
|
||||
{
|
||||
uint8_t* sname = qinfo->qname;
|
||||
size_t snamelen = qinfo->qname_len;
|
||||
struct rrset_parse* rrset, *prev;
|
||||
|
||||
if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR &&
|
||||
FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN)
|
||||
return 1;
|
||||
|
||||
/* For the ANSWER section, remove all "irrelevant" records and add
|
||||
* synthesized CNAMEs from DNAMEs
|
||||
* This will strip out-of-order CNAMEs as well. */
|
||||
|
||||
/* walk through the parse packet rrset list, keep track of previous
|
||||
* for insert and delete ease, and examine every RRset */
|
||||
prev = NULL;
|
||||
rrset = msg->rrset_first;
|
||||
while(rrset && rrset->section == LDNS_SECTION_ANSWER) {
|
||||
if(rrset->type == LDNS_RR_TYPE_DNAME &&
|
||||
pkt_strict_sub(pkt, sname, rrset->dname)) {
|
||||
/* check if next rrset is correct CNAME. else,
|
||||
* synthesize a CNAME */
|
||||
struct rrset_parse* nx = rrset->rrset_all_next;
|
||||
uint8_t alias[LDNS_MAX_DOMAINLEN+1];
|
||||
size_t aliaslen = 0;
|
||||
if(rrset->rr_count != 1) {
|
||||
verbose(VERB_ALGO, "Found DNAME rrset with "
|
||||
"size > 1: %u",
|
||||
(unsigned)rrset->rr_count);
|
||||
return 0;
|
||||
}
|
||||
if(!synth_cname(sname, snamelen, rrset, alias,
|
||||
&aliaslen, pkt)) {
|
||||
verbose(VERB_ALGO, "synthesized CNAME "
|
||||
"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 */
|
||||
uint8_t* t = NULL;
|
||||
size_t tlen = 0;
|
||||
if(!parse_get_cname_target(rrset, &t, &tlen))
|
||||
return 0;
|
||||
if(dname_pkt_compare(pkt, alias, t) == 0) {
|
||||
/* it's OK and better capitalized */
|
||||
prev = rrset;
|
||||
rrset = nx;
|
||||
continue;
|
||||
}
|
||||
/* synth ourselves */
|
||||
}
|
||||
/* synth a CNAME rrset */
|
||||
prev = synth_cname_rrset(&sname, &snamelen, alias,
|
||||
aliaslen, region, msg, rrset, rrset, nx, pkt);
|
||||
if(!prev) {
|
||||
log_err("out of memory synthesizing CNAME");
|
||||
return 0;
|
||||
}
|
||||
/* FIXME: resolve the conflict between synthesized
|
||||
* CNAME ttls and the cache. */
|
||||
rrset = nx;
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
/* The only records in the ANSWER section not allowed to */
|
||||
if(dname_pkt_compare(pkt, sname, rrset->dname) != 0) {
|
||||
remove_rrset("normalize: removing irrelevant RRset:",
|
||||
pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Follow the CNAME chain. */
|
||||
if(rrset->type == LDNS_RR_TYPE_CNAME) {
|
||||
if(!parse_get_cname_target(rrset, &sname, &snamelen))
|
||||
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;
|
||||
}
|
||||
|
||||
/* Otherwise, make sure that the RRset matches the qtype. */
|
||||
if(qinfo->qtype != LDNS_RR_TYPE_ANY &&
|
||||
qinfo->qtype != rrset->type) {
|
||||
remove_rrset("normalize: removing irrelevant RRset:",
|
||||
pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Mark the additional names from relevant rrset as OK. */
|
||||
mark_additional_rrset(pkt, msg, rrset);
|
||||
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
}
|
||||
|
||||
/* Mark additional names from AUTHORITY */
|
||||
while(rrset && rrset->section == LDNS_SECTION_AUTHORITY) {
|
||||
mark_additional_rrset(pkt, msg, rrset);
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
}
|
||||
|
||||
/* For each record in the additional section, remove it if it is an
|
||||
* address record and not in the collection of additional names
|
||||
* found in ANSWER and AUTHORITY. */
|
||||
/* These records have not been marked OK previously */
|
||||
while(rrset && rrset->section == LDNS_SECTION_ADDITIONAL) {
|
||||
/* FIXME: what about other types? */
|
||||
if(rrset->type==LDNS_RR_TYPE_A ||
|
||||
rrset->type==LDNS_RR_TYPE_AAAA)
|
||||
{
|
||||
if((rrset->flags & RRSET_SCRUB_OK)) {
|
||||
/* remove flag to clean up flags variable */
|
||||
rrset->flags &= ~RRSET_SCRUB_OK;
|
||||
} else {
|
||||
remove_rrset("normalize: removing irrelevant "
|
||||
"RRset:", pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store potential poison in the cache (only if hardening disabled).
|
||||
* The rrset is stored in the cache but removed from the message.
|
||||
* So that it will be used for infrastructure purposes, but not be
|
||||
* returned to the client.
|
||||
* @param pkt: packet
|
||||
* @param msg: message parsed
|
||||
* @param env: environment with cache
|
||||
* @param rrset: to store.
|
||||
*/
|
||||
static void
|
||||
store_rrset(ldns_buffer* pkt, struct msg_parse* msg, struct module_env* env,
|
||||
struct rrset_parse* rrset)
|
||||
{
|
||||
struct ub_packed_rrset_key* k;
|
||||
struct packed_rrset_data* d;
|
||||
struct rrset_ref ref;
|
||||
uint32_t now = time(NULL);
|
||||
|
||||
k = alloc_special_obtain(env->alloc);
|
||||
if(!k)
|
||||
return;
|
||||
k->entry.data = NULL;
|
||||
if(!parse_copy_decompress_rrset(pkt, msg, rrset, NULL, k)) {
|
||||
alloc_special_release(env->alloc, k);
|
||||
return;
|
||||
}
|
||||
d = (struct packed_rrset_data*)k->entry.data;
|
||||
packed_rrset_ttl_add(d, now);
|
||||
ref.key = k;
|
||||
ref.id = k->id;
|
||||
/*ignore ret: it was in the cache, ref updated */
|
||||
(void)rrset_cache_update(env->rrset_cache, &ref,
|
||||
env->alloc, now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a response event, remove suspect RRsets from the response.
|
||||
* "Suspect" rrsets are potentially poison. Note that this routine expects
|
||||
* the response to be in a "normalized" state -- that is, all "irrelevant"
|
||||
* RRsets have already been removed, CNAMEs are in order, etc.
|
||||
*
|
||||
* @param pkt: packet.
|
||||
* @param msg: msg to normalize.
|
||||
* @param zonename: name of server zone.
|
||||
* @param env: module environment with config and cache.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
static int
|
||||
scrub_sanitize(ldns_buffer* pkt, struct msg_parse* msg, uint8_t* zonename,
|
||||
struct module_env* env)
|
||||
{
|
||||
struct rrset_parse* rrset, *prev;
|
||||
prev = NULL;
|
||||
rrset = msg->rrset_first;
|
||||
|
||||
/* 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
|
||||
* authoritative for the originating zone. It, of course, MAY
|
||||
* be authoriative for any other zones, and of course, MAY
|
||||
* NOT be authoritative for some subdomains of the originating
|
||||
* zone. */
|
||||
while(rrset) {
|
||||
|
||||
/* skip DNAME records -- they will always be followed by a
|
||||
* synthesized CNAME, which will be relevant.
|
||||
* FIXME: should this do something differently with DNAME
|
||||
* rrsets NOT in Section.ANSWER? */
|
||||
/* But since DNAME records are also subdomains of the zone,
|
||||
* same check can be used */
|
||||
|
||||
if(!pkt_sub(pkt, rrset->dname, zonename)) {
|
||||
if(msg->an_rrsets == 0 &&
|
||||
rrset->type == LDNS_RR_TYPE_NS &&
|
||||
rrset->section == LDNS_SECTION_AUTHORITY &&
|
||||
FLAGS_GET_RCODE(msg->flags) ==
|
||||
LDNS_RCODE_NOERROR) {
|
||||
/* noerror, nodata and this NS rrset is above
|
||||
* the zone. This is LAME!
|
||||
* Leave in the NS for lame classification. */
|
||||
} else if(!env->cfg->harden_glue) {
|
||||
/* store in cache! Since it is relevant
|
||||
* (from normalize) it will be picked up
|
||||
* from the cache to be used later */
|
||||
store_rrset(pkt, msg, env, rrset);
|
||||
remove_rrset("sanitize: storing potential "
|
||||
"poison RRset:", pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
} else {
|
||||
remove_rrset("sanitize: removing potential "
|
||||
"poison RRset:", pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
scrub_message(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct query_info* qinfo, uint8_t* zonename, struct regional* region,
|
||||
struct module_env* env)
|
||||
{
|
||||
/* basic sanity checks */
|
||||
log_nametypeclass(VERB_ALGO, "scrub for", zonename, LDNS_RR_TYPE_NS,
|
||||
qinfo->qclass);
|
||||
if(msg->qdcount > 1)
|
||||
return 0;
|
||||
if( !(msg->flags&BIT_QR) )
|
||||
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) {
|
||||
if(dname_pkt_compare(pkt, msg->qname, qinfo->qname) != 0)
|
||||
return 0;
|
||||
if(msg->qtype != qinfo->qtype || msg->qclass != qinfo->qclass)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* normalize the response, this cleans up the additional. */
|
||||
if(!scrub_normalize(pkt, msg, qinfo, region))
|
||||
return 0;
|
||||
/* delete all out-of-zone information */
|
||||
if(!scrub_sanitize(pkt, msg, zonename, env))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* iterator/iter_scrub.h - scrubbing, normalization, sanitization of DNS msgs.
|
||||
*
|
||||
* 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 has routine(s) for cleaning up incoming DNS messages from
|
||||
* possible useless or malicious junk in it.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_SCRUB_H
|
||||
#define ITERATOR_ITER_SCRUB_H
|
||||
struct msg_parse;
|
||||
struct query_info;
|
||||
struct regional;
|
||||
struct module_env;
|
||||
|
||||
/**
|
||||
* Cleanup the passed dns message.
|
||||
* @param pkt: the packet itself, for resolving name compression pointers.
|
||||
* the packet buffer is unaltered.
|
||||
* @param msg: the parsed packet, this structure is cleaned up.
|
||||
* @param qinfo: the query info that was sent to the server. Checked.
|
||||
* @param zonename: the name of the last delegation point.
|
||||
* 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.
|
||||
* @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);
|
||||
|
||||
#endif /* ITERATOR_ITER_SCRUB_H */
|
||||
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
* iterator/iter_utils.c - iterative resolver module utility functions.
|
||||
*
|
||||
* 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 assist the iterator module.
|
||||
* Configuration options. Forward zones.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_utils.h"
|
||||
#include "iterator/iterator.h"
|
||||
#include "iterator/iter_hints.h"
|
||||
#include "iterator/iter_fwd.h"
|
||||
#include "iterator/iter_donotq.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/module.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/random.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "validator/val_anchor.h"
|
||||
|
||||
/** fillup fetch policy array */
|
||||
static void
|
||||
fetch_fill(struct iter_env* ie, const char* str)
|
||||
{
|
||||
char* s = (char*)str, *e;
|
||||
int i;
|
||||
for(i=0; i<ie->max_dependency_depth+1; i++) {
|
||||
ie->target_fetch_policy[i] = strtol(s, &e, 10);
|
||||
if(s == e)
|
||||
fatal_exit("cannot parse fetch policy number %s", s);
|
||||
s = e;
|
||||
}
|
||||
}
|
||||
|
||||
/** Read config string that represents the target fetch policy */
|
||||
static int
|
||||
read_fetch_policy(struct iter_env* ie, const char* str)
|
||||
{
|
||||
int count = cfg_count_numbers(str);
|
||||
if(count < 1) {
|
||||
log_err("Cannot parse target fetch policy: \"%s\"", str);
|
||||
return 0;
|
||||
}
|
||||
ie->max_dependency_depth = count - 1;
|
||||
ie->target_fetch_policy = (int*)calloc(
|
||||
(size_t)ie->max_dependency_depth+1, sizeof(int));
|
||||
if(!ie->target_fetch_policy) {
|
||||
log_err("alloc fetch policy: out of memory");
|
||||
return 0;
|
||||
}
|
||||
fetch_fill(ie, str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
|
||||
{
|
||||
int i;
|
||||
/* target fetch policy */
|
||||
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_QUERY, "target fetch policy for level %d is %d",
|
||||
i, iter_env->target_fetch_policy[i]);
|
||||
|
||||
if(!iter_env->hints)
|
||||
iter_env->hints = hints_create();
|
||||
if(!iter_env->hints || !hints_apply_cfg(iter_env->hints, cfg)) {
|
||||
log_err("Could not set root or stub hints");
|
||||
return 0;
|
||||
}
|
||||
if(!iter_env->fwds)
|
||||
iter_env->fwds = forwards_create();
|
||||
if(!iter_env->fwds || !forwards_apply_cfg(iter_env->fwds, cfg)) {
|
||||
log_err("Could not set forward zones");
|
||||
return 0;
|
||||
}
|
||||
if(!iter_env->donotq)
|
||||
iter_env->donotq = donotq_create();
|
||||
if(!iter_env->donotq || !donotq_apply_cfg(iter_env->donotq, cfg)) {
|
||||
log_err("Could not set donotqueryaddresses");
|
||||
return 0;
|
||||
}
|
||||
iter_env->supports_ipv6 = cfg->do_ip6;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** filter out unsuitable targets, return rtt or -1 */
|
||||
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)
|
||||
{
|
||||
int rtt;
|
||||
int lame;
|
||||
int dnsseclame;
|
||||
if(donotq_lookup(iter_env->donotq, &a->addr, a->addrlen)) {
|
||||
return -1; /* server is on the donotquery list */
|
||||
}
|
||||
if(!iter_env->supports_ipv6 && addr_is_ip6(&a->addr, a->addrlen)) {
|
||||
return -1; /* there is no ip6 available */
|
||||
}
|
||||
/* check lameness - need zone , class info */
|
||||
if(infra_get_lame_rtt(env->infra_cache, &a->addr, a->addrlen,
|
||||
name, namelen, &lame, &dnsseclame, &rtt, now)) {
|
||||
if(lame)
|
||||
return -1; /* server is lame */
|
||||
else if(rtt >= USEFUL_SERVER_TOP_TIMEOUT)
|
||||
return -1; /* server is unresponsive */
|
||||
else if(dnsseclame)
|
||||
return rtt+USEFUL_SERVER_TOP_TIMEOUT; /* nonpref */
|
||||
else return rtt;
|
||||
}
|
||||
/* no server information present */
|
||||
return UNKNOWN_SERVER_NICENESS;
|
||||
}
|
||||
|
||||
/** 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)
|
||||
{
|
||||
int got_num = 0, got_rtt = 0, thisrtt, swap_to_front;
|
||||
struct delegpt_addr* a, *n, *prev=NULL;
|
||||
|
||||
a = dp->result_list;
|
||||
while(a) {
|
||||
/* filter out unsuitable targets */
|
||||
thisrtt = iter_filter_unsuitable(iter_env, env, name, namelen,
|
||||
now, a);
|
||||
if(thisrtt == -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) {
|
||||
got_num++;
|
||||
swap_to_front = 1;
|
||||
} else if(thisrtt < got_rtt) {
|
||||
got_rtt = thisrtt;
|
||||
got_num = 1; /* start back at count of 1 */
|
||||
swap_to_front = 1;
|
||||
}
|
||||
/* swap to front if necessary, or move to next result */
|
||||
if(swap_to_front && prev) {
|
||||
n = a->next_result;
|
||||
prev->next_result = n;
|
||||
a->next_result = dp->result_list;
|
||||
dp->result_list = a;
|
||||
a = n;
|
||||
} else {
|
||||
prev = a;
|
||||
a = a->next_result;
|
||||
}
|
||||
}
|
||||
*best_rtt = got_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)
|
||||
{
|
||||
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);
|
||||
|
||||
if(num == 0)
|
||||
return NULL;
|
||||
if(selrtt >= USEFUL_SERVER_TOP_TIMEOUT)
|
||||
*dnssec_expected = 0;
|
||||
if(num == 1) {
|
||||
a = dp->result_list;
|
||||
if(++a->attempts < OUTBOUND_MSG_RETRY)
|
||||
return a;
|
||||
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
|
||||
* we do need it to be threadsafe, so we use this */
|
||||
sel = ub_random(env->rnd) % num;
|
||||
a = dp->result_list;
|
||||
prev = NULL;
|
||||
while(sel > 0 && a) {
|
||||
prev = a;
|
||||
a = a->next_result;
|
||||
sel--;
|
||||
}
|
||||
if(!a) /* robustness */
|
||||
return NULL;
|
||||
if(++a->attempts < OUTBOUND_MSG_RETRY)
|
||||
return a;
|
||||
/* remove it from the delegation point result list */
|
||||
if(prev)
|
||||
prev->next_result = a->next_result;
|
||||
else dp->result_list = a->next_result;
|
||||
return a;
|
||||
}
|
||||
|
||||
struct dns_msg*
|
||||
dns_alloc_msg(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct regional* region)
|
||||
{
|
||||
struct dns_msg* m = (struct dns_msg*)regional_alloc(region,
|
||||
sizeof(struct dns_msg));
|
||||
if(!m)
|
||||
return NULL;
|
||||
memset(m, 0, sizeof(*m));
|
||||
if(!parse_create_msg(pkt, msg, NULL, &m->qinfo, &m->rep, region)) {
|
||||
log_err("malloc failure: allocating incoming dns_msg");
|
||||
return NULL;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
struct dns_msg*
|
||||
dns_copy_msg(struct dns_msg* from, struct regional* region)
|
||||
{
|
||||
struct dns_msg* m = (struct dns_msg*)regional_alloc(region,
|
||||
sizeof(struct dns_msg));
|
||||
if(!m)
|
||||
return NULL;
|
||||
m->qinfo = from->qinfo;
|
||||
if(!(m->qinfo.qname = regional_alloc_init(region, from->qinfo.qname,
|
||||
from->qinfo.qname_len)))
|
||||
return NULL;
|
||||
if(!(m->rep = reply_info_copy(from->rep, NULL, region)))
|
||||
return NULL;
|
||||
return m;
|
||||
}
|
||||
|
||||
int
|
||||
iter_dns_store(struct module_env* env, struct query_info* msgqinf,
|
||||
struct reply_info* msgrep, int is_referral)
|
||||
{
|
||||
return dns_cache_store(env, msgqinf, msgrep, is_referral);
|
||||
}
|
||||
|
||||
int
|
||||
iter_ns_probability(struct ub_randstate* rnd, int n, int m)
|
||||
{
|
||||
int sel;
|
||||
if(n == m) /* 100% chance */
|
||||
return 1;
|
||||
/* we do not need secure random numbers here, but
|
||||
* we do need it to be threadsafe, so we use this */
|
||||
sel = ub_random(rnd) % m;
|
||||
return (sel < n);
|
||||
}
|
||||
|
||||
/** detect dependency cycle for query and target */
|
||||
static int
|
||||
causes_cycle(struct module_qstate* qstate, uint8_t* name, size_t namelen,
|
||||
uint16_t t, uint16_t c)
|
||||
{
|
||||
struct query_info qinf;
|
||||
qinf.qname = name;
|
||||
qinf.qname_len = namelen;
|
||||
qinf.qtype = t;
|
||||
qinf.qclass = c;
|
||||
log_assert(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);
|
||||
}
|
||||
|
||||
void
|
||||
iter_mark_cycle_targets(struct module_qstate* qstate, struct delegpt* dp)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
if(ns->resolved)
|
||||
continue;
|
||||
/* see if this ns as target causes dependency cycle */
|
||||
if(causes_cycle(qstate, ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_AAAA, qstate->qinfo.qclass) ||
|
||||
causes_cycle(qstate, ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_A, qstate->qinfo.qclass)) {
|
||||
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,
|
||||
qstate->qinfo.qclass);
|
||||
ns->resolved = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
iter_dp_is_useless(struct module_qstate* qstate, struct delegpt* dp)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
/* check:
|
||||
* o all NS items are required glue.
|
||||
* o no addresses are provided.
|
||||
* o RD qflag is on.
|
||||
* OR
|
||||
* o no addresses are provided.
|
||||
* o RD qflag is on.
|
||||
* 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))
|
||||
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))
|
||||
return 1;
|
||||
|
||||
for(ns = dp->nslist; ns; ns = ns->next) {
|
||||
if(ns->resolved) /* skip failed targets */
|
||||
continue;
|
||||
if(!dname_subdomain_c(ns->name, dp->name))
|
||||
return 0; /* one address is not required glue */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
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)
|
||||
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,
|
||||
LDNS_RR_TYPE_DS, dclass))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iter_msg_has_dnssec(struct dns_msg* msg)
|
||||
{
|
||||
size_t i;
|
||||
if(!msg || !msg->rep)
|
||||
return 0;
|
||||
for(i=0; i<msg->rep->an_numrrsets + msg->rep->ns_numrrsets; i++) {
|
||||
if(((struct packed_rrset_data*)msg->rep->rrsets[i]->
|
||||
entry.data)->rrsig_count > 0)
|
||||
return 1;
|
||||
}
|
||||
/* empty message has no DNSSEC info, with DNSSEC the reply is
|
||||
* not empty (NSEC) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iter_msg_from_zone(struct dns_msg* msg, struct delegpt* dp,
|
||||
enum response_type type, uint16_t dclass)
|
||||
{
|
||||
if(!msg || !dp || !msg->rep || !dp->name)
|
||||
return 0;
|
||||
/* SOA RRset - always from reply zone */
|
||||
if(reply_find_rrset_section_an(msg->rep, dp->name, dp->namelen,
|
||||
LDNS_RR_TYPE_SOA, dclass) ||
|
||||
reply_find_rrset_section_ns(msg->rep, dp->name, dp->namelen,
|
||||
LDNS_RR_TYPE_SOA, dclass))
|
||||
return 1;
|
||||
if(type == RESPONSE_TYPE_REFERRAL) {
|
||||
size_t i;
|
||||
/* if it adds a single label, i.e. we expect .com,
|
||||
* and referral to example.com. NS ... , then origin zone
|
||||
* is .com. For a referral to sub.example.com. NS ... then
|
||||
* we do not know, since example.com. may be in between. */
|
||||
for(i=0; i<msg->rep->an_numrrsets+msg->rep->ns_numrrsets;
|
||||
i++) {
|
||||
struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NS &&
|
||||
ntohs(s->rk.rrset_class) == dclass) {
|
||||
int l = dname_count_labels(s->rk.dname);
|
||||
if(l == dp->namelabs + 1 &&
|
||||
dname_strict_subdomain(s->rk.dname,
|
||||
l, dp->name, dp->namelabs))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
log_assert(type==RESPONSE_TYPE_ANSWER || type==RESPONSE_TYPE_CNAME);
|
||||
/* not a referral, and not lame delegation (upwards), so,
|
||||
* any NS rrset must be from the zone itself */
|
||||
if(reply_find_rrset_section_an(msg->rep, dp->name, dp->namelen,
|
||||
LDNS_RR_TYPE_NS, dclass) ||
|
||||
reply_find_rrset_section_ns(msg->rep, dp->name, dp->namelen,
|
||||
LDNS_RR_TYPE_NS, dclass))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* iterator/iter_utils.h - iterative resolver module utility functions.
|
||||
*
|
||||
* 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 assist the iterator module.
|
||||
* Configuration options. Forward zones.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITER_UTILS_H
|
||||
#define ITERATOR_ITER_UTILS_H
|
||||
#include "iterator/iter_resptype.h"
|
||||
struct iter_env;
|
||||
struct config_file;
|
||||
struct module_env;
|
||||
struct delegpt_addr;
|
||||
struct delegpt;
|
||||
struct regional;
|
||||
struct msg_parse;
|
||||
struct ub_randstate;
|
||||
struct query_info;
|
||||
struct reply_info;
|
||||
struct module_qstate;
|
||||
|
||||
/**
|
||||
* Process config options and set iterator module state.
|
||||
* Sets default values if no config is found.
|
||||
* @param iter_env: iterator module state.
|
||||
* @param cfg: config options.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
int iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Select a valid, nice target to send query to.
|
||||
* Sorting and removing unsuitable targets is combined.
|
||||
*
|
||||
* @param iter_env: iterator module global state, with ip6 enabled and
|
||||
* do-not-query-addresses.
|
||||
* @param env: environment with infra cache (lameness, rtt info).
|
||||
* @param dp: delegation point with result list.
|
||||
* @param name: zone name (for lameness check).
|
||||
* @param namelen: length of name.
|
||||
* @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.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Allocate dns_msg from parsed msg, in regional.
|
||||
* @param pkt: packet.
|
||||
* @param msg: parsed message (cleaned and ready for regional allocation).
|
||||
* @param regional: regional to use for allocation.
|
||||
* @return newly allocated dns_msg, or NULL on memory error.
|
||||
*/
|
||||
struct dns_msg* dns_alloc_msg(ldns_buffer* pkt, struct msg_parse* msg,
|
||||
struct regional* regional);
|
||||
|
||||
/**
|
||||
* Copy a dns_msg to this regional.
|
||||
* @param from: dns message, also in regional.
|
||||
* @param regional: regional to use for allocation.
|
||||
* @return newly allocated dns_msg, or NULL on memory error.
|
||||
*/
|
||||
struct dns_msg* dns_copy_msg(struct dns_msg* from, struct regional* regional);
|
||||
|
||||
/**
|
||||
* Allocate a dns_msg with malloc/alloc structure and store in dns cache.
|
||||
* @param env: environment, with alloc structure and dns cache.
|
||||
* @param qinf: query info, the query for which answer is stored.
|
||||
* @param rep: reply in dns_msg from dns_alloc_msg for example.
|
||||
* @param is_referral: If true, then the given message to be stored is a
|
||||
* referral. The cache implementation may use this as a hint.
|
||||
* @return 0 on alloc error (out of memory).
|
||||
*/
|
||||
int iter_dns_store(struct module_env* env, struct query_info* qinf,
|
||||
struct reply_info* rep, int is_referral);
|
||||
|
||||
/**
|
||||
* Select randomly with n/m probability.
|
||||
* For shuffle NS records for address fetching.
|
||||
* @param rnd: random table
|
||||
* @param n: probability.
|
||||
* @param m: divisor for probability.
|
||||
* @return true with n/m probability.
|
||||
*/
|
||||
int iter_ns_probability(struct ub_randstate* rnd, int n, int m);
|
||||
|
||||
/**
|
||||
* Mark targets that result in a dependency cycle as done, so they
|
||||
* will not get selected as targets.
|
||||
* @param qstate: query state.
|
||||
* @param dp: delegpt to mark ns in.
|
||||
*/
|
||||
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 dp: delegpt to check.
|
||||
*/
|
||||
int iter_dp_is_useless(struct module_qstate* qstate, struct delegpt* dp);
|
||||
|
||||
/**
|
||||
* See if delegation is expected to have DNSSEC information (RRSIGs) in
|
||||
* its answers, or not. Inspects delegation point (name), trust anchors,
|
||||
* and delegation message (DS RRset) to determine this.
|
||||
* @param env: module env with trust anchors.
|
||||
* @param dp: delegation point.
|
||||
* @param msg: delegation message, with DS if a secure referral.
|
||||
* @param dclass: class of query.
|
||||
* @return 1 if dnssec is expected, 0 if not.
|
||||
*/
|
||||
int iter_indicates_dnssec(struct module_env* env, struct delegpt* dp,
|
||||
struct dns_msg* msg, uint16_t dclass);
|
||||
|
||||
/**
|
||||
* See if a message contains DNSSEC.
|
||||
* This is examined by looking for RRSIGs. With DNSSEC a valid answer,
|
||||
* nxdomain, nodata, referral or cname reply has RRSIGs in answer or auth
|
||||
* sections, sigs on answer data, SOA, DS, or NSEC/NSEC3 records.
|
||||
* @param msg: message to examine.
|
||||
* @return true if DNSSEC information was found.
|
||||
*/
|
||||
int iter_msg_has_dnssec(struct dns_msg* msg);
|
||||
|
||||
/**
|
||||
* See if a message is known to be from a certain zone.
|
||||
* This looks for SOA or NS rrsets, for answers.
|
||||
* For referrals, when one label is delegated, the zone is detected.
|
||||
* Does not look at signatures.
|
||||
* @param msg: the message to inspect.
|
||||
* @param dp: delegation point with zone name to look for.
|
||||
* @param type: type of message.
|
||||
* @param dclass: class of query.
|
||||
* @return true if message is certain to be from zone in dp->name.
|
||||
* false if not sure (empty msg), or not from the zone.
|
||||
*/
|
||||
int iter_msg_from_zone(struct dns_msg* msg, struct delegpt* dp,
|
||||
enum response_type type, uint16_t dclass);
|
||||
|
||||
#endif /* ITERATOR_ITER_UTILS_H */
|
||||
+1863
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* iterator/iterator.h - iterative resolver DNS query response module
|
||||
*
|
||||
* 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 a module that performs recusive iterative DNS query
|
||||
* processing.
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_ITERATOR_H
|
||||
#define ITERATOR_ITERATOR_H
|
||||
#include "services/outbound_list.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/module.h"
|
||||
struct delegpt;
|
||||
struct iter_hints;
|
||||
struct iter_forwards;
|
||||
struct iter_donotq;
|
||||
struct iter_prep_list;
|
||||
|
||||
/** max number of query restarts. Determines max number of CNAME chain. */
|
||||
#define MAX_RESTART_COUNT 8
|
||||
/** max number of referrals. Makes sure resolver does not run away */
|
||||
#define MAX_REFERRAL_COUNT 30
|
||||
/** how nice is a server without further information, in msec
|
||||
* Equals rtt initial timeout value.
|
||||
*/
|
||||
#define UNKNOWN_SERVER_NICENESS 376
|
||||
/** maximum timeout before a host is deemed unsuitable, in msec.
|
||||
* After host_ttl this will be timed out and the host will be tried again.
|
||||
* Equals RTT_MAX_TIMEOUT
|
||||
*/
|
||||
#define USEFUL_SERVER_TOP_TIMEOUT 120000
|
||||
/** number of retries on outgoing queries */
|
||||
#define OUTBOUND_MSG_RETRY 4
|
||||
|
||||
/**
|
||||
* Global state for the iterator.
|
||||
*/
|
||||
struct iter_env {
|
||||
/**
|
||||
* The hints -- these aren't stored in the cache because they don't
|
||||
* expire. The hints are always used to "prime" the cache. Note
|
||||
* that both root hints and stub zone "hints" are stored in this
|
||||
* data structure.
|
||||
*/
|
||||
struct iter_hints* hints;
|
||||
|
||||
/** A flag to indicate whether or not we have an IPv6 route */
|
||||
int supports_ipv6;
|
||||
|
||||
/** Mapping of forwarding zones to targets. */
|
||||
struct iter_forwards* fwds;
|
||||
|
||||
/** A set of inetaddrs that should never be queried. */
|
||||
struct iter_donotq* donotq;
|
||||
|
||||
/** The maximum dependency depth that this resolver will pursue. */
|
||||
int max_dependency_depth;
|
||||
|
||||
/**
|
||||
* The target fetch policy for each dependency level. This is
|
||||
* described as a simple number (per dependency level):
|
||||
* negative numbers (usually just -1) mean fetch-all,
|
||||
* 0 means only fetch on demand, and
|
||||
* positive numbers mean to fetch at most that many targets.
|
||||
* array of max_dependency_depth+1 size.
|
||||
*/
|
||||
int* target_fetch_policy;
|
||||
};
|
||||
|
||||
/**
|
||||
* State of the iterator for a query.
|
||||
*/
|
||||
enum iter_state {
|
||||
/**
|
||||
* Externally generated queries start at this state. Query restarts are
|
||||
* reset to this state.
|
||||
*/
|
||||
INIT_REQUEST_STATE = 0,
|
||||
|
||||
/**
|
||||
* Root priming events reactivate here, most other events pass
|
||||
* through this naturally as the 2nd part of the INIT_REQUEST_STATE.
|
||||
*/
|
||||
INIT_REQUEST_2_STATE,
|
||||
|
||||
/**
|
||||
* Stub priming events reactivate here, most other events pass
|
||||
* through this naturally as the 3rd part of the INIT_REQUEST_STATE.
|
||||
*/
|
||||
INIT_REQUEST_3_STATE,
|
||||
|
||||
/**
|
||||
* Each time a delegation point changes for a given query or a
|
||||
* query times out and/or wakes up, this state is (re)visited.
|
||||
* This state is reponsible for iterating through a list of
|
||||
* nameserver targets.
|
||||
*/
|
||||
QUERYTARGETS_STATE,
|
||||
|
||||
/**
|
||||
* Responses to queries start at this state. This state handles
|
||||
* the decision tree associated with handling responses.
|
||||
*/
|
||||
QUERY_RESP_STATE,
|
||||
|
||||
/** Responses to priming queries finish at this state. */
|
||||
PRIME_RESP_STATE,
|
||||
|
||||
/** Responses that are to be returned upstream end at this state.
|
||||
* As well as responses to target queries. */
|
||||
FINISHED_STATE
|
||||
};
|
||||
|
||||
/**
|
||||
* Per query state for the iterator module.
|
||||
*/
|
||||
struct iter_qstate {
|
||||
/**
|
||||
* State of the iterator module.
|
||||
* This is the state that event is in or should sent to -- all
|
||||
* requests should start with the INIT_REQUEST_STATE. All
|
||||
* responses should start with QUERY_RESP_STATE. Subsequent
|
||||
* processing of the event will change this state.
|
||||
*/
|
||||
enum iter_state state;
|
||||
|
||||
/**
|
||||
* Final state for the iterator module.
|
||||
* This is the state that responses should be routed to once the
|
||||
* response is final. For externally initiated queries, this
|
||||
* will be FINISHED_STATE, locally initiated queries will have
|
||||
* different final states.
|
||||
*/
|
||||
enum iter_state final_state;
|
||||
|
||||
/**
|
||||
* The depth of this query, this means the depth of recursion.
|
||||
* This address is needed for another query, which is an address
|
||||
* needed for another query, etc. Original client query has depth 0.
|
||||
*/
|
||||
int depth;
|
||||
|
||||
/**
|
||||
* The response
|
||||
*/
|
||||
struct dns_msg* response;
|
||||
|
||||
/**
|
||||
* This is a list of RRsets that must be prepended to the
|
||||
* ANSWER section of a response before being sent upstream.
|
||||
*/
|
||||
struct iter_prep_list* an_prepend_list;
|
||||
/** Last element of the prepend list */
|
||||
struct iter_prep_list* an_prepend_last;
|
||||
|
||||
/**
|
||||
* This is the list of RRsets that must be prepended to the
|
||||
* AUTHORITY section of the response before being sent upstream.
|
||||
*/
|
||||
struct iter_prep_list* ns_prepend_list;
|
||||
/** Last element of the authority prepend list */
|
||||
struct iter_prep_list* ns_prepend_last;
|
||||
|
||||
/** query name used for chasing the results. Initially the same as
|
||||
* the state qinfo, but after CNAMEs this will be different.
|
||||
* The query info used to elicit the results needed. */
|
||||
struct query_info qchase;
|
||||
/** query flags to use when chasing the answer (i.e. RD flag) */
|
||||
uint16_t chase_flags;
|
||||
|
||||
/**
|
||||
* This is the current delegation point for an in-progress query. This
|
||||
* object retains state as to which delegation targets need to be
|
||||
* (sub)queried for vs which ones have already been visited.
|
||||
*/
|
||||
struct delegpt* dp;
|
||||
|
||||
/** Current delegation message - returned for non-RD queries */
|
||||
struct dns_msg* deleg_msg;
|
||||
|
||||
/** number of outstanding target sub queries */
|
||||
int num_target_queries;
|
||||
|
||||
/** outstanding direct queries */
|
||||
int num_current_queries;
|
||||
|
||||
/** the number of times this query has been restarted. */
|
||||
int query_restart_count;
|
||||
|
||||
/** the number of times this query as followed a referral. */
|
||||
int referral_count;
|
||||
|
||||
/**
|
||||
* expected dnssec information for this iteration step.
|
||||
* If dnssec rrsigs are expected and not given, the server is marked
|
||||
* lame (dnssec-lame).
|
||||
*/
|
||||
int dnssec_expected;
|
||||
|
||||
/**
|
||||
* This is flag that, if true, means that this event is
|
||||
* waiting for a stub priming query.
|
||||
*/
|
||||
int wait_priming_stub;
|
||||
|
||||
/**
|
||||
* This is a flag that, if true, means that this query is
|
||||
* for (re)fetching glue from a zone. Since the address should
|
||||
* have been glue, query again to the servers that should have
|
||||
* been returning it as glue.
|
||||
* The delegation point must be set to the one that should *not*
|
||||
* be used when creating the state. A higher one will be attempted.
|
||||
*/
|
||||
int refetch_glue;
|
||||
|
||||
/** list of pending queries to authoritative servers. */
|
||||
struct outbound_list outlist;
|
||||
};
|
||||
|
||||
/**
|
||||
* List of prepend items
|
||||
*/
|
||||
struct iter_prep_list {
|
||||
/** next in list */
|
||||
struct iter_prep_list* next;
|
||||
/** rrset */
|
||||
struct ub_packed_rrset_key* rrset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the iterator function block.
|
||||
* @return: function block with function pointers to iterator methods.
|
||||
*/
|
||||
struct module_func_block* iter_get_funcblock();
|
||||
|
||||
/**
|
||||
* Get iterator state as a string
|
||||
* @param state: to convert
|
||||
* @return constant string that is printable.
|
||||
*/
|
||||
const char* iter_state_to_string(enum iter_state state);
|
||||
|
||||
/**
|
||||
* See if iterator state is a response state
|
||||
* @param s: to inspect
|
||||
* @return true if response state.
|
||||
*/
|
||||
int iter_state_is_responsestate(enum iter_state s);
|
||||
|
||||
/** iterator init */
|
||||
int iter_init(struct module_env* env, int id);
|
||||
|
||||
/** iterator deinit */
|
||||
void iter_deinit(struct module_env* env, int id);
|
||||
|
||||
/** iterator operate on a query */
|
||||
void iter_operate(struct module_qstate* qstate, enum module_ev event, int id,
|
||||
struct outbound_entry* outbound);
|
||||
|
||||
/**
|
||||
* Return priming query results to interestes super querystates.
|
||||
*
|
||||
* Sets the delegation point and delegation message (not nonRD queries).
|
||||
* This is a callback from walk_supers.
|
||||
*
|
||||
* @param qstate: query state that finished.
|
||||
* @param id: module id.
|
||||
* @param super: the qstate to inform.
|
||||
*/
|
||||
void iter_inform_super(struct module_qstate* qstate, int id,
|
||||
struct module_qstate* super);
|
||||
|
||||
/** iterator cleanup query state */
|
||||
void iter_clear(struct module_qstate* qstate, int id);
|
||||
|
||||
/** iterator alloc size routine */
|
||||
size_t iter_get_mem(struct module_env* env, int id);
|
||||
|
||||
#endif /* ITERATOR_ITERATOR_H */
|
||||
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,342 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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, [0] read from, [1] write on */
|
||||
int qqpipe[2];
|
||||
/** mutex on result read pipe */
|
||||
lock_basic_t rrpipe_lock;
|
||||
/** the result read pipe, [0] read from, [1] write on */
|
||||
int rrpipe[2];
|
||||
|
||||
/* --- 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,923 @@
|
||||
/*
|
||||
* 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 "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;
|
||||
log_init(NULL, 0, NULL); /* logs to stderr */
|
||||
log_ident_set("libunbound");
|
||||
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(socketpair(AF_UNIX, SOCK_STREAM, 0, ctx->qqpipe) == -1) {
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
if(socketpair(AF_UNIX, SOCK_STREAM, 0, ctx->rrpipe) == -1) {
|
||||
int e = errno;
|
||||
close(ctx->qqpipe[0]);
|
||||
close(ctx->qqpipe[1]);
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
errno = e;
|
||||
return NULL;
|
||||
}
|
||||
if(!fd_set_nonblock(ctx->rrpipe[0]) ||
|
||||
!fd_set_nonblock(ctx->rrpipe[1]) ||
|
||||
!fd_set_nonblock(ctx->qqpipe[0]) ||
|
||||
!fd_set_nonblock(ctx->qqpipe[1])) {
|
||||
int e = errno;
|
||||
close(ctx->rrpipe[0]);
|
||||
close(ctx->rrpipe[1]);
|
||||
close(ctx->qqpipe[0]);
|
||||
close(ctx->qqpipe[1]);
|
||||
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) {
|
||||
close(ctx->rrpipe[0]);
|
||||
close(ctx->rrpipe[1]);
|
||||
close(ctx->qqpipe[0]);
|
||||
close(ctx->qqpipe[1]);
|
||||
ub_randfree(ctx->seed_rnd);
|
||||
free(ctx);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
ctx->env->cfg = config_create_forlib();
|
||||
if(!ctx->env->cfg) {
|
||||
close(ctx->rrpipe[0]);
|
||||
close(ctx->rrpipe[1]);
|
||||
close(ctx->qqpipe[0]);
|
||||
close(ctx->qqpipe[1]);
|
||||
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)libworker_write_msg(ctx->qqpipe[1], (uint8_t*)&cmd,
|
||||
(uint32_t)sizeof(cmd), 0);
|
||||
lock_basic_unlock(&ctx->qqpipe_lock);
|
||||
lock_basic_lock(&ctx->rrpipe_lock);
|
||||
while(libworker_read_msg(ctx->rrpipe[0], &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);
|
||||
if(ctx->qqpipe[0] != -1)
|
||||
close(ctx->qqpipe[0]);
|
||||
if(ctx->qqpipe[1] != -1)
|
||||
close(ctx->qqpipe[1]);
|
||||
if(ctx->rrpipe[0] != -1)
|
||||
close(ctx->rrpipe[0]);
|
||||
if(ctx->rrpipe[1] != -1)
|
||||
close(ctx->rrpipe[1]);
|
||||
ctx->qqpipe[0] = -1;
|
||||
ctx->qqpipe[1] = -1;
|
||||
ctx->rrpipe[0] = -1;
|
||||
ctx->rrpipe[1] = -1;
|
||||
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);
|
||||
}
|
||||
|
||||
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)) {
|
||||
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)
|
||||
{
|
||||
#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS)
|
||||
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;
|
||||
}
|
||||
|
||||
/** perform a select() on the result read pipe */
|
||||
static int
|
||||
pollit(struct ub_ctx* ctx, struct timeval* t)
|
||||
{
|
||||
fd_set r;
|
||||
#ifndef S_SPLINT_S
|
||||
FD_ZERO(&r);
|
||||
FD_SET(ctx->rrpipe[0], &r);
|
||||
#endif
|
||||
if(select(ctx->rrpipe[0]+1, &r, NULL, NULL, t) == -1) {
|
||||
return 0;
|
||||
}
|
||||
errno = 0;
|
||||
return FD_ISSET(ctx->rrpipe[0], &r);
|
||||
}
|
||||
|
||||
int
|
||||
ub_poll(struct ub_ctx* ctx)
|
||||
{
|
||||
struct timeval t;
|
||||
memset(&t, 0, sizeof(t));
|
||||
/* no need to hold lock while testing for readability. */
|
||||
return pollit(ctx, &t);
|
||||
}
|
||||
|
||||
int
|
||||
ub_fd(struct ub_ctx* ctx)
|
||||
{
|
||||
return ctx->rrpipe[0];
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
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;
|
||||
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 = libworker_read_msg(ctx->rrpipe[0], &msg, &len, 1);
|
||||
lock_basic_unlock(&ctx->rrpipe_lock);
|
||||
if(r == 0)
|
||||
return UB_PIPE;
|
||||
else if(r == -1)
|
||||
return UB_NOERROR;
|
||||
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 pollit */
|
||||
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 = pollit(ctx, NULL);
|
||||
if(r) {
|
||||
r = libworker_read_msg(ctx->rrpipe[0], &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);
|
||||
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;
|
||||
|
||||
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 */
|
||||
*result = NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
*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(!libworker_write_msg(ctx->qqpipe[1], 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--;
|
||||
context_query_delete(q);
|
||||
msg = context_serialize_cancel(q, &len);
|
||||
lock_basic_unlock(&ctx->cfglock);
|
||||
if(!msg) {
|
||||
return UB_NOMEM;
|
||||
}
|
||||
/* send cancel to background worker */
|
||||
lock_basic_lock(&ctx->qqpipe_lock);
|
||||
if(!libworker_write_msg(ctx->qqpipe[1], 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);
|
||||
}
|
||||
|
||||
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 != ' ' && *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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* 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 libworker_res_list;
|
||||
struct regional;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/** commpoint to listen to commands */
|
||||
struct comm_point* cmd_com;
|
||||
/** are we currently reading a command, 0 if not, else bytecount */
|
||||
size_t cmd_read;
|
||||
/** size of current read command, may be partially read */
|
||||
uint32_t cmd_len;
|
||||
/** the current read command content, malloced, can be partially read*/
|
||||
uint8_t* cmd_msg;
|
||||
|
||||
/** commpoint to write results back */
|
||||
struct comm_point* res_com;
|
||||
/** are we curently writing a result, 0 if not, else bytecount into
|
||||
* the res_list first entry. */
|
||||
size_t res_write;
|
||||
/** list of outstanding results to be written back */
|
||||
struct libworker_res_list* res_list;
|
||||
/** last in list */
|
||||
struct libworker_res_list* res_last;
|
||||
};
|
||||
|
||||
/**
|
||||
* List of results (arbitrary command serializations) to write back
|
||||
*/
|
||||
struct libworker_res_list {
|
||||
/** next in list */
|
||||
struct libworker_res_list* next;
|
||||
/** serialized buffer to write */
|
||||
uint8_t* buf;
|
||||
/** length to write */
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
int libworker_handle_control_cmd(struct comm_point* c, void* arg,
|
||||
int err, struct comm_reply* rep);
|
||||
|
||||
/** handle opportunity to write result back */
|
||||
int libworker_handle_result_write(struct comm_point* c, void* arg,
|
||||
int err, struct comm_reply* rep);
|
||||
|
||||
/**
|
||||
* Write length bytes followed by message.
|
||||
* @param fd: the socket to write on. Is nonblocking.
|
||||
* Set to blocking by the function,
|
||||
* and back to non-blocking at exit of function.
|
||||
* @param buf: the message.
|
||||
* @param len: length of message.
|
||||
* @param nonblock: if set to true, the first write is nonblocking.
|
||||
* If the first write fails the function returns -1.
|
||||
* If set false, the first write is blocking.
|
||||
* @return: all remainder writes are nonblocking.
|
||||
* return 0 on error, in that case blocking/nonblocking of socket is
|
||||
* unknown.
|
||||
* return 1 if all OK.
|
||||
*/
|
||||
int libworker_write_msg(int fd, uint8_t* buf, uint32_t len, int nonblock);
|
||||
|
||||
/**
|
||||
* Read length bytes followed by message.
|
||||
* @param fd: the socket to write on. Is nonblocking.
|
||||
* Set to blocking by the function,
|
||||
* and back to non-blocking at exit of function.
|
||||
* @param buf: the message, malloced.
|
||||
* @param len: length of message, returned.
|
||||
* @param nonblock: if set to true, the first read is nonblocking.
|
||||
* If the first read fails the function returns -1.
|
||||
* If set false, the first read is blocking.
|
||||
* @return: all remainder reads are nonblocking.
|
||||
* return 0 on error, in that case blocking/nonblocking of socket is
|
||||
* unknown. On EOF 0 is returned.
|
||||
* return 1 if all OK.
|
||||
*/
|
||||
int libworker_read_msg(int fd, uint8_t** buf, uint32_t* len, int nonblock);
|
||||
|
||||
/**
|
||||
* 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,461 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
+19
-9
@@ -134,7 +134,7 @@ done
|
||||
# Check if SVNROOT is specified.
|
||||
if [ -z "$SVNROOT" ]; then
|
||||
if test -f .svn/entries; then
|
||||
eval `grep 'url=' .svn/entries | head -1`
|
||||
eval `svn info | grep 'URL:' | sed -e 's/URL: /url=/' | head -1`
|
||||
SVNROOT="$url"
|
||||
fi
|
||||
if test -z "$SVNROOT"; then
|
||||
@@ -146,17 +146,15 @@ if test -z "$LDNSDIR"; then
|
||||
# try to autodetect from Makefile (if present)
|
||||
if test -f Makefile; then
|
||||
eval `grep 'ldnsdir=' Makefile`
|
||||
LDNSDIR="$ldnsdir"
|
||||
fi
|
||||
if test -z "$LDNSDIR"; then
|
||||
error "LDNSDIR not detected in Makefile, specify manually (using -l)"
|
||||
if echo "$ldnsdir" | grep -v ldns-src/ >/dev/null 2>&1; then
|
||||
LDNSDIR="$ldnsdir"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start the packaging process.
|
||||
info "SVNROOT is $SVNROOT"
|
||||
info "SNAPSHOT is $SNAPSHOT"
|
||||
info "LDNSDIR is $LDNSDIR"
|
||||
|
||||
#question "Do you wish to continue with these settings?" || error "User abort."
|
||||
|
||||
@@ -185,9 +183,17 @@ echo "#include \"util/configyyrename.h\"" > util/configlexer.c || error_cleanup
|
||||
flex -i -t util/configlexer.lex >> util/configlexer.c || error_cleanup "Failed to create configlexer"
|
||||
bison -y -d -o util/configparser.c util/configparser.y || error_cleanup "Failed to create configparser"
|
||||
|
||||
# copy ldns-testpkts from ldns examples
|
||||
#cp $LDNSDIR/examples/ldns-testpkts.c testcode/ldns-testpkts.c || error_cleanup "copy ldns/examples/.. failed"
|
||||
#cp $LDNSDIR/examples/ldns-testpkts.h testcode/ldns-testpkts.h || error_cleanup "copy ldns/examples/.. failed"
|
||||
# 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
|
||||
info "ldns-testpkts.c and ldns-testpkts.h are OK"
|
||||
else
|
||||
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 {} \;
|
||||
@@ -207,6 +213,10 @@ fi
|
||||
|
||||
replace_all doc/README
|
||||
replace_all doc/unbound.8
|
||||
replace_all doc/unbound.conf.5
|
||||
replace_all doc/unbound-checkconf.8
|
||||
replace_all doc/unbound-host.1
|
||||
replace_all doc/libunbound.3
|
||||
|
||||
info "Renaming Unbound directory to unbound-$version."
|
||||
cd ..
|
||||
|
||||
Vendored
+669
@@ -0,0 +1,669 @@
|
||||
/*
|
||||
* services/cache/dns.c - Cache services for DNS using msg and rrset caches.
|
||||
*
|
||||
* 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 DNS cache.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "iterator/iter_delegpt.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/module.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/regional.h"
|
||||
|
||||
/** 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).
|
||||
*/
|
||||
static void
|
||||
store_rrsets(struct module_env* env, struct reply_info* rep, uint32_t now)
|
||||
{
|
||||
size_t i;
|
||||
/* see if rrset already exists in cache, if not insert it. */
|
||||
for(i=0; i<rep->rrset_count; i++) {
|
||||
rep->ref[i].key = rep->rrsets[i];
|
||||
rep->ref[i].id = rep->rrsets[i]->id;
|
||||
if(rrset_cache_update(env->rrset_cache, &rep->ref[i],
|
||||
env->alloc, now)) /* it was in the cache */
|
||||
rep->rrsets[i] = rep->ref[i].key;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
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;
|
||||
size_t i;
|
||||
|
||||
/* store RRsets */
|
||||
for(i=0; i<rep->rrset_count; i++) {
|
||||
rep->ref[i].key = rep->rrsets[i];
|
||||
rep->ref[i].id = rep->rrsets[i]->id;
|
||||
}
|
||||
reply_info_sortref(rep);
|
||||
reply_info_set_ttls(rep, now);
|
||||
store_rrsets(env, rep, now);
|
||||
if(ttl == 0) {
|
||||
/* we do not store the message, but we did store the RRs,
|
||||
* which could be useful for delegation information */
|
||||
verbose(VERB_ALGO, "TTL 0: dropped msg from cache");
|
||||
free(rep);
|
||||
return;
|
||||
}
|
||||
|
||||
/* store msg in the cache */
|
||||
reply_info_sortref(rep);
|
||||
if(!(e = query_info_entrysetup(qinfo, rep, hash))) {
|
||||
log_err("store_msg: malloc failed");
|
||||
return;
|
||||
}
|
||||
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,
|
||||
uint16_t qclass, uint32_t now, uint16_t searchtype, int stripfront)
|
||||
{
|
||||
struct ub_packed_rrset_key *rrset;
|
||||
uint8_t lablen;
|
||||
|
||||
if(stripfront) {
|
||||
/* strip off so that DNAMEs have strict subdomain match */
|
||||
lablen = *qname;
|
||||
qname += lablen + 1;
|
||||
qnamelen -= lablen + 1;
|
||||
}
|
||||
|
||||
/* snip off front part of qname until the type is found */
|
||||
while(qnamelen > 0) {
|
||||
if((rrset = rrset_cache_lookup(env->rrset_cache, qname,
|
||||
qnamelen, searchtype, qclass, 0, now, 0)))
|
||||
return rrset;
|
||||
|
||||
/* snip off front label */
|
||||
lablen = *qname;
|
||||
qname += lablen + 1;
|
||||
qnamelen -= lablen + 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** add addr to additional section */
|
||||
static void
|
||||
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))) {
|
||||
msg->rep->ar_numrrsets++;
|
||||
msg->rep->rrset_count++;
|
||||
}
|
||||
}
|
||||
|
||||
/** find and add A and AAAA records for nameservers in delegpt */
|
||||
static int
|
||||
find_add_addrs(struct module_env* env, uint16_t qclass,
|
||||
struct regional* region, struct delegpt* dp, uint32_t now,
|
||||
struct dns_msg** msg)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
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)) {
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
return 0;
|
||||
}
|
||||
if(msg)
|
||||
addr_to_additional(akey, region, *msg, now);
|
||||
lock_rw_unlock(&akey->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)) {
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
return 0;
|
||||
}
|
||||
if(msg)
|
||||
addr_to_additional(akey, region, *msg, now);
|
||||
lock_rw_unlock(&akey->entry.lock);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** find and add A and AAAA records for missing nameservers in delegpt */
|
||||
int
|
||||
cache_fill_missing(struct module_env* env, uint16_t qclass,
|
||||
struct regional* region, struct delegpt* dp)
|
||||
{
|
||||
struct delegpt_ns* ns;
|
||||
struct ub_packed_rrset_key* akey;
|
||||
uint32_t now = time(NULL);
|
||||
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)) {
|
||||
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);
|
||||
}
|
||||
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)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** find and add DS or NSEC to delegation msg */
|
||||
static void
|
||||
find_add_ds(struct module_env* env, struct regional* region,
|
||||
struct dns_msg* msg, struct delegpt* dp, uint32_t now)
|
||||
{
|
||||
/* Lookup the DS or NSEC at the delegation point. */
|
||||
struct ub_packed_rrset_key* rrset = rrset_cache_lookup(
|
||||
env->rrset_cache, dp->name, dp->namelen, LDNS_RR_TYPE_DS,
|
||||
msg->qinfo.qclass, 0, now, 0);
|
||||
if(!rrset) {
|
||||
/* NOTE: this won't work for alternate NSEC schemes
|
||||
* (opt-in, NSEC3) */
|
||||
rrset = rrset_cache_lookup(env->rrset_cache, dp->name,
|
||||
dp->namelen, LDNS_RR_TYPE_NSEC, msg->qinfo.qclass,
|
||||
0, now, 0);
|
||||
/* Note: the PACKED_RRSET_NSEC_AT_APEX flag is not used.
|
||||
* since this is a referral, we need the NSEC at the parent
|
||||
* side of the zone cut, not the NSEC at apex side. */
|
||||
}
|
||||
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))) {
|
||||
msg->rep->ns_numrrsets++;
|
||||
msg->rep->rrset_count++;
|
||||
}
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
}
|
||||
}
|
||||
|
||||
/** 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* msg = (struct dns_msg*)regional_alloc(region,
|
||||
sizeof(struct dns_msg));
|
||||
if(!msg)
|
||||
return NULL;
|
||||
msg->qinfo.qname = regional_alloc_init(region, qname, qnamelen);
|
||||
if(!msg->qinfo.qname)
|
||||
return NULL;
|
||||
msg->qinfo.qname_len = qnamelen;
|
||||
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,
|
||||
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*));
|
||||
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;
|
||||
}
|
||||
|
||||
struct delegpt*
|
||||
dns_cache_find_delegation(struct module_env* env, uint8_t* qname,
|
||||
size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
struct regional* region, struct dns_msg** msg, uint32_t now)
|
||||
{
|
||||
/* try to find closest NS rrset */
|
||||
struct ub_packed_rrset_key* nskey;
|
||||
struct packed_rrset_data* nsdata;
|
||||
struct delegpt* dp;
|
||||
|
||||
nskey = find_closest_of_type(env, qname, qnamelen, qclass, now,
|
||||
LDNS_RR_TYPE_NS, 0);
|
||||
if(!nskey) /* hope the caller has hints to prime or something */
|
||||
return NULL;
|
||||
nsdata = (struct packed_rrset_data*)nskey->entry.data;
|
||||
/* got the NS key, create delegation point */
|
||||
dp = delegpt_create(region);
|
||||
if(!dp || !delegpt_set_name(dp, region, nskey->rk.dname)) {
|
||||
lock_rw_unlock(&nskey->entry.lock);
|
||||
log_err("find_delegation: out of memory");
|
||||
return NULL;
|
||||
}
|
||||
/* create referral message */
|
||||
if(msg) {
|
||||
*msg = create_msg(qname, qnamelen, qtype, qclass, region,
|
||||
nskey, nsdata, now);
|
||||
if(!*msg) {
|
||||
lock_rw_unlock(&nskey->entry.lock);
|
||||
log_err("find_delegation: out of memory");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if(!delegpt_rrset_add_ns(dp, region, nskey))
|
||||
log_err("find_delegation: addns out of memory");
|
||||
lock_rw_unlock(&nskey->entry.lock); /* first unlock before next lookup*/
|
||||
/* find and add DS/NSEC (if any) */
|
||||
if(msg)
|
||||
find_add_ds(env, region, *msg, dp, now);
|
||||
/* find and add A entries */
|
||||
if(!find_add_addrs(env, qclass, region, dp, now, msg))
|
||||
log_err("find_delegation: addrs out of memory");
|
||||
return dp;
|
||||
}
|
||||
|
||||
/** allocate dns_msg from query_info and reply_info */
|
||||
static struct dns_msg*
|
||||
gen_dns_msg(struct regional* region, struct query_info* q, size_t num)
|
||||
{
|
||||
struct dns_msg* msg = (struct dns_msg*)regional_alloc(region,
|
||||
sizeof(struct dns_msg));
|
||||
if(!msg)
|
||||
return NULL;
|
||||
memcpy(&msg->qinfo, q, sizeof(struct query_info));
|
||||
msg->qinfo.qname = regional_alloc_init(region, q->qname, q->qname_len);
|
||||
if(!msg->qinfo.qname)
|
||||
return NULL;
|
||||
/* allocate replyinfo struct and rrset key array separately */
|
||||
msg->rep = (struct reply_info*)regional_alloc(region,
|
||||
sizeof(struct reply_info) - sizeof(struct rrset_ref));
|
||||
if(!msg->rep)
|
||||
return NULL;
|
||||
msg->rep->rrsets = (struct ub_packed_rrset_key**)
|
||||
regional_alloc(region,
|
||||
num * sizeof(struct ub_packed_rrset_key*));
|
||||
if(!msg->rep->rrsets)
|
||||
return NULL;
|
||||
return msg;
|
||||
}
|
||||
|
||||
/** generate dns_msg from cached message */
|
||||
static struct dns_msg*
|
||||
tomsg(struct module_env* env, struct msgreply_entry* e, struct reply_info* r,
|
||||
struct regional* region, uint32_t now, struct regional* scratch)
|
||||
{
|
||||
struct dns_msg* msg;
|
||||
size_t i;
|
||||
if(now > r->ttl)
|
||||
return NULL;
|
||||
msg = gen_dns_msg(region, &e->key, r->rrset_count);
|
||||
if(!msg)
|
||||
return NULL;
|
||||
msg->rep->flags = r->flags;
|
||||
msg->rep->qdcount = r->qdcount;
|
||||
msg->rep->ttl = r->ttl;
|
||||
msg->rep->security = r->security;
|
||||
msg->rep->an_numrrsets = r->an_numrrsets;
|
||||
msg->rep->ns_numrrsets = r->ns_numrrsets;
|
||||
msg->rep->ar_numrrsets = r->ar_numrrsets;
|
||||
msg->rep->rrset_count = r->rrset_count;
|
||||
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);
|
||||
if(!msg->rep->rrsets[i]) {
|
||||
rrset_array_unlock(r->ref, r->rrset_count);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref,
|
||||
r->rrset_count);
|
||||
return msg;
|
||||
}
|
||||
|
||||
/** synthesize RRset-only response from cached RRset item */
|
||||
static struct dns_msg*
|
||||
rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region,
|
||||
uint32_t now, struct query_info* q)
|
||||
{
|
||||
struct dns_msg* msg;
|
||||
struct packed_rrset_data* d = (struct packed_rrset_data*)
|
||||
rrset->entry.data;
|
||||
if(now > d->ttl)
|
||||
return NULL;
|
||||
msg = gen_dns_msg(region, q, 1); /* only the CNAME (or other) RRset */
|
||||
if(!msg)
|
||||
return NULL;
|
||||
msg->rep->flags = BIT_QR; /* reply, no AA, no error */
|
||||
msg->rep->qdcount = 1;
|
||||
msg->rep->ttl = d->ttl - now;
|
||||
msg->rep->security = sec_status_unchecked;
|
||||
msg->rep->an_numrrsets = 1;
|
||||
msg->rep->ns_numrrsets = 0;
|
||||
msg->rep->ar_numrrsets = 0;
|
||||
msg->rep->rrset_count = 1;
|
||||
msg->rep->rrsets[0] = copy_rrset(rrset, region, now);
|
||||
if(!msg->rep->rrsets[0]) /* copy CNAME */
|
||||
return NULL;
|
||||
return msg;
|
||||
}
|
||||
|
||||
/** synthesize DNAME+CNAME response from cached DNAME item */
|
||||
static struct dns_msg*
|
||||
synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region,
|
||||
uint32_t now, struct query_info* q)
|
||||
{
|
||||
struct dns_msg* msg;
|
||||
struct ub_packed_rrset_key* ck;
|
||||
struct packed_rrset_data* newd, *d = (struct packed_rrset_data*)
|
||||
rrset->entry.data;
|
||||
uint8_t* newname, *dtarg = NULL;
|
||||
size_t newlen, dtarglen;
|
||||
if(now > d->ttl)
|
||||
return NULL;
|
||||
msg = gen_dns_msg(region, q, 2); /* DNAME + CNAME RRset */
|
||||
if(!msg)
|
||||
return NULL;
|
||||
msg->rep->flags = BIT_QR; /* reply, no AA, no error */
|
||||
msg->rep->qdcount = 1;
|
||||
msg->rep->ttl = d->ttl - now;
|
||||
msg->rep->security = sec_status_unchecked;
|
||||
msg->rep->an_numrrsets = 1;
|
||||
msg->rep->ns_numrrsets = 0;
|
||||
msg->rep->ar_numrrsets = 0;
|
||||
msg->rep->rrset_count = 1;
|
||||
msg->rep->rrsets[0] = copy_rrset(rrset, region, now);
|
||||
if(!msg->rep->rrsets[0]) /* copy DNAME */
|
||||
return NULL;
|
||||
/* synth CNAME rrset */
|
||||
get_cname_target(rrset, &dtarg, &dtarglen);
|
||||
if(!dtarg)
|
||||
return NULL;
|
||||
newlen = q->qname_len + dtarglen - rrset->rk.dname_len;
|
||||
if(newlen > LDNS_MAX_DOMAINLEN) {
|
||||
msg->rep->flags |= LDNS_RCODE_YXDOMAIN;
|
||||
return msg;
|
||||
}
|
||||
newname = (uint8_t*)regional_alloc(region, newlen);
|
||||
if(!newname)
|
||||
return NULL;
|
||||
/* new name is concatenation of qname front (without DNAME owner)
|
||||
* and DNAME target name */
|
||||
memcpy(newname, q->qname, q->qname_len-rrset->rk.dname_len);
|
||||
memmove(newname+(q->qname_len-rrset->rk.dname_len), dtarg, dtarglen);
|
||||
/* create rest of CNAME rrset */
|
||||
ck = (struct ub_packed_rrset_key*)regional_alloc(region,
|
||||
sizeof(struct ub_packed_rrset_key));
|
||||
if(!ck)
|
||||
return NULL;
|
||||
memset(&ck->entry, 0, sizeof(ck->entry));
|
||||
msg->rep->rrsets[1] = ck;
|
||||
ck->entry.key = ck;
|
||||
ck->rk.type = htons(LDNS_RR_TYPE_CNAME);
|
||||
ck->rk.rrset_class = rrset->rk.rrset_class;
|
||||
ck->rk.flags = 0;
|
||||
ck->rk.dname = regional_alloc_init(region, q->qname, q->qname_len);
|
||||
if(!ck->rk.dname)
|
||||
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,
|
||||
sizeof(struct packed_rrset_data) + sizeof(size_t) +
|
||||
sizeof(uint8_t*) + sizeof(uint32_t) + sizeof(uint16_t)
|
||||
+ newlen);
|
||||
if(!newd)
|
||||
return NULL;
|
||||
ck->entry.data = newd;
|
||||
newd->ttl = 0; /* 0 for synthesized CNAME TTL */
|
||||
newd->count = 1;
|
||||
newd->rrsig_count = 0;
|
||||
newd->trust = rrset_trust_ans_noAA;
|
||||
newd->rr_len = (size_t*)((uint8_t*)newd +
|
||||
sizeof(struct packed_rrset_data));
|
||||
newd->rr_len[0] = newlen + sizeof(uint16_t);
|
||||
packed_rrset_ptr_fixup(newd);
|
||||
newd->rr_ttl[0] = newd->ttl;
|
||||
msg->rep->ttl = newd->ttl;
|
||||
ldns_write_uint16(newd->rr_data[0], newlen);
|
||||
memmove(newd->rr_data[0] + sizeof(uint16_t), newname, newlen);
|
||||
msg->rep->an_numrrsets ++;
|
||||
msg->rep->rrset_count ++;
|
||||
return msg;
|
||||
}
|
||||
|
||||
struct dns_msg*
|
||||
dns_cache_lookup(struct module_env* env,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
struct regional* region, struct regional* scratch)
|
||||
{
|
||||
struct lruhash_entry* e;
|
||||
struct query_info k;
|
||||
hashvalue_t h;
|
||||
uint32_t now = (uint32_t)time(NULL);
|
||||
struct ub_packed_rrset_key* rrset;
|
||||
|
||||
/* lookup first, this has both NXdomains and ANSWER responses */
|
||||
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, 0);
|
||||
if(e) {
|
||||
struct msgreply_entry* key = (struct msgreply_entry*)e->key;
|
||||
struct reply_info* data = (struct reply_info*)e->data;
|
||||
struct dns_msg* msg = tomsg(env, key, data, region, now,
|
||||
scratch);
|
||||
if(msg) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return msg;
|
||||
}
|
||||
/* could be msg==NULL; due to TTL or not all rrsets available */
|
||||
lock_rw_unlock(&e->lock);
|
||||
}
|
||||
|
||||
/* see if a DNAME exists. Checked for first, to enforce that DNAMEs
|
||||
* are more important, the CNAME is resynthesized and thus
|
||||
* consistent with the DNAME */
|
||||
if( (rrset=find_closest_of_type(env, qname, qnamelen, qclass, now,
|
||||
LDNS_RR_TYPE_DNAME, 1))) {
|
||||
/* synthesize a DNAME+CNAME message based on this */
|
||||
struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k);
|
||||
if(msg) {
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
return msg;
|
||||
}
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
}
|
||||
|
||||
/* see if we have CNAME for this domain */
|
||||
if( (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen,
|
||||
LDNS_RR_TYPE_CNAME, qclass, 0, now, 0))) {
|
||||
struct dns_msg* msg = rrset_msg(rrset, region, now, &k);
|
||||
if(msg) {
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
return msg;
|
||||
}
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
}
|
||||
|
||||
/* construct DS, DNSKEY messages from rrset cache. */
|
||||
if((qtype == LDNS_RR_TYPE_DS || qtype == LDNS_RR_TYPE_DNSKEY) &&
|
||||
(rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen,
|
||||
qtype, qclass, 0, now, 0))) {
|
||||
/* if the rrset is from the additional section, and the
|
||||
* signatures have fallen off, then do not synthesize a msg
|
||||
* instead, allow a full query for signed results to happen.
|
||||
* Forego all rrset data from additional section, because
|
||||
* some signatures may not be present and cause validation
|
||||
* failure.
|
||||
*/
|
||||
struct packed_rrset_data *d = (struct packed_rrset_data*)
|
||||
rrset->entry.data;
|
||||
if(d->trust != rrset_trust_add_noAA &&
|
||||
d->trust != rrset_trust_add_AA) {
|
||||
struct dns_msg* msg = rrset_msg(rrset, region, now, &k);
|
||||
if(msg) {
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
dns_cache_store(struct module_env* env, struct query_info* msgqinf,
|
||||
struct reply_info* msgrep, int is_referral)
|
||||
{
|
||||
struct reply_info* rep = NULL;
|
||||
/* alloc, malloc properly (not in region, like msg is) */
|
||||
rep = reply_info_copy(msgrep, env->alloc, NULL);
|
||||
if(!rep)
|
||||
return 0;
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
free(rep);
|
||||
return 1;
|
||||
} else {
|
||||
/* store msg, and rrsets */
|
||||
struct query_info qinf;
|
||||
hashvalue_t h;
|
||||
|
||||
qinf = *msgqinf;
|
||||
qinf.qname = memdup(msgqinf->qname, msgqinf->qname_len);
|
||||
if(!qinf.qname) {
|
||||
reply_info_parsedelete(rep, env->alloc);
|
||||
return 0;
|
||||
}
|
||||
/* fixup flags to be sensible for a reply based on the cache */
|
||||
/* this module means that RA is available. It is an answer QR.
|
||||
* Not AA from cache. Not CD in cache (depends on client bit). */
|
||||
rep->flags |= (BIT_RA | BIT_QR);
|
||||
rep->flags &= ~(BIT_AA | BIT_CD);
|
||||
h = query_info_hash(&qinf);
|
||||
dns_cache_store_msg(env, &qinf, h, rep);
|
||||
/* qname is used inside query_info_entrysetup, and set to
|
||||
* NULL. If it has not been used, free it. free(0) is safe. */
|
||||
free(qinf.qname);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
Vendored
+140
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* services/cache/dns.h - Cache services for DNS using msg and rrset caches.
|
||||
*
|
||||
* 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 DNS cache.
|
||||
*/
|
||||
|
||||
#ifndef SERVICES_CACHE_DNS_H
|
||||
#define SERVICES_CACHE_DNS_H
|
||||
#include "util/storage/lruhash.h"
|
||||
#include "util/data/msgreply.h"
|
||||
struct module_env;
|
||||
struct query_info;
|
||||
struct reply_info;
|
||||
struct regional;
|
||||
struct delegpt;
|
||||
|
||||
/**
|
||||
* Region allocated message reply
|
||||
*/
|
||||
struct dns_msg {
|
||||
/** query info */
|
||||
struct query_info qinfo;
|
||||
/** reply info - ptr to packed repinfo structure */
|
||||
struct reply_info *rep;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocate a dns_msg with malloc/alloc structure and store in dns cache.
|
||||
*
|
||||
* @param env: environment, with alloc structure and dns cache.
|
||||
* @param qinf: query info, the query for which answer is stored.
|
||||
* this is allocated in a region, and will be copied to malloc area
|
||||
* before insertion.
|
||||
* @param rep: reply in dns_msg from dns_alloc_msg for example.
|
||||
* this is allocated in a region, and will be copied to malloc area
|
||||
* before insertion.
|
||||
* @param is_referral: If true, then the given message to be stored is a
|
||||
* referral. The cache implementation may use this as a hint.
|
||||
* It will store only the RRsets, not the message.
|
||||
* @return 0 on alloc error (out of memory).
|
||||
*/
|
||||
int dns_cache_store(struct module_env* env, struct query_info* qinf,
|
||||
struct reply_info* rep, int is_referral);
|
||||
|
||||
/**
|
||||
* Store message in the cache. Stores in message cache and rrset cache.
|
||||
* Both qinfo and rep should be malloced and are put in the cache.
|
||||
* They should not be used after this call, as they are then in shared cache.
|
||||
* Does not return errors, they are logged and only lead to less cache.
|
||||
*
|
||||
* @param env: module environment with the DNS cache.
|
||||
* @param qinfo: query info
|
||||
* @param hash: hash over qinfo.
|
||||
* @param rep: reply info, together with qinfo makes up the message.
|
||||
* Adjusts the reply info TTLs to absolute time.
|
||||
*/
|
||||
void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
|
||||
hashvalue_t hash, struct reply_info* rep);
|
||||
|
||||
/**
|
||||
* Find a delegation from the cache.
|
||||
* @param env: module environment with the DNS cache.
|
||||
* @param qname: query name.
|
||||
* @param qnamelen: length of qname.
|
||||
* @param qtype: query type.
|
||||
* @param qclass: query class.
|
||||
* @param region: where to allocate result delegation.
|
||||
* @param msg: if not NULL, delegation message is returned here, synthesized
|
||||
* from the cache.
|
||||
* @param timenow: the time now, for checking if TTL on cache entries is OK.
|
||||
* @return new delegation or NULL on error or if not found in cache.
|
||||
*/
|
||||
struct delegpt* dns_cache_find_delegation(struct module_env* env,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
struct regional* region, struct dns_msg** msg, uint32_t timenow);
|
||||
|
||||
/**
|
||||
* Find cached message
|
||||
* @param env: module environment with the DNS cache.
|
||||
* @param qname: query name.
|
||||
* @param qnamelen: length of qname.
|
||||
* @param qtype: query type.
|
||||
* @param qclass: query class.
|
||||
* @param region: where to allocate result.
|
||||
* @param scratch: where to allocate temporary data.
|
||||
* @return new response message (alloced in region, rrsets do not have IDs).
|
||||
* or NULL on error or if not found in cache.
|
||||
* TTLs are made relative to the current time.
|
||||
*/
|
||||
struct dns_msg* dns_cache_lookup(struct module_env* env,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
struct regional* region, struct regional* scratch);
|
||||
|
||||
/**
|
||||
* find and add A and AAAA records for missing nameservers in delegpt
|
||||
* @param env: module environment with rrset cache
|
||||
* @param qclass: which class to look in.
|
||||
* @param region: where to store new dp info.
|
||||
* @param dp: delegation point to fill missing entries.
|
||||
* @return false on alloc failure.
|
||||
*/
|
||||
int cache_fill_missing(struct module_env* env, uint16_t qclass,
|
||||
struct regional* region, struct delegpt* dp);
|
||||
|
||||
#endif /* SERVICES_CACHE_DNS_H */
|
||||
Vendored
+548
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
* services/cache/infra.c - infrastructure cache, server rtt and capabilities
|
||||
*
|
||||
* 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 infrastructure cache.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
#include "util/storage/lookup3.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/config_file.h"
|
||||
|
||||
size_t
|
||||
infra_host_sizefunc(void* k, void* ATTR_UNUSED(d))
|
||||
{
|
||||
struct infra_host_key* key = (struct infra_host_key*)k;
|
||||
return sizeof(*key) + sizeof(struct infra_host_data)
|
||||
+ lock_get_mem(&key->entry.lock);
|
||||
}
|
||||
|
||||
int
|
||||
infra_host_compfunc(void* key1, void* key2)
|
||||
{
|
||||
struct infra_host_key* k1 = (struct infra_host_key*)key1;
|
||||
struct infra_host_key* k2 = (struct infra_host_key*)key2;
|
||||
return sockaddr_cmp(&k1->addr, k1->addrlen, &k2->addr, k2->addrlen);
|
||||
}
|
||||
|
||||
void
|
||||
infra_host_delkeyfunc(void* k, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct infra_host_key* key = (struct infra_host_key*)k;
|
||||
if(!key)
|
||||
return;
|
||||
lock_rw_destroy(&key->entry.lock);
|
||||
free(key);
|
||||
}
|
||||
|
||||
void
|
||||
infra_host_deldatafunc(void* d, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct infra_host_data* data = (struct infra_host_data*)d;
|
||||
lruhash_delete(data->lameness);
|
||||
free(data);
|
||||
}
|
||||
|
||||
struct infra_cache*
|
||||
infra_create(struct config_file* cfg)
|
||||
{
|
||||
struct infra_cache* infra = (struct infra_cache*)calloc(1,
|
||||
sizeof(struct infra_cache));
|
||||
/* the size of the lameness tables are not counted */
|
||||
size_t maxmem = cfg->infra_cache_numhosts *
|
||||
(sizeof(struct infra_host_key)+sizeof(struct infra_host_data));
|
||||
infra->hosts = slabhash_create(cfg->infra_cache_slabs,
|
||||
INFRA_HOST_STARTSIZE, maxmem, &infra_host_sizefunc,
|
||||
&infra_host_compfunc, &infra_host_delkeyfunc,
|
||||
&infra_host_deldatafunc, NULL);
|
||||
if(!infra->hosts) {
|
||||
free(infra);
|
||||
return NULL;
|
||||
}
|
||||
infra->host_ttl = cfg->host_ttl;
|
||||
infra->lame_ttl = cfg->lame_ttl;
|
||||
infra->max_lame_size = cfg->infra_cache_lame_size;
|
||||
return infra;
|
||||
}
|
||||
|
||||
void
|
||||
infra_delete(struct infra_cache* infra)
|
||||
{
|
||||
if(!infra)
|
||||
return;
|
||||
slabhash_delete(infra->hosts);
|
||||
free(infra);
|
||||
}
|
||||
|
||||
struct infra_cache*
|
||||
infra_adjust(struct infra_cache* infra, struct config_file* cfg)
|
||||
{
|
||||
size_t maxmem;
|
||||
if(!infra)
|
||||
return infra_create(cfg);
|
||||
infra->host_ttl = cfg->host_ttl;
|
||||
infra->lame_ttl = cfg->lame_ttl;
|
||||
infra->max_lame_size = cfg->infra_cache_lame_size;
|
||||
maxmem = cfg->infra_cache_numhosts *
|
||||
(sizeof(struct infra_host_key)+sizeof(struct infra_host_data));
|
||||
if(maxmem != slabhash_get_size(infra->hosts) ||
|
||||
cfg->infra_cache_slabs != infra->hosts->size) {
|
||||
infra_delete(infra);
|
||||
infra = infra_create(cfg);
|
||||
}
|
||||
return infra;
|
||||
}
|
||||
|
||||
/** calculate the hash value for a host key */
|
||||
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);
|
||||
return h;
|
||||
}
|
||||
|
||||
/** lookup version that does not check host ttl (you check it) */
|
||||
static struct lruhash_entry*
|
||||
infra_lookup_host_nottl(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int wr)
|
||||
{
|
||||
struct infra_host_key k;
|
||||
k.addrlen = addrlen;
|
||||
memcpy(&k.addr, addr, addrlen);
|
||||
k.entry.hash = hash_addr(addr, addrlen);
|
||||
k.entry.key = (void*)&k;
|
||||
k.entry.data = NULL;
|
||||
return slabhash_lookup(infra->hosts, k.entry.hash, &k, wr);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct infra_host_data* data;
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, wr);
|
||||
*key = NULL;
|
||||
if(!e)
|
||||
return NULL;
|
||||
/* check TTL */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
if(data->ttl < timenow) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return NULL;
|
||||
}
|
||||
*key = (struct infra_host_key*)e->key;
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and init a new entry for a host
|
||||
* @param infra: infra structure with config parameters.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
* @param tm: time now.
|
||||
* @return: the new entry or NULL on malloc failure.
|
||||
*/
|
||||
static struct lruhash_entry*
|
||||
new_host_entry(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, time_t tm)
|
||||
{
|
||||
struct infra_host_data* data;
|
||||
struct infra_host_key* key = (struct infra_host_key*)malloc(
|
||||
sizeof(struct infra_host_key));
|
||||
if(!key)
|
||||
return NULL;
|
||||
data = (struct infra_host_data*)malloc(
|
||||
sizeof(struct infra_host_data));
|
||||
if(!data) {
|
||||
free(key);
|
||||
return NULL;
|
||||
}
|
||||
lock_rw_init(&key->entry.lock);
|
||||
key->entry.hash = hash_addr(addr, addrlen);
|
||||
key->entry.key = (void*)key;
|
||||
key->entry.data = (void*)data;
|
||||
key->addrlen = addrlen;
|
||||
memcpy(&key->addr, addr, addrlen);
|
||||
data->ttl = tm + infra->host_ttl;
|
||||
data->lameness = NULL;
|
||||
data->edns_version = 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)
|
||||
{
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, 0);
|
||||
struct infra_host_data* data;
|
||||
if(e && ((struct infra_host_data*)e->data)->ttl < timenow) {
|
||||
/* it expired, try to reuse existing entry */
|
||||
lock_rw_unlock(&e->lock);
|
||||
e = infra_lookup_host_nottl(infra, addr, addrlen, 1);
|
||||
if(e) {
|
||||
/* if its still there we have a writelock, init */
|
||||
/* re-initialise */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
data->ttl = timenow + infra->host_ttl;
|
||||
rtt_init(&data->rtt);
|
||||
/* do not touch lameness, it may be valid still */
|
||||
data->edns_version = 0;
|
||||
}
|
||||
}
|
||||
if(!e) {
|
||||
/* insert new entry */
|
||||
if(!(e = new_host_entry(infra, addr, addrlen, timenow)))
|
||||
return 0;
|
||||
data = (struct infra_host_data*)e->data;
|
||||
*to = rtt_timeout(&data->rtt);
|
||||
*edns_vs = data->edns_version;
|
||||
slabhash_insert(infra->hosts, e->hash, e, data, NULL);
|
||||
return 1;
|
||||
}
|
||||
/* use existing entry */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
*to = rtt_timeout(&data->rtt);
|
||||
*edns_vs = data->edns_version;
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** hash lameness key */
|
||||
static hashvalue_t
|
||||
hash_lameness(uint8_t* name, size_t namelen)
|
||||
{
|
||||
return hashlittle(name, namelen, 0xab);
|
||||
}
|
||||
|
||||
int
|
||||
infra_lookup_lame(struct infra_host_data* host,
|
||||
uint8_t* name, size_t namelen, time_t timenow)
|
||||
{
|
||||
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.zonename = name;
|
||||
k.namelen = namelen;
|
||||
k.entry.key = (void*)&k;
|
||||
k.entry.data = NULL;
|
||||
e = lruhash_lookup(host->lameness, k.entry.hash, &k, 0);
|
||||
if(!e)
|
||||
return 0;
|
||||
d = (struct infra_lame_data*)e->data;
|
||||
if(d->ttl < timenow) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 0;
|
||||
}
|
||||
dl = d->isdnsseclame;
|
||||
lock_rw_unlock(&e->lock);
|
||||
return dl?2:1;
|
||||
}
|
||||
|
||||
size_t
|
||||
infra_lame_sizefunc(void* k, void* ATTR_UNUSED(d))
|
||||
{
|
||||
struct infra_lame_key* key = (struct infra_lame_key*)k;
|
||||
return sizeof(*key) + sizeof(struct infra_lame_data)
|
||||
+ key->namelen + lock_get_mem(&key->entry.lock);
|
||||
}
|
||||
|
||||
int
|
||||
infra_lame_compfunc(void* key1, void* key2)
|
||||
{
|
||||
struct infra_lame_key* k1 = (struct infra_lame_key*)key1;
|
||||
struct infra_lame_key* k2 = (struct infra_lame_key*)key2;
|
||||
if(k1->namelen != k2->namelen) {
|
||||
if(k1->namelen < k2->namelen)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
return query_dname_compare(k1->zonename, k2->zonename);
|
||||
}
|
||||
|
||||
void
|
||||
infra_lame_delkeyfunc(void* k, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct infra_lame_key* key = (struct infra_lame_key*)k;
|
||||
if(!key)
|
||||
return;
|
||||
lock_rw_destroy(&key->entry.lock);
|
||||
free(key->zonename);
|
||||
free(key);
|
||||
}
|
||||
|
||||
void
|
||||
infra_lame_deldatafunc(void* d, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
if(!d)
|
||||
return;
|
||||
free(d);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct infra_host_data* data;
|
||||
struct lruhash_entry* e;
|
||||
int needtoinsert = 0;
|
||||
struct infra_lame_key* k;
|
||||
struct infra_lame_data* d;
|
||||
/* allocate at start, easier cleanup (no locks held) */
|
||||
k = (struct infra_lame_key*)malloc(sizeof(*k));
|
||||
if(!k) {
|
||||
log_err("set_lame: malloc failure");
|
||||
return 0;
|
||||
}
|
||||
d = (struct infra_lame_data*)malloc(sizeof(*d));
|
||||
if(!d) {
|
||||
free(k);
|
||||
log_err("set_lame: malloc failure");
|
||||
return 0;
|
||||
}
|
||||
k->zonename = memdup(name, namelen);
|
||||
if(!k->zonename) {
|
||||
free(d);
|
||||
free(k);
|
||||
log_err("set_lame: malloc failure");
|
||||
return 0;
|
||||
}
|
||||
lock_rw_init(&k->entry.lock);
|
||||
k->entry.hash = hash_lameness(name, namelen);
|
||||
k->entry.key = (void*)k;
|
||||
k->entry.data = (void*)d;
|
||||
d->ttl = timenow + infra->lame_ttl;
|
||||
d->isdnsseclame = dnsseclame;
|
||||
k->namelen = namelen;
|
||||
e = infra_lookup_host_nottl(infra, addr, addrlen, 1);
|
||||
if(!e) {
|
||||
/* insert it */
|
||||
if(!(e = new_host_entry(infra, addr, addrlen, timenow))) {
|
||||
free(k->zonename);
|
||||
free(k);
|
||||
free(d);
|
||||
log_err("set_lame: malloc failure");
|
||||
return 0;
|
||||
}
|
||||
needtoinsert = 1;
|
||||
}
|
||||
/* got an entry, now set the zone lame */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
if(!data->lameness) {
|
||||
/* create hash table if not there already */
|
||||
data->lameness = lruhash_create(INFRA_LAME_STARTSIZE,
|
||||
infra->max_lame_size, infra_lame_sizefunc,
|
||||
infra_lame_compfunc, infra_lame_delkeyfunc,
|
||||
infra_lame_deldatafunc, NULL);
|
||||
if(!data->lameness) {
|
||||
log_err("set_lame: malloc failure");
|
||||
if(needtoinsert) slabhash_insert(infra->hosts,
|
||||
e->hash, e, e->data, NULL);
|
||||
else { lock_rw_unlock(&e->lock); }
|
||||
free(k->zonename);
|
||||
free(k);
|
||||
free(d);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* inserts new entry, or updates TTL of older entry */
|
||||
lruhash_insert(data->lameness, k->entry.hash, &k->entry, d, NULL);
|
||||
|
||||
if(needtoinsert)
|
||||
slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
|
||||
else { lock_rw_unlock(&e->lock); }
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
infra_update_tcp_works(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen)
|
||||
{
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, 1);
|
||||
struct infra_host_data* data;
|
||||
if(!e)
|
||||
return; /* doesn't exist */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
if(data->rtt.rto >= RTT_MAX_TIMEOUT)
|
||||
/* do not disqualify this server altogether, it is better
|
||||
* than nothing */
|
||||
data->rtt.rto = RTT_MAX_TIMEOUT-1;
|
||||
lock_rw_unlock(&e->lock);
|
||||
}
|
||||
|
||||
int
|
||||
infra_rtt_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int roundtrip, time_t timenow)
|
||||
{
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, 1);
|
||||
struct infra_host_data* data;
|
||||
int needtoinsert = 0;
|
||||
int rto = 1;
|
||||
if(!e) {
|
||||
if(!(e = new_host_entry(infra, addr, addrlen, timenow)))
|
||||
return 0;
|
||||
needtoinsert = 1;
|
||||
}
|
||||
/* have an entry, update the rtt, and the ttl */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
data->ttl = timenow + infra->host_ttl;
|
||||
if(roundtrip == -1)
|
||||
rtt_lost(&data->rtt);
|
||||
else rtt_update(&data->rtt, roundtrip);
|
||||
if(data->rtt.rto > 0)
|
||||
rto = data->rtt.rto;
|
||||
|
||||
if(needtoinsert)
|
||||
slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
|
||||
else { lock_rw_unlock(&e->lock); }
|
||||
return rto;
|
||||
}
|
||||
|
||||
int
|
||||
infra_edns_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int edns_version, time_t timenow)
|
||||
{
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, 1);
|
||||
struct infra_host_data* data;
|
||||
int needtoinsert = 0;
|
||||
if(!e) {
|
||||
if(!(e = new_host_entry(infra, addr, addrlen, timenow)))
|
||||
return 0;
|
||||
needtoinsert = 1;
|
||||
}
|
||||
/* have an entry, update the rtt, and the ttl */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
data->ttl = timenow + infra->host_ttl;
|
||||
data->edns_version = edns_version;
|
||||
|
||||
if(needtoinsert)
|
||||
slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
|
||||
else { lock_rw_unlock(&e->lock); }
|
||||
return 1;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct infra_host_data* host;
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
addrlen, 0);
|
||||
int lm;
|
||||
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) {
|
||||
*lame = 1;
|
||||
*dnsseclame = 0;
|
||||
} else {
|
||||
*lame = 0;
|
||||
*dnsseclame = 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
*lame = 0;
|
||||
*dnsseclame = 0;
|
||||
if(timenow > host->ttl) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 0;
|
||||
}
|
||||
lock_rw_unlock(&e->lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** helper memory count for a host lame cache */
|
||||
static size_t
|
||||
count_host_lame(struct lruhash_entry* e)
|
||||
{
|
||||
struct infra_host_data* host_data = (struct infra_host_data*)e->data;
|
||||
if(!host_data->lameness)
|
||||
return 0;
|
||||
return lruhash_get_mem(host_data->lameness);
|
||||
}
|
||||
|
||||
size_t
|
||||
infra_get_mem(struct infra_cache* infra)
|
||||
{
|
||||
size_t i, bin;
|
||||
size_t s = sizeof(*infra) +
|
||||
slabhash_get_mem(infra->hosts);
|
||||
struct lruhash_entry* e;
|
||||
for(i=0; i<infra->hosts->size; i++) {
|
||||
lock_quick_lock(&infra->hosts->array[i]->lock);
|
||||
for(bin=0; bin<infra->hosts->array[i]->size; bin++) {
|
||||
lock_quick_lock(&infra->hosts->array[i]->
|
||||
array[bin].lock);
|
||||
/* count data size in bin items. */
|
||||
for(e = infra->hosts->array[i]->array[bin].
|
||||
overflow_list; e; e = e->overflow_next) {
|
||||
lock_rw_rdlock(&e->lock);
|
||||
s += count_host_lame(e);
|
||||
lock_rw_unlock(&e->lock);
|
||||
}
|
||||
lock_quick_unlock(&infra->hosts->array[i]->
|
||||
array[bin].lock);
|
||||
}
|
||||
lock_quick_unlock(&infra->hosts->array[i]->lock);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
Vendored
+288
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* services/cache/infra.h - infrastructure cache, server rtt and capabilities
|
||||
*
|
||||
* 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 infrastructure cache.
|
||||
*/
|
||||
|
||||
#ifndef SERVICES_CACHE_INFRA_H
|
||||
#define SERVICES_CACHE_INFRA_H
|
||||
#include "util/storage/lruhash.h"
|
||||
#include "util/rtt.h"
|
||||
struct slabhash;
|
||||
struct config_file;
|
||||
|
||||
/**
|
||||
* Host information kept for every server.
|
||||
*/
|
||||
struct infra_host_key {
|
||||
/** the host address. */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr. */
|
||||
socklen_t addrlen;
|
||||
/** hash table entry, data of type infra_host_data. */
|
||||
struct lruhash_entry entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* Host information encompasses host capabilities and retransmission timeouts.
|
||||
*/
|
||||
struct infra_host_data {
|
||||
/** TTL value for this entry. absolute time. */
|
||||
time_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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Lameness information, per host, per zone.
|
||||
*/
|
||||
struct infra_lame_key {
|
||||
/** key is zone name in wireformat */
|
||||
uint8_t* zonename;
|
||||
/** length of zonename */
|
||||
size_t namelen;
|
||||
/** lruhash entry */
|
||||
struct lruhash_entry entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* Lameness information. Expires.
|
||||
* This host is lame because it is in the cache.
|
||||
*/
|
||||
struct infra_lame_data {
|
||||
/** TTL of this entry. absolute time. */
|
||||
time_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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Infra cache
|
||||
*/
|
||||
struct infra_cache {
|
||||
/** The hash table with hosts */
|
||||
struct slabhash* hosts;
|
||||
/** TTL value for host information, in seconds */
|
||||
int host_ttl;
|
||||
/** TTL for Lameness information, in seconds */
|
||||
int lame_ttl;
|
||||
/** infra lame cache max memory per host, in bytes */
|
||||
size_t max_lame_size;
|
||||
};
|
||||
|
||||
/** infra host cache default hash lookup size */
|
||||
#define INFRA_HOST_STARTSIZE 32
|
||||
/** infra lame cache default hash lookup size */
|
||||
#define INFRA_LAME_STARTSIZE 2
|
||||
|
||||
/**
|
||||
* Create infra cache.
|
||||
* @param cfg: config parameters or NULL for defaults.
|
||||
* @return: new infra cache, or NULL.
|
||||
*/
|
||||
struct infra_cache* infra_create(struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Delete infra cache.
|
||||
* @param infra: infrastructure cache to delete.
|
||||
*/
|
||||
void infra_delete(struct infra_cache* infra);
|
||||
|
||||
/**
|
||||
* Adjust infra cache to use updated configuration settings.
|
||||
* This may clean the cache. Operates a bit like realloc.
|
||||
* There may be no threading or use by other threads.
|
||||
* @param infra: existing cache. If NULL a new infra cache is returned.
|
||||
* @param cfg: config options.
|
||||
* @return the new infra cache pointer or NULL on error.
|
||||
*/
|
||||
struct infra_cache* infra_adjust(struct infra_cache* infra,
|
||||
struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Lookup host data
|
||||
* @param infra: infrastructure cache.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
* @param wr: set to true to get a writelock on the entry.
|
||||
* @param timenow: what time it is now.
|
||||
* @param key: the key for the host, returned so caller can unlock when done.
|
||||
* @return: host data or NULL if not found or expired.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Find host information to send a packet. Creates new entry if not found.
|
||||
* Lameness is empty. EDNS is 0 (try with first), and rtt is returned for
|
||||
* the first message to it.
|
||||
* @param infra: infrastructure cache.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
* @param timenow: what time it is now.
|
||||
* @param edns_vs: edns version it supports, 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);
|
||||
|
||||
/**
|
||||
* Check for lameness of this server for a particular zone.
|
||||
* You must have a lock on the host structure.
|
||||
* @param host: infrastructure cache data for the host. Caller holds lock.
|
||||
* @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.
|
||||
*/
|
||||
int infra_lookup_lame(struct infra_host_data* host,
|
||||
uint8_t* name, size_t namelen, time_t timenow);
|
||||
|
||||
/**
|
||||
* Set a host to be lame for the given zone.
|
||||
* @param infra: infrastructure cache.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
* @param name: domain name of zone apex.
|
||||
* @param namelen: length of domain name.
|
||||
* @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).
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Update rtt information for the host.
|
||||
* @param infra: infrastructure cache.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
* @param roundtrip: estimate of roundtrip time in milliseconds or -1 for
|
||||
* timeout.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Update information for the host, store that a TCP transaction works.
|
||||
* @param infra: infrastructure cache.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
*/
|
||||
void infra_update_tcp_works(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen);
|
||||
|
||||
/**
|
||||
* Update edns information for the host.
|
||||
* @param infra: infrastructure cache.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
* @param edns_version: the version that it publishes.
|
||||
* @param timenow: what time it is now.
|
||||
* @return: 0 on error.
|
||||
*/
|
||||
int infra_edns_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int edns_version, time_t timenow);
|
||||
|
||||
/**
|
||||
* Get Lameness information and average RTT if host is in the cache.
|
||||
* @param infra: infrastructure cache.
|
||||
* @param addr: host address.
|
||||
* @param addrlen: length of addr.
|
||||
* @param name: zone name.
|
||||
* @param namelen: zone name length.
|
||||
* @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 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.
|
||||
* @return if found in cache, or false if not (or TTL bad).
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Get memory used by the infra cache.
|
||||
* @param infra: infrastructure cache.
|
||||
* @return memory in use in bytes.
|
||||
*/
|
||||
size_t infra_get_mem(struct infra_cache* infra);
|
||||
|
||||
/** calculate size for the hashtable, does not count size of lameness,
|
||||
* so the hashtable is a fixed number of items */
|
||||
size_t infra_host_sizefunc(void* k, void* d);
|
||||
|
||||
/** compare two addresses, returns -1, 0, or +1 */
|
||||
int infra_host_compfunc(void* key1, void* key2);
|
||||
|
||||
/** delete key, and destroy the lock */
|
||||
void infra_host_delkeyfunc(void* k, void* arg);
|
||||
|
||||
/** delete data and destroy the lameness hashtable */
|
||||
void infra_host_deldatafunc(void* d, void* arg);
|
||||
|
||||
/** calculate size, which is fixed, zonename does not count so that
|
||||
* a fixed number of items is stored */
|
||||
size_t infra_lame_sizefunc(void* k, void* d);
|
||||
|
||||
/** compare zone names, returns -1, 0, +1 */
|
||||
int infra_lame_compfunc(void* key1, void* key2);
|
||||
|
||||
/** free key, lock and zonename */
|
||||
void infra_lame_delkeyfunc(void* k, void* arg);
|
||||
|
||||
/** free the lameness data */
|
||||
void infra_lame_deldatafunc(void* d, void* arg);
|
||||
|
||||
#endif /* SERVICES_CACHE_INFRA_H */
|
||||
Vendored
+368
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* services/cache/rrset.c - Resource record set cache.
|
||||
*
|
||||
* 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 rrset cache.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#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)
|
||||
{
|
||||
size_t slabs = (cfg?cfg->rrset_cache_slabs:HASH_DEFAULT_SLABS);
|
||||
size_t startarray = HASH_DEFAULT_STARTARRAY;
|
||||
size_t maxmem = (cfg?cfg->rrset_cache_size:HASH_DEFAULT_MAXMEM);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
struct config_file* cfg, struct alloc_cache* alloc)
|
||||
{
|
||||
if(!r || !cfg || cfg->rrset_cache_slabs != r->table.size ||
|
||||
cfg->rrset_cache_size != slabhash_get_size(&r->table))
|
||||
{
|
||||
rrset_cache_delete(r);
|
||||
r = rrset_cache_create(cfg, alloc);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key,
|
||||
hashvalue_t hash, rrset_id_t id)
|
||||
{
|
||||
struct lruhash* table = slabhash_gettable(&r->table, hash);
|
||||
/*
|
||||
* This leads to locking problems, deadlocks, if the caller is
|
||||
* holding any other rrset lock.
|
||||
* Because a lookup through the hashtable does:
|
||||
* tablelock -> entrylock (for that entry caller holds)
|
||||
* And this would do
|
||||
* entrylock(already held) -> tablelock
|
||||
* And if two threads do this, it results in deadlock.
|
||||
* So, the caller must not hold entrylock.
|
||||
*/
|
||||
lock_quick_lock(&table->lock);
|
||||
/* we have locked the hash table, the item can still be deleted.
|
||||
* because it could already have been reclaimed, but not yet set id=0.
|
||||
* This is because some lruhash routines have lazy deletion.
|
||||
* so, we must acquire a lock on the item to verify the id != 0.
|
||||
* also, with hash not changed, we are using the right slab.
|
||||
*/
|
||||
lock_rw_rdlock(&key->entry.lock);
|
||||
if(key->id == id && key->entry.hash == hash) {
|
||||
lru_touch(table, &key->entry);
|
||||
}
|
||||
lock_rw_unlock(&key->entry.lock);
|
||||
lock_quick_unlock(&table->lock);
|
||||
}
|
||||
|
||||
/** see if rrset needs to be updated in the cache */
|
||||
static int
|
||||
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) {
|
||||
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,
|
||||
* do not update the TTL - let it expire. */
|
||||
if(equal && cached->ttl >= timenow &&
|
||||
cached->security == sec_status_bogus)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
/* o item in cache has expired */
|
||||
if( cached->ttl < timenow )
|
||||
return 1;
|
||||
/* o same trust, but different in data - insert it */
|
||||
if( newd->trust == cached->trust && !equal )
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Update RRSet special key ID */
|
||||
static void
|
||||
rrset_update_id(struct rrset_ref* ref, struct alloc_cache* alloc)
|
||||
{
|
||||
/* this may clear the cache and invalidate lock below */
|
||||
uint64_t newid = alloc_get_id(alloc);
|
||||
/* obtain writelock */
|
||||
lock_rw_wrlock(&ref->key->entry.lock);
|
||||
/* check if it was deleted in the meantime, if so, skip update */
|
||||
if(ref->key->id == ref->id) {
|
||||
ref->key->id = newid;
|
||||
ref->id = newid;
|
||||
}
|
||||
lock_rw_unlock(&ref->key->entry.lock);
|
||||
}
|
||||
|
||||
int
|
||||
rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref,
|
||||
struct alloc_cache* alloc, uint32_t timenow)
|
||||
{
|
||||
struct lruhash_entry* e;
|
||||
struct ub_packed_rrset_key* k = ref->key;
|
||||
hashvalue_t h = k->entry.hash;
|
||||
uint16_t rrset_type = ntohs(k->rk.type);
|
||||
int equal = 0;
|
||||
log_assert(ref->id != 0 && k->id != 0);
|
||||
/* looks up item with a readlock - no editing! */
|
||||
if((e=slabhash_lookup(&r->table, h, k, 0)) != 0) {
|
||||
/* return id and key as they will be used in the cache
|
||||
* since the lruhash_insert, if item already exists, deallocs
|
||||
* the passed key in favor of the already stored key.
|
||||
* because of the small gap (see below) this key ptr and id
|
||||
* may prove later to be already deleted, which is no problem
|
||||
* as it only makes a cache miss.
|
||||
*/
|
||||
ref->key = (struct ub_packed_rrset_key*)e->key;
|
||||
ref->id = ref->key->id;
|
||||
equal = rrsetdata_equal((struct packed_rrset_data*)k->entry.
|
||||
data, (struct packed_rrset_data*)e->data);
|
||||
if(!need_to_update_rrset(k->entry.data, e->data, timenow,
|
||||
equal)) {
|
||||
/* cache is superior, return that value */
|
||||
lock_rw_unlock(&e->lock);
|
||||
ub_packed_rrset_parsedelete(k, alloc);
|
||||
return 1;
|
||||
}
|
||||
lock_rw_unlock(&e->lock);
|
||||
/* Go on and insert the passed item.
|
||||
* small gap here, where entry is not locked.
|
||||
* possibly entry is updated with something else.
|
||||
* we then overwrite that with our data.
|
||||
* this is just too bad, its cache anyway. */
|
||||
/* use insert to update entry to manage lruhash
|
||||
* cache size values nicely. */
|
||||
}
|
||||
log_assert(ref->key->id != 0);
|
||||
slabhash_insert(&r->table, h, &k->entry, k->entry.data, alloc);
|
||||
if(e) {
|
||||
/* For NSEC, NSEC3, DNAME, when rdata is updated, update
|
||||
* the ID number so that proofs in message cache are
|
||||
* invalidated */
|
||||
if((rrset_type == LDNS_RR_TYPE_NSEC
|
||||
|| rrset_type == LDNS_RR_TYPE_NSEC3
|
||||
|| rrset_type == LDNS_RR_TYPE_DNAME) && !equal) {
|
||||
rrset_update_id(ref, alloc);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ub_packed_rrset_key*
|
||||
rrset_cache_lookup(struct rrset_cache* r, uint8_t* qname, size_t qnamelen,
|
||||
uint16_t qtype, uint16_t qclass, uint32_t flags, uint32_t timenow,
|
||||
int wr)
|
||||
{
|
||||
struct lruhash_entry* e;
|
||||
struct ub_packed_rrset_key key;
|
||||
|
||||
key.entry.key = &key;
|
||||
key.entry.data = NULL;
|
||||
key.rk.dname = qname;
|
||||
key.rk.dname_len = qnamelen;
|
||||
key.rk.type = htons(qtype);
|
||||
key.rk.rrset_class = htons(qclass);
|
||||
key.rk.flags = flags;
|
||||
|
||||
key.entry.hash = rrset_key_hash(&key.rk);
|
||||
|
||||
if((e = slabhash_lookup(&r->table, key.entry.hash, &key, wr))) {
|
||||
/* check TTL */
|
||||
struct packed_rrset_data* data =
|
||||
(struct packed_rrset_data*)e->data;
|
||||
if(timenow > data->ttl) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return NULL;
|
||||
}
|
||||
/* we're done */
|
||||
return (struct ub_packed_rrset_key*)e->key;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
rrset_array_lock(struct rrset_ref* ref, size_t count, uint32_t timenow)
|
||||
{
|
||||
size_t i;
|
||||
for(i=0; i<count; i++) {
|
||||
if(i>0 && ref[i].key == ref[i-1].key)
|
||||
continue; /* only lock items once */
|
||||
lock_rw_rdlock(&ref[i].key->entry.lock);
|
||||
if(ref[i].id != ref[i].key->id || timenow >
|
||||
((struct packed_rrset_data*)(ref[i].key->entry.data))
|
||||
->ttl) {
|
||||
/* failure! rollback our readlocks */
|
||||
rrset_array_unlock(ref, i+1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
rrset_array_unlock(struct rrset_ref* ref, size_t count)
|
||||
{
|
||||
size_t i;
|
||||
for(i=0; i<count; i++) {
|
||||
if(i>0 && ref[i].key == ref[i-1].key)
|
||||
continue; /* only unlock items once */
|
||||
lock_rw_unlock(&ref[i].key->entry.lock);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rrset_array_unlock_touch(struct rrset_cache* r, struct regional* scratch,
|
||||
struct rrset_ref* ref, size_t count)
|
||||
{
|
||||
hashvalue_t* h;
|
||||
size_t i;
|
||||
if(!(h = (hashvalue_t*)regional_alloc(scratch,
|
||||
sizeof(hashvalue_t)*count)))
|
||||
log_warn("rrset LRU: memory allocation failed");
|
||||
else /* store hash values */
|
||||
for(i=0; i<count; i++)
|
||||
h[i] = ref[i].key->entry.hash;
|
||||
/* unlock */
|
||||
for(i=0; i<count; i++) {
|
||||
if(i>0 && ref[i].key == ref[i-1].key)
|
||||
continue; /* only unlock items once */
|
||||
lock_rw_unlock(&ref[i].key->entry.lock);
|
||||
}
|
||||
if(h) {
|
||||
/* LRU touch, with no rrset locks held */
|
||||
for(i=0; i<count; i++) {
|
||||
if(i>0 && ref[i].key == ref[i-1].key)
|
||||
continue; /* only touch items once */
|
||||
rrset_cache_touch(r, ref[i].key, h[i], ref[i].id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rrset_update_sec_status(struct rrset_cache* r,
|
||||
struct ub_packed_rrset_key* rrset)
|
||||
{
|
||||
uint32_t now = (uint32_t)time(0);
|
||||
struct packed_rrset_data* updata =
|
||||
(struct packed_rrset_data*)rrset->entry.data;
|
||||
struct lruhash_entry* e;
|
||||
struct packed_rrset_data* cachedata;
|
||||
|
||||
/* hash it again to make sure it has a hash */
|
||||
rrset->entry.hash = rrset_key_hash(&rrset->rk);
|
||||
|
||||
e = slabhash_lookup(&r->table, rrset->entry.hash, rrset, 1);
|
||||
if(!e)
|
||||
return; /* not in the cache anymore */
|
||||
cachedata = (struct packed_rrset_data*)e->data;
|
||||
if(!rrsetdata_equal(updata, cachedata)) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return; /* rrset has changed in the meantime */
|
||||
}
|
||||
/* update the cached rrset */
|
||||
if(updata->security > cachedata->security) {
|
||||
cachedata->trust = updata->trust;
|
||||
cachedata->security = updata->security;
|
||||
cachedata->ttl = updata->ttl + now;
|
||||
}
|
||||
lock_rw_unlock(&e->lock);
|
||||
}
|
||||
|
||||
void
|
||||
rrset_check_sec_status(struct rrset_cache* r,
|
||||
struct ub_packed_rrset_key* rrset)
|
||||
{
|
||||
uint32_t now = (uint32_t)time(0);
|
||||
struct packed_rrset_data* updata =
|
||||
(struct packed_rrset_data*)rrset->entry.data;
|
||||
struct lruhash_entry* e;
|
||||
struct packed_rrset_data* cachedata;
|
||||
|
||||
/* hash it again to make sure it has a hash */
|
||||
rrset->entry.hash = rrset_key_hash(&rrset->rk);
|
||||
|
||||
e = slabhash_lookup(&r->table, rrset->entry.hash, rrset, 0);
|
||||
if(!e)
|
||||
return; /* not in the cache anymore */
|
||||
cachedata = (struct packed_rrset_data*)e->data;
|
||||
if(now > cachedata->ttl || !rrsetdata_equal(updata, cachedata)) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return; /* expired, or rrset has changed in the meantime */
|
||||
}
|
||||
if(cachedata->security > updata->security) {
|
||||
updata->security = cachedata->security;
|
||||
if(cachedata->security == sec_status_bogus)
|
||||
updata->ttl = cachedata->ttl - now;
|
||||
updata->trust = cachedata->trust;
|
||||
}
|
||||
lock_rw_unlock(&e->lock);
|
||||
}
|
||||
Vendored
+213
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* services/cache/rrset.h - Resource record set cache.
|
||||
*
|
||||
* 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 rrset cache.
|
||||
*/
|
||||
|
||||
#ifndef SERVICES_CACHE_RRSET_H
|
||||
#define SERVICES_CACHE_RRSET_H
|
||||
#include "util/storage/lruhash.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
struct config_file;
|
||||
struct alloc_cache;
|
||||
struct rrset_ref;
|
||||
struct regional;
|
||||
|
||||
/**
|
||||
* The rrset cache
|
||||
* Thin wrapper around hashtable, like a typedef.
|
||||
*/
|
||||
struct rrset_cache {
|
||||
/** uses partitioned hash table */
|
||||
struct slabhash table;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create rrset cache
|
||||
* @param cfg: config settings or NULL for defaults.
|
||||
* @param alloc: initial default rrset key allocation.
|
||||
* @return: NULL on error.
|
||||
*/
|
||||
struct rrset_cache* rrset_cache_create(struct config_file* cfg,
|
||||
struct alloc_cache* alloc);
|
||||
|
||||
/**
|
||||
* Delete rrset cache
|
||||
* @param r: rrset cache to delete.
|
||||
*/
|
||||
void rrset_cache_delete(struct rrset_cache* r);
|
||||
|
||||
/**
|
||||
* Adjust settings of the cache to settings from the config file.
|
||||
* May purge the cache. May recreate the cache.
|
||||
* There may be no threading or use by other threads.
|
||||
* @param r: rrset cache to adjust (like realloc).
|
||||
* @param cfg: config settings or NULL for defaults.
|
||||
* @param alloc: initial default rrset key allocation.
|
||||
* @return 0 on error, or new rrset cache pointer on success.
|
||||
*/
|
||||
struct rrset_cache* rrset_cache_adjust(struct rrset_cache* r,
|
||||
struct config_file* cfg, struct alloc_cache* alloc);
|
||||
|
||||
/**
|
||||
* Touch rrset, with given pointer and id.
|
||||
* Caller may not hold a lock on ANY rrset, this could give deadlock.
|
||||
*
|
||||
* This routine is faster than a hashtable lookup:
|
||||
* o no bin_lock is acquired.
|
||||
* o no walk through the bin-overflow-list.
|
||||
* o no comparison of the entry key to find it.
|
||||
*
|
||||
* @param r: rrset cache.
|
||||
* @param key: rrset key. Marked recently used (if it was not deleted
|
||||
* before the lock is acquired, in that case nothing happens).
|
||||
* @param hash: hash value of the item. Please read it from the key when
|
||||
* you have it locked. Used to find slab from slabhash.
|
||||
* @param id: used to check that the item is unchanged and not deleted.
|
||||
*/
|
||||
void rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key,
|
||||
hashvalue_t hash, rrset_id_t id);
|
||||
|
||||
/**
|
||||
* Update an rrset in the rrset cache. Stores the information for later use.
|
||||
* Will lookup if the rrset is in the cache and perform an update if necessary.
|
||||
* If the item was present, and superior, references are returned to that.
|
||||
* The passed item is then deallocated with rrset_parsedelete.
|
||||
*
|
||||
* A superior rrset is:
|
||||
* o rrset with better trust value.
|
||||
* o same trust value, different rdata, newly passed rrset is inserted.
|
||||
* If rdata is the same, TTL in the cache is updated.
|
||||
*
|
||||
* @param r: the rrset cache.
|
||||
* @param ref: reference (ptr and id) to the rrset. Pass reference setup for
|
||||
* the new rrset. The reference may be changed if the cached rrset is
|
||||
* superior.
|
||||
* Before calling the rrset is presumed newly allocated and changeable.
|
||||
* Afer calling you do not hold a lock, and the rrset is inserted in
|
||||
* the hashtable so you need a lock to change it.
|
||||
* @param alloc: how to allocate (and deallocate) the special rrset key.
|
||||
* @param timenow: current time (to see if ttl in cache is expired).
|
||||
* @return: true if the passed reference is updated, false if it is unchanged.
|
||||
*/
|
||||
int rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref,
|
||||
struct alloc_cache* alloc, uint32_t timenow);
|
||||
|
||||
/**
|
||||
* Lookup rrset. You obtain read/write lock. You must unlock before lookup
|
||||
* anything of else.
|
||||
* @param r: the rrset cache.
|
||||
* @param qname: name of rrset to lookup.
|
||||
* @param qnamelen: length of name of rrset to lookup.
|
||||
* @param qtype: type of rrset to lookup (host order).
|
||||
* @param qclass: class of rrset to lookup (host order).
|
||||
* @param flags: rrset flags, or 0.
|
||||
* @param timenow: used to compare with TTL.
|
||||
* @param wr: set true to get writelock.
|
||||
* @return packed rrset key pointer. Remember to unlock the key.entry.lock.
|
||||
* or NULL if could not be found or it was timed out.
|
||||
*/
|
||||
struct ub_packed_rrset_key* rrset_cache_lookup(struct rrset_cache* r,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
uint32_t flags, uint32_t timenow, int wr);
|
||||
|
||||
/**
|
||||
* Obtain readlock on a (sorted) list of rrset references.
|
||||
* Checks TTLs and IDs of the rrsets and rollbacks locking if not Ok.
|
||||
* @param ref: array of rrset references (key pointer and ID value).
|
||||
* duplicate references are allowed and handled.
|
||||
* @param count: size of array.
|
||||
* @param timenow: used to compare with TTL.
|
||||
* @return true on success, false on a failure, which can be that some
|
||||
* RRsets have timed out, or that they do not exist any more, the
|
||||
* RRsets have been purged from the cache.
|
||||
* If true, you hold readlocks on all the ref items.
|
||||
*/
|
||||
int rrset_array_lock(struct rrset_ref* ref, size_t count, uint32_t timenow);
|
||||
|
||||
/**
|
||||
* Unlock array (sorted) of rrset references.
|
||||
* @param ref: array of rrset references (key pointer and ID value).
|
||||
* duplicate references are allowed and handled.
|
||||
* @param count: size of array.
|
||||
*/
|
||||
void rrset_array_unlock(struct rrset_ref* ref, size_t count);
|
||||
|
||||
/**
|
||||
* Unlock array (sorted) of rrset references and at the same time
|
||||
* touch LRU on the rrsets. It needs the scratch region for temporary
|
||||
* storage as it uses the initial locks to obtain hash values.
|
||||
* @param r: the rrset cache. In this cache LRU is updated.
|
||||
* @param scratch: region for temporary storage of hash values.
|
||||
* if memory allocation fails, the lru touch fails silently,
|
||||
* but locks are released. memory errors are logged.
|
||||
* @param ref: array of rrset references (key pointer and ID value).
|
||||
* duplicate references are allowed and handled.
|
||||
* @param count: size of array.
|
||||
*/
|
||||
void rrset_array_unlock_touch(struct rrset_cache* r, struct regional* scratch,
|
||||
struct rrset_ref* ref, size_t count);
|
||||
|
||||
/**
|
||||
* Update security status of an rrset. Looks up the rrset.
|
||||
* If found, checks if rdata is equal.
|
||||
* If so, it will update the security, trust and rrset-ttl values.
|
||||
* The values are only updated if security is increased (towards secure).
|
||||
* @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.
|
||||
*/
|
||||
void rrset_update_sec_status(struct rrset_cache* r,
|
||||
struct ub_packed_rrset_key* rrset);
|
||||
|
||||
/**
|
||||
* Looks up security status of an rrset. Looks up the rrset.
|
||||
* If found, checks if rdata is equal, and entry did not expire.
|
||||
* If so, it will update the security, trust and rrset-ttl values.
|
||||
* @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.
|
||||
*/
|
||||
void rrset_check_sec_status(struct rrset_cache* r,
|
||||
struct ub_packed_rrset_key* rrset);
|
||||
|
||||
/** mark rrset to be deleted, set id=0 */
|
||||
void rrset_markdel(void* key);
|
||||
|
||||
#endif /* SERVICES_CACHE_RRSET_H */
|
||||
+134
-32
@@ -74,6 +74,7 @@ verbose_print_addr(struct addrinfo *addr)
|
||||
(socklen_t)sizeof(buf)) == 0) {
|
||||
strncpy(buf, "(null)", sizeof(buf));
|
||||
}
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
verbose(VERB_ALGO, "creating %s%s socket %s %d",
|
||||
addr->ai_socktype==SOCK_DGRAM?"udp":
|
||||
addr->ai_socktype==SOCK_STREAM?"tcp":"otherproto",
|
||||
@@ -85,11 +86,13 @@ verbose_print_addr(struct addrinfo *addr)
|
||||
}
|
||||
|
||||
int
|
||||
create_udp_sock(struct addrinfo *addr)
|
||||
create_udp_sock(struct addrinfo *addr, int v6only)
|
||||
{
|
||||
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) {
|
||||
@@ -98,11 +101,14 @@ create_udp_sock(struct addrinfo *addr)
|
||||
}
|
||||
if(addr->ai_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,
|
||||
&val, (socklen_t)sizeof(val)) < 0) {
|
||||
log_err("setsockopt(..., IPV6_V6ONLY"
|
||||
", ...) failed: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
# if defined(IPV6_USE_MIN_MTU)
|
||||
@@ -134,10 +140,11 @@ create_udp_sock(struct addrinfo *addr)
|
||||
/**
|
||||
* Create and bind TCP listening socket
|
||||
* @param addr: address info ready to make socket.
|
||||
* @param v6only: enable ip6 only flag on ip6 sockets.
|
||||
* @return: the socket. -1 on error.
|
||||
*/
|
||||
static int
|
||||
create_tcp_accept_sock(struct addrinfo *addr)
|
||||
create_tcp_accept_sock(struct addrinfo *addr, int v6only)
|
||||
{
|
||||
int s, flag;
|
||||
#if defined(SO_REUSEADDR) || defined(IPV6_V6ONLY)
|
||||
@@ -157,7 +164,7 @@ create_tcp_accept_sock(struct addrinfo *addr)
|
||||
}
|
||||
#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) {
|
||||
log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s",
|
||||
@@ -165,6 +172,8 @@ create_tcp_accept_sock(struct addrinfo *addr)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)v6only;
|
||||
#endif /* IPV6_V6ONLY */
|
||||
if(bind(s, (struct sockaddr*)addr->ai_addr, addr->ai_addrlen) != 0) {
|
||||
log_err("can't bind socket: %s", strerror(errno));
|
||||
@@ -191,7 +200,7 @@ 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)
|
||||
{
|
||||
struct addrinfo *res = NULL;
|
||||
int r, s;
|
||||
@@ -203,8 +212,8 @@ make_sock(int stype, const char* ifname, const char* port,
|
||||
return -1;
|
||||
}
|
||||
if(stype == SOCK_DGRAM)
|
||||
s = create_udp_sock(res);
|
||||
else s = create_tcp_accept_sock(res);
|
||||
s = create_udp_sock(res, v6only);
|
||||
else s = create_tcp_accept_sock(res, v6only);
|
||||
freeaddrinfo(res);
|
||||
return s;
|
||||
}
|
||||
@@ -213,11 +222,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));
|
||||
@@ -225,14 +234,67 @@ 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)
|
||||
{
|
||||
int on = 1;
|
||||
if(family == AF_INET6) {
|
||||
# ifdef IPV6_RECVPKTINFO
|
||||
if(setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
|
||||
&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,
|
||||
&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,
|
||||
&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,
|
||||
&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.
|
||||
@@ -241,25 +303,36 @@ 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;
|
||||
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)) == -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)) == -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)) == -1) {
|
||||
return 0;
|
||||
}
|
||||
if(!port_insert(list, s, 0)) {
|
||||
if(!port_insert(list, s, listen_type_tcp)) {
|
||||
close(s);
|
||||
return 0;
|
||||
}
|
||||
@@ -288,7 +361,8 @@ listen_cp_insert(struct comm_point* c, struct listen_dnsport* front)
|
||||
|
||||
struct listen_dnsport*
|
||||
listen_create(struct comm_base* base, struct listen_port* ports,
|
||||
size_t bufsize, comm_point_callback_t* cb, void *cb_arg)
|
||||
size_t bufsize, int tcp_accept_count,
|
||||
comm_point_callback_t* cb, void *cb_arg)
|
||||
{
|
||||
struct listen_dnsport* front = (struct listen_dnsport*)
|
||||
malloc(sizeof(struct listen_dnsport));
|
||||
@@ -304,11 +378,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,
|
||||
TCP_ACCEPT_COUNT, bufsize, cb, cb_arg);
|
||||
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);
|
||||
@@ -381,10 +459,15 @@ listening_ports_open(struct config_file* cfg)
|
||||
struct listen_port* list = NULL;
|
||||
struct addrinfo hints;
|
||||
int i, do_ip4, do_ip6;
|
||||
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;
|
||||
|
||||
/* getaddrinfo */
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
@@ -399,11 +482,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 IP4 or 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(NULL, cfg->do_udp, cfg->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;
|
||||
@@ -411,7 +499,8 @@ listening_ports_open(struct config_file* cfg)
|
||||
}
|
||||
if(do_ip4) {
|
||||
hints.ai_family = AF_INET;
|
||||
if(!ports_create_if(NULL, cfg->do_udp, cfg->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;
|
||||
@@ -422,8 +511,8 @@ 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,
|
||||
cfg->do_tcp, &hints, portbuf, &list)) {
|
||||
if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp,
|
||||
do_tcp, &hints, portbuf, &list)) {
|
||||
listening_ports_free(list);
|
||||
return NULL;
|
||||
}
|
||||
@@ -431,8 +520,8 @@ 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,
|
||||
cfg->do_tcp, &hints, portbuf, &list)) {
|
||||
if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp,
|
||||
do_tcp, &hints, portbuf, &list)) {
|
||||
listening_ports_free(list);
|
||||
return NULL;
|
||||
}
|
||||
@@ -452,3 +541,16 @@ void listening_ports_free(struct listen_port* list)
|
||||
list = nx;
|
||||
}
|
||||
}
|
||||
|
||||
size_t listen_get_mem(struct listen_dnsport* listen)
|
||||
{
|
||||
size_t s = sizeof(*listen) + sizeof(*listen->base) +
|
||||
sizeof(*listen->udp_buff) +
|
||||
ldns_buffer_capacity(listen->udp_buff);
|
||||
struct listen_list* p;
|
||||
for(p = listen->cps; p; p = p->next) {
|
||||
s += sizeof(*p);
|
||||
s += comm_point_get_mem(p->com);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -48,9 +48,6 @@ struct listen_list;
|
||||
struct addrinfo;
|
||||
struct config_file;
|
||||
|
||||
/** number of simultaneous open TCP connections for queries */
|
||||
#define TCP_ACCEPT_COUNT 10
|
||||
|
||||
/**
|
||||
* Listening for queries structure.
|
||||
* Contains list of query-listen sockets.
|
||||
@@ -77,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.
|
||||
@@ -87,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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -111,13 +120,15 @@ void listening_ports_free(struct listen_port* list);
|
||||
* for default all ifs.
|
||||
* @param ports: the list of shared ports.
|
||||
* @param bufsize: size of datagram buffer.
|
||||
* @param tcp_accept_count: max number of simultaneous TCP connections
|
||||
* from clients.
|
||||
* @param cb: callback function when a request arrives. It is passed
|
||||
* the packet and user argument. Return true to send a reply.
|
||||
* @param cb_arg: user data argument for callback function.
|
||||
* @return: the malloced listening structure, ready for use. NULL on error.
|
||||
*/
|
||||
struct listen_dnsport* listen_create(struct comm_base* base,
|
||||
struct listen_port* ports, size_t bufsize,
|
||||
struct listen_port* ports, size_t bufsize, int tcp_accept_count,
|
||||
comm_point_callback_t* cb, void* cb_arg);
|
||||
|
||||
/**
|
||||
@@ -141,14 +152,24 @@ void listen_resume(struct listen_dnsport* listen);
|
||||
|
||||
/**
|
||||
* delete the listening structure
|
||||
* @param listen: listening structure.
|
||||
*/
|
||||
void listen_delete(struct listen_dnsport* listen);
|
||||
|
||||
/**
|
||||
* get memory size used by the listening structs
|
||||
* @param listen: listening structure.
|
||||
* @return: size in bytes.
|
||||
*/
|
||||
size_t listen_get_mem(struct listen_dnsport* listen);
|
||||
|
||||
/**
|
||||
* Create and bind nonblocking UDP socket
|
||||
* @param addr: address info ready to make socket.
|
||||
* @param v6only: if enabled, IP6 sockets get IP6ONLY option set.
|
||||
* if enabled with value 2 IP6ONLY option is disabled.
|
||||
* @return: the socket. -1 on error.
|
||||
*/
|
||||
int create_udp_sock(struct addrinfo* addr);
|
||||
int create_udp_sock(struct addrinfo* addr, int v6only);
|
||||
|
||||
#endif /* LISTEN_DNSPORT_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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"
|
||||
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.
|
||||
* This tree is fixed at startup, so, readonly, no locks or mutexes necessary.
|
||||
*/
|
||||
struct local_zones {
|
||||
/** 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;
|
||||
|
||||
/** 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.
|
||||
* @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.
|
||||
* @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
|
||||
* @param zones: the zones tree
|
||||
*/
|
||||
void local_zones_print(struct local_zones* zones);
|
||||
|
||||
/**
|
||||
* Answer authoritatively for local zones.
|
||||
* @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);
|
||||
|
||||
#endif /* SERVICES_LOCALZONE_H */
|
||||
+837
@@ -0,0 +1,837 @@
|
||||
/*
|
||||
* services/mesh.c - deal with mesh of query states and handle events for that.
|
||||
*
|
||||
* 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 assist in dealing with a mesh of
|
||||
* query states. This mesh is supposed to be thread-specific.
|
||||
* It consists of query states (per qname, qtype, qclass) and connections
|
||||
* between query states and the super and subquery states, and replies to
|
||||
* send back to clients.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "services/mesh.h"
|
||||
#include "services/outbound_list.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/module.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/timehist.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "util/alloc.h"
|
||||
|
||||
int
|
||||
mesh_state_compare(const void* ap, const void* bp)
|
||||
{
|
||||
struct mesh_state* a = (struct mesh_state*)ap;
|
||||
struct mesh_state* b = (struct mesh_state*)bp;
|
||||
|
||||
if(a->s.is_priming && !b->s.is_priming)
|
||||
return -1;
|
||||
if(!a->s.is_priming && b->s.is_priming)
|
||||
return 1;
|
||||
|
||||
if((a->s.query_flags&BIT_RD) && !(b->s.query_flags&BIT_RD))
|
||||
return -1;
|
||||
if(!(a->s.query_flags&BIT_RD) && (b->s.query_flags&BIT_RD))
|
||||
return 1;
|
||||
|
||||
if((a->s.query_flags&BIT_CD) && !(b->s.query_flags&BIT_CD))
|
||||
return -1;
|
||||
if(!(a->s.query_flags&BIT_CD) && (b->s.query_flags&BIT_CD))
|
||||
return 1;
|
||||
|
||||
return query_info_compare(&a->s.qinfo, &b->s.qinfo);
|
||||
}
|
||||
|
||||
int
|
||||
mesh_state_ref_compare(const void* ap, const void* bp)
|
||||
{
|
||||
struct mesh_state_ref* a = (struct mesh_state_ref*)ap;
|
||||
struct mesh_state_ref* b = (struct mesh_state_ref*)bp;
|
||||
return mesh_state_compare(a->s, b->s);
|
||||
}
|
||||
|
||||
struct mesh_area*
|
||||
mesh_create(struct module_stack* stack, struct module_env* env)
|
||||
{
|
||||
struct mesh_area* mesh = calloc(1, sizeof(struct mesh_area));
|
||||
if(!mesh) {
|
||||
log_err("mesh area alloc: out of memory");
|
||||
return NULL;
|
||||
}
|
||||
mesh->histogram = timehist_setup();
|
||||
if(!mesh->histogram) {
|
||||
free(mesh);
|
||||
log_err("mesh area alloc: out of memory");
|
||||
return NULL;
|
||||
}
|
||||
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;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
/** help mesh delete delete mesh states */
|
||||
static void
|
||||
mesh_delete_helper(rbnode_t* n, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct mesh_state* mstate = (struct mesh_state*)n->key;
|
||||
mesh_state_cleanup(mstate);
|
||||
}
|
||||
|
||||
void
|
||||
mesh_delete(struct mesh_area* mesh)
|
||||
{
|
||||
if(!mesh)
|
||||
return;
|
||||
/* free all query states */
|
||||
traverse_postorder(&mesh->all, &mesh_delete_helper, NULL);
|
||||
timehist_delete(mesh->histogram);
|
||||
free(mesh);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags, 0);
|
||||
int was_detached = 0;
|
||||
int was_noreply = 0;
|
||||
int added = 0;
|
||||
/* 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) {
|
||||
log_err("mesh_state_create: out of memory; SERVFAIL");
|
||||
error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
qinfo, qid, qflags, edns);
|
||||
comm_point_send_reply(rep);
|
||||
return;
|
||||
}
|
||||
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_reply(s, edns, rep, qid, qflags)) {
|
||||
log_err("mesh_new_client: out of memory; SERVFAIL");
|
||||
error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
|
||||
qinfo, qid, qflags, edns);
|
||||
comm_point_send_reply(rep);
|
||||
if(added)
|
||||
mesh_state_delete(&s->s);
|
||||
return;
|
||||
}
|
||||
/* 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);
|
||||
}
|
||||
|
||||
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;
|
||||
/* 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,
|
||||
int is_ok, struct comm_reply* reply)
|
||||
{
|
||||
e->qstate->reply = reply;
|
||||
mesh_run(mesh, e->qstate->mesh_info,
|
||||
is_ok?module_event_reply:module_event_noreply, e);
|
||||
}
|
||||
|
||||
struct mesh_state*
|
||||
mesh_state_create(struct module_env* env, struct query_info* qinfo,
|
||||
uint16_t qflags, int prime)
|
||||
{
|
||||
struct regional* region = alloc_reg_obtain(env->alloc);
|
||||
struct mesh_state* mstate;
|
||||
int i;
|
||||
if(!region)
|
||||
return NULL;
|
||||
mstate = (struct mesh_state*)regional_alloc(region,
|
||||
sizeof(struct mesh_state));
|
||||
if(!mstate) {
|
||||
alloc_reg_release(env->alloc, region);
|
||||
return NULL;
|
||||
}
|
||||
memset(mstate, 0, sizeof(*mstate));
|
||||
mstate->node = *RBTREE_NULL;
|
||||
mstate->run_node = *RBTREE_NULL;
|
||||
mstate->node.key = mstate;
|
||||
mstate->run_node.key = mstate;
|
||||
mstate->reply_list = NULL;
|
||||
rbtree_init(&mstate->super_set, &mesh_state_ref_compare);
|
||||
rbtree_init(&mstate->sub_set, &mesh_state_ref_compare);
|
||||
mstate->num_activated = 0;
|
||||
/* init module qstate */
|
||||
mstate->s.qinfo.qtype = qinfo->qtype;
|
||||
mstate->s.qinfo.qclass = qinfo->qclass;
|
||||
mstate->s.qinfo.qname_len = qinfo->qname_len;
|
||||
mstate->s.qinfo.qname = regional_alloc_init(region, qinfo->qname,
|
||||
qinfo->qname_len);
|
||||
if(!mstate->s.qinfo.qname) {
|
||||
alloc_reg_release(env->alloc, region);
|
||||
return NULL;
|
||||
}
|
||||
/* remove all weird bits from qflags */
|
||||
mstate->s.query_flags = (qflags & (BIT_RD|BIT_CD));
|
||||
mstate->s.is_priming = prime;
|
||||
mstate->s.reply = NULL;
|
||||
mstate->s.region = region;
|
||||
mstate->s.curmod = 0;
|
||||
mstate->s.return_msg = 0;
|
||||
mstate->s.return_rcode = LDNS_RCODE_NOERROR;
|
||||
mstate->s.env = env;
|
||||
mstate->s.mesh_info = mstate;
|
||||
/* init modules */
|
||||
for(i=0; i<env->mesh->mods.num; i++) {
|
||||
mstate->s.minfo[i] = NULL;
|
||||
mstate->s.ext_state[i] = module_state_initial;
|
||||
}
|
||||
return mstate;
|
||||
}
|
||||
|
||||
void
|
||||
mesh_state_cleanup(struct mesh_state* mstate)
|
||||
{
|
||||
struct mesh_area* mesh;
|
||||
int i;
|
||||
if(!mstate)
|
||||
return;
|
||||
/* de-init modules */
|
||||
mesh = mstate->s.env->mesh;
|
||||
for(i=0; i<mesh->mods.num; i++) {
|
||||
log_assert(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;
|
||||
}
|
||||
alloc_reg_release(mstate->s.env->alloc, mstate->s.region);
|
||||
}
|
||||
|
||||
void
|
||||
mesh_state_delete(struct module_qstate* qstate)
|
||||
{
|
||||
struct mesh_area* mesh;
|
||||
struct mesh_state_ref* super, ref;
|
||||
struct mesh_state* mstate;
|
||||
if(!qstate)
|
||||
return;
|
||||
mstate = qstate->mesh_info;
|
||||
mesh = mstate->s.env->mesh;
|
||||
mesh_detach_subs(&mstate->s);
|
||||
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 || mstate->cb_list) {
|
||||
log_assert(mesh->num_reply_states > 0);
|
||||
mesh->num_reply_states--;
|
||||
}
|
||||
ref.node.key = &ref;
|
||||
ref.s = mstate;
|
||||
RBTREE_FOR(super, struct mesh_state_ref*, &mstate->super_set) {
|
||||
(void)rbtree_delete(&super->s->sub_set, &ref);
|
||||
}
|
||||
(void)rbtree_delete(&mesh->run, mstate);
|
||||
(void)rbtree_delete(&mesh->all, mstate);
|
||||
mesh_state_cleanup(mstate);
|
||||
}
|
||||
|
||||
void mesh_detach_subs(struct module_qstate* qstate)
|
||||
{
|
||||
struct mesh_area* mesh = qstate->env->mesh;
|
||||
struct mesh_state_ref* ref, lookup;
|
||||
struct rbnode_t* n;
|
||||
lookup.node.key = &lookup;
|
||||
lookup.s = qstate->mesh_info;
|
||||
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->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);
|
||||
}
|
||||
}
|
||||
rbtree_init(&qstate->mesh_info->sub_set, &mesh_state_ref_compare);
|
||||
}
|
||||
|
||||
int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
|
||||
uint16_t qflags, int prime, struct module_qstate** newq)
|
||||
{
|
||||
/* find it, if not, create it */
|
||||
struct mesh_area* mesh = qstate->env->mesh;
|
||||
struct mesh_state* sub = mesh_area_find(mesh, qinfo, qflags, prime);
|
||||
if(!sub) {
|
||||
struct rbnode_t* n;
|
||||
/* create a new one */
|
||||
sub = mesh_state_create(qstate->env, qinfo, qflags, prime);
|
||||
if(!sub) {
|
||||
log_err("mesh_attach_sub: out of memory");
|
||||
return 0;
|
||||
}
|
||||
n = rbtree_insert(&mesh->all, &sub->node);
|
||||
log_assert(n != NULL);
|
||||
/* set detached (it is now) */
|
||||
mesh->num_detached_states++;
|
||||
/* set new query state to run */
|
||||
n = rbtree_insert(&mesh->run, &sub->run_node);
|
||||
log_assert(n != NULL);
|
||||
*newq = &sub->s;
|
||||
} else
|
||||
*newq = NULL;
|
||||
if(!mesh_state_attachment(qstate->mesh_info, sub))
|
||||
return 0;
|
||||
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--;
|
||||
}
|
||||
/* *newq will be run when inited after the current module stops */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub)
|
||||
{
|
||||
struct rbnode_t* n;
|
||||
struct mesh_state_ref* subref; /* points to sub, inserted in super */
|
||||
struct mesh_state_ref* superref; /* points to super, inserted in sub */
|
||||
if( !(subref = regional_alloc(super->s.region,
|
||||
sizeof(struct mesh_state_ref))) ||
|
||||
!(superref = regional_alloc(sub->s.region,
|
||||
sizeof(struct mesh_state_ref))) ) {
|
||||
log_err("mesh_state_attachment: out of memory");
|
||||
return 0;
|
||||
}
|
||||
superref->node.key = superref;
|
||||
superref->s = super;
|
||||
subref->node.key = subref;
|
||||
subref->s = sub;
|
||||
n = rbtree_insert(&sub->super_set, &superref->node);
|
||||
log_assert(n != NULL);
|
||||
n = rbtree_insert(&super->sub_set, &subref->node);
|
||||
log_assert(n != NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** subtract timers and the values do not overflow or become negative */
|
||||
static void
|
||||
timeval_subtract(struct timeval* d, struct timeval* end, struct timeval* start)
|
||||
{
|
||||
#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--;
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
struct mesh_cb* r)
|
||||
{
|
||||
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);
|
||||
}
|
||||
m->s.env->mesh->num_reply_addrs--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send reply to mesh reply 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: reply entry
|
||||
*/
|
||||
static void
|
||||
mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
|
||||
struct mesh_reply* r)
|
||||
{
|
||||
struct timeval end_time;
|
||||
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(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) {
|
||||
error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo,
|
||||
r->qid, r->qflags, &r->edns);
|
||||
comm_point_send_reply(&r->query_reply);
|
||||
} else {
|
||||
size_t udp_size = r->edns.udp_size;
|
||||
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->query_reply.c->buffer, 0, 1,
|
||||
m->s.env->scratch, udp_size, &r->edns,
|
||||
(int)(r->edns.bits & EDNS_DO), secure))
|
||||
{
|
||||
error_encode(r->query_reply.c->buffer,
|
||||
LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid,
|
||||
r->qflags, &r->edns);
|
||||
}
|
||||
comm_point_send_reply(&r->query_reply);
|
||||
}
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_query_done(struct mesh_state* mstate)
|
||||
{
|
||||
struct mesh_reply* r;
|
||||
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);
|
||||
}
|
||||
for(c = mstate->cb_list; c; c = c->next) {
|
||||
mesh_do_callback(mstate, mstate->s.return_rcode, rep, c);
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate)
|
||||
{
|
||||
struct mesh_state_ref* ref;
|
||||
RBTREE_FOR(ref, struct mesh_state_ref*, &mstate->super_set)
|
||||
{
|
||||
/* 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->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);
|
||||
}
|
||||
}
|
||||
|
||||
struct mesh_state* mesh_area_find(struct mesh_area* mesh,
|
||||
struct query_info* qinfo, uint16_t qflags, int prime)
|
||||
{
|
||||
struct mesh_state key;
|
||||
struct mesh_state* result;
|
||||
|
||||
key.node.key = &key;
|
||||
key.s.is_priming = prime;
|
||||
key.s.qinfo = *qinfo;
|
||||
key.s.query_flags = qflags;
|
||||
|
||||
result = (struct mesh_state*)rbtree_search(&mesh->all, &key);
|
||||
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 mesh_reply* r = regional_alloc(s->s.region,
|
||||
sizeof(struct mesh_reply));
|
||||
if(!r)
|
||||
return 0;
|
||||
r->query_reply = *rep;
|
||||
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->next = s->reply_list;
|
||||
s->reply_list = r;
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Continue processing the mesh state at another module.
|
||||
* Handles module to modules tranfer of control.
|
||||
* Handles module finished.
|
||||
* @param mesh: the mesh area.
|
||||
* @param mstate: currently active mesh state.
|
||||
* Deleted if finished, calls _done and _supers to
|
||||
* send replies to clients and inform other mesh states.
|
||||
* This in turn may create additional runnable mesh states.
|
||||
* @param s: state at which the current module exited.
|
||||
* @param ev: the event sent to the module.
|
||||
* returned is the event to send to the next module.
|
||||
* @return true if continue processing at the new module.
|
||||
* false if not continued processing is needed.
|
||||
*/
|
||||
static int
|
||||
mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate,
|
||||
enum module_ext_state s, enum module_ev* ev)
|
||||
{
|
||||
mstate->num_activated++;
|
||||
if(mstate->num_activated > MESH_MAX_ACTIVATION) {
|
||||
/* module is looping. Stop it. */
|
||||
log_err("internal error: looping module stopped");
|
||||
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->mods.num == mstate->s.curmod) {
|
||||
log_err("Cannot pass to next module; at last module");
|
||||
log_query_info(VERB_QUERY, "pass error for qstate",
|
||||
&mstate->s.qinfo);
|
||||
mstate->s.curmod--;
|
||||
return mesh_continue(mesh, mstate, module_error, ev);
|
||||
}
|
||||
*ev = module_event_pass;
|
||||
return 1;
|
||||
}
|
||||
if(s == module_error && mstate->s.return_rcode == LDNS_RCODE_NOERROR) {
|
||||
/* error is bad, handle pass back up below */
|
||||
mstate->s.return_rcode = LDNS_RCODE_SERVFAIL;
|
||||
}
|
||||
if(s == module_error || s == module_finished) {
|
||||
if(mstate->s.curmod == 0) {
|
||||
mesh_query_done(mstate);
|
||||
mesh_walk_supers(mesh, mstate);
|
||||
mesh_state_delete(&mstate->s);
|
||||
return 0;
|
||||
}
|
||||
/* pass along the locus of control */
|
||||
mstate->s.curmod --;
|
||||
*ev = module_event_moddone;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate,
|
||||
enum module_ev ev, struct outbound_entry* e)
|
||||
{
|
||||
enum module_ext_state s;
|
||||
verbose(VERB_ALGO, "mesh_run: start");
|
||||
while(mstate) {
|
||||
/* run the module */
|
||||
log_assert(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 */
|
||||
mstate->s.reply = NULL;
|
||||
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->mods.mod[mstate->s.curmod]->name, strextstate(s));
|
||||
e = NULL;
|
||||
if(mesh_continue(mesh, mstate, s, &ev))
|
||||
continue;
|
||||
|
||||
/* run more modules */
|
||||
ev = module_event_pass;
|
||||
if(mesh->run.count > 0) {
|
||||
/* pop random element off the runnable tree */
|
||||
mstate = (struct mesh_state*)mesh->run.root->key;
|
||||
(void)rbtree_delete(&mesh->run, mstate);
|
||||
} else mstate = NULL;
|
||||
}
|
||||
if(verbosity >= VERB_ALGO) {
|
||||
mesh_stats(mesh, "mesh_run: end");
|
||||
mesh_log_list(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mesh_log_list(struct mesh_area* mesh)
|
||||
{
|
||||
char buf[30];
|
||||
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%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)?"rep":"", /*hasreply*/
|
||||
(m->cb_list)?"cb":"" /* callbacks */
|
||||
);
|
||||
log_query_info(VERB_ALGO, buf, &m->s.qinfo);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mesh_stats(struct mesh_area* mesh, const char* str)
|
||||
{
|
||||
verbose(VERB_DETAIL, "%s %u recursion states (%u with reply, "
|
||||
"%u detached), %u waiting replies, %u recursion replies "
|
||||
"sent", str, (unsigned)mesh->all.count,
|
||||
(unsigned)mesh->num_reply_states,
|
||||
(unsigned)mesh->num_detached_states,
|
||||
(unsigned)mesh->num_reply_addrs,
|
||||
(unsigned)mesh->replies_sent);
|
||||
if(mesh->replies_sent > 0) {
|
||||
struct timeval avg;
|
||||
timeval_divide(&avg, &mesh->replies_sum_wait,
|
||||
mesh->replies_sent);
|
||||
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;
|
||||
timehist_clear(mesh->histogram);
|
||||
}
|
||||
|
||||
size_t
|
||||
mesh_get_mem(struct mesh_area* mesh)
|
||||
{
|
||||
struct mesh_state* m;
|
||||
size_t s = sizeof(*mesh) + sizeof(struct timehist) +
|
||||
sizeof(struct th_buck)*mesh->histogram->num;
|
||||
RBTREE_FOR(m, struct mesh_state*, &mesh->all) {
|
||||
/* all, including m itself allocated in qstate region */
|
||||
s += regional_get_mem(m->s.region);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/** helper recursive rbtree find routine */
|
||||
static int
|
||||
find_in_subsub(struct mesh_state* m, struct mesh_state* tofind)
|
||||
{
|
||||
struct mesh_state_ref* r;
|
||||
RBTREE_FOR(r, struct mesh_state_ref*, &m->sub_set) {
|
||||
if(r->s == tofind || find_in_subsub(r->s, tofind))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo,
|
||||
uint16_t flags, int prime)
|
||||
{
|
||||
struct mesh_area* mesh = qstate->env->mesh;
|
||||
struct mesh_state* cyc_m = qstate->mesh_info;
|
||||
struct mesh_state* dep_m = mesh_area_find(mesh, qinfo, flags, prime);
|
||||
if(!dep_m)
|
||||
return 0;
|
||||
if(dep_m == cyc_m || find_in_subsub(dep_m, cyc_m))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
+457
@@ -0,0 +1,457 @@
|
||||
/*
|
||||
* services/mesh.h - deal with mesh of query states and handle events for that.
|
||||
*
|
||||
* 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 assist in dealing with a mesh of
|
||||
* query states. This mesh is supposed to be thread-specific.
|
||||
* It consists of query states (per qname, qtype, qclass) and connections
|
||||
* between query states and the super and subquery states, and replies to
|
||||
* send back to clients.
|
||||
*/
|
||||
|
||||
#ifndef SERVICES_MESH_H
|
||||
#define SERVICES_MESH_H
|
||||
|
||||
#include "util/rbtree.h"
|
||||
#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;
|
||||
struct timehist;
|
||||
|
||||
/**
|
||||
* Maximum number of mesh state activations. Any more is likely an
|
||||
* infinite loop in the module. It is then terminated.
|
||||
*/
|
||||
#define MESH_MAX_ACTIVATION 1000
|
||||
|
||||
/**
|
||||
* Mesh of query states
|
||||
*/
|
||||
struct mesh_area {
|
||||
/** active module stack */
|
||||
struct module_stack mods;
|
||||
/** environment for new states */
|
||||
struct module_env* env;
|
||||
|
||||
/** set of runnable queries (mesh_state.run_node) */
|
||||
rbtree_t run;
|
||||
/** rbtree of all current queries (mesh_state.node)*/
|
||||
rbtree_t all;
|
||||
|
||||
/** count of the total number of mesh_reply entries */
|
||||
size_t num_reply_addrs;
|
||||
/** count of the number of mesh_states that have mesh_replies
|
||||
* Because a state can send results to multiple reply addresses,
|
||||
* this number must be equal or lower than num_reply_addrs. */
|
||||
size_t num_reply_states;
|
||||
/** number of mesh_states that have no mesh_replies, and also
|
||||
* an empty set of super-states, thus are 'toplevel' or detached
|
||||
* internal opportunistic queries */
|
||||
size_t num_detached_states;
|
||||
|
||||
/** 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* A mesh query state
|
||||
* Unique per qname, qtype, qclass (from the qstate).
|
||||
* And RD / CD flag; in case a client turns it off.
|
||||
* And priming queries are different from ordinary queries (because of hints).
|
||||
*
|
||||
* The entire structure is allocated in a region, this region is the qstate
|
||||
* region. All parts (rbtree nodes etc) are also allocated in the region.
|
||||
*/
|
||||
struct mesh_state {
|
||||
/** node in mesh_area all tree, key is this struct. Must be first. */
|
||||
rbnode_t node;
|
||||
/** node in mesh_area runnable tree, key is this struct */
|
||||
rbnode_t run_node;
|
||||
/** the query state. Note that the qinfo and query_flags
|
||||
* may not change. */
|
||||
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;
|
||||
/** set of substates (that this state needs to continue)
|
||||
* contains struct mesh_state_ref* */
|
||||
rbtree_t sub_set;
|
||||
/** number of activations for the mesh state */
|
||||
size_t num_activated;
|
||||
};
|
||||
|
||||
/**
|
||||
* Rbtree reference to a mesh_state.
|
||||
* Used in super_set and sub_set.
|
||||
*/
|
||||
struct mesh_state_ref {
|
||||
/** node in rbtree for set, key is this structure */
|
||||
rbnode_t node;
|
||||
/** the mesh state */
|
||||
struct mesh_state* s;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reply to a client
|
||||
*/
|
||||
struct mesh_reply {
|
||||
/** next in reply list */
|
||||
struct mesh_reply* next;
|
||||
/** the query reply destination, packet buffer and where to send. */
|
||||
struct comm_reply query_reply;
|
||||
/** edns data from query */
|
||||
struct edns_data edns;
|
||||
/** the time when request was entered */
|
||||
struct timeval start_time;
|
||||
/** id of query, in network byteorder. */
|
||||
uint16_t qid;
|
||||
/** flags of query, for reply flags */
|
||||
uint16_t qflags;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 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(struct module_stack* stack,
|
||||
struct module_env* env);
|
||||
|
||||
/**
|
||||
* Delete mesh, and all query states and replies in it.
|
||||
* @param mesh: the mesh to delete.
|
||||
*/
|
||||
void mesh_delete(struct mesh_area* mesh);
|
||||
|
||||
/**
|
||||
* New query incoming from clients. Create new query state if needed, and
|
||||
* add mesh_reply to it. Returns error to client on malloc failures.
|
||||
* 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 rep: where to reply to.
|
||||
* @param qid: query id to reply with.
|
||||
*/
|
||||
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
|
||||
* query states until processing is complete.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
|
||||
int is_ok, struct comm_reply* reply);
|
||||
|
||||
/* ------------------- Functions for module environment --------------- */
|
||||
|
||||
/**
|
||||
* Detach-subqueries.
|
||||
* Remove all sub-query references from this query state.
|
||||
* Keeps super-references of those sub-queries correct.
|
||||
* Updates stat items in mesh_area structure.
|
||||
* @param qstate: used to find mesh state.
|
||||
*/
|
||||
void mesh_detach_subs(struct module_qstate* qstate);
|
||||
|
||||
/**
|
||||
* Attach subquery.
|
||||
* Creates it if it does not exist already.
|
||||
* Keeps sub and super references correct.
|
||||
* Updates stat items in mesh_area structure.
|
||||
* Pass if it is priming query or not.
|
||||
* return:
|
||||
* o if error (malloc) happened.
|
||||
* o need to initialise the new state (module init; it is a new state).
|
||||
* so that the next run of the query with this module is successful.
|
||||
* o no init needed, attachment successful.
|
||||
*
|
||||
* @param qstate: the state to find mesh state, and that wants to receive
|
||||
* the results from the new subquery.
|
||||
* @param qinfo: what to query for (copied).
|
||||
* @param qflags: what flags to use (RD / CD flag or not).
|
||||
* @param prime: if it is a (stub) priming query.
|
||||
* @param newq: If the new subquery needs initialisation, it is returned,
|
||||
* otherwise NULL is returned.
|
||||
* @return: false on error, true if success (and init may be needed).
|
||||
*/
|
||||
int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
|
||||
uint16_t qflags, int prime, struct module_qstate** newq);
|
||||
|
||||
/**
|
||||
* Query state is done, send messages to reply entries.
|
||||
* Encode messages using reply entry values and the querystate (with original
|
||||
* qinfo), using given reply_info.
|
||||
* Pass errcode != 0 if an error reply is needed.
|
||||
* If no reply entries, nothing is done.
|
||||
* Must be called before a module can module_finished or return module_error.
|
||||
* The module must handle the super query states itself as well.
|
||||
*
|
||||
* @param mstate: mesh state that is done. return_rcode and return_msg
|
||||
* are used for replies.
|
||||
* return_rcode: if not 0 (NOERROR) an error is sent back (and
|
||||
* return_msg is ignored).
|
||||
* return_msg: reply to encode and send back to clients.
|
||||
*/
|
||||
void mesh_query_done(struct mesh_state* mstate);
|
||||
|
||||
/**
|
||||
* Call inform_super for the super query states that are interested in the
|
||||
* results from this query state. These can then be changed for error
|
||||
* or results.
|
||||
* Called when a module is module_finished or returns module_error.
|
||||
* The super query states become runnable with event module_event_pass.
|
||||
*
|
||||
* @param mesh: mesh area to add newly runnable modules to.
|
||||
* @param mstate: the state that has results, used to find mesh state.
|
||||
*/
|
||||
void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate);
|
||||
|
||||
/**
|
||||
* Delete mesh state, cleanup and also rbtrees and so on.
|
||||
* Will detach from all super/subnodes.
|
||||
* @param qstate: to remove.
|
||||
*/
|
||||
void mesh_state_delete(struct module_qstate* qstate);
|
||||
|
||||
/* ------------------- Functions for mesh -------------------- */
|
||||
|
||||
/**
|
||||
* Create and initialize a new mesh state and its query state
|
||||
* Does not put the mesh state into rbtrees and so on.
|
||||
* @param env: module environment to set.
|
||||
* @param qinfo: query info that the mesh is for.
|
||||
* @param qflags: flags for query (RD / CD flag).
|
||||
* @param prime: if true, it is a priming query, set is_priming on mesh state.
|
||||
* @return: new mesh state or NULL on allocation error.
|
||||
*/
|
||||
struct mesh_state* mesh_state_create(struct module_env* env,
|
||||
struct query_info* qinfo, uint16_t qflags, int prime);
|
||||
|
||||
/**
|
||||
* Cleanup a mesh state and its query state. Does not do rbtree or
|
||||
* reference cleanup.
|
||||
* @param mstate: mesh state to cleanup. Its pointer may no longer be used
|
||||
* afterwards. Cleanup rbtrees before calling this function.
|
||||
*/
|
||||
void mesh_state_cleanup(struct mesh_state* mstate);
|
||||
|
||||
/**
|
||||
* Find a mesh state in the mesh area. Pass relevant flags.
|
||||
*
|
||||
* @param mesh: the mesh area to look in.
|
||||
* @param qinfo: what query
|
||||
* @param qflags: if RD / CD bit is set or not.
|
||||
* @param prime: if it is a priming query.
|
||||
* @return: mesh state or NULL if not found.
|
||||
*/
|
||||
struct mesh_state* mesh_area_find(struct mesh_area* mesh,
|
||||
struct query_info* qinfo, uint16_t qflags, int prime);
|
||||
|
||||
/**
|
||||
* Setup attachment super/sub relation between super and sub mesh state.
|
||||
* The relation must not be present when calling the function.
|
||||
* Does not update stat items in mesh_area.
|
||||
* @param super: super state.
|
||||
* @param sub: sub state.
|
||||
* @return: 0 on alloc error.
|
||||
*/
|
||||
int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub);
|
||||
|
||||
/**
|
||||
* Create new reply 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 rep: comm point reply info.
|
||||
* @param qid: ID of reply.
|
||||
* @param qflags: original query flags.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* runnable mesh states. Until completion. Automatically called by
|
||||
* mesh_report_reply and mesh_new_client as needed.
|
||||
* @param mesh: mesh area.
|
||||
* @param mstate: first mesh state to run.
|
||||
* @param ev: event the mstate. Others get event_pass.
|
||||
* @param e: if a reply, its outbound entry.
|
||||
*/
|
||||
void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate,
|
||||
enum module_ev ev, struct outbound_entry* e);
|
||||
|
||||
/**
|
||||
* Print some stats about the mesh to the log.
|
||||
* @param mesh: the mesh to print it for.
|
||||
* @param str: descriptive string to go with it.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
void mesh_log_list(struct mesh_area* mesh);
|
||||
|
||||
/**
|
||||
* Calculate memory size in use by mesh and all queries inside it.
|
||||
* @param mesh: the mesh to examine.
|
||||
* @return size in bytes.
|
||||
*/
|
||||
size_t mesh_get_mem(struct mesh_area* mesh);
|
||||
|
||||
/**
|
||||
* Find cycle; see if the given mesh is in the targets sub, or sub-sub, ...
|
||||
* trees.
|
||||
* @param qstate: given mesh querystate.
|
||||
* @param qinfo: query info for dependency.
|
||||
* @param flags: query flags of dependency.
|
||||
* @param prime: if dependency is a priming query or not.
|
||||
* @return true if the name,type,class exists and the given qstate mesh exists
|
||||
* as a dependency of that name. Thus if qstate becomes dependent on
|
||||
* name,type,class then a cycle is created.
|
||||
*/
|
||||
int mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo,
|
||||
uint16_t flags, int prime);
|
||||
|
||||
/** compare two mesh_states */
|
||||
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);
|
||||
|
||||
#endif /* SERVICES_MESH_H */
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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);
|
||||
log_assert(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++) {
|
||||
log_assert(fptr_whitelist_mod_deinit(stack->mod[i]->deinit));
|
||||
(*stack->mod[i]->deinit)(env, i);
|
||||
}
|
||||
stack->num = 0;
|
||||
free(stack->mod);
|
||||
stack->mod = NULL;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
#endif /* SERVICES_MODSTACK_H */
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* services/outbound_list.c - keep list of outbound serviced queries.
|
||||
*
|
||||
* 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 a module keep track of the
|
||||
* queries it has outstanding to authoritative servers.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "services/outbound_list.h"
|
||||
#include "services/outside_network.h"
|
||||
|
||||
void
|
||||
outbound_list_init(struct outbound_list* list)
|
||||
{
|
||||
list->first = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
outbound_list_clear(struct outbound_list* list)
|
||||
{
|
||||
struct outbound_entry *p, *np;
|
||||
p = list->first;
|
||||
while(p) {
|
||||
np = p->next;
|
||||
outnet_serviced_query_stop(p->qsent, p);
|
||||
/* in region, no free needed */
|
||||
p = np;
|
||||
}
|
||||
outbound_list_init(list);
|
||||
}
|
||||
|
||||
void
|
||||
outbound_list_insert(struct outbound_list* list, struct outbound_entry* e)
|
||||
{
|
||||
if(list->first)
|
||||
list->first->prev = e;
|
||||
e->next = list->first;
|
||||
e->prev = NULL;
|
||||
list->first = e;
|
||||
}
|
||||
|
||||
void
|
||||
outbound_list_remove(struct outbound_list* list, struct outbound_entry* e)
|
||||
{
|
||||
if(!e)
|
||||
return;
|
||||
outnet_serviced_query_stop(e->qsent, e);
|
||||
if(e->next)
|
||||
e->next->prev = e->prev;
|
||||
if(e->prev)
|
||||
e->prev->next = e->next;
|
||||
else list->first = e->next;
|
||||
/* in region, no free needed */
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* services/outbound_list.h - keep list of outbound serviced queries.
|
||||
*
|
||||
* 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 a module keep track of the
|
||||
* queries it has outstanding to authoritative servers.
|
||||
*/
|
||||
#ifndef SERVICES_OUTBOUND_LIST_H
|
||||
#define SERVICES_OUTBOUND_LIST_H
|
||||
struct outbound_entry;
|
||||
struct serviced_query;
|
||||
struct module_qstate;
|
||||
|
||||
/**
|
||||
* The outbound list. This structure is part of the module specific query
|
||||
* state.
|
||||
*/
|
||||
struct outbound_list {
|
||||
/** The linked list of outbound query entries. */
|
||||
struct outbound_entry* first;
|
||||
};
|
||||
|
||||
/**
|
||||
* Outbound list entry. A serviced query sent by a module processing the
|
||||
* query from the qstate. Double linked list to aid removal.
|
||||
*/
|
||||
struct outbound_entry {
|
||||
/** next in list */
|
||||
struct outbound_entry* next;
|
||||
/** prev in list */
|
||||
struct outbound_entry* prev;
|
||||
/** The query that was sent out */
|
||||
struct serviced_query* qsent;
|
||||
/** the module query state that sent it */
|
||||
struct module_qstate* qstate;
|
||||
};
|
||||
|
||||
/**
|
||||
* Init the user allocated outbound list structure
|
||||
* @param list: the list structure.
|
||||
*/
|
||||
void outbound_list_init(struct outbound_list* list);
|
||||
|
||||
/**
|
||||
* Clear the user owner outbound list structure.
|
||||
* Deletes serviced queries.
|
||||
* @param list: the list structure. It is cleared, but the list struct itself
|
||||
* is callers responsability to delete.
|
||||
*/
|
||||
void outbound_list_clear(struct outbound_list* list);
|
||||
|
||||
/**
|
||||
* Insert new entry into the list. Caller must allocate the entry with malloc.
|
||||
* qstate and qsent are set by caller.
|
||||
* @param list: the list to add to.
|
||||
* @param e: entry to add, it is only half initialised at call start, fully
|
||||
* initialised at call end.
|
||||
*/
|
||||
void outbound_list_insert(struct outbound_list* list,
|
||||
struct outbound_entry* e);
|
||||
|
||||
/**
|
||||
* Remove an entry from the list, and deletes it.
|
||||
* Deletes serviced query in the entry.
|
||||
* @param list: the list to remove from.
|
||||
* @param e: the entry to remove.
|
||||
*/
|
||||
void outbound_list_remove(struct outbound_list* list,
|
||||
struct outbound_entry* e);
|
||||
|
||||
#endif /* SERVICES_OUTBOUND_LIST_H */
|
||||
+671
-122
File diff suppressed because it is too large
Load Diff
+175
-19
@@ -51,6 +51,7 @@ struct pending_timeout;
|
||||
struct ub_randstate;
|
||||
struct pending_tcp;
|
||||
struct waiting_tcp;
|
||||
struct infra_cache;
|
||||
|
||||
/**
|
||||
* Send queries to outside servers and wait for answers from servers.
|
||||
@@ -63,30 +64,39 @@ struct outside_network {
|
||||
/** buffer shared by UDP connections, since there is only one
|
||||
datagram at any time. */
|
||||
ldns_buffer* udp_buff;
|
||||
/** serviced_callbacks malloc overhead when processing multiple
|
||||
* identical serviced queries to the same server. */
|
||||
size_t svcd_overhead;
|
||||
|
||||
/**
|
||||
* Array of udp comm point* that are used to listen to pending events.
|
||||
* Each is on a different port. This is for ip4 ports.
|
||||
*/
|
||||
struct comm_point **udp4_ports;
|
||||
struct comm_point** udp4_ports;
|
||||
/** number of udp4 ports */
|
||||
size_t num_udp4;
|
||||
|
||||
/**
|
||||
* The opened ip6 ports.
|
||||
*/
|
||||
struct comm_point **udp6_ports;
|
||||
struct comm_point** udp6_ports;
|
||||
/** number of udp6 ports */
|
||||
size_t num_udp6;
|
||||
|
||||
/** pending udp answers. sorted by id, addr */
|
||||
rbtree_t *pending;
|
||||
rbtree_t* pending;
|
||||
/** serviced queries, sorted by qbuf, addr, dnssec */
|
||||
rbtree_t* serviced;
|
||||
/** host cache, pointer but not owned by outnet. */
|
||||
struct infra_cache* infra;
|
||||
/** where to get random numbers */
|
||||
struct ub_randstate* rnd;
|
||||
|
||||
/**
|
||||
* Array of tcp pending used for outgoing TCP connections.
|
||||
* Each can be used to establish a TCP connection with a server.
|
||||
* The file descriptors are -1 if its free, need to be opened for
|
||||
* the tcp connection. Can be used for ip4 and ip6.
|
||||
* The file descriptors are -1 if they are free, and need to be
|
||||
* opened for the tcp connection. Can be used for ip4 and ip6.
|
||||
*/
|
||||
struct pending_tcp **tcp_conns;
|
||||
/** number of tcp communication points. */
|
||||
@@ -170,6 +180,68 @@ struct waiting_tcp {
|
||||
void* cb_arg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback to party interested in serviced query results.
|
||||
*/
|
||||
struct service_callback {
|
||||
/** next in callback list */
|
||||
struct service_callback* next;
|
||||
/** callback function */
|
||||
comm_point_callback_t* cb;
|
||||
/** user argument for callback function */
|
||||
void* cb_arg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Query service record.
|
||||
* Contains query and destination. UDP, TCP, EDNS are all tried.
|
||||
* complete with retries and timeouts. A number of interested parties can
|
||||
* receive a callback.
|
||||
*/
|
||||
struct serviced_query {
|
||||
/** The rbtree node, key is this record */
|
||||
rbnode_t node;
|
||||
/** The query that needs to be answered. Starts with flags u16,
|
||||
* then qdcount, ..., including qname, qtype, qclass. Does not include
|
||||
* EDNS record. */
|
||||
uint8_t* qbuf;
|
||||
/** length of qbuf. */
|
||||
size_t qbuflen;
|
||||
/** If an EDNS section is included, the DO/CD bit will be turned on. */
|
||||
int dnssec;
|
||||
/** where to send it */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr field in use. */
|
||||
socklen_t addrlen;
|
||||
/** current status */
|
||||
enum serviced_query_status {
|
||||
/** initial status */
|
||||
serviced_initial,
|
||||
/** UDP with EDNS sent */
|
||||
serviced_query_UDP_EDNS,
|
||||
/** UDP without EDNS sent */
|
||||
serviced_query_UDP,
|
||||
/** TCP with EDNS sent */
|
||||
serviced_query_TCP_EDNS,
|
||||
/** TCP without EDNS sent */
|
||||
serviced_query_TCP
|
||||
}
|
||||
/** variable with current status */
|
||||
status;
|
||||
/** true if serviced_query is scheduled for deletion already */
|
||||
int to_be_deleted;
|
||||
/** number of UDP retries */
|
||||
int retry;
|
||||
/** time last UDP was sent */
|
||||
struct timeval last_sent_time;
|
||||
/** outside network this is part of */
|
||||
struct outside_network* outnet;
|
||||
/** list of interested parties that need callback on results. */
|
||||
struct service_callback* cblist;
|
||||
/** the UDP or TCP query that is pending, see status which */
|
||||
void* pending;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create outside_network structure with N udp ports.
|
||||
* @param base: the communication base to use for event handling.
|
||||
@@ -183,11 +255,14 @@ struct waiting_tcp {
|
||||
* @param port_base: if -1 system assigns ports, otherwise try to get
|
||||
* the ports numbered from this starting number.
|
||||
* @param num_tcp: number of outgoing tcp buffers to preallocate.
|
||||
* @param infra: pointer to infra cached used for serviced queries.
|
||||
* @param rnd: stored to create random numbers for serviced queries.
|
||||
* @return: the new structure (with no pending answers) or NULL on error.
|
||||
*/
|
||||
struct outside_network* outside_network_create(struct comm_base* base,
|
||||
size_t bufsize, size_t num_ports, char** ifs, int num_ifs,
|
||||
int do_ip4, int do_ip6, int port_base, size_t num_tcp);
|
||||
int do_ip4, int do_ip6, int port_base, size_t num_tcp,
|
||||
struct infra_cache* infra, struct ub_randstate* rnd);
|
||||
|
||||
/**
|
||||
* Delete outside_network structure.
|
||||
@@ -202,17 +277,16 @@ void outside_network_delete(struct outside_network* outnet);
|
||||
* @param packet: wireformat query to send to destination.
|
||||
* @param addr: address to send to.
|
||||
* @param addrlen: length of addr.
|
||||
* @param timeout: in seconds from now.
|
||||
* @param timeout: in milliseconds from now.
|
||||
* @param callback: function to call on error, timeout or reply.
|
||||
* The routine does not return an error, instead it calls the callback,
|
||||
* with an error code if an error happens.
|
||||
* @param callback_arg: user argument for callback function.
|
||||
* @param rnd: random state for generating ID and port.
|
||||
* @return: NULL on error for malloc or socket. Else the pending query object.
|
||||
*/
|
||||
void pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
|
||||
comm_point_callback_t* callback, void* callback_arg,
|
||||
struct ub_randstate* rnd);
|
||||
struct pending* pending_udp_query(struct outside_network* outnet,
|
||||
ldns_buffer* packet, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, int timeout, comm_point_callback_t* callback,
|
||||
void* callback_arg, struct ub_randstate* rnd);
|
||||
|
||||
/**
|
||||
* Send TCP query. May wait for TCP buffer. Selects ID to be random, and
|
||||
@@ -225,15 +299,14 @@ void pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
* Timer starts running now. Timer may expire if all buffers are used,
|
||||
* without any query been sent to the server yet.
|
||||
* @param callback: function to call on error, timeout or reply.
|
||||
* The routine does not return an error, instead it calls the callback,
|
||||
* with an error code if an error happens.
|
||||
* @param callback_arg: user argument for callback function.
|
||||
* @param rnd: random state for generating ID.
|
||||
* @return: false on error for malloc or socket. Else the pending TCP object.
|
||||
*/
|
||||
void pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
|
||||
comm_point_callback_t* callback, void* callback_arg,
|
||||
struct ub_randstate* rnd);
|
||||
struct waiting_tcp* pending_tcp_query(struct outside_network* outnet,
|
||||
ldns_buffer* packet, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, int timeout, comm_point_callback_t* callback,
|
||||
void* callback_arg, struct ub_randstate* rnd);
|
||||
|
||||
/**
|
||||
* Delete pending answer.
|
||||
@@ -243,4 +316,87 @@ void pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
*/
|
||||
void pending_delete(struct outside_network* outnet, struct pending* p);
|
||||
|
||||
/**
|
||||
* Perform a serviced query to the authoritative servers.
|
||||
* Duplicate efforts are detected, and EDNS, TCP and UDP retry is performed.
|
||||
* @param outnet: outside network, with rbtree of serviced queries.
|
||||
* @param qname: what qname to query.
|
||||
* @param qnamelen: length of qname in octets including 0 root label.
|
||||
* @param qtype: rrset type to query (host format)
|
||||
* @param qclass: query class. (host format)
|
||||
* @param flags: flags u16 (host format), includes opcode, CD bit.
|
||||
* @param dnssec: if set, DO bit is set in EDNS queries.
|
||||
* If the value includes BIT_CD, CD bit is set when in EDNS queries.
|
||||
* If the value includes BIT_DO, DO bit is set when in EDNS queries.
|
||||
* @param callback: callback function.
|
||||
* @param callback_arg: user argument to callback function.
|
||||
* @param addr: to which server to send the query.
|
||||
* @param addrlen: length of addr.
|
||||
* @param buff: scratch buffer to create query contents in. Empty on exit.
|
||||
* @param arg_compare: function to compare callback args, return true if
|
||||
* identical. It is given the callback_arg and args that are listed.
|
||||
* @return 0 on error, or pointer to serviced query that is used to answer
|
||||
* this serviced query may be shared with other callbacks as well.
|
||||
*/
|
||||
struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
uint16_t flags, int dnssec, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, comm_point_callback_t* callback,
|
||||
void* callback_arg, ldns_buffer* buff,
|
||||
int (*arg_compare)(void*,void*));
|
||||
|
||||
/**
|
||||
* Remove service query callback.
|
||||
* If that leads to zero callbacks, the query is completely cancelled.
|
||||
* @param sq: serviced query to adjust.
|
||||
* @param cb_arg: callback argument of callback that needs removal.
|
||||
* same as the callback_arg to outnet_serviced_query().
|
||||
*/
|
||||
void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg);
|
||||
|
||||
/**
|
||||
* Get memory size in use by outside network.
|
||||
* Counts buffers and outstanding query (serviced queries) malloced data.
|
||||
* @param outnet: outside network structure.
|
||||
* @return size in bytes.
|
||||
*/
|
||||
size_t outnet_get_mem(struct outside_network* outnet);
|
||||
|
||||
/**
|
||||
* Get memory size in use by serviced query while it is servicing callbacks.
|
||||
* This takes into account the pre-deleted status of it; it will be deleted
|
||||
* when the callbacks are done.
|
||||
* @param sq: serviced query.
|
||||
* @return size in bytes.
|
||||
*/
|
||||
size_t serviced_get_mem(struct serviced_query* sq);
|
||||
|
||||
/** callback for incoming udp answers from the network */
|
||||
int outnet_udp_cb(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply *reply_info);
|
||||
|
||||
/** callback for pending tcp connections */
|
||||
int outnet_tcp_cb(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply *reply_info);
|
||||
|
||||
/** callback for udp timeout */
|
||||
void pending_udp_timer_cb(void *arg);
|
||||
|
||||
/** callback for outgoing TCP timer event */
|
||||
void outnet_tcptimer(void* arg);
|
||||
|
||||
/** callback for serviced query UDP answers */
|
||||
int serviced_udp_callback(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* rep);
|
||||
|
||||
/** TCP reply or error callback for serviced queries */
|
||||
int serviced_tcp_callback(struct comm_point* c, void* arg, int error,
|
||||
struct comm_reply* rep);
|
||||
|
||||
/** compare function of pending rbtree */
|
||||
int pending_cmp(const void* key1, const void* key2);
|
||||
|
||||
/** compare function of serviced query rbtree */
|
||||
int serviced_cmp(const void* key1, const void* key2);
|
||||
|
||||
#endif /* OUTSIDE_NETWORK_H */
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* 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 "services/localzone.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 localzones */
|
||||
static void
|
||||
localzonechecks(struct config_file* cfg)
|
||||
{
|
||||
struct local_zones* zs;
|
||||
if(!(zs = local_zones_create()))
|
||||
fatal_exit("out of memory");
|
||||
if(!local_zones_apply_cfg(zs, cfg))
|
||||
fatal_exit("failed local-zone, local-data configuration");
|
||||
local_zones_delete(zs);
|
||||
}
|
||||
|
||||
/** emit warnings for IP in hosts */
|
||||
static void
|
||||
warn_hosts(const char* typ, struct config_stub* list)
|
||||
{
|
||||
struct sockaddr_storage a;
|
||||
socklen_t alen;
|
||||
struct config_stub* s;
|
||||
struct config_strlist* h;
|
||||
for(s=list; s; s=s->next) {
|
||||
for(h=s->hosts; h; h=h->next) {
|
||||
if(extstrtoaddr(h->str, &a, &alen)) {
|
||||
fprintf(stderr, "unbound-checkconf: warning:"
|
||||
" %s %s: \"%s\" is an IP%s address, "
|
||||
"and when looked up as a host name "
|
||||
"during use may not resolve.\n",
|
||||
s->name, typ, h->str,
|
||||
addr_is_ip6(&a, alen)?"6":"4");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** check interface strings */
|
||||
static void
|
||||
interfacechecks(struct config_file* cfg)
|
||||
{
|
||||
struct sockaddr_storage a;
|
||||
socklen_t alen;
|
||||
int i, j;
|
||||
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(j=0; j<cfg->num_ifs; j++) {
|
||||
if(i!=j && strcmp(cfg->ifs[i], cfg->ifs[j])==0)
|
||||
fatal_exit("interface: %s present twice, "
|
||||
"cannot bind same ports twice.",
|
||||
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]);
|
||||
}
|
||||
for(j=0; j<cfg->num_out_ifs; j++) {
|
||||
if(i!=j && strcmp(cfg->out_ifs[i], cfg->out_ifs[j])==0)
|
||||
fatal_exit("outgoing-interface: %s present "
|
||||
"twice, cannot bind same ports twice.",
|
||||
cfg->out_ifs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** check acl ips */
|
||||
static void
|
||||
aclchecks(struct config_file* cfg)
|
||||
{
|
||||
int d;
|
||||
struct sockaddr_storage a;
|
||||
socklen_t alen;
|
||||
struct config_str2list* acl;
|
||||
for(acl=cfg->acls; acl; acl = acl->next) {
|
||||
if(!netblockstrtoaddr(acl->str, UNBOUND_DNS_PORT, &a, &alen,
|
||||
&d)) {
|
||||
fatal_exit("cannot parse access control address %s %s",
|
||||
acl->str, acl->str2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** check file list, every file must be inside the chroot location */
|
||||
static void
|
||||
check_chroot_filelist(const char* desc, struct config_strlist* list,
|
||||
const char* chrootdir)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
if(!chrootdir) return;
|
||||
for(p=list; p; p=p->next) {
|
||||
if(p->str && p->str[0] && strncmp(chrootdir, p->str,
|
||||
strlen(chrootdir)) != 0) {
|
||||
fatal_exit("%s: \"%s\" not in chrootdir %s",
|
||||
desc, p->str, chrootdir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** check configuration for errors */
|
||||
static void
|
||||
morechecks(struct config_file* cfg)
|
||||
{
|
||||
warn_hosts("stub-host", cfg->stubs);
|
||||
warn_hosts("forward-host", cfg->forwards);
|
||||
interfacechecks(cfg);
|
||||
aclchecks(cfg);
|
||||
|
||||
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 && cfg->chrootdir[0] &&
|
||||
cfg->chrootdir[strlen(cfg->chrootdir)-1] == '/')
|
||||
fatal_exit("chootdir %s has trailing slash '/' please remove.",
|
||||
cfg->chrootdir);
|
||||
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);
|
||||
check_chroot_filelist("file with root-hints",
|
||||
cfg->root_hints, cfg->chrootdir);
|
||||
check_chroot_filelist("trust-anchor-file",
|
||||
cfg->trust_anchor_file_list, cfg->chrootdir);
|
||||
check_chroot_filelist("trusted-keys-file",
|
||||
cfg->trusted_keys_file_list, 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]) {
|
||||
if(getpwnam(cfg->username) == NULL)
|
||||
fatal_exit("user '%s' does not exist.", cfg->username);
|
||||
endpwent();
|
||||
}
|
||||
|
||||
localzonechecks(cfg);
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
* checkconf/unbound-host.c - replacement for host that supports validation.
|
||||
*
|
||||
* 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 performs functionality like 'host', and also supports validation.
|
||||
* It uses the libunbound library.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "libunbound/unbound.h"
|
||||
#include <ldns/ldns.h>
|
||||
|
||||
/** verbosity for unbound-host app */
|
||||
static int verb = 0;
|
||||
|
||||
/** Give unbound-host usage, and exit (1). */
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
printf("Usage: unbound-host [-vdh] [-c class] [-t type] hostname\n");
|
||||
printf(" [-y key] [-f keyfile] [-F namedkeyfile]\n");
|
||||
printf(" Queries the DNS for information.\n");
|
||||
printf(" The hostname is looked up for IP4, IP6 and mail.\n");
|
||||
printf(" If an ip-address is given a reverse lookup is done.\n");
|
||||
printf(" Use the -v option to see DNSSEC security information.\n");
|
||||
printf(" -t type what type to look for.\n");
|
||||
printf(" -c class what class to look for, if not class IN.\n");
|
||||
printf(" -y 'keystring' specify trust anchor, DS or DNSKEY, like\n");
|
||||
printf(" -y 'example.com DS 31560 5 1 1CFED8478...'\n");
|
||||
printf(" -f keyfile read trust anchors from file, with lines as -y.\n");
|
||||
printf(" -F keyfile read named.conf-style trust anchors.\n");
|
||||
printf(" -v be more verbose, shows nodata and security.\n");
|
||||
printf(" -d debug, traces the action, -d -d shows more.\n");
|
||||
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);
|
||||
}
|
||||
|
||||
/** determine if str is ip4 and put into reverse lookup format */
|
||||
static int
|
||||
isip4(const char* nm, char** res)
|
||||
{
|
||||
struct in_addr addr;
|
||||
/* ddd.ddd.ddd.ddd.in-addr.arpa. is less than 32 */
|
||||
char buf[32];
|
||||
if(inet_pton(AF_INET, nm, &addr) <= 0) {
|
||||
return 0;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "%u.%u.%u.%u.in-addr.arpa",
|
||||
(unsigned)((uint8_t*)&addr)[3], (unsigned)((uint8_t*)&addr)[2],
|
||||
(unsigned)((uint8_t*)&addr)[1], (unsigned)((uint8_t*)&addr)[0]);
|
||||
*res = strdup(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** determine if str is ip6 and put into reverse lookup format */
|
||||
static int
|
||||
isip6(const char* nm, char** res)
|
||||
{
|
||||
struct in6_addr addr;
|
||||
/* [nibble.]{32}.ip6.arpa. is less than 128 */
|
||||
char* hex = "0123456789abcdef";
|
||||
char buf[128];
|
||||
char *p;
|
||||
int i;
|
||||
if(inet_pton(AF_INET6, nm, &addr) <= 0) {
|
||||
return 0;
|
||||
}
|
||||
p = buf;
|
||||
for(i=15; i>=0; i--) {
|
||||
uint8_t b = ((uint8_t*)&addr)[i];
|
||||
*p++ = hex[ (b&0x0f) ];
|
||||
*p++ = '.';
|
||||
*p++ = hex[ (b&0xf0) >> 4 ];
|
||||
*p++ = '.';
|
||||
}
|
||||
snprintf(buf+16*4, sizeof(buf)-16*4, "ip6.arpa");
|
||||
*res = strdup(buf);
|
||||
if(!*res) {
|
||||
fprintf(stderr, "error: out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** massage input name */
|
||||
static char*
|
||||
massage_qname(const char* nm, int* reverse)
|
||||
{
|
||||
/* recognise IP4 and IP6, create reverse addresses if needed */
|
||||
char* res;
|
||||
if(isip4(nm, &res)) {
|
||||
*reverse = 1;
|
||||
} else if(isip6(nm, &res)) {
|
||||
*reverse = 1;
|
||||
} else {
|
||||
res = strdup(nm);
|
||||
}
|
||||
if(!res) {
|
||||
fprintf(stderr, "error: out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** massage input type */
|
||||
static int
|
||||
massage_type(const char* t, int reverse, int* multi)
|
||||
{
|
||||
if(t) {
|
||||
int r = ldns_get_rr_type_by_name(t);
|
||||
if(r == 0 && strcasecmp(t, "TYPE0") != 0 &&
|
||||
strcmp(t, "") != 0) {
|
||||
fprintf(stderr, "error unknown type %s\n", t);
|
||||
exit(1);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
if(!t && reverse)
|
||||
return LDNS_RR_TYPE_PTR;
|
||||
*multi = 1;
|
||||
return LDNS_RR_TYPE_A;
|
||||
}
|
||||
|
||||
/** massage input class */
|
||||
static int
|
||||
massage_class(const char* c)
|
||||
{
|
||||
if(c) {
|
||||
int r = ldns_get_rr_class_by_name(c);
|
||||
if(r == 0 && strcasecmp(c, "CLASS0") != 0 &&
|
||||
strcmp(c, "") != 0) {
|
||||
fprintf(stderr, "error unknown class %s\n", c);
|
||||
exit(1);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
return LDNS_RR_CLASS_IN;
|
||||
}
|
||||
|
||||
/** nice security status string */
|
||||
static const char*
|
||||
secure_str(struct ub_result* result)
|
||||
{
|
||||
if(result->secure) return "(secure)";
|
||||
if(result->bogus) return "(BOGUS (security failure))";
|
||||
return "(insecure)";
|
||||
}
|
||||
|
||||
/** nice string for type */
|
||||
static void
|
||||
pretty_type(char* s, size_t len, int t)
|
||||
{
|
||||
const ldns_rr_descriptor *d = ldns_rr_descript((uint16_t)t);
|
||||
if(d) {
|
||||
snprintf(s, len, "%s", d->_name);
|
||||
} else {
|
||||
snprintf(s, len, "TYPE%d", t);
|
||||
}
|
||||
}
|
||||
|
||||
/** nice string for class */
|
||||
static void
|
||||
pretty_class(char* s, size_t len, int c)
|
||||
{
|
||||
ldns_lookup_table *cl = ldns_lookup_by_id(ldns_rr_classes, c);
|
||||
if(cl) {
|
||||
snprintf(s, len, "%s", cl->name);
|
||||
} else {
|
||||
snprintf(s, len, "CLASS%d", c);
|
||||
}
|
||||
}
|
||||
|
||||
/** nice string for rcode */
|
||||
static void
|
||||
pretty_rcode(char* s, size_t len, int r)
|
||||
{
|
||||
ldns_lookup_table *rcode = ldns_lookup_by_id(ldns_rcodes, r);
|
||||
if(rcode) {
|
||||
snprintf(s, len, "%s", rcode->name);
|
||||
} else {
|
||||
snprintf(s, len, "RCODE%d", r);
|
||||
}
|
||||
}
|
||||
|
||||
/** convert and print rdata */
|
||||
static void
|
||||
print_rd(int t, char* data, size_t len)
|
||||
{
|
||||
size_t i, pos = 0;
|
||||
uint8_t* rd = (uint8_t*)malloc(len+2);
|
||||
ldns_rr* rr = ldns_rr_new();
|
||||
ldns_status status;
|
||||
if(!rd || !rr) {
|
||||
fprintf(stderr, "out of memory");
|
||||
exit(1);
|
||||
}
|
||||
ldns_rr_set_type(rr, t);
|
||||
ldns_write_uint16(rd, len);
|
||||
memmove(rd+2, data, len);
|
||||
ldns_rr_set_owner(rr, NULL);
|
||||
status = ldns_wire2rdf(rr, rd, len+2, &pos);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
|
||||
free(rd);
|
||||
printf("error_printing_data");
|
||||
}
|
||||
for(i=0; i<ldns_rr_rd_count(rr); i++) {
|
||||
printf(" ");
|
||||
ldns_rdf_print(stdout, ldns_rr_rdf(rr, i));
|
||||
}
|
||||
ldns_rr_free(rr);
|
||||
free(rd);
|
||||
}
|
||||
|
||||
/** pretty line of RR data for results */
|
||||
static void
|
||||
pretty_rdata(char* q, char* cstr, char* tstr, int t, const char* sec,
|
||||
char* data, size_t len)
|
||||
{
|
||||
printf("%s", q);
|
||||
if(strcmp(cstr, "IN") != 0)
|
||||
printf(" in class %s", cstr);
|
||||
if(t == LDNS_RR_TYPE_A)
|
||||
printf(" has address");
|
||||
else if(t == LDNS_RR_TYPE_AAAA)
|
||||
printf(" has IPv6 address");
|
||||
else if(t == LDNS_RR_TYPE_MX)
|
||||
printf(" mail is handled by");
|
||||
else if(t == LDNS_RR_TYPE_PTR)
|
||||
printf(" domain name pointer");
|
||||
else printf(" has %s record", tstr);
|
||||
print_rd(t, data, len);
|
||||
if(verb > 0)
|
||||
printf(" %s", sec);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/** pretty line of output for results */
|
||||
static void
|
||||
pretty_output(char* q, int t, int c, struct ub_result* result, int docname)
|
||||
{
|
||||
int i;
|
||||
const char *secstatus = secure_str(result);
|
||||
char tstr[16];
|
||||
char cstr[16];
|
||||
char rcodestr[16];
|
||||
pretty_type(tstr, 16, t);
|
||||
pretty_class(cstr, 16, c);
|
||||
pretty_rcode(rcodestr, 16, result->rcode);
|
||||
|
||||
if(!result->havedata && result->rcode) {
|
||||
printf("Host %s not found: %d(%s).",
|
||||
q, result->rcode, rcodestr);
|
||||
if(verb > 0)
|
||||
printf(" %s", secstatus);
|
||||
printf("\n");
|
||||
return;
|
||||
}
|
||||
if(docname && result->canonname &&
|
||||
result->canonname != result->qname) {
|
||||
printf("%s is an alias for %s", result->qname,
|
||||
result->canonname);
|
||||
if(verb > 0)
|
||||
printf(" %s", secstatus);
|
||||
printf("\n");
|
||||
}
|
||||
/* remove trailing . from long canonnames for nicer output */
|
||||
if(result->canonname && strlen(result->canonname) > 1 &&
|
||||
result->canonname[strlen(result->canonname)-1] == '.')
|
||||
result->canonname[strlen(result->canonname)-1] = 0;
|
||||
if(!result->havedata) {
|
||||
if(verb > 0) {
|
||||
printf("%s", result->canonname?result->canonname:q);
|
||||
if(strcmp(cstr, "IN") != 0)
|
||||
printf(" in class %s", cstr);
|
||||
if(t == LDNS_RR_TYPE_A)
|
||||
printf(" has no address");
|
||||
else if(t == LDNS_RR_TYPE_AAAA)
|
||||
printf(" has no IPv6 address");
|
||||
else if(t == LDNS_RR_TYPE_PTR)
|
||||
printf(" has no domain name ptr");
|
||||
else if(t == LDNS_RR_TYPE_MX)
|
||||
printf(" has no mail handler record");
|
||||
else printf(" has no %s record", tstr);
|
||||
printf(" %s\n", secstatus);
|
||||
}
|
||||
/* else: emptiness to indicate no data */
|
||||
return;
|
||||
}
|
||||
i=0;
|
||||
while(result->data[i])
|
||||
{
|
||||
pretty_rdata(
|
||||
result->canonname?result->canonname:q,
|
||||
cstr, tstr, t, secstatus, result->data[i],
|
||||
(size_t)result->len[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/** perform a lookup and printout return if domain existed */
|
||||
static int
|
||||
dnslook(struct ub_ctx* ctx, char* q, int t, int c, int docname)
|
||||
{
|
||||
int ret;
|
||||
struct ub_result* result;
|
||||
|
||||
ret = ub_resolve(ctx, q, t, c, &result);
|
||||
if(ret != 0) {
|
||||
fprintf(stderr, "resolve error: %s\n", ub_strerror(ret));
|
||||
exit(1);
|
||||
}
|
||||
pretty_output(q, t, c, result, docname);
|
||||
ret = result->nxdomain;
|
||||
ub_resolve_free(result);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** perform host lookup */
|
||||
static void
|
||||
lookup(struct ub_ctx* ctx, const char* nm, const char* qt, const char* qc)
|
||||
{
|
||||
/* massage input into a query name, type and class */
|
||||
int multi = 0; /* no type, so do A, AAAA, MX */
|
||||
int reverse = 0; /* we are doing a reverse lookup */
|
||||
char* realq = massage_qname(nm, &reverse);
|
||||
int t = massage_type(qt, reverse, &multi);
|
||||
int c = massage_class(qc);
|
||||
|
||||
/* perform the query */
|
||||
if(multi) {
|
||||
if(!dnslook(ctx, realq, LDNS_RR_TYPE_A, c, 1)) {
|
||||
/* domain exists, lookup more */
|
||||
(void)dnslook(ctx, realq, LDNS_RR_TYPE_AAAA, c, 0);
|
||||
(void)dnslook(ctx, realq, LDNS_RR_TYPE_MX, c, 0);
|
||||
}
|
||||
} else {
|
||||
(void)dnslook(ctx, realq, t, c, 1);
|
||||
}
|
||||
ub_ctx_delete(ctx);
|
||||
free(realq);
|
||||
}
|
||||
|
||||
/** 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* qclass = NULL;
|
||||
char* qtype = NULL;
|
||||
struct ub_ctx* ctx = NULL;
|
||||
int debuglevel = 0;
|
||||
|
||||
ctx = ub_ctx_create();
|
||||
if(!ctx) {
|
||||
fprintf(stderr, "error: out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* parse the options */
|
||||
while( (c=getopt(argc, argv, "F:c:df:ht:vy:")) != -1) {
|
||||
switch(c) {
|
||||
case 'c':
|
||||
qclass = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
debuglevel++;
|
||||
if(debuglevel < 2)
|
||||
debuglevel = 2; /* at least VERB_DETAIL */
|
||||
ub_ctx_debuglevel(ctx, debuglevel);
|
||||
break;
|
||||
case 't':
|
||||
qtype = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
verb++;
|
||||
break;
|
||||
case 'y':
|
||||
ub_ctx_add_ta(ctx, optarg);
|
||||
break;
|
||||
case 'f':
|
||||
ub_ctx_add_ta_file(ctx, optarg);
|
||||
break;
|
||||
case 'F':
|
||||
ub_ctx_trustedkeys(ctx, optarg);
|
||||
break;
|
||||
case '?':
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if(argc != 1)
|
||||
usage();
|
||||
|
||||
lookup(ctx, argv[0], qtype, qclass);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* checkconf/worker_cb.c - fake callback routines to make fptr_wlist work
|
||||
*
|
||||
* 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 fake callback functions, so that the symbols exist
|
||||
* and the fptr_wlist continues to work even if the daemon/worker is not
|
||||
* linked into the resulting program.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
struct comm_reply;
|
||||
struct comm_point;
|
||||
struct module_qstate;
|
||||
|
||||
int worker_handle_control_cmd(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_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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int
|
||||
acl_list_cmp(const void* ATTR_UNUSED(k1), const void* ATTR_UNUSED(k2))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int context_query_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void worker_stat_timer_cb(void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
@@ -0,0 +1,472 @@
|
||||
/*
|
||||
* testcode/asynclook.c - debug program perform async libunbound queries.
|
||||
*
|
||||
* 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 program shows the results from several background lookups,
|
||||
* while printing time in the foreground.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "libunbound/unbound.h"
|
||||
#include "util/locks.h"
|
||||
#include "util/log.h"
|
||||
|
||||
/**
|
||||
* result list for the lookups
|
||||
*/
|
||||
struct lookinfo {
|
||||
/** name to look up */
|
||||
char* name;
|
||||
/** tracking number that can be used to cancel the query */
|
||||
int async_id;
|
||||
/** error code from libunbound */
|
||||
int err;
|
||||
/** result from lookup */
|
||||
struct ub_result* result;
|
||||
};
|
||||
|
||||
/** global variable to see how many queries we have left */
|
||||
static int num_wait = 0;
|
||||
|
||||
/** usage information for asynclook */
|
||||
void usage(char* argv[])
|
||||
{
|
||||
printf("usage: %s [options] name ...\n", argv[0]);
|
||||
printf("names are looked up at the same time, asynchronously.\n");
|
||||
printf(" -b : use blocking requests\n");
|
||||
printf(" -c : cancel the requests\n");
|
||||
printf(" -d : enable debug output\n");
|
||||
printf(" -f addr : use addr, forward to that server\n");
|
||||
printf(" -h : this help message\n");
|
||||
printf(" -H fname : read hosts from fname\n");
|
||||
printf(" -r fname : read resolv.conf from fname\n");
|
||||
printf(" -t : use a resolver thread instead of forking a process\n");
|
||||
printf(" -x : perform extended threaded test\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** print result from lookup nicely */
|
||||
static void
|
||||
print_result(struct lookinfo* info)
|
||||
{
|
||||
char buf[100];
|
||||
if(info->err) /* error (from libunbound) */
|
||||
printf("%s: error %s\n", info->name,
|
||||
ub_strerror(info->err));
|
||||
else if(!info->result)
|
||||
printf("%s: cancelled\n", info->name);
|
||||
else if(info->result->havedata)
|
||||
printf("%s: %s\n", info->name,
|
||||
inet_ntop(AF_INET, info->result->data[0],
|
||||
buf, (socklen_t)sizeof(buf)));
|
||||
else {
|
||||
/* there is no data, why that? */
|
||||
if(info->result->rcode == 0 /*noerror*/ ||
|
||||
info->result->nxdomain)
|
||||
printf("%s: no data %s\n", info->name,
|
||||
info->result->nxdomain?"(no such host)":
|
||||
"(no IP4 address)");
|
||||
else /* some error (from the server) */
|
||||
printf("%s: DNS error %d\n", info->name,
|
||||
info->result->rcode);
|
||||
}
|
||||
}
|
||||
|
||||
/** this is a function of type ub_callback_t */
|
||||
static void
|
||||
lookup_is_done(void* mydata, int err, struct ub_result* result)
|
||||
{
|
||||
/* cast mydata back to the correct type */
|
||||
struct lookinfo* info = (struct lookinfo*)mydata;
|
||||
fprintf(stderr, "name %s resolved\n", info->name);
|
||||
info->err = err;
|
||||
info->result = result;
|
||||
/* one less to wait for */
|
||||
num_wait--;
|
||||
}
|
||||
|
||||
/** check error, if bad, exit with error message */
|
||||
static void
|
||||
checkerr(const char* desc, int err)
|
||||
{
|
||||
if(err != 0) {
|
||||
printf("%s error: %s\n", desc, ub_strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS)
|
||||
/** only one process can communicate with async worker */
|
||||
#define NUMTHR 1
|
||||
#else /* have threads */
|
||||
/** number of threads to make in extended test */
|
||||
#define NUMTHR 10
|
||||
#endif
|
||||
|
||||
/** struct for extended thread info */
|
||||
struct ext_thr_info {
|
||||
/** thread num for debug */
|
||||
int thread_num;
|
||||
/** thread id */
|
||||
ub_thread_t tid;
|
||||
/** context */
|
||||
struct ub_ctx* ctx;
|
||||
/** size of array to query */
|
||||
int argc;
|
||||
/** array of names to query */
|
||||
char** argv;
|
||||
/** number of queries to do */
|
||||
int numq;
|
||||
};
|
||||
|
||||
/** if true, we are testing against 'localhost' and extra checking is done */
|
||||
static int q_is_localhost = 0;
|
||||
|
||||
/** check result structure for the 'correct' answer */
|
||||
static void
|
||||
ext_check_result(const char* desc, int err, struct ub_result* result)
|
||||
{
|
||||
checkerr(desc, err);
|
||||
if(result == NULL) {
|
||||
printf("%s: error result is NULL.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(q_is_localhost) {
|
||||
if(strcmp(result->qname, "localhost") != 0) {
|
||||
printf("%s: error result has wrong qname.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->qtype != LDNS_RR_TYPE_A) {
|
||||
printf("%s: error result has wrong qtype.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->qclass != LDNS_RR_CLASS_IN) {
|
||||
printf("%s: error result has wrong qclass.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->data == NULL) {
|
||||
printf("%s: error result->data is NULL.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->len == NULL) {
|
||||
printf("%s: error result->len is NULL.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->rcode != 0) {
|
||||
printf("%s: error result->rcode is set.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->havedata == 0) {
|
||||
printf("%s: error result->havedata is unset.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->nxdomain != 0) {
|
||||
printf("%s: error result->nxdomain is set.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->secure || result->bogus) {
|
||||
printf("%s: error result->secure or bogus is set.\n",
|
||||
desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->data[0] == NULL) {
|
||||
printf("%s: error result->data[0] is NULL.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->len[0] != 4) {
|
||||
printf("%s: error result->len[0] is wrong.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->len[1] != 0 || result->data[1] != NULL) {
|
||||
printf("%s: error result->data[1] or len[1] is "
|
||||
"wrong.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** extended bg result callback, this function is ub_callback_t */
|
||||
static void
|
||||
ext_callback(void* mydata, int err, struct ub_result* result)
|
||||
{
|
||||
int* my_id = (int*)mydata;
|
||||
int doprint = 0;
|
||||
if(my_id) {
|
||||
/* I have an id, make sure we are not cancelled */
|
||||
if(*my_id == 0) {
|
||||
printf("error: query returned, but was cancelled\n");
|
||||
abort();
|
||||
exit(1);
|
||||
}
|
||||
if(doprint)
|
||||
printf("cb %d: ", *my_id);
|
||||
}
|
||||
ext_check_result("ext_callback", err, result);
|
||||
log_assert(result);
|
||||
if(doprint) {
|
||||
struct lookinfo pi;
|
||||
pi.name = result?result->qname:"noname";
|
||||
pi.result = result;
|
||||
pi.err = 0;
|
||||
print_result(&pi);
|
||||
}
|
||||
ub_resolve_free(result);
|
||||
}
|
||||
|
||||
/** extended thread worker */
|
||||
static void*
|
||||
ext_thread(void* arg)
|
||||
{
|
||||
struct ext_thr_info* inf = (struct ext_thr_info*)arg;
|
||||
int i, r;
|
||||
struct ub_result* result;
|
||||
int* async_ids = NULL;
|
||||
log_thread_set(&inf->thread_num);
|
||||
if(inf->thread_num > NUMTHR*2/3) {
|
||||
async_ids = (int*)calloc((size_t)inf->numq, sizeof(int));
|
||||
if(!async_ids) {
|
||||
printf("out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
for(i=0; i<inf->numq; i++) {
|
||||
if(async_ids) {
|
||||
r = ub_resolve_async(inf->ctx,
|
||||
inf->argv[i%inf->argc], LDNS_RR_TYPE_A,
|
||||
LDNS_RR_CLASS_IN, &async_ids[i], ext_callback,
|
||||
&async_ids[i]);
|
||||
checkerr("ub_resolve_async", r);
|
||||
if(i > 100) {
|
||||
r = ub_cancel(inf->ctx, async_ids[i-100]);
|
||||
checkerr("ub_cancel", r);
|
||||
async_ids[i-100]=0;
|
||||
}
|
||||
} else if(inf->thread_num > NUMTHR/2) {
|
||||
/* async */
|
||||
r = ub_resolve_async(inf->ctx,
|
||||
inf->argv[i%inf->argc], LDNS_RR_TYPE_A,
|
||||
LDNS_RR_CLASS_IN, NULL, ext_callback, NULL);
|
||||
checkerr("ub_resolve_async", r);
|
||||
} else {
|
||||
/* blocking */
|
||||
r = ub_resolve(inf->ctx, inf->argv[i%inf->argc],
|
||||
LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN, &result);
|
||||
ext_check_result("ub_resolve", r, result);
|
||||
}
|
||||
}
|
||||
if(inf->thread_num > NUMTHR/2) {
|
||||
r = ub_wait(inf->ctx);
|
||||
checkerr("ub_ctx_wait", r);
|
||||
}
|
||||
free(async_ids);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** perform extended threaded test */
|
||||
static int
|
||||
ext_test(struct ub_ctx* ctx, int argc, char** argv)
|
||||
{
|
||||
struct ext_thr_info inf[NUMTHR];
|
||||
int i;
|
||||
if(argc == 1 && strcmp(argv[0], "localhost") == 0)
|
||||
q_is_localhost = 1;
|
||||
printf("extended test start (%d threads)\n", NUMTHR);
|
||||
for(i=0; i<NUMTHR; i++) {
|
||||
/* 0 = this, 1 = library bg worker */
|
||||
inf[i].thread_num = i+2;
|
||||
inf[i].ctx = ctx;
|
||||
inf[i].argc = argc;
|
||||
inf[i].argv = argv;
|
||||
inf[i].numq = 1000;
|
||||
ub_thread_create(&inf[i].tid, ext_thread, &inf[i]);
|
||||
}
|
||||
/* the work happens here */
|
||||
for(i=0; i<NUMTHR; i++) {
|
||||
ub_thread_join(inf[i].tid);
|
||||
}
|
||||
printf("extended test end\n");
|
||||
ub_ctx_delete(ctx);
|
||||
checklock_stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** 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 program for asynclook */
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int c;
|
||||
struct ub_ctx* ctx;
|
||||
struct lookinfo* lookups;
|
||||
int i, r, cancel=0, blocking=0, ext=0;
|
||||
|
||||
/* lock debug start (if any) */
|
||||
checklock_start();
|
||||
|
||||
/* create context */
|
||||
ctx = ub_ctx_create();
|
||||
if(!ctx) {
|
||||
printf("could not create context, %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* command line options */
|
||||
if(argc == 1) {
|
||||
usage(argv);
|
||||
}
|
||||
while( (c=getopt(argc, argv, "bcdf:hH:r:tx")) != -1) {
|
||||
switch(c) {
|
||||
case 'd':
|
||||
r = ub_ctx_debuglevel(ctx, 3);
|
||||
checkerr("ub_ctx_debuglevel", r);
|
||||
break;
|
||||
case 't':
|
||||
r = ub_ctx_async(ctx, 1);
|
||||
checkerr("ub_ctx_async", r);
|
||||
break;
|
||||
case 'c':
|
||||
cancel = 1;
|
||||
break;
|
||||
case 'b':
|
||||
blocking = 1;
|
||||
break;
|
||||
case 'r':
|
||||
r = ub_ctx_resolvconf(ctx, optarg);
|
||||
if(r != 0) {
|
||||
printf("ub_ctx_resolvconf "
|
||||
"error: %s : %s\n",
|
||||
ub_strerror(r),
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
r = ub_ctx_hosts(ctx, optarg);
|
||||
if(r != 0) {
|
||||
printf("ub_ctx_hosts "
|
||||
"error: %s : %s\n",
|
||||
ub_strerror(r),
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
r = ub_ctx_set_fwd(ctx, optarg);
|
||||
checkerr("ub_ctx_set_fwd", r);
|
||||
break;
|
||||
case 'x':
|
||||
ext = 1;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
default:
|
||||
usage(argv);
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if(ext)
|
||||
return ext_test(ctx, argc, argv);
|
||||
|
||||
/* allocate array for results. */
|
||||
lookups = (struct lookinfo*)calloc((size_t)argc,
|
||||
sizeof(struct lookinfo));
|
||||
if(!lookups) {
|
||||
printf("out of memory\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* perform asyncronous calls */
|
||||
num_wait = argc;
|
||||
for(i=0; i<argc; i++) {
|
||||
lookups[i].name = argv[i];
|
||||
if(blocking) {
|
||||
fprintf(stderr, "lookup %s\n", argv[i]);
|
||||
r = ub_resolve(ctx, argv[i], LDNS_RR_TYPE_A,
|
||||
LDNS_RR_CLASS_IN, &lookups[i].result);
|
||||
checkerr("ub_resolve", r);
|
||||
} else {
|
||||
fprintf(stderr, "start async lookup %s\n", argv[i]);
|
||||
r = ub_resolve_async(ctx, argv[i], LDNS_RR_TYPE_A,
|
||||
LDNS_RR_CLASS_IN, &lookups[i], &lookup_is_done,
|
||||
&lookups[i].async_id);
|
||||
checkerr("ub_resolve_async", r);
|
||||
}
|
||||
}
|
||||
if(blocking)
|
||||
num_wait = 0;
|
||||
else if(cancel) {
|
||||
for(i=0; i<argc; i++) {
|
||||
fprintf(stderr, "cancel %s\n", argv[i]);
|
||||
r = ub_cancel(ctx, lookups[i].async_id);
|
||||
checkerr("ub_cancel", r);
|
||||
}
|
||||
num_wait = 0;
|
||||
}
|
||||
|
||||
/* wait while the hostnames are looked up. Do something useful here */
|
||||
if(num_wait > 0)
|
||||
for(i=0; i<1000; i++) {
|
||||
usleep(100000);
|
||||
fprintf(stderr, "%g seconds passed\n", 0.1*(double)i);
|
||||
r = ub_process(ctx);
|
||||
checkerr("ub_process", r);
|
||||
if(num_wait == 0)
|
||||
break;
|
||||
}
|
||||
if(i>=999) {
|
||||
printf("timed out\n");
|
||||
return 0;
|
||||
}
|
||||
printf("lookup complete\n");
|
||||
|
||||
/* print lookup results */
|
||||
for(i=0; i<argc; i++) {
|
||||
print_result(&lookups[i]);
|
||||
ub_resolve_free(lookups[i].result);
|
||||
}
|
||||
|
||||
ub_ctx_delete(ctx);
|
||||
free(lookups);
|
||||
checklock_stop();
|
||||
return 0;
|
||||
}
|
||||
+142
-11
@@ -54,7 +54,7 @@
|
||||
/** How long to wait before lock attempt is a failure. */
|
||||
#define CHECK_LOCK_TIMEOUT 30 /* seconds */
|
||||
/** How long to wait before join attempt is a failure. */
|
||||
#define CHECK_JOIN_TIMEOUT 240 /* seconds */
|
||||
#define CHECK_JOIN_TIMEOUT 120 /* seconds */
|
||||
|
||||
/** if key has been created */
|
||||
static int key_created = 0;
|
||||
@@ -67,6 +67,9 @@ int check_locking_order = 1;
|
||||
/** the pid of this runset, reasonably unique. */
|
||||
static pid_t check_lock_pid;
|
||||
|
||||
/** print all possible debug info on the state of the system */
|
||||
static void total_debug_info();
|
||||
|
||||
/** print pretty lock error and exit */
|
||||
static void lock_error(struct checked_lock* lock,
|
||||
const char* func, const char* file, int line, const char* err)
|
||||
@@ -82,6 +85,8 @@ static void lock_error(struct checked_lock* lock,
|
||||
(lock->type==check_lock_mutex)?"mutex": (
|
||||
(lock->type==check_lock_spinlock)?"spinlock": (
|
||||
(lock->type==check_lock_rwlock)?"rwlock": "badtype")), err);
|
||||
log_err("complete status display:");
|
||||
total_debug_info();
|
||||
fatal_exit("bailing out");
|
||||
}
|
||||
|
||||
@@ -189,7 +194,7 @@ prot_check(struct checked_lock* lock,
|
||||
}
|
||||
}
|
||||
|
||||
/** Copy protected memory region. */
|
||||
/** Copy protected memory region */
|
||||
static void
|
||||
prot_store(struct checked_lock* lock)
|
||||
{
|
||||
@@ -200,7 +205,24 @@ prot_store(struct checked_lock* lock)
|
||||
}
|
||||
}
|
||||
|
||||
/** write lock trace info to file, while you hold those locks. */
|
||||
/** get memory held by lock */
|
||||
size_t
|
||||
lock_get_mem(void* pp)
|
||||
{
|
||||
size_t s;
|
||||
struct checked_lock* lock = *(struct checked_lock**)pp;
|
||||
struct protected_area* p;
|
||||
s = sizeof(struct checked_lock);
|
||||
acquire_locklock(lock, __func__, __FILE__, __LINE__);
|
||||
for(p = lock->prot; p; p = p->next) {
|
||||
s += sizeof(struct protected_area);
|
||||
s += p->size;
|
||||
}
|
||||
LOCKRET(pthread_mutex_unlock(&lock->lock));
|
||||
return s;
|
||||
}
|
||||
|
||||
/** write lock trace info to file, while you hold those locks */
|
||||
static void
|
||||
ordercheck_locklock(struct thr_check* thr, struct checked_lock* lock)
|
||||
{
|
||||
@@ -220,7 +242,7 @@ ordercheck_locklock(struct thr_check* thr, struct checked_lock* lock)
|
||||
log_err("fwrite: %s", strerror(errno));
|
||||
}
|
||||
|
||||
/** write ordercheck lock creation details to file. */
|
||||
/** write ordercheck lock creation details to file */
|
||||
static void
|
||||
ordercheck_lockcreate(struct thr_check* thr, struct checked_lock* lock)
|
||||
{
|
||||
@@ -335,7 +357,7 @@ checklock_destroy(enum check_lock_type type, struct checked_lock** lock,
|
||||
e->create_func, e->create_file, e->create_line,
|
||||
(unsigned int)e->contention_count,
|
||||
(unsigned int)e->history_count,
|
||||
100*e->contention_count/e->history_count);
|
||||
(int)(100*e->contention_count/e->history_count));
|
||||
}
|
||||
|
||||
/* delete it */
|
||||
@@ -360,7 +382,7 @@ checklock_destroy(enum check_lock_type type, struct checked_lock** lock,
|
||||
free(e);
|
||||
}
|
||||
|
||||
/** finish acquiring lock, shared between _(rd|wr||)lock() routines. */
|
||||
/** finish acquiring lock, shared between _(rd|wr||)lock() routines */
|
||||
static void
|
||||
finish_acquire_lock(struct thr_check* thr, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
@@ -511,6 +533,7 @@ static int timed_spinlock(void* arg, struct timespec* to)
|
||||
#ifndef S_SPLINT_S
|
||||
if(time(NULL) >= to->tv_sec)
|
||||
return ETIMEDOUT;
|
||||
usleep(1000); /* in 1/1000000s of a second */
|
||||
#endif
|
||||
}
|
||||
return err;
|
||||
@@ -606,7 +629,7 @@ checklock_unlock(enum check_lock_type type, struct checked_lock* lock,
|
||||
}
|
||||
}
|
||||
|
||||
/** open order info debug file, thr->num must be valid. */
|
||||
/** open order info debug file, thr->num must be valid */
|
||||
static void
|
||||
open_lockorder(struct thr_check* thr)
|
||||
{
|
||||
@@ -626,7 +649,7 @@ open_lockorder(struct thr_check* thr)
|
||||
log_err("fwrite: %s", strerror(errno));
|
||||
}
|
||||
|
||||
/** checklock thread main, Inits thread structure. */
|
||||
/** checklock thread main, Inits thread structure */
|
||||
static void* checklock_main(void* arg)
|
||||
{
|
||||
struct thr_check* thr = (struct thr_check*)arg;
|
||||
@@ -635,7 +658,9 @@ static void* checklock_main(void* arg)
|
||||
/* Hack to get same numbers as in log file */
|
||||
thr->num = *(int*)(thr->arg);
|
||||
log_assert(thr->num < THRDEBUG_MAX_THREADS);
|
||||
log_assert(thread_infos[thr->num] == NULL);
|
||||
/* as an aside, due to this, won't work for libunbound bg thread */
|
||||
if(thread_infos[thr->num] != NULL)
|
||||
log_warn("thread warning, thr->num %d not NULL", thr->num);
|
||||
thread_infos[thr->num] = thr;
|
||||
LOCKRET(pthread_setspecific(thr_debug_key, thr));
|
||||
if(check_locking_order)
|
||||
@@ -699,13 +724,102 @@ checklock_thrcreate(pthread_t* id, void* (*func)(void*), void* arg)
|
||||
LOCKRET(pthread_create(id, NULL, checklock_main, thr));
|
||||
}
|
||||
|
||||
/** signal handler for join timeout, Exits. */
|
||||
/** count number of thread infos */
|
||||
static int
|
||||
count_thread_infos()
|
||||
{
|
||||
int cnt = 0;
|
||||
int i;
|
||||
for(i=0; i<THRDEBUG_MAX_THREADS; i++)
|
||||
if(thread_infos[i])
|
||||
cnt++;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/** print lots of info on a lock */
|
||||
static void
|
||||
lock_debug_info(struct checked_lock* lock)
|
||||
{
|
||||
if(!lock) return;
|
||||
log_info("+++ Lock %x, %d %d create %s %s %d", (int)lock,
|
||||
lock->create_thread, lock->create_instance,
|
||||
lock->create_func, lock->create_file, lock->create_line);
|
||||
log_info("lock type: %s",
|
||||
(lock->type==check_lock_mutex)?"mutex": (
|
||||
(lock->type==check_lock_spinlock)?"spinlock": (
|
||||
(lock->type==check_lock_rwlock)?"rwlock": "badtype")));
|
||||
log_info("lock contention %u, history:%u, hold:%d, wait:%d",
|
||||
(unsigned)lock->contention_count, (unsigned)lock->history_count,
|
||||
lock->hold_count, lock->wait_count);
|
||||
log_info("last touch %s %s %d", lock->holder_func, lock->holder_file,
|
||||
lock->holder_line);
|
||||
log_info("holder thread %d, writeholder thread %d",
|
||||
lock->holder?lock->holder->num:-1,
|
||||
lock->writeholder?lock->writeholder->num:-1);
|
||||
}
|
||||
|
||||
/** print debug locks held by a thread */
|
||||
static void
|
||||
held_debug_info(struct thr_check* thr, struct checked_lock* lock)
|
||||
{
|
||||
if(!lock) return;
|
||||
lock_debug_info(lock);
|
||||
held_debug_info(thr, lock->next_held_lock[thr->num]);
|
||||
}
|
||||
|
||||
/** print debug info for a thread */
|
||||
static void
|
||||
thread_debug_info(struct thr_check* thr)
|
||||
{
|
||||
struct checked_lock* w = NULL;
|
||||
struct checked_lock* f = NULL;
|
||||
struct checked_lock* l = NULL;
|
||||
if(!thr) return;
|
||||
log_info("pthread id is %x", (int)thr->id);
|
||||
log_info("thread func is %x", (int)thr->func);
|
||||
log_info("thread arg is %x (%d)", (int)thr->arg,
|
||||
(thr->arg?*(int*)thr->arg:0));
|
||||
log_info("thread num is %d", thr->num);
|
||||
log_info("locks created %d", thr->locks_created);
|
||||
log_info("open file for lockinfo: %s",
|
||||
thr->order_info?"yes, flushing":"no");
|
||||
fflush(thr->order_info);
|
||||
w = thr->waiting;
|
||||
f = thr->holding_first;
|
||||
l = thr->holding_last;
|
||||
log_info("thread waiting for a lock: %s %x", w?"yes":"no", (int)w);
|
||||
lock_debug_info(w);
|
||||
log_info("thread holding first: %s, last: %s", f?"yes":"no",
|
||||
l?"yes":"no");
|
||||
held_debug_info(thr, f);
|
||||
}
|
||||
|
||||
static void
|
||||
total_debug_info()
|
||||
{
|
||||
int i;
|
||||
log_info("checklocks: supervising %d threads.",
|
||||
count_thread_infos());
|
||||
if(!key_created) {
|
||||
log_info("No thread debug key created yet");
|
||||
}
|
||||
for(i=0; i<THRDEBUG_MAX_THREADS; i++) {
|
||||
if(thread_infos[i]) {
|
||||
log_info("*** Thread %d information: ***", i);
|
||||
thread_debug_info(thread_infos[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** signal handler for join timeout, Exits */
|
||||
static RETSIGTYPE joinalarm(int ATTR_UNUSED(sig))
|
||||
{
|
||||
log_err("join thread timeout. hangup or deadlock. Info follows.");
|
||||
total_debug_info();
|
||||
fatal_exit("join thread timeout. hangup or deadlock.");
|
||||
}
|
||||
|
||||
/** wait for thread with a timeout. */
|
||||
/** wait for thread with a timeout */
|
||||
void
|
||||
checklock_thrjoin(pthread_t thread)
|
||||
{
|
||||
@@ -718,3 +832,20 @@ checklock_thrjoin(pthread_t thread)
|
||||
}
|
||||
|
||||
#endif /* USE_THREAD_DEBUG */
|
||||
|
||||
int order_lock_cmp(const void* e1, const void* e2)
|
||||
{
|
||||
struct order_id* o1 = (struct order_id*)e1;
|
||||
struct order_id* o2 = (struct order_id*)e2;
|
||||
if(o1->thr < o2->thr) return -1;
|
||||
if(o1->thr > o2->thr) return 1;
|
||||
if(o1->instance < o2->instance) return -1;
|
||||
if(o1->instance > o2->instance) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
codeline_cmp(const void* a, const void* b)
|
||||
{
|
||||
return strcmp((const char*)a, (const char*)b);
|
||||
}
|
||||
|
||||
@@ -196,6 +196,13 @@ void lock_protect(void* lock, void* area, size_t size);
|
||||
*/
|
||||
void lock_unprotect(void* lock, void* area);
|
||||
|
||||
/**
|
||||
* Get memory associated with a checked lock
|
||||
* @param lock: the checked lock, any type. (struct checked_lock**).
|
||||
* @return: in bytes, including protected areas.
|
||||
*/
|
||||
size_t lock_get_mem(void* lock);
|
||||
|
||||
/**
|
||||
* Initialise checklock. Sets up internal debug structures.
|
||||
*/
|
||||
@@ -334,4 +341,16 @@ typedef pthread_key_t ub_thread_key_t;
|
||||
|
||||
#endif /* USE_THREAD_DEBUG */
|
||||
|
||||
/** keep track of lock id in lock-verify application */
|
||||
struct order_id {
|
||||
/** the thread id that created it */
|
||||
int thr;
|
||||
/** the instance number of creation */
|
||||
int instance;
|
||||
};
|
||||
/** compare two order_ids */
|
||||
int order_lock_cmp(const void* e1, const void* e2);
|
||||
/** compare two codeline structs for rbtree */
|
||||
int codeline_cmp(const void* a, const void* b);
|
||||
|
||||
#endif /* TESTCODE_CHECK_LOCKS_H */
|
||||
|
||||
+21
-1
@@ -2,8 +2,12 @@
|
||||
|
||||
NEED_SPLINT='00-lint.tpkg'
|
||||
NEED_DOXYGEN='01-doc.tpkg'
|
||||
NEED_LDNS_TESTNS='fwd_no_edns.tpkg fwd_tcp_tc.tpkg fwd_tcp.tpkg fwd_three_service.tpkg fwd_three.tpkg fwd_ttlexpire.tpkg fwd_udp.tpkg fwd_tcp_tc6.tpkg fwd_compress_c00c.tpkg fwd_ancil.tpkg stat_timer.tpkg 05-asynclook.tpkg stream_tcp.tpkg'
|
||||
NEED_XXD='fwd_compress_c00c.tpkg'
|
||||
NEED_NC='fwd_compress_c00c.tpkg'
|
||||
|
||||
cd testdata;
|
||||
sh ../testcode/mini_tpkg.sh clean
|
||||
for test in `ls *.tpkg`; do
|
||||
SKIP=0
|
||||
if echo $NEED_SPLINT | grep $test >/dev/null; then
|
||||
@@ -16,10 +20,26 @@ for test in `ls *.tpkg`; do
|
||||
SKIP=1;
|
||||
fi
|
||||
fi
|
||||
if echo $NEED_LDNS_TESTNS | grep $test >/dev/null; then
|
||||
if test ! -x "`which ldns-testns`"; then
|
||||
SKIP=1;
|
||||
fi
|
||||
fi
|
||||
if echo $NEED_XXD | grep $test >/dev/null; then
|
||||
if test ! -x "`which xxd`"; then
|
||||
SKIP=1;
|
||||
fi
|
||||
fi
|
||||
if echo $NEED_NC | grep $test >/dev/null; then
|
||||
if test ! -x "`which nc`"; then
|
||||
SKIP=1;
|
||||
fi
|
||||
fi
|
||||
if test $SKIP -eq 0; then
|
||||
echo $test
|
||||
tpkg -a ../.. exe $test
|
||||
sh ../testcode/mini_tpkg.sh -a ../.. exe $test
|
||||
else
|
||||
echo "skip $test"
|
||||
fi
|
||||
done
|
||||
sh ../testcode/mini_tpkg.sh report
|
||||
|
||||
+331
-25
@@ -48,11 +48,15 @@
|
||||
#include "testcode/fake_event.h"
|
||||
#include "util/netevent.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "services/listen_dnsport.h"
|
||||
#include "services/outside_network.h"
|
||||
#include "testcode/replay.h"
|
||||
#include "testcode/ldns-testpkts.h"
|
||||
#include "util/log.h"
|
||||
#include <signal.h>
|
||||
|
||||
/** Global variable: the scenario. Saved here for when event_init is done. */
|
||||
static struct replay_scenario* saved_scenario = NULL;
|
||||
@@ -70,7 +74,7 @@ fake_event_cleanup()
|
||||
saved_scenario = NULL;
|
||||
}
|
||||
|
||||
/** helper function that logs a ldns_pkt packet to logfile. */
|
||||
/** helper function that logs a ldns_pkt packet to logfile */
|
||||
static void
|
||||
log_pkt(const char* desc, ldns_pkt* pkt)
|
||||
{
|
||||
@@ -140,6 +144,10 @@ pending_matches_current(struct replay_runtime* runtime,
|
||||
return 0;
|
||||
/* see if any of the pending queries matches */
|
||||
for(p = runtime->pending_list; p; p = p->next) {
|
||||
if(runtime->now->addrlen != 0 &&
|
||||
sockaddr_cmp(&p->addr, p->addrlen, &runtime->now->addr,
|
||||
runtime->now->addrlen) != 0)
|
||||
continue;
|
||||
if((e=find_match(runtime->now->match, p->pkt, p->transport))) {
|
||||
*entry = e;
|
||||
*pend = p;
|
||||
@@ -164,10 +172,15 @@ pending_find_match(struct replay_runtime* runtime, struct entry** entry,
|
||||
struct replay_range* p = runtime->scenario->range_list;
|
||||
while(p) {
|
||||
if(p->start_step <= timenow && timenow <= p->end_step &&
|
||||
(p->addrlen == 0 || sockaddr_cmp(&p->addr, p->addrlen,
|
||||
&pend->addr, pend->addrlen) == 0) &&
|
||||
(*entry = find_match(p->match, pend->pkt, pend->transport))) {
|
||||
log_info("matched query time %d in range [%d, %d] "
|
||||
"with entry line %d", timenow,
|
||||
p->start_step, p->end_step, (*entry)->lineno);
|
||||
if(p->addrlen != 0)
|
||||
log_addr(0, "matched ip", &p->addr, p->addrlen);
|
||||
log_pkt("matched pkt: ", (*entry)->reply_list->reply);
|
||||
return 1;
|
||||
}
|
||||
p = p->next_range;
|
||||
@@ -189,6 +202,7 @@ pending_matches_range(struct replay_runtime* runtime,
|
||||
struct fake_pending* p = runtime->pending_list;
|
||||
/* slow, O(N*N), but it works as advertised with weird matching */
|
||||
while(p) {
|
||||
log_info("check of pending");
|
||||
if(pending_find_match(runtime, entry, p)) {
|
||||
*pend = p;
|
||||
return 1;
|
||||
@@ -262,6 +276,10 @@ answer_callback_from_entry(struct replay_runtime* runtime,
|
||||
struct entry* entry, struct fake_pending* pend)
|
||||
{
|
||||
struct comm_point c;
|
||||
struct comm_reply repinfo;
|
||||
void* cb_arg = pend->cb_arg;
|
||||
comm_point_callback_t* cb = pend->callback;
|
||||
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.fd = -1;
|
||||
c.buffer = ldns_buffer_new(runtime->bufsize);
|
||||
@@ -269,11 +287,15 @@ answer_callback_from_entry(struct replay_runtime* runtime,
|
||||
if(pend->transport == transport_tcp)
|
||||
c.type = comm_tcp;
|
||||
fill_buffer_with_reply(c.buffer, entry, pend->pkt);
|
||||
if((*pend->callback)(&c, pend->cb_arg, NETEVENT_NOERROR, NULL)) {
|
||||
repinfo.c = &c;
|
||||
repinfo.addrlen = pend->addrlen;
|
||||
memcpy(&repinfo.addr, &pend->addr, pend->addrlen);
|
||||
if(!pend->serviced)
|
||||
pending_list_delete(runtime, pend);
|
||||
if((*cb)(&c, cb_arg, NETEVENT_NOERROR, &repinfo)) {
|
||||
fatal_exit("testbound: unexpected: callback returned 1");
|
||||
}
|
||||
ldns_buffer_free(c.buffer);
|
||||
pending_list_delete(runtime, pend);
|
||||
}
|
||||
|
||||
/** Check the now moment answer check event */
|
||||
@@ -288,7 +310,10 @@ answer_check_it(struct replay_runtime* runtime)
|
||||
enum transport_type tr = transport_tcp;
|
||||
if(ans->repinfo.c->type == comm_udp)
|
||||
tr = transport_udp;
|
||||
if(find_match(runtime->now->match, ans->pkt, tr)) {
|
||||
if((runtime->now->addrlen == 0 || sockaddr_cmp(
|
||||
&runtime->now->addr, runtime->now->addrlen,
|
||||
&ans->repinfo.addr, ans->repinfo.addrlen) == 0) &&
|
||||
find_match(runtime->now->match, ans->pkt, tr)) {
|
||||
struct replay_answer *n = ans->next;
|
||||
log_info("testbound matched event entry from line %d",
|
||||
runtime->now->match->lineno);
|
||||
@@ -323,12 +348,19 @@ fake_front_query(struct replay_runtime* runtime, struct replay_moment *todo)
|
||||
memset(&repinfo, 0, sizeof(repinfo));
|
||||
repinfo.c = (struct comm_point*)calloc(1, sizeof(struct comm_point));
|
||||
repinfo.addrlen = (socklen_t)sizeof(struct sockaddr_in);
|
||||
if(todo->addrlen != 0) {
|
||||
repinfo.addrlen = todo->addrlen;
|
||||
memcpy(&repinfo.addr, &todo->addr, todo->addrlen);
|
||||
}
|
||||
repinfo.c->fd = -1;
|
||||
repinfo.c->ev = (struct internal_event*)runtime;
|
||||
repinfo.c->buffer = ldns_buffer_new(runtime->bufsize);
|
||||
repinfo.c->type = comm_udp;
|
||||
if(todo->match->match_transport == transport_tcp)
|
||||
repinfo.c->type = comm_tcp;
|
||||
else repinfo.c->type = comm_udp;
|
||||
fill_buffer_with_reply(repinfo.c->buffer, todo->match, NULL);
|
||||
log_info("testbound: incoming QUERY");
|
||||
log_pkt("query pkt", todo->match->reply_list->reply);
|
||||
/* call the callback for incoming queries */
|
||||
if((*runtime->callback_query)(repinfo.c, runtime->cb_arg,
|
||||
NETEVENT_NOERROR, &repinfo)) {
|
||||
@@ -347,7 +379,11 @@ fake_pending_callback(struct replay_runtime* runtime,
|
||||
struct replay_moment* todo, int error)
|
||||
{
|
||||
struct fake_pending* p = runtime->pending_list;
|
||||
struct comm_reply repinfo;
|
||||
struct comm_point c;
|
||||
void* cb_arg = p->cb_arg;
|
||||
comm_point_callback_t* cb = p->callback;
|
||||
|
||||
memset(&c, 0, sizeof(c));
|
||||
if(!p) fatal_exit("No pending queries.");
|
||||
log_assert(todo->qname == NULL); /* or find that one */
|
||||
@@ -357,13 +393,17 @@ fake_pending_callback(struct replay_runtime* runtime,
|
||||
c.type = comm_tcp;
|
||||
if(todo->evt_type == repevt_back_reply && todo->match) {
|
||||
fill_buffer_with_reply(c.buffer, todo->match, p->pkt);
|
||||
}
|
||||
if((*p->callback)(&c, p->cb_arg, error, NULL)) {
|
||||
}
|
||||
repinfo.c = &c;
|
||||
repinfo.addrlen = p->addrlen;
|
||||
memcpy(&repinfo.addr, &p->addr, p->addrlen);
|
||||
if(!p->serviced)
|
||||
pending_list_delete(runtime, p);
|
||||
if((*cb)(&c, cb_arg, error, &repinfo)) {
|
||||
fatal_exit("unexpected: pending callback returned 1");
|
||||
}
|
||||
/* delete the pending item. */
|
||||
ldns_buffer_free(c.buffer);
|
||||
pending_list_delete(runtime, p);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -473,19 +513,25 @@ run_scenario(struct replay_runtime* runtime)
|
||||
} while(runtime->now);
|
||||
|
||||
if(runtime->pending_list) {
|
||||
struct fake_pending* p;
|
||||
log_err("testbound: there are still messages pending.");
|
||||
for(p = runtime->pending_list; p; p=p->next)
|
||||
log_pkt("pending msg", p->pkt);
|
||||
fatal_exit("testbound: there are still messages pending.");
|
||||
}
|
||||
if(runtime->answer_list) {
|
||||
fatal_exit("testbound: there are unmatched answers.");
|
||||
}
|
||||
log_info("testbound: exiting fake runloop.");
|
||||
runtime->exit_cleanly = 1;
|
||||
}
|
||||
|
||||
/*********** Dummy routines ***********/
|
||||
|
||||
struct listen_dnsport*
|
||||
listen_create(struct comm_base* base, struct listen_port* ATTR_UNUSED(ports),
|
||||
size_t bufsize, comm_point_callback_t* cb, void* cb_arg)
|
||||
size_t bufsize, int ATTR_UNUSED(tcp_accept_count),
|
||||
comm_point_callback_t* cb, void* cb_arg)
|
||||
{
|
||||
struct replay_runtime* runtime = (struct replay_runtime*)base;
|
||||
struct listen_dnsport* l= calloc(1, sizeof(struct listen_dnsport));
|
||||
@@ -551,13 +597,19 @@ comm_base_dispatch(struct comm_base* b)
|
||||
{
|
||||
struct replay_runtime* runtime = (struct replay_runtime*)b;
|
||||
run_scenario(runtime);
|
||||
if(runtime->sig_cb)
|
||||
(*runtime->sig_cb)(SIGTERM, runtime->sig_cb_arg);
|
||||
else exit(0); /* OK exit when LIBEVENT_SIGNAL_PROBLEM exists */
|
||||
}
|
||||
|
||||
void
|
||||
comm_base_exit(struct comm_base* ATTR_UNUSED(b))
|
||||
comm_base_exit(struct comm_base* b)
|
||||
{
|
||||
/* some sort of failure */
|
||||
fatal_exit("testbound: comm_base_exit was called.");
|
||||
struct replay_runtime* runtime = (struct replay_runtime*)b;
|
||||
if(!runtime->exit_cleanly) {
|
||||
/* some sort of failure */
|
||||
fatal_exit("testbound: comm_base_exit was called.");
|
||||
}
|
||||
}
|
||||
|
||||
struct comm_signal*
|
||||
@@ -610,15 +662,6 @@ comm_point_send_reply(struct comm_reply* repinfo)
|
||||
log_pkt("reply pkt: ", ans->pkt);
|
||||
}
|
||||
|
||||
void
|
||||
comm_point_send_reply_iov(struct comm_reply* repinfo, struct iovec* iov,
|
||||
size_t iovlen)
|
||||
{
|
||||
/* skip tcp len at [0]. */
|
||||
write_iov_buffer(repinfo->c->buffer, iov+1, iovlen-1);
|
||||
comm_point_send_reply(repinfo);
|
||||
}
|
||||
|
||||
void
|
||||
comm_point_drop_reply(struct comm_reply* repinfo)
|
||||
{
|
||||
@@ -634,7 +677,8 @@ outside_network_create(struct comm_base* base, size_t bufsize,
|
||||
size_t ATTR_UNUSED(num_ports), char** ATTR_UNUSED(ifs),
|
||||
int ATTR_UNUSED(num_ifs), int ATTR_UNUSED(do_ip4),
|
||||
int ATTR_UNUSED(do_ip6), int ATTR_UNUSED(port_base),
|
||||
size_t ATTR_UNUSED(num_tcp))
|
||||
size_t ATTR_UNUSED(num_tcp), struct infra_cache* ATTR_UNUSED(infra),
|
||||
struct ub_randstate* ATTR_UNUSED(rnd))
|
||||
{
|
||||
struct outside_network* outnet = calloc(1,
|
||||
sizeof(struct outside_network));
|
||||
@@ -656,7 +700,7 @@ outside_network_delete(struct outside_network* outnet)
|
||||
free(outnet);
|
||||
}
|
||||
|
||||
void
|
||||
struct pending*
|
||||
pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
|
||||
comm_point_callback_t* callback, void* callback_arg,
|
||||
@@ -676,9 +720,11 @@ pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
pend->addrlen = addrlen;
|
||||
pend->callback = callback;
|
||||
pend->cb_arg = callback_arg;
|
||||
pend->timeout = timeout;
|
||||
pend->timeout = timeout/1000;
|
||||
pend->transport = transport_udp;
|
||||
pend->pkt = NULL;
|
||||
pend->serviced = 0;
|
||||
pend->runtime = runtime;
|
||||
status = ldns_buffer2pkt_wire(&pend->pkt, packet);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
log_err("ldns error parsing udp output packet: %s",
|
||||
@@ -689,6 +735,9 @@ pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
|
||||
/* see if it matches the current moment */
|
||||
if(runtime->now && runtime->now->evt_type == repevt_back_query &&
|
||||
(runtime->now->addrlen == 0 || sockaddr_cmp(
|
||||
&runtime->now->addr, runtime->now->addrlen,
|
||||
&pend->addr, pend->addrlen) == 0) &&
|
||||
find_match(runtime->now->match, pend->pkt, pend->transport)) {
|
||||
log_info("testbound: matched pending to event. "
|
||||
"advance time between events.");
|
||||
@@ -701,9 +750,10 @@ pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
/* add to list */
|
||||
pend->next = runtime->pending_list;
|
||||
runtime->pending_list = pend;
|
||||
return (struct pending*)pend;
|
||||
}
|
||||
|
||||
void
|
||||
struct waiting_tcp*
|
||||
pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
|
||||
comm_point_callback_t* callback, void* callback_arg,
|
||||
@@ -726,6 +776,8 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
pend->timeout = timeout;
|
||||
pend->transport = transport_tcp;
|
||||
pend->pkt = NULL;
|
||||
pend->runtime = runtime;
|
||||
pend->serviced = 0;
|
||||
status = ldns_buffer2pkt_wire(&pend->pkt, packet);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
log_err("ldns error parsing tcp output packet: %s",
|
||||
@@ -736,6 +788,9 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
|
||||
/* see if it matches the current moment */
|
||||
if(runtime->now && runtime->now->evt_type == repevt_back_query &&
|
||||
(runtime->now->addrlen == 0 || sockaddr_cmp(
|
||||
&runtime->now->addr, runtime->now->addrlen,
|
||||
&pend->addr, pend->addrlen) == 0) &&
|
||||
find_match(runtime->now->match, pend->pkt, pend->transport)) {
|
||||
log_info("testbound: matched pending to event. "
|
||||
"advance time between events.");
|
||||
@@ -748,6 +803,112 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
|
||||
/* add to list */
|
||||
pend->next = runtime->pending_list;
|
||||
runtime->pending_list = pend;
|
||||
return (struct waiting_tcp*)pend;
|
||||
}
|
||||
|
||||
struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
|
||||
uint16_t flags, int dnssec, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, comm_point_callback_t* callback,
|
||||
void* callback_arg, ldns_buffer* ATTR_UNUSED(buff),
|
||||
int (*arg_compare)(void*,void*))
|
||||
{
|
||||
struct replay_runtime* runtime = (struct replay_runtime*)outnet->base;
|
||||
struct fake_pending* pend = (struct fake_pending*)calloc(1,
|
||||
sizeof(struct fake_pending));
|
||||
ldns_status status;
|
||||
(void)arg_compare;
|
||||
log_assert(pend);
|
||||
log_nametypeclass(VERB_OPS, "pending serviced query",
|
||||
qname, qtype, qclass);
|
||||
verbose(VERB_OPS, "pending serviced query flags%s%s%s%s",
|
||||
(flags&BIT_RD)?" RD":"", (flags&BIT_CD)?" CD":"",
|
||||
(flags&~(BIT_RD|BIT_CD))?" MORE":"", (dnssec)?" DO":"");
|
||||
|
||||
/* create packet with EDNS */
|
||||
pend->buffer = ldns_buffer_new(512);
|
||||
log_assert(pend->buffer);
|
||||
ldns_buffer_write_u16(pend->buffer, 0); /* id */
|
||||
ldns_buffer_write_u16(pend->buffer, flags);
|
||||
ldns_buffer_write_u16(pend->buffer, 1); /* qdcount */
|
||||
ldns_buffer_write_u16(pend->buffer, 0); /* ancount */
|
||||
ldns_buffer_write_u16(pend->buffer, 0); /* nscount */
|
||||
ldns_buffer_write_u16(pend->buffer, 0); /* arcount */
|
||||
ldns_buffer_write(pend->buffer, qname, qnamelen);
|
||||
ldns_buffer_write_u16(pend->buffer, qtype);
|
||||
ldns_buffer_write_u16(pend->buffer, qclass);
|
||||
ldns_buffer_flip(pend->buffer);
|
||||
if(1) {
|
||||
/* add edns */
|
||||
struct edns_data edns;
|
||||
edns.edns_present = 1;
|
||||
edns.ext_rcode = 0;
|
||||
edns.edns_version = EDNS_ADVERTISED_VERSION;
|
||||
edns.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
edns.bits = 0;
|
||||
if(dnssec)
|
||||
edns.bits = EDNS_DO;
|
||||
attach_edns_record(pend->buffer, &edns);
|
||||
}
|
||||
memcpy(&pend->addr, addr, addrlen);
|
||||
pend->addrlen = addrlen;
|
||||
pend->callback = callback;
|
||||
pend->cb_arg = callback_arg;
|
||||
pend->timeout = UDP_AUTH_QUERY_TIMEOUT;
|
||||
pend->transport = transport_udp; /* pretend UDP */
|
||||
pend->pkt = NULL;
|
||||
pend->runtime = runtime;
|
||||
pend->serviced = 1;
|
||||
status = ldns_buffer2pkt_wire(&pend->pkt, pend->buffer);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
log_err("ldns error parsing serviced output packet: %s",
|
||||
ldns_get_errorstr_by_id(status));
|
||||
fatal_exit("internal error");
|
||||
}
|
||||
/*log_pkt("pending serviced query: ", pend->pkt);*/
|
||||
|
||||
/* see if it matches the current moment */
|
||||
if(runtime->now && runtime->now->evt_type == repevt_back_query &&
|
||||
(runtime->now->addrlen == 0 || sockaddr_cmp(
|
||||
&runtime->now->addr, runtime->now->addrlen,
|
||||
&pend->addr, pend->addrlen) == 0) &&
|
||||
find_match(runtime->now->match, pend->pkt, pend->transport)) {
|
||||
log_info("testbound: matched pending to event. "
|
||||
"advance time between events.");
|
||||
log_info("testbound: do STEP %d %s", runtime->now->time_step,
|
||||
repevt_string(runtime->now->evt_type));
|
||||
advance_moment(runtime);
|
||||
/* still create the pending, because we need it to callback */
|
||||
}
|
||||
log_info("testbound: created fake pending");
|
||||
/* add to list */
|
||||
pend->next = runtime->pending_list;
|
||||
runtime->pending_list = pend;
|
||||
return (struct serviced_query*)pend;
|
||||
}
|
||||
|
||||
void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg)
|
||||
{
|
||||
struct fake_pending* pend = (struct fake_pending*)sq;
|
||||
struct replay_runtime* runtime = pend->runtime;
|
||||
/* delete from the list */
|
||||
struct fake_pending* p = runtime->pending_list, *prev=NULL;
|
||||
while(p) {
|
||||
if(p == pend) {
|
||||
log_assert(p->cb_arg == cb_arg);
|
||||
log_info("serviced pending delete");
|
||||
if(prev)
|
||||
prev->next = p->next;
|
||||
else runtime->pending_list = p->next;
|
||||
ldns_buffer_free(p->buffer);
|
||||
ldns_pkt_free(p->pkt);
|
||||
free(p);
|
||||
return;
|
||||
}
|
||||
prev = p;
|
||||
p = p->next;
|
||||
}
|
||||
log_info("double delete of pending serviced query");
|
||||
}
|
||||
|
||||
struct listen_port* listening_ports_open(struct config_file* ATTR_UNUSED(cfg))
|
||||
@@ -782,4 +943,149 @@ void comm_point_delete(struct comm_point* c)
|
||||
free(c);
|
||||
}
|
||||
|
||||
size_t listen_get_mem(struct listen_dnsport* ATTR_UNUSED(listen))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t outnet_get_mem(struct outside_network* ATTR_UNUSED(outnet))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t comm_point_get_mem(struct comm_point* ATTR_UNUSED(c))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t serviced_get_mem(struct serviced_query* ATTR_UNUSED(c))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* fake for fptr wlist */
|
||||
int outnet_udp_cb(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 outnet_tcp_cb(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 pending_udp_timer_cb(void *ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void outnet_tcptimer(void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void comm_point_udp_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(event),
|
||||
void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void comm_point_udp_ancil_callback(int ATTR_UNUSED(fd),
|
||||
short ATTR_UNUSED(event), void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void comm_point_tcp_accept_callback(int ATTR_UNUSED(fd),
|
||||
short ATTR_UNUSED(event), void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void comm_point_tcp_handle_callback(int ATTR_UNUSED(fd),
|
||||
short ATTR_UNUSED(event), void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void comm_timer_callback(int ATTR_UNUSED(fd),
|
||||
short ATTR_UNUSED(event), void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void comm_signal_callback(int ATTR_UNUSED(fd),
|
||||
short ATTR_UNUSED(event), void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void comm_point_local_handle_callback(int ATTR_UNUSED(fd),
|
||||
short ATTR_UNUSED(event), void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void comm_point_raw_handle_callback(int ATTR_UNUSED(fd),
|
||||
short ATTR_UNUSED(event), void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
int serviced_udp_callback(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 serviced_tcp_callback(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 pending_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serviced_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* no statistics timers in testbound */
|
||||
struct comm_timer* comm_timer_create(struct comm_base* ATTR_UNUSED(base),
|
||||
void (*cb)(void*), void* ATTR_UNUSED(cb_arg))
|
||||
{
|
||||
(void)cb;
|
||||
return malloc(1);
|
||||
}
|
||||
|
||||
void comm_timer_disable(struct comm_timer* ATTR_UNUSED(timer))
|
||||
{
|
||||
}
|
||||
|
||||
void comm_timer_set(struct comm_timer* ATTR_UNUSED(timer),
|
||||
struct timeval* ATTR_UNUSED(tv))
|
||||
{
|
||||
}
|
||||
|
||||
void comm_timer_delete(struct comm_timer* timer)
|
||||
{
|
||||
free(timer);
|
||||
}
|
||||
|
||||
/*********** End of Dummy routines ***********/
|
||||
|
||||
+40
-12
@@ -31,7 +31,7 @@ struct sockaddr_storage;
|
||||
/** string to show in warnings and errors */
|
||||
static const char* prog_name = "ldns-testpkts";
|
||||
|
||||
/** logging routine, provided by caller. */
|
||||
/** logging routine, provided by caller */
|
||||
void verbose(int lvl, const char* msg, ...);
|
||||
|
||||
/** print error and exit */
|
||||
@@ -68,7 +68,7 @@ static bool str_keyword(const char** str, const char* keyword)
|
||||
if(strncmp(*str, keyword, len) != 0)
|
||||
return false;
|
||||
*str += len;
|
||||
while(isspace(**str))
|
||||
while(isspace((int)**str))
|
||||
(*str)++;
|
||||
return true;
|
||||
}
|
||||
@@ -108,6 +108,8 @@ static void matchline(const char* line, struct entry* e)
|
||||
e->match_all = true;
|
||||
} else if(str_keyword(&parse, "ttl")) {
|
||||
e->match_ttl = true;
|
||||
} else if(str_keyword(&parse, "DO")) {
|
||||
e->match_do = true;
|
||||
} else if(str_keyword(&parse, "UDP")) {
|
||||
e->match_transport = transport_udp;
|
||||
} else if(str_keyword(&parse, "TCP")) {
|
||||
@@ -118,7 +120,7 @@ static void matchline(const char* line, struct entry* e)
|
||||
error("expected = or : in MATCH: %s", line);
|
||||
parse++;
|
||||
e->ixfr_soa_serial = (uint32_t)strtol(parse, (char**)&parse, 10);
|
||||
while(isspace(*parse))
|
||||
while(isspace((int)*parse))
|
||||
parse++;
|
||||
} else {
|
||||
error("could not parse MATCH: '%s'", parse);
|
||||
@@ -155,6 +157,8 @@ static void replyline(const char* line, ldns_pkt *reply)
|
||||
ldns_pkt_set_rcode(reply, LDNS_RCODE_NXDOMAIN);
|
||||
} else if(str_keyword(&parse, "NOTIMPL")) {
|
||||
ldns_pkt_set_rcode(reply, LDNS_RCODE_NOTIMPL);
|
||||
} else if(str_keyword(&parse, "REFUSED")) {
|
||||
ldns_pkt_set_rcode(reply, LDNS_RCODE_REFUSED);
|
||||
} else if(str_keyword(&parse, "YXDOMAIN")) {
|
||||
ldns_pkt_set_rcode(reply, LDNS_RCODE_YXDOMAIN);
|
||||
} else if(str_keyword(&parse, "YXRRSET")) {
|
||||
@@ -180,6 +184,9 @@ static void replyline(const char* line, ldns_pkt *reply)
|
||||
ldns_pkt_set_ra(reply, true);
|
||||
} else if(str_keyword(&parse, "AD")) {
|
||||
ldns_pkt_set_ad(reply, true);
|
||||
} else if(str_keyword(&parse, "DO")) {
|
||||
ldns_pkt_set_edns_udp_size(reply, 4096);
|
||||
ldns_pkt_set_edns_do(reply, true);
|
||||
} else {
|
||||
error("could not parse REPLY: '%s'", parse);
|
||||
}
|
||||
@@ -196,13 +203,15 @@ static void adjustline(const char* line, struct entry* e,
|
||||
return;
|
||||
if(str_keyword(&parse, "copy_id")) {
|
||||
e->copy_id = true;
|
||||
} else if(str_keyword(&parse, "copy_query")) {
|
||||
e->copy_query = true;
|
||||
} else if(str_keyword(&parse, "sleep=")) {
|
||||
e->sleeptime = (unsigned int) strtol(parse, (char**)&parse, 10);
|
||||
while(isspace(*parse))
|
||||
while(isspace((int)*parse))
|
||||
parse++;
|
||||
} else if(str_keyword(&parse, "packet_sleep=")) {
|
||||
pkt->packet_sleep = (unsigned int) strtol(parse, (char**)&parse, 10);
|
||||
while(isspace(*parse))
|
||||
while(isspace((int)*parse))
|
||||
parse++;
|
||||
} else {
|
||||
error("could not parse ADJUST: '%s'", parse);
|
||||
@@ -220,11 +229,13 @@ static struct entry* new_entry()
|
||||
e->match_qname = false;
|
||||
e->match_all = false;
|
||||
e->match_ttl = false;
|
||||
e->match_do = false;
|
||||
e->match_serial = false;
|
||||
e->ixfr_soa_serial = 0;
|
||||
e->match_transport = transport_any;
|
||||
e->reply_list = NULL;
|
||||
e->copy_id = false;
|
||||
e->copy_query = false;
|
||||
e->sleeptime = 0;
|
||||
e->next = NULL;
|
||||
return e;
|
||||
@@ -380,7 +391,7 @@ get_origin(const char* name, int lineno, ldns_rdf** origin, char* parse)
|
||||
*origin = NULL;
|
||||
|
||||
end=parse;
|
||||
while(!isspace(*end) && !isendline(*end))
|
||||
while(!isspace((int)*end) && !isendline(*end))
|
||||
end++;
|
||||
store = *end;
|
||||
*end = 0;
|
||||
@@ -394,7 +405,7 @@ get_origin(const char* name, int lineno, ldns_rdf** origin, char* parse)
|
||||
|
||||
/* Reads one entry from file. Returns entry or NULL on error. */
|
||||
struct entry*
|
||||
read_entry(FILE* in, const char* name, int *lineno, uint16_t* default_ttl,
|
||||
read_entry(FILE* in, const char* name, int *lineno, uint32_t* default_ttl,
|
||||
ldns_rdf** origin, ldns_rdf** prev_rr)
|
||||
{
|
||||
struct entry* current = NULL;
|
||||
@@ -410,7 +421,7 @@ read_entry(FILE* in, const char* name, int *lineno, uint16_t* default_ttl,
|
||||
parse = line;
|
||||
(*lineno) ++;
|
||||
|
||||
while(isspace(*parse))
|
||||
while(isspace((int)*parse))
|
||||
parse++;
|
||||
/* test for keywords */
|
||||
if(isendline(*parse))
|
||||
@@ -428,7 +439,7 @@ read_entry(FILE* in, const char* name, int *lineno, uint16_t* default_ttl,
|
||||
get_origin(name, *lineno, origin, (char*)parse);
|
||||
continue;
|
||||
} else if(str_keyword(&parse, "$TTL")) {
|
||||
*default_ttl = (uint16_t)atoi(parse);
|
||||
*default_ttl = (uint32_t)atoi(parse);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -493,7 +504,7 @@ read_entry(FILE* in, const char* name, int *lineno, uint16_t* default_ttl,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** reads the canned reply file and returns a list of structs */
|
||||
/* reads the canned reply file and returns a list of structs */
|
||||
struct entry*
|
||||
read_datafile(const char* name)
|
||||
{
|
||||
@@ -502,7 +513,7 @@ read_datafile(const char* name)
|
||||
struct entry* current = NULL;
|
||||
FILE *in;
|
||||
int lineno = 0;
|
||||
uint16_t default_ttl = 0;
|
||||
uint32_t default_ttl = 0;
|
||||
ldns_rdf* origin = NULL;
|
||||
ldns_rdf* prev_rr = NULL;
|
||||
int entry_num = 0;
|
||||
@@ -523,6 +534,8 @@ read_datafile(const char* name)
|
||||
verbose(1, "%s: Read %d entries\n", prog_name, entry_num);
|
||||
|
||||
fclose(in);
|
||||
ldns_rdf_deep_free(origin);
|
||||
ldns_rdf_deep_free(prev_rr);
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -632,7 +645,7 @@ match_all(ldns_pkt* q, ldns_pkt* p, bool mttl)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** finds entry in list, or returns NULL */
|
||||
/* finds entry in list, or returns NULL */
|
||||
struct entry*
|
||||
find_match(struct entry* entries, ldns_pkt* query_pkt,
|
||||
enum transport_type transport)
|
||||
@@ -663,6 +676,10 @@ find_match(struct entry* entries, ldns_pkt* query_pkt,
|
||||
verbose(3, "bad serial\n");
|
||||
continue;
|
||||
}
|
||||
if(p->match_do && !ldns_pkt_edns_do(query_pkt)) {
|
||||
verbose(3, "no DO bit set\n");
|
||||
continue;
|
||||
}
|
||||
if(p->match_transport != transport_any && p->match_transport != transport) {
|
||||
verbose(3, "bad transport\n");
|
||||
continue;
|
||||
@@ -683,6 +700,12 @@ adjust_packet(struct entry* match, ldns_pkt* answer_pkt, ldns_pkt* query_pkt)
|
||||
/* copy & adjust packet */
|
||||
if(match->copy_id)
|
||||
ldns_pkt_set_id(answer_pkt, ldns_pkt_id(query_pkt));
|
||||
if(match->copy_query) {
|
||||
ldns_rr_list* list = ldns_pkt_get_section_clone(query_pkt,
|
||||
LDNS_SECTION_QUESTION);
|
||||
ldns_rr_list_deep_free(ldns_pkt_question(answer_pkt));
|
||||
ldns_pkt_set_question(answer_pkt, list);
|
||||
}
|
||||
if(match->sleeptime > 0) {
|
||||
verbose(3, "sleeping for %d seconds\n", match->sleeptime);
|
||||
sleep(match->sleeptime);
|
||||
@@ -758,9 +781,14 @@ handle_query(uint8_t* inbuf, ssize_t inlen, struct entry* entries, int* count,
|
||||
answer_pkt = NULL;
|
||||
} else {
|
||||
verbose(3, "Could not parse hex data (%s), sending hex data directly.\n", ldns_get_errorstr_by_id(status));
|
||||
/* still try to adjust ID */
|
||||
answer_size = ldns_buffer_capacity(p->reply_from_hex);
|
||||
outbuf = LDNS_XMALLOC(uint8_t, answer_size);
|
||||
memcpy(outbuf, ldns_buffer_export(p->reply_from_hex), answer_size);
|
||||
if(entry->copy_id) {
|
||||
ldns_write_uint16(outbuf,
|
||||
ldns_pkt_id(query_pkt));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
answer_pkt = ldns_pkt_clone(p->reply);
|
||||
|
||||
@@ -45,19 +45,22 @@
|
||||
; 'serial=1023' makes the query match if ixfr serial is 1023.
|
||||
; 'all' has to match header byte for byte and all rrs in packet.
|
||||
; 'ttl' used with all, rrs in packet must also have matching TTLs.
|
||||
; 'DO' will match only queries with DO bit set.
|
||||
MATCH [opcode] [qtype] [qname] [serial=<value>] [all] [ttl]
|
||||
MATCH [UDP|TCP]
|
||||
MATCH [UDP|TCP] DO
|
||||
MATCH ...
|
||||
; Then the REPLY header is specified.
|
||||
REPLY opcode, rcode or flags.
|
||||
(opcode) QUERY IQUERY STATUS NOTIFY UPDATE
|
||||
(rcode) NOERROR FORMERR SERVFAIL NXDOMAIN NOTIMPL YXDOMAIN
|
||||
YXRRSET NXRRSET NOTAUTH NOTZONE
|
||||
(flags) QR AA TC RD CD RA AD
|
||||
(flags) QR AA TC RD CD RA AD DO
|
||||
REPLY ...
|
||||
; any additional actions to do.
|
||||
; 'copy_id' copies the ID from the query to the answer.
|
||||
ADJUST copy_id
|
||||
; 'copy_query' copies the query name, type and class to the answer.
|
||||
ADJUST copy_query
|
||||
; 'sleep=10' sleeps for 10 seconds before giving the answer (TCP is open)
|
||||
ADJUST [sleep=<num>] ; sleep before giving any reply
|
||||
ADJUST [packet_sleep=<num>] ; sleep before this packet in sequence
|
||||
@@ -75,7 +78,7 @@
|
||||
; with the SECTION keywords (only SECTION QUERY
|
||||
; is used to match queries). If the data cannot
|
||||
; be parsed, ADJUST rules for the answer packet
|
||||
; are ignored
|
||||
; are ignored. Only copy_id is done.
|
||||
HEX_ANSWER_END
|
||||
ENTRY_END
|
||||
|
||||
@@ -160,6 +163,8 @@ struct entry {
|
||||
bool match_all;
|
||||
/** match ttls in the packet */
|
||||
bool match_ttl;
|
||||
/** match DO bit */
|
||||
bool match_do;
|
||||
/** match query serial with this value. */
|
||||
uint32_t ixfr_soa_serial;
|
||||
/** match on UDP/TCP */
|
||||
@@ -171,6 +176,8 @@ struct entry {
|
||||
/** how to adjust the reply packet */
|
||||
/** copy over the ID from the query into the answer */
|
||||
bool copy_id;
|
||||
/** copy the query nametypeclass from query into the answer */
|
||||
bool copy_query;
|
||||
/** in seconds */
|
||||
unsigned int sleeptime;
|
||||
|
||||
@@ -207,7 +214,7 @@ void delete_entry(struct entry* list);
|
||||
* @return: The entry read (malloced) or NULL if no entry could be read.
|
||||
*/
|
||||
struct entry* read_entry(FILE* in, const char* name, int *lineno,
|
||||
uint16_t* default_ttl, ldns_rdf** origin, ldns_rdf** prev_rr);
|
||||
uint32_t* default_ttl, ldns_rdf** origin, ldns_rdf** prev_rr);
|
||||
|
||||
/**
|
||||
* finds entry in list, or returns NULL.
|
||||
|
||||
+8
-23
@@ -46,18 +46,12 @@
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/rbtree.h"
|
||||
#include "util/locks.h"
|
||||
#include "testcode/checklocks.h"
|
||||
|
||||
/* --- data structures --- */
|
||||
struct lock_ref;
|
||||
|
||||
/** key for lock lookup */
|
||||
struct order_id {
|
||||
/** the thread id that created it */
|
||||
int thr;
|
||||
/** the instance number of creation */
|
||||
int instance;
|
||||
};
|
||||
|
||||
/** a lock */
|
||||
struct order_lock {
|
||||
/** rbnode in all tree */
|
||||
@@ -103,18 +97,6 @@ usage()
|
||||
printf("lock_verify <trace files>\n");
|
||||
}
|
||||
|
||||
/** compare two order_ids */
|
||||
int order_lock_cmp(const void* e1, const void* e2)
|
||||
{
|
||||
struct order_id* o1 = (struct order_id*)e1;
|
||||
struct order_id* o2 = (struct order_id*)e2;
|
||||
if(o1->thr < o2->thr) return -1;
|
||||
if(o1->thr > o2->thr) return 1;
|
||||
if(o1->instance < o2->instance) return -1;
|
||||
if(o1->instance > o2->instance) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** read header entry.
|
||||
* @param in: file to read header of.
|
||||
* @return: False if it does not belong to the rest. */
|
||||
@@ -139,6 +121,9 @@ read_header(FILE* in)
|
||||
the_time = t;
|
||||
the_pid = p;
|
||||
memset(threads, 0, 256*sizeof(int));
|
||||
if(thrno >= 256) {
|
||||
fatal_exit("Thread number too big. %d", thrno);
|
||||
}
|
||||
threads[thrno] = 1;
|
||||
have_values = 1;
|
||||
printf(" trace %d from pid %u on %s", thrno,
|
||||
@@ -306,7 +291,7 @@ static void found_cycle(struct lock_ref* visit, int level)
|
||||
}
|
||||
}
|
||||
|
||||
/** Detect cycle by comparing visited now with all (unvisited) bigger nodes. */
|
||||
/** Detect cycle by comparing visited now with all (unvisited) bigger nodes */
|
||||
static int detect_cycle(struct lock_ref* visit, struct lock_ref* from)
|
||||
{
|
||||
struct lock_ref* p = from;
|
||||
@@ -332,7 +317,7 @@ static void search_cycle(struct lock_ref* visit, int level,
|
||||
/* check for cycle */
|
||||
if(detect_cycle(visit, from) && level != 0) {
|
||||
found_cycle(visit, level);
|
||||
return;
|
||||
fatal_exit("found lock order cycle");
|
||||
}
|
||||
/* recurse */
|
||||
if(!visit->lock->visited)
|
||||
@@ -400,7 +385,7 @@ main(int argc, char* argv[])
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
log_init(NULL);
|
||||
log_init(NULL, 0, NULL);
|
||||
log_ident_set("lock-verify");
|
||||
/* init */
|
||||
all_locks = rbtree_create(order_lock_cmp);
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* testcode/memstats.c - debug tool to show memory allocation statistics.
|
||||
*
|
||||
* 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 program reads a log file and prints the memory allocation summed
|
||||
* up.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/rbtree.h"
|
||||
#include "util/locks.h"
|
||||
#include "testcode/checklocks.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
/**
|
||||
* The allocation statistics block
|
||||
*/
|
||||
struct codeline {
|
||||
/** rbtree node */
|
||||
rbnode_t node;
|
||||
/** the name of the file:linenumber */
|
||||
char* codeline;
|
||||
/** the name of the function */
|
||||
char* func;
|
||||
/** number of bytes allocated */
|
||||
uint64_t alloc;
|
||||
/** number of bytes freed */
|
||||
uint64_t free;
|
||||
/** number allocations and frees */
|
||||
uint64_t calls;
|
||||
};
|
||||
|
||||
/** print usage and exit */
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
printf("usage: memstats <logfile>\n");
|
||||
printf("statistics are printed on stdout.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** match logfile line to see if it needs accounting processing */
|
||||
static int
|
||||
match(char* line)
|
||||
{
|
||||
/* f.e.:
|
||||
* [1187340064] unbound[24604:0] info: ul/rb.c:81 r_create malloc(12)
|
||||
* 0123456789 123456789 123456789 123456789
|
||||
*/
|
||||
if(strlen(line) < 36) /* up to 'info: ' */
|
||||
return 0;
|
||||
if(strncmp(line+30, "info: ", 6) != 0)
|
||||
return 0;
|
||||
if(strncmp(line+36, "stat ", 5) == 0)
|
||||
return 0; /* skip the hex dumps */
|
||||
if(strstr(line+36, "malloc("))
|
||||
return 1;
|
||||
else if(strstr(line+36, "calloc("))
|
||||
return 1;
|
||||
/* skip reallocs */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** find or alloc codeline in tree */
|
||||
static struct codeline*
|
||||
get_codeline(rbtree_t* tree, char* key, char* func)
|
||||
{
|
||||
struct codeline* cl = (struct codeline*)rbtree_search(tree, key);
|
||||
if(!cl) {
|
||||
cl = calloc(1, sizeof(*cl));
|
||||
if(!cl) return 0;
|
||||
cl->codeline = strdup(key);
|
||||
if(!cl->codeline) return 0;
|
||||
cl->func = strdup(func);
|
||||
if(!cl->func) return 0;
|
||||
cl->alloc = 0;
|
||||
cl->node.key = cl->codeline;
|
||||
(void)rbtree_insert(tree, &cl->node);
|
||||
}
|
||||
return cl;
|
||||
}
|
||||
|
||||
/** read up the malloc stats */
|
||||
static void
|
||||
read_malloc_stat(char* line, rbtree_t* tree)
|
||||
{
|
||||
char codeline[10240];
|
||||
char name[10240];
|
||||
int skip = 0;
|
||||
long num = 0;
|
||||
struct codeline* cl = 0;
|
||||
if(sscanf(line+36, "%s %s %n", codeline, name, &skip) != 2) {
|
||||
printf("%s\n%s\n", line, line+36);
|
||||
fatal_exit("unhandled malloc");
|
||||
}
|
||||
if(sscanf(line+36+skip+7, "%ld", &num) != 1) {
|
||||
printf("%s\n%s\n", line, line+36+skip+7);
|
||||
fatal_exit("unhandled malloc");
|
||||
}
|
||||
cl = get_codeline(tree, codeline, name);
|
||||
if(!cl)
|
||||
fatal_exit("alloc failure");
|
||||
cl->alloc += num;
|
||||
cl->calls ++;
|
||||
}
|
||||
|
||||
/** read up the calloc stats */
|
||||
static void
|
||||
read_calloc_stat(char* line, rbtree_t* tree)
|
||||
{
|
||||
char codeline[10240];
|
||||
char name[10240];
|
||||
int skip = 0;
|
||||
long num = 0, sz = 0;
|
||||
struct codeline* cl = 0;
|
||||
if(sscanf(line+36, "%s %s %n", codeline, name, &skip) != 2) {
|
||||
printf("%s\n%s\n", line, line+36);
|
||||
fatal_exit("unhandled calloc");
|
||||
}
|
||||
if(sscanf(line+36+skip+7, "%ld, %ld", &num, &sz) != 2) {
|
||||
printf("%s\n%s\n", line, line+36+skip+7);
|
||||
fatal_exit("unhandled calloc");
|
||||
}
|
||||
|
||||
cl = get_codeline(tree, codeline, name);
|
||||
if(!cl)
|
||||
fatal_exit("alloc failure");
|
||||
cl->alloc += num*sz;
|
||||
cl->calls ++;
|
||||
}
|
||||
|
||||
/** get size of file */
|
||||
static off_t
|
||||
get_file_size(const char* fname)
|
||||
{
|
||||
struct stat s;
|
||||
if(stat(fname, &s) < 0) {
|
||||
fatal_exit("could not stat %s: %s", fname, strerror(errno));
|
||||
}
|
||||
return s.st_size;
|
||||
}
|
||||
|
||||
/** read the logfile */
|
||||
static void
|
||||
readfile(rbtree_t* tree, const char* fname)
|
||||
{
|
||||
off_t total = get_file_size(fname);
|
||||
off_t done = (off_t)0;
|
||||
int report = 0;
|
||||
FILE* in = fopen(fname, "r");
|
||||
char buf[102400];
|
||||
if(!in)
|
||||
fatal_exit("could not open %s: %s", fname, strerror(errno));
|
||||
printf("Reading %s of size %lld\n", fname, (long long)total);
|
||||
while(fgets(buf, 102400, in)) {
|
||||
buf[102400-1] = 0;
|
||||
done += (off_t)strlen(buf);
|
||||
/* progress count */
|
||||
if((int)(((double)done / (double)total)*100.) > report) {
|
||||
report = (int)(((double)done / (double)total)*100.);
|
||||
fprintf(stderr, " %d%%", report);
|
||||
}
|
||||
|
||||
if(!match(buf))
|
||||
continue;
|
||||
else if(strstr(buf+36, "malloc("))
|
||||
read_malloc_stat(buf, tree);
|
||||
else if(strstr(buf+36, "calloc("))
|
||||
read_calloc_stat(buf, tree);
|
||||
else {
|
||||
printf("%s\n", buf);
|
||||
fatal_exit("unhandled input");
|
||||
}
|
||||
}
|
||||
fprintf(stderr, " done\n");
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
/** print memory stats */
|
||||
static void
|
||||
printstats(rbtree_t* tree)
|
||||
{
|
||||
struct codeline* cl;
|
||||
uint64_t total = 0, tcalls = 0;
|
||||
RBTREE_FOR(cl, struct codeline*, tree) {
|
||||
printf("%12lld / %8lld in %s %s\n", (long long)cl->alloc,
|
||||
(long long)cl->calls, cl->codeline, cl->func);
|
||||
total += cl->alloc;
|
||||
tcalls += cl->calls;
|
||||
}
|
||||
printf("------------\n");
|
||||
printf("%12lld / %8lld total in %ld code lines\n", (long long)total,
|
||||
(long long)tcalls, (long)tree->count);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/** main program */
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
rbtree_t* tree = 0;
|
||||
if(argc != 2) {
|
||||
usage();
|
||||
}
|
||||
tree = rbtree_create(codeline_cmp);
|
||||
if(!tree)
|
||||
fatal_exit("alloc failure");
|
||||
readfile(tree, argv[1]);
|
||||
printstats(tree);
|
||||
return 0;
|
||||
}
|
||||
Executable
+111
@@ -0,0 +1,111 @@
|
||||
# tpkg that only exes the files.
|
||||
args="../.."
|
||||
if test "$1" = "-a"; then
|
||||
args=$2
|
||||
shift
|
||||
shift
|
||||
fi
|
||||
|
||||
if test "$1" = "clean"; then
|
||||
echo "rm -f result.* .done* .tpkg.var.master .tpkg.var.test"
|
||||
rm -f result.* .done* .tpkg.var.master .tpkg.var.test
|
||||
exit 0
|
||||
fi
|
||||
if test "$1" = "fake"; then
|
||||
echo "minitpkg fake $2"
|
||||
echo "fake" > .done-`basename $2 .tpkg`
|
||||
exit 0
|
||||
fi
|
||||
if test "$1" = "report" || test "$2" = "report"; then
|
||||
echo "Minitpkg Report"
|
||||
for result in result.*; do
|
||||
name=`echo $result | sed -e 's/result\.//'`
|
||||
if test -f ".done-$name"; then
|
||||
if test "$1" != "-q"; then
|
||||
echo "** PASSED ** : $name"
|
||||
fi
|
||||
else
|
||||
echo "!! FAILED !! : $name"
|
||||
fi
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test "$1" != 'exe'; then
|
||||
# usage
|
||||
echo "mini tpkg. Reduced functionality for old shells."
|
||||
echo " tpkg exe <file>"
|
||||
echo " tpkg fake <file>"
|
||||
echo " tpkg clean"
|
||||
echo " tpkg [-q] report"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
|
||||
# do not execute if the disk is too full
|
||||
#DISKLIMIT=100000
|
||||
# This check is not portable (to Solaris 10).
|
||||
#avail=`df . | tail -1 | awk '{print $4}'`
|
||||
#if test "$avail" -lt "$DISKLIMIT"; then
|
||||
#echo "minitpkg: The disk is too full! Only $avail."
|
||||
#exit 1
|
||||
#fi
|
||||
|
||||
name=`basename $1 .tpkg`
|
||||
dir=$name.$$
|
||||
result=result.$name
|
||||
done=.done-$name
|
||||
success="no"
|
||||
shell="bash"
|
||||
|
||||
# check already done
|
||||
if test -f .done-$name; then
|
||||
echo "minitpkg .done-$name exists. skip test."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Extract
|
||||
echo "minitpkg extract $1 to $dir"
|
||||
mkdir $dir
|
||||
gzip -cd $name.tpkg | (cd $dir; tar xf -)
|
||||
cd $dir
|
||||
mv $name.dir/* .
|
||||
|
||||
# EXE
|
||||
echo "minitpkg exe $name"
|
||||
echo "minitpkg exe $name" > $result
|
||||
if test -f $name.pre; then
|
||||
echo "minitpkg exe $name.pre"
|
||||
echo "minitpkg exe $name.pre" >> $result
|
||||
$shell $name.pre $args >> $result
|
||||
if test $? -ne 0; then
|
||||
echo "Warning: $name.pre did not exit successfully"
|
||||
fi
|
||||
fi
|
||||
if test -f $name.test; then
|
||||
echo "minitpkg exe $name.test"
|
||||
echo "minitpkg exe $name.test" >> $result
|
||||
$shell $name.test $args >>$result 2>&1
|
||||
if test $? -ne 0; then
|
||||
echo "$name: FAILED" >> $result
|
||||
echo "$name: FAILED"
|
||||
success="no"
|
||||
else
|
||||
echo "$name: PASSED" >> $result
|
||||
echo "$name: PASSED" > ../.done-$name
|
||||
echo "$name: PASSED"
|
||||
success="yes"
|
||||
fi
|
||||
fi
|
||||
if test -f $name.post; then
|
||||
echo "minitpkg exe $name.post"
|
||||
echo "minitpkg exe $name.post" >> $result
|
||||
$shell $name.post $args >> $result
|
||||
if test $? -ne 0; then
|
||||
echo "Warning: $name.post did not exit successfully"
|
||||
fi
|
||||
fi
|
||||
|
||||
mv $result ..
|
||||
cd ..
|
||||
rm -rf $dir
|
||||
+23
-15
@@ -67,7 +67,7 @@ void read_input(ldns_buffer* pkt, FILE* in)
|
||||
hex_to_buf(pkt, buf);
|
||||
}
|
||||
|
||||
/** analyze domain name in packet, possibly compressed. */
|
||||
/** analyze domain name in packet, possibly compressed */
|
||||
void analyze_dname(ldns_buffer* pkt)
|
||||
{
|
||||
size_t oldpos = ldns_buffer_position(pkt);
|
||||
@@ -111,13 +111,18 @@ void analyze_rdata(ldns_buffer*pkt, const ldns_rr_descriptor* desc,
|
||||
}
|
||||
rdf++;
|
||||
}
|
||||
if(rdlen)
|
||||
if(rdlen) {
|
||||
size_t i;
|
||||
printf(" remain[%d]\n", (int)rdlen);
|
||||
for(i=0; i<rdlen; i++)
|
||||
printf(" %2.2X", (unsigned)ldns_buffer_current(pkt)[i]);
|
||||
printf("\n");
|
||||
}
|
||||
else printf("\n");
|
||||
ldns_buffer_skip(pkt, (ssize_t)rdlen);
|
||||
}
|
||||
|
||||
/** analyze rr in packet. */
|
||||
/** analyze rr in packet */
|
||||
void analyze_rr(ldns_buffer* pkt, int q)
|
||||
{
|
||||
uint16_t type, dclass, len;
|
||||
@@ -134,9 +139,9 @@ void analyze_rr(ldns_buffer* pkt, int q)
|
||||
printf("\n");
|
||||
} else {
|
||||
ttl = ldns_buffer_read_u32(pkt);
|
||||
printf(" ttl %d (0x%x)", ttl, ttl);
|
||||
printf(" ttl %d (0x%x)", (int)ttl, (unsigned)ttl);
|
||||
len = ldns_buffer_read_u16(pkt);
|
||||
printf(" rdata len %d:\n", len);
|
||||
printf(" rdata len %d:\n", (int)len);
|
||||
if(ldns_rr_descript(type))
|
||||
analyze_rdata(pkt, ldns_rr_descript(type), len);
|
||||
else ldns_buffer_skip(pkt, (ssize_t)len);
|
||||
@@ -152,25 +157,28 @@ void analyze(ldns_buffer* pkt)
|
||||
if(ldns_buffer_limit(pkt) < 12) return;
|
||||
|
||||
i = ldns_buffer_read_u16(pkt);
|
||||
printf("id (hostorder): %d (0x%x)\n", i, i);
|
||||
printf("id (hostorder): %d (0x%x)\n", (int)i, (unsigned)i);
|
||||
f = ldns_buffer_read_u16(pkt);
|
||||
printf("flags: 0x%x\n", f);
|
||||
printf("flags: 0x%x\n", (unsigned)f);
|
||||
qd = ldns_buffer_read_u16(pkt);
|
||||
printf("qdcount: %d\n", qd);
|
||||
printf("qdcount: %d\n", (int)qd);
|
||||
an = ldns_buffer_read_u16(pkt);
|
||||
printf("ancount: %d\n", an);
|
||||
printf("ancount: %d\n", (int)an);
|
||||
ns = ldns_buffer_read_u16(pkt);
|
||||
printf("nscount: %d\n", ns);
|
||||
printf("nscount: %d\n", (int)ns);
|
||||
ar = ldns_buffer_read_u16(pkt);
|
||||
printf("arcount: %d\n", ar);
|
||||
printf("arcount: %d\n", (int)ar);
|
||||
|
||||
printf(";-- query section\n");
|
||||
while(ldns_buffer_remaining(pkt) > 0) {
|
||||
if(rrnum == qd) printf(";-- answer section\n");
|
||||
if(rrnum == qd+an) printf(";-- authority section\n");
|
||||
if(rrnum == qd+an+ns) printf(";-- additional section\n");
|
||||
if(rrnum == (int)qd)
|
||||
printf(";-- answer section\n");
|
||||
if(rrnum == (int)qd+(int)an)
|
||||
printf(";-- authority section\n");
|
||||
if(rrnum == (int)qd+(int)an+(int)ns)
|
||||
printf(";-- additional section\n");
|
||||
printf("rr %d ", rrnum);
|
||||
analyze_rr(pkt, rrnum < qd);
|
||||
analyze_rr(pkt, rrnum < (int)qd);
|
||||
rrnum++;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -46,7 +46,7 @@ static void
|
||||
skip_whites(const char** p)
|
||||
{
|
||||
while(1) {
|
||||
while(isspace(**p))
|
||||
while(isspace((int)**p))
|
||||
(*p)++;
|
||||
if(**p == ';') {
|
||||
/* comment, skip until newline */
|
||||
@@ -58,7 +58,7 @@ skip_whites(const char** p)
|
||||
}
|
||||
}
|
||||
|
||||
/** takes a hex string and puts into buffer */
|
||||
/* takes a hex string and puts into buffer */
|
||||
void hex_to_buf(ldns_buffer* pkt, const char* hex)
|
||||
{
|
||||
const char* p = hex;
|
||||
@@ -68,11 +68,11 @@ void hex_to_buf(ldns_buffer* pkt, const char* hex)
|
||||
skip_whites(&p);
|
||||
if(ldns_buffer_position(pkt) == ldns_buffer_limit(pkt))
|
||||
fatal_exit("hex_to_buf: buffer too small");
|
||||
if(!isalnum(*p))
|
||||
if(!isalnum((int)*p))
|
||||
break;
|
||||
val = ldns_hexdigit_to_int(*p++) << 4;
|
||||
skip_whites(&p);
|
||||
log_assert(*p && isalnum(*p));
|
||||
log_assert(*p && isalnum((int)*p));
|
||||
val |= ldns_hexdigit_to_int(*p++);
|
||||
ldns_buffer_write_u8(pkt, (uint8_t)val);
|
||||
skip_whites(&p);
|
||||
|
||||
+5
-1
@@ -41,7 +41,11 @@
|
||||
#ifndef TESTCODE_READHEX_H
|
||||
#define TESTCODE_READHEX_H
|
||||
|
||||
/** helper to convert hex string to packet buffer */
|
||||
/**
|
||||
* Helper to convert hex string to packet buffer.
|
||||
* @param pkt: buffer to put result in.
|
||||
* @param hex: string of hex data. Spaces and ';...' comments are skipped.
|
||||
*/
|
||||
void hex_to_buf(ldns_buffer* pkt, const char* hex);
|
||||
|
||||
#endif /* TESTCODE_READHEX_H */
|
||||
|
||||
+54
-9
@@ -41,6 +41,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "testcode/replay.h"
|
||||
#include "testcode/ldns-testpkts.h"
|
||||
|
||||
@@ -85,6 +86,18 @@ replay_range_delete(struct replay_range* rng)
|
||||
free(rng);
|
||||
}
|
||||
|
||||
/** strip whitespace from end of string */
|
||||
static void
|
||||
strip_end_white(char* p)
|
||||
{
|
||||
size_t i;
|
||||
for(i = strlen(p); i > 0; i--) {
|
||||
if(isspace((int)p[i-1]))
|
||||
p[i-1] = 0;
|
||||
else return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a range from file.
|
||||
* @param remain: Rest of line (after RANGE keyword).
|
||||
@@ -99,7 +112,7 @@ replay_range_delete(struct replay_range* rng)
|
||||
*/
|
||||
static struct replay_range*
|
||||
replay_range_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
char* line, uint16_t* ttl, ldns_rdf** or, ldns_rdf** prev)
|
||||
char* line, uint32_t* ttl, ldns_rdf** or, ldns_rdf** prev)
|
||||
{
|
||||
struct replay_range* rng = (struct replay_range*)malloc(
|
||||
sizeof(struct replay_range));
|
||||
@@ -120,10 +133,25 @@ replay_range_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
while(fgets(line, MAX_LINE_LEN-1, in)) {
|
||||
(*lineno)++;
|
||||
parse = line;
|
||||
while(isspace(*parse))
|
||||
while(isspace((int)*parse))
|
||||
parse++;
|
||||
if(!*parse || *parse == ';')
|
||||
if(!*parse || *parse == ';') {
|
||||
pos = ftello(in);
|
||||
continue;
|
||||
}
|
||||
if(parse_keyword(&parse, "ADDRESS")) {
|
||||
while(isspace((int)*parse))
|
||||
parse++;
|
||||
strip_end_white(parse);
|
||||
if(!extstrtoaddr(parse, &rng->addr, &rng->addrlen)) {
|
||||
log_err("Line %d: could not read ADDRESS: %s",
|
||||
*lineno, parse);
|
||||
free(rng);
|
||||
return NULL;
|
||||
}
|
||||
pos = ftello(in);
|
||||
continue;
|
||||
}
|
||||
if(parse_keyword(&parse, "RANGE_END")) {
|
||||
return rng;
|
||||
}
|
||||
@@ -158,7 +186,7 @@ replay_range_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
*/
|
||||
static struct replay_moment*
|
||||
replay_moment_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
uint16_t* ttl, ldns_rdf** or, ldns_rdf** prev)
|
||||
uint32_t* ttl, ldns_rdf** or, ldns_rdf** prev)
|
||||
{
|
||||
struct replay_moment* mom = (struct replay_moment*)malloc(
|
||||
sizeof(struct replay_moment));
|
||||
@@ -173,13 +201,15 @@ replay_moment_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
return NULL;
|
||||
}
|
||||
remain += skip;
|
||||
while(isspace(*remain))
|
||||
while(isspace((int)*remain))
|
||||
remain++;
|
||||
if(parse_keyword(&remain, "NOTHING")) {
|
||||
mom->evt_type = repevt_nothing;
|
||||
} else if(parse_keyword(&remain, "QUERY")) {
|
||||
mom->evt_type = repevt_front_query;
|
||||
readentry = 1;
|
||||
if(!extstrtoaddr("127.0.0.1", &mom->addr, &mom->addrlen))
|
||||
fatal_exit("internal error");
|
||||
} else if(parse_keyword(&remain, "CHECK_ANSWER")) {
|
||||
mom->evt_type = repevt_front_reply;
|
||||
readentry = 1;
|
||||
@@ -198,6 +228,21 @@ replay_moment_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
free(mom);
|
||||
return NULL;
|
||||
}
|
||||
while(isspace((int)*remain))
|
||||
remain++;
|
||||
if(parse_keyword(&remain, "ADDRESS")) {
|
||||
while(isspace((int)*remain))
|
||||
remain++;
|
||||
if(strlen(remain) > 0) /* remove \n */
|
||||
remain[strlen(remain)-1] = 0;
|
||||
printf("remain '%s'\n", remain);
|
||||
if(!extstrtoaddr(remain, &mom->addr, &mom->addrlen)) {
|
||||
log_err("line %d: could not parse ADDRESS: %s",
|
||||
*lineno, remain);
|
||||
free(mom);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if(readentry) {
|
||||
mom->match = read_entry(in, name, lineno, ttl, or, prev);
|
||||
@@ -210,12 +255,12 @@ replay_moment_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
return mom;
|
||||
}
|
||||
|
||||
/** makes scenario with title on rest of line. */
|
||||
/** makes scenario with title on rest of line */
|
||||
static struct replay_scenario*
|
||||
make_scenario(char* line)
|
||||
{
|
||||
struct replay_scenario* scen;
|
||||
while(isspace(*line))
|
||||
while(isspace((int)*line))
|
||||
line++;
|
||||
if(!*line) {
|
||||
log_err("scenario: no title given");
|
||||
@@ -239,7 +284,7 @@ replay_scenario_read(FILE* in, const char* name, int* lineno)
|
||||
char line[MAX_LINE_LEN];
|
||||
char *parse;
|
||||
struct replay_scenario* scen = NULL;
|
||||
uint16_t ttl = 3600;
|
||||
uint32_t ttl = 3600;
|
||||
ldns_rdf* or = NULL;
|
||||
ldns_rdf* prev = NULL;
|
||||
line[MAX_LINE_LEN-1]=0;
|
||||
@@ -247,7 +292,7 @@ replay_scenario_read(FILE* in, const char* name, int* lineno)
|
||||
while(fgets(line, MAX_LINE_LEN-1, in)) {
|
||||
parse=line;
|
||||
(*lineno)++;
|
||||
while(isspace(*parse))
|
||||
while(isspace((int)*parse))
|
||||
parse++;
|
||||
if(!*parse)
|
||||
continue; /* empty line */
|
||||
|
||||
+21
-2
@@ -44,11 +44,13 @@
|
||||
* ; comment line.
|
||||
* SCENARIO_BEGIN name_of_scenario
|
||||
* RANGE_BEGIN start_time end_time
|
||||
* ; give ip of the virtual server, it matches any ip if not present.
|
||||
* ADDRESS ip_address
|
||||
* match_entries
|
||||
* RANGE_END
|
||||
* ; more RANGE items.
|
||||
* ; go to the next moment
|
||||
* STEP time_step event_type
|
||||
* STEP time_step event_type [ADDRESS ip_address]
|
||||
* ; event_type can be:
|
||||
* o NOTHING - nothing
|
||||
* o QUERY - followed by entry
|
||||
@@ -153,11 +155,18 @@ struct replay_moment {
|
||||
repevt_back_query,
|
||||
/** an error happens to outbound query */
|
||||
repevt_error
|
||||
} evt_type;
|
||||
}
|
||||
/** variable with what is to happen this moment */
|
||||
evt_type;
|
||||
|
||||
/** The sent packet must match this. Incoming events, the data. */
|
||||
struct entry* match;
|
||||
|
||||
/** address that must be matched, or packet remote host address. */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr, if 0, then any address will do */
|
||||
socklen_t addrlen;
|
||||
|
||||
/** what pending query should timeout or is answered. or
|
||||
* NULL for last sent query.
|
||||
* Unused at this time.
|
||||
@@ -173,6 +182,10 @@ struct replay_range {
|
||||
int start_step;
|
||||
/** end step of time range. */
|
||||
int end_step;
|
||||
/** address of where this range is served. */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr, if 0, then any address will do */
|
||||
socklen_t addrlen;
|
||||
|
||||
/** Matching list */
|
||||
struct entry* match;
|
||||
@@ -217,6 +230,8 @@ struct replay_runtime {
|
||||
void (*sig_cb)(int, void*);
|
||||
/** signal handler user arg */
|
||||
void *sig_cb_arg;
|
||||
/** time to exit cleanly */
|
||||
int exit_cleanly;
|
||||
|
||||
/** size of buffers */
|
||||
size_t bufsize;
|
||||
@@ -245,6 +260,10 @@ struct fake_pending {
|
||||
ldns_pkt* pkt;
|
||||
/** by what transport was the query sent out */
|
||||
enum transport_type transport;
|
||||
/** if this is a serviced query */
|
||||
int serviced;
|
||||
/** the runtime structure this is part of */
|
||||
struct replay_runtime* runtime;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* testcode/signit.c - debug tool to sign rrsets with given keys.
|
||||
*
|
||||
* 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 program signs rrsets with the given keys. It can be used to
|
||||
* construct input to test the validator with.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/net_help.h"
|
||||
|
||||
/**
|
||||
* Key settings
|
||||
*/
|
||||
struct keysets {
|
||||
/** signature inception */
|
||||
uint32_t incep;
|
||||
/** signature expiration */
|
||||
uint32_t expi;
|
||||
/** owner name */
|
||||
char* owner;
|
||||
/** keytag */
|
||||
uint16_t keytag;
|
||||
/** DNSKEY flags */
|
||||
uint16_t flags;
|
||||
};
|
||||
|
||||
/** print usage and exit */
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
printf("usage: signit expi ince keytag owner keyfile\n");
|
||||
printf("present rrset data on stdin.\n");
|
||||
printf("signed data is printed to stdout.\n");
|
||||
printf("\n");
|
||||
printf("Or use: signit NSEC3PARAM hash flags iter salt\n");
|
||||
printf("present names on stdin, hashed names are printed to stdout.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** read expi ince keytag owner from cmdline */
|
||||
static void
|
||||
parse_cmdline(char *argv[], struct keysets* s)
|
||||
{
|
||||
s->expi = cfg_convert_timeval(argv[1]);
|
||||
s->incep = cfg_convert_timeval(argv[2]);
|
||||
s->keytag = (uint16_t)atoi(argv[3]);
|
||||
s->owner = argv[4];
|
||||
s->flags = DNSKEY_BIT_ZSK; /* to enforce signing */
|
||||
}
|
||||
|
||||
/** read all key files, exit on error */
|
||||
static ldns_key_list*
|
||||
read_keys(int num, char* names[], struct keysets* set)
|
||||
{
|
||||
int i;
|
||||
ldns_key_list* keys = ldns_key_list_new();
|
||||
ldns_key* k;
|
||||
ldns_rdf* rdf;
|
||||
ldns_status s;
|
||||
int b;
|
||||
FILE* in;
|
||||
|
||||
if(!keys) fatal_exit("alloc failure");
|
||||
for(i=0; i<num; i++) {
|
||||
printf("read keyfile %s\n", names[i]);
|
||||
in = fopen(names[i], "r");
|
||||
if(!in) fatal_exit("could not open %s: %s", names[i],
|
||||
strerror(errno));
|
||||
s = ldns_key_new_frm_fp(&k, in);
|
||||
fclose(in);
|
||||
if(s != LDNS_STATUS_OK)
|
||||
fatal_exit("bad keyfile %s: %s", names[i],
|
||||
ldns_get_errorstr_by_id(s));
|
||||
ldns_key_set_expiration(k, set->expi);
|
||||
ldns_key_set_inception(k, set->incep);
|
||||
s = ldns_str2rdf_dname(&rdf, set->owner);
|
||||
if(s != LDNS_STATUS_OK)
|
||||
fatal_exit("bad owner name %s: %s", set->owner,
|
||||
ldns_get_errorstr_by_id(s));
|
||||
ldns_key_set_pubkey_owner(k, rdf);
|
||||
ldns_key_set_flags(k, set->flags);
|
||||
ldns_key_set_keytag(k, set->keytag);
|
||||
b = ldns_key_list_push_key(keys, k);
|
||||
log_assert(b);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
/** read list of rrs from the file */
|
||||
static ldns_rr_list*
|
||||
read_rrs(FILE* in)
|
||||
{
|
||||
uint32_t my_ttl = 3600;
|
||||
ldns_rdf *my_origin = NULL;
|
||||
ldns_rdf *my_prev = NULL;
|
||||
ldns_status s;
|
||||
int line_nr = 1;
|
||||
int b;
|
||||
|
||||
ldns_rr_list* list;
|
||||
ldns_rr *rr;
|
||||
|
||||
list = ldns_rr_list_new();
|
||||
if(!list) fatal_exit("alloc error");
|
||||
|
||||
while(!feof(in)) {
|
||||
s = ldns_rr_new_frm_fp_l(&rr, in, &my_ttl, &my_origin,
|
||||
&my_prev, &line_nr);
|
||||
if(s == LDNS_STATUS_SYNTAX_TTL ||
|
||||
s == LDNS_STATUS_SYNTAX_ORIGIN ||
|
||||
s == LDNS_STATUS_SYNTAX_EMPTY)
|
||||
continue;
|
||||
else if(s != LDNS_STATUS_OK)
|
||||
fatal_exit("parse error in line %d: %s", line_nr,
|
||||
ldns_get_errorstr_by_id(s));
|
||||
b = ldns_rr_list_push_rr(list, rr);
|
||||
log_assert(b);
|
||||
}
|
||||
printf("read %d lines\n", line_nr);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/** sign the rrs with the keys */
|
||||
static void
|
||||
signit(ldns_rr_list* rrs, ldns_key_list* keys)
|
||||
{
|
||||
ldns_rr_list* rrset;
|
||||
ldns_rr_list* sigs;
|
||||
|
||||
while(ldns_rr_list_rr_count(rrs) > 0) {
|
||||
rrset = ldns_rr_list_pop_rrset(rrs);
|
||||
if(!rrset) fatal_exit("copy alloc failure");
|
||||
sigs = ldns_sign_public(rrset, keys);
|
||||
if(!sigs) fatal_exit("failed to sign");
|
||||
ldns_rr_list_print(stdout, rrset);
|
||||
ldns_rr_list_print(stdout, sigs);
|
||||
printf("\n");
|
||||
ldns_rr_list_free(rrset);
|
||||
ldns_rr_list_free(sigs);
|
||||
}
|
||||
}
|
||||
|
||||
/** process keys and signit */
|
||||
static void
|
||||
process_keys(int argc, char* argv[])
|
||||
{
|
||||
ldns_rr_list* rrs;
|
||||
ldns_key_list* keys;
|
||||
struct keysets settings;
|
||||
log_assert(argc == 6);
|
||||
|
||||
parse_cmdline(argv, &settings);
|
||||
keys = read_keys(1, argv+5, &settings);
|
||||
rrs = read_rrs(stdin);
|
||||
signit(rrs, keys);
|
||||
|
||||
ldns_rr_list_deep_free(rrs);
|
||||
ldns_key_list_free(keys);
|
||||
}
|
||||
|
||||
/** process nsec3 params and perform hashing */
|
||||
static void
|
||||
process_nsec3(int argc, char* argv[])
|
||||
{
|
||||
char line[10240];
|
||||
ldns_rdf* salt;
|
||||
ldns_rdf* in, *out;
|
||||
ldns_status status;
|
||||
status = ldns_str2rdf_nsec3_salt(&salt, argv[5]);
|
||||
if(status != LDNS_STATUS_OK)
|
||||
fatal_exit("Could not parse salt %s: %s", argv[5],
|
||||
ldns_get_errorstr_by_id(status));
|
||||
log_assert(argc == 6);
|
||||
while(fgets(line, (int)sizeof(line), stdin)) {
|
||||
if(strlen(line) > 0)
|
||||
line[strlen(line)-1] = 0; /* remove trailing newline */
|
||||
if(line[0]==0)
|
||||
continue;
|
||||
status = ldns_str2rdf_dname(&in, line);
|
||||
if(status != LDNS_STATUS_OK)
|
||||
fatal_exit("Could not parse name %s: %s", line,
|
||||
ldns_get_errorstr_by_id(status));
|
||||
ldns_rdf_print(stdout, in);
|
||||
printf(" -> ");
|
||||
/* arg 3 is flags, unused */
|
||||
out = ldns_nsec3_hash_name(in, (uint8_t)atoi(argv[2]),
|
||||
(uint16_t)atoi(argv[4]),
|
||||
ldns_rdf_data(salt)[0], ldns_rdf_data(salt)+1);
|
||||
if(!out)
|
||||
fatal_exit("Could not hash %s", line);
|
||||
ldns_rdf_print(stdout, out);
|
||||
printf("\n");
|
||||
ldns_rdf_deep_free(in);
|
||||
ldns_rdf_deep_free(out);
|
||||
}
|
||||
ldns_rdf_deep_free(salt);
|
||||
}
|
||||
|
||||
/** main program */
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
log_init(NULL, 0, NULL);
|
||||
if(argc != 6) {
|
||||
usage();
|
||||
}
|
||||
if(strcmp(argv[1], "NSEC3PARAM") == 0) {
|
||||
process_nsec3(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
process_keys(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* testcode/streamtcp.c - debug program perform multiple DNS queries on tcp.
|
||||
*
|
||||
* 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 program performs multiple DNS queries on a TCP stream.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <signal.h>
|
||||
#include "util/locks.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/dname.h"
|
||||
|
||||
/** usage information for streamtcp */
|
||||
void usage(char* argv[])
|
||||
{
|
||||
printf("usage: %s [options] name type class ...\n", argv[0]);
|
||||
printf(" sends the name-type-class queries over TCP.\n");
|
||||
printf("-f server what ipaddr@portnr to send the queries to\n");
|
||||
printf("-h this help text\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** open TCP socket to svr */
|
||||
static int
|
||||
open_svr(char* svr)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
int fd = -1;
|
||||
/* svr can be ip@port */
|
||||
if(!extstrtoaddr(svr, &addr, &addrlen)) {
|
||||
printf("fatal: bad server specs '%s'\n", svr);
|
||||
exit(1);
|
||||
}
|
||||
fd = socket(addr_is_ip6(&addr, addrlen)?PF_INET6:PF_INET,
|
||||
SOCK_STREAM, 0);
|
||||
if(fd == -1) {
|
||||
perror("socket() error");
|
||||
exit(1);
|
||||
}
|
||||
if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) {
|
||||
perror("connect() error");
|
||||
exit(1);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/** write a query over the TCP fd */
|
||||
static void
|
||||
write_q(int fd, ldns_buffer* buf, int id,
|
||||
char* strname, char* strtype, char* strclass)
|
||||
{
|
||||
struct query_info qinfo;
|
||||
ldns_rdf* rdf;
|
||||
int labs;
|
||||
uint16_t len;
|
||||
/* qname */
|
||||
rdf = ldns_dname_new_frm_str(strname);
|
||||
if(!rdf) {
|
||||
printf("cannot parse query name: '%s'\n", strname);
|
||||
exit(1);
|
||||
}
|
||||
qinfo.qname = memdup(ldns_rdf_data(rdf), ldns_rdf_size(rdf));
|
||||
labs = dname_count_size_labels(qinfo.qname, &qinfo.qname_len);
|
||||
ldns_rdf_deep_free(rdf);
|
||||
if(!qinfo.qname) fatal_exit("out of memory");
|
||||
|
||||
/* qtype and qclass */
|
||||
qinfo.qtype = ldns_get_rr_type_by_name(strtype);
|
||||
qinfo.qclass = ldns_get_rr_class_by_name(strclass);
|
||||
|
||||
/* make query */
|
||||
qinfo_query_encode(buf, &qinfo);
|
||||
ldns_buffer_write_u16_at(buf, 0, (uint16_t)id);
|
||||
ldns_buffer_write_u16_at(buf, 2, BIT_RD);
|
||||
|
||||
/* send it */
|
||||
len = (uint16_t)ldns_buffer_limit(buf);
|
||||
len = htons(len);
|
||||
if(write(fd, &len, sizeof(len)) < (ssize_t)sizeof(len)) {
|
||||
perror("write() len failed");
|
||||
exit(1);
|
||||
}
|
||||
if(write(fd, ldns_buffer_begin(buf), ldns_buffer_limit(buf)) <
|
||||
(ssize_t)ldns_buffer_limit(buf)) {
|
||||
perror("write() data failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
free(qinfo.qname);
|
||||
}
|
||||
|
||||
/** receive DNS datagram over TCP and print it */
|
||||
static void
|
||||
recv_one(int fd, ldns_buffer* buf)
|
||||
{
|
||||
uint16_t len;
|
||||
ldns_pkt* pkt;
|
||||
ldns_status status;
|
||||
if(read(fd, &len, sizeof(len)) < (ssize_t)sizeof(len)) {
|
||||
perror("read() len failed");
|
||||
exit(1);
|
||||
}
|
||||
len = ntohs(len);
|
||||
ldns_buffer_clear(buf);
|
||||
ldns_buffer_set_limit(buf, len);
|
||||
if(read(fd, ldns_buffer_begin(buf), len) < (ssize_t)len) {
|
||||
perror("read() data failed");
|
||||
exit(1);
|
||||
}
|
||||
printf("\nnext received packet\n");
|
||||
log_buf(0, "data", buf);
|
||||
|
||||
status = ldns_wire2pkt(&pkt, ldns_buffer_begin(buf), len);
|
||||
if(status != LDNS_STATUS_OK) {
|
||||
printf("could not parse incoming packet: %s\n",
|
||||
ldns_get_errorstr_by_id(status));
|
||||
log_buf(0, "data was", buf);
|
||||
exit(1);
|
||||
}
|
||||
ldns_pkt_print(stdout, pkt);
|
||||
ldns_pkt_free(pkt);
|
||||
}
|
||||
|
||||
/** send the TCP queries and print answers */
|
||||
static void
|
||||
send_em(char* svr, int num, char** qs)
|
||||
{
|
||||
ldns_buffer* buf = ldns_buffer_new(65553);
|
||||
int fd = open_svr(svr);
|
||||
int i;
|
||||
if(!buf) fatal_exit("out of memory");
|
||||
for(i=0; i<num; i+=3) {
|
||||
printf("\nNext query is %s %s %s\n", qs[i], qs[i+1], qs[i+2]);
|
||||
write_q(fd, buf, i, qs[i], qs[i+1], qs[i+2]);
|
||||
/* print at least one result */
|
||||
recv_one(fd, buf);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
ldns_buffer_free(buf);
|
||||
printf("orderly exit\n");
|
||||
}
|
||||
|
||||
/** SIGPIPE handler */
|
||||
static RETSIGTYPE sigh(int sig)
|
||||
{
|
||||
if(sig == SIGPIPE) {
|
||||
printf("got SIGPIPE, remote connection gone\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("Got unhandled signal %d\n", sig);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** 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 program for streamtcp */
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int c;
|
||||
char* svr = "127.0.0.1";
|
||||
|
||||
/* lock debug start (if any) */
|
||||
log_init(0, 0, 0);
|
||||
checklock_start();
|
||||
|
||||
if(signal(SIGPIPE, &sigh) == SIG_ERR) {
|
||||
perror("could not install signal handler");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* command line options */
|
||||
if(argc == 1) {
|
||||
usage(argv);
|
||||
}
|
||||
while( (c=getopt(argc, argv, "f:h")) != -1) {
|
||||
switch(c) {
|
||||
case 'f':
|
||||
svr = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
default:
|
||||
usage(argv);
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if(argc % 3 != 0) {
|
||||
printf("queries must be multiples of name,type,class\n");
|
||||
return 1;
|
||||
}
|
||||
send_em(svr, argc, argv);
|
||||
checklock_stop();
|
||||
return 0;
|
||||
}
|
||||
+6
-7
@@ -43,10 +43,10 @@ function dotest()
|
||||
fi
|
||||
|
||||
cat >makeconf.mak.$$ << EOF
|
||||
configure: configure.ac
|
||||
$AC_CMD
|
||||
touch configure
|
||||
Makefile: configure Makefile.in
|
||||
#configure: configure.ac
|
||||
# $AC_CMD
|
||||
# touch configure
|
||||
Makefile: Makefile.in #configure
|
||||
./configure $CONFIGURE_FLAGS $DISABLE
|
||||
touch Makefile
|
||||
EOF
|
||||
@@ -77,12 +77,11 @@ EOF
|
||||
# echossh $1 "gtar xzf unbound_ttt.tar.gz && rm unbound_ttt.tar.gz"
|
||||
fi
|
||||
echossh $1 "cd $2; $MAKE_CMD -f makeconf.mak.$$ Makefile"
|
||||
echossh $1 "cd $2; $MAKE_CMD"
|
||||
echossh $1 "cd $2; $MAKE_CMD all tests"
|
||||
echossh $1 "cd $2; $MAKE_CMD doc"
|
||||
if test $RUN_TEST = yes; then
|
||||
echossh $1 "cd $2/testdata; tpkg clean"
|
||||
echossh $1 "cd $2; bash testcode/do-tests.sh"
|
||||
echossh $1 "cd $2/testdata; tpkg -q report" | tee -a $REPORT_FILE
|
||||
echossh $1 "cd $2/testdata; sh ../testcode/mini_tpkg.sh -q report" | tee -a $REPORT_FILE
|
||||
fi
|
||||
echossh $1 "cd $2; rm -f makeconf.mak.$$"
|
||||
rm -f makeconf.mak.$$
|
||||
|
||||
+24
-7
@@ -53,6 +53,8 @@
|
||||
|
||||
/** maximum line length for lines in the replay file. */
|
||||
#define MAX_LINE_LEN 1024
|
||||
/** the config file (removed at exit) */
|
||||
static char cfgfile[MAX_LINE_LEN];
|
||||
|
||||
/** give commandline usage for testbound. */
|
||||
static void
|
||||
@@ -82,7 +84,7 @@ add_opts(char* optarg, int* pass_argc, char* pass_argv[])
|
||||
{
|
||||
char *p = optarg, *np;
|
||||
size_t len;
|
||||
while(p && isspace(*p))
|
||||
while(p && isspace((int)*p))
|
||||
p++;
|
||||
while(p && *p) {
|
||||
/* find location of next string and length of this one */
|
||||
@@ -100,12 +102,12 @@ add_opts(char* optarg, int* pass_argc, char* pass_argv[])
|
||||
(*pass_argc)++;
|
||||
/* go to next option */
|
||||
p = np;
|
||||
while(p && isspace(*p))
|
||||
while(p && isspace((int)*p))
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
/** pretty print commandline for unbound in this test. */
|
||||
/** pretty print commandline for unbound in this test */
|
||||
static void
|
||||
echo_cmdline(int argc, char* argv[])
|
||||
{
|
||||
@@ -133,10 +135,16 @@ setup_config(FILE* in, char* configfile, int* lineno,
|
||||
if(!cfg) fatal_exit("could not open %s: %s",
|
||||
configfile, strerror(errno));
|
||||
line[MAX_LINE_LEN-1] = 0;
|
||||
/* some basic settings to not pollute the host system */
|
||||
fprintf(cfg, "server: use-syslog: no\n");
|
||||
fprintf(cfg, " directory: \"\"\n");
|
||||
fprintf(cfg, " chroot: \"\"\n");
|
||||
fprintf(cfg, " username: \"\"\n");
|
||||
fprintf(cfg, " pidfile: \"\"\n");
|
||||
while(fgets(line, MAX_LINE_LEN-1, in)) {
|
||||
parse = line;
|
||||
(*lineno)++;
|
||||
while(isspace(*parse))
|
||||
while(isspace((int)*parse))
|
||||
parse++;
|
||||
if(!*parse || parse[0] == ';')
|
||||
continue;
|
||||
@@ -179,6 +187,12 @@ setup_playback(const char* filename, char* configfile,
|
||||
log_info("Scenario: %s", scen->title);
|
||||
return scen;
|
||||
}
|
||||
|
||||
/** remove config file at exit */
|
||||
void remove_configfile(void)
|
||||
{
|
||||
unlink(cfgfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main fake event test program. Setup, teardown and report errors.
|
||||
@@ -195,11 +209,11 @@ main(int argc, char* argv[])
|
||||
int init_optind = optind;
|
||||
char* init_optarg = optarg;
|
||||
struct replay_scenario* scen = NULL;
|
||||
char cfgfile[MAX_LINE_LEN];
|
||||
|
||||
log_init(NULL);
|
||||
log_init(NULL, 0, NULL);
|
||||
log_info("Start of %s testbound program.", PACKAGE_STRING);
|
||||
/* determine commandline options for the daemon */
|
||||
cfgfile[0] = 0;
|
||||
pass_argc = 1;
|
||||
pass_argv[0] = "unbound";
|
||||
add_opts("-d", &pass_argc, pass_argv);
|
||||
@@ -224,6 +238,8 @@ main(int argc, char* argv[])
|
||||
testbound_usage();
|
||||
return 1;
|
||||
}
|
||||
if(atexit(&remove_configfile) != 0)
|
||||
fatal_exit("atexit() failed: %s", strerror(errno));
|
||||
|
||||
/* setup test environment */
|
||||
scen = setup_playback(playback_file, cfgfile, &pass_argc, pass_argv);
|
||||
@@ -240,9 +256,10 @@ main(int argc, char* argv[])
|
||||
/* run the normal daemon */
|
||||
res = daemon_main(pass_argc, pass_argv);
|
||||
|
||||
unlink(cfgfile);
|
||||
fake_event_cleanup();
|
||||
for(c=1; c<pass_argc; c++)
|
||||
free(pass_argv[c]);
|
||||
if(res == 0)
|
||||
log_info("Testbound Exit Success");
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* testcode/unitanchor.c - unit test for trust anchor storage.
|
||||
*
|
||||
* 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
|
||||
* Calls trust anchor unit tests. Exits with code 1 on a failure.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "testcode/unitmain.h"
|
||||
#include "validator/val_anchor.h"
|
||||
|
||||
/** test empty set */
|
||||
static void
|
||||
test_anchor_empty(struct val_anchors* a)
|
||||
{
|
||||
uint16_t c = LDNS_RR_CLASS_IN;
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\000", 1, c) == NULL);
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\003com\000", 5, c) == NULL);
|
||||
unit_assert(anchors_lookup(a,
|
||||
(uint8_t*)"\007example\003com\000", 11, c) == NULL);
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\002nl\000", 4, c) == NULL);
|
||||
unit_assert(anchors_lookup(a,
|
||||
(uint8_t*)"\004labs\002nl\000", 9, c) == NULL);
|
||||
unit_assert(anchors_lookup(a,
|
||||
(uint8_t*)"\004fabs\002nl\000", 9, c) == NULL);
|
||||
}
|
||||
|
||||
/** test set of one anchor */
|
||||
static void
|
||||
test_anchor_one(ldns_buffer* buff, struct val_anchors* a)
|
||||
{
|
||||
uint16_t c = LDNS_RR_CLASS_IN;
|
||||
unit_assert(anchor_store_str(a, buff,
|
||||
"nl. DS 42860 5 1 14D739EB566D2B1A5E216A0BA4D17FA9B038BE4A"));
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\000", 1, c) == NULL);
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\003com\000", 5, c) == NULL);
|
||||
unit_assert(anchors_lookup(a,
|
||||
(uint8_t*)"\007example\003com\000", 11, c) == NULL);
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\002nl\000", 4, c) != NULL);
|
||||
unit_assert(anchors_lookup(a,
|
||||
(uint8_t*)"\004labs\002nl\000", 9, c) != NULL);
|
||||
unit_assert(anchors_lookup(a,
|
||||
(uint8_t*)"\004fabs\002nl\000", 9, c) != NULL);
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\002oo\000", 4, c) == NULL);
|
||||
}
|
||||
|
||||
/** test with several anchors */
|
||||
static void
|
||||
test_anchors(ldns_buffer* buff, struct val_anchors* a)
|
||||
{
|
||||
struct trust_anchor* ta;
|
||||
uint16_t c = LDNS_RR_CLASS_IN;
|
||||
unit_assert(anchor_store_str(a, buff,
|
||||
"labs.nl. DS 42860 5 1 14D739EB566D2B1A5E216A0BA4D17FA9B038BE4A"));
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\000", 1, c) == NULL);
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\003com\000", 5, c) == NULL);
|
||||
unit_assert(anchors_lookup(a,
|
||||
(uint8_t*)"\007example\003com\000", 11, c) == NULL);
|
||||
unit_assert(ta = anchors_lookup(a, (uint8_t*)"\002nl\000", 4, c));
|
||||
unit_assert(query_dname_compare(ta->name, (uint8_t*)"\002nl\000")==0);
|
||||
unit_assert(ta = anchors_lookup(a,
|
||||
(uint8_t*)"\004labs\002nl\000", 9, c));
|
||||
unit_assert(query_dname_compare(ta->name,
|
||||
(uint8_t*)"\004labs\002nl\000") == 0);
|
||||
unit_assert(ta = anchors_lookup(a,
|
||||
(uint8_t*)"\004fabs\002nl\000", 9, c));
|
||||
unit_assert(query_dname_compare(ta->name,
|
||||
(uint8_t*)"\002nl\000") == 0);
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\002oo\000", 4, c) == NULL);
|
||||
}
|
||||
|
||||
void anchors_test()
|
||||
{
|
||||
ldns_buffer* buff = ldns_buffer_new(65800);
|
||||
struct val_anchors* a;
|
||||
unit_assert(a = anchors_create());
|
||||
ldns_buffer_flip(buff);
|
||||
test_anchor_empty(a);
|
||||
test_anchor_one(buff, a);
|
||||
test_anchors(buff, a);
|
||||
anchors_delete(a);
|
||||
ldns_buffer_free(buff);
|
||||
}
|
||||
+383
-4
@@ -76,21 +76,21 @@ static void
|
||||
dname_test_qdtl(ldns_buffer* buff)
|
||||
{
|
||||
ldns_buffer_write_at(buff, 0, "\012abCDeaBCde\003cOm\000", 16);
|
||||
query_dname_tolower(ldns_buffer_begin(buff), 16);
|
||||
query_dname_tolower(ldns_buffer_begin(buff));
|
||||
unit_assert( memcmp(ldns_buffer_begin(buff),
|
||||
"\012abcdeabcde\003com\000", 16) == 0);
|
||||
|
||||
ldns_buffer_write_at(buff, 0, "\001+\012abC{e-ZYXe\003NET\000", 18);
|
||||
query_dname_tolower(ldns_buffer_begin(buff), 18);
|
||||
query_dname_tolower(ldns_buffer_begin(buff));
|
||||
unit_assert( memcmp(ldns_buffer_begin(buff),
|
||||
"\001+\012abc{e-zyxe\003net\000", 18) == 0);
|
||||
|
||||
ldns_buffer_write_at(buff, 0, "\000", 1);
|
||||
query_dname_tolower(ldns_buffer_begin(buff), 1);
|
||||
query_dname_tolower(ldns_buffer_begin(buff));
|
||||
unit_assert( memcmp(ldns_buffer_begin(buff), "\000", 1) == 0);
|
||||
|
||||
ldns_buffer_write_at(buff, 0, "\002NL\000", 4);
|
||||
query_dname_tolower(ldns_buffer_begin(buff), 4);
|
||||
query_dname_tolower(ldns_buffer_begin(buff));
|
||||
unit_assert( memcmp(ldns_buffer_begin(buff), "\002nl\000", 4) == 0);
|
||||
}
|
||||
|
||||
@@ -361,6 +361,377 @@ dname_test_dname_lab_cmp()
|
||||
unit_assert(ml == 4);
|
||||
}
|
||||
|
||||
/** test dname_subdomain_c */
|
||||
static void
|
||||
dname_test_subdomain()
|
||||
{
|
||||
unit_assert(dname_subdomain_c(
|
||||
(uint8_t*)"",
|
||||
(uint8_t*)""));
|
||||
unit_assert(dname_subdomain_c(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)""));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"",
|
||||
(uint8_t*)"\003com"));
|
||||
unit_assert(dname_subdomain_c(
|
||||
(uint8_t*)"\007example\003com",
|
||||
(uint8_t*)"\003com"));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)"\007example\003com"));
|
||||
unit_assert(dname_subdomain_c(
|
||||
(uint8_t*)"\007example\003com",
|
||||
(uint8_t*)""));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\003com"));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\003org"));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\007example\003net",
|
||||
(uint8_t*)"\003org"));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\007example\003org"));
|
||||
}
|
||||
|
||||
/** test dname_strict_subdomain */
|
||||
static void
|
||||
dname_test_strict_subdomain()
|
||||
{
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"", 1,
|
||||
(uint8_t*)"", 1));
|
||||
unit_assert(dname_strict_subdomain(
|
||||
(uint8_t*)"\003com", 2,
|
||||
(uint8_t*)"", 1));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"", 1,
|
||||
(uint8_t*)"\003com", 2));
|
||||
unit_assert(dname_strict_subdomain(
|
||||
(uint8_t*)"\007example\003com", 3,
|
||||
(uint8_t*)"\003com", 2));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\003com", 2,
|
||||
(uint8_t*)"\007example\003com", 3));
|
||||
unit_assert(dname_strict_subdomain(
|
||||
(uint8_t*)"\007example\003com", 3,
|
||||
(uint8_t*)"", 1));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\003net", 2,
|
||||
(uint8_t*)"\003com", 2));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\003net", 2,
|
||||
(uint8_t*)"\003org", 2));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\007example\003net", 3,
|
||||
(uint8_t*)"\003org", 2));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\003net", 2,
|
||||
(uint8_t*)"\007example\003org", 3));
|
||||
}
|
||||
|
||||
/** test dname_is_root */
|
||||
static void
|
||||
dname_test_isroot()
|
||||
{
|
||||
unit_assert(dname_is_root((uint8_t*)"\000"));
|
||||
unit_assert(!dname_is_root((uint8_t*)"\001a\000"));
|
||||
unit_assert(!dname_is_root((uint8_t*)"\005abvcd\003com\000"));
|
||||
/* malformed dname in this test, but should work */
|
||||
unit_assert(!dname_is_root((uint8_t*)"\077a\000"));
|
||||
unit_assert(dname_is_root((uint8_t*)"\000"));
|
||||
}
|
||||
|
||||
/** test dname_remove_label */
|
||||
static void
|
||||
dname_test_removelabel()
|
||||
{
|
||||
uint8_t* orig = (uint8_t*)"\007example\003com\000";
|
||||
uint8_t* n = orig;
|
||||
size_t l = 13;
|
||||
dname_remove_label(&n, &l);
|
||||
unit_assert( n == orig+8 );
|
||||
unit_assert( l == 5 );
|
||||
dname_remove_label(&n, &l);
|
||||
unit_assert( n == orig+12 );
|
||||
unit_assert( l == 1 );
|
||||
dname_remove_label(&n, &l);
|
||||
unit_assert( n == orig+12 );
|
||||
unit_assert( l == 1 );
|
||||
}
|
||||
|
||||
/** test dname_signame_label_count */
|
||||
static void
|
||||
dname_test_sigcount()
|
||||
{
|
||||
unit_assert(dname_signame_label_count((uint8_t*)"\000") == 0);
|
||||
unit_assert(dname_signame_label_count((uint8_t*)"\001*\000") == 0);
|
||||
unit_assert(dname_signame_label_count((uint8_t*)"\003xom\000") == 1);
|
||||
unit_assert(dname_signame_label_count(
|
||||
(uint8_t*)"\001*\003xom\000") == 1);
|
||||
unit_assert(dname_signame_label_count(
|
||||
(uint8_t*)"\007example\003xom\000") == 2);
|
||||
unit_assert(dname_signame_label_count(
|
||||
(uint8_t*)"\001*\007example\003xom\000") == 2);
|
||||
unit_assert(dname_signame_label_count(
|
||||
(uint8_t*)"\003www\007example\003xom\000") == 3);
|
||||
unit_assert(dname_signame_label_count(
|
||||
(uint8_t*)"\001*\003www\007example\003xom\000") == 3);
|
||||
}
|
||||
|
||||
/** test dname_is_wild routine */
|
||||
static void
|
||||
dname_test_iswild()
|
||||
{
|
||||
unit_assert( !dname_is_wild((uint8_t*)"\000") );
|
||||
unit_assert( dname_is_wild((uint8_t*)"\001*\000") );
|
||||
unit_assert( !dname_is_wild((uint8_t*)"\003net\000") );
|
||||
unit_assert( dname_is_wild((uint8_t*)"\001*\003net\000") );
|
||||
}
|
||||
|
||||
/** test dname_canonical_compare */
|
||||
static void
|
||||
dname_test_canoncmp()
|
||||
{
|
||||
/* equality */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\000",
|
||||
(uint8_t*)"\000"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003net\000",
|
||||
(uint8_t*)"\003net\000"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example\003net\000",
|
||||
(uint8_t*)"\007example\003net\000"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004test\007example\003net\000",
|
||||
(uint8_t*)"\004test\007example\003net\000"
|
||||
) == 0);
|
||||
|
||||
/* subdomains */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)"\000"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\000",
|
||||
(uint8_t*)"\003com"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example\003com",
|
||||
(uint8_t*)"\003com"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)"\007example\003com"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example\003com",
|
||||
(uint8_t*)"\000"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\000",
|
||||
(uint8_t*)"\007example\003com"
|
||||
) == -1);
|
||||
|
||||
/* compare rightmost label */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)"\003net"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\003com"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\003org"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example\003net",
|
||||
(uint8_t*)"\003org"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003org",
|
||||
(uint8_t*)"\007example\003net"
|
||||
) == 1);
|
||||
|
||||
/* label length makes a difference; but only if rest is equal */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004neta",
|
||||
(uint8_t*)"\003net"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\002ne",
|
||||
(uint8_t*)"\004neta"
|
||||
) == -1);
|
||||
|
||||
/* label content */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003aag\007example\003net",
|
||||
(uint8_t*)"\003bla\007example\003net"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003bla\007example\003net",
|
||||
(uint8_t*)"\003aag\007example\003net"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003bla\003aag\007example\003net",
|
||||
(uint8_t*)"\003aag\003bla\007example\003net"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\02sn\003opt\003aag\007example\003net",
|
||||
(uint8_t*)"\02sn\003opt\003bla\007example\003net"
|
||||
) == -1);
|
||||
|
||||
/* lowercase during compare */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003bLa\007examPLe\003net",
|
||||
(uint8_t*)"\003bla\007eXAmple\003nET"
|
||||
) == 0);
|
||||
|
||||
/* example from 4034 */
|
||||
/* example a.example yljkjljk.a.example Z.a.example zABC.a.EXAMPLE
|
||||
z.example \001.z.example *.z.example \200.z.example */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"",
|
||||
(uint8_t*)"\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example",
|
||||
(uint8_t*)"\001a\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001a\007example",
|
||||
(uint8_t*)"\010yljkjljk\001a\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\010yljkjljk\001a\007example",
|
||||
(uint8_t*)"\001Z\001a\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001Z\001a\007example",
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE",
|
||||
(uint8_t*)"\001z\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001z\007example",
|
||||
(uint8_t*)"\001\001\001z\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\001\001z\007example",
|
||||
(uint8_t*)"\001*\001z\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001*\001z\007example",
|
||||
(uint8_t*)"\001\200\001z\007example"
|
||||
) == -1);
|
||||
/* same example in reverse */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example",
|
||||
(uint8_t*)""
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001a\007example",
|
||||
(uint8_t*)"\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\010yljkjljk\001a\007example",
|
||||
(uint8_t*)"\001a\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001Z\001a\007example",
|
||||
(uint8_t*)"\010yljkjljk\001a\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE",
|
||||
(uint8_t*)"\001Z\001a\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001z\007example",
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\001\001z\007example",
|
||||
(uint8_t*)"\001z\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001*\001z\007example",
|
||||
(uint8_t*)"\001\001\001z\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\200\001z\007example",
|
||||
(uint8_t*)"\001*\001z\007example"
|
||||
) == 1);
|
||||
/* same example for equality */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example",
|
||||
(uint8_t*)"\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001a\007example",
|
||||
(uint8_t*)"\001a\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\010yljkjljk\001a\007example",
|
||||
(uint8_t*)"\010yljkjljk\001a\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001Z\001a\007example",
|
||||
(uint8_t*)"\001Z\001a\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE",
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001z\007example",
|
||||
(uint8_t*)"\001z\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\001\001z\007example",
|
||||
(uint8_t*)"\001\001\001z\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001*\001z\007example",
|
||||
(uint8_t*)"\001*\001z\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\200\001z\007example",
|
||||
(uint8_t*)"\001\200\001z\007example"
|
||||
) == 0);
|
||||
}
|
||||
|
||||
/** Test dname_get_shared_topdomain */
|
||||
static void
|
||||
dname_test_topdomain()
|
||||
{
|
||||
unit_assert( query_dname_compare(
|
||||
dname_get_shared_topdomain(
|
||||
(uint8_t*)"",
|
||||
(uint8_t*)""),
|
||||
(uint8_t*)"") == 0);
|
||||
unit_assert( query_dname_compare(
|
||||
dname_get_shared_topdomain(
|
||||
(uint8_t*)"\003www\007example\003com",
|
||||
(uint8_t*)"\003www\007example\003com"),
|
||||
(uint8_t*)"\003www\007example\003com") == 0);
|
||||
unit_assert( query_dname_compare(
|
||||
dname_get_shared_topdomain(
|
||||
(uint8_t*)"\003www\007example\003com",
|
||||
(uint8_t*)"\003bla\007example\003com"),
|
||||
(uint8_t*)"\007example\003com") == 0);
|
||||
}
|
||||
|
||||
void dname_test()
|
||||
{
|
||||
ldns_buffer* buff = ldns_buffer_new(65800);
|
||||
@@ -372,5 +743,13 @@ void dname_test()
|
||||
dname_test_count_size_labels();
|
||||
dname_test_dname_lab_cmp();
|
||||
dname_test_pkt_dname_len(buff);
|
||||
dname_test_strict_subdomain();
|
||||
dname_test_subdomain();
|
||||
dname_test_isroot();
|
||||
dname_test_removelabel();
|
||||
dname_test_sigcount();
|
||||
dname_test_iswild();
|
||||
dname_test_canoncmp();
|
||||
dname_test_topdomain();
|
||||
ldns_buffer_free(buff);
|
||||
}
|
||||
|
||||
+58
-94
@@ -42,36 +42,24 @@
|
||||
#include "testcode/unitmain.h"
|
||||
#include "util/log.h"
|
||||
#include "util/storage/lruhash.h"
|
||||
#include "util/storage/slabhash.h" /* for the test structures */
|
||||
|
||||
/* --- test representation --- */
|
||||
/** structure contains test key */
|
||||
struct testkey {
|
||||
/** the key id */
|
||||
int id;
|
||||
/** the entry */
|
||||
struct lruhash_entry entry;
|
||||
};
|
||||
/** structure contains test data */
|
||||
struct testdata {
|
||||
/** data value */
|
||||
int data;
|
||||
};
|
||||
/** use this type for the lruhash test key */
|
||||
typedef struct slabhash_testkey testkey_t;
|
||||
/** use this type for the lruhash test data */
|
||||
typedef struct slabhash_testdata testdata_t;
|
||||
|
||||
/** sizefunc for lruhash */
|
||||
static size_t test_sizefunc(void*, void*);
|
||||
/** comparefunc for lruhash */
|
||||
static int test_compfunc(void*, void*);
|
||||
/** delkey for lruhash */
|
||||
static void test_delkey(void*, void*);
|
||||
/** deldata for lruhash */
|
||||
static void test_deldata(void*, void*);
|
||||
/* --- end test representation --- */
|
||||
/** delete key */
|
||||
static void delkey(struct slabhash_testkey* k) {
|
||||
lock_rw_destroy(&k->entry.lock); free(k);}
|
||||
/** delete data */
|
||||
static void deldata(struct slabhash_testdata* d) {free(d);}
|
||||
|
||||
/** hash func, very bad to improve collisions. */
|
||||
/** hash func, very bad to improve collisions */
|
||||
static hashvalue_t myhash(int id) {return (hashvalue_t)id & 0x0f;}
|
||||
/** allocate new key, fill in hash. */
|
||||
static struct testkey* newkey(int id) {
|
||||
struct testkey* k = (struct testkey*)calloc(1, sizeof(struct testkey));
|
||||
/** allocate new key, fill in hash */
|
||||
static testkey_t* newkey(int id) {
|
||||
testkey_t* k = (testkey_t*)calloc(1, sizeof(testkey_t));
|
||||
if(!k) fatal_exit("out of memory");
|
||||
k->id = id;
|
||||
k->entry.hash = myhash(id);
|
||||
@@ -80,28 +68,23 @@ static struct testkey* newkey(int id) {
|
||||
return k;
|
||||
}
|
||||
/** new data el */
|
||||
static struct testdata* newdata(int val) {
|
||||
struct testdata* d = (struct testdata*)calloc(1,
|
||||
sizeof(struct testdata));
|
||||
static testdata_t* newdata(int val) {
|
||||
testdata_t* d = (testdata_t*)calloc(1,
|
||||
sizeof(testdata_t));
|
||||
if(!d) fatal_exit("out of memory");
|
||||
d->data = val;
|
||||
return d;
|
||||
}
|
||||
/** delete key */
|
||||
static void delkey(struct testkey* k) {
|
||||
lock_rw_destroy(&k->entry.lock); free(k);}
|
||||
/** delete data */
|
||||
static void deldata(struct testdata* d) {free(d);}
|
||||
|
||||
/** test bin_find_entry function and bin_overflow_remove */
|
||||
static void
|
||||
test_bin_find_entry(struct lruhash* table)
|
||||
{
|
||||
struct testkey* k = newkey(12);
|
||||
struct testdata* d = newdata(128);
|
||||
struct testkey* k2 = newkey(12 + 1024);
|
||||
struct testkey* k3 = newkey(14);
|
||||
struct testkey* k4 = newkey(12 + 1024*2);
|
||||
testkey_t* k = newkey(12);
|
||||
testdata_t* d = newdata(128);
|
||||
testkey_t* k2 = newkey(12 + 1024);
|
||||
testkey_t* k3 = newkey(14);
|
||||
testkey_t* k4 = newkey(12 + 1024*2);
|
||||
hashvalue_t h = myhash(12);
|
||||
struct lruhash_bin bin;
|
||||
memset(&bin, 0, sizeof(bin));
|
||||
@@ -178,8 +161,8 @@ test_bin_find_entry(struct lruhash* table)
|
||||
/** test lru_front lru_remove */
|
||||
static void test_lru(struct lruhash* table)
|
||||
{
|
||||
struct testkey* k = newkey(12);
|
||||
struct testkey* k2 = newkey(14);
|
||||
testkey_t* k = newkey(12);
|
||||
testkey_t* k2 = newkey(14);
|
||||
lock_quick_lock(&table->lock);
|
||||
|
||||
unit_assert( table->lru_start == NULL && table->lru_end == NULL);
|
||||
@@ -225,10 +208,10 @@ static void test_lru(struct lruhash* table)
|
||||
static void
|
||||
test_short_table(struct lruhash* table)
|
||||
{
|
||||
struct testkey* k = newkey(12);
|
||||
struct testkey* k2 = newkey(14);
|
||||
struct testdata* d = newdata(128);
|
||||
struct testdata* d2 = newdata(129);
|
||||
testkey_t* k = newkey(12);
|
||||
testkey_t* k2 = newkey(14);
|
||||
testdata_t* d = newdata(128);
|
||||
testdata_t* d2 = newdata(129);
|
||||
|
||||
k->entry.data = d;
|
||||
k2->entry.data = d2;
|
||||
@@ -249,11 +232,11 @@ test_short_table(struct lruhash* table)
|
||||
|
||||
/** test adding a random element */
|
||||
static void
|
||||
testadd(struct lruhash* table, struct testdata* ref[])
|
||||
testadd(struct lruhash* table, testdata_t* ref[])
|
||||
{
|
||||
int numtoadd = random() % HASHTESTMAX;
|
||||
struct testdata* data = newdata(numtoadd);
|
||||
struct testkey* key = newkey(numtoadd);
|
||||
testdata_t* data = newdata(numtoadd);
|
||||
testkey_t* key = newkey(numtoadd);
|
||||
key->entry.data = data;
|
||||
lruhash_insert(table, myhash(numtoadd), &key->entry, data, NULL);
|
||||
ref[numtoadd] = data;
|
||||
@@ -261,10 +244,10 @@ testadd(struct lruhash* table, struct testdata* ref[])
|
||||
|
||||
/** test adding a random element */
|
||||
static void
|
||||
testremove(struct lruhash* table, struct testdata* ref[])
|
||||
testremove(struct lruhash* table, testdata_t* ref[])
|
||||
{
|
||||
int num = random() % HASHTESTMAX;
|
||||
struct testkey* key = newkey(num);
|
||||
testkey_t* key = newkey(num);
|
||||
lruhash_remove(table, myhash(num), key);
|
||||
ref[num] = NULL;
|
||||
delkey(key);
|
||||
@@ -272,12 +255,12 @@ testremove(struct lruhash* table, struct testdata* ref[])
|
||||
|
||||
/** test adding a random element */
|
||||
static void
|
||||
testlookup(struct lruhash* table, struct testdata* ref[])
|
||||
testlookup(struct lruhash* table, testdata_t* ref[])
|
||||
{
|
||||
int num = random() % HASHTESTMAX;
|
||||
struct testkey* key = newkey(num);
|
||||
testkey_t* key = newkey(num);
|
||||
struct lruhash_entry* en = lruhash_lookup(table, myhash(num), key, 0);
|
||||
struct testdata* data = en? (struct testdata*)en->data : NULL;
|
||||
testdata_t* data = en? (testdata_t*)en->data : NULL;
|
||||
if(en) {
|
||||
unit_assert(en->key);
|
||||
unit_assert(en->data);
|
||||
@@ -321,17 +304,17 @@ check_table(struct lruhash* table)
|
||||
|
||||
/* this assertion is specific to the unit test */
|
||||
unit_assert( table->space_used ==
|
||||
table->num * test_sizefunc(NULL, NULL) );
|
||||
table->num * test_slabhash_sizefunc(NULL, NULL) );
|
||||
lock_quick_unlock(&table->lock);
|
||||
}
|
||||
|
||||
/** test adding a random element (unlimited range) */
|
||||
static void
|
||||
testadd_unlim(struct lruhash* table, struct testdata** ref)
|
||||
testadd_unlim(struct lruhash* table, testdata_t** ref)
|
||||
{
|
||||
int numtoadd = random() % (HASHTESTMAX * 10);
|
||||
struct testdata* data = newdata(numtoadd);
|
||||
struct testkey* key = newkey(numtoadd);
|
||||
testdata_t* data = newdata(numtoadd);
|
||||
testkey_t* key = newkey(numtoadd);
|
||||
key->entry.data = data;
|
||||
lruhash_insert(table, myhash(numtoadd), &key->entry, data, NULL);
|
||||
if(ref)
|
||||
@@ -340,10 +323,10 @@ testadd_unlim(struct lruhash* table, struct testdata** ref)
|
||||
|
||||
/** test adding a random element (unlimited range) */
|
||||
static void
|
||||
testremove_unlim(struct lruhash* table, struct testdata** ref)
|
||||
testremove_unlim(struct lruhash* table, testdata_t** ref)
|
||||
{
|
||||
int num = random() % (HASHTESTMAX*10);
|
||||
struct testkey* key = newkey(num);
|
||||
testkey_t* key = newkey(num);
|
||||
lruhash_remove(table, myhash(num), key);
|
||||
if(ref)
|
||||
ref[num] = NULL;
|
||||
@@ -352,12 +335,12 @@ testremove_unlim(struct lruhash* table, struct testdata** ref)
|
||||
|
||||
/** test adding a random element (unlimited range) */
|
||||
static void
|
||||
testlookup_unlim(struct lruhash* table, struct testdata** ref)
|
||||
testlookup_unlim(struct lruhash* table, testdata_t** ref)
|
||||
{
|
||||
int num = random() % (HASHTESTMAX*10);
|
||||
struct testkey* key = newkey(num);
|
||||
testkey_t* key = newkey(num);
|
||||
struct lruhash_entry* en = lruhash_lookup(table, myhash(num), key, 0);
|
||||
struct testdata* data = en? (struct testdata*)en->data : NULL;
|
||||
testdata_t* data = en? (testdata_t*)en->data : NULL;
|
||||
if(en) {
|
||||
unit_assert(en->key);
|
||||
unit_assert(en->data);
|
||||
@@ -377,17 +360,22 @@ static void
|
||||
test_long_table(struct lruhash* table)
|
||||
{
|
||||
/* assuming it all fits in the hastable, this check will work */
|
||||
struct testdata* ref[HASHTESTMAX * 100];
|
||||
testdata_t* ref[HASHTESTMAX * 100];
|
||||
size_t i;
|
||||
memset(ref, 0, sizeof(ref));
|
||||
/* test assumption */
|
||||
if(0) log_info(" size %d x %d < %d", (int)test_sizefunc(NULL, NULL),
|
||||
if(0) log_info(" size %d x %d < %d", (int)test_slabhash_sizefunc(NULL, NULL),
|
||||
(int)HASHTESTMAX, (int)table->space_max);
|
||||
unit_assert( test_sizefunc(NULL, NULL)*HASHTESTMAX < table->space_max);
|
||||
unit_assert( test_slabhash_sizefunc(NULL, NULL)*HASHTESTMAX < table->space_max);
|
||||
if(0) lruhash_status(table, "unit test", 1);
|
||||
srandom(48);
|
||||
for(i=0; i<1000; i++) {
|
||||
/* what to do? */
|
||||
if(i == 500) {
|
||||
lruhash_clear(table);
|
||||
memset(ref, 0, sizeof(ref));
|
||||
continue;
|
||||
}
|
||||
switch(random() % 4) {
|
||||
case 0:
|
||||
case 3:
|
||||
@@ -469,7 +457,7 @@ test_thr_main(void* arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** test hash table access by multiple threads. */
|
||||
/** test hash table access by multiple threads */
|
||||
static void
|
||||
test_threaded_table(struct lruhash* table)
|
||||
{
|
||||
@@ -496,40 +484,16 @@ void lruhash_test()
|
||||
struct lruhash* table ;
|
||||
printf("lruhash test\n");
|
||||
table = lruhash_create(2, 4096,
|
||||
test_sizefunc, test_compfunc, test_delkey, test_deldata, NULL);
|
||||
test_slabhash_sizefunc, test_slabhash_compfunc,
|
||||
test_slabhash_delkey, test_slabhash_deldata, NULL);
|
||||
test_bin_find_entry(table);
|
||||
test_lru(table);
|
||||
test_short_table(table);
|
||||
test_long_table(table);
|
||||
lruhash_delete(table);
|
||||
table = lruhash_create(2, 4096,
|
||||
test_sizefunc, test_compfunc, test_delkey, test_deldata, NULL);
|
||||
test_slabhash_sizefunc, test_slabhash_compfunc,
|
||||
test_slabhash_delkey, test_slabhash_deldata, NULL);
|
||||
test_threaded_table(table);
|
||||
lruhash_delete(table);
|
||||
}
|
||||
|
||||
static size_t test_sizefunc(void* ATTR_UNUSED(key), void* ATTR_UNUSED(data))
|
||||
{
|
||||
return sizeof(struct testkey) + sizeof(struct testdata);
|
||||
}
|
||||
|
||||
static int test_compfunc(void* key1, void* key2)
|
||||
{
|
||||
struct testkey* k1 = (struct testkey*)key1;
|
||||
struct testkey* k2 = (struct testkey*)key2;
|
||||
if(k1->id == k2->id)
|
||||
return 0;
|
||||
if(k1->id > k2->id)
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void test_delkey(void* key, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
delkey((struct testkey*)key);
|
||||
}
|
||||
|
||||
static void test_deldata(void* data, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
deldata((struct testdata*)data);
|
||||
}
|
||||
|
||||
+267
-1
@@ -89,6 +89,42 @@ alloc_test() {
|
||||
static void
|
||||
net_test()
|
||||
{
|
||||
char* t4[] = {"\000\000\000\000",
|
||||
"\200\000\000\000",
|
||||
"\300\000\000\000",
|
||||
"\340\000\000\000",
|
||||
"\360\000\000\000",
|
||||
"\370\000\000\000",
|
||||
"\374\000\000\000",
|
||||
"\376\000\000\000",
|
||||
"\377\000\000\000",
|
||||
"\377\200\000\000",
|
||||
"\377\300\000\000",
|
||||
"\377\340\000\000",
|
||||
"\377\360\000\000",
|
||||
"\377\370\000\000",
|
||||
"\377\374\000\000",
|
||||
"\377\376\000\000",
|
||||
"\377\377\000\000",
|
||||
"\377\377\200\000",
|
||||
"\377\377\300\000",
|
||||
"\377\377\340\000",
|
||||
"\377\377\360\000",
|
||||
"\377\377\370\000",
|
||||
"\377\377\374\000",
|
||||
"\377\377\376\000",
|
||||
"\377\377\377\000",
|
||||
"\377\377\377\200",
|
||||
"\377\377\377\300",
|
||||
"\377\377\377\340",
|
||||
"\377\377\377\360",
|
||||
"\377\377\377\370",
|
||||
"\377\377\377\374",
|
||||
"\377\377\377\376",
|
||||
"\377\377\377\377",
|
||||
"\377\377\377\377",
|
||||
"\377\377\377\377",
|
||||
};
|
||||
unit_assert( str_is_ip6("::") );
|
||||
unit_assert( str_is_ip6("::1") );
|
||||
unit_assert( str_is_ip6("2001:7b8:206:1:240:f4ff:fe37:8810") );
|
||||
@@ -117,6 +153,230 @@ net_test()
|
||||
unit_assert( !is_pow2(23) );
|
||||
unit_assert( !is_pow2(257) );
|
||||
unit_assert( !is_pow2(259) );
|
||||
|
||||
/* test addr_mask */
|
||||
if(1) {
|
||||
struct sockaddr_in a4;
|
||||
struct sockaddr_in6 a6;
|
||||
socklen_t l4 = (socklen_t)sizeof(a4);
|
||||
socklen_t l6 = (socklen_t)sizeof(a6);
|
||||
int i;
|
||||
a4.sin_family = AF_INET;
|
||||
a6.sin6_family = AF_INET6;
|
||||
for(i=0; i<35; i++) {
|
||||
/* address 255.255.255.255 */
|
||||
memcpy(&a4.sin_addr, "\377\377\377\377", 4);
|
||||
addr_mask((struct sockaddr_storage*)&a4, l4, i);
|
||||
unit_assert(memcmp(&a4.sin_addr, t4[i], 4) == 0);
|
||||
}
|
||||
memcpy(&a6.sin6_addr, "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377", 16);
|
||||
addr_mask((struct sockaddr_storage*)&a6, l6, 128);
|
||||
unit_assert(memcmp(&a6.sin6_addr, "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377", 16) == 0);
|
||||
addr_mask((struct sockaddr_storage*)&a6, l6, 122);
|
||||
unit_assert(memcmp(&a6.sin6_addr, "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\300", 16) == 0);
|
||||
addr_mask((struct sockaddr_storage*)&a6, l6, 120);
|
||||
unit_assert(memcmp(&a6.sin6_addr, "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\000", 16) == 0);
|
||||
addr_mask((struct sockaddr_storage*)&a6, l6, 64);
|
||||
unit_assert(memcmp(&a6.sin6_addr, "\377\377\377\377\377\377\377\377\000\000\000\000\000\000\000\000", 16) == 0);
|
||||
addr_mask((struct sockaddr_storage*)&a6, l6, 0);
|
||||
unit_assert(memcmp(&a6.sin6_addr, "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", 16) == 0);
|
||||
}
|
||||
|
||||
/* test addr_in_common */
|
||||
if(1) {
|
||||
struct sockaddr_in a4, b4;
|
||||
struct sockaddr_in6 a6, b6;
|
||||
socklen_t l4 = (socklen_t)sizeof(a4);
|
||||
socklen_t l6 = (socklen_t)sizeof(a6);
|
||||
int i;
|
||||
a4.sin_family = AF_INET;
|
||||
b4.sin_family = AF_INET;
|
||||
a6.sin6_family = AF_INET6;
|
||||
b6.sin6_family = AF_INET6;
|
||||
memcpy(&a4.sin_addr, "abcd", 4);
|
||||
memcpy(&b4.sin_addr, "abcd", 4);
|
||||
unit_assert(addr_in_common((struct sockaddr_storage*)&a4, 32,
|
||||
(struct sockaddr_storage*)&b4, 32, l4) == 32);
|
||||
unit_assert(addr_in_common((struct sockaddr_storage*)&a4, 34,
|
||||
(struct sockaddr_storage*)&b4, 32, l4) == 32);
|
||||
for(i=0; i<=32; i++) {
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&a4, 32,
|
||||
(struct sockaddr_storage*)&b4, i, l4) == i);
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&a4, i,
|
||||
(struct sockaddr_storage*)&b4, 32, l4) == i);
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&a4, i,
|
||||
(struct sockaddr_storage*)&b4, i, l4) == i);
|
||||
}
|
||||
for(i=0; i<=32; i++) {
|
||||
memcpy(&a4.sin_addr, "\377\377\377\377", 4);
|
||||
memcpy(&b4.sin_addr, t4[i], 4);
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&a4, 32,
|
||||
(struct sockaddr_storage*)&b4, 32, l4) == i);
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&b4, 32,
|
||||
(struct sockaddr_storage*)&a4, 32, l4) == i);
|
||||
}
|
||||
memcpy(&a6.sin6_addr, "abcdefghabcdefgh", 16);
|
||||
memcpy(&b6.sin6_addr, "abcdefghabcdefgh", 16);
|
||||
unit_assert(addr_in_common((struct sockaddr_storage*)&a6, 128,
|
||||
(struct sockaddr_storage*)&b6, 128, l6) == 128);
|
||||
unit_assert(addr_in_common((struct sockaddr_storage*)&a6, 129,
|
||||
(struct sockaddr_storage*)&b6, 128, l6) == 128);
|
||||
for(i=0; i<=128; i++) {
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&a6, 128,
|
||||
(struct sockaddr_storage*)&b6, i, l6) == i);
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&a6, i,
|
||||
(struct sockaddr_storage*)&b6, 128, l6) == i);
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&a6, i,
|
||||
(struct sockaddr_storage*)&b6, i, l6) == i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "util/config_file.h"
|
||||
/** test config_file: cfg_parse_memsize */
|
||||
static void
|
||||
config_memsize_test()
|
||||
{
|
||||
size_t v = 0;
|
||||
if(0) {
|
||||
/* these emit errors */
|
||||
unit_assert( cfg_parse_memsize("", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("bla", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("nop", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("n0b", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("gb", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("b", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("kb", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("kk kb", &v) == 0);
|
||||
}
|
||||
unit_assert( cfg_parse_memsize("0", &v) && v==0);
|
||||
unit_assert( cfg_parse_memsize("1", &v) && v==1);
|
||||
unit_assert( cfg_parse_memsize("10", &v) && v==10);
|
||||
unit_assert( cfg_parse_memsize("10b", &v) && v==10);
|
||||
unit_assert( cfg_parse_memsize("5b", &v) && v==5);
|
||||
unit_assert( cfg_parse_memsize("1024", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1k", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1K", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1Kb", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1kb", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1 kb", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("10 kb", &v) && v==10240);
|
||||
unit_assert( cfg_parse_memsize("2k", &v) && v==2048);
|
||||
unit_assert( cfg_parse_memsize("2m", &v) && v==2048*1024);
|
||||
unit_assert( cfg_parse_memsize("3M", &v) && v==3072*1024);
|
||||
unit_assert( cfg_parse_memsize("40m", &v) && v==40960*1024);
|
||||
unit_assert( cfg_parse_memsize("1G", &v) && v==1024*1024*1024);
|
||||
unit_assert( cfg_parse_memsize("1 Gb", &v) && v==1024*1024*1024);
|
||||
unit_assert( cfg_parse_memsize("0 Gb", &v) && v==0*1024*1024);
|
||||
}
|
||||
|
||||
#include "util/rtt.h"
|
||||
/** test RTT code */
|
||||
static void
|
||||
rtt_test()
|
||||
{
|
||||
int init = 376;
|
||||
int i;
|
||||
struct rtt_info r;
|
||||
rtt_init(&r);
|
||||
/* initial value sensible */
|
||||
unit_assert( rtt_timeout(&r) == init );
|
||||
rtt_lost(&r);
|
||||
unit_assert( rtt_timeout(&r) == init*2 );
|
||||
rtt_lost(&r);
|
||||
unit_assert( rtt_timeout(&r) == init*4 );
|
||||
rtt_update(&r, 4000);
|
||||
unit_assert( rtt_timeout(&r) >= 2000 );
|
||||
rtt_lost(&r);
|
||||
for(i=0; i<100; i++) {
|
||||
rtt_lost(&r);
|
||||
unit_assert( rtt_timeout(&r) > RTT_MIN_TIMEOUT-1);
|
||||
unit_assert( rtt_timeout(&r) < RTT_MAX_TIMEOUT+1);
|
||||
}
|
||||
}
|
||||
|
||||
#include "services/cache/infra.h"
|
||||
#include "util/config_file.h"
|
||||
/** test host cache */
|
||||
static void
|
||||
infra_test()
|
||||
{
|
||||
int one = 1;
|
||||
uint8_t* zone = (uint8_t*)"\007example\003com\000";
|
||||
size_t zonelen = 13;
|
||||
struct infra_cache* slab;
|
||||
struct config_file* cfg = config_create();
|
||||
time_t now = 0;
|
||||
int vs, to;
|
||||
struct infra_host_key* k;
|
||||
struct infra_host_data* d;
|
||||
int init = 376;
|
||||
|
||||
slab = infra_create(cfg);
|
||||
unit_assert( infra_host(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), now, &vs, &to) );
|
||||
unit_assert( vs == 0 && to == init );
|
||||
|
||||
unit_assert( infra_rtt_update(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), -1, now) );
|
||||
unit_assert( infra_host(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), now, &vs, &to) );
|
||||
unit_assert( vs == 0 && to == init*2 );
|
||||
|
||||
unit_assert( infra_edns_update(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), -1, now) );
|
||||
unit_assert( infra_host(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), now, &vs, &to) );
|
||||
unit_assert( vs == -1 && to == init*2 );
|
||||
|
||||
now += cfg->host_ttl + 10;
|
||||
unit_assert( infra_host(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), now, &vs, &to) );
|
||||
unit_assert( vs == 0 && to == init );
|
||||
|
||||
unit_assert( infra_set_lame(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), zone, zonelen, now, 0) );
|
||||
unit_assert( (d=infra_lookup_host(slab, (struct sockaddr_storage*)&one,
|
||||
(socklen_t)sizeof(int), 0, now, &k)) );
|
||||
unit_assert( d->ttl == now+cfg->host_ttl );
|
||||
unit_assert( d->edns_version == 0 );
|
||||
unit_assert( infra_lookup_lame(d, zone, zonelen, now)==1 );
|
||||
unit_assert( !infra_lookup_lame(d, zone, zonelen,
|
||||
now+cfg->lame_ttl+10) );
|
||||
unit_assert( !infra_lookup_lame(d, (uint8_t*)"\000", 1, now) );
|
||||
lock_rw_unlock(&k->entry.lock);
|
||||
|
||||
infra_delete(slab);
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
#include "util/random.h"
|
||||
/** test randomness */
|
||||
static void
|
||||
rnd_test()
|
||||
{
|
||||
struct ub_randstate* r;
|
||||
int num = 100, i;
|
||||
long int a[100];
|
||||
unit_assert( (r = ub_initstate((unsigned)time(NULL), NULL)) );
|
||||
for(i=0; i<num; i++) {
|
||||
a[i] = ub_random(r);
|
||||
unit_assert(a[i] >= 0);
|
||||
unit_assert((size_t)a[i] <= (size_t)RAND_MAX);
|
||||
if(i > 5)
|
||||
unit_assert(a[i] != a[i-1] || a[i] != a[i-2] ||
|
||||
a[i] != a[i-3] || a[i] != a[i-4] ||
|
||||
a[i] != a[i-5] || a[i] != a[i-6]);
|
||||
}
|
||||
ub_randfree(r);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,7 +387,7 @@ net_test()
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
log_init(NULL);
|
||||
log_init(NULL, 0, NULL);
|
||||
if(argc != 1) {
|
||||
printf("usage: %s\n", argv[0]);
|
||||
printf("\tperforms unit tests.\n");
|
||||
@@ -135,11 +395,17 @@ main(int argc, char* argv[])
|
||||
}
|
||||
printf("Start of %s unit test.\n", PACKAGE_STRING);
|
||||
checklock_start();
|
||||
rnd_test();
|
||||
verify_test();
|
||||
net_test();
|
||||
config_memsize_test();
|
||||
dname_test();
|
||||
anchors_test();
|
||||
rtt_test();
|
||||
alloc_test();
|
||||
lruhash_test();
|
||||
slabhash_test();
|
||||
infra_test();
|
||||
msgparse_test();
|
||||
checklock_stop();
|
||||
printf("%d checks ok.\n", testcount);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user